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

Unified Diff: runtime/vm/parser.cc

Issue 944893005: Implement async* functions in VM (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 5 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: runtime/vm/parser.cc
===================================================================
--- runtime/vm/parser.cc (revision 43883)
+++ runtime/vm/parser.cc (working copy)
@@ -3104,6 +3104,19 @@
false,
&discarded_params);
}
+ } else if (func.IsAsyncGenClosure()) {
+ AddAsyncGenClosureParameters(&params);
+ SetupDefaultsForOptionalParams(&params, default_parameter_values);
+ AddFormalParamsToScope(&params, current_block_->scope);
+ ASSERT(AbstractType::Handle(Z, func.result_type()).IsResolved());
+ if (!Function::Handle(func.parent_function()).IsGetterFunction()) {
+ // Parse and discard any formal parameters. They are accessed as
+ // context variables.
+ ParamList discarded_params;
+ ParseFormalParameterList(allow_explicit_default_values,
+ false,
+ &discarded_params);
+ }
} else {
ParseFormalParameterList(allow_explicit_default_values, false, &params);
@@ -3172,6 +3185,13 @@
// The closure containing the body of a sync generator is debuggable.
ASSERT(func.is_debuggable());
async_temp_scope_ = current_block_->scope;
+ } else if (func.IsAsyncGenerator()) {
+ func.set_is_debuggable(false);
+ generated_body_closure = OpenAsyncGeneratorFunction(func.token_pos());
+ } else if (func.IsAsyncGenClosure()) {
+ // The closure containing the body of an async* function is debuggable.
+ ASSERT(func.is_debuggable());
+ OpenAsyncGeneratorClosure();
}
BoolScope allow_await(&this->await_is_keyword_,
@@ -3249,6 +3269,11 @@
generated_body_closure.set_end_token_pos(end_token_pos);
} else if (func.IsSyncGenClosure()) {
body->scope()->RecursivelyCaptureAllVariables();
+ } else if (func.IsAsyncGenerator()) {
+ body = CloseAsyncGeneratorFunction(generated_body_closure, body);
+ generated_body_closure.set_end_token_pos(end_token_pos);
+ } else if (func.IsAsyncGenClosure()) {
+ body = CloseAsyncGeneratorClosure(body);
}
current_block_->statements->Add(body);
innermost_function_ = saved_innermost_function.raw();
@@ -5249,7 +5274,10 @@
if (CurrentLiteral()->raw() == Symbols::Async().raw()) {
ConsumeToken();
if (CurrentToken() == Token::kMUL) {
- ReportError("async* generator functions are not yet supported");
+ const bool enableAsyncStar = true;
+ if (!enableAsyncStar) {
+ ReportError("async* generator functions are not yet supported");
+ }
ConsumeToken();
return RawFunction::kAsyncGen;
} else {
@@ -5940,11 +5968,22 @@
}
-SequenceNode* Parser::CloseAsyncTryBlock(SequenceNode* try_block) {
+SequenceNode* Parser::CloseAsyncGeneratorTryBlock(SequenceNode *body) {
+ TRACE_PARSER("CloseAsyncGeneratorTryBlock");
+ // The generated try-catch-finally that wraps the async generator function
+ // body is the outermost try statement.
+ ASSERT(try_blocks_list_ != NULL);
+ ASSERT(try_blocks_list_->outer_try_block() == NULL);
+ // We only get here when parsing an async generator body.
+ ASSERT(innermost_function().IsAsyncGenClosure());
+
+ // The try-block (closure body code) has been parsed. We are now
+ // generating the code for the catch block.
try_blocks_list_->enter_catch();
+ OpenBlock(); // Catch handler list.
+ OpenBlock(); // Catch block.
- OpenBlock();
- OpenBlock();
+ // Add the exception and stack trace parameters to the scope.
const AbstractType& dynamic_type =
AbstractType::ZoneHandle(Z, Type::DynamicType());
CatchParamDesc exception_param;
@@ -5955,10 +5994,11 @@
stack_trace_param.token_pos = Scanner::kNoSourcePos;
stack_trace_param.type = &dynamic_type;
stack_trace_param.name = &Symbols::StackTraceParameter();
-
AddCatchParamsToScope(
&exception_param, &stack_trace_param, current_block_->scope);
+ // Generate code to save the exception object and stack trace
+ // in local variables.
LocalVariable* context_var = current_block_->scope->LookupVariable(
Symbols::SavedTryContextVar(), false);
ASSERT(context_var != NULL);
@@ -5986,26 +6026,151 @@
new(Z) LoadLocalNode(Scanner::kNoSourcePos, stack_trace_var)));
}
+ parsed_function()->reset_saved_try_ctx_vars();
+
+ // Catch block: add the error to the stream.
+ // :controller.AddError(:exception, :stack_trace);
+ // return; // The finally block will close the stream.
+ LocalVariable* controller =
+ current_block_->scope->LookupVariable(Symbols::Controller(), false);
+ ASSERT(controller != NULL);
+ ArgumentListNode* args =
+ new(Z) ArgumentListNode(Scanner::kNoSourcePos);
+ args->Add(new(Z) LoadLocalNode(Scanner::kNoSourcePos, exception_param.var));
+ args->Add(new(Z) LoadLocalNode(Scanner::kNoSourcePos, stack_trace_param.var));
+ current_block_->statements->Add(
+ new(Z) InstanceCallNode(TokenPos(),
+ new(Z) LoadLocalNode(Scanner::kNoSourcePos, controller),
+ Symbols::AddError(),
+ args));
+ ReturnNode* return_node = new(Z) ReturnNode(Scanner::kNoSourcePos);
+ current_block_->statements->Add(return_node);
+ AstNode* catch_block = CloseBlock();
+ current_block_->statements->Add(catch_block);
+ SequenceNode* catch_handler_list = CloseBlock();
+
+ TryBlocks* try_block = PopTryBlock();
+ ASSERT(try_blocks_list_ == NULL); // We popped the outermost try block.
+
+ // Finally block: closing the stream and returning. (Note: the return
+ // is necessary otherwise the back-end will append a rethrow of the
+ // current exception.)
+ // :controller.close();
+ // return;
+ // We need to inline this code in all recorded exit points.
+ intptr_t node_index = 0;
+ SequenceNode* finally_clause = NULL;
+ do {
+ OpenBlock();
+ ArgumentListNode* no_args =
+ new(Z) ArgumentListNode(Scanner::kNoSourcePos);
+ current_block_->statements->Add(
+ new(Z) InstanceCallNode(TokenPos(),
+ new(Z) LoadLocalNode(Scanner::kNoSourcePos, controller),
+ Symbols::Close(),
+ no_args));
+
+ ReturnNode* return_node = new(Z) ReturnNode(Scanner::kNoSourcePos);
+ current_block_->statements->Add(return_node);
+
+ finally_clause = CloseBlock();
+ AstNode* node_to_inline = try_block->GetNodeToInlineFinally(node_index);
+ if (node_to_inline != NULL) {
+ InlinedFinallyNode* node =
+ new(Z) InlinedFinallyNode(TokenPos(),
+ finally_clause,
+ context_var,
+ // No outer try statement
+ CatchClauseNode::kInvalidTryIndex);
+ finally_clause = NULL;
+ AddFinallyBlockToNode(node_to_inline, node);
+ node_index++;
+ }
+ } while (finally_clause == NULL);
+
+ const GrowableObjectArray& handler_types =
+ GrowableObjectArray::Handle(Z, GrowableObjectArray::New());
+ handler_types.Add(dynamic_type); // Catch block handles all exceptions.
+
+ CatchClauseNode* catch_clause = new(Z) CatchClauseNode(
+ Scanner::kNoSourcePos,
+ catch_handler_list,
+ Array::ZoneHandle(Z, Array::MakeArray(handler_types)),
+ context_var,
+ exception_var,
+ stack_trace_var,
+ AllocateTryIndex(),
+ true);
+
+ const intptr_t try_index = try_block->try_index();
+
+ AstNode* try_catch_node =
+ new(Z) TryCatchNode(Scanner::kNoSourcePos,
+ body,
+ context_var,
+ catch_clause,
+ finally_clause,
+ try_index);
+ current_block_->statements->Add(try_catch_node);
+ return CloseBlock();
+}
+
+
+SequenceNode* Parser::CloseAsyncTryBlock(SequenceNode* try_block) {
+ // This is the outermost try-catch of the function.
ASSERT(try_blocks_list_ != NULL);
- ASSERT(innermost_function().IsAsyncClosure() ||
- innermost_function().IsAsyncFunction());
- if ((try_blocks_list_->outer_try_block() != NULL) &&
- (try_blocks_list_->outer_try_block()->try_block()
- ->scope->function_level() ==
- current_block_->scope->function_level())) {
- // We need to unchain three scope levels: catch clause, catch
- // parameters, and the general try block.
- RestoreSavedTryContext(
- current_block_->scope->parent()->parent()->parent(),
- try_blocks_list_->outer_try_block()->try_index(),
- current_block_->statements);
- } else {
- parsed_function()->reset_saved_try_ctx_vars();
+ ASSERT(try_blocks_list_->outer_try_block() == NULL);
+ ASSERT(innermost_function().IsAsyncClosure());
+
+ try_blocks_list_->enter_catch();
+
+ OpenBlock(); // Catch handler list.
+ OpenBlock(); // Catch block.
+ const AbstractType& dynamic_type =
+ AbstractType::ZoneHandle(Z, Type::DynamicType());
+ CatchParamDesc exception_param;
+ CatchParamDesc stack_trace_param;
+ exception_param.token_pos = Scanner::kNoSourcePos;
+ exception_param.type = &dynamic_type;
+ exception_param.name = &Symbols::ExceptionParameter();
+ stack_trace_param.token_pos = Scanner::kNoSourcePos;
+ stack_trace_param.type = &dynamic_type;
+ stack_trace_param.name = &Symbols::StackTraceParameter();
+
+ AddCatchParamsToScope(
+ &exception_param, &stack_trace_param, current_block_->scope);
+
+ LocalVariable* context_var = current_block_->scope->LookupVariable(
+ Symbols::SavedTryContextVar(), false);
+ ASSERT(context_var != NULL);
+ LocalVariable* exception_var = current_block_->scope->LookupVariable(
+ Symbols::ExceptionVar(), false);
+ if (exception_param.var != NULL) {
+ // Generate code to load the exception object (:exception_var) into
+ // the exception variable specified in this block.
+ ASSERT(exception_var != NULL);
+ current_block_->statements->Add(new(Z) StoreLocalNode(
+ Scanner::kNoSourcePos,
+ exception_param.var,
+ new(Z) LoadLocalNode(Scanner::kNoSourcePos, exception_var)));
}
+ LocalVariable* stack_trace_var =
+ current_block_->scope->LookupVariable(Symbols::StackTraceVar(), false);
+ if (stack_trace_param.var != NULL) {
+ // A stack trace variable is specified in this block, so generate code
+ // to load the stack trace object (:stack_trace_var) into the stack
+ // trace variable specified in this block.
+ ASSERT(stack_trace_var != NULL);
+ current_block_->statements->Add(new(Z) StoreLocalNode(
+ Scanner::kNoSourcePos,
+ stack_trace_param.var,
+ new(Z) LoadLocalNode(Scanner::kNoSourcePos, stack_trace_var)));
+ }
- // Complete the async future with an error.
- // Since we control the catch block there is no need to generate a nested
- // if/then/else.
+ parsed_function()->reset_saved_try_ctx_vars();
+
+ // Complete the async future with an error. This catch block executes
+ // unconditionally, there is no need to generate a type check for.
LocalVariable* async_completer = current_block_->scope->LookupVariable(
Symbols::AsyncCompleter(), false);
ASSERT(async_completer != NULL);
@@ -6050,15 +6215,18 @@
try_block,
context_var,
catch_clause,
- NULL,
+ NULL, // No finally clause.
try_index);
current_block_->statements->Add(try_catch_node);
return CloseBlock();
}
+// Wrap the body of the async or arync* closure in a try/catch block.
void Parser::OpenAsyncTryBlock() {
- // Manually wrapping the actual body into a try/catch block.
+ ASSERT(innermost_function().IsAsyncClosure() ||
+ innermost_function().IsAsyncGenClosure());
+
LocalVariable* context_var =
current_block_->scope->LocalLookupVariable(Symbols::SavedTryContextVar());
if (context_var == NULL) {
@@ -6089,14 +6257,11 @@
// Open the try block.
OpenBlock();
+ // This is the outermost try-catch in the function.
+ ASSERT(try_blocks_list_ == NULL);
PushTryBlock(current_block_);
- if (innermost_function().IsAsyncClosure() ||
- innermost_function().IsAsyncFunction() ||
- innermost_function().IsSyncGenClosure() ||
- innermost_function().IsSyncGenerator()) {
- SetupSavedTryContext(context_var);
- }
+ SetupSavedTryContext(context_var);
}
@@ -6117,6 +6282,17 @@
}
+void Parser::AddAsyncGenClosureParameters(ParamList* params) {
+ // Create the parameter list for the body closure of a sync generator:
Ivan Posva 2015/02/23 08:23:36 sync?
hausner 2015/02/23 17:12:01 Yes.
+ // The closure has no explicit parameters.
+ const Type& dynamic_type = Type::ZoneHandle(Z, Type::DynamicType());
+ // Add implicit closure parameter if not already present.
+ if (params->parameters->length() == 0) {
+ params->AddFinalParameter(0, &Symbols::ClosureParameter(), &dynamic_type);
+ }
+}
+
+
RawFunction* Parser::OpenSyncGeneratorFunction(intptr_t func_pos) {
Function& body = Function::Handle(Z);
String& body_closure_name = String::Handle(Z);
@@ -6356,6 +6532,187 @@
}
+void Parser::AddAsyncGeneratorVariables() {
+ // Add to current block's scope:
+ // var :controller;
+ // This variables is used by the nested async generator closure to
+ // store the StreamController object to which the yielded expressions
+ // are added.
+ const Type& dynamic_type = Type::ZoneHandle(Z, Type::DynamicType());
+ LocalVariable* controller_var = new(Z) LocalVariable(
+ Scanner::kNoSourcePos, Symbols::Controller(), dynamic_type);
+ current_block_->scope->AddVariable(controller_var);
+ current_block_->scope->CaptureVariable(Symbols::Controller());
+ controller_var->set_is_captured();
+}
+
+
+RawFunction* Parser::OpenAsyncGeneratorFunction(intptr_t async_func_pos) {
+ TRACE_PARSER("OpenAsyncGeneratorFunction");
+ AddContinuationVariables();
+ AddAsyncGeneratorVariables();
+
+ Function& closure = Function::Handle(Z);
+ bool is_new_closure = false;
+
+ // Check whether a function for the asynchronous function body of
+ // this async generator has already been created by a previous
+ // compilation of this function.
+ const Function& found_func = Function::Handle(
+ Z, current_class().LookupClosureFunction(async_func_pos));
+ if (!found_func.IsNull() &&
+ (found_func.token_pos() == async_func_pos) &&
+ (found_func.script() == innermost_function().script()) &&
+ (found_func.parent_function() == innermost_function().raw())) {
+ ASSERT(found_func.IsAsyncGenClosure());
+ closure = found_func.raw();
+ } else {
+ // Create the closure containing the body of this async generator function.
+ const String& async_generator_name =
+ String::Handle(Z, innermost_function().name());
+ String& closure_name = String::Handle(Z,
+ String::NewFormatted("<%s_async_body>",
+ async_generator_name.ToCString()));
+ closure = Function::NewClosureFunction(
+ String::Handle(Z, Symbols::New(closure_name)),
+ innermost_function(),
+ async_func_pos);
+ closure.set_is_generated_body(true);
+ closure.set_result_type(AbstractType::Handle(Type::DynamicType()));
+ is_new_closure = true;
+ }
+
+ ParamList closure_params;
+ AddAsyncGenClosureParameters(&closure_params);
+
+ if (is_new_closure) {
+ // Add the parameters to the newly created closure.
+ AddFormalParamsToFunction(&closure_params, closure);
+
+ // Create and set the signature class of the closure.
+ const String& sig = String::Handle(Z, closure.Signature());
+ Class& sig_cls = Class::Handle(Z, library_.LookupLocalClass(sig));
+ if (sig_cls.IsNull()) {
+ sig_cls =
+ Class::NewSignatureClass(sig, closure, script_, closure.token_pos());
+ library_.AddClass(sig_cls);
+ }
+ closure.set_signature_class(sig_cls);
+ const Type& sig_type = Type::Handle(Z, sig_cls.SignatureType());
+ if (!sig_type.IsFinalized()) {
+ ClassFinalizer::FinalizeType(
+ sig_cls, sig_type, ClassFinalizer::kCanonicalize);
+ }
+ ASSERT(AbstractType::Handle(Z, closure.result_type()).IsResolved());
+ ASSERT(closure.NumParameters() == closure_params.parameters->length());
+ }
+
+ OpenFunctionBlock(closure);
+ AddFormalParamsToScope(&closure_params, current_block_->scope);
+ OpenBlock();
+ async_temp_scope_ = current_block_->scope;
+ return closure.raw();
+}
+
+
+// Generate the Ast nodes for the implicit code of the async* function.
+//
+// f(...) async* {
+// var :controller;
+// var :await_jump_var = -1;
+// var :await_context_var;
+// f_async_body() {
+// ... source code of f ...
+// }
+// :controller = new _AsyncStarStreamController(f_async_body);
+// return :controller.stream;
+// }
+SequenceNode* Parser::CloseAsyncGeneratorFunction(const Function& closure,
+ SequenceNode* closure_body) {
+ TRACE_PARSER("CloseAsyncGeneratorFunction");
+ ASSERT(!closure.IsNull());
+ ASSERT(closure_body != NULL);
+
+ // The block for the async closure body has already been closed. Close the
+ // corresponding function block.
+ CloseBlock();
+
+ // Make sure the implicit variables of the async generator function
+ // are captured.
+ closure_body->scope()->LookupVariable(Symbols::AwaitJumpVar(), false);
+ closure_body->scope()->LookupVariable(Symbols::AwaitContextVar(), false);
+ closure_body->scope()->LookupVariable(Symbols::Controller(), false);
+
+ const Class& controller_class = Class::Handle(Z,
+ Library::LookupCoreClass(Symbols::_AsyncStarStreamController()));
+ ASSERT(!controller_class.IsNull());
+ const Function& controller_constructor = Function::ZoneHandle(Z,
+ controller_class.LookupConstructorAllowPrivate(
+ Symbols::_AsyncStarStreamControllerConstructor()));
+
+ // :await_jump_var = -1;
+ LocalVariable* jump_var =
+ current_block_->scope->LookupVariable(Symbols::AwaitJumpVar(), false);
+ LiteralNode* init_value =
+ new(Z) LiteralNode(Scanner::kNoSourcePos, Smi::ZoneHandle(Smi::New(-1)));
+ current_block_->statements->Add(
+ new(Z) StoreLocalNode(Scanner::kNoSourcePos, jump_var, init_value));
+
+ // :controller = new _AsyncStarStreamController(body_closure);
+ ArgumentListNode* arguments = new(Z) ArgumentListNode(Scanner::kNoSourcePos);
+ ClosureNode* closure_obj = new(Z) ClosureNode(
+ Scanner::kNoSourcePos, closure, NULL, closure_body->scope());
+ arguments->Add(closure_obj);
+ ConstructorCallNode* controller_constructor_call =
+ new(Z) ConstructorCallNode(Scanner::kNoSourcePos,
+ TypeArguments::ZoneHandle(Z),
+ controller_constructor,
+ arguments);
+ LocalVariable* controller_var =
+ current_block_->scope->LookupVariable(Symbols::Controller(), false);
+ StoreLocalNode* store_controller =
+ new(Z) StoreLocalNode(Scanner::kNoSourcePos,
+ controller_var,
+ controller_constructor_call);
+ current_block_->statements->Add(store_controller);
+
+ // return :controller.stream;
+ ReturnNode* return_node = new(Z) ReturnNode(Scanner::kNoSourcePos,
+ new(Z) InstanceGetterNode(Scanner::kNoSourcePos,
+ new(Z) LoadLocalNode(Scanner::kNoSourcePos,
+ controller_var),
+ Symbols::ControllerStream()));
+ current_block_->statements->Add(return_node);
+ return CloseBlock();
+}
+
+
+void Parser::OpenAsyncGeneratorClosure() {
+ async_temp_scope_ = current_block_->scope;
+ OpenAsyncTryBlock();
+}
+
+
+SequenceNode* Parser::CloseAsyncGeneratorClosure(SequenceNode* body) {
+ // TODO(hausner): Is the temporary expression necessary?
+ // We need a temporary expression to store intermediate return values.
+ parsed_function()->EnsureExpressionTemp();
+
+ SequenceNode* new_body = CloseAsyncGeneratorTryBlock(body);
+ ASSERT(new_body != NULL);
+ ASSERT(new_body->scope() != NULL);
+
+ // Implicitly mark those variables below as captured. We currently mark all
+ // variables of all scopes as captured, but as soon as we do something
+ // smarter we rely on these internal variables to be available.
+ new_body->scope()->LookupVariable(Symbols::AwaitJumpVar(), false);
+ new_body->scope()->LookupVariable(Symbols::AwaitContextVar(), false);
+ new_body->scope()->LookupVariable(Symbols::Controller(), false);
+ new_body->scope()->RecursivelyCaptureAllVariables();
+ return new_body;
+}
+
+
SequenceNode* Parser::CloseBlock() {
SequenceNode* statements = current_block_->statements;
if (current_block_->scope != NULL) {
@@ -7771,9 +8128,11 @@
ExpectToken(Token::kLPAREN);
if (!innermost_function().IsAsyncFunction() &&
- !innermost_function().IsAsyncClosure()) {
+ !innermost_function().IsAsyncClosure() &&
+ !innermost_function().IsAsyncGenerator() &&
+ !innermost_function().IsAsyncGenClosure()) {
ReportError(await_for_pos,
- "await for loop is only allowed in async function");
+ "await for loop is only allowed in an asynchronous function");
}
// Parse loop variable.
@@ -8212,7 +8571,9 @@
if ((innermost_function().IsAsyncClosure() ||
innermost_function().IsAsyncFunction() ||
innermost_function().IsSyncGenClosure() ||
- innermost_function().IsSyncGenerator()) &&
+ innermost_function().IsSyncGenerator() ||
+ innermost_function().IsAsyncGenerator() ||
+ innermost_function().IsAsyncGenClosure()) &&
(try_blocks_list_ != NULL)) {
// We need two unchain two scopes: finally clause, and the try block level.
RestoreSavedTryContext(current_block_->scope->parent()->parent(),
@@ -8321,7 +8682,6 @@
exception_param.name = ExpectIdentifier("identifier expected");
if (CurrentToken() == Token::kCOMMA) {
ConsumeToken();
- // TODO(hausner): Make implicit type be StackTrace, not dynamic.
stack_trace_param.type =
&AbstractType::ZoneHandle(Z, Type::DynamicType());
stack_trace_param.token_pos = TokenPos();
@@ -8358,7 +8718,7 @@
catch_pos, stack_trace_var)));
}
- // Add nested block with user-defined code. This blocks allows
+ // Add nested block with user-defined code. This block allows
// declarations in the body to shadow the catch parameters.
CheckToken(Token::kLBRACE);
@@ -8368,7 +8728,9 @@
if (innermost_function().IsAsyncClosure() ||
innermost_function().IsAsyncFunction() ||
innermost_function().IsSyncGenClosure() ||
- innermost_function().IsSyncGenerator()) {
+ innermost_function().IsSyncGenerator() ||
+ innermost_function().IsAsyncGenerator() ||
+ innermost_function().IsAsyncGenClosure()) {
if ((try_blocks_list_->outer_try_block() != NULL) &&
(try_blocks_list_->outer_try_block()->try_block()
->scope->function_level() ==
@@ -8478,7 +8840,9 @@
if (innermost_function().IsAsyncClosure() ||
innermost_function().IsAsyncFunction() ||
innermost_function().IsSyncGenClosure() ||
- innermost_function().IsSyncGenerator()) {
+ innermost_function().IsSyncGenerator() ||
+ innermost_function().IsAsyncGenerator() ||
+ innermost_function().IsAsyncGenClosure()) {
if ((try_blocks_list_->outer_try_block() != NULL) &&
(try_blocks_list_->outer_try_block()->try_block()
->scope->function_level() ==
@@ -8617,7 +8981,9 @@
if (innermost_function().IsAsyncClosure() ||
innermost_function().IsAsyncFunction() ||
innermost_function().IsSyncGenClosure() ||
- innermost_function().IsSyncGenerator()) {
+ innermost_function().IsSyncGenerator() ||
+ innermost_function().IsAsyncGenerator() ||
+ innermost_function().IsAsyncGenClosure()) {
SetupSavedTryContext(context_var);
}
@@ -8761,6 +9127,112 @@
}
+AstNode* Parser::ParseYieldStatement() {
+ bool is_yield_each = false;
+ const intptr_t yield_pos = TokenPos();
+ ConsumeToken(); // yield reserved word.
+ ASSERT(innermost_function().IsGenerator() ||
+ innermost_function().IsSyncGenClosure() ||
+ innermost_function().IsAsyncGenerator() ||
+ innermost_function().IsAsyncGenClosure());
+ if (CurrentToken() == Token::kMUL) {
+ is_yield_each = true;
+ ConsumeToken();
+ }
+ AstNode* expr = ParseAwaitableExpr(kAllowConst, kConsumeCascades, NULL);
+
+ LetNode* yield = new(Z) LetNode(yield_pos);
+ if (innermost_function().IsSyncGenerator() ||
+ innermost_function().IsSyncGenClosure()) {
+ // Yield statement in sync* function.
+
+ LocalVariable* iterator_param =
+ LookupLocalScope(Symbols::IteratorParameter());
+ ASSERT(iterator_param != NULL);
+ // Generate :iterator.current = expr;
+ AstNode* iterator =
+ new(Z) LoadLocalNode(Scanner::kNoSourcePos, iterator_param);
+ AstNode* store_current =
+ new(Z) InstanceSetterNode(Scanner::kNoSourcePos,
+ iterator,
+ String::ZoneHandle(Symbols::Current().raw()),
+ expr);
+ yield->AddNode(store_current);
+ if (is_yield_each) {
+ // Generate :iterator.isYieldEach = true;
+ AstNode* set_is_yield_each =
+ new(Z) InstanceSetterNode(Scanner::kNoSourcePos,
+ iterator,
+ String::ZoneHandle(Symbols::IsYieldEach().raw()),
+ new(Z) LiteralNode(TokenPos(), Bool::True()));
+ yield->AddNode(set_is_yield_each);
+ }
+ AwaitMarkerNode* await_marker = new(Z) AwaitMarkerNode();
+ await_marker->set_scope(current_block_->scope);
+ yield->AddNode(await_marker);
+ // Return true to indicate that a value has been generated.
+ ReturnNode* return_true = new(Z) ReturnNode(yield_pos,
+ new(Z) LiteralNode(TokenPos(), Bool::True()));
+ return_true->set_return_type(ReturnNode::kContinuationTarget);
+ yield->AddNode(return_true);
+
+ // If this expression is part of a try block, also append the code for
+ // restoring the saved try context that lives on the stack.
+ const String& async_saved_try_ctx_name =
+ String::Handle(Z, parsed_function()->async_saved_try_ctx_name());
+ if (!async_saved_try_ctx_name.IsNull()) {
+ LocalVariable* async_saved_try_ctx =
+ current_block_->scope->LookupVariable(async_saved_try_ctx_name,
+ false);
+ ASSERT(async_saved_try_ctx != NULL);
+ yield->AddNode(new (Z) StoreLocalNode(
+ Scanner::kNoSourcePos,
+ parsed_function()->saved_try_ctx(),
+ new (Z) LoadLocalNode(Scanner::kNoSourcePos, async_saved_try_ctx)));
+ }
+ } else {
+ // yield statement in async* function.
+ ASSERT(innermost_function().IsAsyncGenerator() ||
+ innermost_function().IsAsyncGenClosure());
+
+ LocalVariable* controller_var = LookupLocalScope(Symbols::Controller());
+ ASSERT(controller_var != NULL);
+ // :controller.add[Stream](expr);
+ ArgumentListNode* add_args = new(Z) ArgumentListNode(yield_pos);
+ add_args->Add(expr);
+ AstNode* add_call =
+ new(Z) InstanceCallNode(yield_pos,
+ new(Z) LoadLocalNode(Scanner::kNoSourcePos, controller_var),
+ is_yield_each ? Symbols::AddStream() : Symbols::add(),
+ add_args);
+
+ // if (:controller.isPaused) {
+ // await_marker;
+ // continuation_return;
+ // }
+ /*
+ AstNode* is_paused = new(Z) InstanceGetterNode(
+ Scanner::kNoSourcePos,
+ new(Z) LoadLocalNode(Scanner::kNoSourcePos, controller_var),
+ Symbols::isPaused());*/
+
+ SequenceNode* true_branch =
+ new(Z) SequenceNode(Scanner::kNoSourcePos, current_block_->scope);
+ AwaitMarkerNode* await_marker = new(Z) AwaitMarkerNode();
+ await_marker->set_scope(current_block_->scope);
+ true_branch->Add(await_marker);
+ ReturnNode* continuation_return = new(Z) ReturnNode(yield_pos);
+ continuation_return->set_return_type(ReturnNode::kContinuationTarget);
+ true_branch->Add(continuation_return);
+
+ AstNode* if_is_paused =
+ new(Z) IfNode(Scanner::kNoSourcePos, add_call, true_branch, NULL);
+ yield->AddNode(if_is_paused);
+ }
+ return yield;
+}
+
+
AstNode* Parser::ParseStatement() {
TRACE_PARSER("ParseStatement");
AstNode* statement = NULL;
@@ -8800,7 +9272,8 @@
(current_block_->scope->function_level() == 0)) {
ReportError(expr_pos,
"return of a value is not allowed in constructors");
- } else if (current_function().IsGenerator()) {
+ } else if (current_function().IsGeneratorClosure() &&
+ (current_block_->scope->function_level() == 0)) {
ReportError(expr_pos, "generator functions may not return a value");
}
AstNode* expr = ParseAwaitableExpr(kAllowConst, kConsumeCascades, NULL);
@@ -8820,62 +9293,7 @@
AddNodeForFinallyInlining(statement);
ExpectSemicolon();
} else if (IsYieldKeyword()) {
- bool is_yield_each = false;
- ConsumeToken();
- ASSERT(innermost_function().IsGenerator() ||
- innermost_function().IsSyncGenClosure());
- if (CurrentToken() == Token::kMUL) {
- is_yield_each = true;
- ConsumeToken();
- }
- AstNode* expr = ParseAwaitableExpr(kAllowConst, kConsumeCascades, NULL);
- LocalVariable* iterator_param =
- LookupLocalScope(Symbols::IteratorParameter());
- ASSERT(iterator_param != NULL);
- // Generate :iterator.current = expr;
- AstNode* iterator =
- new(Z) LoadLocalNode(Scanner::kNoSourcePos, iterator_param);
- AstNode* store_current =
- new(Z) InstanceSetterNode(Scanner::kNoSourcePos,
- iterator,
- String::ZoneHandle(Symbols::Current().raw()),
- expr);
- LetNode* yield = new(Z) LetNode(statement_pos);
- yield->AddNode(store_current);
- if (is_yield_each) {
- // Generate :iterator.isYieldEach = true;
- AstNode* set_is_yield_each =
- new(Z) InstanceSetterNode(Scanner::kNoSourcePos,
- iterator,
- String::ZoneHandle(Symbols::IsYieldEach().raw()),
- new(Z) LiteralNode(TokenPos(), Bool::True()));
- yield->AddNode(set_is_yield_each);
- }
- AwaitMarkerNode* await_marker = new(Z) AwaitMarkerNode();
- await_marker->set_scope(current_block_->scope);
- yield->AddNode(await_marker);
- // Return true to indicate that a value has been generated.
- ReturnNode* return_true = new(Z) ReturnNode(statement_pos,
- new(Z) LiteralNode(TokenPos(), Bool::True()));
- return_true->set_return_type(ReturnNode::kContinuationTarget);
- yield->AddNode(return_true);
-
- // If this expression is part of a try block, also append the code for
- // restoring the saved try context that lives on the stack.
- const String& async_saved_try_ctx_name =
- String::Handle(Z, parsed_function()->async_saved_try_ctx_name());
- if (!async_saved_try_ctx_name.IsNull()) {
- LocalVariable* async_saved_try_ctx =
- current_block_->scope->LookupVariable(async_saved_try_ctx_name,
- false);
- ASSERT(async_saved_try_ctx != NULL);
- yield->AddNode(new (Z) StoreLocalNode(
- Scanner::kNoSourcePos,
- parsed_function()->saved_try_ctx(),
- new (Z) LoadLocalNode(Scanner::kNoSourcePos, async_saved_try_ctx)));
- }
-
- statement = yield;
+ statement = ParseYieldStatement();
ExpectSemicolon();
} else if (token == Token::kIF) {
statement = ParseIfStatement(label_name);
@@ -9726,8 +10144,10 @@
if (IsAwaitKeyword()) {
TRACE_PARSER("ParseAwaitExpr");
if (!innermost_function().IsAsyncFunction() &&
- !innermost_function().IsAsyncClosure()) {
- ReportError("await operator is only allowed in async function");
+ !innermost_function().IsAsyncClosure() &&
+ !innermost_function().IsAsyncGenerator() &&
+ !innermost_function().IsAsyncGenClosure()) {
+ ReportError("await operator is only allowed in an asynchronous function");
}
ConsumeToken();
parsed_function()->record_await();

Powered by Google App Engine
This is Rietveld 408576698