Adds a len() function Avoids needing to use a for-each loop to compute list size: https://source.chromium.org/chromium/chromium/src/+/main:build/config/android/rules.gni;drc=7048ca9df6b3c3a4ea6e34294a0a4e506f1ab569;l=902 I haven't seen the need for it on strings, but it seems like a thing you'd expect it to work on. Change-Id: I233c172a7a121e4f364a152282f68011932d9562 Reviewed-on: https://gn-review.googlesource.com/c/gn/+/19820 Commit-Queue: Andrew Grieve <agrieve@google.com> Reviewed-by: Dirk Pranke <dpranke@chromium.org> Reviewed-by: Sylvain Defresne <sdefresne@chromium.org>
diff --git a/build/gen.py b/build/gen.py index 533df58..d3ecfdc 100755 --- a/build/gen.py +++ b/build/gen.py
@@ -697,6 +697,7 @@ 'src/gn/function_get_path_info.cc', 'src/gn/function_get_target_outputs.cc', 'src/gn/function_label_matches.cc', + 'src/gn/function_len.cc', 'src/gn/function_path_exists.cc', 'src/gn/function_process_file_template.cc', 'src/gn/function_read_file.cc', @@ -836,6 +837,7 @@ 'src/gn/function_get_path_info_unittest.cc', 'src/gn/function_get_target_outputs_unittest.cc', 'src/gn/function_label_matches_unittest.cc', + 'src/gn/function_len_unittest.cc', 'src/gn/function_path_exists_unittest.cc', 'src/gn/function_process_file_template_unittest.cc', 'src/gn/function_rebase_path_unittest.cc',
diff --git a/docs/reference.md b/docs/reference.md index ea95bc8..6e99270 100644 --- a/docs/reference.md +++ b/docs/reference.md
@@ -53,6 +53,7 @@ * [getenv: Get an environment variable.](#func_getenv) * [import: Import a file into the current scope.](#func_import) * [label_matches: Returns whether a label matches any of a list of patterns.](#func_label_matches) + * [len: Returns the length of a string or a list.](#func_len) * [not_needed: Mark variables from scope as not needed.](#func_not_needed) * [path_exists: Returns whether the given path exists.](#func_path_exists) * [pool: Defines a pool object.](#func_pool) @@ -3047,6 +3048,24 @@ result = label_matches("//baz:bar", [ "//foo/bar/*", "//baz:*" ]) # result will be true ``` + +### <a name="func_len"></a>**len**: Returns the length of a string or a list. [Back to Top](#gn-reference) + +``` + len(item) +``` + +#### **Argument** + + The argument can be a string or a list. + +#### **Examples** + +``` + len("foo") # 3 + len([ "a", "b", "c" ]) # 3 +``` + ### <a name="func_not_needed"></a>**not_needed**: Mark variables from scope as not needed. [Back to Top](#gn-reference) ```
diff --git a/src/gn/function_len.cc b/src/gn/function_len.cc new file mode 100644 index 0000000..826ba0e --- /dev/null +++ b/src/gn/function_len.cc
@@ -0,0 +1,52 @@ +// Copyright 2025 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <stddef.h> + +#include "gn/err.h" +#include "gn/functions.h" +#include "gn/parse_tree.h" +#include "gn/value.h" + +namespace functions { + +Value RunLen(Scope* scope, + const FunctionCallNode* function, + const std::vector<Value>& args, + Err* err) { + if (args.size() != 1) { + *err = Err(function->function(), "Expecting exactly one argument."); + return Value(); + } + + const Value& value = args[0]; + if (value.type() == Value::STRING) { + return Value(function, static_cast<int64_t>(value.string_value().size())); + } + + if (value.type() == Value::LIST) { + return Value(function, static_cast<int64_t>(value.list_value().size())); + } + + *err = Err( + value.origin(), "len() expects a string or a list.", + "Got " + std::string(Value::DescribeType(value.type())) + " instead."); + return Value(); +} + +const char kLen[] = "len"; +const char kLen_HelpShort[] = "len: Returns the length of a string or a list."; +const char kLen_Help[] = + R"(len: Returns the length of a string or a list. + + len(item) + + The argument can be a string or a list. + +Examples: + len("foo") # 3 + len([ "a", "b", "c" ]) # 3 +)"; + +} // namespace functions
diff --git a/src/gn/function_len_unittest.cc b/src/gn/function_len_unittest.cc new file mode 100644 index 0000000..47e3631 --- /dev/null +++ b/src/gn/function_len_unittest.cc
@@ -0,0 +1,60 @@ +// Copyright 2025 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "gn/functions.h" +#include "gn/test_with_scope.h" +#include "util/test/test.h" + +TEST(LenTest, StringLen) { + TestWithScope setup; + FunctionCallNode function_call; + Err err; + std::vector<Value> args; + args.push_back(Value(nullptr, "foo")); + Value result = functions::RunLen(setup.scope(), &function_call, args, &err); + EXPECT_FALSE(err.has_error()); + EXPECT_EQ(result.type(), Value::INTEGER); + EXPECT_EQ(result.int_value(), 3); +} + +TEST(LenTest, ListLen) { + TestWithScope setup; + FunctionCallNode function_call; + Err err; + std::vector<Value> args; + Value list_value(nullptr, Value::LIST); + list_value.list_value().push_back(Value(nullptr, "a")); + list_value.list_value().push_back(Value(nullptr, "b")); + args.push_back(list_value); + Value result = functions::RunLen(setup.scope(), &function_call, args, &err); + EXPECT_FALSE(err.has_error()); + EXPECT_EQ(result.type(), Value::INTEGER); + EXPECT_EQ(result.int_value(), 2); +} + +TEST(LenTest, InvalidType) { + TestWithScope setup; + FunctionCallNode function_call; + Err err; + std::vector<Value> args; + args.push_back(Value(nullptr, static_cast<int64_t>(123))); + functions::RunLen(setup.scope(), &function_call, args, &err); + EXPECT_TRUE(err.has_error()); +} + +TEST(LenTest, WrongArugmentCount) { + TestWithScope setup; + FunctionCallNode function_call; + Err err; + std::vector<Value> args; + // No Args: + functions::RunLen(setup.scope(), &function_call, args, &err); + EXPECT_TRUE(err.has_error()); + + // Two Args: + args.push_back(Value(nullptr, "a")); + args.push_back(Value(nullptr, "b")); + functions::RunLen(setup.scope(), &function_call, args, &err); + EXPECT_TRUE(err.has_error()); +}
diff --git a/src/gn/functions.cc b/src/gn/functions.cc index a12b078..8386de5 100644 --- a/src/gn/functions.cc +++ b/src/gn/functions.cc
@@ -1475,6 +1475,7 @@ INSERT_FUNCTION(GetTargetOutputs, false) INSERT_FUNCTION(Import, false) INSERT_FUNCTION(LabelMatches, false) + INSERT_FUNCTION(Len, false) INSERT_FUNCTION(NotNeeded, false) INSERT_FUNCTION(PathExists, false) INSERT_FUNCTION(Pool, false)
diff --git a/src/gn/functions.h b/src/gn/functions.h index 59325ec..787e6ed 100644 --- a/src/gn/functions.h +++ b/src/gn/functions.h
@@ -260,6 +260,14 @@ const std::vector<Value>& args, Err* err); +extern const char kLen[]; +extern const char kLen_HelpShort[]; +extern const char kLen_Help[]; +Value RunLen(Scope* scope, + const FunctionCallNode* function, + const std::vector<Value>& args, + Err* err); + extern const char kLoadableModule[]; extern const char kLoadableModule_HelpShort[]; extern const char kLoadableModule_Help[];