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[];