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