16d528ed9Sopenharmony_ci// Copyright 2014 The Chromium Authors. All rights reserved. 26d528ed9Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be 36d528ed9Sopenharmony_ci// found in the LICENSE file. 46d528ed9Sopenharmony_ci 56d528ed9Sopenharmony_ci#include "gn/template.h" 66d528ed9Sopenharmony_ci 76d528ed9Sopenharmony_ci#include <memory> 86d528ed9Sopenharmony_ci#include <utility> 96d528ed9Sopenharmony_ci 106d528ed9Sopenharmony_ci#include "gn/err.h" 116d528ed9Sopenharmony_ci#include "gn/functions.h" 126d528ed9Sopenharmony_ci#include "gn/parse_tree.h" 136d528ed9Sopenharmony_ci#include "gn/scope.h" 146d528ed9Sopenharmony_ci#include "gn/scope_per_file_provider.h" 156d528ed9Sopenharmony_ci#include "gn/settings.h" 166d528ed9Sopenharmony_ci#include "gn/trace.h" 176d528ed9Sopenharmony_ci#include "gn/value.h" 186d528ed9Sopenharmony_ci#include "gn/variables.h" 196d528ed9Sopenharmony_ci 206d528ed9Sopenharmony_ciTemplate::Template(const Scope* scope, const FunctionCallNode* def) 216d528ed9Sopenharmony_ci : closure_(scope->MakeClosure()), definition_(def) {} 226d528ed9Sopenharmony_ci 236d528ed9Sopenharmony_ciTemplate::Template(std::unique_ptr<Scope> scope, const FunctionCallNode* def) 246d528ed9Sopenharmony_ci : closure_(std::move(scope)), definition_(def) {} 256d528ed9Sopenharmony_ci 266d528ed9Sopenharmony_ciTemplate::~Template() = default; 276d528ed9Sopenharmony_ci 286d528ed9Sopenharmony_ciValue Template::Invoke(Scope* scope, 296d528ed9Sopenharmony_ci const FunctionCallNode* invocation, 306d528ed9Sopenharmony_ci const std::string& template_name, 316d528ed9Sopenharmony_ci const std::vector<Value>& args, 326d528ed9Sopenharmony_ci BlockNode* block, 336d528ed9Sopenharmony_ci Err* err) const { 346d528ed9Sopenharmony_ci // Don't allow templates to be executed from imported files. Imports are for 356d528ed9Sopenharmony_ci // simple values only. 366d528ed9Sopenharmony_ci if (!EnsureNotProcessingImport(invocation, scope, err)) 376d528ed9Sopenharmony_ci return Value(); 386d528ed9Sopenharmony_ci 396d528ed9Sopenharmony_ci ScopedTrace trace(TraceItem::TRACE_FILE_EXECUTE_TEMPLATE, template_name); 406d528ed9Sopenharmony_ci trace.SetToolchain(scope->settings()->toolchain_label()); 416d528ed9Sopenharmony_ci 426d528ed9Sopenharmony_ci // First run the invocation's block. Need to allocate the scope on the heap 436d528ed9Sopenharmony_ci // so we can pass ownership to the template. 446d528ed9Sopenharmony_ci std::unique_ptr<Scope> invocation_scope = std::make_unique<Scope>(scope); 456d528ed9Sopenharmony_ci if (!FillTargetBlockScope(scope, invocation, template_name, block, args, 466d528ed9Sopenharmony_ci invocation_scope.get(), err)) 476d528ed9Sopenharmony_ci return Value(); 486d528ed9Sopenharmony_ci 496d528ed9Sopenharmony_ci { 506d528ed9Sopenharmony_ci // Don't allow the block of the template invocation to include other 516d528ed9Sopenharmony_ci // targets configs, or template invocations. This must only be applied 526d528ed9Sopenharmony_ci // to the invoker's block rather than the whole function because the 536d528ed9Sopenharmony_ci // template execution itself must be able to define targets, etc. 546d528ed9Sopenharmony_ci NonNestableBlock non_nestable(scope, invocation, "template invocation"); 556d528ed9Sopenharmony_ci if (!non_nestable.Enter(err)) 566d528ed9Sopenharmony_ci return Value(); 576d528ed9Sopenharmony_ci 586d528ed9Sopenharmony_ci block->Execute(invocation_scope.get(), err); 596d528ed9Sopenharmony_ci if (err->has_error()) 606d528ed9Sopenharmony_ci return Value(); 616d528ed9Sopenharmony_ci } 626d528ed9Sopenharmony_ci 636d528ed9Sopenharmony_ci // Set up the scope to run the template and set the current directory for the 646d528ed9Sopenharmony_ci // template (which ScopePerFileProvider uses to base the target-related 656d528ed9Sopenharmony_ci // variables target_gen_dir and target_out_dir on) to be that of the invoker. 666d528ed9Sopenharmony_ci // This way, files don't have to be rebased and target_*_dir works the way 676d528ed9Sopenharmony_ci // people expect (otherwise its to easy to be putting generated files in the 686d528ed9Sopenharmony_ci // gen dir corresponding to an imported file). 696d528ed9Sopenharmony_ci Scope template_scope(closure_.get()); 706d528ed9Sopenharmony_ci template_scope.set_source_dir(scope->GetSourceDir()); 716d528ed9Sopenharmony_ci 726d528ed9Sopenharmony_ci // Track the invocation of the template on the template's scope 736d528ed9Sopenharmony_ci template_scope.SetTemplateInvocationEntry( 746d528ed9Sopenharmony_ci template_name, args[0].string_value(), invocation->GetRange().begin()); 756d528ed9Sopenharmony_ci 766d528ed9Sopenharmony_ci // Propagate build dependency files from invoker scope (template scope already 776d528ed9Sopenharmony_ci // propagated via parent scope). 786d528ed9Sopenharmony_ci template_scope.AddBuildDependencyFiles( 796d528ed9Sopenharmony_ci invocation_scope->build_dependency_files()); 806d528ed9Sopenharmony_ci 816d528ed9Sopenharmony_ci ScopePerFileProvider per_file_provider(&template_scope, true); 826d528ed9Sopenharmony_ci 836d528ed9Sopenharmony_ci // Targets defined in the template go in the collector for the invoking file. 846d528ed9Sopenharmony_ci template_scope.set_item_collector(scope->GetItemCollector()); 856d528ed9Sopenharmony_ci 866d528ed9Sopenharmony_ci // We jump through some hoops to avoid copying the invocation scope when 876d528ed9Sopenharmony_ci // setting it in the template scope (since the invocation scope may have 886d528ed9Sopenharmony_ci // large lists of source files in it and could be expensive to copy). 896d528ed9Sopenharmony_ci // 906d528ed9Sopenharmony_ci // Scope.SetValue will copy the value which will in turn copy the scope, but 916d528ed9Sopenharmony_ci // if we instead create a value and then set the scope on it, the copy can 926d528ed9Sopenharmony_ci // be avoided. 936d528ed9Sopenharmony_ci template_scope.SetValue(variables::kInvoker, 946d528ed9Sopenharmony_ci Value(nullptr, std::unique_ptr<Scope>()), invocation); 956d528ed9Sopenharmony_ci Value* invoker_value = template_scope.GetMutableValue( 966d528ed9Sopenharmony_ci variables::kInvoker, Scope::SEARCH_NESTED, false); 976d528ed9Sopenharmony_ci invoker_value->SetScopeValue(std::move(invocation_scope)); 986d528ed9Sopenharmony_ci template_scope.set_source_dir(scope->GetSourceDir()); 996d528ed9Sopenharmony_ci 1006d528ed9Sopenharmony_ci const std::string_view target_name(variables::kTargetName); 1016d528ed9Sopenharmony_ci template_scope.SetValue( 1026d528ed9Sopenharmony_ci target_name, Value(invocation, args[0].string_value()), invocation); 1036d528ed9Sopenharmony_ci 1046d528ed9Sopenharmony_ci // Actually run the template code. 1056d528ed9Sopenharmony_ci Value result = definition_->block()->Execute(&template_scope, err); 1066d528ed9Sopenharmony_ci if (err->has_error()) { 1076d528ed9Sopenharmony_ci // If there was an error, append the caller location so the error message 1086d528ed9Sopenharmony_ci // displays a stack trace of how it got here. 1096d528ed9Sopenharmony_ci err->AppendSubErr(Err(invocation, "whence it was called.")); 1106d528ed9Sopenharmony_ci return Value(); 1116d528ed9Sopenharmony_ci } 1126d528ed9Sopenharmony_ci 1136d528ed9Sopenharmony_ci // Check for unused variables in the invocation scope. This will find typos 1146d528ed9Sopenharmony_ci // of things the caller meant to pass to the template but the template didn't 1156d528ed9Sopenharmony_ci // read out. 1166d528ed9Sopenharmony_ci // 1176d528ed9Sopenharmony_ci // This is a bit tricky because it's theoretically possible for the template 1186d528ed9Sopenharmony_ci // to overwrite the value of "invoker" and free the Scope owned by the 1196d528ed9Sopenharmony_ci // value. So we need to look it up again and don't do anything if it doesn't 1206d528ed9Sopenharmony_ci // exist. 1216d528ed9Sopenharmony_ci invoker_value = template_scope.GetMutableValue(variables::kInvoker, 1226d528ed9Sopenharmony_ci Scope::SEARCH_NESTED, false); 1236d528ed9Sopenharmony_ci if (invoker_value && invoker_value->type() == Value::SCOPE) { 1246d528ed9Sopenharmony_ci if (!invoker_value->scope_value()->CheckForUnusedVars(err)) { 1256d528ed9Sopenharmony_ci // If there was an error, append the caller location so the error message 1266d528ed9Sopenharmony_ci // displays a stack trace of how it got here. 1276d528ed9Sopenharmony_ci err->AppendSubErr(Err(invocation, "whence it was called.")); 1286d528ed9Sopenharmony_ci return Value(); 1296d528ed9Sopenharmony_ci } 1306d528ed9Sopenharmony_ci } 1316d528ed9Sopenharmony_ci 1326d528ed9Sopenharmony_ci // Check for unused variables in the template itself. 1336d528ed9Sopenharmony_ci if (!template_scope.CheckForUnusedVars(err)) { 1346d528ed9Sopenharmony_ci // If there was an error, append the caller location so the error message 1356d528ed9Sopenharmony_ci // displays a stack trace of how it got here. 1366d528ed9Sopenharmony_ci err->AppendSubErr(Err(invocation, "whence it was called.")); 1376d528ed9Sopenharmony_ci return Value(); 1386d528ed9Sopenharmony_ci } 1396d528ed9Sopenharmony_ci 1406d528ed9Sopenharmony_ci return result; 1416d528ed9Sopenharmony_ci} 1426d528ed9Sopenharmony_ci 1436d528ed9Sopenharmony_ciLocationRange Template::GetDefinitionRange() const { 1446d528ed9Sopenharmony_ci return definition_->GetRange(); 1456d528ed9Sopenharmony_ci} 146