• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   // Sources assignment filter.
383   if (sources_assignment_filter_) {
384     if (!options.clobber_existing) {
385       if (dest->GetSourcesAssignmentFilter()) {
386         // Sources assignment filter present in both the source and the dest.
387         std::string desc_string(desc_for_err);
388         *err = Err(node_for_err, "Assignment filter collision.",
389                    "The " + desc_string +
390                        " contains a sources_assignment_filter "
391                        "which\nwould clobber the one in your current scope.");
392         return false;
393       }
394     }
395     dest->sources_assignment_filter_ =
396         std::make_unique<PatternList>(*sources_assignment_filter_);
397   }
398 
399   // Templates.
400   for (const auto& pair : templates_) {
401     const std::string& current_name = pair.first;
402     if (options.skip_private_vars && IsPrivateVar(current_name))
403       continue;  // Skip this private template.
404     if (!options.excluded_values.empty() &&
405         options.excluded_values.find(current_name) !=
406             options.excluded_values.end()) {
407       continue;  // Skip the excluded value.
408     }
409 
410     if (!options.clobber_existing) {
411       const Template* existing_template = dest->GetTemplate(current_name);
412       // Since templates are refcounted, we can check if it's the same one by
413       // comparing pointers.
414       if (existing_template && pair.second.get() != existing_template) {
415         // Rule present in both the source and the dest, and they're not the
416         // same one.
417         std::string desc_string(desc_for_err);
418         *err = Err(node_for_err, "Template collision.",
419                    "This " + desc_string + " contains a template \"" +
420                        current_name + "\"");
421         err->AppendSubErr(
422             Err(pair.second->GetDefinitionRange(), "defined here.",
423                 "Which would clobber the one in your current scope"));
424         err->AppendSubErr(Err(existing_template->GetDefinitionRange(),
425                               "defined here.",
426                               "Executing " + desc_string +
427                                   " should not conflict with anything "
428                                   "in the current\nscope."));
429         return false;
430       }
431     }
432 
433     // Be careful to delete any pointer we're about to clobber.
434     dest->templates_[current_name] = pair.second;
435   }
436 
437   // Propogate build dependency files,
438   dest->build_dependency_files_.insert(build_dependency_files_.begin(),
439                                        build_dependency_files_.end());
440 
441   return true;
442 }
443 
MakeClosure() const444 std::unique_ptr<Scope> Scope::MakeClosure() const {
445   std::unique_ptr<Scope> result;
446   if (const_containing_) {
447     // We reached the top of the mutable scope stack. The result scope just
448     // references the const scope (which will never change).
449     result = std::make_unique<Scope>(const_containing_);
450   } else if (mutable_containing_) {
451     // There are more nested mutable scopes. Recursively go up the stack to
452     // get the closure.
453     result = mutable_containing_->MakeClosure();
454   } else {
455     // This is a standalone scope, just copy it.
456     result = std::make_unique<Scope>(settings_);
457   }
458 
459   // Want to clobber since we've flattened some nested scopes, and our parent
460   // scope may have a duplicate value set.
461   MergeOptions options;
462   options.clobber_existing = true;
463 
464   // Add in our variables and we're done.
465   Err err;
466   NonRecursiveMergeTo(result.get(), options, nullptr, "<SHOULDN'T HAPPEN>",
467                       &err);
468   DCHECK(!err.has_error());
469   return result;
470 }
471 
MakeTargetDefaults(const std::string & target_type)472 Scope* Scope::MakeTargetDefaults(const std::string& target_type) {
473   std::unique_ptr<Scope>& dest = target_defaults_[target_type];
474   dest = std::make_unique<Scope>(settings_);
475   return dest.get();
476 }
477 
GetTargetDefaults(const std::string & target_type) const478 const Scope* Scope::GetTargetDefaults(const std::string& target_type) const {
479   NamedScopeMap::const_iterator found = target_defaults_.find(target_type);
480   if (found != target_defaults_.end())
481     return found->second.get();
482   if (containing())
483     return containing()->GetTargetDefaults(target_type);
484   return nullptr;
485 }
486 
GetSourcesAssignmentFilter() const487 const PatternList* Scope::GetSourcesAssignmentFilter() const {
488   if (sources_assignment_filter_)
489     return sources_assignment_filter_.get();
490   if (containing())
491     return containing()->GetSourcesAssignmentFilter();
492   return nullptr;
493 }
494 
SetProcessingBuildConfig()495 void Scope::SetProcessingBuildConfig() {
496   DCHECK((mode_flags_ & kProcessingBuildConfigFlag) == 0);
497   mode_flags_ |= kProcessingBuildConfigFlag;
498 }
499 
ClearProcessingBuildConfig()500 void Scope::ClearProcessingBuildConfig() {
501   DCHECK(mode_flags_ & kProcessingBuildConfigFlag);
502   mode_flags_ &= ~(kProcessingBuildConfigFlag);
503 }
504 
IsProcessingBuildConfig() const505 bool Scope::IsProcessingBuildConfig() const {
506   if (mode_flags_ & kProcessingBuildConfigFlag)
507     return true;
508   if (containing())
509     return containing()->IsProcessingBuildConfig();
510   return false;
511 }
512 
SetProcessingImport()513 void Scope::SetProcessingImport() {
514   DCHECK((mode_flags_ & kProcessingImportFlag) == 0);
515   mode_flags_ |= kProcessingImportFlag;
516 }
517 
ClearProcessingImport()518 void Scope::ClearProcessingImport() {
519   DCHECK(mode_flags_ & kProcessingImportFlag);
520   mode_flags_ &= ~(kProcessingImportFlag);
521 }
522 
IsProcessingImport() const523 bool Scope::IsProcessingImport() const {
524   if (mode_flags_ & kProcessingImportFlag)
525     return true;
526   if (containing())
527     return containing()->IsProcessingImport();
528   return false;
529 }
530 
GetSourceDir() const531 const SourceDir& Scope::GetSourceDir() const {
532   if (!source_dir_.is_null())
533     return source_dir_;
534   if (containing())
535     return containing()->GetSourceDir();
536   return source_dir_;
537 }
538 
AddBuildDependencyFile(const SourceFile & build_dependency_file)539 void Scope::AddBuildDependencyFile(const SourceFile& build_dependency_file) {
540   build_dependency_files_.insert(build_dependency_file);
541 }
542 
GetItemCollector()543 Scope::ItemVector* Scope::GetItemCollector() {
544   if (item_collector_)
545     return item_collector_;
546   if (mutable_containing())
547     return mutable_containing()->GetItemCollector();
548   return nullptr;
549 }
550 
SetProperty(const void * key,void * value)551 void Scope::SetProperty(const void* key, void* value) {
552   if (!value) {
553     DCHECK(properties_.find(key) != properties_.end());
554     properties_.erase(key);
555   } else {
556     properties_[key] = value;
557   }
558 }
559 
GetProperty(const void * key,const Scope ** found_on_scope) const560 void* Scope::GetProperty(const void* key, const Scope** found_on_scope) const {
561   PropertyMap::const_iterator found = properties_.find(key);
562   if (found != properties_.end()) {
563     if (found_on_scope)
564       *found_on_scope = this;
565     return found->second;
566   }
567   if (containing())
568     return containing()->GetProperty(key, found_on_scope);
569   return nullptr;
570 }
571 
AddProvider(ProgrammaticProvider * p)572 void Scope::AddProvider(ProgrammaticProvider* p) {
573   programmatic_providers_.insert(p);
574 }
575 
RemoveProvider(ProgrammaticProvider * p)576 void Scope::RemoveProvider(ProgrammaticProvider* p) {
577   DCHECK(programmatic_providers_.find(p) != programmatic_providers_.end());
578   programmatic_providers_.erase(p);
579 }
580 
581 // static
RecordMapValuesEqual(const RecordMap & a,const RecordMap & b)582 bool Scope::RecordMapValuesEqual(const RecordMap& a, const RecordMap& b) {
583   if (a.size() != b.size())
584     return false;
585   for (const auto& pair : a) {
586     const auto& found_b = b.find(pair.first);
587     if (found_b == b.end())
588       return false;  // Item in 'a' but not 'b'.
589     if (pair.second.value != found_b->second.value)
590       return false;  // Values for variable in 'a' and 'b' are different.
591   }
592   return true;
593 }
594