1 // Copyright 2014 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 "gn/template.h"
6 
7 #include <memory>
8 #include <utility>
9 
10 #include "gn/err.h"
11 #include "gn/functions.h"
12 #include "gn/parse_tree.h"
13 #include "gn/scope.h"
14 #include "gn/scope_per_file_provider.h"
15 #include "gn/settings.h"
16 #include "gn/trace.h"
17 #include "gn/value.h"
18 #include "gn/variables.h"
19 
Template(const Scope* scope, const FunctionCallNode* def)20 Template::Template(const Scope* scope, const FunctionCallNode* def)
21     : closure_(scope->MakeClosure()), definition_(def) {}
22 
Template(std::unique_ptr<Scope> scope, const FunctionCallNode* def)23 Template::Template(std::unique_ptr<Scope> scope, const FunctionCallNode* def)
24     : closure_(std::move(scope)), definition_(def) {}
25 
26 Template::~Template() = default;
27 
Invoke(Scope* scope, const FunctionCallNode* invocation, const std::string& template_name, const std::vector<Value>& args, BlockNode* block, Err* err) const28 Value Template::Invoke(Scope* scope,
29                        const FunctionCallNode* invocation,
30                        const std::string& template_name,
31                        const std::vector<Value>& args,
32                        BlockNode* block,
33                        Err* err) const {
34   // Don't allow templates to be executed from imported files. Imports are for
35   // simple values only.
36   if (!EnsureNotProcessingImport(invocation, scope, err))
37     return Value();
38 
39   ScopedTrace trace(TraceItem::TRACE_FILE_EXECUTE_TEMPLATE, template_name);
40   trace.SetToolchain(scope->settings()->toolchain_label());
41 
42   // First run the invocation's block. Need to allocate the scope on the heap
43   // so we can pass ownership to the template.
44   std::unique_ptr<Scope> invocation_scope = std::make_unique<Scope>(scope);
45   if (!FillTargetBlockScope(scope, invocation, template_name, block, args,
46                             invocation_scope.get(), err))
47     return Value();
48 
49   {
50     // Don't allow the block of the template invocation to include other
51     // targets configs, or template invocations. This must only be applied
52     // to the invoker's block rather than the whole function because the
53     // template execution itself must be able to define targets, etc.
54     NonNestableBlock non_nestable(scope, invocation, "template invocation");
55     if (!non_nestable.Enter(err))
56       return Value();
57 
58     block->Execute(invocation_scope.get(), err);
59     if (err->has_error())
60       return Value();
61   }
62 
63   // Set up the scope to run the template and set the current directory for the
64   // template (which ScopePerFileProvider uses to base the target-related
65   // variables target_gen_dir and target_out_dir on) to be that of the invoker.
66   // This way, files don't have to be rebased and target_*_dir works the way
67   // people expect (otherwise its to easy to be putting generated files in the
68   // gen dir corresponding to an imported file).
69   Scope template_scope(closure_.get());
70   template_scope.set_source_dir(scope->GetSourceDir());
71 
72   // Track the invocation of the template on the template's scope
73   template_scope.SetTemplateInvocationEntry(
74       template_name, args[0].string_value(), invocation->GetRange().begin());
75 
76   // Propagate build dependency files from invoker scope (template scope already
77   // propagated via parent scope).
78   template_scope.AddBuildDependencyFiles(
79       invocation_scope->build_dependency_files());
80 
81   ScopePerFileProvider per_file_provider(&template_scope, true);
82 
83   // Targets defined in the template go in the collector for the invoking file.
84   template_scope.set_item_collector(scope->GetItemCollector());
85 
86   // We jump through some hoops to avoid copying the invocation scope when
87   // setting it in the template scope (since the invocation scope may have
88   // large lists of source files in it and could be expensive to copy).
89   //
90   // Scope.SetValue will copy the value which will in turn copy the scope, but
91   // if we instead create a value and then set the scope on it, the copy can
92   // be avoided.
93   template_scope.SetValue(variables::kInvoker,
94                           Value(nullptr, std::unique_ptr<Scope>()), invocation);
95   Value* invoker_value = template_scope.GetMutableValue(
96       variables::kInvoker, Scope::SEARCH_NESTED, false);
97   invoker_value->SetScopeValue(std::move(invocation_scope));
98   template_scope.set_source_dir(scope->GetSourceDir());
99 
100   const std::string_view target_name(variables::kTargetName);
101   template_scope.SetValue(
102       target_name, Value(invocation, args[0].string_value()), invocation);
103 
104   // Actually run the template code.
105   Value result = definition_->block()->Execute(&template_scope, err);
106   if (err->has_error()) {
107     // If there was an error, append the caller location so the error message
108     // displays a stack trace of how it got here.
109     err->AppendSubErr(Err(invocation, "whence it was called."));
110     return Value();
111   }
112 
113   // Check for unused variables in the invocation scope. This will find typos
114   // of things the caller meant to pass to the template but the template didn't
115   // read out.
116   //
117   // This is a bit tricky because it's theoretically possible for the template
118   // to overwrite the value of "invoker" and free the Scope owned by the
119   // value. So we need to look it up again and don't do anything if it doesn't
120   // exist.
121   invoker_value = template_scope.GetMutableValue(variables::kInvoker,
122                                                  Scope::SEARCH_NESTED, false);
123   if (invoker_value && invoker_value->type() == Value::SCOPE) {
124     if (!invoker_value->scope_value()->CheckForUnusedVars(err)) {
125       // If there was an error, append the caller location so the error message
126       // displays a stack trace of how it got here.
127       err->AppendSubErr(Err(invocation, "whence it was called."));
128       return Value();
129     }
130   }
131 
132   // Check for unused variables in the template itself.
133   if (!template_scope.CheckForUnusedVars(err)) {
134     // If there was an error, append the caller location so the error message
135     // displays a stack trace of how it got here.
136     err->AppendSubErr(Err(invocation, "whence it was called."));
137     return Value();
138   }
139 
140   return result;
141 }
142 
GetDefinitionRange() const143 LocationRange Template::GetDefinitionRange() const {
144   return definition_->GetRange();
145 }
146