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()); |
} |