mirror of
https://github.com/dart-lang/sdk
synced 2024-09-22 01:34:52 +00:00
Transform functions marked as async
In a nutshell: foo(params) async { <body> return result; } transforms to foo(params) async { var c = new Completer(); var async_body = () { <body> completer.complete(result); } new Future(async_body); return c.future; } BUG= R=hausner@google.com Review URL: https://codereview.chromium.org//362153002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@38681 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
parent
30366cf249
commit
dfc33496a4
|
@ -165,6 +165,7 @@ class SequenceNode : public AstNode {
|
|||
void Add(AstNode* node) { nodes_.Add(node); }
|
||||
intptr_t length() const { return nodes_.length(); }
|
||||
AstNode* NodeAt(intptr_t index) const { return nodes_[index]; }
|
||||
void ReplaceNodeAt(intptr_t index, AstNode* value) { nodes_[index] = value; }
|
||||
|
||||
DECLARE_COMMON_NODE_FUNCTIONS(SequenceNode);
|
||||
|
||||
|
|
|
@ -5425,6 +5425,11 @@ void Function::set_kind(RawFunction::Kind value) const {
|
|||
}
|
||||
|
||||
|
||||
void Function::set_modifier(RawFunction::AsyncModifier value) const {
|
||||
set_kind_tag(ModifierBits::update(value, raw_ptr()->kind_tag_));
|
||||
}
|
||||
|
||||
|
||||
void Function::set_is_intrinsic(bool value) const {
|
||||
set_kind_tag(IntrinsicBit::update(value, raw_ptr()->kind_tag_));
|
||||
}
|
||||
|
@ -5455,6 +5460,11 @@ void Function::set_is_external(bool value) const {
|
|||
}
|
||||
|
||||
|
||||
void Function::set_is_async_closure(bool value) const {
|
||||
set_kind_tag(AsyncClosureBit::update(value, raw_ptr()->kind_tag_));
|
||||
}
|
||||
|
||||
|
||||
void Function::set_token_pos(intptr_t value) const {
|
||||
ASSERT(value >= 0);
|
||||
raw_ptr()->token_pos_ = value;
|
||||
|
@ -5462,7 +5472,7 @@ void Function::set_token_pos(intptr_t value) const {
|
|||
|
||||
|
||||
void Function::set_kind_tag(intptr_t value) const {
|
||||
raw_ptr()->kind_tag_ = static_cast<uint16_t>(value);
|
||||
raw_ptr()->kind_tag_ = static_cast<uint32_t>(value);
|
||||
}
|
||||
|
||||
|
||||
|
@ -6052,6 +6062,7 @@ RawFunction* Function::New(const String& name,
|
|||
result.set_parameter_names(Object::empty_array());
|
||||
result.set_name(name);
|
||||
result.set_kind(kind);
|
||||
result.set_modifier(RawFunction::kNoModifier);
|
||||
result.set_is_static(is_static);
|
||||
result.set_is_const(is_const);
|
||||
result.set_is_abstract(is_abstract);
|
||||
|
@ -6061,6 +6072,7 @@ RawFunction* Function::New(const String& name,
|
|||
result.set_is_intrinsic(false);
|
||||
result.set_is_recognized(false);
|
||||
result.set_is_redirecting(false);
|
||||
result.set_is_async_closure(false);
|
||||
result.set_owner(owner);
|
||||
result.set_token_pos(token_pos);
|
||||
result.set_end_token_pos(token_pos);
|
||||
|
|
|
@ -1665,6 +1665,10 @@ class Function : public Object {
|
|||
return KindBits::decode(raw_ptr()->kind_tag_);
|
||||
}
|
||||
|
||||
RawFunction::AsyncModifier modifier() const {
|
||||
return ModifierBits::decode(raw_ptr()->kind_tag_);
|
||||
}
|
||||
|
||||
static const char* KindToCString(RawFunction::Kind kind);
|
||||
|
||||
bool is_static() const { return StaticBit::decode(raw_ptr()->kind_tag_); }
|
||||
|
@ -1811,6 +1815,11 @@ class Function : public Object {
|
|||
void SetIsOptimizable(bool value) const;
|
||||
void SetIsNativeAutoSetupScope(bool value) const;
|
||||
|
||||
bool is_async_closure() const {
|
||||
return AsyncClosureBit::decode(raw_ptr()->kind_tag_);
|
||||
}
|
||||
void set_is_async_closure(bool value) const;
|
||||
|
||||
bool is_native() const { return NativeBit::decode(raw_ptr()->kind_tag_); }
|
||||
void set_is_native(bool value) const;
|
||||
|
||||
|
@ -1961,6 +1970,10 @@ class Function : public Object {
|
|||
return kind() == RawFunction::kSignatureFunction;
|
||||
}
|
||||
|
||||
bool IsAsyncFunction() const {
|
||||
return modifier() == RawFunction::kAsync;
|
||||
}
|
||||
|
||||
static intptr_t InstanceSize() {
|
||||
return RoundedAllocationSize(sizeof(RawFunction));
|
||||
}
|
||||
|
@ -2015,6 +2028,8 @@ class Function : public Object {
|
|||
static const int kCtorPhaseBody = 1 << 1;
|
||||
static const int kCtorPhaseAll = (kCtorPhaseInit | kCtorPhaseBody);
|
||||
|
||||
void set_modifier(RawFunction::AsyncModifier value) const;
|
||||
|
||||
private:
|
||||
void set_ic_data_array(const Array& value) const;
|
||||
|
||||
|
@ -2033,6 +2048,8 @@ class Function : public Object {
|
|||
kRedirectingBit = 13,
|
||||
kExternalBit = 14,
|
||||
kAllowsHoistingCheckClassBit = 15,
|
||||
kModifierPos = 16,
|
||||
kAsyncClosureBit = 18,
|
||||
};
|
||||
class KindBits :
|
||||
public BitField<RawFunction::Kind, kKindTagPos, kKindTagSize> {}; // NOLINT
|
||||
|
@ -2049,6 +2066,9 @@ class Function : public Object {
|
|||
class RedirectingBit : public BitField<bool, kRedirectingBit, 1> {};
|
||||
class AllowsHoistingCheckClassBit :
|
||||
public BitField<bool, kAllowsHoistingCheckClassBit, 1> {}; // NOLINT
|
||||
class ModifierBits :
|
||||
public BitField<RawFunction::AsyncModifier, kModifierPos, 2> {}; // NOLINT
|
||||
class AsyncClosureBit : public BitField<bool, kAsyncClosureBit, 1> {};
|
||||
|
||||
void set_name(const String& value) const;
|
||||
void set_kind(RawFunction::Kind value) const;
|
||||
|
|
|
@ -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);
|
||||
|
@ -2898,6 +2899,17 @@ 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");
|
||||
}
|
||||
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 +2924,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 +2959,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 +3016,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 +3086,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 +3404,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 +3527,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 +4848,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 +4912,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 +4948,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 +5051,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 +5088,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 +5505,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 +5557,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 +6467,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;
|
||||
}
|
||||
|
@ -10767,6 +11008,7 @@ void Parser::SkipFunctionLiteral() {
|
|||
params.skipped = true;
|
||||
ParseFormalParameterList(allow_explicit_default_values, false, ¶ms);
|
||||
}
|
||||
ParseFunctionModifier();
|
||||
if (CurrentToken() == Token::kLBRACE) {
|
||||
SkipBlock();
|
||||
ExpectToken(Token::kRBRACE);
|
||||
|
@ -10792,7 +11034,8 @@ void Parser::SkipFunctionPreamble() {
|
|||
// 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 (CurrentLiteral()->raw() == Symbols::Native().raw() ||
|
||||
CurrentLiteral()->raw() == Symbols::Async().raw()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -363,6 +363,8 @@ class Parser : public ValueObject {
|
|||
void ParseTopLevelAccessor(TopLevel* top_level, intptr_t metadata_pos);
|
||||
RawArray* EvaluateMetadata();
|
||||
|
||||
RawFunction::AsyncModifier ParseFunctionModifier();
|
||||
|
||||
// Support for parsing libraries.
|
||||
RawObject* CallLibraryTagHandler(Dart_LibraryTag tag,
|
||||
intptr_t token_pos,
|
||||
|
@ -463,6 +465,7 @@ class Parser : public ValueObject {
|
|||
Array* default_parameter_values);
|
||||
SequenceNode* ParseFunc(const Function& func,
|
||||
Array* default_parameter_values);
|
||||
RawClass* GetClassForAsync(const String& class_name);
|
||||
|
||||
void ParseNativeFunctionBlock(const ParamList* params, const Function& func);
|
||||
|
||||
|
@ -483,7 +486,12 @@ class Parser : public ValueObject {
|
|||
void OpenBlock();
|
||||
void OpenLoopBlock();
|
||||
void OpenFunctionBlock(const Function& func);
|
||||
RawFunction* OpenAsyncFunction(intptr_t formal_param_pos);
|
||||
SequenceNode* CloseBlock();
|
||||
SequenceNode* CloseAsyncFunction(const Function& closure,
|
||||
SequenceNode* closure_node);
|
||||
void CloseAsyncClosure(SequenceNode* body);
|
||||
|
||||
|
||||
LocalVariable* LookupPhaseParameter();
|
||||
LocalVariable* LookupReceiver(LocalScope* from_scope, bool test_only);
|
||||
|
|
|
@ -615,6 +615,11 @@ class RawFunction : public RawObject {
|
|||
kInvokeFieldDispatcher, // invokes a field as a closure.
|
||||
};
|
||||
|
||||
enum AsyncModifier {
|
||||
kNoModifier,
|
||||
kAsync,
|
||||
};
|
||||
|
||||
private:
|
||||
// So that the MarkingVisitor::DetachCode can null out the code fields.
|
||||
friend class MarkingVisitor;
|
||||
|
@ -650,7 +655,7 @@ class RawFunction : public RawObject {
|
|||
int16_t num_fixed_parameters_;
|
||||
int16_t num_optional_parameters_; // > 0: positional; < 0: named.
|
||||
int16_t deoptimization_counter_;
|
||||
uint16_t kind_tag_; // See Function::KindTagBits.
|
||||
uint32_t kind_tag_; // See Function::KindTagBits.
|
||||
uint16_t optimized_instruction_count_;
|
||||
uint16_t optimized_call_site_count_;
|
||||
};
|
||||
|
|
|
@ -698,7 +698,7 @@ RawFunction* Function::ReadFrom(SnapshotReader* reader,
|
|||
func.set_num_fixed_parameters(reader->Read<int16_t>());
|
||||
func.set_num_optional_parameters(reader->Read<int16_t>());
|
||||
func.set_deoptimization_counter(reader->Read<int16_t>());
|
||||
func.set_kind_tag(reader->Read<uint16_t>());
|
||||
func.set_kind_tag(reader->Read<uint32_t>());
|
||||
func.set_optimized_instruction_count(reader->Read<uint16_t>());
|
||||
func.set_optimized_call_site_count(reader->Read<uint16_t>());
|
||||
|
||||
|
@ -739,7 +739,7 @@ void RawFunction::WriteTo(SnapshotWriter* writer,
|
|||
writer->Write<int16_t>(ptr()->num_fixed_parameters_);
|
||||
writer->Write<int16_t>(ptr()->num_optional_parameters_);
|
||||
writer->Write<int16_t>(ptr()->deoptimization_counter_);
|
||||
writer->Write<uint16_t>(ptr()->kind_tag_);
|
||||
writer->Write<uint32_t>(ptr()->kind_tag_);
|
||||
writer->Write<uint16_t>(ptr()->optimized_instruction_count_);
|
||||
writer->Write<uint16_t>(ptr()->optimized_call_site_count_);
|
||||
|
||||
|
|
|
@ -68,6 +68,15 @@ class ObjectPointerVisitor;
|
|||
V(Library, "library") \
|
||||
V(LoadLibrary, "loadLibrary") \
|
||||
V(_LibraryPrefix, "_LibraryPrefix") \
|
||||
V(Async, "async") \
|
||||
V(AsyncCompleter, ":async_completer") \
|
||||
V(AsyncOperation, ":async_op") \
|
||||
V(Future, "Future") \
|
||||
V(FutureConstructor, "Future.") \
|
||||
V(Completer, "Completer") \
|
||||
V(CompleterComplete, "complete") \
|
||||
V(CompleterConstructor, "Completer.") \
|
||||
V(CompleterFuture, "future") \
|
||||
V(Native, "native") \
|
||||
V(Import, "import") \
|
||||
V(Source, "source") \
|
||||
|
|
121
tests/language/async_test.dart
Normal file
121
tests/language/async_test.dart
Normal file
|
@ -0,0 +1,121 @@
|
|||
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// VMOptions=--enable_async
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
topLevelFunction() async { }
|
||||
|
||||
Future<int> topLevelWithParameter(int a) async {
|
||||
return 7 + a;
|
||||
}
|
||||
|
||||
int topLevelWithParameterWrongType(int a) async {
|
||||
return 7 + a;
|
||||
}
|
||||
|
||||
var what = 'async getter';
|
||||
Future<String> get topLevelGetter async {
|
||||
return 'I want to be an ${what}';
|
||||
}
|
||||
|
||||
class A {
|
||||
static int staticVar = 1;
|
||||
|
||||
static staticMethod(int param) async => staticVar + param;
|
||||
static get staticGetter async => staticVar + 3;
|
||||
|
||||
int _x;
|
||||
A(this._x);
|
||||
|
||||
A.fail() async; /// constructor2: compile-time error
|
||||
factory A.create() async {return null; } /// constructor3: compile-time error
|
||||
|
||||
int someMethod(int param1, int param2, int param3) async => _x + param2;
|
||||
int get getter async { return 5 + _x; }
|
||||
operator+(A other) async {
|
||||
return new A(_x + other._x);
|
||||
}
|
||||
|
||||
get value => _x;
|
||||
}
|
||||
|
||||
class B {
|
||||
final _y;
|
||||
const B._internal(this._y);
|
||||
const factory B.createConst(int y) async = A._internal; /// constructor4: compile-time error
|
||||
|
||||
B();
|
||||
|
||||
set dontDoThat(value) async {} /// setter1: compile-time error
|
||||
}
|
||||
|
||||
|
||||
main() {
|
||||
var asyncReturn;
|
||||
|
||||
asyncReturn = topLevelFunction();
|
||||
Expect.isTrue(asyncReturn is Future);
|
||||
|
||||
int a1 = topLevelWithParameter(2); /// type-mismatch1: static type warning, dynamic type error
|
||||
int a2 = topLevelWithParameterWrongType(2); /// type-mismatch2: static type warning, dynamic type error
|
||||
asyncReturn = topLevelWithParameter(4);
|
||||
Expect.isTrue(asyncReturn is Future);
|
||||
asyncReturn.then((int result) => Expect.equals(result, 11));
|
||||
|
||||
asyncReturn = topLevelGetter;
|
||||
Expect.isTrue(asyncReturn is Future);
|
||||
asyncReturn.then((String result) =>
|
||||
Expect.stringEquals(result, 'I want to be an async getter'));
|
||||
|
||||
asyncReturn = A.staticMethod(2);
|
||||
Expect.isTrue(asyncReturn is Future);
|
||||
asyncReturn.then((int result) => Expect.equals(result, 3));
|
||||
|
||||
asyncReturn = A.staticGetter;
|
||||
Expect.isTrue(asyncReturn is Future);
|
||||
asyncReturn.then((int result) => Expect.equals(result, 4));
|
||||
|
||||
A a = new A(13);
|
||||
|
||||
asyncReturn = a.someMethod(1,2,3); /// type-mismatch3: static type warning, dynamic type error
|
||||
Expect.isTrue(asyncReturn is Future); /// type-mismatch3: continued
|
||||
asyncReturn.then((int result) => Expect.equals(result, 15)); /// type-mismatch3: continued
|
||||
|
||||
asyncReturn = a.getter; /// type-mismatch4: static type warning, dynamic type error
|
||||
Expect.isTrue(asyncReturn is Future); /// type-mismatch4: continued
|
||||
asyncReturn.then((int result) => Expect.equals(result, 18)); /// type-mismatch4: continued
|
||||
|
||||
var b = new A(9);
|
||||
asyncReturn = a + b;
|
||||
Expect.isTrue(asyncReturn is Future);
|
||||
asyncReturn.then((A result) => Expect.equals(result.value, 22));
|
||||
|
||||
var foo = 17;
|
||||
bar(int p1, p2) async {
|
||||
var z = 8;
|
||||
return p2 + z + foo;
|
||||
}
|
||||
asyncReturn = bar(1,2);
|
||||
Expect.isTrue(asyncReturn is Future);
|
||||
asyncReturn.then((int result) => Expect.equals(result, 27));
|
||||
|
||||
var moreNesting = (int shadowP1, String p2, num p3) {
|
||||
var z = 3;
|
||||
aa(int shadowP1) async {
|
||||
return foo + z + p3 + shadowP1;
|
||||
}
|
||||
return aa(6);
|
||||
};
|
||||
asyncReturn = moreNesting(1, "ignore", 2);
|
||||
Expect.isTrue(asyncReturn is Future);
|
||||
asyncReturn.then((int result) => Expect.equals(result, 28));
|
||||
|
||||
var b1 = const B.createConst(4); /// constructor4: compile-time error
|
||||
var b2 = new B();
|
||||
b2.dontDoThat = 4; /// setter1: compile-time error
|
||||
}
|
|
@ -31,6 +31,9 @@ deferred_constraints_constants_old_syntax_test/default_argument2: Fail, Ok
|
|||
deferred_constraints_constants_old_syntax_test/constructor1: Fail, Ok
|
||||
deferred_constraints_constants_old_syntax_test/constructor2: Fail, Ok
|
||||
|
||||
[ $runtime != vm ]
|
||||
# Async tests are currently only supported by the vm.
|
||||
async_test/*: Skip
|
||||
|
||||
[ $compiler == dart2dart]
|
||||
deferred_load_library_wrong_args_test/none: Fail # Issue 17523
|
||||
|
|
Loading…
Reference in a new issue