| Index: runtime/vm/parser.cc
|
| diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc
|
| index c68767a7db78aa77d7cf79d07cbaed4c68792494..ebabf104d6c6b1823544064e9abd8351bb51ec34 100644
|
| --- a/runtime/vm/parser.cc
|
| +++ b/runtime/vm/parser.cc
|
| @@ -39,6 +39,7 @@ DEFINE_FLAG(bool, enable_asserts, false, "Enable assert statements.");
|
| DEFINE_FLAG(bool, enable_type_checks, false, "Enable type checks.");
|
| DEFINE_FLAG(bool, trace_parser, false, "Trace parser operations.");
|
| DEFINE_FLAG(bool, warn_mixin_typedef, true, "Warning on legacy mixin typedef.");
|
| +DEFINE_FLAG(bool, enable_async, false, "Enable async operations.");
|
| DECLARE_FLAG(bool, error_on_bad_type);
|
| DECLARE_FLAG(bool, throw_on_javascript_int_overflow);
|
| DECLARE_FLAG(bool, warn_on_javascript_compatibility);
|
| @@ -795,7 +796,7 @@ void Parser::ParseFunction(ParsedFunction* parsed_function) {
|
| case RawFunction::kConstructor:
|
| // The call to a redirecting factory is redirected.
|
| ASSERT(!func.IsRedirectingFactory());
|
| - if (!func.IsImplicitConstructor()) {
|
| + if (!func.IsImplicitConstructor() && !func.is_async_closure()) {
|
| parser.SkipFunctionPreamble();
|
| }
|
| node_sequence = parser.ParseFunc(func, &default_parameter_values);
|
| @@ -2898,6 +2899,18 @@ SequenceNode* Parser::ParseConstructor(const Function& func,
|
| }
|
|
|
|
|
| +// TODO(mlippautz): Once we know where these classes should come from, adjust
|
| +// how we get their definition.
|
| +RawClass* Parser::GetClassForAsync(const String& class_name) {
|
| + const Class& cls = Class::Handle(library_.LookupClass(class_name));
|
| + if (cls.IsNull()) {
|
| + ReportError("async modifier requires dart:async to be imported without "
|
| + "prefix");
|
| + }
|
| + return cls.raw();
|
| +}
|
| +
|
| +
|
| // Parser is at the opening parenthesis of the formal parameter
|
| // declaration of the function or constructor.
|
| // Parse the formal parameters and code.
|
| @@ -2912,6 +2925,7 @@ SequenceNode* Parser::ParseFunc(const Function& func,
|
| intptr_t saved_try_index = last_used_try_index_;
|
| last_used_try_index_ = 0;
|
|
|
| + intptr_t formal_params_pos = TokenPos();
|
| // TODO(12455) : Need better validation mechanism.
|
|
|
| if (func.IsConstructor()) {
|
| @@ -2946,12 +2960,23 @@ SequenceNode* Parser::ParseFunc(const Function& func,
|
| &Symbols::TypeArgumentsParameter(),
|
| &Type::ZoneHandle(I, Type::DynamicType()));
|
| }
|
| - ASSERT((CurrentToken() == Token::kLPAREN) || func.IsGetterFunction());
|
| + ASSERT((CurrentToken() == Token::kLPAREN) ||
|
| + func.IsGetterFunction() ||
|
| + func.is_async_closure());
|
| const bool allow_explicit_default_values = true;
|
| if (func.IsGetterFunction()) {
|
| // Populate function scope with the formal parameters. Since in this case
|
| // we are compiling a getter this will at most populate the receiver.
|
| AddFormalParamsToScope(¶ms, current_block_->scope);
|
| + } else if (func.is_async_closure()) {
|
| + AddFormalParamsToScope(¶ms, current_block_->scope);
|
| + ASSERT(AbstractType::Handle(I, func.result_type()).IsResolved());
|
| + ASSERT(func.NumParameters() == params.parameters->length());
|
| + if (!Function::Handle(func.parent_function()).IsGetterFunction()) {
|
| + // Parse away any formal parameters, as they are accessed as as context
|
| + // variables.
|
| + ParseFormalParameterList(allow_explicit_default_values, false, ¶ms);
|
| + }
|
| } else {
|
| ParseFormalParameterList(allow_explicit_default_values, false, ¶ms);
|
|
|
| @@ -2992,7 +3017,16 @@ SequenceNode* Parser::ParseFunc(const Function& func,
|
| }
|
| }
|
|
|
| + RawFunction::AsyncModifier func_modifier = ParseFunctionModifier();
|
| + func.set_modifier(func_modifier);
|
| +
|
| OpenBlock(); // Open a nested scope for the outermost function block.
|
| +
|
| + Function& async_closure = Function::ZoneHandle(I);
|
| + if (func.IsAsyncFunction() && !func.is_async_closure()) {
|
| + async_closure = OpenAsyncFunction(formal_params_pos);
|
| + }
|
| +
|
| intptr_t end_token_pos = 0;
|
| if (CurrentToken() == Token::kLBRACE) {
|
| ConsumeToken();
|
| @@ -3053,6 +3087,11 @@ SequenceNode* Parser::ParseFunc(const Function& func,
|
| func.end_token_pos() == end_token_pos);
|
| func.set_end_token_pos(end_token_pos);
|
| SequenceNode* body = CloseBlock();
|
| + if (func.IsAsyncFunction() && !func.is_async_closure()) {
|
| + body = CloseAsyncFunction(async_closure, body);
|
| + } else if (func.is_async_closure()) {
|
| + CloseAsyncClosure(body);
|
| + }
|
| current_block_->statements->Add(body);
|
| innermost_function_ = saved_innermost_function.raw();
|
| last_used_try_index_ = saved_try_index;
|
| @@ -3366,6 +3405,15 @@ void Parser::ParseMethodOrConstructor(ClassDesc* members, MemberDesc* method) {
|
| method->name->ToCString());
|
| }
|
|
|
| + RawFunction::AsyncModifier async_modifier = ParseFunctionModifier();
|
| + if ((method->IsFactoryOrConstructor() || method->IsSetter()) &&
|
| + async_modifier != RawFunction::kNoModifier) {
|
| + ReportError(method->name_pos,
|
| + "%s '%s' may not be async",
|
| + (method->IsSetter()) ? "setter" : "constructor",
|
| + method->name->ToCString());
|
| + }
|
| +
|
| intptr_t method_end_pos = TokenPos();
|
| if ((CurrentToken() == Token::kLBRACE) ||
|
| (CurrentToken() == Token::kARROW)) {
|
| @@ -3480,6 +3528,7 @@ void Parser::ParseMethodOrConstructor(ClassDesc* members, MemberDesc* method) {
|
| func.set_result_type(*method->type);
|
| func.set_end_token_pos(method_end_pos);
|
| func.set_is_redirecting(is_redirecting);
|
| + func.set_modifier(async_modifier);
|
| if (method->has_native && library_.is_dart_scheme() &&
|
| library_.IsPrivate(*method->name)) {
|
| func.set_is_visible(false);
|
| @@ -4800,6 +4849,17 @@ void Parser::ParseTopLevelVariable(TopLevel* top_level,
|
| }
|
|
|
|
|
| +RawFunction::AsyncModifier Parser::ParseFunctionModifier() {
|
| + if (FLAG_enable_async) {
|
| + if (CurrentLiteral()->raw() == Symbols::Async().raw()) {
|
| + ConsumeToken();
|
| + return RawFunction::kAsync;
|
| + }
|
| + }
|
| + return RawFunction::kNoModifier;
|
| +}
|
| +
|
| +
|
| void Parser::ParseTopLevelFunction(TopLevel* top_level,
|
| intptr_t metadata_pos) {
|
| TRACE_PARSER("ParseTopLevelFunction");
|
| @@ -4853,6 +4913,8 @@ void Parser::ParseTopLevelFunction(TopLevel* top_level,
|
| const bool allow_explicit_default_values = true;
|
| ParseFormalParameterList(allow_explicit_default_values, false, ¶ms);
|
|
|
| + RawFunction::AsyncModifier func_modifier = ParseFunctionModifier();
|
| +
|
| intptr_t function_end_pos = function_pos;
|
| bool is_native = false;
|
| if (is_external) {
|
| @@ -4887,6 +4949,7 @@ void Parser::ParseTopLevelFunction(TopLevel* top_level,
|
| decl_begin_pos));
|
| func.set_result_type(result_type);
|
| func.set_end_token_pos(function_end_pos);
|
| + func.set_modifier(func_modifier);
|
| if (is_native && library_.is_dart_scheme() && library_.IsPrivate(func_name)) {
|
| func.set_is_visible(false);
|
| }
|
| @@ -4989,6 +5052,8 @@ void Parser::ParseTopLevelAccessor(TopLevel* top_level,
|
| field_name->ToCString());
|
| }
|
|
|
| + RawFunction::AsyncModifier func_modifier = ParseFunctionModifier();
|
| +
|
| intptr_t accessor_end_pos = accessor_pos;
|
| bool is_native = false;
|
| if (is_external) {
|
| @@ -5024,6 +5089,7 @@ void Parser::ParseTopLevelAccessor(TopLevel* top_level,
|
| decl_begin_pos));
|
| func.set_result_type(result_type);
|
| func.set_end_token_pos(accessor_end_pos);
|
| + func.set_modifier(func_modifier);
|
| if (is_native && library_.is_dart_scheme() &&
|
| library_.IsPrivate(accessor_name)) {
|
| func.set_is_visible(false);
|
| @@ -5440,6 +5506,45 @@ void Parser::OpenFunctionBlock(const Function& func) {
|
| }
|
|
|
|
|
| +RawFunction* Parser::OpenAsyncFunction(intptr_t formal_param_pos) {
|
| + // Create the closure containing the old body of this function.
|
| + Class& sig_cls = Class::ZoneHandle(I);
|
| + Type& sig_type = Type::ZoneHandle(I);
|
| + Function& closure = Function::ZoneHandle(I);
|
| + String& sig = String::ZoneHandle(I);
|
| + ParamList closure_params;
|
| + closure_params.AddFinalParameter(
|
| + formal_param_pos,
|
| + &Symbols::ClosureParameter(),
|
| + &Type::ZoneHandle(I, Type::DynamicType()));
|
| + closure = Function::NewClosureFunction(
|
| + Symbols::AnonymousClosure(),
|
| + innermost_function(),
|
| + formal_param_pos);
|
| + AddFormalParamsToFunction(&closure_params, closure);
|
| + closure.set_is_async_closure(true);
|
| + closure.set_result_type(AbstractType::Handle(Type::DynamicType()));
|
| + sig = closure.Signature();
|
| + sig_cls = library_.LookupLocalClass(sig);
|
| + if (sig_cls.IsNull()) {
|
| + sig_cls = Class::NewSignatureClass(sig, closure, script_, formal_param_pos);
|
| + library_.AddClass(sig_cls);
|
| + }
|
| + closure.set_signature_class(sig_cls);
|
| + sig_type = sig_cls.SignatureType();
|
| + if (!sig_type.IsFinalized()) {
|
| + ClassFinalizer::FinalizeType(
|
| + sig_cls, sig_type, ClassFinalizer::kCanonicalize);
|
| + }
|
| + ASSERT(AbstractType::Handle(I, closure.result_type()).IsResolved());
|
| + ASSERT(closure.NumParameters() == closure_params.parameters->length());
|
| + OpenFunctionBlock(closure);
|
| + AddFormalParamsToScope(&closure_params, current_block_->scope);
|
| + OpenBlock();
|
| + return closure.raw();
|
| +}
|
| +
|
| +
|
| SequenceNode* Parser::CloseBlock() {
|
| SequenceNode* statements = current_block_->statements;
|
| if (current_block_->scope != NULL) {
|
| @@ -5453,6 +5558,141 @@ SequenceNode* Parser::CloseBlock() {
|
| }
|
|
|
|
|
| +SequenceNode* Parser::CloseAsyncFunction(const Function& closure,
|
| + SequenceNode* closure_body) {
|
| + ASSERT(!closure.IsNull());
|
| + ASSERT(closure_body != NULL);
|
| + // The block for the async closure body has already been closed. Close the
|
| + // corresponding function block.
|
| + CloseBlock();
|
| +
|
| + // Create and return a new future that executes a closure with the current
|
| + // body.
|
| +
|
| + bool found = false;
|
| +
|
| + // No need to capture parameters or other variables, since they have already
|
| + // been captured in the corresponding scope as the body has been parsed within
|
| + // a nested block (contained in the async funtion's block).
|
| + const Class& future = Class::ZoneHandle(I,
|
| + GetClassForAsync(Symbols::Future()));
|
| + ASSERT(!future.IsNull());
|
| + const Function& constructor = Function::ZoneHandle(I,
|
| + future.LookupFunction(Symbols::FutureConstructor()));
|
| + ASSERT(!constructor.IsNull());
|
| + const Class& completer = Class::ZoneHandle(I,
|
| + GetClassForAsync(Symbols::Completer()));
|
| + ASSERT(!completer.IsNull());
|
| + const Function& completer_constructor = Function::ZoneHandle(I,
|
| + completer.LookupFunction(Symbols::CompleterConstructor()));
|
| + ASSERT(!completer_constructor.IsNull());
|
| +
|
| + // Add to AST:
|
| + // var :async_op;
|
| + // var :async_completer;
|
| + LocalVariable* async_op_var = new (I) LocalVariable(
|
| + Scanner::kNoSourcePos,
|
| + Symbols::AsyncOperation(),
|
| + Type::ZoneHandle(I, Type::DynamicType()));
|
| + current_block_->scope->AddVariable(async_op_var);
|
| + found = closure_body->scope()->CaptureVariable(Symbols::AsyncOperation());
|
| + ASSERT(found);
|
| + LocalVariable* async_completer = new (I) LocalVariable(
|
| + Scanner::kNoSourcePos,
|
| + Symbols::AsyncCompleter(),
|
| + Type::ZoneHandle(I, Type::DynamicType()));
|
| + current_block_->scope->AddVariable(async_completer);
|
| + found = closure_body->scope()->CaptureVariable(Symbols::AsyncCompleter());
|
| + ASSERT(found);
|
| +
|
| + // Add to AST:
|
| + // :async_completer = new Completer();
|
| + ArgumentListNode* empty_args = new (I) ArgumentListNode(
|
| + Scanner::kNoSourcePos);
|
| + ConstructorCallNode* completer_constructor_node = new (I) ConstructorCallNode(
|
| + Scanner::kNoSourcePos,
|
| + TypeArguments::ZoneHandle(I),
|
| + completer_constructor,
|
| + empty_args);
|
| + StoreLocalNode* store_completer = new (I) StoreLocalNode(
|
| + Scanner::kNoSourcePos,
|
| + async_completer,
|
| + completer_constructor_node);
|
| + current_block_->statements->Add(store_completer);
|
| +
|
| + // Add to AST:
|
| + // :async_op = <closure>; (containing the original body)
|
| + ClosureNode* cn = new(I) ClosureNode(
|
| + Scanner::kNoSourcePos, closure, NULL, closure_body->scope());
|
| + StoreLocalNode* store_async_op = new (I) StoreLocalNode(
|
| + Scanner::kNoSourcePos,
|
| + async_op_var,
|
| + cn);
|
| + current_block_->statements->Add(store_async_op);
|
| +
|
| + // Add to AST:
|
| + // new Future(:async_op);
|
| + ArgumentListNode* arguments = new (I) ArgumentListNode(Scanner::kNoSourcePos);
|
| + arguments->Add(new (I) LoadLocalNode(
|
| + Scanner::kNoSourcePos, async_op_var));
|
| + ConstructorCallNode* future_node = new (I) ConstructorCallNode(
|
| + Scanner::kNoSourcePos, TypeArguments::ZoneHandle(I), constructor,
|
| + arguments);
|
| + current_block_->statements->Add(future_node);
|
| +
|
| + // Add to AST:
|
| + // return :async_completer.future;
|
| + ReturnNode* return_node = new (I) ReturnNode(
|
| + Scanner::kNoSourcePos,
|
| + new (I) InstanceGetterNode(
|
| + Scanner::kNoSourcePos,
|
| + new (I) LoadLocalNode(
|
| + Scanner::kNoSourcePos,
|
| + async_completer),
|
| + Symbols::CompleterFuture()));
|
| + current_block_->statements->Add(return_node);
|
| + return CloseBlock();
|
| +}
|
| +
|
| +
|
| +void Parser::CloseAsyncClosure(SequenceNode* body) {
|
| + ASSERT(body != NULL);
|
| + // Replace an optional ReturnNode with the appropriate completer calls.
|
| + intptr_t last_index = body->length() - 1;
|
| + AstNode* last = NULL;
|
| + if (last_index >= 0) {
|
| + // Non-empty async closure.
|
| + last = body->NodeAt(last_index);
|
| + }
|
| + ArgumentListNode* args = new (I) ArgumentListNode(Scanner::kNoSourcePos);
|
| + LocalVariable* completer = body->scope()->LookupVariable(
|
| + Symbols::AsyncCompleter(), false);
|
| + ASSERT(completer != NULL);
|
| + if (last != NULL && last->IsReturnNode()) {
|
| + // Replace
|
| + // return <expr>;
|
| + // with
|
| + // completer.complete(<expr>);
|
| + args->Add(body->NodeAt(last_index)->AsReturnNode()->value());
|
| + body->ReplaceNodeAt(last_index,
|
| + new (I) InstanceCallNode(
|
| + Scanner::kNoSourcePos,
|
| + new (I) LoadLocalNode(Scanner::kNoSourcePos, completer),
|
| + Symbols::CompleterComplete(),
|
| + args));
|
| + } else {
|
| + // Add to AST:
|
| + // completer.complete();
|
| + body->Add(
|
| + new (I) InstanceCallNode(
|
| + Scanner::kNoSourcePos,
|
| + new (I) LoadLocalNode(Scanner::kNoSourcePos, completer),
|
| + Symbols::CompleterComplete(),
|
| + args));
|
| + }
|
| +}
|
| +
|
| +
|
| // Set up default values for all optional parameters to the function.
|
| void Parser::SetupDefaultsForOptionalParams(const ParamList* params,
|
| Array* default_values) {
|
| @@ -6228,7 +6468,9 @@ bool Parser::IsFunctionDeclaration() {
|
| if ((CurrentToken() == Token::kLBRACE) ||
|
| (CurrentToken() == Token::kARROW) ||
|
| (is_top_level_ && IsLiteral("native")) ||
|
| - is_external) {
|
| + is_external ||
|
| + (FLAG_enable_async &&
|
| + CurrentLiteral()->raw() == Symbols::Async().raw())) {
|
| SetPosition(saved_pos);
|
| return true;
|
| }
|
| @@ -6271,6 +6513,7 @@ bool Parser::IsFunctionLiteral() {
|
| const intptr_t saved_pos = TokenPos();
|
| bool is_function_literal = false;
|
| SkipToMatchingParenthesis();
|
| + ParseFunctionModifier();
|
| if ((CurrentToken() == Token::kLBRACE) ||
|
| (CurrentToken() == Token::kARROW)) {
|
| is_function_literal = true;
|
| @@ -10767,6 +11010,7 @@ void Parser::SkipFunctionLiteral() {
|
| params.skipped = true;
|
| ParseFormalParameterList(allow_explicit_default_values, false, ¶ms);
|
| }
|
| + ParseFunctionModifier();
|
| if (CurrentToken() == Token::kLBRACE) {
|
| SkipBlock();
|
| ExpectToken(Token::kRBRACE);
|
| @@ -10783,18 +11027,19 @@ void Parser::SkipFunctionLiteral() {
|
| void Parser::SkipFunctionPreamble() {
|
| while (true) {
|
| const Token::Kind token = CurrentToken();
|
| - if (token == Token::kLPAREN ||
|
| - token == Token::kARROW ||
|
| - token == Token::kSEMICOLON ||
|
| - token == Token::kLBRACE) {
|
| + if (token == Token::kLPAREN) {
|
| return;
|
| }
|
| - // Case handles "native" keyword, but also return types of form
|
| - // native.SomeType where native is the name of a library.
|
| - if (token == Token::kIDENT && LookaheadToken(1) != Token::kPERIOD) {
|
| - if (CurrentLiteral()->raw() == Symbols::Native().raw()) {
|
| + if (token == Token::kGET) {
|
| + if (LookaheadToken(1) == Token::kLPAREN) {
|
| + // Case: Function/method named get.
|
| + ConsumeToken(); // Parse away 'get' (the function's name).
|
| return;
|
| }
|
| + // Case: Getter.
|
| + ConsumeToken(); // Parse away 'get'.
|
| + ConsumeToken(); // Parse away the getter name.
|
| + return;
|
| }
|
| ConsumeToken();
|
| }
|
|
|