Version 2.12.0-202.0.dev
Merge commit '800ec511f52861cdaf5e8c5d40739a253e63880a' into 'dev'
diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json
index 5b08de7..9b2a9f2 100644
--- a/.dart_tool/package_config.json
+++ b/.dart_tool/package_config.json
@@ -396,7 +396,7 @@
"name": "matcher",
"rootUri": "../third_party/pkg/matcher",
"packageUri": "lib/",
- "languageVersion": "2.10"
+ "languageVersion": "2.12"
},
{
"name": "meta",
diff --git a/DEPS b/DEPS
index b61db7e..b286e0e 100644
--- a/DEPS
+++ b/DEPS
@@ -122,7 +122,7 @@
"logging_rev": "e2f633b543ef89c54688554b15ca3d7e425b86a2",
"markupsafe_rev": "8f45f5cfa0009d2a70589bcda0349b8cb2b72783",
"markdown_rev": "6f89681d59541ddb1cf3a58efbdaa2304ffc3f51",
- "matcher_rev": "9cae8faa7868bf3a88a7ba45eb0bd128e66ac515",
+ "matcher_rev": "63f1110a657fb5e2dc378db8895fef7b0a13ab30",
"mime_rev": "c931f4bed87221beaece356494b43731445ce7b8",
"mockito_rev": "d39ac507483b9891165e422ec98d9fb480037c8b",
"mustache_rev": "664737ecad027e6b96d0d1e627257efa0e46fcb1",
diff --git a/pkg/dds/CHANGELOG.md b/pkg/dds/CHANGELOG.md
index aebaf89..c657089 100644
--- a/pkg/dds/CHANGELOG.md
+++ b/pkg/dds/CHANGELOG.md
@@ -1,6 +1,9 @@
# 1.7.0
- Added `package:dds/vm_service_extensions.dart`, which adds DDS functionality to
`package:vm_service` when imported.
+ - Added `onEventWithHistory` method and `onLoggingEventWithHistory`,
+ `onStdoutEventWithHistory`, `onStderrEventWithHistory`, and
+ `onExtensionEventWithHistory` getters.
- Added `getStreamHistory` RPC.
# 1.6.1
diff --git a/pkg/dds/lib/src/stream_manager.dart b/pkg/dds/lib/src/stream_manager.dart
index e7b0f22..28c7f28 100644
--- a/pkg/dds/lib/src/stream_manager.dart
+++ b/pkg/dds/lib/src/stream_manager.dart
@@ -181,7 +181,7 @@
return null;
}
return [
- for (final event in loggingRepositories[stream]()) event,
+ for (final event in loggingRepositories[stream]()) event['event'],
];
}
diff --git a/pkg/dds/lib/vm_service_extensions.dart b/pkg/dds/lib/vm_service_extensions.dart
index 069c737..5b540da 100644
--- a/pkg/dds/lib/vm_service_extensions.dart
+++ b/pkg/dds/lib/vm_service_extensions.dart
@@ -2,9 +2,12 @@
// 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 'dart:collection';
+import 'package:async/async.dart';
import 'package:meta/meta.dart';
+import 'package:pedantic/pedantic.dart';
import 'package:vm_service/src/vm_service.dart';
extension DdsExtension on VmService {
@@ -36,6 +39,70 @@
});
}
+ /// Returns the stream for a given stream id which includes historical
+ /// events.
+ ///
+ /// If `stream` does not have event history collected, a parameter error is
+ /// sent over the returned [Stream].
+ Stream<Event> onEventWithHistory(String stream) {
+ StreamController<Event> controller;
+ StreamQueue<Event> streamEvents;
+
+ controller = StreamController<Event>(onListen: () async {
+ streamEvents = StreamQueue<Event>(onEvent(stream));
+ final history = (await getStreamHistory(stream)).history;
+ Event firstStreamEvent;
+ unawaited(streamEvents.peek.then((e) {
+ firstStreamEvent = e;
+ }));
+ for (final event in history) {
+ if (firstStreamEvent != null &&
+ event.timestamp > firstStreamEvent.timestamp) {
+ break;
+ }
+ controller.sink.add(event);
+ }
+ unawaited(controller.sink.addStream(streamEvents.rest));
+ }, onCancel: () {
+ streamEvents.cancel();
+ });
+
+ return controller.stream;
+ }
+
+ /// Returns a new [Stream<Event>] of `Logging` events which outputs
+ /// historical events before streaming real-time events.
+ ///
+ /// Note: unlike [onLoggingEvent], the returned stream is a single
+ /// subscription stream and a new stream is created for each invocation of
+ /// this getter.
+ Stream<Event> get onLoggingEventWithHistory => onEventWithHistory('Logging');
+
+ /// Returns a new [Stream<Event>] of `Stdout` events which outputs
+ /// historical events before streaming real-time events.
+ ///
+ /// Note: unlike [onStdoutEvent], the returned stream is a single
+ /// subscription stream and a new stream is created for each invocation of
+ /// this getter.
+ Stream<Event> get onStdoutEventWithHistory => onEventWithHistory('Stdout');
+
+ /// Returns a new [Stream<Event>] of `Stderr` events which outputs
+ /// historical events before streaming real-time events.
+ ///
+ /// Note: unlike [onStderrEvent], the returned stream is a single
+ /// subscription stream and a new stream is created for each invocation of
+ /// this getter.
+ Stream<Event> get onStderrEventWithHistory => onEventWithHistory('Stderr');
+
+ /// Returns a new [Stream<Event>] of `Extension` events which outputs
+ /// historical events before streaming real-time events.
+ ///
+ /// Note: unlike [onExtensionEvent], the returned stream is a single
+ /// subscription stream and a new stream is created for each invocation of
+ /// this getter.
+ Stream<Event> get onExtensionEventWithHistory =>
+ onEventWithHistory('Extension');
+
Future<bool> _versionCheck(int major, int minor) async {
if (_ddsVersion == null) {
_ddsVersion = await getDartDevelopmentServiceVersion();
@@ -72,10 +139,14 @@
StreamHistory({@required List<Event> history}) : _history = history;
StreamHistory._fromJson(Map<String, dynamic> json)
- : _history = List<Event>.from(
- createServiceObject(json['history'], const ['Event']) as List ??
- []) {
+ : _history = json['history']
+ .map(
+ (e) => Event.parse(e),
+ )
+ .toList()
+ .cast<Event>() {
type = json['type'];
+ this.json = json;
}
/// Historical [Event]s for a stream.
diff --git a/pkg/dds/test/on_event_with_history_script.dart b/pkg/dds/test/on_event_with_history_script.dart
new file mode 100644
index 0000000..3f27df0
--- /dev/null
+++ b/pkg/dds/test/on_event_with_history_script.dart
@@ -0,0 +1,12 @@
+import 'dart:developer';
+
+void main() {
+ for (int i = 1; i <= 10; ++i) {
+ log(i.toString());
+ }
+ debugger();
+ for (int i = 11; i <= 20; ++i) {
+ log(i.toString());
+ }
+ debugger();
+}
diff --git a/pkg/dds/test/on_event_with_history_test.dart b/pkg/dds/test/on_event_with_history_test.dart
new file mode 100644
index 0000000..55e04b9
--- /dev/null
+++ b/pkg/dds/test/on_event_with_history_test.dart
@@ -0,0 +1,59 @@
+// 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.
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:dds/dds.dart';
+import 'package:dds/vm_service_extensions.dart';
+import 'package:test/test.dart';
+import 'package:vm_service/vm_service_io.dart';
+import 'common/test_helper.dart';
+
+void main() {
+ Process process;
+ DartDevelopmentService dds;
+
+ setUp(() async {
+ process = await spawnDartProcess('on_event_with_history_script.dart',
+ pauseOnStart: false);
+ });
+
+ tearDown(() async {
+ await dds?.shutdown();
+ process?.kill();
+ dds = null;
+ process = null;
+ });
+
+ test('onEventWithHistory returns stream including log history', () async {
+ dds = await DartDevelopmentService.startDartDevelopmentService(
+ remoteVmServiceUri,
+ );
+ expect(dds.isRunning, true);
+ final service = await vmServiceConnectUri(dds.wsUri.toString());
+
+ await service.streamListen('Logging');
+ final stream = service.onLoggingEventWithHistory;
+
+ var completer = Completer<void>();
+ int count = 0;
+ stream.listen((event) {
+ count++;
+ expect(event.logRecord.message.valueAsString, count.toString());
+ if (count % 10 == 0) {
+ completer.complete();
+ }
+ });
+
+ await completer.future;
+
+ completer = Completer<void>();
+ final isolateId = (await service.getVM()).isolates.first.id;
+ await service.resume(isolateId);
+
+ await completer.future;
+ expect(count, 20);
+ });
+}
diff --git a/runtime/docs/pragmas.md b/runtime/docs/pragmas.md
index 3f7d23b..10affff 100644
--- a/runtime/docs/pragmas.md
+++ b/runtime/docs/pragmas.md
@@ -7,8 +7,12 @@
| Pragma | Meaning |
| --- | --- |
| `vm:entry-point` | [Defining entry-points into Dart code for an embedder or native methods](compiler/aot/entry_point_pragma.md) |
-| `vm:never-inline` | [Never inline a function or method](compiler/pragmas_recognized_by_compiler.md#requesting-a-function-never-be-inlined) |
-| `vm:prefer-inline` | [Inline a function or method when possible](compiler/pragmas_recognized_by_compiler.md#requesting-a-function-be-inlined) |
+| `vm:never-inline` | [Never inline a function or method](compiler/pragmas_recognized_by_compiler.md#requesting-a-function-never-be-inlined) |
+| `vm:prefer-inline` | [Inline a function or method when possible](compiler/pragmas_recognized_by_compiler.md#requesting-a-function-be-inlined) |
+| `vm:notify-debugger-on-exception` | Marks a function that catches exceptions,
+making the VM treat any caught exception as if they were uncaught.
+This can be used to notify an attached debugger during debugging, without
+pausing the app during regular execution. |
## Pragmas for internal use
diff --git a/runtime/observatory/tests/service/notify_debugger_on_exception_test.dart b/runtime/observatory/tests/service/notify_debugger_on_exception_test.dart
new file mode 100644
index 0000000..1d03341
--- /dev/null
+++ b/runtime/observatory/tests/service/notify_debugger_on_exception_test.dart
@@ -0,0 +1,37 @@
+// Copyright (c) 2020, 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/flutter/flutter/issues/17007
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE_A = 24;
+
+@pragma('vm:notify-debugger-on-exception')
+void catchNotifyDebugger(Function() code) {
+ try {
+ code();
+ } catch (e) {
+ // Ignore. Internals will notify debugger.
+ }
+}
+
+syncThrow() {
+ throw 'Hello from syncThrow!'; // Line A.
+}
+
+testMain() {
+ catchNotifyDebugger(syncThrow);
+}
+
+final tests = <IsolateTest>[
+ hasStoppedWithUnhandledException,
+ stoppedAtLine(LINE_A),
+];
+
+main([args = const <String>[]]) => runIsolateTests(args, tests,
+ testeeConcurrent: testMain, pause_on_unhandled_exceptions: true);
diff --git a/runtime/observatory/tests/service/service_kernel.status b/runtime/observatory/tests/service/service_kernel.status
index 3241e5f..aa7899e 100644
--- a/runtime/observatory/tests/service/service_kernel.status
+++ b/runtime/observatory/tests/service/service_kernel.status
@@ -153,6 +153,7 @@
next_through_simple_async_with_returns_test: SkipByDesign # Debugger is disabled in AOT mode.
next_through_simple_linear_2_test: SkipByDesign # Debugger is disabled in AOT mode.
next_through_simple_linear_test: SkipByDesign # Debugger is disabled in AOT mode.
+notify_debugger_on_exception_test: SkipByDesign # Debugger is disabled in AOT mode.
parameters_in_scope_at_entry_test: SkipByDesign # Debugger is disabled in AOT mode.
pause_idle_isolate_test: SkipByDesign # Debugger is disabled in AOT mode.
pause_on_exception_from_slow_path_test: SkipByDesign # Debugger is disabled in AOT mode.
diff --git a/runtime/observatory_2/tests/service_2/notify_debugger_on_exception_test.dart b/runtime/observatory_2/tests/service_2/notify_debugger_on_exception_test.dart
new file mode 100644
index 0000000..1d03341
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/notify_debugger_on_exception_test.dart
@@ -0,0 +1,37 @@
+// Copyright (c) 2020, 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/flutter/flutter/issues/17007
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE_A = 24;
+
+@pragma('vm:notify-debugger-on-exception')
+void catchNotifyDebugger(Function() code) {
+ try {
+ code();
+ } catch (e) {
+ // Ignore. Internals will notify debugger.
+ }
+}
+
+syncThrow() {
+ throw 'Hello from syncThrow!'; // Line A.
+}
+
+testMain() {
+ catchNotifyDebugger(syncThrow);
+}
+
+final tests = <IsolateTest>[
+ hasStoppedWithUnhandledException,
+ stoppedAtLine(LINE_A),
+];
+
+main([args = const <String>[]]) => runIsolateTests(args, tests,
+ testeeConcurrent: testMain, pause_on_unhandled_exceptions: true);
diff --git a/runtime/observatory_2/tests/service_2/service_2_kernel.status b/runtime/observatory_2/tests/service_2/service_2_kernel.status
index 0eca6fb..cad23e2 100644
--- a/runtime/observatory_2/tests/service_2/service_2_kernel.status
+++ b/runtime/observatory_2/tests/service_2/service_2_kernel.status
@@ -153,6 +153,7 @@
next_through_simple_async_with_returns_test: SkipByDesign # Debugger is disabled in AOT mode.
next_through_simple_linear_2_test: SkipByDesign # Debugger is disabled in AOT mode.
next_through_simple_linear_test: SkipByDesign # Debugger is disabled in AOT mode.
+notify_debugger_on_exception_test: SkipByDesign # Debugger is disabled in AOT mode.
parameters_in_scope_at_entry_test: SkipByDesign # Debugger is disabled in AOT mode.
pause_idle_isolate_test: SkipByDesign # Debugger is disabled in AOT mode.
pause_on_exceptions_test: SkipByDesign # Debugger is disabled in AOT mode.
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
index 5fa8d7f..323fd11 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -2172,7 +2172,7 @@
return false;
}
ActivationFrame* handler_frame = stack_trace->GetHandlerFrame(exception);
- if (handler_frame == NULL) {
+ if (handler_frame == nullptr) {
// Did not find an exception handler that catches this exception.
// Note that this check is not precise, since we can't check
// uninstantiated types, i.e. types containing type parameters.
@@ -2180,6 +2180,14 @@
// it will be caught once we unwind the stack.
return true;
}
+ // 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(),
+ Symbols::vm_notify_debugger_on_exception())) {
+ return true;
+ }
return false;
}
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 3d52bb5..fedaad2 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -3647,7 +3647,9 @@
pragma_name.raw()) {
continue;
}
- *options = Instance::Cast(pragma).GetField(pragma_options_field);
+ if (options != nullptr) {
+ *options = Instance::Cast(pragma).GetField(pragma_options_field);
+ }
return true;
}
@@ -10627,7 +10629,6 @@
#if !defined(DART_PRECOMPILED_RUNTIME)
if (is_static() && is_const()) {
- ASSERT(!FLAG_precompiled_mode);
return kernel::EvaluateStaticConstFieldInitializer(*this);
}
#endif // !defined(DART_PRECOMPILED_RUNTIME)
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 67df8c9..31f2632 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -4775,7 +4775,7 @@
bool only_core,
const Object& object,
const String& pragma_name,
- Object* options);
+ Object* options = nullptr);
ClassPtr toplevel_class() const { return raw_ptr()->toplevel_class(); }
void set_toplevel_class(const Class& value) const;
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index c84c323..63bcae3 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -494,6 +494,7 @@
V(vm_inferred_type_metadata, "vm.inferred-type.metadata") \
V(vm_never_inline, "vm:never-inline") \
V(vm_non_nullable_result_type, "vm:non-nullable-result-type") \
+ V(vm_notify_debugger_on_exception, "vm:notify-debugger-on-exception") \
V(vm_recognized, "vm:recognized") \
V(vm_trace_entrypoints, "vm:testing.unsafe.trace-entrypoints-fn") \
V(vm_procedure_attributes_metadata, "vm.procedure-attributes.metadata") \
diff --git a/tools/VERSION b/tools/VERSION
index 58124d3..959ece6 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 12
PATCH 0
-PRERELEASE 201
+PRERELEASE 202
PRERELEASE_PATCH 0
\ No newline at end of file