Index: tools/gn/parse_tree.cc |
diff --git a/tools/gn/parse_tree.cc b/tools/gn/parse_tree.cc |
index dc80514639a883063d284889b2522b10787414d5..ecc438b245aee02c62551efaee117796786dc02f 100644 |
--- a/tools/gn/parse_tree.cc |
+++ b/tools/gn/parse_tree.cc |
@@ -172,12 +172,6 @@ void AccessorNode::Print(std::ostream& out, int indent) const { |
} |
Value AccessorNode::ExecuteArrayAccess(Scope* scope, Err* err) const { |
- Value index_value = index_->Execute(scope, err); |
- if (err->has_error()) |
- return Value(); |
- if (!index_value.VerifyTypeIs(Value::INTEGER, err)) |
- return Value(); |
- |
const Value* base_value = scope->GetValue(base_.value(), true); |
if (!base_value) { |
*err = MakeErrorDescribing("Undefined identifier."); |
@@ -186,29 +180,11 @@ Value AccessorNode::ExecuteArrayAccess(Scope* scope, Err* err) const { |
if (!base_value->VerifyTypeIs(Value::LIST, err)) |
return Value(); |
- int64_t index_int = index_value.int_value(); |
- if (index_int < 0) { |
- *err = Err(index_->GetRange(), "Negative array subscript.", |
- "You gave me " + base::Int64ToString(index_int) + "."); |
+ size_t index = 0; |
+ if (!ComputeAndValidateListIndex(scope, base_value->list_value().size(), |
+ &index, err)) |
return Value(); |
- } |
- size_t index_sizet = static_cast<size_t>(index_int); |
- if (index_sizet >= base_value->list_value().size()) { |
- *err = |
- Err(index_->GetRange(), "Array subscript out of range.", |
- "You gave me " + base::Int64ToString(index_int) + |
- " but I was expecting something from 0 to " + |
- base::Int64ToString( |
- static_cast<int64_t>(base_value->list_value().size()) - 1) + |
- ", inclusive."); |
- return Value(); |
- } |
- |
- // Doing this assumes that there's no way in the language to do anything |
- // between the time the reference is created and the time that the reference |
- // is used. If there is, this will crash! Currently, this is just used for |
- // array accesses where this "shouldn't" happen. |
- return base_value->list_value()[index_sizet]; |
+ return base_value->list_value()[index]; |
} |
Value AccessorNode::ExecuteScopeAccess(Scope* scope, Err* err) const { |
@@ -222,7 +198,8 @@ Value AccessorNode::ExecuteScopeAccess(Scope* scope, Err* err) const { |
const Value* result = nullptr; |
// Look up the value in the scope named by "base_". |
- Value* mutable_base_value = scope->GetMutableValue(base_.value(), true); |
+ Value* mutable_base_value = scope->GetMutableValue( |
+ base_.value(), Scope::SEARCH_NESTED, true); |
if (mutable_base_value) { |
// Common case: base value is mutable so we can track variable accesses |
// for unused value warnings. |
@@ -259,6 +236,35 @@ void AccessorNode::SetNewLocation(int line_number) { |
Location(old.file(), line_number, old.column_number(), old.byte())); |
} |
+bool AccessorNode::ComputeAndValidateListIndex(Scope* scope, |
+ size_t max_len, |
+ size_t* computed_index, |
+ Err* err) const { |
+ Value index_value = index_->Execute(scope, err); |
+ if (err->has_error()) |
+ return false; |
+ if (!index_value.VerifyTypeIs(Value::INTEGER, err)) |
+ return false; |
+ |
+ int64_t index_int = index_value.int_value(); |
+ if (index_int < 0) { |
+ *err = Err(index_->GetRange(), "Negative array subscript.", |
+ "You gave me " + base::Int64ToString(index_int) + "."); |
+ return false; |
+ } |
+ size_t index_sizet = static_cast<size_t>(index_int); |
+ if (index_sizet >= max_len) { |
+ *err = Err(index_->GetRange(), "Array subscript out of range.", |
+ "You gave me " + base::Int64ToString(index_int) + |
+ " but I was expecting something from 0 to " + |
+ base::SizeTToString(max_len) + ", inclusive."); |
+ return false; |
+ } |
+ |
+ *computed_index = index_sizet; |
+ return true; |
+} |
+ |
// BinaryOpNode --------------------------------------------------------------- |
BinaryOpNode::BinaryOpNode() { |
@@ -293,7 +299,7 @@ void BinaryOpNode::Print(std::ostream& out, int indent) const { |
// BlockNode ------------------------------------------------------------------ |
-BlockNode::BlockNode() { |
+BlockNode::BlockNode(ResultMode result_mode) : result_mode_(result_mode) { |
} |
BlockNode::~BlockNode() { |
@@ -303,18 +309,46 @@ const BlockNode* BlockNode::AsBlock() const { |
return this; |
} |
-Value BlockNode::Execute(Scope* scope, Err* err) const { |
+Value BlockNode::Execute(Scope* enclosing_scope, Err* err) const { |
+ std::unique_ptr<Scope> nested_scope; // May be null. |
+ |
+ Scope* execution_scope; // Either the enclosing_scope or nested_scope. |
+ if (result_mode_ == RETURNS_SCOPE) { |
+ // Create a nested scope to save the values for returning. |
+ nested_scope.reset(new Scope(enclosing_scope)); |
+ execution_scope = nested_scope.get(); |
+ } else { |
+ // Use the enclosing scope. Modifications will go into this also (for |
+ // example, if conditions and loops). |
+ execution_scope = enclosing_scope; |
+ } |
+ |
for (size_t i = 0; i < statements_.size() && !err->has_error(); i++) { |
// Check for trying to execute things with no side effects in a block. |
+ // |
+ // A BlockNode here means that somebody has a free-floating { }. |
+ // Technically this can have side effects since it could generated targets, |
+ // but we don't want to allow this since it creates ambiguity when |
+ // immediately following a function call that takes no block. By not |
+ // allowing free-floating blocks that aren't passed anywhere or assigned to |
+ // anything, this ambiguity is resolved. |
const ParseNode* cur = statements_[i].get(); |
if (cur->AsList() || cur->AsLiteral() || cur->AsUnaryOp() || |
- cur->AsIdentifier()) { |
+ cur->AsIdentifier() || cur->AsBlock()) { |
*err = cur->MakeErrorDescribing( |
"This statement has no effect.", |
"Either delete it or do something with the result."); |
return Value(); |
} |
- cur->Execute(scope, err); |
+ cur->Execute(execution_scope, err); |
+ } |
+ |
+ if (result_mode_ == RETURNS_SCOPE) { |
+ // Clear the reference to the containing scope. This will be passed in |
+ // a value whose lifetime will not be related to the enclosing_scope passed |
+ // to this function. |
+ nested_scope->DetachFromContaining(); |
+ return Value(this, std::move(nested_scope)); |
} |
return Value(); |
} |