Commit 80567914 authored by neis's avatar neis Committed by Commit bot

[parsing] Fix context allocation for async functions.

For generator-based functions (e.g. async functions) we force variables to be
context-allocated.  Due to a bug in the parser, this didn't always work
correctly.  For instance, in "async function foo([a]) { ... }" the variable "a"
could become stack-allocated due to context allocation being forced on the wrong
scope.

Besides fixing this, I'm also cleaning up some related code in the async parsing
setup and adding some guards.

R=adamk@chromium.org, littledan@chromium.org
BUG=

Review-Url: https://codereview.chromium.org/2561093002
Cr-Commit-Position: refs/heads/master@{#41635}
parent d024df4d
...@@ -419,8 +419,9 @@ class ParserBase { ...@@ -419,8 +419,9 @@ class ParserBase {
FunctionState* outer() const { return outer_function_state_; } FunctionState* outer() const { return outer_function_state_; }
void set_generator_object_variable(typename Types::Variable* variable) { void set_generator_object_variable(typename Types::Variable* variable) {
DCHECK(variable != NULL); DCHECK_NOT_NULL(variable);
DCHECK(IsResumableFunction(kind())); DCHECK(IsResumableFunction(kind()));
DCHECK(scope()->has_forced_context_allocation());
generator_object_variable_ = variable; generator_object_variable_ = variable;
} }
typename Types::Variable* generator_object_variable() const { typename Types::Variable* generator_object_variable() const {
...@@ -4172,8 +4173,6 @@ void ParserBase<Impl>::ParseAsyncFunctionBody(Scope* scope, StatementListT body, ...@@ -4172,8 +4173,6 @@ void ParserBase<Impl>::ParseAsyncFunctionBody(Scope* scope, StatementListT body,
FunctionBodyType body_type, FunctionBodyType body_type,
bool accept_IN, int pos, bool accept_IN, int pos,
bool* ok) { bool* ok) {
scope->ForceContextAllocation();
impl()->PrepareAsyncFunctionBody(body, kind, pos); impl()->PrepareAsyncFunctionBody(body, kind, pos);
BlockT block = factory()->NewBlock(nullptr, 8, true, kNoSourcePosition); BlockT block = factory()->NewBlock(nullptr, 8, true, kNoSourcePosition);
......
...@@ -754,7 +754,7 @@ FunctionLiteral* Parser::DoParseProgram(ParseInfo* info) { ...@@ -754,7 +754,7 @@ FunctionLiteral* Parser::DoParseProgram(ParseInfo* info) {
DCHECK(!is_duplicate); DCHECK(!is_duplicate);
var->AllocateTo(VariableLocation::PARAMETER, 0); var->AllocateTo(VariableLocation::PARAMETER, 0);
PrepareGeneratorVariables(&function_state); PrepareGeneratorVariables();
Expression* initial_yield = Expression* initial_yield =
BuildInitialYield(kNoSourcePosition, kGeneratorFunction); BuildInitialYield(kNoSourcePosition, kGeneratorFunction);
body->Add( body->Add(
...@@ -2505,19 +2505,19 @@ void Parser::ReindexLiterals(const ParserFormalParameters& parameters) { ...@@ -2505,19 +2505,19 @@ void Parser::ReindexLiterals(const ParserFormalParameters& parameters) {
} }
} }
void Parser::PrepareGeneratorVariables(FunctionState* function_state) { void Parser::PrepareGeneratorVariables() {
// For generators, allocating variables in contexts is currently a win because // For generators, allocating variables in contexts is currently a win because
// it minimizes the work needed to suspend and resume an activation. The // it minimizes the work needed to suspend and resume an activation. The
// code produced for generators relies on this forced context allocation, but // code produced for generators relies on this forced context allocation (it
// not in an essential way. // does not restore the frame's parameters upon resume).
scope()->ForceContextAllocation(); function_state_->scope()->ForceContextAllocation();
// Calling a generator returns a generator object. That object is stored // Calling a generator returns a generator object. That object is stored
// in a temporary variable, a definition that is used by "yield" // in a temporary variable, a definition that is used by "yield"
// expressions. // expressions.
Variable* temp = Variable* temp =
NewTemporary(ast_value_factory()->dot_generator_object_string()); NewTemporary(ast_value_factory()->dot_generator_object_string());
function_state->set_generator_object_variable(temp); function_state_->set_generator_object_variable(temp);
} }
FunctionLiteral* Parser::ParseFunctionLiteral( FunctionLiteral* Parser::ParseFunctionLiteral(
...@@ -3075,15 +3075,20 @@ Block* Parser::BuildRejectPromiseOnException(Block* inner_block, bool* ok) { ...@@ -3075,15 +3075,20 @@ Block* Parser::BuildRejectPromiseOnException(Block* inner_block, bool* ok) {
return result; return result;
} }
Expression* Parser::BuildCreateJSGeneratorObject(int pos, FunctionKind kind) { Assignment* Parser::BuildCreateJSGeneratorObject(int pos, FunctionKind kind) {
// .generator = %CreateJSGeneratorObject(...);
DCHECK_NOT_NULL(function_state_->generator_object_variable()); DCHECK_NOT_NULL(function_state_->generator_object_variable());
ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(2, zone()); ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(2, zone());
args->Add(factory()->NewThisFunction(pos), zone()); args->Add(factory()->NewThisFunction(pos), zone());
args->Add(IsArrowFunction(kind) ? GetLiteralUndefined(pos) args->Add(IsArrowFunction(kind) ? GetLiteralUndefined(pos)
: ThisExpression(kNoSourcePosition), : ThisExpression(kNoSourcePosition),
zone()); zone());
return factory()->NewCallRuntime(Runtime::kCreateJSGeneratorObject, args, Expression* allocation =
pos); factory()->NewCallRuntime(Runtime::kCreateJSGeneratorObject, args, pos);
VariableProxy* proxy =
factory()->NewVariableProxy(function_state_->generator_object_variable());
return factory()->NewAssignment(Token::INIT, proxy, allocation,
kNoSourcePosition);
} }
Expression* Parser::BuildResolvePromise(Expression* value, int pos) { Expression* Parser::BuildResolvePromise(Expression* value, int pos) {
...@@ -3126,17 +3131,13 @@ Variable* Parser::PromiseVariable() { ...@@ -3126,17 +3131,13 @@ Variable* Parser::PromiseVariable() {
} }
Expression* Parser::BuildInitialYield(int pos, FunctionKind kind) { Expression* Parser::BuildInitialYield(int pos, FunctionKind kind) {
Expression* allocation = BuildCreateJSGeneratorObject(pos, kind); Assignment* assignment = BuildCreateJSGeneratorObject(pos, kind);
VariableProxy* init_proxy = VariableProxy* generator =
factory()->NewVariableProxy(function_state_->generator_object_variable());
Assignment* assignment = factory()->NewAssignment(
Token::INIT, init_proxy, allocation, kNoSourcePosition);
VariableProxy* get_proxy =
factory()->NewVariableProxy(function_state_->generator_object_variable()); factory()->NewVariableProxy(function_state_->generator_object_variable());
// The position of the yield is important for reporting the exception // The position of the yield is important for reporting the exception
// caused by calling the .throw method on a generator suspended at the // caused by calling the .throw method on a generator suspended at the
// initial yield (i.e. right after generator instantiation). // initial yield (i.e. right after generator instantiation).
return factory()->NewYield(get_proxy, assignment, scope()->start_position(), return factory()->NewYield(generator, assignment, scope()->start_position(),
Yield::kOnExceptionThrow); Yield::kOnExceptionThrow);
} }
...@@ -3153,7 +3154,7 @@ ZoneList<Statement*>* Parser::ParseFunction( ...@@ -3153,7 +3154,7 @@ ZoneList<Statement*>* Parser::ParseFunction(
DuplicateFinder duplicate_finder; DuplicateFinder duplicate_finder;
ExpressionClassifier formals_classifier(this, &duplicate_finder); ExpressionClassifier formals_classifier(this, &duplicate_finder);
if (IsGeneratorFunction(kind)) PrepareGeneratorVariables(&function_state); if (IsResumableFunction(kind)) PrepareGeneratorVariables();
ParserFormalParameters formals(function_scope); ParserFormalParameters formals(function_scope);
ParseFormalParameterList(&formals, CHECK_OK); ParseFormalParameterList(&formals, CHECK_OK);
...@@ -4168,23 +4169,13 @@ Expression* Parser::ExpressionListToExpression(ZoneList<Expression*>* args) { ...@@ -4168,23 +4169,13 @@ Expression* Parser::ExpressionListToExpression(ZoneList<Expression*>* args) {
// when desugaring the body of async_function. // when desugaring the body of async_function.
void Parser::PrepareAsyncFunctionBody(ZoneList<Statement*>* body, void Parser::PrepareAsyncFunctionBody(ZoneList<Statement*>* body,
FunctionKind kind, int pos) { FunctionKind kind, int pos) {
// function async_function() { // When parsing an async arrow function, we get here without having called
// .generator_object = %CreateGeneratorObject(); // PrepareGeneratorVariables yet, so do it now.
// BuildRejectPromiseOnException({ if (function_state_->generator_object_variable() == nullptr) {
// ... block ... PrepareGeneratorVariables();
// return %ResolvePromise(.promise, expr), .promise; }
// }) body->Add(factory()->NewExpressionStatement(
// } BuildCreateJSGeneratorObject(pos, kind), kNoSourcePosition),
Variable* temp =
NewTemporary(ast_value_factory()->dot_generator_object_string());
function_state_->set_generator_object_variable(temp);
Expression* init_generator_variable = factory()->NewAssignment(
Token::INIT, factory()->NewVariableProxy(temp),
BuildCreateJSGeneratorObject(pos, kind), kNoSourcePosition);
body->Add(factory()->NewExpressionStatement(init_generator_variable,
kNoSourcePosition),
zone()); zone());
} }
...@@ -4192,7 +4183,7 @@ void Parser::PrepareAsyncFunctionBody(ZoneList<Statement*>* body, ...@@ -4192,7 +4183,7 @@ void Parser::PrepareAsyncFunctionBody(ZoneList<Statement*>* body,
void Parser::RewriteAsyncFunctionBody(ZoneList<Statement*>* body, Block* block, void Parser::RewriteAsyncFunctionBody(ZoneList<Statement*>* body, Block* block,
Expression* return_value, bool* ok) { Expression* return_value, bool* ok) {
// function async_function() { // function async_function() {
// .generator_object = %CreateGeneratorObject(); // .generator_object = %CreateJSGeneratorObject();
// BuildRejectPromiseOnException({ // BuildRejectPromiseOnException({
// ... block ... // ... block ...
// return %ResolvePromise(.promise, expr), .promise; // return %ResolvePromise(.promise, expr), .promise;
...@@ -4229,10 +4220,7 @@ Expression* Parser::RewriteAwaitExpression(Expression* value, int await_pos) { ...@@ -4229,10 +4220,7 @@ Expression* Parser::RewriteAwaitExpression(Expression* value, int await_pos) {
// TODO(littledan): investigate why this ordering is needed in more detail. // TODO(littledan): investigate why this ordering is needed in more detail.
Variable* generator_object_variable = Variable* generator_object_variable =
function_state_->generator_object_variable(); function_state_->generator_object_variable();
DCHECK_NOT_NULL(generator_object_variable);
// If generator_object_variable is null,
// TODO(littledan): Is this necessary?
if (!generator_object_variable) return value;
const int nopos = kNoSourcePosition; const int nopos = kNoSourcePosition;
......
...@@ -264,7 +264,7 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { ...@@ -264,7 +264,7 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
return scope()->NewTemporary(name); return scope()->NewTemporary(name);
} }
void PrepareGeneratorVariables(FunctionState* function_state); void PrepareGeneratorVariables();
// Limit the allowed number of local variables in a function. The hard limit // Limit the allowed number of local variables in a function. The hard limit
// is that offsets computed by FullCodeGenerator::StackOperand and similar // is that offsets computed by FullCodeGenerator::StackOperand and similar
...@@ -630,7 +630,7 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { ...@@ -630,7 +630,7 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
void RewriteParameterInitializer(Expression* expr, Scope* scope); void RewriteParameterInitializer(Expression* expr, Scope* scope);
Expression* BuildInitialYield(int pos, FunctionKind kind); Expression* BuildInitialYield(int pos, FunctionKind kind);
Expression* BuildCreateJSGeneratorObject(int pos, FunctionKind kind); Assignment* BuildCreateJSGeneratorObject(int pos, FunctionKind kind);
Expression* BuildResolvePromise(Expression* value, int pos); Expression* BuildResolvePromise(Expression* value, int pos);
Expression* BuildRejectPromise(Expression* value, int pos); Expression* BuildRejectPromise(Expression* value, int pos);
Variable* PromiseVariable(); Variable* PromiseVariable();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment