Chromium Code Reviews| Index: runtime/vm/parser.cc |
| =================================================================== |
| --- runtime/vm/parser.cc (revision 44153) |
| +++ runtime/vm/parser.cc (working copy) |
| @@ -3110,6 +3110,20 @@ |
| false, |
| &discarded_params); |
| } |
| + } else if (func.IsAsyncGenClosure()) { |
| + AddAsyncGenClosureParameters(¶ms); |
| + SetupDefaultsForOptionalParams(¶ms, default_parameter_values); |
| + AddFormalParamsToScope(¶ms, current_block_->scope); |
| + ASSERT(AbstractType::Handle(Z, func.result_type()).IsResolved()); |
| + ASSERT(func.NumParameters() == params.parameters->length()); |
| + 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, ¶ms); |
| @@ -3178,6 +3192,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_, |
| @@ -3255,6 +3276,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(); |
| @@ -5252,7 +5278,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 { |
| @@ -5943,11 +5972,24 @@ |
| } |
| -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()); |
| + |
| + const intptr_t try_end_pos = innermost_function().end_token_pos(); |
| + |
| + // 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; |
| @@ -5962,6 +6004,8 @@ |
| 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); |
| @@ -5993,22 +6037,152 @@ |
| SaveExceptionAndStacktrace(exception_var, stack_trace_var); |
| + // 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(try_end_pos, |
| + new(Z) LoadLocalNode(Scanner::kNoSourcePos, controller), |
| + Symbols::AddError(), |
| + args)); |
| + ReturnNode* return_node = new(Z) ReturnNode(Scanner::kNoSourcePos); |
| + AddNodeForFinallyInlining(return_node); |
| + 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(try_end_pos, |
| + 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(try_end_pos, |
| + 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()); |
| - const TryBlocks* outer_try_block = try_blocks_list_->outer_try_block(); |
| - if (outer_try_block != NULL) { |
| - LocalScope* scope = outer_try_block->try_block()->scope; |
| - if (scope->function_level() == current_block_->scope->function_level()) { |
| - current_block_->statements->Add( |
| - AwaitTransformer::RestoreSavedTryContext( |
| - Z, scope->parent(), outer_try_block->try_index())); |
| - } |
| + 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))); |
| } |
| - // Complete the async future with an error. |
| - // Since we control the catch block there is no need to generate a nested |
| - // if/then/else. |
| + 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))); |
| + } |
| + |
| + SaveExceptionAndStacktrace(exception_var, stack_trace_var); |
| + |
| + // 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); |
| @@ -6053,15 +6227,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) { |
| @@ -6094,6 +6271,8 @@ |
| // Open the try block. |
| OpenBlock(); |
| + // This is the outermost try-catch in the function. |
| + ASSERT(try_blocks_list_ == NULL); |
| PushTryBlock(current_block_); |
| SetupSavedTryContext(context_var); |
| @@ -6117,6 +6296,13 @@ |
| } |
| +void Parser::AddAsyncGenClosureParameters(ParamList* params) { |
| + // Create the parameter list for the body closure of an async generator. |
| + // The closure has the same parameters as an asynchronous non-generator. |
| + AddAsyncClosureParameters(params); |
| +} |
| + |
| + |
| RawFunction* Parser::OpenSyncGeneratorFunction(intptr_t func_pos) { |
| Function& body = Function::Handle(Z); |
| String& body_closure_name = String::Handle(Z); |
| @@ -6227,7 +6413,7 @@ |
| void Parser::AddAsyncClosureParameters(ParamList* params) { |
| - // Async closures have two optional parameters: |
| + // Async closures have three optional parameters: |
| // * A continuation result. |
| // * A continuation error. |
| // * A continuation stack trace. |
| @@ -6356,6 +6542,209 @@ |
| } |
| +void Parser::AddAsyncGeneratorVariables() { |
| + // Add to current block's scope: |
| + // var :controller; |
|
Ivan Posva
2015/03/03 01:05:52
// var :async_op;?
hausner
2015/03/03 17:51:28
Done.
|
| + // This variable 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(); |
| + |
| + LocalVariable* async_op_var = new(Z) LocalVariable( |
| + Scanner::kNoSourcePos, Symbols::AsyncOperation(), dynamic_type); |
| + current_block_->scope->AddVariable(async_op_var); |
| + current_block_->scope->CaptureVariable(Symbols::AsyncOperation()); |
| + async_op_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_gen_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; |
|
Ivan Posva
2015/03/03 01:05:52
// var :async_op;
hausner
2015/03/03 17:51:28
Done.
|
| +// 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); |
|
Ivan Posva
2015/03/03 01:05:52
Force capture :async_op?
hausner
2015/03/03 17:51:28
Done.
|
| + |
| + 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)); |
| + |
| + // Add to AST: |
| + // :async_op = <closure>; (containing the original body) |
| + LocalVariable* async_op_var = |
| + current_block_->scope->LookupVariable(Symbols::AsyncOperation(), false); |
| + ClosureNode* cn = new(Z) ClosureNode( |
| + Scanner::kNoSourcePos, closure, NULL, closure_body->scope()); |
| + StoreLocalNode* store_async_op = new (Z) StoreLocalNode( |
| + Scanner::kNoSourcePos, |
| + async_op_var, |
| + cn); |
| + current_block_->statements->Add(store_async_op); |
| + |
| + // :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::Stream())); |
| + 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? |
|
Ivan Posva
2015/03/03 01:05:52
Remove TODO
hausner
2015/03/03 17:51:28
Done.
|
| + // 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()->LookupVariable(Symbols::AsyncOperationParam(), false); |
| + new_body->scope()->LookupVariable(Symbols::AsyncOperationErrorParam(), false); |
| + new_body->scope()->LookupVariable( |
| + Symbols::AsyncOperationStackTraceParam(), false); |
| + new_body->scope()->RecursivelyCaptureAllVariables(); |
| + return new_body; |
| +} |
| + |
| + |
| SequenceNode* Parser::CloseBlock() { |
| SequenceNode* statements = current_block_->statements; |
| if (current_block_->scope != NULL) { |
| @@ -7801,6 +8190,7 @@ |
| // be created with a try scope. |
| ASSERT((*try_scope != NULL) || |
| innermost_function().IsAsyncFunction() || |
| + innermost_function().IsAsyncGenerator() || |
| innermost_function().IsSyncGenClosure() || |
| innermost_function().IsSyncGenerator()); |
| } |
| @@ -7816,9 +8206,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. |
| @@ -8263,7 +8655,9 @@ |
| ASSERT(innermost_function().IsAsyncClosure() || |
| innermost_function().IsAsyncFunction() || |
| innermost_function().IsSyncGenClosure() || |
| - innermost_function().IsSyncGenerator()); |
| + innermost_function().IsSyncGenerator() || |
| + innermost_function().IsAsyncGenerator() || |
| + innermost_function().IsAsyncGenClosure()); |
| // Add :saved_exception_var and :saved_stack_trace_var to current scope. |
| // They will automatically get captured. |
| // Parallel try statements share the same set of variables. |
| @@ -8297,7 +8691,9 @@ |
| ASSERT(innermost_function().IsAsyncClosure() || |
| innermost_function().IsAsyncFunction() || |
| innermost_function().IsSyncGenClosure() || |
| - innermost_function().IsSyncGenerator()); |
| + innermost_function().IsSyncGenerator() || |
| + innermost_function().IsAsyncGenClosure() || |
| + innermost_function().IsAsyncGenerator()); |
| LocalVariable* saved_exception_var = current_block_->scope->LookupVariable( |
| Symbols::SavedExceptionVar(), false); |
| ASSERT(saved_exception_var != NULL); |
| @@ -8331,7 +8727,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)) { |
| LocalScope* scope = try_blocks_list_->try_block()->scope; |
| if (scope->function_level() == current_block_->scope->function_level()) { |
| @@ -8440,7 +8838,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(); |
| @@ -8479,7 +8876,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); |
| @@ -8489,7 +8886,9 @@ |
| if (innermost_function().IsAsyncClosure() || |
| innermost_function().IsAsyncFunction() || |
| innermost_function().IsSyncGenClosure() || |
| - innermost_function().IsSyncGenerator()) { |
| + innermost_function().IsSyncGenerator() || |
| + innermost_function().IsAsyncGenerator() || |
| + innermost_function().IsAsyncGenClosure()) { |
| const TryBlocks* try_block = try_blocks_list_->outer_try_block(); |
| if (try_block != NULL) { |
| LocalScope* scope = try_block->try_block()->scope; |
| @@ -8602,7 +9001,9 @@ |
| if (innermost_function().IsAsyncClosure() || |
| innermost_function().IsAsyncFunction() || |
| innermost_function().IsSyncGenClosure() || |
| - innermost_function().IsSyncGenerator()) { |
| + innermost_function().IsSyncGenerator() || |
| + innermost_function().IsAsyncGenerator() || |
| + innermost_function().IsAsyncGenClosure()) { |
| const TryBlocks* try_block = try_blocks_list_->outer_try_block(); |
| if (try_block != NULL) { |
| LocalScope* scope = try_block->try_block()->scope; |
| @@ -8694,7 +9095,9 @@ |
| if (innermost_function().IsAsyncClosure() || |
| innermost_function().IsAsyncFunction() || |
| innermost_function().IsSyncGenClosure() || |
| - innermost_function().IsSyncGenerator()) { |
| + innermost_function().IsSyncGenerator() || |
| + innermost_function().IsAsyncGenClosure() || |
| + innermost_function().IsAsyncGenerator()) { |
| SetupSavedExceptionAndStacktrace(); |
| } |
| @@ -8716,7 +9119,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); |
| } |
| @@ -8860,6 +9265,146 @@ |
| } |
| +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 and possibly the |
| + // saved try context of the outer try block. |
| + LocalScope* try_scope; |
| + int16_t try_index; |
| + LocalScope* outer_try_scope; |
| + int16_t outer_try_index; |
| + CheckAsyncOpInTryBlock(&try_scope, &try_index, |
| + &outer_try_scope, &outer_try_index); |
| + if (try_scope != NULL) { |
| + yield->AddNode( |
| + AwaitTransformer::RestoreSavedTryContext(Z, |
| + try_scope, |
| + try_index)); |
| + if (outer_try_scope != NULL) { |
| + yield->AddNode( |
| + AwaitTransformer::RestoreSavedTryContext(Z, |
| + outer_try_scope, |
| + outer_try_index)); |
| + } |
| + } else { |
| + ASSERT(outer_try_scope == NULL); |
| + } |
| + } 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.add[Stream](expr)) { |
| + // return; |
| + // } |
| + // await_marker; |
| + // continuation_return; |
| + // restore saved_try_context |
| + |
| + SequenceNode* true_branch = |
| + new(Z) SequenceNode(Scanner::kNoSourcePos, current_block_->scope); |
| + AstNode* return_from_generator = new(Z) ReturnNode(yield_pos); |
| + true_branch->Add(return_from_generator); |
| + AddNodeForFinallyInlining(return_from_generator); |
| + AstNode* if_is_cancelled = |
| + new(Z) IfNode(Scanner::kNoSourcePos, add_call, true_branch, NULL); |
| + yield->AddNode(if_is_cancelled); |
| + |
| + AwaitMarkerNode* await_marker = new(Z) AwaitMarkerNode(); |
| + await_marker->set_scope(current_block_->scope); |
| + yield->AddNode(await_marker); |
| + ReturnNode* continuation_return = new(Z) ReturnNode(yield_pos); |
| + continuation_return->set_return_type(ReturnNode::kContinuationTarget); |
| + yield->AddNode(continuation_return); |
| + |
| + // If this expression is part of a try block, also append the code for |
| + // restoring the saved try context that lives on the stack and possibly the |
| + // saved try context of the outer try block. |
| + LocalScope* try_scope; |
| + int16_t try_index; |
| + LocalScope* outer_try_scope; |
| + int16_t outer_try_index; |
| + CheckAsyncOpInTryBlock(&try_scope, &try_index, |
| + &outer_try_scope, &outer_try_index); |
| + if (try_scope != NULL) { |
| + yield->AddNode( |
| + AwaitTransformer::RestoreSavedTryContext(Z, |
| + try_scope, |
| + try_index)); |
| + if (outer_try_scope != NULL) { |
| + yield->AddNode( |
| + AwaitTransformer::RestoreSavedTryContext(Z, |
| + outer_try_scope, |
| + outer_try_index)); |
| + } |
| + } else { |
| + ASSERT(outer_try_scope == NULL); |
| + } |
| + } |
| + return yield; |
| +} |
| + |
| + |
| AstNode* Parser::ParseStatement() { |
| TRACE_PARSER("ParseStatement"); |
| AstNode* statement = NULL; |
| @@ -8899,7 +9444,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); |
| @@ -8919,71 +9465,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 and possibly the |
| - // saved try context of the outer try block. |
| - LocalScope* try_scope; |
| - int16_t try_index; |
| - LocalScope* outer_try_scope; |
| - int16_t outer_try_index; |
| - CheckAsyncOpInTryBlock(&try_scope, &try_index, |
| - &outer_try_scope, &outer_try_index); |
| - if (try_scope != NULL) { |
| - yield->AddNode( |
| - AwaitTransformer::RestoreSavedTryContext(Z, |
| - try_scope, |
| - try_index)); |
| - if (outer_try_scope != NULL) { |
| - yield->AddNode( |
| - AwaitTransformer::RestoreSavedTryContext(Z, |
| - outer_try_scope, |
| - outer_try_index)); |
| - } |
| - } else { |
| - ASSERT(outer_try_scope == NULL); |
| - } |
| - |
| - statement = yield; |
| + statement = ParseYieldStatement(); |
| ExpectSemicolon(); |
| } else if (token == Token::kIF) { |
| statement = ParseIfStatement(label_name); |
| @@ -9844,8 +10326,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(); |
| @@ -11676,7 +12160,7 @@ |
| } else if (CurrentToken() == Token::kLBRACE) { |
| primary = ParseMapLiteral(type_pos, is_const, type_arguments); |
| } else { |
| - ReportError("unexpected token %s", Token::Str(CurrentToken())); |
| + UnexpectedToken(); |
| } |
| return primary; |
| } |