diff --git a/runtime/observatory/tests/service/notify_debugger_on_exception_yielding_test.dart b/runtime/observatory/tests/service/notify_debugger_on_exception_yielding_test.dart new file mode 100644 index 00000000000..1cab6a7d938 --- /dev/null +++ b/runtime/observatory/tests/service/notify_debugger_on_exception_yielding_test.dart @@ -0,0 +1,64 @@ +// Copyright (c) 2021, 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=--verbose_debug + +// See: https://github.com/dart-lang/sdk/issues/45673 + +import 'test_helper.dart'; +import 'service_test_common.dart'; + +const int LINE_A = 19; +const int LINE_B = 29; +const int LINE_C = 39; + +@pragma('vm:notify-debugger-on-exception') +Future throwFromAsync() async { + try { + throw 'Throw from throwFromAsync'; // LINE_A + } catch (e) { + // Ignore. Internals will notify debugger. + } + return Future.value(); +} + +@pragma('vm:notify-debugger-on-exception') +Stream throwFromAsyncStar() async* { + try { + throw 'Throw from throwFromAsyncStar'; // LINE_B + } catch (e) { + // Ignore. Internals will notify debugger. + } + yield 13; +} + +@pragma('vm:notify-debugger-on-exception') +Iterable throwFromSyncStar() sync* { + try { + throw 'Throw from throwFromSyncStar'; // LINE_C + } catch (e) { + // Ignore. Internals will notify debugger. + } + yield 7; +} + +testMain() async { + throwFromAsync(); + await for (var e in throwFromAsyncStar()) {/*ignore*/} + for (var e in throwFromSyncStar()) {/*ignore*/} +} + +final tests = [ + hasStoppedWithUnhandledException, + stoppedAtLine(LINE_A), + resumeIsolate, + hasStoppedWithUnhandledException, + stoppedAtLine(LINE_B), + resumeIsolate, + hasStoppedWithUnhandledException, + stoppedAtLine(LINE_C), +]; + +main([args = const []]) => runIsolateTests(args, tests, + testeeConcurrent: testMain, pause_on_unhandled_exceptions: true); diff --git a/runtime/observatory_2/tests/service_2/notify_debugger_on_exception_yielding_test.dart b/runtime/observatory_2/tests/service_2/notify_debugger_on_exception_yielding_test.dart new file mode 100644 index 00000000000..1cab6a7d938 --- /dev/null +++ b/runtime/observatory_2/tests/service_2/notify_debugger_on_exception_yielding_test.dart @@ -0,0 +1,64 @@ +// Copyright (c) 2021, 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=--verbose_debug + +// See: https://github.com/dart-lang/sdk/issues/45673 + +import 'test_helper.dart'; +import 'service_test_common.dart'; + +const int LINE_A = 19; +const int LINE_B = 29; +const int LINE_C = 39; + +@pragma('vm:notify-debugger-on-exception') +Future throwFromAsync() async { + try { + throw 'Throw from throwFromAsync'; // LINE_A + } catch (e) { + // Ignore. Internals will notify debugger. + } + return Future.value(); +} + +@pragma('vm:notify-debugger-on-exception') +Stream throwFromAsyncStar() async* { + try { + throw 'Throw from throwFromAsyncStar'; // LINE_B + } catch (e) { + // Ignore. Internals will notify debugger. + } + yield 13; +} + +@pragma('vm:notify-debugger-on-exception') +Iterable throwFromSyncStar() sync* { + try { + throw 'Throw from throwFromSyncStar'; // LINE_C + } catch (e) { + // Ignore. Internals will notify debugger. + } + yield 7; +} + +testMain() async { + throwFromAsync(); + await for (var e in throwFromAsyncStar()) {/*ignore*/} + for (var e in throwFromSyncStar()) {/*ignore*/} +} + +final tests = [ + hasStoppedWithUnhandledException, + stoppedAtLine(LINE_A), + resumeIsolate, + hasStoppedWithUnhandledException, + stoppedAtLine(LINE_B), + resumeIsolate, + hasStoppedWithUnhandledException, + stoppedAtLine(LINE_C), +]; + +main([args = const []]) => runIsolateTests(args, tests, + testeeConcurrent: testMain, pause_on_unhandled_exceptions: true); diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc index dc9d048b6f8..0f18a4756f4 100644 --- a/runtime/vm/debugger.cc +++ b/runtime/vm/debugger.cc @@ -2248,11 +2248,27 @@ bool Debugger::ShouldPauseOnException(DebuggerStackTrace* stack_trace, // it will be caught once we unwind the stack. return true; } + + auto& handler_function = Function::Handle(handler_frame->function().ptr()); + // If the handler function is an synthetic inner function, we need to look for + // the annotations on the outer function. + if (handler_function.IsAsyncClosure()) { + // async :async_op + handler_function = handler_function.parent_function(); + } else if (handler_frame->function().IsAsyncGenClosure()) { + // async* :async_op + handler_function = handler_function.parent_function(); + } else if (handler_frame->function().IsSyncGenClosure()) { + // sync* :sync_op + :sync_op_gen + handler_function = handler_function.parent_function(); + handler_function = handler_function.parent_function(); + } + // If handler_frame's function is annotated with // @pragma('vm:notify-debugger-on-exception'), we specifically want to notify // the debugger of this otherwise ignored exception. if (Library::FindPragma(Thread::Current(), /*only_core=*/false, - handler_frame->function(), + handler_function, Symbols::vm_notify_debugger_on_exception())) { return true; }