Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(940)

Unified Diff: runtime/vm/parser.cc

Issue 2010283004: Fix capturing variables in optimized compilations (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Fix skipping closurization Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « runtime/vm/parser.h ('k') | tests/language/regress_26453_test.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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(&params, 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);
« no previous file with comments | « runtime/vm/parser.h ('k') | tests/language/regress_26453_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698