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