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 "tools/gn/template.h"
6
7#include "tools/gn/err.h"
8#include "tools/gn/functions.h"
9#include "tools/gn/parse_tree.h"
10#include "tools/gn/scope.h"
11#include "tools/gn/scope_per_file_provider.h"
12#include "tools/gn/value.h"
13
14Template::Template(const Scope* scope, const FunctionCallNode* def)
15    : closure_(scope->MakeClosure()),
16      definition_(def) {
17}
18
19Template::Template(scoped_ptr<Scope> scope, const FunctionCallNode* def)
20    : closure_(scope.Pass()),
21      definition_(def) {
22}
23
24Template::~Template() {
25}
26
27Value Template::Invoke(Scope* scope,
28                       const FunctionCallNode* invocation,
29                       const std::vector<Value>& args,
30                       BlockNode* block,
31                       Err* err) const {
32  // Don't allow templates to be executed from imported files. Imports are for
33  // simple values only.
34  if (!EnsureNotProcessingImport(invocation, scope, err))
35    return Value();
36
37  // First run the invocation's block. Need to allocate the scope on the heap
38  // so we can pass ownership to the template.
39  scoped_ptr<Scope> invocation_scope(new Scope(scope));
40  if (!FillTargetBlockScope(scope, invocation,
41                            invocation->function().value().as_string(),
42                            block, args, invocation_scope.get(), err))
43    return Value();
44  block->ExecuteBlockInScope(invocation_scope.get(), err);
45  if (err->has_error())
46    return Value();
47
48  // Set up the scope to run the template and set the current directory for the
49  // template (which ScopePerFileProvider uses to base the target-related
50  // variables target_gen_dir and target_out_dir on) to be that of the invoker.
51  // This way, files don't have to be rebased and target_*_dir works the way
52  // people expect (otherwise its to easy to be putting generated files in the
53  // gen dir corresponding to an imported file).
54  Scope template_scope(closure_.get());
55  template_scope.set_source_dir(scope->GetSourceDir());
56
57  ScopePerFileProvider per_file_provider(&template_scope, true);
58
59  // Targets defined in the template go in the collector for the invoking file.
60  template_scope.set_item_collector(scope->GetItemCollector());
61
62  // We jump through some hoops to avoid copying the invocation scope when
63  // setting it in the template scope (since the invocation scope may have
64  // large lists of source files in it and could be expensive to copy).
65  //
66  // Scope.SetValue will copy the value which will in turn copy the scope, but
67  // if we instead create a value and then set the scope on it, the copy can
68  // be avoided.
69  const char kInvoker[] = "invoker";
70  template_scope.SetValue(kInvoker, Value(NULL, scoped_ptr<Scope>()),
71                          invocation);
72  Value* invoker_value = template_scope.GetMutableValue(kInvoker, false);
73  invoker_value->SetScopeValue(invocation_scope.Pass());
74  template_scope.set_source_dir(scope->GetSourceDir());
75
76  const base::StringPiece target_name("target_name");
77  template_scope.SetValue(target_name,
78                          Value(invocation, args[0].string_value()),
79                          invocation);
80
81  // Actually run the template code.
82  Value result =
83      definition_->block()->ExecuteBlockInScope(&template_scope, err);
84  if (err->has_error()) {
85    // If there was an error, append the caller location so the error message
86    // displays a stack trace of how it got here.
87    err->AppendSubErr(Err(invocation, "whence it was called."));
88    return Value();
89  }
90
91  // Check for unused variables in the invocation scope. This will find typos
92  // of things the caller meant to pass to the template but the template didn't
93  // read out.
94  //
95  // This is a bit tricky because it's theoretically possible for the template
96  // to overwrite the value of "invoker" and free the Scope owned by the
97  // value. So we need to look it up again and don't do anything if it doesn't
98  // exist.
99  invoker_value = template_scope.GetMutableValue(kInvoker, false);
100  if (invoker_value && invoker_value->type() == Value::SCOPE) {
101    if (!invoker_value->scope_value()->CheckForUnusedVars(err))
102      return Value();
103  }
104
105  // Check for unused variables in the template itself.
106  if (!template_scope.CheckForUnusedVars(err))
107    return Value();
108
109  return result;
110}
111
112LocationRange Template::GetDefinitionRange() const {
113  return definition_->GetRange();
114}
115