Fix async machinery (issue 22445 and possibly others to be triaged later).

R=hausner@google.com, iposva@google.com

Review URL: https://codereview.chromium.org//958243003

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@44107 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
regis@google.com 2015-02-27 22:19:47 +00:00
parent f54675b398
commit 0036014d8a
10 changed files with 341 additions and 187 deletions

View file

@ -159,19 +159,37 @@ class AstNode : public ZoneAllocated {
class AwaitNode : public AstNode {
public:
AwaitNode(intptr_t token_pos, AstNode* expr)
: AstNode(token_pos), expr_(expr) { }
AwaitNode(intptr_t token_pos,
AstNode* expr,
LocalScope* try_scope,
int16_t try_index,
LocalScope* outer_try_scope,
intptr_t outer_try_index)
: AstNode(token_pos),
expr_(expr),
try_scope_(try_scope),
try_index_(try_index),
outer_try_scope_(outer_try_scope),
outer_try_index_(outer_try_index) { }
void VisitChildren(AstNodeVisitor* visitor) const {
expr_->Visit(visitor);
}
AstNode* expr() const { return expr_; }
LocalScope* try_scope() const { return try_scope_; }
int16_t try_index() const { return try_index_; }
LocalScope* outer_try_scope() const { return outer_try_scope_; }
int16_t outer_try_index() const { return outer_try_index_; }
DECLARE_COMMON_NODE_FUNCTIONS(AwaitNode);
private:
AstNode* expr_;
LocalScope* try_scope_;
int16_t try_index_;
LocalScope* outer_try_scope_;
int16_t outer_try_index_;
DISALLOW_COPY_AND_ASSIGN(AwaitNode);
};

View file

@ -45,11 +45,9 @@ FOR_EACH_UNREACHABLE_NODE(DEFINE_UNREACHABLE)
#undef DEFINE_UNREACHABLE
AwaitTransformer::AwaitTransformer(SequenceNode* preamble,
const ParsedFunction& parsed_function,
LocalScope* function_top)
: preamble_(preamble),
temp_cnt_(0),
parsed_function_(parsed_function),
function_top_(function_top),
thread_(Thread::Current()) {
ASSERT(function_top_ != NULL);
@ -109,6 +107,30 @@ void AwaitTransformer::VisitTypeNode(TypeNode* node) {
}
// Restore the currently relevant :saved_try_context_var on the stack
// from the captured :async_saved_try_ctx_var_<try_index>.
AstNode* AwaitTransformer::RestoreSavedTryContext(Zone* zone,
LocalScope* scope,
int16_t try_index) {
LocalVariable* saved_try_ctx =
scope->LookupVariable(Symbols::SavedTryContextVar(), false);
ASSERT((saved_try_ctx != NULL) && !saved_try_ctx->is_captured());
const String& async_saved_try_ctx_name = String::ZoneHandle(zone,
Symbols::New(String::Handle(zone,
String::NewFormatted("%s%d",
Symbols::AsyncSavedTryCtxVarPrefix().ToCString(),
try_index))));
LocalVariable* async_saved_try_ctx =
scope->LookupVariable(async_saved_try_ctx_name, false);
ASSERT(async_saved_try_ctx != NULL);
ASSERT(async_saved_try_ctx->is_captured());
return new (zone) StoreLocalNode(
Scanner::kNoSourcePos,
saved_try_ctx,
new (zone) LoadLocalNode(Scanner::kNoSourcePos, async_saved_try_ctx));
}
void AwaitTransformer::VisitAwaitNode(AwaitNode* node) {
// Await transformation:
//
@ -212,16 +234,19 @@ void AwaitTransformer::VisitAwaitNode(AwaitNode* node) {
preamble_->Add(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.
const String& async_saved_try_ctx_name =
String::Handle(Z, parsed_function_.async_saved_try_ctx_name());
if (!async_saved_try_ctx_name.IsNull()) {
LocalVariable* async_saved_try_ctx =
GetVariableInScope(preamble_->scope(), async_saved_try_ctx_name);
preamble_->Add(new (Z) StoreLocalNode(
Scanner::kNoSourcePos,
parsed_function_.saved_try_ctx(),
new (Z) LoadLocalNode(Scanner::kNoSourcePos, async_saved_try_ctx)));
// restoring the saved try context that lives on the stack and possibly the
// saved try context of the outer try block.
if (node->try_scope() != NULL) {
preamble_->Add(RestoreSavedTryContext(Z,
node->try_scope(),
node->try_index()));
if (node->outer_try_scope() != NULL) {
preamble_->Add(RestoreSavedTryContext(Z,
node->outer_try_scope(),
node->outer_try_index()));
}
} else {
ASSERT(node->outer_try_scope() == NULL);
}
LoadLocalNode* load_error_param = new (Z) LoadLocalNode(

View file

@ -10,7 +10,6 @@
namespace dart {
class ParsedFunction;
class Thread;
// Translate an AstNode containing an expression (that itself contains one or
@ -40,9 +39,7 @@ class Thread;
//
class AwaitTransformer : public AstNodeVisitor {
public:
AwaitTransformer(SequenceNode* preamble,
const ParsedFunction& parsed_function,
LocalScope* function_top);
AwaitTransformer(SequenceNode* preamble, LocalScope* function_top);
#define DECLARE_VISIT(BaseName) \
virtual void Visit##BaseName##Node(BaseName##Node* node);
@ -52,6 +49,10 @@ class AwaitTransformer : public AstNodeVisitor {
AstNode* Transform(AstNode* expr);
static AstNode* RestoreSavedTryContext(Zone* zone,
LocalScope* scope,
int16_t try_index);
private:
LocalVariable* EnsureCurrentTempVar();
LocalVariable* AddToPreambleNewTempVar(AstNode* node);
@ -69,7 +70,6 @@ class AwaitTransformer : public AstNodeVisitor {
SequenceNode* preamble_;
int32_t temp_cnt_;
AstNode* result_;
const ParsedFunction& parsed_function_;
LocalScope* function_top_;
Thread* thread_;

View file

@ -4092,6 +4092,7 @@ void EffectGraphVisitor::VisitTryCatchNode(TryCatchNode* node) {
finally_block->Visit(&for_finally);
if (for_finally.is_open()) {
// Rethrow the exception. Manually build the graph for rethrow.
// TODO(regis): This is not correct. Issue 22595.
Value* exception = for_finally.Bind(
for_finally.BuildLoadLocal(catch_block->exception_var()));
for_finally.PushArgument(exception);

View file

@ -3036,13 +3036,8 @@ SequenceNode* Parser::ParseFunc(const Function& func,
intptr_t saved_try_index = last_used_try_index_;
last_used_try_index_ = 0;
// In case of nested async functions we also need to save the currently saved
// try context, the corresponding stack variable, and the scope where
// In case of nested async functions we also need to save the scope where
// temporaries are added.
LocalVariable* saved_saved_try_ctx = parsed_function()->saved_try_ctx();
const String& saved_async_saved_try_ctx_name =
String::Handle(Z, parsed_function()->async_saved_try_ctx_name());
parsed_function()->reset_saved_try_ctx_vars();
LocalScope* saved_async_temp_scope = async_temp_scope_;
if (func.IsGenerativeConstructor()) {
@ -3265,9 +3260,6 @@ SequenceNode* Parser::ParseFunc(const Function& func,
innermost_function_ = saved_innermost_function.raw();
last_used_try_index_ = saved_try_index;
async_temp_scope_ = saved_async_temp_scope;
parsed_function()->set_saved_try_ctx(saved_saved_try_ctx);
parsed_function()->set_async_saved_try_ctx_name(
saved_async_saved_try_ctx_name);
return CloseBlock();
}
@ -5999,8 +5991,7 @@ SequenceNode* Parser::CloseAsyncTryBlock(SequenceNode* try_block) {
new(Z) LoadLocalNode(Scanner::kNoSourcePos, stack_trace_var)));
}
AddSavedExceptionAndStacktraceToScope(
exception_var, stack_trace_var, current_block_->scope);
SaveExceptionAndStacktrace(exception_var, stack_trace_var);
ASSERT(try_blocks_list_ != NULL);
ASSERT(innermost_function().IsAsyncClosure() ||
@ -6011,12 +6002,11 @@ SequenceNode* Parser::CloseAsyncTryBlock(SequenceNode* try_block) {
current_block_->scope->function_level())) {
// We need to unchain three scope levels: catch clause, catch
// parameters, and the general try block.
RestoreSavedTryContext(
current_block_->scope->parent()->parent()->parent(),
try_blocks_list_->outer_try_block()->try_index(),
current_block_->statements);
} else {
parsed_function()->reset_saved_try_ctx_vars();
current_block_->statements->Add(
AwaitTransformer::RestoreSavedTryContext(
Z,
current_block_->scope->parent()->parent()->parent(),
try_blocks_list_->outer_try_block()->try_index()));
}
// Complete the async future with an error.
@ -6103,16 +6093,13 @@ void Parser::OpenAsyncTryBlock() {
current_block_->scope->AddVariable(stack_trace_var);
}
SetupSavedExceptionAndStacktrace();
// Open the try block.
OpenBlock();
PushTryBlock(current_block_);
if (innermost_function().IsAsyncClosure() ||
innermost_function().IsAsyncFunction() ||
innermost_function().IsSyncGenClosure() ||
innermost_function().IsSyncGenerator()) {
SetupSavedTryContext(context_var);
}
SetupSavedTryContext(context_var);
}
@ -6385,16 +6372,6 @@ SequenceNode* Parser::CloseBlock() {
}
static inline String& BuildAsyncSavedTryContextName(Zone* zone,
int16_t id) {
const char* async_saved_prefix = ":async_saved_try_ctx_var_";
// Can be a regular handle since we only use it to build an actual symbol.
const String& cnt_str = String::Handle(zone,
String::NewFormatted("%s%d", async_saved_prefix, id));
return String::ZoneHandle(zone, Symbols::New(cnt_str));
}
SequenceNode* Parser::CloseAsyncFunction(const Function& closure,
SequenceNode* closure_body) {
TRACE_PARSER("CloseAsyncFunction");
@ -7777,6 +7754,61 @@ AstNode* Parser::ParseDoWhileStatement(String* label_name) {
}
// If the await or yield being parsed is in a try block, the continuation code
// needs to restore the corresponding stack-based variable :saved_try_ctx_var,
// and possibly the stack-based variable :saved_try_ctx_var of the outer try
// block.
// The inner :saved_try_ctx_var is used by a finally clause handling an
// exception thrown by the continuation code in a catch clause. If no finally
// clause exists, the catch or finally clause of the outer try block, if any,
// uses the outer :saved_try_ctx_var to handle the exception.
//
// * Try blocks: Set the context variable for this try block.
// * Catch blocks: Set the context variable for this try block and for any outer
// try block (if existent).
// * Finally blocks: Set the context variable for any outer try block (if
// existent). Note that this try block is popped before
// parsing the finally clause, so the outer try block (if
// existent) is at the top of the try block list.
//
// TODO(regis): Could we return the variables instead of their containing
// scopes? Check if they are already setup at this point.
void Parser::CheckAsyncOpInTryBlock(LocalScope** try_scope,
int16_t* try_index,
LocalScope** outer_try_scope,
int16_t* outer_try_index) const {
*try_scope = NULL;
*try_index = CatchClauseNode::kInvalidTryIndex;
*outer_try_scope = NULL;
*outer_try_index = CatchClauseNode::kInvalidTryIndex;
if (try_blocks_list_ != NULL) {
LocalScope* scope = try_blocks_list_->try_block()->scope;
const int current_function_level = current_block_->scope->function_level();
if (scope->function_level() == current_function_level) {
// The block declaring :saved_try_ctx_var variable is the parent of the
// pushed try block.
*try_scope = scope->parent();
*try_index = try_blocks_list_->try_index();
if (try_blocks_list_->inside_catch() &&
(try_blocks_list_->outer_try_block() != NULL)) {
scope = try_blocks_list_->outer_try_block()->try_block()->scope;
if (scope->function_level() == current_function_level) {
*outer_try_scope = scope->parent();
*outer_try_index = try_blocks_list_->outer_try_block()->try_index();
}
}
}
}
// An async or async* has an implicitly created try-catch around the
// function body, so the await or yield inside the async closure should always
// be created with a try scope.
ASSERT((*try_scope != NULL) ||
innermost_function().IsAsyncFunction() ||
innermost_function().IsSyncGenClosure() ||
innermost_function().IsSyncGenerator());
}
AstNode* Parser::ParseAwaitForStatement(String* label_name) {
TRACE_PARSER("ParseAwaitForStatement");
ASSERT(IsAwaitKeyword());
@ -7844,6 +7876,13 @@ AstNode* Parser::ParseAwaitForStatement(String* label_name) {
new(Z) StoreLocalNode(stream_pos, iterator_var, ctor_call);
current_block_->statements->Add(iterator_init);
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);
// Build while loop condition.
// while (await :for-in-iter.moveNext())
ArgumentListNode* no_args = new(Z) ArgumentListNode(stream_pos);
@ -7852,11 +7891,14 @@ AstNode* Parser::ParseAwaitForStatement(String* label_name) {
new(Z) LoadLocalNode(stream_pos, iterator_var),
Symbols::MoveNext(),
no_args);
AstNode* await_moveNext = new (Z) AwaitNode(stream_pos, iterator_moveNext);
AstNode* await_moveNext = new (Z) AwaitNode(stream_pos,
iterator_moveNext,
try_scope,
try_index,
outer_try_scope,
outer_try_index);
OpenBlock();
AwaitTransformer at(current_block_->statements,
*parsed_function(),
async_temp_scope_);
AwaitTransformer at(current_block_->statements, async_temp_scope_);
AstNode* transformed_await = at.Transform(await_moveNext);
SequenceNode* await_preamble = CloseBlock();
@ -8218,34 +8260,48 @@ void Parser::AddCatchParamsToScope(CatchParamDesc* exception_param,
}
// Populate local scope of the catch block with the saved exception and saved
// Populate current scope of the try block with the saved exception and saved
// stack trace.
void Parser::AddSavedExceptionAndStacktraceToScope(
LocalVariable* exception_var,
LocalVariable* stack_trace_var,
LocalScope* scope) {
void Parser::SetupSavedExceptionAndStacktrace() {
ASSERT(innermost_function().IsAsyncClosure() ||
innermost_function().IsAsyncFunction() ||
innermost_function().IsSyncGenClosure() ||
innermost_function().IsSyncGenerator());
// Add :saved_exception_var and :saved_stack_trace_var to scope.
// Add :saved_exception_var and :saved_stack_trace_var to current scope.
// They will automatically get captured.
LocalVariable* saved_exception_var = new (Z) LocalVariable(
Scanner::kNoSourcePos,
Symbols::SavedExceptionVar(),
Type::ZoneHandle(Z, Type::DynamicType()));
saved_exception_var->set_is_final();
scope->AddVariable(saved_exception_var);
LocalVariable* saved_stack_trace_var = new (Z) LocalVariable(
// Parallel try statements share the same set of variables.
LocalVariable* saved_exception_var =
current_block_->scope->LocalLookupVariable(Symbols::SavedExceptionVar());
if (saved_exception_var == NULL) {
saved_exception_var = new (Z) LocalVariable(
Scanner::kNoSourcePos,
Symbols::SavedExceptionVar(),
Type::ZoneHandle(Z, Type::DynamicType()));
saved_exception_var->set_is_final();
current_block_->scope->AddVariable(saved_exception_var);
}
LocalVariable* saved_stack_trace_var =
current_block_->scope->LocalLookupVariable(Symbols::SavedStackTraceVar());
if (saved_stack_trace_var == NULL) {
saved_stack_trace_var = new (Z) LocalVariable(
Scanner::kNoSourcePos,
Symbols::SavedStackTraceVar(),
Type::ZoneHandle(Z, Type::DynamicType()));
saved_exception_var->set_is_final();
scope->AddVariable(saved_stack_trace_var);
saved_exception_var->set_is_final();
current_block_->scope->AddVariable(saved_stack_trace_var);
}
}
// Generate code to load the exception object (:exception_var) into
// the saved exception variable (:saved_exception_var) used to rethrow.
saved_exception_var = current_block_->scope->LookupVariable(
// Generate code to load the exception object (:exception_var) into
// the saved exception variable (:saved_exception_var) used to rethrow.
void Parser::SaveExceptionAndStacktrace(LocalVariable* exception_var,
LocalVariable* stack_trace_var) {
ASSERT(innermost_function().IsAsyncClosure() ||
innermost_function().IsAsyncFunction() ||
innermost_function().IsSyncGenClosure() ||
innermost_function().IsSyncGenerator());
LocalVariable* saved_exception_var = current_block_->scope->LookupVariable(
Symbols::SavedExceptionVar(), false);
ASSERT(saved_exception_var != NULL);
ASSERT(exception_var != NULL);
@ -8256,7 +8312,7 @@ void Parser::AddSavedExceptionAndStacktraceToScope(
// Generate code to load the stack trace object (:stack_trace_var) into
// the saved stacktrace variable (:saved_stack_trace_var) used to rethrow.
saved_stack_trace_var = current_block_->scope->LookupVariable(
LocalVariable* saved_stack_trace_var = current_block_->scope->LookupVariable(
Symbols::SavedStackTraceVar(), false);
ASSERT(saved_stack_trace_var != NULL);
ASSERT(stack_trace_var != NULL);
@ -8281,11 +8337,11 @@ SequenceNode* Parser::ParseFinallyBlock() {
innermost_function().IsSyncGenerator()) &&
(try_blocks_list_ != NULL)) {
// We need two unchain two scopes: finally clause, and the try block level.
RestoreSavedTryContext(current_block_->scope->parent()->parent(),
try_blocks_list_->try_index(),
current_block_->statements);
} else {
parsed_function()->reset_saved_try_ctx_vars();
current_block_->statements->Add(
AwaitTransformer::RestoreSavedTryContext(
Z,
current_block_->scope->parent()->parent(),
try_blocks_list_->try_index()));
}
ParseStatementSequence();
@ -8443,15 +8499,13 @@ SequenceNode* Parser::ParseCatchClauses(
current_block_->scope->function_level())) {
// We need to unchain three scope levels: catch clause, catch
// parameters, and the general try block.
RestoreSavedTryContext(
current_block_->scope->parent()->parent()->parent(),
try_blocks_list_->outer_try_block()->try_index(),
current_block_->statements);
} else {
parsed_function()->reset_saved_try_ctx_vars();
current_block_->statements->Add(
AwaitTransformer::RestoreSavedTryContext(
Z,
current_block_->scope->parent()->parent()->parent(),
try_blocks_list_->outer_try_block()->try_index()));
}
AddSavedExceptionAndStacktraceToScope(
exception_var, stack_trace_var, current_block_->scope);
SaveExceptionAndStacktrace(exception_var, stack_trace_var);
}
current_block_->statements->Add(ParseNestedStatement(false, NULL));
@ -8519,6 +8573,9 @@ SequenceNode* Parser::ParseCatchClauses(
// There isn't a generic catch clause so create a clause body that
// rethrows the exception. This includes the case that there were no
// catch clauses.
// An await cannot possibly be executed inbetween the catch entry and here,
// therefore, it is safe to rethrow the stack-based :exception_var instead
// of the captured copy :saved_exception_var.
current = new(Z) SequenceNode(handler_pos, NULL);
current->Add(new(Z) ThrowNode(
handler_pos,
@ -8542,6 +8599,17 @@ SequenceNode* Parser::ParseCatchClauses(
AstNode* type_test = type_tests.RemoveLast();
SequenceNode* catch_block = catch_blocks.RemoveLast();
// TODO(regis): Understand the purpose of the following code restoring
// :saved_try_context_var. This code was added as part of r39926.
// In some cases, this code even crashed the compiler (debug mode assert),
// because the scope unchaining was starting from the wrong block.
// The catch clause(s) emitted below contain the same restoring code.
// So why is it necessary? Could it be an attempt to handle the case where
// the catch clause is replaced by a throw because of a bad type? It is not
// necessary in this case either, because no await could have been executed
// between the setup of :saved_try_context_var in the try clause and here
// (it is the execution of an await that clears all stack-based variables).
// In case of async closures we need to restore the saved try index of an
// outer try block (if it exists).
ASSERT(try_blocks_list_ != NULL);
@ -8553,14 +8621,14 @@ SequenceNode* Parser::ParseCatchClauses(
(try_blocks_list_->outer_try_block()->try_block()
->scope->function_level() ==
current_block_->scope->function_level())) {
// We need to unchain three scope levels: catch clause, catch
// We need to unchain three scope levels (from the catch block and not
// from the current block): catch clause, catch
// parameters, and the general try block.
RestoreSavedTryContext(
current_block_->scope->parent()->parent(),
try_blocks_list_->outer_try_block()->try_index(),
current_block_->statements);
} else {
parsed_function()->reset_saved_try_ctx_vars();
current_block_->statements->Add(
AwaitTransformer::RestoreSavedTryContext(
Z,
catch_block->scope()->parent()->parent()->parent(),
try_blocks_list_->outer_try_block()->try_index()));
}
}
@ -8573,8 +8641,11 @@ SequenceNode* Parser::ParseCatchClauses(
void Parser::SetupSavedTryContext(LocalVariable* saved_try_context) {
const String& async_saved_try_ctx_name =
BuildAsyncSavedTryContextName(Z, last_used_try_index_ - 1);
const String& async_saved_try_ctx_name = String::ZoneHandle(Z,
Symbols::New(String::Handle(Z,
String::NewFormatted("%s%d",
Symbols::AsyncSavedTryCtxVarPrefix().ToCString(),
last_used_try_index_ - 1))));
LocalVariable* async_saved_try_ctx = new (Z) LocalVariable(
Scanner::kNoSourcePos,
async_saved_try_ctx_name,
@ -8590,38 +8661,6 @@ void Parser::SetupSavedTryContext(LocalVariable* saved_try_context) {
Scanner::kNoSourcePos,
async_saved_try_ctx,
new(Z) LoadLocalNode(Scanner::kNoSourcePos, saved_try_context)));
parsed_function()->set_saved_try_ctx(saved_try_context);
parsed_function()->set_async_saved_try_ctx_name(async_saved_try_ctx_name);
}
// Restore the currently relevant :saved_try_context_var on the stack
// from the captured :async_saved_try_ctx_var_.
// * Try blocks: Set the context variable for this try block.
// * Catch/finally blocks: Set the context variable for any outer try block (if
// existent).
//
// Also save the captured variable and the stack variable to be able to set
// it after a function continues execution (await).
void Parser::RestoreSavedTryContext(LocalScope* saved_try_context_scope,
int16_t try_index,
SequenceNode* target) {
LocalVariable* saved_try_ctx = saved_try_context_scope->LookupVariable(
Symbols::SavedTryContextVar(), false);
ASSERT((saved_try_ctx != NULL) && !saved_try_ctx->is_captured());
const String& async_saved_try_ctx_name =
BuildAsyncSavedTryContextName(Z, try_index);
LocalVariable* async_saved_try_ctx =
target->scope()->LookupVariable(async_saved_try_ctx_name, false);
ASSERT(async_saved_try_ctx != NULL);
ASSERT(async_saved_try_ctx->is_captured());
target->Add(new (Z) StoreLocalNode(
Scanner::kNoSourcePos,
saved_try_ctx,
new (Z) LoadLocalNode(Scanner::kNoSourcePos, async_saved_try_ctx)));
parsed_function()->set_saved_try_ctx(saved_try_ctx);
parsed_function()->set_async_saved_try_ctx_name(async_saved_try_ctx_name);
}
@ -8641,6 +8680,7 @@ AstNode* Parser::ParseTryStatement(String* label_name) {
// :exception_var and :stack_trace_var get set with the exception object
// and the stack trace object when an exception is thrown. These three
// implicit variables can never be captured.
// Parallel try statements share the same set of variables.
LocalVariable* context_var =
current_block_->scope->LocalLookupVariable(Symbols::SavedTryContextVar());
if (context_var == NULL) {
@ -8669,6 +8709,13 @@ AstNode* Parser::ParseTryStatement(String* label_name) {
current_block_->scope->AddVariable(stack_trace_var);
}
if (innermost_function().IsAsyncClosure() ||
innermost_function().IsAsyncFunction() ||
innermost_function().IsSyncGenClosure() ||
innermost_function().IsSyncGenerator()) {
SetupSavedExceptionAndStacktrace();
}
const intptr_t try_pos = TokenPos();
ConsumeToken(); // Consume the 'try'.
@ -8931,18 +8978,27 @@ AstNode* Parser::ParseStatement() {
yield->AddNode(return_true);
// If this expression is part of a try block, also append the code for
// restoring the saved try context that lives on the stack.
const String& async_saved_try_ctx_name =
String::Handle(Z, parsed_function()->async_saved_try_ctx_name());
if (!async_saved_try_ctx_name.IsNull()) {
LocalVariable* async_saved_try_ctx =
current_block_->scope->LookupVariable(async_saved_try_ctx_name,
false);
ASSERT(async_saved_try_ctx != NULL);
yield->AddNode(new (Z) StoreLocalNode(
Scanner::kNoSourcePos,
parsed_function()->saved_try_ctx(),
new (Z) LoadLocalNode(Scanner::kNoSourcePos, async_saved_try_ctx)));
// 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;
@ -8993,22 +9049,19 @@ AstNode* Parser::ParseStatement() {
// If in async code, use :saved_exception_var and :saved_stack_trace_var
// instead of :exception_var and :stack_trace_var.
// These variables are bound in the block containing the try.
// Look in the try scope directly.
LocalScope* scope = try_blocks_list_->try_block()->scope->parent();
ASSERT(scope != NULL);
LocalVariable* excp_var;
LocalVariable* trace_var;
if (innermost_function().IsAsyncClosure() ||
innermost_function().IsAsyncFunction() ||
innermost_function().IsSyncGenClosure() ||
innermost_function().IsSyncGenerator()) {
// The saved exception and stack trace variables are bound in the block
// containing the catch. So start looking in the current scope.
LocalScope* scope = current_block_->scope;
excp_var = scope->LookupVariable(Symbols::SavedExceptionVar(), false);
trace_var = scope->LookupVariable(Symbols::SavedStackTraceVar(), false);
excp_var = scope->LocalLookupVariable(Symbols::SavedExceptionVar());
trace_var = scope->LocalLookupVariable(Symbols::SavedStackTraceVar());
} else {
// The exception and stack trace variables are bound in the block
// containing the try. Look in the try scope directly.
LocalScope* scope = try_blocks_list_->try_block()->scope->parent();
ASSERT(scope != NULL);
excp_var = scope->LocalLookupVariable(Symbols::ExceptionVar());
trace_var = scope->LocalLookupVariable(Symbols::StackTraceVar());
}
@ -9698,9 +9751,7 @@ AstNode* Parser::ParseAwaitableExpr(bool require_compiletime_const,
// See FlowGraphBuilder::VisitSequenceNode() for details on when contexts
// are created.
OpenBlock();
AwaitTransformer at(current_block_->statements,
*parsed_function(),
async_temp_scope_);
AwaitTransformer at(current_block_->statements, async_temp_scope_);
AstNode* result = at.Transform(expr);
SequenceNode* preamble = CloseBlock();
if (await_preamble == NULL) {
@ -9816,7 +9867,20 @@ AstNode* Parser::ParseUnaryExpr() {
}
ConsumeToken();
parsed_function()->record_await();
expr = new (Z) AwaitNode(op_pos, ParseUnaryExpr());
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);
expr = new (Z) AwaitNode(op_pos,
ParseUnaryExpr(),
try_scope,
try_index,
outer_try_scope,
outer_try_index);
} else if (IsPrefixOperator(CurrentToken())) {
Token::Kind unary_op = CurrentToken();
if (unary_op == Token::kSUB) {
@ -11089,7 +11153,7 @@ RawAbstractType* Parser::ParseType(
CheckToken(Token::kIDENT, "type name expected");
intptr_t ident_pos = TokenPos();
LibraryPrefix& prefix = LibraryPrefix::Handle(Z);
String& type_name = String::Handle(Z);;
String& type_name = String::Handle(Z);
if (finalization == ClassFinalizer::kIgnore) {
if (!is_top_level_ && (current_block_ != NULL)) {

View file

@ -57,9 +57,7 @@ class ParsedFunction : public ZoneAllocated {
first_stack_local_index_(0),
num_copied_params_(0),
num_stack_locals_(0),
have_seen_await_expr_(false),
saved_try_ctx_(NULL),
async_saved_try_ctx_name_(String::ZoneHandle(zone(), String::null())) {
have_seen_await_expr_(false) {
ASSERT(function.IsZoneHandle());
// Every function has a local variable for the current context.
LocalVariable* temp = new(zone()) LocalVariable(
@ -152,24 +150,6 @@ class ParsedFunction : public ZoneAllocated {
void record_await() { have_seen_await_expr_ = true; }
bool have_seen_await() const { return have_seen_await_expr_; }
void set_saved_try_ctx(LocalVariable* saved_try_ctx) {
ASSERT((saved_try_ctx == NULL) || !saved_try_ctx->is_captured());
saved_try_ctx_ = saved_try_ctx;
}
LocalVariable* saved_try_ctx() const { return saved_try_ctx_; }
void set_async_saved_try_ctx_name(const String& async_saved_try_ctx_name) {
async_saved_try_ctx_name_ = async_saved_try_ctx_name.raw();
}
RawString* async_saved_try_ctx_name() const {
return async_saved_try_ctx_name_.raw();
}
void reset_saved_try_ctx_vars() {
saved_try_ctx_ = NULL;
async_saved_try_ctx_name_ = String::null();
}
Thread* thread() const { return thread_; }
Isolate* isolate() const { return thread()->isolate(); }
Zone* zone() const { return thread()->zone(); }
@ -193,8 +173,6 @@ class ParsedFunction : public ZoneAllocated {
int num_copied_params_;
int num_stack_locals_;
bool have_seen_await_expr_;
LocalVariable* saved_try_ctx_;
String& async_saved_try_ctx_name_;
friend class Parser;
DISALLOW_COPY_AND_ASSIGN(ParsedFunction);
@ -602,9 +580,9 @@ class Parser : public ValueObject {
void AddCatchParamsToScope(CatchParamDesc* exception_param,
CatchParamDesc* stack_trace_param,
LocalScope* scope);
void AddSavedExceptionAndStacktraceToScope(LocalVariable* exception_var,
LocalVariable* stack_trace_var,
LocalScope* scope);
void SetupSavedExceptionAndStacktrace();
void SaveExceptionAndStacktrace(LocalVariable* exception_var,
LocalVariable* stack_trace_var);
// Parse all the catch clause of a try.
SequenceNode* ParseCatchClauses(intptr_t handler_pos,
LocalVariable* exception_var,
@ -617,6 +595,11 @@ class Parser : public ValueObject {
void PushTryBlock(Block* try_block);
// Pops the inner most try block from the list.
TryBlocks* PopTryBlock();
// Collect try block scopes and indices if await or yield is in try block.
void CheckAsyncOpInTryBlock(LocalScope** try_scope,
int16_t* try_index,
LocalScope** outer_try_scope,
int16_t* outer_try_index) const;
// Add specified node to try block list so that it can be patched with
// inlined finally code if needed.
void AddNodeForFinallyInlining(AstNode* node);
@ -752,9 +735,6 @@ class Parser : public ValueObject {
const Function* func);
void SetupSavedTryContext(LocalVariable* saved_try_context);
void RestoreSavedTryContext(LocalScope* saved_try_context_scope,
int16_t try_index,
SequenceNode* target);
void CheckOperatorArity(const MemberDesc& member);

View file

@ -94,6 +94,7 @@ class ObjectPointerVisitor;
V(AsyncOperationParam, ":async_result") \
V(AsyncOperationErrorParam, ":async_error_param") \
V(AsyncOperationStackTraceParam, ":async_stack_trace_param") \
V(AsyncSavedTryCtxVarPrefix, ":async_saved_try_ctx_var_") \
V(AsyncCatchHelper, "_asyncCatchHelper") \
V(Await, "await") \
V(AwaitContextVar, ":await_ctx_var") \

View file

@ -0,0 +1,29 @@
// Copyright (c) 2015, 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.
import "dart:async";
import "package:expect/expect.dart";
foo() async {
try {
await 1;
throw "error";
} on String catch (e) {
await 2;
throw e;
} finally {
await 3;
}
}
main() async {
var error = "no error";
try {
await foo();
} catch (e) {
error = e;
}
Expect.equals("error", error);
}

View file

@ -44,7 +44,6 @@ cyclic_type_test/04: Fail, OK
cyclic_type2_test: Fail, OK
least_upper_bound_expansive_test/*: Fail, OK
async_await_syntax_test/a09a: CompileTimeError # Issue 21404
async_await_syntax_test/a10a: CompileTimeError # Issue 21404
async_await_syntax_test/b09a: CompileTimeError # Issue 21404
@ -55,7 +54,8 @@ async_await_syntax_test/d09a: CompileTimeError # Issue 21404
async_await_syntax_test/d10a: CompileTimeError # Issue 21404
[ $compiler == none || ($compiler == dart2dart && $builder_tag != new_backend) ]
async_throw_in_catch_test: Crash # Issue 22445, 22446
async_finally_rethrow_test: RuntimeError # Issue 22595.
async_throw_in_catch_test: Crash, RuntimeError # Issue 21404 or it could be a test error
asyncstar_throw_in_catch_test: CompileTimeError # Issue 21404
async_await_syntax_test/a03a: CompileTimeError # Issue 21404
async_await_syntax_test/a03b: CompileTimeError # Issue 21404
@ -67,7 +67,8 @@ async_await_syntax_test/d03a: CompileTimeError # Issue 21404
sync_generator3_test/test2: Fail # Issue 22300
[ $compiler == none && ($runtime == drt || $runtime == dartium|| $runtime == ContentShellOnAndroid) ]
async_throw_in_catch_test: Timeout # Issue 22445, 22446
async_finally_rethrow_test: RuntimeError # Issue 22595.
async_throw_in_catch_test: Crash, RuntimeError # Issue 21404 or it could be a test error
asyncstar_throw_in_catch_test: RuntimeError # Issue 21404
async_await_syntax_test/a03a: RuntimeError # Issue 21404
async_await_syntax_test/a03b: RuntimeError # Issue 21404

View file

@ -0,0 +1,35 @@
// Copyright (c) 2015, 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.
import "dart:async";
import "package:expect/expect.dart";
foo() async {
try {
print("a");
await new Future.value(3);
print("b");
throw "Error";
print("c");
} catch (e) {
print("d");
await new Future.error("Error2");
} finally {
print("e");
}
print("f");
}
main() async {
var error = "no error";
try {
await foo();
} catch (e) {
error = e;
}
Expect.equals("Error2", error);
}