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