1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "tools/gn/functions.h"
6
7 #include <iostream>
8
9 #include "base/strings/string_util.h"
10 #include "tools/gn/config.h"
11 #include "tools/gn/config_values_generator.h"
12 #include "tools/gn/err.h"
13 #include "tools/gn/input_file.h"
14 #include "tools/gn/parse_tree.h"
15 #include "tools/gn/scheduler.h"
16 #include "tools/gn/scope.h"
17 #include "tools/gn/settings.h"
18 #include "tools/gn/token.h"
19 #include "tools/gn/value.h"
20
21 namespace {
22
23 // This is called when a template is invoked. When we see a template
24 // declaration, that funciton is RunTemplate.
RunTemplateInvocation(Scope * scope,const FunctionCallNode * invocation,const std::vector<Value> & args,BlockNode * block,const FunctionCallNode * rule,Err * err)25 Value RunTemplateInvocation(Scope* scope,
26 const FunctionCallNode* invocation,
27 const std::vector<Value>& args,
28 BlockNode* block,
29 const FunctionCallNode* rule,
30 Err* err) {
31 if (!EnsureNotProcessingImport(invocation, scope, err))
32 return Value();
33
34 Scope block_scope(scope);
35 if (!FillTargetBlockScope(scope, invocation,
36 invocation->function().value().as_string(),
37 block, args, &block_scope, err))
38 return Value();
39
40 // Run the block for the rule invocation.
41 block->ExecuteBlockInScope(&block_scope, err);
42 if (err->has_error())
43 return Value();
44
45 // Now run the rule itself with that block as the current scope.
46 rule->block()->ExecuteBlockInScope(&block_scope, err);
47 if (err->has_error())
48 return Value();
49
50 block_scope.CheckForUnusedVars(err);
51 return Value();
52 }
53
54 } // namespace
55
56 // ----------------------------------------------------------------------------
57
EnsureNotProcessingImport(const ParseNode * node,const Scope * scope,Err * err)58 bool EnsureNotProcessingImport(const ParseNode* node,
59 const Scope* scope,
60 Err* err) {
61 if (scope->IsProcessingImport()) {
62 *err = Err(node, "Not valid from an import.",
63 "Imports are for defining defaults, variables, and rules. The\n"
64 "appropriate place for this kind of thing is really in a normal\n"
65 "BUILD file.");
66 return false;
67 }
68 return true;
69 }
70
EnsureNotProcessingBuildConfig(const ParseNode * node,const Scope * scope,Err * err)71 bool EnsureNotProcessingBuildConfig(const ParseNode* node,
72 const Scope* scope,
73 Err* err) {
74 if (scope->IsProcessingBuildConfig()) {
75 *err = Err(node, "Not valid from the build config.",
76 "You can't do this kind of thing from the build config script, "
77 "silly!\nPut it in a regular BUILD file.");
78 return false;
79 }
80 return true;
81 }
82
FillTargetBlockScope(const Scope * scope,const FunctionCallNode * function,const std::string & target_type,const BlockNode * block,const std::vector<Value> & args,Scope * block_scope,Err * err)83 bool FillTargetBlockScope(const Scope* scope,
84 const FunctionCallNode* function,
85 const std::string& target_type,
86 const BlockNode* block,
87 const std::vector<Value>& args,
88 Scope* block_scope,
89 Err* err) {
90 if (!block) {
91 FillNeedsBlockError(function, err);
92 return false;
93 }
94
95 // Copy the target defaults, if any, into the scope we're going to execute
96 // the block in.
97 const Scope* default_scope = scope->GetTargetDefaults(target_type);
98 if (default_scope) {
99 if (!default_scope->NonRecursiveMergeTo(block_scope, function,
100 "target defaults", err))
101 return false;
102 }
103
104 // The name is the single argument to the target function.
105 if (!EnsureSingleStringArg(function, args, err))
106 return false;
107
108 // Set the target name variable to the current target, and mark it used
109 // because we don't want to issue an error if the script ignores it.
110 const base::StringPiece target_name("target_name");
111 block_scope->SetValue(target_name, Value(function, args[0].string_value()),
112 function);
113 block_scope->MarkUsed(target_name);
114 return true;
115 }
116
FillNeedsBlockError(const FunctionCallNode * function,Err * err)117 void FillNeedsBlockError(const FunctionCallNode* function, Err* err) {
118 *err = Err(function->function(), "This function call requires a block.",
119 "The block's \"{\" must be on the same line as the function "
120 "call's \")\".");
121 }
122
EnsureSingleStringArg(const FunctionCallNode * function,const std::vector<Value> & args,Err * err)123 bool EnsureSingleStringArg(const FunctionCallNode* function,
124 const std::vector<Value>& args,
125 Err* err) {
126 if (args.size() != 1) {
127 *err = Err(function->function(), "Incorrect arguments.",
128 "This function requires a single string argument.");
129 return false;
130 }
131 return args[0].VerifyTypeIs(Value::STRING, err);
132 }
133
ToolchainLabelForScope(const Scope * scope)134 const Label& ToolchainLabelForScope(const Scope* scope) {
135 return scope->settings()->toolchain_label();
136 }
137
MakeLabelForScope(const Scope * scope,const FunctionCallNode * function,const std::string & name)138 Label MakeLabelForScope(const Scope* scope,
139 const FunctionCallNode* function,
140 const std::string& name) {
141 const Label& toolchain_label = ToolchainLabelForScope(scope);
142 return Label(scope->GetSourceDir(), name, toolchain_label.dir(),
143 toolchain_label.name());
144 }
145
146 namespace functions {
147
148 // assert ----------------------------------------------------------------------
149
150 const char kAssert[] = "assert";
151 const char kAssert_Help[] =
152 "assert: Assert an expression is true at generation time.\n"
153 "\n"
154 " assert(<condition> [, <error string>])\n"
155 "\n"
156 " If the condition is false, the build will fail with an error. If the\n"
157 " optional second argument is provided, that string will be printed\n"
158 " with the error message.\n"
159 "\n"
160 "Examples:\n"
161 " assert(is_win)\n"
162 " assert(defined(sources), \"Sources must be defined\")\n";
163
RunAssert(Scope * scope,const FunctionCallNode * function,const std::vector<Value> & args,Err * err)164 Value RunAssert(Scope* scope,
165 const FunctionCallNode* function,
166 const std::vector<Value>& args,
167 Err* err) {
168 if (args.size() != 1 && args.size() != 2) {
169 *err = Err(function->function(), "Wrong number of arguments.",
170 "assert() takes one or two argument, "
171 "were you expecting somethig else?");
172 } else if (args[0].type() != Value::BOOLEAN) {
173 *err = Err(function->function(), "Assertion value not a bool.");
174 } else if (!args[0].boolean_value()) {
175 if (args.size() == 2) {
176 // Optional string message.
177 if (args[1].type() != Value::STRING) {
178 *err = Err(function->function(), "Assertion failed.",
179 "<<<ERROR MESSAGE IS NOT A STRING>>>");
180 } else {
181 *err = Err(function->function(), "Assertion failed.",
182 args[1].string_value());
183 }
184 } else {
185 *err = Err(function->function(), "Assertion failed.");
186 }
187
188 if (args[0].origin()) {
189 // If you do "assert(foo)" we'd ideally like to show you where foo was
190 // set, and in this case the origin of the args will tell us that.
191 // However, if you do "assert(foo && bar)" the source of the value will
192 // be the assert like, which isn't so helpful.
193 //
194 // So we try to see if the args are from the same line or not. This will
195 // break if you do "assert(\nfoo && bar)" and we may show the second line
196 // as the source, oh well. The way around this is to check to see if the
197 // origin node is inside our function call block.
198 Location origin_location = args[0].origin()->GetRange().begin();
199 if (origin_location.file() != function->function().location().file() ||
200 origin_location.line_number() !=
201 function->function().location().line_number()) {
202 err->AppendSubErr(Err(args[0].origin()->GetRange(), "",
203 "This is where it was set."));
204 }
205 }
206 }
207 return Value();
208 }
209
210 // config ----------------------------------------------------------------------
211
212 const char kConfig[] = "config";
213 const char kConfig_Help[] =
214 "config: Defines a configuration object.\n"
215 "\n"
216 " Configuration objects can be applied to targets and specify sets of\n"
217 " compiler flags, includes, defines, etc. They provide a way to\n"
218 " conveniently group sets of this configuration information.\n"
219 "\n"
220 " A config is referenced by its label just like a target.\n"
221 "\n"
222 " The values in a config are additive only. If you want to remove a flag\n"
223 " you need to remove the corresponding config that sets it. The final\n"
224 " set of flags, defines, etc. for a target is generated in this order:\n"
225 "\n"
226 " 1. The values specified directly on the target (rather than using a\n"
227 " config.\n"
228 " 2. The configs specified in the target's \"configs\" list, in order.\n"
229 " 3. Direct dependent configs from a breadth-first traversal of the\n"
230 " dependency tree in the order that the targets appear in \"deps\".\n"
231 " 4. All dependent configs from a breadth-first traversal of the\n"
232 " dependency tree in the order that the targets appear in \"deps\".\n"
233 "\n"
234 "Variables valid in a config definition:\n"
235 CONFIG_VALUES_VARS_HELP
236 "\n"
237 "Variables on a target used to apply configs:\n"
238 " all_dependent_configs, configs, direct_dependent_configs,\n"
239 " forward_dependent_configs_from\n"
240 "\n"
241 "Example:\n"
242 " config(\"myconfig\") {\n"
243 " includes = [ \"include/common\" ]\n"
244 " defines = [ \"ENABLE_DOOM_MELON\" ]\n"
245 " }\n"
246 "\n"
247 " executable(\"mything\") {\n"
248 " configs = [ \":myconfig\" ]\n"
249 " }\n";
250
RunConfig(const FunctionCallNode * function,const std::vector<Value> & args,Scope * scope,Err * err)251 Value RunConfig(const FunctionCallNode* function,
252 const std::vector<Value>& args,
253 Scope* scope,
254 Err* err) {
255 if (!EnsureSingleStringArg(function, args, err) ||
256 !EnsureNotProcessingImport(function, scope, err))
257 return Value();
258
259 Label label(MakeLabelForScope(scope, function, args[0].string_value()));
260
261 if (g_scheduler->verbose_logging())
262 g_scheduler->Log("Defining config", label.GetUserVisibleName(true));
263
264 // Create the new config.
265 scoped_ptr<Config> config(new Config(scope->settings(), label));
266 config->set_defined_from(function);
267
268 // Fill it.
269 const SourceDir& input_dir = scope->GetSourceDir();
270 ConfigValuesGenerator gen(&config->config_values(), scope, input_dir, err);
271 gen.Run();
272 if (err->has_error())
273 return Value();
274
275 // Mark as complete.
276 scope->settings()->build_settings()->ItemDefined(config.PassAs<Item>());
277 return Value();
278 }
279
280 // declare_args ----------------------------------------------------------------
281
282 const char kDeclareArgs[] = "declare_args";
283 const char kDeclareArgs_Help[] =
284 "declare_args: Declare build arguments used by this file.\n"
285 "\n"
286 " Introduces the given arguments into the current scope. If they are\n"
287 " not specified on the command line or in a toolchain's arguments,\n"
288 " the default values given in the declare_args block will be used.\n"
289 " However, these defaults will not override command-line values.\n"
290 "\n"
291 " See also \"gn help buildargs\" for an overview.\n"
292 "\n"
293 "Example:\n"
294 " declare_args() {\n"
295 " enable_teleporter = true\n"
296 " enable_doom_melon = false\n"
297 " }\n"
298 "\n"
299 " If you want to override the (default disabled) Doom Melon:\n"
300 " gn --args=\"enable_doom_melon=true enable_teleporter=false\"\n"
301 " This also sets the teleporter, but it's already defaulted to on so\n"
302 " it will have no effect.\n";
303
RunDeclareArgs(Scope * scope,const FunctionCallNode * function,const std::vector<Value> & args,BlockNode * block,Err * err)304 Value RunDeclareArgs(Scope* scope,
305 const FunctionCallNode* function,
306 const std::vector<Value>& args,
307 BlockNode* block,
308 Err* err) {
309 Scope block_scope(scope);
310 block->ExecuteBlockInScope(&block_scope, err);
311 if (err->has_error())
312 return Value();
313
314 // Pass the values from our scope into the Args object for adding to the
315 // scope with the proper values (taking into account the defaults given in
316 // the block_scope, and arguments passed into the build).
317 Scope::KeyValueMap values;
318 block_scope.GetCurrentScopeValues(&values);
319 scope->settings()->build_settings()->build_args().DeclareArgs(
320 values, scope, err);
321 return Value();
322 }
323
324 // defined ---------------------------------------------------------------------
325
326 const char kDefined[] = "defined";
327 const char kDefined_Help[] =
328 "defined: Returns whether an identifier is defined.\n"
329 "\n"
330 " Returns true if the given argument is defined. This is most useful in\n"
331 " templates to assert that the caller set things up properly.\n"
332 "\n"
333 "Example:\n"
334 "\n"
335 " template(\"mytemplate\") {\n"
336 " # To help users call this template properly...\n"
337 " assert(defined(sources), \"Sources must be defined\")\n"
338 "\n"
339 " # If we want to accept an optional \"values\" argument, we don't\n"
340 " # want to dereference something that may not be defined.\n"
341 " if (!defined(outputs)) {\n"
342 " outputs = []\n"
343 " }\n"
344 " }\n";
345
RunDefined(Scope * scope,const FunctionCallNode * function,const ListNode * args_list,Err * err)346 Value RunDefined(Scope* scope,
347 const FunctionCallNode* function,
348 const ListNode* args_list,
349 Err* err) {
350 const std::vector<const ParseNode*>& args_vector = args_list->contents();
351 const IdentifierNode* identifier = NULL;
352 if (args_vector.size() != 1 ||
353 !(identifier = args_vector[0]->AsIdentifier())) {
354 *err = Err(function, "Bad argument to defined().",
355 "defined() takes one argument which should be an identifier.");
356 return Value();
357 }
358
359 if (scope->GetValue(identifier->value().value()))
360 return Value(function, true);
361 return Value(function, false);
362 }
363
364 // import ----------------------------------------------------------------------
365
366 const char kImport[] = "import";
367 const char kImport_Help[] =
368 "import: Import a file into the current scope.\n"
369 "\n"
370 " The import command loads the rules and variables resulting from\n"
371 " executing the given file into the current scope.\n"
372 "\n"
373 " By convention, imported files are named with a .gni extension.\n"
374 "\n"
375 " An import is different than a C++ \"include\". The imported file is\n"
376 " executed in a standalone environment from the caller of the import\n"
377 " command. The results of this execution are cached for other files that\n"
378 " import the same .gni file.\n"
379 "\n"
380 " Note that you can not import a BUILD.gn file that's otherwise used\n"
381 " in the build. Files must either be imported or implicitly loaded as\n"
382 " a result of deps rules, but not both.\n"
383 "\n"
384 " The imported file's scope will be merged with the scope at the point\n"
385 " import was called. If there is a conflict (both the current scope and\n"
386 " the imported file define some variable or rule with the same name but\n"
387 " different value), a runtime error will be thrown. Therefore, it's good\n"
388 " practice to minimize the stuff that an imported file defines.\n"
389 "\n"
390 "Examples:\n"
391 "\n"
392 " import(\"//build/rules/idl_compilation_rule.gni\")\n"
393 "\n"
394 " # Looks in the current directory.\n"
395 " import(\"my_vars.gni\")\n";
396
RunImport(Scope * scope,const FunctionCallNode * function,const std::vector<Value> & args,Err * err)397 Value RunImport(Scope* scope,
398 const FunctionCallNode* function,
399 const std::vector<Value>& args,
400 Err* err) {
401 if (!EnsureSingleStringArg(function, args, err))
402 return Value();
403
404 const SourceDir& input_dir = scope->GetSourceDir();
405 SourceFile import_file =
406 input_dir.ResolveRelativeFile(args[0].string_value());
407 scope->settings()->import_manager().DoImport(import_file, function,
408 scope, err);
409 return Value();
410 }
411
412 // set_sources_assignment_filter -----------------------------------------------
413
414 const char kSetSourcesAssignmentFilter[] = "set_sources_assignment_filter";
415 const char kSetSourcesAssignmentFilter_Help[] =
416 "set_sources_assignment_filter: Set a pattern to filter source files.\n"
417 "\n"
418 " The sources assignment filter is a list of patterns that remove files\n"
419 " from the list implicitly whenever the \"sources\" variable is\n"
420 " assigned to. This is intended to be used to globally filter out files\n"
421 " with platform-specific naming schemes when they don't apply, for\n"
422 " example, you may want to filter out all \"*_win.cc\" files on non-\n"
423 " Windows platforms.\n"
424 "\n"
425 " See \"gn help patterns\" for specifics on patterns.\n"
426 "\n"
427 " Typically this will be called once in the master build config script\n"
428 " to set up the filter for the current platform. Subsequent calls will\n"
429 " overwrite the previous values.\n"
430 "\n"
431 " If you want to bypass the filter and add a file even if it might\n"
432 " be filtered out, call set_sources_assignment_filter([]) to clear the\n"
433 " list of filters. This will apply until the current scope exits\n"
434 "\n"
435 "Example:\n"
436 " # Filter out all _win files.\n"
437 " set_sources_assignment_filter([ \"*_win.cc\", \"*_win.h\" ])\n";
438
RunSetSourcesAssignmentFilter(Scope * scope,const FunctionCallNode * function,const std::vector<Value> & args,Err * err)439 Value RunSetSourcesAssignmentFilter(Scope* scope,
440 const FunctionCallNode* function,
441 const std::vector<Value>& args,
442 Err* err) {
443 if (args.size() != 1) {
444 *err = Err(function, "set_sources_assignment_filter takes one argument.");
445 } else {
446 scoped_ptr<PatternList> f(new PatternList);
447 f->SetFromValue(args[0], err);
448 if (!err->has_error())
449 scope->set_sources_assignment_filter(f.Pass());
450 }
451 return Value();
452 }
453
454 // print -----------------------------------------------------------------------
455
456 const char kPrint[] = "print";
457 const char kPrint_Help[] =
458 "print(...)\n"
459 " Prints all arguments to the console separated by spaces. A newline is\n"
460 " automatically appended to the end.\n"
461 "\n"
462 " This function is intended for debugging. Note that build files are run\n"
463 " in parallel so you may get interleaved prints. A buildfile may also\n"
464 " be executed more than once in parallel in the context of different\n"
465 " toolchains so the prints from one file may be duplicated or\n"
466 " interleaved with itself.\n"
467 "\n"
468 "Examples:\n"
469 " print(\"Hello world\")\n"
470 "\n"
471 " print(sources, deps)\n";
472
RunPrint(Scope * scope,const FunctionCallNode * function,const std::vector<Value> & args,Err * err)473 Value RunPrint(Scope* scope,
474 const FunctionCallNode* function,
475 const std::vector<Value>& args,
476 Err* err) {
477 for (size_t i = 0; i < args.size(); i++) {
478 if (i != 0)
479 std::cout << " ";
480 std::cout << args[i].ToString(false);
481 }
482 std::cout << std::endl;
483 return Value();
484 }
485
486 // -----------------------------------------------------------------------------
487
FunctionInfo()488 FunctionInfo::FunctionInfo()
489 : self_evaluating_args_runner(NULL),
490 generic_block_runner(NULL),
491 executed_block_runner(NULL),
492 no_block_runner(NULL),
493 help(NULL) {
494 }
495
FunctionInfo(SelfEvaluatingArgsFunction seaf,const char * in_help)496 FunctionInfo::FunctionInfo(SelfEvaluatingArgsFunction seaf, const char* in_help)
497 : self_evaluating_args_runner(seaf),
498 generic_block_runner(NULL),
499 executed_block_runner(NULL),
500 no_block_runner(NULL),
501 help(in_help) {
502 }
503
FunctionInfo(GenericBlockFunction gbf,const char * in_help)504 FunctionInfo::FunctionInfo(GenericBlockFunction gbf, const char* in_help)
505 : self_evaluating_args_runner(NULL),
506 generic_block_runner(gbf),
507 executed_block_runner(NULL),
508 no_block_runner(NULL),
509 help(in_help) {
510 }
511
FunctionInfo(ExecutedBlockFunction ebf,const char * in_help)512 FunctionInfo::FunctionInfo(ExecutedBlockFunction ebf, const char* in_help)
513 : self_evaluating_args_runner(NULL),
514 generic_block_runner(NULL),
515 executed_block_runner(ebf),
516 no_block_runner(NULL),
517 help(in_help) {
518 }
519
FunctionInfo(NoBlockFunction nbf,const char * in_help)520 FunctionInfo::FunctionInfo(NoBlockFunction nbf, const char* in_help)
521 : self_evaluating_args_runner(NULL),
522 generic_block_runner(NULL),
523 executed_block_runner(NULL),
524 no_block_runner(nbf),
525 help(in_help) {
526 }
527
528 // Setup the function map via a static initializer. We use this because it
529 // avoids race conditions without having to do some global setup function or
530 // locking-heavy singleton checks at runtime. In practice, we always need this
531 // before we can do anything interesting, so it's OK to wait for the
532 // initializer.
533 struct FunctionInfoInitializer {
534 FunctionInfoMap map;
535
FunctionInfoInitializerfunctions::FunctionInfoInitializer536 FunctionInfoInitializer() {
537 #define INSERT_FUNCTION(command) \
538 map[k##command] = FunctionInfo(&Run##command, k##command##_Help);
539
540 INSERT_FUNCTION(Assert)
541 INSERT_FUNCTION(Component)
542 INSERT_FUNCTION(Config)
543 INSERT_FUNCTION(Copy)
544 INSERT_FUNCTION(Custom)
545 INSERT_FUNCTION(DeclareArgs)
546 INSERT_FUNCTION(Defined)
547 INSERT_FUNCTION(ExecScript)
548 INSERT_FUNCTION(Executable)
549 INSERT_FUNCTION(Group)
550 INSERT_FUNCTION(Import)
551 INSERT_FUNCTION(Print)
552 INSERT_FUNCTION(ProcessFileTemplate)
553 INSERT_FUNCTION(ReadFile)
554 INSERT_FUNCTION(RebasePath)
555 INSERT_FUNCTION(SetDefaults)
556 INSERT_FUNCTION(SetDefaultToolchain)
557 INSERT_FUNCTION(SetSourcesAssignmentFilter)
558 INSERT_FUNCTION(SharedLibrary)
559 INSERT_FUNCTION(SourceSet)
560 INSERT_FUNCTION(StaticLibrary)
561 INSERT_FUNCTION(Template)
562 INSERT_FUNCTION(Test)
563 INSERT_FUNCTION(Tool)
564 INSERT_FUNCTION(Toolchain)
565 INSERT_FUNCTION(ToolchainArgs)
566 INSERT_FUNCTION(WriteFile)
567
568 #undef INSERT_FUNCTION
569 }
570 };
571 const FunctionInfoInitializer function_info;
572
GetFunctions()573 const FunctionInfoMap& GetFunctions() {
574 return function_info.map;
575 }
576
RunFunction(Scope * scope,const FunctionCallNode * function,const ListNode * args_list,BlockNode * block,Err * err)577 Value RunFunction(Scope* scope,
578 const FunctionCallNode* function,
579 const ListNode* args_list,
580 BlockNode* block,
581 Err* err) {
582 const Token& name = function->function();
583
584 const FunctionInfoMap& function_map = GetFunctions();
585 FunctionInfoMap::const_iterator found_function =
586 function_map.find(name.value());
587 if (found_function == function_map.end()) {
588 // No build-in function matching this, check for a template.
589 const FunctionCallNode* rule =
590 scope->GetTemplate(function->function().value().as_string());
591 if (rule) {
592 Value args = args_list->Execute(scope, err);
593 if (err->has_error())
594 return Value();
595 return RunTemplateInvocation(scope, function, args.list_value(), block,
596 rule, err);
597 }
598
599 *err = Err(name, "Unknown function.");
600 return Value();
601 }
602
603 if (found_function->second.self_evaluating_args_runner) {
604 return found_function->second.self_evaluating_args_runner(
605 scope, function, args_list, err);
606 }
607
608 // All other function types take a pre-executed set of args.
609 Value args = args_list->Execute(scope, err);
610 if (err->has_error())
611 return Value();
612
613 if (found_function->second.generic_block_runner) {
614 if (!block) {
615 FillNeedsBlockError(function, err);
616 return Value();
617 }
618 return found_function->second.generic_block_runner(
619 scope, function, args.list_value(), block, err);
620 }
621
622 if (found_function->second.executed_block_runner) {
623 if (!block) {
624 FillNeedsBlockError(function, err);
625 return Value();
626 }
627
628 Scope block_scope(scope);
629 block->ExecuteBlockInScope(&block_scope, err);
630 if (err->has_error())
631 return Value();
632 return found_function->second.executed_block_runner(
633 function, args.list_value(), &block_scope, err);
634 }
635
636 // Otherwise it's a no-block function.
637 return found_function->second.no_block_runner(scope, function,
638 args.list_value(), err);
639 }
640
641 } // namespace functions
642