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