Index: runtime/vm/parser.cc |
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc |
index a07921f84cd2cc2b83ce83d852d6296271f20c50..1c526729885dd2e6c84107fec63d6ee46ac7557f 100644 |
--- a/runtime/vm/parser.cc |
+++ b/runtime/vm/parser.cc |
@@ -508,6 +508,14 @@ const Function& Parser::innermost_function() const { |
} |
+int Parser::FunctionLevel() const { |
+ if (current_block_ != NULL) { |
+ return current_block_->scope->function_level(); |
+ } |
+ return 0; |
+} |
+ |
+ |
const Class& Parser::current_class() const { |
return current_class_; |
} |
@@ -2369,7 +2377,7 @@ ClosureNode* Parser::CreateImplicitClosureNode(const Function& func, |
// If we create an implicit instance closure from inside a closure of a |
// parameterized class, make sure that the receiver is captured as |
// instantiator. |
- if (current_block_->scope->function_level() > 0) { |
+ if (FunctionLevel() > 0) { |
const Type& signature_type = Type::Handle(Z, |
implicit_closure_function.SignatureType()); |
const Class& scope_class = Class::Handle(Z, signature_type.type_class()); |
@@ -3358,7 +3366,7 @@ SequenceNode* Parser::ParseFunc(const Function& func, bool check_semicolon) { |
AddFormalParamsToScope(¶ms, current_block_->scope); |
if (I->type_checks() && |
- (current_block_->scope->function_level() > 0)) { |
+ (FunctionLevel() > 0)) { |
// We are parsing, but not compiling, a local function. |
// The instantiator may be required at run time for generic type checks. |
if (IsInstantiatorRequired()) { |
@@ -7406,7 +7414,7 @@ LocalVariable* Parser::LookupTypeArgumentsParameter(LocalScope* from_scope, |
void Parser::CaptureInstantiator() { |
- ASSERT(current_block_->scope->function_level() > 0); |
+ ASSERT(FunctionLevel() > 0); |
const String* variable_name = current_function().IsInFactoryScope() ? |
&Symbols::TypeArgumentsParameter() : &Symbols::This(); |
current_block_->scope->CaptureVariable( |
@@ -7643,6 +7651,7 @@ AstNode* Parser::ParseFunctionStatement(bool is_literal) { |
// Note that we cannot share the same closure function between the closurized |
// and non-closurized versions of the same parent function. |
Function& function = Function::ZoneHandle(Z); |
+ bool found_func = true; |
// TODO(hausner): There could be two different closures at the given |
// function_pos, one enclosed in a closurized function and one enclosed in the |
// non-closurized version of this same function. |
@@ -7651,6 +7660,7 @@ AstNode* Parser::ParseFunctionStatement(bool is_literal) { |
// The function will be registered in the lookup table by the |
// EffectGraphVisitor::VisitClosureNode when the newly allocated closure |
// function has been properly setup. |
+ found_func = false; |
function = Function::NewClosureFunction(*function_name, |
innermost_function(), |
function_pos); |
@@ -7700,15 +7710,40 @@ AstNode* Parser::ParseFunctionStatement(bool is_literal) { |
} |
} |
- // Parse the local function. |
- SequenceNode* statements = Parser::ParseFunc(function, !is_literal); |
- INC_STAT(thread(), num_functions_parsed, 1); |
+ Type& signature_type = Type::ZoneHandle(Z); |
+ SequenceNode* statements = NULL; |
+ if (!found_func) { |
+ // Parse the local function. As a side effect of the parsing, the |
+ // variables of this function's scope that are referenced by the local |
+ // function (and its inner nested functions) will be marked as captured. |
- // Now that the local function has formal parameters, lookup the signature |
- Type& signature_type = Type::ZoneHandle(Z, function.SignatureType()); |
- signature_type ^= ClassFinalizer::FinalizeType( |
- current_class(), signature_type, ClassFinalizer::kCanonicalize); |
- function.SetSignatureType(signature_type); |
+ statements = Parser::ParseFunc(function, !is_literal); |
+ INC_STAT(thread(), num_functions_parsed, 1); |
+ |
+ // Now that the local function has formal parameters, lookup the signature |
+ signature_type = function.SignatureType(); |
+ signature_type ^= ClassFinalizer::FinalizeType( |
+ current_class(), signature_type, ClassFinalizer::kCanonicalize); |
+ function.SetSignatureType(signature_type); |
+ } else { |
+ // The local function was parsed before. The captured variables are |
+ // saved in the function's context scope. Iterate over the context scope |
+ // and mark its variables as captured. |
+ const ContextScope& context_scope = |
+ ContextScope::Handle(Z, function.context_scope()); |
+ ASSERT(!context_scope.IsNull()); |
+ String& var_name = String::Handle(Z); |
+ for (int i = 0; i < context_scope.num_variables(); i++) { |
+ var_name = context_scope.NameAt(i); |
+ // We need to look up the name in a way that returns even hidden |
+ // variables, e.g. 'this' in an initializer list. |
+ LocalVariable* v = current_block_->scope->LookupVariable(var_name, true); |
+ ASSERT(v != NULL); |
+ current_block_->scope->CaptureVariable(v); |
+ } |
+ SkipFunctionLiteral(); |
+ signature_type = function.SignatureType(); |
+ } |
// Local functions are registered in the enclosing class, but |
// ignored during class finalization. The enclosing class has |
@@ -7717,7 +7752,7 @@ AstNode* Parser::ParseFunctionStatement(bool is_literal) { |
ASSERT(signature_type.IsFinalized()); |
// Make sure that the instantiator is captured. |
- if ((current_block_->scope->function_level() > 0) && |
+ if ((FunctionLevel() > 0) && |
Class::Handle(signature_type.type_class()).IsGeneric()) { |
CaptureInstantiator(); |
} |
@@ -7763,8 +7798,9 @@ AstNode* Parser::ParseFunctionStatement(bool is_literal) { |
// variables are not relevant for the compilation of the enclosing function. |
// This pruning is done by omitting to hook the local scope in its parent |
// scope in the constructor of LocalScope. |
- AstNode* closure = new(Z) ClosureNode( |
- function_pos, function, NULL, statements->scope()); |
+ AstNode* closure = |
+ new(Z) ClosureNode(function_pos, function, NULL, |
+ statements != NULL ? statements->scope() : NULL); |
if (function_variable == NULL) { |
ASSERT(is_literal); |
@@ -8548,7 +8584,7 @@ void Parser::CheckAsyncOpInTryBlock( |
if (try_stack_ != NULL) { |
LocalScope* scope = try_stack_->try_block()->scope; |
uint16_t try_index = try_stack_->try_index(); |
- const int current_function_level = current_block_->scope->function_level(); |
+ const int current_function_level = FunctionLevel(); |
if (scope->function_level() == current_function_level) { |
// The block declaring :saved_try_ctx_var variable is the parent of the |
// pushed try block. |
@@ -9299,7 +9335,7 @@ void Parser::AddNodeForFinallyInlining(AstNode* node) { |
return; |
} |
ASSERT(node->IsReturnNode() || node->IsJumpNode()); |
- const intptr_t func_level = current_block_->scope->function_level(); |
+ const intptr_t func_level = FunctionLevel(); |
TryStack* iterator = try_stack_; |
while ((iterator != NULL) && |
(iterator->try_block()->scope->function_level() == func_level)) { |
@@ -9329,7 +9365,9 @@ void Parser::AddFinallyClauseToNode(bool is_async, |
InlinedFinallyNode* finally_clause) { |
ReturnNode* return_node = node->AsReturnNode(); |
if (return_node != NULL) { |
- parsed_function()->EnsureFinallyReturnTemp(is_async); |
+ if (FunctionLevel() == 0) { |
+ parsed_function()->EnsureFinallyReturnTemp(is_async); |
+ } |
return_node->AddInlinedFinallyNode(finally_clause); |
return; |
} |
@@ -9444,7 +9482,7 @@ SequenceNode* Parser::ParseCatchClauses( |
// Has a type specification that is not malformed or malbounded. Now |
// form an 'if type check' to guard the catch handler code. |
if (!exception_param.type->IsInstantiated() && |
- (current_block_->scope->function_level() > 0)) { |
+ (FunctionLevel() > 0)) { |
// Make sure that the instantiator is captured. |
CaptureInstantiator(); |
} |
@@ -9859,7 +9897,7 @@ AstNode* Parser::ParseJump(String* label_name) { |
if (jump_kind == Token::kBREAK && target->kind() == SourceLabel::kCase) { |
ReportError(jump_pos, "'break' to case clause label is illegal"); |
} |
- if (target->FunctionLevel() != current_block_->scope->function_level()) { |
+ if (target->FunctionLevel() != FunctionLevel()) { |
ReportError(jump_pos, "'%s' target must be in same function context", |
Token::Str(jump_kind)); |
} |
@@ -10058,17 +10096,16 @@ AstNode* Parser::ParseStatement() { |
if (CurrentToken() != Token::kSEMICOLON) { |
const TokenPosition expr_pos = TokenPos(); |
if (current_function().IsGenerativeConstructor() && |
- (current_block_->scope->function_level() == 0)) { |
+ (FunctionLevel() == 0)) { |
ReportError(expr_pos, |
"return of a value is not allowed in constructors"); |
} else if (current_function().IsGeneratorClosure() && |
- (current_block_->scope->function_level() == 0)) { |
+ (FunctionLevel() == 0)) { |
ReportError(expr_pos, "generator functions may not return a value"); |
} |
AstNode* expr = ParseAwaitableExpr(kAllowConst, kConsumeCascades, NULL); |
if (I->type_checks() && |
- current_function().IsAsyncClosure() && |
- (current_block_->scope->function_level() == 0)) { |
+ current_function().IsAsyncClosure() && (FunctionLevel() == 0)) { |
// In checked mode, when the declared result type is Future<T>, verify |
// that the returned expression is of type T or Future<T> as follows: |
// return temp = expr, temp is Future ? temp as Future<T> : temp as T; |
@@ -10119,7 +10156,7 @@ AstNode* Parser::ParseStatement() { |
statement = new(Z) ReturnNode(statement_pos, expr); |
} else { |
if (current_function().IsSyncGenClosure() && |
- (current_block_->scope->function_level() == 0)) { |
+ (FunctionLevel() == 0)) { |
// In a synchronous generator, return without an expression |
// returns false, signaling that the iterator terminates and |
// did not yield a value. |
@@ -10529,8 +10566,7 @@ AstNode* Parser::ParseBinaryExpr(int min_preced) { |
const TokenPosition type_pos = TokenPos(); |
const AbstractType& type = AbstractType::ZoneHandle(Z, |
ParseType(ClassFinalizer::kCanonicalize)); |
- if (!type.IsInstantiated() && |
- (current_block_->scope->function_level() > 0)) { |
+ if (!type.IsInstantiated() && (FunctionLevel() > 0)) { |
// Make sure that the instantiator is captured. |
CaptureInstantiator(); |
} |
@@ -11451,7 +11487,7 @@ AstNode* Parser::ParseSelectors(AstNode* primary, bool is_cascade) { |
"from static function", |
name.ToCString()); |
} |
- if (current_block_->scope->function_level() > 0) { |
+ if (FunctionLevel() > 0) { |
// Make sure that the instantiator is captured. |
CaptureInstantiator(); |
} |
@@ -11547,7 +11583,7 @@ AstNode* Parser::ParseSelectors(AstNode* primary, bool is_cascade) { |
"from static function", |
name.ToCString()); |
} |
- if (current_block_->scope->function_level() > 0) { |
+ if (FunctionLevel() > 0) { |
// Make sure that the instantiator is captured. |
CaptureInstantiator(); |
} |
@@ -11665,7 +11701,7 @@ AstNode* Parser::ParseSelectors(AstNode* primary, bool is_cascade) { |
"from static function", |
name.ToCString()); |
} |
- if (current_block_->scope->function_level() > 0) { |
+ if (FunctionLevel() > 0) { |
// Make sure that the instantiator is captured. |
CaptureInstantiator(); |
} |
@@ -12498,7 +12534,7 @@ AstNode* Parser::ResolveIdent(TokenPosition ident_pos, |
TypeParameter& type_parameter = TypeParameter::ZoneHandle(Z, |
current_class().LookupTypeParameter(ident)); |
if (!type_parameter.IsNull()) { |
- if (current_block_->scope->function_level() > 0) { |
+ if (FunctionLevel() > 0) { |
// Make sure that the instantiator is captured. |
CaptureInstantiator(); |
} |
@@ -12839,7 +12875,7 @@ AstNode* Parser::ParseListLiteral(TokenPosition type_pos, |
ASSERT(!factory_method.IsNull()); |
if (!list_type_arguments.IsNull() && |
!list_type_arguments.IsInstantiated() && |
- (current_block_->scope->function_level() > 0)) { |
+ (FunctionLevel() > 0)) { |
// Make sure that the instantiator is captured. |
CaptureInstantiator(); |
} |
@@ -13101,7 +13137,7 @@ AstNode* Parser::ParseMapLiteral(TokenPosition type_pos, |
ASSERT(!factory_method.IsNull()); |
if (!map_type_arguments.IsNull() && |
!map_type_arguments.IsInstantiated() && |
- (current_block_->scope->function_level() > 0)) { |
+ (FunctionLevel() > 0)) { |
// Make sure that the instantiator is captured. |
CaptureInstantiator(); |
} |
@@ -13685,7 +13721,7 @@ AstNode* Parser::ParseNewOperator(Token::Kind op_kind) { |
CheckConstructorCallTypeArguments(new_pos, constructor, type_arguments); |
if (!type_arguments.IsNull() && |
!type_arguments.IsInstantiated() && |
- (current_block_->scope->function_level() > 0)) { |
+ (FunctionLevel() > 0)) { |
// Make sure that the instantiator is captured. |
CaptureInstantiator(); |
} |
@@ -14352,6 +14388,7 @@ void Parser::SkipSelectors() { |
void Parser::SkipPostfixExpr() { |
SkipPrimary(); |
if (CurrentToken() == Token::kHASH) { |
+ ConsumeToken(); |
if (IsIdentifier()) { |
ConsumeToken(); |
SkipIf(Token::kASSIGN); |