mirror of
https://github.com/dart-lang/sdk
synced 2024-09-22 05:31:22 +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); }
|
void Add(AstNode* node) { nodes_.Add(node); }
|
||||||
intptr_t length() const { return nodes_.length(); }
|
intptr_t length() const { return nodes_.length(); }
|
||||||
AstNode* NodeAt(intptr_t index) const { return nodes_[index]; }
|
AstNode* NodeAt(intptr_t index) const { return nodes_[index]; }
|
||||||
|
void ReplaceNodeAt(intptr_t index, AstNode* value) { nodes_[index] = value; }
|
||||||
|
|
||||||
DECLARE_COMMON_NODE_FUNCTIONS(SequenceNode);
|
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 {
|
void Function::set_is_intrinsic(bool value) const {
|
||||||
set_kind_tag(IntrinsicBit::update(value, raw_ptr()->kind_tag_));
|
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 {
|
void Function::set_token_pos(intptr_t value) const {
|
||||||
ASSERT(value >= 0);
|
ASSERT(value >= 0);
|
||||||
raw_ptr()->token_pos_ = value;
|
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 {
|
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_parameter_names(Object::empty_array());
|
||||||
result.set_name(name);
|
result.set_name(name);
|
||||||
result.set_kind(kind);
|
result.set_kind(kind);
|
||||||
|
result.set_modifier(RawFunction::kNoModifier);
|
||||||
result.set_is_static(is_static);
|
result.set_is_static(is_static);
|
||||||
result.set_is_const(is_const);
|
result.set_is_const(is_const);
|
||||||
result.set_is_abstract(is_abstract);
|
result.set_is_abstract(is_abstract);
|
||||||
|
@ -6061,6 +6072,7 @@ RawFunction* Function::New(const String& name,
|
||||||
result.set_is_intrinsic(false);
|
result.set_is_intrinsic(false);
|
||||||
result.set_is_recognized(false);
|
result.set_is_recognized(false);
|
||||||
result.set_is_redirecting(false);
|
result.set_is_redirecting(false);
|
||||||
|
result.set_is_async_closure(false);
|
||||||
result.set_owner(owner);
|
result.set_owner(owner);
|
||||||
result.set_token_pos(token_pos);
|
result.set_token_pos(token_pos);
|
||||||
result.set_end_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_);
|
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);
|
static const char* KindToCString(RawFunction::Kind kind);
|
||||||
|
|
||||||
bool is_static() const { return StaticBit::decode(raw_ptr()->kind_tag_); }
|
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 SetIsOptimizable(bool value) const;
|
||||||
void SetIsNativeAutoSetupScope(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_); }
|
bool is_native() const { return NativeBit::decode(raw_ptr()->kind_tag_); }
|
||||||
void set_is_native(bool value) const;
|
void set_is_native(bool value) const;
|
||||||
|
|
||||||
|
@ -1961,6 +1970,10 @@ class Function : public Object {
|
||||||
return kind() == RawFunction::kSignatureFunction;
|
return kind() == RawFunction::kSignatureFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsAsyncFunction() const {
|
||||||
|
return modifier() == RawFunction::kAsync;
|
||||||
|
}
|
||||||
|
|
||||||
static intptr_t InstanceSize() {
|
static intptr_t InstanceSize() {
|
||||||
return RoundedAllocationSize(sizeof(RawFunction));
|
return RoundedAllocationSize(sizeof(RawFunction));
|
||||||
}
|
}
|
||||||
|
@ -2015,6 +2028,8 @@ class Function : public Object {
|
||||||
static const int kCtorPhaseBody = 1 << 1;
|
static const int kCtorPhaseBody = 1 << 1;
|
||||||
static const int kCtorPhaseAll = (kCtorPhaseInit | kCtorPhaseBody);
|
static const int kCtorPhaseAll = (kCtorPhaseInit | kCtorPhaseBody);
|
||||||
|
|
||||||
|
void set_modifier(RawFunction::AsyncModifier value) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void set_ic_data_array(const Array& value) const;
|
void set_ic_data_array(const Array& value) const;
|
||||||
|
|
||||||
|
@ -2033,6 +2048,8 @@ class Function : public Object {
|
||||||
kRedirectingBit = 13,
|
kRedirectingBit = 13,
|
||||||
kExternalBit = 14,
|
kExternalBit = 14,
|
||||||
kAllowsHoistingCheckClassBit = 15,
|
kAllowsHoistingCheckClassBit = 15,
|
||||||
|
kModifierPos = 16,
|
||||||
|
kAsyncClosureBit = 18,
|
||||||
};
|
};
|
||||||
class KindBits :
|
class KindBits :
|
||||||
public BitField<RawFunction::Kind, kKindTagPos, kKindTagSize> {}; // NOLINT
|
public BitField<RawFunction::Kind, kKindTagPos, kKindTagSize> {}; // NOLINT
|
||||||
|
@ -2049,6 +2066,9 @@ class Function : public Object {
|
||||||
class RedirectingBit : public BitField<bool, kRedirectingBit, 1> {};
|
class RedirectingBit : public BitField<bool, kRedirectingBit, 1> {};
|
||||||
class AllowsHoistingCheckClassBit :
|
class AllowsHoistingCheckClassBit :
|
||||||
public BitField<bool, kAllowsHoistingCheckClassBit, 1> {}; // NOLINT
|
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_name(const String& value) const;
|
||||||
void set_kind(RawFunction::Kind 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, enable_type_checks, false, "Enable type checks.");
|
||||||
DEFINE_FLAG(bool, trace_parser, false, "Trace parser operations.");
|
DEFINE_FLAG(bool, trace_parser, false, "Trace parser operations.");
|
||||||
DEFINE_FLAG(bool, warn_mixin_typedef, true, "Warning on legacy mixin typedef.");
|
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, error_on_bad_type);
|
||||||
DECLARE_FLAG(bool, throw_on_javascript_int_overflow);
|
DECLARE_FLAG(bool, throw_on_javascript_int_overflow);
|
||||||
DECLARE_FLAG(bool, warn_on_javascript_compatibility);
|
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
|
// Parser is at the opening parenthesis of the formal parameter
|
||||||
// declaration of the function or constructor.
|
// declaration of the function or constructor.
|
||||||
// Parse the formal parameters and code.
|
// 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_;
|
intptr_t saved_try_index = last_used_try_index_;
|
||||||
last_used_try_index_ = 0;
|
last_used_try_index_ = 0;
|
||||||
|
|
||||||
|
intptr_t formal_params_pos = TokenPos();
|
||||||
// TODO(12455) : Need better validation mechanism.
|
// TODO(12455) : Need better validation mechanism.
|
||||||
|
|
||||||
if (func.IsConstructor()) {
|
if (func.IsConstructor()) {
|
||||||
|
@ -2946,12 +2959,23 @@ SequenceNode* Parser::ParseFunc(const Function& func,
|
||||||
&Symbols::TypeArgumentsParameter(),
|
&Symbols::TypeArgumentsParameter(),
|
||||||
&Type::ZoneHandle(I, Type::DynamicType()));
|
&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;
|
const bool allow_explicit_default_values = true;
|
||||||
if (func.IsGetterFunction()) {
|
if (func.IsGetterFunction()) {
|
||||||
// Populate function scope with the formal parameters. Since in this case
|
// Populate function scope with the formal parameters. Since in this case
|
||||||
// we are compiling a getter this will at most populate the receiver.
|
// we are compiling a getter this will at most populate the receiver.
|
||||||
AddFormalParamsToScope(¶ms, current_block_->scope);
|
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 {
|
} else {
|
||||||
ParseFormalParameterList(allow_explicit_default_values, false, ¶ms);
|
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.
|
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;
|
intptr_t end_token_pos = 0;
|
||||||
if (CurrentToken() == Token::kLBRACE) {
|
if (CurrentToken() == Token::kLBRACE) {
|
||||||
ConsumeToken();
|
ConsumeToken();
|
||||||
|
@ -3053,6 +3086,11 @@ SequenceNode* Parser::ParseFunc(const Function& func,
|
||||||
func.end_token_pos() == end_token_pos);
|
func.end_token_pos() == end_token_pos);
|
||||||
func.set_end_token_pos(end_token_pos);
|
func.set_end_token_pos(end_token_pos);
|
||||||
SequenceNode* body = CloseBlock();
|
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);
|
current_block_->statements->Add(body);
|
||||||
innermost_function_ = saved_innermost_function.raw();
|
innermost_function_ = saved_innermost_function.raw();
|
||||||
last_used_try_index_ = saved_try_index;
|
last_used_try_index_ = saved_try_index;
|
||||||
|
@ -3366,6 +3404,15 @@ void Parser::ParseMethodOrConstructor(ClassDesc* members, MemberDesc* method) {
|
||||||
method->name->ToCString());
|
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();
|
intptr_t method_end_pos = TokenPos();
|
||||||
if ((CurrentToken() == Token::kLBRACE) ||
|
if ((CurrentToken() == Token::kLBRACE) ||
|
||||||
(CurrentToken() == Token::kARROW)) {
|
(CurrentToken() == Token::kARROW)) {
|
||||||
|
@ -3480,6 +3527,7 @@ void Parser::ParseMethodOrConstructor(ClassDesc* members, MemberDesc* method) {
|
||||||
func.set_result_type(*method->type);
|
func.set_result_type(*method->type);
|
||||||
func.set_end_token_pos(method_end_pos);
|
func.set_end_token_pos(method_end_pos);
|
||||||
func.set_is_redirecting(is_redirecting);
|
func.set_is_redirecting(is_redirecting);
|
||||||
|
func.set_modifier(async_modifier);
|
||||||
if (method->has_native && library_.is_dart_scheme() &&
|
if (method->has_native && library_.is_dart_scheme() &&
|
||||||
library_.IsPrivate(*method->name)) {
|
library_.IsPrivate(*method->name)) {
|
||||||
func.set_is_visible(false);
|
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,
|
void Parser::ParseTopLevelFunction(TopLevel* top_level,
|
||||||
intptr_t metadata_pos) {
|
intptr_t metadata_pos) {
|
||||||
TRACE_PARSER("ParseTopLevelFunction");
|
TRACE_PARSER("ParseTopLevelFunction");
|
||||||
|
@ -4853,6 +4912,8 @@ void Parser::ParseTopLevelFunction(TopLevel* top_level,
|
||||||
const bool allow_explicit_default_values = true;
|
const bool allow_explicit_default_values = true;
|
||||||
ParseFormalParameterList(allow_explicit_default_values, false, ¶ms);
|
ParseFormalParameterList(allow_explicit_default_values, false, ¶ms);
|
||||||
|
|
||||||
|
RawFunction::AsyncModifier func_modifier = ParseFunctionModifier();
|
||||||
|
|
||||||
intptr_t function_end_pos = function_pos;
|
intptr_t function_end_pos = function_pos;
|
||||||
bool is_native = false;
|
bool is_native = false;
|
||||||
if (is_external) {
|
if (is_external) {
|
||||||
|
@ -4887,6 +4948,7 @@ void Parser::ParseTopLevelFunction(TopLevel* top_level,
|
||||||
decl_begin_pos));
|
decl_begin_pos));
|
||||||
func.set_result_type(result_type);
|
func.set_result_type(result_type);
|
||||||
func.set_end_token_pos(function_end_pos);
|
func.set_end_token_pos(function_end_pos);
|
||||||
|
func.set_modifier(func_modifier);
|
||||||
if (is_native && library_.is_dart_scheme() && library_.IsPrivate(func_name)) {
|
if (is_native && library_.is_dart_scheme() && library_.IsPrivate(func_name)) {
|
||||||
func.set_is_visible(false);
|
func.set_is_visible(false);
|
||||||
}
|
}
|
||||||
|
@ -4989,6 +5051,8 @@ void Parser::ParseTopLevelAccessor(TopLevel* top_level,
|
||||||
field_name->ToCString());
|
field_name->ToCString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RawFunction::AsyncModifier func_modifier = ParseFunctionModifier();
|
||||||
|
|
||||||
intptr_t accessor_end_pos = accessor_pos;
|
intptr_t accessor_end_pos = accessor_pos;
|
||||||
bool is_native = false;
|
bool is_native = false;
|
||||||
if (is_external) {
|
if (is_external) {
|
||||||
|
@ -5024,6 +5088,7 @@ void Parser::ParseTopLevelAccessor(TopLevel* top_level,
|
||||||
decl_begin_pos));
|
decl_begin_pos));
|
||||||
func.set_result_type(result_type);
|
func.set_result_type(result_type);
|
||||||
func.set_end_token_pos(accessor_end_pos);
|
func.set_end_token_pos(accessor_end_pos);
|
||||||
|
func.set_modifier(func_modifier);
|
||||||
if (is_native && library_.is_dart_scheme() &&
|
if (is_native && library_.is_dart_scheme() &&
|
||||||
library_.IsPrivate(accessor_name)) {
|
library_.IsPrivate(accessor_name)) {
|
||||||
func.set_is_visible(false);
|
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* Parser::CloseBlock() {
|
||||||
SequenceNode* statements = current_block_->statements;
|
SequenceNode* statements = current_block_->statements;
|
||||||
if (current_block_->scope != NULL) {
|
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.
|
// Set up default values for all optional parameters to the function.
|
||||||
void Parser::SetupDefaultsForOptionalParams(const ParamList* params,
|
void Parser::SetupDefaultsForOptionalParams(const ParamList* params,
|
||||||
Array* default_values) {
|
Array* default_values) {
|
||||||
|
@ -6228,7 +6467,9 @@ bool Parser::IsFunctionDeclaration() {
|
||||||
if ((CurrentToken() == Token::kLBRACE) ||
|
if ((CurrentToken() == Token::kLBRACE) ||
|
||||||
(CurrentToken() == Token::kARROW) ||
|
(CurrentToken() == Token::kARROW) ||
|
||||||
(is_top_level_ && IsLiteral("native")) ||
|
(is_top_level_ && IsLiteral("native")) ||
|
||||||
is_external) {
|
is_external ||
|
||||||
|
(FLAG_enable_async &&
|
||||||
|
CurrentLiteral()->raw() == Symbols::Async().raw())) {
|
||||||
SetPosition(saved_pos);
|
SetPosition(saved_pos);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -10767,6 +11008,7 @@ void Parser::SkipFunctionLiteral() {
|
||||||
params.skipped = true;
|
params.skipped = true;
|
||||||
ParseFormalParameterList(allow_explicit_default_values, false, ¶ms);
|
ParseFormalParameterList(allow_explicit_default_values, false, ¶ms);
|
||||||
}
|
}
|
||||||
|
ParseFunctionModifier();
|
||||||
if (CurrentToken() == Token::kLBRACE) {
|
if (CurrentToken() == Token::kLBRACE) {
|
||||||
SkipBlock();
|
SkipBlock();
|
||||||
ExpectToken(Token::kRBRACE);
|
ExpectToken(Token::kRBRACE);
|
||||||
|
@ -10792,7 +11034,8 @@ void Parser::SkipFunctionPreamble() {
|
||||||
// Case handles "native" keyword, but also return types of form
|
// Case handles "native" keyword, but also return types of form
|
||||||
// native.SomeType where native is the name of a library.
|
// native.SomeType where native is the name of a library.
|
||||||
if (token == Token::kIDENT && LookaheadToken(1) != Token::kPERIOD) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -363,6 +363,8 @@ class Parser : public ValueObject {
|
||||||
void ParseTopLevelAccessor(TopLevel* top_level, intptr_t metadata_pos);
|
void ParseTopLevelAccessor(TopLevel* top_level, intptr_t metadata_pos);
|
||||||
RawArray* EvaluateMetadata();
|
RawArray* EvaluateMetadata();
|
||||||
|
|
||||||
|
RawFunction::AsyncModifier ParseFunctionModifier();
|
||||||
|
|
||||||
// Support for parsing libraries.
|
// Support for parsing libraries.
|
||||||
RawObject* CallLibraryTagHandler(Dart_LibraryTag tag,
|
RawObject* CallLibraryTagHandler(Dart_LibraryTag tag,
|
||||||
intptr_t token_pos,
|
intptr_t token_pos,
|
||||||
|
@ -463,6 +465,7 @@ class Parser : public ValueObject {
|
||||||
Array* default_parameter_values);
|
Array* default_parameter_values);
|
||||||
SequenceNode* ParseFunc(const Function& func,
|
SequenceNode* ParseFunc(const Function& func,
|
||||||
Array* default_parameter_values);
|
Array* default_parameter_values);
|
||||||
|
RawClass* GetClassForAsync(const String& class_name);
|
||||||
|
|
||||||
void ParseNativeFunctionBlock(const ParamList* params, const Function& func);
|
void ParseNativeFunctionBlock(const ParamList* params, const Function& func);
|
||||||
|
|
||||||
|
@ -483,7 +486,12 @@ class Parser : public ValueObject {
|
||||||
void OpenBlock();
|
void OpenBlock();
|
||||||
void OpenLoopBlock();
|
void OpenLoopBlock();
|
||||||
void OpenFunctionBlock(const Function& func);
|
void OpenFunctionBlock(const Function& func);
|
||||||
|
RawFunction* OpenAsyncFunction(intptr_t formal_param_pos);
|
||||||
SequenceNode* CloseBlock();
|
SequenceNode* CloseBlock();
|
||||||
|
SequenceNode* CloseAsyncFunction(const Function& closure,
|
||||||
|
SequenceNode* closure_node);
|
||||||
|
void CloseAsyncClosure(SequenceNode* body);
|
||||||
|
|
||||||
|
|
||||||
LocalVariable* LookupPhaseParameter();
|
LocalVariable* LookupPhaseParameter();
|
||||||
LocalVariable* LookupReceiver(LocalScope* from_scope, bool test_only);
|
LocalVariable* LookupReceiver(LocalScope* from_scope, bool test_only);
|
||||||
|
|
|
@ -615,6 +615,11 @@ class RawFunction : public RawObject {
|
||||||
kInvokeFieldDispatcher, // invokes a field as a closure.
|
kInvokeFieldDispatcher, // invokes a field as a closure.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum AsyncModifier {
|
||||||
|
kNoModifier,
|
||||||
|
kAsync,
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// So that the MarkingVisitor::DetachCode can null out the code fields.
|
// So that the MarkingVisitor::DetachCode can null out the code fields.
|
||||||
friend class MarkingVisitor;
|
friend class MarkingVisitor;
|
||||||
|
@ -650,7 +655,7 @@ class RawFunction : public RawObject {
|
||||||
int16_t num_fixed_parameters_;
|
int16_t num_fixed_parameters_;
|
||||||
int16_t num_optional_parameters_; // > 0: positional; < 0: named.
|
int16_t num_optional_parameters_; // > 0: positional; < 0: named.
|
||||||
int16_t deoptimization_counter_;
|
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_instruction_count_;
|
||||||
uint16_t optimized_call_site_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_fixed_parameters(reader->Read<int16_t>());
|
||||||
func.set_num_optional_parameters(reader->Read<int16_t>());
|
func.set_num_optional_parameters(reader->Read<int16_t>());
|
||||||
func.set_deoptimization_counter(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_instruction_count(reader->Read<uint16_t>());
|
||||||
func.set_optimized_call_site_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_fixed_parameters_);
|
||||||
writer->Write<int16_t>(ptr()->num_optional_parameters_);
|
writer->Write<int16_t>(ptr()->num_optional_parameters_);
|
||||||
writer->Write<int16_t>(ptr()->deoptimization_counter_);
|
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_instruction_count_);
|
||||||
writer->Write<uint16_t>(ptr()->optimized_call_site_count_);
|
writer->Write<uint16_t>(ptr()->optimized_call_site_count_);
|
||||||
|
|
||||||
|
|
|
@ -68,6 +68,15 @@ class ObjectPointerVisitor;
|
||||||
V(Library, "library") \
|
V(Library, "library") \
|
||||||
V(LoadLibrary, "loadLibrary") \
|
V(LoadLibrary, "loadLibrary") \
|
||||||
V(_LibraryPrefix, "_LibraryPrefix") \
|
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(Native, "native") \
|
||||||
V(Import, "import") \
|
V(Import, "import") \
|
||||||
V(Source, "source") \
|
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/constructor1: Fail, Ok
|
||||||
deferred_constraints_constants_old_syntax_test/constructor2: 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]
|
[ $compiler == dart2dart]
|
||||||
deferred_load_library_wrong_args_test/none: Fail # Issue 17523
|
deferred_load_library_wrong_args_test/none: Fail # Issue 17523
|
||||||
|
|
Loading…
Reference in a new issue