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/scope.h"
6
7#include "base/logging.h"
8#include "base/stl_util.h"
9#include "tools/gn/parse_tree.h"
10#include "tools/gn/template.h"
11
12namespace {
13
14// FLags set in the mode_flags_ of a scope. If a bit is set, it applies
15// recursively to all dependent scopes.
16const unsigned kProcessingBuildConfigFlag = 1;
17const unsigned kProcessingImportFlag = 2;
18
19// Returns true if this variable name should be considered private. Private
20// values start with an underscore, and are not imported from "gni" files
21// when processing an import.
22bool IsPrivateVar(const base::StringPiece& name) {
23  return name.empty() || name[0] == '_';
24}
25
26}  // namespace
27
28Scope::Scope(const Settings* settings)
29    : const_containing_(NULL),
30      mutable_containing_(NULL),
31      settings_(settings),
32      mode_flags_(0),
33      item_collector_(NULL) {
34}
35
36Scope::Scope(Scope* parent)
37    : const_containing_(NULL),
38      mutable_containing_(parent),
39      settings_(parent->settings()),
40      mode_flags_(0),
41      item_collector_(NULL) {
42}
43
44Scope::Scope(const Scope* parent)
45    : const_containing_(parent),
46      mutable_containing_(NULL),
47      settings_(parent->settings()),
48      mode_flags_(0),
49      item_collector_(NULL) {
50}
51
52Scope::~Scope() {
53  STLDeleteContainerPairSecondPointers(target_defaults_.begin(),
54                                       target_defaults_.end());
55}
56
57const Value* Scope::GetValue(const base::StringPiece& ident,
58                             bool counts_as_used) {
59  // First check for programatically-provided values.
60  for (ProviderSet::const_iterator i = programmatic_providers_.begin();
61       i != programmatic_providers_.end(); ++i) {
62    const Value* v = (*i)->GetProgrammaticValue(ident);
63    if (v)
64      return v;
65  }
66
67  RecordMap::iterator found = values_.find(ident);
68  if (found != values_.end()) {
69    if (counts_as_used)
70      found->second.used = true;
71    return &found->second.value;
72  }
73
74  // Search in the parent scope.
75  if (const_containing_)
76    return const_containing_->GetValue(ident);
77  if (mutable_containing_)
78    return mutable_containing_->GetValue(ident, counts_as_used);
79  return NULL;
80}
81
82Value* Scope::GetMutableValue(const base::StringPiece& ident,
83                              bool counts_as_used) {
84  // Don't do programatic values, which are not mutable.
85  RecordMap::iterator found = values_.find(ident);
86  if (found != values_.end()) {
87    if (counts_as_used)
88      found->second.used = true;
89    return &found->second.value;
90  }
91
92  // Search in the parent mutable scope, but not const one.
93  if (mutable_containing_)
94    return mutable_containing_->GetMutableValue(ident, counts_as_used);
95  return NULL;
96}
97
98Value* Scope::GetValueForcedToCurrentScope(const base::StringPiece& ident,
99                                           const ParseNode* set_node) {
100  RecordMap::iterator found = values_.find(ident);
101  if (found != values_.end())
102    return &found->second.value;  // Already have in the current scope.
103
104  // Search in the parent scope.
105  if (containing()) {
106    const Value* in_containing = containing()->GetValue(ident);
107    if (in_containing) {
108      // Promote to current scope.
109      return SetValue(ident, *in_containing, set_node);
110    }
111  }
112  return NULL;
113}
114
115const Value* Scope::GetValue(const base::StringPiece& ident) const {
116  RecordMap::const_iterator found = values_.find(ident);
117  if (found != values_.end())
118    return &found->second.value;
119  if (containing())
120    return containing()->GetValue(ident);
121  return NULL;
122}
123
124Value* Scope::SetValue(const base::StringPiece& ident,
125                       const Value& v,
126                       const ParseNode* set_node) {
127  Record& r = values_[ident];  // Clears any existing value.
128  r.value = v;
129  r.value.set_origin(set_node);
130  return &r.value;
131}
132
133void Scope::RemoveIdentifier(const base::StringPiece& ident) {
134  RecordMap::iterator found = values_.find(ident);
135  if (found != values_.end())
136    values_.erase(found);
137}
138
139void Scope::RemovePrivateIdentifiers() {
140  // Do it in two phases to avoid mutating while iterating. Our hash map is
141  // currently backed by several different vendor-specific implementations and
142  // I'm not sure if all of them support mutating while iterating. Since this
143  // is not perf-critical, do the safe thing.
144  std::vector<base::StringPiece> to_remove;
145  for (RecordMap::const_iterator i = values_.begin(); i != values_.end(); ++i) {
146    if (IsPrivateVar(i->first))
147      to_remove.push_back(i->first);
148  }
149
150  for (size_t i = 0; i < to_remove.size(); i++)
151    values_.erase(to_remove[i]);
152}
153
154bool Scope::AddTemplate(const std::string& name, const Template* templ) {
155  if (GetTemplate(name))
156    return false;
157  templates_[name] = templ;
158  return true;
159}
160
161const Template* Scope::GetTemplate(const std::string& name) const {
162  TemplateMap::const_iterator found = templates_.find(name);
163  if (found != templates_.end())
164    return found->second.get();
165  if (containing())
166    return containing()->GetTemplate(name);
167  return NULL;
168}
169
170void Scope::MarkUsed(const base::StringPiece& ident) {
171  RecordMap::iterator found = values_.find(ident);
172  if (found == values_.end()) {
173    NOTREACHED();
174    return;
175  }
176  found->second.used = true;
177}
178
179void Scope::MarkUnused(const base::StringPiece& ident) {
180  RecordMap::iterator found = values_.find(ident);
181  if (found == values_.end()) {
182    NOTREACHED();
183    return;
184  }
185  found->second.used = false;
186}
187
188bool Scope::IsSetButUnused(const base::StringPiece& ident) const {
189  RecordMap::const_iterator found = values_.find(ident);
190  if (found != values_.end()) {
191    if (!found->second.used) {
192      return true;
193    }
194  }
195  return false;
196}
197
198bool Scope::CheckForUnusedVars(Err* err) const {
199  for (RecordMap::const_iterator i = values_.begin();
200       i != values_.end(); ++i) {
201    if (!i->second.used) {
202      std::string help = "You set the variable \"" + i->first.as_string() +
203          "\" here and it was unused before it went\nout of scope.";
204
205      const BinaryOpNode* binary = i->second.value.origin()->AsBinaryOp();
206      if (binary && binary->op().type() == Token::EQUAL) {
207        // Make a nicer error message for normal var sets.
208        *err = Err(binary->left()->GetRange(), "Assignment had no effect.",
209                   help);
210      } else {
211        // This will happen for internally-generated variables.
212        *err = Err(i->second.value.origin(), "Assignment had no effect.", help);
213      }
214      return false;
215    }
216  }
217  return true;
218}
219
220void Scope::GetCurrentScopeValues(KeyValueMap* output) const {
221  for (RecordMap::const_iterator i = values_.begin(); i != values_.end(); ++i)
222    (*output)[i->first] = i->second.value;
223}
224
225bool Scope::NonRecursiveMergeTo(Scope* dest,
226                                const MergeOptions& options,
227                                const ParseNode* node_for_err,
228                                const char* desc_for_err,
229                                Err* err) const {
230  // Values.
231  for (RecordMap::const_iterator i = values_.begin(); i != values_.end(); ++i) {
232    if (options.skip_private_vars && IsPrivateVar(i->first))
233      continue;  // Skip this private var.
234
235    const Value& new_value = i->second.value;
236    if (!options.clobber_existing) {
237      const Value* existing_value = dest->GetValue(i->first);
238      if (existing_value && new_value != *existing_value) {
239        // Value present in both the source and the dest.
240        std::string desc_string(desc_for_err);
241        *err = Err(node_for_err, "Value collision.",
242            "This " + desc_string + " contains \"" + i->first.as_string() +
243            "\"");
244        err->AppendSubErr(Err(i->second.value, "defined here.",
245            "Which would clobber the one in your current scope"));
246        err->AppendSubErr(Err(*existing_value, "defined here.",
247            "Executing " + desc_string + " should not conflict with anything "
248            "in the current\nscope unless the values are identical."));
249        return false;
250      }
251    }
252    dest->values_[i->first] = i->second;
253
254    if (options.mark_used)
255      dest->MarkUsed(i->first);
256  }
257
258  // Target defaults are owning pointers.
259  for (NamedScopeMap::const_iterator i = target_defaults_.begin();
260       i != target_defaults_.end(); ++i) {
261    if (!options.clobber_existing) {
262      if (dest->GetTargetDefaults(i->first)) {
263        // TODO(brettw) it would be nice to know the origin of a
264        // set_target_defaults so we can give locations for the colliding target
265        // defaults.
266        std::string desc_string(desc_for_err);
267        *err = Err(node_for_err, "Target defaults collision.",
268            "This " + desc_string + " contains target defaults for\n"
269            "\"" + i->first + "\" which would clobber one for the\n"
270            "same target type in your current scope. It's unfortunate that I'm "
271            "too stupid\nto tell you the location of where the target defaults "
272            "were set. Usually\nthis happens in the BUILDCONFIG.gn file.");
273        return false;
274      }
275    }
276
277    // Be careful to delete any pointer we're about to clobber.
278    Scope** dest_scope = &dest->target_defaults_[i->first];
279    if (*dest_scope)
280      delete *dest_scope;
281    *dest_scope = new Scope(settings_);
282    i->second->NonRecursiveMergeTo(*dest_scope, options, node_for_err,
283                                   "<SHOULDN'T HAPPEN>", err);
284  }
285
286  // Sources assignment filter.
287  if (sources_assignment_filter_) {
288    if (!options.clobber_existing) {
289      if (dest->GetSourcesAssignmentFilter()) {
290        // Sources assignment filter present in both the source and the dest.
291        std::string desc_string(desc_for_err);
292        *err = Err(node_for_err, "Assignment filter collision.",
293            "The " + desc_string + " contains a sources_assignment_filter "
294            "which\nwould clobber the one in your current scope.");
295        return false;
296      }
297    }
298    dest->sources_assignment_filter_.reset(
299        new PatternList(*sources_assignment_filter_));
300  }
301
302  // Templates.
303  for (TemplateMap::const_iterator i = templates_.begin();
304       i != templates_.end(); ++i) {
305    if (options.skip_private_vars && IsPrivateVar(i->first))
306      continue;  // Skip this private template.
307
308    if (!options.clobber_existing) {
309      const Template* existing_template = dest->GetTemplate(i->first);
310      // Since templates are refcounted, we can check if it's the same one by
311      // comparing pointers.
312      if (existing_template && i->second.get() != existing_template) {
313        // Rule present in both the source and the dest, and they're not the
314        // same one.
315        std::string desc_string(desc_for_err);
316        *err = Err(node_for_err, "Template collision.",
317            "This " + desc_string + " contains a template \"" +
318            i->first + "\"");
319        err->AppendSubErr(Err(i->second->GetDefinitionRange(), "defined here.",
320            "Which would clobber the one in your current scope"));
321        err->AppendSubErr(Err(existing_template->GetDefinitionRange(),
322            "defined here.",
323            "Executing " + desc_string + " should not conflict with anything "
324            "in the current\nscope."));
325        return false;
326      }
327    }
328
329    // Be careful to delete any pointer we're about to clobber.
330    dest->templates_[i->first] = i->second;
331  }
332
333  return true;
334}
335
336scoped_ptr<Scope> Scope::MakeClosure() const {
337  scoped_ptr<Scope> result;
338  if (const_containing_) {
339    // We reached the top of the mutable scope stack. The result scope just
340    // references the const scope (which will never change).
341    result.reset(new Scope(const_containing_));
342  } else if (mutable_containing_) {
343    // There are more nested mutable scopes. Recursively go up the stack to
344    // get the closure.
345    result = mutable_containing_->MakeClosure();
346  } else {
347    // This is a standalone scope, just copy it.
348    result.reset(new Scope(settings_));
349  }
350
351  // Want to clobber since we've flattened some nested scopes, and our parent
352  // scope may have a duplicate value set.
353  MergeOptions options;
354  options.clobber_existing = true;
355
356  // Add in our variables and we're done.
357  Err err;
358  NonRecursiveMergeTo(result.get(), options, NULL, "<SHOULDN'T HAPPEN>", &err);
359  DCHECK(!err.has_error());
360  return result.Pass();
361}
362
363Scope* Scope::MakeTargetDefaults(const std::string& target_type) {
364  if (GetTargetDefaults(target_type))
365    return NULL;
366
367  Scope** dest = &target_defaults_[target_type];
368  if (*dest) {
369    NOTREACHED();  // Already set.
370    return *dest;
371  }
372  *dest = new Scope(settings_);
373  return *dest;
374}
375
376const Scope* Scope::GetTargetDefaults(const std::string& target_type) const {
377  NamedScopeMap::const_iterator found = target_defaults_.find(target_type);
378  if (found != target_defaults_.end())
379    return found->second;
380  if (containing())
381    return containing()->GetTargetDefaults(target_type);
382  return NULL;
383}
384
385const PatternList* Scope::GetSourcesAssignmentFilter() const {
386  if (sources_assignment_filter_)
387    return sources_assignment_filter_.get();
388  if (containing())
389    return containing()->GetSourcesAssignmentFilter();
390  return NULL;
391}
392
393void Scope::SetProcessingBuildConfig() {
394  DCHECK((mode_flags_ & kProcessingBuildConfigFlag) == 0);
395  mode_flags_ |= kProcessingBuildConfigFlag;
396}
397
398void Scope::ClearProcessingBuildConfig() {
399  DCHECK(mode_flags_ & kProcessingBuildConfigFlag);
400  mode_flags_ &= ~(kProcessingBuildConfigFlag);
401}
402
403bool Scope::IsProcessingBuildConfig() const {
404  if (mode_flags_ & kProcessingBuildConfigFlag)
405    return true;
406  if (containing())
407    return containing()->IsProcessingBuildConfig();
408  return false;
409}
410
411void Scope::SetProcessingImport() {
412  DCHECK((mode_flags_ & kProcessingImportFlag) == 0);
413  mode_flags_ |= kProcessingImportFlag;
414}
415
416void Scope::ClearProcessingImport() {
417  DCHECK(mode_flags_ & kProcessingImportFlag);
418  mode_flags_ &= ~(kProcessingImportFlag);
419}
420
421bool Scope::IsProcessingImport() const {
422  if (mode_flags_ & kProcessingImportFlag)
423    return true;
424  if (containing())
425    return containing()->IsProcessingImport();
426  return false;
427}
428
429const SourceDir& Scope::GetSourceDir() const {
430  if (!source_dir_.is_null())
431    return source_dir_;
432  if (containing())
433    return containing()->GetSourceDir();
434  return source_dir_;
435}
436
437Scope::ItemVector* Scope::GetItemCollector() {
438  if (item_collector_)
439    return item_collector_;
440  if (mutable_containing())
441    return mutable_containing()->GetItemCollector();
442  return NULL;
443}
444
445void Scope::SetProperty(const void* key, void* value) {
446  if (!value) {
447    DCHECK(properties_.find(key) != properties_.end());
448    properties_.erase(key);
449  } else {
450    properties_[key] = value;
451  }
452}
453
454void* Scope::GetProperty(const void* key, const Scope** found_on_scope) const {
455  PropertyMap::const_iterator found = properties_.find(key);
456  if (found != properties_.end()) {
457    if (found_on_scope)
458      *found_on_scope = this;
459    return found->second;
460  }
461  if (containing())
462    return containing()->GetProperty(key, found_on_scope);
463  return NULL;
464}
465
466void Scope::AddProvider(ProgrammaticProvider* p) {
467  programmatic_providers_.insert(p);
468}
469
470void Scope::RemoveProvider(ProgrammaticProvider* p) {
471  DCHECK(programmatic_providers_.find(p) != programmatic_providers_.end());
472  programmatic_providers_.erase(p);
473}
474