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