diff --git a/src/ast/ast.cc b/src/ast/ast.cc index a2fc8997a4..12e6f20671 100644 --- a/src/ast/ast.cc +++ b/src/ast/ast.cc @@ -204,6 +204,18 @@ VariableProxy::VariableProxy(Zone* zone, const AstRawString* name, raw_name_(name), next_unresolved_(nullptr) {} +VariableProxy::VariableProxy(Zone* zone, const VariableProxy* copy_from) + : Expression(zone, copy_from->position(), kVariableProxy), + bit_field_(copy_from->bit_field_), + end_position_(copy_from->end_position_), + next_unresolved_(nullptr) { + if (copy_from->is_resolved()) { + var_ = copy_from->var_; + } else { + raw_name_ = copy_from->raw_name_; + } +} + void VariableProxy::BindTo(Variable* var) { DCHECK((is_this() && var->is_this()) || raw_name() == var->raw_name()); set_var(var); diff --git a/src/ast/ast.h b/src/ast/ast.h index 19e530c915..85f15fb2d0 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -1690,6 +1690,7 @@ class VariableProxy final : public Expression { VariableProxy(Zone* zone, const AstRawString* name, Variable::Kind variable_kind, int start_position, int end_position); + VariableProxy(Zone* zone, const VariableProxy* copy_from); static int parent_num_ids() { return Expression::num_ids(); } int local_id(int n) const { return base_id() + parent_num_ids() + n; } @@ -3043,27 +3044,22 @@ class AstVisitor BASE_EMBEDDED { class AstNodeFactory final BASE_EMBEDDED { public: explicit AstNodeFactory(AstValueFactory* ast_value_factory) - : local_zone_(nullptr), - parser_zone_(nullptr), - ast_value_factory_(ast_value_factory) { + : zone_(nullptr), ast_value_factory_(ast_value_factory) { if (ast_value_factory != nullptr) { - local_zone_ = ast_value_factory->zone(); - parser_zone_ = ast_value_factory->zone(); + zone_ = ast_value_factory->zone(); } } AstValueFactory* ast_value_factory() const { return ast_value_factory_; } void set_ast_value_factory(AstValueFactory* ast_value_factory) { ast_value_factory_ = ast_value_factory; - local_zone_ = ast_value_factory->zone(); - parser_zone_ = ast_value_factory->zone(); + zone_ = ast_value_factory->zone(); } VariableDeclaration* NewVariableDeclaration(VariableProxy* proxy, VariableMode mode, Scope* scope, int pos) { - return new (parser_zone_) - VariableDeclaration(parser_zone_, proxy, mode, scope, pos); + return new (zone_) VariableDeclaration(zone_, proxy, mode, scope, pos); } FunctionDeclaration* NewFunctionDeclaration(VariableProxy* proxy, @@ -3071,19 +3067,18 @@ class AstNodeFactory final BASE_EMBEDDED { FunctionLiteral* fun, Scope* scope, int pos) { - return new (parser_zone_) - FunctionDeclaration(parser_zone_, proxy, mode, fun, scope, pos); + return new (zone_) FunctionDeclaration(zone_, proxy, mode, fun, scope, pos); } Block* NewBlock(ZoneList* labels, int capacity, bool ignore_completion_value, int pos) { - return new (local_zone_) - Block(local_zone_, labels, capacity, ignore_completion_value, pos); + return new (zone_) + Block(zone_, labels, capacity, ignore_completion_value, pos); } #define STATEMENT_WITH_LABELS(NodeType) \ NodeType* New##NodeType(ZoneList* labels, int pos) { \ - return new (local_zone_) NodeType(local_zone_, labels, pos); \ + return new (zone_) NodeType(zone_, labels, pos); \ } STATEMENT_WITH_LABELS(DoWhileStatement) STATEMENT_WITH_LABELS(WhileStatement) @@ -3096,10 +3091,10 @@ class AstNodeFactory final BASE_EMBEDDED { int pos) { switch (visit_mode) { case ForEachStatement::ENUMERATE: { - return new (local_zone_) ForInStatement(local_zone_, labels, pos); + return new (zone_) ForInStatement(zone_, labels, pos); } case ForEachStatement::ITERATE: { - return new (local_zone_) ForOfStatement(local_zone_, labels, pos); + return new (zone_) ForOfStatement(zone_, labels, pos); } } UNREACHABLE(); @@ -3107,42 +3102,41 @@ class AstNodeFactory final BASE_EMBEDDED { } ExpressionStatement* NewExpressionStatement(Expression* expression, int pos) { - return new (local_zone_) ExpressionStatement(local_zone_, expression, pos); + return new (zone_) ExpressionStatement(zone_, expression, pos); } ContinueStatement* NewContinueStatement(IterationStatement* target, int pos) { - return new (local_zone_) ContinueStatement(local_zone_, target, pos); + return new (zone_) ContinueStatement(zone_, target, pos); } BreakStatement* NewBreakStatement(BreakableStatement* target, int pos) { - return new (local_zone_) BreakStatement(local_zone_, target, pos); + return new (zone_) BreakStatement(zone_, target, pos); } ReturnStatement* NewReturnStatement(Expression* expression, int pos) { - return new (local_zone_) ReturnStatement(local_zone_, expression, pos); + return new (zone_) ReturnStatement(zone_, expression, pos); } WithStatement* NewWithStatement(Scope* scope, Expression* expression, Statement* statement, int pos) { - return new (local_zone_) - WithStatement(local_zone_, scope, expression, statement, pos); + return new (zone_) WithStatement(zone_, scope, expression, statement, pos); } IfStatement* NewIfStatement(Expression* condition, Statement* then_statement, Statement* else_statement, int pos) { - return new (local_zone_) IfStatement(local_zone_, condition, then_statement, - else_statement, pos); + return new (zone_) + IfStatement(zone_, condition, then_statement, else_statement, pos); } TryCatchStatement* NewTryCatchStatement(Block* try_block, Scope* scope, Variable* variable, Block* catch_block, int pos) { - return new (local_zone_) - TryCatchStatement(local_zone_, try_block, scope, variable, catch_block, + return new (zone_) + TryCatchStatement(zone_, try_block, scope, variable, catch_block, HandlerTable::CAUGHT, pos); } @@ -3151,8 +3145,8 @@ class AstNodeFactory final BASE_EMBEDDED { Variable* variable, Block* catch_block, int pos) { - return new (local_zone_) - TryCatchStatement(local_zone_, try_block, scope, variable, catch_block, + return new (zone_) + TryCatchStatement(zone_, try_block, scope, variable, catch_block, HandlerTable::UNCAUGHT, pos); } @@ -3161,88 +3155,81 @@ class AstNodeFactory final BASE_EMBEDDED { Variable* variable, Block* catch_block, int pos) { - return new (local_zone_) - TryCatchStatement(local_zone_, try_block, scope, variable, catch_block, + return new (zone_) + TryCatchStatement(zone_, try_block, scope, variable, catch_block, HandlerTable::PROMISE, pos); } TryFinallyStatement* NewTryFinallyStatement(Block* try_block, Block* finally_block, int pos) { - return new (local_zone_) - TryFinallyStatement(local_zone_, try_block, finally_block, pos); + return new (zone_) + TryFinallyStatement(zone_, try_block, finally_block, pos); } DebuggerStatement* NewDebuggerStatement(int pos) { - return new (local_zone_) DebuggerStatement(local_zone_, pos); + return new (zone_) DebuggerStatement(zone_, pos); } EmptyStatement* NewEmptyStatement(int pos) { - return new (local_zone_) EmptyStatement(local_zone_, pos); + return new (zone_) EmptyStatement(zone_, pos); } SloppyBlockFunctionStatement* NewSloppyBlockFunctionStatement( Statement* statement, Scope* scope) { - return new (parser_zone_) - SloppyBlockFunctionStatement(parser_zone_, statement, scope); + return new (zone_) SloppyBlockFunctionStatement(zone_, statement, scope); } CaseClause* NewCaseClause( Expression* label, ZoneList* statements, int pos) { - return new (local_zone_) CaseClause(local_zone_, label, statements, pos); + return new (zone_) CaseClause(zone_, label, statements, pos); } Literal* NewStringLiteral(const AstRawString* string, int pos) { - return new (local_zone_) - Literal(local_zone_, ast_value_factory_->NewString(string), pos); + return new (zone_) + Literal(zone_, ast_value_factory_->NewString(string), pos); } // A JavaScript symbol (ECMA-262 edition 6). Literal* NewSymbolLiteral(const char* name, int pos) { - return new (local_zone_) - Literal(local_zone_, ast_value_factory_->NewSymbol(name), pos); + return new (zone_) Literal(zone_, ast_value_factory_->NewSymbol(name), pos); } Literal* NewNumberLiteral(double number, int pos, bool with_dot = false) { - return new (local_zone_) Literal( - local_zone_, ast_value_factory_->NewNumber(number, with_dot), pos); + return new (zone_) + Literal(zone_, ast_value_factory_->NewNumber(number, with_dot), pos); } Literal* NewSmiLiteral(int number, int pos) { - return new (local_zone_) - Literal(local_zone_, ast_value_factory_->NewSmi(number), pos); + return new (zone_) Literal(zone_, ast_value_factory_->NewSmi(number), pos); } Literal* NewBooleanLiteral(bool b, int pos) { - return new (local_zone_) - Literal(local_zone_, ast_value_factory_->NewBoolean(b), pos); + return new (zone_) Literal(zone_, ast_value_factory_->NewBoolean(b), pos); } Literal* NewNullLiteral(int pos) { - return new (local_zone_) - Literal(local_zone_, ast_value_factory_->NewNull(), pos); + return new (zone_) Literal(zone_, ast_value_factory_->NewNull(), pos); } Literal* NewUndefinedLiteral(int pos) { - return new (local_zone_) - Literal(local_zone_, ast_value_factory_->NewUndefined(), pos); + return new (zone_) Literal(zone_, ast_value_factory_->NewUndefined(), pos); } Literal* NewTheHoleLiteral(int pos) { - return new (local_zone_) - Literal(local_zone_, ast_value_factory_->NewTheHole(), pos); + return new (zone_) Literal(zone_, ast_value_factory_->NewTheHole(), pos); } ObjectLiteral* NewObjectLiteral( ZoneList* properties, int literal_index, uint32_t boilerplate_properties, int pos) { - return new (local_zone_) ObjectLiteral( - local_zone_, properties, literal_index, boilerplate_properties, pos); + return new (zone_) ObjectLiteral(zone_, properties, literal_index, + boilerplate_properties, pos); } ObjectLiteral::Property* NewObjectLiteralProperty( Expression* key, Expression* value, ObjectLiteralProperty::Kind kind, bool is_static, bool is_computed_name) { - return new (local_zone_) + return new (zone_) ObjectLiteral::Property(key, value, kind, is_static, is_computed_name); } @@ -3250,35 +3237,32 @@ class AstNodeFactory final BASE_EMBEDDED { Expression* value, bool is_static, bool is_computed_name) { - return new (local_zone_) ObjectLiteral::Property( - ast_value_factory_, key, value, is_static, is_computed_name); + return new (zone_) ObjectLiteral::Property(ast_value_factory_, key, value, + is_static, is_computed_name); } RegExpLiteral* NewRegExpLiteral(const AstRawString* pattern, int flags, int literal_index, int pos) { - return new (local_zone_) - RegExpLiteral(local_zone_, pattern, flags, literal_index, pos); + return new (zone_) RegExpLiteral(zone_, pattern, flags, literal_index, pos); } ArrayLiteral* NewArrayLiteral(ZoneList* values, int literal_index, int pos) { - return new (local_zone_) - ArrayLiteral(local_zone_, values, -1, literal_index, pos); + return new (zone_) ArrayLiteral(zone_, values, -1, literal_index, pos); } ArrayLiteral* NewArrayLiteral(ZoneList* values, int first_spread_index, int literal_index, int pos) { - return new (local_zone_) ArrayLiteral( - local_zone_, values, first_spread_index, literal_index, pos); + return new (zone_) + ArrayLiteral(zone_, values, first_spread_index, literal_index, pos); } VariableProxy* NewVariableProxy(Variable* var, int start_position = kNoSourcePosition, int end_position = kNoSourcePosition) { - return new (parser_zone_) - VariableProxy(parser_zone_, var, start_position, end_position); + return new (zone_) VariableProxy(zone_, var, start_position, end_position); } VariableProxy* NewVariableProxy(const AstRawString* name, @@ -3286,87 +3270,89 @@ class AstNodeFactory final BASE_EMBEDDED { int start_position = kNoSourcePosition, int end_position = kNoSourcePosition) { DCHECK_NOT_NULL(name); - return new (parser_zone_) VariableProxy(parser_zone_, name, variable_kind, - start_position, end_position); + return new (zone_) + VariableProxy(zone_, name, variable_kind, start_position, end_position); + } + + // Recreates the VariableProxy in this Zone. + VariableProxy* CopyVariableProxy(VariableProxy* proxy) { + return new (zone_) VariableProxy(zone_, proxy); } Property* NewProperty(Expression* obj, Expression* key, int pos) { - return new (local_zone_) Property(local_zone_, obj, key, pos); + return new (zone_) Property(zone_, obj, key, pos); } Call* NewCall(Expression* expression, ZoneList* arguments, int pos) { - return new (local_zone_) Call(local_zone_, expression, arguments, pos); + return new (zone_) Call(zone_, expression, arguments, pos); } CallNew* NewCallNew(Expression* expression, ZoneList* arguments, int pos) { - return new (local_zone_) CallNew(local_zone_, expression, arguments, pos); + return new (zone_) CallNew(zone_, expression, arguments, pos); } CallRuntime* NewCallRuntime(Runtime::FunctionId id, ZoneList* arguments, int pos) { - return new (local_zone_) - CallRuntime(local_zone_, Runtime::FunctionForId(id), arguments, pos); + return new (zone_) + CallRuntime(zone_, Runtime::FunctionForId(id), arguments, pos); } CallRuntime* NewCallRuntime(const Runtime::Function* function, ZoneList* arguments, int pos) { - return new (local_zone_) CallRuntime(local_zone_, function, arguments, pos); + return new (zone_) CallRuntime(zone_, function, arguments, pos); } CallRuntime* NewCallRuntime(int context_index, ZoneList* arguments, int pos) { - return new (local_zone_) - CallRuntime(local_zone_, context_index, arguments, pos); + return new (zone_) CallRuntime(zone_, context_index, arguments, pos); } UnaryOperation* NewUnaryOperation(Token::Value op, Expression* expression, int pos) { - return new (local_zone_) UnaryOperation(local_zone_, op, expression, pos); + return new (zone_) UnaryOperation(zone_, op, expression, pos); } BinaryOperation* NewBinaryOperation(Token::Value op, Expression* left, Expression* right, int pos) { - return new (local_zone_) BinaryOperation(local_zone_, op, left, right, pos); + return new (zone_) BinaryOperation(zone_, op, left, right, pos); } CountOperation* NewCountOperation(Token::Value op, bool is_prefix, Expression* expr, int pos) { - return new (local_zone_) - CountOperation(local_zone_, op, is_prefix, expr, pos); + return new (zone_) CountOperation(zone_, op, is_prefix, expr, pos); } CompareOperation* NewCompareOperation(Token::Value op, Expression* left, Expression* right, int pos) { - return new (local_zone_) - CompareOperation(local_zone_, op, left, right, pos); + return new (zone_) CompareOperation(zone_, op, left, right, pos); } Spread* NewSpread(Expression* expression, int pos, int expr_pos) { - return new (local_zone_) Spread(local_zone_, expression, pos, expr_pos); + return new (zone_) Spread(zone_, expression, pos, expr_pos); } Conditional* NewConditional(Expression* condition, Expression* then_expression, Expression* else_expression, int position) { - return new (local_zone_) Conditional( - local_zone_, condition, then_expression, else_expression, position); + return new (zone_) Conditional(zone_, condition, then_expression, + else_expression, position); } RewritableExpression* NewRewritableExpression(Expression* expression) { DCHECK_NOT_NULL(expression); - return new (local_zone_) RewritableExpression(local_zone_, expression); + return new (zone_) RewritableExpression(zone_, expression); } Assignment* NewAssignment(Token::Value op, @@ -3374,8 +3360,7 @@ class AstNodeFactory final BASE_EMBEDDED { Expression* value, int pos) { DCHECK(Token::IsAssignmentOp(op)); - Assignment* assign = - new (local_zone_) Assignment(local_zone_, op, target, value, pos); + Assignment* assign = new (zone_) Assignment(zone_, op, target, value, pos); if (assign->is_compound()) { DCHECK(Token::IsAssignmentOp(op)); assign->binary_operation_ = @@ -3387,12 +3372,12 @@ class AstNodeFactory final BASE_EMBEDDED { Yield* NewYield(Expression* generator_object, Expression* expression, int pos, Yield::OnException on_exception) { if (!expression) expression = NewUndefinedLiteral(pos); - return new (local_zone_) - Yield(local_zone_, generator_object, expression, pos, on_exception); + return new (zone_) + Yield(zone_, generator_object, expression, pos, on_exception); } Throw* NewThrow(Expression* exception, int pos) { - return new (local_zone_) Throw(local_zone_, exception, pos); + return new (zone_) Throw(zone_, exception, pos); } FunctionLiteral* NewFunctionLiteral( @@ -3403,8 +3388,8 @@ class AstNodeFactory final BASE_EMBEDDED { FunctionLiteral::FunctionType function_type, FunctionLiteral::EagerCompileHint eager_compile_hint, FunctionKind kind, int position) { - return new (local_zone_) FunctionLiteral( - local_zone_, name, ast_value_factory_, scope, body, + return new (zone_) FunctionLiteral( + zone_, name, ast_value_factory_, scope, body, materialized_literal_count, expected_property_count, parameter_count, function_type, has_duplicate_parameters, eager_compile_hint, kind, position, true); @@ -3416,9 +3401,9 @@ class AstNodeFactory final BASE_EMBEDDED { FunctionLiteral* NewScriptOrEvalFunctionLiteral( Scope* scope, ZoneList* body, int materialized_literal_count, int expected_property_count) { - return new (local_zone_) FunctionLiteral( - local_zone_, ast_value_factory_->empty_string(), ast_value_factory_, - scope, body, materialized_literal_count, expected_property_count, 0, + return new (zone_) FunctionLiteral( + zone_, ast_value_factory_->empty_string(), ast_value_factory_, scope, + body, materialized_literal_count, expected_property_count, 0, FunctionLiteral::kAnonymousExpression, FunctionLiteral::kNoDuplicateParameters, FunctionLiteral::kShouldLazyCompile, FunctionKind::kNormalFunction, 0, @@ -3429,59 +3414,58 @@ class AstNodeFactory final BASE_EMBEDDED { FunctionLiteral* constructor, ZoneList* properties, int start_position, int end_position) { - return new (local_zone_) - ClassLiteral(local_zone_, proxy, extends, constructor, properties, - start_position, end_position); + return new (zone_) ClassLiteral(zone_, proxy, extends, constructor, + properties, start_position, end_position); } NativeFunctionLiteral* NewNativeFunctionLiteral(const AstRawString* name, v8::Extension* extension, int pos) { - return new (local_zone_) - NativeFunctionLiteral(local_zone_, name, extension, pos); + return new (zone_) NativeFunctionLiteral(zone_, name, extension, pos); } DoExpression* NewDoExpression(Block* block, Variable* result_var, int pos) { VariableProxy* result = NewVariableProxy(result_var, pos); - return new (local_zone_) DoExpression(local_zone_, block, result, pos); + return new (zone_) DoExpression(zone_, block, result, pos); } ThisFunction* NewThisFunction(int pos) { - return new (local_zone_) ThisFunction(local_zone_, pos); + return new (zone_) ThisFunction(zone_, pos); } SuperPropertyReference* NewSuperPropertyReference(VariableProxy* this_var, Expression* home_object, int pos) { - return new (local_zone_) - SuperPropertyReference(local_zone_, this_var, home_object, pos); + return new (zone_) + SuperPropertyReference(zone_, this_var, home_object, pos); } SuperCallReference* NewSuperCallReference(VariableProxy* this_var, VariableProxy* new_target_var, VariableProxy* this_function_var, int pos) { - return new (local_zone_) SuperCallReference( - local_zone_, this_var, new_target_var, this_function_var, pos); + return new (zone_) SuperCallReference(zone_, this_var, new_target_var, + this_function_var, pos); } EmptyParentheses* NewEmptyParentheses(int pos) { - return new (local_zone_) EmptyParentheses(local_zone_, pos); + return new (zone_) EmptyParentheses(zone_, pos); } - Zone* zone() const { return local_zone_; } + Zone* zone() const { return zone_; } + void set_zone(Zone* zone) { zone_ = zone; } // Handles use of temporary zones when parsing inner function bodies. class BodyScope { public: BodyScope(AstNodeFactory* factory, Zone* temp_zone, bool use_temp_zone) - : factory_(factory), prev_zone_(factory->local_zone_) { + : factory_(factory), prev_zone_(factory->zone_) { if (use_temp_zone) { - factory->local_zone_ = temp_zone; + factory->zone_ = temp_zone; } } - ~BodyScope() { factory_->local_zone_ = prev_zone_; } + ~BodyScope() { factory_->zone_ = prev_zone_; } private: AstNodeFactory* factory_; @@ -3493,10 +3477,7 @@ class AstNodeFactory final BASE_EMBEDDED { // which we can guarantee is not going to be compiled or have its AST // inspected. // See ParseFunctionLiteral in parser.cc for preconditions. - Zone* local_zone_; - // ZoneObjects which need to persist until scope analysis must be allocated in - // the parser-level zone. - Zone* parser_zone_; + Zone* zone_; AstValueFactory* ast_value_factory_; }; diff --git a/src/ast/scopes.cc b/src/ast/scopes.cc index 3d9fbfbdc0..dfa66609b1 100644 --- a/src/ast/scopes.cc +++ b/src/ast/scopes.cc @@ -279,6 +279,7 @@ bool Scope::Analyze(ParseInfo* info) { scope->Print(); } scope->CheckScopePositions(); + scope->CheckZones(); #endif info->set_scope(scope); @@ -502,9 +503,11 @@ Variable* Scope::LookupFunctionVar(const AstRawString* name, if (index < 0) return NULL; Variable* var = new (zone()) Variable(this, name, mode, Variable::NORMAL, kCreatedInitialized); + DCHECK_NOT_NULL(factory); VariableProxy* proxy = factory->NewVariableProxy(var); VariableDeclaration* declaration = factory->NewVariableDeclaration(proxy, mode, this, kNoSourcePosition); + DCHECK_EQ(factory->zone(), zone()); DeclareFunctionVar(declaration); var->AllocateTo(VariableLocation::CONTEXT, index); return var; @@ -889,6 +892,41 @@ Handle Scope::CollectNonLocals(Handle non_locals) { return non_locals; } +void Scope::AnalyzePartially(Scope* migrate_to, + AstNodeFactory* ast_node_factory) { + // Gather info from inner scopes. + PropagateScopeInfo(false); + + // Try to resolve unresolved variables for this Scope and collect those which + // cannot be resolved inside. It doesn't make sense to try to resolve them in + // the outer Scopes here, because they are incomplete. + VariableProxy* still_unresolved = nullptr; + CollectUnresolvableLocals(&still_unresolved, this); + + // Re-create the VariableProxies in the right Zone and insert them into + // migrate_to. + for (VariableProxy* proxy = still_unresolved; proxy != nullptr; + proxy = proxy->next_unresolved()) { + // Recreate the VariableProxy. + DCHECK(!proxy->is_resolved()); + VariableProxy* copy = ast_node_factory->CopyVariableProxy(proxy); + migrate_to->AddUnresolved(copy); + } + + // Push scope data up to migrate_to. Note that migrate_to and this Scope + // describe the same Scope, just in different Zones. + PropagateUsageFlagsToScope(migrate_to); + if (inner_scope_calls_eval_) { + migrate_to->inner_scope_calls_eval_ = true; + } + DCHECK(!force_eager_compilation_); + migrate_to->set_start_position(start_position_); + migrate_to->set_end_position(end_position_); + migrate_to->language_mode_ = language_mode_; + outer_scope_->RemoveInnerScope(this); + DCHECK_EQ(outer_scope_, migrate_to->outer_scope_); + DCHECK_EQ(outer_scope_->zone(), migrate_to->zone()); +} #ifdef DEBUG static const char* Header(ScopeType scope_type, FunctionKind function_kind, @@ -1097,6 +1135,12 @@ void Scope::CheckScopePositions() { scope->CheckScopePositions(); } } + +void Scope::CheckZones() { + for (Scope* scope = inner_scope_; scope != nullptr; scope = scope->sibling_) { + CHECK_EQ(scope->zone(), zone()); + } +} #endif // DEBUG @@ -1119,10 +1163,10 @@ Variable* Scope::NonLocal(const AstRawString* name, VariableMode mode) { return var; } - Variable* Scope::LookupRecursive(VariableProxy* proxy, BindingKind* binding_kind, - AstNodeFactory* factory) { + AstNodeFactory* factory, + Scope* max_outer_scope) { DCHECK(binding_kind != NULL); if (already_resolved() && is_with_scope()) { // Short-cut: if the scope is deserialized from a scope info, variable @@ -1149,13 +1193,14 @@ Variable* Scope::LookupRecursive(VariableProxy* proxy, var = LookupFunctionVar(proxy->raw_name(), factory); if (var != NULL) { *binding_kind = BOUND; - } else if (outer_scope_ != NULL) { - var = outer_scope_->LookupRecursive(proxy, binding_kind, factory); + } else if (outer_scope_ != nullptr && this != max_outer_scope) { + var = outer_scope_->LookupRecursive(proxy, binding_kind, factory, + max_outer_scope); if (*binding_kind == BOUND && (is_function_scope() || is_with_scope())) { var->ForceContextAllocation(); } } else { - DCHECK(is_script_scope()); + DCHECK(is_script_scope() || this == max_outer_scope); } // "this" can't be shadowed by "eval"-introduced bindings or by "with" scopes. @@ -1288,6 +1333,26 @@ bool Scope::ResolveVariablesRecursively(ParseInfo* info, return true; } +void Scope::CollectUnresolvableLocals(VariableProxy** still_unresolved, + Scope* max_outer_scope) { + BindingKind binding_kind; + for (VariableProxy *proxy = unresolved_, *next = nullptr; proxy != nullptr; + proxy = next) { + next = proxy->next_unresolved(); + // Note that we pass nullptr as AstNodeFactory: this phase should not create + // any new AstNodes, since none of the Scopes involved are backed up by + // ScopeInfo. + if (LookupRecursive(proxy, &binding_kind, nullptr, max_outer_scope) == + nullptr) { + proxy->set_next_unresolved(*still_unresolved); + *still_unresolved = proxy; + } + } + + for (Scope* scope = inner_scope_; scope != nullptr; scope = scope->sibling_) { + scope->CollectUnresolvableLocals(still_unresolved, max_outer_scope); + } +} void Scope::PropagateScopeInfo(bool outer_scope_calls_sloppy_eval ) { if (outer_scope_calls_sloppy_eval) { diff --git a/src/ast/scopes.h b/src/ast/scopes.h index 9ebc7961c6..304b877bfa 100644 --- a/src/ast/scopes.h +++ b/src/ast/scopes.h @@ -201,6 +201,7 @@ class Scope: public ZoneObject { // the same name because they may be removed selectively via // RemoveUnresolved(). DCHECK(!already_resolved()); + DCHECK_EQ(factory->zone(), zone()); VariableProxy* proxy = factory->NewVariableProxy(name, kind, start_position, end_position); proxy->set_next_unresolved(unresolved_); @@ -609,6 +610,13 @@ class Scope: public ZoneObject { return &sloppy_block_function_map_; } + // To be called during parsing. Do just enough scope analysis that we can + // discard the Scope for lazily compiled functions. In particular, this + // records variables which cannot be resolved inside the Scope (we don't yet + // know what they will resolve to since the outer Scopes are incomplete) and + // migrates them into migrate_to. + void AnalyzePartially(Scope* migrate_to, AstNodeFactory* ast_node_factory); + // --------------------------------------------------------------------------- // Debugging. @@ -617,6 +625,9 @@ class Scope: public ZoneObject { // Check that the scope has positions assigned. void CheckScopePositions(); + + // Check that all Scopes in the scope tree use the same Zone. + void CheckZones(); #endif // --------------------------------------------------------------------------- @@ -777,16 +788,23 @@ class Scope: public ZoneObject { }; // Lookup a variable reference given by name recursively starting with this - // scope. If the code is executed because of a call to 'eval', the context - // parameter should be set to the calling context of 'eval'. + // scope, but only until max_outer_scope (if not nullptr). If the code is + // executed because of a call to 'eval', the context parameter should be set + // to the calling context of 'eval'. Variable* LookupRecursive(VariableProxy* proxy, BindingKind* binding_kind, - AstNodeFactory* factory); + AstNodeFactory* factory, + Scope* max_outer_scope = nullptr); MUST_USE_RESULT bool ResolveVariable(ParseInfo* info, VariableProxy* proxy, AstNodeFactory* factory); MUST_USE_RESULT bool ResolveVariablesRecursively(ParseInfo* info, AstNodeFactory* factory); + // Tries to resolve local variables inside max_outer_scope; collects those + // which cannot be resolved. + void CollectUnresolvableLocals(VariableProxy** still_unresolved, + Scope* max_outer_scope); + // Scope analysis. void PropagateScopeInfo(bool outer_scope_calls_sloppy_eval); bool HasTrivialContext() const; diff --git a/src/parsing/parser-base.h b/src/parsing/parser-base.h index 7bde0a721b..10c36aa917 100644 --- a/src/parsing/parser-base.h +++ b/src/parsing/parser-base.h @@ -472,7 +472,11 @@ class ParserBase : public Traits { return &non_patterns_to_rewrite_; } - void next_function_is_parenthesized(bool parenthesized) { + bool next_function_is_parenthesized() const { + return next_function_is_parenthesized_; + } + + void set_next_function_is_parenthesized(bool parenthesized) { next_function_is_parenthesized_ = parenthesized; } @@ -1245,6 +1249,8 @@ class ParserBase : public Traits { bool allow_harmony_async_await_; bool allow_harmony_restrictive_generators_; bool allow_harmony_trailing_commas_; + + friend class DiscardableZoneScope; }; template @@ -1638,8 +1644,8 @@ ParserBase::ParsePrimaryExpression(ExpressionClassifier* classifier, } // Heuristically try to detect immediately called functions before // seeing the call parentheses. - function_state_->next_function_is_parenthesized(peek() == - Token::FUNCTION); + function_state_->set_next_function_is_parenthesized(peek() == + Token::FUNCTION); ExpressionT expr = this->ParseExpression(true, classifier, CHECK_OK); Expect(Token::RPAREN, CHECK_OK); return expr; diff --git a/src/parsing/parser.cc b/src/parsing/parser.cc index c93a052ff4..bfa5e1c27b 100644 --- a/src/parsing/parser.cc +++ b/src/parsing/parser.cc @@ -176,18 +176,26 @@ class DiscardableZoneScope { : ast_node_factory_scope_(parser->factory(), temp_zone, use_temp_zone), fni_(parser->ast_value_factory_, temp_zone), parser_(parser), - prev_fni_(parser->fni_) { + prev_fni_(parser->fni_), + prev_zone_(parser->zone_) { if (use_temp_zone) { parser_->fni_ = &fni_; + parser_->zone_ = temp_zone; } } - ~DiscardableZoneScope() { parser_->fni_ = prev_fni_; } + ~DiscardableZoneScope() { + parser_->fni_ = prev_fni_; + parser_->zone_ = prev_zone_; + } private: AstNodeFactory::BodyScope ast_node_factory_scope_; FuncNameInferrer fni_; Parser* parser_; FuncNameInferrer* prev_fni_; + Zone* prev_zone_; + + DISALLOW_COPY_AND_ASSIGN(DiscardableZoneScope); }; void Parser::SetCachedData(ParseInfo* info) { @@ -4284,29 +4292,113 @@ FunctionLiteral* Parser::ParseFunctionLiteral( function_name = ast_value_factory()->empty_string(); } - Scope* scope = NewFunctionScope(kind); - SetLanguageMode(scope, language_mode); - ZoneList* body = NULL; + FunctionLiteral::EagerCompileHint eager_compile_hint = + function_state_->next_function_is_parenthesized() + ? FunctionLiteral::kShouldEagerCompile + : FunctionLiteral::kShouldLazyCompile; + + // Determine if the function can be parsed lazily. Lazy parsing is + // different from lazy compilation; we need to parse more eagerly than we + // compile. + + // We can only parse lazily if we also compile lazily. The heuristics for lazy + // compilation are: + // - It must not have been prohibited by the caller to Parse (some callers + // need a full AST). + // - The outer scope must allow lazy compilation of inner functions. + // - The function mustn't be a function expression with an open parenthesis + // before; we consider that a hint that the function will be called + // immediately, and it would be a waste of time to make it lazily + // compiled. + // These are all things we can know at this point, without looking at the + // function itself. + + // In addition, we need to distinguish between these cases: + // (function foo() { + // bar = function() { return 1; } + // })(); + // and + // (function foo() { + // var a = 1; + // bar = function() { return a; } + // })(); + + // Now foo will be parsed eagerly and compiled eagerly (optimization: assume + // parenthesis before the function means that it will be called + // immediately). The inner function *must* be parsed eagerly to resolve the + // possible reference to the variable in foo's scope. However, it's possible + // that it will be compiled lazily. + + // To make this additional case work, both Parser and PreParser implement a + // logic where only top-level functions will be parsed lazily. + bool is_lazily_parsed = mode() == PARSE_LAZILY && + this->scope()->AllowsLazyParsing() && + !function_state_->next_function_is_parenthesized(); + + // Determine whether the function body can be discarded after parsing. + // The preconditions are: + // - Lazy compilation has to be enabled. + // - Neither V8 natives nor native function declarations can be allowed, + // since parsing one would retroactively force the function to be + // eagerly compiled. + // - The invoker of this parser can't depend on the AST being eagerly + // built (either because the function is about to be compiled, or + // because the AST is going to be inspected for some reason). + // - Because of the above, we can't be attempting to parse a + // FunctionExpression; even without enclosing parentheses it might be + // immediately invoked. + // - The function literal shouldn't be hinted to eagerly compile. + // - For asm.js functions the body needs to be available when module + // validation is active, because we examine the entire module at once. + bool use_temp_zone = + !is_lazily_parsed && FLAG_lazy && !allow_natives() && + extension_ == NULL && allow_lazy() && + function_type == FunctionLiteral::kDeclaration && + eager_compile_hint != FunctionLiteral::kShouldEagerCompile && + !(FLAG_validate_asm && scope()->asm_module()); + + Scope* main_scope = nullptr; + if (use_temp_zone) { + // This Scope lives in the main Zone; we'll migrate data into it later. + main_scope = NewFunctionScope(kind); + } + + ZoneList* body = nullptr; int arity = -1; int materialized_literal_count = -1; int expected_property_count = -1; DuplicateFinder duplicate_finder(scanner()->unicode_cache()); bool should_be_used_once_hint = false; bool has_duplicate_parameters; - FunctionLiteral::EagerCompileHint eager_compile_hint; - // Parse function. { + // Temporary zones can nest. When we migrate free variables (see below), we + // need to recreate them in the previous Zone. + AstNodeFactory previous_zone_ast_node_factory(ast_value_factory()); + previous_zone_ast_node_factory.set_zone(zone()); + + // Open a new zone scope, which sets our AstNodeFactory to allocate in the + // new temporary zone if the preconditions are satisfied, and ensures that + // the previous zone is always restored after parsing the body. To be able + // to do scope analysis correctly after full parsing, we migrate needed + // information from scope into main_scope when the function has been parsed. + Zone temp_zone(zone()->allocator()); + DiscardableZoneScope zone_scope(this, &temp_zone, use_temp_zone); + + Scope* scope = NewFunctionScope(kind); + SetLanguageMode(scope, language_mode); + if (!use_temp_zone) { + main_scope = scope; + } else { + DCHECK(main_scope->zone() != scope->zone()); + } + FunctionState function_state(&function_state_, &scope_state_, scope, kind); #ifdef DEBUG this->scope()->SetScopeName(function_name); #endif ExpressionClassifier formals_classifier(this, &duplicate_finder); - eager_compile_hint = function_state_->this_function_is_parenthesized() - ? FunctionLiteral::kShouldEagerCompile - : FunctionLiteral::kShouldLazyCompile; - if (is_generator) { // For generators, allocating variables in contexts is currently a win // because it minimizes the work needed to suspend and resume an @@ -4339,43 +4431,6 @@ FunctionLiteral* Parser::ParseFunctionLiteral( // which says whether we need to create an arguments adaptor frame). if (formals.has_rest) arity--; - // Determine if the function can be parsed lazily. Lazy parsing is different - // from lazy compilation; we need to parse more eagerly than we compile. - - // We can only parse lazily if we also compile lazily. The heuristics for - // lazy compilation are: - // - It must not have been prohibited by the caller to Parse (some callers - // need a full AST). - // - The outer scope must allow lazy compilation of inner functions. - // - The function mustn't be a function expression with an open parenthesis - // before; we consider that a hint that the function will be called - // immediately, and it would be a waste of time to make it lazily - // compiled. - // These are all things we can know at this point, without looking at the - // function itself. - - // In addition, we need to distinguish between these cases: - // (function foo() { - // bar = function() { return 1; } - // })(); - // and - // (function foo() { - // var a = 1; - // bar = function() { return a; } - // })(); - - // Now foo will be parsed eagerly and compiled eagerly (optimization: assume - // parenthesis before the function means that it will be called - // immediately). The inner function *must* be parsed eagerly to resolve the - // possible reference to the variable in foo's scope. However, it's possible - // that it will be compiled lazily. - - // To make this additional case work, both Parser and PreParser implement a - // logic where only top-level functions will be parsed lazily. - bool is_lazily_parsed = mode() == PARSE_LAZILY && - this->scope()->AllowsLazyParsing() && - !function_state_->this_function_is_parenthesized(); - // Eager or lazy parse? // If is_lazily_parsed, we'll parse lazy. If we can set a bookmark, we'll // pass it to SkipLazyFunctionBody, which may use it to abort lazy @@ -4404,45 +4459,15 @@ FunctionLiteral* Parser::ParseFunctionLiteral( } } if (!is_lazily_parsed) { - // Determine whether the function body can be discarded after parsing. - // The preconditions are: - // - Lazy compilation has to be enabled. - // - Neither V8 natives nor native function declarations can be allowed, - // since parsing one would retroactively force the function to be - // eagerly compiled. - // - The invoker of this parser can't depend on the AST being eagerly - // built (either because the function is about to be compiled, or - // because the AST is going to be inspected for some reason). - // - Because of the above, we can't be attempting to parse a - // FunctionExpression; even without enclosing parentheses it might be - // immediately invoked. - // - The function literal shouldn't be hinted to eagerly compile. - // - For asm.js functions the body needs to be available when module - // validation is active, because we examine the entire module at once. - bool use_temp_zone = - FLAG_lazy && !allow_natives() && extension_ == NULL && allow_lazy() && - function_type == FunctionLiteral::kDeclaration && - eager_compile_hint != FunctionLiteral::kShouldEagerCompile && - !(FLAG_validate_asm && scope->asm_function()); - // Open a new zone scope, which sets our AstNodeFactory to allocate in the - // new temporary zone if the preconditions are satisfied, and ensures that - // the previous zone is always restored after parsing the body. - // For the purpose of scope analysis, some ZoneObjects allocated by the - // factory must persist after the function body is thrown away and - // temp_zone is deallocated. These objects are instead allocated in a - // parser-persistent zone (see parser_zone_ in AstNodeFactory). - { - Zone temp_zone(zone()->allocator()); - DiscardableZoneScope zone_scope(this, &temp_zone, use_temp_zone); - body = ParseEagerFunctionBody(function_name, pos, formals, kind, - function_type, CHECK_OK); - } + body = ParseEagerFunctionBody(function_name, pos, formals, kind, + function_type, CHECK_OK); + materialized_literal_count = function_state.materialized_literal_count(); expected_property_count = function_state.expected_property_count(); if (use_temp_zone) { // If the preconditions are correct the function body should never be // accessed, but do this anyway for better behaviour if they're wrong. - body = NULL; + body = nullptr; } } @@ -4472,14 +4497,20 @@ FunctionLiteral* Parser::ParseFunctionLiteral( } has_duplicate_parameters = !formals_classifier.is_valid_formal_parameter_list_without_duplicates(); - } + + if (use_temp_zone) { + DCHECK(main_scope != scope); + scope->AnalyzePartially(main_scope, &previous_zone_ast_node_factory); + } + } // DiscardableZoneScope goes out of scope. FunctionLiteral::ParameterFlag duplicate_parameters = has_duplicate_parameters ? FunctionLiteral::kHasDuplicateParameters : FunctionLiteral::kNoDuplicateParameters; + // Note that the FunctionLiteral needs to be created in the main Zone again. FunctionLiteral* function_literal = factory()->NewFunctionLiteral( - function_name, scope, body, materialized_literal_count, + function_name, main_scope, body, materialized_literal_count, expected_property_count, arity, duplicate_parameters, function_type, eager_compile_hint, kind, pos); function_literal->set_function_token_position(function_token_pos); diff --git a/src/parsing/parser.h b/src/parsing/parser.h index 5c7442bb9e..6fbd9126af 100644 --- a/src/parsing/parser.h +++ b/src/parsing/parser.h @@ -724,7 +724,6 @@ class Parser : public ParserBase { private: friend class ParserTraits; - friend class DiscardableZoneScope; // Runtime encoding of different completion modes. enum CompletionKind { diff --git a/test/mjsunit/context-variable-assignments.js b/test/mjsunit/context-variable-assignments.js index 930b969609..f564b8f648 100644 --- a/test/mjsunit/context-variable-assignments.js +++ b/test/mjsunit/context-variable-assignments.js @@ -35,3 +35,19 @@ function foo() { } assertEquals("hello world", foo()); + +// Also test that it works from more deeply nested inner functions: + +var v = (function foo2() { + var a, b; + var bar = function() { + var baz = function() { + a = b = "bye world"; + } + baz(); + } + bar(); + return a; +})(); + +assertEquals("bye world", v);