Track the 'awaiter return' call stack use it to detect uncaught exceptions in async functions

Tracking the awaiter return call stack:

- [x] Each async function closure now knows who is awaiting on their
return. This is effectively the asynchronous equivalent of the 'frame pointer'.
- [x] Each async* function closure now knows how is listening on their
stream. This is effectively the asynchronous equivalent of the 'frame pointer'.

Detecting uncaught exceptions in async functions:

- [x] Code object keeps a map from :await_jump_var to token position
- [x] Exception Handlers keep track if they are generated (as part of compilation) or directly from user code
- [x] Debugger maps :await_jump_var to a specific try index

Fixes #27242

R=rmacnak@google.com

Review-Url: https://codereview.chromium.org/2692803006 .
This commit is contained in:
John McCutchan
2017-02-27 14:16:15 -08:00
parent 69063303ac
commit cba7e3e79a
33 changed files with 770 additions and 67 deletions
+37 -3
View File
@@ -45,7 +45,10 @@ Function _asyncErrorWrapperHelper(continuation) {
///
/// Returns the result of registering with `.then`.
Future _awaitHelper(
var object, Function thenCallback, Function errorCallback) {
var object,
Function thenCallback,
Function errorCallback,
var awaiter) {
if (object is! Future) {
object = new _Future().._setValue(object);
} else if (object is! _Future) {
@@ -59,9 +62,20 @@ Future _awaitHelper(
//
// We can only do this for our internal futures (the default implementation of
// all futures that are constructed by the `dart:async` library).
object._awaiter = awaiter;
return object._thenNoZoneRegistration(thenCallback, errorCallback);
}
// Called as part of the 'await for (...)' construct. Registers the
// awaiter on the stream.
void _asyncStarListenHelper(var object, var awaiter) {
if (object is! _StreamImpl) {
return;
}
// `object` is a `_StreamImpl`.
object._awaiter = awaiter;
}
// _AsyncStarStreamController is used by the compiler to implement
// async* generator functions.
class _AsyncStarStreamController {
@@ -73,7 +87,14 @@ class _AsyncStarStreamController {
bool isSuspendedAtYield = false;
Completer cancellationCompleter = null;
Stream get stream => controller.stream;
Stream get stream {
Stream local = controller.stream;
if (local is! _StreamImpl) {
return local;
}
local._generator = asyncStarBody;
return local;
}
void runBody() {
isScheduled = false;
@@ -194,10 +215,23 @@ class _AsyncStarStreamController {
@patch void _rethrow(Object error, StackTrace stackTrace) native "Async_rethrow";
@patch class _Future<T> {
/// The closure implementing the async[*]-body that is `await`ing this future.
Function _awaiter;
}
@patch class _StreamImpl<T> {
/// The closure implementing the async[*]-body that is `await`ing this future.
Function _awaiter;
/// The closure implementing the async-generator body that is creating events
/// for this stream.
Function _generator;
}
/// Returns a [StackTrace] object containing the synchronous prefix for this
/// asynchronous method.
Object _asyncStackTraceHelper() native "StackTrace_asyncStackTraceHelper";
Object _asyncStackTraceHelper()
native "StackTrace_asyncStackTraceHelper";
void _clearAsyncThreadStackTrace()
native "StackTrace_clearAsyncThreadStackTrace";
@@ -0,0 +1,53 @@
// Copyright (c) 2017, 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=--error_on_bad_type --error_on_bad_override
import 'package:observatory/service_io.dart';
import 'package:observatory/models.dart' as M;
import 'package:unittest/unittest.dart';
import 'test_helper.dart';
import 'service_test_common.dart';
const LINE_A = 29;
class Foo {
}
doThrow() {
throw "TheException"; // Line 13.
return "end of doThrow";
}
asyncThrower() async {
doThrow();
}
testeeMain() async {
// No try ... catch.
await asyncThrower();
}
var tests = [
hasStoppedWithUnhandledException,
(Isolate isolate) async {
print("We stoppped!");
var stack = await isolate.getStack();
expect(stack['asyncCausalFrames'], isNotNull);
var asyncStack = stack['asyncCausalFrames'];
expect(asyncStack[0].toString(), contains('doThrow'));
expect(asyncStack[1].toString(), contains('asyncThrower'));
expect(asyncStack[2].kind, equals(M.FrameKind.asyncSuspensionMarker));
expect(asyncStack[3].toString(), contains('testeeMain'));
// We've stopped at LINE_A.
expect(await asyncStack[3].location.toUserString(),
contains('.dart:$LINE_A'));
}
];
main(args) => runIsolateTests(args,
tests,
pause_on_unhandled_exceptions: true,
testeeConcurrent: testeeMain);
@@ -0,0 +1,65 @@
// Copyright (c) 2017, 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=--error_on_bad_type --error_on_bad_override
import 'package:observatory/service_io.dart';
import 'package:observatory/models.dart' as M;
import 'package:unittest/unittest.dart';
import 'test_helper.dart';
import 'service_test_common.dart';
const LINE_A = 37;
class Foo {
}
doThrow() {
throw "TheException"; // Line 13.
return "end of doThrow";
}
asyncThrower() async {
doThrow();
}
testeeMain() async {
try {
// caught.
try {
await asyncThrower();
} catch (e) {
}
// uncaught.
try {
await asyncThrower(); // LINE_A.
} on double catch (e) {
}
} on Foo catch (e) {
}
}
var tests = [
hasStoppedWithUnhandledException,
(Isolate isolate) async {
print("We stoppped!");
var stack = await isolate.getStack();
expect(stack['asyncCausalFrames'], isNotNull);
var asyncStack = stack['asyncCausalFrames'];
expect(asyncStack[0].toString(), contains('doThrow'));
expect(asyncStack[1].toString(), contains('asyncThrower'));
expect(asyncStack[2].kind, equals(M.FrameKind.asyncSuspensionMarker));
expect(asyncStack[3].toString(), contains('testeeMain'));
// We've stopped at LINE_A.
expect(await asyncStack[3].location.toUserString(),
contains('.dart:$LINE_A'));
}
];
main(args) => runIsolateTests(args,
tests,
pause_on_unhandled_exceptions: true,
testeeConcurrent: testeeMain);
+1
View File
@@ -1801,6 +1801,7 @@ class NativeBodyNode : public AstNode {
class CatchClauseNode : public AstNode {
public:
static const intptr_t kInvalidTryIndex = -1;
static const intptr_t kImplicitAsyncTryIndex = 0;
CatchClauseNode(TokenPosition token_pos,
SequenceNode* catch_block,
+9 -2
View File
@@ -136,7 +136,10 @@ void AwaitTransformer::VisitAwaitNode(AwaitNode* node) {
// :await_temp_var_X = <expr>;
// AwaitMarker(kNewContinuationState);
// :result_param = _awaitHelper(
// :await_temp_var_X, :async_then_callback, :async_catch_error_callback);
// :await_temp_var_X,
// :async_then_callback,
// :async_catch_error_callback,
// :async_op);
// return; // (return_type() == kContinuationTarget)
//
// :saved_try_ctx_var = :await_saved_try_ctx_var_y;
@@ -165,7 +168,10 @@ void AwaitTransformer::VisitAwaitNode(AwaitNode* node) {
preamble_->Add(await_marker);
// :result_param = _awaitHelper(
// :await_temp, :async_then_callback, :async_catch_error_callback)
// :await_temp,
// :async_then_callback,
// :async_catch_error_callback,
// :async_op)
const Library& async_lib = Library::Handle(Library::AsyncLibrary());
const Function& async_await_helper = Function::ZoneHandle(
Z, async_lib.LookupFunctionAllowPrivate(Symbols::AsyncAwaitHelper()));
@@ -177,6 +183,7 @@ void AwaitTransformer::VisitAwaitNode(AwaitNode* node) {
new (Z) LoadLocalNode(token_pos, async_then_callback));
async_await_helper_args->Add(
new (Z) LoadLocalNode(token_pos, async_catch_error_callback));
async_await_helper_args->Add(new (Z) LoadLocalNode(token_pos, async_op));
StaticCallNode* await_helper_call = new (Z) StaticCallNode(
node->token_pos(), async_await_helper, async_await_helper_args);
+4 -2
View File
@@ -102,12 +102,14 @@ RawExceptionHandlers* ExceptionHandlerList::FinalizeExceptionHandlers(
ASSERT((list_[i].outer_try_index == -1) &&
(list_[i].pc_offset == ExceptionHandlers::kInvalidPcOffset));
handlers.SetHandlerInfo(i, list_[i].outer_try_index, list_[i].pc_offset,
list_[i].needs_stacktrace, has_catch_all);
list_[i].needs_stacktrace, has_catch_all,
list_[i].token_pos, list_[i].is_generated);
handlers.SetHandledTypes(i, Array::empty_array());
} else {
const bool has_catch_all = ContainsDynamic(*list_[i].handler_types);
handlers.SetHandlerInfo(i, list_[i].outer_try_index, list_[i].pc_offset,
list_[i].needs_stacktrace, has_catch_all);
list_[i].needs_stacktrace, has_catch_all,
list_[i].token_pos, list_[i].is_generated);
handlers.SetHandledTypes(i, *list_[i].handler_types);
}
}
+8
View File
@@ -74,6 +74,8 @@ class ExceptionHandlerList : public ZoneAllocated {
struct HandlerDesc {
intptr_t outer_try_index; // Try block in which this try block is nested.
intptr_t pc_offset; // Handler PC offset value.
TokenPosition token_pos; // Token position of handler.
bool is_generated; // False if this is directly from Dart code.
const Array* handler_types; // Catch clause guards.
bool needs_stacktrace;
};
@@ -86,6 +88,8 @@ class ExceptionHandlerList : public ZoneAllocated {
struct HandlerDesc data;
data.outer_try_index = -1;
data.pc_offset = ExceptionHandlers::kInvalidPcOffset;
data.token_pos = TokenPosition::kNoSource;
data.is_generated = true;
data.handler_types = NULL;
data.needs_stacktrace = false;
list_.Add(data);
@@ -94,6 +98,8 @@ class ExceptionHandlerList : public ZoneAllocated {
void AddHandler(intptr_t try_index,
intptr_t outer_try_index,
intptr_t pc_offset,
TokenPosition token_pos,
bool is_generated,
const Array& handler_types,
bool needs_stacktrace) {
ASSERT(try_index >= 0);
@@ -103,6 +109,8 @@ class ExceptionHandlerList : public ZoneAllocated {
list_[try_index].outer_try_index = outer_try_index;
ASSERT(list_[try_index].pc_offset == ExceptionHandlers::kInvalidPcOffset);
list_[try_index].pc_offset = pc_offset;
list_[try_index].token_pos = token_pos;
list_[try_index].is_generated = is_generated;
ASSERT(handler_types.IsZoneHandle());
list_[try_index].handler_types = &handler_types;
list_[try_index].needs_stacktrace |= needs_stacktrace;
+28
View File
@@ -543,6 +543,34 @@ void CompileParsedFunctionHelper::FinalizeCompilation(
Code::Handle(Code::FinalizeCode(function, assembler, optimized()));
code.set_is_optimized(optimized());
code.set_owner(function);
#if !defined(PRODUCT)
if (FLAG_support_debugger) {
ZoneGrowableArray<TokenPosition>* await_token_positions =
flow_graph->await_token_positions();
if (await_token_positions != NULL) {
Smi& token_pos_value = Smi::Handle(zone);
if (await_token_positions->length() > 0) {
const Array& await_to_token_map = Array::Handle(
zone, Array::New(await_token_positions->length(), Heap::kOld));
ASSERT(!await_to_token_map.IsNull());
for (intptr_t i = 0; i < await_token_positions->length(); i++) {
TokenPosition token_pos =
await_token_positions->At(i).FromSynthetic();
if (!token_pos.IsReal()) {
// Some async machinary uses sentinel values. Map them to
// no source position.
token_pos_value = Smi::New(TokenPosition::kNoSourcePos);
} else {
token_pos_value = Smi::New(token_pos.value());
}
await_to_token_map.SetAt(i, token_pos_value);
}
code.SetAwaitTokenPositions(await_to_token_map);
}
}
}
#endif // !defined(PRODUCT)
if (!function.IsOptimizable()) {
// A function with huge unoptimized code can become non-optimizable
// after generating unoptimized code.
+358 -34
View File
@@ -259,7 +259,7 @@ ActivationFrame::ActivationFrame(uword pc,
ctx_(Context::ZoneHandle()),
code_(Code::ZoneHandle(code.raw())),
function_(Function::ZoneHandle(code.function())),
live_frame_(kind == kRegular),
live_frame_((kind == kRegular) || (kind == kAsyncActivation)),
token_pos_initialized_(false),
token_pos_(TokenPosition::kNoSource),
try_index_(-1),
@@ -297,6 +297,37 @@ ActivationFrame::ActivationFrame(Kind kind)
desc_indices_(8),
pc_desc_(PcDescriptors::ZoneHandle()) {}
ActivationFrame::ActivationFrame(const Closure& async_activation)
: pc_(0),
fp_(0),
sp_(0),
ctx_(Context::ZoneHandle()),
code_(Code::ZoneHandle()),
function_(Function::ZoneHandle()),
live_frame_(false),
token_pos_initialized_(false),
token_pos_(TokenPosition::kNoSource),
try_index_(-1),
line_number_(-1),
column_number_(-1),
context_level_(-1),
deopt_frame_(Array::ZoneHandle()),
deopt_frame_offset_(0),
kind_(kAsyncActivation),
vars_initialized_(false),
var_descriptors_(LocalVarDescriptors::ZoneHandle()),
desc_indices_(8),
pc_desc_(PcDescriptors::ZoneHandle()) {
// Extract the function and the code from the asynchronous activation.
function_ = async_activation.function();
code_ = function_.unoptimized_code();
ctx_ = async_activation.context();
ASSERT(fp_ == 0);
ASSERT(!ctx_.IsNull());
}
bool Debugger::NeedsIsolateEvents() {
return ((isolate_ != Dart::vm_isolate()) &&
!ServiceIsolate::IsServiceIsolateDescendant(isolate_) &&
@@ -679,6 +710,231 @@ intptr_t ActivationFrame::ContextLevel() {
return context_level_;
}
RawObject* ActivationFrame::GetAsyncCompleter() {
if (!function_.IsAsyncClosure()) {
return Object::null();
}
GetVarDescriptors();
intptr_t var_desc_len = var_descriptors_.Length();
if (!live_frame_) {
// Not actually on the stack. Pull it out of the closure's context.
intptr_t var_desc_len = var_descriptors_.Length();
for (intptr_t i = 0; i < var_desc_len; i++) {
RawLocalVarDescriptors::VarInfo var_info;
var_descriptors_.GetInfo(i, &var_info);
const int8_t kind = var_info.kind();
if (var_descriptors_.GetName(i) == Symbols::AsyncCompleter().raw()) {
ASSERT(kind == RawLocalVarDescriptors::kContextVar);
ASSERT(!ctx_.IsNull());
return ctx_.At(var_info.index());
}
}
} else {
ASSERT(fp() != 0);
// On the stack.
for (intptr_t i = 0; i < var_desc_len; i++) {
RawLocalVarDescriptors::VarInfo var_info;
var_descriptors_.GetInfo(i, &var_info);
if (var_descriptors_.GetName(i) == Symbols::AsyncCompleter().raw()) {
const int8_t kind = var_info.kind();
if (kind == RawLocalVarDescriptors::kStackVar) {
return GetStackVar(var_info.index());
} else {
ASSERT(kind == RawLocalVarDescriptors::kContextVar);
return GetContextVar(var_info.scope_id, var_info.index());
}
}
}
}
return Object::null();
}
RawObject* ActivationFrame::GetAsyncCompleterAwaiter(const Object& completer) {
const Class& sync_completer_cls = Class::Handle(completer.clazz());
ASSERT(!sync_completer_cls.IsNull());
const Class& completer_cls = Class::Handle(sync_completer_cls.SuperClass());
const Field& future_field =
Field::Handle(completer_cls.LookupInstanceFieldAllowPrivate(
Symbols::CompleterFuture()));
ASSERT(!future_field.IsNull());
Instance& future = Instance::Handle();
future ^= Instance::Cast(completer).GetField(future_field);
ASSERT(!future.IsNull());
const Class& future_cls = Class::Handle(future.clazz());
ASSERT(!future_cls.IsNull());
const Field& awaiter_field = Field::Handle(
future_cls.LookupInstanceFieldAllowPrivate(Symbols::_Awaiter()));
ASSERT(!awaiter_field.IsNull());
return future.GetField(awaiter_field);
}
RawObject* ActivationFrame::GetAsyncStreamControllerStream() {
if (!function_.IsAsyncGenClosure()) {
return Object::null();
}
GetVarDescriptors();
intptr_t var_desc_len = var_descriptors_.Length();
if (!live_frame_) {
// Not actually on the stack. Pull it out of the closure's context.
intptr_t var_desc_len = var_descriptors_.Length();
for (intptr_t i = 0; i < var_desc_len; i++) {
RawLocalVarDescriptors::VarInfo var_info;
var_descriptors_.GetInfo(i, &var_info);
const int8_t kind = var_info.kind();
if (var_descriptors_.GetName(i) == Symbols::ControllerStream().raw()) {
ASSERT(kind == RawLocalVarDescriptors::kContextVar);
ASSERT(!ctx_.IsNull());
return ctx_.At(var_info.index());
}
}
} else {
ASSERT(fp() != 0);
// On the stack.
for (intptr_t i = 0; i < var_desc_len; i++) {
RawLocalVarDescriptors::VarInfo var_info;
var_descriptors_.GetInfo(i, &var_info);
if (var_descriptors_.GetName(i) == Symbols::ControllerStream().raw()) {
const int8_t kind = var_info.kind();
if (kind == RawLocalVarDescriptors::kStackVar) {
return GetStackVar(var_info.index());
} else {
ASSERT(kind == RawLocalVarDescriptors::kContextVar);
return GetContextVar(var_info.scope_id, var_info.index());
}
}
}
}
return Object::null();
}
RawObject* ActivationFrame::GetAsyncStreamControllerStreamAwaiter(
const Object& stream) {
const Class& stream_cls = Class::Handle(stream.clazz());
ASSERT(!stream_cls.IsNull());
const Class& stream_impl_cls = Class::Handle(stream_cls.SuperClass());
const Field& awaiter_field = Field::Handle(
stream_impl_cls.LookupInstanceFieldAllowPrivate(Symbols::_Awaiter()));
ASSERT(!awaiter_field.IsNull());
return Instance::Cast(stream).GetField(awaiter_field);
}
RawObject* ActivationFrame::GetAsyncAwaiter() {
const Object& completer = Object::Handle(GetAsyncCompleter());
if (!completer.IsNull()) {
return GetAsyncCompleterAwaiter(completer);
}
const Object& async_stream_controller_stream =
Object::Handle(GetAsyncStreamControllerStream());
if (!async_stream_controller_stream.IsNull()) {
return GetAsyncStreamControllerStreamAwaiter(
async_stream_controller_stream);
}
return Object::null();
}
bool ActivationFrame::HandlesException(const Instance& exc_obj) {
intptr_t try_index = TryIndex();
if (try_index < 0) {
return false;
}
ExceptionHandlers& handlers = ExceptionHandlers::Handle();
Array& handled_types = Array::Handle();
AbstractType& type = Type::Handle();
const TypeArguments& no_instantiator = TypeArguments::Handle();
const intptr_t try_index_threshold = CatchClauseNode::kImplicitAsyncTryIndex;
const bool is_async =
function().IsAsyncClosure() || function().IsAsyncGenClosure();
handlers = code().exception_handlers();
ASSERT(!handlers.IsNull());
intptr_t num_handlers_checked = 0;
while (try_index >= try_index_threshold) {
// Detect circles in the exception handler data.
num_handlers_checked++;
ASSERT(num_handlers_checked <= handlers.num_entries());
// Only consider user written handlers for async methods.
if (!is_async || !handlers.IsGenerated(try_index)) {
handled_types = handlers.GetHandledTypes(try_index);
const intptr_t num_types = handled_types.Length();
for (intptr_t k = 0; k < num_types; k++) {
type ^= handled_types.At(k);
ASSERT(!type.IsNull());
// Uninstantiated types are not added to ExceptionHandlers data.
ASSERT(type.IsInstantiated());
if (type.IsMalformed()) {
continue;
}
if (type.IsDynamicType()) {
return true;
}
if (exc_obj.IsInstanceOf(type, no_instantiator, NULL)) {
return true;
}
}
}
try_index = handlers.OuterTryIndex(try_index);
}
return false;
}
void ActivationFrame::ExtractTokenPositionFromAsyncClosure() {
// Attempt to determine the token position from the async closure.
ASSERT(function_.IsAsyncGenClosure() || function_.IsAsyncClosure());
// This should only be called on frames that aren't active on the stack.
ASSERT(fp() == 0);
const Array& await_to_token_map =
Array::Handle(code_.await_token_positions());
if (await_to_token_map.IsNull()) {
// No mapping.
return;
}
GetVarDescriptors();
GetPcDescriptors();
intptr_t var_desc_len = var_descriptors_.Length();
intptr_t await_jump_var = -1;
for (intptr_t i = 0; i < var_desc_len; i++) {
RawLocalVarDescriptors::VarInfo var_info;
var_descriptors_.GetInfo(i, &var_info);
const int8_t kind = var_info.kind();
if (var_descriptors_.GetName(i) == Symbols::AwaitJumpVar().raw()) {
ASSERT(kind == RawLocalVarDescriptors::kContextVar);
ASSERT(!ctx_.IsNull());
Object& await_jump_index = Object::Handle(ctx_.At(var_info.index()));
ASSERT(await_jump_index.IsSmi());
await_jump_var = Smi::Cast(await_jump_index).Value();
}
}
if (await_jump_var < 0) {
return;
}
ASSERT(await_jump_var < await_to_token_map.Length());
const Object& token_pos =
Object::Handle(await_to_token_map.At(await_jump_var));
if (token_pos.IsNull()) {
return;
}
ASSERT(token_pos.IsSmi());
token_pos_ = TokenPosition(Smi::Cast(token_pos).Value());
token_pos_initialized_ = true;
PcDescriptors::Iterator iter(pc_desc_, RawPcDescriptors::kAnyKind);
while (iter.MoveNext()) {
if (iter.TokenPos() == token_pos_) {
// Match the lowest try index at this token position.
// TODO(johnmccutchan): Is this heuristic precise enough?
if (iter.TryIndex() != CatchClauseNode::kInvalidTryIndex) {
if ((try_index_ == -1) || (iter.TryIndex() < try_index_)) {
try_index_ = iter.TryIndex();
}
}
}
}
}
// Get the saved current context of this activation.
const Context& ActivationFrame::GetSavedCurrentContext() {
@@ -724,35 +980,10 @@ RawObject* ActivationFrame::GetAsyncOperation() {
ActivationFrame* DebuggerStackTrace::GetHandlerFrame(
const Instance& exc_obj) const {
ExceptionHandlers& handlers = ExceptionHandlers::Handle();
Array& handled_types = Array::Handle();
AbstractType& type = Type::Handle();
const TypeArguments& no_instantiator = TypeArguments::Handle();
for (intptr_t frame_index = 0; frame_index < Length(); frame_index++) {
ActivationFrame* frame = FrameAt(frame_index);
intptr_t try_index = frame->TryIndex();
if (try_index < 0) continue;
handlers = frame->code().exception_handlers();
ASSERT(!handlers.IsNull());
intptr_t num_handlers_checked = 0;
while (try_index >= 0) {
// Detect circles in the exception handler data.
num_handlers_checked++;
ASSERT(num_handlers_checked <= handlers.num_entries());
handled_types = handlers.GetHandledTypes(try_index);
const intptr_t num_types = handled_types.Length();
for (intptr_t k = 0; k < num_types; k++) {
type ^= handled_types.At(k);
ASSERT(!type.IsNull());
// Uninstantiated types are not added to ExceptionHandlers data.
ASSERT(type.IsInstantiated());
if (type.IsMalformed()) continue;
if (type.IsDynamicType()) return frame;
if (exc_obj.IsInstanceOf(type, no_instantiator, NULL)) {
return frame;
}
}
try_index = handlers.OuterTryIndex(try_index);
if (frame->HandlesException(exc_obj)) {
return frame;
}
}
return NULL;
@@ -1152,7 +1383,10 @@ void ActivationFrame::PrintToJSONObjectRegular(JSONObject* jsobj, bool full) {
TokenPosition visible_end_token_pos;
VariableAt(v, &var_name, &declaration_token_pos, &visible_start_token_pos,
&visible_end_token_pos, &var_value);
if (var_name.raw() != Symbols::AsyncOperation().raw()) {
if ((var_name.raw() != Symbols::AsyncOperation().raw()) &&
(var_name.raw() != Symbols::AsyncCompleter().raw()) &&
(var_name.raw() != Symbols::ControllerStream().raw()) &&
(var_name.raw() != Symbols::AwaitJumpVar().raw())) {
JSONObject jsvar(&jsvars);
jsvar.AddProperty("type", "BoundVariable");
var_name = String::ScrubName(var_name);
@@ -1548,10 +1782,12 @@ ActivationFrame* Debugger::CollectDartFrame(Isolate* isolate,
StackFrame* frame,
const Code& code,
const Array& deopt_frame,
intptr_t deopt_frame_offset) {
intptr_t deopt_frame_offset,
ActivationFrame::Kind kind) {
ASSERT(code.ContainsInstructionAt(pc));
ActivationFrame* activation = new ActivationFrame(
pc, frame->fp(), frame->sp(), code, deopt_frame, deopt_frame_offset);
ActivationFrame* activation =
new ActivationFrame(pc, frame->fp(), frame->sp(), code, deopt_frame,
deopt_frame_offset, kind);
if (FLAG_trace_debugger_stacktrace) {
const Context& ctx = activation->GetSavedCurrentContext();
OS::PrintErr("\tUsing saved context: %s\n", ctx.ToCString());
@@ -1728,6 +1964,64 @@ DebuggerStackTrace* Debugger::CollectAsyncCausalStackTrace() {
return stack_trace;
}
DebuggerStackTrace* Debugger::CollectAwaiterReturnStackTrace() {
if (!FLAG_causal_async_stacks) {
return NULL;
}
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
Isolate* isolate = thread->isolate();
DebuggerStackTrace* stack_trace = new DebuggerStackTrace(8);
StackFrameIterator iterator(StackFrameIterator::kDontValidateFrames);
Code& code = Code::Handle(zone);
Function& function = Function::Handle(zone);
Code& inlined_code = Code::Handle(zone);
Closure& async_activation = Closure::Handle(zone);
Array& deopt_frame = Array::Handle(zone);
for (StackFrame* frame = iterator.NextFrame(); frame != NULL;
frame = iterator.NextFrame()) {
ASSERT(frame->IsValid());
if (frame->IsDartFrame()) {
code = frame->LookupDartCode();
function = code.function();
if (function.IsAsyncClosure() || function.IsAsyncGenClosure()) {
ActivationFrame* activation = CollectDartFrame(
isolate, frame->pc(), frame, code, Object::null_array(), 0,
ActivationFrame::kAsyncActivation);
ASSERT(activation != NULL);
stack_trace->AddActivation(activation);
// Grab the awaiter.
async_activation ^= activation->GetAsyncAwaiter();
break;
} else {
AppendCodeFrames(thread, isolate, zone, stack_trace, frame, &code,
&inlined_code, &deopt_frame);
}
}
}
// Return NULL to indicate that there is no useful information in this stack
// trace because we never found an awaiter.
if (async_activation.IsNull()) {
return NULL;
}
// Append the awaiter return call stack.
while (!async_activation.IsNull()) {
ActivationFrame* activation = new ActivationFrame(async_activation);
async_activation ^= activation->GetAsyncAwaiter();
activation->ExtractTokenPositionFromAsyncClosure();
stack_trace->AddActivation(activation);
}
return stack_trace;
}
ActivationFrame* Debugger::TopDartFrame() const {
StackFrameIterator iterator(false);
StackFrame* frame = iterator.NextFrame();
@@ -1828,6 +2122,29 @@ Dart_ExceptionPauseInfo Debugger::GetExceptionPauseInfo() const {
}
bool Debugger::ShouldPauseOnAsyncException(DebuggerStackTrace* stack_trace,
const Instance& exc) {
if (exc_pause_info_ == kNoPauseOnExceptions) {
return false;
}
if (exc_pause_info_ == kPauseOnAllExceptions) {
return true;
}
ASSERT(exc_pause_info_ == kPauseOnUnhandledExceptions);
for (intptr_t i = 0; i < stack_trace->Length(); i++) {
ActivationFrame* frame = stack_trace->FrameAt(i);
if (frame->HandlesException(exc)) {
if (FLAG_verbose_debug) {
OS::PrintErr("%s is caught by frame %s\n", exc.ToCString(),
frame->ToCString());
}
return false;
}
}
return true;
}
bool Debugger::ShouldPauseOnException(DebuggerStackTrace* stack_trace,
const Instance& exception) {
if (exc_pause_info_ == kNoPauseOnExceptions) {
@@ -1859,9 +2176,16 @@ void Debugger::PauseException(const Instance& exc) {
(exc_pause_info_ == kNoPauseOnExceptions)) {
return;
}
DebuggerStackTrace* awaiter_stack_trace = CollectAwaiterReturnStackTrace();
DebuggerStackTrace* stack_trace = CollectStackTrace();
if (!ShouldPauseOnException(stack_trace, exc)) {
return;
if (awaiter_stack_trace != NULL) {
if (!ShouldPauseOnAsyncException(awaiter_stack_trace, exc)) {
return;
}
} else {
if (!ShouldPauseOnException(stack_trace, exc)) {
return;
}
}
ServiceEvent event(isolate_, ServiceEvent::kPauseException);
event.set_exception(&exc);
+25 -6
View File
@@ -260,6 +260,7 @@ class ActivationFrame : public ZoneAllocated {
kRegular,
kAsyncSuspensionMarker,
kAsyncCausal,
kAsyncActivation,
};
ActivationFrame(uword pc,
@@ -274,6 +275,8 @@ class ActivationFrame : public ZoneAllocated {
explicit ActivationFrame(Kind kind);
explicit ActivationFrame(const Closure& async_activation);
uword pc() const { return pc_; }
uword fp() const { return fp_; }
uword sp() const { return sp_; }
@@ -332,6 +335,10 @@ class ActivationFrame : public ZoneAllocated {
// the complete script, function, and, local variable objects are included.
void PrintToJSONObject(JSONObject* jsobj, bool full = false);
RawObject* GetAsyncAwaiter();
bool HandlesException(const Instance& exc_obj);
private:
void PrintToJSONObjectRegular(JSONObject* jsobj, bool full);
void PrintToJSONObjectAsyncCausal(JSONObject* jsobj, bool full);
@@ -346,6 +353,12 @@ class ActivationFrame : public ZoneAllocated {
void GetVarDescriptors();
void GetDescIndices();
RawObject* GetAsyncStreamControllerStreamAwaiter(const Object& stream);
RawObject* GetAsyncStreamControllerStream();
RawObject* GetAsyncCompleterAwaiter(const Object& completer);
RawObject* GetAsyncCompleter();
void ExtractTokenPositionFromAsyncClosure();
static const char* KindToCString(Kind kind) {
switch (kind) {
case kRegular:
@@ -627,12 +640,14 @@ class Debugger {
void SyncBreakpointLocation(BreakpointLocation* loc);
ActivationFrame* TopDartFrame() const;
static ActivationFrame* CollectDartFrame(Isolate* isolate,
uword pc,
StackFrame* frame,
const Code& code,
const Array& deopt_frame,
intptr_t deopt_frame_offset);
static ActivationFrame* CollectDartFrame(
Isolate* isolate,
uword pc,
StackFrame* frame,
const Code& code,
const Array& deopt_frame,
intptr_t deopt_frame_offset,
ActivationFrame::Kind kind = ActivationFrame::kRegular);
static RawArray* DeoptimizeToArray(Thread* thread,
StackFrame* frame,
const Code& code);
@@ -648,10 +663,14 @@ class Debugger {
Array* deopt_frame);
static DebuggerStackTrace* CollectStackTrace();
static DebuggerStackTrace* CollectAsyncCausalStackTrace();
static DebuggerStackTrace* CollectAwaiterReturnStackTrace();
void SignalPausedEvent(ActivationFrame* top_frame, Breakpoint* bpt);
intptr_t nextId() { return next_id_++; }
bool ShouldPauseOnAsyncException(DebuggerStackTrace* stack_trace,
const Instance& exc);
bool ShouldPauseOnException(DebuggerStackTrace* stack_trace,
const Instance& exc);
+1
View File
@@ -99,6 +99,7 @@ struct ExceptionHandlerInfo {
int16_t outer_try_index; // Try block index of enclosing try block.
int8_t needs_stacktrace; // True if a stacktrace is needed.
int8_t has_catch_all; // Catches all exceptions.
int8_t is_generated; // True if this is a generated handler.
};
} // namespace dart
+1
View File
@@ -46,6 +46,7 @@ FlowGraph::FlowGraph(const ParsedFunction& parsed_function,
loop_headers_(NULL),
loop_invariant_loads_(NULL),
deferred_prefixes_(parsed_function.deferred_prefixes()),
await_token_positions_(NULL),
captured_parameters_(new (zone()) BitVector(zone(), variable_count())),
inlining_id_(-1) {
DiscoverBlocks();
+10
View File
@@ -286,6 +286,15 @@ class FlowGraph : public ZoneAllocated {
// Merge instructions (only per basic-block).
void TryOptimizePatterns();
ZoneGrowableArray<TokenPosition>* await_token_positions() const {
return await_token_positions_;
}
void set_await_token_positions(
ZoneGrowableArray<TokenPosition>* await_token_positions) {
await_token_positions_ = await_token_positions;
}
// Replaces uses that are dominated by dom of 'def' with 'other'.
// Note: uses that occur at instruction dom itself are not dominated by it.
static void RenameDominatedUses(Definition* def,
@@ -391,6 +400,7 @@ class FlowGraph : public ZoneAllocated {
ZoneGrowableArray<BlockEntryInstr*>* loop_headers_;
ZoneGrowableArray<BitVector*>* loop_invariant_loads_;
ZoneGrowableArray<const LibraryPrefix*>* deferred_prefixes_;
ZoneGrowableArray<TokenPosition>* await_token_positions_;
DirectChainedHashMap<ConstantPoolTrait> constant_instr_pool_;
BitVector* captured_parameters_;
+13 -1
View File
@@ -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) {
@@ -2186,6 +2187,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());
}
@@ -4078,6 +4081,7 @@ void EffectGraphVisitor::VisitTryCatchNode(TryCatchNode* node) {
ASSERT(!catch_block->stacktrace_var().is_captured());
CatchBlockEntryInstr* catch_entry = new (Z) CatchBlockEntryInstr(
catch_block->token_pos(), (node->token_pos() == TokenPosition::kNoSource),
owner()->AllocateBlockId(), catch_handler_index, owner()->graph_entry(),
catch_block->handler_types(), try_handler_index,
catch_block->exception_var(), catch_block->stacktrace_var(),
@@ -4120,6 +4124,8 @@ void EffectGraphVisitor::VisitTryCatchNode(TryCatchNode* node) {
const Array& types = Array::ZoneHandle(Z, Array::New(1, Heap::kOld));
types.SetAt(0, Object::dynamic_type());
CatchBlockEntryInstr* finally_entry = new (Z) CatchBlockEntryInstr(
finally_block->token_pos(),
true, // this is not a catch block from user code.
owner()->AllocateBlockId(), original_handler_index,
owner()->graph_entry(), types, catch_handler_index,
catch_block->exception_var(), catch_block->stacktrace_var(),
@@ -4367,10 +4373,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);
+7
View File
@@ -172,6 +172,12 @@ class FlowGraphBuilder : public ValueObject {
Isolate* isolate() const { return parsed_function().isolate(); }
Zone* zone() const { return parsed_function().zone(); }
void AppendAwaitTokenPosition(TokenPosition token_pos);
ZoneGrowableArray<TokenPosition>* await_token_positions() const {
return await_token_positions_;
}
private:
friend class NestedStatement; // Explicit access to nesting_stack_.
friend class Intrinsifier;
@@ -212,6 +218,7 @@ class FlowGraphBuilder : public ValueObject {
intptr_t jump_count_;
ZoneGrowableArray<JoinEntryInstr*>* await_joins_;
ZoneGrowableArray<TokenPosition>* await_token_positions_;
DISALLOW_IMPLICIT_CONSTRUCTORS(FlowGraphBuilder);
};
+4 -1
View File
@@ -671,10 +671,13 @@ void FlowGraphCompiler::GenerateDeferredCode() {
void FlowGraphCompiler::AddExceptionHandler(intptr_t try_index,
intptr_t outer_try_index,
intptr_t pc_offset,
TokenPosition token_pos,
bool is_generated,
const Array& handler_types,
bool needs_stacktrace) {
exception_handlers_list_->AddHandler(try_index, outer_try_index, pc_offset,
handler_types, needs_stacktrace);
token_pos, is_generated, handler_types,
needs_stacktrace);
}
+2
View File
@@ -491,6 +491,8 @@ class FlowGraphCompiler : public ValueObject {
void AddExceptionHandler(intptr_t try_index,
intptr_t outer_try_index,
intptr_t pc_offset,
TokenPosition token_pos,
bool is_generated,
const Array& handler_types,
bool needs_stacktrace);
void SetNeedsStackTrace(intptr_t try_index);
+11 -2
View File
@@ -1469,7 +1469,9 @@ class IndirectEntryInstr : public JoinEntryInstr {
class CatchBlockEntryInstr : public BlockEntryInstr {
public:
CatchBlockEntryInstr(intptr_t block_id,
CatchBlockEntryInstr(TokenPosition handler_token_pos,
bool is_generated,
intptr_t block_id,
intptr_t try_index,
GraphEntryInstr* graph_entry,
const Array& handler_types,
@@ -1487,7 +1489,9 @@ class CatchBlockEntryInstr : public BlockEntryInstr {
exception_var_(exception_var),
stacktrace_var_(stacktrace_var),
needs_stacktrace_(needs_stacktrace),
should_restore_closure_context_(should_restore_closure_context) {
should_restore_closure_context_(should_restore_closure_context),
handler_token_pos_(handler_token_pos),
is_generated_(is_generated) {
deopt_id_ = deopt_id;
}
@@ -1508,6 +1512,9 @@ class CatchBlockEntryInstr : public BlockEntryInstr {
bool needs_stacktrace() const { return needs_stacktrace_; }
bool is_generated() const { return is_generated_; }
TokenPosition handler_token_pos() const { return handler_token_pos_; }
// Returns try index for the try block to which this catch handler
// corresponds.
intptr_t catch_try_index() const { return catch_try_index_; }
@@ -1541,6 +1548,8 @@ class CatchBlockEntryInstr : public BlockEntryInstr {
const LocalVariable& stacktrace_var_;
const bool needs_stacktrace_;
const bool should_restore_closure_context_;
TokenPosition handler_token_pos_;
bool is_generated_;
DISALLOW_COPY_AND_ASSIGN(CatchBlockEntryInstr);
};
+1
View File
@@ -2874,6 +2874,7 @@ void CatchBlockEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
__ Bind(compiler->GetJumpLabel(this));
compiler->AddExceptionHandler(catch_try_index(), try_index(),
compiler->assembler()->CodeSize(),
handler_token_pos(), is_generated(),
catch_handler_types_, needs_stacktrace());
// On lazy deoptimization we patch the optimized code here to enter the
// deoptimization stub.
@@ -2594,6 +2594,7 @@ void CatchBlockEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
__ Bind(compiler->GetJumpLabel(this));
compiler->AddExceptionHandler(catch_try_index(), try_index(),
compiler->assembler()->CodeSize(),
handler_token_pos(), is_generated(),
catch_handler_types_, needs_stacktrace());
// On lazy deoptimization we patch the optimized code here to enter the
// deoptimization stub.
+1
View File
@@ -1149,6 +1149,7 @@ EMIT_NATIVE_CODE(CatchBlockEntry, 0) {
__ Bind(compiler->GetJumpLabel(this));
compiler->AddExceptionHandler(catch_try_index(), try_index(),
compiler->assembler()->CodeSize(),
handler_token_pos(), is_generated(),
catch_handler_types_, needs_stacktrace());
// On lazy deoptimization we patch the optimized code here to enter the
// deoptimization stub.
+1
View File
@@ -2515,6 +2515,7 @@ void CatchBlockEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
__ Bind(compiler->GetJumpLabel(this));
compiler->AddExceptionHandler(catch_try_index(), try_index(),
compiler->assembler()->CodeSize(),
handler_token_pos(), is_generated(),
catch_handler_types_, needs_stacktrace());
// On lazy deoptimization we patch the optimized code here to enter the
// deoptimization stub.
+1
View File
@@ -2731,6 +2731,7 @@ void CatchBlockEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
__ Bind(compiler->GetJumpLabel(this));
compiler->AddExceptionHandler(catch_try_index(), try_index(),
compiler->assembler()->CodeSize(),
handler_token_pos(), is_generated(),
catch_handler_types_, needs_stacktrace());
// On lazy deoptimization we patch the optimized code here to enter the
// deoptimization stub.
+1
View File
@@ -2546,6 +2546,7 @@ void CatchBlockEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
__ Bind(compiler->GetJumpLabel(this));
compiler->AddExceptionHandler(catch_try_index(), try_index(),
compiler->assembler()->CodeSize(),
handler_token_pos(), is_generated(),
catch_handler_types_, needs_stacktrace());
// On lazy deoptimization we patch the optimized code here to enter the
// deoptimization stub.
+2
View File
@@ -2277,6 +2277,8 @@ Fragment FlowGraphBuilder::CatchBlockEntry(const Array& handler_types,
const bool should_restore_closure_context =
CurrentException()->is_captured() || CurrentCatchContext()->is_captured();
CatchBlockEntryInstr* entry = new (Z) CatchBlockEntryInstr(
TokenPosition::kNoSource, // Token position of catch block.
false, // Not an artifact of compilation.
AllocateBlockId(), CurrentTryIndex(), graph_entry_, handler_types,
handler_index, *CurrentException(), *CurrentStackTrace(),
/* needs_stacktrace = */ true, H.thread()->GetNextDeoptId(),
+15 -4
View File
@@ -12493,7 +12493,9 @@ void ExceptionHandlers::SetHandlerInfo(intptr_t try_index,
intptr_t outer_try_index,
uword handler_pc_offset,
bool needs_stacktrace,
bool has_catch_all) const {
bool has_catch_all,
TokenPosition token_pos,
bool is_generated) const {
ASSERT((try_index >= 0) && (try_index < num_entries()));
NoSafepointScope no_safepoint;
ExceptionHandlerInfo* info =
@@ -12506,6 +12508,7 @@ void ExceptionHandlers::SetHandlerInfo(intptr_t try_index,
info->handler_pc_offset = handler_pc_offset;
info->needs_stacktrace = needs_stacktrace;
info->has_catch_all = has_catch_all;
info->is_generated = is_generated;
}
void ExceptionHandlers::GetHandlerInfo(intptr_t try_index,
@@ -12534,6 +12537,12 @@ bool ExceptionHandlers::NeedsStackTrace(intptr_t try_index) const {
}
bool ExceptionHandlers::IsGenerated(intptr_t try_index) const {
ASSERT((try_index >= 0) && (try_index < num_entries()));
return raw_ptr()->data()[try_index].is_generated;
}
bool ExceptionHandlers::HasCatchAll(intptr_t try_index) const {
ASSERT((try_index >= 0) && (try_index < num_entries()));
return raw_ptr()->data()[try_index].has_catch_all;
@@ -12612,7 +12621,7 @@ RawExceptionHandlers* ExceptionHandlers::New(const Array& handled_types_data) {
const char* ExceptionHandlers::ToCString() const {
#define FORMAT1 "%" Pd " => %#x (%" Pd " types) (outer %d)\n"
#define FORMAT1 "%" Pd " => %#x (%" Pd " types) (outer %d) %s\n"
#define FORMAT2 " %d. %s\n"
if (num_entries() == 0) {
return "empty ExceptionHandlers\n";
@@ -12628,7 +12637,8 @@ const char* ExceptionHandlers::ToCString() const {
const intptr_t num_types =
handled_types.IsNull() ? 0 : handled_types.Length();
len += OS::SNPrint(NULL, 0, FORMAT1, i, info.handler_pc_offset, num_types,
info.outer_try_index);
info.outer_try_index,
info.is_generated ? "(generated)" : "");
for (int k = 0; k < num_types; k++) {
type ^= handled_types.At(k);
ASSERT(!type.IsNull());
@@ -12646,7 +12656,8 @@ const char* ExceptionHandlers::ToCString() const {
handled_types.IsNull() ? 0 : handled_types.Length();
num_chars +=
OS::SNPrint((buffer + num_chars), (len - num_chars), FORMAT1, i,
info.handler_pc_offset, num_types, info.outer_try_index);
info.handler_pc_offset, num_types, info.outer_try_index,
info.is_generated ? "(generated)" : "");
for (int k = 0; k < num_types; k++) {
type ^= handled_types.At(k);
num_chars += OS::SNPrint((buffer + num_chars), (len - num_chars), FORMAT2,
+14 -1
View File
@@ -4513,12 +4513,15 @@ class ExceptionHandlers : public Object {
uword HandlerPCOffset(intptr_t try_index) const;
intptr_t OuterTryIndex(intptr_t try_index) const;
bool NeedsStackTrace(intptr_t try_index) const;
bool IsGenerated(intptr_t try_index) const;
void SetHandlerInfo(intptr_t try_index,
intptr_t outer_try_index,
uword handler_pc_offset,
bool needs_stacktrace,
bool has_catch_all) const;
bool has_catch_all,
TokenPosition token_pos,
bool is_generated) const;
RawArray* GetHandledTypes(intptr_t try_index) const;
void SetHandledTypes(intptr_t try_index, const Array& handled_types) const;
@@ -4682,6 +4685,16 @@ class Code : public Object {
StorePointer(&raw_ptr()->code_source_map_, code_source_map.raw());
}
RawArray* await_token_positions() const {
#if defined(DART_PRECOMPILED_RUNTIME)
return Array::null();
#else
return raw_ptr()->await_token_positions_;
#endif
}
void SetAwaitTokenPositions(const Array& await_token_positions) const;
// Used during reloading (see object_reload.cc). Calls Reset on all ICDatas
// that are embedded inside the Code object.
void ResetICDatas(Zone* zone) const;
+5
View File
@@ -869,6 +869,11 @@ void Code::PrintJSONImpl(JSONStream* stream, bool ref) const {
}
void Code::SetAwaitTokenPositions(const Array& await_token_positions) const {
StorePointer(&raw_ptr()->await_token_positions_, await_token_positions.raw());
}
void Context::PrintJSONImpl(JSONStream* stream, bool ref) const {
JSONObject jsobj(stream);
// TODO(turnidge): Should the user level type for Context be Context
+8 -4
View File
@@ -2799,10 +2799,14 @@ ISOLATE_UNIT_TEST_CASE(ExceptionHandlers) {
exception_handlers ^= ExceptionHandlers::New(kNumEntries);
const bool kNeedsStackTrace = true;
const bool kNoStackTrace = false;
exception_handlers.SetHandlerInfo(0, -1, 20u, kNeedsStackTrace, false);
exception_handlers.SetHandlerInfo(1, 0, 30u, kNeedsStackTrace, false);
exception_handlers.SetHandlerInfo(2, -1, 40u, kNoStackTrace, true);
exception_handlers.SetHandlerInfo(3, 1, 150u, kNoStackTrace, true);
exception_handlers.SetHandlerInfo(0, -1, 20u, kNeedsStackTrace, false,
TokenPosition::kNoSource, true);
exception_handlers.SetHandlerInfo(1, 0, 30u, kNeedsStackTrace, false,
TokenPosition::kNoSource, true);
exception_handlers.SetHandlerInfo(2, -1, 40u, kNoStackTrace, true,
TokenPosition::kNoSource, true);
exception_handlers.SetHandlerInfo(3, 1, 150u, kNoStackTrace, true,
TokenPosition::kNoSource, true);
extern void GenerateIncrement(Assembler * assembler);
Assembler _assembler_;
+63 -6
View File
@@ -6817,6 +6817,8 @@ void Parser::OpenAsyncTryBlock() {
// This is the outermost try-catch in the function.
ASSERT(try_stack_ == NULL);
PushTry(current_block_);
// Validate that we always get try index of 0.
ASSERT(try_stack_->try_index() == CatchClauseNode::kImplicitAsyncTryIndex);
SetupSavedTryContext(context_var);
}
@@ -7086,6 +7088,7 @@ void Parser::AddAsyncGeneratorVariables() {
// var :async_then_callback;
// var :async_catch_error_callback;
// var :async_stack_trace;
// var :controller_stream;
// These variables are used to store the async generator closure containing
// the body of the async* function. They are used by the await operator.
LocalVariable* controller_var =
@@ -7108,6 +7111,10 @@ void Parser::AddAsyncGeneratorVariables() {
LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::AsyncStackTraceVar(), Object::dynamic_type());
current_block_->scope->AddVariable(async_stack_trace);
LocalVariable* controller_stream = new (Z)
LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
Symbols::ControllerStream(), Object::dynamic_type());
current_block_->scope->AddVariable(controller_stream);
}
@@ -7177,7 +7184,8 @@ RawFunction* Parser::OpenAsyncGeneratorFunction(TokenPosition async_func_pos) {
// var :async_then_callback = _asyncThenWrapperHelper(:async_op);
// var :async_catch_error_callback = _asyncCatchErrorWrapperHelper(:async_op);
// :controller = new _AsyncStarStreamController(:async_op);
// return :controller.stream;
// var :controller_stream = :controller.stream;
// return :controller_stream;
// }
SequenceNode* Parser::CloseAsyncGeneratorFunction(const Function& closure_func,
SequenceNode* closure_body) {
@@ -7208,6 +7216,9 @@ SequenceNode* Parser::CloseAsyncGeneratorFunction(const Function& closure_func,
existing_var = closure_body->scope()->LookupVariable(
Symbols::AsyncStackTraceVar(), false);
ASSERT((existing_var != NULL) && existing_var->is_captured());
existing_var =
closure_body->scope()->LookupVariable(Symbols::ControllerStream(), false);
ASSERT((existing_var != NULL) && existing_var->is_captured());
const Library& async_lib = Library::Handle(Library::AsyncLibrary());
@@ -7316,13 +7327,28 @@ SequenceNode* Parser::CloseAsyncGeneratorFunction(const Function& closure_func,
TokenPosition::kNoSource, controller_var, controller_constructor_call);
current_block_->statements->Add(store_controller);
// Grab :controller.stream
InstanceGetterNode* controller_stream = new (Z) InstanceGetterNode(
TokenPosition::kNoSource,
new (Z) LoadLocalNode(TokenPosition::kNoSource, controller_var),
Symbols::Stream());
// Store :controller.stream into :controller_stream inside the closure.
// We have to remember the stream because a new instance is generated for
// each getter invocation and in order to recreate the linkage, we need the
// awaited on instance.
LocalVariable* controller_stream_var =
current_block_->scope->LookupVariable(Symbols::ControllerStream(), false);
ASSERT(controller_stream_var != NULL);
StoreLocalNode* store_controller_stream = new (Z) StoreLocalNode(
TokenPosition::kNoSource, controller_stream_var, controller_stream);
current_block_->statements->Add(store_controller_stream);
// return :controller.stream;
ReturnNode* return_node = new (Z) ReturnNode(
TokenPosition::kNoSource,
new (Z) InstanceGetterNode(
TokenPosition::kNoSource,
new (Z) LoadLocalNode(TokenPosition::kNoSource, controller_var),
Symbols::Stream()));
new (Z) LoadLocalNode(TokenPosition::kNoSource, controller_stream_var));
current_block_->statements->Add(return_node);
return CloseBlock();
}
@@ -9066,6 +9092,37 @@ AstNode* Parser::ParseAwaitForStatement(String* label_name) {
ParseAwaitableExpr(kAllowConst, kConsumeCascades, NULL);
ExpectToken(Token::kRPAREN);
// Create :stream to store the stream into temporarily.
LocalVariable* stream_var =
new (Z) LocalVariable(stream_expr_pos, stream_expr_pos,
Symbols::ColonStream(), Object::dynamic_type());
current_block_->scope->AddVariable(stream_var);
// Store the stream expression into a variable.
StoreLocalNode* store_stream_var =
new (Z) StoreLocalNode(stream_expr_pos, stream_var, stream_expr);
current_block_->statements->Add(store_stream_var);
// Register the awaiter on the stream by invoking `_asyncStarListenHelper`.
const Library& async_lib = Library::Handle(Library::AsyncLibrary());
const Function& async_star_listen_helper = Function::ZoneHandle(
Z,
async_lib.LookupFunctionAllowPrivate(Symbols::_AsyncStarListenHelper()));
ASSERT(!async_star_listen_helper.IsNull());
LocalVariable* async_op_var =
current_block_->scope->LookupVariable(Symbols::AsyncOperation(), false);
ASSERT(async_op_var != NULL);
ArgumentListNode* async_star_listen_helper_args =
new (Z) ArgumentListNode(stream_expr_pos);
async_star_listen_helper_args->Add(
new (Z) LoadLocalNode(stream_expr_pos, stream_var));
async_star_listen_helper_args->Add(
new (Z) LoadLocalNode(stream_expr_pos, async_op_var));
StaticCallNode* async_star_listen_helper_call = new (Z) StaticCallNode(
stream_expr_pos, async_star_listen_helper, async_star_listen_helper_args);
current_block_->statements->Add(async_star_listen_helper_call);
// Build creation of implicit StreamIterator.
// var :for-in-iter = new StreamIterator(stream_expr).
const Class& stream_iterator_cls =
@@ -9076,7 +9133,7 @@ AstNode* Parser::ParseAwaitForStatement(String* label_name) {
stream_iterator_cls.LookupFunction(Symbols::StreamIteratorConstructor()));
ASSERT(!iterator_ctor.IsNull());
ArgumentListNode* ctor_args = new (Z) ArgumentListNode(stream_expr_pos);
ctor_args->Add(stream_expr);
ctor_args->Add(new (Z) LoadLocalNode(stream_expr_pos, stream_var));
ConstructorCallNode* ctor_call = new (Z) ConstructorCallNode(
stream_expr_pos, TypeArguments::ZoneHandle(Z), iterator_ctor, ctor_args);
const AbstractType& iterator_type = Object::dynamic_type();
+1
View File
@@ -1141,6 +1141,7 @@ class RawCode : public RawObject {
RawArray* stackmaps_;
RawArray* inlined_id_to_function_;
RawCodeSourceMap* code_source_map_;
NOT_IN_PRECOMPILED(RawArray* await_token_positions_);
NOT_IN_PRECOMPILED(RawInstructions* active_instructions_);
NOT_IN_PRECOMPILED(RawArray* deopt_info_array_);
// (code-offset, function, code) triples.
+12
View File
@@ -264,6 +264,18 @@ static bool IsFilteredIdentifier(const String& str) {
// Keep :async_op for asynchronous debugging.
return false;
}
if (str.raw() == Symbols::AsyncCompleter().raw()) {
// Keep :async_completer for asynchronous debugging.
return false;
}
if (str.raw() == Symbols::ControllerStream().raw()) {
// Keep :controller_stream for asynchronous debugging.
return false;
}
if (str.raw() == Symbols::AwaitJumpVar().raw()) {
// Keep :await_jump_var for asynchronous debugging.
return false;
}
return str.CharAt(0) == ':';
}
+7 -1
View File
@@ -47,7 +47,10 @@ class ObjectPointerVisitor;
V(_AsyncStarStreamController, "_AsyncStarStreamController") \
V(_AsyncStarStreamControllerConstructor, "_AsyncStarStreamController.") \
V(Controller, ":controller") \
V(ControllerStream, ":controller_stream") \
V(Controller2, "controller") \
V(Stream, "stream") \
V(_StreamImpl, "_StreamImpl") \
V(isPaused, "isPaused") \
V(AddError, "addError") \
V(AddStream, "addStream") \
@@ -127,6 +130,7 @@ class ObjectPointerVisitor;
V(AsyncStackTraceHelper, "_asyncStackTraceHelper") \
V(AsyncAwaitHelper, "_awaitHelper") \
V(Await, "await") \
V(_Awaiter, "_awaiter") \
V(AwaitTempVarPrefix, ":await_temp_var_") \
V(AwaitContextVar, ":await_ctx_var") \
V(AwaitJumpVar, ":await_jump_var") \
@@ -218,6 +222,7 @@ class ObjectPointerVisitor;
V(_RegExp, "_RegExp") \
V(RegExp, "RegExp") \
V(ColonMatcher, ":matcher") \
V(ColonStream, ":stream") \
V(Object, "Object") \
V(Int, "int") \
V(Double, "double") \
@@ -429,7 +434,8 @@ class ObjectPointerVisitor;
V(_classRangeCheckNegative, "_classRangeCheckNegative") \
V(GetRuntimeType, "get:runtimeType") \
V(HaveSameRuntimeType, "_haveSameRuntimeType") \
V(DartDeveloperCausalAsyncStacks, "dart.developer.causal_async_stacks")
V(DartDeveloperCausalAsyncStacks, "dart.developer.causal_async_stacks") \
V(_AsyncStarListenHelper, "_asyncStarListenHelper")
// Contains a list of frequently used strings in a canonicalized form. This