Index: runtime/vm/flow_graph_builder.cc |
diff --git a/runtime/vm/flow_graph_builder.cc b/runtime/vm/flow_graph_builder.cc |
index 265c3088f86794e090dc8e8031afc7559b43efc2..ffe1cd8c11d16cadfbc2068c2477fd25cec76886 100644 |
--- a/runtime/vm/flow_graph_builder.cc |
+++ b/runtime/vm/flow_graph_builder.cc |
@@ -323,7 +323,8 @@ FlowGraphBuilder::FlowGraphBuilder( |
nesting_stack_(NULL), |
osr_id_(osr_id), |
jump_count_(0), |
- await_joins_(new (Z) ZoneGrowableArray<JoinEntryInstr*>()) {} |
+ await_joins_(new (Z) ZoneGrowableArray<JoinEntryInstr*>()), |
+ await_token_positions_(new (Z) ZoneGrowableArray<TokenPosition>()) {} |
void FlowGraphBuilder::AddCatchEntry(CatchBlockEntryInstr* entry) { |
@@ -1146,6 +1147,30 @@ void EffectGraphVisitor::VisitReturnNode(ReturnNode* node) { |
} |
} |
+ if (function.IsAsyncClosure() || function.IsAsyncGenClosure()) { |
+ // We are returning from an asynchronous closure. Before we do that, be |
+ // sure to clear the thread's asynchronous stack trace. |
+ const Library& async_lib = Library::Handle(Library::AsyncLibrary()); |
+ ASSERT(!async_lib.IsNull()); |
+ const String& private_name = String::ZoneHandle( |
+ async_lib.PrivateName(Symbols::ClearAsyncThreadStackTrace())); |
+ ASSERT(!private_name.IsNull()); |
+ const Function& async_clear_thread_stack_trace = Function::ZoneHandle( |
+ Z, |
+ Resolver::ResolveStatic(async_lib, String::ZoneHandle(String::null()), |
+ private_name, 0, Object::null_array())); |
+ ASSERT(!async_clear_thread_stack_trace.IsNull()); |
+ // Mark that this function is not debuggable. |
+ async_clear_thread_stack_trace.set_is_debuggable(false); |
+ ZoneGrowableArray<PushArgumentInstr*>* no_arguments = |
+ new (Z) ZoneGrowableArray<PushArgumentInstr*>(0); |
+ StaticCallInstr* call_async_clear_thread_stack_trace = new (Z) |
+ StaticCallInstr(node->token_pos().ToSynthetic(), |
+ async_clear_thread_stack_trace, Object::null_array(), |
+ no_arguments, owner()->ic_data_array()); |
+ Do(call_async_clear_thread_stack_trace); |
+ } |
+ |
// Async functions contain two types of return statements: |
// 1) Returns that should complete the completer once all finally blocks have |
// been inlined (call: :async_completer.complete(return_value)). These |
@@ -1169,9 +1194,18 @@ void EffectGraphVisitor::VisitReturnNode(ReturnNode* node) { |
arguments->Add(PushArgument(rcv_value)); |
Value* returned_value = Bind(BuildLoadExprTemp(node->token_pos())); |
arguments->Add(PushArgument(returned_value)); |
- InstanceCallInstr* call = new (Z) InstanceCallInstr( |
- node->token_pos(), Symbols::CompleterComplete(), Token::kILLEGAL, |
- arguments, Object::null_array(), 1, owner()->ic_data_array()); |
+ |
+ const Library& async_library = Library::Handle(Library::AsyncLibrary()); |
+ ASSERT(!async_library.IsNull()); |
+ |
+ const Function& complete_on_async_return_function = |
+ Function::ZoneHandle(async_library.LookupFunctionAllowPrivate( |
+ Symbols::CompleterCompleteOnAsyncReturn())); |
+ ASSERT(!complete_on_async_return_function.IsNull()); |
+ |
+ StaticCallInstr* call = new (Z) StaticCallInstr( |
+ node->token_pos().ToSynthetic(), complete_on_async_return_function, |
+ Object::null_array(), arguments, owner()->ic_data_array()); |
Do(call); |
// Rebind the return value for the actual return call to be null. |
@@ -2181,6 +2215,8 @@ void EffectGraphVisitor::VisitAwaitMarkerNode(AwaitMarkerNode* node) { |
Value* jump_val = Bind(new (Z) ConstantInstr( |
Smi::ZoneHandle(Z, Smi::New(jump_count)), node->token_pos())); |
Do(BuildStoreLocal(*jump_var, jump_val, node->token_pos())); |
+ // Add a mapping from jump_count -> token_position. |
+ owner()->AppendAwaitTokenPosition(node->token_pos()); |
// Save the current context for resuming. |
BuildSaveContext(*ctx_var, node->token_pos()); |
} |
@@ -3802,6 +3838,43 @@ void EffectGraphVisitor::VisitSequenceNode(SequenceNode* node) { |
} |
} |
+ if (is_top_level_sequence && |
+ (function.IsAsyncClosure() || function.IsAsyncGenClosure())) { |
+ LocalScope* top_scope = node->scope(); |
+ // Fetch the :async_stack_trace variable and store it into the thread. |
+ LocalVariable* async_stack_trace_var = |
+ top_scope->LookupVariable(Symbols::AsyncStackTraceVar(), false); |
+ ASSERT((async_stack_trace_var != NULL) && |
+ async_stack_trace_var->is_captured()); |
+ // Load :async_stack_trace |
+ Value* async_stack_trace_value = Bind(BuildLoadLocal( |
+ *async_stack_trace_var, node->token_pos().ToSynthetic())); |
+ // Setup arguments for _asyncSetThreadStackTrace. |
+ ZoneGrowableArray<PushArgumentInstr*>* arguments = |
+ new (Z) ZoneGrowableArray<PushArgumentInstr*>(1); |
+ arguments->Add(PushArgument(async_stack_trace_value)); |
+ |
+ // Lookup _asyncSetThreadStackTrace |
+ const Library& async_lib = Library::Handle(Library::AsyncLibrary()); |
+ ASSERT(!async_lib.IsNull()); |
+ const String& private_name = String::ZoneHandle( |
+ async_lib.PrivateName(Symbols::SetAsyncThreadStackTrace())); |
+ ASSERT(!private_name.IsNull()); |
+ const Function& async_set_thread_stack_trace = Function::ZoneHandle( |
+ Z, |
+ Resolver::ResolveStatic(async_lib, String::ZoneHandle(String::null()), |
+ private_name, 1, Object::null_array())); |
+ ASSERT(!async_set_thread_stack_trace.IsNull()); |
+ // Mark that this function is not debuggable. |
+ async_set_thread_stack_trace.set_is_debuggable(false); |
+ // Call _asyncSetThreadStackTrace |
+ StaticCallInstr* call_async_set_thread_stack_trace = new (Z) |
+ StaticCallInstr(node->token_pos().ToSynthetic(), |
+ async_set_thread_stack_trace, Object::null_array(), |
+ arguments, owner()->ic_data_array()); |
+ Do(call_async_set_thread_stack_trace); |
+ } |
+ |
if (FLAG_support_debugger && is_top_level_sequence && |
function.is_debuggable()) { |
// Place a debug check at method entry to ensure breaking on a method always |
@@ -3910,6 +3983,7 @@ void EffectGraphVisitor::VisitSequenceNode(SequenceNode* node) { |
entry_ = NULL; |
exit_ = NULL; |
+ // Load the jump counter. |
LoadLocalNode* load_jump_count = |
new (Z) LoadLocalNode(node->token_pos(), jump_var); |
ComparisonNode* check_jump_count; |
@@ -4337,10 +4411,16 @@ FlowGraph* FlowGraphBuilder::BuildGraph() { |
FlowGraph* graph = |
new (Z) FlowGraph(parsed_function(), graph_entry_, last_used_block_id_); |
+ graph->set_await_token_positions(await_token_positions_); |
return graph; |
} |
+void FlowGraphBuilder::AppendAwaitTokenPosition(TokenPosition token_pos) { |
+ await_token_positions_->Add(token_pos); |
+} |
+ |
+ |
void FlowGraphBuilder::PruneUnreachable() { |
ASSERT(osr_id_ != Compiler::kNoOSRDeoptId); |
BitVector* block_marks = new (Z) BitVector(Z, last_used_block_id_ + 1); |