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

Unified Diff: src/parsing/parser.cc

Issue 2672313003: [async await] fix async await desugaring
Patch Set: refactor Created 3 years, 10 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
Index: src/parsing/parser.cc
diff --git a/src/parsing/parser.cc b/src/parsing/parser.cc
index 805b6ef066bfe5ea0cd4669cf2ad2a387b022873..9309a2a3140e362fabbc864b6e6b409fa92f18ef 100644
--- a/src/parsing/parser.cc
+++ b/src/parsing/parser.cc
@@ -1616,7 +1616,18 @@ Expression* Parser::RewriteReturn(Expression* return_value, int pos) {
if (is_generator()) {
return_value = BuildIteratorResult(return_value, true);
} else if (is_async_function()) {
- return_value = BuildResolvePromise(return_value, return_value->position());
+ // In an async function,
+ // return expr;
+ // is rewritten as
+ // return .async_return_value = expr, undefined;
neis 2017/02/08 10:28:41 Why bother returning undefined? You can just do
gsathya 2017/02/09 00:55:44 Yeah it doesn't matter; the returned value is thro
+ Assignment* set_async_return_var = factory()->NewAssignment(
+ Token::INIT, factory()->NewVariableProxy(AsyncReturnVariable()),
+ return_value, kNoSourcePosition);
neis 2017/02/08 10:28:41 This must be ASSIGN, not INIT.
gsathya 2017/02/09 00:55:44 Done.
+
+ return_value = factory()->NewBinaryOperation(
+ Token::COMMA, set_async_return_var,
+ factory()->NewUndefinedLiteral(kNoSourcePosition),
+ return_value->position());
}
return return_value;
}
@@ -2977,21 +2988,37 @@ Block* Parser::BuildParameterInitializationBlock(
return init_block;
}
+Block* Parser::BuildRejectPromiseOnExceptionForParameters(Block* inner_block) {
+ return BuildRejectPromiseOnException(inner_block, true);
+}
+
Block* Parser::BuildRejectPromiseOnException(Block* inner_block) {
+ return BuildRejectPromiseOnException(inner_block, false);
+}
+
+Block* Parser::BuildRejectPromiseOnException(Block* inner_block,
+ bool is_parameter_block) {
// .promise = %AsyncFunctionPromiseCreate();
+ // .is_rejection = false;
// try {
// <inner_block>
// } catch (.catch) {
- // %RejectPromise(.promise, .catch);
- // return .promise;
+ // .is_rejection = true;
+ // .async_return_value = .catch;
// } finally {
+ // .is_rejection ? %RejectPromise(.promise, .async_return_value)
+ // : %ResolvePromise(.promise, .async_return_value);
caitp 2017/02/07 23:47:17 Given that the parameter-init-block doesn't need t
gsathya 2017/02/09 00:55:44 I agree, it is wasteful. I've split this into two
// %AsyncFunctionPromiseRelease(.promise);
+ // return .promise;
// }
Block* result = factory()->NewBlock(nullptr, 2, true, kNoSourcePosition);
+ Variable* is_rejection_var =
+ scope()->NewTemporary(ast_value_factory()->empty_string());
// .promise = %AsyncFunctionPromiseCreate();
- Statement* set_promise;
+ // .is_rejection = false;
{
+ Statement* set_promise;
Expression* create_promise = factory()->NewCallRuntime(
Context::ASYNC_FUNCTION_PROMISE_CREATE_INDEX,
new (zone()) ZoneList<Expression*>(0, zone()), kNoSourcePosition);
@@ -3000,21 +3027,43 @@ Block* Parser::BuildRejectPromiseOnException(Block* inner_block) {
create_promise, kNoSourcePosition);
set_promise =
factory()->NewExpressionStatement(assign_promise, kNoSourcePosition);
+ result->statements()->Add(set_promise, zone());
+
+ Assignment* set_is_rejection = factory()->NewAssignment(
+ Token::INIT, factory()->NewVariableProxy(is_rejection_var),
+ factory()->NewBooleanLiteral(false, kNoSourcePosition),
+ kNoSourcePosition);
+ result->statements()->Add(
+ factory()->NewExpressionStatement(set_is_rejection, kNoSourcePosition),
+ zone());
}
- result->statements()->Add(set_promise, zone());
- // catch (.catch) { return %RejectPromise(.promise, .catch), .promise }
+ // catch (.catch) {
+ // .is_rejection = true;
+ // .async_return_value = .catch;
+ // }
Scope* catch_scope = NewScope(CATCH_SCOPE);
catch_scope->set_is_hidden();
Variable* catch_variable =
catch_scope->DeclareLocal(ast_value_factory()->dot_catch_string(), VAR);
Block* catch_block = factory()->NewBlock(nullptr, 1, true, kNoSourcePosition);
+ {
+ Assignment* set_is_rejection = factory()->NewAssignment(
+ Token::INIT, factory()->NewVariableProxy(is_rejection_var),
+ factory()->NewBooleanLiteral(true, kNoSourcePosition),
+ kNoSourcePosition);
neis 2017/02/08 10:28:41 This must be ASSIGN, not INIT.
gsathya 2017/02/09 00:55:45 Done.
+
+ Assignment* set_async_return_var = factory()->NewAssignment(
+ Token::INIT, factory()->NewVariableProxy(AsyncReturnVariable()),
+ factory()->NewVariableProxy(catch_variable), kNoSourcePosition);
neis 2017/02/08 10:28:41 This must be ASSIGN, not INIT.
gsathya 2017/02/09 00:55:45 Done.
- Expression* promise_reject = BuildRejectPromise(
- factory()->NewVariableProxy(catch_variable), kNoSourcePosition);
- ReturnStatement* return_promise_reject =
- factory()->NewReturnStatement(promise_reject, kNoSourcePosition);
- catch_block->statements()->Add(return_promise_reject, zone());
+ catch_block->statements()->Add(
+ factory()->NewExpressionStatement(set_is_rejection, kNoSourcePosition),
+ zone());
+ catch_block->statements()->Add(factory()->NewExpressionStatement(
+ set_async_return_var, kNoSourcePosition),
+ zone());
+ }
TryStatement* try_catch_statement =
factory()->NewTryCatchStatementForAsyncAwait(inner_block, catch_scope,
@@ -3026,10 +3075,28 @@ Block* Parser::BuildRejectPromiseOnException(Block* inner_block) {
factory()->NewBlock(nullptr, 1, true, kNoSourcePosition);
outer_try_block->statements()->Add(try_catch_statement, zone());
- // finally { %AsyncFunctionPromiseRelease(.promise) }
+ // finally {
+ // .is_rejection
+ // ? %RejectPromise(.promise, .catch)
neis 2017/02/08 10:28:41 .catch should be .async_return_value
gsathya 2017/02/09 00:55:45 Done.
+ // : %ResolvePromise(.promise, .async_return_value);
+ // %AsyncFunctionPromiseRelease(.promise);
+ // return .promise;
+ // }
Block* finally_block =
factory()->NewBlock(nullptr, 1, true, kNoSourcePosition);
{
+ // .is_rejection
+ // ? %RejectPromise(.promise, .catch)
+ // : %ResolvePromise(.promise, .async_return_value);
+ Expression* resolve_or_reject_promise = factory()->NewConditional(
+ factory()->NewVariableProxy(is_rejection_var), BuildRejectPromise(),
+ BuildResolvePromise(), kNoSourcePosition);
+ finally_block->statements()->Add(
+ factory()->NewExpressionStatement(resolve_or_reject_promise,
+ kNoSourcePosition),
+ zone());
+
+ // %AsyncFunctionPromiseRelease(.promise);
ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(1, zone());
args->Add(factory()->NewVariableProxy(PromiseVariable()), zone());
Expression* call_promise_release = factory()->NewCallRuntime(
@@ -3037,6 +3104,29 @@ Block* Parser::BuildRejectPromiseOnException(Block* inner_block) {
Statement* promise_release = factory()->NewExpressionStatement(
call_promise_release, kNoSourcePosition);
finally_block->statements()->Add(promise_release, zone());
+
+ // return .promise;
+ Statement* return_promise = factory()->NewReturnStatement(
+ factory()->NewVariableProxy(PromiseVariable()), kNoSourcePosition);
+
+ // If we're in a try-catch-finally block of parameters block
+ // desugaring, then we return the promise only on rejection.
+ // The desugaring changes from
+ // return .promise;
+ // to
+ // if (.is_rejection) {
+ // return .promise;
+ // }
neis 2017/02/08 10:28:41 Please also mention this in the main comment at th
gsathya 2017/02/09 00:55:44 Done.
+ if (is_parameter_block) {
+ Expression* condition = factory()->NewCompareOperation(
+ Token::EQ, factory()->NewVariableProxy(is_rejection_var),
+ factory()->NewBooleanLiteral(true, kNoSourcePosition),
+ kNoSourcePosition);
neis 2017/02/08 10:28:41 No need for a comparison here.
gsathya 2017/02/09 00:55:44 Why not? We need to return only in the case of rej
neis 2017/02/09 08:22:07 I meant you can just do "if (is_rejection_var) ...
+ return_promise = factory()->NewIfStatement(
+ condition, return_promise,
+ factory()->NewEmptyStatement(kNoSourcePosition), kNoSourcePosition);
+ }
+ finally_block->statements()->Add(return_promise, zone());
}
Statement* try_finally_statement = factory()->NewTryFinallyStatement(
@@ -3062,31 +3152,29 @@ Assignment* Parser::BuildCreateJSGeneratorObject(int pos, FunctionKind kind) {
kNoSourcePosition);
}
-Expression* Parser::BuildResolvePromise(Expression* value, int pos) {
- // %ResolvePromise(.promise, value), .promise
+Expression* Parser::BuildResolvePromise() {
+ // %ResolvePromise(.promise, .async_return_variable), .promise
+ int pos = kNoSourcePosition;
adamk 2017/02/07 21:42:01 No need for this variable, just pass kNoSourcePosi
gsathya 2017/02/09 00:55:45 Done.
ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(2, zone());
args->Add(factory()->NewVariableProxy(PromiseVariable()), zone());
- args->Add(value, zone());
+ args->Add(factory()->NewVariableProxy(AsyncReturnVariable()), zone());
Expression* call_runtime =
factory()->NewCallRuntime(Context::PROMISE_RESOLVE_INDEX, args, pos);
- return factory()->NewBinaryOperation(
- Token::COMMA, call_runtime,
- factory()->NewVariableProxy(PromiseVariable()), pos);
+ return call_runtime;
}
-Expression* Parser::BuildRejectPromise(Expression* value, int pos) {
- // %promise_internal_reject(.promise, value, false), .promise
+Expression* Parser::BuildRejectPromise() {
+ // %promise_internal_reject(.promise, .async_return_variable, false)
// Disables the additional debug event for the rejection since a debug event
// already happened for the exception that got us here.
+ int pos = kNoSourcePosition;
adamk 2017/02/07 21:42:01 Same here as above.
gsathya 2017/02/09 00:55:45 Done.
ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(3, zone());
args->Add(factory()->NewVariableProxy(PromiseVariable()), zone());
- args->Add(value, zone());
+ args->Add(factory()->NewVariableProxy(AsyncReturnVariable()), zone());
args->Add(factory()->NewBooleanLiteral(false, pos), zone());
Expression* call_runtime = factory()->NewCallRuntime(
Context::PROMISE_INTERNAL_REJECT_INDEX, args, pos);
- return factory()->NewBinaryOperation(
- Token::COMMA, call_runtime,
- factory()->NewVariableProxy(PromiseVariable()), pos);
+ return call_runtime;
}
Variable* Parser::PromiseVariable() {
@@ -3101,6 +3189,18 @@ Variable* Parser::PromiseVariable() {
return promise;
}
+Variable* Parser::AsyncReturnVariable() {
+ // Based on the various compilation paths, there are many different code
+ // paths which may be the first to access the Promise temporary. Whichever
neis 2017/02/08 10:28:41 s/Promise/something else/
gsathya 2017/02/09 00:55:44 Done.
+ // comes first should create it and stash it in the FunctionState.
+ Variable* async_return = function_state_->async_return_variable();
+ if (async_return == nullptr) {
+ async_return = scope()->NewTemporary(ast_value_factory()->empty_string());
+ function_state_->set_async_return_variable(async_return);
+ }
+ return async_return;
+}
+
Expression* Parser::BuildInitialYield(int pos, FunctionKind kind) {
Assignment* assignment = BuildCreateJSGeneratorObject(pos, kind);
VariableProxy* generator =
@@ -3782,14 +3882,16 @@ void Parser::RewriteAsyncFunctionBody(ZoneList<Statement*>* body, Block* block,
// .generator_object = %CreateJSGeneratorObject();
// BuildRejectPromiseOnException({
// ... block ...
- // return %ResolvePromise(.promise, expr), .promise;
+ // .async_return_var = expr;
// })
// }
- return_value = BuildResolvePromise(return_value, return_value->position());
- block->statements()->Add(
- factory()->NewReturnStatement(return_value, return_value->position()),
- zone());
+ Assignment* set_async_return_var = factory()->NewAssignment(
+ Token::INIT, factory()->NewVariableProxy(AsyncReturnVariable()),
+ return_value, kNoSourcePosition);
neis 2017/02/08 10:28:41 This must be ASSIGN, not INIT.
gsathya 2017/02/09 00:55:45 Done.
+ block->statements()->Add(factory()->NewExpressionStatement(
+ set_async_return_var, kNoSourcePosition),
+ zone());
block = BuildRejectPromiseOnException(block);
body->Add(block, zone());
}

Powered by Google App Engine
This is Rietveld 408576698