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 "gn/scope.h"
6 
7 #include <memory>
8 
9 #include "base/logging.h"
10 #include "gn/parse_tree.h"
11 #include "gn/source_file.h"
12 #include "gn/template.h"
13 
14 namespace {
15 
16 // FLags set in the mode_flags_ of a scope. If a bit is set, it applies
17 // recursively to all dependent scopes.
18 const unsigned kProcessingBuildConfigFlag = 1;
19 const unsigned kProcessingImportFlag = 2;
20 
21 // Returns true if this variable name should be considered private. Private
22 // values start with an underscore, and are not imported from "gni" files
23 // when processing an import.
IsPrivateVar(const std::string_view & name)24 bool IsPrivateVar(const std::string_view& name) {
25   return name.empty() || name[0] == '_';
26 }
27 
28 }  // namespace
29 
30 // Defaults to all false, which are the things least likely to cause errors.
MergeOptions()31 Scope::MergeOptions::MergeOptions()
32     : clobber_existing(false),
33       skip_private_vars(false),
34       mark_dest_used(false) {}
35 
36 Scope::MergeOptions::~MergeOptions() = default;
37 
~ProgrammaticProvider()38 Scope::ProgrammaticProvider::~ProgrammaticProvider() {
39   scope_->RemoveProvider(this);
40 }
41 
Scope(const Settings * settings)42 Scope::Scope(const Settings* settings)
43     : const_containing_(nullptr),
44       mutable_containing_(nullptr),
45       settings_(settings),
46       mode_flags_(0),
47       item_collector_(nullptr) {}
48 
Scope(Scope * parent)49 Scope::Scope(Scope* parent)
50     : const_containing_(nullptr),
51       mutable_containing_(parent),
52       settings_(parent->settings()),
53       mode_flags_(0),
54       item_collector_(nullptr),
55       build_dependency_files_(parent->build_dependency_files_) {}
56 
Scope(const Scope * parent)57 Scope::Scope(const Scope* parent)
58     : const_containing_(parent),
59       mutable_containing_(nullptr),
60       settings_(parent->settings()),
61       mode_flags_(0),
62       item_collector_(nullptr),
63       build_dependency_files_(parent->build_dependency_files_) {}
64 
65 Scope::~Scope() = default;
66 
DetachFromContaining()67 void Scope::DetachFromContaining() {
68   const_containing_ = nullptr;
69   mutable_containing_ = nullptr;
70 }
71 
HasValues(SearchNested search_nested) const72 bool Scope::HasValues(SearchNested search_nested) const {
73   DCHECK(search_nested == SEARCH_CURRENT);
74   return !values_.empty();
75 }
76 
GetValue(const std::string_view & ident,bool counts_as_used)77 const Value* Scope::GetValue(const std::string_view& ident,
78                              bool counts_as_used) {
79   const Scope* found_in_scope = nullptr;
80   return GetValueWithScope(ident, counts_as_used, &found_in_scope);
81 }
82 
GetValueWithScope(const std::string_view & ident,bool counts_as_used,const Scope ** found_in_scope)83 const Value* Scope::GetValueWithScope(const std::string_view& ident,
84                                       bool counts_as_used,
85                                       const Scope** found_in_scope) {
86   // First check for programmatically-provided values.
87   for (auto* provider : programmatic_providers_) {
88     const Value* v = provider->GetProgrammaticValue(ident);
89     if (v) {
90       *found_in_scope = nullptr;
91       return v;
92     }
93   }
94 
95   RecordMap::iterator found = values_.find(ident);
96   if (found != values_.end()) {
97     if (counts_as_used)
98       found->second.used = true;
99     *found_in_scope = this;
100     return &found->second.value;
101   }
102 
103   // Search in the parent scope.
104   if (const_containing_)
105     return const_containing_->GetValueWithScope(ident, found_in_scope);
106   if (mutable_containing_) {
107     return mutable_containing_->GetValueWithScope(ident, counts_as_used,
108                                                   found_in_scope);
109   }
110   return nullptr;
111 }
112 
GetMutableValue(const std::string_view & ident,SearchNested search_mode,bool counts_as_used)113 Value* Scope::GetMutableValue(const std::string_view& ident,
114                               SearchNested search_mode,
115                               bool counts_as_used) {
116   // Don't do programmatic values, which are not mutable.
117   RecordMap::iterator found = values_.find(ident);
118   if (found != values_.end()) {
119     if (counts_as_used)
120       found->second.used = true;
121     return &found->second.value;
122   }
123 
124   // Search in the parent mutable scope if requested, but not const one.
125   if (search_mode == SEARCH_NESTED && mutable_containing_) {
126     return mutable_containing_->GetMutableValue(ident, Scope::SEARCH_NESTED,
127                                                 counts_as_used);
128   }
129   return nullptr;
130 }
131 
GetStorageKey(const std::string_view & ident) const132 std::string_view Scope::GetStorageKey(const std::string_view& ident) const {
133   RecordMap::const_iterator found = values_.find(ident);
134   if (found != values_.end())
135     return found->first;
136 
137   // Search in parent scope.
138   if (containing())
139     return containing()->GetStorageKey(ident);
140   return std::string_view();
141 }
142 
GetValue(const std::string_view & ident) const143 const Value* Scope::GetValue(const std::string_view& ident) const {
144   const Scope* found_in_scope = nullptr;
145   return GetValueWithScope(ident, &found_in_scope);
146 }
147 
GetValueWithScope(const std::string_view & ident,const Scope ** found_in_scope) const148 const Value* Scope::GetValueWithScope(const std::string_view& ident,
149                                       const Scope** found_in_scope) const {
150   RecordMap::const_iterator found = values_.find(ident);
151   if (found != values_.end()) {
152     *found_in_scope = this;
153     return &found->second.value;
154   }
155   if (containing())
156     return containing()->GetValueWithScope(ident, found_in_scope);
157   return nullptr;
158 }
159 
SetValue(const std::string_view & ident,Value v,const ParseNode * set_node)160 Value* Scope::SetValue(const std::string_view& ident,
161                        Value v,
162                        const ParseNode* set_node) {
163   Record& r = values_[ident];  // Clears any existing value.
164   r.value = std::move(v);
165   r.value.set_origin(set_node);
166   return &r.value;
167 }
168 
RemoveIdentifier(const std::string_view & ident)169 void Scope::RemoveIdentifier(const std::string_view& ident) {
170   RecordMap::iterator found = values_.find(ident);
171   if (found != values_.end())
172     values_.erase(found);
173 }
174 
RemovePrivateIdentifiers()175 void Scope::RemovePrivateIdentifiers() {
176   // Do it in two phases to avoid mutating while iterating. Our hash map is
177   // currently backed by several different vendor-specific implementations and
178   // I'm not sure if all of them support mutating while iterating. Since this
179   // is not perf-critical, do the safe thing.
180   std::vector<std::string_view> to_remove;
181   for (const auto& cur : values_) {
182     if (IsPrivateVar(cur.first))
183       to_remove.push_back(cur.first);
184   }
185 
186   for (const auto& cur : to_remove)
187     values_.erase(cur);
188 }
189 
AddTemplate(const std::string & name,const Template * templ)190 bool Scope::AddTemplate(const std::string& name, const Template* templ) {
191   if (GetTemplate(name))
192     return false;
193   templates_[name] = templ;
194   return true;
195 }
196 
GetTemplate(const std::string & name) const197 const Template* Scope::GetTemplate(const std::string& name) const {
198   TemplateMap::const_iterator found = templates_.find(name);
199   if (found != templates_.end())
200     return found->second.get();
201   if (containing())
202     return containing()->GetTemplate(name);
203   return nullptr;
204 }
205 
MarkUsed(const std::string_view & ident)206 void Scope::MarkUsed(const std::string_view& ident) {
207   RecordMap::iterator found = values_.find(ident);
208   if (found == values_.end()) {
209     NOTREACHED();
210     return;
211   }
212   found->second.used = true;
213 }
214 
MarkAllUsed()215 void Scope::MarkAllUsed() {
216   for (auto& cur : values_)
217     cur.second.used = true;
218 }
219 
MarkAllUsed(const std::set<std::string> & excluded_values)220 void Scope::MarkAllUsed(const std::set<std::string>& excluded_values) {
221   for (auto& cur : values_) {
222     if (!excluded_values.empty() &&
223         excluded_values.find(std::string(cur.first)) != excluded_values.end()) {
224       continue;  // Skip this excluded value.
225     }
226     cur.second.used = true;
227   }
228 }
229 
MarkUnused(const std::string_view & ident)230 void Scope::MarkUnused(const std::string_view& ident) {
231   RecordMap::iterator found = values_.find(ident);
232   if (found == values_.end()) {
233     NOTREACHED();
234     return;
235   }
236   found->second.used = false;
237 }
238 
IsSetButUnused(const std::string_view & ident) const239 bool Scope::IsSetButUnused(const std::string_view& ident) const {
240   RecordMap::const_iterator found = values_.find(ident);
241   if (found != values_.end()) {
242     if (!found->second.used) {
243       return true;
244     }
245   }
246   return false;
247 }
248 
CheckForUnusedVars(Err * err) const249 bool Scope::CheckForUnusedVars(Err* err) const {
250   for (const auto& pair : values_) {
251     if (!pair.second.used) {
252       std::string help =
253           "You set the variable \"" + std::string(pair.first) +
254           "\" here and it was unused before it went\nout of scope.";
255 
256       const BinaryOpNode* binary = pair.second.value.origin()->AsBinaryOp();
257       if (binary && binary->op().type() == Token::EQUAL) {
258         // Make a nicer error message for normal var sets.
259         *err =
260             Err(binary->left()->GetRange(), "Assignment had no effect.", help);
261       } else {
262         // This will happen for internally-generated variables.
263         *err =
264             Err(pair.second.value.origin(), "Assignment had no effect.", help);
265       }
266       return false;
267     }
268   }
269   return true;
270 }
271 
GetCurrentScopeValues(KeyValueMap * output) const272 void Scope::GetCurrentScopeValues(KeyValueMap* output) const {
273   for (const auto& pair : values_)
274     (*output)[pair.first] = pair.second.value;
275 }
276 
CheckCurrentScopeValuesEqual(const Scope * other) const277 bool Scope::CheckCurrentScopeValuesEqual(const Scope* other) const {
278   // If there are containing scopes, equality shouldn't work.
279   if (containing()) {
280     return false;
281   }
282   if (values_.size() != other->values_.size()) {
283     return false;
284   }
285   for (const auto& pair : values_) {
286     const Value* v = other->GetValue(pair.first);
287     if (!v || *v != pair.second.value) {
288       return false;
289     }
290   }
291   return true;
292 }
293 
NonRecursiveMergeTo(Scope * dest,const MergeOptions & options,const ParseNode * node_for_err,const char * desc_for_err,Err * err) const294 bool Scope::NonRecursiveMergeTo(Scope* dest,
295                                 const MergeOptions& options,
296                                 const ParseNode* node_for_err,
297                                 const char* desc_for_err,
298                                 Err* err) const {
299   // Values.
300   for (const auto& pair : values_) {
301     const std::string_view& current_name = pair.first;
302     if (options.skip_private_vars && IsPrivateVar(current_name))
303       continue;  // Skip this private var.
304     if (!options.excluded_values.empty() &&
305         options.excluded_values.find(std::string(current_name)) !=
306             options.excluded_values.end()) {
307       continue;  // Skip this excluded value.
308     }
309 
310     const Value& new_value = pair.second.value;
311     if (!options.clobber_existing) {
312       const Value* existing_value = dest->GetValue(current_name);
313       if (existing_value && new_value != *existing_value) {
314         // Value present in both the source and the dest.
315         std::string desc_string(desc_for_err);
316         *err = Err(node_for_err, "Value collision.",
317                    "This " + desc_string + " contains \"" +
318                        std::string(current_name) + "\"");
319         err->AppendSubErr(
320             Err(pair.second.value, "defined here.",
321                 "Which would clobber the one in your current scope"));
322         err->AppendSubErr(
323             Err(*existing_value, "defined here.",
324                 "Executing " + desc_string +
325                     " should not conflict with anything "
326                     "in the current\nscope unless the values are identical."));
327         return false;
328       }
329     }
330     dest->values_[current_name] = pair.second;
331 
332     if (options.mark_dest_used)
333       dest->MarkUsed(current_name);
334   }
335 
336   // Target defaults are owning pointers.
337   for (const auto& pair : target_defaults_) {
338     const std::string& current_name = pair.first;
339     if (!options.excluded_values.empty() &&
340         options.excluded_values.find(current_name) !=
341             options.excluded_values.end()) {
342       continue;  // Skip the excluded value.
343     }
344 
345     if (!options.clobber_existing) {
346       const Scope* dest_defaults = dest->GetTargetDefaults(current_name);
347       if (dest_defaults) {
348         if (RecordMapValuesEqual(pair.second->values_,
349                                  dest_defaults->values_)) {
350           // Values of the two defaults are equivalent, just ignore the
351           // collision.
352           continue;
353         } else {
354           // TODO(brettw) it would be nice to know the origin of a
355           // set_target_defaults so we can give locations for the colliding
356           // target defaults.
357           std::string desc_string(desc_for_err);
358           *err = Err(node_for_err, "Target defaults collision.",
359                      "This " + desc_string +
360                          " contains target defaults for\n"
361                          "\"" +
362                          current_name +
363                          "\" which would clobber one for the\n"
364                          "same target type in your current scope. It's "
365                          "unfortunate that "
366                          "I'm too stupid\nto tell you the location of where "
367                          "the target "
368                          "defaults were set. Usually\nthis happens in the "
369                          "BUILDCONFIG.gn "
370                          "file or in a related .gni file.\n");
371           return false;
372         }
373       }
374     }
375 
376     std::unique_ptr<Scope>& dest_scope = dest->target_defaults_[current_name];
377     dest_scope = std::make_unique<Scope>(settings_);
378     pair.second->NonRecursiveMergeTo(dest_scope.get(), options, node_for_err,
379                                      "<SHOULDN'T HAPPEN>", err);
380   }
381 
382   // Templates.
383   for (const auto& pair : templates_) {
384     const std::string& current_name = pair.first;
385     if (options.skip_private_vars && IsPrivateVar(current_name))
386       continue;  // Skip this private template.
387     if (!options.excluded_values.empty() &&
388         options.excluded_values.find(current_name) !=
389             options.excluded_values.end()) {
390       continue;  // Skip the excluded value.
391     }
392 
393     if (!options.clobber_existing) {
394       const Template* existing_template = dest->GetTemplate(current_name);
395       // Since templates are refcounted, we can check if it's the same one by
396       // comparing pointers.
397       if (existing_template && pair.second.get() != existing_template) {
398         // Rule present in both the source and the dest, and they're not the
399         // same one.
400         std::string desc_string(desc_for_err);
401         *err = Err(node_for_err, "Template collision.",
402                    "This " + desc_string + " contains a template \"" +
403                        current_name + "\"");
404         err->AppendSubErr(
405             Err(pair.second->GetDefinitionRange(), "defined here.",
406                 "Which would clobber the one in your current scope"));
407         err->AppendSubErr(Err(existing_template->GetDefinitionRange(),
408                               "defined here.",
409                               "Executing " + desc_string +
410                                   " should not conflict with anything "
411                                   "in the current\nscope."));
412         return false;
413       }
414     }
415 
416     // Be careful to delete any pointer we're about to clobber.
417     dest->templates_[current_name] = pair.second;
418   }
419 
420   // Propogate build dependency files,
421   dest->AddBuildDependencyFiles(build_dependency_files_);
422 
423   return true;
424 }
425 
MakeClosure() const426 std::unique_ptr<Scope> Scope::MakeClosure() const {
427   std::unique_ptr<Scope> result;
428   if (const_containing_) {
429     // We reached the top of the mutable scope stack. The result scope just
430     // references the const scope (which will never change).
431     result = std::make_unique<Scope>(const_containing_);
432   } else if (mutable_containing_) {
433     // There are more nested mutable scopes. Recursively go up the stack to
434     // get the closure.
435     result = mutable_containing_->MakeClosure();
436   } else {
437     // This is a standalone scope, just copy it.
438     result = std::make_unique<Scope>(settings_);
439   }
440 
441   // Want to clobber since we've flattened some nested scopes, and our parent
442   // scope may have a duplicate value set.
443   MergeOptions options;
444   options.clobber_existing = true;
445 
446   // Add in our variables and we're done.
447   Err err;
448   NonRecursiveMergeTo(result.get(), options, nullptr, "<SHOULDN'T HAPPEN>",
449                       &err);
450   DCHECK(!err.has_error());
451   return result;
452 }
453 
MakeTargetDefaults(const std::string & target_type)454 Scope* Scope::MakeTargetDefaults(const std::string& target_type) {
455   std::unique_ptr<Scope>& dest = target_defaults_[target_type];
456   dest = std::make_unique<Scope>(settings_);
457   return dest.get();
458 }
459 
GetTargetDefaults(const std::string & target_type) const460 const Scope* Scope::GetTargetDefaults(const std::string& target_type) const {
461   NamedScopeMap::const_iterator found = target_defaults_.find(target_type);
462   if (found != target_defaults_.end())
463     return found->second.get();
464   if (containing())
465     return containing()->GetTargetDefaults(target_type);
466   return nullptr;
467 }
468 
SetProcessingBuildConfig()469 void Scope::SetProcessingBuildConfig() {
470   DCHECK((mode_flags_ & kProcessingBuildConfigFlag) == 0);
471   mode_flags_ |= kProcessingBuildConfigFlag;
472 }
473 
ClearProcessingBuildConfig()474 void Scope::ClearProcessingBuildConfig() {
475   DCHECK(mode_flags_ & kProcessingBuildConfigFlag);
476   mode_flags_ &= ~(kProcessingBuildConfigFlag);
477 }
478 
IsProcessingBuildConfig() const479 bool Scope::IsProcessingBuildConfig() const {
480   if (mode_flags_ & kProcessingBuildConfigFlag)
481     return true;
482   if (containing())
483     return containing()->IsProcessingBuildConfig();
484   return false;
485 }
486 
SetProcessingImport()487 void Scope::SetProcessingImport() {
488   DCHECK((mode_flags_ & kProcessingImportFlag) == 0);
489   mode_flags_ |= kProcessingImportFlag;
490 }
491 
ClearProcessingImport()492 void Scope::ClearProcessingImport() {
493   DCHECK(mode_flags_ & kProcessingImportFlag);
494   mode_flags_ &= ~(kProcessingImportFlag);
495 }
496 
IsProcessingImport() const497 bool Scope::IsProcessingImport() const {
498   if (mode_flags_ & kProcessingImportFlag)
499     return true;
500   if (containing())
501     return containing()->IsProcessingImport();
502   return false;
503 }
504 
GetSourceDir() const505 const SourceDir& Scope::GetSourceDir() const {
506   if (!source_dir_.is_null())
507     return source_dir_;
508   if (containing())
509     return containing()->GetSourceDir();
510   return source_dir_;
511 }
512 
AddBuildDependencyFile(const SourceFile & build_dependency_file)513 void Scope::AddBuildDependencyFile(const SourceFile& build_dependency_file) {
514   build_dependency_files_.insert(build_dependency_file);
515 }
516 
AddBuildDependencyFiles(const SourceFileSet & build_dependency_files)517 void Scope::AddBuildDependencyFiles(
518     const SourceFileSet& build_dependency_files) {
519   build_dependency_files_.insert(build_dependency_files.begin(),
520                                  build_dependency_files.end());
521 }
522 
GetItemCollector()523 Scope::ItemVector* Scope::GetItemCollector() {
524   if (item_collector_)
525     return item_collector_;
526   if (mutable_containing())
527     return mutable_containing()->GetItemCollector();
528   return nullptr;
529 }
530 
SetProperty(const void * key,void * value)531 void Scope::SetProperty(const void* key, void* value) {
532   if (!value) {
533     DCHECK(properties_.find(key) != properties_.end());
534     properties_.erase(key);
535   } else {
536     properties_[key] = value;
537   }
538 }
539 
GetProperty(const void * key,const Scope ** found_on_scope) const540 void* Scope::GetProperty(const void* key, const Scope** found_on_scope) const {
541   PropertyMap::const_iterator found = properties_.find(key);
542   if (found != properties_.end()) {
543     if (found_on_scope)
544       *found_on_scope = this;
545     return found->second;
546   }
547   if (containing())
548     return containing()->GetProperty(key, found_on_scope);
549   return nullptr;
550 }
551 
AddProvider(ProgrammaticProvider * p)552 void Scope::AddProvider(ProgrammaticProvider* p) {
553   programmatic_providers_.insert(p);
554 }
555 
RemoveProvider(ProgrammaticProvider * p)556 void Scope::RemoveProvider(ProgrammaticProvider* p) {
557   DCHECK(programmatic_providers_.find(p) != programmatic_providers_.end());
558   programmatic_providers_.erase(p);
559 }
560 
561 // static
RecordMapValuesEqual(const RecordMap & a,const RecordMap & b)562 bool Scope::RecordMapValuesEqual(const RecordMap& a, const RecordMap& b) {
563   if (a.size() != b.size())
564     return false;
565   for (const auto& pair : a) {
566     const auto& found_b = b.find(pair.first);
567     if (found_b == b.end())
568       return false;  // Item in 'a' but not 'b'.
569     if (pair.second.value != found_b->second.value)
570       return false;  // Values for variable in 'a' and 'b' are different.
571   }
572   return true;
573 }
574