mirror of
https://github.com/dart-lang/sdk
synced 2024-09-15 22:51:29 +00:00
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:
parent
f54675b398
commit
0036014d8a
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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") \
|
||||
|
|
29
tests/language/async_finally_rethrow_test.dart
Normal file
29
tests/language/async_finally_rethrow_test.dart
Normal 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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
35
tests/language/regress_22445_test.dart
Normal file
35
tests/language/regress_22445_test.dart
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in a new issue