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