Version 2.5.0-dev.0.0

Merge commit '75b469fe1736e2101b9a43872233e78e371fa7c7' into dev
diff --git a/.gitignore b/.gitignore
index 8012a26..0216f06 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,16 +46,15 @@
 .vscode
 .history
 
+# https://github.com/Dart-Code/Dart-Code/issues/1295
+/analysis_options.yaml
+
 # analysis server files
-analysis_options.yaml
 compile_commands.json
 
 # GDB files
 .gdb_history
 
-# https://github.com/Dart-Code/Dart-Code/issues/1295
-analysis_options.yaml
-
 # Built by chromebot and downloaded from Google Storage
 client/tests/drt
 
diff --git a/.packages b/.packages
index 37c475e..80bae2d 100644
--- a/.packages
+++ b/.packages
@@ -94,10 +94,10 @@
 test:third_party/pkg/test/pkgs/test/lib
 test_api:third_party/pkg/test/pkgs/test_api/lib
 test_core:third_party/pkg/test/pkgs/test_core/lib
-test_dart:tools/testing/dart
 test_descriptor:third_party/pkg/test_descriptor/lib
 test_process:third_party/pkg/test_process/lib
 test_reflective_loader:third_party/pkg/test_reflective_loader/lib
+test_runner:pkg/test_runner/lib
 testing:pkg/testing/lib
 typed_data:third_party/pkg/typed_data/lib
 unittest:third_party/pkg/unittest/lib
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b9554f5..dac2a39 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,85 @@
+## Next release
+(Add new changes here, and they will be copied to the change section for the
+ next release)
+
+### Language
+
+### Core libraries
+
+* As part of (Issue [36900][]), the following methods and properties across
+  various core libraries, which used to declare a return type of `List<int>`,
+  were updated to declare a return type of `Uint8List`:
+
+  * `Utf8Codec.encode()` (and `Utf8Encoder.convert()`)
+  * `BytesBuilder.takeBytes()`
+  * `BytesBuilder.toBytes()`
+  * `File.readAsBytes()` (`Future<Uint8List>`)
+  * `File.readAsBytesSync()`
+  * `RandomAccessFile.read()` (`Future<Uint8List>`)
+  * `RandomAccessFile.readSync()`
+  * `InternetAddress.rawAddress`
+  * `RawSocket.read()`
+
+  In addition, the following typed lists were updated to have their `sublist()`
+  methods declare a return type that is the same as the source list:
+
+  * `Uint8List.sublist()` → `Uint8List`
+  * `Int8List.sublist()` → `Int8List`
+  * `Uint8ClampedList.sublist()` → `Uint8ClampedList`
+  * `Int16List.sublist()` → `Int16List`
+  * `Uint16List.sublist()` → `Uint16List`
+  * `Int32List.sublist()` → `Int32List`
+  * `Uint32List.sublist()` → `Uint32List`
+  * `Int64List.sublist()` → `Int64List`
+  * `Uint64List.sublist()` → `Uint64List`
+  * `Float32List.sublist()` → `Float32List`
+  * `Float64List.sublist()` → `Float64List`
+  * `Float32x4List.sublist()` → `Float32x4List`
+  * `Int32x4List.sublist()` → `Int32x4List`
+  * `Float64x2List.sublist()` → `Float64x2List`
+
+  [36900]: https://github.com/dart-lang/sdk/issues/36900
+
+#### `dart:core`
+
+* Update `Uri` class to support [RFC6874](https://tools.ietf.org/html/rfc6874):
+  "%25" or "%" can be appended to the end of a valid IPv6 representing a Zone
+  Identifier. A valid zone ID consists of unreversed character or Percent
+  encoded octet, which was defined in RFC3986.
+  IPv6addrz = IPv6address "%25" ZoneID
+
+  [29456]: https://github.com/dart-lang/sdk/issues/29456
+
+#### `dart:io`
+
+* **Breaking Change:** The `Cookie` class's constructor's `name` and `value`
+  optional positional parameters are now mandatory (Issue [37192][]). The
+  signature changes from:
+
+      Cookie([String name, String value])
+
+  to
+
+      Cookie(String name, String value)
+
+  However, it has not been possible to set `name` and `value` to null since Dart
+  1.3.0 (2014) where a bug made it impossible. Any code not using both
+  parameters or setting any to null would necessarily get a noSuchMethod
+  exception at runtime. This change catches such erroneous uses at compile time.
+  Since code could not previously correctly omit the parameters, this is not
+  really a breaking change.
+
+* **Breaking Change:** The `Cookie` class's `name` and `value` setters now
+  validates that the strings are made from the allowed character set and are not
+  null (Issue [37192][]). The constructor already made these checks and this
+  fixes the loophole where the setters didn't also validate.
+
+[37192]: https://github.com/dart-lang/sdk/issues/37192
+
+### Dart VM
+
+### Tools
+
 ## 2.4.0 - 2019-06-24
 
 ### Core libraries
@@ -89,6 +171,19 @@
 
 [35097]: https://github.com/dart-lang/sdk/issues/35097
 
+### Dart for the Web
+
+#### Dart Dev Compiler (DDC)
+
+* Improve `NoSuchMethod` errors for failing dynamic calls. Now they include
+  specific information about the nature of the error such as:
+  * Attempting to call a null value.
+  * Calling an object instance with a null `call()` method.
+  * Passing too few or too many arguments.
+  * Passing incorrect named arguments.
+  * Passing too few or too many type arguments.
+  * Passing type arguments to a non-generic method.
+
 ### Tools
 
 #### Linter
@@ -104,7 +199,6 @@
 * Added `prefer_if_null_operators`.
 * Fixed `prefer_contains` false positives.
 * Fixed `unnecessary_parenthesis` false positives.
-* New lint: `prefer_double_quotes`
 * Fixed `prefer_asserts_in_initializer_lists` false positives
 * Fixed `curly_braces_in_flow_control_structures` to handle more cases
 * New lint: `prefer_double_quotes`
diff --git a/DEPS b/DEPS
index 81897b0..e7731b9 100644
--- a/DEPS
+++ b/DEPS
@@ -36,7 +36,7 @@
   "chromium_git": "https://chromium.googlesource.com",
   "fuchsia_git": "https://fuchsia.googlesource.com",
 
-  "co19_2_rev": "c4a8862775188ecb25991b815e2f1f700b19d0cc",
+  "co19_2_rev": "b0220fc898c32d3944cb8c54cf7b78dd8c7cbadb",
 
   # As Flutter does, we use Fuchsia's GN and Clang toolchain. These revision
   # should be kept up to date with the revisions pulled by the Flutter engine.
@@ -85,7 +85,7 @@
   "fixnum_tag": "0.10.9",
   "glob_tag": "1.1.7",
   "html_tag" : "0.14.0+1",
-  "http_io_rev": "57da05a66f5bf7df3dd7aebe7b7efe0dfc477baa",
+  "http_io_rev": "773f4bc73ef572e2c37e879b065c3b406d75e8fd",
   "http_multi_server_tag" : "2.0.5",
   "http_parser_tag" : "3.1.3",
   "http_retry_tag": "0.1.1",
@@ -109,7 +109,7 @@
   "package_config_tag": "1.0.5",
   "package_resolver_tag": "1.0.10",
   "path_tag": "1.6.2",
-  "pedantic_tag": "v1.5.0",
+  "pedantic_tag": "v1.7.0",
   "ply_rev": "604b32590ffad5cbb82e4afef1d305512d06ae93",
   "pool_tag": "1.3.6",
   "protobuf_rev": "7d34c9e4e552a4f66acce32e4344ae27756a1949",
@@ -157,7 +157,7 @@
   Var("dart_root") + "/tools/sdks": {
       "packages": [{
           "package": "dart/dart-sdk/${{platform}}",
-          "version": "version:2.3.0",
+          "version": "version:2.3.3-dev.0.0",
       }],
       "dep_type": "cipd",
   },
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 8bf1471..520d015 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -77,6 +77,15 @@
     return []
 
   def HasFormatErrors(filename=None, contents=None):
+    # Don't look for formatting errors in multitests. Since those are very
+    # sensitive to whitespace, many cannot be formatted with dartfmt without
+    # breaking them.
+    if filename and filename.endswith('_test.dart'):
+      with open(filename) as f:
+        contents = f.read()
+        if '//#' in contents:
+          return False
+
     args = [prebuilt_dartfmt, '--set-exit-if-changed']
     if not contents:
       args += [filename, '-n']
diff --git a/WATCHLISTS b/WATCHLISTS
index ea5b5d0..1c0bf93 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -67,7 +67,7 @@
   'WATCHLISTS': {
     'build': [ 'keertip@google.com' ],
     'dart2js': [ 'johnniwinther@google.com', 'sigmund@google.com',
-                 'sra@google.com'],
+                 'sra@google.com', 'fishythefish@google.com' ],
     'front_end': [ 'dart-fe-team+reviews@google.com' ],
     'kernel': [ 'karlklose@google.com', 'jensj@google.com', 'kmillikin@google.com',
                 'alexmarkov@google.com' ],
diff --git a/pkg/analysis_server/benchmark/perf/memory_tests.dart b/pkg/analysis_server/benchmark/perf/memory_tests.dart
index 6af8813..84385b6 100644
--- a/pkg/analysis_server/benchmark/perf/memory_tests.dart
+++ b/pkg/analysis_server/benchmark/perf/memory_tests.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'dart:async';
+import 'dart:io';
 
 import 'package:analysis_server/protocol/protocol_generated.dart';
 import 'package:analysis_server/src/status/diagnostics.dart';
@@ -15,10 +16,10 @@
  */
 class AnalysisServerMemoryUsageTest
     extends AbstractAnalysisServerIntegrationTest {
-  static const int vmServicePort = 12345;
+  int _vmServicePort;
 
   Future<int> getMemoryUsage() async {
-    Uri uri = Uri.parse('ws://127.0.0.1:$vmServicePort/ws');
+    Uri uri = Uri.parse('ws://127.0.0.1:$_vmServicePort/ws');
     final ServiceProtocol service = await ServiceProtocol.connect(uri);
     final Map vm = await service.call('getVM');
 
@@ -50,7 +51,9 @@
    * The server is automatically started before every test.
    */
   @override
-  Future setUp() {
+  Future setUp() async {
+    _vmServicePort = await _findAvailableSocketPort();
+
     onAnalysisErrors.listen((AnalysisErrorsParams params) {
       currentAnalysisErrors[params.file] = params.errors;
     });
@@ -63,7 +66,7 @@
       outOfTestExpect(serverConnected.isCompleted, isFalse);
       serverConnected.complete();
     });
-    return startServer(servicesPort: vmServicePort).then((_) {
+    return startServer(servicesPort: _vmServicePort).then((_) {
       server.listenToOutput(dispatchNotification);
       server.exitCode.then((_) {
         skipShutdown = true;
@@ -84,4 +87,13 @@
   Future subscribeToStatusNotifications() async {
     await sendServerSetSubscriptions([ServerService.STATUS]);
   }
+
+  static Future<int> _findAvailableSocketPort() async {
+    var socket = await ServerSocket.bind(InternetAddress.loopbackIPv4, 0);
+    try {
+      return socket.port;
+    } finally {
+      await socket.close();
+    }
+  }
 }
diff --git a/pkg/analysis_server/lib/lsp_protocol/protocol_custom_generated.dart b/pkg/analysis_server/lib/lsp_protocol/protocol_custom_generated.dart
index deb8901..20633bd 100644
--- a/pkg/analysis_server/lib/lsp_protocol/protocol_custom_generated.dart
+++ b/pkg/analysis_server/lib/lsp_protocol/protocol_custom_generated.dart
@@ -17,6 +17,7 @@
 import 'dart:convert' show JsonEncoder;
 import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
 import 'package:analysis_server/lsp_protocol/protocol_special.dart';
+import 'package:analysis_server/src/lsp/json_parsing.dart';
 import 'package:analysis_server/src/protocol/protocol_internal.dart'
     show listEqual, mapEqual;
 import 'package:analyzer/src/generated/utilities_general.dart';
@@ -46,10 +47,30 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('isAnalyzing') &&
-        obj['isAnalyzing'] is bool;
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('isAnalyzing');
+      try {
+        if (!obj.containsKey('isAnalyzing')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['isAnalyzing'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['isAnalyzing'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type AnalyzerStatusParams");
+      return false;
+    }
   }
 
   @override
@@ -99,12 +120,47 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('range') &&
-        Range.canParse(obj['range']) &&
-        obj.containsKey('label') &&
-        obj['label'] is String;
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('range');
+      try {
+        if (!obj.containsKey('range')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['range'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(Range.canParse(obj['range'], reporter))) {
+          reporter.reportError("must be of type Range");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('label');
+      try {
+        if (!obj.containsKey('label')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['label'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['label'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type ClosingLabel");
+      return false;
+    }
   }
 
   @override
@@ -185,20 +241,115 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('file') &&
-        obj['file'] is String &&
-        obj.containsKey('offset') &&
-        obj['offset'] is num &&
-        obj.containsKey('libId') &&
-        obj['libId'] is num &&
-        obj.containsKey('displayUri') &&
-        obj['displayUri'] is String &&
-        obj.containsKey('rOffset') &&
-        obj['rOffset'] is num &&
-        obj.containsKey('rLength') &&
-        obj['rLength'] is num;
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('file');
+      try {
+        if (!obj.containsKey('file')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['file'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['file'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('offset');
+      try {
+        if (!obj.containsKey('offset')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['offset'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['offset'] is num)) {
+          reporter.reportError("must be of type num");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('libId');
+      try {
+        if (!obj.containsKey('libId')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['libId'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['libId'] is num)) {
+          reporter.reportError("must be of type num");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('displayUri');
+      try {
+        if (!obj.containsKey('displayUri')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['displayUri'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['displayUri'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('rOffset');
+      try {
+        if (!obj.containsKey('rOffset')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['rOffset'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['rOffset'] is num)) {
+          reporter.reportError("must be of type num");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('rLength');
+      try {
+        if (!obj.containsKey('rLength')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['rLength'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['rLength'] is num)) {
+          reporter.reportError("must be of type num");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type CompletionItemResolutionInfo");
+      return false;
+    }
   }
 
   @override
@@ -253,10 +404,30 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('port') &&
-        obj['port'] is num;
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('port');
+      try {
+        if (!obj.containsKey('port')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['port'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['port'] is num)) {
+          reporter.reportError("must be of type num");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type DartDiagnosticServer");
+      return false;
+    }
   }
 
   @override
@@ -309,13 +480,49 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('uri') &&
-        obj['uri'] is String &&
-        obj.containsKey('labels') &&
-        (obj['labels'] is List &&
-            (obj['labels'].every((item) => ClosingLabel.canParse(item))));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('uri');
+      try {
+        if (!obj.containsKey('uri')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['uri'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['uri'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('labels');
+      try {
+        if (!obj.containsKey('labels')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['labels'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!((obj['labels'] is List &&
+            (obj['labels']
+                .every((item) => ClosingLabel.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<ClosingLabel>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type PublishClosingLabelsParams");
+      return false;
+    }
   }
 
   @override
diff --git a/pkg/analysis_server/lib/lsp_protocol/protocol_generated.dart b/pkg/analysis_server/lib/lsp_protocol/protocol_generated.dart
index af4a262..a245b14 100644
--- a/pkg/analysis_server/lib/lsp_protocol/protocol_generated.dart
+++ b/pkg/analysis_server/lib/lsp_protocol/protocol_generated.dart
@@ -17,6 +17,7 @@
 import 'dart:convert' show JsonEncoder;
 import 'package:analysis_server/lsp_protocol/protocol_custom_generated.dart';
 import 'package:analysis_server/lsp_protocol/protocol_special.dart';
+import 'package:analysis_server/src/lsp/json_parsing.dart';
 import 'package:analysis_server/src/protocol/protocol_internal.dart'
     show listEqual, mapEqual;
 import 'package:analyzer/src/generated/utilities_general.dart';
@@ -55,11 +56,39 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['label'] == null || obj['label'] is String) &&
-        obj.containsKey('edit') &&
-        WorkspaceEdit.canParse(obj['edit']);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('label');
+      try {
+        if (obj['label'] != null && !(obj['label'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('edit');
+      try {
+        if (!obj.containsKey('edit')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['edit'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(WorkspaceEdit.canParse(obj['edit'], reporter))) {
+          reporter.reportError("must be of type WorkspaceEdit");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type ApplyWorkspaceEditParams");
+      return false;
+    }
   }
 
   @override
@@ -115,11 +144,39 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('applied') &&
-        obj['applied'] is bool &&
-        (obj['failureReason'] == null || obj['failureReason'] is String);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('applied');
+      try {
+        if (!obj.containsKey('applied')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['applied'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['applied'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('failureReason');
+      try {
+        if (obj['failureReason'] != null && !(obj['failureReason'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type ApplyWorkspaceEditResponse");
+      return false;
+    }
   }
 
   @override
@@ -171,10 +228,30 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('id') &&
-        (obj['id'] is num || obj['id'] is String);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('id');
+      try {
+        if (!obj.containsKey('id')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['id'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!((obj['id'] is num || obj['id'] is String))) {
+          reporter.reportError("must be of type Either2<num, String>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type CancelParams");
+      return false;
+    }
   }
 
   @override
@@ -235,13 +312,45 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['workspace'] == null ||
-            WorkspaceClientCapabilities.canParse(obj['workspace'])) &&
-        (obj['textDocument'] == null ||
-            TextDocumentClientCapabilities.canParse(obj['textDocument'])) &&
-        (obj['experimental'] == null || true);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('workspace');
+      try {
+        if (obj['workspace'] != null &&
+            !(WorkspaceClientCapabilities.canParse(
+                obj['workspace'], reporter))) {
+          reporter.reportError("must be of type WorkspaceClientCapabilities");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('textDocument');
+      try {
+        if (obj['textDocument'] != null &&
+            !(TextDocumentClientCapabilities.canParse(
+                obj['textDocument'], reporter))) {
+          reporter
+              .reportError("must be of type TextDocumentClientCapabilities");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('experimental');
+      try {
+        if (obj['experimental'] != null && !(true)) {
+          reporter.reportError("must be of type dynamic");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type ClientCapabilities");
+      return false;
+    }
   }
 
   @override
@@ -333,17 +442,72 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('title') &&
-        obj['title'] is String &&
-        (obj['kind'] == null || CodeActionKind.canParse(obj['kind'])) &&
-        (obj['diagnostics'] == null ||
-            (obj['diagnostics'] is List &&
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('title');
+      try {
+        if (!obj.containsKey('title')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['title'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['title'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('kind');
+      try {
+        if (obj['kind'] != null &&
+            !(CodeActionKind.canParse(obj['kind'], reporter))) {
+          reporter.reportError("must be of type CodeActionKind");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('diagnostics');
+      try {
+        if (obj['diagnostics'] != null &&
+            !((obj['diagnostics'] is List &&
                 (obj['diagnostics']
-                    .every((item) => Diagnostic.canParse(item))))) &&
-        (obj['edit'] == null || WorkspaceEdit.canParse(obj['edit'])) &&
-        (obj['command'] == null || Command.canParse(obj['command']));
+                    .every((item) => Diagnostic.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<Diagnostic>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('edit');
+      try {
+        if (obj['edit'] != null &&
+            !(WorkspaceEdit.canParse(obj['edit'], reporter))) {
+          reporter.reportError("must be of type WorkspaceEdit");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('command');
+      try {
+        if (obj['command'] != null &&
+            !(Command.canParse(obj['command'], reporter))) {
+          reporter.reportError("must be of type Command");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type CodeAction");
+      return false;
+    }
   }
 
   @override
@@ -417,14 +581,44 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('diagnostics') &&
-        (obj['diagnostics'] is List &&
-            (obj['diagnostics'].every((item) => Diagnostic.canParse(item)))) &&
-        (obj['only'] == null ||
-            (obj['only'] is List &&
-                (obj['only'].every((item) => CodeActionKind.canParse(item)))));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('diagnostics');
+      try {
+        if (!obj.containsKey('diagnostics')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['diagnostics'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!((obj['diagnostics'] is List &&
+            (obj['diagnostics']
+                .every((item) => Diagnostic.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<Diagnostic>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('only');
+      try {
+        if (obj['only'] != null &&
+            !((obj['only'] is List &&
+                (obj['only'].every(
+                    (item) => CodeActionKind.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<CodeActionKind>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type CodeActionContext");
+      return false;
+    }
   }
 
   @override
@@ -458,7 +652,7 @@
 
   final String _value;
 
-  static bool canParse(Object obj) {
+  static bool canParse(Object obj, LspJsonReporter reporter) {
     return obj is String;
   }
 
@@ -528,7 +722,7 @@
 
   CodeActionOptions(this.codeActionKinds);
   static CodeActionOptions fromJson(Map<String, dynamic> json) {
-    if (CodeActionRegistrationOptions.canParse(json)) {
+    if (CodeActionRegistrationOptions.canParse(json, nullLspJsonReporter)) {
       return CodeActionRegistrationOptions.fromJson(json);
     }
     final codeActionKinds = json['codeActionKinds']
@@ -552,12 +746,25 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['codeActionKinds'] == null ||
-            (obj['codeActionKinds'] is List &&
-                (obj['codeActionKinds']
-                    .every((item) => CodeActionKind.canParse(item)))));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('codeActionKinds');
+      try {
+        if (obj['codeActionKinds'] != null &&
+            !((obj['codeActionKinds'] is List &&
+                (obj['codeActionKinds'].every(
+                    (item) => CodeActionKind.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<CodeActionKind>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type CodeActionOptions");
+      return false;
+    }
   }
 
   @override
@@ -627,14 +834,64 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('textDocument') &&
-        TextDocumentIdentifier.canParse(obj['textDocument']) &&
-        obj.containsKey('range') &&
-        Range.canParse(obj['range']) &&
-        obj.containsKey('context') &&
-        CodeActionContext.canParse(obj['context']);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('textDocument');
+      try {
+        if (!obj.containsKey('textDocument')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['textDocument'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(TextDocumentIdentifier.canParse(obj['textDocument'], reporter))) {
+          reporter.reportError("must be of type TextDocumentIdentifier");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('range');
+      try {
+        if (!obj.containsKey('range')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['range'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(Range.canParse(obj['range'], reporter))) {
+          reporter.reportError("must be of type Range");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('context');
+      try {
+        if (!obj.containsKey('context')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['context'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(CodeActionContext.canParse(obj['context'], reporter))) {
+          reporter.reportError("must be of type CodeActionContext");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type CodeActionParams");
+      return false;
+    }
   }
 
   @override
@@ -699,17 +956,41 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('documentSelector') &&
-        (obj['documentSelector'] == null ||
-            (obj['documentSelector'] is List &&
-                (obj['documentSelector']
-                    .every((item) => DocumentFilter.canParse(item))))) &&
-        (obj['codeActionKinds'] == null ||
-            (obj['codeActionKinds'] is List &&
-                (obj['codeActionKinds']
-                    .every((item) => CodeActionKind.canParse(item)))));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('documentSelector');
+      try {
+        if (!obj.containsKey('documentSelector')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['documentSelector'] != null &&
+            !((obj['documentSelector'] is List &&
+                (obj['documentSelector'].every(
+                    (item) => DocumentFilter.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<DocumentFilter>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('codeActionKinds');
+      try {
+        if (obj['codeActionKinds'] != null &&
+            !((obj['codeActionKinds'] is List &&
+                (obj['codeActionKinds'].every(
+                    (item) => CodeActionKind.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<CodeActionKind>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type CodeActionRegistrationOptions");
+      return false;
+    }
   }
 
   @override
@@ -781,12 +1062,49 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('range') &&
-        Range.canParse(obj['range']) &&
-        (obj['command'] == null || Command.canParse(obj['command'])) &&
-        (obj['data'] == null || true);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('range');
+      try {
+        if (!obj.containsKey('range')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['range'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(Range.canParse(obj['range'], reporter))) {
+          reporter.reportError("must be of type Range");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('command');
+      try {
+        if (obj['command'] != null &&
+            !(Command.canParse(obj['command'], reporter))) {
+          reporter.reportError("must be of type Command");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('data');
+      try {
+        if (obj['data'] != null && !(true)) {
+          reporter.reportError("must be of type dynamic");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type CodeLens");
+      return false;
+    }
   }
 
   @override
@@ -835,9 +1153,23 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['resolveProvider'] == null || obj['resolveProvider'] is bool);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('resolveProvider');
+      try {
+        if (obj['resolveProvider'] != null &&
+            !(obj['resolveProvider'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type CodeLensOptions");
+      return false;
+    }
   }
 
   @override
@@ -885,10 +1217,30 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('textDocument') &&
-        TextDocumentIdentifier.canParse(obj['textDocument']);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('textDocument');
+      try {
+        if (!obj.containsKey('textDocument')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['textDocument'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(TextDocumentIdentifier.canParse(obj['textDocument'], reporter))) {
+          reporter.reportError("must be of type TextDocumentIdentifier");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type CodeLensParams");
+      return false;
+    }
   }
 
   @override
@@ -942,14 +1294,39 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['resolveProvider'] == null || obj['resolveProvider'] is bool) &&
-        obj.containsKey('documentSelector') &&
-        (obj['documentSelector'] == null ||
-            (obj['documentSelector'] is List &&
-                (obj['documentSelector']
-                    .every((item) => DocumentFilter.canParse(item)))));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('resolveProvider');
+      try {
+        if (obj['resolveProvider'] != null &&
+            !(obj['resolveProvider'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('documentSelector');
+      try {
+        if (!obj.containsKey('documentSelector')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['documentSelector'] != null &&
+            !((obj['documentSelector'] is List &&
+                (obj['documentSelector'].every(
+                    (item) => DocumentFilter.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<DocumentFilter>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type CodeLensRegistrationOptions");
+      return false;
+    }
   }
 
   @override
@@ -1022,16 +1399,81 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('red') &&
-        obj['red'] is num &&
-        obj.containsKey('green') &&
-        obj['green'] is num &&
-        obj.containsKey('blue') &&
-        obj['blue'] is num &&
-        obj.containsKey('alpha') &&
-        obj['alpha'] is num;
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('red');
+      try {
+        if (!obj.containsKey('red')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['red'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['red'] is num)) {
+          reporter.reportError("must be of type num");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('green');
+      try {
+        if (!obj.containsKey('green')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['green'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['green'] is num)) {
+          reporter.reportError("must be of type num");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('blue');
+      try {
+        if (!obj.containsKey('blue')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['blue'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['blue'] is num)) {
+          reporter.reportError("must be of type num");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('alpha');
+      try {
+        if (!obj.containsKey('alpha')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['alpha'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['alpha'] is num)) {
+          reporter.reportError("must be of type num");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type Color");
+      return false;
+    }
   }
 
   @override
@@ -1091,12 +1533,47 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('range') &&
-        Range.canParse(obj['range']) &&
-        obj.containsKey('color') &&
-        Color.canParse(obj['color']);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('range');
+      try {
+        if (!obj.containsKey('range')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['range'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(Range.canParse(obj['range'], reporter))) {
+          reporter.reportError("must be of type Range");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('color');
+      try {
+        if (!obj.containsKey('color')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['color'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(Color.canParse(obj['color'], reporter))) {
+          reporter.reportError("must be of type Color");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type ColorInformation");
+      return false;
+    }
   }
 
   @override
@@ -1166,15 +1643,52 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('label') &&
-        obj['label'] is String &&
-        (obj['textEdit'] == null || TextEdit.canParse(obj['textEdit'])) &&
-        (obj['additionalTextEdits'] == null ||
-            (obj['additionalTextEdits'] is List &&
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('label');
+      try {
+        if (!obj.containsKey('label')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['label'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['label'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('textEdit');
+      try {
+        if (obj['textEdit'] != null &&
+            !(TextEdit.canParse(obj['textEdit'], reporter))) {
+          reporter.reportError("must be of type TextEdit");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('additionalTextEdits');
+      try {
+        if (obj['additionalTextEdits'] != null &&
+            !((obj['additionalTextEdits'] is List &&
                 (obj['additionalTextEdits']
-                    .every((item) => TextEdit.canParse(item)))));
+                    .every((item) => TextEdit.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<TextEdit>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type ColorPresentation");
+      return false;
+    }
   }
 
   @override
@@ -1244,14 +1758,64 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('textDocument') &&
-        TextDocumentIdentifier.canParse(obj['textDocument']) &&
-        obj.containsKey('color') &&
-        Color.canParse(obj['color']) &&
-        obj.containsKey('range') &&
-        Range.canParse(obj['range']);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('textDocument');
+      try {
+        if (!obj.containsKey('textDocument')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['textDocument'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(TextDocumentIdentifier.canParse(obj['textDocument'], reporter))) {
+          reporter.reportError("must be of type TextDocumentIdentifier");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('color');
+      try {
+        if (!obj.containsKey('color')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['color'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(Color.canParse(obj['color'], reporter))) {
+          reporter.reportError("must be of type Color");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('range');
+      try {
+        if (!obj.containsKey('range')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['range'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(Range.canParse(obj['range'], reporter))) {
+          reporter.reportError("must be of type Range");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type ColorPresentationParams");
+      return false;
+    }
   }
 
   @override
@@ -1292,8 +1856,13 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic>;
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      return true;
+    } else {
+      reporter.reportError("must be of type ColorProviderOptions");
+      return false;
+    }
   }
 
   @override
@@ -1354,15 +1923,58 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('title') &&
-        obj['title'] is String &&
-        obj.containsKey('command') &&
-        obj['command'] is String &&
-        (obj['arguments'] == null ||
-            (obj['arguments'] is List &&
-                (obj['arguments'].every((item) => true))));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('title');
+      try {
+        if (!obj.containsKey('title')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['title'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['title'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('command');
+      try {
+        if (!obj.containsKey('command')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['command'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['command'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('arguments');
+      try {
+        if (obj['arguments'] != null &&
+            !((obj['arguments'] is List &&
+                (obj['arguments'].every((item) => true))))) {
+          reporter.reportError("must be of type List<dynamic>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type Command");
+      return false;
+    }
   }
 
   @override
@@ -1426,11 +2038,40 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('triggerKind') &&
-        CompletionTriggerKind.canParse(obj['triggerKind']) &&
-        (obj['triggerCharacter'] == null || obj['triggerCharacter'] is String);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('triggerKind');
+      try {
+        if (!obj.containsKey('triggerKind')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['triggerKind'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(CompletionTriggerKind.canParse(obj['triggerKind'], reporter))) {
+          reporter.reportError("must be of type CompletionTriggerKind");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('triggerCharacter');
+      try {
+        if (obj['triggerCharacter'] != null &&
+            !(obj['triggerCharacter'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type CompletionContext");
+      return false;
+    }
   }
 
   @override
@@ -1486,7 +2127,7 @@
     final detail = json['detail'];
     final documentation = json['documentation'] is String
         ? new Either2<String, MarkupContent>.t1(json['documentation'])
-        : (MarkupContent.canParse(json['documentation'])
+        : (MarkupContent.canParse(json['documentation'], nullLspJsonReporter)
             ? new Either2<String, MarkupContent>.t2(
                 json['documentation'] != null
                     ? MarkupContent.fromJson(json['documentation'])
@@ -1663,33 +2304,169 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('label') &&
-        obj['label'] is String &&
-        (obj['kind'] == null || CompletionItemKind.canParse(obj['kind'])) &&
-        (obj['detail'] == null || obj['detail'] is String) &&
-        (obj['documentation'] == null ||
-            (obj['documentation'] is String ||
-                MarkupContent.canParse(obj['documentation']))) &&
-        (obj['deprecated'] == null || obj['deprecated'] is bool) &&
-        (obj['preselect'] == null || obj['preselect'] is bool) &&
-        (obj['sortText'] == null || obj['sortText'] is String) &&
-        (obj['filterText'] == null || obj['filterText'] is String) &&
-        (obj['insertText'] == null || obj['insertText'] is String) &&
-        (obj['insertTextFormat'] == null ||
-            InsertTextFormat.canParse(obj['insertTextFormat'])) &&
-        (obj['textEdit'] == null || TextEdit.canParse(obj['textEdit'])) &&
-        (obj['additionalTextEdits'] == null ||
-            (obj['additionalTextEdits'] is List &&
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('label');
+      try {
+        if (!obj.containsKey('label')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['label'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['label'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('kind');
+      try {
+        if (obj['kind'] != null &&
+            !(CompletionItemKind.canParse(obj['kind'], reporter))) {
+          reporter.reportError("must be of type CompletionItemKind");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('detail');
+      try {
+        if (obj['detail'] != null && !(obj['detail'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('documentation');
+      try {
+        if (obj['documentation'] != null &&
+            !((obj['documentation'] is String ||
+                MarkupContent.canParse(obj['documentation'], reporter)))) {
+          reporter
+              .reportError("must be of type Either2<String, MarkupContent>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('deprecated');
+      try {
+        if (obj['deprecated'] != null && !(obj['deprecated'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('preselect');
+      try {
+        if (obj['preselect'] != null && !(obj['preselect'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('sortText');
+      try {
+        if (obj['sortText'] != null && !(obj['sortText'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('filterText');
+      try {
+        if (obj['filterText'] != null && !(obj['filterText'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('insertText');
+      try {
+        if (obj['insertText'] != null && !(obj['insertText'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('insertTextFormat');
+      try {
+        if (obj['insertTextFormat'] != null &&
+            !(InsertTextFormat.canParse(obj['insertTextFormat'], reporter))) {
+          reporter.reportError("must be of type InsertTextFormat");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('textEdit');
+      try {
+        if (obj['textEdit'] != null &&
+            !(TextEdit.canParse(obj['textEdit'], reporter))) {
+          reporter.reportError("must be of type TextEdit");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('additionalTextEdits');
+      try {
+        if (obj['additionalTextEdits'] != null &&
+            !((obj['additionalTextEdits'] is List &&
                 (obj['additionalTextEdits']
-                    .every((item) => TextEdit.canParse(item))))) &&
-        (obj['commitCharacters'] == null ||
-            (obj['commitCharacters'] is List &&
-                (obj['commitCharacters'].every((item) => item is String)))) &&
-        (obj['command'] == null || Command.canParse(obj['command'])) &&
-        (obj['data'] == null ||
-            CompletionItemResolutionInfo.canParse(obj['data']));
+                    .every((item) => TextEdit.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<TextEdit>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('commitCharacters');
+      try {
+        if (obj['commitCharacters'] != null &&
+            !((obj['commitCharacters'] is List &&
+                (obj['commitCharacters'].every((item) => item is String))))) {
+          reporter.reportError("must be of type List<String>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('command');
+      try {
+        if (obj['command'] != null &&
+            !(Command.canParse(obj['command'], reporter))) {
+          reporter.reportError("must be of type Command");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('data');
+      try {
+        if (obj['data'] != null &&
+            !(CompletionItemResolutionInfo.canParse(obj['data'], reporter))) {
+          reporter.reportError("must be of type CompletionItemResolutionInfo");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type CompletionItem");
+      return false;
+    }
   }
 
   @override
@@ -1749,7 +2526,7 @@
 
   final num _value;
 
-  static bool canParse(Object obj) {
+  static bool canParse(Object obj, LspJsonReporter reporter) {
     return obj is num;
   }
 
@@ -1828,13 +2605,49 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('isIncomplete') &&
-        obj['isIncomplete'] is bool &&
-        obj.containsKey('items') &&
-        (obj['items'] is List &&
-            (obj['items'].every((item) => CompletionItem.canParse(item))));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('isIncomplete');
+      try {
+        if (!obj.containsKey('isIncomplete')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['isIncomplete'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['isIncomplete'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('items');
+      try {
+        if (!obj.containsKey('items')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['items'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!((obj['items'] is List &&
+            (obj['items']
+                .every((item) => CompletionItem.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<CompletionItem>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type CompletionList");
+      return false;
+    }
   }
 
   @override
@@ -1893,12 +2706,34 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['resolveProvider'] == null || obj['resolveProvider'] is bool) &&
-        (obj['triggerCharacters'] == null ||
-            (obj['triggerCharacters'] is List &&
-                (obj['triggerCharacters'].every((item) => item is String))));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('resolveProvider');
+      try {
+        if (obj['resolveProvider'] != null &&
+            !(obj['resolveProvider'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('triggerCharacters');
+      try {
+        if (obj['triggerCharacters'] != null &&
+            !((obj['triggerCharacters'] is List &&
+                (obj['triggerCharacters'].every((item) => item is String))))) {
+          reporter.reportError("must be of type List<String>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type CompletionOptions");
+      return false;
+    }
   }
 
   @override
@@ -1971,14 +2806,57 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['context'] == null ||
-            CompletionContext.canParse(obj['context'])) &&
-        obj.containsKey('textDocument') &&
-        TextDocumentIdentifier.canParse(obj['textDocument']) &&
-        obj.containsKey('position') &&
-        Position.canParse(obj['position']);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('context');
+      try {
+        if (obj['context'] != null &&
+            !(CompletionContext.canParse(obj['context'], reporter))) {
+          reporter.reportError("must be of type CompletionContext");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('textDocument');
+      try {
+        if (!obj.containsKey('textDocument')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['textDocument'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(TextDocumentIdentifier.canParse(obj['textDocument'], reporter))) {
+          reporter.reportError("must be of type TextDocumentIdentifier");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('position');
+      try {
+        if (!obj.containsKey('position')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['position'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(Position.canParse(obj['position'], reporter))) {
+          reporter.reportError("must be of type Position");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type CompletionParams");
+      return false;
+    }
   }
 
   @override
@@ -2074,21 +2952,62 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['triggerCharacters'] == null ||
-            (obj['triggerCharacters'] is List &&
-                (obj['triggerCharacters'].every((item) => item is String)))) &&
-        (obj['allCommitCharacters'] == null ||
-            (obj['allCommitCharacters'] is List &&
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('triggerCharacters');
+      try {
+        if (obj['triggerCharacters'] != null &&
+            !((obj['triggerCharacters'] is List &&
+                (obj['triggerCharacters'].every((item) => item is String))))) {
+          reporter.reportError("must be of type List<String>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('allCommitCharacters');
+      try {
+        if (obj['allCommitCharacters'] != null &&
+            !((obj['allCommitCharacters'] is List &&
                 (obj['allCommitCharacters']
-                    .every((item) => item is String)))) &&
-        (obj['resolveProvider'] == null || obj['resolveProvider'] is bool) &&
-        obj.containsKey('documentSelector') &&
-        (obj['documentSelector'] == null ||
-            (obj['documentSelector'] is List &&
-                (obj['documentSelector']
-                    .every((item) => DocumentFilter.canParse(item)))));
+                    .every((item) => item is String))))) {
+          reporter.reportError("must be of type List<String>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('resolveProvider');
+      try {
+        if (obj['resolveProvider'] != null &&
+            !(obj['resolveProvider'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('documentSelector');
+      try {
+        if (!obj.containsKey('documentSelector')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['documentSelector'] != null &&
+            !((obj['documentSelector'] is List &&
+                (obj['documentSelector'].every(
+                    (item) => DocumentFilter.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<DocumentFilter>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type CompletionRegistrationOptions");
+      return false;
+    }
   }
 
   @override
@@ -2126,7 +3045,7 @@
 
   final num _value;
 
-  static bool canParse(Object obj) {
+  static bool canParse(Object obj, LspJsonReporter reporter) {
     switch (obj) {
       case 1:
       case 2:
@@ -2187,10 +3106,31 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['scopeUri'] == null || obj['scopeUri'] is String) &&
-        (obj['section'] == null || obj['section'] is String);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('scopeUri');
+      try {
+        if (obj['scopeUri'] != null && !(obj['scopeUri'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('section');
+      try {
+        if (obj['section'] != null && !(obj['section'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type ConfigurationItem");
+      return false;
+    }
   }
 
   @override
@@ -2238,11 +3178,32 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('items') &&
-        (obj['items'] is List &&
-            (obj['items'].every((item) => ConfigurationItem.canParse(item))));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('items');
+      try {
+        if (!obj.containsKey('items')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['items'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!((obj['items'] is List &&
+            (obj['items'].every(
+                (item) => ConfigurationItem.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<ConfigurationItem>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type ConfigurationParams");
+      return false;
+    }
   }
 
   @override
@@ -2307,13 +3268,57 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('kind') &&
-        obj['kind'] is String &&
-        obj.containsKey('uri') &&
-        obj['uri'] is String &&
-        (obj['options'] == null || CreateFileOptions.canParse(obj['options']));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('kind');
+      try {
+        if (!obj.containsKey('kind')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['kind'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['kind'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('uri');
+      try {
+        if (!obj.containsKey('uri')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['uri'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['uri'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('options');
+      try {
+        if (obj['options'] != null &&
+            !(CreateFileOptions.canParse(obj['options'], reporter))) {
+          reporter.reportError("must be of type CreateFileOptions");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type CreateFile");
+      return false;
+    }
   }
 
   @override
@@ -2369,10 +3374,31 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['overwrite'] == null || obj['overwrite'] is bool) &&
-        (obj['ignoreIfExists'] == null || obj['ignoreIfExists'] is bool);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('overwrite');
+      try {
+        if (obj['overwrite'] != null && !(obj['overwrite'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('ignoreIfExists');
+      try {
+        if (obj['ignoreIfExists'] != null && !(obj['ignoreIfExists'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type CreateFileOptions");
+      return false;
+    }
   }
 
   @override
@@ -2438,13 +3464,57 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('kind') &&
-        obj['kind'] is String &&
-        obj.containsKey('uri') &&
-        obj['uri'] is String &&
-        (obj['options'] == null || DeleteFileOptions.canParse(obj['options']));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('kind');
+      try {
+        if (!obj.containsKey('kind')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['kind'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['kind'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('uri');
+      try {
+        if (!obj.containsKey('uri')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['uri'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['uri'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('options');
+      try {
+        if (obj['options'] != null &&
+            !(DeleteFileOptions.canParse(obj['options'], reporter))) {
+          reporter.reportError("must be of type DeleteFileOptions");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type DeleteFile");
+      return false;
+    }
   }
 
   @override
@@ -2500,10 +3570,32 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['recursive'] == null || obj['recursive'] is bool) &&
-        (obj['ignoreIfNotExists'] == null || obj['ignoreIfNotExists'] is bool);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('recursive');
+      try {
+        if (obj['recursive'] != null && !(obj['recursive'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('ignoreIfNotExists');
+      try {
+        if (obj['ignoreIfNotExists'] != null &&
+            !(obj['ignoreIfNotExists'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type DeleteFileOptions");
+      return false;
+    }
   }
 
   @override
@@ -2599,20 +3691,88 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('range') &&
-        Range.canParse(obj['range']) &&
-        (obj['severity'] == null ||
-            DiagnosticSeverity.canParse(obj['severity'])) &&
-        (obj['code'] == null || obj['code'] is String) &&
-        (obj['source'] == null || obj['source'] is String) &&
-        obj.containsKey('message') &&
-        obj['message'] is String &&
-        (obj['relatedInformation'] == null ||
-            (obj['relatedInformation'] is List &&
-                (obj['relatedInformation'].every(
-                    (item) => DiagnosticRelatedInformation.canParse(item)))));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('range');
+      try {
+        if (!obj.containsKey('range')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['range'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(Range.canParse(obj['range'], reporter))) {
+          reporter.reportError("must be of type Range");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('severity');
+      try {
+        if (obj['severity'] != null &&
+            !(DiagnosticSeverity.canParse(obj['severity'], reporter))) {
+          reporter.reportError("must be of type DiagnosticSeverity");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('code');
+      try {
+        if (obj['code'] != null && !(obj['code'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('source');
+      try {
+        if (obj['source'] != null && !(obj['source'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('message');
+      try {
+        if (!obj.containsKey('message')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['message'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['message'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('relatedInformation');
+      try {
+        if (obj['relatedInformation'] != null &&
+            !((obj['relatedInformation'] is List &&
+                (obj['relatedInformation'].every((item) =>
+                    DiagnosticRelatedInformation.canParse(item, reporter)))))) {
+          reporter.reportError(
+              "must be of type List<DiagnosticRelatedInformation>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type Diagnostic");
+      return false;
+    }
   }
 
   @override
@@ -2688,12 +3848,47 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('location') &&
-        Location.canParse(obj['location']) &&
-        obj.containsKey('message') &&
-        obj['message'] is String;
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('location');
+      try {
+        if (!obj.containsKey('location')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['location'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(Location.canParse(obj['location'], reporter))) {
+          reporter.reportError("must be of type Location");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('message');
+      try {
+        if (!obj.containsKey('message')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['message'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['message'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type DiagnosticRelatedInformation");
+      return false;
+    }
   }
 
   @override
@@ -2722,7 +3917,7 @@
 
   final num _value;
 
-  static bool canParse(Object obj) {
+  static bool canParse(Object obj, LspJsonReporter reporter) {
     return obj is num;
   }
 
@@ -2769,10 +3964,26 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('settings') &&
-        (obj['settings'] == null || true);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('settings');
+      try {
+        if (!obj.containsKey('settings')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['settings'] != null && !(true)) {
+          reporter.reportError("must be of type dynamic");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type DidChangeConfigurationParams");
+      return false;
+    }
   }
 
   @override
@@ -2837,14 +4048,52 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('textDocument') &&
-        VersionedTextDocumentIdentifier.canParse(obj['textDocument']) &&
-        obj.containsKey('contentChanges') &&
-        (obj['contentChanges'] is List &&
-            (obj['contentChanges'].every(
-                (item) => TextDocumentContentChangeEvent.canParse(item))));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('textDocument');
+      try {
+        if (!obj.containsKey('textDocument')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['textDocument'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(VersionedTextDocumentIdentifier.canParse(
+            obj['textDocument'], reporter))) {
+          reporter
+              .reportError("must be of type VersionedTextDocumentIdentifier");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('contentChanges');
+      try {
+        if (!obj.containsKey('contentChanges')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['contentChanges'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!((obj['contentChanges'] is List &&
+            (obj['contentChanges'].every((item) =>
+                TextDocumentContentChangeEvent.canParse(item, reporter)))))) {
+          reporter.reportError(
+              "must be of type List<TextDocumentContentChangeEvent>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type DidChangeTextDocumentParams");
+      return false;
+    }
   }
 
   @override
@@ -2902,11 +4151,32 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('changes') &&
-        (obj['changes'] is List &&
-            (obj['changes'].every((item) => FileEvent.canParse(item))));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('changes');
+      try {
+        if (!obj.containsKey('changes')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['changes'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!((obj['changes'] is List &&
+            (obj['changes']
+                .every((item) => FileEvent.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<FileEvent>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type DidChangeWatchedFilesParams");
+      return false;
+    }
   }
 
   @override
@@ -2960,12 +4230,33 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('watchers') &&
-        (obj['watchers'] is List &&
-            (obj['watchers']
-                .every((item) => FileSystemWatcher.canParse(item))));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('watchers');
+      try {
+        if (!obj.containsKey('watchers')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['watchers'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!((obj['watchers'] is List &&
+            (obj['watchers'].every(
+                (item) => FileSystemWatcher.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<FileSystemWatcher>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError(
+          "must be of type DidChangeWatchedFilesRegistrationOptions");
+      return false;
+    }
   }
 
   @override
@@ -3015,10 +4306,30 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('event') &&
-        WorkspaceFoldersChangeEvent.canParse(obj['event']);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('event');
+      try {
+        if (!obj.containsKey('event')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['event'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(WorkspaceFoldersChangeEvent.canParse(obj['event'], reporter))) {
+          reporter.reportError("must be of type WorkspaceFoldersChangeEvent");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type DidChangeWorkspaceFoldersParams");
+      return false;
+    }
   }
 
   @override
@@ -3066,10 +4377,30 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('textDocument') &&
-        TextDocumentIdentifier.canParse(obj['textDocument']);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('textDocument');
+      try {
+        if (!obj.containsKey('textDocument')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['textDocument'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(TextDocumentIdentifier.canParse(obj['textDocument'], reporter))) {
+          reporter.reportError("must be of type TextDocumentIdentifier");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type DidCloseTextDocumentParams");
+      return false;
+    }
   }
 
   @override
@@ -3117,10 +4448,30 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('textDocument') &&
-        TextDocumentItem.canParse(obj['textDocument']);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('textDocument');
+      try {
+        if (!obj.containsKey('textDocument')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['textDocument'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(TextDocumentItem.canParse(obj['textDocument'], reporter))) {
+          reporter.reportError("must be of type TextDocumentItem");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type DidOpenTextDocumentParams");
+      return false;
+    }
   }
 
   @override
@@ -3176,11 +4527,39 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('textDocument') &&
-        TextDocumentIdentifier.canParse(obj['textDocument']) &&
-        (obj['text'] == null || obj['text'] is String);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('textDocument');
+      try {
+        if (!obj.containsKey('textDocument')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['textDocument'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(TextDocumentIdentifier.canParse(obj['textDocument'], reporter))) {
+          reporter.reportError("must be of type TextDocumentIdentifier");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('text');
+      try {
+        if (obj['text'] != null && !(obj['text'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type DidSaveTextDocumentParams");
+      return false;
+    }
   }
 
   @override
@@ -3250,11 +4629,40 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['language'] == null || obj['language'] is String) &&
-        (obj['scheme'] == null || obj['scheme'] is String) &&
-        (obj['pattern'] == null || obj['pattern'] is String);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('language');
+      try {
+        if (obj['language'] != null && !(obj['language'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('scheme');
+      try {
+        if (obj['scheme'] != null && !(obj['scheme'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('pattern');
+      try {
+        if (obj['pattern'] != null && !(obj['pattern'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type DocumentFilter");
+      return false;
+    }
   }
 
   @override
@@ -3318,12 +4726,47 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('textDocument') &&
-        TextDocumentIdentifier.canParse(obj['textDocument']) &&
-        obj.containsKey('options') &&
-        FormattingOptions.canParse(obj['options']);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('textDocument');
+      try {
+        if (!obj.containsKey('textDocument')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['textDocument'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(TextDocumentIdentifier.canParse(obj['textDocument'], reporter))) {
+          reporter.reportError("must be of type TextDocumentIdentifier");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('options');
+      try {
+        if (!obj.containsKey('options')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['options'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(FormattingOptions.canParse(obj['options'], reporter))) {
+          reporter.reportError("must be of type FormattingOptions");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type DocumentFormattingParams");
+      return false;
+    }
   }
 
   @override
@@ -3383,11 +4826,40 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('range') &&
-        Range.canParse(obj['range']) &&
-        (obj['kind'] == null || DocumentHighlightKind.canParse(obj['kind']));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('range');
+      try {
+        if (!obj.containsKey('range')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['range'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(Range.canParse(obj['range'], reporter))) {
+          reporter.reportError("must be of type Range");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('kind');
+      try {
+        if (obj['kind'] != null &&
+            !(DocumentHighlightKind.canParse(obj['kind'], reporter))) {
+          reporter.reportError("must be of type DocumentHighlightKind");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type DocumentHighlight");
+      return false;
+    }
   }
 
   @override
@@ -3417,7 +4889,7 @@
 
   final num _value;
 
-  static bool canParse(Object obj) {
+  static bool canParse(Object obj, LspJsonReporter reporter) {
     return obj is num;
   }
 
@@ -3481,12 +4953,48 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('range') &&
-        Range.canParse(obj['range']) &&
-        (obj['target'] == null || obj['target'] is String) &&
-        (obj['data'] == null || true);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('range');
+      try {
+        if (!obj.containsKey('range')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['range'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(Range.canParse(obj['range'], reporter))) {
+          reporter.reportError("must be of type Range");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('target');
+      try {
+        if (obj['target'] != null && !(obj['target'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('data');
+      try {
+        if (obj['data'] != null && !(true)) {
+          reporter.reportError("must be of type dynamic");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type DocumentLink");
+      return false;
+    }
   }
 
   @override
@@ -3535,9 +5043,23 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['resolveProvider'] == null || obj['resolveProvider'] is bool);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('resolveProvider');
+      try {
+        if (obj['resolveProvider'] != null &&
+            !(obj['resolveProvider'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type DocumentLinkOptions");
+      return false;
+    }
   }
 
   @override
@@ -3585,10 +5107,30 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('textDocument') &&
-        TextDocumentIdentifier.canParse(obj['textDocument']);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('textDocument');
+      try {
+        if (!obj.containsKey('textDocument')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['textDocument'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(TextDocumentIdentifier.canParse(obj['textDocument'], reporter))) {
+          reporter.reportError("must be of type TextDocumentIdentifier");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type DocumentLinkParams");
+      return false;
+    }
   }
 
   @override
@@ -3643,14 +5185,39 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['resolveProvider'] == null || obj['resolveProvider'] is bool) &&
-        obj.containsKey('documentSelector') &&
-        (obj['documentSelector'] == null ||
-            (obj['documentSelector'] is List &&
-                (obj['documentSelector']
-                    .every((item) => DocumentFilter.canParse(item)))));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('resolveProvider');
+      try {
+        if (obj['resolveProvider'] != null &&
+            !(obj['resolveProvider'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('documentSelector');
+      try {
+        if (!obj.containsKey('documentSelector')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['documentSelector'] != null &&
+            !((obj['documentSelector'] is List &&
+                (obj['documentSelector'].every(
+                    (item) => DocumentFilter.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<DocumentFilter>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type DocumentLinkRegistrationOptions");
+      return false;
+    }
   }
 
   @override
@@ -3713,13 +5280,42 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('firstTriggerCharacter') &&
-        obj['firstTriggerCharacter'] is String &&
-        (obj['moreTriggerCharacter'] == null ||
-            (obj['moreTriggerCharacter'] is List &&
-                (obj['moreTriggerCharacter'].every((item) => item is String))));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('firstTriggerCharacter');
+      try {
+        if (!obj.containsKey('firstTriggerCharacter')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['firstTriggerCharacter'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['firstTriggerCharacter'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('moreTriggerCharacter');
+      try {
+        if (obj['moreTriggerCharacter'] != null &&
+            !((obj['moreTriggerCharacter'] is List &&
+                (obj['moreTriggerCharacter']
+                    .every((item) => item is String))))) {
+          reporter.reportError("must be of type List<String>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type DocumentOnTypeFormattingOptions");
+      return false;
+    }
   }
 
   @override
@@ -3803,16 +5399,81 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('textDocument') &&
-        TextDocumentIdentifier.canParse(obj['textDocument']) &&
-        obj.containsKey('position') &&
-        Position.canParse(obj['position']) &&
-        obj.containsKey('ch') &&
-        obj['ch'] is String &&
-        obj.containsKey('options') &&
-        FormattingOptions.canParse(obj['options']);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('textDocument');
+      try {
+        if (!obj.containsKey('textDocument')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['textDocument'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(TextDocumentIdentifier.canParse(obj['textDocument'], reporter))) {
+          reporter.reportError("must be of type TextDocumentIdentifier");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('position');
+      try {
+        if (!obj.containsKey('position')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['position'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(Position.canParse(obj['position'], reporter))) {
+          reporter.reportError("must be of type Position");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('ch');
+      try {
+        if (!obj.containsKey('ch')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['ch'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['ch'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('options');
+      try {
+        if (!obj.containsKey('options')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['options'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(FormattingOptions.canParse(obj['options'], reporter))) {
+          reporter.reportError("must be of type FormattingOptions");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type DocumentOnTypeFormattingParams");
+      return false;
+    }
   }
 
   @override
@@ -3889,19 +5550,59 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('firstTriggerCharacter') &&
-        obj['firstTriggerCharacter'] is String &&
-        (obj['moreTriggerCharacter'] == null ||
-            (obj['moreTriggerCharacter'] is List &&
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('firstTriggerCharacter');
+      try {
+        if (!obj.containsKey('firstTriggerCharacter')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['firstTriggerCharacter'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['firstTriggerCharacter'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('moreTriggerCharacter');
+      try {
+        if (obj['moreTriggerCharacter'] != null &&
+            !((obj['moreTriggerCharacter'] is List &&
                 (obj['moreTriggerCharacter']
-                    .every((item) => item is String)))) &&
-        obj.containsKey('documentSelector') &&
-        (obj['documentSelector'] == null ||
-            (obj['documentSelector'] is List &&
-                (obj['documentSelector']
-                    .every((item) => DocumentFilter.canParse(item)))));
+                    .every((item) => item is String))))) {
+          reporter.reportError("must be of type List<String>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('documentSelector');
+      try {
+        if (!obj.containsKey('documentSelector')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['documentSelector'] != null &&
+            !((obj['documentSelector'] is List &&
+                (obj['documentSelector'].every(
+                    (item) => DocumentFilter.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<DocumentFilter>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError(
+          "must be of type DocumentOnTypeFormattingRegistrationOptions");
+      return false;
+    }
   }
 
   @override
@@ -3975,14 +5676,64 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('textDocument') &&
-        TextDocumentIdentifier.canParse(obj['textDocument']) &&
-        obj.containsKey('range') &&
-        Range.canParse(obj['range']) &&
-        obj.containsKey('options') &&
-        FormattingOptions.canParse(obj['options']);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('textDocument');
+      try {
+        if (!obj.containsKey('textDocument')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['textDocument'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(TextDocumentIdentifier.canParse(obj['textDocument'], reporter))) {
+          reporter.reportError("must be of type TextDocumentIdentifier");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('range');
+      try {
+        if (!obj.containsKey('range')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['range'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(Range.canParse(obj['range'], reporter))) {
+          reporter.reportError("must be of type Range");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('options');
+      try {
+        if (!obj.containsKey('options')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['options'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(FormattingOptions.canParse(obj['options'], reporter))) {
+          reporter.reportError("must be of type FormattingOptions");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type DocumentRangeFormattingParams");
+      return false;
+    }
   }
 
   @override
@@ -4096,22 +5847,111 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('name') &&
-        obj['name'] is String &&
-        (obj['detail'] == null || obj['detail'] is String) &&
-        obj.containsKey('kind') &&
-        SymbolKind.canParse(obj['kind']) &&
-        (obj['deprecated'] == null || obj['deprecated'] is bool) &&
-        obj.containsKey('range') &&
-        Range.canParse(obj['range']) &&
-        obj.containsKey('selectionRange') &&
-        Range.canParse(obj['selectionRange']) &&
-        (obj['children'] == null ||
-            (obj['children'] is List &&
-                (obj['children']
-                    .every((item) => DocumentSymbol.canParse(item)))));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('name');
+      try {
+        if (!obj.containsKey('name')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['name'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['name'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('detail');
+      try {
+        if (obj['detail'] != null && !(obj['detail'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('kind');
+      try {
+        if (!obj.containsKey('kind')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['kind'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(SymbolKind.canParse(obj['kind'], reporter))) {
+          reporter.reportError("must be of type SymbolKind");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('deprecated');
+      try {
+        if (obj['deprecated'] != null && !(obj['deprecated'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('range');
+      try {
+        if (!obj.containsKey('range')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['range'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(Range.canParse(obj['range'], reporter))) {
+          reporter.reportError("must be of type Range");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('selectionRange');
+      try {
+        if (!obj.containsKey('selectionRange')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['selectionRange'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(Range.canParse(obj['selectionRange'], reporter))) {
+          reporter.reportError("must be of type Range");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('children');
+      try {
+        if (obj['children'] != null &&
+            !((obj['children'] is List &&
+                (obj['children'].every(
+                    (item) => DocumentSymbol.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<DocumentSymbol>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type DocumentSymbol");
+      return false;
+    }
   }
 
   @override
@@ -4173,10 +6013,30 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('textDocument') &&
-        TextDocumentIdentifier.canParse(obj['textDocument']);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('textDocument');
+      try {
+        if (!obj.containsKey('textDocument')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['textDocument'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(TextDocumentIdentifier.canParse(obj['textDocument'], reporter))) {
+          reporter.reportError("must be of type TextDocumentIdentifier");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type DocumentSymbolParams");
+      return false;
+    }
   }
 
   @override
@@ -4204,7 +6064,7 @@
 
   final num _value;
 
-  static bool canParse(Object obj) {
+  static bool canParse(Object obj, LspJsonReporter reporter) {
     return obj is num;
   }
 
@@ -4260,11 +6120,31 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('commands') &&
-        (obj['commands'] is List &&
-            (obj['commands'].every((item) => item is String)));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('commands');
+      try {
+        if (!obj.containsKey('commands')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['commands'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!((obj['commands'] is List &&
+            (obj['commands'].every((item) => item is String))))) {
+          reporter.reportError("must be of type List<String>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type ExecuteCommandOptions");
+      return false;
+    }
   }
 
   @override
@@ -4320,13 +6200,41 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('command') &&
-        obj['command'] is String &&
-        (obj['arguments'] == null ||
-            (obj['arguments'] is List &&
-                (obj['arguments'].every((item) => true))));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('command');
+      try {
+        if (!obj.containsKey('command')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['command'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['command'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('arguments');
+      try {
+        if (obj['arguments'] != null &&
+            !((obj['arguments'] is List &&
+                (obj['arguments'].every((item) => true))))) {
+          reporter.reportError("must be of type List<dynamic>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type ExecuteCommandParams");
+      return false;
+    }
   }
 
   @override
@@ -4379,11 +6287,31 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('commands') &&
-        (obj['commands'] is List &&
-            (obj['commands'].every((item) => item is String)));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('commands');
+      try {
+        if (!obj.containsKey('commands')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['commands'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!((obj['commands'] is List &&
+            (obj['commands'].every((item) => item is String))))) {
+          reporter.reportError("must be of type List<String>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type ExecuteCommandRegistrationOptions");
+      return false;
+    }
   }
 
   @override
@@ -4413,7 +6341,7 @@
 
   final String _value;
 
-  static bool canParse(Object obj) {
+  static bool canParse(Object obj, LspJsonReporter reporter) {
     switch (obj) {
       case 'abort':
       case 'transactional':
@@ -4461,7 +6389,7 @@
 
   final num _value;
 
-  static bool canParse(Object obj) {
+  static bool canParse(Object obj, LspJsonReporter reporter) {
     return obj is num;
   }
 
@@ -4517,12 +6445,47 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('uri') &&
-        obj['uri'] is String &&
-        obj.containsKey('type') &&
-        obj['type'] is num;
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('uri');
+      try {
+        if (!obj.containsKey('uri')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['uri'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['uri'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('type');
+      try {
+        if (!obj.containsKey('type')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['type'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['type'] is num)) {
+          reporter.reportError("must be of type num");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type FileEvent");
+      return false;
+    }
   }
 
   @override
@@ -4589,11 +6552,40 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('globPattern') &&
-        obj['globPattern'] is String &&
-        (obj['kind'] == null || WatchKind.canParse(obj['kind']));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('globPattern');
+      try {
+        if (!obj.containsKey('globPattern')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['globPattern'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['globPattern'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('kind');
+      try {
+        if (obj['kind'] != null &&
+            !(WatchKind.canParse(obj['kind'], reporter))) {
+          reporter.reportError("must be of type WatchKind");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type FileSystemWatcher");
+      return false;
+    }
   }
 
   @override
@@ -4679,15 +6671,75 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('startLine') &&
-        obj['startLine'] is num &&
-        (obj['startCharacter'] == null || obj['startCharacter'] is num) &&
-        obj.containsKey('endLine') &&
-        obj['endLine'] is num &&
-        (obj['endCharacter'] == null || obj['endCharacter'] is num) &&
-        (obj['kind'] == null || FoldingRangeKind.canParse(obj['kind']));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('startLine');
+      try {
+        if (!obj.containsKey('startLine')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['startLine'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['startLine'] is num)) {
+          reporter.reportError("must be of type num");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('startCharacter');
+      try {
+        if (obj['startCharacter'] != null && !(obj['startCharacter'] is num)) {
+          reporter.reportError("must be of type num");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('endLine');
+      try {
+        if (!obj.containsKey('endLine')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['endLine'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['endLine'] is num)) {
+          reporter.reportError("must be of type num");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('endCharacter');
+      try {
+        if (obj['endCharacter'] != null && !(obj['endCharacter'] is num)) {
+          reporter.reportError("must be of type num");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('kind');
+      try {
+        if (obj['kind'] != null &&
+            !(FoldingRangeKind.canParse(obj['kind'], reporter))) {
+          reporter.reportError("must be of type FoldingRangeKind");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type FoldingRange");
+      return false;
+    }
   }
 
   @override
@@ -4725,7 +6777,7 @@
 
   final String _value;
 
-  static bool canParse(Object obj) {
+  static bool canParse(Object obj, LspJsonReporter reporter) {
     return obj is String;
   }
 
@@ -4775,10 +6827,30 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('textDocument') &&
-        TextDocumentIdentifier.canParse(obj['textDocument']);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('textDocument');
+      try {
+        if (!obj.containsKey('textDocument')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['textDocument'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(TextDocumentIdentifier.canParse(obj['textDocument'], reporter))) {
+          reporter.reportError("must be of type TextDocumentIdentifier");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type FoldingRangeParams");
+      return false;
+    }
   }
 
   @override
@@ -4815,8 +6887,13 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic>;
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      return true;
+    } else {
+      reporter.reportError("must be of type FoldingRangeProviderOptions");
+      return false;
+    }
   }
 
   @override
@@ -4871,12 +6948,47 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('tabSize') &&
-        obj['tabSize'] is num &&
-        obj.containsKey('insertSpaces') &&
-        obj['insertSpaces'] is bool;
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('tabSize');
+      try {
+        if (!obj.containsKey('tabSize')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['tabSize'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['tabSize'] is num)) {
+          reporter.reportError("must be of type num");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('insertSpaces');
+      try {
+        if (!obj.containsKey('insertSpaces')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['insertSpaces'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['insertSpaces'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type FormattingOptions");
+      return false;
+    }
   }
 
   @override
@@ -4914,7 +7026,7 @@
   static Hover fromJson(Map<String, dynamic> json) {
     final contents = json['contents'] is String
         ? new Either2<String, MarkupContent>.t1(json['contents'])
-        : (MarkupContent.canParse(json['contents'])
+        : (MarkupContent.canParse(json['contents'], nullLspJsonReporter)
             ? new Either2<String, MarkupContent>.t2(json['contents'] != null
                 ? MarkupContent.fromJson(json['contents'])
                 : null)
@@ -4940,12 +7052,41 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('contents') &&
-        (obj['contents'] is String ||
-            MarkupContent.canParse(obj['contents'])) &&
-        (obj['range'] == null || Range.canParse(obj['range']));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('contents');
+      try {
+        if (!obj.containsKey('contents')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['contents'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!((obj['contents'] is String ||
+            MarkupContent.canParse(obj['contents'], reporter)))) {
+          reporter
+              .reportError("must be of type Either2<String, MarkupContent>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('range');
+      try {
+        if (obj['range'] != null && !(Range.canParse(obj['range'], reporter))) {
+          reporter.reportError("must be of type Range");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type Hover");
+      return false;
+    }
   }
 
   @override
@@ -5054,21 +7195,95 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('processId') &&
-        (obj['processId'] == null || obj['processId'] is num) &&
-        (obj['rootPath'] == null || obj['rootPath'] is String) &&
-        obj.containsKey('rootUri') &&
-        (obj['rootUri'] == null || obj['rootUri'] is String) &&
-        (obj['initializationOptions'] == null || true) &&
-        obj.containsKey('capabilities') &&
-        ClientCapabilities.canParse(obj['capabilities']) &&
-        (obj['trace'] == null || obj['trace'] is String) &&
-        (obj['workspaceFolders'] == null ||
-            (obj['workspaceFolders'] is List &&
-                (obj['workspaceFolders']
-                    .every((item) => WorkspaceFolder.canParse(item)))));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('processId');
+      try {
+        if (!obj.containsKey('processId')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['processId'] != null && !(obj['processId'] is num)) {
+          reporter.reportError("must be of type num");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('rootPath');
+      try {
+        if (obj['rootPath'] != null && !(obj['rootPath'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('rootUri');
+      try {
+        if (!obj.containsKey('rootUri')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['rootUri'] != null && !(obj['rootUri'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('initializationOptions');
+      try {
+        if (obj['initializationOptions'] != null && !(true)) {
+          reporter.reportError("must be of type dynamic");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('capabilities');
+      try {
+        if (!obj.containsKey('capabilities')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['capabilities'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(ClientCapabilities.canParse(obj['capabilities'], reporter))) {
+          reporter.reportError("must be of type ClientCapabilities");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('trace');
+      try {
+        if (obj['trace'] != null && !(obj['trace'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('workspaceFolders');
+      try {
+        if (obj['workspaceFolders'] != null &&
+            !((obj['workspaceFolders'] is List &&
+                (obj['workspaceFolders'].every(
+                    (item) => WorkspaceFolder.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<WorkspaceFolder>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type InitializeParams");
+      return false;
+    }
   }
 
   @override
@@ -5130,10 +7345,30 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('capabilities') &&
-        ServerCapabilities.canParse(obj['capabilities']);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('capabilities');
+      try {
+        if (!obj.containsKey('capabilities')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['capabilities'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(ServerCapabilities.canParse(obj['capabilities'], reporter))) {
+          reporter.reportError("must be of type ServerCapabilities");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type InitializeResult");
+      return false;
+    }
   }
 
   @override
@@ -5168,8 +7403,13 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic>;
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      return true;
+    } else {
+      reporter.reportError("must be of type InitializedParams");
+      return false;
+    }
   }
 
   @override
@@ -5198,7 +7438,7 @@
 
   final num _value;
 
-  static bool canParse(Object obj) {
+  static bool canParse(Object obj, LspJsonReporter reporter) {
     switch (obj) {
       case 1:
       case 2:
@@ -5257,12 +7497,47 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('uri') &&
-        obj['uri'] is String &&
-        obj.containsKey('range') &&
-        Range.canParse(obj['range']);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('uri');
+      try {
+        if (!obj.containsKey('uri')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['uri'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['uri'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('range');
+      try {
+        if (!obj.containsKey('range')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['range'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(Range.canParse(obj['range'], reporter))) {
+          reporter.reportError("must be of type Range");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type Location");
+      return false;
+    }
   }
 
   @override
@@ -5350,16 +7625,74 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['originSelectionRange'] == null ||
-            Range.canParse(obj['originSelectionRange'])) &&
-        obj.containsKey('targetUri') &&
-        obj['targetUri'] is String &&
-        obj.containsKey('targetRange') &&
-        Range.canParse(obj['targetRange']) &&
-        obj.containsKey('targetSelectionRange') &&
-        Range.canParse(obj['targetSelectionRange']);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('originSelectionRange');
+      try {
+        if (obj['originSelectionRange'] != null &&
+            !(Range.canParse(obj['originSelectionRange'], reporter))) {
+          reporter.reportError("must be of type Range");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('targetUri');
+      try {
+        if (!obj.containsKey('targetUri')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['targetUri'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['targetUri'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('targetRange');
+      try {
+        if (!obj.containsKey('targetRange')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['targetRange'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(Range.canParse(obj['targetRange'], reporter))) {
+          reporter.reportError("must be of type Range");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('targetSelectionRange');
+      try {
+        if (!obj.containsKey('targetSelectionRange')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['targetSelectionRange'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(Range.canParse(obj['targetSelectionRange'], reporter))) {
+          reporter.reportError("must be of type Range");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type LocationLink");
+      return false;
+    }
   }
 
   @override
@@ -5421,12 +7754,47 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('type') &&
-        MessageType.canParse(obj['type']) &&
-        obj.containsKey('message') &&
-        obj['message'] is String;
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('type');
+      try {
+        if (!obj.containsKey('type')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['type'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(MessageType.canParse(obj['type'], reporter))) {
+          reporter.reportError("must be of type MessageType");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('message');
+      try {
+        if (!obj.containsKey('message')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['message'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['message'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type LogMessageParams");
+      return false;
+    }
   }
 
   @override
@@ -5503,12 +7871,47 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('kind') &&
-        MarkupKind.canParse(obj['kind']) &&
-        obj.containsKey('value') &&
-        obj['value'] is String;
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('kind');
+      try {
+        if (!obj.containsKey('kind')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['kind'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(MarkupKind.canParse(obj['kind'], reporter))) {
+          reporter.reportError("must be of type MarkupKind");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('value');
+      try {
+        if (!obj.containsKey('value')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['value'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['value'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type MarkupContent");
+      return false;
+    }
   }
 
   @override
@@ -5542,7 +7945,7 @@
 
   final String _value;
 
-  static bool canParse(Object obj) {
+  static bool canParse(Object obj, LspJsonReporter reporter) {
     switch (obj) {
       case r'plaintext':
       case r'markdown':
@@ -5578,13 +7981,13 @@
     }
   }
   static Message fromJson(Map<String, dynamic> json) {
-    if (RequestMessage.canParse(json)) {
+    if (RequestMessage.canParse(json, nullLspJsonReporter)) {
       return RequestMessage.fromJson(json);
     }
-    if (ResponseMessage.canParse(json)) {
+    if (ResponseMessage.canParse(json, nullLspJsonReporter)) {
       return ResponseMessage.fromJson(json);
     }
-    if (NotificationMessage.canParse(json)) {
+    if (NotificationMessage.canParse(json, nullLspJsonReporter)) {
       return NotificationMessage.fromJson(json);
     }
     final jsonrpc = json['jsonrpc'];
@@ -5600,10 +8003,30 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('jsonrpc') &&
-        obj['jsonrpc'] is String;
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('jsonrpc');
+      try {
+        if (!obj.containsKey('jsonrpc')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['jsonrpc'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['jsonrpc'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type Message");
+      return false;
+    }
   }
 
   @override
@@ -5648,10 +8071,30 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('title') &&
-        obj['title'] is String;
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('title');
+      try {
+        if (!obj.containsKey('title')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['title'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['title'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type MessageActionItem");
+      return false;
+    }
   }
 
   @override
@@ -5679,7 +8122,7 @@
 
   final num _value;
 
-  static bool canParse(Object obj) {
+  static bool canParse(Object obj, LspJsonReporter reporter) {
     return obj is num;
   }
 
@@ -5713,7 +8156,7 @@
 
   final String _value;
 
-  static bool canParse(Object obj) {
+  static bool canParse(Object obj, LspJsonReporter reporter) {
     return obj is String;
   }
 
@@ -5935,13 +8378,56 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('method') &&
-        Method.canParse(obj['method']) &&
-        (obj['params'] == null || true) &&
-        obj.containsKey('jsonrpc') &&
-        obj['jsonrpc'] is String;
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('method');
+      try {
+        if (!obj.containsKey('method')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['method'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(Method.canParse(obj['method'], reporter))) {
+          reporter.reportError("must be of type Method");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('params');
+      try {
+        if (obj['params'] != null && !(true)) {
+          reporter.reportError("must be of type dynamic");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('jsonrpc');
+      try {
+        if (!obj.containsKey('jsonrpc')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['jsonrpc'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['jsonrpc'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type NotificationMessage");
+      return false;
+    }
   }
 
   @override
@@ -5983,7 +8469,7 @@
     final label = json['label'];
     final documentation = json['documentation'] is String
         ? new Either2<String, MarkupContent>.t1(json['documentation'])
-        : (MarkupContent.canParse(json['documentation'])
+        : (MarkupContent.canParse(json['documentation'], nullLspJsonReporter)
             ? new Either2<String, MarkupContent>.t2(
                 json['documentation'] != null
                     ? MarkupContent.fromJson(json['documentation'])
@@ -6019,13 +8505,42 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('label') &&
-        obj['label'] is String &&
-        (obj['documentation'] == null ||
-            (obj['documentation'] is String ||
-                MarkupContent.canParse(obj['documentation'])));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('label');
+      try {
+        if (!obj.containsKey('label')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['label'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['label'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('documentation');
+      try {
+        if (obj['documentation'] != null &&
+            !((obj['documentation'] is String ||
+                MarkupContent.canParse(obj['documentation'], reporter)))) {
+          reporter
+              .reportError("must be of type Either2<String, MarkupContent>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type ParameterInformation");
+      return false;
+    }
   }
 
   @override
@@ -6087,12 +8602,47 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('line') &&
-        obj['line'] is num &&
-        obj.containsKey('character') &&
-        obj['character'] is num;
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('line');
+      try {
+        if (!obj.containsKey('line')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['line'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['line'] is num)) {
+          reporter.reportError("must be of type num");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('character');
+      try {
+        if (!obj.containsKey('character')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['character'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['character'] is num)) {
+          reporter.reportError("must be of type num");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type Position");
+      return false;
+    }
   }
 
   @override
@@ -6150,13 +8700,49 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('uri') &&
-        obj['uri'] is String &&
-        obj.containsKey('diagnostics') &&
-        (obj['diagnostics'] is List &&
-            (obj['diagnostics'].every((item) => Diagnostic.canParse(item))));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('uri');
+      try {
+        if (!obj.containsKey('uri')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['uri'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['uri'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('diagnostics');
+      try {
+        if (!obj.containsKey('diagnostics')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['diagnostics'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!((obj['diagnostics'] is List &&
+            (obj['diagnostics']
+                .every((item) => Diagnostic.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<Diagnostic>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type PublishDiagnosticsParams");
+      return false;
+    }
   }
 
   @override
@@ -6214,12 +8800,47 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('start') &&
-        Position.canParse(obj['start']) &&
-        obj.containsKey('end') &&
-        Position.canParse(obj['end']);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('start');
+      try {
+        if (!obj.containsKey('start')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['start'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(Position.canParse(obj['start'], reporter))) {
+          reporter.reportError("must be of type Position");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('end');
+      try {
+        if (!obj.containsKey('end')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['end'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(Position.canParse(obj['end'], reporter))) {
+          reporter.reportError("must be of type Position");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type Range");
+      return false;
+    }
   }
 
   @override
@@ -6271,12 +8892,47 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('range') &&
-        Range.canParse(obj['range']) &&
-        obj.containsKey('placeholder') &&
-        obj['placeholder'] is String;
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('range');
+      try {
+        if (!obj.containsKey('range')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['range'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(Range.canParse(obj['range'], reporter))) {
+          reporter.reportError("must be of type Range");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('placeholder');
+      try {
+        if (!obj.containsKey('placeholder')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['placeholder'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['placeholder'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type RangeAndPlaceholder");
+      return false;
+    }
   }
 
   @override
@@ -6323,10 +8979,30 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('includeDeclaration') &&
-        obj['includeDeclaration'] is bool;
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('includeDeclaration');
+      try {
+        if (!obj.containsKey('includeDeclaration')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['includeDeclaration'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['includeDeclaration'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type ReferenceContext");
+      return false;
+    }
   }
 
   @override
@@ -6394,14 +9070,64 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('context') &&
-        ReferenceContext.canParse(obj['context']) &&
-        obj.containsKey('textDocument') &&
-        TextDocumentIdentifier.canParse(obj['textDocument']) &&
-        obj.containsKey('position') &&
-        Position.canParse(obj['position']);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('context');
+      try {
+        if (!obj.containsKey('context')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['context'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(ReferenceContext.canParse(obj['context'], reporter))) {
+          reporter.reportError("must be of type ReferenceContext");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('textDocument');
+      try {
+        if (!obj.containsKey('textDocument')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['textDocument'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(TextDocumentIdentifier.canParse(obj['textDocument'], reporter))) {
+          reporter.reportError("must be of type TextDocumentIdentifier");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('position');
+      try {
+        if (!obj.containsKey('position')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['position'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(Position.canParse(obj['position'], reporter))) {
+          reporter.reportError("must be of type Position");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type ReferenceParams");
+      return false;
+    }
   }
 
   @override
@@ -6468,13 +9194,56 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('id') &&
-        obj['id'] is String &&
-        obj.containsKey('method') &&
-        obj['method'] is String &&
-        (obj['registerOptions'] == null || true);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('id');
+      try {
+        if (!obj.containsKey('id')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['id'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['id'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('method');
+      try {
+        if (!obj.containsKey('method')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['method'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['method'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('registerOptions');
+      try {
+        if (obj['registerOptions'] != null && !(true)) {
+          reporter.reportError("must be of type dynamic");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type Registration");
+      return false;
+    }
   }
 
   @override
@@ -6527,12 +9296,32 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('registrations') &&
-        (obj['registrations'] is List &&
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('registrations');
+      try {
+        if (!obj.containsKey('registrations')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['registrations'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!((obj['registrations'] is List &&
             (obj['registrations']
-                .every((item) => Registration.canParse(item))));
+                .every((item) => Registration.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<Registration>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type RegistrationParams");
+      return false;
+    }
   }
 
   @override
@@ -6605,15 +9394,74 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('kind') &&
-        obj['kind'] is String &&
-        obj.containsKey('oldUri') &&
-        obj['oldUri'] is String &&
-        obj.containsKey('newUri') &&
-        obj['newUri'] is String &&
-        (obj['options'] == null || RenameFileOptions.canParse(obj['options']));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('kind');
+      try {
+        if (!obj.containsKey('kind')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['kind'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['kind'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('oldUri');
+      try {
+        if (!obj.containsKey('oldUri')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['oldUri'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['oldUri'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('newUri');
+      try {
+        if (!obj.containsKey('newUri')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['newUri'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['newUri'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('options');
+      try {
+        if (obj['options'] != null &&
+            !(RenameFileOptions.canParse(obj['options'], reporter))) {
+          reporter.reportError("must be of type RenameFileOptions");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type RenameFile");
+      return false;
+    }
   }
 
   @override
@@ -6671,10 +9519,31 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['overwrite'] == null || obj['overwrite'] is bool) &&
-        (obj['ignoreIfExists'] == null || obj['ignoreIfExists'] is bool);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('overwrite');
+      try {
+        if (obj['overwrite'] != null && !(obj['overwrite'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('ignoreIfExists');
+      try {
+        if (obj['ignoreIfExists'] != null && !(obj['ignoreIfExists'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type RenameFileOptions");
+      return false;
+    }
   }
 
   @override
@@ -6721,9 +9590,23 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['prepareProvider'] == null || obj['prepareProvider'] is bool);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('prepareProvider');
+      try {
+        if (obj['prepareProvider'] != null &&
+            !(obj['prepareProvider'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type RenameOptions");
+      return false;
+    }
   }
 
   @override
@@ -6791,14 +9674,64 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('textDocument') &&
-        TextDocumentIdentifier.canParse(obj['textDocument']) &&
-        obj.containsKey('position') &&
-        Position.canParse(obj['position']) &&
-        obj.containsKey('newName') &&
-        obj['newName'] is String;
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('textDocument');
+      try {
+        if (!obj.containsKey('textDocument')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['textDocument'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(TextDocumentIdentifier.canParse(obj['textDocument'], reporter))) {
+          reporter.reportError("must be of type TextDocumentIdentifier");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('position');
+      try {
+        if (!obj.containsKey('position')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['position'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(Position.canParse(obj['position'], reporter))) {
+          reporter.reportError("must be of type Position");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('newName');
+      try {
+        if (!obj.containsKey('newName')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['newName'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['newName'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type RenameParams");
+      return false;
+    }
   }
 
   @override
@@ -6856,14 +9789,39 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['prepareProvider'] == null || obj['prepareProvider'] is bool) &&
-        obj.containsKey('documentSelector') &&
-        (obj['documentSelector'] == null ||
-            (obj['documentSelector'] is List &&
-                (obj['documentSelector']
-                    .every((item) => DocumentFilter.canParse(item)))));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('prepareProvider');
+      try {
+        if (obj['prepareProvider'] != null &&
+            !(obj['prepareProvider'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('documentSelector');
+      try {
+        if (!obj.containsKey('documentSelector')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['documentSelector'] != null &&
+            !((obj['documentSelector'] is List &&
+                (obj['documentSelector'].every(
+                    (item) => DocumentFilter.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<DocumentFilter>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type RenameRegistrationOptions");
+      return false;
+    }
   }
 
   @override
@@ -6938,15 +9896,73 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('id') &&
-        (obj['id'] is num || obj['id'] is String) &&
-        obj.containsKey('method') &&
-        Method.canParse(obj['method']) &&
-        (obj['params'] == null || true) &&
-        obj.containsKey('jsonrpc') &&
-        obj['jsonrpc'] is String;
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('id');
+      try {
+        if (!obj.containsKey('id')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['id'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!((obj['id'] is num || obj['id'] is String))) {
+          reporter.reportError("must be of type Either2<num, String>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('method');
+      try {
+        if (!obj.containsKey('method')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['method'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(Method.canParse(obj['method'], reporter))) {
+          reporter.reportError("must be of type Method");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('params');
+      try {
+        if (obj['params'] != null && !(true)) {
+          reporter.reportError("must be of type dynamic");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('jsonrpc');
+      try {
+        if (!obj.containsKey('jsonrpc')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['jsonrpc'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['jsonrpc'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type RequestMessage");
+      return false;
+    }
   }
 
   @override
@@ -6981,7 +9997,7 @@
 
   final String _value;
 
-  static bool canParse(Object obj) {
+  static bool canParse(Object obj, LspJsonReporter reporter) {
     switch (obj) {
       case 'create':
       case 'rename':
@@ -7052,13 +10068,56 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('code') &&
-        ErrorCodes.canParse(obj['code']) &&
-        obj.containsKey('message') &&
-        obj['message'] is String &&
-        (obj['data'] == null || obj['data'] is String);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('code');
+      try {
+        if (!obj.containsKey('code')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['code'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(ErrorCodes.canParse(obj['code'], reporter))) {
+          reporter.reportError("must be of type ErrorCodes");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('message');
+      try {
+        if (!obj.containsKey('message')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['message'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['message'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('data');
+      try {
+        if (obj['data'] != null && !(obj['data'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type ResponseError<D>");
+      return false;
+    }
   }
 
   @override
@@ -7136,14 +10195,62 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('id') &&
-        (obj['id'] == null || (obj['id'] is num || obj['id'] is String)) &&
-        (obj['result'] == null || true) &&
-        (obj['error'] == null || ResponseError.canParse(obj['error'])) &&
-        obj.containsKey('jsonrpc') &&
-        obj['jsonrpc'] is String;
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('id');
+      try {
+        if (!obj.containsKey('id')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['id'] != null && !((obj['id'] is num || obj['id'] is String))) {
+          reporter.reportError("must be of type Either2<num, String>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('result');
+      try {
+        if (obj['result'] != null && !(true)) {
+          reporter.reportError("must be of type dynamic");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('error');
+      try {
+        if (obj['error'] != null &&
+            !(ResponseError.canParse(obj['error'], reporter))) {
+          reporter.reportError("must be of type ResponseError<dynamic>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('jsonrpc');
+      try {
+        if (!obj.containsKey('jsonrpc')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['jsonrpc'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['jsonrpc'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type ResponseMessage");
+      return false;
+    }
   }
 
   @override
@@ -7194,9 +10301,22 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['includeText'] == null || obj['includeText'] is bool);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('includeText');
+      try {
+        if (obj['includeText'] != null && !(obj['includeText'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type SaveOptions");
+      return false;
+    }
   }
 
   @override
@@ -7249,7 +10369,7 @@
       this.experimental);
   static ServerCapabilities fromJson(Map<String, dynamic> json) {
     final textDocumentSync = TextDocumentSyncOptions.canParse(
-            json['textDocumentSync'])
+            json['textDocumentSync'], nullLspJsonReporter)
         ? new Either2<TextDocumentSyncOptions, num>.t1(
             json['textDocumentSync'] != null
                 ? TextDocumentSyncOptions.fromJson(json['textDocumentSync'])
@@ -7276,7 +10396,8 @@
     final workspaceSymbolProvider = json['workspaceSymbolProvider'];
     final codeActionProvider = json['codeActionProvider'] is bool
         ? new Either2<bool, CodeActionOptions>.t1(json['codeActionProvider'])
-        : (CodeActionOptions.canParse(json['codeActionProvider'])
+        : (CodeActionOptions.canParse(
+                json['codeActionProvider'], nullLspJsonReporter)
             ? new Either2<bool, CodeActionOptions>.t2(
                 json['codeActionProvider'] != null
                     ? CodeActionOptions.fromJson(json['codeActionProvider'])
@@ -7297,7 +10418,7 @@
             : null;
     final renameProvider = json['renameProvider'] is bool
         ? new Either2<bool, RenameOptions>.t1(json['renameProvider'])
-        : (RenameOptions.canParse(json['renameProvider'])
+        : (RenameOptions.canParse(json['renameProvider'], nullLspJsonReporter)
             ? new Either2<bool, RenameOptions>.t2(json['renameProvider'] != null
                 ? RenameOptions.fromJson(json['renameProvider'])
                 : null)
@@ -7512,53 +10633,260 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['textDocumentSync'] == null ||
-            (TextDocumentSyncOptions.canParse(obj['textDocumentSync']) ||
-                obj['textDocumentSync'] is num)) &&
-        (obj['hoverProvider'] == null || obj['hoverProvider'] is bool) &&
-        (obj['completionProvider'] == null ||
-            CompletionOptions.canParse(obj['completionProvider'])) &&
-        (obj['signatureHelpProvider'] == null ||
-            SignatureHelpOptions.canParse(obj['signatureHelpProvider'])) &&
-        (obj['definitionProvider'] == null ||
-            obj['definitionProvider'] is bool) &&
-        (obj['typeDefinitionProvider'] == null || true) &&
-        (obj['implementationProvider'] == null || true) &&
-        (obj['referencesProvider'] == null ||
-            obj['referencesProvider'] is bool) &&
-        (obj['documentHighlightProvider'] == null ||
-            obj['documentHighlightProvider'] is bool) &&
-        (obj['documentSymbolProvider'] == null ||
-            obj['documentSymbolProvider'] is bool) &&
-        (obj['workspaceSymbolProvider'] == null ||
-            obj['workspaceSymbolProvider'] is bool) &&
-        (obj['codeActionProvider'] == null ||
-            (obj['codeActionProvider'] is bool ||
-                CodeActionOptions.canParse(obj['codeActionProvider']))) &&
-        (obj['codeLensProvider'] == null ||
-            CodeLensOptions.canParse(obj['codeLensProvider'])) &&
-        (obj['documentFormattingProvider'] == null ||
-            obj['documentFormattingProvider'] is bool) &&
-        (obj['documentRangeFormattingProvider'] == null ||
-            obj['documentRangeFormattingProvider'] is bool) &&
-        (obj['documentOnTypeFormattingProvider'] == null ||
-            DocumentOnTypeFormattingOptions.canParse(
-                obj['documentOnTypeFormattingProvider'])) &&
-        (obj['renameProvider'] == null ||
-            (obj['renameProvider'] is bool ||
-                RenameOptions.canParse(obj['renameProvider']))) &&
-        (obj['documentLinkProvider'] == null ||
-            DocumentLinkOptions.canParse(obj['documentLinkProvider'])) &&
-        (obj['colorProvider'] == null || true) &&
-        (obj['foldingRangeProvider'] == null || true) &&
-        (obj['declarationProvider'] == null || true) &&
-        (obj['executeCommandProvider'] == null ||
-            ExecuteCommandOptions.canParse(obj['executeCommandProvider'])) &&
-        (obj['workspace'] == null ||
-            ServerCapabilitiesWorkspace.canParse(obj['workspace'])) &&
-        (obj['experimental'] == null || true);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('textDocumentSync');
+      try {
+        if (obj['textDocumentSync'] != null &&
+            !((TextDocumentSyncOptions.canParse(
+                    obj['textDocumentSync'], reporter) ||
+                obj['textDocumentSync'] is num))) {
+          reporter.reportError(
+              "must be of type Either2<TextDocumentSyncOptions, num>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('hoverProvider');
+      try {
+        if (obj['hoverProvider'] != null && !(obj['hoverProvider'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('completionProvider');
+      try {
+        if (obj['completionProvider'] != null &&
+            !(CompletionOptions.canParse(
+                obj['completionProvider'], reporter))) {
+          reporter.reportError("must be of type CompletionOptions");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('signatureHelpProvider');
+      try {
+        if (obj['signatureHelpProvider'] != null &&
+            !(SignatureHelpOptions.canParse(
+                obj['signatureHelpProvider'], reporter))) {
+          reporter.reportError("must be of type SignatureHelpOptions");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('definitionProvider');
+      try {
+        if (obj['definitionProvider'] != null &&
+            !(obj['definitionProvider'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('typeDefinitionProvider');
+      try {
+        if (obj['typeDefinitionProvider'] != null && !(true)) {
+          reporter.reportError("must be of type dynamic");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('implementationProvider');
+      try {
+        if (obj['implementationProvider'] != null && !(true)) {
+          reporter.reportError("must be of type dynamic");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('referencesProvider');
+      try {
+        if (obj['referencesProvider'] != null &&
+            !(obj['referencesProvider'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('documentHighlightProvider');
+      try {
+        if (obj['documentHighlightProvider'] != null &&
+            !(obj['documentHighlightProvider'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('documentSymbolProvider');
+      try {
+        if (obj['documentSymbolProvider'] != null &&
+            !(obj['documentSymbolProvider'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('workspaceSymbolProvider');
+      try {
+        if (obj['workspaceSymbolProvider'] != null &&
+            !(obj['workspaceSymbolProvider'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('codeActionProvider');
+      try {
+        if (obj['codeActionProvider'] != null &&
+            !((obj['codeActionProvider'] is bool ||
+                CodeActionOptions.canParse(
+                    obj['codeActionProvider'], reporter)))) {
+          reporter
+              .reportError("must be of type Either2<bool, CodeActionOptions>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('codeLensProvider');
+      try {
+        if (obj['codeLensProvider'] != null &&
+            !(CodeLensOptions.canParse(obj['codeLensProvider'], reporter))) {
+          reporter.reportError("must be of type CodeLensOptions");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('documentFormattingProvider');
+      try {
+        if (obj['documentFormattingProvider'] != null &&
+            !(obj['documentFormattingProvider'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('documentRangeFormattingProvider');
+      try {
+        if (obj['documentRangeFormattingProvider'] != null &&
+            !(obj['documentRangeFormattingProvider'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('documentOnTypeFormattingProvider');
+      try {
+        if (obj['documentOnTypeFormattingProvider'] != null &&
+            !(DocumentOnTypeFormattingOptions.canParse(
+                obj['documentOnTypeFormattingProvider'], reporter))) {
+          reporter
+              .reportError("must be of type DocumentOnTypeFormattingOptions");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('renameProvider');
+      try {
+        if (obj['renameProvider'] != null &&
+            !((obj['renameProvider'] is bool ||
+                RenameOptions.canParse(obj['renameProvider'], reporter)))) {
+          reporter.reportError("must be of type Either2<bool, RenameOptions>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('documentLinkProvider');
+      try {
+        if (obj['documentLinkProvider'] != null &&
+            !(DocumentLinkOptions.canParse(
+                obj['documentLinkProvider'], reporter))) {
+          reporter.reportError("must be of type DocumentLinkOptions");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('colorProvider');
+      try {
+        if (obj['colorProvider'] != null && !(true)) {
+          reporter.reportError("must be of type dynamic");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('foldingRangeProvider');
+      try {
+        if (obj['foldingRangeProvider'] != null && !(true)) {
+          reporter.reportError("must be of type dynamic");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('declarationProvider');
+      try {
+        if (obj['declarationProvider'] != null && !(true)) {
+          reporter.reportError("must be of type dynamic");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('executeCommandProvider');
+      try {
+        if (obj['executeCommandProvider'] != null &&
+            !(ExecuteCommandOptions.canParse(
+                obj['executeCommandProvider'], reporter))) {
+          reporter.reportError("must be of type ExecuteCommandOptions");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('workspace');
+      try {
+        if (obj['workspace'] != null &&
+            !(ServerCapabilitiesWorkspace.canParse(
+                obj['workspace'], reporter))) {
+          reporter.reportError("must be of type ServerCapabilitiesWorkspace");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('experimental');
+      try {
+        if (obj['experimental'] != null && !(true)) {
+          reporter.reportError("must be of type dynamic");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type ServerCapabilities");
+      return false;
+    }
   }
 
   @override
@@ -7657,11 +10985,25 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['workspaceFolders'] == null ||
-            ServerCapabilitiesWorkspaceFolders.canParse(
-                obj['workspaceFolders']));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('workspaceFolders');
+      try {
+        if (obj['workspaceFolders'] != null &&
+            !(ServerCapabilitiesWorkspaceFolders.canParse(
+                obj['workspaceFolders'], reporter))) {
+          reporter.reportError(
+              "must be of type ServerCapabilitiesWorkspaceFolders");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type ServerCapabilitiesWorkspace");
+      return false;
+    }
   }
 
   @override
@@ -7719,11 +11061,33 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['supported'] == null || obj['supported'] is bool) &&
-        (obj['changeNotifications'] == null ||
-            obj['changeNotifications'] is bool);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('supported');
+      try {
+        if (obj['supported'] != null && !(obj['supported'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('changeNotifications');
+      try {
+        if (obj['changeNotifications'] != null &&
+            !(obj['changeNotifications'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter
+          .reportError("must be of type ServerCapabilitiesWorkspaceFolders");
+      return false;
+    }
   }
 
   @override
@@ -7781,12 +11145,47 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('type') &&
-        MessageType.canParse(obj['type']) &&
-        obj.containsKey('message') &&
-        obj['message'] is String;
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('type');
+      try {
+        if (!obj.containsKey('type')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['type'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(MessageType.canParse(obj['type'], reporter))) {
+          reporter.reportError("must be of type MessageType");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('message');
+      try {
+        if (!obj.containsKey('message')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['message'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['message'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type ShowMessageParams");
+      return false;
+    }
   }
 
   @override
@@ -7852,16 +11251,59 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('type') &&
-        MessageType.canParse(obj['type']) &&
-        obj.containsKey('message') &&
-        obj['message'] is String &&
-        (obj['actions'] == null ||
-            (obj['actions'] is List &&
-                (obj['actions']
-                    .every((item) => MessageActionItem.canParse(item)))));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('type');
+      try {
+        if (!obj.containsKey('type')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['type'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(MessageType.canParse(obj['type'], reporter))) {
+          reporter.reportError("must be of type MessageType");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('message');
+      try {
+        if (!obj.containsKey('message')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['message'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['message'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('actions');
+      try {
+        if (obj['actions'] != null &&
+            !((obj['actions'] is List &&
+                (obj['actions'].every(
+                    (item) => MessageActionItem.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<MessageActionItem>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type ShowMessageRequestParams");
+      return false;
+    }
   }
 
   @override
@@ -7943,14 +11385,52 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('signatures') &&
-        (obj['signatures'] is List &&
-            (obj['signatures']
-                .every((item) => SignatureInformation.canParse(item)))) &&
-        (obj['activeSignature'] == null || obj['activeSignature'] is num) &&
-        (obj['activeParameter'] == null || obj['activeParameter'] is num);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('signatures');
+      try {
+        if (!obj.containsKey('signatures')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['signatures'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!((obj['signatures'] is List &&
+            (obj['signatures'].every(
+                (item) => SignatureInformation.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<SignatureInformation>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('activeSignature');
+      try {
+        if (obj['activeSignature'] != null &&
+            !(obj['activeSignature'] is num)) {
+          reporter.reportError("must be of type num");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('activeParameter');
+      try {
+        if (obj['activeParameter'] != null &&
+            !(obj['activeParameter'] is num)) {
+          reporter.reportError("must be of type num");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type SignatureHelp");
+      return false;
+    }
   }
 
   @override
@@ -8003,11 +11483,24 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['triggerCharacters'] == null ||
-            (obj['triggerCharacters'] is List &&
-                (obj['triggerCharacters'].every((item) => item is String))));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('triggerCharacters');
+      try {
+        if (obj['triggerCharacters'] != null &&
+            !((obj['triggerCharacters'] is List &&
+                (obj['triggerCharacters'].every((item) => item is String))))) {
+          reporter.reportError("must be of type List<String>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type SignatureHelpOptions");
+      return false;
+    }
   }
 
   @override
@@ -8068,16 +11561,40 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['triggerCharacters'] == null ||
-            (obj['triggerCharacters'] is List &&
-                (obj['triggerCharacters'].every((item) => item is String)))) &&
-        obj.containsKey('documentSelector') &&
-        (obj['documentSelector'] == null ||
-            (obj['documentSelector'] is List &&
-                (obj['documentSelector']
-                    .every((item) => DocumentFilter.canParse(item)))));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('triggerCharacters');
+      try {
+        if (obj['triggerCharacters'] != null &&
+            !((obj['triggerCharacters'] is List &&
+                (obj['triggerCharacters'].every((item) => item is String))))) {
+          reporter.reportError("must be of type List<String>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('documentSelector');
+      try {
+        if (!obj.containsKey('documentSelector')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['documentSelector'] != null &&
+            !((obj['documentSelector'] is List &&
+                (obj['documentSelector'].every(
+                    (item) => DocumentFilter.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<DocumentFilter>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type SignatureHelpRegistrationOptions");
+      return false;
+    }
   }
 
   @override
@@ -8118,7 +11635,7 @@
     final label = json['label'];
     final documentation = json['documentation'] is String
         ? new Either2<String, MarkupContent>.t1(json['documentation'])
-        : (MarkupContent.canParse(json['documentation'])
+        : (MarkupContent.canParse(json['documentation'], nullLspJsonReporter)
             ? new Either2<String, MarkupContent>.t2(
                 json['documentation'] != null
                     ? MarkupContent.fromJson(json['documentation'])
@@ -8156,17 +11673,54 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('label') &&
-        obj['label'] is String &&
-        (obj['documentation'] == null ||
-            (obj['documentation'] is String ||
-                MarkupContent.canParse(obj['documentation']))) &&
-        (obj['parameters'] == null ||
-            (obj['parameters'] is List &&
-                (obj['parameters']
-                    .every((item) => ParameterInformation.canParse(item)))));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('label');
+      try {
+        if (!obj.containsKey('label')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['label'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['label'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('documentation');
+      try {
+        if (obj['documentation'] != null &&
+            !((obj['documentation'] is String ||
+                MarkupContent.canParse(obj['documentation'], reporter)))) {
+          reporter
+              .reportError("must be of type Either2<String, MarkupContent>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('parameters');
+      try {
+        if (obj['parameters'] != null &&
+            !((obj['parameters'] is List &&
+                (obj['parameters'].every((item) =>
+                    ParameterInformation.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<ParameterInformation>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type SignatureInformation");
+      return false;
+    }
   }
 
   @override
@@ -8217,9 +11771,22 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['id'] == null || obj['id'] is String);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('id');
+      try {
+        if (obj['id'] != null && !(obj['id'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type StaticRegistrationOptions");
+      return false;
+    }
   }
 
   @override
@@ -8312,16 +11879,82 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('name') &&
-        obj['name'] is String &&
-        obj.containsKey('kind') &&
-        SymbolKind.canParse(obj['kind']) &&
-        (obj['deprecated'] == null || obj['deprecated'] is bool) &&
-        obj.containsKey('location') &&
-        Location.canParse(obj['location']) &&
-        (obj['containerName'] == null || obj['containerName'] is String);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('name');
+      try {
+        if (!obj.containsKey('name')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['name'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['name'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('kind');
+      try {
+        if (!obj.containsKey('kind')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['kind'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(SymbolKind.canParse(obj['kind'], reporter))) {
+          reporter.reportError("must be of type SymbolKind");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('deprecated');
+      try {
+        if (obj['deprecated'] != null && !(obj['deprecated'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('location');
+      try {
+        if (!obj.containsKey('location')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['location'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(Location.canParse(obj['location'], reporter))) {
+          reporter.reportError("must be of type Location");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('containerName');
+      try {
+        if (obj['containerName'] != null && !(obj['containerName'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type SymbolInformation");
+      return false;
+    }
   }
 
   @override
@@ -8359,7 +11992,7 @@
 
   final num _value;
 
-  static bool canParse(Object obj) {
+  static bool canParse(Object obj, LspJsonReporter reporter) {
     return obj is num;
   }
 
@@ -8443,15 +12076,47 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('syncKind') &&
-        TextDocumentSyncKind.canParse(obj['syncKind']) &&
-        obj.containsKey('documentSelector') &&
-        (obj['documentSelector'] == null ||
-            (obj['documentSelector'] is List &&
-                (obj['documentSelector']
-                    .every((item) => DocumentFilter.canParse(item)))));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('syncKind');
+      try {
+        if (!obj.containsKey('syncKind')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['syncKind'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(TextDocumentSyncKind.canParse(obj['syncKind'], reporter))) {
+          reporter.reportError("must be of type TextDocumentSyncKind");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('documentSelector');
+      try {
+        if (!obj.containsKey('documentSelector')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['documentSelector'] != null &&
+            !((obj['documentSelector'] is List &&
+                (obj['documentSelector'].every(
+                    (item) => DocumentFilter.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<DocumentFilter>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter
+          .reportError("must be of type TextDocumentChangeRegistrationOptions");
+      return false;
+    }
   }
 
   @override
@@ -8745,68 +12410,265 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['synchronization'] == null ||
-            TextDocumentClientCapabilitiesSynchronization.canParse(
-                obj['synchronization'])) &&
-        (obj['completion'] == null ||
-            TextDocumentClientCapabilitiesCompletion.canParse(
-                obj['completion'])) &&
-        (obj['hover'] == null ||
-            TextDocumentClientCapabilitiesHover.canParse(obj['hover'])) &&
-        (obj['signatureHelp'] == null ||
-            TextDocumentClientCapabilitiesSignatureHelp.canParse(
-                obj['signatureHelp'])) &&
-        (obj['references'] == null ||
-            TextDocumentClientCapabilitiesReferences.canParse(
-                obj['references'])) &&
-        (obj['documentHighlight'] == null ||
-            TextDocumentClientCapabilitiesDocumentHighlight.canParse(
-                obj['documentHighlight'])) &&
-        (obj['documentSymbol'] == null ||
-            TextDocumentClientCapabilitiesDocumentSymbol.canParse(
-                obj['documentSymbol'])) &&
-        (obj['formatting'] == null ||
-            TextDocumentClientCapabilitiesFormatting.canParse(
-                obj['formatting'])) &&
-        (obj['rangeFormatting'] == null ||
-            TextDocumentClientCapabilitiesRangeFormatting.canParse(
-                obj['rangeFormatting'])) &&
-        (obj['onTypeFormatting'] == null ||
-            TextDocumentClientCapabilitiesOnTypeFormatting.canParse(
-                obj['onTypeFormatting'])) &&
-        (obj['declaration'] == null ||
-            TextDocumentClientCapabilitiesDeclaration.canParse(
-                obj['declaration'])) &&
-        (obj['definition'] == null ||
-            TextDocumentClientCapabilitiesDefinition.canParse(
-                obj['definition'])) &&
-        (obj['typeDefinition'] == null ||
-            TextDocumentClientCapabilitiesTypeDefinition.canParse(
-                obj['typeDefinition'])) &&
-        (obj['implementation'] == null ||
-            TextDocumentClientCapabilitiesImplementation.canParse(
-                obj['implementation'])) &&
-        (obj['codeAction'] == null ||
-            TextDocumentClientCapabilitiesCodeAction.canParse(
-                obj['codeAction'])) &&
-        (obj['codeLens'] == null ||
-            TextDocumentClientCapabilitiesCodeLens.canParse(obj['codeLens'])) &&
-        (obj['documentLink'] == null ||
-            TextDocumentClientCapabilitiesDocumentLink.canParse(
-                obj['documentLink'])) &&
-        (obj['colorProvider'] == null ||
-            TextDocumentClientCapabilitiesColorProvider.canParse(
-                obj['colorProvider'])) &&
-        (obj['rename'] == null ||
-            TextDocumentClientCapabilitiesRename.canParse(obj['rename'])) &&
-        (obj['publishDiagnostics'] == null ||
-            TextDocumentClientCapabilitiesPublishDiagnostics.canParse(
-                obj['publishDiagnostics'])) &&
-        (obj['foldingRange'] == null ||
-            TextDocumentClientCapabilitiesFoldingRange.canParse(
-                obj['foldingRange']));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('synchronization');
+      try {
+        if (obj['synchronization'] != null &&
+            !(TextDocumentClientCapabilitiesSynchronization.canParse(
+                obj['synchronization'], reporter))) {
+          reporter.reportError(
+              "must be of type TextDocumentClientCapabilitiesSynchronization");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('completion');
+      try {
+        if (obj['completion'] != null &&
+            !(TextDocumentClientCapabilitiesCompletion.canParse(
+                obj['completion'], reporter))) {
+          reporter.reportError(
+              "must be of type TextDocumentClientCapabilitiesCompletion");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('hover');
+      try {
+        if (obj['hover'] != null &&
+            !(TextDocumentClientCapabilitiesHover.canParse(
+                obj['hover'], reporter))) {
+          reporter.reportError(
+              "must be of type TextDocumentClientCapabilitiesHover");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('signatureHelp');
+      try {
+        if (obj['signatureHelp'] != null &&
+            !(TextDocumentClientCapabilitiesSignatureHelp.canParse(
+                obj['signatureHelp'], reporter))) {
+          reporter.reportError(
+              "must be of type TextDocumentClientCapabilitiesSignatureHelp");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('references');
+      try {
+        if (obj['references'] != null &&
+            !(TextDocumentClientCapabilitiesReferences.canParse(
+                obj['references'], reporter))) {
+          reporter.reportError(
+              "must be of type TextDocumentClientCapabilitiesReferences");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('documentHighlight');
+      try {
+        if (obj['documentHighlight'] != null &&
+            !(TextDocumentClientCapabilitiesDocumentHighlight.canParse(
+                obj['documentHighlight'], reporter))) {
+          reporter.reportError(
+              "must be of type TextDocumentClientCapabilitiesDocumentHighlight");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('documentSymbol');
+      try {
+        if (obj['documentSymbol'] != null &&
+            !(TextDocumentClientCapabilitiesDocumentSymbol.canParse(
+                obj['documentSymbol'], reporter))) {
+          reporter.reportError(
+              "must be of type TextDocumentClientCapabilitiesDocumentSymbol");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('formatting');
+      try {
+        if (obj['formatting'] != null &&
+            !(TextDocumentClientCapabilitiesFormatting.canParse(
+                obj['formatting'], reporter))) {
+          reporter.reportError(
+              "must be of type TextDocumentClientCapabilitiesFormatting");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('rangeFormatting');
+      try {
+        if (obj['rangeFormatting'] != null &&
+            !(TextDocumentClientCapabilitiesRangeFormatting.canParse(
+                obj['rangeFormatting'], reporter))) {
+          reporter.reportError(
+              "must be of type TextDocumentClientCapabilitiesRangeFormatting");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('onTypeFormatting');
+      try {
+        if (obj['onTypeFormatting'] != null &&
+            !(TextDocumentClientCapabilitiesOnTypeFormatting.canParse(
+                obj['onTypeFormatting'], reporter))) {
+          reporter.reportError(
+              "must be of type TextDocumentClientCapabilitiesOnTypeFormatting");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('declaration');
+      try {
+        if (obj['declaration'] != null &&
+            !(TextDocumentClientCapabilitiesDeclaration.canParse(
+                obj['declaration'], reporter))) {
+          reporter.reportError(
+              "must be of type TextDocumentClientCapabilitiesDeclaration");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('definition');
+      try {
+        if (obj['definition'] != null &&
+            !(TextDocumentClientCapabilitiesDefinition.canParse(
+                obj['definition'], reporter))) {
+          reporter.reportError(
+              "must be of type TextDocumentClientCapabilitiesDefinition");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('typeDefinition');
+      try {
+        if (obj['typeDefinition'] != null &&
+            !(TextDocumentClientCapabilitiesTypeDefinition.canParse(
+                obj['typeDefinition'], reporter))) {
+          reporter.reportError(
+              "must be of type TextDocumentClientCapabilitiesTypeDefinition");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('implementation');
+      try {
+        if (obj['implementation'] != null &&
+            !(TextDocumentClientCapabilitiesImplementation.canParse(
+                obj['implementation'], reporter))) {
+          reporter.reportError(
+              "must be of type TextDocumentClientCapabilitiesImplementation");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('codeAction');
+      try {
+        if (obj['codeAction'] != null &&
+            !(TextDocumentClientCapabilitiesCodeAction.canParse(
+                obj['codeAction'], reporter))) {
+          reporter.reportError(
+              "must be of type TextDocumentClientCapabilitiesCodeAction");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('codeLens');
+      try {
+        if (obj['codeLens'] != null &&
+            !(TextDocumentClientCapabilitiesCodeLens.canParse(
+                obj['codeLens'], reporter))) {
+          reporter.reportError(
+              "must be of type TextDocumentClientCapabilitiesCodeLens");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('documentLink');
+      try {
+        if (obj['documentLink'] != null &&
+            !(TextDocumentClientCapabilitiesDocumentLink.canParse(
+                obj['documentLink'], reporter))) {
+          reporter.reportError(
+              "must be of type TextDocumentClientCapabilitiesDocumentLink");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('colorProvider');
+      try {
+        if (obj['colorProvider'] != null &&
+            !(TextDocumentClientCapabilitiesColorProvider.canParse(
+                obj['colorProvider'], reporter))) {
+          reporter.reportError(
+              "must be of type TextDocumentClientCapabilitiesColorProvider");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('rename');
+      try {
+        if (obj['rename'] != null &&
+            !(TextDocumentClientCapabilitiesRename.canParse(
+                obj['rename'], reporter))) {
+          reporter.reportError(
+              "must be of type TextDocumentClientCapabilitiesRename");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('publishDiagnostics');
+      try {
+        if (obj['publishDiagnostics'] != null &&
+            !(TextDocumentClientCapabilitiesPublishDiagnostics.canParse(
+                obj['publishDiagnostics'], reporter))) {
+          reporter.reportError(
+              "must be of type TextDocumentClientCapabilitiesPublishDiagnostics");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('foldingRange');
+      try {
+        if (obj['foldingRange'] != null &&
+            !(TextDocumentClientCapabilitiesFoldingRange.canParse(
+                obj['foldingRange'], reporter))) {
+          reporter.reportError(
+              "must be of type TextDocumentClientCapabilitiesFoldingRange");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type TextDocumentClientCapabilities");
+      return false;
+    }
   }
 
   @override
@@ -8908,13 +12770,36 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['dynamicRegistration'] == null ||
-            obj['dynamicRegistration'] is bool) &&
-        (obj['codeActionLiteralSupport'] == null ||
-            TextDocumentClientCapabilitiesCodeActionLiteralSupport.canParse(
-                obj['codeActionLiteralSupport']));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('dynamicRegistration');
+      try {
+        if (obj['dynamicRegistration'] != null &&
+            !(obj['dynamicRegistration'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('codeActionLiteralSupport');
+      try {
+        if (obj['codeActionLiteralSupport'] != null &&
+            !(TextDocumentClientCapabilitiesCodeActionLiteralSupport.canParse(
+                obj['codeActionLiteralSupport'], reporter))) {
+          reporter.reportError(
+              "must be of type TextDocumentClientCapabilitiesCodeActionLiteralSupport");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError(
+          "must be of type TextDocumentClientCapabilitiesCodeAction");
+      return false;
+    }
   }
 
   @override
@@ -8970,11 +12855,33 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('valueSet') &&
-        (obj['valueSet'] is List &&
-            (obj['valueSet'].every((item) => CodeActionKind.canParse(item))));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('valueSet');
+      try {
+        if (!obj.containsKey('valueSet')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['valueSet'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!((obj['valueSet'] is List &&
+            (obj['valueSet']
+                .every((item) => CodeActionKind.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<CodeActionKind>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError(
+          "must be of type TextDocumentClientCapabilitiesCodeActionKind");
+      return false;
+    }
   }
 
   @override
@@ -9029,11 +12936,33 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('codeActionKind') &&
-        TextDocumentClientCapabilitiesCodeActionKind.canParse(
-            obj['codeActionKind']);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('codeActionKind');
+      try {
+        if (!obj.containsKey('codeActionKind')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['codeActionKind'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(TextDocumentClientCapabilitiesCodeActionKind.canParse(
+            obj['codeActionKind'], reporter))) {
+          reporter.reportError(
+              "must be of type TextDocumentClientCapabilitiesCodeActionKind");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError(
+          "must be of type TextDocumentClientCapabilitiesCodeActionLiteralSupport");
+      return false;
+    }
   }
 
   @override
@@ -9078,10 +13007,24 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['dynamicRegistration'] == null ||
-            obj['dynamicRegistration'] is bool);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('dynamicRegistration');
+      try {
+        if (obj['dynamicRegistration'] != null &&
+            !(obj['dynamicRegistration'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError(
+          "must be of type TextDocumentClientCapabilitiesCodeLens");
+      return false;
+    }
   }
 
   @override
@@ -9129,10 +13072,24 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['dynamicRegistration'] == null ||
-            obj['dynamicRegistration'] is bool);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('dynamicRegistration');
+      try {
+        if (obj['dynamicRegistration'] != null &&
+            !(obj['dynamicRegistration'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError(
+          "must be of type TextDocumentClientCapabilitiesColorProvider");
+      return false;
+    }
   }
 
   @override
@@ -9205,17 +13162,57 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['dynamicRegistration'] == null ||
-            obj['dynamicRegistration'] is bool) &&
-        (obj['completionItem'] == null ||
-            TextDocumentClientCapabilitiesCompletionItem.canParse(
-                obj['completionItem'])) &&
-        (obj['completionItemKind'] == null ||
-            TextDocumentClientCapabilitiesCompletionItemKind.canParse(
-                obj['completionItemKind'])) &&
-        (obj['contextSupport'] == null || obj['contextSupport'] is bool);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('dynamicRegistration');
+      try {
+        if (obj['dynamicRegistration'] != null &&
+            !(obj['dynamicRegistration'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('completionItem');
+      try {
+        if (obj['completionItem'] != null &&
+            !(TextDocumentClientCapabilitiesCompletionItem.canParse(
+                obj['completionItem'], reporter))) {
+          reporter.reportError(
+              "must be of type TextDocumentClientCapabilitiesCompletionItem");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('completionItemKind');
+      try {
+        if (obj['completionItemKind'] != null &&
+            !(TextDocumentClientCapabilitiesCompletionItemKind.canParse(
+                obj['completionItemKind'], reporter))) {
+          reporter.reportError(
+              "must be of type TextDocumentClientCapabilitiesCompletionItemKind");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('contextSupport');
+      try {
+        if (obj['contextSupport'] != null && !(obj['contextSupport'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError(
+          "must be of type TextDocumentClientCapabilitiesCompletion");
+      return false;
+    }
   }
 
   @override
@@ -9314,18 +13311,65 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['snippetSupport'] == null || obj['snippetSupport'] is bool) &&
-        (obj['commitCharactersSupport'] == null ||
-            obj['commitCharactersSupport'] is bool) &&
-        (obj['documentationFormat'] == null ||
-            (obj['documentationFormat'] is List &&
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('snippetSupport');
+      try {
+        if (obj['snippetSupport'] != null && !(obj['snippetSupport'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('commitCharactersSupport');
+      try {
+        if (obj['commitCharactersSupport'] != null &&
+            !(obj['commitCharactersSupport'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('documentationFormat');
+      try {
+        if (obj['documentationFormat'] != null &&
+            !((obj['documentationFormat'] is List &&
                 (obj['documentationFormat']
-                    .every((item) => MarkupKind.canParse(item))))) &&
-        (obj['deprecatedSupport'] == null ||
-            obj['deprecatedSupport'] is bool) &&
-        (obj['preselectSupport'] == null || obj['preselectSupport'] is bool);
+                    .every((item) => MarkupKind.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<MarkupKind>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('deprecatedSupport');
+      try {
+        if (obj['deprecatedSupport'] != null &&
+            !(obj['deprecatedSupport'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('preselectSupport');
+      try {
+        if (obj['preselectSupport'] != null &&
+            !(obj['preselectSupport'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError(
+          "must be of type TextDocumentClientCapabilitiesCompletionItem");
+      return false;
+    }
   }
 
   @override
@@ -9389,12 +13433,26 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['valueSet'] == null ||
-            (obj['valueSet'] is List &&
-                (obj['valueSet']
-                    .every((item) => CompletionItemKind.canParse(item)))));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('valueSet');
+      try {
+        if (obj['valueSet'] != null &&
+            !((obj['valueSet'] is List &&
+                (obj['valueSet'].every(
+                    (item) => CompletionItemKind.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<CompletionItemKind>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError(
+          "must be of type TextDocumentClientCapabilitiesCompletionItemKind");
+      return false;
+    }
   }
 
   @override
@@ -9455,11 +13513,33 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['dynamicRegistration'] == null ||
-            obj['dynamicRegistration'] is bool) &&
-        (obj['linkSupport'] == null || obj['linkSupport'] is bool);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('dynamicRegistration');
+      try {
+        if (obj['dynamicRegistration'] != null &&
+            !(obj['dynamicRegistration'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('linkSupport');
+      try {
+        if (obj['linkSupport'] != null && !(obj['linkSupport'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError(
+          "must be of type TextDocumentClientCapabilitiesDeclaration");
+      return false;
+    }
   }
 
   @override
@@ -9516,11 +13596,33 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['dynamicRegistration'] == null ||
-            obj['dynamicRegistration'] is bool) &&
-        (obj['linkSupport'] == null || obj['linkSupport'] is bool);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('dynamicRegistration');
+      try {
+        if (obj['dynamicRegistration'] != null &&
+            !(obj['dynamicRegistration'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('linkSupport');
+      try {
+        if (obj['linkSupport'] != null && !(obj['linkSupport'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError(
+          "must be of type TextDocumentClientCapabilitiesDefinition");
+      return false;
+    }
   }
 
   @override
@@ -9569,10 +13671,24 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['dynamicRegistration'] == null ||
-            obj['dynamicRegistration'] is bool);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('dynamicRegistration');
+      try {
+        if (obj['dynamicRegistration'] != null &&
+            !(obj['dynamicRegistration'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError(
+          "must be of type TextDocumentClientCapabilitiesDocumentHighlight");
+      return false;
+    }
   }
 
   @override
@@ -9617,10 +13733,24 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['dynamicRegistration'] == null ||
-            obj['dynamicRegistration'] is bool);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('dynamicRegistration');
+      try {
+        if (obj['dynamicRegistration'] != null &&
+            !(obj['dynamicRegistration'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError(
+          "must be of type TextDocumentClientCapabilitiesDocumentLink");
+      return false;
+    }
   }
 
   @override
@@ -9685,15 +13815,46 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['dynamicRegistration'] == null ||
-            obj['dynamicRegistration'] is bool) &&
-        (obj['symbolKind'] == null ||
-            TextDocumentClientCapabilitiesSymbolKind.canParse(
-                obj['symbolKind'])) &&
-        (obj['hierarchicalDocumentSymbolSupport'] == null ||
-            obj['hierarchicalDocumentSymbolSupport'] is bool);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('dynamicRegistration');
+      try {
+        if (obj['dynamicRegistration'] != null &&
+            !(obj['dynamicRegistration'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('symbolKind');
+      try {
+        if (obj['symbolKind'] != null &&
+            !(TextDocumentClientCapabilitiesSymbolKind.canParse(
+                obj['symbolKind'], reporter))) {
+          reporter.reportError(
+              "must be of type TextDocumentClientCapabilitiesSymbolKind");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('hierarchicalDocumentSymbolSupport');
+      try {
+        if (obj['hierarchicalDocumentSymbolSupport'] != null &&
+            !(obj['hierarchicalDocumentSymbolSupport'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError(
+          "must be of type TextDocumentClientCapabilitiesDocumentSymbol");
+      return false;
+    }
   }
 
   @override
@@ -9769,12 +13930,43 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['dynamicRegistration'] == null ||
-            obj['dynamicRegistration'] is bool) &&
-        (obj['rangeLimit'] == null || obj['rangeLimit'] is num) &&
-        (obj['lineFoldingOnly'] == null || obj['lineFoldingOnly'] is bool);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('dynamicRegistration');
+      try {
+        if (obj['dynamicRegistration'] != null &&
+            !(obj['dynamicRegistration'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('rangeLimit');
+      try {
+        if (obj['rangeLimit'] != null && !(obj['rangeLimit'] is num)) {
+          reporter.reportError("must be of type num");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('lineFoldingOnly');
+      try {
+        if (obj['lineFoldingOnly'] != null &&
+            !(obj['lineFoldingOnly'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError(
+          "must be of type TextDocumentClientCapabilitiesFoldingRange");
+      return false;
+    }
   }
 
   @override
@@ -9824,10 +14016,24 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['dynamicRegistration'] == null ||
-            obj['dynamicRegistration'] is bool);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('dynamicRegistration');
+      try {
+        if (obj['dynamicRegistration'] != null &&
+            !(obj['dynamicRegistration'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError(
+          "must be of type TextDocumentClientCapabilitiesFormatting");
+      return false;
+    }
   }
 
   @override
@@ -9885,14 +14091,36 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['dynamicRegistration'] == null ||
-            obj['dynamicRegistration'] is bool) &&
-        (obj['contentFormat'] == null ||
-            (obj['contentFormat'] is List &&
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('dynamicRegistration');
+      try {
+        if (obj['dynamicRegistration'] != null &&
+            !(obj['dynamicRegistration'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('contentFormat');
+      try {
+        if (obj['contentFormat'] != null &&
+            !((obj['contentFormat'] is List &&
                 (obj['contentFormat']
-                    .every((item) => MarkupKind.canParse(item)))));
+                    .every((item) => MarkupKind.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<MarkupKind>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter
+          .reportError("must be of type TextDocumentClientCapabilitiesHover");
+      return false;
+    }
   }
 
   @override
@@ -9955,11 +14183,33 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['dynamicRegistration'] == null ||
-            obj['dynamicRegistration'] is bool) &&
-        (obj['linkSupport'] == null || obj['linkSupport'] is bool);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('dynamicRegistration');
+      try {
+        if (obj['dynamicRegistration'] != null &&
+            !(obj['dynamicRegistration'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('linkSupport');
+      try {
+        if (obj['linkSupport'] != null && !(obj['linkSupport'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError(
+          "must be of type TextDocumentClientCapabilitiesImplementation");
+      return false;
+    }
   }
 
   @override
@@ -10008,10 +14258,24 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['dynamicRegistration'] == null ||
-            obj['dynamicRegistration'] is bool);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('dynamicRegistration');
+      try {
+        if (obj['dynamicRegistration'] != null &&
+            !(obj['dynamicRegistration'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError(
+          "must be of type TextDocumentClientCapabilitiesOnTypeFormatting");
+      return false;
+    }
   }
 
   @override
@@ -10060,10 +14324,24 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['labelOffsetSupport'] == null ||
-            obj['labelOffsetSupport'] is bool);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('labelOffsetSupport');
+      try {
+        if (obj['labelOffsetSupport'] != null &&
+            !(obj['labelOffsetSupport'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError(
+          "must be of type TextDocumentClientCapabilitiesParameterInformation");
+      return false;
+    }
   }
 
   @override
@@ -10109,10 +14387,24 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['relatedInformation'] == null ||
-            obj['relatedInformation'] is bool);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('relatedInformation');
+      try {
+        if (obj['relatedInformation'] != null &&
+            !(obj['relatedInformation'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError(
+          "must be of type TextDocumentClientCapabilitiesPublishDiagnostics");
+      return false;
+    }
   }
 
   @override
@@ -10158,10 +14450,24 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['dynamicRegistration'] == null ||
-            obj['dynamicRegistration'] is bool);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('dynamicRegistration');
+      try {
+        if (obj['dynamicRegistration'] != null &&
+            !(obj['dynamicRegistration'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError(
+          "must be of type TextDocumentClientCapabilitiesRangeFormatting");
+      return false;
+    }
   }
 
   @override
@@ -10206,10 +14512,24 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['dynamicRegistration'] == null ||
-            obj['dynamicRegistration'] is bool);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('dynamicRegistration');
+      try {
+        if (obj['dynamicRegistration'] != null &&
+            !(obj['dynamicRegistration'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError(
+          "must be of type TextDocumentClientCapabilitiesReferences");
+      return false;
+    }
   }
 
   @override
@@ -10264,11 +14584,33 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['dynamicRegistration'] == null ||
-            obj['dynamicRegistration'] is bool) &&
-        (obj['prepareSupport'] == null || obj['prepareSupport'] is bool);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('dynamicRegistration');
+      try {
+        if (obj['dynamicRegistration'] != null &&
+            !(obj['dynamicRegistration'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('prepareSupport');
+      try {
+        if (obj['prepareSupport'] != null && !(obj['prepareSupport'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter
+          .reportError("must be of type TextDocumentClientCapabilitiesRename");
+      return false;
+    }
   }
 
   @override
@@ -10329,13 +14671,36 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['dynamicRegistration'] == null ||
-            obj['dynamicRegistration'] is bool) &&
-        (obj['signatureInformation'] == null ||
-            TextDocumentClientCapabilitiesSignatureInformation.canParse(
-                obj['signatureInformation']));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('dynamicRegistration');
+      try {
+        if (obj['dynamicRegistration'] != null &&
+            !(obj['dynamicRegistration'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('signatureInformation');
+      try {
+        if (obj['signatureInformation'] != null &&
+            !(TextDocumentClientCapabilitiesSignatureInformation.canParse(
+                obj['signatureInformation'], reporter))) {
+          reporter.reportError(
+              "must be of type TextDocumentClientCapabilitiesSignatureInformation");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError(
+          "must be of type TextDocumentClientCapabilitiesSignatureHelp");
+      return false;
+    }
   }
 
   @override
@@ -10399,15 +14764,38 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['documentationFormat'] == null ||
-            (obj['documentationFormat'] is List &&
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('documentationFormat');
+      try {
+        if (obj['documentationFormat'] != null &&
+            !((obj['documentationFormat'] is List &&
                 (obj['documentationFormat']
-                    .every((item) => MarkupKind.canParse(item))))) &&
-        (obj['parameterInformation'] == null ||
-            TextDocumentClientCapabilitiesParameterInformation.canParse(
-                obj['parameterInformation']));
+                    .every((item) => MarkupKind.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<MarkupKind>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('parameterInformation');
+      try {
+        if (obj['parameterInformation'] != null &&
+            !(TextDocumentClientCapabilitiesParameterInformation.canParse(
+                obj['parameterInformation'], reporter))) {
+          reporter.reportError(
+              "must be of type TextDocumentClientCapabilitiesParameterInformation");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError(
+          "must be of type TextDocumentClientCapabilitiesSignatureInformation");
+      return false;
+    }
   }
 
   @override
@@ -10464,11 +14852,26 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['valueSet'] == null ||
-            (obj['valueSet'] is List &&
-                (obj['valueSet'].every((item) => SymbolKind.canParse(item)))));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('valueSet');
+      try {
+        if (obj['valueSet'] != null &&
+            !((obj['valueSet'] is List &&
+                (obj['valueSet']
+                    .every((item) => SymbolKind.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<SymbolKind>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError(
+          "must be of type TextDocumentClientCapabilitiesSymbolKind");
+      return false;
+    }
   }
 
   @override
@@ -10540,14 +14943,52 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['dynamicRegistration'] == null ||
-            obj['dynamicRegistration'] is bool) &&
-        (obj['willSave'] == null || obj['willSave'] is bool) &&
-        (obj['willSaveWaitUntil'] == null ||
-            obj['willSaveWaitUntil'] is bool) &&
-        (obj['didSave'] == null || obj['didSave'] is bool);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('dynamicRegistration');
+      try {
+        if (obj['dynamicRegistration'] != null &&
+            !(obj['dynamicRegistration'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('willSave');
+      try {
+        if (obj['willSave'] != null && !(obj['willSave'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('willSaveWaitUntil');
+      try {
+        if (obj['willSaveWaitUntil'] != null &&
+            !(obj['willSaveWaitUntil'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('didSave');
+      try {
+        if (obj['didSave'] != null && !(obj['didSave'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError(
+          "must be of type TextDocumentClientCapabilitiesSynchronization");
+      return false;
+    }
   }
 
   @override
@@ -10613,11 +15054,33 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['dynamicRegistration'] == null ||
-            obj['dynamicRegistration'] is bool) &&
-        (obj['linkSupport'] == null || obj['linkSupport'] is bool);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('dynamicRegistration');
+      try {
+        if (obj['dynamicRegistration'] != null &&
+            !(obj['dynamicRegistration'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('linkSupport');
+      try {
+        if (obj['linkSupport'] != null && !(obj['linkSupport'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError(
+          "must be of type TextDocumentClientCapabilitiesTypeDefinition");
+      return false;
+    }
   }
 
   @override
@@ -10683,12 +15146,48 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['range'] == null || Range.canParse(obj['range'])) &&
-        (obj['rangeLength'] == null || obj['rangeLength'] is num) &&
-        obj.containsKey('text') &&
-        obj['text'] is String;
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('range');
+      try {
+        if (obj['range'] != null && !(Range.canParse(obj['range'], reporter))) {
+          reporter.reportError("must be of type Range");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('rangeLength');
+      try {
+        if (obj['rangeLength'] != null && !(obj['rangeLength'] is num)) {
+          reporter.reportError("must be of type num");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('text');
+      try {
+        if (!obj.containsKey('text')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['text'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['text'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type TextDocumentContentChangeEvent");
+      return false;
+    }
   }
 
   @override
@@ -10752,13 +15251,51 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('textDocument') &&
-        VersionedTextDocumentIdentifier.canParse(obj['textDocument']) &&
-        obj.containsKey('edits') &&
-        (obj['edits'] is List &&
-            (obj['edits'].every((item) => TextEdit.canParse(item))));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('textDocument');
+      try {
+        if (!obj.containsKey('textDocument')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['textDocument'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(VersionedTextDocumentIdentifier.canParse(
+            obj['textDocument'], reporter))) {
+          reporter
+              .reportError("must be of type VersionedTextDocumentIdentifier");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('edits');
+      try {
+        if (!obj.containsKey('edits')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['edits'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!((obj['edits'] is List &&
+            (obj['edits']
+                .every((item) => TextEdit.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<TextEdit>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type TextDocumentEdit");
+      return false;
+    }
   }
 
   @override
@@ -10793,7 +15330,7 @@
     }
   }
   static TextDocumentIdentifier fromJson(Map<String, dynamic> json) {
-    if (VersionedTextDocumentIdentifier.canParse(json)) {
+    if (VersionedTextDocumentIdentifier.canParse(json, nullLspJsonReporter)) {
       return VersionedTextDocumentIdentifier.fromJson(json);
     }
     final uri = json['uri'];
@@ -10809,10 +15346,30 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('uri') &&
-        obj['uri'] is String;
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('uri');
+      try {
+        if (!obj.containsKey('uri')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['uri'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['uri'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type TextDocumentIdentifier");
+      return false;
+    }
   }
 
   @override
@@ -10884,16 +15441,81 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('uri') &&
-        obj['uri'] is String &&
-        obj.containsKey('languageId') &&
-        obj['languageId'] is String &&
-        obj.containsKey('version') &&
-        obj['version'] is num &&
-        obj.containsKey('text') &&
-        obj['text'] is String;
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('uri');
+      try {
+        if (!obj.containsKey('uri')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['uri'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['uri'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('languageId');
+      try {
+        if (!obj.containsKey('languageId')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['languageId'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['languageId'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('version');
+      try {
+        if (!obj.containsKey('version')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['version'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['version'] is num)) {
+          reporter.reportError("must be of type num");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('text');
+      try {
+        if (!obj.containsKey('text')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['text'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['text'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type TextDocumentItem");
+      return false;
+    }
   }
 
   @override
@@ -10935,10 +15557,10 @@
     }
   }
   static TextDocumentPositionParams fromJson(Map<String, dynamic> json) {
-    if (CompletionParams.canParse(json)) {
+    if (CompletionParams.canParse(json, nullLspJsonReporter)) {
       return CompletionParams.fromJson(json);
     }
-    if (ReferenceParams.canParse(json)) {
+    if (ReferenceParams.canParse(json, nullLspJsonReporter)) {
       return ReferenceParams.fromJson(json);
     }
     final textDocument = json['textDocument'] != null
@@ -10964,12 +15586,47 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('textDocument') &&
-        TextDocumentIdentifier.canParse(obj['textDocument']) &&
-        obj.containsKey('position') &&
-        Position.canParse(obj['position']);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('textDocument');
+      try {
+        if (!obj.containsKey('textDocument')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['textDocument'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(TextDocumentIdentifier.canParse(obj['textDocument'], reporter))) {
+          reporter.reportError("must be of type TextDocumentIdentifier");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('position');
+      try {
+        if (!obj.containsKey('position')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['position'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(Position.canParse(obj['position'], reporter))) {
+          reporter.reportError("must be of type Position");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type TextDocumentPositionParams");
+      return false;
+    }
   }
 
   @override
@@ -11001,31 +15658,34 @@
 
   TextDocumentRegistrationOptions(this.documentSelector);
   static TextDocumentRegistrationOptions fromJson(Map<String, dynamic> json) {
-    if (TextDocumentChangeRegistrationOptions.canParse(json)) {
+    if (TextDocumentChangeRegistrationOptions.canParse(
+        json, nullLspJsonReporter)) {
       return TextDocumentChangeRegistrationOptions.fromJson(json);
     }
-    if (TextDocumentSaveRegistrationOptions.canParse(json)) {
+    if (TextDocumentSaveRegistrationOptions.canParse(
+        json, nullLspJsonReporter)) {
       return TextDocumentSaveRegistrationOptions.fromJson(json);
     }
-    if (CompletionRegistrationOptions.canParse(json)) {
+    if (CompletionRegistrationOptions.canParse(json, nullLspJsonReporter)) {
       return CompletionRegistrationOptions.fromJson(json);
     }
-    if (SignatureHelpRegistrationOptions.canParse(json)) {
+    if (SignatureHelpRegistrationOptions.canParse(json, nullLspJsonReporter)) {
       return SignatureHelpRegistrationOptions.fromJson(json);
     }
-    if (CodeActionRegistrationOptions.canParse(json)) {
+    if (CodeActionRegistrationOptions.canParse(json, nullLspJsonReporter)) {
       return CodeActionRegistrationOptions.fromJson(json);
     }
-    if (CodeLensRegistrationOptions.canParse(json)) {
+    if (CodeLensRegistrationOptions.canParse(json, nullLspJsonReporter)) {
       return CodeLensRegistrationOptions.fromJson(json);
     }
-    if (DocumentLinkRegistrationOptions.canParse(json)) {
+    if (DocumentLinkRegistrationOptions.canParse(json, nullLspJsonReporter)) {
       return DocumentLinkRegistrationOptions.fromJson(json);
     }
-    if (DocumentOnTypeFormattingRegistrationOptions.canParse(json)) {
+    if (DocumentOnTypeFormattingRegistrationOptions.canParse(
+        json, nullLspJsonReporter)) {
       return DocumentOnTypeFormattingRegistrationOptions.fromJson(json);
     }
-    if (RenameRegistrationOptions.canParse(json)) {
+    if (RenameRegistrationOptions.canParse(json, nullLspJsonReporter)) {
       return RenameRegistrationOptions.fromJson(json);
     }
     final documentSelector = json['documentSelector']
@@ -11045,13 +15705,29 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('documentSelector') &&
-        (obj['documentSelector'] == null ||
-            (obj['documentSelector'] is List &&
-                (obj['documentSelector']
-                    .every((item) => DocumentFilter.canParse(item)))));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('documentSelector');
+      try {
+        if (!obj.containsKey('documentSelector')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['documentSelector'] != null &&
+            !((obj['documentSelector'] is List &&
+                (obj['documentSelector'].every(
+                    (item) => DocumentFilter.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<DocumentFilter>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type TextDocumentRegistrationOptions");
+      return false;
+    }
   }
 
   @override
@@ -11080,7 +15756,7 @@
 
   final num _value;
 
-  static bool canParse(Object obj) {
+  static bool canParse(Object obj, LspJsonReporter reporter) {
     return obj is num;
   }
 
@@ -11139,14 +15815,39 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['includeText'] == null || obj['includeText'] is bool) &&
-        obj.containsKey('documentSelector') &&
-        (obj['documentSelector'] == null ||
-            (obj['documentSelector'] is List &&
-                (obj['documentSelector']
-                    .every((item) => DocumentFilter.canParse(item)))));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('includeText');
+      try {
+        if (obj['includeText'] != null && !(obj['includeText'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('documentSelector');
+      try {
+        if (!obj.containsKey('documentSelector')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['documentSelector'] != null &&
+            !((obj['documentSelector'] is List &&
+                (obj['documentSelector'].every(
+                    (item) => DocumentFilter.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<DocumentFilter>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter
+          .reportError("must be of type TextDocumentSaveRegistrationOptions");
+      return false;
+    }
   }
 
   @override
@@ -11179,7 +15880,7 @@
 
   final num _value;
 
-  static bool canParse(Object obj) {
+  static bool canParse(Object obj, LspJsonReporter reporter) {
     return obj is num;
   }
 
@@ -11265,15 +15966,61 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['openClose'] == null || obj['openClose'] is bool) &&
-        (obj['change'] == null ||
-            TextDocumentSyncKind.canParse(obj['change'])) &&
-        (obj['willSave'] == null || obj['willSave'] is bool) &&
-        (obj['willSaveWaitUntil'] == null ||
-            obj['willSaveWaitUntil'] is bool) &&
-        (obj['save'] == null || SaveOptions.canParse(obj['save']));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('openClose');
+      try {
+        if (obj['openClose'] != null && !(obj['openClose'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('change');
+      try {
+        if (obj['change'] != null &&
+            !(TextDocumentSyncKind.canParse(obj['change'], reporter))) {
+          reporter.reportError("must be of type TextDocumentSyncKind");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('willSave');
+      try {
+        if (obj['willSave'] != null && !(obj['willSave'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('willSaveWaitUntil');
+      try {
+        if (obj['willSaveWaitUntil'] != null &&
+            !(obj['willSaveWaitUntil'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('save');
+      try {
+        if (obj['save'] != null &&
+            !(SaveOptions.canParse(obj['save'], reporter))) {
+          reporter.reportError("must be of type SaveOptions");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type TextDocumentSyncOptions");
+      return false;
+    }
   }
 
   @override
@@ -11337,12 +16084,47 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('range') &&
-        Range.canParse(obj['range']) &&
-        obj.containsKey('newText') &&
-        obj['newText'] is String;
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('range');
+      try {
+        if (!obj.containsKey('range')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['range'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(Range.canParse(obj['range'], reporter))) {
+          reporter.reportError("must be of type Range");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('newText');
+      try {
+        if (!obj.containsKey('newText')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['newText'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['newText'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type TextEdit");
+      return false;
+    }
   }
 
   @override
@@ -11398,12 +16180,47 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('id') &&
-        obj['id'] is String &&
-        obj.containsKey('method') &&
-        obj['method'] is String;
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('id');
+      try {
+        if (!obj.containsKey('id')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['id'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['id'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('method');
+      try {
+        if (!obj.containsKey('method')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['method'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['method'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type Unregistration");
+      return false;
+    }
   }
 
   @override
@@ -11452,12 +16269,32 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('unregisterations') &&
-        (obj['unregisterations'] is List &&
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('unregisterations');
+      try {
+        if (!obj.containsKey('unregisterations')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['unregisterations'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!((obj['unregisterations'] is List &&
             (obj['unregisterations']
-                .every((item) => Unregistration.canParse(item))));
+                .every((item) => Unregistration.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<Unregistration>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type UnregistrationParams");
+      return false;
+    }
   }
 
   @override
@@ -11518,12 +16355,43 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('version') &&
-        (obj['version'] == null || obj['version'] is num) &&
-        obj.containsKey('uri') &&
-        obj['uri'] is String;
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('version');
+      try {
+        if (!obj.containsKey('version')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['version'] != null && !(obj['version'] is num)) {
+          reporter.reportError("must be of type num");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('uri');
+      try {
+        if (!obj.containsKey('uri')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['uri'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['uri'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type VersionedTextDocumentIdentifier");
+      return false;
+    }
   }
 
   @override
@@ -11552,7 +16420,7 @@
 
   final num _value;
 
-  static bool canParse(Object obj) {
+  static bool canParse(Object obj, LspJsonReporter reporter) {
     return obj is num;
   }
 
@@ -11611,12 +16479,47 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('textDocument') &&
-        TextDocumentIdentifier.canParse(obj['textDocument']) &&
-        obj.containsKey('reason') &&
-        obj['reason'] is num;
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('textDocument');
+      try {
+        if (!obj.containsKey('textDocument')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['textDocument'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(TextDocumentIdentifier.canParse(obj['textDocument'], reporter))) {
+          reporter.reportError("must be of type TextDocumentIdentifier");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('reason');
+      try {
+        if (!obj.containsKey('reason')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['reason'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['reason'] is num)) {
+          reporter.reportError("must be of type num");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type WillSaveTextDocumentParams");
+      return false;
+    }
   }
 
   @override
@@ -11751,25 +16654,101 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['applyEdit'] == null || obj['applyEdit'] is bool) &&
-        (obj['workspaceEdit'] == null ||
-            WorkspaceClientCapabilitiesWorkspaceEdit.canParse(
-                obj['workspaceEdit'])) &&
-        (obj['didChangeConfiguration'] == null ||
-            WorkspaceClientCapabilitiesDidChangeConfiguration.canParse(
-                obj['didChangeConfiguration'])) &&
-        (obj['didChangeWatchedFiles'] == null ||
-            WorkspaceClientCapabilitiesDidChangeWatchedFiles.canParse(
-                obj['didChangeWatchedFiles'])) &&
-        (obj['symbol'] == null ||
-            WorkspaceClientCapabilitiesSymbol.canParse(obj['symbol'])) &&
-        (obj['executeCommand'] == null ||
-            WorkspaceClientCapabilitiesExecuteCommand.canParse(
-                obj['executeCommand'])) &&
-        (obj['workspaceFolders'] == null || obj['workspaceFolders'] is bool) &&
-        (obj['configuration'] == null || obj['configuration'] is bool);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('applyEdit');
+      try {
+        if (obj['applyEdit'] != null && !(obj['applyEdit'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('workspaceEdit');
+      try {
+        if (obj['workspaceEdit'] != null &&
+            !(WorkspaceClientCapabilitiesWorkspaceEdit.canParse(
+                obj['workspaceEdit'], reporter))) {
+          reporter.reportError(
+              "must be of type WorkspaceClientCapabilitiesWorkspaceEdit");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('didChangeConfiguration');
+      try {
+        if (obj['didChangeConfiguration'] != null &&
+            !(WorkspaceClientCapabilitiesDidChangeConfiguration.canParse(
+                obj['didChangeConfiguration'], reporter))) {
+          reporter.reportError(
+              "must be of type WorkspaceClientCapabilitiesDidChangeConfiguration");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('didChangeWatchedFiles');
+      try {
+        if (obj['didChangeWatchedFiles'] != null &&
+            !(WorkspaceClientCapabilitiesDidChangeWatchedFiles.canParse(
+                obj['didChangeWatchedFiles'], reporter))) {
+          reporter.reportError(
+              "must be of type WorkspaceClientCapabilitiesDidChangeWatchedFiles");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('symbol');
+      try {
+        if (obj['symbol'] != null &&
+            !(WorkspaceClientCapabilitiesSymbol.canParse(
+                obj['symbol'], reporter))) {
+          reporter
+              .reportError("must be of type WorkspaceClientCapabilitiesSymbol");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('executeCommand');
+      try {
+        if (obj['executeCommand'] != null &&
+            !(WorkspaceClientCapabilitiesExecuteCommand.canParse(
+                obj['executeCommand'], reporter))) {
+          reporter.reportError(
+              "must be of type WorkspaceClientCapabilitiesExecuteCommand");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('workspaceFolders');
+      try {
+        if (obj['workspaceFolders'] != null &&
+            !(obj['workspaceFolders'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('configuration');
+      try {
+        if (obj['configuration'] != null && !(obj['configuration'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type WorkspaceClientCapabilities");
+      return false;
+    }
   }
 
   @override
@@ -11830,10 +16809,24 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['dynamicRegistration'] == null ||
-            obj['dynamicRegistration'] is bool);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('dynamicRegistration');
+      try {
+        if (obj['dynamicRegistration'] != null &&
+            !(obj['dynamicRegistration'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError(
+          "must be of type WorkspaceClientCapabilitiesDidChangeConfiguration");
+      return false;
+    }
   }
 
   @override
@@ -11881,10 +16874,24 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['dynamicRegistration'] == null ||
-            obj['dynamicRegistration'] is bool);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('dynamicRegistration');
+      try {
+        if (obj['dynamicRegistration'] != null &&
+            !(obj['dynamicRegistration'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError(
+          "must be of type WorkspaceClientCapabilitiesDidChangeWatchedFiles");
+      return false;
+    }
   }
 
   @override
@@ -11929,10 +16936,24 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['dynamicRegistration'] == null ||
-            obj['dynamicRegistration'] is bool);
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('dynamicRegistration');
+      try {
+        if (obj['dynamicRegistration'] != null &&
+            !(obj['dynamicRegistration'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError(
+          "must be of type WorkspaceClientCapabilitiesExecuteCommand");
+      return false;
+    }
   }
 
   @override
@@ -11987,12 +17008,35 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['dynamicRegistration'] == null ||
-            obj['dynamicRegistration'] is bool) &&
-        (obj['symbolKind'] == null ||
-            WorkspaceClientCapabilitiesSymbolKind.canParse(obj['symbolKind']));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('dynamicRegistration');
+      try {
+        if (obj['dynamicRegistration'] != null &&
+            !(obj['dynamicRegistration'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('symbolKind');
+      try {
+        if (obj['symbolKind'] != null &&
+            !(WorkspaceClientCapabilitiesSymbolKind.canParse(
+                obj['symbolKind'], reporter))) {
+          reporter.reportError(
+              "must be of type WorkspaceClientCapabilitiesSymbolKind");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type WorkspaceClientCapabilitiesSymbol");
+      return false;
+    }
   }
 
   @override
@@ -12048,11 +17092,26 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['valueSet'] == null ||
-            (obj['valueSet'] is List &&
-                (obj['valueSet'].every((item) => SymbolKind.canParse(item)))));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('valueSet');
+      try {
+        if (obj['valueSet'] != null &&
+            !((obj['valueSet'] is List &&
+                (obj['valueSet']
+                    .every((item) => SymbolKind.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<SymbolKind>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter
+          .reportError("must be of type WorkspaceClientCapabilitiesSymbolKind");
+      return false;
+    }
   }
 
   @override
@@ -12123,15 +17182,46 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['documentChanges'] == null || obj['documentChanges'] is bool) &&
-        (obj['resourceOperations'] == null ||
-            (obj['resourceOperations'] is List &&
-                (obj['resourceOperations']
-                    .every((item) => ResourceOperationKind.canParse(item))))) &&
-        (obj['failureHandling'] == null ||
-            FailureHandlingKind.canParse(obj['failureHandling']));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('documentChanges');
+      try {
+        if (obj['documentChanges'] != null &&
+            !(obj['documentChanges'] is bool)) {
+          reporter.reportError("must be of type bool");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('resourceOperations');
+      try {
+        if (obj['resourceOperations'] != null &&
+            !((obj['resourceOperations'] is List &&
+                (obj['resourceOperations'].every((item) =>
+                    ResourceOperationKind.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<ResourceOperationKind>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('failureHandling');
+      try {
+        if (obj['failureHandling'] != null &&
+            !(FailureHandlingKind.canParse(obj['failureHandling'], reporter))) {
+          reporter.reportError("must be of type FailureHandlingKind");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError(
+          "must be of type WorkspaceClientCapabilitiesWorkspaceEdit");
+      return false;
+    }
   }
 
   @override
@@ -12173,21 +17263,21 @@
                 ?.cast<TextEdit>()
                 ?.toList()))
         ?.cast<String, List<TextEdit>>();
-    final documentChanges = (json['documentChanges'] is List && (json['documentChanges'].every((item) => TextDocumentEdit.canParse(item))))
+    final documentChanges = (json['documentChanges'] is List && (json['documentChanges'].every((item) => TextDocumentEdit.canParse(item, nullLspJsonReporter))))
         ? new Either2<List<TextDocumentEdit>, List<Either4<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>>>.t1(
             json['documentChanges']
                 ?.map((item) =>
                     item != null ? TextDocumentEdit.fromJson(item) : null)
                 ?.cast<TextDocumentEdit>()
                 ?.toList())
-        : ((json['documentChanges'] is List && (json['documentChanges'].every((item) => (TextDocumentEdit.canParse(item) || CreateFile.canParse(item) || RenameFile.canParse(item) || DeleteFile.canParse(item)))))
+        : ((json['documentChanges'] is List && (json['documentChanges'].every((item) => (TextDocumentEdit.canParse(item, nullLspJsonReporter) || CreateFile.canParse(item, nullLspJsonReporter) || RenameFile.canParse(item, nullLspJsonReporter) || DeleteFile.canParse(item, nullLspJsonReporter)))))
             ? new Either2<List<TextDocumentEdit>, List<Either4<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>>>.t2(json['documentChanges']
-                ?.map((item) => TextDocumentEdit.canParse(item)
+                ?.map((item) => TextDocumentEdit.canParse(item, nullLspJsonReporter)
                     ? new Either4<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>.t1(
                         item != null ? TextDocumentEdit.fromJson(item) : null)
-                    : (CreateFile.canParse(item)
+                    : (CreateFile.canParse(item, nullLspJsonReporter)
                         ? new Either4<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>.t2(item != null ? CreateFile.fromJson(item) : null)
-                        : (RenameFile.canParse(item) ? new Either4<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>.t3(item != null ? RenameFile.fromJson(item) : null) : (DeleteFile.canParse(item) ? new Either4<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>.t4(item != null ? DeleteFile.fromJson(item) : null) : (item == null ? null : (throw '''${item} was not one of (TextDocumentEdit, CreateFile, RenameFile, DeleteFile)'''))))))
+                        : (RenameFile.canParse(item, nullLspJsonReporter) ? new Either4<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>.t3(item != null ? RenameFile.fromJson(item) : null) : (DeleteFile.canParse(item, nullLspJsonReporter) ? new Either4<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>.t4(item != null ? DeleteFile.fromJson(item) : null) : (item == null ? null : (throw '''${item} was not one of (TextDocumentEdit, CreateFile, RenameFile, DeleteFile)'''))))))
                 ?.cast<Either4<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>>()
                 ?.toList())
             : (json['documentChanges'] == null ? null : (throw '''${json['documentChanges']} was not one of (List<TextDocumentEdit>, List<Either4<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>>)''')));
@@ -12225,24 +17315,47 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        (obj['changes'] == null ||
-            (obj['changes'] is Map &&
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('changes');
+      try {
+        if (obj['changes'] != null &&
+            !((obj['changes'] is Map &&
                 (obj['changes'].keys.every((item) =>
                     item is String &&
                     obj['changes'].values.every((item) => (item is List &&
-                        (item.every((item) => TextEdit.canParse(item))))))))) &&
-        (obj['documentChanges'] == null ||
-            ((obj['documentChanges'] is List &&
-                    (obj['documentChanges']
-                        .every((item) => TextDocumentEdit.canParse(item)))) ||
+                        (item.every((item) =>
+                            TextEdit.canParse(item, reporter)))))))))) {
+          reporter.reportError("must be of type Map<String, List<TextEdit>>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('documentChanges');
+      try {
+        if (obj['documentChanges'] != null &&
+            !(((obj['documentChanges'] is List &&
+                    (obj['documentChanges'].every((item) =>
+                        TextDocumentEdit.canParse(item, reporter)))) ||
                 (obj['documentChanges'] is List &&
                     (obj['documentChanges'].every((item) =>
-                        (TextDocumentEdit.canParse(item) ||
-                            CreateFile.canParse(item) ||
-                            RenameFile.canParse(item) ||
-                            DeleteFile.canParse(item)))))));
+                        (TextDocumentEdit.canParse(item, reporter) ||
+                            CreateFile.canParse(item, reporter) ||
+                            RenameFile.canParse(item, reporter) ||
+                            DeleteFile.canParse(item, reporter)))))))) {
+          reporter.reportError(
+              "must be of type Either2<List<TextDocumentEdit>, List<Either4<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>>>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type WorkspaceEdit");
+      return false;
+    }
   }
 
   @override
@@ -12300,12 +17413,47 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('uri') &&
-        obj['uri'] is String &&
-        obj.containsKey('name') &&
-        obj['name'] is String;
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('uri');
+      try {
+        if (!obj.containsKey('uri')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['uri'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['uri'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('name');
+      try {
+        if (!obj.containsKey('name')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['name'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['name'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type WorkspaceFolder");
+      return false;
+    }
   }
 
   @override
@@ -12368,14 +17516,51 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('added') &&
-        (obj['added'] is List &&
-            (obj['added'].every((item) => WorkspaceFolder.canParse(item)))) &&
-        obj.containsKey('removed') &&
-        (obj['removed'] is List &&
-            (obj['removed'].every((item) => WorkspaceFolder.canParse(item))));
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('added');
+      try {
+        if (!obj.containsKey('added')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['added'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!((obj['added'] is List &&
+            (obj['added']
+                .every((item) => WorkspaceFolder.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<WorkspaceFolder>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('removed');
+      try {
+        if (!obj.containsKey('removed')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['removed'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!((obj['removed'] is List &&
+            (obj['removed']
+                .every((item) => WorkspaceFolder.canParse(item, reporter)))))) {
+          reporter.reportError("must be of type List<WorkspaceFolder>");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type WorkspaceFoldersChangeEvent");
+      return false;
+    }
   }
 
   @override
@@ -12426,10 +17611,30 @@
     return __result;
   }
 
-  static bool canParse(Object obj) {
-    return obj is Map<String, dynamic> &&
-        obj.containsKey('query') &&
-        obj['query'] is String;
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('query');
+      try {
+        if (!obj.containsKey('query')) {
+          reporter.reportError("must not be undefined");
+          return false;
+        }
+        if (obj['query'] == null) {
+          reporter.reportError("must not be null");
+          return false;
+        }
+        if (!(obj['query'] is String)) {
+          reporter.reportError("must be of type String");
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError("must be of type WorkspaceSymbolParams");
+      return false;
+    }
   }
 
   @override
diff --git a/pkg/analysis_server/lib/lsp_protocol/protocol_special.dart b/pkg/analysis_server/lib/lsp_protocol/protocol_special.dart
index ca304eb..206c092 100644
--- a/pkg/analysis_server/lib/lsp_protocol/protocol_special.dart
+++ b/pkg/analysis_server/lib/lsp_protocol/protocol_special.dart
@@ -5,6 +5,7 @@
 import 'dart:async';
 
 import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
+import 'package:analysis_server/src/lsp/json_parsing.dart';
 
 const jsonRpcVersion = '2.0';
 
@@ -29,9 +30,9 @@
 
 ErrorOr<R> success<R>([R t]) => new ErrorOr<R>.success(t);
 
-Null _alwaysNull(_) => null;
+Null _alwaysNull(_, [__]) => null;
 
-bool _alwaysTrue(_) => true;
+bool _alwaysTrue(_, [__]) => true;
 
 class Either2<T1, T2> {
   final int _which;
@@ -227,7 +228,8 @@
 /// A helper to allow handlers to declare both a JSON validation function and
 /// parse function.
 class LspJsonHandler<T> {
-  final bool Function(Map<String, Object>) validateParams;
+  final bool Function(Map<String, Object>, LspJsonReporter reporter)
+      validateParams;
   final T Function(Map<String, Object>) convertParams;
 
   const LspJsonHandler(this.validateParams, this.convertParams);
diff --git a/pkg/analysis_server/lib/plugin/edit/fix/fix_dart.dart b/pkg/analysis_server/lib/plugin/edit/fix/fix_dart.dart
index 499c925..fb3c5ec 100644
--- a/pkg/analysis_server/lib/plugin/edit/fix/fix_dart.dart
+++ b/pkg/analysis_server/lib/plugin/edit/fix/fix_dart.dart
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analysis_server/plugin/edit/fix/fix_core.dart';
-import 'package:analysis_server/src/services/correction/fix/dart/top_level_declarations.dart';
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer_plugin/utilities/change_builder/change_workspace.dart';
 
@@ -22,10 +21,4 @@
    * The workspace in which the fix contributor operates.
    */
   ChangeWorkspace get workspace;
-
-  /**
-   * Return top-level declarations with the [name] in libraries that are
-   * available to this context.
-   */
-  List<TopLevelDeclaration> getTopLevelDeclarations(String name);
 }
diff --git a/pkg/analysis_server/lib/src/computer/computer_hover.dart b/pkg/analysis_server/lib/src/computer/computer_hover.dart
index 102a46f..413df0e 100644
--- a/pkg/analysis_server/lib/src/computer/computer_hover.dart
+++ b/pkg/analysis_server/lib/src/computer/computer_hover.dart
@@ -43,8 +43,15 @@
     }
     if (node is Expression) {
       Expression expression = node;
-      HoverInformation hover =
-          new HoverInformation(expression.offset, expression.length);
+      // For constructor calls the whole expression is selected (above) but this
+      // results in the range covering the whole call so narrow it to just the
+      // ConstructorName.
+      HoverInformation hover = expression is InstanceCreationExpression
+          ? new HoverInformation(
+              expression.constructorName.offset,
+              expression.constructorName.length,
+            )
+          : new HoverInformation(expression.offset, expression.length);
       // element
       Element element = ElementLocator.locate(expression);
       if (element != null) {
diff --git a/pkg/analysis_server/lib/src/edit/edit_domain.dart b/pkg/analysis_server/lib/src/edit/edit_domain.dart
index 5941656..99a39bd 100644
--- a/pkg/analysis_server/lib/src/edit/edit_domain.dart
+++ b/pkg/analysis_server/lib/src/edit/edit_domain.dart
@@ -23,7 +23,6 @@
 import 'package:analysis_server/src/services/correction/change_workspace.dart';
 import 'package:analysis_server/src/services/correction/fix.dart';
 import 'package:analysis_server/src/services/correction/fix/analysis_options/fix_generator.dart';
-import 'package:analysis_server/src/services/correction/fix/dart/top_level_declarations.dart';
 import 'package:analysis_server/src/services/correction/fix/manifest/fix_generator.dart';
 import 'package:analysis_server/src/services/correction/fix/pubspec/fix_generator.dart';
 import 'package:analysis_server/src/services/correction/fix_internal.dart';
@@ -635,16 +634,7 @@
         int errorLine = lineInfo.getLocation(error.offset).lineNumber;
         if (errorLine == requestLine) {
           var workspace = DartChangeWorkspace(server.currentSessions);
-          var context =
-              new DartFixContextImpl(workspace, result, error, (name) {
-            var tracker = server.declarationsTracker;
-            var provider = TopLevelDeclarationsProvider(tracker);
-            return provider.get(
-              result.session.analysisContext,
-              result.path,
-              name,
-            );
-          });
+          var context = new DartFixContextImpl(workspace, result, error);
           List<Fix> fixes =
               await new DartFixContributor().computeFixes(context);
           if (fixes.isNotEmpty) {
diff --git a/pkg/analysis_server/lib/src/edit/fix/fix_error_task.dart b/pkg/analysis_server/lib/src/edit/fix/fix_error_task.dart
index 378b3b1..e71fd1d 100644
--- a/pkg/analysis_server/lib/src/edit/fix/fix_error_task.dart
+++ b/pkg/analysis_server/lib/src/edit/fix/fix_error_task.dart
@@ -28,12 +28,7 @@
 
   Future<void> fixError(ResolvedUnitResult result, AnalysisError error) async {
     final workspace = DartChangeWorkspace(listener.server.currentSessions);
-    final dartContext = new DartFixContextImpl(
-      workspace,
-      result,
-      error,
-      (name) => [],
-    );
+    final dartContext = new DartFixContextImpl(workspace, result, error);
     final processor = new FixProcessor(dartContext);
     Fix fix = await processor.computeFix();
     final location = listener.locationFor(result, error.offset, error.length);
diff --git a/pkg/analysis_server/lib/src/lsp/channel/lsp_byte_stream_channel.dart b/pkg/analysis_server/lib/src/lsp/channel/lsp_byte_stream_channel.dart
index d412402..3c81264 100644
--- a/pkg/analysis_server/lib/src/lsp/channel/lsp_byte_stream_channel.dart
+++ b/pkg/analysis_server/lib/src/lsp/channel/lsp_byte_stream_channel.dart
@@ -10,6 +10,7 @@
 import 'package:analysis_server/lsp_protocol/protocol_special.dart';
 import 'package:analysis_server/src/analysis_server.dart';
 import 'package:analysis_server/src/lsp/channel/lsp_channel.dart';
+import 'package:analysis_server/src/lsp/json_parsing.dart';
 import 'package:analysis_server/src/lsp/lsp_packet_transformer.dart';
 import 'package:analyzer/instrumentation/instrumentation.dart';
 
@@ -91,11 +92,11 @@
     ServerPerformanceStatistics.serverChannel.makeCurrentWhile(() {
       _instrumentationService.logRequest(data);
       final Map<String, Object> json = jsonDecode(data);
-      if (RequestMessage.canParse(json)) {
+      if (RequestMessage.canParse(json, nullLspJsonReporter)) {
         onMessage(RequestMessage.fromJson(json));
-      } else if (NotificationMessage.canParse(json)) {
+      } else if (NotificationMessage.canParse(json, nullLspJsonReporter)) {
         onMessage(NotificationMessage.fromJson(json));
-      } else if (ResponseMessage.canParse(json)) {
+      } else if (ResponseMessage.canParse(json, nullLspJsonReporter)) {
         onMessage(ResponseMessage.fromJson(json));
       } else {
         _sendParseError();
diff --git a/pkg/analysis_server/lib/src/lsp/constants.dart b/pkg/analysis_server/lib/src/lsp/constants.dart
index 1801317..bb6a56f 100644
--- a/pkg/analysis_server/lib/src/lsp/constants.dart
+++ b/pkg/analysis_server/lib/src/lsp/constants.dart
@@ -43,10 +43,12 @@
     sortMembers,
     organizeImports,
     sendWorkspaceEdit,
+    performRefactor,
   ];
   static const sortMembers = 'edit.sortMembers';
   static const organizeImports = 'edit.organizeImports';
   static const sendWorkspaceEdit = 'edit.sendWorkspaceEdit';
+  static const performRefactor = 'refactor.perform';
 }
 
 abstract class CustomMethods {
@@ -84,6 +86,7 @@
   static const FileHasErrors = const ErrorCodes(-32008);
   static const ClientFailedToApplyEdit = const ErrorCodes(-32009);
   static const RenameNotValid = const ErrorCodes(-32010);
+  static const RefactorFailed = const ErrorCodes(-32011);
 
   /// An error raised when the server detects that the server and client are out
   /// of sync and cannot recover. For example if a textDocument/didChange notification
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/commands/perform_refactor.dart b/pkg/analysis_server/lib/src/lsp/handlers/commands/perform_refactor.dart
new file mode 100644
index 0000000..bd1e958
--- /dev/null
+++ b/pkg/analysis_server/lib/src/lsp/handlers/commands/perform_refactor.dart
@@ -0,0 +1,116 @@
+// Copyright (c) 2019, 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 'package:analysis_server/lsp_protocol/protocol_generated.dart';
+import 'package:analysis_server/lsp_protocol/protocol_special.dart';
+import 'package:analysis_server/src/lsp/constants.dart';
+import 'package:analysis_server/src/lsp/handlers/commands/simple_edit_handler.dart';
+import 'package:analysis_server/src/lsp/lsp_analysis_server.dart';
+import 'package:analysis_server/src/lsp/mapping.dart';
+import 'package:analysis_server/src/protocol_server.dart';
+import 'package:analysis_server/src/services/refactoring/refactoring.dart';
+import 'package:analyzer/dart/analysis/results.dart';
+
+class PerformRefactorCommandHandler extends SimpleEditCommandHandler {
+  PerformRefactorCommandHandler(LspAnalysisServer server) : super(server);
+
+  @override
+  String get commandName => 'Perform Refactor';
+
+  @override
+  Future<ErrorOr<void>> handle(List<dynamic> arguments) async {
+    if (arguments == null ||
+        arguments.length != 6 ||
+        arguments[0] is! String || // kind
+        arguments[1] is! String || // path
+        (arguments[2] != null && arguments[2] is! int) || // docVersion
+        arguments[3] is! int || // offset
+        arguments[4] is! int || // length
+        // options
+        (arguments[5] != null && arguments[5] is! Map<String, dynamic>)) {
+      // length
+      return ErrorOr.error(new ResponseError(
+        ServerErrorCodes.InvalidCommandArguments,
+        '$commandName requires 6 parameters: RefactoringKind, docVersion, filePath, offset, length, options (optional)',
+        null,
+      ));
+    }
+
+    String kind = arguments[0];
+    String path = arguments[1];
+    int docVersion = arguments[2];
+    int offset = arguments[3];
+    int length = arguments[4];
+    Map<String, dynamic> options = arguments[5];
+
+    final result = await requireResolvedUnit(path);
+    return result.mapResult((result) async {
+      return _getRefactoring(
+              RefactoringKind(kind), result, offset, length, options)
+          .mapResult((refactoring) async {
+        final status = await refactoring.checkAllConditions();
+
+        if (status.hasError) {
+          return error(ServerErrorCodes.RefactorFailed, status.message);
+        }
+
+        final change = await refactoring.createChange();
+
+        // If the file changed while we were validating and preparing the change,
+        // we should fail to avoid sending bad edits.
+        if (docVersion != null &&
+            docVersion != server.getVersionedDocumentIdentifier(path).version) {
+          return error(ErrorCodes.ContentModified,
+              'Content was modified before refactor was applied');
+        }
+
+        final edit = createWorkspaceEdit(server, change.edits);
+        return await sendWorkspaceEditToClient(edit);
+      });
+    });
+  }
+
+  ErrorOr<Refactoring> _getRefactoring(
+    RefactoringKind kind,
+    ResolvedUnitResult result,
+    int offset,
+    int length,
+    Map<String, dynamic> options,
+  ) {
+    switch (kind) {
+      case RefactoringKind.EXTRACT_METHOD:
+        final refactor = ExtractMethodRefactoring(
+            server.searchEngine, result, offset, length);
+        // TODO(dantup): For now we don't have a good way to prompt the user
+        // for a method name so we just use a placeholder and expect them to
+        // rename (this is what C#/Omnisharp does), but there's an open request
+        // to handle this better.
+        // https://github.com/microsoft/language-server-protocol/issues/764
+        refactor.name =
+            (options != null ? options['name'] : null) ?? 'newMethod';
+        // Defaults to true, but may be surprising if users didn't have an option
+        // to opt in.
+        refactor.extractAll = false;
+        return success(refactor);
+
+      case RefactoringKind.EXTRACT_WIDGET:
+        final refactor = ExtractWidgetRefactoring(
+            server.searchEngine, result, offset, length);
+        // TODO(dantup): For now we don't have a good way to prompt the user
+        // for a method name so we just use a placeholder and expect them to
+        // rename (this is what C#/Omnisharp does), but there's an open request
+        // to handle this better.
+        // https://github.com/microsoft/language-server-protocol/issues/764
+        refactor.name =
+            (options != null ? options['name'] : null) ?? 'NewWidget';
+        return success(refactor);
+
+      default:
+        return error(ServerErrorCodes.InvalidCommandArguments,
+            'Unknown RefactoringKind $kind was supplied to $commandName');
+    }
+  }
+}
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
index 326b25a..539a782 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
@@ -13,12 +13,13 @@
 import 'package:analysis_server/src/lsp/handlers/handlers.dart';
 import 'package:analysis_server/src/lsp/lsp_analysis_server.dart';
 import 'package:analysis_server/src/lsp/mapping.dart';
+import 'package:analysis_server/src/protocol_server.dart';
 import 'package:analysis_server/src/services/correction/assist.dart';
 import 'package:analysis_server/src/services/correction/assist_internal.dart';
 import 'package:analysis_server/src/services/correction/change_workspace.dart';
 import 'package:analysis_server/src/services/correction/fix.dart';
-import 'package:analysis_server/src/services/correction/fix/dart/top_level_declarations.dart';
 import 'package:analysis_server/src/services/correction/fix_internal.dart';
+import 'package:analysis_server/src/services/refactoring/refactoring.dart';
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/analysis/session.dart'
     show InconsistentAnalysisException;
@@ -164,7 +165,7 @@
       _getSourceActions(
           kinds, supportsLiterals, supportsWorkspaceApplyEdit, path),
       _getAssistActions(kinds, supportsLiterals, offset, length, unit),
-      _getRefactorActions(kinds, supportsLiterals, path, range, unit),
+      _getRefactorActions(kinds, supportsLiterals, path, offset, length, unit),
       _getFixActions(kinds, supportsLiterals, range, unit),
     ]);
     final flatResults = results.expand((x) => x).toList();
@@ -193,14 +194,7 @@
         int errorLine = lineInfo.getLocation(error.offset).lineNumber - 1;
         if (errorLine >= range.start.line && errorLine <= range.end.line) {
           var workspace = DartChangeWorkspace(server.currentSessions);
-          var context = new DartFixContextImpl(workspace, unit, error, (name) {
-            var tracker = server.declarationsTracker;
-            return TopLevelDeclarationsProvider(tracker).get(
-              unit.session.analysisContext,
-              unit.path,
-              name,
-            );
-          });
+          var context = new DartFixContextImpl(workspace, unit, error);
           final fixes = await fixContributor.computeFixes(context);
           if (fixes.isNotEmpty) {
             fixes.sort(Fix.SORT_BY_RELEVANCE);
@@ -225,18 +219,61 @@
     HashSet<CodeActionKind> clientSupportedCodeActionKinds,
     bool clientSupportsLiteralCodeActions,
     String path,
-    Range range,
+    int offset,
+    int length,
     ResolvedUnitResult unit,
   ) async {
-    // We only support these for clients that advertise codeActionLiteralSupport.
-    if (!clientSupportsLiteralCodeActions ||
+    // The refactor actions supported are only valid for Dart files.
+    if (!AnalysisEngine.isDartFileName(path)) {
+      return const [];
+    }
+
+    // If the client told us what kinds they support but it does not include
+    // Refactor then don't return any.
+    if (clientSupportsLiteralCodeActions &&
         !clientSupportedCodeActionKinds.contains(CodeActionKind.Refactor)) {
       return const [];
     }
 
+    /// Helper to create refactors that execute commands provided with
+    /// the current file, location and document version.
+    createRefactor(
+      CodeActionKind actionKind,
+      String name,
+      RefactoringKind refactorKind, [
+      Map<String, dynamic> options,
+    ]) {
+      return _commandOrCodeAction(
+          clientSupportsLiteralCodeActions,
+          actionKind,
+          new Command(name, Commands.performRefactor, [
+            refactorKind.toJson(),
+            path,
+            server.getVersionedDocumentIdentifier(path).version,
+            offset,
+            length,
+            options,
+          ]));
+    }
+
     try {
-      // TODO(dantup): Implement refactors.
-      return [];
+      final refactorActions = <Either2<Command, CodeAction>>[];
+
+      // Extract Method
+      if (ExtractMethodRefactoring(server.searchEngine, unit, offset, length)
+          .isAvailable()) {
+        refactorActions.add(createRefactor(CodeActionKind.RefactorExtract,
+            'Extract Method', RefactoringKind.EXTRACT_METHOD));
+      }
+
+      // Extract Widget
+      if (ExtractWidgetRefactoring(server.searchEngine, unit, offset, length)
+          .isAvailable()) {
+        refactorActions.add(createRefactor(CodeActionKind.RefactorExtract,
+            'Extract Widget', RefactoringKind.EXTRACT_WIDGET));
+      }
+
+      return refactorActions;
     } on InconsistentAnalysisException {
       // If an InconsistentAnalysisException occurs, it's likely the user modified
       // the source and therefore is no longer interested in the results, so
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
index 0602df0..52d8a9c 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
@@ -87,6 +87,37 @@
         ));
   }
 
+  /// Build a list of existing imports so we can filter out any suggestions
+  /// that resolve to the same underlying declared symbol.
+  /// Map with key "elementName/elementDeclaringLibraryUri"
+  /// Value is a set of imported URIs that import that element.
+  Map<String, Set<String>> _buildLookupOfImportedSymbols(
+      ResolvedUnitResult unit) {
+    final alreadyImportedSymbols = <String, Set<String>>{};
+    final importElementList = unit.libraryElement.imports;
+    for (var import in importElementList) {
+      final importedLibrary = import.importedLibrary;
+      if (importedLibrary == null) continue;
+
+      for (var element in import.namespace.definedNames.values) {
+        if (element.librarySource != null) {
+          final declaringLibraryUri = element.librarySource.uri;
+          final elementName = element.name;
+
+          final key =
+              _createImportedSymbolKey(elementName, declaringLibraryUri);
+          alreadyImportedSymbols.putIfAbsent(key, () => Set<String>());
+          alreadyImportedSymbols[key]
+              .add('${importedLibrary.librarySource.uri}');
+        }
+      }
+    }
+    return alreadyImportedSymbols;
+  }
+
+  String _createImportedSymbolKey(String name, Uri declaringUri) =>
+      '$name/$declaringUri';
+
   Future<ErrorOr<List<CompletionItem>>> _getItems(
     TextDocumentClientCapabilitiesCompletion completionCapabilities,
     HashSet<CompletionItemKind> clientSupportedCompletionKinds,
@@ -142,6 +173,10 @@
                   unit,
                 );
 
+      // Build a fast lookup for imported symbols so that we can filter out
+      // duplicates.
+      final alreadyImportedSymbols = _buildLookupOfImportedSymbols(unit);
+
       includedSuggestionSets.forEach((includedSet) {
         final library = server.declarationsTracker.getLibrary(includedSet.id);
         if (library == null) {
@@ -157,7 +192,23 @@
             // Filter to only the kinds we should return.
             .where((item) =>
                 includedElementKinds.contains(protocolElementKind(item.kind)))
-            .map((item) => declarationToCompletionItem(
+            .where((item) {
+          // Check existing imports to ensure we don't already import
+          // this element (this exact element from its declaring
+          // library, not just something with the same name). If we do
+          // we'll want to skip it.
+          final declaringUri = item.parent != null
+              ? item.parent.locationLibraryUri
+              : item.locationLibraryUri;
+          final key = _createImportedSymbolKey(item.name, declaringUri);
+          final importingUris = alreadyImportedSymbols[key];
+
+          // Keep it only if there are either:
+          // - no URIs importing it
+          // - the URIs importing it include this one
+          return importingUris == null ||
+              importingUris.contains('${library.uri}');
+        }).map((item) => declarationToCompletionItem(
                   completionCapabilities,
                   clientSupportedCompletionKinds,
                   unit.path,
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart
index aa54dae..38c5c23 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart
@@ -113,7 +113,7 @@
           );
         }
 
-        var newLabel = item.label;
+        var newInsertText = item.label;
         final builder = DartChangeBuilder(session);
         await builder.addFileEdit(libraryPath, (builder) {
           final result = builder.importLibraryElement(
@@ -124,7 +124,7 @@
             requestedElement: requestedElement,
           );
           if (result.prefix != null) {
-            newLabel = '${result.prefix}.$newLabel';
+            newInsertText = '${result.prefix}.$newInsertText';
           }
         });
 
@@ -155,9 +155,9 @@
         final documentation = asStringOrMarkupContent(formats, dartDoc);
 
         return success(CompletionItem(
-          newLabel,
+          item.label,
           item.kind,
-          data.displayUri != null
+          data.displayUri != null && thisFilesChanges.isNotEmpty
               ? "Auto import from '${data.displayUri}'\n\n${item.detail ?? ''}"
                   .trim()
               : item.detail,
@@ -166,13 +166,13 @@
           item.preselect,
           item.sortText,
           item.filterText,
-          newLabel,
+          newInsertText,
           item.insertTextFormat,
           new TextEdit(
             // TODO(dantup): If `clientSupportsSnippets == true` then we should map
             // `selection` in to a snippet (see how Dart Code does this).
             toRange(lineInfo, item.data.rOffset, item.data.rLength),
-            newLabel,
+            newInsertText,
           ),
           thisFilesChanges
               .expand((change) =>
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_execute_command.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_execute_command.dart
index f552cf6..9e11820 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_execute_command.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_execute_command.dart
@@ -8,6 +8,7 @@
 import 'package:analysis_server/lsp_protocol/protocol_special.dart';
 import 'package:analysis_server/src/lsp/constants.dart';
 import 'package:analysis_server/src/lsp/handlers/commands/organize_imports.dart';
+import 'package:analysis_server/src/lsp/handlers/commands/perform_refactor.dart';
 import 'package:analysis_server/src/lsp/handlers/commands/send_workspace_edit.dart';
 import 'package:analysis_server/src/lsp/handlers/commands/sort_members.dart';
 import 'package:analysis_server/src/lsp/handlers/handlers.dart';
@@ -22,6 +23,7 @@
       : commandHandlers = {
           Commands.sortMembers: new SortMembersCommandHandler(server),
           Commands.organizeImports: new OrganizeImportsCommandHandler(server),
+          Commands.performRefactor: new PerformRefactorCommandHandler(server),
           Commands.sendWorkspaceEdit:
               new SendWorkspaceEditCommandHandler(server),
         },
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_hover.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_hover.dart
index b724b1a..2d1bb84 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_hover.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_hover.dart
@@ -63,11 +63,6 @@
     if (hover.containingLibraryName != null &&
         hover.containingLibraryName.isNotEmpty) {
       content..writeln('*${hover.containingLibraryName}*')..writeln();
-    } else if (hover.containingLibraryPath != null) {
-      // TODO(dantup): Support displaying the package name (probably by adding
-      // containingPackageName to the main hover?) once the analyzer work to
-      // support this (inc Bazel/Gn) is done.
-      // content..writeln('*${hover.containingPackageName}*')..writeln();
     }
 
     // Doc comments.
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_initialize.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_initialize.dart
index 1aa7293..3b1d28a 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_initialize.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_initialize.dart
@@ -105,8 +105,8 @@
         null,
         null,
         true, // foldingRangeProvider
-        new ExecuteCommandOptions(Commands.serverSupportedCommands),
         null, // declarationProvider
+        new ExecuteCommandOptions(Commands.serverSupportedCommands),
         new ServerCapabilitiesWorkspace(
             new ServerCapabilitiesWorkspaceFolders(true, true)),
         null);
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handlers.dart b/pkg/analysis_server/lib/src/lsp/handlers/handlers.dart
index 89a8c1b..7d51d73 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handlers.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handlers.dart
@@ -9,6 +9,7 @@
 import 'package:analysis_server/src/lsp/constants.dart';
 import 'package:analysis_server/src/lsp/handlers/handler_cancel_request.dart';
 import 'package:analysis_server/src/lsp/handlers/handler_reject.dart';
+import 'package:analysis_server/src/lsp/json_parsing.dart';
 import 'package:analysis_server/src/lsp/lsp_analysis_server.dart';
 import 'package:analyzer/dart/analysis/results.dart';
 
@@ -85,9 +86,15 @@
   /// [NotificationMessage]s are not expected to return results.
   FutureOr<ErrorOr<R>> handleMessage(
       IncomingMessage message, CancellationToken token) {
-    if (!jsonHandler.validateParams(message.params)) {
-      return error(ErrorCodes.InvalidParams,
-          'Invalid params for ${message.method}', null);
+    final reporter = LspJsonReporter('params');
+    if (!jsonHandler.validateParams(message.params, reporter)) {
+      return error(
+        ErrorCodes.InvalidParams,
+        'Invalid params for ${message.method}:\n'
+                '${reporter.errors.isNotEmpty ? reporter.errors.first : ''}'
+            .trim(),
+        null,
+      );
     }
 
     final params = jsonHandler.convertParams(message.params);
diff --git a/pkg/analysis_server/lib/src/lsp/json_parsing.dart b/pkg/analysis_server/lib/src/lsp/json_parsing.dart
new file mode 100644
index 0000000..9a43a68
--- /dev/null
+++ b/pkg/analysis_server/lib/src/lsp/json_parsing.dart
@@ -0,0 +1,53 @@
+// Copyright (c) 2019, 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:collection';
+
+final nullLspJsonReporter = _NullLspJsonReporter();
+
+/// Tracks a path through a JSON object during validation to allow reporting
+/// validation errors with user-friendly paths to the invalid fields.
+class LspJsonReporter {
+  /// A list of errors collected so far.
+  final List<String> errors = [];
+
+  /// The path from the root object (usually `params`) to the current object
+  /// being validated.
+  final ListQueue<String> path = new ListQueue<String>();
+
+  LspJsonReporter([String initialField]) {
+    if (initialField != null) {
+      path.add(initialField);
+    }
+  }
+
+  /// Pops the last field off the stack to become the current gield.
+  void pop() => path.removeLast();
+
+  /// Pushes the current field onto a stack to allow reporting errors in child
+  /// properties.
+  void push(String field) => path.add(field);
+
+  /// Reports an error message for the field represented by [field] at [path].
+  void reportError(String message) {
+    errors.add('${path.join(".")} $message');
+  }
+}
+
+class _NullLspJsonReporter implements LspJsonReporter {
+  @override
+  final errors = const <String>[];
+
+  @override
+  final path = ListQueue<String>();
+
+  @override
+  void pop() {}
+
+  @override
+  void push(String field) {}
+
+  @override
+  void reportError(String message) {}
+}
diff --git a/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart b/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
index 301e631..de88f0f 100644
--- a/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
+++ b/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
@@ -32,6 +32,7 @@
 import 'package:analysis_server/src/services/search/search_engine.dart';
 import 'package:analysis_server/src/services/search/search_engine_internal.dart';
 import 'package:analysis_server/src/utilities/null_string_sink.dart';
+import 'package:analyzer/error/error.dart';
 import 'package:analyzer/exception/exception.dart';
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/instrumentation/instrumentation.dart';
@@ -601,7 +602,9 @@
         final serverErrors = protocol.mapEngineErrors(
             result.session.analysisContext.analysisOptions,
             result.lineInfo,
-            result.errors,
+            result.errors
+                .where((e) => e.errorCode.type != ErrorType.TODO)
+                .toList(),
             toDiagnostic);
 
         analysisServer.publishDiagnostics(result.path, serverErrors);
diff --git a/pkg/analysis_server/lib/src/services/correction/fix.dart b/pkg/analysis_server/lib/src/services/correction/fix.dart
index adfc446..7ca77e9 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix.dart
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analysis_server/plugin/edit/fix/fix_dart.dart';
-import 'package:analysis_server/src/services/correction/fix/dart/top_level_declarations.dart';
 import 'package:analysis_server/src/services/correction/fix_internal.dart';
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/error/error.dart';
@@ -120,16 +119,7 @@
   @override
   final AnalysisError error;
 
-  final List<TopLevelDeclaration> Function(String name)
-      getTopLevelDeclarationsFunction;
-
-  DartFixContextImpl(this.workspace, this.resolveResult, this.error,
-      this.getTopLevelDeclarationsFunction);
-
-  @override
-  List<TopLevelDeclaration> getTopLevelDeclarations(String name) {
-    return getTopLevelDeclarationsFunction(name);
-  }
+  DartFixContextImpl(this.workspace, this.resolveResult, this.error);
 }
 
 /// An enumeration of quick fix kinds found in a Dart file.
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/dart/top_level_declarations.dart b/pkg/analysis_server/lib/src/services/correction/fix/dart/top_level_declarations.dart
deleted file mode 100644
index ef4b987..0000000
--- a/pkg/analysis_server/lib/src/services/correction/fix/dart/top_level_declarations.dart
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright (c) 2019, 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 'package:analyzer/dart/analysis/analysis_context.dart';
-import 'package:analyzer/src/services/available_declarations.dart';
-
-/// Information about a single top-level declaration.
-class TopLevelDeclaration {
-  /// The path of the library that exports this declaration.
-  final String path;
-
-  /// The URI of the library that exports this declaration.
-  final Uri uri;
-
-  final TopLevelDeclarationKind kind;
-
-  final String name;
-
-  /// Is `true` if the declaration is exported, not declared in the [path].
-  final bool isExported;
-
-  TopLevelDeclaration(
-    this.path,
-    this.uri,
-    this.kind,
-    this.name,
-    this.isExported,
-  );
-
-  @override
-  String toString() => '($path, $uri, $kind, $name, $isExported)';
-}
-
-/// Kind of a top-level declaration.
-///
-/// We don't need it to be precise, just enough to support quick fixes.
-enum TopLevelDeclarationKind { type, function, variable }
-
-class TopLevelDeclarationsProvider {
-  final DeclarationsTracker tracker;
-
-  TopLevelDeclarationsProvider(this.tracker);
-
-  void doTrackerWork() {
-    while (tracker.hasWork) {
-      tracker.doWork();
-    }
-  }
-
-  List<TopLevelDeclaration> get(
-    AnalysisContext analysisContext,
-    String path,
-    String name,
-  ) {
-    var declarations = <TopLevelDeclaration>[];
-
-    void addDeclarations(Library library) {
-      for (var declaration in library.declarations) {
-        if (declaration.name != name) continue;
-
-        var kind = _getTopKind(declaration.kind);
-        if (kind == null) continue;
-
-        declarations.add(
-          TopLevelDeclaration(
-            library.path,
-            library.uri,
-            kind,
-            name,
-            declaration.locationLibraryUri != library.uri,
-          ),
-        );
-      }
-    }
-
-    var declarationsContext = tracker.getContext(analysisContext);
-    var libraries = declarationsContext.getLibraries(path);
-    libraries.context.forEach(addDeclarations);
-    libraries.dependencies.forEach(addDeclarations);
-    libraries.sdk.forEach(addDeclarations);
-
-    return declarations;
-  }
-
-  TopLevelDeclarationKind _getTopKind(DeclarationKind kind) {
-    switch (kind) {
-      case DeclarationKind.CLASS:
-      case DeclarationKind.CLASS_TYPE_ALIAS:
-      case DeclarationKind.ENUM:
-      case DeclarationKind.FUNCTION_TYPE_ALIAS:
-      case DeclarationKind.MIXIN:
-        return TopLevelDeclarationKind.type;
-        break;
-      case DeclarationKind.FUNCTION:
-        return TopLevelDeclarationKind.function;
-        break;
-      case DeclarationKind.GETTER:
-      case DeclarationKind.SETTER:
-      case DeclarationKind.VARIABLE:
-        return TopLevelDeclarationKind.variable;
-        break;
-      default:
-        return null;
-    }
-  }
-}
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index 4cd9e33..dc43fd4 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -10,7 +10,6 @@
 import 'package:analysis_server/plugin/edit/fix/fix_dart.dart';
 import 'package:analysis_server/src/services/completion/dart/utilities.dart';
 import 'package:analysis_server/src/services/correction/fix.dart';
-import 'package:analysis_server/src/services/correction/fix/dart/top_level_declarations.dart';
 import 'package:analysis_server/src/services/correction/levenshtein.dart';
 import 'package:analysis_server/src/services/correction/namespace.dart';
 import 'package:analysis_server/src/services/correction/strings.dart';
@@ -27,6 +26,7 @@
 import 'package:analyzer/error/error.dart';
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/src/dart/analysis/session_helper.dart';
+import 'package:analyzer/src/dart/analysis/top_level_declaration.dart';
 import 'package:analyzer/src/dart/ast/ast.dart';
 import 'package:analyzer/src/dart/ast/token.dart';
 import 'package:analyzer/src/dart/ast/utilities.dart';
@@ -105,11 +105,7 @@
     // For each fix, put the fix into the HashMap.
     for (int i = 0; i < allAnalysisErrors.length; i++) {
       final FixContext fixContextI = new DartFixContextImpl(
-        context.workspace,
-        context.resolveResult,
-        allAnalysisErrors[i],
-        (name) => [],
-      );
+          context.workspace, context.resolveResult, allAnalysisErrors[i]);
       final FixProcessor processorI = new FixProcessor(fixContextI);
       final List<Fix> fixesListI = await processorI.compute();
       for (Fix f in fixesListI) {
@@ -2416,7 +2412,7 @@
     }
     // may be there is an existing import,
     // but it is with prefix and we don't use this prefix
-    var alreadyImportedWithPrefix = new Set<String>();
+    Set<Source> alreadyImportedWithPrefix = new Set<Source>();
     for (ImportElement imp in unitLibraryElement.imports) {
       // prepare element
       LibraryElement libraryElement = imp.importedLibrary;
@@ -2458,7 +2454,7 @@
           libraryName = libraryElement.source.shortName;
         }
         // don't add this library again
-        alreadyImportedWithPrefix.add(libraryElement.source.fullName);
+        alreadyImportedWithPrefix.add(libraryElement.source);
         // update library
         String newShowCode = 'show ${showNames.join(', ')}';
         int offset = showCombinator.offset;
@@ -2477,21 +2473,25 @@
     }
     // Find new top-level declarations.
     {
-      var declarations = await context.getTopLevelDeclarations(name);
-      for (var declaration in declarations) {
+      var declarations = await session.getTopLevelDeclarations(name);
+      for (TopLevelDeclarationInSource declaration in declarations) {
         // Check the kind.
-        if (!kinds2.contains(declaration.kind)) {
+        if (!kinds2.contains(declaration.declaration.kind)) {
           continue;
         }
         // Check the source.
-        if (alreadyImportedWithPrefix.contains(declaration.path)) {
+        Source librarySource = declaration.source;
+        if (alreadyImportedWithPrefix.contains(librarySource)) {
+          continue;
+        }
+        if (!_isSourceVisibleToLibrary(librarySource)) {
           continue;
         }
         // Compute the fix kind.
         FixKind fixKind;
-        if (declaration.uri.isScheme('dart')) {
+        if (librarySource.isInSystemLibrary) {
           fixKind = DartFixKind.IMPORT_LIBRARY_SDK;
-        } else if (_isLibSrcPath(declaration.path)) {
+        } else if (_isLibSrcPath(librarySource.fullName)) {
           // Bad: non-API.
           fixKind = DartFixKind.IMPORT_LIBRARY_PROJECT3;
         } else if (declaration.isExported) {
@@ -2503,8 +2503,8 @@
         }
         // Add the fix.
         var relativeURI =
-            _getRelativeURIFromLibrary(unitLibraryElement, declaration.path);
-        await _addFix_importLibrary(fixKind, declaration.uri, relativeURI);
+            _getRelativeURIFromLibrary(unitLibraryElement, librarySource);
+        await _addFix_importLibrary(fixKind, librarySource.uri, relativeURI);
       }
     }
   }
@@ -4246,20 +4246,21 @@
   }
 
   /**
-   * Return the relative uri from the passed [library] to the given [path].
-   * If the [path] is not in the LibraryElement, `null` is returned.
+   * Return the relative uri from the passed [library] to the passed
+   * [source]. If the [source] is not in the LibraryElement, `null` is returned.
    */
-  String _getRelativeURIFromLibrary(LibraryElement library, String path) {
+  String _getRelativeURIFromLibrary(LibraryElement library, Source source) {
     var librarySource = library?.librarySource;
     if (librarySource == null) {
       return null;
     }
     var pathCtx = resourceProvider.pathContext;
     var libraryDirectory = pathCtx.dirname(librarySource.fullName);
-    var sourceDirectory = pathCtx.dirname(path);
-    if (pathCtx.isWithin(libraryDirectory, path) ||
+    var sourceDirectory = pathCtx.dirname(source.fullName);
+    if (pathCtx.isWithin(libraryDirectory, source.fullName) ||
         pathCtx.isWithin(sourceDirectory, libraryDirectory)) {
-      String relativeFile = pathCtx.relative(path, from: libraryDirectory);
+      String relativeFile =
+          pathCtx.relative(source.fullName, from: libraryDirectory);
       return pathCtx.split(relativeFile).join('/');
     }
     return null;
@@ -4468,6 +4469,30 @@
     return false;
   }
 
+  /**
+   * Return `true` if the [source] can be imported into current library.
+   */
+  bool _isSourceVisibleToLibrary(Source source) {
+    String path = source.fullName;
+
+    var contextRoot = context.resolveResult.session.analysisContext.contextRoot;
+    if (contextRoot == null) {
+      return true;
+    }
+
+    // We don't want to use private libraries of other packages.
+    if (source.uri.isScheme('package') && _isLibSrcPath(path)) {
+      return contextRoot.root.contains(path);
+    }
+
+    // We cannot use relative URIs to reference files outside of our package.
+    if (source.uri.isScheme('file')) {
+      return contextRoot.root.contains(path);
+    }
+
+    return true;
+  }
+
   bool _isToListMethodElement(MethodElement method) {
     if (method.name != 'toList') {
       return false;
diff --git a/pkg/analysis_server/test/abstract_context.dart b/pkg/analysis_server/test/abstract_context.dart
index 578053b..9b67afb8 100644
--- a/pkg/analysis_server/test/abstract_context.dart
+++ b/pkg/analysis_server/test/abstract_context.dart
@@ -21,6 +21,7 @@
 import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
 
 import 'src/utilities/flutter_util.dart';
+import 'src/utilities/meta_util.dart';
 
 /**
  * Finds an [Element] with the given [name].
@@ -68,111 +69,8 @@
   }
 
   void addMetaPackage() {
-    addPackageFile('meta', 'meta.dart', r'''
-library meta;
-
-const _AlwaysThrows alwaysThrows = const _AlwaysThrows();
-
-@deprecated
-const _Checked checked = const _Checked();
-
-const _Experimental experimental = const _Experimental();
-
-const _Factory factory = const _Factory();
-
-const Immutable immutable = const Immutable();
-
-const _IsTest isTest = const _IsTest();
-
-const _IsTestGroup isTestGroup = const _IsTestGroup();
-
-const _Literal literal = const _Literal();
-
-const _MustCallSuper mustCallSuper = const _MustCallSuper();
-
-const _OptionalTypeArgs optionalTypeArgs = const _OptionalTypeArgs();
-
-const _Protected protected = const _Protected();
-
-const Required required = const Required();
-
-const _Sealed sealed = const _Sealed();
-
-@deprecated
-const _Virtual virtual = const _Virtual();
-
-const _VisibleForOverriding visibleForOverriding =
-    const _VisibleForOverriding();
-
-const _VisibleForTesting visibleForTesting = const _VisibleForTesting();
-
-class Immutable {
-  final String reason;
-  const Immutable([this.reason]);
-}
-
-class Required {
-  final String reason;
-  const Required([this.reason]);
-}
-
-class _AlwaysThrows {
-  const _AlwaysThrows();
-}
-
-class _Checked {
-  const _Checked();
-}
-
-class _Experimental {
-  const _Experimental();
-}
-
-class _Factory {
-  const _Factory();
-}
-
-class _IsTest {
-  const _IsTest();
-}
-
-class _IsTestGroup {
-  const _IsTestGroup();
-}
-
-class _Literal {
-  const _Literal();
-}
-
-class _MustCallSuper {
-  const _MustCallSuper();
-}
-
-class _OptionalTypeArgs {
-  const _OptionalTypeArgs();
-}
-
-class _Protected {
-  const _Protected();
-}
-
-class _Sealed {
-  const _Sealed();
-}
-
-@deprecated
-class _Virtual {
-  const _Virtual();
-}
-
-class _VisibleForOverriding {
-  const _VisibleForOverriding();
-}
-
-class _VisibleForTesting {
-  const _VisibleForTesting();
-}
-''');
+    Folder libFolder = configureMetaPackage(resourceProvider);
+    addTestPackageDependency('meta', libFolder.parent.path);
   }
 
   /// Add a new file with the given [pathInLib] to the package with the
@@ -194,7 +92,8 @@
 
   void addTestPackageDependency(String name, String rootPath) {
     var packagesFile = getFile('/home/test/.packages');
-    var packagesContent = packagesFile.readAsStringSync();
+    var packagesContent =
+        packagesFile.exists ? packagesFile.readAsStringSync() : '';
 
     // Ignore if there is already the same package dependency.
     if (packagesContent.contains('$name:file://')) {
diff --git a/pkg/analysis_server/test/analysis/get_hover_test.dart b/pkg/analysis_server/test/analysis/get_hover_test.dart
index 3c1219a..0a94c06 100644
--- a/pkg/analysis_server/test/analysis/get_hover_test.dart
+++ b/pkg/analysis_server/test/analysis/get_hover_test.dart
@@ -83,8 +83,8 @@
 ''');
     void onConstructor(HoverInformation hover) {
       // range
-      expect(hover.offset, findOffset('new A'));
-      expect(hover.length, 'new A.named()'.length);
+      expect(hover.offset, findOffset('new A') + 'new '.length);
+      expect(hover.length, 'A.named'.length);
       // element
       expect(hover.dartdoc, 'my doc');
       expect(hover.elementDescription, 'A A.named()');
@@ -114,7 +114,7 @@
     HoverInformation hover = await prepareHover('A(0)');
     // range
     expect(hover.offset, findOffset('A(0)'));
-    expect(hover.length, 'A(0)'.length);
+    expect(hover.length, 'A'.length);
     // element
     expect(hover.containingLibraryName, 'bin/test.dart');
     expect(hover.containingLibraryPath, testFile);
@@ -139,7 +139,7 @@
     HoverInformation hover = await prepareHover('A()');
     // range
     expect(hover.offset, findOffset('A()'));
-    expect(hover.length, 'A()'.length);
+    expect(hover.length, 'A'.length);
     // element
     expect(hover.containingLibraryName, 'bin/test.dart');
     expect(hover.containingLibraryPath, testFile);
@@ -164,8 +164,8 @@
 ''');
     HoverInformation hover = await prepareHover('new A');
     // range
-    expect(hover.offset, findOffset('new A'));
-    expect(hover.length, 'new A()'.length);
+    expect(hover.offset, findOffset('new A') + 'new '.length);
+    expect(hover.length, 'A'.length);
     // element
     expect(hover.containingLibraryName, 'bin/test.dart');
     expect(hover.containingLibraryPath, testFile);
@@ -189,8 +189,8 @@
 ''');
     void onConstructor(HoverInformation hover) {
       // range
-      expect(hover.offset, findOffset('new A<String>'));
-      expect(hover.length, 'new A<String>()'.length);
+      expect(hover.offset, findOffset('A<String>'));
+      expect(hover.length, 'A<String>'.length);
       // element
       expect(hover.containingLibraryName, 'bin/test.dart');
       expect(hover.containingLibraryPath, testFile);
diff --git a/pkg/analysis_server/test/analysis_abstract.dart b/pkg/analysis_server/test/analysis_abstract.dart
index 22715a4..f4593b4 100644
--- a/pkg/analysis_server/test/analysis_abstract.dart
+++ b/pkg/analysis_server/test/analysis_abstract.dart
@@ -135,12 +135,6 @@
     handleSuccessfulRequest(request, handler: analysisHandler);
   }
 
-  void doAllDeclarationsTrackerWork() {
-    while (server.declarationsTracker.hasWork) {
-      server.declarationsTracker.doWork();
-    }
-  }
-
   /**
    * Returns the offset of [search] in [testCode].
    * Fails if not found.
diff --git a/pkg/analysis_server/test/edit/fixes_test.dart b/pkg/analysis_server/test/edit/fixes_test.dart
index 22a7245..e80a447 100644
--- a/pkg/analysis_server/test/edit/fixes_test.dart
+++ b/pkg/analysis_server/test/edit/fixes_test.dart
@@ -40,7 +40,6 @@
 }
 ''');
     await waitForTasksFinished();
-    doAllDeclarationsTrackerWork();
     List<AnalysisErrorFixes> errorFixes =
         await _getFixesAt('Completer<String>');
     expect(errorFixes, hasLength(1));
@@ -144,6 +143,9 @@
   bbb: any
 ''');
 
+    // Ensure that the target is analyzed as an implicit source.
+    newFile('/aaa/lib/foo.dart', content: 'import "package:bbb/target.dart";');
+
     newFolder('/bbb');
     newFile('/bbb/.packages', content: '''
 bbb:${toUri('/bbb/lib')}
@@ -161,7 +163,6 @@
     _addOverlay(testFile, testCode);
 
     await waitForTasksFinished();
-    doAllDeclarationsTrackerWork();
 
     List<String> fixes = (await _getFixesAt('Foo()'))
         .single
diff --git a/pkg/analysis_server/test/lsp/code_actions_abstract.dart b/pkg/analysis_server/test/lsp/code_actions_abstract.dart
index 64aea35..d10c35c 100644
--- a/pkg/analysis_server/test/lsp/code_actions_abstract.dart
+++ b/pkg/analysis_server/test/lsp/code_actions_abstract.dart
@@ -41,11 +41,14 @@
   }
 
   Either2<Command, CodeAction> findCommand(
-      List<Either2<Command, CodeAction>> actions, String commandID) {
+      List<Either2<Command, CodeAction>> actions, String commandID,
+      [String wantedTitle]) {
     for (var codeAction in actions) {
       final id = codeAction.map(
           (cmd) => cmd.command, (action) => action.command.command);
-      if (id == commandID) {
+      final title =
+          codeAction.map((cmd) => cmd.title, (action) => action.title);
+      if (id == commandID && (wantedTitle == null || wantedTitle == title)) {
         return codeAction;
       }
     }
@@ -67,4 +70,64 @@
     }
     return null;
   }
+
+  /// Verifies that executing the given code actions command on the server
+  /// results in an edit being sent in the client that updates the file to match
+  /// the expected content.
+  Future verifyCodeActionEdits(Either2<Command, CodeAction> codeAction,
+      String content, String expectedContent,
+      {bool expectDocumentChanges = false}) async {
+    final command = codeAction.map(
+      (command) => command,
+      (codeAction) => codeAction.command,
+    );
+
+    await verifyCommandEdits(command, content, expectedContent,
+        expectDocumentChanges: expectDocumentChanges);
+  }
+
+  /// Verifies that executing the given command on the server results in an edit
+  /// being sent in the client that updates the file to match the expected
+  /// content.
+  Future<void> verifyCommandEdits(
+      Command command, String content, String expectedContent,
+      {bool expectDocumentChanges = false}) async {
+    ApplyWorkspaceEditParams editParams;
+
+    final commandResponse = await handleExpectedRequest<Object,
+        ApplyWorkspaceEditParams, ApplyWorkspaceEditResponse>(
+      Method.workspace_applyEdit,
+      () => executeCommand(command),
+      handler: (edit) {
+        // When the server sends the edit back, just keep a copy and say we
+        // applied successfully (it'll be verified below).
+        editParams = edit;
+        return new ApplyWorkspaceEditResponse(true, null);
+      },
+    );
+    // Successful edits return an empty success() response.
+    expect(commandResponse, isNull);
+
+    // Ensure the edit came back, and using the expected changes.
+    expect(editParams, isNotNull);
+    if (expectDocumentChanges) {
+      expect(editParams.edit.changes, isNull);
+      expect(editParams.edit.documentChanges, isNotNull);
+    } else {
+      expect(editParams.edit.changes, isNotNull);
+      expect(editParams.edit.documentChanges, isNull);
+    }
+
+    // Ensure applying the changes will give us the expected content.
+    final contents = {
+      mainFilePath: withoutMarkers(content),
+    };
+
+    if (expectDocumentChanges) {
+      applyDocumentChanges(contents, editParams.edit.documentChanges);
+    } else {
+      applyChanges(contents, editParams.edit.changes);
+    }
+    expect(contents[mainFilePath], equals(expectedContent));
+  }
 }
diff --git a/pkg/analysis_server/test/lsp/code_actions_refactor_test.dart b/pkg/analysis_server/test/lsp/code_actions_refactor_test.dart
new file mode 100644
index 0000000..d232188
--- /dev/null
+++ b/pkg/analysis_server/test/lsp/code_actions_refactor_test.dart
@@ -0,0 +1,167 @@
+// Copyright (c) 2019, 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 'package:analysis_server/src/lsp/constants.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../src/utilities/flutter_util.dart' as flutter;
+import '../src/utilities/meta_util.dart' as meta;
+import 'code_actions_abstract.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(ExtractMethodRefactorCodeActionsTest);
+    defineReflectiveTests(ExtractWidgetRefactorCodeActionsTest);
+  });
+}
+
+@reflectiveTest
+class ExtractMethodRefactorCodeActionsTest extends AbstractCodeActionsTest {
+  final extractMethodTitle = 'Extract Method';
+  test_appliesCorrectEdits() async {
+    const content = '''
+main() {
+  print('Test!');
+  [[print('Test!');]]
+}
+    ''';
+    const expectedContent = '''
+main() {
+  print('Test!');
+  newMethod();
+}
+
+void newMethod() {
+  print('Test!');
+}
+    ''';
+    await newFile(mainFilePath, content: withoutMarkers(content));
+    await initialize();
+
+    final codeActions = await getCodeActions(mainFileUri.toString(),
+        range: rangeFromMarkers(content));
+    final codeAction =
+        findCommand(codeActions, Commands.performRefactor, extractMethodTitle);
+    expect(codeAction, isNotNull);
+
+    await verifyCodeActionEdits(
+        codeAction, withoutMarkers(content), expectedContent);
+  }
+
+  test_invalidLocation() async {
+    const content = '''
+import 'dart:convert';
+^
+main() {}
+    ''';
+    await newFile(mainFilePath, content: content);
+    await initialize();
+
+    final codeActions = await getCodeActions(mainFileUri.toString());
+    final codeAction =
+        findCommand(codeActions, Commands.performRefactor, extractMethodTitle);
+    expect(codeAction, isNull);
+  }
+}
+
+@reflectiveTest
+class ExtractWidgetRefactorCodeActionsTest extends AbstractCodeActionsTest {
+  final extractWidgetTitle = 'Extract Widget';
+
+  @override
+  void setUp() {
+    super.setUp();
+
+    final flutterLibFolder = flutter.configureFlutterPackage(resourceProvider);
+    final metaLibFolder = meta.configureMetaPackage(resourceProvider);
+    // Create .packages in the project.
+    newFile(join(projectFolderPath, '.packages'), content: '''
+flutter:${flutterLibFolder.toUri()}
+meta:${metaLibFolder.toUri()}
+''');
+  }
+
+  test_appliesCorrectEdits() async {
+    const content = '''
+import 'package:flutter/material.dart';
+
+class MyWidget extends StatelessWidget {
+  @override
+  Widget build(BuildContext context) {
+    return new Row(
+      children: <Widget>[
+        new [[Column]](
+          children: <Widget>[
+            new Text('AAA'),
+            new Text('BBB'),
+          ],
+        ),
+        new Text('CCC'),
+        new Text('DDD'),
+      ],
+    );
+  }
+}
+    ''';
+    const expectedContent = '''
+import 'package:flutter/material.dart';
+
+class MyWidget extends StatelessWidget {
+  @override
+  Widget build(BuildContext context) {
+    return new Row(
+      children: <Widget>[
+        new NewWidget(),
+        new Text('CCC'),
+        new Text('DDD'),
+      ],
+    );
+  }
+}
+
+class NewWidget extends StatelessWidget {
+  const NewWidget({
+    Key key,
+  }) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return new Column(
+      children: <Widget>[
+        new Text('AAA'),
+        new Text('BBB'),
+      ],
+    );
+  }
+}
+    ''';
+    await newFile(mainFilePath, content: withoutMarkers(content));
+    await initialize();
+
+    final codeActions = await getCodeActions(mainFileUri.toString(),
+        range: rangeFromMarkers(content));
+    final codeAction =
+        findCommand(codeActions, Commands.performRefactor, extractWidgetTitle);
+    expect(codeAction, isNotNull);
+
+    await verifyCodeActionEdits(
+        codeAction, withoutMarkers(content), expectedContent);
+  }
+
+  test_invalidLocation() async {
+    const content = '''
+import 'dart:convert';
+^
+main() {}
+    ''';
+    await newFile(mainFilePath, content: content);
+    await initialize();
+
+    final codeActions = await getCodeActions(mainFileUri.toString());
+    final codeAction =
+        findCommand(codeActions, Commands.performRefactor, extractWidgetTitle);
+    expect(codeAction, isNull);
+  }
+}
diff --git a/pkg/analysis_server/test/lsp/code_actions_source_test.dart b/pkg/analysis_server/test/lsp/code_actions_source_test.dart
index fb1daa5..6a9307f 100644
--- a/pkg/analysis_server/test/lsp/code_actions_source_test.dart
+++ b/pkg/analysis_server/test/lsp/code_actions_source_test.dart
@@ -44,38 +44,8 @@
     final codeAction = findCommand(codeActions, Commands.organizeImports);
     expect(codeAction, isNotNull);
 
-    final command = codeAction.map(
-      (command) => command,
-      (codeAction) => codeAction.command,
-    );
-
-    ApplyWorkspaceEditParams editParams;
-
-    final commandResponse = await handleExpectedRequest<Object,
-        ApplyWorkspaceEditParams, ApplyWorkspaceEditResponse>(
-      Method.workspace_applyEdit,
-      () => executeCommand(command),
-      handler: (edit) {
-        // When the server sends the edit back, just keep a copy and say we
-        // applied successfully (it'll be verified below).
-        editParams = edit;
-        return new ApplyWorkspaceEditResponse(true, null);
-      },
-    );
-    // Successful edits return an empty success() response.
-    expect(commandResponse, isNull);
-
-    // Ensure the edit came back, and using the documentChanges.
-    expect(editParams, isNotNull);
-    expect(editParams.edit.documentChanges, isNotNull);
-    expect(editParams.edit.changes, isNull);
-
-    // Ensure applying the changes will give us the expected content.
-    final contents = {
-      mainFilePath: withoutMarkers(content),
-    };
-    applyDocumentChanges(contents, editParams.edit.documentChanges);
-    expect(contents[mainFilePath], equals(expectedContent));
+    await verifyCodeActionEdits(codeAction, content, expectedContent,
+        expectDocumentChanges: true);
   }
 
   test_appliesCorrectEdits_withoutDocumentChangesSupport() async {
@@ -103,38 +73,7 @@
     final codeAction = findCommand(codeActions, Commands.organizeImports);
     expect(codeAction, isNotNull);
 
-    final command = codeAction.map(
-      (command) => command,
-      (codeAction) => codeAction.command,
-    );
-
-    ApplyWorkspaceEditParams editParams;
-
-    final commandResponse = await handleExpectedRequest<Object,
-        ApplyWorkspaceEditParams, ApplyWorkspaceEditResponse>(
-      Method.workspace_applyEdit,
-      () => executeCommand(command),
-      handler: (edit) {
-        // When the server sends the edit back, just keep a copy and say we
-        // applied successfully (it'll be verified below).
-        editParams = edit;
-        return new ApplyWorkspaceEditResponse(true, null);
-      },
-    );
-    // Successful edits return an empty success() response.
-    expect(commandResponse, isNull);
-
-    // Ensure the edit came back, and using changes.
-    expect(editParams, isNotNull);
-    expect(editParams.edit.changes, isNotNull);
-    expect(editParams.edit.documentChanges, isNull);
-
-    // Ensure applying the changes will give us the expected content.
-    final contents = {
-      mainFilePath: withoutMarkers(content),
-    };
-    applyChanges(contents, editParams.edit.changes);
-    expect(contents[mainFilePath], equals(expectedContent));
+    await verifyCodeActionEdits(codeAction, content, expectedContent);
   }
 
   test_availableAsCodeActionLiteral() async {
@@ -261,38 +200,8 @@
     final codeAction = findCommand(codeActions, Commands.sortMembers);
     expect(codeAction, isNotNull);
 
-    final command = codeAction.map(
-      (command) => command,
-      (codeAction) => codeAction.command,
-    );
-
-    ApplyWorkspaceEditParams editParams;
-
-    final commandResponse = await handleExpectedRequest<Object,
-        ApplyWorkspaceEditParams, ApplyWorkspaceEditResponse>(
-      Method.workspace_applyEdit,
-      () => executeCommand(command),
-      handler: (edit) {
-        // When the server sends the edit back, just keep a copy and say we
-        // applied successfully (it'll be verified below).
-        editParams = edit;
-        return new ApplyWorkspaceEditResponse(true, null);
-      },
-    );
-    // Successful edits return an empty success() response.
-    expect(commandResponse, isNull);
-
-    // Ensure the edit came back, and using the documentChanges.
-    expect(editParams, isNotNull);
-    expect(editParams.edit.documentChanges, isNotNull);
-    expect(editParams.edit.changes, isNull);
-
-    // Ensure applying the changes will give us the expected content.
-    final contents = {
-      mainFilePath: withoutMarkers(content),
-    };
-    applyDocumentChanges(contents, editParams.edit.documentChanges);
-    expect(contents[mainFilePath], equals(expectedContent));
+    await verifyCodeActionEdits(codeAction, content, expectedContent,
+        expectDocumentChanges: true);
   }
 
   test_appliesCorrectEdits_withoutDocumentChangesSupport() async {
@@ -313,38 +222,7 @@
     final codeAction = findCommand(codeActions, Commands.sortMembers);
     expect(codeAction, isNotNull);
 
-    final command = codeAction.map(
-      (command) => command,
-      (codeAction) => codeAction.command,
-    );
-
-    ApplyWorkspaceEditParams editParams;
-
-    final commandResponse = await handleExpectedRequest<Object,
-        ApplyWorkspaceEditParams, ApplyWorkspaceEditResponse>(
-      Method.workspace_applyEdit,
-      () => executeCommand(command),
-      handler: (edit) {
-        // When the server sends the edit back, just keep a copy and say we
-        // applied successfully (it'll be verified below).
-        editParams = edit;
-        return new ApplyWorkspaceEditResponse(true, null);
-      },
-    );
-    // Successful edits return an empty success() response.
-    expect(commandResponse, isNull);
-
-    // Ensure the edit came back, and using changes.
-    expect(editParams, isNotNull);
-    expect(editParams.edit.changes, isNotNull);
-    expect(editParams.edit.documentChanges, isNull);
-
-    // Ensure applying the changes will give us the expected content.
-    final contents = {
-      mainFilePath: withoutMarkers(content),
-    };
-    applyChanges(contents, editParams.edit.changes);
-    expect(contents[mainFilePath], equals(expectedContent));
+    await verifyCodeActionEdits(codeAction, content, expectedContent);
   }
 
   test_availableAsCodeActionLiteral() async {
diff --git a/pkg/analysis_server/test/lsp/completion_test.dart b/pkg/analysis_server/test/lsp/completion_test.dart
index 4e7b84b..040ff87 100644
--- a/pkg/analysis_server/test/lsp/completion_test.dart
+++ b/pkg/analysis_server/test/lsp/completion_test.dart
@@ -17,6 +17,16 @@
 
 @reflectiveTest
 class CompletionTest extends AbstractLspAnalysisServerTest {
+  expectAutoImportCompletion(List<CompletionItem> items, String file) {
+    expect(
+      items.singleWhere(
+        (c) => c.detail?.contains("Auto import from '$file'") ?? false,
+        orElse: () => null,
+      ),
+      isNotNull,
+    );
+  }
+
   test_completionKinds_default() async {
     newFile(join(projectFolderPath, 'file.dart'));
     newFolder(join(projectFolderPath, 'folder'));
@@ -296,6 +306,137 @@
     '''));
   }
 
+  test_suggestionSets_doesNotFilterSymbolsWithSameName() async {
+    // Classes here are not re-exports, so should not be filtered out.
+    newFile(
+      join(projectFolderPath, 'source_file1.dart'),
+      content: 'class MyDuplicatedClass {}',
+    );
+    newFile(
+      join(projectFolderPath, 'source_file2.dart'),
+      content: 'class MyDuplicatedClass {}',
+    );
+    newFile(
+      join(projectFolderPath, 'source_file3.dart'),
+      content: 'class MyDuplicatedClass {}',
+    );
+
+    final content = '''
+main() {
+  MyDuplicated^
+}
+    ''';
+
+    final initialAnalysis = waitForAnalysisComplete();
+    await initialize(
+        workspaceCapabilities:
+            withApplyEditSupport(emptyWorkspaceClientCapabilities));
+    await openFile(mainFileUri, withoutMarkers(content));
+    await initialAnalysis;
+    final res = await getCompletion(mainFileUri, positionFromMarker(content));
+
+    final completions =
+        res.where((c) => c.label == 'MyDuplicatedClass').toList();
+    expect(completions, hasLength(3));
+
+    // Resolve the completions so we can get the auto-import text.
+    final resolvedCompletions =
+        await Future.wait(completions.map(resolveCompletion));
+
+    expectAutoImportCompletion(resolvedCompletions, '../source_file1.dart');
+    expectAutoImportCompletion(resolvedCompletions, '../source_file2.dart');
+    expectAutoImportCompletion(resolvedCompletions, '../source_file3.dart');
+  }
+
+  test_suggestionSets_filtersOutAlreadyImportedSymbols() async {
+    newFile(
+      join(projectFolderPath, 'source_file.dart'),
+      content: '''
+      class MyExportedClass {}
+      ''',
+    );
+    newFile(
+      join(projectFolderPath, 'reexport1.dart'),
+      content: '''
+      export 'source_file.dart';
+      ''',
+    );
+    newFile(
+      join(projectFolderPath, 'reexport2.dart'),
+      content: '''
+      export 'source_file.dart';
+      ''',
+    );
+
+    final content = '''
+import '../reexport1.dart';
+
+main() {
+  MyExported^
+}
+    ''';
+
+    final initialAnalysis = waitForAnalysisComplete();
+    await initialize(
+        workspaceCapabilities:
+            withApplyEditSupport(emptyWorkspaceClientCapabilities));
+    await openFile(mainFileUri, withoutMarkers(content));
+    await initialAnalysis;
+    final res = await getCompletion(mainFileUri, positionFromMarker(content));
+
+    final completions = res.where((c) => c.label == 'MyExportedClass').toList();
+    expect(completions, hasLength(1));
+    final resolved = await resolveCompletion(completions.first);
+    // It should not include auto-import text since it's already imported.
+    expect(resolved.detail, isNull);
+  }
+
+  test_suggestionSets_includesReexportedSymbolsForEachFile() async {
+    newFile(
+      join(projectFolderPath, 'source_file.dart'),
+      content: '''
+      class MyExportedClass {}
+      ''',
+    );
+    newFile(
+      join(projectFolderPath, 'reexport1.dart'),
+      content: '''
+      export 'source_file.dart';
+      ''',
+    );
+    newFile(
+      join(projectFolderPath, 'reexport2.dart'),
+      content: '''
+      export 'source_file.dart';
+      ''',
+    );
+
+    final content = '''
+main() {
+  MyExported^
+}
+    ''';
+
+    final initialAnalysis = waitForAnalysisComplete();
+    await initialize(
+        workspaceCapabilities:
+            withApplyEditSupport(emptyWorkspaceClientCapabilities));
+    await openFile(mainFileUri, withoutMarkers(content));
+    await initialAnalysis;
+    final res = await getCompletion(mainFileUri, positionFromMarker(content));
+
+    final completions = res.where((c) => c.label == 'MyExportedClass').toList();
+    expect(completions, hasLength(3));
+
+    // Resolve the completions so we can get the auto-import text.
+    final resolvedCompletions =
+        await Future.wait(completions.map(resolveCompletion));
+
+    expectAutoImportCompletion(resolvedCompletions, '../source_file.dart');
+    expectAutoImportCompletion(resolvedCompletions, '../reexport1.dart');
+    expectAutoImportCompletion(resolvedCompletions, '../reexport2.dart');
+  }
+
   test_suggestionSets_insertsIntoPartFiles() async {
     // File we'll be adding an import for.
     newFile(
diff --git a/pkg/analysis_server/test/lsp/diagnostic_test.dart b/pkg/analysis_server/test/lsp/diagnostic_test.dart
index 06ba649..88a6e62 100644
--- a/pkg/analysis_server/test/lsp/diagnostic_test.dart
+++ b/pkg/analysis_server/test/lsp/diagnostic_test.dart
@@ -81,4 +81,19 @@
     // transmitted.
     expect(diagnostics, isNull);
   }
+
+  test_todos() async {
+    // TODOs only show up if there's also some code in the file.
+    const initialContents = '''
+    // TODO: This
+    String a = "";
+    ''';
+    newFile(mainFilePath, content: initialContents);
+
+    final firstDiagnosticsUpdate = waitForDiagnostics(mainFileUri);
+    await initialize();
+    final initialDiagnostics = await firstDiagnosticsUpdate;
+    // TODOs should not be sent by LSP.
+    expect(initialDiagnostics, hasLength(0));
+  }
 }
diff --git a/pkg/analysis_server/test/lsp/hover_test.dart b/pkg/analysis_server/test/lsp/hover_test.dart
index 957b16f..9cddb82 100644
--- a/pkg/analysis_server/test/lsp/hover_test.dart
+++ b/pkg/analysis_server/test/lsp/hover_test.dart
@@ -113,6 +113,21 @@
     expect(markup.value, contains('This is a string.'));
   }
 
+  test_range_multiLineConstructorCall() async {
+    final content = '''
+    final a = new [[Str^ing.fromCharCodes]]([
+      1,
+      2,
+    ]);
+    ''';
+
+    await initialize();
+    await openFile(mainFileUri, withoutMarkers(content));
+    final hover = await getHover(mainFileUri, positionFromMarker(content));
+    expect(hover, isNotNull);
+    expect(hover.range, equals(rangeFromMarkers(content)));
+  }
+
   test_noElement() async {
     final content = '''
     String abc;
diff --git a/pkg/analysis_server/test/lsp/server_abstract.dart b/pkg/analysis_server/test/lsp/server_abstract.dart
index a531618..d252fd2 100644
--- a/pkg/analysis_server/test/lsp/server_abstract.dart
+++ b/pkg/analysis_server/test/lsp/server_abstract.dart
@@ -147,34 +147,51 @@
     // Complex text manipulations are described with an array of TextEdit's,
     // representing a single change to the document.
     //
-    //  All text edits ranges refer to positions in the original document. Text
+    // All text edits ranges refer to positions in the original document. Text
     // edits ranges must never overlap, that means no part of the original
-    // document must be manipulated by more than one edit. However, it is possible
-    // that multiple edits have the same start position: multiple inserts, or any
-    // number of inserts followed by a single remove or replace edit. If multiple
-    // inserts have the same position, the order in the array defines the order in
-    // which the inserted strings appear in the resulting text.
+    // document must be manipulated by more than one edit. It is possible
+    // that multiple edits have the same start position (eg. multiple inserts in
+    // reverse order), however since that involves complicated tracking and we
+    // only apply edits here sequentially, we don't supported them. We do sort
+    // edits to ensure we apply the later ones first, so we can assume the locations
+    // in the edit are still valid against the new string as each edit is applied.
 
     /// Ensures changes are simple enough to apply easily without any complicated
     /// logic.
     void validateChangesCanBeApplied() {
-      bool intersectsWithOrComesAfter(Position pos, Position other) =>
-          pos.line > other.line ||
-          (pos.line == other.line || pos.character >= other.character);
+      /// Check if a position is before (but not equal) to another position.
+      bool isBefore(Position p, Position other) =>
+          p.line < other.line ||
+          (p.line == other.line && p.character < other.character);
 
-      Position earliestPositionChanged;
-      for (final change in changes) {
-        if (earliestPositionChanged != null &&
-            intersectsWithOrComesAfter(
-                change.range.end, earliestPositionChanged)) {
-          throw 'Test helper applyTextEdits does not support applying multiple edits '
-              'where the edits are not in reverse order.';
+      /// Check if a position is after (but not equal) to another position.
+      bool isAfter(Position p, Position other) =>
+          p.line > other.line ||
+          (p.line == other.line && p.character > other.character);
+      // Check if two ranges intersect or touch.
+      bool rangesIntersect(Range r1, Range r2) {
+        bool endsBefore = isBefore(r1.end, r2.start);
+        bool startsAfter = isAfter(r1.start, r2.end);
+        return !(endsBefore || startsAfter);
+      }
+
+      for (final change1 in changes) {
+        for (final change2 in changes) {
+          if (change1 != change2 &&
+              rangesIntersect(change1.range, change2.range)) {
+            throw 'Test helper applyTextEdits does not support applying multiple edits '
+                'where the edits are not in reverse order.';
+          }
         }
-        earliestPositionChanged = change.range.start;
       }
     }
 
     validateChangesCanBeApplied();
+    changes.sort(
+      (c1, c2) =>
+          positionCompare(c1.range.start, c2.range.start) *
+          -1, // Multiply by -1 to get descending sort.
+    );
     for (final change in changes) {
       newContent = applyTextEdit(newContent, change);
     }
@@ -623,6 +640,16 @@
     await pumpEventQueue();
   }
 
+  int positionCompare(Position p1, Position p2) {
+    if (p1.line < p2.line) return -1;
+    if (p1.line > p2.line) return 1;
+
+    if (p1.character < p2.character) return -1;
+    if (p1.character > p2.character) return -1;
+
+    return 0;
+  }
+
   Position positionFromMarker(String contents) =>
       positionFromOffset(withoutRangeMarkers(contents).indexOf('^'), contents);
 
@@ -861,7 +888,7 @@
         new DartSdkManager(convertPath('/sdk'), false),
         InstrumentationService.NULL_SERVICE);
 
-    projectFolderPath = convertPath('/project');
+    projectFolderPath = convertPath('/home/test');
     projectFolderUri = Uri.file(projectFolderPath);
     newFolder(projectFolderPath);
     newFolder(join(projectFolderPath, 'lib'));
diff --git a/pkg/analysis_server/test/lsp/test_all.dart b/pkg/analysis_server/test/lsp/test_all.dart
index f1cc08c..6793f81 100644
--- a/pkg/analysis_server/test/lsp/test_all.dart
+++ b/pkg/analysis_server/test/lsp/test_all.dart
@@ -8,9 +8,10 @@
 import 'analyzer_status_test.dart' as analyzer_status;
 import 'cancel_request_test.dart' as cancel_request;
 import 'change_workspace_folders_test.dart' as change_workspace_folders;
-import 'code_actions_assists_test.dart' as code_actions_assists;
 import 'closing_labels_test.dart' as closing_labels;
+import 'code_actions_assists_test.dart' as code_actions_assists;
 import 'code_actions_fixes_test.dart' as code_actions_fixes;
+import 'code_actions_refactor_test.dart' as code_actions_refactor;
 import 'code_actions_source_test.dart' as code_actions_source;
 import 'completion_test.dart' as completion;
 import 'definition_test.dart' as definition;
@@ -20,7 +21,6 @@
 import 'file_modification_test.dart' as file_modification;
 import 'folding_test.dart' as folding;
 import 'format_test.dart' as format;
-import 'super_test.dart' as get_super;
 import 'hover_test.dart' as hover;
 import 'implementation_test.dart' as implementation;
 import 'initialization_test.dart' as initialization;
@@ -30,6 +30,7 @@
 import 'rename_test.dart' as rename;
 import 'server_test.dart' as server;
 import 'signature_help_test.dart' as signature_help;
+import 'super_test.dart' as get_super;
 import 'workspace_symbols_test.dart' as workspace_symbols;
 
 main() {
@@ -41,6 +42,7 @@
     code_actions_assists.main();
     code_actions_fixes.main();
     code_actions_source.main();
+    code_actions_refactor.main();
     completion.main();
     definition.main();
     diagnostic.main();
diff --git a/pkg/analysis_server/test/lsp/workspace_symbols_test.dart b/pkg/analysis_server/test/lsp/workspace_symbols_test.dart
index 8bf1e30..8cccc13 100644
--- a/pkg/analysis_server/test/lsp/workspace_symbols_test.dart
+++ b/pkg/analysis_server/test/lsp/workspace_symbols_test.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
+import 'package:analysis_server/lsp_protocol/protocol_special.dart';
 import 'package:analyzer/src/dart/analysis/driver.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -70,6 +71,27 @@
     expect(symbols.any((s) => s.name == 'myMethod'), isFalse);
   }
 
+  test_invalidParams() async {
+    await initialize();
+
+    // Create a request that doesn't supply the query param.
+    final request = new RequestMessage(
+      Either2<num, String>.t1(1),
+      Method.workspace_symbol,
+      <String, dynamic>{},
+      jsonRpcVersion,
+    );
+
+    final response = await sendRequestToServer(request);
+    expect(response.error.code, equals(ErrorCodes.InvalidParams));
+    // Ensure the error is useful to the client.
+    expect(
+      response.error.message,
+      equals('Invalid params for workspace/symbol:\n'
+          'params.query must not be undefined'),
+    );
+  }
+
   test_partialMatch() async {
     const content = '''
     String topLevel = '';
diff --git a/pkg/analysis_server/test/src/services/correction/fix/fix_processor.dart b/pkg/analysis_server/test/src/services/correction/fix/fix_processor.dart
index 4dbfa14..beb7025 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/fix_processor.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/fix_processor.dart
@@ -7,12 +7,9 @@
 import 'package:analysis_server/plugin/edit/fix/fix_core.dart';
 import 'package:analysis_server/src/services/correction/change_workspace.dart';
 import 'package:analysis_server/src/services/correction/fix.dart';
-import 'package:analysis_server/src/services/correction/fix/dart/top_level_declarations.dart';
 import 'package:analysis_server/src/services/correction/fix_internal.dart';
 import 'package:analyzer/error/error.dart';
-import 'package:analyzer/src/dart/analysis/byte_store.dart';
 import 'package:analyzer/src/dart/error/lint_codes.dart';
-import 'package:analyzer/src/services/available_declarations.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart'
     hide AnalysisError;
 import 'package:analyzer_plugin/utilities/change_builder/change_workspace.dart';
@@ -253,19 +250,7 @@
 
   /// Computes fixes for the given [error] in [testUnit].
   Future<List<Fix>> _computeFixes(AnalysisError error) async {
-    var tracker = DeclarationsTracker(MemoryByteStore(), resourceProvider);
-    tracker.addContext(driver.analysisContext);
-
-    var context = new DartFixContextImpl(
-      workspace,
-      testAnalysisResult,
-      error,
-      (name) {
-        var provider = TopLevelDeclarationsProvider(tracker);
-        provider.doTrackerWork();
-        return provider.get(driver.analysisContext, testFile, name);
-      },
-    );
+    var context = new DartFixContextImpl(workspace, testAnalysisResult, error);
     return await new DartFixContributor().computeFixes(context);
   }
 
diff --git a/pkg/analysis_server/test/src/utilities/meta_util.dart b/pkg/analysis_server/test/src/utilities/meta_util.dart
new file mode 100644
index 0000000..2fc9cff
--- /dev/null
+++ b/pkg/analysis_server/test/src/utilities/meta_util.dart
@@ -0,0 +1,128 @@
+// Copyright (c) 2019, 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 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/file_system/memory_file_system.dart';
+
+const String metaPkgLibPath = '/packages/meta/lib';
+
+/**
+ * Add a meta library and types to the given [provider] and return
+ * the `lib` folder.
+ */
+Folder configureMetaPackage(MemoryResourceProvider provider) {
+  File newFile(String path, String content) =>
+      provider.newFile(provider.convertPath(path), content ?? '');
+
+  Folder newFolder(String path) =>
+      provider.newFolder(provider.convertPath(path));
+
+  newFile('$metaPkgLibPath/meta.dart', r'''
+library meta;
+
+const _AlwaysThrows alwaysThrows = const _AlwaysThrows();
+
+@deprecated
+const _Checked checked = const _Checked();
+
+const _Experimental experimental = const _Experimental();
+
+const _Factory factory = const _Factory();
+
+const Immutable immutable = const Immutable();
+
+const _IsTest isTest = const _IsTest();
+
+const _IsTestGroup isTestGroup = const _IsTestGroup();
+
+const _Literal literal = const _Literal();
+
+const _MustCallSuper mustCallSuper = const _MustCallSuper();
+
+const _OptionalTypeArgs optionalTypeArgs = const _OptionalTypeArgs();
+
+const _Protected protected = const _Protected();
+
+const Required required = const Required();
+
+const _Sealed sealed = const _Sealed();
+
+@deprecated
+const _Virtual virtual = const _Virtual();
+
+const _VisibleForOverriding visibleForOverriding =
+    const _VisibleForOverriding();
+
+const _VisibleForTesting visibleForTesting = const _VisibleForTesting();
+
+class Immutable {
+  final String reason;
+  const Immutable([this.reason]);
+}
+
+class Required {
+  final String reason;
+  const Required([this.reason]);
+}
+
+class _AlwaysThrows {
+  const _AlwaysThrows();
+}
+
+class _Checked {
+  const _Checked();
+}
+
+class _Experimental {
+  const _Experimental();
+}
+
+class _Factory {
+  const _Factory();
+}
+
+class _IsTest {
+  const _IsTest();
+}
+
+class _IsTestGroup {
+  const _IsTestGroup();
+}
+
+class _Literal {
+  const _Literal();
+}
+
+class _MustCallSuper {
+  const _MustCallSuper();
+}
+
+class _OptionalTypeArgs {
+  const _OptionalTypeArgs();
+}
+
+class _Protected {
+  const _Protected();
+}
+
+class _Sealed {
+  const _Sealed();
+}
+
+@deprecated
+class _Virtual {
+  const _Virtual();
+}
+
+class _VisibleForOverriding {
+  const _VisibleForOverriding();
+}
+
+class _VisibleForTesting {
+  const _VisibleForTesting();
+}
+''');
+
+  return newFolder(metaPkgLibPath);
+}
diff --git a/pkg/analysis_server/test/tool/lsp_spec/json_test.dart b/pkg/analysis_server/test/tool/lsp_spec/json_test.dart
index b4cb36e..37487ef 100644
--- a/pkg/analysis_server/test/tool/lsp_spec/json_test.dart
+++ b/pkg/analysis_server/test/tool/lsp_spec/json_test.dart
@@ -6,6 +6,7 @@
 
 import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
 import 'package:analysis_server/lsp_protocol/protocol_special.dart';
+import 'package:analysis_server/src/lsp/json_parsing.dart';
 import 'package:test/test.dart';
 
 main() {
@@ -100,16 +101,25 @@
     });
 
     test('canParse returns false for out-of-spec (restricted) enum values', () {
-      expect(MarkupKind.canParse('NotAMarkupKind'), isFalse);
+      expect(
+        MarkupKind.canParse('NotAMarkupKind', nullLspJsonReporter),
+        isFalse,
+      );
     });
 
     test('canParse returns true for in-spec (restricted) enum values', () {
-      expect(MarkupKind.canParse('plaintext'), isTrue);
+      expect(
+        MarkupKind.canParse('plaintext', nullLspJsonReporter),
+        isTrue,
+      );
     });
 
     test('canParse returns true for out-of-spec (unrestricted) enum values',
         () {
-      expect(SymbolKind.canParse(-1), isTrue);
+      expect(
+        SymbolKind.canParse(-1, nullLspJsonReporter),
+        isTrue,
+      );
     });
 
     test('canParse allows nulls in nullable and undefinable fields', () {
@@ -119,22 +129,110 @@
         'processId': null,
         'rootUri': null,
         'capabilities': <String, Object>{}
-      });
+      }, nullLspJsonReporter);
       expect(canParse, isTrue);
     });
 
     test('canParse validates optional fields', () {
-      expect(RenameFileOptions.canParse(<String, Object>{}), isTrue);
-      expect(RenameFileOptions.canParse({'overwrite': true}), isTrue);
-      expect(RenameFileOptions.canParse({'overwrite': 1}), isFalse);
+      expect(
+        RenameFileOptions.canParse(<String, Object>{}, nullLspJsonReporter),
+        isTrue,
+      );
+      expect(
+        RenameFileOptions.canParse({'overwrite': true}, nullLspJsonReporter),
+        isTrue,
+      );
+      expect(
+        RenameFileOptions.canParse({'overwrite': 1}, nullLspJsonReporter),
+        isFalse,
+      );
     });
 
     test('canParse ignores fields not in the spec', () {
       expect(
-          RenameFileOptions.canParse({'overwrite': true, 'invalidField': true}),
-          isTrue);
-      expect(RenameFileOptions.canParse({'overwrite': 1, 'invalidField': true}),
+        RenameFileOptions.canParse(
+            {'overwrite': true, 'invalidField': true}, nullLspJsonReporter),
+        isTrue,
+      );
+      expect(
+        RenameFileOptions.canParse(
+            {'overwrite': 1, 'invalidField': true}, nullLspJsonReporter),
+        isFalse,
+      );
+    });
+
+    test('canParse records undefined fields', () {
+      final reporter = LspJsonReporter('params');
+      expect(CreateFile.canParse(<String, dynamic>{}, reporter), isFalse);
+      expect(reporter.errors, hasLength(1));
+      expect(
+          reporter.errors.first, equals("params.kind must not be undefined"));
+    });
+
+    test('canParse records null fields', () {
+      final reporter = LspJsonReporter('params');
+      expect(CreateFile.canParse({'kind': null}, reporter), isFalse);
+      expect(reporter.errors, hasLength(1));
+      expect(reporter.errors.first, equals("params.kind must not be null"));
+    });
+
+    test('canParse records fields of the wrong type', () {
+      final reporter = LspJsonReporter('params');
+      expect(RenameFileOptions.canParse({'overwrite': 1}, reporter), isFalse);
+      expect(reporter.errors, hasLength(1));
+      expect(reporter.errors.first,
+          equals("params.overwrite must be of type bool"));
+    });
+
+    test('canParse records nested undefined fields', () {
+      final reporter = LspJsonReporter('params');
+      expect(
+          CompletionParams.canParse(
+              {'textDocument': <String, dynamic>{}}, reporter),
           isFalse);
+      expect(reporter.errors, hasLength(greaterThanOrEqualTo(1)));
+      expect(reporter.errors.first,
+          equals("params.textDocument.uri must not be undefined"));
+    });
+
+    test('canParse records nested null fields', () {
+      final reporter = LspJsonReporter('params');
+      expect(
+          CompletionParams.canParse({
+            'textDocument': {'uri': null}
+          }, reporter),
+          isFalse);
+      expect(reporter.errors, hasLength(greaterThanOrEqualTo(1)));
+      expect(reporter.errors.first,
+          equals("params.textDocument.uri must not be null"));
+    });
+
+    test('canParse records nested fields of the wrong type', () {
+      final reporter = LspJsonReporter('params');
+      expect(
+          CompletionParams.canParse({
+            'textDocument': {'uri': 1}
+          }, reporter),
+          isFalse);
+      expect(reporter.errors, hasLength(greaterThanOrEqualTo(1)));
+      expect(reporter.errors.first,
+          equals("params.textDocument.uri must be of type String"));
+    });
+
+    test(
+        'canParse records errors when the type is not in the set of allowed types',
+        () {
+      final reporter = LspJsonReporter('params');
+      expect(
+          WorkspaceEdit.canParse({
+            'documentChanges': {'uri': 1}
+          }, reporter),
+          isFalse);
+      expect(reporter.errors, hasLength(greaterThanOrEqualTo(1)));
+      expect(
+          reporter.errors.first,
+          equals(
+              "params.documentChanges must be of type Either2<List<TextDocumentEdit>, List<Either4<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>>>"));
     });
 
     test('ResponseMessage can include a null result', () {
diff --git a/pkg/analysis_server/tool/lsp_spec/README.md b/pkg/analysis_server/tool/lsp_spec/README.md
index d0cd4be..e0600c7 100644
--- a/pkg/analysis_server/tool/lsp_spec/README.md
+++ b/pkg/analysis_server/tool/lsp_spec/README.md
@@ -59,7 +59,7 @@
 | textDocument/didClose | ✅ | ✅ | ✅ | ✅ |
 | textDocument/publishDiagnostics | ✅ | ✅ | ✅ | ✅ |
 | textDocument/completion | ✅ | ✅ | ✅ | ✅ |
-| completionItem/resolve | | | | | not required |
+| completionItem/resolve | ✅ | ✅ | ✅ | ✅ |
 | textDocument/hover | ✅ | ✅ | ✅ | ✅ |
 | textDocument/signatureHelp | ✅ | ✅ | ✅ | ✅ | trigger character handling outstanding
 | textDocument/declaration | | | | |
@@ -71,7 +71,7 @@
 | textDocument/documentSymbol | ✅ | ✅ | ✅ | ✅ |
 | textDocument/codeAction (sortMembers) | ✅ | ✅ | ✅ | ✅ |
 | textDocument/codeAction (organiseImports) | ✅ | ✅ | ✅ | ✅ |
-| textDocument/codeAction (refactors) | | | | | <!-- Only if the client advertises `codeActionLiteralSupport` with Refactors -->
+| textDocument/codeAction (refactors) | ✅ | ✅ | ✅ | ✅ |
 | textDocument/codeAction (assists) | ✅ | ✅ | ✅ | ✅ | Only if the client advertises `codeActionLiteralSupport` with `Refactor`
 | textDocument/codeAction (fixes) | ✅ | ✅ | ✅ | ✅ | Only if the client advertises `codeActionLiteralSupport` with `QuickFix`
 | textDocument/codeLens | | | | |
@@ -84,7 +84,7 @@
 | textDocument/rangeFormatting | | | | | requires support from dart_style?
 | textDocument/onTypeFormatting | ✅ | ✅ | ✅ | ✅ |
 | textDocument/rename | ✅ | ✅ | ✅ | ✅ |
-| textDocument/prepareRename | | | | |
+| textDocument/prepareRename | ✅ | ✅ | ✅ | ✅ |
 | textDocument/foldingRange | ✅ | ✅ | ✅ | ✅ |
 
 ## Custom Methods
diff --git a/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart b/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart
index cbaec69..c83b0af 100644
--- a/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart
+++ b/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart
@@ -161,29 +161,70 @@
 
 void _writeCanParseMethod(IndentableStringBuffer buffer, Interface interface) {
   buffer
-    ..writeIndentedln('static bool canParse(Object obj) {')
+    ..writeIndentedln(
+        'static bool canParse(Object obj, LspJsonReporter reporter) {')
     ..indent()
-    ..writeIndented('return obj is Map<String, dynamic>');
-  // In order to consider this valid for parsing, all fields that may not be
+    ..writeIndentedln('if (obj is Map<String, dynamic>) {')
+    ..indent();
+  // In order to consider this valid for parsing, all fields that must not be
   // undefined must be present and also type check for the correct type.
   // Any fields that are optional but present, must still type check.
   final fields = _getAllFields(interface);
   for (var field in fields) {
+    buffer
+      ..writeIndentedln("reporter.push('${field.name}');")
+      ..writeIndentedln('try {')
+      ..indent();
     if (!field.allowsUndefined) {
-      buffer.write(" && obj.containsKey('${field.name}') && ");
-    } else {
-      buffer.write(" && ");
+      buffer
+        ..writeIndentedln("if (!obj.containsKey('${field.name}')) {")
+        ..indent()
+        ..writeIndentedln('reporter.reportError("must not be undefined");')
+        ..writeIndentedln('return false;')
+        ..outdent()
+        ..writeIndentedln('}');
     }
+    if (!field.allowsNull && !field.allowsUndefined) {
+      buffer
+        ..writeIndentedln("if (obj['${field.name}'] == null) {")
+        ..indent()
+        ..writeIndentedln('reporter.reportError("must not be null");')
+        ..writeIndentedln('return false;')
+        ..outdent()
+        ..writeIndentedln('}');
+    }
+    buffer.writeIndented('if (');
     if (field.allowsNull || field.allowsUndefined) {
-      buffer.write("(obj['${field.name}'] == null || ");
+      buffer.write("obj['${field.name}'] != null && ");
     }
-    _writeTypeCheckCondition(buffer, "obj['${field.name}']", field.type);
-    if (field.allowsNull || field.allowsUndefined) {
-      buffer.write(")");
-    }
+    buffer.write('!(');
+    _writeTypeCheckCondition(
+        buffer, "obj['${field.name}']", field.type, 'reporter');
+    buffer
+      ..write(')) {')
+      ..indent()
+      ..writeIndentedln(
+          'reporter.reportError("must be of type ${field.type.dartTypeWithTypeArgs}");')
+      ..writeIndentedln('return false;')
+      ..outdent()
+      ..writeIndentedln('}')
+      ..outdent()
+      ..writeIndentedln('} finally {')
+      ..indent()
+      ..writeIndentedln('reporter.pop();')
+      ..outdent()
+      ..writeIndentedln('}');
   }
   buffer
-    ..writeln(';')
+    ..writeIndentedln('return true;')
+    ..outdent()
+    ..writeIndentedln('} else {')
+    ..indent()
+    ..writeIndentedln(
+        'reporter.reportError("must be of type ${interface.nameWithTypeArgs}");')
+    ..writeIndentedln('return false;')
+    ..outdent()
+    ..writeIndentedln('}')
     ..outdent()
     ..writeIndentedln('}');
 }
@@ -265,11 +306,12 @@
     ..writeln()
     ..writeIndentedln('final ${typeOfValues.dartTypeWithTypeArgs} _value;')
     ..writeln()
-    ..writeIndentedln('static bool canParse(Object obj) {')
+    ..writeIndentedln(
+        'static bool canParse(Object obj, LspJsonReporter reporter) {')
     ..indent();
   if (allowsAnyValue) {
     buffer.writeIndentedln('return ');
-    _writeTypeCheckCondition(buffer, 'obj', typeOfValues);
+    _writeTypeCheckCondition(buffer, 'obj', typeOfValues, 'reporter');
     buffer.writeln(';');
   } else {
     buffer
@@ -399,7 +441,7 @@
 
     // Dynamic matches all type checks, so only emit it if required.
     if (!isDynamic) {
-      _writeTypeCheckCondition(buffer, valueCode, type);
+      _writeTypeCheckCondition(buffer, valueCode, type, 'nullLspJsonReporter');
       buffer.write(' ? ');
     }
 
@@ -442,7 +484,8 @@
   for (final subclassName in _subtypes[interface.name] ?? const <String>[]) {
     final subclass = _interfaces[subclassName];
     buffer
-      ..writeIndentedln('if (${subclass.name}.canParse(json)) {')
+      ..writeIndentedln(
+          'if (${subclass.name}.canParse(json, nullLspJsonReporter)) {')
       ..indent()
       ..writeln('return ${subclass.nameWithTypeArgs}.fromJson(json);')
       ..outdent()
@@ -623,8 +666,8 @@
   }
 }
 
-void _writeTypeCheckCondition(
-    IndentableStringBuffer buffer, String valueCode, TypeBase type) {
+void _writeTypeCheckCondition(IndentableStringBuffer buffer, String valueCode,
+    TypeBase type, String reporter) {
   type = resolveTypeAlias(type);
 
   final dartType = type.dartType;
@@ -634,14 +677,14 @@
   } else if (_isSimpleType(type)) {
     buffer.write('$valueCode is $fullDartType');
   } else if (_isSpecType(type)) {
-    buffer.write('$dartType.canParse($valueCode)');
+    buffer.write('$dartType.canParse($valueCode, $reporter)');
   } else if (type is ArrayType) {
     buffer.write('($valueCode is List');
     if (fullDartType != 'dynamic') {
       // TODO(dantup): If we're happy to assume we never have two lists in a union
       // we could skip this bit.
       buffer.write(' && ($valueCode.every((item) => ');
-      _writeTypeCheckCondition(buffer, 'item', type.elementType);
+      _writeTypeCheckCondition(buffer, 'item', type.elementType, reporter);
       buffer.write('))');
     }
     buffer.write(')');
@@ -649,9 +692,9 @@
     buffer.write('($valueCode is Map');
     if (fullDartType != 'dynamic') {
       buffer..write(' && (')..write('$valueCode.keys.every((item) => ');
-      _writeTypeCheckCondition(buffer, 'item', type.indexType);
+      _writeTypeCheckCondition(buffer, 'item', type.indexType, reporter);
       buffer..write('&& $valueCode.values.every((item) => ');
-      _writeTypeCheckCondition(buffer, 'item', type.valueType);
+      _writeTypeCheckCondition(buffer, 'item', type.valueType, reporter);
       buffer.write(')))');
     }
     buffer.write(')');
@@ -662,7 +705,7 @@
       if (i != 0) {
         buffer.write(' || ');
       }
-      _writeTypeCheckCondition(buffer, valueCode, type.types[i]);
+      _writeTypeCheckCondition(buffer, valueCode, type.types[i], reporter);
     }
     buffer.write(')');
   } else {
diff --git a/pkg/analysis_server/tool/lsp_spec/generate_all.dart b/pkg/analysis_server/tool/lsp_spec/generate_all.dart
index 08a111d..870b917 100644
--- a/pkg/analysis_server/tool/lsp_spec/generate_all.dart
+++ b/pkg/analysis_server/tool/lsp_spec/generate_all.dart
@@ -165,6 +165,7 @@
 import 'dart:convert' show JsonEncoder;
 import 'package:analysis_server/lsp_protocol/protocol${importCustom ? '_custom' : ''}_generated.dart';
 import 'package:analysis_server/lsp_protocol/protocol_special.dart';
+import 'package:analysis_server/src/lsp/json_parsing.dart';
 import 'package:analysis_server/src/protocol/protocol_internal.dart'
     show listEqual, mapEqual;
 import 'package:analyzer/src/generated/utilities_general.dart';
diff --git a/pkg/analyzer/CHANGELOG.md b/pkg/analyzer/CHANGELOG.md
index 37ac0ad..f0d1b34a 100644
--- a/pkg/analyzer/CHANGELOG.md
+++ b/pkg/analyzer/CHANGELOG.md
@@ -13,6 +13,14 @@
   impacts to value returned by `FunctionType.displayName` and
   `FunctionType.toString` and `ExecutableElement.toString`. Client code might be
   broken if it depends on the content of the returned value.
+* Introduced the function `parseString` to the public API.  This can be used in
+  place of the deprecated functions `parseCompilationUnit` and
+  `parseDirectives`.  Note that there is no option to parse only directives,
+  since this functionality is broken anyway (`parseDirectives`, despite its
+  name, parses the entire compilation unit).
+* Changed the return type of `ClassTypeAlias.declaredElement` to `ClassElement`.
+  There is no functional change; it has always returned an instance of
+  `ClassElement`.
 
 ## 0.36.3
 * Deprecated `AstFactory.compilationUnit`.  In a future analyzer release, this
diff --git a/pkg/analyzer/lib/dart/analysis/results.dart b/pkg/analyzer/lib/dart/analysis/results.dart
index 7f82c94..db9f9f8 100644
--- a/pkg/analyzer/lib/dart/analysis/results.dart
+++ b/pkg/analyzer/lib/dart/analysis/results.dart
@@ -105,6 +105,27 @@
   CompilationUnit get unit;
 }
 
+/// The result of parsing of a single file. The errors returned include only
+/// those discovered during scanning and parsing.
+///
+/// Similar to [ParsedUnitResult], but does not allow access to an analysis
+/// session.
+///
+/// Clients may not extend, implement or mix-in this class.
+abstract class ParseStringResult {
+  /// The content of the file that was scanned and parsed.
+  String get content;
+
+  /// The analysis errors that were computed during analysis.
+  List<AnalysisError> get errors;
+
+  /// Information about lines in the content.
+  LineInfo get lineInfo;
+
+  /// The parsed, unresolved compilation unit for the [content].
+  CompilationUnit get unit;
+}
+
 /// The result of building resolved AST(s) for the whole library.
 ///
 /// Clients may not extend, implement or mix-in this class.
diff --git a/pkg/analyzer/lib/dart/analysis/session.dart b/pkg/analyzer/lib/dart/analysis/session.dart
index 542661b..6f31a7b 100644
--- a/pkg/analyzer/lib/dart/analysis/session.dart
+++ b/pkg/analyzer/lib/dart/analysis/session.dart
@@ -11,6 +11,7 @@
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/exception/exception.dart';
 import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/src/dart/analysis/top_level_declaration.dart';
 import 'package:analyzer/src/generated/resolver.dart';
 import 'package:analyzer/src/generated/source.dart';
 
@@ -121,6 +122,11 @@
   /// complete with [SourceKind.UNKNOWN].
   Future<SourceKind> getSourceKind(String path);
 
+  /// Return a future that will complete with a list of the top-level
+  /// declarations with the given [name] in all known libraries.
+  Future<List<TopLevelDeclarationInSource>> getTopLevelDeclarations(
+      String name);
+
   /// Return a future that will complete with information about the results of
   /// building the element model for the file with the given absolute,
   /// normalized[path].
diff --git a/pkg/analyzer/lib/dart/analysis/utilities.dart b/pkg/analyzer/lib/dart/analysis/utilities.dart
index 75ef87b..7b96945 100644
--- a/pkg/analyzer/lib/dart/analysis/utilities.dart
+++ b/pkg/analyzer/lib/dart/analysis/utilities.dart
@@ -6,9 +6,17 @@
 
 import 'package:analyzer/dart/analysis/analysis_context.dart';
 import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
+import 'package:analyzer/dart/analysis/features.dart';
 import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/error/listener.dart';
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/file_system/physical_file_system.dart';
+import 'package:analyzer/source/line_info.dart';
+import 'package:analyzer/src/dart/analysis/results.dart';
+import 'package:analyzer/src/dart/scanner/reader.dart';
+import 'package:analyzer/src/dart/scanner/scanner.dart';
+import 'package:analyzer/src/generated/parser.dart';
+import 'package:analyzer/src/string_source.dart';
 import 'package:meta/meta.dart';
 
 /// Return the result of parsing the file at the given [path].
@@ -25,6 +33,37 @@
   return context.currentSession.getParsedUnit(path);
 }
 
+/// Returns the result of parsing the given [content] as a compilation unit.
+///
+/// If a [featureSet] is provided, it will be the default set of features that
+/// will be assumed by the parser.
+///
+/// If [throwIfDiagnostics] is `true` (the default), then if any diagnostics are
+/// produced because of syntactic errors in the [content] an `ArgumentError`
+/// will be thrown. If the parameter is `false`, then the caller can check the
+/// result to see whether there are any `errors`.
+ParseStringResult parseString(
+    {@required String content,
+    FeatureSet featureSet,
+    bool throwIfDiagnostics: true}) {
+  featureSet ??= FeatureSet.fromEnableFlags([]);
+  var source = StringSource(content, null);
+  var reader = CharSequenceReader(content);
+  var errorCollector = RecordingErrorListener();
+  var scanner = Scanner(source, reader, errorCollector)
+    ..configureFeatures(featureSet);
+  var token = scanner.tokenize();
+  var parser = Parser(source, errorCollector, featureSet: scanner.featureSet);
+  var unit = parser.parseCompilationUnit(token);
+  unit.lineInfo = LineInfo(scanner.lineStarts);
+  ParseStringResult result =
+      ParseStringResultImpl(content, unit, errorCollector.errors);
+  if (throwIfDiagnostics && result.errors.isNotEmpty) {
+    throw new ArgumentError('Content produced diagnostics when parsed');
+  }
+  return result;
+}
+
 /// Return the result of resolving the file at the given [path].
 ///
 /// If a [resourceProvider] is given, it will be used to access the file system.
diff --git a/pkg/analyzer/lib/dart/ast/ast.dart b/pkg/analyzer/lib/dart/ast/ast.dart
index 14df41a..a7b03cd 100644
--- a/pkg/analyzer/lib/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/dart/ast/ast.dart
@@ -1051,6 +1051,9 @@
   /// Set the token for the 'abstract' keyword to the given [token].
   void set abstractKeyword(Token token);
 
+  @override
+  ClassElement get declaredElement;
+
   /// Return the token for the '=' separating the name from the definition.
   Token get equals;
 
@@ -2113,11 +2116,11 @@
 /// The declaration of an extension of a type.
 ///
 ///    extension ::=
-///        'extension' [SimpleIdentifier] [TypeParameterList]?
+///        'extension' [SimpleIdentifier]? [TypeParameterList]?
 ///        'on' [TypeAnnotation] '{' [ClassMember]* '}'
 ///
 /// Clients may not extend, implement or mix-in this class.
-abstract class ExtensionDeclaration implements NamedCompilationUnitMember {
+abstract class ExtensionDeclaration implements CompilationUnitMember {
   /// Return the type that is being extended.
   TypeAnnotation get extendedType;
 
@@ -2130,6 +2133,10 @@
   /// Return the members being added to the extended class.
   NodeList<ClassMember> get members;
 
+  /// Return the name of the extension, or `null` if the extension does not have
+  /// a name.
+  SimpleIdentifier get name;
+
   /// Return the token representing the 'on' keyword.
   Token get onKeyword;
 
diff --git a/pkg/analyzer/lib/dart/ast/visitor.dart b/pkg/analyzer/lib/dart/ast/visitor.dart
index 68f4621..1d89813 100644
--- a/pkg/analyzer/lib/dart/ast/visitor.dart
+++ b/pkg/analyzer/lib/dart/ast/visitor.dart
@@ -278,7 +278,7 @@
 
   @override
   R visitExtensionDeclaration(ExtensionDeclaration node) =>
-      visitNamedCompilationUnitMember(node);
+      visitCompilationUnitMember(node);
 
   @override
   R visitFieldDeclaration(FieldDeclaration node) => visitClassMember(node);
diff --git a/pkg/analyzer/lib/dart/element/type_system.dart b/pkg/analyzer/lib/dart/element/type_system.dart
index df6cbe8..7906724 100644
--- a/pkg/analyzer/lib/dart/element/type_system.dart
+++ b/pkg/analyzer/lib/dart/element/type_system.dart
@@ -94,7 +94,7 @@
   @experimental
   bool isPotentiallyNonNullable(DartType type);
 
-  /// Return `true` if the [type] is not a potentially nullable type.
+  /// Return `true` if the [type] is a potentially nullable type.
   ///
   /// We say that a type `T` is potentially nullable if `T` is not non-nullable.
   /// Note that this is different from saying that `T` is nullable. For example,
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index ed09816..7c1fa00 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -236,6 +236,8 @@
   CompileTimeErrorCode.NON_GENERATIVE_CONSTRUCTOR,
   CompileTimeErrorCode.NON_SYNC_FACTORY,
   CompileTimeErrorCode.NOT_ENOUGH_REQUIRED_ARGUMENTS,
+  CompileTimeErrorCode.NOT_INITIALIZED_NON_NULLABLE_TOP_LEVEL_VARIABLE,
+  CompileTimeErrorCode.NOT_INITIALIZED_POTENTIALLY_NON_NULLABLE_LOCAL_VARIABLE,
   CompileTimeErrorCode.NOT_ITERABLE_SPREAD,
   CompileTimeErrorCode.NOT_MAP_SPREAD,
   CompileTimeErrorCode.NOT_NULL_AWARE_NULL_SPREAD,
diff --git a/pkg/analyzer/lib/src/dart/analysis/ddc.dart b/pkg/analyzer/lib/src/dart/analysis/ddc.dart
new file mode 100644
index 0000000..0426f4a
--- /dev/null
+++ b/pkg/analyzer/lib/src/dart/analysis/ddc.dart
@@ -0,0 +1,278 @@
+// Copyright (c) 2019, 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:collection';
+
+import 'package:analyzer/dart/analysis/declared_variables.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/analysis/driver.dart';
+import 'package:analyzer/src/dart/analysis/file_state.dart';
+import 'package:analyzer/src/dart/analysis/restricted_analysis_context.dart';
+import 'package:analyzer/src/dart/analysis/session.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/generated/utilities_dart.dart';
+import 'package:analyzer/src/summary/format.dart';
+import 'package:analyzer/src/summary/idl.dart';
+import 'package:analyzer/src/summary/link.dart';
+import 'package:analyzer/src/summary/package_bundle_reader.dart';
+import 'package:analyzer/src/summary/resynthesize.dart';
+import 'package:analyzer/src/summary/summarize_ast.dart';
+import 'package:analyzer/src/summary/summarize_elements.dart';
+import 'package:analyzer/src/summary2/link.dart' as summary2;
+import 'package:analyzer/src/summary2/linked_bundle_context.dart' as summary2;
+import 'package:analyzer/src/summary2/linked_element_factory.dart' as summary2;
+import 'package:analyzer/src/summary2/reference.dart' as summary2;
+import 'package:meta/meta.dart';
+
+class DevCompilerResynthesizerBuilder {
+  final FileSystemState _fsState;
+  final SourceFactory _sourceFactory;
+  final DeclaredVariables _declaredVariables;
+  final AnalysisOptionsImpl _analysisOptions;
+  final SummaryDataStore _summaryData;
+  final List<Uri> _explicitSources;
+
+  _SourceCrawler _fileCrawler;
+
+  final PackageBundleAssembler _assembler;
+  List<int> summaryBytes;
+
+  RestrictedAnalysisContext context;
+  SummaryResynthesizer resynthesizer;
+  summary2.LinkedElementFactory elementFactory;
+
+  DevCompilerResynthesizerBuilder({
+    @required FileSystemState fsState,
+    @required SourceFactory sourceFactory,
+    @required DeclaredVariables declaredVariables,
+    @required AnalysisOptionsImpl analysisOptions,
+    @required SummaryDataStore summaryData,
+    @required List<Uri> explicitSources,
+  })  : _fsState = fsState,
+        _sourceFactory = sourceFactory,
+        _declaredVariables = declaredVariables,
+        _analysisOptions = analysisOptions,
+        _summaryData = summaryData,
+        _explicitSources = explicitSources,
+        _assembler = PackageBundleAssembler();
+
+  /// URIs of libraries that should be linked.
+  List<String> get libraryUris => _fileCrawler.libraryUris;
+
+  /// Link explicit sources, serialize [PackageBundle] into [summaryBytes].
+  ///
+  /// Create a new [context], [resynthesizer] and [elementFactory].
+  void build() {
+    _fileCrawler = _SourceCrawler(
+      _fsState,
+      _sourceFactory,
+      _summaryData,
+      _explicitSources,
+    );
+    _fileCrawler.crawl();
+
+    _buildPackageBundleBytes();
+    var bundle = PackageBundle.fromBuffer(summaryBytes);
+
+    // Create an analysis context to contain the state for this build unit.
+    var synchronousSession = SynchronousSession(
+      _analysisOptions,
+      _declaredVariables,
+    );
+    context = RestrictedAnalysisContext(synchronousSession, _sourceFactory);
+
+    resynthesizer = StoreBasedSummaryResynthesizer(
+      context,
+      null,
+      context.sourceFactory,
+      /*strongMode*/ true,
+      SummaryDataStore([])
+        ..addStore(_summaryData)
+        ..addBundle(null, bundle),
+    );
+    resynthesizer.finishCoreAsyncLibraries();
+    context.typeProvider = resynthesizer.typeProvider;
+  }
+
+  void _buildPackageBundleBytes() {
+    if (AnalysisDriver.useSummary2) {
+      _computeLinkedLibraries2();
+    } else {
+      _computeLinkedLibraries1();
+    }
+    summaryBytes = _assembler.assemble().toBuffer();
+  }
+
+  void _computeLinkedLibraries1() {
+    _fileCrawler.sourceToUnlinkedUnit.forEach((source, unlinkedUnit) {
+      _assembler.addUnlinkedUnit(source, unlinkedUnit);
+    });
+
+    var linkResult = link(
+        _fileCrawler.libraryUris.toSet(),
+        (uri) => _summaryData.linkedMap[uri],
+        (uri) =>
+            _summaryData.unlinkedMap[uri] ??
+            _fileCrawler.uriToUnlinkedUnit[uri],
+        _declaredVariables,
+        _analysisOptions);
+    linkResult.forEach(_assembler.addLinkedLibrary);
+  }
+
+  /// Link libraries, and fill [_assembler].
+  void _computeLinkedLibraries2() {
+    var inputLibraries = <summary2.LinkInputLibrary>[];
+
+    var sourceToUnit = _fileCrawler.sourceToUnit;
+    for (var librarySource in sourceToUnit.keys) {
+      var unit = sourceToUnit[librarySource];
+
+      if (_explicitSources.contains(librarySource.uri)) {
+        var isPart = unit.directives.any((d) => d is PartOfDirective);
+        if (isPart) {
+          continue;
+        }
+      }
+
+      var inputUnits = <summary2.LinkInputUnit>[];
+      inputUnits.add(
+        summary2.LinkInputUnit(null, librarySource, false, unit),
+      );
+
+      for (var directive in unit.directives) {
+        if (directive is PartDirective) {
+          var partUri = directive.uri.stringValue;
+          var partSource = _sourceFactory.resolveUri(librarySource, partUri);
+          if (partSource != null) {
+            var partUnit = sourceToUnit[partSource];
+            inputUnits.add(
+              summary2.LinkInputUnit(partUri, partSource, false, partUnit),
+            );
+          }
+        }
+      }
+
+      inputLibraries.add(
+        summary2.LinkInputLibrary(librarySource, inputUnits),
+      );
+    }
+
+    var analysisContext = RestrictedAnalysisContext(
+      SynchronousSession(_analysisOptions, _declaredVariables),
+      _sourceFactory,
+    );
+
+    var elementFactory = summary2.LinkedElementFactory(
+      analysisContext,
+      null,
+      summary2.Reference.root(),
+    );
+
+    for (var bundle in _summaryData.bundles) {
+      elementFactory.addBundle(
+        summary2.LinkedBundleContext(elementFactory, bundle.bundle2),
+      );
+    }
+
+    var linkResult = summary2.link(elementFactory, inputLibraries);
+    _assembler.setBundle2(linkResult.bundle);
+  }
+}
+
+class _SourceCrawler {
+  final FileSystemState _fsState;
+  final SourceFactory _sourceFactory;
+  final SummaryDataStore _summaryData;
+  final List<Uri> _explicitSources;
+
+  /// The pending list of sources to visit.
+  var _pendingSource = Queue<Uri>();
+
+  /// The sources that have been added to [_pendingSource], used to ensure
+  /// we only visit a given source once.
+  var _knownSources = Set<Uri>();
+
+  final Map<Source, UnlinkedUnitBuilder> sourceToUnlinkedUnit = {};
+  final Map<String, UnlinkedUnitBuilder> uriToUnlinkedUnit = {};
+  final Map<Source, CompilationUnit> sourceToUnit = {};
+  final List<String> libraryUris = [];
+
+  _SourceCrawler(
+    this._fsState,
+    this._sourceFactory,
+    this._summaryData,
+    this._explicitSources,
+  );
+
+  /// Starting with [_explicitSources], visit all transitive imports, exports,
+  /// parts, and create an unlinked unit for each (unless it is provided by an
+  /// input summary from [_summaryData]).
+  void crawl() {
+    _pendingSource.addAll(_explicitSources);
+    _knownSources.addAll(_explicitSources);
+
+    // Collect the unlinked units for all transitive sources.
+    //
+    // TODO(jmesserly): consider using parallelism via asynchronous IO here,
+    // once we fix debugger extension (web/web_command.dart) to allow async.
+    //
+    // It would let computation tasks (parsing/serializing unlinked units)
+    // proceed in parallel with reading the sources from disk.
+    while (_pendingSource.isNotEmpty) {
+      _visit(_pendingSource.removeFirst());
+    }
+  }
+
+  /// Visit the file with the given [uri], and fill its data.
+  void _visit(Uri uri) {
+    var uriStr = uri.toString();
+
+    // Maybe an input package contains the source.
+    if (_summaryData.unlinkedMap[uriStr] != null) {
+      return;
+    }
+
+    var source = _sourceFactory.forUri2(uri);
+    if (source == null) {
+      return;
+    }
+
+    var file = _fsState.getFileForPath(source.fullName);
+    var unit = file.parse();
+    sourceToUnit[source] = unit;
+
+    var unlinkedUnit = serializeAstUnlinked(unit);
+    uriToUnlinkedUnit[uriStr] = unlinkedUnit;
+    sourceToUnlinkedUnit[source] = unlinkedUnit;
+
+    void enqueueSource(String relativeUri) {
+      var sourceUri = resolveRelativeUri(uri, Uri.parse(relativeUri));
+      if (_knownSources.add(sourceUri)) {
+        _pendingSource.add(sourceUri);
+      }
+    }
+
+    // Add reachable imports/exports/parts, if any.
+    var isPart = false;
+    for (var directive in unit.directives) {
+      if (directive is UriBasedDirective) {
+        enqueueSource(directive.uri.stringValue);
+        // Handle conditional imports.
+        if (directive is NamespaceDirective) {
+          for (var config in directive.configurations) {
+            enqueueSource(config.uri.stringValue);
+          }
+        }
+      } else if (directive is PartOfDirective) {
+        isPart = true;
+      }
+    }
+
+    // Remember library URIs, for linking and compiling.
+    if (!isPart) {
+      libraryUris.add(uriStr);
+    }
+  }
+}
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index 0620474..ead50fe5 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -28,6 +28,7 @@
 import 'package:analyzer/src/dart/analysis/search.dart';
 import 'package:analyzer/src/dart/analysis/session.dart';
 import 'package:analyzer/src/dart/analysis/status.dart';
+import 'package:analyzer/src/dart/analysis/top_level_declaration.dart';
 import 'package:analyzer/src/error/codes.dart';
 import 'package:analyzer/src/generated/engine.dart'
     show
@@ -93,7 +94,7 @@
   /**
    * The version of data format, should be incremented on every format change.
    */
-  static const int DATA_VERSION = 81;
+  static const int DATA_VERSION = 83;
 
   /**
    * The number of exception contexts allowed to write. Once this field is
@@ -216,6 +217,11 @@
   final _referencingNameTasks = <_FilesReferencingNameTask>[];
 
   /**
+   * The list of tasks to compute top-level declarations of a name.
+   */
+  final _topLevelNameDeclarationsTasks = <_TopLevelNameDeclarationsTask>[];
+
+  /**
    * The mapping from the files for which the index was requested using
    * [getIndex] to the [Completer]s to report the result.
    */
@@ -513,6 +519,9 @@
     if (_unitElementRequestedFiles.isNotEmpty) {
       return AnalysisDriverPriority.interactive;
     }
+    if (_topLevelNameDeclarationsTasks.isNotEmpty) {
+      return AnalysisDriverPriority.interactive;
+    }
     if (_priorityFiles.isNotEmpty) {
       for (String path in _priorityFiles) {
         if (_fileTracker.isFilePending(path)) {
@@ -962,6 +971,19 @@
   }
 
   /**
+   * Return a [Future] that completes with top-level declarations with the
+   * given [name] in all known libraries.
+   */
+  Future<List<TopLevelDeclarationInSource>> getTopLevelNameDeclarations(
+      String name) {
+    _discoverAvailableFiles();
+    var task = new _TopLevelNameDeclarationsTask(this, name);
+    _topLevelNameDeclarationsTasks.add(task);
+    _scheduler.notify(this);
+    return task.completer.future;
+  }
+
+  /**
    * Return a [Future] that completes with the [UnitElementResult] for the
    * file with the given [path], or with `null` if the file cannot be analyzed.
    */
@@ -1186,6 +1208,16 @@
       return;
     }
 
+    // Compute top-level declarations.
+    if (_topLevelNameDeclarationsTasks.isNotEmpty) {
+      _TopLevelNameDeclarationsTask task = _topLevelNameDeclarationsTasks.first;
+      bool isDone = task.perform();
+      if (isDone) {
+        _topLevelNameDeclarationsTasks.remove(task);
+      }
+      return;
+    }
+
     // Analyze a priority file.
     if (_priorityFiles.isNotEmpty) {
       for (String path in _priorityFiles) {
@@ -1575,7 +1607,6 @@
       _unlinkedSalt,
       _linkedSalt,
       externalSummaries: _externalSummaries,
-      useSummary2: useSummary2,
     );
     _fileTracker = new FileTracker(_logger, _fsState, _changeHook);
   }
@@ -2516,3 +2547,66 @@
     return true;
   }
 }
+
+/**
+ * Task that computes top-level declarations for a certain name in all
+ * known libraries.
+ */
+class _TopLevelNameDeclarationsTask {
+  final AnalysisDriver driver;
+  final String name;
+  final Completer<List<TopLevelDeclarationInSource>> completer =
+      new Completer<List<TopLevelDeclarationInSource>>();
+
+  final List<TopLevelDeclarationInSource> libraryDeclarations =
+      <TopLevelDeclarationInSource>[];
+  final Set<String> checkedFiles = new Set<String>();
+  final List<String> filesToCheck = <String>[];
+
+  _TopLevelNameDeclarationsTask(this.driver, this.name);
+
+  /**
+   * Perform a single piece of work, and either complete the [completer] and
+   * return `true` to indicate that the task is done, return `false` to indicate
+   * that the task should continue to be run.
+   */
+  bool perform() {
+    // Prepare files to check.
+    if (filesToCheck.isEmpty) {
+      filesToCheck.addAll(driver.addedFiles.difference(checkedFiles));
+      filesToCheck.addAll(driver.knownFiles.difference(checkedFiles));
+    }
+
+    // If no more files to check, complete and done.
+    if (filesToCheck.isEmpty) {
+      completer.complete(libraryDeclarations);
+      return true;
+    }
+
+    // Check the next file.
+    String path = filesToCheck.removeLast();
+    if (checkedFiles.add(path)) {
+      FileState file = driver._fsState.getFileForPath(path);
+      if (!file.isPart) {
+        bool isExported = false;
+
+        TopLevelDeclaration declaration;
+        for (FileState part in file.libraryFiles) {
+          declaration ??= part.topLevelDeclarations[name];
+        }
+
+        if (declaration == null) {
+          declaration = file.exportedTopLevelDeclarations[name];
+          isExported = true;
+        }
+        if (declaration != null) {
+          libraryDeclarations.add(new TopLevelDeclarationInSource(
+              file.source, declaration, isExported));
+        }
+      }
+    }
+
+    // We're not done yet.
+    return false;
+  }
+}
diff --git a/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart b/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart
index 3d782f0..abd0bd5 100644
--- a/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart
@@ -16,7 +16,10 @@
   EnableString.set_literals: ExperimentalFeatures.set_literals,
   EnableString.spread_collections: ExperimentalFeatures.spread_collections,
   EnableString.triple_shift: ExperimentalFeatures.triple_shift,
+
+  // ignore: deprecated_member_use_from_same_package
   EnableString.bogus_disabled: ExperimentalFeatures.bogus_disabled,
+  // ignore: deprecated_member_use_from_same_package
   EnableString.bogus_enabled: ExperimentalFeatures.bogus_enabled,
 };
 
@@ -57,9 +60,11 @@
   static const String triple_shift = 'triple-shift';
 
   /// String to enable the experiment "bogus-disabled"
+  @deprecated
   static const String bogus_disabled = 'bogus-disabled';
 
   /// String to enable the experiment "bogus-enabled"
+  @deprecated
   static const String bogus_enabled = 'bogus-enabled';
 }
 
@@ -116,15 +121,19 @@
       IsExpired.triple_shift,
       'Triple-shift operator');
 
+  @deprecated
   static const bogus_disabled = const ExperimentalFeature(
       7,
+      // ignore: deprecated_member_use_from_same_package
       EnableString.bogus_disabled,
       IsEnabledByDefault.bogus_disabled,
       IsExpired.bogus_disabled,
       null);
 
+  @deprecated
   static const bogus_enabled = const ExperimentalFeature(
       8,
+      // ignore: deprecated_member_use_from_same_package
       EnableString.bogus_enabled,
       IsEnabledByDefault.bogus_enabled,
       IsExpired.bogus_enabled,
@@ -157,9 +166,11 @@
   static const bool triple_shift = false;
 
   /// Default state of the experiment "bogus-disabled"
+  @deprecated
   static const bool bogus_disabled = false;
 
   /// Default state of the experiment "bogus-enabled"
+  @deprecated
   static const bool bogus_enabled = true;
 }
 
@@ -197,9 +208,11 @@
 
 mixin _CurrentState {
   /// Current state for the flag "bogus-disabled"
+  @deprecated
   bool get bogus_disabled => isEnabled(ExperimentalFeatures.bogus_disabled);
 
   /// Current state for the flag "bogus-enabled"
+  @deprecated
   bool get bogus_enabled => isEnabled(ExperimentalFeatures.bogus_enabled);
 
   /// Current state for the flag "constant-update-2018"
diff --git a/pkg/analyzer/lib/src/dart/analysis/file_state.dart b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
index 252c1e4..665f248 100644
--- a/pkg/analyzer/lib/src/dart/analysis/file_state.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
@@ -13,10 +13,12 @@
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/src/dart/analysis/byte_store.dart';
 import 'package:analyzer/src/dart/analysis/defined_names.dart';
+import 'package:analyzer/src/dart/analysis/driver.dart';
 import 'package:analyzer/src/dart/analysis/library_graph.dart';
 import 'package:analyzer/src/dart/analysis/performance_logger.dart';
 import 'package:analyzer/src/dart/analysis/referenced_names.dart';
 import 'package:analyzer/src/dart/analysis/unlinked_api_signature.dart';
+import 'package:analyzer/src/dart/analysis/top_level_declaration.dart';
 import 'package:analyzer/src/dart/scanner/reader.dart';
 import 'package:analyzer/src/dart/scanner/scanner.dart';
 import 'package:analyzer/src/generated/engine.dart';
@@ -83,6 +85,11 @@
  * should be called.
  */
 class FileState {
+  /**
+   * The next value for [_exportDeclarationsId].
+   */
+  static int _exportDeclarationsNextId = 0;
+
   final FileSystemState _fsState;
 
   /**
@@ -135,6 +142,10 @@
   String _transitiveSignature;
   String _transitiveSignatureLinked;
 
+  Map<String, TopLevelDeclaration> _topLevelDeclarations;
+  Map<String, TopLevelDeclaration> _exportedTopLevelDeclarations;
+  int _exportDeclarationsId = 0;
+
   /**
    * The flag that shows whether the file has an error or warning that
    * might be fixed by a change to another file.
@@ -205,6 +216,18 @@
    */
   List<FileState> get exportedFiles => _exportedFiles;
 
+  /**
+   * Return [TopLevelDeclaration]s exported from the this library file. The
+   * keys to the map are names of declarations.
+   */
+  Map<String, TopLevelDeclaration> get exportedTopLevelDeclarations {
+    if (AnalysisDriver.useSummary2) {
+      return <String, TopLevelDeclaration>{};
+    }
+    _exportDeclarationsNextId = 1;
+    return _computeExportedDeclarations().declarations;
+  }
+
   @override
   int get hashCode => uri.hashCode;
 
@@ -303,6 +326,63 @@
   FileStateTestView get test => new FileStateTestView(this);
 
   /**
+   * Return public top-level declarations declared in the file. The keys to the
+   * map are names of declarations.
+   */
+  Map<String, TopLevelDeclaration> get topLevelDeclarations {
+    if (AnalysisDriver.useSummary2) {
+      return <String, TopLevelDeclaration>{};
+    }
+
+    if (_topLevelDeclarations == null) {
+      _topLevelDeclarations = <String, TopLevelDeclaration>{};
+
+      void addDeclaration(TopLevelDeclarationKind kind, String name) {
+        if (!name.startsWith('_')) {
+          _topLevelDeclarations[name] = new TopLevelDeclaration(kind, name);
+        }
+      }
+
+      // Add types.
+      for (UnlinkedClass type in unlinked.classes) {
+        addDeclaration(TopLevelDeclarationKind.type, type.name);
+      }
+      for (UnlinkedEnum type in unlinked.enums) {
+        addDeclaration(TopLevelDeclarationKind.type, type.name);
+      }
+      for (UnlinkedClass type in unlinked.mixins) {
+        addDeclaration(TopLevelDeclarationKind.type, type.name);
+      }
+      for (UnlinkedTypedef type in unlinked.typedefs) {
+        addDeclaration(TopLevelDeclarationKind.type, type.name);
+      }
+      // Add functions and variables.
+      Set<String> addedVariableNames = new Set<String>();
+      for (UnlinkedExecutable executable in unlinked.executables) {
+        String name = executable.name;
+        if (executable.kind == UnlinkedExecutableKind.functionOrMethod) {
+          addDeclaration(TopLevelDeclarationKind.function, name);
+        } else if (executable.kind == UnlinkedExecutableKind.getter ||
+            executable.kind == UnlinkedExecutableKind.setter) {
+          if (executable.kind == UnlinkedExecutableKind.setter) {
+            name = name.substring(0, name.length - 1);
+          }
+          if (addedVariableNames.add(name)) {
+            addDeclaration(TopLevelDeclarationKind.variable, name);
+          }
+        }
+      }
+      for (UnlinkedVariable variable in unlinked.variables) {
+        String name = variable.name;
+        if (addedVariableNames.add(name)) {
+          addDeclaration(TopLevelDeclarationKind.variable, name);
+        }
+      }
+    }
+    return _topLevelDeclarations;
+  }
+
+  /**
    * Return the set of transitive files - the file itself and all of the
    * directly or indirectly referenced files.
    */
@@ -396,7 +476,7 @@
   bool refresh({bool allowCached: false}) {
     counterFileStateRefresh++;
 
-    if (_fsState.useSummary2) {
+    if (AnalysisDriver.useSummary2) {
       return _refresh2(allowCached: allowCached);
     }
 
@@ -471,6 +551,10 @@
           library.libraryCycle?.invalidate();
         }
       }
+
+      for (FileState file in _fsState._uriToFile.values) {
+        file._exportedTopLevelDeclarations = null;
+      }
     }
 
     // This file is potentially not a library for its previous parts anymore.
@@ -537,6 +621,69 @@
   @override
   String toString() => path ?? '<unresolved>';
 
+  /**
+   * Compute the full or partial map of exported declarations for this library.
+   */
+  _ExportedDeclarations _computeExportedDeclarations() {
+    // If we know exported declarations, return them.
+    if (_exportedTopLevelDeclarations != null) {
+      return new _ExportedDeclarations(0, _exportedTopLevelDeclarations);
+    }
+
+    // If we are already computing exported declarations for this library,
+    // report that we found a cycle.
+    if (_exportDeclarationsId != 0) {
+      return new _ExportedDeclarations(_exportDeclarationsId, null);
+    }
+
+    var declarations = <String, TopLevelDeclaration>{};
+
+    // Give each library a unique identifier.
+    _exportDeclarationsId = _exportDeclarationsNextId++;
+
+    // Append the exported declarations.
+    int firstCycleId = 0;
+    for (int i = 0; i < _exportedFiles.length; i++) {
+      var exported = _exportedFiles[i]._computeExportedDeclarations();
+      if (exported.declarations != null) {
+        for (TopLevelDeclaration t in exported.declarations.values) {
+          if (_exportFilters[i].accepts(t.name)) {
+            declarations[t.name] = t;
+          }
+        }
+      }
+      if (exported.firstCycleId > 0) {
+        if (firstCycleId == 0 || firstCycleId > exported.firstCycleId) {
+          firstCycleId = exported.firstCycleId;
+        }
+      }
+    }
+
+    // If this library is the first component of the cycle, then we are at
+    // the beginning of this cycle, and combination of partial export
+    // namespaces of other exported libraries and declarations of this library
+    // is the full export namespace of this library.
+    if (firstCycleId != 0 && firstCycleId == _exportDeclarationsId) {
+      firstCycleId = 0;
+    }
+
+    // We're done with this library, successfully or not.
+    _exportDeclarationsId = 0;
+
+    // Append the library declarations.
+    for (FileState file in libraryFiles) {
+      declarations.addAll(file.topLevelDeclarations);
+    }
+
+    // Record the declarations only if it is the full result.
+    if (firstCycleId == 0) {
+      _exportedTopLevelDeclarations = declarations;
+    }
+
+    // Return the full or partial result.
+    return new _ExportedDeclarations(firstCycleId, declarations);
+  }
+
   CompilationUnit _createEmptyCompilationUnit(FeatureSet featureSet) {
     var token = new Token.eof(0);
     return astFactory.compilationUnit2(
@@ -572,6 +719,7 @@
     _definedTopLevelNames = null;
     _definedClassMemberNames = null;
     _referencedNames = null;
+    _topLevelDeclarations = null;
 
     if (_driverUnlinkedUnit != null) {
       for (var name in _driverUnlinkedUnit.subtypedNames) {
@@ -686,6 +834,10 @@
           library.libraryCycle?.invalidate();
         }
       }
+
+      for (FileState file in _fsState._uriToFile.values) {
+        file._exportedTopLevelDeclarations = null;
+      }
     }
 
     // This file is potentially not a library for its previous parts anymore.
@@ -748,8 +900,9 @@
 
   static UnlinkedUnit2Builder serializeAstUnlinked2(CompilationUnit unit) {
     var exports = <String>[];
-    var imports = <String>['dart:core'];
+    var imports = <String>[];
     var parts = <String>[];
+    var hasDartCoreImport = false;
     var hasLibraryDirective = false;
     var hasPartOfDirective = false;
     for (var directive in unit.directives) {
@@ -759,6 +912,9 @@
       } else if (directive is ImportDirective) {
         var uriStr = directive.uri.stringValue;
         imports.add(uriStr ?? '');
+        if (uriStr == 'dart:core') {
+          hasDartCoreImport = true;
+        }
       } else if (directive is LibraryDirective) {
         hasLibraryDirective = true;
       } else if (directive is PartDirective) {
@@ -768,6 +924,9 @@
         hasPartOfDirective = true;
       }
     }
+    if (!hasDartCoreImport) {
+      imports.add('dart:core');
+    }
     var informativeData = createInformativeData(unit);
     return UnlinkedUnit2Builder(
       apiSignature: computeUnlinkedApiSignature(unit),
@@ -823,7 +982,6 @@
   final AnalysisOptions _analysisOptions;
   final Uint32List _unlinkedSalt;
   final Uint32List _linkedSalt;
-  final bool useSummary2;
 
   /**
    * The optional store with externally provided unlinked and corresponding
@@ -904,7 +1062,6 @@
     this._unlinkedSalt,
     this._linkedSalt, {
     this.externalSummaries,
-    this.useSummary2 = false,
   }) {
     _fileContentCache = _FileContentCache.getInstance(
       _resourceProvider,
@@ -1103,6 +1260,23 @@
         .where((f) => f._libraryCycle == null)
         .toSet();
   }
+
+  Set<FileState> get librariesWithComputedExportedDeclarations {
+    return state._uriToFile.values
+        .where((f) => !f.isPart && f._exportedTopLevelDeclarations != null)
+        .toSet();
+  }
+}
+
+/**
+ * The result of computing exported top-level declarations.
+ * It can be full (when [firstCycleId] is zero), or partial (when a cycle)
+ */
+class _ExportedDeclarations {
+  final int firstCycleId;
+  final Map<String, TopLevelDeclaration> declarations;
+
+  _ExportedDeclarations(this.firstCycleId, this.declarations);
 }
 
 /**
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
index f3b0f77..ba3be49 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
@@ -10,6 +10,7 @@
 import 'package:analyzer/error/error.dart';
 import 'package:analyzer/error/listener.dart';
 import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/src/dart/analysis/driver.dart';
 import 'package:analyzer/src/dart/analysis/file_state.dart';
 import 'package:analyzer/src/dart/ast/ast.dart';
 import 'package:analyzer/src/dart/ast/utilities.dart';
@@ -20,6 +21,7 @@
 import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/dart/element/handle.dart';
 import 'package:analyzer/src/dart/element/inheritance_manager2.dart';
+import 'package:analyzer/src/dart/resolver/legacy_type_asserter.dart';
 import 'package:analyzer/src/error/codes.dart';
 import 'package:analyzer/src/error/inheritance_override.dart';
 import 'package:analyzer/src/error/pending_error.dart';
@@ -201,6 +203,9 @@
         }
       });
     }
+
+    assert(units.values.every(LegacyTypeAsserter.assertLegacyTypes));
+
     timerLibraryAnalyzerVerify.stop();
 
     // Return full results.
@@ -519,7 +524,11 @@
     definingCompilationUnit.element = _libraryElement.definingCompilationUnit;
 
     bool matchNodeElement(Directive node, Element element) {
-      return node.offset == element.nameOffset;
+      if (AnalysisDriver.useSummary2) {
+        return node.keyword.offset == element.nameOffset;
+      } else {
+        return node.offset == element.nameOffset;
+      }
     }
 
     ErrorReporter libraryErrorReporter = _getErrorReporter(_library);
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_context.dart b/pkg/analyzer/lib/src/dart/analysis/library_context.dart
index 8f90231..87010c4 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_context.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_context.dart
@@ -133,6 +133,9 @@
    * Get the [LibraryElement] for the given library.
    */
   LibraryElement getLibraryElement(FileState library) {
+    if (elementFactory != null) {
+      return elementFactory.libraryOfUri(library.uriStr);
+    }
     return resynthesizer.getLibraryElement(library.uriStr);
   }
 
@@ -283,11 +286,24 @@
           if (librarySource == null) continue;
 
           var inputUnits = <link2.LinkInputUnit>[];
+          var partIndex = -1;
           for (var file in libraryFile.libraryFiles) {
             var isSynthetic = !file.exists;
             var unit = file.parse();
+
+            String partUriStr;
+            if (partIndex >= 0) {
+              partUriStr = libraryFile.unlinked2.parts[partIndex];
+            }
+            partIndex++;
+
             inputUnits.add(
-              link2.LinkInputUnit(file.source, isSynthetic, unit),
+              link2.LinkInputUnit(
+                partUriStr,
+                file.source,
+                isSynthetic,
+                unit,
+              ),
             );
           }
 
@@ -322,8 +338,7 @@
       // We are about to load dart:core, but if we have just linked it, the
       // linker might have set the type provider. So, clear it, and recreate
       // the element factory - it is empty anyway.
-      var hasDartCoreBeforeBundle = elementFactory.hasDartCore;
-      if (!hasDartCoreBeforeBundle) {
+      if (!elementFactory.hasDartCore) {
         analysisContext.clearTypeProvider();
         _createElementFactory();
       }
@@ -346,10 +361,8 @@
         }
       }
 
-      // If the first bundle, with dart:core, create the type provider.
-      if (!hasDartCoreBeforeBundle && elementFactory.hasDartCore) {
-        _createElementFactoryTypeProvider();
-      }
+      // We might have just linked dart:core, ensure the type provider.
+      _createElementFactoryTypeProvider();
     }
 
     logger.run('Prepare linked bundles', () {
@@ -365,6 +378,11 @@
       );
     });
 
+    // There might be a rare (and wrong) situation, when the external summaries
+    // already include the [targetLibrary]. When this happens, [loadBundle]
+    // exists without doing any work. But the type provider must be created.
+    _createElementFactoryTypeProvider();
+
     timerLoad2.stop();
   }
 
@@ -392,7 +410,10 @@
     }
   }
 
+  /// Ensure that type provider is created.
   void _createElementFactoryTypeProvider() {
+    if (analysisContext.typeProvider != null) return;
+
     var dartCore = elementFactory.libraryOfUri('dart:core');
     var dartAsync = elementFactory.libraryOfUri('dart:async');
     var typeProvider = SummaryTypeProvider()
diff --git a/pkg/analyzer/lib/src/dart/analysis/results.dart b/pkg/analyzer/lib/src/dart/analysis/results.dart
index b38336b..e14ef3f 100644
--- a/pkg/analyzer/lib/src/dart/analysis/results.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/results.dart
@@ -163,6 +163,22 @@
   ResultState get state => ResultState.VALID;
 }
 
+class ParseStringResultImpl implements ParseStringResult {
+  @override
+  final String content;
+
+  @override
+  final List<AnalysisError> errors;
+
+  @override
+  final CompilationUnit unit;
+
+  ParseStringResultImpl(this.content, this.unit, this.errors);
+
+  @override
+  LineInfo get lineInfo => unit.lineInfo;
+}
+
 class ResolvedLibraryResultImpl extends AnalysisResultImpl
     implements ResolvedLibraryResult {
   @override
diff --git a/pkg/analyzer/lib/src/dart/analysis/session.dart b/pkg/analyzer/lib/src/dart/analysis/session.dart
index 376e315..30db6fa 100644
--- a/pkg/analyzer/lib/src/dart/analysis/session.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/session.dart
@@ -12,6 +12,7 @@
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/src/dart/analysis/driver.dart' as driver;
+import 'package:analyzer/src/dart/analysis/top_level_declaration.dart';
 import 'package:analyzer/src/dart/analysis/uri_converter.dart';
 import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl;
 import 'package:analyzer/src/generated/resolver.dart';
@@ -158,6 +159,13 @@
   }
 
   @override
+  Future<List<TopLevelDeclarationInSource>> getTopLevelDeclarations(
+      String name) {
+    _checkConsistency();
+    return _driver.getTopLevelNameDeclarations(name);
+  }
+
+  @override
   Future<UnitElementResult> getUnitElement(String path) {
     _checkConsistency();
     return _driver.getUnitElement(path);
diff --git a/pkg/analyzer/lib/src/dart/analysis/top_level_declaration.dart b/pkg/analyzer/lib/src/dart/analysis/top_level_declaration.dart
new file mode 100644
index 0000000..1658aad
--- /dev/null
+++ b/pkg/analyzer/lib/src/dart/analysis/top_level_declaration.dart
@@ -0,0 +1,50 @@
+// Copyright (c) 2016, 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 'package:analyzer/src/generated/source.dart';
+
+/**
+ * Information about a single top-level declaration.
+ */
+class TopLevelDeclaration {
+  final TopLevelDeclarationKind kind;
+  final String name;
+
+  TopLevelDeclaration(this.kind, this.name);
+
+  @override
+  String toString() => '($kind, $name)';
+}
+
+/**
+ * A declaration in a source.
+ */
+class TopLevelDeclarationInSource {
+  /**
+   * The declaring source.
+   */
+  final Source source;
+
+  /**
+   * The declaration.
+   */
+  final TopLevelDeclaration declaration;
+
+  /**
+   * Is `true` if the [declaration] is exported, not declared in the [source].
+   */
+  final bool isExported;
+
+  TopLevelDeclarationInSource(this.source, this.declaration, this.isExported);
+
+  @override
+  String toString() => '($source, $declaration, $isExported)';
+}
+
+/**
+ * Kind of a top-level declaration.
+ *
+ * We don't need it to be precise, just enough to support quick fixes.
+ */
+enum TopLevelDeclarationKind { type, function, variable }
diff --git a/pkg/analyzer/lib/src/dart/ast/ast.dart b/pkg/analyzer/lib/src/dart/ast/ast.dart
index 1d17498..f327523 100644
--- a/pkg/analyzer/lib/src/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/src/dart/ast/ast.dart
@@ -3828,13 +3828,17 @@
 ///        'on' [TypeAnnotation] '{' [ClassMember]* '}'
 ///
 /// Clients may not extend, implement or mix-in this class.
-class ExtensionDeclarationImpl extends NamedCompilationUnitMemberImpl
+class ExtensionDeclarationImpl extends CompilationUnitMemberImpl
     implements ExtensionDeclaration {
   @override
   Token extensionKeyword;
 
-  /// The type parameters for the extension, or `null` if the extension
-  /// does not have any type parameters.
+  /// The name of the extension, or `null` if the extension does not have a
+  /// name.
+  SimpleIdentifierImpl _name;
+
+  /// The type parameters for the extension, or `null` if the extension does not
+  /// have any type parameters.
   TypeParameterListImpl _typeParameters;
 
   @override
@@ -3856,14 +3860,14 @@
       CommentImpl comment,
       List<Annotation> metadata,
       this.extensionKeyword,
-      SimpleIdentifierImpl name,
+      this._name,
       TypeParameterListImpl typeParameters,
       this.onKeyword,
       TypeAnnotationImpl extendedType,
       this.leftBracket,
       List<ClassMember> members,
       this.rightBracket)
-      : super(comment, metadata, name) {
+      : super(comment, metadata) {
     _typeParameters = _becomeParentOf(typeParameters);
     _extendedType = _becomeParentOf(extendedType);
     _members = new NodeListImpl<ClassMember>(this, members);
@@ -3906,6 +3910,13 @@
   NodeList<ClassMember> get members => _members;
 
   @override
+  SimpleIdentifier get name => _name;
+
+  void set name(SimpleIdentifier identifier) {
+    _name = _becomeParentOf(identifier as SimpleIdentifierImpl);
+  }
+
+  @override
   TypeParameterList get typeParameters => _typeParameters;
 
   void set typeParameters(TypeParameterList typeParameters) {
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index cf5423c..199de05 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -1323,8 +1323,8 @@
     assert(_fields == null);
 
     var context = enclosingUnit.linkedContext;
-    var accessorList = <PropertyAccessorElementImpl>[];
-    var fieldList = <FieldElementImpl>[];
+    var accessorList = <PropertyAccessorElement>[];
+    var fieldList = <FieldElement>[];
 
     var fields = context.getFields(linkedNode);
     for (var field in fields) {
@@ -2164,18 +2164,18 @@
     assert(unit._accessors == null);
 
     var accessorMap =
-        <CompilationUnitElementImpl, List<PropertyAccessorElementImpl>>{};
+        <CompilationUnitElementImpl, List<PropertyAccessorElement>>{};
     var variableMap =
-        <CompilationUnitElementImpl, List<TopLevelVariableElementImpl>>{};
+        <CompilationUnitElementImpl, List<TopLevelVariableElement>>{};
 
     var units = unit.library.units;
     for (CompilationUnitElementImpl unit in units) {
       var context = unit.linkedContext;
 
-      var accessorList = <PropertyAccessorElementImpl>[];
+      var accessorList = <PropertyAccessorElement>[];
       accessorMap[unit] = accessorList;
 
-      var variableList = <TopLevelVariableElementImpl>[];
+      var variableList = <TopLevelVariableElement>[];
       variableMap[unit] = variableList;
 
       var unitNode = unit.linkedContext.unit_withDeclarations;
@@ -4851,6 +4851,11 @@
 
   @override
   String get uri {
+    if (linkedNode != null) {
+      ExportDirective node = linkedNode;
+      return node.uri.stringValue;
+    }
+
     if (_unlinkedExportPublic != null) {
       return _selectedUri ??= _selectUri(
           _unlinkedExportPublic.uri, _unlinkedExportPublic.configurations);
@@ -5611,7 +5616,9 @@
         var function = context.getGeneticTypeAliasFunction(linkedNode);
         if (function != null) {
           var reference = context.getGenericFunctionTypeReference(function);
-          return _function = reference.element;
+          _function = reference.element;
+          encloseElement(_function);
+          return _function;
         } else {
           return null;
         }
@@ -6123,6 +6130,11 @@
 
   @override
   String get uri {
+    if (linkedNode != null) {
+      ImportDirective node = linkedNode;
+      return node.uri.stringValue;
+    }
+
     if (_unlinkedImport != null) {
       if (_unlinkedImport.isImplicit) {
         return null;
diff --git a/pkg/analyzer/lib/src/dart/element/type.dart b/pkg/analyzer/lib/src/dart/element/type.dart
index 8da95fd..27dd52e 100644
--- a/pkg/analyzer/lib/src/dart/element/type.dart
+++ b/pkg/analyzer/lib/src/dart/element/type.dart
@@ -13,7 +13,6 @@
     show AnalysisContext, AnalysisEngine;
 import 'package:analyzer/src/generated/resolver.dart';
 import 'package:analyzer/src/generated/type_system.dart';
-import 'package:analyzer/src/generated/utilities_collection.dart';
 import 'package:analyzer/src/generated/utilities_dart.dart';
 import 'package:analyzer/src/summary/resynthesize.dart'
     show RecursiveInstantiateToBounds;
@@ -567,8 +566,12 @@
   /// element tree.
   factory FunctionTypeImpl.synthetic(DartType returnType,
       List<TypeParameterElement> typeFormals, List<ParameterElement> parameters,
-      {NullabilitySuffix nullabilitySuffix = NullabilitySuffix.star}) {
+      {Element element,
+      List<DartType> typeArguments = const <DartType>[],
+      NullabilitySuffix nullabilitySuffix = NullabilitySuffix.star}) {
     return new _FunctionTypeImplStrict._(returnType, typeFormals, parameters,
+        element: element,
+        typeArguments: typeArguments,
         nullabilitySuffix: nullabilitySuffix);
   }
 
@@ -721,8 +724,7 @@
               normalParameterTypes, object.normalParameterTypes) &&
           TypeImpl.equalArrays(
               optionalParameterTypes, object.optionalParameterTypes) &&
-          _equals(namedParameterTypes, object.namedParameterTypes) &&
-          TypeImpl.equalArrays(typeArguments, object.typeArguments);
+          _equals(namedParameterTypes, object.namedParameterTypes);
     }
     return false;
   }
@@ -2258,9 +2260,6 @@
 
     List<DartType> newTypeArguments = TypeImpl.substitute(
         typeArguments, argumentTypes, parameterTypes, prune);
-    if (listsEqual(newTypeArguments, typeArguments)) {
-      return this;
-    }
 
     InterfaceTypeImpl newType =
         new InterfaceTypeImpl(element, prune, nullabilitySuffix);
@@ -3773,14 +3772,29 @@
   @override
   final List<ParameterElement> parameters;
 
+  @override
+  final List<DartType> typeArguments;
+
   _FunctionTypeImplStrict._(this.returnType, this.typeFormals, this.parameters,
-      {NullabilitySuffix nullabilitySuffix = NullabilitySuffix.star})
-      : super._(null, null, nullabilitySuffix);
+      {Element element,
+      List<DartType> typeArguments,
+      NullabilitySuffix nullabilitySuffix = NullabilitySuffix.star})
+      : typeArguments = typeArguments ?? const <DartType>[],
+        super._(element, null, nullabilitySuffix);
 
   @override
   List<TypeParameterElement> get boundTypeParameters => typeFormals;
 
   @override
+  FunctionTypedElement get element {
+    var element = super.element;
+    if (element is GenericTypeAliasElement) {
+      return element.function;
+    }
+    return element;
+  }
+
+  @override
   bool get isInstantiated => throw new UnimplementedError('TODO(paulberry)');
 
   @override
@@ -3802,9 +3816,6 @@
   List<FunctionTypeAliasElement> get prunedTypedefs => const [];
 
   @override
-  List<DartType> get typeArguments => const [] /*TODO(paulberry)*/;
-
-  @override
   List<TypeParameterElement> get typeParameters => const [] /*TODO(paulberry)*/;
 
   @override
@@ -3891,8 +3902,13 @@
         identical(parameters, newParameters)) {
       return this;
     }
+
+    var typeArguments = this.typeArguments.map(transformType).toList();
+
     return new _FunctionTypeImplStrict._(
         newReturnType, newTypeFormals, newParameters,
+        element: element,
+        typeArguments: typeArguments,
         nullabilitySuffix: nullabilitySuffix);
   }
 
@@ -3900,6 +3916,8 @@
   TypeImpl withNullability(NullabilitySuffix nullabilitySuffix) {
     if (this.nullabilitySuffix == nullabilitySuffix) return this;
     return _FunctionTypeImplStrict._(returnType, typeFormals, parameters,
+        element: element,
+        typeArguments: typeArguments,
         nullabilitySuffix: nullabilitySuffix);
   }
 
diff --git a/pkg/analyzer/lib/src/dart/element/type_algebra.dart b/pkg/analyzer/lib/src/dart/element/type_algebra.dart
index d73ab28..a6f3bc0 100644
--- a/pkg/analyzer/lib/src/dart/element/type_algebra.dart
+++ b/pkg/analyzer/lib/src/dart/element/type_algebra.dart
@@ -318,10 +318,12 @@
     inner.invertVariance();
 
     var returnType = inner.visit(type.returnType);
+    var typeArguments = type.typeArguments.map(visit).toList();
 
     if (this.useCounter == before) return type;
 
-    return FunctionTypeImpl.synthetic(returnType, typeFormals, parameters);
+    return FunctionTypeImpl.synthetic(returnType, typeFormals, parameters,
+        element: type.element, typeArguments: typeArguments);
   }
 
   @override
diff --git a/pkg/analyzer/lib/src/dart/error/hint_codes.dart b/pkg/analyzer/lib/src/dart/error/hint_codes.dart
index 651d581..fa76944 100644
--- a/pkg/analyzer/lib/src/dart/error/hint_codes.dart
+++ b/pkg/analyzer/lib/src/dart/error/hint_codes.dart
@@ -79,8 +79,7 @@
   // #### Common fixes
   //
   // The documentation for declarations that are annotated with `@deprecated`
-  // should have documentation to indicate what code to use in place of the
-  // deprecated code.
+  // should indicate what code to use in place of the deprecated code.
   static const HintCode DEPRECATED_MEMBER_USE = const HintCode(
       'DEPRECATED_MEMBER_USE', "'{0}' is deprecated and shouldn't be used.",
       correction:
@@ -668,8 +667,8 @@
   //
   // #### Example
   //
-  // In a package that defines SDK constraints in the `pubspec.yaml` file that
-  // have a lower bound that's less than 2.2:
+  // In a package that defines the SDK constraint (in the pubspec.yaml file),
+  // with a lower bound of less than 2.2. For example:
   //
   // ```yaml
   // environment:
diff --git a/pkg/analyzer/lib/src/dart/resolver/flow_analysis.dart b/pkg/analyzer/lib/src/dart/resolver/flow_analysis.dart
index 1c8dc31..b711bcb 100644
--- a/pkg/analyzer/lib/src/dart/resolver/flow_analysis.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/flow_analysis.dart
@@ -2,62 +2,60 @@
 // 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 'package:analyzer/dart/ast/ast.dart';
-import 'package:analyzer/dart/element/element.dart';
+/// Sets of variables that are potentially assigned in a statement.
+class AssignedVariables<Statement, Element> {
+  final emptySet = Set<Element>();
 
-/// Sets of variables that are potentially assigned in a node.
-class AssignedVariables {
-  static final emptySet = Set<VariableElement>();
+  /// Mapping from a [Statement] representing a loop to the set of variables
+  /// that are potentially assigned in that loop.
+  final Map<Statement, Set<Element>> _map = {};
 
-  /// Mapping from a loop [AstNode] to the set of variables that are
-  /// potentially assigned in this loop.
-  final Map<AstNode, Set<VariableElement>> _map = {};
+  /// The stack of nested nodes.
+  final List<Set<Element>> _stack = [];
 
-  /// The stack of nested loops.
-  final List<Set<VariableElement>> _stack = [];
+  AssignedVariables();
 
-  /// Return the set of variables that are potentially assigned in the [loop].
-  Set<VariableElement> operator [](AstNode loop) {
-    return _map[loop] ?? emptySet;
+  /// Return the set of variables that are potentially assigned in the
+  /// [statement].
+  Set<Element> operator [](Statement statement) {
+    return _map[statement] ?? emptySet;
   }
 
   void beginLoop() {
-    var set = Set<VariableElement>.identity();
+    var set = Set<Element>.identity();
     _stack.add(set);
   }
 
-  void endLoop(AstNode loop) {
-    _map[loop] = _stack.removeLast();
+  void endLoop(Statement node) {
+    _map[node] = _stack.removeLast();
   }
 
-  void write(VariableElement variable) {
+  void write(Element variable) {
     for (var i = 0; i < _stack.length; ++i) {
       _stack[i].add(variable);
     }
   }
 }
 
-class FlowAnalysis<T> {
-  final _identity = _State<T>(
-    false,
-    _ElementSet.empty,
-    _ElementSet.empty,
-    _ElementSet.empty,
-    const {},
-  );
+class FlowAnalysis<Statement, Expression, Element, Type> {
+  final _ElementSet<Element> _emptySet;
+  final _State<Element, Type> _identity;
 
   /// The output list of variables that were read before they were written.
   /// TODO(scheglov) use _ElementSet?
-  final List<LocalVariableElement> readBeforeWritten = [];
+  final List<Element> readBeforeWritten = [];
+
+  /// The [NodeOperations], used to manipulate expressions.
+  final NodeOperations<Expression> nodeOperations;
 
   /// The [TypeOperations], used to access types, and check subtyping.
-  final TypeOperations<T> typeOperations;
+  final TypeOperations<Element, Type> typeOperations;
 
-  /// The enclosing [FunctionBody], used to check for potential mutations.
-  final FunctionBody functionBody;
+  /// The enclosing function body, used to check for potential mutations.
+  final FunctionBodyAccess functionBody;
 
   /// The stack of states of variables that are not definitely assigned.
-  final List<_State> _stack = [];
+  final List<_State<Element, Type>> _stack = [];
 
   /// The mapping from labeled [Statement]s to the index in the [_stack]
   /// where the first related element is located.  The number of elements
@@ -66,25 +64,55 @@
   final Map<Statement, int> _statementToStackIndex = {};
 
   /// The list of all variables.
-  final List<VariableElement> _variables = [];
+  final List<Element> _variables = [];
 
-  _State<T> _current;
+  _State<Element, Type> _current;
 
   /// The last boolean condition, for [_conditionTrue] and [_conditionFalse].
   Expression _condition;
 
   /// The state when [_condition] evaluates to `true`.
-  _State<T> _conditionTrue;
+  _State<Element, Type> _conditionTrue;
 
   /// The state when [_condition] evaluates to `false`.
-  _State<T> _conditionFalse;
+  _State<Element, Type> _conditionFalse;
 
-  FlowAnalysis(this.typeOperations, this.functionBody) {
-    _current = _State<T>(
+  factory FlowAnalysis(
+    NodeOperations<Expression> nodeOperations,
+    TypeOperations<Element, Type> typeOperations,
+    FunctionBodyAccess functionBody,
+  ) {
+    var emptySet = _ElementSet<Element>._(
+      List<Element>(0),
+    );
+    var identifyState = _State<Element, Type>(
+      false,
+      emptySet,
+      emptySet,
+      emptySet,
+      const {},
+    );
+    return FlowAnalysis._(
+      nodeOperations,
+      typeOperations,
+      functionBody,
+      emptySet,
+      identifyState,
+    );
+  }
+
+  FlowAnalysis._(
+    this.nodeOperations,
+    this.typeOperations,
+    this.functionBody,
+    this._emptySet,
+    this._identity,
+  ) {
+    _current = _State<Element, Type>(
       true,
-      _ElementSet.empty,
-      _ElementSet.empty,
-      _ElementSet.empty,
+      _emptySet,
+      _emptySet,
+      _emptySet,
       const {},
     );
   }
@@ -93,17 +121,18 @@
   bool get isReachable => _current.reachable;
 
   /// Add a new [variable], which might be already [assigned].
-  void add(VariableElement variable, {bool assigned: false}) {
+  void add(Element variable, {bool assigned: false}) {
     _variables.add(variable);
     _current = _current.add(variable, assigned: assigned);
   }
 
-  void conditional_elseBegin(ConditionalExpression node, bool isBool) {
+  void conditional_elseBegin(Expression conditionalExpression,
+      Expression thenExpression, bool isBool) {
     var afterThen = _current;
     var falseCondition = _stack.removeLast();
 
     if (isBool) {
-      _conditionalEnd(node.thenExpression);
+      _conditionalEnd(thenExpression);
       // Tail of the stack: falseThen, trueThen
     }
 
@@ -111,12 +140,13 @@
     _current = falseCondition;
   }
 
-  void conditional_end(ConditionalExpression node, bool isBool) {
+  void conditional_end(Expression conditionalExpression,
+      Expression elseExpression, bool isBool) {
     var afterThen = _stack.removeLast();
     var afterElse = _current;
 
     if (isBool) {
-      _conditionalEnd(node.elseExpression);
+      _conditionalEnd(elseExpression);
       // Tail of the stack: falseThen, trueThen, falseElse, trueElse
 
       var trueElse = _stack.removeLast();
@@ -128,7 +158,7 @@
       var trueResult = _join(trueThen, trueElse);
       var falseResult = _join(falseThen, falseElse);
 
-      _condition = node;
+      _condition = conditionalExpression;
       _conditionTrue = trueResult;
       _conditionFalse = falseResult;
     }
@@ -136,41 +166,41 @@
     _current = _join(afterThen, afterElse);
   }
 
-  void conditional_thenBegin(ConditionalExpression node) {
-    _conditionalEnd(node.condition);
+  void conditional_thenBegin(
+      Expression conditionalExpression, Expression condition) {
+    _conditionalEnd(condition);
     // Tail of the stack: falseCondition, trueCondition
 
     var trueCondition = _stack.removeLast();
     _current = trueCondition;
   }
 
-  /// The [node] checks that the [variable] is equal to `null`.
-  void conditionEqNull(BinaryExpression node, VariableElement variable) {
+  /// The [binaryExpression] checks that the [variable] is equal to `null`.
+  void conditionEqNull(Expression binaryExpression, Element variable) {
     if (functionBody.isPotentiallyMutatedInClosure(variable)) {
       return;
     }
 
-    _condition = node;
-    _conditionTrue = _current.markNullable(variable);
-    _conditionFalse = _current.markNonNullable(variable);
+    _condition = binaryExpression;
+    _conditionTrue = _current.markNullable(_emptySet, variable);
+    _conditionFalse = _current.markNonNullable(_emptySet, variable);
   }
 
-  /// The [node] checks that the [variable] is not equal to `null`.
-  void conditionNotEqNull(BinaryExpression node, VariableElement variable) {
+  /// The [binaryExpression] checks that the [variable] is not equal to `null`.
+  void conditionNotEqNull(Expression binaryExpression, Element variable) {
     if (functionBody.isPotentiallyMutatedInClosure(variable)) {
       return;
     }
 
-    _condition = node;
-    _conditionTrue = _current.markNonNullable(variable);
-    _conditionFalse = _current.markNullable(variable);
+    _condition = binaryExpression;
+    _conditionTrue = _current.markNonNullable(_emptySet, variable);
+    _conditionFalse = _current.markNullable(_emptySet, variable);
   }
 
-  void doStatement_bodyBegin(
-      DoStatement node, Set<VariableElement> loopAssigned) {
+  void doStatement_bodyBegin(Statement doStatement, Set<Element> loopAssigned) {
     _current = _current.removePromotedAll(loopAssigned);
 
-    _statementToStackIndex[node] = _stack.length;
+    _statementToStackIndex[doStatement] = _stack.length;
     _stack.add(_identity); // break
     _stack.add(_identity); // continue
   }
@@ -182,8 +212,8 @@
     _current = _join(_current, continueState);
   }
 
-  void doStatement_end(DoStatement node) {
-    _conditionalEnd(node.condition);
+  void doStatement_end(Statement doStatement, Expression condition) {
+    _conditionalEnd(condition);
     // Tail of the stack:  break, falseCondition, trueCondition
 
     _stack.removeLast(); // trueCondition
@@ -193,13 +223,13 @@
     _current = _join(falseCondition, breakState);
   }
 
-  void falseLiteral(BooleanLiteral expression) {
+  void falseLiteral(Expression expression) {
     _condition = expression;
     _conditionTrue = _identity;
     _conditionFalse = _current;
   }
 
-  void forEachStatement_bodyBegin(Set<VariableElement> loopAssigned) {
+  void forEachStatement_bodyBegin(Set<Element> loopAssigned) {
     _stack.add(_current);
     _current = _current.removePromotedAll(loopAssigned);
   }
@@ -222,7 +252,7 @@
     _current = trueCondition;
   }
 
-  void forStatement_conditionBegin(Set<VariableElement> loopAssigned) {
+  void forStatement_conditionBegin(Set<Element> loopAssigned) {
     _current = _current.removePromotedAll(loopAssigned);
   }
 
@@ -245,10 +275,10 @@
   void functionExpression_begin() {
     _stack.add(_current);
 
-    Set<VariableElement> notPromoted = null;
+    Set<Element> notPromoted = null;
     for (var variable in _current.promoted.keys) {
       if (functionBody.isPotentiallyMutatedInScope(variable)) {
-        notPromoted ??= Set<VariableElement>.identity();
+        notPromoted ??= Set<Element>.identity();
         notPromoted.add(variable);
       }
     }
@@ -262,7 +292,7 @@
     _current = _stack.removeLast();
   }
 
-  void handleBreak(AstNode target) {
+  void handleBreak(Statement target) {
     var breakIndex = _statementToStackIndex[target];
     if (breakIndex != null) {
       _stack[breakIndex] = _join(_stack[breakIndex], _current);
@@ -270,7 +300,7 @@
     _current = _current.exit();
   }
 
-  void handleContinue(AstNode target) {
+  void handleContinue(Statement target) {
     var breakIndex = _statementToStackIndex[target];
     if (breakIndex != null) {
       var continueIndex = breakIndex + 1;
@@ -302,8 +332,8 @@
   }
 
   void ifStatement_end(bool hasElse) {
-    _State<T> afterThen;
-    _State<T> afterElse;
+    _State<Element, Type> afterThen;
+    _State<Element, Type> afterElse;
     if (hasElse) {
       afterThen = _stack.removeLast();
       afterElse = _current;
@@ -314,8 +344,8 @@
     _current = _join(afterThen, afterElse);
   }
 
-  void ifStatement_thenBegin(IfStatement ifStatement) {
-    _conditionalEnd(ifStatement.condition);
+  void ifStatement_thenBegin(Statement ifStatement, Expression condition) {
+    _conditionalEnd(condition);
     // Tail of the stack:  falseCondition, trueCondition
 
     var trueCondition = _stack.removeLast();
@@ -323,33 +353,33 @@
   }
 
   void isExpression_end(
-      IsExpression isExpression, VariableElement variable, T type) {
+      Expression isExpression, Element variable, bool isNot, Type type) {
     if (functionBody.isPotentiallyMutatedInClosure(variable)) {
       return;
     }
 
     _condition = isExpression;
-    if (isExpression.notOperator == null) {
-      _conditionTrue = _current.promote(typeOperations, variable, type);
-      _conditionFalse = _current;
-    } else {
+    if (isNot) {
       _conditionTrue = _current;
       _conditionFalse = _current.promote(typeOperations, variable, type);
+    } else {
+      _conditionTrue = _current.promote(typeOperations, variable, type);
+      _conditionFalse = _current;
     }
   }
 
   /// Return `true` if the [variable] is known to be be nullable.
-  bool isNonNullable(VariableElement variable) {
+  bool isNonNullable(Element variable) {
     return !_current.notNonNullable.contains(variable);
   }
 
   /// Return `true` if the [variable] is known to be be non-nullable.
-  bool isNullable(VariableElement variable) {
+  bool isNullable(Element variable) {
     return !_current.notNullable.contains(variable);
   }
 
-  void logicalAnd_end(BinaryExpression andExpression) {
-    _conditionalEnd(andExpression.rightOperand);
+  void logicalAnd_end(Expression andExpression, Expression rightOperand) {
+    _conditionalEnd(rightOperand);
     // Tail of the stack: falseLeft, trueLeft, falseRight, trueRight
 
     var trueRight = _stack.removeLast();
@@ -369,16 +399,16 @@
     _current = afterResult;
   }
 
-  void logicalAnd_rightBegin(BinaryExpression andExpression) {
-    _conditionalEnd(andExpression.leftOperand);
+  void logicalAnd_rightBegin(Expression andExpression, Expression leftOperand) {
+    _conditionalEnd(leftOperand);
     // Tail of the stack: falseLeft, trueLeft
 
     var trueLeft = _stack.last;
     _current = trueLeft;
   }
 
-  void logicalNot_end(PrefixExpression notExpression) {
-    _conditionalEnd(notExpression.operand);
+  void logicalNot_end(Expression notExpression, Expression operand) {
+    _conditionalEnd(operand);
     var trueExpr = _stack.removeLast();
     var falseExpr = _stack.removeLast();
 
@@ -387,8 +417,8 @@
     _conditionFalse = trueExpr;
   }
 
-  void logicalOr_end(BinaryExpression orExpression) {
-    _conditionalEnd(orExpression.rightOperand);
+  void logicalOr_end(Expression orExpression, Expression rightOperand) {
+    _conditionalEnd(rightOperand);
     // Tail of the stack: falseLeft, trueLeft, falseRight, trueRight
 
     var trueRight = _stack.removeLast();
@@ -408,8 +438,8 @@
     _current = afterResult;
   }
 
-  void logicalOr_rightBegin(BinaryExpression orExpression) {
-    _conditionalEnd(orExpression.leftOperand);
+  void logicalOr_rightBegin(Expression orExpression, Expression leftOperand) {
+    _conditionalEnd(leftOperand);
     // Tail of the stack: falseLeft, trueLeft
 
     var falseLeft = _stack[_stack.length - 2];
@@ -418,12 +448,12 @@
 
   /// Retrieves the type that the [variable] is promoted to, if the [variable]
   /// is currently promoted.  Otherwise returns `null`.
-  T promotedType(VariableElement variable) {
+  Type promotedType(Element variable) {
     return _current.promoted[variable];
   }
 
   /// Register read of the given [variable] in the current state.
-  void read(LocalVariableElement variable) {
+  void read(Element variable) {
     if (_current.notAssigned.contains(variable)) {
       // Add to the list of violating variables, if not there yet.
       for (var i = 0; i < readBeforeWritten.length; ++i) {
@@ -440,11 +470,11 @@
   /// assigned in other cases that might target this with `continue`, so
   /// these variables might have different types and are "un-promoted" from
   /// the "afterExpression" state.
-  void switchStatement_beginCase(Set<VariableElement> notPromoted) {
+  void switchStatement_beginCase(Set<Element> notPromoted) {
     _current = _stack.last.removePromotedAll(notPromoted);
   }
 
-  void switchStatement_end(SwitchStatement node, bool hasDefault) {
+  void switchStatement_end(Statement switchStatement, bool hasDefault) {
     // Tail of the stack: break, continue, afterExpression
     var afterExpression = _current = _stack.removeLast();
     _stack.removeLast(); // continue
@@ -457,14 +487,14 @@
     }
   }
 
-  void switchStatement_expressionEnd(SwitchStatement node) {
-    _statementToStackIndex[node] = _stack.length;
+  void switchStatement_expressionEnd(Statement switchStatement) {
+    _statementToStackIndex[switchStatement] = _stack.length;
     _stack.add(_identity); // break
     _stack.add(_identity); // continue
     _stack.add(_current); // afterExpression
   }
 
-  void trueLiteral(BooleanLiteral expression) {
+  void trueLiteral(Expression expression) {
     _condition = expression;
     _conditionTrue = _current;
     _conditionFalse = _identity;
@@ -475,7 +505,7 @@
     // Tail of the stack: beforeBody
   }
 
-  void tryCatchStatement_bodyEnd(Set<VariableElement> assignedInBody) {
+  void tryCatchStatement_bodyEnd(Set<Element> assignedInBody) {
     var beforeBody = _stack.removeLast();
     var beforeCatch = beforeBody.removePromotedAll(assignedInBody);
     _stack.add(beforeCatch);
@@ -503,12 +533,17 @@
     _stack.add(_current); // beforeTry
   }
 
-  void tryFinallyStatement_end(Set<VariableElement> assignedInFinally) {
+  void tryFinallyStatement_end(Set<Element> assignedInFinally) {
     var afterBody = _stack.removeLast();
-    _current = _current.restrict(typeOperations, afterBody, assignedInFinally);
+    _current = _current.restrict(
+      typeOperations,
+      _emptySet,
+      afterBody,
+      assignedInFinally,
+    );
   }
 
-  void tryFinallyStatement_finallyBegin(Set<VariableElement> assignedInBody) {
+  void tryFinallyStatement_finallyBegin(Set<Element> assignedInBody) {
     var beforeTry = _stack.removeLast();
     var afterBody = _current;
     _stack.add(afterBody);
@@ -519,20 +554,21 @@
     assert(_stack.isEmpty);
   }
 
-  void whileStatement_bodyBegin(WhileStatement node) {
-    _conditionalEnd(node.condition);
+  void whileStatement_bodyBegin(
+      Statement whileStatement, Expression condition) {
+    _conditionalEnd(condition);
     // Tail of the stack: falseCondition, trueCondition
 
     var trueCondition = _stack.removeLast();
 
-    _statementToStackIndex[node] = _stack.length;
+    _statementToStackIndex[whileStatement] = _stack.length;
     _stack.add(_identity); // break
     _stack.add(_identity); // continue
 
     _current = trueCondition;
   }
 
-  void whileStatement_conditionBegin(Set<VariableElement> loopAssigned) {
+  void whileStatement_conditionBegin(Set<Element> loopAssigned) {
     _current = _current.removePromotedAll(loopAssigned);
   }
 
@@ -546,17 +582,16 @@
 
   /// Register write of the given [variable] in the current state.
   void write(
-    VariableElement variable, {
+    Element variable, {
     bool isNull = false,
     bool isNonNull = false,
   }) {
-    _current = _current.write(variable, isNull: isNull, isNonNull: isNonNull);
+    _current = _current.write(typeOperations, _emptySet, variable,
+        isNull: isNull, isNonNull: isNonNull);
   }
 
   void _conditionalEnd(Expression condition) {
-    while (condition is ParenthesizedExpression) {
-      condition = (condition as ParenthesizedExpression).expression;
-    }
+    condition = nodeOperations.unwrapParenthesized(condition);
     if (identical(condition, _condition)) {
       _stack.add(_conditionFalse);
       _stack.add(_conditionTrue);
@@ -566,7 +601,10 @@
     }
   }
 
-  _State<T> _join(_State<T> first, _State<T> second) {
+  _State<Element, Type> _join(
+    _State<Element, Type> first,
+    _State<Element, Type> second,
+  ) {
     if (identical(first, _identity)) return second;
     if (identical(second, _identity)) return first;
 
@@ -590,14 +628,14 @@
     );
   }
 
-  Map<VariableElement, T> _joinPromoted(
-    Map<VariableElement, T> first,
-    Map<VariableElement, T> second,
+  Map<Element, Type> _joinPromoted(
+    Map<Element, Type> first,
+    Map<Element, Type> second,
   ) {
     if (identical(first, second)) return first;
     if (first.isEmpty || second.isEmpty) return const {};
 
-    var result = <VariableElement, T>{};
+    var result = <Element, Type>{};
     var alwaysFirst = true;
     var alwaysSecond = true;
     for (var element in first.keys) {
@@ -627,32 +665,44 @@
   }
 }
 
+/// Accessor for function body information.
+abstract class FunctionBodyAccess<Element> {
+  bool isPotentiallyMutatedInClosure(Element variable);
+
+  bool isPotentiallyMutatedInScope(Element variable);
+}
+
+/// Operations on nodes, abstracted from concrete node interfaces.
+abstract class NodeOperations<Expression> {
+  /// If the [node] is a parenthesized expression, recursively unwrap it.
+  Expression unwrapParenthesized(Expression node);
+}
+
 /// Operations on types, abstracted from concrete type interfaces.
-abstract class TypeOperations<T> {
-  /// Return the static type of with the given [element].
-  T elementType(VariableElement element);
+abstract class TypeOperations<Element, Type> {
+  /// Return the static type of the given [element].
+  Type elementType(Element element);
+
+  /// Return `true` if the [element] is a local variable, not a parameter.
+  bool isLocalVariable(Element element);
 
   /// Return `true` if the [leftType] is a subtype of the [rightType].
-  bool isSubtypeOf(T leftType, T rightType);
+  bool isSubtypeOf(Type leftType, Type rightType);
 }
 
 /// List based immutable set of elements.
-class _ElementSet {
-  static final empty = _ElementSet._(
-    List<VariableElement>(0),
-  );
-
-  final List<VariableElement> elements;
+class _ElementSet<Element> {
+  final List<Element> elements;
 
   _ElementSet._(this.elements);
 
-  _ElementSet add(VariableElement addedElement) {
+  _ElementSet<Element> add(Element addedElement) {
     if (contains(addedElement)) {
       return this;
     }
 
     var length = elements.length;
-    var newElements = List<VariableElement>(length + 1);
+    var newElements = List<Element>(length + 1);
     for (var i = 0; i < length; ++i) {
       newElements[i] = elements[i];
     }
@@ -660,7 +710,7 @@
     return _ElementSet._(newElements);
   }
 
-  _ElementSet addAll(Iterable<VariableElement> elements) {
+  _ElementSet<Element> addAll(Iterable<Element> elements) {
     var result = this;
     for (var element in elements) {
       result = result.add(element);
@@ -668,7 +718,7 @@
     return result;
   }
 
-  bool contains(VariableElement element) {
+  bool contains(Element element) {
     var length = elements.length;
     for (var i = 0; i < length; ++i) {
       if (identical(elements[i], element)) {
@@ -678,7 +728,10 @@
     return false;
   }
 
-  _ElementSet intersect(_ElementSet other) {
+  _ElementSet<Element> intersect({
+    _ElementSet<Element> empty,
+    _ElementSet<Element> other,
+  }) {
     if (identical(other, empty)) return empty;
 
     // TODO(scheglov) optimize
@@ -689,7 +742,10 @@
     return _ElementSet._(newElements);
   }
 
-  _ElementSet remove(VariableElement removedElement) {
+  _ElementSet<Element> remove(
+    _ElementSet<Element> empty,
+    Element removedElement,
+  ) {
     if (!contains(removedElement)) {
       return this;
     }
@@ -699,7 +755,7 @@
       return empty;
     }
 
-    var newElements = List<VariableElement>(length - 1);
+    var newElements = List<Element>(length - 1);
     var newIndex = 0;
     for (var i = 0; i < length; ++i) {
       var element = elements[i];
@@ -711,7 +767,7 @@
     return _ElementSet._(newElements);
   }
 
-  _ElementSet union(_ElementSet other) {
+  _ElementSet<Element> union(_ElementSet<Element> other) {
     if (other.elements.isEmpty) {
       return this;
     }
@@ -726,12 +782,12 @@
   }
 }
 
-class _State<T> {
+class _State<Element, Type> {
   final bool reachable;
-  final _ElementSet notAssigned;
-  final _ElementSet notNullable;
-  final _ElementSet notNonNullable;
-  final Map<VariableElement, T> promoted;
+  final _ElementSet<Element> notAssigned;
+  final _ElementSet<Element> notNullable;
+  final _ElementSet<Element> notNonNullable;
+  final Map<Element, Type> promoted;
 
   _State(
     this.reachable,
@@ -742,7 +798,7 @@
   );
 
   /// Add a new [variable] to track definite assignment.
-  _State<T> add(VariableElement variable, {bool assigned: false}) {
+  _State<Element, Type> add(Element variable, {bool assigned: false}) {
     var newNotAssigned = assigned ? notAssigned : notAssigned.add(variable);
     var newNotNullable = notNullable.add(variable);
     var newNotNonNullable = notNonNullable.add(variable);
@@ -753,7 +809,7 @@
       return this;
     }
 
-    return _State<T>(
+    return _State<Element, Type>(
       reachable,
       newNotAssigned,
       newNotNullable,
@@ -762,20 +818,27 @@
     );
   }
 
-  _State<T> exit() {
-    return _State<T>(false, notAssigned, notNullable, notNonNullable, promoted);
+  _State<Element, Type> exit() {
+    return _State<Element, Type>(
+      false,
+      notAssigned,
+      notNullable,
+      notNonNullable,
+      promoted,
+    );
   }
 
-  _State<T> markNonNullable(VariableElement variable) {
+  _State<Element, Type> markNonNullable(
+      _ElementSet<Element> emptySet, Element variable) {
     var newNotNullable = notNullable.add(variable);
-    var newNotNonNullable = notNonNullable.remove(variable);
+    var newNotNonNullable = notNonNullable.remove(emptySet, variable);
 
     if (identical(newNotNullable, notNullable) &&
         identical(newNotNonNullable, notNonNullable)) {
       return this;
     }
 
-    return _State<T>(
+    return _State<Element, Type>(
       reachable,
       notAssigned,
       newNotNullable,
@@ -784,8 +847,9 @@
     );
   }
 
-  _State<T> markNullable(VariableElement variable) {
-    var newNotNullable = notNullable.remove(variable);
+  _State<Element, Type> markNullable(
+      _ElementSet<Element> emptySet, Element variable) {
+    var newNotNullable = notNullable.remove(emptySet, variable);
     var newNotNonNullable = notNonNullable.add(variable);
 
     if (identical(newNotNullable, notNullable) &&
@@ -793,7 +857,7 @@
       return this;
     }
 
-    return _State<T>(
+    return _State<Element, Type>(
       reachable,
       notAssigned,
       newNotNullable,
@@ -802,19 +866,19 @@
     );
   }
 
-  _State<T> promote(
-    TypeOperations<T> typeOperations,
-    VariableElement variable,
-    T type,
+  _State<Element, Type> promote(
+    TypeOperations<Element, Type> typeOperations,
+    Element variable,
+    Type type,
   ) {
     var previousType = promoted[variable];
     previousType ??= typeOperations.elementType(variable);
 
     if (typeOperations.isSubtypeOf(type, previousType) &&
         type != previousType) {
-      var newPromoted = <VariableElement, T>{}..addAll(promoted);
+      var newPromoted = <Element, Type>{}..addAll(promoted);
       newPromoted[variable] = type;
-      return _State<T>(
+      return _State<Element, Type>(
         reachable,
         notAssigned,
         notNullable,
@@ -826,7 +890,7 @@
     return this;
   }
 
-  _State<T> removePromotedAll(Set<VariableElement> variables) {
+  _State<Element, Type> removePromotedAll(Set<Element> variables) {
     var newNotNullable = notNullable.addAll(variables);
     var newNotNonNullable = notNonNullable.addAll(variables);
     var newPromoted = _removePromotedAll(promoted, variables);
@@ -835,7 +899,7 @@
         identical(newNotNonNullable, notNonNullable) &&
         identical(newPromoted, promoted)) return this;
 
-    return _State<T>(
+    return _State<Element, Type>(
       reachable,
       notAssigned,
       newNotNullable,
@@ -844,22 +908,26 @@
     );
   }
 
-  _State<T> restrict(
-    TypeOperations<T> typeOperations,
-    _State<T> other,
-    Set<VariableElement> unsafe,
+  _State<Element, Type> restrict(
+    TypeOperations<Element, Type> typeOperations,
+    _ElementSet<Element> emptySet,
+    _State<Element, Type> other,
+    Set<Element> unsafe,
   ) {
     var newReachable = reachable && other.reachable;
-    var newNotAssigned = notAssigned.intersect(other.notAssigned);
+    var newNotAssigned = notAssigned.intersect(
+      empty: emptySet,
+      other: other.notAssigned,
+    );
 
-    var newNotNullable = _ElementSet.empty;
+    var newNotNullable = emptySet;
     for (var variable in notNullable.elements) {
       if (unsafe.contains(variable) || other.notNullable.contains(variable)) {
         newNotNullable = newNotNullable.add(variable);
       }
     }
 
-    var newNotNonNullable = _ElementSet.empty;
+    var newNotNonNullable = emptySet;
     for (var variable in notNonNullable.elements) {
       if (unsafe.contains(variable) ||
           other.notNonNullable.contains(variable)) {
@@ -867,7 +935,7 @@
       }
     }
 
-    var newPromoted = <VariableElement, T>{};
+    var newPromoted = <Element, Type>{};
     for (var variable in promoted.keys) {
       var thisType = promoted[variable];
       if (!unsafe.contains(variable)) {
@@ -892,10 +960,10 @@
     );
   }
 
-  _State<T> setReachable(bool reachable) {
+  _State<Element, Type> setReachable(bool reachable) {
     if (this.reachable == reachable) return this;
 
-    return _State<T>(
+    return _State<Element, Type>(
       reachable,
       notAssigned,
       notNullable,
@@ -904,20 +972,23 @@
     );
   }
 
-  _State<T> write(
-    VariableElement variable, {
+  _State<Element, Type> write(
+    TypeOperations<Element, Type> typeOperations,
+    _ElementSet<Element> emptySet,
+    Element variable, {
     bool isNull = false,
     bool isNonNull = false,
   }) {
-    var newNotAssigned = variable is LocalVariableElement
-        ? notAssigned.remove(variable)
+    var newNotAssigned = typeOperations.isLocalVariable(variable)
+        ? notAssigned.remove(emptySet, variable)
         : notAssigned;
 
-    var newNotNullable =
-        isNull ? notNullable.remove(variable) : notNullable.add(variable);
+    var newNotNullable = isNull
+        ? notNullable.remove(emptySet, variable)
+        : notNullable.add(variable);
 
     var newNotNonNullable = isNonNull
-        ? notNonNullable.remove(variable)
+        ? notNonNullable.remove(emptySet, variable)
         : notNonNullable.add(variable);
 
     var newPromoted = _removePromoted(promoted, variable);
@@ -929,7 +1000,7 @@
       return this;
     }
 
-    return _State<T>(
+    return _State<Element, Type>(
       reachable,
       newNotAssigned,
       newNotNullable,
@@ -938,13 +1009,10 @@
     );
   }
 
-  Map<VariableElement, T> _removePromoted(
-    Map<VariableElement, T> map,
-    VariableElement variable,
-  ) {
+  Map<Element, Type> _removePromoted(Map<Element, Type> map, Element variable) {
     if (map.isEmpty) return const {};
 
-    var result = <VariableElement, T>{};
+    var result = <Element, Type>{};
     for (var key in map.keys) {
       if (!identical(key, variable)) {
         result[key] = map[key];
@@ -955,14 +1023,14 @@
     return result;
   }
 
-  Map<VariableElement, T> _removePromotedAll(
-    Map<VariableElement, T> map,
-    Set<VariableElement> variables,
+  Map<Element, Type> _removePromotedAll(
+    Map<Element, Type> map,
+    Set<Element> variables,
   ) {
     if (map.isEmpty) return const {};
     if (variables.isEmpty) return map;
 
-    var result = <VariableElement, T>{};
+    var result = <Element, Type>{};
     var noChanges = true;
     for (var key in map.keys) {
       if (variables.contains(key)) {
@@ -977,14 +1045,14 @@
     return result;
   }
 
-  static _State<T> _identicalOrNew<T>(
-    _State<T> first,
-    _State<T> second,
+  static _State<Element, Type> _identicalOrNew<Element, Type>(
+    _State<Element, Type> first,
+    _State<Element, Type> second,
     bool newReachable,
-    _ElementSet newNotAssigned,
-    _ElementSet newNotNullable,
-    _ElementSet newNotNonNullable,
-    Map<VariableElement, T> newPromoted,
+    _ElementSet<Element> newNotAssigned,
+    _ElementSet<Element> newNotNullable,
+    _ElementSet<Element> newNotNonNullable,
+    Map<Element, Type> newPromoted,
   ) {
     if (first.reachable == newReachable &&
         identical(first.notAssigned, newNotAssigned) &&
@@ -1001,7 +1069,7 @@
       return second;
     }
 
-    return _State<T>(
+    return _State<Element, Type>(
       newReachable,
       newNotAssigned,
       newNotNullable,
diff --git a/pkg/analyzer/lib/src/dart/resolver/legacy_type_asserter.dart b/pkg/analyzer/lib/src/dart/resolver/legacy_type_asserter.dart
new file mode 100644
index 0000000..cd09795
--- /dev/null
+++ b/pkg/analyzer/lib/src/dart/resolver/legacy_type_asserter.dart
@@ -0,0 +1,170 @@
+// Copyright (c) 2019, 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:collection';
+
+import 'package:analyzer/dart/analysis/features.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/visitor.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/type.dart';
+import 'package:analyzer/src/dart/element/type.dart';
+
+/// A visitor to assert that legacy libraries deal with legacy types.
+///
+/// Intended to be used via the static method
+/// [LegacyTypeAsserter.assertLegacyTypes], inside an `assert()` node.
+///
+/// Has a defense against being accidentally run outside of an assert statement,
+/// but that can be overridden if needed.
+///
+/// Checks that the static type of every node, as well as the elements of many
+/// nodes, have legacy types, and asserts that the legacy types are deep legacy
+/// types.
+class LegacyTypeAsserter extends GeneralizingAstVisitor {
+  // TODO(mfairhurst): remove custom equality/hashCode once both use nullability
+  Set<DartType> visitedTypes = LinkedHashSet<DartType>(
+      equals: (a, b) =>
+          a == b &&
+          (a as TypeImpl).nullabilitySuffix ==
+              (b as TypeImpl).nullabilitySuffix,
+      hashCode: (a) =>
+          a.hashCode * 11 + (a as TypeImpl).nullabilitySuffix.hashCode);
+
+  LegacyTypeAsserter({bool requireIsDebug = true}) {
+    if (requireIsDebug) {
+      bool isDebug = false;
+
+      assert(() {
+        isDebug = true;
+        return true;
+      }());
+
+      if (!isDebug) {
+        throw UnsupportedError(
+            'Legacy type asserter is being run outside of a debug environment');
+      }
+    }
+  }
+
+  @override
+  visitClassDeclaration(ClassDeclaration node) {
+    _assertLegacyType(node.declaredElement?.type);
+    super.visitClassDeclaration(node);
+  }
+
+  @override
+  visitClassMember(ClassMember node) {
+    final element = node.declaredElement;
+    if (element is ExecutableElement) {
+      _assertLegacyType(element?.type);
+    }
+    super.visitClassMember(node);
+  }
+
+  @override
+  visitCompilationUnit(CompilationUnit node) {
+    if (!node.featureSet.isEnabled(Feature.non_nullable)) {
+      super.visitCompilationUnit(node);
+    }
+  }
+
+  @override
+  visitDeclaredIdentifier(DeclaredIdentifier node) {
+    _assertLegacyType(node.declaredElement?.type);
+    super.visitDeclaredIdentifier(node);
+  }
+
+  @override
+  visitEnumDeclaration(EnumDeclaration node) {
+    _assertLegacyType(node.declaredElement?.type);
+    super.visitEnumDeclaration(node);
+  }
+
+  @override
+  visitExpression(Expression e) {
+    _assertLegacyType(e.staticType);
+    super.visitExpression(e);
+  }
+
+  @override
+  visitFormalParameter(FormalParameter node) {
+    _assertLegacyType(node.declaredElement?.type);
+    super.visitFormalParameter(node);
+  }
+
+  @override
+  visitFunctionDeclaration(FunctionDeclaration node) {
+    _assertLegacyType(node.declaredElement?.type);
+    super.visitFunctionDeclaration(node);
+  }
+
+  @override
+  visitTypeAnnotation(TypeAnnotation node) {
+    _assertLegacyType(node.type);
+    super.visitTypeAnnotation(node);
+  }
+
+  @override
+  visitTypeName(TypeName node) {
+    _assertLegacyType(node.type);
+    super.visitTypeName(node);
+  }
+
+  @override
+  visitVariableDeclaration(VariableDeclaration node) {
+    _assertLegacyType(node.declaredElement?.type);
+    super.visitVariableDeclaration(node);
+  }
+
+  void _assertLegacyType(DartType type) {
+    if (type == null) {
+      return;
+    }
+
+    if (type.isDynamic || type.isVoid) {
+      return;
+    }
+
+    if (type.isBottom && type.isDartCoreNull) {
+      // Never?, which is ok.
+      //
+      // Note: we could allow Null? and Null, but we really should be able to
+      // guarantee that we are only working with Null*, so that's what this
+      // currently does.
+      return;
+    }
+
+    if (visitedTypes.contains(type)) {
+      return;
+    }
+
+    visitedTypes.add(type);
+
+    if (type is TypeParameterType) {
+      _assertLegacyType(type.bound);
+    } else if (type is InterfaceType) {
+      type.typeArguments.forEach(_assertLegacyType);
+      type.typeParameters.map((param) => param.type).forEach(_assertLegacyType);
+    } else if (type is FunctionType) {
+      _assertLegacyType(type.returnType);
+      type.parameters.map((param) => param.type).forEach(_assertLegacyType);
+      type.typeArguments.forEach(_assertLegacyType);
+      type.typeParameters.map((param) => param.type).forEach(_assertLegacyType);
+    }
+
+    if ((type as TypeImpl).nullabilitySuffix == NullabilitySuffix.star) {
+      return;
+    }
+
+    throw StateError('Expected all legacy types, but got '
+        '${(type as TypeImpl).toString(withNullability: true)} '
+        '(${type.runtimeType})');
+  }
+
+  static bool assertLegacyTypes(CompilationUnit compilationUnit) {
+    new LegacyTypeAsserter().visitCompilationUnit(compilationUnit);
+    return true;
+  }
+}
diff --git a/pkg/analyzer/lib/src/error/codes.dart b/pkg/analyzer/lib/src/error/codes.dart
index 1b304fd..791135a 100644
--- a/pkg/analyzer/lib/src/error/codes.dart
+++ b/pkg/analyzer/lib/src/error/codes.dart
@@ -175,9 +175,9 @@
   // #### Common fixes
   //
   // There are two common ways to fix this problem. The first is to remove all
-  // of the spread elements of one kind or the other, so that the elements are
-  // consistent. In this case, that likely means removing the list (and
-  // deciding what to do about the now unused parameter):
+  // of the spread elements of one kind or another, so that the elements are
+  // consistent. In this case, that likely means removing the list and deciding
+  // what to do about the now unused parameter:
   //
   // ```dart
   // union(Map<String, String> a, List<String> b, Map<String, String> c) =>
@@ -207,16 +207,16 @@
    */
   // #### Description
   //
-  // Because map and set literals use the same delimiters (`‘{` and `}`), the
+  // Because map and set literals use the same delimiters (`{` and `}`), the
   // analyzer looks at the type arguments and the elements to determine which
   // kind of literal you meant. When there are no type arguments and all of the
   // elements are spread elements (which are allowed in both kinds of literals)
-  // then the analyzer uses the types of the expressions that are being spread
-  // to decide. If all of the expressions have the type `Iterable`, then it's a
-  // set literal, if they all have the type `Map`, then it's a map literal.
+  // then the analyzer uses the types of the expressions that are being spread.
+  // If all of the expressions have the type `Iterable`, then it's a set
+  // literal; if they all have the type `Map`, then it's a map literal.
   //
-  // This diagnostic is produced when none of the expressions being spread has a
-  // type that allows the analyzer to decide whether you were writing a map
+  // This diagnostic is produced when none of the expressions being spread have
+  // a type that allows the analyzer to decide whether you were writing a map
   // literal or a set literal.
   //
   // #### Example
@@ -277,7 +277,7 @@
       const CompileTimeErrorCode(
           'AMBIGUOUS_SET_OR_MAP_LITERAL_EITHER',
           "This literal must be either a map or a set, but the elements don't "
-              "have enough type information for type inference to work.",
+              "have enough information for type inference to work.",
           correction:
               "Try adding type arguments to the literal (one for sets, two "
               "for maps).");
@@ -942,7 +942,7 @@
   /**
    * No parameters.
    */
-  // #### Description
+  /* #### Description
   //
   // The analyzer produces this diagnostic when a named parameter has both the
   // `required` modifier and a default value. If the parameter is required, then
@@ -970,7 +970,7 @@
   //
   // ```dart
   // void log({String message = 'no message'}) {}
-  // ```
+  // ``` */
   static const CompileTimeErrorCode DEFAULT_VALUE_ON_REQUIRED_PARAMETER =
       const CompileTimeErrorCode('DEFAULT_VALUE_ON_REQUIRED_PARAMETER',
           "Required named parameters can't have a default value.",
@@ -1097,10 +1097,11 @@
   // var map = <String, int>{'a': 0, 'b': 1, !'c'!};
   // ```
   //
-  // #### Common fixes
+  // #### Common fix
   //
   // If the expression is intended to compute either a key or a value in an
-  // entry, fix the issue by completing the code:
+  // entry, fix the issue by replacing the expression with the key or the value.
+  // For example:
   //
   // ```dart
   // var map = <String, int>{'a': 0, 'b': 1, 'c': 2};
@@ -1882,7 +1883,7 @@
   /**
    * No parameters.
    */
-  // #### Description
+  /* #### Description
   //
   // The analyzer produces this diagnostic when an optional parameter doesn't
   // have a default value, but has a
@@ -1919,7 +1920,7 @@
   //
   // ```dart
   // void log({required String message}) {}
-  // ```
+  // ``` */
   static const CompileTimeErrorCode MISSING_DEFAULT_VALUE_FOR_PARAMETER =
       const CompileTimeErrorCode(
           'MISSING_DEFAULT_VALUE_FOR_PARAMETER',
@@ -2452,6 +2453,39 @@
           correction: "Try adding the missing arguments.");
 
   /**
+   * It is an error if a top level variable <cut> with potentially non-nullable
+   * type has no initializer expression <cut>.
+   *
+   * Parameters:
+   * 0: the name of the variable that is invalid
+   */
+  static const CompileTimeErrorCode
+      NOT_INITIALIZED_NON_NULLABLE_TOP_LEVEL_VARIABLE =
+      const CompileTimeErrorCode(
+          'NOT_INITIALIZED_NON_NULLABLE_TOP_LEVEL_VARIABLE',
+          "Non-nullable top-level variable '{0}' must be initialized.",
+          correction: "Try adding an initializer expression.");
+
+  /**
+   * It is an error if a potentially non-nullable local variable which has no
+   * initializer expression and is not marked `late` is used before it is
+   * definitely assigned.
+   *
+   * TODO(scheglov) Update the code and the message when implement definite
+   * assignment analysis.
+   *
+   * Parameters:
+   * 0: the name of the variable that is invalid
+   */
+  static const CompileTimeErrorCode
+      NOT_INITIALIZED_POTENTIALLY_NON_NULLABLE_LOCAL_VARIABLE =
+      const CompileTimeErrorCode(
+          'NOT_INITIALIZED_POTENTIALLY_NON_NULLABLE_LOCAL_VARIABLE',
+          "Non-nullable local variable '{0}' must be initialized.",
+          correction:
+              "Try giving it an initializer expression, or mark it 'late'.");
+
+  /**
    * No parameters.
    */
   // #### Description
@@ -2469,7 +2503,7 @@
   // var s = <String>{...m};
   // ```
   //
-  // #### Common fixes
+  // #### Common fix
   //
   // The most common fix is to replace the expression with one that produces an
   // iterable object:
@@ -2518,7 +2552,7 @@
   /**
    * No parameters.
    */
-  // #### Description
+  /* #### Description
   //
   // The analyzer produces this diagnostic when a class declaration uses an
   // extends clause to specify a superclass, and the type that's specified is a
@@ -2545,7 +2579,7 @@
   //
   // ```dart
   // class Invalid extends Duration {}
-  // ```
+  // ``` */
   static const CompileTimeErrorCode NULLABLE_TYPE_IN_EXTENDS_CLAUSE =
       const CompileTimeErrorCode('NULLABLE_TYPE_IN_EXTENDS_CLAUSE',
           "A class can't extend a nullable type.",
diff --git a/pkg/analyzer/lib/src/error/literal_element_verifier.dart b/pkg/analyzer/lib/src/error/literal_element_verifier.dart
index ba32987..1e9916c 100644
--- a/pkg/analyzer/lib/src/error/literal_element_verifier.dart
+++ b/pkg/analyzer/lib/src/error/literal_element_verifier.dart
@@ -146,11 +146,12 @@
     }
 
     InterfaceType iterableType;
-    var iterableObjectType = typeProvider.iterableObjectType;
+    var iterableDynamicType = (typeProvider.iterableDynamicType as TypeImpl)
+        .withNullability(NullabilitySuffix.question);
     if (expressionType is InterfaceTypeImpl &&
-        typeSystem.isSubtypeOf(expressionType, iterableObjectType)) {
+        typeSystem.isSubtypeOf(expressionType, iterableDynamicType)) {
       iterableType = expressionType.asInstanceOf(
-        iterableObjectType.element,
+        iterableDynamicType.element,
       );
     }
 
diff --git a/pkg/analyzer/lib/src/fasta/ast_builder.dart b/pkg/analyzer/lib/src/fasta/ast_builder.dart
index 73b6a7c..955d11e 100644
--- a/pkg/analyzer/lib/src/fasta/ast_builder.dart
+++ b/pkg/analyzer/lib/src/fasta/ast_builder.dart
@@ -198,10 +198,14 @@
     debugEvent("ExtensionHeader");
 
     TypeParameterList typeParameters = pop();
-    SimpleIdentifier name = pop();
     List<Annotation> metadata = pop();
     Comment comment = _findComment(metadata, extensionKeyword);
 
+    SimpleIdentifier name;
+    if (nameToken != null) {
+      name = ast.simpleIdentifier(nameToken);
+    }
+
     extensionDeclaration = ast.extensionDeclaration(
       comment: comment,
       metadata: metadata,
@@ -323,6 +327,116 @@
     }
   }
 
+  ConstructorInitializer buildInitializer(Object initializerObject) {
+    if (initializerObject is FunctionExpressionInvocation) {
+      Expression function = initializerObject.function;
+      if (function is SuperExpression) {
+        return ast.superConstructorInvocation(
+            function.superKeyword, null, null, initializerObject.argumentList);
+      } else {
+        return ast.redirectingConstructorInvocation(
+            (function as ThisExpression).thisKeyword,
+            null,
+            null,
+            initializerObject.argumentList);
+      }
+    }
+
+    if (initializerObject is MethodInvocation) {
+      Expression target = initializerObject.target;
+      if (target is SuperExpression) {
+        return ast.superConstructorInvocation(
+            target.superKeyword,
+            initializerObject.operator,
+            initializerObject.methodName,
+            initializerObject.argumentList);
+      }
+      if (target is ThisExpression) {
+        return ast.redirectingConstructorInvocation(
+            target.thisKeyword,
+            initializerObject.operator,
+            initializerObject.methodName,
+            initializerObject.argumentList);
+      }
+      return buildInitializerTargetExpressionRecovery(
+          target, initializerObject);
+    }
+
+    if (initializerObject is PropertyAccess) {
+      return buildInitializerTargetExpressionRecovery(
+          initializerObject.target, initializerObject);
+    }
+
+    if (initializerObject is AssignmentExpression) {
+      Token thisKeyword;
+      Token period;
+      SimpleIdentifier fieldName;
+      Expression left = initializerObject.leftHandSide;
+      if (left is PropertyAccess) {
+        Expression target = left.target;
+        if (target is ThisExpression) {
+          thisKeyword = target.thisKeyword;
+          period = left.operator;
+        } else {
+          assert(target is SuperExpression);
+          // Recovery:
+          // Parser has reported FieldInitializedOutsideDeclaringClass.
+        }
+        fieldName = left.propertyName;
+      } else if (left is SimpleIdentifier) {
+        fieldName = left;
+      } else {
+        // Recovery:
+        // Parser has reported invalid assignment.
+        SuperExpression superExpression = left;
+        fieldName = ast.simpleIdentifier(superExpression.superKeyword);
+      }
+      return ast.constructorFieldInitializer(thisKeyword, period, fieldName,
+          initializerObject.operator, initializerObject.rightHandSide);
+    }
+
+    if (initializerObject is AssertInitializer) {
+      return initializerObject;
+    }
+
+    if (initializerObject is IndexExpression) {
+      return buildInitializerTargetExpressionRecovery(
+          initializerObject.target, initializerObject);
+    }
+
+    throw new UnsupportedError('unsupported initializer:'
+        ' ${initializerObject.runtimeType} :: $initializerObject');
+  }
+
+  AstNode buildInitializerTargetExpressionRecovery(
+      Expression target, Object initializerObject) {
+    if (target is FunctionExpressionInvocation) {
+      Expression targetFunct = target.function;
+      if (targetFunct is SuperExpression) {
+        // TODO(danrubel): Consider generating this error in the parser
+        // This error is also reported in the body builder
+        handleRecoverableError(messageInvalidSuperInInitializer,
+            targetFunct.superKeyword, targetFunct.superKeyword);
+        return ast.superConstructorInvocation(
+            targetFunct.superKeyword, null, null, target.argumentList);
+      }
+      if (targetFunct is ThisExpression) {
+        // TODO(danrubel): Consider generating this error in the parser
+        // This error is also reported in the body builder
+        handleRecoverableError(messageInvalidThisInInitializer,
+            targetFunct.thisKeyword, targetFunct.thisKeyword);
+        return ast.redirectingConstructorInvocation(
+            targetFunct.thisKeyword, null, null, target.argumentList);
+      }
+      throw new UnsupportedError('unsupported initializer:'
+          ' ${initializerObject.runtimeType} :: $initializerObject'
+          ' %% targetFunct : ${targetFunct.runtimeType} :: $targetFunct');
+    }
+    throw new UnsupportedError('unsupported initializer:'
+        ' ${initializerObject.runtimeType} :: $initializerObject'
+        ' %% target : ${target.runtimeType} :: $target');
+  }
+
   void checkFieldFormalParameters(FormalParameterList parameters) {
     if (parameters?.parameters != null) {
       parameters.parameters.forEach((FormalParameter param) {
@@ -1217,122 +1331,12 @@
 
     var initializers = <ConstructorInitializer>[];
     for (Object initializerObject in initializerObjects) {
-      if (initializerObject is FunctionExpressionInvocation) {
-        Expression function = initializerObject.function;
-        if (function is SuperExpression) {
-          initializers.add(ast.superConstructorInvocation(function.superKeyword,
-              null, null, initializerObject.argumentList));
-        } else {
-          initializers.add(ast.redirectingConstructorInvocation(
-              (function as ThisExpression).thisKeyword,
-              null,
-              null,
-              initializerObject.argumentList));
-        }
-      } else if (initializerObject is MethodInvocation) {
-        Expression target = initializerObject.target;
-        if (target is SuperExpression) {
-          initializers.add(ast.superConstructorInvocation(
-              target.superKeyword,
-              initializerObject.operator,
-              initializerObject.methodName,
-              initializerObject.argumentList));
-        } else if (target is ThisExpression) {
-          initializers.add(ast.redirectingConstructorInvocation(
-              target.thisKeyword,
-              initializerObject.operator,
-              initializerObject.methodName,
-              initializerObject.argumentList));
-        } else {
-          // Recovery: Invalid initializer
-          if (target is FunctionExpressionInvocation) {
-            var targetFunct = target.function;
-            if (targetFunct is SuperExpression) {
-              initializers.add(ast.superConstructorInvocation(
-                  targetFunct.superKeyword, null, null, target.argumentList));
-              // TODO(danrubel): Consider generating this error in the parser
-              // This error is also reported in the body builder
-              handleRecoverableError(messageInvalidSuperInInitializer,
-                  targetFunct.superKeyword, targetFunct.superKeyword);
-            } else if (targetFunct is ThisExpression) {
-              initializers.add(ast.redirectingConstructorInvocation(
-                  targetFunct.thisKeyword, null, null, target.argumentList));
-              // TODO(danrubel): Consider generating this error in the parser
-              // This error is also reported in the body builder
-              handleRecoverableError(messageInvalidThisInInitializer,
-                  targetFunct.thisKeyword, targetFunct.thisKeyword);
-            } else {
-              throw new UnsupportedError(
-                  'unsupported initializer $initializerObject');
-            }
-          } else {
-            throw new UnsupportedError(
-                'unsupported initializer $initializerObject');
-          }
-        }
-      } else if (initializerObject is AssignmentExpression) {
-        Token thisKeyword;
-        Token period;
-        SimpleIdentifier fieldName;
-        Expression left = initializerObject.leftHandSide;
-        if (left is PropertyAccess) {
-          Expression target = left.target;
-          if (target is ThisExpression) {
-            thisKeyword = target.thisKeyword;
-            period = left.operator;
-          } else {
-            assert(target is SuperExpression);
-            // Recovery:
-            // Parser has reported FieldInitializedOutsideDeclaringClass.
-          }
-          fieldName = left.propertyName;
-        } else if (left is SimpleIdentifier) {
-          fieldName = left;
-        } else {
-          // Recovery:
-          // Parser has reported invalid assignment.
-          SuperExpression superExpression = left;
-          fieldName = ast.simpleIdentifier(superExpression.superKeyword);
-        }
-        initializers.add(ast.constructorFieldInitializer(
-            thisKeyword,
-            period,
-            fieldName,
-            initializerObject.operator,
-            initializerObject.rightHandSide));
-      } else if (initializerObject is AssertInitializer) {
-        initializers.add(initializerObject);
-      } else if (initializerObject is PropertyAccess) {
-        // Recovery: Invalid initializer
-        Expression target = initializerObject.target;
-        if (target is FunctionExpressionInvocation) {
-          var targetFunct = target.function;
-          if (targetFunct is SuperExpression) {
-            initializers.add(ast.superConstructorInvocation(
-                targetFunct.superKeyword, null, null, target.argumentList));
-            // TODO(danrubel): Consider generating this error in the parser
-            // This error is also reported in the body builder
-            handleRecoverableError(messageInvalidSuperInInitializer,
-                targetFunct.superKeyword, targetFunct.superKeyword);
-          } else if (targetFunct is ThisExpression) {
-            initializers.add(ast.redirectingConstructorInvocation(
-                targetFunct.thisKeyword, null, null, target.argumentList));
-            // TODO(danrubel): Consider generating this error in the parser
-            // This error is also reported in the body builder
-            handleRecoverableError(messageInvalidThisInInitializer,
-                targetFunct.thisKeyword, targetFunct.thisKeyword);
-          } else {
-            throw new UnsupportedError(
-                'unsupported initializer $initializerObject');
-          }
-        } else {
-          throw new UnsupportedError(
-              'unsupported initializer $initializerObject');
-        }
-      } else {
+      ConstructorInitializer initializer = buildInitializer(initializerObject);
+      if (initializer == null) {
         throw new UnsupportedError('unsupported initializer:'
             ' ${initializerObject.runtimeType} :: $initializerObject');
       }
+      initializers.add(initializer);
     }
 
     push(initializers);
@@ -2478,7 +2482,8 @@
   }
 
   void handleIndexedExpression(Token leftBracket, Token rightBracket) {
-    assert(optional('[', leftBracket));
+    assert(optional('[', leftBracket) ||
+        (enableNonNullable && optional('?.[', leftBracket)));
     assert(optional(']', rightBracket));
     debugEvent("IndexedExpression");
 
diff --git a/pkg/analyzer/lib/src/generated/element_resolver.dart b/pkg/analyzer/lib/src/generated/element_resolver.dart
index 27097db..22baf4d 100644
--- a/pkg/analyzer/lib/src/generated/element_resolver.dart
+++ b/pkg/analyzer/lib/src/generated/element_resolver.dart
@@ -169,7 +169,7 @@
         if (_shouldReportInvalidMember(staticType, staticMethod)) {
           _recordUndefinedToken(
               staticType.element,
-              StaticTypeWarningCode.UNDEFINED_METHOD,
+              StaticTypeWarningCode.UNDEFINED_OPERATOR,
               operator,
               [methodName, staticType.displayName]);
         }
@@ -1743,14 +1743,10 @@
 
   /**
    * Return `true` if we should report an error for a [member] lookup that found
-   * no match on the given [type], or accessing a [member] on a nullable type.
+   * no match on the given [type].
    */
-  bool _shouldReportInvalidMember(DartType type, Element member) {
-    return type != null &&
-        member == null &&
-        !type.isDynamic &&
-        !type.isDartCoreNull;
-  }
+  bool _shouldReportInvalidMember(DartType type, Element member) =>
+      type != null && member == null && !type.isDynamic;
 
   /**
    * Checks whether the given [expression] is a reference to a class. If it is
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index 93bc24d..4b370a9 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -34,6 +34,7 @@
 import 'package:analyzer/src/generated/resolver.dart';
 import 'package:analyzer/src/generated/sdk.dart' show DartSdk, SdkLibrary;
 import 'package:analyzer/src/generated/source.dart';
+import 'package:meta/meta.dart';
 
 /**
  * A visitor used to traverse an AST structure looking for additional errors and
@@ -369,15 +370,15 @@
 
   @override
   void visitAssertInitializer(AssertInitializer node) {
-    _checkForNonBoolExpression(node);
-    _checkForNullableDereference(node.condition);
+    _checkForNonBoolExpression(node.condition,
+        errorCode: StaticTypeWarningCode.NON_BOOL_EXPRESSION);
     super.visitAssertInitializer(node);
   }
 
   @override
   void visitAssertStatement(AssertStatement node) {
-    _checkForNonBoolExpression(node);
-    _checkForNullableDereference(node.condition);
+    _checkForNonBoolExpression(node.condition,
+        errorCode: StaticTypeWarningCode.NON_BOOL_EXPRESSION);
     super.visitAssertStatement(node);
   }
 
@@ -418,8 +419,6 @@
       _checkForAssignability(node.rightOperand, _boolType,
           StaticTypeWarningCode.NON_BOOL_OPERAND, [lexeme]);
       _checkForUseOfVoidResult(node.rightOperand);
-      _checkForNullableDereference(node.leftOperand);
-      _checkForNullableDereference(node.rightOperand);
     } else if (type == TokenType.EQ_EQ || type == TokenType.BANG_EQ) {
       _checkForArgumentTypeNotAssignableForArgument(node.rightOperand,
           promoteParameterToNullable: true);
@@ -1168,12 +1167,14 @@
     Expression operand = node.operand;
     if (operatorType == TokenType.BANG) {
       _checkForNonBoolNegationExpression(operand);
-    } else if (operatorType.isIncrementOperator) {
-      _checkForAssignmentToFinal(operand);
+    } else {
+      if (operatorType.isIncrementOperator) {
+        _checkForAssignmentToFinal(operand);
+      }
+      _checkForNullableDereference(operand);
+      _checkForUseOfVoidResult(operand);
+      _checkForIntNotAssignable(operand);
     }
-    _checkForIntNotAssignable(operand);
-    _checkForNullableDereference(operand);
-    _checkForUseOfVoidResult(operand);
     super.visitPrefixExpression(node);
   }
 
@@ -1293,6 +1294,14 @@
   }
 
   @override
+  void visitSpreadElement(SpreadElement node) {
+    if (node.spreadOperator.type != TokenType.PERIOD_PERIOD_PERIOD_QUESTION) {
+      _checkForNullableDereference(node.expression);
+    }
+    super.visitSpreadElement(node);
+  }
+
+  @override
   void visitSuperConstructorInvocation(SuperConstructorInvocation node) {
     DartType type =
         resolutionMap.staticElementForConstructorReference(node)?.type;
@@ -1344,6 +1353,7 @@
   @override
   void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
     _checkForFinalNotInitialized(node.variables);
+    _checkForNotInitializedNonNullableTopLevelVariable(node.variables);
     super.visitTopLevelVariableDeclaration(node);
   }
 
@@ -1430,6 +1440,7 @@
   @override
   void visitVariableDeclarationStatement(VariableDeclarationStatement node) {
     _checkForFinalNotInitialized(node.variables);
+    _checkForNotInitializedPotentiallyNonNullableLocalVariable(node.variables);
     super.visitVariableDeclarationStatement(node);
   }
 
@@ -3358,6 +3369,75 @@
     }
   }
 
+  void _checkForNotInitializedPotentiallyNonNullableLocalVariable(
+    VariableDeclarationList node,
+  ) {
+    // Const and final checked separately.
+    if (node.isConst || node.isFinal) {
+      return;
+    }
+
+    if (!_isNonNullable) {
+      return;
+    }
+
+    if (node.isLate) {
+      return;
+    }
+
+    if (node.type == null) {
+      return;
+    }
+    var type = node.type.type;
+
+    if (!_typeSystem.isPotentiallyNonNullable(type)) {
+      return;
+    }
+
+    for (var variable in node.variables) {
+      if (variable.initializer == null) {
+        _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode
+              .NOT_INITIALIZED_POTENTIALLY_NON_NULLABLE_LOCAL_VARIABLE,
+          variable.name,
+          [variable.name.name],
+        );
+      }
+    }
+  }
+
+  void _checkForNotInitializedNonNullableTopLevelVariable(
+    VariableDeclarationList node,
+  ) {
+    // Const and final checked separately.
+    if (node.isConst || node.isFinal) {
+      return;
+    }
+
+    if (!_isNonNullable) {
+      return;
+    }
+
+    if (node.type == null) {
+      return;
+    }
+    var type = node.type.type;
+
+    if (!_typeSystem.isPotentiallyNonNullable(type)) {
+      return;
+    }
+
+    for (var variable in node.variables) {
+      if (variable.initializer == null) {
+        _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.NOT_INITIALIZED_NON_NULLABLE_TOP_LEVEL_VARIABLE,
+          variable.name,
+          [variable.name.name],
+        );
+      }
+    }
+  }
+
   /**
    * If there are no constructors in the given [members], verify that all
    * final fields are initialized.  Cases in which there is at least one
@@ -4561,38 +4641,25 @@
    * See [StaticTypeWarningCode.NON_BOOL_CONDITION].
    */
   void _checkForNonBoolCondition(Expression condition) {
-    DartType conditionType = getStaticType(condition);
-    if (!_checkForNullableDereference(condition) &&
-        !_checkForUseOfVoidResult(condition) &&
-        conditionType != null &&
-        !_typeSystem.isAssignableTo(conditionType, _boolType,
-            featureSet: _featureSet)) {
-      _errorReporter.reportErrorForNode(
-          StaticTypeWarningCode.NON_BOOL_CONDITION, condition);
-    }
+    _checkForNonBoolExpression(condition,
+        errorCode: StaticTypeWarningCode.NON_BOOL_CONDITION);
   }
 
   /**
-   * Verify that the given [assertion] has either a 'bool' or '() -> bool'
-   * condition.
+   * Verify that the given [expression] is of type 'bool', and report
+   * [errorCode] if not, or a nullability error if its improperly nullable.
    */
-  void _checkForNonBoolExpression(Assertion assertion) {
-    Expression expression = assertion.condition;
+  void _checkForNonBoolExpression(Expression expression,
+      {@required ErrorCode errorCode}) {
     DartType type = getStaticType(expression);
-    if (type is InterfaceType) {
-      if (!_typeSystem.isAssignableTo(type, _boolType,
-          featureSet: _featureSet)) {
-        if (type.element == _boolType.element) {
-          _errorReporter.reportErrorForNode(
-              StaticWarningCode.UNCHECKED_USE_OF_NULLABLE_VALUE, expression);
-        } else {
-          _errorReporter.reportErrorForNode(
-              StaticTypeWarningCode.NON_BOOL_EXPRESSION, expression);
-        }
+    if (!_checkForUseOfVoidResult(expression) &&
+        !_typeSystem.isAssignableTo(type, _boolType, featureSet: _featureSet)) {
+      if (type.element == _boolType.element) {
+        _errorReporter.reportErrorForNode(
+            StaticWarningCode.UNCHECKED_USE_OF_NULLABLE_VALUE, expression);
+      } else {
+        _errorReporter.reportErrorForNode(errorCode, expression);
       }
-    } else if (type is FunctionType) {
-      _errorReporter.reportErrorForNode(
-          StaticTypeWarningCode.NON_BOOL_EXPRESSION, expression);
     }
   }
 
@@ -4600,18 +4667,8 @@
    * Checks to ensure that the given [expression] is assignable to bool.
    */
   void _checkForNonBoolNegationExpression(Expression expression) {
-    DartType conditionType = getStaticType(expression);
-    if (conditionType != null &&
-        !_typeSystem.isAssignableTo(conditionType, _boolType,
-            featureSet: _featureSet)) {
-      if (conditionType.element == _boolType.element) {
-        _errorReporter.reportErrorForNode(
-            StaticWarningCode.UNCHECKED_USE_OF_NULLABLE_VALUE, expression);
-      } else {
-        _errorReporter.reportErrorForNode(
-            StaticTypeWarningCode.NON_BOOL_NEGATION_EXPRESSION, expression);
-      }
-    }
+    _checkForNonBoolExpression(expression,
+        errorCode: StaticTypeWarningCode.NON_BOOL_NEGATION_EXPRESSION);
   }
 
   /**
diff --git a/pkg/analyzer/lib/src/generated/testing/ast_test_factory.dart b/pkg/analyzer/lib/src/generated/testing/ast_test_factory.dart
index 469e29c..9237339 100644
--- a/pkg/analyzer/lib/src/generated/testing/ast_test_factory.dart
+++ b/pkg/analyzer/lib/src/generated/testing/ast_test_factory.dart
@@ -2,6 +2,7 @@
 // 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 'package:analyzer/dart/analysis/features.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/ast/standard_ast_factory.dart';
 import 'package:analyzer/dart/ast/token.dart';
@@ -287,6 +288,22 @@
           endToken: TokenFactory.tokenFromType(TokenType.EOF),
           featureSet: null);
 
+  static CompilationUnit compilationUnit9(
+          {String scriptTag,
+          List<Directive> directives,
+          List<CompilationUnitMember> declarations,
+          FeatureSet featureSet}) =>
+      astFactory.compilationUnit2(
+          beginToken: TokenFactory.tokenFromType(TokenType.EOF),
+          scriptTag:
+              scriptTag == null ? null : AstTestFactory.scriptTag(scriptTag),
+          directives: directives == null ? new List<Directive>() : directives,
+          declarations: declarations == null
+              ? new List<CompilationUnitMember>()
+              : declarations,
+          endToken: TokenFactory.tokenFromType(TokenType.EOF),
+          featureSet: featureSet);
+
   static ConditionalExpression conditionalExpression(Expression condition,
           Expression thenExpression, Expression elseExpression) =>
       astFactory.conditionalExpression(
diff --git a/pkg/analyzer/lib/src/generated/type_system.dart b/pkg/analyzer/lib/src/generated/type_system.dart
index 7b41701..53d06e1 100644
--- a/pkg/analyzer/lib/src/generated/type_system.dart
+++ b/pkg/analyzer/lib/src/generated/type_system.dart
@@ -33,7 +33,8 @@
  * This is expressed by their topiness (higher = more toppy).
  */
 int _getTopiness(DartType t) {
-  assert(_isTop(t), 'only Top types have a topiness');
+  // TODO(mfairhurst): switch legacy Top checks to true Top checks
+  assert(_isLegacyTop(t, orTrueTop: true), 'only Top types have a topiness');
 
   // Highest top
   if (t.isVoid) return 3;
@@ -50,13 +51,26 @@
 }
 
 bool _isBottom(DartType t) {
-  return t.isBottom ||
-      // TODO(mfairhurst): Remove the exception treating Null* as Top.
-      t.isDartCoreNull &&
-          (t as TypeImpl).nullabilitySuffix == NullabilitySuffix.star ||
+  return (t.isBottom &&
+          (t as TypeImpl).nullabilitySuffix != NullabilitySuffix.question) ||
       identical(t, UnknownInferredType.instance);
 }
 
+/// Is [t] the bottom of the legacy type hierarchy.
+bool _isLegacyBottom(DartType t, {@required bool orTrueBottom}) {
+  return (t.isBottom &&
+          (t as TypeImpl).nullabilitySuffix == NullabilitySuffix.question) ||
+      t.isDartCoreNull ||
+      (orTrueBottom ? _isBottom(t) : false);
+}
+
+/// Is [t] the top of the legacy type hierarch.
+bool _isLegacyTop(DartType t, {@required bool orTrueTop}) =>
+// TODO(mfairhurst): handle FutureOr<LegacyTop> cases, with tests.
+    (t.isObject &&
+        (t as TypeImpl).nullabilitySuffix == NullabilitySuffix.none) ||
+    (orTrueTop ? _isTop(t) : false);
+
 bool _isTop(DartType t) {
   if (t.isDartAsyncFutureOr) {
     return _isTop((t as InterfaceType).typeArguments[0]);
@@ -141,16 +155,23 @@
 
     // For the purpose of GLB, we say some Tops are subtypes (less toppy) than
     // the others. Return the least toppy.
-    if (_isTop(type1) && _isTop(type2)) {
+    // TODO(mfairhurst): switch legacy Top checks to true Top checks
+    if (_isLegacyTop(type1, orTrueTop: true) &&
+        _isLegacyTop(type2, orTrueTop: true)) {
       return _getTopiness(type1) < _getTopiness(type2) ? type1 : type2;
     }
 
     // The GLB of top and any type is just that type.
     // Also GLB of bottom and any type is bottom.
-    if (_isTop(type1) || _isBottom(type2)) {
+    // TODO(mfairhurst): switch legacy Top checks to true Top checks
+    // TODO(mfairhurst): switch legacy Bottom checks to true Bottom checks.
+    if (_isLegacyTop(type1, orTrueTop: true) ||
+        _isLegacyBottom(type2, orTrueBottom: true)) {
       return type2;
     }
-    if (_isTop(type2) || _isBottom(type1)) {
+    // TODO(mfairhurst): switch legacy Bottom checks to true Bottom checks
+    if (_isLegacyTop(type2, orTrueTop: true) ||
+        _isLegacyBottom(type1, orTrueBottom: true)) {
       return type1;
     }
 
@@ -515,13 +536,17 @@
     if (t is TypeParameterType) {
       return false;
     }
-    if (_isTop(t)) {
+    // TODO(mfairhurst): switch legacy Top checks to true Top checks
+    if (_isLegacyTop(t, orTrueTop: true)) {
       return true;
     }
 
     if (t is FunctionType) {
-      if (!_isTop(t.returnType) ||
-          anyParameterType(t, (pt) => !_isBottom(pt))) {
+      // TODO(mfairhurst): switch legacy Top checks to true Top checks
+      // TODO(mfairhurst): switch legacy Bottom checks to true Bottom checks
+      if (!_isLegacyTop(t.returnType, orTrueTop: true) ||
+          anyParameterType(
+              t, (pt) => !_isLegacyBottom(pt, orTrueBottom: true))) {
         return false;
       } else {
         return true;
@@ -531,7 +556,8 @@
     if (t is InterfaceType) {
       List<DartType> typeArguments = t.typeArguments;
       for (DartType typeArgument in typeArguments) {
-        if (!_isTop(typeArgument)) return false;
+        // TODO(mfairhurst): switch legacy Top checks to true Top checks
+        if (!_isLegacyTop(typeArgument, orTrueTop: true)) return false;
       }
       return true;
     }
@@ -577,6 +603,14 @@
     var t1 = _t1 as TypeImpl;
     var t2 = _t2 as TypeImpl;
 
+    // Convert Null to Never? so that NullabilitySuffix can handle more cases.
+    if (t1.isDartCoreNull) {
+      t1 = BottomTypeImpl.instanceNullable;
+    }
+    if (t2.isDartCoreNull) {
+      t2 = BottomTypeImpl.instanceNullable;
+    }
+
     if (identical(t1, t2)) {
       return true;
     }
@@ -585,25 +619,26 @@
     //
     // We proceed by eliminating these different classes from consideration.
 
-    // Trivially true.
-    //
-    // Note that `?` is treated as a top and a bottom type during inference,
-    // so it's also covered here.
-    if (_isTop(t2) || _isBottom(t1)) {
+    // `?` is treated as a top and a bottom type during inference.
+    if (identical(t1, UnknownInferredType.instance) ||
+        identical(t2, UnknownInferredType.instance)) {
       return true;
     }
 
-    // Trivially false.
-    if (_isTop(t1) || _isBottom(t2)) {
-      return false;
+    // Trivial top case.
+    if (_isTop(t2)) {
+      return true;
     }
 
-    // TODO(mfairhurst): Convert Null to Never?, to simplify the algorithm, and
-    // remove this check.
-    if (t1.isDartCoreNull) {
-      if (t2.nullabilitySuffix != NullabilitySuffix.none) {
-        return true;
-      }
+    // Legacy top case. Must be done now to find Object* <: Object.
+    if (t1.nullabilitySuffix == NullabilitySuffix.star &&
+        _isLegacyTop(t2, orTrueTop: false)) {
+      return true;
+    }
+
+    // Having excluded RHS top, this now must be false.
+    if (_isTop(t1)) {
+      return false;
     }
 
     // Handle T1? <: T2
@@ -614,16 +649,23 @@
           return false;
         }
 
-        // T1? <: FutureOr<T2> is true if T2 is S2?.
-        // TODO(mfairhurst): handle T1? <: FutureOr<dynamic>, etc.
-        if (t2 is InterfaceTypeImpl &&
-            (t2.typeArguments[0] as TypeImpl).nullabilitySuffix ==
-                NullabilitySuffix.none) {
+        // T1? <: FutureOr<S2> is true if S2 is nullable.
+        final s2 = (t2 as InterfaceType).typeArguments[0];
+        if (!isNullable(s2)) {
           return false;
         }
       }
     }
 
+    // Legacy bottom cases
+    if (_isLegacyBottom(t1, orTrueBottom: true)) {
+      return true;
+    }
+
+    if (_isLegacyBottom(t2, orTrueBottom: true)) {
+      return false;
+    }
+
     // Handle FutureOr<T> union type.
     if (t1 is InterfaceTypeImpl && t1.isDartAsyncFutureOr) {
       var t1TypeArg = t1.typeArguments[0];
@@ -1101,8 +1143,11 @@
       }
 
       return new FunctionTypeImpl.synthetic(
-          newReturnType, type.typeFormals, newParameters,
-          nullabilitySuffix: (type as TypeImpl).nullabilitySuffix);
+        newReturnType,
+        type.typeFormals,
+        newParameters,
+        nullabilitySuffix: (type as TypeImpl).nullabilitySuffix,
+      );
     }
     return type;
   }
@@ -1775,7 +1820,10 @@
       return false;
     }
 
-    if (_isBottom(t1) || _isTop(t2)) return true;
+    // TODO(mfairhurst): switch legacy Bottom checks to true Bottom checks
+    // TODO(mfairhurst): switch legacy Top checks to true Top checks
+    if (_isLegacyBottom(t1, orTrueBottom: true) ||
+        _isLegacyTop(t2, orTrueTop: true)) return true;
 
     if (t1 is InterfaceType && t2 is InterfaceType) {
       return _matchInterfaceSubtypeOf(t1, t2, visited, origin,
@@ -1955,16 +2003,24 @@
 
     // For the purpose of LUB, we say some Tops are subtypes (less toppy) than
     // the others. Return the most toppy.
-    if (_isTop(type1) && _isTop(type2)) {
+    // TODO(mfairhurst): switch legacy Top checks to true Top checks
+    if (_isLegacyTop(type1, orTrueTop: true) &&
+        _isLegacyTop(type2, orTrueTop: true)) {
       return _getTopiness(type1) > _getTopiness(type2) ? type1 : type2;
     }
 
     // The least upper bound of top and any type T is top.
     // The least upper bound of bottom and any type T is T.
-    if (_isTop(type1) || _isBottom(type2)) {
+    // TODO(mfairhurst): switch legacy Top checks to true Top checks
+    // TODO(mfairhurst): switch legacy Bottom checks to true Bottom checks
+    if (_isLegacyTop(type1, orTrueTop: true) ||
+        _isLegacyBottom(type2, orTrueBottom: true)) {
       return type1;
     }
-    if (_isTop(type2) || _isBottom(type1)) {
+    // TODO(mfairhurst): switch legacy Top checks to true Top checks
+    // TODO(mfairhurst): switch legacy Bottom checks to true Bottom checks
+    if (_isLegacyTop(type2, orTrueTop: true) ||
+        _isLegacyBottom(type1, orTrueBottom: true)) {
       return type2;
     }
 
@@ -2051,9 +2107,13 @@
       return false;
     } else if (type.isDartAsyncFutureOr) {
       isNonNullable((type as InterfaceType).typeArguments[0]);
+    } else if ((type as TypeImpl).nullabilitySuffix ==
+        NullabilitySuffix.question) {
+      return false;
+    } else if (type is TypeParameterType) {
+      return isNonNullable(type.bound);
     }
-    return (type as TypeImpl).nullabilitySuffix != NullabilitySuffix.question &&
-        (type is TypeParameterType ? isNonNullable(type.bound) : true);
+    return true;
   }
 
   @override
diff --git a/pkg/analyzer/lib/src/summary/format.dart b/pkg/analyzer/lib/src/summary/format.dart
index c9f3506..5c6d947 100644
--- a/pkg/analyzer/lib/src/summary/format.dart
+++ b/pkg/analyzer/lib/src/summary/format.dart
@@ -16035,9 +16035,9 @@
     implements idl.LinkedNodeType {
   List<LinkedNodeTypeFormalParameterBuilder> _functionFormalParameters;
   LinkedNodeTypeBuilder _functionReturnType;
+  int _functionTypedef;
+  List<LinkedNodeTypeBuilder> _functionTypedefTypeArguments;
   List<LinkedNodeTypeTypeParameterBuilder> _functionTypeParameters;
-  int _genericTypeAliasReference;
-  List<LinkedNodeTypeBuilder> _genericTypeAliasTypeArguments;
   int _interfaceClass;
   List<LinkedNodeTypeBuilder> _interfaceTypeArguments;
   idl.LinkedNodeTypeKind _kind;
@@ -16062,6 +16062,23 @@
   }
 
   @override
+  int get functionTypedef => _functionTypedef ??= 0;
+
+  /// The typedef this function type is created for.
+  set functionTypedef(int value) {
+    assert(value == null || value >= 0);
+    this._functionTypedef = value;
+  }
+
+  @override
+  List<LinkedNodeTypeBuilder> get functionTypedefTypeArguments =>
+      _functionTypedefTypeArguments ??= <LinkedNodeTypeBuilder>[];
+
+  set functionTypedefTypeArguments(List<LinkedNodeTypeBuilder> value) {
+    this._functionTypedefTypeArguments = value;
+  }
+
+  @override
   List<LinkedNodeTypeTypeParameterBuilder> get functionTypeParameters =>
       _functionTypeParameters ??= <LinkedNodeTypeTypeParameterBuilder>[];
 
@@ -16070,22 +16087,6 @@
   }
 
   @override
-  int get genericTypeAliasReference => _genericTypeAliasReference ??= 0;
-
-  set genericTypeAliasReference(int value) {
-    assert(value == null || value >= 0);
-    this._genericTypeAliasReference = value;
-  }
-
-  @override
-  List<LinkedNodeTypeBuilder> get genericTypeAliasTypeArguments =>
-      _genericTypeAliasTypeArguments ??= <LinkedNodeTypeBuilder>[];
-
-  set genericTypeAliasTypeArguments(List<LinkedNodeTypeBuilder> value) {
-    this._genericTypeAliasTypeArguments = value;
-  }
-
-  @override
   int get interfaceClass => _interfaceClass ??= 0;
 
   /// Reference to a [LinkedNodeReferences].
@@ -16136,9 +16137,9 @@
   LinkedNodeTypeBuilder(
       {List<LinkedNodeTypeFormalParameterBuilder> functionFormalParameters,
       LinkedNodeTypeBuilder functionReturnType,
+      int functionTypedef,
+      List<LinkedNodeTypeBuilder> functionTypedefTypeArguments,
       List<LinkedNodeTypeTypeParameterBuilder> functionTypeParameters,
-      int genericTypeAliasReference,
-      List<LinkedNodeTypeBuilder> genericTypeAliasTypeArguments,
       int interfaceClass,
       List<LinkedNodeTypeBuilder> interfaceTypeArguments,
       idl.LinkedNodeTypeKind kind,
@@ -16147,9 +16148,9 @@
       int typeParameterId})
       : _functionFormalParameters = functionFormalParameters,
         _functionReturnType = functionReturnType,
+        _functionTypedef = functionTypedef,
+        _functionTypedefTypeArguments = functionTypedefTypeArguments,
         _functionTypeParameters = functionTypeParameters,
-        _genericTypeAliasReference = genericTypeAliasReference,
-        _genericTypeAliasTypeArguments = genericTypeAliasTypeArguments,
         _interfaceClass = interfaceClass,
         _interfaceTypeArguments = interfaceTypeArguments,
         _kind = kind,
@@ -16161,8 +16162,8 @@
   void flushInformative() {
     _functionFormalParameters?.forEach((b) => b.flushInformative());
     _functionReturnType?.flushInformative();
+    _functionTypedefTypeArguments?.forEach((b) => b.flushInformative());
     _functionTypeParameters?.forEach((b) => b.flushInformative());
-    _genericTypeAliasTypeArguments?.forEach((b) => b.flushInformative());
     _interfaceTypeArguments?.forEach((b) => b.flushInformative());
   }
 
@@ -16198,24 +16199,24 @@
     signature.addInt(this._kind == null ? 0 : this._kind.index);
     signature.addInt(this._typeParameterElement ?? 0);
     signature.addInt(this._typeParameterId ?? 0);
-    signature.addInt(this._genericTypeAliasReference ?? 0);
-    if (this._genericTypeAliasTypeArguments == null) {
+    signature.addInt(
+        this._nullabilitySuffix == null ? 0 : this._nullabilitySuffix.index);
+    signature.addInt(this._functionTypedef ?? 0);
+    if (this._functionTypedefTypeArguments == null) {
       signature.addInt(0);
     } else {
-      signature.addInt(this._genericTypeAliasTypeArguments.length);
-      for (var x in this._genericTypeAliasTypeArguments) {
+      signature.addInt(this._functionTypedefTypeArguments.length);
+      for (var x in this._functionTypedefTypeArguments) {
         x?.collectApiSignature(signature);
       }
     }
-    signature.addInt(
-        this._nullabilitySuffix == null ? 0 : this._nullabilitySuffix.index);
   }
 
   fb.Offset finish(fb.Builder fbBuilder) {
     fb.Offset offset_functionFormalParameters;
     fb.Offset offset_functionReturnType;
+    fb.Offset offset_functionTypedefTypeArguments;
     fb.Offset offset_functionTypeParameters;
-    fb.Offset offset_genericTypeAliasTypeArguments;
     fb.Offset offset_interfaceTypeArguments;
     if (!(_functionFormalParameters == null ||
         _functionFormalParameters.isEmpty)) {
@@ -16225,17 +16226,17 @@
     if (_functionReturnType != null) {
       offset_functionReturnType = _functionReturnType.finish(fbBuilder);
     }
+    if (!(_functionTypedefTypeArguments == null ||
+        _functionTypedefTypeArguments.isEmpty)) {
+      offset_functionTypedefTypeArguments = fbBuilder.writeList(
+          _functionTypedefTypeArguments
+              .map((b) => b.finish(fbBuilder))
+              .toList());
+    }
     if (!(_functionTypeParameters == null || _functionTypeParameters.isEmpty)) {
       offset_functionTypeParameters = fbBuilder.writeList(
           _functionTypeParameters.map((b) => b.finish(fbBuilder)).toList());
     }
-    if (!(_genericTypeAliasTypeArguments == null ||
-        _genericTypeAliasTypeArguments.isEmpty)) {
-      offset_genericTypeAliasTypeArguments = fbBuilder.writeList(
-          _genericTypeAliasTypeArguments
-              .map((b) => b.finish(fbBuilder))
-              .toList());
-    }
     if (!(_interfaceTypeArguments == null || _interfaceTypeArguments.isEmpty)) {
       offset_interfaceTypeArguments = fbBuilder.writeList(
           _interfaceTypeArguments.map((b) => b.finish(fbBuilder)).toList());
@@ -16247,15 +16248,15 @@
     if (offset_functionReturnType != null) {
       fbBuilder.addOffset(1, offset_functionReturnType);
     }
+    if (_functionTypedef != null && _functionTypedef != 0) {
+      fbBuilder.addUint32(9, _functionTypedef);
+    }
+    if (offset_functionTypedefTypeArguments != null) {
+      fbBuilder.addOffset(10, offset_functionTypedefTypeArguments);
+    }
     if (offset_functionTypeParameters != null) {
       fbBuilder.addOffset(2, offset_functionTypeParameters);
     }
-    if (_genericTypeAliasReference != null && _genericTypeAliasReference != 0) {
-      fbBuilder.addUint32(8, _genericTypeAliasReference);
-    }
-    if (offset_genericTypeAliasTypeArguments != null) {
-      fbBuilder.addOffset(9, offset_genericTypeAliasTypeArguments);
-    }
     if (_interfaceClass != null && _interfaceClass != 0) {
       fbBuilder.addUint32(3, _interfaceClass);
     }
@@ -16267,7 +16268,7 @@
     }
     if (_nullabilitySuffix != null &&
         _nullabilitySuffix != idl.EntityRefNullabilitySuffix.starOrIrrelevant) {
-      fbBuilder.addUint8(10, _nullabilitySuffix.index);
+      fbBuilder.addUint8(8, _nullabilitySuffix.index);
     }
     if (_typeParameterElement != null && _typeParameterElement != 0) {
       fbBuilder.addUint32(6, _typeParameterElement);
@@ -16297,9 +16298,9 @@
 
   List<idl.LinkedNodeTypeFormalParameter> _functionFormalParameters;
   idl.LinkedNodeType _functionReturnType;
+  int _functionTypedef;
+  List<idl.LinkedNodeType> _functionTypedefTypeArguments;
   List<idl.LinkedNodeTypeTypeParameter> _functionTypeParameters;
-  int _genericTypeAliasReference;
-  List<idl.LinkedNodeType> _genericTypeAliasTypeArguments;
   int _interfaceClass;
   List<idl.LinkedNodeType> _interfaceTypeArguments;
   idl.LinkedNodeTypeKind _kind;
@@ -16325,6 +16326,21 @@
   }
 
   @override
+  int get functionTypedef {
+    _functionTypedef ??=
+        const fb.Uint32Reader().vTableGet(_bc, _bcOffset, 9, 0);
+    return _functionTypedef;
+  }
+
+  @override
+  List<idl.LinkedNodeType> get functionTypedefTypeArguments {
+    _functionTypedefTypeArguments ??=
+        const fb.ListReader<idl.LinkedNodeType>(const _LinkedNodeTypeReader())
+            .vTableGet(_bc, _bcOffset, 10, const <idl.LinkedNodeType>[]);
+    return _functionTypedefTypeArguments;
+  }
+
+  @override
   List<idl.LinkedNodeTypeTypeParameter> get functionTypeParameters {
     _functionTypeParameters ??=
         const fb.ListReader<idl.LinkedNodeTypeTypeParameter>(
@@ -16335,21 +16351,6 @@
   }
 
   @override
-  int get genericTypeAliasReference {
-    _genericTypeAliasReference ??=
-        const fb.Uint32Reader().vTableGet(_bc, _bcOffset, 8, 0);
-    return _genericTypeAliasReference;
-  }
-
-  @override
-  List<idl.LinkedNodeType> get genericTypeAliasTypeArguments {
-    _genericTypeAliasTypeArguments ??=
-        const fb.ListReader<idl.LinkedNodeType>(const _LinkedNodeTypeReader())
-            .vTableGet(_bc, _bcOffset, 9, const <idl.LinkedNodeType>[]);
-    return _genericTypeAliasTypeArguments;
-  }
-
-  @override
   int get interfaceClass {
     _interfaceClass ??= const fb.Uint32Reader().vTableGet(_bc, _bcOffset, 3, 0);
     return _interfaceClass;
@@ -16373,7 +16374,7 @@
   @override
   idl.EntityRefNullabilitySuffix get nullabilitySuffix {
     _nullabilitySuffix ??= const _EntityRefNullabilitySuffixReader().vTableGet(
-        _bc, _bcOffset, 10, idl.EntityRefNullabilitySuffix.starOrIrrelevant);
+        _bc, _bcOffset, 8, idl.EntityRefNullabilitySuffix.starOrIrrelevant);
     return _nullabilitySuffix;
   }
 
@@ -16401,15 +16402,14 @@
           functionFormalParameters.map((_value) => _value.toJson()).toList();
     if (functionReturnType != null)
       _result["functionReturnType"] = functionReturnType.toJson();
+    if (functionTypedef != 0) _result["functionTypedef"] = functionTypedef;
+    if (functionTypedefTypeArguments.isNotEmpty)
+      _result["functionTypedefTypeArguments"] = functionTypedefTypeArguments
+          .map((_value) => _value.toJson())
+          .toList();
     if (functionTypeParameters.isNotEmpty)
       _result["functionTypeParameters"] =
           functionTypeParameters.map((_value) => _value.toJson()).toList();
-    if (genericTypeAliasReference != 0)
-      _result["genericTypeAliasReference"] = genericTypeAliasReference;
-    if (genericTypeAliasTypeArguments.isNotEmpty)
-      _result["genericTypeAliasTypeArguments"] = genericTypeAliasTypeArguments
-          .map((_value) => _value.toJson())
-          .toList();
     if (interfaceClass != 0) _result["interfaceClass"] = interfaceClass;
     if (interfaceTypeArguments.isNotEmpty)
       _result["interfaceTypeArguments"] =
@@ -16428,9 +16428,9 @@
   Map<String, Object> toMap() => {
         "functionFormalParameters": functionFormalParameters,
         "functionReturnType": functionReturnType,
+        "functionTypedef": functionTypedef,
+        "functionTypedefTypeArguments": functionTypedefTypeArguments,
         "functionTypeParameters": functionTypeParameters,
-        "genericTypeAliasReference": genericTypeAliasReference,
-        "genericTypeAliasTypeArguments": genericTypeAliasTypeArguments,
         "interfaceClass": interfaceClass,
         "interfaceTypeArguments": interfaceTypeArguments,
         "kind": kind,
@@ -16698,6 +16698,7 @@
   bool _isNNBD;
   bool _isSynthetic;
   LinkedNodeBuilder _node;
+  String _partUriStr;
   UnlinkedTokensBuilder _tokens;
   String _uriStr;
 
@@ -16723,6 +16724,15 @@
   }
 
   @override
+  String get partUriStr => _partUriStr ??= '';
+
+  /// If the unit is a part, the URI specified in the `part` directive.
+  /// Otherwise empty.
+  set partUriStr(String value) {
+    this._partUriStr = value;
+  }
+
+  @override
   UnlinkedTokensBuilder get tokens => _tokens;
 
   set tokens(UnlinkedTokensBuilder value) {
@@ -16732,6 +16742,7 @@
   @override
   String get uriStr => _uriStr ??= '';
 
+  /// The absolute URI.
   set uriStr(String value) {
     this._uriStr = value;
   }
@@ -16740,11 +16751,13 @@
       {bool isNNBD,
       bool isSynthetic,
       LinkedNodeBuilder node,
+      String partUriStr,
       UnlinkedTokensBuilder tokens,
       String uriStr})
       : _isNNBD = isNNBD,
         _isSynthetic = isSynthetic,
         _node = node,
+        _partUriStr = partUriStr,
         _tokens = tokens,
         _uriStr = uriStr;
 
@@ -16763,15 +16776,20 @@
     this._node?.collectApiSignature(signature);
     signature.addBool(this._isSynthetic == true);
     signature.addBool(this._isNNBD == true);
+    signature.addString(this._partUriStr ?? '');
   }
 
   fb.Offset finish(fb.Builder fbBuilder) {
     fb.Offset offset_node;
+    fb.Offset offset_partUriStr;
     fb.Offset offset_tokens;
     fb.Offset offset_uriStr;
     if (_node != null) {
       offset_node = _node.finish(fbBuilder);
     }
+    if (_partUriStr != null) {
+      offset_partUriStr = fbBuilder.writeString(_partUriStr);
+    }
     if (_tokens != null) {
       offset_tokens = _tokens.finish(fbBuilder);
     }
@@ -16788,6 +16806,9 @@
     if (offset_node != null) {
       fbBuilder.addOffset(2, offset_node);
     }
+    if (offset_partUriStr != null) {
+      fbBuilder.addOffset(5, offset_partUriStr);
+    }
     if (offset_tokens != null) {
       fbBuilder.addOffset(1, offset_tokens);
     }
@@ -16817,6 +16838,7 @@
   bool _isNNBD;
   bool _isSynthetic;
   idl.LinkedNode _node;
+  String _partUriStr;
   idl.UnlinkedTokens _tokens;
   String _uriStr;
 
@@ -16839,6 +16861,12 @@
   }
 
   @override
+  String get partUriStr {
+    _partUriStr ??= const fb.StringReader().vTableGet(_bc, _bcOffset, 5, '');
+    return _partUriStr;
+  }
+
+  @override
   idl.UnlinkedTokens get tokens {
     _tokens ??=
         const _UnlinkedTokensReader().vTableGet(_bc, _bcOffset, 1, null);
@@ -16859,6 +16887,7 @@
     if (isNNBD != false) _result["isNNBD"] = isNNBD;
     if (isSynthetic != false) _result["isSynthetic"] = isSynthetic;
     if (node != null) _result["node"] = node.toJson();
+    if (partUriStr != '') _result["partUriStr"] = partUriStr;
     if (tokens != null) _result["tokens"] = tokens.toJson();
     if (uriStr != '') _result["uriStr"] = uriStr;
     return _result;
@@ -16869,6 +16898,7 @@
         "isNNBD": isNNBD,
         "isSynthetic": isSynthetic,
         "node": node,
+        "partUriStr": partUriStr,
         "tokens": tokens,
         "uriStr": uriStr,
       };
@@ -22621,12 +22651,12 @@
     implements idl.UnlinkedInformativeData {
   int _variantField_2;
   int _variantField_3;
+  List<int> _variantField_7;
+  int _variantField_6;
+  int _variantField_5;
   int _variantField_1;
   List<String> _variantField_4;
   idl.LinkedNodeKind _kind;
-  int _variantField_5;
-  int _variantField_6;
-  List<int> _variantField_7;
 
   @override
   int get codeLength {
@@ -22713,6 +22743,43 @@
   }
 
   @override
+  List<int> get compilationUnit_lineStarts {
+    assert(kind == idl.LinkedNodeKind.compilationUnit);
+    return _variantField_7 ??= <int>[];
+  }
+
+  /// Offsets of the first character of each line in the source code.
+  set compilationUnit_lineStarts(List<int> value) {
+    assert(kind == idl.LinkedNodeKind.compilationUnit);
+    assert(value == null || value.every((e) => e >= 0));
+    _variantField_7 = value;
+  }
+
+  @override
+  int get constructorDeclaration_periodOffset {
+    assert(kind == idl.LinkedNodeKind.constructorDeclaration);
+    return _variantField_6 ??= 0;
+  }
+
+  set constructorDeclaration_periodOffset(int value) {
+    assert(kind == idl.LinkedNodeKind.constructorDeclaration);
+    assert(value == null || value >= 0);
+    _variantField_6 = value;
+  }
+
+  @override
+  int get constructorDeclaration_returnTypeOffset {
+    assert(kind == idl.LinkedNodeKind.constructorDeclaration);
+    return _variantField_5 ??= 0;
+  }
+
+  set constructorDeclaration_returnTypeOffset(int value) {
+    assert(kind == idl.LinkedNodeKind.constructorDeclaration);
+    assert(value == null || value >= 0);
+    _variantField_5 = value;
+  }
+
+  @override
   int get directiveKeywordOffset {
     assert(kind == idl.LinkedNodeKind.exportDirective ||
         kind == idl.LinkedNodeKind.importDirective ||
@@ -22815,43 +22882,6 @@
     this._kind = value;
   }
 
-  @override
-  int get constructorDeclaration_returnTypeOffset {
-    assert(kind == idl.LinkedNodeKind.constructorDeclaration);
-    return _variantField_5 ??= 0;
-  }
-
-  set constructorDeclaration_returnTypeOffset(int value) {
-    assert(kind == idl.LinkedNodeKind.constructorDeclaration);
-    assert(value == null || value >= 0);
-    _variantField_5 = value;
-  }
-
-  @override
-  int get constructorDeclaration_periodOffset {
-    assert(kind == idl.LinkedNodeKind.constructorDeclaration);
-    return _variantField_6 ??= 0;
-  }
-
-  set constructorDeclaration_periodOffset(int value) {
-    assert(kind == idl.LinkedNodeKind.constructorDeclaration);
-    assert(value == null || value >= 0);
-    _variantField_6 = value;
-  }
-
-  @override
-  List<int> get compilationUnit_lineStarts {
-    assert(kind == idl.LinkedNodeKind.compilationUnit);
-    return _variantField_7 ??= <int>[];
-  }
-
-  /// Offsets of the first character of each line in the source code.
-  set compilationUnit_lineStarts(List<int> value) {
-    assert(kind == idl.LinkedNodeKind.compilationUnit);
-    assert(value == null || value.every((e) => e >= 0));
-    _variantField_7 = value;
-  }
-
   UnlinkedInformativeDataBuilder.classDeclaration({
     int codeLength,
     int codeOffset,
@@ -22886,17 +22916,17 @@
   UnlinkedInformativeDataBuilder.constructorDeclaration({
     int codeLength,
     int codeOffset,
+    int constructorDeclaration_periodOffset,
+    int constructorDeclaration_returnTypeOffset,
     int nameOffset,
     List<String> documentationComment_tokens,
-    int constructorDeclaration_returnTypeOffset,
-    int constructorDeclaration_periodOffset,
   })  : _kind = idl.LinkedNodeKind.constructorDeclaration,
         _variantField_2 = codeLength,
         _variantField_3 = codeOffset,
-        _variantField_1 = nameOffset,
-        _variantField_4 = documentationComment_tokens,
+        _variantField_6 = constructorDeclaration_periodOffset,
         _variantField_5 = constructorDeclaration_returnTypeOffset,
-        _variantField_6 = constructorDeclaration_periodOffset;
+        _variantField_1 = nameOffset,
+        _variantField_4 = documentationComment_tokens;
 
   UnlinkedInformativeDataBuilder.defaultFormalParameter({
     int codeLength,
@@ -23308,15 +23338,15 @@
   }
 
   fb.Offset finish(fb.Builder fbBuilder) {
-    fb.Offset offset_variantField_4;
     fb.Offset offset_variantField_7;
+    fb.Offset offset_variantField_4;
+    if (!(_variantField_7 == null || _variantField_7.isEmpty)) {
+      offset_variantField_7 = fbBuilder.writeListUint32(_variantField_7);
+    }
     if (!(_variantField_4 == null || _variantField_4.isEmpty)) {
       offset_variantField_4 = fbBuilder.writeList(
           _variantField_4.map((b) => fbBuilder.writeString(b)).toList());
     }
-    if (!(_variantField_7 == null || _variantField_7.isEmpty)) {
-      offset_variantField_7 = fbBuilder.writeListUint32(_variantField_7);
-    }
     fbBuilder.startTable();
     if (_variantField_2 != null && _variantField_2 != 0) {
       fbBuilder.addUint32(2, _variantField_2);
@@ -23324,6 +23354,15 @@
     if (_variantField_3 != null && _variantField_3 != 0) {
       fbBuilder.addUint32(3, _variantField_3);
     }
+    if (offset_variantField_7 != null) {
+      fbBuilder.addOffset(7, offset_variantField_7);
+    }
+    if (_variantField_6 != null && _variantField_6 != 0) {
+      fbBuilder.addUint32(6, _variantField_6);
+    }
+    if (_variantField_5 != null && _variantField_5 != 0) {
+      fbBuilder.addUint32(5, _variantField_5);
+    }
     if (_variantField_1 != null && _variantField_1 != 0) {
       fbBuilder.addUint32(1, _variantField_1);
     }
@@ -23333,15 +23372,6 @@
     if (_kind != null && _kind != idl.LinkedNodeKind.adjacentStrings) {
       fbBuilder.addUint8(0, _kind.index);
     }
-    if (_variantField_5 != null && _variantField_5 != 0) {
-      fbBuilder.addUint32(5, _variantField_5);
-    }
-    if (_variantField_6 != null && _variantField_6 != 0) {
-      fbBuilder.addUint32(6, _variantField_6);
-    }
-    if (offset_variantField_7 != null) {
-      fbBuilder.addOffset(7, offset_variantField_7);
-    }
     return fbBuilder.endTable();
   }
 }
@@ -23365,12 +23395,12 @@
 
   int _variantField_2;
   int _variantField_3;
+  List<int> _variantField_7;
+  int _variantField_6;
+  int _variantField_5;
   int _variantField_1;
   List<String> _variantField_4;
   idl.LinkedNodeKind _kind;
-  int _variantField_5;
-  int _variantField_6;
-  List<int> _variantField_7;
 
   @override
   int get codeLength {
@@ -23417,6 +23447,28 @@
   }
 
   @override
+  List<int> get compilationUnit_lineStarts {
+    assert(kind == idl.LinkedNodeKind.compilationUnit);
+    _variantField_7 ??=
+        const fb.Uint32ListReader().vTableGet(_bc, _bcOffset, 7, const <int>[]);
+    return _variantField_7;
+  }
+
+  @override
+  int get constructorDeclaration_periodOffset {
+    assert(kind == idl.LinkedNodeKind.constructorDeclaration);
+    _variantField_6 ??= const fb.Uint32Reader().vTableGet(_bc, _bcOffset, 6, 0);
+    return _variantField_6;
+  }
+
+  @override
+  int get constructorDeclaration_returnTypeOffset {
+    assert(kind == idl.LinkedNodeKind.constructorDeclaration);
+    _variantField_5 ??= const fb.Uint32Reader().vTableGet(_bc, _bcOffset, 5, 0);
+    return _variantField_5;
+  }
+
+  @override
   int get directiveKeywordOffset {
     assert(kind == idl.LinkedNodeKind.exportDirective ||
         kind == idl.LinkedNodeKind.importDirective ||
@@ -23474,28 +23526,6 @@
         .vTableGet(_bc, _bcOffset, 0, idl.LinkedNodeKind.adjacentStrings);
     return _kind;
   }
-
-  @override
-  int get constructorDeclaration_returnTypeOffset {
-    assert(kind == idl.LinkedNodeKind.constructorDeclaration);
-    _variantField_5 ??= const fb.Uint32Reader().vTableGet(_bc, _bcOffset, 5, 0);
-    return _variantField_5;
-  }
-
-  @override
-  int get constructorDeclaration_periodOffset {
-    assert(kind == idl.LinkedNodeKind.constructorDeclaration);
-    _variantField_6 ??= const fb.Uint32Reader().vTableGet(_bc, _bcOffset, 6, 0);
-    return _variantField_6;
-  }
-
-  @override
-  List<int> get compilationUnit_lineStarts {
-    assert(kind == idl.LinkedNodeKind.compilationUnit);
-    _variantField_7 ??=
-        const fb.Uint32ListReader().vTableGet(_bc, _bcOffset, 7, const <int>[]);
-    return _variantField_7;
-  }
 }
 
 abstract class _UnlinkedInformativeDataMixin
@@ -23528,15 +23558,15 @@
     if (kind == idl.LinkedNodeKind.constructorDeclaration) {
       if (codeLength != 0) _result["codeLength"] = codeLength;
       if (codeOffset != 0) _result["codeOffset"] = codeOffset;
-      if (nameOffset != 0) _result["nameOffset"] = nameOffset;
-      if (documentationComment_tokens.isNotEmpty)
-        _result["documentationComment_tokens"] = documentationComment_tokens;
-      if (constructorDeclaration_returnTypeOffset != 0)
-        _result["constructorDeclaration_returnTypeOffset"] =
-            constructorDeclaration_returnTypeOffset;
       if (constructorDeclaration_periodOffset != 0)
         _result["constructorDeclaration_periodOffset"] =
             constructorDeclaration_periodOffset;
+      if (constructorDeclaration_returnTypeOffset != 0)
+        _result["constructorDeclaration_returnTypeOffset"] =
+            constructorDeclaration_returnTypeOffset;
+      if (nameOffset != 0) _result["nameOffset"] = nameOffset;
+      if (documentationComment_tokens.isNotEmpty)
+        _result["documentationComment_tokens"] = documentationComment_tokens;
     }
     if (kind == idl.LinkedNodeKind.defaultFormalParameter) {
       if (codeLength != 0) _result["codeLength"] = codeLength;
@@ -23671,21 +23701,21 @@
       return {
         "codeLength": codeLength,
         "codeOffset": codeOffset,
-        "kind": kind,
         "compilationUnit_lineStarts": compilationUnit_lineStarts,
+        "kind": kind,
       };
     }
     if (kind == idl.LinkedNodeKind.constructorDeclaration) {
       return {
         "codeLength": codeLength,
         "codeOffset": codeOffset,
+        "constructorDeclaration_periodOffset":
+            constructorDeclaration_periodOffset,
+        "constructorDeclaration_returnTypeOffset":
+            constructorDeclaration_returnTypeOffset,
         "nameOffset": nameOffset,
         "documentationComment_tokens": documentationComment_tokens,
         "kind": kind,
-        "constructorDeclaration_returnTypeOffset":
-            constructorDeclaration_returnTypeOffset,
-        "constructorDeclaration_periodOffset":
-            constructorDeclaration_periodOffset,
       };
     }
     if (kind == idl.LinkedNodeKind.defaultFormalParameter) {
diff --git a/pkg/analyzer/lib/src/summary/format.fbs b/pkg/analyzer/lib/src/summary/format.fbs
index d68ae09..caaf7d3 100644
--- a/pkg/analyzer/lib/src/summary/format.fbs
+++ b/pkg/analyzer/lib/src/summary/format.fbs
@@ -1996,12 +1996,13 @@
 
   functionReturnType:LinkedNodeType (id: 1);
 
+  /// The typedef this function type is created for.
+  functionTypedef:uint (id: 9);
+
+  functionTypedefTypeArguments:[LinkedNodeType] (id: 10);
+
   functionTypeParameters:[LinkedNodeTypeTypeParameter] (id: 2);
 
-  genericTypeAliasReference:uint (id: 8);
-
-  genericTypeAliasTypeArguments:[LinkedNodeType] (id: 9);
-
   /// Reference to a [LinkedNodeReferences].
   interfaceClass:uint (id: 3);
 
@@ -2009,7 +2010,7 @@
 
   kind:LinkedNodeTypeKind (id: 5);
 
-  nullabilitySuffix:EntityRefNullabilitySuffix (id: 10);
+  nullabilitySuffix:EntityRefNullabilitySuffix (id: 8);
 
   typeParameterElement:uint (id: 6);
 
@@ -2040,8 +2041,13 @@
 
   node:LinkedNode (id: 2);
 
+  /// If the unit is a part, the URI specified in the `part` directive.
+  /// Otherwise empty.
+  partUriStr:string (id: 5);
+
   tokens:UnlinkedTokens (id: 1);
 
+  /// The absolute URI.
   uriStr:string (id: 0);
 }
 
@@ -2745,19 +2751,19 @@
 
   variantField_3:uint (id: 3);
 
+  /// Offsets of the first character of each line in the source code.
+  variantField_7:[uint] (id: 7);
+
+  variantField_6:uint (id: 6);
+
+  variantField_5:uint (id: 5);
+
   variantField_1:uint (id: 1);
 
   variantField_4:[string] (id: 4);
 
   /// The kind of the node.
   kind:LinkedNodeKind (id: 0);
-
-  variantField_5:uint (id: 5);
-
-  variantField_6:uint (id: 6);
-
-  /// Offsets of the first character of each line in the source code.
-  variantField_7:[uint] (id: 7);
 }
 
 /// Unlinked summary information about a function parameter.
diff --git a/pkg/analyzer/lib/src/summary/idl.dart b/pkg/analyzer/lib/src/summary/idl.dart
index 6590b07..1034e2b 100644
--- a/pkg/analyzer/lib/src/summary/idl.dart
+++ b/pkg/analyzer/lib/src/summary/idl.dart
@@ -1903,15 +1903,16 @@
   @Id(1)
   LinkedNodeType get functionReturnType;
 
+  /// The typedef this function type is created for.
+  @Id(9)
+  int get functionTypedef;
+
+  @Id(10)
+  List<LinkedNodeType> get functionTypedefTypeArguments;
+
   @Id(2)
   List<LinkedNodeTypeTypeParameter> get functionTypeParameters;
 
-  @Id(8)
-  int get genericTypeAliasReference;
-
-  @Id(9)
-  List<LinkedNodeType> get genericTypeAliasTypeArguments;
-
   /// Reference to a [LinkedNodeReferences].
   @Id(3)
   int get interfaceClass;
@@ -1922,7 +1923,7 @@
   @Id(5)
   LinkedNodeTypeKind get kind;
 
-  @Id(10)
+  @Id(8)
   EntityRefNullabilitySuffix get nullabilitySuffix;
 
   @Id(6)
@@ -1974,9 +1975,15 @@
   @Id(2)
   LinkedNode get node;
 
+  /// If the unit is a part, the URI specified in the `part` directive.
+  /// Otherwise empty.
+  @Id(5)
+  String get partUriStr;
+
   @Id(1)
   UnlinkedTokens get tokens;
 
+  /// The absolute URI.
   @Id(0)
   String get uriStr;
 }
@@ -3567,6 +3574,16 @@
   ])
   int get codeOffset;
 
+  /// Offsets of the first character of each line in the source code.
+  @VariantId(7, variant: LinkedNodeKind.compilationUnit)
+  List<int> get compilationUnit_lineStarts;
+
+  @VariantId(6, variant: LinkedNodeKind.constructorDeclaration)
+  int get constructorDeclaration_periodOffset;
+
+  @VariantId(5, variant: LinkedNodeKind.constructorDeclaration)
+  int get constructorDeclaration_returnTypeOffset;
+
   @VariantId(1, variantList: [
     LinkedNodeKind.exportDirective,
     LinkedNodeKind.importDirective,
@@ -3615,16 +3632,6 @@
     LinkedNodeKind.variableDeclaration,
   ])
   int get nameOffset;
-
-  @VariantId(5, variant: LinkedNodeKind.constructorDeclaration)
-  int get constructorDeclaration_returnTypeOffset;
-
-  @VariantId(6, variant: LinkedNodeKind.constructorDeclaration)
-  int get constructorDeclaration_periodOffset;
-
-  /// Offsets of the first character of each line in the source code.
-  @VariantId(7, variant: LinkedNodeKind.compilationUnit)
-  List<int> get compilationUnit_lineStarts;
 }
 
 /// Unlinked summary information about a function parameter.
diff --git a/pkg/analyzer/lib/src/summary/summary_file_builder.dart b/pkg/analyzer/lib/src/summary/summary_file_builder.dart
index cf9c22f..62f79f8 100644
--- a/pkg/analyzer/lib/src/summary/summary_file_builder.dart
+++ b/pkg/analyzer/lib/src/summary/summary_file_builder.dart
@@ -115,7 +115,7 @@
     CompilationUnit definingUnit = _parse(source);
     _addUnlinked(source, definingUnit);
     inputUnits.add(
-      summary2.LinkInputUnit(source, false, definingUnit),
+      summary2.LinkInputUnit(null, source, false, definingUnit),
     );
 
     for (Directive directive in definingUnit.directives) {
@@ -129,7 +129,7 @@
         CompilationUnit partUnit = _parse(partSource);
         _addUnlinked(partSource, partUnit);
         inputUnits.add(
-          summary2.LinkInputUnit(partSource, false, partUnit),
+          summary2.LinkInputUnit(partUri, partSource, false, partUnit),
         );
       }
     }
diff --git a/pkg/analyzer/lib/src/summary2/ast_binary_flags.dart b/pkg/analyzer/lib/src/summary2/ast_binary_flags.dart
index 5f973e1..95a829f 100644
--- a/pkg/analyzer/lib/src/summary2/ast_binary_flags.dart
+++ b/pkg/analyzer/lib/src/summary2/ast_binary_flags.dart
@@ -24,6 +24,11 @@
     VariableDeclaration,
   );
 
+  static final _hasName = _checkBit(
+    5,
+    ConstructorDeclaration,
+  );
+
   static final _hasNot = _checkBit(
     0,
     IsExpression,
@@ -217,6 +222,7 @@
     bool hasAwait: false,
     bool hasEqual: false,
     bool hasInitializer: false,
+    bool hasName: false,
     bool hasNot: false,
     bool hasPeriod: false,
     bool hasPeriod2: false,
@@ -260,6 +266,9 @@
     if (hasInitializer) {
       result |= _hasInitializer;
     }
+    if (hasName) {
+      result |= _hasName;
+    }
     if (hasNot) {
       result |= _hasNot;
     }
@@ -371,6 +380,10 @@
     return (flags & _hasInitializer) != 0;
   }
 
+  static bool hasName(int flags) {
+    return (flags & _hasName) != 0;
+  }
+
   static bool hasNot(int flags) {
     return (flags & _hasNot) != 0;
   }
diff --git a/pkg/analyzer/lib/src/summary2/ast_binary_reader.dart b/pkg/analyzer/lib/src/summary2/ast_binary_reader.dart
index 44b9e83..46f9ec4 100644
--- a/pkg/analyzer/lib/src/summary2/ast_binary_reader.dart
+++ b/pkg/analyzer/lib/src/summary2/ast_binary_reader.dart
@@ -384,6 +384,16 @@
     returnType.token.offset =
         informativeData?.constructorDeclaration_returnTypeOffset ?? 0;
 
+    Token periodToken;
+    SimpleIdentifier nameIdentifier;
+    if (AstBinaryFlags.hasName(data.flags)) {
+      periodToken = Token(
+        TokenType.PERIOD,
+        informativeData?.constructorDeclaration_periodOffset ?? 0,
+      );
+      nameIdentifier = _declaredIdentifier(data);
+    }
+
     var node = astFactory.constructorDeclaration(
       _readDocumentationComment(data),
       _readNodeListLazy(data.annotatedNode_metadata),
@@ -391,13 +401,8 @@
       AstBinaryFlags.isConst(data.flags) ? _Tokens.CONST : null,
       AstBinaryFlags.isFactory(data.flags) ? _Tokens.FACTORY : null,
       returnType,
-      data.name.isNotEmpty
-          ? Token(
-              TokenType.PERIOD,
-              informativeData?.constructorDeclaration_periodOffset ?? 0,
-            )
-          : null,
-      data.name.isNotEmpty ? _declaredIdentifier(data) : null,
+      periodToken,
+      nameIdentifier,
       _readNodeLazy(data.constructorDeclaration_parameters),
       _Tokens.choose(
         AstBinaryFlags.hasSeparatorColon(data.flags),
@@ -831,7 +836,6 @@
       question:
           AstBinaryFlags.hasQuestion(data.flags) ? _Tokens.QUESTION : null,
     );
-    node.type = _readType(data.genericFunctionType_type);
 
     // Create the node element, so now type parameter elements are available.
     LazyAst.setGenericFunctionTypeId(node, id);
@@ -849,6 +853,7 @@
     }
     node.returnType = readNode(data.genericFunctionType_returnType);
     node.parameters = _readNode(data.genericFunctionType_formalParameters);
+    node.type = _readType(data.genericFunctionType_type);
 
     return node;
   }
diff --git a/pkg/analyzer/lib/src/summary2/ast_binary_writer.dart b/pkg/analyzer/lib/src/summary2/ast_binary_writer.dart
index 5d3ce26..3e362e7 100644
--- a/pkg/analyzer/lib/src/summary2/ast_binary_writer.dart
+++ b/pkg/analyzer/lib/src/summary2/ast_binary_writer.dart
@@ -315,6 +315,7 @@
       informativeId: getInformativeId(node),
     );
     builder.flags = AstBinaryFlags.encode(
+      hasName: node.name != null,
       hasSeparatorColon: node.separator?.type == TokenType.COLON,
       hasSeparatorEquals: node.separator?.type == TokenType.EQ,
       isAbstract: node.body is EmptyFunctionBody,
diff --git a/pkg/analyzer/lib/src/summary2/builder/source_library_builder.dart b/pkg/analyzer/lib/src/summary2/builder/source_library_builder.dart
index 2845328..22fb71ee57 100644
--- a/pkg/analyzer/lib/src/summary2/builder/source_library_builder.dart
+++ b/pkg/analyzer/lib/src/summary2/builder/source_library_builder.dart
@@ -247,7 +247,7 @@
         linker.elementFactory,
         element,
         unitReference,
-        linker.contextFeatures.isEnabled(Feature.non_nullable),
+        unitContext.unit.featureSet.isEnabled(Feature.non_nullable),
         scope,
       );
       unitContext.unit.accept(resolver);
diff --git a/pkg/analyzer/lib/src/summary2/link.dart b/pkg/analyzer/lib/src/summary2/link.dart
index af4948d..bc3b18f 100644
--- a/pkg/analyzer/lib/src/summary2/link.dart
+++ b/pkg/analyzer/lib/src/summary2/link.dart
@@ -63,10 +63,6 @@
     return elementFactory.analysisContext;
   }
 
-  FeatureSet get contextFeatures {
-    return analysisContext.analysisOptions.contextFeatures;
-  }
-
   DeclaredVariables get declaredVariables {
     return analysisContext.declaredVariables;
   }
@@ -188,6 +184,7 @@
         builder.node.units.add(
           LinkedNodeUnitBuilder(
             isSynthetic: unitContext.isSynthetic,
+            partUriStr: unitContext.partUriStr,
             uriStr: unitContext.uriStr,
             node: unitLinkedNode,
             isNNBD: unit.featureSet.isEnabled(Feature.non_nullable),
@@ -273,11 +270,17 @@
 }
 
 class LinkInputUnit {
+  final String partUriStr;
   final Source source;
   final bool isSynthetic;
   final CompilationUnit unit;
 
-  LinkInputUnit(this.source, this.isSynthetic, this.unit);
+  LinkInputUnit(
+    this.partUriStr,
+    this.source,
+    this.isSynthetic,
+    this.unit,
+  );
 }
 
 class LinkResult {
diff --git a/pkg/analyzer/lib/src/summary2/linked_bundle_context.dart b/pkg/analyzer/lib/src/summary2/linked_bundle_context.dart
index 4c218be..7047fb0 100644
--- a/pkg/analyzer/lib/src/summary2/linked_bundle_context.dart
+++ b/pkg/analyzer/lib/src/summary2/linked_bundle_context.dart
@@ -41,6 +41,7 @@
           this,
           libraryContext,
           unitIndex,
+          unit.partUriStr,
           uriStr,
           reference,
           unit.isSynthetic,
@@ -79,6 +80,7 @@
           this,
           libraryContext,
           unitIndex++,
+          inputUnit.partUriStr,
           uriStr,
           reference,
           inputUnit.isSynthetic,
diff --git a/pkg/analyzer/lib/src/summary2/linked_element_factory.dart b/pkg/analyzer/lib/src/summary2/linked_element_factory.dart
index e1561c0..846cdab 100644
--- a/pkg/analyzer/lib/src/summary2/linked_element_factory.dart
+++ b/pkg/analyzer/lib/src/summary2/linked_element_factory.dart
@@ -213,6 +213,12 @@
       return reference.element;
     }
 
+    if (reference.name == '@function' && parent2.name == '@typeAlias') {
+      var parent = reference.parent;
+      GenericTypeAliasElementImpl alias = elementOfReference(parent);
+      return alias.function;
+    }
+
     throw StateError('Not found: $input');
   }
 
@@ -302,6 +308,7 @@
       unitElement.lineInfo = unitNode.lineInfo;
       unitElement.source = unitSource;
       unitElement.librarySource = librarySource;
+      unitElement.uri = unitContext.partUriStr;
       units.add(unitElement);
       unitContainerRef.getChild(unitContext.uriStr).element = unitElement;
     }
diff --git a/pkg/analyzer/lib/src/summary2/linked_unit_context.dart b/pkg/analyzer/lib/src/summary2/linked_unit_context.dart
index 2f285f7..ec6a624 100644
--- a/pkg/analyzer/lib/src/summary2/linked_unit_context.dart
+++ b/pkg/analyzer/lib/src/summary2/linked_unit_context.dart
@@ -25,6 +25,7 @@
   final LinkedBundleContext bundleContext;
   final LinkedLibraryContext libraryContext;
   final int indexInLibrary;
+  final String partUriStr;
   final String uriStr;
   final Reference reference;
   final bool isSynthetic;
@@ -50,6 +51,7 @@
       this.bundleContext,
       this.libraryContext,
       this.indexInLibrary,
+      this.partUriStr,
       this.uriStr,
       this.reference,
       this.isSynthetic,
@@ -85,20 +87,12 @@
   CompilationUnit get unit => _unit;
 
   CompilationUnit get unit_withDeclarations {
-    if (_unit == null) {
-      _unit = _astReader.readNode(data.node);
-
-      var informativeData = getInformativeData(data.node);
-      var lineStarts = informativeData?.compilationUnit_lineStarts ?? [];
-      if (lineStarts.isEmpty) {
-        lineStarts = [0];
-      }
-      _unit.lineInfo = LineInfo(lineStarts);
-    }
+    _ensureUnitWithDeclarations();
     return _unit;
   }
 
   CompilationUnit get unit_withDirectives {
+    _ensureUnitWithDeclarations();
     if (!_hasDirectivesRead) {
       var directiveDataList = data.node.compilationUnit_directives;
       for (var i = 0; i < directiveDataList.length; ++i) {
@@ -110,17 +104,6 @@
     return _unit;
   }
 
-  void createGenericFunctionTypeElement(int id, GenericFunctionTypeImpl node) {
-    var containerRef = this.reference.getChild('@genericFunctionType');
-    var reference = containerRef.getChild('$id');
-    var element = GenericFunctionTypeElementImpl.forLinkedNode(
-      this.reference.element,
-      reference,
-      node,
-    );
-    node.declaredElement = element;
-  }
-
   Comment createComment(LinkedNode data) {
     var informativeData = getInformativeData(data);
     var tokenStringList = informativeData?.documentationComment_tokens;
@@ -134,6 +117,17 @@
     return astFactory.documentationComment(tokens);
   }
 
+  void createGenericFunctionTypeElement(int id, GenericFunctionTypeImpl node) {
+    var containerRef = this.reference.getChild('@genericFunctionType');
+    var reference = containerRef.getChild('$id');
+    var element = GenericFunctionTypeElementImpl.forLinkedNode(
+      this.reference.element,
+      reference,
+      node,
+    );
+    node.declaredElement = element;
+  }
+
   /// Return the [LibraryElement] referenced in the [node].
   LibraryElement directiveLibrary(UriBasedDirective node) {
     var uriStr = LazyDirective.getSelectedUri(node);
@@ -898,11 +892,19 @@
 
       var nullabilitySuffix = _nullabilitySuffix(linkedType.nullabilitySuffix);
 
+      GenericTypeAliasElement typedefElement;
+      List<DartType> typedefTypeArguments = const <DartType>[];
+      if (linkedType.functionTypedef != 0) {
+        typedefElement =
+            bundleContext.elementOfIndex(linkedType.functionTypedef);
+        typedefTypeArguments =
+            linkedType.functionTypedefTypeArguments.map(readType).toList();
+      }
+
       return FunctionTypeImpl.synthetic(
-        returnType,
-        typeParameters,
-        formalParameters,
-      ).withNullability(nullabilitySuffix);
+              returnType, typeParameters, formalParameters,
+              element: typedefElement, typeArguments: typedefTypeArguments)
+          .withNullability(nullabilitySuffix);
     } else if (kind == LinkedNodeTypeKind.interface) {
       var element = bundleContext.elementOfIndex(linkedType.interfaceClass);
       var nullabilitySuffix = _nullabilitySuffix(linkedType.nullabilitySuffix);
@@ -987,6 +989,19 @@
     }
   }
 
+  void _ensureUnitWithDeclarations() {
+    if (_unit == null) {
+      _unit = _astReader.readNode(data.node);
+
+      var informativeData = getInformativeData(data.node);
+      var lineStarts = informativeData?.compilationUnit_lineStarts ?? [];
+      if (lineStarts.isEmpty) {
+        lineStarts = [0];
+      }
+      _unit.lineInfo = LineInfo(lineStarts);
+    }
+  }
+
   ParameterKind _formalParameterKind(LinkedNodeFormalParameterKind kind) {
     if (kind == LinkedNodeFormalParameterKind.optionalNamed) {
       return ParameterKind.NAMED;
diff --git a/pkg/analyzer/lib/src/summary2/linking_bundle_context.dart b/pkg/analyzer/lib/src/summary2/linking_bundle_context.dart
index 7a7c22d..5ed338c 100644
--- a/pkg/analyzer/lib/src/summary2/linking_bundle_context.dart
+++ b/pkg/analyzer/lib/src/summary2/linking_bundle_context.dart
@@ -167,6 +167,19 @@
       typeParameterBuilders[i].bound = writeType(typeParameter.bound);
     }
 
+    Element typedefElement;
+    List<DartType> typedefTypeArguments = const <DartType>[];
+    if (type.element is GenericTypeAliasElement) {
+      typedefElement = type.element;
+      typedefTypeArguments = type.typeArguments;
+    }
+    // TODO(scheglov) Cleanup to always use GenericTypeAliasElement.
+    if (type.element is GenericFunctionTypeElement &&
+        type.element.enclosingElement is GenericTypeAliasElement) {
+      typedefElement = type.element.enclosingElement;
+      typedefTypeArguments = type.typeArguments;
+    }
+
     var result = LinkedNodeTypeBuilder(
       kind: LinkedNodeTypeKind.function,
       functionFormalParameters: type.parameters
@@ -178,6 +191,9 @@
           .toList(),
       functionReturnType: writeType(type.returnType),
       functionTypeParameters: typeParameterBuilders,
+      functionTypedef: indexOfElement(typedefElement),
+      functionTypedefTypeArguments:
+          typedefTypeArguments.map(writeType).toList(),
       nullabilitySuffix: _nullabilitySuffix(type),
     );
 
diff --git a/pkg/analyzer/lib/src/summary2/metadata_resolver.dart b/pkg/analyzer/lib/src/summary2/metadata_resolver.dart
index a9265e1..b2f1eaa 100644
--- a/pkg/analyzer/lib/src/summary2/metadata_resolver.dart
+++ b/pkg/analyzer/lib/src/summary2/metadata_resolver.dart
@@ -28,6 +28,7 @@
     node.accept(LocalElementBuilder(holder, null));
 
     var astResolver = AstResolver(_linker, _libraryElement, _libraryScope);
+    astResolver.rewriteAst(node);
     astResolver.resolve(node);
   }
 
diff --git a/pkg/analyzer/lib/src/summary2/named_type_builder.dart b/pkg/analyzer/lib/src/summary2/named_type_builder.dart
index 003f614..003393b 100644
--- a/pkg/analyzer/lib/src/summary2/named_type_builder.dart
+++ b/pkg/analyzer/lib/src/summary2/named_type_builder.dart
@@ -134,6 +134,8 @@
       return _buildFormalParameterType(node.parameter);
     } else if (node is FunctionTypedFormalParameter) {
       return _buildFunctionType(
+        null,
+        null,
         node.typeParameters,
         node.returnType,
         node.parameters,
@@ -146,20 +148,14 @@
   }
 
   FunctionType _buildFunctionType(
+    GenericTypeAliasElement typedefElement,
+    List<DartType> typedefTypeParameterTypes,
     TypeParameterList typeParameterList,
     TypeAnnotation returnTypeNode,
     FormalParameterList parameterList,
   ) {
     var returnType = _buildNodeType(returnTypeNode);
-
-    List<TypeParameterElement> typeParameters;
-    if (typeParameterList != null) {
-      typeParameters = typeParameterList.typeParameters
-          .map<TypeParameterElement>((p) => p.declaredElement)
-          .toList();
-    } else {
-      typeParameters = const <TypeParameterElement>[];
-    }
+    var typeParameters = _typeParameters(typeParameterList);
 
     var formalParameters = parameterList.parameters.map((parameter) {
       return ParameterElementImpl.synthetic(
@@ -174,6 +170,8 @@
       returnType,
       typeParameters,
       formalParameters,
+      element: typedefElement,
+      typeArguments: typedefTypeParameterTypes,
     );
   }
 
@@ -210,6 +208,8 @@
     var typedefNode = element.linkedNode;
     if (typedefNode is FunctionTypeAlias) {
       return _buildFunctionType(
+        element,
+        _typeParameterTypes(typedefNode.typeParameters),
         null,
         typedefNode.returnType,
         typedefNode.parameters,
@@ -218,6 +218,8 @@
       var functionNode = typedefNode.functionType;
       if (functionNode != null) {
         return _buildFunctionType(
+          element,
+          _typeParameterTypes(typedefNode.typeParameters),
           functionNode.typeParameters,
           functionNode.returnType,
           functionNode.parameters,
@@ -233,4 +235,19 @@
   static List<DartType> _listOfDynamic(int length) {
     return List<DartType>.filled(length, _dynamicType);
   }
+
+  static List<TypeParameterElement> _typeParameters(TypeParameterList node) {
+    if (node != null) {
+      return node.typeParameters
+          .map<TypeParameterElement>((p) => p.declaredElement)
+          .toList();
+    } else {
+      return const <TypeParameterElement>[];
+    }
+  }
+
+  static List<DartType> _typeParameterTypes(TypeParameterList node) {
+    var elements = _typeParameters(node);
+    return TypeParameterTypeImpl.getTypes(elements);
+  }
 }
diff --git a/pkg/analyzer/lib/src/task/strong/checker.dart b/pkg/analyzer/lib/src/task/strong/checker.dart
index 7725c55..b286b97 100644
--- a/pkg/analyzer/lib/src/task/strong/checker.dart
+++ b/pkg/analyzer/lib/src/task/strong/checker.dart
@@ -896,7 +896,7 @@
       // parameters are properly substituted.
       var classType = targetType.element.type;
       var classLowerBound = classType.instantiate(new List.filled(
-          classType.typeParameters.length, typeProvider.bottomType));
+          classType.typeParameters.length, BottomTypeImpl.instance));
       var memberLowerBound = inheritance.getMember(
           classLowerBound, Name(element.librarySource.uri, element.name));
       var expectedType = invokeType.returnType;
diff --git a/pkg/analyzer/lib/src/test_utilities/find_element.dart b/pkg/analyzer/lib/src/test_utilities/find_element.dart
index bf87a5f..e4066ca 100644
--- a/pkg/analyzer/lib/src/test_utilities/find_element.dart
+++ b/pkg/analyzer/lib/src/test_utilities/find_element.dart
@@ -481,6 +481,15 @@
     throw StateError('Not found: $name');
   }
 
+  GenericTypeAliasElement functionTypeAlias(String name) {
+    for (var element in definingUnit.functionTypeAliases) {
+      if (element.name == name) {
+        return element;
+      }
+    }
+    throw StateError('Not found: $name');
+  }
+
   FunctionElement topFunction(String name) {
     for (var function in definingUnit.functions) {
       if (function.name == name) {
diff --git a/pkg/analyzer/test/dart/analysis/test_all.dart b/pkg/analyzer/test/dart/analysis/test_all.dart
index fe1f2ee..44f950b 100644
--- a/pkg/analyzer/test/dart/analysis/test_all.dart
+++ b/pkg/analyzer/test/dart/analysis/test_all.dart
@@ -5,9 +5,11 @@
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
 import 'declared_variables_test.dart' as declared_variables;
+import 'utilities_test.dart' as utilities;
 
 main() {
   defineReflectiveSuite(() {
     declared_variables.main();
+    utilities.main();
   }, name: 'analysis');
 }
diff --git a/pkg/analyzer/test/dart/analysis/utilities_test.dart b/pkg/analyzer/test/dart/analysis/utilities_test.dart
new file mode 100644
index 0000000..1aebc1fe8
--- /dev/null
+++ b/pkg/analyzer/test/dart/analysis/utilities_test.dart
@@ -0,0 +1,91 @@
+// Copyright (c) 2019, 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 'package:analyzer/dart/analysis/features.dart';
+import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/dart/analysis/utilities.dart';
+import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+void main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(UtilitiesTest);
+  });
+}
+
+@reflectiveTest
+class UtilitiesTest with ResourceProviderMixin {
+  test_parseString_errors_noThrow() {
+    String content = '''
+void main() => print('Hello, world!')
+''';
+    ParseStringResult result =
+        parseString(content: content, throwIfDiagnostics: false);
+    expect(result.content, content);
+    expect(result.errors, hasLength(1));
+    expect(result.lineInfo, isNotNull);
+    expect(result.unit.toString(),
+        equals("void main() => print('Hello, world!');"));
+  }
+
+  test_parseString_errors_throw() {
+    String content = '''
+void main() => print('Hello, world!')
+''';
+    expect(() => parseString(content: content),
+        throwsA(const TypeMatcher<ArgumentError>()));
+  }
+
+  test_parseString_featureSet_nnbd_off() {
+    String content = '''
+int? f() => 1;
+''';
+    var featureSet = FeatureSet.forTesting(sdkVersion: '2.3.0');
+    expect(featureSet.isEnabled(Feature.non_nullable), isFalse);
+    ParseStringResult result = parseString(
+        content: content, throwIfDiagnostics: false, featureSet: featureSet);
+    expect(result.content, content);
+    expect(result.errors, hasLength(1));
+    expect(result.lineInfo, isNotNull);
+    expect(result.unit.toString(), equals('int? f() => 1;'));
+  }
+
+  test_parseString_featureSet_nnbd_on() {
+    String content = '''
+int? f() => 1;
+''';
+    var featureSet =
+        FeatureSet.forTesting(additionalFeatures: [Feature.non_nullable]);
+    ParseStringResult result = parseString(
+        content: content, throwIfDiagnostics: false, featureSet: featureSet);
+    expect(result.content, content);
+    expect(result.errors, isEmpty);
+    expect(result.lineInfo, isNotNull);
+    expect(result.unit.toString(), equals('int? f() => 1;'));
+  }
+
+  test_parseString_lineInfo() {
+    String content = '''
+main() {
+  print('Hello, world!');
+}
+''';
+    ParseStringResult result = parseString(content: content);
+    expect(result.lineInfo, same(result.unit.lineInfo));
+    expect(result.lineInfo.lineStarts, [0, 9, 35, 37]);
+  }
+
+  test_parseString_noErrors() {
+    String content = '''
+void main() => print('Hello, world!');
+''';
+    ParseStringResult result = parseString(content: content);
+    expect(result.content, content);
+    expect(result.errors, isEmpty);
+    expect(result.lineInfo, isNotNull);
+    expect(result.unit.toString(),
+        equals("void main() => print('Hello, world!');"));
+  }
+}
diff --git a/pkg/analyzer/test/generated/compile_time_error_code.dart b/pkg/analyzer/test/generated/compile_time_error_code.dart
index e1db296..181575f 100644
--- a/pkg/analyzer/test/generated/compile_time_error_code.dart
+++ b/pkg/analyzer/test/generated/compile_time_error_code.dart
@@ -648,25 +648,28 @@
 
   test_constEvalThrowsException_unaryBitNot_null() async {
     await assertErrorsInCode('''
-const C = ~null;
+const dynamic D = null;
+const C = ~D;
 ''', [
-      error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 10, 5),
+      error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 34, 2),
     ]);
   }
 
   test_constEvalThrowsException_unaryNegated_null() async {
     await assertErrorsInCode('''
-const C = -null;
+const dynamic D = null;
+const C = -D;
 ''', [
-      error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 10, 5),
+      error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 34, 2),
     ]);
   }
 
   test_constEvalThrowsException_unaryNot_null() async {
     await assertErrorsInCode('''
-const C = !null;
+const dynamic D = null;
+const C = !D;
 ''', [
-      error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 10, 5),
+      error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 34, 2),
     ]);
   }
 
@@ -5547,9 +5550,10 @@
   Future<void> _check_constEvalThrowsException_binary_null(
       String expr, bool resolved) async {
     await assertErrorsInCode('''
-const C = $expr;
+const dynamic D = null;
+const C = ${expr.replaceAll('null', 'D')};
 ''', [
-      error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 10, 8),
+      error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 34, 5),
     ]);
   }
 
diff --git a/pkg/analyzer/test/generated/invalid_code_test.dart b/pkg/analyzer/test/generated/invalid_code_test.dart
index 73fb3ef..6b501b9 100644
--- a/pkg/analyzer/test/generated/invalid_code_test.dart
+++ b/pkg/analyzer/test/generated/invalid_code_test.dart
@@ -32,6 +32,22 @@
 ''');
   }
 
+  test_constructorDeclaration_named_missingName() async {
+    await _assertCanBeAnalyzed('''
+class C {
+  C.();
+}
+''');
+  }
+
+  test_constructorDeclaration_named_missingName_factory() async {
+    await _assertCanBeAnalyzed('''
+class C {
+  factory C.();
+}
+''');
+  }
+
   test_genericFunction_asTypeArgument_ofUnresolvedClass() async {
     await _assertCanBeAnalyzed(r'''
 C<int Function()> c;
diff --git a/pkg/analyzer/test/generated/parser_fasta_test.dart b/pkg/analyzer/test/generated/parser_fasta_test.dart
index 291c7ff..cb15507 100644
--- a/pkg/analyzer/test/generated/parser_fasta_test.dart
+++ b/pkg/analyzer/test/generated/parser_fasta_test.dart
@@ -23,7 +23,6 @@
 import 'package:front_end/src/fasta/scanner.dart'
     show LanguageVersionToken, ScannerConfiguration, ScannerResult, scanString;
 import 'package:front_end/src/fasta/scanner/error_token.dart' show ErrorToken;
-import 'package:front_end/src/fasta/scanner/string_scanner.dart';
 import 'package:pub_semver/src/version.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -1495,6 +1494,30 @@
     expect(extension.members, hasLength(0));
   }
 
+  void test_complex_type2() {
+    var unit = parseCompilationUnit('extension E<T> on C<T> { }');
+    expect(unit.declarations, hasLength(1));
+    var extension = unit.declarations[0] as ExtensionDeclaration;
+    expect(extension.name.name, 'E');
+    expect(extension.onKeyword.lexeme, 'on');
+    var namedType = (extension.extendedType as NamedType);
+    expect(namedType.name.name, 'C');
+    expect(namedType.typeArguments.arguments, hasLength(1));
+    expect(extension.members, hasLength(0));
+  }
+
+  void test_complex_type2_no_name() {
+    var unit = parseCompilationUnit('extension<T> on C<T> { }');
+    expect(unit.declarations, hasLength(1));
+    var extension = unit.declarations[0] as ExtensionDeclaration;
+    expect(extension.name, isNull);
+    expect(extension.onKeyword.lexeme, 'on');
+    var namedType = (extension.extendedType as NamedType);
+    expect(namedType.name.name, 'C');
+    expect(namedType.typeArguments.arguments, hasLength(1));
+    expect(extension.members, hasLength(0));
+  }
+
   void test_missing_on() {
     var unit = parseCompilationUnit('extension E', errors: [
       expectedError(ParserErrorCode.EXPECTED_TOKEN, 10, 1),
@@ -1571,6 +1594,19 @@
     expect(extension.members, hasLength(0));
   }
 
+  void test_simple_no_name() {
+    var unit = parseCompilationUnit('extension on C { }');
+    expect(unit.declarations, hasLength(1));
+    var extension = unit.declarations[0] as ExtensionDeclaration;
+    expect(extension.name, isNull);
+    expect(extension.onKeyword.lexeme, 'on');
+    expect((extension.extendedType as NamedType).name.name, 'C');
+    var namedType = (extension.extendedType as NamedType);
+    expect(namedType.name.name, 'C');
+    expect(namedType.typeArguments, isNull);
+    expect(extension.members, hasLength(0));
+  }
+
   void test_simple_not_enabled() {
     parseCompilationUnit('extension E on C { }',
         errors: [
@@ -1669,9 +1705,12 @@
   void createParser(String content,
       {int expectedEndOffset, FeatureSet featureSet}) {
     featureSet ??= FeatureSet.forTesting();
-    var scanner = new StringScanner(content,
-        configuration: ScannerConfiguration.nonNullable, includeComments: true);
-    _fastaTokens = scanner.tokenize();
+    var result = scanString(content,
+        configuration: featureSet.isEnabled(Feature.non_nullable)
+            ? ScannerConfiguration.nonNullable
+            : ScannerConfiguration.classic,
+        includeComments: true);
+    _fastaTokens = result.tokens;
     _parserProxy = new ParserProxy(_fastaTokens, featureSet,
         allowNativeClause: allowNativeClause,
         expectedEndOffset: expectedEndOffset);
@@ -2417,6 +2456,39 @@
     parseCompilationUnit('main() { C.a? Function()? x = 7; }');
   }
 
+  void test_indexed() {
+    CompilationUnit unit = parseCompilationUnit('main() { a[7]; }');
+    FunctionDeclaration method = unit.declarations[0];
+    BlockFunctionBody body = method.functionExpression.body;
+    ExpressionStatement statement = body.block.statements[0];
+    IndexExpression expression = statement.expression;
+    expect(expression.leftBracket.lexeme, '[');
+  }
+
+  void test_indexed_nullAware() {
+    CompilationUnit unit = parseCompilationUnit('main() { a?.[7]; }');
+    FunctionDeclaration method = unit.declarations[0];
+    BlockFunctionBody body = method.functionExpression.body;
+    ExpressionStatement statement = body.block.statements[0];
+    IndexExpression expression = statement.expression;
+    expect(expression.leftBracket.lexeme, '?.[');
+    expect(expression.rightBracket.lexeme, ']');
+    expect(expression.leftBracket.endGroup, expression.rightBracket);
+  }
+
+  void test_indexed_nullAware_optOut() {
+    CompilationUnit unit = parseCompilationUnit('''
+// @dart = 2.2
+main() { a?.[7]; }''',
+        errors: [expectedError(ParserErrorCode.MISSING_IDENTIFIER, 27, 1)]);
+    FunctionDeclaration method = unit.declarations[0];
+    BlockFunctionBody body = method.functionExpression.body;
+    ExpressionStatement statement = body.block.statements[0];
+    PropertyAccess expression = statement.expression;
+    expect(expression.target.toSource(), 'a');
+    expect(expression.operator.lexeme, '?.');
+  }
+
   void test_is_nullable() {
     CompilationUnit unit =
         parseCompilationUnit('main() { x is String? ? (x + y) : z; }');
diff --git a/pkg/analyzer/test/generated/parser_test.dart b/pkg/analyzer/test/generated/parser_test.dart
index 4de8709..e038ca8 100644
--- a/pkg/analyzer/test/generated/parser_test.dart
+++ b/pkg/analyzer/test/generated/parser_test.dart
@@ -1566,6 +1566,54 @@
     expect(constructor.body, isEmptyFunctionBody);
   }
 
+  void test_parseConstructor_superIndexed() {
+    createParser('C() : super()[];');
+    var constructor = parser.parseClassMember('C') as ConstructorDeclaration;
+    listener.assertErrors([
+      expectedError(ParserErrorCode.INVALID_SUPER_IN_INITIALIZER, 6, 5),
+      expectedError(ParserErrorCode.MISSING_IDENTIFIER, 14, 1),
+    ]);
+    expect(constructor, isNotNull);
+    expect(constructor.externalKeyword, isNull);
+    expect(constructor.constKeyword, isNull);
+    expect(constructor.factoryKeyword, isNull);
+    expect(constructor.returnType.name, 'C');
+    _assertIsDeclarationName(constructor.returnType, false);
+    expect(constructor.name, isNull);
+    expect(constructor.parameters, isNotNull);
+    expect(constructor.parameters.parameters, isEmpty);
+    expect(constructor.separator.lexeme, ':');
+    expect(constructor.initializers, hasLength(1));
+    SuperConstructorInvocation initializer = constructor.initializers[0];
+    expect(initializer.argumentList.arguments, isEmpty);
+    expect(constructor.redirectedConstructor, isNull);
+    expect(constructor.body, isEmptyFunctionBody);
+  }
+
+  void test_parseConstructor_thisIndexed() {
+    createParser('C() : this()[];');
+    var constructor = parser.parseClassMember('C') as ConstructorDeclaration;
+    listener.assertErrors([
+      expectedError(ParserErrorCode.INVALID_THIS_IN_INITIALIZER, 6, 4),
+      expectedError(ParserErrorCode.MISSING_IDENTIFIER, 13, 1),
+    ]);
+    expect(constructor, isNotNull);
+    expect(constructor.externalKeyword, isNull);
+    expect(constructor.constKeyword, isNull);
+    expect(constructor.factoryKeyword, isNull);
+    expect(constructor.returnType.name, 'C');
+    _assertIsDeclarationName(constructor.returnType, false);
+    expect(constructor.name, isNull);
+    expect(constructor.parameters, isNotNull);
+    expect(constructor.parameters.parameters, isEmpty);
+    expect(constructor.separator.lexeme, ':');
+    expect(constructor.initializers, hasLength(1));
+    RedirectingConstructorInvocation initializer = constructor.initializers[0];
+    expect(initializer.argumentList.arguments, isEmpty);
+    expect(constructor.redirectedConstructor, isNull);
+    expect(constructor.body, isEmptyFunctionBody);
+  }
+
   void test_parseConstructor_unnamed() {
     createParser('C();');
     var constructor = parser.parseClassMember('C') as ConstructorDeclaration;
diff --git a/pkg/analyzer/test/generated/static_type_warning_code_test.dart b/pkg/analyzer/test/generated/static_type_warning_code_test.dart
index 655d38e..ad2f86f 100644
--- a/pkg/analyzer/test/generated/static_type_warning_code_test.dart
+++ b/pkg/analyzer/test/generated/static_type_warning_code_test.dart
@@ -1732,7 +1732,7 @@
   }
 }
 ''', [
-      error(StaticTypeWarningCode.UNDEFINED_METHOD, 58, 2),
+      error(StaticTypeWarningCode.UNDEFINED_OPERATOR, 58, 2),
     ]);
   }
 
diff --git a/pkg/analyzer/test/generated/strong_mode_test.dart b/pkg/analyzer/test/generated/strong_mode_test.dart
index 1f4358e..176d24f 100644
--- a/pkg/analyzer/test/generated/strong_mode_test.dart
+++ b/pkg/analyzer/test/generated/strong_mode_test.dart
@@ -4,6 +4,7 @@
 
 import 'dart:async';
 
+import 'package:analyzer/dart/analysis/features.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/ast/standard_resolution_map.dart';
 import 'package:analyzer/dart/element/element.dart';
@@ -26,6 +27,7 @@
   defineReflectiveSuite(() {
     defineReflectiveTests(StrongModeCastsTest);
     defineReflectiveTests(StrongModeLocalInferenceTest);
+    defineReflectiveTests(StrongModeLocalInferenceTest_NNBD);
     defineReflectiveTests(StrongModeStaticTypeAnalyzer2Test);
     defineReflectiveTests(StrongModeTypePropagationTest);
   });
@@ -1708,6 +1710,12 @@
   cD.m1;
   cD.m1();
   cD.m2();
+
+  cN.f;
+  cN.g;
+  cN.m1;
+  cN.m1();
+  cN.m2();
 }
 
 noCasts() {
@@ -1723,12 +1731,6 @@
   (d.g)(42);
   d.m1()(42);
   d.m2()()(42);
-
-  cN.f;
-  cN.g;
-  cN.m1;
-  cN.m1();
-  cN.m2();
 }
 ''');
     var unit = (await computeAnalysisResult(source)).unit;
@@ -3907,6 +3909,233 @@
 }
 
 @reflectiveTest
+class StrongModeLocalInferenceTest_NNBD extends ResolverTestCase {
+  @override
+  AnalysisOptions get analysisOptions => new AnalysisOptionsImpl()
+    ..contextFeatures =
+        new FeatureSet.forTesting(additionalFeatures: [Feature.non_nullable]);
+
+  @override
+  void setUp() {
+    //TODO(mfairhurst): why is this override required?
+    super.setUp();
+    AnalysisOptionsImpl options = analysisOptions;
+    resetWith(options: options);
+  }
+
+  test_covarianceChecks_returnFunction() async {
+    // test Never cases.
+    var source = addSource(r'''
+typedef F<T>(T t);
+typedef T R<T>();
+class C<T> {
+  F<T> f = throw '';
+
+  C();
+  factory C.fact() => new C<Never>();
+
+  F<T> get g => throw '';
+  F<T> m1() => throw '';
+  R<F<T>> m2() => throw '';
+
+  casts(C<T> other, T t) {
+    other.f;
+    other.g(t);
+    other.m1();
+    other.m2;
+
+    new C<T>.fact().f(t);
+    new C<int>.fact().g;
+    new C<int>.fact().m1;
+    new C<T>.fact().m2();
+
+    new C<Object>.fact().f(42);
+    new C<Object>.fact().g;
+    new C<Object>.fact().m1;
+    new C<Object>.fact().m2();
+
+    new C.fact().f(42);
+    new C.fact().g;
+    new C.fact().m1;
+    new C.fact().m2();
+  }
+
+  noCasts(T t) {
+    f;
+    g;
+    m1();
+    m2();
+
+    f(t);
+    g(t);
+    (f)(t);
+    (g)(t);
+    m1;
+    m2;
+
+    this.f;
+    this.g;
+    this.m1();
+    this.m2();
+    this.m1;
+    this.m2;
+    (this.m1)();
+    (this.m2)();
+    this.f(t);
+    this.g(t);
+    (this.f)(t);
+    (this.g)(t);
+
+    new C<int>().f;
+    new C<T>().g;
+    new C<int>().m1();
+    new C().m2();
+
+    new D().f;
+    new D().g;
+    new D().m1();
+    new D().m2();
+  }
+}
+class D extends C<num> {
+  noCasts(t) {
+    f;
+    this.g;
+    this.m1();
+    m2;
+
+    super.f;
+    super.g;
+    super.m1;
+    super.m2();
+  }
+}
+
+D d = throw '';
+C<Object> c = throw '';
+C cD = throw '';
+C<Null> cNu = throw '';
+C<Never> cN = throw '';
+F<Object> f = throw '';
+F<Never> fN = throw '';
+R<F<Object>> rf = throw '';
+R<F<Never>> rfN = throw '';
+R<R<F<Object>>> rrf = throw '';
+R<R<F<Never>>> rrfN = throw '';
+Object obj = throw '';
+F<int> fi = throw '';
+R<F<int>> rfi = throw '';
+R<R<F<int>>> rrfi = throw '';
+
+casts() {
+  c.f;
+  c.g;
+  c.m1;
+  c.m1();
+  c.m2();
+
+  fN = c.f;
+  fN = c.g;
+  rfN = c.m1;
+  rrfN = c.m2;
+  fN = c.m1();
+  rfN = c.m2();
+
+  f = c.f;
+  f = c.g;
+  rf = c.m1;
+  rrf = c.m2;
+  f = c.m1();
+  rf = c.m2();
+  c.m2()();
+
+  c.f(obj);
+  c.g(obj);
+  (c.f)(obj);
+  (c.g)(obj);
+  (c.m1)();
+  c.m1()(obj);
+  (c.m2)();
+
+  cD.f;
+  cD.g;
+  cD.m1;
+  cD.m1();
+  cD.m2();
+
+  cNu.f;
+  cNu.g;
+  cNu.m1;
+  cNu.m1();
+  cNu.m2();
+}
+
+noCasts() {
+  fi = d.f;
+  fi = d.g;
+  rfi = d.m1;
+  fi = d.m1();
+  rrfi = d.m2;
+  rfi = d.m2();
+  d.f(42);
+  d.g(42);
+  (d.f)(42);
+  (d.g)(42);
+  d.m1()(42);
+  d.m2()()(42);
+
+  cN.f;
+  cN.g;
+  cN.m1;
+  cN.m1();
+  cN.m2();
+}
+''');
+    var unit = (await computeAnalysisResult(source)).unit;
+    assertNoErrors(source);
+
+    void expectCast(Statement statement, bool hasCast) {
+      var value = (statement as ExpressionStatement).expression;
+      if (value is AssignmentExpression) {
+        value = (value as AssignmentExpression).rightHandSide;
+      }
+      while (value is FunctionExpressionInvocation) {
+        value = (value as FunctionExpressionInvocation).function;
+      }
+      while (value is ParenthesizedExpression) {
+        value = (value as ParenthesizedExpression).expression;
+      }
+      var isCallingGetter =
+          value is MethodInvocation && !value.methodName.name.startsWith('m');
+      var cast = isCallingGetter
+          ? getImplicitOperationCast(value)
+          : getImplicitCast(value);
+      var castKind = isCallingGetter ? 'special cast' : 'cast';
+      expect(cast, hasCast ? isNotNull : isNull,
+          reason: '`$statement` should ' +
+              (hasCast ? '' : 'not ') +
+              'have a $castKind on `$value`.');
+    }
+
+    for (var s in AstFinder.getStatementsInMethod(unit, 'C', 'noCasts')) {
+      expectCast(s, false);
+    }
+    for (var s in AstFinder.getStatementsInMethod(unit, 'C', 'casts')) {
+      expectCast(s, true);
+    }
+    for (var s in AstFinder.getStatementsInMethod(unit, 'D', 'noCasts')) {
+      expectCast(s, false);
+    }
+    for (var s in AstFinder.getStatementsInTopLevelFunction(unit, 'noCasts')) {
+      expectCast(s, false);
+    }
+    for (var s in AstFinder.getStatementsInTopLevelFunction(unit, 'casts')) {
+      expectCast(s, true);
+    }
+  }
+}
+
+@reflectiveTest
 class StrongModeStaticTypeAnalyzer2Test extends StaticTypeAnalyzer2TestShared {
   void expectStaticInvokeType(String search, String type) {
     var invocation = findIdentifier(search).parent as MethodInvocation;
diff --git a/pkg/analyzer/test/generated/type_system_test.dart b/pkg/analyzer/test/generated/type_system_test.dart
index 964112f..511ce30 100644
--- a/pkg/analyzer/test/generated/type_system_test.dart
+++ b/pkg/analyzer/test/generated/type_system_test.dart
@@ -22,6 +22,7 @@
 import 'package:analyzer/src/generated/testing/element_factory.dart';
 import 'package:analyzer/src/generated/testing/test_type_provider.dart';
 import 'package:analyzer/src/generated/utilities_dart.dart';
+import 'package:meta/meta.dart';
 import 'package:path/path.dart' show toUri;
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -1867,19 +1868,41 @@
     typeProvider = new NonNullableTypeProvider(coreLibrary, asyncLibrary);
   }
 
+  void test_dynamicType() {
+    List<DartType> equivalents = <DartType>[
+      voidType,
+      _question(objectType),
+      _star(objectType),
+    ];
+    List<DartType> subtypes = <DartType>[bottomType, nullType, objectType];
+    _checkGroups(dynamicType, equivalents: equivalents, subtypes: subtypes);
+  }
+
   void test_int_nullableTypes() {
     List<DartType> equivalents = <DartType>[
       intType,
       _star(intType),
     ];
+    List<DartType> subtypes = <DartType>[
+      bottomType,
+    ];
     List<DartType> supertypes = <DartType>[
       _question(intType),
       objectType,
       _question(objectType),
     ];
-    List<DartType> unrelated = <DartType>[doubleType, nullType];
+    List<DartType> unrelated = <DartType>[
+      doubleType,
+      nullType,
+      _star(nullType),
+      _question(nullType),
+      _question(bottomType),
+    ];
     _checkGroups(intType,
-        equivalents: equivalents, supertypes: supertypes, unrelated: unrelated);
+        equivalents: equivalents,
+        supertypes: supertypes,
+        unrelated: unrelated,
+        subtypes: subtypes);
   }
 
   void test_intQuestion_nullableTypes() {
@@ -1890,6 +1913,11 @@
     List<DartType> subtypes = <DartType>[
       intType,
       nullType,
+      _question(nullType),
+      _star(nullType),
+      bottomType,
+      _question(bottomType),
+      _star(bottomType),
     ];
     List<DartType> supertypes = <DartType>[
       _question(numType),
@@ -1911,7 +1939,14 @@
       _question(intType),
       _star(intType),
     ];
-    List<DartType> subtypes = <DartType>[nullType];
+    List<DartType> subtypes = <DartType>[
+      nullType,
+      _star(nullType),
+      _question(nullType),
+      bottomType,
+      _star(bottomType),
+      _question(bottomType),
+    ];
     List<DartType> supertypes = <DartType>[
       numType,
       _question(numType),
@@ -1927,6 +1962,61 @@
         subtypes: subtypes);
   }
 
+  void test_nullType() {
+    List<DartType> equivalents = <DartType>[
+      nullType,
+      _question(nullType),
+      _star(nullType),
+      _question(bottomType),
+    ];
+    List<DartType> supertypes = <DartType>[
+      _question(intType),
+      _star(intType),
+      _question(objectType),
+      _star(objectType),
+      dynamicType,
+      voidType,
+    ];
+    List<DartType> subtypes = <DartType>[bottomType];
+    List<DartType> unrelated = <DartType>[
+      doubleType,
+      intType,
+      numType,
+      objectType
+    ];
+
+    for (final formOfNull in equivalents) {
+      _checkGroups(formOfNull,
+          equivalents: equivalents,
+          supertypes: supertypes,
+          unrelated: unrelated,
+          subtypes: subtypes);
+    }
+  }
+
+  void test_objectType() {
+    List<DartType> equivalents = <DartType>[
+      _star(objectType),
+    ];
+    List<DartType> supertypes = <DartType>[
+      _question(objectType),
+      dynamicType,
+      voidType,
+    ];
+    List<DartType> subtypes = <DartType>[bottomType];
+    List<DartType> unrelated = <DartType>[
+      _question(doubleType),
+      _question(numType),
+      _question(intType),
+      nullType
+    ];
+    _checkGroups(objectType,
+        equivalents: equivalents,
+        supertypes: supertypes,
+        unrelated: unrelated,
+        subtypes: subtypes);
+  }
+
   DartType _question(DartType dartType) =>
       (dartType as TypeImpl).withNullability(NullabilitySuffix.question);
 
@@ -2370,8 +2460,10 @@
 class TypeSystemTest extends AbstractTypeSystemTest {
   DartType get futureOrWithNoneType =>
       typeProvider.futureOrType.instantiate([noneType]);
+
   DartType get futureOrWithQuestionType =>
       typeProvider.futureOrType.instantiate([questionType]);
+
   DartType get futureOrWithStarType =>
       typeProvider.futureOrType.instantiate([starType]);
 
@@ -2416,6 +2508,51 @@
     expect(typeSystem.isNonNullable(starType), true);
   }
 
+  test_isNonNullable_typeParameter_noneBound_none() {
+    expect(
+      typeSystem.isNonNullable(
+        typeParameterTypeNone(bound: noneType),
+      ),
+      true,
+    );
+  }
+
+  test_isNonNullable_typeParameter_noneBound_question() {
+    expect(
+      typeSystem.isNonNullable(
+        typeParameterTypeQuestion(bound: noneType),
+      ),
+      false,
+    );
+  }
+
+  test_isNonNullable_typeParameter_questionBound_none() {
+    expect(
+      typeSystem.isNonNullable(
+        typeParameterTypeNone(bound: questionType),
+      ),
+      false,
+    );
+  }
+
+  test_isNonNullable_typeParameter_questionBound_question() {
+    expect(
+      typeSystem.isNonNullable(
+        typeParameterTypeQuestion(bound: questionType),
+      ),
+      false,
+    );
+  }
+
+  test_isNonNullable_typeParameter_starBound_star() {
+    expect(
+      typeSystem.isNonNullable(
+        typeParameterTypeStar(bound: starType),
+      ),
+      true,
+    );
+  }
+
   test_isNonNullable_void() {
     expect(typeSystem.isNonNullable(voidType), false);
   }
@@ -2452,6 +2589,51 @@
     expect(typeSystem.isNullable(starType), true);
   }
 
+  test_isNullable_typeParameter_noneBound_none() {
+    expect(
+      typeSystem.isNullable(
+        typeParameterTypeNone(bound: noneType),
+      ),
+      false,
+    );
+  }
+
+  test_isNullable_typeParameter_noneBound_question() {
+    expect(
+      typeSystem.isNullable(
+        typeParameterTypeQuestion(bound: noneType),
+      ),
+      true,
+    );
+  }
+
+  test_isNullable_typeParameter_questionBound_none() {
+    expect(
+      typeSystem.isNullable(
+        typeParameterTypeNone(bound: questionType),
+      ),
+      false,
+    );
+  }
+
+  test_isNullable_typeParameter_questionBound_question() {
+    expect(
+      typeSystem.isNullable(
+        typeParameterTypeQuestion(bound: questionType),
+      ),
+      true,
+    );
+  }
+
+  test_isNullable_typeParameter_starBound_star() {
+    expect(
+      typeSystem.isNullable(
+        typeParameterTypeStar(bound: starType),
+      ),
+      true,
+    );
+  }
+
   test_isNullable_void() {
     expect(typeSystem.isNullable(voidType), true);
   }
@@ -2528,4 +2710,28 @@
   test_isPotentiallyNullable_void() {
     expect(typeSystem.isPotentiallyNullable(voidType), true);
   }
+
+  DartType typeParameterTypeNone({@required DartType bound}) {
+    expect(bound, isNotNull);
+    var element = TypeParameterElementImpl.synthetic('T');
+    element.bound = bound;
+    return TypeParameterTypeImpl(element,
+        nullabilitySuffix: NullabilitySuffix.none);
+  }
+
+  DartType typeParameterTypeQuestion({@required DartType bound}) {
+    expect(bound, isNotNull);
+    var element = TypeParameterElementImpl.synthetic('T');
+    element.bound = bound;
+    return TypeParameterTypeImpl(element,
+        nullabilitySuffix: NullabilitySuffix.question);
+  }
+
+  DartType typeParameterTypeStar({@required DartType bound}) {
+    expect(bound, isNotNull);
+    var element = TypeParameterElementImpl.synthetic('T');
+    element.bound = bound;
+    return TypeParameterTypeImpl(element,
+        nullabilitySuffix: NullabilitySuffix.star);
+  }
 }
diff --git a/pkg/analyzer/test/src/dart/analysis/driver_test.dart b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
index dcc2fad..eac7525 100644
--- a/pkg/analyzer/test/src/dart/analysis/driver_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
@@ -15,6 +15,7 @@
 import 'package:analyzer/src/dart/analysis/file_state.dart';
 import 'package:analyzer/src/dart/analysis/performance_logger.dart';
 import 'package:analyzer/src/dart/analysis/status.dart';
+import 'package:analyzer/src/dart/analysis/top_level_declaration.dart';
 import 'package:analyzer/src/dart/constant/evaluation.dart';
 import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/error/codes.dart';
@@ -2227,6 +2228,102 @@
     expect(await driver.getSourceKind(path), SourceKind.PART);
   }
 
+  test_getTopLevelNameDeclarations() async {
+    var a = convertPath('/test/lib/a.dart');
+    var b = convertPath('/test/lib/b.dart');
+    var c = convertPath('/test/lib/c.dart');
+    var d = convertPath('/test/lib/d.dart');
+
+    newFile(a, content: 'class A {}');
+    newFile(b, content: 'export "a.dart"; class B {}');
+    newFile(c, content: 'import "d.dart"; class C {}');
+    newFile(d, content: 'class D {}');
+
+    driver.addFile(a);
+    driver.addFile(b);
+    driver.addFile(c);
+    // Don't add d.dart, it is referenced implicitly.
+
+    _assertTopLevelDeclarations(
+        await driver.getTopLevelNameDeclarations('A'), [a, b], [false, true]);
+
+    _assertTopLevelDeclarations(
+        await driver.getTopLevelNameDeclarations('B'), [b], [false]);
+
+    _assertTopLevelDeclarations(
+        await driver.getTopLevelNameDeclarations('C'), [c], [false]);
+
+    _assertTopLevelDeclarations(
+        await driver.getTopLevelNameDeclarations('D'), [d], [false]);
+
+    _assertTopLevelDeclarations(
+        await driver.getTopLevelNameDeclarations('X'), [], []);
+  }
+
+  test_getTopLevelNameDeclarations_discover() async {
+    var t = convertPath('/test/lib/test.dart');
+    var a1 = convertPath('/aaa/lib/a1.dart');
+    var a2 = convertPath('/aaa/lib/src/a2.dart');
+    var b = convertPath('/bbb/lib/b.dart');
+    var c = convertPath('/ccc/lib/c.dart');
+
+    newFile(t, content: 'class T {}');
+    newFile(a1, content: 'class A1 {}');
+    newFile(a2, content: 'class A2 {}');
+    newFile(b, content: 'class B {}');
+    newFile(c, content: 'class C {}');
+
+    driver.addFile(t);
+    // Don't add a1.dart, a2.dart, or b.dart - they should be discovered.
+    // And c.dart is not in .packages, so should not be discovered.
+
+    _assertTopLevelDeclarations(
+        await driver.getTopLevelNameDeclarations('T'), [t], [false]);
+
+    _assertTopLevelDeclarations(
+        await driver.getTopLevelNameDeclarations('A1'), [a1], [false]);
+
+    _assertTopLevelDeclarations(
+        await driver.getTopLevelNameDeclarations('A2'), [a2], [false]);
+
+    _assertTopLevelDeclarations(
+        await driver.getTopLevelNameDeclarations('B'), [b], [false]);
+
+    _assertTopLevelDeclarations(
+        await driver.getTopLevelNameDeclarations('C'), [], []);
+  }
+
+  test_getTopLevelNameDeclarations_parts() async {
+    var a = convertPath('/test/lib/a.dart');
+    var b = convertPath('/test/lib/b.dart');
+    var c = convertPath('/test/lib/c.dart');
+
+    newFile(a, content: r'''
+library lib;
+part 'b.dart';
+part 'c.dart';
+class A {}
+''');
+    newFile(b, content: 'part of lib; class B {}');
+    newFile(c, content: 'part of lib; class C {}');
+
+    driver.addFile(a);
+    driver.addFile(b);
+    driver.addFile(c);
+
+    _assertTopLevelDeclarations(
+        await driver.getTopLevelNameDeclarations('A'), [a], [false]);
+
+    _assertTopLevelDeclarations(
+        await driver.getTopLevelNameDeclarations('B'), [a], [false]);
+
+    _assertTopLevelDeclarations(
+        await driver.getTopLevelNameDeclarations('C'), [a], [false]);
+
+    _assertTopLevelDeclarations(
+        await driver.getTopLevelNameDeclarations('X'), [], []);
+  }
+
   test_getUnitElement() async {
     String content = r'''
 foo(int p) {}
@@ -3416,6 +3513,20 @@
     }
   }
 
+  void _assertTopLevelDeclarations(
+      List<TopLevelDeclarationInSource> declarations,
+      List<String> expectedFiles,
+      List<bool> expectedIsExported) {
+    expect(expectedFiles, hasLength(expectedIsExported.length));
+    for (int i = 0; i < expectedFiles.length; i++) {
+      expect(declarations,
+          contains(predicate((TopLevelDeclarationInSource declaration) {
+        return declaration.source.fullName == expectedFiles[i] &&
+            declaration.isExported == expectedIsExported[i];
+      })));
+    }
+  }
+
   void _expectCircularityError(EvaluationResultImpl evaluationResult) {
     expect(evaluationResult, isNotNull);
     expect(evaluationResult.value, isNull);
diff --git a/pkg/analyzer/test/src/dart/analysis/file_state_test.dart b/pkg/analyzer/test/src/dart/analysis/file_state_test.dart
index 24ec015..98a279e 100644
--- a/pkg/analyzer/test/src/dart/analysis/file_state_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/file_state_test.dart
@@ -7,9 +7,11 @@
 
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/src/dart/analysis/byte_store.dart';
+import 'package:analyzer/src/dart/analysis/driver.dart';
 import 'package:analyzer/src/dart/analysis/file_state.dart';
 import 'package:analyzer/src/dart/analysis/library_graph.dart';
 import 'package:analyzer/src/dart/analysis/performance_logger.dart';
+import 'package:analyzer/src/dart/analysis/top_level_declaration.dart';
 import 'package:analyzer/src/file_system/file_system.dart';
 import 'package:analyzer/src/generated/engine.dart'
     show AnalysisOptions, AnalysisOptionsImpl;
@@ -103,6 +105,249 @@
         unorderedEquals(['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']));
   }
 
+  test_exportedTopLevelDeclarations_cycle() {
+    String a = convertPath('/aaa/lib/a.dart');
+    String b = convertPath('/aaa/lib/b.dart');
+    String c = convertPath('/aaa/lib/c.dart');
+    newFile(a, content: r'''
+export 'b.dart';
+class A {}
+''');
+    newFile(b, content: r'''
+export 'c.dart';
+class B {}
+''');
+    newFile(c, content: r'''
+export 'a.dart';
+class C {}
+''');
+    _assertExportedTopLevelDeclarations(a, ['A', 'B', 'C']);
+
+    // We asked for 'a', and it was computed.
+    // But 'b' and 'c' are not computed, because we detect that there is
+    // cycle with 'a', so we cannot get all exported declarations of 'a'.
+    _assertHasComputedExportedDeclarations([a]);
+  }
+
+  test_exportedTopLevelDeclarations_cycle_anotherOutsideCycle() {
+    String a = convertPath('/aaa/lib/a.dart');
+    String b = convertPath('/aaa/lib/b.dart');
+    String c = convertPath('/aaa/lib/c.dart');
+    String d = convertPath('/aaa/lib/d.dart');
+    newFile(a, content: r'''
+export 'b.dart';
+class A {}
+''');
+    newFile(b, content: r'''
+export 'c.dart';
+class B {}
+''');
+    newFile(c, content: r'''
+export 'b.dart';
+export 'd.dart';
+class C {}
+''');
+    newFile(d, content: r'''
+class D {}
+''');
+    _assertExportedTopLevelDeclarations(a, ['A', 'B', 'C', 'D']);
+
+    // To compute 'a' we compute 'b'.
+    // But 'c' is not computed, because of the cycle [b, c].
+    // However 'd' is not a part of a cycle, so it is computed too.
+    _assertHasComputedExportedDeclarations([a, b, d]);
+  }
+
+  test_exportedTopLevelDeclarations_cycle_onSequence() {
+    String a = convertPath('/aaa/lib/a.dart');
+    String b = convertPath('/aaa/lib/b.dart');
+    String c = convertPath('/aaa/lib/c.dart');
+    String d = convertPath('/aaa/lib/d.dart');
+    String e = convertPath('/aaa/lib/e.dart');
+    newFile(a, content: r'''
+export 'b.dart';
+class A {}
+''');
+    newFile(b, content: r'''
+export 'c.dart';
+class B {}
+''');
+    newFile(c, content: r'''
+export 'd.dart';
+class C {}
+''');
+    newFile(d, content: r'''
+export 'e.dart';
+class D {}
+''');
+    newFile(e, content: r'''
+export 'c.dart';
+class E {}
+''');
+    // We compute 'a'.
+    // To compute it we also compute 'b' and 'c'.
+    // But 'd' and 'e' are not computed, because of the cycle [c, d, e].
+    _assertExportedTopLevelDeclarations(a, ['A', 'B', 'C', 'D', 'E']);
+    _assertHasComputedExportedDeclarations([a, b, c]);
+
+    // We compute 'd', and try to compute 'e', because 'd' needs 'e'; 'e' can
+    // be computed because 'c' is ready, so the cycle [c, d, e] is broken.
+    _assertExportedTopLevelDeclarations(d, ['C', 'D', 'E']);
+    _assertHasComputedExportedDeclarations([a, b, c, d, e]);
+  }
+
+  test_exportedTopLevelDeclarations_export() {
+    String a = convertPath('/aaa/lib/a.dart');
+    String b = convertPath('/aaa/lib/b.dart');
+    newFile(a, content: r'''
+class A {}
+''');
+    newFile(b, content: r'''
+export 'a.dart';
+class B {}
+''');
+    _assertExportedTopLevelDeclarations(b, ['A', 'B']);
+    _assertHasComputedExportedDeclarations([a, b]);
+  }
+
+  test_exportedTopLevelDeclarations_export2_show() {
+    String a = convertPath('/aaa/lib/a.dart');
+    String b = convertPath('/aaa/lib/b.dart');
+    String c = convertPath('/aaa/lib/c.dart');
+    newFile(a, content: r'''
+class A1 {}
+class A2 {}
+class A3 {}
+''');
+    newFile(b, content: r'''
+export 'a.dart' show A1, A2;
+class B1 {}
+class B2 {}
+''');
+    newFile(c, content: r'''
+export 'b.dart' show A2, A3, B1;
+class C {}
+''');
+    _assertExportedTopLevelDeclarations(c, ['A2', 'B1', 'C']);
+    _assertHasComputedExportedDeclarations([a, b, c]);
+  }
+
+  test_exportedTopLevelDeclarations_export_flushOnChange() {
+    String a = convertPath('/aaa/lib/a.dart');
+    String b = convertPath('/aaa/lib/b.dart');
+    newFile(a, content: r'''
+class A {}
+''');
+    newFile(b, content: r'''
+export 'a.dart';
+class B {}
+''');
+
+    // Initial exported declarations.
+    _assertExportedTopLevelDeclarations(b, ['A', 'B']);
+
+    // Update a.dart, so a.dart and b.dart exported declarations are flushed.
+    newFile(a, content: 'class A {} class A2 {}');
+    fileSystemState.getFileForPath(a).refresh();
+    _assertExportedTopLevelDeclarations(b, ['A', 'A2', 'B']);
+  }
+
+  test_exportedTopLevelDeclarations_export_hide() {
+    String a = convertPath('/aaa/lib/a.dart');
+    String b = convertPath('/aaa/lib/b.dart');
+    newFile(a, content: r'''
+class A1 {}
+class A2 {}
+class A3 {}
+''');
+    newFile(b, content: r'''
+export 'a.dart' hide A2;
+class B {}
+''');
+    _assertExportedTopLevelDeclarations(b, ['A1', 'A3', 'B']);
+  }
+
+  test_exportedTopLevelDeclarations_export_preferLocal() {
+    String a = convertPath('/aaa/lib/a.dart');
+    String b = convertPath('/aaa/lib/b.dart');
+    newFile(a, content: r'''
+class V {}
+''');
+    newFile(b, content: r'''
+export 'a.dart';
+int V;
+''');
+    FileState file = fileSystemState.getFileForPath(b);
+    Map<String, TopLevelDeclaration> declarations =
+        file.exportedTopLevelDeclarations;
+    expect(declarations.keys, unorderedEquals(['V']));
+    expect(declarations['V'].kind, TopLevelDeclarationKind.variable);
+  }
+
+  test_exportedTopLevelDeclarations_export_show() {
+    String a = convertPath('/aaa/lib/a.dart');
+    String b = convertPath('/aaa/lib/b.dart');
+    newFile(a, content: r'''
+class A1 {}
+class A2 {}
+''');
+    newFile(b, content: r'''
+export 'a.dart' show A2;
+class B {}
+''');
+    _assertExportedTopLevelDeclarations(b, ['A2', 'B']);
+  }
+
+  test_exportedTopLevelDeclarations_export_show2() {
+    String a = convertPath('/aaa/lib/a.dart');
+    String b = convertPath('/aaa/lib/b.dart');
+    String c = convertPath('/aaa/lib/c.dart');
+    String d = convertPath('/aaa/lib/d.dart');
+    newFile(a, content: r'''
+export 'b.dart' show Foo;
+export 'c.dart' show Bar;
+''');
+    newFile(b, content: r'''
+export 'd.dart';
+''');
+    newFile(c, content: r'''
+export 'd.dart';
+''');
+    newFile(d, content: r'''
+class Foo {}
+class Bar {}
+''');
+    _assertExportedTopLevelDeclarations(a, ['Foo', 'Bar']);
+  }
+
+  test_exportedTopLevelDeclarations_import() {
+    String a = convertPath('/aaa/lib/a.dart');
+    String b = convertPath('/aaa/lib/b.dart');
+    newFile(a, content: r'''
+class A {}
+''');
+    newFile(b, content: r'''
+import 'a.dart';
+class B {}
+''');
+    _assertExportedTopLevelDeclarations(b, ['B']);
+  }
+
+  test_exportedTopLevelDeclarations_parts() {
+    String a = convertPath('/aaa/lib/a.dart');
+    String a2 = convertPath('/aaa/lib/a2.dart');
+    newFile(a, content: r'''
+library lib;
+part 'a2.dart';
+class A1 {}
+''');
+    newFile(a2, content: r'''
+part of lib;
+class A2 {}
+''');
+    _assertExportedTopLevelDeclarations(a, ['A1', 'A2']);
+  }
+
   test_getFileForPath_doesNotExist() {
     String path = convertPath('/aaa/lib/a.dart');
     FileState file = fileSystemState.getFileForPath(path);
@@ -117,8 +362,13 @@
     expect(_excludeSdk(file.directReferencedFiles), isEmpty);
     expect(file.isPart, isFalse);
     expect(file.library, isNull);
-    expect(file.unlinked, isNotNull);
-    expect(file.unlinked.classes, isEmpty);
+    if (AnalysisDriver.useSummary2) {
+      expect(file.unlinked2, isNotNull);
+      expect(file.unlinked2.exports, isEmpty);
+    } else {
+      expect(file.unlinked, isNotNull);
+      expect(file.unlinked.classes, isEmpty);
+    }
   }
 
   test_getFileForPath_emptyUri() {
@@ -208,9 +458,13 @@
 
     expect(file.isPart, isFalse);
     expect(file.library, isNull);
-    expect(file.unlinked, isNotNull);
-    expect(file.unlinked.classes, hasLength(1));
-    expect(file.unlinked.classes[0].name, 'A1');
+    if (AnalysisDriver.useSummary2) {
+      expect(file.unlinked2, isNotNull);
+    } else {
+      expect(file.unlinked, isNotNull);
+      expect(file.unlinked.classes, hasLength(1));
+      expect(file.unlinked.classes[0].name, 'A1');
+    }
 
     expect(_excludeSdk(file.importedFiles), hasLength(2));
     expect(file.importedFiles[0].path, a2);
@@ -281,9 +535,13 @@
     expect(file_a2.path, a2);
     expect(file_a2.uri, Uri.parse('package:aaa/a2.dart'));
 
-    expect(file_a2.unlinked, isNotNull);
-    expect(file_a2.unlinked.classes, hasLength(1));
-    expect(file_a2.unlinked.classes[0].name, 'A2');
+    if (AnalysisDriver.useSummary2) {
+      expect(file_a2.unlinked2, isNotNull);
+    } else {
+      expect(file_a2.unlinked, isNotNull);
+      expect(file_a2.unlinked.classes, hasLength(1));
+      expect(file_a2.unlinked.classes[0].name, 'A2');
+    }
 
     expect(_excludeSdk(file_a2.importedFiles), isEmpty);
     expect(file_a2.exportedFiles, isEmpty);
@@ -500,7 +758,11 @@
 class A {}
 ''');
     FileState file = fileSystemState.getFileForPath(path);
-    expect(file.unlinked.classes[0].name, 'A');
+    if (AnalysisDriver.useSummary2) {
+      expect(file.definedTopLevelNames, contains('A'));
+    } else {
+      expect(file.unlinked.classes[0].name, 'A');
+    }
     List<int> signature = file.apiSignature;
 
     // Update the resource and refresh the file state.
@@ -510,7 +772,11 @@
     bool apiSignatureChanged = file.refresh();
     expect(apiSignatureChanged, isTrue);
 
-    expect(file.unlinked.classes[0].name, 'B');
+    if (AnalysisDriver.useSummary2) {
+      expect(file.definedTopLevelNames, contains('B'));
+    } else {
+      expect(file.unlinked.classes[0].name, 'B');
+    }
     expect(file.apiSignature, isNot(signature));
   }
 
@@ -546,14 +812,22 @@
 
     // Get the file, prepare unlinked.
     FileState file = fileSystemState.getFileForPath(path);
-    expect(file.unlinked, isNotNull);
+    if (AnalysisDriver.useSummary2) {
+      expect(file.unlinked2, isNotNull);
+    } else {
+      expect(file.unlinked, isNotNull);
+    }
 
     // Make the unlinked unit in the byte store zero-length, damaged.
     byteStore.put(file.test.unlinkedKey, <int>[]);
 
     // Refresh should not fail, zero bytes in the store are ignored.
     file.refresh();
-    expect(file.unlinked, isNotNull);
+    if (AnalysisDriver.useSummary2) {
+      expect(file.unlinked2, isNotNull);
+    } else {
+      expect(file.unlinked, isNotNull);
+    }
   }
 
   test_subtypedNames() {
@@ -567,6 +841,52 @@
     expect(file.referencedNames, unorderedEquals(['A', 'B', 'C', 'D']));
   }
 
+  test_topLevelDeclarations() {
+    String path = convertPath('/aaa/lib/a.dart');
+    newFile(path, content: r'''
+class C {}
+typedef F();
+enum E {E1, E2}
+mixin M {}
+void f() {}
+var V1;
+get V2 => null;
+set V3(_) {}
+get V4 => null;
+set V4(_) {}
+
+class _C {}
+typedef _F();
+enum _E {E1, E2}
+mixin _M {}
+void _f() {}
+var _V1;
+get _V2 => null;
+set _V3(_) {}
+''');
+    FileState file = fileSystemState.getFileForPath(path);
+
+    Map<String, TopLevelDeclaration> declarations = file.topLevelDeclarations;
+
+    void assertHas(String name, TopLevelDeclarationKind kind) {
+      expect(declarations[name]?.kind, kind);
+    }
+
+    expect(
+      declarations.keys,
+      unorderedEquals(['C', 'F', 'E', 'M', 'f', 'V1', 'V2', 'V3', 'V4']),
+    );
+    assertHas('C', TopLevelDeclarationKind.type);
+    assertHas('F', TopLevelDeclarationKind.type);
+    assertHas('E', TopLevelDeclarationKind.type);
+    assertHas('M', TopLevelDeclarationKind.type);
+    assertHas('f', TopLevelDeclarationKind.function);
+    assertHas('V1', TopLevelDeclarationKind.variable);
+    assertHas('V2', TopLevelDeclarationKind.variable);
+    assertHas('V3', TopLevelDeclarationKind.variable);
+    assertHas('V4', TopLevelDeclarationKind.variable);
+  }
+
   test_transitiveSignature() {
     String pa = convertPath('/aaa/lib/a.dart');
     String pb = convertPath('/aaa/lib/b.dart');
@@ -627,11 +947,24 @@
     expect(bSignature, isNot(aSignature));
   }
 
+  void _assertExportedTopLevelDeclarations(String path, List<String> expected) {
+    FileState file = fileSystemState.getFileForPath(path);
+    Map<String, TopLevelDeclaration> declarations =
+        file.exportedTopLevelDeclarations;
+    expect(declarations.keys, unorderedEquals(expected));
+  }
+
   void _assertFilesWithoutLibraryCycle(List<FileState> expected) {
     var actual = fileSystemState.test.filesWithoutLibraryCycle;
     expect(_excludeSdk(actual), unorderedEquals(expected));
   }
 
+  void _assertHasComputedExportedDeclarations(List<String> expectedPathList) {
+    FileSystemStateTestView test = fileSystemState.test;
+    expect(test.librariesWithComputedExportedDeclarations.map((f) => f.path),
+        unorderedEquals(expectedPathList));
+  }
+
   void _assertIsUnresolvedFile(FileState file) {
     expect(file.path, isNull);
     expect(file.uri, isNull);
diff --git a/pkg/analyzer/test/src/dart/resolution/constant_test.dart b/pkg/analyzer/test/src/dart/resolution/constant_test.dart
index 0283b13..6430fc1 100644
--- a/pkg/analyzer/test/src/dart/resolution/constant_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/constant_test.dart
@@ -2,6 +2,7 @@
 // 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 'package:analyzer/dart/element/type.dart';
 import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/error/codes.dart';
 import 'package:test/test.dart';
@@ -20,66 +21,6 @@
 class ConstantDriverTest extends DriverResolutionTest with ConstantMixin {}
 
 mixin ConstantMixin implements ResolutionTest {
-  test_annotation_constructor_named() async {
-    newFile('/test/lib/a.dart', content: r'''
-class A {
-  final int f;
-  const A.named(this.f);
-}
-''');
-
-    newFile('/test/lib/b.dart', content: r'''
-import 'a.dart';
-
-@A.named(42)
-class B {}
-''');
-
-    addTestFile(r'''
-import 'b.dart';
-
-B b;
-''');
-    await resolveTestFile();
-    assertNoTestErrors();
-
-    var classB = findNode.typeName('B b;').name.staticElement;
-    var annotation = classB.metadata.single;
-    var value = annotation.computeConstantValue();
-    expect(value, isNotNull);
-    expect(value.getField('f').toIntValue(), 42);
-  }
-
-  test_annotation_constructor_unnamed() async {
-    newFile('/test/lib/a.dart', content: r'''
-class A {
-  final int f;
-  const A(this.f);
-}
-''');
-
-    newFile('/test/lib/b.dart', content: r'''
-import 'a.dart';
-
-@A(42)
-class B {}
-''');
-
-    addTestFile(r'''
-import 'b.dart';
-
-B b;
-''');
-    await resolveTestFile();
-    assertNoTestErrors();
-
-    var classB = findNode.typeName('B b;').name.staticElement;
-    var annotation = classB.metadata.single;
-    var value = annotation.computeConstantValue();
-    expect(value, isNotNull);
-    expect(value.getField('f').toIntValue(), 42);
-  }
-
   test_constantValue_defaultParameter_noDefaultValue() async {
     newFile('/test/lib/a.dart', content: r'''
 class A {
@@ -147,4 +88,39 @@
       CompileTimeErrorCode.CONST_NOT_INITIALIZED,
     ]);
   }
+
+  test_functionType_element_typeArguments() async {
+    newFile('/test/lib/a.dart', content: r'''
+typedef F<T> = T Function(int);
+const a = C<F<double>>();
+
+class C<T> {
+  const C();
+}
+''');
+    addTestFile(r'''
+import 'a.dart';
+
+const v = a;
+''');
+    await resolveTestFile();
+    assertNoTestErrors();
+
+    var v = findElement.topVar('v') as ConstVariableElement;
+    var value = v.computeConstantValue();
+
+    var type = value.type as InterfaceType;
+    assertElementTypeString(type, 'C<double Function(int)>');
+
+    expect(type.typeArguments, hasLength(1));
+    var typeArgument = type.typeArguments[0] as FunctionType;
+    assertElementTypeString(typeArgument, 'double Function(int)');
+
+    // The element and type arguments are available for the function type.
+    var importFind = findElement.importFind('package:test/a.dart');
+    var elementF = importFind.functionTypeAlias('F');
+    expect(typeArgument.element, elementF.function);
+    expect(typeArgument.element.enclosingElement, elementF);
+    assertElementTypeStrings(typeArgument.typeArguments, ['double']);
+  }
 }
diff --git a/pkg/analyzer/test/src/dart/resolution/flow_analysis_test.dart b/pkg/analyzer/test/src/dart/resolution/flow_analysis_test.dart
index 5dd9420..6a39734 100644
--- a/pkg/analyzer/test/src/dart/resolution/flow_analysis_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/flow_analysis_test.dart
@@ -1308,13 +1308,13 @@
 
     var unit = result.unit;
 
-    var loopAssignedVariables = AssignedVariables();
-    unit.accept(_AssignedVariablesVisitor(loopAssignedVariables));
+    var assignedVariables = AssignedVariables<Statement, VariableElement>();
+    unit.accept(_AssignedVariablesVisitor(assignedVariables));
 
     var typeSystem = unit.declaredElement.context.typeSystem;
     unit.accept(_AstVisitor(
       typeSystem,
-      loopAssignedVariables,
+      assignedVariables,
       {},
       readBeforeWritten,
       [],
@@ -1623,13 +1623,13 @@
 
     var unit = result.unit;
 
-    var loopAssignedVariables = AssignedVariables();
-    unit.accept(_AssignedVariablesVisitor(loopAssignedVariables));
+    var assignedVariables = AssignedVariables<Statement, VariableElement>();
+    unit.accept(_AssignedVariablesVisitor(assignedVariables));
 
     var typeSystem = unit.declaredElement.context.typeSystem;
     unit.accept(_AstVisitor(
       typeSystem,
-      loopAssignedVariables,
+      assignedVariables,
       {},
       [],
       nullableNodes,
@@ -2070,13 +2070,13 @@
 
     var unit = result.unit;
 
-    var loopAssignedVariables = AssignedVariables();
-    unit.accept(_AssignedVariablesVisitor(loopAssignedVariables));
+    var assignedVariables = AssignedVariables<Statement, VariableElement>();
+    unit.accept(_AssignedVariablesVisitor(assignedVariables));
 
     var typeSystem = unit.declaredElement.context.typeSystem;
     unit.accept(_AstVisitor(
       typeSystem,
-      loopAssignedVariables,
+      assignedVariables,
       {},
       [],
       [],
@@ -2903,13 +2903,13 @@
 
     var unit = result.unit;
 
-    var loopAssignedVariables = AssignedVariables();
-    unit.accept(_AssignedVariablesVisitor(loopAssignedVariables));
+    var assignedVariables = AssignedVariables<Statement, VariableElement>();
+    unit.accept(_AssignedVariablesVisitor(assignedVariables));
 
     var typeSystem = unit.declaredElement.context.typeSystem;
     unit.accept(_AstVisitor(
       typeSystem,
-      loopAssignedVariables,
+      assignedVariables,
       promotedTypes,
       [],
       [],
@@ -3018,7 +3018,8 @@
 class _AstVisitor extends GeneralizingAstVisitor<void> {
   static final trueLiteral = astFactory.booleanLiteral(null, true);
 
-  final TypeOperations<DartType> typeOperations;
+  final NodeOperations<Expression> nodeOperations;
+  final TypeOperations<VariableElement, DartType> typeOperations;
   final AssignedVariables assignedVariables;
   final Map<AstNode, DartType> promotedTypes;
   final List<LocalVariableElement> readBeforeWritten;
@@ -3027,7 +3028,7 @@
   final List<AstNode> unreachableNodes;
   final List<FunctionBody> functionBodiesThatDontComplete;
 
-  FlowAnalysis<DartType> flow;
+  FlowAnalysis<Statement, Expression, VariableElement, DartType> flow;
 
   _AstVisitor(
       TypeSystem typeSystem,
@@ -3038,7 +3039,8 @@
       this.nonNullableNodes,
       this.unreachableNodes,
       this.functionBodiesThatDontComplete)
-      : typeOperations = _TypeSystemTypeOperations(typeSystem);
+      : nodeOperations = _NodeOperations(),
+        typeOperations = _TypeSystemTypeOperations(typeSystem);
 
   @override
   void visitAssignmentExpression(AssignmentExpression node) {
@@ -3080,19 +3082,19 @@
     if (operator == TokenType.AMPERSAND_AMPERSAND) {
       left.accept(this);
 
-      flow.logicalAnd_rightBegin(node);
+      flow.logicalAnd_rightBegin(node, node.leftOperand);
       _checkUnreachableNode(node.rightOperand);
       right.accept(this);
 
-      flow.logicalAnd_end(node);
+      flow.logicalAnd_end(node, node.rightOperand);
     } else if (operator == TokenType.BAR_BAR) {
       left.accept(this);
 
-      flow.logicalOr_rightBegin(node);
+      flow.logicalOr_rightBegin(node, node.leftOperand);
       _checkUnreachableNode(node.rightOperand);
       right.accept(this);
 
-      flow.logicalOr_end(node);
+      flow.logicalOr_end(node, node.rightOperand);
     } else if (operator == TokenType.BANG_EQ) {
       left.accept(this);
       right.accept(this);
@@ -3133,7 +3135,11 @@
     var isFlowOwner = flow == null;
 
     if (isFlowOwner) {
-      flow = FlowAnalysis<DartType>(typeOperations, node);
+      flow = FlowAnalysis<Statement, Expression, VariableElement, DartType>(
+        nodeOperations,
+        typeOperations,
+        _FunctionBodyAccess(node),
+      );
 
       var function = node.parent;
       if (function is FunctionExpression) {
@@ -3149,7 +3155,10 @@
     super.visitBlockFunctionBody(node);
 
     if (isFlowOwner) {
-      readBeforeWritten.addAll(flow.readBeforeWritten);
+      for (var variable in flow.readBeforeWritten) {
+        assert(variable is LocalVariableElement);
+        readBeforeWritten.add(variable);
+      }
 
       if (!flow.isReachable) {
         functionBodiesThatDontComplete.add(node);
@@ -3186,16 +3195,16 @@
 
     condition.accept(this);
 
-    flow.conditional_thenBegin(node);
+    flow.conditional_thenBegin(node, node.condition);
     _checkUnreachableNode(node.thenExpression);
     thenExpression.accept(this);
     var isBool = thenExpression.staticType.isDartCoreBool;
 
-    flow.conditional_elseBegin(node, isBool);
+    flow.conditional_elseBegin(node, node.thenExpression, isBool);
     _checkUnreachableNode(node.elseExpression);
     elseExpression.accept(this);
 
-    flow.conditional_end(node, isBool);
+    flow.conditional_end(node, node.elseExpression, isBool);
   }
 
   @override
@@ -3218,7 +3227,7 @@
     flow.doStatement_conditionBegin();
     condition.accept(this);
 
-    flow.doStatement_end(node);
+    flow.doStatement_end(node, node.condition);
   }
 
   @override
@@ -3285,7 +3294,7 @@
 
     condition.accept(this);
 
-    flow.ifStatement_thenBegin(node);
+    flow.ifStatement_thenBegin(node, node.condition);
     thenStatement.accept(this);
 
     if (elseStatement != null) {
@@ -3305,7 +3314,12 @@
     if (expression is SimpleIdentifier) {
       var element = expression.staticElement;
       if (element is VariableElement) {
-        flow.isExpression_end(node, element, typeAnnotation.type);
+        flow.isExpression_end(
+          node,
+          element,
+          node.notOperator != null,
+          typeAnnotation.type,
+        );
       }
     }
   }
@@ -3317,7 +3331,7 @@
     var operator = node.operator.type;
     if (operator == TokenType.BANG) {
       operand.accept(this);
-      flow.logicalNot_end(node);
+      flow.logicalNot_end(node, node.operand);
     } else {
       operand.accept(this);
     }
@@ -3385,7 +3399,7 @@
       var member = members[i];
 
       flow.switchStatement_beginCase(
-        member.labels.isNotEmpty ? assignedInCases : AssignedVariables.emptySet,
+        member.labels.isNotEmpty ? assignedInCases : assignedVariables.emptySet,
       );
       member.accept(this);
 
@@ -3460,7 +3474,7 @@
     flow.whileStatement_conditionBegin(assignedVariables[node]);
     condition.accept(this);
 
-    flow.whileStatement_bodyBegin(node);
+    flow.whileStatement_bodyBegin(node, node.condition);
     body.accept(this);
 
     flow.whileStatement_end();
@@ -3532,7 +3546,34 @@
   }
 }
 
-class _TypeSystemTypeOperations implements TypeOperations<DartType> {
+class _FunctionBodyAccess implements FunctionBodyAccess<VariableElement> {
+  final FunctionBody node;
+
+  _FunctionBodyAccess(this.node);
+
+  @override
+  bool isPotentiallyMutatedInClosure(VariableElement variable) {
+    return node.isPotentiallyMutatedInClosure(variable);
+  }
+
+  @override
+  bool isPotentiallyMutatedInScope(VariableElement variable) {
+    return node.isPotentiallyMutatedInScope(variable);
+  }
+}
+
+class _NodeOperations implements NodeOperations<Expression> {
+  @override
+  Expression unwrapParenthesized(Expression node) {
+    while (node is ParenthesizedExpression) {
+      node = (node as ParenthesizedExpression).expression;
+    }
+    return node;
+  }
+}
+
+class _TypeSystemTypeOperations
+    implements TypeOperations<VariableElement, DartType> {
   final TypeSystem typeSystem;
 
   _TypeSystemTypeOperations(this.typeSystem);
@@ -3546,4 +3587,9 @@
   bool isSubtypeOf(DartType leftType, DartType rightType) {
     return typeSystem.isSubtypeOf(leftType, rightType);
   }
+
+  @override
+  bool isLocalVariable(VariableElement element) {
+    return element is LocalVariableElement;
+  }
 }
diff --git a/pkg/analyzer/test/src/dart/resolution/function_type_alias_test.dart b/pkg/analyzer/test/src/dart/resolution/function_type_alias_test.dart
new file mode 100644
index 0000000..c233a3d
--- /dev/null
+++ b/pkg/analyzer/test/src/dart/resolution/function_type_alias_test.dart
@@ -0,0 +1,38 @@
+// Copyright (c) 2019, 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 'package:analyzer/dart/element/type.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'driver_resolution.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(FunctionTypeAliasResolutionTest);
+  });
+}
+
+@reflectiveTest
+class FunctionTypeAliasResolutionTest extends DriverResolutionTest {
+  test_type_element() async {
+    addTestFile(r'''
+G<int> g;
+
+typedef T G<T>();
+''');
+    await resolveTestFile();
+
+    FunctionType type = findElement.topVar('g').type;
+    assertElementTypeString(type, 'int Function()');
+
+    var typedefG = findElement.genericTypeAlias('G');
+    var functionG = typedefG.function;
+
+    expect(type.element, functionG);
+    expect(type.element?.enclosingElement, typedefG);
+
+    assertElementTypeStrings(type.typeArguments, ['int']);
+  }
+}
diff --git a/pkg/analyzer/test/src/dart/resolution/generic_type_alias_test.dart b/pkg/analyzer/test/src/dart/resolution/generic_type_alias_test.dart
index 7974854..5dde449 100644
--- a/pkg/analyzer/test/src/dart/resolution/generic_type_alias_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/generic_type_alias_test.dart
@@ -2,12 +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 'package:analyzer/dart/element/type.dart';
 import 'package:analyzer/src/error/codes.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
 import 'driver_resolution.dart';
-import 'resolution.dart';
 
 main() {
   defineReflectiveSuite(() {
@@ -16,10 +16,7 @@
 }
 
 @reflectiveTest
-class GenericTypeAliasDriverResolutionTest extends DriverResolutionTest
-    with GenericTypeAliasResolutionMixin {}
-
-mixin GenericTypeAliasResolutionMixin implements ResolutionTest {
+class GenericTypeAliasDriverResolutionTest extends DriverResolutionTest {
   test_genericFunctionTypeCannotBeTypeArgument_def_class() async {
     addTestFile(r'''
 class C<T> {}
@@ -124,6 +121,26 @@
     assertNoTestErrors();
   }
 
+  test_type_element() async {
+    addTestFile(r'''
+G<int> g;
+
+typedef G<T> = T Function(double);
+''');
+    await resolveTestFile();
+
+    FunctionType type = findElement.topVar('g').type;
+    assertElementTypeString(type, 'int Function(double)');
+
+    var typedefG = findElement.genericTypeAlias('G');
+    var functionG = typedefG.function;
+
+    expect(type.element, functionG);
+    expect(type.element?.enclosingElement, typedefG);
+
+    assertElementTypeStrings(type.typeArguments, ['int']);
+  }
+
   test_typeParameters() async {
     addTestFile(r'''
 class A {}
diff --git a/pkg/analyzer/test/src/dart/resolution/metadata_test.dart b/pkg/analyzer/test/src/dart/resolution/metadata_test.dart
new file mode 100644
index 0000000..14dc87d
--- /dev/null
+++ b/pkg/analyzer/test/src/dart/resolution/metadata_test.dart
@@ -0,0 +1,108 @@
+// Copyright (c) 2019, 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 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'driver_resolution.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(MetadataResolutionTest);
+  });
+}
+
+@reflectiveTest
+class MetadataResolutionTest extends DriverResolutionTest {
+  test_constructor_named() async {
+    newFile('/test/lib/a.dart', content: r'''
+class A {
+  final int f;
+  const A.named(this.f);
+}
+''');
+
+    newFile('/test/lib/b.dart', content: r'''
+import 'a.dart';
+
+@A.named(42)
+class B {}
+''');
+
+    addTestFile(r'''
+import 'b.dart';
+
+B b;
+''');
+    await resolveTestFile();
+    assertNoTestErrors();
+
+    var classB = findNode.typeName('B b;').name.staticElement;
+    var annotation = classB.metadata.single;
+    var value = annotation.computeConstantValue();
+    expect(value, isNotNull);
+    expect(value.getField('f').toIntValue(), 42);
+  }
+
+  test_constructor_unnamed() async {
+    newFile('/test/lib/a.dart', content: r'''
+class A {
+  final int f;
+  const A(this.f);
+}
+''');
+
+    newFile('/test/lib/b.dart', content: r'''
+import 'a.dart';
+
+@A(42)
+class B {}
+''');
+
+    addTestFile(r'''
+import 'b.dart';
+
+B b;
+''');
+    await resolveTestFile();
+    assertNoTestErrors();
+
+    var classB = findNode.typeName('B b;').name.staticElement;
+    var annotation = classB.metadata.single;
+    var value = annotation.computeConstantValue();
+    expect(value, isNotNull);
+    expect(value.getField('f').toIntValue(), 42);
+  }
+
+  test_implicitConst() async {
+    newFile('/test/lib/a.dart', content: r'''
+class A {
+  final int f;
+  const A(this.f);
+}
+
+class B {
+  final A a;
+  const B(this.a);
+}
+
+@B( A(42) )
+class C {}
+''');
+
+    addTestFile(r'''
+import 'a.dart';
+
+C c;
+''');
+    await resolveTestFile();
+    assertNoTestErrors();
+
+    var classC = findNode.typeName('C c;').name.staticElement;
+    var annotation = classC.metadata.single;
+    var value = annotation.computeConstantValue();
+    expect(value, isNotNull);
+    expect(value.getField('a').getField('f').toIntValue(), 42);
+  }
+}
diff --git a/pkg/analyzer/test/src/dart/resolution/non_nullable_test.dart b/pkg/analyzer/test/src/dart/resolution/non_nullable_test.dart
index c722521..35f6532 100644
--- a/pkg/analyzer/test/src/dart/resolution/non_nullable_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/non_nullable_test.dart
@@ -26,243 +26,6 @@
   @override
   bool get typeToStringWithNullability => true;
 
-  test_local_getterNullAwareAccess_interfaceType() async {
-    addTestFile(r'''
-m() {
-  int? x;
-  return x?.isEven;
-}
-''');
-
-    await resolveTestFile();
-    assertNoTestErrors();
-    assertType(findNode.propertyAccess('x?.isEven'), 'bool?');
-  }
-
-  test_local_methodNullAwareCall_interfaceType() async {
-    await addTestFile(r'''
-class C {
-  bool x() => true;
-}
-m() {
-  C? c;
-  return c?.x();
-}
-''');
-
-    await resolveTestFile();
-    assertNoTestErrors();
-    assertType(findNode.methodInvocation('c?.x()'), 'bool?');
-  }
-
-  test_local_nullCoalesce_nullableInt_int() async {
-    await addTestFile(r'''
-m() {
-  int? x;
-  int y;
-  x ?? y;
-}
-''');
-    await resolveTestFile();
-    assertNoTestErrors();
-    assertType(findNode.binary('x ?? y'), 'int');
-  }
-
-  test_local_nullCoalesce_nullableInt_nullableInt() async {
-    await addTestFile(r'''
-m() {
-  int? x;
-  x ?? x;
-}
-''');
-    await resolveTestFile();
-    assertNoTestErrors();
-    assertType(findNode.binary('x ?? x'), 'int?');
-  }
-
-  test_local_nullCoalesceAssign_nullableInt_int() async {
-    await addTestFile(r'''
-m() {
-  int? x;
-  int y;
-  x ??= y;
-}
-''');
-    await resolveTestFile();
-    assertNoTestErrors();
-    assertType(findNode.assignment('x ??= y'), 'int');
-  }
-
-  test_local_nullCoalesceAssign_nullableInt_nullableInt() async {
-    await addTestFile(r'''
-m() {
-  int? x;
-  x ??= x;
-}
-''');
-    await resolveTestFile();
-    assertNoTestErrors();
-    assertType(findNode.assignment('x ??= x'), 'int?');
-  }
-
-  test_local_parameter_interfaceType() async {
-    addTestFile('''
-main() {
-  f(int? a, int b) {}
-}
-''');
-    await resolveTestFile();
-    assertNoTestErrors();
-
-    assertType(findNode.typeName('int? a'), 'int?');
-    assertType(findNode.typeName('int b'), 'int');
-  }
-
-  test_local_returnType_interfaceType() async {
-    addTestFile('''
-main() {
-  int? f() => 0;
-  int g() => 0;
-}
-''');
-    await resolveTestFile();
-    assertNoTestErrors();
-
-    assertType(findNode.typeName('int? f'), 'int?');
-    assertType(findNode.typeName('int g'), 'int');
-  }
-
-  @failingTest
-  test_local_variable_genericFunctionType() async {
-    addTestFile('''
-main() {
-  int? Function(bool, String?)? a;
-}
-''');
-    await resolveTestFile();
-    assertNoTestErrors();
-
-    assertType(
-      findNode.genericFunctionType('Function('),
-      '(bool!, String?) → int??',
-    );
-  }
-
-  test_local_variable_interfaceType() async {
-    addTestFile('''
-main() {
-  int? a = 0;
-  int b = 0;
-}
-''');
-    await resolveTestFile();
-    assertNoTestErrors();
-
-    assertType(findNode.typeName('int? a'), 'int?');
-    assertType(findNode.typeName('int b'), 'int');
-  }
-
-  test_local_variable_interfaceType_generic() async {
-    addTestFile('''
-main() {
-  List<int?>? a = [];
-  List<int>? b = [];
-  List<int?> c = [];
-  List<int> d = [];
-}
-''');
-    await resolveTestFile();
-    assertNoTestErrors();
-
-    assertType(findNode.typeName('List<int?>? a'), 'List<int?>?');
-    assertType(findNode.typeName('List<int>? b'), 'List<int>?');
-    assertType(findNode.typeName('List<int?> c'), 'List<int?>');
-    assertType(findNode.typeName('List<int> d'), 'List<int>');
-  }
-
-  test_local_variable_typeParameter() async {
-    addTestFile('''
-class A<T> {
-  main(T a) {
-    T? b;
-  }
-}
-''');
-    await resolveTestFile();
-    assertNoTestErrors();
-
-    assertType(findNode.typeName('T a'), 'T');
-    assertType(findNode.typeName('T? b'), 'T?');
-  }
-
-  test_member_potentiallyNullable_called() async {
-    addTestFile(r'''
-m<T extends Function>() {
-  List<T?> x;
-  x.first();
-}
-''');
-    await resolveTestFile();
-    // Do not assert no test errors. Deliberately invokes nullable type.
-    assertType(findNode.methodInvocation('first').methodName, 'Function?');
-  }
-
-  test_null_assertion_operator_changes_null_to_never() async {
-    addTestFile('''
-main() {
-  Null x = null;
-  x!;
-}
-''');
-    await resolveTestFile();
-    assertNoTestErrors();
-    assertType(findNode.postfix('x!'), 'Never');
-  }
-
-  test_null_assertion_operator_removes_nullability() async {
-    addTestFile('''
-main() {
-  Object? x = null;
-  x!;
-}
-''');
-    await resolveTestFile();
-    assertNoTestErrors();
-    assertType(findNode.postfix('x!'), 'Object');
-  }
-
-  test_typedef_classic() async {
-    addTestFile('''
-typedef int? F(bool a, String? b);
-
-main() {
-  F? a;
-}
-''');
-    await resolveTestFile();
-    assertNoTestErrors();
-
-    assertType(findNode.typeName('F? a'), 'int? Function(bool, String?)?');
-  }
-
-  @failingTest
-  test_typedef_function() async {
-    addTestFile('''
-typedef F<T> = int? Function(bool, T, T?);
-
-main() {
-  F<String>? a;
-}
-''');
-    await resolveTestFile();
-    assertNoTestErrors();
-
-    assertType(
-      findNode.typeName('F<String>'),
-      'int? Function(bool!, String!, String?)?',
-    );
-  }
-
   test_class_hierarchy() async {
     addTestFile('''
 class A {}
@@ -295,6 +58,187 @@
     assertType(findNode.typeName('C;'), 'C');
   }
 
+  test_local_getterNullAwareAccess_interfaceType() async {
+    addTestFile(r'''
+main() {
+  int? x;
+  return x?.isEven;
+}
+''');
+
+    await resolveTestFile();
+    assertNoTestErrors();
+    assertType(findNode.propertyAccess('x?.isEven'), 'bool?');
+  }
+
+  test_local_interfaceType() async {
+    addTestFile('''
+main() {
+  int? a = 0;
+  int b = 0;
+}
+''');
+    await resolveTestFile();
+    assertNoTestErrors();
+
+    assertType(findNode.typeName('int? a'), 'int?');
+    assertType(findNode.typeName('int b'), 'int');
+  }
+
+  test_local_interfaceType_generic() async {
+    addTestFile('''
+main() {
+  List<int?>? a = [];
+  List<int>? b = [];
+  List<int?> c = [];
+  List<int> d = [];
+}
+''');
+    await resolveTestFile();
+    assertNoTestErrors();
+
+    assertType(findNode.typeName('List<int?>? a'), 'List<int?>?');
+    assertType(findNode.typeName('List<int>? b'), 'List<int>?');
+    assertType(findNode.typeName('List<int?> c'), 'List<int?>');
+    assertType(findNode.typeName('List<int> d'), 'List<int>');
+  }
+
+  test_local_methodNullAwareCall_interfaceType() async {
+    await addTestFile(r'''
+class C {
+  bool x() => true;
+}
+
+main() {
+  C? c;
+  return c?.x();
+}
+''');
+
+    await resolveTestFile();
+    assertNoTestErrors();
+    assertType(findNode.methodInvocation('c?.x()'), 'bool?');
+  }
+
+  test_local_nullCoalesce_nullableInt_int() async {
+    await addTestFile(r'''
+main() {
+  int? x;
+  int y = 0;
+  x ?? y;
+}
+''');
+    await resolveTestFile();
+    assertNoTestErrors();
+    assertType(findNode.binary('x ?? y'), 'int');
+  }
+
+  test_local_nullCoalesce_nullableInt_nullableInt() async {
+    await addTestFile(r'''
+main() {
+  int? x;
+  x ?? x;
+}
+''');
+    await resolveTestFile();
+    assertNoTestErrors();
+    assertType(findNode.binary('x ?? x'), 'int?');
+  }
+
+  test_local_nullCoalesceAssign_nullableInt_int() async {
+    await addTestFile(r'''
+main() {
+  int? x;
+  int y = 0;
+  x ??= y;
+}
+''');
+    await resolveTestFile();
+    assertNoTestErrors();
+    assertType(findNode.assignment('x ??= y'), 'int');
+  }
+
+  test_local_nullCoalesceAssign_nullableInt_nullableInt() async {
+    await addTestFile(r'''
+main() {
+  int? x;
+  x ??= x;
+}
+''');
+    await resolveTestFile();
+    assertNoTestErrors();
+    assertType(findNode.assignment('x ??= x'), 'int?');
+  }
+
+  test_local_typeParameter() async {
+    addTestFile('''
+main<T>(T a) {
+  T x = a;
+  T? y;
+}
+''');
+    await resolveTestFile();
+    assertNoTestErrors();
+
+    assertType(findNode.typeName('T x'), 'T');
+    assertType(findNode.typeName('T? y'), 'T?');
+  }
+
+  @failingTest
+  test_local_variable_genericFunctionType() async {
+    addTestFile('''
+main() {
+  int? Function(bool, String?)? a;
+}
+''');
+    await resolveTestFile();
+    assertNoTestErrors();
+
+    assertType(
+      findNode.genericFunctionType('Function('),
+      '(bool!, String?) → int??',
+    );
+  }
+
+  test_localFunction_parameter_interfaceType() async {
+    addTestFile('''
+main() {
+  f(int? a, int b) {}
+}
+''');
+    await resolveTestFile();
+    assertNoTestErrors();
+
+    assertType(findNode.typeName('int? a'), 'int?');
+    assertType(findNode.typeName('int b'), 'int');
+  }
+
+  test_localFunction_returnType_interfaceType() async {
+    addTestFile('''
+main() {
+  int? f() => 0;
+  int g() => 0;
+}
+''');
+    await resolveTestFile();
+    assertNoTestErrors();
+
+    assertType(findNode.typeName('int? f'), 'int?');
+    assertType(findNode.typeName('int g'), 'int');
+  }
+
+  test_member_potentiallyNullable_called() async {
+    addTestFile(r'''
+m<T extends Function>() {
+  List<T?> x;
+  x.first();
+}
+''');
+    await resolveTestFile();
+    // Do not assert no test errors. Deliberately invokes nullable type.
+    assertType(findNode.methodInvocation('first').methodName, 'Function?');
+  }
+
   test_mixin_hierarchy() async {
     addTestFile('''
 class A {}
@@ -308,6 +252,187 @@
     assertType(findNode.typeName('A {} // 1'), 'A');
     assertType(findNode.typeName('A {} // 2'), 'A');
   }
+
+  test_null_assertion_operator_changes_null_to_never() async {
+    addTestFile('''
+main() {
+  Null x = null;
+  x!;
+}
+''');
+    await resolveTestFile();
+    assertNoTestErrors();
+    assertType(findNode.postfix('x!'), 'Never');
+  }
+
+  test_null_assertion_operator_removes_nullability() async {
+    addTestFile('''
+main() {
+  Object? x = null;
+  x!;
+}
+''');
+    await resolveTestFile();
+    assertNoTestErrors();
+    assertType(findNode.postfix('x!'), 'Object');
+  }
+
+  @failingTest
+  test_parameter_genericFunctionType() async {
+    addTestFile('''
+main(int? Function(bool, String?)? a) {
+}
+''');
+    await resolveTestFile();
+    assertNoTestErrors();
+
+    assertType(
+      findNode.genericFunctionType('Function('),
+      '(bool!, String?) → int??',
+    );
+  }
+
+  test_parameter_getterNullAwareAccess_interfaceType() async {
+    addTestFile(r'''
+main(int? x) {
+  return x?.isEven;
+}
+''');
+
+    await resolveTestFile();
+    assertNoTestErrors();
+    assertType(findNode.propertyAccess('x?.isEven'), 'bool?');
+  }
+
+  test_parameter_interfaceType() async {
+    addTestFile('''
+main(int? a, int b) {
+}
+''');
+    await resolveTestFile();
+    assertNoTestErrors();
+
+    assertType(findNode.typeName('int? a'), 'int?');
+    assertType(findNode.typeName('int b'), 'int');
+  }
+
+  test_parameter_interfaceType_generic() async {
+    addTestFile('''
+main(List<int?>? a, List<int>? b, List<int?> c, List<int> d) {
+}
+''');
+    await resolveTestFile();
+    assertNoTestErrors();
+
+    assertType(findNode.typeName('List<int?>? a'), 'List<int?>?');
+    assertType(findNode.typeName('List<int>? b'), 'List<int>?');
+    assertType(findNode.typeName('List<int?> c'), 'List<int?>');
+    assertType(findNode.typeName('List<int> d'), 'List<int>');
+  }
+
+  test_parameter_methodNullAwareCall_interfaceType() async {
+    await addTestFile(r'''
+class C {
+  bool x() => true;
+}
+
+main(C? c) {
+  return c?.x();
+}
+''');
+
+    await resolveTestFile();
+    assertNoTestErrors();
+    assertType(findNode.methodInvocation('c?.x()'), 'bool?');
+  }
+
+  test_parameter_nullCoalesce_nullableInt_int() async {
+    await addTestFile(r'''
+main(int? x, int y) {
+  x ?? y;
+}
+''');
+    await resolveTestFile();
+    assertNoTestErrors();
+    assertType(findNode.binary('x ?? y'), 'int');
+  }
+
+  test_parameter_nullCoalesce_nullableInt_nullableInt() async {
+    await addTestFile(r'''
+main(int? x) {
+  x ?? x;
+}
+''');
+    await resolveTestFile();
+    assertNoTestErrors();
+    assertType(findNode.binary('x ?? x'), 'int?');
+  }
+
+  test_parameter_nullCoalesceAssign_nullableInt_int() async {
+    await addTestFile(r'''
+main(int? x, int y) {
+  x ??= y;
+}
+''');
+    await resolveTestFile();
+    assertNoTestErrors();
+    assertType(findNode.assignment('x ??= y'), 'int');
+  }
+
+  test_parameter_nullCoalesceAssign_nullableInt_nullableInt() async {
+    await addTestFile(r'''
+main(int? x) {
+  x ??= x;
+}
+''');
+    await resolveTestFile();
+    assertNoTestErrors();
+    assertType(findNode.assignment('x ??= x'), 'int?');
+  }
+
+  test_parameter_typeParameter() async {
+    addTestFile('''
+main<T>(T a, T? b) {
+}
+''');
+    await resolveTestFile();
+    assertNoTestErrors();
+
+    assertType(findNode.typeName('T a'), 'T');
+    assertType(findNode.typeName('T? b'), 'T?');
+  }
+
+  test_typedef_classic() async {
+    addTestFile('''
+typedef int? F(bool a, String? b);
+
+main() {
+  F? a;
+}
+''');
+    await resolveTestFile();
+    assertNoTestErrors();
+
+    assertType(findNode.typeName('F? a'), 'int? Function(bool, String?)?');
+  }
+
+  @failingTest
+  test_typedef_function() async {
+    addTestFile('''
+typedef F<T> = int? Function(bool, T, T?);
+
+main() {
+  F<String>? a;
+}
+''');
+    await resolveTestFile();
+    assertNoTestErrors();
+
+    assertType(
+      findNode.typeName('F<String>'),
+      'int? Function(bool!, String!, String?)?',
+    );
+  }
 }
 
 @reflectiveTest
diff --git a/pkg/analyzer/test/src/dart/resolution/test_all.dart b/pkg/analyzer/test/src/dart/resolution/test_all.dart
index 10b3832..95539c2 100644
--- a/pkg/analyzer/test/src/dart/resolution/test_all.dart
+++ b/pkg/analyzer/test/src/dart/resolution/test_all.dart
@@ -18,6 +18,7 @@
 import 'for_in_test.dart' as for_in;
 import 'function_expression_invocation_test.dart'
     as function_expression_invocation;
+import 'function_type_alias_test.dart' as function_type_alias;
 import 'generic_function_type_test.dart' as generic_function_type;
 import 'generic_type_alias_test.dart' as generic_type_alias;
 import 'import_prefix_test.dart' as import_prefix;
@@ -26,6 +27,7 @@
     as instance_member_inference_class;
 import 'instance_member_inference_mixin_test.dart'
     as instance_member_inference_mixin;
+import 'metadata_test.dart' as metadata;
 import 'method_invocation_test.dart' as method_invocation;
 import 'mixin_test.dart' as mixin_resolution;
 import 'namespace_test.dart' as namespace;
@@ -50,12 +52,14 @@
     for_element.main();
     for_in.main();
     function_expression_invocation.main();
+    function_type_alias.main();
     generic_function_type.main();
     generic_type_alias.main();
     import_prefix.main();
     instance_creation.main();
     instance_member_inference_class.main();
     instance_member_inference_mixin.main();
+    metadata.main();
     method_invocation.main();
     mixin_resolution.main();
     namespace.main();
diff --git a/pkg/analyzer/test/src/dart/resolver/legacy_type_asserter_test.dart b/pkg/analyzer/test/src/dart/resolver/legacy_type_asserter_test.dart
new file mode 100644
index 0000000..2447515
--- /dev/null
+++ b/pkg/analyzer/test/src/dart/resolver/legacy_type_asserter_test.dart
@@ -0,0 +1,163 @@
+// Copyright (c) 2019, 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 'package:analyzer/dart/analysis/features.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/element/element.dart';
+import 'package:analyzer/src/dart/element/type.dart';
+import 'package:analyzer/src/dart/resolver/legacy_type_asserter.dart';
+import 'package:analyzer/src/generated/resolver.dart';
+import 'package:analyzer/src/generated/testing/ast_test_factory.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../resolution/driver_resolution.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(LegacyTypeAsserterTest);
+  });
+}
+
+/// Tests for the [ExitDetector] that require that the control flow and spread
+/// experiments be enabled.
+@reflectiveTest
+class LegacyTypeAsserterTest extends DriverResolutionTest {
+  TypeProvider typeProvider;
+  setUp() async {
+    await super.setUp();
+    typeProvider = await this.driver.currentSession.typeProvider;
+  }
+
+  test_nullableUnit_expressionStaticType_bottom() async {
+    var identifier = AstTestFactory.identifier3('foo');
+    var unit = _wrapExpression(identifier);
+    identifier.staticType = BottomTypeImpl.instance;
+    expect(() {
+      LegacyTypeAsserter.assertLegacyTypes(unit);
+    }, throwsStateError);
+  }
+
+  test_nullableUnit_expressionStaticType_bottomQuestion() async {
+    var identifier = AstTestFactory.identifier3('foo');
+    var unit = _wrapExpression(identifier);
+    identifier.staticType = BottomTypeImpl.instanceNullable;
+    LegacyTypeAsserter.assertLegacyTypes(unit);
+  }
+
+  test_nullableUnit_expressionStaticType_dynamic() async {
+    var identifier = AstTestFactory.identifier3('foo');
+    var unit = _wrapExpression(identifier);
+    identifier.staticType = typeProvider.dynamicType;
+    LegacyTypeAsserter.assertLegacyTypes(unit);
+  }
+
+  test_nullableUnit_expressionStaticType_nonNull() async {
+    var identifier = AstTestFactory.identifier3('foo');
+    var unit = _wrapExpression(identifier);
+    identifier.staticType = (typeProvider.intType as TypeImpl)
+        .withNullability(NullabilitySuffix.none);
+    expect(() {
+      LegacyTypeAsserter.assertLegacyTypes(unit);
+    }, throwsStateError);
+  }
+
+  test_nullableUnit_expressionStaticType_nonNullTypeArgument() async {
+    var identifier = AstTestFactory.identifier3('foo');
+    var unit = _wrapExpression(identifier);
+    identifier.staticType = typeProvider.listType.instantiate([
+      (typeProvider.intType as TypeImpl)
+          .withNullability(NullabilitySuffix.question)
+    ]);
+
+    expect(() {
+      LegacyTypeAsserter.assertLegacyTypes(unit);
+    }, throwsStateError);
+  }
+
+  test_nullableUnit_expressionStaticType_nonNullTypeParameter() async {
+    var identifier = AstTestFactory.identifier3('foo');
+    var unit = _wrapExpression(identifier);
+    final listType = typeProvider.listType;
+    listType.typeParameters[0] = TypeParameterElementImpl('E', 0)
+      ..type = (listType.typeParameters[0].type as TypeImpl)
+          .withNullability(NullabilitySuffix.none) as TypeParameterTypeImpl;
+    identifier.staticType = listType;
+    expect(
+        (listType as dynamic)
+            .typeParameters[0]
+            .type
+            .toString(withNullability: true),
+        'E');
+    expect(() {
+      LegacyTypeAsserter.assertLegacyTypes(unit);
+    }, throwsStateError);
+  }
+
+  test_nullableUnit_expressionStaticType_nonNullTypeParameterBound() async {
+    var identifier = AstTestFactory.identifier3('foo');
+    var unit = _wrapExpression(identifier);
+    final listType = typeProvider.listType;
+    (listType.typeParameters[0] as TypeParameterElementImpl).bound =
+        (typeProvider.intType as TypeImpl)
+            .withNullability(NullabilitySuffix.none);
+    identifier.staticType = listType;
+    expect(
+        (listType as dynamic)
+            .typeParameters[0]
+            .type
+            .bound
+            .toString(withNullability: true),
+        'int');
+    expect(() {
+      LegacyTypeAsserter.assertLegacyTypes(unit);
+    }, throwsStateError);
+  }
+
+  test_nullableUnit_expressionStaticType_null() async {
+    var identifier = AstTestFactory.identifier3('foo');
+    var unit = _wrapExpression(identifier);
+    identifier.staticType = typeProvider.nullType;
+    LegacyTypeAsserter.assertLegacyTypes(unit);
+  }
+
+  test_nullableUnit_expressionStaticType_question() async {
+    var identifier = AstTestFactory.identifier3('foo');
+    var unit = _wrapExpression(identifier);
+    identifier.staticType = (typeProvider.intType as TypeImpl)
+        .withNullability(NullabilitySuffix.question);
+    expect(() {
+      LegacyTypeAsserter.assertLegacyTypes(unit);
+    }, throwsStateError);
+  }
+
+  test_nullableUnit_expressionStaticType_star() async {
+    var identifier = AstTestFactory.identifier3('foo');
+    var unit = _wrapExpression(identifier);
+    identifier.staticType = (typeProvider.intType as TypeImpl)
+        .withNullability(NullabilitySuffix.star);
+    LegacyTypeAsserter.assertLegacyTypes(unit);
+  }
+
+  test_nullableUnit_expressionStaticType_void() async {
+    var identifier = AstTestFactory.identifier3('foo');
+    var unit = _wrapExpression(identifier);
+    identifier.staticType = VoidTypeImpl.instance;
+    LegacyTypeAsserter.assertLegacyTypes(unit);
+  }
+
+  CompilationUnit _wrapExpression(Expression e, {bool nonNullable = false}) {
+    return AstTestFactory.compilationUnit9(
+        declarations: [
+          AstTestFactory.functionDeclaration(
+              null,
+              null,
+              null,
+              AstTestFactory.functionExpression2(
+                  null, AstTestFactory.expressionFunctionBody(e)))
+        ],
+        featureSet: FeatureSet.forTesting(
+            additionalFeatures: nonNullable ? [Feature.non_nullable] : []));
+  }
+}
diff --git a/pkg/analyzer/test/src/dart/resolver/test_all.dart b/pkg/analyzer/test/src/dart/resolver/test_all.dart
index e6430bd..f100f90 100644
--- a/pkg/analyzer/test/src/dart/resolver/test_all.dart
+++ b/pkg/analyzer/test/src/dart/resolver/test_all.dart
@@ -5,9 +5,11 @@
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
 import 'exit_detector_test.dart' as exit_detector;
+import 'legacy_type_asserter_test.dart' as legacy_type_asserter;
 
 main() {
   defineReflectiveSuite(() {
     exit_detector.main();
+    legacy_type_asserter.main();
   }, name: 'resolver');
 }
diff --git a/pkg/analyzer/test/src/diagnostics/dead_code_test.dart b/pkg/analyzer/test/src/diagnostics/dead_code_test.dart
index c84553a..3a2c54b 100644
--- a/pkg/analyzer/test/src/diagnostics/dead_code_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/dead_code_test.dart
@@ -724,12 +724,11 @@
 @pragma('analyzer:non-nullable')
 library foo;
 
-m() {
-  int x;
+m(int x) {
   x ?? 1;
 }
 ''', [
-      error(HintCode.DEAD_CODE, 69, 1),
+      error(HintCode.DEAD_CODE, 65, 1),
     ]);
   }
 
@@ -774,12 +773,11 @@
 @pragma('analyzer:non-nullable')
 library foo;
 
-m() {
-  int x;
+m(int x) {
   x ??= 1;
 }
 ''', [
-      error(HintCode.DEAD_CODE, 70, 1),
+      error(HintCode.DEAD_CODE, 66, 1),
     ]);
   }
 
diff --git a/pkg/analyzer/test/src/diagnostics/missing_default_value_for_parameter_test.dart b/pkg/analyzer/test/src/diagnostics/missing_default_value_for_parameter_test.dart
index b18a85e..679f2ce 100644
--- a/pkg/analyzer/test/src/diagnostics/missing_default_value_for_parameter_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/missing_default_value_for_parameter_test.dart
@@ -121,7 +121,7 @@
 
   test_genericFunctionType() async {
     await assertNoErrorsInCode('''
-void Function({String s}) log;
+void Function({String s})? log;
 ''');
   }
 
diff --git a/pkg/analyzer/test/src/diagnostics/non_null_opt_out_test.dart b/pkg/analyzer/test/src/diagnostics/non_null_opt_out_test.dart
index 8c5936c..933fa1b 100644
--- a/pkg/analyzer/test/src/diagnostics/non_null_opt_out_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/non_null_opt_out_test.dart
@@ -27,8 +27,8 @@
     assertErrorsInCode('''
 // @dart = 2.2
 // NNBD syntax is not allowed
-f(x, y, z) { (x is String?) ? (x + y) : z; }
-''', [error(ParserErrorCode.EXPERIMENT_NOT_ENABLED, 70, 1)]);
+f(x, z) { (x is String?) ? x : z; }
+''', [error(ParserErrorCode.EXPERIMENT_NOT_ENABLED, 67, 1)]);
   }
 
   test_nnbd_optOut_late() async {
@@ -40,4 +40,15 @@
 }
 ''');
   }
+
+  @failingTest
+  test_nnbd_optOut_transformsOptedInSignatures() async {
+    // Failing because we don't transform opted out signatures.
+    await assertNoErrorsInCode('''
+// @dart = 2.2
+f(String x) {
+  x + null; // OK because we're in a nullable library.
+}
+''');
+  }
 }
diff --git a/pkg/analyzer/test/src/diagnostics/not_initialized_non_nullable_top_level_variable_test.dart b/pkg/analyzer/test/src/diagnostics/not_initialized_non_nullable_top_level_variable_test.dart
new file mode 100644
index 0000000..9399e70
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/not_initialized_non_nullable_top_level_variable_test.dart
@@ -0,0 +1,76 @@
+// Copyright (c) 2019, 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 'package:analyzer/src/dart/analysis/experiments.dart';
+import 'package:analyzer/src/error/codes.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/driver_resolution.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(NotInitializedNonNullableTopLevelVariableTest);
+  });
+}
+
+@reflectiveTest
+class NotInitializedNonNullableTopLevelVariableTest
+    extends DriverResolutionTest {
+  @override
+  AnalysisOptionsImpl get analysisOptions =>
+      AnalysisOptionsImpl()..enabledExperiments = [EnableString.non_nullable];
+
+  test_hasInitializer() async {
+    assertNoErrorsInCode('''
+int v = 0;
+''');
+  }
+
+  test_noInitializer() async {
+    assertErrorsInCode('''
+int x = 0, y, z = 2;
+''', [
+      error(
+          CompileTimeErrorCode.NOT_INITIALIZED_NON_NULLABLE_TOP_LEVEL_VARIABLE,
+          11,
+          1),
+    ]);
+  }
+
+  test_nullable() async {
+    assertNoErrorsInCode('''
+int? v;
+''');
+  }
+
+  test_type_dynamic() async {
+    assertNoErrorsInCode('''
+dynamic v;
+''');
+  }
+
+  test_type_dynamic_implicit() async {
+    assertNoErrorsInCode('''
+var v;
+''');
+  }
+
+  test_type_never() async {
+    assertErrorsInCode('''
+Never v;
+''', [
+      error(
+          CompileTimeErrorCode.NOT_INITIALIZED_NON_NULLABLE_TOP_LEVEL_VARIABLE,
+          6,
+          1),
+    ]);
+  }
+
+  test_type_void() async {
+    assertNoErrorsInCode('''
+void v;
+''');
+  }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/not_initialized_potentially_non_nullable_local_variable_test.dart b/pkg/analyzer/test/src/diagnostics/not_initialized_potentially_non_nullable_local_variable_test.dart
new file mode 100644
index 0000000..373e9b5
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/not_initialized_potentially_non_nullable_local_variable_test.dart
@@ -0,0 +1,104 @@
+// Copyright (c) 2019, 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 'package:analyzer/src/dart/analysis/experiments.dart';
+import 'package:analyzer/src/error/codes.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/driver_resolution.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(
+      NotInitializedPotentiallyNonNullableLocalVariableTest,
+    );
+  });
+}
+
+@reflectiveTest
+class NotInitializedPotentiallyNonNullableLocalVariableTest
+    extends DriverResolutionTest {
+  @override
+  AnalysisOptionsImpl get analysisOptions =>
+      AnalysisOptionsImpl()..enabledExperiments = [EnableString.non_nullable];
+
+  test_hasInitializer() async {
+    assertNoErrorsInCode('''
+f() {
+  int v = 0;
+}
+''');
+  }
+
+  test_late() async {
+    assertNoErrorsInCode('''
+f() {
+  late int v;
+}
+''');
+  }
+
+  test_noInitializer() async {
+    assertErrorsInCode('''
+f() {
+  int v;
+}
+''', [
+      error(
+          CompileTimeErrorCode
+              .NOT_INITIALIZED_POTENTIALLY_NON_NULLABLE_LOCAL_VARIABLE,
+          12,
+          1),
+      error(HintCode.UNUSED_LOCAL_VARIABLE, 12, 1),
+    ]);
+  }
+
+  test_noInitializer_typeParameter() async {
+    assertErrorsInCode('''
+f<T>() {
+  T v;
+}
+''', [
+      error(
+          CompileTimeErrorCode
+              .NOT_INITIALIZED_POTENTIALLY_NON_NULLABLE_LOCAL_VARIABLE,
+          13,
+          1),
+      error(HintCode.UNUSED_LOCAL_VARIABLE, 13, 1),
+    ]);
+  }
+
+  test_nullable() async {
+    assertNoErrorsInCode('''
+f() {
+  int? v;
+}
+''');
+  }
+
+  test_type_dynamic() async {
+    assertNoErrorsInCode('''
+f() {
+  dynamic v;
+}
+''');
+  }
+
+  test_type_dynamicImplicit() async {
+    assertNoErrorsInCode('''
+f() {
+  var v;
+}
+''');
+  }
+
+  test_type_void() async {
+    assertNoErrorsInCode('''
+f() {
+  void v;
+}
+''');
+  }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/sdk_version_never_test.dart b/pkg/analyzer/test/src/diagnostics/sdk_version_never_test.dart
index f9703db..e19ac7b 100644
--- a/pkg/analyzer/test/src/diagnostics/sdk_version_never_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/sdk_version_never_test.dart
@@ -42,7 +42,7 @@
 
   test_lessThan() async {
     await verifyVersion('2.3.0', '''
-Never sink;
+Never sink = (throw 42);
 ''', expectedErrors: [
       error(HintCode.SDK_VERSION_NEVER, 0, 5),
     ]);
diff --git a/pkg/analyzer/test/src/diagnostics/test_all.dart b/pkg/analyzer/test/src/diagnostics/test_all.dart
index bf27038..61643d4 100644
--- a/pkg/analyzer/test/src/diagnostics/test_all.dart
+++ b/pkg/analyzer/test/src/diagnostics/test_all.dart
@@ -96,6 +96,10 @@
 import 'non_constant_spread_expression_from_deferred_library_test.dart'
     as non_constant_spread_expression_from_deferred_library;
 import 'non_null_opt_out_test.dart' as non_null_opt_out;
+import 'not_initialized_non_nullable_top_level_variable_test.dart'
+    as not_initialized_non_nullable_top_level_variable;
+import 'not_initialized_potentially_non_nullable_local_variable_test.dart'
+    as not_initialized_potentially_non_nullable_local_variable;
 import 'not_iterable_spread_test.dart' as not_iterable_spread;
 import 'not_map_spread_test.dart' as not_map_spread;
 import 'not_null_aware_null_spread_test.dart' as not_null_aware_null_spread;
@@ -235,6 +239,8 @@
     non_constant_set_element_from_deferred_library.main();
     non_constant_spread_expression_from_deferred_library.main();
     non_null_opt_out.main();
+    not_initialized_non_nullable_top_level_variable.main();
+    not_initialized_potentially_non_nullable_local_variable.main();
     not_iterable_spread.main();
     not_map_spread.main();
     not_null_aware_null_spread.main();
diff --git a/pkg/analyzer/test/src/diagnostics/unchecked_use_of_nullable_value_test.dart b/pkg/analyzer/test/src/diagnostics/unchecked_use_of_nullable_value_test.dart
index 6dc6cfc..906d4da 100644
--- a/pkg/analyzer/test/src/diagnostics/unchecked_use_of_nullable_value_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/unchecked_use_of_nullable_value_test.dart
@@ -305,23 +305,21 @@
 
   test_member_potentiallyNullable() async {
     await assertErrorsInCode(r'''
-m<T extends int?>() {
-  T x;
+m<T extends int?>(T x) {
   x.isEven;
 }
 ''', [
-      error(StaticWarningCode.UNCHECKED_USE_OF_NULLABLE_VALUE, 31, 1),
+      error(StaticWarningCode.UNCHECKED_USE_OF_NULLABLE_VALUE, 27, 1),
     ]);
   }
 
   test_member_potentiallyNullable_called() async {
     await assertErrorsInCode(r'''
-m<T extends Function>() {
-  List<T?> x;
+m<T extends Function>(List<T?> x) {
   x.first();
 }
 ''', [
-      error(StaticWarningCode.UNCHECKED_USE_OF_NULLABLE_VALUE, 44, 5),
+      error(StaticWarningCode.UNCHECKED_USE_OF_NULLABLE_VALUE, 40, 5),
     ]);
   }
 
@@ -354,8 +352,7 @@
 
   test_method_noSuchMethod_nullable() async {
     await assertNoErrorsInCode(r'''
-m() {
-  int x;
+m(int x) {
   x.noSuchMethod(throw '');
 }
 ''');
@@ -383,8 +380,7 @@
 
   test_method_toString_nullable() async {
     await assertNoErrorsInCode(r'''
-m() {
-  int x;
+m(int x) {
   x.toString();
 }
 ''');
@@ -503,8 +499,7 @@
 
   test_operatorPostfixInc_nonNullable() async {
     await assertNoErrorsInCode(r'''
-m() {
-  int x;
+m(int x) {
   x++;
 }
 ''');
@@ -512,13 +507,11 @@
 
   test_operatorPostfixInc_nullable() async {
     await assertErrorsInCode(r'''
-m() {
-  int? x;
+m(int? x) {
   x++;
 }
 ''', [
-      error(HintCode.UNUSED_LOCAL_VARIABLE, 13, 1),
-      error(StaticWarningCode.UNCHECKED_USE_OF_NULLABLE_VALUE, 18, 1),
+      error(StaticWarningCode.UNCHECKED_USE_OF_NULLABLE_VALUE, 14, 1),
     ]);
   }
 
@@ -545,8 +538,7 @@
 
   test_operatorPrefixInc_nonNullable() async {
     await assertNoErrorsInCode(r'''
-m() {
-  int x;
+m(int x) {
   ++x;
 }
 ''');
@@ -554,13 +546,11 @@
 
   test_operatorPrefixInc_nullable() async {
     await assertErrorsInCode(r'''
-m() {
-  int? x;
+m(int? x) {
   ++x;
 }
 ''', [
-      error(HintCode.UNUSED_LOCAL_VARIABLE, 13, 1),
-      error(StaticWarningCode.UNCHECKED_USE_OF_NULLABLE_VALUE, 20, 1),
+      error(StaticWarningCode.UNCHECKED_USE_OF_NULLABLE_VALUE, 16, 1),
     ]);
   }
 
@@ -607,8 +597,7 @@
 
   test_plusEq_nonNullable() async {
     await assertNoErrorsInCode(r'''
-m() {
-  int x;
+m(int x) {
   x += 1;
 }
 ''');
@@ -616,16 +605,41 @@
 
   test_plusEq_nullable() async {
     await assertErrorsInCode(r'''
-m() {
-  int? x;
+m(int? x) {
   x += 1;
 }
 ''', [
-      error(HintCode.UNUSED_LOCAL_VARIABLE, 13, 1),
-      error(StaticWarningCode.UNCHECKED_USE_OF_NULLABLE_VALUE, 18, 1),
+      error(StaticWarningCode.UNCHECKED_USE_OF_NULLABLE_VALUE, 14, 1),
     ]);
   }
 
+  test_spread_nonNullable() async {
+    await assertNoErrorsInCode(r'''
+m() {
+  var list = [];
+  [...list];
+}
+''');
+  }
+
+  test_spread_nullable() async {
+    await assertErrorCodesInCode(r'''
+m() {
+  List? list;
+  [...list];
+}
+''', [StaticWarningCode.UNCHECKED_USE_OF_NULLABLE_VALUE]);
+  }
+
+  test_spread_nullable_question() async {
+    await assertNoErrorsInCode(r'''
+m() {
+  List? list;
+  [...?list];
+}
+''');
+  }
+
   test_ternary_condition_nullable() async {
     await assertErrorsInCode(r'''
 m() {
diff --git a/pkg/analyzer/test/src/diagnostics/undefined_getter_test.dart b/pkg/analyzer/test/src/diagnostics/undefined_getter_test.dart
index 6feaae5..3958e85 100644
--- a/pkg/analyzer/test/src/diagnostics/undefined_getter_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/undefined_getter_test.dart
@@ -91,6 +91,15 @@
 ''');
   }
 
+  test_nullMember_undefined() async {
+    await assertErrorCodesInCode(r'''
+m() {
+  Null _null;
+  _null.foo;
+}
+''', [StaticTypeWarningCode.UNDEFINED_GETTER]);
+  }
+
   test_promotedTypeParameter_regress35305() async {
     await assertErrorsInCode(r'''
 void f<X extends num, Y extends X>(Y y) {
diff --git a/pkg/analyzer/test/src/diagnostics/unnecessary_null_aware_call_test.dart b/pkg/analyzer/test/src/diagnostics/unnecessary_null_aware_call_test.dart
index a1aa080..110b35b 100644
--- a/pkg/analyzer/test/src/diagnostics/unnecessary_null_aware_call_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/unnecessary_null_aware_call_test.dart
@@ -23,25 +23,17 @@
 
   test_getter_parenthesized_nonNull() async {
     await assertErrorsInCode('''
-@pragma('analyzer:non-nullable')
-library foo;
-
-f() {
-  int x;
+f(int x) {
   (x)?.isEven;
 }
 ''', [
-      error(HintCode.UNNECESSARY_NULL_AWARE_CALL, 67, 2),
+      error(HintCode.UNNECESSARY_NULL_AWARE_CALL, 16, 2),
     ]);
   }
 
   test_getter_parenthesized_nullable() async {
     await assertNoErrorsInCode('''
-@pragma('analyzer:non-nullable')
-library foo;
-
-f() {
-  int? x;
+f(int? x) {
   (x)?.isEven;
 }
 ''');
@@ -49,25 +41,17 @@
 
   test_getter_simple_nonNull() async {
     await assertErrorsInCode('''
-@pragma('analyzer:non-nullable')
-library foo;
-
-f() {
-  int x;
+f(int x) {
   x?.isEven;
 }
 ''', [
-      error(HintCode.UNNECESSARY_NULL_AWARE_CALL, 65, 2),
+      error(HintCode.UNNECESSARY_NULL_AWARE_CALL, 14, 2),
     ]);
   }
 
   test_getter_simple_nullable() async {
     await assertNoErrorsInCode('''
-@pragma('analyzer:non-nullable')
-library foo;
-
-f() {
-  int? x;
+f(int? x) {
   x?.isEven;
 }
 ''');
@@ -75,25 +59,17 @@
 
   test_method_parenthesized_nonNull() async {
     await assertErrorsInCode('''
-@pragma('analyzer:non-nullable')
-library foo;
-
-f() {
-  int x;
+f(int x) {
   (x)?.round();
 }
 ''', [
-      error(HintCode.UNNECESSARY_NULL_AWARE_CALL, 67, 2),
+      error(HintCode.UNNECESSARY_NULL_AWARE_CALL, 16, 2),
     ]);
   }
 
   test_method_parenthesized_nullable() async {
     await assertNoErrorsInCode('''
-@pragma('analyzer:non-nullable')
-library foo;
-
-f() {
-  int? x;
+f(int? x) {
   (x)?.round();
 }
 ''');
@@ -101,25 +77,17 @@
 
   test_method_simple_nonNull() async {
     await assertErrorsInCode('''
-@pragma('analyzer:non-nullable')
-library foo;
-
-f() {
-  int x;
+f(int x) {
   x?.round();
 }
 ''', [
-      error(HintCode.UNNECESSARY_NULL_AWARE_CALL, 65, 2),
+      error(HintCode.UNNECESSARY_NULL_AWARE_CALL, 14, 2),
     ]);
   }
 
   test_method_simple_nullable() async {
     await assertNoErrorsInCode('''
-@pragma('analyzer:non-nullable')
-library foo;
-
-f() {
-  int? x;
+f(int? x) {
   x?.round();
 }
 ''');
diff --git a/pkg/analyzer/test/src/fasta/recovery/paired_tokens_test.dart b/pkg/analyzer/test/src/fasta/recovery/paired_tokens_test.dart
index 5b9b63a..0a202ed 100644
--- a/pkg/analyzer/test/src/fasta/recovery/paired_tokens_test.dart
+++ b/pkg/analyzer/test/src/fasta/recovery/paired_tokens_test.dart
@@ -2,6 +2,7 @@
 // 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 'package:analyzer/dart/analysis/features.dart';
 import 'package:analyzer/src/dart/error/syntactic_errors.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -250,6 +251,16 @@
 ''');
   }
 
+  void test_indexOperator_nullAware() {
+    testRecovery('''
+f(x) => l?.[x
+''', [ScannerErrorCode.EXPECTED_TOKEN, ParserErrorCode.EXPECTED_TOKEN], '''
+f(x) => l?.[x];
+''',
+        featureSet: FeatureSet.forTesting(
+            sdkVersion: '2.3.0', additionalFeatures: [Feature.non_nullable]));
+  }
+
   void test_listLiteral_inner_last() {
     testRecovery('''
 var x = [[0], [1];
diff --git a/pkg/analyzer/test/src/fasta/recovery/partial_code/extension_declaration_test.dart b/pkg/analyzer/test/src/fasta/recovery/partial_code/extension_declaration_test.dart
index e04eadd..ad7ff9a 100644
--- a/pkg/analyzer/test/src/fasta/recovery/partial_code/extension_declaration_test.dart
+++ b/pkg/analyzer/test/src/fasta/recovery/partial_code/extension_declaration_test.dart
@@ -20,13 +20,19 @@
               'keyword',
               'extension',
               [
-                ParserErrorCode.MISSING_IDENTIFIER,
                 ParserErrorCode.EXPECTED_TOKEN,
                 ParserErrorCode.EXPECTED_TYPE_NAME,
                 ParserErrorCode.EXPECTED_BODY,
               ],
-              'extension _s_ on _s_ {}',
-              failing: ['getter', 'functionNonVoid', 'functionVoid', 'mixin']),
+              'extension on _s_ {}',
+              failing: [
+                'getter',
+                'functionNonVoid',
+                'functionVoid',
+                'mixin',
+                'setter',
+                'typedef'
+              ]),
           new TestDescriptor(
               'named',
               'extension E',
diff --git a/pkg/analyzer/test/src/summary/resynthesize_ast2_test.dart b/pkg/analyzer/test/src/summary/resynthesize_ast2_test.dart
index ec7bbef..282c385 100644
--- a/pkg/analyzer/test/src/summary/resynthesize_ast2_test.dart
+++ b/pkg/analyzer/test/src/summary/resynthesize_ast2_test.dart
@@ -150,7 +150,7 @@
     List<LinkInputUnit> units,
   ) {
     units.add(
-      LinkInputUnit(definingSource, false, definingUnit),
+      LinkInputUnit(null, definingSource, false, definingUnit),
     );
     for (var directive in definingUnit.directives) {
       if (directive is PartDirective) {
@@ -165,12 +165,12 @@
           var text = _readSafely(partSource.fullName);
           var unit = parseText(text, featureSet);
           units.add(
-            LinkInputUnit(partSource, false, unit),
+            LinkInputUnit(relativeUriStr, partSource, false, unit),
           );
         } else {
           var unit = parseText('', featureSet);
           units.add(
-            LinkInputUnit(partSource, false, unit),
+            LinkInputUnit(relativeUriStr, partSource, false, unit),
           );
         }
       }
diff --git a/pkg/analyzer/test/src/summary/resynthesize_common.dart b/pkg/analyzer/test/src/summary/resynthesize_common.dart
index b67a6f2..f4c870d 100644
--- a/pkg/analyzer/test/src/summary/resynthesize_common.dart
+++ b/pkg/analyzer/test/src/summary/resynthesize_common.dart
@@ -5720,6 +5720,14 @@
         withExportScope: true);
   }
 
+  test_export_uri() async {
+    allowMissingFiles = true;
+    var library = await checkLibrary('''
+export 'foo.dart';
+''');
+    expect(library.exports[0].uri, 'foo.dart');
+  }
+
   test_export_variable() async {
     addLibrarySource('/a.dart', 'var x;');
     var library = await checkLibrary('export "a.dart";');
@@ -6452,6 +6460,17 @@
 ''');
   }
 
+  test_genericFunction_typeParameter_asTypedefArgument() async {
+    var library = await checkLibrary(r'''
+typedef F1 = Function<V1>(F2<V1>);
+typedef F2<V2> = V2 Function();
+''');
+    checkElementText(library, r'''
+typedef F1 = dynamic Function<V1>(V1 Function() );
+typedef F2<V2> = V2 Function();
+''');
+  }
+
   test_getter_documented() async {
     var library = await checkLibrary('''
 // Extra comment so doc comment offset != 0
@@ -6719,6 +6738,14 @@
 ''');
   }
 
+  test_import_uri() async {
+    allowMissingFiles = true;
+    var library = await checkLibrary('''
+import 'foo.dart';
+''');
+    expect(library.imports[0].uri, 'foo.dart');
+  }
+
   test_imports() async {
     addLibrarySource('/a.dart', 'library a; class C {}');
     addLibrarySource('/b.dart', 'library b; class D {}');
@@ -9227,6 +9254,14 @@
 ''');
   }
 
+  test_part_uri() async {
+    allowMissingFiles = true;
+    var library = await checkLibrary('''
+part 'foo.dart';
+''');
+    expect(library.parts[0].uri, 'foo.dart');
+  }
+
   test_parts() async {
     addSource('/a.dart', 'part of my.lib;');
     addSource('/b.dart', 'part of my.lib;');
diff --git a/pkg/analyzer/tool/diagnostics/diagnostics.md b/pkg/analyzer/tool/diagnostics/diagnostics.md
index b3fc35b..7bcca54 100644
--- a/pkg/analyzer/tool/diagnostics/diagnostics.md
+++ b/pkg/analyzer/tool/diagnostics/diagnostics.md
@@ -2,29 +2,23 @@
 title: Diagnostics
 description: Details for diagnostics produced by the Dart analyzer.
 ---
+{%- comment %}
+WARNING: Do NOT EDIT this file directly. It is autogenerated by the script in
+`pkg/analyzer/tool/diagnostics/generate.dart` in the sdk repository.
+{% endcomment -%}
 
 This page lists diagnostic messages produced by the Dart analyzer,
 with details about what those messages mean and how you can fix your code.
 For more information about the analyzer, see
 [Customizing static analysis](/guides/language/analysis-options).
 
-## Glossary
-
-This page uses the following terms.
-
-### Potentially non-nullable
-
-A type is _potentially non-nullable_ if it's either explicitly non-nullable or
-if it's a type parameter. The latter case is included because the actual runtime
-type might be non-nullable.
-
 ## Diagnostics
 
 The analyzer produces the following diagnostics for code that
 doesn't conform to the language specification or
 that might work in unexpected ways.
 
-### ambiguous\_set\_or\_map\_literal\_both
+### ambiguous_set_or_map_literal_both
 
 _This literal contains both 'Map' and 'Iterable' spreads, which makes it
 impossible to determine whether the literal is a map or a set._
@@ -59,9 +53,9 @@
 #### Common fixes
 
 There are two common ways to fix this problem. The first is to remove all
-of the spread elements of one kind or the other, so that the elements are
-consistent. In this case, that likely means removing the list (and
-deciding what to do about the now unused parameter):
+of the spread elements of one kind or another, so that the elements are
+consistent. In this case, that likely means removing the list and deciding
+what to do about the now unused parameter:
 
 ```dart
 union(Map<String, String> a, List<String> b, Map<String, String> c) =>
@@ -77,23 +71,23 @@
     {...a, for (String s in b) s: s, ...c};
 ```
 
-### ambiguous\_set\_or\_map\_literal\_either
+### ambiguous_set_or_map_literal_either
 
 _This literal must be either a map or a set, but the elements don't have enough
-type information for type inference to work._
+information for type inference to work._
 
 #### Description
 
-Because map and set literals use the same delimiters (`‘{` and `}`), the
+Because map and set literals use the same delimiters (`{` and `}`), the
 analyzer looks at the type arguments and the elements to determine which
 kind of literal you meant. When there are no type arguments and all of the
 elements are spread elements (which are allowed in both kinds of literals)
-then the analyzer uses the types of the expressions that are being spread
-to decide. If all of the expressions have the type `Iterable`, then it's a
-set literal, if they all have the type `Map`, then it's a map literal.
+then the analyzer uses the types of the expressions that are being spread.
+If all of the expressions have the type `Iterable`, then it's a set
+literal; if they all have the type `Map`, then it's a map literal.
 
-This diagnostic is produced when none of the expressions being spread has a
-type that allows the analyzer to decide whether you were writing a map
+This diagnostic is produced when none of the expressions being spread have
+a type that allows the analyzer to decide whether you were writing a map
 literal or a set literal.
 
 #### Example
@@ -151,41 +145,7 @@
 }
 ```
 
-### default\_value\_on\_required\_parameter
-
-_Required named parameters can't have a default value._
-
-#### Description
-
-The analyzer produces this diagnostic when a named parameter has both the
-`required` modifier and a default value. If the parameter is required, then
-a value for the parameter is always provided at the call sites, so the
-default value can never be used.
-
-#### Example
-
-The following code generates this diagnostic:
-
-```dart
-void log({required String !message! = 'no message'}) {}
-```
-
-#### Common fixes
-
-If the parameter is really required, then remove the default value:
-
-```dart
-void log({required String message}) {}
-```
-
-If the parameter isn't always required, then remove the `required`
-modifier:
-
-```dart
-void log({String message = 'no message'}) {}
-```
-
-### deprecated\_member\_use
+### deprecated_member_use
 
 _'{0}' is deprecated and shouldn't be used._
 
@@ -208,10 +168,9 @@
 #### Common fixes
 
 The documentation for declarations that are annotated with `@deprecated`
-should have documentation to indicate what code to use in place of the
-deprecated code.
+should indicate what code to use in place of the deprecated code.
 
-### expression\_in\_map
+### expression_in_map
 
 _Expressions can't be used in a map literal._
 
@@ -228,16 +187,17 @@
 var map = <String, int>{'a': 0, 'b': 1, !'c'!};
 ```
 
-#### Common fixes
+#### Common fix
 
 If the expression is intended to compute either a key or a value in an
-entry, fix the issue by completing the code:
+entry, fix the issue by replacing the expression with the key or the value.
+For example:
 
 ```dart
 var map = <String, int>{'a': 0, 'b': 1, 'c': 2};
 ```
 
-### invalid\_literal\_annotation
+### invalid_literal_annotation
 
 _Only const constructors can have the `@literal` annotation._
 
@@ -263,51 +223,7 @@
 var x;
 ```
 
-### missing\_default\_value\_for\_parameter
-
-_The parameter '{0}' can't have a value of 'null' because of its type, so it
-must either be a required parameter or have a default value._
-
-#### Description
-
-The analyzer produces this diagnostic when an optional parameter doesn't
-have a default value, but has a
-<a href=”#potentially-non-nullable”>potentially non-nullable</a> type.
-Optional parameters that have no explicit default value have an implicit
-default value of `null`. If the type of the parameter doesn't allow the
-parameter to have a value of null, then the implicit default value is not
-valid.
-
-#### Example
-
-The following code generates this diagnostic:
-
-```dart
-void log({String !message!}) {}
-```
-
-#### Common fixes
-
-If the parameter can have the value `null`, then add a question mark after
-the type annotation:
-
-```dart
-void log({String? message}) {}
-```
-
-If the parameter can't be null, then either provide a default value:
-
-```dart
-void log({String message = ''}) {}
-```
-
-or add the `required` modifier to the parameter:
-
-```dart
-void log({required String message}) {}
-```
-
-### not\_iterable\_spread
+### not_iterable_spread
 
 _Spread elements in list or set literals must implement 'Iterable'._
 
@@ -326,7 +242,7 @@
 var s = <String>{...m};
 ```
 
-#### Common fixes
+#### Common fix
 
 The most common fix is to replace the expression with one that produces an
 iterable object:
@@ -336,40 +252,7 @@
 var s = <String>{...m.keys};
 ```
 
-### nullable\_type\_in\_extends\_clause
-
-_A class can't extend a nullable type._
-
-#### Description
-
-The analyzer produces this diagnostic when a class declaration uses an
-extends clause to specify a superclass, and the type that's specified is a
-nullable type.
-
-The reason the supertype is a _type_ rather than a class name is to allow
-you to control the signatures of the members to be inherited from the
-supertype, such as by specifying type arguments. However, the nullability
-of a type doesn't change the signatures of any members, so there isn't any
-reason to allow the nullability to be specified when used in the extends
-clause.
-
-#### Example
-
-The following code generates this diagnostic:
-
-```dart
-class Invalid extends !Duration?! {}
-```
-
-#### Common fixes
-
-The most common fix is to remove the question mark:
-
-```dart
-class Invalid extends Duration {}
-```
-
-### sdk\_version\_set\_literal
+### sdk_version_set_literal
 
 _Set literals weren't supported until version 2.2, but this code must be able to
 run on earlier versions._
@@ -383,8 +266,8 @@
 
 #### Example
 
-In a package that defines SDK constraints in the `pubspec.yaml` file that
-have a lower bound that's less than 2.2:
+In a package that defines the SDK constraint (in the pubspec.yaml file),
+with a lower bound of less than 2.2. For example:
 
 ```yaml
 environment:
diff --git a/pkg/analyzer/tool/diagnostics/generate.dart b/pkg/analyzer/tool/diagnostics/generate.dart
index 17350b5..4a27869 100644
--- a/pkg/analyzer/tool/diagnostics/generate.dart
+++ b/pkg/analyzer/tool/diagnostics/generate.dart
@@ -50,7 +50,7 @@
   void writeDocumentation(String outputPath) async {
     IOSink sink = File(outputPath).openWrite();
     _writeHeader(sink);
-    _writeGlossary(sink);
+//    _writeGlossary(sink);
     _writeDiagnostics(sink);
     await sink.flush();
     await sink.close();
@@ -118,7 +118,7 @@
                           .arguments[1] as StringLiteral)
                       .stringValue;
               docs = [
-                '### ${_escape(variableName.toLowerCase())}',
+                '### ${variableName.toLowerCase()}',
                 '',
                 ..._split('_${_escape(message)}_'),
                 '',
@@ -183,21 +183,21 @@
     }
   }
 
-  /// Write the glossary.
-  void _writeGlossary(IOSink sink) {
-    sink.write('''
-
-## Glossary
-
-This page uses the following terms.
-
-### Potentially non-nullable
-
-A type is _potentially non-nullable_ if it's either explicitly non-nullable or
-if it's a type parameter. The latter case is included because the actual runtime
-type might be non-nullable.
-''');
-  }
+//  /// Write the glossary.
+//  void _writeGlossary(IOSink sink) {
+//    sink.write('''
+//
+//## Glossary
+//
+//This page uses the following terms.
+//
+//### Potentially non-nullable
+//
+//A type is _potentially non-nullable_ if it's either explicitly non-nullable or
+//if it's a type parameter. The latter case is included because the actual runtime
+//type might be non-nullable.
+//''');
+//  }
 
   /// Write the header of the file.
   void _writeHeader(IOSink sink) {
@@ -206,6 +206,10 @@
 title: Diagnostics
 description: Details for diagnostics produced by the Dart analyzer.
 ---
+{%- comment %}
+WARNING: Do NOT EDIT this file directly. It is autogenerated by the script in
+`pkg/analyzer/tool/diagnostics/generate.dart` in the sdk repository.
+{% endcomment -%}
 
 This page lists diagnostic messages produced by the Dart analyzer,
 with details about what those messages mean and how you can fix your code.
diff --git a/pkg/analyzer/tool/experiments/generate.dart b/pkg/analyzer/tool/experiments/generate.dart
index d74f223..6527331 100644
--- a/pkg/analyzer/tool/experiments/generate.dart
+++ b/pkg/analyzer/tool/experiments/generate.dart
@@ -101,9 +101,11 @@
 
 mixin _CurrentState {
   /// Current state for the flag "bogus-disabled"
+  @deprecated
   bool get bogus_disabled => isEnabled(ExperimentalFeatures.bogus_disabled);
 
   /// Current state for the flag "bogus-enabled"
+  @deprecated
   bool get bogus_enabled => isEnabled(ExperimentalFeatures.bogus_enabled);
 ''');
     for (var key in keysSorted) {
@@ -136,9 +138,11 @@
     out.write('''
 
       /// String to enable the experiment "bogus-disabled"
+      @deprecated
       static const String bogus_disabled = 'bogus-disabled';
 
       /// String to enable the experiment "bogus-enabled"
+      @deprecated
       static const String bogus_enabled = 'bogus-enabled';
     }''');
   }
@@ -174,15 +178,19 @@
     // TODO(danrubel): Remove bogus entries
     out.write('''
 
+      @deprecated
       static const bogus_disabled = const ExperimentalFeature(
         $index,
+        // ignore: deprecated_member_use_from_same_package
         EnableString.bogus_disabled,
         IsEnabledByDefault.bogus_disabled,
         IsExpired.bogus_disabled,
         null);
 
+      @deprecated
       static const bogus_enabled = const ExperimentalFeature(
         ${index + 1},
+        // ignore: deprecated_member_use_from_same_package
         EnableString.bogus_enabled,
         IsEnabledByDefault.bogus_enabled,
         IsExpired.bogus_enabled,
@@ -210,9 +218,11 @@
     out.write('''
 
       /// Default state of the experiment "bogus-disabled"
+      @deprecated
       static const bool bogus_disabled = false;
 
       /// Default state of the experiment "bogus-enabled"
+      @deprecated
       static const bool bogus_enabled = true;
     }''');
   }
@@ -263,7 +273,9 @@
     // TODO(danrubel): Remove bogus entries
     out.write('''
 
+  // ignore: deprecated_member_use_from_same_package
   EnableString.bogus_disabled: ExperimentalFeatures.bogus_disabled,
+  // ignore: deprecated_member_use_from_same_package
   EnableString.bogus_enabled: ExperimentalFeatures.bogus_enabled,
 };
 ''');
diff --git a/pkg/analyzer/tool/summary/generate.dart b/pkg/analyzer/tool/summary/generate.dart
index d27a4d8..2e0a224 100644
--- a/pkg/analyzer/tool/summary/generate.dart
+++ b/pkg/analyzer/tool/summary/generate.dart
@@ -19,7 +19,7 @@
 import 'dart:io';
 
 import 'package:analysis_tool/tools.dart';
-import 'package:front_end/src/fasta/scanner/string_scanner.dart';
+import 'package:front_end/src/fasta/scanner.dart';
 import 'package:front_end/src/scanner/token.dart' show Token;
 
 import 'idl_model.dart' as idlModel;
@@ -648,8 +648,7 @@
     String idlText =
         idlFile.readAsStringSync().replaceAll(new RegExp('\r\n?'), '\n');
     // Extract a description of the IDL and make sure it is valid.
-    var scanner = new StringScanner(idlText, includeComments: true);
-    var startingToken = scanner.tokenize();
+    var startingToken = scanString(idlText, includeComments: true).tokens;
     var listener = new MiniAstBuilder();
     var parser = new MiniAstParser(listener);
     parser.parseUnit(startingToken);
diff --git a/pkg/analyzer_cli/lib/src/build_mode.dart b/pkg/analyzer_cli/lib/src/build_mode.dart
index 39e0a65..c50528d 100644
--- a/pkg/analyzer_cli/lib/src/build_mode.dart
+++ b/pkg/analyzer_cli/lib/src/build_mode.dart
@@ -195,7 +195,7 @@
   PackageBundleAssembler assembler;
   final Map<String, UnlinkedUnit> uriToUnit = <String, UnlinkedUnit>{};
 
-  final bool buildSummary2 = true;
+  final bool buildSummary2 = false;
   final bool consumeSummary2 = false;
   final Map<String, ParsedUnitResult> inputParsedUnitResults = {};
   summary2.LinkedElementFactory elementFactory;
@@ -398,7 +398,7 @@
 
         var inputUnits = <summary2.LinkInputUnit>[];
         inputUnits.add(
-          summary2.LinkInputUnit(librarySource, false, unit),
+          summary2.LinkInputUnit(null, librarySource, false, unit),
         );
 
         for (var directive in unit.directives) {
@@ -411,7 +411,12 @@
               throw ArgumentError('No parsed unit for part $partPath in $path');
             }
             inputUnits.add(
-              summary2.LinkInputUnit(partSource, false, partParseResult.unit),
+              summary2.LinkInputUnit(
+                partUri,
+                partSource,
+                false,
+                partParseResult.unit,
+              ),
             );
           }
         }
diff --git a/pkg/analyzer_cli/lib/src/error_formatter.dart b/pkg/analyzer_cli/lib/src/error_formatter.dart
index 4bcfb70..e6e61f4 100644
--- a/pkg/analyzer_cli/lib/src/error_formatter.dart
+++ b/pkg/analyzer_cli/lib/src/error_formatter.dart
@@ -111,6 +111,7 @@
   final String message;
   final String errorCode;
   final String correction;
+  final String url;
 
   CLIError({
     this.severity,
@@ -121,6 +122,7 @@
     this.message,
     this.errorCode,
     this.correction,
+    this.url,
   });
 
   @override
@@ -248,10 +250,15 @@
       out.write('${ansi.bullet} ${error.errorCode}');
       out.writeln();
 
-      // If verbose, also print any associated correction.
-      if (options.verbose && error.correction != null) {
-        out.writeln(
-            '${' '.padLeft(error.severity.length + 2)}${error.correction}');
+      // If verbose, also print any associated correction and URL.
+      if (options.verbose) {
+        String padding = ' '.padLeft(error.severity.length + 2);
+        if (error.correction != null) {
+          out.writeln('$padding${error.correction}');
+        }
+        if (error.url != null) {
+          out.writeln('$padding${error.url}');
+        }
       }
     }
 
@@ -303,6 +310,7 @@
       message: message,
       errorCode: error.errorCode.name.toLowerCase(),
       correction: error.correction,
+      url: error.errorCode.url,
     ));
   }
 }
diff --git a/pkg/compiler/lib/src/backend_strategy.dart b/pkg/compiler/lib/src/backend_strategy.dart
index d9a48b1..b5307e1 100644
--- a/pkg/compiler/lib/src/backend_strategy.dart
+++ b/pkg/compiler/lib/src/backend_strategy.dart
@@ -12,18 +12,24 @@
 import 'elements/entities.dart';
 import 'inferrer/types.dart';
 import 'io/source_information.dart';
+import 'js_backend/backend.dart';
+import 'js_backend/enqueuer.dart';
 import 'js_backend/inferred_data.dart';
-import 'js_backend/interceptor_data.dart';
-import 'js_backend/native_data.dart';
+import 'js_emitter/code_emitter_task.dart';
 import 'serialization/serialization.dart';
 import 'ssa/ssa.dart';
 import 'universe/codegen_world_builder.dart';
-import 'universe/world_builder.dart';
 import 'world.dart';
 
 /// Strategy pattern that defines the element model used in type inference
 /// and code generation.
 abstract class BackendStrategy {
+  List<CompilerTask> get tasks;
+
+  FunctionCompiler get functionCompiler;
+
+  CodeEmitterTask get emitterTask;
+
   /// Create the [JClosedWorld] from [closedWorld].
   JClosedWorld createJClosedWorld(
       KClosedWorld closedWorld, OutputUnitData outputUnitData);
@@ -34,24 +40,27 @@
   /// This is used to support serialization after type inference.
   void registerJClosedWorld(JClosedWorld closedWorld);
 
-  /// Creates the [CodegenWorldBuilder] used by the codegen enqueuer.
-  CodegenWorldBuilder createCodegenWorldBuilder(
-      NativeBasicData nativeBasicData,
-      JClosedWorld closedWorld,
-      SelectorConstraintsStrategy selectorConstraintsStrategy,
-      OneShotInterceptorData oneShotInterceptorData);
+  /// Called when the compiler starts running the codegen.
+  ///
+  /// Returns the [CodegenInputs] objects with the needed data.
+  CodegenInputs onCodegenStart(
+      GlobalTypeInferenceResults globalTypeInferenceResults);
 
-  /// Creates the [WorkItemBuilder] used by the codegen enqueuer.
-  WorkItemBuilder createCodegenWorkItemBuilder(
-      JClosedWorld closedWorld, CodegenResults codegenResults);
+  /// Creates an [Enqueuer] for code generation specific to this backend.
+  CodegenEnqueuer createCodegenEnqueuer(
+      CompilerTask task,
+      JClosedWorld closedWorld,
+      GlobalTypeInferenceResults globalInferenceResults,
+      CodegenInputs codegen,
+      CodegenResults codegenResults);
+
+  /// Called when code generation has been completed.
+  void onCodegenEnd(CodegenInputs codegen);
 
   /// Creates the [SsaBuilder] used for the element model.
   SsaBuilder createSsaBuilder(
       CompilerTask task, SourceInformationStrategy sourceInformationStrategy);
 
-  /// Returns the [SourceInformationStrategy] use for the element model.
-  SourceInformationStrategy get sourceInformationStrategy;
-
   /// Creates a [SourceSpan] from [spannable] in context of [currentElement].
   SourceSpan spanFromSpannable(Spannable spannable, Entity currentElement);
 
@@ -69,4 +78,8 @@
 
   /// Prepare [source] to deserialize modular code generation data.
   void prepareCodegenReader(DataSource source);
+
+  /// Generates the output and returns the total size of the generated code.
+  int assembleProgram(JClosedWorld closedWorld, InferredData inferredData,
+      CodegenInputs codegenInputs, CodegenWorld codegenWorld);
 }
diff --git a/pkg/compiler/lib/src/closure.dart b/pkg/compiler/lib/src/closure.dart
index beb4c95..2429d36 100644
--- a/pkg/compiler/lib/src/closure.dart
+++ b/pkg/compiler/lib/src/closure.dart
@@ -27,7 +27,7 @@
   /// used inside the scope of [node].
   ScopeInfo getScopeInfo(MemberEntity member);
 
-  ClosureRepresentationInfo getClosureInfo(ir.Node localFunction);
+  ClosureRepresentationInfo getClosureInfo(ir.LocalFunction localFunction);
 
   /// Look up information about a loop, in case any variables it declares need
   /// to be boxed/snapshotted.
diff --git a/pkg/compiler/lib/src/common/names.dart b/pkg/compiler/lib/src/common/names.dart
index a8eaea4..d567451 100644
--- a/pkg/compiler/lib/src/common/names.dart
+++ b/pkg/compiler/lib/src/common/names.dart
@@ -43,6 +43,24 @@
 
   /// The name of the signature function in closure classes.
   static const String signature = ':signature';
+
+  /// The name of the 'JS' foreign function.
+  static const String JS = 'JS';
+
+  /// The name of the 'JS_BUILTIN' foreign function.
+  static const String JS_BUILTIN = 'JS_BUILTIN';
+
+  /// The name of the 'JS_EMBEDDED_GLOBAL' foreign function.
+  static const String JS_EMBEDDED_GLOBAL = 'JS_EMBEDDED_GLOBAL';
+
+  /// The name of the 'JS_INTERCEPTOR_CONSTANT' foreign function.
+  static const String JS_INTERCEPTOR_CONSTANT = 'JS_INTERCEPTOR_CONSTANT';
+
+  /// The name of the 'JS_STRING_CONCAT' foreign function.
+  static const String JS_STRING_CONCAT = 'JS_STRING_CONCAT';
+
+  /// The name of the 'DART_CLOSURE_TO_JS' foreign function.
+  static const String DART_CLOSURE_TO_JS = 'DART_CLOSURE_TO_JS';
 }
 
 /// [Name]s commonly used.
diff --git a/pkg/compiler/lib/src/common_elements.dart b/pkg/compiler/lib/src/common_elements.dart
index 36d7296..2778f65 100644
--- a/pkg/compiler/lib/src/common_elements.dart
+++ b/pkg/compiler/lib/src/common_elements.dart
@@ -99,6 +99,9 @@
   LibraryEntity get foreignLibrary;
 
   /// The dart:_internal library.
+  LibraryEntity get rtiLibrary;
+
+  /// The dart:_internal library.
   LibraryEntity get internalLibrary;
 
   /// The `NativeTypedData` class from dart:typed_data.
@@ -475,6 +478,15 @@
 
   FunctionEntity get extractFunctionTypeObjectFromInternal;
 
+  // From dart:_rti
+
+  FunctionEntity get findType;
+  FieldEntity get rtiAsField;
+  FieldEntity get rtiCheckField;
+  FieldEntity get rtiIsField;
+  FunctionEntity get rtiEvalMethod;
+  FunctionEntity get rtiBindMethod;
+
   // From dart:_internal
 
   ClassEntity get symbolImplementationClass;
@@ -729,6 +741,11 @@
   LibraryEntity get foreignLibrary =>
       _foreignLibrary ??= _env.lookupLibrary(Uris.dart__foreign_helper);
 
+  LibraryEntity _rtiLibrary;
+  @override
+  LibraryEntity get rtiLibrary =>
+      _rtiLibrary ??= _env.lookupLibrary(Uris.dart__rti, required: true);
+
   /// Reference to the internal library to lookup functions to always inline.
   LibraryEntity _internalLibrary;
   @override
@@ -1785,6 +1802,42 @@
         cls.name.startsWith('Instantiation');
   }
 
+  // From dart:_rti
+
+  FunctionEntity _findRtiFunction(String name) =>
+      _findLibraryMember(rtiLibrary, name);
+
+  FunctionEntity _findType;
+  @override
+  FunctionEntity get findType => _findType ??= _findRtiFunction('findType');
+
+  ClassEntity get _rtiImplClass => _findClass(rtiLibrary, 'Rti');
+  FieldEntity _findRtiClassField(String name) =>
+      _findClassMember(_rtiImplClass, name);
+
+  FieldEntity _rtiAsField;
+  @override
+  FieldEntity get rtiAsField => _rtiAsField ??= _findRtiClassField('_as');
+
+  FieldEntity _rtiIsField;
+  @override
+  FieldEntity get rtiIsField => _rtiIsField ??= _findRtiClassField('_is');
+
+  FieldEntity _rtiCheckField;
+  @override
+  FieldEntity get rtiCheckField =>
+      _rtiCheckField ??= _findRtiClassField('_check');
+
+  FunctionEntity _rtiEvalMethod;
+  @override
+  FunctionEntity get rtiEvalMethod =>
+      _rtiEvalMethod ??= _findClassMember(_rtiImplClass, '_eval');
+
+  FunctionEntity _rtiBindMethod;
+  @override
+  FunctionEntity get rtiBindMethod =>
+      _rtiBindMethod ??= _findClassMember(_rtiImplClass, '_bind');
+
   // From dart:_internal
 
   ClassEntity _symbolImplementationClass;
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index ed69778..e10e024 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -32,7 +32,7 @@
 import 'inferrer/types.dart'
     show GlobalTypeInferenceResults, GlobalTypeInferenceTask;
 import 'io/source_information.dart' show SourceInformation;
-import 'js_backend/backend.dart' show CodegenInputs, JavaScriptBackend;
+import 'js_backend/backend.dart' show CodegenInputs, JavaScriptImpactStrategy;
 import 'js_backend/inferred_data.dart';
 import 'js_model/js_strategy.dart';
 import 'kernel/kernel_strategy.dart';
@@ -97,7 +97,6 @@
   List<CompilerTask> tasks;
   KernelLoaderTask kernelLoader;
   GlobalTypeInferenceTask globalInference;
-  JavaScriptBackend backend;
   CodegenWorldBuilder _codegenWorldBuilder;
 
   AbstractValueStrategy abstractValueStrategy;
@@ -148,9 +147,9 @@
       _reporter = new CompilerDiagnosticReporter(this, options);
     }
     kernelFrontEndTask = new GenericTask('Front end', measurer);
-    frontendStrategy = new KernelFrontEndStrategy(
+    frontendStrategy = new KernelFrontendStrategy(
         kernelFrontEndTask, options, reporter, environment);
-    backendStrategy = new JsBackendStrategy(this);
+    backendStrategy = createBackendStrategy();
     _impactCache = <Entity, WorldImpact>{};
     _impactCacheDeleter = new _MapImpactCacheDeleter(_impactCache);
 
@@ -158,8 +157,7 @@
       progress = new InteractiveProgress();
     }
 
-    backend = createBackend();
-    enqueuer = backend.makeEnqueuer();
+    enqueuer = new EnqueueTask(this);
 
     tasks = [
       kernelLoader = new KernelLoaderTask(
@@ -176,17 +174,14 @@
           options, reporter, provider, outputProvider, measurer),
     ];
 
-    tasks.addAll(backend.tasks);
+    tasks.addAll(backendStrategy.tasks);
   }
 
-  /// Creates the backend.
+  /// Creates the backend strategy.
   ///
-  /// Override this to mock the backend for testing.
-  JavaScriptBackend createBackend() {
-    return new JavaScriptBackend(this,
-        generateSourceMap: options.generateSourceMap,
-        useMultiSourceInfo: options.useMultiSourceInfo,
-        useNewSourceInfo: options.useNewSourceInfo);
+  /// Override this to mock the backend strategy for testing.
+  BackendStrategy createBackendStrategy() {
+    return new JsBackendStrategy(this);
   }
 
   ResolutionWorldBuilder get resolutionWorldBuilder =>
@@ -247,11 +242,6 @@
       if (options.cfeOnly) return;
 
       frontendStrategy.registerLoadedLibraries(result);
-      for (Uri uri in result.libraries) {
-        LibraryEntity library =
-            frontendStrategy.elementEnvironment.lookupLibrary(uri);
-        backend.setAnnotations(library);
-      }
 
       // TODO(efortuna, sigmund): These validation steps should be done in the
       // front end for the Kernel path since Kernel doesn't have the notion of
@@ -272,7 +262,7 @@
     backendStrategy.registerJClosedWorld(closedWorld);
     phase = PHASE_COMPILING;
     CodegenInputs codegenInputs =
-        backend.onCodegenStart(globalTypeInferenceResults);
+        backendStrategy.onCodegenStart(globalTypeInferenceResults);
 
     if (options.readCodegenUri != null) {
       CodegenResults codegenResults =
@@ -283,7 +273,9 @@
     } else {
       reporter.log('Compiling methods');
       CodegenResults codegenResults = new OnDemandCodegenResults(
-          globalTypeInferenceResults, codegenInputs, backend.functionCompiler);
+          globalTypeInferenceResults,
+          codegenInputs,
+          backendStrategy.functionCompiler);
       if (options.writeCodegenUri != null) {
         serializationTask.serializeCodegen(backendStrategy, codegenResults);
       } else {
@@ -320,7 +312,7 @@
       resolutionEnqueuer = enqueuer.resolution;
     } else {
       resolutionEnqueuer = enqueuer.createResolutionEnqueuer();
-      backend.onResolutionStart();
+      frontendStrategy.onResolutionStart();
     }
     return resolutionEnqueuer;
   }
@@ -344,7 +336,9 @@
     // something to the resolution queue.  So we cannot wait with
     // this until after the resolution queue is processed.
     deferredLoadTask.beforeResolution(rootLibraryUri, libraries);
-    impactStrategy = backend.createImpactStrategy(
+
+    impactStrategy = new JavaScriptImpactStrategy(
+        impactCacheDeleter, dumpInfoTask,
         supportDeferredLoad: deferredLoadTask.isProgramSplit,
         supportDumpInfo: options.dumpInfo);
 
@@ -355,7 +349,7 @@
     processQueue(
         frontendStrategy.elementEnvironment, resolutionEnqueuer, mainFunction,
         onProgress: showResolutionProgress);
-    backend.onResolutionEnd();
+    frontendStrategy.onResolutionEnd();
     resolutionEnqueuer.logSummary(reporter.log);
 
     _reporter.reportSuppressedMessagesSummary();
@@ -402,7 +396,7 @@
       codegenWorldForTesting = codegenWorld;
     }
     reporter.log('Emitting JavaScript');
-    int programSize = backend.assembleProgram(closedWorld,
+    int programSize = backendStrategy.assembleProgram(closedWorld,
         globalInferenceResults.inferredData, codegenInputs, codegenWorld);
 
     if (options.dumpInfo) {
@@ -410,7 +404,7 @@
       dumpInfoTask.dumpInfo(closedWorld, globalInferenceResults);
     }
 
-    backend.onCodegenEnd(codegenInputs);
+    backendStrategy.onCodegenEnd(codegenInputs);
 
     checkQueue(codegenEnqueuer);
   }
diff --git a/pkg/compiler/lib/src/dump_info.dart b/pkg/compiler/lib/src/dump_info.dart
index 454d157..83f3790 100644
--- a/pkg/compiler/lib/src/dump_info.dart
+++ b/pkg/compiler/lib/src/dump_info.dart
@@ -12,6 +12,7 @@
 import 'package:dart2js_info/binary_serialization.dart' as dump_info;
 
 import '../compiler_new.dart';
+import 'backend_strategy.dart';
 import 'common/names.dart';
 import 'common/tasks.dart' show CompilerTask;
 import 'common.dart';
@@ -24,7 +25,6 @@
 import 'inferrer/types.dart'
     show GlobalTypeInferenceMemberResult, GlobalTypeInferenceResults;
 import 'js/js.dart' as jsAst;
-import 'js_backend/js_backend.dart' show JavaScriptBackend;
 import 'js_backend/field_analysis.dart';
 import 'universe/codegen_world_builder.dart';
 import 'universe/world_impact.dart'
@@ -204,7 +204,7 @@
 
     classInfo.size = size;
 
-    if (!compiler.backend.emitterTask.neededClasses.contains(clazz) &&
+    if (!compiler.backendStrategy.emitterTask.neededClasses.contains(clazz) &&
         classInfo.fields.isEmpty &&
         classInfo.functions.isEmpty) {
       return null;
@@ -344,13 +344,13 @@
     return _outputToInfo.putIfAbsent(outputUnit, () {
       // Dump-info currently only works with the full emitter. If another
       // emitter is used it will fail here.
-      JavaScriptBackend backend = compiler.backend;
+      BackendStrategy backendStrategy = compiler.backendStrategy;
       assert(outputUnit.name != null || outputUnit.isMainOutput);
       var filename = outputUnit.isMainOutput
           ? compiler.options.outputUri.pathSegments.last
           : deferredPartFileName(compiler.options, outputUnit.name);
       OutputUnitInfo info = new OutputUnitInfo(filename, outputUnit.name,
-          backend.emitterTask.emitter.generatedSize(outputUnit));
+          backendStrategy.emitterTask.emitter.generatedSize(outputUnit));
       info.imports
           .addAll(closedWorld.outputUnitData.getImportNames(outputUnit));
       result.outputUnits.add(info);
diff --git a/pkg/compiler/lib/src/enqueue.dart b/pkg/compiler/lib/src/enqueue.dart
index 81ca07d..9529615 100644
--- a/pkg/compiler/lib/src/enqueue.dart
+++ b/pkg/compiler/lib/src/enqueue.dart
@@ -61,7 +61,7 @@
   }
 
   ResolutionEnqueuer createResolutionEnqueuer() {
-    return _resolution ??= compiler.backend
+    return _resolution ??= compiler.frontendStrategy
         .createResolutionEnqueuer(this, compiler)
           ..onEmptyForTesting = compiler.onResolutionQueueEmptyForTesting;
   }
@@ -71,7 +71,7 @@
       GlobalTypeInferenceResults globalInferenceResults,
       CodegenInputs codegenInputs,
       CodegenResults codegenResults) {
-    Enqueuer enqueuer = compiler.backend.createCodegenEnqueuer(this, compiler,
+    Enqueuer enqueuer = compiler.backendStrategy.createCodegenEnqueuer(this,
         closedWorld, globalInferenceResults, codegenInputs, codegenResults)
       ..onEmptyForTesting = compiler.onCodegenQueueEmptyForTesting;
     if (retainDataForTesting) {
diff --git a/pkg/compiler/lib/src/frontend_strategy.dart b/pkg/compiler/lib/src/frontend_strategy.dart
index 4d0e320..5ae38ec 100644
--- a/pkg/compiler/lib/src/frontend_strategy.dart
+++ b/pkg/compiler/lib/src/frontend_strategy.dart
@@ -4,27 +4,16 @@
 
 library dart2js.frontend_strategy;
 
-import 'common/backend_api.dart';
 import 'common.dart';
+import 'common/tasks.dart';
 import 'common_elements.dart';
 import 'compiler.dart' show Compiler;
 import 'deferred_load.dart' show DeferredLoadTask;
 import 'elements/entities.dart';
-import 'elements/types.dart';
 import 'enqueue.dart';
-import 'js_backend/annotations.dart';
-import 'js_backend/field_analysis.dart' show KFieldAnalysis;
-import 'js_backend/backend_usage.dart';
-import 'js_backend/interceptor_data.dart';
 import 'js_backend/native_data.dart';
 import 'js_backend/no_such_method_registry.dart';
-import 'js_backend/runtime_types.dart';
 import 'kernel/loader.dart';
-import 'native/enqueue.dart' show NativeResolutionEnqueuer;
-import 'native/resolver.dart';
-import 'universe/class_hierarchy.dart';
-import 'universe/resolution_world_builder.dart';
-import 'universe/world_builder.dart';
 import 'universe/world_impact.dart';
 
 /// Strategy pattern that defines the connection between the input format and
@@ -41,63 +30,27 @@
   /// strategy.
   CommonElements get commonElements;
 
-  /// Returns the [DartTypes] for the element model used in this strategy.
-  DartTypes get dartTypes;
-
-  /// Returns the [AnnotationProcessor] for this strategy.
-  AnnotationProcessor get annotationProcessor;
-
   NativeBasicData get nativeBasicData;
 
   /// Creates a [DeferredLoadTask] for the element model used in this strategy.
   DeferredLoadTask createDeferredLoadTask(Compiler compiler);
 
-  /// Creates the [NativeClassFinder] for this strategy.
-  NativeClassFinder createNativeClassFinder(NativeBasicData nativeBasicData);
+  /// Support for classifying `noSuchMethod` implementations.
+  NoSuchMethodRegistry get noSuchMethodRegistry;
 
-  /// Creates the [NoSuchMethodResolver] corresponding the resolved model of
-  /// this strategy.
-  NoSuchMethodResolver createNoSuchMethodResolver();
+  /// Called before processing of the resolution queue is started.
+  void onResolutionStart();
 
-  /// Returns the [RuntimeTypesNeedBuilder] used by this frontend strategy.
-  RuntimeTypesNeedBuilder get runtimeTypesNeedBuilderForTesting;
+  ResolutionEnqueuer createResolutionEnqueuer(
+      CompilerTask task, Compiler compiler);
 
-  /// Creates the [ResolutionWorldBuilder] corresponding to the element model
-  /// used in this strategy.
-  ResolutionWorldBuilder createResolutionWorldBuilder(
-      NativeBasicData nativeBasicData,
-      NativeDataBuilder nativeDataBuilder,
-      InterceptorDataBuilder interceptorDataBuilder,
-      BackendUsageBuilder backendUsageBuilder,
-      RuntimeTypesNeedBuilder rtiNeedBuilder,
-      KFieldAnalysis allocatorAnalysis,
-      NativeResolutionEnqueuer nativeResolutionEnqueuer,
-      NoSuchMethodRegistry noSuchMethodRegistry,
-      AnnotationsDataBuilder annotationsDataBuilder,
-      SelectorConstraintsStrategy selectorConstraintsStrategy,
-      ClassHierarchyBuilder classHierarchyBuilder,
-      ClassQueries classQueries);
-
-  /// Creates the [WorkItemBuilder] corresponding to how a resolved model for
-  /// a single member is obtained in this strategy.
-  WorkItemBuilder createResolutionWorkItemBuilder(
-      NativeBasicData nativeBasicData,
-      NativeDataBuilder nativeDataBuilder,
-      AnnotationsDataBuilder annotationsDataBuilder,
-      ImpactTransformer impactTransformer,
-      Map<Entity, WorldImpact> impactCache,
-      KFieldAnalysis fieldAnalysis);
+  /// Called when the resolution queue has been closed.
+  void onResolutionEnd();
 
   /// Computes the main function from [mainLibrary] adding additional world
   /// impact to [impactBuilder].
   FunctionEntity computeMain(WorldImpactBuilder impactBuilder);
 
-  /// Creates the [RuntimeTypesNeedBuilder] for this strategy.
-  RuntimeTypesNeedBuilder createRuntimeTypesNeedBuilder();
-
-  /// Creates the [ClassQueries] for this strategy.
-  ClassQueries createClassQueries();
-
   /// Creates a [SourceSpan] from [spannable] in context of [currentElement].
   SourceSpan spanFromSpannable(Spannable spannable, Entity currentElement);
 }
@@ -112,24 +65,6 @@
       NativeBasicData nativeBasicData, NativeDataBuilder nativeDataBuilder);
 }
 
-abstract class FrontendStrategyBase implements FrontendStrategy {
-  final NativeBasicDataBuilderImpl nativeBasicDataBuilder =
-      new NativeBasicDataBuilderImpl();
-  NativeBasicData _nativeBasicData;
-
-  @override
-  NativeBasicData get nativeBasicData {
-    if (_nativeBasicData == null) {
-      _nativeBasicData = nativeBasicDataBuilder.close(elementEnvironment);
-      assert(
-          _nativeBasicData != null,
-          failedAt(NO_LOCATION_SPANNABLE,
-              "NativeBasicData has not been computed yet."));
-    }
-    return _nativeBasicData;
-  }
-}
-
 /// Class that deletes the contents of an [WorldImpact] cache.
 // TODO(redemption): this can be deleted when we sunset the old front end.
 abstract class ImpactCacheDeleter {
diff --git a/pkg/compiler/lib/src/inferrer/builder_kernel.dart b/pkg/compiler/lib/src/inferrer/builder_kernel.dart
index 585c560..d34c1d0 100644
--- a/pkg/compiler/lib/src/inferrer/builder_kernel.dart
+++ b/pkg/compiler/lib/src/inferrer/builder_kernel.dart
@@ -17,7 +17,6 @@
 import '../ir/constants.dart';
 import '../ir/static_type_provider.dart';
 import '../ir/util.dart';
-import '../js_backend/backend.dart';
 import '../js_backend/field_analysis.dart';
 import '../js_model/element_map.dart';
 import '../js_model/locals.dart' show JumpVisitor;
@@ -1244,22 +1243,22 @@
       AbstractValue mask) {
     String name = function.name;
     handleStaticInvoke(node, selector, mask, function, arguments);
-    if (name == JavaScriptBackend.JS) {
+    if (name == Identifiers.JS) {
       NativeBehavior nativeBehavior =
           _elementMap.getNativeBehaviorForJsCall(node);
       _sideEffectsBuilder.add(nativeBehavior.sideEffects);
       return _inferrer.typeOfNativeBehavior(nativeBehavior);
-    } else if (name == JavaScriptBackend.JS_EMBEDDED_GLOBAL) {
+    } else if (name == Identifiers.JS_EMBEDDED_GLOBAL) {
       NativeBehavior nativeBehavior =
           _elementMap.getNativeBehaviorForJsEmbeddedGlobalCall(node);
       _sideEffectsBuilder.add(nativeBehavior.sideEffects);
       return _inferrer.typeOfNativeBehavior(nativeBehavior);
-    } else if (name == JavaScriptBackend.JS_BUILTIN) {
+    } else if (name == Identifiers.JS_BUILTIN) {
       NativeBehavior nativeBehavior =
           _elementMap.getNativeBehaviorForJsBuiltinCall(node);
       _sideEffectsBuilder.add(nativeBehavior.sideEffects);
       return _inferrer.typeOfNativeBehavior(nativeBehavior);
-    } else if (name == JavaScriptBackend.JS_STRING_CONCAT) {
+    } else if (name == Identifiers.JS_STRING_CONCAT) {
       return _types.stringType;
     } else {
       _sideEffectsBuilder.setAllSideEffects();
diff --git a/pkg/compiler/lib/src/inferrer/closure_tracer.dart b/pkg/compiler/lib/src/inferrer/closure_tracer.dart
index 7558fcc..3ee6a53 100644
--- a/pkg/compiler/lib/src/inferrer/closure_tracer.dart
+++ b/pkg/compiler/lib/src/inferrer/closure_tracer.dart
@@ -4,9 +4,8 @@
 
 library compiler.src.inferrer.closure_tracer;
 
-import '../common/names.dart' show Names;
+import '../common/names.dart' show Identifiers, Names;
 import '../elements/entities.dart';
-import '../js_backend/backend.dart' show JavaScriptBackend;
 import '../universe/selector.dart' show Selector;
 import 'abstract_value_domain.dart';
 import 'debug.dart' as debug;
@@ -85,7 +84,7 @@
     MemberEntity called = info.calledElement;
     if (inferrer.closedWorld.commonElements.isForeign(called)) {
       String name = called.name;
-      if (name == JavaScriptBackend.JS || name == 'DART_CLOSURE_TO_JS') {
+      if (name == Identifiers.JS || name == Identifiers.DART_CLOSURE_TO_JS) {
         bailout('Used in JS ${info.debugName}');
       }
     }
diff --git a/pkg/compiler/lib/src/inferrer/inferrer_engine.dart b/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
index a33cffc..6266882 100644
--- a/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
+++ b/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
@@ -725,13 +725,8 @@
         }
         break;
       case MemberKind.closureCall:
-        ir.TreeNode node = definition.node;
-        if (node is ir.FunctionDeclaration) {
-          return node.function;
-        } else if (node is ir.FunctionExpression) {
-          return node.function;
-        }
-        break;
+        ir.LocalFunction node = definition.node;
+        return node.function;
       case MemberKind.closureField:
       case MemberKind.signature:
       case MemberKind.generatorBody:
@@ -1361,8 +1356,7 @@
     bool isClosure = false;
     if (functionNode.parent is ir.Member) {
       member = _closedWorld.elementMap.getMember(functionNode.parent);
-    } else if (functionNode.parent is ir.FunctionExpression ||
-        functionNode.parent is ir.FunctionDeclaration) {
+    } else if (functionNode.parent is ir.LocalFunction) {
       ClosureRepresentationInfo info =
           _closedWorld.closureDataLookup.getClosureInfo(functionNode.parent);
       member = info.callMethod;
diff --git a/pkg/compiler/lib/src/inferrer/list_tracer.dart b/pkg/compiler/lib/src/inferrer/list_tracer.dart
index 0f7aa07..aaa69e4 100644
--- a/pkg/compiler/lib/src/inferrer/list_tracer.dart
+++ b/pkg/compiler/lib/src/inferrer/list_tracer.dart
@@ -4,8 +4,8 @@
 
 library compiler.src.inferrer.list_tracer;
 
+import '../common/names.dart';
 import '../elements/entities.dart';
-import '../js_backend/backend.dart' show JavaScriptBackend;
 import '../universe/selector.dart' show Selector;
 import '../util/util.dart' show Setlet;
 import 'node_tracer.dart';
@@ -164,7 +164,7 @@
     super.visitStaticCallSiteTypeInformation(info);
     MemberEntity called = info.calledElement;
     if (inferrer.closedWorld.commonElements.isForeign(called) &&
-        called.name == JavaScriptBackend.JS) {
+        called.name == Identifiers.JS) {
       bailout('Used in JS ${info.debugName}');
     }
   }
diff --git a/pkg/compiler/lib/src/inferrer/map_tracer.dart b/pkg/compiler/lib/src/inferrer/map_tracer.dart
index 4925dfc..f21fd15 100644
--- a/pkg/compiler/lib/src/inferrer/map_tracer.dart
+++ b/pkg/compiler/lib/src/inferrer/map_tracer.dart
@@ -4,8 +4,8 @@
 
 library compiler.src.inferrer.map_tracer;
 
+import '../common/names.dart';
 import '../elements/entities.dart';
-import '../js_backend/backend.dart' show JavaScriptBackend;
 import '../universe/selector.dart' show Selector;
 import 'node_tracer.dart';
 import 'type_graph_nodes.dart';
@@ -68,7 +68,7 @@
     super.visitStaticCallSiteTypeInformation(info);
     MemberEntity called = info.calledElement;
     if (inferrer.closedWorld.commonElements.isForeign(called) &&
-        called.name == JavaScriptBackend.JS) {
+        called.name == Identifiers.JS) {
       bailout('Used in JS ${info.debugName}');
     }
   }
diff --git a/pkg/compiler/lib/src/inferrer/set_tracer.dart b/pkg/compiler/lib/src/inferrer/set_tracer.dart
index 6c809ad..72cf043 100644
--- a/pkg/compiler/lib/src/inferrer/set_tracer.dart
+++ b/pkg/compiler/lib/src/inferrer/set_tracer.dart
@@ -4,8 +4,8 @@
 
 library compiler.src.inferrer.set_tracer;
 
+import '../common/names.dart';
 import '../elements/entities.dart';
-import '../js_backend/backend.dart' show JavaScriptBackend;
 import '../universe/selector.dart' show Selector;
 import 'node_tracer.dart';
 import 'type_graph_nodes.dart';
@@ -93,7 +93,7 @@
     super.visitStaticCallSiteTypeInformation(info);
     MemberEntity called = info.calledElement;
     if (inferrer.closedWorld.commonElements.isForeign(called) &&
-        called.name == JavaScriptBackend.JS) {
+        called.name == Identifiers.JS) {
       bailout('Used in JS ${info.debugName}');
     }
   }
diff --git a/pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart b/pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart
index d1b919c..3a41951 100644
--- a/pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart
+++ b/pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart
@@ -76,7 +76,7 @@
         _compiler.reporter,
         _compiler.outputProvider,
         closedWorld,
-        _compiler.backend.noSuchMethodRegistry,
+        _compiler.frontendStrategy.noSuchMethodRegistry,
         main,
         _inferredDataBuilder);
   }
diff --git a/pkg/compiler/lib/src/io/kernel_source_information.dart b/pkg/compiler/lib/src/io/kernel_source_information.dart
index 520fbb40..5bc26a3 100644
--- a/pkg/compiler/lib/src/io/kernel_source_information.dart
+++ b/pkg/compiler/lib/src/io/kernel_source_information.dart
@@ -137,32 +137,24 @@
   SourceInformation _buildFunctionEnd(MemberEntity member, [ir.TreeNode base]) {
     MemberDefinition definition = _elementMap.getMemberDefinition(member);
     String name = computeKernelElementNameForSourceMaps(_elementMap, member);
-    ir.Node node = definition.node;
     switch (definition.kind) {
       case MemberKind.regular:
+        ir.Member node = definition.node;
         if (node is ir.Procedure) {
           return _buildFunction(name, base ?? node, node.function);
         }
         break;
       case MemberKind.constructor:
       case MemberKind.constructorBody:
-        if (node is ir.Procedure) {
-          return _buildFunction(name, base ?? node, node.function);
-        } else if (node is ir.Constructor) {
-          return _buildFunction(name, base ?? node, node.function);
-        }
-        break;
+        ir.Member node = definition.node;
+        return _buildFunction(name, base ?? node, node.function);
       case MemberKind.closureCall:
-        if (node is ir.FunctionDeclaration) {
-          return _buildFunction(name, base ?? node, node.function);
-        } else if (node is ir.FunctionExpression) {
-          return _buildFunction(name, base ?? node, node.function);
-        }
-        break;
+        ir.LocalFunction node = definition.node;
+        return _buildFunction(name, base ?? node, node.function);
       // TODO(sra): generatorBody
       default:
     }
-    return _buildTreeNode(base ?? node, name: name);
+    return _buildTreeNode(base ?? definition.node, name: name);
   }
 
   /// Creates the source information for exiting a function definition defined
@@ -220,18 +212,11 @@
         }
         break;
       case MemberKind.closureCall:
-        ir.Node node = definition.node;
-        if (node is ir.FunctionDeclaration) {
-          return _buildBody(node, node.function.body);
-        } else if (node is ir.FunctionExpression) {
-          return _buildBody(node, node.function.body);
-        }
-        break;
+        ir.LocalFunction node = definition.node;
+        return _buildBody(node, node.function.body);
       case MemberKind.generatorBody:
         ir.Node node = definition.node;
-        if (node is ir.FunctionDeclaration) {
-          return _buildBody(node, node.function.body);
-        } else if (node is ir.FunctionExpression) {
+        if (node is ir.LocalFunction) {
           return _buildBody(node, node.function.body);
         } else if (node is ir.Member && node.function != null) {
           return _buildBody(node, node.function.body);
@@ -254,21 +239,11 @@
         break;
       case MemberKind.constructor:
       case MemberKind.constructorBody:
-        ir.Node node = definition.node;
-        if (node is ir.Procedure) {
-          return _buildFunctionExit(node, node.function);
-        } else if (node is ir.Constructor) {
-          return _buildFunctionExit(node, node.function);
-        }
-        break;
+        ir.Member node = definition.node;
+        return _buildFunctionExit(node, node.function);
       case MemberKind.closureCall:
-        ir.Node node = definition.node;
-        if (node is ir.FunctionDeclaration) {
-          return _buildFunctionExit(node, node.function);
-        } else if (node is ir.FunctionExpression) {
-          return _buildFunctionExit(node, node.function);
-        }
-        break;
+        ir.LocalFunction node = definition.node;
+        return _buildFunctionExit(node, node.function);
       default:
     }
     return _buildTreeNode(definition.node);
diff --git a/pkg/compiler/lib/src/ir/closure.dart b/pkg/compiler/lib/src/ir/closure.dart
index d237f3f..102ac3e 100644
--- a/pkg/compiler/lib/src/ir/closure.dart
+++ b/pkg/compiler/lib/src/ir/closure.dart
@@ -14,8 +14,8 @@
       <ir.Node, KernelCapturedScope>{};
 
   /// Collected [ScopeInfo] data for nodes.
-  Map<ir.TreeNode, KernelScopeInfo> closuresToGenerate =
-      <ir.TreeNode, KernelScopeInfo>{};
+  Map<ir.LocalFunction, KernelScopeInfo> closuresToGenerate =
+      <ir.LocalFunction, KernelScopeInfo>{};
 
   @override
   String toString() {
@@ -227,8 +227,7 @@
 class VariableUse {
   final VariableUseKind kind;
   final ir.Member member;
-  final ir.TreeNode /*ir.FunctionDeclaration|ir.FunctionExpression*/
-      localFunction;
+  final ir.LocalFunction localFunction;
   final ir.MethodInvocation invocation;
   final ir.Instantiation instantiation;
 
@@ -248,10 +247,7 @@
       : this.kind = VariableUseKind.localParameter,
         this.member = null,
         this.invocation = null,
-        this.instantiation = null {
-    assert(localFunction is ir.FunctionDeclaration ||
-        localFunction is ir.FunctionExpression);
-  }
+        this.instantiation = null;
 
   VariableUse.memberReturnType(this.member)
       : this.kind = VariableUseKind.memberReturnType,
@@ -263,10 +259,7 @@
       : this.kind = VariableUseKind.localReturnType,
         this.member = null,
         this.invocation = null,
-        this.instantiation = null {
-    assert(localFunction is ir.FunctionDeclaration ||
-        localFunction is ir.FunctionExpression);
-  }
+        this.instantiation = null;
 
   VariableUse.constructorTypeArgument(this.member)
       : this.kind = VariableUseKind.constructorTypeArgument,
@@ -289,10 +282,7 @@
   VariableUse.localTypeArgument(this.localFunction, this.invocation)
       : this.kind = VariableUseKind.localTypeArgument,
         this.member = null,
-        this.instantiation = null {
-    assert(localFunction is ir.FunctionDeclaration ||
-        localFunction is ir.FunctionExpression);
-  }
+        this.instantiation = null;
 
   VariableUse.instantiationTypeArgument(this.instantiation)
       : this.kind = VariableUseKind.instantiationTypeArgument,
@@ -388,9 +378,7 @@
     } else {
       // We have a generic local function type variable, like `T` in
       // `m() { local<T>() { ... } ... }`.
-      assert(
-          typeDeclaration.parent is ir.FunctionExpression ||
-              typeDeclaration.parent is ir.FunctionDeclaration,
+      assert(typeDeclaration.parent is ir.LocalFunction,
           "Unexpected type declaration: $typeDeclaration");
       kind = TypeVariableKind.local;
       typeDeclaration = typeDeclaration.parent;
diff --git a/pkg/compiler/lib/src/ir/scope_visitor.dart b/pkg/compiler/lib/src/ir/scope_visitor.dart
index ede0784..ea3559b 100644
--- a/pkg/compiler/lib/src/ir/scope_visitor.dart
+++ b/pkg/compiler/lib/src/ir/scope_visitor.dart
@@ -484,9 +484,7 @@
   }
 
   void visitInvokable(ir.TreeNode node, void f()) {
-    assert(node is ir.Member ||
-        node is ir.FunctionExpression ||
-        node is ir.FunctionDeclaration);
+    assert(node is ir.Member || node is ir.LocalFunction);
     bool oldIsInsideClosure = _isInsideClosure;
     ir.TreeNode oldExecutableContext = _executableContext;
     KernelScopeInfo oldScopeInfo = _currentScopeInfo;
@@ -866,8 +864,7 @@
     if (node.arguments.types.isNotEmpty) {
       VariableUse usage;
       if (receiver is ir.VariableGet &&
-          (receiver.variable.parent is ir.FunctionDeclaration ||
-              receiver.variable.parent is ir.FunctionExpression)) {
+          (receiver.variable.parent is ir.LocalFunction)) {
         usage =
             new VariableUse.localTypeArgument(receiver.variable.parent, node);
       } else {
diff --git a/pkg/compiler/lib/src/js_backend/backend.dart b/pkg/compiler/lib/src/js_backend/backend.dart
index d187523..5e22b23 100644
--- a/pkg/compiler/lib/src/js_backend/backend.dart
+++ b/pkg/compiler/lib/src/js_backend/backend.dart
@@ -5,50 +5,20 @@
 library js_backend.backend;
 
 import '../common.dart';
-import '../common/backend_api.dart' show ImpactTransformer;
 import '../common/codegen.dart';
-import '../common/names.dart' show Uris;
-import '../common/tasks.dart' show CompilerTask;
-import '../common/work.dart';
-import '../common_elements.dart' show CommonElements, ElementEnvironment;
-import '../compiler.dart' show Compiler;
 import '../deferred_load.dart' show DeferredLoadTask;
 import '../dump_info.dart' show DumpInfoTask;
 import '../elements/entities.dart';
-import '../enqueue.dart' show Enqueuer, EnqueueTask, ResolutionEnqueuer;
+import '../enqueue.dart' show ResolutionEnqueuer;
 import '../frontend_strategy.dart';
 import '../inferrer/types.dart';
-import '../io/source_information.dart' show SourceInformationStrategy;
-import '../js/js.dart' as jsAst;
 import '../js_model/elements.dart';
-import '../js_emitter/js_emitter.dart' show CodeEmitterTask;
-import '../kernel/dart2js_target.dart';
-import '../native/enqueue.dart';
-import '../serialization/serialization.dart';
-import '../ssa/ssa.dart' show SsaFunctionCompiler;
 import '../tracer.dart';
-import '../universe/class_hierarchy.dart'
-    show ClassHierarchyBuilder, ClassQueries;
-import '../universe/codegen_world_builder.dart';
-import '../universe/world_builder.dart';
 import '../universe/world_impact.dart'
     show ImpactStrategy, ImpactUseCase, WorldImpact, WorldImpactVisitor;
-import '../world.dart' show JClosedWorld;
-import 'field_analysis.dart';
 import 'annotations.dart';
-import 'backend_impact.dart';
-import 'backend_usage.dart';
 import 'checked_mode_helpers.dart';
-import 'codegen_listener.dart';
-import 'custom_elements_analysis.dart';
-import 'enqueuer.dart';
-import 'impact_transformer.dart';
-import 'inferred_data.dart';
-import 'interceptor_data.dart';
 import 'namer.dart';
-import 'native_data.dart';
-import 'no_such_method_registry.dart';
-import 'resolution_listener.dart';
 import 'runtime_types.dart';
 
 abstract class FunctionCompiler {
@@ -292,466 +262,6 @@
   }
 }
 
-class JavaScriptBackend {
-  static const String JS = 'JS';
-  static const String JS_BUILTIN = 'JS_BUILTIN';
-  static const String JS_EMBEDDED_GLOBAL = 'JS_EMBEDDED_GLOBAL';
-  static const String JS_INTERCEPTOR_CONSTANT = 'JS_INTERCEPTOR_CONSTANT';
-  static const String JS_STRING_CONCAT = 'JS_STRING_CONCAT';
-
-  final Compiler compiler;
-
-  FrontendStrategy get frontendStrategy => compiler.frontendStrategy;
-
-  FunctionCompiler functionCompiler;
-
-  CodeEmitterTask emitterTask;
-
-  /// The generated code as a js AST for compiled methods.
-  final Map<MemberEntity, jsAst.Expression> generatedCode =
-      <MemberEntity, jsAst.Expression>{};
-
-  Namer _namer;
-
-  Namer get namerForTesting => _namer;
-
-  /// Set of classes whose `operator ==` methods handle `null` themselves.
-  final Set<ClassEntity> specialOperatorEqClasses = new Set<ClassEntity>();
-
-  List<CompilerTask> get tasks {
-    List<CompilerTask> result = functionCompiler.tasks;
-    result.add(emitterTask);
-    return result;
-  }
-
-  RuntimeTypesChecksBuilder _rtiChecksBuilder;
-
-  /// True if the html library has been loaded.
-  bool htmlLibraryIsLoaded = false;
-
-  /// Resolution support for generating table of interceptors and
-  /// constructors for custom elements.
-  CustomElementsResolutionAnalysis _customElementsResolutionAnalysis;
-
-  /// Codegen support for generating table of interceptors and
-  /// constructors for custom elements.
-  CustomElementsCodegenAnalysis _customElementsCodegenAnalysis;
-
-  KFieldAnalysis _fieldAnalysis;
-
-  /// Support for classifying `noSuchMethod` implementations.
-  NoSuchMethodRegistry noSuchMethodRegistry;
-
-  /// Backend transformation methods for the world impacts.
-  ImpactTransformer impactTransformer;
-
-  CodegenImpactTransformer _codegenImpactTransformer;
-
-  /// The strategy used for collecting and emitting source information.
-  SourceInformationStrategy sourceInformationStrategy;
-
-  NativeDataBuilderImpl _nativeDataBuilder;
-  NativeDataBuilder get nativeDataBuilder => _nativeDataBuilder;
-  BackendUsageBuilder _backendUsageBuilder;
-
-  NativeResolutionEnqueuer _nativeResolutionEnqueuer;
-  NativeCodegenEnqueuer _nativeCodegenEnqueuer;
-
-  JavaScriptBackend(this.compiler,
-      {bool generateSourceMap: true,
-      bool useMultiSourceInfo: false,
-      bool useNewSourceInfo: false})
-      : this.sourceInformationStrategy =
-            compiler.backendStrategy.sourceInformationStrategy {
-    CommonElements commonElements = compiler.frontendStrategy.commonElements;
-    _backendUsageBuilder =
-        new BackendUsageBuilderImpl(compiler.frontendStrategy);
-    emitterTask = new CodeEmitterTask(compiler, generateSourceMap);
-    noSuchMethodRegistry = new NoSuchMethodRegistryImpl(
-        commonElements, compiler.frontendStrategy.createNoSuchMethodResolver());
-    functionCompiler = new SsaFunctionCompiler(
-        compiler.options,
-        compiler.reporter,
-        compiler.backendStrategy,
-        compiler.measurer,
-        sourceInformationStrategy);
-  }
-
-  DiagnosticReporter get reporter => compiler.reporter;
-
-  ImpactCacheDeleter get impactCacheDeleter => compiler.impactCacheDeleter;
-
-  KFieldAnalysis get fieldAnalysisForTesting => _fieldAnalysis;
-
-  /// Resolution support for generating table of interceptors and
-  /// constructors for custom elements.
-  CustomElementsResolutionAnalysis get customElementsResolutionAnalysis {
-    assert(
-        _customElementsResolutionAnalysis != null,
-        failedAt(NO_LOCATION_SPANNABLE,
-            "CustomElementsResolutionAnalysis has not been created yet."));
-    return _customElementsResolutionAnalysis;
-  }
-
-  /// Codegen support for generating table of interceptors and
-  /// constructors for custom elements.
-  CustomElementsCodegenAnalysis get customElementsCodegenAnalysis {
-    assert(
-        _customElementsCodegenAnalysis != null,
-        failedAt(NO_LOCATION_SPANNABLE,
-            "CustomElementsCodegenAnalysis has not been created yet."));
-    return _customElementsCodegenAnalysis;
-  }
-
-  RuntimeTypesChecksBuilder get rtiChecksBuilder {
-    assert(
-        _rtiChecksBuilder != null,
-        failedAt(NO_LOCATION_SPANNABLE,
-            "RuntimeTypesChecksBuilder has not been created yet."));
-    assert(
-        !_rtiChecksBuilder.rtiChecksBuilderClosed,
-        failedAt(NO_LOCATION_SPANNABLE,
-            "RuntimeTypesChecks has already been computed."));
-    return _rtiChecksBuilder;
-  }
-
-  RuntimeTypesChecksBuilder get rtiChecksBuilderForTesting => _rtiChecksBuilder;
-
-  void validateInterceptorImplementsAllObjectMethods(
-      ClassEntity interceptorClass) {
-    if (interceptorClass == null) return;
-    ClassEntity objectClass = frontendStrategy.commonElements.objectClass;
-    frontendStrategy.elementEnvironment.forEachClassMember(objectClass,
-        (_, MemberEntity member) {
-      MemberEntity interceptorMember = frontendStrategy.elementEnvironment
-          .lookupLocalClassMember(interceptorClass, member.name);
-      // Interceptors must override all Object methods due to calling convention
-      // differences.
-      assert(
-          interceptorMember.enclosingClass == interceptorClass,
-          failedAt(
-              interceptorMember,
-              "Member ${member.name} not overridden in ${interceptorClass}. "
-              "Found $interceptorMember from "
-              "${interceptorMember.enclosingClass}."));
-    });
-  }
-
-  /// Called before processing of the resolution queue is started.
-  void onResolutionStart() {
-    // TODO(johnniwinther): Avoid the compiler.elementEnvironment.getThisType
-    // calls. Currently needed to ensure resolution of the classes for various
-    // queries in native behavior computation, inference and codegen.
-    frontendStrategy.elementEnvironment
-        .getThisType(frontendStrategy.commonElements.jsArrayClass);
-    frontendStrategy.elementEnvironment
-        .getThisType(frontendStrategy.commonElements.jsExtendableArrayClass);
-
-    validateInterceptorImplementsAllObjectMethods(
-        frontendStrategy.commonElements.jsInterceptorClass);
-    // The null-interceptor must also implement *all* methods.
-    validateInterceptorImplementsAllObjectMethods(
-        frontendStrategy.commonElements.jsNullClass);
-  }
-
-  /// Called when the resolution queue has been closed.
-  void onResolutionEnd() {
-    frontendStrategy.annotationProcessor.processJsInteropAnnotations(
-        frontendStrategy.nativeBasicData, nativeDataBuilder);
-  }
-
-  ResolutionEnqueuer createResolutionEnqueuer(
-      CompilerTask task, Compiler compiler) {
-    ElementEnvironment elementEnvironment =
-        compiler.frontendStrategy.elementEnvironment;
-    CommonElements commonElements = compiler.frontendStrategy.commonElements;
-    NativeBasicData nativeBasicData = compiler.frontendStrategy.nativeBasicData;
-    RuntimeTypesNeedBuilder rtiNeedBuilder =
-        compiler.frontendStrategy.createRuntimeTypesNeedBuilder();
-    BackendImpacts impacts = new BackendImpacts(commonElements);
-    _nativeResolutionEnqueuer = new NativeResolutionEnqueuer(
-        compiler.options,
-        elementEnvironment,
-        commonElements,
-        compiler.frontendStrategy.dartTypes,
-        compiler.frontendStrategy.createNativeClassFinder(nativeBasicData));
-    _nativeDataBuilder = new NativeDataBuilderImpl(nativeBasicData);
-    _customElementsResolutionAnalysis = new CustomElementsResolutionAnalysis(
-        elementEnvironment,
-        commonElements,
-        nativeBasicData,
-        _backendUsageBuilder);
-    _fieldAnalysis = new KFieldAnalysis(compiler.frontendStrategy);
-    ClassQueries classQueries = compiler.frontendStrategy.createClassQueries();
-    ClassHierarchyBuilder classHierarchyBuilder =
-        new ClassHierarchyBuilder(commonElements, classQueries);
-    impactTransformer = new JavaScriptImpactTransformer(
-        compiler.options,
-        elementEnvironment,
-        commonElements,
-        impacts,
-        nativeBasicData,
-        _nativeResolutionEnqueuer,
-        _backendUsageBuilder,
-        customElementsResolutionAnalysis,
-        rtiNeedBuilder,
-        classHierarchyBuilder);
-    InterceptorDataBuilder interceptorDataBuilder =
-        new InterceptorDataBuilderImpl(
-            nativeBasicData, elementEnvironment, commonElements);
-    AnnotationsDataBuilder annotationsDataBuilder =
-        new AnnotationsDataBuilder();
-    return new ResolutionEnqueuer(
-        task,
-        compiler.options,
-        compiler.reporter,
-        new ResolutionEnqueuerListener(
-            compiler.options,
-            elementEnvironment,
-            commonElements,
-            impacts,
-            nativeBasicData,
-            interceptorDataBuilder,
-            _backendUsageBuilder,
-            noSuchMethodRegistry,
-            customElementsResolutionAnalysis,
-            _nativeResolutionEnqueuer,
-            _fieldAnalysis,
-            compiler.deferredLoadTask),
-        compiler.frontendStrategy.createResolutionWorldBuilder(
-            nativeBasicData,
-            _nativeDataBuilder,
-            interceptorDataBuilder,
-            _backendUsageBuilder,
-            rtiNeedBuilder,
-            _fieldAnalysis,
-            _nativeResolutionEnqueuer,
-            noSuchMethodRegistry,
-            annotationsDataBuilder,
-            const StrongModeWorldStrategy(),
-            classHierarchyBuilder,
-            classQueries),
-        compiler.frontendStrategy.createResolutionWorkItemBuilder(
-            nativeBasicData,
-            _nativeDataBuilder,
-            annotationsDataBuilder,
-            impactTransformer,
-            compiler.impactCache,
-            _fieldAnalysis));
-  }
-
-  /// Creates an [Enqueuer] for code generation specific to this backend.
-  CodegenEnqueuer createCodegenEnqueuer(
-      CompilerTask task,
-      Compiler compiler,
-      JClosedWorld closedWorld,
-      GlobalTypeInferenceResults globalInferenceResults,
-      CodegenInputs codegen,
-      CodegenResults codegenResults) {
-    OneShotInterceptorData oneShotInterceptorData = new OneShotInterceptorData(
-        closedWorld.interceptorData,
-        closedWorld.commonElements,
-        closedWorld.nativeData);
-    _onCodegenEnqueuerStart(
-        globalInferenceResults, codegen, oneShotInterceptorData);
-    ElementEnvironment elementEnvironment = closedWorld.elementEnvironment;
-    CommonElements commonElements = closedWorld.commonElements;
-    BackendImpacts impacts = new BackendImpacts(commonElements);
-    _customElementsCodegenAnalysis = new CustomElementsCodegenAnalysis(
-        commonElements, elementEnvironment, closedWorld.nativeData);
-    return new CodegenEnqueuer(
-        task,
-        compiler.options,
-        compiler.backendStrategy.createCodegenWorldBuilder(
-            closedWorld.nativeData,
-            closedWorld,
-            compiler.abstractValueStrategy.createSelectorStrategy(),
-            oneShotInterceptorData),
-        compiler.backendStrategy
-            .createCodegenWorkItemBuilder(closedWorld, codegenResults),
-        new CodegenEnqueuerListener(
-            elementEnvironment,
-            commonElements,
-            impacts,
-            closedWorld.backendUsage,
-            closedWorld.rtiNeed,
-            customElementsCodegenAnalysis,
-            nativeCodegenEnqueuer));
-  }
-
-  Map<MemberEntity, WorldImpact> codegenImpactsForTesting;
-
-  WorldImpact generateCode(
-      WorkItem work,
-      JClosedWorld closedWorld,
-      CodegenResults codegenResults,
-      EntityLookup entityLookup,
-      ComponentLookup componentLookup) {
-    MemberEntity member = work.element;
-    CodegenResult result = codegenResults.getCodegenResults(member);
-    if (compiler.options.testMode) {
-      bool useDataKinds = true;
-      List<Object> data = [];
-      DataSink sink = new ObjectSink(data, useDataKinds: useDataKinds);
-      sink.registerCodegenWriter(new CodegenWriterImpl(closedWorld));
-      result.writeToDataSink(sink);
-      DataSource source = new ObjectSource(data, useDataKinds: useDataKinds);
-      List<ModularName> modularNames = [];
-      List<ModularExpression> modularExpression = [];
-      source.registerCodegenReader(
-          new CodegenReaderImpl(closedWorld, modularNames, modularExpression));
-      source.registerEntityLookup(entityLookup);
-      source.registerComponentLookup(componentLookup);
-      result = CodegenResult.readFromDataSource(
-          source, modularNames, modularExpression);
-    }
-    if (result.code != null) {
-      generatedCode[member] = result.code;
-    }
-    if (retainDataForTesting) {
-      codegenImpactsForTesting ??= <MemberEntity, WorldImpact>{};
-      codegenImpactsForTesting[member] = result.impact;
-    }
-    WorldImpact worldImpact =
-        _codegenImpactTransformer.transformCodegenImpact(result.impact);
-    compiler.dumpInfoTask.registerImpact(member, worldImpact);
-    result.applyModularState(_namer, emitterTask.emitter);
-    return worldImpact;
-  }
-
-  NativeResolutionEnqueuer get nativeResolutionEnqueuerForTesting =>
-      _nativeResolutionEnqueuer;
-
-  NativeEnqueuer get nativeCodegenEnqueuer => _nativeCodegenEnqueuer;
-
-  /// Unit test hook that returns code of an element as a String.
-  ///
-  /// Invariant: [element] must be a declaration element.
-  String getGeneratedCode(MemberEntity element) {
-    return jsAst.prettyPrint(generatedCode[element],
-        enableMinification: compiler.options.enableMinification);
-  }
-
-  /// Generates the output and returns the total size of the generated code.
-  int assembleProgram(JClosedWorld closedWorld, InferredData inferredData,
-      CodegenInputs codegenInputs, CodegenWorld codegenWorld) {
-    int programSize = emitterTask.assembleProgram(
-        _namer, closedWorld, inferredData, codegenInputs, codegenWorld);
-    closedWorld.noSuchMethodData.emitDiagnostic(reporter);
-    return programSize;
-  }
-
-  /// This method is called immediately after the [library] and its parts have
-  /// been loaded.
-  void setAnnotations(LibraryEntity library) {
-    AnnotationProcessor processor =
-        compiler.frontendStrategy.annotationProcessor;
-    if (maybeEnableNative(library.canonicalUri)) {
-      processor.extractNativeAnnotations(library);
-    }
-    processor.extractJsInteropAnnotations(library);
-    Uri uri = library.canonicalUri;
-    if (uri == Uris.dart_html) {
-      _backendUsageBuilder.registerHtmlIsLoaded();
-    }
-  }
-
-  /// Called when the compiler starts running the codegen.
-  ///
-  /// Returns the [CodegenInputs] objects with the needed data.
-  CodegenInputs onCodegenStart(
-      GlobalTypeInferenceResults globalTypeInferenceResults) {
-    JClosedWorld closedWorld = globalTypeInferenceResults.closedWorld;
-    RuntimeTypeTags rtiTags = const RuntimeTypeTags();
-    FixedNames fixedNames = compiler.options.enableMinification
-        ? const MinifiedFixedNames()
-        : const FixedNames();
-
-    Tracer tracer = new Tracer(closedWorld, compiler.outputProvider);
-    RuntimeTypesEncoder rtiEncoder = new RuntimeTypesEncoderImpl(
-        rtiTags,
-        closedWorld.nativeData,
-        closedWorld.elementEnvironment,
-        closedWorld.commonElements,
-        closedWorld.rtiNeed);
-    RuntimeTypesSubstitutions rtiSubstitutions;
-    if (compiler.options.disableRtiOptimization) {
-      rtiSubstitutions = new TrivialRuntimeTypesSubstitutions(closedWorld);
-      _rtiChecksBuilder =
-          new TrivialRuntimeTypesChecksBuilder(closedWorld, rtiSubstitutions);
-    } else {
-      RuntimeTypesImpl runtimeTypesImpl = new RuntimeTypesImpl(closedWorld);
-      _rtiChecksBuilder = runtimeTypesImpl;
-      rtiSubstitutions = runtimeTypesImpl;
-    }
-
-    CodegenInputs codegen = new CodegenInputsImpl(
-        rtiSubstitutions, rtiEncoder, tracer, rtiTags, fixedNames);
-
-    functionCompiler.initialize(globalTypeInferenceResults, codegen);
-    return codegen;
-  }
-
-  /// Called before the compiler starts running the codegen enqueuer.
-  void _onCodegenEnqueuerStart(
-      GlobalTypeInferenceResults globalTypeInferenceResults,
-      CodegenInputs codegen,
-      OneShotInterceptorData oneShotInterceptorData) {
-    JClosedWorld closedWorld = globalTypeInferenceResults.closedWorld;
-    RuntimeTypeTags rtiTags = codegen.rtiTags;
-    FixedNames fixedNames = codegen.fixedNames;
-    _namer = compiler.options.enableMinification
-        ? compiler.options.useFrequencyNamer
-            ? new FrequencyBasedNamer(closedWorld, rtiTags, fixedNames)
-            : new MinifyNamer(closedWorld, rtiTags, fixedNames)
-        : new Namer(closedWorld, rtiTags, fixedNames);
-    _nativeCodegenEnqueuer = new NativeCodegenEnqueuer(
-        compiler.options,
-        closedWorld.elementEnvironment,
-        closedWorld.commonElements,
-        closedWorld.dartTypes,
-        emitterTask,
-        closedWorld.liveNativeClasses,
-        closedWorld.nativeData);
-    emitterTask.createEmitter(_namer, codegen, closedWorld);
-    // TODO(johnniwinther): Share the impact object created in
-    // createCodegenEnqueuer.
-    BackendImpacts impacts = new BackendImpacts(closedWorld.commonElements);
-
-    _codegenImpactTransformer = new CodegenImpactTransformer(
-        compiler.options,
-        closedWorld,
-        closedWorld.elementEnvironment,
-        closedWorld.commonElements,
-        impacts,
-        closedWorld.nativeData,
-        closedWorld.backendUsage,
-        closedWorld.rtiNeed,
-        nativeCodegenEnqueuer,
-        _namer,
-        oneShotInterceptorData,
-        rtiChecksBuilder,
-        emitterTask.nativeEmitter);
-  }
-
-  /// Called when code generation has been completed.
-  void onCodegenEnd(CodegenInputs codegen) {
-    sourceInformationStrategy.onComplete();
-    codegen.tracer.close();
-  }
-
-  /// Creates an impact strategy to use for compilation.
-  ImpactStrategy createImpactStrategy(
-      {bool supportDeferredLoad: true, bool supportDumpInfo: true}) {
-    return new JavaScriptImpactStrategy(
-        impactCacheDeleter, compiler.dumpInfoTask,
-        supportDeferredLoad: supportDeferredLoad,
-        supportDumpInfo: supportDumpInfo);
-  }
-
-  EnqueueTask makeEnqueuer() => new EnqueueTask(compiler);
-}
-
 class JavaScriptImpactStrategy extends ImpactStrategy {
   final ImpactCacheDeleter impactCacheDeleter;
   final DumpInfoTask dumpInfoTask;
diff --git a/pkg/compiler/lib/src/js_backend/backend_impact.dart b/pkg/compiler/lib/src/js_backend/backend_impact.dart
index 256d8f1..9f683b7 100644
--- a/pkg/compiler/lib/src/js_backend/backend_impact.dart
+++ b/pkg/compiler/lib/src/js_backend/backend_impact.dart
@@ -90,8 +90,9 @@
 /// The JavaScript backend dependencies for various features.
 class BackendImpacts {
   final CommonElements _commonElements;
+  final bool _newRti;
 
-  BackendImpacts(this._commonElements);
+  BackendImpacts(this._commonElements, this._newRti);
 
   BackendImpact _getRuntimeTypeArgument;
 
@@ -110,7 +111,7 @@
       _commonElements.setRuntimeTypeInfo,
       _commonElements.getRuntimeTypeInfo,
       _commonElements.computeSignature,
-      _commonElements.getRuntimeTypeArguments
+      _commonElements.getRuntimeTypeArguments,
     ], otherImpacts: [
       listValues
     ]);
@@ -186,8 +187,9 @@
   BackendImpact _asCheck;
 
   BackendImpact get asCheck {
-    return _asCheck ??=
-        new BackendImpact(staticUses: [_commonElements.throwRuntimeError]);
+    return _asCheck ??= new BackendImpact(staticUses: [
+      _commonElements.throwRuntimeError,
+    ], otherImpacts: _newRti ? [usesNewRti] : []);
   }
 
   BackendImpact _throwNoSuchMethod;
@@ -759,4 +761,16 @@
       ], instantiatedClasses: [
         _commonElements.getInstantiationClass(typeArgumentCount),
       ]);
+
+  BackendImpact _usesNewRti;
+
+  /// Backend impact for --experiment-new-rti.
+  BackendImpact get usesNewRti {
+    // TODO(sra): Can this be broken down into more selective impacts?
+    return _usesNewRti ??= BackendImpact(staticUses: [
+      _commonElements.findType,
+      _commonElements.rtiEvalMethod,
+      _commonElements.rtiBindMethod,
+    ]);
+  }
 }
diff --git a/pkg/compiler/lib/src/js_backend/field_analysis.dart b/pkg/compiler/lib/src/js_backend/field_analysis.dart
index 7349b4d..77aa43e 100644
--- a/pkg/compiler/lib/src/js_backend/field_analysis.dart
+++ b/pkg/compiler/lib/src/js_backend/field_analysis.dart
@@ -41,7 +41,7 @@
   final Map<KClass, ClassData> _classData = {};
   final Map<KField, StaticFieldData> _staticFieldData = {};
 
-  KFieldAnalysis(KernelFrontEndStrategy kernelStrategy)
+  KFieldAnalysis(KernelFrontendStrategy kernelStrategy)
       : _elementMap = kernelStrategy.elementMap;
 
   // Register class during resolution. Use simple syntactic analysis to find
diff --git a/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart b/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart
index 3871204..c9b0ec1 100644
--- a/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart
+++ b/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart
@@ -11,9 +11,10 @@
 import '../deferred_load.dart' show OutputUnit;
 import '../elements/entities.dart';
 import '../js/js.dart' as jsAst;
-import '../js_backend/backend.dart' show CodegenInputs, JavaScriptBackend;
+import '../js_backend/backend.dart' show CodegenInputs;
 import '../js_backend/inferred_data.dart';
 import '../js_backend/namer.dart' show Namer;
+import '../js_model/js_strategy.dart';
 import '../universe/codegen_world_builder.dart';
 import '../world.dart' show JClosedWorld;
 import 'program_builder/program_builder.dart';
@@ -36,7 +37,7 @@
   final Compiler _compiler;
   final bool _generateSourceMap;
 
-  JavaScriptBackend get _backend => _compiler.backend;
+  JsBackendStrategy get _backendStrategy => _compiler.backendStrategy;
 
   @deprecated
   // This field should be removed. It's currently only needed for dump-info and
@@ -69,18 +70,18 @@
     // Compute the required type checks to know which classes need a
     // 'is$' method.
     typeTestRegistry.computeRequiredTypeChecks(
-        _backend.rtiChecksBuilder, codegenWorld);
+        _backendStrategy.rtiChecksBuilder, codegenWorld);
     // Compute the classes needed by RTI.
     typeTestRegistry.computeRtiNeededClasses(
-        codegen.rtiSubstitutions, _backend.generatedCode.keys);
+        codegen.rtiSubstitutions, _backendStrategy.generatedCode.keys);
   }
 
   /// Creates the [Emitter] for this task.
   void createEmitter(
       Namer namer, CodegenInputs codegen, JClosedWorld closedWorld) {
     measure(() {
-      _nativeEmitter =
-          new NativeEmitter(this, closedWorld, _backend.nativeCodegenEnqueuer);
+      _nativeEmitter = new NativeEmitter(
+          this, closedWorld, _backendStrategy.nativeCodegenEnqueuer);
       _emitter = new startup_js_emitter.EmitterImpl(
           _compiler.options,
           _compiler.reporter,
@@ -89,7 +90,7 @@
           namer,
           closedWorld,
           codegen.rtiEncoder,
-          _backend.sourceInformationStrategy,
+          _backendStrategy.sourceInformationStrategy,
           this,
           _generateSourceMap);
       metadataCollector = new MetadataCollector(
@@ -120,7 +121,7 @@
           closedWorld.commonElements,
           closedWorld.outputUnitData,
           codegenWorld,
-          _backend.nativeCodegenEnqueuer,
+          _backendStrategy.nativeCodegenEnqueuer,
           closedWorld.backendUsage,
           closedWorld.nativeData,
           closedWorld.rtiNeed,
@@ -128,14 +129,14 @@
           typeTestRegistry.rtiChecks,
           codegenInputs.rtiEncoder,
           codegenWorld.oneShotInterceptorData,
-          _backend.customElementsCodegenAnalysis,
-          _backend.generatedCode,
+          _backendStrategy.customElementsCodegenAnalysis,
+          _backendStrategy.generatedCode,
           namer,
           this,
           closedWorld,
           closedWorld.fieldAnalysis,
           inferredData,
-          _backend.sourceInformationStrategy,
+          _backendStrategy.sourceInformationStrategy,
           closedWorld.sorter,
           typeTestRegistry.rtiNeededClasses,
           closedWorld.elementEnvironment.mainFunction);
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
index 0e58794..c2b4798 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
@@ -432,12 +432,15 @@
 // Builds the inheritance structure.
 #inheritance;
 
+// Emits the embedded globals. This needs to be before constants so the embedded
+// global type resources are available for generating constants.
+#embeddedGlobalsPart1;
+
 // Instantiates all constants.
 #constants;
 
-// Emits the embedded globals. Due to type checks in eager initializers this is
-// needed before static non-final fields initializers.
-#embeddedGlobals;
+// Adds to the embedded globals. A few globals refer to constants.
+#embeddedGlobalsPart2; 
 
 // Initializes the static non-final fields (with their constant values).
 #staticNonFinalFields;
@@ -660,7 +663,10 @@
       'constants': emitConstants(fragment),
       'staticNonFinalFields': emitStaticNonFinalFields(fragment),
       'lazyStatics': emitLazilyInitializedStatics(fragment),
-      'embeddedGlobals': emitEmbeddedGlobals(program, deferredLoadingState),
+      'embeddedGlobalsPart1':
+          emitEmbeddedGlobalsPart1(program, deferredLoadingState),
+      'embeddedGlobalsPart2':
+          emitEmbeddedGlobalsPart2(program, deferredLoadingState),
       'nativeSupport': program.needsNativeSupport
           ? emitNativeSupport(fragment)
           : new js.EmptyStatement(),
@@ -1848,7 +1854,7 @@
   }
 
   /// Emits all embedded globals.
-  js.Statement emitEmbeddedGlobals(
+  js.Statement emitEmbeddedGlobalsPart1(
       Program program, DeferredLoadingState deferredLoadingState) {
     List<js.Property> globals = [];
 
@@ -1858,8 +1864,14 @@
     }
 
     if (program.typeToInterceptorMap != null) {
+      // This property is assigned later.
+      // Initialize property to avoid map transitions.
       globals.add(new js.Property(
-          js.string(TYPE_TO_INTERCEPTOR_MAP), program.typeToInterceptorMap));
+          js.string(TYPE_TO_INTERCEPTOR_MAP), js.LiteralNull()));
+    }
+
+    if (_options.experimentNewRti) {
+      globals.add(js.Property(js.string(RTI_UNIVERSE), createRtiUniverse()));
     }
 
     globals.add(emitMangledGlobalNames());
@@ -1887,6 +1899,33 @@
     return js.js.statement('var init = #;', globalsObject);
   }
 
+  /// Finish setting up embedded globals.
+  js.Statement emitEmbeddedGlobalsPart2(
+      Program program, DeferredLoadingState deferredLoadingState) {
+    List<js.Statement> statements = [];
+    if (program.typeToInterceptorMap != null) {
+      statements.add(js.js.statement('init.# = #;',
+          [js.string(TYPE_TO_INTERCEPTOR_MAP), program.typeToInterceptorMap]));
+    }
+    return js.Block(statements);
+  }
+
+  /// Returns an expression that creates the initial Rti Universe.
+  ///
+  /// This needs to be kept in sync with `_Universe.create` in `dart:_rti`.
+  js.Expression createRtiUniverse() {
+    List<js.Property> universeFields = [];
+    void initField(String name, String value) {
+      universeFields.add(js.Property(js.string(name), js.js(value)));
+    }
+
+    initField(RtiUniverseFieldNames.evalCache, 'new Map()');
+    initField(RtiUniverseFieldNames.typeRules, '{}');
+    initField(RtiUniverseFieldNames.sharedEmptyArray, '[]');
+
+    return js.ObjectInitializer(universeFields);
+  }
+
   /// Emits data needed for native classes.
   ///
   /// We don't try to reduce the size of the native data, but rather build
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
index 78b6164..fd8db9b 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
@@ -24,6 +24,8 @@
         MANGLED_NAMES,
         METADATA,
         NATIVE_SUPERCLASS_TAG_NAME,
+        RTI_UNIVERSE,
+        RtiUniverseFieldNames,
         TYPE_TO_INTERCEPTOR_MAP,
         TYPES;
 
diff --git a/pkg/compiler/lib/src/js_model/closure.dart b/pkg/compiler/lib/src/js_model/closure.dart
index 9a39986..2ee5d5c 100644
--- a/pkg/compiler/lib/src/js_model/closure.dart
+++ b/pkg/compiler/lib/src/js_model/closure.dart
@@ -38,8 +38,7 @@
   // Signature function.
   final Map<MemberEntity, CapturedScope> _capturedScopeForSignatureMap;
 
-  // The key is either a [ir.FunctionDeclaration] or [ir.FunctionExpression].
-  final Map<ir.TreeNode, ClosureRepresentationInfo>
+  final Map<ir.LocalFunction, ClosureRepresentationInfo>
       _localClosureRepresentationMap;
 
   ClosureDataImpl(this._elementMap, this._scopeMap, this._capturedScopesMap,
@@ -56,8 +55,8 @@
         .readTreeNodeMap(() => new CapturedScope.readFromDataSource(source));
     Map<MemberEntity, CapturedScope> capturedScopeForSignatureMap = source
         .readMemberMap(() => new CapturedScope.readFromDataSource(source));
-    Map<ir.TreeNode, ClosureRepresentationInfo> localClosureRepresentationMap =
-        source.readTreeNodeMap(
+    Map<ir.LocalFunction, ClosureRepresentationInfo>
+        localClosureRepresentationMap = source.readTreeNodeMap(
             () => new ClosureRepresentationInfo.readFromDataSource(source));
     source.end(tag);
     return new ClosureDataImpl(elementMap, scopeMap, capturedScopesMap,
@@ -123,8 +122,7 @@
       _capturedScopesMap[loopNode] ?? const CapturedLoopScope();
 
   @override
-  ClosureRepresentationInfo getClosureInfo(ir.Node node) {
-    assert(node is ir.FunctionExpression || node is ir.FunctionDeclaration);
+  ClosureRepresentationInfo getClosureInfo(ir.LocalFunction node) {
     var closure = _localClosureRepresentationMap[node];
     assert(
         closure != null,
@@ -159,9 +157,8 @@
   // Signature function.
   Map<MemberEntity, CapturedScope> _capturedScopeForSignatureMap = {};
 
-  // The key is either a [ir.FunctionDeclaration] or [ir.FunctionExpression].
-  Map<ir.TreeNode, ClosureRepresentationInfo> _localClosureRepresentationMap =
-      {};
+  Map<ir.LocalFunction, ClosureRepresentationInfo>
+      _localClosureRepresentationMap = {};
 
   ClosureDataBuilder(this._elementMap, this._globalLocalsMap, this._options);
 
@@ -323,17 +320,10 @@
         allBoxedVariables.addAll(boxedVariables);
       });
 
-      Map<ir.TreeNode, KernelScopeInfo> closuresToGenerate =
+      Map<ir.LocalFunction, KernelScopeInfo> closuresToGenerate =
           model.closuresToGenerate;
-      for (ir.TreeNode node in closuresToGenerate.keys) {
-        ir.FunctionNode functionNode;
-        if (node is ir.FunctionDeclaration) {
-          functionNode = node.function;
-        } else if (node is ir.FunctionExpression) {
-          functionNode = node.function;
-        } else {
-          failedAt(member, "Unexpected closure node ${node}");
-        }
+      for (ir.LocalFunction node in closuresToGenerate.keys) {
+        ir.FunctionNode functionNode = node.function;
         KernelClosureClassInfo closureClassInfo = _produceSyntheticElements(
             closedWorldBuilder,
             member,
@@ -402,8 +392,7 @@
     if (node.parent is ir.Member) {
       assert(_elementMap.getMember(node.parent) == member);
     } else {
-      assert(node.parent is ir.FunctionExpression ||
-          node.parent is ir.FunctionDeclaration);
+      assert(node.parent is ir.LocalFunction);
       _localClosureRepresentationMap[node.parent] = closureClassInfo;
     }
     return closureClassInfo;
@@ -1281,9 +1270,9 @@
 
   bool methodNeedsSignature(MemberEntity method);
 
-  bool localFunctionNeedsTypeArguments(ir.Node node);
+  bool localFunctionNeedsTypeArguments(ir.LocalFunction node);
 
-  bool localFunctionNeedsSignature(ir.Node node);
+  bool localFunctionNeedsSignature(ir.LocalFunction node);
 
   bool selectorNeedsTypeArguments(Selector selector);
 
diff --git a/pkg/compiler/lib/src/js_model/element_map.dart b/pkg/compiler/lib/src/js_model/element_map.dart
index 2b2d412..3fb71a4 100644
--- a/pkg/compiler/lib/src/js_model/element_map.dart
+++ b/pkg/compiler/lib/src/js_model/element_map.dart
@@ -273,28 +273,15 @@
   MemberDefinition definition = elementMap.getMemberDefinition(member);
   switch (definition.kind) {
     case MemberKind.regular:
-      ir.Node node = definition.node;
-      if (node is ir.Procedure) {
-        return node.function;
-      }
-      break;
+      ir.Member node = definition.node;
+      return node.function;
     case MemberKind.constructor:
     case MemberKind.constructorBody:
-      ir.Node node = definition.node;
-      if (node is ir.Procedure) {
-        return node.function;
-      } else if (node is ir.Constructor) {
-        return node.function;
-      }
-      break;
+      ir.Member node = definition.node;
+      return node.function;
     case MemberKind.closureCall:
-      ir.Node node = definition.node;
-      if (node is ir.FunctionDeclaration) {
-        return node.function;
-      } else if (node is ir.FunctionExpression) {
-        return node.function;
-      }
-      break;
+      ir.LocalFunction node = definition.node;
+      return node.function;
     default:
   }
   return null;
@@ -303,23 +290,29 @@
 // TODO(johnniwinther,efortuna): Add more when needed.
 // TODO(johnniwinther): Should we split regular into method, field, etc.?
 enum MemberKind {
-  // A regular member defined by an [ir.Node].
+  /// A regular member defined by an [ir.Node].
   regular,
-  // A constructor whose initializer is defined by an [ir.Constructor] node.
+
+  /// A constructor whose initializer is defined by an [ir.Constructor] node.
   constructor,
-  // A constructor whose body is defined by an [ir.Constructor] node.
+
+  /// A constructor whose body is defined by an [ir.Constructor] node.
   constructorBody,
-  // A closure class `call` method whose body is defined by an
-  // [ir.FunctionExpression] or [ir.FunctionDeclaration].
+
+  /// A closure class `call` method whose body is defined by an
+  /// [ir.LocalFunction].
   closureCall,
-  // A field corresponding to a captured variable in the closure. It does not
-  // have a corresponding ir.Node.
+
+  /// A field corresponding to a captured variable in the closure. It does not
+  /// have a corresponding ir.Node.
   closureField,
-  // A method that describes the type of a function (in this case the type of
-  // the closure class. It does not have a corresponding ir.Node or a method
-  // body.
+
+  /// A method that describes the type of a function (in this case the type of
+  /// the closure class. It does not have a corresponding ir.Node or a method
+  /// body.
   signature,
-  // A separated body of a generator (sync*/async/async*) function.
+
+  /// A separated body of a generator (sync*/async/async*) function.
   generatorBody,
 }
 
@@ -588,17 +581,10 @@
       }
       break;
     case MemberKind.closureCall:
-      ir.Node node = definition.node;
-      if (node is ir.FunctionDeclaration) {
-        forEachOrderedParameterByFunctionNode(
-            node.function, parameterStructure, handleParameter);
-        return;
-      } else if (node is ir.FunctionExpression) {
-        forEachOrderedParameterByFunctionNode(
-            node.function, parameterStructure, handleParameter);
-        return;
-      }
-      break;
+      ir.LocalFunction node = definition.node;
+      forEachOrderedParameterByFunctionNode(
+          node.function, parameterStructure, handleParameter);
+      return;
     default:
   }
   failedAt(function, "Unexpected function definition $definition.");
diff --git a/pkg/compiler/lib/src/js_model/js_strategy.dart b/pkg/compiler/lib/src/js_model/js_strategy.dart
index 2a64023..2c0c686 100644
--- a/pkg/compiler/lib/src/js_model/js_strategy.dart
+++ b/pkg/compiler/lib/src/js_model/js_strategy.dart
@@ -8,9 +8,10 @@
 
 import '../backend_strategy.dart';
 import '../common.dart';
-import '../common/codegen.dart' show CodegenRegistry, CodegenResults;
+import '../common/codegen.dart';
 import '../common/tasks.dart';
 import '../common/work.dart';
+import '../common_elements.dart' show CommonElements, ElementEnvironment;
 import '../compiler.dart';
 import '../deferred_load.dart' hide WorkItem;
 import '../dump_info.dart';
@@ -24,22 +25,30 @@
 import '../inferrer/types.dart';
 import '../js/js_source_mapping.dart';
 import '../js_backend/backend.dart';
+import '../js_backend/backend_impact.dart';
+import '../js_backend/codegen_listener.dart';
+import '../js_backend/custom_elements_analysis.dart';
+import '../js_backend/enqueuer.dart';
+import '../js_backend/impact_transformer.dart';
 import '../js_backend/inferred_data.dart';
 import '../js_backend/interceptor_data.dart';
-import '../js_backend/native_data.dart';
-import '../js_backend/namer.dart' show ModularNamer;
+import '../js_backend/namer.dart';
+import '../js_backend/runtime_types.dart';
 import '../js_emitter/code_emitter_task.dart' show ModularEmitter;
+import '../js_emitter/js_emitter.dart' show CodeEmitterTask;
+import '../js/js.dart' as js;
 import '../kernel/kernel_strategy.dart';
 import '../native/behavior.dart';
+import '../native/enqueue.dart';
 import '../options.dart';
 import '../serialization/serialization.dart';
 import '../ssa/builder_kernel.dart';
 import '../ssa/nodes.dart';
 import '../ssa/ssa.dart';
 import '../ssa/types.dart';
+import '../tracer.dart';
 import '../universe/codegen_world_builder.dart';
 import '../universe/selector.dart';
-import '../universe/world_builder.dart';
 import '../universe/world_impact.dart';
 import '../world.dart';
 import 'closure.dart';
@@ -53,7 +62,69 @@
   final Compiler _compiler;
   JsKernelToElementMap _elementMap;
 
-  JsBackendStrategy(this._compiler);
+  /// Codegen support for generating table of interceptors and
+  /// constructors for custom elements.
+  CustomElementsCodegenAnalysis _customElementsCodegenAnalysis;
+
+  NativeCodegenEnqueuer _nativeCodegenEnqueuer;
+
+  Namer _namer;
+
+  CodegenImpactTransformer _codegenImpactTransformer;
+
+  CodeEmitterTask _emitterTask;
+
+  RuntimeTypesChecksBuilder _rtiChecksBuilder;
+
+  FunctionCompiler _functionCompiler;
+
+  SourceInformationStrategy sourceInformationStrategy;
+
+  /// The generated code as a js AST for compiled methods.
+  final Map<MemberEntity, js.Expression> generatedCode =
+      <MemberEntity, js.Expression>{};
+
+  JsBackendStrategy(this._compiler) {
+    bool generateSourceMap = _compiler.options.generateSourceMap;
+    if (!generateSourceMap) {
+      sourceInformationStrategy = const JavaScriptSourceInformationStrategy();
+    } else {
+      sourceInformationStrategy = new KernelSourceInformationStrategy(this);
+    }
+    _emitterTask = new CodeEmitterTask(_compiler, generateSourceMap);
+    _functionCompiler = new SsaFunctionCompiler(
+        _compiler.options,
+        _compiler.reporter,
+        this,
+        _compiler.measurer,
+        sourceInformationStrategy);
+  }
+
+  @override
+  List<CompilerTask> get tasks {
+    List<CompilerTask> result = functionCompiler.tasks;
+    result.add(emitterTask);
+    return result;
+  }
+
+  @override
+  FunctionCompiler get functionCompiler => _functionCompiler;
+
+  @override
+  CodeEmitterTask get emitterTask => _emitterTask;
+
+  Namer get namerForTesting => _namer;
+
+  NativeEnqueuer get nativeCodegenEnqueuer => _nativeCodegenEnqueuer;
+
+  RuntimeTypesChecksBuilder get rtiChecksBuilderForTesting => _rtiChecksBuilder;
+
+  Map<MemberEntity, WorldImpact> codegenImpactsForTesting;
+
+  String getGeneratedCodeForTesting(MemberEntity element) {
+    return js.prettyPrint(generatedCode[element],
+        enableMinification: _compiler.options.enableMinification);
+  }
 
   @deprecated
   JsToElementMap get elementMap {
@@ -62,10 +133,32 @@
     return _elementMap;
   }
 
+  /// Codegen support for generating table of interceptors and
+  /// constructors for custom elements.
+  CustomElementsCodegenAnalysis get customElementsCodegenAnalysis {
+    assert(
+        _customElementsCodegenAnalysis != null,
+        failedAt(NO_LOCATION_SPANNABLE,
+            "CustomElementsCodegenAnalysis has not been created yet."));
+    return _customElementsCodegenAnalysis;
+  }
+
+  RuntimeTypesChecksBuilder get rtiChecksBuilder {
+    assert(
+        _rtiChecksBuilder != null,
+        failedAt(NO_LOCATION_SPANNABLE,
+            "RuntimeTypesChecksBuilder has not been created yet."));
+    assert(
+        !_rtiChecksBuilder.rtiChecksBuilderClosed,
+        failedAt(NO_LOCATION_SPANNABLE,
+            "RuntimeTypesChecks has already been computed."));
+    return _rtiChecksBuilder;
+  }
+
   @override
   JClosedWorld createJClosedWorld(
       KClosedWorld closedWorld, OutputUnitData outputUnitData) {
-    KernelFrontEndStrategy strategy = _compiler.frontendStrategy;
+    KernelFrontendStrategy strategy = _compiler.frontendStrategy;
     _elementMap = new JsKernelToElementMap(
         _compiler.reporter,
         _compiler.environment,
@@ -94,11 +187,180 @@
   }
 
   @override
-  SourceInformationStrategy get sourceInformationStrategy {
-    if (!_compiler.options.generateSourceMap) {
-      return const JavaScriptSourceInformationStrategy();
+  CodegenInputs onCodegenStart(
+      GlobalTypeInferenceResults globalTypeInferenceResults) {
+    JClosedWorld closedWorld = globalTypeInferenceResults.closedWorld;
+    RuntimeTypeTags rtiTags = const RuntimeTypeTags();
+    FixedNames fixedNames = _compiler.options.enableMinification
+        ? const MinifiedFixedNames()
+        : const FixedNames();
+
+    Tracer tracer = new Tracer(closedWorld, _compiler.outputProvider);
+    RuntimeTypesEncoder rtiEncoder = new RuntimeTypesEncoderImpl(
+        rtiTags,
+        closedWorld.nativeData,
+        closedWorld.elementEnvironment,
+        closedWorld.commonElements,
+        closedWorld.rtiNeed);
+    RuntimeTypesSubstitutions rtiSubstitutions;
+    if (_compiler.options.disableRtiOptimization) {
+      rtiSubstitutions = new TrivialRuntimeTypesSubstitutions(closedWorld);
+      _rtiChecksBuilder =
+          new TrivialRuntimeTypesChecksBuilder(closedWorld, rtiSubstitutions);
+    } else {
+      RuntimeTypesImpl runtimeTypesImpl = new RuntimeTypesImpl(closedWorld);
+      _rtiChecksBuilder = runtimeTypesImpl;
+      rtiSubstitutions = runtimeTypesImpl;
     }
-    return new KernelSourceInformationStrategy(this);
+
+    CodegenInputs codegen = new CodegenInputsImpl(
+        rtiSubstitutions, rtiEncoder, tracer, rtiTags, fixedNames);
+
+    functionCompiler.initialize(globalTypeInferenceResults, codegen);
+    return codegen;
+  }
+
+  @override
+  CodegenEnqueuer createCodegenEnqueuer(
+      CompilerTask task,
+      JClosedWorld closedWorld,
+      GlobalTypeInferenceResults globalInferenceResults,
+      CodegenInputs codegen,
+      CodegenResults codegenResults) {
+    assert(_elementMap != null,
+        "JsBackendStrategy.elementMap has not been created yet.");
+    OneShotInterceptorData oneShotInterceptorData = new OneShotInterceptorData(
+        closedWorld.interceptorData,
+        closedWorld.commonElements,
+        closedWorld.nativeData);
+    _onCodegenEnqueuerStart(
+        globalInferenceResults, codegen, oneShotInterceptorData);
+    ElementEnvironment elementEnvironment = closedWorld.elementEnvironment;
+    CommonElements commonElements = closedWorld.commonElements;
+    BackendImpacts impacts =
+        new BackendImpacts(commonElements, _compiler.options.experimentNewRti);
+    _customElementsCodegenAnalysis = new CustomElementsCodegenAnalysis(
+        commonElements, elementEnvironment, closedWorld.nativeData);
+    return new CodegenEnqueuer(
+        task,
+        _compiler.options,
+        new CodegenWorldBuilderImpl(
+            closedWorld,
+            _compiler.abstractValueStrategy.createSelectorStrategy(),
+            oneShotInterceptorData),
+        new KernelCodegenWorkItemBuilder(
+            this,
+            closedWorld,
+            codegenResults,
+            new ClosedEntityLookup(_elementMap),
+            // TODO(johnniwinther): Avoid the need for a [ComponentLookup]. This
+            // is caused by some type masks holding a kernel node for using in
+            // tracing.
+            new ComponentLookup(_elementMap.programEnv.mainComponent)),
+        new CodegenEnqueuerListener(
+            elementEnvironment,
+            commonElements,
+            impacts,
+            closedWorld.backendUsage,
+            closedWorld.rtiNeed,
+            customElementsCodegenAnalysis,
+            nativeCodegenEnqueuer));
+  }
+
+  /// Called before the compiler starts running the codegen enqueuer.
+  void _onCodegenEnqueuerStart(
+      GlobalTypeInferenceResults globalTypeInferenceResults,
+      CodegenInputs codegen,
+      OneShotInterceptorData oneShotInterceptorData) {
+    JClosedWorld closedWorld = globalTypeInferenceResults.closedWorld;
+    RuntimeTypeTags rtiTags = codegen.rtiTags;
+    FixedNames fixedNames = codegen.fixedNames;
+    _namer = _compiler.options.enableMinification
+        ? _compiler.options.useFrequencyNamer
+            ? new FrequencyBasedNamer(closedWorld, rtiTags, fixedNames)
+            : new MinifyNamer(closedWorld, rtiTags, fixedNames)
+        : new Namer(closedWorld, rtiTags, fixedNames);
+    _nativeCodegenEnqueuer = new NativeCodegenEnqueuer(
+        _compiler.options,
+        closedWorld.elementEnvironment,
+        closedWorld.commonElements,
+        closedWorld.dartTypes,
+        emitterTask,
+        closedWorld.liveNativeClasses,
+        closedWorld.nativeData);
+    emitterTask.createEmitter(_namer, codegen, closedWorld);
+    // TODO(johnniwinther): Share the impact object created in
+    // createCodegenEnqueuer.
+    BackendImpacts impacts = new BackendImpacts(
+        closedWorld.commonElements, _compiler.options.experimentNewRti);
+
+    _codegenImpactTransformer = new CodegenImpactTransformer(
+        _compiler.options,
+        closedWorld,
+        closedWorld.elementEnvironment,
+        closedWorld.commonElements,
+        impacts,
+        closedWorld.nativeData,
+        closedWorld.backendUsage,
+        closedWorld.rtiNeed,
+        nativeCodegenEnqueuer,
+        _namer,
+        oneShotInterceptorData,
+        rtiChecksBuilder,
+        emitterTask.nativeEmitter);
+  }
+
+  WorldImpact generateCode(
+      WorkItem work,
+      JClosedWorld closedWorld,
+      CodegenResults codegenResults,
+      EntityLookup entityLookup,
+      ComponentLookup componentLookup) {
+    MemberEntity member = work.element;
+    CodegenResult result = codegenResults.getCodegenResults(member);
+    if (_compiler.options.testMode) {
+      bool useDataKinds = true;
+      List<Object> data = [];
+      DataSink sink = new ObjectSink(data, useDataKinds: useDataKinds);
+      sink.registerCodegenWriter(new CodegenWriterImpl(closedWorld));
+      result.writeToDataSink(sink);
+      DataSource source = new ObjectSource(data, useDataKinds: useDataKinds);
+      List<ModularName> modularNames = [];
+      List<ModularExpression> modularExpression = [];
+      source.registerCodegenReader(
+          new CodegenReaderImpl(closedWorld, modularNames, modularExpression));
+      source.registerEntityLookup(entityLookup);
+      source.registerComponentLookup(componentLookup);
+      result = CodegenResult.readFromDataSource(
+          source, modularNames, modularExpression);
+    }
+    if (result.code != null) {
+      generatedCode[member] = result.code;
+    }
+    if (retainDataForTesting) {
+      codegenImpactsForTesting ??= <MemberEntity, WorldImpact>{};
+      codegenImpactsForTesting[member] = result.impact;
+    }
+    WorldImpact worldImpact =
+        _codegenImpactTransformer.transformCodegenImpact(result.impact);
+    _compiler.dumpInfoTask.registerImpact(member, worldImpact);
+    result.applyModularState(_namer, emitterTask.emitter);
+    return worldImpact;
+  }
+
+  @override
+  void onCodegenEnd(CodegenInputs codegen) {
+    sourceInformationStrategy.onComplete();
+    codegen.tracer.close();
+  }
+
+  @override
+  int assembleProgram(JClosedWorld closedWorld, InferredData inferredData,
+      CodegenInputs codegenInputs, CodegenWorld codegenWorld) {
+    int programSize = emitterTask.assembleProgram(
+        _namer, closedWorld, inferredData, codegenInputs, codegenWorld);
+    closedWorld.noSuchMethodData.emitDiagnostic(_compiler.reporter);
+    return programSize;
   }
 
   @override
@@ -115,32 +377,6 @@
   }
 
   @override
-  WorkItemBuilder createCodegenWorkItemBuilder(
-      JClosedWorld closedWorld, CodegenResults codegenResults) {
-    assert(_elementMap != null,
-        "JsBackendStrategy.elementMap has not been created yet.");
-    return new KernelCodegenWorkItemBuilder(
-        _compiler.backend,
-        closedWorld,
-        codegenResults,
-        new ClosedEntityLookup(_elementMap),
-        // TODO(johnniwinther): Avoid the need for a [ComponentLookup]. This
-        // is caused by some type masks holding a kernel node for using in
-        // tracing.
-        new ComponentLookup(_elementMap.programEnv.mainComponent));
-  }
-
-  @override
-  CodegenWorldBuilder createCodegenWorldBuilder(
-      NativeBasicData nativeBasicData,
-      JClosedWorld closedWorld,
-      SelectorConstraintsStrategy selectorConstraintsStrategy,
-      OneShotInterceptorData oneShotInterceptorData) {
-    return new CodegenWorldBuilderImpl(
-        closedWorld, selectorConstraintsStrategy, oneShotInterceptorData);
-  }
-
-  @override
   SourceSpan spanFromSpannable(Spannable spannable, Entity currentElement) {
     return _elementMap.getSourceSpan(spannable, currentElement);
   }
@@ -176,25 +412,25 @@
 }
 
 class KernelCodegenWorkItemBuilder implements WorkItemBuilder {
-  final JavaScriptBackend _backend;
+  final JsBackendStrategy _backendStrategy;
   final JClosedWorld _closedWorld;
   final CodegenResults _codegenResults;
   final EntityLookup _entityLookup;
   final ComponentLookup _componentLookup;
 
-  KernelCodegenWorkItemBuilder(this._backend, this._closedWorld,
+  KernelCodegenWorkItemBuilder(this._backendStrategy, this._closedWorld,
       this._codegenResults, this._entityLookup, this._componentLookup);
 
   @override
   WorkItem createWorkItem(MemberEntity entity) {
     if (entity.isAbstract) return null;
-    return new KernelCodegenWorkItem(_backend, _closedWorld, _codegenResults,
-        _entityLookup, _componentLookup, entity);
+    return new KernelCodegenWorkItem(_backendStrategy, _closedWorld,
+        _codegenResults, _entityLookup, _componentLookup, entity);
   }
 }
 
 class KernelCodegenWorkItem extends WorkItem {
-  final JavaScriptBackend _backend;
+  final JsBackendStrategy _backendStrategy;
   final JClosedWorld _closedWorld;
   final CodegenResults _codegenResults;
   final EntityLookup _entityLookup;
@@ -202,12 +438,17 @@
   @override
   final MemberEntity element;
 
-  KernelCodegenWorkItem(this._backend, this._closedWorld, this._codegenResults,
-      this._entityLookup, this._componentLookup, this.element);
+  KernelCodegenWorkItem(
+      this._backendStrategy,
+      this._closedWorld,
+      this._codegenResults,
+      this._entityLookup,
+      this._componentLookup,
+      this.element);
 
   @override
   WorldImpact run() {
-    return _backend.generateCode(
+    return _backendStrategy.generateCode(
         this, _closedWorld, _codegenResults, _entityLookup, _componentLookup);
   }
 }
diff --git a/pkg/compiler/lib/src/js_model/js_world_builder.dart b/pkg/compiler/lib/src/js_model/js_world_builder.dart
index f4d964c..805c178 100644
--- a/pkg/compiler/lib/src/js_model/js_world_builder.dart
+++ b/pkg/compiler/lib/src/js_model/js_world_builder.dart
@@ -143,20 +143,18 @@
           callMethods);
     } else {
       RuntimeTypesNeedImpl kernelRtiNeed = closedWorld.rtiNeed;
-      Set<ir.Node> localFunctionsNodesNeedingSignature = new Set<ir.Node>();
+      Set<ir.LocalFunction> localFunctionsNodesNeedingSignature =
+          new Set<ir.LocalFunction>();
       for (KLocalFunction localFunction
           in kernelRtiNeed.localFunctionsNeedingSignature) {
-        ir.Node node = localFunction.node;
-        assert(node is ir.FunctionDeclaration || node is ir.FunctionExpression,
-            "Unexpected local function node: $node");
+        ir.LocalFunction node = localFunction.node;
         localFunctionsNodesNeedingSignature.add(node);
       }
-      Set<ir.Node> localFunctionsNodesNeedingTypeArguments = new Set<ir.Node>();
+      Set<ir.LocalFunction> localFunctionsNodesNeedingTypeArguments =
+          new Set<ir.LocalFunction>();
       for (KLocalFunction localFunction
           in kernelRtiNeed.localFunctionsNeedingTypeArguments) {
-        ir.Node node = localFunction.node;
-        assert(node is ir.FunctionDeclaration || node is ir.FunctionExpression,
-            "Unexpected local function node: $node");
+        ir.LocalFunction node = localFunction.node;
         localFunctionsNodesNeedingTypeArguments.add(node);
       }
 
@@ -172,12 +170,12 @@
           callMethods);
 
       List<FunctionEntity> callMethodsNeedingSignature = <FunctionEntity>[];
-      for (ir.Node node in localFunctionsNodesNeedingSignature) {
+      for (ir.LocalFunction node in localFunctionsNodesNeedingSignature) {
         callMethodsNeedingSignature
             .add(closureData.getClosureInfo(node).callMethod);
       }
       List<FunctionEntity> callMethodsNeedingTypeArguments = <FunctionEntity>[];
-      for (ir.Node node in localFunctionsNodesNeedingTypeArguments) {
+      for (ir.LocalFunction node in localFunctionsNodesNeedingTypeArguments) {
         callMethodsNeedingTypeArguments
             .add(closureData.getClosureInfo(node).callMethod);
       }
@@ -533,15 +531,14 @@
 
 class JsClosureRtiNeed implements ClosureRtiNeed {
   final RuntimeTypesNeed rtiNeed;
-  final Set<ir.Node> localFunctionsNodesNeedingTypeArguments;
-  final Set<ir.Node> localFunctionsNodesNeedingSignature;
+  final Set<ir.LocalFunction> localFunctionsNodesNeedingTypeArguments;
+  final Set<ir.LocalFunction> localFunctionsNodesNeedingSignature;
 
   JsClosureRtiNeed(this.rtiNeed, this.localFunctionsNodesNeedingTypeArguments,
       this.localFunctionsNodesNeedingSignature);
 
   @override
-  bool localFunctionNeedsSignature(ir.Node node) {
-    assert(node is ir.FunctionDeclaration || node is ir.FunctionExpression);
+  bool localFunctionNeedsSignature(ir.LocalFunction node) {
     return localFunctionsNodesNeedingSignature.contains(node);
   }
 
@@ -554,8 +551,7 @@
       rtiNeed.methodNeedsTypeArguments(method);
 
   @override
-  bool localFunctionNeedsTypeArguments(ir.Node node) {
-    assert(node is ir.FunctionDeclaration || node is ir.FunctionExpression);
+  bool localFunctionNeedsTypeArguments(ir.LocalFunction node) {
     return localFunctionsNodesNeedingTypeArguments.contains(node);
   }
 
diff --git a/pkg/compiler/lib/src/js_model/type_recipe.dart b/pkg/compiler/lib/src/js_model/type_recipe.dart
new file mode 100644
index 0000000..2c72265
--- /dev/null
+++ b/pkg/compiler/lib/src/js_model/type_recipe.dart
@@ -0,0 +1,85 @@
+// Copyright (c) 2019, 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.
+
+library dart2js.js_model.type_recipe;
+
+import '../elements/types.dart';
+
+/// A TypeEnvironmentStructure describes the shape or layout of a reified type
+/// environment.
+///
+/// A type environment maps type parameter variables to type values. The type
+/// variables are mostly elided in the runtime representation, replaced by
+/// indexes into the reified environment.
+abstract class TypeEnvironmentStructure {}
+
+/// A singleton type environment maps a binds a single value.
+class SingletonTypeEnvironmentStructure extends TypeEnvironmentStructure {
+  final TypeVariableType variable;
+
+  SingletonTypeEnvironmentStructure(this.variable);
+
+  @override
+  String toString() => 'SingletonTypeEnvironmentStructure($variable)';
+}
+
+/// A type environment containing an interface type and/or a tuple of function
+/// type parameters.
+class FullTypeEnvironmentStructure extends TypeEnvironmentStructure {
+  final InterfaceType classType;
+  final List<TypeVariableType> bindings;
+
+  FullTypeEnvironmentStructure({this.classType, this.bindings = const []});
+
+  @override
+  String toString() => 'FullTypeEnvironmentStructure($classType, $bindings)';
+}
+
+/// A TypeRecipe is evaluated against a type environment to produce either a
+/// type, or another type environment.
+abstract class TypeRecipe {}
+
+/// A recipe that yields a reified type.
+class TypeExpressionRecipe extends TypeRecipe {
+  final DartType type;
+
+  TypeExpressionRecipe(this.type);
+
+  @override
+  String toString() => 'TypeExpressionRecipe($type)';
+}
+
+/// A recipe that yields a reified type environment.
+abstract class TypeEnvironmentRecipe extends TypeRecipe {}
+
+/// A recipe that yields a reified type environment that binds a single generic
+/// function type parameter.
+class SingletonTypeEnvironmentRecipe extends TypeEnvironmentRecipe {
+  final DartType type;
+
+  SingletonTypeEnvironmentRecipe(this.type);
+
+  @override
+  String toString() => 'SingletonTypeEnvironmentRecipe($type)';
+}
+
+/// A recipe that yields a reified type environment that binds a class instance
+/// type and/or a tuple of types, usually generic function type arguments.
+///
+/// With no class is also used as a tuple of types.
+class FullTypeEnvironmentRecipe extends TypeEnvironmentRecipe {
+  /// Type expression for the interface type of a class scope.  `null` for
+  /// environments outside a class scope or a class scope where no supertype is
+  /// generic, or where optimization has determined that no use of the
+  /// environment requires any of the class type variables.
+  final InterfaceType classType;
+
+  // Type expressions for the tuple of function type arguments.
+  final List<DartType> types;
+
+  FullTypeEnvironmentRecipe({this.classType, this.types = const []});
+
+  @override
+  String toString() => 'FullTypeEnvironmentRecipe($classType, $types)';
+}
diff --git a/pkg/compiler/lib/src/kernel/element_map.dart b/pkg/compiler/lib/src/kernel/element_map.dart
index 3c523ff..9285a64 100644
--- a/pkg/compiler/lib/src/kernel/element_map.dart
+++ b/pkg/compiler/lib/src/kernel/element_map.dart
@@ -163,9 +163,8 @@
   /// [JS_INTERCEPTOR_CONSTANT] function, if any.
   InterfaceType getInterfaceTypeForJsInterceptorCall(ir.StaticInvocation node);
 
-  /// Returns the [Local] corresponding to the [node]. The node must be either
-  /// a [ir.FunctionDeclaration] or [ir.FunctionExpression].
-  Local getLocalFunction(ir.TreeNode node);
+  /// Returns the [Local] corresponding to the local function [node].
+  Local getLocalFunction(ir.LocalFunction node);
 
   /// Returns the [ir.Library] corresponding to [library].
   ir.Library getLibraryNode(LibraryEntity library);
diff --git a/pkg/compiler/lib/src/kernel/element_map_impl.dart b/pkg/compiler/lib/src/kernel/element_map_impl.dart
index 84f80cc..ce5aed9 100644
--- a/pkg/compiler/lib/src/kernel/element_map_impl.dart
+++ b/pkg/compiler/lib/src/kernel/element_map_impl.dart
@@ -41,7 +41,6 @@
 import '../ir/util.dart';
 import '../js/js.dart' as js;
 import '../js_backend/annotations.dart';
-import '../js_backend/backend.dart' show JavaScriptBackend;
 import '../js_backend/namer.dart';
 import '../js_backend/native_data.dart';
 import '../js_backend/no_such_method_registry.dart';
@@ -1206,8 +1205,7 @@
                   getMethodInternal(procedure), node.name, index),
               new KTypeVariableData(node));
         }
-      } else if (func.parent is ir.FunctionDeclaration ||
-          func.parent is ir.FunctionExpression) {
+      } else if (func.parent is ir.LocalFunction) {
         // Ensure that local function type variables have been created.
         getLocalFunction(func.parent);
         return typeVariableMap[node];
@@ -1417,11 +1415,7 @@
   }
 
   @override
-  Local getLocalFunction(ir.TreeNode node) {
-    assert(
-        node is ir.FunctionDeclaration || node is ir.FunctionExpression,
-        failedAt(
-            CURRENT_ELEMENT_SPANNABLE, 'Invalid local function node: $node'));
+  Local getLocalFunction(ir.LocalFunction node) {
     KLocalFunction localFunction = localFunctionMap[node];
     if (localFunction == null) {
       MemberEntity memberContext;
@@ -1432,8 +1426,7 @@
           executableContext = memberContext = getMember(parent);
           break;
         }
-        if (parent is ir.FunctionDeclaration ||
-            parent is ir.FunctionExpression) {
+        if (parent is ir.LocalFunction) {
           KLocalFunction localFunction = getLocalFunction(parent);
           executableContext = localFunction;
           memberContext = localFunction.memberContext;
@@ -1492,13 +1485,13 @@
   ForeignKind getForeignKind(ir.StaticInvocation node) {
     if (commonElements.isForeignHelper(getMember(node.target))) {
       switch (node.target.name.name) {
-        case JavaScriptBackend.JS:
+        case Identifiers.JS:
           return ForeignKind.JS;
-        case JavaScriptBackend.JS_BUILTIN:
+        case Identifiers.JS_BUILTIN:
           return ForeignKind.JS_BUILTIN;
-        case JavaScriptBackend.JS_EMBEDDED_GLOBAL:
+        case Identifiers.JS_EMBEDDED_GLOBAL:
           return ForeignKind.JS_EMBEDDED_GLOBAL;
-        case JavaScriptBackend.JS_INTERCEPTOR_CONSTANT:
+        case Identifiers.JS_INTERCEPTOR_CONSTANT:
           return ForeignKind.JS_INTERCEPTOR_CONSTANT;
       }
     }
diff --git a/pkg/compiler/lib/src/kernel/env.dart b/pkg/compiler/lib/src/kernel/env.dart
index c76aaaa..fe6d512 100644
--- a/pkg/compiler/lib/src/kernel/env.dart
+++ b/pkg/compiler/lib/src/kernel/env.dart
@@ -4,8 +4,8 @@
 
 library dart2js.kernel.env;
 
-import 'package:front_end/src/api_unstable/dart2js.dart' as ir
-    show RedirectingFactoryBody;
+import 'package:front_end/src/api_unstable/dart2js.dart'
+    show isRedirectingFactory;
 
 import 'package:kernel/ast.dart' as ir;
 import 'package:kernel/clone.dart';
@@ -415,7 +415,7 @@
       var name = member.name.name;
       assert(!name.contains('#'));
       if (member.kind == ir.ProcedureKind.Factory) {
-        if (member.function.body is ir.RedirectingFactoryBody) {
+        if (isRedirectingFactory(member)) {
           // Don't include redirecting factories.
           return;
         }
diff --git a/pkg/compiler/lib/src/kernel/kelements.dart b/pkg/compiler/lib/src/kernel/kelements.dart
index 1ac70af..3062fc2 100644
--- a/pkg/compiler/lib/src/kernel/kelements.dart
+++ b/pkg/compiler/lib/src/kernel/kelements.dart
@@ -281,7 +281,7 @@
   final String name;
   final MemberEntity memberContext;
   final Entity executableContext;
-  final ir.Node node;
+  final ir.LocalFunction node;
   FunctionType functionType;
 
   KLocalFunction(
diff --git a/pkg/compiler/lib/src/kernel/kernel_strategy.dart b/pkg/compiler/lib/src/kernel/kernel_strategy.dart
index c93e50f..4ecf6d3 100644
--- a/pkg/compiler/lib/src/kernel/kernel_strategy.dart
+++ b/pkg/compiler/lib/src/kernel/kernel_strategy.dart
@@ -8,6 +8,7 @@
 
 import '../common.dart';
 import '../common/backend_api.dart';
+import '../common/names.dart' show Uris;
 import '../common/resolution.dart';
 import '../common/tasks.dart';
 import '../common/work.dart';
@@ -15,7 +16,6 @@
 import '../compiler.dart';
 import '../deferred_load.dart' show DeferredLoadTask;
 import '../elements/entities.dart';
-import '../elements/types.dart';
 import '../enqueue.dart';
 import '../environment.dart' as env;
 import '../frontend_strategy.dart';
@@ -25,12 +25,17 @@
 import '../ir/modular.dart';
 import '../ir/scope.dart' show ScopeModel;
 import '../js_backend/annotations.dart';
-import '../js_backend/field_analysis.dart' show KFieldAnalysis;
+import '../js_backend/backend_impact.dart';
 import '../js_backend/backend_usage.dart';
+import '../js_backend/custom_elements_analysis.dart';
+import '../js_backend/field_analysis.dart' show KFieldAnalysis;
+import '../js_backend/impact_transformer.dart';
 import '../js_backend/interceptor_data.dart';
 import '../js_backend/native_data.dart';
 import '../js_backend/no_such_method_registry.dart';
+import '../js_backend/resolution_listener.dart';
 import '../js_backend/runtime_types.dart';
+import '../kernel/dart2js_target.dart';
 import '../native/enqueue.dart' show NativeResolutionEnqueuer;
 import '../native/resolver.dart';
 import '../options.dart';
@@ -46,7 +51,10 @@
 
 /// Front end strategy that loads '.dill' files and builds a resolved element
 /// model from kernel IR nodes.
-class KernelFrontEndStrategy extends FrontendStrategyBase {
+class KernelFrontendStrategy extends FrontendStrategy {
+  final NativeBasicDataBuilderImpl nativeBasicDataBuilder =
+      new NativeBasicDataBuilderImpl();
+  NativeBasicData _nativeBasicData;
   CompilerOptions _options;
   CompilerTask _compilerTask;
   KernelToElementMapImpl _elementMap;
@@ -59,12 +67,180 @@
   ModularStrategy _modularStrategy;
   IrAnnotationData _irAnnotationData;
 
-  KernelFrontEndStrategy(this._compilerTask, this._options,
+  NativeDataBuilderImpl _nativeDataBuilder;
+  NativeDataBuilder get nativeDataBuilder => _nativeDataBuilder;
+
+  BackendUsageBuilder _backendUsageBuilder;
+
+  NativeResolutionEnqueuer _nativeResolutionEnqueuer;
+
+  /// Resolution support for generating table of interceptors and
+  /// constructors for custom elements.
+  CustomElementsResolutionAnalysis _customElementsResolutionAnalysis;
+
+  KFieldAnalysis _fieldAnalysis;
+
+  /// Backend transformation methods for the world impacts.
+  ImpactTransformer impactTransformer;
+
+  @override
+  NoSuchMethodRegistry noSuchMethodRegistry;
+
+  KernelFrontendStrategy(this._compilerTask, this._options,
       DiagnosticReporter reporter, env.Environment environment) {
     assert(_compilerTask != null);
     _elementMap =
         new KernelToElementMapImpl(reporter, environment, this, _options);
     _modularStrategy = new KernelModularStrategy(_compilerTask, _elementMap);
+    _backendUsageBuilder = new BackendUsageBuilderImpl(this);
+    noSuchMethodRegistry = new NoSuchMethodRegistryImpl(
+        commonElements, new KernelNoSuchMethodResolver(_elementMap));
+  }
+
+  NativeResolutionEnqueuer get nativeResolutionEnqueuerForTesting =>
+      _nativeResolutionEnqueuer;
+
+  KFieldAnalysis get fieldAnalysisForTesting => _fieldAnalysis;
+
+  @override
+  void onResolutionStart() {
+    // TODO(johnniwinther): Avoid the compiler.elementEnvironment.getThisType
+    // calls. Currently needed to ensure resolution of the classes for various
+    // queries in native behavior computation, inference and codegen.
+    elementEnvironment.getThisType(commonElements.jsArrayClass);
+    elementEnvironment.getThisType(commonElements.jsExtendableArrayClass);
+
+    _validateInterceptorImplementsAllObjectMethods(
+        commonElements.jsInterceptorClass);
+    // The null-interceptor must also implement *all* methods.
+    _validateInterceptorImplementsAllObjectMethods(commonElements.jsNullClass);
+  }
+
+  void _validateInterceptorImplementsAllObjectMethods(
+      ClassEntity interceptorClass) {
+    if (interceptorClass == null) return;
+    ClassEntity objectClass = commonElements.objectClass;
+    elementEnvironment.forEachClassMember(objectClass,
+        (_, MemberEntity member) {
+      MemberEntity interceptorMember = elementEnvironment
+          .lookupLocalClassMember(interceptorClass, member.name);
+      // Interceptors must override all Object methods due to calling convention
+      // differences.
+      assert(
+          interceptorMember.enclosingClass == interceptorClass,
+          failedAt(
+              interceptorMember,
+              "Member ${member.name} not overridden in ${interceptorClass}. "
+              "Found $interceptorMember from "
+              "${interceptorMember.enclosingClass}."));
+    });
+  }
+
+  @override
+  ResolutionEnqueuer createResolutionEnqueuer(
+      CompilerTask task, Compiler compiler) {
+    RuntimeTypesNeedBuilder rtiNeedBuilder = _createRuntimeTypesNeedBuilder();
+    BackendImpacts impacts =
+        new BackendImpacts(commonElements, compiler.options.experimentNewRti);
+    _nativeResolutionEnqueuer = new NativeResolutionEnqueuer(
+        compiler.options,
+        elementEnvironment,
+        commonElements,
+        _elementMap.types,
+        new BaseNativeClassFinder(elementEnvironment, nativeBasicData));
+    _nativeDataBuilder = new NativeDataBuilderImpl(nativeBasicData);
+    _customElementsResolutionAnalysis = new CustomElementsResolutionAnalysis(
+        elementEnvironment,
+        commonElements,
+        nativeBasicData,
+        _backendUsageBuilder);
+    _fieldAnalysis = new KFieldAnalysis(this);
+    ClassQueries classQueries = new KernelClassQueries(elementMap);
+    ClassHierarchyBuilder classHierarchyBuilder =
+        new ClassHierarchyBuilder(commonElements, classQueries);
+    impactTransformer = new JavaScriptImpactTransformer(
+        compiler.options,
+        elementEnvironment,
+        commonElements,
+        impacts,
+        nativeBasicData,
+        _nativeResolutionEnqueuer,
+        _backendUsageBuilder,
+        _customElementsResolutionAnalysis,
+        rtiNeedBuilder,
+        classHierarchyBuilder);
+    InterceptorDataBuilder interceptorDataBuilder =
+        new InterceptorDataBuilderImpl(
+            nativeBasicData, elementEnvironment, commonElements);
+    AnnotationsDataBuilder annotationsDataBuilder =
+        new AnnotationsDataBuilder();
+    return new ResolutionEnqueuer(
+        task,
+        compiler.options,
+        compiler.reporter,
+        new ResolutionEnqueuerListener(
+            compiler.options,
+            elementEnvironment,
+            commonElements,
+            impacts,
+            nativeBasicData,
+            interceptorDataBuilder,
+            _backendUsageBuilder,
+            noSuchMethodRegistry,
+            _customElementsResolutionAnalysis,
+            _nativeResolutionEnqueuer,
+            _fieldAnalysis,
+            compiler.deferredLoadTask),
+        new ResolutionWorldBuilderImpl(
+            _options,
+            elementMap,
+            elementEnvironment,
+            _elementMap.types,
+            commonElements,
+            nativeBasicData,
+            nativeDataBuilder,
+            interceptorDataBuilder,
+            _backendUsageBuilder,
+            rtiNeedBuilder,
+            _fieldAnalysis,
+            _nativeResolutionEnqueuer,
+            noSuchMethodRegistry,
+            annotationsDataBuilder,
+            const StrongModeWorldStrategy(),
+            classHierarchyBuilder,
+            classQueries),
+        new KernelWorkItemBuilder(
+            _compilerTask,
+            elementMap,
+            nativeBasicData,
+            nativeDataBuilder,
+            annotationsDataBuilder,
+            impactTransformer,
+            closureModels,
+            compiler.impactCache,
+            _fieldAnalysis,
+            _modularStrategy,
+            _irAnnotationData));
+  }
+
+  @override
+  void onResolutionEnd() {
+    assert(_annotationProcessor != null,
+        "AnnotationProcessor has not been created.");
+    _annotationProcessor.processJsInteropAnnotations(
+        nativeBasicData, nativeDataBuilder);
+  }
+
+  @override
+  NativeBasicData get nativeBasicData {
+    if (_nativeBasicData == null) {
+      _nativeBasicData = nativeBasicDataBuilder.close(elementEnvironment);
+      assert(
+          _nativeBasicData != null,
+          failedAt(NO_LOCATION_SPANNABLE,
+              "NativeBasicData has not been computed yet."));
+    }
+    return _nativeBasicData;
   }
 
   @override
@@ -76,6 +252,16 @@
     }
     _annotationProcessor = new KernelAnnotationProcessor(
         elementMap, nativeBasicDataBuilder, _irAnnotationData);
+    for (Uri uri in kernelResult.libraries) {
+      LibraryEntity library = elementEnvironment.lookupLibrary(uri);
+      if (maybeEnableNative(library.canonicalUri)) {
+        _annotationProcessor.extractNativeAnnotations(library);
+      }
+      _annotationProcessor.extractJsInteropAnnotations(library);
+      if (uri == Uris.dart_html) {
+        _backendUsageBuilder.registerHtmlIsLoaded();
+      }
+    }
   }
 
   IrAnnotationData get irAnnotationDataForTesting => _irAnnotationData;
@@ -88,112 +274,28 @@
   @override
   CommonElements get commonElements => _elementMap.commonElements;
 
-  @override
-  DartTypes get dartTypes => _elementMap.types;
-
   KernelToElementMap get elementMap => _elementMap;
 
   @override
-  AnnotationProcessor get annotationProcessor {
-    assert(_annotationProcessor != null,
-        "AnnotationProcessor has not been created.");
-    return _annotationProcessor;
-  }
-
-  @override
   DeferredLoadTask createDeferredLoadTask(Compiler compiler) =>
       new KernelDeferredLoadTask(compiler, _elementMap);
 
   @override
-  NativeClassFinder createNativeClassFinder(NativeBasicData nativeBasicData) {
-    return new BaseNativeClassFinder(
-        _elementMap.elementEnvironment, nativeBasicData);
-  }
-
-  @override
-  NoSuchMethodResolver createNoSuchMethodResolver() {
-    return new KernelNoSuchMethodResolver(elementMap);
-  }
-
-  @override
   FunctionEntity computeMain(WorldImpactBuilder impactBuilder) {
     return elementEnvironment.mainFunction;
   }
 
-  @override
-  RuntimeTypesNeedBuilder createRuntimeTypesNeedBuilder() {
+  RuntimeTypesNeedBuilder _createRuntimeTypesNeedBuilder() {
     return _runtimeTypesNeedBuilder ??= _options.disableRtiOptimization
         ? const TrivialRuntimeTypesNeedBuilder()
         : new RuntimeTypesNeedBuilderImpl(
             elementEnvironment, _elementMap.types);
   }
 
-  @override
   RuntimeTypesNeedBuilder get runtimeTypesNeedBuilderForTesting =>
       _runtimeTypesNeedBuilder;
 
   @override
-  ResolutionWorldBuilder createResolutionWorldBuilder(
-      NativeBasicData nativeBasicData,
-      NativeDataBuilder nativeDataBuilder,
-      InterceptorDataBuilder interceptorDataBuilder,
-      BackendUsageBuilder backendUsageBuilder,
-      RuntimeTypesNeedBuilder rtiNeedBuilder,
-      KFieldAnalysis allocatorAnalysis,
-      NativeResolutionEnqueuer nativeResolutionEnqueuer,
-      NoSuchMethodRegistry noSuchMethodRegistry,
-      AnnotationsDataBuilder annotationsDataBuilder,
-      SelectorConstraintsStrategy selectorConstraintsStrategy,
-      ClassHierarchyBuilder classHierarchyBuilder,
-      ClassQueries classQueries) {
-    return new ResolutionWorldBuilderImpl(
-        _options,
-        elementMap,
-        elementMap.elementEnvironment,
-        elementMap.types,
-        elementMap.commonElements,
-        nativeBasicData,
-        nativeDataBuilder,
-        interceptorDataBuilder,
-        backendUsageBuilder,
-        rtiNeedBuilder,
-        allocatorAnalysis,
-        nativeResolutionEnqueuer,
-        noSuchMethodRegistry,
-        annotationsDataBuilder,
-        selectorConstraintsStrategy,
-        classHierarchyBuilder,
-        classQueries);
-  }
-
-  @override
-  WorkItemBuilder createResolutionWorkItemBuilder(
-      NativeBasicData nativeBasicData,
-      NativeDataBuilder nativeDataBuilder,
-      AnnotationsDataBuilder annotationsDataBuilder,
-      ImpactTransformer impactTransformer,
-      Map<Entity, WorldImpact> impactCache,
-      KFieldAnalysis fieldAnalysis) {
-    return new KernelWorkItemBuilder(
-        _compilerTask,
-        elementMap,
-        nativeBasicData,
-        nativeDataBuilder,
-        annotationsDataBuilder,
-        impactTransformer,
-        closureModels,
-        impactCache,
-        fieldAnalysis,
-        _modularStrategy,
-        _irAnnotationData);
-  }
-
-  @override
-  ClassQueries createClassQueries() {
-    return new KernelClassQueries(elementMap);
-  }
-
-  @override
   SourceSpan spanFromSpannable(Spannable spannable, Entity currentElement) {
     return _elementMap.getSourceSpan(spannable, currentElement);
   }
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index 9c0a6a5..257c019 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -45,6 +45,7 @@
 import '../tracer.dart';
 import '../universe/call_structure.dart';
 import '../universe/feature.dart';
+import '../universe/member_usage.dart' show MemberAccess;
 import '../universe/selector.dart';
 import '../universe/side_effects.dart' show SideEffects;
 import '../universe/target_checks.dart' show TargetChecks;
@@ -477,10 +478,7 @@
               }
             }
             _buildField(target);
-          } else if (target is ir.FunctionExpression) {
-            _buildFunctionNode(
-                targetElement, _ensureDefaultArgumentValues(target.function));
-          } else if (target is ir.FunctionDeclaration) {
+          } else if (target is ir.LocalFunction) {
             _buildFunctionNode(
                 targetElement, _ensureDefaultArgumentValues(target.function));
           } else {
@@ -506,9 +504,7 @@
           ir.FunctionNode originalClosureNode;
           if (target is ir.Procedure) {
             originalClosureNode = target.function;
-          } else if (target is ir.FunctionExpression) {
-            originalClosureNode = target.function;
-          } else if (target is ir.FunctionDeclaration) {
+          } else if (target is ir.LocalFunction) {
             originalClosureNode = target.function;
           } else {
             failedAt(
@@ -546,8 +542,7 @@
 
   ir.FunctionNode _functionNodeOf(ir.TreeNode node) {
     if (node is ir.Member) return node.function;
-    if (node is ir.FunctionDeclaration) return node.function;
-    if (node is ir.FunctionExpression) return node.function;
+    if (node is ir.LocalFunction) return node.function;
     return null;
   }
 
@@ -1203,7 +1198,7 @@
   /// that no corresponding ir.Node actually exists for it. We just use the
   /// targetElement.
   void _buildMethodSignature(ir.FunctionNode originalClosureNode) {
-    _openFunction(targetElement);
+    _openFunction(targetElement, checks: TargetChecks.none);
     List<HInstruction> typeArguments = <HInstruction>[];
 
     // Add function type variables.
@@ -1249,13 +1244,10 @@
       return;
     }
 
-    // TODO(sra): Static methods with no tear-off can be generated with no
-    // checks.
-    // TODO(sra): Instance methods can be generated with reduced checks if
-    // called only from non-dynamic call-sites.
     _openFunction(function,
         functionNode: functionNode,
-        parameterStructure: function.parameterStructure);
+        parameterStructure: function.parameterStructure,
+        checks: _checksForFunction(function));
 
     // If [functionNode] is `operator==` we explicitly add a null check at the
     // beginning of the method. This is to avoid having call sites do the null
@@ -1314,7 +1306,8 @@
   void _buildGenerator(FunctionEntity function, ir.FunctionNode functionNode) {
     _openFunction(function,
         functionNode: functionNode,
-        parameterStructure: function.parameterStructure);
+        parameterStructure: function.parameterStructure,
+        checks: _checksForFunction(function));
 
     // Prepare to tail-call the body.
 
@@ -1480,7 +1473,8 @@
     assert(functionNode.body == null);
     _openFunction(function,
         functionNode: functionNode,
-        parameterStructure: function.parameterStructure);
+        parameterStructure: function.parameterStructure,
+        checks: _checksForFunction(function));
 
     if (closedWorld.nativeData.isNativeMember(targetElement)) {
       registry.registerNativeMethod(targetElement);
@@ -1578,12 +1572,24 @@
     }
   }
 
+  TargetChecks _checksForFunction(FunctionEntity function) {
+    if (!function.isInstanceMember) {
+      // Static methods with no tear-off can be generated with no checks.
+      MemberAccess access = closedWorld.getMemberAccess(function);
+      if (access != null && access.reads.isEmpty) {
+        return TargetChecks.none;
+      }
+    }
+    // TODO(sra): Instance methods can be generated with reduced checks if
+    // called only from non-dynamic call-sites.
+    return TargetChecks.dynamicChecks;
+  }
+
   void _openFunction(MemberEntity member,
       {ir.FunctionNode functionNode,
       ParameterStructure parameterStructure,
       TargetChecks checks}) {
-    // TODO(sra): Pass from all sites.
-    checks ??= TargetChecks.dynamicChecks;
+    assert(checks != null);
 
     Map<Local, AbstractValue> parameterMap = {};
     List<ir.VariableDeclaration> elidedParameters = [];
@@ -6098,15 +6104,9 @@
         }
         break;
       case MemberKind.closureCall:
-        ir.Node node = definition.node;
-        if (node is ir.FunctionExpression) {
-          node.function.body.accept(this);
-          return;
-        } else if (node is ir.FunctionDeclaration) {
-          node.function.body.accept(this);
-          return;
-        }
-        break;
+        ir.LocalFunction node = definition.node;
+        node.function.body.accept(this);
+        return;
       default:
         break;
     }
diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart
index 5fabe81..a80a7a6 100644
--- a/pkg/compiler/lib/src/ssa/codegen.dart
+++ b/pkg/compiler/lib/src/ssa/codegen.dart
@@ -696,6 +696,7 @@
     if (instruction is HCheck) {
       if (instruction is HTypeConversion ||
           instruction is HPrimitiveCheck ||
+          instruction is HAsCheck ||
           instruction is HBoolConversion) {
         String inputName = variableNames.getName(instruction.checkedInput);
         if (variableNames.getName(instruction) == inputName) {
@@ -3333,31 +3334,91 @@
 
   @override
   visitIsTest(HIsTest node) {
-    throw UnimplementedError('SsaCodeGenerator.visitIsTest');
+    use(node.typeInput);
+    js.Expression first = pop();
+    use(node.checkedInput);
+    js.Expression second = pop();
+
+    FieldEntity field = _commonElements.rtiIsField;
+    js.Name name = _namer.instanceFieldPropertyName(field);
+
+    push(js.js('#.#(#)', [first, name, second]).withSourceInformation(
+        node.sourceInformation));
   }
 
   @override
   visitAsCheck(HAsCheck node) {
-    throw UnimplementedError('SsaCodeGenerator.visitAsCheck');
+    use(node.typeInput);
+    js.Expression first = pop();
+    use(node.checkedInput);
+    js.Expression second = pop();
+
+    FieldEntity field = node.isTypeError
+        ? _commonElements.rtiCheckField
+        : _commonElements.rtiAsField;
+    js.Name name = _namer.instanceFieldPropertyName(field);
+
+    push(js.js('#.#(#)', [first, name, second]).withSourceInformation(
+        node.sourceInformation));
   }
 
   @override
   visitSubtypeCheck(HSubtypeCheck node) {
-    throw UnimplementedError('SsaCodeGenerator.visitSubtypeCheck');
+    throw UnimplementedError('SsaCodeGenerator.visitSubtypeCheck  $node');
   }
 
   @override
   visitLoadType(HLoadType node) {
-    throw UnimplementedError('SsaCodeGenerator.visitLoadType');
+    FunctionEntity helperElement = _commonElements.findType;
+    _registry.registerStaticUse(
+        new StaticUse.staticInvoke(helperElement, CallStructure.ONE_ARG));
+    // TODO(sra): Encode recipe.
+    js.Expression recipe = js.string('${node.typeExpression}');
+    js.Expression helper = _emitter.staticFunctionAccess(helperElement);
+    push(js.js(r'#(#)', [helper, recipe]).withSourceInformation(
+        node.sourceInformation));
   }
 
   @override
   visitTypeEval(HTypeEval node) {
-    throw UnimplementedError('SsaCodeGenerator.visitTypeEval');
+    // Call `env._eval("recipe")`.
+    use(node.inputs[0]);
+    js.Expression environment = pop();
+    // TODO(sra): Encode recipe.
+    js.Expression recipe = js.string('${node.typeExpression}');
+
+    MemberEntity method = _commonElements.rtiEvalMethod;
+    Selector selector = Selector.fromElement(method);
+    js.Name methodLiteral = _namer.invocationName(selector);
+    push(js.js('#.#(#)', [
+      environment,
+      methodLiteral,
+      recipe
+    ]).withSourceInformation(node.sourceInformation));
+
+    _registry.registerStaticUse(
+        new StaticUse.directInvoke(method, selector.callStructure, null));
   }
 
   @override
   visitTypeBind(HTypeBind node) {
-    throw UnimplementedError('SsaCodeGenerator.visitTypeBind');
+    // Call `env1._bind(env2)`.
+    assert(node.inputs.length == 2);
+    use(node.inputs[0]);
+    js.Expression environment = pop();
+    use(node.inputs[1]);
+    js.Expression extensions = pop();
+
+    MemberEntity method = _commonElements.rtiEvalMethod;
+    Selector selector = Selector.fromElement(method);
+    js.Name methodLiteral = _namer.invocationName(selector);
+    push(js.js('#.#(#)', [
+      environment,
+      methodLiteral,
+      extensions
+    ]).withSourceInformation(node.sourceInformation));
+
+    _registry.registerStaticUse(
+        new StaticUse.directInvoke(method, selector.callStructure, null));
   }
 }
diff --git a/pkg/compiler/lib/src/ssa/codegen_helpers.dart b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
index 8ffd0ed..c0ae4ec 100644
--- a/pkg/compiler/lib/src/ssa/codegen_helpers.dart
+++ b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
@@ -755,6 +755,20 @@
   }
 
   @override
+  void visitAsCheck(HAsCheck instruction) {
+    // Type checks and cast checks compile to code that only use their input
+    // once, so we can safely visit them and try to merge the input.
+    visitInstruction(instruction);
+  }
+
+  @override
+  void visitTypeEval(HTypeEval instruction) {
+    // Type expressions compile to code that only use their input once, so we
+    // can safely visit them and try to merge the input.
+    visitInstruction(instruction);
+  }
+
+  @override
   void visitPrimitiveCheck(HPrimitiveCheck instruction) {}
 
   @override
diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart
index 3d8a473..ac8f779 100644
--- a/pkg/compiler/lib/src/ssa/nodes.dart
+++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -14,6 +14,7 @@
 import '../inferrer/abstract_value_domain.dart';
 import '../io/source_information.dart';
 import '../js/js.dart' as js;
+import '../js_model/type_recipe.dart' show TypeEnvironmentStructure, TypeRecipe;
 import '../native/behavior.dart';
 import '../universe/selector.dart' show Selector;
 import '../universe/side_effects.dart' show SideEffects;
@@ -4373,7 +4374,8 @@
 
   HAsCheck(HInstruction checked, HInstruction rti, this.isTypeError,
       AbstractValue type)
-      : super([rti, checked], type) {}
+      : assert(isTypeError != null),
+        super([rti, checked], type);
 
   // The type input is first to facilitate the `type.as(value)` codegen pattern.
   HInstruction get typeInput => inputs[0];
@@ -4381,6 +4383,9 @@
   HInstruction get checkedInput => inputs[1];
 
   @override
+  bool isJsStatement() => false;
+
+  @override
   accept(HVisitor visitor) => visitor.visitAsCheck(this);
 
   @override
@@ -4461,9 +4466,11 @@
 
 /// Evaluates an Rti type recipe in an Rti environment.
 class HTypeEval extends HRtiInstruction {
-  DartType typeExpression; // TODO(sra); Allow a type environment expression.
+  TypeEnvironmentStructure envStructure;
+  TypeRecipe typeExpression;
 
-  HTypeEval(HInstruction environment, this.typeExpression, AbstractValue type)
+  HTypeEval(HInstruction environment, this.envStructure, this.typeExpression,
+      AbstractValue type)
       : super([environment], type) {
     setUseGvn();
   }
diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart
index 249121e..1a27e5b 100644
--- a/pkg/compiler/lib/src/ssa/optimize.dart
+++ b/pkg/compiler/lib/src/ssa/optimize.dart
@@ -1322,6 +1322,11 @@
     // convention, but is not a call on an interceptor.
     HInstruction value = node.inputs.last;
     if (_options.parameterCheckPolicy.isEmitted) {
+      if (_options.experimentNewRti) {
+        // TODO(sra): Implement inlining of setters with checks for new rti.
+        node.needsCheck = true;
+        return node;
+      }
       DartType type = _closedWorld.elementEnvironment.getFieldType(field);
       if (!type.treatAsRaw ||
           type.isTypeVariable ||
diff --git a/pkg/compiler/lib/src/ssa/ssa_tracer.dart b/pkg/compiler/lib/src/ssa/ssa_tracer.dart
index 95bfbb5..539261d 100644
--- a/pkg/compiler/lib/src/ssa/ssa_tracer.dart
+++ b/pkg/compiler/lib/src/ssa/ssa_tracer.dart
@@ -696,13 +696,13 @@
   @override
   visitLoadType(HLoadType node) {
     var inputs = node.inputs.map(temporaryId).join(', ');
-    return "LoadType: $inputs";
+    return "LoadType: ${node.typeExpression}  $inputs";
   }
 
   @override
   visitTypeEval(HTypeEval node) {
     var inputs = node.inputs.map(temporaryId).join(', ');
-    return "TypeEval: $inputs";
+    return "TypeEval: ${node.typeExpression}  $inputs";
   }
 
   @override
diff --git a/pkg/compiler/lib/src/ssa/type_builder.dart b/pkg/compiler/lib/src/ssa/type_builder.dart
index 0a5d8fe..11c31ec 100644
--- a/pkg/compiler/lib/src/ssa/type_builder.dart
+++ b/pkg/compiler/lib/src/ssa/type_builder.dart
@@ -7,6 +7,7 @@
 import '../elements/entities.dart';
 import '../elements/types.dart';
 import '../inferrer/abstract_value_domain.dart';
+import '../js_model/type_recipe.dart';
 import '../io/source_information.dart';
 import '../universe/use.dart' show TypeUse;
 import '../world.dart';
@@ -69,10 +70,11 @@
 
   /// Produces code that checks the runtime type is actually the type specified
   /// by attempting a type conversion.
-  HInstruction _checkType(HInstruction original, DartType type, int kind) {
+  HInstruction _checkType(HInstruction original, DartType type) {
     assert(type != null);
     type = builder.localsHandler.substInContext(type);
-    HInstruction other = buildTypeConversion(original, type, kind);
+    HInstruction other =
+        buildTypeConversion(original, type, HTypeConversion.TYPE_CHECK);
     // TODO(johnniwinther): This operation on `registry` may be inconsistent.
     // If it is needed then it seems likely that similar invocations of
     // `buildTypeConversion` in `SsaBuilder.visitAs` should also be followed by
@@ -116,7 +118,7 @@
     if (builder.options.parameterCheckPolicy.isTrusted) {
       checkedOrTrusted = _trustType(original, type);
     } else if (builder.options.parameterCheckPolicy.isEmitted) {
-      checkedOrTrusted = _checkType(original, type, HTypeConversion.TYPE_CHECK);
+      checkedOrTrusted = _checkType(original, type);
     }
     if (checkedOrTrusted == original) return original;
     builder.add(checkedOrTrusted);
@@ -127,14 +129,13 @@
   /// instruction that checks the type is what we expect or automatically
   /// trusts the written type.
   HInstruction potentiallyCheckOrTrustTypeOfAssignment(
-      HInstruction original, DartType type,
-      {int kind: HTypeConversion.TYPE_CHECK}) {
+      HInstruction original, DartType type) {
     if (type == null) return original;
     HInstruction checkedOrTrusted = original;
     if (builder.options.assignmentCheckPolicy.isTrusted) {
       checkedOrTrusted = _trustType(original, type);
     } else if (builder.options.assignmentCheckPolicy.isEmitted) {
-      checkedOrTrusted = _checkType(original, type, kind);
+      checkedOrTrusted = _checkType(original, type);
     }
     if (checkedOrTrusted == original) return original;
     builder.add(checkedOrTrusted);
@@ -273,6 +274,30 @@
     return result;
   }
 
+  HInstruction analyzeTypeArgumentNewRti(
+      DartType argument, MemberEntity sourceElement,
+      {SourceInformation sourceInformation}) {
+    argument = argument.unaliased;
+
+    if (argument.containsFreeTypeVariables) {
+      // TODO(sra): Locate type environment.
+      HInstruction environment =
+          builder.graph.addConstantString("env", _closedWorld);
+      // TODO(sra): Determine environment structure from context.
+      TypeEnvironmentStructure structure = null;
+      HInstruction rti = HTypeEval(environment, structure,
+          TypeExpressionRecipe(argument), _abstractValueDomain.dynamicType)
+        ..sourceInformation = sourceInformation;
+      builder.add(rti);
+      return rti;
+    }
+
+    HInstruction rti = HLoadType(argument, _abstractValueDomain.dynamicType)
+      ..sourceInformation = sourceInformation;
+    builder.add(rti);
+    return rti;
+  }
+
   /// Build a [HTypeConversion] for converting [original] to type [type].
   ///
   /// Invariant: [type] must be valid in the context.
@@ -280,6 +305,12 @@
   HInstruction buildTypeConversion(
       HInstruction original, DartType type, int kind,
       {SourceInformation sourceInformation}) {
+    if (builder.options.experimentNewRti) {
+      return buildAsCheck(original, type,
+          isTypeError: kind == HTypeConversion.TYPE_CHECK,
+          sourceInformation: sourceInformation);
+    }
+
     if (type == null) return original;
     type = type.unaliased;
     if (type.isInterfaceType && !type.treatAsRaw) {
@@ -312,4 +343,34 @@
         ..sourceInformation = sourceInformation;
     }
   }
+
+  /// Build a [HAsCheck] for converting [original] to type [type].
+  ///
+  /// Invariant: [type] must be valid in the context.
+  /// See [LocalsHandler.substInContext].
+  HInstruction buildAsCheck(HInstruction original, DartType type,
+      {bool isTypeError, SourceInformation sourceInformation}) {
+    if (type == null) return original;
+    type = type.unaliased;
+
+    if (type.isDynamic) return original;
+    if (type.isVoid) return original;
+    if (type == _closedWorld.commonElements.objectType) return original;
+
+    HInstruction reifiedType =
+        analyzeTypeArgumentNewRti(type, builder.sourceElement);
+    if (type is InterfaceType) {
+      // TODO(sra): Under NNDB opt-in, this will be NonNullable.
+      AbstractValue subtype =
+          _abstractValueDomain.createNullableSubtype(type.element);
+      return HAsCheck(original, reifiedType, isTypeError, subtype)
+        ..sourceInformation = sourceInformation;
+    } else {
+      // TypeMasks don't encode function types or FutureOr types or type
+      // variable types.
+      AbstractValue abstractValue = original.instructionType;
+      return HAsCheck(original, reifiedType, isTypeError, abstractValue)
+        ..sourceInformation = sourceInformation;
+    }
+  }
 }
diff --git a/pkg/dartfix/analysis_options.yaml b/pkg/dartfix/analysis_options.yaml
index 309f5ca..bc2e864 100644
--- a/pkg/dartfix/analysis_options.yaml
+++ b/pkg/dartfix/analysis_options.yaml
@@ -1,4 +1,4 @@
-include: package:pedantic/analysis_options.yaml
+include: package:pedantic/analysis_options.1.7.0.yaml
 
 linter:
   rules:
diff --git a/pkg/dartfix/lib/src/driver.dart b/pkg/dartfix/lib/src/driver.dart
index d999d83..1ae0b29 100644
--- a/pkg/dartfix/lib/src/driver.dart
+++ b/pkg/dartfix/lib/src/driver.dart
@@ -31,99 +31,68 @@
 
   Ansi get ansi => logger.ansi;
 
-  Future start(
-    List<String> args, {
-    Context testContext,
-    Logger testLogger,
-  }) async {
-    final Options options = Options.parse(args, testContext, testLogger);
-
-    force = options.force;
-    overwrite = options.overwrite;
-    targets = options.targets;
-    context = testContext ?? options.context;
-    logger = testLogger ?? options.logger;
-    server = new Server(listener: new _Listener(logger));
-    handler = new _Handler(this);
-
-    // Start showing progress before we start the analysis server.
-    Progress progress;
-    if (options.showHelp) {
-      progress = logger.progress('${ansi.emphasized('Listing fixes')}');
-    } else {
-      progress = logger.progress('${ansi.emphasized('Calculating fixes')}');
+  Future applyFixes() async {
+    showDescriptions('Recommended changes', result.suggestions);
+    showDescriptions('Recommended changes that cannot be automatically applied',
+        result.otherSuggestions);
+    showDetails(result.details);
+    if (result.suggestions.isEmpty) {
+      logger.stdout('');
+      logger.stdout(result.otherSuggestions.isNotEmpty
+          ? 'None of the recommended changes can be automatically applied.'
+          : 'No recommended changes.');
+      return;
     }
-
-    if (!await startServer(options)) {
-      context.exit(15);
+    logger.stdout('');
+    logger.stdout(ansi.emphasized('Files to be changed:'));
+    for (SourceFileEdit fileEdit in result.edits) {
+      logger.stdout('  ${_relativePath(fileEdit.file)}');
     }
-
-    if (!checkSupported(options)) {
-      await server.stop();
-      context.exit(1);
-    }
-
-    if (options.showHelp) {
-      try {
-        await showListOfFixes(progress: progress);
-      } finally {
-        await server.stop();
+    if (checkIfChangesShouldBeApplied(result)) {
+      for (SourceFileEdit fileEdit in result.edits) {
+        final file = new File(fileEdit.file);
+        String code = file.existsSync() ? file.readAsStringSync() : '';
+        code = SourceEdit.applySequence(code, fileEdit.edits);
+        await file.writeAsString(code);
       }
-      context.exit(0);
-    }
-
-    Future serverStopped;
-    try {
-      await setupFixesAnalysis(options);
-      result = await requestFixes(options, progress: progress);
-      serverStopped = server.stop();
-      await applyFixes();
-      await serverStopped;
-    } finally {
-      // If we didn't already try to stop the server, then stop it now.
-      if (serverStopped == null) {
-        await server.stop();
-      }
+      logger.stdout(ansi.emphasized('Changes applied.'));
     }
   }
 
-  Future<bool> startServer(Options options) async {
-    if (options.verbose) {
-      logger.trace('Dart SDK version ${Platform.version}');
-      logger.trace('  ${Platform.resolvedExecutable}');
-      logger.trace('dartfix');
-      logger.trace('  ${Platform.script.toFilePath()}');
+  bool checkIfChangesShouldBeApplied(EditDartfixResult result) {
+    logger.stdout('');
+    if (result.hasErrors) {
+      logger.stdout('WARNING: The analyzed source contains errors'
+          ' that may affect the accuracy of these changes.');
+      logger.stdout('');
+      if (!force) {
+        logger.stdout('Rerun with --$forceOption to apply these changes.');
+        return false;
+      }
+    } else if (!overwrite && !force) {
+      logger.stdout('Rerun with --$overwriteOption to apply these changes.');
+      return false;
     }
-    // Automatically run analysis server from source
-    // if this command line tool is being run from source within the SDK repo.
-    String serverPath = findServerPath();
-    await server.start(
-      clientId: 'dartfix',
-      clientVersion: 'unspecified',
-      sdkPath: options.sdkPath,
-      serverPath: serverPath,
-    );
-    server.listenToOutput(notificationProcessor: handler.handleEvent);
-    return handler.serverConnected(timeLimit: const Duration(seconds: 30));
+    return true;
   }
 
   /// Check if the specified options is supported by the version of analysis
   /// server being run and return `true` if they are.
   /// Display an error message and return `false` if not.
-  bool checkSupported(Options options) {
+  bool checkIfSelectedOptionsAreSupported(Options options) {
     if (handler.serverProtocolVersion.compareTo(new Version(1, 22, 2)) >= 0) {
       return true;
     }
     if (options.excludeFixes.isNotEmpty) {
-      unsupportedOption(excludeOption);
+      _unsupportedOption(excludeOption);
       return false;
     }
     if (options.includeFixes.isNotEmpty) {
-      unsupportedOption(includeOption);
+      _unsupportedOption(includeOption);
       return false;
     }
     if (options.requiredFixes) {
-      unsupportedOption(requiredOption);
+      _unsupportedOption(requiredOption);
       return false;
     }
     if (options.showHelp) {
@@ -132,30 +101,6 @@
     return true;
   }
 
-  void unsupportedOption(String option) {
-    final version = handler.serverProtocolVersion.toString();
-    logger.stderr('''
-The --$option option is not supported by analysis server version $version.
-Please upgrade to a newer version of the Dart SDK to use this option.''');
-  }
-
-  Future<Progress> setupFixesAnalysis(
-    Options options, {
-    Progress progress,
-  }) async {
-    logger.trace('');
-    logger.trace('Setup analysis');
-    await server.send(SERVER_REQUEST_SET_SUBSCRIPTIONS,
-        new ServerSetSubscriptionsParams([ServerService.STATUS]).toJson());
-    await server.send(
-        ANALYSIS_REQUEST_SET_ANALYSIS_ROOTS,
-        new AnalysisSetAnalysisRootsParams(
-          options.targets,
-          const [],
-        ).toJson());
-    return progress;
-  }
-
   Future<EditDartfixResult> requestFixes(
     Options options, {
     Progress progress,
@@ -187,33 +132,6 @@
     return EditDartfixResult.fromJson(decoder, 'result', json);
   }
 
-  Future applyFixes() async {
-    showDescriptions('Recommended changes', result.suggestions);
-    showDescriptions('Recommended changes that cannot be automatically applied',
-        result.otherSuggestions);
-    if (result.suggestions.isEmpty) {
-      logger.stdout('');
-      logger.stdout(result.otherSuggestions.isNotEmpty
-          ? 'None of the recommended changes can be automatically applied.'
-          : 'No recommended changes.');
-      return;
-    }
-    logger.stdout('');
-    logger.stdout(ansi.emphasized('Files to be changed:'));
-    for (SourceFileEdit fileEdit in result.edits) {
-      logger.stdout('  ${relativePath(fileEdit.file)}');
-    }
-    if (shouldApplyChanges(result)) {
-      for (SourceFileEdit fileEdit in result.edits) {
-        final file = new File(fileEdit.file);
-        String code = file.existsSync() ? file.readAsStringSync() : '';
-        code = SourceEdit.applySequence(code, fileEdit.edits);
-        await file.writeAsString(code);
-      }
-      logger.stdout(ansi.emphasized('Changes applied.'));
-    }
-  }
-
   void showDescriptions(String title, List<DartFixSuggestion> suggestions) {
     if (suggestions.isNotEmpty) {
       logger.stdout('');
@@ -225,7 +143,7 @@
         msg.write('  ${toSentenceFragment(suggestion.description)}');
         final loc = suggestion.location;
         if (loc != null) {
-          msg.write(' • ${relativePath(loc.file)}');
+          msg.write(' • ${_relativePath(loc.file)}');
           msg.write(' • ${loc.startLine}:${loc.startColumn}');
         }
         logger.stdout(msg.toString());
@@ -233,33 +151,33 @@
     }
   }
 
-  bool shouldApplyChanges(EditDartfixResult result) {
-    logger.stdout('');
-    if (result.hasErrors) {
-      logger.stdout('WARNING: The analyzed source contains errors'
-          ' that may affect the accuracy of these changes.');
-      logger.stdout('');
-      if (!force) {
-        logger.stdout('Rerun with --$forceOption to apply these changes.');
-        return false;
-      }
-    } else if (!overwrite && !force) {
-      logger.stdout('Rerun with --$overwriteOption to apply these changes.');
-      return false;
+  void showDetails(List<String> details) {
+    if (details == null || details.isEmpty) {
+      return;
     }
-    return true;
+    logger.stdout('''
+
+Analysis Details:
+''');
+    for (String detail in details) {
+      logger.stdout('''
+ • $detail
+''');
+    }
   }
 
-  String relativePath(String filePath) {
-    for (String target in targets) {
-      if (filePath.startsWith(target)) {
-        return filePath.substring(target.length + 1);
+  void showFix(DartFix fix) {
+    logger.stdout('''
+
+• ${fix.name}''');
+    if (fix.description != null) {
+      for (String line in _indentAndWrapDescription(fix.description)) {
+        logger.stdout(line);
       }
     }
-    return filePath;
   }
 
-  Future<EditGetDartfixInfoResult> showListOfFixes({Progress progress}) async {
+  Future<EditGetDartfixInfoResult> showFixes({Progress progress}) async {
     Map<String, dynamic> json = await server.send(
         EDIT_REQUEST_GET_DARTFIX_INFO, new EditGetDartfixInfoParams().toJson());
     progress?.finish(showTiming: true);
@@ -286,32 +204,120 @@
     return result;
   }
 
-  void showFix(DartFix fix) {
-    logger.stdout('''
+  Future start(
+    List<String> args, {
+    Context testContext,
+    Logger testLogger,
+  }) async {
+    final Options options = Options.parse(args, testContext, testLogger);
 
-* ${fix.name}''');
-    if (fix.description != null) {
-      for (String line in indentAndWrapDescription(fix.description)) {
-        logger.stdout(line);
+    force = options.force;
+    overwrite = options.overwrite;
+    targets = options.targets;
+    context = testContext ?? options.context;
+    logger = testLogger ?? options.logger;
+    server = new Server(listener: new _Listener(logger));
+    handler = new _Handler(this);
+
+    // Start showing progress before we start the analysis server.
+    Progress progress;
+    if (options.showHelp) {
+      progress = logger.progress('${ansi.emphasized('Listing fixes')}');
+    } else {
+      progress = logger.progress('${ansi.emphasized('Calculating fixes')}');
+    }
+
+    if (!await startServer(options)) {
+      context.exit(15);
+    }
+
+    if (!checkIfSelectedOptionsAreSupported(options)) {
+      await server.stop();
+      context.exit(1);
+    }
+
+    if (options.showHelp) {
+      try {
+        await showFixes(progress: progress);
+      } finally {
+        await server.stop();
+      }
+      context.exit(0);
+    }
+
+    Future serverStopped;
+    try {
+      await startServerAnalysis(options);
+      result = await requestFixes(options, progress: progress);
+      serverStopped = server.stop();
+      await applyFixes();
+      await serverStopped;
+    } finally {
+      // If we didn't already try to stop the server, then stop it now.
+      if (serverStopped == null) {
+        await server.stop();
       }
     }
   }
 
-  List<String> indentAndWrapDescription(String description) =>
-      description.split('\n').map((line) => '    $line').toList();
-}
-
-class _Listener with ServerListener, BadMessageListener {
-  final Logger logger;
-  final bool verbose;
-
-  _Listener(this.logger) : verbose = logger.isVerbose;
-
-  @override
-  void log(String prefix, String details) {
-    if (verbose) {
-      logger.trace('$prefix $details');
+  Future<bool> startServer(Options options) async {
+    // Automatically run analysis server from source
+    // if this command line tool is being run from source within the SDK repo.
+    String serverPath = options.serverSnapshot ?? findServerPath();
+    if (options.verbose) {
+      logger.trace('''
+Dart SDK version ${Platform.version}
+  ${Platform.resolvedExecutable}
+dartfix
+  ${Platform.script.toFilePath()}
+analysis server
+  $serverPath
+''');
     }
+    await server.start(
+      clientId: 'dartfix',
+      clientVersion: 'unspecified',
+      sdkPath: options.sdkPath,
+      serverPath: serverPath,
+    );
+    server.listenToOutput(notificationProcessor: handler.handleEvent);
+    return handler.serverConnected(timeLimit: const Duration(seconds: 30));
+  }
+
+  Future<Progress> startServerAnalysis(
+    Options options, {
+    Progress progress,
+  }) async {
+    logger.trace('');
+    logger.trace('Setup analysis');
+    await server.send(SERVER_REQUEST_SET_SUBSCRIPTIONS,
+        new ServerSetSubscriptionsParams([ServerService.STATUS]).toJson());
+    await server.send(
+        ANALYSIS_REQUEST_SET_ANALYSIS_ROOTS,
+        new AnalysisSetAnalysisRootsParams(
+          options.targets,
+          const [],
+        ).toJson());
+    return progress;
+  }
+
+  List<String> _indentAndWrapDescription(String description) =>
+      description.split('\n').map((line) => '    $line').toList();
+
+  String _relativePath(String filePath) {
+    for (String target in targets) {
+      if (filePath.startsWith(target)) {
+        return filePath.substring(target.length + 1);
+      }
+    }
+    return filePath;
+  }
+
+  void _unsupportedOption(String option) {
+    final version = handler.serverProtocolVersion.toString();
+    logger.stderr('''
+The --$option option is not supported by analysis server version $version.
+Please upgrade to a newer version of the Dart SDK to use this option.''');
   }
 }
 
@@ -333,6 +339,23 @@
   }
 
   @override
+  void onAnalysisErrors(AnalysisErrorsParams params) {
+    List<AnalysisError> errors = params.errors;
+    bool foundAtLeastOneError = false;
+    for (AnalysisError error in errors) {
+      if (shouldShowError(error)) {
+        if (!foundAtLeastOneError) {
+          foundAtLeastOneError = true;
+          logger.stdout('${driver._relativePath(params.file)}:');
+        }
+        Location loc = error.location;
+        logger.stdout('  ${toSentenceFragment(error.message)}'
+            ' • ${loc.startLine}:${loc.startColumn}');
+      }
+    }
+  }
+
+  @override
   void onFailedToConnect() {
     logger.stderr('Failed to connect to server');
   }
@@ -344,7 +367,7 @@
     final expectedVersion = Version.parse(PROTOCOL_VERSION);
     if (version > expectedVersion) {
       logger.stdout('''
-This version of dartfix is incompatible with the current Dart SDK. 
+This version of dartfix is incompatible with the current Dart SDK.
 Try installing a newer version of dartfix by running
 
     pub global activate dartfix
@@ -372,21 +395,18 @@
     }
     super.onServerError(params);
   }
+}
+
+class _Listener with ServerListener, BadMessageListener {
+  final Logger logger;
+  final bool verbose;
+
+  _Listener(this.logger) : verbose = logger.isVerbose;
 
   @override
-  void onAnalysisErrors(AnalysisErrorsParams params) {
-    List<AnalysisError> errors = params.errors;
-    bool foundAtLeastOneError = false;
-    for (AnalysisError error in errors) {
-      if (shouldShowError(error)) {
-        if (!foundAtLeastOneError) {
-          foundAtLeastOneError = true;
-          logger.stdout('${driver.relativePath(params.file)}:');
-        }
-        Location loc = error.location;
-        logger.stdout('  ${toSentenceFragment(error.message)}'
-            ' • ${loc.startLine}:${loc.startColumn}');
-      }
+  void log(String prefix, String details) {
+    if (verbose) {
+      logger.trace('$prefix $details');
     }
   }
 }
diff --git a/pkg/dartfix/lib/src/options.dart b/pkg/dartfix/lib/src/options.dart
index a4d8923..fc4e262 100644
--- a/pkg/dartfix/lib/src/options.dart
+++ b/pkg/dartfix/lib/src/options.dart
@@ -9,6 +9,20 @@
 import 'package:dartfix/src/context.dart';
 import 'package:path/path.dart' as path;
 
+const excludeOption = 'exclude';
+
+const forceOption = 'force';
+const includeOption = 'include';
+const overwriteOption = 'overwrite';
+const requiredOption = 'required';
+const _binaryName = 'dartfix';
+const _colorOption = 'color';
+const _serverSnapshot = 'server';
+
+// options only supported by server 1.22.2 and greater
+const _helpOption = 'help';
+const _verboseOption = 'verbose';
+
 /// Command line options for `dartfix`.
 class Options {
   final Context context;
@@ -16,6 +30,7 @@
 
   List<String> targets;
   final String sdkPath;
+  final String serverSnapshot;
 
   final bool requiredFixes;
   final List<String> includeFixes;
@@ -27,6 +42,30 @@
   final bool useColor;
   final bool verbose;
 
+  Options._fromArgs(this.context, ArgResults results)
+      : force = results[forceOption] as bool,
+        includeFixes =
+            (results[includeOption] as List ?? []).cast<String>().toList(),
+        excludeFixes =
+            (results[excludeOption] as List ?? []).cast<String>().toList(),
+        overwrite = results[overwriteOption] as bool,
+        requiredFixes = results[requiredOption] as bool,
+        sdkPath = _getSdkPath(),
+        serverSnapshot = results[_serverSnapshot],
+        showHelp = results[_helpOption] as bool || results.arguments.isEmpty,
+        targets = results.rest,
+        useColor = results.wasParsed(_colorOption)
+            ? results[_colorOption] as bool
+            : null,
+        verbose = results[_verboseOption] as bool;
+
+  String makeAbsoluteAndNormalize(String target) {
+    if (!path.isAbsolute(target)) {
+      target = path.join(context.workingDir, target);
+    }
+    return path.normalize(target);
+  }
+
   static Options parse(List<String> args, Context context, Logger logger) {
     final parser = new ArgParser(allowTrailingOptions: true)
       ..addSeparator('Choosing fixes to be applied:')
@@ -56,6 +95,8 @@
           help: 'Display this help message.',
           defaultsTo: false,
           negatable: false)
+      ..addOption(_serverSnapshot,
+          help: 'Path to the analysis server snapshot file.', valueHelp: 'path')
       ..addFlag(_verboseOption,
           abbr: 'v',
           defaultsTo: false,
@@ -73,7 +114,6 @@
     } on FormatException catch (e) {
       logger ??= new Logger.standard(ansi: new Ansi(Ansi.terminalSupportsAnsi));
       logger.stderr(e.message);
-      logger.stderr('\n');
       _showUsage(parser, logger);
       context.exit(15);
     }
@@ -142,29 +182,6 @@
     return options;
   }
 
-  Options._fromArgs(this.context, ArgResults results)
-      : force = results[forceOption] as bool,
-        includeFixes =
-            (results[includeOption] as List ?? []).cast<String>().toList(),
-        excludeFixes =
-            (results[excludeOption] as List ?? []).cast<String>().toList(),
-        overwrite = results[overwriteOption] as bool,
-        requiredFixes = results[requiredOption] as bool,
-        sdkPath = _getSdkPath(),
-        showHelp = results[_helpOption] as bool || results.arguments.isEmpty,
-        targets = results.rest,
-        useColor = results.wasParsed(_colorOption)
-            ? results[_colorOption] as bool
-            : null,
-        verbose = results[_verboseOption] as bool;
-
-  String makeAbsoluteAndNormalize(String target) {
-    if (!path.isAbsolute(target)) {
-      target = path.join(context.workingDir, target);
-    }
-    return path.normalize(target);
-  }
-
   static String _getSdkPath() {
     return Platform.environment['DART_SDK'] != null
         ? Platform.environment['DART_SDK']
@@ -181,20 +198,9 @@
     out(parser.usage);
     out(showHelpHint
         ? '''
+
 Use --$_helpOption to display the fixes that can be specified using either
 --$includeOption or --$excludeOption.'''
         : '');
   }
 }
-
-const _binaryName = 'dartfix';
-const _colorOption = 'color';
-const forceOption = 'force';
-const _helpOption = 'help';
-const overwriteOption = 'overwrite';
-const _verboseOption = 'verbose';
-
-// options only supported by server 1.22.2 and greater
-const excludeOption = 'exclude';
-const includeOption = 'include';
-const requiredOption = 'required';
diff --git a/pkg/dartfix/pubspec.yaml b/pkg/dartfix/pubspec.yaml
index 8f6b34b..06f97d6 100644
--- a/pkg/dartfix/pubspec.yaml
+++ b/pkg/dartfix/pubspec.yaml
@@ -6,7 +6,7 @@
   and fixing common issues.
 homepage: https://github.com/dart-lang/sdk/tree/master/pkg/dartfix
 environment:
-  sdk: '>=2.2.0 <3.0.0'
+  sdk: '>=2.3.0 <3.0.0'
 
 # Add the bin/dartfix.dart script to the scripts pub installs.
 executables:
@@ -23,5 +23,5 @@
 
 dev_dependencies:
   analyzer: ^0.33.0
-  pedantic: ^1.5.0
+  pedantic: ^1.7.0
   test: ^1.3.0
diff --git a/pkg/dartfix/test/src/driver_test.dart b/pkg/dartfix/test/src/driver_test.dart
index f84e20b..fc5bd29 100644
--- a/pkg/dartfix/test/src/driver_test.dart
+++ b/pkg/dartfix/test/src/driver_test.dart
@@ -27,11 +27,13 @@
   test('fix example', () async {
     final driver = new Driver();
     final testContext = new TestContext();
-    final testLogger = new TestLogger();
+    final testLogger = new TestLogger(debug: _debug);
     String exampleSource = await exampleFile.readAsString();
 
-    await driver.start([exampleDir.path],
-        testContext: testContext, testLogger: testLogger);
+    await driver.start([
+      if (_debug) '-v',
+      exampleDir.path,
+    ], testContext: testContext, testLogger: testLogger);
     if (_debug) {
       print(testLogger.stderrBuffer.toString());
       print(testLogger.stdoutBuffer.toString());
diff --git a/pkg/dartfix/test/src/options_test.dart b/pkg/dartfix/test/src/options_test.dart
index b0b74d7..c8c2713 100644
--- a/pkg/dartfix/test/src/options_test.dart
+++ b/pkg/dartfix/test/src/options_test.dart
@@ -30,6 +30,7 @@
     String normalOut,
     bool requiredFixes = false,
     bool overwrite = false,
+    String serverSnapshot,
     List<String> targetSuffixes,
     bool verbose = false,
   }) {
@@ -53,6 +54,7 @@
     expect(options.force, force);
     expect(options.requiredFixes, requiredFixes);
     expect(options.overwrite, overwrite);
+    expect(options.serverSnapshot, serverSnapshot);
     expect(options.showHelp, showHelp);
     expect(options.includeFixes, includeFixes);
     expect(options.excludeFixes, excludeFixes);
@@ -119,6 +121,10 @@
     parse(['--required', 'foo'], requiredFixes: true);
   });
 
+  test('server snapshot', () {
+    parse(['--server', 'some/path', 'foo'], serverSnapshot: 'some/path');
+  });
+
   test('simple', () {
     parse(['foo'], targetSuffixes: ['foo']);
   });
diff --git a/pkg/dev_compiler/analysis_options.yaml b/pkg/dev_compiler/analysis_options.yaml
index 007c3e7..249dc5d 100644
--- a/pkg/dev_compiler/analysis_options.yaml
+++ b/pkg/dev_compiler/analysis_options.yaml
@@ -16,12 +16,15 @@
     - annotate_overrides
     - avoid_empty_else
     - avoid_init_to_null
+    - avoid_relative_lib_imports
     - avoid_return_types_on_setters
     - avoid_shadowing_type_parameters
     - avoid_types_as_parameter_names
     - curly_braces_in_flow_control_structures
+    - empty_catches
     - empty_constructor_bodies
     - library_names
+    - library_prefixes
     - no_duplicate_case_values
     - null_closures
     - prefer_contains
@@ -30,6 +33,7 @@
     - prefer_is_not_empty
     - recursive_getters
     - type_init_formals
+    - unawaited_futures
     - unnecessary_null_in_if_null_operators
     - unrelated_type_equality_checks
     - use_rethrow_when_possible
diff --git a/pkg/dev_compiler/lib/src/analyzer/code_generator.dart b/pkg/dev_compiler/lib/src/analyzer/code_generator.dart
index 3ff97b0..3d4d114 100644
--- a/pkg/dev_compiler/lib/src/analyzer/code_generator.dart
+++ b/pkg/dev_compiler/lib/src/analyzer/code_generator.dart
@@ -29,12 +29,12 @@
 import 'package:path/path.dart' as path;
 import 'package:source_span/source_span.dart' show SourceLocation;
 
-import '../compiler/js_metalet.dart' as JS;
-import '../compiler/js_names.dart' as JS;
-import '../compiler/js_utils.dart' as JS;
+import '../compiler/js_metalet.dart' as js_ast;
+import '../compiler/js_names.dart' as js_ast;
+import '../compiler/js_utils.dart' as js_ast;
 import '../compiler/module_builder.dart' show pathToJSIdentifier;
 import '../compiler/shared_compiler.dart';
-import '../js_ast/js_ast.dart' as JS;
+import '../js_ast/js_ast.dart' as js_ast;
 import '../js_ast/js_ast.dart' show js;
 import '../js_ast/source_map_printer.dart' show NodeEnd, NodeSpan, HoverComment;
 import 'ast_builder.dart';
@@ -68,12 +68,12 @@
 // (which result in (JS.Statement).
 class CodeGenerator extends Object
     with
-        UIAsCodeVisitorMixin<JS.Node>,
+        UIAsCodeVisitorMixin<js_ast.Node>,
         NullableTypeInference,
         SharedCompiler<LibraryElement, ClassElement, InterfaceType,
             FunctionBody>
     implements
-        AstVisitor<JS.Node> {
+        AstVisitor<js_ast.Node> {
   final SummaryDataStore summaryData;
 
   final CompilerOptions options;
@@ -87,7 +87,7 @@
 
   /// The list of dart:_runtime SDK functions; these are assumed by other code
   /// in the SDK to be generated before anything else.
-  final _internalSdkFunctions = <JS.ModuleItem>[];
+  final _internalSdkFunctions = <js_ast.ModuleItem>[];
 
   /// Table of named and possibly hoisted types.
   TypeTable _typeTable;
@@ -105,9 +105,10 @@
   SimpleIdentifier _rethrowParameter;
 
   /// In an async* function, this represents the stream controller parameter.
-  JS.TemporaryId _asyncStarController;
+  js_ast.TemporaryId _asyncStarController;
 
-  final _initializingFormalTemps = HashMap<ParameterElement, JS.TemporaryId>();
+  final _initializingFormalTemps =
+      HashMap<ParameterElement, js_ast.TemporaryId>();
 
   /// The  type provider from the current Analysis [context].
   final TypeProvider types;
@@ -167,13 +168,13 @@
   /// mappings.
   Element _currentElement;
 
-  final _deferredProperties = HashMap<PropertyAccessorElement, JS.Method>();
+  final _deferredProperties = HashMap<PropertyAccessorElement, js_ast.Method>();
 
   String _libraryRoot;
 
   bool _superAllowed = true;
 
-  final _superHelpers = Map<String, JS.Method>();
+  final _superHelpers = Map<String, js_ast.Method>();
 
   /// Whether we are currently generating code for the body of a `JS()` call.
   bool _isInForeignJS = false;
@@ -194,7 +195,7 @@
   /// Tracks the temporary variable used to build collections containing
   /// control flow [IfElement] and [ForElement] nodes. Should be saved when
   /// visiting a new control flow tree and restored after.
-  JS.Expression _currentCollectionVariable;
+  js_ast.Expression _currentCollectionVariable;
 
   CodeGenerator(LinkedAnalysisDriver driver, this.types, this.summaryData,
       this.options, this._extensionTypes, this.errors)
@@ -261,7 +262,7 @@
   ///
   /// Takes the metadata for the build unit, as well as resolved trees and
   /// errors, and computes the output module code and optionally the source map.
-  JS.Program compile(List<CompilationUnit> compilationUnits) {
+  js_ast.Program compile(List<CompilationUnit> compilationUnits) {
     _libraryRoot = options.libraryRoot;
     if (!_libraryRoot.endsWith(path.separator)) {
       _libraryRoot += path.separator;
@@ -373,18 +374,18 @@
     return libraryJSName != null ? '$libraryJSName.$jsName' : jsName;
   }
 
-  JS.PropertyAccess _emitJSInterop(Element e) {
+  js_ast.PropertyAccess _emitJSInterop(Element e) {
     var jsName = _getJSNameWithoutGlobal(e);
     if (jsName == null) return null;
     return _emitJSInteropForGlobal(jsName);
   }
 
-  JS.PropertyAccess _emitJSInteropForGlobal(String name) {
+  js_ast.PropertyAccess _emitJSInteropForGlobal(String name) {
     var parts = name.split('.');
     if (parts.isEmpty) parts = [''];
-    JS.PropertyAccess access;
+    js_ast.PropertyAccess access;
     for (var part in parts) {
-      access = JS.PropertyAccess(
+      access = js_ast.PropertyAccess(
           access ?? runtimeCall('global'), js.escapedString(part, "'"));
     }
     return access;
@@ -474,7 +475,7 @@
     var source = library.source;
     // TODO(jmesserly): we need to split out HTML.
     if (source.uri.scheme == 'dart') {
-      return JS.dartSdkModule;
+      return js_ast.dartSdkModule;
     }
     var summaryPath = (source as InSummarySource).summaryPath;
     var moduleName = options.summaryModules[summaryPath];
@@ -507,7 +508,7 @@
     // only run this on the outermost function, and not any closures.
     inferNullableTypes(node);
 
-    moduleItems.add(node.accept(this) as JS.ModuleItem);
+    moduleItems.add(node.accept(this) as js_ast.ModuleItem);
 
     _currentElement = savedElement;
   }
@@ -566,7 +567,7 @@
       }
 
       inferNullableTypes(declaration);
-      var item = declaration.accept(this) as JS.ModuleItem;
+      var item = declaration.accept(this) as js_ast.ModuleItem;
       if (isInternalSdk && element is FunctionElement) {
         _internalSdkFunctions.add(item);
       } else {
@@ -709,7 +710,7 @@
     return _emitCast(to, jsFrom, implicit: isImplicit);
   }
 
-  JS.Expression _emitCast(DartType type, JS.Expression expr,
+  js_ast.Expression _emitCast(DartType type, js_ast.Expression expr,
       {bool implicit = true}) {
     // If [type] is a top type we can omit the cast.
     if (rules.isSubtypeOf(types.objectType, type)) {
@@ -720,15 +721,15 @@
   }
 
   @override
-  JS.Expression visitIsExpression(IsExpression node) {
+  js_ast.Expression visitIsExpression(IsExpression node) {
     return _emitIsExpression(
         node.expression, node.type.type, node.notOperator != null);
   }
 
-  JS.Expression _emitIsExpression(Expression operand, DartType type,
+  js_ast.Expression _emitIsExpression(Expression operand, DartType type,
       [bool negated = false]) {
     // Generate `is` as `dart.is` or `typeof` depending on the RHS type.
-    JS.Expression result;
+    js_ast.Expression result;
     var lhs = _visitExpression(operand);
     var typeofName = jsTypeRep.typeFor(type).primitiveTypeOf;
     // Inline primitives other than int (which requires a Math.floor check).
@@ -750,12 +751,12 @@
   visitGenericTypeAlias(GenericTypeAlias node) => null;
 
   @override
-  JS.Expression visitTypeName(node) => _emitTypeAnnotation(node);
+  js_ast.Expression visitTypeName(node) => _emitTypeAnnotation(node);
 
   @override
-  JS.Expression visitGenericFunctionType(node) => _emitTypeAnnotation(node);
+  js_ast.Expression visitGenericFunctionType(node) => _emitTypeAnnotation(node);
 
-  JS.Expression _emitTypeAnnotation(TypeAnnotation node) {
+  js_ast.Expression _emitTypeAnnotation(TypeAnnotation node) {
     var type = node.type;
     if (type == null) {
       // TODO(jmesserly): if the type fails to resolve, should we generate code
@@ -767,17 +768,16 @@
   }
 
   @override
-  JS.Statement visitClassTypeAlias(ClassTypeAlias node) {
-    return _emitClassDeclaration(
-        node, node.declaredElement as ClassElement, []);
+  js_ast.Statement visitClassTypeAlias(ClassTypeAlias node) {
+    return _emitClassDeclaration(node, node.declaredElement, []);
   }
 
   @override
-  JS.Statement visitClassDeclaration(ClassDeclaration node) {
+  js_ast.Statement visitClassDeclaration(ClassDeclaration node) {
     return _emitClassDeclaration(node, node.declaredElement, node.members);
   }
 
-  JS.Statement _emitClassDeclaration(Declaration classNode,
+  js_ast.Statement _emitClassDeclaration(Declaration classNode,
       ClassElement classElem, List<ClassMember> members) {
     // If this class is annotated with `@JS`, then there is nothing to emit.
     if (_hasJSInteropAnnotation(classElem)) return null;
@@ -790,8 +790,8 @@
     // https://github.com/dart-lang/sdk/issues/31003
     var className = classElem.typeParameters.isNotEmpty
         ? (classElem == _jsArray
-            ? JS.Identifier(classElem.name)
-            : JS.TemporaryId(classElem.name))
+            ? js_ast.Identifier(classElem.name)
+            : js_ast.TemporaryId(classElem.name))
         : _emitTopLevelName(classElem);
 
     var savedClassProperties = _classProperties;
@@ -818,9 +818,9 @@
     var jsMethods = _emitClassMethods(classElem, members);
     _emitSuperclassCovarianceChecks(classNode, jsMethods);
 
-    var body = <JS.Statement>[];
+    var body = <js_ast.Statement>[];
     _emitSuperHelperSymbols(body);
-    var deferredSupertypes = <JS.Statement>[];
+    var deferredSupertypes = <js_ast.Statement>[];
 
     // Emit the class, e.g. `core.Object = class Object { ... }`
     _defineClass(classElem, className, jsMethods, body, deferredSupertypes);
@@ -844,7 +844,7 @@
     }
     _emitClassMetadata(classNode.metadata, className, body);
 
-    var classDef = JS.Statement.from(body);
+    var classDef = js_ast.Statement.from(body);
     var typeFormals = classElem.typeParameters;
     if (typeFormals.isNotEmpty) {
       classDef = _defineClassTypeArguments(
@@ -861,12 +861,12 @@
     }
 
     _classProperties = savedClassProperties;
-    return JS.Statement.from(body);
+    return js_ast.Statement.from(body);
   }
 
-  JS.Statement _emitClassTypeTests(ClassElement classElem,
-      JS.Expression className, List<JS.Statement> body) {
-    JS.Expression getInterfaceSymbol(ClassElement c) {
+  js_ast.Statement _emitClassTypeTests(ClassElement classElem,
+      js_ast.Expression className, List<js_ast.Statement> body) {
+    js_ast.Expression getInterfaceSymbol(ClassElement c) {
       var library = c.library;
       if (library.isDartCore || library.isDartAsync) {
         switch (c.name) {
@@ -882,7 +882,7 @@
       return null;
     }
 
-    void markSubtypeOf(JS.Expression testSymbol) {
+    void markSubtypeOf(js_ast.Expression testSymbol) {
       body.add(js.statement('#.prototype[#] = true', [className, testSymbol]));
     }
 
@@ -1052,7 +1052,7 @@
     if (isClassSymbol == null) {
       // TODO(jmesserly): we could export these symbols, if we want to mark
       // implemented interfaces for user-defined classes.
-      var id = JS.TemporaryId("_is_${classElem.name}_default");
+      var id = js_ast.TemporaryId("_is_${classElem.name}_default");
       moduleItems.add(
           js.statement('const # = Symbol(#);', [id, js.string(id.name, "'")]));
       isClassSymbol = id;
@@ -1070,53 +1070,55 @@
     return runtimeStatement('addTypeTests(#, #)', [defaultInst, isClassSymbol]);
   }
 
-  void _emitSymbols(Iterable<JS.TemporaryId> vars, List<JS.ModuleItem> body) {
+  void _emitSymbols(
+      Iterable<js_ast.TemporaryId> vars, List<js_ast.ModuleItem> body) {
     for (var id in vars) {
       body.add(js.statement('const # = Symbol(#)', [id, js.string(id.name)]));
     }
   }
 
-  void _emitSuperHelperSymbols(List<JS.Statement> body) {
+  void _emitSuperHelperSymbols(List<js_ast.Statement> body) {
     _emitSymbols(
-        _superHelpers.values.map((m) => m.name as JS.TemporaryId), body);
+        _superHelpers.values.map((m) => m.name as js_ast.TemporaryId), body);
     _superHelpers.clear();
   }
 
   void _emitVirtualFieldSymbols(
-      ClassElement classElement, List<JS.Statement> body) {
+      ClassElement classElement, List<js_ast.Statement> body) {
     _classProperties.virtualFields.forEach((field, virtualField) {
       body.add(js.statement('const # = Symbol(#);',
           [virtualField, js.string('${classElement.name}.${field.name}')]));
     });
   }
 
-  List<JS.Identifier> _emitTypeFormals(List<TypeParameterElement> typeFormals) {
+  List<js_ast.Identifier> _emitTypeFormals(
+      List<TypeParameterElement> typeFormals) {
     return typeFormals
-        .map((t) => JS.Identifier(t.name))
+        .map((t) => js_ast.Identifier(t.name))
         .toList(growable: false);
   }
 
   @override
-  JS.Statement visitEnumDeclaration(EnumDeclaration node) {
+  js_ast.Statement visitEnumDeclaration(EnumDeclaration node) {
     return _emitClassDeclaration(node, node.declaredElement, []);
   }
 
   @override
-  JS.Statement visitMixinDeclaration(MixinDeclaration node) {
+  js_ast.Statement visitMixinDeclaration(MixinDeclaration node) {
     return _emitClassDeclaration(node, node.declaredElement, node.members);
   }
 
   /// Wraps a possibly generic class in its type arguments.
-  JS.Statement _defineClassTypeArguments(TypeDefiningElement element,
-      List<TypeParameterElement> formals, JS.Statement body,
-      [JS.Expression className, List<JS.Statement> deferredBaseClass]) {
+  js_ast.Statement _defineClassTypeArguments(TypeDefiningElement element,
+      List<TypeParameterElement> formals, js_ast.Statement body,
+      [js_ast.Expression className, List<js_ast.Statement> deferredBaseClass]) {
     assert(formals.isNotEmpty);
     var jsFormals = _emitTypeFormals(formals);
     var typeConstructor = js.call('(#) => { #; #; return #; }', [
       jsFormals,
       _typeTable.discharge(formals),
       body,
-      className ?? JS.Identifier(element.name)
+      className ?? js_ast.Identifier(element.name)
     ]);
 
     var genericArgs = [typeConstructor];
@@ -1131,17 +1133,18 @@
         [genericName, genericCall, _emitTopLevelName(element), genericName]);
   }
 
-  JS.Statement _emitClassStatement(
+  js_ast.Statement _emitClassStatement(
       ClassElement classElem,
-      JS.Expression className,
-      JS.Expression heritage,
-      List<JS.Method> methods) {
+      js_ast.Expression className,
+      js_ast.Expression heritage,
+      List<js_ast.Method> methods) {
     if (classElem.typeParameters.isNotEmpty) {
-      return JS.ClassExpression(className as JS.Identifier, heritage, methods)
+      return js_ast.ClassExpression(
+              className as js_ast.Identifier, heritage, methods)
           .toStatement();
     }
-    var classExpr =
-        JS.ClassExpression(JS.TemporaryId(classElem.name), heritage, methods);
+    var classExpr = js_ast.ClassExpression(
+        js_ast.TemporaryId(classElem.name), heritage, methods);
     return js.statement('# = #;', [className, classExpr]);
   }
 
@@ -1174,10 +1177,10 @@
   /// minimal compiler and runtime changes.
   void _emitMixinStatement(
       ClassElement classElem,
-      JS.Expression className,
-      JS.Expression heritage,
-      List<JS.Method> methods,
-      List<JS.Statement> body) {
+      js_ast.Expression className,
+      js_ast.Expression heritage,
+      List<js_ast.Method> methods,
+      List<js_ast.Statement> body) {
     assert(classElem.isMixin);
 
     var staticMethods = methods.where((m) => m.isStatic).toList();
@@ -1185,42 +1188,43 @@
     body.add(
         _emitClassStatement(classElem, className, heritage, staticMethods));
 
-    var superclassId = JS.TemporaryId(
+    var superclassId = js_ast.TemporaryId(
         classElem.superclassConstraints.map((t) => t.name).join('_'));
-    var classId =
-        className is JS.Identifier ? className : JS.TemporaryId(classElem.name);
+    var classId = className is js_ast.Identifier
+        ? className
+        : js_ast.TemporaryId(classElem.name);
 
     var mixinMemberClass =
-        JS.ClassExpression(classId, superclassId, instanceMethods);
+        js_ast.ClassExpression(classId, superclassId, instanceMethods);
 
-    JS.Node arrowFnBody = mixinMemberClass;
-    var extensionInit = <JS.Statement>[];
+    js_ast.Node arrowFnBody = mixinMemberClass;
+    var extensionInit = <js_ast.Statement>[];
     _defineExtensionMembers(classId, extensionInit);
     if (extensionInit.isNotEmpty) {
       extensionInit.insert(0, mixinMemberClass.toStatement());
       extensionInit.add(classId.toReturn());
-      arrowFnBody = JS.Block(extensionInit);
+      arrowFnBody = js_ast.Block(extensionInit);
     }
 
     body.add(js.statement('#[#.mixinOn] = #', [
       className,
       runtimeModule,
-      JS.ArrowFun([superclassId], arrowFnBody)
+      js_ast.ArrowFun([superclassId], arrowFnBody)
     ]));
   }
 
   void _defineClass(
       ClassElement classElem,
-      JS.Expression className,
-      List<JS.Method> methods,
-      List<JS.Statement> body,
-      List<JS.Statement> deferredSupertypes) {
+      js_ast.Expression className,
+      List<js_ast.Method> methods,
+      List<js_ast.Statement> body,
+      List<js_ast.Statement> deferredSupertypes) {
     if (classElem.type.isObject) {
       body.add(_emitClassStatement(classElem, className, null, methods));
       return;
     }
 
-    JS.Expression emitDeferredType(DartType t) {
+    js_ast.Expression emitDeferredType(DartType t) {
       if (t is InterfaceType && t.typeArguments.isNotEmpty) {
         _declareBeforeUse(t.element);
         return _emitGenericClassType(
@@ -1267,9 +1271,10 @@
     var supertype = classElem.isMixin ? types.objectType : classElem.supertype;
     var hasUnnamedSuper = _hasUnnamedInheritedConstructor(supertype.element);
 
-    void emitMixinConstructors(JS.Expression className, [InterfaceType mixin]) {
+    void emitMixinConstructors(js_ast.Expression className,
+        [InterfaceType mixin]) {
       var supertype = classElem.supertype;
-      JS.Statement mixinCtor;
+      js_ast.Statement mixinCtor;
       if (mixin != null && _hasUnnamedConstructor(mixin.element)) {
         mixinCtor = js.statement('#.#.call(this);', [
           emitClassRef(mixin),
@@ -1281,14 +1286,14 @@
 
       for (var ctor in supertype.constructors) {
         var jsParams = _emitParametersForElement(ctor);
-        var ctorBody = <JS.Statement>[];
+        var ctorBody = <js_ast.Statement>[];
         if (mixinCtor != null) ctorBody.add(mixinCtor);
         if (ctor.name != '' || hasUnnamedSuper) {
           ctorBody
               .add(_emitSuperConstructorCall(className, ctor.name, jsParams));
         }
         body.add(_addConstructorToClass(classElem, className, ctor.name,
-            JS.Fun(jsParams, JS.Block(ctorBody))));
+            js_ast.Fun(jsParams, js_ast.Block(ctorBody))));
       }
     }
 
@@ -1334,8 +1339,8 @@
         //     applyMixin(C, class C$ extends M { <methods>  });
         mixinBody.add(runtimeStatement('applyMixin(#, #)', [
           classExpr,
-          JS.ClassExpression(
-              JS.TemporaryId(classElem.name), mixinClass, methods)
+          js_ast.ClassExpression(
+              js_ast.TemporaryId(classElem.name), mixinClass, methods)
         ]));
       }
 
@@ -1347,10 +1352,10 @@
       var m = classElem.mixins[i];
 
       var mixinString = classElem.supertype.name + '_' + m.name;
-      var mixinClassName = JS.TemporaryId(mixinString);
-      var mixinId = JS.TemporaryId(mixinString + '\$');
+      var mixinClassName = js_ast.TemporaryId(mixinString);
+      var mixinId = js_ast.TemporaryId(mixinString + '\$');
       var mixinClassExpression =
-          JS.ClassExpression(mixinClassName, baseClass, []);
+          js_ast.ClassExpression(mixinClassName, baseClass, []);
       // Bind the mixin class to a name to workaround a V8 bug with es6 classes
       // and anonymous function names.
       // TODO(leafp:) Eliminate this once the bug is fixed:
@@ -1392,28 +1397,28 @@
   /// field.  Note that the Dart names are always symbolized to avoid
   /// conflicts.  They will be installed as extension methods on the underlying
   /// native type.
-  List<JS.Method> _emitNativeFieldAccessors(FieldDeclaration node) {
+  List<js_ast.Method> _emitNativeFieldAccessors(FieldDeclaration node) {
     // TODO(vsm): Can this by meta-programmed?
     // E.g., dart.nativeField(symbol, jsName)
     // Alternatively, perhaps it could be meta-programmed directly in
     // dart.registerExtensions?
-    var jsMethods = <JS.Method>[];
+    var jsMethods = <js_ast.Method>[];
     if (!node.isStatic) {
       for (var decl in node.fields.variables) {
         var field = decl.declaredElement as FieldElement;
         var name = getAnnotationName(field, isJSName) ?? field.name;
         // Generate getter
-        var fn = JS.Fun([], js.block('{ return this.#; }', [name]));
+        var fn = js_ast.Fun([], js.block('{ return this.#; }', [name]));
         var method =
-            JS.Method(_declareMemberName(field.getter), fn, isGetter: true);
+            js_ast.Method(_declareMemberName(field.getter), fn, isGetter: true);
         jsMethods.add(method);
 
         // Generate setter
         if (!decl.isFinal) {
-          var value = JS.TemporaryId('value');
-          fn = JS.Fun([value], js.block('{ this.# = #; }', [name, value]));
-          method =
-              JS.Method(_declareMemberName(field.setter), fn, isSetter: true);
+          var value = js_ast.TemporaryId('value');
+          fn = js_ast.Fun([value], js.block('{ this.# = #; }', [name, value]));
+          method = js_ast.Method(_declareMemberName(field.setter), fn,
+              isSetter: true);
           jsMethods.add(method);
         }
       }
@@ -1421,20 +1426,20 @@
     return jsMethods;
   }
 
-  List<JS.Method> _emitClassMethods(
+  List<js_ast.Method> _emitClassMethods(
       ClassElement classElem, List<ClassMember> memberNodes) {
     var type = classElem.type;
     var virtualFields = _classProperties.virtualFields;
 
-    var jsMethods = <JS.Method>[];
+    var jsMethods = <js_ast.Method>[];
     bool hasJsPeer = _extensionTypes.isNativeClass(classElem);
     bool hasIterator = false;
 
     if (type.isObject) {
       // Dart does not use ES6 constructors.
       // Add an error to catch any invalid usage.
-      jsMethods
-          .add(JS.Method(propertyName('constructor'), js.fun(r'''function() {
+      jsMethods.add(
+          js_ast.Method(propertyName('constructor'), js.fun(r'''function() {
                   throw Error("use `new " + #.typeName(#.getReifiedType(this)) +
                       ".new(...)` to create a Dart object");
               }''', [runtimeModule, runtimeModule])));
@@ -1445,20 +1450,20 @@
       //
       // This will become obsolete when
       // https://github.com/dart-lang/sdk/issues/31003 is addressed.
-      jsMethods.add(JS.Method(
+      jsMethods.add(js_ast.Method(
           propertyName('constructor'), js.fun(r'function() { return []; }')));
     } else if (classElem.isEnum) {
       // Generate Enum.toString()
       var fields = classElem.fields.where((f) => f.type == type).toList();
-      var mapMap = List<JS.Property>(fields.length);
+      var mapMap = List<js_ast.Property>(fields.length);
       for (var i = 0; i < fields.length; ++i) {
-        mapMap[i] = JS.Property(
+        mapMap[i] = js_ast.Property(
             js.number(i), js.string('${type.name}.${fields[i].name}'));
       }
-      jsMethods.add(JS.Method(
+      jsMethods.add(js_ast.Method(
           _declareMemberName(types.objectType.getMethod('toString')),
           js.fun('function() { return #[this.index]; }',
-              JS.ObjectInitializer(mapMap, multiline: true))));
+              js_ast.ObjectInitializer(mapMap, multiline: true))));
     }
 
     for (var m in memberNodes) {
@@ -1515,7 +1520,7 @@
   }
 
   void _emitSuperclassCovarianceChecks(
-      Declaration node, List<JS.Method> methods) {
+      Declaration node, List<js_ast.Method> methods) {
     var covariantParams = getSuperclassCovariantParameters(node);
     if (covariantParams == null) return;
 
@@ -1526,22 +1531,22 @@
       if (member is PropertyAccessorElement) {
         var param =
             covariantParams.lookup(member.parameters[0]) as ParameterElement;
-        methods.add(JS.Method(
+        methods.add(js_ast.Method(
             name,
             js.fun('function(x) { return super.# = #; }',
-                [name, _emitCast(param.type, JS.Identifier('x'))]),
+                [name, _emitCast(param.type, js_ast.Identifier('x'))]),
             isSetter: true));
-        methods.add(JS.Method(
+        methods.add(js_ast.Method(
             name, js.fun('function() { return super.#; }', [name]),
             isGetter: true));
       } else if (member is MethodElement) {
         var type = member.type;
 
-        var body = <JS.Statement>[];
+        var body = <js_ast.Statement>[];
         _emitCovarianceBoundsCheck(type.typeFormals, covariantParams, body);
 
         var typeFormals = _emitTypeFormals(type.typeFormals);
-        var jsParams = List<JS.Parameter>.from(typeFormals);
+        var jsParams = List<js_ast.Parameter>.from(typeFormals);
         bool foundNamedParams = false;
         for (var param in member.parameters) {
           param = covariantParams.lookup(param) as ParameterElement;
@@ -1554,7 +1559,8 @@
             body.add(js.statement('if (# in #) #;', [
               name,
               namedArgumentTemp,
-              _emitCast(param.type, JS.PropertyAccess(namedArgumentTemp, name))
+              _emitCast(
+                  param.type, js_ast.PropertyAccess(namedArgumentTemp, name))
             ]));
           } else {
             var jsParam = _emitParameter(param);
@@ -1579,8 +1585,8 @@
           body.add(js.statement(
               'return super.#(#)(#);', [name, typeFormals, jsParams]));
         }
-        var fn = JS.Fun(jsParams, JS.Block(body));
-        methods.add(JS.Method(name, fn));
+        var fn = js_ast.Fun(jsParams, js_ast.Block(body));
+        methods.add(js_ast.Method(name, fn));
       } else {
         throw StateError(
             'unable to generate a covariant check for element: `$member` '
@@ -1590,12 +1596,12 @@
   }
 
   /// Emits a Dart factory constructor to a JS static method.
-  JS.Method _emitFactoryConstructor(ConstructorDeclaration node) {
+  js_ast.Method _emitFactoryConstructor(ConstructorDeclaration node) {
     if (isUnsupportedFactoryConstructor(node)) return null;
 
     var element = node.declaredElement;
     var name = _constructorName(element.name);
-    JS.Fun fun;
+    js_ast.Fun fun;
 
     var savedFunction = _currentFunction;
     _currentFunction = node.body;
@@ -1610,27 +1616,27 @@
       // rest/spread support, but this should work for now.
       var params = _emitParameters(node.parameters?.parameters);
 
-      fun = JS.Fun(
+      fun = js_ast.Fun(
           params,
-          JS.Block([
+          js_ast.Block([
             js.statement('return $newKeyword #(#)',
                 [visitConstructorName(redirect), params])
               ..sourceInformation = _nodeStart(redirect)
           ]));
     } else {
       // Normal factory constructor
-      var body = <JS.Statement>[];
+      var body = <js_ast.Statement>[];
       var init = _emitArgumentInitializers(element, node.parameters);
       if (init != null) body.add(init);
       body.add(_visitStatement(node.body));
 
       var params = _emitParameters(node.parameters?.parameters);
-      fun = JS.Fun(params, JS.Block(body));
+      fun = js_ast.Fun(params, js_ast.Block(body));
     }
 
     _currentFunction = savedFunction;
 
-    return JS.Method(name, fun, isStatic: true)
+    return js_ast.Method(name, fun, isStatic: true)
       ..sourceInformation = _functionEnd(node);
   }
 
@@ -1653,14 +1659,15 @@
   ///       return core.bool.as(this.noSuchMethod(
   ///           new dart.InvocationImpl.new('eatFood', [food])));
   ///     }
-  JS.Method _implementMockMember(ExecutableElement method, InterfaceType type) {
-    var invocationProps = <JS.Property>[];
-    addProperty(String name, JS.Expression value) {
-      invocationProps.add(JS.Property(js.string(name), value));
+  js_ast.Method _implementMockMember(
+      ExecutableElement method, InterfaceType type) {
+    var invocationProps = <js_ast.Property>[];
+    addProperty(String name, js_ast.Expression value) {
+      invocationProps.add(js_ast.Property(js.string(name), value));
     }
 
     var typeParams = _emitTypeFormals(method.type.typeFormals);
-    var fnArgs = List<JS.Parameter>.from(typeParams);
+    var fnArgs = List<js_ast.Parameter>.from(typeParams);
     var args = _emitParametersForElement(method);
     fnArgs.addAll(args);
     var argInit = _emitArgumentInitializers(method);
@@ -1682,12 +1689,12 @@
       // Sort the names to match dart2js order.
       var sortedNames = (namedParameterTypes.keys.toList())..sort();
       var named = sortedNames
-          .map((n) => JS.Property(propertyName(n), JS.Identifier(n)));
-      addProperty('namedArguments', JS.ObjectInitializer(named.toList()));
+          .map((n) => js_ast.Property(propertyName(n), js_ast.Identifier(n)));
+      addProperty('namedArguments', js_ast.ObjectInitializer(named.toList()));
       positionalArgs.removeLast();
     }
     if (typeParams.isNotEmpty) {
-      addProperty('typeArguments', JS.ArrayInitializer(typeParams));
+      addProperty('typeArguments', js_ast.ArrayInitializer(typeParams));
     }
 
     var fnBody =
@@ -1695,19 +1702,19 @@
       runtimeModule,
       _declareMemberName(method),
       args,
-      JS.ObjectInitializer(invocationProps)
+      js_ast.ObjectInitializer(invocationProps)
     ]);
 
     fnBody = _emitCast(method.returnType, fnBody);
 
     var fnBlock = argInit != null
-        ? JS.Block([argInit, fnBody.toReturn()])
+        ? js_ast.Block([argInit, fnBody.toReturn()])
         : fnBody.toReturn().toBlock();
 
-    return JS.Method(
+    return js_ast.Method(
         _declareMemberName(method,
             useExtension: _extensionTypes.isNativeClass(type.element)),
-        JS.Fun(fnArgs, fnBlock),
+        js_ast.Fun(fnArgs, fnBlock),
         isGetter: method is PropertyAccessorElement && method.isGetter,
         isSetter: method is PropertyAccessorElement && method.isSetter,
         isStatic: false);
@@ -1720,23 +1727,25 @@
   /// would end up calling the getter or setter, and one of those might not even
   /// exist, resulting in a runtime error. Even if they did exist, that's the
   /// wrong behavior if a new field was declared.
-  List<JS.Method> _emitVirtualFieldAccessor(VariableDeclaration field) {
+  List<js_ast.Method> _emitVirtualFieldAccessor(VariableDeclaration field) {
     var element = field.declaredElement as FieldElement;
     var virtualField = _classProperties.virtualFields[element];
-    var result = <JS.Method>[];
+    var result = <js_ast.Method>[];
     var name = _declareMemberName(element.getter);
 
     var mocks = _classProperties.mockMembers;
     if (!mocks.containsKey(element.name)) {
       var getter = js.fun('function() { return this[#]; }', [virtualField]);
-      result.add(JS.Method(name, getter, isGetter: true)
+      result.add(js_ast.Method(name, getter, isGetter: true)
         ..sourceInformation = _functionSpan(field.name));
     }
 
     if (!mocks.containsKey(element.name + '=')) {
-      var args = field.isFinal ? [JS.Super(), name] : [JS.This(), virtualField];
+      var args = field.isFinal
+          ? [js_ast.Super(), name]
+          : [js_ast.This(), virtualField];
 
-      JS.Expression value = JS.Identifier('value');
+      js_ast.Expression value = js_ast.Identifier('value');
 
       var setter = element.setter;
       if (setter != null) {
@@ -1749,7 +1758,8 @@
       }
       args.add(value);
 
-      result.add(JS.Method(name, js.fun('function(value) { #[#] = #; }', args),
+      result.add(js_ast.Method(
+          name, js.fun('function(value) { #[#] = #; }', args),
           isSetter: true)
         ..sourceInformation = _functionSpan(field.name));
     }
@@ -1761,7 +1771,7 @@
   /// setter. This is needed because in ES6, if you only override a getter
   /// (alternatively, a setter), then there is an implicit override of the
   /// setter (alternatively, the getter) that does nothing.
-  JS.Method _emitSuperAccessorWrapper(
+  js_ast.Method _emitSuperAccessorWrapper(
       MethodDeclaration member, InterfaceType type) {
     var accessorElement = member.declaredElement as PropertyAccessorElement;
     var field = accessorElement.variable;
@@ -1775,7 +1785,7 @@
           _classProperties.inheritedSetters.contains(field.name)) {
         // Generate a setter that forwards to super.
         var fn = js.fun('function(value) { super[#] = value; }', [name]);
-        return JS.Method(name, fn, isSetter: true);
+        return js_ast.Method(name, fn, isSetter: true);
       }
     } else {
       var getter = field.getter;
@@ -1783,7 +1793,7 @@
           _classProperties.inheritedGetters.contains(field.name)) {
         // Generate a getter that forwards to super.
         var fn = js.fun('function() { return super[#]; }', [name]);
-        return JS.Method(name, fn, isGetter: true);
+        return js_ast.Method(name, fn, isGetter: true);
       }
     }
     return null;
@@ -1800,7 +1810,7 @@
   /// This will return `null` if the adapter was already added on a super type,
   /// otherwise it returns the adapter code.
   // TODO(jmesserly): should we adapt `Iterator` too?
-  JS.Method _emitIterable(InterfaceType t) {
+  js_ast.Method _emitIterable(InterfaceType t) {
     // If a parent had an `iterator` (concrete or abstract) or implements
     // Iterable, we know the adapter is already there, so we can skip it as a
     // simple code size optimization.
@@ -1816,13 +1826,15 @@
 
     // Otherwise, emit the adapter method, which wraps the Dart iterator in
     // an ES6 iterator.
-    return JS.Method(
+    return js_ast.Method(
         js.call('Symbol.iterator'),
-        js.call('function() { return new #.JsIterator(this.#); }',
-            [runtimeModule, _emitMemberName('iterator', type: t)]) as JS.Fun);
+        js.call('function() { return new #.JsIterator(this.#); }', [
+          runtimeModule,
+          _emitMemberName('iterator', type: t)
+        ]) as js_ast.Fun);
   }
 
-  JS.Expression _instantiateAnnotation(Annotation node) {
+  js_ast.Expression _instantiateAnnotation(Annotation node) {
     var element = node.element;
     if (element is ConstructorElement) {
       return _emitInstanceCreationExpression(
@@ -1836,7 +1848,7 @@
   }
 
   void _registerExtensionType(
-      ClassElement classElem, String jsPeerName, List<JS.Statement> body) {
+      ClassElement classElem, String jsPeerName, List<js_ast.Statement> body) {
     var className = _emitTopLevelName(classElem);
     if (jsTypeRep.isPrimitive(classElem.type)) {
       body.add(runtimeStatement(
@@ -1847,18 +1859,18 @@
   }
 
   /// Defines all constructors for this class as ES5 constructors.
-  List<JS.Statement> _defineConstructors(
+  List<js_ast.Statement> _defineConstructors(
       ClassElement classElem,
-      JS.Expression className,
+      js_ast.Expression className,
       Map<Element, Declaration> memberMap,
       Declaration classNode) {
-    var body = <JS.Statement>[];
+    var body = <js_ast.Statement>[];
     if (classElem.isMixinApplication) {
       // We already handled this when we defined the class.
       return body;
     }
 
-    addConstructor(String name, JS.Expression jsCtor) {
+    addConstructor(String name, js_ast.Expression jsCtor) {
       body.add(_addConstructorToClass(classElem, className, name, jsCtor));
     }
 
@@ -1878,12 +1890,12 @@
       assert(classElem.constructors.length == 1,
           'default constructor only if no other constructors');
       var superCall = _emitSuperConstructorCallIfNeeded(classElem, className);
-      var ctorBody = <JS.Statement>[_initializeFields(fields)];
+      var ctorBody = <js_ast.Statement>[_initializeFields(fields)];
       if (superCall != null) ctorBody.add(superCall);
 
       addConstructor(
           '',
-          JS.Fun([], JS.Block(ctorBody))
+          js_ast.Fun([], js_ast.Block(ctorBody))
             ..sourceInformation = _functionEnd(classNode));
       return body;
     }
@@ -1923,8 +1935,8 @@
             c.isSynthetic && c.name != '' || c.isFactory || c.isExternal);
   }
 
-  JS.Statement _addConstructorToClass(ClassElement c, JS.Expression className,
-      String name, JS.Expression jsCtor) {
+  js_ast.Statement _addConstructorToClass(ClassElement c,
+      js_ast.Expression className, String name, js_ast.Expression jsCtor) {
     jsCtor = defineValueOnClass(c, className, _constructorName(name), jsCtor);
     return js.statement('#.prototype = #.prototype;', [jsCtor, className]);
   }
@@ -1951,22 +1963,22 @@
   /// Emits static fields for a class, and initialize them eagerly if possible,
   /// otherwise define them as lazy properties.
   void _emitStaticFields(ClassElement classElem,
-      Map<Element, Declaration> members, List<JS.Statement> body) {
+      Map<Element, Declaration> members, List<js_ast.Statement> body) {
     if (classElem.isEnum) {
       // Emit enum static fields
       var type = classElem.type;
-      void addField(FieldElement e, JS.Expression value) {
+      void addField(FieldElement e, js_ast.Expression value) {
         body.add(defineValueOnClass(classElem, _emitStaticClassName(e),
                 _declareMemberName(e.getter), value)
             .toStatement());
       }
 
       int index = 0;
-      var values = <JS.Expression>[];
+      var values = <js_ast.Expression>[];
       for (var f in classElem.fields) {
         if (f.type != type) continue;
         // static const E id_i = const E(i);
-        values.add(JS.PropertyAccess(
+        values.add(js_ast.PropertyAccess(
             _emitStaticClassName(f), _declareMemberName(f.getter)));
         var enumValue = runtimeCall('const(new (#.#)(#))', [
           emitConstructorAccess(type),
@@ -1993,8 +2005,8 @@
     }
   }
 
-  void _emitClassMetadata(List<Annotation> metadata, JS.Expression className,
-      List<JS.Statement> body) {
+  void _emitClassMetadata(List<Annotation> metadata,
+      js_ast.Expression className, List<js_ast.Statement> body) {
     // Metadata
     if (options.emitMetadata && metadata.isNotEmpty) {
       body.add(js.statement('#[#.metadata] = () => [#];',
@@ -2019,18 +2031,18 @@
   /// If a concrete class implements one of our extensions, we might need to
   /// add forwarders.
   void _defineExtensionMembers(
-      JS.Expression className, List<JS.Statement> body) {
+      js_ast.Expression className, List<js_ast.Statement> body) {
     void emitExtensions(String helperName, Iterable<String> extensions) {
       if (extensions.isEmpty) return;
 
       var names = extensions
-          .map((e) => propertyName(JS.memberNameForDartMember(e)))
+          .map((e) => propertyName(js_ast.memberNameForDartMember(e)))
           .toList();
       body.add(js.statement('#.#(#, #);', [
         runtimeModule,
         helperName,
         className,
-        JS.ArrayInitializer(names, multiline: names.length > 4)
+        js_ast.ArrayInitializer(names, multiline: names.length > 4)
       ]));
     }
 
@@ -2040,8 +2052,8 @@
   }
 
   /// Emit the signature on the class recording the runtime type information
-  void _emitClassSignature(ClassElement classElem, JS.Expression className,
-      Map<Element, Declaration> annotatedMembers, List<JS.Statement> body) {
+  void _emitClassSignature(ClassElement classElem, js_ast.Expression className,
+      Map<Element, Declaration> annotatedMembers, List<js_ast.Statement> body) {
     if (classElem.interfaces.isNotEmpty ||
         classElem.superclassConstraints.isNotEmpty) {
       var interfaces = classElem.interfaces.toList()
@@ -2051,18 +2063,18 @@
           [className, runtimeModule, interfaces.map(_emitType)]));
     }
 
-    void emitSignature(String name, List<JS.Property> elements) {
+    void emitSignature(String name, List<js_ast.Property> elements) {
       if (elements.isEmpty) return;
 
       if (!name.startsWith('Static')) {
         var proto = classElem.type.isObject
             ? js.call('Object.create(null)')
             : runtimeCall('get${name}s(#.__proto__)', [className]);
-        elements.insert(0, JS.Property(propertyName('__proto__'), proto));
+        elements.insert(0, js_ast.Property(propertyName('__proto__'), proto));
       }
       body.add(runtimeStatement('set${name}Signature(#, () => #)', [
         className,
-        JS.ObjectInitializer(elements, multiline: elements.length > 1)
+        js_ast.ObjectInitializer(elements, multiline: elements.length > 1)
       ]));
     }
 
@@ -2070,8 +2082,8 @@
 
     {
       var extMembers = _classProperties.extensionMethods;
-      var staticMethods = <JS.Property>[];
-      var instanceMethods = <JS.Property>[];
+      var staticMethods = <js_ast.Property>[];
+      var instanceMethods = <js_ast.Property>[];
       var classMethods = List.of(classElem.methods.where((m) => !m.isAbstract));
       for (var m in mockMembers.values) {
         if (m is MethodElement) classMethods.add(m);
@@ -2100,13 +2112,13 @@
           var type = _emitAnnotatedFunctionType(
               reifiedType, annotationNode?.metadata,
               parameters: annotationNode?.parameters?.parameters);
-          var property = JS.Property(_declareMemberName(method), type);
+          var property = js_ast.Property(_declareMemberName(method), type);
           if (isStatic) {
             staticMethods.add(property);
           } else {
             instanceMethods.add(property);
             if (extMembers.contains(name)) {
-              instanceMethods.add(JS.Property(
+              instanceMethods.add(js_ast.Property(
                   _declareMemberName(method, useExtension: true), type));
             }
           }
@@ -2119,10 +2131,10 @@
 
     {
       var extMembers = _classProperties.extensionAccessors;
-      var staticGetters = <JS.Property>[];
-      var instanceGetters = <JS.Property>[];
-      var staticSetters = <JS.Property>[];
-      var instanceSetters = <JS.Property>[];
+      var staticGetters = <js_ast.Property>[];
+      var instanceGetters = <js_ast.Property>[];
+      var staticSetters = <js_ast.Property>[];
+      var instanceSetters = <js_ast.Property>[];
 
       var classAccessors = classElem.accessors
           .where((m) => !m.isAbstract && !m.isSynthetic)
@@ -2163,14 +2175,14 @@
                   cacheType: false),
               annotationNode?.metadata);
 
-          var property = JS.Property(_declareMemberName(accessor), type);
+          var property = js_ast.Property(_declareMemberName(accessor), type);
           if (isStatic) {
             (isGetter ? staticGetters : staticSetters).add(property);
           } else {
             var accessors = isGetter ? instanceGetters : instanceSetters;
             accessors.add(property);
             if (extMembers.contains(accessor.variable.name)) {
-              accessors.add(JS.Property(
+              accessors.add(js_ast.Property(
                   _declareMemberName(accessor, useExtension: true), type));
             }
           }
@@ -2187,8 +2199,8 @@
     }
 
     {
-      var instanceFields = <JS.Property>[];
-      var staticFields = <JS.Property>[];
+      var instanceFields = <js_ast.Property>[];
+      var staticFields = <js_ast.Property>[];
 
       for (var field in classElem.fields) {
         if (field.isSynthetic && !classElem.isEnum) continue;
@@ -2205,21 +2217,21 @@
         var fieldSig = _emitFieldSignature(field.type,
             metadata: metadata, isFinal: field.isFinal);
         (isStatic ? staticFields : instanceFields)
-            .add(JS.Property(memberName, fieldSig));
+            .add(js_ast.Property(memberName, fieldSig));
       }
       emitSignature('Field', instanceFields);
       emitSignature('StaticField', staticFields);
     }
 
     if (options.emitMetadata) {
-      var constructors = <JS.Property>[];
+      var constructors = <js_ast.Property>[];
       for (var ctor in classElem.constructors) {
         var annotationNode = annotatedMembers[ctor] as ConstructorDeclaration;
         var memberName = _constructorName(ctor.name);
         var type = _emitAnnotatedFunctionType(
             ctor.type, annotationNode?.metadata,
             parameters: annotationNode?.parameters?.parameters);
-        constructors.add(JS.Property(memberName, type));
+        constructors.add(js_ast.Property(memberName, type));
       }
 
       emitSignature('Constructor', constructors);
@@ -2234,8 +2246,8 @@
     }
   }
 
-  JS.Expression _emitConstructor(ConstructorDeclaration node,
-      List<VariableDeclaration> fields, JS.Expression className) {
+  js_ast.Expression _emitConstructor(ConstructorDeclaration node,
+      List<VariableDeclaration> fields, js_ast.Expression className) {
     var params = _emitParameters(node.parameters?.parameters);
 
     var savedFunction = _currentFunction;
@@ -2247,7 +2259,7 @@
     _superAllowed = savedSuperAllowed;
     _currentFunction = savedFunction;
 
-    return JS.Fun(params, body)..sourceInformation = _functionEnd(node);
+    return js_ast.Fun(params, body)..sourceInformation = _functionEnd(node);
   }
 
   FunctionType _getMemberRuntimeType(ExecutableElement element) {
@@ -2273,7 +2285,7 @@
     return function.type = FunctionTypeImpl(function);
   }
 
-  JS.Expression _constructorName(String name) {
+  js_ast.Expression _constructorName(String name) {
     if (name == '') {
       // Default constructors (factory or not) use `new` as their name.
       return propertyName('new');
@@ -2281,9 +2293,9 @@
     return _emitStaticMemberName(name);
   }
 
-  JS.Block _emitConstructorBody(ConstructorDeclaration node,
-      List<VariableDeclaration> fields, JS.Expression className) {
-    var body = <JS.Statement>[];
+  js_ast.Block _emitConstructorBody(ConstructorDeclaration node,
+      List<VariableDeclaration> fields, js_ast.Expression className) {
+    var body = <js_ast.Statement>[];
     var cls = node.parent as ClassDeclaration;
 
     // Generate optional/named argument value assignment. These can not have
@@ -2299,7 +2311,7 @@
     for (var init in node.initializers) {
       if (init is RedirectingConstructorInvocation) {
         body.add(_emitRedirectingConstructor(init, className));
-        return JS.Block(body);
+        return js_ast.Block(body);
       }
     }
 
@@ -2326,11 +2338,11 @@
     }
 
     body.add(_emitFunctionScopedBody(node.body, node.declaredElement));
-    return JS.Block(body);
+    return js_ast.Block(body);
   }
 
-  JS.Statement _emitRedirectingConstructor(
-      RedirectingConstructorInvocation node, JS.Expression className) {
+  js_ast.Statement _emitRedirectingConstructor(
+      RedirectingConstructorInvocation node, js_ast.Expression className) {
     var ctor = node.staticElement;
     // We can't dispatch to the constructor with `this.new` as that might hit a
     // derived class constructor with the same name.
@@ -2341,9 +2353,9 @@
     ]);
   }
 
-  JS.Statement _emitSuperConstructorCallIfNeeded(
-      ClassElement element, JS.Expression className,
-      [ConstructorElement superCtor, List<JS.Expression> args]) {
+  js_ast.Statement _emitSuperConstructorCallIfNeeded(
+      ClassElement element, js_ast.Expression className,
+      [ConstructorElement superCtor, List<js_ast.Expression> args]) {
     // Get the supertype's unnamed constructor.
     superCtor ??= element.supertype?.element?.unnamedConstructor;
     if (superCtor == null) {
@@ -2362,8 +2374,8 @@
     return _emitSuperConstructorCall(className, superCtor.name, args);
   }
 
-  JS.Statement _emitSuperConstructorCall(
-      JS.Expression className, String name, List<JS.Expression> args) {
+  js_ast.Statement _emitSuperConstructorCall(
+      js_ast.Expression className, String name, List<js_ast.Expression> args) {
     return js.statement('#.__proto__.#.call(this, #);',
         [className, _constructorName(name), args ?? []]);
   }
@@ -2393,7 +2405,7 @@
   ///   2. field initializing parameters,
   ///   3. constructor field initializers,
   ///   4. initialize fields not covered in 1-3
-  JS.Statement _initializeFields(List<VariableDeclaration> fieldDecls,
+  js_ast.Statement _initializeFields(List<VariableDeclaration> fieldDecls,
       [ConstructorDeclaration ctor]) {
     Set<FieldElement> ctorFields;
     emitFieldInit(FieldElement f, Expression initializer, AstNode hoverInfo) {
@@ -2407,7 +2419,7 @@
           .toStatement();
     }
 
-    var body = <JS.Statement>[];
+    var body = <js_ast.Statement>[];
     if (ctor != null) {
       ctorFields = HashSet<FieldElement>();
 
@@ -2437,7 +2449,7 @@
     // (for example, it's a literal value such as implicit `null`) and where
     // there's another explicit initialization (either in the initializer list
     // like `field = value`, or via a `this.field` parameter).
-    var fieldInit = <JS.Statement>[];
+    var fieldInit = <js_ast.Statement>[];
     for (var field in fieldDecls) {
       var f = field.declaredElement as FieldElement;
       if (f.isStatic) continue;
@@ -2450,14 +2462,14 @@
     }
     // Run field initializers before the other ones.
     fieldInit.addAll(body);
-    return JS.Statement.from(fieldInit);
+    return js_ast.Statement.from(fieldInit);
   }
 
   /// Emits argument initializers, which handles optional/named args, as well
   /// as generic type checks needed due to our covariance.
-  JS.Statement _emitArgumentInitializers(ExecutableElement element,
+  js_ast.Statement _emitArgumentInitializers(ExecutableElement element,
       [FormalParameterList parameterNodes]) {
-    var body = <JS.Statement>[];
+    var body = <js_ast.Statement>[];
 
     _emitCovarianceBoundsCheck(
         element.typeParameters, _classProperties?.covariantParameters, body);
@@ -2471,13 +2483,13 @@
       }
 
       if (param.isOptional) {
-        JS.Expression defaultValue;
+        js_ast.Expression defaultValue;
         if (findAnnotation(param, isUndefinedAnnotation) != null) {
           defaultValue = null;
         } else if (paramNode != null) {
           var paramDefault = (paramNode as DefaultFormalParameter).defaultValue;
           if (paramDefault == null) {
-            defaultValue = JS.LiteralNull();
+            defaultValue = js_ast.LiteralNull();
           } else {
             defaultValue = _visitExpression(paramDefault);
           }
@@ -2528,7 +2540,7 @@
         body.add(_nullParameterCheck(jsParam));
       }
     }
-    return body.isEmpty ? null : JS.Statement.from(body);
+    return body.isEmpty ? null : js_ast.Statement.from(body);
   }
 
   bool _isCovariant(ParameterElement p) {
@@ -2536,26 +2548,27 @@
         (_classProperties?.covariantParameters?.contains(p) ?? false);
   }
 
-  JS.Fun _emitNativeFunctionBody(MethodDeclaration node) {
+  js_ast.Fun _emitNativeFunctionBody(MethodDeclaration node) {
     String name = getAnnotationName(node.declaredElement, isJSAnnotation) ??
         node.name.name;
     if (node.isGetter) {
-      return JS.Fun([], js.block('{ return this.#; }', [name]));
+      return js_ast.Fun([], js.block('{ return this.#; }', [name]));
     } else if (node.isSetter) {
       var params = _emitParameters(node.parameters?.parameters);
-      return JS.Fun(params, js.block('{ this.# = #; }', [name, params.last]));
+      return js_ast.Fun(
+          params, js.block('{ this.# = #; }', [name, params.last]));
     } else {
       return js.fun(
           'function (...args) { return this.#.apply(this, args); }', name);
     }
   }
 
-  JS.Method _emitMethodDeclaration(MethodDeclaration node) {
+  js_ast.Method _emitMethodDeclaration(MethodDeclaration node) {
     if (node.isAbstract) {
       return null;
     }
 
-    JS.Fun fn;
+    js_ast.Fun fn;
     if (node.externalKeyword != null || node.body is NativeFunctionBody) {
       if (node.isStatic) {
         // TODO(vsm): Do we need to handle this case?
@@ -2566,7 +2579,7 @@
       fn = _emitFunction(node.declaredElement, node.parameters, node.body);
     }
 
-    return JS.Method(_declareMemberName(node.declaredElement), fn,
+    return js_ast.Method(_declareMemberName(node.declaredElement), fn,
         isGetter: node.isGetter,
         isSetter: node.isSetter,
         isStatic: node.isStatic)
@@ -2574,7 +2587,7 @@
   }
 
   @override
-  JS.Statement visitFunctionDeclaration(FunctionDeclaration node) {
+  js_ast.Statement visitFunctionDeclaration(FunctionDeclaration node) {
     assert(node.parent is CompilationUnit);
 
     if (node.externalKeyword != null ||
@@ -2589,7 +2602,7 @@
           : element.correspondingGetter;
 
       var jsCode = _emitTopLevelProperty(node);
-      var props = <JS.Method>[jsCode];
+      var props = <js_ast.Method>[jsCode];
       if (pairAccessor != null) {
         // If we have a getter/setter pair, they need to be defined together.
         // If this is the first one, save the generated code for later.
@@ -2605,18 +2618,21 @@
           'copyProperties(#, { # })', [emitLibraryName(currentLibrary), props]);
     }
 
-    var body = <JS.Statement>[];
+    var body = <js_ast.Statement>[];
     var fn = _emitFunctionExpression(node.functionExpression);
 
     if (currentLibrary.source.isInSystemLibrary &&
         _isInlineJSFunction(node.functionExpression)) {
-      fn = JS.simplifyPassThroughArrowFunCallBody(fn);
+      fn = js_ast.simplifyPassThroughArrowFunCallBody(fn);
     }
     fn.sourceInformation = _functionEnd(node);
 
     var element = resolutionMap.elementDeclaredByFunctionDeclaration(node);
     var nameExpr = _emitTopLevelName(element);
-    body.add(js.statement('# = #', [nameExpr, fn]));
+    body.add(js.statement('# = #', [
+      nameExpr,
+      js_ast.NamedFunction(js_ast.TemporaryId(element.name), fn)
+    ]));
     // Function types of top-level/static functions are only needed when
     // dart:mirrors is enabled.
     // TODO(jmesserly): do we even need this for mirrors, since statics are not
@@ -2626,7 +2642,7 @@
           .toStatement());
     }
 
-    return JS.Statement.from(body);
+    return js_ast.Statement.from(body);
   }
 
   bool _isInlineJSFunction(FunctionExpression functionExpression) {
@@ -2648,9 +2664,9 @@
   bool _isJSInvocation(Expression expr) =>
       expr is MethodInvocation && isInlineJS(expr.methodName.staticElement);
 
-  JS.Method _emitTopLevelProperty(FunctionDeclaration node) {
+  js_ast.Method _emitTopLevelProperty(FunctionDeclaration node) {
     var name = node.name.name;
-    return JS.Method(
+    return js_ast.Method(
         propertyName(name), _emitFunctionExpression(node.functionExpression),
         isGetter: node.isGetter, isSetter: node.isSetter)
       ..sourceInformation = _functionEnd(node);
@@ -2697,7 +2713,7 @@
     return !_declarationNodes.containsKey(type.element);
   }
 
-  JS.Expression _emitFunctionTagged(JS.Expression fn, FunctionType type,
+  js_ast.Expression _emitFunctionTagged(js_ast.Expression fn, FunctionType type,
       {bool topLevel = false}) {
     var lazy = topLevel && !_canEmitTypeAtTopLevel(type);
     var typeRep = _emitFunctionType(type, lazy: lazy);
@@ -2712,7 +2728,7 @@
   ///
   /// Contrast with [_emitFunctionExpression].
   @override
-  JS.Expression visitFunctionExpression(FunctionExpression node) {
+  js_ast.Expression visitFunctionExpression(FunctionExpression node) {
     assert(node.parent is! FunctionDeclaration &&
         node.parent is! MethodDeclaration);
     var fn = _emitArrowFunction(node);
@@ -2721,22 +2737,22 @@
         topLevel: _executesAtTopLevel(node));
   }
 
-  JS.ArrowFun _emitArrowFunction(FunctionExpression node) {
+  js_ast.ArrowFun _emitArrowFunction(FunctionExpression node) {
     var f = _emitFunction(node.declaredElement, node.parameters, node.body);
-    JS.Node body = f.body;
+    js_ast.Node body = f.body;
 
     // Simplify `=> { return e; }` to `=> e`
-    if (body is JS.Block) {
-      var block = body as JS.Block;
+    if (body is js_ast.Block) {
+      var block = body as js_ast.Block;
       if (block.statements.length == 1) {
-        JS.Statement s = block.statements[0];
-        if (s is JS.Return && s.value != null) body = s.value;
+        js_ast.Statement s = block.statements[0];
+        if (s is js_ast.Return && s.value != null) body = s.value;
       }
     }
 
     // Convert `function(...) { ... }` to `(...) => ...`
     // This is for readability, but it also ensures correct `this` binding.
-    return JS.ArrowFun(f.params, body);
+    return js_ast.ArrowFun(f.params, body);
   }
 
   /// Emits a non-arrow FunctionExpression node.
@@ -2746,11 +2762,11 @@
   /// as methods, properties, and top-level functions.
   ///
   /// Contrast with [visitFunctionExpression].
-  JS.Fun _emitFunctionExpression(FunctionExpression node) {
+  js_ast.Fun _emitFunctionExpression(FunctionExpression node) {
     return _emitFunction(node.declaredElement, node.parameters, node.body);
   }
 
-  JS.Fun _emitFunction(ExecutableElement element,
+  js_ast.Fun _emitFunction(ExecutableElement element,
       FormalParameterList parameters, FunctionBody body) {
     FunctionType type = element.type;
 
@@ -2766,19 +2782,19 @@
         () => isPotentiallyMutated(
             body, parameters.parameters.last.declaredElement));
 
-    JS.Block code = isSync
+    js_ast.Block code = isSync
         ? _emitSyncFunctionBody(element, parameters, body)
         : _emitGeneratorFunctionBody(element, parameters, body);
 
     code = super.exitFunction(element.name, formals, code);
-    return JS.Fun(formals, code);
+    return js_ast.Fun(formals, code);
   }
 
   /// Emits a `sync` function body (the default in Dart)
   ///
   /// To emit an `async`, `sync*`, or `async*` function body, use
   /// [_emitGeneratorFunctionBody] instead.
-  JS.Block _emitSyncFunctionBody(ExecutableElement element,
+  js_ast.Block _emitSyncFunctionBody(ExecutableElement element,
       FormalParameterList parameters, FunctionBody body) {
     var savedFunction = _currentFunction;
     _currentFunction = body;
@@ -2786,7 +2802,7 @@
     var initArgs = _emitArgumentInitializers(element, parameters);
     var block = _emitFunctionScopedBody(body, element);
 
-    if (initArgs != null) block = JS.Block([initArgs, block]);
+    if (initArgs != null) block = js_ast.Block([initArgs, block]);
 
     _currentFunction = savedFunction;
 
@@ -2794,7 +2810,7 @@
       // TODO(jmesserly: JS AST printer does not understand the need to emit a
       // nested scoped block in a JS function. So we need to add a non-scoped
       // wrapper to ensure it gets printed.
-      block = JS.Block([block]);
+      block = js_ast.Block([block]);
     }
     return block;
   }
@@ -2810,17 +2826,17 @@
   ///
   /// To emit a `sync` function body (the default in Dart), use
   /// [_emitSyncFunctionBody] instead.
-  JS.Block _emitGeneratorFunctionBody(ExecutableElement element,
+  js_ast.Block _emitGeneratorFunctionBody(ExecutableElement element,
       FormalParameterList parameters, FunctionBody body) {
     var savedFunction = _currentFunction;
     _currentFunction = body;
 
     var initArgs = _emitArgumentInitializers(element, parameters);
-    var block = JS.Block([
+    var block = js_ast.Block([
       _emitGeneratorFunction(element, parameters, body).toReturn()
         ..sourceInformation = _nodeStart(body)
     ]);
-    if (initArgs != null) block = JS.Block([initArgs, block]);
+    if (initArgs != null) block = js_ast.Block([initArgs, block]);
 
     _currentFunction = savedFunction;
 
@@ -2828,14 +2844,14 @@
       // TODO(jmesserly: JS AST printer does not understand the need to emit a
       // nested scoped block in a JS function. So we need to add a non-scoped
       // wrapper to ensure it gets printed.
-      block = JS.Block([block]);
+      block = js_ast.Block([block]);
     }
     return block;
   }
 
-  JS.Block _emitFunctionScopedBody(
+  js_ast.Block _emitFunctionScopedBody(
       FunctionBody body, ExecutableElement element) {
-    var block = body.accept(this) as JS.Block;
+    var block = body.accept(this) as js_ast.Block;
     if (element.parameters.isNotEmpty) {
       // Handle shadowing of parameters by local variables, which is allowed in
       // Dart but not in JS.
@@ -2852,7 +2868,7 @@
   }
 
   void _emitCovarianceBoundsCheck(List<TypeParameterElement> typeFormals,
-      Set<Element> covariantParams, List<JS.Statement> body) {
+      Set<Element> covariantParams, List<js_ast.Statement> body) {
     if (covariantParams == null) return;
     for (var t in typeFormals) {
       t = covariantParams.lookup(t) as TypeParameterElement;
@@ -2863,14 +2879,15 @@
     }
   }
 
-  JS.Expression _emitGeneratorFunction(ExecutableElement element,
+  js_ast.Expression _emitGeneratorFunction(ExecutableElement element,
       FormalParameterList parameters, FunctionBody body) {
     // Transforms `sync*` `async` and `async*` function bodies
     // using ES6 generators.
 
     var returnType = _getExpectedReturnType(element);
 
-    emitGeneratorFn(List<JS.Parameter> jsParams, [JS.TemporaryId asyncStar]) {
+    emitGeneratorFn(List<js_ast.Parameter> jsParams,
+        [js_ast.TemporaryId asyncStar]) {
       var savedSuperAllowed = _superAllowed;
       var savedController = _asyncStarController;
       _superAllowed = false;
@@ -2885,14 +2902,15 @@
       var jsBody = _emitFunctionScopedBody(body, element);
       _currentFunction = savedFunction;
 
-      var genFn = JS.Fun(jsParams, jsBody, isGenerator: true);
+      var genFn = js_ast.Fun(jsParams, jsBody, isGenerator: true);
 
       // Name the function if possible, to get better stack traces.
       var name = element.name;
-      JS.Expression gen = genFn;
+      js_ast.Expression gen = genFn;
       if (name.isNotEmpty) {
-        gen = JS.NamedFunction(
-            JS.TemporaryId(JS.friendlyNameForDartOperator[name] ?? name),
+        gen = js_ast.NamedFunction(
+            js_ast.TemporaryId(
+                js_ast.friendlyNameForDartOperator[name] ?? name),
             genFn);
       }
       gen.sourceInformation = _functionEnd(body);
@@ -2943,7 +2961,7 @@
       // `await` is generated as `yield`.
       //
       // _AsyncStarImpl has an example of the generated code.
-      var asyncStarParam = JS.TemporaryId('stream');
+      var asyncStarParam = js_ast.TemporaryId('stream');
       var gen = emitGeneratorFn([asyncStarParam], asyncStarParam);
 
       var asyncStarImpl = asyncStarImplType.instantiate([returnType]);
@@ -2966,7 +2984,7 @@
   }
 
   @override
-  JS.Statement visitFunctionDeclarationStatement(
+  js_ast.Statement visitFunctionDeclarationStatement(
       FunctionDeclarationStatement node) {
     var func = node.functionDeclaration;
     if (func.isGetter || func.isSetter) {
@@ -2975,11 +2993,11 @@
 
     var fn = _emitFunctionExpression(func.functionExpression);
     var name = _emitVariableDef(func.name);
-    JS.Statement declareFn;
+    js_ast.Statement declareFn;
     declareFn = toBoundFunctionStatement(fn, name);
     var element = func.declaredElement;
     if (_reifyFunctionType(element)) {
-      declareFn = JS.Block(
+      declareFn = js_ast.Block(
           [declareFn, _emitFunctionTagged(name, element.type).toStatement()]);
     }
     return declareFn;
@@ -2988,15 +3006,15 @@
   /// Emits a simple identifier, including handling an inferred generic
   /// function instantiation.
   @override
-  JS.Expression visitSimpleIdentifier(SimpleIdentifier node,
+  js_ast.Expression visitSimpleIdentifier(SimpleIdentifier node,
       [PrefixedIdentifier prefix]) {
     var typeArgs = _getTypeArgs(node.staticElement, node.staticType);
     var simpleId = _emitSimpleIdentifier(node, prefix)
       ..sourceInformation = _nodeSpan(node);
     if (prefix != null &&
         // Check that the JS AST is for a Dart property and not JS interop.
-        simpleId is JS.PropertyAccess &&
-        simpleId.receiver is JS.Identifier) {
+        simpleId is js_ast.PropertyAccess &&
+        simpleId.receiver is js_ast.Identifier) {
       // Attach the span to the library prefix.
       simpleId.receiver.sourceInformation = _nodeSpan(prefix.prefix);
     }
@@ -3007,7 +3025,7 @@
   /// Emits a simple identifier, handling implicit `this` as well as
   /// going through the qualified library name if necessary, but *not* handling
   /// inferred generic function instantiation.
-  JS.Expression _emitSimpleIdentifier(SimpleIdentifier node,
+  js_ast.Expression _emitSimpleIdentifier(SimpleIdentifier node,
       [PrefixedIdentifier prefix]) {
     var accessor = resolutionMap.staticElementForIdentifier(node);
     if (accessor == null) {
@@ -3074,10 +3092,11 @@
       return _emitParameter(element);
     }
 
-    return JS.Identifier(element.name);
+    return js_ast.Identifier(element.name);
   }
 
-  JS.Expression _emitLibraryMemberElement(Element element, Expression node) {
+  js_ast.Expression _emitLibraryMemberElement(
+      Element element, Expression node) {
     var result = _emitTopLevelName(element);
     if (element is FunctionElement && _reifyTearoff(element, node)) {
       return _emitFunctionTagged(result, element.type);
@@ -3085,7 +3104,7 @@
     return result;
   }
 
-  JS.Expression _emitClassMemberElement(
+  js_ast.Expression _emitClassMemberElement(
       ClassMemberElement element, Element accessor, Expression node) {
     bool isStatic = element.isStatic;
     var classElem = element.enclosingElement;
@@ -3095,22 +3114,22 @@
 
     // For instance members, we add implicit-this.
     // For method tear-offs, we ensure it's a bound method.
-    var target = isStatic ? _emitStaticClassName(element) : JS.This();
+    var target = isStatic ? _emitStaticClassName(element) : js_ast.This();
     if (element is MethodElement && _reifyTearoff(element, node)) {
       if (isStatic) {
         // TODO(jmesserly): we could tag static/top-level function types once
         // in the module initialization, rather than at the point where they
         // escape.
         return _emitFunctionTagged(
-            JS.PropertyAccess(target, member), element.type);
+            js_ast.PropertyAccess(target, member), element.type);
       }
       return runtimeCall('bind(#, #)', [target, member]);
     }
-    return JS.PropertyAccess(target, member);
+    return js_ast.PropertyAccess(target, member);
   }
 
-  JS.Identifier _emitVariableDef(SimpleIdentifier id) {
-    return JS.Identifier(id.name)..sourceInformation = _nodeStart(id);
+  js_ast.Identifier _emitVariableDef(SimpleIdentifier id) {
+    return js_ast.Identifier(id.name)..sourceInformation = _nodeStart(id);
   }
 
   /// Returns `true` if the type name referred to by [node] is used in a
@@ -3139,7 +3158,7 @@
     return true;
   }
 
-  JS.Identifier _emitParameter(ParameterElement element,
+  js_ast.Identifier _emitParameter(ParameterElement element,
       {bool declaration = false}) {
     // initializing formal parameter, e.g. `Point(this._x)`
     // TODO(jmesserly): type ref is not attached in this case.
@@ -3148,10 +3167,10 @@
       /// The renamer would handle this, but it would prefer to rename the
       /// temporary used for the private symbol. Instead rename the parameter.
       return _initializingFormalTemps.putIfAbsent(
-          element, () => JS.TemporaryId(element.name.substring(1)));
+          element, () => js_ast.TemporaryId(element.name.substring(1)));
     }
 
-    return JS.Identifier(element.name);
+    return js_ast.Identifier(element.name);
   }
 
   List<Annotation> _parameterMetadata(FormalParameter p) =>
@@ -3161,52 +3180,52 @@
 
   // Wrap a result - usually a type - with its metadata.  The runtime is
   // responsible for unpacking this.
-  JS.Expression _emitAnnotatedResult(
-      JS.Expression result, List<Annotation> metadata) {
+  js_ast.Expression _emitAnnotatedResult(
+      js_ast.Expression result, List<Annotation> metadata) {
     if (options.emitMetadata && metadata != null && metadata.isNotEmpty) {
-      result = JS.ArrayInitializer(
+      result = js_ast.ArrayInitializer(
           [result]..addAll(metadata.map(_instantiateAnnotation)));
     }
     return result;
   }
 
-  JS.Expression _emitFieldSignature(DartType type,
+  js_ast.Expression _emitFieldSignature(DartType type,
       {List<Annotation> metadata, bool isFinal = true}) {
     var args = [_emitType(type)];
     if (options.emitMetadata && metadata != null && metadata.isNotEmpty) {
-      args.add(
-          JS.ArrayInitializer(metadata.map(_instantiateAnnotation).toList()));
+      args.add(js_ast.ArrayInitializer(
+          metadata.map(_instantiateAnnotation).toList()));
     }
     return runtimeCall(isFinal ? 'finalFieldType(#)' : 'fieldType(#)', [args]);
   }
 
-  JS.ArrayInitializer _emitTypeNames(
+  js_ast.ArrayInitializer _emitTypeNames(
       List<DartType> types, List<FormalParameter> parameters,
       {bool cacheType = true}) {
-    var result = <JS.Expression>[];
+    var result = <js_ast.Expression>[];
     for (int i = 0; i < types.length; ++i) {
       var metadata =
           parameters != null ? _parameterMetadata(parameters[i]) : null;
       var typeName = _emitType(types[i], cacheType: cacheType);
       result.add(_emitAnnotatedResult(typeName, metadata));
     }
-    return JS.ArrayInitializer(result);
+    return js_ast.ArrayInitializer(result);
   }
 
-  JS.ObjectInitializer _emitTypeProperties(Map<String, DartType> types,
+  js_ast.ObjectInitializer _emitTypeProperties(Map<String, DartType> types,
       {bool cacheType = true}) {
-    var properties = <JS.Property>[];
+    var properties = <js_ast.Property>[];
     types.forEach((name, type) {
       var key = propertyName(name);
       var value = _emitType(type, cacheType: cacheType);
-      properties.add(JS.Property(key, value));
+      properties.add(js_ast.Property(key, value));
     });
-    return JS.ObjectInitializer(properties);
+    return js_ast.ObjectInitializer(properties);
   }
 
   /// Emit the pieces of a function type, as an array of return type,
   /// regular args, and optional/named args.
-  JS.Expression _emitFunctionType(FunctionType type,
+  js_ast.Expression _emitFunctionType(FunctionType type,
       {List<FormalParameter> parameters,
       bool cacheType = true,
       bool lazy = false}) {
@@ -3217,7 +3236,7 @@
 
     var ra = _emitTypeNames(parameterTypes, parameters, cacheType: cacheType);
 
-    List<JS.Expression> typeParts;
+    List<js_ast.Expression> typeParts;
     if (namedTypes.isNotEmpty) {
       assert(optionalTypes.isEmpty);
       // TODO(vsm): Pass in annotations here as well.
@@ -3233,13 +3252,13 @@
       typeParts = [rt, ra];
     }
 
-    JS.Expression fullType;
+    js_ast.Expression fullType;
     String helperCall;
     var typeFormals = type.typeFormals;
     if (typeFormals.isNotEmpty) {
       var tf = _emitTypeFormals(typeFormals);
 
-      addTypeFormalsAsParameters(List<JS.Expression> elements) {
+      addTypeFormalsAsParameters(List<js_ast.Expression> elements) {
         var names = _typeTable.discharge(typeFormals);
         return names.isEmpty
             ? js.call('(#) => [#]', [tf, elements])
@@ -3264,7 +3283,7 @@
     return _typeTable.nameFunctionType(type, fullType, lazy: lazy);
   }
 
-  JS.Expression _emitAnnotatedFunctionType(
+  js_ast.Expression _emitAnnotatedFunctionType(
       FunctionType type, List<Annotation> metadata,
       {List<FormalParameter> parameters}) {
     var result =
@@ -3273,7 +3292,7 @@
   }
 
   @override
-  JS.Expression emitConstructorAccess(DartType type) {
+  js_ast.Expression emitConstructorAccess(DartType type) {
     return _emitJSInterop(type.element) ?? _emitType(type);
   }
 
@@ -3282,7 +3301,7 @@
   /// Typically this is equivalent to [_declareBeforeUse] followed by
   /// [_emitTopLevelName] on the class, but if the member is external, then the
   /// native class name will be used, for direct access to the native member.
-  JS.Expression _emitStaticClassName(ClassMemberElement member) {
+  js_ast.Expression _emitStaticClassName(ClassMemberElement member) {
     var c = member.enclosingElement;
     _declareBeforeUse(c);
 
@@ -3301,7 +3320,7 @@
   /// Emits a Dart [type] into code.
   ///
   /// If [cacheType] is true, then the type will be cached for the module.
-  JS.Expression _emitType(DartType type, {bool cacheType = true}) {
+  js_ast.Expression _emitType(DartType type, {bool cacheType = true}) {
     // The void and dynamic types are not defined in core.
     if (type.isVoid) {
       return runtimeCall('void');
@@ -3341,7 +3360,7 @@
 
     var name = type.name;
     if (type is TypeParameterType) {
-      return JS.Identifier(name);
+      return js_ast.Identifier(name);
     }
 
     if (type is ParameterizedType) {
@@ -3349,7 +3368,7 @@
         return _emitFunctionType(type, cacheType: cacheType);
       }
       var args = type.typeArguments;
-      List<JS.Expression> jsArgs;
+      List<js_ast.Expression> jsArgs;
       if (args.any((a) => !a.isDynamic)) {
         jsArgs = args.map((x) => _emitType(x, cacheType: cacheType)).toList();
       }
@@ -3363,23 +3382,23 @@
   }
 
   /// Emits the raw type corresponding to the [element].
-  JS.Expression _emitTypeDefiningElement(TypeDefiningElement e) {
+  js_ast.Expression _emitTypeDefiningElement(TypeDefiningElement e) {
     return _emitType(instantiateElementTypeToBounds(rules, e));
   }
 
-  JS.Expression _emitGenericClassType(
-      ParameterizedType t, List<JS.Expression> typeArgs) {
+  js_ast.Expression _emitGenericClassType(
+      ParameterizedType t, List<js_ast.Expression> typeArgs) {
     var genericName = _emitTopLevelNameNoInterop(t.element, suffix: '\$');
     return js.call('#(#)', [genericName, typeArgs]);
   }
 
-  JS.PropertyAccess _emitTopLevelName(Element e, {String suffix = ''}) {
+  js_ast.PropertyAccess _emitTopLevelName(Element e, {String suffix = ''}) {
     return _emitJSInterop(e) ?? _emitTopLevelNameNoInterop(e, suffix: suffix);
   }
 
-  JS.PropertyAccess _emitTopLevelNameNoInterop(Element e,
+  js_ast.PropertyAccess _emitTopLevelNameNoInterop(Element e,
       {String suffix = ''}) {
-    return JS.PropertyAccess(
+    return js_ast.PropertyAccess(
         emitLibraryName(e.library), _emitTopLevelMemberName(e, suffix: suffix));
   }
 
@@ -3387,13 +3406,13 @@
   ///
   /// NOTE: usually you should use [_emitTopLevelName] instead of this. This
   /// function does not handle JS interop.
-  JS.Expression _emitTopLevelMemberName(Element e, {String suffix = ''}) {
+  js_ast.Expression _emitTopLevelMemberName(Element e, {String suffix = ''}) {
     var name = getJSExportName(e) ?? _getElementName(e);
     return propertyName(name + suffix);
   }
 
   @override
-  JS.Expression visitAssignmentExpression(AssignmentExpression node) {
+  js_ast.Expression visitAssignmentExpression(AssignmentExpression node) {
     var left = node.leftHandSide;
     var right = node.rightHandSide;
     if (node.operator.type == TokenType.EQ) return _emitSet(left, right);
@@ -3403,7 +3422,7 @@
     return _emitOpAssign(left, right, op, node.staticElement, context: node);
   }
 
-  JS.MetaLet _emitOpAssign(
+  js_ast.MetaLet _emitOpAssign(
       Expression left, Expression right, String op, MethodElement element,
       {Expression context}) {
     if (op == '??') {
@@ -3414,11 +3433,11 @@
 
       // Handle the left hand side, to ensure each of its subexpressions are
       // evaluated only once.
-      var vars = <JS.MetaLetVariable, JS.Expression>{};
+      var vars = <js_ast.MetaLetVariable, js_ast.Expression>{};
       var x = _bindLeftHandSide(vars, left, context: left);
       // Capture the result of evaluating the left hand side in a temp.
       var t = _bindValue(vars, 't', x, context: x);
-      return JS.MetaLet(vars, [
+      return js_ast.MetaLet(vars, [
         js.call('# == null ? # : #',
             [_visitExpression(t), _emitSet(x, right), _visitExpression(t)])
       ]);
@@ -3426,7 +3445,7 @@
 
     // Desugar `x += y` as `x = x + y`, ensuring that if `x` has subexpressions
     // (for example, x is IndexExpression) we evaluate those once.
-    var vars = <JS.MetaLetVariable, JS.Expression>{};
+    var vars = <js_ast.MetaLetVariable, js_ast.Expression>{};
     var lhs = _bindLeftHandSide(vars, left, context: context);
     // TODO(leafp): The element for lhs here will be the setter element
     // instead of the getter element if lhs is a property access. This
@@ -3437,10 +3456,10 @@
 
     var castTo = getImplicitOperationCast(left);
     if (castTo != null) inc = CoercionReifier.castExpression(inc, castTo);
-    return JS.MetaLet(vars, [_emitSet(lhs, inc)]);
+    return js_ast.MetaLet(vars, [_emitSet(lhs, inc)]);
   }
 
-  JS.Expression _emitSet(Expression left, Expression right) {
+  js_ast.Expression _emitSet(Expression left, Expression right) {
     if (left is IndexExpression) {
       var target = _getTarget(left);
       if (_useNativeJsIndexer(target.staticType)) {
@@ -3500,7 +3519,8 @@
   // access to the target expression there (needed for `dart.replNameLookup`).
   String get _replSuffix => options.replCompile ? 'Repl' : '';
 
-  JS.Expression _badAssignment(String problem, Expression lhs, Expression rhs) {
+  js_ast.Expression _badAssignment(
+      String problem, Expression lhs, Expression rhs) {
     // TODO(sra): We should get here only for compiler bugs or weirdness due to
     // --unsafe-force-compile. Once those paths have been addressed, throw at
     // compile time.
@@ -3512,9 +3532,9 @@
   /// Emits assignment to a simple identifier. Handles all legal simple
   /// identifier assignment targets (local, top level library member, implicit
   /// `this` or class, etc.)
-  JS.Expression _emitSetSimpleIdentifier(
+  js_ast.Expression _emitSetSimpleIdentifier(
       SimpleIdentifier node, Expression right) {
-    JS.Expression unimplemented() {
+    js_ast.Expression unimplemented() {
       return _badAssignment("Unimplemented: unknown name '$node'", node, right);
     }
 
@@ -3543,7 +3563,7 @@
     // Unqualified class member. This could mean implicit `this`, or implicit
     // static from the same class.
     if (element is FieldElement) {
-      return _emitSetField(right, element, JS.This(), node);
+      return _emitSetField(right, element, js_ast.This(), node);
     }
 
     // We should not get here.
@@ -3551,41 +3571,42 @@
   }
 
   /// Emits assignment to a simple local variable or parameter.
-  JS.Expression _emitSetLocal(Element element, Expression rhs, AstNode left) {
-    JS.Expression target;
+  js_ast.Expression _emitSetLocal(
+      Element element, Expression rhs, AstNode left) {
+    js_ast.Expression target;
     if (element is TemporaryVariableElement) {
       // If this is one of our compiler's temporary variables, use its JS form.
       target = element.jsVariable;
     } else if (element is ParameterElement) {
       target = _emitParameter(element);
     } else {
-      target = JS.Identifier(element.name);
+      target = js_ast.Identifier(element.name);
     }
     target.sourceInformation = _nodeSpan(left);
     return _visitExpression(rhs).toAssignExpression(target);
   }
 
   /// Emits assignment to library scope element [element].
-  JS.Expression _emitSetTopLevel(
+  js_ast.Expression _emitSetTopLevel(
       PropertyAccessorElement element, Expression rhs) {
     return _visitExpression(rhs).toAssignExpression(_emitTopLevelName(element));
   }
 
   /// Emits assignment to a static field element or property.
-  JS.Expression _emitSetField(Expression right, FieldElement field,
-      JS.Expression jsTarget, SimpleIdentifier id) {
+  js_ast.Expression _emitSetField(Expression right, FieldElement field,
+      js_ast.Expression jsTarget, SimpleIdentifier id) {
     var classElem = field.enclosingElement;
     var isStatic = field.isStatic;
     var member = _emitMemberName(field.name,
         isStatic: isStatic, type: classElem.type, element: field.setter);
     jsTarget = isStatic
-        ? (JS.PropertyAccess(_emitStaticClassName(field), member)
+        ? (js_ast.PropertyAccess(_emitStaticClassName(field), member)
           ..sourceInformation = _nodeSpan(id))
         : _emitTargetAccess(jsTarget, member, field.setter, id);
     return _visitExpression(right).toAssignExpression(jsTarget);
   }
 
-  JS.Expression _emitNullSafeSet(PropertyAccess node, Expression right) {
+  js_ast.Expression _emitNullSafeSet(PropertyAccess node, Expression right) {
     // Emit `obj?.prop = expr` as:
     //
     //     (_ => _ == null ? null : _.prop = expr)(obj).
@@ -3594,31 +3615,32 @@
     //
     // However with MetaLet, we get clean code in statement or void context,
     // or when one of the expressions is stateless, which seems common.
-    var vars = <JS.MetaLetVariable, JS.Expression>{};
+    var vars = <js_ast.MetaLetVariable, js_ast.Expression>{};
     var left = _bindValue(vars, 'l', node.target);
     var body = js.call('# == null ? null : #', [
       _visitExpression(left),
       _emitSet(_stripNullAwareOp(node, left), right)
     ]);
-    return JS.MetaLet(vars, [body]);
+    return js_ast.MetaLet(vars, [body]);
   }
 
   @override
-  JS.Block visitExpressionFunctionBody(ExpressionFunctionBody node) {
-    return JS.Block([_visitExpression(node.expression).toReturn()]);
+  js_ast.Block visitExpressionFunctionBody(ExpressionFunctionBody node) {
+    return js_ast.Block([_visitExpression(node.expression).toReturn()]);
   }
 
   @override
-  JS.Block visitEmptyFunctionBody(EmptyFunctionBody node) => JS.Block([]);
+  js_ast.Block visitEmptyFunctionBody(EmptyFunctionBody node) =>
+      js_ast.Block([]);
 
   @override
-  JS.Block visitBlockFunctionBody(BlockFunctionBody node) {
-    return JS.Block(_visitStatementList(node.block.statements));
+  js_ast.Block visitBlockFunctionBody(BlockFunctionBody node) {
+    return js_ast.Block(_visitStatementList(node.block.statements));
   }
 
   @override
-  JS.Block visitBlock(Block node) =>
-      JS.Block(_visitStatementList(node.statements), isScope: true);
+  js_ast.Block visitBlock(Block node) =>
+      js_ast.Block(_visitStatementList(node.statements), isScope: true);
 
   @override
   visitMethodInvocation(MethodInvocation node) {
@@ -3672,7 +3694,8 @@
     return _emitMethodCall(target, node);
   }
 
-  JS.Expression _emitTarget(Expression target, Element member, bool isStatic) {
+  js_ast.Expression _emitTarget(
+      Expression target, Element member, bool isStatic) {
     if (isStatic) {
       if (member is ConstructorElement) {
         return emitConstructorAccess(member.enclosingElement.type)
@@ -3699,15 +3722,15 @@
     return result;
   }
 
-  /// Emits the [JS.PropertyAccess] for accessors or method calls to
+  /// Emits the [js_ast.PropertyAccess] for accessors or method calls to
   /// [jsTarget].[jsName], replacing `super` if it is not allowed in scope.
-  JS.Expression _emitTargetAccess(JS.Expression jsTarget, JS.Expression jsName,
-      Element member, AstNode node) {
-    JS.Expression result;
-    if (!_superAllowed && jsTarget is JS.Super && member != null) {
+  js_ast.Expression _emitTargetAccess(js_ast.Expression jsTarget,
+      js_ast.Expression jsName, Element member, AstNode node) {
+    js_ast.Expression result;
+    if (!_superAllowed && jsTarget is js_ast.Super && member != null) {
       result = _getSuperHelper(member, jsName);
     } else {
-      result = JS.PropertyAccess(jsTarget, jsName);
+      result = js_ast.PropertyAccess(jsTarget, jsName);
     }
     if (node != null) {
       // Use the full span for a cascade property so we can hover over `bar` in
@@ -3722,7 +3745,7 @@
     return result;
   }
 
-  JS.Expression _getSuperHelper(Element member, JS.Expression jsName) {
+  js_ast.Expression _getSuperHelper(Element member, js_ast.Expression jsName) {
     var jsMethod = _superHelpers.putIfAbsent(member.name, () {
       if (member is PropertyAccessorElement) {
         var isSetter = member.isSetter;
@@ -3731,31 +3754,31 @@
                 ? 'function(x) { super[#] = x; }'
                 : 'function() { return super[#]; }',
             [jsName]);
-        return JS.Method(JS.TemporaryId(member.variable.name), fn,
+        return js_ast.Method(js_ast.TemporaryId(member.variable.name), fn,
             isGetter: !isSetter, isSetter: isSetter);
       } else {
         var method = member as MethodElement;
-        var params =
-            List<JS.Identifier>.from(_emitTypeFormals(method.typeParameters));
+        var params = List<js_ast.Identifier>.from(
+            _emitTypeFormals(method.typeParameters));
         for (var param in method.parameters) {
           if (param.isNamed) {
             params.add(namedArgumentTemp);
             break;
           }
-          params.add(JS.Identifier(param.name));
+          params.add(js_ast.Identifier(param.name));
         }
 
         var fn = js.fun(
             'function(#) { return super[#](#); }', [params, jsName, params]);
         var name = method.name;
-        name = JS.friendlyNameForDartOperator[name] ?? name;
-        return JS.Method(JS.TemporaryId(name), fn);
+        name = js_ast.friendlyNameForDartOperator[name] ?? name;
+        return js_ast.Method(js_ast.TemporaryId(name), fn);
       }
     });
-    return JS.PropertyAccess(JS.This(), jsMethod.name);
+    return js_ast.PropertyAccess(js_ast.This(), jsMethod.name);
   }
 
-  JS.Expression _emitMethodCall(Expression target, MethodInvocation node) {
+  js_ast.Expression _emitMethodCall(Expression target, MethodInvocation node) {
     var argumentList = node.argumentList;
     var args = _emitArgumentList(argumentList);
     var typeArgs = _emitInvokeTypeArguments(node);
@@ -3768,8 +3791,8 @@
         _emitMemberName(name, type: type, isStatic: isStatic, element: element);
 
     if (isDynamicInvoke(target) || isDynamicInvoke(node.methodName)) {
-      JS.Expression jsTarget = _emitTarget(target, element, isStatic);
-      if (jsTarget is JS.Super) {
+      js_ast.Expression jsTarget = _emitTarget(target, element, isStatic);
+      if (jsTarget is js_ast.Super) {
         jsTarget =
             _emitTargetAccess(jsTarget, jsName, element, node.methodName);
         jsName = null;
@@ -3777,7 +3800,7 @@
       return _emitDynamicInvoke(jsTarget, typeArgs, jsName, args, argumentList);
     }
 
-    JS.Expression jsTarget = _emitTarget(target, element, isStatic);
+    js_ast.Expression jsTarget = _emitTarget(target, element, isStatic);
 
     // Handle Object methods that are supported by `null`.
     if (_isObjectMethodCall(name, argumentList.arguments) &&
@@ -3794,7 +3817,7 @@
       if (fromType is InterfaceType) {
         var callName = _getImplicitCallTarget(fromType);
         if (callName != null) {
-          jsTarget = JS.PropertyAccess(jsTarget, callName);
+          jsTarget = js_ast.PropertyAccess(jsTarget, callName);
         }
       }
     }
@@ -3803,14 +3826,14 @@
       jsTarget = _emitCast(castTo, jsTarget);
     }
     if (typeArgs != null) args.insertAll(0, typeArgs);
-    return JS.Call(jsTarget, args);
+    return js_ast.Call(jsTarget, args);
   }
 
-  JS.Expression _emitDynamicInvoke(
-      JS.Expression fn,
-      List<JS.Expression> typeArgs,
-      JS.Expression methodName,
-      List<JS.Expression> args,
+  js_ast.Expression _emitDynamicInvoke(
+      js_ast.Expression fn,
+      List<js_ast.Expression> typeArgs,
+      js_ast.Expression methodName,
+      List<js_ast.Expression> args,
       ArgumentList argumentList) {
     var jsArgs = <Object>[fn];
     String jsCode;
@@ -3856,19 +3879,19 @@
     return !isNullable(left) || !isNullable(right);
   }
 
-  JS.Expression _emitJSDoubleEq(List<JS.Expression> args,
+  js_ast.Expression _emitJSDoubleEq(List<js_ast.Expression> args,
       {bool negated = false}) {
     var op = negated ? '# != #' : '# == #';
     return js.call(op, args);
   }
 
-  JS.Expression _emitJSTripleEq(List<JS.Expression> args,
+  js_ast.Expression _emitJSTripleEq(List<js_ast.Expression> args,
       {bool negated = false}) {
     var op = negated ? '# !== #' : '# === #';
     return js.call(op, args);
   }
 
-  JS.Expression _emitCoreIdenticalCall(List<Expression> arguments,
+  js_ast.Expression _emitCoreIdenticalCall(List<Expression> arguments,
       {bool negated = false}) {
     if (arguments.length != 2) {
       // Shouldn't happen in typechecked code
@@ -3885,12 +3908,13 @@
       return _emitJSDoubleEq(args, negated: negated);
     }
     var code = negated ? '!#' : '#';
-    return js.call(code, JS.Call(_emitTopLevelName(_coreIdentical), args));
+    return js.call(code, js_ast.Call(_emitTopLevelName(_coreIdentical), args));
   }
 
   /// Emits a function call, to a top-level function, local function, or
   /// an expression.
-  JS.Node _emitFunctionCall(InvocationExpression node, [Expression function]) {
+  js_ast.Node _emitFunctionCall(InvocationExpression node,
+      [Expression function]) {
     function ??= node.function;
     var castTo = getImplicitOperationCast(function);
     if (castTo != null) {
@@ -3925,10 +3949,10 @@
       }
     }
 
-    return JS.Call(fn, args);
+    return js_ast.Call(fn, args);
   }
 
-  JS.Node _emitDebuggerCall(InvocationExpression node) {
+  js_ast.Node _emitDebuggerCall(InvocationExpression node) {
     var args = node.argumentList.arguments;
     var isStatement = node.parent is ExpressionStatement;
     if (args.isEmpty) {
@@ -3948,7 +3972,7 @@
     // to decide whether to break or not.
     //
     // We also need to return the value of `when`.
-    var jsArgs = <JS.Property>[];
+    var jsArgs = <js_ast.Property>[];
     var foundWhen = false;
     for (var arg in args) {
       var namedArg = arg as NamedExpression;
@@ -3960,16 +3984,16 @@
         //
         // For a single `message` argument, use `{message: ...}`, which
         // coerces to true (the default value of `when`).
-        ? (foundWhen ? jsArgs[0].value : JS.ObjectInitializer(jsArgs))
+        ? (foundWhen ? jsArgs[0].value : js_ast.ObjectInitializer(jsArgs))
         // If we have both `message` and `when` arguments, evaluate them in
         // order, then extract the `when` argument.
-        : js.call('#.when', JS.ObjectInitializer(jsArgs));
+        : js.call('#.when', js_ast.ObjectInitializer(jsArgs));
     return isStatement
         ? js.statement('if (#) debugger;', when)
         : js.call('# && (() => { debugger; return true })()', when);
   }
 
-  List<JS.Expression> _emitInvokeTypeArguments(InvocationExpression node) {
+  List<js_ast.Expression> _emitInvokeTypeArguments(InvocationExpression node) {
     // add no reify generic check here: if (node.function)
     // node is Identifier
     var function = node.function;
@@ -3982,7 +4006,7 @@
 
   /// If `g` is a generic function type, and `f` is an instantiation of it,
   /// then this will return the type arguments to apply, otherwise null.
-  List<JS.Expression> _emitFunctionTypeArguments(
+  List<js_ast.Expression> _emitFunctionTypeArguments(
       AstNode node, DartType g, DartType f,
       [TypeArgumentList typeArgs]) {
     if (node is InvocationExpression) {
@@ -4030,7 +4054,7 @@
   }
 
   /// Emits code for the `JS(...)` macro.
-  JS.Node _emitForeignJS(MethodInvocation node, Element e) {
+  js_ast.Node _emitForeignJS(MethodInvocation node, Element e) {
     if (!isInlineJS(e)) return null;
 
     var args = node.argumentList.arguments;
@@ -4069,46 +4093,47 @@
     var result = js.parseForeignJS(source).instantiate(jsArgs);
 
     // `throw` is emitted as a statement by `parseForeignJS`.
-    assert(result is JS.Expression ||
-        result is JS.Statement && node.parent is ExpressionStatement);
+    assert(result is js_ast.Expression ||
+        result is js_ast.Statement && node.parent is ExpressionStatement);
     return result;
   }
 
   @override
-  JS.Node visitFunctionExpressionInvocation(
+  js_ast.Node visitFunctionExpressionInvocation(
           FunctionExpressionInvocation node) =>
       _emitFunctionCall(node);
 
-  List<JS.Expression> _emitArgumentList(ArgumentList node) {
-    var args = <JS.Expression>[];
-    var named = <JS.Property>[];
+  List<js_ast.Expression> _emitArgumentList(ArgumentList node) {
+    var args = <js_ast.Expression>[];
+    var named = <js_ast.Property>[];
     for (var arg in node.arguments) {
       if (arg is NamedExpression) {
         named.add(visitNamedExpression(arg));
       } else if (arg is MethodInvocation && isJsSpreadInvocation(arg)) {
-        args.add(JS.Spread(_visitExpression(arg.argumentList.arguments[0])));
+        args.add(
+            js_ast.Spread(_visitExpression(arg.argumentList.arguments[0])));
       } else {
         args.add(_visitExpression(arg));
       }
     }
     if (named.isNotEmpty) {
-      args.add(JS.ObjectInitializer(named));
+      args.add(js_ast.ObjectInitializer(named));
     }
     return args;
   }
 
   @override
-  JS.Property visitNamedExpression(NamedExpression node) {
+  js_ast.Property visitNamedExpression(NamedExpression node) {
     assert(node.parent is ArgumentList);
-    return JS.Property(
+    return js_ast.Property(
         propertyName(node.name.label.name), _visitExpression(node.expression));
   }
 
-  List<JS.Parameter> _emitParametersForElement(ExecutableElement member) {
-    var jsParams = <JS.Identifier>[];
+  List<js_ast.Parameter> _emitParametersForElement(ExecutableElement member) {
+    var jsParams = <js_ast.Identifier>[];
     for (var p in member.parameters) {
       if (p.isPositional) {
-        jsParams.add(JS.Identifier(p.name));
+        jsParams.add(js_ast.Identifier(p.name));
       } else {
         jsParams.add(namedArgumentTemp);
         break;
@@ -4117,10 +4142,10 @@
     return jsParams;
   }
 
-  List<JS.Parameter> _emitParameters(Iterable<FormalParameter> parameters) {
+  List<js_ast.Parameter> _emitParameters(Iterable<FormalParameter> parameters) {
     if (parameters == null) return [];
 
-    var result = <JS.Parameter>[];
+    var result = <js_ast.Parameter>[];
     for (var param in parameters) {
       if (param.isNamed) {
         result.add(namedArgumentTemp);
@@ -4133,19 +4158,19 @@
   }
 
   @override
-  JS.Statement visitExpressionStatement(ExpressionStatement node) =>
+  js_ast.Statement visitExpressionStatement(ExpressionStatement node) =>
       node.expression.accept(this).toStatement();
 
   @override
-  JS.EmptyStatement visitEmptyStatement(EmptyStatement node) =>
-      JS.EmptyStatement();
+  js_ast.EmptyStatement visitEmptyStatement(EmptyStatement node) =>
+      js_ast.EmptyStatement();
 
   @override
-  JS.Statement visitAssertStatement(AssertStatement node) =>
+  js_ast.Statement visitAssertStatement(AssertStatement node) =>
       _emitAssert(node.condition, node.message);
 
-  JS.Statement _emitAssert(Expression condition, Expression message) {
-    if (!options.enableAsserts) return JS.EmptyStatement();
+  js_ast.Statement _emitAssert(Expression condition, Expression message) {
+    if (!options.enableAsserts) return js_ast.EmptyStatement();
     // TODO(jmesserly): only emit in checked mode.
     var conditionType = condition.staticType;
     var jsCondition = _visitExpression(condition);
@@ -4165,7 +4190,7 @@
       jsCondition,
       runtimeModule,
       if (message == null)
-        JS.LiteralNull()
+        js_ast.LiteralNull()
       else
         _visitExpression(message),
       js.escapedString(location.sourceUrl.toString()),
@@ -4177,12 +4202,12 @@
   }
 
   @override
-  JS.Statement visitReturnStatement(ReturnStatement node) {
+  js_ast.Statement visitReturnStatement(ReturnStatement node) {
     return super.emitReturnStatement(_visitExpression(node.expression));
   }
 
   @override
-  JS.Statement visitYieldStatement(YieldStatement node) {
+  js_ast.Statement visitYieldStatement(YieldStatement node) {
     var jsExpr = _visitExpression(node.expression);
     var star = node.star != null;
     if (_asyncStarController != null) {
@@ -4201,7 +4226,7 @@
         _asyncStarController,
         helperName,
         jsExpr,
-        JS.Yield(null)..sourceInformation = _nodeStart(node)
+        js_ast.Yield(null)..sourceInformation = _nodeStart(node)
       ]);
     }
     // A normal yield in a sync*
@@ -4209,8 +4234,8 @@
   }
 
   @override
-  JS.Expression visitAwaitExpression(AwaitExpression node) {
-    return JS.Yield(_visitExpression(node.expression));
+  js_ast.Expression visitAwaitExpression(AwaitExpression node) {
+    return js_ast.Yield(_visitExpression(node.expression));
   }
 
   /// This is not used--we emit top-level fields as we are emitting the
@@ -4228,7 +4253,7 @@
   }
 
   @override
-  JS.Statement visitVariableDeclarationStatement(
+  js_ast.Statement visitVariableDeclarationStatement(
       VariableDeclarationStatement node) {
     // Special case a single variable with an initializer.
     // This helps emit cleaner code for things like:
@@ -4239,7 +4264,7 @@
       var initializer = variable.initializer;
       if (initializer != null) {
         var name = _emitVariableDef(variable.name);
-        JS.Expression value;
+        js_ast.Expression value;
         if (_annotatedNullCheck(variable.declaredElement)) {
           value = notNull(initializer);
         } else if (initializer is FunctionExpression) {
@@ -4253,7 +4278,7 @@
           //     dart.fn(f, typeOfF);
           //
           value = _emitArrowFunction(initializer);
-          return JS.Block([
+          return js_ast.Block([
             value.toVariableDeclaration(name),
             _emitFunctionTagged(
                     name, getStaticType(initializer) as FunctionType,
@@ -4270,15 +4295,16 @@
   }
 
   @override
-  JS.VariableDeclarationList visitVariableDeclarationList(
+  js_ast.VariableDeclarationList visitVariableDeclarationList(
       VariableDeclarationList node) {
     if (node == null) return null;
-    return JS.VariableDeclarationList(
+    return js_ast.VariableDeclarationList(
         'let', node.variables?.map(visitVariableDeclaration)?.toList());
   }
 
   @override
-  JS.VariableInitialization visitVariableDeclaration(VariableDeclaration node) {
+  js_ast.VariableInitialization visitVariableDeclaration(
+      VariableDeclaration node) {
     if (node.declaredElement is PropertyInducingElement) {
       // All fields are handled elsewhere.
       assert(false);
@@ -4286,7 +4312,7 @@
     }
 
     var name = _emitVariableDef(node.name);
-    return JS.VariableInitialization(
+    return js_ast.VariableInitialization(
         name, _visitInitializer(node.initializer, node.declaredElement));
   }
 
@@ -4319,35 +4345,36 @@
     return lazyFields;
   }
 
-  JS.Expression _visitInitializer(Expression init, Element variable) {
+  js_ast.Expression _visitInitializer(Expression init, Element variable) {
     // explicitly initialize to null, to avoid getting `undefined`.
     // TODO(jmesserly): do this only for vars that aren't definitely assigned.
-    if (init == null) return JS.LiteralNull();
+    if (init == null) return js_ast.LiteralNull();
     return _annotatedNullCheck(variable)
         ? notNull(init)
         : _visitExpression(init);
   }
 
-  JS.Statement _emitLazyFields(
-      JS.Expression objExpr,
+  js_ast.Statement _emitLazyFields(
+      js_ast.Expression objExpr,
       List<VariableDeclaration> fields,
-      JS.Expression Function(Element e) emitFieldName) {
-    var accessors = <JS.Method>[];
+      js_ast.Expression Function(Element e) emitFieldName) {
+    var accessors = <js_ast.Method>[];
 
     for (var node in fields) {
       var element = node.declaredElement;
       var access = emitFieldName(element);
-      accessors.add(JS.Method(
+      accessors.add(js_ast.Method(
           access,
           js.call('function() { return #; }',
-              _visitInitializer(node.initializer, element)) as JS.Fun,
+              _visitInitializer(node.initializer, element)) as js_ast.Fun,
           isGetter: true)
         ..sourceInformation =
-            _hoverComment(JS.PropertyAccess(objExpr, access), node.name));
+            _hoverComment(js_ast.PropertyAccess(objExpr, access), node.name));
 
       // TODO(jmesserly): currently uses a dummy setter to indicate writable.
       if (!node.isFinal && !node.isConst) {
-        accessors.add(JS.Method(access, js.call('function(_) {}') as JS.Fun,
+        accessors.add(js_ast.Method(
+            access, js.call('function(_) {}') as js_ast.Fun,
             isSetter: true));
       }
     }
@@ -4355,18 +4382,19 @@
     return runtimeStatement('defineLazy(#, { # })', [objExpr, accessors]);
   }
 
-  JS.Expression _emitConstructorName(DartType type, String name) {
+  js_ast.Expression _emitConstructorName(DartType type, String name) {
     return _emitJSInterop(type.element) ??
-        JS.PropertyAccess(emitConstructorAccess(type), _constructorName(name));
+        js_ast.PropertyAccess(
+            emitConstructorAccess(type), _constructorName(name));
   }
 
   @override
-  JS.Expression visitConstructorName(ConstructorName node) {
+  js_ast.Expression visitConstructorName(ConstructorName node) {
     return _emitConstructorName(node.type.type, node.staticElement.name);
   }
 
-  JS.Expression _emitInstanceCreationExpression(
-      ConstructorElement element, InterfaceType type, List<JS.Expression> args,
+  js_ast.Expression _emitInstanceCreationExpression(ConstructorElement element,
+      InterfaceType type, List<js_ast.Expression> args,
       {bool isConst = false, ConstructorName ctorNode}) {
     if (element == null) {
       return _throwUnsafe('unresolved constructor: ${type?.name ?? '<null>'}'
@@ -4375,10 +4403,12 @@
 
     var classElem = type.element;
     if (_isObjectLiteral(classElem)) {
-      return args.isEmpty ? js.call('{}') : args.single as JS.ObjectInitializer;
+      return args.isEmpty
+          ? js.call('{}')
+          : args.single as js_ast.ObjectInitializer;
     }
 
-    JS.Expression emitNew() {
+    js_ast.Expression emitNew() {
       var name = element.name;
       if (args.isEmpty && classElem.source.isInSystemLibrary) {
         // Skip the slow SDK factory constructors when possible.
@@ -4414,8 +4444,8 @@
       var ctor = _emitConstructorName(type, name);
       if (ctorNode != null) ctor.sourceInformation = _nodeSpan(ctorNode);
       return element.isFactory && !_hasJSInteropAnnotation(classElem)
-          ? JS.Call(ctor, args)
-          : JS.New(ctor, args);
+          ? js_ast.Call(ctor, args)
+          : js_ast.New(ctor, args);
     }
 
     var result = emitNew();
@@ -4443,10 +4473,10 @@
 
   /// If the constant [value] is primitive, directly emit the
   /// corresponding JavaScript.  Otherwise, return null.
-  JS.Expression _emitDartObject(DartObject value,
+  js_ast.Expression _emitDartObject(DartObject value,
       {bool handleUnknown = false}) {
     if (value == null || value.isNull) {
-      return JS.LiteralNull();
+      return js_ast.LiteralNull();
     }
     var type = value.type;
     // Handle unknown value: when the declared variable wasn't found, and no
@@ -4457,7 +4487,7 @@
     // https://api.dartlang.org/stable/1.20.1/dart-core/bool/bool.fromEnvironment.html
     if (!value.hasKnownValue) {
       if (!handleUnknown) return null;
-      return type == types.boolType ? js.boolean(false) : JS.LiteralNull();
+      return type == types.boolType ? js.boolean(false) : js_ast.LiteralNull();
     }
     if (type == types.boolType) {
       return js.boolean(value.toBoolValue());
@@ -4484,7 +4514,7 @@
             value.toListValue().map(_emitDartObject).toList());
       }
       if (type.element == types.mapType.element) {
-        var entries = <JS.Expression>[];
+        var entries = <js_ast.Expression>[];
         value.toMapValue().forEach((key, value) {
           entries.add(_emitDartObject(key));
           entries.add(_emitDartObject(value));
@@ -4506,11 +4536,12 @@
           return _emitClassMemberElement(field, field.getter, null);
         }
         var args = ctor.positionalArguments.map(_emitDartObject).toList();
-        var named = <JS.Property>[];
+        var named = <js_ast.Property>[];
         ctor.namedArguments.forEach((name, value) {
-          named.add(JS.Property(propertyName(name), _emitDartObject(value)));
+          named
+              .add(js_ast.Property(propertyName(name), _emitDartObject(value)));
         });
-        if (named.isNotEmpty) args.add(JS.ObjectInitializer(named));
+        if (named.isNotEmpty) args.add(js_ast.ObjectInitializer(named));
         return _emitInstanceCreationExpression(ctor.constructor, type, args,
             isConst: true);
       }
@@ -4564,19 +4595,19 @@
         ctorNode: constructor);
   }
 
-  JS.Statement _nullParameterCheck(JS.Expression param) {
+  js_ast.Statement _nullParameterCheck(js_ast.Expression param) {
     var call = runtimeCall('argumentError((#))', [param]);
     return js.statement('if (# == null) #;', [param, call]);
   }
 
-  JS.Expression notNull(Expression expr) {
+  js_ast.Expression notNull(Expression expr) {
     if (expr == null) return null;
     var jsExpr = _visitExpression(expr);
     if (!isNullable(expr)) return jsExpr;
     return runtimeCall('notNull(#)', [jsExpr]);
   }
 
-  JS.Expression _emitEqualityOperator(BinaryExpression node, Token op) {
+  js_ast.Expression _emitEqualityOperator(BinaryExpression node, Token op) {
     var left = node.leftOperand;
     var right = node.rightOperand;
     var leftType = left.staticType;
@@ -4632,7 +4663,7 @@
   }
 
   @override
-  JS.Expression visitBinaryExpression(BinaryExpression node) {
+  js_ast.Expression visitBinaryExpression(BinaryExpression node) {
     var op = node.operator;
 
     // The operands of logical boolean operators are subject to boolean
@@ -4653,10 +4684,10 @@
       // This should be a hint or warning for dead code.
       if (!isNullable(left)) return _visitExpression(left);
 
-      var vars = <JS.MetaLetVariable, JS.Expression>{};
+      var vars = <js_ast.MetaLetVariable, js_ast.Expression>{};
       // Desugar `l ?? r` as `l != null ? l : r`
       var l = _visitExpression(_bindValue(vars, 'l', left, context: left));
-      return JS.MetaLet(vars, [
+      return js_ast.MetaLet(vars, [
         js.call('# != null ? # : #', [l, l, _visitExpression(right)])
       ]);
     }
@@ -4664,7 +4695,7 @@
     var leftType = getStaticType(left);
     var rightType = getStaticType(right);
 
-    JS.Expression operatorCall() {
+    js_ast.Expression operatorCall() {
       return _emitOperatorCall(left, op.lexeme, [right])
         ..sourceInformation = _getLocation(node.operator.offset);
     }
@@ -4677,12 +4708,12 @@
 
       /// Emits an inlined binary operation using the JS [code], adding null
       /// checks if needed to ensure we throw the appropriate error.
-      JS.Expression binary(String code) {
+      js_ast.Expression binary(String code) {
         return js.call(code, [notNull(left), notNull(right)])
           ..sourceInformation = _getLocation(node.operator.offset);
       }
 
-      JS.Expression bitwise(String code) {
+      js_ast.Expression bitwise(String code) {
         return _coerceBitOperationResultToUnsigned(node, binary(code));
       }
 
@@ -4691,7 +4722,7 @@
       ///
       /// Short circuiting operators should not be used in [code], because the
       /// null checks for both operands must happen unconditionally.
-      JS.Expression bitwiseBool(String code) {
+      js_ast.Expression bitwiseBool(String code) {
         return js.call(code, [notNull(left), _visitTest(right)])
           ..sourceInformation = _getLocation(node.operator.offset);
       }
@@ -4764,8 +4795,8 @@
   /// the interpretation of the 32-bit value from signed to unsigned.  Most
   /// JavaScript operations interpret their operands as signed and generate
   /// signed results.
-  JS.Expression _coerceBitOperationResultToUnsigned(
-      Expression node, JS.Expression uncoerced) {
+  js_ast.Expression _coerceBitOperationResultToUnsigned(
+      Expression node, js_ast.Expression uncoerced) {
     // Don't coerce if the parent will coerce.
     AstNode parent = _parentOperation(node);
     if (_nodeIsBitwiseOperation(parent)) return uncoerced;
@@ -4936,7 +4967,7 @@
       expr is NullLiteral || getStaticType(expr).isDartCoreNull;
 
   SimpleIdentifier _createTemporary(String name, DartType type,
-      {bool nullable = true, JS.Expression variable, bool dynamicInvoke}) {
+      {bool nullable = true, js_ast.Expression variable, bool dynamicInvoke}) {
     // We use an invalid source location to signal that this is a temporary.
     // See [_isTemporary].
     // TODO(jmesserly): alternatives are
@@ -4947,7 +4978,7 @@
     var id = astFactory
         .simpleIdentifier(StringToken(TokenType.IDENTIFIER, name, -1));
 
-    variable ??= JS.TemporaryId(name);
+    variable ??= js_ast.TemporaryId(name);
 
     var idElement =
         TemporaryVariableElement.forNode(id, variable, _currentElement);
@@ -4975,7 +5006,7 @@
   /// unless [expr] is a SimpleIdentifier, in which case a temporary is not
   /// needed.
   Expression _bindLeftHandSide(
-      Map<JS.MetaLetVariable, JS.Expression> scope, Expression expr,
+      Map<js_ast.MetaLetVariable, js_ast.Expression> scope, Expression expr,
       {Expression context}) {
     Expression result;
     if (expr is IndexExpression) {
@@ -5019,13 +5050,13 @@
   /// variables), then the resulting code will be simplified automatically.
   ///
   /// [scope] will be mutated to contain the new temporary's initialization.
-  Expression _bindValue(Map<JS.MetaLetVariable, JS.Expression> scope,
+  Expression _bindValue(Map<js_ast.MetaLetVariable, js_ast.Expression> scope,
       String name, Expression expr,
       {Expression context}) {
     // No need to do anything for stateless expressions.
     if (isStateless(_currentFunction, expr, context)) return expr;
 
-    var variable = JS.MetaLetVariable(name);
+    var variable = js_ast.MetaLetVariable(name);
     var t = _createTemporary(name, getStaticType(expr),
         variable: variable,
         dynamicInvoke: isDynamicInvoke(expr),
@@ -5050,10 +5081,10 @@
   ///     // pseudocode mix of Scheme and JS:
   ///     (let* (x1=expr1, x2=expr2, t=expr1[expr2]) { x1[x2] = t + 1; t })
   ///
-  /// The [JS.MetaLet] nodes automatically simplify themselves if they can.
+  /// The [js_ast.MetaLet] nodes automatically simplify themselves if they can.
   /// For example, if the result value is not used, then `t` goes away.
   @override
-  JS.Expression visitPostfixExpression(PostfixExpression node) {
+  js_ast.Expression visitPostfixExpression(PostfixExpression node) {
     var op = node.operator;
     var expr = node.operand;
 
@@ -5073,7 +5104,7 @@
 
     // Handle the left hand side, to ensure each of its subexpressions are
     // evaluated only once.
-    var vars = <JS.MetaLetVariable, JS.Expression>{};
+    var vars = <js_ast.MetaLetVariable, js_ast.Expression>{};
     var left = _bindLeftHandSide(vars, expr, context: expr);
 
     // Desugar `x++` as `(x1 = x0 + 1, x0)` where `x0` is the original value
@@ -5085,12 +5116,15 @@
       ..staticElement = node.staticElement
       ..staticType = getStaticType(expr);
 
-    var body = <JS.Expression>[_emitSet(left, increment), _visitExpression(x)];
-    return JS.MetaLet(vars, body, statelessResult: true);
+    var body = <js_ast.Expression>[
+      _emitSet(left, increment),
+      _visitExpression(x)
+    ];
+    return js_ast.MetaLet(vars, body, statelessResult: true);
   }
 
   @override
-  JS.Expression visitPrefixExpression(PrefixExpression node) {
+  js_ast.Expression visitPrefixExpression(PrefixExpression node) {
     var op = node.operator;
 
     // Logical negation, `!e`, is a boolean conversion context since it is
@@ -5103,7 +5137,7 @@
     if (jsTypeRep.unaryOperationIsPrimitive(dispatchType)) {
       if (op.lexeme == '~') {
         if (jsTypeRep.isNumber(dispatchType)) {
-          JS.Expression jsExpr = js.call('~#', notNull(expr));
+          js_ast.Expression jsExpr = js.call('~#', notNull(expr));
           return _coerceBitOperationResultToUnsigned(node, jsExpr);
         }
         return _emitOperatorCall(expr, op.lexeme[0], []);
@@ -5113,7 +5147,7 @@
       }
       if (op.lexeme == '++' || op.lexeme == '--') {
         // We need a null check, so the increment must be expanded out.
-        var vars = <JS.MetaLetVariable, JS.Expression>{};
+        var vars = <js_ast.MetaLetVariable, js_ast.Expression>{};
         var x = _bindLeftHandSide(vars, expr, context: expr);
 
         var one = ast.integerLiteral(1)..staticType = types.intType;
@@ -5121,7 +5155,7 @@
           ..staticElement = node.staticElement
           ..staticType = getStaticType(expr);
 
-        return JS.MetaLet(vars, [_emitSet(x, increment)]);
+        return js_ast.MetaLet(vars, [_emitSet(x, increment)]);
       }
       return js.call('$op#', notNull(expr));
     }
@@ -5148,33 +5182,35 @@
   visitCascadeExpression(CascadeExpression node) {
     var savedCascadeTemp = _cascadeTarget;
 
-    var vars = <JS.MetaLetVariable, JS.Expression>{};
+    var vars = <js_ast.MetaLetVariable, js_ast.Expression>{};
     _cascadeTarget = _bindValue(vars, '_', node.target, context: node);
     var sections = _visitExpressionList(node.cascadeSections);
     sections.add(_visitExpression(_cascadeTarget));
-    var result = JS.MetaLet(vars, sections, statelessResult: true);
+    var result = js_ast.MetaLet(vars, sections, statelessResult: true);
     _cascadeTarget = savedCascadeTemp;
     return result;
   }
 
   @override
-  JS.Expression visitParenthesizedExpression(ParenthesizedExpression node) =>
+  js_ast.Expression visitParenthesizedExpression(
+          ParenthesizedExpression node) =>
       // The printer handles precedence so we don't need to.
       _visitExpression(node.expression);
 
-  JS.Parameter _emitFormalParameter(FormalParameter node) {
+  js_ast.Parameter _emitFormalParameter(FormalParameter node) {
     var id = _emitParameter(node.declaredElement, declaration: true)
       ..sourceInformation = _nodeSpan(node);
     var isRestArg = node is! DefaultFormalParameter &&
         findAnnotation(node.declaredElement, isJsRestAnnotation) != null;
-    return isRestArg ? JS.RestParameter(id) : id;
+    return isRestArg ? js_ast.RestParameter(id) : id;
   }
 
   @override
-  JS.This visitThisExpression(ThisExpression node) => JS.This();
+  js_ast.This visitThisExpression(ThisExpression node) => js_ast.This();
 
   @override
-  JS.Expression visitSuperExpression(SuperExpression node) => JS.Super();
+  js_ast.Expression visitSuperExpression(SuperExpression node) =>
+      js_ast.Super();
 
   @override
   visitPrefixedIdentifier(PrefixedIdentifier node) {
@@ -5198,14 +5234,14 @@
     return _emitPropertyGet(_getTarget(node), node.propertyName, node);
   }
 
-  JS.Expression _emitNullSafe(Expression node) {
+  js_ast.Expression _emitNullSafe(Expression node) {
     // Desugar `obj?.name` as ((x) => x == null ? null : x.name)(obj)
     var target = _getTarget(node);
-    var vars = <JS.MetaLetVariable, JS.Expression>{};
+    var vars = <js_ast.MetaLetVariable, js_ast.Expression>{};
     var t = _bindValue(vars, 't', target, context: target);
 
     var desugared = _stripNullAwareOp(node, t);
-    return JS.MetaLet(vars, [
+    return js_ast.MetaLet(vars, [
       js.call('# == null ? null : #',
           [_visitExpression(t), _visitExpression(desugared)])
     ]);
@@ -5226,7 +5262,7 @@
     }
   }
 
-  List<JS.Expression> _getTypeArgs(Element member, DartType instantiated) {
+  List<js_ast.Expression> _getTypeArgs(Element member, DartType instantiated) {
     DartType type;
     if (member is ExecutableElement) {
       type = member.type;
@@ -5240,7 +5276,7 @@
   }
 
   /// Shared code for [PrefixedIdentifier] and [PropertyAccess].
-  JS.Expression _emitPropertyGet(
+  js_ast.Expression _emitPropertyGet(
       Expression receiver, SimpleIdentifier memberId, Expression accessNode) {
     var resultType = accessNode.staticType;
     var accessor = memberId.staticElement;
@@ -5262,17 +5298,17 @@
     }
 
     var jsTarget = _emitTarget(receiver, accessor, isStatic);
-    var isSuper = jsTarget is JS.Super;
+    var isSuper = jsTarget is js_ast.Super;
     if (isSuper &&
         accessor.isSynthetic &&
         field is FieldElementImpl &&
         !virtualFields.isVirtual(field)) {
       // If super.x is a sealed field, then x is an instance property since
       // subclasses cannot override x.
-      jsTarget = JS.This()..sourceInformation = jsTarget.sourceInformation;
+      jsTarget = js_ast.This()..sourceInformation = jsTarget.sourceInformation;
     }
 
-    JS.Expression result;
+    js_ast.Expression result;
     if (isObjectMember(memberName) && isNullable(receiver)) {
       if (_isObjectMethodTearoff(memberName)) {
         result = runtimeCall('bind(#, #)', [jsTarget, jsName]);
@@ -5311,7 +5347,7 @@
   bool _isDirectCallable(DartType t) =>
       t is FunctionType || t is InterfaceType && _usesJSInterop(t.element);
 
-  JS.Expression _getImplicitCallTarget(InterfaceType fromType) {
+  js_ast.Expression _getImplicitCallTarget(InterfaceType fromType) {
     var callMethod = fromType.lookUpInheritedMethod('call');
     if (callMethod == null || _usesJSInterop(fromType.element)) return null;
     return _emitMemberName('call', type: fromType, element: callMethod);
@@ -5322,7 +5358,7 @@
   /// **Please note** this function does not support method invocation syntax
   /// `obj.name(args)` because that could be a getter followed by a call.
   /// See [visitMethodInvocation].
-  JS.Expression _emitOperatorCall(
+  js_ast.Expression _emitOperatorCall(
       Expression target, String name, List<Expression> args,
       [Element element]) {
     // TODO(jmesserly): calls that don't pass `element` are probably broken for
@@ -5352,7 +5388,7 @@
   visitIndexExpression(IndexExpression node) {
     var target = _getTarget(node);
     if (_useNativeJsIndexer(target.staticType)) {
-      return JS.PropertyAccess(
+      return js_ast.PropertyAccess(
           _visitExpression(target), _visitExpression(node.index));
     }
     return _emitOperatorCall(target, '[]', [node.index], node.staticElement);
@@ -5378,7 +5414,7 @@
   }
 
   @override
-  JS.Expression visitConditionalExpression(ConditionalExpression node) {
+  js_ast.Expression visitConditionalExpression(ConditionalExpression node) {
     return js.call('# ? # : #', [
       _visitTest(node.condition),
       _visitExpression(node.thenExpression),
@@ -5387,12 +5423,12 @@
   }
 
   @override
-  JS.Expression visitThrowExpression(ThrowExpression node) {
+  js_ast.Expression visitThrowExpression(ThrowExpression node) {
     return runtimeCall('throw(#)', [_visitExpression(node.expression)]);
   }
 
   @override
-  JS.Expression visitRethrowExpression(RethrowExpression node) {
+  js_ast.Expression visitRethrowExpression(RethrowExpression node) {
     return runtimeCall(
         'rethrow(#)', [_emitSimpleIdentifier(_rethrowParameter)]);
   }
@@ -5403,32 +5439,33 @@
   ///
   ///     do var x = 5; while (false); // Dart
   ///     do { let x = 5; } while (false); // JS
-  JS.Statement _visitScope(Statement stmt) {
+  js_ast.Statement _visitScope(Statement stmt) {
     var result = _visitStatement(stmt);
-    if (result is JS.ExpressionStatement &&
-        result.expression is JS.VariableDeclarationList) {
-      return JS.Block([result]);
+    if (result is js_ast.ExpressionStatement &&
+        result.expression is js_ast.VariableDeclarationList) {
+      return js_ast.Block([result]);
     }
     return result;
   }
 
   @override
-  JS.Statement visitIfStatement(IfStatement node) {
-    return JS.If(_visitTest(node.condition), _visitScope(node.thenStatement),
-        _visitScope(node.elseStatement));
+  js_ast.Statement visitIfStatement(IfStatement node) {
+    return js_ast.If(_visitTest(node.condition),
+        _visitScope(node.thenStatement), _visitScope(node.elseStatement));
   }
 
   @override
-  JS.While visitWhileStatement(WhileStatement node) {
-    return JS.While(_visitTest(node.condition), _visitScope(node.body));
+  js_ast.While visitWhileStatement(WhileStatement node) {
+    return js_ast.While(_visitTest(node.condition), _visitScope(node.body));
   }
 
   @override
-  JS.Do visitDoStatement(DoStatement node) {
-    return JS.Do(_visitScope(node.body), _visitTest(node.condition));
+  js_ast.Do visitDoStatement(DoStatement node) {
+    return js_ast.Do(_visitScope(node.body), _visitTest(node.condition));
   }
 
-  JS.Statement _emitAwaitFor(ForEachParts forParts, JS.Statement jsBody) {
+  js_ast.Statement _emitAwaitFor(
+      ForEachParts forParts, js_ast.Statement jsBody) {
     // Emits `await for (var value in stream) ...`, which desugars as:
     //
     // let iter = new StreamIterator(stream);
@@ -5452,9 +5489,9 @@
         streamIterator.element.unnamedConstructor,
         streamIterator,
         [_visitExpression(forParts.iterable)]);
-    var iter = JS.TemporaryId('iter');
+    var iter = js_ast.TemporaryId('iter');
     SimpleIdentifier variable;
-    JS.Expression init;
+    js_ast.Expression init;
     if (forParts is ForEachPartsWithIdentifier) {
       variable = forParts.identifier;
       init = js
@@ -5475,11 +5512,11 @@
         [
           iter,
           createStreamIter,
-          JS.Yield(js.call('#.moveNext()', iter))
+          js_ast.Yield(js.call('#.moveNext()', iter))
             ..sourceInformation = _nodeStart(variable),
           init,
           jsBody,
-          JS.Yield(js.call('#.cancel()', iter))
+          js_ast.Yield(js.call('#.cancel()', iter))
             ..sourceInformation = _nodeStart(variable)
         ]);
   }
@@ -5487,7 +5524,7 @@
   @override
   visitBreakStatement(BreakStatement node) {
     var label = node.label;
-    return JS.Break(label?.name);
+    return js_ast.Break(label?.name);
   }
 
   @override
@@ -5521,7 +5558,7 @@
         }
       }
     }
-    return JS.Continue(label?.name);
+    return js_ast.Continue(label?.name);
   }
 
   @override
@@ -5530,11 +5567,11 @@
     _superAllowed = false;
     var finallyBlock = _visitStatement(node.finallyBlock)?.toBlock();
     _superAllowed = savedSuperAllowed;
-    return JS.Try(_visitStatement(node.body).toBlock(),
+    return js_ast.Try(_visitStatement(node.body).toBlock(),
         _visitCatch(node.catchClauses), finallyBlock);
   }
 
-  JS.Catch _visitCatch(NodeList<CatchClause> clauses) {
+  js_ast.Catch _visitCatch(NodeList<CatchClause> clauses) {
     if (clauses == null || clauses.isEmpty) return null;
 
     var caughtError = _createTemporary('e', types.dynamicType);
@@ -5554,7 +5591,8 @@
                 : null);
 
     // Rethrow if the exception type didn't match.
-    JS.Statement catchBody = JS.Throw(_emitSimpleIdentifier(caughtError));
+    js_ast.Statement catchBody =
+        js_ast.Throw(_emitSimpleIdentifier(caughtError));
     for (var clause in clauses.reversed) {
       catchBody = _catchClauseGuard(
           clause, catchBody, exceptionParameter, stackTraceParameter);
@@ -5575,17 +5613,17 @@
     }
     catchStatements.add(catchBody);
 
-    var catchVarDecl = _emitSimpleIdentifier(caughtError) as JS.Identifier;
+    var catchVarDecl = _emitSimpleIdentifier(caughtError) as js_ast.Identifier;
     _rethrowParameter = savedRethrow;
-    return JS.Catch(catchVarDecl, JS.Block(catchStatements));
+    return js_ast.Catch(catchVarDecl, js_ast.Block(catchStatements));
   }
 
-  JS.Statement _catchClauseGuard(
+  js_ast.Statement _catchClauseGuard(
       CatchClause node,
-      JS.Statement otherwise,
+      js_ast.Statement otherwise,
       SimpleIdentifier exceptionParameter,
       SimpleIdentifier stackTraceParameter) {
-    var body = <JS.Statement>[];
+    var body = <js_ast.Statement>[];
     var vars = HashSet<String>();
 
     void declareVariable(SimpleIdentifier variable, SimpleIdentifier value) {
@@ -5603,7 +5641,7 @@
     }
 
     body.add(_visitStatement(node.body).toScopedBlock(vars));
-    var then = JS.Statement.from(body);
+    var then = js_ast.Statement.from(body);
 
     // Discard following clauses, if any, as they are unreachable.
     if (node.exceptionType == null ||
@@ -5613,32 +5651,32 @@
 
     var condition =
         _emitIsExpression(exceptionParameter, node.exceptionType.type);
-    return JS.If(condition, then, otherwise)
+    return js_ast.If(condition, then, otherwise)
       ..sourceInformation = _nodeStart(node);
   }
 
   @override
-  JS.SwitchCase visitSwitchCase(SwitchCase node) {
+  js_ast.SwitchCase visitSwitchCase(SwitchCase node) {
     var expr = _visitExpression(node.expression);
     var body = _visitStatementList(node.statements);
     if (node.labels.isNotEmpty) {
       body.insert(0, js.comment('Unimplemented case labels: ${node.labels}'));
     }
     // TODO(jmesserly): make sure we are statically checking fall through
-    return JS.SwitchCase(expr, JS.Block(body));
+    return js_ast.SwitchCase(expr, js_ast.Block(body));
   }
 
   @override
-  JS.SwitchCase visitSwitchDefault(SwitchDefault node) {
+  js_ast.SwitchCase visitSwitchDefault(SwitchDefault node) {
     var body = _visitStatementList(node.statements);
     if (node.labels.isNotEmpty) {
       body.insert(0, js.comment('Unimplemented case labels: ${node.labels}'));
     }
     // TODO(jmesserly): make sure we are statically checking fall through
-    return JS.SwitchCase.defaultCase(JS.Block(body));
+    return js_ast.SwitchCase.defaultCase(js_ast.Block(body));
   }
 
-  JS.SwitchCase _emitSwitchMember(SwitchMember node) {
+  js_ast.SwitchCase _emitSwitchMember(SwitchMember node) {
     if (node is SwitchCase) {
       return visitSwitchCase(node);
     } else {
@@ -5647,15 +5685,15 @@
   }
 
   @override
-  JS.Switch visitSwitchStatement(SwitchStatement node) => JS.Switch(
+  js_ast.Switch visitSwitchStatement(SwitchStatement node) => js_ast.Switch(
       _visitExpression(node.expression),
       node.members?.map(_emitSwitchMember)?.toList());
 
   @override
-  JS.Statement visitLabeledStatement(LabeledStatement node) {
+  js_ast.Statement visitLabeledStatement(LabeledStatement node) {
     var result = _visitStatement(node.statement);
     for (var label in node.labels.reversed) {
-      result = JS.LabeledStatement(label.label.name, result);
+      result = js_ast.LabeledStatement(label.label.name, result);
     }
     return result;
   }
@@ -5682,14 +5720,14 @@
           AnalysisError(_currentCompilationUnit.source, node.offset,
               node.length, invalidJSInteger, [lexeme, nearest]));
     }
-    return JS.LiteralNumber('$valueInJS');
+    return js_ast.LiteralNumber('$valueInJS');
   }
 
   @override
   visitDoubleLiteral(DoubleLiteral node) => js.number(node.value);
 
   @override
-  visitNullLiteral(NullLiteral node) => JS.LiteralNull();
+  visitNullLiteral(NullLiteral node) => js_ast.LiteralNull();
 
   @override
   visitSymbolLiteral(SymbolLiteral node) {
@@ -5697,7 +5735,7 @@
   }
 
   @override
-  JS.Expression visitListLiteral(ListLiteral node) {
+  js_ast.Expression visitListLiteral(ListLiteral node) {
     var elementType = (node.staticType as InterfaceType).typeArguments[0];
     var elements = _visitCollectionElementList(node.elements, elementType);
     if (!node.isConst) {
@@ -5706,7 +5744,7 @@
     return _emitConstList(elementType, elements);
   }
 
-  JS.Expression _emitSetLiteral(SetOrMapLiteral node) {
+  js_ast.Expression _emitSetLiteral(SetOrMapLiteral node) {
     var type = node.staticType as InterfaceType;
     var elementType = type.typeArguments[0];
     var jsElements = _visitCollectionElementList(node.elements, elementType);
@@ -5721,16 +5759,17 @@
         runtimeCall('constSet(#, [#])', [_emitType(elementType), jsElements]));
   }
 
-  JS.Expression _emitConstList(
-      DartType elementType, List<JS.Expression> elements) {
+  js_ast.Expression _emitConstList(
+      DartType elementType, List<js_ast.Expression> elements) {
     // dart.constList helper internally depends on _interceptors.JSArray.
     _declareBeforeUse(_jsArray);
     return cacheConst(
         runtimeCall('constList([#], #)', [elements, _emitType(elementType)]));
   }
 
-  JS.Expression _emitList(DartType itemType, List<JS.Expression> items) {
-    var list = JS.ArrayInitializer(items);
+  js_ast.Expression _emitList(
+      DartType itemType, List<js_ast.Expression> items) {
+    var list = js_ast.ArrayInitializer(items);
 
     // TODO(jmesserly): analyzer will usually infer `List<Object>` because
     // that is the least upper bound of the element types. So we rarely
@@ -5742,7 +5781,7 @@
     return js.call('#.of(#)', [_emitType(arrayType), list]);
   }
 
-  JS.Expression _emitMapLiteral(SetOrMapLiteral node) {
+  js_ast.Expression _emitMapLiteral(SetOrMapLiteral node) {
     var type = node.staticType as InterfaceType;
     var elementType = type.typeArguments[0];
     var jsElements = _visitCollectionElementList(node.elements, elementType);
@@ -5756,13 +5795,14 @@
     return _emitConstMap(type, jsElements);
   }
 
-  JS.Expression _emitConstMap(InterfaceType type, List<JS.Expression> entries) {
+  js_ast.Expression _emitConstMap(
+      InterfaceType type, List<js_ast.Expression> entries) {
     var typeArgs = type.typeArguments;
     return cacheConst(runtimeCall('constMap(#, #, [#])',
         [_emitType(typeArgs[0]), _emitType(typeArgs[1]), entries]));
   }
 
-  JS.Expression _emitMapImplType(InterfaceType type, {bool identity}) {
+  js_ast.Expression _emitMapImplType(InterfaceType type, {bool identity}) {
     var typeArgs = type.typeArguments;
     if (typeArgs.isEmpty) return _emitType(type);
     identity ??= jsTypeRep.isPrimitive(typeArgs[0]);
@@ -5770,7 +5810,7 @@
     return _emitType(type.instantiate(typeArgs));
   }
 
-  JS.Expression _emitSetImplType(InterfaceType type, {bool identity}) {
+  js_ast.Expression _emitSetImplType(InterfaceType type, {bool identity}) {
     var typeArgs = type.typeArguments;
     if (typeArgs.isEmpty) return _emitType(type);
     identity ??= jsTypeRep.isPrimitive(typeArgs[0]);
@@ -5779,19 +5819,19 @@
   }
 
   @override
-  JS.LiteralString visitSimpleStringLiteral(SimpleStringLiteral node) =>
+  js_ast.LiteralString visitSimpleStringLiteral(SimpleStringLiteral node) =>
       js.escapedString(node.value, '"');
 
   @override
-  JS.Expression visitAdjacentStrings(AdjacentStrings node) {
+  js_ast.Expression visitAdjacentStrings(AdjacentStrings node) {
     var nodes = node.strings;
     if (nodes == null || nodes.isEmpty) return null;
-    return JS.Expression.binary(_visitExpressionList(nodes), '+');
+    return js_ast.Expression.binary(_visitExpressionList(nodes), '+');
   }
 
   @override
-  JS.Expression visitStringInterpolation(StringInterpolation node) {
-    var parts = <JS.Expression>[];
+  js_ast.Expression visitStringInterpolation(StringInterpolation node) {
+    var parts = <js_ast.Expression>[];
     for (var elem in node.elements) {
       if (elem is InterpolationString) {
         if (elem.value.isEmpty) continue;
@@ -5805,7 +5845,7 @@
       }
     }
     if (parts.isEmpty) return js.string('');
-    return JS.Expression.binary(parts, '+');
+    return js_ast.Expression.binary(parts, '+');
   }
 
   @override
@@ -5818,33 +5858,33 @@
   /// Visit a Dart [node] that produces a JS expression, and attaches a source
   /// location.
   // TODO(jmesserly): parameter type should be `Expression`
-  JS.Expression _visitExpression(AstNode node) {
+  js_ast.Expression _visitExpression(AstNode node) {
     if (node == null) return null;
-    var e = node.accept<JS.Node>(this) as JS.Expression;
+    var e = node.accept<js_ast.Node>(this) as js_ast.Expression;
     e.sourceInformation ??= _nodeStart(node);
     return e;
   }
 
   /// Visits [nodes] with [_visitExpression].
-  List<JS.Expression> _visitExpressionList(Iterable<AstNode> nodes) {
+  List<js_ast.Expression> _visitExpressionList(Iterable<AstNode> nodes) {
     return nodes?.map(_visitExpression)?.toList();
   }
 
   /// Visit a Dart [node] that produces a JS statement, and marks its source
   /// location for debugging.
-  JS.Statement _visitStatement(AstNode node) {
+  js_ast.Statement _visitStatement(AstNode node) {
     if (node == null) return null;
-    var s = node.accept<JS.Node>(this) as JS.Statement;
+    var s = node.accept<js_ast.Node>(this) as js_ast.Statement;
     if (s is! Block) s.sourceInformation = _nodeStart(node);
     return s;
   }
 
   /// Visits [nodes] with [_visitStatement].
-  List<JS.Statement> _visitStatementList(Iterable<AstNode> nodes) {
+  List<js_ast.Statement> _visitStatementList(Iterable<AstNode> nodes) {
     return nodes?.map(_visitStatement)?.toList();
   }
 
-  /// Returns a [JS.Expression] for each [CollectionElement] in [nodes].
+  /// Returns a [js_ast.Expression] for each [CollectionElement] in [nodes].
   ///
   /// Visits all [nodes] in order and nested [CollectionElement]s depth first
   /// to produce [JS.Expresison]s intended to be used when outputing a
@@ -5862,7 +5902,7 @@
   ///       if (true) for (let i = 2; i < 10; i++) temp.push(i);
   ///       return temp;
   ///     })(), 10]
-  List<JS.Expression> _visitCollectionElementList(
+  List<js_ast.Expression> _visitCollectionElementList(
       Iterable<CollectionElement> nodes, DartType elementType) {
     /// Returns [body] wrapped in a function and a call.
     ///
@@ -5870,19 +5910,20 @@
     /// fucntion, call it, and yield the result of [yieldType].
     /// TODO(nshahan) Move to share between compilers. Need to work out a common
     /// emitLibraryName().
-    JS.Expression detectYieldAndCall(JS.Block body, InterfaceType yieldType) {
+    js_ast.Expression detectYieldAndCall(
+        js_ast.Block body, InterfaceType yieldType) {
       var finder = YieldFinder();
       body.accept(finder);
       if (finder.hasYield) {
-        var genFn = JS.Fun([], body, isGenerator: true);
+        var genFn = js_ast.Fun([], body, isGenerator: true);
         var asyncLibrary = emitLibraryName(types.futureType.element.library);
-        return JS.Yield(js.call(
+        return js_ast.Yield(js.call(
             '#.async(#, #)', [asyncLibrary, _emitType(yieldType), genFn]));
       }
-      return JS.Call(JS.ArrowFun([], body), []);
+      return js_ast.Call(js_ast.ArrowFun([], body), []);
     }
 
-    var expressions = <JS.Expression>[];
+    var expressions = <js_ast.Expression>[];
     for (var node in nodes) {
       if (_isUiAsCodeElement(node)) {
         // Create a temporary variable to build a new collection from.
@@ -5895,15 +5936,15 @@
 
         // Build up a list for the control-flow-collections element and wrap in
         // a function call that returns the list.
-        var functionBody = JS.Block([
+        var functionBody = js_ast.Block([
           items,
-          node.accept<JS.Node>(this) as JS.Statement,
-          JS.Return(_currentCollectionVariable)
+          node.accept<js_ast.Node>(this) as js_ast.Statement,
+          js_ast.Return(_currentCollectionVariable)
         ]);
         var functionCall = detectYieldAndCall(functionBody, arrayType);
 
         // Finally, spread the temporary control-flow-collections list.
-        expressions.add(JS.Spread(functionCall));
+        expressions.add(js_ast.Spread(functionCall));
         _currentCollectionVariable = previousCollectionVariable;
       } else if (node is MapLiteralEntry) {
         expressions.add(_visitExpression(node.key));
@@ -5917,12 +5958,12 @@
 
   /// Visits [node] with [_visitExpression] and wraps the result in a call to
   /// append it to the list tracked by [_currentCollectionVariable].
-  JS.Statement _visitNestedCollectionElement(CollectionElement node) {
-    JS.Statement pushToCurrentCollection(Expression value) => js.statement(
+  js_ast.Statement _visitNestedCollectionElement(CollectionElement node) {
+    js_ast.Statement pushToCurrentCollection(Expression value) => js.statement(
         '#.push(#)', [_currentCollectionVariable, _visitExpression(value)]);
 
     if (node is MapLiteralEntry) {
-      return JS.Block([
+      return js_ast.Block([
         pushToCurrentCollection(node.key),
         pushToCurrentCollection(node.value)
       ]);
@@ -5987,7 +6028,7 @@
   /// For example, top-level and static fields are defined as lazy properties,
   /// on the library/class, so their access expressions do not appear in the
   /// source code.
-  HoverComment _hoverComment(JS.Expression expr, AstNode node) {
+  HoverComment _hoverComment(js_ast.Expression expr, AstNode node) {
     var start = _getLocation(node.offset);
     var end = _getLocation(node.end);
     return start != null && end != null ? HoverComment(expr, start, end) : null;
@@ -6022,7 +6063,7 @@
   /// to give a more helpful message.
   // TODO(sra): When nullablility is available earlier, it would be cleaner to
   // build an input AST where the boolean conversion is a single AST node.
-  JS.Expression _visitTest(Expression node) {
+  js_ast.Expression _visitTest(Expression node) {
     if (node == null) return null;
 
     if (node is PrefixExpression && node.operator.lexeme == '!') {
@@ -6034,7 +6075,7 @@
       return _visitTest(node.expression);
     }
     if (node is BinaryExpression) {
-      JS.Expression shortCircuit(String code) {
+      js_ast.Expression shortCircuit(String code) {
         return js.call(code,
             [_visitTest(node.leftOperand), _visitTest(node.rightOperand)]);
       }
@@ -6056,7 +6097,8 @@
   ///
   /// Unlike call sites, we always have an element available, so we can use it
   /// directly rather than computing the relevant options for [_emitMemberName].
-  JS.Expression _declareMemberName(ExecutableElement e, {bool useExtension}) {
+  js_ast.Expression _declareMemberName(ExecutableElement e,
+      {bool useExtension}) {
     return _emitMemberName(_getElementName(e),
         isStatic: e.isStatic,
         useExtension:
@@ -6104,7 +6146,7 @@
   /// Equality is a bit special, it is generated via the Dart `equals` runtime
   /// helper, that checks for null. The user defined method is called '=='.
   ///
-  JS.Expression _emitMemberName(String name,
+  js_ast.Expression _emitMemberName(String name,
       {DartType type,
       bool isStatic = false,
       bool useExtension,
@@ -6123,9 +6165,9 @@
         var parts = runtimeName.split('.');
         if (parts.length < 2) return propertyName(runtimeName);
 
-        JS.Expression result = JS.Identifier(parts[0]);
+        js_ast.Expression result = js_ast.Identifier(parts[0]);
         for (int i = 1; i < parts.length; i++) {
-          result = JS.PropertyAccess(result, propertyName(parts[i]));
+          result = js_ast.PropertyAccess(result, propertyName(parts[i]));
         }
         return result;
       }
@@ -6138,7 +6180,7 @@
     useExtension ??= _isSymbolizedMember(type, name);
     // Rename members that conflict with standard JS members unless we are
     // actually try to access those JS members via interop.
-    name = JS.memberNameForDartMember(name, _isExternal(element));
+    name = js_ast.memberNameForDartMember(name, _isExternal(element));
     if (useExtension) {
       return getExtensionSymbolInternal(name);
     }
@@ -6151,7 +6193,7 @@
   /// The member [name] should be passed, as well as its [element] when it's
   /// available. If the element is `external`, the element is used to statically
   /// resolve the JS interop/dart:html static member. Otherwise it is ignored.
-  JS.Expression _emitStaticMemberName(String name, [Element element]) {
+  js_ast.Expression _emitStaticMemberName(String name, [Element element]) {
     if (element != null && _isExternal(element)) {
       var newName = getAnnotationName(element, isJSName) ??
           _getJSInteropStaticMemberName(element);
@@ -6316,7 +6358,7 @@
     return DynamicTypeImpl.instance;
   }
 
-  JS.Expression _throwUnsafe(String message) => runtimeCall(
+  js_ast.Expression _throwUnsafe(String message) => runtimeCall(
       'throw(Error(#))', [js.escapedString("compile error: $message")]);
 
   Null _unreachable(Object node) {
@@ -6403,7 +6445,7 @@
 
   /// Unused, handled by [visitMixinDeclaration].
   @override
-  JS.Node visitOnClause(OnClause node) => _unreachable(node);
+  js_ast.Node visitOnClause(OnClause node) => _unreachable(node);
 
   /// Unused, handled by imports/exports.
   @override
@@ -6469,8 +6511,8 @@
   @override
   visitWithClause(node) => _unreachable(node);
 
-  JS.For _emitFor(ForParts forParts, JS.Statement body) {
-    JS.Expression init;
+  js_ast.For _emitFor(ForParts forParts, js_ast.Statement body) {
+    js_ast.Expression init;
     if (forParts is ForPartsWithExpression) {
       init = _visitExpression(forParts.initialization);
     } else if (forParts is ForPartsWithDeclarations) {
@@ -6478,64 +6520,66 @@
     } else {
       throw new StateError('Unrecognized for loop parts');
     }
-    JS.Expression update;
+    js_ast.Expression update;
     if (forParts.updaters != null && forParts.updaters.isNotEmpty) {
-      update = JS.Expression.binary(
+      update = js_ast.Expression.binary(
               forParts.updaters.map(_visitExpression).toList(), ',')
           .toVoidExpression();
     }
-    return JS.For(init, _visitTest(forParts.condition), update, body);
+    return js_ast.For(init, _visitTest(forParts.condition), update, body);
   }
 
-  JS.Statement _emitForEach(ForEachParts forParts, JS.Statement jsBody) {
+  js_ast.Statement _emitForEach(
+      ForEachParts forParts, js_ast.Statement jsBody) {
     var jsIterable = _visitExpression(forParts.iterable);
-    JS.Expression jsLeftExpression;
+    js_ast.Expression jsLeftExpression;
     if (forParts is ForEachPartsWithIdentifier) {
       jsLeftExpression = _visitExpression(forParts.identifier);
     } else if (forParts is ForEachPartsWithDeclaration) {
       var id = _emitVariableDef(forParts.loopVariable.identifier);
       jsLeftExpression = js.call('let #', id);
       if (_annotatedNullCheck(forParts.loopVariable.declaredElement)) {
-        jsBody =
-            JS.Block([_nullParameterCheck(JS.Identifier(id.name)), jsBody]);
+        jsBody = js_ast.Block(
+            [_nullParameterCheck(js_ast.Identifier(id.name)), jsBody]);
       }
       if (variableIsReferenced(id.name, jsIterable)) {
-        var temp = JS.TemporaryId('iter');
-        return JS.Block([
+        var temp = js_ast.TemporaryId('iter');
+        return js_ast.Block([
           jsIterable.toVariableDeclaration(temp),
-          JS.ForOf(jsLeftExpression, temp, jsBody)
+          js_ast.ForOf(jsLeftExpression, temp, jsBody)
         ]);
       }
     } else {
       throw new StateError('Unrecognized for loop parts');
     }
-    return JS.ForOf(jsLeftExpression, jsIterable, jsBody);
+    return js_ast.ForOf(jsLeftExpression, jsIterable, jsBody);
   }
 
   @override
-  JS.Statement visitForElement(ForElement node) {
+  js_ast.Statement visitForElement(ForElement node) {
     var jsBody = _isUiAsCodeElement(node.body)
-        ? node.body.accept(this) as JS.Statement
+        ? node.body.accept(this) as js_ast.Statement
         : _visitNestedCollectionElement(node.body);
     return _forAdaptor(node.forLoopParts, node.awaitKeyword, jsBody);
   }
 
   @override
-  JS.Statement visitIfElement(IfElement node) {
+  js_ast.Statement visitIfElement(IfElement node) {
     var thenElement = _isUiAsCodeElement(node.thenElement)
-        ? node.thenElement.accept(this) as JS.Statement
+        ? node.thenElement.accept(this) as js_ast.Statement
         : _visitNestedCollectionElement(node.thenElement);
 
-    JS.Statement elseElement;
+    js_ast.Statement elseElement;
     if (node.elseElement != null) {
       if (_isUiAsCodeElement(node.elseElement)) {
-        elseElement = node.elseElement.accept<JS.Node>(this) as JS.Statement;
+        elseElement =
+            node.elseElement.accept<js_ast.Node>(this) as js_ast.Statement;
       } else {
         elseElement = _visitNestedCollectionElement(node.elseElement);
       }
     }
 
-    return JS.If(_visitTest(node.condition), thenElement, elseElement);
+    return js_ast.If(_visitTest(node.condition), thenElement, elseElement);
   }
 
   @override
@@ -6547,23 +6591,23 @@
       _unreachable(node);
 
   @override
-  JS.Statement visitForStatement(ForStatement node) =>
+  js_ast.Statement visitForStatement(ForStatement node) =>
       _forAdaptor(node.forLoopParts, node.awaitKeyword, _visitScope(node.body));
 
-  JS.Statement _forAdaptor(
-      ForLoopParts forParts, Token awaitKeyword, JS.Statement jsBody) {
+  js_ast.Statement _forAdaptor(
+      ForLoopParts forParts, Token awaitKeyword, js_ast.Statement jsBody) {
     /// Returns a new scoped block starting with [first] followed by [rest].
     ///
     /// Performs one level of scope flattening when [rest] is already a scoped
     /// block.
-    JS.Block insertFirst(JS.Statement first, JS.Statement rest) {
+    js_ast.Block insertFirst(js_ast.Statement first, js_ast.Statement rest) {
       var bodyStatements = [first];
-      if (rest is JS.Block) {
+      if (rest is js_ast.Block) {
         bodyStatements.addAll(rest.statements);
       } else {
         bodyStatements.add(rest);
       }
-      return JS.Block(bodyStatements);
+      return js_ast.Block(bodyStatements);
     }
 
     if (forParts is ForParts) {
@@ -6598,7 +6642,7 @@
       node.isSet ? _emitSetLiteral(node) : _emitMapLiteral(node);
 
   @override
-  JS.Statement visitSpreadElement(SpreadElement node) {
+  js_ast.Statement visitSpreadElement(SpreadElement node) {
     /// Returns `true` if [node] is or is a child element of a map literal.
     bool isMap(AstNode node) {
       if (node is SetOrMapLiteral) return node.isMap;
@@ -6609,8 +6653,8 @@
     /// Returns [expression] wrapped in an implict cast to [castType] or
     /// [expression] as provided if [castType] is `null` signifying that
     /// no cast is needed.
-    JS.Expression wrapInImplicitCast(
-            JS.Expression expression, DartType castType) =>
+    js_ast.Expression wrapInImplicitCast(
+            js_ast.Expression expression, DartType castType) =>
         castType == null ? expression : _emitCast(castType, expression);
 
     /// Returns a statement spreading the elements of [expression] into
@@ -6619,8 +6663,8 @@
     /// Expects the collection literal containing [expression] to be a list or
     /// set literal. Inserts implicit casts to [elementCastType] for each
     /// element if needed.
-    JS.Statement emitListOrSetSpread(
-        JS.Expression expression, DartType elementCastType) {
+    js_ast.Statement emitListOrSetSpread(
+        js_ast.Expression expression, DartType elementCastType) {
       var forEachTemp =
           _emitSimpleIdentifier(_createTemporary('i', types.dynamicType));
       return js.statement('#.forEach((#) => #.push(#))', [
@@ -6637,8 +6681,8 @@
     /// Expects the collection literal containing [expression] to be a map
     /// literal. Inserts implicit casts to [keyCastType] for keys and
     /// [valueCastType] for values if needed.
-    JS.Statement emitMapSpread(JS.Expression expression, DartType keyCastType,
-        DartType valueCastType) {
+    js_ast.Statement emitMapSpread(js_ast.Expression expression,
+        DartType keyCastType, DartType valueCastType) {
       var keyTemp =
           _emitSimpleIdentifier(_createTemporary('k', types.dynamicType));
       var valueTemp =
@@ -6659,7 +6703,7 @@
     /// Uses implict cast information from [node] to insert the correct casts
     /// for the collection elements when spreading. Inspects parents of [node]
     /// to determine the type of the enclosing collection literal.
-    JS.Statement emitSpread(JS.Expression expression, Expression node) {
+    js_ast.Statement emitSpread(js_ast.Expression expression, Expression node) {
       expression = wrapInImplicitCast(expression, getImplicitCast(node));
 
       // Start searching for a map literal at the parent of the SpreadElement.
@@ -6676,11 +6720,12 @@
     /// Uses implict cast information from [node] to insert the correct casts
     /// for the collection elements when spreading. Inspects parents of [node]
     /// to determine the type of the enclosing collection literal.
-    JS.Statement emitNullSafeSpread(JS.Expression expression, Expression node) {
+    js_ast.Statement emitNullSafeSpread(
+        js_ast.Expression expression, Expression node) {
       // TODO(nshahan) Could optimize out if we know the value is null.
       var spreadItems =
           _emitSimpleIdentifier(_createTemporary('items', getStaticType(node)));
-      return JS.Block([
+      return js_ast.Block([
         js.statement('let # = #', [spreadItems, expression]),
         js.statement(
             'if (# != null) #', [spreadItems, emitSpread(spreadItems, node)])
@@ -6714,7 +6759,7 @@
 /// variable. These objects use instance equality, and should be shared
 /// everywhere in the tree where they are treated as the same variable.
 class TemporaryVariableElement extends LocalVariableElementImpl {
-  final JS.Expression jsVariable;
+  final js_ast.Expression jsVariable;
 
   TemporaryVariableElement.forNode(
       Identifier name, this.jsVariable, Element enclosingElement)
diff --git a/pkg/dev_compiler/lib/src/analyzer/driver.dart b/pkg/dev_compiler/lib/src/analyzer/driver.dart
index 9e9cb02..24ea8fe 100644
--- a/pkg/dev_compiler/lib/src/analyzer/driver.dart
+++ b/pkg/dev_compiler/lib/src/analyzer/driver.dart
@@ -2,31 +2,24 @@
 // 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:collection';
 import 'dart:typed_data';
 
 import 'package:analyzer/dart/analysis/declared_variables.dart';
-import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/file_system/file_system.dart' show ResourceProvider;
 import 'package:analyzer/src/dart/analysis/byte_store.dart';
+import 'package:analyzer/src/dart/analysis/ddc.dart';
 import 'package:analyzer/src/dart/analysis/driver.dart' show AnalysisDriver;
 import 'package:analyzer/src/dart/analysis/file_state.dart';
 import 'package:analyzer/src/dart/analysis/library_analyzer.dart';
 import 'package:analyzer/src/dart/analysis/performance_logger.dart';
-import 'package:analyzer/src/dart/analysis/session.dart';
-import 'package:analyzer/src/dart/analysis/restricted_analysis_context.dart';
 import 'package:analyzer/src/dart/element/inheritance_manager2.dart';
 import 'package:analyzer/src/generated/engine.dart';
 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider;
 import 'package:analyzer/src/generated/sdk.dart';
 import 'package:analyzer/src/generated/source.dart';
-import 'package:analyzer/src/summary/idl.dart';
-import 'package:analyzer/src/summary/link.dart' as summary_link;
 import 'package:analyzer/src/summary/package_bundle_reader.dart';
 import 'package:analyzer/src/summary/resynthesize.dart';
-import 'package:analyzer/src/summary/summarize_ast.dart';
-import 'package:analyzer/src/summary/summarize_elements.dart';
 import 'package:meta/meta.dart';
 
 import '../compiler/shared_command.dart' show sdkLibraryVariables;
@@ -114,26 +107,6 @@
   /// [SourceFactory]) and declared variables, if any (`-Dfoo=bar`).
   LinkedAnalysisDriver linkLibraries(
       List<Uri> explicitSources, AnalyzerOptions options) {
-    /// This code was ported from analyzer_cli (with a few changes/improvements).
-    ///
-    /// Here's a summary of the process:
-    ///
-    /// 1. starting with [explicitSources], visit all transitive
-    ///    imports/exports/parts, and create an unlinked unit for each
-    ///    (unless it's provided by an input summary). Add these to [assembler].
-    ///
-    /// 2. call [summary_link.link] to create the linked libraries, and add the
-    ///    results to the assembler.
-    ///
-    /// 3. serialize the data into [summaryBytes], then deserialize it back into
-    ///    the [bundle] that contains the summary for all [explicitSources] and
-    ///    their transitive dependencies.
-    ///
-    /// 4. create the analysis [context] and element [resynthesizer], and use
-    ///    them to return a new [LinkedAnalysisDriver] that can analyze all of
-    ///    the compilation units (and provide the resolved AST/errors for each).
-    var assembler = PackageBundleAssembler();
-
     /// The URI resolution logic for this build unit.
     var sourceFactory = createSourceFactory(options,
         sdkResolver: DartUriResolver(dartSdk), summaryData: summaryData);
@@ -141,133 +114,34 @@
     /// A fresh file system state for this list of [explicitSources].
     var fsState = _createFileSystemState(sourceFactory);
 
-    var uriToUnit = <String, UnlinkedUnit>{};
-
-    /// The sources that have been added to [sourcesToProcess], used to ensure
-    /// we only visit a given source once.
-    var knownSources = HashSet<Uri>.from(explicitSources);
-
-    /// The pending list of sources to visit.
-    var sourcesToProcess = Queue<Uri>.from(explicitSources);
-
-    /// Prepare URIs of unlinked units (for libraries) that should be linked.
-    var libraryUris = <String>[];
-
-    /// Ensure that the [UnlinkedUnit] for [absoluteUri] is available.
-    ///
-    /// If the unit is in the input [summaryData], do nothing.
-    /// Otherwise compute it and store into the [uriToUnit] and [assembler].
-    void prepareUnlinkedUnit(Uri uri) {
-      var absoluteUri = uri.toString();
-      // Maybe an input package contains the source.
-      if (summaryData.unlinkedMap[absoluteUri] != null) {
-        return;
-      }
-      // Parse the source and serialize its AST.
-      var source = sourceFactory.forUri2(uri);
-      if (source == null || !source.exists()) {
-        // Skip this source. We don't need to report an error here because it
-        // will be reported later during analysis.
-        return;
-      }
-      var file = fsState.getFileForPath(source.fullName);
-      var unit = file.parse();
-      var unlinkedUnit = serializeAstUnlinked(unit);
-      uriToUnit[absoluteUri] = unlinkedUnit;
-      assembler.addUnlinkedUnit(source, unlinkedUnit);
-
-      /// The URI to resolve imports/exports/parts against.
-      var baseUri = uri;
-      if (baseUri.scheme == 'dart' && baseUri.pathSegments.length == 1) {
-        // Add a trailing slash so relative URIs will resolve correctly, e.g.
-        // "map.dart" from "dart:core/" yields "dart:core/map.dart".
-        baseUri = Uri(scheme: 'dart', path: baseUri.path + '/');
-      }
-
-      void enqueueSource(String relativeUri) {
-        var sourceUri = baseUri.resolve(relativeUri);
-        if (knownSources.add(sourceUri)) {
-          sourcesToProcess.add(sourceUri);
-        }
-      }
-
-      // Add reachable imports/exports/parts, if any.
-      var isPart = false;
-      for (var directive in unit.directives) {
-        if (directive is UriBasedDirective) {
-          enqueueSource(directive.uri.stringValue);
-          // Handle conditional imports.
-          if (directive is NamespaceDirective) {
-            for (var config in directive.configurations) {
-              enqueueSource(config.uri.stringValue);
-            }
-          }
-        } else if (directive is PartOfDirective) {
-          isPart = true;
-        }
-      }
-
-      // Remember library URIs, so we can use it for linking libraries and
-      // compiling them.
-      if (!isPart) libraryUris.add(absoluteUri);
-    }
-
-    // Collect the unlinked units for all transitive sources.
-    //
-    // TODO(jmesserly): consider using parallelism via asynchronous IO here,
-    // once we fix debugger extension (web/web_command.dart) to allow async.
-    //
-    // It would let computation tasks (parsing/serializing unlinked units)
-    // proceed in parallel with reading the sources from disk.
-    while (sourcesToProcess.isNotEmpty) {
-      prepareUnlinkedUnit(sourcesToProcess.removeFirst());
-    }
-
     var declaredVariables = DeclaredVariables.fromMap(
         Map.of(options.declaredVariables)..addAll(sdkLibraryVariables));
 
-    /// Perform the linking step and store the result.
-    ///
-    /// TODO(jmesserly): can we pass in `getAst` to reuse existing ASTs we
-    /// created when we did `file.parse()` in [prepareUnlinkedUnit]?
-    var linkResult = summary_link.link(
-        libraryUris.toSet(),
-        (uri) => summaryData.linkedMap[uri],
-        (uri) => summaryData.unlinkedMap[uri] ?? uriToUnit[uri],
-        declaredVariables,
-        analysisOptions);
-    linkResult.forEach(assembler.addLinkedLibrary);
-
-    var summaryBytes = assembler.assemble().toBuffer();
-    var bundle = PackageBundle.fromBuffer(summaryBytes);
-
-    /// Create an analysis context to contain the state for this build unit.
-    var synchronousSession =
-        SynchronousSession(analysisOptions, declaredVariables);
-    var context = RestrictedAnalysisContext(synchronousSession, sourceFactory);
-    var resynthesizer = StoreBasedSummaryResynthesizer(
-      context,
-      null,
-      context.sourceFactory,
-      /*strongMode*/ true,
-      SummaryDataStore([])
-        ..addStore(summaryData)
-        ..addBundle(null, bundle),
+    var resynthesizerBuilder = DevCompilerResynthesizerBuilder(
+      fsState: fsState,
+      analysisOptions: analysisOptions,
+      declaredVariables: declaredVariables,
+      sourceFactory: sourceFactory,
+      summaryData: summaryData,
+      explicitSources: explicitSources,
     );
-    resynthesizer.finishCoreAsyncLibraries();
-    context.typeProvider = resynthesizer.typeProvider;
+    resynthesizerBuilder.build();
 
-    _extensionTypes ??= ExtensionTypeSet(context.typeProvider, resynthesizer);
+    _extensionTypes ??= ExtensionTypeSet(
+      resynthesizerBuilder.context.typeProvider,
+      resynthesizerBuilder.resynthesizer,
+    );
 
     return LinkedAnalysisDriver(
-        analysisOptions,
-        resynthesizer,
-        sourceFactory,
-        libraryUris,
-        declaredVariables,
-        summaryBytes,
-        fsState,
-        _resourceProvider);
+      analysisOptions,
+      resynthesizerBuilder.resynthesizer,
+      sourceFactory,
+      resynthesizerBuilder.libraryUris,
+      declaredVariables,
+      resynthesizerBuilder.summaryBytes,
+      fsState,
+      _resourceProvider,
+    );
   }
 
   FileSystemState _createFileSystemState(SourceFactory sourceFactory) {
diff --git a/pkg/dev_compiler/lib/src/analyzer/module_compiler.dart b/pkg/dev_compiler/lib/src/analyzer/module_compiler.dart
index 3997597..70bdc0a 100644
--- a/pkg/dev_compiler/lib/src/analyzer/module_compiler.dart
+++ b/pkg/dev_compiler/lib/src/analyzer/module_compiler.dart
@@ -15,12 +15,12 @@
 import 'package:path/path.dart' as path;
 import 'package:source_maps/source_maps.dart';
 
-import '../compiler/js_names.dart' as JS;
+import '../compiler/js_names.dart' as js_ast;
 import '../compiler/module_builder.dart'
     show transformModuleFormat, ModuleFormat;
 import '../compiler/shared_command.dart';
 import '../compiler/shared_compiler.dart';
-import '../js_ast/js_ast.dart' as JS;
+import '../js_ast/js_ast.dart' as js_ast;
 import '../js_ast/js_ast.dart' show js;
 import '../js_ast/source_map_printer.dart' show SourceMapPrintingContext;
 import 'code_generator.dart' show CodeGenerator;
@@ -95,7 +95,7 @@
     }
   }
 
-  JS.Program jsProgram;
+  js_ast.Program jsProgram;
   if (options.unsafeForceCompile || !errors.hasFatalErrors) {
     var codeGenerator = CodeGenerator(
         driver,
@@ -227,7 +227,7 @@
 
   /// The AST that will be used to generate the [code] and [sourceMap] for this
   /// module.
-  final JS.Program moduleTree;
+  final js_ast.Program moduleTree;
 
   /// The compiler options used to generate this module.
   final CompilerOptions options;
@@ -256,20 +256,21 @@
   // TODO(jmesserly): this should match our old logic, but I'm not sure we are
   // correctly handling the pointer from the .js file to the .map file.
   JSModuleCode getCode(ModuleFormat format, String jsUrl, String mapUrl) {
-    var opts = JS.JavaScriptPrintingOptions(
+    var opts = js_ast.JavaScriptPrintingOptions(
         allowKeywordsInProperties: true, allowSingleLineIfStatements: true);
-    JS.SimpleJavaScriptPrintingContext printer;
+    js_ast.SimpleJavaScriptPrintingContext printer;
     SourceMapBuilder sourceMap;
     if (options.sourceMap) {
       var sourceMapContext = SourceMapPrintingContext();
       sourceMap = sourceMapContext.sourceMap;
       printer = sourceMapContext;
     } else {
-      printer = JS.SimpleJavaScriptPrintingContext();
+      printer = js_ast.SimpleJavaScriptPrintingContext();
     }
 
     var tree = transformModuleFormat(format, moduleTree);
-    tree.accept(JS.Printer(opts, printer, localNamer: JS.TemporaryNamer(tree)));
+    tree.accept(
+        js_ast.Printer(opts, printer, localNamer: js_ast.TemporaryNamer(tree)));
 
     Map builtMap;
     if (options.sourceMap && sourceMap != null) {
diff --git a/pkg/dev_compiler/lib/src/analyzer/property_model.dart b/pkg/dev_compiler/lib/src/analyzer/property_model.dart
index d63abc3..227afa7 100644
--- a/pkg/dev_compiler/lib/src/analyzer/property_model.dart
+++ b/pkg/dev_compiler/lib/src/analyzer/property_model.dart
@@ -8,8 +8,7 @@
 import 'package:analyzer/dart/element/type.dart' show InterfaceType;
 import 'package:analyzer/src/dart/element/element.dart' show FieldElementImpl;
 
-import '../compiler/js_names.dart' as JS;
-import '../js_ast/js_ast.dart' as JS;
+import '../compiler/js_names.dart' as js_ast;
 import 'element_helpers.dart';
 import 'extension_types.dart';
 
@@ -176,7 +175,7 @@
   /// pair in JavaScript.
   ///
   /// The value property stores the symbol used for the field's storage slot.
-  final virtualFields = <FieldElement, JS.TemporaryId>{};
+  final virtualFields = <FieldElement, js_ast.TemporaryId>{};
 
   /// The set of inherited getters, used because JS getters/setters are paired,
   /// so if we're generating a setter we may need to emit a getter that calls
@@ -250,7 +249,7 @@
                 covariantParameters != null &&
                 covariantParameters.contains(setter.parameters[0]) &&
                 covariantPrivateMembers.contains(setter)) {
-          virtualFields[field] = JS.TemporaryId(name);
+          virtualFields[field] = js_ast.TemporaryId(name);
         }
       }
     }
diff --git a/pkg/dev_compiler/lib/src/analyzer/type_utilities.dart b/pkg/dev_compiler/lib/src/analyzer/type_utilities.dart
index dc52e86..ffa6943 100644
--- a/pkg/dev_compiler/lib/src/analyzer/type_utilities.dart
+++ b/pkg/dev_compiler/lib/src/analyzer/type_utilities.dart
@@ -3,13 +3,14 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'dart:collection';
+
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/src/dart/element/member.dart' show TypeParameterMember;
 import 'package:analyzer/dart/element/type.dart';
 
 import '../analyzer/element_helpers.dart';
-import '../compiler/js_names.dart' as JS;
-import '../js_ast/js_ast.dart' as JS;
+import '../compiler/js_names.dart' as js_ast;
+import '../js_ast/js_ast.dart' as js_ast;
 import '../js_ast/js_ast.dart' show js;
 
 Set<TypeParameterElement> freeTypeParameters(DartType t) {
@@ -54,11 +55,11 @@
   // Use a LinkedHashMap to maintain key insertion order so the generated code
   // is stable under slight perturbation.  (If this is not good enough we could
   // sort by name to canonicalize order.)
-  final _names = LinkedHashMap<DartType, JS.TemporaryId>(
+  final _names = LinkedHashMap<DartType, js_ast.TemporaryId>(
       equals: typesAreEqual, hashCode: typeHashCode);
   Iterable<DartType> get keys => _names.keys.toList();
 
-  JS.Statement _dischargeType(DartType type) {
+  js_ast.Statement _dischargeType(DartType type) {
     var name = _names.remove(type);
     if (name != null) {
       return js.statement('let #;', [name]);
@@ -69,8 +70,8 @@
   /// Emit a list of statements declaring the cache variables for
   /// types tracked by this table.  If [typeFilter] is given,
   /// only emit the types listed in the filter.
-  List<JS.Statement> discharge([Iterable<DartType> typeFilter]) {
-    var decls = <JS.Statement>[];
+  List<js_ast.Statement> discharge([Iterable<DartType> typeFilter]) {
+    var decls = <js_ast.Statement>[];
     var types = typeFilter ?? keys;
     for (var t in types) {
       var stmt = _dischargeType(t);
@@ -119,26 +120,26 @@
 
   /// Heuristically choose a good name for the cache and generator
   /// variables.
-  JS.TemporaryId chooseTypeName(DartType type) {
-    return JS.TemporaryId(_typeString(type));
+  js_ast.TemporaryId chooseTypeName(DartType type) {
+    return js_ast.TemporaryId(_typeString(type));
   }
 }
 
 /// _GeneratorTable tracks types which have been
 /// named and hoisted.
 class _GeneratorTable extends _CacheTable {
-  final _defs = LinkedHashMap<DartType, JS.Expression>(
+  final _defs = LinkedHashMap<DartType, js_ast.Expression>(
       equals: typesAreEqual, hashCode: typeHashCode);
 
-  final JS.Identifier _runtimeModule;
+  final js_ast.Identifier _runtimeModule;
 
   _GeneratorTable(this._runtimeModule);
 
   @override
-  JS.Statement _dischargeType(DartType t) {
+  js_ast.Statement _dischargeType(DartType t) {
     var name = _names.remove(t);
     if (name != null) {
-      JS.Expression init = _defs.remove(t);
+      js_ast.Expression init = _defs.remove(t);
       assert(init != null);
       return js.statement('let # = () => ((# = #.constFn(#))());',
           [name, name, _runtimeModule, init]);
@@ -149,7 +150,7 @@
   /// If [type] does not already have a generator name chosen for it,
   /// assign it one, using [typeRep] as the initializer for it.
   /// Emit the generator name.
-  JS.TemporaryId _nameType(DartType type, JS.Expression typeRep) {
+  js_ast.TemporaryId _nameType(DartType type, js_ast.Expression typeRep) {
     var temp = _names[type];
     if (temp == null) {
       _names[type] = temp = chooseTypeName(type);
@@ -169,12 +170,12 @@
   /// parameter.
   final _scopeDependencies = <TypeParameterElement, List<DartType>>{};
 
-  TypeTable(JS.Identifier runtime) : _generators = _GeneratorTable(runtime);
+  TypeTable(js_ast.Identifier runtime) : _generators = _GeneratorTable(runtime);
 
   /// Emit a list of statements declaring the cache variables and generator
   /// definitions tracked by the table.  If [formals] is present, only
   /// emit the definitions which depend on the formals.
-  List<JS.Statement> discharge([List<TypeParameterElement> formals]) {
+  List<js_ast.Statement> discharge([List<TypeParameterElement> formals]) {
     var filter = formals?.expand((p) => _scopeDependencies[p] ?? <DartType>[]);
     var stmts = [_generators].expand((c) => c.discharge(filter)).toList();
     formals?.forEach(_scopeDependencies.remove);
@@ -205,7 +206,8 @@
   /// Given a type [type], and a JS expression [typeRep] which implements it,
   /// add the type and its representation to the table, returning an
   /// expression which implements the type (but which caches the value).
-  JS.Expression nameType(ParameterizedType type, JS.Expression typeRep) {
+  js_ast.Expression nameType(
+      ParameterizedType type, js_ast.Expression typeRep) {
     if (!_generators.isNamed(type) && recordScopeDependencies(type)) {
       return typeRep;
     }
@@ -223,10 +225,11 @@
   /// should be a function that is invoked to compute the type, rather than the
   /// type itself. This allows better integration with `lazyFn`, avoiding an
   /// extra level of indirection.
-  JS.Expression nameFunctionType(FunctionType type, JS.Expression typeRep,
+  js_ast.Expression nameFunctionType(
+      FunctionType type, js_ast.Expression typeRep,
       {bool lazy = false}) {
     if (!_generators.isNamed(type) && recordScopeDependencies(type)) {
-      return lazy ? JS.ArrowFun([], typeRep) : typeRep;
+      return lazy ? js_ast.ArrowFun([], typeRep) : typeRep;
     }
 
     var name = _generators._nameType(type, typeRep);
diff --git a/pkg/dev_compiler/lib/src/compiler/shared_command.dart b/pkg/dev_compiler/lib/src/compiler/shared_command.dart
index 397f0ed..0388456 100644
--- a/pkg/dev_compiler/lib/src/compiler/shared_command.dart
+++ b/pkg/dev_compiler/lib/src/compiler/shared_command.dart
@@ -344,21 +344,28 @@
 
 /// Adjusts the source paths in [sourceMap] to be relative to [sourceMapPath],
 /// and returns the new map.  Relative paths are in terms of URIs ('/'), not
-/// local OS paths (e.g., windows '\').
+/// local OS paths (e.g., windows '\'). Sources with a multi-root scheme
+/// matching [multiRootScheme] are adjusted to be relative to
+/// [multiRootOutputPath].
 // TODO(jmesserly): find a new home for this.
 Map placeSourceMap(Map sourceMap, String sourceMapPath,
-    Map<String, String> bazelMappings, String customScheme) {
+    Map<String, String> bazelMappings, String multiRootScheme,
+    {String multiRootOutputPath}) {
   var map = Map.from(sourceMap);
   // Convert to a local file path if it's not.
   sourceMapPath = path.fromUri(sourcePathToUri(sourceMapPath));
   var sourceMapDir = path.dirname(path.absolute(sourceMapPath));
   var list = (map['sources'] as List).toList();
-  map['sources'] = list;
 
   String makeRelative(String sourcePath) {
     var uri = sourcePathToUri(sourcePath);
     var scheme = uri.scheme;
-    if (scheme == 'dart' || scheme == 'package' || scheme == customScheme) {
+    if (scheme == 'dart' || scheme == 'package' || scheme == multiRootScheme) {
+      if (scheme == multiRootScheme) {
+        var multiRootPath = '$multiRootOutputPath${uri.path}';
+        multiRootPath = path.relative(multiRootPath, from: sourceMapDir);
+        return multiRootPath;
+      }
       return sourcePath;
     }
 
@@ -379,6 +386,7 @@
   for (int i = 0; i < list.length; i++) {
     list[i] = makeRelative(list[i] as String);
   }
+  map['sources'] = list;
   map['file'] = makeRelative(map['file'] as String);
   return map;
 }
diff --git a/pkg/dev_compiler/lib/src/compiler/shared_compiler.dart b/pkg/dev_compiler/lib/src/compiler/shared_compiler.dart
index 6a3182d..21daddb 100644
--- a/pkg/dev_compiler/lib/src/compiler/shared_compiler.dart
+++ b/pkg/dev_compiler/lib/src/compiler/shared_compiler.dart
@@ -5,10 +5,8 @@
 import 'dart:collection';
 import 'package:meta/meta.dart';
 
-import '../compiler/js_metalet.dart' as JS;
-import '../compiler/js_names.dart' as JS;
-import '../compiler/js_utils.dart' as JS;
-import '../js_ast/js_ast.dart' as JS;
+import '../compiler/js_names.dart' as js_ast;
+import '../js_ast/js_ast.dart' as js_ast;
 import '../js_ast/js_ast.dart' show js;
 
 /// Shared code between Analyzer and Kernel backends.
@@ -20,34 +18,34 @@
   /// returned by any `return;` statement.
   ///
   /// This lets DDC use the setter method's return value directly.
-  final List<JS.Identifier> _operatorSetResultStack = [];
+  final List<js_ast.Identifier> _operatorSetResultStack = [];
 
   /// Private member names in this module, organized by their library.
-  final _privateNames = HashMap<Library, HashMap<String, JS.TemporaryId>>();
+  final _privateNames = HashMap<Library, HashMap<String, js_ast.TemporaryId>>();
 
   /// Extension member symbols for adding Dart members to JS types.
   ///
   /// These are added to the [extensionSymbolsModule]; see that field for more
   /// information.
-  final _extensionSymbols = <String, JS.TemporaryId>{};
+  final _extensionSymbols = <String, js_ast.TemporaryId>{};
 
   /// The set of libraries we are currently compiling, and the temporaries used
   /// to refer to them.
-  final _libraries = <Library, JS.Identifier>{};
+  final _libraries = <Library, js_ast.Identifier>{};
 
   /// Imported libraries, and the temporaries used to refer to them.
-  final _imports = <Library, JS.TemporaryId>{};
+  final _imports = <Library, js_ast.TemporaryId>{};
 
   /// The identifier used to reference DDC's core "dart:_runtime" library from
   /// generated JS code, typically called "dart" e.g. `dart.dcall`.
   @protected
-  JS.Identifier runtimeModule;
+  js_ast.Identifier runtimeModule;
 
   /// The identifier used to reference DDC's "extension method" symbols, used to
   /// safely add Dart-specific member names to JavaScript classes, such as
   /// primitive types (e.g. String) or DOM types in "dart:html".
   @protected
-  JS.Identifier extensionSymbolsModule;
+  js_ast.Identifier extensionSymbolsModule;
 
   /// Whether we're currently building the SDK, which may require special
   /// bootstrapping logic.
@@ -60,18 +58,18 @@
   /// The temporary variable that stores named arguments (these are passed via a
   /// JS object literal, to match JS conventions).
   @protected
-  final namedArgumentTemp = JS.TemporaryId('opts');
+  final namedArgumentTemp = js_ast.TemporaryId('opts');
 
   /// The list of output module items, in the order they need to be emitted in.
   @protected
-  final moduleItems = <JS.ModuleItem>[];
+  final moduleItems = <js_ast.ModuleItem>[];
 
   /// Like [moduleItems] but for items that should be emitted after classes.
   ///
   /// This is used for deferred supertypes of mutually recursive non-generic
   /// classes.
   @protected
-  final afterClassDefItems = <JS.ModuleItem>[];
+  final afterClassDefItems = <js_ast.ModuleItem>[];
 
   /// The type used for private Dart [Symbol]s.
   @protected
@@ -126,12 +124,12 @@
 
   /// Emits the expression necessary to access a constructor of [type];
   @protected
-  JS.Expression emitConstructorAccess(InterfaceType type);
+  js_ast.Expression emitConstructorAccess(InterfaceType type);
 
   /// When compiling the body of a `operator []=` method, this will be non-null
   /// and will indicate the the value that should be returned from any `return;`
   /// statements.
-  JS.Identifier get _operatorSetResult {
+  js_ast.Identifier get _operatorSetResult {
     var stack = _operatorSetResultStack;
     return stack.isEmpty ? null : stack.last;
   }
@@ -141,12 +139,12 @@
   ///
   /// See also [exitFunction] and [emitReturnStatement].
   @protected
-  void enterFunction(String name, List<JS.Parameter> formals,
+  void enterFunction(String name, List<js_ast.Parameter> formals,
       bool Function() isLastParamMutated) {
     if (name == '[]=') {
       _operatorSetResultStack.add(isLastParamMutated()
-          ? JS.TemporaryId((formals.last as JS.Identifier).name)
-          : formals.last as JS.Identifier);
+          ? js_ast.TemporaryId((formals.last as js_ast.Identifier).name)
+          : formals.last as js_ast.Identifier);
     } else {
       _operatorSetResultStack.add(null);
     }
@@ -155,8 +153,8 @@
   /// Called when finished emitting methods/functions, and must correspond to a
   /// previous [enterFunction] call.
   @protected
-  JS.Block exitFunction(
-      String name, List<JS.Parameter> formals, JS.Block code) {
+  js_ast.Block exitFunction(
+      String name, List<js_ast.Parameter> formals, js_ast.Block code) {
     if (name == "==" &&
         formals.isNotEmpty &&
         currentLibraryUri.scheme != 'dart') {
@@ -177,7 +175,7 @@
       var valueParam = formals.last;
       var statements = code.statements;
       if (statements.isEmpty || !statements.last.alwaysReturns) {
-        statements.add(JS.Return(setOperatorResult));
+        statements.add(js_ast.Return(setOperatorResult));
       }
       if (!identical(setOperatorResult, valueParam)) {
         // If the value parameter was mutated, then we use a temporary
@@ -193,12 +191,14 @@
   /// Emits a return statement `return <value>;`, handling special rules for
   /// the `operator []=` method.
   @protected
-  JS.Statement emitReturnStatement(JS.Expression value) {
+  js_ast.Statement emitReturnStatement(js_ast.Expression value) {
     if (_operatorSetResult != null) {
-      var result = JS.Return(_operatorSetResult);
-      return value != null ? JS.Block([value.toStatement(), result]) : result;
+      var result = js_ast.Return(_operatorSetResult);
+      return value != null
+          ? js_ast.Block([value.toStatement(), result])
+          : result;
     }
-    return value != null ? value.toReturn() : JS.Return();
+    return value != null ? value.toReturn() : js_ast.Return();
   }
 
   /// Prepends the `dart.` and then uses [js.call] to parse the specified JS
@@ -213,13 +213,13 @@
   ///     dart.asInt(<expr>)
   ///
   @protected
-  JS.Expression runtimeCall(String code, [List<Object> args]) =>
+  js_ast.Expression runtimeCall(String code, [List<Object> args]) =>
       js.call('#.$code', <Object>[runtimeModule, ...?args]);
 
   /// Calls [runtimeCall] and uses `toStatement()` to convert the resulting
   /// expression into a statement.
   @protected
-  JS.Statement runtimeStatement(String code, [List<Object> args]) =>
+  js_ast.Statement runtimeStatement(String code, [List<Object> args]) =>
       runtimeCall(code, args).toStatement();
 
   /// Emits a private name JS Symbol for [name] scoped to the Dart [library].
@@ -229,7 +229,7 @@
   /// member names, that won't collide at runtime, as required by the Dart
   /// language spec.
   @protected
-  JS.TemporaryId emitPrivateNameSymbol(Library library, String name) {
+  js_ast.TemporaryId emitPrivateNameSymbol(Library library, String name) {
     /// Initializes the JS `Symbol` for the private member [name] in [library].
     ///
     /// If the library is in the current JS module ([_libraries] contains it),
@@ -243,9 +243,9 @@
     /// If the library is imported, then the existing private name will be
     /// retrieved from it. In both cases, we use the same `dart.privateName`
     /// runtime call.
-    JS.TemporaryId initPrivateNameSymbol() {
+    js_ast.TemporaryId initPrivateNameSymbol() {
       var idName = name.endsWith('=') ? name.replaceAll('=', '_') : name;
-      var id = JS.TemporaryId(idName);
+      var id = js_ast.TemporaryId(idName);
       moduleItems.add(js.statement('const # = #.privateName(#, #)',
           [id, runtimeModule, emitLibraryName(library), js.string(name)]));
       return id;
@@ -262,12 +262,13 @@
   /// `dart.defineValue(className, name, value)`. This is required when
   /// `FunctionNode.prototype` already defins a getters with the same name.
   @protected
-  JS.Expression defineValueOnClass(Class c, JS.Expression className,
-      JS.Expression nameExpr, JS.Expression value) {
+  js_ast.Expression defineValueOnClass(Class c, js_ast.Expression className,
+      js_ast.Expression nameExpr, js_ast.Expression value) {
     var args = [className, nameExpr, value];
-    if (nameExpr is JS.LiteralString) {
+    if (nameExpr is js_ast.LiteralString) {
       var name = nameExpr.valueWithoutQuotes;
-      if (JS.isFunctionPrototypeGetter(name) || superclassHasStatic(c, name)) {
+      if (js_ast.isFunctionPrototypeGetter(name) ||
+          superclassHasStatic(c, name)) {
         return runtimeCall('defineValue(#, #, #)', args);
       }
     }
@@ -300,10 +301,10 @@
   /// per-module, though, as that would be relatively easy for the compiler to
   /// implement once we have a single Kernel backend).
   @protected
-  JS.Expression cacheConst(JS.Expression jsExpr) {
+  js_ast.Expression cacheConst(js_ast.Expression jsExpr) {
     if (currentFunction == null) return jsExpr;
 
-    var temp = JS.TemporaryId('const');
+    var temp = js_ast.TemporaryId('const');
     moduleItems.add(js.statement('let #;', [temp]));
     return js.call('# || (# = #)', [temp, temp, jsExpr]);
   }
@@ -313,11 +314,11 @@
   /// If the symbol refers to a private name, its library will be set to the
   /// [currentLibrary], so the Symbol is scoped properly.
   @protected
-  JS.Expression emitDartSymbol(String symbolName) {
+  js_ast.Expression emitDartSymbol(String symbolName) {
     // TODO(vsm): Handle qualified symbols correctly.
     var last = symbolName.split('.').last;
     var name = js.escapedString(symbolName, "'");
-    JS.Expression result;
+    js_ast.Expression result;
     if (last.startsWith('_')) {
       var nativeSymbol = emitPrivateNameSymbol(currentLibrary, last);
       result = js.call('new #.new(#, #)',
@@ -332,7 +333,7 @@
   /// Calls the `dart.const` function in "dart:_runtime" to canonicalize a
   /// constant instance of a user-defined class stored in [expr].
   @protected
-  JS.Expression canonicalizeConstObject(JS.Expression expr) =>
+  js_ast.Expression canonicalizeConstObject(js_ast.Expression expr) =>
       cacheConst(runtimeCall('const(#)', [expr]));
 
   /// Emits preamble for the module containing [libraries], and returns the
@@ -351,31 +352,31 @@
   /// [extensionSymbolsModule], as well as the [_libraries] map needed by
   /// [emitLibraryName].
   @protected
-  List<JS.ModuleItem> startModule(Iterable<Library> libraries) {
+  List<js_ast.ModuleItem> startModule(Iterable<Library> libraries) {
     isBuildingSdk = libraries.any(isSdkInternalRuntime);
     if (isBuildingSdk) {
       // Don't allow these to be renamed when we're building the SDK.
       // There is JS code in dart:* that depends on their names.
-      runtimeModule = JS.Identifier('dart');
-      extensionSymbolsModule = JS.Identifier('dartx');
+      runtimeModule = js_ast.Identifier('dart');
+      extensionSymbolsModule = js_ast.Identifier('dartx');
     } else {
       // Otherwise allow these to be renamed so users can write them.
-      runtimeModule = JS.TemporaryId('dart');
-      extensionSymbolsModule = JS.TemporaryId('dartx');
+      runtimeModule = js_ast.TemporaryId('dart');
+      extensionSymbolsModule = js_ast.TemporaryId('dartx');
     }
 
     // Initialize our library variables.
-    var items = <JS.ModuleItem>[];
-    var exports = <JS.NameSpecifier>[];
+    var items = <js_ast.ModuleItem>[];
+    var exports = <js_ast.NameSpecifier>[];
 
     if (isBuildingSdk) {
       // Bootstrap the ability to create Dart library objects.
-      var libraryProto = JS.TemporaryId('_library');
+      var libraryProto = js_ast.TemporaryId('_library');
       items.add(js.statement('const # = Object.create(null)', libraryProto));
       items.add(js.statement(
           'const # = Object.create(#)', [runtimeModule, libraryProto]));
       items.add(js.statement('#.library = #', [runtimeModule, libraryProto]));
-      exports.add(JS.NameSpecifier(runtimeModule));
+      exports.add(js_ast.NameSpecifier(runtimeModule));
     }
 
     for (var library in libraries) {
@@ -383,12 +384,12 @@
         _libraries[library] = runtimeModule;
         continue;
       }
-      var id = JS.TemporaryId(jsLibraryName(library));
+      var id = js_ast.TemporaryId(jsLibraryName(library));
       _libraries[library] = id;
 
       items.add(js.statement(
           'const # = Object.create(#.library)', [id, runtimeModule]));
-      exports.add(JS.NameSpecifier(id));
+      exports.add(js_ast.NameSpecifier(id));
     }
 
     // dart:_runtime has a magic module that holds extension method symbols.
@@ -397,15 +398,15 @@
       var id = extensionSymbolsModule;
       items.add(js.statement(
           'const # = Object.create(#.library)', [id, runtimeModule]));
-      exports.add(JS.NameSpecifier(id));
+      exports.add(js_ast.NameSpecifier(id));
     }
 
-    items.add(JS.ExportDeclaration(JS.ExportClause(exports)));
+    items.add(js_ast.ExportDeclaration(js_ast.ExportClause(exports)));
 
     if (isBuildingSdk) {
       // Initialize the private name function.
       // To bootstrap the SDK, this needs to be emitted before other code.
-      var symbol = JS.TemporaryId('_privateNames');
+      var symbol = js_ast.TemporaryId('_privateNames');
       items.add(js.statement('const # = Symbol("_privateNames")', symbol));
       items.add(js.statement(r'''
         #.privateName = function(library, name) {
@@ -423,16 +424,16 @@
 
   /// Returns the canonical name to refer to the Dart library.
   @protected
-  JS.Identifier emitLibraryName(Library library) {
+  js_ast.Identifier emitLibraryName(Library library) {
     // It's either one of the libraries in this module, or it's an import.
     return _libraries[library] ??
         _imports.putIfAbsent(
-            library, () => JS.TemporaryId(jsLibraryName(library)));
+            library, () => js_ast.TemporaryId(jsLibraryName(library)));
   }
 
   /// Emits imports and extension methods into [items].
   @protected
-  void emitImportsAndExtensionSymbols(List<JS.ModuleItem> items) {
+  void emitImportsAndExtensionSymbols(List<js_ast.ModuleItem> items) {
     var modules = Map<String, List<Library>>();
 
     for (var import in _imports.keys) {
@@ -454,20 +455,20 @@
       //     import {foo as foo$} from 'foo'; // if rename was needed
       //
       var imports =
-          libraries.map((l) => JS.NameSpecifier(_imports[l])).toList();
+          libraries.map((l) => js_ast.NameSpecifier(_imports[l])).toList();
       if (module == coreModuleName) {
-        imports.add(JS.NameSpecifier(runtimeModule));
-        imports.add(JS.NameSpecifier(extensionSymbolsModule));
+        imports.add(js_ast.NameSpecifier(runtimeModule));
+        imports.add(js_ast.NameSpecifier(extensionSymbolsModule));
       }
 
-      items.add(JS.ImportDeclaration(
+      items.add(js_ast.ImportDeclaration(
           namedImports: imports, from: js.string(module, "'")));
     });
 
     // Initialize extension symbols
     _extensionSymbols.forEach((name, id) {
-      JS.Expression value =
-          JS.PropertyAccess(extensionSymbolsModule, propertyName(name));
+      js_ast.Expression value =
+          js_ast.PropertyAccess(extensionSymbolsModule, propertyName(name));
       if (isBuildingSdk) {
         value = js.call('# = Symbol(#)', [value, js.string("dartx.$name")]);
       }
@@ -476,20 +477,20 @@
   }
 
   void _emitDebuggerExtensionInfo(String name) {
-    var properties = <JS.Property>[];
-    var parts = <JS.Property>[];
+    var properties = <js_ast.Property>[];
+    var parts = <js_ast.Property>[];
     _libraries.forEach((library, value) {
       // TODO(jacobr): we could specify a short library name instead of the
       // full library uri if we wanted to save space.
       var libraryName = js.escapedString(jsLibraryDebuggerName(library));
-      properties.add(JS.Property(libraryName, value));
+      properties.add(js_ast.Property(libraryName, value));
       var partNames = jsPartDebuggerNames(library);
       if (partNames.isNotEmpty) {
-        parts.add(JS.Property(libraryName, js.stringArray(partNames)));
+        parts.add(js_ast.Property(libraryName, js.stringArray(partNames)));
       }
     });
-    var module = JS.ObjectInitializer(properties, multiline: true);
-    var partMap = JS.ObjectInitializer(parts, multiline: true);
+    var module = js_ast.ObjectInitializer(properties, multiline: true);
+    var partMap = js_ast.ObjectInitializer(parts, multiline: true);
 
     // Track the module name for each library in the module.
     // This data is only required for debugging.
@@ -509,7 +510,8 @@
   /// Note, this function mutates the items list and returns it as the `body`
   /// field of the result.
   @protected
-  JS.Program finishModule(List<JS.ModuleItem> items, String moduleName) {
+  js_ast.Program finishModule(
+      List<js_ast.ModuleItem> items, String moduleName) {
     // TODO(jmesserly): there's probably further consolidation we can do
     // between DDC's two backends, by moving more code into this method, as the
     // code between `startModule` and `finishModule` is very similar in both.
@@ -520,16 +522,16 @@
     moduleItems.clear();
 
     // Build the module.
-    return JS.Program(items, name: moduleName);
+    return js_ast.Program(items, name: moduleName);
   }
 
   /// Flattens blocks in [items] to a single list.
   ///
   /// This will not flatten blocks that are marked as being scopes.
   void _copyAndFlattenBlocks(
-      List<JS.ModuleItem> result, Iterable<JS.ModuleItem> items) {
+      List<js_ast.ModuleItem> result, Iterable<js_ast.ModuleItem> items) {
     for (var item in items) {
-      if (item is JS.Block && !item.isScope) {
+      if (item is js_ast.Block && !item.isScope) {
         _copyAndFlattenBlocks(result, item.statements);
       } else if (item != null) {
         result.add(item);
@@ -544,11 +546,11 @@
   /// Do not call this directly; you want [_emitMemberName], which knows how to
   /// handle the many details involved in naming.
   @protected
-  JS.TemporaryId getExtensionSymbolInternal(String name) {
+  js_ast.TemporaryId getExtensionSymbolInternal(String name) {
     return _extensionSymbols.putIfAbsent(
         name,
-        () => JS.TemporaryId(
-            '\$${JS.friendlyNameForDartOperator[name] ?? name}'));
+        () => js_ast.TemporaryId(
+            '\$${js_ast.friendlyNameForDartOperator[name] ?? name}'));
   }
 
   /// Shorthand for identifier-like property names.
@@ -556,7 +558,7 @@
   /// identifiers if it can.
   // TODO(jmesserly): avoid the round tripping through quoted form.
   @protected
-  JS.LiteralString propertyName(String name) => js.string(name, "'");
+  js_ast.LiteralString propertyName(String name) => js.string(name, "'");
 
   /// Unique identifier indicating the location to inline the source map.
   ///
@@ -568,7 +570,7 @@
 }
 
 /// Whether a variable with [name] is referenced in the [node].
-bool variableIsReferenced(String name, JS.Node node) {
+bool variableIsReferenced(String name, js_ast.Node node) {
   var finder = _IdentifierFinder.instance;
   finder.nameToFind = name;
   finder.found = false;
@@ -576,7 +578,7 @@
   return finder.found;
 }
 
-class _IdentifierFinder extends JS.BaseVisitor<void> {
+class _IdentifierFinder extends js_ast.BaseVisitor<void> {
   String nameToFind;
   bool found = false;
 
@@ -593,18 +595,18 @@
   }
 }
 
-class YieldFinder extends JS.BaseVisitor {
+class YieldFinder extends js_ast.BaseVisitor {
   bool hasYield = false;
   bool hasThis = false;
   bool _nestedFunction = false;
 
   @override
-  visitThis(JS.This node) {
+  visitThis(js_ast.This node) {
     hasThis = true;
   }
 
   @override
-  visitFunctionExpression(JS.FunctionExpression node) {
+  visitFunctionExpression(js_ast.FunctionExpression node) {
     var savedNested = _nestedFunction;
     _nestedFunction = true;
     super.visitFunctionExpression(node);
@@ -612,13 +614,13 @@
   }
 
   @override
-  visitYield(JS.Yield node) {
+  visitYield(js_ast.Yield node) {
     if (!_nestedFunction) hasYield = true;
     super.visitYield(node);
   }
 
   @override
-  visitNode(JS.Node node) {
+  visitNode(js_ast.Node node) {
     if (hasYield && hasThis) return; // found both, nothing more to do.
     super.visitNode(node);
   }
@@ -626,39 +628,40 @@
 
 /// Given the function [fn], returns a function declaration statement, binding
 /// `this` and `super` if necessary (using an arrow function).
-JS.Statement toBoundFunctionStatement(JS.Fun fn, JS.Identifier name) {
+js_ast.Statement toBoundFunctionStatement(
+    js_ast.Fun fn, js_ast.Identifier name) {
   if (usesThisOrSuper(fn)) {
     return js.statement('const # = (#) => {#}', [name, fn.params, fn.body]);
   } else {
-    return JS.FunctionDeclaration(name, fn);
+    return js_ast.FunctionDeclaration(name, fn);
   }
 }
 
 /// Returns whether [node] uses `this` or `super`.
-bool usesThisOrSuper(JS.Expression node) {
+bool usesThisOrSuper(js_ast.Expression node) {
   var finder = _ThisOrSuperFinder.instance;
   finder.found = false;
   node.accept(finder);
   return finder.found;
 }
 
-class _ThisOrSuperFinder extends JS.BaseVisitor<void> {
+class _ThisOrSuperFinder extends js_ast.BaseVisitor<void> {
   bool found = false;
 
   static final instance = _ThisOrSuperFinder();
 
   @override
-  visitThis(JS.This node) {
+  visitThis(js_ast.This node) {
     found = true;
   }
 
   @override
-  visitSuper(JS.Super node) {
+  visitSuper(js_ast.Super node) {
     found = true;
   }
 
   @override
-  visitNode(JS.Node node) {
+  visitNode(js_ast.Node node) {
     if (!found) super.visitNode(node);
   }
 }
diff --git a/pkg/dev_compiler/lib/src/js_ast/js_ast.dart b/pkg/dev_compiler/lib/src/js_ast/js_ast.dart
index ee8289e..bf84e7a 100644
--- a/pkg/dev_compiler/lib/src/js_ast/js_ast.dart
+++ b/pkg/dev_compiler/lib/src/js_ast/js_ast.dart
@@ -5,6 +5,7 @@
 library js_ast;
 
 import 'precedence.dart';
+// ignore: library_prefixes
 import 'characters.dart' as charCodes;
 
 part 'nodes.dart';
diff --git a/pkg/dev_compiler/lib/src/kernel/command.dart b/pkg/dev_compiler/lib/src/kernel/command.dart
index 364981a..ca85c12 100644
--- a/pkg/dev_compiler/lib/src/kernel/command.dart
+++ b/pkg/dev_compiler/lib/src/kernel/command.dart
@@ -18,11 +18,11 @@
 import 'package:path/path.dart' as path;
 import 'package:source_maps/source_maps.dart' show SourceMapBuilder;
 
-import '../compiler/js_names.dart' as JS;
+import '../compiler/js_names.dart' as js_ast;
 import '../compiler/module_builder.dart';
 import '../compiler/shared_command.dart';
 import '../compiler/shared_compiler.dart';
-import '../js_ast/js_ast.dart' as JS;
+import '../js_ast/js_ast.dart' as js_ast;
 import '../js_ast/js_ast.dart' show js;
 import '../js_ast/source_map_printer.dart' show SourceMapPrintingContext;
 
@@ -130,13 +130,14 @@
   // the correct location and keeps the real file location hidden from the
   // front end.
   var multiRootScheme = argResults['multi-root-scheme'] as String;
+  var multiRootPaths = (argResults['multi-root'] as Iterable<String>)
+      .map(Uri.base.resolve)
+      .toList();
+  var multiRootOutputPath =
+      _longestPrefixingPath(path.absolute(output), multiRootPaths);
 
   var fileSystem = MultiRootFileSystem(
-      multiRootScheme,
-      (argResults['multi-root'] as Iterable<String>)
-          .map(Uri.base.resolve)
-          .toList(),
-      fe.StandardFileSystem.instance);
+      multiRootScheme, multiRootPaths, fe.StandardFileSystem.instance);
 
   Uri toCustomUri(Uri uri) {
     if (uri.scheme == '') {
@@ -354,7 +355,8 @@
       jsUrl: path.toUri(output).toString(),
       mapUrl: path.toUri(output + '.map').toString(),
       bazelMapping: options.bazelMapping,
-      customScheme: multiRootScheme);
+      customScheme: multiRootScheme,
+      multiRootOutputPath: multiRootOutputPath);
 
   outFiles.add(file.writeAsString(jsCode.code));
   if (jsCode.sourceMap != null) {
@@ -384,32 +386,35 @@
   JSCode(this.code, this.sourceMap);
 }
 
-JSCode jsProgramToCode(JS.Program moduleTree, ModuleFormat format,
+JSCode jsProgramToCode(js_ast.Program moduleTree, ModuleFormat format,
     {bool buildSourceMap = false,
     bool inlineSourceMap = false,
     String jsUrl,
     String mapUrl,
     Map<String, String> bazelMapping,
-    String customScheme}) {
-  var opts = JS.JavaScriptPrintingOptions(
+    String customScheme,
+    String multiRootOutputPath}) {
+  var opts = js_ast.JavaScriptPrintingOptions(
       allowKeywordsInProperties: true, allowSingleLineIfStatements: true);
-  JS.SimpleJavaScriptPrintingContext printer;
+  js_ast.SimpleJavaScriptPrintingContext printer;
   SourceMapBuilder sourceMap;
   if (buildSourceMap) {
     var sourceMapContext = SourceMapPrintingContext();
     sourceMap = sourceMapContext.sourceMap;
     printer = sourceMapContext;
   } else {
-    printer = JS.SimpleJavaScriptPrintingContext();
+    printer = js_ast.SimpleJavaScriptPrintingContext();
   }
 
   var tree = transformModuleFormat(format, moduleTree);
-  tree.accept(JS.Printer(opts, printer, localNamer: JS.TemporaryNamer(tree)));
+  tree.accept(
+      js_ast.Printer(opts, printer, localNamer: js_ast.TemporaryNamer(tree)));
 
   Map builtMap;
   if (buildSourceMap && sourceMap != null) {
     builtMap = placeSourceMap(
-        sourceMap.build(jsUrl), mapUrl, bazelMapping, customScheme);
+        sourceMap.build(jsUrl), mapUrl, bazelMapping, customScheme,
+        multiRootOutputPath: multiRootOutputPath);
     var jsDir = path.dirname(path.fromUri(jsUrl));
     var relative = path.relative(path.fromUri(mapUrl), from: jsDir);
     var relativeMapUrl = path.toUri(relative).toString();
@@ -505,3 +510,14 @@
     dir = parent;
   }
 }
+
+/// Inputs must be absolute paths. Returns null if no prefixing path is found.
+String _longestPrefixingPath(String basePath, List<Uri> prefixingPaths) {
+  return prefixingPaths.fold(null, (String previousValue, Uri element) {
+    if (basePath.startsWith(element.path) &&
+        (previousValue == null || previousValue.length < element.path.length)) {
+      return element.path;
+    }
+    return previousValue;
+  });
+}
diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart
index 531ca40..9971f7c 100644
--- a/pkg/dev_compiler/lib/src/kernel/compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart
@@ -15,12 +15,12 @@
 import 'package:kernel/type_environment.dart';
 import 'package:source_span/source_span.dart' show SourceLocation;
 
-import '../compiler/js_names.dart' as JS;
-import '../compiler/js_utils.dart' as JS;
+import '../compiler/js_names.dart' as js_ast;
+import '../compiler/js_utils.dart' as js_ast;
 import '../compiler/module_builder.dart' show pathToJSIdentifier;
 import '../compiler/shared_command.dart' show SharedCompilerOptions;
 import '../compiler/shared_compiler.dart';
-import '../js_ast/js_ast.dart' as JS;
+import '../js_ast/js_ast.dart' as js_ast;
 import '../js_ast/js_ast.dart' show js;
 import '../js_ast/source_map_printer.dart' show NodeEnd, NodeSpan, HoverComment;
 import 'constants.dart';
@@ -35,10 +35,10 @@
 class ProgramCompiler extends Object
     with SharedCompiler<Library, Class, InterfaceType, FunctionNode>
     implements
-        StatementVisitor<JS.Statement>,
-        ExpressionVisitor<JS.Expression>,
-        DartTypeVisitor<JS.Expression>,
-        ConstantVisitor<JS.Expression> {
+        StatementVisitor<js_ast.Statement>,
+        ExpressionVisitor<js_ast.Expression>,
+        DartTypeVisitor<js_ast.Expression>,
+        ConstantVisitor<js_ast.Expression> {
   final SharedCompilerOptions options;
 
   /// Maps a library URI import, that is not in [_libraries], to the
@@ -52,15 +52,15 @@
   VariableDeclaration _rethrowParameter;
 
   /// In an async* function, this represents the stream controller parameter.
-  JS.TemporaryId _asyncStarController;
+  js_ast.TemporaryId _asyncStarController;
 
   Set<Class> _pendingClasses;
 
   /// Temporary variables mapped to their corresponding JavaScript variable.
-  final _tempVariables = <VariableDeclaration, JS.TemporaryId>{};
+  final _tempVariables = <VariableDeclaration, js_ast.TemporaryId>{};
 
   /// Let variables collected for the given function.
-  List<JS.TemporaryId> _letVariables;
+  List<js_ast.TemporaryId> _letVariables;
 
   /// The class that is emitting its base class or mixin references, otherwise
   /// null.
@@ -116,7 +116,7 @@
 
   bool _superAllowed = true;
 
-  final _superHelpers = Map<String, JS.Method>();
+  final _superHelpers = Map<String, js_ast.Method>();
 
   // Compilation of Kernel's [BreakStatement].
   //
@@ -257,7 +257,7 @@
 
   bool get emitMetadata => options.emitMetadata;
 
-  JS.Program emitModule(Component component, List<Component> summaries,
+  js_ast.Program emitModule(Component component, List<Component> summaries,
       List<Uri> summaryUris, Map<Uri, String> moduleImportForSummary) {
     if (moduleItems.isNotEmpty) {
       throw StateError('Can only call emitModule once.');
@@ -354,7 +354,7 @@
   String libraryToModule(Library library) {
     if (library.importUri.scheme == 'dart') {
       // TODO(jmesserly): we need to split out HTML.
-      return JS.dartSdkModule;
+      return js_ast.dartSdkModule;
     }
     var summary = _importToSummary[library];
     var moduleName = _summaryToModule[summary];
@@ -466,7 +466,7 @@
     if (c != null && _emittingClassExtends) _emitClass(c);
   }
 
-  JS.Statement _emitClassDeclaration(Class c) {
+  js_ast.Statement _emitClassDeclaration(Class c) {
     // Mixins are unrolled in _defineClass.
     if (c.isAnonymousMixin) return null;
 
@@ -481,8 +481,8 @@
     // https://github.com/dart-lang/sdk/issues/31003
     var className = c.typeParameters.isNotEmpty
         ? (c == _jsArrayClass
-            ? JS.Identifier(c.name)
-            : JS.TemporaryId(getLocalClassName(c)))
+            ? js_ast.Identifier(c.name)
+            : js_ast.TemporaryId(getLocalClassName(c)))
         : _emitTopLevelName(c);
 
     var savedClassProperties = _classProperties;
@@ -492,9 +492,9 @@
     var jsCtors = _defineConstructors(c, className);
     var jsMethods = _emitClassMethods(c);
 
-    var body = <JS.Statement>[];
+    var body = <js_ast.Statement>[];
     _emitSuperHelperSymbols(body);
-    var deferredSupertypes = <JS.Statement>[];
+    var deferredSupertypes = <js_ast.Statement>[];
 
     // Emit the class, e.g. `core.Object = class Object { ... }`
     _defineClass(c, className, jsMethods, body, deferredSupertypes);
@@ -518,7 +518,7 @@
     }
     _emitClassMetadata(c.annotations, className, body);
 
-    var classDef = JS.Statement.from(body);
+    var classDef = js_ast.Statement.from(body);
     var typeFormals = c.typeParameters;
     if (typeFormals.isNotEmpty) {
       classDef = _defineClassTypeArguments(
@@ -535,13 +535,13 @@
     }
 
     _classProperties = savedClassProperties;
-    return JS.Statement.from(body);
+    return js_ast.Statement.from(body);
   }
 
   /// Wraps a possibly generic class in its type arguments.
-  JS.Statement _defineClassTypeArguments(
-      NamedNode c, List<TypeParameter> formals, JS.Statement body,
-      [JS.Expression className, List<JS.Statement> deferredBaseClass]) {
+  js_ast.Statement _defineClassTypeArguments(
+      NamedNode c, List<TypeParameter> formals, js_ast.Statement body,
+      [js_ast.Expression className, List<js_ast.Statement> deferredBaseClass]) {
     assert(formals.isNotEmpty);
     var name = getTopLevelName(c);
     var jsFormals = _emitTypeFormals(formals);
@@ -549,7 +549,7 @@
       jsFormals,
       _typeTable.discharge(formals),
       body,
-      className ?? JS.Identifier(name)
+      className ?? js_ast.Identifier(name)
     ]);
 
     var genericArgs = [
@@ -565,14 +565,15 @@
         [genericName, genericCall, _emitTopLevelName(c), genericName]);
   }
 
-  JS.Statement _emitClassStatement(Class c, JS.Expression className,
-      JS.Expression heritage, List<JS.Method> methods) {
+  js_ast.Statement _emitClassStatement(Class c, js_ast.Expression className,
+      js_ast.Expression heritage, List<js_ast.Method> methods) {
     if (c.typeParameters.isNotEmpty) {
-      return JS.ClassExpression(className as JS.Identifier, heritage, methods)
+      return js_ast.ClassExpression(
+              className as js_ast.Identifier, heritage, methods)
           .toStatement();
     }
-    var classExpr = JS.ClassExpression(
-        JS.TemporaryId(getLocalClassName(c)), heritage, methods);
+    var classExpr = js_ast.ClassExpression(
+        js_ast.TemporaryId(getLocalClassName(c)), heritage, methods);
     return js.statement('# = #;', [className, classExpr]);
   }
 
@@ -605,46 +606,50 @@
   /// minimal compiler and runtime changes.
   void _emitMixinStatement(
       Class c,
-      JS.Expression className,
-      JS.Expression heritage,
-      List<JS.Method> methods,
-      List<JS.Statement> body) {
+      js_ast.Expression className,
+      js_ast.Expression heritage,
+      List<js_ast.Method> methods,
+      List<js_ast.Statement> body) {
     var staticMethods = methods.where((m) => m.isStatic).toList();
     var instanceMethods = methods.where((m) => !m.isStatic).toList();
 
     body.add(_emitClassStatement(c, className, heritage, staticMethods));
-    var superclassId = JS.TemporaryId(getLocalClassName(c.superclass));
-    var classId = className is JS.Identifier
+    var superclassId = js_ast.TemporaryId(getLocalClassName(c.superclass));
+    var classId = className is js_ast.Identifier
         ? className
-        : JS.TemporaryId(getLocalClassName(c));
+        : js_ast.TemporaryId(getLocalClassName(c));
 
     var mixinMemberClass =
-        JS.ClassExpression(classId, superclassId, instanceMethods);
+        js_ast.ClassExpression(classId, superclassId, instanceMethods);
 
-    JS.Node arrowFnBody = mixinMemberClass;
-    var extensionInit = <JS.Statement>[];
+    js_ast.Node arrowFnBody = mixinMemberClass;
+    var extensionInit = <js_ast.Statement>[];
     _defineExtensionMembers(classId, extensionInit);
     if (extensionInit.isNotEmpty) {
       extensionInit.insert(0, mixinMemberClass.toStatement());
       extensionInit.add(classId.toReturn());
-      arrowFnBody = JS.Block(extensionInit);
+      arrowFnBody = js_ast.Block(extensionInit);
     }
 
     body.add(js.statement('#[#.mixinOn] = #', [
       className,
       runtimeModule,
-      JS.ArrowFun([superclassId], arrowFnBody)
+      js_ast.ArrowFun([superclassId], arrowFnBody)
     ]));
   }
 
-  void _defineClass(Class c, JS.Expression className, List<JS.Method> methods,
-      List<JS.Statement> body, List<JS.Statement> deferredSupertypes) {
+  void _defineClass(
+      Class c,
+      js_ast.Expression className,
+      List<js_ast.Method> methods,
+      List<js_ast.Statement> body,
+      List<js_ast.Statement> deferredSupertypes) {
     if (c == coreTypes.objectClass) {
       body.add(_emitClassStatement(c, className, null, methods));
       return;
     }
 
-    JS.Expression emitDeferredType(DartType t) {
+    js_ast.Expression emitDeferredType(DartType t) {
       if (t is InterfaceType && t.typeArguments.isNotEmpty) {
         _declareBeforeUse(t.classNode);
         return _emitGenericClassType(t, t.typeArguments.map(emitDeferredType));
@@ -709,8 +714,9 @@
 
     var hasUnnamedSuper = _hasUnnamedInheritedConstructor(superclass);
 
-    void emitMixinConstructors(JS.Expression className, InterfaceType mixin) {
-      JS.Statement mixinCtor;
+    void emitMixinConstructors(
+        js_ast.Expression className, InterfaceType mixin) {
+      js_ast.Statement mixinCtor;
       if (_hasUnnamedConstructor(mixin.classNode)) {
         mixinCtor = js.statement('#.#.call(this);', [
           emitClassRef(mixin),
@@ -732,7 +738,7 @@
             _emitSuperConstructorCall(className, name, jsParams),
         ];
         body.add(_addConstructorToClass(
-            c, className, name, JS.Fun(jsParams, JS.Block(ctorBody))));
+            c, className, name, js_ast.Fun(jsParams, js_ast.Block(ctorBody))));
       }
     }
 
@@ -775,8 +781,8 @@
         //     applyMixin(C, class C$ extends M { <methods>  });
         mixinBody.add(runtimeStatement('applyMixin(#, #)', [
           classExpr,
-          JS.ClassExpression(
-              JS.TemporaryId(getLocalClassName(c)), mixinClass, methods)
+          js_ast.ClassExpression(
+              js_ast.TemporaryId(getLocalClassName(c)), mixinClass, methods)
         ]));
       }
 
@@ -794,14 +800,14 @@
       var m = mixins[i];
       var mixinName =
           getLocalClassName(superclass) + '_' + getLocalClassName(m.classNode);
-      var mixinId = JS.TemporaryId(mixinName + '\$');
+      var mixinId = js_ast.TemporaryId(mixinName + '\$');
       // Bind the mixin class to a name to workaround a V8 bug with es6 classes
       // and anonymous function names.
       // TODO(leafp:) Eliminate this once the bug is fixed:
       // https://bugs.chromium.org/p/v8/issues/detail?id=7069
       body.add(js.statement("const # = #", [
         mixinId,
-        JS.ClassExpression(JS.TemporaryId(mixinName), baseClass, [])
+        js_ast.ClassExpression(js_ast.TemporaryId(mixinName), baseClass, [])
       ]));
 
       emitMixinConstructors(mixinId, m);
@@ -828,14 +834,15 @@
   }
 
   /// Defines all constructors for this class as ES5 constructors.
-  List<JS.Statement> _defineConstructors(Class c, JS.Expression className) {
-    var body = <JS.Statement>[];
+  List<js_ast.Statement> _defineConstructors(
+      Class c, js_ast.Expression className) {
+    var body = <js_ast.Statement>[];
     if (c.isAnonymousMixin || isMixinAliasClass(c)) {
       // We already handled this when we defined the class.
       return body;
     }
 
-    addConstructor(String name, JS.Expression jsCtor) {
+    addConstructor(String name, js_ast.Expression jsCtor) {
       body.add(_addConstructorToClass(c, className, name, jsCtor));
     }
 
@@ -861,9 +868,9 @@
     return body;
   }
 
-  JS.Statement _emitClassTypeTests(
-      Class c, JS.Expression className, List<JS.Statement> body) {
-    JS.Expression getInterfaceSymbol(Class interface) {
+  js_ast.Statement _emitClassTypeTests(
+      Class c, js_ast.Expression className, List<js_ast.Statement> body) {
+    js_ast.Expression getInterfaceSymbol(Class interface) {
       var library = interface.enclosingLibrary;
       if (library == coreTypes.coreLibrary ||
           library == coreTypes.asyncLibrary) {
@@ -880,7 +887,7 @@
       return null;
     }
 
-    void markSubtypeOf(JS.Expression testSymbol) {
+    void markSubtypeOf(js_ast.Expression testSymbol) {
       body.add(js.statement('#.prototype[#] = true', [className, testSymbol]));
     }
 
@@ -1054,7 +1061,7 @@
     if (isClassSymbol == null) {
       // TODO(jmesserly): we could export these symbols, if we want to mark
       // implemented interfaces for user-defined classes.
-      var id = JS.TemporaryId("_is_${getLocalClassName(c)}_default");
+      var id = js_ast.TemporaryId("_is_${getLocalClassName(c)}_default");
       moduleItems.add(
           js.statement('const # = Symbol(#);', [id, js.string(id.name, "'")]));
       isClassSymbol = id;
@@ -1073,21 +1080,21 @@
   }
 
   void _emitDartSymbols(
-      Iterable<JS.TemporaryId> vars, List<JS.ModuleItem> body) {
+      Iterable<js_ast.TemporaryId> vars, List<js_ast.ModuleItem> body) {
     for (var id in vars) {
       body.add(js.statement('const # = Symbol(#)', [id, js.string(id.name)]));
     }
   }
 
-  void _emitSuperHelperSymbols(List<JS.Statement> body) {
+  void _emitSuperHelperSymbols(List<js_ast.Statement> body) {
     _emitDartSymbols(
-        _superHelpers.values.map((m) => m.name as JS.TemporaryId), body);
+        _superHelpers.values.map((m) => m.name as js_ast.TemporaryId), body);
     _superHelpers.clear();
   }
 
   /// Emits static fields for a class, and initialize them eagerly if possible,
   /// otherwise define them as lazy properties.
-  void _emitStaticFields(Class c, List<JS.Statement> body) {
+  void _emitStaticFields(Class c, List<js_ast.Statement> body) {
     var fields = c.fields
         .where((f) => f.isStatic && getRedirectingFactories(f) == null)
         .toList();
@@ -1113,15 +1120,15 @@
     }
   }
 
-  void _emitClassMetadata(List<Expression> metadata, JS.Expression className,
-      List<JS.Statement> body) {
+  void _emitClassMetadata(List<Expression> metadata,
+      js_ast.Expression className, List<js_ast.Statement> body) {
     // Metadata
     if (emitMetadata && metadata.isNotEmpty) {
       body.add(js.statement('#[#.metadata] = #;', [
         className,
         runtimeModule,
-        _arrowFunctionWithLetScope(() =>
-            JS.ArrayInitializer(metadata.map(_instantiateAnnotation).toList()))
+        _arrowFunctionWithLetScope(() => js_ast.ArrayInitializer(
+            metadata.map(_instantiateAnnotation).toList()))
       ]));
     }
   }
@@ -1140,18 +1147,18 @@
   /// If a concrete class implements one of our extensions, we might need to
   /// add forwarders.
   void _defineExtensionMembers(
-      JS.Expression className, List<JS.Statement> body) {
+      js_ast.Expression className, List<js_ast.Statement> body) {
     void emitExtensions(String helperName, Iterable<String> extensions) {
       if (extensions.isEmpty) return;
 
       var names = extensions
-          .map((e) => propertyName(JS.memberNameForDartMember(e)))
+          .map((e) => propertyName(js_ast.memberNameForDartMember(e)))
           .toList();
       body.add(js.statement('#.#(#, #);', [
         runtimeModule,
         helperName,
         className,
-        JS.ArrayInitializer(names, multiline: names.length > 4)
+        js_ast.ArrayInitializer(names, multiline: names.length > 4)
       ]));
     }
 
@@ -1162,7 +1169,7 @@
 
   /// Emit the signature on the class recording the runtime type information
   void _emitClassSignature(
-      Class c, JS.Expression className, List<JS.Statement> body) {
+      Class c, js_ast.Expression className, List<js_ast.Statement> body) {
     var savedClass = _classEmittingSignatures;
     _classEmittingSignatures = c;
 
@@ -1176,30 +1183,30 @@
       ]));
     }
 
-    void emitSignature(String name, List<JS.Property> elements) {
+    void emitSignature(String name, List<js_ast.Property> elements) {
       if (elements.isEmpty) return;
 
       if (!name.startsWith('Static')) {
         var proto = c == coreTypes.objectClass
             ? js.call('Object.create(null)')
             : runtimeCall('get${name}s(#.__proto__)', [className]);
-        elements.insert(0, JS.Property(propertyName('__proto__'), proto));
+        elements.insert(0, js_ast.Property(propertyName('__proto__'), proto));
       }
       body.add(runtimeStatement('set${name}Signature(#, () => #)', [
         className,
-        JS.ObjectInitializer(elements, multiline: elements.length > 1)
+        js_ast.ObjectInitializer(elements, multiline: elements.length > 1)
       ]));
     }
 
     var extMethods = _classProperties.extensionMethods;
     var extAccessors = _classProperties.extensionAccessors;
-    var staticMethods = <JS.Property>[];
-    var instanceMethods = <JS.Property>[];
-    var staticGetters = <JS.Property>[];
-    var instanceGetters = <JS.Property>[];
-    var staticSetters = <JS.Property>[];
-    var instanceSetters = <JS.Property>[];
-    List<JS.Property> getSignatureList(Procedure p) {
+    var staticMethods = <js_ast.Property>[];
+    var instanceMethods = <js_ast.Property>[];
+    var staticGetters = <js_ast.Property>[];
+    var instanceGetters = <js_ast.Property>[];
+    var staticSetters = <js_ast.Property>[];
+    var instanceSetters = <js_ast.Property>[];
+    List<js_ast.Property> getSignatureList(Procedure p) {
       if (p.isStatic) {
         if (p.isGetter) {
           return staticGetters;
@@ -1242,7 +1249,7 @@
           reifiedType != _getMemberRuntimeType(memberOverride, c);
 
       if (needsSignature) {
-        JS.Expression type;
+        js_ast.Expression type;
         if (member.isAccessor) {
           type = _emitAnnotatedResult(
               _emitType(member.isGetter
@@ -1253,12 +1260,12 @@
         } else {
           type = _emitAnnotatedFunctionType(reifiedType, member);
         }
-        var property = JS.Property(_declareMemberName(member), type);
+        var property = js_ast.Property(_declareMemberName(member), type);
         var signatures = getSignatureList(member);
         signatures.add(property);
         if (!member.isStatic &&
             (extMethods.contains(name) || extAccessors.contains(name))) {
-          signatures.add(JS.Property(
+          signatures.add(js_ast.Property(
               _declareMemberName(member, useExtension: true), type));
         }
       }
@@ -1275,8 +1282,8 @@
       js.escapedString(jsLibraryDebuggerName(c.enclosingLibrary))
     ]));
 
-    var instanceFields = <JS.Property>[];
-    var staticFields = <JS.Property>[];
+    var instanceFields = <js_ast.Property>[];
+    var staticFields = <js_ast.Property>[];
 
     var classFields = c.fields.toList();
     for (var field in classFields) {
@@ -1287,13 +1294,13 @@
       var memberName = _declareMemberName(field);
       var fieldSig = _emitFieldSignature(field, c);
       (isStatic ? staticFields : instanceFields)
-          .add(JS.Property(memberName, fieldSig));
+          .add(js_ast.Property(memberName, fieldSig));
     }
     emitSignature('Field', instanceFields);
     emitSignature('StaticField', staticFields);
 
     if (emitMetadata) {
-      var constructors = <JS.Property>[];
+      var constructors = <js_ast.Property>[];
       var allConstructors = [
         ...c.constructors,
         ...c.procedures.where((p) => p.isFactory),
@@ -1302,7 +1309,7 @@
         var memberName = _constructorName(ctor.name.name);
         var type = _emitAnnotatedFunctionType(
             ctor.function.functionType.withoutTypeParameters, ctor);
-        constructors.add(JS.Property(memberName, type));
+        constructors.add(js_ast.Property(memberName, type));
       }
       emitSignature('Constructor', constructors);
     }
@@ -1317,14 +1324,14 @@
     _classEmittingSignatures = savedClass;
   }
 
-  JS.Expression _emitFieldSignature(Field field, Class fromClass) {
+  js_ast.Expression _emitFieldSignature(Field field, Class fromClass) {
     var type = _getTypeFromClass(field.type, field.enclosingClass, fromClass);
     var args = [_emitType(type)];
     var annotations = field.annotations;
     if (emitMetadata && annotations != null && annotations.isNotEmpty) {
       var savedUri = _currentUri;
       _currentUri = field.enclosingClass.fileUri;
-      args.add(JS.ArrayInitializer(
+      args.add(js_ast.ArrayInitializer(
           annotations.map(_instantiateAnnotation).toList()));
       _currentUri = savedUri;
     }
@@ -1367,8 +1374,8 @@
         .substituteType(type);
   }
 
-  JS.Expression _emitConstructor(
-      Constructor node, List<Field> fields, JS.Expression className) {
+  js_ast.Expression _emitConstructor(
+      Constructor node, List<Field> fields, js_ast.Expression className) {
     var savedUri = _currentUri;
     _currentUri = node.fileUri ?? savedUri;
     var params = _emitParameters(node.function);
@@ -1381,11 +1388,11 @@
     _currentUri = savedUri;
     end ??= _nodeEnd(node.enclosingClass.fileEndOffset);
 
-    return JS.Fun(params, JS.Block(body))..sourceInformation = end;
+    return js_ast.Fun(params, js_ast.Block(body))..sourceInformation = end;
   }
 
-  List<JS.Statement> _emitConstructorBody(
-      Constructor node, List<Field> fields, JS.Expression className) {
+  List<js_ast.Statement> _emitConstructorBody(
+      Constructor node, List<Field> fields, js_ast.Expression className) {
     var cls = node.enclosingClass;
 
     // Generate optional/named argument value assignment. These can not have
@@ -1427,7 +1434,7 @@
     return body;
   }
 
-  JS.Expression _constructorName(String name) {
+  js_ast.Expression _constructorName(String name) {
     if (name == '') {
       // Default constructors (factory or not) use `new` as their name.
       return propertyName('new');
@@ -1435,8 +1442,8 @@
     return _emitStaticMemberName(name);
   }
 
-  JS.Statement _emitRedirectingConstructor(
-      RedirectingInitializer node, JS.Expression className) {
+  js_ast.Statement _emitRedirectingConstructor(
+      RedirectingInitializer node, js_ast.Expression className) {
     var ctor = node.target;
     // We can't dispatch to the constructor with `this.new` as that might hit a
     // derived class constructor with the same name.
@@ -1447,13 +1454,13 @@
     ]);
   }
 
-  JS.Statement _emitSuperConstructorCallIfNeeded(
-      Class c, JS.Expression className,
+  js_ast.Statement _emitSuperConstructorCallIfNeeded(
+      Class c, js_ast.Expression className,
       [SuperInitializer superInit]) {
     if (c == coreTypes.objectClass) return null;
 
     Constructor ctor;
-    List<JS.Expression> args;
+    List<js_ast.Expression> args;
     if (superInit == null) {
       ctor = unnamedConstructor(c.superclass);
       args = [];
@@ -1470,8 +1477,8 @@
     return _emitSuperConstructorCall(className, ctor.name.name, args);
   }
 
-  JS.Statement _emitSuperConstructorCall(
-      JS.Expression className, String name, List<JS.Expression> args) {
+  js_ast.Statement _emitSuperConstructorCall(
+      js_ast.Expression className, String name, List<js_ast.Expression> args) {
     return js.statement('#.__proto__.#.call(this, #);',
         [className, _constructorName(name), args ?? []]);
   }
@@ -1499,7 +1506,7 @@
   ///   2. field initializing parameters,
   ///   3. constructor field initializers,
   ///   4. initialize fields not covered in 1-3
-  JS.Statement _initializeFields(List<Field> fields, [Constructor ctor]) {
+  js_ast.Statement _initializeFields(List<Field> fields, [Constructor ctor]) {
     // Run field initializers if they can have side-effects.
     Set<Field> ctorFields;
     if (ctor != null) {
@@ -1509,7 +1516,7 @@
             ..remove(null);
     }
 
-    var body = <JS.Statement>[];
+    var body = <js_ast.Statement>[];
     emitFieldInit(Field f, Expression initializer, TreeNode hoverInfo) {
       var access = _classProperties.virtualFields[f] ?? _declareMemberName(f);
       var jsInit = _visitInitializer(initializer, f.annotations);
@@ -1543,20 +1550,20 @@
       }
     }
 
-    return JS.Statement.from(body);
+    return js_ast.Statement.from(body);
   }
 
-  JS.Expression _visitInitializer(
+  js_ast.Expression _visitInitializer(
       Expression init, List<Expression> annotations) {
     // explicitly initialize to null, to avoid getting `undefined`.
     // TODO(jmesserly): do this only for vars that aren't definitely assigned.
-    if (init == null) return JS.LiteralNull();
+    if (init == null) return js_ast.LiteralNull();
     return _annotatedNullCheck(annotations)
         ? notNull(init)
         : _visitExpression(init);
   }
 
-  JS.Expression notNull(Expression expr) {
+  js_ast.Expression notNull(Expression expr) {
     if (expr == null) return null;
     var jsExpr = _visitExpression(expr);
     if (!isNullable(expr)) return jsExpr;
@@ -1574,8 +1581,8 @@
             mixin.constructors.every((c) => c.isExternal);
   }
 
-  JS.Statement _addConstructorToClass(
-      Class c, JS.Expression className, String name, JS.Expression jsCtor) {
+  js_ast.Statement _addConstructorToClass(Class c, js_ast.Expression className,
+      String name, js_ast.Expression jsCtor) {
     jsCtor = defineValueOnClass(c, className, _constructorName(name), jsCtor);
     return js.statement('#.prototype = #.prototype;', [jsCtor, className]);
   }
@@ -1597,18 +1604,18 @@
     }
   }
 
-  List<JS.Method> _emitClassMethods(Class c) {
+  List<js_ast.Method> _emitClassMethods(Class c) {
     var virtualFields = _classProperties.virtualFields;
 
-    var jsMethods = <JS.Method>[];
+    var jsMethods = <js_ast.Method>[];
     bool hasJsPeer = _extensionTypes.isNativeClass(c);
     bool hasIterator = false;
 
     if (c == coreTypes.objectClass) {
       // Dart does not use ES6 constructors.
       // Add an error to catch any invalid usage.
-      jsMethods
-          .add(JS.Method(propertyName('constructor'), js.fun(r'''function() {
+      jsMethods.add(
+          js_ast.Method(propertyName('constructor'), js.fun(r'''function() {
                   throw Error("use `new " + #.typeName(#.getReifiedType(this)) +
                       ".new(...)` to create a Dart object");
               }''', [runtimeModule, runtimeModule])));
@@ -1619,7 +1626,7 @@
       //
       // This will become obsolete when
       // https://github.com/dart-lang/sdk/issues/31003 is addressed.
-      jsMethods.add(JS.Method(
+      jsMethods.add(js_ast.Method(
           propertyName('constructor'), js.fun(r'function() { return []; }')));
     }
 
@@ -1715,12 +1722,12 @@
   }
 
   /// Emits a method, getter, or setter.
-  JS.Method _emitMethodDeclaration(Procedure member) {
+  js_ast.Method _emitMethodDeclaration(Procedure member) {
     if (member.isAbstract) {
       return null;
     }
 
-    JS.Fun fn;
+    js_ast.Fun fn;
     if (member.isExternal && !member.isNoSuchMethodForwarder) {
       if (member.isStatic) {
         // TODO(vsm): Do we need to handle this case?
@@ -1731,27 +1738,28 @@
       fn = _emitFunction(member.function, member.name.name);
     }
 
-    return JS.Method(_declareMemberName(member), fn,
+    return js_ast.Method(_declareMemberName(member), fn,
         isGetter: member.isGetter,
         isSetter: member.isSetter,
         isStatic: member.isStatic)
       ..sourceInformation = _nodeEnd(member.fileEndOffset);
   }
 
-  JS.Fun _emitNativeFunctionBody(Procedure node) {
+  js_ast.Fun _emitNativeFunctionBody(Procedure node) {
     String name = getAnnotationName(node, isJSAnnotation) ?? node.name.name;
     if (node.isGetter) {
-      return JS.Fun([], js.block('{ return this.#; }', [name]));
+      return js_ast.Fun([], js.block('{ return this.#; }', [name]));
     } else if (node.isSetter) {
       var params = _emitParameters(node.function);
-      return JS.Fun(params, js.block('{ this.# = #; }', [name, params.last]));
+      return js_ast.Fun(
+          params, js.block('{ this.# = #; }', [name, params.last]));
     } else {
       return js.fun(
           'function (...args) { return this.#.apply(this, args); }', name);
     }
   }
 
-  List<JS.Method> _emitCovarianceCheckStub(Procedure member) {
+  List<js_ast.Method> _emitCovarianceCheckStub(Procedure member) {
     // TODO(jmesserly): kernel stubs have a few problems:
     // - they're generated even when there is no concrete super member
     // - the stub parameter types don't match the types we need to check to
@@ -1781,12 +1789,12 @@
       var setterType = substituteType(superMember.setterType);
       if (types.isTop(setterType)) return const [];
       return [
-        JS.Method(
+        js_ast.Method(
             name,
             js.fun('function(x) { return super.# = #; }',
-                [name, _emitCast(JS.Identifier('x'), setterType)]),
+                [name, _emitCast(js_ast.Identifier('x'), setterType)]),
             isSetter: true),
-        JS.Method(name, js.fun('function() { return super.#; }', [name]),
+        js_ast.Method(name, js.fun('function() { return super.#; }', [name]),
             isGetter: true)
       ];
     }
@@ -1796,16 +1804,16 @@
         substituteType(superMember.function.functionType) as FunctionType;
     var function = member.function;
 
-    var body = <JS.Statement>[];
+    var body = <js_ast.Statement>[];
     var typeParameters = superMethodType.typeParameters;
     _emitCovarianceBoundsCheck(typeParameters, body);
 
     var typeFormals = _emitTypeFormals(typeParameters);
-    var jsParams = List<JS.Parameter>.from(typeFormals);
+    var jsParams = List<js_ast.Parameter>.from(typeFormals);
     var positionalParameters = function.positionalParameters;
     for (var i = 0, n = positionalParameters.length; i < n; i++) {
       var param = positionalParameters[i];
-      var jsParam = JS.Identifier(param.name);
+      var jsParam = js_ast.Identifier(param.name);
       jsParams.add(jsParam);
 
       if (isCovariantParameter(param) &&
@@ -1829,7 +1837,8 @@
         body.add(js.statement('if (# in #) #;', [
           name,
           namedArgumentTemp,
-          _emitCast(JS.PropertyAccess(namedArgumentTemp, name), paramType.type)
+          _emitCast(
+              js_ast.PropertyAccess(namedArgumentTemp, name), paramType.type)
         ]));
       }
     }
@@ -1838,11 +1847,11 @@
 
     if (namedParameters.isNotEmpty) jsParams.add(namedArgumentTemp);
     body.add(js.statement('return super.#(#);', [name, jsParams]));
-    return [JS.Method(name, JS.Fun(jsParams, JS.Block(body)))];
+    return [js_ast.Method(name, js_ast.Fun(jsParams, js_ast.Block(body)))];
   }
 
   /// Emits a Dart factory constructor to a JS static method.
-  JS.Method _emitFactoryConstructor(Procedure node) {
+  js_ast.Method _emitFactoryConstructor(Procedure node) {
     if (node.isExternal || isUnsupportedFactoryConstructor(node)) return null;
 
     var function = node.function;
@@ -1858,14 +1867,14 @@
     /// [_emitFunction] instead.
     var jsBody = _emitSyncFunctionBody(function);
 
-    return JS.Method(_constructorName(node.name.name),
-        JS.Fun(_emitParameters(function), jsBody),
+    return js_ast.Method(_constructorName(node.name.name),
+        js_ast.Fun(_emitParameters(function), jsBody),
         isStatic: true)
       ..sourceInformation = _nodeEnd(node.fileEndOffset);
   }
 
   @override
-  JS.Expression emitConstructorAccess(InterfaceType type) {
+  js_ast.Expression emitConstructorAccess(InterfaceType type) {
     return _emitJSInterop(type.classNode) ?? visitInterfaceType(type);
   }
 
@@ -1876,23 +1885,24 @@
   /// would end up calling the getter or setter, and one of those might not even
   /// exist, resulting in a runtime error. Even if they did exist, that's the
   /// wrong behavior if a new field was declared.
-  List<JS.Method> _emitVirtualFieldAccessor(Field field) {
+  List<js_ast.Method> _emitVirtualFieldAccessor(Field field) {
     var virtualField = _classProperties.virtualFields[field];
     var name = _declareMemberName(field);
 
     var getter = js.fun('function() { return this[#]; }', [virtualField]);
-    var jsGetter = JS.Method(name, getter, isGetter: true)
+    var jsGetter = js_ast.Method(name, getter, isGetter: true)
       ..sourceInformation = _nodeStart(field);
 
-    var args = field.isFinal ? [JS.Super(), name] : [JS.This(), virtualField];
+    var args =
+        field.isFinal ? [js_ast.Super(), name] : [js_ast.This(), virtualField];
 
-    JS.Expression value = JS.Identifier('value');
+    js_ast.Expression value = js_ast.Identifier('value');
     if (!field.isFinal && isCovariantField(field)) {
       value = _emitCast(value, field.type);
     }
     args.add(value);
 
-    var jsSetter = JS.Method(
+    var jsSetter = js_ast.Method(
         name, js.fun('function(value) { #[#] = #; }', args),
         isSetter: true)
       ..sourceInformation = _nodeStart(field);
@@ -1904,25 +1914,25 @@
   /// field.  Note that the Dart names are always symbolized to avoid
   /// conflicts.  They will be installed as extension methods on the underlying
   /// native type.
-  List<JS.Method> _emitNativeFieldAccessors(Field field) {
+  List<js_ast.Method> _emitNativeFieldAccessors(Field field) {
     // TODO(vsm): Can this by meta-programmed?
     // E.g., dart.nativeField(symbol, jsName)
     // Alternatively, perhaps it could be meta-programmed directly in
     // dart.registerExtensions?
-    var jsMethods = <JS.Method>[];
+    var jsMethods = <js_ast.Method>[];
     assert(!field.isStatic);
 
     var name = getAnnotationName(field, isJSName) ?? field.name.name;
     // Generate getter
-    var fn = JS.Fun([], js.block('{ return this.#; }', [name]));
-    var method = JS.Method(_declareMemberName(field), fn, isGetter: true);
+    var fn = js_ast.Fun([], js.block('{ return this.#; }', [name]));
+    var method = js_ast.Method(_declareMemberName(field), fn, isGetter: true);
     jsMethods.add(method);
 
     // Generate setter
     if (!field.isFinal) {
-      var value = JS.TemporaryId('value');
-      fn = JS.Fun([value], js.block('{ this.# = #; }', [name, value]));
-      method = JS.Method(_declareMemberName(field), fn, isSetter: true);
+      var value = js_ast.TemporaryId('value');
+      fn = js_ast.Fun([value], js.block('{ this.# = #; }', [name, value]));
+      method = js_ast.Method(_declareMemberName(field), fn, isSetter: true);
       jsMethods.add(method);
     }
 
@@ -1935,7 +1945,7 @@
   /// This is needed because in ES6, if you only override a getter
   /// (alternatively, a setter), then there is an implicit override of the
   /// setter (alternatively, the getter) that does nothing.
-  JS.Method _emitSuperAccessorWrapper(Procedure member,
+  js_ast.Method _emitSuperAccessorWrapper(Procedure member,
       Map<String, Procedure> getters, Map<String, Procedure> setters) {
     if (member.isAbstract) return null;
 
@@ -1946,7 +1956,7 @@
           _classProperties.inheritedSetters.contains(name)) {
         // Generate a setter that forwards to super.
         var fn = js.fun('function(value) { super[#] = value; }', [memberName]);
-        return JS.Method(memberName, fn, isSetter: true);
+        return js_ast.Method(memberName, fn, isSetter: true);
       }
     } else {
       assert(member.isSetter);
@@ -1954,7 +1964,7 @@
           _classProperties.inheritedGetters.contains(name)) {
         // Generate a getter that forwards to super.
         var fn = js.fun('function() { return super[#]; }', [memberName]);
-        return JS.Method(memberName, fn, isGetter: true);
+        return js_ast.Method(memberName, fn, isGetter: true);
       }
     }
     return null;
@@ -1968,7 +1978,7 @@
   /// This will return `null` if the adapter was already added on a super type,
   /// otherwise it returns the adapter code.
   // TODO(jmesserly): should we adapt `Iterator` too?
-  JS.Method _emitIterable(Class c) {
+  js_ast.Method _emitIterable(Class c) {
     var iterable = hierarchy.getClassAsInstanceOf(c, coreTypes.iterableClass);
     if (iterable == null) return null;
 
@@ -1989,19 +1999,19 @@
 
     // Otherwise, emit the adapter method, which wraps the Dart iterator in
     // an ES6 iterator.
-    return JS.Method(
+    return js_ast.Method(
         js.call('Symbol.iterator'),
         js.call('function() { return new #.JsIterator(this.#); }', [
           runtimeModule,
           _emitMemberName('iterator', memberClass: coreTypes.iterableClass)
-        ]) as JS.Fun);
+        ]) as js_ast.Fun);
   }
 
-  JS.Expression _instantiateAnnotation(Expression node) =>
+  js_ast.Expression _instantiateAnnotation(Expression node) =>
       _visitExpression(node);
 
   void _registerExtensionType(
-      Class c, String jsPeerName, List<JS.Statement> body) {
+      Class c, String jsPeerName, List<js_ast.Statement> body) {
     var className = _emitTopLevelName(c);
     if (_typeRep.isPrimitive(c.rawType)) {
       body.add(runtimeStatement(
@@ -2041,24 +2051,27 @@
         emitLibraryName(_currentLibrary), fields, _emitTopLevelMemberName));
   }
 
-  JS.Statement _emitLazyFields(JS.Expression objExpr, Iterable<Field> fields,
-      JS.Expression Function(Field f) emitFieldName) {
-    var accessors = <JS.Method>[];
+  js_ast.Statement _emitLazyFields(
+      js_ast.Expression objExpr,
+      Iterable<Field> fields,
+      js_ast.Expression Function(Field f) emitFieldName) {
+    var accessors = <js_ast.Method>[];
     var savedUri = _currentUri;
 
     for (var field in fields) {
       _currentUri = field.fileUri;
       var access = emitFieldName(field);
-      accessors.add(
-          JS.Method(access, _emitStaticFieldInitializer(field), isGetter: true)
-            ..sourceInformation = _hoverComment(
-                JS.PropertyAccess(objExpr, access),
-                field.fileOffset,
-                field.name.name.length));
+      accessors.add(js_ast.Method(access, _emitStaticFieldInitializer(field),
+          isGetter: true)
+        ..sourceInformation = _hoverComment(
+            js_ast.PropertyAccess(objExpr, access),
+            field.fileOffset,
+            field.name.name.length));
 
       // TODO(jmesserly): currently uses a dummy setter to indicate writable.
       if (!field.isFinal && !field.isConst) {
-        accessors.add(JS.Method(access, js.call('function(_) {}') as JS.Fun,
+        accessors.add(js_ast.Method(
+            access, js.call('function(_) {}') as js_ast.Fun,
             isSetter: true));
       }
     }
@@ -2068,15 +2081,16 @@
     return runtimeStatement('defineLazy(#, { # })', [objExpr, accessors]);
   }
 
-  JS.Fun _emitStaticFieldInitializer(Field field) {
-    return JS.Fun(
+  js_ast.Fun _emitStaticFieldInitializer(Field field) {
+    return js_ast.Fun(
         [],
-        JS.Block(_withLetScope(() => [
-              JS.Return(_visitInitializer(field.initializer, field.annotations))
+        js_ast.Block(_withLetScope(() => [
+              js_ast.Return(
+                  _visitInitializer(field.initializer, field.annotations))
             ])));
   }
 
-  List<JS.Statement> _withLetScope(List<JS.Statement> visitBody()) {
+  List<js_ast.Statement> _withLetScope(List<js_ast.Statement> visitBody()) {
     var savedLetVariables = _letVariables;
     _letVariables = [];
 
@@ -2088,7 +2102,7 @@
     return body;
   }
 
-  JS.ArrowFun _arrowFunctionWithLetScope(JS.Expression visitBody()) {
+  js_ast.ArrowFun _arrowFunctionWithLetScope(js_ast.Expression visitBody()) {
     var savedLetVariables = _letVariables;
     _letVariables = [];
 
@@ -2096,11 +2110,11 @@
     var letVars = _initLetVariables();
 
     _letVariables = savedLetVariables;
-    return JS.ArrowFun(
-        [], letVars == null ? expr : JS.Block([letVars, expr.toReturn()]));
+    return js_ast.ArrowFun(
+        [], letVars == null ? expr : js_ast.Block([letVars, expr.toReturn()]));
   }
 
-  JS.PropertyAccess _emitTopLevelName(NamedNode n, {String suffix = ''}) {
+  js_ast.PropertyAccess _emitTopLevelName(NamedNode n, {String suffix = ''}) {
     return _emitJSInterop(n) ?? _emitTopLevelNameNoInterop(n, suffix: suffix);
   }
 
@@ -2108,7 +2122,7 @@
   ///
   /// Unlike call sites, we always have an element available, so we can use it
   /// directly rather than computing the relevant options for [_emitMemberName].
-  JS.Expression _declareMemberName(Member m, {bool useExtension}) {
+  js_ast.Expression _declareMemberName(Member m, {bool useExtension}) {
     return _emitMemberName(m.name.name,
         isStatic: m is Field ? m.isStatic : (m as Procedure).isStatic,
         useExtension:
@@ -2156,7 +2170,7 @@
   /// Equality is a bit special, it is generated via the Dart `equals` runtime
   /// helper, that checks for null. The user defined method is called '=='.
   ///
-  JS.Expression _emitMemberName(String name,
+  js_ast.Expression _emitMemberName(String name,
       {bool isStatic = false,
       bool useExtension,
       Member member,
@@ -2175,9 +2189,9 @@
         var parts = runtimeName.split('.');
         if (parts.length < 2) return propertyName(runtimeName);
 
-        JS.Expression result = JS.Identifier(parts[0]);
+        js_ast.Expression result = js_ast.Identifier(parts[0]);
         for (int i = 1; i < parts.length; i++) {
-          result = JS.PropertyAccess(result, propertyName(parts[i]));
+          result = js_ast.PropertyAccess(result, propertyName(parts[i]));
         }
         return result;
       }
@@ -2193,7 +2207,7 @@
     }
 
     useExtension ??= _isSymbolizedMember(memberClass, name);
-    name = JS.memberNameForDartMember(
+    name = js_ast.memberNameForDartMember(
         name, member is Procedure && member.isExternal);
     if (useExtension) {
       return getExtensionSymbolInternal(name);
@@ -2248,7 +2262,7 @@
             hierarchy.getDispatchTarget(c, Name(name), setter: true));
   }
 
-  JS.Expression _emitStaticMemberName(String name, [NamedNode member]) {
+  js_ast.Expression _emitStaticMemberName(String name, [NamedNode member]) {
     if (member != null) {
       var jsName = _emitJSInteropStaticMemberName(member);
       if (jsName != null) return jsName;
@@ -2280,7 +2294,7 @@
     return propertyName(name);
   }
 
-  JS.Expression _emitJSInteropStaticMemberName(NamedNode n) {
+  js_ast.Expression _emitJSInteropStaticMemberName(NamedNode n) {
     if (!usesJSInterop(n)) return null;
     var name = getAnnotationName(n, isPublicJSAnnotation);
     if (name != null) {
@@ -2295,9 +2309,9 @@
     return js.escapedString(name, "'");
   }
 
-  JS.PropertyAccess _emitTopLevelNameNoInterop(NamedNode n,
+  js_ast.PropertyAccess _emitTopLevelNameNoInterop(NamedNode n,
       {String suffix = ''}) {
-    return JS.PropertyAccess(emitLibraryName(getLibrary(n)),
+    return js_ast.PropertyAccess(emitLibraryName(getLibrary(n)),
         _emitTopLevelMemberName(n, suffix: suffix));
   }
 
@@ -2305,7 +2319,7 @@
   ///
   /// NOTE: usually you should use [_emitTopLevelName] instead of this. This
   /// function does not handle JS interop.
-  JS.Expression _emitTopLevelMemberName(NamedNode n, {String suffix = ''}) {
+  js_ast.Expression _emitTopLevelMemberName(NamedNode n, {String suffix = ''}) {
     var name = getJSExportName(n) ?? getTopLevelName(n);
     return propertyName(name + suffix);
   }
@@ -2318,18 +2332,18 @@
     return libraryJSName != null ? '$libraryJSName.$jsName' : jsName;
   }
 
-  JS.PropertyAccess _emitJSInterop(NamedNode n) {
+  js_ast.PropertyAccess _emitJSInterop(NamedNode n) {
     var jsName = _getJSNameWithoutGlobal(n);
     if (jsName == null) return null;
     return _emitJSInteropForGlobal(jsName);
   }
 
-  JS.PropertyAccess _emitJSInteropForGlobal(String name) {
+  js_ast.PropertyAccess _emitJSInteropForGlobal(String name) {
     var parts = name.split('.');
     if (parts.isEmpty) parts = [''];
-    JS.PropertyAccess access;
+    js_ast.PropertyAccess access;
     for (var part in parts) {
-      access = JS.PropertyAccess(
+      access = js_ast.PropertyAccess(
           access ?? runtimeCall('global'), js.escapedString(part, "'"));
     }
     return access;
@@ -2354,12 +2368,12 @@
     ]));
   }
 
-  JS.Method _emitLibraryAccessor(Procedure node) {
+  js_ast.Method _emitLibraryAccessor(Procedure node) {
     var savedUri = _currentUri;
     _currentUri = node.fileUri;
 
     var name = node.name.name;
-    var result = JS.Method(
+    var result = js_ast.Method(
         propertyName(name), _emitFunction(node.function, node.name.name),
         isGetter: node.isGetter, isSetter: node.isSetter)
       ..sourceInformation = _nodeEnd(node.fileEndOffset);
@@ -2368,21 +2382,22 @@
     return result;
   }
 
-  JS.Statement _emitLibraryFunction(Procedure p) {
+  js_ast.Statement _emitLibraryFunction(Procedure p) {
     var savedUri = _currentUri;
     _currentUri = p.fileUri;
 
-    var body = <JS.Statement>[];
+    var body = <js_ast.Statement>[];
     var fn = _emitFunction(p.function, p.name.name)
       ..sourceInformation = _nodeEnd(p.fileEndOffset);
 
     if (_currentLibrary.importUri.scheme == 'dart' &&
         _isInlineJSFunction(p.function.body)) {
-      fn = JS.simplifyPassThroughArrowFunCallBody(fn);
+      fn = js_ast.simplifyPassThroughArrowFunCallBody(fn);
     }
 
     var nameExpr = _emitTopLevelName(p);
-    body.add(js.statement('# = #', [nameExpr, fn]));
+    body.add(js.statement('# = #',
+        [nameExpr, js_ast.NamedFunction(js_ast.TemporaryId(p.name.name), fn)]));
     // Function types of top-level/static functions are only needed when
     // dart:mirrors is enabled.
     // TODO(jmesserly): do we even need this for mirrors, since statics are not
@@ -2394,10 +2409,10 @@
     }
 
     _currentUri = savedUri;
-    return JS.Statement.from(body);
+    return js_ast.Statement.from(body);
   }
 
-  JS.Expression _emitFunctionTagged(JS.Expression fn, FunctionType type,
+  js_ast.Expression _emitFunctionTagged(js_ast.Expression fn, FunctionType type,
       {bool topLevel = false}) {
     var lazy = topLevel && !_canEmitTypeAtTopLevel(type);
     var typeRep = visitFunctionType(type, lazy: lazy);
@@ -2436,31 +2451,33 @@
   }
 
   /// Emits a Dart [type] into code.
-  JS.Expression _emitType(DartType type) => type.accept(this) as JS.Expression;
+  js_ast.Expression _emitType(DartType type) =>
+      type.accept(this) as js_ast.Expression;
 
-  JS.Expression _emitInvalidNode(Node node, [String message = '']) {
+  js_ast.Expression _emitInvalidNode(Node node, [String message = '']) {
     if (message.isNotEmpty) message += ' ';
     return runtimeCall('throwUnimplementedError(#)',
         [js.escapedString('node <${node.runtimeType}> $message`$node`')]);
   }
 
   @override
-  JS.Expression defaultDartType(DartType type) => _emitInvalidNode(type);
+  js_ast.Expression defaultDartType(DartType type) => _emitInvalidNode(type);
 
   @override
-  JS.Expression visitInvalidType(InvalidType type) => defaultDartType(type);
+  js_ast.Expression visitInvalidType(InvalidType type) => defaultDartType(type);
 
   @override
-  JS.Expression visitDynamicType(DynamicType type) => runtimeCall('dynamic');
+  js_ast.Expression visitDynamicType(DynamicType type) =>
+      runtimeCall('dynamic');
 
   @override
-  JS.Expression visitVoidType(VoidType type) => runtimeCall('void');
+  js_ast.Expression visitVoidType(VoidType type) => runtimeCall('void');
 
   @override
-  JS.Expression visitBottomType(BottomType type) => runtimeCall('bottom');
+  js_ast.Expression visitBottomType(BottomType type) => runtimeCall('bottom');
 
   @override
-  JS.Expression visitInterfaceType(InterfaceType type) {
+  js_ast.Expression visitInterfaceType(InterfaceType type) {
     var c = type.classNode;
     _declareBeforeUse(c);
 
@@ -2488,7 +2505,7 @@
     }
 
     var args = type.typeArguments;
-    Iterable<JS.Expression> jsArgs;
+    Iterable<js_ast.Expression> jsArgs;
     if (args.any((a) => a != const DynamicType())) {
       jsArgs = args.map(_emitType);
     }
@@ -2511,14 +2528,15 @@
       !_emittingClassExtends && !_emittingClassSignatures ||
       _currentFunction != null;
 
-  JS.Expression _emitGenericClassType(
-      InterfaceType t, Iterable<JS.Expression> typeArgs) {
+  js_ast.Expression _emitGenericClassType(
+      InterfaceType t, Iterable<js_ast.Expression> typeArgs) {
     var genericName = _emitTopLevelNameNoInterop(t.classNode, suffix: '\$');
     return js.call('#(#)', [genericName, typeArgs]);
   }
 
   @override
-  JS.Expression visitFunctionType(type, {Member member, bool lazy = false}) {
+  js_ast.Expression visitFunctionType(type,
+      {Member member, bool lazy = false}) {
     var requiredTypes =
         type.positionalParameters.take(type.requiredParameterCount).toList();
     var function = member?.function;
@@ -2535,7 +2553,7 @@
     var rt = _emitType(type.returnType);
     var ra = _emitTypeNames(requiredTypes, requiredParams, member);
 
-    List<JS.Expression> typeParts;
+    List<js_ast.Expression> typeParts;
     if (namedTypes.isNotEmpty) {
       assert(optionalTypes.isEmpty);
       // TODO(vsm): Pass in annotations here as well.
@@ -2554,7 +2572,7 @@
     if (typeFormals.isNotEmpty) {
       var tf = _emitTypeFormals(typeFormals);
 
-      addTypeFormalsAsParameters(List<JS.Expression> elements) {
+      addTypeFormalsAsParameters(List<js_ast.Expression> elements) {
         var names = _typeTable.discharge(typeFormals);
         return names.isEmpty
             ? js.call('(#) => [#]', [tf, elements])
@@ -2606,7 +2624,8 @@
         : typeRep;
   }
 
-  JS.Expression _emitAnnotatedFunctionType(FunctionType type, Member member) {
+  js_ast.Expression _emitAnnotatedFunctionType(
+      FunctionType type, Member member) {
     var result = visitFunctionType(type, member: member);
 
     var annotations = member.annotations;
@@ -2614,7 +2633,7 @@
       // TODO(jmesserly): should we disable source info for annotations?
       var savedUri = _currentUri;
       _currentUri = member.enclosingClass.fileUri;
-      result = JS.ArrayInitializer(
+      result = js_ast.ArrayInitializer(
           [result]..addAll(annotations.map(_instantiateAnnotation)));
       _currentUri = savedUri;
     }
@@ -2622,46 +2641,46 @@
   }
 
   /// Emits an expression that lets you access statics on a [type] from code.
-  JS.Expression _emitConstructorAccess(InterfaceType type) {
+  js_ast.Expression _emitConstructorAccess(InterfaceType type) {
     return _emitJSInterop(type.classNode) ?? _emitType(type);
   }
 
-  JS.Expression _emitConstructorName(InterfaceType type, Member c) {
+  js_ast.Expression _emitConstructorName(InterfaceType type, Member c) {
     return _emitJSInterop(type.classNode) ??
-        JS.PropertyAccess(
+        js_ast.PropertyAccess(
             _emitConstructorAccess(type), _constructorName(c.name.name));
   }
 
   /// Emits an expression that lets you access statics on an [c] from code.
-  JS.Expression _emitStaticClassName(Class c) {
+  js_ast.Expression _emitStaticClassName(Class c) {
     _declareBeforeUse(c);
     return _emitTopLevelName(c);
   }
 
   // Wrap a result - usually a type - with its metadata.  The runtime is
   // responsible for unpacking this.
-  JS.Expression _emitAnnotatedResult(
-      JS.Expression result, List<Expression> metadata, Member member) {
+  js_ast.Expression _emitAnnotatedResult(
+      js_ast.Expression result, List<Expression> metadata, Member member) {
     if (emitMetadata && metadata.isNotEmpty) {
       // TODO(jmesserly): should we disable source info for annotations?
       var savedUri = _currentUri;
       _currentUri = member.enclosingClass.fileUri;
-      result = JS.ArrayInitializer(
+      result = js_ast.ArrayInitializer(
           [result]..addAll(metadata.map(_instantiateAnnotation)));
       _currentUri = savedUri;
     }
     return result;
   }
 
-  JS.ObjectInitializer _emitTypeProperties(Iterable<NamedType> types) {
-    return JS.ObjectInitializer(types
-        .map((t) => JS.Property(propertyName(t.name), _emitType(t.type)))
+  js_ast.ObjectInitializer _emitTypeProperties(Iterable<NamedType> types) {
+    return js_ast.ObjectInitializer(types
+        .map((t) => js_ast.Property(propertyName(t.name), _emitType(t.type)))
         .toList());
   }
 
-  JS.ArrayInitializer _emitTypeNames(List<DartType> types,
+  js_ast.ArrayInitializer _emitTypeNames(List<DartType> types,
       List<VariableDeclaration> parameters, Member member) {
-    var result = <JS.Expression>[];
+    var result = <js_ast.Expression>[];
     for (int i = 0; i < types.length; ++i) {
       var type = _emitType(types[i]);
       if (parameters != null) {
@@ -2669,21 +2688,21 @@
       }
       result.add(type);
     }
-    return JS.ArrayInitializer(result);
+    return js_ast.ArrayInitializer(result);
   }
 
   @override
-  JS.Expression visitTypeParameterType(TypeParameterType type) =>
+  js_ast.Expression visitTypeParameterType(TypeParameterType type) =>
       _emitTypeParameter(type.parameter);
 
-  JS.Identifier _emitTypeParameter(TypeParameter t) =>
-      JS.Identifier(getTypeParameterName(t));
+  js_ast.Identifier _emitTypeParameter(TypeParameter t) =>
+      js_ast.Identifier(getTypeParameterName(t));
 
   @override
-  JS.Expression visitTypedefType(TypedefType type) =>
+  js_ast.Expression visitTypedefType(TypedefType type) =>
       visitFunctionType(type.unalias as FunctionType);
 
-  JS.Fun _emitFunction(FunctionNode f, String name) {
+  js_ast.Fun _emitFunction(FunctionNode f, String name) {
     // normal function (sync), vs (sync*, async, async*)
     var isSync = f.asyncMarker == AsyncMarker.Sync;
     var formals = _emitParameters(f);
@@ -2698,26 +2717,26 @@
     // potentially mutated in Kernel. For now we assume all parameters are.
     super.enterFunction(name, formals, () => true);
 
-    JS.Block block =
+    js_ast.Block block =
         isSync ? _emitSyncFunctionBody(f) : _emitGeneratorFunctionBody(f, name);
 
     block = super.exitFunction(name, formals, block);
-    return JS.Fun(formals, block);
+    return js_ast.Fun(formals, block);
   }
 
-  List<JS.Parameter> _emitParameters(FunctionNode f) {
+  List<js_ast.Parameter> _emitParameters(FunctionNode f) {
     var positional = f.positionalParameters;
-    var result = List<JS.Parameter>.of(positional.map(_emitVariableDef));
+    var result = List<js_ast.Parameter>.of(positional.map(_emitVariableDef));
     if (positional.isNotEmpty &&
         f.requiredParameterCount == positional.length &&
         positional.last.annotations.any(isJsRestAnnotation)) {
-      result.last = JS.RestParameter(result.last as JS.Identifier);
+      result.last = js_ast.RestParameter(result.last as js_ast.Identifier);
     }
     if (f.namedParameters.isNotEmpty) result.add(namedArgumentTemp);
     return result;
   }
 
-  void _emitVirtualFieldSymbols(Class c, List<JS.Statement> body) {
+  void _emitVirtualFieldSymbols(Class c, List<js_ast.Statement> body) {
     _classProperties.virtualFields.forEach((field, virtualField) {
       body.add(js.statement('const # = Symbol(#);', [
         virtualField,
@@ -2726,9 +2745,9 @@
     });
   }
 
-  List<JS.Identifier> _emitTypeFormals(List<TypeParameter> typeFormals) {
+  List<js_ast.Identifier> _emitTypeFormals(List<TypeParameter> typeFormals) {
     return typeFormals
-        .map((t) => JS.Identifier(getTypeParameterName(t)))
+        .map((t) => js_ast.Identifier(getTypeParameterName(t)))
         .toList();
   }
 
@@ -2737,29 +2756,31 @@
   ///
   /// This is an internal part of [_emitGeneratorFunctionBody] and should not be
   /// called directly.
-  JS.Expression _emitGeneratorFunctionExpression(
+  js_ast.Expression _emitGeneratorFunctionExpression(
       FunctionNode function, String name) {
-    emitGeneratorFn(List<JS.Parameter> getParameters(JS.Block jsBody)) {
+    emitGeneratorFn(List<js_ast.Parameter> getParameters(js_ast.Block jsBody)) {
       var savedController = _asyncStarController;
       _asyncStarController = function.asyncMarker == AsyncMarker.AsyncStar
-          ? JS.TemporaryId('stream')
+          ? js_ast.TemporaryId('stream')
           : null;
 
-      JS.Expression gen;
+      js_ast.Expression gen;
       _superDisallowed(() {
         // Visit the body with our async* controller set.
         //
         // Note: we intentionally don't emit argument initializers here, because
         // they were already emitted outside of the generator expression.
-        var jsBody = JS.Block(_withCurrentFunction(
+        var jsBody = js_ast.Block(_withCurrentFunction(
             function, () => [_emitFunctionScopedBody(function)]));
-        var genFn = JS.Fun(getParameters(jsBody), jsBody, isGenerator: true);
+        var genFn =
+            js_ast.Fun(getParameters(jsBody), jsBody, isGenerator: true);
 
         // Name the function if possible, to get better stack traces.
         gen = genFn;
         if (name != null) {
-          gen = JS.NamedFunction(
-              JS.TemporaryId(JS.friendlyNameForDartOperator[name] ?? name),
+          gen = js_ast.NamedFunction(
+              js_ast.TemporaryId(
+                  js_ast.friendlyNameForDartOperator[name] ?? name),
               genFn);
         }
 
@@ -2792,7 +2813,7 @@
       var jsParams = _emitParameters(function);
       var mutatedParams = jsParams;
       var gen = emitGeneratorFn((fnBody) {
-        var mutatedVars = JS.findMutatedVariables(fnBody);
+        var mutatedVars = js_ast.findMutatedVariables(fnBody);
         mutatedParams = jsParams
             .where((id) => mutatedVars.contains(id.parameterName))
             .toList();
@@ -2857,7 +2878,7 @@
   ///
   /// To emit an `async`, `sync*`, or `async*` function body, use
   /// [_emitGeneratorFunctionBody] instead.
-  JS.Block _emitSyncFunctionBody(FunctionNode f) {
+  js_ast.Block _emitSyncFunctionBody(FunctionNode f) {
     assert(f.asyncMarker == AsyncMarker.Sync);
 
     var block = _withCurrentFunction(f, () {
@@ -2868,7 +2889,7 @@
       return block;
     });
 
-    return JS.Block(block);
+    return js_ast.Block(block);
   }
 
   /// Emits an `async`, `sync*`, or `async*` function body.
@@ -2882,18 +2903,18 @@
   ///
   /// To emit a `sync` function body (the default in Dart), use
   /// [_emitSyncFunctionBody] instead.
-  JS.Block _emitGeneratorFunctionBody(FunctionNode f, String name) {
+  js_ast.Block _emitGeneratorFunctionBody(FunctionNode f, String name) {
     assert(f.asyncMarker != AsyncMarker.Sync);
 
     var statements =
         _withCurrentFunction(f, () => _emitArgumentInitializers(f));
     statements.add(_emitGeneratorFunctionExpression(f, name).toReturn()
       ..sourceInformation = _nodeStart(f));
-    return JS.Block(statements);
+    return js_ast.Block(statements);
   }
 
-  List<JS.Statement> _withCurrentFunction(
-      FunctionNode fn, List<JS.Statement> action()) {
+  List<js_ast.Statement> _withCurrentFunction(
+      FunctionNode fn, List<js_ast.Statement> action()) {
     var savedFunction = _currentFunction;
     _currentFunction = fn;
     _nullableInference.enterFunction(fn);
@@ -2915,12 +2936,12 @@
 
   /// Emits argument initializers, which handles optional/named args, as well
   /// as generic type checks needed due to our covariance.
-  List<JS.Statement> _emitArgumentInitializers(FunctionNode f) {
-    var body = <JS.Statement>[];
+  List<js_ast.Statement> _emitArgumentInitializers(FunctionNode f) {
+    var body = <js_ast.Statement>[];
 
     _emitCovarianceBoundsCheck(f.typeParameters, body);
 
-    initParameter(VariableDeclaration p, JS.Identifier jsParam) {
+    initParameter(VariableDeclaration p, js_ast.Identifier jsParam) {
       if (isCovariantParameter(p)) {
         var castExpr = _emitCast(jsParam, p.type);
         if (!identical(castExpr, jsParam)) body.add(castExpr.toStatement());
@@ -2931,11 +2952,11 @@
     }
 
     for (var p in f.positionalParameters.take(f.requiredParameterCount)) {
-      var jsParam = JS.Identifier(p.name);
+      var jsParam = js_ast.Identifier(p.name);
       initParameter(p, jsParam);
     }
     for (var p in f.positionalParameters.skip(f.requiredParameterCount)) {
-      var jsParam = JS.Identifier(p.name);
+      var jsParam = js_ast.Identifier(p.name);
       var defaultValue = _defaultParamValue(p);
       if (defaultValue != null) {
         body.add(js.statement(
@@ -2982,23 +3003,23 @@
       !m.annotations
           .any((a) => isBuiltinAnnotation(a, '_js_helper', 'NoReifyGeneric'));
 
-  JS.Statement _nullParameterCheck(JS.Expression param) {
+  js_ast.Statement _nullParameterCheck(js_ast.Expression param) {
     var call = runtimeCall('argumentError((#))', [param]);
     return js.statement('if (# == null) #;', [param, call]);
   }
 
-  JS.Expression _defaultParamValue(VariableDeclaration p) {
+  js_ast.Expression _defaultParamValue(VariableDeclaration p) {
     if (p.annotations.any(isUndefinedAnnotation)) {
       return null;
     } else if (p.initializer != null) {
       return _visitExpression(p.initializer);
     } else {
-      return JS.LiteralNull();
+      return js_ast.LiteralNull();
     }
   }
 
   void _emitCovarianceBoundsCheck(
-      List<TypeParameter> typeFormals, List<JS.Statement> body) {
+      List<TypeParameter> typeFormals, List<js_ast.Statement> body) {
     for (var t in typeFormals) {
       if (t.isGenericCovariantImpl && !types.isTop(t.bound)) {
         body.add(runtimeStatement('checkTypeBound(#, #, #)', [
@@ -3010,19 +3031,19 @@
     }
   }
 
-  JS.Statement _visitStatement(Statement s) {
+  js_ast.Statement _visitStatement(Statement s) {
     if (s == null) return null;
-    var result = s.accept(this) as JS.Statement;
+    var result = s.accept(this) as js_ast.Statement;
     // TODO(jmesserly): is the `is! Block` still necessary?
     if (s is! Block) result.sourceInformation = _nodeStart(s);
 
     // The statement might be the target of a break or continue with a label.
     var name = _labelNames[s];
-    if (name != null) result = JS.LabeledStatement(name, result);
+    if (name != null) result = js_ast.LabeledStatement(name, result);
     return result;
   }
 
-  JS.Statement _emitFunctionScopedBody(FunctionNode f) {
+  js_ast.Statement _emitFunctionScopedBody(FunctionNode f) {
     var jsBody = _visitStatement(f.body);
     if (f.positionalParameters.isNotEmpty || f.namedParameters.isNotEmpty) {
       // Handle shadowing of parameters by local varaibles, which is allowed in
@@ -3043,7 +3064,7 @@
   }
 
   /// Visits [nodes] with [_visitExpression].
-  List<JS.Expression> _visitExpressionList(Iterable<Expression> nodes) {
+  List<js_ast.Expression> _visitExpressionList(Iterable<Expression> nodes) {
     return nodes?.map(_visitExpression)?.toList();
   }
 
@@ -3052,14 +3073,14 @@
   /// to give a more helpful message.
   // TODO(sra): When nullablility is available earlier, it would be cleaner to
   // build an input AST where the boolean conversion is a single AST node.
-  JS.Expression _visitTest(Expression node) {
+  js_ast.Expression _visitTest(Expression node) {
     if (node == null) return null;
 
     if (node is Not) {
       return visitNot(node);
     }
     if (node is LogicalExpression) {
-      JS.Expression shortCircuit(String code) {
+      js_ast.Expression shortCircuit(String code) {
         return js.call(code, [_visitTest(node.left), _visitTest(node.right)]);
       }
 
@@ -3078,9 +3099,9 @@
     return result;
   }
 
-  JS.Expression _visitExpression(Expression e) {
+  js_ast.Expression _visitExpression(Expression e) {
     if (e == null) return null;
-    var result = e.accept(this) as JS.Expression;
+    var result = e.accept(this) as js_ast.Expression;
     result.sourceInformation ??= _nodeStart(e);
     return result;
   }
@@ -3138,18 +3159,19 @@
   /// For example, top-level and static fields are defined as lazy properties,
   /// on the library/class, so their access expressions do not appear in the
   /// source code.
-  HoverComment _hoverComment(JS.Expression expr, int offset, int nameLength) {
+  HoverComment _hoverComment(
+      js_ast.Expression expr, int offset, int nameLength) {
     var start = _getLocation(offset);
     var end = _getLocation(offset + nameLength);
     return start != null && end != null ? HoverComment(expr, start, end) : null;
   }
 
   @override
-  JS.Statement defaultStatement(Statement node) =>
+  js_ast.Statement defaultStatement(Statement node) =>
       _emitInvalidNode(node).toStatement();
 
   @override
-  JS.Statement visitExpressionStatement(ExpressionStatement node) {
+  js_ast.Statement visitExpressionStatement(ExpressionStatement node) {
     var expr = node.expression;
     if (expr is StaticInvocation) {
       if (isInlineJS(expr.target)) {
@@ -3163,7 +3185,7 @@
   }
 
   @override
-  JS.Statement visitBlock(Block node) {
+  js_ast.Statement visitBlock(Block node) {
     // If this is the block body of a function, don't mark it as a separate
     // scope, because the function is the scope. This avoids generating an
     // unncessary nested block.
@@ -3172,23 +3194,24 @@
     // slightly different (in Dart, there is a nested scope), but that's handled
     // by _emitSyncFunctionBody.
     var isScope = !identical(node.parent, _currentFunction);
-    return JS.Block(node.statements.map(_visitStatement).toList(),
+    return js_ast.Block(node.statements.map(_visitStatement).toList(),
         isScope: isScope);
   }
 
   @override
-  JS.Statement visitEmptyStatement(EmptyStatement node) => JS.EmptyStatement();
+  js_ast.Statement visitEmptyStatement(EmptyStatement node) =>
+      js_ast.EmptyStatement();
 
   @override
-  JS.Statement visitAssertBlock(AssertBlock node) {
+  js_ast.Statement visitAssertBlock(AssertBlock node) {
     // AssertBlocks are introduced by the VM-specific async elimination
     // transformation.  We do not expect them to arise here.
     throw UnsupportedError('compilation of an assert block');
   }
 
   @override
-  JS.Statement visitAssertStatement(AssertStatement node) {
-    if (!options.enableAsserts) return JS.EmptyStatement();
+  js_ast.Statement visitAssertStatement(AssertStatement node) {
+    if (!options.enableAsserts) return js_ast.EmptyStatement();
     var condition = node.condition;
     var conditionType = condition.getStaticType(types);
     var jsCondition = _visitExpression(condition);
@@ -3213,7 +3236,7 @@
       jsCondition,
       runtimeModule,
       if (node.message == null)
-        JS.LiteralNull()
+        js_ast.LiteralNull()
       else
         _visitExpression(node.message),
       js.escapedString(location.sourceUrl.toString()),
@@ -3235,7 +3258,7 @@
   }
 
   @override
-  JS.Statement visitLabeledStatement(LabeledStatement node) {
+  js_ast.Statement visitLabeledStatement(LabeledStatement node) {
     List<LabeledStatement> saved;
     var target = _effectiveTargets[node];
     // If the effective target is known then this statement is either contained
@@ -3273,20 +3296,20 @@
   }
 
   @override
-  JS.Statement visitBreakStatement(BreakStatement node) {
+  js_ast.Statement visitBreakStatement(BreakStatement node) {
     // Switch statements with continue labels must explicitly break to their
     // implicit label due to their being wrapped in a loop.
     if (_inLabeledContinueSwitch &&
         _switchLabelStates.containsKey(node.target.body)) {
-      return JS.Break(_switchLabelStates[node.target.body].label);
+      return js_ast.Break(_switchLabelStates[node.target.body].label);
     }
     // Can it be compiled to a break without a label?
     if (_currentBreakTargets.contains(node.target)) {
-      return JS.Break(null);
+      return js_ast.Break(null);
     }
     // Can it be compiled to a continue without a label?
     if (_currentContinueTargets.contains(node.target)) {
-      return JS.Continue(null);
+      return js_ast.Continue(null);
     }
 
     // Ensure the effective target is labeled.  Labels are named globally per
@@ -3304,10 +3327,10 @@
       current = (current as LabeledStatement).body;
     }
     if (identical(current, target)) {
-      return JS.Break(name);
+      return js_ast.Break(name);
     }
     // Otherwise it is a continue.
-    return JS.Continue(name);
+    return js_ast.Continue(name);
   }
 
   // Labeled loop bodies can be the target of a continue without a label
@@ -3327,7 +3350,7 @@
     return body;
   }
 
-  T translateLoop<T extends JS.Statement>(Statement node, T action()) {
+  T translateLoop<T extends js_ast.Statement>(Statement node, T action()) {
     List<LabeledStatement> savedBreakTargets;
     if (_currentBreakTargets.isNotEmpty &&
         _effectiveTargets[_currentBreakTargets.first] != node) {
@@ -3347,48 +3370,49 @@
   }
 
   @override
-  JS.While visitWhileStatement(WhileStatement node) {
+  js_ast.While visitWhileStatement(WhileStatement node) {
     return translateLoop(node, () {
       var condition = _visitTest(node.condition);
       var body = _visitScope(effectiveBodyOf(node, node.body));
-      return JS.While(condition, body);
+      return js_ast.While(condition, body);
     });
   }
 
   @override
-  JS.Do visitDoStatement(DoStatement node) {
+  js_ast.Do visitDoStatement(DoStatement node) {
     return translateLoop(node, () {
       var body = _visitScope(effectiveBodyOf(node, node.body));
       var condition = _visitTest(node.condition);
-      return JS.Do(body, condition);
+      return js_ast.Do(body, condition);
     });
   }
 
   @override
-  JS.For visitForStatement(ForStatement node) {
+  js_ast.For visitForStatement(ForStatement node) {
     return translateLoop(node, () {
-      emitForInitializer(VariableDeclaration v) => JS.VariableInitialization(
-          _emitVariableDef(v), _visitInitializer(v.initializer, v.annotations));
+      emitForInitializer(VariableDeclaration v) =>
+          js_ast.VariableInitialization(_emitVariableDef(v),
+              _visitInitializer(v.initializer, v.annotations));
 
       var init = node.variables.map(emitForInitializer).toList();
       var initList =
-          init.isEmpty ? null : JS.VariableDeclarationList('let', init);
+          init.isEmpty ? null : js_ast.VariableDeclarationList('let', init);
       var updates = node.updates;
-      JS.Expression update;
+      js_ast.Expression update;
       if (updates.isNotEmpty) {
-        update =
-            JS.Expression.binary(updates.map(_visitExpression).toList(), ',')
-                .toVoidExpression();
+        update = js_ast.Expression.binary(
+                updates.map(_visitExpression).toList(), ',')
+            .toVoidExpression();
       }
       var condition = _visitTest(node.condition);
       var body = _visitScope(effectiveBodyOf(node, node.body));
 
-      return JS.For(initList, condition, update, body);
+      return js_ast.For(initList, condition, update, body);
     });
   }
 
   @override
-  JS.Statement visitForInStatement(ForInStatement node) {
+  js_ast.Statement visitForInStatement(ForInStatement node) {
     return translateLoop(node, () {
       if (node.isAsync) {
         return _emitAwaitFor(node);
@@ -3399,20 +3423,22 @@
 
       var init = js.call('let #', _emitVariableDef(node.variable));
       if (_annotatedNullCheck(node.variable.annotations)) {
-        body = JS.Block(
+        body = js_ast.Block(
             [_nullParameterCheck(_emitVariableRef(node.variable)), body]);
       }
 
       if (variableIsReferenced(node.variable.name, iterable)) {
-        var temp = JS.TemporaryId('iter');
-        return JS.Block(
-            [iterable.toVariableDeclaration(temp), JS.ForOf(init, temp, body)]);
+        var temp = js_ast.TemporaryId('iter');
+        return js_ast.Block([
+          iterable.toVariableDeclaration(temp),
+          js_ast.ForOf(init, temp, body)
+        ]);
       }
-      return JS.ForOf(init, iterable, body);
+      return js_ast.ForOf(init, iterable, body);
     });
   }
 
-  JS.Statement _emitAwaitFor(ForInStatement node) {
+  js_ast.Statement _emitAwaitFor(ForInStatement node) {
     // Emits `await for (var value in stream) ...`, which desugars as:
     //
     // var iter = new StreamIterator(stream);
@@ -3431,14 +3457,14 @@
     // TODO(jmesserly): we may want a helper if these become common. For now the
     // full desugaring seems okay.
     var streamIterator = _asyncStreamIteratorClass.rawType;
-    var createStreamIter = JS.Call(
+    var createStreamIter = js_ast.Call(
         _emitConstructorName(
             streamIterator,
             _asyncStreamIteratorClass.procedures
                 .firstWhere((p) => p.isFactory && p.name.name == '')),
         [_visitExpression(node.iterable)]);
 
-    var iter = JS.TemporaryId('iter');
+    var iter = js_ast.TemporaryId('iter');
     return js.statement(
         '{'
         '  let # = #;'
@@ -3449,18 +3475,18 @@
         [
           iter,
           createStreamIter,
-          JS.Yield(js.call('#.moveNext()', iter))
+          js_ast.Yield(js.call('#.moveNext()', iter))
             ..sourceInformation = _nodeStart(node.variable),
           _emitVariableDef(node.variable),
           iter,
           _visitStatement(node.body),
-          JS.Yield(js.call('#.cancel()', iter))
+          js_ast.Yield(js.call('#.cancel()', iter))
             ..sourceInformation = _nodeStart(node.variable)
         ]);
   }
 
   @override
-  JS.Statement visitSwitchStatement(SwitchStatement node) {
+  js_ast.Statement visitSwitchStatement(SwitchStatement node) {
     // Switches with labeled continues are generated as an infinite loop with
     // an explicit variable for holding the switch's next case state and an
     // explicit label. Any implicit breaks are made explicit (e.g., when break
@@ -3468,10 +3494,10 @@
     var previous = _inLabeledContinueSwitch;
     _inLabeledContinueSwitch = hasLabeledContinue(node);
 
-    var cases = <JS.SwitchCase>[];
+    var cases = <js_ast.SwitchCase>[];
 
     if (_inLabeledContinueSwitch) {
-      var labelState = JS.TemporaryId("labelState");
+      var labelState = js_ast.TemporaryId("labelState");
       // TODO(markzipan): Retrieve the real label name with source offsets
       var labelName = 'SL${_switchLabelStates.length}';
       _switchLabelStates[node] = SwitchLabelState(labelName, labelState);
@@ -3483,13 +3509,13 @@
       }
 
       var switchExpr = _visitExpression(node.expression);
-      var switchStmt = JS.Switch(labelState, cases);
-      var loopBody = JS.Block([switchStmt, JS.Break(null)]);
-      var loopStmt = JS.While(js.boolean(true), loopBody);
+      var switchStmt = js_ast.Switch(labelState, cases);
+      var loopBody = js_ast.Block([switchStmt, js_ast.Break(null)]);
+      var loopStmt = js_ast.While(js.boolean(true), loopBody);
       // Note: Cannot use _labelNames, as the label must be on the loop.
       // not the block surrounding the switch statement.
-      var labeledStmt = JS.LabeledStatement(labelName, loopStmt);
-      var block = JS.Block([
+      var labeledStmt = js_ast.LabeledStatement(labelName, loopStmt);
+      var block = js_ast.Block([
         js.statement('let # = #', [labelState, switchExpr]),
         labeledStmt
       ]);
@@ -3502,7 +3528,7 @@
       if (subcases.isNotEmpty) cases.addAll(subcases);
     }
 
-    var stmt = JS.Switch(_visitExpression(node.expression), cases);
+    var stmt = js_ast.Switch(_visitExpression(node.expression), cases);
     _inLabeledContinueSwitch = previous;
     return stmt;
   }
@@ -3513,10 +3539,10 @@
   /// labeled continues. Dart permits the final case to implicitly break, but
   /// switch statements with labeled continues must explicitly break/continue
   /// to escape the surrounding infinite loop.
-  List<JS.SwitchCase> _visitSwitchCase(SwitchCase node,
+  List<js_ast.SwitchCase> _visitSwitchCase(SwitchCase node,
       {bool lastSwitchCase = false}) {
-    var cases = <JS.SwitchCase>[];
-    var emptyBlock = JS.Block.empty();
+    var cases = <js_ast.SwitchCase>[];
+    var emptyBlock = js_ast.Block.empty();
     // TODO(jmesserly): make sure we are statically checking fall through
     var body = _visitStatement(node.body).toBlock();
     var expressions = node.expressions;
@@ -3524,19 +3550,19 @@
         expressions.isNotEmpty && !node.isDefault ? expressions.last : null;
     for (var e in expressions) {
       var jsExpr = _visitExpression(e);
-      cases.add(JS.SwitchCase(jsExpr, e == lastExpr ? body : emptyBlock));
+      cases.add(js_ast.SwitchCase(jsExpr, e == lastExpr ? body : emptyBlock));
     }
     if (node.isDefault) {
-      cases.add(JS.SwitchCase.defaultCase(body));
+      cases.add(js_ast.SwitchCase.defaultCase(body));
     }
     // Switch statements with continue labels must explicitly break from their
     // last case to escape the additional loop around the switch.
     if (lastSwitchCase && _inLabeledContinueSwitch && cases.isNotEmpty) {
       // TODO(markzipan): avoid generating unreachable breaks
       assert(_switchLabelStates.containsKey(node.parent));
-      var breakStmt = JS.Break(_switchLabelStates[node.parent].label);
-      var switchBody = JS.Block(cases.last.body.statements..add(breakStmt));
-      var updatedSwitch = JS.SwitchCase(cases.last.expression, switchBody);
+      var breakStmt = js_ast.Break(_switchLabelStates[node.parent].label);
+      var switchBody = js_ast.Block(cases.last.body.statements..add(breakStmt));
+      var updatedSwitch = js_ast.SwitchCase(cases.last.expression, switchBody);
       cases.removeLast();
       cases.add(updatedSwitch);
     }
@@ -3544,7 +3570,7 @@
   }
 
   @override
-  JS.Statement visitContinueSwitchStatement(ContinueSwitchStatement node) {
+  js_ast.Statement visitContinueSwitchStatement(ContinueSwitchStatement node) {
     var switchStmt = node.target.parent;
     if (_inLabeledContinueSwitch &&
         _switchLabelStates.containsKey(switchStmt)) {
@@ -3555,8 +3581,8 @@
           ? js.call("Symbol('_default')", [])
           : _visitExpression(node.target.expressions[0]);
       var setStateStmt = js.statement("# = #", [switchState.variable, jsExpr]);
-      var continueStmt = JS.Continue(switchState.label);
-      return JS.Block([setStateStmt, continueStmt]);
+      var continueStmt = js_ast.Continue(switchState.label);
+      return js_ast.Block([setStateStmt, continueStmt]);
     }
     return _emitInvalidNode(
             node, 'see https://github.com/dart-lang/sdk/issues/29352')
@@ -3564,8 +3590,8 @@
   }
 
   @override
-  JS.Statement visitIfStatement(IfStatement node) {
-    return JS.If(_visitTest(node.condition), _visitScope(node.then),
+  js_ast.Statement visitIfStatement(IfStatement node) {
+    return js_ast.If(_visitTest(node.condition), _visitScope(node.then),
         _visitScope(node.otherwise));
   }
 
@@ -3575,27 +3601,27 @@
   ///
   ///     do var x = 5; while (false); // Dart
   ///     do { let x = 5; } while (false); // JS
-  JS.Statement _visitScope(Statement stmt) {
+  js_ast.Statement _visitScope(Statement stmt) {
     var result = _visitStatement(stmt);
-    if (result is JS.ExpressionStatement &&
-        result.expression is JS.VariableDeclarationList) {
-      return JS.Block([result]);
+    if (result is js_ast.ExpressionStatement &&
+        result.expression is js_ast.VariableDeclarationList) {
+      return js_ast.Block([result]);
     }
     return result;
   }
 
   @override
-  JS.Statement visitReturnStatement(ReturnStatement node) {
+  js_ast.Statement visitReturnStatement(ReturnStatement node) {
     return super.emitReturnStatement(_visitExpression(node.expression));
   }
 
   @override
-  JS.Statement visitTryCatch(TryCatch node) {
-    return JS.Try(
+  js_ast.Statement visitTryCatch(TryCatch node) {
+    return js_ast.Try(
         _visitStatement(node.body).toBlock(), _visitCatch(node.catches), null);
   }
 
-  JS.Catch _visitCatch(List<Catch> clauses) {
+  js_ast.Catch _visitCatch(List<Catch> clauses) {
     if (clauses.isEmpty) return null;
 
     var caughtError = VariableDeclaration('#e');
@@ -3614,7 +3640,7 @@
                 ? VariableDeclaration('#st')
                 : null);
 
-    JS.Statement catchBody = JS.Throw(_emitVariableRef(caughtError));
+    js_ast.Statement catchBody = js_ast.Throw(_emitVariableRef(caughtError));
     for (var clause in clauses.reversed) {
       catchBody = _catchClauseGuard(
           clause, catchBody, exceptionParameter, stackTraceParameter);
@@ -3634,15 +3660,16 @@
       catchBody,
     ];
     _rethrowParameter = savedRethrow;
-    return JS.Catch(_emitVariableDef(caughtError), JS.Block(catchStatements));
+    return js_ast.Catch(
+        _emitVariableDef(caughtError), js_ast.Block(catchStatements));
   }
 
-  JS.Statement _catchClauseGuard(
+  js_ast.Statement _catchClauseGuard(
       Catch node,
-      JS.Statement otherwise,
+      js_ast.Statement otherwise,
       VariableDeclaration exceptionParameter,
       VariableDeclaration stackTraceParameter) {
-    var body = <JS.Statement>[];
+    var body = <js_ast.Statement>[];
     var vars = HashSet<String>();
 
     void declareVariable(
@@ -3659,34 +3686,34 @@
     declareVariable(node.stackTrace, stackTraceParameter);
 
     body.add(_visitStatement(node.body).toScopedBlock(vars));
-    var then = JS.Block(body);
+    var then = js_ast.Block(body);
 
     // Discard following clauses, if any, as they are unreachable.
     if (types.isTop(node.guard)) return then;
 
     var condition =
         _emitIsExpression(VariableGet(exceptionParameter), node.guard);
-    return JS.If(condition, then, otherwise)
+    return js_ast.If(condition, then, otherwise)
       ..sourceInformation = _nodeStart(node);
   }
 
   @override
-  JS.Statement visitTryFinally(TryFinally node) {
+  js_ast.Statement visitTryFinally(TryFinally node) {
     var body = _visitStatement(node.body);
     var finallyBlock =
         _superDisallowed(() => _visitStatement(node.finalizer).toBlock());
 
-    if (body is JS.Try && body.finallyPart == null) {
+    if (body is js_ast.Try && body.finallyPart == null) {
       // Kernel represents Dart try/catch/finally as try/catch nested inside of
       // try/finally.  Flatten that pattern in the output into JS try/catch/
       // finally.
-      return JS.Try(body.body, body.catchPart, finallyBlock);
+      return js_ast.Try(body.body, body.catchPart, finallyBlock);
     }
-    return JS.Try(body.toBlock(), null, finallyBlock);
+    return js_ast.Try(body.toBlock(), null, finallyBlock);
   }
 
   @override
-  JS.Statement visitYieldStatement(YieldStatement node) {
+  js_ast.Statement visitYieldStatement(YieldStatement node) {
     var jsExpr = _visitExpression(node.expression);
     var star = node.isYieldStar;
     if (_asyncStarController != null) {
@@ -3705,7 +3732,7 @@
         _asyncStarController,
         helperName,
         jsExpr,
-        JS.Yield(null)..sourceInformation = _nodeStart(node)
+        js_ast.Yield(null)..sourceInformation = _nodeStart(node)
       ]);
     }
     // A normal yield in a sync*
@@ -3713,7 +3740,7 @@
   }
 
   @override
-  JS.Statement visitVariableDeclaration(VariableDeclaration node) {
+  js_ast.Statement visitVariableDeclaration(VariableDeclaration node) {
     // TODO(jmesserly): casts are sometimes required here.
     // Kernel does not represent these explicitly.
     var v = _emitVariableDef(node);
@@ -3722,15 +3749,15 @@
   }
 
   @override
-  JS.Statement visitFunctionDeclaration(FunctionDeclaration node) {
+  js_ast.Statement visitFunctionDeclaration(FunctionDeclaration node) {
     var func = node.function;
     var fn = _emitFunction(func, node.variable.name);
 
     var name = _emitVariableDef(node.variable);
-    JS.Statement declareFn;
+    js_ast.Statement declareFn;
     declareFn = toBoundFunctionStatement(fn, name);
     if (_reifyFunctionType(func)) {
-      declareFn = JS.Block([
+      declareFn = js_ast.Block([
         declareFn,
         _emitFunctionTagged(_emitVariableRef(node.variable), func.functionType)
             .toStatement()
@@ -3740,22 +3767,23 @@
   }
 
   @override
-  JS.Expression defaultExpression(Expression node) => _emitInvalidNode(node);
+  js_ast.Expression defaultExpression(Expression node) =>
+      _emitInvalidNode(node);
 
   @override
-  JS.Expression defaultBasicLiteral(BasicLiteral node) =>
+  js_ast.Expression defaultBasicLiteral(BasicLiteral node) =>
       defaultExpression(node);
 
   @override
-  JS.Expression visitInvalidExpression(InvalidExpression node) =>
+  js_ast.Expression visitInvalidExpression(InvalidExpression node) =>
       defaultExpression(node);
 
   @override
-  JS.Expression visitConstantExpression(ConstantExpression node) =>
-      node.constant.accept(this) as JS.Expression;
+  js_ast.Expression visitConstantExpression(ConstantExpression node) =>
+      node.constant.accept(this) as js_ast.Expression;
 
   @override
-  JS.Expression visitVariableGet(VariableGet node) {
+  js_ast.Expression visitVariableGet(VariableGet node) {
     var v = node.variable;
     var id = _emitVariableRef(v);
     if (id.name == v.name) {
@@ -3764,29 +3792,29 @@
     return id;
   }
 
-  JS.Identifier _emitVariableRef(VariableDeclaration v) {
+  js_ast.Identifier _emitVariableRef(VariableDeclaration v) {
     var name = v.name;
     if (name == null || name.startsWith('#')) {
       name = name == null ? 't${_tempVariables.length}' : name.substring(1);
-      return _tempVariables.putIfAbsent(v, () => JS.TemporaryId(name));
+      return _tempVariables.putIfAbsent(v, () => js_ast.TemporaryId(name));
     }
-    return JS.Identifier(name);
+    return js_ast.Identifier(name);
   }
 
   /// Emits the declaration of a variable.
   ///
   /// This is similar to [_emitVariableRef] but it also attaches source
   /// location information, so hover will work as expected.
-  JS.Identifier _emitVariableDef(VariableDeclaration v) {
+  js_ast.Identifier _emitVariableDef(VariableDeclaration v) {
     return _emitVariableRef(v)..sourceInformation = _nodeStart(v);
   }
 
-  JS.Statement _initLetVariables() {
+  js_ast.Statement _initLetVariables() {
     if (_letVariables.isEmpty) return null;
-    var result = JS.VariableDeclarationList(
+    var result = js_ast.VariableDeclarationList(
             'let',
             _letVariables
-                .map((v) => JS.VariableInitialization(v, null))
+                .map((v) => js_ast.VariableInitialization(v, null))
                 .toList())
         .toStatement();
     _letVariables.clear();
@@ -3795,33 +3823,33 @@
 
   // TODO(jmesserly): resugar operators for kernel, such as ++x, x++, x+=.
   @override
-  JS.Expression visitVariableSet(VariableSet node) =>
+  js_ast.Expression visitVariableSet(VariableSet node) =>
       _visitExpression(node.value)
           .toAssignExpression(_emitVariableRef(node.variable));
 
   @override
-  JS.Expression visitPropertyGet(PropertyGet node) {
+  js_ast.Expression visitPropertyGet(PropertyGet node) {
     return _emitPropertyGet(
         node.receiver, node.interfaceTarget, node.name.name);
   }
 
   @override
-  JS.Expression visitPropertySet(PropertySet node) {
+  js_ast.Expression visitPropertySet(PropertySet node) {
     return _emitPropertySet(
         node.receiver, node.interfaceTarget, node.value, node.name.name);
   }
 
   @override
-  JS.Expression visitDirectPropertyGet(DirectPropertyGet node) {
+  js_ast.Expression visitDirectPropertyGet(DirectPropertyGet node) {
     return _emitPropertyGet(node.receiver, node.target);
   }
 
   @override
-  JS.Expression visitDirectPropertySet(DirectPropertySet node) {
+  js_ast.Expression visitDirectPropertySet(DirectPropertySet node) {
     return _emitPropertySet(node.receiver, node.target, node.value);
   }
 
-  JS.Expression _emitPropertyGet(Expression receiver, Member member,
+  js_ast.Expression _emitPropertyGet(Expression receiver, Member member,
       [String memberName]) {
     memberName ??= member.name.name;
     // TODO(jmesserly): should tearoff of `.call` on a function type be
@@ -3856,7 +3884,7 @@
     if (_reifyTearoff(member)) {
       return runtimeCall('bind(#, #)', [jsReceiver, jsName]);
     } else {
-      return JS.PropertyAccess(jsReceiver, jsName);
+      return js_ast.PropertyAccess(jsReceiver, jsName);
     }
   }
 
@@ -3865,7 +3893,7 @@
   // access to the target expression there (needed for `dart.replNameLookup`).
   String get _replSuffix => options.replCompile ? 'Repl' : '';
 
-  JS.Expression _emitPropertySet(
+  js_ast.Expression _emitPropertySet(
       Expression receiver, Member member, Expression value,
       [String memberName]) {
     var jsName =
@@ -3882,7 +3910,7 @@
   }
 
   @override
-  JS.Expression visitSuperPropertyGet(SuperPropertyGet node) {
+  js_ast.Expression visitSuperPropertyGet(SuperPropertyGet node) {
     var target = node.interfaceTarget;
     var jsTarget = _emitSuperTarget(target);
     if (_reifyTearoff(target)) {
@@ -3892,16 +3920,17 @@
   }
 
   @override
-  JS.Expression visitSuperPropertySet(SuperPropertySet node) {
+  js_ast.Expression visitSuperPropertySet(SuperPropertySet node) {
     var target = node.interfaceTarget;
     var jsTarget = _emitSuperTarget(target, setter: true);
     return _visitExpression(node.value).toAssignExpression(jsTarget);
   }
 
   @override
-  JS.Expression visitStaticGet(StaticGet node) => _emitStaticGet(node.target);
+  js_ast.Expression visitStaticGet(StaticGet node) =>
+      _emitStaticGet(node.target);
 
-  JS.Expression _emitStaticGet(Member target) {
+  js_ast.Expression _emitStaticGet(Member target) {
     // TODO(vsm): Re-inline constants.  See:
     // https://github.com/dart-lang/sdk/issues/36285
     var result = _emitStaticTarget(target);
@@ -3915,23 +3944,23 @@
   }
 
   @override
-  JS.Expression visitStaticSet(StaticSet node) {
+  js_ast.Expression visitStaticSet(StaticSet node) {
     return _visitExpression(node.value)
         .toAssignExpression(_emitStaticTarget(node.target));
   }
 
   @override
-  JS.Expression visitMethodInvocation(MethodInvocation node) {
+  js_ast.Expression visitMethodInvocation(MethodInvocation node) {
     return _emitMethodCall(
         node.receiver, node.interfaceTarget, node.arguments, node);
   }
 
   @override
-  JS.Expression visitDirectMethodInvocation(DirectMethodInvocation node) {
+  js_ast.Expression visitDirectMethodInvocation(DirectMethodInvocation node) {
     return _emitMethodCall(node.receiver, node.target, node.arguments, node);
   }
 
-  JS.Expression _emitMethodCall(Expression receiver, Member target,
+  js_ast.Expression _emitMethodCall(Expression receiver, Member target,
       Arguments arguments, InvocationExpression node) {
     var name = node.name.name;
 
@@ -3957,7 +3986,7 @@
         return _emitDynamicInvoke(jsReceiver, null, args, arguments);
       } else if (_isDirectCallable(receiverType)) {
         // Call methods on function types should be handled as function calls.
-        return JS.Call(jsReceiver, args);
+        return js_ast.Call(jsReceiver, args);
       }
     }
 
@@ -3988,8 +4017,11 @@
     return js.call('#.#(#)', [jsReceiver, jsName, args]);
   }
 
-  JS.Expression _emitDynamicInvoke(JS.Expression fn, JS.Expression methodName,
-      Iterable<JS.Expression> args, Arguments arguments) {
+  js_ast.Expression _emitDynamicInvoke(
+      js_ast.Expression fn,
+      js_ast.Expression methodName,
+      Iterable<js_ast.Expression> args,
+      Arguments arguments) {
     var jsArgs = <Object>[fn];
     String jsCode;
 
@@ -4026,7 +4058,7 @@
   bool _isDirectCallable(DartType t) =>
       t is FunctionType || t is InterfaceType && usesJSInterop(t.classNode);
 
-  JS.Expression _getImplicitCallTarget(InterfaceType from) {
+  js_ast.Expression _getImplicitCallTarget(InterfaceType from) {
     var c = from.classNode;
     var member = hierarchy.getInterfaceMember(c, Name("call"));
     if (member is Procedure && !member.isAccessor && !usesJSInterop(c)) {
@@ -4038,7 +4070,7 @@
   bool _isDynamicOrFunction(DartType t) =>
       t == coreTypes.functionClass.rawType || t == const DynamicType();
 
-  JS.Expression _emitUnaryOperator(
+  js_ast.Expression _emitUnaryOperator(
       Expression expr, Member target, InvocationExpression node) {
     var op = node.name.name;
     if (target != null) {
@@ -4062,8 +4094,8 @@
   /// the interpretation of the 32-bit value from signed to unsigned.  Most
   /// JavaScript operations interpret their operands as signed and generate
   /// signed results.
-  JS.Expression _coerceBitOperationResultToUnsigned(
-      Expression node, JS.Expression uncoerced) {
+  js_ast.Expression _coerceBitOperationResultToUnsigned(
+      Expression node, js_ast.Expression uncoerced) {
     // Don't coerce if the parent will coerce.
     var parent = node.parent;
     if (parent is InvocationExpression && _nodeIsBitwiseOperation(parent)) {
@@ -4203,7 +4235,7 @@
     return bitWidth(expr, 0) < 32;
   }
 
-  JS.Expression _emitBinaryOperator(Expression left, Member target,
+  js_ast.Expression _emitBinaryOperator(Expression left, Member target,
       Expression right, InvocationExpression node) {
     var op = node.name.name;
     if (op == '==') return _emitEqualityOperator(left, target, right);
@@ -4223,11 +4255,11 @@
 
         /// Emits an inlined binary operation using the JS [code], adding null
         /// checks if needed to ensure we throw the appropriate error.
-        JS.Expression binary(String code) {
+        js_ast.Expression binary(String code) {
           return js.call(code, [notNull(left), notNull(right)]);
         }
 
-        JS.Expression bitwise(String code) {
+        js_ast.Expression bitwise(String code) {
           return _coerceBitOperationResultToUnsigned(node, binary(code));
         }
 
@@ -4236,7 +4268,7 @@
         ///
         /// Short circuiting operators should not be used in [code], because the
         /// null checks for both operands must happen unconditionally.
-        JS.Expression bitwiseBool(String code) {
+        js_ast.Expression bitwiseBool(String code) {
           return js.call(code, [notNull(left), _visitTest(right)]);
         }
 
@@ -4307,7 +4339,7 @@
     return _emitOperatorCall(left, target, op, [right]);
   }
 
-  JS.Expression _emitEqualityOperator(
+  js_ast.Expression _emitEqualityOperator(
       Expression left, Member target, Expression right,
       {bool negated = false}) {
     var targetClass = target?.enclosingClass;
@@ -4364,7 +4396,7 @@
   /// **Please note** this function does not support method invocation syntax
   /// `obj.name(args)` because that could be a getter followed by a call.
   /// See [visitMethodInvocation].
-  JS.Expression _emitOperatorCall(
+  js_ast.Expression _emitOperatorCall(
       Expression receiver, Member target, String name, List<Expression> args) {
     // TODO(jmesserly): calls that don't pass `element` are probably broken for
     // `super` calls from disallowed super locations.
@@ -4391,20 +4423,20 @@
 
   // TODO(jmesserly): optimize super operators for kernel
   @override
-  JS.Expression visitSuperMethodInvocation(SuperMethodInvocation node) {
+  js_ast.Expression visitSuperMethodInvocation(SuperMethodInvocation node) {
     var target = node.interfaceTarget;
-    return JS.Call(_emitSuperTarget(target),
+    return js_ast.Call(_emitSuperTarget(target),
         _emitArgumentList(node.arguments, target: target));
   }
 
-  /// Emits the [JS.PropertyAccess] for accessors or method calls to
+  /// Emits the [js_ast.PropertyAccess] for accessors or method calls to
   /// [jsTarget].[jsName], replacing `super` if it is not allowed in scope.
-  JS.PropertyAccess _emitSuperTarget(Member member, {bool setter = false}) {
+  js_ast.PropertyAccess _emitSuperTarget(Member member, {bool setter = false}) {
     var jsName = _emitMemberName(member.name.name, member: member);
     if (member is Field && !virtualFields.isVirtual(member)) {
-      return JS.PropertyAccess(JS.This(), jsName);
+      return js_ast.PropertyAccess(js_ast.This(), jsName);
     }
-    if (_superAllowed) return JS.PropertyAccess(JS.Super(), jsName);
+    if (_superAllowed) return js_ast.PropertyAccess(js_ast.Super(), jsName);
 
     // If we can't emit `super` in this context, generate a helper that does it
     // for us, and call the helper.
@@ -4421,30 +4453,30 @@
                 : 'function() { return super[#]; }',
             [jsName]);
 
-        return JS.Method(JS.TemporaryId(name), fn,
+        return js_ast.Method(js_ast.TemporaryId(name), fn,
             isGetter: !setter, isSetter: setter);
       } else {
         var function = member.function;
         var params = [
           ..._emitTypeFormals(function.typeParameters),
           for (var param in function.positionalParameters)
-            JS.Identifier(param.name),
+            js_ast.Identifier(param.name),
           if (function.namedParameters.isNotEmpty) namedArgumentTemp,
         ];
 
         var fn = js.fun(
             'function(#) { return super[#](#); }', [params, jsName, params]);
-        name = JS.friendlyNameForDartOperator[name] ?? name;
-        return JS.Method(JS.TemporaryId(name), fn);
+        name = js_ast.friendlyNameForDartOperator[name] ?? name;
+        return js_ast.Method(js_ast.TemporaryId(name), fn);
       }
     });
-    return JS.PropertyAccess(JS.This(), jsMethod.name);
+    return js_ast.PropertyAccess(js_ast.This(), jsMethod.name);
   }
 
   @override
-  JS.Expression visitStaticInvocation(StaticInvocation node) {
+  js_ast.Expression visitStaticInvocation(StaticInvocation node) {
     var target = node.target;
-    if (isInlineJS(target)) return _emitInlineJSCode(node) as JS.Expression;
+    if (isInlineJS(target)) return _emitInlineJSCode(node) as js_ast.Expression;
     if (target.isFactory) return _emitFactoryInvocation(node);
 
     // Optimize some internal SDK calls.
@@ -4469,12 +4501,12 @@
       return _emitCoreIdenticalCall(node.arguments.positional);
     }
     if (_isDebuggerCall(target)) {
-      return _emitDebuggerCall(node) as JS.Expression;
+      return _emitDebuggerCall(node) as js_ast.Expression;
     }
 
     var fn = _emitStaticTarget(target);
     var args = _emitArgumentList(node.arguments, target: target);
-    return JS.Call(fn, args);
+    return js_ast.Call(fn, args);
   }
 
   bool _isDebuggerCall(Procedure target) {
@@ -4482,7 +4514,7 @@
         target.enclosingLibrary.importUri.toString() == 'dart:developer';
   }
 
-  JS.Node _emitDebuggerCall(StaticInvocation node) {
+  js_ast.Node _emitDebuggerCall(StaticInvocation node) {
     var args = node.arguments.named;
     var isStatement = node.parent is ExpressionStatement;
     if (args.isEmpty) {
@@ -4510,17 +4542,17 @@
         // coerces to true (the default value of `when`).
         ? (args[0].name == 'when'
             ? jsArgs[0].value
-            : JS.ObjectInitializer(jsArgs))
+            : js_ast.ObjectInitializer(jsArgs))
         // If we have both `message` and `when` arguments, evaluate them in
         // order, then extract the `when` argument.
-        : js.call('#.when', JS.ObjectInitializer(jsArgs));
+        : js.call('#.when', js_ast.ObjectInitializer(jsArgs));
     return isStatement
         ? js.statement('if (#) debugger;', when)
         : js.call('# && (() => { debugger; return true })()', when);
   }
 
   /// Emits the target of a [StaticInvocation], [StaticGet], or [StaticSet].
-  JS.Expression _emitStaticTarget(Member target) {
+  js_ast.Expression _emitStaticTarget(Member target) {
     var c = target.enclosingClass;
     if (c != null) {
       // A static native element should just forward directly to the JS type's
@@ -4534,13 +4566,13 @@
           return runtimeCall('global.#.#', [nativeName[0], memberName]);
         }
       }
-      return JS.PropertyAccess(_emitStaticClassName(c),
+      return js_ast.PropertyAccess(_emitStaticClassName(c),
           _emitStaticMemberName(target.name.name, target));
     }
     return _emitTopLevelName(target);
   }
 
-  List<JS.Expression> _emitArgumentList(Arguments node,
+  List<js_ast.Expression> _emitArgumentList(Arguments node,
       {bool types = true, Member target}) {
     types = types && _reifyGenericFunction(target);
     return [
@@ -4549,20 +4581,20 @@
         if (arg is StaticInvocation &&
             isJSSpreadInvocation(arg.target) &&
             arg.arguments.positional.length == 1)
-          JS.Spread(_visitExpression(arg.arguments.positional[0]))
+          js_ast.Spread(_visitExpression(arg.arguments.positional[0]))
         else
           _visitExpression(arg),
       if (node.named.isNotEmpty)
-        JS.ObjectInitializer(node.named.map(_emitNamedExpression).toList()),
+        js_ast.ObjectInitializer(node.named.map(_emitNamedExpression).toList()),
     ];
   }
 
-  JS.Property _emitNamedExpression(NamedExpression arg) {
-    return JS.Property(propertyName(arg.name), _visitExpression(arg.value));
+  js_ast.Property _emitNamedExpression(NamedExpression arg) {
+    return js_ast.Property(propertyName(arg.name), _visitExpression(arg.value));
   }
 
   /// Emits code for the `JS(...)` macro.
-  JS.Node _emitInlineJSCode(StaticInvocation node) {
+  js_ast.Node _emitInlineJSCode(StaticInvocation node) {
     var args = node.arguments.positional;
     // arg[0] is static return type, used in `RestrictedStaticTypeAnalyzer`
     var code = args[1];
@@ -4604,8 +4636,8 @@
 
     var result = js.parseForeignJS(source).instantiate(jsArgs);
 
-    assert(result is JS.Expression ||
-        result is JS.Statement && node.parent is ExpressionStatement);
+    assert(result is js_ast.Expression ||
+        result is js_ast.Statement && node.parent is ExpressionStatement);
     return result;
   }
 
@@ -4635,19 +4667,19 @@
   /// [_notNullLocals].
   bool isNullable(Expression expr) => _nullableInference.isNullable(expr);
 
-  JS.Expression _emitJSDoubleEq(List<JS.Expression> args,
+  js_ast.Expression _emitJSDoubleEq(List<js_ast.Expression> args,
       {bool negated = false}) {
     var op = negated ? '# != #' : '# == #';
     return js.call(op, args);
   }
 
-  JS.Expression _emitJSTripleEq(List<JS.Expression> args,
+  js_ast.Expression _emitJSTripleEq(List<js_ast.Expression> args,
       {bool negated = false}) {
     var op = negated ? '# !== #' : '# === #';
     return js.call(op, args);
   }
 
-  JS.Expression _emitCoreIdenticalCall(List<Expression> args,
+  js_ast.Expression _emitCoreIdenticalCall(List<Expression> args,
       {bool negated = false}) {
     if (args.length != 2) {
       // Shouldn't happen in typechecked code
@@ -4664,21 +4696,21 @@
       return _emitJSDoubleEq(jsArgs, negated: negated);
     }
     var code = negated ? '!#' : '#';
-    return js.call(
-        code, JS.Call(_emitTopLevelName(coreTypes.identicalProcedure), jsArgs));
+    return js.call(code,
+        js_ast.Call(_emitTopLevelName(coreTypes.identicalProcedure), jsArgs));
   }
 
   @override
-  JS.Expression visitConstructorInvocation(ConstructorInvocation node) {
+  js_ast.Expression visitConstructorInvocation(ConstructorInvocation node) {
     var ctor = node.target;
     var args = node.arguments;
-    var result = JS.New(_emitConstructorName(node.constructedType, ctor),
+    var result = js_ast.New(_emitConstructorName(node.constructedType, ctor),
         _emitArgumentList(args, types: false));
 
     return node.isConst ? canonicalizeConstObject(result) : result;
   }
 
-  JS.Expression _emitFactoryInvocation(StaticInvocation node) {
+  js_ast.Expression _emitFactoryInvocation(StaticInvocation node) {
     var args = node.arguments;
     var ctor = node.target;
     var ctorClass = ctor.enclosingClass;
@@ -4693,7 +4725,7 @@
     if (isFromEnvironmentInvocation(coreTypes, node)) {
       var value = _constants.evaluate(node);
       if (value is PrimitiveConstant) {
-        return value.accept(this) as JS.Expression;
+        return value.accept(this) as js_ast.Expression;
       }
     }
 
@@ -4730,20 +4762,20 @@
       }
     }
 
-    var result = JS.Call(_emitConstructorName(type, ctor),
+    var result = js_ast.Call(_emitConstructorName(type, ctor),
         _emitArgumentList(args, types: false));
 
     return node.isConst ? canonicalizeConstObject(result) : result;
   }
 
-  JS.Expression _emitJSInteropNew(Member ctor, Arguments args) {
+  js_ast.Expression _emitJSInteropNew(Member ctor, Arguments args) {
     var ctorClass = ctor.enclosingClass;
     if (isJSAnonymousType(ctorClass)) return _emitObjectLiteral(args);
-    return JS.New(_emitConstructorName(ctorClass.rawType, ctor),
+    return js_ast.New(_emitConstructorName(ctorClass.rawType, ctor),
         _emitArgumentList(args, types: false));
   }
 
-  JS.Expression _emitMapImplType(InterfaceType type, {bool identity}) {
+  js_ast.Expression _emitMapImplType(InterfaceType type, {bool identity}) {
     var typeArgs = type.typeArguments;
     if (typeArgs.isEmpty) return _emitType(type);
     identity ??= _typeRep.isPrimitive(typeArgs[0]);
@@ -4751,7 +4783,7 @@
     return _emitType(InterfaceType(c, typeArgs));
   }
 
-  JS.Expression _emitSetImplType(InterfaceType type, {bool identity}) {
+  js_ast.Expression _emitSetImplType(InterfaceType type, {bool identity}) {
     var typeArgs = type.typeArguments;
     if (typeArgs.isEmpty) return _emitType(type);
     identity ??= _typeRep.isPrimitive(typeArgs[0]);
@@ -4759,15 +4791,15 @@
     return _emitType(InterfaceType(c, typeArgs));
   }
 
-  JS.Expression _emitObjectLiteral(Arguments node) {
+  js_ast.Expression _emitObjectLiteral(Arguments node) {
     var args = _emitArgumentList(node, types: false);
     if (args.isEmpty) return js.call('{}');
-    assert(args.single is JS.ObjectInitializer);
+    assert(args.single is js_ast.ObjectInitializer);
     return args.single;
   }
 
   @override
-  JS.Expression visitNot(Not node) {
+  js_ast.Expression visitNot(Not node) {
     var operand = node.operand;
     if (operand is MethodInvocation && operand.name.name == '==') {
       return _emitEqualityOperator(operand.receiver, operand.interfaceTarget,
@@ -4789,14 +4821,14 @@
   }
 
   @override
-  JS.Expression visitLogicalExpression(LogicalExpression node) {
+  js_ast.Expression visitLogicalExpression(LogicalExpression node) {
     // The operands of logical boolean operators are subject to boolean
     // conversion.
     return _visitTest(node);
   }
 
   @override
-  JS.Expression visitConditionalExpression(ConditionalExpression node) {
+  js_ast.Expression visitConditionalExpression(ConditionalExpression node) {
     return js.call('# ? # : #', [
       _visitTest(node.condition),
       _visitExpression(node.then),
@@ -4806,11 +4838,11 @@
   }
 
   @override
-  JS.Expression visitStringConcatenation(StringConcatenation node) {
-    var parts = <JS.Expression>[];
+  js_ast.Expression visitStringConcatenation(StringConcatenation node) {
+    var parts = <js_ast.Expression>[];
     for (var e in node.expressions) {
       var jsExpr = _visitExpression(e);
-      if (jsExpr is JS.LiteralString && jsExpr.valueWithoutQuotes.isEmpty) {
+      if (jsExpr is js_ast.LiteralString && jsExpr.valueWithoutQuotes.isEmpty) {
         continue;
       }
       parts.add(e.getStaticType(types) == types.stringType && !isNullable(e)
@@ -4818,13 +4850,13 @@
           : runtimeCall('str(#)', [jsExpr]));
     }
     if (parts.isEmpty) return js.string('');
-    return JS.Expression.binary(parts, '+');
+    return js_ast.Expression.binary(parts, '+');
   }
 
   @override
-  JS.Expression visitListConcatenation(ListConcatenation node) {
+  js_ast.Expression visitListConcatenation(ListConcatenation node) {
     // Only occurs inside unevaluated constants.
-    List<JS.Expression> entries = [];
+    List<js_ast.Expression> entries = [];
     _concatenate(Expression node) {
       if (node is ListConcatenation) {
         node.lists.forEach(_concatenate);
@@ -4844,9 +4876,9 @@
   }
 
   @override
-  JS.Expression visitSetConcatenation(SetConcatenation node) {
+  js_ast.Expression visitSetConcatenation(SetConcatenation node) {
     // Only occurs inside unevaluated constants.
-    List<JS.Expression> entries = [];
+    List<js_ast.Expression> entries = [];
     _concatenate(Expression node) {
       if (node is SetConcatenation) {
         node.sets.forEach(_concatenate);
@@ -4866,9 +4898,9 @@
   }
 
   @override
-  JS.Expression visitMapConcatenation(MapConcatenation node) {
+  js_ast.Expression visitMapConcatenation(MapConcatenation node) {
     // Only occurs inside unevaluated constants.
-    List<JS.Expression> entries = [];
+    List<js_ast.Expression> entries = [];
     _concatenate(Expression node) {
       if (node is MapConcatenation) {
         node.maps.forEach(_concatenate);
@@ -4894,17 +4926,17 @@
   }
 
   @override
-  JS.Expression visitInstanceCreation(InstanceCreation node) {
+  js_ast.Expression visitInstanceCreation(InstanceCreation node) {
     // Only occurs inside unevaluated constants.
     throw new UnsupportedError("Instance creation");
   }
 
   @override
-  JS.Expression visitIsExpression(IsExpression node) {
+  js_ast.Expression visitIsExpression(IsExpression node) {
     return _emitIsExpression(node.operand, node.type);
   }
 
-  JS.Expression _emitIsExpression(Expression operand, DartType type) {
+  js_ast.Expression _emitIsExpression(Expression operand, DartType type) {
     // Generate `is` as `dart.is` or `typeof` depending on the RHS type.
     var lhs = _visitExpression(operand);
     var typeofName = _typeRep.typeFor(type).primitiveTypeOf;
@@ -4917,7 +4949,7 @@
   }
 
   @override
-  JS.Expression visitAsExpression(AsExpression node) {
+  js_ast.Expression visitAsExpression(AsExpression node) {
     Expression fromExpr = node.operand;
     var to = node.type;
     var jsFrom = _visitExpression(fromExpr);
@@ -4961,7 +4993,7 @@
     return _emitCast(jsFrom, to, implicit: isTypeError);
   }
 
-  JS.Expression _emitCast(JS.Expression expr, DartType type,
+  js_ast.Expression _emitCast(js_ast.Expression expr, DartType type,
       {bool implicit = true}) {
     if (types.isTop(type)) return expr;
 
@@ -4970,14 +5002,14 @@
   }
 
   @override
-  JS.Expression visitSymbolLiteral(SymbolLiteral node) =>
+  js_ast.Expression visitSymbolLiteral(SymbolLiteral node) =>
       emitDartSymbol(node.value);
 
   @override
-  JS.Expression visitTypeLiteral(TypeLiteral node) =>
+  js_ast.Expression visitTypeLiteral(TypeLiteral node) =>
       _emitTypeLiteral(node.type);
 
-  JS.Expression _emitTypeLiteral(DartType type) {
+  js_ast.Expression _emitTypeLiteral(DartType type) {
     var typeRep = _emitType(type);
     // If the type is a type literal expression in Dart code, wrap the raw
     // runtime type in a "Type" instance.
@@ -4985,19 +5017,19 @@
   }
 
   @override
-  JS.Expression visitThisExpression(ThisExpression node) => JS.This();
+  js_ast.Expression visitThisExpression(ThisExpression node) => js_ast.This();
 
   @override
-  JS.Expression visitRethrow(Rethrow node) {
+  js_ast.Expression visitRethrow(Rethrow node) {
     return runtimeCall('rethrow(#)', [_emitVariableRef(_rethrowParameter)]);
   }
 
   @override
-  JS.Expression visitThrow(Throw node) =>
+  js_ast.Expression visitThrow(Throw node) =>
       runtimeCall('throw(#)', [_visitExpression(node.expression)]);
 
   @override
-  JS.Expression visitListLiteral(ListLiteral node) {
+  js_ast.Expression visitListLiteral(ListLiteral node) {
     var elementType = node.typeArgument;
     var elements = _visitExpressionList(node.expressions);
     // TODO(markzipan): remove const check when we use front-end const eval
@@ -5007,8 +5039,9 @@
     return _emitConstList(elementType, elements);
   }
 
-  JS.Expression _emitList(DartType itemType, List<JS.Expression> items) {
-    var list = JS.ArrayInitializer(items);
+  js_ast.Expression _emitList(
+      DartType itemType, List<js_ast.Expression> items) {
+    var list = js_ast.ArrayInitializer(items);
 
     // TODO(jmesserly): analyzer will usually infer `List<Object>` because
     // that is the least upper bound of the element types. So we rarely
@@ -5020,8 +5053,8 @@
     return js.call('#.of(#)', [_emitType(arrayType), list]);
   }
 
-  JS.Expression _emitConstList(
-      DartType elementType, List<JS.Expression> elements) {
+  js_ast.Expression _emitConstList(
+      DartType elementType, List<js_ast.Expression> elements) {
     // dart.constList helper internally depends on _interceptors.JSArray.
     _declareBeforeUse(_jsArrayClass);
     return cacheConst(
@@ -5029,7 +5062,7 @@
   }
 
   @override
-  JS.Expression visitSetLiteral(SetLiteral node) {
+  js_ast.Expression visitSetLiteral(SetLiteral node) {
     // TODO(markzipan): remove const check when we use front-end const eval
     if (!node.isConst) {
       var setType = visitInterfaceType(
@@ -5044,14 +5077,14 @@
         node.typeArgument, _visitExpressionList(node.expressions));
   }
 
-  JS.Expression _emitConstSet(
-      DartType elementType, List<JS.Expression> elements) {
+  js_ast.Expression _emitConstSet(
+      DartType elementType, List<js_ast.Expression> elements) {
     return cacheConst(
         runtimeCall('constSet(#, [#])', [_emitType(elementType), elements]));
   }
 
   @override
-  JS.Expression visitMapLiteral(MapLiteral node) {
+  js_ast.Expression visitMapLiteral(MapLiteral node) {
     var entries = [
       for (var e in node.entries) ...[
         _visitExpression(e.key),
@@ -5071,71 +5104,72 @@
     return _emitConstMap(node.keyType, node.valueType, entries);
   }
 
-  JS.Expression _emitConstMap(
-      DartType keyType, DartType valueType, List<JS.Expression> entries) {
+  js_ast.Expression _emitConstMap(
+      DartType keyType, DartType valueType, List<js_ast.Expression> entries) {
     return cacheConst(runtimeCall('constMap(#, #, [#])',
         [_emitType(keyType), _emitType(valueType), entries]));
   }
 
   @override
-  JS.Expression visitAwaitExpression(AwaitExpression node) =>
-      JS.Yield(_visitExpression(node.operand));
+  js_ast.Expression visitAwaitExpression(AwaitExpression node) =>
+      js_ast.Yield(_visitExpression(node.operand));
 
   @override
-  JS.Expression visitFunctionExpression(FunctionExpression node) {
+  js_ast.Expression visitFunctionExpression(FunctionExpression node) {
     var fn = _emitArrowFunction(node);
     if (!_reifyFunctionType(node.function)) return fn;
     return _emitFunctionTagged(fn, node.getStaticType(types) as FunctionType);
   }
 
-  JS.ArrowFun _emitArrowFunction(FunctionExpression node) {
-    JS.Fun f = _emitFunction(node.function, null);
-    JS.Node body = f.body;
+  js_ast.ArrowFun _emitArrowFunction(FunctionExpression node) {
+    js_ast.Fun f = _emitFunction(node.function, null);
+    js_ast.Node body = f.body;
 
     // Simplify `=> { return e; }` to `=> e`
-    if (body is JS.Block) {
-      var block = body as JS.Block;
+    if (body is js_ast.Block) {
+      var block = body as js_ast.Block;
       if (block.statements.length == 1) {
-        JS.Statement s = block.statements[0];
-        if (s is JS.Block) {
-          block = s as JS.Block;
+        js_ast.Statement s = block.statements[0];
+        if (s is js_ast.Block) {
+          block = s as js_ast.Block;
           s = block.statements.length == 1 ? block.statements[0] : null;
         }
-        if (s is JS.Return && s.value != null) body = s.value;
+        if (s is js_ast.Return && s.value != null) body = s.value;
       }
     }
 
     // Convert `function(...) { ... }` to `(...) => ...`
     // This is for readability, but it also ensures correct `this` binding.
-    return JS.ArrowFun(f.params, body);
+    return js_ast.ArrowFun(f.params, body);
   }
 
   @override
-  JS.Expression visitStringLiteral(StringLiteral node) =>
+  js_ast.Expression visitStringLiteral(StringLiteral node) =>
       js.escapedString(node.value, '"');
 
   @override
-  JS.Expression visitIntLiteral(IntLiteral node) => js.uint64(node.value);
+  js_ast.Expression visitIntLiteral(IntLiteral node) => js.uint64(node.value);
 
   @override
-  JS.Expression visitDoubleLiteral(DoubleLiteral node) => js.number(node.value);
+  js_ast.Expression visitDoubleLiteral(DoubleLiteral node) =>
+      js.number(node.value);
 
   @override
-  JS.Expression visitBoolLiteral(BoolLiteral node) =>
-      JS.LiteralBool(node.value);
+  js_ast.Expression visitBoolLiteral(BoolLiteral node) =>
+      js_ast.LiteralBool(node.value);
 
   @override
-  JS.Expression visitNullLiteral(NullLiteral node) => JS.LiteralNull();
+  js_ast.Expression visitNullLiteral(NullLiteral node) => js_ast.LiteralNull();
 
   @override
-  JS.Expression visitLet(Let node) {
+  js_ast.Expression visitLet(Let node) {
     var v = node.variable;
     var init = _visitExpression(v.initializer);
     var body = _visitExpression(node.body);
     var temp = _tempVariables.remove(v);
     if (temp != null) {
       if (_letVariables != null) {
-        init = JS.Assignment(temp, init);
+        init = js_ast.Assignment(temp, init);
         _letVariables.add(temp);
       } else {
         // TODO(jmesserly): make sure this doesn't happen on any performance
@@ -5143,37 +5177,37 @@
         //
         // Annotations on a top-level, non-lazy function type should be the only
         // remaining use.
-        return JS.Call(JS.ArrowFun([temp], body), [init]);
+        return js_ast.Call(js_ast.ArrowFun([temp], body), [init]);
       }
     }
-    return JS.Binary(',', init, body);
+    return js_ast.Binary(',', init, body);
   }
 
   @override
-  JS.Expression visitBlockExpression(BlockExpression node) {
+  js_ast.Expression visitBlockExpression(BlockExpression node) {
     var jsExpr = _visitExpression(node.value);
     var jsStmts = [
       for (var s in node.body.statements) _visitStatement(s),
-      JS.Return(jsExpr),
+      js_ast.Return(jsExpr),
     ];
-    var jsBlock = JS.Block(jsStmts);
+    var jsBlock = js_ast.Block(jsStmts);
     // BlockExpressions with async operations must be constructed
     // with a generator instead of a lambda.
     var finder = YieldFinder();
     jsBlock.accept(finder);
     if (finder.hasYield) {
-      var genFn = JS.Fun([], jsBlock, isGenerator: true);
+      var genFn = js_ast.Fun([], jsBlock, isGenerator: true);
       var asyncLibrary = emitLibraryName(coreTypes.asyncLibrary);
       var returnType = _emitType(node.getStaticType(types));
       var asyncCall =
           js.call('#.async(#, #)', [asyncLibrary, returnType, genFn]);
-      return JS.Yield(asyncCall);
+      return js_ast.Yield(asyncCall);
     }
-    return JS.Call(JS.ArrowFun([], jsBlock), []);
+    return js_ast.Call(js_ast.ArrowFun([], jsBlock), []);
   }
 
   @override
-  JS.Expression visitInstantiation(Instantiation node) {
+  js_ast.Expression visitInstantiation(Instantiation node) {
     return runtimeCall('gbind(#, #)', [
       _visitExpression(node.expression),
       node.typeArguments.map(_emitType).toList()
@@ -5181,7 +5215,7 @@
   }
 
   @override
-  JS.Expression visitLoadLibrary(LoadLibrary node) =>
+  js_ast.Expression visitLoadLibrary(LoadLibrary node) =>
       runtimeCall('loadLibrary()');
 
   // TODO(jmesserly): DDC loads all libraries eagerly.
@@ -5189,7 +5223,7 @@
   // https://github.com/dart-lang/sdk/issues/27776
   // https://github.com/dart-lang/sdk/issues/27777
   @override
-  JS.Expression visitCheckLibraryIsLoaded(CheckLibraryIsLoaded node) =>
+  js_ast.Expression visitCheckLibraryIsLoaded(CheckLibraryIsLoaded node) =>
       js.boolean(true);
 
   bool _reifyFunctionType(FunctionNode f) {
@@ -5236,16 +5270,18 @@
         findAnnotation(node, test), 'name') as String;
   }
 
-  JS.Expression visitConstant(Constant node) =>
-      node.accept(this) as JS.Expression;
+  js_ast.Expression visitConstant(Constant node) =>
+      node.accept(this) as js_ast.Expression;
   @override
-  JS.Expression visitNullConstant(NullConstant node) => JS.LiteralNull();
+  js_ast.Expression visitNullConstant(NullConstant node) =>
+      js_ast.LiteralNull();
   @override
-  JS.Expression visitBoolConstant(BoolConstant node) => js.boolean(node.value);
+  js_ast.Expression visitBoolConstant(BoolConstant node) =>
+      js.boolean(node.value);
   @override
-  JS.Expression visitIntConstant(IntConstant node) => js.number(node.value);
+  js_ast.Expression visitIntConstant(IntConstant node) => js.number(node.value);
   @override
-  JS.Expression visitDoubleConstant(DoubleConstant node) {
+  js_ast.Expression visitDoubleConstant(DoubleConstant node) {
     var value = node.value;
 
     // Emit the constant as an integer, if possible.
@@ -5263,20 +5299,20 @@
   }
 
   @override
-  JS.Expression visitStringConstant(StringConstant node) =>
+  js_ast.Expression visitStringConstant(StringConstant node) =>
       js.escapedString(node.value, '"');
 
   // DDC does not currently use the non-primivite constant nodes; rather these
   // are emitted via their normal expression nodes.
   @override
-  JS.Expression defaultConstant(Constant node) => _emitInvalidNode(node);
+  js_ast.Expression defaultConstant(Constant node) => _emitInvalidNode(node);
 
   @override
-  JS.Expression visitSymbolConstant(SymbolConstant node) =>
+  js_ast.Expression visitSymbolConstant(SymbolConstant node) =>
       emitDartSymbol(node.name);
 
   @override
-  JS.Expression visitMapConstant(MapConstant node) {
+  js_ast.Expression visitMapConstant(MapConstant node) {
     var entries = [
       for (var e in node.entries) ...[
         visitConstant(e.key),
@@ -5287,42 +5323,42 @@
   }
 
   @override
-  JS.Expression visitListConstant(ListConstant node) => _emitConstList(
+  js_ast.Expression visitListConstant(ListConstant node) => _emitConstList(
       node.typeArgument, node.entries.map(visitConstant).toList());
 
   @override
-  JS.Expression visitSetConstant(SetConstant node) => _emitConstSet(
+  js_ast.Expression visitSetConstant(SetConstant node) => _emitConstSet(
       node.typeArgument, node.entries.map(visitConstant).toList());
 
   @override
-  JS.Expression visitInstanceConstant(InstanceConstant node) {
+  js_ast.Expression visitInstanceConstant(InstanceConstant node) {
     entryToProperty(MapEntry<Reference, Constant> entry) {
-      var constant = entry.value.accept(this) as JS.Expression;
+      var constant = entry.value.accept(this) as js_ast.Expression;
       var member = entry.key.asField;
-      return JS.Property(
+      return js_ast.Property(
           _emitMemberName(member.name.name, member: member), constant);
     }
 
     var type = visitInterfaceType(node.getType(types) as InterfaceType);
     var prototype = js.call("#.prototype", [type]);
     var properties = [
-      JS.Property(propertyName("__proto__"), prototype),
+      js_ast.Property(propertyName("__proto__"), prototype),
       for (var e in node.fieldValues.entries) entryToProperty(e),
     ];
     return canonicalizeConstObject(
-        JS.ObjectInitializer(properties, multiline: true));
+        js_ast.ObjectInitializer(properties, multiline: true));
   }
 
   @override
-  JS.Expression visitTearOffConstant(TearOffConstant node) =>
+  js_ast.Expression visitTearOffConstant(TearOffConstant node) =>
       _emitStaticGet(node.procedure);
 
   @override
-  JS.Expression visitTypeLiteralConstant(TypeLiteralConstant node) =>
+  js_ast.Expression visitTypeLiteralConstant(TypeLiteralConstant node) =>
       _emitTypeLiteral(node.type);
 
   @override
-  JS.Expression visitPartialInstantiationConstant(
+  js_ast.Expression visitPartialInstantiationConstant(
           PartialInstantiationConstant node) =>
       runtimeCall('gbind(#, #)', [
         visitConstant(node.tearOffConstant),
@@ -5330,7 +5366,7 @@
       ]);
 
   @override
-  JS.Expression visitUnevaluatedConstant(UnevaluatedConstant node) =>
+  js_ast.Expression visitUnevaluatedConstant(UnevaluatedConstant node) =>
       _visitExpression(node.expression);
 }
 
@@ -5383,7 +5419,7 @@
 
 class SwitchLabelState {
   String label;
-  JS.Identifier variable;
+  js_ast.Identifier variable;
 
   SwitchLabelState(this.label, this.variable);
 }
diff --git a/pkg/dev_compiler/lib/src/kernel/property_model.dart b/pkg/dev_compiler/lib/src/kernel/property_model.dart
index df0444b..7c5bcee 100644
--- a/pkg/dev_compiler/lib/src/kernel/property_model.dart
+++ b/pkg/dev_compiler/lib/src/kernel/property_model.dart
@@ -7,8 +7,8 @@
 import 'package:kernel/core_types.dart';
 import 'package:kernel/kernel.dart';
 import 'package:kernel/type_environment.dart';
-import '../compiler/js_names.dart' as JS;
-import '../js_ast/js_ast.dart' as JS;
+
+import '../compiler/js_names.dart' as js_ast;
 import 'kernel_helpers.dart';
 import 'native_types.dart';
 
@@ -183,7 +183,7 @@
   /// pair in JavaScript.
   ///
   /// The value property stores the symbol used for the field's storage slot.
-  final virtualFields = <Field, JS.TemporaryId>{};
+  final virtualFields = <Field, js_ast.TemporaryId>{};
 
   /// The set of inherited getters, used because JS getters/setters are paired,
   /// so if we're generating a setter we may need to emit a getter that calls
@@ -246,7 +246,7 @@
           fieldModel.isVirtual(field) ||
           field.isCovariant ||
           field.isGenericCovariantImpl) {
-        virtualFields[field] = JS.TemporaryId(name);
+        virtualFields[field] = js_ast.TemporaryId(name);
       }
     }
   }
diff --git a/pkg/dev_compiler/lib/src/kernel/target.dart b/pkg/dev_compiler/lib/src/kernel/target.dart
index e954139..c1635a0 100644
--- a/pkg/dev_compiler/lib/src/kernel/target.dart
+++ b/pkg/dev_compiler/lib/src/kernel/target.dart
@@ -20,6 +20,8 @@
 
   ClassHierarchy hierarchy;
 
+  WidgetCreatorTracker _widgetTracker;
+
   @override
   bool get legacyMode => false;
 
@@ -117,7 +119,10 @@
       DiagnosticReporter diagnosticReporter,
       {void logger(String msg)}) {
     if (flags.trackWidgetCreation) {
-      WidgetCreatorTracker().transform(component, libraries);
+      if (_widgetTracker == null) {
+        _widgetTracker = WidgetCreatorTracker();
+      }
+      _widgetTracker.transform(component, libraries);
     }
   }
 
diff --git a/pkg/dev_compiler/lib/src/kernel/type_table.dart b/pkg/dev_compiler/lib/src/kernel/type_table.dart
index 7dd63c9..076075c 100644
--- a/pkg/dev_compiler/lib/src/kernel/type_table.dart
+++ b/pkg/dev_compiler/lib/src/kernel/type_table.dart
@@ -4,9 +4,9 @@
 
 import 'package:kernel/kernel.dart';
 
-import '../js_ast/js_ast.dart' as JS;
+import '../compiler/js_names.dart' as js_ast;
+import '../js_ast/js_ast.dart' as js_ast;
 import '../js_ast/js_ast.dart' show js;
-import '../compiler/js_names.dart' as JS;
 
 Set<TypeParameter> freeTypeParameters(DartType t) {
   var result = Set<TypeParameter>();
@@ -37,10 +37,10 @@
   // Use a LinkedHashMap to maintain key insertion order so the generated code
   // is stable under slight perturbation.  (If this is not good enough we could
   // sort by name to canonicalize order.)
-  final _names = <DartType, JS.TemporaryId>{};
+  final _names = <DartType, js_ast.TemporaryId>{};
   Iterable<DartType> get keys => _names.keys.toList();
 
-  JS.Statement _dischargeType(DartType type) {
+  js_ast.Statement _dischargeType(DartType type) {
     var name = _names.remove(type);
     if (name != null) {
       return js.statement('let #;', [name]);
@@ -51,8 +51,8 @@
   /// Emit a list of statements declaring the cache variables for
   /// types tracked by this table.  If [typeFilter] is given,
   /// only emit the types listed in the filter.
-  List<JS.Statement> discharge([Iterable<DartType> typeFilter]) {
-    var decls = <JS.Statement>[];
+  List<js_ast.Statement> discharge([Iterable<DartType> typeFilter]) {
+    var decls = <js_ast.Statement>[];
     var types = typeFilter ?? keys;
     for (var t in types) {
       var stmt = _dischargeType(t);
@@ -102,25 +102,25 @@
 
   /// Heuristically choose a good name for the cache and generator
   /// variables.
-  JS.TemporaryId chooseTypeName(DartType type) {
-    return JS.TemporaryId(_typeString(type));
+  js_ast.TemporaryId chooseTypeName(DartType type) {
+    return js_ast.TemporaryId(_typeString(type));
   }
 }
 
 /// _GeneratorTable tracks types which have been
 /// named and hoisted.
 class _GeneratorTable extends _CacheTable {
-  final _defs = <DartType, JS.Expression>{};
+  final _defs = <DartType, js_ast.Expression>{};
 
-  final JS.Identifier _runtimeModule;
+  final js_ast.Identifier _runtimeModule;
 
   _GeneratorTable(this._runtimeModule);
 
   @override
-  JS.Statement _dischargeType(DartType t) {
+  js_ast.Statement _dischargeType(DartType t) {
     var name = _names.remove(t);
     if (name != null) {
-      JS.Expression init = _defs.remove(t);
+      js_ast.Expression init = _defs.remove(t);
       assert(init != null);
       return js.statement('let # = () => ((# = #.constFn(#))());',
           [name, name, _runtimeModule, init]);
@@ -131,7 +131,7 @@
   /// If [type] does not already have a generator name chosen for it,
   /// assign it one, using [typeRep] as the initializer for it.
   /// Emit the generator name.
-  JS.TemporaryId _nameType(DartType type, JS.Expression typeRep) {
+  js_ast.TemporaryId _nameType(DartType type, js_ast.Expression typeRep) {
     var temp = _names[type];
     if (temp == null) {
       _names[type] = temp = chooseTypeName(type);
@@ -151,12 +151,12 @@
   /// parameter.
   final _scopeDependencies = <TypeParameter, List<DartType>>{};
 
-  TypeTable(JS.Identifier runtime) : _generators = _GeneratorTable(runtime);
+  TypeTable(js_ast.Identifier runtime) : _generators = _GeneratorTable(runtime);
 
   /// Emit a list of statements declaring the cache variables and generator
   /// definitions tracked by the table.  If [formals] is present, only
   /// emit the definitions which depend on the formals.
-  List<JS.Statement> discharge([List<TypeParameter> formals]) {
+  List<js_ast.Statement> discharge([List<TypeParameter> formals]) {
     var filter = formals?.expand((p) => _scopeDependencies[p] ?? <DartType>[]);
     var stmts = _generators.discharge(filter);
     formals?.forEach(_scopeDependencies.remove);
@@ -186,7 +186,7 @@
   /// Given a type [type], and a JS expression [typeRep] which implements it,
   /// add the type and its representation to the table, returning an
   /// expression which implements the type (but which caches the value).
-  JS.Expression nameType(DartType type, JS.Expression typeRep) {
+  js_ast.Expression nameType(DartType type, js_ast.Expression typeRep) {
     if (!_generators.isNamed(type) && recordScopeDependencies(type)) {
       return typeRep;
     }
@@ -200,10 +200,11 @@
   /// should be a function that is invoked to compute the type, rather than the
   /// type itself. This allows better integration with `lazyFn`, avoiding an
   /// extra level of indirection.
-  JS.Expression nameFunctionType(FunctionType type, JS.Expression typeRep,
+  js_ast.Expression nameFunctionType(
+      FunctionType type, js_ast.Expression typeRep,
       {bool lazy = false}) {
     if (!_generators.isNamed(type) && recordScopeDependencies(type)) {
-      return lazy ? JS.ArrowFun([], typeRep) : typeRep;
+      return lazy ? js_ast.ArrowFun([], typeRep) : typeRep;
     }
     var name = _generators._nameType(type, typeRep);
     return lazy ? name : js.call('#()', [name]);
diff --git a/pkg/dev_compiler/test/modular_suite.dart b/pkg/dev_compiler/test/modular_suite.dart
index 78c22cb..62fb349 100644
--- a/pkg/dev_compiler/test/modular_suite.dart
+++ b/pkg/dev_compiler/test/modular_suite.dart
@@ -68,7 +68,7 @@
         _sourceToImportUri(module, rootScheme, relativeUri);
 
     Set<Module> transitiveDependencies = computeTransitiveDependencies(module);
-    _createPackagesFile(module, root, transitiveDependencies);
+    await _createPackagesFile(module, root, transitiveDependencies);
 
     List<String> sources;
     List<String> extraArgs;
@@ -132,7 +132,7 @@
     if (_options.verbose) print("\nstep: ddk on $module");
 
     Set<Module> transitiveDependencies = computeTransitiveDependencies(module);
-    _createPackagesFile(module, root, transitiveDependencies);
+    await _createPackagesFile(module, root, transitiveDependencies);
 
     String rootScheme = module.isSdk ? 'dev-dart-sdk' : 'dev-dart-app';
     List<String> sources;
diff --git a/pkg/dev_compiler/test/options/options_test.dart b/pkg/dev_compiler/test/options/options_test.dart
index 7f78142..454daab 100644
--- a/pkg/dev_compiler/test/options/options_test.dart
+++ b/pkg/dev_compiler/test/options/options_test.dart
@@ -7,13 +7,13 @@
 import 'package:analyzer/src/command_line/arguments.dart';
 import 'package:analyzer/src/error/codes.dart';
 import 'package:analyzer/src/summary/summary_sdk.dart';
+import 'package:dev_compiler/src/analyzer/context.dart';
+import 'package:dev_compiler/src/analyzer/command.dart';
+import 'package:dev_compiler/src/analyzer/driver.dart';
+import 'package:dev_compiler/src/analyzer/module_compiler.dart';
 import 'package:path/path.dart' as path;
 import 'package:test/test.dart';
 
-import '../../lib/src/analyzer/context.dart';
-import '../../lib/src/analyzer/command.dart';
-import '../../lib/src/analyzer/driver.dart';
-import '../../lib/src/analyzer/module_compiler.dart';
 import '../testing.dart' show repoDirectory, testDirectory;
 
 /// The `test/options` directory.
diff --git a/pkg/dev_compiler/test/sourcemap/ddc_common.dart b/pkg/dev_compiler/test/sourcemap/ddc_common.dart
index 4308f5b..33f0a99 100644
--- a/pkg/dev_compiler/test/sourcemap/ddc_common.dart
+++ b/pkg/dev_compiler/test/sourcemap/ddc_common.dart
@@ -152,7 +152,6 @@
     };
 
     let main = $inputFileNameNoExt.main;
-    dart.ignoreWhitelistedErrors(false);
     try {
       dartMainRunner(main, []);
     } catch(e) {
@@ -163,7 +162,7 @@
 
 void createHtmlWrapper(File sdkJsFile, Uri outputFile, String jsContent,
     String outputFilename, Uri outDir) {
-  // For debugging via HTML, Chrome and ./tools/testing/dart/http_server.dart.
+  // For debugging via HTML, Chrome and ./pkg/test_runner/bin/http_server.dart.
   var sdkFile = File(path.relative(sdkJsFile.path, from: sdkRoot.path));
   String jsRootDart = "/root_dart/${sdkFile.uri}";
   File.fromUri(outputFile.resolve("$outputFilename.html.js")).writeAsStringSync(
@@ -173,7 +172,7 @@
           jsRootDart, "/root_build/$outputFilename.html.js"));
 
   print("You should now be able to run\n\n"
-      "dart ${sdkRoot.path}/tools/testing/dart/http_server.dart -p 39550 "
+      "dart ${sdkRoot.path}/pkg/test_runner/bin/http_server.dart -p 39550 "
       "--network 127.0.0.1 "
       "--build-directory=${outDir.toFilePath()}"
       "\n\nand go to\n\n"
@@ -191,7 +190,6 @@
     import { dart, _isolate_helper } from '$jsRootDart';
     import { test } from '$outFileRootBuild';
     let main = test.main;
-    dart.ignoreWhitelistedErrors(false);
     main();
     </script>
   </head>
diff --git a/pkg/dev_compiler/test/sourcemap/stacktrace_testfiles/throw_in_try_catch.dart b/pkg/dev_compiler/test/sourcemap/stacktrace_testfiles/throw_in_try_catch.dart
index 1da6f77..7effa59 100644
--- a/pkg/dev_compiler/test/sourcemap/stacktrace_testfiles/throw_in_try_catch.dart
+++ b/pkg/dev_compiler/test/sourcemap/stacktrace_testfiles/throw_in_try_catch.dart
@@ -10,5 +10,7 @@
   try {
     /*2:test*/ throw '>ExceptionMarker<';
     // ignore: UNUSED_CATCH_CLAUSE
-  } on Error catch (e) {}
+  } on Error catch (e) {
+    // ignore: EMPTY_CATCHES
+  }
 }
diff --git a/pkg/dev_compiler/test/sourcemap/testfiles/next_through_assert.dart b/pkg/dev_compiler/test/sourcemap/testfiles/next_through_assert.dart
index bcf0307..eab2e71 100644
--- a/pkg/dev_compiler/test/sourcemap/testfiles/next_through_assert.dart
+++ b/pkg/dev_compiler/test/sourcemap/testfiles/next_through_assert.dart
@@ -7,6 +7,6 @@
   assert(/*bc:1*/ foo());
 }
 
-foo() {
+bool foo() {
   return true;
 }
diff --git a/pkg/dev_compiler/test/worker/worker_test.dart b/pkg/dev_compiler/test/worker/worker_test.dart
index 5d8e8ff..8bc7a64 100644
--- a/pkg/dev_compiler/test/worker/worker_test.dart
+++ b/pkg/dev_compiler/test/worker/worker_test.dart
@@ -143,7 +143,7 @@
       argsFile.writeAsStringSync(compilerArgs.join('\n'));
       var args = executableArgs.toList()..add('@${argsFile.path}');
       var process = await Process.start(executable, args);
-      stderr.addStream(process.stderr);
+      await stderr.addStream(process.stderr);
       var futureProcessOutput = process.stdout.map(utf8.decode).toList();
 
       expect(await process.exitCode, EXIT_CODE_OK);
diff --git a/pkg/dev_compiler/tool/ddb b/pkg/dev_compiler/tool/ddb
index 6cd6de3..67be845 100755
--- a/pkg/dev_compiler/tool/ddb
+++ b/pkg/dev_compiler/tool/ddb
@@ -17,10 +17,6 @@
 import 'package:args/args.dart' show ArgParser;
 import 'package:path/path.dart' as path;
 
-// TODO(vsm): Remove this once we stop ignoring.  It's off here, but
-// configurable for manual testing.
-const ignoreWhitelistedErrors = false;
-
 void main(List<String> args) async {
   void printUsage() {
     print('Usage: ddb [options] <dart-script-file>\n');
@@ -201,7 +197,6 @@
         function(sdk, app) {
     'use strict';
     sdk._debugger.registerDevtoolsFormatter();
-    sdk.dart.ignoreWhitelistedErrors($ignoreWhitelistedErrors);
     app.$basename.main();
   });
 </script>
@@ -228,7 +223,6 @@
     }
     let sdk = require(\"dart_sdk\");
     let main = require(\"./$basename\").$basename.main;
-    sdk.dart.ignoreWhitelistedErrors($ignoreWhitelistedErrors);
     try {
       sdk._isolate_helper.startRootIsolate(main, []);
     } catch(e) {
@@ -259,7 +253,6 @@
     import { dart, _isolate_helper } from '$sdkJsPath/dart_sdk.js';
     import { $basename } from '$basename.js';
     let main = $basename.main;
-    dart.ignoreWhitelistedErrors($ignoreWhitelistedErrors);
     try {
       _isolate_helper.startRootIsolate(() => {}, []);
       main();
diff --git a/pkg/dev_compiler/tool/ddc b/pkg/dev_compiler/tool/ddc
index e8d4ad0..493f531 100755
--- a/pkg/dev_compiler/tool/ddc
+++ b/pkg/dev_compiler/tool/ddc
@@ -106,7 +106,6 @@
     }
     let sdk = require(\"dart_sdk\");
     let main = require(\"./$BASENAME\").$BASENAME.main;
-    sdk.dart.ignoreWhitelistedErrors(false);
     try {
       sdk._isolate_helper.startRootIsolate(main, []);
     } catch(e) {
diff --git a/pkg/dev_compiler/tool/kernel_sdk.dart b/pkg/dev_compiler/tool/kernel_sdk.dart
index 0366522..b8d2311 100755
--- a/pkg/dev_compiler/tool/kernel_sdk.dart
+++ b/pkg/dev_compiler/tool/kernel_sdk.dart
@@ -31,7 +31,8 @@
   var parser = ArgParser()
     ..addOption('output')
     ..addOption('libraries',
-        defaultsTo: path.join(ddcPath, '../../sdk/lib/libraries.json'));
+        defaultsTo: path.join(ddcPath, '../../sdk/lib/libraries.json'))
+    ..addOption('packages', defaultsTo: path.join(ddcPath, '../../.packages'));
   var parserOptions = parser.parse(args);
 
   var outputPath = parserOptions['output'] as String;
@@ -43,7 +44,7 @@
   }
 
   var librarySpecPath = parserOptions['libraries'] as String;
-  var packagesPath = path.join(ddcPath, '../../.packages');
+  var packagesPath = parserOptions['packages'] as String;
 
   var target = DevCompilerTarget(TargetFlags());
   void onDiagnostic(DiagnosticMessage message) {
diff --git a/pkg/front_end/lib/src/api_unstable/dart2js.dart b/pkg/front_end/lib/src/api_unstable/dart2js.dart
index e6f8ac6..0955d51 100644
--- a/pkg/front_end/lib/src/api_unstable/dart2js.dart
+++ b/pkg/front_end/lib/src/api_unstable/dart2js.dart
@@ -6,6 +6,8 @@
 
 import 'package:kernel/kernel.dart' show Component;
 
+import 'package:kernel/ast.dart' as ir;
+
 import 'package:kernel/target/targets.dart' show Target;
 
 import '../api_prototype/compiler_options.dart' show CompilerOptions;
@@ -30,6 +32,8 @@
 
 import '../fasta/scanner.dart' show ErrorToken, StringToken, Token;
 
+import '../fasta/kernel/redirecting_factory_body.dart' as redirecting;
+
 import 'compiler_state.dart' show InitializedCompilerState;
 
 import 'util.dart' show equalLists, equalMaps;
@@ -61,9 +65,6 @@
 
 export '../fasta/fasta_codes.dart' show LocatedMessage;
 
-export '../fasta/kernel/redirecting_factory_body.dart'
-    show RedirectingFactoryBody;
-
 export '../fasta/operator.dart' show operatorFromString;
 
 export '../fasta/parser/async_modifier.dart' show AsyncModifier;
@@ -212,3 +213,23 @@
       .where((l) => l.isSupported)
       .map((l) => l.name);
 }
+
+/// Desugar API to determine whether [member] is a redirecting factory
+/// constructor.
+// TODO(sigmund): Delete this API once `member.isRedirectingFactoryConstructor`
+// is implemented correctly for patch files (Issue #33495).
+bool isRedirectingFactory(ir.Procedure member) {
+  if (member.kind == ir.ProcedureKind.Factory) {
+    var body = member.function.body;
+    if (body is redirecting.RedirectingFactoryBody) return true;
+    if (body is ir.ExpressionStatement) {
+      ir.Expression expression = body.expression;
+      if (expression is ir.Let) {
+        if (expression.variable.name == redirecting.letName) {
+          return true;
+        }
+      }
+    }
+  }
+  return false;
+}
diff --git a/pkg/front_end/lib/src/api_unstable/vm.dart b/pkg/front_end/lib/src/api_unstable/vm.dart
index 20ad6ad..77ea7b9 100644
--- a/pkg/front_end/lib/src/api_unstable/vm.dart
+++ b/pkg/front_end/lib/src/api_unstable/vm.dart
@@ -40,7 +40,8 @@
         templateFfiTypeInvalid,
         templateFfiTypeMismatch,
         templateFfiTypeUnsized,
-        templateFfiFieldInitializer;
+        templateFfiFieldInitializer,
+        templateIllegalRecursiveType;
 
 export '../fasta/hybrid_file_system.dart' show HybridFileSystem;
 
diff --git a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
index e61cf1c..b25813b 100644
--- a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
+++ b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
@@ -4277,6 +4277,29 @@
 }
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<Message Function(DartType _type)> templateIllegalRecursiveType =
+    const Template<Message Function(DartType _type)>(
+        messageTemplate: r"""Illegal recursive type '#type'.""",
+        withArguments: _withArgumentsIllegalRecursiveType);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(DartType _type)> codeIllegalRecursiveType =
+    const Code<Message Function(DartType _type)>(
+  "IllegalRecursiveType",
+  templateIllegalRecursiveType,
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsIllegalRecursiveType(DartType _type) {
+  TypeLabeler labeler = new TypeLabeler();
+  List<Object> typeParts = labeler.labelType(_type);
+  String type = typeParts.join();
+  return new Message(codeIllegalRecursiveType,
+      message: """Illegal recursive type '${type}'.""" + labeler.originMessages,
+      arguments: {'type': _type});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Code<Null> codeIllegalSyncGeneratorReturnType =
     messageIllegalSyncGeneratorReturnType;
 
@@ -8359,14 +8382,6 @@
     message: r"""A set literal requires exactly one type argument.""");
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Code<Null> codeSetLiteralsNotSupported = messageSetLiteralsNotSupported;
-
-// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const MessageCode messageSetLiteralsNotSupported = const MessageCode(
-    "SetLiteralsNotSupported",
-    message: r"""Set literals are not supported yet.""");
-
-// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Code<Null> codeSetOrMapLiteralTooManyTypeArguments =
     messageSetOrMapLiteralTooManyTypeArguments;
 
diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
index 2599943..71c58dc 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -2092,7 +2092,7 @@
         isConst: isConst)
       ..fileOffset = identifier.charOffset
       ..fileEqualsOffset = offsetForToken(equalsToken);
-    library.checkBoundsInVariableDeclaration(variable, typeEnvironment);
+    library.checkBoundsInVariableDeclaration(variable, typeEnvironment, uri);
     push(variable);
   }
 
@@ -2464,7 +2464,7 @@
         leftBracket,
         expressions,
         rightBracket);
-    library.checkBoundsInListLiteral(node, typeEnvironment);
+    library.checkBoundsInListLiteral(node, typeEnvironment, uri);
     push(node);
   }
 
@@ -2503,12 +2503,7 @@
         leftBrace,
         expressions,
         leftBrace.endGroup);
-    library.checkBoundsInSetLiteral(node, typeEnvironment);
-    if (!library.loader.target.enableSetLiterals) {
-      internalProblem(
-          fasta.messageSetLiteralsNotSupported, node.fileOffset, uri);
-      return;
-    }
+    library.checkBoundsInSetLiteral(node, typeEnvironment, uri);
     push(node);
   }
 
@@ -2627,7 +2622,7 @@
         leftBrace,
         entries,
         leftBrace.endGroup);
-    library.checkBoundsInMapLiteral(node, typeEnvironment);
+    library.checkBoundsInMapLiteral(node, typeEnvironment, uri);
     push(node);
   }
 
@@ -2793,7 +2788,7 @@
   void handleAsOperator(Token operator) {
     debugEvent("AsOperator");
     DartType type = buildDartType(pop());
-    library.checkBoundsInType(type, typeEnvironment, operator.charOffset);
+    library.checkBoundsInType(type, typeEnvironment, uri, operator.charOffset);
     Expression expression = popForValue();
     if (!library.loader.target.enableConstantUpdate2018 &&
         constantContext != ConstantContext.none) {
@@ -2815,7 +2810,8 @@
     bool isInverted = not != null;
     Expression isExpression =
         forest.isExpression(operand, isOperator, not, type);
-    library.checkBoundsInType(type, typeEnvironment, isOperator.charOffset);
+    library.checkBoundsInType(
+        type, typeEnvironment, uri, isOperator.charOffset);
     if (operand is VariableGet) {
       typePromoter?.handleIsCheck(isExpression, isInverted, operand.variable,
           type, functionNestingLevel);
@@ -3372,7 +3368,7 @@
           target, forest.castArguments(arguments),
           isConst: isConst)
         ..fileOffset = charOffset;
-      library.checkBoundsInConstructorInvocation(node, typeEnvironment);
+      library.checkBoundsInConstructorInvocation(node, typeEnvironment, uri);
       return node;
     } else {
       Procedure procedure = target;
@@ -3392,14 +3388,14 @@
             target, forest.castArguments(arguments),
             isConst: isConst)
           ..fileOffset = charOffset;
-        library.checkBoundsInFactoryInvocation(node, typeEnvironment);
+        library.checkBoundsInFactoryInvocation(node, typeEnvironment, uri);
         return node;
       } else {
         StaticInvocation node = new StaticInvocation(
             target, forest.castArguments(arguments),
             isConst: isConst)
           ..fileOffset = charOffset;
-        library.checkBoundsInStaticInvocation(node, typeEnvironment);
+        library.checkBoundsInStaticInvocation(node, typeEnvironment, uri);
         return node;
       }
     }
diff --git a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
index b015270..d334dac 100644
--- a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
@@ -2139,8 +2139,9 @@
     fields.forEach((Field field, Constant value) {
       fieldValues[field.reference] = evaluator.extract(value);
     });
+    // TODO(askesc): Put actual unused arguments.
     return new InstanceCreation(
-        klass.reference, typeArguments, fieldValues, asserts);
+        klass.reference, typeArguments, fieldValues, asserts, []);
   }
 }
 
diff --git a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
index 830e13a..67a503d 100644
--- a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
@@ -183,7 +183,7 @@
       KernelLibraryBuilder library = inferrer.library;
       if (!hasExplicitTypeArguments) {
         library.checkBoundsInConstructorInvocation(
-            node, inferrer.typeSchemaEnvironment,
+            node, inferrer.typeSchemaEnvironment, inferrer.helper.uri,
             inferred: true);
       }
     }
@@ -244,7 +244,7 @@
       KernelLibraryBuilder library = inferrer.library;
       if (!hadExplicitTypeArguments) {
         library.checkBoundsInFactoryInvocation(
-            node, inferrer.typeSchemaEnvironment,
+            node, inferrer.typeSchemaEnvironment, inferrer.helper.uri,
             inferred: true);
       }
     }
@@ -919,7 +919,8 @@
     if (!inferrer.isTopLevel) {
       KernelLibraryBuilder library = inferrer.library;
       if (inferenceNeeded) {
-        library.checkBoundsInListLiteral(node, inferrer.typeSchemaEnvironment,
+        library.checkBoundsInListLiteral(
+            node, inferrer.typeSchemaEnvironment, inferrer.helper.uri,
             inferred: true);
       }
     }
@@ -1277,34 +1278,30 @@
     assert((node.keyType is ImplicitTypeArgument) ==
         (node.valueType is ImplicitTypeArgument));
     bool inferenceNeeded = node.keyType is ImplicitTypeArgument;
-    KernelLibraryBuilder library = inferrer.library;
     bool typeContextIsMap = node.keyType is! ImplicitTypeArgument;
     bool typeContextIsIterable = false;
-    if (!inferrer.isTopLevel) {
-      if (library.loader.target.enableSetLiterals && inferenceNeeded) {
-        // Ambiguous set/map literal
-        DartType context =
-            inferrer.typeSchemaEnvironment.unfutureType(typeContext);
-        if (context is InterfaceType) {
-          typeContextIsMap = typeContextIsMap ||
-              inferrer.classHierarchy
-                  .isSubtypeOf(context.classNode, inferrer.coreTypes.mapClass);
-          typeContextIsIterable = typeContextIsIterable ||
-              inferrer.classHierarchy.isSubtypeOf(
-                  context.classNode, inferrer.coreTypes.iterableClass);
-          if (node.entries.isEmpty &&
-              typeContextIsIterable &&
-              !typeContextIsMap) {
-            // Set literal
-            SetLiteralJudgment setLiteral = new SetLiteralJudgment([],
-                typeArgument: const ImplicitTypeArgument(),
-                isConst: node.isConst)
-              ..fileOffset = node.fileOffset;
-            node.parent.replaceChild(node, setLiteral);
-            visitSetLiteralJudgment(setLiteral, typeContext);
-            node.inferredType = setLiteral.inferredType;
-            return;
-          }
+    if (!inferrer.isTopLevel && inferenceNeeded) {
+      // Ambiguous set/map literal
+      DartType context =
+          inferrer.typeSchemaEnvironment.unfutureType(typeContext);
+      if (context is InterfaceType) {
+        typeContextIsMap = typeContextIsMap ||
+            inferrer.classHierarchy
+                .isSubtypeOf(context.classNode, inferrer.coreTypes.mapClass);
+        typeContextIsIterable = typeContextIsIterable ||
+            inferrer.classHierarchy.isSubtypeOf(
+                context.classNode, inferrer.coreTypes.iterableClass);
+        if (node.entries.isEmpty &&
+            typeContextIsIterable &&
+            !typeContextIsMap) {
+          // Set literal
+          SetLiteralJudgment setLiteral = new SetLiteralJudgment([],
+              typeArgument: const ImplicitTypeArgument(), isConst: node.isConst)
+            ..fileOffset = node.fileOffset;
+          node.parent.replaceChild(node, setLiteral);
+          visitSetLiteralJudgment(setLiteral, typeContext);
+          node.inferredType = setLiteral.inferredType;
+          return;
         }
       }
     }
@@ -1475,7 +1472,8 @@
       // Either both [_declaredKeyType] and [_declaredValueType] are omitted or
       // none of them, so we may just check one.
       if (inferenceNeeded) {
-        library.checkBoundsInMapLiteral(node, inferrer.typeSchemaEnvironment,
+        library.checkBoundsInMapLiteral(
+            node, inferrer.typeSchemaEnvironment, inferrer.helper.uri,
             inferred: true);
       }
     }
@@ -1760,7 +1758,8 @@
     if (!inferrer.isTopLevel) {
       KernelLibraryBuilder library = inferrer.library;
       if (inferenceNeeded) {
-        library.checkBoundsInSetLiteral(node, inferrer.typeSchemaEnvironment,
+        library.checkBoundsInSetLiteral(
+            node, inferrer.typeSchemaEnvironment, inferrer.helper.uri,
             inferred: true);
       }
 
@@ -1814,7 +1813,7 @@
     inferrer.storeInferredType(node, inferenceResult.type);
     if (!hadExplicitTypeArguments && node.target != null) {
       inferrer.library?.checkBoundsInStaticInvocation(
-          node, inferrer.typeSchemaEnvironment,
+          node, inferrer.typeSchemaEnvironment, inferrer.helper.uri,
           inferred: true);
     }
   }
@@ -2025,7 +2024,7 @@
       KernelLibraryBuilder library = inferrer.library;
       if (node._implicitlyTyped) {
         library.checkBoundsInVariableDeclaration(
-            node, inferrer.typeSchemaEnvironment,
+            node, inferrer.typeSchemaEnvironment, inferrer.helper.uri,
             inferred: true);
       }
     }
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart
index 5e4ac3a..b36770c 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart
@@ -340,7 +340,8 @@
           }
         }
 
-        library.reportTypeArgumentIssue(message, charOffset, typeParameter);
+        library.reportTypeArgumentIssue(
+            message, fileUri, charOffset, typeParameter);
       }
     }
   }
@@ -379,7 +380,7 @@
           }
 
           library.reportTypeArgumentIssue(
-              message, parameter.fileOffset, typeParameter);
+              message, fileUri, parameter.fileOffset, typeParameter);
         }
       }
     }
@@ -402,15 +403,17 @@
       library.checkBoundsInField(field, typeEnvironment);
     }
     for (Procedure procedure in cls.procedures) {
-      library.checkBoundsInFunctionNode(procedure.function, typeEnvironment);
+      library.checkBoundsInFunctionNode(
+          procedure.function, typeEnvironment, fileUri);
     }
     for (Constructor constructor in cls.constructors) {
-      library.checkBoundsInFunctionNode(constructor.function, typeEnvironment);
+      library.checkBoundsInFunctionNode(
+          constructor.function, typeEnvironment, fileUri);
     }
     for (RedirectingFactoryConstructor redirecting
         in cls.redirectingFactoryConstructors) {
       library.checkBoundsInFunctionNodeParts(
-          typeEnvironment, redirecting.fileOffset,
+          typeEnvironment, fileUri, redirecting.fileOffset,
           typeParameters: redirecting.typeParameters,
           positionalParameters: redirecting.positionalParameters,
           namedParameters: redirecting.namedParameters);
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart
index 1532c58..91a846a 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart
@@ -1481,7 +1481,8 @@
     addToExportScope(name, member);
   }
 
-  void reportTypeArgumentIssues(List<TypeArgumentIssue> issues, int offset,
+  void reportTypeArgumentIssues(
+      List<TypeArgumentIssue> issues, Uri fileUri, int offset,
       {bool inferred, DartType targetReceiver, String targetName}) {
     for (TypeArgumentIssue issue in issues) {
       DartType argument = issue.argument;
@@ -1542,12 +1543,12 @@
         }
       }
 
-      reportTypeArgumentIssue(message, offset, typeParameter);
+      reportTypeArgumentIssue(message, fileUri, offset, typeParameter);
     }
   }
 
-  void reportTypeArgumentIssue(
-      Message message, int fileOffset, TypeParameter typeParameter) {
+  void reportTypeArgumentIssue(Message message, Uri fileUri, int fileOffset,
+      TypeParameter typeParameter) {
     List<LocatedMessage> context;
     if (typeParameter != null && typeParameter.fileOffset != -1) {
       // It looks like when parameters come from patch files, they don't
@@ -1562,12 +1563,13 @@
 
   void checkBoundsInField(Field field, TypeEnvironment typeEnvironment) {
     if (loader.target.legacyMode) return;
-    checkBoundsInType(field.type, typeEnvironment, field.fileOffset,
+    checkBoundsInType(
+        field.type, typeEnvironment, field.fileUri, field.fileOffset,
         allowSuperBounded: true);
   }
 
   void checkBoundsInFunctionNodeParts(
-      TypeEnvironment typeEnvironment, int fileOffset,
+      TypeEnvironment typeEnvironment, Uri fileUri, int fileOffset,
       {List<TypeParameter> typeParameters,
       List<VariableDeclaration> positionalParameters,
       List<VariableDeclaration> namedParameters,
@@ -1576,19 +1578,21 @@
     if (typeParameters != null) {
       for (TypeParameter parameter in typeParameters) {
         checkBoundsInType(
-            parameter.bound, typeEnvironment, parameter.fileOffset,
+            parameter.bound, typeEnvironment, fileUri, parameter.fileOffset,
             allowSuperBounded: true);
       }
     }
     if (positionalParameters != null) {
       for (VariableDeclaration formal in positionalParameters) {
-        checkBoundsInType(formal.type, typeEnvironment, formal.fileOffset,
+        checkBoundsInType(
+            formal.type, typeEnvironment, fileUri, formal.fileOffset,
             allowSuperBounded: true);
       }
     }
     if (namedParameters != null) {
       for (VariableDeclaration named in namedParameters) {
-        checkBoundsInType(named.type, typeEnvironment, named.fileOffset,
+        checkBoundsInType(
+            named.type, typeEnvironment, fileUri, named.fileOffset,
             allowSuperBounded: true);
       }
     }
@@ -1617,16 +1621,17 @@
                 getGenericTypeName(issue.enclosingType));
           }
 
-          reportTypeArgumentIssue(message, offset, typeParameter);
+          reportTypeArgumentIssue(message, fileUri, offset, typeParameter);
         }
       }
     }
   }
 
   void checkBoundsInFunctionNode(
-      FunctionNode function, TypeEnvironment typeEnvironment) {
+      FunctionNode function, TypeEnvironment typeEnvironment, Uri fileUri) {
     if (loader.target.legacyMode) return;
-    checkBoundsInFunctionNodeParts(typeEnvironment, function.fileOffset,
+    checkBoundsInFunctionNodeParts(
+        typeEnvironment, fileUri, function.fileOffset,
         typeParameters: function.typeParameters,
         positionalParameters: function.positionalParameters,
         namedParameters: function.namedParameters,
@@ -1634,64 +1639,69 @@
   }
 
   void checkBoundsInListLiteral(
-      ListLiteral node, TypeEnvironment typeEnvironment,
+      ListLiteral node, TypeEnvironment typeEnvironment, Uri fileUri,
       {bool inferred = false}) {
     if (loader.target.legacyMode) return;
-    checkBoundsInType(node.typeArgument, typeEnvironment, node.fileOffset,
+    checkBoundsInType(
+        node.typeArgument, typeEnvironment, fileUri, node.fileOffset,
         inferred: inferred, allowSuperBounded: true);
   }
 
-  void checkBoundsInSetLiteral(SetLiteral node, TypeEnvironment typeEnvironment,
+  void checkBoundsInSetLiteral(
+      SetLiteral node, TypeEnvironment typeEnvironment, Uri fileUri,
       {bool inferred = false}) {
     if (loader.target.legacyMode) return;
-    checkBoundsInType(node.typeArgument, typeEnvironment, node.fileOffset,
+    checkBoundsInType(
+        node.typeArgument, typeEnvironment, fileUri, node.fileOffset,
         inferred: inferred, allowSuperBounded: true);
   }
 
-  void checkBoundsInMapLiteral(MapLiteral node, TypeEnvironment typeEnvironment,
+  void checkBoundsInMapLiteral(
+      MapLiteral node, TypeEnvironment typeEnvironment, Uri fileUri,
       {bool inferred = false}) {
     if (loader.target.legacyMode) return;
-    checkBoundsInType(node.keyType, typeEnvironment, node.fileOffset,
+    checkBoundsInType(node.keyType, typeEnvironment, fileUri, node.fileOffset,
         inferred: inferred, allowSuperBounded: true);
-    checkBoundsInType(node.valueType, typeEnvironment, node.fileOffset,
+    checkBoundsInType(node.valueType, typeEnvironment, fileUri, node.fileOffset,
         inferred: inferred, allowSuperBounded: true);
   }
 
   void checkBoundsInType(
-      DartType type, TypeEnvironment typeEnvironment, int offset,
+      DartType type, TypeEnvironment typeEnvironment, Uri fileUri, int offset,
       {bool inferred, bool allowSuperBounded = true}) {
     if (loader.target.legacyMode) return;
     List<TypeArgumentIssue> issues = findTypeArgumentIssues(
         type, typeEnvironment,
         allowSuperBounded: allowSuperBounded);
     if (issues != null) {
-      reportTypeArgumentIssues(issues, offset, inferred: inferred);
+      reportTypeArgumentIssues(issues, fileUri, offset, inferred: inferred);
     }
   }
 
   void checkBoundsInVariableDeclaration(
-      VariableDeclaration node, TypeEnvironment typeEnvironment,
+      VariableDeclaration node, TypeEnvironment typeEnvironment, Uri fileUri,
       {bool inferred = false}) {
     if (loader.target.legacyMode) return;
     if (node.type == null) return;
-    checkBoundsInType(node.type, typeEnvironment, node.fileOffset,
+    checkBoundsInType(node.type, typeEnvironment, fileUri, node.fileOffset,
         inferred: inferred, allowSuperBounded: true);
   }
 
   void checkBoundsInConstructorInvocation(
-      ConstructorInvocation node, TypeEnvironment typeEnvironment,
+      ConstructorInvocation node, TypeEnvironment typeEnvironment, Uri fileUri,
       {bool inferred = false}) {
     if (loader.target.legacyMode) return;
     if (node.arguments.types.isEmpty) return;
     Constructor constructor = node.target;
     Class klass = constructor.enclosingClass;
     DartType constructedType = new InterfaceType(klass, node.arguments.types);
-    checkBoundsInType(constructedType, typeEnvironment, node.fileOffset,
+    checkBoundsInType(
+        constructedType, typeEnvironment, fileUri, node.fileOffset,
         inferred: inferred, allowSuperBounded: false);
   }
 
   void checkBoundsInFactoryInvocation(
-      StaticInvocation node, TypeEnvironment typeEnvironment,
+      StaticInvocation node, TypeEnvironment typeEnvironment, Uri fileUri,
       {bool inferred = false}) {
     if (loader.target.legacyMode) return;
     if (node.arguments.types.isEmpty) return;
@@ -1699,12 +1709,13 @@
     assert(factory.isFactory);
     Class klass = factory.enclosingClass;
     DartType constructedType = new InterfaceType(klass, node.arguments.types);
-    checkBoundsInType(constructedType, typeEnvironment, node.fileOffset,
+    checkBoundsInType(
+        constructedType, typeEnvironment, fileUri, node.fileOffset,
         inferred: inferred, allowSuperBounded: false);
   }
 
   void checkBoundsInStaticInvocation(
-      StaticInvocation node, TypeEnvironment typeEnvironment,
+      StaticInvocation node, TypeEnvironment typeEnvironment, Uri fileUri,
       {bool inferred = false}) {
     if (loader.target.legacyMode) return;
     if (node.arguments.types.isEmpty) return;
@@ -1721,7 +1732,7 @@
         targetReceiver = new InterfaceType(klass);
       }
       String targetName = node.target.name.name;
-      reportTypeArgumentIssues(issues, node.fileOffset,
+      reportTypeArgumentIssues(issues, fileUri, node.fileOffset,
           inferred: inferred,
           targetReceiver: targetReceiver,
           targetName: targetName);
@@ -1736,6 +1747,7 @@
       Name name,
       Member interfaceTarget,
       Arguments arguments,
+      Uri fileUri,
       int offset,
       {bool inferred = false}) {
     if (loader.target.legacyMode) return;
@@ -1781,7 +1793,7 @@
     List<TypeArgumentIssue> issues = findTypeArgumentIssuesForInvocation(
         instantiatedMethodParameters, arguments.types, typeEnvironment);
     if (issues != null) {
-      reportTypeArgumentIssues(issues, offset,
+      reportTypeArgumentIssues(issues, fileUri, offset,
           inferred: inferred,
           targetReceiver: receiverType,
           targetName: name.name);
@@ -1796,7 +1808,8 @@
       if (declaration is KernelFieldBuilder) {
         checkBoundsInField(declaration.target, typeEnvironment);
       } else if (declaration is KernelProcedureBuilder) {
-        checkBoundsInFunctionNode(declaration.target.function, typeEnvironment);
+        checkBoundsInFunctionNode(
+            declaration.target.function, typeEnvironment, declaration.fileUri);
       } else if (declaration is KernelClassBuilder) {
         declaration.checkBoundsInOutline(typeEnvironment);
       }
diff --git a/pkg/front_end/lib/src/fasta/parser/listener.dart b/pkg/front_end/lib/src/fasta/parser/listener.dart
index 63d333f..e92f6f3 100644
--- a/pkg/front_end/lib/src/fasta/parser/listener.dart
+++ b/pkg/front_end/lib/src/fasta/parser/listener.dart
@@ -181,7 +181,6 @@
 
   /// Handle the beginning of an extension methods declaration.  Substructures:
   /// - metadata
-  /// - extension name
   /// - type variables
   void beginExtensionDeclaration(Token extensionKeyword, Token name) {}
 
diff --git a/pkg/front_end/lib/src/fasta/parser/parser.dart b/pkg/front_end/lib/src/fasta/parser/parser.dart
index 3583dc6..ed13bfe 100644
--- a/pkg/front_end/lib/src/fasta/parser/parser.dart
+++ b/pkg/front_end/lib/src/fasta/parser/parser.dart
@@ -578,9 +578,19 @@
       // as an identifier such as "abstract<T>() => 0;"
       // or as a prefix such as "abstract.A b() => 0;".
       String nextValue = keyword.next.stringValue;
-      if (identical(nextValue, '(') ||
-          identical(nextValue, '<') ||
-          identical(nextValue, '.')) {
+      if (identical(nextValue, '(') || identical(nextValue, '.')) {
+        directiveState?.checkDeclaration();
+        return parseTopLevelMemberImpl(start);
+      } else if (identical(nextValue, '<')) {
+        if (identical(value, 'extension')) {
+          // The neame in an extension declaration is optional:
+          // `extension<T> on ...`
+          Token endGroup = keyword.next.endGroup;
+          if (endGroup != null && optional('on', endGroup.next)) {
+            directiveState?.checkDeclaration();
+            return parseExtension(keyword);
+          }
+        }
         directiveState?.checkDeclaration();
         return parseTopLevelMemberImpl(start);
       } else {
@@ -2058,16 +2068,21 @@
   }
 
   /// ```
-  /// 'extension' <identifier><typeParameters>? 'on' <type> '?'?
+  /// 'extension' <identifier>? <typeParameters>? 'on' <type> '?'?
   //   `{'
   //     <memberDeclaration>*
   //   `}'
   /// ```
   Token parseExtension(Token extensionKeyword) {
     assert(optional('extension', extensionKeyword));
-    Token name = ensureIdentifier(
-        extensionKeyword, IdentifierContext.classOrMixinOrExtensionDeclaration);
-    Token token = computeTypeParamOrArg(name, true).parseVariables(name, this);
+    Token token = extensionKeyword;
+    Token name = token.next;
+    if (name.isIdentifier && !optional('on', name)) {
+      token = name;
+    } else {
+      name = null;
+    }
+    token = computeTypeParamOrArg(token, true).parseVariables(token, this);
     listener.beginExtensionDeclaration(extensionKeyword, name);
     Token onKeyword = token.next;
     if (!optional('on', onKeyword)) {
@@ -2118,20 +2133,9 @@
   Token parseStringPart(Token token) {
     Token next = token.next;
     if (next.kind != STRING_TOKEN) {
-      bool errorReported = false;
-      while (next is ErrorToken) {
-        errorReported = true;
-        reportErrorToken(next);
-        token = next;
-        next = token.next;
-      }
-      if (next.kind != STRING_TOKEN) {
-        if (!errorReported) {
-          reportRecoverableErrorWithToken(next, fasta.templateExpectedString);
-        }
-        next = rewriter.insertToken(token,
-            new SyntheticStringToken(TokenType.STRING, '', next.charOffset));
-      }
+      reportRecoverableErrorWithToken(next, fasta.templateExpectedString);
+      next = rewriter.insertToken(token,
+          new SyntheticStringToken(TokenType.STRING, '', next.charOffset));
     }
     listener.handleStringPart(next);
     return next;
@@ -4005,8 +4009,9 @@
             token = parsePrimary(
                 token.next, IdentifierContext.expressionContinuation);
             listener.endBinaryExpression(operator);
-          } else if ((identical(type, TokenType.OPEN_PAREN)) ||
-              (identical(type, TokenType.OPEN_SQUARE_BRACKET))) {
+          } else if (identical(type, TokenType.OPEN_PAREN) ||
+              identical(type, TokenType.OPEN_SQUARE_BRACKET) ||
+              identical(type, TokenType.QUESTION_PERIOD_OPEN_SQUARE_BRACKET)) {
             token = parseArgumentOrIndexStar(token, typeArg);
           } else if (identical(type, TokenType.INDEX)) {
             BeginToken replacement = link(
@@ -4176,7 +4181,7 @@
     Token next = token.next;
     Token beginToken = next;
     while (true) {
-      if (optional('[', next)) {
+      if (optional('[', next) || optional('?.[', next)) {
         assert(typeArg == noTypeParamOrArg);
         Token openSquareBracket = next;
         bool old = mayParseFunctionExpressions;
@@ -4287,20 +4292,7 @@
     //
     // Recovery code.
     //
-    if (token.next is ErrorToken) {
-      token = token.next;
-      Token previous;
-      do {
-        // Report the error in the error token, skip the error token, and try
-        // again.
-        previous = token;
-        reportErrorToken(token);
-        token = token.next;
-      } while (token is ErrorToken);
-      return parsePrimary(previous, context);
-    } else {
-      return parseSend(token, context);
-    }
+    return parseSend(token, context);
   }
 
   Token parseParenthesizedExpressionOrFunctionLiteral(Token token) {
@@ -6356,32 +6348,20 @@
   void reportRecoverableError(Token token, Message message) {
     // Find a non-synthetic token on which to report the error.
     token = findNonZeroLengthToken(token);
-    if (token is ErrorToken) {
-      reportErrorToken(token);
-    } else {
-      listener.handleRecoverableError(message, token, token);
-    }
+    listener.handleRecoverableError(message, token, token);
   }
 
   void reportRecoverableErrorWithToken(
       Token token, Template<_MessageWithArgument<Token>> template) {
     // Find a non-synthetic token on which to report the error.
     token = findNonZeroLengthToken(token);
-    if (token is ErrorToken) {
-      reportErrorToken(token);
-    } else {
-      listener.handleRecoverableError(
-          template.withArguments(token), token, token);
-    }
-  }
-
-  void reportErrorToken(ErrorToken token) {
-    listener.handleErrorToken(token);
+    listener.handleRecoverableError(
+        template.withArguments(token), token, token);
   }
 
   Token reportAllErrorTokens(Token token) {
     while (token is ErrorToken) {
-      reportErrorToken(token);
+      listener.handleErrorToken(token);
       token = token.next;
     }
     return token;
diff --git a/pkg/front_end/lib/src/fasta/scanner/abstract_scanner.dart b/pkg/front_end/lib/src/fasta/scanner/abstract_scanner.dart
index e59d613..21646c3 100644
--- a/pkg/front_end/lib/src/fasta/scanner/abstract_scanner.dart
+++ b/pkg/front_end/lib/src/fasta/scanner/abstract_scanner.dart
@@ -564,8 +564,13 @@
       return select(
           $EQ, TokenType.QUESTION_QUESTION_EQ, TokenType.QUESTION_QUESTION);
     } else if (identical(next, $PERIOD)) {
+      next = advance();
+      if (_enableNonNullable && identical($OPEN_SQUARE_BRACKET, next)) {
+        appendBeginGroup(TokenType.QUESTION_PERIOD_OPEN_SQUARE_BRACKET);
+        return advance();
+      }
       appendPrecedenceToken(TokenType.QUESTION_PERIOD);
-      return advance();
+      return next;
     } else {
       appendPrecedenceToken(TokenType.QUESTION);
       return next;
@@ -1454,6 +1459,7 @@
     '{': TokenType.CLOSE_CURLY_BRACKET,
     '<': TokenType.GT,
     r'${': TokenType.CLOSE_CURLY_BRACKET,
+    '?.[': TokenType.CLOSE_SQUARE_BRACKET,
   }[begin.lexeme];
 }
 
diff --git a/pkg/front_end/lib/src/fasta/scanner/array_based_scanner.dart b/pkg/front_end/lib/src/fasta/scanner/array_based_scanner.dart
index 22df483..6eadbf2 100644
--- a/pkg/front_end/lib/src/fasta/scanner/array_based_scanner.dart
+++ b/pkg/front_end/lib/src/fasta/scanner/array_based_scanner.dart
@@ -16,6 +16,8 @@
         LT_TOKEN,
         OPEN_CURLY_BRACKET_TOKEN,
         OPEN_PAREN_TOKEN,
+        OPEN_SQUARE_BRACKET_TOKEN,
+        QUESTION_PERIOD_OPEN_SQUARE_BRACKET_TOKEN,
         STRING_INTERPOLATION_TOKEN;
 
 import 'characters.dart' show $LF, $STX;
@@ -148,7 +150,9 @@
     appendPrecedenceToken(type);
     Token close = tail;
     BeginToken begin = groupingStack.head;
-    if (!identical(begin.kind, openKind)) {
+    if (!identical(begin.kind, openKind) &&
+        !(begin.kind == QUESTION_PERIOD_OPEN_SQUARE_BRACKET_TOKEN &&
+            openKind == OPEN_SQUARE_BRACKET_TOKEN)) {
       assert(begin.kind == STRING_INTERPOLATION_TOKEN &&
           openKind == OPEN_CURLY_BRACKET_TOKEN);
       // We're ending an interpolated expression.
@@ -181,7 +185,9 @@
       BeginToken begin = groupingStack.head;
       if (openKind == begin.kind ||
           (openKind == OPEN_CURLY_BRACKET_TOKEN &&
-              begin.kind == STRING_INTERPOLATION_TOKEN)) {
+              begin.kind == STRING_INTERPOLATION_TOKEN) ||
+          (openKind == OPEN_SQUARE_BRACKET_TOKEN &&
+              begin.kind == QUESTION_PERIOD_OPEN_SQUARE_BRACKET_TOKEN)) {
         if (first) {
           // If the expected opener has been found on the first pass
           // then no recovery necessary.
diff --git a/pkg/front_end/lib/src/fasta/scanner/recover.dart b/pkg/front_end/lib/src/fasta/scanner/recover.dart
index 51b59d6..e9c33e8 100644
--- a/pkg/front_end/lib/src/fasta/scanner/recover.dart
+++ b/pkg/front_end/lib/src/fasta/scanner/recover.dart
@@ -242,6 +242,7 @@
     '{': '}',
     '<': '>',
     r'${': '}',
+    '?.[': ']',
   }[openBrace];
 }
 
diff --git a/pkg/front_end/lib/src/fasta/scanner/token_constants.dart b/pkg/front_end/lib/src/fasta/scanner/token_constants.dart
index fff742e..e468145 100644
--- a/pkg/front_end/lib/src/fasta/scanner/token_constants.dart
+++ b/pkg/front_end/lib/src/fasta/scanner/token_constants.dart
@@ -89,3 +89,4 @@
 const int GT_GT_GT_TOKEN = GENERIC_METHOD_TYPE_LIST_TOKEN + 1;
 const int PERIOD_PERIOD_PERIOD_QUESTION_TOKEN = GT_GT_GT_TOKEN + 1;
 const int GT_GT_GT_EQ_TOKEN = PERIOD_PERIOD_PERIOD_QUESTION_TOKEN + 1;
+const int QUESTION_PERIOD_OPEN_SQUARE_BRACKET_TOKEN = GT_GT_GT_EQ_TOKEN + 1;
diff --git a/pkg/front_end/lib/src/fasta/source/diet_listener.dart b/pkg/front_end/lib/src/fasta/source/diet_listener.dart
index cf3c27f..c556487 100644
--- a/pkg/front_end/lib/src/fasta/source/diet_listener.dart
+++ b/pkg/front_end/lib/src/fasta/source/diet_listener.dart
@@ -496,8 +496,14 @@
 
     Token metadata = pop();
     Library libraryNode = library.target;
-    LibraryPart part = libraryNode.parts[partDirectiveIndex++];
-    parseMetadata(library, metadata, part);
+    if (libraryNode.parts.length > partDirectiveIndex) {
+      // If partDirectiveIndex >= libraryNode.parts.length we are in a case of
+      // on part having other parts. An error has already been issued.
+      // Don't try to parse metadata into other parts that have nothing to do
+      // with the one this keyword is talking about.
+      LibraryPart part = libraryNode.parts[partDirectiveIndex++];
+      parseMetadata(library, metadata, part);
+    }
   }
 
   @override
diff --git a/pkg/front_end/lib/src/fasta/source/source_loader.dart b/pkg/front_end/lib/src/fasta/source/source_loader.dart
index c420708..3040e27 100644
--- a/pkg/front_end/lib/src/fasta/source/source_loader.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_loader.dart
@@ -289,7 +289,6 @@
         Token tokens = await tokenize(part);
         if (tokens != null) {
           listener.uri = part.fileUri;
-          listener.partDirectiveIndex = 0;
           parser.parseUnit(tokens);
         }
       }
diff --git a/pkg/front_end/lib/src/fasta/target_implementation.dart b/pkg/front_end/lib/src/fasta/target_implementation.dart
index 2ca6f77..586100d 100644
--- a/pkg/front_end/lib/src/fasta/target_implementation.dart
+++ b/pkg/front_end/lib/src/fasta/target_implementation.dart
@@ -52,7 +52,6 @@
   bool enableControlFlowCollections;
   bool enableExtensionMethods;
   bool enableNonNullable;
-  bool enableSetLiterals;
   bool enableSpreadCollections;
   bool enableTripleShift;
 
@@ -65,8 +64,6 @@
             .isExperimentEnabled(ExperimentalFlag.extensionMethods),
         enableNonNullable = CompilerContext.current.options
             .isExperimentEnabled(ExperimentalFlag.nonNullable),
-        enableSetLiterals = CompilerContext.current.options
-            .isExperimentEnabled(ExperimentalFlag.setLiterals),
         enableSpreadCollections = CompilerContext.current.options
             .isExperimentEnabled(ExperimentalFlag.spreadCollections),
         enableTripleShift = CompilerContext.current.options
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
index 05052a6..bbe6899 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
@@ -1611,6 +1611,7 @@
           actualMethodName,
           interfaceTarget,
           arguments,
+          helper.uri,
           fileOffset,
           inferred: getExplicitTypeArguments(arguments) == null);
     }
@@ -1957,9 +1958,9 @@
               mixinSuperclass.implementedTypes.length != 2)) {
         unexpected(
             'Compiler-generated mixin applications have a mixin or else '
-            'implement exactly one type',
+                'implement exactly one type',
             '$mixinSuperclass implements '
-            '${mixinSuperclass.implementedTypes.length} types',
+                '${mixinSuperclass.implementedTypes.length} types',
             mixinSuperclass.fileOffset,
             mixinSuperclass.fileUri);
       }
diff --git a/pkg/front_end/lib/src/scanner/errors.dart b/pkg/front_end/lib/src/scanner/errors.dart
index 4a7bb1d..1ca75e5 100644
--- a/pkg/front_end/lib/src/scanner/errors.dart
+++ b/pkg/front_end/lib/src/scanner/errors.dart
@@ -49,7 +49,7 @@
       const ScannerErrorCode(
           'UNEXPECTED_DOLLAR_IN_STRING',
           "A '\$' has special meaning inside a string, and must be followed by "
-          "an identifier or an expression in curly braces ({}).",
+              "an identifier or an expression in curly braces ({}).",
           correction: "Try adding a backslash (\\) to escape the '\$'.");
 
   /**
@@ -157,7 +157,8 @@
             type == TokenType.STRING_INTERPOLATION_EXPRESSION) {
           return _makeError(ScannerErrorCode.EXPECTED_TOKEN, ['}']);
         }
-        if (type == TokenType.OPEN_SQUARE_BRACKET) {
+        if (type == TokenType.OPEN_SQUARE_BRACKET ||
+            type == TokenType.QUESTION_PERIOD_OPEN_SQUARE_BRACKET) {
           return _makeError(ScannerErrorCode.EXPECTED_TOKEN, [']']);
         }
         if (type == TokenType.OPEN_PAREN) {
diff --git a/pkg/front_end/lib/src/scanner/token.dart b/pkg/front_end/lib/src/scanner/token.dart
index 5947f80..f7429c3 100644
--- a/pkg/front_end/lib/src/scanner/token.dart
+++ b/pkg/front_end/lib/src/scanner/token.dart
@@ -51,6 +51,7 @@
         type == TokenType.OPEN_CURLY_BRACKET ||
         type == TokenType.OPEN_PAREN ||
         type == TokenType.OPEN_SQUARE_BRACKET ||
+        type == TokenType.QUESTION_PERIOD_OPEN_SQUARE_BRACKET ||
         type == TokenType.STRING_INTERPOLATION_EXPRESSION);
   }
 
@@ -1443,6 +1444,12 @@
       NO_PRECEDENCE,
       PERIOD_PERIOD_PERIOD_QUESTION_TOKEN);
 
+  static const TokenType QUESTION_PERIOD_OPEN_SQUARE_BRACKET = const TokenType(
+      '?.[',
+      'QUESTION_PERIOD_OPEN_SQUARE_BRACKET',
+      SELECTOR_PRECEDENCE,
+      QUESTION_PERIOD_OPEN_SQUARE_BRACKET_TOKEN);
+
   static const TokenType AS = Keyword.AS;
 
   static const TokenType IS = Keyword.IS;
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index 0fccb0c..8f99626 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -3,6 +3,8 @@
 # BSD-style license that can be found in the LICENSE.md file.
 
 AbstractClassInstantiation/example: Fail
+AbstractClassMember/part_wrapped_script5: Fail
+AbstractClassMember/part_wrapped_script6: Fail
 AbstractClassMember/script5: Fail
 AbstractClassMember/script6: Fail
 AbstractNotSync/example: Fail
@@ -11,7 +13,9 @@
 AccessError/example: Fail
 AmbiguousSupertypes/example: Fail
 AnnotationOnEnumConstant/example: Fail
+AnonymousBreakTargetOutsideFunction/part_wrapped_statement: Fail
 AnonymousBreakTargetOutsideFunction/statement: Fail # Duplicated error as parser also complains.
+AnonymousContinueTargetOutsideFunction/part_wrapped_statement: Fail
 AnonymousContinueTargetOutsideFunction/statement: Fail # Duplicated error as parser also complains.
 ArgumentTypeNotAssignable/example: Fail
 AssertAsExpression/analyzerCode: Fail
@@ -39,10 +43,15 @@
 CantInferPackagesFromPackageUri/example: Fail
 CantInferTypeDueToCircularity/example: Fail
 CantInferTypeDueToInconsistentOverrides/example: Fail
+CantReadFile/part_wrapped_script: Fail # Importing file in the (now) part.
 CantUseControlFlowOrSpreadAsConstant/example: Fail
+CantUseDeferredPrefixAsConstant/part_wrapped_script: Fail # Importing file in the (now) part.
+CantUsePrefixAsExpression/part_wrapped_script: Fail # Importing file in the (now) part.
+CantUsePrefixWithNullAware/part_wrapped_script: Fail # Importing file in the (now) part.
 CantUseSuperBoundedTypeForInstanceCreation/analyzerCode: Fail
 CantUseSuperBoundedTypeForInstanceCreation/example: Fail
 ColonInPlaceOfIn/example: Fail
+ConflictingModifiers/part_wrapped_script1: Fail
 ConflictingModifiers/script1: Fail
 ConflictsWithConstructor/example: Fail
 ConflictsWithFactory/analyzerCode: Fail
@@ -54,6 +63,8 @@
 ConflictsWithTypeVariable/example: Fail
 ConstAndFinal/declaration3: Fail
 ConstAndFinal/declaration4: Fail
+ConstAndFinal/part_wrapped_declaration3: Fail
+ConstAndFinal/part_wrapped_declaration4: Fail
 ConstConstructorInSubclassOfMixinApplication/example: Fail
 ConstConstructorNonFinalField/example: Fail
 ConstConstructorRedirectionToNonConst/analyzerCode: Fail # The analyzer doesn't report this error.
@@ -98,15 +109,24 @@
 ConstructorNotFound/example: Fail
 ConstructorNotSync/example: Fail
 ContinueLabelNotTarget/example: Fail
+ContinueOutsideOfLoop/part_wrapped_script1: Fail
 ContinueOutsideOfLoop/script1: Fail
+ContinueWithoutLabelInCase/part_wrapped_script1: Fail
 ContinueWithoutLabelInCase/script1: Fail
 CouldNotParseUri/analyzerCode: Fail
 CouldNotParseUri/example: Fail
+CovariantAndStatic/part_wrapped_script1: Fail
+CovariantAndStatic/part_wrapped_script2: Fail
 CovariantAndStatic/script1: Fail
 CovariantAndStatic/script2: Fail
+CovariantMember/part_wrapped_script1: Fail
+CovariantMember/part_wrapped_script2: Fail
 CovariantMember/script1: Fail
 CovariantMember/script2: Fail
+CycleInTypeVariables/part_wrapped_script1: Fail
 CycleInTypeVariables/script1: Fail # We report an error for each type variable involved in the cycle.
+CyclicClassHierarchy/part_wrapped_script1: Fail
+CyclicClassHierarchy/part_wrapped_script2: Fail
 CyclicClassHierarchy/script1: Fail # We report an error for each class involved in the cycle.
 CyclicClassHierarchy/script2: Fail # We report an error for each class involved in the cycle.
 CyclicTypedef/example: Fail
@@ -115,17 +135,26 @@
 DeferredTypeAnnotation/example: Fail
 DillOutlineSummary/analyzerCode: Fail
 DillOutlineSummary/example: Fail
+DirectiveAfterDeclaration/part_wrapped_script1: Fail
+DirectiveAfterDeclaration/part_wrapped_script2: Fail
 DirectiveAfterDeclaration/script1: Fail
 DirectiveAfterDeclaration/script2: Fail
 DuplicateDeferred/example: Fail
 DuplicatePrefix/example: Fail
 DuplicatedDeclarationUse/analyzerCode: Fail # No corresponding analyzer code.
+DuplicatedDeclarationUse/part_wrapped_script1: Fail
+DuplicatedDeclarationUse/part_wrapped_script2: Fail
 DuplicatedDeclarationUse/script1: Fail # This test can't pass.
 DuplicatedDeclarationUse/script2: Fail # Wrong error.
 DuplicatedDefinition/analyzerCode: Fail
 DuplicatedDefinition/example: Fail
+DuplicatedExport/part_wrapped_script: Fail # Exporting file in the (now) part.
 DuplicatedExportInType/analyzerCode: Fail
 DuplicatedExportInType/example: Fail
+DuplicatedImportInType/part_wrapped_script: Fail # Importing file in the (now) part.
+DuplicatedLibraryExport/part_wrapped_script: Fail # Exporting file in the (now) part.
+DuplicatedLibraryImport/part_wrapped_script: Fail # Importing file in the (now) part.
+DuplicatedModifier/part_wrapped_script1: Fail
 DuplicatedModifier/script1: Fail
 DuplicatedName/example: Fail
 DuplicatedNamedArgument/example: Fail
@@ -133,10 +162,16 @@
 Encoding/analyzerCode: Fail
 EnumConstantSameNameAsEnclosing/example: Fail
 EnumInstantiation/example: Fail
+EqualityCannotBeEqualityOperand/part_wrapped_script1: Fail
+EqualityCannotBeEqualityOperand/part_wrapped_script2: Fail
 EqualityCannotBeEqualityOperand/script1: Fail
 EqualityCannotBeEqualityOperand/script2: Fail
+ExpectedBlock/part_wrapped_script: Fail
 ExpectedBlock/script: Fail
+ExpectedBlockToSkip/part_wrapped_script: Fail
 ExpectedBlockToSkip/script: Fail
+ExpectedButGot/part_wrapped_script1: Fail
+ExpectedButGot/part_wrapped_script2: Fail
 ExpectedButGot/script1: Fail
 ExpectedButGot/script2: Fail
 ExpectedClassMember/example: Fail
@@ -149,6 +184,7 @@
 ExpectedOneExpression/example: Fail
 ExpectedOpenParens/analyzerCode: Fail
 ExpectedOpenParens/example: Fail
+ExpectedStatement/part_wrapped_statement: Fail
 ExpectedStatement/statement: Fail
 ExpectedString/example: Fail
 ExpectedToken/example: Fail
@@ -156,15 +192,35 @@
 ExpectedUri/analyzerCode: Fail
 ExpectedUri/example: Fail
 ExperimentNotEnabled/example: Fail
+ExportAfterPart/part_wrapped_script1: Fail
 ExportAfterPart/script1: Fail
 ExpressionNotMetadata/analyzerCode: Fail
 ExpressionNotMetadata/example: Fail
 ExtendingEnum/example: Fail
 ExtendingRestricted/example: Fail
+ExternalConstructorWithBody/part_wrapped_script1: Fail
 ExternalConstructorWithBody/script1: Fail
 ExternalConstructorWithFieldInitializers/example: Fail
 ExternalFactoryRedirection/example: Fail
+ExternalFactoryWithBody/part_wrapped_script1: Fail
 ExternalFactoryWithBody/script1: Fail
+ExtraneousModifier/part_wrapped_script1: Fail
+ExtraneousModifier/part_wrapped_script2: Fail
+ExtraneousModifier/part_wrapped_script3: Fail
+ExtraneousModifier/part_wrapped_script4: Fail
+ExtraneousModifier/part_wrapped_script5: Fail
+ExtraneousModifier/part_wrapped_script7: Fail
+ExtraneousModifier/part_wrapped_script8: Fail
+ExtraneousModifier/part_wrapped_script9: Fail
+ExtraneousModifier/part_wrapped_script10: Fail
+ExtraneousModifier/part_wrapped_script11: Fail
+ExtraneousModifier/part_wrapped_script12: Fail
+ExtraneousModifier/part_wrapped_script13: Fail
+ExtraneousModifier/part_wrapped_script16: Fail
+ExtraneousModifier/part_wrapped_script17: Fail
+ExtraneousModifier/part_wrapped_script18: Fail
+ExtraneousModifier/part_wrapped_script19: Fail
+ExtraneousModifier/part_wrapped_script20: Fail
 ExtraneousModifier/script1: Fail
 ExtraneousModifier/script10: Fail
 ExtraneousModifier/script11: Fail
@@ -196,34 +252,50 @@
 FfiTypeInvalid/analyzerCode: Fail
 FfiTypeMismatch/analyzerCode: Fail
 FfiTypeUnsized/analyzerCode: Fail
+FieldInitializedOutsideDeclaringClass/part_wrapped_script1: Fail
 FieldInitializedOutsideDeclaringClass/script1: Fail
+FieldInitializerOutsideConstructor/part_wrapped_script1: Fail
 FieldInitializerOutsideConstructor/script1: Fail
+FinalAndCovariant/part_wrapped_script2: Fail
 FinalAndCovariant/script2: Fail
 FinalFieldWithoutInitializer/example: Fail
 FinalInstanceVariableAlreadyInitialized/example: Fail
 ForInLoopElementTypeNotAssignable/example: Fail
 ForInLoopExactlyOneVariable/analyzerCode: Fail # The analyzer doesn't recover well.
+ForInLoopExactlyOneVariable/part_wrapped_statement: Fail
 ForInLoopExactlyOneVariable/statement: Fail # Fasta reports too many errors.
 ForInLoopNotAssignable/analyzerCode: Fail # The analyzer reports a different error.
+ForInLoopNotAssignable/part_wrapped_statement: Fail
 ForInLoopNotAssignable/statement: Fail
 ForInLoopTypeNotIterable/example: Fail
 ForInLoopWithConstVariable/example: Fail
 FunctionTypeDefaultValue/example: Fail
+FunctionTypedParameterVar/part_wrapped_script1: Fail
 FunctionTypedParameterVar/script1: Fail
 GeneratorReturnsValue/example: Fail
 GetterNotFound/example: Fail
 GetterWithFormals/example: Fail
+IllegalAssignmentToNonAssignable/part_wrapped_script1: Fail
 IllegalAssignmentToNonAssignable/script1: Fail
 IllegalAsyncGeneratorVoidReturnType/analyzerCode: Fail # The analyzer doesn't report this error.
 IllegalMixin/example: Fail
 IllegalMixinDueToConstructors/example: Fail
+IllegalRecursiveType/analyzerCode: Fail
+IllegalRecursiveType/part_wrapped_script: Fail
+IllegalRecursiveType/script: Fail
 IllegalSyncGeneratorVoidReturnType/analyzerCode: Fail # The analyzer doesn't report this error.
+ImplementsBeforeExtends/part_wrapped_script: Fail
 ImplementsBeforeExtends/script: Fail
+ImplementsBeforeOn/part_wrapped_script: Fail
 ImplementsBeforeOn/script: Fail
+ImplementsBeforeWith/part_wrapped_script: Fail
 ImplementsBeforeWith/script: Fail
 ImplementsFutureOr/analyzerCode: Fail # The analyzer doesn't report this error.
+ImplementsFutureOr/part_wrapped_script1: Fail # Importing file in the (now) part.
 ImplicitCallOfNonMethod/example: Fail
+ImportAfterPart/part_wrapped_script1: Fail
 ImportAfterPart/script1: Fail
+IncompatibleRedirecteeFunctionType/part_wrapped_script6: Fail
 IncompatibleRedirecteeFunctionType/script6: Fail # Triggers multiple errors.
 InitializerForStaticField/example: Fail
 InitializerOutsideConstructor/example: Fail
@@ -252,8 +324,13 @@
 InvalidSuperInInitializer/example: Fail
 InvalidThisInInitializer/example: Fail
 InvalidUseOfNullAwareAccess/example: Fail
+InvalidVoid/part_wrapped_script1: Fail
+InvalidVoid/part_wrapped_script2: Fail
 InvalidVoid/script1: Fail
 InvalidVoid/script2: Fail
+LibraryDirectiveNotFirst/part_wrapped_script1: Fail # Defining library name in the (now) part.
+LibraryDirectiveNotFirst/part_wrapped_script2: Fail
+LibraryDirectiveNotFirst/part_wrapped_script3: Fail
 LibraryDirectiveNotFirst/script2: Fail
 LibraryDirectiveNotFirst/script3: Fail
 ListLiteralTooManyTypeArguments/example: Fail
@@ -265,19 +342,27 @@
 MethodNotFound/example: Fail
 MissingArgumentList/analyzerCode: Fail
 MissingArgumentList/example: Fail
+MissingAssignableSelector/part_wrapped_script1: Fail
 MissingAssignableSelector/script1: Fail
+MissingAssignmentInInitializer/part_wrapped_script1: Fail
 MissingAssignmentInInitializer/script1: Fail
 MissingInput/analyzerCode: Fail
 MissingInput/example: Fail
 MissingMain/analyzerCode: Fail
 MissingMain/example: Fail
+MissingPartOf/part_wrapped_script: Fail # Using 'part' in the (now) part.
 MissingPrefixInDeferredImport/example: Fail
 MixinInferenceNoMatchingClass/example: Fail
+ModifierOutOfOrder/part_wrapped_script1: Fail
 ModifierOutOfOrder/script1: Fail
+MultipleExtends/part_wrapped_script: Fail
 MultipleExtends/script: Fail
+MultipleImplements/part_wrapped_script: Fail
 MultipleImplements/script: Fail
 MultipleLibraryDirectives/example: Fail
+MultipleOnClauses/part_wrapped_script: Fail
 MultipleOnClauses/script: Fail
+MultipleWith/part_wrapped_script: Fail
 MultipleWith/script: Fail
 NamedFunctionExpression/example: Fail
 NativeClauseShouldBeAnnotation/example: Fail
@@ -286,10 +371,12 @@
 NoUnnamedConstructorInObject/analyzerCode: Fail
 NoUnnamedConstructorInObject/example: Fail
 NonAsciiIdentifier/expression: Fail
+NonAsciiIdentifier/part_wrapped_expression: Fail
 NonConstConstructor/example: Fail
 NonConstFactory/example: Fail
 NonInstanceTypeVariableUse/example: Fail
 NonNullAwareSpreadIsNull/analyzerCode: Fail # There's no analyzer code for that error yet.
+NonPartOfDirectiveInPart/part_wrapped_script1: Fail
 NonPartOfDirectiveInPart/script1: Fail
 NotAConstantExpression/example: Fail
 NotAType/example: Fail
@@ -313,10 +400,20 @@
 PackageNotFound/analyzerCode: Fail
 PackageNotFound/example: Fail
 PackagesFileFormat/analyzerCode: Fail # Analyzer crashes when .packages file has format error
+PartExport/part_wrapped_script: Fail # Exporting file in the (now) part.
+PartInPart/part_wrapped_script: Fail # Using 'part' in the (now) part.
+PartOfInLibrary/part_wrapped_script: Fail # Importing file in the (now) part.
 PartOfLibraryNameMismatch/example: Fail
+PartOfSelf/part_wrapped_script: Fail # Using 'part' in the (now) part.
+PartOfTwice/part_wrapped_script1: Fail # Using 'part' in the (now) part.
+PartOfTwice/part_wrapped_script2: Fail # Using 'part' in the (now) part.
+PartOfTwice/part_wrapped_script3: Fail # Using 'part' in the (now) part.
+PartOfTwoLibraries/part_wrapped_script: Fail # Defining library name in the (now) part.
 PartOfUriMismatch/example: Fail
 PartOfUseUri/example: Fail
 PartOrphan/analyzerCode: Fail # Analyzer can't handle this situation
+PartOrphan/part_wrapped_script: Fail # Already using 'part of' in the (now) part.
+PartTwice/part_wrapped_script: Fail # Using 'part' in the (now) part.
 PatchClassTypeVariablesMismatch/analyzerCode: Fail
 PatchClassTypeVariablesMismatch/example: Fail
 PatchDeclarationMismatch/analyzerCode: Fail
@@ -331,7 +428,9 @@
 PreviousUseOfName/analyzerCode: Fail
 PreviousUseOfName/example: Fail
 PrivateNamedParameter/example: Fail
+RedirectingConstructorWithBody/part_wrapped_script1: Fail
 RedirectingConstructorWithBody/script1: Fail
+RedirectionInNonFactory/part_wrapped_script1: Fail
 RedirectionInNonFactory/script1: Fail
 RedirectionTargetNotFound/example: Fail
 RethrowNotCatch/example: Fail
@@ -345,8 +444,6 @@
 SdkSummaryNotFound/example: Fail
 SetLiteralTooManyTypeArguments/analyzerCode: Fail
 SetLiteralTooManyTypeArguments/example: Fail
-SetLiteralsNotSupported/analyzerCode: Fail
-SetLiteralsNotSupported/example: Fail
 SetOrMapLiteralTooManyTypeArguments/analyzerCode: Fail
 SetOrMapLiteralTooManyTypeArguments/example: Fail
 SetterNotFound/example: Fail
@@ -381,6 +478,7 @@
 TooManyArguments/example: Fail
 TypeAfterVar/example: Fail
 TypeArgumentMismatch/example: Fail
+TypeArgumentsOnTypeVariable/part_wrapped_script1: Fail
 TypeArgumentsOnTypeVariable/script1: Fail
 TypeNotFound/example: Fail
 TypeVariableDuplicatedName/example: Fail
@@ -388,13 +486,26 @@
 TypeVariableInStaticContext/declaration2: Fail # Unfortunate message from outline phase.
 TypeVariableInStaticContext/declaration3: Fail # Unfortunate message from outline phase.
 TypeVariableInStaticContext/declaration4: Fail # Unfortunate message from outline phase.
+TypeVariableInStaticContext/part_wrapped_declaration1: Fail
+TypeVariableInStaticContext/part_wrapped_declaration2: Fail
+TypeVariableInStaticContext/part_wrapped_declaration3: Fail
+TypeVariableInStaticContext/part_wrapped_declaration4: Fail
 TypeVariableSameNameAsEnclosing/example: Fail
 TypedefNotFunction/example: Fail
+UnexpectedToken/part_wrapped_script1: Fail
 UnexpectedToken/script1: Fail
+UnmatchedToken/part_wrapped_script1: Fail
+UnmatchedToken/part_wrapped_script3: Fail
 UnmatchedToken/script1: Fail
 UnmatchedToken/script3: Fail
 Unspecified/analyzerCode: Fail
 Unspecified/example: Fail
+UnterminatedString/part_wrapped_script2: Fail
+UnterminatedString/part_wrapped_script4: Fail
+UnterminatedString/part_wrapped_script5: Fail
+UnterminatedString/part_wrapped_script6: Fail
+UnterminatedString/part_wrapped_script7: Fail
+UnterminatedString/part_wrapped_script8: Fail
 UnterminatedString/script2: Fail
 UnterminatedString/script4: Fail
 UnterminatedString/script5: Fail
@@ -403,6 +514,8 @@
 UnterminatedString/script8: Fail
 UnterminatedToken/analyzerCode: Fail
 UnterminatedToken/example: Fail
+UntranslatableUri/part_wrapped_script: Fail # Importing file in the (now) part.
+VarAsTypeName/part_wrapped_script1: Fail
 VarAsTypeName/script1: Fail # Too many problems
 WebLiteralCannotBeRepresentedExactly/analyzerCode: Fail
 WebLiteralCannotBeRepresentedExactly/example: Fail
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index 8f1b921..d2b0dac 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -1438,9 +1438,6 @@
   template: "A set or map literal requires exactly one or two type arguments, respectively."
   severity: ERROR_LEGACY_WARNING
 
-SetLiteralsNotSupported:
-  template: "Set literals are not supported yet."
-
 LoadLibraryTakesNoArguments:
   template: "'loadLibrary' takes no arguments."
   severity: ERROR_LEGACY_WARNING
@@ -3563,3 +3560,9 @@
   script: >
     class A<X> {}
     class B<Y> extends A<Function(Y)> {}
+
+IllegalRecursiveType:
+  template: "Illegal recursive type '#type'."
+  script: >
+    class Base<T> {}
+    class Derived<T> extends Base<Derived<Derived<T>>> {}
diff --git a/pkg/front_end/test/desugar_test.dart b/pkg/front_end/test/desugar_test.dart
new file mode 100644
index 0000000..9ec4d38
--- /dev/null
+++ b/pkg/front_end/test/desugar_test.dart
@@ -0,0 +1,82 @@
+// Copyright (c) 2019, 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.
+
+/// Test to ensure that desugaring APIs used by clients like dart2js are
+/// always up to date.
+///
+/// Desugaring APIs are methods that inspect the kernel output to find
+/// structures that are assumed to always be generated by the CFE. If the CFE
+/// changes its invariants, the desugaring APIs will change together to hide
+/// these changes from clients.
+
+import 'dart:io';
+
+import 'package:async_helper/async_helper.dart';
+import 'package:expect/expect.dart';
+import 'package:front_end/src/api_unstable/dart2js.dart' as api;
+import 'package:front_end/src/compute_platform_binaries_location.dart';
+import 'package:front_end/src/fasta/kernel/utils.dart' show serializeComponent;
+import 'package:front_end/src/testing/compiler_common.dart';
+import 'package:kernel/ast.dart' as ir;
+import 'package:kernel/binary/ast_from_binary.dart' show BinaryBuilder;
+
+main() async {
+  await asyncTest(() async {
+    await testRedirectingFactoryDirect();
+    await testRedirectingFactorySerialized();
+    await testRedirectingFactoryPatchFile();
+  });
+}
+
+testRedirectingFactoryDirect() async {
+  var component = await compileUnit(['a.dart'], {'a.dart': aSource});
+  checkIsRedirectingFactory(component, 'a.dart', 'A', 'foo');
+  checkIsRedirectingFactory(component, 'core', 'Uri', 'file');
+}
+
+testRedirectingFactorySerialized() async {
+  var component = await compileUnit(['a.dart'], {'a.dart': aSource});
+  var bytes = serializeComponent(component);
+  component = new ir.Component();
+  new BinaryBuilder(bytes).readComponent(component);
+  checkIsRedirectingFactory(component, 'a.dart', 'A', 'foo');
+  checkIsRedirectingFactory(component, 'core', 'Uri', 'file');
+}
+
+// regression test: redirecting factories from patch files don't have the
+// redirecting-factory flag stored in kernel.
+testRedirectingFactoryPatchFile() async {
+  var componentUri =
+      computePlatformBinariesLocation().resolve('dart2js_platform.dill');
+  var component = new ir.Component();
+  new BinaryBuilder(new File.fromUri(componentUri).readAsBytesSync())
+      .readComponent(component);
+  checkIsRedirectingFactory(component, 'collection', 'HashMap', 'identity',
+      isPatch: true);
+}
+
+void checkIsRedirectingFactory(ir.Component component, String uriPath,
+    String className, String constructorName,
+    {bool isPatch: false}) {
+  var lib =
+      component.libraries.firstWhere((l) => l.importUri.path.endsWith(uriPath));
+  var cls = lib.classes.firstWhere((c) => c.name == className);
+  ir.Procedure member =
+      cls.members.firstWhere((m) => m.name.name == constructorName);
+  Expect.isTrue(
+      member.kind == ir.ProcedureKind.Factory, "$member is not a factory");
+  Expect.isTrue(api.isRedirectingFactory(member));
+  // TODO: this should always be true. Issue #33495
+  Expect.equals(!isPatch, member.isRedirectingFactoryConstructor);
+}
+
+const aSource = '''
+class A {
+  factory A.foo(int x) = _B;
+}
+
+class _B implements A {
+  _B(int x);
+}
+''';
diff --git a/pkg/front_end/test/fasta/messages_test.dart b/pkg/front_end/test/fasta/messages_test.dart
index de25e18..f0141c1 100644
--- a/pkg/front_end/test/fasta/messages_test.dart
+++ b/pkg/front_end/test/fasta/messages_test.dart
@@ -213,6 +213,11 @@
       for (Example example in examples) {
         yield createDescription(example.name, example, null);
       }
+      // "Wrap" example as a part.
+      for (Example example in examples) {
+        yield createDescription("part_wrapped_${example.name}",
+            new PartWrapExample("part_wrapped_${example.name}", name, example), null);
+      }
 
       yield createDescription(
           "knownKeys",
@@ -312,8 +317,10 @@
   Uint8List get bytes;
 
   Map<String, Uint8List> get scripts {
-    return {"main.dart": bytes};
+    return {mainFilename: bytes};
   }
+
+  String get mainFilename => "main.dart";
 }
 
 class BytesExample extends Example {
@@ -418,11 +425,57 @@
       });
       return scriptFiles;
     } else {
-      return {"main.dart": new Uint8List.fromList(utf8.encode(script))};
+      return {mainFilename: new Uint8List.fromList(utf8.encode(script))};
     }
   }
 }
 
+class PartWrapExample extends Example {
+  final Example example;
+  PartWrapExample(String name, String code, this.example) : super(name, code);
+
+  @override
+  Uint8List get bytes => throw "Unsupported: PartWrapExample.bytes";
+
+  @override
+  String get mainFilename => "main_wrapped.dart";
+
+  @override
+  Map<String, Uint8List> get scripts {
+    Map<String, Uint8List> wrapped = example.scripts;
+
+    var scriptFiles = <String, Uint8List>{};
+    scriptFiles.addAll(wrapped);
+
+    // Create a new main file
+    // TODO: Technically we should find a un-used name.
+    if (scriptFiles.containsKey(mainFilename)) {
+      throw "Framework failure: "
+          "Wanted to create wrapper file, but the file alread exists!";
+    }
+    scriptFiles[mainFilename] = new Uint8List.fromList(utf8.encode("""
+      part "${example.mainFilename}";
+    """));
+
+    // Modify the original main file to be part of the wrapper and add lots of
+    // gunk so every actual position in the file is not a valid position in the
+    // wrapper.
+    scriptFiles[example.mainFilename] = new Uint8List.fromList(utf8.encode("""
+      part of "${mainFilename}";
+      // La la la la la la la la la la la la la.
+      // La la la la la la la la la la la la la.
+      // La la la la la la la la la la la la la.
+      // La la la la la la la la la la la la la.
+      // La la la la la la la la la la la la la.
+    """) + scriptFiles[example.mainFilename]);
+
+    return scriptFiles;
+  }
+
+  @override
+  YamlNode get node => example.node;
+}
+
 class Validate extends Step<MessageTestDescription, Example, MessageTestSuite> {
   const Validate();
 
@@ -450,7 +503,8 @@
       Uri uri = suite.fileSystem.currentDirectory.resolve("$dir/$fileName");
       suite.fileSystem.entityForUri(uri).writeAsBytesSync(bytes);
     });
-    Uri main = suite.fileSystem.currentDirectory.resolve("$dir/main.dart");
+    Uri main = suite.fileSystem.currentDirectory
+        .resolve("$dir/${example.mainFilename}");
     Uri output =
         suite.fileSystem.currentDirectory.resolve("$dir/main.dart.dill");
 
diff --git a/pkg/front_end/test/fasta/testing/suite.dart b/pkg/front_end/test/fasta/testing/suite.dart
index cc77877..c183abd 100644
--- a/pkg/front_end/test/fasta/testing/suite.dart
+++ b/pkg/front_end/test/fasta/testing/suite.dart
@@ -132,7 +132,6 @@
   final bool legacyMode;
   final bool onlyCrashes;
   final bool enableControlFlowCollections;
-  final bool enableSetLiterals;
   final bool enableSpreadCollections;
   final bool skipVm;
   final Map<Component, KernelTarget> componentToTarget =
@@ -158,7 +157,6 @@
       this.platformBinaries,
       this.onlyCrashes,
       this.enableControlFlowCollections,
-      this.enableSetLiterals,
       this.enableSpreadCollections,
       bool ignoreExpectations,
       this.updateExpectations,
@@ -250,7 +248,6 @@
     bool legacyMode = environment.containsKey(LEGACY_MODE);
     bool enableControlFlowCollections =
         environment["enableControlFlowCollections"] != "false" && !legacyMode;
-    bool enableSetLiterals = environment["enableSetLiterals"] != "false";
     bool enableSpreadCollections =
         environment["enableSpreadCollections"] != "false" && !legacyMode;
     var options = new ProcessedOptions(
@@ -263,7 +260,6 @@
           ..experimentalFlags = <ExperimentalFlag, bool>{
             ExperimentalFlag.controlFlowCollections:
                 enableControlFlowCollections,
-            ExperimentalFlag.setLiterals: enableSetLiterals,
             ExperimentalFlag.spreadCollections: enableSpreadCollections,
           });
     UriTranslator uriTranslator = await options.getUriTranslator();
@@ -286,7 +282,6 @@
             : Uri.base.resolve(platformBinaries),
         onlyCrashes,
         enableControlFlowCollections,
-        enableSetLiterals,
         enableSpreadCollections,
         ignoreExpectations,
         updateExpectations,
@@ -356,7 +351,6 @@
           ..experimentalFlags = <ExperimentalFlag, bool>{
             ExperimentalFlag.controlFlowCollections:
                 context.enableControlFlowCollections,
-            ExperimentalFlag.setLiterals: context.enableSetLiterals,
             ExperimentalFlag.spreadCollections: context.enableSpreadCollections,
           },
         inputs: <Uri>[description.uri]);
diff --git a/pkg/front_end/test/precedence_info_test.dart b/pkg/front_end/test/precedence_info_test.dart
index 169f67c..80fd8a4 100644
--- a/pkg/front_end/test/precedence_info_test.dart
+++ b/pkg/front_end/test/precedence_info_test.dart
@@ -2,9 +2,9 @@
 // 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 'package:front_end/src/fasta/scanner.dart';
 import 'package:front_end/src/fasta/scanner/abstract_scanner.dart'
     show AbstractScanner;
-import 'package:front_end/src/fasta/scanner/string_scanner.dart';
 import 'package:front_end/src/fasta/scanner/token.dart' as fasta;
 import 'package:front_end/src/scanner/token.dart';
 import 'package:test/test.dart';
@@ -22,8 +22,10 @@
   void assertInfo(check(String source, Token token)) {
     void assertLexeme(String source) {
       if (source == null || source.isEmpty) return;
-      var scanner = new StringScanner(source, includeComments: true);
-      var token = scanner.tokenize();
+      var token = scanString(source, includeComments: true).tokens;
+      while (token is ErrorToken) {
+        token = token.next;
+      }
       check(source, token);
     }
 
@@ -288,9 +290,8 @@
   void test_name() {
     void assertName(String source, String name, {int offset: 0}) {
       if (source == null || source.isEmpty) return;
-      var scanner = new StringScanner(source, includeComments: true);
-      var token = scanner.tokenize();
-      while (token.offset < offset) {
+      var token = scanString(source, includeComments: true).tokens;
+      while (token is ErrorToken || token.offset < offset) {
         token = token.next;
       }
       expect(token.type.name, name,
@@ -388,8 +389,10 @@
     };
     precedenceTable.forEach((precedence, lexemes) {
       for (String source in lexemes) {
-        var scanner = new StringScanner(source, includeComments: true);
-        var token = scanner.tokenize();
+        var token = scanString(source, includeComments: true).tokens;
+        while (token is ErrorToken) {
+          token = token.next;
+        }
         expect(token.type.precedence, precedence, reason: source);
       }
     });
@@ -397,8 +400,7 @@
 
   void test_type() {
     void assertLexeme(String source, TokenType tt) {
-      var scanner = new StringScanner(source, includeComments: true);
-      var token = scanner.tokenize();
+      var token = scanString(source, includeComments: true).tokens;
       expect(token.type, same(tt), reason: source);
     }
 
diff --git a/pkg/front_end/test/token_test.dart b/pkg/front_end/test/token_test.dart
index fa62c06..dda8af0 100644
--- a/pkg/front_end/test/token_test.dart
+++ b/pkg/front_end/test/token_test.dart
@@ -2,8 +2,8 @@
 // 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 'package:front_end/src/fasta/scanner.dart' show ScannerConfiguration;
-import 'package:front_end/src/fasta/scanner/string_scanner.dart';
+import 'package:front_end/src/fasta/scanner.dart'
+    show ScannerConfiguration, scanString;
 import 'package:front_end/src/scanner/token.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -32,8 +32,7 @@
   }
 }
 ''';
-    var scanner = new StringScanner(source, includeComments: true);
-    Token token = scanner.tokenize();
+    Token token = scanString(source, includeComments: true).tokens;
 
     Token nextComment() {
       while (!token.isEof) {
@@ -66,8 +65,7 @@
   }
 
   void test_isSynthetic() {
-    var scanner = new StringScanner('/* 1 */ foo', includeComments: true);
-    var token = scanner.tokenize();
+    var token = scanString('/* 1 */ foo', includeComments: true).tokens;
     expect(token.isSynthetic, false);
     expect(token.precedingComments.isSynthetic, false);
     expect(token.previous.isSynthetic, true);
@@ -76,8 +74,7 @@
   }
 
   void test_matchesAny() {
-    var scanner = new StringScanner('true', includeComments: true);
-    var token = scanner.tokenize();
+    var token = scanString('true', includeComments: true).tokens;
     expect(token.matchesAny([Keyword.TRUE]), true);
     expect(token.matchesAny([TokenType.AMPERSAND, Keyword.TRUE]), true);
     expect(token.matchesAny([TokenType.AMPERSAND]), false);
@@ -127,10 +124,10 @@
     ]);
     for (Keyword keyword in Keyword.values) {
       var isModifier = modifierKeywords.contains(keyword);
-      var scanner = new StringScanner(keyword.lexeme,
-          configuration: ScannerConfiguration.nonNullable,
-          includeComments: true);
-      Token token = scanner.tokenize();
+      Token token = scanString(keyword.lexeme,
+              configuration: ScannerConfiguration.nonNullable,
+              includeComments: true)
+          .tokens;
       expect(token.isModifier, isModifier, reason: keyword.name);
       if (isModifier) {
         expect(token.isTopLevelKeyword, isFalse, reason: keyword.name);
@@ -152,8 +149,7 @@
     ]);
     for (Keyword keyword in Keyword.values) {
       var isTopLevelKeyword = topLevelKeywords.contains(keyword);
-      var scanner = new StringScanner(keyword.lexeme, includeComments: true);
-      Token token = scanner.tokenize();
+      Token token = scanString(keyword.lexeme, includeComments: true).tokens;
       expect(token.isTopLevelKeyword, isTopLevelKeyword, reason: keyword.name);
       if (isTopLevelKeyword) {
         expect(token.isModifier, isFalse, reason: keyword.name);
@@ -191,8 +187,7 @@
   }
 
   void test_value() {
-    var scanner = new StringScanner('true & "home"', includeComments: true);
-    var token = scanner.tokenize();
+    var token = scanString('true & "home"', includeComments: true).tokens;
     // Keywords
     expect(token.lexeme, 'true');
     expect(token.value(), Keyword.TRUE);
diff --git a/pkg/front_end/testcases/having_part_with_part_and_annotation.dart b/pkg/front_end/testcases/having_part_with_part_and_annotation.dart
new file mode 100644
index 0000000..9e3a783
--- /dev/null
+++ b/pkg/front_end/testcases/having_part_with_part_and_annotation.dart
@@ -0,0 +1,8 @@
+@Foo
+part 'having_part_with_part_and_annotation_lib1.dart';
+
+const int Foo = 42;
+
+void fromMain() {}
+
+main() {}
\ No newline at end of file
diff --git a/pkg/front_end/testcases/having_part_with_part_and_annotation.dart.legacy.expect b/pkg/front_end/testcases/having_part_with_part_and_annotation.dart.legacy.expect
new file mode 100644
index 0000000..929cc78
--- /dev/null
+++ b/pkg/front_end/testcases/having_part_with_part_and_annotation.dart.legacy.expect
@@ -0,0 +1,20 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/having_part_with_part_and_annotation_lib1.dart:4:6: Error: A file that's a part of a library can't have parts itself.
+// Try moving the 'part' declaration to the containing library.
+// part 'having_part_with_part_and_annotation_lib2.dart';
+//      ^
+// pkg/front_end/testcases/having_part_with_part_and_annotation.dart: Context: This is the containing library.
+//
+import self as self;
+import "dart:core" as core;
+
+@self::Foo
+part having_part_with_part_and_annotation_lib1.dart;
+static const field core::int Foo = 42;
+static const field core::int Bar = 43 /* from org-dartlang-testcase:///having_part_with_part_and_annotation_lib1.dart */;
+static method fromMain() → void {}
+static method main() → dynamic {}
+static method /* from org-dartlang-testcase:///having_part_with_part_and_annotation_lib1.dart */ fromLib1() → void {}
diff --git a/pkg/front_end/testcases/having_part_with_part_and_annotation.dart.legacy.transformed.expect b/pkg/front_end/testcases/having_part_with_part_and_annotation.dart.legacy.transformed.expect
new file mode 100644
index 0000000..929cc78
--- /dev/null
+++ b/pkg/front_end/testcases/having_part_with_part_and_annotation.dart.legacy.transformed.expect
@@ -0,0 +1,20 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/having_part_with_part_and_annotation_lib1.dart:4:6: Error: A file that's a part of a library can't have parts itself.
+// Try moving the 'part' declaration to the containing library.
+// part 'having_part_with_part_and_annotation_lib2.dart';
+//      ^
+// pkg/front_end/testcases/having_part_with_part_and_annotation.dart: Context: This is the containing library.
+//
+import self as self;
+import "dart:core" as core;
+
+@self::Foo
+part having_part_with_part_and_annotation_lib1.dart;
+static const field core::int Foo = 42;
+static const field core::int Bar = 43 /* from org-dartlang-testcase:///having_part_with_part_and_annotation_lib1.dart */;
+static method fromMain() → void {}
+static method main() → dynamic {}
+static method /* from org-dartlang-testcase:///having_part_with_part_and_annotation_lib1.dart */ fromLib1() → void {}
diff --git a/pkg/front_end/testcases/having_part_with_part_and_annotation.dart.outline.expect b/pkg/front_end/testcases/having_part_with_part_and_annotation.dart.outline.expect
new file mode 100644
index 0000000..6043aa4
--- /dev/null
+++ b/pkg/front_end/testcases/having_part_with_part_and_annotation.dart.outline.expect
@@ -0,0 +1,22 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/having_part_with_part_and_annotation_lib1.dart:4:6: Error: A file that's a part of a library can't have parts itself.
+// Try moving the 'part' declaration to the containing library.
+// part 'having_part_with_part_and_annotation_lib2.dart';
+//      ^
+// pkg/front_end/testcases/having_part_with_part_and_annotation.dart: Context: This is the containing library.
+//
+import self as self;
+import "dart:core" as core;
+
+part having_part_with_part_and_annotation_lib1.dart;
+static const field core::int Foo = 42;
+static const field core::int Bar = 43 /* from org-dartlang-testcase:///having_part_with_part_and_annotation_lib1.dart */;
+static method fromMain() → void
+  ;
+static method main() → dynamic
+  ;
+static method /* from org-dartlang-testcase:///having_part_with_part_and_annotation_lib1.dart */ fromLib1() → void
+  ;
diff --git a/pkg/front_end/testcases/having_part_with_part_and_annotation.dart.strong.expect b/pkg/front_end/testcases/having_part_with_part_and_annotation.dart.strong.expect
new file mode 100644
index 0000000..929cc78
--- /dev/null
+++ b/pkg/front_end/testcases/having_part_with_part_and_annotation.dart.strong.expect
@@ -0,0 +1,20 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/having_part_with_part_and_annotation_lib1.dart:4:6: Error: A file that's a part of a library can't have parts itself.
+// Try moving the 'part' declaration to the containing library.
+// part 'having_part_with_part_and_annotation_lib2.dart';
+//      ^
+// pkg/front_end/testcases/having_part_with_part_and_annotation.dart: Context: This is the containing library.
+//
+import self as self;
+import "dart:core" as core;
+
+@self::Foo
+part having_part_with_part_and_annotation_lib1.dart;
+static const field core::int Foo = 42;
+static const field core::int Bar = 43 /* from org-dartlang-testcase:///having_part_with_part_and_annotation_lib1.dart */;
+static method fromMain() → void {}
+static method main() → dynamic {}
+static method /* from org-dartlang-testcase:///having_part_with_part_and_annotation_lib1.dart */ fromLib1() → void {}
diff --git a/pkg/front_end/testcases/having_part_with_part_and_annotation.dart.strong.transformed.expect b/pkg/front_end/testcases/having_part_with_part_and_annotation.dart.strong.transformed.expect
new file mode 100644
index 0000000..929cc78
--- /dev/null
+++ b/pkg/front_end/testcases/having_part_with_part_and_annotation.dart.strong.transformed.expect
@@ -0,0 +1,20 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/having_part_with_part_and_annotation_lib1.dart:4:6: Error: A file that's a part of a library can't have parts itself.
+// Try moving the 'part' declaration to the containing library.
+// part 'having_part_with_part_and_annotation_lib2.dart';
+//      ^
+// pkg/front_end/testcases/having_part_with_part_and_annotation.dart: Context: This is the containing library.
+//
+import self as self;
+import "dart:core" as core;
+
+@self::Foo
+part having_part_with_part_and_annotation_lib1.dart;
+static const field core::int Foo = 42;
+static const field core::int Bar = 43 /* from org-dartlang-testcase:///having_part_with_part_and_annotation_lib1.dart */;
+static method fromMain() → void {}
+static method main() → dynamic {}
+static method /* from org-dartlang-testcase:///having_part_with_part_and_annotation_lib1.dart */ fromLib1() → void {}
diff --git a/pkg/front_end/testcases/having_part_with_part_and_annotation_lib1.dart b/pkg/front_end/testcases/having_part_with_part_and_annotation_lib1.dart
new file mode 100644
index 0000000..b2bb2d8
--- /dev/null
+++ b/pkg/front_end/testcases/having_part_with_part_and_annotation_lib1.dart
@@ -0,0 +1,8 @@
+part of 'having_part_with_part_and_annotation.dart';
+
+@Bar
+part 'having_part_with_part_and_annotation_lib2.dart';
+
+const int Bar = 43;
+
+void fromLib1() {}
diff --git a/pkg/front_end/testcases/having_part_with_part_and_annotation_lib2.dart b/pkg/front_end/testcases/having_part_with_part_and_annotation_lib2.dart
new file mode 100644
index 0000000..5614f61
--- /dev/null
+++ b/pkg/front_end/testcases/having_part_with_part_and_annotation_lib2.dart
@@ -0,0 +1,3 @@
+part of 'having_part_with_part_and_annotation_lib1.dart';
+
+void fromLib2() {}
diff --git a/pkg/front_end/testcases/having_part_with_parts_and_annotation.dart b/pkg/front_end/testcases/having_part_with_parts_and_annotation.dart
new file mode 100644
index 0000000..46ba730
--- /dev/null
+++ b/pkg/front_end/testcases/having_part_with_parts_and_annotation.dart
@@ -0,0 +1,8 @@
+@Foo
+part 'having_part_with_parts_and_annotation_lib1.dart';
+
+const int Foo = 42;
+
+void fromMain() {}
+
+main() {}
\ No newline at end of file
diff --git a/pkg/front_end/testcases/having_part_with_parts_and_annotation.dart.legacy.expect b/pkg/front_end/testcases/having_part_with_parts_and_annotation.dart.legacy.expect
new file mode 100644
index 0000000..a33aba1
--- /dev/null
+++ b/pkg/front_end/testcases/having_part_with_parts_and_annotation.dart.legacy.expect
@@ -0,0 +1,27 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/having_part_with_parts_and_annotation_lib1.dart:4:6: Error: A file that's a part of a library can't have parts itself.
+// Try moving the 'part' declaration to the containing library.
+// part 'having_part_with_parts_and_annotation_lib2.dart';
+//      ^
+// pkg/front_end/testcases/having_part_with_parts_and_annotation.dart: Context: This is the containing library.
+//
+// pkg/front_end/testcases/having_part_with_parts_and_annotation_lib1.dart:6:6: Error: A file that's a part of a library can't have parts itself.
+// Try moving the 'part' declaration to the containing library.
+// part 'having_part_with_parts_and_annotation_lib2.dart';
+//      ^
+// pkg/front_end/testcases/having_part_with_parts_and_annotation.dart: Context: This is the containing library.
+//
+import self as self;
+import "dart:core" as core;
+
+@self::Foo
+part having_part_with_parts_and_annotation_lib1.dart;
+static const field core::int Foo = 42;
+static const field core::int Bar = 43 /* from org-dartlang-testcase:///having_part_with_parts_and_annotation_lib1.dart */;
+static const field core::int Baz = 44 /* from org-dartlang-testcase:///having_part_with_parts_and_annotation_lib1.dart */;
+static method fromMain() → void {}
+static method main() → dynamic {}
+static method /* from org-dartlang-testcase:///having_part_with_parts_and_annotation_lib1.dart */ fromLib1() → void {}
diff --git a/pkg/front_end/testcases/having_part_with_parts_and_annotation.dart.legacy.transformed.expect b/pkg/front_end/testcases/having_part_with_parts_and_annotation.dart.legacy.transformed.expect
new file mode 100644
index 0000000..a33aba1
--- /dev/null
+++ b/pkg/front_end/testcases/having_part_with_parts_and_annotation.dart.legacy.transformed.expect
@@ -0,0 +1,27 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/having_part_with_parts_and_annotation_lib1.dart:4:6: Error: A file that's a part of a library can't have parts itself.
+// Try moving the 'part' declaration to the containing library.
+// part 'having_part_with_parts_and_annotation_lib2.dart';
+//      ^
+// pkg/front_end/testcases/having_part_with_parts_and_annotation.dart: Context: This is the containing library.
+//
+// pkg/front_end/testcases/having_part_with_parts_and_annotation_lib1.dart:6:6: Error: A file that's a part of a library can't have parts itself.
+// Try moving the 'part' declaration to the containing library.
+// part 'having_part_with_parts_and_annotation_lib2.dart';
+//      ^
+// pkg/front_end/testcases/having_part_with_parts_and_annotation.dart: Context: This is the containing library.
+//
+import self as self;
+import "dart:core" as core;
+
+@self::Foo
+part having_part_with_parts_and_annotation_lib1.dart;
+static const field core::int Foo = 42;
+static const field core::int Bar = 43 /* from org-dartlang-testcase:///having_part_with_parts_and_annotation_lib1.dart */;
+static const field core::int Baz = 44 /* from org-dartlang-testcase:///having_part_with_parts_and_annotation_lib1.dart */;
+static method fromMain() → void {}
+static method main() → dynamic {}
+static method /* from org-dartlang-testcase:///having_part_with_parts_and_annotation_lib1.dart */ fromLib1() → void {}
diff --git a/pkg/front_end/testcases/having_part_with_parts_and_annotation.dart.outline.expect b/pkg/front_end/testcases/having_part_with_parts_and_annotation.dart.outline.expect
new file mode 100644
index 0000000..b6959ee
--- /dev/null
+++ b/pkg/front_end/testcases/having_part_with_parts_and_annotation.dart.outline.expect
@@ -0,0 +1,29 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/having_part_with_parts_and_annotation_lib1.dart:4:6: Error: A file that's a part of a library can't have parts itself.
+// Try moving the 'part' declaration to the containing library.
+// part 'having_part_with_parts_and_annotation_lib2.dart';
+//      ^
+// pkg/front_end/testcases/having_part_with_parts_and_annotation.dart: Context: This is the containing library.
+//
+// pkg/front_end/testcases/having_part_with_parts_and_annotation_lib1.dart:6:6: Error: A file that's a part of a library can't have parts itself.
+// Try moving the 'part' declaration to the containing library.
+// part 'having_part_with_parts_and_annotation_lib2.dart';
+//      ^
+// pkg/front_end/testcases/having_part_with_parts_and_annotation.dart: Context: This is the containing library.
+//
+import self as self;
+import "dart:core" as core;
+
+part having_part_with_parts_and_annotation_lib1.dart;
+static const field core::int Foo = 42;
+static const field core::int Bar = 43 /* from org-dartlang-testcase:///having_part_with_parts_and_annotation_lib1.dart */;
+static const field core::int Baz = 44 /* from org-dartlang-testcase:///having_part_with_parts_and_annotation_lib1.dart */;
+static method fromMain() → void
+  ;
+static method main() → dynamic
+  ;
+static method /* from org-dartlang-testcase:///having_part_with_parts_and_annotation_lib1.dart */ fromLib1() → void
+  ;
diff --git a/pkg/front_end/testcases/having_part_with_parts_and_annotation.dart.strong.expect b/pkg/front_end/testcases/having_part_with_parts_and_annotation.dart.strong.expect
new file mode 100644
index 0000000..a33aba1
--- /dev/null
+++ b/pkg/front_end/testcases/having_part_with_parts_and_annotation.dart.strong.expect
@@ -0,0 +1,27 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/having_part_with_parts_and_annotation_lib1.dart:4:6: Error: A file that's a part of a library can't have parts itself.
+// Try moving the 'part' declaration to the containing library.
+// part 'having_part_with_parts_and_annotation_lib2.dart';
+//      ^
+// pkg/front_end/testcases/having_part_with_parts_and_annotation.dart: Context: This is the containing library.
+//
+// pkg/front_end/testcases/having_part_with_parts_and_annotation_lib1.dart:6:6: Error: A file that's a part of a library can't have parts itself.
+// Try moving the 'part' declaration to the containing library.
+// part 'having_part_with_parts_and_annotation_lib2.dart';
+//      ^
+// pkg/front_end/testcases/having_part_with_parts_and_annotation.dart: Context: This is the containing library.
+//
+import self as self;
+import "dart:core" as core;
+
+@self::Foo
+part having_part_with_parts_and_annotation_lib1.dart;
+static const field core::int Foo = 42;
+static const field core::int Bar = 43 /* from org-dartlang-testcase:///having_part_with_parts_and_annotation_lib1.dart */;
+static const field core::int Baz = 44 /* from org-dartlang-testcase:///having_part_with_parts_and_annotation_lib1.dart */;
+static method fromMain() → void {}
+static method main() → dynamic {}
+static method /* from org-dartlang-testcase:///having_part_with_parts_and_annotation_lib1.dart */ fromLib1() → void {}
diff --git a/pkg/front_end/testcases/having_part_with_parts_and_annotation.dart.strong.transformed.expect b/pkg/front_end/testcases/having_part_with_parts_and_annotation.dart.strong.transformed.expect
new file mode 100644
index 0000000..a33aba1
--- /dev/null
+++ b/pkg/front_end/testcases/having_part_with_parts_and_annotation.dart.strong.transformed.expect
@@ -0,0 +1,27 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/having_part_with_parts_and_annotation_lib1.dart:4:6: Error: A file that's a part of a library can't have parts itself.
+// Try moving the 'part' declaration to the containing library.
+// part 'having_part_with_parts_and_annotation_lib2.dart';
+//      ^
+// pkg/front_end/testcases/having_part_with_parts_and_annotation.dart: Context: This is the containing library.
+//
+// pkg/front_end/testcases/having_part_with_parts_and_annotation_lib1.dart:6:6: Error: A file that's a part of a library can't have parts itself.
+// Try moving the 'part' declaration to the containing library.
+// part 'having_part_with_parts_and_annotation_lib2.dart';
+//      ^
+// pkg/front_end/testcases/having_part_with_parts_and_annotation.dart: Context: This is the containing library.
+//
+import self as self;
+import "dart:core" as core;
+
+@self::Foo
+part having_part_with_parts_and_annotation_lib1.dart;
+static const field core::int Foo = 42;
+static const field core::int Bar = 43 /* from org-dartlang-testcase:///having_part_with_parts_and_annotation_lib1.dart */;
+static const field core::int Baz = 44 /* from org-dartlang-testcase:///having_part_with_parts_and_annotation_lib1.dart */;
+static method fromMain() → void {}
+static method main() → dynamic {}
+static method /* from org-dartlang-testcase:///having_part_with_parts_and_annotation_lib1.dart */ fromLib1() → void {}
diff --git a/pkg/front_end/testcases/having_part_with_parts_and_annotation_lib1.dart b/pkg/front_end/testcases/having_part_with_parts_and_annotation_lib1.dart
new file mode 100644
index 0000000..61dbfa8
--- /dev/null
+++ b/pkg/front_end/testcases/having_part_with_parts_and_annotation_lib1.dart
@@ -0,0 +1,11 @@
+part of 'having_part_with_parts_and_annotation.dart';
+
+@Bar
+part 'having_part_with_parts_and_annotation_lib2.dart';
+@Baz
+part 'having_part_with_parts_and_annotation_lib2.dart';
+
+const int Bar = 43;
+const int Baz = 44;
+
+void fromLib1() {}
diff --git a/pkg/front_end/testcases/having_part_with_parts_and_annotation_lib2.dart b/pkg/front_end/testcases/having_part_with_parts_and_annotation_lib2.dart
new file mode 100644
index 0000000..f5c6150
--- /dev/null
+++ b/pkg/front_end/testcases/having_part_with_parts_and_annotation_lib2.dart
@@ -0,0 +1,3 @@
+part of 'having_part_with_parts_and_annotation_lib1.dart';
+
+void fromLib2() {}
diff --git a/pkg/front_end/testcases/text_serialization.status b/pkg/front_end/testcases/text_serialization.status
index a32cc66..8508fee 100644
--- a/pkg/front_end/testcases/text_serialization.status
+++ b/pkg/front_end/testcases/text_serialization.status
@@ -104,6 +104,8 @@
 functions: TextSerializationFailure # Was: Pass
 future_or_test: TextSerializationFailure # Was: Pass
 hello: TextSerializationFailure # Was: Pass
+having_part_with_part_and_annotation: TextSerializationFailure
+having_part_with_parts_and_annotation: TextSerializationFailure
 illegal_named_function_expression: TextSerializationFailure # Was: Pass
 illegal_named_function_expression_scope: TextSerializationFailure # Was: Pass
 implicit_const_with_static_fields: TextSerializationFailure # Was: Pass
diff --git a/pkg/front_end/tool/_fasta/generate_experimental_flags_test.dart b/pkg/front_end/tool/_fasta/generate_experimental_flags_test.dart
index 81fdca0..ad1ac6e 100644
--- a/pkg/front_end/tool/_fasta/generate_experimental_flags_test.dart
+++ b/pkg/front_end/tool/_fasta/generate_experimental_flags_test.dart
@@ -17,7 +17,16 @@
     String generated = await generateMessagesFile();
     String actual = (await new File.fromUri(generatedFile).readAsString())
         .replaceAll('\r\n', '\n');
-    Expect.stringEquals(
-        generated, actual, "${generatedFile.path} is out of date");
+    Expect.stringEquals(generated, actual, """
+------------------------
+
+The generated file
+  ${generatedFile.path}
+
+is out of date. To regenerate the file, run
+  pkg/front_end/tool/fasta generate-experimental-flags
+
+------------------------
+""");
   });
 }
diff --git a/pkg/kernel/binary.md b/pkg/kernel/binary.md
index 6b247af..9dc8a27 100644
--- a/pkg/kernel/binary.md
+++ b/pkg/kernel/binary.md
@@ -139,7 +139,7 @@
 
 type ComponentFile {
   UInt32 magic = 0x90ABCDEF;
-  UInt32 formatVersion = 25;
+  UInt32 formatVersion = 26;
   List<String> problemsAsJson; // Described in problems.md.
   Library[] libraries;
   UriSource sourceMap;
@@ -730,6 +730,7 @@
   List<DartType> typeArguments;
   List<[FieldReference, Expression]> fieldValues;
   List<AssertStatement> asserts;
+  List<Expression> unusedArguments;
 }
 
 type IsExpression extends Expression {
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index 0a9e01b..0ff11cf 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -3330,17 +3330,18 @@
 /// Create an instance directly from the field values.
 ///
 /// This expression arises from const constructor calls when one or more field
-/// initializing expressions, field initializers or assert initializers contain
-/// unevaluated expressions. They only ever occur within unevaluated constants
-/// in constant expressions.
+/// initializing expressions, field initializers, assert initializers or unused
+/// arguments contain unevaluated expressions. They only ever occur within
+/// unevaluated constants in constant expressions.
 class InstanceCreation extends Expression {
   final Reference classReference;
   final List<DartType> typeArguments;
   final Map<Reference, Expression> fieldValues;
   final List<AssertStatement> asserts;
+  final List<Expression> unusedArguments;
 
-  InstanceCreation(
-      this.classReference, this.typeArguments, this.fieldValues, this.asserts);
+  InstanceCreation(this.classReference, this.typeArguments, this.fieldValues,
+      this.asserts, this.unusedArguments);
 
   Class get classNode => classReference.asClass;
 
@@ -3363,6 +3364,7 @@
       value.accept(v);
     }
     visitList(asserts, v);
+    visitList(unusedArguments, v);
   }
 
   transformChildren(Transformer v) {
@@ -3374,6 +3376,7 @@
       }
     });
     transformList(asserts, v, this);
+    transformList(unusedArguments, v, this);
   }
 }
 
@@ -3737,10 +3740,15 @@
   }
 }
 
+/// Common super-interface for [FunctionExpression] and [FunctionDeclaration].
+abstract class LocalFunction implements TreeNode {
+  FunctionNode get function;
+}
+
 /// Expression of form `(x,y) => ...` or `(x,y) { ... }`
 ///
 /// The arrow-body form `=> e` is desugared into `return e;`.
-class FunctionExpression extends Expression {
+class FunctionExpression extends Expression implements LocalFunction {
   FunctionNode function;
 
   FunctionExpression(this.function) {
@@ -4676,7 +4684,7 @@
 /// Declaration a local function.
 ///
 /// The body of the function may use [variable] as its self-reference.
-class FunctionDeclaration extends Statement {
+class FunctionDeclaration extends Statement implements LocalFunction {
   VariableDeclaration variable; // Is final and has no initializer.
   FunctionNode function;
 
diff --git a/pkg/kernel/lib/binary/ast_from_binary.dart b/pkg/kernel/lib/binary/ast_from_binary.dart
index 905363a..f1996a9 100644
--- a/pkg/kernel/lib/binary/ast_from_binary.dart
+++ b/pkg/kernel/lib/binary/ast_from_binary.dart
@@ -1592,8 +1592,9 @@
         for (int i = 0; i < assertCount; i++) {
           asserts[i] = readStatement();
         }
-        return new InstanceCreation(
-            classReference, typeArguments, fieldValues, asserts)
+        List<Expression> unusedArguments = readExpressionList();
+        return new InstanceCreation(classReference, typeArguments, fieldValues,
+            asserts, unusedArguments)
           ..fileOffset = offset;
       case Tag.IsExpression:
         int offset = readOffset();
diff --git a/pkg/kernel/lib/binary/ast_to_binary.dart b/pkg/kernel/lib/binary/ast_to_binary.dart
index 0292314..35337ea 100644
--- a/pkg/kernel/lib/binary/ast_to_binary.dart
+++ b/pkg/kernel/lib/binary/ast_to_binary.dart
@@ -1544,6 +1544,7 @@
       writeNode(value);
     });
     writeNodeList(node.asserts);
+    writeNodeList(node.unusedArguments);
   }
 
   @override
diff --git a/pkg/kernel/lib/clone.dart b/pkg/kernel/lib/clone.dart
index d5f1940..2c7df60 100644
--- a/pkg/kernel/lib/clone.dart
+++ b/pkg/kernel/lib/clone.dart
@@ -214,7 +214,8 @@
         node.classReference,
         node.typeArguments.map(visitType).toList(),
         fieldValues,
-        node.asserts.map(clone).toList());
+        node.asserts.map(clone).toList(),
+        node.unusedArguments.map(clone).toList());
   }
 
   visitIsExpression(IsExpression node) {
diff --git a/pkg/kernel/lib/text/ast_to_text.dart b/pkg/kernel/lib/text/ast_to_text.dart
index 5862b83..4578819 100644
--- a/pkg/kernel/lib/text/ast_to_text.dart
+++ b/pkg/kernel/lib/text/ast_to_text.dart
@@ -1330,6 +1330,14 @@
         writeExpression(assert_.message);
       }
       write(')');
+      first = false;
+    }
+    for (Expression unusedArgument in node.unusedArguments) {
+      if (!first) {
+        writeComma();
+      }
+      writeExpression(unusedArgument);
+      first = false;
     }
 
     write('}');
diff --git a/pkg/modular_test/lib/src/runner.dart b/pkg/modular_test/lib/src/runner.dart
index d3803bb..1ed2fcb 100644
--- a/pkg/modular_test/lib/src/runner.dart
+++ b/pkg/modular_test/lib/src/runner.dart
@@ -100,7 +100,7 @@
         exit(1);
       }
     }
-    Uri toUri(s) => s == null ? null : Uri.base.resolve(s);
+    Uri toUri(s) => s == null ? null : Uri.base.resolveUri(Uri.file(s));
     return Options()
       ..showSkipped = argResults['show-skipped']
       ..verbose = argResults['verbose']
diff --git a/pkg/nnbd_migration/lib/src/decorated_type.dart b/pkg/nnbd_migration/lib/src/decorated_type.dart
index a740c1c..5205490 100644
--- a/pkg/nnbd_migration/lib/src/decorated_type.dart
+++ b/pkg/nnbd_migration/lib/src/decorated_type.dart
@@ -45,33 +45,44 @@
 
   /// Creates a [DecoratedType] corresponding to the given [element], which is
   /// presumed to have come from code that is already migrated.
-  factory DecoratedType.forElement(Element element) {
+  factory DecoratedType.forElement(Element element, NullabilityGraph graph) {
     DecoratedType decorate(DartType type) {
+      if (type.isVoid || type.isDynamic) {
+        return DecoratedType(type, graph.always);
+      }
       assert((type as TypeImpl).nullabilitySuffix ==
           NullabilitySuffix.star); // TODO(paulberry)
       if (type is FunctionType) {
-        var decoratedType = DecoratedType(type, NullabilityNode.never,
-            returnType: decorate(type.returnType), positionalParameters: []);
+        var positionalParameters = <DecoratedType>[];
+        var namedParameters = <String, DecoratedType>{};
         for (var parameter in type.parameters) {
-          assert(parameter.isPositional); // TODO(paulberry)
-          decoratedType.positionalParameters.add(decorate(parameter.type));
+          if (parameter.isPositional) {
+            positionalParameters.add(decorate(parameter.type));
+          } else {
+            namedParameters[parameter.name] = decorate(parameter.type);
+          }
         }
-        return decoratedType;
+        return DecoratedType(type, graph.never,
+            returnType: decorate(type.returnType),
+            namedParameters: namedParameters,
+            positionalParameters: positionalParameters);
       } else if (type is InterfaceType) {
-        assert(type.typeParameters.isEmpty); // TODO(paulberry)
-        return DecoratedType(type, NullabilityNode.never);
+        if (type.typeParameters.isNotEmpty) {
+          // TODO(paulberry)
+          throw UnimplementedError('Decorating ${type.displayName}');
+        }
+        return DecoratedType(type, graph.never);
       } else {
         throw type.runtimeType; // TODO(paulberry)
       }
     }
 
     DecoratedType decoratedType;
-    if (element is MethodElement) {
-      decoratedType = decorate(element.type);
-    } else if (element is PropertyAccessorElement) {
+    if (element is ExecutableElement) {
       decoratedType = decorate(element.type);
     } else {
-      throw element.runtimeType; // TODO(paulberry)
+      // TODO(paulberry)
+      throw UnimplementedError('Decorating ${element.runtimeType}');
     }
     return decoratedType;
   }
diff --git a/pkg/nnbd_migration/lib/src/decorated_type_operations.dart b/pkg/nnbd_migration/lib/src/decorated_type_operations.dart
index b1e3fa8..0b7a04c 100644
--- a/pkg/nnbd_migration/lib/src/decorated_type_operations.dart
+++ b/pkg/nnbd_migration/lib/src/decorated_type_operations.dart
@@ -9,7 +9,8 @@
 import 'package:nnbd_migration/src/node_builder.dart';
 
 /// [TypeOperations] that works with [DecoratedType]s.
-class DecoratedTypeOperations implements TypeOperations<DecoratedType> {
+class DecoratedTypeOperations
+    implements TypeOperations<VariableElement, DecoratedType> {
   final TypeSystem _typeSystem;
   final VariableRepository _variableRepository;
 
@@ -21,6 +22,11 @@
   }
 
   @override
+  bool isLocalVariable(VariableElement element) {
+    return element is LocalVariableElement;
+  }
+
+  @override
   bool isSubtypeOf(DecoratedType leftType, DecoratedType rightType) {
     return _typeSystem.isSubtypeOf(leftType.type, rightType.type);
   }
diff --git a/pkg/nnbd_migration/lib/src/edge_origin.dart b/pkg/nnbd_migration/lib/src/edge_origin.dart
new file mode 100644
index 0000000..487edc1
--- /dev/null
+++ b/pkg/nnbd_migration/lib/src/edge_origin.dart
@@ -0,0 +1,101 @@
+// Copyright (c) 2019, 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 'package:analyzer/src/generated/source.dart';
+
+/// Edge origin resulting from the use of a type that is always nullable.
+///
+/// For example, in the following code snippet:
+///   void f(dynamic x) {}
+///
+/// this class is used for the edge connecting `always` to the type of f's `x`
+/// parameter, due to the fact that the `dynamic` type is always considered
+/// nullable.
+class AlwaysNullableTypeOrigin extends EdgeOriginWithLocation {
+  AlwaysNullableTypeOrigin(Source source, int offset) : super(source, offset);
+}
+
+/// Common interface for classes providing information about how an edge came
+/// to be; that is, what was found in the source code that led the migration
+/// tool to create the edge.
+abstract class EdgeOrigin {}
+
+/// Common base class for edge origins that are associated with a single
+/// location in the source code.
+abstract class EdgeOriginWithLocation extends EdgeOrigin {
+  /// The source file containing the code construct that led to the edge.
+  final Source source;
+
+  /// The offset within the source file of the code construct that led to the
+  /// edge.
+  final int offset;
+
+  EdgeOriginWithLocation(this.source, this.offset);
+}
+
+/// Edge origin resulting from a class that is instantiated to bounds.
+///
+/// For example, in the following code snippet:
+///   class C<T extends Object> {}
+///   C x;
+///
+/// this class is used for the edge connecting the type of x's type parameter
+/// with the type bound in the declaration of C.
+class InstantiateToBoundsOrigin extends EdgeOriginWithLocation {
+  InstantiateToBoundsOrigin(Source source, int offset) : super(source, offset);
+}
+
+/// Edge origin resulting from a call site that does not supply a named
+/// parameter.
+///
+/// For example, in the following code snippet:
+///   void f({int i}) {}
+///   main() {
+///     f();
+///   }
+///
+/// this class is used for the edge connecting `always` to the type of f's `i`
+/// parameter, due to the fact that the call to `f` implicitly passes a null
+/// value for `i`.
+class NamedParameterNotSuppliedOrigin extends EdgeOriginWithLocation {
+  NamedParameterNotSuppliedOrigin(Source source, int offset)
+      : super(source, offset);
+}
+
+/// Edge origin resulting from the presence of a non-null assertion.
+///
+/// For example, in the following code snippet:
+///   void f(int i) {
+///     assert(i != null);
+///   }
+///
+/// this class is used for the edge connecting the type of f's `i` parameter to
+/// `never`, due to the assert statement proclaiming that `i` is not `null`.
+class NonNullAssertionOrigin extends EdgeOriginWithLocation {
+  NonNullAssertionOrigin(Source source, int offset) : super(source, offset);
+}
+
+/// Edge origin resulting from the presence of an explicit nullability hint
+/// comment.
+///
+/// For example, in the following code snippet:
+///   void f(int/*?*/ i) {}
+///
+/// this class is used for the edge connecting `always` to the type of f's `i`
+/// parameter, due to the presence of the `/*?*/` comment.
+class NullabilityCommentOrigin extends EdgeOriginWithLocation {
+  NullabilityCommentOrigin(Source source, int offset) : super(source, offset);
+}
+
+/// Edge origin resulting from the presence of an optional formal parameter.
+///
+/// For example, in the following code snippet:
+///   void f({int i}) {}
+///
+/// this class is used for the edge connecting `always` to the type of f's `i`
+/// parameter, due to the fact that `i` is optional and has no initializer.
+class OptionalFormalParameterOrigin extends EdgeOriginWithLocation {
+  OptionalFormalParameterOrigin(Source source, int offset)
+      : super(source, offset);
+}
diff --git a/pkg/nnbd_migration/lib/src/expression_checks.dart b/pkg/nnbd_migration/lib/src/expression_checks.dart
index 6dd34af..fd87c85 100644
--- a/pkg/nnbd_migration/lib/src/expression_checks.dart
+++ b/pkg/nnbd_migration/lib/src/expression_checks.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
+import 'package:nnbd_migration/src/edge_origin.dart';
 import 'package:nnbd_migration/src/nullability_node.dart';
 import 'package:nnbd_migration/src/potential_modification.dart';
 
@@ -10,38 +11,51 @@
 /// set of runtime checks that might need to be performed on the value of an
 /// expression.
 ///
-/// TODO(paulberry): the only check we support now is [nullCheck], which checks
-/// that the expression is not null.  We need to add other checks, e.g. to check
-/// that a List<int?> is actually a List<int>.
-class ExpressionChecks extends PotentialModification {
+/// TODO(paulberry): we don't currently have any way of distinguishing checks
+/// based on the nullability of the type itself (which can be checked by adding
+/// a trailing `!`) from checks based on type parameters (which will have to be
+/// checked using an `as` expression).
+class ExpressionChecks extends PotentialModification implements EdgeOrigin {
   /// Source offset where a trailing `!` might need to be inserted.
   final int offset;
 
-  /// Nullability node indicating whether the expression's value is nullable.
-  final NullabilityNode valueNode;
+  /// List of all nullability edges that are related to this potential check.
+  ///
+  /// TODO(paulberry): update this data structure to keep track of all the ways
+  /// in which edges can be related to an [ExpressionChecks], including:
+  ///
+  /// - An edge which, if unsatisfied, indicates that the expression needs to be
+  ///   null-checked.
+  /// - An edge which, if unsatisfied, indicates that a type parameter of the
+  ///   expression needs to be checked for nullability (e.g. by the migration
+  ///   engine inserting a test like `as List<int>?`)
+  /// - An edge which, if unsatisfied, indicates that a return type of the
+  ///   expression needs to be checked for nullability (e.g. by the migration
+  ///   engine inserting a test like `as int Function(...)?`)
+  /// - An edge which, if unsatisfied, indicates that a parameter type of the
+  ///   expression needs to be checked for nullability (e.g. by the migration
+  ///   engine inserting a test like `as void Function(int?)?`)
+  ///
+  /// ...and so on.
+  final List<NullabilityEdge> edges = [];
 
-  /// Nullability node indicating whether the expression's context requires a
-  /// nullable value.
-  final NullabilityNode contextNode;
-
-  /// Nullability nodes guarding execution of the expression.  If any of the
-  /// nodes in this list turns out to be non-nullable, the expression is dead
-  /// code and will be removed by the migration tool.
-  final List<NullabilityNode> guards;
-
-  ExpressionChecks(this.offset, this.valueNode, this.contextNode,
-      Iterable<NullabilityNode> guards)
-      : guards = guards.toList();
+  ExpressionChecks(this.offset);
 
   @override
   bool get isEmpty {
-    for (var guard in guards) {
-      if (!guard.isNullable) return true;
+    for (var edge in edges) {
+      if (!edge.isSatisfied) return false;
     }
-    return !valueNode.isNullable || contextNode.isNullable;
+    return true;
   }
 
   @override
-  Iterable<SourceEdit> get modifications =>
-      isEmpty ? [] : [SourceEdit(offset, 0, '!')];
+  Iterable<SourceEdit> get modifications {
+    // TODO(paulberry): this assumes that the check that needs to be done is for
+    // the nullability of the type itself (in which case all we need is a simple
+    // null check).  Need to support checks that will have to be addressed by
+    // adding an `as` expression, e.g. `as List<int>?` to verify that a list is
+    // reified to contain only non-null ints.
+    return isEmpty ? [] : [SourceEdit(offset, 0, '!')];
+  }
 }
diff --git a/pkg/nnbd_migration/lib/src/graph_builder.dart b/pkg/nnbd_migration/lib/src/graph_builder.dart
index cf3c46d..6f99c8a 100644
--- a/pkg/nnbd_migration/lib/src/graph_builder.dart
+++ b/pkg/nnbd_migration/lib/src/graph_builder.dart
@@ -14,6 +14,7 @@
 import 'package:nnbd_migration/nnbd_migration.dart';
 import 'package:nnbd_migration/src/conditional_discard.dart';
 import 'package:nnbd_migration/src/decorated_type.dart';
+import 'package:nnbd_migration/src/edge_origin.dart';
 import 'package:nnbd_migration/src/expression_checks.dart';
 import 'package:nnbd_migration/src/node_builder.dart';
 import 'package:nnbd_migration/src/nullability_node.dart';
@@ -78,14 +79,12 @@
 
   GraphBuilder(TypeProvider typeProvider, this._variables, this._graph,
       this._source, this.listener)
-      : _notNullType =
-            DecoratedType(typeProvider.objectType, NullabilityNode.never),
+      : _notNullType = DecoratedType(typeProvider.objectType, _graph.never),
         _nonNullableBoolType =
-            DecoratedType(typeProvider.boolType, NullabilityNode.never),
+            DecoratedType(typeProvider.boolType, _graph.never),
         _nonNullableTypeType =
-            DecoratedType(typeProvider.typeType, NullabilityNode.never),
-        _nullType =
-            DecoratedType(typeProvider.nullType, NullabilityNode.always);
+            DecoratedType(typeProvider.typeType, _graph.never),
+        _nullType = DecoratedType(typeProvider.nullType, _graph.always);
 
   /// Gets the decorated type of [element] from [_variables], performing any
   /// necessary substitutions.
@@ -120,13 +119,11 @@
       var decoratedElementType =
           _variables.decoratedElementType(variable, create: true);
       if (baseElement.isGetter) {
-        decoratedBaseType = DecoratedType(
-            baseElement.type, NullabilityNode.never,
+        decoratedBaseType = DecoratedType(baseElement.type, _graph.never,
             returnType: decoratedElementType);
       } else {
         assert(baseElement.isSetter);
-        decoratedBaseType = DecoratedType(
-            baseElement.type, NullabilityNode.never,
+        decoratedBaseType = DecoratedType(baseElement.type, _graph.never,
             positionalParameters: [decoratedElementType]);
       }
     } else {
@@ -147,13 +144,20 @@
   }
 
   @override
+  DecoratedType visitAsExpression(AsExpression node) {
+    // TODO(brianwilkerson)
+    _unimplemented(node, 'AsExpression');
+  }
+
+  @override
   DecoratedType visitAssertStatement(AssertStatement node) {
     _handleAssignment(_notNullType, node.condition);
     if (identical(_conditionInfo?.condition, node.condition)) {
       if (!_inConditionalControlFlow &&
           _conditionInfo.trueDemonstratesNonNullIntent != null) {
-        _conditionInfo.trueDemonstratesNonNullIntent
-            ?.recordNonNullIntent(_guards, _graph);
+        _graph.connect(_conditionInfo.trueDemonstratesNonNullIntent,
+            _graph.never, NonNullAssertionOrigin(_source, node.offset),
+            hard: true);
       }
     }
     node.message?.accept(this);
@@ -163,7 +167,8 @@
   @override
   DecoratedType visitAssignmentExpression(AssignmentExpression node) {
     if (node.operator.type != TokenType.EQ) {
-      throw UnimplementedError('TODO(paulberry)');
+      // TODO(paulberry)
+      _unimplemented(node, 'Assignment with operator ${node.operator.lexeme}');
     }
     var leftType = node.leftHandSide.accept(this);
     var conditionalNode = _lastConditionalNode;
@@ -178,49 +183,59 @@
   }
 
   @override
+  DecoratedType visitAwaitExpression(AwaitExpression node) {
+    var expressionType = node.expression.accept(this);
+    // TODO(paulberry) Handle subclasses of Future.
+    if (expressionType.type.isDartAsyncFuture ||
+        expressionType.type.isDartAsyncFutureOr) {
+      expressionType = expressionType.typeArguments[0];
+    }
+    return expressionType;
+  }
+
+  @override
   DecoratedType visitBinaryExpression(BinaryExpression node) {
-    switch (node.operator.type) {
-      case TokenType.EQ_EQ:
-      case TokenType.BANG_EQ:
-        assert(node.leftOperand is! NullLiteral); // TODO(paulberry)
-        var leftType = node.leftOperand.accept(this);
-        node.rightOperand.accept(this);
-        if (node.rightOperand is NullLiteral) {
-          // TODO(paulberry): figure out what the rules for isPure should be.
-          // TODO(paulberry): only set falseChecksNonNull in unconditional
-          // control flow
-          bool isPure = node.leftOperand is SimpleIdentifier;
-          var conditionInfo = _ConditionInfo(node,
-              isPure: isPure,
-              trueGuard: leftType.node,
-              falseDemonstratesNonNullIntent: leftType.node);
-          _conditionInfo = node.operator.type == TokenType.EQ_EQ
-              ? conditionInfo
-              : conditionInfo.not(node);
-        }
-        return _nonNullableBoolType;
-      case TokenType.PLUS:
-        _handleAssignment(_notNullType, node.leftOperand);
-        var callee = node.staticElement;
-        assert(!(callee is ClassMemberElement &&
-            callee.enclosingElement.typeParameters
-                .isNotEmpty)); // TODO(paulberry)
-        assert(callee != null); // TODO(paulberry)
-        var calleeType = getOrComputeElementType(callee);
-        // TODO(paulberry): substitute if necessary
-        assert(calleeType.positionalParameters.length > 0); // TODO(paulberry)
-        _handleAssignment(
-            calleeType.positionalParameters[0], node.rightOperand);
-        return calleeType.returnType;
-      default:
-        assert(false); // TODO(paulberry)
-        return null;
+    var operatorType = node.operator.type;
+    if (operatorType == TokenType.EQ_EQ || operatorType == TokenType.BANG_EQ) {
+      assert(node.leftOperand is! NullLiteral); // TODO(paulberry)
+      var leftType = node.leftOperand.accept(this);
+      node.rightOperand.accept(this);
+      if (node.rightOperand is NullLiteral) {
+        // TODO(paulberry): figure out what the rules for isPure should be.
+        // TODO(paulberry): only set falseChecksNonNull in unconditional
+        // control flow
+        bool isPure = node.leftOperand is SimpleIdentifier;
+        var conditionInfo = _ConditionInfo(node,
+            isPure: isPure,
+            trueGuard: leftType.node,
+            falseDemonstratesNonNullIntent: leftType.node);
+        _conditionInfo = operatorType == TokenType.EQ_EQ
+            ? conditionInfo
+            : conditionInfo.not(node);
+      }
+      return _nonNullableBoolType;
+    } else if (operatorType.isUserDefinableOperator) {
+      _handleAssignment(_notNullType, node.leftOperand);
+      var callee = node.staticElement;
+      assert(!(callee is ClassMemberElement &&
+          callee
+              .enclosingElement.typeParameters.isNotEmpty)); // TODO(paulberry)
+      assert(callee != null); // TODO(paulberry)
+      var calleeType = getOrComputeElementType(callee);
+      // TODO(paulberry): substitute if necessary
+      assert(calleeType.positionalParameters.length > 0); // TODO(paulberry)
+      _handleAssignment(calleeType.positionalParameters[0], node.rightOperand);
+      return calleeType.returnType;
+    } else {
+      // TODO(paulberry)
+      _unimplemented(
+          node, 'Binary expression with operator ${node.operator.lexeme}');
     }
   }
 
   @override
   DecoratedType visitBooleanLiteral(BooleanLiteral node) {
-    return DecoratedType(node.staticType, NullabilityNode.never);
+    return DecoratedType(node.staticType, _graph.never);
   }
 
   @override
@@ -258,9 +273,11 @@
         // Nothing to do; the implicit default value of `null` will never be
         // reached.
       } else {
-        NullabilityNode.recordAssignment(NullabilityNode.always,
-            getOrComputeElementType(node.declaredElement).node, _guards, _graph,
-            hard: false);
+        _graph.connect(
+            _graph.always,
+            getOrComputeElementType(node.declaredElement).node,
+            OptionalFormalParameterOrigin(_source, node.offset),
+            guards: _guards);
       }
     } else {
       _handleAssignment(
@@ -271,6 +288,11 @@
   }
 
   @override
+  DecoratedType visitDoubleLiteral(DoubleLiteral node) {
+    return DecoratedType(node.staticType, _graph.never);
+  }
+
+  @override
   DecoratedType visitExpressionFunctionBody(ExpressionFunctionBody node) {
     _handleAssignment(_currentFunctionType.returnType, node.expression);
     return null;
@@ -292,6 +314,19 @@
   }
 
   @override
+  DecoratedType visitFunctionExpression(FunctionExpression node) {
+    // TODO(brianwilkerson)
+    _unimplemented(node, 'FunctionExpression');
+  }
+
+  @override
+  DecoratedType visitFunctionExpressionInvocation(
+      FunctionExpressionInvocation node) {
+    // TODO(brianwilkerson)
+    _unimplemented(node, 'FunctionExpressionInvocation');
+  }
+
+  @override
   DecoratedType visitIfStatement(IfStatement node) {
     // TODO(paulberry): should the use of a boolean in an if-statement be
     // treated like an implicit `assert(b != null)`?  Probably.
@@ -337,7 +372,8 @@
     }
     var callee = node.staticElement;
     if (callee == null) {
-      throw new UnimplementedError('TODO(paulberry)');
+      // TODO(paulberry)
+      _unimplemented(node, 'Index expression with no static type');
     }
     var calleeType = getOrComputeElementType(callee, targetType: targetType);
     // TODO(paulberry): substitute if necessary
@@ -350,8 +386,66 @@
   }
 
   @override
+  DecoratedType visitInstanceCreationExpression(
+      InstanceCreationExpression node) {
+    var callee = node.staticElement;
+    var calleeType = getOrComputeElementType(callee);
+    if (callee.enclosingElement.typeParameters.isNotEmpty) {
+      // If the class has type parameters then we might need to substitute the
+      // appropriate type arguments.
+      // TODO(brianwilkerson)
+      _unimplemented(node, 'Instance creation expression with type arguments');
+    }
+    _handleInvocationArguments(node.argumentList, calleeType);
+    return calleeType.returnType;
+  }
+
+  @override
   DecoratedType visitIntegerLiteral(IntegerLiteral node) {
-    return DecoratedType(node.staticType, NullabilityNode.never);
+    return DecoratedType(node.staticType, _graph.never);
+  }
+
+  @override
+  DecoratedType visitIsExpression(IsExpression node) {
+    var type = node.type;
+    if (type is NamedType && type.typeArguments != null) {
+      // TODO(brianwilkerson) Figure out what constraints we need to add to
+      //  allow the tool to decide whether to make the type arguments nullable.
+      // TODO(brianwilkerson)
+      _unimplemented(node, 'Is expression with type arguments');
+    } else if (type is GenericFunctionType) {
+      // TODO(brianwilkerson)
+      _unimplemented(node, 'Is expression with GenericFunctionType');
+    }
+    node.visitChildren(this);
+    return DecoratedType(node.staticType, _graph.never);
+  }
+
+  @override
+  DecoratedType visitListLiteral(ListLiteral node) {
+    var listType = node.staticType as InterfaceType;
+    if (node.typeArguments == null) {
+      // TODO(brianwilkerson) We might want to create a fake node in the graph
+      //  to represent the type argument so that we can still create edges from
+      //  the elements to it.
+      // TODO(brianwilkerson)
+      _unimplemented(node, 'List literal with no type arguments');
+    } else {
+      var typeArgumentType = _variables.decoratedTypeAnnotation(
+          _source, node.typeArguments.arguments[0]);
+      for (var element in node.elements) {
+        if (element is Expression) {
+          _handleAssignment(typeArgumentType, element);
+        } else {
+          // Handle spread and control flow elements.
+          element.accept(this);
+          // TODO(brianwilkerson)
+          _unimplemented(node, 'Spread or control flow element');
+        }
+      }
+      return DecoratedType(listType, _graph.never,
+          typeArguments: [typeArgumentType]);
+    }
   }
 
   @override
@@ -384,30 +478,12 @@
     }
     var callee = node.methodName.staticElement;
     if (callee == null) {
-      throw new UnimplementedError('TODO(paulberry)');
+      // TODO(paulberry)
+      _unimplemented(node, 'Unresolved method name');
     }
     var calleeType = getOrComputeElementType(callee, targetType: targetType);
     // TODO(paulberry): substitute if necessary
-    var arguments = node.argumentList.arguments;
-    int i = 0;
-    var suppliedNamedParameters = Set<String>();
-    for (var expression in arguments) {
-      if (expression is NamedExpression) {
-        var name = expression.name.label.name;
-        var parameterType = calleeType.namedParameters[name];
-        assert(parameterType != null); // TODO(paulberry)
-        _handleAssignment(parameterType, expression.expression);
-        suppliedNamedParameters.add(name);
-      } else {
-        assert(calleeType.positionalParameters.length > i); // TODO(paulberry)
-        _handleAssignment(calleeType.positionalParameters[i++], expression);
-      }
-    }
-    // Any parameters not supplied must be optional.
-    for (var entry in calleeType.namedParameters.entries) {
-      if (suppliedNamedParameters.contains(entry.key)) continue;
-      entry.value.node.recordNamedParameterNotSupplied(_guards, _graph);
-    }
+    _handleInvocationArguments(node.argumentList, calleeType);
     var expressionType = calleeType.returnType;
     if (isConditional) {
       expressionType = expressionType.withNode(
@@ -418,6 +494,12 @@
   }
 
   @override
+  DecoratedType visitNamespaceDirective(NamespaceDirective node) {
+    // skip directives
+    return null;
+  }
+
+  @override
   DecoratedType visitNode(AstNode node) {
     if (listener != null) {
       try {
@@ -445,15 +527,34 @@
   }
 
   @override
+  DecoratedType visitPostfixExpression(PostfixExpression node) {
+    // TODO(brianwilkerson)
+    _unimplemented(node, 'PostfixExpression');
+  }
+
+  @override
   DecoratedType visitPrefixedIdentifier(PrefixedIdentifier node) {
     if (node.prefix.staticElement is ImportElement) {
-      throw new UnimplementedError('TODO(paulberry)');
+      // TODO(paulberry)
+      _unimplemented(node, 'PrefixedIdentifier with a prefix');
     } else {
       return _handlePropertyAccess(node, node.prefix, node.identifier);
     }
   }
 
   @override
+  DecoratedType visitPrefixExpression(PrefixExpression node) {
+    /* DecoratedType operandType = */
+    _handleAssignment(_notNullType, node.operand);
+    if (node.operator.type == TokenType.BANG) {
+      return _nonNullableBoolType;
+    }
+    // TODO(brianwilkerson) The remaining cases are invocations.
+    _unimplemented(
+        node, 'Prefix expression with operator ${node.operator.lexeme}');
+  }
+
+  @override
   DecoratedType visitPropertyAccess(PropertyAccess node) {
     return _handlePropertyAccess(node, node.realTarget, node.propertyName);
   }
@@ -461,7 +562,8 @@
   @override
   DecoratedType visitReturnStatement(ReturnStatement node) {
     if (node.expression == null) {
-      _checkAssignment(_currentFunctionType.returnType, _nullType, null);
+      _checkAssignment(_currentFunctionType.returnType, _nullType, null,
+          hard: false);
     } else {
       _handleAssignment(_currentFunctionType.returnType, node.expression);
     }
@@ -469,55 +571,135 @@
   }
 
   @override
+  DecoratedType visitSetOrMapLiteral(SetOrMapLiteral node) {
+    var listType = node.staticType as InterfaceType;
+    var typeArguments = node.typeArguments?.arguments;
+    if (typeArguments == null) {
+      // TODO(brianwilkerson) We might want to create fake nodes in the graph to
+      //  represent the type arguments so that we can still create edges from
+      //  the elements to them.
+      // TODO(brianwilkerson)
+      _unimplemented(node, 'Set or map literal with no type arguments');
+    } else if (typeArguments.length == 1) {
+      var elementType =
+          _variables.decoratedTypeAnnotation(_source, typeArguments[0]);
+      for (var element in node.elements) {
+        if (element is Expression) {
+          _handleAssignment(elementType, element);
+        } else {
+          // Handle spread and control flow elements.
+          element.accept(this);
+          // TODO(brianwilkerson)
+          _unimplemented(node, 'Spread or control flow element');
+        }
+      }
+      return DecoratedType(listType, _graph.never,
+          typeArguments: [elementType]);
+    } else if (typeArguments.length == 2) {
+      var keyType =
+          _variables.decoratedTypeAnnotation(_source, typeArguments[0]);
+      var valueType =
+          _variables.decoratedTypeAnnotation(_source, typeArguments[1]);
+      for (var element in node.elements) {
+        if (element is MapLiteralEntry) {
+          _handleAssignment(keyType, element.key);
+          _handleAssignment(valueType, element.value);
+        } else {
+          // Handle spread and control flow elements.
+          element.accept(this);
+          // TODO(brianwilkerson)
+          _unimplemented(node, 'Spread or control flow element');
+        }
+      }
+      return DecoratedType(listType, _graph.never,
+          typeArguments: [keyType, valueType]);
+    } else {
+      // TODO(brianwilkerson)
+      _unimplemented(
+          node, 'Set or map literal with more than two type arguments');
+    }
+  }
+
+  @override
   DecoratedType visitSimpleIdentifier(SimpleIdentifier node) {
     var staticElement = node.staticElement;
     if (staticElement is ParameterElement ||
         staticElement is LocalVariableElement) {
       return getOrComputeElementType(staticElement);
+    } else if (staticElement is PropertyAccessorElement) {
+      // TODO(danrubel): assuming getter context... need to handle setter
+      return getOrComputeElementType(staticElement).returnType;
     } else if (staticElement is ClassElement) {
       return _nonNullableTypeType;
     } else {
       // TODO(paulberry)
-      throw new UnimplementedError('${staticElement.runtimeType}');
+      _unimplemented(node,
+          'Simple identifier with a static element of type ${staticElement.runtimeType}');
     }
   }
 
   @override
   DecoratedType visitStringLiteral(StringLiteral node) {
-    return DecoratedType(node.staticType, NullabilityNode.never);
+    node.visitChildren(this);
+    return DecoratedType(node.staticType, _graph.never);
+  }
+
+  @override
+  DecoratedType visitSuperExpression(SuperExpression node) {
+    return DecoratedType(node.staticType, _graph.never);
+  }
+
+  @override
+  DecoratedType visitSymbolLiteral(SymbolLiteral node) {
+    return DecoratedType(node.staticType, _graph.never);
   }
 
   @override
   DecoratedType visitThisExpression(ThisExpression node) {
-    return DecoratedType(node.staticType, NullabilityNode.never);
+    return DecoratedType(node.staticType, _graph.never);
   }
 
   @override
   DecoratedType visitThrowExpression(ThrowExpression node) {
     node.expression.accept(this);
     // TODO(paulberry): do we need to check the expression type?  I think not.
-    return DecoratedType(node.staticType, NullabilityNode.never);
+    return DecoratedType(node.staticType, _graph.never);
   }
 
   @override
   DecoratedType visitTypeName(TypeName typeName) {
     var typeArguments = typeName.typeArguments?.arguments;
     var element = typeName.name.staticElement;
-    if (typeArguments != null) {
-      for (int i = 0; i < typeArguments.length; i++) {
-        DecoratedType bound;
-        if (element is TypeParameterizedElement) {
+    if (element is TypeParameterizedElement) {
+      if (typeArguments == null) {
+        var instantiatedType =
+            _variables.decoratedTypeAnnotation(_source, typeName);
+        if (instantiatedType == null) {
+          throw new StateError('No type annotation for type name '
+              '${typeName.toSource()}, offset=${typeName.offset}');
+        }
+        var origin = InstantiateToBoundsOrigin(_source, typeName.offset);
+        for (int i = 0; i < instantiatedType.typeArguments.length; i++) {
+          _unionDecoratedTypes(
+              instantiatedType.typeArguments[i],
+              _variables.decoratedElementType(element.typeParameters[i],
+                  create: true),
+              origin);
+        }
+      } else {
+        for (int i = 0; i < typeArguments.length; i++) {
+          DecoratedType bound;
           bound = _variables.decoratedElementType(element.typeParameters[i],
               create: true);
-        } else {
-          throw new UnimplementedError('TODO(paulberry)');
+          _checkAssignment(
+              bound,
+              _variables.decoratedTypeAnnotation(_source, typeArguments[i]),
+              null,
+              hard: true);
         }
-        _checkAssignment(bound,
-            _variables.decoratedTypeAnnotation(_source, typeArguments[i]), null,
-            hard: true);
       }
     }
-    return DecoratedType(typeName.type, NullabilityNode.never);
+    return _nonNullableTypeType;
   }
 
   @override
@@ -525,7 +707,8 @@
     var destinationType = getOrComputeElementType(node.declaredElement);
     var initializer = node.initializer;
     if (initializer == null) {
-      throw UnimplementedError('TODO(paulberry)');
+      // TODO(paulberry)
+      _unimplemented(node, 'Variable declaration with no initializer');
     } else {
       _handleAssignment(destinationType, initializer);
     }
@@ -533,46 +716,44 @@
   }
 
   /// Creates the necessary constraint(s) for an assignment from [sourceType] to
-  /// [destinationType].  [expression] is the expression whose type is
-  /// [sourceType]; it is the expression we will have to null-check in the case
-  /// where a nullable source is assigned to a non-nullable destination.
+  /// [destinationType].  [expressionChecks] tracks checks that might have to be
+  /// done on the type of an expression.  [hard] indicates whether a hard edge
+  /// should be created.
   void _checkAssignment(DecoratedType destinationType, DecoratedType sourceType,
-      Expression expression,
-      {bool hard}) {
-    if (expression != null) {
-      _variables.recordExpressionChecks(
-          _source,
-          expression,
-          ExpressionChecks(
-              expression.end, sourceType.node, destinationType.node, _guards));
-    }
-    NullabilityNode.recordAssignment(
-        sourceType.node, destinationType.node, _guards, _graph,
-        hard: hard ??
-            (_isVariableOrParameterReference(expression) &&
-                !_inConditionalControlFlow));
-    // TODO(paulberry): it's a cheat to pass in expression=null for the
-    // recursive checks.  Really we want to unify all the checks in a single
-    // ExpressionChecks object.
-    expression = null;
+      ExpressionChecks expressionChecks,
+      {@required bool hard}) {
+    var edge = _graph.connect(
+        sourceType.node, destinationType.node, expressionChecks,
+        guards: _guards, hard: hard);
+    expressionChecks?.edges?.add(edge);
     // TODO(paulberry): generalize this.
+
     if ((_isSimple(sourceType) || destinationType.type.isObject) &&
         _isSimple(destinationType)) {
       // Ok; nothing further to do.
-    } else if (sourceType.type is InterfaceType &&
+      return;
+    }
+
+    if (sourceType.type is InterfaceType &&
         destinationType.type is InterfaceType &&
         sourceType.type.element == destinationType.type.element) {
       assert(sourceType.typeArguments.length ==
           destinationType.typeArguments.length);
       for (int i = 0; i < sourceType.typeArguments.length; i++) {
         _checkAssignment(destinationType.typeArguments[i],
-            sourceType.typeArguments[i], expression);
+            sourceType.typeArguments[i], expressionChecks,
+            hard: false);
       }
-    } else if (destinationType.type.isDynamic || sourceType.type.isDynamic) {
-      // ok; nothing further to do.
-    } else {
-      throw '$destinationType <= $sourceType'; // TODO(paulberry)
+      return;
     }
+
+    if (destinationType.type.isDynamic || sourceType.type.isDynamic) {
+      // ok; nothing further to do.
+      return;
+    }
+
+    // TODO(paulberry)
+    throw '$destinationType <= $sourceType';
   }
 
   /// Double checks that [name] is not the name of a method or getter declared
@@ -593,11 +774,54 @@
       DecoratedType destinationType, Expression expression,
       {bool canInsertChecks = true}) {
     var sourceType = expression.accept(this);
-    _checkAssignment(
-        destinationType, sourceType, canInsertChecks ? expression : null);
+    if (sourceType == null) {
+      throw StateError('No type computed for ${expression.runtimeType} '
+          '(${expression.toSource()}) offset=${expression.offset}');
+    }
+    ExpressionChecks expressionChecks;
+    if (canInsertChecks) {
+      expressionChecks = ExpressionChecks(expression.end);
+      _variables.recordExpressionChecks(_source, expression, expressionChecks);
+    }
+    _checkAssignment(destinationType, sourceType, expressionChecks,
+        hard: _isVariableOrParameterReference(expression) &&
+            !_inConditionalControlFlow);
     return sourceType;
   }
 
+  /// Creates the necessary constraint(s) for an [argumentList] when invoking an
+  /// executable element whose type is [calleeType].
+  void _handleInvocationArguments(
+      ArgumentList argumentList, DecoratedType calleeType) {
+    var arguments = argumentList.arguments;
+    int i = 0;
+    var suppliedNamedParameters = Set<String>();
+    for (var expression in arguments) {
+      if (expression is NamedExpression) {
+        var name = expression.name.label.name;
+        var parameterType = calleeType.namedParameters[name];
+        if (parameterType == null) {
+          // TODO(paulberry)
+          _unimplemented(expression, 'Missing type for named parameter');
+        }
+        _handleAssignment(parameterType, expression.expression);
+        suppliedNamedParameters.add(name);
+      } else {
+        if (calleeType.positionalParameters.length <= i) {
+          // TODO(paulberry)
+          _unimplemented(argumentList, 'Missing positional parameter at $i');
+        }
+        _handleAssignment(calleeType.positionalParameters[i++], expression);
+      }
+    }
+    // Any parameters not supplied must be optional.
+    for (var entry in calleeType.namedParameters.entries) {
+      if (suppliedNamedParameters.contains(entry.key)) continue;
+      entry.value.node.recordNamedParameterNotSupplied(_guards, _graph,
+          NamedParameterNotSuppliedOrigin(_source, argumentList.offset));
+    }
+  }
+
   DecoratedType _handlePropertyAccess(
       Expression node, Expression target, SimpleIdentifier propertyName) {
     DecoratedType targetType;
@@ -610,7 +834,8 @@
     }
     var callee = propertyName.staticElement;
     if (callee == null) {
-      throw new UnimplementedError('TODO(paulberry)');
+      // TODO(paulberry)
+      _unimplemented(node, 'Unresolved property access');
     }
     var calleeType = getOrComputeElementType(callee, targetType: targetType);
     // TODO(paulberry): substitute if necessary
@@ -647,7 +872,9 @@
       case TokenType.QUESTION_PERIOD:
         return true;
       default:
-        throw new UnimplementedError('TODO(paulberry)');
+        // TODO(paulberry)
+        _unimplemented(
+            expression, 'Conditional expression with operator ${token.lexeme}');
     }
   }
 
@@ -666,6 +893,7 @@
   }
 
   bool _isVariableOrParameterReference(Expression expression) {
+    expression = expression.unParenthesized;
     if (expression is SimpleIdentifier) {
       var element = expression.staticElement;
       if (element is LocalVariableElement) return true;
@@ -673,6 +901,37 @@
     }
     return false;
   }
+
+  @alwaysThrows
+  void _unimplemented(AstNode node, String message) {
+    CompilationUnit unit = node.root as CompilationUnit;
+    StringBuffer buffer = StringBuffer();
+    buffer.write(message);
+    buffer.write(' in "');
+    buffer.write(node.toSource());
+    buffer.write('" on line ');
+    buffer.write(unit.lineInfo.getLocation(node.offset).lineNumber);
+    buffer.write(' of "');
+    buffer.write(unit.declaredElement.source.fullName);
+    buffer.write('"');
+    throw UnimplementedError(buffer.toString());
+  }
+
+  void _unionDecoratedTypes(
+      DecoratedType x, DecoratedType y, EdgeOrigin origin) {
+    _graph.union(x.node, y.node, origin);
+    if (x.typeArguments.isNotEmpty ||
+        y.typeArguments.isNotEmpty ||
+        x.returnType != null ||
+        y.returnType != null ||
+        x.positionalParameters.isNotEmpty ||
+        y.positionalParameters.isNotEmpty ||
+        x.namedParameters.isNotEmpty ||
+        y.namedParameters.isNotEmpty) {
+      // TODO(paulberry)
+      throw UnimplementedError('_unionDecoratedTypes($x, $y, $origin)');
+    }
+  }
 }
 
 /// Information about a binary expression whose boolean value could possibly
diff --git a/pkg/nnbd_migration/lib/src/node_builder.dart b/pkg/nnbd_migration/lib/src/node_builder.dart
index 44a7475..202fe0a 100644
--- a/pkg/nnbd_migration/lib/src/node_builder.dart
+++ b/pkg/nnbd_migration/lib/src/node_builder.dart
@@ -10,12 +10,15 @@
 import 'package:analyzer/src/generated/resolver.dart';
 import 'package:analyzer/src/generated/source.dart';
 import 'package:front_end/src/scanner/token.dart';
+import 'package:meta/meta.dart';
 import 'package:nnbd_migration/nnbd_migration.dart';
 import 'package:nnbd_migration/src/conditional_discard.dart';
 import 'package:nnbd_migration/src/decorated_type.dart';
 import 'package:nnbd_migration/src/expression_checks.dart';
 import 'package:nnbd_migration/src/nullability_node.dart';
 
+import 'edge_origin.dart';
+
 /// Visitor that builds nullability nodes based on visiting code to be migrated.
 ///
 /// The return type of each `visit...` method is a [DecoratedType] indicating
@@ -30,12 +33,14 @@
   final Source _source;
 
   /// If the parameters of a function or method are being visited, the
-  /// [DecoratedType] of the corresponding function or method type.
-  ///
-  /// TODO(paulberry): should this be updated when we visit generic function
-  /// type syntax?  How about when we visit old-style function-typed formal
-  /// parameters?
-  DecoratedType _currentFunctionType;
+  /// [DecoratedType]s of the function's named parameters that have been seen so
+  /// far.  Otherwise `null`.
+  Map<String, DecoratedType> _namedParameters;
+
+  /// If the parameters of a function or method are being visited, the
+  /// [DecoratedType]s of the function's positional parameters that have been
+  /// seen so far.  Otherwise `null`.
+  List<DecoratedType> _positionalParameters;
 
   final NullabilityMigrationListener /*?*/ listener;
 
@@ -56,12 +61,18 @@
         ? new DecoratedType(
             DynamicTypeImpl.instance,
             NullabilityNode.forInferredDynamicType(
-                _graph, enclosingNode.offset))
+                _graph, _source, enclosingNode.offset))
         : type.accept(this);
   }
 
   @override
   DecoratedType visitConstructorDeclaration(ConstructorDeclaration node) {
+    if (node.factoryKeyword != null) {
+      // Factory constructors can return null, but we don't want to propagate a
+      // null type if we can prove that null is never returned.
+      // TODO(brianwilkerson)
+      _unimplemented(node, 'Declaration of a factory constructor');
+    }
     _handleExecutableDeclaration(
         node.declaredElement, null, node.parameters, node.body, node);
     return null;
@@ -73,12 +84,22 @@
     if (node.declaredElement.hasRequired || node.defaultValue != null) {
       return null;
     }
+    if (decoratedType == null) {
+      throw StateError('No type computed for ${node.parameter.runtimeType} '
+          '(${node.parent.parent.toSource()}) offset=${node.offset}');
+    }
     decoratedType.node.trackPossiblyOptional();
     _variables.recordPossiblyOptional(_source, node, decoratedType.node);
     return null;
   }
 
   @override
+  DecoratedType visitFieldFormalParameter(FieldFormalParameter node) {
+    // TODO(brianwilkerson)
+    _unimplemented(node, 'FieldFormalParameter');
+  }
+
+  @override
   DecoratedType visitFormalParameter(FormalParameter node) {
     // Do not visit children
     // TODO(paulberry): handle all types of formal parameters
@@ -98,6 +119,19 @@
   }
 
   @override
+  DecoratedType visitFunctionTypeAlias(FunctionTypeAlias node) {
+    // TODO(brianwilkerson)
+    _unimplemented(node, 'FunctionTypeAlias');
+  }
+
+  @override
+  DecoratedType visitFunctionTypedFormalParameter(
+      FunctionTypedFormalParameter node) {
+    // TODO(brianwilkerson)
+    _unimplemented(node, 'FunctionTypedFormalParameter');
+  }
+
+  @override
   DecoratedType visitMethodDeclaration(MethodDeclaration node) {
     _handleExecutableDeclaration(node.declaredElement, node.returnType,
         node.parameters, node.body, node);
@@ -127,9 +161,9 @@
     var declaredElement = node.declaredElement;
     _variables.recordDecoratedElementType(declaredElement, type);
     if (declaredElement.isNamed) {
-      _currentFunctionType.namedParameters[declaredElement.name] = type;
+      _namedParameters[declaredElement.name] = type;
     } else {
-      _currentFunctionType.positionalParameters.add(type);
+      _positionalParameters.add(type);
     }
     return type;
   }
@@ -140,7 +174,8 @@
     var type = node.type;
     if (type.isVoid || type.isDynamic) {
       var nullabilityNode = NullabilityNode.forTypeAnnotation(node.end);
-      _graph.connect(NullabilityNode.always, nullabilityNode);
+      _graph.connect(_graph.always, nullabilityNode,
+          AlwaysNullableTypeOrigin(_source, node.offset));
       var decoratedType =
           DecoratedTypeAnnotation(type, nullabilityNode, node.offset);
       _variables.recordDecoratedTypeAnnotation(_source, node, decoratedType,
@@ -153,9 +188,13 @@
     var namedParameters = const <String, DecoratedType>{};
     if (type is InterfaceType && type.typeParameters.isNotEmpty) {
       if (node is TypeName) {
-        assert(node.typeArguments != null);
-        typeArguments =
-            node.typeArguments.arguments.map((t) => t.accept(this)).toList();
+        if (node.typeArguments == null) {
+          typeArguments =
+              type.typeArguments.map(_decorateImplicitTypeArgument).toList();
+        } else {
+          typeArguments =
+              node.typeArguments.arguments.map((t) => t.accept(this)).toList();
+        }
       } else {
         assert(false); // TODO(paulberry): is this possible?
       }
@@ -163,11 +202,24 @@
     if (node is GenericFunctionType) {
       returnType = decorateType(node.returnType, node);
       if (node.typeParameters != null) {
-        throw UnimplementedError('TODO(paulberry)');
+        // TODO(paulberry)
+        _unimplemented(node, 'Generic function type with type parameters');
       }
       positionalParameters = <DecoratedType>[];
       namedParameters = <String, DecoratedType>{};
     }
+    if (node is GenericFunctionType) {
+      var previousPositionalParameters = _positionalParameters;
+      var previousNamedParameters = _namedParameters;
+      try {
+        _positionalParameters = positionalParameters;
+        _namedParameters = namedParameters;
+        node.parameters.accept(this);
+      } finally {
+        _positionalParameters = previousPositionalParameters;
+        _namedParameters = previousNamedParameters;
+      }
+    }
     var decoratedType = DecoratedTypeAnnotation(
         type, NullabilityNode.forTypeAnnotation(node.end), node.end,
         typeArguments: typeArguments,
@@ -175,21 +227,16 @@
         positionalParameters: positionalParameters,
         namedParameters: namedParameters);
     _variables.recordDecoratedTypeAnnotation(_source, node, decoratedType);
-    if (node is GenericFunctionType) {
-      var previousFunctionType = _currentFunctionType;
-      try {
-        _currentFunctionType = decoratedType;
-        node.parameters.accept(this);
-      } finally {
-        _currentFunctionType = previousFunctionType;
-      }
-    }
-    switch (_classifyComment(node.endToken.next.precedingComments)) {
+    var commentToken = node.endToken.next.precedingComments;
+    switch (_classifyComment(commentToken)) {
       case _NullabilityComment.bang:
-        _graph.connect(decoratedType.node, NullabilityNode.never, hard: true);
+        _graph.connect(decoratedType.node, _graph.never,
+            NullabilityCommentOrigin(_source, commentToken.offset),
+            hard: true);
         break;
       case _NullabilityComment.question:
-        _graph.connect(NullabilityNode.always, decoratedType.node);
+        _graph.connect(_graph.always, decoratedType.node,
+            NullabilityCommentOrigin(_source, commentToken.offset));
         break;
       case _NullabilityComment.none:
         break;
@@ -204,8 +251,10 @@
   DecoratedType visitTypeParameter(TypeParameter node) {
     var element = node.declaredElement;
     var decoratedBound = node.bound?.accept(this) ??
-        DecoratedType(element.bound ?? _typeProvider.objectType,
-            NullabilityNode.forInferredDynamicType(_graph, node.offset));
+        DecoratedType(
+            element.bound ?? _typeProvider.objectType,
+            NullabilityNode.forInferredDynamicType(
+                _graph, _source, node.offset));
     _variables.recordDecoratedElementType(element, decoratedBound);
     return null;
   }
@@ -227,6 +276,22 @@
     return _NullabilityComment.none;
   }
 
+  /// Creates a DecoratedType corresponding to [type], with fresh nullability
+  /// nodes everywhere that don't correspond to any source location.  These
+  /// nodes can later be unioned with other nodes.
+  DecoratedType _decorateImplicitTypeArgument(DartType type) {
+    if (type.isDynamic) {
+      return DecoratedType(type, _graph.always);
+    } else if (type is InterfaceType) {
+      return DecoratedType(type, NullabilityNode.forInferredType(),
+          typeArguments:
+              type.typeArguments.map(_decorateImplicitTypeArgument).toList());
+    }
+    // TODO(paulberry)
+    throw UnimplementedError(
+        '_decorateImplicitTypeArgument(${type.runtimeType})');
+  }
+
   /// Common handling of function and method declarations.
   void _handleExecutableDeclaration(
       ExecutableElement declaredElement,
@@ -234,24 +299,61 @@
       FormalParameterList parameters,
       FunctionBody body,
       AstNode enclosingNode) {
-    var decoratedReturnType = decorateType(returnType, enclosingNode);
-    var previousFunctionType = _currentFunctionType;
-    // TODO(paulberry): test that it's correct to use `null` for the nullability
-    // of the function type
-    var functionType = DecoratedType(
-        declaredElement.type, NullabilityNode.never,
-        returnType: decoratedReturnType,
-        positionalParameters: [],
-        namedParameters: {});
-    _currentFunctionType = functionType;
+    DecoratedType decoratedReturnType;
+    if (returnType == null && declaredElement is ConstructorElement) {
+      // Constructors have no explicit return type annotation, so use the
+      // implicit return type.
+      if (declaredElement.isFactory) {
+        // Factory constructors can return null, but we don't want to propagate
+        // a null type if we can prove that null is never returned.
+        // TODO(brianwilkerson)
+        _unimplemented(
+            parameters.parent, 'Declaration of a factory constructor');
+      }
+      if (declaredElement.enclosingElement.typeParameters.isNotEmpty) {
+        // Need to decorate the type parameters appropriately.
+        // TODO(paulberry,brianwilkerson)
+        _unimplemented(parameters.parent,
+            'Declaration of a constructor with type parameters');
+      }
+      decoratedReturnType = new DecoratedType(
+          declaredElement.enclosingElement.type, _graph.never);
+    } else {
+      decoratedReturnType = decorateType(returnType, enclosingNode);
+    }
+    var previousPositionalParameters = _positionalParameters;
+    var previousNamedParameters = _namedParameters;
+    _positionalParameters = [];
+    _namedParameters = {};
+    DecoratedType functionType;
     try {
       parameters?.accept(this);
       body?.accept(this);
+      functionType = DecoratedType(declaredElement.type, _graph.never,
+          returnType: decoratedReturnType,
+          positionalParameters: _positionalParameters,
+          namedParameters: _namedParameters);
     } finally {
-      _currentFunctionType = previousFunctionType;
+      _positionalParameters = previousPositionalParameters;
+      _namedParameters = previousNamedParameters;
     }
     _variables.recordDecoratedElementType(declaredElement, functionType);
   }
+
+  @alwaysThrows
+  void _unimplemented(AstNode node, String message) {
+    CompilationUnit unit = node.root as CompilationUnit;
+    StringBuffer buffer = StringBuffer();
+    buffer.write(message);
+    buffer.write(' in "');
+    buffer.write(node.toSource());
+    buffer.write('" on line ');
+    buffer.write(unit.lineInfo.getLocation(node.offset).lineNumber);
+    buffer.write(' of "');
+    buffer.write(unit.declaredElement.source.fullName);
+    buffer.write('"');
+    throw UnimplementedError(buffer.toString());
+  }
 }
 
 /// Repository of constraint variables and decorated types corresponding to the
diff --git a/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart b/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart
index a94f87b..3d405a3 100644
--- a/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart
+++ b/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart
@@ -18,9 +18,9 @@
 class NullabilityMigrationImpl implements NullabilityMigration {
   final NullabilityMigrationListener listener;
 
-  final _variables = Variables();
+  final Variables _variables;
 
-  final _graph = NullabilityGraph();
+  final NullabilityGraph _graph;
 
   final bool _permissive;
 
@@ -30,8 +30,12 @@
   /// as far as possible even though the migration algorithm is not yet
   /// complete.  TODO(paulberry): remove this mode once the migration algorithm
   /// is fully implemented.
-  NullabilityMigrationImpl(this.listener, {bool permissive: false})
-      : _permissive = permissive;
+  NullabilityMigrationImpl(NullabilityMigrationListener listener,
+      {bool permissive: false})
+      : this._(listener, NullabilityGraph(), permissive);
+
+  NullabilityMigrationImpl._(this.listener, this._graph, this._permissive)
+      : _variables = Variables(_graph);
 
   void finish() {
     _graph.propagate();
diff --git a/pkg/nnbd_migration/lib/src/nullability_node.dart b/pkg/nnbd_migration/lib/src/nullability_node.dart
index 4abbc72..d71920e 100644
--- a/pkg/nnbd_migration/lib/src/nullability_node.dart
+++ b/pkg/nnbd_migration/lib/src/nullability_node.dart
@@ -2,8 +2,11 @@
 // 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 'package:analyzer/src/generated/source.dart';
 import 'package:meta/meta.dart';
 
+import 'edge_origin.dart';
+
 /// Data structure to keep track of the relationship from one [NullabilityNode]
 /// object to another [NullabilityNode] that is "downstream" from it (meaning
 /// that if the former node is nullable, then the latter node will either have
@@ -17,13 +20,58 @@
   /// need to be made nullable if all the source nodes are nullable.
   final List<NullabilityNode> sources;
 
-  final bool hard;
+  final _NullabilityEdgeKind _kind;
 
-  NullabilityEdge(this.destinationNode, this.sources, this.hard);
+  /// An [EdgeOrigin] object indicating what was found in the source code that
+  /// caused the edge to be generated.
+  final EdgeOrigin origin;
+
+  NullabilityEdge._(
+      this.destinationNode, this.sources, this._kind, this.origin);
 
   Iterable<NullabilityNode> get guards => sources.skip(1);
 
+  bool get hard => _kind != _NullabilityEdgeKind.soft;
+
+  /// Indicates whether nullability was successfully propagated through this
+  /// edge.
+  bool get isSatisfied {
+    if (!_isTriggered) return true;
+    return destinationNode.isNullable;
+  }
+
+  bool get isUnion => _kind == _NullabilityEdgeKind.union;
+
   NullabilityNode get primarySource => sources.first;
+
+  /// Indicates whether all the sources of this edge are nullable (and thus
+  /// downstream nullability propagation should try to make the destination node
+  /// nullable, if possible).
+  bool get _isTriggered {
+    for (var source in sources) {
+      if (!source.isNullable) return false;
+    }
+    return true;
+  }
+
+  @override
+  String toString() {
+    var edgeDecorations = <Object>[];
+    switch (_kind) {
+      case _NullabilityEdgeKind.soft:
+        break;
+      case _NullabilityEdgeKind.hard:
+        edgeDecorations.add('hard');
+        break;
+      case _NullabilityEdgeKind.union:
+        edgeDecorations.add('union');
+        break;
+    }
+    edgeDecorations.addAll(guards);
+    var edgeDecoration =
+        edgeDecorations.isEmpty ? '' : '-(${edgeDecorations.join(', ')})';
+    return '$primarySource $edgeDecoration-> $destinationNode';
+  }
 }
 
 /// Data structure to keep track of the relationship between [NullabilityNode]
@@ -37,44 +85,72 @@
   /// `sourceNode` argument to [connect].
   final _allSourceNodes = Set<NullabilityNode>.identity();
 
-  /// List of [NullabilityEdge] objects that are downstream from
-  /// [NullabilityNode.always].  (They can't be stored in
-  /// [NullabilityNode.always] directly because it is immutable).
-  final _downstreamFromAlways = <NullabilityEdge>[];
+  /// Returns a [NullabilityNode] that is a priori nullable.
+  ///
+  /// Propagation of nullability always proceeds downstream starting at this
+  /// node.
+  final NullabilityNode always = _NullabilityNodeImmutable('always', true);
 
-  /// List of [NullabilityEdge] objects that are upstream from
-  /// [NullabilityNode.never] due to unconditional control flow.  (They can't be
-  /// stored in [NullabilityNode.never] directly because it is immutable).
-  final _upstreamFromNever = <NullabilityEdge>[];
-
-  /// List of [NullabilityNodeMutable] objects that were set into the nullable
-  /// state by a process other than nullability propagation.  The next time
-  /// nullability is propagated, the propagation algorithm will ensure that
-  /// edges originating at these nodes are examined.
-  final _pendingDownstreamNodes = <NullabilityNodeMutable>[];
+  /// Returns a [NullabilityNode] that is a priori non-nullable.
+  ///
+  /// Propagation of nullability always proceeds upstream starting at this
+  /// node.
+  final NullabilityNode never = _NullabilityNodeImmutable('never', false);
 
   /// Records that [sourceNode] is immediately upstream from [destinationNode].
-  void connect(NullabilityNode sourceNode, NullabilityNode destinationNode,
+  ///
+  /// Returns the edge created by the connection.
+  NullabilityEdge connect(NullabilityNode sourceNode,
+      NullabilityNode destinationNode, EdgeOrigin origin,
       {bool hard: false, List<NullabilityNode> guards: const []}) {
     var sources = [sourceNode]..addAll(guards);
-    var edge = NullabilityEdge(destinationNode, sources, hard);
+    var kind = hard ? _NullabilityEdgeKind.hard : _NullabilityEdgeKind.soft;
+    return _connect(sources, destinationNode, kind, origin);
+  }
+
+  /// Determines the nullability of each node in the graph by propagating
+  /// nullability information from one node to another.
+  ///
+  /// Returns a list of edges that couldn't be satisfied.
+  List<NullabilityEdge> propagate() {
+    if (_debugBeforePropagation) _debugDump();
+    var nullableNodes = _propagateAlways();
+    _propagateUpstream();
+    return _propagateDownstream(nullableNodes);
+  }
+
+  /// Records that nodes [x] and [y] should have exactly the same nullability.
+  void union(NullabilityNode x, NullabilityNode y, EdgeOrigin origin) {
+    _connect([x], y, _NullabilityEdgeKind.union, origin);
+    _connect([y], x, _NullabilityEdgeKind.union, origin);
+  }
+
+  NullabilityEdge _connect(
+      List<NullabilityNode> sources,
+      NullabilityNode destinationNode,
+      _NullabilityEdgeKind kind,
+      EdgeOrigin origin) {
+    var edge = NullabilityEdge._(destinationNode, sources, kind, origin);
     for (var source in sources) {
       _connectDownstream(source, edge);
     }
-    if (destinationNode is NullabilityNodeMutable) {
-      destinationNode._upstreamEdges.add(edge);
-    } else if (destinationNode == NullabilityNode.never) {
-      _upstreamFromNever.add(edge);
-    } else {
-      // We don't need to track nodes that are upstream from `always` because
-      // `always` will never have non-null intent.
-      assert(destinationNode == NullabilityNode.always);
+    destinationNode._upstreamEdges.add(edge);
+    return edge;
+  }
+
+  void _connectDownstream(NullabilityNode source, NullabilityEdge edge) {
+    _allSourceNodes.add(source);
+    source._downstreamEdges.add(edge);
+    if (source is _NullabilityNodeCompound) {
+      for (var component in source._components) {
+        _connectDownstream(component, edge);
+      }
     }
   }
 
-  void debugDump() {
+  void _debugDump() {
     for (var source in _allSourceNodes) {
-      var edges = _getDownstreamEdges(source);
+      var edges = source._downstreamEdges;
       var destinations =
           edges.where((edge) => edge.primarySource == source).map((edge) {
         var suffixes = <Object>[];
@@ -90,104 +166,50 @@
     }
   }
 
-  /// Iterates through all nodes that are "upstream" of [node] due to
-  /// unconditional control flow.
+  /// Propagates nullability downstream along union edges from "always".
   ///
-  /// There is no guarantee of uniqueness of the iterated nodes.
-  Iterable<NullabilityEdge> getUpstreamEdges(NullabilityNode node) {
-    if (node is NullabilityNodeMutable) {
-      return node._upstreamEdges;
-    } else if (node == NullabilityNode.never) {
-      return _upstreamFromNever;
-    } else {
-      // No nodes are upstream from `always`.
-      assert(node == NullabilityNode.always);
-      return const [];
-    }
-  }
-
-  /// Iterates through all nodes that are "upstream" of [node] (i.e. if
-  /// any of the iterated nodes are nullable, then [node] will either have to be
-  /// nullable, or null checks will have to be added).
-  ///
-  /// There is no guarantee of uniqueness of the iterated nodes.
-  ///
-  /// This method is inefficent since it has to search the entire graph, so it
-  /// is for testing only.
-  @visibleForTesting
-  Iterable<NullabilityNode> getUpstreamNodesForTesting(
-      NullabilityNode node) sync* {
-    for (var source in _allSourceNodes) {
-      for (var edge in _getDownstreamEdges(source)) {
-        if (edge.destinationNode == node) {
-          yield source;
-        }
+  /// Returns a list of nodes that are nullable after this operation (including
+  /// "always")
+  List<NullabilityNode> _propagateAlways() {
+    var nullableNodes = <NullabilityNode>[always];
+    var pendingEdges = always._downstreamEdges.toList();
+    while (pendingEdges.isNotEmpty) {
+      var edge = pendingEdges.removeLast();
+      if (!edge.isUnion) continue;
+      // Union edges always have exactly one source, so we don't need to check
+      // whether all sources are nullable.
+      assert(edge.sources.length == 1);
+      var node = edge.destinationNode;
+      if (node is NullabilityNodeMutable && !node.isNullable) {
+        nullableNodes.add(node);
+        node._state = _NullabilityState.ordinaryNullable;
+        // Was not previously nullable, so we need to propagate.
+        pendingEdges.addAll(node._downstreamEdges);
       }
     }
-  }
-
-  /// Determines the nullability of each node in the graph by propagating
-  /// nullability information from one node to another.
-  void propagate() {
-    if (_debugBeforePropagation) debugDump();
-    _propagateUpstream();
-    _propagateDownstream();
-  }
-
-  void _connectDownstream(NullabilityNode source, NullabilityEdge edge) {
-    _allSourceNodes.add(source);
-    if (source is NullabilityNodeMutable) {
-      source._downstreamEdges.add(edge);
-      if (source is _NullabilityNodeCompound) {
-        for (var component in source._components) {
-          _connectDownstream(component, edge);
-        }
-      }
-    } else if (source == NullabilityNode.always) {
-      _downstreamFromAlways.add(edge);
-    } else {
-      // We don't need to track nodes that are downstream from `never` because
-      // `never` will never be nullable.
-      assert(source == NullabilityNode.never);
-    }
-  }
-
-  Iterable<NullabilityEdge> _getDownstreamEdges(NullabilityNode node) {
-    if (node is NullabilityNodeMutable) {
-      return node._downstreamEdges;
-    } else if (node == NullabilityNode.always) {
-      return _downstreamFromAlways;
-    } else {
-      // No nodes are downstream from `never`.
-      assert(node == NullabilityNode.never);
-      return const [];
-    }
+    return nullableNodes;
   }
 
   /// Propagates nullability downstream.
-  void _propagateDownstream() {
-    var pendingEdges = <NullabilityEdge>[]..addAll(_downstreamFromAlways);
-    for (var node in _pendingDownstreamNodes) {
+  List<NullabilityEdge> _propagateDownstream(
+      List<NullabilityNode> nullableNodes) {
+    var unsatisfiedEdges = <NullabilityEdge>[];
+    var pendingEdges = <NullabilityEdge>[];
+    for (var node in nullableNodes) {
       pendingEdges.addAll(node._downstreamEdges);
     }
-    _pendingDownstreamNodes.clear();
     var pendingSubstitutions = <NullabilityNodeForSubstitution>[];
     while (true) {
-      nextEdge:
       while (pendingEdges.isNotEmpty) {
         var edge = pendingEdges.removeLast();
+        if (!edge._isTriggered) continue;
         var node = edge.destinationNode;
         if (node._state == _NullabilityState.nonNullable) {
-          // Non-nullable nodes are never made nullable; a null check will need
-          // to be added instead.
+          // The node has already been marked as non-nullable, so the edge can't
+          // be satisfied.
+          unsatisfiedEdges.add(edge);
           continue;
         }
-        for (var source in edge.sources) {
-          if (!source.isNullable) {
-            // Not all sources are nullable, so this edge doesn't apply yet.
-            continue nextEdge;
-          }
-        }
         if (node is NullabilityNodeMutable && !node.isNullable) {
           node._state = _NullabilityState.ordinaryNullable;
           // Was not previously nullable, so we need to propagate.
@@ -206,14 +228,16 @@
       }
       // Heuristically choose to propagate to the inner node since this seems
       // to lead to better quality migrations.
-      pendingEdges.add(NullabilityEdge(node.innerNode, const [], false));
+      pendingEdges.add(NullabilityEdge._(node.innerNode, const [],
+          _NullabilityEdgeKind.soft, _SubstitutionHeuristicOrigin()));
     }
+    return unsatisfiedEdges;
   }
 
   /// Propagates non-null intent upstream along unconditional control flow
   /// lines.
   void _propagateUpstream() {
-    var pendingEdges = <NullabilityEdge>[]..addAll(_upstreamFromNever);
+    var pendingEdges = never._upstreamEdges.toList();
     while (pendingEdges.isNotEmpty) {
       var edge = pendingEdges.removeLast();
       if (!edge.hard) continue;
@@ -229,6 +253,19 @@
   }
 }
 
+/// Same as [NullabilityGraph], but extended with extra methods for easier
+/// testing.
+@visibleForTesting
+class NullabilityGraphForTesting extends NullabilityGraph {
+  /// Iterates through all edges that have this node as their destination.
+  ///
+  /// There is no guarantee of uniqueness of the iterated nodes.
+  @visibleForTesting
+  Iterable<NullabilityEdge> getUpstreamEdges(NullabilityNode node) {
+    return node._upstreamEdges;
+  }
+}
+
 /// Representation of a single node in the nullability inference graph.
 ///
 /// Initially, this is just a wrapper over constraint variables, and the
@@ -236,35 +273,38 @@
 /// variables.  Over time this will be replaced by a first class representation
 /// of the nullability inference graph.
 abstract class NullabilityNode {
-  /// [NullabilityNode] used for types that are known a priori to be nullable
-  /// (e.g. the type of the `null` literal).
-  static final NullabilityNode always =
-      _NullabilityNodeImmutable('always', true);
-
-  /// [NullabilityNode] used for types that are known a priori to be
-  /// non-nullable (e.g. the type of an integer literal).
-  static final NullabilityNode never =
-      _NullabilityNodeImmutable('never', false);
-
   static final _debugNamesInUse = Set<String>();
 
   bool _isPossiblyOptional = false;
 
   String _debugName;
 
+  /// List of [NullabilityEdge] objects describing this node's relationship to
+  /// other nodes that are "downstream" from it (meaning that if a key node is
+  /// nullable, then all the nodes in the corresponding value will either have
+  /// to be nullable, or null checks will have to be added).
+  final _downstreamEdges = <NullabilityEdge>[];
+
+  /// List of edges that have this node as their destination.
+  final _upstreamEdges = <NullabilityEdge>[];
+
   /// Creates a [NullabilityNode] representing the nullability of a variable
   /// whose type is `dynamic` due to type inference.
   ///
   /// TODO(paulberry): this should go away; we should decorate the actual
   /// inferred type rather than assuming `dynamic`.
   factory NullabilityNode.forInferredDynamicType(
-      NullabilityGraph graph, int offset) {
-    var node = _NullabilityNodeSimple('inferredDynamic($offset)',
-        initialState: _NullabilityState.ordinaryNullable);
-    graph._pendingDownstreamNodes.add(node);
+      NullabilityGraph graph, Source source, int offset) {
+    var node = _NullabilityNodeSimple('inferredDynamic($offset)');
+    graph.union(node, graph.always, AlwaysNullableTypeOrigin(source, offset));
     return node;
   }
 
+  /// Creates a [NullabilityNode] representing the nullability of a variable
+  /// whose type is determined by type inference.
+  factory NullabilityNode.forInferredType() =>
+      _NullabilityNodeSimple('inferred');
+
   /// Creates a [NullabilityNode] representing the nullability of an
   /// expression which is nullable iff both [a] and [b] are nullable.
   ///
@@ -272,7 +312,7 @@
   /// [joinNullabilities] callback.  TODO(paulberry): this should become
   /// unnecessary once constraint solving is performed directly using
   /// [NullabilityNode] objects.
-  factory NullabilityNode.forLUB(NullabilityNode a, NullabilityNode b) =
+  factory NullabilityNode.forLUB(NullabilityNode left, NullabilityNode right) =
       NullabilityNodeForLUB._;
 
   /// Creates a [NullabilityNode] representing the nullability of a type
@@ -299,8 +339,7 @@
 
   /// Gets a string that can be appended to a type name during debugging to help
   /// annotate the nullability of that type.
-  String get debugSuffix =>
-      this == always ? '?' : this == never ? '' : '?($this)';
+  String get debugSuffix => '?($this)';
 
   /// After nullability propagation, this getter can be used to query whether
   /// the type associated with this node should be considered nullable.
@@ -317,18 +356,13 @@
   /// Records the fact that an invocation was made to a function with named
   /// parameters, and the named parameter associated with this node was not
   /// supplied.
-  void recordNamedParameterNotSupplied(
-      List<NullabilityNode> guards, NullabilityGraph graph) {
+  void recordNamedParameterNotSupplied(List<NullabilityNode> guards,
+      NullabilityGraph graph, NamedParameterNotSuppliedOrigin origin) {
     if (isPossiblyOptional) {
-      graph.connect(NullabilityNode.always, this, guards: guards);
+      graph.connect(graph.always, this, origin, guards: guards);
     }
   }
 
-  void recordNonNullIntent(
-      List<NullabilityNode> guards, NullabilityGraph graph) {
-    graph.connect(this, NullabilityNode.never, hard: true);
-  }
-
   String toString() {
     if (_debugName == null) {
       var prefix = _debugPrefix;
@@ -353,25 +387,6 @@
   void trackPossiblyOptional() {
     _isPossiblyOptional = true;
   }
-
-  /// Connect the nullability nodes [sourceNode] and [destinationNode]
-  /// appopriately to account for an assignment in the source code being
-  /// analyzed.  Any constraints generated are recorded in [constraints].
-  ///
-  /// If [checkNotNull] is non-null, then it tracks the expression that may
-  /// require null-checking.
-  ///
-  /// [inConditionalControlFlow] indicates whether the assignment being analyzed
-  /// is reachable conditionally or unconditionally from the entry point of the
-  /// function; this affects how non-null intent is back-propagated.
-  static void recordAssignment(
-      NullabilityNode sourceNode,
-      NullabilityNode destinationNode,
-      List<NullabilityNode> guards,
-      NullabilityGraph graph,
-      {@required bool hard}) {
-    graph.connect(sourceNode, destinationNode, guards: guards, hard: hard);
-  }
 }
 
 /// Derived class for nullability nodes that arise from the least-upper-bound
@@ -419,22 +434,10 @@
 /// Base class for nullability nodes whose state can be mutated safely.
 ///
 /// Nearly all nullability nodes derive from this class; the only exceptions are
-/// the fixed nodes [NullabilityNode.always] and [NullabilityNode.never].
+/// the fixed nodes "always "never".
 abstract class NullabilityNodeMutable extends NullabilityNode {
   _NullabilityState _state;
 
-  /// List of [NullabilityEdge] objects describing this node's relationship to
-  /// other nodes that are "downstream" from it (meaning that if a key node is
-  /// nullable, then all the nodes in the corresponding value will either have
-  /// to be nullable, or null checks will have to be added).
-  final _downstreamEdges = <NullabilityEdge>[];
-
-  /// List of nodes that are "upstream" from this node via unconditional control
-  /// flow (meaning that if a node in the list is nullable, then there exists
-  /// code that is unguarded by an "if" statement that indicates that this node
-  /// will have to be nullable, or null checks will have to be added).
-  final _upstreamEdges = <NullabilityEdge>[];
-
   NullabilityNodeMutable._(
       {_NullabilityState initialState: _NullabilityState.undetermined})
       : _state = initialState,
@@ -444,6 +447,20 @@
   bool get isNullable => _state.isNullable;
 }
 
+/// Kinds of nullability edges
+enum _NullabilityEdgeKind {
+  /// Soft edge.  Propagates nullability downstream only.
+  soft,
+
+  /// Hard edge.  Propagates nullability downstream and non-nullability
+  /// upstream.
+  hard,
+
+  /// Union edge.  Indicates that two nodes should have exactly the same
+  /// nullability.
+  union,
+}
+
 abstract class _NullabilityNodeCompound extends NullabilityNodeMutable {
   _NullabilityNodeCompound() : super._();
 
@@ -463,6 +480,9 @@
   _NullabilityNodeImmutable(this._debugPrefix, this.isNullable) : super._();
 
   @override
+  String get debugSuffix => isNullable ? '?' : '';
+
+  @override
   _NullabilityState get _state => isNullable
       ? _NullabilityState.ordinaryNullable
       : _NullabilityState.nonNullable;
@@ -472,9 +492,8 @@
   @override
   final String _debugPrefix;
 
-  _NullabilityNodeSimple(this._debugPrefix,
-      {_NullabilityState initialState: _NullabilityState.undetermined})
-      : super._(initialState: initialState);
+  _NullabilityNodeSimple(this._debugPrefix)
+      : super._(initialState: _NullabilityState.undetermined);
 }
 
 /// State of a nullability node.
@@ -510,3 +529,5 @@
   @override
   String toString() => name;
 }
+
+class _SubstitutionHeuristicOrigin extends EdgeOrigin {}
diff --git a/pkg/nnbd_migration/lib/src/potential_modification.dart b/pkg/nnbd_migration/lib/src/potential_modification.dart
index 6bc0d16..c6059e8 100644
--- a/pkg/nnbd_migration/lib/src/potential_modification.dart
+++ b/pkg/nnbd_migration/lib/src/potential_modification.dart
@@ -33,7 +33,7 @@
           discard,
           _KeepNode(node.condition),
           _KeepNode(node.thenStatement),
-          _KeepNode(node.elseStatement));
+          node.elseStatement == null ? null : _KeepNode(node.elseStatement));
     } else {
       throw new UnimplementedError('TODO(paulberry)');
     }
@@ -58,7 +58,7 @@
     if (discard.keepTrue) {
       keepNodes.add(thenStatement); // TODO(paulberry): test
     }
-    if (discard.keepFalse) {
+    if (discard.keepFalse && elseStatement != null) {
       keepNodes.add(elseStatement); // TODO(paulberry): test
     }
     // TODO(paulberry): test thoroughly
diff --git a/pkg/nnbd_migration/lib/src/variables.dart b/pkg/nnbd_migration/lib/src/variables.dart
index df66005..9a8e653 100644
--- a/pkg/nnbd_migration/lib/src/variables.dart
+++ b/pkg/nnbd_migration/lib/src/variables.dart
@@ -13,6 +13,8 @@
 import 'package:nnbd_migration/src/potential_modification.dart';
 
 class Variables implements VariableRecorder, VariableRepository {
+  final NullabilityGraph _graph;
+
   final _decoratedElementTypes = <Element, DecoratedType>{};
 
   final _decoratedTypeAnnotations =
@@ -20,10 +22,12 @@
 
   final _potentialModifications = <Source, List<PotentialModification>>{};
 
+  Variables(this._graph);
+
   @override
   DecoratedType decoratedElementType(Element element, {bool create: false}) =>
       _decoratedElementTypes[element] ??= create
-          ? DecoratedType.forElement(element)
+          ? DecoratedType.forElement(element, _graph)
           : throw StateError('No element found');
 
   @override
diff --git a/pkg/nnbd_migration/test/api_test.dart b/pkg/nnbd_migration/test/api_test.dart
index 8f3e7d7..c378285 100644
--- a/pkg/nnbd_migration/test/api_test.dart
+++ b/pkg/nnbd_migration/test/api_test.dart
@@ -645,6 +645,45 @@
     await _checkSingleFileChanges(content, expected);
   }
 
+  test_genericType_noTypeArguments() async {
+    var content = '''
+void f(C c) {}
+class C<E> {}
+''';
+    var expected = '''
+void f(C c) {}
+class C<E> {}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
+  test_genericType_noTypeArguments_use_bound() async {
+    var content = '''
+abstract class C<T extends Object> { // (1)
+  void put(T t);
+  T get();
+}
+Object f(C c) => c.get();            // (2)
+void g(C<int> c) {                   // (3)
+  c.put(null);                       // (4)
+}
+''';
+    // (4) forces `...C<int?>...` at (3), which means (1) must be
+    // `...extends Object?`.  Therefore (2) is equivalent to
+    // `...f(C<Object?> c)...`, so the return type of `f` is `Object?`.
+    var expected = '''
+abstract class C<T extends Object?> { // (1)
+  void put(T t);
+  T get();
+}
+Object? f(C c) => c.get();            // (2)
+void g(C<int?> c) {                   // (3)
+  c.put(null);                       // (4)
+}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
   test_getter_topLevel() async {
     var content = '''
 int get g => 0;
@@ -655,6 +694,44 @@
     await _checkSingleFileChanges(content, expected);
   }
 
+  test_ifStatement_nullCheck_noElse() async {
+    var content = '''
+int f(int x) {
+  if (x == null) return 0;
+  return x;
+}
+''';
+    var expected = '''
+int f(int x) {
+  if (x == null) return 0;
+  return x;
+}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
+  test_instanceCreation_noTypeArguments_noParameters() async {
+    var content = '''
+void main() {
+  C c = C();
+  c.length;
+}
+class C {
+  int get length => 0;
+}
+''';
+    var expected = '''
+void main() {
+  C c = C();
+  c.length;
+}
+class C {
+  int get length => 0;
+}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
   test_named_parameter_no_default_unused() async {
     var content = '''
 void f({String s}) {}
@@ -960,6 +1037,32 @@
     await _checkSingleFileChanges(content, expected);
   }
 
+  test_prefixExpression_bang() async {
+    var content = '''
+bool f(bool b) => !b;
+void g(bool b1, bool b2) {
+  if (b1) {
+    f(b2);
+  }
+}
+main() {
+  g(false, null);
+}
+''';
+    var expected = '''
+bool f(bool b) => !b;
+void g(bool b1, bool? b2) {
+  if (b1) {
+    f(b2!);
+  }
+}
+main() {
+  g(false, null);
+}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
   test_single_file_multiple_changes() async {
     var content = '''
 int f() => null;
diff --git a/pkg/nnbd_migration/test/graph_builder_test.dart b/pkg/nnbd_migration/test/graph_builder_test.dart
new file mode 100644
index 0000000..f3a938e
--- /dev/null
+++ b/pkg/nnbd_migration/test/graph_builder_test.dart
@@ -0,0 +1,1706 @@
+// Copyright (c) 2019, 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 'package:analyzer/dart/ast/ast.dart';
+import 'package:nnbd_migration/src/decorated_type.dart';
+import 'package:nnbd_migration/src/expression_checks.dart';
+import 'package:nnbd_migration/src/graph_builder.dart';
+import 'package:nnbd_migration/src/nullability_node.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'migration_visitor_test_base.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(GraphBuilderTest);
+  });
+}
+
+@reflectiveTest
+class GraphBuilderTest extends MigrationVisitorTestBase {
+  /// Analyzes the given source code, producing constraint variables and
+  /// constraints for it.
+  @override
+  Future<CompilationUnit> analyze(String code) async {
+    var unit = await super.analyze(code);
+    unit.accept(GraphBuilder(typeProvider, variables, graph, testSource, null));
+    return unit;
+  }
+
+  void assertConditional(
+      NullabilityNode node, NullabilityNode left, NullabilityNode right) {
+    var conditionalNode = node as NullabilityNodeForLUB;
+    expect(conditionalNode.left, same(left));
+    expect(conditionalNode.right, same(right));
+  }
+
+  /// Checks that there are no nullability nodes upstream from [node] that could
+  /// cause it to become nullable.
+  void assertNoUpstreamNullability(NullabilityNode node) {
+    // never can never become nullable, even if it has nodes
+    // upstream from it.
+    if (node == never) return;
+
+    for (var edge in graph.getUpstreamEdges(node)) {
+      expect(edge.primarySource, never);
+    }
+  }
+
+  /// Verifies that a null check will occur when the given edge is unsatisfied.
+  ///
+  /// [expressionChecks] is the object tracking whether or not a null check is
+  /// needed.
+  void assertNullCheck(
+      ExpressionChecks expressionChecks, NullabilityEdge expectedEdge) {
+    expect(expressionChecks.edges, contains(expectedEdge));
+  }
+
+  /// Gets the [ExpressionChecks] associated with the expression whose text
+  /// representation is [text], or `null` if the expression has no
+  /// [ExpressionChecks] associated with it.
+  ExpressionChecks checkExpression(String text) {
+    return variables.checkExpression(findNode.expression(text));
+  }
+
+  /// Gets the [DecoratedType] associated with the expression whose text
+  /// representation is [text], or `null` if the expression has no
+  /// [DecoratedType] associated with it.
+  DecoratedType decoratedExpressionType(String text) {
+    return variables.decoratedExpressionType(findNode.expression(text));
+  }
+
+  test_assert_demonstrates_non_null_intent() async {
+    await analyze('''
+void f(int i) {
+  assert(i != null);
+}
+''');
+
+    assertEdge(decoratedTypeAnnotation('int i').node, never, hard: true);
+  }
+
+  test_assignmentExpression_field() async {
+    await analyze('''
+class C {
+  int x = 0;
+}
+void f(C c, int i) {
+  c.x = i;
+}
+''');
+    assertEdge(decoratedTypeAnnotation('int i').node,
+        decoratedTypeAnnotation('int x').node,
+        hard: true);
+  }
+
+  test_assignmentExpression_field_cascaded() async {
+    await analyze('''
+class C {
+  int x = 0;
+}
+void f(C c, int i) {
+  c..x = i;
+}
+''');
+    assertEdge(decoratedTypeAnnotation('int i').node,
+        decoratedTypeAnnotation('int x').node,
+        hard: true);
+  }
+
+  test_assignmentExpression_field_target_check() async {
+    await analyze('''
+class C {
+  int x = 0;
+}
+void f(C c, int i) {
+  c.x = i;
+}
+''');
+    assertNullCheck(checkExpression('c.x'),
+        assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true));
+  }
+
+  test_assignmentExpression_field_target_check_cascaded() async {
+    await analyze('''
+class C {
+  int x = 0;
+}
+void f(C c, int i) {
+  c..x = i;
+}
+''');
+    assertNullCheck(checkExpression('c..x'),
+        assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true));
+  }
+
+  test_assignmentExpression_indexExpression_index() async {
+    await analyze('''
+class C {
+  void operator[]=(int a, int b) {}
+}
+void f(C c, int i, int j) {
+  c[i] = j;
+}
+''');
+    assertEdge(decoratedTypeAnnotation('int i').node,
+        decoratedTypeAnnotation('int a').node,
+        hard: true);
+  }
+
+  test_assignmentExpression_indexExpression_return_value() async {
+    await analyze('''
+class C {
+  void operator[]=(int a, int b) {}
+}
+int f(C c, int i, int j) => c[i] = j;
+''');
+    assertEdge(decoratedTypeAnnotation('int j').node,
+        decoratedTypeAnnotation('int f').node,
+        hard: false);
+  }
+
+  test_assignmentExpression_indexExpression_target_check() async {
+    await analyze('''
+class C {
+  void operator[]=(int a, int b) {}
+}
+void f(C c, int i, int j) {
+  c[i] = j;
+}
+''');
+    assertNullCheck(checkExpression('c['),
+        assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true));
+  }
+
+  test_assignmentExpression_indexExpression_value() async {
+    await analyze('''
+class C {
+  void operator[]=(int a, int b) {}
+}
+void f(C c, int i, int j) {
+  c[i] = j;
+}
+''');
+    assertEdge(decoratedTypeAnnotation('int j').node,
+        decoratedTypeAnnotation('int b').node,
+        hard: true);
+  }
+
+  test_assignmentExpression_operands() async {
+    await analyze('''
+void f(int i, int j) {
+  i = j;
+}
+''');
+    assertEdge(decoratedTypeAnnotation('int j').node,
+        decoratedTypeAnnotation('int i').node,
+        hard: true);
+  }
+
+  test_assignmentExpression_return_value() async {
+    await analyze('''
+void f(int i, int j) {
+  g(i = j);
+}
+void g(int k) {}
+''');
+    assertEdge(decoratedTypeAnnotation('int j').node,
+        decoratedTypeAnnotation('int k').node,
+        hard: false);
+  }
+
+  test_assignmentExpression_setter() async {
+    await analyze('''
+class C {
+  void set s(int value) {}
+}
+void f(C c, int i) {
+  c.s = i;
+}
+''');
+    assertEdge(decoratedTypeAnnotation('int i').node,
+        decoratedTypeAnnotation('int value').node,
+        hard: true);
+  }
+
+  test_assignmentExpression_setter_null_aware() async {
+    await analyze('''
+class C {
+  void set s(int value) {}
+}
+int f(C c, int i) => (c?.s = i);
+''');
+    var lubNode =
+        decoratedExpressionType('(c?.s = i)').node as NullabilityNodeForLUB;
+    expect(lubNode.left, same(decoratedTypeAnnotation('C c').node));
+    expect(lubNode.right, same(decoratedTypeAnnotation('int i').node));
+    assertEdge(lubNode, decoratedTypeAnnotation('int f').node, hard: false);
+  }
+
+  test_assignmentExpression_setter_target_check() async {
+    await analyze('''
+class C {
+  void set s(int value) {}
+}
+void f(C c, int i) {
+  c.s = i;
+}
+''');
+    assertNullCheck(checkExpression('c.s'),
+        assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true));
+  }
+
+  @failingTest
+  test_awaitExpression_future_nonNullable() async {
+    await analyze('''
+Future<void> f() async {
+  int x = await g();
+}
+Future<int> g() async => 3;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('int').node);
+  }
+
+  @failingTest
+  test_awaitExpression_future_nullable() async {
+    await analyze('''
+Future<void> f() async {
+  int x = await g();
+}
+Future<int> g() async => null;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('int').node);
+  }
+
+  test_awaitExpression_nonFuture() async {
+    await analyze('''
+Future<void> f() async {
+  int x = await 3;
+}
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('int').node);
+  }
+
+  test_binaryExpression_ampersand_result_not_null() async {
+    await analyze('''
+int f(int i, int j) => i & j;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node);
+  }
+
+  test_binaryExpression_bar_result_not_null() async {
+    await analyze('''
+int f(int i, int j) => i | j;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node);
+  }
+
+  test_binaryExpression_caret_result_not_null() async {
+    await analyze('''
+int f(int i, int j) => i ^ j;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node);
+  }
+
+  test_binaryExpression_equal() async {
+    await analyze('''
+bool f(int i, int j) => i == j;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('bool f').node);
+  }
+
+  test_binaryExpression_gt_result_not_null() async {
+    await analyze('''
+bool f(int i, int j) => i > j;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('bool f').node);
+  }
+
+  test_binaryExpression_gtEq_result_not_null() async {
+    await analyze('''
+bool f(int i, int j) => i >= j;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('bool f').node);
+  }
+
+  test_binaryExpression_gtGt_result_not_null() async {
+    await analyze('''
+int f(int i, int j) => i >> j;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node);
+  }
+
+  test_binaryExpression_lt_result_not_null() async {
+    await analyze('''
+bool f(int i, int j) => i < j;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('bool f').node);
+  }
+
+  test_binaryExpression_ltEq_result_not_null() async {
+    await analyze('''
+bool f(int i, int j) => i <= j;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('bool f').node);
+  }
+
+  test_binaryExpression_ltLt_result_not_null() async {
+    await analyze('''
+int f(int i, int j) => i << j;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node);
+  }
+
+  test_binaryExpression_minus_result_not_null() async {
+    await analyze('''
+int f(int i, int j) => i - j;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node);
+  }
+
+  test_binaryExpression_notEqual() async {
+    await analyze('''
+bool f(int i, int j) => i != j;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('bool f').node);
+  }
+
+  test_binaryExpression_percent_result_not_null() async {
+    await analyze('''
+int f(int i, int j) => i % j;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node);
+  }
+
+  test_binaryExpression_plus_left_check() async {
+    await analyze('''
+int f(int i, int j) => i + j;
+''');
+
+    assertNullCheck(checkExpression('i +'),
+        assertEdge(decoratedTypeAnnotation('int i').node, never, hard: true));
+  }
+
+  test_binaryExpression_plus_left_check_custom() async {
+    await analyze('''
+class Int {
+  Int operator+(Int other) => this;
+}
+Int f(Int i, Int j) => i + j;
+''');
+
+    assertNullCheck(checkExpression('i +'),
+        assertEdge(decoratedTypeAnnotation('Int i').node, never, hard: true));
+  }
+
+  test_binaryExpression_plus_result_custom() async {
+    await analyze('''
+class Int {
+  Int operator+(Int other) => this;
+}
+Int f(Int i, Int j) => (i + j);
+''');
+
+    assertNullCheck(
+        checkExpression('(i + j)'),
+        assertEdge(decoratedTypeAnnotation('Int operator+').node,
+            decoratedTypeAnnotation('Int f').node,
+            hard: false));
+  }
+
+  test_binaryExpression_plus_result_not_null() async {
+    await analyze('''
+int f(int i, int j) => i + j;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node);
+  }
+
+  test_binaryExpression_plus_right_check() async {
+    await analyze('''
+int f(int i, int j) => i + j;
+''');
+
+    assertNullCheck(checkExpression('j;'),
+        assertEdge(decoratedTypeAnnotation('int j').node, never, hard: true));
+  }
+
+  test_binaryExpression_plus_right_check_custom() async {
+    await analyze('''
+class Int {
+  Int operator+(Int other) => this;
+}
+Int f(Int i, Int j) => i + j/*check*/;
+''');
+
+    assertNullCheck(
+        checkExpression('j/*check*/'),
+        assertEdge(decoratedTypeAnnotation('Int j').node,
+            decoratedTypeAnnotation('Int other').node,
+            hard: true));
+  }
+
+  test_binaryExpression_slash_result_not_null() async {
+    await analyze('''
+double f(int i, int j) => i / j;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('double f').node);
+  }
+
+  test_binaryExpression_star_result_not_null() async {
+    await analyze('''
+int f(int i, int j) => i * j;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node);
+  }
+
+  test_binaryExpression_tildeSlash_result_not_null() async {
+    await analyze('''
+int f(int i, int j) => i ~/ j;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node);
+  }
+
+  test_boolLiteral() async {
+    await analyze('''
+bool f() {
+  return true;
+}
+''');
+    assertNoUpstreamNullability(decoratedTypeAnnotation('bool').node);
+  }
+
+  test_cascadeExpression() async {
+    await analyze('''
+class C {
+  int x = 0;
+}
+C f(C c, int i) => c..x = i;
+''');
+    assertEdge(decoratedTypeAnnotation('C c').node,
+        decoratedTypeAnnotation('C f').node,
+        hard: false);
+  }
+
+  test_conditionalExpression_condition_check() async {
+    await analyze('''
+int f(bool b, int i, int j) {
+  return (b ? i : j);
+}
+''');
+
+    var nullable_b = decoratedTypeAnnotation('bool b').node;
+    var check_b = checkExpression('b ?');
+    assertNullCheck(check_b, assertEdge(nullable_b, never, hard: true));
+  }
+
+  test_conditionalExpression_general() async {
+    await analyze('''
+int f(bool b, int i, int j) {
+  return (b ? i : j);
+}
+''');
+
+    var nullable_i = decoratedTypeAnnotation('int i').node;
+    var nullable_j = decoratedTypeAnnotation('int j').node;
+    var nullable_conditional = decoratedExpressionType('(b ?').node;
+    assertConditional(nullable_conditional, nullable_i, nullable_j);
+    var nullable_return = decoratedTypeAnnotation('int f').node;
+    assertNullCheck(checkExpression('(b ? i : j)'),
+        assertEdge(nullable_conditional, nullable_return, hard: false));
+  }
+
+  test_conditionalExpression_left_non_null() async {
+    await analyze('''
+int f(bool b, int i) {
+  return (b ? (throw i) : i);
+}
+''');
+
+    var nullable_i = decoratedTypeAnnotation('int i').node;
+    var nullable_conditional =
+        decoratedExpressionType('(b ?').node as NullabilityNodeForLUB;
+    var nullable_throw = nullable_conditional.left;
+    assertNoUpstreamNullability(nullable_throw);
+    assertConditional(nullable_conditional, nullable_throw, nullable_i);
+  }
+
+  test_conditionalExpression_left_null() async {
+    await analyze('''
+int f(bool b, int i) {
+  return (b ? null : i);
+}
+''');
+
+    var nullable_i = decoratedTypeAnnotation('int i').node;
+    var nullable_conditional = decoratedExpressionType('(b ?').node;
+    assertConditional(nullable_conditional, always, nullable_i);
+  }
+
+  test_conditionalExpression_right_non_null() async {
+    await analyze('''
+int f(bool b, int i) {
+  return (b ? i : (throw i));
+}
+''');
+
+    var nullable_i = decoratedTypeAnnotation('int i').node;
+    var nullable_conditional =
+        decoratedExpressionType('(b ?').node as NullabilityNodeForLUB;
+    var nullable_throw = nullable_conditional.right;
+    assertNoUpstreamNullability(nullable_throw);
+    assertConditional(nullable_conditional, nullable_i, nullable_throw);
+  }
+
+  test_conditionalExpression_right_null() async {
+    await analyze('''
+int f(bool b, int i) {
+  return (b ? i : null);
+}
+''');
+
+    var nullable_i = decoratedTypeAnnotation('int i').node;
+    var nullable_conditional = decoratedExpressionType('(b ?').node;
+    assertConditional(nullable_conditional, nullable_i, always);
+  }
+
+  test_doubleLiteral() async {
+    await analyze('''
+double f() {
+  return 1.0;
+}
+''');
+    assertNoUpstreamNullability(decoratedTypeAnnotation('double').node);
+  }
+
+  test_functionDeclaration_expression_body() async {
+    await analyze('''
+int/*1*/ f(int/*2*/ i) => i/*3*/;
+''');
+
+    assertNullCheck(
+        checkExpression('i/*3*/'),
+        assertEdge(decoratedTypeAnnotation('int/*2*/').node,
+            decoratedTypeAnnotation('int/*1*/').node,
+            hard: true));
+  }
+
+  test_functionDeclaration_parameter_named_default_notNull() async {
+    await analyze('''
+void f({int i = 1}) {}
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('int').node);
+  }
+
+  test_functionDeclaration_parameter_named_default_null() async {
+    await analyze('''
+void f({int i = null}) {}
+''');
+
+    assertEdge(always, decoratedTypeAnnotation('int').node, hard: false);
+  }
+
+  test_functionDeclaration_parameter_named_no_default() async {
+    await analyze('''
+void f({int i}) {}
+''');
+
+    assertEdge(always, decoratedTypeAnnotation('int').node, hard: false);
+  }
+
+  test_functionDeclaration_parameter_named_no_default_required() async {
+    addMetaPackage();
+    await analyze('''
+import 'package:meta/meta.dart';
+void f({@required int i}) {}
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('int').node);
+  }
+
+  test_functionDeclaration_parameter_positionalOptional_default_notNull() async {
+    await analyze('''
+void f([int i = 1]) {}
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('int').node);
+  }
+
+  test_functionDeclaration_parameter_positionalOptional_default_null() async {
+    await analyze('''
+void f([int i = null]) {}
+''');
+
+    assertEdge(always, decoratedTypeAnnotation('int').node, hard: false);
+  }
+
+  test_functionDeclaration_parameter_positionalOptional_no_default() async {
+    await analyze('''
+void f([int i]) {}
+''');
+
+    assertEdge(always, decoratedTypeAnnotation('int').node, hard: false);
+  }
+
+  test_functionDeclaration_resets_unconditional_control_flow() async {
+    await analyze('''
+void f(bool b, int i, int j) {
+  assert(i != null);
+  if (b) return;
+  assert(j != null);
+}
+void g(int k) {
+  assert(k != null);
+}
+''');
+    assertEdge(decoratedTypeAnnotation('int i').node, never, hard: true);
+    assertNoEdge(always, decoratedTypeAnnotation('int j').node);
+    assertEdge(decoratedTypeAnnotation('int k').node, never, hard: true);
+  }
+
+  test_functionInvocation_parameter_fromLocalParameter() async {
+    await analyze('''
+void f(int/*1*/ i) {}
+void test(int/*2*/ i) {
+  f(i/*3*/);
+}
+''');
+
+    var int_1 = decoratedTypeAnnotation('int/*1*/');
+    var int_2 = decoratedTypeAnnotation('int/*2*/');
+    var i_3 = checkExpression('i/*3*/');
+    assertNullCheck(i_3, assertEdge(int_2.node, int_1.node, hard: true));
+    assertEdge(int_2.node, int_1.node, hard: true);
+  }
+
+  test_functionInvocation_parameter_named() async {
+    await analyze('''
+void f({int i: 0}) {}
+void g(int j) {
+  f(i: j/*check*/);
+}
+''');
+    var nullable_i = decoratedTypeAnnotation('int i').node;
+    var nullable_j = decoratedTypeAnnotation('int j').node;
+    assertNullCheck(checkExpression('j/*check*/'),
+        assertEdge(nullable_j, nullable_i, hard: true));
+  }
+
+  test_functionInvocation_parameter_named_missing() async {
+    await analyze('''
+void f({int i}) {}
+void g() {
+  f();
+}
+''');
+    var optional_i = possiblyOptionalParameter('int i');
+    expect(getEdges(always, optional_i), isNotEmpty);
+  }
+
+  test_functionInvocation_parameter_named_missing_required() async {
+    addMetaPackage();
+    verifyNoTestUnitErrors = false;
+    await analyze('''
+import 'package:meta/meta.dart';
+void f({@required int i}) {}
+void g() {
+  f();
+}
+''');
+    // The call at `f()` is presumed to be in error; no constraint is recorded.
+    var optional_i = possiblyOptionalParameter('int i');
+    expect(optional_i, isNull);
+    var nullable_i = decoratedTypeAnnotation('int i').node;
+    assertNoUpstreamNullability(nullable_i);
+  }
+
+  test_functionInvocation_parameter_null() async {
+    await analyze('''
+void f(int i) {}
+void test() {
+  f(null);
+}
+''');
+
+    assertNullCheck(checkExpression('null'),
+        assertEdge(always, decoratedTypeAnnotation('int').node, hard: false));
+  }
+
+  test_functionInvocation_return() async {
+    await analyze('''
+int/*1*/ f() => 0;
+int/*2*/ g() {
+  return (f());
+}
+''');
+
+    assertNullCheck(
+        checkExpression('(f())'),
+        assertEdge(decoratedTypeAnnotation('int/*1*/').node,
+            decoratedTypeAnnotation('int/*2*/').node,
+            hard: false));
+  }
+
+  test_if_condition() async {
+    await analyze('''
+void f(bool b) {
+  if (b) {}
+}
+''');
+
+    assertNullCheck(checkExpression('b) {}'),
+        assertEdge(decoratedTypeAnnotation('bool b').node, never, hard: true));
+  }
+
+  test_if_conditional_control_flow_after() async {
+    // Asserts after ifs don't demonstrate non-null intent.
+    // TODO(paulberry): if both branches complete normally, they should.
+    await analyze('''
+void f(bool b, int i) {
+  if (b) return;
+  assert(i != null);
+}
+''');
+
+    assertNoEdge(always, decoratedTypeAnnotation('int i').node);
+  }
+
+  test_if_conditional_control_flow_within() async {
+    // Asserts inside ifs don't demonstrate non-null intent.
+    await analyze('''
+void f(bool b, int i) {
+  if (b) {
+    assert(i != null);
+  } else {
+    assert(i != null);
+  }
+}
+''');
+
+    assertNoEdge(always, decoratedTypeAnnotation('int i').node);
+  }
+
+  test_if_guard_equals_null() async {
+    await analyze('''
+int f(int i, int j, int k) {
+  if (i == null) {
+    return j/*check*/;
+  } else {
+    return k/*check*/;
+  }
+}
+''');
+    var nullable_i = decoratedTypeAnnotation('int i').node;
+    var nullable_j = decoratedTypeAnnotation('int j').node;
+    var nullable_k = decoratedTypeAnnotation('int k').node;
+    var nullable_return = decoratedTypeAnnotation('int f').node;
+    assertNullCheck(
+        checkExpression('j/*check*/'),
+        assertEdge(nullable_j, nullable_return,
+            guards: [nullable_i], hard: false));
+    assertNullCheck(checkExpression('k/*check*/'),
+        assertEdge(nullable_k, nullable_return, hard: false));
+    var discard = statementDiscard('if (i == null)');
+    expect(discard.trueGuard, same(nullable_i));
+    expect(discard.falseGuard, null);
+    expect(discard.pureCondition, true);
+  }
+
+  test_if_simple() async {
+    await analyze('''
+int f(bool b, int i, int j) {
+  if (b) {
+    return i/*check*/;
+  } else {
+    return j/*check*/;
+  }
+}
+''');
+
+    var nullable_i = decoratedTypeAnnotation('int i').node;
+    var nullable_j = decoratedTypeAnnotation('int j').node;
+    var nullable_return = decoratedTypeAnnotation('int f').node;
+    assertNullCheck(checkExpression('i/*check*/'),
+        assertEdge(nullable_i, nullable_return, hard: false));
+    assertNullCheck(checkExpression('j/*check*/'),
+        assertEdge(nullable_j, nullable_return, hard: false));
+  }
+
+  test_if_without_else() async {
+    await analyze('''
+int f(bool b, int i) {
+  if (b) {
+    return i/*check*/;
+  }
+  return 0;
+}
+''');
+
+    var nullable_i = decoratedTypeAnnotation('int i').node;
+    var nullable_return = decoratedTypeAnnotation('int f').node;
+    assertNullCheck(checkExpression('i/*check*/'),
+        assertEdge(nullable_i, nullable_return, hard: false));
+  }
+
+  test_indexExpression_index() async {
+    await analyze('''
+class C {
+  int operator[](int i) => 1;
+}
+int f(C c, int j) => c[j];
+''');
+    assertEdge(decoratedTypeAnnotation('int j').node,
+        decoratedTypeAnnotation('int i').node,
+        hard: true);
+  }
+
+  test_indexExpression_index_cascaded() async {
+    await analyze('''
+class C {
+  int operator[](int i) => 1;
+}
+C f(C c, int j) => c..[j];
+''');
+    assertEdge(decoratedTypeAnnotation('int j').node,
+        decoratedTypeAnnotation('int i').node,
+        hard: true);
+  }
+
+  test_indexExpression_return_type() async {
+    await analyze('''
+class C {
+  int operator[](int i) => 1;
+}
+int f(C c) => c[0];
+''');
+    assertEdge(decoratedTypeAnnotation('int operator').node,
+        decoratedTypeAnnotation('int f').node,
+        hard: false);
+  }
+
+  test_indexExpression_target_check() async {
+    await analyze('''
+class C {
+  int operator[](int i) => 1;
+}
+int f(C c) => c[0];
+''');
+    assertNullCheck(checkExpression('c['),
+        assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true));
+  }
+
+  test_indexExpression_target_check_cascaded() async {
+    await analyze('''
+class C {
+  int operator[](int i) => 1;
+}
+C f(C c) => c..[0];
+''');
+    assertNullCheck(checkExpression('c..['),
+        assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true));
+  }
+
+  test_indexExpression_target_demonstrates_non_null_intent() async {
+    await analyze('''
+class C {
+  int operator[](int i) => 1;
+}
+int f(C c) => c[0];
+''');
+    assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true);
+  }
+
+  test_indexExpression_target_demonstrates_non_null_intent_cascaded() async {
+    await analyze('''
+class C {
+  int operator[](int i) => 1;
+}
+C f(C c) => c..[0];
+''');
+    assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true);
+  }
+
+  test_instanceCreation_parameter_named_optional() async {
+    await analyze('''
+class C {
+  C({int x = 0});
+}
+void f(int y) {
+  C(x: y);
+}
+''');
+
+    assertEdge(decoratedTypeAnnotation('int y').node,
+        decoratedTypeAnnotation('int x').node,
+        hard: true);
+  }
+
+  test_instanceCreation_parameter_positional_optional() async {
+    await analyze('''
+class C {
+  C([int x]);
+}
+void f(int y) {
+  C(y);
+}
+''');
+
+    assertEdge(decoratedTypeAnnotation('int y').node,
+        decoratedTypeAnnotation('int x').node,
+        hard: true);
+  }
+
+  test_instanceCreation_parameter_positional_required() async {
+    await analyze('''
+class C {
+  C(int x);
+}
+void f(int y) {
+  C(y);
+}
+''');
+
+    assertEdge(decoratedTypeAnnotation('int y').node,
+        decoratedTypeAnnotation('int x').node,
+        hard: true);
+  }
+
+  test_integerLiteral() async {
+    await analyze('''
+int f() {
+  return 0;
+}
+''');
+    assertNoUpstreamNullability(decoratedTypeAnnotation('int').node);
+  }
+
+  @failingTest
+  test_isExpression_genericFunctionType() async {
+    await analyze('''
+bool f(a) => a is int Function(String);
+''');
+    assertNoUpstreamNullability(decoratedTypeAnnotation('bool').node);
+  }
+
+  test_isExpression_typeName_noTypeArguments() async {
+    await analyze('''
+bool f(a) => a is String;
+''');
+    assertNoUpstreamNullability(decoratedTypeAnnotation('bool').node);
+  }
+
+  @failingTest
+  test_isExpression_typeName_typeArguments() async {
+    await analyze('''
+bool f(a) => a is List<int>;
+''');
+    assertNoUpstreamNullability(decoratedTypeAnnotation('bool').node);
+  }
+
+  @failingTest
+  test_listLiteral_noTypeArgument_noNullableElements() async {
+    // Failing because we're not yet handling collection literals without a
+    // type argument.
+    await analyze('''
+List<String> f() {
+  return ['a', 'b'];
+}
+''');
+    assertNoUpstreamNullability(decoratedTypeAnnotation('List').node);
+    // TODO(brianwilkerson) Add an assertion that there is an edge from the list
+    //  literal's fake type argument to the return type's type argument.
+  }
+
+  @failingTest
+  test_listLiteral_noTypeArgument_nullableElement() async {
+    // Failing because we're not yet handling collection literals without a
+    // type argument.
+    await analyze('''
+List<String> f() {
+  return ['a', null, 'c'];
+}
+''');
+    assertNoUpstreamNullability(decoratedTypeAnnotation('List').node);
+    assertEdge(always, decoratedTypeAnnotation('String').node, hard: false);
+  }
+
+  test_listLiteral_typeArgument_noNullableElements() async {
+    await analyze('''
+List<String> f() {
+  return <String>['a', 'b'];
+}
+''');
+    assertNoUpstreamNullability(decoratedTypeAnnotation('List').node);
+    var typeArgForLiteral = decoratedTypeAnnotation('String>[').node;
+    var typeArgForReturnType = decoratedTypeAnnotation('String> ').node;
+    assertNoUpstreamNullability(typeArgForLiteral);
+    assertEdge(typeArgForLiteral, typeArgForReturnType, hard: false);
+  }
+
+  test_listLiteral_typeArgument_nullableElement() async {
+    await analyze('''
+List<String> f() {
+  return <String>['a', null, 'c'];
+}
+''');
+    assertNoUpstreamNullability(decoratedTypeAnnotation('List').node);
+    assertEdge(always, decoratedTypeAnnotation('String>[').node, hard: false);
+  }
+
+  test_methodDeclaration_resets_unconditional_control_flow() async {
+    await analyze('''
+class C {
+  void f(bool b, int i, int j) {
+    assert(i != null);
+    if (b) return;
+    assert(j != null);
+  }
+  void g(int k) {
+    assert(k != null);
+  }
+}
+''');
+    assertEdge(decoratedTypeAnnotation('int i').node, never, hard: true);
+    assertNoEdge(always, decoratedTypeAnnotation('int j').node);
+    assertEdge(decoratedTypeAnnotation('int k').node, never, hard: true);
+  }
+
+  test_methodInvocation_parameter_contravariant() async {
+    await analyze('''
+class C<T> {
+  void f(T t) {}
+}
+void g(C<int> c, int i) {
+  c.f(i/*check*/);
+}
+''');
+
+    var nullable_i = decoratedTypeAnnotation('int i').node;
+    var nullable_c_t = decoratedTypeAnnotation('C<int>').typeArguments[0].node;
+    var nullable_t = decoratedTypeAnnotation('T t').node;
+    var check_i = checkExpression('i/*check*/');
+    var nullable_c_t_or_nullable_t =
+        check_i.edges.single.destinationNode as NullabilityNodeForSubstitution;
+    expect(nullable_c_t_or_nullable_t.innerNode, same(nullable_c_t));
+    expect(nullable_c_t_or_nullable_t.outerNode, same(nullable_t));
+    assertNullCheck(check_i,
+        assertEdge(nullable_i, nullable_c_t_or_nullable_t, hard: true));
+  }
+
+  test_methodInvocation_parameter_generic() async {
+    await analyze('''
+class C<T> {}
+void f(C<int/*1*/>/*2*/ c) {}
+void g(C<int/*3*/>/*4*/ c) {
+  f(c/*check*/);
+}
+''');
+
+    assertEdge(decoratedTypeAnnotation('int/*3*/').node,
+        decoratedTypeAnnotation('int/*1*/').node,
+        hard: false);
+    assertNullCheck(
+        checkExpression('c/*check*/'),
+        assertEdge(decoratedTypeAnnotation('C<int/*3*/>/*4*/').node,
+            decoratedTypeAnnotation('C<int/*1*/>/*2*/').node,
+            hard: true));
+  }
+
+  test_methodInvocation_parameter_named() async {
+    await analyze('''
+class C {
+  void f({int i: 0}) {}
+}
+void g(C c, int j) {
+  c.f(i: j/*check*/);
+}
+''');
+    var nullable_i = decoratedTypeAnnotation('int i').node;
+    var nullable_j = decoratedTypeAnnotation('int j').node;
+    assertNullCheck(checkExpression('j/*check*/'),
+        assertEdge(nullable_j, nullable_i, hard: true));
+  }
+
+  test_methodInvocation_parameter_named_differentPackage() async {
+    addPackageFile('pkgC', 'c.dart', '''
+class C {
+  void f({int i}) {}
+}
+''');
+    await analyze('''
+import "package:pkgC/c.dart";
+void g(C c, int j) {
+  c.f(i: j/*check*/);
+}
+''');
+    var nullable_j = decoratedTypeAnnotation('int j');
+    assertNullCheck(checkExpression('j/*check*/'),
+        assertEdge(nullable_j.node, never, hard: true));
+  }
+
+  test_methodInvocation_return_type() async {
+    await analyze('''
+class C {
+  bool m() => true;
+}
+bool f(C c) => c.m();
+''');
+    assertEdge(decoratedTypeAnnotation('bool m').node,
+        decoratedTypeAnnotation('bool f').node,
+        hard: false);
+  }
+
+  test_methodInvocation_return_type_null_aware() async {
+    await analyze('''
+class C {
+  bool m() => true;
+}
+bool f(C c) => (c?.m());
+''');
+    var lubNode =
+        decoratedExpressionType('(c?.m())').node as NullabilityNodeForLUB;
+    expect(lubNode.left, same(decoratedTypeAnnotation('C c').node));
+    expect(lubNode.right, same(decoratedTypeAnnotation('bool m').node));
+    assertEdge(lubNode, decoratedTypeAnnotation('bool f').node, hard: false);
+  }
+
+  test_methodInvocation_target_check() async {
+    await analyze('''
+class C {
+  void m() {}
+}
+void test(C c) {
+  c.m();
+}
+''');
+
+    assertNullCheck(checkExpression('c.m'),
+        assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true));
+  }
+
+  test_methodInvocation_target_check_cascaded() async {
+    await analyze('''
+class C {
+  void m() {}
+}
+void test(C c) {
+  c..m();
+}
+''');
+
+    assertNullCheck(checkExpression('c..m'),
+        assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true));
+  }
+
+  test_methodInvocation_target_demonstrates_non_null_intent() async {
+    await analyze('''
+class C {
+  void m() {}
+}
+void test(C c) {
+  c.m();
+}
+''');
+
+    assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true);
+  }
+
+  test_methodInvocation_target_demonstrates_non_null_intent_cascaded() async {
+    await analyze('''
+class C {
+  void m() {}
+}
+void test(C c) {
+  c..m();
+}
+''');
+
+    assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true);
+  }
+
+  test_never() async {
+    await analyze('');
+
+    expect(never.isNullable, isFalse);
+  }
+
+  test_parenthesizedExpression() async {
+    await analyze('''
+int f() {
+  return (null);
+}
+''');
+
+    assertNullCheck(checkExpression('(null)'),
+        assertEdge(always, decoratedTypeAnnotation('int').node, hard: false));
+  }
+
+  test_prefixedIdentifier_field_type() async {
+    await analyze('''
+class C {
+  bool b = true;
+}
+bool f(C c) => c.b;
+''');
+    assertEdge(decoratedTypeAnnotation('bool b').node,
+        decoratedTypeAnnotation('bool f').node,
+        hard: false);
+  }
+
+  test_prefixedIdentifier_getter_type() async {
+    await analyze('''
+class C {
+  bool get b => true;
+}
+bool f(C c) => c.b;
+''');
+    assertEdge(decoratedTypeAnnotation('bool get').node,
+        decoratedTypeAnnotation('bool f').node,
+        hard: false);
+  }
+
+  test_prefixedIdentifier_target_check() async {
+    await analyze('''
+class C {
+  int get x => 1;
+}
+void test(C c) {
+  c.x;
+}
+''');
+
+    assertNullCheck(checkExpression('c.x'),
+        assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true));
+  }
+
+  test_prefixedIdentifier_target_demonstrates_non_null_intent() async {
+    await analyze('''
+class C {
+  int get x => 1;
+}
+void test(C c) {
+  c.x;
+}
+''');
+
+    assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true);
+  }
+
+  test_prefixExpression_bang2() async {
+    await analyze('''
+bool f(bool b) {
+  return !b;
+}
+''');
+
+    var nullable_b = decoratedTypeAnnotation('bool b').node;
+    var check_b = checkExpression('b;');
+    assertNullCheck(check_b, assertEdge(nullable_b, never, hard: true));
+
+    var return_f = decoratedTypeAnnotation('bool f').node;
+    assertEdge(never, return_f, hard: false);
+  }
+
+  test_propertyAccess_return_type() async {
+    await analyze('''
+class C {
+  bool get b => true;
+}
+bool f(C c) => (c).b;
+''');
+    assertEdge(decoratedTypeAnnotation('bool get').node,
+        decoratedTypeAnnotation('bool f').node,
+        hard: false);
+  }
+
+  test_propertyAccess_return_type_null_aware() async {
+    await analyze('''
+class C {
+  bool get b => true;
+}
+bool f(C c) => (c?.b);
+''');
+    var lubNode =
+        decoratedExpressionType('(c?.b)').node as NullabilityNodeForLUB;
+    expect(lubNode.left, same(decoratedTypeAnnotation('C c').node));
+    expect(lubNode.right, same(decoratedTypeAnnotation('bool get b').node));
+    assertEdge(lubNode, decoratedTypeAnnotation('bool f').node, hard: false);
+  }
+
+  test_propertyAccess_target_check() async {
+    await analyze('''
+class C {
+  int get x => 1;
+}
+void test(C c) {
+  (c).x;
+}
+''');
+
+    assertNullCheck(checkExpression('c).x'),
+        assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true));
+  }
+
+  test_return_implicit_null() async {
+    verifyNoTestUnitErrors = false;
+    await analyze('''
+int f() {
+  return;
+}
+''');
+
+    assertEdge(always, decoratedTypeAnnotation('int').node, hard: false);
+  }
+
+  test_return_null() async {
+    await analyze('''
+int f() {
+  return null;
+}
+''');
+
+    assertNullCheck(checkExpression('null'),
+        assertEdge(always, decoratedTypeAnnotation('int').node, hard: false));
+  }
+
+  test_return_null_generic() async {
+    await analyze('''
+class C<T> {
+  T f() {
+    return null;
+  }
+}
+''');
+    var tNode = decoratedTypeAnnotation('T f').node;
+    assertEdge(always, tNode, hard: false);
+    assertNullCheck(
+        checkExpression('null'), assertEdge(always, tNode, hard: false));
+  }
+
+  @failingTest
+  test_setOrMapLiteral_map_noTypeArgument_noNullableKeysAndValues() async {
+    // Failing because we're not yet handling collection literals without a
+    // type argument.
+    await analyze('''
+Map<String, int> f() {
+  return {'a' : 1, 'b' : 2};
+}
+''');
+    assertNoUpstreamNullability(decoratedTypeAnnotation('Map').node);
+    // TODO(brianwilkerson) Add an assertion that there is an edge from the set
+    //  literal's fake type argument to the return type's type argument.
+  }
+
+  @failingTest
+  test_setOrMapLiteral_map_noTypeArgument_nullableKey() async {
+    // Failing because we're not yet handling collection literals without a
+    // type argument.
+    await analyze('''
+Map<String, int> f() {
+  return {'a' : 1, null : 2, 'c' : 3};
+}
+''');
+    assertNoUpstreamNullability(decoratedTypeAnnotation('Map').node);
+    assertEdge(always, decoratedTypeAnnotation('String').node, hard: false);
+    assertNoUpstreamNullability(decoratedTypeAnnotation('int').node);
+  }
+
+  @failingTest
+  test_setOrMapLiteral_map_noTypeArgument_nullableKeyAndValue() async {
+    // Failing because we're not yet handling collection literals without a
+    // type argument.
+    await analyze('''
+Map<String, int> f() {
+  return {'a' : 1, null : null, 'c' : 3};
+}
+''');
+    assertNoUpstreamNullability(decoratedTypeAnnotation('Map').node);
+    assertEdge(always, decoratedTypeAnnotation('String').node, hard: false);
+    assertEdge(always, decoratedTypeAnnotation('int').node, hard: false);
+  }
+
+  @failingTest
+  test_setOrMapLiteral_map_noTypeArgument_nullableValue() async {
+    // Failing because we're not yet handling collection literals without a
+    // type argument.
+    await analyze('''
+Map<String, int> f() {
+  return {'a' : 1, 'b' : null, 'c' : 3};
+}
+''');
+    assertNoUpstreamNullability(decoratedTypeAnnotation('Map').node);
+    assertNoUpstreamNullability(decoratedTypeAnnotation('String').node);
+    assertEdge(always, decoratedTypeAnnotation('int').node, hard: false);
+  }
+
+  test_setOrMapLiteral_map_typeArguments_noNullableKeysAndValues() async {
+    await analyze('''
+Map<String, int> f() {
+  return <String, int>{'a' : 1, 'b' : 2};
+}
+''');
+    assertNoUpstreamNullability(decoratedTypeAnnotation('Map').node);
+
+    var keyForLiteral = decoratedTypeAnnotation('String, int>{').node;
+    var keyForReturnType = decoratedTypeAnnotation('String, int> ').node;
+    assertNoUpstreamNullability(keyForLiteral);
+    assertEdge(keyForLiteral, keyForReturnType, hard: false);
+
+    var valueForLiteral = decoratedTypeAnnotation('int>{').node;
+    var valueForReturnType = decoratedTypeAnnotation('int> ').node;
+    assertNoUpstreamNullability(valueForLiteral);
+    assertEdge(valueForLiteral, valueForReturnType, hard: false);
+  }
+
+  test_setOrMapLiteral_map_typeArguments_nullableKey() async {
+    await analyze('''
+Map<String, int> f() {
+  return <String, int>{'a' : 1, null : 2, 'c' : 3};
+}
+''');
+    assertNoUpstreamNullability(decoratedTypeAnnotation('Map').node);
+    assertEdge(always, decoratedTypeAnnotation('String, int>{').node,
+        hard: false);
+    assertNoUpstreamNullability(decoratedTypeAnnotation('int>{').node);
+  }
+
+  test_setOrMapLiteral_map_typeArguments_nullableKeyAndValue() async {
+    await analyze('''
+Map<String, int> f() {
+  return <String, int>{'a' : 1, null : null, 'c' : 3};
+}
+''');
+    assertNoUpstreamNullability(decoratedTypeAnnotation('Map').node);
+    assertEdge(always, decoratedTypeAnnotation('String, int>{').node,
+        hard: false);
+    assertEdge(always, decoratedTypeAnnotation('int>{').node, hard: false);
+  }
+
+  test_setOrMapLiteral_map_typeArguments_nullableValue() async {
+    await analyze('''
+Map<String, int> f() {
+  return <String, int>{'a' : 1, 'b' : null, 'c' : 3};
+}
+''');
+    assertNoUpstreamNullability(decoratedTypeAnnotation('Map').node);
+    assertNoUpstreamNullability(decoratedTypeAnnotation('String, int>{').node);
+    assertEdge(always, decoratedTypeAnnotation('int>{').node, hard: false);
+  }
+
+  @failingTest
+  test_setOrMapLiteral_set_noTypeArgument_noNullableElements() async {
+    // Failing because we're not yet handling collection literals without a
+    // type argument.
+    await analyze('''
+Set<String> f() {
+  return {'a', 'b'};
+}
+''');
+    assertNoUpstreamNullability(decoratedTypeAnnotation('Set').node);
+    // TODO(brianwilkerson) Add an assertion that there is an edge from the set
+    //  literal's fake type argument to the return type's type argument.
+  }
+
+  @failingTest
+  test_setOrMapLiteral_set_noTypeArgument_nullableElement() async {
+    // Failing because we're not yet handling collection literals without a
+    // type argument.
+    await analyze('''
+Set<String> f() {
+  return {'a', null, 'c'};
+}
+''');
+    assertNoUpstreamNullability(decoratedTypeAnnotation('Set').node);
+    assertEdge(always, decoratedTypeAnnotation('String').node, hard: false);
+  }
+
+  test_setOrMapLiteral_set_typeArgument_noNullableElements() async {
+    await analyze('''
+Set<String> f() {
+  return <String>{'a', 'b'};
+}
+''');
+    assertNoUpstreamNullability(decoratedTypeAnnotation('Set').node);
+    var typeArgForLiteral = decoratedTypeAnnotation('String>{').node;
+    var typeArgForReturnType = decoratedTypeAnnotation('String> ').node;
+    assertNoUpstreamNullability(typeArgForLiteral);
+    assertEdge(typeArgForLiteral, typeArgForReturnType, hard: false);
+  }
+
+  test_setOrMapLiteral_set_typeArgument_nullableElement() async {
+    await analyze('''
+Set<String> f() {
+  return <String>{'a', null, 'c'};
+}
+''');
+    assertNoUpstreamNullability(decoratedTypeAnnotation('Set').node);
+    assertEdge(always, decoratedTypeAnnotation('String>{').node, hard: false);
+  }
+
+  test_simpleIdentifier_local() async {
+    await analyze('''
+main() {
+  int i = 0;
+  int j = i;
+}
+''');
+
+    assertEdge(decoratedTypeAnnotation('int i').node,
+        decoratedTypeAnnotation('int j').node,
+        hard: true);
+  }
+
+  test_skipDirectives() async {
+    await analyze('''
+import "dart:core" as one;
+main() {}
+''');
+    // No test expectations.
+    // Just verifying that the test passes
+  }
+
+  test_soft_edge_for_non_variable_reference() async {
+    // Edges originating in things other than variable references should be
+    // soft.
+    await analyze('''
+int f() => null;
+''');
+    assertEdge(always, decoratedTypeAnnotation('int').node, hard: false);
+  }
+
+  test_stringLiteral() async {
+    // TODO(paulberry): also test string interpolations
+    await analyze('''
+String f() {
+  return 'x';
+}
+''');
+    assertNoUpstreamNullability(decoratedTypeAnnotation('String').node);
+  }
+
+  test_superExpression() async {
+    await analyze('''
+class C {
+  C f() => super;
+}
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('C f').node);
+  }
+
+  test_symbolLiteral() async {
+    await analyze('''
+Symbol f() {
+  return #symbol;
+}
+''');
+    assertNoUpstreamNullability(decoratedTypeAnnotation('Symbol').node);
+  }
+
+  test_thisExpression() async {
+    await analyze('''
+class C {
+  C f() => this;
+}
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('C f').node);
+  }
+
+  test_throwExpression() async {
+    await analyze('''
+int f() {
+  return throw null;
+}
+''');
+    assertNoUpstreamNullability(decoratedTypeAnnotation('int').node);
+  }
+
+  test_topLevelVar_reference() async {
+    await analyze('''
+double pi = 3.1415;
+double get myPi => pi;
+''');
+    var pi = findNode.topLevelVariableDeclaration('double pi');
+    var piType =
+        variables.decoratedTypeAnnotation(testSource, pi.variables.type);
+    var myPi = findNode.any('myPi').parent as FunctionDeclaration;
+    var myPiType =
+        variables.decoratedTypeAnnotation(testSource, myPi.returnType);
+    assertEdge(piType.node, myPiType.node, hard: false);
+  }
+
+  test_type_argument_explicit_bound() async {
+    await analyze('''
+class C<T extends Object> {}
+void f(C<int> c) {}
+''');
+    assertEdge(decoratedTypeAnnotation('int>').node,
+        decoratedTypeAnnotation('Object>').node,
+        hard: true);
+  }
+
+  test_typeName() async {
+    await analyze('''
+Type f() {
+  return int;
+}
+''');
+    assertNoUpstreamNullability(decoratedTypeAnnotation('Type').node);
+  }
+
+  test_typeName_union_with_bound() async {
+    await analyze('''
+class C<T extends Object> {}
+void f(C c) {}
+''');
+    var cType = decoratedTypeAnnotation('C c');
+    var cBound = decoratedTypeAnnotation('Object');
+    assertUnion(cType.typeArguments[0].node, cBound.node);
+  }
+
+  test_typeName_union_with_bounds() async {
+    await analyze('''
+class C<T extends Object, U extends Object> {}
+void f(C c) {}
+''');
+    var cType = decoratedTypeAnnotation('C c');
+    var tBound = decoratedTypeAnnotation('Object,');
+    var uBound = decoratedTypeAnnotation('Object>');
+    assertUnion(cType.typeArguments[0].node, tBound.node);
+    assertUnion(cType.typeArguments[1].node, uBound.node);
+  }
+
+  test_variableDeclaration() async {
+    await analyze('''
+void f(int i) {
+  int j = i;
+}
+''');
+    assertEdge(decoratedTypeAnnotation('int i').node,
+        decoratedTypeAnnotation('int j').node,
+        hard: true);
+  }
+}
diff --git a/pkg/nnbd_migration/test/migration_visitor_test.dart b/pkg/nnbd_migration/test/migration_visitor_test.dart
deleted file mode 100644
index 27969b5..0000000
--- a/pkg/nnbd_migration/test/migration_visitor_test.dart
+++ /dev/null
@@ -1,1566 +0,0 @@
-// Copyright (c) 2019, 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 'package:analyzer/dart/ast/ast.dart';
-import 'package:analyzer/src/generated/resolver.dart';
-import 'package:analyzer/src/generated/source.dart';
-import 'package:meta/meta.dart';
-import 'package:nnbd_migration/src/conditional_discard.dart';
-import 'package:nnbd_migration/src/decorated_type.dart';
-import 'package:nnbd_migration/src/expression_checks.dart';
-import 'package:nnbd_migration/src/graph_builder.dart';
-import 'package:nnbd_migration/src/node_builder.dart';
-import 'package:nnbd_migration/src/nullability_node.dart';
-import 'package:nnbd_migration/src/variables.dart';
-import 'package:test/test.dart';
-import 'package:test_reflective_loader/test_reflective_loader.dart';
-
-import 'abstract_single_unit.dart';
-
-main() {
-  defineReflectiveSuite(() {
-    defineReflectiveTests(GraphBuilderTest);
-    defineReflectiveTests(NodeBuilderTest);
-  });
-}
-
-@reflectiveTest
-class GraphBuilderTest extends MigrationVisitorTestBase {
-  /// Analyzes the given source code, producing constraint variables and
-  /// constraints for it.
-  @override
-  Future<CompilationUnit> analyze(String code) async {
-    var unit = await super.analyze(code);
-    unit.accept(
-        GraphBuilder(typeProvider, _variables, graph, testSource, null));
-    return unit;
-  }
-
-  void assertConditional(
-      NullabilityNode node, NullabilityNode left, NullabilityNode right) {
-    var conditionalNode = node as NullabilityNodeForLUB;
-    expect(conditionalNode.left, same(left));
-    expect(conditionalNode.right, same(right));
-  }
-
-  /// Checks that there are no nullability nodes upstream from [node] that could
-  /// cause it to become nullable.
-  void assertNoUpstreamNullability(NullabilityNode node) {
-    // NullabilityNode.never can never become nullable, even if it has nodes
-    // upstream from it.
-    if (node == NullabilityNode.never) return;
-
-    for (var upstreamNode in graph.getUpstreamNodesForTesting(node)) {
-      expect(upstreamNode, NullabilityNode.never);
-    }
-  }
-
-  /// Verifies that a null check will occur under the proper circumstances.
-  ///
-  /// [expressionChecks] is the object tracking whether or not a null check is
-  /// needed.  [valueNode] is the node representing the possibly-nullable value
-  /// that is the source of the assignment or use.  [contextNode] is the node
-  /// representing the possibly-nullable value that is the destination of the
-  /// assignment (if the value is being assigned), or `null` if the value is
-  /// being used in a circumstance where `null` is not permitted.  [guards] is
-  /// a list of nullability nodes for which there are enclosing if statements
-  /// checking that the corresponding values are non-null.
-  void assertNullCheck(
-      ExpressionChecks expressionChecks, NullabilityNode valueNode,
-      {NullabilityNode contextNode, List<NullabilityNode> guards = const []}) {
-    expect(expressionChecks.valueNode, same(valueNode));
-    if (contextNode == null) {
-      expect(expressionChecks.contextNode, same(NullabilityNode.never));
-    } else {
-      expect(expressionChecks.contextNode, same(contextNode));
-    }
-    expect(expressionChecks.guards, guards);
-  }
-
-  /// Gets the [ExpressionChecks] associated with the expression whose text
-  /// representation is [text], or `null` if the expression has no
-  /// [ExpressionChecks] associated with it.
-  ExpressionChecks checkExpression(String text) {
-    return _variables.checkExpression(findNode.expression(text));
-  }
-
-  /// Gets the [DecoratedType] associated with the expression whose text
-  /// representation is [text], or `null` if the expression has no
-  /// [DecoratedType] associated with it.
-  DecoratedType decoratedExpressionType(String text) {
-    return _variables.decoratedExpressionType(findNode.expression(text));
-  }
-
-  test_always() async {
-    await analyze('');
-
-    expect(NullabilityNode.always.isNullable, isTrue);
-  }
-
-  test_assert_demonstrates_non_null_intent() async {
-    await analyze('''
-void f(int i) {
-  assert(i != null);
-}
-''');
-
-    assertEdge(decoratedTypeAnnotation('int i').node, never, hard: true);
-  }
-
-  test_assignmentExpression_field() async {
-    await analyze('''
-class C {
-  int x = 0;
-}
-void f(C c, int i) {
-  c.x = i;
-}
-''');
-    assertEdge(decoratedTypeAnnotation('int i').node,
-        decoratedTypeAnnotation('int x').node,
-        hard: true);
-  }
-
-  test_assignmentExpression_field_cascaded() async {
-    await analyze('''
-class C {
-  int x = 0;
-}
-void f(C c, int i) {
-  c..x = i;
-}
-''');
-    assertEdge(decoratedTypeAnnotation('int i').node,
-        decoratedTypeAnnotation('int x').node,
-        hard: true);
-  }
-
-  test_assignmentExpression_field_target_check() async {
-    await analyze('''
-class C {
-  int x = 0;
-}
-void f(C c, int i) {
-  c.x = i;
-}
-''');
-    assertNullCheck(
-        checkExpression('c.x'), decoratedTypeAnnotation('C c').node);
-  }
-
-  test_assignmentExpression_field_target_check_cascaded() async {
-    await analyze('''
-class C {
-  int x = 0;
-}
-void f(C c, int i) {
-  c..x = i;
-}
-''');
-    assertNullCheck(
-        checkExpression('c..x'), decoratedTypeAnnotation('C c').node);
-  }
-
-  test_assignmentExpression_indexExpression_index() async {
-    await analyze('''
-class C {
-  void operator[]=(int a, int b) {}
-}
-void f(C c, int i, int j) {
-  c[i] = j;
-}
-''');
-    assertEdge(decoratedTypeAnnotation('int i').node,
-        decoratedTypeAnnotation('int a').node,
-        hard: true);
-  }
-
-  test_assignmentExpression_indexExpression_return_value() async {
-    await analyze('''
-class C {
-  void operator[]=(int a, int b) {}
-}
-int f(C c, int i, int j) => c[i] = j;
-''');
-    assertEdge(decoratedTypeAnnotation('int j').node,
-        decoratedTypeAnnotation('int f').node,
-        hard: false);
-  }
-
-  test_assignmentExpression_indexExpression_target_check() async {
-    await analyze('''
-class C {
-  void operator[]=(int a, int b) {}
-}
-void f(C c, int i, int j) {
-  c[i] = j;
-}
-''');
-    assertNullCheck(checkExpression('c['), decoratedTypeAnnotation('C c').node);
-  }
-
-  test_assignmentExpression_indexExpression_value() async {
-    await analyze('''
-class C {
-  void operator[]=(int a, int b) {}
-}
-void f(C c, int i, int j) {
-  c[i] = j;
-}
-''');
-    assertEdge(decoratedTypeAnnotation('int j').node,
-        decoratedTypeAnnotation('int b').node,
-        hard: true);
-  }
-
-  test_assignmentExpression_operands() async {
-    await analyze('''
-void f(int i, int j) {
-  i = j;
-}
-''');
-    assertEdge(decoratedTypeAnnotation('int j').node,
-        decoratedTypeAnnotation('int i').node,
-        hard: true);
-  }
-
-  test_assignmentExpression_return_value() async {
-    await analyze('''
-void f(int i, int j) {
-  g(i = j);
-}
-void g(int k) {}
-''');
-    assertEdge(decoratedTypeAnnotation('int j').node,
-        decoratedTypeAnnotation('int k').node,
-        hard: false);
-  }
-
-  test_assignmentExpression_setter() async {
-    await analyze('''
-class C {
-  void set s(int value) {}
-}
-void f(C c, int i) {
-  c.s = i;
-}
-''');
-    assertEdge(decoratedTypeAnnotation('int i').node,
-        decoratedTypeAnnotation('int value').node,
-        hard: true);
-  }
-
-  test_assignmentExpression_setter_null_aware() async {
-    await analyze('''
-class C {
-  void set s(int value) {}
-}
-int f(C c, int i) => (c?.s = i);
-''');
-    var lubNode =
-        decoratedExpressionType('(c?.s = i)').node as NullabilityNodeForLUB;
-    expect(lubNode.left, same(decoratedTypeAnnotation('C c').node));
-    expect(lubNode.right, same(decoratedTypeAnnotation('int i').node));
-    assertEdge(lubNode, decoratedTypeAnnotation('int f').node, hard: false);
-  }
-
-  test_assignmentExpression_setter_target_check() async {
-    await analyze('''
-class C {
-  void set s(int value) {}
-}
-void f(C c, int i) {
-  c.s = i;
-}
-''');
-    assertNullCheck(
-        checkExpression('c.s'), decoratedTypeAnnotation('C c').node);
-  }
-
-  test_binaryExpression_add_left_check() async {
-    await analyze('''
-int f(int i, int j) => i + j;
-''');
-
-    assertNullCheck(
-        checkExpression('i +'), decoratedTypeAnnotation('int i').node);
-  }
-
-  test_binaryExpression_add_left_check_custom() async {
-    await analyze('''
-class Int {
-  Int operator+(Int other) => this;
-}
-Int f(Int i, Int j) => i + j;
-''');
-
-    assertNullCheck(
-        checkExpression('i +'), decoratedTypeAnnotation('Int i').node);
-  }
-
-  test_binaryExpression_add_result_custom() async {
-    await analyze('''
-class Int {
-  Int operator+(Int other) => this;
-}
-Int f(Int i, Int j) => (i + j);
-''');
-
-    assertNullCheck(checkExpression('(i + j)'),
-        decoratedTypeAnnotation('Int operator+').node,
-        contextNode: decoratedTypeAnnotation('Int f').node);
-  }
-
-  test_binaryExpression_add_result_not_null() async {
-    await analyze('''
-int f(int i, int j) => i + j;
-''');
-
-    assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node);
-  }
-
-  test_binaryExpression_add_right_check() async {
-    await analyze('''
-int f(int i, int j) => i + j;
-''');
-
-    assertNullCheck(
-        checkExpression('j;'), decoratedTypeAnnotation('int j').node);
-  }
-
-  test_binaryExpression_add_right_check_custom() async {
-    await analyze('''
-class Int {
-  Int operator+(Int other) => this;
-}
-Int f(Int i, Int j) => i + j/*check*/;
-''');
-
-    assertNullCheck(
-        checkExpression('j/*check*/'), decoratedTypeAnnotation('Int j').node,
-        contextNode: decoratedTypeAnnotation('Int other').node);
-  }
-
-  test_binaryExpression_equal() async {
-    await analyze('''
-bool f(int i, int j) => i == j;
-''');
-
-    assertNoUpstreamNullability(decoratedTypeAnnotation('bool f').node);
-  }
-
-  test_boolLiteral() async {
-    await analyze('''
-bool f() {
-  return true;
-}
-''');
-    assertNoUpstreamNullability(decoratedTypeAnnotation('bool').node);
-  }
-
-  test_cascadeExpression() async {
-    await analyze('''
-class C {
-  int x = 0;
-}
-C f(C c, int i) => c..x = i;
-''');
-    assertEdge(decoratedTypeAnnotation('C c').node,
-        decoratedTypeAnnotation('C f').node,
-        hard: false);
-  }
-
-  test_conditionalExpression_condition_check() async {
-    await analyze('''
-int f(bool b, int i, int j) {
-  return (b ? i : j);
-}
-''');
-
-    var nullable_b = decoratedTypeAnnotation('bool b').node;
-    var check_b = checkExpression('b ?');
-    assertNullCheck(check_b, nullable_b);
-  }
-
-  test_conditionalExpression_general() async {
-    await analyze('''
-int f(bool b, int i, int j) {
-  return (b ? i : j);
-}
-''');
-
-    var nullable_i = decoratedTypeAnnotation('int i').node;
-    var nullable_j = decoratedTypeAnnotation('int j').node;
-    var nullable_conditional = decoratedExpressionType('(b ?').node;
-    assertConditional(nullable_conditional, nullable_i, nullable_j);
-    var nullable_return = decoratedTypeAnnotation('int f').node;
-    assertNullCheck(checkExpression('(b ? i : j)'), nullable_conditional,
-        contextNode: nullable_return);
-  }
-
-  test_conditionalExpression_left_non_null() async {
-    await analyze('''
-int f(bool b, int i) {
-  return (b ? (throw i) : i);
-}
-''');
-
-    var nullable_i = decoratedTypeAnnotation('int i').node;
-    var nullable_conditional =
-        decoratedExpressionType('(b ?').node as NullabilityNodeForLUB;
-    var nullable_throw = nullable_conditional.left;
-    assertNoUpstreamNullability(nullable_throw);
-    assertConditional(nullable_conditional, nullable_throw, nullable_i);
-  }
-
-  test_conditionalExpression_left_null() async {
-    await analyze('''
-int f(bool b, int i) {
-  return (b ? null : i);
-}
-''');
-
-    var nullable_i = decoratedTypeAnnotation('int i').node;
-    var nullable_conditional = decoratedExpressionType('(b ?').node;
-    assertConditional(nullable_conditional, NullabilityNode.always, nullable_i);
-  }
-
-  test_conditionalExpression_right_non_null() async {
-    await analyze('''
-int f(bool b, int i) {
-  return (b ? i : (throw i));
-}
-''');
-
-    var nullable_i = decoratedTypeAnnotation('int i').node;
-    var nullable_conditional =
-        decoratedExpressionType('(b ?').node as NullabilityNodeForLUB;
-    var nullable_throw = nullable_conditional.right;
-    assertNoUpstreamNullability(nullable_throw);
-    assertConditional(nullable_conditional, nullable_i, nullable_throw);
-  }
-
-  test_conditionalExpression_right_null() async {
-    await analyze('''
-int f(bool b, int i) {
-  return (b ? i : null);
-}
-''');
-
-    var nullable_i = decoratedTypeAnnotation('int i').node;
-    var nullable_conditional = decoratedExpressionType('(b ?').node;
-    assertConditional(nullable_conditional, nullable_i, NullabilityNode.always);
-  }
-
-  test_functionDeclaration_expression_body() async {
-    await analyze('''
-int/*1*/ f(int/*2*/ i) => i/*3*/;
-''');
-
-    assertNullCheck(
-        checkExpression('i/*3*/'), decoratedTypeAnnotation('int/*2*/').node,
-        contextNode: decoratedTypeAnnotation('int/*1*/').node);
-  }
-
-  test_functionDeclaration_parameter_named_default_notNull() async {
-    await analyze('''
-void f({int i = 1}) {}
-''');
-
-    assertNoUpstreamNullability(decoratedTypeAnnotation('int').node);
-  }
-
-  test_functionDeclaration_parameter_named_default_null() async {
-    await analyze('''
-void f({int i = null}) {}
-''');
-
-    assertEdge(NullabilityNode.always, decoratedTypeAnnotation('int').node,
-        hard: false);
-  }
-
-  test_functionDeclaration_parameter_named_no_default() async {
-    await analyze('''
-void f({int i}) {}
-''');
-
-    assertEdge(NullabilityNode.always, decoratedTypeAnnotation('int').node,
-        hard: false);
-  }
-
-  test_functionDeclaration_parameter_named_no_default_required() async {
-    addMetaPackage();
-    await analyze('''
-import 'package:meta/meta.dart';
-void f({@required int i}) {}
-''');
-
-    assertNoUpstreamNullability(decoratedTypeAnnotation('int').node);
-  }
-
-  test_functionDeclaration_parameter_positionalOptional_default_notNull() async {
-    await analyze('''
-void f([int i = 1]) {}
-''');
-
-    assertNoUpstreamNullability(decoratedTypeAnnotation('int').node);
-  }
-
-  test_functionDeclaration_parameter_positionalOptional_default_null() async {
-    await analyze('''
-void f([int i = null]) {}
-''');
-
-    assertEdge(NullabilityNode.always, decoratedTypeAnnotation('int').node,
-        hard: false);
-  }
-
-  test_functionDeclaration_parameter_positionalOptional_no_default() async {
-    await analyze('''
-void f([int i]) {}
-''');
-
-    assertEdge(NullabilityNode.always, decoratedTypeAnnotation('int').node,
-        hard: false);
-  }
-
-  test_functionDeclaration_resets_unconditional_control_flow() async {
-    await analyze('''
-void f(bool b, int i, int j) {
-  assert(i != null);
-  if (b) return;
-  assert(j != null);
-}
-void g(int k) {
-  assert(k != null);
-}
-''');
-    assertEdge(decoratedTypeAnnotation('int i').node, never, hard: true);
-    assertNoEdge(always, decoratedTypeAnnotation('int j').node);
-    assertEdge(decoratedTypeAnnotation('int k').node, never, hard: true);
-  }
-
-  test_functionInvocation_parameter_fromLocalParameter() async {
-    await analyze('''
-void f(int/*1*/ i) {}
-void test(int/*2*/ i) {
-  f(i/*3*/);
-}
-''');
-
-    var int_1 = decoratedTypeAnnotation('int/*1*/');
-    var int_2 = decoratedTypeAnnotation('int/*2*/');
-    var i_3 = checkExpression('i/*3*/');
-    assertNullCheck(i_3, int_2.node, contextNode: int_1.node);
-    assertEdge(int_2.node, int_1.node, hard: true);
-  }
-
-  test_functionInvocation_parameter_named() async {
-    await analyze('''
-void f({int i: 0}) {}
-void g(int j) {
-  f(i: j/*check*/);
-}
-''');
-    var nullable_i = decoratedTypeAnnotation('int i').node;
-    var nullable_j = decoratedTypeAnnotation('int j').node;
-    assertNullCheck(checkExpression('j/*check*/'), nullable_j,
-        contextNode: nullable_i);
-  }
-
-  test_functionInvocation_parameter_named_missing() async {
-    await analyze('''
-void f({int i}) {}
-void g() {
-  f();
-}
-''');
-    var optional_i = possiblyOptionalParameter('int i');
-    expect(getEdges(NullabilityNode.always, optional_i), isNotEmpty);
-  }
-
-  test_functionInvocation_parameter_named_missing_required() async {
-    addMetaPackage();
-    verifyNoTestUnitErrors = false;
-    await analyze('''
-import 'package:meta/meta.dart';
-void f({@required int i}) {}
-void g() {
-  f();
-}
-''');
-    // The call at `f()` is presumed to be in error; no constraint is recorded.
-    var optional_i = possiblyOptionalParameter('int i');
-    expect(optional_i, isNull);
-    var nullable_i = decoratedTypeAnnotation('int i').node;
-    assertNoUpstreamNullability(nullable_i);
-  }
-
-  test_functionInvocation_parameter_null() async {
-    await analyze('''
-void f(int i) {}
-void test() {
-  f(null);
-}
-''');
-
-    assertNullCheck(checkExpression('null'), NullabilityNode.always,
-        contextNode: decoratedTypeAnnotation('int').node);
-  }
-
-  test_functionInvocation_return() async {
-    await analyze('''
-int/*1*/ f() => 0;
-int/*2*/ g() {
-  return (f());
-}
-''');
-
-    assertNullCheck(
-        checkExpression('(f())'), decoratedTypeAnnotation('int/*1*/').node,
-        contextNode: decoratedTypeAnnotation('int/*2*/').node);
-  }
-
-  test_if_condition() async {
-    await analyze('''
-void f(bool b) {
-  if (b) {}
-}
-''');
-
-    assertNullCheck(
-        checkExpression('b) {}'), decoratedTypeAnnotation('bool b').node);
-  }
-
-  test_if_conditional_control_flow_after() async {
-    // Asserts after ifs don't demonstrate non-null intent.
-    // TODO(paulberry): if both branches complete normally, they should.
-    await analyze('''
-void f(bool b, int i) {
-  if (b) return;
-  assert(i != null);
-}
-''');
-
-    assertNoEdge(always, decoratedTypeAnnotation('int i').node);
-  }
-
-  test_if_conditional_control_flow_within() async {
-    // Asserts inside ifs don't demonstrate non-null intent.
-    await analyze('''
-void f(bool b, int i) {
-  if (b) {
-    assert(i != null);
-  } else {
-    assert(i != null);
-  }
-}
-''');
-
-    assertNoEdge(always, decoratedTypeAnnotation('int i').node);
-  }
-
-  test_if_guard_equals_null() async {
-    await analyze('''
-int f(int i, int j, int k) {
-  if (i == null) {
-    return j/*check*/;
-  } else {
-    return k/*check*/;
-  }
-}
-''');
-    var nullable_i = decoratedTypeAnnotation('int i').node;
-    var nullable_j = decoratedTypeAnnotation('int j').node;
-    var nullable_k = decoratedTypeAnnotation('int k').node;
-    var nullable_return = decoratedTypeAnnotation('int f').node;
-    assertNullCheck(checkExpression('j/*check*/'), nullable_j,
-        contextNode: nullable_return, guards: [nullable_i]);
-    assertNullCheck(checkExpression('k/*check*/'), nullable_k,
-        contextNode: nullable_return);
-    var discard = statementDiscard('if (i == null)');
-    expect(discard.trueGuard, same(nullable_i));
-    expect(discard.falseGuard, null);
-    expect(discard.pureCondition, true);
-  }
-
-  test_if_simple() async {
-    await analyze('''
-int f(bool b, int i, int j) {
-  if (b) {
-    return i/*check*/;
-  } else {
-    return j/*check*/;
-  }
-}
-''');
-
-    var nullable_i = decoratedTypeAnnotation('int i').node;
-    var nullable_j = decoratedTypeAnnotation('int j').node;
-    var nullable_return = decoratedTypeAnnotation('int f').node;
-    assertNullCheck(checkExpression('i/*check*/'), nullable_i,
-        contextNode: nullable_return);
-    assertNullCheck(checkExpression('j/*check*/'), nullable_j,
-        contextNode: nullable_return);
-  }
-
-  test_if_without_else() async {
-    await analyze('''
-int f(bool b, int i) {
-  if (b) {
-    return i/*check*/;
-  }
-  return 0;
-}
-''');
-
-    var nullable_i = decoratedTypeAnnotation('int i').node;
-    var nullable_return = decoratedTypeAnnotation('int f').node;
-    assertNullCheck(checkExpression('i/*check*/'), nullable_i,
-        contextNode: nullable_return);
-  }
-
-  test_indexExpression_index() async {
-    await analyze('''
-class C {
-  int operator[](int i) => 1;
-}
-int f(C c, int j) => c[j];
-''');
-    assertEdge(decoratedTypeAnnotation('int j').node,
-        decoratedTypeAnnotation('int i').node,
-        hard: true);
-  }
-
-  test_indexExpression_index_cascaded() async {
-    await analyze('''
-class C {
-  int operator[](int i) => 1;
-}
-C f(C c, int j) => c..[j];
-''');
-    assertEdge(decoratedTypeAnnotation('int j').node,
-        decoratedTypeAnnotation('int i').node,
-        hard: true);
-  }
-
-  test_indexExpression_return_type() async {
-    await analyze('''
-class C {
-  int operator[](int i) => 1;
-}
-int f(C c) => c[0];
-''');
-    assertEdge(decoratedTypeAnnotation('int operator').node,
-        decoratedTypeAnnotation('int f').node,
-        hard: false);
-  }
-
-  test_indexExpression_target_check() async {
-    await analyze('''
-class C {
-  int operator[](int i) => 1;
-}
-int f(C c) => c[0];
-''');
-    assertNullCheck(checkExpression('c['), decoratedTypeAnnotation('C c').node);
-  }
-
-  test_indexExpression_target_check_cascaded() async {
-    await analyze('''
-class C {
-  int operator[](int i) => 1;
-}
-C f(C c) => c..[0];
-''');
-    assertNullCheck(
-        checkExpression('c..['), decoratedTypeAnnotation('C c').node);
-  }
-
-  test_indexExpression_target_demonstrates_non_null_intent() async {
-    await analyze('''
-class C {
-  int operator[](int i) => 1;
-}
-int f(C c) => c[0];
-''');
-    assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true);
-  }
-
-  test_indexExpression_target_demonstrates_non_null_intent_cascaded() async {
-    await analyze('''
-class C {
-  int operator[](int i) => 1;
-}
-C f(C c) => c..[0];
-''');
-    assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true);
-  }
-
-  test_intLiteral() async {
-    await analyze('''
-int f() {
-  return 0;
-}
-''');
-    assertNoUpstreamNullability(decoratedTypeAnnotation('int').node);
-  }
-
-  test_methodDeclaration_resets_unconditional_control_flow() async {
-    await analyze('''
-class C {
-  void f(bool b, int i, int j) {
-    assert(i != null);
-    if (b) return;
-    assert(j != null);
-  }
-  void g(int k) {
-    assert(k != null);
-  }
-}
-''');
-    assertEdge(decoratedTypeAnnotation('int i').node, never, hard: true);
-    assertNoEdge(always, decoratedTypeAnnotation('int j').node);
-    assertEdge(decoratedTypeAnnotation('int k').node, never, hard: true);
-  }
-
-  test_methodInvocation_parameter_contravariant() async {
-    await analyze('''
-class C<T> {
-  void f(T t) {}
-}
-void g(C<int> c, int i) {
-  c.f(i/*check*/);
-}
-''');
-
-    var nullable_i = decoratedTypeAnnotation('int i').node;
-    var nullable_c_t = decoratedTypeAnnotation('C<int>').typeArguments[0].node;
-    var nullable_t = decoratedTypeAnnotation('T t').node;
-    var check_i = checkExpression('i/*check*/');
-    var nullable_c_t_or_nullable_t =
-        check_i.contextNode as NullabilityNodeForSubstitution;
-    expect(nullable_c_t_or_nullable_t.innerNode, same(nullable_c_t));
-    expect(nullable_c_t_or_nullable_t.outerNode, same(nullable_t));
-    assertNullCheck(check_i, nullable_i,
-        contextNode: nullable_c_t_or_nullable_t);
-  }
-
-  test_methodInvocation_parameter_generic() async {
-    await analyze('''
-class C<T> {}
-void f(C<int/*1*/>/*2*/ c) {}
-void g(C<int/*3*/>/*4*/ c) {
-  f(c/*check*/);
-}
-''');
-
-    assertEdge(decoratedTypeAnnotation('int/*3*/').node,
-        decoratedTypeAnnotation('int/*1*/').node,
-        hard: false);
-    assertNullCheck(checkExpression('c/*check*/'),
-        decoratedTypeAnnotation('C<int/*3*/>/*4*/').node,
-        contextNode: decoratedTypeAnnotation('C<int/*1*/>/*2*/').node);
-  }
-
-  test_methodInvocation_parameter_named() async {
-    await analyze('''
-class C {
-  void f({int i: 0}) {}
-}
-void g(C c, int j) {
-  c.f(i: j/*check*/);
-}
-''');
-    var nullable_i = decoratedTypeAnnotation('int i').node;
-    var nullable_j = decoratedTypeAnnotation('int j').node;
-    assertNullCheck(checkExpression('j/*check*/'), nullable_j,
-        contextNode: nullable_i);
-  }
-
-  test_methodInvocation_return_type() async {
-    await analyze('''
-class C {
-  bool m() => true;
-}
-bool f(C c) => c.m();
-''');
-    assertEdge(decoratedTypeAnnotation('bool m').node,
-        decoratedTypeAnnotation('bool f').node,
-        hard: false);
-  }
-
-  test_methodInvocation_return_type_null_aware() async {
-    await analyze('''
-class C {
-  bool m() => true;
-}
-bool f(C c) => (c?.m());
-''');
-    var lubNode =
-        decoratedExpressionType('(c?.m())').node as NullabilityNodeForLUB;
-    expect(lubNode.left, same(decoratedTypeAnnotation('C c').node));
-    expect(lubNode.right, same(decoratedTypeAnnotation('bool m').node));
-    assertEdge(lubNode, decoratedTypeAnnotation('bool f').node, hard: false);
-  }
-
-  test_methodInvocation_target_check() async {
-    await analyze('''
-class C {
-  void m() {}
-}
-void test(C c) {
-  c.m();
-}
-''');
-
-    assertNullCheck(
-        checkExpression('c.m'), decoratedTypeAnnotation('C c').node);
-  }
-
-  test_methodInvocation_target_check_cascaded() async {
-    await analyze('''
-class C {
-  void m() {}
-}
-void test(C c) {
-  c..m();
-}
-''');
-
-    assertNullCheck(
-        checkExpression('c..m'), decoratedTypeAnnotation('C c').node);
-  }
-
-  test_methodInvocation_target_demonstrates_non_null_intent() async {
-    await analyze('''
-class C {
-  void m() {}
-}
-void test(C c) {
-  c.m();
-}
-''');
-
-    assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true);
-  }
-
-  test_methodInvocation_target_demonstrates_non_null_intent_cascaded() async {
-    await analyze('''
-class C {
-  void m() {}
-}
-void test(C c) {
-  c..m();
-}
-''');
-
-    assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true);
-  }
-
-  test_never() async {
-    await analyze('');
-
-    expect(NullabilityNode.never.isNullable, isFalse);
-  }
-
-  test_parenthesizedExpression() async {
-    await analyze('''
-int f() {
-  return (null);
-}
-''');
-
-    assertNullCheck(checkExpression('(null)'), NullabilityNode.always,
-        contextNode: decoratedTypeAnnotation('int').node);
-  }
-
-  test_prefixedIdentifier_field_type() async {
-    await analyze('''
-class C {
-  bool b = true;
-}
-bool f(C c) => c.b;
-''');
-    assertEdge(decoratedTypeAnnotation('bool b').node,
-        decoratedTypeAnnotation('bool f').node,
-        hard: false);
-  }
-
-  test_prefixedIdentifier_getter_type() async {
-    await analyze('''
-class C {
-  bool get b => true;
-}
-bool f(C c) => c.b;
-''');
-    assertEdge(decoratedTypeAnnotation('bool get').node,
-        decoratedTypeAnnotation('bool f').node,
-        hard: false);
-  }
-
-  test_prefixedIdentifier_target_check() async {
-    await analyze('''
-class C {
-  int get x => 1;
-}
-void test(C c) {
-  c.x;
-}
-''');
-
-    assertNullCheck(
-        checkExpression('c.x'), decoratedTypeAnnotation('C c').node);
-  }
-
-  test_prefixedIdentifier_target_demonstrates_non_null_intent() async {
-    await analyze('''
-class C {
-  int get x => 1;
-}
-void test(C c) {
-  c.x;
-}
-''');
-
-    assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true);
-  }
-
-  test_propertyAccess_return_type() async {
-    await analyze('''
-class C {
-  bool get b => true;
-}
-bool f(C c) => (c).b;
-''');
-    assertEdge(decoratedTypeAnnotation('bool get').node,
-        decoratedTypeAnnotation('bool f').node,
-        hard: false);
-  }
-
-  test_propertyAccess_return_type_null_aware() async {
-    await analyze('''
-class C {
-  bool get b => true;
-}
-bool f(C c) => (c?.b);
-''');
-    var lubNode =
-        decoratedExpressionType('(c?.b)').node as NullabilityNodeForLUB;
-    expect(lubNode.left, same(decoratedTypeAnnotation('C c').node));
-    expect(lubNode.right, same(decoratedTypeAnnotation('bool get b').node));
-    assertEdge(lubNode, decoratedTypeAnnotation('bool f').node, hard: false);
-  }
-
-  test_propertyAccess_target_check() async {
-    await analyze('''
-class C {
-  int get x => 1;
-}
-void test(C c) {
-  (c).x;
-}
-''');
-
-    assertNullCheck(
-        checkExpression('c).x'), decoratedTypeAnnotation('C c').node);
-  }
-
-  test_return_implicit_null() async {
-    verifyNoTestUnitErrors = false;
-    await analyze('''
-int f() {
-  return;
-}
-''');
-
-    assertEdge(NullabilityNode.always, decoratedTypeAnnotation('int').node,
-        hard: false);
-  }
-
-  test_return_null() async {
-    await analyze('''
-int f() {
-  return null;
-}
-''');
-
-    assertNullCheck(checkExpression('null'), NullabilityNode.always,
-        contextNode: decoratedTypeAnnotation('int').node);
-  }
-
-  test_return_null_generic() async {
-    await analyze('''
-class C<T> {
-  T f() {
-    return null;
-  }
-}
-''');
-    var tNode = decoratedTypeAnnotation('T f').node;
-    assertEdge(always, tNode, hard: false);
-    assertNullCheck(checkExpression('null'), always, contextNode: tNode);
-  }
-
-  test_simpleIdentifier_local() async {
-    await analyze('''
-main() {
-  int i = 0;
-  int j = i;
-}
-''');
-
-    assertEdge(decoratedTypeAnnotation('int i').node,
-        decoratedTypeAnnotation('int j').node,
-        hard: true);
-  }
-
-  test_soft_edge_for_non_variable_reference() async {
-    // Edges originating in things other than variable references should be
-    // soft.
-    await analyze('''
-int f() => null;
-''');
-    assertEdge(always, decoratedTypeAnnotation('int').node, hard: false);
-  }
-
-  test_stringLiteral() async {
-    // TODO(paulberry): also test string interpolations
-    await analyze('''
-String f() {
-  return 'x';
-}
-''');
-    assertNoUpstreamNullability(decoratedTypeAnnotation('String').node);
-  }
-
-  test_thisExpression() async {
-    await analyze('''
-class C {
-  C f() => this;
-}
-''');
-
-    assertNoUpstreamNullability(decoratedTypeAnnotation('C f').node);
-  }
-
-  test_throwExpression() async {
-    await analyze('''
-int f() {
-  return throw null;
-}
-''');
-    assertNoUpstreamNullability(decoratedTypeAnnotation('int').node);
-  }
-
-  test_type_argument_explicit_bound() async {
-    await analyze('''
-class C<T extends Object> {}
-void f(C<int> c) {}
-''');
-    assertEdge(decoratedTypeAnnotation('int>').node,
-        decoratedTypeAnnotation('Object>').node,
-        hard: true);
-  }
-
-  test_typeName() async {
-    await analyze('''
-Type f() {
-  return int;
-}
-''');
-    assertNoUpstreamNullability(decoratedTypeAnnotation('Type').node);
-  }
-
-  test_variableDeclaration() async {
-    await analyze('''
-void f(int i) {
-  int j = i;
-}
-''');
-    assertEdge(decoratedTypeAnnotation('int i').node,
-        decoratedTypeAnnotation('int j').node,
-        hard: true);
-  }
-}
-
-class MigrationVisitorTestBase extends AbstractSingleUnitTest {
-  final _Variables _variables;
-
-  final NullabilityGraph graph;
-
-  MigrationVisitorTestBase() : this._(NullabilityGraph());
-
-  MigrationVisitorTestBase._(this.graph) : _variables = _Variables();
-
-  NullabilityNode get always => NullabilityNode.always;
-
-  NullabilityNode get never => NullabilityNode.never;
-
-  TypeProvider get typeProvider => testAnalysisResult.typeProvider;
-
-  Future<CompilationUnit> analyze(String code) async {
-    await resolveTestUnit(code);
-    testUnit
-        .accept(NodeBuilder(_variables, testSource, null, graph, typeProvider));
-    return testUnit;
-  }
-
-  void assertEdge(NullabilityNode source, NullabilityNode destination,
-      {@required bool hard}) {
-    var edges = getEdges(source, destination);
-    if (edges.length == 0) {
-      fail('Expected edge $source -> $destination, found none');
-    } else if (edges.length != 1) {
-      fail('Found multiple edges $source -> $destination');
-    } else {
-      var edge = edges[0];
-      expect(edge.hard, hard);
-    }
-  }
-
-  void assertNoEdge(NullabilityNode source, NullabilityNode destination) {
-    var edges = getEdges(source, destination);
-    if (edges.isNotEmpty) {
-      fail('Expected no edge $source -> $destination, found ${edges.length}');
-    }
-  }
-
-  /// Gets the [DecoratedType] associated with the generic function type
-  /// annotation whose text is [text].
-  DecoratedType decoratedGenericFunctionTypeAnnotation(String text) {
-    return _variables.decoratedTypeAnnotation(
-        testSource, findNode.genericFunctionType(text));
-  }
-
-  /// Gets the [DecoratedType] associated with the type annotation whose text
-  /// is [text].
-  DecoratedType decoratedTypeAnnotation(String text) {
-    return _variables.decoratedTypeAnnotation(
-        testSource, findNode.typeAnnotation(text));
-  }
-
-  List<NullabilityEdge> getEdges(
-          NullabilityNode source, NullabilityNode destination) =>
-      graph
-          .getUpstreamEdges(destination)
-          .where((e) => e.primarySource == source)
-          .toList();
-
-  NullabilityNode possiblyOptionalParameter(String text) {
-    return _variables
-        .possiblyOptionalParameter(findNode.defaultParameter(text));
-  }
-
-  /// Gets the [ConditionalDiscard] information associated with the statement
-  /// whose text is [text].
-  ConditionalDiscard statementDiscard(String text) {
-    return _variables.conditionalDiscard(findNode.statement(text));
-  }
-}
-
-@reflectiveTest
-class NodeBuilderTest extends MigrationVisitorTestBase {
-  /// Gets the [DecoratedType] associated with the function declaration whose
-  /// name matches [search].
-  DecoratedType decoratedFunctionType(String search) =>
-      _variables.decoratedElementType(
-          findNode.functionDeclaration(search).declaredElement);
-
-  DecoratedType decoratedTypeParameterBound(String search) => _variables
-      .decoratedElementType(findNode.typeParameter(search).declaredElement);
-
-  test_dynamic_type() async {
-    await analyze('''
-dynamic f() {}
-''');
-    var decoratedType = decoratedTypeAnnotation('dynamic');
-    expect(decoratedFunctionType('f').returnType, same(decoratedType));
-    assertEdge(always, decoratedType.node, hard: false);
-  }
-
-  test_field_type_simple() async {
-    await analyze('''
-class C {
-  int f = 0;
-}
-''');
-    var decoratedType = decoratedTypeAnnotation('int');
-    expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
-    expect(
-        _variables.decoratedElementType(
-            findNode.fieldDeclaration('f').fields.variables[0].declaredElement),
-        same(decoratedType));
-  }
-
-  test_genericFunctionType_namedParameterType() async {
-    await analyze('''
-void f(void Function({int y}) x) {}
-''');
-    var decoratedType =
-        decoratedGenericFunctionTypeAnnotation('void Function({int y})');
-    expect(decoratedFunctionType('f').positionalParameters[0],
-        same(decoratedType));
-    expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
-    var decoratedIntType = decoratedTypeAnnotation('int');
-    expect(decoratedType.namedParameters['y'], same(decoratedIntType));
-    expect(decoratedIntType.node, isNotNull);
-    expect(decoratedIntType.node, isNot(NullabilityNode.never));
-  }
-
-  test_genericFunctionType_returnType() async {
-    await analyze('''
-void f(int Function() x) {}
-''');
-    var decoratedType =
-        decoratedGenericFunctionTypeAnnotation('int Function()');
-    expect(decoratedFunctionType('f').positionalParameters[0],
-        same(decoratedType));
-    expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
-    var decoratedIntType = decoratedTypeAnnotation('int');
-    expect(decoratedType.returnType, same(decoratedIntType));
-    expect(decoratedIntType.node, isNotNull);
-    expect(decoratedIntType.node, isNot(NullabilityNode.never));
-  }
-
-  test_genericFunctionType_unnamedParameterType() async {
-    await analyze('''
-void f(void Function(int) x) {}
-''');
-    var decoratedType =
-        decoratedGenericFunctionTypeAnnotation('void Function(int)');
-    expect(decoratedFunctionType('f').positionalParameters[0],
-        same(decoratedType));
-    expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
-    var decoratedIntType = decoratedTypeAnnotation('int');
-    expect(decoratedType.positionalParameters[0], same(decoratedIntType));
-    expect(decoratedIntType.node, isNotNull);
-    expect(decoratedIntType.node, isNot(NullabilityNode.never));
-  }
-
-  test_interfaceType_typeParameter() async {
-    await analyze('''
-void f(List<int> x) {}
-''');
-    var decoratedListType = decoratedTypeAnnotation('List<int>');
-    expect(decoratedFunctionType('f').positionalParameters[0],
-        same(decoratedListType));
-    expect(decoratedListType.node, isNotNull);
-    expect(decoratedListType.node, isNot(NullabilityNode.never));
-    var decoratedIntType = decoratedTypeAnnotation('int');
-    expect(decoratedListType.typeArguments[0], same(decoratedIntType));
-    expect(decoratedIntType.node, isNotNull);
-    expect(decoratedIntType.node, isNot(NullabilityNode.never));
-  }
-
-  test_topLevelFunction_parameterType_implicit_dynamic() async {
-    await analyze('''
-void f(x) {}
-''');
-    var decoratedType =
-        _variables.decoratedElementType(findNode.simple('x').staticElement);
-    expect(decoratedFunctionType('f').positionalParameters[0],
-        same(decoratedType));
-    expect(decoratedType.type.isDynamic, isTrue);
-    expect(decoratedType.node.isNullable, isTrue);
-  }
-
-  test_topLevelFunction_parameterType_named_no_default() async {
-    await analyze('''
-void f({String s}) {}
-''');
-    var decoratedType = decoratedTypeAnnotation('String');
-    var functionType = decoratedFunctionType('f');
-    expect(functionType.namedParameters['s'], same(decoratedType));
-    expect(decoratedType.node, isNotNull);
-    expect(decoratedType.node, isNot(NullabilityNode.never));
-    expect(decoratedType.node, isNot(NullabilityNode.always));
-    expect(functionType.namedParameters['s'].node.isPossiblyOptional, true);
-  }
-
-  test_topLevelFunction_parameterType_named_no_default_required() async {
-    addMetaPackage();
-    await analyze('''
-import 'package:meta/meta.dart';
-void f({@required String s}) {}
-''');
-    var decoratedType = decoratedTypeAnnotation('String');
-    var functionType = decoratedFunctionType('f');
-    expect(functionType.namedParameters['s'], same(decoratedType));
-    expect(decoratedType.node, isNotNull);
-    expect(decoratedType.node, isNot(NullabilityNode.never));
-    expect(decoratedType.node, isNot(NullabilityNode.always));
-    expect(functionType.namedParameters['s'].node.isPossiblyOptional, false);
-  }
-
-  test_topLevelFunction_parameterType_named_with_default() async {
-    await analyze('''
-void f({String s: 'x'}) {}
-''');
-    var decoratedType = decoratedTypeAnnotation('String');
-    var functionType = decoratedFunctionType('f');
-    expect(functionType.namedParameters['s'], same(decoratedType));
-    expect(decoratedType.node, isNotNull);
-    expect(decoratedType.node, isNot(NullabilityNode.never));
-    expect(functionType.namedParameters['s'].node.isPossiblyOptional, false);
-  }
-
-  test_topLevelFunction_parameterType_positionalOptional() async {
-    await analyze('''
-void f([int i]) {}
-''');
-    var decoratedType = decoratedTypeAnnotation('int');
-    expect(decoratedFunctionType('f').positionalParameters[0],
-        same(decoratedType));
-    expect(decoratedType.node, isNotNull);
-    expect(decoratedType.node, isNot(NullabilityNode.never));
-  }
-
-  test_topLevelFunction_parameterType_simple() async {
-    await analyze('''
-void f(int i) {}
-''');
-    var decoratedType = decoratedTypeAnnotation('int');
-    expect(decoratedFunctionType('f').positionalParameters[0],
-        same(decoratedType));
-    expect(decoratedType.node, isNotNull);
-    expect(decoratedType.node, isNot(NullabilityNode.never));
-  }
-
-  test_topLevelFunction_returnType_implicit_dynamic() async {
-    await analyze('''
-f() {}
-''');
-    var decoratedType = decoratedFunctionType('f').returnType;
-    expect(decoratedType.type.isDynamic, isTrue);
-    expect(decoratedType.node.isNullable, isTrue);
-  }
-
-  test_topLevelFunction_returnType_simple() async {
-    await analyze('''
-int f() => 0;
-''');
-    var decoratedType = decoratedTypeAnnotation('int');
-    expect(decoratedFunctionType('f').returnType, same(decoratedType));
-    expect(decoratedType.node, isNotNull);
-    expect(decoratedType.node, isNot(NullabilityNode.never));
-  }
-
-  test_type_comment_bang() async {
-    await analyze('''
-void f(int/*!*/ i) {}
-''');
-    assertEdge(decoratedTypeAnnotation('int').node, never, hard: true);
-  }
-
-  test_type_comment_question() async {
-    await analyze('''
-void f(int/*?*/ i) {}
-''');
-    assertEdge(always, decoratedTypeAnnotation('int').node, hard: false);
-  }
-
-  test_type_parameter_explicit_bound() async {
-    await analyze('''
-class C<T extends Object> {}
-''');
-    var bound = decoratedTypeParameterBound('T');
-    expect(decoratedTypeAnnotation('Object'), same(bound));
-    expect(bound.node, isNot(NullabilityNode.always));
-    expect(bound.type, typeProvider.objectType);
-  }
-
-  test_type_parameter_implicit_bound() async {
-    // The implicit bound of `T` is automatically `Object?`.  TODO(paulberry):
-    // consider making it possible for type inference to infer an explicit bound
-    // of `Object`.
-    await analyze('''
-class C<T> {}
-''');
-    var bound = decoratedTypeParameterBound('T');
-    expect(bound.node.isNullable, isTrue);
-    expect(bound.type, same(typeProvider.objectType));
-  }
-
-  test_variableDeclaration_type_simple() async {
-    await analyze('''
-main() {
-  int i;
-}
-''');
-    var decoratedType = decoratedTypeAnnotation('int');
-    expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
-  }
-
-  test_void_type() async {
-    await analyze('''
-void f() {}
-''');
-    var decoratedType = decoratedTypeAnnotation('void');
-    expect(decoratedFunctionType('f').returnType, same(decoratedType));
-    assertEdge(always, decoratedType.node, hard: false);
-  }
-}
-
-/// Mock representation of constraint variables.
-class _Variables extends Variables {
-  final _conditionalDiscard = <AstNode, ConditionalDiscard>{};
-
-  final _decoratedExpressionTypes = <Expression, DecoratedType>{};
-
-  final _expressionChecks = <Expression, ExpressionChecks>{};
-
-  final _possiblyOptional = <DefaultFormalParameter, NullabilityNode>{};
-
-  /// Gets the [ExpressionChecks] associated with the given [expression].
-  ExpressionChecks checkExpression(Expression expression) =>
-      _expressionChecks[_normalizeExpression(expression)];
-
-  /// Gets the [conditionalDiscard] associated with the given [expression].
-  ConditionalDiscard conditionalDiscard(AstNode node) =>
-      _conditionalDiscard[node];
-
-  /// Gets the [DecoratedType] associated with the given [expression].
-  DecoratedType decoratedExpressionType(Expression expression) =>
-      _decoratedExpressionTypes[_normalizeExpression(expression)];
-
-  /// Gets the [NullabilityNode] associated with the possibility that
-  /// [parameter] may be optional.
-  NullabilityNode possiblyOptionalParameter(DefaultFormalParameter parameter) =>
-      _possiblyOptional[parameter];
-
-  @override
-  void recordConditionalDiscard(
-      Source source, AstNode node, ConditionalDiscard conditionalDiscard) {
-    _conditionalDiscard[node] = conditionalDiscard;
-    super.recordConditionalDiscard(source, node, conditionalDiscard);
-  }
-
-  void recordDecoratedExpressionType(Expression node, DecoratedType type) {
-    super.recordDecoratedExpressionType(node, type);
-    _decoratedExpressionTypes[_normalizeExpression(node)] = type;
-  }
-
-  @override
-  void recordExpressionChecks(
-      Source source, Expression expression, ExpressionChecks checks) {
-    super.recordExpressionChecks(source, expression, checks);
-    _expressionChecks[_normalizeExpression(expression)] = checks;
-  }
-
-  @override
-  void recordPossiblyOptional(
-      Source source, DefaultFormalParameter parameter, NullabilityNode node) {
-    _possiblyOptional[parameter] = node;
-    super.recordPossiblyOptional(source, parameter, node);
-  }
-
-  /// Unwraps any parentheses surrounding [expression].
-  Expression _normalizeExpression(Expression expression) {
-    while (expression is ParenthesizedExpression) {
-      expression = (expression as ParenthesizedExpression).expression;
-    }
-    return expression;
-  }
-}
diff --git a/pkg/nnbd_migration/test/migration_visitor_test_base.dart b/pkg/nnbd_migration/test/migration_visitor_test_base.dart
new file mode 100644
index 0000000..2e2e9d1
--- /dev/null
+++ b/pkg/nnbd_migration/test/migration_visitor_test_base.dart
@@ -0,0 +1,170 @@
+// Copyright (c) 2019, 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 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/src/generated/resolver.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:meta/meta.dart';
+import 'package:nnbd_migration/src/conditional_discard.dart';
+import 'package:nnbd_migration/src/decorated_type.dart';
+import 'package:nnbd_migration/src/expression_checks.dart';
+import 'package:nnbd_migration/src/node_builder.dart';
+import 'package:nnbd_migration/src/nullability_node.dart';
+import 'package:nnbd_migration/src/variables.dart';
+import 'package:test/test.dart';
+
+import 'abstract_single_unit.dart';
+
+/// Mock representation of constraint variables.
+class InstrumentedVariables extends Variables {
+  final _conditionalDiscard = <AstNode, ConditionalDiscard>{};
+
+  final _decoratedExpressionTypes = <Expression, DecoratedType>{};
+
+  final _expressionChecks = <Expression, ExpressionChecks>{};
+
+  final _possiblyOptional = <DefaultFormalParameter, NullabilityNode>{};
+
+  InstrumentedVariables(NullabilityGraph graph) : super(graph);
+
+  /// Gets the [ExpressionChecks] associated with the given [expression].
+  ExpressionChecks checkExpression(Expression expression) =>
+      _expressionChecks[_normalizeExpression(expression)];
+
+  /// Gets the [conditionalDiscard] associated with the given [expression].
+  ConditionalDiscard conditionalDiscard(AstNode node) =>
+      _conditionalDiscard[node];
+
+  /// Gets the [DecoratedType] associated with the given [expression].
+  DecoratedType decoratedExpressionType(Expression expression) =>
+      _decoratedExpressionTypes[_normalizeExpression(expression)];
+
+  /// Gets the [NullabilityNode] associated with the possibility that
+  /// [parameter] may be optional.
+  NullabilityNode possiblyOptionalParameter(DefaultFormalParameter parameter) =>
+      _possiblyOptional[parameter];
+
+  @override
+  void recordConditionalDiscard(
+      Source source, AstNode node, ConditionalDiscard conditionalDiscard) {
+    _conditionalDiscard[node] = conditionalDiscard;
+    super.recordConditionalDiscard(source, node, conditionalDiscard);
+  }
+
+  void recordDecoratedExpressionType(Expression node, DecoratedType type) {
+    super.recordDecoratedExpressionType(node, type);
+    _decoratedExpressionTypes[_normalizeExpression(node)] = type;
+  }
+
+  @override
+  void recordExpressionChecks(
+      Source source, Expression expression, ExpressionChecks checks) {
+    super.recordExpressionChecks(source, expression, checks);
+    _expressionChecks[_normalizeExpression(expression)] = checks;
+  }
+
+  @override
+  void recordPossiblyOptional(
+      Source source, DefaultFormalParameter parameter, NullabilityNode node) {
+    _possiblyOptional[parameter] = node;
+    super.recordPossiblyOptional(source, parameter, node);
+  }
+
+  /// Unwraps any parentheses surrounding [expression].
+  Expression _normalizeExpression(Expression expression) {
+    while (expression is ParenthesizedExpression) {
+      expression = (expression as ParenthesizedExpression).expression;
+    }
+    return expression;
+  }
+}
+
+class MigrationVisitorTestBase extends AbstractSingleUnitTest {
+  final InstrumentedVariables variables;
+
+  final NullabilityGraphForTesting graph;
+
+  MigrationVisitorTestBase() : this._(NullabilityGraphForTesting());
+
+  MigrationVisitorTestBase._(this.graph)
+      : variables = InstrumentedVariables(graph);
+
+  NullabilityNode get always => graph.always;
+
+  NullabilityNode get never => graph.never;
+
+  TypeProvider get typeProvider => testAnalysisResult.typeProvider;
+
+  Future<CompilationUnit> analyze(String code) async {
+    await resolveTestUnit(code);
+    testUnit
+        .accept(NodeBuilder(variables, testSource, null, graph, typeProvider));
+    return testUnit;
+  }
+
+  NullabilityEdge assertEdge(
+      NullabilityNode source, NullabilityNode destination,
+      {@required bool hard, List<NullabilityNode> guards = const []}) {
+    var edges = getEdges(source, destination);
+    if (edges.length == 0) {
+      fail('Expected edge $source -> $destination, found none');
+    } else if (edges.length != 1) {
+      fail('Found multiple edges $source -> $destination');
+    } else {
+      var edge = edges[0];
+      expect(edge.hard, hard);
+      expect(edge.guards, unorderedEquals(guards));
+      return edge;
+    }
+  }
+
+  void assertNoEdge(NullabilityNode source, NullabilityNode destination) {
+    var edges = getEdges(source, destination);
+    if (edges.isNotEmpty) {
+      fail('Expected no edge $source -> $destination, found ${edges.length}');
+    }
+  }
+
+  void assertUnion(NullabilityNode x, NullabilityNode y) {
+    var edges = getEdges(x, y);
+    for (var edge in edges) {
+      if (edge.isUnion) {
+        expect(edge.sources, hasLength(1));
+        return;
+      }
+    }
+    fail('Expected union between $x and $y, not found');
+  }
+
+  /// Gets the [DecoratedType] associated with the generic function type
+  /// annotation whose text is [text].
+  DecoratedType decoratedGenericFunctionTypeAnnotation(String text) {
+    return variables.decoratedTypeAnnotation(
+        testSource, findNode.genericFunctionType(text));
+  }
+
+  /// Gets the [DecoratedType] associated with the type annotation whose text
+  /// is [text].
+  DecoratedType decoratedTypeAnnotation(String text) {
+    return variables.decoratedTypeAnnotation(
+        testSource, findNode.typeAnnotation(text));
+  }
+
+  List<NullabilityEdge> getEdges(
+          NullabilityNode source, NullabilityNode destination) =>
+      graph
+          .getUpstreamEdges(destination)
+          .where((e) => e.primarySource == source)
+          .toList();
+
+  NullabilityNode possiblyOptionalParameter(String text) {
+    return variables.possiblyOptionalParameter(findNode.defaultParameter(text));
+  }
+
+  /// Gets the [ConditionalDiscard] information associated with the statement
+  /// whose text is [text].
+  ConditionalDiscard statementDiscard(String text) {
+    return variables.conditionalDiscard(findNode.statement(text));
+  }
+}
diff --git a/pkg/nnbd_migration/test/node_builder_test.dart b/pkg/nnbd_migration/test/node_builder_test.dart
new file mode 100644
index 0000000..ef4476f
--- /dev/null
+++ b/pkg/nnbd_migration/test/node_builder_test.dart
@@ -0,0 +1,352 @@
+// Copyright (c) 2019, 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 'package:nnbd_migration/src/decorated_type.dart';
+import 'package:nnbd_migration/src/nullability_node.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'migration_visitor_test_base.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(NodeBuilderTest);
+  });
+}
+
+@reflectiveTest
+class NodeBuilderTest extends MigrationVisitorTestBase {
+  /// Gets the [DecoratedType] associated with the constructor declaration whose
+  /// name matches [search].
+  DecoratedType decoratedConstructorDeclaration(String search) => variables
+      .decoratedElementType(findNode.constructor(search).declaredElement);
+
+  /// Gets the [DecoratedType] associated with the function declaration whose
+  /// name matches [search].
+  DecoratedType decoratedFunctionType(String search) =>
+      variables.decoratedElementType(
+          findNode.functionDeclaration(search).declaredElement);
+
+  DecoratedType decoratedTypeParameterBound(String search) => variables
+      .decoratedElementType(findNode.typeParameter(search).declaredElement);
+
+  test_constructor_returnType_implicit_dynamic() async {
+    await analyze('''
+class C {
+  C();
+}
+''');
+    var decoratedType = decoratedConstructorDeclaration('C(').returnType;
+    expect(decoratedType.node, same(never));
+  }
+
+  test_dynamic_type() async {
+    await analyze('''
+dynamic f() {}
+''');
+    var decoratedType = decoratedTypeAnnotation('dynamic');
+    expect(decoratedFunctionType('f').returnType, same(decoratedType));
+    assertEdge(always, decoratedType.node, hard: false);
+  }
+
+  test_field_type_simple() async {
+    await analyze('''
+class C {
+  int f = 0;
+}
+''');
+    var decoratedType = decoratedTypeAnnotation('int');
+    expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
+    expect(
+        variables.decoratedElementType(
+            findNode.fieldDeclaration('f').fields.variables[0].declaredElement),
+        same(decoratedType));
+  }
+
+  test_genericFunctionType_namedParameterType() async {
+    await analyze('''
+void f(void Function({int y}) x) {}
+''');
+    var decoratedType =
+        decoratedGenericFunctionTypeAnnotation('void Function({int y})');
+    expect(decoratedFunctionType('f').positionalParameters[0],
+        same(decoratedType));
+    expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
+    var decoratedIntType = decoratedTypeAnnotation('int');
+    expect(decoratedType.namedParameters['y'], same(decoratedIntType));
+    expect(decoratedIntType.node, isNotNull);
+    expect(decoratedIntType.node, isNot(never));
+  }
+
+  test_genericFunctionType_returnType() async {
+    await analyze('''
+void f(int Function() x) {}
+''');
+    var decoratedType =
+        decoratedGenericFunctionTypeAnnotation('int Function()');
+    expect(decoratedFunctionType('f').positionalParameters[0],
+        same(decoratedType));
+    expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
+    var decoratedIntType = decoratedTypeAnnotation('int');
+    expect(decoratedType.returnType, same(decoratedIntType));
+    expect(decoratedIntType.node, isNotNull);
+    expect(decoratedIntType.node, isNot(never));
+  }
+
+  test_genericFunctionType_unnamedParameterType() async {
+    await analyze('''
+void f(void Function(int) x) {}
+''');
+    var decoratedType =
+        decoratedGenericFunctionTypeAnnotation('void Function(int)');
+    expect(decoratedFunctionType('f').positionalParameters[0],
+        same(decoratedType));
+    expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
+    var decoratedIntType = decoratedTypeAnnotation('int');
+    expect(decoratedType.positionalParameters[0], same(decoratedIntType));
+    expect(decoratedIntType.node, isNotNull);
+    expect(decoratedIntType.node, isNot(never));
+  }
+
+  test_interfaceType_generic_instantiate_to_dynamic() async {
+    await analyze('''
+void f(List x) {}
+''');
+    var decoratedListType = decoratedTypeAnnotation('List');
+    expect(decoratedFunctionType('f').positionalParameters[0],
+        same(decoratedListType));
+    expect(decoratedListType.node, isNotNull);
+    expect(decoratedListType.node, isNot(never));
+    var decoratedArgType = decoratedListType.typeArguments[0];
+    expect(decoratedArgType.node, same(always));
+  }
+
+  test_interfaceType_generic_instantiate_to_generic_type() async {
+    await analyze('''
+class C<T> {}
+class D<T extends C<int>> {}
+void f(D x) {}
+''');
+    var decoratedListType = decoratedTypeAnnotation('D x');
+    expect(decoratedFunctionType('f').positionalParameters[0],
+        same(decoratedListType));
+    expect(decoratedListType.node, TypeMatcher<NullabilityNodeMutable>());
+    expect(decoratedListType.typeArguments, hasLength(1));
+    var decoratedArgType = decoratedListType.typeArguments[0];
+    expect(decoratedArgType.node, TypeMatcher<NullabilityNodeMutable>());
+    expect(decoratedArgType.typeArguments, hasLength(1));
+    var decoratedArgArgType = decoratedArgType.typeArguments[0];
+    expect(decoratedArgArgType.node, TypeMatcher<NullabilityNodeMutable>());
+    expect(decoratedArgArgType.typeArguments, isEmpty);
+  }
+
+  test_interfaceType_generic_instantiate_to_generic_type_2() async {
+    await analyze('''
+class C<T, U> {}
+class D<T extends C<int, String>, U extends C<num, double>> {}
+void f(D x) {}
+''');
+    var decoratedDType = decoratedTypeAnnotation('D x');
+    expect(decoratedFunctionType('f').positionalParameters[0],
+        same(decoratedDType));
+    expect(decoratedDType.node, TypeMatcher<NullabilityNodeMutable>());
+    expect(decoratedDType.typeArguments, hasLength(2));
+    var decoratedArg0Type = decoratedDType.typeArguments[0];
+    expect(decoratedArg0Type.node, TypeMatcher<NullabilityNodeMutable>());
+    expect(decoratedArg0Type.typeArguments, hasLength(2));
+    var decoratedArg0Arg0Type = decoratedArg0Type.typeArguments[0];
+    expect(decoratedArg0Arg0Type.node, TypeMatcher<NullabilityNodeMutable>());
+    expect(decoratedArg0Arg0Type.typeArguments, isEmpty);
+    var decoratedArg0Arg1Type = decoratedArg0Type.typeArguments[1];
+    expect(decoratedArg0Arg1Type.node, TypeMatcher<NullabilityNodeMutable>());
+    expect(decoratedArg0Arg1Type.typeArguments, isEmpty);
+    var decoratedArg1Type = decoratedDType.typeArguments[1];
+    expect(decoratedArg1Type.node, TypeMatcher<NullabilityNodeMutable>());
+    expect(decoratedArg1Type.typeArguments, hasLength(2));
+    var decoratedArg1Arg0Type = decoratedArg1Type.typeArguments[0];
+    expect(decoratedArg1Arg0Type.node, TypeMatcher<NullabilityNodeMutable>());
+    expect(decoratedArg1Arg0Type.typeArguments, isEmpty);
+    var decoratedArg1Arg1Type = decoratedArg1Type.typeArguments[1];
+    expect(decoratedArg1Arg1Type.node, TypeMatcher<NullabilityNodeMutable>());
+    expect(decoratedArg1Arg1Type.typeArguments, isEmpty);
+  }
+
+  test_interfaceType_generic_instantiate_to_object() async {
+    await analyze('''
+class C<T extends Object> {}
+void f(C x) {}
+''');
+    var decoratedListType = decoratedTypeAnnotation('C x');
+    expect(decoratedFunctionType('f').positionalParameters[0],
+        same(decoratedListType));
+    expect(decoratedListType.node, TypeMatcher<NullabilityNodeMutable>());
+    expect(decoratedListType.typeArguments, hasLength(1));
+    var decoratedArgType = decoratedListType.typeArguments[0];
+    expect(decoratedArgType.node, TypeMatcher<NullabilityNodeMutable>());
+    expect(decoratedArgType.typeArguments, isEmpty);
+  }
+
+  test_interfaceType_typeParameter() async {
+    await analyze('''
+void f(List<int> x) {}
+''');
+    var decoratedListType = decoratedTypeAnnotation('List<int>');
+    expect(decoratedFunctionType('f').positionalParameters[0],
+        same(decoratedListType));
+    expect(decoratedListType.node, isNotNull);
+    expect(decoratedListType.node, isNot(never));
+    var decoratedIntType = decoratedTypeAnnotation('int');
+    expect(decoratedListType.typeArguments[0], same(decoratedIntType));
+    expect(decoratedIntType.node, isNotNull);
+    expect(decoratedIntType.node, isNot(never));
+  }
+
+  test_topLevelFunction_parameterType_implicit_dynamic() async {
+    await analyze('''
+void f(x) {}
+''');
+    var decoratedType =
+        variables.decoratedElementType(findNode.simple('x').staticElement);
+    expect(decoratedFunctionType('f').positionalParameters[0],
+        same(decoratedType));
+    expect(decoratedType.type.isDynamic, isTrue);
+    assertUnion(always, decoratedType.node);
+  }
+
+  test_topLevelFunction_parameterType_named_no_default() async {
+    await analyze('''
+void f({String s}) {}
+''');
+    var decoratedType = decoratedTypeAnnotation('String');
+    var functionType = decoratedFunctionType('f');
+    expect(functionType.namedParameters['s'], same(decoratedType));
+    expect(decoratedType.node, isNotNull);
+    expect(decoratedType.node, isNot(never));
+    expect(decoratedType.node, isNot(always));
+    expect(functionType.namedParameters['s'].node.isPossiblyOptional, true);
+  }
+
+  test_topLevelFunction_parameterType_named_no_default_required() async {
+    addMetaPackage();
+    await analyze('''
+import 'package:meta/meta.dart';
+void f({@required String s}) {}
+''');
+    var decoratedType = decoratedTypeAnnotation('String');
+    var functionType = decoratedFunctionType('f');
+    expect(functionType.namedParameters['s'], same(decoratedType));
+    expect(decoratedType.node, isNotNull);
+    expect(decoratedType.node, isNot(never));
+    expect(decoratedType.node, isNot(always));
+    expect(functionType.namedParameters['s'].node.isPossiblyOptional, false);
+  }
+
+  test_topLevelFunction_parameterType_named_with_default() async {
+    await analyze('''
+void f({String s: 'x'}) {}
+''');
+    var decoratedType = decoratedTypeAnnotation('String');
+    var functionType = decoratedFunctionType('f');
+    expect(functionType.namedParameters['s'], same(decoratedType));
+    expect(decoratedType.node, isNotNull);
+    expect(decoratedType.node, isNot(never));
+    expect(functionType.namedParameters['s'].node.isPossiblyOptional, false);
+  }
+
+  test_topLevelFunction_parameterType_positionalOptional() async {
+    await analyze('''
+void f([int i]) {}
+''');
+    var decoratedType = decoratedTypeAnnotation('int');
+    expect(decoratedFunctionType('f').positionalParameters[0],
+        same(decoratedType));
+    expect(decoratedType.node, isNotNull);
+    expect(decoratedType.node, isNot(never));
+  }
+
+  test_topLevelFunction_parameterType_simple() async {
+    await analyze('''
+void f(int i) {}
+''');
+    var decoratedType = decoratedTypeAnnotation('int');
+    expect(decoratedFunctionType('f').positionalParameters[0],
+        same(decoratedType));
+    expect(decoratedType.node, isNotNull);
+    expect(decoratedType.node, isNot(never));
+  }
+
+  test_topLevelFunction_returnType_implicit_dynamic() async {
+    await analyze('''
+f() {}
+''');
+    var decoratedType = decoratedFunctionType('f').returnType;
+    expect(decoratedType.type.isDynamic, isTrue);
+    assertUnion(always, decoratedType.node);
+  }
+
+  test_topLevelFunction_returnType_simple() async {
+    await analyze('''
+int f() => 0;
+''');
+    var decoratedType = decoratedTypeAnnotation('int');
+    expect(decoratedFunctionType('f').returnType, same(decoratedType));
+    expect(decoratedType.node, isNotNull);
+    expect(decoratedType.node, isNot(never));
+  }
+
+  test_type_comment_bang() async {
+    await analyze('''
+void f(int/*!*/ i) {}
+''');
+    assertEdge(decoratedTypeAnnotation('int').node, never, hard: true);
+  }
+
+  test_type_comment_question() async {
+    await analyze('''
+void f(int/*?*/ i) {}
+''');
+    assertEdge(always, decoratedTypeAnnotation('int').node, hard: false);
+  }
+
+  test_type_parameter_explicit_bound() async {
+    await analyze('''
+class C<T extends Object> {}
+''');
+    var bound = decoratedTypeParameterBound('T');
+    expect(decoratedTypeAnnotation('Object'), same(bound));
+    expect(bound.node, isNot(always));
+    expect(bound.type, typeProvider.objectType);
+  }
+
+  test_type_parameter_implicit_bound() async {
+    // The implicit bound of `T` is automatically `Object?`.  TODO(paulberry):
+    // consider making it possible for type inference to infer an explicit bound
+    // of `Object`.
+    await analyze('''
+class C<T> {}
+''');
+    var bound = decoratedTypeParameterBound('T');
+    assertUnion(always, bound.node);
+    expect(bound.type, same(typeProvider.objectType));
+  }
+
+  test_variableDeclaration_type_simple() async {
+    await analyze('''
+main() {
+  int i;
+}
+''');
+    var decoratedType = decoratedTypeAnnotation('int');
+    expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
+  }
+
+  test_void_type() async {
+    await analyze('''
+void f() {}
+''');
+    var decoratedType = decoratedTypeAnnotation('void');
+    expect(decoratedFunctionType('f').returnType, same(decoratedType));
+    assertEdge(always, decoratedType.node, hard: false);
+  }
+}
diff --git a/pkg/nnbd_migration/test/nullability_node_test.dart b/pkg/nnbd_migration/test/nullability_node_test.dart
new file mode 100644
index 0000000..9cc8d36
--- /dev/null
+++ b/pkg/nnbd_migration/test/nullability_node_test.dart
@@ -0,0 +1,549 @@
+// Copyright (c) 2019, 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 'package:nnbd_migration/src/edge_origin.dart';
+import 'package:nnbd_migration/src/nullability_node.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(NullabilityNodeTest);
+  });
+}
+
+@reflectiveTest
+class NullabilityNodeTest {
+  final graph = NullabilityGraphForTesting();
+
+  List<NullabilityEdge> unsatisfiedEdges;
+
+  NullabilityNode get always => graph.always;
+
+  NullabilityNode get never => graph.never;
+
+  void assertUnsatisfied(List<NullabilityEdge> expectedUnsatisfiedEdges) {
+    expect(unsatisfiedEdges, unorderedEquals(expectedUnsatisfiedEdges));
+  }
+
+  NullabilityEdge connect(NullabilityNode source, NullabilityNode destination,
+      {bool hard = false, List<NullabilityNode> guards = const []}) {
+    return graph.connect(source, destination, _TestEdgeOrigin(),
+        hard: hard, guards: guards);
+  }
+
+  NullabilityNode lub(NullabilityNode left, NullabilityNode right) {
+    return NullabilityNode.forLUB(left, right);
+  }
+
+  NullabilityNode newNode(int offset) =>
+      NullabilityNode.forTypeAnnotation(offset);
+
+  void propagate() {
+    unsatisfiedEdges = graph.propagate();
+  }
+
+  NullabilityNode subst(NullabilityNode inner, NullabilityNode outer) {
+    return NullabilityNode.forSubstitution(inner, outer);
+  }
+
+  test_always_and_never_state() {
+    propagate();
+    expect(always.isNullable, isTrue);
+    expect(never.isNullable, isFalse);
+    assertUnsatisfied([]);
+  }
+
+  test_always_and_never_unaffected_by_hard_edges() {
+    var edge = connect(always, never, hard: true);
+    propagate();
+    expect(always.isNullable, isTrue);
+    expect(never.isNullable, isFalse);
+    assertUnsatisfied([edge]);
+  }
+
+  test_always_and_never_unaffected_by_soft_edges() {
+    var edge = connect(always, never);
+    propagate();
+    expect(always.isNullable, isTrue);
+    expect(never.isNullable, isFalse);
+    assertUnsatisfied([edge]);
+  }
+
+  test_always_destination() {
+    // always -> 1 -(hard)-> always
+    var n1 = newNode(1);
+    connect(always, n1);
+    connect(n1, always, hard: true);
+    propagate();
+    // Upstream propagation of non-nullability ignores edges terminating at
+    // `always`, so n1 should be nullable.
+    expect(n1.isNullable, true);
+    assertUnsatisfied([]);
+  }
+
+  test_edge_satisfied_due_to_guard() {
+    // always -> 1
+    // 1 -(2) -> 3
+    // 3 -(hard)-> never
+    var n1 = newNode(1);
+    var n2 = newNode(2);
+    var n3 = newNode(3);
+    connect(always, n1);
+    connect(n1, n3, guards: [n2]);
+    connect(n3, never, hard: true);
+    propagate();
+    expect(n1.isNullable, true);
+    expect(n2.isNullable, false);
+    expect(n3.isNullable, false);
+    // Although n1 is nullable and n3 is non-nullable, the edge from 1 to 3 is
+    // considered satisfied because the guard (n2) is non-nullable.
+    assertUnsatisfied([]);
+  }
+
+  test_never_source() {
+    // never -> 1
+    var n1 = newNode(1);
+    connect(never, n1);
+    propagate();
+    // Downstream propagation of nullability ignores edges originating at
+    // `never`, so n1 should be non-nullable.
+    expect(n1.isNullable, false);
+    assertUnsatisfied([]);
+  }
+
+  test_propagation_always_union() {
+    // always == 1
+    // 1 -(hard)-> never
+    // 1 -> 2
+    // 1 -> 3
+    // 3 -(hard)-> never
+    var n1 = newNode(1);
+    var n2 = newNode(2);
+    var n3 = newNode(3);
+    union(always, n1);
+    var edge_1_never = connect(n1, never, hard: true);
+    connect(n1, n2);
+    var edge_1_3 = connect(n1, n3);
+    connect(n3, never, hard: true);
+    propagate();
+    // Union edges take precedence over hard ones, so n1 should be nullable.
+    expect(n1.isNullable, true);
+    // And nullability should be propagated to n2.
+    expect(n2.isNullable, true);
+    // But it should not be propagated to n3 because non-nullability propagation
+    // takes precedence over ordinary nullability propagation.
+    expect(n3.isNullable, false);
+    assertUnsatisfied([edge_1_never, edge_1_3]);
+  }
+
+  test_propagation_always_union_reversed() {
+    // always == 1
+    // 1 -(hard)-> never
+    // 1 -> 2
+    // 1 -> 3
+    // 3 -(hard)-> never
+    var n1 = newNode(1);
+    var n2 = newNode(2);
+    var n3 = newNode(3);
+    union(n1, always);
+    var edge_1_never = connect(n1, never, hard: true);
+    connect(n1, n2);
+    var edge_1_3 = connect(n1, n3);
+    connect(n3, never, hard: true);
+    propagate();
+    // Union edges take precedence over hard ones, so n1 should be nullable.
+    expect(n1.isNullable, true);
+    // And nullability should be propagated to n2.
+    expect(n2.isNullable, true);
+    // But it should not be propagated to n3 because non-nullability propagation
+    // takes precedence over ordinary nullability propagation.
+    expect(n3.isNullable, false);
+    assertUnsatisfied([edge_1_never, edge_1_3]);
+  }
+
+  test_propagation_downstream_guarded_multiple_guards_all_satisfied() {
+    // always -> 1
+    // always -> 2
+    // always -> 3
+    // 1 -(2,3)-> 4
+    var n1 = newNode(1);
+    var n2 = newNode(2);
+    var n3 = newNode(3);
+    var n4 = newNode(3);
+    connect(always, n1);
+    connect(always, n2);
+    connect(always, n3);
+    connect(n1, n4, guards: [n2, n3]);
+    propagate();
+    expect(n4.isNullable, true);
+    assertUnsatisfied([]);
+  }
+
+  test_propagation_downstream_guarded_multiple_guards_not_all_satisfied() {
+    // always -> 1
+    // always -> 2
+    // 1 -(2,3)-> 4
+    var n1 = newNode(1);
+    var n2 = newNode(2);
+    var n3 = newNode(3);
+    var n4 = newNode(3);
+    connect(always, n1);
+    connect(always, n2);
+    connect(n1, n4, guards: [n2, n3]);
+    propagate();
+    expect(n4.isNullable, false);
+    assertUnsatisfied([]);
+  }
+
+  test_propagation_downstream_guarded_satisfy_guard_first() {
+    // always -> 1
+    // always -> 2
+    // 2 -(1)-> 3
+    var n1 = newNode(1);
+    var n2 = newNode(2);
+    var n3 = newNode(3);
+    connect(always, n1);
+    connect(always, n2);
+    connect(n2, n3, guards: [n1]);
+    propagate();
+    expect(n3.isNullable, true);
+    assertUnsatisfied([]);
+  }
+
+  test_propagation_downstream_guarded_satisfy_source_first() {
+    // always -> 1
+    // always -> 2
+    // 1 -(2)-> 3
+    var n1 = newNode(1);
+    var n2 = newNode(2);
+    var n3 = newNode(3);
+    connect(always, n1);
+    connect(always, n2);
+    connect(n1, n3, guards: [n2]);
+    propagate();
+    expect(n3.isNullable, true);
+    assertUnsatisfied([]);
+  }
+
+  test_propagation_downstream_guarded_unsatisfied_guard() {
+    // always -> 1
+    // 1 -(2)-> 3
+    var n1 = newNode(1);
+    var n2 = newNode(2);
+    var n3 = newNode(3);
+    connect(always, n1);
+    connect(n1, n3, guards: [n2]);
+    propagate();
+    expect(n3.isNullable, false);
+    assertUnsatisfied([]);
+  }
+
+  test_propagation_downstream_guarded_unsatisfied_source() {
+    // always -> 1
+    // 2 -(1)-> 3
+    var n1 = newNode(1);
+    var n2 = newNode(2);
+    var n3 = newNode(3);
+    connect(always, n1);
+    connect(n2, n3, guards: [n1]);
+    propagate();
+    expect(n3.isNullable, false);
+    assertUnsatisfied([]);
+  }
+
+  test_propagation_downstream_through_lub_both() {
+    // always -> 1
+    // always -> 2
+    // LUB(1, 2) -> 3
+    var n1 = newNode(1);
+    var n2 = newNode(2);
+    var n3 = newNode(3);
+    connect(always, n1);
+    connect(always, n2);
+    connect(lub(n1, n2), n3);
+    propagate();
+    expect(n3.isNullable, true);
+    assertUnsatisfied([]);
+  }
+
+  test_propagation_downstream_through_lub_cascaded() {
+    // always -> 1
+    // LUB(LUB(1, 2), 3) -> 4
+    var n1 = newNode(1);
+    var n2 = newNode(2);
+    var n3 = newNode(3);
+    var n4 = newNode(3);
+    connect(always, n1);
+    connect(lub(lub(n1, n2), n3), n4);
+    propagate();
+    expect(n4.isNullable, true);
+    assertUnsatisfied([]);
+  }
+
+  test_propagation_downstream_through_lub_left() {
+    // always -> 1
+    // LUB(1, 2) -> 3
+    var n1 = newNode(1);
+    var n2 = newNode(2);
+    var n3 = newNode(3);
+    connect(always, n1);
+    connect(lub(n1, n2), n3);
+    propagate();
+    expect(n3.isNullable, true);
+    assertUnsatisfied([]);
+  }
+
+  test_propagation_downstream_through_lub_neither() {
+    // LUB(1, 2) -> 3
+    var n1 = newNode(1);
+    var n2 = newNode(2);
+    var n3 = newNode(3);
+    connect(lub(n1, n2), n3);
+    propagate();
+    expect(n3.isNullable, false);
+    assertUnsatisfied([]);
+  }
+
+  test_propagation_downstream_through_lub_right() {
+    // always -> 2
+    // LUB(1, 2) -> 3
+    var n1 = newNode(1);
+    var n2 = newNode(2);
+    var n3 = newNode(3);
+    connect(always, n2);
+    connect(lub(n1, n2), n3);
+    propagate();
+    expect(n3.isNullable, true);
+    assertUnsatisfied([]);
+  }
+
+  test_propagation_downstream_through_substitution_both() {
+    // always -> 1
+    // always -> 2
+    // subst(1, 2) -> 3
+    var n1 = newNode(1);
+    var n2 = newNode(2);
+    var n3 = newNode(3);
+    connect(always, n1);
+    connect(always, n2);
+    connect(subst(n1, n2), n3);
+    propagate();
+    expect(n3.isNullable, true);
+    assertUnsatisfied([]);
+  }
+
+  test_propagation_downstream_through_substitution_cascaded() {
+    // always -> 1
+    // LUB(LUB(1, 2), 3) -> 4
+    var n1 = newNode(1);
+    var n2 = newNode(2);
+    var n3 = newNode(3);
+    var n4 = newNode(3);
+    connect(always, n1);
+    connect(subst(subst(n1, n2), n3), n4);
+    propagate();
+    expect(n4.isNullable, true);
+    assertUnsatisfied([]);
+  }
+
+  test_propagation_downstream_through_substitution_inner() {
+    // always -> 1
+    // LUB(1, 2) -> 3
+    var n1 = newNode(1);
+    var n2 = newNode(2);
+    var n3 = newNode(3);
+    connect(always, n1);
+    connect(subst(n1, n2), n3);
+    propagate();
+    expect(n3.isNullable, true);
+    assertUnsatisfied([]);
+  }
+
+  test_propagation_downstream_through_substitution_neither() {
+    // LUB(1, 2) -> 3
+    var n1 = newNode(1);
+    var n2 = newNode(2);
+    var n3 = newNode(3);
+    connect(subst(n1, n2), n3);
+    propagate();
+    expect(n3.isNullable, false);
+    assertUnsatisfied([]);
+  }
+
+  test_propagation_downstream_through_substitution_outer() {
+    // always -> 2
+    // LUB(1, 2) -> 3
+    var n1 = newNode(1);
+    var n2 = newNode(2);
+    var n3 = newNode(3);
+    connect(always, n2);
+    connect(subst(n1, n2), n3);
+    propagate();
+    expect(n3.isNullable, true);
+    assertUnsatisfied([]);
+  }
+
+  test_propagation_downstream_through_union() {
+    // always -> 1
+    // 1 == 2
+    // 2 -> 3
+    var n1 = newNode(1);
+    var n2 = newNode(2);
+    var n3 = newNode(3);
+    connect(always, n1);
+    union(n1, n2);
+    connect(n2, n3);
+    propagate();
+    expect(n1.isNullable, true);
+    expect(n2.isNullable, true);
+    expect(n3.isNullable, true);
+    assertUnsatisfied([]);
+  }
+
+  test_propagation_downstream_through_union_reversed() {
+    // always -> 1
+    // 2 == 1
+    // 2 -> 3
+    var n1 = newNode(1);
+    var n2 = newNode(2);
+    var n3 = newNode(3);
+    connect(always, n1);
+    union(n2, n1);
+    connect(n2, n3);
+    propagate();
+    expect(n1.isNullable, true);
+    expect(n2.isNullable, true);
+    expect(n3.isNullable, true);
+    assertUnsatisfied([]);
+  }
+
+  test_propagation_simple() {
+    // always -(soft)-> 1 -(soft)-> 2 -(hard) -> 3 -(hard)-> never
+    var n1 = newNode(1);
+    var n2 = newNode(2);
+    var n3 = newNode(3);
+    connect(always, n1);
+    var edge_1_2 = connect(n1, n2);
+    connect(n2, n3, hard: true);
+    connect(n3, never, hard: true);
+    propagate();
+    expect(n1.isNullable, true);
+    expect(n2.isNullable, false);
+    expect(n3.isNullable, false);
+    assertUnsatisfied([edge_1_2]);
+  }
+
+  test_propagation_upstream_through_union() {
+    // always -> 1
+    // always -> 2
+    // always -> 3
+    // 1 -(hard)-> 2
+    // 2 == 3
+    // 3 -(hard)-> never
+    var n1 = newNode(1);
+    var n2 = newNode(2);
+    var n3 = newNode(3);
+    var edge_always_1 = connect(always, n1);
+    var edge_always_2 = connect(always, n2);
+    var edge_always_3 = connect(always, n3);
+    connect(n1, n2, hard: true);
+    union(n2, n3);
+    connect(n3, never, hard: true);
+    propagate();
+    expect(n1.isNullable, false);
+    expect(n2.isNullable, false);
+    expect(n3.isNullable, false);
+    assertUnsatisfied([edge_always_1, edge_always_2, edge_always_3]);
+  }
+
+  test_propagation_upstream_through_union_reversed() {
+    // always -> 1
+    // always -> 2
+    // always -> 3
+    // 1 -(hard)-> 2
+    // 3 == 2
+    // 3 -(hard)-> never
+    var n1 = newNode(1);
+    var n2 = newNode(2);
+    var n3 = newNode(3);
+    var edge_always_1 = connect(always, n1);
+    var edge_always_2 = connect(always, n2);
+    var edge_always_3 = connect(always, n3);
+    connect(n1, n2, hard: true);
+    union(n3, n2);
+    connect(n3, never, hard: true);
+    propagate();
+    expect(n1.isNullable, false);
+    expect(n2.isNullable, false);
+    expect(n3.isNullable, false);
+    assertUnsatisfied([edge_always_1, edge_always_2, edge_always_3]);
+  }
+
+  test_satisfied_edge_destination_nullable() {
+    var n1 = newNode(1);
+    var edge = connect(always, n1);
+    propagate();
+    assertUnsatisfied([]);
+    expect(edge.isSatisfied, true);
+  }
+
+  test_satisfied_edge_source_non_nullable() {
+    var n1 = newNode(1);
+    var n2 = newNode(1);
+    var edge = connect(n1, n2);
+    propagate();
+    assertUnsatisfied([]);
+    expect(edge.isSatisfied, true);
+  }
+
+  test_satisfied_edge_two_sources_first_non_nullable() {
+    var n1 = newNode(1);
+    var n2 = newNode(1);
+    connect(always, n2);
+    var edge = connect(n1, never, guards: [n2]);
+    propagate();
+    assertUnsatisfied([]);
+    expect(edge.isSatisfied, true);
+  }
+
+  test_satisfied_edge_two_sources_second_non_nullable() {
+    var n1 = newNode(1);
+    var n2 = newNode(1);
+    connect(always, n1);
+    var edge = connect(n1, never, guards: [n2]);
+    propagate();
+    assertUnsatisfied([]);
+    expect(edge.isSatisfied, true);
+  }
+
+  test_unconstrainted_node_non_nullable() {
+    var n1 = newNode(1);
+    propagate();
+    expect(n1.isNullable, false);
+    assertUnsatisfied([]);
+  }
+
+  test_unsatisfied_edge_multiple_sources() {
+    var n1 = newNode(1);
+    connect(always, n1);
+    var edge = connect(always, never, guards: [n1]);
+    propagate();
+    assertUnsatisfied([edge]);
+    expect(edge.isSatisfied, false);
+  }
+
+  test_unsatisfied_edge_single_source() {
+    var edge = connect(always, never);
+    propagate();
+    assertUnsatisfied([edge]);
+    expect(edge.isSatisfied, false);
+  }
+
+  void union(NullabilityNode x, NullabilityNode y) {
+    graph.union(x, y, _TestEdgeOrigin());
+  }
+}
+
+class _TestEdgeOrigin extends EdgeOrigin {}
diff --git a/pkg/nnbd_migration/test/test_all.dart b/pkg/nnbd_migration/test/test_all.dart
index 8f19fb9..4241cf2 100644
--- a/pkg/nnbd_migration/test/test_all.dart
+++ b/pkg/nnbd_migration/test/test_all.dart
@@ -5,11 +5,15 @@
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
 import 'api_test.dart' as api_test;
-import 'migration_visitor_test.dart' as migration_visitor_test;
+import 'graph_builder_test.dart' as graph_builder_test;
+import 'node_builder_test.dart' as node_builder_test;
+import 'nullability_node_test.dart' as nullability_node_test;
 
 main() {
   defineReflectiveSuite(() {
-    migration_visitor_test.main();
     api_test.main();
+    graph_builder_test.main();
+    node_builder_test.main();
+    nullability_node_test.main();
   });
 }
diff --git a/pkg/pkg.status b/pkg/pkg.status
index 367542c..df75de5 100644
--- a/pkg/pkg.status
+++ b/pkg/pkg.status
@@ -84,6 +84,7 @@
 status_file/test/parse_and_normalize_test: SkipByDesign # Uses dart:io
 status_file/test/repo_status_files_test: SkipByDesign # Uses dart:io
 telemetry/test/*: SkipByDesign # Only meant to run on vm
+test_runner/test/*: SkipByDesign # Only meant to run on vm
 testing/*: SkipByDesign # Only meant to run on vm
 typed_data/test/typed_buffers_test/01: Fail # Not supporting Int64List, Uint64List.
 
diff --git a/pkg/smith/pubspec.yaml b/pkg/smith/pubspec.yaml
index 42a702f..0b80a7d 100644
--- a/pkg/smith/pubspec.yaml
+++ b/pkg/smith/pubspec.yaml
@@ -2,6 +2,10 @@
 author: Dart Team <misc@dartlang.org>
 description: Shared code for working with the Dart SDK's tests and test runner.
 homepage: http://www.dartlang.org
+# This package is not intended to be published.
+publish_to: none
+environment:
+  sdk: "^2.3.0"
 dev_dependencies:
   expect:
     path: ../expect
diff --git a/pkg/status_file/pubspec.yaml b/pkg/status_file/pubspec.yaml
index e3a5960..4eb3bcc 100644
--- a/pkg/status_file/pubspec.yaml
+++ b/pkg/status_file/pubspec.yaml
@@ -1,11 +1,11 @@
 name: status_file
 # This package is not intended to be published.
 publish_to: none
-
+environment:
+  sdk: "^2.3.0"
 dependencies:
   path: "^1.4.0"
   args: "^1.4.4"
-
 dev_dependencies:
   expect:
     path: ../expect
diff --git a/pkg/status_file/test/data/standalone_2_vm.status b/pkg/status_file/test/data/standalone_2_vm.status
index 01c5b89..8c521ab 100644
--- a/pkg/status_file/test/data/standalone_2_vm.status
+++ b/pkg/status_file/test/data/standalone_2_vm.status
@@ -35,7 +35,6 @@
 io/web_socket_test: Pass, RuntimeError # Issue 26814.
 
 [ $system == windows ]
-io/skipping_dart2js_compilations_test: Skip # Issue 19551.
 verbose_gc_to_bmu_test: Skip
 io/process_sync_test: Pass, Timeout # Issue 24596
 io/sleep_test: Pass, Fail # Issue 25757
diff --git a/pkg/test_runner/.gitignore b/pkg/test_runner/.gitignore
new file mode 100644
index 0000000..b26da7f
--- /dev/null
+++ b/pkg/test_runner/.gitignore
@@ -0,0 +1,3 @@
+# Don’t commit the following files and directories created by pub.
+.packages
+pubspec.lock
diff --git a/tools/testing/dart/analysis_options.yaml b/pkg/test_runner/analysis_options.yaml
similarity index 100%
rename from tools/testing/dart/analysis_options.yaml
rename to pkg/test_runner/analysis_options.yaml
diff --git a/pkg/test_runner/bin/http_server.dart b/pkg/test_runner/bin/http_server.dart
new file mode 100644
index 0000000..881dd35
--- /dev/null
+++ b/pkg/test_runner/bin/http_server.dart
@@ -0,0 +1,53 @@
+// Copyright (c) 2013, 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 'package:test_runner/src/configuration.dart';
+import 'package:test_runner/src/testing_servers.dart';
+import 'package:test_runner/src/utils.dart';
+import 'package:test_runner/src/vendored_pkg/args/args.dart';
+
+void main(List<String> arguments) {
+  var parser = ArgParser();
+  parser.addOption('port',
+      abbr: 'p',
+      help: 'The main server port we wish to respond to requests.',
+      defaultsTo: '0');
+  parser.addOption('crossOriginPort',
+      abbr: 'c',
+      help: 'A different port that accepts request from the main server port.',
+      defaultsTo: '0');
+  parser.addFlag('help',
+      abbr: 'h', negatable: false, help: 'Print this usage information.');
+  parser.addOption('build-directory', help: 'The build directory to use.');
+  parser.addOption('package-root', help: 'The package root to use.');
+  parser.addOption('packages', help: 'The package spec file to use.');
+  parser.addOption('network',
+      help: 'The network interface to use.', defaultsTo: '0.0.0.0');
+  parser.addFlag('csp',
+      help: 'Use Content Security Policy restrictions.', defaultsTo: false);
+  parser.addOption('runtime',
+      help: 'The runtime we are using (for csp flags).', defaultsTo: 'none');
+
+  var args = parser.parse(arguments);
+  if (args['help'] as bool) {
+    print(parser.getUsage());
+  } else {
+    var servers = TestingServers(
+        args['build-directory'] as String,
+        args['csp'] as bool,
+        Runtime.find(args['runtime'] as String),
+        null,
+        args['package-root'] as String,
+        args['packages'] as String);
+    var port = int.parse(args['port'] as String);
+    var crossOriginPort = int.parse(args['crossOriginPort'] as String);
+    servers
+        .startServers(args['network'] as String,
+            port: port, crossOriginPort: crossOriginPort)
+        .then((_) {
+      DebugLogger.info('Server listening on port ${servers.port}');
+      DebugLogger.info('Server listening on port ${servers.crossOriginPort}');
+    });
+  }
+}
diff --git a/tools/testing/dart/launch_browser.dart b/pkg/test_runner/bin/launch_browser.dart
similarity index 60%
rename from tools/testing/dart/launch_browser.dart
rename to pkg/test_runner/bin/launch_browser.dart
index b4f598e..1924521 100644
--- a/tools/testing/dart/launch_browser.dart
+++ b/pkg/test_runner/bin/launch_browser.dart
@@ -2,21 +2,19 @@
 // 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.
 
-/**
- * Simple command line interface to launching browsers.
- * Uses the browser_controller framework.
- * The usage is:
- *   DARTBIN launch_browser.dart BROWSER_NAME URL
- * DARTBIN should be the checked in stable binary.
- */
+/// Simple command line interface to launching browsers.
+/// Uses the browser_controller framework.
+/// The usage is:
+///   DARTBIN launch_browser.dart BROWSER_NAME URL
+/// DARTBIN should be the checked in stable binary.
 
-import 'browser_controller.dart';
-import 'configuration.dart';
+import 'package:test_runner/src/browser_controller.dart';
+import 'package:test_runner/src/configuration.dart';
 
 void printHelp() {
   print("Usage pattern:");
   print("launch_browser.dart browser url");
-  print("Supported browsers: ${Browser.SUPPORTED_BROWSERS}");
+  print("Supported browsers: ${Browser.supportedBrowsers}");
 }
 
 void main(List<String> arguments) {
@@ -34,10 +32,10 @@
   }
 
   var runtime = Runtime.find(name);
-  var configuration = new TestConfiguration(
-      configuration: new Configuration(
+  var configuration = TestConfiguration(
+      configuration: Configuration(
           "dummy configuration", null, null, null, runtime, null));
   var executable = configuration.browserLocation;
-  var browser = new Browser.byRuntime(runtime, executable);
+  var browser = Browser.byRuntime(runtime, executable);
   browser.start(arguments[1]);
 }
diff --git a/pkg/test_runner/bin/package_testing_support.dart b/pkg/test_runner/bin/package_testing_support.dart
new file mode 100644
index 0000000..0f01485
--- /dev/null
+++ b/pkg/test_runner/bin/package_testing_support.dart
@@ -0,0 +1,17 @@
+// 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.md file.
+
+import 'package:test_runner/src/configuration.dart';
+import 'package:test_runner/src/options.dart';
+import 'package:test_runner/src/repository.dart';
+import 'package:test_runner/src/test_configurations.dart';
+
+void main(List<String> arguments) {
+  Repository.uri = Uri.base;
+  var configurations = <TestConfiguration>[];
+  for (var argument in arguments) {
+    configurations.addAll(OptionsParser().parse(argument.split(" ")));
+  }
+  testConfigurations(configurations);
+}
diff --git a/tools/testing/dart/main.dart b/pkg/test_runner/bin/test_runner.dart
similarity index 90%
rename from tools/testing/dart/main.dart
rename to pkg/test_runner/bin/test_runner.dart
index 3e1b904..491e10b 100644
--- a/tools/testing/dart/main.dart
+++ b/pkg/test_runner/bin/test_runner.dart
@@ -21,13 +21,13 @@
 ///
 /// The default test directory layout is documented in "test_suite.dart", above
 /// `factory StandardTestSuite.forDirectory`.
-import "options.dart";
-import "test_configurations.dart";
+import "package:test_runner/src/options.dart";
+import "package:test_runner/src/test_configurations.dart";
 
 /// Runs all of the tests specified by the given command line [arguments].
 void main(List<String> arguments) {
   // Parse the command line arguments to a configuration.
-  var parser = new OptionsParser();
+  var parser = OptionsParser();
   var configurations = parser.parse(arguments);
   if (configurations == null || configurations.isEmpty) return;
 
diff --git a/tools/testing/dart/android.dart b/pkg/test_runner/lib/src/android.dart
similarity index 67%
rename from tools/testing/dart/android.dart
rename to pkg/test_runner/lib/src/android.dart
index 20e5fca..ab133aa 100644
--- a/tools/testing/dart/android.dart
+++ b/pkg/test_runner/lib/src/android.dart
@@ -1,9 +1,6 @@
 // Copyright (c) 2013, 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.
-
-library android;
-
 import "dart:async";
 import "dart:convert" show LineSplitter, utf8;
 import "dart:core";
@@ -30,18 +27,16 @@
           "stderr:\n  ${stderr.trim()}\n"
           "exitCode: $exitCode\n"
           "timedOut: $timedOut";
-      throw new Exception(error);
+      throw Exception(error);
     }
   }
 }
 
-/**
- * [_executeCommand] will write [stdin] to the standard input of the created
- * process and will return a tuple (stdout, stderr).
- *
- * If the exit code of the process was nonzero it will complete with an error.
- * If starting the process failed, it will complete with an error as well.
- */
+/// [_executeCommand] will write [stdin] to the standard input of the created
+/// process and will return a tuple (stdout, stderr).
+///
+/// If the exit code of the process was nonzero it will complete with an error.
+/// If starting the process failed, it will complete with an error as well.
 Future<AdbCommandResult> _executeCommand(String executable, List<String> args,
     {String stdin, Duration timeout}) {
   Future<String> getOutput(Stream<List<int>> stream) {
@@ -61,7 +56,7 @@
     Timer timer;
     bool timedOut = false;
     if (timeout != null) {
-      timer = new Timer(timeout, () {
+      timer = Timer(timeout, () {
         timedOut = true;
         process.kill(ProcessSignal.sigterm);
         timer = null;
@@ -76,40 +71,36 @@
     if (timer != null) timer.cancel();
 
     String command = "$executable ${args.join(' ')}";
-    return new AdbCommandResult(command, results[0] as String,
-        results[1] as String, results[2] as int, timedOut);
+    return AdbCommandResult(command, results[0] as String, results[1] as String,
+        results[2] as int, timedOut);
   });
 }
 
-/**
- * Helper class to loop through all adb ports.
- *
- * The ports come in pairs:
- *  - even number: console connection
- *  - odd number: adb connection
- * Note that this code doesn't check if the ports are used.
- */
+/// Helper class to loop through all adb ports.
+///
+/// The ports come in pairs:
+///  - even number: console connection
+///  - odd number: adb connection
+/// Note that this code doesn't check if the ports are used.
 class AdbServerPortPool {
-  static int MIN_PORT = 5554;
-  static int MAX_PORT = 5584;
+  static const _minPort = 5554;
+  static const _maxPort = 5584;
 
-  static int _nextPort = MIN_PORT;
+  static int _nextPort = _minPort;
 
   static int next() {
     var port = _nextPort;
-    if (port > MAX_PORT) {
-      throw new Exception("All ports are used.");
-    }
+    if (port > _maxPort) throw Exception("All ports are used.");
+
     _nextPort += 2;
     return port;
   }
 }
 
-/**
- * Represents the interface to the emulator.
- * New emulators can be launched by calling the static [launchNewEmulator]
- * method.
- */
+/// Represents the interface to the emulator.
+///
+/// New emulators can be launched by calling the static [launchNewEmulator]
+/// method.
 class AndroidEmulator {
   int _port;
   Process _emulatorProcess;
@@ -123,14 +114,14 @@
     var portNumber = AdbServerPortPool.next();
     var args = ['-avd', '$avdName', '-port', "$portNumber" /*, '-gpu', 'on'*/];
     return Process.start("emulator64-arm", args).then((Process process) {
-      var adbDevice = new AdbDevice('emulator-$portNumber');
-      return new AndroidEmulator._private(portNumber, adbDevice, process);
+      var adbDevice = AdbDevice('emulator-$portNumber');
+      return AndroidEmulator._private(portNumber, adbDevice, process);
     });
   }
 
   AndroidEmulator._private(this._port, this._adbDevice, this._emulatorProcess) {
     Stream<String> getLines(Stream s) {
-      return s.transform(utf8.decoder).transform(new LineSplitter());
+      return s.transform(utf8.decoder).transform(LineSplitter());
     }
 
     getLines(_emulatorProcess.stdout).listen((line) {
@@ -145,7 +136,7 @@
   }
 
   Future<bool> kill() {
-    var completer = new Completer<bool>();
+    var completer = Completer<bool>();
     if (_emulatorProcess.kill()) {
       _emulatorProcess.exitCode.then((exitCode) {
         // TODO: Should we use exitCode to do something clever?
@@ -163,9 +154,7 @@
   }
 }
 
-/**
- * Helper class to create avd device configurations.
- */
+/// Helper class to create avd device configurations.
 class AndroidHelper {
   static Future createAvd(String name, String target) async {
     var args = [
@@ -186,29 +175,23 @@
   }
 }
 
-/**
- * Used for communicating with an emulator or with a real device.
- */
+/// Used for communicating with an emulator or with a real device.
 class AdbDevice {
-  static const _adbServerStartupTime = const Duration(seconds: 3);
+  static const _adbServerStartupTime = Duration(seconds: 3);
   String _deviceId;
-  Map<String, String> _cachedData = new Map<String, String>();
+  Map<String, String> _cachedData = {};
 
   String get deviceId => _deviceId;
 
   AdbDevice(this._deviceId);
 
-  /**
-   * Blocks execution until the device is online
-   */
+  /// Blocks execution until the device is online.
   Future waitForDevice() {
     return _adbCommand(['wait-for-device']);
   }
 
-  /**
-   * Polls the 'sys.boot_completed' property. Returns as soon as the property is
-   * 1.
-   */
+  /// Polls the 'sys.boot_completed' property. Returns as soon as the property
+  /// is 1.
   Future<Null> waitForBootCompleted() async {
     while (true) {
       try {
@@ -216,71 +199,57 @@
             await _adbCommand(['shell', 'getprop', 'sys.boot_completed']);
         if (result.stdout.trim() == '1') return;
       } catch (_) {}
-      await new Future<Null>.delayed(const Duration(seconds: 2));
+      await Future<Null>.delayed(const Duration(seconds: 2));
     }
   }
 
-  /**
-   * Put adb in root mode.
-   */
+  /// Put adb in root mode.
   Future<bool> adbRoot() {
-    var adbRootCompleter = new Completer<bool>();
+    var adbRootCompleter = Completer<bool>();
     _adbCommand(['root']).then((_) {
       // TODO: Figure out a way to wait until the adb daemon was restarted in
       // 'root mode' on the device.
-      new Timer(_adbServerStartupTime, () => adbRootCompleter.complete(true));
+      Timer(_adbServerStartupTime, () => adbRootCompleter.complete(true));
     }).catchError((error) => adbRootCompleter.completeError(error));
     return adbRootCompleter.future;
   }
 
-  /**
-   * Download data form the device.
-   */
+  /// Download data from the device.
   Future pullData(Path remote, Path local) {
     return _adbCommand(['pull', '$remote', '$local']);
   }
 
-  /**
-   * Upload data to the device.
-   */
+  /// Upload data to the device.
   Future pushData(Path local, Path remote) {
     return _adbCommand(['push', '$local', '$remote']);
   }
 
-  /**
-   * Upload data to the device, unless [local] is the same as the most recently
-   * used source for [remote].
-   */
+  /// Upload data to the device, unless [local] is the same as the most recently
+  /// used source for [remote].
   Future<AdbCommandResult> pushCachedData(String local, String remote) {
     if (_cachedData[remote] == local) {
-      return new Future.value(
-          new AdbCommandResult("Skipped cached push", "", "", 0, false));
+      return Future.value(
+          AdbCommandResult("Skipped cached push", "", "", 0, false));
     }
     _cachedData[remote] = local;
     return _adbCommand(['push', local, remote]);
   }
 
-  /**
-   * Change permission of directory recursively.
-   */
+  /// Change permission of directory recursively.
   Future chmod(String mode, Path directory) {
     var arguments = ['shell', 'chmod', '-R', mode, '$directory'];
     return _adbCommand(arguments);
   }
 
-  /**
-   * Install an application on the device.
-   */
+  /// Install an application on the device.
   Future installApk(Path filename) {
     return _adbCommand(
         ['install', '-i', 'com.google.android.feedback', '-r', '$filename']);
   }
 
-  /**
-   * Start the given intent on the device.
-   */
+  /// Start the given intent on the device.
   Future startActivity(Intent intent) {
-    var arguments = [
+    return _adbCommand([
       'shell',
       'am',
       'start',
@@ -288,35 +257,24 @@
       '-a',
       intent.action,
       '-n',
-      "${intent.package}/${intent.activity}"
-    ];
-    if (intent.dataUri != null) {
-      arguments.addAll(['-d', intent.dataUri]);
-    }
-    return _adbCommand(arguments);
+      "${intent.package}/${intent.activity}",
+      if (intent.dataUri != null) ...['-d', intent.dataUri]
+    ]);
   }
 
-  /**
-   * Force to stop everything associated with [package].
-   */
+  /// Force to stop everything associated with [package].
   Future forceStop(String package) {
-    var arguments = ['shell', 'am', 'force-stop', package];
-    return _adbCommand(arguments);
+    return _adbCommand(['shell', 'am', 'force-stop', package]);
   }
 
-  /**
-   * Set system property name to value.
-   */
+  /// Set system property name to value.
   Future setProp(String name, String value) {
     return _adbCommand(['shell', 'setprop', name, value]);
   }
 
-  /**
-   * Kill all background processes.
-   */
+  /// Kill all background processes.
   Future killAll() {
-    var arguments = ['shell', 'am', 'kill-all'];
-    return _adbCommand(arguments);
+    return _adbCommand(['shell', 'am', 'kill-all']);
   }
 
   Future<AdbCommandResult> runAdbCommand(List<String> adbArgs,
@@ -327,28 +285,27 @@
 
   Future<AdbCommandResult> runAdbShellCommand(List<String> shellArgs,
       {Duration timeout}) async {
-    const MARKER = 'AdbShellExitCode: ';
+    const marker = 'AdbShellExitCode: ';
 
     // The exitcode of 'adb shell ...' can be 0 even though the command failed
     // with a non-zero exit code. We therefore explicitly print it to stdout and
     // search for it.
 
-    var args = ['shell', "${shellArgs.join(' ')} ; echo $MARKER \$?"];
-    AdbCommandResult result = await _executeCommand(
-        "adb", _deviceSpecificArgs(args),
+    var args = ['shell', "${shellArgs.join(' ')} ; echo $marker \$?"];
+    var result = await _executeCommand("adb", _deviceSpecificArgs(args),
         timeout: timeout);
-    int exitCode = result.exitCode;
+    var exitCode = result.exitCode;
     var lines = result.stdout
         .split('\n')
-        .where((line) => line.trim().length > 0)
+        .where((line) => line.trim().isNotEmpty)
         .toList();
-    if (lines.length > 0) {
-      int index = lines.last.indexOf(MARKER);
+    if (lines.isNotEmpty) {
+      int index = lines.last.indexOf(marker);
       if (index >= 0) {
         exitCode =
-            int.parse(lines.last.substring(index + MARKER.length).trim());
+            int.parse(lines.last.substring(index + marker.length).trim());
         if (exitCode > 128 && exitCode <= 128 + 31) {
-          // Return negative exit codes for signals 1..31 (128+N for signal N)
+          // Return negative exit codes for signals 1..31 (128+N for signal N).
           exitCode = 128 - exitCode;
         }
       } else {
@@ -369,7 +326,7 @@
         assert(result.exitCode != 0);
       }
     }
-    return new AdbCommandResult(result.command, result.stdout, result.stderr,
+    return AdbCommandResult(result.command, result.stdout, result.stderr,
         exitCode, result.timedOut);
   }
 
@@ -389,17 +346,15 @@
   }
 }
 
-/**
- * Helper to list all adb devices available.
- */
+/// Helper to list all adb devices available.
 class AdbHelper {
   static RegExp _deviceLineRegexp =
-      new RegExp(r'^([a-zA-Z0-9_-]+)[ \t]+device$', multiLine: true);
+      RegExp(r'^([a-zA-Z0-9_-]+)[ \t]+device$', multiLine: true);
 
   static Future<List<String>> listDevices() {
     return Process.run('adb', ['devices']).then((ProcessResult result) {
       if (result.exitCode != 0) {
-        throw new Exception("Could not list devices [stdout: ${result.stdout},"
+        throw Exception("Could not list devices [stdout: ${result.stdout},"
             "stderr: ${result.stderr}]");
       }
       return _deviceLineRegexp
@@ -410,9 +365,7 @@
   }
 }
 
-/**
- * Represents an android intent.
- */
+/// Represents an android intent.
 class Intent {
   String action;
   String package;
@@ -422,12 +375,10 @@
   Intent(this.action, this.package, this.activity, [this.dataUri]);
 }
 
-/**
- * Discovers all available devices and supports acquire/release.
- */
+/// Discovers all available devices and supports acquire/release.
 class AdbDevicePool {
-  final Queue<AdbDevice> _idleDevices = new Queue<AdbDevice>();
-  final Queue<Completer> _waiter = new Queue<Completer>();
+  final Queue<AdbDevice> _idleDevices = Queue();
+  final Queue<Completer<AdbDevice>> _waiter = Queue();
 
   AdbDevicePool(List<AdbDevice> idleDevices) {
     _idleDevices.addAll(idleDevices);
@@ -435,28 +386,28 @@
 
   static Future<AdbDevicePool> create() async {
     var names = await AdbHelper.listDevices();
-    var devices = names.map((id) => new AdbDevice(id)).toList();
+    var devices = names.map((id) => AdbDevice(id)).toList();
     if (devices.length == 0) {
-      throw new Exception('No android devices found. '
+      throw Exception('No android devices found. '
           'Please make sure "adb devices" shows your device!');
     }
     print("Found ${devices.length} Android devices.");
-    return new AdbDevicePool(devices);
+    return AdbDevicePool(devices);
   }
 
   Future<AdbDevice> acquireDevice() async {
-    if (_idleDevices.length > 0) {
+    if (_idleDevices.isNotEmpty) {
       return _idleDevices.removeFirst();
     } else {
-      var completer = new Completer<AdbDevice>();
+      var completer = Completer<AdbDevice>();
       _waiter.add(completer);
       return completer.future;
     }
   }
 
   void releaseDevice(AdbDevice device) {
-    if (_waiter.length > 0) {
-      Completer completer = _waiter.removeFirst();
+    if (_waiter.isNotEmpty) {
+      var completer = _waiter.removeFirst();
       completer.complete(device);
     } else {
       _idleDevices.add(device);
diff --git a/tools/testing/dart/babel_transform.js b/pkg/test_runner/lib/src/babel_transform.js
similarity index 100%
rename from tools/testing/dart/babel_transform.js
rename to pkg/test_runner/lib/src/babel_transform.js
diff --git a/tools/testing/dart/browser_test.dart b/pkg/test_runner/lib/src/browser.dart
similarity index 96%
rename from tools/testing/dart/browser_test.dart
rename to pkg/test_runner/lib/src/browser.dart
index b7f51ca..1a5747e 100644
--- a/tools/testing/dart/browser_test.dart
+++ b/pkg/test_runner/lib/src/browser.dart
@@ -23,7 +23,7 @@
 <body>
   <h1> Running $title </h1>
   <script type="text/javascript"
-          src="/root_dart/tools/testing/dart/test_controller.js">
+          src="/root_dart/pkg/test_runner/lib/src/test_controller.js">
   </script>
   <script type="text/javascript" src="$scriptPath"
           onerror="scriptTagOnErrorCallback(null)"
@@ -152,7 +152,7 @@
 <body>
 <h1>Running $testName</h1>
 <script type="text/javascript"
-        src="/root_dart/tools/testing/dart/test_controller.js">
+        src="/root_dart/pkg/test_runner/lib/src/test_controller.js">
 </script>
 <script>
 var require = {
@@ -174,15 +174,14 @@
 <script type="text/javascript">
 requirejs(["$testName", "dart_sdk", "async_helper"],
     function($testId, sdk, async_helper) {
-  sdk.dart.ignoreWhitelistedErrors(false);
   sdk._isolate_helper.startRootIsolate(function() {}, []);
   sdk._debugger.registerDevtoolsFormatter();
 
   testErrorToStackTrace = function(error) {
     var stackTrace = sdk.dart.stackTrace(error).toString();
-    
+
     var lines = stackTrace.split("\\n");
-    
+
     // Remove the first line, which is just "Error".
     lines = lines.slice(1);
 
@@ -193,7 +192,7 @@
         break;
       }
     }
-    
+
     // TODO(rnystrom): It would be nice to shorten the URLs of the remaining
     // lines too.
     return lines.join("\\n");
diff --git a/tools/testing/dart/browser_controller.dart b/pkg/test_runner/lib/src/browser_controller.dart
similarity index 84%
rename from tools/testing/dart/browser_controller.dart
rename to pkg/test_runner/lib/src/browser_controller.dart
index c29fe62..dd67eef 100644
--- a/tools/testing/dart/browser_controller.dart
+++ b/pkg/test_runner/lib/src/browser_controller.dart
@@ -14,59 +14,55 @@
 import 'reset_safari.dart';
 import 'utils.dart';
 
-typedef void BrowserDoneCallback(BrowserTestOutput output);
-typedef void TestChangedCallback(String browserId, String output, int testId);
-typedef BrowserTest NextTestCallback(String browserId);
+typedef BrowserDoneCallback = void Function(BrowserTestOutput output);
+typedef TestChangedCallback = void Function(
+    String browserId, String output, int testId);
+typedef NextTestCallback = BrowserTest Function(String browserId);
 
 class BrowserOutput {
-  final StringBuffer stdout = new StringBuffer();
-  final StringBuffer stderr = new StringBuffer();
-  final StringBuffer eventLog = new StringBuffer();
+  final StringBuffer stdout = StringBuffer();
+  final StringBuffer stderr = StringBuffer();
+  final StringBuffer eventLog = StringBuffer();
 }
 
-/** Class describing the interface for communicating with browsers. */
+/// Class describing the interface for communicating with browsers.
 abstract class Browser {
-  BrowserOutput _allBrowserOutput = new BrowserOutput();
-  BrowserOutput _testBrowserOutput = new BrowserOutput();
+  BrowserOutput _allBrowserOutput = BrowserOutput();
+  BrowserOutput _testBrowserOutput = BrowserOutput();
 
-  // This is called after the process is closed, before the done future
-  // is completed.
-  // Subclasses can use this to cleanup any browser specific resources
-  // (temp directories, profiles, etc). The function is expected to do
-  // it's work synchronously.
+  /// This is called after the process is closed, before the done future
+  /// is completed.
+  ///
+  /// Subclasses can use this to cleanup any browser specific resources
+  /// (temp directories, profiles, etc). The function is expected to do
+  /// it's work synchronously.
   Function _cleanup;
 
-  /** The version of the browser - normally set when starting a browser */
+  /// The version of the browser - normally set when starting a browser
   String version = "";
 
-  // The path to the browser executable.
+  /// The path to the browser executable.
   String _binary;
 
-  /**
-   * The underlying process - don't mess directly with this if you don't
-   * know what you are doing (this is an interactive process that needs
-   * special treatment to not leak).
-   */
+  /// The underlying process - don't mess directly with this if you don't
+  /// know what you are doing (this is an interactive process that needs
+  /// special treatment to not leak).
   Process process;
 
   Function logger;
 
-  /**
-   * Id of the browser
-   */
+  /// Id of the browser.
   String id;
 
-  /**
-   * Reset the browser to a known configuration on start-up.
-   * Browser specific implementations are free to ignore this.
-   */
+  /// Reset the browser to a known configuration on start-up.
+  /// Browser specific implementations are free to ignore this.
   static bool resetBrowserConfiguration = false;
 
-  /** Print everything (stdout, stderr, usageLog) whenever we add to it */
+  /// Print everything (stdout, stderr, usageLog) whenever we add to it
   bool debugPrint = false;
 
-  // This future returns when the process exits. It is also the return value
-  // of close()
+  /// This future returns when the process exits. It is also the return value
+  /// of close()
   Future done;
 
   Browser();
@@ -76,18 +72,18 @@
     Browser browser;
     switch (runtime) {
       case Runtime.firefox:
-        browser = new Firefox();
+        browser = Firefox();
         break;
       case Runtime.chrome:
-        browser = new Chrome();
+        browser = Chrome();
         break;
       case Runtime.safari:
-        browser = new Safari();
+        browser = Safari();
         break;
       case Runtime.ie9:
       case Runtime.ie10:
       case Runtime.ie11:
-        browser = new IE();
+        browser = IE();
         break;
       default:
         throw "unreachable";
@@ -97,7 +93,7 @@
     return browser;
   }
 
-  static const List<String> SUPPORTED_BROWSERS = const [
+  static const List<String> supportedBrowsers = [
     'safari',
     'ff',
     'firefox',
@@ -113,11 +109,11 @@
 
   // TODO(kustermann): add standard support for chrome on android
   static bool supportedBrowser(String name) {
-    return SUPPORTED_BROWSERS.contains(name);
+    return supportedBrowsers.contains(name);
   }
 
   void _logEvent(String event) {
-    String toLog = "$this ($id) - $event \n";
+    var toLog = "$this ($id) - $event \n";
     if (debugPrint) print("usageLog: $toLog");
     if (logger != null) logger(toLog);
 
@@ -150,14 +146,12 @@
       return done;
     } else {
       _logEvent("The process is already dead.");
-      return new Future.value(true);
+      return Future.value(true);
     }
   }
 
-  /**
-   * Start the browser using the supplied argument.
-   * This sets up the error handling and usage logging.
-   */
+  /// Start the browser using the supplied argument.
+  /// This sets up the error handling and usage logging.
   Future<bool> startBrowserProcess(String command, List<String> arguments,
       {Map<String, String> environment}) {
     return Process.start(command, arguments, environment: environment)
@@ -166,14 +160,14 @@
       process = startedProcess;
       // Used to notify when exiting, and as a return value on calls to
       // close().
-      var doneCompleter = new Completer<bool>();
+      var doneCompleter = Completer<bool>();
       done = doneCompleter.future;
 
-      Completer stdoutDone = new Completer<Null>();
-      Completer stderrDone = new Completer<Null>();
+      var stdoutDone = Completer<Null>();
+      var stderrDone = Completer<Null>();
 
-      bool stdoutIsDone = false;
-      bool stderrIsDone = false;
+      var stdoutIsDone = false;
+      var stderrIsDone = false;
       StreamSubscription stdoutSubscription;
       StreamSubscription stderrSubscription;
 
@@ -226,9 +220,8 @@
         _logEvent("Browser closed with exitcode $exitCode");
 
         if (!stdoutIsDone || !stderrIsDone) {
-          watchdogTimer = new Timer(MAX_STDIO_DELAY, () {
-            DebugLogger.warning(
-                "$MAX_STDIO_DELAY_PASSED_MESSAGE (browser: $this)");
+          watchdogTimer = Timer(maxStdioDelay, () {
+            DebugLogger.warning("$maxStdioDelayPassedMessage (browser: $this)");
             watchdogTimer = null;
             stdoutSubscription.cancel();
             stderrSubscription.cancel();
@@ -253,26 +246,20 @@
     });
   }
 
-  /**
-   * Get the output that was written so far to stdout/stderr/eventLog.
-   */
+  /// Get the output that was written so far to stdout/stderr/eventLog.
   BrowserOutput get allBrowserOutput => _allBrowserOutput;
   BrowserOutput get testBrowserOutput => _testBrowserOutput;
 
   void resetTestBrowserOutput() {
-    _testBrowserOutput = new BrowserOutput();
+    _testBrowserOutput = BrowserOutput();
   }
 
-  /**
-   * Add useful info about the browser to the _testBrowserOutput.stdout,
-   * where it will be reported for failing tests.  Used to report which
-   * android device a failing test is running on.
-   */
+  /// Add useful info about the browser to the _testBrowserOutput.stdout,
+  /// where it will be reported for failing tests.  Used to report which
+  /// android device a failing test is running on.
   void logBrowserInfoToTestBrowserOutput() {}
 
-  String toString();
-
-  /** Starts the browser loading the given url */
+  /// Starts the browser loading the given url
   Future<bool> start(String url);
 
   /// Called when the driver page is requested, that is, when the browser first
@@ -280,13 +267,11 @@
   /// browser process has started and opened its first window.
   ///
   /// This is used by [Safari] to ensure the browser window has focus.
-  Future<Null> onDriverPageRequested() => new Future<Null>.value();
+  Future<Null> onDriverPageRequested() => Future.value();
 }
 
 class Safari extends Browser {
-  /**
-   * We get the safari version by parsing a version file
-   */
+  /// We get the safari version by parsing a version file
   static const String versionFile =
       "/Applications/Safari.app/Contents/version.plist";
 
@@ -297,17 +282,17 @@
   Future<bool> resetConfiguration() async {
     if (!Browser.resetBrowserConfiguration) return true;
 
-    var completer = new Completer<Null>();
+    var completer = Completer<Null>();
     handleUncaughtError(error, StackTrace stackTrace) {
       if (!completer.isCompleted) {
         completer.completeError(error, stackTrace);
       } else {
-        throw new AsyncError(error, stackTrace);
+        throw AsyncError(error, stackTrace);
       }
     }
 
-    Zone parent = Zone.current;
-    ZoneSpecification specification = new ZoneSpecification(
+    var parent = Zone.current;
+    var specification = ZoneSpecification(
         print: (Zone self, ZoneDelegate delegate, Zone zone, String line) {
       delegate.run(parent, () {
         _logEvent(line);
@@ -315,7 +300,7 @@
     });
     Future zoneWrapper() {
       Uri safariUri = Uri.base.resolve(safariBundleLocation);
-      return new Future(() => killAndResetSafari(bundle: safariUri))
+      return Future(() => killAndResetSafari(bundle: safariUri))
           .then(completer.complete);
     }
 
@@ -335,28 +320,25 @@
   }
 
   Future<String> getVersion() {
-    /**
-     * Example of the file:
-     * <?xml version="1.0" encoding="UTF-8"?>
-     * <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-     * <plist version="1.0">
-     * <dict>
-     *	     <key>BuildVersion</key>
-     * 	     <string>2</string>
-     * 	     <key>CFBundleShortVersionString</key>
-     * 	     <string>6.0.4</string>
-     * 	     <key>CFBundleVersion</key>
-     * 	     <string>8536.29.13</string>
-     * 	     <key>ProjectName</key>
-     * 	     <string>WebBrowser</string>
-     * 	     <key>SourceVersion</key>
-     * 	     <string>7536029013000000</string>
-     * </dict>
-     * </plist>
-     */
-    File f = new File(versionFile);
-    return f.readAsLines().then((content) {
-      bool versionOnNextLine = false;
+    // Example of the file:
+    // <?xml version="1.0" encoding="UTF-8"?>
+    // <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+    // <plist version="1.0">
+    // <dict>
+    // 	     <key>BuildVersion</key>
+    // 	     <string>2</string>
+    // 	     <key>CFBundleShortVersionString</key>
+    // 	     <string>6.0.4</string>
+    // 	     <key>CFBundleVersion</key>
+    // 	     <string>8536.29.13</string>
+    // 	     <key>ProjectName</key>
+    // 	     <string>WebBrowser</string>
+    // 	     <key>SourceVersion</key>
+    // 	     <string>7536029013000000</string>
+    // </dict>
+    // </plist>
+    return File(versionFile).readAsLines().then((content) {
+      var versionOnNextLine = false;
       for (var line in content) {
         if (versionOnNextLine) return line;
         if (line.contains("CFBundleShortVersionString")) {
@@ -368,7 +350,7 @@
   }
 
   Future<Null> _createLaunchHTML(String path, String url) async {
-    var file = new File("$path/launch.html");
+    var file = File("$path/launch.html");
     var randomFile = await file.open(mode: FileMode.write);
     var content = '<script language="JavaScript">location = "$url"</script>';
     await randomFile.writeString(content);
@@ -444,7 +426,7 @@
       _version = "Can't get version on windows";
       // We still validate that the binary exists so that we can give good
       // feedback.
-      return new File(_binary).exists().then((exists) {
+      return File(_binary).exists().then((exists) {
         if (!exists) {
           _logEvent("Chrome binary not available.");
           _logEvent("Make sure $_binary is a valid program for running chrome");
@@ -478,7 +460,7 @@
             _logEvent(
                 "Error: failed to delete Chrome user-data-dir ${userDir.path}"
                 ", will try again in 40 seconds: $e");
-            new Timer(new Duration(seconds: 40), () {
+            Timer(Duration(seconds: 40), () {
               try {
                 userDir.deleteSync(recursive: true);
               } catch (e) {
@@ -556,7 +538,7 @@
     }
 
     var localAppData = Platform.environment['LOCALAPPDATA'];
-    Directory dir = new Directory("$localAppData\\Microsoft\\"
+    Directory dir = Directory("$localAppData\\Microsoft\\"
         "Internet Explorer\\Recovery");
     return dir.delete(recursive: true).then((_) {
       return true;
@@ -594,22 +576,20 @@
   AndroidChrome(this._adbDevice);
 
   Future<bool> start(String url) {
-    var chromeIntent =
-        new Intent(viewAction, chromePackage, chromeActivity, url);
+    var chromeIntent = Intent(viewAction, chromePackage, chromeActivity, url);
     var turnScreenOnIntent =
-        new Intent(mainAction, turnScreenOnPackage, turnScreenOnActivity);
+        Intent(mainAction, turnScreenOnPackage, turnScreenOnActivity);
 
-    var testing_resources_dir =
-        new Path('third_party/android_testing_resources');
-    if (!new Directory(testing_resources_dir.toNativePath()).existsSync()) {
-      DebugLogger.error("$testing_resources_dir doesn't exist. Exiting now.");
+    var testingResourcesDir = Path('third_party/android_testing_resources');
+    if (!Directory(testingResourcesDir.toNativePath()).existsSync()) {
+      DebugLogger.error("$testingResourcesDir doesn't exist. Exiting now.");
       exit(1);
     }
 
-    var chromeAPK = testing_resources_dir.append('com.android.chrome-1.apk');
-    var turnScreenOnAPK = testing_resources_dir.append('TurnScreenOn.apk');
-    var chromeConfDir = testing_resources_dir.append('chrome_configuration');
-    var chromeConfDirRemote = new Path('/data/user/0/com.android.chrome/');
+    var chromeAPK = testingResourcesDir.append('com.android.chrome-1.apk');
+    var turnScreenOnAPK = testingResourcesDir.append('TurnScreenOn.apk');
+    var chromeConfDir = testingResourcesDir.append('chrome_configuration');
+    var chromeConfDirRemote = Path('/data/user/0/com.android.chrome/');
 
     return _adbDevice.waitForBootCompleted().then((_) {
       return _adbDevice.forceStop(chromeIntent.package);
@@ -638,7 +618,7 @@
         return _adbDevice.killAll().then((_) => true);
       });
     }
-    return new Future.value(true);
+    return Future.value(true);
   }
 
   void logBrowserInfoToTestBrowserOutput() {
@@ -658,7 +638,7 @@
       'user_pref("dom.max_script_run_time", 0);';
 
   void _createPreferenceFile(String path) {
-    var file = new File("$path/user.js");
+    var file = File("$path/user.js");
     var randomFile = file.openSync(mode: FileMode.write);
     randomFile.writeStringSync(enablePopUp);
     randomFile.writeStringSync(disableDefaultCheck);
@@ -673,7 +653,7 @@
       if (versionResult.exitCode != 0) {
         _logEvent("Failed to firefox get version");
         _logEvent("Make sure $_binary is a valid program for running firefox");
-        return new Future.value(false);
+        return Future.value(false);
       }
       version = versionResult.stdout as String;
       _logEvent("Got version: $version");
@@ -690,7 +670,7 @@
           "-new-instance",
           url
         ];
-        var environment = new Map<String, String>.from(Platform.environment);
+        var environment = Map<String, String>.from(Platform.environment);
         environment["MOZ_CRASHREPORTER_DISABLE"] = "1";
         return startBrowserProcess(_binary, args, environment: environment);
       });
@@ -703,9 +683,7 @@
   String toString() => "Firefox";
 }
 
-/**
- * Describes the current state of a browser used for testing.
- */
+/// Describes the current state of a browser used for testing.
 class BrowserStatus {
   Browser browser;
   BrowserTest currentTest;
@@ -716,14 +694,12 @@
   BrowserTest lastTest;
   bool timeout = false;
   Timer nextTestTimeout;
-  Stopwatch timeSinceRestart = new Stopwatch()..start();
+  Stopwatch timeSinceRestart = Stopwatch()..start();
 
   BrowserStatus(Browser this.browser);
 }
 
-/**
- * Describes a single test to be run in the browser.
- */
+/// Describes a single test to be run in the browser.
 class BrowserTest {
   // TODO(ricow): Add timeout callback instead of the string passing hack.
   BrowserDoneCallback doneCallback;
@@ -763,7 +739,7 @@
 
   BrowserTestOutput(this.delayUntilTestStarted, this.duration,
       this.lastKnownMessage, this.browserOutput,
-      {this.didTimeout: false});
+      {this.didTimeout = false});
 }
 
 /// Encapsulates all the functionality for running tests in browsers.
@@ -776,12 +752,12 @@
 /// driver page to the browsers, serves tests, and receives results and
 /// requests back from the browsers.
 class BrowserTestRunner {
-  static const int MAX_NEXT_TEST_TIMEOUTS = 10;
-  static const Duration NEXT_TEST_TIMEOUT = const Duration(seconds: 120);
-  static const Duration RESTART_BROWSER_INTERVAL = const Duration(seconds: 60);
+  static const int _maxNextTestTimeouts = 10;
+  static const Duration _nextTestTimeout = Duration(seconds: 120);
+  static const Duration _restartBrowserInterval = Duration(seconds: 60);
 
   /// If the queue was recently empty, don't start another browser.
-  static const Duration MIN_NONEMPTY_QUEUE_TIME = const Duration(seconds: 1);
+  static const Duration _minNonemptyQueueTime = Duration(seconds: 1);
 
   final TestConfiguration configuration;
   final BrowserTestingServer testingServer;
@@ -789,7 +765,8 @@
   final String localIp;
   int maxNumBrowsers;
   int numBrowsers = 0;
-  // Used to send back logs from the browser (start, stop etc)
+
+  /// Used to send back logs from the browser (start, stop etc.).
   Function logger;
 
   int browserIdCounter = 1;
@@ -797,7 +774,7 @@
   bool testingServerStarted = false;
   bool underTermination = false;
   int numBrowserGetTestTimeouts = 0;
-  DateTime lastEmptyTestQueueTime = new DateTime.now();
+  DateTime lastEmptyTestQueueTime = DateTime.now();
   String _currentStartingBrowserId;
   List<BrowserTest> testQueue = [];
   Map<String, BrowserStatus> browserStatus = {};
@@ -805,26 +782,26 @@
   Map<String, AdbDevice> adbDeviceMapping = {};
   List<AdbDevice> idleAdbDevices;
 
-  // This cache is used to guarantee that we never see double reporting.
-  // If we do we need to provide developers with this information.
-  // We don't add urls to the cache until we have run it.
+  /// This cache is used to guarantee that we never see double reporting.
+  /// If we do we need to provide developers with this information.
+  /// We don't add urls to the cache until we have run it.
   Map<int, String> testCache = {};
 
   Map<int, String> doubleReportingOutputs = {};
   List<String> timedOut = [];
 
-  // We will start a new browser when the test queue hasn't been empty
-  // recently, we have fewer than maxNumBrowsers browsers, and there is
-  // no other browser instance currently starting up.
+  /// We will start a new browser when the test queue hasn't been empty
+  /// recently, we have fewer than maxNumBrowsers browsers, and there is
+  /// no other browser instance currently starting up.
   bool get queueWasEmptyRecently {
     return testQueue.isEmpty ||
-        new DateTime.now().difference(lastEmptyTestQueueTime) <
-            MIN_NONEMPTY_QUEUE_TIME;
+        DateTime.now().difference(lastEmptyTestQueueTime) <
+            _minNonemptyQueueTime;
   }
 
-  // While a browser is starting, but has not requested its first test, its
-  // browserId is stored in _currentStartingBrowserId.
-  // When no browser is currently starting, _currentStartingBrowserId is null.
+  /// While a browser is starting, but has not requested its first test, its
+  /// browserId is stored in _currentStartingBrowserId.
+  /// When no browser is currently starting, _currentStartingBrowserId is null.
   bool get aBrowserIsCurrentlyStarting => _currentStartingBrowserId != null;
   void markCurrentlyStarting(String id) {
     _currentStartingBrowserId = id;
@@ -838,7 +815,7 @@
       TestConfiguration configuration, String localIp, this.maxNumBrowsers)
       : configuration = configuration,
         localIp = localIp,
-        testingServer = new BrowserTestingServer(configuration, localIp,
+        testingServer = BrowserTestingServer(configuration, localIp,
             Browser.requiresFocus(configuration.runtime.name)) {
     testingServer.testRunner = this;
   }
@@ -852,7 +829,7 @@
       ..nextTestCallBack = getNextTest;
     if (configuration.runtime == Runtime.chromeOnAndroid) {
       var idbNames = await AdbHelper.listDevices();
-      idleAdbDevices = new List.from(idbNames.map((id) => new AdbDevice(id)));
+      idleAdbDevices = List.from(idbNames.map((id) => AdbDevice(id)));
       maxNumBrowsers = min(maxNumBrowsers, idleAdbDevices.length);
     }
     testingServerStarted = true;
@@ -861,6 +838,7 @@
 
   /// requestBrowser() is called whenever we might want to start an additional
   /// browser instance.
+  ///
   /// It is called when starting the BrowserTestRunner, and whenever a browser
   /// is killed, whenever a new test is enqueued, or whenever a browser
   /// finishes a test.
@@ -885,17 +863,17 @@
     if (configuration.runtime == Runtime.chromeOnAndroid) {
       AdbDevice device = idleAdbDevices.removeLast();
       adbDeviceMapping[id] = device;
-      browser = new AndroidChrome(device);
+      browser = AndroidChrome(device);
     } else {
       var path = configuration.browserLocation;
-      browser = new Browser.byRuntime(
+      browser = Browser.byRuntime(
           configuration.runtime, path, configuration.isChecked);
       browser.logger = logger;
     }
 
     browser.id = id;
     markCurrentlyStarting(id);
-    var status = new BrowserStatus(browser);
+    var status = BrowserStatus(browser);
     browserStatus[id] = status;
     numBrowsers++;
     status.nextTestTimeout = createNextTestTimer(status);
@@ -928,7 +906,7 @@
       testCache[testId] = status.currentTest.url;
 
       // Report that the test is finished now
-      var browserTestOutput = new BrowserTestOutput(
+      var browserTestOutput = BrowserTestOutput(
           status.currentTest.delayUntilTestStarted,
           status.currentTest.stopwatch.elapsed,
           output,
@@ -987,7 +965,7 @@
     await status.browser.close();
     var lastKnownMessage =
         'Dom could not be fetched, since the test timed out.';
-    if (status.currentTest.lastKnownMessage.length > 0) {
+    if (status.currentTest.lastKnownMessage.isNotEmpty) {
       lastKnownMessage = status.currentTest.lastKnownMessage;
     }
     if (status.lastTest != null) {
@@ -996,7 +974,7 @@
     // Wait until the browser is closed before reporting the test as timeout.
     // This will enable us to capture stdout/stderr from the browser
     // (which might provide us with information about what went wrong).
-    var browserTestOutput = new BrowserTestOutput(
+    var browserTestOutput = BrowserTestOutput(
         status.currentTest.delayUntilTestStarted,
         status.currentTest.stopwatch.elapsed,
         lastKnownMessage,
@@ -1041,7 +1019,7 @@
     // had flaky timeouts, and this may help.
     if ((configuration.runtime == Runtime.ie10 ||
             configuration.runtime == Runtime.ie11) &&
-        status.timeSinceRestart.elapsed > RESTART_BROWSER_INTERVAL) {
+        status.timeSinceRestart.elapsed > _restartBrowserInterval) {
       var id = status.browser.id;
       // Reset stopwatch so we don't trigger again before restarting.
       status.timeout = true;
@@ -1058,7 +1036,7 @@
     BrowserTest test = testQueue.removeLast();
     // If our queue isn't empty, try starting more browsers
     if (testQueue.isEmpty) {
-      lastEmptyTestQueueTime = new DateTime.now();
+      lastEmptyTestQueueTime = DateTime.now();
     } else {
       requestBrowser();
     }
@@ -1079,7 +1057,7 @@
     }
 
     status.currentTest.timeoutTimer = createTimeoutTimer(test, status);
-    status.currentTest.stopwatch = new Stopwatch()..start();
+    status.currentTest.stopwatch = Stopwatch()..start();
 
     // Reset the test specific output information (stdout, stderr) on the
     // browser, since a new test is being started.
@@ -1090,7 +1068,7 @@
 
   /// Creates a timer that is active while a test is running on a browser.
   Timer createTimeoutTimer(BrowserTest test, BrowserStatus status) {
-    return new Timer(new Duration(seconds: test.timeout), () {
+    return Timer(Duration(seconds: test.timeout), () {
       handleTimeout(status);
     });
   }
@@ -1098,7 +1076,7 @@
   /// Creates a timer that is active while no test is running on the
   /// browser. It has finished one test, and it has not requested a new test.
   Timer createNextTestTimer(BrowserStatus status) {
-    return new Timer(BrowserTestRunner.NEXT_TEST_TIMEOUT, () {
+    return Timer(BrowserTestRunner._nextTestTimeout, () {
       handleNextTestTimeout(status);
     });
   }
@@ -1108,7 +1086,7 @@
         "Browser timed out before getting next test. Restarting");
     if (status.timeout) return;
     numBrowserGetTestTimeouts++;
-    if (numBrowserGetTestTimeouts >= MAX_NEXT_TEST_TIMEOUTS) {
+    if (numBrowserGetTestTimeouts >= _maxNextTestTimeouts) {
       DebugLogger.error(
           "Too many browser timeouts before getting next test. Terminating");
       terminate().then((_) => exit(1));
@@ -1169,21 +1147,20 @@
   }
 }
 
+/// Interface of the testing server:
+///
+/// GET /driver/BROWSER_ID -- This will get the driver page to fetch
+///                           and run tests ...
+/// GET /next_test/BROWSER_ID -- returns "WAIT" "TERMINATE" or "url#id"
+/// where url is the test to run, and id is the id of the test.
+/// If there are currently no available tests the waitSignal is send
+/// back. If we are in the process of terminating the terminateSignal
+/// is send back and the browser will stop requesting new tasks.
+/// POST /report/BROWSER_ID?id=NUM -- sends back the dom of the executed
+///                                   test
 class BrowserTestingServer {
   final TestConfiguration configuration;
 
-  /// Interface of the testing server:
-  ///
-  /// GET /driver/BROWSER_ID -- This will get the driver page to fetch
-  ///                           and run tests ...
-  /// GET /next_test/BROWSER_ID -- returns "WAIT" "TERMINATE" or "url#id"
-  /// where url is the test to run, and id is the id of the test.
-  /// If there are currently no available tests the waitSignal is send
-  /// back. If we are in the process of terminating the terminateSignal
-  /// is send back and the browser will stop requesting new tasks.
-  /// POST /report/BROWSER_ID?id=NUM -- sends back the dom of the executed
-  ///                                   test
-
   final String localIp;
   final bool requiresFocus;
   BrowserTestRunner testRunner;
@@ -1216,11 +1193,11 @@
   void setupErrorServer(HttpServer server) {
     errorReportingServer = server;
     void errorReportingHandler(HttpRequest request) {
-      StringBuffer buffer = new StringBuffer();
+      var buffer = StringBuffer();
       request.transform(utf8.decoder).listen((data) {
         buffer.write(data);
       }, onDone: () {
-        String back = buffer.toString();
+        var back = buffer.toString();
         request.response.headers.set("Access-Control-Allow-Origin", "*");
         request.response.done.catchError((error) {
           DebugLogger.error("Error getting error from browser"
@@ -1278,15 +1255,15 @@
         textResponse = getDriverPage(browserId(request, driverPath));
         request.response.headers.set('Content-Type', 'text/html');
       } else if (request.uri.path.startsWith(nextTestPath)) {
-        textResponse = new Future<String>.value(
-            getNextTest(browserId(request, nextTestPath)));
+        textResponse =
+            Future.value(getNextTest(browserId(request, nextTestPath)));
         request.response.headers.set('Content-Type', 'text/plain');
       } else {
-        textResponse = new Future<String>.value("");
+        textResponse = Future.value("");
       }
       request.response.done.catchError((error) async {
         if (!underTermination) {
-          String text = await textResponse;
+          var text = await textResponse;
           print("URI ${request.uri}");
           print("text $text");
           throw "Error returning content to browser: $error";
@@ -1307,11 +1284,11 @@
 
   void handleReport(HttpRequest request, String browserId, int testId,
       {bool isStatusUpdate}) {
-    StringBuffer buffer = new StringBuffer();
+    var buffer = StringBuffer();
     request.transform(utf8.decoder).listen((data) {
       buffer.write(data);
     }, onDone: () {
-      String back = buffer.toString();
+      var back = buffer.toString();
       request.response.close();
       if (isStatusUpdate) {
         testStatusUpdateCallBack(browserId, back, testId);
@@ -1325,14 +1302,14 @@
   }
 
   void handleStarted(HttpRequest request, String browserId, int testId) {
-    StringBuffer buffer = new StringBuffer();
+    var buffer = StringBuffer();
     // If an error occurs while receiving the data from the request stream,
     // we don't handle it specially. We can safely ignore it, since the started
     // events are not crucial.
     request.transform(utf8.decoder).listen((data) {
       buffer.write(data);
     }, onDone: () {
-      String back = buffer.toString();
+      var back = buffer.toString();
       request.response.close();
       testStartedCallBack(browserId, back, testId);
     }, onError: (error) {
@@ -1365,7 +1342,7 @@
     await testRunner.browserStatus[browserId].browser.onDriverPageRequested();
     var errorReportingUrl =
         "http://$localIp:${errorReportingServer.port}/$browserId";
-    String driverContent = """
+    var driverContent = """
 <!DOCTYPE html><html>
 <head>
   <title>Driving page</title>
diff --git a/tools/testing/dart/co19_test_config.dart b/pkg/test_runner/lib/src/co19_test_config.dart
similarity index 85%
rename from tools/testing/dart/co19_test_config.dart
rename to pkg/test_runner/lib/src/co19_test_config.dart
index e736dec..f2c1fc8 100644
--- a/tools/testing/dart/co19_test_config.dart
+++ b/pkg/test_runner/lib/src/co19_test_config.dart
@@ -7,10 +7,10 @@
 import 'test_suite.dart';
 
 class Co19TestSuite extends StandardTestSuite {
-  RegExp _testRegExp = new RegExp(r"t[0-9]{2}.dart$");
+  static final _testRegExp = RegExp(r"t[0-9]{2}.dart$");
 
   Co19TestSuite(TestConfiguration configuration, String selector)
-      : super(configuration, selector, new Path("tests/$selector/src"), [
+      : super(configuration, selector, Path("tests/$selector/src"), [
           "tests/$selector/$selector-co19.status",
           "tests/$selector/$selector-analyzer.status",
           "tests/$selector/$selector-runtime.status",
diff --git a/tools/testing/dart/command.dart b/pkg/test_runner/lib/src/command.dart
similarity index 91%
rename from tools/testing/dart/command.dart
rename to pkg/test_runner/lib/src/command.dart
index 2eb7648..e6ea871 100644
--- a/tools/testing/dart/command.dart
+++ b/pkg/test_runner/lib/src/command.dart
@@ -18,7 +18,7 @@
 class Command {
   static Command browserTest(String url, TestConfiguration configuration,
       {bool retry}) {
-    return new BrowserTestCommand._(url, configuration, retry);
+    return BrowserTestCommand._(url, configuration, retry);
   }
 
   static Command compilation(
@@ -28,9 +28,9 @@
       String executable,
       List<String> arguments,
       Map<String, String> environment,
-      {bool alwaysCompile: false,
+      {bool alwaysCompile = false,
       String workingDirectory}) {
-    return new CompilationCommand._(displayName, outputFile, alwaysCompile,
+    return CompilationCommand._(displayName, outputFile, alwaysCompile,
         bootstrapDependencies, executable, arguments, environment,
         workingDirectory: workingDirectory);
   }
@@ -43,36 +43,35 @@
       List<String> arguments,
       Map<String, String> environment,
       List<String> batchArgs) {
-    return new VMKernelCompilationCommand._(outputFile, neverSkipCompilation,
+    return VMKernelCompilationCommand._(outputFile, neverSkipCompilation,
         bootstrapDependencies, executable, arguments, environment, batchArgs);
   }
 
   static Command analysis(String executable, List<String> arguments,
       Map<String, String> environmentOverrides) {
-    return new AnalysisCommand._(executable, arguments, environmentOverrides);
+    return AnalysisCommand._(executable, arguments, environmentOverrides);
   }
 
   static Command compareAnalyzerCfe(String executable, List<String> arguments,
       Map<String, String> environmentOverrides) {
-    return new CompareAnalyzerCfeCommand._(
+    return CompareAnalyzerCfeCommand._(
         executable, arguments, environmentOverrides);
   }
 
   static Command specParse(String executable, List<String> arguments,
       Map<String, String> environmentOverrides) {
-    return new SpecParseCommand._(executable, arguments, environmentOverrides);
+    return SpecParseCommand._(executable, arguments, environmentOverrides);
   }
 
   static Command vm(String executable, List<String> arguments,
       Map<String, String> environmentOverrides) {
-    return new VmCommand._(executable, arguments, environmentOverrides);
+    return VmCommand._(executable, arguments, environmentOverrides);
   }
 
   static Command vmBatch(String executable, String tester,
       List<String> arguments, Map<String, String> environmentOverrides,
-      {bool checked: true}) {
-    return new VmBatchCommand._(
-        executable, tester, arguments, environmentOverrides,
+      {bool checked = true}) {
+    return VmBatchCommand._(executable, tester, arguments, environmentOverrides,
         checked: checked);
   }
 
@@ -83,37 +82,36 @@
       List<String> arguments,
       bool useBlobs,
       bool useElf) {
-    return new AdbPrecompilationCommand._(precompiledRunner, processTest,
+    return AdbPrecompilationCommand._(precompiledRunner, processTest,
         testDirectory, arguments, useBlobs, useElf);
   }
 
   static Command adbDartk(String precompiledRunner, String processTest,
       String script, List<String> arguments, List<String> extraLibraries) {
-    return new AdbDartkCommand._(
+    return AdbDartkCommand._(
         precompiledRunner, processTest, script, arguments, extraLibraries);
   }
 
   static Command jsCommandLine(
       String displayName, String executable, List<String> arguments,
       [Map<String, String> environment]) {
-    return new JSCommandlineCommand._(
+    return JSCommandlineCommand._(
         displayName, executable, arguments, environment);
   }
 
   static Command process(
       String displayName, String executable, List<String> arguments,
       [Map<String, String> environment, String workingDirectory]) {
-    return new ProcessCommand._(
+    return ProcessCommand._(
         displayName, executable, arguments, environment, workingDirectory);
   }
 
   static Command copy(String sourceDirectory, String destinationDirectory) {
-    return new CleanDirectoryCopyCommand._(
-        sourceDirectory, destinationDirectory);
+    return CleanDirectoryCopyCommand._(sourceDirectory, destinationDirectory);
   }
 
   static Command makeSymlink(String link, String target) {
-    return new MakeSymlinkCommand._(link, target);
+    return MakeSymlinkCommand._(link, target);
   }
 
   static Command fasta(
@@ -124,7 +122,7 @@
       List<String> arguments,
       Map<String, String> environment,
       Uri workingDirectory) {
-    return new FastaCompilationCommand._(
+    return FastaCompilationCommand._(
         compilerLocation,
         outputFile.toFilePath(),
         bootstrapDependencies,
@@ -163,7 +161,7 @@
 
   int get hashCode {
     if (_cachedHashCode == null) {
-      var builder = new HashCodeBuilder();
+      var builder = HashCodeBuilder();
       _buildHashCode(builder);
       _cachedHashCode = builder.value;
     }
@@ -234,7 +232,7 @@
       deepJsonCompare(environmentOverrides, other.environmentOverrides);
 
   String get reproductionCommand {
-    var env = new StringBuffer();
+    var env = StringBuffer();
     environmentOverrides?.forEach((key, value) =>
         (io.Platform.operatingSystem == 'windows')
             ? env.write('set $key=${escapeCommandLineArgument(value)} & ')
@@ -292,7 +290,7 @@
   bool get outputIsUpToDate {
     if (_alwaysCompile) return false;
 
-    var file = new io.File(new Path("$outputFile.deps").toNativePath());
+    var file = io.File(Path("$outputFile.deps").toNativePath());
     if (!file.existsSync()) return false;
 
     var lines = file.readAsLinesSync();
@@ -306,7 +304,7 @@
 
     dependencies.addAll(_bootstrapDependencies);
     var jsOutputLastModified = TestUtils.lastModifiedCache
-        .getLastModified(new Uri(scheme: 'file', path: outputFile));
+        .getLastModified(Uri(scheme: 'file', path: outputFile));
     if (jsOutputLastModified == null) return false;
 
     for (var dependency in dependencies) {
@@ -374,12 +372,12 @@
     String relativizeAndEscape(String argument) {
       if (workingDirectory != null) {
         argument = argument.replaceAll(
-            workingDirectory, new Uri.directory(".").toFilePath());
+            workingDirectory, Uri.directory(".").toFilePath());
       }
       return escapeCommandLineArgument(argument);
     }
 
-    StringBuffer buffer = new StringBuffer();
+    StringBuffer buffer = StringBuffer();
     if (workingDirectory != null && !io.Platform.isWindows) {
       buffer.write("(cd ");
       buffer.write(escapeCommandLineArgument(workingDirectory));
@@ -502,7 +500,7 @@
   String get reproductionCommand {
     var parts = [
       io.Platform.resolvedExecutable,
-      'tools/testing/dart/launch_browser.dart',
+      'pkg/test_runner/bin/launch_browser.dart',
       browser.name,
       url
     ];
@@ -561,7 +559,7 @@
 
   VmBatchCommand._(String executable, String dartFile, List<String> arguments,
       Map<String, String> environmentOverrides,
-      {this.checked: true, int index = 0})
+      {this.checked = true, int index = 0})
       : this.dartFile = dartFile,
         super._('vm-batch', executable, arguments, environmentOverrides, null,
             index);
@@ -705,24 +703,24 @@
       "Copying '$_sourceDirectory' to '$_destinationDirectory'.";
 
   Future<ScriptCommandOutput> run() {
-    var watch = new Stopwatch()..start();
+    var watch = Stopwatch()..start();
 
-    var destination = new io.Directory(_destinationDirectory);
+    var destination = io.Directory(_destinationDirectory);
 
     return destination.exists().then((bool exists) {
       Future cleanDirectoryFuture;
       if (exists) {
         cleanDirectoryFuture = TestUtils.deleteDirectory(_destinationDirectory);
       } else {
-        cleanDirectoryFuture = new Future.value(null);
+        cleanDirectoryFuture = Future.value(null);
       }
       return cleanDirectoryFuture.then((_) {
         return TestUtils.copyDirectory(_sourceDirectory, _destinationDirectory);
       });
     }).then((_) {
-      return new ScriptCommandOutput(this, Expectation.pass, "", watch.elapsed);
+      return ScriptCommandOutput(this, Expectation.pass, "", watch.elapsed);
     }).catchError((error) {
-      return new ScriptCommandOutput(
+      return ScriptCommandOutput(
           this, Expectation.fail, "An error occured: $error.", watch.elapsed);
     });
   }
@@ -754,21 +752,21 @@
       "Make symbolic link '$_link' (target: $_target)'.";
 
   Future<ScriptCommandOutput> run() {
-    var watch = new Stopwatch()..start();
-    var targetFile = new io.Directory(_target);
+    var watch = Stopwatch()..start();
+    var targetFile = io.Directory(_target);
     return targetFile.exists().then((bool targetExists) {
       if (!targetExists) {
-        throw new Exception("Target '$_target' does not exist");
+        throw Exception("Target '$_target' does not exist");
       }
-      var link = new io.Link(_link);
+      var link = io.Link(_link);
 
       return link.exists().then((bool exists) {
         if (exists) link.deleteSync();
       }).then((_) => link.create(_target));
     }).then((_) {
-      return new ScriptCommandOutput(this, Expectation.pass, "", watch.elapsed);
+      return ScriptCommandOutput(this, Expectation.pass, "", watch.elapsed);
     }).catchError((error) {
-      return new ScriptCommandOutput(
+      return ScriptCommandOutput(
           this, Expectation.fail, "An error occured: $error.", watch.elapsed);
     });
   }
diff --git a/tools/testing/dart/command_output.dart b/pkg/test_runner/lib/src/command_output.dart
similarity index 97%
rename from tools/testing/dart/command_output.dart
rename to pkg/test_runner/lib/src/command_output.dart
index 43caac4..f3be68f 100644
--- a/tools/testing/dart/command_output.dart
+++ b/pkg/test_runner/lib/src/command_output.dart
@@ -12,8 +12,9 @@
 import 'browser_controller.dart';
 import 'command.dart';
 import 'configuration.dart';
+import 'process_queue.dart';
 import 'test_progress.dart';
-import 'test_runner.dart';
+import 'test_case.dart';
 import 'utils.dart';
 
 /// CommandOutput records the output of a completed command: the process's exit
@@ -149,7 +150,7 @@
 }
 
 class BrowserTestJsonResult {
-  static const _allowedTypes = const [
+  static const _allowedTypes = [
     'sync_exception',
     'window_onerror',
     'script_onerror',
@@ -211,7 +212,7 @@
           dom = '$dom\n';
         }
 
-        return new BrowserTestJsonResult(
+        return BrowserTestJsonResult(
             _getOutcome(messagesByType), dom, events as List<dynamic>);
       }
     } catch (error) {
@@ -311,7 +312,7 @@
       }
     }
 
-    return new BrowserCommandOutput._internal(command, result, outcome,
+    return BrowserCommandOutput._internal(command, result, outcome,
         parsedResult, encodeUtf8(""), encodeUtf8(stderr));
   }
 
@@ -528,7 +529,7 @@
     // Parse a line delimited by the | character using \ as an escape character
     // like:  FOO|BAR|FOO\|BAR|FOO\\BAZ as 4 fields: FOO BAR FOO|BAR FOO\BAZ
     List<String> splitMachineError(String line) {
-      var field = new StringBuffer();
+      var field = StringBuffer();
       var result = <String>[];
       var escaped = false;
       for (var i = 0; i < line.length; i++) {
@@ -540,7 +541,7 @@
         escaped = false;
         if (c == '|') {
           result.add(field.toString());
-          field = new StringBuffer();
+          field = StringBuffer();
           continue;
         }
         field.write(c);
@@ -1022,40 +1023,40 @@
     List<int> stdout, List<int> stderr, Duration time, bool compilationSkipped,
     [int pid = 0]) {
   if (command is AnalysisCommand) {
-    return new AnalysisCommandOutput(
+    return AnalysisCommandOutput(
         command, exitCode, timedOut, stdout, stderr, time, compilationSkipped);
   } else if (command is CompareAnalyzerCfeCommand) {
-    return new CompareAnalyzerCfeCommandOutput(
+    return CompareAnalyzerCfeCommandOutput(
         command, exitCode, timedOut, stdout, stderr, time, compilationSkipped);
   } else if (command is SpecParseCommand) {
-    return new SpecParseCommandOutput(
+    return SpecParseCommandOutput(
         command, exitCode, timedOut, stdout, stderr, time, compilationSkipped);
   } else if (command is VmCommand) {
-    return new VMCommandOutput(
+    return VMCommandOutput(
         command, exitCode, timedOut, stdout, stderr, time, pid);
   } else if (command is VMKernelCompilationCommand) {
-    return new VMKernelCompilationCommandOutput(
+    return VMKernelCompilationCommandOutput(
         command, exitCode, timedOut, stdout, stderr, time, compilationSkipped);
   } else if (command is AdbPrecompilationCommand) {
-    return new VMCommandOutput(
+    return VMCommandOutput(
         command, exitCode, timedOut, stdout, stderr, time, pid);
   } else if (command is CompilationCommand) {
     if (command.displayName == 'precompiler' ||
         command.displayName == 'app_jit') {
-      return new VMCommandOutput(
+      return VMCommandOutput(
           command, exitCode, timedOut, stdout, stderr, time, pid);
     } else if (command.displayName == 'dartdevc') {
-      return new DevCompilerCommandOutput(command, exitCode, timedOut, stdout,
+      return DevCompilerCommandOutput(command, exitCode, timedOut, stdout,
           stderr, time, compilationSkipped, pid);
     }
-    return new CompilationCommandOutput(
+    return CompilationCommandOutput(
         command, exitCode, timedOut, stdout, stderr, time, compilationSkipped);
   } else if (command is JSCommandlineCommand) {
-    return new JSCommandLineOutput(
+    return JSCommandLineOutput(
         command, exitCode, timedOut, stdout, stderr, time);
   }
 
-  return new CommandOutput(command, exitCode, timedOut, stdout, stderr, time,
+  return CommandOutput(command, exitCode, timedOut, stdout, stderr, time,
       compilationSkipped, pid);
 }
 
diff --git a/tools/testing/dart/compiler_configuration.dart b/pkg/test_runner/lib/src/compiler_configuration.dart
similarity index 92%
rename from tools/testing/dart/compiler_configuration.dart
rename to pkg/test_runner/lib/src/compiler_configuration.dart
index c67ae47..8a5ee37 100644
--- a/tools/testing/dart/compiler_configuration.dart
+++ b/pkg/test_runner/lib/src/compiler_configuration.dart
@@ -53,54 +53,51 @@
   factory CompilerConfiguration(TestConfiguration configuration) {
     switch (configuration.compiler) {
       case Compiler.dart2analyzer:
-        return new AnalyzerCompilerConfiguration(configuration);
+        return AnalyzerCompilerConfiguration(configuration);
 
       case Compiler.compareAnalyzerCfe:
-        return new CompareAnalyzerCfeCompilerConfiguration(configuration);
+        return CompareAnalyzerCfeCompilerConfiguration(configuration);
 
       case Compiler.dart2js:
-        return new Dart2jsCompilerConfiguration(configuration);
+        return Dart2jsCompilerConfiguration(configuration);
 
       case Compiler.dartdevc:
-        return new DevCompilerConfiguration(configuration);
+        return DevCompilerConfiguration(configuration);
 
       case Compiler.dartdevk:
-        return new DevCompilerConfiguration(configuration);
+        return DevCompilerConfiguration(configuration);
 
       case Compiler.appJit:
-        return new AppJitCompilerConfiguration(configuration,
-            previewDart2: false);
+        return AppJitCompilerConfiguration(configuration, previewDart2: false);
 
       case Compiler.appJitk:
-        return new AppJitCompilerConfiguration(configuration);
+        return AppJitCompilerConfiguration(configuration);
 
       case Compiler.precompiler:
-        return new PrecompilerCompilerConfiguration(configuration,
+        return PrecompilerCompilerConfiguration(configuration,
             previewDart2: false);
 
       case Compiler.dartk:
+      case Compiler.dartkb:
         if (configuration.architecture == Architecture.simdbc64 ||
             configuration.architecture == Architecture.simarm ||
             configuration.architecture == Architecture.simarm64 ||
             configuration.system == System.android) {
-          return new VMKernelCompilerConfiguration(configuration);
+          return VMKernelCompilerConfiguration(configuration);
         }
-        return new NoneCompilerConfiguration(configuration);
-
-      case Compiler.dartkb:
-        return new VMKernelCompilerConfiguration(configuration);
+        return NoneCompilerConfiguration(configuration);
 
       case Compiler.dartkp:
-        return new PrecompilerCompilerConfiguration(configuration);
+        return PrecompilerCompilerConfiguration(configuration);
 
       case Compiler.specParser:
-        return new SpecParserCompilerConfiguration(configuration);
+        return SpecParserCompilerConfiguration(configuration);
 
       case Compiler.fasta:
-        return new FastaCompilerConfiguration(configuration);
+        return FastaCompilerConfiguration(configuration);
 
       case Compiler.none:
-        return new NoneCompilerConfiguration(configuration);
+        return NoneCompilerConfiguration(configuration);
     }
 
     throw "unreachable";
@@ -134,7 +131,7 @@
       String tempDir,
       List<String> arguments,
       Map<String, String> environmentOverrides) {
-    return new CommandArtifact([], null, null);
+    return CommandArtifact([], null, null);
   }
 
   List<String> computeCompilerArguments(
@@ -224,7 +221,7 @@
     final commands = <Command>[
       computeCompileToKernelCommand(tempDir, arguments, environmentOverrides),
     ];
-    return new CommandArtifact(commands, tempKernelFile(tempDir),
+    return CommandArtifact(commands, tempKernelFile(tempDir),
         'application/kernel-ir-fully-linked');
   }
 
@@ -274,7 +271,7 @@
   }
 }
 
-typedef List<String> CompilerArgumentsFunction(
+typedef CompilerArgumentsFunction = List<String> Function(
     List<String> globalArguments, String previousCompilerOutput);
 
 class PipelineCommand {
@@ -285,7 +282,7 @@
 
   factory PipelineCommand.runWithGlobalArguments(
       CompilerConfiguration configuration) {
-    return new PipelineCommand._(configuration,
+    return PipelineCommand._(configuration,
         (List<String> globalArguments, String previousOutput) {
       assert(previousOutput == null);
       return globalArguments;
@@ -294,7 +291,7 @@
 
   factory PipelineCommand.runWithDartOrKernelFile(
       CompilerConfiguration configuration) {
-    return new PipelineCommand._(configuration,
+    return PipelineCommand._(configuration,
         (List<String> globalArguments, String previousOutput) {
       var filtered = globalArguments
           .where((name) => name.endsWith('.dart') || name.endsWith('.dill'))
@@ -306,7 +303,7 @@
 
   factory PipelineCommand.runWithPreviousKernelOutput(
       CompilerConfiguration configuration) {
-    return new PipelineCommand._(configuration,
+    return PipelineCommand._(configuration,
         (List<String> globalArguments, String previousOutput) {
       assert(previousOutput.endsWith('.dill'));
       return _replaceDartFiles(globalArguments, previousOutput);
@@ -349,8 +346,7 @@
       allCommands.addAll(artifact.commands);
     }
 
-    return new CommandArtifact(
-        allCommands, artifact.filename, artifact.mimeType);
+    return CommandArtifact(allCommands, artifact.filename, artifact.mimeType);
   }
 
   List<String> computeCompilerArguments(
@@ -438,7 +434,7 @@
         _configuration.buildDirectory,
         () => [
               Uri.base
-                  .resolveUri(new Uri.directory(_configuration.buildDirectory))
+                  .resolveUri(Uri.directory(_configuration.buildDirectory))
                   .resolve('dart-sdk/bin/snapshots/dart2js.dart.snapshot')
             ]);
   }
@@ -480,7 +476,7 @@
     // TODO(athom): input filename extraction is copied from DDC. Maybe this
     // should be passed to computeCompilationArtifact, instead?
     var inputFile = arguments.last;
-    var inputFilename = (new Uri.file(inputFile)).pathSegments.last;
+    var inputFilename = (Uri.file(inputFile)).pathSegments.last;
     var out = "$tempDir/${inputFilename.replaceAll('.dart', '.js')}";
     var babel = _configuration.babel;
     var babelOut = out;
@@ -494,7 +490,7 @@
       commands.add(computeBabelCommand(out, babelOut, babel));
     }
 
-    return new CommandArtifact(commands, babelOut, 'application/javascript');
+    return CommandArtifact(commands, babelOut, 'application/javascript');
   }
 
   List<String> computeRuntimeArguments(
@@ -506,8 +502,8 @@
       List<String> originalArguments,
       CommandArtifact artifact) {
     Uri sdk = _useSdk
-        ? new Uri.directory(_configuration.buildDirectory).resolve('dart-sdk/')
-        : new Uri.directory(Repository.dir.toNativePath()).resolve('sdk/');
+        ? Uri.directory(_configuration.buildDirectory).resolve('dart-sdk/')
+        : Uri.directory(Repository.dir.toNativePath()).resolve('sdk/');
     Uri preambleDir = sdk.resolve('lib/_internal/js_runtime/lib/preambles/');
     return runtimeConfiguration.dart2jsPreambles(preambleDir)
       ..add(artifact.filename);
@@ -516,7 +512,7 @@
   Command computeBabelCommand(String input, String output, String options) {
     var uri = Repository.uri;
     var babelTransform =
-        uri.resolve('tools/testing/dart/babel_transform.js').toFilePath();
+        uri.resolve('pkg/test_runner/lib/src/babel_transform.js').toFilePath();
     var babelStandalone =
         uri.resolve('third_party/babel/babel.min.js').toFilePath();
     return Command.compilation(
@@ -589,7 +585,7 @@
       // at the built summary file location.
       var sdkSummaryFile =
           useDillFormat ? 'kernel/ddc_sdk.dill' : 'ddc_sdk.sum';
-      var sdkSummary = new Path(_configuration.buildDirectory)
+      var sdkSummary = Path(_configuration.buildDirectory)
           .append("/gen/utils/dartdevc/$sdkSummaryFile")
           .absolute
           .toNativePath();
@@ -600,7 +596,7 @@
     if (!useKernel) {
       // TODO(jmesserly): library-root needs to be removed.
       args.addAll(
-          ["--library-root", new Path(inputFile).directoryPath.toNativePath()]);
+          ["--library-root", Path(inputFile).directoryPath.toNativePath()]);
     }
 
     args.addAll([
@@ -622,15 +618,14 @@
       // Since the summaries for the packages are not near the tests, we give
       // dartdevc explicit module paths for each one. When the test is run, we
       // will tell require.js where to find each package's compiled JS.
-      var summary = new Path(_configuration.buildDirectory)
+      var summary = Path(_configuration.buildDirectory)
           .append("/gen/utils/dartdevc/$pkgDir/$package.$pkgExtension")
           .absolute
           .toNativePath();
       args.add("$summary=$package");
     }
 
-    var inputDir =
-        new Path(inputFile).append("..").canonicalize().toNativePath();
+    var inputDir = Path(inputFile).append("..").canonicalize().toNativePath();
     var displayName = useKernel ? 'dartdevk' : 'dartdevc';
     return Command.compilation(displayName, outputFile, bootstrapDependencies(),
         computeCompilerPath(), args, environment,
@@ -646,10 +641,10 @@
     // computeCompilerArguments() to here seems hacky. Is there a cleaner way?
     var sharedOptions = arguments.sublist(0, arguments.length - 1);
     var inputFile = arguments.last;
-    var inputFilename = (new Uri.file(inputFile)).pathSegments.last;
+    var inputFilename = (Uri.file(inputFile)).pathSegments.last;
     var outputFile = "$tempDir/${inputFilename.replaceAll('.dart', '.js')}";
 
-    return new CommandArtifact(
+    return CommandArtifact(
         [_createCommand(inputFile, outputFile, sharedOptions, environment)],
         outputFile,
         "application/javascript");
@@ -669,7 +664,7 @@
   bool get _isAot => true;
 
   PrecompilerCompilerConfiguration(TestConfiguration configuration,
-      {this.previewDart2: true})
+      {this.previewDart2 = true})
       : super._subclass(configuration);
 
   int get timeoutMultiplier {
@@ -703,7 +698,7 @@
       }
     }
 
-    return new CommandArtifact(
+    return CommandArtifact(
         commands, '$tempDir', 'application/dart-precompiled');
   }
 
@@ -913,7 +908,7 @@
   final bool previewDart2;
 
   AppJitCompilerConfiguration(TestConfiguration configuration,
-      {this.previewDart2: true})
+      {this.previewDart2 = true})
       : super._subclass(configuration);
 
   int get timeoutMultiplier {
@@ -926,7 +921,7 @@
   CommandArtifact computeCompilationArtifact(String tempDir,
       List<String> arguments, Map<String, String> environmentOverrides) {
     var snapshot = "$tempDir/out.jitsnapshot";
-    return new CommandArtifact(
+    return CommandArtifact(
         [computeCompilationCommand(tempDir, arguments, environmentOverrides)],
         snapshot,
         'application/dart-snapshot');
@@ -1013,7 +1008,7 @@
       List<String> arguments, Map<String, String> environmentOverrides) {
     arguments = arguments.toList();
     if (!previewDart2) {
-      throw new ArgumentError('--no-preview-dart-2 not supported');
+      throw ArgumentError('--no-preview-dart-2 not supported');
     }
     if (_configuration.useAnalyzerCfe) {
       arguments.add('--use-cfe');
@@ -1023,7 +1018,7 @@
     }
 
     // Since this is not a real compilation, no artifacts are produced.
-    return new CommandArtifact([
+    return CommandArtifact([
       Command.analysis(computeCompilerPath(), arguments, environmentOverrides)
     ], null, null);
   }
@@ -1059,11 +1054,11 @@
       List<String> arguments, Map<String, String> environmentOverrides) {
     arguments = arguments.toList();
     if (!previewDart2) {
-      throw new ArgumentError('--no-preview-dart-2 not supported');
+      throw ArgumentError('--no-preview-dart-2 not supported');
     }
 
     // Since this is not a real compilation, no artifacts are produced.
-    return new CommandArtifact([
+    return CommandArtifact([
       Command.compareAnalyzerCfe(
           computeCompilerPath(), arguments, environmentOverrides)
     ], null, null);
@@ -1093,7 +1088,7 @@
     arguments = arguments.toList();
 
     // Since this is not a real compilation, no artifacts are produced.
-    return new CommandArtifact([
+    return CommandArtifact([
       Command.specParse(computeCompilerPath(), arguments, environmentOverrides)
     ], null, null);
   }
@@ -1112,7 +1107,7 @@
 
 abstract class VMKernelCompilerMixin {
   static final noCausalAsyncStacksRegExp =
-      new RegExp('--no[_-]causal[_-]async[_-]stacks');
+      RegExp('--no[_-]causal[_-]async[_-]stacks');
 
   TestConfiguration get _configuration;
 
@@ -1127,7 +1122,7 @@
   List<Uri> bootstrapDependencies();
 
   String tempKernelFile(String tempDir) =>
-      new Path('$tempDir/out.dill').toNativePath();
+      Path('$tempDir/out.dill').toNativePath();
 
   Command computeCompileToKernelCommand(String tempDir, List<String> arguments,
       Map<String, String> environmentOverrides) {
@@ -1202,7 +1197,7 @@
 
   factory FastaCompilerConfiguration(TestConfiguration configuration) {
     var buildDirectory =
-        Uri.base.resolveUri(new Uri.directory(configuration.buildDirectory));
+        Uri.base.resolveUri(Uri.directory(configuration.buildDirectory));
 
     var dillDir = buildDirectory;
     if (configuration.useSdk) {
@@ -1214,7 +1209,7 @@
 
     var vmExecutable = buildDirectory
         .resolve(configuration.useSdk ? "dart-sdk/bin/dart" : "dart");
-    return new FastaCompilerConfiguration._(
+    return FastaCompilerConfiguration._(
         platformDill, vmExecutable, configuration);
   }
 
@@ -1232,7 +1227,7 @@
   CommandArtifact computeCompilationArtifact(String tempDir,
       List<String> arguments, Map<String, String> environmentOverrides) {
     var output =
-        Uri.base.resolveUri(new Uri.directory(tempDir)).resolve("out.dill");
+        Uri.base.resolveUri(Uri.directory(tempDir)).resolve("out.dill");
     var outputFileName = output.toFilePath();
 
     var compilerArguments = <String>['--verify'];
@@ -1244,7 +1239,7 @@
         ["-o", outputFileName, "--platform", _platformDill.toFilePath()]);
     compilerArguments.addAll(arguments);
 
-    return new CommandArtifact([
+    return CommandArtifact([
       Command.fasta(
           _compilerLocation,
           output,
@@ -1264,7 +1259,7 @@
       List<String> dart2jsOptions,
       List<String> ddcOptions,
       List<String> args) {
-    List<String> arguments = new List<String>.from(sharedOptions);
+    List<String> arguments = List<String>.from(sharedOptions);
     arguments.addAll(_configuration.sharedOptions);
     for (String argument in args) {
       if (argument == "--ignore-unrecognized-flags") continue;
diff --git a/tools/testing/dart/configuration.dart b/pkg/test_runner/lib/src/configuration.dart
similarity index 90%
rename from tools/testing/dart/configuration.dart
rename to pkg/test_runner/lib/src/configuration.dart
index da990fb9..e37b2f3 100644
--- a/tools/testing/dart/configuration.dart
+++ b/pkg/test_runner/lib/src/configuration.dart
@@ -10,10 +10,10 @@
 export 'package:smith/smith.dart';
 
 import 'compiler_configuration.dart';
-import 'http_server.dart';
 import 'path.dart';
 import 'repository.dart';
 import 'runtime_configuration.dart';
+import 'testing_servers.dart';
 
 /// All of the contextual information to determine how a test suite should be
 /// run.
@@ -173,7 +173,7 @@
 
   TestingServers get servers {
     if (_servers == null) {
-      throw new StateError("Servers have not been started yet.");
+      throw StateError("Servers have not been started yet.");
     }
     return _servers;
   }
@@ -255,7 +255,7 @@
 
   String get windowsSdkPath {
     if (!Platform.isWindows) {
-      throw new StateError(
+      throw StateError(
           "Should not use windowsSdkPath when not running on Windows.");
     }
 
@@ -263,8 +263,8 @@
       // When running tests on Windows, use cdb from depot_tools to dump
       // stack traces of tests timing out.
       try {
-        var path = new Path("build/win_toolchain.json").toNativePath();
-        var text = new File(path).readAsStringSync();
+        var path = Path("build/win_toolchain.json").toNativePath();
+        var text = File(path).readAsStringSync();
         _windowsSdkPath = jsonDecode(text)['win_sdk'] as String;
       } on dynamic {
         // Ignore errors here. If win_sdk is not found, stack trace dumping
@@ -293,29 +293,29 @@
 
     if (location != null) return location;
 
-    const locations = const {
-      Runtime.firefox: const {
+    const locations = {
+      Runtime.firefox: {
         System.win: 'C:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe',
         System.linux: 'firefox',
         System.mac: '/Applications/Firefox.app/Contents/MacOS/firefox'
       },
-      Runtime.chrome: const {
+      Runtime.chrome: {
         System.win:
             'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
         System.mac:
             '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
         System.linux: 'google-chrome'
       },
-      Runtime.safari: const {
+      Runtime.safari: {
         System.mac: '/Applications/Safari.app/Contents/MacOS/Safari'
       },
-      Runtime.ie9: const {
+      Runtime.ie9: {
         System.win: 'C:\\Program Files\\Internet Explorer\\iexplore.exe'
       },
-      Runtime.ie10: const {
+      Runtime.ie10: {
         System.win: 'C:\\Program Files\\Internet Explorer\\iexplore.exe'
       },
-      Runtime.ie11: const {
+      Runtime.ie11: {
         System.win: 'C:\\Program Files\\Internet Explorer\\iexplore.exe'
       }
     };
@@ -332,12 +332,12 @@
   RuntimeConfiguration _runtimeConfiguration;
 
   RuntimeConfiguration get runtimeConfiguration =>
-      _runtimeConfiguration ??= new RuntimeConfiguration(this);
+      _runtimeConfiguration ??= RuntimeConfiguration(this);
 
   CompilerConfiguration _compilerConfiguration;
 
   CompilerConfiguration get compilerConfiguration =>
-      _compilerConfiguration ??= new CompilerConfiguration(this);
+      _compilerConfiguration ??= CompilerConfiguration(this);
 
   /// Determines if this configuration has a compatible compiler and runtime
   /// and other valid fields.
@@ -377,7 +377,7 @@
   /// server for cross-domain tests can be found by calling
   /// `getCrossOriginPortNumber()`.
   Future startServers() {
-    _servers = new TestingServers(
+    _servers = TestingServers(
         buildDirectory, isCsp, runtime, null, packageRoot, packages);
     var future = servers.startServers(localIP,
         port: testServerPort, crossOriginPort: testServerCrossOriginPort);
@@ -414,8 +414,8 @@
     var normal = '$modeName$os$arch';
     var cross = '$modeName${os}X$arch';
     var outDir = system.outputDirectory;
-    var normalDir = new Directory(new Path('$outDir$normal').toNativePath());
-    var crossDir = new Directory(new Path('$outDir$cross').toNativePath());
+    var normalDir = Directory(Path('$outDir$normal').toNativePath());
+    var crossDir = Directory(Path('$outDir$cross').toNativePath());
 
     if (normalDir.existsSync() && crossDir.existsSync()) {
       throw "You can't have both $normalDir and $crossDir. We don't know which"
@@ -468,18 +468,18 @@
 }
 
 class Progress {
-  static const compact = const Progress._('compact');
-  static const color = const Progress._('color');
-  static const line = const Progress._('line');
-  static const verbose = const Progress._('verbose');
-  static const silent = const Progress._('silent');
-  static const status = const Progress._('status');
-  static const buildbot = const Progress._('buildbot');
-  static const diff = const Progress._('diff');
+  static const compact = Progress._('compact');
+  static const color = Progress._('color');
+  static const line = Progress._('line');
+  static const verbose = Progress._('verbose');
+  static const silent = Progress._('silent');
+  static const status = Progress._('status');
+  static const buildbot = Progress._('buildbot');
+  static const diff = Progress._('diff');
 
   static final List<String> names = _all.keys.toList();
 
-  static final _all = new Map<String, Progress>.fromIterable(
+  static final _all = Map<String, Progress>.fromIterable(
       [compact, color, line, verbose, silent, status, buildbot, diff],
       key: (progress) => (progress as Progress).name);
 
@@ -487,7 +487,7 @@
     var progress = _all[name];
     if (progress != null) return progress;
 
-    throw new ArgumentError('Unknown progress type "$name".');
+    throw ArgumentError('Unknown progress type "$name".');
   }
 
   final String name;
diff --git a/tools/testing/dart/dependency_graph.dart b/pkg/test_runner/lib/src/dependency_graph.dart
similarity index 80%
rename from tools/testing/dart/dependency_graph.dart
rename to pkg/test_runner/lib/src/dependency_graph.dart
index 1b6cb0e..bf89d9e 100644
--- a/tools/testing/dart/dependency_graph.dart
+++ b/pkg/test_runner/lib/src/dependency_graph.dart
@@ -12,7 +12,7 @@
 /// The graph exposes a few broadcast streams that can be subscribed to in
 /// order to be notified of modifications to the graph.
 class Graph<T> {
-  final _nodes = new Set<Node<T>>();
+  final _nodes = <Node<T>>{};
   final _stateCounts = <NodeState, int>{};
   bool _isSealed = false;
 
@@ -29,11 +29,11 @@
   final StreamController<Null> _sealedController;
 
   factory Graph() {
-    var added = new StreamController<Node<T>>();
-    var changed = new StreamController<StateChangedEvent<T>>();
-    var sealed = new StreamController<Null>();
+    var added = StreamController<Node<T>>();
+    var changed = StreamController<StateChangedEvent<T>>();
+    var sealed = StreamController<Null>();
 
-    return new Graph._(
+    return Graph._(
         added,
         added.stream.asBroadcastStream(),
         changed,
@@ -74,7 +74,7 @@
       {bool timingDependency = false}) {
     assert(!_isSealed);
 
-    var node = new Node._(userData, timingDependency);
+    var node = Node._(userData, timingDependency);
     _nodes.add(node);
 
     for (var dependency in dependencies) {
@@ -99,8 +99,7 @@
     _stateCounts.putIfAbsent(state, () => 0);
     _stateCounts[state] += 1;
 
-    _emitEvent(
-        _changedController, new StateChangedEvent(node, fromState, state));
+    _emitEvent(_changedController, StateChangedEvent(node, fromState, state));
   }
 
   /// We emit events asynchronously so the graph can be build up in small
@@ -117,8 +116,8 @@
   final T data;
   final bool timingDependency;
   NodeState _state = NodeState.initialized;
-  final Set<Node<T>> _dependencies = new Set();
-  final Set<Node<T>> _neededFor = new Set();
+  final Set<Node<T>> _dependencies = {};
+  final Set<Node<T>> _neededFor = {};
 
   Node._(this.data, this.timingDependency);
 
@@ -128,13 +127,13 @@
 }
 
 class NodeState {
-  static const initialized = const NodeState._("Initialized");
-  static const waiting = const NodeState._("Waiting");
-  static const enqueuing = const NodeState._("Enqueuing");
-  static const processing = const NodeState._("Running");
-  static const successful = const NodeState._("Successful");
-  static const failed = const NodeState._("Failed");
-  static const unableToRun = const NodeState._("UnableToRun");
+  static const initialized = NodeState._("Initialized");
+  static const waiting = NodeState._("Waiting");
+  static const enqueuing = NodeState._("Enqueuing");
+  static const processing = NodeState._("Running");
+  static const successful = NodeState._("Successful");
+  static const failed = NodeState._("Failed");
+  static const unableToRun = NodeState._("UnableToRun");
 
   final String name;
 
diff --git a/tools/testing/dart/environment.dart b/pkg/test_runner/lib/src/environment.dart
similarity index 68%
rename from tools/testing/dart/environment.dart
rename to pkg/test_runner/lib/src/environment.dart
index 5245977..b028db0 100644
--- a/tools/testing/dart/environment.dart
+++ b/pkg/test_runner/lib/src/environment.dart
@@ -6,38 +6,37 @@
 
 import 'configuration.dart';
 
-typedef String _LookUpFunction(TestConfiguration configuration);
-typedef bool _BoolLookUpFunction(TestConfiguration configuration);
+typedef _LookUpFunction = String Function(TestConfiguration configuration);
+typedef _BoolLookUpFunction = bool Function(TestConfiguration configuration);
 
 // TODO(29756): Instead of synthesized negated variables like "unchecked",
 // consider adding support for "!" to status expressions.
 final _variables = {
-  "analyzer": new _Variable.bool((c) => c.compiler == Compiler.dart2analyzer),
-  "analyzer_use_fasta_parser":
-      new _Variable.bool((c) => c.useAnalyzerFastaParser),
-  "arch": new _Variable((c) => c.architecture.name, Architecture.names),
-  "browser": new _Variable.bool((c) => c.runtime.isBrowser),
-  "builder_tag": new _Variable((c) => c.builderTag ?? "", const []),
-  "checked": new _Variable.bool((c) => c.isChecked),
-  "compiler": new _Variable((c) => c.compiler.name, Compiler.names),
-  "csp": new _Variable.bool((c) => c.isCsp),
-  "enable_asserts": new _Variable.bool((c) => c.useEnableAsserts),
-  "fasta": new _Variable.bool((c) => c.usesFasta),
-  "host_checked": new _Variable.bool((c) => c.isHostChecked),
-  "host_unchecked": new _Variable.bool((c) => !c.isHostChecked),
-  "hot_reload": new _Variable.bool((c) => c.hotReload),
-  "hot_reload_rollback": new _Variable.bool((c) => c.hotReloadRollback),
-  "ie": new _Variable.bool((c) => c.runtime.isIE),
-  "jscl": new _Variable.bool((c) => c.runtime.isJSCommandLine),
-  "minified": new _Variable.bool((c) => c.isMinified),
-  "mode": new _Variable((c) => c.mode.name, Mode.names),
-  "no_preview_dart_2": new _Variable.bool((c) => c.noPreviewDart2),
-  "preview_dart_2": new _Variable.bool((c) => !c.noPreviewDart2),
-  "runtime": new _Variable(_runtimeName, _runtimeNames),
-  "spec_parser": new _Variable.bool((c) => c.compiler == Compiler.specParser),
-  "strong": new _Variable.bool((c) => !c.noPreviewDart2),
-  "system": new _Variable(_systemName, _systemNames),
-  "use_sdk": new _Variable.bool((c) => c.useSdk)
+  "analyzer": _Variable.bool((c) => c.compiler == Compiler.dart2analyzer),
+  "analyzer_use_fasta_parser": _Variable.bool((c) => c.useAnalyzerFastaParser),
+  "arch": _Variable((c) => c.architecture.name, Architecture.names),
+  "browser": _Variable.bool((c) => c.runtime.isBrowser),
+  "builder_tag": _Variable((c) => c.builderTag ?? "", const []),
+  "checked": _Variable.bool((c) => c.isChecked),
+  "compiler": _Variable((c) => c.compiler.name, Compiler.names),
+  "csp": _Variable.bool((c) => c.isCsp),
+  "enable_asserts": _Variable.bool((c) => c.useEnableAsserts),
+  "fasta": _Variable.bool((c) => c.usesFasta),
+  "host_checked": _Variable.bool((c) => c.isHostChecked),
+  "host_unchecked": _Variable.bool((c) => !c.isHostChecked),
+  "hot_reload": _Variable.bool((c) => c.hotReload),
+  "hot_reload_rollback": _Variable.bool((c) => c.hotReloadRollback),
+  "ie": _Variable.bool((c) => c.runtime.isIE),
+  "jscl": _Variable.bool((c) => c.runtime.isJSCommandLine),
+  "minified": _Variable.bool((c) => c.isMinified),
+  "mode": _Variable((c) => c.mode.name, Mode.names),
+  "no_preview_dart_2": _Variable.bool((c) => c.noPreviewDart2),
+  "preview_dart_2": _Variable.bool((c) => !c.noPreviewDart2),
+  "runtime": _Variable(_runtimeName, _runtimeNames),
+  "spec_parser": _Variable.bool((c) => c.compiler == Compiler.specParser),
+  "strong": _Variable.bool((c) => !c.noPreviewDart2),
+  "system": _Variable(_systemName, _systemNames),
+  "use_sdk": _Variable.bool((c) => c.useSdk)
 };
 
 /// Gets the name of the runtime as it appears in status files.
@@ -103,7 +102,7 @@
     if (variable == null) {
       // This shouldn't happen since we validate variables before evaluating
       // expressions.
-      throw new ArgumentError('Unknown variable "$variable".');
+      throw ArgumentError('Unknown variable "$variable".');
     }
 
     return variable.lookUp(_configuration);
diff --git a/tools/testing/dart/expectation_set.dart b/pkg/test_runner/lib/src/expectation_set.dart
similarity index 90%
rename from tools/testing/dart/expectation_set.dart
rename to pkg/test_runner/lib/src/expectation_set.dart
index 4e83724..40f84af 100644
--- a/tools/testing/dart/expectation_set.dart
+++ b/pkg/test_runner/lib/src/expectation_set.dart
@@ -27,16 +27,16 @@
   final Map<String, RegExp> _globCache = {};
 
   /// The root of the expectation tree.
-  final _PathNode _tree = new _PathNode();
+  final _PathNode _tree = _PathNode();
 
   /// Reads the expectations defined by the status files at [statusFilePaths]
   /// when in [configuration].
   ExpectationSet.read(
       List<String> statusFilePaths, TestConfiguration configuration) {
     try {
-      var environment = new ConfigurationEnvironment(configuration);
+      var environment = ConfigurationEnvironment(configuration);
       for (var path in statusFilePaths) {
-        var file = new StatusFile.read(path);
+        var file = StatusFile.read(path);
         file.validate(environment);
         for (var section in file.sections) {
           if (section.isEnabled(environment)) {
@@ -60,11 +60,11 @@
     for (var part in entry.path.split('/')) {
       if (part.contains("*")) {
         var regExp = _globCache.putIfAbsent(part, () {
-          return new RegExp("^" + part.replaceAll("*", ".*") + r"$");
+          return RegExp("^" + part.replaceAll("*", ".*") + r"$");
         });
-        tree = tree.regExpChildren.putIfAbsent(regExp, () => new _PathNode());
+        tree = tree.regExpChildren.putIfAbsent(regExp, () => _PathNode());
       } else {
-        tree = tree.stringChildren.putIfAbsent(part, () => new _PathNode());
+        tree = tree.stringChildren.putIfAbsent(part, () => _PathNode());
       }
     }
 
@@ -80,7 +80,7 @@
   /// checks that the anchored regular expression "^$keyComponent\$" matches
   /// the corresponding filename component.
   Set<Expectation> expectations(String path) {
-    var result = new Set<Expectation>();
+    var result = <Expectation>{};
     _tree.walk(path.split('/'), 0, result);
 
     // If no status files modified the expectation, default to the test passing.
@@ -105,7 +105,7 @@
 
   /// The test expectatations that any test within this directory should
   /// include.
-  final Set<Expectation> expectations = new Set();
+  final Set<Expectation> expectations = {};
 
   /// Walks the list of path [parts], starting at [index] adding any
   /// expectations to [result] from this node and any of its matching children.
diff --git a/tools/testing/dart/multitest.dart b/pkg/test_runner/lib/src/multitest.dart
similarity index 91%
rename from tools/testing/dart/multitest.dart
rename to pkg/test_runner/lib/src/multitest.dart
index 902dd9e..987af25 100644
--- a/tools/testing/dart/multitest.dart
+++ b/pkg/test_runner/lib/src/multitest.dart
@@ -77,7 +77,7 @@
 import "utils.dart";
 
 /// Until legacy multitests are ported we need to support both /// and //#
-final _multitestMarker = new RegExp(r"//[/#]");
+final _multitestMarker = RegExp(r"//[/#]");
 
 final _multitestOutcomes = [
   'ok',
@@ -90,13 +90,9 @@
   'checked mode compile-time error' // This is now a no-op
 ].toSet();
 
-// Note: This function is called directly by:
-//
-//     tests/compiler/dart2js/frontend_checker.dart
-//     tools/status_clean.dart
 void extractTestsFromMultitest(Path filePath, Map<String, String> tests,
     Map<String, Set<String>> outcomes) {
-  var contents = new File(filePath.toNativePath()).readAsStringSync();
+  var contents = File(filePath.toNativePath()).readAsStringSync();
 
   var firstNewline = contents.indexOf('\n');
   var lineSeparator =
@@ -109,8 +105,8 @@
   var testsAsLines = <String, List<String>>{};
 
   // Add the default case with key "none".
-  testsAsLines['none'] = <String>[];
-  outcomes['none'] = new Set<String>();
+  testsAsLines['none'] = [];
+  outcomes['none'] = {};
 
   var lineCount = 0;
   for (var line in lines) {
@@ -118,12 +114,12 @@
     var annotation = _Annotation.tryParse(line);
     if (annotation != null) {
       testsAsLines.putIfAbsent(
-          annotation.key, () => new List<String>.from(testsAsLines["none"]));
+          annotation.key, () => List<String>.from(testsAsLines["none"]));
       // Add line to test with annotation.key as key, empty line to the rest.
       for (var key in testsAsLines.keys) {
         testsAsLines[key].add(annotation.key == key ? line : "");
       }
-      outcomes.putIfAbsent(annotation.key, () => new Set<String>());
+      outcomes.putIfAbsent(annotation.key, () => <String>{});
       if (annotation.rest != 'continued') {
         for (var nextOutcome in annotation.outcomes) {
           if (_multitestOutcomes.contains(nextOutcome)) {
@@ -169,8 +165,7 @@
 Future doMultitest(Path filePath, String outputDir, Path suiteDir,
     CreateTest doTest, bool hotReload) {
   void writeFile(String filepath, String content) {
-    final File file = new File(filepath);
-
+    var file = File(filepath);
     if (file.existsSync()) {
       var oldContent = file.readAsStringSync();
       if (oldContent == content) {
@@ -194,7 +189,7 @@
   var importsToCopy = _findAllRelativeImports(filePath);
   var futureCopies = <Future>[];
   for (var relativeImport in importsToCopy) {
-    var importPath = new Path(relativeImport);
+    var importPath = Path(relativeImport);
     // Make sure the target directory exists.
     var importDir = importPath.directoryPath;
     if (!importDir.isEmpty) {
@@ -253,12 +248,12 @@
         .split(_multitestMarker)[1]
         .split(':')
         .map((s) => s.trim())
-        .where((s) => s.length > 0)
+        .where((s) => s.isNotEmpty)
         .toList();
 
     if (parts.length <= 1) return null;
 
-    return new _Annotation._(parts[0], parts[1]);
+    return _Annotation._(parts[0], parts[1]);
   }
 
   final String key;
@@ -276,9 +271,9 @@
 /// Finds all relative imports and copies them into the directory with the
 /// generated tests.
 Set<String> _findAllRelativeImports(Path topLibrary) {
-  var found = new Set<String>();
+  var found = <String>{};
   var libraryDir = topLibrary.directoryPath;
-  var relativeImportRegExp = new RegExp(
+  var relativeImportRegExp = RegExp(
       '^(?:@.*\\s+)?' // Allow for a meta-data annotation.
       '(import|part)'
       '\\s+["\']'
@@ -287,7 +282,7 @@
       '["\']');
 
   processFile(Path filePath) {
-    var file = new File(filePath.toNativePath());
+    var file = File(filePath.toNativePath());
     for (var line in file.readAsLinesSync()) {
       var match = relativeImportRegExp.firstMatch(line);
       if (match == null) continue;
@@ -331,10 +326,10 @@
 Path _createMultitestDirectory(
     String outputDir, Path suiteDir, Path sourceDir) {
   var relative = sourceDir.relativeTo(suiteDir);
-  var path = new Path(outputDir)
+  var path = Path(outputDir)
       .append('generated_tests')
       .append(_suiteNameFromPath(suiteDir))
       .join(relative);
   TestUtils.mkdirRecursive(Path.workingDirectory, path);
-  return new Path(new File(path.toNativePath()).absolute.path);
+  return Path(File(path.toNativePath()).absolute.path);
 }
diff --git a/tools/testing/dart/options.dart b/pkg/test_runner/lib/src/options.dart
similarity index 86%
rename from tools/testing/dart/options.dart
rename to pkg/test_runner/lib/src/options.dart
index 9519f1c..59f4f59 100644
--- a/tools/testing/dart/options.dart
+++ b/pkg/test_runner/lib/src/options.dart
@@ -12,7 +12,7 @@
 import 'repository.dart';
 import 'utils.dart';
 
-const _defaultTestSelectors = const [
+const _defaultTestSelectors = [
   'samples',
   'standalone',
   'standalone_2',
@@ -82,9 +82,9 @@
 /// Parses command line arguments and produces a test runner configuration.
 class OptionsParser {
   static final List<_Option> _options = [
-    new _Option('mode', 'Mode in which to run the tests.',
+    _Option('mode', 'Mode in which to run the tests.',
         abbr: 'm', values: ['all']..addAll(Mode.names)),
-    new _Option(
+    _Option(
         'compiler',
         '''How the Dart code should be compiled or statically processed.
 
@@ -102,7 +102,7 @@
 spec_parser:   Parse Dart code using the specification parser.''',
         abbr: 'c',
         values: Compiler.names),
-    new _Option(
+    _Option(
         'runtime',
         '''Where the tests should be run.
 vm:               Run Dart code on the standalone Dart VM.
@@ -126,7 +126,7 @@
 none:             No runtime, compile only.''',
         abbr: 'r',
         values: Runtime.names),
-    new _Option(
+    _Option(
         'arch',
         '''The architecture to run tests for.
 
@@ -140,54 +140,51 @@
         values: ['all']..addAll(Architecture.names),
         defaultsTo: Architecture.x64.name,
         hide: true),
-    new _Option('system', 'The operating system to run tests on.',
+    _Option('system', 'The operating system to run tests on.',
         abbr: 's',
         values: System.names,
         defaultsTo: Platform.operatingSystem,
         hide: true),
-    new _Option(
+    _Option(
         'named_configuration',
         '''The named test configuration that supplies the values for all
 test options, specifying how tests should be run.''',
         abbr: 'n',
         hide: true),
-    new _Option.bool('strong', 'Deprecated, no-op.', hide: true),
+    _Option.bool('strong', 'Deprecated, no-op.', hide: true),
     // TODO(sigmund): rename flag once we migrate all dart2js bots to the test
     // matrix.
-    new _Option.bool('host_checked', 'Run compiler with assertions enabled.',
+    _Option.bool('host_checked', 'Run compiler with assertions enabled.',
         hide: true),
-    new _Option.bool('minified', 'Enable minification in the compiler.',
+    _Option.bool('minified', 'Enable minification in the compiler.',
         hide: true),
-    new _Option.bool(
-        'csp', 'Run tests under Content Security Policy restrictions.',
+    _Option.bool('csp', 'Run tests under Content Security Policy restrictions.',
         hide: true),
-    new _Option.bool('fast_tests',
+    _Option.bool('fast_tests',
         'Only run tests that are not marked `Slow` or `Timeout`.'),
-    new _Option.bool('enable_asserts',
+    _Option.bool('enable_asserts',
         'Pass the --enable-asserts flag to dart2js or to the vm.'),
-    new _Option.bool('no_preview_dart_2',
+    _Option.bool('no_preview_dart_2',
         'Enable legacy Dart 1 behavior for some runtimes and compilers.',
         hide: true),
-    new _Option.bool('use_cfe', 'Pass the --use-cfe flag to analyzer',
-        hide: true),
-    new _Option.bool('analyzer_use_fasta_parser',
+    _Option.bool('use_cfe', 'Pass the --use-cfe flag to analyzer', hide: true),
+    _Option.bool('analyzer_use_fasta_parser',
         'Pass the --use-fasta-parser flag to analyzer',
         hide: true),
 
-    new _Option.bool('hot_reload', 'Run hot reload stress tests.', hide: true),
-    new _Option.bool(
-        'hot_reload_rollback', 'Run hot reload rollback stress tests.',
+    _Option.bool('hot_reload', 'Run hot reload stress tests.', hide: true),
+    _Option.bool('hot_reload_rollback', 'Run hot reload rollback stress tests.',
         hide: true),
-    new _Option.bool(
+    _Option.bool(
         'use_blobs', 'Use mmap instead of shared libraries for precompilation.',
         hide: true),
-    new _Option.bool(
-        'use_elf', 'Directly generate an ELF shared libraries for precompilation.',
+    _Option.bool('use_elf',
+        'Directly generate an ELF shared libraries for precompilation.',
         hide: true),
-    new _Option.bool('keep_generated_files', 'Keep any generated files.',
+    _Option.bool('keep_generated_files', 'Keep any generated files.',
         abbr: 'k'),
-    new _Option.int('timeout', 'Timeout in seconds.', abbr: 't'),
-    new _Option(
+    _Option.int('timeout', 'Timeout in seconds.', abbr: 't'),
+    _Option(
         'progress',
         '''Progress indication mode.
 
@@ -196,130 +193,126 @@
         abbr: 'p',
         values: Progress.names,
         defaultsTo: Progress.compact.name),
-    new _Option('step_name', 'Step name for use by -pbuildbot.', hide: true),
-    new _Option.bool('report',
+    _Option('step_name', 'Step name for use by -pbuildbot.', hide: true),
+    _Option.bool('report',
         'Print a summary report of the number of tests, by expectation.',
         hide: true),
-    new _Option.int('tasks', 'The number of parallel tasks to run.',
+    _Option.int('tasks', 'The number of parallel tasks to run.',
         abbr: 'j', defaultsTo: Platform.numberOfProcessors),
-    new _Option.int('shards',
+    _Option.int('shards',
         'The number of instances that the tests will be sharded over.',
         defaultsTo: 1, hide: true),
-    new _Option.int(
+    _Option.int(
         'shard', 'The index of this instance when running in sharded mode.',
         defaultsTo: 1, hide: true),
-    new _Option.bool('help', 'Print list of options.', abbr: 'h'),
-    new _Option.int('repeat', 'How many times each test is run', defaultsTo: 1),
-    new _Option.bool('verbose', 'Verbose output.', abbr: 'v'),
-    new _Option.bool('verify-ir', 'Verify kernel IR.', hide: true),
-    new _Option.bool('no-tree-shake', 'Disable kernel IR tree shaking.',
+    _Option.bool('help', 'Print list of options.', abbr: 'h'),
+    _Option.int('repeat', 'How many times each test is run', defaultsTo: 1),
+    _Option.bool('verbose', 'Verbose output.', abbr: 'v'),
+    _Option.bool('verify-ir', 'Verify kernel IR.', hide: true),
+    _Option.bool('no-tree-shake', 'Disable kernel IR tree shaking.',
         hide: true),
-    new _Option.bool('list', 'List tests only, do not run them.'),
-    new _Option.bool('list-configurations', 'Output list of configurations.'),
-    new _Option.bool('list_status_files',
+    _Option.bool('list', 'List tests only, do not run them.'),
+    _Option.bool('list-configurations', 'Output list of configurations.'),
+    _Option.bool('list_status_files',
         'List status files for test-suites. Do not run any test suites.',
         hide: true),
-    new _Option.bool(
-        'clean_exit', 'Exit 0 if tests ran and results were output.',
+    _Option.bool('clean_exit', 'Exit 0 if tests ran and results were output.',
         hide: true),
-    new _Option.bool(
+    _Option.bool(
         'silent_failures',
         "Don't complain about failing tests. This is useful when in "
             "combination with --write-results.",
         hide: true),
-    new _Option.bool('report_in_json',
+    _Option.bool('report_in_json',
         'When listing with --list, output result summary in JSON.',
         hide: true),
-    new _Option.bool('time', 'Print timing information after running tests.'),
-    new _Option('dart', 'Path to dart executable.', hide: true),
-    new _Option('gen-snapshot', 'Path to gen_snapshot executable.', hide: true),
-    new _Option('firefox', 'Path to firefox browser executable.', hide: true),
-    new _Option('chrome', 'Path to chrome browser executable.', hide: true),
-    new _Option('safari', 'Path to safari browser executable.', hide: true),
-    new _Option.bool('use_sdk', '''Use compiler or runtime from the SDK.'''),
+    _Option.bool('time', 'Print timing information after running tests.'),
+    _Option('dart', 'Path to dart executable.', hide: true),
+    _Option('gen-snapshot', 'Path to gen_snapshot executable.', hide: true),
+    _Option('firefox', 'Path to firefox browser executable.', hide: true),
+    _Option('chrome', 'Path to chrome browser executable.', hide: true),
+    _Option('safari', 'Path to safari browser executable.', hide: true),
+    _Option.bool('use_sdk', '''Use compiler or runtime from the SDK.'''),
     // TODO(rnystrom): This does not appear to be used. Remove?
-    new _Option('build_directory',
+    _Option('build_directory',
         'The name of the build directory, where products are placed.',
         hide: true),
-    new _Option('output_directory',
+    _Option('output_directory',
         'The name of the output directory for storing log files.',
         defaultsTo: "logs", hide: true),
-    new _Option.bool('noBatch', 'Do not run tests in batch mode.', hide: true),
-    new _Option.bool('dart2js_batch', 'Run dart2js tests in batch mode.',
+    _Option.bool('noBatch', 'Do not run tests in batch mode.', hide: true),
+    _Option.bool('dart2js_batch', 'Run dart2js tests in batch mode.',
         hide: true),
-    new _Option.bool('write_debug_log',
+    _Option.bool('write_debug_log',
         'Don\'t write debug messages to stdout but rather to a logfile.',
         hide: true),
-    new _Option.bool(
+    _Option.bool(
         'write_results',
         'Write results to a "${TestUtils.resultsFileName}" json file '
             'located at the debug_output_directory.',
         hide: true),
-    new _Option.bool(
+    _Option.bool(
         'write_logs',
         'Include the stdout and stderr of tests that don\'t match expectations '
             'in the "${TestUtils.logsFileName}" file',
         hide: true),
-    new _Option.bool(
+    _Option.bool(
         'reset_browser_configuration',
         '''Browser specific reset of configuration.
 
 Warning: Using this option may remove your bookmarks and other
 settings.''',
         hide: true),
-    new _Option.bool(
+    _Option.bool(
         'copy_coredumps',
         '''If we see a crash that we did not expect, copy the core dumps to
 "/tmp".''',
         hide: true),
-    new _Option(
+    _Option(
         'local_ip',
         '''IP address the HTTP servers should listen on. This address is also
 used for browsers to connect to.''',
         defaultsTo: '127.0.0.1',
         hide: true),
-    new _Option.int('test_server_port', 'Port for test http server.',
+    _Option.int('test_server_port', 'Port for test http server.',
         defaultsTo: 0, hide: true),
-    new _Option.int('test_server_cross_origin_port',
+    _Option.int('test_server_cross_origin_port',
         'Port for test http server cross origin.',
         defaultsTo: 0, hide: true),
-    new _Option.int('test_driver_port', 'Port for http test driver server.',
+    _Option.int('test_driver_port', 'Port for http test driver server.',
         defaultsTo: 0, hide: true),
-    new _Option.int(
+    _Option.int(
         'test_driver_error_port', 'Port for http test driver server errors.',
         defaultsTo: 0, hide: true),
-    new _Option('test_list', 'File containing a list of tests to be executed',
+    _Option('test_list', 'File containing a list of tests to be executed',
         hide: true),
-    new _Option('tests', 'A newline separated list of tests to be executed'),
-    new _Option(
+    _Option('tests', 'A newline separated list of tests to be executed'),
+    _Option(
         'builder_tag',
         '''Machine specific options that is not captured by the regular test
 options. Used to be able to make sane updates to the status files.''',
         hide: true),
-    new _Option('vm_options', 'Extra options to send to the VM when running.',
+    _Option('vm_options', 'Extra options to send to the VM when running.',
         hide: true),
-    new _Option(
-        'dart2js_options', 'Extra options for dart2js compilation step.',
+    _Option('dart2js_options', 'Extra options for dart2js compilation step.',
         hide: true),
-    new _Option('shared_options', 'Extra shared options.', hide: true),
-    new _Option(
+    _Option('shared_options', 'Extra shared options.', hide: true),
+    _Option(
         'babel',
         '''Transforms dart2js output with Babel. The value must be
 Babel options JSON.''',
         hide: true),
-    new _Option(
-        'suite_dir', 'Additional directory to add to the testing matrix.',
+    _Option('suite_dir', 'Additional directory to add to the testing matrix.',
         hide: true),
-    new _Option('package_root', 'The package root to use for testing.',
+    _Option('package_root', 'The package root to use for testing.', hide: true),
+    _Option('packages', 'The package spec file to use for testing.',
         hide: true),
-    new _Option('packages', 'The package spec file to use for testing.',
-        hide: true),
-    new _Option(
+    _Option(
         'exclude_suite',
         '''Exclude suites from default selector, only works when no selector
 has been specified on the command line.''',
         hide: true),
-    new _Option.bool(
+    _Option.bool(
         'skip_compilation',
         '''
 Skip the compilation step, using the compilation artifacts left in
@@ -328,7 +321,7 @@
 dirty offline testing when not making changes that affect the
 compiler.''',
         hide: true),
-    new _Option.bool('print_passing_stdout',
+    _Option.bool('print_passing_stdout',
         'Print the stdout of passing, as well as failing, tests.',
         hide: true)
   ];
@@ -583,8 +576,7 @@
         // observatory_ui, and remove observatory_ui from the original
         // selectors. The only mutable value in the map is the selectors, so a
         // shallow copy is safe.
-        var observatoryConfiguration =
-            new Map<String, dynamic>.from(configuration);
+        var observatoryConfiguration = Map<String, dynamic>.from(configuration);
         var observatorySelectors = {
           'observatory_ui': selectors['observatory_ui']
         };
@@ -688,8 +680,8 @@
             var namedConfiguration =
                 getNamedConfiguration(data["named_configuration"] as String);
             var innerConfiguration = namedConfiguration ??
-                new Configuration("custom configuration", architecture,
-                    compiler, mode, runtime, system,
+                Configuration("custom configuration", architecture, compiler,
+                    mode, runtime, system,
                     timeout: data["timeout"] as int,
                     enableAsserts: data["enable_asserts"] as bool,
                     useAnalyzerCfe: data["use_cfe"] as bool,
@@ -708,7 +700,7 @@
                     babel: data['babel'] as String,
                     builderTag: data["builder_tag"] as String,
                     previewDart2: true);
-            var configuration = new TestConfiguration(
+            var configuration = TestConfiguration(
                 configuration: innerConfiguration,
                 progress: Progress.find(data["progress"] as String),
                 selectors: selectors,
@@ -817,7 +809,7 @@
         _fail("Error: '$suite/$pattern'.  Only one test selection"
             " pattern is allowed to start with '$suite/'");
       }
-      selectorMap[suite] = new RegExp(pattern);
+      selectorMap[suite] = RegExp(pattern);
     }
 
     return selectorMap;
@@ -825,7 +817,7 @@
 
   /// Print out usage information.
   void _printHelp({bool verbose}) {
-    var buffer = new StringBuffer();
+    var buffer = StringBuffer();
 
     buffer.writeln('''The Dart SDK's internal test runner.
 
diff --git a/pkg/test_runner/lib/src/output_log.dart b/pkg/test_runner/lib/src/output_log.dart
new file mode 100644
index 0000000..54d9aff
--- /dev/null
+++ b/pkg/test_runner/lib/src/output_log.dart
@@ -0,0 +1,134 @@
+// Copyright (c) 2019, 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:convert';
+import 'dart:io';
+
+/// An OutputLog records the output from a test, but truncates it if
+/// it is longer than [_maxHead] characters, and just keeps the head and
+/// the last [_tailLength] characters of the output.
+class OutputLog implements StreamConsumer<List<int>> {
+  static const _maxHead = 500 * 1024;
+  static const _tailLength = 10 * 1024;
+
+  List<int> _head = [];
+  List<int> _tail;
+  List<int> complete;
+  bool _dataDropped = false;
+  StreamSubscription _subscription;
+
+  bool _hasNonUtf8 = false;
+  bool get hasNonUtf8 => _hasNonUtf8;
+
+  void add(List<int> data) {
+    if (complete != null) {
+      throw StateError("Cannot add to OutputLog after calling toList");
+    }
+    if (_tail == null) {
+      _head.addAll(data);
+      if (_head.length > _maxHead) {
+        _tail = _head.sublist(_maxHead);
+        _head.length = _maxHead;
+      }
+    } else {
+      _tail.addAll(data);
+    }
+    if (_tail != null && _tail.length > 2 * _tailLength) {
+      _tail = _truncatedTail();
+      _dataDropped = true;
+    }
+  }
+
+  List<int> _truncatedTail() => _tail.length > _tailLength
+      ? _tail.sublist(_tail.length - _tailLength)
+      : _tail;
+
+  void _checkUtf8(List<int> data) {
+    try {
+      utf8.decode(data, allowMalformed: false);
+    } on FormatException {
+      _hasNonUtf8 = true;
+      var malformed = utf8.decode(data, allowMalformed: true);
+      data
+        ..clear()
+        ..addAll(utf8.encode(malformed))
+        ..addAll("""
+*****************************************************************************
+test.dart: The output of this test contained non-UTF8 formatted data.
+*****************************************************************************
+"""
+            .codeUnits);
+    }
+  }
+
+  List<int> toList() {
+    if (complete == null) {
+      complete = _head;
+      if (_dataDropped) {
+        complete.addAll("""
+*****************************************************************************
+test.dart: Data was removed due to excessive length. If you need the limit to
+be increased, please contact dart-engprod or file an issue.
+*****************************************************************************
+"""
+            .codeUnits);
+        complete.addAll(_truncatedTail());
+      } else if (_tail != null) {
+        complete.addAll(_tail);
+      }
+      _head = null;
+      _tail = null;
+      _checkUtf8(complete);
+    }
+    return complete;
+  }
+
+  @override
+  Future addStream(Stream<List<int>> stream) {
+    _subscription = stream.listen(this.add);
+    return _subscription.asFuture();
+  }
+
+  @override
+  Future close() {
+    toList();
+    return _subscription?.cancel();
+  }
+
+  Future cancel() {
+    return _subscription?.cancel();
+  }
+}
+
+/// An [OutputLog] that tees the output to a file as well.
+class FileOutputLog extends OutputLog {
+  File _outputFile;
+  IOSink _sink;
+
+  FileOutputLog(this._outputFile);
+
+  @override
+  void add(List<int> data) {
+    super.add(data);
+    _sink ??= _outputFile.openWrite();
+    _sink.add(data);
+  }
+
+  @override
+  Future close() {
+    return Future.wait([
+      super.close(),
+      if (_sink != null) _sink.flush().whenComplete(_sink.close)
+    ]);
+  }
+
+  @override
+  Future cancel() {
+    return Future.wait([
+      super.cancel(),
+      if (_sink != null) _sink.flush().whenComplete(_sink.close)
+    ]);
+  }
+}
diff --git a/tools/testing/dart/path.dart b/pkg/test_runner/lib/src/path.dart
similarity index 87%
rename from tools/testing/dart/path.dart
rename to pkg/test_runner/lib/src/path.dart
index 555b549..9152d14 100644
--- a/tools/testing/dart/path.dart
+++ b/pkg/test_runner/lib/src/path.dart
@@ -6,7 +6,7 @@
 
 // TODO: Remove this class, and use the URI class for all path manipulation.
 class Path {
-  static Path workingDirectory = new Path(Directory.current.path);
+  static Path workingDirectory = Path(Directory.current.path);
 
   final String _path;
   final bool isWindowsShare;
@@ -67,7 +67,7 @@
     // Throws exception if an impossible case is reached.
     if (base.isAbsolute != isAbsolute ||
         base.isWindowsShare != isWindowsShare) {
-      throw new ArgumentError("Invalid case of Path.relativeTo(base):\n"
+      throw ArgumentError("Invalid case of Path.relativeTo(base):\n"
           "  Path and base must both be relative, or both absolute.\n"
           "  Arguments: $_path.relativeTo($base)");
     }
@@ -87,22 +87,22 @@
           if (basePath[1] != _path[1]) {
             // Replace the drive letter in basePath with that from _path.
             basePath = '/${_path[1]}:/${basePath.substring(4)}';
-            base = new Path(basePath);
+            base = Path(basePath);
           }
         } else {
-          throw new ArgumentError("Invalid case of Path.relativeTo(base):\n"
+          throw ArgumentError("Invalid case of Path.relativeTo(base):\n"
               "  Base path and target path are on different Windows drives.\n"
               "  Arguments: $_path.relativeTo($base)");
         }
       } else if (baseHasDrive != pathHasDrive) {
-        throw new ArgumentError("Invalid case of Path.relativeTo(base):\n"
+        throw ArgumentError("Invalid case of Path.relativeTo(base):\n"
             "  Base path must start with a drive letter if and "
             "only if target path does.\n"
             "  Arguments: $_path.relativeTo($base)");
       }
     }
     if (_path.startsWith(basePath)) {
-      if (_path == basePath) return new Path('.');
+      if (_path == basePath) return Path('.');
       // There must be a '/' at the end of the match, or immediately after.
       int matchEnd = basePath.length;
       if (_path[matchEnd - 1] == '/' || _path[matchEnd] == '/') {
@@ -110,7 +110,7 @@
         while (matchEnd < _path.length && _path[matchEnd] == '/') {
           matchEnd++;
         }
-        return new Path(_path.substring(matchEnd)).canonicalize();
+        return Path(_path.substring(matchEnd)).canonicalize();
       }
     }
 
@@ -127,10 +127,10 @@
     while (common < length && pathSegments[common] == baseSegments[common]) {
       common++;
     }
-    final segments = new List<String>();
+    final segments = List<String>();
 
     if (common < baseSegments.length && baseSegments[common] == '..') {
-      throw new ArgumentError("Invalid case of Path.relativeTo(base):\n"
+      throw ArgumentError("Invalid case of Path.relativeTo(base):\n"
           "  Base path has more '..'s than path does.\n"
           "  Arguments: $_path.relativeTo($base)");
     }
@@ -146,22 +146,21 @@
     if (hasTrailingSeparator) {
       segments.add('');
     }
-    return new Path(segments.join('/'));
+    return Path(segments.join('/'));
   }
 
   Path join(Path further) {
     if (further.isAbsolute) {
-      throw new ArgumentError(
-          "Path.join called with absolute Path as argument.");
+      throw ArgumentError("Path.join called with absolute Path as argument.");
     }
     if (isEmpty) {
       return further.canonicalize();
     }
     if (hasTrailingSeparator) {
-      var joined = new Path._internal('$_path${further}', isWindowsShare);
+      var joined = Path._internal('$_path${further}', isWindowsShare);
       return joined.canonicalize();
     }
-    var joined = new Path._internal('$_path/${further}', isWindowsShare);
+    var joined = Path._internal('$_path/${further}', isWindowsShare);
     return joined.canonicalize();
   }
 
@@ -250,7 +249,7 @@
         segmentsToJoin.add('');
       }
     }
-    return new Path._internal(segmentsToJoin.join('/'), isWindowsShare);
+    return Path._internal(segmentsToJoin.join('/'), isWindowsShare);
   }
 
   String toNativePath() {
@@ -281,11 +280,11 @@
 
   Path append(String finalSegment) {
     if (isEmpty) {
-      return new Path._internal(finalSegment, isWindowsShare);
+      return Path._internal(finalSegment, isWindowsShare);
     } else if (hasTrailingSeparator) {
-      return new Path._internal('$_path$finalSegment', isWindowsShare);
+      return Path._internal('$_path$finalSegment', isWindowsShare);
     } else {
-      return new Path._internal('$_path/$finalSegment', isWindowsShare);
+      return Path._internal('$_path/$finalSegment', isWindowsShare);
     }
   }
 
@@ -304,10 +303,10 @@
 
   Path get directoryPath {
     int pos = _path.lastIndexOf('/');
-    if (pos < 0) return new Path('');
+    if (pos < 0) return Path('');
     while (pos > 0 && _path[pos - 1] == '/') --pos;
     var dirPath = (pos > 0) ? _path.substring(0, pos) : '/';
-    return new Path._internal(dirPath, isWindowsShare);
+    return Path._internal(dirPath, isWindowsShare);
   }
 
   String get filename {
diff --git a/pkg/test_runner/lib/src/process_queue.dart b/pkg/test_runner/lib/src/process_queue.dart
new file mode 100644
index 0000000..0d5c77c
--- /dev/null
+++ b/pkg/test_runner/lib/src/process_queue.dart
@@ -0,0 +1,1191 @@
+// Copyright (c) 2019, 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:collection';
+import 'dart:convert';
+// We need to use the 'io' prefix here, otherwise io.exitCode will shadow
+// CommandOutput.exitCode in subclasses of CommandOutput.
+import 'dart:io' as io;
+import 'dart:math' as math;
+
+import 'android.dart';
+import 'browser_controller.dart';
+import 'command.dart';
+import 'command_output.dart';
+import 'configuration.dart';
+import 'dependency_graph.dart';
+import 'output_log.dart';
+import 'runtime_configuration.dart';
+import 'test_case.dart';
+import 'test_progress.dart';
+import 'test_suite.dart';
+import 'utils.dart';
+
+const unhandledCompilerExceptionExitCode = 253;
+const parseFailExitCode = 245;
+
+const _cannotOpenDisplayMessage = 'Gtk-WARNING **: cannot open display';
+const _failedToRunCommandMessage = 'Failed to run command. return code=1';
+
+typedef _StepFunction = Future<AdbCommandResult> Function();
+
+class ProcessQueue {
+  TestConfiguration _globalConfiguration;
+
+  Function _allDone;
+  final Graph<Command> _graph = Graph();
+  List<EventListener> _eventListener;
+
+  ProcessQueue(
+      this._globalConfiguration,
+      int maxProcesses,
+      int maxBrowserProcesses,
+      DateTime startTime,
+      List<TestSuite> testSuites,
+      this._eventListener,
+      this._allDone,
+      [bool verbose = false,
+      AdbDevicePool adbDevicePool]) {
+    void setupForListing(TestCaseEnqueuer testCaseEnqueuer) {
+      _graph.sealed.listen((_) {
+        var testCases = testCaseEnqueuer.remainingTestCases.toList();
+        testCases.sort((a, b) => a.displayName.compareTo(b.displayName));
+
+        print("\nGenerating all matching test cases ....\n");
+
+        for (TestCase testCase in testCases) {
+          eventFinishedTestCase(testCase);
+          var outcomes = testCase.expectedOutcomes.map((o) => '$o').toList()
+            ..sort();
+          print("${testCase.displayName}   "
+              "Expectations: ${outcomes.join(', ')}   "
+              "Configuration: '${testCase.configurationString}'");
+        }
+        eventAllTestsKnown();
+      });
+    }
+
+    TestCaseEnqueuer testCaseEnqueuer;
+    CommandQueue commandQueue;
+
+    void setupForRunning(TestCaseEnqueuer testCaseEnqueuer) {
+      Timer _debugTimer;
+      // If we haven't seen a single test finishing during a 10 minute period
+      // something is definitely wrong, so we dump the debugging information.
+      final debugTimerDuration = const Duration(minutes: 10);
+
+      void cancelDebugTimer() {
+        if (_debugTimer != null) {
+          _debugTimer.cancel();
+        }
+      }
+
+      void resetDebugTimer() {
+        cancelDebugTimer();
+        _debugTimer = Timer(debugTimerDuration, () {
+          print("The debug timer of test.dart expired. Please report this issue"
+              " to whesse@ and provide the following information:");
+          print("");
+          print("Graph is sealed: ${_graph.isSealed}");
+          print("");
+          _graph.dumpCounts();
+          print("");
+          var unfinishedNodeStates = [
+            NodeState.initialized,
+            NodeState.waiting,
+            NodeState.enqueuing,
+            NodeState.processing
+          ];
+
+          for (var nodeState in unfinishedNodeStates) {
+            if (_graph.stateCount(nodeState) > 0) {
+              print("Commands in state '$nodeState':");
+              print("=================================");
+              print("");
+              for (var node in _graph.nodes) {
+                if (node.state == nodeState) {
+                  var command = node.data;
+                  var testCases = testCaseEnqueuer.command2testCases[command];
+                  print("  Command: $command");
+                  for (var testCase in testCases) {
+                    print("    Enqueued by: ${testCase.configurationString} "
+                        "-- ${testCase.displayName}");
+                  }
+                  print("");
+                }
+              }
+              print("");
+              print("");
+            }
+          }
+
+          if (commandQueue != null) {
+            commandQueue.dumpState();
+          }
+        });
+      }
+
+      // When the graph building is finished, notify event listeners.
+      _graph.sealed.listen((_) {
+        eventAllTestsKnown();
+      });
+
+      // Queue commands as they become "runnable"
+      CommandEnqueuer(_graph);
+
+      // CommandExecutor will execute commands
+      var executor = CommandExecutorImpl(
+          _globalConfiguration, maxProcesses, maxBrowserProcesses,
+          adbDevicePool: adbDevicePool);
+
+      // Run "runnable commands" using [executor] subject to
+      // maxProcesses/maxBrowserProcesses constraint
+      commandQueue = CommandQueue(_graph, testCaseEnqueuer, executor,
+          maxProcesses, maxBrowserProcesses, verbose);
+
+      // Finish test cases when all commands were run (or some failed)
+      var testCaseCompleter =
+          TestCaseCompleter(_graph, testCaseEnqueuer, commandQueue);
+      testCaseCompleter.finishedTestCases.listen((TestCase finishedTestCase) {
+        resetDebugTimer();
+
+        eventFinishedTestCase(finishedTestCase);
+      }, onDone: () {
+        // Wait until the commandQueue/exectutor is done (it may need to stop
+        // batch runners, browser controllers, ....)
+        commandQueue.done.then((_) {
+          cancelDebugTimer();
+          eventAllTestsDone();
+        });
+      });
+
+      resetDebugTimer();
+    }
+
+    // Build up the dependency graph
+    testCaseEnqueuer = TestCaseEnqueuer(_graph, (TestCase newTestCase) {
+      eventTestAdded(newTestCase);
+    });
+
+    // Either list or run the tests
+    if (_globalConfiguration.listTests) {
+      setupForListing(testCaseEnqueuer);
+    } else {
+      setupForRunning(testCaseEnqueuer);
+    }
+
+    // Start enqueing all TestCases
+    testCaseEnqueuer.enqueueTestSuites(testSuites);
+  }
+
+  void eventFinishedTestCase(TestCase testCase) {
+    for (var listener in _eventListener) {
+      listener.done(testCase);
+    }
+  }
+
+  void eventTestAdded(TestCase testCase) {
+    for (var listener in _eventListener) {
+      listener.testAdded();
+    }
+  }
+
+  void eventAllTestsKnown() {
+    for (var listener in _eventListener) {
+      listener.allTestsKnown();
+    }
+  }
+
+  void eventAllTestsDone() {
+    for (var listener in _eventListener) {
+      listener.allDone();
+    }
+    _allDone();
+  }
+}
+
+/// [TestCaseEnqueuer] takes a list of TestSuites, generates TestCases and
+/// builds a dependency graph of all commands in every TestSuite.
+///
+/// It maintains three helper data structures:
+///
+/// - command2node: A mapping from a [Command] to a node in the dependency
+///   graph.
+///
+/// - command2testCases: A mapping from [Command] to all TestCases that it is
+///   part of.
+///
+/// - remainingTestCases: A set of TestCases that were enqueued but are not
+///   finished.
+///
+/// [Command] and it's subclasses all have hashCode/operator== methods defined
+/// on them, so we can safely use them as keys in Map/Set objects.
+class TestCaseEnqueuer {
+  final Graph<Command> graph;
+  final Function _onTestCaseAdded;
+
+  final command2node = <Command, Node<Command>>{};
+  final command2testCases = <Command, List<TestCase>>{};
+  final remainingTestCases = <TestCase>{};
+
+  TestCaseEnqueuer(this.graph, this._onTestCaseAdded);
+
+  void enqueueTestSuites(List<TestSuite> testSuites) {
+    // Cache information about test cases per test suite. For multiple
+    // configurations there is no need to repeatedly search the file
+    // system, generate tests, and search test files for options.
+    var testCache = <String, List<TestInformation>>{};
+
+    var iterator = testSuites.iterator;
+    void enqueueNextSuite() {
+      if (!iterator.moveNext()) {
+        // We're finished with building the dependency graph.
+        graph.seal();
+      } else {
+        iterator.current.forEachTest(_newTest, testCache, enqueueNextSuite);
+      }
+    }
+
+    enqueueNextSuite();
+  }
+
+  /// Adds a test case to the list of active test cases, and adds its commands
+  /// to the dependency graph of commands.
+  ///
+  /// If the repeat flag is > 1, replicates the test case and its commands,
+  /// adding an index field with a distinct value to each of the copies.
+  ///
+  /// Each copy of the test case depends on the previous copy of the test
+  /// case completing, with its first command having a dependency on the last
+  /// command of the previous copy of the test case. This dependency is
+  /// marked as a "timingDependency", so that it doesn't depend on the previous
+  /// test completing successfully, just on it completing.
+  void _newTest(TestCase testCase) {
+    Node<Command> lastNode;
+    for (var i = 0; i < testCase.configuration.repeat; ++i) {
+      if (i > 0) {
+        testCase = testCase.indexedCopy(i);
+      }
+      remainingTestCases.add(testCase);
+      bool isFirstCommand = true;
+      for (var command in testCase.commands) {
+        // Make exactly *one* node in the dependency graph for every command.
+        // This ensures that we never have two commands c1 and c2 in the graph
+        // with "c1 == c2".
+        var node = command2node[command];
+        if (node == null) {
+          var requiredNodes =
+              (lastNode != null) ? [lastNode] : <Node<Command>>[];
+          node = graph.add(command, requiredNodes,
+              timingDependency: isFirstCommand);
+          command2node[command] = node;
+          command2testCases[command] = <TestCase>[];
+        }
+        // Keep mapping from command to all testCases that refer to it
+        command2testCases[command].add(testCase);
+
+        lastNode = node;
+        isFirstCommand = false;
+      }
+      _onTestCaseAdded(testCase);
+    }
+  }
+}
+
+/// [CommandEnqueuer] will:
+///
+/// - Change node.state to NodeState.enqueuing as soon as all dependencies have
+///   a state of NodeState.Successful.
+/// - Change node.state to NodeState.unableToRun if one or more dependencies
+///   have a state of NodeState.failed/NodeState.unableToRun.
+class CommandEnqueuer {
+  static const _initStates = [NodeState.initialized, NodeState.waiting];
+
+  static const _finishedStates = [
+    NodeState.successful,
+    NodeState.failed,
+    NodeState.unableToRun
+  ];
+
+  final Graph<Command> _graph;
+
+  CommandEnqueuer(this._graph) {
+    _graph.added.listen(_changeNodeStateIfNecessary);
+
+    _graph.changed.listen((event) {
+      if (event.from == NodeState.waiting ||
+          event.from == NodeState.processing) {
+        if (_finishedStates.contains(event.to)) {
+          for (var dependentNode in event.node.neededFor) {
+            _changeNodeStateIfNecessary(dependentNode);
+          }
+        }
+      }
+    });
+  }
+
+  /// Called when either a new node was added or if one of it's dependencies
+  /// changed it's state.
+  void _changeNodeStateIfNecessary(Node<Command> node) {
+    if (_initStates.contains(node.state)) {
+      bool allDependenciesFinished =
+          node.dependencies.every((dep) => _finishedStates.contains(dep.state));
+      bool anyDependenciesUnsuccessful = node.dependencies.any((dep) =>
+          [NodeState.failed, NodeState.unableToRun].contains(dep.state));
+      bool allDependenciesSuccessful =
+          node.dependencies.every((dep) => dep.state == NodeState.successful);
+
+      var newState = NodeState.waiting;
+      if (allDependenciesSuccessful ||
+          (allDependenciesFinished && node.timingDependency)) {
+        newState = NodeState.enqueuing;
+      } else if (anyDependenciesUnsuccessful) {
+        newState = NodeState.unableToRun;
+      }
+      if (node.state != newState) {
+        _graph.changeState(node, newState);
+      }
+    }
+  }
+}
+
+/// [CommandQueue] will listen for nodes entering the NodeState.enqueuing state,
+/// queue them up and run them. While nodes are processed they will be in the
+/// NodeState.processing state. After running a command, the node will change
+/// to a state of NodeState.Successful or NodeState.failed.
+///
+/// It provides a synchronous stream [completedCommands] which provides the
+/// [CommandOutputs] for the finished commands.
+///
+/// It provides a [done] future, which will complete once there are no more
+/// nodes left in the states Initialized/Waiting/Enqueing/Processing
+/// and the [executor] has cleaned up it's resources.
+class CommandQueue {
+  final Graph<Command> graph;
+  final CommandExecutor executor;
+  final TestCaseEnqueuer enqueuer;
+
+  final _runQueue = Queue<Command>();
+  final _commandOutputStream = StreamController<CommandOutput>(sync: true);
+  final _completer = Completer<Null>();
+
+  int _numProcesses = 0;
+  int _maxProcesses;
+  int _numBrowserProcesses = 0;
+  int _maxBrowserProcesses;
+  bool _finishing = false;
+  bool _verbose = false;
+
+  CommandQueue(this.graph, this.enqueuer, this.executor, this._maxProcesses,
+      this._maxBrowserProcesses, this._verbose) {
+    graph.changed.listen((event) {
+      if (event.to == NodeState.enqueuing) {
+        assert(event.from == NodeState.initialized ||
+            event.from == NodeState.waiting);
+        graph.changeState(event.node, NodeState.processing);
+        var command = event.node.data;
+        if (event.node.dependencies.isNotEmpty) {
+          _runQueue.addFirst(command);
+        } else {
+          _runQueue.add(command);
+        }
+        Timer.run(() => _tryRunNextCommand());
+      } else if (event.to == NodeState.unableToRun) {
+        _checkDone();
+      }
+    });
+
+    // We're finished if the graph is sealed and all nodes are in a finished
+    // state (Successful, Failed or UnableToRun).
+    // So we're calling '_checkDone()' to check whether that condition is met
+    // and we can cleanup.
+    graph.sealed.listen((event) {
+      _checkDone();
+    });
+  }
+
+  Stream<CommandOutput> get completedCommands => _commandOutputStream.stream;
+
+  Future get done => _completer.future;
+
+  void _tryRunNextCommand() {
+    _checkDone();
+
+    if (_numProcesses < _maxProcesses && !_runQueue.isEmpty) {
+      Command command = _runQueue.removeFirst();
+      var isBrowserCommand = command is BrowserTestCommand;
+
+      if (isBrowserCommand && _numBrowserProcesses == _maxBrowserProcesses) {
+        // If there is no free browser runner, put it back into the queue.
+        _runQueue.add(command);
+        // Don't lose a process.
+        Timer(Duration(milliseconds: 100), _tryRunNextCommand);
+        return;
+      }
+
+      _numProcesses++;
+      if (isBrowserCommand) _numBrowserProcesses++;
+
+      var node = enqueuer.command2node[command];
+      Iterable<TestCase> testCases = enqueuer.command2testCases[command];
+      // If a command is part of many TestCases we set the timeout to be
+      // the maximum over all [TestCase.timeout]s. At some point, we might
+      // eliminate [TestCase.timeout] completely and move it to [Command].
+      int timeout = testCases
+          .map((TestCase test) => test.timeout)
+          .fold(0, (int a, b) => math.max(a, b));
+
+      if (_verbose) {
+        print('Running "${command.displayName}" command: $command');
+      }
+
+      executor.runCommand(node, command, timeout).then((CommandOutput output) {
+        assert(command == output.command);
+
+        _commandOutputStream.add(output);
+        if (output.canRunDependendCommands) {
+          graph.changeState(node, NodeState.successful);
+        } else {
+          graph.changeState(node, NodeState.failed);
+        }
+
+        _numProcesses--;
+        if (isBrowserCommand) _numBrowserProcesses--;
+
+        // Don't lose a process
+        Timer.run(() => _tryRunNextCommand());
+      });
+    }
+  }
+
+  void _checkDone() {
+    if (!_finishing &&
+        _runQueue.isEmpty &&
+        _numProcesses == 0 &&
+        graph.isSealed &&
+        graph.stateCount(NodeState.initialized) == 0 &&
+        graph.stateCount(NodeState.waiting) == 0 &&
+        graph.stateCount(NodeState.enqueuing) == 0 &&
+        graph.stateCount(NodeState.processing) == 0) {
+      _finishing = true;
+      executor.cleanup().then((_) {
+        _completer.complete();
+        _commandOutputStream.close();
+      });
+    }
+  }
+
+  void dumpState() {
+    print("");
+    print("CommandQueue state:");
+    print("  Processes: used: $_numProcesses max: $_maxProcesses");
+    print("  BrowserProcesses: used: $_numBrowserProcesses "
+        "max: $_maxBrowserProcesses");
+    print("  Finishing: $_finishing");
+    print("  Queue (length = ${_runQueue.length}):");
+    for (var command in _runQueue) {
+      print("      $command");
+    }
+  }
+}
+
+/// [CommandExecutor] is responsible for executing commands. It will make sure
+/// that the following two constraints are satisfied
+///  - `numberOfProcessesUsed <= maxProcesses`
+///  - `numberOfBrowserProcessesUsed <= maxBrowserProcesses`
+///
+/// It provides a [runCommand] method which will complete with a
+/// [CommandOutput] object.
+///
+/// It provides a [cleanup] method to free all the allocated resources.
+abstract class CommandExecutor {
+  Future cleanup();
+  // TODO(kustermann): The [timeout] parameter should be a property of Command.
+  Future<CommandOutput> runCommand(
+      Node<Command> node, covariant Command command, int timeout);
+}
+
+class CommandExecutorImpl implements CommandExecutor {
+  final TestConfiguration globalConfiguration;
+  final int maxProcesses;
+  final int maxBrowserProcesses;
+  AdbDevicePool adbDevicePool;
+
+  /// For dart2js and analyzer batch processing,
+  /// we keep a list of batch processes.
+  final _batchProcesses = <String, List<BatchRunnerProcess>>{};
+
+  /// We keep a BrowserTestRunner for every configuration.
+  final _browserTestRunners = <TestConfiguration, BrowserTestRunner>{};
+
+  bool _finishing = false;
+
+  CommandExecutorImpl(
+      this.globalConfiguration, this.maxProcesses, this.maxBrowserProcesses,
+      {this.adbDevicePool});
+
+  Future cleanup() {
+    assert(!_finishing);
+    _finishing = true;
+
+    Future _terminateBatchRunners() {
+      var futures = <Future>[];
+      for (var runners in _batchProcesses.values) {
+        futures.addAll(runners.map((runner) => runner.terminate()));
+      }
+      return Future.wait(futures);
+    }
+
+    Future _terminateBrowserRunners() {
+      var futures =
+          _browserTestRunners.values.map((runner) => runner.terminate());
+      return Future.wait(futures);
+    }
+
+    return Future.wait([
+      _terminateBatchRunners(),
+      _terminateBrowserRunners(),
+    ]);
+  }
+
+  Future<CommandOutput> runCommand(node, Command command, int timeout) {
+    assert(!_finishing);
+
+    Future<CommandOutput> runCommand(int retriesLeft) {
+      return _runCommand(command, timeout).then((CommandOutput output) {
+        if (retriesLeft > 0 && shouldRetryCommand(output)) {
+          DebugLogger.warning("Rerunning Command: ($retriesLeft "
+              "attempt(s) remains) [cmd: $command]");
+          return runCommand(retriesLeft - 1);
+        } else {
+          return Future.value(output);
+        }
+      });
+    }
+
+    return runCommand(command.maxNumRetries);
+  }
+
+  Future<CommandOutput> _runCommand(Command command, int timeout) {
+    if (command is BrowserTestCommand) {
+      return _startBrowserControllerTest(command, timeout);
+    } else if (command is VMKernelCompilationCommand) {
+      // For now, we always run vm_compile_to_kernel in batch mode.
+      var name = command.displayName;
+      assert(name == 'vm_compile_to_kernel');
+      return _getBatchRunner(name)
+          .runCommand(name, command, timeout, command.arguments);
+    } else if (command is CompilationCommand &&
+        globalConfiguration.batchDart2JS &&
+        command.displayName == 'dart2js') {
+      return _getBatchRunner("dart2js")
+          .runCommand("dart2js", command, timeout, command.arguments);
+    } else if (command is AnalysisCommand && globalConfiguration.batch) {
+      return _getBatchRunner(command.displayName)
+          .runCommand(command.displayName, command, timeout, command.arguments);
+    } else if (command is CompilationCommand &&
+        (command.displayName == 'dartdevc' ||
+            command.displayName == 'dartdevk' ||
+            command.displayName == 'fasta') &&
+        globalConfiguration.batch) {
+      return _getBatchRunner(command.displayName)
+          .runCommand(command.displayName, command, timeout, command.arguments);
+    } else if (command is ScriptCommand) {
+      return command.run();
+    } else if (command is AdbPrecompilationCommand ||
+        command is AdbDartkCommand) {
+      assert(adbDevicePool != null);
+      return adbDevicePool.acquireDevice().then((AdbDevice device) async {
+        try {
+          if (command is AdbPrecompilationCommand) {
+            return await _runAdbPrecompilationCommand(device, command, timeout);
+          } else {
+            return await _runAdbDartkCommand(
+                device, command as AdbDartkCommand, timeout);
+          }
+        } finally {
+          await adbDevicePool.releaseDevice(device);
+        }
+      });
+    } else if (command is VmBatchCommand) {
+      var name = command.displayName;
+      return _getBatchRunner(command.displayName + command.dartFile)
+          .runCommand(name, command, timeout, command.arguments);
+    } else if (command is CompilationCommand &&
+        command.displayName == 'babel') {
+      return RunningProcess(command, timeout,
+              configuration: globalConfiguration,
+              outputFile: io.File(command.outputFile))
+          .run();
+    } else if (command is ProcessCommand) {
+      return RunningProcess(command, timeout,
+              configuration: globalConfiguration)
+          .run();
+    } else {
+      throw ArgumentError("Unknown command type ${command.runtimeType}.");
+    }
+  }
+
+  Future<CommandOutput> _runAdbPrecompilationCommand(
+      AdbDevice device, AdbPrecompilationCommand command, int timeout) async {
+    var runner = command.precompiledRunnerFilename;
+    var processTest = command.processTestFilename;
+    var testdir = command.precompiledTestDirectory;
+    var arguments = command.arguments;
+    var devicedir = DartPrecompiledAdbRuntimeConfiguration.DeviceDir;
+    var deviceTestDir = DartPrecompiledAdbRuntimeConfiguration.DeviceTestDir;
+
+    // We copy all the files which the vm precompiler puts into the test
+    // directory.
+    List<String> files = io.Directory(testdir)
+        .listSync()
+        .map((file) => file.path)
+        .map((path) => path.substring(path.lastIndexOf('/') + 1))
+        .toList();
+
+    var timeoutDuration = Duration(seconds: timeout);
+
+    var steps = <_StepFunction>[];
+
+    steps.add(() => device.runAdbShellCommand(['rm', '-Rf', deviceTestDir]));
+    steps.add(() => device.runAdbShellCommand(['mkdir', '-p', deviceTestDir]));
+    steps.add(() =>
+        device.pushCachedData(runner, '$devicedir/dart_precompiled_runtime'));
+    steps.add(
+        () => device.pushCachedData(processTest, '$devicedir/process_test'));
+    steps.add(() => device.runAdbShellCommand([
+          'chmod',
+          '777',
+          '$devicedir/dart_precompiled_runtime $devicedir/process_test'
+        ]));
+
+    for (var file in files) {
+      steps.add(() => device
+          .runAdbCommand(['push', '$testdir/$file', '$deviceTestDir/$file']));
+    }
+
+    steps.add(() => device.runAdbShellCommand(
+        [
+          '$devicedir/dart_precompiled_runtime',
+        ]..addAll(arguments),
+        timeout: timeoutDuration));
+
+    var stopwatch = Stopwatch()..start();
+    var writer = StringBuffer();
+
+    await device.waitForBootCompleted();
+    await device.waitForDevice();
+
+    AdbCommandResult result;
+    for (var i = 0; i < steps.length; i++) {
+      var fun = steps[i];
+      var commandStopwatch = Stopwatch()..start();
+      result = await fun();
+
+      writer.writeln("Executing ${result.command}");
+      if (result.stdout.isNotEmpty) {
+        writer.writeln("Stdout:\n${result.stdout.trim()}");
+      }
+      if (result.stderr.isNotEmpty) {
+        writer.writeln("Stderr:\n${result.stderr.trim()}");
+      }
+      writer.writeln("ExitCode: ${result.exitCode}");
+      writer.writeln("Time: ${commandStopwatch.elapsed}");
+      writer.writeln("");
+
+      // If one command fails, we stop processing the others and return
+      // immediately.
+      if (result.exitCode != 0) break;
+    }
+    return createCommandOutput(command, result.exitCode, result.timedOut,
+        utf8.encode('$writer'), [], stopwatch.elapsed, false);
+  }
+
+  Future<CommandOutput> _runAdbDartkCommand(
+      AdbDevice device, AdbDartkCommand command, int timeout) async {
+    final String buildPath = command.buildPath;
+    final String hostKernelFile = command.kernelFile;
+    final List<String> arguments = command.arguments;
+    final String devicedir = DartkAdbRuntimeConfiguration.DeviceDir;
+    final String deviceTestDir = DartkAdbRuntimeConfiguration.DeviceTestDir;
+
+    final timeoutDuration = Duration(seconds: timeout);
+
+    final steps = <_StepFunction>[];
+
+    steps.add(() => device.runAdbShellCommand(['rm', '-Rf', deviceTestDir]));
+    steps.add(() => device.runAdbShellCommand(['mkdir', '-p', deviceTestDir]));
+    steps.add(
+        () => device.pushCachedData("${buildPath}/dart", '$devicedir/dart'));
+    steps.add(() => device
+        .runAdbCommand(['push', hostKernelFile, '$deviceTestDir/out.dill']));
+
+    for (var lib in command.extraLibraries) {
+      var libName = "lib${lib}.so";
+      steps.add(() => device.runAdbCommand(
+          ['push', '${buildPath}/$libName', '$deviceTestDir/$libName']));
+    }
+
+    steps.add(() => device.runAdbShellCommand(
+        [
+          'export LD_LIBRARY_PATH=\$LD_LIBRARY_PATH:$deviceTestDir;'
+              '$devicedir/dart',
+        ]..addAll(arguments),
+        timeout: timeoutDuration));
+
+    var stopwatch = Stopwatch()..start();
+    var writer = StringBuffer();
+
+    await device.waitForBootCompleted();
+    await device.waitForDevice();
+
+    AdbCommandResult result;
+    for (var i = 0; i < steps.length; i++) {
+      var step = steps[i];
+      var commandStopwatch = Stopwatch()..start();
+      result = await step();
+
+      writer.writeln("Executing ${result.command}");
+      if (result.stdout.isNotEmpty) {
+        writer.writeln("Stdout:\n${result.stdout.trim()}");
+      }
+      if (result.stderr.isNotEmpty) {
+        writer.writeln("Stderr:\n${result.stderr.trim()}");
+      }
+      writer.writeln("ExitCode: ${result.exitCode}");
+      writer.writeln("Time: ${commandStopwatch.elapsed}");
+      writer.writeln("");
+
+      // If one command fails, we stop processing the others and return
+      // immediately.
+      if (result.exitCode != 0) break;
+    }
+    return createCommandOutput(command, result.exitCode, result.timedOut,
+        utf8.encode('$writer'), [], stopwatch.elapsed, false);
+  }
+
+  BatchRunnerProcess _getBatchRunner(String identifier) {
+    // Start batch processes if needed.
+    var runners = _batchProcesses[identifier];
+    if (runners == null) {
+      runners = List<BatchRunnerProcess>(maxProcesses);
+      for (int i = 0; i < maxProcesses; i++) {
+        runners[i] = BatchRunnerProcess(useJson: identifier == "fasta");
+      }
+      _batchProcesses[identifier] = runners;
+    }
+
+    for (var runner in runners) {
+      if (!runner._currentlyRunning) return runner;
+    }
+    throw Exception('Unable to find inactive batch runner.');
+  }
+
+  Future<CommandOutput> _startBrowserControllerTest(
+      BrowserTestCommand browserCommand, int timeout) {
+    var completer = Completer<CommandOutput>();
+
+    var callback = (BrowserTestOutput output) {
+      completer.complete(BrowserCommandOutput(browserCommand, output));
+    };
+
+    var browserTest = BrowserTest(browserCommand.url, callback, timeout);
+    _getBrowserTestRunner(browserCommand.configuration).then((testRunner) {
+      testRunner.enqueueTest(browserTest);
+    });
+
+    return completer.future;
+  }
+
+  Future<BrowserTestRunner> _getBrowserTestRunner(
+      TestConfiguration configuration) async {
+    if (_browserTestRunners[configuration] == null) {
+      var testRunner = BrowserTestRunner(
+          configuration, globalConfiguration.localIP, maxBrowserProcesses);
+      if (globalConfiguration.isVerbose) {
+        testRunner.logger = DebugLogger.info;
+      }
+      _browserTestRunners[configuration] = testRunner;
+      await testRunner.start();
+    }
+    return _browserTestRunners[configuration];
+  }
+}
+
+bool shouldRetryCommand(CommandOutput output) {
+  if (!output.successful) {
+    List<String> stdout, stderr;
+
+    decodeOutput() {
+      if (stdout == null && stderr == null) {
+        stdout = decodeUtf8(output.stderr).split("\n");
+        stderr = decodeUtf8(output.stderr).split("\n");
+      }
+    }
+
+    final command = output.command;
+
+    // The dartk batch compiler sometimes runs out of memory. In such a case we
+    // will retry running it.
+    if (command is VMKernelCompilationCommand) {
+      if (output.hasCrashed) {
+        bool containsOutOfMemoryMessage(String line) {
+          return line.contains('Exhausted heap space, trying to allocat');
+        }
+
+        decodeOutput();
+        if (stdout.any(containsOutOfMemoryMessage) ||
+            stderr.any(containsOutOfMemoryMessage)) {
+          return true;
+        }
+      }
+    }
+
+    if (io.Platform.operatingSystem == 'linux') {
+      decodeOutput();
+      // No matter which command we ran: If we get failures due to the
+      // "xvfb-run" issue 7564, try re-running the test.
+      bool containsFailureMsg(String line) {
+        return line.contains(_cannotOpenDisplayMessage) ||
+            line.contains(_failedToRunCommandMessage);
+      }
+
+      if (stdout.any(containsFailureMsg) || stderr.any(containsFailureMsg)) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+/// [TestCaseCompleter] will listen for
+/// NodeState.processing -> NodeState.{successful,failed} state changes and
+/// will complete a TestCase if it is finished.
+///
+/// It provides a stream [finishedTestCases], which will stream all TestCases
+/// once they're finished. After all TestCases are done, the stream will be
+/// closed.
+class TestCaseCompleter {
+  static const _completedStates = [NodeState.failed, NodeState.successful];
+
+  final Graph<Command> _graph;
+  final TestCaseEnqueuer _enqueuer;
+  final CommandQueue _commandQueue;
+
+  final Map<Command, CommandOutput> _outputs = {};
+  final StreamController<TestCase> _controller = StreamController();
+  bool _closed = false;
+
+  TestCaseCompleter(this._graph, this._enqueuer, this._commandQueue) {
+    var finishedRemainingTestCases = false;
+
+    // Store all the command outputs -- they will be delivered synchronously
+    // (i.e. before state changes in the graph)
+    _commandQueue.completedCommands.listen((CommandOutput output) {
+      _outputs[output.command] = output;
+    }, onDone: () {
+      _completeTestCasesIfPossible(List.from(_enqueuer.remainingTestCases));
+      finishedRemainingTestCases = true;
+      assert(_enqueuer.remainingTestCases.isEmpty);
+      _checkDone();
+    });
+
+    // Listen for NodeState.Processing -> NodeState.{Successful,Failed}
+    // changes.
+    _graph.changed.listen((event) {
+      if (event.from == NodeState.processing && !finishedRemainingTestCases) {
+        var command = event.node.data;
+
+        assert(_completedStates.contains(event.to));
+        assert(_outputs[command] != null);
+
+        _completeTestCasesIfPossible(_enqueuer.command2testCases[command]);
+        _checkDone();
+      }
+    });
+
+    // Listen also for GraphSealedEvents. If there is not a single node in the
+    // graph, we still want to finish after the graph was sealed.
+    _graph.sealed.listen((_) {
+      if (!_closed && _enqueuer.remainingTestCases.isEmpty) {
+        _controller.close();
+        _closed = true;
+      }
+    });
+  }
+
+  Stream<TestCase> get finishedTestCases => _controller.stream;
+
+  void _checkDone() {
+    if (!_closed && _graph.isSealed && _enqueuer.remainingTestCases.isEmpty) {
+      _controller.close();
+      _closed = true;
+    }
+  }
+
+  void _completeTestCasesIfPossible(Iterable<TestCase> testCases) {
+    // Update TestCases with command outputs.
+    for (TestCase test in testCases) {
+      for (var icommand in test.commands) {
+        var output = _outputs[icommand];
+        if (output != null) {
+          test.commandOutputs[icommand] = output;
+        }
+      }
+    }
+
+    void completeTestCase(TestCase testCase) {
+      if (_enqueuer.remainingTestCases.contains(testCase)) {
+        _controller.add(testCase);
+        _enqueuer.remainingTestCases.remove(testCase);
+      } else {
+        DebugLogger.error("${testCase.displayName} would be finished twice");
+      }
+    }
+
+    for (var testCase in testCases) {
+      // Ask the [testCase] if it's done. Note that we assume, that
+      // [TestCase.isFinished] will return true if all commands were executed
+      // or if a previous one failed.
+      if (testCase.isFinished) {
+        completeTestCase(testCase);
+      }
+    }
+  }
+}
+
+class BatchRunnerProcess {
+  /// When true, the command line is passed to the test runner as a
+  /// JSON-encoded list of strings.
+  final bool _useJson;
+
+  Completer<CommandOutput> _completer;
+  ProcessCommand _command;
+  List<String> _arguments;
+  String _runnerType;
+
+  io.Process _process;
+  Map<String, String> _processEnvironmentOverrides;
+  Completer<Null> _stdoutCompleter;
+  Completer<Null> _stderrCompleter;
+  StreamSubscription<String> _stdoutSubscription;
+  StreamSubscription<String> _stderrSubscription;
+  Function _processExitHandler;
+
+  bool _currentlyRunning = false;
+  OutputLog _testStdout;
+  OutputLog _testStderr;
+  String _status;
+  DateTime _startTime;
+  Timer _timer;
+  int _testCount = 0;
+
+  BatchRunnerProcess({bool useJson = true}) : _useJson = useJson;
+
+  Future<CommandOutput> runCommand(String runnerType, ProcessCommand command,
+      int timeout, List<String> arguments) {
+    assert(_completer == null);
+    assert(!_currentlyRunning);
+
+    _completer = Completer();
+    bool sameRunnerType = _runnerType == runnerType &&
+        _dictEquals(_processEnvironmentOverrides, command.environmentOverrides);
+    _runnerType = runnerType;
+    _currentlyRunning = true;
+    _command = command;
+    _arguments = arguments;
+    _processEnvironmentOverrides = command.environmentOverrides;
+
+    // TOOD(jmesserly): this restarts `dartdevc --batch` to work around a
+    // memory leak, see https://github.com/dart-lang/sdk/issues/30314.
+    var clearMemoryLeak = command is CompilationCommand &&
+        command.displayName == 'dartdevc' &&
+        ++_testCount % 100 == 0;
+    if (_process == null) {
+      // Start process if not yet started.
+      _startProcess(() {
+        doStartTest(command, timeout);
+      });
+    } else if (!sameRunnerType || clearMemoryLeak) {
+      // Restart this runner with the right executable for this test if needed.
+      _processExitHandler = (_) {
+        _startProcess(() {
+          doStartTest(command, timeout);
+        });
+      };
+      _process.kill();
+      _stdoutSubscription.cancel();
+      _stderrSubscription.cancel();
+    } else {
+      doStartTest(command, timeout);
+    }
+    return _completer.future;
+  }
+
+  Future<bool> terminate() {
+    if (_process == null) return Future.value(true);
+    var terminateCompleter = Completer<bool>();
+    _processExitHandler = (_) {
+      terminateCompleter.complete(true);
+    };
+    _process.kill();
+    _stdoutSubscription.cancel();
+    _stderrSubscription.cancel();
+
+    return terminateCompleter.future;
+  }
+
+  void doStartTest(Command command, int timeout) {
+    _startTime = DateTime.now();
+    _testStdout = OutputLog();
+    _testStderr = OutputLog();
+    _status = null;
+    _stdoutCompleter = Completer();
+    _stderrCompleter = Completer();
+    _timer = Timer(Duration(seconds: timeout), _timeoutHandler);
+
+    var line = _createArgumentsLine(_arguments, timeout);
+    _process.stdin.write(line);
+    _stdoutSubscription.resume();
+    _stderrSubscription.resume();
+    Future.wait([_stdoutCompleter.future, _stderrCompleter.future])
+        .then((_) => _reportResult());
+  }
+
+  String _createArgumentsLine(List<String> arguments, int timeout) {
+    if (_useJson) {
+      return "${jsonEncode(arguments)}\n";
+    } else {
+      return arguments.join(' ') + '\n';
+    }
+  }
+
+  void _reportResult() {
+    if (!_currentlyRunning) return;
+
+    var outcome = _status.split(" ")[2];
+    var exitCode = 0;
+    if (outcome == "CRASH") exitCode = unhandledCompilerExceptionExitCode;
+    if (outcome == "PARSE_FAIL") exitCode = parseFailExitCode;
+    if (outcome == "FAIL" || outcome == "TIMEOUT") exitCode = 1;
+    var output = createCommandOutput(
+        _command,
+        exitCode,
+        (outcome == "TIMEOUT"),
+        _testStdout.toList(),
+        _testStderr.toList(),
+        DateTime.now().difference(_startTime),
+        false);
+    assert(_completer != null);
+    _completer.complete(output);
+    _completer = null;
+    _currentlyRunning = false;
+  }
+
+  void Function(int) makeExitHandler(String status) {
+    return (int exitCode) {
+      if (_currentlyRunning) {
+        if (_timer != null) _timer.cancel();
+        _status = status;
+        _stdoutSubscription.cancel();
+        _stderrSubscription.cancel();
+        _startProcess(_reportResult);
+      } else {
+        // No active test case running.
+        _process = null;
+      }
+    };
+  }
+
+  void _timeoutHandler() {
+    _processExitHandler = makeExitHandler(">>> TEST TIMEOUT");
+    _process.kill();
+  }
+
+  void _startProcess(void Function() callback) {
+    assert(_command is ProcessCommand);
+    var executable = _command.executable;
+    var arguments = _command.batchArguments.toList();
+    arguments.add('--batch');
+    var environment = Map<String, String>.from(io.Platform.environment);
+    if (_processEnvironmentOverrides != null) {
+      for (var key in _processEnvironmentOverrides.keys) {
+        environment[key] = _processEnvironmentOverrides[key];
+      }
+    }
+    var processFuture =
+        io.Process.start(executable, arguments, environment: environment);
+    processFuture.then((io.Process p) {
+      _process = p;
+
+      Stream<String> _stdoutStream =
+          _process.stdout.transform(utf8.decoder).transform(LineSplitter());
+      _stdoutSubscription = _stdoutStream.listen((String line) {
+        if (line.startsWith('>>> TEST')) {
+          _status = line;
+        } else if (line.startsWith('>>> BATCH')) {
+          // ignore
+        } else if (line.startsWith('>>> ')) {
+          throw Exception("Unexpected command from batch runner: '$line'.");
+        } else {
+          _testStdout.add(encodeUtf8(line));
+          _testStdout.add("\n".codeUnits);
+        }
+        if (_status != null) {
+          _stdoutSubscription.pause();
+          _timer.cancel();
+          _stdoutCompleter.complete(null);
+        }
+      });
+      _stdoutSubscription.pause();
+
+      Stream<String> _stderrStream =
+          _process.stderr.transform(utf8.decoder).transform(LineSplitter());
+      _stderrSubscription = _stderrStream.listen((String line) {
+        if (line.startsWith('>>> EOF STDERR')) {
+          _stderrSubscription.pause();
+          _stderrCompleter.complete(null);
+        } else {
+          _testStderr.add(encodeUtf8(line));
+          _testStderr.add("\n".codeUnits);
+        }
+      });
+      _stderrSubscription.pause();
+
+      _processExitHandler = makeExitHandler(">>> TEST CRASH");
+      _process.exitCode.then((exitCode) {
+        _processExitHandler(exitCode);
+      });
+
+      _process.stdin.done.catchError((err) {
+        print('Error on batch runner input stream stdin');
+        print('  Previous test\'s status: $_status');
+        print('  Error: $err');
+        throw err;
+      });
+      callback();
+    }).catchError((e) {
+      // TODO(floitsch): should we try to report the stacktrace?
+      print("Process error:");
+      print("  Command: $executable ${arguments.join(' ')} ($_arguments)");
+      print("  Error: $e");
+      // If there is an error starting a batch process, chances are that
+      // it will always fail. So rather than re-trying a 1000+ times, we
+      // exit.
+      io.exit(1);
+      return true;
+    });
+  }
+
+  bool _dictEquals(Map a, Map b) {
+    if (a == null) return b == null;
+    if (b == null) return false;
+    if (a.length != b.length) return false;
+    for (var key in a.keys) {
+      if (a[key] != b[key]) return false;
+    }
+    return true;
+  }
+}
diff --git a/tools/testing/dart/repository.dart b/pkg/test_runner/lib/src/repository.dart
similarity index 92%
rename from tools/testing/dart/repository.dart
rename to pkg/test_runner/lib/src/repository.dart
index 18ea378..79687fb 100644
--- a/tools/testing/dart/repository.dart
+++ b/pkg/test_runner/lib/src/repository.dart
@@ -8,7 +8,7 @@
 /// Provides information about the surrounding Dart repository.
 class Repository {
   /// File path pointing to the root directory of the Dart checkout.
-  static Path get dir => new Path(uri.toFilePath());
+  static Path get dir => Path(uri.toFilePath());
 
   /// The URI pointing to the root of the Dart checkout.
   ///
diff --git a/tools/testing/dart/reset_safari.dart b/pkg/test_runner/lib/src/reset_safari.dart
similarity index 89%
rename from tools/testing/dart/reset_safari.dart
rename to pkg/test_runner/lib/src/reset_safari.dart
index 07cefdf..3084518 100644
--- a/tools/testing/dart/reset_safari.dart
+++ b/pkg/test_runner/lib/src/reset_safari.dart
@@ -34,7 +34,7 @@
   "Library/Preferences/$safari.plist",
 ];
 
-const Duration defaultPollDelay = const Duration(milliseconds: 1);
+const Duration defaultPollDelay = Duration(milliseconds: 1);
 
 final String cpgi = "$safari.ContentPageGroupIdentifier";
 
@@ -97,12 +97,12 @@
 }
 ''';
 
-Future<Null> get pollDelay => new Future.delayed(defaultPollDelay);
+Future<Null> get pollDelay => Future.delayed(defaultPollDelay);
 
 String signalArgument(String defaultSignal,
-    {bool force: false, bool testOnly: false}) {
+    {bool force = false, bool testOnly = false}) {
   if (force && testOnly) {
-    throw new ArgumentError("[force] and [testOnly] can't both be true.");
+    throw ArgumentError("[force] and [testOnly] can't both be true.");
   }
   if (force) return "-KILL";
   if (testOnly) return "-0";
@@ -110,7 +110,7 @@
 }
 
 Future<int> kill(List<String> pids,
-    {bool force: false, bool testOnly: false}) async {
+    {bool force = false, bool testOnly = false}) async {
   var arguments = [signalArgument("-TERM", force: force, testOnly: testOnly)]
     ..addAll(pids);
   var result = await Process.run(killLocation, arguments);
@@ -118,7 +118,7 @@
 }
 
 Future<int> pkill(String pattern,
-    {bool force: false, bool testOnly: false}) async {
+    {bool force = false, bool testOnly = false}) async {
   var arguments = [
     signalArgument("-HUP", force: force, testOnly: testOnly),
     pattern
@@ -130,7 +130,7 @@
 Uri validatedBundleName(Uri bundle) {
   if (bundle == null) return Uri.base.resolve(defaultSafariBundleLocation);
   if (!bundle.path.endsWith("/")) {
-    throw new ArgumentError("Bundle ('$bundle') must end with a slash ('/').");
+    throw ArgumentError("Bundle ('$bundle') must end with a slash ('/').");
   }
   return bundle;
 }
@@ -144,7 +144,7 @@
     var stdout = result.stdout as String;
     var pids =
         stdout.split("\n").where((String line) => !line.isEmpty).toList();
-    var timer = new Timer(const Duration(seconds: 10), () {
+    var timer = Timer(const Duration(seconds: 10), () {
       print("Kill -9 Safari $pids");
       kill(pids, force: true);
     });
@@ -156,7 +156,7 @@
     }
     timer.cancel();
   }
-  var timer = new Timer(const Duration(seconds: 10), () {
+  var timer = Timer(const Duration(seconds: 10), () {
     print("Kill -9 $safari");
     pkill(safari, force: true);
   });
@@ -170,12 +170,12 @@
 }
 
 Future<Null> deleteIfExists(Uri uri) async {
-  var directory = new Directory.fromUri(uri);
+  var directory = Directory.fromUri(uri);
   if (await directory.exists()) {
     print("Deleting directory '$uri'.");
     await directory.delete(recursive: true);
   } else {
-    var file = new File.fromUri(uri);
+    var file = File.fromUri(uri);
     if (await file.exists()) {
       print("Deleting file '$uri'.");
       await file.delete();
diff --git a/tools/testing/dart/runtime_configuration.dart b/pkg/test_runner/lib/src/runtime_configuration.dart
similarity index 94%
rename from tools/testing/dart/runtime_configuration.dart
rename to pkg/test_runner/lib/src/runtime_configuration.dart
index 38c10b1..16690f8 100644
--- a/tools/testing/dart/runtime_configuration.dart
+++ b/pkg/test_runner/lib/src/runtime_configuration.dart
@@ -28,31 +28,31 @@
       case Runtime.ie9:
       case Runtime.safari:
         // TODO(ahe): Replace this with one or more browser runtimes.
-        return new DummyRuntimeConfiguration();
+        return DummyRuntimeConfiguration();
 
       case Runtime.jsshell:
-        return new JsshellRuntimeConfiguration();
+        return JsshellRuntimeConfiguration();
 
       case Runtime.d8:
-        return new D8RuntimeConfiguration();
+        return D8RuntimeConfiguration();
 
       case Runtime.none:
-        return new NoneRuntimeConfiguration();
+        return NoneRuntimeConfiguration();
 
       case Runtime.vm:
         if (configuration.system == System.android) {
-          return new DartkAdbRuntimeConfiguration();
+          return DartkAdbRuntimeConfiguration();
         }
-        return new StandaloneDartRuntimeConfiguration();
+        return StandaloneDartRuntimeConfiguration();
 
       case Runtime.dartPrecompiled:
         if (configuration.system == System.android) {
-          return new DartPrecompiledAdbRuntimeConfiguration(
+          return DartPrecompiledAdbRuntimeConfiguration(
             useBlobs: configuration.useBlobs,
             useElf: configuration.useElf,
           );
         } else {
-          return new DartPrecompiledRuntimeConfiguration(
+          return DartPrecompiledRuntimeConfiguration(
             useBlobs: configuration.useBlobs,
             useElf: configuration.useElf,
           );
@@ -60,7 +60,7 @@
         break;
 
       case Runtime.selfCheck:
-        return new SelfCheckRuntimeConfiguration();
+        return SelfCheckRuntimeConfiguration();
     }
     throw "unreachable";
   }
@@ -73,8 +73,8 @@
 
   int timeoutMultiplier(
       {Mode mode,
-      bool isChecked: false,
-      bool isReload: false,
+      bool isChecked = false,
+      bool isReload = false,
       Architecture arch}) {
     return 1;
   }
@@ -89,9 +89,7 @@
     throw "Unimplemented runtime '$runtimeType'";
   }
 
-  /**
-   * The output directory for this suite's configuration.
-   */
+  /// The output directory for this suite's configuration.
   String get buildDir => _configuration.buildDirectory;
 
   List<String> dart2jsPreambles(Uri preambleDir) => [];
@@ -237,8 +235,8 @@
 
   int timeoutMultiplier(
       {Mode mode,
-      bool isChecked: false,
-      bool isReload: false,
+      bool isChecked = false,
+      bool isReload = false,
       Architecture arch}) {
     var multiplier = 1;
 
@@ -389,7 +387,7 @@
 
   void searchForSelfCheckers() {
     Uri pkg = Repository.uri.resolve('pkg');
-    for (var entry in new Directory.fromUri(pkg).listSync(recursive: true)) {
+    for (var entry in Directory.fromUri(pkg).listSync(recursive: true)) {
       if (entry is File && entry.path.endsWith('_self_check.dart')) {
         selfCheckers.add(entry.path);
       }
diff --git a/tools/testing/dart/status_reporter.dart b/pkg/test_runner/lib/src/status_reporter.dart
similarity index 93%
rename from tools/testing/dart/status_reporter.dart
rename to pkg/test_runner/lib/src/status_reporter.dart
index 1a0d7ae..b8b4d78 100644
--- a/tools/testing/dart/status_reporter.dart
+++ b/pkg/test_runner/lib/src/status_reporter.dart
@@ -84,13 +84,13 @@
   if (result.exitCode != 0) {
     print('ERROR');
     print(result.stderr);
-    throw new Exception('Error while building.');
+    throw Exception('Error while building.');
   }
   print('Done building.');
 }
 
 void sanityCheck(String output) {
-  var splitter = new LineSplitter();
+  var splitter = LineSplitter();
   var lines = splitter.convert(output);
   // Looks like this:
   // Total: 15556 tests
@@ -104,16 +104,15 @@
   }
   if (count != total) {
     print('Count: $count, total: $total');
-    throw new Exception(
-        'Count and total do not align. Please validate manually.');
+    throw Exception('Count and total do not align. Please validate manually.');
   }
 }
 
 void main(List<String> args) {
   var combinations = _combinations[Platform.operatingSystem];
 
-  var arches = new Set<String>();
-  var modes = new Set<String>();
+  var arches = <String>{};
+  var modes = <String>{};
 
   if (args.contains('--simple')) {
     arches = ['ia32'].toSet();
@@ -155,7 +154,7 @@
           if (result.exitCode != 0) {
             print(result.stdout);
             print(result.stderr);
-            throw new Exception("Error running: ${args.join(" ")}");
+            throw Exception("Error running: ${args.join(" ")}");
           }
 
           // Find "JSON:"
diff --git a/tools/testing/dart/summary_report.dart b/pkg/test_runner/lib/src/summary_report.dart
similarity index 97%
rename from tools/testing/dart/summary_report.dart
rename to pkg/test_runner/lib/src/summary_report.dart
index a87f174..6db17f7 100644
--- a/tools/testing/dart/summary_report.dart
+++ b/pkg/test_runner/lib/src/summary_report.dart
@@ -6,9 +6,9 @@
 
 import "package:status_file/expectation.dart";
 
-import "test_runner.dart";
+import "test_case.dart";
 
-final summaryReport = new SummaryReport();
+final summaryReport = SummaryReport();
 
 class SummaryReport {
   int _total = 0;
diff --git a/pkg/test_runner/lib/src/test_case.dart b/pkg/test_runner/lib/src/test_case.dart
new file mode 100644
index 0000000..6d1aa4e
--- /dev/null
+++ b/pkg/test_runner/lib/src/test_case.dart
@@ -0,0 +1,440 @@
+// Copyright (c) 2019, 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';
+// We need to use the 'io' prefix here, otherwise io.exitCode will shadow
+// CommandOutput.exitCode in subclasses of CommandOutput.
+import 'dart:io' as io;
+
+import "package:status_file/expectation.dart";
+
+import 'command.dart';
+import 'command_output.dart';
+import 'configuration.dart';
+import 'output_log.dart';
+import 'process_queue.dart';
+import 'repository.dart';
+import 'test_suite.dart';
+import 'utils.dart';
+
+const _slowTimeoutMultiplier = 4;
+const _extraSlowTimeoutMultiplier = 8;
+const nonUtfFakeExitCode = 0xFFFD;
+
+/// Some IO tests use these variables and get confused if the host environment
+/// variables are inherited so they are excluded.
+const _excludedEnvironmentVariables = [
+  'http_proxy',
+  'https_proxy',
+  'no_proxy',
+  'HTTP_PROXY',
+  'HTTPS_PROXY',
+  'NO_PROXY'
+];
+
+/// TestCase contains all the information needed to run a test and evaluate
+/// its output.  Running a test involves starting a separate process, with
+/// the executable and arguments given by the TestCase, and recording its
+/// stdout and stderr output streams, and its exit code.  TestCase only
+/// contains static information about the test; actually running the test is
+/// performed by [ProcessQueue] using a [RunningProcess] object.
+///
+/// The output information is stored in a [CommandOutput] instance contained
+/// in TestCase.commandOutputs. The last CommandOutput instance is responsible
+/// for evaluating if the test has passed, failed, crashed, or timed out, and
+/// the TestCase has information about what the expected result of the test
+/// should be.
+///
+/// The TestCase has a callback function, [completedHandler], that is run when
+/// the test is completed.
+class TestCase extends UniqueObject {
+  /// Flags set in _expectations from the optional argument info.
+  static const _hasRuntimeError = 1 << 0;
+  static const _hasSyntaxError = 1 << 1;
+  static const _hasCompileError = 1 << 2;
+  static const _hasStaticWarning = 1 << 3;
+  static const _hasCrash = 1 << 4;
+
+  /// A list of commands to execute. Most test cases have a single command.
+  /// Dart2js tests have two commands, one to compile the source and another
+  /// to execute it. Some isolate tests might even have three, if they require
+  /// compiling multiple sources that are run in isolation.
+  List<Command> commands;
+  Map<Command, CommandOutput> commandOutputs = {};
+
+  TestConfiguration configuration;
+  String displayName;
+  int _expectations = 0;
+  int hash = 0;
+  Set<Expectation> expectedOutcomes;
+
+  TestCase(this.displayName, this.commands, this.configuration,
+      this.expectedOutcomes,
+      {TestInformation info}) {
+    // A test case should do something.
+    assert(commands.isNotEmpty);
+
+    if (info != null) {
+      _setExpectations(info);
+      hash = (info?.originTestPath?.relativeTo(Repository.dir)?.toString())
+          .hashCode;
+    }
+  }
+
+  void _setExpectations(TestInformation info) {
+    // We don't want to keep the entire (large) TestInformation structure,
+    // so we copy the needed bools into flags set in a single integer.
+    if (info.hasRuntimeError) _expectations |= _hasRuntimeError;
+    if (info.hasSyntaxError) _expectations |= _hasSyntaxError;
+    if (info.hasCrash) _expectations |= _hasCrash;
+    if (info.hasCompileError || info.hasSyntaxError) {
+      _expectations |= _hasCompileError;
+    }
+    if (info.hasStaticWarning) _expectations |= _hasStaticWarning;
+  }
+
+  TestCase indexedCopy(int index) {
+    var newCommands = commands.map((c) => c.indexedCopy(index)).toList();
+    return TestCase(displayName, newCommands, configuration, expectedOutcomes)
+      .._expectations = _expectations
+      ..hash = hash;
+  }
+
+  bool get hasRuntimeError => _expectations & _hasRuntimeError != 0;
+  bool get hasStaticWarning => _expectations & _hasStaticWarning != 0;
+  bool get hasSyntaxError => _expectations & _hasSyntaxError != 0;
+  bool get hasCompileError => _expectations & _hasCompileError != 0;
+  bool get hasCrash => _expectations & _hasCrash != 0;
+  bool get isNegative =>
+      hasCompileError ||
+      hasRuntimeError && configuration.runtime != Runtime.none ||
+      displayName.contains("negative_test");
+
+  bool get unexpectedOutput {
+    var outcome = this.result;
+    return !expectedOutcomes.any((expectation) {
+      return outcome.canBeOutcomeOf(expectation);
+    });
+  }
+
+  Expectation get result => lastCommandOutput.result(this);
+  Expectation get realResult => lastCommandOutput.realResult(this);
+  Expectation get realExpected {
+    if (hasCrash) {
+      return Expectation.crash;
+    }
+    if (configuration.compiler == Compiler.specParser) {
+      if (hasSyntaxError) {
+        return Expectation.syntaxError;
+      }
+    } else if (hasCompileError) {
+      if (hasRuntimeError && configuration.runtime != Runtime.none) {
+        return Expectation.fail;
+      }
+      return Expectation.compileTimeError;
+    }
+    if (hasRuntimeError) {
+      if (configuration.runtime != Runtime.none) {
+        return Expectation.runtimeError;
+      }
+      return Expectation.pass;
+    }
+    if (displayName.contains("negative_test")) {
+      return Expectation.fail;
+    }
+    if (configuration.compiler == Compiler.dart2analyzer && hasStaticWarning) {
+      return Expectation.staticWarning;
+    }
+    return Expectation.pass;
+  }
+
+  CommandOutput get lastCommandOutput {
+    if (commandOutputs.length == 0) {
+      throw Exception("CommandOutputs is empty, maybe no command was run? ("
+          "displayName: '$displayName', "
+          "configurationString: '$configurationString')");
+    }
+    return commandOutputs[commands[commandOutputs.length - 1]];
+  }
+
+  Command get lastCommandExecuted {
+    if (commandOutputs.length == 0) {
+      throw Exception("CommandOutputs is empty, maybe no command was run? ("
+          "displayName: '$displayName', "
+          "configurationString: '$configurationString')");
+    }
+    return commands[commandOutputs.length - 1];
+  }
+
+  int get timeout {
+    var result = configuration.timeout;
+    if (expectedOutcomes.contains(Expectation.slow)) {
+      result *= _slowTimeoutMultiplier;
+    } else if (expectedOutcomes.contains(Expectation.extraSlow)) {
+      result *= _extraSlowTimeoutMultiplier;
+    }
+    return result;
+  }
+
+  String get configurationString {
+    var compiler = configuration.compiler.name;
+    var runtime = configuration.runtime.name;
+    var mode = configuration.mode.name;
+    var arch = configuration.architecture.name;
+    var checked = configuration.isChecked ? '-checked' : '';
+    return "$compiler-$runtime$checked ${mode}_$arch";
+  }
+
+  List<String> get batchTestArguments {
+    assert(commands.last is ProcessCommand);
+    return (commands.last as ProcessCommand).arguments;
+  }
+
+  bool get isFlaky {
+    if (expectedOutcomes.contains(Expectation.skip) ||
+        expectedOutcomes.contains(Expectation.skipByDesign)) {
+      return false;
+    }
+
+    return expectedOutcomes
+            .where((expectation) => expectation.isOutcome)
+            .length >
+        1;
+  }
+
+  bool get isFinished {
+    return commandOutputs.isNotEmpty &&
+        (!lastCommandOutput.successful ||
+            commands.length == commandOutputs.length);
+  }
+}
+
+/// Helper to get a list of all child pids for a parent process.
+Future<List<int>> _getPidList(int parentId, List<String> diagnostics) async {
+  var pids = [parentId];
+  List<String> lines;
+  var startLine = 0;
+  if (io.Platform.isLinux || io.Platform.isMacOS) {
+    var result =
+        await io.Process.run("pgrep", ["-P", "${pids[0]}"], runInShell: true);
+    lines = (result.stdout as String).split('\n');
+  } else if (io.Platform.isWindows) {
+    var result = await io.Process.run(
+        "wmic",
+        [
+          "process",
+          "where",
+          "(ParentProcessId=${pids[0]})",
+          "get",
+          "ProcessId"
+        ],
+        runInShell: true);
+    lines = (result.stdout as String).split('\n');
+    // Skip first line containing header "ProcessId".
+    startLine = 1;
+  } else {
+    assert(false);
+  }
+  if (lines.length > startLine) {
+    for (var i = startLine; i < lines.length; ++i) {
+      var pid = int.tryParse(lines[i]);
+      if (pid != null) pids.add(pid);
+    }
+  } else {
+    diagnostics.add("Could not find child pids");
+    diagnostics.addAll(lines);
+  }
+  return pids;
+}
+
+/// A RunningProcess actually runs a test, getting the command lines from
+/// its [TestCase], starting the test process (and first, a compilation
+/// process if the TestCase is a [BrowserTestCase]), creating a timeout
+/// timer, and recording the results in a new [CommandOutput] object, which it
+/// attaches to the TestCase.  The lifetime of the RunningProcess is limited
+/// to the time it takes to start the process, run the process, and record
+/// the result; there are no pointers to it, so it should be available to
+/// be garbage collected as soon as it is done.
+class RunningProcess {
+  ProcessCommand command;
+  int timeout;
+  bool timedOut = false;
+  DateTime startTime;
+  int pid;
+  OutputLog stdout;
+  OutputLog stderr = OutputLog();
+  List<String> diagnostics = <String>[];
+  bool compilationSkipped = false;
+  Completer<CommandOutput> completer;
+  TestConfiguration configuration;
+
+  RunningProcess(this.command, this.timeout,
+      {this.configuration, io.File outputFile}) {
+    stdout = outputFile != null ? FileOutputLog(outputFile) : OutputLog();
+  }
+
+  Future<CommandOutput> run() {
+    completer = Completer<CommandOutput>();
+    startTime = DateTime.now();
+    _runCommand();
+    return completer.future;
+  }
+
+  void _runCommand() {
+    if (command.outputIsUpToDate) {
+      compilationSkipped = true;
+      _commandComplete(0);
+    } else {
+      var processEnvironment = _createProcessEnvironment();
+      var args = command.arguments;
+      var processFuture = io.Process.start(command.executable, args,
+          environment: processEnvironment,
+          workingDirectory: command.workingDirectory);
+      processFuture.then((io.Process process) {
+        var stdoutFuture = process.stdout.pipe(stdout);
+        var stderrFuture = process.stderr.pipe(stderr);
+        pid = process.pid;
+
+        // Close stdin so that tests that try to block on input will fail.
+        process.stdin.close();
+        timeoutHandler() async {
+          timedOut = true;
+          if (process != null) {
+            String executable;
+            if (io.Platform.isLinux) {
+              executable = 'eu-stack';
+            } else if (io.Platform.isMacOS) {
+              // Try to print stack traces of the timed out process.
+              // `sample` is a sampling profiler but we ask it sample for 1
+              // second with a 4 second delay between samples so that we only
+              // sample the threads once.
+              executable = '/usr/bin/sample';
+            } else if (io.Platform.isWindows) {
+              var isX64 = command.executable.contains("X64") ||
+                  command.executable.contains("SIMARM64");
+              if (configuration.windowsSdkPath != null) {
+                executable = configuration.windowsSdkPath +
+                    "\\Debuggers\\${isX64 ? 'x64' : 'x86'}\\cdb.exe";
+                diagnostics.add("Using $executable to print stack traces");
+              } else {
+                diagnostics.add("win_sdk_path not found");
+              }
+            } else {
+              diagnostics.add("Capturing stack traces on"
+                  "${io.Platform.operatingSystem} not supported");
+            }
+            if (executable != null) {
+              var pids = await _getPidList(process.pid, diagnostics);
+              diagnostics.add("Process list including children: $pids");
+              for (pid in pids) {
+                List<String> arguments;
+                if (io.Platform.isLinux) {
+                  arguments = ['-p $pid'];
+                } else if (io.Platform.isMacOS) {
+                  arguments = ['$pid', '1', '4000', '-mayDie'];
+                } else if (io.Platform.isWindows) {
+                  arguments = ['-p', '$pid', '-c', '!uniqstack;qd'];
+                } else {
+                  assert(false);
+                }
+                diagnostics.add("Trying to capture stack trace for pid $pid");
+                try {
+                  var result = await io.Process.run(executable, arguments);
+                  diagnostics.addAll((result.stdout as String).split('\n'));
+                  diagnostics.addAll((result.stderr as String).split('\n'));
+                } catch (error) {
+                  diagnostics.add("Unable to capture stack traces: $error");
+                }
+              }
+            }
+
+            if (!process.kill()) {
+              diagnostics.add("Unable to kill ${process.pid}");
+            }
+          }
+        }
+
+        // Wait for the process to finish or timeout.
+        process.exitCode
+            .timeout(Duration(seconds: timeout), onTimeout: timeoutHandler)
+            .then((exitCode) {
+          // This timeout is used to close stdio to the subprocess once we got
+          // the exitCode. Sometimes descendants of the subprocess keep stdio
+          // handles alive even though the direct subprocess is dead.
+          Future.wait([stdoutFuture, stderrFuture]).timeout(maxStdioDelay,
+              onTimeout: () async {
+            DebugLogger.warning(
+                "$maxStdioDelayPassedMessage (command: $command)");
+            await stdout.cancel();
+            await stderr.cancel();
+            _commandComplete(exitCode);
+            return null;
+          }).then((_) {
+            if (stdout is FileOutputLog) {
+              // Prevent logging data that has already been written to a file
+              // and is unlikely too add value in the logs because the command
+              // succeeded.
+              stdout.complete = <int>[];
+            }
+            _commandComplete(exitCode);
+          });
+        });
+      }).catchError((e) {
+        // TODO(floitsch): should we try to report the stacktrace?
+        print("Process error:");
+        print("  Command: $command");
+        print("  Error: $e");
+        _commandComplete(-1);
+        return true;
+      });
+    }
+  }
+
+  void _commandComplete(int exitCode) {
+    var commandOutput = _createCommandOutput(command, exitCode);
+    completer.complete(commandOutput);
+  }
+
+  CommandOutput _createCommandOutput(ProcessCommand command, int exitCode) {
+    List<int> stdoutData = stdout.toList();
+    List<int> stderrData = stderr.toList();
+    if (stdout.hasNonUtf8 || stderr.hasNonUtf8) {
+      // If the output contained non-utf8 formatted data, then make the exit
+      // code non-zero if it isn't already.
+      if (exitCode == 0) {
+        exitCode = nonUtfFakeExitCode;
+      }
+    }
+    var commandOutput = createCommandOutput(
+        command,
+        exitCode,
+        timedOut,
+        stdoutData,
+        stderrData,
+        DateTime.now().difference(startTime),
+        compilationSkipped,
+        pid);
+    commandOutput.diagnostics.addAll(diagnostics);
+    return commandOutput;
+  }
+
+  Map<String, String> _createProcessEnvironment() {
+    var environment = Map<String, String>.from(io.Platform.environment);
+
+    if (command.environmentOverrides != null) {
+      for (var key in command.environmentOverrides.keys) {
+        environment[key] = command.environmentOverrides[key];
+      }
+    }
+    for (var excludedEnvironmentVariable in _excludedEnvironmentVariables) {
+      environment.remove(excludedEnvironmentVariable);
+    }
+
+    // TODO(terry): Needed for roll 50?
+    environment["GLIBCPP_FORCE_NEW"] = "1";
+    environment["GLIBCXX_FORCE_NEW"] = "1";
+
+    return environment;
+  }
+}
diff --git a/tools/testing/dart/test_configurations.dart b/pkg/test_runner/lib/src/test_configurations.dart
similarity index 74%
rename from tools/testing/dart/test_configurations.dart
rename to pkg/test_runner/lib/src/test_configurations.dart
index 68f97a4..a452642 100644
--- a/tools/testing/dart/test_configurations.dart
+++ b/pkg/test_runner/lib/src/test_configurations.dart
@@ -12,43 +12,38 @@
 import 'configuration.dart';
 import 'path.dart';
 import 'test_progress.dart';
-import 'test_runner.dart';
+import 'process_queue.dart';
 import 'test_suite.dart';
 import 'utils.dart';
 
-/**
- * The directories that contain test suites which follow the conventions
- * required by [StandardTestSuite]'s forDirectory constructor.
- * New test suites should follow this convention because it makes it much
- * simpler to add them to test.dart.  Existing test suites should be
- * moved to here, if possible.
-*/
+/// The directories that contain test suites which follow the conventions
+/// required by [StandardTestSuite]'s forDirectory constructor.
+/// New test suites should follow this convention because it makes it much
+/// simpler to add them to test.dart.  Existing test suites should be
+/// moved to here, if possible.
 final TEST_SUITE_DIRECTORIES = [
-  new Path('third_party/pkg/dartdoc'),
-  new Path('pkg'),
-  new Path('third_party/pkg_tested'),
-  new Path('runtime/tests/vm'),
-  new Path('runtime/observatory/tests/service'),
-  new Path('runtime/observatory/tests/observatory_ui'),
-  new Path('samples'),
-  new Path('samples-dev'),
-  new Path('tests/compiler/dart2js'),
-  new Path('tests/compiler/dart2js_extra'),
-  new Path('tests/compiler/dart2js_native'),
-  new Path('tests/compiler/dartdevc_native'),
-  new Path('tests/corelib_2'),
-  new Path('tests/kernel'),
-  new Path('tests/language_2'),
-  new Path('tests/lib_2'),
-  new Path('tests/standalone'),
-  new Path('tests/standalone_2'),
-  new Path('tests/ffi'),
-  new Path('utils/tests/peg'),
+  Path('third_party/pkg/dartdoc'),
+  Path('pkg'),
+  Path('third_party/pkg_tested'),
+  Path('runtime/tests/vm'),
+  Path('runtime/observatory/tests/service'),
+  Path('runtime/observatory/tests/observatory_ui'),
+  Path('samples'),
+  Path('samples-dev'),
+  Path('tests/compiler/dart2js'),
+  Path('tests/compiler/dart2js_extra'),
+  Path('tests/compiler/dart2js_native'),
+  Path('tests/compiler/dartdevc_native'),
+  Path('tests/corelib_2'),
+  Path('tests/kernel'),
+  Path('tests/language_2'),
+  Path('tests/lib_2'),
+  Path('tests/standalone'),
+  Path('tests/standalone_2'),
+  Path('tests/ffi'),
+  Path('utils/tests/peg'),
 ];
 
-// This file is created by gclient runhooks.
-final VS_TOOLCHAIN_FILE = new Path("build/win_toolchain.json");
-
 Future testConfigurations(List<TestConfiguration> configurations) async {
   var startTime = DateTime.now();
   var startStopwatch = Stopwatch()..start();
@@ -120,20 +115,20 @@
 
     // If we specifically pass in a suite only run that.
     if (configuration.suiteDirectory != null) {
-      var suitePath = new Path(configuration.suiteDirectory);
-      testSuites.add(new PKGTestSuite(configuration, suitePath));
+      var suitePath = Path(configuration.suiteDirectory);
+      testSuites.add(PKGTestSuite(configuration, suitePath));
     } else {
       for (var testSuiteDir in TEST_SUITE_DIRECTORIES) {
         var name = testSuiteDir.filename;
         if (configuration.selectors.containsKey(name)) {
-          testSuites.add(
-              new StandardTestSuite.forDirectory(configuration, testSuiteDir));
+          testSuites
+              .add(StandardTestSuite.forDirectory(configuration, testSuiteDir));
         }
       }
 
       for (var key in configuration.selectors.keys) {
         if (key == 'co19_2') {
-          testSuites.add(new Co19TestSuite(configuration, key));
+          testSuites.add(Co19TestSuite(configuration, key));
         } else if ((configuration.compiler == Compiler.none ||
                 configuration.compiler == Compiler.dartk ||
                 configuration.compiler == Compiler.dartkb) &&
@@ -141,10 +136,10 @@
             key == 'vm') {
           // vm tests contain both cc tests (added here) and dart tests (added
           // in [TEST_SUITE_DIRECTORIES]).
-          testSuites.add(new VMTestSuite(configuration));
+          testSuites.add(VMTestSuite(configuration));
         } else if (configuration.compiler == Compiler.dart2analyzer) {
           if (key == 'analyze_library') {
-            testSuites.add(new AnalyzeLibraryTestSuite(configuration));
+            testSuites.add(AnalyzeLibraryTestSuite(configuration));
           }
         }
       }
@@ -188,49 +183,49 @@
       progressIndicator = Progress.compact;
       formatter = Formatter.color;
       printFailures = false;
-      eventListener.add(new StatusFileUpdatePrinter());
+      eventListener.add(StatusFileUpdatePrinter());
     }
     if (firstConf.silentFailures) {
       printFailures = false;
     }
-    eventListener.add(new SummaryPrinter());
+    eventListener.add(SummaryPrinter());
     if (printFailures) {
       // The buildbot has it's own failure summary since it needs to wrap it
       // into '@@@'-annotated sections.
       var printFailureSummary = progressIndicator != Progress.buildbot;
-      eventListener.add(new TestFailurePrinter(printFailureSummary, formatter));
+      eventListener.add(TestFailurePrinter(printFailureSummary, formatter));
     }
     if (firstConf.printPassingStdout) {
-      eventListener.add(new PassingStdoutPrinter(formatter));
+      eventListener.add(PassingStdoutPrinter(formatter));
     }
     eventListener.add(ProgressIndicator.fromProgress(
         progressIndicator, startTime, formatter));
     if (printTiming) {
-      eventListener.add(new TimingPrinter(startTime));
+      eventListener.add(TimingPrinter(startTime));
     }
-    eventListener.add(new SkippedCompilationsPrinter());
+    eventListener.add(SkippedCompilationsPrinter());
     if (progressIndicator == Progress.status) {
-      eventListener.add(new TimedProgressPrinter());
+      eventListener.add(TimedProgressPrinter());
     }
   }
 
   if (firstConf.writeResults) {
-    eventListener.add(new ResultWriter(firstConf, startTime, startStopwatch));
+    eventListener.add(ResultWriter(firstConf, startTime, startStopwatch));
   }
 
   if (firstConf.copyCoreDumps) {
-    eventListener.add(new UnexpectedCrashLogger());
+    eventListener.add(UnexpectedCrashLogger());
   }
 
   // The only progress indicator when listing tests should be the
   // the summary printer.
   if (listTests) {
-    eventListener.add(new SummaryPrinter(jsonOnly: reportInJson));
+    eventListener.add(SummaryPrinter(jsonOnly: reportInJson));
   } else {
     if (!firstConf.cleanExit) {
-      eventListener.add(new ExitCodeSetter());
+      eventListener.add(ExitCodeSetter());
     }
-    eventListener.add(new IgnoredTestMonitor());
+    eventListener.add(IgnoredTestMonitor());
   }
 
   // If any of the configurations need to access android devices we'll first
@@ -250,6 +245,6 @@
 
   // [firstConf] is needed here, since the ProcessQueue needs to know the
   // settings of 'noBatch' and 'local_ip'
-  new ProcessQueue(firstConf, maxProcesses, maxBrowserProcesses, startTime,
+  ProcessQueue(firstConf, maxProcesses, maxBrowserProcesses, startTime,
       testSuites, eventListener, allTestsFinished, verbose, adbDevicePool);
 }
diff --git a/tools/testing/dart/test_controller.js b/pkg/test_runner/lib/src/test_controller.js
similarity index 99%
rename from tools/testing/dart/test_controller.js
rename to pkg/test_runner/lib/src/test_controller.js
index 8515ddf..9abf450 100644
--- a/tools/testing/dart/test_controller.js
+++ b/pkg/test_runner/lib/src/test_controller.js
@@ -251,7 +251,7 @@
 // Note: before renaming this function, note that it is also included in an
 // inlined error handler in generated HTML files, and manually in tests that
 // include an HTML file.
-// See: tools/testing/dart/browser_test.dart
+// See: pkg/test_runner/lib/src/browser.dart
 function scriptTagOnErrorCallback(e) {
   var message = e && e.message;
   recordEvent('script_onerror', 'script.onError called: ' + message);
diff --git a/tools/testing/dart/test_progress.dart b/pkg/test_runner/lib/src/test_progress.dart
similarity index 92%
rename from tools/testing/dart/test_progress.dart
rename to pkg/test_runner/lib/src/test_progress.dart
index 25dd879..19cbea8 100644
--- a/tools/testing/dart/test_progress.dart
+++ b/pkg/test_runner/lib/src/test_progress.dart
@@ -13,17 +13,17 @@
 import 'configuration.dart';
 import 'path.dart';
 import 'summary_report.dart';
-import 'test_runner.dart';
+import 'test_case.dart';
 import 'utils.dart';
 
 /// Controls how message strings are processed before being displayed.
 class Formatter {
   /// Messages are left as-is.
-  static const normal = const Formatter._();
+  static const normal = Formatter._();
 
   /// Messages are wrapped in ANSI escape codes to color them for display on a
   /// terminal.
-  static const color = const _ColorFormatter();
+  static const color = _ColorFormatter();
 
   const Formatter._();
 
@@ -143,16 +143,16 @@
       // folder next to core dumps and name them
       // `binary.${mode}_${arch}_${binary_name}`.
       final binName = lastCommand.executable;
-      final binFile = new File(binName);
-      final binBaseName = new Path(binName).filename;
+      final binFile = File(binName);
+      final binBaseName = Path(binName).filename;
       if (!archivedBinaries.containsKey(binName) && binFile.existsSync()) {
         final archived = "binary.${mode}_${arch}_${binBaseName}";
-        TestUtils.copyFile(new Path(binName), new Path(archived));
+        TestUtils.copyFile(Path(binName), Path(archived));
         // On Windows also copy PDB file for the binary.
         if (Platform.isWindows) {
-          final pdbPath = new Path("$binName.pdb");
-          if (new File(pdbPath.toNativePath()).existsSync()) {
-            TestUtils.copyFile(pdbPath, new Path("$archived.pdb"));
+          final pdbPath = Path("$binName.pdb");
+          if (File(pdbPath.toNativePath()).existsSync()) {
+            TestUtils.copyFile(pdbPath, Path("$archived.pdb"));
           }
         }
         archivedBinaries[binName] = archived;
@@ -160,11 +160,11 @@
 
       final kernelServiceBaseName = 'kernel-service.dart.snapshot';
       final kernelService =
-          new File('${binFile.parent.path}/$kernelServiceBaseName');
+          File('${binFile.parent.path}/$kernelServiceBaseName');
       if (!archivedBinaries.containsKey(kernelService) &&
           kernelService.existsSync()) {
         final archived = "binary.${mode}_${arch}_${kernelServiceBaseName}";
-        TestUtils.copyFile(new Path(kernelService.path), new Path(archived));
+        TestUtils.copyFile(Path(kernelService.path), Path(archived));
         archivedBinaries[kernelServiceBaseName] = archived;
       }
 
@@ -180,7 +180,7 @@
         RandomAccessFile unexpectedCrashesFile;
         try {
           unexpectedCrashesFile =
-              new File('unexpected-crashes').openSync(mode: FileMode.append);
+              File('unexpected-crashes').openSync(mode: FileMode.append);
           unexpectedCrashesFile.writeStringSync(
               "${test.displayName},${pid},${binaries.join(',')}\n");
         } catch (e) {
@@ -216,8 +216,8 @@
 }
 
 class TimingPrinter extends EventListener {
-  final _command2testCases = new Map<Command, List<TestCase>>();
-  final _commandOutputs = new Set<CommandOutput>();
+  final _commandToTestCases = <Command, List<TestCase>>{};
+  final _commandOutputs = <CommandOutput>{};
   DateTime _startTime;
 
   TimingPrinter(this._startTime);
@@ -226,22 +226,22 @@
     for (var commandOutput in testCase.commandOutputs.values) {
       var command = commandOutput.command;
       _commandOutputs.add(commandOutput);
-      _command2testCases.putIfAbsent(command, () => <TestCase>[]);
-      _command2testCases[command].add(testCase);
+      _commandToTestCases.putIfAbsent(command, () => <TestCase>[]);
+      _commandToTestCases[command].add(testCase);
     }
   }
 
   void allDone() {
-    Duration d = (new DateTime.now()).difference(_startTime);
+    var d = DateTime.now().difference(_startTime);
     print('\n--- Total time: ${_timeString(d)} ---');
     var outputs = _commandOutputs.toList();
     outputs.sort((a, b) {
       return b.time.inMilliseconds - a.time.inMilliseconds;
     });
-    for (int i = 0; i < 20 && i < outputs.length; i++) {
+    for (var i = 0; i < 20 && i < outputs.length; i++) {
       var commandOutput = outputs[i];
       var command = commandOutput.command;
-      var testCases = _command2testCases[command];
+      var testCases = _commandToTestCases[command];
 
       var testCasesDescription = testCases.map((testCase) {
         return "${testCase.configurationString}/${testCase.displayName}";
@@ -255,7 +255,7 @@
 }
 
 class StatusFileUpdatePrinter extends EventListener {
-  var statusToConfigs = new Map<String, List<String>>();
+  var statusToConfigs = <String, List<String>>{};
   var _failureSummary = <String>[];
 
   void done(TestCase test) {
@@ -399,7 +399,7 @@
   void done(TestCase test) {
     if (!test.unexpectedOutput) {
       var lines = <String>[];
-      var output = new OutputWriter(_formatter, lines);
+      var output = OutputWriter(_formatter, lines);
       for (final command in test.commands) {
         var commandOutput = test.commandOutputs[command];
         if (commandOutput == null) continue;
@@ -422,15 +422,15 @@
       Progress progress, DateTime startTime, Formatter formatter) {
     switch (progress) {
       case Progress.compact:
-        return new CompactProgressIndicator(startTime, formatter);
+        return CompactProgressIndicator(startTime, formatter);
       case Progress.line:
-        return new LineProgressIndicator();
+        return LineProgressIndicator();
       case Progress.verbose:
-        return new VerboseProgressIndicator(startTime);
+        return VerboseProgressIndicator(startTime);
       case Progress.status:
-        return new ProgressIndicator(startTime);
+        return ProgressIndicator(startTime);
       case Progress.buildbot:
-        return new BuildbotProgressIndicator(startTime);
+        return BuildbotProgressIndicator(startTime);
     }
 
     throw "unreachable";
@@ -491,7 +491,7 @@
     var progressPadded = (_allTestsKnown ? percent : '--').padLeft(3);
     var passedPadded = _passedTests.toString().padLeft(5);
     var failedPadded = _failedTests.toString().padLeft(5);
-    var elapsed = (new DateTime.now()).difference(_startTime);
+    var elapsed = (DateTime.now()).difference(_startTime);
     var progressLine = '\r[${_timeString(elapsed)} | $progressPadded% | '
         '+${_formatter.passed(passedPadded)} | '
         '-${_formatter.failed(failedPadded)}]';
@@ -604,7 +604,7 @@
 List<String> _buildFailureOutput(TestCase test,
     [Formatter formatter = Formatter.normal]) {
   var lines = <String>[];
-  var output = new OutputWriter(formatter, lines);
+  var output = OutputWriter(formatter, lines);
   _writeFailureStatus(test, formatter, output);
   _writeFailureOutput(test, formatter, output);
   _writeFailureReproductionCommands(test, formatter, output);
@@ -614,7 +614,7 @@
 List<String> _buildFailureLog(TestCase test) {
   final formatter = Formatter.normal;
   final lines = <String>[];
-  final output = new OutputWriter(formatter, lines);
+  final output = OutputWriter(formatter, lines);
   _writeFailureOutput(test, formatter, output);
   _writeFailureReproductionCommands(test, formatter, output);
   return lines;
@@ -717,7 +717,7 @@
 
   void done(TestCase test) {
     if (_configuration != test.configuration) {
-      throw new Exception("Two configurations in the same run. "
+      throw Exception("Two configurations in the same run. "
           "Cannot output results for multiple configurations.");
     }
     final name = test.displayName;
diff --git a/tools/testing/dart/test_suite.dart b/pkg/test_runner/lib/src/test_suite.dart
similarity index 75%
rename from tools/testing/dart/test_suite.dart
rename to pkg/test_runner/lib/src/test_suite.dart
index f00c75d..1204a81 100644
--- a/tools/testing/dart/test_suite.dart
+++ b/pkg/test_runner/lib/src/test_suite.dart
@@ -2,89 +2,81 @@
 // 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.
 
-/**
- * Classes and methods for enumerating and preparing tests.
- *
- * This library includes:
- *
- * - Creating tests by listing all the Dart files in certain directories,
- *   and creating [TestCase]s for those files that meet the relevant criteria.
- * - Preparing tests, including copying files and frameworks to temporary
- *   directories, and computing the command line and arguments to be run.
- */
+/// Classes and methods for enumerating and preparing tests.
+///
+/// This library includes:
+///
+/// - Creating tests by listing all the Dart files in certain directories,
+///   and creating [TestCase]s for those files that meet the relevant criteria.
+/// - Preparing tests, including copying files and frameworks to temporary
+///   directories, and computing the command line and arguments to be run.
 import 'dart:async';
 import 'dart:io';
 import 'dart:math';
 
 import "package:status_file/expectation.dart";
 
-import 'browser_test.dart';
+import 'browser.dart';
 import 'command.dart';
-import 'compiler_configuration.dart';
 import 'configuration.dart';
 import 'expectation_set.dart';
-import 'http_server.dart';
 import 'multitest.dart';
 import 'path.dart';
 import 'repository.dart';
 import 'summary_report.dart';
+import 'test_case.dart';
 import 'test_configurations.dart';
-import 'test_runner.dart';
+import 'testing_servers.dart';
 import 'utils.dart';
 
-RegExp multiHtmlTestGroupRegExp = new RegExp(r"\s*[^/]\s*group\('[^,']*");
-RegExp multiHtmlTestRegExp = new RegExp(r"useHtmlIndividualConfiguration\(\)");
-// Require at least one non-space character before '//[/#]'
-RegExp multiTestRegExp = new RegExp(r"\S *"
-    r"//[#/] \w+:(.*)");
-RegExp dartExtension = new RegExp(r'\.dart$');
+RegExp _multiHtmlTestGroupRegExp = RegExp(r"\s*[^/]\s*group\('[^,']*");
+RegExp _multiHtmlTestRegExp = RegExp(r"useHtmlIndividualConfiguration\(\)");
 
-/**
- * A simple function that tests [arg] and returns `true` or `false`.
- */
-typedef bool Predicate<T>(T arg);
+/// Require at least one non-space character before '//[/#]'.
+RegExp _multiTestRegExp = RegExp(r"\S *//[#/] \w+:(.*)");
 
-typedef void CreateTest(Path filePath, Path originTestPath,
+typedef TestCaseEvent = void Function(TestCase testCase);
+
+/// A simple function that tests [arg] and returns `true` or `false`.
+typedef Predicate<T> = bool Function(T arg);
+
+typedef CreateTest = void Function(Path filePath, Path originTestPath,
     {bool hasSyntaxError,
     bool hasCompileError,
     bool hasRuntimeError,
     bool hasStaticWarning,
     String multitestKey});
 
-typedef void VoidFunction();
+typedef VoidFunction = void Function();
 
-/**
- * Calls [function] asynchronously. Returns a future that completes with the
- * result of the function. If the function is `null`, returns a future that
- * completes immediately with `null`.
- */
+/// Calls [function] asynchronously. Returns a future that completes with the
+/// result of the function. If the function is `null`, returns a future that
+/// completes immediately with `null`.
 Future asynchronously<T>(T function()) {
-  if (function == null) return new Future<T>.value(null);
+  if (function == null) return Future<T>.value(null);
 
-  var completer = new Completer<T>();
+  var completer = Completer<T>();
   Timer.run(() => completer.complete(function()));
 
   return completer.future;
 }
 
-/** A completer that waits until all added [Future]s complete. */
+/// A completer that waits until all added [Future]s complete.
 // TODO(rnystrom): Copied from web_components. Remove from here when it gets
 // added to dart:core. (See #6626.)
 class FutureGroup {
-  static const _FINISHED = -1;
+  static const _finished = -1;
   int _pending = 0;
-  Completer<List> _completer = new Completer<List>();
-  final List<Future> futures = <Future>[];
+  Completer<List> _completer = Completer();
+  final List<Future> futures = [];
   bool wasCompleted = false;
 
-  /**
-   * Wait for [task] to complete (assuming this barrier has not already been
-   * marked as completed, otherwise you'll get an exception indicating that a
-   * future has already been completed).
-   */
+  /// Wait for [task] to complete (assuming this barrier has not already been
+  /// marked as completed, otherwise you'll get an exception indicating that a
+  /// future has already been completed).
   void add(Future task) {
-    if (_pending == _FINISHED) {
-      throw new Exception("FutureFutureAlreadyCompleteException");
+    if (_pending == _finished) {
+      throw Exception("FutureFutureAlreadyCompleteException");
     }
     _pending++;
     var handledTaskFuture = task.catchError((e, StackTrace s) {
@@ -95,7 +87,7 @@
     }).then((_) {
       _pending--;
       if (_pending == 0) {
-        _pending = _FINISHED;
+        _pending = _finished;
         if (!wasCompleted) {
           _completer.complete(futures);
           wasCompleted = true;
@@ -108,18 +100,17 @@
   Future<List> get future => _completer.future;
 }
 
-/**
- * A TestSuite represents a collection of tests.  It creates a [TestCase]
- * object for each test to be run, and passes the test cases to a callback.
- *
- * Most TestSuites represent a directory or directory tree containing tests,
- * and a status file containing the expected results when these tests are run.
- */
+/// A TestSuite represents a collection of tests.  It creates a [TestCase]
+/// object for each test to be run, and passes the test cases to a callback.
+///
+/// Most TestSuites represent a directory or directory tree containing tests,
+/// and a status file containing the expected results when these tests are run.
 abstract class TestSuite {
   final TestConfiguration configuration;
   final String suiteName;
   final List<String> statusFilePaths;
-  // This function is set by subclasses before enqueueing starts.
+
+  /// This function is set by subclasses before enqueueing starts.
   Function doTest;
   Map<String, String> _environmentOverrides;
 
@@ -131,24 +122,18 @@
       _environmentOverrides['DART_SUPPRESS_WER'] = '1';
       if (configuration.copyCoreDumps) {
         _environmentOverrides['DART_CRASHPAD_HANDLER'] =
-            new Path(buildDir + '/crashpad_handler.exe')
-                .absolute
-                .toNativePath();
+            Path(buildDir + '/crashpad_handler.exe').absolute.toNativePath();
       }
     }
   }
 
   Map<String, String> get environmentOverrides => _environmentOverrides;
 
-  /**
-   * The output directory for this suite's configuration.
-   */
+  /// The output directory for this suite's configuration.
   String get buildDir => configuration.buildDirectory;
 
-  /**
-   * The path to the compiler for this suite's configuration. Returns `null` if
-   * no compiler should be used.
-   */
+  /// The path to the compiler for this suite's configuration. Returns `null` if
+  /// no compiler should be used.
   String get compilerPath {
     var compilerConfiguration = configuration.compilerConfiguration;
     if (!compilerConfiguration.hasCompiler) return null;
@@ -159,25 +144,23 @@
     return name;
   }
 
-  /**
-   * Call the callback function onTest with a [TestCase] argument for each
-   * test in the suite.  When all tests have been processed, call [onDone].
-   *
-   * The [testCache] argument provides a persistent store that can be used to
-   * cache information about the test suite, so that directories do not need
-   * to be listed each time.
-   */
+  /// Call the callback function onTest with a [TestCase] argument for each
+  /// test in the suite.  When all tests have been processed, call [onDone].
+  ///
+  /// The [testCache] argument provides a persistent store that can be used to
+  /// cache information about the test suite, so that directories do not need
+  /// to be listed each time.
   Future forEachTest(
       TestCaseEvent onTest, Map<String, List<TestInformation>> testCache,
       [VoidFunction onDone]);
 
-  // This function will be called for every TestCase of this test suite.
-  // It will
-  //  - handle sharding
-  //  - update SummaryReport
-  //  - handle SKIP/SKIP_BY_DESIGN markers
-  //  - test if the selector matches
-  // and will enqueue the test (if necessary).
+  /// This function will be called for every TestCase of this test suite.
+  /// It will:
+  ///  - handle sharding
+  ///  - update SummaryReport
+  ///  - handle SKIP/SKIP_BY_DESIGN markers
+  ///  - test if the selector matches
+  /// and will enqueue the test (if necessary).
   void enqueueNewTestCase(
       String testName, List<Command> commands, Set<Expectation> expectations,
       [TestInformation info]) {
@@ -196,8 +179,7 @@
     }
 
     var negative = info != null ? isNegative(info) : false;
-    var testCase = new TestCase(
-        displayName, commands, configuration, expectations,
+    var testCase = TestCase(displayName, commands, configuration, expectations,
         info: info);
     if (negative &&
         configuration.runtimeConfiguration.shouldSkipNegativeTests) {
@@ -264,20 +246,20 @@
     relative = relative.directoryPath.append(relative.filenameWithoutExtension);
     String testUniqueName = TestUtils.getShortName(relative.toString());
 
-    Path generatedTestPath = new Path(buildDir)
+    Path generatedTestPath = Path(buildDir)
         .append('generated_$name')
         .append(dirname)
         .append(testUniqueName);
 
-    TestUtils.mkdirRecursive(new Path('.'), generatedTestPath);
-    return new File(generatedTestPath.toNativePath())
+    TestUtils.mkdirRecursive(Path('.'), generatedTestPath);
+    return File(generatedTestPath.toNativePath())
         .absolute
         .path
         .replaceAll('\\', '/');
   }
 
   String buildTestCaseDisplayName(Path suiteDir, Path originTestPath,
-      {String multitestName: ""}) {
+      {String multitestName = ""}) {
     Path testNamePath = originTestPath.relativeTo(suiteDir);
     var directory = testNamePath.directoryPath;
     var filenameWithoutExt = testNamePath.filenameWithoutExtension;
@@ -294,11 +276,8 @@
     return testName;
   }
 
-  /**
-   * Create a directories for generated assets (tests, html files,
-   * pubspec checkouts ...).
-   */
-
+  /// Create a directories for generated assets (tests, html files,
+  /// pubspec checkouts ...).
   String createOutputDirectory(Path testPath) {
     var checked = configuration.isChecked ? '-checked' : '';
     var legacy = configuration.noPreviewDart2 ? '-legacy' : '';
@@ -355,7 +334,7 @@
     // For listing the tests we use the '$runnerName.host' binary if it exists
     // and use '$runnerName' if it doesn't.
     var hostBinary = '$targetRunnerPath.host$binarySuffix';
-    if (new File(hostBinary).existsSync()) {
+    if (File(hostBinary).existsSync()) {
       hostRunnerPath = hostBinary;
     } else {
       hostRunnerPath = targetRunnerPath;
@@ -368,10 +347,10 @@
 
     var statusFiles =
         statusFilePaths.map((statusFile) => "$dartDir/$statusFile").toList();
-    var expectations = new ExpectationSet.read(statusFiles, configuration);
+    var expectations = ExpectationSet.read(statusFiles, configuration);
 
     try {
-      for (VmUnitTest test in await _listTests(hostRunnerPath)) {
+      for (VMUnitTest test in await _listTests(hostRunnerPath)) {
         _addTest(expectations, test);
       }
 
@@ -384,7 +363,7 @@
     }
   }
 
-  void _addTest(ExpectationSet testExpectations, VmUnitTest test) {
+  void _addTest(ExpectationSet testExpectations, VMUnitTest test) {
     final fullName = 'cc/${test.name}';
     var expectations = testExpectations.expectations(fullName);
 
@@ -416,7 +395,7 @@
       final filename = configuration.architecture == Architecture.x64
           ? '$buildDir/gen/kernel-service.dart.snapshot'
           : '$buildDir/gen/kernel_service.dill';
-      final dfePath = new Path(filename).absolute.toNativePath();
+      final dfePath = Path(filename).absolute.toNativePath();
       // '--dfe' has to be the first argument for run_vm_test to pick it up.
       args.insert(0, '--dfe=$dfePath');
     }
@@ -431,7 +410,7 @@
     enqueueNewTestCase(fullName, [command], expectations, testInfo);
   }
 
-  Future<Iterable<VmUnitTest>> _listTests(String runnerPath) async {
+  Future<Iterable<VMUnitTest>> _listTests(String runnerPath) async {
     var result = await Process.run(runnerPath, ["--list"]);
     if (result.exitCode != 0) {
       throw "Failed to list tests: '$runnerPath --list'. "
@@ -444,16 +423,16 @@
         .where((name) => name.isNotEmpty)
         .map((String line) {
       final parts = line.split(' ');
-      return VmUnitTest(parts[0].trim(), parts.skip(1).single);
+      return VMUnitTest(parts[0].trim(), parts.skip(1).single);
     });
   }
 }
 
-class VmUnitTest {
+class VMUnitTest {
   final String name;
   final String expectation;
 
-  VmUnitTest(this.name, this.expectation);
+  VMUnitTest(this.name, this.expectation);
 }
 
 class TestInformation {
@@ -475,16 +454,14 @@
       this.hasCompileError,
       this.hasRuntimeError,
       this.hasStaticWarning,
-      {this.multitestKey: '',
-      this.hasCrash: false}) {
+      {this.multitestKey = '',
+      this.hasCrash = false}) {
     assert(filePath.isAbsolute);
   }
 }
 
-/**
- * A standard [TestSuite] implementation that searches for tests in a
- * directory, and creates [TestCase]s that compile and/or run them.
- */
+/// A standard [TestSuite] implementation that searches for tests in a
+/// directory, and creates [TestCase]s that compile and/or run them.
 class StandardTestSuite extends TestSuite {
   final Path suiteDir;
   ExpectationSet testExpectations;
@@ -498,26 +475,26 @@
 
   StandardTestSuite(TestConfiguration configuration, String suiteName,
       Path suiteDirectory, List<String> statusFilePaths,
-      {bool recursive: false})
+      {bool recursive = false})
       : dartDir = Repository.dir,
         listRecursively = recursive,
         suiteDir = Repository.dir.join(suiteDirectory),
         extraVmOptions = configuration.vmOptions,
         super(configuration, suiteName, statusFilePaths) {
-    // Initialize _dart2JsBootstrapDependencies
+    // Initialize _dart2JsBootstrapDependencies.
     if (!configuration.useSdk) {
       _dart2JsBootstrapDependencies = [];
     } else {
       _dart2JsBootstrapDependencies = [
         Uri.base
-            .resolveUri(new Uri.directory(buildDir))
+            .resolveUri(Uri.directory(buildDir))
             .resolve('dart-sdk/bin/snapshots/dart2js.dart.snapshot')
       ];
     }
 
-    // Initialize _testListPossibleFilenames
+    // Initialize _testListPossibleFilenames.
     if (configuration.testList != null) {
-      _testListPossibleFilenames = Set<String>();
+      _testListPossibleFilenames = <String>{};
       for (String s in configuration.testList) {
         if (s.startsWith("$suiteName/")) {
           s = s.substring(s.indexOf('/') + 1);
@@ -537,46 +514,44 @@
       }
     }
 
-    // Initialize _selectorFilenameRegExp
-    String pattern = configuration.selectors[suiteName].pattern;
+    // Initialize _selectorFilenameRegExp.
+    var pattern = configuration.selectors[suiteName].pattern;
     if (pattern.contains("/")) {
-      String lastPart = pattern.substring(pattern.lastIndexOf("/") + 1);
+      var lastPart = pattern.substring(pattern.lastIndexOf("/") + 1);
       // If the selector is a multitest name ending in a number or 'none'
       // we also accept test file names that don't contain that last part.
       if (int.tryParse(lastPart) != null || lastPart == "none") {
         pattern = pattern.substring(0, pattern.lastIndexOf("/"));
       }
     }
-    _selectorFilenameRegExp = new RegExp(pattern);
+    _selectorFilenameRegExp = RegExp(pattern);
   }
 
-  /**
-   * Creates a test suite whose file organization matches an expected structure.
-   * To use this, your suite should look like:
-   *
-   *     dart/
-   *       path/
-   *         to/
-   *           mytestsuite/
-   *             mytestsuite.status
-   *             example1_test.dart
-   *             example2_test.dart
-   *             example3_test.dart
-   *
-   * The important parts:
-   *
-   * * The leaf directory name is the name of your test suite.
-   * * The status file uses the same name.
-   * * Test files are directly in that directory and end in "_test.dart".
-   *
-   * If you follow that convention, then you can construct one of these like:
-   *
-   * new StandardTestSuite.forDirectory(configuration, 'path/to/mytestsuite');
-   *
-   * instead of having to create a custom [StandardTestSuite] subclass. In
-   * particular, if you add 'path/to/mytestsuite' to [TEST_SUITE_DIRECTORIES]
-   * in test.dart, this will all be set up for you.
-   */
+  /// Creates a test suite whose file organization matches an expected structure.
+  /// To use this, your suite should look like:
+  ///
+  ///     dart/
+  ///       path/
+  ///         to/
+  ///           mytestsuite/
+  ///             mytestsuite.status
+  ///             example1_test.dart
+  ///             example2_test.dart
+  ///             example3_test.dart
+  ///
+  /// The important parts:
+  ///
+  /// * The leaf directory name is the name of your test suite.
+  /// * The status file uses the same name.
+  /// * Test files are directly in that directory and end in "_test.dart".
+  ///
+  /// If you follow that convention, then you can construct one of these like:
+  ///
+  /// new StandardTestSuite.forDirectory(configuration, 'path/to/mytestsuite');
+  ///
+  /// instead of having to create a custom [StandardTestSuite] subclass. In
+  /// particular, if you add 'path/to/mytestsuite' to [TEST_SUITE_DIRECTORIES]
+  /// in test.dart, this will all be set up for you.
   factory StandardTestSuite.forDirectory(
       TestConfiguration configuration, Path directory) {
     var name = directory.filename;
@@ -594,7 +569,7 @@
       '$directory/${name}_vm.status',
     ];
 
-    return new StandardTestSuite(configuration, name, directory, status_paths,
+    return StandardTestSuite(configuration, name, directory, status_paths,
         recursive: true);
   }
 
@@ -627,28 +602,26 @@
     if (onDone != null) onDone();
   }
 
-  /**
-   * Reads the status files and completes with the parsed expectations.
-   */
+  /// Reads the status files and completes with the parsed expectations.
   ExpectationSet readExpectations() {
     var statusFiles = statusFilePaths.where((String statusFilePath) {
-      var file = new File(dartDir.append(statusFilePath).toNativePath());
+      var file = File(dartDir.append(statusFilePath).toNativePath());
       return file.existsSync();
     }).map((statusFilePath) {
       return dartDir.append(statusFilePath).toNativePath();
     }).toList();
 
-    return new ExpectationSet.read(statusFiles, configuration);
+    return ExpectationSet.read(statusFiles, configuration);
   }
 
   Future enqueueTests() {
-    Directory dir = new Directory(suiteDir.toNativePath());
+    Directory dir = Directory(suiteDir.toNativePath());
     return dir.exists().then((exists) {
       if (!exists) {
         print('Directory containing tests missing: ${suiteDir.toNativePath()}');
-        return new Future.value(null);
+        return Future.value(null);
       } else {
-        var group = new FutureGroup();
+        var group = FutureGroup();
         enqueueDirectory(dir, group);
         return group.future;
       }
@@ -679,7 +652,7 @@
 
     if (!isTestFile(filename)) return;
 
-    var optionsFromFile = readOptionsFromFile(new Uri.file(filename));
+    var optionsFromFile = readOptionsFromFile(Uri.file(filename));
     CreateTest createTestCase = makeTestCaseCreator(optionsFromFile);
 
     if (optionsFromFile['isMultitest'] as bool) {
@@ -709,11 +682,11 @@
     if (optionsFromFile['packageRoot'] == null &&
         optionsFromFile['packages'] == null) {
       if (configuration.packageRoot != null) {
-        packageRoot = new Path(configuration.packageRoot);
+        packageRoot = Path(configuration.packageRoot);
         optionsFromFile['packageRoot'] = packageRoot.toNativePath();
       }
       if (configuration.packages != null) {
-        Path packages = new Path(configuration.packages);
+        Path packages = Path(configuration.packages);
         optionsFromFile['packages'] = packages.toNativePath();
       }
     }
@@ -801,23 +774,22 @@
       var path = info.filePath;
       if (vmOptionsVariant != 0) {
         // Ensure a unique directory for each test case.
-        path = path.join(new Path(vmOptionsVariant.toString()));
+        path = path.join(Path(vmOptionsVariant.toString()));
       }
       tempDir = createCompilationOutputDirectory(path);
 
       var otherResources =
           info.optionsFromFile['otherResources'] as List<String>;
       for (var name in otherResources) {
-        var namePath = new Path(name);
+        var namePath = Path(name);
         var fromPath = info.filePath.directoryPath.join(namePath);
-        new File('$tempDir/$name').parent.createSync(recursive: true);
-        new File(fromPath.toNativePath()).copySync('$tempDir/$name');
+        File('$tempDir/$name').parent.createSync(recursive: true);
+        File(fromPath.toNativePath()).copySync('$tempDir/$name');
       }
     }
 
-    CommandArtifact compilationArtifact =
-        compilerConfiguration.computeCompilationArtifact(
-            tempDir, compileTimeArguments, environmentOverrides);
+    var compilationArtifact = compilerConfiguration.computeCompilationArtifact(
+        tempDir, compileTimeArguments, environmentOverrides);
     if (!configuration.skipCompilation) {
       commands.addAll(compilationArtifact.commands);
     }
@@ -835,20 +807,19 @@
             s.replaceAll("__RANDOM__", "${Random().nextInt(0x7fffffff)}"))
         .toList();
 
-    List<String> runtimeArguments =
-        compilerConfiguration.computeRuntimeArguments(
-            configuration.runtimeConfiguration,
-            info,
-            vmOptions,
-            sharedOptions,
-            dartOptions,
-            args,
-            compilationArtifact);
+    var runtimeArguments = compilerConfiguration.computeRuntimeArguments(
+        configuration.runtimeConfiguration,
+        info,
+        vmOptions,
+        sharedOptions,
+        dartOptions,
+        args,
+        compilationArtifact);
 
-    Map<String, String> environment = environmentOverrides;
+    var environment = environmentOverrides;
     var extraEnv = info.optionsFromFile['environment'] as Map<String, String>;
     if (extraEnv != null) {
-      environment = new Map.from(environment)..addAll(extraEnv);
+      environment = {...environment, ...extraEnv};
     }
 
     return commands
@@ -865,10 +836,10 @@
         {bool hasSyntaxError,
         bool hasCompileError,
         bool hasRuntimeError,
-        bool hasStaticWarning: false,
+        bool hasStaticWarning = false,
         String multitestKey}) {
       // Cache the test information for each test case.
-      var info = new TestInformation(filePath, originTestPath, optionsFromFile,
+      var info = TestInformation(filePath, originTestPath, optionsFromFile,
           hasSyntaxError, hasCompileError, hasRuntimeError, hasStaticWarning,
           multitestKey: multitestKey);
       cachedTests.add(info);
@@ -876,30 +847,30 @@
     };
   }
 
-  /**
-   * _createUrlPathFromFile takes a [file], which is either located in the dart
-   * or in the build directory, and will return a String representing
-   * the relative path to either the dart or the build directory.
-   * Thus, the returned [String] will be the path component of the URL
-   * corresponding to [file] (the http server serves files relative to the
-   * dart/build directories).
-   */
+  /// Takes a [file], which is either located in the dart or in the build
+  /// directory, and returns a String representing the relative path to either
+  /// the dart or the build directory.
+  ///
+  /// Thus, the returned [String] will be the path component of the URL
+  /// corresponding to [file] (the HTTP server serves files relative to the
+  /// dart/build directories).
   String _createUrlPathFromFile(Path file) {
     file = file.absolute;
 
-    var relativeBuildDir = new Path(configuration.buildDirectory);
+    var relativeBuildDir = Path(configuration.buildDirectory);
     var buildDir = relativeBuildDir.absolute;
     var dartDir = Repository.dir.absolute;
 
     var fileString = file.toString();
     if (fileString.startsWith(buildDir.toString())) {
       var fileRelativeToBuildDir = file.relativeTo(buildDir);
-      return "/$PREFIX_BUILDDIR/$fileRelativeToBuildDir";
+      return "/$prefixBuildDir/$fileRelativeToBuildDir";
     } else if (fileString.startsWith(dartDir.toString())) {
       var fileRelativeToDartDir = file.relativeTo(dartDir);
-      return "/$PREFIX_DARTDIR/$fileRelativeToDartDir";
+      return "/$prefixDartDir/$fileRelativeToDartDir";
     }
-    // Unreachable
+
+    // Unreachable.
     print("Cannot create URL for path $file. Not in build or dart directory.");
     exit(1);
     return null;
@@ -918,7 +889,7 @@
     if (subtestName != null) {
       parameters['group'] = subtestName;
     }
-    return new Uri(
+    return Uri(
             scheme: 'http',
             host: configuration.localIP,
             port: serverPort,
@@ -954,7 +925,7 @@
 
     // Use existing HTML document if available.
     String content;
-    var customHtml = new File(
+    var customHtml = File(
         info.filePath.directoryPath.append('$nameNoExt.html').toNativePath());
     if (customHtml.existsSync()) {
       outputDir = tempDir;
@@ -963,18 +934,18 @@
     } else {
       // Synthesize an HTML file for the test.
       if (configuration.compiler == Compiler.dart2js) {
-        var scriptPath = _createUrlPathFromFile(
-            new Path('$compilationTempDir/$nameNoExt.js'));
+        var scriptPath =
+            _createUrlPathFromFile(Path('$compilationTempDir/$nameNoExt.js'));
         content = dart2jsHtml(fileName, scriptPath);
       } else {
         var jsDir =
-            new Path(compilationTempDir).relativeTo(Repository.dir).toString();
+            Path(compilationTempDir).relativeTo(Repository.dir).toString();
         content = dartdevcHtml(nameNoExt, jsDir, configuration.compiler);
       }
     }
 
     var htmlPath = '$tempDir/test.html';
-    new File(htmlPath).writeAsStringSync(content);
+    File(htmlPath).writeAsStringSync(content);
 
     // Construct the command(s) that compile all the inputs needed by the
     // browser test.
@@ -1019,7 +990,7 @@
     // Construct the command that executes the browser test.
     commands = commands.toList();
 
-    var htmlPathSubtest = _createUrlPathFromFile(new Path(htmlPath));
+    var htmlPathSubtest = _createUrlPathFromFile(Path(htmlPath));
     var fullHtmlPath = _uriForBrowserTest(htmlPathSubtest, subtestName);
 
     commands.add(Command.browserTest(fullHtmlPath, configuration,
@@ -1034,7 +1005,7 @@
       Path filePath, Map<String, dynamic> optionsFromFile) {
     var args = configuration.standardOptions.toList();
 
-    String packages = packagesArgument(optionsFromFile['packageRoot'] as String,
+    var packages = packagesArgument(optionsFromFile['packageRoot'] as String,
         optionsFromFile['packages'] as String);
     if (packages != null) {
       args.add(packages);
@@ -1067,74 +1038,72 @@
     }
   }
 
-  /**
-   * Special options for individual tests are currently specified in various
-   * ways: with comments directly in test files, by using certain imports, or by
-   * creating additional files in the test directories.
-   *
-   * Here is a list of options that are used by 'test.dart' today:
-   *   - Flags can be passed to the vm process that runs the test by adding a
-   *   comment to the test file:
-   *
-   *     // VMOptions=--flag1 --flag2
-   *
-   *   - Flags can be passed to dart2js, vm or dartdevc by adding a comment to
-   *   the test file:
-   *
-   *     // SharedOptions=--flag1 --flag2
-   *
-   *   - Flags can be passed to dart2js by adding a comment to the test file:
-   *
-   *     // dart2jsOptions=--flag1 --flag2
-   *
-   *   - Flags can be passed to the dart script that contains the test also
-   *   using comments, as follows:
-   *
-   *     // DartOptions=--flag1 --flag2
-   *
-   *   - Extra environment variables can be passed to the process that runs
-   *   the test by adding comment(s) to the test file:
-   *
-   *     // Environment=ENV_VAR1=foo bar
-   *     // Environment=ENV_VAR2=bazz
-   *
-   *   - Most tests are not web tests, but can (and will be) wrapped within
-   *   an HTML file and another script file to test them also on browser
-   *   environments (e.g. language and corelib tests are run this way).
-   *   We deduce that if a file with the same name as the test, but ending in
-   *   .html instead of .dart exists, the test was intended to be a web test
-   *   and no wrapping is necessary.
-   *
-   *     // SharedObjects=foobar
-   *
-   *   - This test requires libfoobar.so, libfoobar.dylib or foobar.dll to be
-   *   in the system linker path of the VM.
-   *
-   *   - 'test.dart' assumes tests fail if
-   *   the process returns a non-zero exit code (in the case of web tests, we
-   *   check for PASS/FAIL indications in the test output).
-   *
-   * This method is static as the map is cached and shared amongst
-   * configurations, so it may not use [configuration].
-   */
+  /// Special options for individual tests are currently specified in various
+  /// ways: with comments directly in test files, by using certain imports, or
+  /// by creating additional files in the test directories.
+  ///
+  /// Here is a list of options that are used by 'test.dart' today:
+  ///   - Flags can be passed to the vm process that runs the test by adding a
+  ///   comment to the test file:
+  ///
+  ///     // VMOptions=--flag1 --flag2
+  ///
+  ///   - Flags can be passed to dart2js, vm or dartdevc by adding a comment to
+  ///   the test file:
+  ///
+  ///     // SharedOptions=--flag1 --flag2
+  ///
+  ///   - Flags can be passed to dart2js by adding a comment to the test file:
+  ///
+  ///     // dart2jsOptions=--flag1 --flag2
+  ///
+  ///   - Flags can be passed to the dart script that contains the test also
+  ///   using comments, as follows:
+  ///
+  ///     // DartOptions=--flag1 --flag2
+  ///
+  ///   - Extra environment variables can be passed to the process that runs
+  ///   the test by adding comment(s) to the test file:
+  ///
+  ///     // Environment=ENV_VAR1=foo bar
+  ///     // Environment=ENV_VAR2=bazz
+  ///
+  ///   - Most tests are not web tests, but can (and will be) wrapped within
+  ///   an HTML file and another script file to test them also on browser
+  ///   environments (e.g. language and corelib tests are run this way).
+  ///   We deduce that if a file with the same name as the test, but ending in
+  ///   .html instead of .dart exists, the test was intended to be a web test
+  ///   and no wrapping is necessary.
+  ///
+  ///     // SharedObjects=foobar
+  ///
+  ///   - This test requires libfoobar.so, libfoobar.dylib or foobar.dll to be
+  ///   in the system linker path of the VM.
+  ///
+  ///   - 'test.dart' assumes tests fail if
+  ///   the process returns a non-zero exit code (in the case of web tests, we
+  ///   check for PASS/FAIL indications in the test output).
+  ///
+  /// This method is static as the map is cached and shared amongst
+  /// configurations, so it may not use [configuration].
   Map<String, dynamic> readOptionsFromFile(Uri uri) {
     if (uri.path.endsWith('.dill')) {
       return optionsFromKernelFile();
     }
-    RegExp testOptionsRegExp = new RegExp(r"// VMOptions=(.*)");
-    RegExp environmentRegExp = new RegExp(r"// Environment=(.*)");
-    RegExp otherResourcesRegExp = new RegExp(r"// OtherResources=(.*)");
-    RegExp sharedObjectsRegExp = new RegExp(r"// SharedObjects=(.*)");
-    RegExp packageRootRegExp = new RegExp(r"// PackageRoot=(.*)");
-    RegExp packagesRegExp = new RegExp(r"// Packages=(.*)");
-    RegExp isolateStubsRegExp = new RegExp(r"// IsolateStubs=(.*)");
+    var testOptionsRegExp = RegExp(r"// VMOptions=(.*)");
+    var environmentRegExp = RegExp(r"// Environment=(.*)");
+    var otherResourcesRegExp = RegExp(r"// OtherResources=(.*)");
+    var sharedObjectsRegExp = RegExp(r"// SharedObjects=(.*)");
+    var packageRootRegExp = RegExp(r"// PackageRoot=(.*)");
+    var packagesRegExp = RegExp(r"// Packages=(.*)");
+    var isolateStubsRegExp = RegExp(r"// IsolateStubs=(.*)");
     // TODO(gram) Clean these up once the old directives are not supported.
-    RegExp domImportRegExp = new RegExp(
+    var domImportRegExp = RegExp(
         r"^[#]?import.*dart:(html|web_audio|indexed_db|svg|web_sql)",
         multiLine: true);
 
-    var bytes = new File.fromUri(uri).readAsBytesSync();
-    String contents = decodeUtf8(bytes);
+    var bytes = File.fromUri(uri).readAsBytesSync();
+    var contents = decodeUtf8(bytes);
     bytes = null;
 
     // Find the options in the file.
@@ -1151,11 +1120,11 @@
         s.split(' ').where((e) => e != '').toList();
 
     List<String> singleListOfOptions(String name) {
-      var matches = new RegExp('// $name=(.*)').allMatches(contents);
+      var matches = RegExp('// $name=(.*)').allMatches(contents);
       List<String> options;
       for (var match in matches) {
         if (options != null) {
-          throw new Exception(
+          throw Exception(
               'More than one "// $name=" line in test ${uri.toFilePath()}');
         }
         options = wordSplit(match[1]);
@@ -1176,10 +1145,10 @@
 
     matches = environmentRegExp.allMatches(contents);
     for (var match in matches) {
-      final String envDef = match[1];
-      final int pos = envDef.indexOf('=');
-      final String name = (pos < 0) ? envDef : envDef.substring(0, pos);
-      final String value = (pos < 0) ? '' : envDef.substring(pos + 1);
+      var envDef = match[1];
+      var pos = envDef.indexOf('=');
+      var name = (pos < 0) ? envDef : envDef.substring(0, pos);
+      var value = (pos < 0) ? '' : envDef.substring(pos + 1);
       environment ??= <String, String>{};
       environment[name] = value;
     }
@@ -1187,7 +1156,7 @@
     matches = packageRootRegExp.allMatches(contents);
     for (var match in matches) {
       if (packageRoot != null || packages != null) {
-        throw new Exception(
+        throw Exception(
             'More than one "// Package... line in test ${uri.toFilePath()}');
       }
       packageRoot = match[1];
@@ -1195,15 +1164,14 @@
         // PackageRoot=none means that no packages or package-root option
         // should be given. Any other value overrides package-root and
         // removes any packages option.  Don't use with // Packages=.
-        packageRoot =
-            uri.resolveUri(new Uri.directory(packageRoot)).toFilePath();
+        packageRoot = uri.resolveUri(Uri.directory(packageRoot)).toFilePath();
       }
     }
 
     matches = packagesRegExp.allMatches(contents);
     for (var match in matches) {
       if (packages != null || packageRoot != null) {
-        throw new Exception(
+        throw Exception(
             'More than one "// Package..." line in test ${uri.toFilePath()}');
       }
       packages = match[1];
@@ -1211,7 +1179,7 @@
         // Packages=none means that no packages or package-root option
         // should be given. Any other value overrides packages and removes
         // any package-root option. Don't use with // PackageRoot=.
-        packages = uri.resolveUri(new Uri.file(packages)).toFilePath();
+        packages = uri.resolveUri(Uri.file(packages)).toFilePath();
       }
     }
 
@@ -1227,14 +1195,14 @@
       sharedObjects.addAll(wordSplit(match[1]));
     }
 
-    var isMultitest = multiTestRegExp.hasMatch(contents);
-    var isMultiHtmlTest = multiHtmlTestRegExp.hasMatch(contents);
+    var isMultitest = _multiTestRegExp.hasMatch(contents);
+    var isMultiHtmlTest = _multiHtmlTestRegExp.hasMatch(contents);
     var isolateMatch = isolateStubsRegExp.firstMatch(contents);
     var isolateStubs = isolateMatch != null ? isolateMatch[1] : '';
     var containsDomImport = domImportRegExp.hasMatch(contents);
 
     var subtestNames = <String>[];
-    var matchesIter = multiHtmlTestGroupRegExp.allMatches(contents).iterator;
+    var matchesIter = _multiHtmlTestGroupRegExp.allMatches(contents).iterator;
     while (matchesIter.moveNext() && isMultiHtmlTest) {
       var fullMatch = matchesIter.current.group(0);
       subtestNames.add(fullMatch.substring(fullMatch.indexOf("'") + 1));
@@ -1294,10 +1262,10 @@
 
   Map<String, dynamic> optionsFromKernelFile() {
     return const {
-      "vmOptions": const [const <String>[]],
-      "sharedOptions": const <String>[],
-      "dart2jsOptions": const <String>[],
-      "dartOptions": const <String>[],
+      "vmOptions": [<String>[]],
+      "sharedOptions": <String>[],
+      "dart2jsOptions": <String>[],
+      "dartOptions": <String>[],
       "packageRoot": null,
       "packages": null,
       "hasSyntaxError": false,
@@ -1306,14 +1274,14 @@
       "hasStaticWarning": false,
       "isMultitest": false,
       "isMultiHtmlTest": false,
-      "subtestNames": const [],
+      "subtestNames": [],
       "isolateStubs": '',
       "containsDomImport": false,
     };
   }
 
   List<List<String>> getVmOptions(Map<String, dynamic> optionsFromFile) {
-    const compilers = const [
+    const compilers = [
       Compiler.none,
       Compiler.dartk,
       Compiler.dartkb,
@@ -1323,7 +1291,7 @@
       Compiler.appJitk,
     ];
 
-    const runtimes = const [Runtime.none, Runtime.dartPrecompiled, Runtime.vm];
+    const runtimes = [Runtime.none, Runtime.dartPrecompiled, Runtime.vm];
 
     var needsVmOptions = compilers.contains(configuration.compiler) &&
         runtimes.contains(configuration.runtime);
@@ -1346,7 +1314,7 @@
     var dir = filePath.directoryPath;
     var nameNoExt = filePath.filenameWithoutExtension;
     var customHtmlPath = dir.append('$nameNoExt.html');
-    var customHtml = new File(customHtmlPath.toNativePath());
+    var customHtml = File(customHtmlPath.toNativePath());
     if (!customHtml.existsSync()) {
       super._enqueueBrowserTest(
           packageRoot, packages, info, testName, expectations);
@@ -1361,7 +1329,7 @@
 
 class AnalyzeLibraryTestSuite extends StandardTestSuite {
   static Path _libraryPath(TestConfiguration configuration) =>
-      new Path(configuration.useSdk
+      Path(configuration.useSdk
           ? '${configuration.buildDirectory}/dart-sdk'
           : 'sdk');
 
@@ -1375,9 +1343,9 @@
       const ['--fatal-warnings', '--fatal-type-errors', '--sdk-warnings'];
 
   Future enqueueTests() {
-    var group = new FutureGroup();
+    var group = FutureGroup();
 
-    var dir = new Directory(suiteDir.append('lib').toNativePath());
+    var dir = Directory(suiteDir.append('lib').toNativePath());
     if (dir.existsSync()) {
       enqueueDirectory(dir, group);
     }
diff --git a/tools/testing/dart/http_server.dart b/pkg/test_runner/lib/src/testing_servers.dart
similarity index 72%
rename from tools/testing/dart/http_server.dart
rename to pkg/test_runner/lib/src/testing_servers.dart
index ea8f3c3..92d3949 100644
--- a/tools/testing/dart/http_server.dart
+++ b/pkg/test_runner/lib/src/testing_servers.dart
@@ -8,14 +8,13 @@
 
 import 'package:package_resolver/package_resolver.dart';
 
-import 'configuration.dart';
-import 'repository.dart';
-import 'utils.dart';
-import 'vendored_pkg/args/args.dart';
+import 'package:test_runner/src/configuration.dart';
+import 'package:test_runner/src/repository.dart';
+import 'package:test_runner/src/utils.dart';
 
 class DispatchingServer {
   HttpServer server;
-  Map<String, Function> _handlers = new Map<String, Function>();
+  Map<String, Function> _handlers = {};
   Function _notFound;
 
   DispatchingServer(
@@ -58,62 +57,14 @@
 /// In case a path does not refer to a file but rather to a directory, a
 /// directory listing will be displayed.
 
-const PREFIX_BUILDDIR = 'root_build';
-const PREFIX_DARTDIR = 'root_dart';
+const prefixBuildDir = 'root_build';
+const prefixDartDir = 'root_dart';
 
-void main(List<String> arguments) {
-  /** Convenience method for local testing. */
-  var parser = new ArgParser();
-  parser.addOption('port',
-      abbr: 'p',
-      help: 'The main server port we wish to respond to requests.',
-      defaultsTo: '0');
-  parser.addOption('crossOriginPort',
-      abbr: 'c',
-      help: 'A different port that accepts request from the main server port.',
-      defaultsTo: '0');
-  parser.addFlag('help',
-      abbr: 'h', negatable: false, help: 'Print this usage information.');
-  parser.addOption('build-directory', help: 'The build directory to use.');
-  parser.addOption('package-root', help: 'The package root to use.');
-  parser.addOption('packages', help: 'The package spec file to use.');
-  parser.addOption('network',
-      help: 'The network interface to use.', defaultsTo: '0.0.0.0');
-  parser.addFlag('csp',
-      help: 'Use Content Security Policy restrictions.', defaultsTo: false);
-  parser.addOption('runtime',
-      help: 'The runtime we are using (for csp flags).', defaultsTo: 'none');
-
-  var args = parser.parse(arguments);
-  if (args['help'] as bool) {
-    print(parser.getUsage());
-  } else {
-    var servers = new TestingServers(
-        args['build-directory'] as String,
-        args['csp'] as bool,
-        Runtime.find(args['runtime'] as String),
-        null,
-        args['package-root'] as String,
-        args['packages'] as String);
-    var port = int.parse(args['port'] as String);
-    var crossOriginPort = int.parse(args['crossOriginPort'] as String);
-    servers
-        .startServers(args['network'] as String,
-            port: port, crossOriginPort: crossOriginPort)
-        .then((_) {
-      DebugLogger.info('Server listening on port ${servers.port}');
-      DebugLogger.info('Server listening on port ${servers.crossOriginPort}');
-    });
-  }
-}
-
-/**
- * Runs a set of servers that are initialized specifically for the needs of our
- * test framework, such as dealing with package-root.
- */
+/// Runs a set of servers that are initialized specifically for the needs of our
+/// test framework, such as dealing with package-root.
 class TestingServers {
-  static final _CACHE_EXPIRATION_IN_SECONDS = 30;
-  static final _HARMLESS_REQUEST_PATH_ENDINGS = [
+  static final _cacheExpirationSeconds = 30;
+  static final _harmlessRequestPathSuffixes = [
     "/apple-touch-icon.png",
     "/apple-touch-icon-precomposed.png",
     "/favicon.ico",
@@ -138,20 +89,20 @@
       String dartDirectory,
       String packageRoot,
       String packages]) {
-    _buildDirectory = Uri.base.resolveUri(new Uri.directory(buildDirectory));
+    _buildDirectory = Uri.base.resolveUri(Uri.directory(buildDirectory));
     if (dartDirectory == null) {
       _dartDirectory = Repository.uri;
     } else {
-      _dartDirectory = Uri.base.resolveUri(new Uri.directory(dartDirectory));
+      _dartDirectory = Uri.base.resolveUri(Uri.directory(dartDirectory));
     }
     if (packageRoot == null) {
       if (packages == null) {
         _packages = _dartDirectory.resolve('.packages');
       } else {
-        _packages = new Uri.file(packages);
+        _packages = Uri.file(packages);
       }
     } else {
-      _packageRoot = new Uri.directory(packageRoot);
+      _packageRoot = Uri.directory(packageRoot);
     }
   }
 
@@ -163,20 +114,19 @@
 
   DispatchingServer get server => _server;
 
-  /**
-   * [startServers] will start two Http servers.
-   * The first server listens on [port] and sets
-   *   "Access-Control-Allow-Origin: *"
-   * The second server listens on [crossOriginPort] and sets
-   *   "Access-Control-Allow-Origin: client:port1
-   *   "Access-Control-Allow-Credentials: true"
-   */
+  /// [startServers] will start two Http servers.
+  ///
+  /// The first server listens on [port] and sets
+  ///   "Access-Control-Allow-Origin: *"
+  /// The second server listens on [crossOriginPort] and sets
+  ///   "Access-Control-Allow-Origin: client:port1
+  ///   "Access-Control-Allow-Credentials: true"
   Future startServers(String host,
-      {int port: 0, int crossOriginPort: 0}) async {
+      {int port = 0, int crossOriginPort = 0}) async {
     if (_packages != null) {
       _resolver = await SyncPackageResolver.loadConfig(_packages);
     } else {
-      _resolver = new SyncPackageResolver.root(_packageRoot);
+      _resolver = SyncPackageResolver.root(_packageRoot);
     }
     _server = await _startHttpServer(host, port: port);
     await _startHttpServer(host,
@@ -186,10 +136,10 @@
   /// Gets the command line string to spawn the server.
   String get commandLine {
     var dart = Platform.resolvedExecutable;
-    var script = _dartDirectory.resolve('tools/testing/dart/http_server.dart');
+    var script = _dartDirectory.resolve('pkg/test_runner/bin/http_server.dart');
     var buildDirectory = _buildDirectory.toFilePath();
 
-    var command = [
+    return [
       dart,
       script.toFilePath(),
       '-p',
@@ -199,20 +149,13 @@
       '--network',
       network,
       '--build-directory=$buildDirectory',
-      '--runtime=${runtime.name}'
-    ];
-
-    if (useContentSecurityPolicy) {
-      command.add('--csp');
-    }
-
-    if (_packages != null) {
-      command.add('--packages=${_packages.toFilePath()}');
-    } else if (_packageRoot != null) {
-      command.add('--package-root=${_packageRoot.toFilePath()}');
-    }
-
-    return command.join(' ');
+      '--runtime=${runtime.name}',
+      if (useContentSecurityPolicy) '--csp',
+      if (_packages != null)
+        '--packages=${_packages.toFilePath()}'
+      else if (_packageRoot != null)
+        '--package-root=${_packageRoot.toFilePath()}'
+    ].join(' ');
   }
 
   void stopServers() {
@@ -226,17 +169,17 @@
   }
 
   Future<DispatchingServer> _startHttpServer(String host,
-      {int port: 0, int allowedPort: -1}) {
+      {int port = 0, int allowedPort = -1}) {
     return HttpServer.bind(host, port).then((HttpServer httpServer) {
-      var server = new DispatchingServer(httpServer, _onError, _sendNotFound);
+      var server = DispatchingServer(httpServer, _onError, _sendNotFound);
       server.addHandler('/echo', _handleEchoRequest);
       server.addHandler('/ws', _handleWebSocketRequest);
       fileHandler(HttpRequest request) {
         _handleFileOrDirectoryRequest(request, allowedPort);
       }
 
-      server.addHandler('/$PREFIX_BUILDDIR', fileHandler);
-      server.addHandler('/$PREFIX_DARTDIR', fileHandler);
+      server.addHandler('/$prefixBuildDir', fileHandler);
+      server.addHandler('/$prefixDartDir', fileHandler);
       server.addHandler('/packages', fileHandler);
       _serverList.add(httpServer);
       return server;
@@ -247,13 +190,12 @@
       HttpRequest request, int allowedPort) async {
     // Enable browsers to cache file/directory responses.
     var response = request.response;
-    response.headers
-        .set("Cache-Control", "max-age=$_CACHE_EXPIRATION_IN_SECONDS");
+    response.headers.set("Cache-Control", "max-age=$_cacheExpirationSeconds");
     try {
       var path = _getFileUriFromRequestUri(request.uri);
       if (path != null) {
-        var file = new File.fromUri(path);
-        var directory = new Directory.fromUri(path);
+        var file = File.fromUri(path);
+        var directory = Directory.fromUri(path);
         if (await file.exists()) {
           _sendFileContent(request, response, allowedPort, file);
         } else if (await directory.exists()) {
@@ -265,9 +207,9 @@
       } else {
         if (request.uri.path == '/') {
           var entries = [
-            new _Entry('root_dart', 'root_dart/'),
-            new _Entry('root_build', 'root_build/'),
-            new _Entry('echo', 'echo')
+            _Entry('root_dart', 'root_dart/'),
+            _Entry('root_build', 'root_build/'),
+            _Entry('echo', 'echo')
           ];
           _sendDirectoryListing(entries, request, response);
         } else {
@@ -316,32 +258,32 @@
     if (pathSegments.length == 0) return null;
     int packagesIndex = pathSegments.indexOf('packages');
     if (packagesIndex != -1) {
-      var packageUri = new Uri(
+      var packageUri = Uri(
           scheme: 'package',
           pathSegments: pathSegments.skip(packagesIndex + 1));
       return _resolver.resolveUri(packageUri);
     }
-    if (pathSegments[0] == PREFIX_BUILDDIR) {
+    if (pathSegments[0] == prefixBuildDir) {
       return _buildDirectory.resolve(pathSegments.skip(1).join('/'));
     }
-    if (pathSegments[0] == PREFIX_DARTDIR) {
+    if (pathSegments[0] == prefixDartDir) {
       return _dartDirectory.resolve(pathSegments.skip(1).join('/'));
     }
     return null;
   }
 
   Future<List<_Entry>> _listDirectory(Directory directory) {
-    var completer = new Completer<List<_Entry>>();
+    var completer = Completer<List<_Entry>>();
     var entries = <_Entry>[];
 
     directory.list().listen((FileSystemEntity fse) {
       var segments = fse.uri.pathSegments;
       if (fse is File) {
         var filename = segments.last;
-        entries.add(new _Entry(filename, filename));
+        entries.add(_Entry(filename, filename));
       } else if (fse is Directory) {
         var dirname = segments[segments.length - 2];
-        entries.add(new _Entry(dirname, '$dirname/'));
+        entries.add(_Entry(dirname, '$dirname/'));
       }
     }, onDone: () {
       completer.complete(entries);
@@ -439,7 +381,7 @@
 
   void _sendNotFound(HttpRequest request) {
     bool isHarmlessPath(String path) {
-      return _HARMLESS_REQUEST_PATH_ENDINGS.any((pattern) {
+      return _harmlessRequestPathSuffixes.any((pattern) {
         return path.contains(pattern);
       });
     }
@@ -478,7 +420,7 @@
   }
 }
 
-// Helper class for displaying directory listings.
+/// Helper class for displaying directory listings.
 class _Entry implements Comparable<_Entry> {
   final String name;
   final String displayName;
diff --git a/tools/testing/dart/utils.dart b/pkg/test_runner/lib/src/utils.dart
similarity index 80%
rename from tools/testing/dart/utils.dart
rename to pkg/test_runner/lib/src/utils.dart
index 0da3dbf..1d1d0a5 100644
--- a/tools/testing/dart/utils.dart
+++ b/pkg/test_runner/lib/src/utils.dart
@@ -9,17 +9,17 @@
 import 'configuration.dart';
 import 'path.dart';
 
-// This is the maximum time we expect stdout/stderr of subprocesses to deliver
-// data after we've got the exitCode.
-const Duration MAX_STDIO_DELAY = const Duration(seconds: 30);
+/// This is the maximum time we expect stdout/stderr of subprocesses to deliver
+/// data after we've got the exitCode.
+const Duration maxStdioDelay = Duration(seconds: 30);
 
-String MAX_STDIO_DELAY_PASSED_MESSAGE =
+final maxStdioDelayPassedMessage =
     """Not waiting for stdout/stderr from subprocess anymore
- ($MAX_STDIO_DELAY passed). Please note that this could be an indicator
+ ($maxStdioDelay passed). Please note that this could be an indicator
  that there is a hanging process which we were unable to kill.""";
 
 /// The names of the packages that are available for use in tests.
-const testPackages = const [
+const testPackages = [
   "async_helper",
   "collection",
   "expect",
@@ -34,12 +34,10 @@
 class DebugLogger {
   static IOSink _sink;
 
-  /**
-   * If [path] was null, the DebugLogger will write messages to stdout.
-   */
+  /// If [path] was null, the DebugLogger will write messages to stdout.
   static void init(Path path) {
     if (path != null) {
-      _sink = new File(path.toNativePath()).openWrite(mode: FileMode.append);
+      _sink = File(path.toNativePath()).openWrite(mode: FileMode.append);
     }
   }
 
@@ -82,18 +80,19 @@
     }
   }
 
-  static String get _datetime => "${new DateTime.now()}";
+  static String get _datetime => "${DateTime.now()}";
 }
 
-String prettifyJson(Object json, {int startIndentation: 0, int shiftWidth: 6}) {
+String prettifyJson(Object json,
+    {int startIndentation = 0, int shiftWidth = 6}) {
   int currentIndentation = startIndentation;
-  var buffer = new StringBuffer();
+  var buffer = StringBuffer();
 
   String indentationString() {
-    return new List.filled(currentIndentation, ' ').join('');
+    return List.filled(currentIndentation, ' ').join('');
   }
 
-  addString(String s, {bool indentation: true, bool newLine: true}) {
+  addString(String s, {bool indentation = true, bool newLine = true}) {
     if (indentation) {
       buffer.write(indentationString());
     }
@@ -102,7 +101,7 @@
   }
 
   prettifyJsonInternal(Object obj,
-      {bool indentation: true, bool newLine: true}) {
+      {bool indentation = true, bool newLine = true}) {
     if (obj is List) {
       addString("[", indentation: indentation);
       currentIndentation += shiftWidth;
@@ -132,15 +131,13 @@
   return buffer.toString();
 }
 
-/**
- * [areByteArraysEqual] compares a range of bytes from [buffer1] with a
- * range of bytes from [buffer2].
- *
- * Returns [true] if the [count] bytes in [buffer1] (starting at
- * [offset1]) match the [count] bytes in [buffer2] (starting at
- * [offset2]).
- * Otherwise [false] is returned.
- */
+/// [areByteArraysEqual] compares a range of bytes from [buffer1] with a
+/// range of bytes from [buffer2].
+///
+/// Returns [true] if the [count] bytes in [buffer1] (starting at
+/// [offset1]) match the [count] bytes in [buffer2] (starting at
+/// [offset2]).
+/// Otherwise [false] is returned.
 bool areByteArraysEqual(
     List<int> buffer1, int offset1, List<int> buffer2, int offset2, int count) {
   if ((offset1 + count) > buffer1.length ||
@@ -156,12 +153,10 @@
   return true;
 }
 
-/**
- * [findBytes] searches for [pattern] in [data] beginning at [startPos].
- *
- * Returns [true] if [pattern] was found in [data].
- * Otherwise [false] is returned.
- */
+/// [findBytes] searches for [pattern] in [data] beginning at [startPos].
+///
+/// Returns [true] if [pattern] was found in [data].
+/// Otherwise [false] is returned.
 int findBytes(List<int> data, List<int> pattern, [int startPos = 0]) {
   // TODO(kustermann): Use one of the fast string-matching algorithms!
   for (int i = startPos; i < (data.length - pattern.length); i++) {
@@ -201,7 +196,7 @@
 }
 
 String indent(String string, int numSpaces) {
-  var spaces = new List.filled(numSpaces, ' ').join('');
+  var spaces = List.filled(numSpaces, ' ').join('');
   return string
       .replaceAll('\r\n', '\n')
       .split('\n')
@@ -232,8 +227,8 @@
   }
 }
 
-// This function is pretty stupid and only puts quotes around an argument if
-// it the argument contains a space.
+/// This function is pretty stupid and only puts quotes around an argument if
+/// it the argument contains a space.
 String escapeCommandLineArgument(String argument) {
   if (argument.contains(' ')) {
     return '"$argument"';
@@ -263,7 +258,7 @@
         addJson(object[key]);
       }
     } else {
-      throw new Exception("Can't build hashcode for non json-like object "
+      throw Exception("Can't build hashcode for non json-like object "
           "(${object.runtimeType})");
     }
   }
@@ -296,7 +291,7 @@
     }
     return false;
   } else {
-    throw new Exception("Can't compare two non json-like objects "
+    throw Exception("Can't compare two non json-like objects "
         "(a: ${a.runtimeType}, b: ${b.runtimeType})");
   }
 }
@@ -315,57 +310,51 @@
 class LastModifiedCache {
   Map<String, DateTime> _cache = <String, DateTime>{};
 
-  /**
-   * Returns the last modified date of the given [uri].
-   *
-   * The return value will be cached for future queries. If [uri] is a local
-   * file, it's last modified [Date] will be returned. If the file does not
-   * exist, null will be returned instead.
-   * In case [uri] is not a local file, this method will always return
-   * the current date.
-   */
+  /// Returns the last modified date of the given [uri].
+  ///
+  /// The return value will be cached for future queries. If [uri] is a local
+  /// file, it's last modified [Date] will be returned. If the file does not
+  /// exist, null will be returned instead.
+  /// In case [uri] is not a local file, this method will always return
+  /// the current date.
   DateTime getLastModified(Uri uri) {
     if (uri.scheme == "file") {
       if (_cache.containsKey(uri.path)) {
         return _cache[uri.path];
       }
-      var file = new File(new Path(uri.path).toNativePath());
+      var file = File(Path(uri.path).toNativePath());
       _cache[uri.path] = file.existsSync() ? file.lastModifiedSync() : null;
       return _cache[uri.path];
     }
-    return new DateTime.now();
+    return DateTime.now();
   }
 }
 
 class ExistsCache {
   Map<String, bool> _cache = <String, bool>{};
 
-  /**
-   * Returns true if the file in [path] exists, false otherwise.
-   *
-   * The information will be cached.
-   */
+  /// Returns true if the file in [path] exists, false otherwise.
+  ///
+  /// The information will be cached.
   bool doesFileExist(String path) {
     if (!_cache.containsKey(path)) {
-      _cache[path] = new File(path).existsSync();
+      _cache[path] = File(path).existsSync();
     }
     return _cache[path];
   }
 }
 
 class TestUtils {
-  static LastModifiedCache lastModifiedCache = new LastModifiedCache();
-  static ExistsCache existsCache = new ExistsCache();
+  static LastModifiedCache lastModifiedCache = LastModifiedCache();
+  static ExistsCache existsCache = ExistsCache();
 
-  /**
-   * Creates a directory using a [relativePath] to an existing
-   * [base] directory if that [relativePath] does not already exist.
-   */
+  /// Creates a directory using a [relativePath] to an existing
+  /// [base] directory if that [relativePath] does not already exist.
   static Directory mkdirRecursive(Path base, Path relativePath) {
     if (relativePath.isAbsolute) {
-      base = new Path('/');
+      base = Path('/');
     }
-    Directory dir = new Directory(base.toNativePath());
+    Directory dir = Directory(base.toNativePath());
     assert(dir.existsSync());
     var segments = relativePath.segments();
     for (String segment in segments) {
@@ -376,7 +365,7 @@
         // Skip the directory creation for a path like "/E:".
         continue;
       }
-      dir = new Directory(base.toNativePath());
+      dir = Directory(base.toNativePath());
       if (!dir.existsSync()) {
         dir.createSync();
       }
@@ -385,23 +374,19 @@
     return dir;
   }
 
-  /**
-   * Keep a map of files copied to avoid race conditions.
-   */
+  /// Keep a map of files copied to avoid race conditions.
   static Map<String, Future> _copyFilesMap = {};
 
-  /**
-   * Copy a [source] file to a new place.
-   * Assumes that the directory for [dest] already exists.
-   */
+  /// Copy a [source] file to a new place.
+  /// Assumes that the directory for [dest] already exists.
   static Future copyFile(Path source, Path dest) {
     return _copyFilesMap.putIfAbsent(dest.toNativePath(),
-        () => new File(source.toNativePath()).copy(dest.toNativePath()));
+        () => File(source.toNativePath()).copy(dest.toNativePath()));
   }
 
   static Future copyDirectory(String source, String dest) {
-    source = new Path(source).toNativePath();
-    dest = new Path(dest).toNativePath();
+    source = Path(source).toNativePath();
+    dest = Path(dest).toNativePath();
 
     var executable = 'cp';
     var args = ['-Rp', source, dest];
@@ -411,7 +396,7 @@
     }
     return Process.run(executable, args).then((ProcessResult result) {
       if (result.exitCode != 0) {
-        throw new Exception("Failed to execute '$executable "
+        throw Exception("Failed to execute '$executable "
             "${args.join(' ')}'.");
       }
     });
@@ -422,18 +407,18 @@
     // deleting them. Use the system tools to delete our long paths.
     // See issue 16264.
     if (Platform.operatingSystem == 'windows') {
-      var native_path = new Path(path).toNativePath();
+      var native_path = Path(path).toNativePath();
       // Running this in a shell sucks, but rmdir is not part of the standard
       // path.
       return Process.run('rmdir', ['/s', '/q', native_path], runInShell: true)
           .then((ProcessResult result) {
         if (result.exitCode != 0) {
-          throw new Exception('Can\'t delete path $native_path. '
+          throw Exception('Can\'t delete path $native_path. '
               'This path might be too long');
         }
       });
     } else {
-      var dir = new Directory(path);
+      var dir = Directory(path);
       return dir.delete(recursive: true);
     }
   }
@@ -459,7 +444,7 @@
     }
   }
 
-  static final debugLogFilePath = new Path(".debug.log");
+  static final debugLogFilePath = Path(".debug.log");
 
   /// If test.py was invoked with '--write-results' it will write
   /// test outcomes to this file in the '--output-directory'.
@@ -479,10 +464,11 @@
     }
   }
 
-  static int shortNameCounter = 0; // Make unique short file names on Windows.
+  /// Make unique short file names on Windows.
+  static int shortNameCounter = 0;
 
   static String getShortName(String path) {
-    const pathReplacements = const {
+    const pathReplacements = {
       "tests_co19_src_Language_12_Expressions_14_Function_Invocation_":
           "co19_fn_invoke_",
       "tests_co19_src_LayoutTests_fast_css_getComputedStyle_getComputedStyle-":
@@ -513,10 +499,10 @@
       "tests_co19_src_WebPlatformTest_html-templates_parsing-html-"
           "templates_appending-to-a-template_": "co19_htmltemplates_append_",
       "tests_co19_src_WebPlatformTest_html-templates_parsing-html-"
-          "templates_clearing-the-stack-back-to-a-given-context_":
+              "templates_clearing-the-stack-back-to-a-given-context_":
           "co19_htmltemplates_clearstack_",
       "tests_co19_src_WebPlatformTest_html-templates_parsing-html-"
-          "templates_creating-an-element-for-the-token_":
+              "templates_creating-an-element-for-the-token_":
           "co19_htmltemplates_create_",
       "tests_co19_src_WebPlatformTest_html-templates_template-element"
           "_template-": "co19_htmltemplates_element-",
diff --git a/tools/testing/dart/vendored_pkg/README.txt b/pkg/test_runner/lib/src/vendored_pkg/README.txt
similarity index 100%
rename from tools/testing/dart/vendored_pkg/README.txt
rename to pkg/test_runner/lib/src/vendored_pkg/README.txt
diff --git a/tools/testing/dart/vendored_pkg/args/args.dart b/pkg/test_runner/lib/src/vendored_pkg/args/args.dart
similarity index 100%
rename from tools/testing/dart/vendored_pkg/args/args.dart
rename to pkg/test_runner/lib/src/vendored_pkg/args/args.dart
diff --git a/tools/testing/dart/vendored_pkg/args/src/parser.dart b/pkg/test_runner/lib/src/vendored_pkg/args/src/parser.dart
similarity index 100%
rename from tools/testing/dart/vendored_pkg/args/src/parser.dart
rename to pkg/test_runner/lib/src/vendored_pkg/args/src/parser.dart
diff --git a/tools/testing/dart/vendored_pkg/args/src/usage.dart b/pkg/test_runner/lib/src/vendored_pkg/args/src/usage.dart
similarity index 100%
rename from tools/testing/dart/vendored_pkg/args/src/usage.dart
rename to pkg/test_runner/lib/src/vendored_pkg/args/src/usage.dart
diff --git a/pkg/test_runner/pubspec.yaml b/pkg/test_runner/pubspec.yaml
new file mode 100644
index 0000000..876918c
--- /dev/null
+++ b/pkg/test_runner/pubspec.yaml
@@ -0,0 +1,17 @@
+# 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.
+
+name: test_runner
+publish_to: none
+environment:
+  sdk: "^2.3.0"
+dependencies:
+  expect:
+    path: ../expect
+  package_resolver:
+    path: ../../third_party/pkg_tested/package_resolver
+  smith:
+    path: ../smith
+  status_file:
+    path: ../status_file
\ No newline at end of file
diff --git a/tests/standalone_2/io/dependency_graph_test.dart b/pkg/test_runner/test/dependency_graph_test.dart
similarity index 93%
rename from tests/standalone_2/io/dependency_graph_test.dart
rename to pkg/test_runner/test/dependency_graph_test.dart
index 3f7dab1..86c8966 100644
--- a/tests/standalone_2/io/dependency_graph_test.dart
+++ b/pkg/test_runner/test/dependency_graph_test.dart
@@ -4,10 +4,10 @@
 
 import 'package:expect/expect.dart';
 
-import '../../../tools/testing/dart/dependency_graph.dart';
+import 'package:test_runner/src/dependency_graph.dart';
 
 main() {
-  var graph = new Graph<int>();
+  var graph = Graph<int>();
   var numberOfEvents = 0;
   var addEventAssertions = [];
   var changeEventAssertions = [];
@@ -45,7 +45,7 @@
     });
   }
 
-  var node1, node2, node3;
+  Node<int> node1, node2, node3;
 
   node1 = newNode(1, []);
   changeState(node1, NodeState.processing);
diff --git a/tests/standalone_2/io/skipping_dart2js_compilations_helper.dart b/pkg/test_runner/test/skipping_dart2js_compilations_helper.dart
similarity index 89%
rename from tests/standalone_2/io/skipping_dart2js_compilations_helper.dart
rename to pkg/test_runner/test/skipping_dart2js_compilations_helper.dart
index cda26da..74fc991 100644
--- a/tests/standalone_2/io/skipping_dart2js_compilations_helper.dart
+++ b/pkg/test_runner/test/skipping_dart2js_compilations_helper.dart
@@ -6,6 +6,6 @@
 
 main(List<String> arguments) {
   var outputFile = arguments[0];
-  var file = new File(outputFile);
+  var file = File(outputFile);
   file.createSync();
 }
diff --git a/tests/standalone_2/io/skipping_dart2js_compilations_test.dart b/pkg/test_runner/test/skipping_dart2js_compilations_test.dart
similarity index 77%
rename from tests/standalone_2/io/skipping_dart2js_compilations_test.dart
rename to pkg/test_runner/test/skipping_dart2js_compilations_test.dart
index 4ddd934..16fb24b 100644
--- a/tests/standalone_2/io/skipping_dart2js_compilations_test.dart
+++ b/pkg/test_runner/test/skipping_dart2js_compilations_test.dart
@@ -16,19 +16,19 @@
  * output (+deps file), dart application)
  */
 
-import 'package:expect/expect.dart';
 import 'dart:async';
 import 'dart:io';
-import '../../../tools/testing/dart/command.dart';
-import '../../../tools/testing/dart/command_output.dart';
-import '../../../tools/testing/dart/path.dart';
-import '../../../tools/testing/dart/repository.dart';
-import '../../../tools/testing/dart/test_runner.dart' as runner;
 
-/**
- * This class is reponsible for setting up the files necessary for this test
- * as well as touching a file.
- */
+import 'package:expect/expect.dart';
+
+import 'package:test_runner/src/command.dart';
+import 'package:test_runner/src/command_output.dart';
+import 'package:test_runner/src/path.dart';
+import 'package:test_runner/src/repository.dart';
+import 'package:test_runner/src/test_case.dart';
+
+/// This class is reponsible for setting up the files necessary for this test
+/// as well as touching a file.
 class FileUtils {
   Directory tempDir;
   File testJs;
@@ -57,7 +57,7 @@
     }
     if (createJsDeps) {
       testJsDeps = _createFile(testJsDepsFilePath);
-      var path = new Path(tempDir.path).append("test.dart").absolute;
+      var path = Path(tempDir.path).append("test.dart").absolute;
       _writeToFile(testJsDeps, "file://$path");
     }
   }
@@ -69,7 +69,7 @@
     if (testSnapshot != null) testSnapshot.deleteSync();
 
     // if the script did run, it created this file, so we need to delete it
-    File file = new File(scriptOutputPath.toNativePath());
+    File file = File(scriptOutputPath.toNativePath());
     if (file.existsSync()) {
       file.deleteSync();
     }
@@ -78,25 +78,23 @@
   }
 
   Path get scriptOutputPath {
-    return new Path(tempDir.path)
-        .append('created_if_command_did_run.txt')
-        .absolute;
+    return Path(tempDir.path).append('created_if_command_did_run.txt').absolute;
   }
 
   Path get testDartFilePath {
-    return new Path(tempDir.path).append('test.dart').absolute;
+    return Path(tempDir.path).append('test.dart').absolute;
   }
 
   Path get testJsFilePath {
-    return new Path(tempDir.path).append('test.js').absolute;
+    return Path(tempDir.path).append('test.js').absolute;
   }
 
   Path get testJsDepsFilePath {
-    return new Path(tempDir.path).append('test.js.deps').absolute;
+    return Path(tempDir.path).append('test.js.deps').absolute;
   }
 
   Path get testSnapshotFilePath {
-    return new Path(tempDir.path).append('test_dart2js.snapshot').absolute;
+    return Path(tempDir.path).append('test_dart2js.snapshot').absolute;
   }
 
   void touchFile(File file) {
@@ -105,8 +103,8 @@
 
   void _writeToFile(File file, String content) {
     if (content != null) {
-      var fd = new File(file.resolveSymbolicLinksSync())
-          .openSync(mode: FileMode.write);
+      var fd =
+          File(file.resolveSymbolicLinksSync()).openSync(mode: FileMode.write);
       fd.writeStringSync(content);
       fd.closeSync();
     }
@@ -117,7 +115,7 @@
   }
 
   File _createFile(Path path) {
-    var file = new File(path.toNativePath());
+    var file = File(path.toNativePath());
     file.createSync();
     return file;
   }
@@ -135,10 +133,10 @@
     if (_shouldHaveRun) {
       Expect.equals(0, output.stdout.length);
       Expect.isTrue(
-          new File(fileUtils.scriptOutputPath.toNativePath()).existsSync());
+          File(fileUtils.scriptOutputPath.toNativePath()).existsSync());
     } else {
       Expect.isFalse(
-          new File(fileUtils.scriptOutputPath.toNativePath()).existsSync());
+          File(fileUtils.scriptOutputPath.toNativePath()).existsSync());
     }
   }
 }
@@ -159,40 +157,40 @@
 }
 
 void main() {
-  // This script is in [sdk]/tests/standalone/io.
+  // This script is in [sdk]/pkg/test_runner/test.
   Repository.uri = Platform.script.resolve('../../..');
 
-  var fs_noTestJs = new FileUtils(
+  var fs_noTestJs = FileUtils(
       createJs: false,
       createJsDeps: true,
       createDart: true,
       createSnapshot: true);
-  var fs_noTestJsDeps = new FileUtils(
+  var fs_noTestJsDeps = FileUtils(
       createJs: true,
       createJsDeps: false,
       createDart: true,
       createSnapshot: true);
-  var fs_noTestDart = new FileUtils(
+  var fs_noTestDart = FileUtils(
       createJs: true,
       createJsDeps: true,
       createDart: false,
       createSnapshot: true);
-  var fs_noTestSnapshot = new FileUtils(
+  var fs_noTestSnapshot = FileUtils(
       createJs: true,
       createJsDeps: true,
       createDart: true,
       createSnapshot: false);
-  var fs_notUpToDate_snapshot = new FileUtils(
+  var fs_notUpToDate_snapshot = FileUtils(
       createJs: true,
       createJsDeps: true,
       createDart: true,
       createSnapshot: true);
-  var fs_notUpToDate_dart = new FileUtils(
+  var fs_notUpToDate_dart = FileUtils(
       createJs: true,
       createJsDeps: true,
       createDart: true,
       createSnapshot: true);
-  var fs_upToDate = new FileUtils(
+  var fs_upToDate = FileUtils(
       createJs: true,
       createJsDeps: true,
       createDart: true,
@@ -213,9 +211,9 @@
     fs_upToDate.touchFile(fs_upToDate.testJs);
 
     Future runTest(String name, FileUtils fileUtils, bool shouldRun) {
-      var completedHandler = new CommandCompletedHandler(fileUtils, shouldRun);
-      var command = makeCompilationCommand(name, fileUtils);
-      var process = new runner.RunningProcess(command, 60);
+      var completedHandler = CommandCompletedHandler(fileUtils, shouldRun);
+      var command = makeCompilationCommand(name, fileUtils) as ProcessCommand;
+      var process = RunningProcess(command, 60);
       return process.run().then((CommandOutput output) {
         completedHandler.processCompletedTest(output);
       });
@@ -248,5 +246,5 @@
 
   // We need to wait some time to make sure that the files we 'touch' get a
   // bigger timestamp than the old ones
-  new Timer(new Duration(seconds: 1), touchFilesAndRunTests);
+  Timer(Duration(seconds: 1), touchFilesAndRunTests);
 }
diff --git a/tests/standalone_2/io/test_runner_test.dart b/pkg/test_runner/test/test_runner_test.dart
similarity index 64%
rename from tests/standalone_2/io/test_runner_test.dart
rename to pkg/test_runner/test/test_runner_test.dart
index 2a1b140..79305bc 100644
--- a/tests/standalone_2/io/test_runner_test.dart
+++ b/pkg/test_runner/test/test_runner_test.dart
@@ -2,27 +2,29 @@
 // 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.
 
+// TODO(rnystrom): This test is only run by the analyzer and front end
+// configurations, so nothing is actually *executing* it. It's likely broken.
+// We should either remove it or get it working again.
+
 import "dart:io";
 import "dart:async";
 
 import "package:status_file/expectation.dart";
 
-import "../../../tools/testing/dart/command.dart";
-import "../../../tools/testing/dart/configuration.dart";
-import "../../../tools/testing/dart/options.dart";
-import "../../../tools/testing/dart/repository.dart";
-import "../../../tools/testing/dart/test_runner.dart";
-import "../../../tools/testing/dart/test_suite.dart";
-import "../../../tools/testing/dart/test_progress.dart" as progress;
-import "process_test_util.dart";
+import "package:test_runner/src/command.dart";
+import "package:test_runner/src/configuration.dart";
+import "package:test_runner/src/options.dart";
+import "package:test_runner/src/process_queue.dart";
+import "package:test_runner/src/repository.dart";
+import "package:test_runner/src/test_case.dart";
+import "package:test_runner/src/test_suite.dart";
+import "package:test_runner/src/test_progress.dart" as progress;
 
 final DEFAULT_TIMEOUT = 20;
 final LONG_TIMEOUT = 30;
 
 List<String> packageOptions() {
-  if (Platform.packageRoot != null) {
-    return <String>['--package-root=${Platform.packageRoot}'];
-  } else if (Platform.packageConfig != null) {
+  if (Platform.packageConfig != null) {
     return <String>['--packages=${Platform.packageConfig}'];
   } else {
     return <String>[];
@@ -38,10 +40,8 @@
     numCompletedTests++;
     if (testCase.displayName == "fail-unexpected") {
       if (!testCase.unexpectedOutput) {
-        var stdout =
-            new String.fromCharCodes(testCase.lastCommandOutput.stdout);
-        var stderr =
-            new String.fromCharCodes(testCase.lastCommandOutput.stderr);
+        var stdout = String.fromCharCodes(testCase.lastCommandOutput.stdout);
+        var stderr = String.fromCharCodes(testCase.lastCommandOutput.stderr);
         print("stdout = [$stdout]");
         print("stderr = [$stderr]");
         throw "Test case ${testCase.displayName} passed unexpectedly, "
@@ -49,10 +49,8 @@
       }
     } else {
       if (testCase.unexpectedOutput) {
-        var stdout =
-            new String.fromCharCodes(testCase.lastCommandOutput.stdout);
-        var stderr =
-            new String.fromCharCodes(testCase.lastCommandOutput.stderr);
+        var stdout = String.fromCharCodes(testCase.lastCommandOutput.stdout);
+        var stderr = String.fromCharCodes(testCase.lastCommandOutput.stderr);
         print("stdout = [$stdout]");
         print("stderr = [$stderr]");
         throw "Test case ${testCase.displayName} failed, "
@@ -74,7 +72,7 @@
       : super(configuration, "CustomTestSuite", []);
 
   Future forEachTest(TestCaseEvent onTest, Map testCache, [onDone]) async {
-    void enqueueTestCase(testCase) {
+    void enqueueTestCase(TestCase testCase) {
       TestController.numTests++;
       onTest(testCase);
     }
@@ -97,14 +95,15 @@
     }
   }
 
-  TestCase _makeNormalTestCase(name, expectations) {
+  TestCase _makeNormalTestCase(
+      String name, Iterable<Expectation> expectations) {
     var args = packageOptions();
     args.addAll([Platform.script.toFilePath(), name]);
     var command = Command.process('custom', Platform.executable, args, {});
     return _makeTestCase(name, DEFAULT_TIMEOUT, command, expectations);
   }
 
-  _makeCrashTestCase(name, expectations) {
+  TestCase _makeCrashTestCase(String name, Iterable<Expectation> expectations) {
     var crashCommand = Command.process(
         'custom_crash', getProcessTestFileName(), ["0", "0", "1", "1"], {});
     // The crash test sometimes times out. Run it with a large timeout
@@ -115,25 +114,20 @@
     return _makeTestCase(name, LONG_TIMEOUT, crashCommand, expectations);
   }
 
-  _makeTestCase(name, timeout, command, expectations) {
-    var configuration = new OptionsParser().parse(['--timeout', '$timeout'])[0];
-    return new TestCase(name, [command], configuration,
-        new Set<Expectation>.from(expectations));
+  TestCase _makeTestCase(String name, timeout, Command command,
+      Iterable<Expectation> expectations) {
+    var configuration = OptionsParser().parse(['--timeout', '$timeout'])[0];
+    return TestCase(
+        name, [command], configuration, Set<Expectation>.from(expectations));
   }
 }
 
 void testProcessQueue() {
   var maxProcesses = 2;
   var maxBrowserProcesses = maxProcesses;
-  var config = new OptionsParser().parse(['--noBatch'])[0];
-  new ProcessQueue(
-      config,
-      maxProcesses,
-      maxBrowserProcesses,
-      new DateTime.now(),
-      [new CustomTestSuite(config)],
-      [new EventListener()],
-      TestController.finished);
+  var config = OptionsParser().parse(['--noBatch'])[0];
+  ProcessQueue(config, maxProcesses, maxBrowserProcesses, DateTime.now(),
+      [CustomTestSuite(config)], [EventListener()], TestController.finished);
 }
 
 class EventListener extends progress.EventListener {
@@ -160,10 +154,25 @@
         break;
       case 'timeout':
         // This process should be killed by the test after DEFAULT_TIMEOUT
-        new Timer(new Duration(hours: 42), () {});
+        Timer(Duration(hours: 42), () {});
         break;
       default:
         throw "Unknown option ${arguments[0]} passed to test_runner_test";
     }
   }
 }
+
+String getPlatformExecutableExtension() {
+  var os = Platform.operatingSystem;
+  if (os == 'windows') return '.exe';
+  return ''; // Linux and Mac OS.
+}
+
+String getProcessTestFileName() {
+  var extension = getPlatformExecutableExtension();
+  var executable = Platform.executable;
+  var dirIndex = executable.lastIndexOf('dart');
+  var buffer = StringBuffer(executable.substring(0, dirIndex));
+  buffer.write('process_test$extension');
+  return buffer.toString();
+}
diff --git a/pkg/test_runner/tool/co19.dart b/pkg/test_runner/tool/co19.dart
new file mode 100644
index 0000000..72b7a6d
--- /dev/null
+++ b/pkg/test_runner/tool/co19.dart
@@ -0,0 +1,55 @@
+// Copyright (c) 2012, 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.
+
+/// Tool for running co19 tests. Used when updating co19.
+///
+/// Currently, this tool is merely a convenience around multiple
+/// invocations of test.dart. Long term, we hope to evolve this into a
+/// script that can automate most of the tasks necessary when updating
+/// co19.
+///
+/// Usage:
+/// [: dart pkg/test_runner/tool/co19.dart :]
+import 'package:test_runner/src/configuration.dart';
+import 'package:test_runner/src/options.dart';
+import 'package:test_runner/src/test_configurations.dart';
+
+const List<String> _commonArguments = <String>[
+  '--report',
+  '--progress=diff',
+  'co19'
+];
+
+const List<List<String>> _commandLines = <List<String>>[
+  <String>['-mrelease,debug', '-rvm', '-cnone'],
+  <String>['-mrelease,debug', '-rvm', '-cnone', '--checked'],
+  <String>['-mrelease', '-rnone', '-cdart2analyzer'],
+  <String>['-mrelease', '-rd8', '-cdart2js', '--use-sdk'],
+  <String>['-mrelease', '-rd8,jsshell', '-cdart2js', '--use-sdk', '--minified'],
+  <String>['-mrelease', '-rd8,jsshell', '-cdart2js', '--use-sdk', '--checked'],
+  <String>[
+    '-mrelease',
+    '-rd8,jsshell',
+    '-cdart2js',
+    '--use-sdk',
+    '--checked',
+    '--fast-startup'
+  ],
+];
+
+void main(List<String> args) {
+  var optionsParser = OptionsParser();
+  var configurations = <TestConfiguration>[];
+  for (var commandLine in _commandLines) {
+    var arguments = <String>[];
+    arguments.addAll(_commonArguments);
+    arguments.addAll(args);
+    arguments.addAll(commandLine);
+    configurations.addAll(optionsParser.parse(arguments));
+  }
+
+  if (configurations != null || configurations.isNotEmpty) {
+    testConfigurations(configurations);
+  }
+}
diff --git a/pkg/testing/lib/src/test_dart.dart b/pkg/testing/lib/src/test_dart.dart
index bba559b..7cf1f89 100644
--- a/pkg/testing/lib/src/test_dart.dart
+++ b/pkg/testing/lib/src/test_dart.dart
@@ -52,7 +52,7 @@
     }
     List<String> processedArguments = <String>[];
     processedArguments.add(Uri.base
-        .resolve("tools/testing/dart/package_testing_support.dart")
+        .resolve("pkg/test_runner/bin/package_testing_support.dart")
         .toFilePath());
     for (String commandLine in commandLines) {
       String arguments = common;
diff --git a/pkg/vm/bin/kernel_service.dart b/pkg/vm/bin/kernel_service.dart
index be1e4ee..fc07655 100644
--- a/pkg/vm/bin/kernel_service.dart
+++ b/pkg/vm/bin/kernel_service.dart
@@ -145,11 +145,13 @@
 
       if (options.bytecode && errors.isEmpty) {
         await runWithFrontEndCompilerContext(script, options, component, () {
-          // TODO(alexmarkov): pass environment defines
-          // TODO(alexmarkov): disable source positions and local variables
-          // in VM PRODUCT mode.
+          // TODO(alexmarkov): disable source positions, local variables info
+          //  and source files in VM PRODUCT mode.
           generateBytecode(component,
-              emitSourcePositions: true, emitLocalVarInfo: true);
+              environmentDefines: options.environmentDefines,
+              emitSourcePositions: true,
+              emitLocalVarInfo: true,
+              emitSourceFiles: true);
         });
       }
 
diff --git a/pkg/vm/lib/bytecode/ast_remover.dart b/pkg/vm/lib/bytecode/ast_remover.dart
deleted file mode 100644
index e7a28d1..0000000
--- a/pkg/vm/lib/bytecode/ast_remover.dart
+++ /dev/null
@@ -1,181 +0,0 @@
-// Copyright (c) 2019, 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.
-
-library vm.bytecode.ast_remover;
-
-import 'package:kernel/ast.dart' hide MapEntry;
-import '../metadata/bytecode.dart';
-
-/// Drops kernel AST for members with bytecode.
-/// Can preserve removed AST and restore it if needed.
-class ASTRemover extends Transformer {
-  final BytecodeMetadataRepository metadata;
-  final stashes = <Node, _Stash>{};
-
-  ASTRemover(Component component)
-      : metadata = component.metadata[new BytecodeMetadataRepository().tag] {
-    stashes[component] = new _ComponentStash(component.mainMethod,
-        new Map<String, MetadataRepository<dynamic>>.from(component.metadata));
-    component.mainMethod = null;
-    component.metadata.removeWhere((tag, md) => tag != metadata.tag);
-  }
-
-  @override
-  visitLibrary(Library node) {
-    stashes[node] = new _LibraryStash(
-        new List<Expression>.from(node.annotations),
-        new List<Field>.from(node.fields),
-        new List<Procedure>.from(node.procedures),
-        new List<Reference>.from(node.additionalExports));
-
-    node.annotations.clear();
-    node.fields.clear();
-    node.procedures.clear();
-    node.additionalExports.clear();
-
-    super.visitLibrary(node);
-
-    return node;
-  }
-
-  @override
-  visitLibraryDependency(LibraryDependency node) {
-    stashes[node] = new _LibraryDependencyStash(
-        new List<Expression>.from(node.annotations));
-
-    node.annotations.clear();
-
-    super.visitLibraryDependency(node);
-
-    return node;
-  }
-
-  // Still referenced from function types which may appear in class supertypes.
-  @override
-  visitTypedef(Typedef node) {
-    stashes[node] = new _TypedefStash(node.annotations);
-
-    node.annotations = const <Expression>[];
-
-    super.visitTypedef(node);
-
-    // TODO(alexmarkov): fix Typedef visitor to visit these fields.
-    transformList(node.positionalParameters, this, node);
-    transformList(node.namedParameters, this, node);
-
-    return node;
-  }
-
-  // May appear in typedefs.
-  @override
-  visitVariableDeclaration(VariableDeclaration node) {
-    stashes[node] = new _VariableDeclarationStash(node.annotations);
-
-    node.annotations = const <Expression>[];
-
-    super.visitVariableDeclaration(node);
-
-    return node;
-  }
-
-  @override
-  visitClass(Class node) {
-    stashes[node] = new _ClassStash(
-        node.annotations,
-        new List<Field>.from(node.fields),
-        new List<Procedure>.from(node.procedures),
-        new List<Constructor>.from(node.constructors));
-
-    node.annotations = const <Expression>[];
-    node.fields.clear();
-    node.procedures.clear();
-    node.constructors.clear();
-
-    super.visitClass(node);
-
-    return node;
-  }
-
-  void restoreAST() {
-    stashes.forEach((Node node, _Stash stash) {
-      if (node is Component) {
-        _ComponentStash componentStash = stash as _ComponentStash;
-        node.mainMethod = componentStash.mainMethod;
-        node.metadata.addAll(componentStash.metadata);
-      } else if (node is Library) {
-        _LibraryStash libraryStash = stash as _LibraryStash;
-        node.annotations.addAll(libraryStash.annotations);
-        node.fields.addAll(libraryStash.fields);
-        node.procedures.addAll(libraryStash.procedures);
-        node.additionalExports.addAll(libraryStash.additionalExports);
-      } else if (node is LibraryDependency) {
-        _LibraryDependencyStash libraryDependencyStash =
-            stash as _LibraryDependencyStash;
-        node.annotations.addAll(libraryDependencyStash.annotations);
-      } else if (node is Typedef) {
-        _TypedefStash typedefStash = stash as _TypedefStash;
-        node.annotations = typedefStash.annotations;
-      } else if (node is VariableDeclaration) {
-        _VariableDeclarationStash variableDeclarationStash =
-            stash as _VariableDeclarationStash;
-        node.annotations = variableDeclarationStash.annotations;
-      } else if (node is Class) {
-        _ClassStash classStash = stash as _ClassStash;
-        node.annotations = classStash.annotations;
-        node.fields.addAll(classStash.fields);
-        node.procedures.addAll(classStash.procedures);
-        node.constructors.addAll(classStash.constructors);
-      } else {
-        throw 'Unexpected ${node.runtimeType} $node';
-      }
-    });
-  }
-}
-
-abstract class _Stash {}
-
-class _ClassStash extends _Stash {
-  final List<Expression> annotations;
-  final List<Field> fields;
-  final List<Procedure> procedures;
-  final List<Constructor> constructors;
-
-  _ClassStash(
-      this.annotations, this.fields, this.procedures, this.constructors);
-}
-
-class _LibraryStash extends _Stash {
-  final List<Expression> annotations;
-  final List<Field> fields;
-  final List<Procedure> procedures;
-  final List<Reference> additionalExports;
-
-  _LibraryStash(
-      this.annotations, this.fields, this.procedures, this.additionalExports);
-}
-
-class _LibraryDependencyStash extends _Stash {
-  final List<Expression> annotations;
-
-  _LibraryDependencyStash(this.annotations);
-}
-
-class _TypedefStash extends _Stash {
-  final List<Expression> annotations;
-
-  _TypedefStash(this.annotations);
-}
-
-class _VariableDeclarationStash extends _Stash {
-  final List<Expression> annotations;
-
-  _VariableDeclarationStash(this.annotations);
-}
-
-class _ComponentStash extends _Stash {
-  final Procedure mainMethod;
-  final Map<String, MetadataRepository<dynamic>> metadata;
-
-  _ComponentStash(this.mainMethod, this.metadata);
-}
diff --git a/pkg/vm/lib/bytecode/bytecode_serialization.dart b/pkg/vm/lib/bytecode/bytecode_serialization.dart
index db9fa71..5f2dd85 100644
--- a/pkg/vm/lib/bytecode/bytecode_serialization.dart
+++ b/pkg/vm/lib/bytecode/bytecode_serialization.dart
@@ -234,6 +234,11 @@
     return linkReader.get<T>(offset);
   }
 
+  ForwardReference<T> readLinkOffsetAsForwardReference<T>() {
+    final offset = readPackedUInt30();
+    return new ForwardReference<T>(offset, linkReader);
+  }
+
   void align(int alignment) {
     assert(alignment & (alignment - 1) == 0);
     _pos = ((_pos + alignment - 1) & -alignment);
@@ -455,6 +460,16 @@
   }
 }
 
+// Placeholder for an object which will be read in future.
+class ForwardReference<T> {
+  final int offset;
+  final LinkReader linkReader;
+
+  ForwardReference(this.offset, this.linkReader);
+
+  T get() => linkReader.get<T>(offset);
+}
+
 class NamedEntryStatistics {
   final String name;
   int size = 0;
@@ -471,7 +486,15 @@
   static int objectTableSize = 0;
   static int objectTableEntriesCount = 0;
   static int stringTableSize = 0;
+  static int librariesSize = 0;
+  static int classesSize = 0;
   static int membersSize = 0;
+  static int codeSize = 0;
+  static int sourcePositionsSize = 0;
+  static int sourceFilesSize = 0;
+  static int lineStartsSize = 0;
+  static int localVariablesSize = 0;
+  static int annotationsSize = 0;
   static int constantPoolSize = 0;
   static int instructionsSize = 0;
   static List<NamedEntryStatistics> constantPoolStats =
@@ -483,7 +506,15 @@
     objectTableSize = 0;
     objectTableEntriesCount = 0;
     stringTableSize = 0;
+    librariesSize = 0;
+    classesSize = 0;
     membersSize = 0;
+    codeSize = 0;
+    sourcePositionsSize = 0;
+    sourceFilesSize = 0;
+    lineStartsSize = 0;
+    localVariablesSize = 0;
+    annotationsSize = 0;
     constantPoolSize = 0;
     instructionsSize = 0;
     constantPoolStats = <NamedEntryStatistics>[];
@@ -499,7 +530,15 @@
       print("       - $entry");
     }
     print("   - string table:     $stringTableSize");
-    print("  Bytecode members:    $membersSize");
+    print("  Libraries:           $librariesSize");
+    print("  Classes:             $classesSize");
+    print("  Members:             $membersSize");
+    print("  Source positions:    $sourcePositionsSize");
+    print("  Source files:        $sourceFilesSize");
+    print("  Line starts:         $lineStartsSize");
+    print("  Local variables:     $localVariablesSize");
+    print("  Annotations:         $annotationsSize");
+    print("  Code:                $codeSize");
     print("   - constant pool:    $constantPoolSize");
     for (var entry in constantPoolStats) {
       print("       - $entry");
diff --git a/pkg/vm/lib/bytecode/dbc.dart b/pkg/vm/lib/bytecode/dbc.dart
index 351a3e7..244e800 100644
--- a/pkg/vm/lib/bytecode/dbc.dart
+++ b/pkg/vm/lib/bytecode/dbc.dart
@@ -10,7 +10,7 @@
 /// Before bumping current bytecode version format, make sure that
 /// all users have switched to a VM which is able to consume new
 /// version of bytecode.
-const int currentBytecodeFormatVersion = 9;
+const int currentBytecodeFormatVersion = 10;
 
 /// Version of experimental / bleeding edge bytecode format.
 /// Produced by bytecode generator when --use-future-bytecode-format
diff --git a/pkg/vm/lib/bytecode/declarations.dart b/pkg/vm/lib/bytecode/declarations.dart
index 1d8cfc5..58827b6 100644
--- a/pkg/vm/lib/bytecode/declarations.dart
+++ b/pkg/vm/lib/bytecode/declarations.dart
@@ -14,7 +14,250 @@
 import 'exceptions.dart' show ExceptionsTable;
 import 'local_variable_table.dart' show LocalVariableTable;
 import 'object_table.dart' show ObjectTable, ObjectHandle, NameAndType;
-import 'source_positions.dart' show SourcePositions;
+import 'source_positions.dart' show LineStarts, SourcePositions;
+
+class LibraryDeclaration {
+  static const usesDartMirrorsFlag = 1 << 0;
+  static const usesDartFfiFlag = 1 << 1;
+
+  ObjectHandle importUri;
+  final int flags;
+  final ObjectHandle name;
+  final ObjectHandle script;
+  final List<ClassDeclaration> classes;
+
+  LibraryDeclaration(
+      this.importUri, this.flags, this.name, this.script, this.classes);
+
+  void write(BufferedWriter writer) {
+    final start = writer.offset;
+    writer.writePackedUInt30(flags);
+    writer.writePackedObject(name);
+    writer.writePackedObject(script);
+    writer.writePackedUInt30(classes.length);
+    for (var cls in classes) {
+      writer.writePackedObject(cls.name);
+      writer.writeLinkOffset(cls);
+    }
+    BytecodeSizeStatistics.librariesSize += (writer.offset - start);
+  }
+
+  factory LibraryDeclaration.read(BufferedReader reader) {
+    final flags = reader.readPackedUInt30();
+    final name = reader.readPackedObject();
+    final script = reader.readPackedObject();
+    final classes =
+        List<ClassDeclaration>.generate(reader.readPackedUInt30(), (_) {
+      final className = reader.readPackedObject();
+      return reader.readLinkOffset<ClassDeclaration>()..name = className;
+    });
+    return new LibraryDeclaration(null, flags, name, script, classes);
+  }
+
+  @override
+  String toString() {
+    final StringBuffer sb = new StringBuffer();
+    sb.writeln('Library $importUri');
+    sb.writeln('    name $name');
+    sb.writeln('    script $script');
+    if ((flags & usesDartMirrorsFlag) != 0) {
+      sb.writeln('    uses dart:mirrors');
+    }
+    if ((flags & usesDartFfiFlag) != 0) {
+      sb.writeln('    uses dart:ffi');
+    }
+    sb.writeln();
+    for (var cls in classes) {
+      sb.write(cls);
+    }
+    return sb.toString();
+  }
+}
+
+class ClassDeclaration {
+  static const isAbstractFlag = 1 << 0;
+  static const isEnumFlag = 1 << 1;
+  static const hasTypeParamsFlag = 1 << 2;
+  static const hasTypeArgumentsFlag = 1 << 3;
+  static const isTransformedMixinApplicationFlag = 1 << 4;
+  static const hasSourcePositionsFlag = 1 << 5;
+  static const hasAnnotationsFlag = 1 << 6;
+  static const hasPragmaFlag = 1 << 7;
+
+  ObjectHandle name;
+  final int flags;
+  final ObjectHandle script;
+  final int position;
+  final int endPosition;
+  final TypeParametersDeclaration typeParameters;
+  final int numTypeArguments;
+  final ObjectHandle superType;
+  final List<ObjectHandle> interfaces;
+  final Members members;
+  final ObjectHandle annotations;
+
+  ClassDeclaration(
+      this.name,
+      this.flags,
+      this.script,
+      this.position,
+      this.endPosition,
+      this.typeParameters,
+      this.numTypeArguments,
+      this.superType,
+      this.interfaces,
+      this.members,
+      this.annotations);
+
+  void write(BufferedWriter writer) {
+    final start = writer.offset;
+    writer.writePackedUInt30(flags);
+    writer.writePackedObject(script);
+
+    if ((flags & hasSourcePositionsFlag) != 0) {
+      writer.writePackedUInt30(position + 1);
+      writer.writePackedUInt30(endPosition + 1);
+    }
+    if ((flags & hasTypeArgumentsFlag) != 0) {
+      writer.writePackedUInt30(numTypeArguments);
+    }
+    if ((flags & hasTypeParamsFlag) != 0) {
+      typeParameters.write(writer);
+    }
+    writer.writePackedObject(superType);
+    writer.writePackedList(interfaces);
+    if ((flags & hasAnnotationsFlag) != 0) {
+      writer.writeLinkOffset(annotations);
+    }
+    writer.writeLinkOffset(members);
+    BytecodeSizeStatistics.classesSize += (writer.offset - start);
+  }
+
+  factory ClassDeclaration.read(BufferedReader reader) {
+    final flags = reader.readPackedUInt30();
+    final script = reader.readPackedObject();
+    final position = ((flags & hasSourcePositionsFlag) != 0)
+        ? reader.readPackedUInt30() - 1
+        : TreeNode.noOffset;
+    final endPosition = ((flags & hasSourcePositionsFlag) != 0)
+        ? reader.readPackedUInt30() - 1
+        : TreeNode.noOffset;
+    final numTypeArguments =
+        ((flags & hasTypeArgumentsFlag) != 0) ? reader.readPackedUInt30() : 0;
+    final typeParameters = ((flags & hasTypeParamsFlag) != 0)
+        ? new TypeParametersDeclaration.read(reader)
+        : null;
+    final superType = reader.readPackedObject();
+    final interfaces = reader.readPackedList<ObjectHandle>();
+    final annotations = ((flags & hasAnnotationsFlag) != 0)
+        ? reader.readLinkOffset<ObjectHandle>()
+        : null;
+    final members = reader.readLinkOffset<Members>();
+    return new ClassDeclaration(
+        null,
+        flags,
+        script,
+        position,
+        endPosition,
+        typeParameters,
+        numTypeArguments,
+        superType,
+        interfaces,
+        members,
+        annotations);
+  }
+
+  @override
+  String toString() {
+    final StringBuffer sb = new StringBuffer();
+    sb.write('Class $name, script = $script');
+    if ((flags & isAbstractFlag) != 0) {
+      sb.write(', abstract');
+    }
+    if ((flags & isEnumFlag) != 0) {
+      sb.write(', enum');
+    }
+    if ((flags & isTransformedMixinApplicationFlag) != 0) {
+      sb.write(', mixin-application');
+    }
+    if ((flags & hasPragmaFlag) != 0) {
+      sb.write(', has-pragma');
+    }
+    if ((flags & hasSourcePositionsFlag) != 0) {
+      sb.write(', pos = $position, end-pos = $endPosition');
+    }
+    sb.writeln();
+    if ((flags & hasTypeParamsFlag) != 0) {
+      sb.write('    type-params $typeParameters (args: $numTypeArguments)\n');
+    }
+    if (superType != null) {
+      sb.write('    extends $superType\n');
+    }
+    if (interfaces.isNotEmpty) {
+      sb.write('    implements $interfaces\n');
+    }
+    if ((flags & hasAnnotationsFlag) != 0) {
+      sb.write('    annotations $annotations\n');
+    }
+    sb.writeln();
+    sb.write(members.toString());
+    return sb.toString();
+  }
+}
+
+class SourceFile {
+  static const hasLineStartsFlag = 1 << 0;
+  static const hasSourceFlag = 1 << 1;
+
+  int flags;
+  final ObjectHandle importUri;
+  final LineStarts lineStarts;
+  final String source;
+
+  SourceFile(this.importUri, this.lineStarts, this.source) {
+    flags = 0;
+    if (lineStarts != null) {
+      flags |= hasLineStartsFlag;
+    }
+    if (source != null && source != '') {
+      flags |= hasSourceFlag;
+    }
+  }
+
+  void write(BufferedWriter writer) {
+    writer.writePackedUInt30(flags);
+    writer.writePackedObject(importUri);
+    if ((flags & hasLineStartsFlag) != 0) {
+      writer.writeLinkOffset(lineStarts);
+    }
+    if ((flags & hasSourceFlag) != 0) {
+      writer.writePackedStringReference(source);
+    }
+  }
+
+  factory SourceFile.read(BufferedReader reader) {
+    final flags = reader.readPackedUInt30();
+    final importUri = reader.readPackedObject();
+    final lineStarts = ((flags & hasLineStartsFlag) != 0)
+        ? reader.readLinkOffset<LineStarts>()
+        : null;
+    final source = ((flags & hasSourceFlag) != 0)
+        ? reader.readPackedStringReference()
+        : null;
+    return new SourceFile(importUri, lineStarts, source);
+  }
+
+  @override
+  String toString() {
+    final StringBuffer sb = new StringBuffer();
+    sb.writeln('SourceFile $importUri');
+    sb.writeln('------------------------------------------------');
+    sb.write(source);
+    sb.writeln('------------------------------------------------');
+    sb.writeln(lineStarts);
+    return sb.toString();
+  }
+}
 
 class Members {
   final List<FieldDeclaration> fields;
@@ -36,6 +279,7 @@
   }
 
   void write(BufferedWriter writer) {
+    final start = writer.offset;
     writer.writePackedUInt30(countFunctions());
     writer.writePackedUInt30(fields.length);
     for (var field in fields) {
@@ -45,6 +289,7 @@
     for (var func in functions) {
       func.write(writer);
     }
+    BytecodeSizeStatistics.membersSize += (writer.offset - start);
   }
 
   factory Members.read(BufferedReader reader) {
@@ -57,11 +302,8 @@
   }
 
   @override
-  String toString() => "\n"
-      "Members {\n"
-      "${fields.join('\n')}\n"
-      "${functions.join('\n')}"
-      "}\n";
+  String toString() => "${fields.join('\n')}\n"
+      "${functions.join('\n')}";
 }
 
 class FieldDeclaration {
@@ -164,7 +406,7 @@
 
   @override
   String toString() {
-    StringBuffer sb = new StringBuffer();
+    final StringBuffer sb = new StringBuffer();
     sb.write('Field $name, type = $type');
     if ((flags & hasGetterFlag) != 0) {
       sb.write(', getter = $getterName');
@@ -195,7 +437,7 @@
     }
     sb.writeln();
     if ((flags & hasInitializerFlag) != 0) {
-      sb.write('    initializer $initializerCode\n');
+      sb.write('    initializer\n$initializerCode\n');
     } else {
       sb.write('    value = $value\n');
     }
@@ -341,7 +583,7 @@
 
   @override
   String toString() {
-    StringBuffer sb = new StringBuffer();
+    final StringBuffer sb = new StringBuffer();
     sb.write('Function $name');
     if ((flags & isConstructorFlag) != 0) {
       sb.write(', constructor');
@@ -571,7 +813,7 @@
     if (hasClosures) {
       closures.forEach((c) => c.code.write(writer));
     }
-    BytecodeSizeStatistics.membersSize += (writer.offset - start);
+    BytecodeSizeStatistics.codeSize += (writer.offset - start);
   }
 
   factory Code.read(BufferedReader reader) {
@@ -625,8 +867,7 @@
 
   // TODO(alexmarkov): Consider printing constant pool before bytecode.
   @override
-  String toString() => "\n"
-      "Bytecode {\n"
+  String toString() => "Bytecode {\n"
       "${new BytecodeDisassembler().disassemble(bytecodes, exceptionsTable, annotations: [
         hasSourcePositions
             ? sourcePositions.getBytecodeAnnotations()
@@ -761,7 +1002,7 @@
 
   @override
   String toString() {
-    StringBuffer sb = new StringBuffer();
+    final StringBuffer sb = new StringBuffer();
     sb.write('Closure $parent::$name');
     if (position != TreeNode.noOffset) {
       sb.write(' pos = $position, end-pos = $endPosition');
@@ -850,7 +1091,7 @@
   String toString() {
     StringBuffer sb = new StringBuffer();
     sb.writeln('ClosureCode {');
-    sb.writeln(new BytecodeDisassembler()
+    sb.write(new BytecodeDisassembler()
         .disassemble(bytecodes, exceptionsTable, annotations: [
       hasSourcePositions
           ? sourcePositions.getBytecodeAnnotations()
@@ -876,7 +1117,7 @@
 
 class Component {
   static const int magicValue = 0x44424332; // 'DBC2'
-  static const int numSections = 8;
+  static const int numSections = 13;
   static const int sectionAlignment = 4;
 
   //  UInt32 magic, version, numSections x (numItems, offset)
@@ -885,11 +1126,16 @@
   int version;
   StringTable stringTable;
   ObjectTable objectTable;
-  List<Members> members = <Members>[];
-  List<Code> codes = <Code>[];
-  List<SourcePositions> sourcePositions = <SourcePositions>[];
-  List<LocalVariableTable> localVariables = <LocalVariableTable>[];
-  List<ObjectHandle> annotations = <ObjectHandle>[];
+  final List<LibraryDeclaration> libraries = <LibraryDeclaration>[];
+  final List<ClassDeclaration> classes = <ClassDeclaration>[];
+  final List<Members> members = <Members>[];
+  final List<Code> codes = <Code>[];
+  final List<SourcePositions> sourcePositions = <SourcePositions>[];
+  final List<LineStarts> lineStarts = <LineStarts>[];
+  final List<SourceFile> sourceFiles = <SourceFile>[];
+  final Map<Uri, SourceFile> uriToSource = <Uri, SourceFile>{};
+  final List<LocalVariableTable> localVariables = <LocalVariableTable>[];
+  final List<ObjectHandle> annotations = <ObjectHandle>[];
   ObjectHandle mainLibrary;
 
   Component(this.version)
@@ -902,58 +1148,96 @@
     // Write sections to their own buffers in reverse order as section may
     // reference data structures from successor sections by offsets.
 
-    final BufferedWriter annotationsWriter =
-        new BufferedWriter.fromWriter(writer);
+    final annotationsWriter = new BufferedWriter.fromWriter(writer);
     for (var annot in annotations) {
       writer.linkWriter.put(annot, annotationsWriter.offset);
       annotationsWriter.writePackedObject(annot);
     }
+    BytecodeSizeStatistics.annotationsSize += annotationsWriter.offset;
 
-    final BufferedWriter localVariablesWriter =
-        new BufferedWriter.fromWriter(writer);
+    final localVariablesWriter = new BufferedWriter.fromWriter(writer);
     for (var lv in localVariables) {
       writer.linkWriter.put(lv, localVariablesWriter.offset);
       lv.write(localVariablesWriter);
     }
+    BytecodeSizeStatistics.localVariablesSize += localVariablesWriter.offset;
 
-    final BufferedWriter sourcePositionsWriter =
-        new BufferedWriter.fromWriter(writer);
+    final lineStartsWriter = new BufferedWriter.fromWriter(writer);
+    for (var ls in lineStarts) {
+      writer.linkWriter.put(ls, lineStartsWriter.offset);
+      ls.write(lineStartsWriter);
+    }
+    BytecodeSizeStatistics.lineStartsSize += lineStartsWriter.offset;
+
+    final sourceFilesWriter = new BufferedWriter.fromWriter(writer);
+    for (var sf in sourceFiles) {
+      writer.linkWriter.put(sf, sourceFilesWriter.offset);
+      sf.write(sourceFilesWriter);
+    }
+    BytecodeSizeStatistics.sourceFilesSize += sourceFilesWriter.offset;
+
+    final sourcePositionsWriter = new BufferedWriter.fromWriter(writer);
     for (var sp in sourcePositions) {
       writer.linkWriter.put(sp, sourcePositionsWriter.offset);
       sp.write(sourcePositionsWriter);
     }
+    BytecodeSizeStatistics.sourcePositionsSize += sourcePositionsWriter.offset;
 
-    final BufferedWriter codesWriter = new BufferedWriter.fromWriter(writer);
+    final codesWriter = new BufferedWriter.fromWriter(writer);
     for (var code in codes) {
       writer.linkWriter.put(code, codesWriter.offset);
       code.write(codesWriter);
     }
 
-    final BufferedWriter membersWriter = new BufferedWriter.fromWriter(writer);
+    final membersWriter = new BufferedWriter.fromWriter(writer);
     for (var m in members) {
       writer.linkWriter.put(m, membersWriter.offset);
       m.write(membersWriter);
     }
 
+    final classesWriter = new BufferedWriter.fromWriter(writer);
+    for (var cls in classes) {
+      writer.linkWriter.put(cls, classesWriter.offset);
+      cls.write(classesWriter);
+    }
+
+    final librariesWriter = new BufferedWriter.fromWriter(writer);
+    for (var library in libraries) {
+      writer.linkWriter.put(library, librariesWriter.offset);
+      library.write(librariesWriter);
+    }
+
+    final libraryIndexWriter = new BufferedWriter.fromWriter(writer);
+    for (var library in libraries) {
+      libraryIndexWriter.writePackedObject(library.importUri);
+      libraryIndexWriter.writeLinkOffset(library);
+    }
+    BytecodeSizeStatistics.librariesSize += libraryIndexWriter.offset;
+
     BufferedWriter mainWriter;
     if (mainLibrary != null) {
       mainWriter = new BufferedWriter.fromWriter(writer);
       mainWriter.writePackedObject(mainLibrary);
     }
 
-    final BufferedWriter objectsWriter = new BufferedWriter.fromWriter(writer);
+    final objectsWriter = new BufferedWriter.fromWriter(writer);
     objectTable.write(objectsWriter);
 
-    final BufferedWriter stringsWriter = new BufferedWriter.fromWriter(writer);
+    final stringsWriter = new BufferedWriter.fromWriter(writer);
     stringTable.write(stringsWriter);
 
     List<_Section> sections = [
       new _Section(0, stringsWriter),
       new _Section(0, objectsWriter),
       new _Section(0, mainWriter),
+      new _Section(libraries.length, libraryIndexWriter),
+      new _Section(libraries.length, librariesWriter),
+      new _Section(classes.length, classesWriter),
       new _Section(members.length, membersWriter),
       new _Section(codes.length, codesWriter),
       new _Section(sourcePositions.length, sourcePositionsWriter),
+      new _Section(sourceFiles.length, sourceFilesWriter),
+      new _Section(lineStarts.length, lineStartsWriter),
       new _Section(localVariables.length, localVariablesWriter),
       new _Section(annotations.length, annotationsWriter),
     ];
@@ -1014,6 +1298,15 @@
     reader.readUInt32();
     final mainOffset = reader.readUInt32();
 
+    final librariesNum = reader.readUInt32();
+    final libraryIndexOffset = reader.readUInt32();
+
+    reader.readUInt32();
+    final librariesOffset = reader.readUInt32();
+
+    final classesNum = reader.readUInt32();
+    final classesOffset = reader.readUInt32();
+
     final membersNum = reader.readUInt32();
     final membersOffset = reader.readUInt32();
 
@@ -1023,6 +1316,12 @@
     final sourcePositionsNum = reader.readUInt32();
     final sourcePositionsOffset = reader.readUInt32();
 
+    final sourceFilesNum = reader.readUInt32();
+    final sourceFilesOffset = reader.readUInt32();
+
+    final lineStartsNum = reader.readUInt32();
+    final lineStartsOffset = reader.readUInt32();
+
     final localVariablesNum = reader.readUInt32();
     final localVariablesOffset = reader.readUInt32();
 
@@ -1049,6 +1348,24 @@
       annotations.add(annot);
     }
 
+    final lineStartsStart = start + lineStartsOffset;
+    reader.offset = lineStartsStart;
+    for (int i = 0; i < lineStartsNum; ++i) {
+      int offset = reader.offset - lineStartsStart;
+      LineStarts ls = new LineStarts.read(reader);
+      reader.linkReader.setOffset(ls, offset);
+      lineStarts.add(ls);
+    }
+
+    final sourceFilesStart = start + sourceFilesOffset;
+    reader.offset = sourceFilesStart;
+    for (int i = 0; i < sourceFilesNum; ++i) {
+      int offset = reader.offset - sourceFilesStart;
+      SourceFile sf = new SourceFile.read(reader);
+      reader.linkReader.setOffset(sf, offset);
+      sourceFiles.add(sf);
+    }
+
     final sourcePositionsStart = start + sourcePositionsOffset;
     reader.offset = sourcePositionsStart;
     for (int i = 0; i < sourcePositionsNum; ++i) {
@@ -1085,22 +1402,58 @@
       members.add(m);
     }
 
+    final classesStart = start + classesOffset;
+    reader.offset = classesStart;
+    for (int i = 0; i < classesNum; ++i) {
+      int offset = reader.offset - classesStart;
+      ClassDeclaration cls = new ClassDeclaration.read(reader);
+      reader.linkReader.setOffset(cls, offset);
+      classes.add(cls);
+    }
+
+    final librariesStart = start + librariesOffset;
+    reader.offset = librariesStart;
+    for (int i = 0; i < librariesNum; ++i) {
+      int offset = reader.offset - librariesStart;
+      LibraryDeclaration library = new LibraryDeclaration.read(reader);
+      reader.linkReader.setOffset(library, offset);
+      libraries.add(library);
+    }
+
+    final libraryIndexStart = start + libraryIndexOffset;
+    reader.offset = libraryIndexStart;
+    for (int i = 0; i < librariesNum; ++i) {
+      final importUri = reader.readPackedObject();
+      final library = reader.readLinkOffset<LibraryDeclaration>();
+      library.importUri = importUri;
+    }
+
     if (mainOffset != 0) {
       reader.offset = start + mainOffset;
       mainLibrary = reader.readPackedObject();
     }
   }
 
-  String toString() => "\n"
-      "Bytecode"
-      " (version: "
-      "${version == currentBytecodeFormatVersion ? 'stable' : version == futureBytecodeFormatVersion ? 'future' : "v$version"}"
-      ")\n"
-//      "$objectTable\n"
-//      "$stringTable\n"
-      "${mainLibrary != null ? 'Main library: $mainLibrary\n' : ''}"
-//      "${members.join('\n')}\n"
-      ;
+  @override
+  String toString() {
+    final StringBuffer sb = new StringBuffer();
+    sb.write("Bytecode (version: ");
+    if (version == currentBytecodeFormatVersion) {
+      sb.write("stable");
+    } else if (version == futureBytecodeFormatVersion) {
+      sb.write("future");
+    } else {
+      sb.write("v$version");
+    }
+    sb.writeln(")");
+    if (mainLibrary != null) {
+      sb.writeln("Main library: $mainLibrary");
+    }
+    for (var library in libraries) {
+      sb.write(library);
+    }
+    return sb.toString();
+  }
 }
 
 void _writeBytecodeInstructions(BufferedWriter writer, List<int> bytecodes) {
diff --git a/pkg/vm/lib/bytecode/gen_bytecode.dart b/pkg/vm/lib/bytecode/gen_bytecode.dart
index ba4f75e..01426c5 100644
--- a/pkg/vm/lib/bytecode/gen_bytecode.dart
+++ b/pkg/vm/lib/bytecode/gen_bytecode.dart
@@ -8,6 +8,8 @@
 // explicitly once constant-update-2018 is shipped.
 import 'package:front_end/src/api_prototype/constant_evaluator.dart'
     show ConstantEvaluator, EvaluationEnvironment, ErrorReporter;
+import 'package:front_end/src/api_unstable/vm.dart'
+    show CompilerContext, Severity, templateIllegalRecursiveType;
 
 import 'package:kernel/ast.dart' hide MapEntry, Component, FunctionDeclaration;
 import 'package:kernel/ast.dart' as ast show Component, FunctionDeclaration;
@@ -38,12 +40,15 @@
 import 'local_variable_table.dart' show LocalVariableTable;
 import 'local_vars.dart' show LocalVariables;
 import 'nullability_detector.dart' show NullabilityDetector;
-import 'object_table.dart' show ObjectHandle, ObjectTable, NameAndType;
+import 'object_table.dart'
+    show ObjectHandle, ObjectTable, NameAndType, topLevelClassName;
 import 'recognized_methods.dart' show RecognizedMethods;
-import 'source_positions.dart' show SourcePositions;
+import 'recursive_types_validator.dart' show IllegalRecursiveTypeException;
+import 'source_positions.dart' show LineStarts, SourcePositions;
 import '../constants_error_reporter.dart' show ForwardConstantEvaluationErrors;
 import '../metadata/bytecode.dart';
 
+import 'dart:convert' show utf8;
 import 'dart:math' as math;
 
 // This symbol is used as the name in assert assignable's to indicate it comes
@@ -55,6 +60,7 @@
   ast.Component component, {
   bool enableAsserts: true,
   bool emitSourcePositions: false,
+  bool emitSourceFiles: false,
   bool emitLocalVarInfo: false,
   bool emitAnnotations: false,
   bool omitAssertSourcePositions: false,
@@ -73,22 +79,29 @@
   final constantsBackend = new VmConstantsBackend(coreTypes);
   final errorReporter = new ForwardConstantEvaluationErrors();
   libraries ??= component.libraries;
-  final bytecodeGenerator = new BytecodeGenerator(
-      component,
-      coreTypes,
-      hierarchy,
-      typeEnvironment,
-      constantsBackend,
-      environmentDefines,
-      enableAsserts,
-      emitSourcePositions,
-      emitLocalVarInfo,
-      emitAnnotations,
-      omitAssertSourcePositions,
-      useFutureBytecodeFormat,
-      errorReporter);
-  for (var library in libraries) {
-    bytecodeGenerator.visitLibrary(library);
+  try {
+    final bytecodeGenerator = new BytecodeGenerator(
+        component,
+        coreTypes,
+        hierarchy,
+        typeEnvironment,
+        constantsBackend,
+        environmentDefines,
+        enableAsserts,
+        emitSourcePositions,
+        emitSourceFiles,
+        emitLocalVarInfo,
+        emitAnnotations,
+        omitAssertSourcePositions,
+        useFutureBytecodeFormat,
+        errorReporter);
+    for (var library in libraries) {
+      bytecodeGenerator.visitLibrary(library);
+    }
+  } on IllegalRecursiveTypeException catch (e) {
+    CompilerContext.current.options.report(
+        templateIllegalRecursiveType.withArguments(e.type).withoutLocation(),
+        Severity.error);
   }
 }
 
@@ -100,6 +113,7 @@
   final Map<String, String> environmentDefines;
   final bool enableAsserts;
   final bool emitSourcePositions;
+  final bool emitSourceFiles;
   final bool emitLocalVarInfo;
   final bool emitAnnotations;
   final bool omitAssertSourcePositions;
@@ -108,11 +122,13 @@
   final BytecodeMetadataRepository metadata = new BytecodeMetadataRepository();
   final RecognizedMethods recognizedMethods;
   final int formatVersion;
+  final Map<Uri, Source> astUriToSource;
   StringTable stringTable;
   ObjectTable objectTable;
   Component bytecodeComponent;
   NullabilityDetector nullabilityDetector;
 
+  List<ClassDeclaration> classDeclarations;
   List<FieldDeclaration> fieldDeclarations;
   List<FunctionDeclaration> functionDeclarations;
   Class enclosingClass;
@@ -152,6 +168,7 @@
       this.environmentDefines,
       this.enableAsserts,
       this.emitSourcePositions,
+      this.emitSourceFiles,
       this.emitLocalVarInfo,
       this.emitAnnotations,
       this.omitAssertSourcePositions,
@@ -160,14 +177,13 @@
       : recognizedMethods = new RecognizedMethods(typeEnvironment),
         formatVersion = useFutureBytecodeFormat
             ? futureBytecodeFormatVersion
-            : currentBytecodeFormatVersion {
+            : currentBytecodeFormatVersion,
+        astUriToSource = component.uriToSource {
     nullabilityDetector = new NullabilityDetector(recognizedMethods);
     component.addMetadataRepository(metadata);
 
     bytecodeComponent = new Component(formatVersion);
-    metadata.bytecodeComponent = bytecodeComponent;
-    metadata.mapping[component] =
-        new ComponentBytecodeMetadata(bytecodeComponent);
+    metadata.mapping[component] = new BytecodeMetadata(bytecodeComponent);
 
     stringTable = bytecodeComponent.stringTable;
     objectTable = bytecodeComponent.objectTable;
@@ -185,12 +201,20 @@
       return;
     }
 
-    visitList(node.classes, this);
-
     startMembers();
     visitList(node.procedures, this);
     visitList(node.fields, this);
-    endMembers(node);
+    final members = endMembers(node);
+
+    classDeclarations = <ClassDeclaration>[
+      getTopLevelClassDeclaration(node, members)
+    ];
+
+    visitList(node.classes, this);
+
+    bytecodeComponent.libraries
+        .add(getLibraryDeclaration(node, classDeclarations));
+    classDeclarations = null;
   }
 
   @override
@@ -199,7 +223,9 @@
     visitList(node.constructors, this);
     visitList(node.procedures, this);
     visitList(node.fields, this);
-    endMembers(node);
+    final members = endMembers(node);
+
+    classDeclarations.add(getClassDeclaration(node, members));
   }
 
   void startMembers() {
@@ -207,13 +233,159 @@
     functionDeclarations = <FunctionDeclaration>[];
   }
 
-  void endMembers(TreeNode node) {
+  Members endMembers(TreeNode node) {
     final members = new Members(fieldDeclarations, functionDeclarations);
     bytecodeComponent.members.add(members);
-    metadata.mapping[node] = new MembersBytecodeMetadata(members);
-
     fieldDeclarations = null;
     functionDeclarations = null;
+    return members;
+  }
+
+  ObjectHandle getScript(Uri uri, bool includeSource) {
+    SourceFile source;
+    if (includeSource && (emitSourceFiles || emitSourcePositions)) {
+      source = bytecodeComponent.uriToSource[uri];
+      if (source == null) {
+        final astSource = astUriToSource[uri];
+        if (astSource != null) {
+          final importUri =
+              objectTable.getNameHandle(null, astSource.importUri.toString());
+          LineStarts lineStarts;
+          if (emitSourcePositions) {
+            lineStarts = new LineStarts(astSource.lineStarts);
+            bytecodeComponent.lineStarts.add(lineStarts);
+          }
+          String text = '';
+          if (emitSourceFiles) {
+            text = astSource.cachedText ??
+                utf8.decode(astSource.source, allowMalformed: true);
+          }
+          source = new SourceFile(importUri, lineStarts, text);
+          bytecodeComponent.sourceFiles.add(source);
+          bytecodeComponent.uriToSource[uri] = source;
+        }
+      }
+    }
+    return objectTable.getScriptHandle(uri, source);
+  }
+
+  LibraryDeclaration getLibraryDeclaration(
+      Library library, List<ClassDeclaration> classes) {
+    final importUri =
+        objectTable.getNameHandle(null, library.importUri.toString());
+    int flags = 0;
+    for (var dependency in library.dependencies) {
+      final targetLibrary = dependency.targetLibrary;
+      assert(targetLibrary != null);
+      if (targetLibrary == coreTypes.mirrorsLibrary) {
+        flags |= LibraryDeclaration.usesDartMirrorsFlag;
+      } else if (targetLibrary == dartFfiLibrary) {
+        flags |= LibraryDeclaration.usesDartFfiFlag;
+      }
+    }
+    final name = objectTable.getNameHandle(null, library.name ?? '');
+    final script = getScript(library.fileUri, true);
+    return new LibraryDeclaration(importUri, flags, name, script, classes);
+  }
+
+  ClassDeclaration getClassDeclaration(Class cls, Members members) {
+    int flags = 0;
+    if (cls.isAbstract) {
+      flags |= ClassDeclaration.isAbstractFlag;
+    }
+    if (cls.isEnum) {
+      flags |= ClassDeclaration.isEnumFlag;
+    }
+    int numTypeArguments = 0;
+    TypeParametersDeclaration typeParameters;
+    if (hasInstantiatorTypeArguments(cls)) {
+      flags |= ClassDeclaration.hasTypeArgumentsFlag;
+      numTypeArguments = flattenInstantiatorTypeArguments(
+              cls,
+              cls.typeParameters
+                  .map((tp) => new TypeParameterType(tp))
+                  .toList())
+          .length;
+      assert(numTypeArguments > 0);
+      if (cls.typeParameters.isNotEmpty) {
+        flags |= ClassDeclaration.hasTypeParamsFlag;
+        typeParameters = getTypeParametersDeclaration(cls.typeParameters);
+      }
+    }
+    if (cls.isEliminatedMixin) {
+      flags |= ClassDeclaration.isTransformedMixinApplicationFlag;
+    }
+    int position = TreeNode.noOffset;
+    int endPosition = TreeNode.noOffset;
+    if (emitSourcePositions && cls.fileOffset != TreeNode.noOffset) {
+      flags |= ClassDeclaration.hasSourcePositionsFlag;
+      position = cls.fileOffset;
+      endPosition = cls.fileEndOffset;
+    }
+    Annotations annotations = getAnnotations(cls.annotations);
+    if (annotations.object != null) {
+      flags |= ClassDeclaration.hasAnnotationsFlag;
+      if (annotations.hasPragma) {
+        flags |= ClassDeclaration.hasPragmaFlag;
+      }
+    }
+
+    final nameHandle = objectTable.getNameHandle(
+        cls.name.startsWith('_') ? cls.enclosingLibrary : null, cls.name);
+    final script = getScript(cls.fileUri, !cls.isAnonymousMixin);
+    final superType = objectTable.getHandle(cls.supertype?.asInterfaceType);
+    final interfaces = objectTable.getHandles(
+        cls.implementedTypes.map((t) => t.asInterfaceType).toList());
+
+    final classDeclaration = new ClassDeclaration(
+        nameHandle,
+        flags,
+        script,
+        position,
+        endPosition,
+        typeParameters,
+        numTypeArguments,
+        superType,
+        interfaces,
+        members,
+        annotations.object);
+    bytecodeComponent.classes.add(classDeclaration);
+    return classDeclaration;
+  }
+
+  ClassDeclaration getTopLevelClassDeclaration(
+      Library library, Members members) {
+    int flags = 0;
+    int position = TreeNode.noOffset;
+    if (emitSourcePositions && library.fileOffset != TreeNode.noOffset) {
+      flags |= ClassDeclaration.hasSourcePositionsFlag;
+      position = library.fileOffset;
+    }
+    Annotations annotations = getAnnotations(library.annotations);
+    if (annotations.object != null) {
+      flags |= ClassDeclaration.hasAnnotationsFlag;
+      if (annotations.hasPragma) {
+        flags |= ClassDeclaration.hasPragmaFlag;
+      }
+    }
+
+    final nameHandle = objectTable.getNameHandle(null, topLevelClassName);
+    final script = getScript(library.fileUri, true);
+
+    final classDeclaration = new ClassDeclaration(
+        nameHandle,
+        flags,
+        script,
+        position,
+        /* endPosition */ TreeNode.noOffset,
+        /* typeParameters */ null,
+        /* numTypeArguments */ 0,
+        /* superType */ null,
+        /* interfaces */ const <ObjectHandle>[],
+        members,
+        annotations.object);
+    bytecodeComponent.classes.add(classDeclaration);
+    return classDeclaration;
   }
 
   bool _isPragma(Constant annotation) =>
@@ -224,7 +396,14 @@
     if (nodes.isEmpty) {
       return const Annotations(null, false);
     }
+    final savedConstantEvaluator = constantEvaluator;
+    if (constantEvaluator == null) {
+      constantEvaluator = new ConstantEvaluator(constantsBackend,
+          environmentDefines, typeEnvironment, enableAsserts, errorReporter)
+        ..env = new EvaluationEnvironment();
+    }
     List<Constant> constants = nodes.map(_evaluateConstantExpression).toList();
+    constantEvaluator = savedConstantEvaluator;
     bool hasPragma = constants.any(_isPragma);
     if (!emitAnnotations) {
       if (hasPragma) {
@@ -673,6 +852,10 @@
       _asyncAwaitCompleterGetFuture ??= libraryIndex.getMember(
           'dart:async', '_AsyncAwaitCompleter', 'get:future');
 
+  Library _dartFfiLibrary;
+  Library get dartFfiLibrary =>
+      _dartFfiLibrary ??= libraryIndex.tryGetLibrary('dart:ffi');
+
   void _recordSourcePosition(int fileOffset) {
     if (emitSourcePositions) {
       asm.currentSourcePosition = fileOffset;
@@ -3535,3 +3718,15 @@
 
   const Annotations(this.object, this.hasPragma);
 }
+
+ast.Component createFreshComponentWithBytecode(ast.Component component) {
+  final newComponent = new ast.Component();
+  final newRepository = new BytecodeMetadataRepository();
+  newComponent.addMetadataRepository(newRepository);
+
+  final oldRepository = component.metadata[newRepository.tag];
+  final metadata = oldRepository.mapping[component];
+  newRepository.mapping[newComponent] = metadata;
+
+  return newComponent;
+}
diff --git a/pkg/vm/lib/bytecode/generics.dart b/pkg/vm/lib/bytecode/generics.dart
index 4e795d6..91f8a70 100644
--- a/pkg/vm/lib/bytecode/generics.dart
+++ b/pkg/vm/lib/bytecode/generics.dart
@@ -152,93 +152,6 @@
   }
 }
 
-/// Returns true if the given type is recursive after its type arguments
-/// are flattened.
-bool isRecursiveAfterFlattening(InterfaceType type) {
-  final visitor = new IsRecursiveAfterFlatteningVisitor();
-  visitor.visit(type);
-  return visitor.isRecursive(type);
-}
-
-class IsRecursiveAfterFlatteningVisitor extends DartTypeVisitor<void> {
-  Set<DartType> _visited = new Set<DartType>();
-  List<DartType> _stack = <DartType>[];
-  Set<DartType> _recursive;
-
-  bool isRecursive(DartType type) =>
-      _recursive != null && _recursive.contains(type);
-
-  void visit(DartType type) {
-    if (!_visited.add(type)) {
-      _recordRecursiveType(type);
-      return;
-    }
-    _stack.add(type);
-
-    type.accept(this);
-
-    _stack.removeLast();
-    _visited.remove(type);
-  }
-
-  void _recordRecursiveType(DartType type) {
-    final int start = _stack.lastIndexOf(type);
-    final recursive = (_recursive ??= new Set<DartType>());
-    for (int i = start; i < _stack.length; ++i) {
-      recursive.add(_stack[i]);
-    }
-  }
-
-  @override
-  void defaultDartType(DartType node) =>
-      throw 'Unexpected type ${node.runtimeType} $node';
-
-  @override
-  void visitInvalidType(InvalidType node) {}
-
-  @override
-  void visitDynamicType(DynamicType node) {}
-
-  @override
-  void visitVoidType(VoidType node) {}
-
-  @override
-  void visitBottomType(BottomType node) {}
-
-  @override
-  void visitTypeParameterType(TypeParameterType node) {}
-
-  @override
-  void visitInterfaceType(InterfaceType node) {
-    for (var typeArg in node.typeArguments) {
-      visit(typeArg);
-    }
-    if (isRecursive(node)) {
-      return;
-    }
-    final flatTypeArgs =
-        flattenInstantiatorTypeArguments(node.classNode, node.typeArguments);
-    for (var typeArg in flatTypeArgs.getRange(
-        0, flatTypeArgs.length - node.typeArguments.length)) {
-      visit(typeArg);
-    }
-  }
-
-  @override
-  void visitTypedefType(TypedefType node) => visit(node.unalias);
-
-  @override
-  void visitFunctionType(FunctionType node) {
-    for (var p in node.positionalParameters) {
-      visit(p);
-    }
-    for (var p in node.namedParameters) {
-      visit(p.type);
-    }
-    visit(node.returnType);
-  }
-}
-
 /// Returns static type of [expr].
 DartType getStaticType(Expression expr, TypeEnvironment typeEnvironment) {
   // TODO(dartbug.com/34496): Remove this try/catch once
diff --git a/pkg/vm/lib/bytecode/object_table.dart b/pkg/vm/lib/bytecode/object_table.dart
index 80324cc..be1a7ed 100644
--- a/pkg/vm/lib/bytecode/object_table.dart
+++ b/pkg/vm/lib/bytecode/object_table.dart
@@ -13,6 +13,7 @@
         BufferedReader,
         BytecodeObject,
         BytecodeSizeStatistics,
+        ForwardReference,
         NamedEntryStatistics,
         doubleToIntBits,
         intBitsToDouble,
@@ -20,8 +21,9 @@
         ObjectWriter,
         StringWriter;
 import 'generics.dart'
-    show getInstantiatorTypeArguments, isRecursiveAfterFlattening;
-import 'declarations.dart' show TypeParametersDeclaration;
+    show getInstantiatorTypeArguments, hasInstantiatorTypeArguments;
+import 'declarations.dart' show SourceFile, TypeParametersDeclaration;
+import 'recursive_types_validator.dart' show RecursiveTypesValidator;
 
 /*
 
@@ -29,47 +31,43 @@
 (using notation from pkg/kernel/binary.md):
 
 type ObjectTable {
-  UInt numEntries;
-  UInt contentsSize;
+  UInt numEntries
 
-  //  Occupies contentsSize bytes.
-  ObjectContents objects[numEntries]
+  // Total size of ‘objects’ in bytes.
+  UInt objectsSize
 
-  UInt objectOffsets[numEntries]
+  ObjectContents[numEntries] objects
+
+  // Offsets relative to ‘objects’.
+  UInt[numEntries] objectOffsets
 }
 
+
 // Either reference to an object in object table, or object contents
-// written inline.
+// written inline (determined by bit 0).
 PackedObject = ObjectReference | ObjectContents
 
 type ObjectReference {
   // Bit 0 (reference bit): 1
   // Bits 1+: index in object table
-  UInt reference(<index>1)
+  UInt reference
 }
 
 type ObjectContents {
   // Bit 0 (reference bit): 0
   // Bits 1-4: object kind
   // Bits 5+ object flags
-  UInt header(<flags><kind>0)
+  UInt header
 }
 
-// Reference to a string in string table.
-type PackedString {
-  // Bit 0: set for two byte string
-  // Bits 1+: index in string table
-  UInt indexAndKind(<index><kind>)
-}
-
-// Invalid object table entry (at index 0).
+// Invalid/null object (always present at index 0).
 type InvalidObject extends ObjectContents {
   kind = 0;
 }
 
 type Library extends ObjectContents {
   kind = 1;
-  PackedString importUri;
+  PackedObject importUri;
 }
 
 type Class extends ObjectContents {
@@ -93,50 +91,6 @@
   UInt closureIndex;
 }
 
-type SimpleType extends ObjectContents {
-  kind = 5;
-  flags = (isDynamic, isVoid);
-  PackedObject class;
-}
-
-type TypeParameter extends ObjectContents {
-  kind = 6;
-  // Class, Member or Closure declaring this type parameter.
-  // Invalid if declared by function type.
-  PackedObject parent;
-  UInt indexInParent;
-}
-
-type GenericType extends ObjectContents {
-  kind = 7;
-  PackedObject class;
-  List<PackedObject> typeArgs;
-}
-
-type FunctionType extends ObjectContents {
-  kind = 8;
-  flags = (hasOptionalPositionalParams, hasOptionalNamedParams, hasTypeParams)
-
-  if hasTypeParams
-    UInt numTypeParameters
-    PackedObject[numTypeParameters] typeParameterNames
-    PackedObject[numTypeParameters] typeParameterBounds
-
-  UInt numParameters
-
-  if hasOptionalPositionalParams || hasOptionalNamedParams
-    UInt numRequiredParameters
-
-   Type[] positionalParameters
-   NameAndType[] namedParameters
-   PackedObject returnType
-}
-
-type NameAndType {
-  PackedObject name;
-  PackedObject type;
-}
-
 type Name extends ObjectContents {
   kind = 9;
 
@@ -154,12 +108,6 @@
   List<PackedObject> args;
 }
 
-type FinalizedGenericType extends ObjectContents {
-  kind = 11;
-  PackedObject class;
-  PackedObject typeArgs;
-}
-
 abstract type ConstObject extends ObjectContents {
   kind = 12;
   flags = constantTag (4 bits)
@@ -181,7 +129,7 @@
 type ConstDouble extends ConstValue {
   kind = 12
   constantTag (flags) = 3
-  // double bits are converted to int
+  // double bits are reinterpreted as 64-bit int
   SLEB128 value;
 }
 
@@ -223,11 +171,110 @@
 
   UInt numArguments
 
-  if hasTypeArgs
-    UInt numTypeArguments
+ if hasTypeArgs
+   UInt numTypeArguments
 
-  if hasNamedArgs
-    List<PackedObject> argNames;
+ if hasNamedArgs
+   List<PackedObject> argNames;
+}
+
+type Script extends ObjectContents {
+  kind = 14
+  flags = (hasSourceFile)
+  PackedObject uri
+  if hasSourceFile
+    UInt sourceFileOffset
+}
+
+abstract type Type extends ObjectContents {
+  kind = 15
+  flags = typeTag (4 bits)
+}
+
+type DynamicType extends Type {
+  kind = 15
+  typeTag (flags) = 1
+}
+
+type VoidType extends Type {
+  kind = 15
+  typeTag (flags) = 2
+}
+
+// SimpleType can be used only for types without instantiator type arguments.
+type SimpleType extends Type {
+  kind = 15
+  typeTag (flags) = 3
+  PackedObject class
+}
+
+type TypeParameter extends Type {
+  kind = 15
+  typeTag (flags) = 4
+  // Class, Member or Closure declaring this type parameter.
+  // Null (Invalid) if declared by function type.
+  PackedObject parent
+  UInt indexInParent
+}
+
+// Non-recursive finalized generic type.
+type GenericType extends Type {
+  kind = 15
+  typeTag (flags) = 5
+  PackedObject class
+  // Flattened type arguments vector.
+  PackedObject typeArgs
+}
+
+// Recursive finalized generic type.
+type RecursiveGenericType extends Type {
+  kind = 15
+  typeTag (flags) = 6
+  // This id is used to reference recursive types using RecursiveTypeRef.
+  // Type should be declared using RecursiveGenericType before it can be referenced.
+  // The root type should have zero recursiveId.
+  UInt recursiveId
+  PackedObject class
+  // Flattened type arguments vector.
+  PackedObject typeArgs
+}
+
+type RecursiveTypeRef extends Type {
+  kind = 15
+  typeTag (flags) = 7
+  UInt recursiveId
+}
+
+type FunctionType extends Type {
+  kind = 15
+  typeTag (flags) = 8
+
+  UInt functionTypeFlags(hasOptionalPositionalParams,
+                         hasOptionalNamedParams,
+                         hasTypeParams)
+
+  if hasTypeParams
+    TypeParametersDeclaration typeParameters
+
+  UInt numParameters
+
+  if hasOptionalPositionalParams || hasOptionalNamedParams
+    UInt numRequiredParameters
+
+  Type[] positionalParameters
+  NameAndType[] namedParameters
+  PackedObject returnType
+}
+
+type TypeParametersDeclaration {
+   UInt numTypeParameters
+   PackedObject[numTypeParameters] typeParameterNames
+   PackedObject[numTypeParameters] typeParameterBounds
+}
+
+type NameAndType {
+  PackedObject name;
+  PackedObject type;
 }
 
 */
@@ -238,15 +285,17 @@
   kClass,
   kMember,
   kClosure,
-  kSimpleType,
-  kTypeParameter,
-  kGenericType,
-  kFunctionType,
+  kUnused1,
+  kUnused2,
+  kUnused3,
+  kUnused4,
   kName,
   kTypeArguments,
-  kFinalizedGenericType,
+  kUnused5,
   kConstObject,
   kArgDesc,
+  kScript,
+  kType,
 }
 
 enum ConstTag {
@@ -261,6 +310,21 @@
   kTearOffInstantiation,
 }
 
+enum TypeTag {
+  kInvalid,
+  kDynamic,
+  kVoid,
+  kSimpleType,
+  kTypeParameter,
+  kGenericType,
+  kRecursiveGenericType,
+  kRecursiveTypeRef,
+  kFunctionType,
+}
+
+/// Name of artificial class containing top-level members of a library.
+const String topLevelClassName = '';
+
 String objectKindToString(ObjectKind kind) =>
     kind.toString().substring('ObjectKind.k'.length);
 
@@ -315,7 +379,7 @@
 
   bool get isCacheable => true;
 
-  factory ObjectHandle._empty(ObjectKind kind) {
+  factory ObjectHandle._empty(ObjectKind kind, int flags) {
     switch (kind) {
       case ObjectKind.kInvalid:
         return new _InvalidHandle();
@@ -327,24 +391,44 @@
         return new _MemberHandle._empty();
       case ObjectKind.kClosure:
         return new _ClosureHandle._empty();
-      case ObjectKind.kSimpleType:
-        return new _SimpleTypeHandle._empty();
-      case ObjectKind.kGenericType:
-        return new _GenericTypeHandle._empty();
-      case ObjectKind.kTypeParameter:
-        return new _TypeParameterHandle._empty();
-      case ObjectKind.kFunctionType:
-        return new _FunctionTypeHandle._empty();
       case ObjectKind.kName:
         return new _NameHandle._empty();
       case ObjectKind.kTypeArguments:
         return new _TypeArgumentsHandle._empty();
-      case ObjectKind.kFinalizedGenericType:
-        return new _FinalizedGenericTypeHandle._empty();
       case ObjectKind.kConstObject:
         return new _ConstObjectHandle._empty();
       case ObjectKind.kArgDesc:
         return new _ArgDescHandle._empty();
+      case ObjectKind.kScript:
+        return new _ScriptHandle._empty();
+      case ObjectKind.kType:
+        switch (TypeTag.values[flags ~/ flagBit0]) {
+          case TypeTag.kInvalid:
+            break;
+          case TypeTag.kDynamic:
+            return new _DynamicTypeHandle();
+          case TypeTag.kVoid:
+            return new _VoidTypeHandle();
+          case TypeTag.kSimpleType:
+            return new _SimpleTypeHandle._empty();
+          case TypeTag.kTypeParameter:
+            return new _TypeParameterHandle._empty();
+          case TypeTag.kGenericType:
+            return new _GenericTypeHandle._empty();
+          case TypeTag.kRecursiveGenericType:
+            return new _RecursiveGenericTypeHandle._empty();
+          case TypeTag.kRecursiveTypeRef:
+            return new _RecursiveTypeRefHandle._empty();
+          case TypeTag.kFunctionType:
+            return new _FunctionTypeHandle._empty();
+        }
+        throw 'Unexpected type tag $flags';
+      case ObjectKind.kUnused1:
+      case ObjectKind.kUnused2:
+      case ObjectKind.kUnused3:
+      case ObjectKind.kUnused4:
+      case ObjectKind.kUnused5:
+        break;
     }
     throw 'Unexpected object kind $kind';
   }
@@ -361,8 +445,9 @@
   factory ObjectHandle._read(BufferedReader reader, int header) {
     assert((header & referenceBit) == 0);
     final ObjectKind kind = _getKindFromHeader(header);
-    final obj = new ObjectHandle._empty(kind);
-    obj.flags = _getFlagsFromHeader(header);
+    final int flags = _getFlagsFromHeader(header);
+    final obj = new ObjectHandle._empty(kind, flags);
+    obj.flags = flags;
     obj.readContents(reader);
     return obj;
   }
@@ -391,7 +476,7 @@
 }
 
 class _LibraryHandle extends ObjectHandle {
-  String uri;
+  _NameHandle uri;
 
   _LibraryHandle._empty();
 
@@ -402,17 +487,17 @@
 
   @override
   void writeContents(BufferedWriter writer) {
-    writer.writePackedStringReference(uri);
+    writer.writePackedObject(uri);
   }
 
   @override
   void readContents(BufferedReader reader) {
-    uri = reader.readPackedStringReference();
+    uri = reader.readPackedObject();
   }
 
   @override
-  void indexStrings(StringWriter strings) {
-    strings.put(uri);
+  void accountUsesForObjectCopies(int numCopies) {
+    uri._useCount += numCopies;
   }
 
   @override
@@ -422,13 +507,10 @@
   bool operator ==(other) => other is _LibraryHandle && this.uri == other.uri;
 
   @override
-  String toString() => uri;
+  String toString() => uri.name;
 }
 
 class _ClassHandle extends ObjectHandle {
-  /// Name of artificial class containing top-level members of a library.
-  static const String topLevelClassName = '';
-
   _LibraryHandle library;
   _NameHandle name;
 
@@ -578,33 +660,69 @@
   String toString() => '$enclosingMember::Closure/$closureIndex';
 }
 
-abstract class _TypeHandle extends ObjectHandle {}
+abstract class _TypeHandle extends ObjectHandle {
+  final TypeTag tag;
 
-class _SimpleTypeHandle extends _TypeHandle {
-  static const int flagIsDynamic = ObjectHandle.flagBit0;
-  static const int flagIsVoid = ObjectHandle.flagBit1;
-
-  _ClassHandle class_;
-  int _flags = 0;
-
-  _SimpleTypeHandle._empty();
-
-  _SimpleTypeHandle(this.class_);
-
-  _SimpleTypeHandle._dynamic() : _flags = flagIsDynamic;
-
-  _SimpleTypeHandle._void() : _flags = flagIsVoid;
+  _TypeHandle(this.tag);
 
   @override
-  ObjectKind get kind => ObjectKind.kSimpleType;
+  ObjectKind get kind => ObjectKind.kType;
 
   @override
-  int get flags => _flags;
+  int get flags => tag.index * ObjectHandle.flagBit0;
 
   @override
   set flags(int value) {
-    _flags = value;
+    if (value != flags) {
+      throw 'Unable to set flags for _TypeHandle (they are occupied by type tag)';
+    }
   }
+}
+
+class _DynamicTypeHandle extends _TypeHandle {
+  _DynamicTypeHandle() : super(TypeTag.kDynamic);
+
+  @override
+  void writeContents(BufferedWriter writer) {}
+
+  @override
+  void readContents(BufferedReader reader) {}
+
+  @override
+  int get hashCode => 2029;
+
+  @override
+  bool operator ==(other) => other is _DynamicTypeHandle;
+
+  @override
+  String toString() => 'dynamic';
+}
+
+class _VoidTypeHandle extends _TypeHandle {
+  _VoidTypeHandle() : super(TypeTag.kVoid);
+
+  @override
+  void writeContents(BufferedWriter writer) {}
+
+  @override
+  void readContents(BufferedReader reader) {}
+
+  @override
+  int get hashCode => 2039;
+
+  @override
+  bool operator ==(other) => other is _VoidTypeHandle;
+
+  @override
+  String toString() => 'void';
+}
+
+class _SimpleTypeHandle extends _TypeHandle {
+  _ClassHandle class_;
+
+  _SimpleTypeHandle._empty() : super(TypeTag.kSimpleType);
+
+  _SimpleTypeHandle(this.class_) : super(TypeTag.kSimpleType);
 
   @override
   void writeContents(BufferedWriter writer) {
@@ -624,29 +742,24 @@
   }
 
   @override
-  int get hashCode => class_.hashCode + _flags + 11;
+  int get hashCode => class_.hashCode + 11;
 
   @override
   bool operator ==(other) =>
-      other is _SimpleTypeHandle &&
-      this.class_ == other.class_ &&
-      this._flags == other._flags;
+      other is _SimpleTypeHandle && this.class_ == other.class_;
 
   @override
-  String toString() {
-    if ((_flags & flagIsDynamic) != 0) return 'dynamic';
-    if ((_flags & flagIsVoid) != 0) return 'void';
-    return '$class_';
-  }
+  String toString() => '$class_';
 }
 
 class _TypeParameterHandle extends _TypeHandle {
   ObjectHandle parent;
   int indexInParent;
 
-  _TypeParameterHandle._empty();
+  _TypeParameterHandle._empty() : super(TypeTag.kTypeParameter);
 
-  _TypeParameterHandle(this.parent, this.indexInParent) {
+  _TypeParameterHandle(this.parent, this.indexInParent)
+      : super(TypeTag.kTypeParameter) {
     assert(parent is _ClassHandle ||
         parent is _MemberHandle ||
         parent is _ClosureHandle ||
@@ -655,9 +768,6 @@
   }
 
   @override
-  ObjectKind get kind => ObjectKind.kTypeParameter;
-
-  @override
   bool get isCacheable => (parent != null);
 
   @override
@@ -694,46 +804,122 @@
 
 class _GenericTypeHandle extends _TypeHandle {
   _ClassHandle class_;
-  List<_TypeHandle> typeArgs;
+  _TypeArgumentsHandle typeArgs;
 
-  _GenericTypeHandle._empty();
+  _GenericTypeHandle._empty() : super(TypeTag.kGenericType);
 
-  _GenericTypeHandle(this.class_, this.typeArgs);
-
-  @override
-  ObjectKind get kind => ObjectKind.kGenericType;
+  _GenericTypeHandle(this.class_, this.typeArgs) : super(TypeTag.kGenericType);
 
   @override
   void writeContents(BufferedWriter writer) {
     writer.writePackedObject(class_);
-    writer.writePackedList(typeArgs);
+    writer.writePackedObject(typeArgs);
   }
 
   @override
   void readContents(BufferedReader reader) {
     class_ = reader.readPackedObject();
-    typeArgs = reader.readPackedList<_TypeHandle>();
+    typeArgs = reader.readPackedObject();
   }
 
   @override
   void accountUsesForObjectCopies(int numCopies) {
     class_._useCount += numCopies;
-    typeArgs.forEach((t) {
-      t._useCount += numCopies;
-    });
+    if (typeArgs != null) {
+      typeArgs._useCount += numCopies;
+    }
   }
 
   @override
-  int get hashCode => _combineHashes(class_.hashCode, listHashCode(typeArgs));
+  int get hashCode => _combineHashes(class_.hashCode, typeArgs.hashCode);
 
   @override
   bool operator ==(other) =>
       other is _GenericTypeHandle &&
       this.class_ == other.class_ &&
-      listEquals(this.typeArgs, other.typeArgs);
+      this.typeArgs == other.typeArgs;
 
   @override
-  String toString() => '$class_ < ${typeArgs.join(', ')} >';
+  String toString() => '$class_ $typeArgs';
+}
+
+class _RecursiveGenericTypeHandle extends _TypeHandle {
+  int id;
+  _ClassHandle class_;
+  _TypeArgumentsHandle typeArgs;
+
+  _RecursiveGenericTypeHandle._empty() : super(TypeTag.kRecursiveGenericType);
+
+  _RecursiveGenericTypeHandle(this.id, this.class_, this.typeArgs)
+      : super(TypeTag.kRecursiveGenericType);
+
+  @override
+  bool get isCacheable => (id == 0);
+
+  @override
+  void writeContents(BufferedWriter writer) {
+    writer.writePackedUInt30(id);
+    writer.writePackedObject(class_);
+    writer.writePackedObject(typeArgs);
+  }
+
+  @override
+  void readContents(BufferedReader reader) {
+    id = reader.readPackedUInt30();
+    class_ = reader.readPackedObject();
+    typeArgs = reader.readPackedObject();
+  }
+
+  @override
+  void accountUsesForObjectCopies(int numCopies) {
+    class_._useCount += numCopies;
+    if (typeArgs != null) {
+      typeArgs._useCount += numCopies;
+    }
+  }
+
+  @override
+  int get hashCode => _combineHashes(class_.hashCode, typeArgs.hashCode);
+
+  @override
+  bool operator ==(other) =>
+      other is _RecursiveGenericTypeHandle &&
+      this.class_ == other.class_ &&
+      this.typeArgs == other.typeArgs;
+
+  @override
+  String toString() => '(recursive #$id) $class_ $typeArgs';
+}
+
+class _RecursiveTypeRefHandle extends _TypeHandle {
+  int id;
+
+  _RecursiveTypeRefHandle._empty() : super(TypeTag.kRecursiveTypeRef);
+
+  _RecursiveTypeRefHandle(this.id) : super(TypeTag.kRecursiveTypeRef);
+
+  @override
+  bool get isCacheable => false;
+
+  @override
+  void writeContents(BufferedWriter writer) {
+    writer.writePackedUInt30(id);
+  }
+
+  @override
+  void readContents(BufferedReader reader) {
+    id = reader.readPackedUInt30();
+  }
+
+  @override
+  int get hashCode => id;
+
+  @override
+  bool operator ==(other) =>
+      other is _RecursiveTypeRefHandle && this.id == other.id;
+
+  @override
+  String toString() => 'recursive-ref #$id';
 }
 
 class NameAndType {
@@ -756,52 +942,44 @@
 }
 
 class _FunctionTypeHandle extends _TypeHandle {
-  static const int flagHasOptionalPositionalParams = ObjectHandle.flagBit0;
-  static const int flagHasOptionalNamedParams = ObjectHandle.flagBit1;
-  static const int flagHasTypeParams = ObjectHandle.flagBit2;
+  static const int flagHasOptionalPositionalParams = 1 << 0;
+  static const int flagHasOptionalNamedParams = 1 << 1;
+  static const int flagHasTypeParams = 1 << 2;
 
-  int _flags = 0;
+  int functionTypeFlags = 0;
   List<NameAndType> typeParams;
   int numRequiredParams;
   List<_TypeHandle> positionalParams;
   List<NameAndType> namedParams;
   _TypeHandle returnType;
 
-  _FunctionTypeHandle._empty();
+  _FunctionTypeHandle._empty() : super(TypeTag.kFunctionType);
 
   _FunctionTypeHandle(this.typeParams, this.numRequiredParams,
-      this.positionalParams, this.namedParams, this.returnType) {
+      this.positionalParams, this.namedParams, this.returnType)
+      : super(TypeTag.kFunctionType) {
     assert(numRequiredParams <= positionalParams.length + namedParams.length);
     if (numRequiredParams < positionalParams.length) {
       assert(namedParams.isEmpty);
-      _flags |= flagHasOptionalPositionalParams;
+      functionTypeFlags |= flagHasOptionalPositionalParams;
     }
     if (namedParams.isNotEmpty) {
       assert(numRequiredParams == positionalParams.length);
-      _flags |= flagHasOptionalNamedParams;
+      functionTypeFlags |= flagHasOptionalNamedParams;
     }
     if (typeParams.isNotEmpty) {
-      _flags |= flagHasTypeParams;
+      functionTypeFlags |= flagHasTypeParams;
     }
   }
 
   @override
-  int get flags => _flags;
-
-  @override
-  set flags(int value) {
-    _flags = value;
-  }
-
-  ObjectKind get kind => ObjectKind.kFunctionType;
-
-  @override
   void writeContents(BufferedWriter writer) {
-    if ((_flags & flagHasTypeParams) != 0) {
+    writer.writePackedUInt30(functionTypeFlags);
+    if ((functionTypeFlags & flagHasTypeParams) != 0) {
       new TypeParametersDeclaration(typeParams).write(writer);
     }
     writer.writePackedUInt30(positionalParams.length + namedParams.length);
-    if (_flags &
+    if (functionTypeFlags &
             (flagHasOptionalPositionalParams | flagHasOptionalNamedParams) !=
         0) {
       writer.writePackedUInt30(numRequiredParams);
@@ -818,19 +996,21 @@
 
   @override
   void readContents(BufferedReader reader) {
-    if ((_flags & flagHasTypeParams) != 0) {
+    functionTypeFlags = reader.readPackedUInt30();
+    if ((functionTypeFlags & flagHasTypeParams) != 0) {
       typeParams = new TypeParametersDeclaration.read(reader).typeParams;
     } else {
       typeParams = const <NameAndType>[];
     }
     final int numParams = reader.readPackedUInt30();
     numRequiredParams = numParams;
-    if ((_flags &
+    if ((functionTypeFlags &
             (flagHasOptionalPositionalParams | flagHasOptionalNamedParams)) !=
         0) {
       numRequiredParams = reader.readPackedUInt30();
     }
-    final bool hasNamedParams = (_flags & flagHasOptionalNamedParams) != 0;
+    final bool hasNamedParams =
+        (functionTypeFlags & flagHasOptionalNamedParams) != 0;
     positionalParams = new List<_TypeHandle>.generate(
         hasNamedParams ? numRequiredParams : numParams,
         (_) => reader.readPackedObject());
@@ -857,6 +1037,24 @@
   }
 
   @override
+  bool get isCacheable {
+    for (var param in positionalParams) {
+      if (!param.isCacheable) {
+        return false;
+      }
+    }
+    for (var param in namedParams) {
+      if (!param.type.isCacheable) {
+        return false;
+      }
+    }
+    if (!returnType.isCacheable) {
+      return false;
+    }
+    return true;
+  }
+
+  @override
   int get hashCode {
     int hash = listHashCode(typeParams);
     hash = _combineHashes(hash, numRequiredParams);
@@ -988,50 +1186,6 @@
   String toString() => '< ${args.join(', ')} >';
 }
 
-class _FinalizedGenericTypeHandle extends _TypeHandle {
-  _ClassHandle class_;
-  _TypeArgumentsHandle typeArgs;
-
-  _FinalizedGenericTypeHandle._empty();
-
-  _FinalizedGenericTypeHandle(this.class_, this.typeArgs);
-
-  @override
-  ObjectKind get kind => ObjectKind.kFinalizedGenericType;
-
-  @override
-  void writeContents(BufferedWriter writer) {
-    writer.writePackedObject(class_);
-    writer.writePackedObject(typeArgs);
-  }
-
-  @override
-  void readContents(BufferedReader reader) {
-    class_ = reader.readPackedObject();
-    typeArgs = reader.readPackedObject();
-  }
-
-  @override
-  void accountUsesForObjectCopies(int numCopies) {
-    class_._useCount += numCopies;
-    if (typeArgs != null) {
-      typeArgs._useCount += numCopies;
-    }
-  }
-
-  @override
-  int get hashCode => _combineHashes(class_.hashCode, typeArgs.hashCode);
-
-  @override
-  bool operator ==(other) =>
-      other is _FinalizedGenericTypeHandle &&
-      this.class_ == other.class_ &&
-      this.typeArgs == other.typeArgs;
-
-  @override
-  String toString() => '$class_ $typeArgs';
-}
-
 class _ConstObjectHandle extends ObjectHandle {
   ConstTag tag;
   dynamic value;
@@ -1274,7 +1428,7 @@
   }
 }
 
-class _ArgDescHandle extends _TypeHandle {
+class _ArgDescHandle extends ObjectHandle {
   static const int flagHasNamedArgs = ObjectHandle.flagBit0;
   static const int flagHasTypeArgs = ObjectHandle.flagBit1;
 
@@ -1351,6 +1505,75 @@
       'ArgDesc num-args $numArguments, num-type-args $numTypeArguments, names $argNames';
 }
 
+class _ScriptHandle extends ObjectHandle {
+  static const int flagHasSourceFile = ObjectHandle.flagBit0;
+
+  int _flags = 0;
+  ObjectHandle uri;
+  SourceFile _source;
+  ForwardReference<SourceFile> _sourceForwardReference;
+
+  _ScriptHandle._empty();
+
+  _ScriptHandle(this.uri, this._source) {
+    if (_source != null) {
+      _flags |= flagHasSourceFile;
+    }
+  }
+
+  @override
+  ObjectKind get kind => ObjectKind.kScript;
+
+  @override
+  int get flags => _flags;
+
+  @override
+  set flags(int value) {
+    _flags = value;
+  }
+
+  SourceFile get source {
+    // Unwrap forward reference on the first access.
+    if (_sourceForwardReference != null) {
+      _source = _sourceForwardReference.get();
+      _sourceForwardReference = null;
+    }
+    return _source;
+  }
+
+  set source(SourceFile sourceFile) {
+    _source = sourceFile;
+  }
+
+  @override
+  void writeContents(BufferedWriter writer) {
+    writer.writePackedObject(uri);
+    if ((_flags & flagHasSourceFile) != 0) {
+      writer.writeLinkOffset(source);
+    }
+  }
+
+  @override
+  void readContents(BufferedReader reader) {
+    uri = reader.readPackedObject();
+    if ((_flags & flagHasSourceFile) != 0) {
+      // Script handles in the object table may be read before source files,
+      // so use forwarding reference here.
+      _sourceForwardReference =
+          reader.readLinkOffsetAsForwardReference<SourceFile>();
+    }
+  }
+
+  @override
+  int get hashCode => uri.hashCode;
+
+  @override
+  bool operator ==(other) => other is _ScriptHandle && this.uri == other.uri;
+
+  @override
+  String toString() => "$uri";
+}
+
 class ObjectTable implements ObjectWriter, ObjectReader {
   /// Object is added to an index table if it is used more than this
   /// number of times.
@@ -1367,8 +1590,8 @@
   _NodeVisitor _nodeVisitor;
 
   ObjectTable() {
-    _dynamicType = getOrAddObject(new _SimpleTypeHandle._dynamic());
-    _voidType = getOrAddObject(new _SimpleTypeHandle._void());
+    _dynamicType = getOrAddObject(new _DynamicTypeHandle());
+    _voidType = getOrAddObject(new _VoidTypeHandle());
     _nodeVisitor = new _NodeVisitor(this);
   }
 
@@ -1417,6 +1640,7 @@
   }
 
   ObjectHandle getNameHandle(Library library, String name) {
+    assert(name != null);
     final libraryHandle = library != null ? getHandle(library) : null;
     return getOrAddObject(new _NameHandle(libraryHandle, name));
   }
@@ -1427,6 +1651,12 @@
         name.library, mangleSelectorName(name.name, isGetter, isSetter));
   }
 
+  ObjectHandle getTopLevelClassHandle(Library library) {
+    final libraryHandle = getHandle(library);
+    final name = getNameHandle(null, topLevelClassName);
+    return getOrAddObject(new _ClassHandle(libraryHandle, name));
+  }
+
   ObjectHandle getMemberHandle(Member member,
       {bool isGetter: false, bool isSetter: false}) {
     final parent = member.parent;
@@ -1434,9 +1664,7 @@
     if (parent is Class) {
       classHandle = getHandle(parent);
     } else if (parent is Library) {
-      final library = getHandle(parent);
-      final name = getNameHandle(null, _ClassHandle.topLevelClassName);
-      classHandle = getOrAddObject(new _ClassHandle(library, name));
+      classHandle = getTopLevelClassHandle(parent);
     } else {
       throw "Unexpected Member's parent ${parent.runtimeType} $parent";
     }
@@ -1484,6 +1712,15 @@
         new List<String>.from(args.named.map((ne) => ne.name)));
   }
 
+  ObjectHandle getScriptHandle(Uri uri, SourceFile source) {
+    ObjectHandle uriHandle = getNameHandle(null, uri.toString());
+    _ScriptHandle handle = getOrAddObject(new _ScriptHandle(uriHandle, source));
+    if (handle.source == null && source != null) {
+      handle.source = source;
+    }
+    return handle;
+  }
+
   void declareClosure(
       FunctionNode function, Member enclosingMember, int closureIndex) {
     final handle = getOrAddObject(
@@ -1635,6 +1872,8 @@
 class _NodeVisitor extends Visitor<ObjectHandle> {
   final ObjectTable objectTable;
   final _typeParameters = <TypeParameter, ObjectHandle>{};
+  final Map<DartType, int> _recursiveTypeIds = <DartType, int>{};
+  final recursiveTypesValidator = new RecursiveTypesValidator();
 
   _NodeVisitor(this.objectTable);
 
@@ -1643,8 +1882,10 @@
       throw 'Unexpected node ${node.runtimeType} $node';
 
   @override
-  ObjectHandle visitLibrary(Library node) =>
-      objectTable.getOrAddObject(new _LibraryHandle(node.importUri.toString()));
+  ObjectHandle visitLibrary(Library node) {
+    final uri = objectTable.getNameHandle(null, node.importUri.toString());
+    return objectTable.getOrAddObject(new _LibraryHandle(uri));
+  }
 
   @override
   ObjectHandle visitClass(Class node) {
@@ -1670,14 +1911,11 @@
   @override
   ObjectHandle visitInterfaceType(InterfaceType node) {
     final classHandle = objectTable.getHandle(node.classNode);
-    if (node.typeArguments.isEmpty) {
+    if (!hasInstantiatorTypeArguments(node.classNode)) {
       return objectTable.getOrAddObject(new _SimpleTypeHandle(classHandle));
     }
-    // In order to save loading time, generic types are written out in
-    // finalized form, if possible.
-    //
-    // Object table serialization/deserialization cannot handle cycles between
-    // objects. Non-finalized types are not recursive, but finalization of
+
+    // Non-finalized types are not recursive, but finalization of
     // generic types includes flattening of type arguments and types could
     // become recursive. Consider the following example:
     //
@@ -1687,27 +1925,41 @@
     //  Foo<int> is not recursive, but finalized type is recursive:
     //  Foo<int>* = Foo [ Base [ Foo<int>* ], int ]
     //
-    // Recursive types are very rare, so object table includes such types in
-    // non-finalized form.
+    // Object table serialization/deserialization cannot handle cycles between
+    // objects, so recursive types require extra care when serializing.
+    // Back references to the already serialized types are represented as
+    // _RecursiveTypeRefHandle objects, which are only valid in the context
+    // of enclosing top-level _RecursiveGenericType.
     //
-    // VM handles recursive types by introducing placeholder
-    // TypeRef objects. Also, VM ensures that recursive types are contractive
-    // (e.g. their fully finalized representation should be finite).
-    //
-    if (!isRecursiveAfterFlattening(node)) {
-      List<DartType> instantiatorArgs =
-          getInstantiatorTypeArguments(node.classNode, node.typeArguments);
-      ObjectHandle typeArgsHandle =
-          objectTable.getTypeArgumentsHandle(instantiatorArgs);
-      return objectTable.getOrAddObject(
-          new _FinalizedGenericTypeHandle(classHandle, typeArgsHandle));
-    } else {
-      final List<_TypeHandle> typeArgs = node.typeArguments
-          .map((t) => objectTable.getHandle(t) as _TypeHandle)
-          .toList();
+    int recursiveId = _recursiveTypeIds[node];
+    if (recursiveId != null) {
       return objectTable
-          .getOrAddObject(new _GenericTypeHandle(classHandle, typeArgs));
+          .getOrAddObject(new _RecursiveTypeRefHandle(recursiveId));
     }
+
+    recursiveTypesValidator.validateType(node);
+
+    final isRecursive = recursiveTypesValidator.isRecursive(node);
+    if (isRecursive) {
+      recursiveId = _recursiveTypeIds.length;
+      _recursiveTypeIds[node] = recursiveId;
+    }
+
+    List<DartType> instantiatorArgs =
+        getInstantiatorTypeArguments(node.classNode, node.typeArguments);
+    ObjectHandle typeArgsHandle =
+        objectTable.getTypeArgumentsHandle(instantiatorArgs);
+
+    final result = objectTable.getOrAddObject(isRecursive
+        ? new _RecursiveGenericTypeHandle(
+            recursiveId, classHandle, typeArgsHandle)
+        : new _GenericTypeHandle(classHandle, typeArgsHandle));
+
+    if (isRecursive) {
+      _recursiveTypeIds.remove(node);
+    }
+
+    return result;
   }
 
   @override
diff --git a/pkg/vm/lib/bytecode/recursive_types_validator.dart b/pkg/vm/lib/bytecode/recursive_types_validator.dart
new file mode 100644
index 0000000..acaa020
--- /dev/null
+++ b/pkg/vm/lib/bytecode/recursive_types_validator.dart
@@ -0,0 +1,175 @@
+// Copyright (c) 2019, 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.
+
+library vm.bytecode.recursive_types_validator;
+
+import 'package:kernel/ast.dart' hide MapEntry;
+import 'package:kernel/type_algebra.dart' show Substitution;
+
+import 'generics.dart'
+    show flattenInstantiatorTypeArguments, hasFreeTypeParameters;
+
+/// Detect recursive types and validates that finalized (flattened)
+/// representation of generic types is valid (finite).
+class RecursiveTypesValidator {
+  final Set<DartType> _validatedTypes = <DartType>{};
+  final Set<DartType> _recursiveTypes = <DartType>{};
+  final Set<Class> _validatedClases = <Class>{};
+
+  /// Validates [type].
+  void validateType(DartType type) {
+    if (!isValidated(type)) {
+      final visitor = new _RecursiveTypesVisitor(this);
+      visitor.visit(type);
+      _validatedTypes.addAll(visitor.validated);
+      _recursiveTypes.addAll(visitor.recursive);
+    }
+  }
+
+  bool isValidated(DartType type) => _validatedTypes.contains(type);
+
+  /// Returns true if [type] is recursive.
+  /// Should be called only after validating [type].
+  bool isRecursive(DartType type) {
+    assert(isValidated(type));
+    return _recursiveTypes.contains(type);
+  }
+
+  void validateClass(Class cls) {
+    if (_validatedClases.add(cls)) {
+      try {
+        validateType(cls.thisType);
+      } on IllegalRecursiveTypeException catch (e) {
+        _validatedClases.remove(cls);
+        throw e;
+      }
+    }
+  }
+}
+
+class IllegalRecursiveTypeException {
+  final DartType type;
+  IllegalRecursiveTypeException(this.type);
+}
+
+class _RecursiveTypesVisitor extends DartTypeVisitor<void> {
+  final RecursiveTypesValidator validator;
+  final Set<DartType> validated = <DartType>{};
+  final Set<DartType> recursive = <DartType>{};
+  final Set<DartType> _visited = new Set<DartType>();
+  final List<DartType> _stack = <DartType>[];
+
+  _RecursiveTypesVisitor(this.validator);
+
+  void visit(DartType type) {
+    if (validator.isValidated(type)) {
+      return;
+    }
+
+    if (!_visited.add(type)) {
+      final int start = _stack.lastIndexOf(type);
+      _verifyRecursiveType(start, type);
+      for (int i = start; i < _stack.length; ++i) {
+        recursive.add(_stack[i]);
+      }
+      return;
+    }
+
+    _stack.add(type);
+
+    type.accept(this);
+
+    _stack.removeLast();
+    _visited.remove(type);
+
+    validated.add(type);
+  }
+
+  @override
+  void defaultDartType(DartType node) =>
+      throw 'Unexpected type ${node.runtimeType} $node';
+
+  @override
+  void visitInvalidType(InvalidType node) {}
+
+  @override
+  void visitDynamicType(DynamicType node) {}
+
+  @override
+  void visitVoidType(VoidType node) {}
+
+  @override
+  void visitBottomType(BottomType node) {}
+
+  @override
+  void visitTypeParameterType(TypeParameterType node) {}
+
+  @override
+  void visitInterfaceType(InterfaceType node) {
+    // Validate class declaration type separately
+    // to avoid failures due to types in the current _stack.
+    validator.validateClass(node.classNode);
+
+    for (var typeArg in node.typeArguments) {
+      visit(typeArg);
+    }
+    final flatTypeArgs =
+        flattenInstantiatorTypeArguments(node.classNode, node.typeArguments);
+    for (var typeArg in flatTypeArgs.getRange(
+        0, flatTypeArgs.length - node.typeArguments.length)) {
+      visit(typeArg);
+    }
+  }
+
+  @override
+  void visitTypedefType(TypedefType node) => visit(node.unalias);
+
+  @override
+  void visitFunctionType(FunctionType node) {
+    for (var p in node.positionalParameters) {
+      visit(p);
+    }
+    for (var p in node.namedParameters) {
+      visit(p.type);
+    }
+    visit(node.returnType);
+  }
+
+  void _verifyRecursiveType(int start, DartType type) {
+    if (type is InterfaceType) {
+      if (!hasFreeTypeParameters(type.typeArguments)) {
+        return;
+      }
+
+      for (int i = start + 1; i < _stack.length; ++i) {
+        final other = _stack[i];
+        if (other is InterfaceType &&
+            other.classNode == type.classNode &&
+            hasFreeTypeParameters(other.typeArguments)) {
+          if (!listEquals(_eraseTypeParameters(type.typeArguments),
+              _eraseTypeParameters(other.typeArguments))) {
+            throw IllegalRecursiveTypeException(type);
+          }
+        }
+      }
+    } else {
+      throw 'Unexpected recursive type ${type.runtimeType} $type';
+    }
+  }
+
+  List<DartType> _eraseTypeParameters(List<DartType> typeArgs) {
+    return typeArgs
+        .map((DartType t) =>
+            const _EraseTypeParametersToDynamic().substituteType(t))
+        .toList();
+  }
+}
+
+class _EraseTypeParametersToDynamic extends Substitution {
+  const _EraseTypeParametersToDynamic();
+
+  DartType getSubstitute(TypeParameter parameter, bool upperBound) {
+    return const DynamicType();
+  }
+}
diff --git a/pkg/vm/lib/bytecode/source_positions.dart b/pkg/vm/lib/bytecode/source_positions.dart
index f0bef3d..af58534 100644
--- a/pkg/vm/lib/bytecode/source_positions.dart
+++ b/pkg/vm/lib/bytecode/source_positions.dart
@@ -60,3 +60,29 @@
         new MapEntry(pc, 'source position $fileOffset'));
   }
 }
+
+/// Keeps file offsets of line starts. This information is used to
+/// decode source positions to line/column.
+class LineStarts {
+  final List<int> lineStarts;
+
+  LineStarts(this.lineStarts);
+
+  void write(BufferedWriter writer) {
+    writer.writePackedUInt30(lineStarts.length);
+    final encodeLineStarts = new PackedUInt30DeltaEncoder();
+    for (int lineStart in lineStarts) {
+      encodeLineStarts.write(writer, lineStart);
+    }
+  }
+
+  factory LineStarts.read(BufferedReader reader) {
+    final decodeLineStarts = new PackedUInt30DeltaDecoder();
+    final lineStarts = new List<int>.generate(
+        reader.readPackedUInt30(), (_) => decodeLineStarts.read(reader));
+    return new LineStarts(lineStarts);
+  }
+
+  @override
+  String toString() => 'Line starts: $lineStarts';
+}
diff --git a/pkg/vm/lib/kernel_front_end.dart b/pkg/vm/lib/kernel_front_end.dart
index 0dd2962..8402ef8 100644
--- a/pkg/vm/lib/kernel_front_end.dart
+++ b/pkg/vm/lib/kernel_front_end.dart
@@ -45,9 +45,9 @@
 import 'package:kernel/target/targets.dart' show Target, TargetFlags, getTarget;
 import 'package:kernel/vm/constants_native_effects.dart' as vm_constants;
 
-import 'bytecode/ast_remover.dart' show ASTRemover;
 import 'bytecode/bytecode_serialization.dart' show BytecodeSizeStatistics;
-import 'bytecode/gen_bytecode.dart' show generateBytecode;
+import 'bytecode/gen_bytecode.dart'
+    show generateBytecode, createFreshComponentWithBytecode;
 
 import 'constants_error_reporter.dart' show ForwardConstantEvaluationErrors;
 import 'target/install.dart' show installAdditionalTargets;
@@ -306,7 +306,7 @@
   options.onDiagnostic = errorDetector;
 
   setVMEnvironmentDefines(environmentDefines, options);
-  final component = await kernelForProgram(source, options);
+  Component component = await kernelForProgram(source, options);
 
   // Run global transformations only if component is correct.
   if (aot && component != null) {
@@ -327,6 +327,7 @@
       generateBytecode(component,
           enableAsserts: enableAsserts,
           emitSourcePositions: emitBytecodeSourcePositions,
+          emitSourceFiles: options.embedSourceText,
           emitLocalVarInfo: emitBytecodeLocalVarInfo,
           emitAnnotations: emitBytecodeAnnotations,
           useFutureBytecodeFormat: useFutureBytecodeFormat,
@@ -334,7 +335,7 @@
     });
 
     if (dropAST) {
-      new ASTRemover(component).visitComponent(component);
+      component = createFreshComponentWithBytecode(component);
     }
   }
 
@@ -717,7 +718,7 @@
         component.problemsAsJson = null;
       }
 
-      ASTRemover astRemover;
+      Component partComponent = component;
       if (genBytecode) {
         final List<Library> libraries = component.libraries
             .where((lib) => packageFor(lib) == package)
@@ -727,26 +728,21 @@
             hierarchy: hierarchy,
             enableAsserts: enableAsserts,
             emitSourcePositions: emitBytecodeSourcePositions,
+            emitSourceFiles: compilerOptions.embedSourceText,
             emitLocalVarInfo: emitBytecodeLocalVarInfo,
             emitAnnotations: emitBytecodeAnnotations,
             useFutureBytecodeFormat: useFutureBytecodeFormat,
             environmentDefines: environmentDefines);
 
         if (dropAST) {
-          astRemover = new ASTRemover(component);
-          for (var library in libraries) {
-            astRemover.visitLibrary(library);
-          }
+          partComponent = createFreshComponentWithBytecode(component);
         }
       }
 
       final BinaryPrinter printer = new LimitedBinaryPrinter(sink,
           (lib) => packageFor(lib) == package, false /* excludeUriToSource */);
-      printer.writeComponentFile(component);
+      printer.writeComponentFile(partComponent);
 
-      if (genBytecode && dropAST) {
-        astRemover.restoreAST();
-      }
       component.mainMethod = main;
       component.problemsAsJson = problems;
 
diff --git a/pkg/vm/lib/metadata/bytecode.dart b/pkg/vm/lib/metadata/bytecode.dart
index 6b18d0e..97e250c 100644
--- a/pkg/vm/lib/metadata/bytecode.dart
+++ b/pkg/vm/lib/metadata/bytecode.dart
@@ -6,53 +6,26 @@
 
 import 'package:kernel/ast.dart'
     show BinarySink, BinarySource, MetadataRepository, Node, TreeNode;
-import 'package:kernel/ast.dart' as ast show Component;
 import '../bytecode/bytecode_serialization.dart'
     show BufferedWriter, BufferedReader, LinkWriter, LinkReader;
-import '../bytecode/declarations.dart' show Component, Members;
+import '../bytecode/declarations.dart' show Component;
 
-abstract class BytecodeMetadata {
-  void write(BufferedWriter writer);
-}
-
-class MembersBytecodeMetadata extends BytecodeMetadata {
-  final Members members;
-
-  MembersBytecodeMetadata(this.members);
-
-  @override
-  void write(BufferedWriter writer) {
-    writer.writeLinkOffset(members);
-  }
-
-  factory MembersBytecodeMetadata.read(BufferedReader reader) {
-    return new MembersBytecodeMetadata(reader.readLinkOffset<Members>());
-  }
-
-  @override
-  String toString() => "\n"
-      "MembersBytecodeMetadata {\n"
-      "$members\n"
-      "}\n";
-}
-
-class ComponentBytecodeMetadata extends BytecodeMetadata {
+class BytecodeMetadata {
   final Component component;
 
-  ComponentBytecodeMetadata(this.component);
+  BytecodeMetadata(this.component);
 
-  @override
   void write(BufferedWriter writer) {
     component.write(writer);
   }
 
-  factory ComponentBytecodeMetadata.read(BufferedReader reader) {
-    return new ComponentBytecodeMetadata(new Component.read(reader));
+  factory BytecodeMetadata.read(BufferedReader reader) {
+    return new BytecodeMetadata(new Component.read(reader));
   }
 
   @override
   String toString() => "\n"
-      "ComponentBytecodeMetadata {\n"
+      "BytecodeMetadata {\n"
       "$component\n"
       "}\n";
 }
@@ -66,19 +39,10 @@
   final Map<TreeNode, BytecodeMetadata> mapping =
       <TreeNode, BytecodeMetadata>{};
 
-  Component bytecodeComponent;
-  LinkWriter linkWriter;
-  LinkReader linkReader;
-
   @override
   void writeToBinary(BytecodeMetadata metadata, Node node, BinarySink sink) {
-    if (node is ast.Component) {
-      bytecodeComponent = (metadata as ComponentBytecodeMetadata).component;
-      linkWriter = new LinkWriter();
-    } else {
-      assert(bytecodeComponent != null);
-      assert(linkWriter != null);
-    }
+    final bytecodeComponent = metadata.component;
+    final linkWriter = new LinkWriter();
     final writer = new BufferedWriter(
         bytecodeComponent.version,
         bytecodeComponent.stringTable,
@@ -91,22 +55,10 @@
 
   @override
   BytecodeMetadata readFromBinary(Node node, BinarySource source) {
-    if (node is ast.Component) {
-      linkReader = new LinkReader();
-      final reader = new BufferedReader(
-          -1, null, null, linkReader, source.bytes,
-          baseOffset: source.currentOffset);
-      bytecodeComponent = new Component.read(reader);
-      return new ComponentBytecodeMetadata(bytecodeComponent);
-    } else {
-      final reader = new BufferedReader(
-          bytecodeComponent.version,
-          bytecodeComponent.stringTable,
-          bytecodeComponent.objectTable,
-          linkReader,
-          source.bytes,
-          baseOffset: source.currentOffset);
-      return new MembersBytecodeMetadata.read(reader);
-    }
+    final linkReader = new LinkReader();
+    final reader = new BufferedReader(-1, null, null, linkReader, source.bytes,
+        baseOffset: source.currentOffset);
+    final bytecodeComponent = new Component.read(reader);
+    return new BytecodeMetadata(bytecodeComponent);
   }
 }
diff --git a/pkg/vm/lib/target/flutter.dart b/pkg/vm/lib/target/flutter.dart
index 02673e7..ed4683c 100644
--- a/pkg/vm/lib/target/flutter.dart
+++ b/pkg/vm/lib/target/flutter.dart
@@ -12,6 +12,8 @@
 class FlutterTarget extends VmTarget {
   FlutterTarget(TargetFlags flags) : super(flags);
 
+  WidgetCreatorTracker _widgetTracker;
+
   @override
   String get name => 'flutter';
 
@@ -53,7 +55,10 @@
       DiagnosticReporter diagnosticReporter,
       {void logger(String msg)}) {
     if (flags.trackWidgetCreation) {
-      new WidgetCreatorTracker().transform(component, libraries);
+      if (_widgetTracker == null) {
+        _widgetTracker = WidgetCreatorTracker();
+      }
+      _widgetTracker.transform(component, libraries);
     }
   }
 }
diff --git a/pkg/vm/lib/transformations/type_flow/transformer.dart b/pkg/vm/lib/transformations/type_flow/transformer.dart
index b0fb04b..5843438 100644
--- a/pkg/vm/lib/transformations/type_flow/transformer.dart
+++ b/pkg/vm/lib/transformations/type_flow/transformer.dart
@@ -739,8 +739,8 @@
   }
 
   @override
-  TreeNode visitConstantExpression(ConstantExpression node) {
-    shaker.constantVisitor.analyzeConstant(node.constant);
+  Constant visitConstant(Constant node) {
+    shaker.constantVisitor.analyzeConstant(node);
     return node;
   }
 
diff --git a/pkg/vm/lib/v8_snapshot_profile.dart b/pkg/vm/lib/v8_snapshot_profile.dart
index efda4c1..2b1ad73 100644
--- a/pkg/vm/lib/v8_snapshot_profile.dart
+++ b/pkg/vm/lib/v8_snapshot_profile.dart
@@ -46,10 +46,10 @@
 ];
 
 class NodeInfo {
-  String type;
-  String name;
-  int id;
-  int selfSize;
+  final String type;
+  final String name;
+  final int id;
+  final int selfSize;
   NodeInfo(
     this.type,
     this.name,
@@ -58,6 +58,16 @@
   );
 }
 
+class EdgeInfo {
+  final int target;
+  final String type;
+
+  // Either a string for property names or an int for array/context elements.
+  final dynamic nameOrIndex;
+
+  EdgeInfo(this.target, this.type, this.nameOrIndex);
+}
+
 class V8SnapshotProfile extends Graph<int> {
   // Indexed by node offset.
   final Map<int, _NodeInfo> _nodes = {};
@@ -265,4 +275,12 @@
     final name = info.name != null ? _strings[info.name] : null;
     return NodeInfo(type, name, info.id, info.selfSize);
   }
+
+  Iterable<EdgeInfo> targets(int node) sync* {
+    for (final _EdgeInfo info in _toEdges[node]) {
+      final String type = _edgeTypes[info.type];
+      yield EdgeInfo(info.nodeOffset, type,
+          type == "property" ? _strings[info.nameOrIndex] : info.nameOrIndex);
+    }
+  }
 }
diff --git a/pkg/vm/test/bytecode/generics_test.dart b/pkg/vm/test/bytecode/generics_test.dart
deleted file mode 100644
index 6b0a75f..0000000
--- a/pkg/vm/test/bytecode/generics_test.dart
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright (c) 2019, 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 'package:kernel/ast.dart';
-import 'package:kernel/core_types.dart' show CoreTypes;
-import 'package:kernel/testing/mock_sdk_component.dart';
-import 'package:test/test.dart';
-import 'package:vm/bytecode/generics.dart';
-
-main() {
-  Library lib;
-  Supertype objectSuper;
-  DartType intType;
-  Class base;
-
-  Class addClass(String name, List<TypeParameter> typeParameters) {
-    Class cls = new Class(
-        name: name, supertype: objectSuper, typeParameters: typeParameters);
-    lib.addClass(cls);
-    return cls;
-  }
-
-  setUp(() {
-    // Start with mock SDK libraries.
-    Component component = createMockSdkComponent();
-    CoreTypes coreTypes = new CoreTypes(component);
-    objectSuper = coreTypes.objectClass.asThisSupertype;
-    intType = new InterfaceType(coreTypes.intClass);
-
-    // Add the test library.
-    lib = new Library(Uri.parse('org-dartlang:///test.dart'), name: 'lib');
-    lib.parent = component;
-    component.libraries.add(lib);
-
-    // class Base<T>
-    base = addClass('Base', [new TypeParameter('T')]);
-  });
-
-  tearDown(() {});
-
-  test('isRecursiveAfterFlattening-00', () async {
-    // class Derived<T> extends Base<Derived<T>>
-    TypeParameter t = new TypeParameter('T');
-    Class derived = addClass('Derived', [t]);
-    DartType derivedOfT =
-        new InterfaceType(derived, [new TypeParameterType(t)]);
-    DartType derivedOfInt = new InterfaceType(derived, [intType]);
-    derived.supertype = new Supertype(base, [derivedOfT]);
-
-    expect(isRecursiveAfterFlattening(derivedOfT), isTrue);
-    expect(isRecursiveAfterFlattening(derivedOfInt), isTrue);
-  });
-
-  test('isRecursiveAfterFlattening-01', () async {
-    // class Derived<T> extends Base<Derived<Derived<int>>>
-    TypeParameter t = new TypeParameter('T');
-    Class derived = addClass('Derived', [t]);
-    DartType derivedOfT =
-        new InterfaceType(derived, [new TypeParameterType(t)]);
-    DartType derivedOfInt = new InterfaceType(derived, [intType]);
-    DartType derivedOfDerivedOfInt = new InterfaceType(derived, [derivedOfInt]);
-    derived.supertype = new Supertype(base, [derivedOfDerivedOfInt]);
-
-    expect(isRecursiveAfterFlattening(derivedOfT), isFalse);
-    expect(isRecursiveAfterFlattening(derivedOfInt), isTrue);
-  });
-
-  test('isRecursiveAfterFlattening-02', () async {
-    // class Derived<T> extends Base<Derived<Derived<T>>>
-    TypeParameter t = new TypeParameter('T');
-    Class derived = addClass('Derived', [t]);
-    DartType derivedOfT =
-        new InterfaceType(derived, [new TypeParameterType(t)]);
-    DartType derivedOfInt = new InterfaceType(derived, [intType]);
-    DartType derivedOfDerivedOfT = new InterfaceType(derived, [derivedOfT]);
-    derived.supertype = new Supertype(base, [derivedOfDerivedOfT]);
-
-    expect(isRecursiveAfterFlattening(derivedOfT), isTrue);
-    expect(isRecursiveAfterFlattening(derivedOfInt), isTrue);
-  });
-
-  test('isRecursiveAfterFlattening-03', () async {
-    // class Derived1<U> extends Base<Derived2<U>>
-    // class Derived2<V> extends Base<Derived1<V>>
-    TypeParameter u = new TypeParameter('U');
-    Class derived1 = addClass('Derived1', [u]);
-
-    TypeParameter v = new TypeParameter('V');
-    Class derived2 = addClass('Derived2', [v]);
-
-    DartType derived2OfU =
-        new InterfaceType(derived2, [new TypeParameterType(u)]);
-    derived1.supertype = new Supertype(base, [derived2OfU]);
-
-    DartType derived1OfV =
-        new InterfaceType(derived1, [new TypeParameterType(v)]);
-    derived2.supertype = new Supertype(base, [derived1OfV]);
-
-    DartType derived1OfU =
-        new InterfaceType(derived1, [new TypeParameterType(u)]);
-    DartType derived1OfInt = new InterfaceType(derived1, [intType]);
-
-    DartType derived2OfV =
-        new InterfaceType(derived2, [new TypeParameterType(v)]);
-    DartType derived2OfInt = new InterfaceType(derived2, [intType]);
-
-    expect(isRecursiveAfterFlattening(derived1OfU), isTrue);
-    expect(isRecursiveAfterFlattening(derived1OfInt), isTrue);
-    expect(isRecursiveAfterFlattening(derived2OfV), isTrue);
-    expect(isRecursiveAfterFlattening(derived2OfInt), isTrue);
-  });
-}
diff --git a/pkg/vm/test/bytecode/recursive_types_validator_test.dart b/pkg/vm/test/bytecode/recursive_types_validator_test.dart
new file mode 100644
index 0000000..44c4522
--- /dev/null
+++ b/pkg/vm/test/bytecode/recursive_types_validator_test.dart
@@ -0,0 +1,164 @@
+// Copyright (c) 2019, 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 'package:kernel/ast.dart';
+import 'package:kernel/core_types.dart' show CoreTypes;
+import 'package:kernel/testing/mock_sdk_component.dart';
+import 'package:test/test.dart';
+import 'package:expect/expect.dart';
+import 'package:vm/bytecode/recursive_types_validator.dart';
+
+main() {
+  Library lib;
+  Supertype objectSuper;
+  DartType intType;
+  DartType doubleType;
+  Class base;
+  RecursiveTypesValidator validator;
+
+  Class addClass(String name, List<TypeParameter> typeParameters) {
+    Class cls = new Class(
+        name: name, supertype: objectSuper, typeParameters: typeParameters);
+    lib.addClass(cls);
+    return cls;
+  }
+
+  setUp(() {
+    // Start with mock SDK libraries.
+    Component component = createMockSdkComponent();
+    CoreTypes coreTypes = new CoreTypes(component);
+    objectSuper = coreTypes.objectClass.asThisSupertype;
+    intType = new InterfaceType(coreTypes.intClass);
+    doubleType = new InterfaceType(coreTypes.doubleClass);
+
+    // Add the test library.
+    lib = new Library(Uri.parse('org-dartlang:///test.dart'), name: 'lib');
+    lib.parent = component;
+    component.libraries.add(lib);
+
+    // class Base<T>
+    base = addClass('Base', [new TypeParameter('T')]);
+
+    validator = new RecursiveTypesValidator();
+  });
+
+  tearDown(() {});
+
+  test('simple-recursive-type', () async {
+    // class Derived<T> extends Base<Derived<T>>
+    TypeParameter t = new TypeParameter('T');
+    Class derived = addClass('Derived', [t]);
+    DartType derivedOfT =
+        new InterfaceType(derived, [new TypeParameterType(t)]);
+    DartType derivedOfInt = new InterfaceType(derived, [intType]);
+    derived.supertype = new Supertype(base, [derivedOfT]);
+
+    validator.validateType(derivedOfT);
+    Expect.isTrue(validator.isRecursive(derivedOfT));
+
+    validator.validateType(derivedOfInt);
+    Expect.isTrue(validator.isRecursive(derivedOfInt));
+  });
+
+  test('recursive-type-extends-instantiated', () async {
+    // class Derived<T> extends Base<Derived<Derived<int>>>
+    TypeParameter t = new TypeParameter('T');
+    Class derived = addClass('Derived', [t]);
+    DartType derivedOfT =
+        new InterfaceType(derived, [new TypeParameterType(t)]);
+    DartType derivedOfInt = new InterfaceType(derived, [intType]);
+    DartType derivedOfDerivedOfInt = new InterfaceType(derived, [derivedOfInt]);
+    derived.supertype = new Supertype(base, [derivedOfDerivedOfInt]);
+
+    validator.validateType(derivedOfT);
+    validator.validateType(derivedOfInt);
+
+    Expect.isFalse(validator.isRecursive(derivedOfT));
+    Expect.isTrue(validator.isRecursive(derivedOfInt));
+    Expect.isTrue(validator.isRecursive(derivedOfDerivedOfInt));
+  });
+
+  test('recursive-non-contractive-type', () async {
+    // class Derived<T> extends Base<Derived<Derived<T>>>
+    TypeParameter t = new TypeParameter('T');
+    Class derived = addClass('Derived', [t]);
+    DartType derivedOfT =
+        new InterfaceType(derived, [new TypeParameterType(t)]);
+    DartType derivedOfInt = new InterfaceType(derived, [intType]);
+    DartType derivedOfDerivedOfT = new InterfaceType(derived, [derivedOfT]);
+    derived.supertype = new Supertype(base, [derivedOfDerivedOfT]);
+
+    Expect.throws(() {
+      validator.validateType(derivedOfT);
+    });
+    Expect.throws(() {
+      validator.validateType(derivedOfDerivedOfT);
+    });
+    Expect.throws(() {
+      validator.validateType(derivedOfInt);
+    });
+  });
+
+  test('mutually-recursive-types', () async {
+    // class Derived1<U> extends Base<Derived2<U>>
+    // class Derived2<V> extends Base<Derived1<V>>
+    TypeParameter u = new TypeParameter('U');
+    Class derived1 = addClass('Derived1', [u]);
+
+    TypeParameter v = new TypeParameter('V');
+    Class derived2 = addClass('Derived2', [v]);
+
+    DartType derived2OfU =
+        new InterfaceType(derived2, [new TypeParameterType(u)]);
+    derived1.supertype = new Supertype(base, [derived2OfU]);
+
+    DartType derived1OfV =
+        new InterfaceType(derived1, [new TypeParameterType(v)]);
+    derived2.supertype = new Supertype(base, [derived1OfV]);
+
+    DartType derived1OfU =
+        new InterfaceType(derived1, [new TypeParameterType(u)]);
+    DartType derived1OfInt = new InterfaceType(derived1, [intType]);
+
+    DartType derived2OfV =
+        new InterfaceType(derived2, [new TypeParameterType(v)]);
+    DartType derived2OfInt = new InterfaceType(derived2, [intType]);
+
+    validator.validateType(derived1OfU);
+    Expect.isTrue(validator.isRecursive(derived1OfU));
+
+    validator.validateType(derived1OfInt);
+    Expect.isTrue(validator.isRecursive(derived1OfInt));
+
+    validator.validateType(derived2OfV);
+    Expect.isTrue(validator.isRecursive(derived2OfV));
+
+    validator.validateType(derived2OfInt);
+    Expect.isTrue(validator.isRecursive(derived2OfInt));
+  });
+
+  test('recursive-two-type-params', () async {
+    // class F<P1, P2> {}
+    // class E<Q1, Q2> extends F<E<Q1, int>, Q2> {}
+    TypeParameter p1 = new TypeParameter('P1');
+    TypeParameter p2 = new TypeParameter('P2');
+    Class f = addClass('F', [p1, p2]);
+
+    TypeParameter q1 = new TypeParameter('Q1');
+    TypeParameter q2 = new TypeParameter('Q2');
+    Class e = addClass('E', [q1, q2]);
+
+    DartType eOfQ1Int =
+        new InterfaceType(e, [new TypeParameterType(q1), intType]);
+    e.supertype = new Supertype(f, [eOfQ1Int, new TypeParameterType(q2)]);
+
+    DartType eOfIntDouble = new InterfaceType(e, [intType, doubleType]);
+
+    validator.validateType(eOfIntDouble);
+    validator.validateType(e.thisType);
+
+    Expect.isFalse(validator.isRecursive(eOfIntDouble));
+    Expect.isFalse(validator.isRecursive(e.thisType));
+  });
+}
diff --git a/pkg/vm/testcases/bytecode/asserts.dart.expect b/pkg/vm/testcases/bytecode/asserts.dart.expect
index 8bfc0e9..e1323cb 100644
--- a/pkg/vm/testcases/bytecode/asserts.dart.expect
+++ b/pkg/vm/testcases/bytecode/asserts.dart.expect
@@ -1,21 +1,19 @@
 main = #lib::main;
  [@vm.bytecode=
-ComponentBytecodeMetadata {
-
+BytecodeMetadata {
 Bytecode (version: stable)
 Main library: #lib
+Library '#lib'
+    name '#lib'
+    script '#lib'
 
-}
-] [@vm.bytecode=
-MembersBytecodeMetadata {
+Class '', script = '#lib'
 
-Members {
 
 Function 'test1', static, reflectable, debuggable
     parameters [dart:core::bool 'condition'] (required: 1)
     return-type void
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -42,7 +40,6 @@
     parameters [FunctionType () -> dart:core::bool 'condition', FunctionType () -> dart:core::String 'message'] (required: 2)
     return-type void
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -74,7 +71,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -84,7 +80,6 @@
 ConstantPool {
 }
 
-}
 
 }
 ]library #lib from "#lib" as #lib {
diff --git a/pkg/vm/testcases/bytecode/async.dart.expect b/pkg/vm/testcases/bytecode/async.dart.expect
index 4805482..32e7ee9 100644
--- a/pkg/vm/testcases/bytecode/async.dart.expect
+++ b/pkg/vm/testcases/bytecode/async.dart.expect
@@ -1,17 +1,16 @@
 main = #lib::main;
  [@vm.bytecode=
-ComponentBytecodeMetadata {
-
+BytecodeMetadata {
 Bytecode (version: stable)
 Main library: #lib
+Library '#lib'
+    name '#lib'
+    script '#lib'
 
-}
-] [@vm.bytecode=
-MembersBytecodeMetadata {
+Class '', script = '#lib'
 
-Members {
 Field 'asyncInFieldInitializer', type = FunctionType (dart:async::Future < dart:core::int >) -> dart:async::Future < dart:core::Null >, getter = 'get:asyncInFieldInitializer', reflectable, static
-    initializer 
+    initializer
 Bytecode {
   Entry                3
   CheckStack           0
@@ -167,7 +166,6 @@
   LoadContextVar       0, 1
   InterfaceCall        CP#37, 1
   ReturnTOS
-
 }
 
 Closure #lib::asyncInFieldInitializer (field)::Closure/0::':async_op' ([ dynamic :result, dynamic :exception, dynamic :stack_trace ]) -> dynamic
@@ -255,7 +253,6 @@
   LoadContextVar       0, 6
   PopLocal             r4
   Jump                 L4
-
 }
 
 
@@ -263,7 +260,6 @@
     parameters [] (required: 0)
     return-type dart:async::Future < dart:core::int >
 
-
 Bytecode {
   Entry                7
   CheckStack           0
@@ -420,7 +416,6 @@
   ReturnTOS
 L1:
   Trap
-
 }
 
 
@@ -428,7 +423,6 @@
     parameters [dart:async::Future < dart:core::int > 'a', dart:async::Future < dart:core::int > 'b'] (required: 2)
     return-type dart:async::Future < dart:core::int >
 
-
 Bytecode {
   Entry                4
   CheckStack           0
@@ -677,7 +671,6 @@
   PushInt              1
   JumpIfEqStrict       L6
   Jump                 L7
-
 }
 
 
@@ -685,7 +678,6 @@
     parameters [dart:core::List < dart:core::int > 'list'] (required: 1)
     return-type dart:async::Future < dart:core::int >
 
-
 Bytecode {
   Entry                4
   CheckStack           0
@@ -1058,7 +1050,6 @@
   LoadContextVar       0, 6
   PopLocal             r4
   Jump                 L11
-
 }
 
 
@@ -1066,7 +1057,6 @@
     parameters [dart:async::Future < dart:core::int > 'a', dart:async::Future < dart:core::int > 'b', dart:async::Future < dart:core::int > 'c'] (required: 3)
     return-type dart:async::Future < dart:core::int >
 
-
 Bytecode {
   Entry                4
   CheckStack           0
@@ -1637,7 +1627,6 @@
   PushInt              4
   JumpIfEqStrict       L16
   Jump                 L17
-
 }
 
 
@@ -1645,7 +1634,6 @@
     parameters [dart:async::Future < dart:core::int > 'a'] (required: 1)
     return-type dynamic
 
-
 Bytecode {
   Entry                4
   CheckStack           0
@@ -1808,7 +1796,6 @@
   LoadContextVar       1, 0
   InterfaceCall        CP#37, 1
   ReturnTOS
-
 }
 
 Closure #lib::closure::Closure/0::':async_op' ([ dynamic :result, dynamic :exception, dynamic :stack_trace ]) -> dynamic
@@ -1966,7 +1953,6 @@
   LoadContextVar       1, 5
   PopLocal             r4
   Jump                 L6
-
 }
 
 
@@ -1974,7 +1960,6 @@
     parameters [dart:async::Future < dart:core::int > 'a'] (required: 1)
     return-type dart:async::Future < dart:core::int >
 
-
 Bytecode {
   Entry                4
   CheckStack           0
@@ -2198,7 +2183,6 @@
   LoadContextVar       0, 6
   PopLocal             r4
   Jump                 L6
-
 }
 
 
@@ -2206,7 +2190,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -2216,7 +2199,6 @@
 ConstantPool {
 }
 
-}
 
 }
 ]library #lib from "#lib" as #lib {
diff --git a/pkg/vm/testcases/bytecode/bootstrapping.dart.expect b/pkg/vm/testcases/bytecode/bootstrapping.dart.expect
index e80bb9b..bfa9314 100644
--- a/pkg/vm/testcases/bytecode/bootstrapping.dart.expect
+++ b/pkg/vm/testcases/bytecode/bootstrapping.dart.expect
@@ -1,15 +1,14 @@
 main = #lib::main;
  [@vm.bytecode=
-ComponentBytecodeMetadata {
-
+BytecodeMetadata {
 Bytecode (version: stable)
 Main library: #lib
+Library '#lib'
+    name '#lib'
+    script '#lib'
 
-}
-] [@vm.bytecode=
-MembersBytecodeMetadata {
+Class '', script = '#lib'
 
-Members {
 Field '_stdinFD', type = dart:core::int, reflectable, static
     value = const 0
 
@@ -29,7 +28,6 @@
     parameters [dart:core::String 's'] (required: 1)
     return-type void
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -46,7 +44,6 @@
     parameters [dynamic 'arg'] (required: 1)
     return-type void
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -69,7 +66,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -85,7 +81,6 @@
     parameters [FunctionType (FunctionType () -> void) -> void 'closure'] (required: 1)
     return-type void
 
-
 Bytecode {
   Entry                1
   CheckStack           0
@@ -103,7 +98,6 @@
     parameters [dart:core::int 'stdin', dart:core::int 'stdout', dart:core::int 'stderr'] (required: 3)
     return-type void
 
-
 Bytecode {
   Entry                1
   CheckStack           0
@@ -127,7 +121,6 @@
     parameters [] (required: 0)
     return-type dart:core::Uri
 
-
 Bytecode {
   Entry                2
   CheckStack           0
@@ -198,7 +191,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                1
   CheckStack           0
@@ -219,7 +211,6 @@
     parameters [] (required: 0)
     return-type #lib::Stdin
 
-
 Bytecode {
   Entry                2
   CheckStack           0
@@ -257,7 +248,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -267,16 +257,9 @@
 ConstantPool {
 }
 
-}
+Class '_ScheduleImmediate', script = '#lib'
+    extends dart:core::Object
 
-}
-]library #lib from "#lib" as #lib {
-
-  typedef _ScheduleImmediateClosure = (() → void) → void;
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 Field '_closure', type = FunctionType (FunctionType () -> void) -> void, reflectable, static
     value = null
 
@@ -284,7 +267,6 @@
     parameters [] (required: 0)
     return-type #lib::_ScheduleImmediate
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -299,19 +281,10 @@
   [1] = Reserved
 }
 
-}
+Class '_NamespaceImpl', script = '#lib'
+    extends dart:core::Object
+    implements [#lib::_Namespace]
 
-}
-]  class _ScheduleImmediate extends dart.core::Object {
-    static field (() → void) → void _closure = null;
-    synthetic constructor •() → #lib::_ScheduleImmediate
-      : super dart.core::Object::•()
-      ;
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 Field '_cachedNamespace', type = #lib::_NamespaceImpl, reflectable, static
     value = null
 
@@ -319,7 +292,6 @@
     parameters [] (required: 0)
     return-type #lib::_NamespaceImpl
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -339,7 +311,6 @@
     parameters [#lib::_NamespaceImpl 'namespace', dynamic 'n'] (required: 2)
     return-type #lib::_NamespaceImpl
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -357,7 +328,6 @@
     parameters [#lib::_NamespaceImpl 'namespace'] (required: 1)
     return-type dart:core::int
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -374,7 +344,6 @@
     parameters [] (required: 0)
     return-type dart:core::int
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -390,7 +359,6 @@
     parameters [dynamic 'namespace'] (required: 1)
     return-type void
 
-
 Bytecode {
   Entry                2
   CheckStack           0
@@ -419,7 +387,6 @@
     parameters [] (required: 0)
     return-type #lib::_NamespaceImpl
 
-
 Bytecode {
   Entry                2
   CheckStack           0
@@ -456,7 +423,6 @@
     parameters [] (required: 0)
     return-type dart:core::int
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -471,42 +437,14 @@
   [3] = Reserved
 }
 
-}
+Class '_Namespace', script = '#lib'
+    extends dart:core::Object
 
-}
-]  class _NamespaceImpl extends dart.core::Object implements #lib::_Namespace {
-    static field #lib::_NamespaceImpl _cachedNamespace = null;
-    constructor _() → #lib::_NamespaceImpl
-      : super dart.core::Object::•()
-      ;
-    @dart._internal::ExternalName::•("Namespace_Create")
-    external static method _create(#lib::_NamespaceImpl namespace, dynamic n) → #lib::_NamespaceImpl;
-    @dart._internal::ExternalName::•("Namespace_GetPointer")
-    external static method _getPointer(#lib::_NamespaceImpl namespace) → dart.core::int;
-    @dart._internal::ExternalName::•("Namespace_GetDefault")
-    external static method _getDefault() → dart.core::int;
-    static method _setupNamespace(dynamic namespace) → void {
-      #lib::_NamespaceImpl::_cachedNamespace = #lib::_NamespaceImpl::_create(new #lib::_NamespaceImpl::_(), namespace);
-    }
-    static get _namespace() → #lib::_NamespaceImpl {
-      if(#lib::_NamespaceImpl::_cachedNamespace.{dart.core::Object::==}(null)) {
-        #lib::_NamespaceImpl::_cachedNamespace = #lib::_NamespaceImpl::_create(new #lib::_NamespaceImpl::_(), #lib::_NamespaceImpl::_getDefault());
-      }
-      return #lib::_NamespaceImpl::_cachedNamespace;
-    }
-    static get _namespacePointer() → dart.core::int
-      return #lib::_NamespaceImpl::_getPointer(#lib::_NamespaceImpl::_namespace);
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 
 Function '', constructor, reflectable
     parameters [] (required: 0)
     return-type #lib::_Namespace
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -526,7 +464,6 @@
     parameters [dynamic 'namespace'] (required: 1)
     return-type void
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -546,7 +483,6 @@
     parameters [] (required: 0)
     return-type #lib::_Namespace
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -563,7 +499,6 @@
     parameters [] (required: 0)
     return-type dart:core::int
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -575,25 +510,9 @@
   [1] = Reserved
 }
 
-}
+Class 'VMLibraryHooks', script = '#lib'
+    extends dart:core::Object
 
-}
-]  class _Namespace extends dart.core::Object {
-    synthetic constructor •() → #lib::_Namespace
-      : super dart.core::Object::•()
-      ;
-    static method _setupNamespace(dynamic namespace) → void {
-      #lib::_NamespaceImpl::_setupNamespace(namespace);
-    }
-    static get _namespace() → #lib::_Namespace
-      return #lib::_NamespaceImpl::_namespace;
-    static get _namespacePointer() → dart.core::int
-      return #lib::_NamespaceImpl::_namespacePointer;
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 Field 'timerFactory', type = dynamic, reflectable, static
     value = null
 
@@ -631,7 +550,6 @@
     parameters [] (required: 0)
     return-type #lib::VMLibraryHooks
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -651,7 +569,6 @@
     parameters [dynamic 'f'] (required: 1)
     return-type void
 
-
 Bytecode {
   Entry                1
   CheckStack           0
@@ -672,7 +589,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                1
   CheckStack           0
@@ -708,10 +624,111 @@
   [3] = ICData dynamic target-name 'call', arg-desc CP#2
 }
 
+Class 'Stdin', script = '#lib'
+    extends dart:core::Object
+
+
+Function '', constructor, reflectable
+    parameters [] (required: 0)
+    return-type #lib::Stdin
+
+Bytecode {
+  Entry                0
+  CheckStack           0
+  Push                 FP[-5]
+  DirectCall           CP#0, 1
+  Drop1
+  PushNull
+  ReturnTOS
+}
+ConstantPool {
+  [0] = DirectCall 'dart:core::Object:: (constructor)', ArgDesc num-args 1, num-type-args 0, names []
+  [1] = Reserved
 }
 
+Class '_StdIOUtils', script = '#lib'
+    extends dart:core::Object
+
+
+Function '', constructor, reflectable
+    parameters [] (required: 0)
+    return-type #lib::_StdIOUtils
+
+Bytecode {
+  Entry                0
+  CheckStack           0
+  Push                 FP[-5]
+  DirectCall           CP#0, 1
+  Drop1
+  PushNull
+  ReturnTOS
 }
-]  class VMLibraryHooks extends dart.core::Object {
+ConstantPool {
+  [0] = DirectCall 'dart:core::Object:: (constructor)', ArgDesc num-args 1, num-type-args 0, names []
+  [1] = Reserved
+}
+
+
+Function '_getStdioInputStream', static, reflectable, debuggable
+    parameters [dart:core::int 'fd'] (required: 1)
+    return-type #lib::Stdin
+
+Bytecode {
+  Entry                0
+  CheckStack           0
+  PushNull
+  ReturnTOS
+}
+ConstantPool {
+}
+
+
+}
+]library #lib from "#lib" as #lib {
+
+  typedef _ScheduleImmediateClosure = (() → void) → void;
+  class _ScheduleImmediate extends dart.core::Object {
+    static field (() → void) → void _closure = null;
+    synthetic constructor •() → #lib::_ScheduleImmediate
+      : super dart.core::Object::•()
+      ;
+  }
+  class _NamespaceImpl extends dart.core::Object implements #lib::_Namespace {
+    static field #lib::_NamespaceImpl _cachedNamespace = null;
+    constructor _() → #lib::_NamespaceImpl
+      : super dart.core::Object::•()
+      ;
+    @dart._internal::ExternalName::•("Namespace_Create")
+    external static method _create(#lib::_NamespaceImpl namespace, dynamic n) → #lib::_NamespaceImpl;
+    @dart._internal::ExternalName::•("Namespace_GetPointer")
+    external static method _getPointer(#lib::_NamespaceImpl namespace) → dart.core::int;
+    @dart._internal::ExternalName::•("Namespace_GetDefault")
+    external static method _getDefault() → dart.core::int;
+    static method _setupNamespace(dynamic namespace) → void {
+      #lib::_NamespaceImpl::_cachedNamespace = #lib::_NamespaceImpl::_create(new #lib::_NamespaceImpl::_(), namespace);
+    }
+    static get _namespace() → #lib::_NamespaceImpl {
+      if(#lib::_NamespaceImpl::_cachedNamespace.{dart.core::Object::==}(null)) {
+        #lib::_NamespaceImpl::_cachedNamespace = #lib::_NamespaceImpl::_create(new #lib::_NamespaceImpl::_(), #lib::_NamespaceImpl::_getDefault());
+      }
+      return #lib::_NamespaceImpl::_cachedNamespace;
+    }
+    static get _namespacePointer() → dart.core::int
+      return #lib::_NamespaceImpl::_getPointer(#lib::_NamespaceImpl::_namespace);
+  }
+  class _Namespace extends dart.core::Object {
+    synthetic constructor •() → #lib::_Namespace
+      : super dart.core::Object::•()
+      ;
+    static method _setupNamespace(dynamic namespace) → void {
+      #lib::_NamespaceImpl::_setupNamespace(namespace);
+    }
+    static get _namespace() → #lib::_Namespace
+      return #lib::_NamespaceImpl::_namespace;
+    static get _namespacePointer() → dart.core::int
+      return #lib::_NamespaceImpl::_namespacePointer;
+  }
+  class VMLibraryHooks extends dart.core::Object {
     static field dynamic timerFactory = null;
     static field dynamic eventHandlerSendData = null;
     static field dynamic timerMillisecondClock = null;
@@ -737,81 +754,12 @@
       return #lib::VMLibraryHooks::_cachedScript;
     }
   }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
-
-Function '', constructor, reflectable
-    parameters [] (required: 0)
-    return-type #lib::Stdin
-
-
-Bytecode {
-  Entry                0
-  CheckStack           0
-  Push                 FP[-5]
-  DirectCall           CP#0, 1
-  Drop1
-  PushNull
-  ReturnTOS
-}
-ConstantPool {
-  [0] = DirectCall 'dart:core::Object:: (constructor)', ArgDesc num-args 1, num-type-args 0, names []
-  [1] = Reserved
-}
-
-}
-
-}
-]  class Stdin extends dart.core::Object {
+  class Stdin extends dart.core::Object {
     synthetic constructor •() → #lib::Stdin
       : super dart.core::Object::•()
       ;
   }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
-
-Function '', constructor, reflectable
-    parameters [] (required: 0)
-    return-type #lib::_StdIOUtils
-
-
-Bytecode {
-  Entry                0
-  CheckStack           0
-  Push                 FP[-5]
-  DirectCall           CP#0, 1
-  Drop1
-  PushNull
-  ReturnTOS
-}
-ConstantPool {
-  [0] = DirectCall 'dart:core::Object:: (constructor)', ArgDesc num-args 1, num-type-args 0, names []
-  [1] = Reserved
-}
-
-
-Function '_getStdioInputStream', static, reflectable, debuggable
-    parameters [dart:core::int 'fd'] (required: 1)
-    return-type #lib::Stdin
-
-
-Bytecode {
-  Entry                0
-  CheckStack           0
-  PushNull
-  ReturnTOS
-}
-ConstantPool {
-}
-
-}
-
-}
-]  class _StdIOUtils extends dart.core::Object {
+  class _StdIOUtils extends dart.core::Object {
     synthetic constructor •() → #lib::_StdIOUtils
       : super dart.core::Object::•()
       ;
diff --git a/pkg/vm/testcases/bytecode/closures.dart.expect b/pkg/vm/testcases/bytecode/closures.dart.expect
index 465e333..2932382 100644
--- a/pkg/vm/testcases/bytecode/closures.dart.expect
+++ b/pkg/vm/testcases/bytecode/closures.dart.expect
@@ -1,21 +1,19 @@
 main = #lib::main;
  [@vm.bytecode=
-ComponentBytecodeMetadata {
-
+BytecodeMetadata {
 Bytecode (version: stable)
 Main library: #lib
+Library '#lib'
+    name '#lib'
+    script '#lib'
 
-}
-] [@vm.bytecode=
-MembersBytecodeMetadata {
+Class '', script = '#lib'
 
-Members {
 
 Function 'simpleClosure', static, reflectable, debuggable
     parameters [] (required: 0)
     return-type dart:core::int
 
-
 Bytecode {
   Entry                4
   CheckStack           0
@@ -92,7 +90,6 @@
   StoreContextVar      0, 0
   PushNull
   ReturnTOS
-
 }
 
 
@@ -101,7 +98,6 @@
     parameters [] (required: 0)
     return-type void
 
-
 Bytecode {
   Entry                2
   CheckStack           0
@@ -187,7 +183,6 @@
     parameters [] (required: 0)
     return-type void
 
-
 Bytecode {
   Entry                1
   CheckStack           0
@@ -241,7 +236,6 @@
     parameters [] (required: 0)
     return-type FunctionType (dart:core::int) -> dynamic
 
-
 Bytecode {
   Entry                7
   CheckStack           0
@@ -351,7 +345,6 @@
   Drop1
   PushNull
   ReturnTOS
-
 }
 
 
@@ -359,7 +352,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -369,22 +361,14 @@
 ConstantPool {
 }
 
-}
+Class 'C1', script = '#lib'
+    extends dart:core::Object
 
-}
-]library #lib from "#lib" as #lib {
-
-  typedef IntFunc = (dart.core::int) → dynamic;
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 
 Function '', constructor, reflectable
     parameters [] (required: 0)
     return-type #lib::C1
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -399,24 +383,14 @@
   [1] = Reserved
 }
 
-}
+Class 'C2', script = '#lib'
+    extends dart:core::Object
 
-}
-]  class C1 extends dart.core::Object {
-    synthetic constructor •() → #lib::C1
-      : super dart.core::Object::•()
-      ;
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 
 Function '', constructor, reflectable
     parameters [] (required: 0)
     return-type #lib::C2
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -431,24 +405,14 @@
   [1] = Reserved
 }
 
-}
+Class 'C3', script = '#lib'
+    extends dart:core::Object
 
-}
-]  class C2 extends dart.core::Object {
-    synthetic constructor •() → #lib::C2
-      : super dart.core::Object::•()
-      ;
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 
 Function '', constructor, reflectable
     parameters [] (required: 0)
     return-type #lib::C3
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -463,24 +427,14 @@
   [1] = Reserved
 }
 
-}
+Class 'C4', script = '#lib'
+    extends dart:core::Object
 
-}
-]  class C3 extends dart.core::Object {
-    synthetic constructor •() → #lib::C3
-      : super dart.core::Object::•()
-      ;
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 
 Function '', constructor, reflectable
     parameters [] (required: 0)
     return-type #lib::C4
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -495,24 +449,14 @@
   [1] = Reserved
 }
 
-}
+Class 'C5', script = '#lib'
+    extends dart:core::Object
 
-}
-]  class C4 extends dart.core::Object {
-    synthetic constructor •() → #lib::C4
-      : super dart.core::Object::•()
-      ;
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 
 Function '', constructor, reflectable
     parameters [] (required: 0)
     return-type #lib::C5
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -527,24 +471,14 @@
   [1] = Reserved
 }
 
-}
+Class 'C6', script = '#lib'
+    extends dart:core::Object
 
-}
-]  class C5 extends dart.core::Object {
-    synthetic constructor •() → #lib::C5
-      : super dart.core::Object::•()
-      ;
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 
 Function '', constructor, reflectable
     parameters [] (required: 0)
     return-type #lib::C6
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -559,24 +493,14 @@
   [1] = Reserved
 }
 
-}
+Class 'C7', script = '#lib'
+    extends dart:core::Object
 
-}
-]  class C6 extends dart.core::Object {
-    synthetic constructor •() → #lib::C6
-      : super dart.core::Object::•()
-      ;
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 
 Function '', constructor, reflectable
     parameters [] (required: 0)
     return-type #lib::C7
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -591,24 +515,14 @@
   [1] = Reserved
 }
 
-}
+Class 'C8', script = '#lib'
+    extends dart:core::Object
 
-}
-]  class C7 extends dart.core::Object {
-    synthetic constructor •() → #lib::C7
-      : super dart.core::Object::•()
-      ;
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 
 Function '', constructor, reflectable
     parameters [] (required: 0)
     return-type #lib::C8
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -623,24 +537,15 @@
   [1] = Reserved
 }
 
-}
+Class 'A', script = '#lib'
+    type-params <dart:core::Object T1, dart:core::Object T2> (args: 2)
+    extends dart:core::Object
 
-}
-]  class C8 extends dart.core::Object {
-    synthetic constructor •() → #lib::C8
-      : super dart.core::Object::•()
-      ;
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 
 Function '', constructor, reflectable
     parameters [] (required: 0)
     return-type #lib::A < #lib::A::TypeParam/0, #lib::A::TypeParam/1 >
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -661,7 +566,6 @@
     parameters [] (required: 0)
     return-type void
 
-
 Bytecode {
   Entry                5
   CheckStack           0
@@ -804,7 +708,6 @@
   Drop1
   PushNull
   ReturnTOS
-
 }
 
 Closure #lib::A::foo::Closure/0::'nested2' <dart:core::Object T7, dart:core::Object T8> () -> void
@@ -856,7 +759,6 @@
   Drop1
   PushNull
   ReturnTOS
-
 }
 
 Closure #lib::A::foo::Closure/1::'<anonymous closure>' () -> dart:core::Null
@@ -939,36 +841,11 @@
   Drop1
   PushNull
   ReturnTOS
-
 }
 
-}
+Class 'B', script = '#lib'
+    extends dart:core::Object
 
-}
-]  class A<T1 extends dart.core::Object = dynamic, T2 extends dart.core::Object = dynamic> extends dart.core::Object {
-    synthetic constructor •() → #lib::A<#lib::A::T1, #lib::A::T2>
-      : super dart.core::Object::•()
-      ;
-    method foo<T3 extends dart.core::Object = dynamic, T4 extends dart.core::Object = dynamic>() → void {
-      function nested1<T5 extends dart.core::Object = dynamic, T6 extends dart.core::Object = dynamic>() → void {
-        function nested2<T7 extends dart.core::Object = dynamic, T8 extends dart.core::Object = dynamic>() → void {
-          () → dart.core::Null nested3 = () → dart.core::Null {
-            dart.core::print(<dart.core::Type>[#lib::A::T1, #lib::A::T2, #lib::A::foo::T3, #lib::A::foo::T4, T5, T6, T7, T8]);
-            #lib::callWithArgs<#lib::A::T1, #lib::A::T2, #lib::A::foo::T3, #lib::A::foo::T4, T5, T6, T7, T8>();
-          };
-          [@vm.call-site-attributes.metadata=receiverType:() → dart.core::Null] nested3.call();
-        }
-        [@vm.call-site-attributes.metadata=receiverType:<T7 extends dart.core::Object = dynamic, T8 extends dart.core::Object = dynamic>() → void] nested2.call<#lib::C7, #lib::C8>();
-        [@vm.call-site-attributes.metadata=receiverType:<T7 extends dart.core::Object = dynamic, T8 extends dart.core::Object = dynamic>() → void] nested2.call<dart.core::List<#lib::C7>, dart.core::List<#lib::C8>>();
-      }
-      [@vm.call-site-attributes.metadata=receiverType:<T5 extends dart.core::Object = dynamic, T6 extends dart.core::Object = dynamic>() → void] nested1.call<#lib::C5, #lib::C6>();
-      [@vm.call-site-attributes.metadata=receiverType:<T5 extends dart.core::Object = dynamic, T6 extends dart.core::Object = dynamic>() → void] nested1.call<dart.core::List<#lib::C5>, dart.core::List<#lib::C6>>();
-    }
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 Field 'foo', type = dart:core::int, getter = 'get:foo', setter = 'set:foo', reflectable
     value = null
 
@@ -976,7 +853,6 @@
     parameters [] (required: 0)
     return-type #lib::B
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -997,7 +873,6 @@
     parameters [] (required: 0)
     return-type void
 
-
 Bytecode {
   Entry                5
   CheckStack           0
@@ -1179,7 +1054,6 @@
 L1:
   PushNull
   ReturnTOS
-
 }
 
 Closure #lib::B::topLevel::Closure/0::'closure2' () -> void
@@ -1208,7 +1082,6 @@
   StoreContextVar      1, 1
   PushNull
   ReturnTOS
-
 }
 
 Closure #lib::B::topLevel::'<anonymous closure>' () -> dart:core::Null
@@ -1226,61 +1099,16 @@
   Drop1
   PushNull
   ReturnTOS
-
 }
 
-}
+Class 'C', script = '#lib'
+    extends dart:core::Object
 
-}
-]  class B extends dart.core::Object {
-    field dart.core::int foo = null;
-    synthetic constructor •() → #lib::B
-      : super dart.core::Object::•()
-      ;
-    method topLevel() → void {
-      {
-        dart.core::int x = 1;
-        {
-          dart.core::int y = 2;
-          dart.core::int z = 3;
-          (dart.core::int) → dart.core::Null closure1 = (dart.core::int y) → dart.core::Null {
-            x = y.{dart.core::num::+}(1);
-            if(x.{dart.core::num::>}(5)) {
-              dart.core::int w = 4;
-              function closure2() → void {
-                z = x.{dart.core::num::+}(2);
-                w = this.{#lib::B::foo}.{dart.core::num::+}(y);
-              }
-              [@vm.call-site-attributes.metadata=receiverType:() → void] closure2.call();
-              dart.core::print(w);
-            }
-          };
-          [@vm.call-site-attributes.metadata=receiverType:(dart.core::int) → dart.core::Null] closure1.call(10);
-          [@vm.call-site-attributes.metadata=receiverType:(dart.core::int) → dart.core::Null] closure1.call(11);
-          dart.core::print(y);
-          dart.core::print(z);
-        }
-        dart.core::print(x);
-      }
-      {
-        dart.core::int x = 42;
-        () → dart.core::Null closure3 = () → dart.core::Null {
-          this.{#lib::B::foo} = x;
-        };
-        [@vm.call-site-attributes.metadata=receiverType:() → dart.core::Null] closure3.call();
-      }
-    }
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 
 Function '', constructor, reflectable
     parameters [] (required: 0)
     return-type #lib::C
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -1300,7 +1128,6 @@
     parameters [] (required: 0)
     return-type void
 
-
 Bytecode {
   Entry                5
   CheckStack           0
@@ -1436,7 +1263,6 @@
   LoadContextVar       0, 0
   AddInt
   ReturnTOS
-
 }
 
 Closure #lib::C::testForLoop::'<anonymous closure>' (dart:core::int ii) -> dart:core::Null
@@ -1462,7 +1288,6 @@
   StoreContextVar      1, 0
   PushNull
   ReturnTOS
-
 }
 
 
@@ -1470,7 +1295,6 @@
     parameters [dart:core::List < dart:core::int > 'list'] (required: 1)
     return-type void
 
-
 Bytecode {
   Entry                5
   CheckStack           0
@@ -1561,47 +1385,17 @@
   StoreContextVar      0, 0
   PushNull
   ReturnTOS
-
 }
 
-}
+Class 'D', script = '#lib'
+    type-params <dart:core::Object T> (args: 1)
+    extends dart:core::Object
 
-}
-]  class C extends dart.core::Object {
-    synthetic constructor •() → #lib::C
-      : super dart.core::Object::•()
-      ;
-    method testForLoop() → void {
-      dart.core::int delta = 0;
-      dart.core::List<dart.core::Function> getI = <dart.core::Function>[];
-      dart.core::List<dart.core::Function> setI = <dart.core::Function>[];
-      for (dart.core::int i = 0; i.{dart.core::num::<}(10); i = i.{dart.core::num::+}(1)) {
-        [@vm.call-site-attributes.metadata=receiverType:dart.core::List<dart.core::Function>] getI.{dart.core::List::add}(() → dart.core::int => i.{dart.core::num::+}(delta));
-        [@vm.call-site-attributes.metadata=receiverType:dart.core::List<dart.core::Function>] setI.{dart.core::List::add}((dart.core::int ii) → dart.core::Null {
-          i = ii.{dart.core::num::+}(delta);
-        });
-      }
-    }
-    method testForInLoop(dart.core::List<dart.core::int> list) → void {
-      for (dart.core::int i in list) {
-        () → dart.core::Null inc = () → dart.core::Null {
-          i = i.{dart.core::num::+}(1);
-        };
-        [@vm.call-site-attributes.metadata=receiverType:() → dart.core::Null] inc.call();
-        dart.core::print(i);
-      }
-    }
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 
 Function '', constructor, reflectable
     parameters [] (required: 0)
     return-type #lib::D < #lib::D::TypeParam/0 >
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -1621,7 +1415,6 @@
     parameters [#lib::D::TypeParam/0 't'] (required: 1)
     return-type dynamic
 
-
 Bytecode {
   Entry                3
   CheckStack           0
@@ -1688,7 +1481,6 @@
   Push                 r0
   LoadContextVar       0, 0
   ReturnTOS
-
 }
 
 
@@ -1696,7 +1488,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                3
   CheckStack           0
@@ -1777,7 +1568,6 @@
   Drop1
   PushNull
   ReturnTOS
-
 }
 
 Closure #lib::D::bar::Closure/0::'inner' () -> dart:core::Null
@@ -1789,13 +1579,138 @@
   PopLocal             r0
   PushNull
   ReturnTOS
-
 }
 
-}
 
 }
-]  class D<T extends dart.core::Object = dynamic> extends dart.core::Object {
+]library #lib from "#lib" as #lib {
+
+  typedef IntFunc = (dart.core::int) → dynamic;
+  class C1 extends dart.core::Object {
+    synthetic constructor •() → #lib::C1
+      : super dart.core::Object::•()
+      ;
+  }
+  class C2 extends dart.core::Object {
+    synthetic constructor •() → #lib::C2
+      : super dart.core::Object::•()
+      ;
+  }
+  class C3 extends dart.core::Object {
+    synthetic constructor •() → #lib::C3
+      : super dart.core::Object::•()
+      ;
+  }
+  class C4 extends dart.core::Object {
+    synthetic constructor •() → #lib::C4
+      : super dart.core::Object::•()
+      ;
+  }
+  class C5 extends dart.core::Object {
+    synthetic constructor •() → #lib::C5
+      : super dart.core::Object::•()
+      ;
+  }
+  class C6 extends dart.core::Object {
+    synthetic constructor •() → #lib::C6
+      : super dart.core::Object::•()
+      ;
+  }
+  class C7 extends dart.core::Object {
+    synthetic constructor •() → #lib::C7
+      : super dart.core::Object::•()
+      ;
+  }
+  class C8 extends dart.core::Object {
+    synthetic constructor •() → #lib::C8
+      : super dart.core::Object::•()
+      ;
+  }
+  class A<T1 extends dart.core::Object = dynamic, T2 extends dart.core::Object = dynamic> extends dart.core::Object {
+    synthetic constructor •() → #lib::A<#lib::A::T1, #lib::A::T2>
+      : super dart.core::Object::•()
+      ;
+    method foo<T3 extends dart.core::Object = dynamic, T4 extends dart.core::Object = dynamic>() → void {
+      function nested1<T5 extends dart.core::Object = dynamic, T6 extends dart.core::Object = dynamic>() → void {
+        function nested2<T7 extends dart.core::Object = dynamic, T8 extends dart.core::Object = dynamic>() → void {
+          () → dart.core::Null nested3 = () → dart.core::Null {
+            dart.core::print(<dart.core::Type>[#lib::A::T1, #lib::A::T2, #lib::A::foo::T3, #lib::A::foo::T4, T5, T6, T7, T8]);
+            #lib::callWithArgs<#lib::A::T1, #lib::A::T2, #lib::A::foo::T3, #lib::A::foo::T4, T5, T6, T7, T8>();
+          };
+          [@vm.call-site-attributes.metadata=receiverType:() → dart.core::Null] nested3.call();
+        }
+        [@vm.call-site-attributes.metadata=receiverType:<T7 extends dart.core::Object = dynamic, T8 extends dart.core::Object = dynamic>() → void] nested2.call<#lib::C7, #lib::C8>();
+        [@vm.call-site-attributes.metadata=receiverType:<T7 extends dart.core::Object = dynamic, T8 extends dart.core::Object = dynamic>() → void] nested2.call<dart.core::List<#lib::C7>, dart.core::List<#lib::C8>>();
+      }
+      [@vm.call-site-attributes.metadata=receiverType:<T5 extends dart.core::Object = dynamic, T6 extends dart.core::Object = dynamic>() → void] nested1.call<#lib::C5, #lib::C6>();
+      [@vm.call-site-attributes.metadata=receiverType:<T5 extends dart.core::Object = dynamic, T6 extends dart.core::Object = dynamic>() → void] nested1.call<dart.core::List<#lib::C5>, dart.core::List<#lib::C6>>();
+    }
+  }
+  class B extends dart.core::Object {
+    field dart.core::int foo = null;
+    synthetic constructor •() → #lib::B
+      : super dart.core::Object::•()
+      ;
+    method topLevel() → void {
+      {
+        dart.core::int x = 1;
+        {
+          dart.core::int y = 2;
+          dart.core::int z = 3;
+          (dart.core::int) → dart.core::Null closure1 = (dart.core::int y) → dart.core::Null {
+            x = y.{dart.core::num::+}(1);
+            if(x.{dart.core::num::>}(5)) {
+              dart.core::int w = 4;
+              function closure2() → void {
+                z = x.{dart.core::num::+}(2);
+                w = this.{#lib::B::foo}.{dart.core::num::+}(y);
+              }
+              [@vm.call-site-attributes.metadata=receiverType:() → void] closure2.call();
+              dart.core::print(w);
+            }
+          };
+          [@vm.call-site-attributes.metadata=receiverType:(dart.core::int) → dart.core::Null] closure1.call(10);
+          [@vm.call-site-attributes.metadata=receiverType:(dart.core::int) → dart.core::Null] closure1.call(11);
+          dart.core::print(y);
+          dart.core::print(z);
+        }
+        dart.core::print(x);
+      }
+      {
+        dart.core::int x = 42;
+        () → dart.core::Null closure3 = () → dart.core::Null {
+          this.{#lib::B::foo} = x;
+        };
+        [@vm.call-site-attributes.metadata=receiverType:() → dart.core::Null] closure3.call();
+      }
+    }
+  }
+  class C extends dart.core::Object {
+    synthetic constructor •() → #lib::C
+      : super dart.core::Object::•()
+      ;
+    method testForLoop() → void {
+      dart.core::int delta = 0;
+      dart.core::List<dart.core::Function> getI = <dart.core::Function>[];
+      dart.core::List<dart.core::Function> setI = <dart.core::Function>[];
+      for (dart.core::int i = 0; i.{dart.core::num::<}(10); i = i.{dart.core::num::+}(1)) {
+        [@vm.call-site-attributes.metadata=receiverType:dart.core::List<dart.core::Function>] getI.{dart.core::List::add}(() → dart.core::int => i.{dart.core::num::+}(delta));
+        [@vm.call-site-attributes.metadata=receiverType:dart.core::List<dart.core::Function>] setI.{dart.core::List::add}((dart.core::int ii) → dart.core::Null {
+          i = ii.{dart.core::num::+}(delta);
+        });
+      }
+    }
+    method testForInLoop(dart.core::List<dart.core::int> list) → void {
+      for (dart.core::int i in list) {
+        () → dart.core::Null inc = () → dart.core::Null {
+          i = i.{dart.core::num::+}(1);
+        };
+        [@vm.call-site-attributes.metadata=receiverType:() → dart.core::Null] inc.call();
+        dart.core::print(i);
+      }
+    }
+  }
+  class D<T extends dart.core::Object = dynamic> extends dart.core::Object {
     synthetic constructor •() → #lib::D<#lib::D::T>
       : super dart.core::Object::•()
       ;
diff --git a/pkg/vm/testcases/bytecode/deferred_lib.dart.expect b/pkg/vm/testcases/bytecode/deferred_lib.dart.expect
index f4ef7c3..a3a1685 100644
--- a/pkg/vm/testcases/bytecode/deferred_lib.dart.expect
+++ b/pkg/vm/testcases/bytecode/deferred_lib.dart.expect
@@ -1,21 +1,19 @@
 main = #lib::main;
  [@vm.bytecode=
-ComponentBytecodeMetadata {
-
+BytecodeMetadata {
 Bytecode (version: stable)
 Main library: #lib
+Library '#lib'
+    name '#lib'
+    script '#lib'
 
-}
-] [@vm.bytecode=
-MembersBytecodeMetadata {
+Class '', script = '#lib'
 
-Members {
 
 Function 'callDeferred', static, reflectable, debuggable
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                1
   CheckStack           0
@@ -37,7 +35,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -55,7 +52,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -65,7 +61,6 @@
 ConstantPool {
 }
 
-}
 
 }
 ]library #lib from "#lib" as #lib {
diff --git a/pkg/vm/testcases/bytecode/field_initializers.dart.expect b/pkg/vm/testcases/bytecode/field_initializers.dart.expect
index ee724e8..a2d1442 100644
--- a/pkg/vm/testcases/bytecode/field_initializers.dart.expect
+++ b/pkg/vm/testcases/bytecode/field_initializers.dart.expect
@@ -1,21 +1,19 @@
 main = #lib::main;
  [@vm.bytecode=
-ComponentBytecodeMetadata {
-
+BytecodeMetadata {
 Bytecode (version: stable)
 Main library: #lib
+Library '#lib'
+    name '#lib'
+    script '#lib'
 
-}
-] [@vm.bytecode=
-MembersBytecodeMetadata {
+Class '', script = '#lib'
 
-Members {
 
 Function 'main', static, reflectable, debuggable
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -25,15 +23,9 @@
 ConstantPool {
 }
 
-}
+Class 'A', script = '#lib'
+    extends dart:core::Object
 
-}
-]library #lib from "#lib" as #lib {
-
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 Field 'foo1', type = dart:core::int, getter = 'get:foo1', setter = 'set:foo1', reflectable
     value = null
 
@@ -53,7 +45,6 @@
     parameters [dart:core::int 'foo4'] (required: 1)
     return-type #lib::A
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -92,7 +83,6 @@
     parameters [dart:core::int 'x', dart:core::int 'y'] (required: 2)
     return-type #lib::A
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -133,7 +123,6 @@
     parameters [] (required: 0)
     return-type #lib::A
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -154,7 +143,6 @@
     parameters [dart:core::int 'a', dart:core::int 'b', dart:core::int 'c'] (required: 3)
     return-type #lib::A
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -173,32 +161,9 @@
   [1] = Reserved
 }
 
-}
+Class 'B', script = '#lib'
+    extends #lib::A
 
-}
-]  class A extends dart.core::Object {
-    field dart.core::int foo1;
-    field dart.core::int foo2 = null;
-    field dart.core::int foo3 = 42;
-    field dart.core::int foo4;
-    field dart.core::int foo5 = 43;
-    constructor •(dart.core::int foo4) → #lib::A
-      : #lib::A::foo1 = null, #lib::A::foo4 = foo4, #lib::A::foo5 = 44, super dart.core::Object::•()
-      ;
-    constructor constr2(dart.core::int x, dart.core::int y) → #lib::A
-      : #lib::A::foo4 = null, #lib::A::foo1 = x, #lib::A::foo5 = y.{dart.core::num::+}(1), super dart.core::Object::•()
-      ;
-    constructor redirecting1() → #lib::A
-      : this #lib::A::•(45)
-      ;
-    constructor redirecting2(dart.core::int a, dart.core::int b, dart.core::int c) → #lib::A
-      : this #lib::A::constr2(a, b.{dart.core::num::*}(c))
-      ;
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 Field 'foo6', type = dart:core::int, getter = 'get:foo6', setter = 'set:foo6', reflectable
     value = const 46
 
@@ -212,7 +177,6 @@
     parameters [] (required: 0)
     return-type #lib::B
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -238,7 +202,6 @@
     parameters [dart:core::int 'i', dart:core::int 'j'] (required: 2)
     return-type #lib::B
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -264,10 +227,30 @@
   [3] = Reserved
 }
 
-}
 
 }
-]  class B extends #lib::A {
+]library #lib from "#lib" as #lib {
+
+  class A extends dart.core::Object {
+    field dart.core::int foo1;
+    field dart.core::int foo2 = null;
+    field dart.core::int foo3 = 42;
+    field dart.core::int foo4;
+    field dart.core::int foo5 = 43;
+    constructor •(dart.core::int foo4) → #lib::A
+      : #lib::A::foo1 = null, #lib::A::foo4 = foo4, #lib::A::foo5 = 44, super dart.core::Object::•()
+      ;
+    constructor constr2(dart.core::int x, dart.core::int y) → #lib::A
+      : #lib::A::foo4 = null, #lib::A::foo1 = x, #lib::A::foo5 = y.{dart.core::num::+}(1), super dart.core::Object::•()
+      ;
+    constructor redirecting1() → #lib::A
+      : this #lib::A::•(45)
+      ;
+    constructor redirecting2(dart.core::int a, dart.core::int b, dart.core::int c) → #lib::A
+      : this #lib::A::constr2(a, b.{dart.core::num::*}(c))
+      ;
+  }
+  class B extends #lib::A {
     field dart.core::int foo6 = 46;
     static field dart.core::int foo7 = 47;
     static const field dart.core::int foo8 = 48;
diff --git a/pkg/vm/testcases/bytecode/hello.dart.expect b/pkg/vm/testcases/bytecode/hello.dart.expect
index aa15b42..12f2f55 100644
--- a/pkg/vm/testcases/bytecode/hello.dart.expect
+++ b/pkg/vm/testcases/bytecode/hello.dart.expect
@@ -1,21 +1,19 @@
 main = #lib::main;
  [@vm.bytecode=
-ComponentBytecodeMetadata {
-
+BytecodeMetadata {
 Bytecode (version: stable)
 Main library: #lib
+Library '#lib'
+    name '#lib'
+    script '#lib'
 
-}
-] [@vm.bytecode=
-MembersBytecodeMetadata {
+Class '', script = '#lib'
 
-Members {
 
 Function 'main', static, reflectable, debuggable
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -31,7 +29,6 @@
   [2] = Reserved
 }
 
-}
 
 }
 ]library #lib from "#lib" as #lib {
diff --git a/pkg/vm/testcases/bytecode/instance_creation.dart.expect b/pkg/vm/testcases/bytecode/instance_creation.dart.expect
index 7243ab2..eadce66 100644
--- a/pkg/vm/testcases/bytecode/instance_creation.dart.expect
+++ b/pkg/vm/testcases/bytecode/instance_creation.dart.expect
@@ -1,21 +1,19 @@
 main = #lib::main;
  [@vm.bytecode=
-ComponentBytecodeMetadata {
-
+BytecodeMetadata {
 Bytecode (version: stable)
 Main library: #lib
+Library '#lib'
+    name '#lib'
+    script '#lib'
 
-}
-] [@vm.bytecode=
-MembersBytecodeMetadata {
+Class '', script = '#lib'
 
-Members {
 
 Function 'foo1', static, reflectable, debuggable
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                1
   CheckStack           0
@@ -39,7 +37,6 @@
     parameters [] (required: 0)
     return-type void
 
-
 Bytecode {
   Entry                1
   CheckStack           0
@@ -81,7 +78,6 @@
     parameters [] (required: 0)
     return-type void
 
-
 Bytecode {
   Entry                2
   CheckStack           0
@@ -111,7 +107,6 @@
     parameters [] (required: 0)
     return-type void
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -132,7 +127,6 @@
     parameters [] (required: 0)
     return-type void
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -158,7 +152,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -178,7 +171,6 @@
     parameters [dart:core::int 'n'] (required: 1)
     return-type dynamic
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -198,7 +190,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -222,15 +213,10 @@
   [6] = Reserved
 }
 
-}
+Class 'Base', script = '#lib'
+    type-params <dart:core::Object T1, dart:core::Object T2> (args: 2)
+    extends dart:core::Object
 
-}
-]library #lib from "#lib" as #lib {
-
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 Field 't1', type = #lib::Base::TypeParam/0, getter = 'get:t1', setter = 'set:t1', reflectable
     value = null
 
@@ -241,7 +227,6 @@
     parameters [] (required: 0)
     return-type #lib::Base < #lib::Base::TypeParam/0, #lib::Base::TypeParam/1 >
 
-
 Bytecode {
   Entry                1
   CheckStack           0
@@ -295,26 +280,13 @@
   [10] = Reserved
 }
 
-}
+Class 'A', script = '#lib'
+    extends #lib::Base < dart:core::int, dart:core::String >
 
-}
-]  class Base<T1 extends dart.core::Object = dynamic, T2 extends dart.core::Object = dynamic> extends dart.core::Object {
-    generic-covariant-impl field #lib::Base::T1 t1 = null;
-    generic-covariant-impl field #lib::Base::T2 t2 = null;
-    constructor •() → #lib::Base<#lib::Base::T1, #lib::Base::T2>
-      : super dart.core::Object::•() {
-      dart.core::print("Base: ${#lib::Base::T1}, ${#lib::Base::T2}");
-    }
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 
 Function '', constructor, reflectable, debuggable
     parameters [dart:core::String 's'] (required: 1)
-    return-type #lib::A
-
+    return-type #lib::A < dart:core::int, dart:core::String >
 
 Bytecode {
   Entry                0
@@ -330,24 +302,15 @@
   [1] = Reserved
 }
 
-}
+Class 'B', script = '#lib'
+    type-params <dart:core::Object T> (args: 3)
+    extends #lib::Base < dart:core::List < #lib::B::TypeParam/0 >, dart:core::String >
 
-}
-]  class A extends #lib::Base<dart.core::int, dart.core::String> {
-    constructor •(dart.core::String s) → #lib::A
-      : super #lib::Base::•()
-      ;
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 
 Function '', constructor, reflectable, debuggable
     parameters [] (required: 0)
     return-type #lib::B < dart:core::List < #lib::B::TypeParam/0 >, dart:core::String, #lib::B::TypeParam/0 >
 
-
 Bytecode {
   Entry                1
   CheckStack           0
@@ -387,25 +350,14 @@
   [8] = Reserved
 }
 
-}
+Class 'C', script = '#lib'
+    extends dart:core::Object
 
-}
-]  class B<T extends dart.core::Object = dynamic> extends #lib::Base<dart.core::List<#lib::B::T>, dart.core::String> {
-    constructor •() → #lib::B<#lib::B::T>
-      : super #lib::Base::•() {
-      dart.core::print("B: ${#lib::B::T}");
-    }
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 
 Function '', constructor, reflectable, debuggable
     parameters [dart:core::String 's'] (required: 1)
     return-type #lib::C
 
-
 Bytecode {
   Entry                1
   CheckStack           0
@@ -440,25 +392,15 @@
   [6] = Reserved
 }
 
-}
+Class 'E', script = '#lib'
+    type-params <dart:core::Object K, dart:core::Object V> (args: 2)
+    extends dart:core::Object
 
-}
-]  class C extends dart.core::Object {
-    constructor •(dart.core::String s) → #lib::C
-      : super dart.core::Object::•() {
-      dart.core::print("C: ${s}");
-    }
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 
 Function '', constructor, reflectable
     parameters [] (required: 0)
     return-type #lib::E < #lib::E::TypeParam/0, #lib::E::TypeParam/1 >
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -478,7 +420,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -493,26 +434,15 @@
   [2] = Reserved
 }
 
-}
+Class 'F', script = '#lib'
+    type-params <dart:core::Object K, dart:core::Object V> (args: 4)
+    extends #lib::E < dart:core::String, dart:core::List < #lib::F::TypeParam/1 > >
 
-}
-]  class E<K extends dart.core::Object = dynamic, V extends dart.core::Object = dynamic> extends dart.core::Object {
-    synthetic constructor •() → #lib::E<#lib::E::K, #lib::E::V>
-      : super dart.core::Object::•()
-      ;
-    method test_reuse1() → dynamic
-      return dart.core::Map::•<#lib::E::K, #lib::E::V>();
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 
 Function '', constructor, reflectable
     parameters [] (required: 0)
     return-type #lib::F < dart:core::String, dart:core::List < #lib::F::TypeParam/1 >, #lib::F::TypeParam/0, #lib::F::TypeParam/1 >
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -532,7 +462,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -547,26 +476,15 @@
   [2] = Reserved
 }
 
-}
+Class 'G', script = '#lib'
+    type-params <dart:core::Object K, dart:core::Object V> (args: 2)
+    extends dart:core::Object
 
-}
-]  class F<K extends dart.core::Object = dynamic, V extends dart.core::Object = dynamic> extends #lib::E<dart.core::String, dart.core::List<#lib::F::V>> {
-    synthetic constructor •() → #lib::F<#lib::F::K, #lib::F::V>
-      : super #lib::E::•()
-      ;
-    method test_reuse2() → dynamic
-      return dart.core::Map::•<dart.core::String, dart.core::List<#lib::F::V>>();
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 
 Function '', constructor, reflectable, debuggable
     parameters [] (required: 0)
     return-type #lib::G < #lib::G::TypeParam/0, #lib::G::TypeParam/1 >
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -587,7 +505,6 @@
     parameters [] (required: 0)
     return-type #lib::G < #lib::G::test_factory (constructor)::TypeParam/0, #lib::G::test_factory (constructor)::TypeParam/1 >
 
-
 Bytecode {
   Entry                1
   CheckStack           0
@@ -609,26 +526,15 @@
   [3] = Reserved
 }
 
-}
+Class 'H', script = '#lib'
+    type-params <dart:core::Object P1, dart:core::Object P2, dart:core::Object P3> (args: 5)
+    extends #lib::G < #lib::H::TypeParam/1, #lib::H::TypeParam/2 >
 
-}
-]  class G<K extends dart.core::Object = dynamic, V extends dart.core::Object = dynamic> extends dart.core::Object {
-    constructor •() → #lib::G<#lib::G::K, #lib::G::V>
-      : super dart.core::Object::•()
-      ;
-    static factory test_factory<K extends dart.core::Object = dynamic, V extends dart.core::Object = dynamic>() → #lib::G<#lib::G::test_factory::K, #lib::G::test_factory::V>
-      return new #lib::H::•<dart.core::String, #lib::G::test_factory::K, #lib::G::test_factory::V>();
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 
 Function '', constructor, reflectable
     parameters [] (required: 0)
     return-type #lib::H < #lib::H::TypeParam/1, #lib::H::TypeParam/2, #lib::H::TypeParam/0, #lib::H::TypeParam/1, #lib::H::TypeParam/2 >
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -643,24 +549,14 @@
   [1] = Reserved
 }
 
-}
+Class 'I', script = '#lib'
+    extends dart:core::Object
 
-}
-]  class H<P1 extends dart.core::Object = dynamic, P2 extends dart.core::Object = dynamic, P3 extends dart.core::Object = dynamic> extends #lib::G<#lib::H::P2, #lib::H::P3> {
-    synthetic constructor •() → #lib::H<#lib::H::P1, #lib::H::P2, #lib::H::P3>
-      : super #lib::G::•()
-      ;
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 
 Function '', constructor, reflectable, debuggable
     parameters [dynamic 'param'] (required: 1)
     return-type #lib::I
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -680,7 +576,6 @@
     parameters [dynamic 'param'] (required: 0)
     return-type #lib::I
 
-
 Bytecode {
   EntryOptional        1, 0, 1
   LoadConstant         r1, CP#0
@@ -703,26 +598,14 @@
   [4] = Reserved
 }
 
-}
+Class 'J', script = '#lib'
+    extends dart:core::Object
 
-}
-]  class I extends dart.core::Object {
-    constructor •(dynamic param) → #lib::I
-      : super dart.core::Object::•()
-      ;
-    static factory test_factory2({dynamic param = null}) → #lib::I
-      return new #lib::I::•(param);
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 
 Function '', factory, static, reflectable, debuggable, native 'agent_J'
     parameters [] (required: 0)
     return-type #lib::J
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -734,24 +617,16 @@
   [0] = NativeEntry agent_J
 }
 
-}
+Class 'K', script = '#lib', abstract
+    type-params <dart:core::Object A, dart:core::Object B> (args: 2)
+    extends dart:core::Object
 
-}
-]  class J extends dart.core::Object {
-    @dart._internal::ExternalName::•("agent_J")
-    external static factory •() → #lib::J;
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 
 Function '', factory, static, reflectable, debuggable
     type-params <dart:core::Object A, dart:core::Object B>
     parameters [] (required: 0)
     return-type #lib::K < #lib::K:: (constructor)::TypeParam/0, #lib::K:: (constructor)::TypeParam/1 >
 
-
 Bytecode {
   Entry                1
   CheckStack           0
@@ -770,23 +645,16 @@
   [2] = Reserved
 }
 
-}
+Class 'TestTypeArgReuse', script = '#lib'
+    type-params <dart:core::Object P, dart:core::Object Q> (args: 2)
+    extends #lib::Base < #lib::TestTypeArgReuse::TypeParam/0, #lib::TestTypeArgReuse::TypeParam/1 >
+    implements [#lib::K < #lib::TestTypeArgReuse::TypeParam/0, #lib::TestTypeArgReuse::TypeParam/1 >]
 
-}
-]  abstract class K<A extends dart.core::Object = dynamic, B extends dart.core::Object = dynamic> extends dart.core::Object {
-    static factory •<A extends dart.core::Object = dynamic, B extends dart.core::Object = dynamic>() → #lib::K<#lib::K::•::A, #lib::K::•::B>
-      return new #lib::TestTypeArgReuse::•<#lib::K::•::A, #lib::K::•::B>();
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 
 Function '', constructor, reflectable
     parameters [] (required: 0)
     return-type #lib::TestTypeArgReuse < #lib::TestTypeArgReuse::TypeParam/0, #lib::TestTypeArgReuse::TypeParam/1 >
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -801,10 +669,77 @@
   [1] = Reserved
 }
 
-}
 
 }
-]  class TestTypeArgReuse<P extends dart.core::Object = dynamic, Q extends dart.core::Object = dynamic> extends #lib::Base<#lib::TestTypeArgReuse::P, #lib::TestTypeArgReuse::Q> implements #lib::K<#lib::TestTypeArgReuse::P, #lib::TestTypeArgReuse::Q> {
+]library #lib from "#lib" as #lib {
+
+  class Base<T1 extends dart.core::Object = dynamic, T2 extends dart.core::Object = dynamic> extends dart.core::Object {
+    generic-covariant-impl field #lib::Base::T1 t1 = null;
+    generic-covariant-impl field #lib::Base::T2 t2 = null;
+    constructor •() → #lib::Base<#lib::Base::T1, #lib::Base::T2>
+      : super dart.core::Object::•() {
+      dart.core::print("Base: ${#lib::Base::T1}, ${#lib::Base::T2}");
+    }
+  }
+  class A extends #lib::Base<dart.core::int, dart.core::String> {
+    constructor •(dart.core::String s) → #lib::A
+      : super #lib::Base::•()
+      ;
+  }
+  class B<T extends dart.core::Object = dynamic> extends #lib::Base<dart.core::List<#lib::B::T>, dart.core::String> {
+    constructor •() → #lib::B<#lib::B::T>
+      : super #lib::Base::•() {
+      dart.core::print("B: ${#lib::B::T}");
+    }
+  }
+  class C extends dart.core::Object {
+    constructor •(dart.core::String s) → #lib::C
+      : super dart.core::Object::•() {
+      dart.core::print("C: ${s}");
+    }
+  }
+  class E<K extends dart.core::Object = dynamic, V extends dart.core::Object = dynamic> extends dart.core::Object {
+    synthetic constructor •() → #lib::E<#lib::E::K, #lib::E::V>
+      : super dart.core::Object::•()
+      ;
+    method test_reuse1() → dynamic
+      return dart.core::Map::•<#lib::E::K, #lib::E::V>();
+  }
+  class F<K extends dart.core::Object = dynamic, V extends dart.core::Object = dynamic> extends #lib::E<dart.core::String, dart.core::List<#lib::F::V>> {
+    synthetic constructor •() → #lib::F<#lib::F::K, #lib::F::V>
+      : super #lib::E::•()
+      ;
+    method test_reuse2() → dynamic
+      return dart.core::Map::•<dart.core::String, dart.core::List<#lib::F::V>>();
+  }
+  class G<K extends dart.core::Object = dynamic, V extends dart.core::Object = dynamic> extends dart.core::Object {
+    constructor •() → #lib::G<#lib::G::K, #lib::G::V>
+      : super dart.core::Object::•()
+      ;
+    static factory test_factory<K extends dart.core::Object = dynamic, V extends dart.core::Object = dynamic>() → #lib::G<#lib::G::test_factory::K, #lib::G::test_factory::V>
+      return new #lib::H::•<dart.core::String, #lib::G::test_factory::K, #lib::G::test_factory::V>();
+  }
+  class H<P1 extends dart.core::Object = dynamic, P2 extends dart.core::Object = dynamic, P3 extends dart.core::Object = dynamic> extends #lib::G<#lib::H::P2, #lib::H::P3> {
+    synthetic constructor •() → #lib::H<#lib::H::P1, #lib::H::P2, #lib::H::P3>
+      : super #lib::G::•()
+      ;
+  }
+  class I extends dart.core::Object {
+    constructor •(dynamic param) → #lib::I
+      : super dart.core::Object::•()
+      ;
+    static factory test_factory2({dynamic param = null}) → #lib::I
+      return new #lib::I::•(param);
+  }
+  class J extends dart.core::Object {
+    @dart._internal::ExternalName::•("agent_J")
+    external static factory •() → #lib::J;
+  }
+  abstract class K<A extends dart.core::Object = dynamic, B extends dart.core::Object = dynamic> extends dart.core::Object {
+    static factory •<A extends dart.core::Object = dynamic, B extends dart.core::Object = dynamic>() → #lib::K<#lib::K::•::A, #lib::K::•::B>
+      return new #lib::TestTypeArgReuse::•<#lib::K::•::A, #lib::K::•::B>();
+  }
+  class TestTypeArgReuse<P extends dart.core::Object = dynamic, Q extends dart.core::Object = dynamic> extends #lib::Base<#lib::TestTypeArgReuse::P, #lib::TestTypeArgReuse::Q> implements #lib::K<#lib::TestTypeArgReuse::P, #lib::TestTypeArgReuse::Q> {
     synthetic constructor •() → #lib::TestTypeArgReuse<#lib::TestTypeArgReuse::P, #lib::TestTypeArgReuse::Q>
       : super #lib::Base::•()
       ;
diff --git a/pkg/vm/testcases/bytecode/literals.dart.expect b/pkg/vm/testcases/bytecode/literals.dart.expect
index 290438e..0785aa4 100644
--- a/pkg/vm/testcases/bytecode/literals.dart.expect
+++ b/pkg/vm/testcases/bytecode/literals.dart.expect
@@ -1,17 +1,16 @@
 main = #lib::main;
  [@vm.bytecode=
-ComponentBytecodeMetadata {
-
+BytecodeMetadata {
 Bytecode (version: stable)
 Main library: #lib
+Library '#lib'
+    name '#lib'
+    script '#lib'
 
-}
-] [@vm.bytecode=
-MembersBytecodeMetadata {
+Class '', script = '#lib'
 
-Members {
 Field 'c1', type = #lib::A, getter = 'get:c1', reflectable, static, const, final
-    initializer 
+    initializer
 Bytecode {
   Entry                0
   CheckStack           0
@@ -30,7 +29,7 @@
     value = const 6
 
 Field 'c4', type = #lib::C, getter = 'get:c4', reflectable, static, const, final
-    initializer 
+    initializer
 Bytecode {
   Entry                0
   CheckStack           0
@@ -43,7 +42,7 @@
 
 
 Field 'c5', type = #lib::D, getter = 'get:c5', reflectable, static, const, final
-    initializer 
+    initializer
 Bytecode {
   Entry                0
   CheckStack           0
@@ -62,7 +61,6 @@
     parameters [] (required: 0)
     return-type void
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -98,7 +96,6 @@
     parameters [] (required: 0)
     return-type void
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -138,7 +135,6 @@
     parameters [dart:core::int 'a'] (required: 1)
     return-type void
 
-
 Bytecode {
   Entry                1
   CheckStack           0
@@ -207,7 +203,6 @@
     parameters [dart:core::int 'a', dart:core::int 'b', #lib::test_map_literal::TypeParam/0 'c'] (required: 3)
     return-type void
 
-
 Bytecode {
   Entry                2
   CheckStack           0
@@ -310,7 +305,6 @@
     parameters [] (required: 0)
     return-type void
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -336,7 +330,6 @@
     parameters [] (required: 0)
     return-type void
 
-
 Bytecode {
   Entry                1
   CheckStack           0
@@ -364,7 +357,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -380,7 +372,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -396,7 +387,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -413,7 +403,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -423,16 +412,9 @@
 ConstantPool {
 }
 
-}
+Class 'A', script = '#lib', enum
+    extends dart:core::Object
 
-}
-]library #lib from "#lib" as #lib {
-
-  typedef GenericFunctionType = <X extends dart.core::Object = dynamic>(X) → X;
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 Field 'index', type = dart:core::int, getter = 'get:index', reflectable, final
     value = null
 
@@ -440,7 +422,7 @@
     value = null
 
 Field 'values', type = dart:core::List < #lib::A >, getter = 'get:values', reflectable, static, const, final
-    initializer 
+    initializer
 Bytecode {
   Entry                0
   CheckStack           0
@@ -453,7 +435,7 @@
 
 
 Field 'elem1', type = #lib::A, getter = 'get:elem1', reflectable, static, const, final
-    initializer 
+    initializer
 Bytecode {
   Entry                0
   CheckStack           0
@@ -466,7 +448,7 @@
 
 
 Field 'elem2', type = #lib::A, getter = 'get:elem2', reflectable, static, const, final
-    initializer 
+    initializer
 Bytecode {
   Entry                0
   CheckStack           0
@@ -479,7 +461,7 @@
 
 
 Field 'elem3', type = #lib::A, getter = 'get:elem3', reflectable, static, const, final
-    initializer 
+    initializer
 Bytecode {
   Entry                0
   CheckStack           0
@@ -492,7 +474,7 @@
 
 
 Field 'elem4', type = #lib::A, getter = 'get:elem4', reflectable, static, const, final
-    initializer 
+    initializer
 Bytecode {
   Entry                0
   CheckStack           0
@@ -508,7 +490,6 @@
     parameters [dart:core::int 'index', dart:core::String '_name'] (required: 2)
     return-type #lib::A
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -538,7 +519,6 @@
     parameters [] (required: 0)
     return-type dart:core::String
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -551,27 +531,9 @@
   [1] = Reserved
 }
 
-}
+Class 'B', script = '#lib'
+    extends dart:core::Object
 
-}
-]  class A extends dart.core::Object {
-    final field dart.core::int index;
-    final field dart.core::String _name;
-    static const field dart.core::List<#lib::A> values = const <#lib::A>[#lib::A::elem1, #lib::A::elem2, #lib::A::elem3, #lib::A::elem4];
-    static const field #lib::A elem1 = const #lib::A::•(0, "A.elem1");
-    static const field #lib::A elem2 = const #lib::A::•(1, "A.elem2");
-    static const field #lib::A elem3 = const #lib::A::•(2, "A.elem3");
-    static const field #lib::A elem4 = const #lib::A::•(3, "A.elem4");
-    const constructor •(dart.core::int index, dart.core::String _name) → #lib::A
-      : #lib::A::index = index, #lib::A::_name = _name, super dart.core::Object::•()
-      ;
-    method toString() → dart.core::String
-      return this.{=#lib::A::_name};
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 Field 'i', type = dart:core::int, getter = 'get:i', reflectable, final
     value = null
 
@@ -579,7 +541,6 @@
     parameters [dart:core::int 'i'] (required: 1)
     return-type #lib::B
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -599,19 +560,9 @@
   [3] = Reserved
 }
 
-}
+Class 'C', script = '#lib'
+    extends #lib::B
 
-}
-]  class B extends dart.core::Object {
-    final field dart.core::int i;
-    const constructor •(dart.core::int i) → #lib::B
-      : #lib::B::i = i, super dart.core::Object::•()
-      ;
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 Field 'j', type = dart:core::int, getter = 'get:j', reflectable, final
     value = null
 
@@ -619,7 +570,6 @@
     parameters [dart:core::int 'a', dart:core::int 'b', dart:core::int 'c'] (required: 3)
     return-type #lib::C
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -644,19 +594,9 @@
   [3] = Reserved
 }
 
-}
+Class 'D', script = '#lib'
+    extends dart:core::Object
 
-}
-]  class C extends #lib::B {
-    final field dart.core::int j;
-    const constructor •(dart.core::int a, dart.core::int b, dart.core::int c) → #lib::C
-      : #lib::C::j = a.{dart.core::num::+}(b), super #lib::B::•(c.{dart.core::num::*}(5))
-      ;
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 Field 'x', type = dynamic, getter = 'get:x', reflectable, final
     value = null
 
@@ -667,7 +607,6 @@
     parameters [dynamic 'x', dynamic 'y'] (required: 1)
     return-type #lib::D
 
-
 Bytecode {
   EntryOptional        2, 1, 0
   LoadConstant         r2, CP#0
@@ -695,26 +634,15 @@
   [6] = Reserved
 }
 
-}
+Class 'E', script = '#lib'
+    type-params <dart:core::Object T> (args: 1)
+    extends dart:core::Object
 
-}
-]  class D extends dart.core::Object {
-    final field dynamic x;
-    final field dynamic y;
-    const constructor •(dynamic x, [dynamic y = null]) → #lib::D
-      : #lib::D::x = x, #lib::D::y = y, super dart.core::Object::•()
-      ;
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 
 Function '', constructor, const, reflectable, debuggable
     parameters [] (required: 0)
     return-type #lib::E < #lib::E::TypeParam/0 >
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -729,24 +657,15 @@
   [1] = Reserved
 }
 
-}
+Class 'F', script = '#lib'
+    type-params <dart:core::Object P, dart:core::Object Q> (args: 3)
+    extends #lib::E < dart:core::Map < #lib::F::TypeParam/0, #lib::F::TypeParam/1 > >
 
-}
-]  class E<T extends dart.core::Object = dynamic> extends dart.core::Object {
-    const constructor •() → #lib::E<#lib::E::T>
-      : super dart.core::Object::•()
-      ;
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 
 Function '', constructor, const, reflectable, debuggable
     parameters [] (required: 0)
     return-type #lib::F < dart:core::Map < #lib::F::TypeParam/0, #lib::F::TypeParam/1 >, #lib::F::TypeParam/0, #lib::F::TypeParam/1 >
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -761,10 +680,50 @@
   [1] = Reserved
 }
 
-}
 
 }
-]  class F<P extends dart.core::Object = dynamic, Q extends dart.core::Object = dynamic> extends #lib::E<dart.core::Map<#lib::F::P, #lib::F::Q>> {
+]library #lib from "#lib" as #lib {
+
+  typedef GenericFunctionType = <X extends dart.core::Object = dynamic>(X) → X;
+  class A extends dart.core::Object {
+    final field dart.core::int index;
+    final field dart.core::String _name;
+    static const field dart.core::List<#lib::A> values = const <#lib::A>[#lib::A::elem1, #lib::A::elem2, #lib::A::elem3, #lib::A::elem4];
+    static const field #lib::A elem1 = const #lib::A::•(0, "A.elem1");
+    static const field #lib::A elem2 = const #lib::A::•(1, "A.elem2");
+    static const field #lib::A elem3 = const #lib::A::•(2, "A.elem3");
+    static const field #lib::A elem4 = const #lib::A::•(3, "A.elem4");
+    const constructor •(dart.core::int index, dart.core::String _name) → #lib::A
+      : #lib::A::index = index, #lib::A::_name = _name, super dart.core::Object::•()
+      ;
+    method toString() → dart.core::String
+      return this.{=#lib::A::_name};
+  }
+  class B extends dart.core::Object {
+    final field dart.core::int i;
+    const constructor •(dart.core::int i) → #lib::B
+      : #lib::B::i = i, super dart.core::Object::•()
+      ;
+  }
+  class C extends #lib::B {
+    final field dart.core::int j;
+    const constructor •(dart.core::int a, dart.core::int b, dart.core::int c) → #lib::C
+      : #lib::C::j = a.{dart.core::num::+}(b), super #lib::B::•(c.{dart.core::num::*}(5))
+      ;
+  }
+  class D extends dart.core::Object {
+    final field dynamic x;
+    final field dynamic y;
+    const constructor •(dynamic x, [dynamic y = null]) → #lib::D
+      : #lib::D::x = x, #lib::D::y = y, super dart.core::Object::•()
+      ;
+  }
+  class E<T extends dart.core::Object = dynamic> extends dart.core::Object {
+    const constructor •() → #lib::E<#lib::E::T>
+      : super dart.core::Object::•()
+      ;
+  }
+  class F<P extends dart.core::Object = dynamic, Q extends dart.core::Object = dynamic> extends #lib::E<dart.core::Map<#lib::F::P, #lib::F::Q>> {
     const constructor •() → #lib::F<#lib::F::P, #lib::F::Q>
       : super #lib::E::•()
       ;
diff --git a/pkg/vm/testcases/bytecode/loops.dart.expect b/pkg/vm/testcases/bytecode/loops.dart.expect
index 99ac193..da711c9 100644
--- a/pkg/vm/testcases/bytecode/loops.dart.expect
+++ b/pkg/vm/testcases/bytecode/loops.dart.expect
@@ -1,21 +1,19 @@
 main = #lib::main;
  [@vm.bytecode=
-ComponentBytecodeMetadata {
-
+BytecodeMetadata {
 Bytecode (version: stable)
 Main library: #lib
+Library '#lib'
+    name '#lib'
+    script '#lib'
 
-}
-] [@vm.bytecode=
-MembersBytecodeMetadata {
+Class '', script = '#lib'
 
-Members {
 
 Function 'test_for', static, reflectable, debuggable
     parameters [dart:core::List < dart:core::int > 'list'] (required: 1)
     return-type dart:core::int
 
-
 Bytecode {
   Entry                2
   CheckStack           0
@@ -58,7 +56,6 @@
     parameters [dart:core::List < dart:core::int > 'list'] (required: 1)
     return-type dart:core::int
 
-
 Bytecode {
   Entry                2
   CheckStack           0
@@ -107,7 +104,6 @@
     parameters [dart:core::List < dart:core::int > 'list'] (required: 1)
     return-type dart:core::int
 
-
 Bytecode {
   Entry                2
   CheckStack           0
@@ -158,7 +154,6 @@
     parameters [dart:core::List < dart:core::int > 'list'] (required: 1)
     return-type dart:core::int
 
-
 Bytecode {
   Entry                4
   CheckStack           0
@@ -203,7 +198,6 @@
     parameters [dart:core::List < dart:core::int > 'list'] (required: 1)
     return-type dart:core::int
 
-
 Bytecode {
   Entry                2
   CheckStack           0
@@ -243,7 +237,6 @@
     parameters [dart:core::List < dart:core::int > 'list'] (required: 1)
     return-type dart:core::int
 
-
 Bytecode {
   Entry                3
   CheckStack           0
@@ -283,7 +276,6 @@
     parameters [dart:core::List < dart:core::int > 'list'] (required: 1)
     return-type dart:core::int
 
-
 Bytecode {
   Entry                4
   CheckStack           0
@@ -327,7 +319,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -337,7 +328,6 @@
 ConstantPool {
 }
 
-}
 
 }
 ]library #lib from "#lib" as #lib {
diff --git a/pkg/vm/testcases/bytecode/optional_params.dart.expect b/pkg/vm/testcases/bytecode/optional_params.dart.expect
index 49156f2..62dae5f 100644
--- a/pkg/vm/testcases/bytecode/optional_params.dart.expect
+++ b/pkg/vm/testcases/bytecode/optional_params.dart.expect
@@ -1,21 +1,19 @@
 main = #lib::main;
  [@vm.bytecode=
-ComponentBytecodeMetadata {
-
+BytecodeMetadata {
 Bytecode (version: stable)
 Main library: #lib
+Library '#lib'
+    name '#lib'
+    script '#lib'
 
-}
-] [@vm.bytecode=
-MembersBytecodeMetadata {
+Class '', script = '#lib'
 
-Members {
 
 Function 'foo1', static, has-optional-positional-params, reflectable, debuggable
     parameters [dynamic 'x', dynamic 'a', dynamic 'b'] (required: 1)
     return-type void
 
-
 Bytecode {
   EntryOptional        1, 2, 0
   LoadConstant         r1, CP#0
@@ -87,7 +85,6 @@
     parameters [dynamic 'y', dynamic 'z', dynamic 'c', dynamic 'a', dynamic 'b'] (required: 2)
     return-type void
 
-
 Bytecode {
   EntryOptional        2, 0, 3
   LoadConstant         r2, CP#0
@@ -200,7 +197,6 @@
     parameters [dynamic 'z', dynamic 'y', dart:core::bool 'a', dart:core::Map < #lib::foo3::TypeParam/0, #lib::foo3::TypeParam/1 > 'b'] (required: 2)
     return-type void
 
-
 Bytecode {
   EntryOptional        2, 0, 2
   LoadConstant         r2, CP#0
@@ -239,7 +235,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -266,7 +261,6 @@
   [7] = Reserved
 }
 
-}
 
 }
 ]library #lib from "#lib" as #lib {
diff --git a/pkg/vm/testcases/bytecode/super_calls.dart.expect b/pkg/vm/testcases/bytecode/super_calls.dart.expect
index 31ff703..0ac5882 100644
--- a/pkg/vm/testcases/bytecode/super_calls.dart.expect
+++ b/pkg/vm/testcases/bytecode/super_calls.dart.expect
@@ -1,21 +1,19 @@
 main = #lib::main;
  [@vm.bytecode=
-ComponentBytecodeMetadata {
-
+BytecodeMetadata {
 Bytecode (version: stable)
 Main library: #lib
+Library '#lib'
+    name '#lib'
+    script '#lib'
 
-}
-] [@vm.bytecode=
-MembersBytecodeMetadata {
+Class '', script = '#lib'
 
-Members {
 
 Function 'main', static, reflectable, debuggable
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -25,21 +23,14 @@
 ConstantPool {
 }
 
-}
+Class 'Base1', script = '#lib'
+    extends dart:core::Object
 
-}
-]library #lib from "#lib" as #lib {
-
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 
 Function '', constructor, reflectable
     parameters [] (required: 0)
     return-type #lib::Base1
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -60,7 +51,6 @@
     parameters [#lib::Base1::foo::TypeParam/0 'a1', dart:core::int 'a2'] (required: 2)
     return-type void
 
-
 Bytecode {
   Entry                1
   CheckStack           0
@@ -76,7 +66,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -91,7 +80,6 @@
     parameters [dart:core::int 'x'] (required: 1)
     return-type void
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -101,28 +89,14 @@
 ConstantPool {
 }
 
-}
+Class 'A', script = '#lib'
+    extends #lib::Base1
 
-}
-]  class Base1 extends dart.core::Object {
-    synthetic constructor •() → #lib::Base1
-      : super dart.core::Object::•()
-      ;
-    method foo<T extends dart.core::Object = dynamic>(#lib::Base1::foo::T a1, dart.core::int a2) → void {}
-    get bar() → dynamic
-      return 42;
-    set bazz(dart.core::int x) → void {}
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 
 Function '', constructor, reflectable
     parameters [] (required: 0)
     return-type #lib::A
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -142,7 +116,6 @@
     parameters [dart:core::int 'x'] (required: 1)
     return-type dynamic
 
-
 Bytecode {
   Entry                1
   CheckStack           0
@@ -165,7 +138,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                1
   CheckStack           0
@@ -183,7 +155,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                1
   CheckStack           0
@@ -201,7 +172,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                1
   CheckStack           0
@@ -226,7 +196,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                1
   CheckStack           0
@@ -242,35 +211,14 @@
   [1] = Reserved
 }
 
-}
+Class 'Base2', script = '#lib', abstract
+    extends dart:core::Object
 
-}
-]  class A extends #lib::Base1 {
-    synthetic constructor •() → #lib::A
-      : super #lib::Base1::•()
-      ;
-    method testSuperCall(dart.core::int x) → dynamic
-      return super.{#lib::Base1::foo}<dart.core::String>("a1", 2);
-    method testSuperTearOff() → dynamic
-      return super.{#lib::Base1::foo};
-    method testSuperGet() → dynamic
-      return super.{#lib::Base1::bar};
-    method testSuperCallViaGetter() → dynamic
-      return [@vm.call-site-attributes.metadata=receiverType:dynamic] super.{#lib::Base1::bar}.call<dart.core::int>("param");
-    method testSuperSet() → dynamic {
-      super.{#lib::Base1::bazz} = 3;
-    }
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 
 Function '', constructor, reflectable
     parameters [] (required: 0)
     return-type #lib::Base2
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -298,27 +246,14 @@
 Function 'set:bazz', setter, abstract, reflectable, debuggable
     parameters [dart:core::int 'x'] (required: 1)
     return-type void
-}
+Class 'B', script = '#lib', abstract
+    extends #lib::Base2
 
-}
-]  abstract class Base2 extends dart.core::Object {
-    synthetic constructor •() → #lib::Base2
-      : super dart.core::Object::•()
-      ;
-    abstract method foo<T extends dart.core::Object = dynamic>(dart.core::String a1, #lib::Base2::foo::T a2, dart.core::int a3) → void;
-    abstract get bar() → dynamic;
-    abstract set bazz(dart.core::int x) → void;
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 
 Function '', constructor, reflectable
     parameters [] (required: 0)
     return-type #lib::B
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -338,7 +273,6 @@
     parameters [dart:core::int 'x'] (required: 1)
     return-type dynamic
 
-
 Bytecode {
   Entry                1
   CheckStack           0
@@ -392,7 +326,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                1
   CheckStack           0
@@ -427,7 +360,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                1
   CheckStack           0
@@ -462,7 +394,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                1
   CheckStack           0
@@ -504,7 +435,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                1
   CheckStack           0
@@ -540,10 +470,44 @@
   [6] = Reserved
 }
 
-}
 
 }
-]  abstract class B extends #lib::Base2 {
+]library #lib from "#lib" as #lib {
+
+  class Base1 extends dart.core::Object {
+    synthetic constructor •() → #lib::Base1
+      : super dart.core::Object::•()
+      ;
+    method foo<T extends dart.core::Object = dynamic>(#lib::Base1::foo::T a1, dart.core::int a2) → void {}
+    get bar() → dynamic
+      return 42;
+    set bazz(dart.core::int x) → void {}
+  }
+  class A extends #lib::Base1 {
+    synthetic constructor •() → #lib::A
+      : super #lib::Base1::•()
+      ;
+    method testSuperCall(dart.core::int x) → dynamic
+      return super.{#lib::Base1::foo}<dart.core::String>("a1", 2);
+    method testSuperTearOff() → dynamic
+      return super.{#lib::Base1::foo};
+    method testSuperGet() → dynamic
+      return super.{#lib::Base1::bar};
+    method testSuperCallViaGetter() → dynamic
+      return [@vm.call-site-attributes.metadata=receiverType:dynamic] super.{#lib::Base1::bar}.call<dart.core::int>("param");
+    method testSuperSet() → dynamic {
+      super.{#lib::Base1::bazz} = 3;
+    }
+  }
+  abstract class Base2 extends dart.core::Object {
+    synthetic constructor •() → #lib::Base2
+      : super dart.core::Object::•()
+      ;
+    abstract method foo<T extends dart.core::Object = dynamic>(dart.core::String a1, #lib::Base2::foo::T a2, dart.core::int a3) → void;
+    abstract get bar() → dynamic;
+    abstract set bazz(dart.core::int x) → void;
+  }
+  abstract class B extends #lib::Base2 {
     synthetic constructor •() → #lib::B
       : super #lib::Base2::•()
       ;
diff --git a/pkg/vm/testcases/bytecode/switch.dart.expect b/pkg/vm/testcases/bytecode/switch.dart.expect
index 80a1682..bc8357f 100644
--- a/pkg/vm/testcases/bytecode/switch.dart.expect
+++ b/pkg/vm/testcases/bytecode/switch.dart.expect
@@ -1,21 +1,19 @@
 main = #lib::main;
  [@vm.bytecode=
-ComponentBytecodeMetadata {
-
+BytecodeMetadata {
 Bytecode (version: stable)
 Main library: #lib
+Library '#lib'
+    name '#lib'
+    script '#lib'
 
-}
-] [@vm.bytecode=
-MembersBytecodeMetadata {
+Class '', script = '#lib'
 
-Members {
 
 Function 'foo1', static, reflectable, debuggable
     parameters [dart:core::int 'x'] (required: 1)
     return-type dart:core::int
 
-
 Bytecode {
   Entry                2
   CheckStack           0
@@ -62,7 +60,6 @@
     parameters [dart:core::int 'x'] (required: 1)
     return-type dart:core::int
 
-
 Bytecode {
   Entry                2
   CheckStack           0
@@ -120,7 +117,6 @@
     parameters [dart:core::int 'x'] (required: 1)
     return-type dart:core::int
 
-
 Bytecode {
   Entry                2
   CheckStack           0
@@ -178,7 +174,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -188,7 +183,6 @@
 ConstantPool {
 }
 
-}
 
 }
 ]library #lib from "#lib" as #lib {
diff --git a/pkg/vm/testcases/bytecode/try_blocks.dart.expect b/pkg/vm/testcases/bytecode/try_blocks.dart.expect
index 3527e207..f1ef3d4 100644
--- a/pkg/vm/testcases/bytecode/try_blocks.dart.expect
+++ b/pkg/vm/testcases/bytecode/try_blocks.dart.expect
@@ -1,21 +1,19 @@
 main = #lib::main;
  [@vm.bytecode=
-ComponentBytecodeMetadata {
-
+BytecodeMetadata {
 Bytecode (version: stable)
 Main library: #lib
+Library '#lib'
+    name '#lib'
+    script '#lib'
 
-}
-] [@vm.bytecode=
-MembersBytecodeMetadata {
+Class '', script = '#lib'
 
-Members {
 
 Function 'testTryCatch1', static, reflectable, debuggable
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                4
   CheckStack           0
@@ -69,7 +67,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                5
   CheckStack           0
@@ -207,7 +204,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                7
   CheckStack           0
@@ -381,7 +377,6 @@
 L1:
   PushNull
   ReturnTOS
-
 }
 
 Closure #lib::testTryCatch3::'bar' () -> void
@@ -443,7 +438,6 @@
 L1:
   PushNull
   ReturnTOS
-
 }
 
 
@@ -451,7 +445,6 @@
     parameters [dart:core::bool 'cond'] (required: 1)
     return-type dynamic
 
-
 Bytecode {
   Entry                8
   CheckStack           0
@@ -535,7 +528,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                3
   CheckStack           0
@@ -599,7 +591,6 @@
     parameters [dart:core::int 'x'] (required: 1)
     return-type dynamic
 
-
 Bytecode {
   Entry                9
   CheckStack           0
@@ -756,7 +747,6 @@
   Drop1
   PushNull
   ReturnTOS
-
 }
 
 
@@ -764,7 +754,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                6
   CheckStack           0
@@ -944,7 +933,6 @@
   Drop1
   PushInt              43
   ReturnTOS
-
 }
 
 
@@ -952,7 +940,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                5
   CheckStack           0
@@ -1011,7 +998,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -1021,7 +1007,6 @@
 ConstantPool {
 }
 
-}
 
 }
 ]library #lib from "#lib" as #lib {
diff --git a/pkg/vm/testcases/bytecode/type_ops.dart.expect b/pkg/vm/testcases/bytecode/type_ops.dart.expect
index ca572e8..0e84cb2 100644
--- a/pkg/vm/testcases/bytecode/type_ops.dart.expect
+++ b/pkg/vm/testcases/bytecode/type_ops.dart.expect
@@ -1,15 +1,14 @@
 main = #lib::main;
  [@vm.bytecode=
-ComponentBytecodeMetadata {
-
+BytecodeMetadata {
 Bytecode (version: stable)
 Main library: #lib
+Library '#lib'
+    name '#lib'
+    script '#lib'
 
-}
-] [@vm.bytecode=
-MembersBytecodeMetadata {
+Class '', script = '#lib'
 
-Members {
 Field 'globalVar', type = dart:core::List < dart:core::Iterable null >, reflectable, static
     value = null
 
@@ -17,7 +16,6 @@
     parameters [dynamic 'x'] (required: 1)
     return-type dynamic
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -48,7 +46,7 @@
   ReturnTOS
 }
 ConstantPool {
-  [0] = Type #lib::B
+  [0] = Type #lib::B < dart:core::String >
   [1] = InterfaceCall 'dart:core::Object::_simpleInstanceOf', ArgDesc num-args 2, num-type-args 0, names []
   [2] = Reserved
   [3] = ObjectRef '11'
@@ -68,7 +66,6 @@
     parameters [dynamic 'x'] (required: 1)
     return-type void
 
-
 Bytecode {
   Entry                1
   CheckStack           0
@@ -94,7 +91,6 @@
     parameters [] (required: 0)
     return-type dynamic
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -104,21 +100,15 @@
 ConstantPool {
 }
 
-}
+Class 'A', script = '#lib'
+    type-params <dart:core::Object T> (args: 1)
+    extends dart:core::Object
 
-}
-]library #lib from "#lib" as #lib {
-
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 
 Function '', constructor, reflectable
     parameters [] (required: 0)
     return-type #lib::A < #lib::A::TypeParam/0 >
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -133,23 +123,13 @@
   [1] = Reserved
 }
 
-}
+Class 'B', script = '#lib'
+    extends #lib::A < dart:core::String >
 
-}
-]  class A<T extends dart.core::Object = dynamic> extends dart.core::Object {
-    synthetic constructor •() → #lib::A<#lib::A::T>
-      : super dart.core::Object::•()
-      ;
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 
 Function '', constructor, reflectable
     parameters [] (required: 0)
-    return-type #lib::B
-
+    return-type #lib::B < dart:core::String >
 
 Bytecode {
   Entry                0
@@ -165,24 +145,15 @@
   [1] = Reserved
 }
 
-}
+Class 'C', script = '#lib'
+    type-params <dart:core::Object T1, dart:core::Object T2, dart:core::Object T3> (args: 4)
+    extends #lib::B < dart:core::String >
 
-}
-]  class B extends #lib::A<dart.core::String> {
-    synthetic constructor •() → #lib::B
-      : super #lib::A::•()
-      ;
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 
 Function '', constructor, reflectable
     parameters [] (required: 0)
     return-type #lib::C < dart:core::String, #lib::C::TypeParam/0, #lib::C::TypeParam/1, #lib::C::TypeParam/2 >
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -197,18 +168,10 @@
   [1] = Reserved
 }
 
-}
+Class 'D', script = '#lib'
+    type-params <dart:core::Object P, dart:core::Object Q> (args: 5)
+    extends #lib::C < dart:core::String, dart:core::int, #lib::D::TypeParam/1, #lib::D::TypeParam/0 >
 
-}
-]  class C<T1 extends dart.core::Object = dynamic, T2 extends dart.core::Object = dynamic, T3 extends dart.core::Object = dynamic> extends #lib::B {
-    synthetic constructor •() → #lib::C<#lib::C::T1, #lib::C::T2, #lib::C::T3>
-      : super #lib::B::•()
-      ;
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 Field 'foo', type = dart:core::Map < #lib::D::TypeParam/0, #lib::D::TypeParam/1 >, getter = 'get:foo', setter = 'set:foo', reflectable
     value = null
 
@@ -216,7 +179,6 @@
     parameters [dynamic 'tt'] (required: 1)
     return-type #lib::D < dart:core::String, dart:core::int, #lib::D::TypeParam/1, #lib::D::TypeParam/0, #lib::D::TypeParam/1 >
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -251,7 +213,6 @@
     parameters [dynamic 'y'] (required: 1)
     return-type dynamic
 
-
 Bytecode {
   Entry                1
   CheckStack           0
@@ -313,7 +274,6 @@
     parameters [dynamic 'z'] (required: 1)
     return-type dynamic
 
-
 Bytecode {
   Entry                1
   CheckStack           0
@@ -371,7 +331,6 @@
     parameters [dynamic 'w'] (required: 1)
     return-type dart:core::Map < #lib::D::TypeParam/0, #lib::D::TypeParam/1 >
 
-
 Bytecode {
   Entry                2
   CheckStack           0
@@ -416,48 +375,16 @@
   [7] = SubtypeTestCache
 }
 
-}
+Class 'E', script = '#lib'
+    type-params <dart:core::String P> (args: 1)
+    extends dart:core::Object
 
-}
-]  class D<P extends dart.core::Object = dynamic, Q extends dart.core::Object = dynamic> extends #lib::C<dart.core::int, #lib::D::Q, #lib::D::P> {
-    generic-covariant-impl field dart.core::Map<#lib::D::P, #lib::D::Q> foo;
-    constructor •(dynamic tt) → #lib::D<#lib::D::P, #lib::D::Q>
-      : #lib::D::foo = tt as{TypeError} dart.core::Map<#lib::D::P, #lib::D::Q>, super #lib::C::•()
-      ;
-    method foo2(dynamic y) → dynamic {
-      if(y is #lib::A<#lib::D::P>) {
-        dart.core::print("21");
-      }
-      if(y is #lib::C<dynamic, #lib::D::Q, dart.core::List<#lib::D::P>>) {
-        dart.core::print("22");
-      }
-      [@vm.call-site-attributes.metadata=receiverType:#lib::D<#lib::D::P, #lib::D::Q>] this.{#lib::D::foo} = y as{TypeError} dart.core::Map<#lib::D::P, #lib::D::Q>;
-    }
-    method foo3<T1 extends dart.core::Object = dynamic, T2 extends dart.core::Object = dynamic>(dynamic z) → dynamic {
-      if(z is #lib::A<#lib::D::foo3::T1>) {
-        dart.core::print("31");
-      }
-      if(z is #lib::C<dart.core::Map<#lib::D::foo3::T1, #lib::D::P>, dart.core::List<#lib::D::foo3::T2>, #lib::D::Q>) {
-        dart.core::print("32");
-      }
-      return (z as dart.core::Map<#lib::D::foo3::T2, #lib::D::Q>).{dart.core::Map::values};
-    }
-    method foo4(dynamic w) → dart.core::Map<#lib::D::P, #lib::D::Q> {
-      dart.core::List<dart.core::Map<#lib::D::P, #lib::D::Q>> list = <dart.core::Map<#lib::D::P, #lib::D::Q>>[w as{TypeError} dart.core::Map<#lib::D::P, #lib::D::Q>];
-      return w as{TypeError} dart.core::Map<#lib::D::P, #lib::D::Q>;
-    }
-  }
-[@vm.bytecode=
-MembersBytecodeMetadata {
-
-Members {
 
 Function '', factory, static, reflectable, debuggable
     type-params <dart:core::String P>
     parameters [] (required: 0)
     return-type #lib::E < #lib::E:: (constructor)::TypeParam/0 >
 
-
 Bytecode {
   Entry                0
   CheckStack           0
@@ -473,7 +400,6 @@
     parameters [dart:core::Map < #lib::E::foo6::TypeParam/0, #lib::E::foo6::TypeParam/1 > 'map'] (required: 1)
     return-type void
 
-
 Bytecode {
   Entry                1
   CheckStack           0
@@ -504,10 +430,54 @@
   [4] = ObjectRef 'T'
 }
 
-}
 
 }
-]  class E<P extends dart.core::String = dart.core::String> extends dart.core::Object {
+]library #lib from "#lib" as #lib {
+
+  class A<T extends dart.core::Object = dynamic> extends dart.core::Object {
+    synthetic constructor •() → #lib::A<#lib::A::T>
+      : super dart.core::Object::•()
+      ;
+  }
+  class B extends #lib::A<dart.core::String> {
+    synthetic constructor •() → #lib::B
+      : super #lib::A::•()
+      ;
+  }
+  class C<T1 extends dart.core::Object = dynamic, T2 extends dart.core::Object = dynamic, T3 extends dart.core::Object = dynamic> extends #lib::B {
+    synthetic constructor •() → #lib::C<#lib::C::T1, #lib::C::T2, #lib::C::T3>
+      : super #lib::B::•()
+      ;
+  }
+  class D<P extends dart.core::Object = dynamic, Q extends dart.core::Object = dynamic> extends #lib::C<dart.core::int, #lib::D::Q, #lib::D::P> {
+    generic-covariant-impl field dart.core::Map<#lib::D::P, #lib::D::Q> foo;
+    constructor •(dynamic tt) → #lib::D<#lib::D::P, #lib::D::Q>
+      : #lib::D::foo = tt as{TypeError} dart.core::Map<#lib::D::P, #lib::D::Q>, super #lib::C::•()
+      ;
+    method foo2(dynamic y) → dynamic {
+      if(y is #lib::A<#lib::D::P>) {
+        dart.core::print("21");
+      }
+      if(y is #lib::C<dynamic, #lib::D::Q, dart.core::List<#lib::D::P>>) {
+        dart.core::print("22");
+      }
+      [@vm.call-site-attributes.metadata=receiverType:#lib::D<#lib::D::P, #lib::D::Q>] this.{#lib::D::foo} = y as{TypeError} dart.core::Map<#lib::D::P, #lib::D::Q>;
+    }
+    method foo3<T1 extends dart.core::Object = dynamic, T2 extends dart.core::Object = dynamic>(dynamic z) → dynamic {
+      if(z is #lib::A<#lib::D::foo3::T1>) {
+        dart.core::print("31");
+      }
+      if(z is #lib::C<dart.core::Map<#lib::D::foo3::T1, #lib::D::P>, dart.core::List<#lib::D::foo3::T2>, #lib::D::Q>) {
+        dart.core::print("32");
+      }
+      return (z as dart.core::Map<#lib::D::foo3::T2, #lib::D::Q>).{dart.core::Map::values};
+    }
+    method foo4(dynamic w) → dart.core::Map<#lib::D::P, #lib::D::Q> {
+      dart.core::List<dart.core::Map<#lib::D::P, #lib::D::Q>> list = <dart.core::Map<#lib::D::P, #lib::D::Q>>[w as{TypeError} dart.core::Map<#lib::D::P, #lib::D::Q>];
+      return w as{TypeError} dart.core::Map<#lib::D::P, #lib::D::Q>;
+    }
+  }
+  class E<P extends dart.core::String = dart.core::String> extends dart.core::Object {
     static factory •<P extends dart.core::String = dynamic>() → #lib::E<#lib::E::•::P>
       return null;
     method foo6<generic-covariant-impl T extends #lib::E::P = #lib::E::P, U extends dart.core::List<#lib::E::foo6::T> = dart.core::List<#lib::E::P>>(dart.core::Map<#lib::E::foo6::T, #lib::E::foo6::U> map) → void {}
diff --git a/runtime/bin/BUILD.gn b/runtime/bin/BUILD.gn
index 5b5cacc..f8631d7 100644
--- a/runtime/bin/BUILD.gn
+++ b/runtime/bin/BUILD.gn
@@ -1028,7 +1028,7 @@
     ":dart",
   ]
   sources = [
-    "ffi_test_dynamic_library.cc",
+    "ffi_test/ffi_test_dynamic_library.cc",
   ]
   include_dirs = [ ".." ]
   defines = [
@@ -1050,8 +1050,13 @@
     ":dart",
   ]
   sources = [
-    "ffi_test_functions.cc",
+    "ffi_test/ffi_test_functions.cc",
   ]
+  if (is_win && current_cpu == "x64") {
+    sources += [ "ffi_test/clobber_x64_win.S" ]
+  } else if (!is_win) {
+    sources += [ "ffi_test/clobber_$current_cpu.S" ]
+  }
   include_dirs = [ ".." ]
   defines = [
     # The only effect of DART_SHARED_LIB is to export the Dart API.
diff --git a/runtime/bin/ffi_test/clobber_arm.S b/runtime/bin/ffi_test/clobber_arm.S
new file mode 100644
index 0000000..9564a4a
--- /dev/null
+++ b/runtime/bin/ffi_test/clobber_arm.S
@@ -0,0 +1,29 @@
+.text
+
+.global ClobberAndCall
+ClobberAndCall:
+
+/* Save r3 to keep the stack aligned to 8 bytes. */
+stmdb sp!,{r3, r4, r5, r6, r10, lr}
+
+/* Arguments descriptor register. */
+mov r4, #1
+
+/* Pool pointer register. */
+mov r5, #1
+
+/* Code pointer register. */
+mov r6, #1
+
+/* Thread register. */
+mov r10, #1
+
+/* Clobber callee-saved registers. */
+mov r1, #1
+mov r2, #1
+mov r3, #1
+mov r12, #1
+
+blx r0
+
+ldm sp!,{r3, r4, r5, r6, r10, pc}
diff --git a/runtime/bin/ffi_test/clobber_arm64.S b/runtime/bin/ffi_test/clobber_arm64.S
new file mode 100644
index 0000000..111c8fa
--- /dev/null
+++ b/runtime/bin/ffi_test/clobber_arm64.S
@@ -0,0 +1,44 @@
+.text
+
+.global ClobberAndCall
+ClobberAndCall:
+
+/* Save link register register and thread register. Keep stack aligned to 16 bytes. */
+stp lr, x26, [sp, #-16]!
+mov lr, #1
+mov x26, #1
+
+/* Arguments descriptor register isn't callee-saved. */
+mov x4, #1
+
+/* Dart stack pointer, also volatile. */
+mov x15, #1
+
+/* Pool pointer register and code register. Keep stack aligned to 16 bytes. */
+stp x24, x27, [sp, #-16]!
+
+mov x24, #1
+mov x27, #1
+
+/* Clobber all other volatile registers. */
+mov x1, #1
+mov x2, #1
+mov x3, #1
+mov x4, #1
+mov x5, #1
+mov x6, #1
+mov x7, #1
+mov x8, #1
+mov x9, #1
+mov x10, #1
+mov x11, #1
+mov x12, #1
+mov x13, #1
+mov x14, #1
+
+blr x0
+
+ldp x24, x27, [sp], #16
+ldp lr, x26, [sp], #16
+
+blr lr
diff --git a/runtime/bin/ffi_test/clobber_x64.S b/runtime/bin/ffi_test/clobber_x64.S
new file mode 100644
index 0000000..a54aa73
--- /dev/null
+++ b/runtime/bin/ffi_test/clobber_x64.S
@@ -0,0 +1,44 @@
+.intel_syntax noprefix
+.text
+
+.globl _ClobberAndCall
+_ClobberAndCall:
+.globl ClobberAndCall
+ClobberAndCall:
+
+/* Clobber some significant registers and call the nullary function which is
+   passed in as the first argument. */
+
+/* Pool pointer register. */
+push r15
+mov r15, 1
+
+/* Thread register. */
+push r14
+mov r14, 1
+
+/* Code register. */
+push r12
+mov r12, 1
+
+/* Arguments descriptor register (volatile). */
+mov r10, 1
+
+/* Clobber all other volatile registers (except the argument). */
+mov rax, 1
+mov rcx, 1
+mov rdx, 1
+mov rsi, 1
+mov r8, 1
+mov r9, 1
+mov r11, 1
+
+/* Stack must be 16-byte aligned before the call. We save three registers above
+	 to ensure this. */
+call rdi
+
+pop r12
+pop r14
+pop r15
+
+ret
diff --git a/runtime/bin/ffi_test/clobber_x64_win.S b/runtime/bin/ffi_test/clobber_x64_win.S
new file mode 100644
index 0000000..2301e15
--- /dev/null
+++ b/runtime/bin/ffi_test/clobber_x64_win.S
@@ -0,0 +1,41 @@
+.code
+
+ClobberAndCall proc
+
+;; Clobber some significant registers and call the nullary function which is
+;; passed in as the first argument.
+
+;; Pool pointer register.
+push r15
+mov r15, 1
+
+;; Thread register.
+push r14
+mov r14, 1
+
+;; Code register.
+push r12
+mov r12, 1
+
+;; Arguments descriptor register (volatile).
+mov r10, 1
+
+;; Clobber all other volatile registers (except the argument).
+mov rax, 1
+mov rcx, 1
+mov r8, 1
+mov r9, 1
+mov r11, 1
+
+;; Stack must be 16-byte aligned before the call. We save three registers above
+;; to ensure this.
+call rdx
+
+pop r12
+pop r14
+pop r15
+
+ret
+ClobberAndCall endp
+
+end
diff --git a/runtime/bin/ffi_test/clobber_x86.S b/runtime/bin/ffi_test/clobber_x86.S
new file mode 100644
index 0000000..d5de1ee
--- /dev/null
+++ b/runtime/bin/ffi_test/clobber_x86.S
@@ -0,0 +1,37 @@
+.intel_syntax noprefix
+.text
+
+.globl _ClobberAndCall
+_ClobberAndCall:
+.globl ClobberAndCall
+ClobberAndCall:
+
+/* Load the target function. */
+mov eax, [esp+0x4]
+
+/* Code register. */
+push edi
+mov edi, 1
+
+/* Thread register. */
+push esi
+mov esi, 1
+
+/* Arguments descriptor register (volatile). */
+mov edx, 1
+
+/* Clobber all other volatile registers. */
+mov ecx, 1
+mov edx, 1
+
+/* Align the frame to 16 bytes. */
+sub esp, 4
+
+call eax
+
+add esp, 4
+
+pop esi
+pop edi
+
+ret
diff --git a/runtime/bin/ffi_test_dynamic_library.cc b/runtime/bin/ffi_test/ffi_test_dynamic_library.cc
similarity index 100%
rename from runtime/bin/ffi_test_dynamic_library.cc
rename to runtime/bin/ffi_test/ffi_test_dynamic_library.cc
diff --git a/runtime/bin/ffi_test_functions.cc b/runtime/bin/ffi_test/ffi_test_functions.cc
similarity index 91%
rename from runtime/bin/ffi_test_functions.cc
rename to runtime/bin/ffi_test/ffi_test_functions.cc
index 52b4512..91c7856 100644
--- a/runtime/bin/ffi_test_functions.cc
+++ b/runtime/bin/ffi_test/ffi_test_functions.cc
@@ -149,7 +149,7 @@
 }
 
 // Sums many ints.
-// Used for testing calling conventions. With so many doubles we are using all
+// Used for testing calling conventions. With so many integers we are using all
 // normal parameter registers and some stack slots.
 DART_EXPORT intptr_t SumManyInts(intptr_t a,
                                  intptr_t b,
@@ -169,6 +169,28 @@
   return retval;
 }
 
+// Sums an odd number of ints.
+// Used for testing calling conventions. With so many arguments, and an odd
+// number of arguments, we are testing stack alignment on various architectures.
+DART_EXPORT intptr_t SumManyIntsOdd(intptr_t a,
+                                    intptr_t b,
+                                    intptr_t c,
+                                    intptr_t d,
+                                    intptr_t e,
+                                    intptr_t f,
+                                    intptr_t g,
+                                    intptr_t h,
+                                    intptr_t i,
+                                    intptr_t j,
+                                    intptr_t k) {
+  std::cout << "SumManyInts(" << a << ", " << b << ", " << c << ", " << d
+            << ", " << e << ", " << f << ", " << g << ", " << h << ", " << i
+            << ", " << j << ", " << k << ")\n";
+  intptr_t retval = a + b + c + d + e + f + g + h + i + j + k;
+  std::cout << "returning " << retval << "\n";
+  return retval;
+}
+
 // Sums many doubles.
 // Used for testing calling conventions. With so many doubles we are using all
 // xmm parameter registers and some stack slots.
@@ -485,23 +507,20 @@
   Dart_ExecuteInternalCommand("gc-now");
 }
 
-// Calls a Dart function to allocate 'count' objects.
-// Used for stress-testing GC when re-entering the API.
-DART_EXPORT void AllocateThroughDart() {
-  Dart_EnterScope();
-  Dart_Handle root = Dart_RootLibrary();
-  Dart_Handle result = Dart_Invoke(
-      root, Dart_NewStringFromCString("testAllocationsInDartHelper"), 0, NULL);
-  const char* error;
-  if (Dart_IsError(result)) {
-    Dart_StringToCString(Dart_ToString(result), &error);
-    fprintf(stderr, "Could not call 'testAllocationsInDartHelper': %s\n",
-            error);
-    Dart_DumpNativeStackTrace(nullptr);
-    Dart_PrepareToAbort();
-    abort();
-  }
-  Dart_ExitScope();
+// Triggers GC. Has 11 dummy arguments as unboxed odd integers which should be
+// ignored by GC.
+DART_EXPORT void Regress37069(uint64_t a,
+                              uint64_t b,
+                              uint64_t c,
+                              uint64_t d,
+                              uint64_t e,
+                              uint64_t f,
+                              uint64_t g,
+                              uint64_t h,
+                              uint64_t i,
+                              uint64_t j,
+                              uint64_t k) {
+  Dart_ExecuteInternalCommand("gc-now");
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -625,6 +644,24 @@
   return 0;
 }
 
+// Defined in ffi_test_functions.S.
+//
+// Clobbers some registers with special meaning in Dart before re-entry, for
+// stress-testing. Not used on 32-bit Windows due to complications with Windows
+// "safeseh".
+#if defined(TARGET_OS_WINDOWS) && defined(HOST_ARCH_IA32)
+void ClobberAndCall(void (*fn)()) {
+  fn();
+}
+#else
+extern "C" void ClobberAndCall(void (*fn)());
+#endif
+
+DART_EXPORT int TestGC(void (*do_gc)()) {
+  ClobberAndCall(do_gc);
+  return 0;
+}
+
 struct CallbackTestData {
   int success;
   void (*callback)();
diff --git a/runtime/bin/ffi_test/ffi_test_functions_helpers.S b/runtime/bin/ffi_test/ffi_test_functions_helpers.S
new file mode 100644
index 0000000..f78ef2a
--- /dev/null
+++ b/runtime/bin/ffi_test/ffi_test_functions_helpers.S
@@ -0,0 +1,98 @@
+#if defined(_M_X64) || defined(__x86_64__) /* HOST_ARCH_X64 */
+
+.intel_syntax noprefix
+.text
+
+#if defined(__linux__) || defined(__FreeBSD__) /* HOST_OS_LINUX */
+.globl ClobberAndCall
+.type ClobberAndCall, @function
+ClobberAndCall:
+#else /* HOST_OS_MACOS */
+.globl _ClobberAndCall
+_ClobberAndCall:
+#endif
+
+/* Clobber some significant registers and call the nullary function which is
+   passed in as the first argument. */
+
+/* Pool pointer register. */
+push r15
+mov r15, 1
+
+/* Thread register. */
+push r14
+mov r14, 1
+
+/* Code register. */
+push r12
+mov r12, 1
+
+/* Arguments descriptor register. */
+push r10
+mov r10, 1
+
+call rdi
+
+pop r10
+pop r12
+pop r14
+pop r15
+
+ret
+
+#elif defined(_M_IX86) || defined(__i386__)  /* HOST_ARCH_IA32 */
+
+#elif defined(__aarch64__) /* HOST_ARCH_ARM64 */
+
+.text
+.global ClobberAndCall
+.type ClobberAndCall, %function
+ClobberAndCall:
+
+/* Save link register register and thread register. */
+stp lr, x26, [sp, #-16]!
+mov lr, #1
+mov x26, #1
+
+/* Arguments descriptor register isn't callee-saved. */
+mov x4, #1
+
+/* Pool pointer register and code register. */
+stp x24, x27, [sp, #-16]!
+
+mov x24, #1
+mov x27, #1
+
+blr x0
+
+ldp x24, x27, [sp], #16
+ldp lr, x26, [sp], #16
+
+blr lr
+
+#elif defined(__ARMEL__)  /* HOST_ARCH_ARM */
+
+.text
+.global ClobberAndCall
+.type ClobberAndCall, %function
+ClobberAndCall:
+
+stmdb sp!,{r4, r5, r6, r10, lr}
+
+/* Arguments descriptor register. */
+mov r4, #1
+
+/* Pool pointer register. */
+mov r5, #1
+
+/* Code pointer register. */
+mov r6, #1
+
+/* Thread register. */
+mov r10, #1
+
+blx r0
+
+ldm sp!,{r4, r5, r6, r10, pc}
+
+#endif
diff --git a/runtime/bin/socket_patch.dart b/runtime/bin/socket_patch.dart
index 4a87275..9bb1b94 100644
--- a/runtime/bin/socket_patch.dart
+++ b/runtime/bin/socket_patch.dart
@@ -144,7 +144,7 @@
 
   String get host => _host != null ? _host : address;
 
-  List<int> get rawAddress => new Uint8List.fromList(_in_addr);
+  Uint8List get rawAddress => new Uint8List.fromList(_in_addr);
 
   bool get isLoopback {
     switch (type) {
@@ -665,7 +665,7 @@
   String get _serviceTypePath => throw new UnimplementedError();
   String get _serviceTypeName => throw new UnimplementedError();
 
-  List<int> read(int len) {
+  Uint8List read(int len) {
     if (len != null && len <= 0) {
       throw new ArgumentError("Illegal length $len");
     }
@@ -1177,15 +1177,15 @@
   nativeRecvFrom() native "Socket_RecvFrom";
   nativeWrite(List<int> buffer, int offset, int bytes)
       native "Socket_WriteList";
-  nativeSendTo(List<int> buffer, int offset, int bytes, List<int> address,
+  nativeSendTo(List<int> buffer, int offset, int bytes, Uint8List address,
       int port) native "Socket_SendTo";
-  nativeCreateConnect(List<int> addr, int port) native "Socket_CreateConnect";
-  nativeCreateBindConnect(List<int> addr, int port, List<int> sourceAddr)
+  nativeCreateConnect(Uint8List addr, int port) native "Socket_CreateConnect";
+  nativeCreateBindConnect(Uint8List addr, int port, Uint8List sourceAddr)
       native "Socket_CreateBindConnect";
   bool isBindError(int errorNumber) native "SocketBase_IsBindError";
-  nativeCreateBindListen(List<int> addr, int port, int backlog, bool v6Only,
+  nativeCreateBindListen(Uint8List addr, int port, int backlog, bool v6Only,
       bool shared) native "ServerSocket_CreateBindListen";
-  nativeCreateBindDatagram(List<int> addr, int port, bool reuseAddress,
+  nativeCreateBindDatagram(Uint8List addr, int port, bool reuseAddress,
       bool reusePort, int ttl) native "Socket_CreateBindDatagram";
   nativeAccept(_NativeSocket socket) native "ServerSocket_Accept";
   int nativeGetPort() native "Socket_GetPort";
@@ -1199,9 +1199,9 @@
       native "Socket_SetOption";
   OSError nativeSetRawOption(int level, int option, Uint8List data)
       native "Socket_SetRawOption";
-  OSError nativeJoinMulticast(List<int> addr, List<int> interfaceAddr,
+  OSError nativeJoinMulticast(Uint8List addr, Uint8List interfaceAddr,
       int interfaceIndex) native "Socket_JoinMulticast";
-  bool nativeLeaveMulticast(List<int> addr, List<int> interfaceAddr,
+  bool nativeLeaveMulticast(Uint8List addr, Uint8List interfaceAddr,
       int interfaceIndex) native "Socket_LeaveMulticast";
 }
 
@@ -1378,7 +1378,7 @@
 
   int available() => _socket.available;
 
-  List<int> read([int len]) {
+  Uint8List read([int len]) {
     if (_isMacOSTerminalInput) {
       var available = this.available();
       if (available == 0) return null;
@@ -1985,6 +1985,6 @@
 
 @pragma("vm:entry-point", "call")
 Datagram _makeDatagram(
-    List<int> data, String address, List<int> in_addr, int port) {
+    List<int> data, String address, Uint8List in_addr, int port) {
   return new Datagram(data, new _InternetAddress(address, null, in_addr), port);
 }
diff --git a/runtime/bin/vmservice/server.dart b/runtime/bin/vmservice/server.dart
index b003c6a..ec77ddc 100644
--- a/runtime/bin/vmservice/server.dart
+++ b/runtime/bin/vmservice/server.dart
@@ -300,7 +300,12 @@
       try {
         result = await _service.devfs.handlePutStream(
             fsName, fsPath, fsUri, request.transform(GZIP.decoder));
-      } catch (e) {/* ignore */}
+      } catch (e) {
+        request.response.statusCode = HttpStatus.internalServerError;
+        request.response.write(e);
+        request.response.close();
+        return;
+      }
 
       if (result != null) {
         request.response.headers.contentType =
diff --git a/runtime/docs/infra/coredumps.md b/runtime/docs/infra/coredumps.md
index e24a3b6..3e1e2d5 100644
--- a/runtime/docs/infra/coredumps.md
+++ b/runtime/docs/infra/coredumps.md
@@ -124,9 +124,9 @@
 Implementation of crash dump archiving consists of two parts:
 
 * Code in the testing framework (`UnexpectedCrashLogger` in
-`tools/testing/dart/test_progress.dart`) that detects unexpected crashes, that
-is situations when test _unexpectedly_ completes with a `Crash` status. In this
-case it logs a line about this crash into `unexpected-crashes` file in the
+`pkg/test_runner/lib/src/test_progress.dart`) that detects unexpected crashes,
+that is situations when test _unexpectedly_ completes with a `Crash` status. In
+this case it logs a line about this crash into `unexpected-crashes` file in the
 root of the SDK checkout. This line has format `test,pid,binary` and identifies
 which test crashed, what was the process id and which binary crashed.
 * Code in the Python wrapper around testing framework (see `CoreDumpArchiver`
@@ -202,7 +202,7 @@
 
 **Note: `DART_CRASHPAD_CRASHES_DIR` is set by `WindowsCoreDumpEnabler` in
 `tools/utils.py`, while `DART_CRASHPAD_HANDLER` is set by `TestSuite` in
-`tools/testing/dart/test_suite.dart`.**
+`pkg/test_runner/lib/src/test_suite.dart`.**
 
 **Note: Crashpad is optimized for end-user crash reporting use case and does not write full crash dumps.**
 
diff --git a/runtime/include/dart_tools_api.h b/runtime/include/dart_tools_api.h
index 03b38aa..b4985bf 100644
--- a/runtime/include/dart_tools_api.h
+++ b/runtime/include/dart_tools_api.h
@@ -301,7 +301,8 @@
 
 /**
  * Returns a timestamp in microseconds. This timestamp is suitable for
- * passing into the timeline system.
+ * passing into the timeline system, and uses the same monotonic clock
+ * as dart:developer's Timeline.now.
  *
  * \return A timestamp that can be passed to the timeline system.
  */
diff --git a/runtime/lib/core_patch.dart b/runtime/lib/core_patch.dart
index 7b1876d6..c095178 100644
--- a/runtime/lib/core_patch.dart
+++ b/runtime/lib/core_patch.dart
@@ -94,14 +94,6 @@
   bool _equalToInteger(int other);
 }
 
-// The members of this class are cloned and added to each class that
-// represents an enum type.
-class _EnumHelper {
-  String _name;
-  String toString() => _name;
-  int get hashCode => _name.hashCode;
-}
-
 // _SyncIterable and _syncIterator are used by the compiler to
 // implement sync* generator functions. A sync* generator allocates
 // and returns a new _SyncIterable object.
diff --git a/runtime/lib/ffi.cc b/runtime/lib/ffi.cc
index 5dd4367..f2ac373 100644
--- a/runtime/lib/ffi.cc
+++ b/runtime/lib/ffi.cc
@@ -5,6 +5,7 @@
 #include "lib/ffi.h"
 
 #include "include/dart_api.h"
+#include "platform/globals.h"
 #include "vm/bootstrap_natives.h"
 #include "vm/class_finalizer.h"
 #include "vm/compiler/assembler/assembler.h"
@@ -677,6 +678,10 @@
   data_[kOffsetNumStackSlots] = num_args;
 }
 
+void FfiMarshalledArguments::SetAlignmentMask(uint64_t alignment_mask) const {
+  data_[kOffsetAlignmentMask] = alignment_mask;
+}
+
 intptr_t FfiMarshalledArguments::GetNumStackSlots() const {
   return data_[kOffsetNumStackSlots];
 }
@@ -691,6 +696,7 @@
     const compiler::ffi::FfiSignatureDescriptor& signature,
     const uint64_t* arg_values) {
   const intptr_t num_stack_slots = signature.num_stack_slots();
+  const uint64_t alignment_mask = ~(OS::ActivationFrameAlignment() - 1);
   const intptr_t size =
       FfiMarshalledArguments::kOffsetStackSlotValues + num_stack_slots;
   uint64_t* data = Thread::Current()->GetFfiMarshalledArguments(size);
@@ -699,6 +705,7 @@
   descr.SetFunctionAddress(arg_values[compiler::ffi::kFunctionAddressRegister]);
   const intptr_t num_args = signature.length();
   descr.SetNumStackSlots(num_stack_slots);
+  descr.SetAlignmentMask(alignment_mask);
   for (int i = 0; i < num_args; i++) {
     uint64_t arg_value = arg_values[compiler::ffi::kFirstArgumentRegister + i];
     HostLocation loc = signature.LocationAt(i);
@@ -737,10 +744,12 @@
                  RegisterNames::FpuRegisterName(
                      host::CallingConventions::FpuArgumentRegisters[i]));
   }
-  const intptr_t index = kOffsetNumStackSlots;
-  const intptr_t num_stack_slots = data_[index];
-  OS::PrintErr("  %02" Pd " 0x%" Pp " (number of stack slots)\n", index,
-               num_stack_slots);
+  const intptr_t alignment_mask = data_[kOffsetAlignmentMask];
+  OS::PrintErr("  %02" Pd " 0x%" Pp " (stack alignment mask)\n",
+               kOffsetAlignmentMask, alignment_mask);
+  const intptr_t num_stack_slots = data_[kOffsetNumStackSlots];
+  OS::PrintErr("  %02" Pd " 0x%" Pp " (number of stack slots)\n",
+               kOffsetNumStackSlots, num_stack_slots);
   for (intptr_t i = 0; i < num_stack_slots; i++) {
     const intptr_t index = kOffsetStackSlotValues + i;
     OS::PrintErr("  %02" Pd " 0x%016" Px64 " (stack slot %" Pd ")\n", index,
diff --git a/runtime/lib/ffi.h b/runtime/lib/ffi.h
index 00c527f..4cfa855 100644
--- a/runtime/lib/ffi.h
+++ b/runtime/lib/ffi.h
@@ -47,6 +47,7 @@
   void SetFunctionAddress(uint64_t value) const;
   void SetRegister(::dart::host::Register reg, uint64_t value) const;
   void SetFpuRegister(::dart::host::FpuRegister reg, uint64_t value) const;
+  void SetAlignmentMask(uint64_t kOffsetAlignmentMask) const;
   void SetNumStackSlots(intptr_t num_args) const;
   intptr_t GetNumStackSlots() const;
   void SetStackSlotValue(intptr_t index, uint64_t value) const;
@@ -59,8 +60,9 @@
   static const intptr_t kOffsetRegisters = 1;
   static const intptr_t kOffsetFpuRegisters =
       kOffsetRegisters + ::dart::host::CallingConventions::kNumArgRegs;
-  static const intptr_t kOffsetNumStackSlots =
+  static const intptr_t kOffsetAlignmentMask =
       kOffsetFpuRegisters + ::dart::host::CallingConventions::kNumFpuArgRegs;
+  static const intptr_t kOffsetNumStackSlots = kOffsetAlignmentMask + 1;
   static const intptr_t kOffsetStackSlotValues = kOffsetNumStackSlots + 1;
 
   static const intptr_t kOffsetIntResult = 0;
diff --git a/runtime/lib/function.dart b/runtime/lib/function.dart
index ae3902a..1c99ca2 100644
--- a/runtime/lib/function.dart
+++ b/runtime/lib/function.dart
@@ -6,6 +6,10 @@
 
 @pragma("vm:entry-point")
 class _Closure implements Function {
+  factory _Closure._uninstantiable() {
+    throw "Unreachable";
+  }
+
   bool operator ==(Object other) native "Closure_equals";
 
   int get hashCode {
diff --git a/runtime/lib/integers.dart b/runtime/lib/integers.dart
index 2289012..3a01913 100644
--- a/runtime/lib/integers.dart
+++ b/runtime/lib/integers.dart
@@ -480,8 +480,9 @@
 @pragma("vm:entry-point")
 class _Smi extends _IntegerImplementation {
   factory _Smi._uninstantiable() {
-    throw new UnsupportedError("_Smi can only be allocated by the VM");
+    throw "Unreachable";
   }
+
   int get hashCode => this;
   int get _identityHashCode => this;
   @pragma("vm:exact-result-type", "dart:core#_Smi")
@@ -682,8 +683,9 @@
 @pragma("vm:entry-point")
 class _Mint extends _IntegerImplementation {
   factory _Mint._uninstantiable() {
-    throw new UnsupportedError("_Mint can only be allocated by the VM");
+    throw "Unreachable";
   }
+
   int get hashCode => this;
   int get _identityHashCode => this;
   @pragma("vm:non-nullable-result-type")
diff --git a/runtime/lib/isolate.cc b/runtime/lib/isolate.cc
index 84c2aed..436e895 100644
--- a/runtime/lib/isolate.cc
+++ b/runtime/lib/isolate.cc
@@ -2,6 +2,9 @@
 // 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.
 
+#include <memory>
+#include <utility>
+
 #include "include/dart_native_api.h"
 #include "platform/assert.h"
 #include "platform/unicode.h"
@@ -113,8 +116,9 @@
 
 class SpawnIsolateTask : public ThreadPool::Task {
  public:
-  SpawnIsolateTask(Isolate* parent_isolate, IsolateSpawnState* state)
-      : parent_isolate_(parent_isolate), state_(state) {
+  SpawnIsolateTask(Isolate* parent_isolate,
+                   std::unique_ptr<IsolateSpawnState> state)
+      : parent_isolate_(parent_isolate), state_(std::move(state)) {
     parent_isolate->IncrementSpawnCount();
   }
 
@@ -131,8 +135,6 @@
     if (callback == NULL) {
       ReportError(
           "Isolate spawn is not supported by this Dart implementation\n");
-      delete state_;
-      state_ = NULL;
       return;
     }
 
@@ -149,8 +151,6 @@
     parent_isolate_ = nullptr;
     if (isolate == NULL) {
       ReportError(error);
-      delete state_;
-      state_ = NULL;
       free(error);
       return;
     }
@@ -162,8 +162,7 @@
     }
     MutexLocker ml(isolate->mutex());
     state_->set_isolate(isolate);
-    isolate->set_spawn_state(state_);
-    state_ = NULL;
+    isolate->set_spawn_state(std::move(state_));
     if (isolate->is_runnable()) {
       isolate->Run();
     }
@@ -181,7 +180,7 @@
   }
 
   Isolate* parent_isolate_;
-  IsolateSpawnState* state_;
+  std::unique_ptr<IsolateSpawnState> state_;
 
   DISALLOW_COPY_AND_ASSIGN(SpawnIsolateTask);
 };
@@ -238,23 +237,15 @@
       const char* utf8_debug_name =
           debugName.IsNull() ? NULL : String2UTF8(debugName);
 
-      IsolateSpawnState* state = new IsolateSpawnState(
+      std::unique_ptr<IsolateSpawnState> state(new IsolateSpawnState(
           port.Id(), isolate->origin_id(), String2UTF8(script_uri), func,
           &message_buffer, utf8_package_config, paused.value(), fatal_errors,
-          on_exit_port, on_error_port, utf8_debug_name);
+          on_exit_port, on_error_port, utf8_debug_name));
 
       // Since this is a call to Isolate.spawn, copy the parent isolate's code.
       state->isolate_flags()->copy_parent_code = true;
 
-      ThreadPool::Task* spawn_task = new SpawnIsolateTask(isolate, state);
-
-      if (!Dart::thread_pool()->Run(spawn_task)) {
-        // Running on the thread pool failed. Clean up everything.
-        delete state;
-        state = NULL;
-        delete spawn_task;
-        spawn_task = NULL;
-      }
+      Dart::thread_pool()->Run<SpawnIsolateTask>(isolate, std::move(state));
       return Object::null();
     }
   }
@@ -360,10 +351,10 @@
   const char* utf8_debug_name =
       debugName.IsNull() ? NULL : String2UTF8(debugName);
 
-  IsolateSpawnState* state = new IsolateSpawnState(
+  std::unique_ptr<IsolateSpawnState> state(new IsolateSpawnState(
       port.Id(), canonical_uri, utf8_package_config, &arguments_buffer,
       &message_buffer, paused.value(), fatal_errors, on_exit_port,
-      on_error_port, utf8_debug_name);
+      on_error_port, utf8_debug_name));
 
   // If we were passed a value then override the default flags state for
   // checked mode.
@@ -375,15 +366,7 @@
   // Since this is a call to Isolate.spawnUri, don't copy the parent's code.
   state->isolate_flags()->copy_parent_code = false;
 
-  ThreadPool::Task* spawn_task = new SpawnIsolateTask(isolate, state);
-
-  if (!Dart::thread_pool()->Run(spawn_task)) {
-    // Running on the thread pool failed. Clean up everything.
-    delete state;
-    state = NULL;
-    delete spawn_task;
-    spawn_task = NULL;
-  }
+  Dart::thread_pool()->Run<SpawnIsolateTask>(isolate, std::move(state));
   return Object::null();
 }
 
diff --git a/runtime/lib/lib_prefix.dart b/runtime/lib/lib_prefix.dart
index 483a660..fe12660 100644
--- a/runtime/lib/lib_prefix.dart
+++ b/runtime/lib/lib_prefix.dart
@@ -7,6 +7,10 @@
 // This type corresponds to the VM-internal class LibraryPrefix.
 @pragma("vm:entry-point")
 class _LibraryPrefix {
+  factory _LibraryPrefix._uninstantiable() {
+    throw "Unreachable";
+  }
+
   bool _load() native "LibraryPrefix_load";
   Object _loadError() native "LibraryPrefix_loadError";
   bool isLoaded() native "LibraryPrefix_isLoaded";
diff --git a/runtime/lib/mirror_reference.dart b/runtime/lib/mirror_reference.dart
index ca21b5b..d203953 100644
--- a/runtime/lib/mirror_reference.dart
+++ b/runtime/lib/mirror_reference.dart
@@ -7,7 +7,7 @@
 @pragma("vm:entry-point")
 class _MirrorReference {
   factory _MirrorReference._uninstantiable() {
-    throw new UnsupportedError("class _MirrorReference cannot be instantiated");
+    throw "Unreachable";
   }
 
   bool operator ==(other) native "MirrorReference_equals";
diff --git a/runtime/lib/regexp_patch.dart b/runtime/lib/regexp_patch.dart
index 35a13b1..7621bb0 100644
--- a/runtime/lib/regexp_patch.dart
+++ b/runtime/lib/regexp_patch.dart
@@ -148,7 +148,7 @@
 }
 
 class _RegExpMatch implements RegExpMatch {
-  _RegExpMatch(this._regexp, this.input, this._match);
+  _RegExpMatch._(this._regexp, this.input, this._match);
 
   int get start => _start(0);
   int get end => _end(0);
@@ -222,7 +222,7 @@
     if (match == null) {
       return null;
     }
-    return new _RegExpMatch(this, str, match);
+    return new _RegExpMatch._(this, str, match);
   }
 
   Iterable<RegExpMatch> allMatches(String string, [int start = 0]) {
@@ -242,7 +242,7 @@
     }
     List<int> list = _ExecuteMatchSticky(string, start);
     if (list == null) return null;
-    return new _RegExpMatch(this, string, list);
+    return new _RegExpMatch._(this, string, list);
   }
 
   bool hasMatch(String str) {
@@ -379,7 +379,7 @@
     if (_nextIndex <= _str.length) {
       var match = _re._ExecuteMatch(_str, _nextIndex);
       if (match != null) {
-        _current = new _RegExpMatch(_re, _str, match);
+        _current = new _RegExpMatch._(_re, _str, match);
         _nextIndex = _current.end;
         if (_nextIndex == _current.start) {
           // Zero-width match. Advance by one more, unless the regexp
diff --git a/runtime/lib/string_patch.dart b/runtime/lib/string_patch.dart
index 1099263..52d27db 100644
--- a/runtime/lib/string_patch.dart
+++ b/runtime/lib/string_patch.dart
@@ -945,10 +945,7 @@
 
 @pragma("vm:entry-point")
 class _OneByteString extends _StringBase {
-  factory _OneByteString._uninstantiable() {
-    throw new UnsupportedError(
-        "_OneByteString can only be allocated by the VM");
-  }
+  factory _OneByteString._uninstantiable() { throw "Unreachable"; }
 
   @pragma("vm:exact-result-type", "dart:core#_Smi")
   int get hashCode native "String_getHashCode";
@@ -1254,10 +1251,7 @@
 
 @pragma("vm:entry-point")
 class _TwoByteString extends _StringBase {
-  factory _TwoByteString._uninstantiable() {
-    throw new UnsupportedError(
-        "_TwoByteString can only be allocated by the VM");
-  }
+  factory _TwoByteString._uninstantiable() { throw "Unreachable"; }
 
   static String _allocateFromTwoByteList(List<int> list, int start, int end)
       native "TwoByteString_allocateFromTwoByteList";
@@ -1277,10 +1271,7 @@
 
 @pragma("vm:entry-point")
 class _ExternalOneByteString extends _StringBase {
-  factory _ExternalOneByteString._uninstantiable() {
-    throw new UnsupportedError(
-        "_ExternalOneByteString can only be allocated by the VM");
-  }
+  factory _ExternalOneByteString._uninstantiable() { throw "Unreachable"; }
 
   bool _isWhitespace(int codeUnit) {
     return _StringBase._isOneByteWhitespace(codeUnit);
@@ -1296,10 +1287,7 @@
 
 @pragma("vm:entry-point")
 class _ExternalTwoByteString extends _StringBase {
-  factory _ExternalTwoByteString._uninstantiable() {
-    throw new UnsupportedError(
-        "_ExternalTwoByteString can only be allocated by the VM");
-  }
+  factory _ExternalTwoByteString._uninstantiable() { throw "Unreachable"; }
 
   bool _isWhitespace(int codeUnit) {
     return _StringBase._isTwoByteWhitespace(codeUnit);
diff --git a/runtime/lib/type_patch.dart b/runtime/lib/type_patch.dart
index a05ce70..35ce89f 100644
--- a/runtime/lib/type_patch.dart
+++ b/runtime/lib/type_patch.dart
@@ -14,14 +14,26 @@
 // Equivalent of RawType.
 @pragma("vm:entry-point")
 class _Type extends _AbstractType {
+  factory _Type._uninstantiable() {
+    throw "Unreachable";
+  }
+
   @pragma("vm:exact-result-type", "dart:core#_Smi")
   int get hashCode native "Type_getHashCode";
 }
 
 // Equivalent of RawTypeRef.
 @pragma("vm:entry-point")
-class _TypeRef extends _AbstractType {}
+class _TypeRef extends _AbstractType {
+  factory _TypeRef._uninstantiable() {
+    throw "Unreachable";
+  }
+}
 
 // Equivalent of RawTypeParameter.
 @pragma("vm:entry-point")
-class _TypeParameter extends _AbstractType {}
+class _TypeParameter extends _AbstractType {
+  factory _TypeParameter._uninstantiable() {
+    throw "Unreachable";
+  }
+}
diff --git a/runtime/lib/typed_data_patch.dart b/runtime/lib/typed_data_patch.dart
index 7af2181..f79cf3e 100644
--- a/runtime/lib/typed_data_patch.dart
+++ b/runtime/lib/typed_data_patch.dart
@@ -40,14 +40,14 @@
   factory ByteData(int length) {
     final list = new Uint8List(length) as _TypedList;
     _rangeCheck(list.lengthInBytes, 0, length);
-    return new _ByteDataView(list, 0, length);
+    return new _ByteDataView._(list, 0, length);
   }
 
   // Called directly from C code.
   @pragma("vm:entry-point")
   factory ByteData._view(_TypedList typedData, int offsetInBytes, int length) {
     _rangeCheck(typedData.lengthInBytes, offsetInBytes, length);
-    return new _ByteDataView(typedData, offsetInBytes, length);
+    return new _ByteDataView._(typedData, offsetInBytes, length);
   }
 }
 
@@ -111,12 +111,13 @@
       int startFromInBytes, int toCid, int fromCid) native "TypedData_setRange";
 }
 
-abstract class _IntListMixin implements List<int> {
+abstract class _IntListMixin<SpawnedType extends List<int>>
+    implements List<int> {
   int get elementSizeInBytes;
   int get offsetInBytes;
   _ByteBuffer get buffer;
 
-  List<int> _createList(int length);
+  SpawnedType _createList(int length);
 
   Iterable<T> whereType<T>() => new WhereTypeIterable<T>(this);
 
@@ -441,10 +442,10 @@
     throw IterableElementError.tooMany();
   }
 
-  List<int> sublist(int start, [int end]) {
+  SpawnedType sublist(int start, [int end]) {
     end = RangeError.checkValidRange(start, end, this.length);
     var length = end - start;
-    List<int> result = _createList(length);
+    SpawnedType result = _createList(length);
     result.setRange(0, length, this, start);
     return result;
   }
@@ -462,12 +463,13 @@
   }
 }
 
-abstract class _DoubleListMixin implements List<double> {
+abstract class _DoubleListMixin<SpawnedType extends List<double>>
+    implements List<double> {
   int get elementSizeInBytes;
   int get offsetInBytes;
   _ByteBuffer get buffer;
 
-  List<double> _createList(int length);
+  SpawnedType _createList(int length);
 
   Iterable<T> whereType<T>() => new WhereTypeIterable<T>(this);
 
@@ -795,10 +797,10 @@
     throw IterableElementError.tooMany();
   }
 
-  List<double> sublist(int start, [int end]) {
+  SpawnedType sublist(int start, [int end]) {
     end = RangeError.checkValidRange(start, end, this.length);
     var length = end - start;
-    List<double> result = _createList(length);
+    SpawnedType result = _createList(length);
     result.setRange(0, length, this, start);
     return result;
   }
@@ -821,7 +823,7 @@
   int get offsetInBytes;
   _ByteBuffer get buffer;
 
-  List<Float32x4> _createList(int length);
+  Float32x4List _createList(int length);
 
   Iterable<T> whereType<T>() => new WhereTypeIterable<T>(this);
 
@@ -1153,10 +1155,10 @@
     throw IterableElementError.tooMany();
   }
 
-  List<Float32x4> sublist(int start, [int end]) {
+  Float32x4List sublist(int start, [int end]) {
     end = RangeError.checkValidRange(start, end, this.length);
     var length = end - start;
-    List<Float32x4> result = _createList(length);
+    Float32x4List result = _createList(length);
     result.setRange(0, length, this, start);
     return result;
   }
@@ -1179,7 +1181,7 @@
   int get offsetInBytes;
   _ByteBuffer get buffer;
 
-  List<Int32x4> _createList(int length);
+  Int32x4List _createList(int length);
 
   Iterable<T> whereType<T>() => new WhereTypeIterable<T>(this);
 
@@ -1510,10 +1512,10 @@
     throw IterableElementError.tooMany();
   }
 
-  List<Int32x4> sublist(int start, [int end]) {
+  Int32x4List sublist(int start, [int end]) {
     end = RangeError.checkValidRange(start, end, this.length);
     var length = end - start;
-    List<Int32x4> result = _createList(length);
+    Int32x4List result = _createList(length);
     result.setRange(0, length, this, start);
     return result;
   }
@@ -1536,7 +1538,7 @@
   int get offsetInBytes;
   _ByteBuffer get buffer;
 
-  List<Float64x2> _createList(int length);
+  Float64x2List _createList(int length);
 
   Iterable<T> whereType<T>() => new WhereTypeIterable<T>(this);
 
@@ -1868,10 +1870,10 @@
     throw IterableElementError.tooMany();
   }
 
-  List<Float64x2> sublist(int start, [int end]) {
+  Float64x2List sublist(int start, [int end]) {
     end = RangeError.checkValidRange(start, end, this.length);
     var length = end - start;
-    List<Float64x2> result = _createList(length);
+    Float64x2List result = _createList(length);
     result.setRange(0, length, this, start);
     return result;
   }
@@ -1907,14 +1909,14 @@
   ByteData asByteData([int offsetInBytes = 0, int length]) {
     length ??= this.lengthInBytes - offsetInBytes;
     _rangeCheck(this._data.lengthInBytes, offsetInBytes, length);
-    return new _ByteDataView(this._data, offsetInBytes, length);
+    return new _ByteDataView._(this._data, offsetInBytes, length);
   }
 
   Int8List asInt8List([int offsetInBytes = 0, int length]) {
     length ??= (this.lengthInBytes - offsetInBytes) ~/ Int8List.bytesPerElement;
     _rangeCheck(
         this.lengthInBytes, offsetInBytes, length * Int8List.bytesPerElement);
-    return new _Int8ArrayView(this._data, offsetInBytes, length);
+    return new _Int8ArrayView._(this._data, offsetInBytes, length);
   }
 
   Uint8List asUint8List([int offsetInBytes = 0, int length]) {
@@ -1922,7 +1924,7 @@
         (this.lengthInBytes - offsetInBytes) ~/ Uint8List.bytesPerElement;
     _rangeCheck(
         this.lengthInBytes, offsetInBytes, length * Uint8List.bytesPerElement);
-    return new _Uint8ArrayView(this._data, offsetInBytes, length);
+    return new _Uint8ArrayView._(this._data, offsetInBytes, length);
   }
 
   Uint8ClampedList asUint8ClampedList([int offsetInBytes = 0, int length]) {
@@ -1930,7 +1932,7 @@
         Uint8ClampedList.bytesPerElement;
     _rangeCheck(this.lengthInBytes, offsetInBytes,
         length * Uint8ClampedList.bytesPerElement);
-    return new _Uint8ClampedArrayView(this._data, offsetInBytes, length);
+    return new _Uint8ClampedArrayView._(this._data, offsetInBytes, length);
   }
 
   Int16List asInt16List([int offsetInBytes = 0, int length]) {
@@ -1939,7 +1941,7 @@
     _rangeCheck(
         this.lengthInBytes, offsetInBytes, length * Int16List.bytesPerElement);
     _offsetAlignmentCheck(offsetInBytes, Int16List.bytesPerElement);
-    return new _Int16ArrayView(this._data, offsetInBytes, length);
+    return new _Int16ArrayView._(this._data, offsetInBytes, length);
   }
 
   Uint16List asUint16List([int offsetInBytes = 0, int length]) {
@@ -1948,7 +1950,7 @@
     _rangeCheck(
         this.lengthInBytes, offsetInBytes, length * Uint16List.bytesPerElement);
     _offsetAlignmentCheck(offsetInBytes, Uint16List.bytesPerElement);
-    return new _Uint16ArrayView(this._data, offsetInBytes, length);
+    return new _Uint16ArrayView._(this._data, offsetInBytes, length);
   }
 
   Int32List asInt32List([int offsetInBytes = 0, int length]) {
@@ -1957,7 +1959,7 @@
     _rangeCheck(
         this.lengthInBytes, offsetInBytes, length * Int32List.bytesPerElement);
     _offsetAlignmentCheck(offsetInBytes, Int32List.bytesPerElement);
-    return new _Int32ArrayView(this._data, offsetInBytes, length);
+    return new _Int32ArrayView._(this._data, offsetInBytes, length);
   }
 
   Uint32List asUint32List([int offsetInBytes = 0, int length]) {
@@ -1966,7 +1968,7 @@
     _rangeCheck(
         this.lengthInBytes, offsetInBytes, length * Uint32List.bytesPerElement);
     _offsetAlignmentCheck(offsetInBytes, Uint32List.bytesPerElement);
-    return new _Uint32ArrayView(this._data, offsetInBytes, length);
+    return new _Uint32ArrayView._(this._data, offsetInBytes, length);
   }
 
   Int64List asInt64List([int offsetInBytes = 0, int length]) {
@@ -1975,7 +1977,7 @@
     _rangeCheck(
         this.lengthInBytes, offsetInBytes, length * Int64List.bytesPerElement);
     _offsetAlignmentCheck(offsetInBytes, Int64List.bytesPerElement);
-    return new _Int64ArrayView(this._data, offsetInBytes, length);
+    return new _Int64ArrayView._(this._data, offsetInBytes, length);
   }
 
   Uint64List asUint64List([int offsetInBytes = 0, int length]) {
@@ -1984,7 +1986,7 @@
     _rangeCheck(
         this.lengthInBytes, offsetInBytes, length * Uint64List.bytesPerElement);
     _offsetAlignmentCheck(offsetInBytes, Uint64List.bytesPerElement);
-    return new _Uint64ArrayView(this._data, offsetInBytes, length);
+    return new _Uint64ArrayView._(this._data, offsetInBytes, length);
   }
 
   Float32List asFloat32List([int offsetInBytes = 0, int length]) {
@@ -1993,7 +1995,7 @@
     _rangeCheck(this.lengthInBytes, offsetInBytes,
         length * Float32List.bytesPerElement);
     _offsetAlignmentCheck(offsetInBytes, Float32List.bytesPerElement);
-    return new _Float32ArrayView(this._data, offsetInBytes, length);
+    return new _Float32ArrayView._(this._data, offsetInBytes, length);
   }
 
   Float64List asFloat64List([int offsetInBytes = 0, int length]) {
@@ -2002,7 +2004,7 @@
     _rangeCheck(this.lengthInBytes, offsetInBytes,
         length * Float64List.bytesPerElement);
     _offsetAlignmentCheck(offsetInBytes, Float64List.bytesPerElement);
-    return new _Float64ArrayView(this._data, offsetInBytes, length);
+    return new _Float64ArrayView._(this._data, offsetInBytes, length);
   }
 
   Float32x4List asFloat32x4List([int offsetInBytes = 0, int length]) {
@@ -2011,7 +2013,7 @@
     _rangeCheck(this.lengthInBytes, offsetInBytes,
         length * Float32x4List.bytesPerElement);
     _offsetAlignmentCheck(offsetInBytes, Float32x4List.bytesPerElement);
-    return new _Float32x4ArrayView(this._data, offsetInBytes, length);
+    return new _Float32x4ArrayView._(this._data, offsetInBytes, length);
   }
 
   Int32x4List asInt32x4List([int offsetInBytes = 0, int length]) {
@@ -2020,7 +2022,7 @@
     _rangeCheck(this.lengthInBytes, offsetInBytes,
         length * Int32x4List.bytesPerElement);
     _offsetAlignmentCheck(offsetInBytes, Int32x4List.bytesPerElement);
-    return new _Int32x4ArrayView(this._data, offsetInBytes, length);
+    return new _Int32x4ArrayView._(this._data, offsetInBytes, length);
   }
 
   Float64x2List asFloat64x2List([int offsetInBytes = 0, int length]) {
@@ -2029,7 +2031,7 @@
     _rangeCheck(this.lengthInBytes, offsetInBytes,
         length * Float64x2List.bytesPerElement);
     _offsetAlignmentCheck(offsetInBytes, Float64x2List.bytesPerElement);
-    return new _Float64x2ArrayView(this._data, offsetInBytes, length);
+    return new _Float64x2ArrayView._(this._data, offsetInBytes, length);
   }
 }
 
@@ -2138,7 +2140,13 @@
 }
 
 @pragma("vm:entry-point")
-class _Int8List extends _TypedList with _IntListMixin implements Int8List {
+class _Int8List extends _TypedList
+    with _IntListMixin<Int8List>
+    implements Int8List {
+  factory _Int8List._uninstantiable() {
+    throw "Unreachable";
+  }
+
   // Method(s) implementing List interface.
   @pragma("vm:exact-result-type", "dart:core#_Smi")
   int operator [](int index) {
@@ -2180,7 +2188,13 @@
 }
 
 @pragma("vm:entry-point")
-class _Uint8List extends _TypedList with _IntListMixin implements Uint8List {
+class _Uint8List extends _TypedList
+    with _IntListMixin<Uint8List>
+    implements Uint8List {
+  factory _Uint8List._uninstantiable() {
+    throw "Unreachable";
+  }
+
   // Methods implementing List interface.
   @pragma("vm:exact-result-type", "dart:core#_Smi")
   int operator [](int index) {
@@ -2223,8 +2237,12 @@
 
 @pragma("vm:entry-point")
 class _Uint8ClampedList extends _TypedList
-    with _IntListMixin
+    with _IntListMixin<Uint8ClampedList>
     implements Uint8ClampedList {
+  factory _Uint8ClampedList._uninstantiable() {
+    throw "Unreachable";
+  }
+
   // Methods implementing List interface.
   @pragma("vm:exact-result-type", "dart:core#_Smi")
   int operator [](int index) {
@@ -2266,7 +2284,13 @@
 }
 
 @pragma("vm:entry-point")
-class _Int16List extends _TypedList with _IntListMixin implements Int16List {
+class _Int16List extends _TypedList
+    with _IntListMixin<Int16List>
+    implements Int16List {
+  factory _Int16List._uninstantiable() {
+    throw "Unreachable";
+  }
+
   // Method(s) implementing List interface.
   @pragma("vm:exact-result-type", "dart:core#_Smi")
   int operator [](int index) {
@@ -2327,7 +2351,13 @@
 }
 
 @pragma("vm:entry-point")
-class _Uint16List extends _TypedList with _IntListMixin implements Uint16List {
+class _Uint16List extends _TypedList
+    with _IntListMixin<Uint16List>
+    implements Uint16List {
+  factory _Uint16List._uninstantiable() {
+    throw "Unreachable";
+  }
+
   // Method(s) implementing the List interface.
   @pragma("vm:exact-result-type", "dart:core#_Smi")
   int operator [](int index) {
@@ -2388,7 +2418,13 @@
 }
 
 @pragma("vm:entry-point")
-class _Int32List extends _TypedList with _IntListMixin implements Int32List {
+class _Int32List extends _TypedList
+    with _IntListMixin<Int32List>
+    implements Int32List {
+  factory _Int32List._uninstantiable() {
+    throw "Unreachable";
+  }
+
   // Method(s) implementing the List interface.
   int operator [](int index) {
     if (index < 0 || index >= length) {
@@ -2437,7 +2473,13 @@
 }
 
 @pragma("vm:entry-point")
-class _Uint32List extends _TypedList with _IntListMixin implements Uint32List {
+class _Uint32List extends _TypedList
+    with _IntListMixin<Uint32List>
+    implements Uint32List {
+  factory _Uint32List._uninstantiable() {
+    throw "Unreachable";
+  }
+
   // Method(s) implementing the List interface.
   int operator [](int index) {
     if (index < 0 || index >= length) {
@@ -2486,7 +2528,13 @@
 }
 
 @pragma("vm:entry-point")
-class _Int64List extends _TypedList with _IntListMixin implements Int64List {
+class _Int64List extends _TypedList
+    with _IntListMixin<Int64List>
+    implements Int64List {
+  factory _Int64List._uninstantiable() {
+    throw "Unreachable";
+  }
+
   // Method(s) implementing the List interface.
   int operator [](int index) {
     if (index < 0 || index >= length) {
@@ -2535,7 +2583,13 @@
 }
 
 @pragma("vm:entry-point")
-class _Uint64List extends _TypedList with _IntListMixin implements Uint64List {
+class _Uint64List extends _TypedList
+    with _IntListMixin<Uint64List>
+    implements Uint64List {
+  factory _Uint64List._uninstantiable() {
+    throw "Unreachable";
+  }
+
   // Method(s) implementing the List interface.
   int operator [](int index) {
     if (index < 0 || index >= length) {
@@ -2585,8 +2639,12 @@
 
 @pragma("vm:entry-point")
 class _Float32List extends _TypedList
-    with _DoubleListMixin
+    with _DoubleListMixin<Float32List>
     implements Float32List {
+  factory _Float32List._uninstantiable() {
+    throw "Unreachable";
+  }
+
   // Method(s) implementing the List interface.
   @pragma("vm:exact-result-type", "dart:core#_Double")
   double operator [](int index) {
@@ -2637,8 +2695,12 @@
 
 @pragma("vm:entry-point")
 class _Float64List extends _TypedList
-    with _DoubleListMixin
+    with _DoubleListMixin<Float64List>
     implements Float64List {
+  factory _Float64List._uninstantiable() {
+    throw "Unreachable";
+  }
+
   // Method(s) implementing the List interface.
   @pragma("vm:exact-result-type", "dart:core#_Double")
   double operator [](int index) {
@@ -2691,6 +2753,10 @@
 class _Float32x4List extends _TypedList
     with _Float32x4ListMixin
     implements Float32x4List {
+  factory _Float32x4List._uninstantiable() {
+    throw "Unreachable";
+  }
+
   @pragma("vm:exact-result-type", _Float32x4)
   Float32x4 operator [](int index) {
     if (index < 0 || index >= length) {
@@ -2742,6 +2808,10 @@
 class _Int32x4List extends _TypedList
     with _Int32x4ListMixin
     implements Int32x4List {
+  factory _Int32x4List._uninstantiable() {
+    throw "Unreachable";
+  }
+
   @pragma("vm:exact-result-type", _Int32x4)
   Int32x4 operator [](int index) {
     if (index < 0 || index >= length) {
@@ -2793,6 +2863,10 @@
 class _Float64x2List extends _TypedList
     with _Float64x2ListMixin
     implements Float64x2List {
+  factory _Float64x2List._uninstantiable() {
+    throw "Unreachable";
+  }
+
   @pragma("vm:exact-result-type", _Float64x2)
   Float64x2 operator [](int index) {
     if (index < 0 || index >= length) {
@@ -2829,8 +2903,12 @@
 
 @pragma("vm:entry-point")
 class _ExternalInt8Array extends _TypedList
-    with _IntListMixin
+    with _IntListMixin<Int8List>
     implements Int8List {
+  factory _ExternalInt8Array._uninstantiable() {
+    throw "Unreachable";
+  }
+
   // Method(s) implementing the List interface.
   int operator [](int index) {
     if (index < 0 || index >= length) {
@@ -2859,8 +2937,12 @@
 
 @pragma("vm:entry-point")
 class _ExternalUint8Array extends _TypedList
-    with _IntListMixin
+    with _IntListMixin<Uint8List>
     implements Uint8List {
+  factory _ExternalUint8Array._uninstantiable() {
+    throw "Unreachable";
+  }
+
   // Method(s) implementing the List interface.
   @pragma("vm:exact-result-type", "dart:core#_Smi")
   int operator [](int index) {
@@ -2890,8 +2972,12 @@
 
 @pragma("vm:entry-point")
 class _ExternalUint8ClampedArray extends _TypedList
-    with _IntListMixin
+    with _IntListMixin<Uint8ClampedList>
     implements Uint8ClampedList {
+  factory _ExternalUint8ClampedArray._uninstantiable() {
+    throw "Unreachable";
+  }
+
   // Method(s) implementing the List interface.
   @pragma("vm:exact-result-type", "dart:core#_Smi")
   int operator [](int index) {
@@ -2921,10 +3007,13 @@
 
 @pragma("vm:entry-point")
 class _ExternalInt16Array extends _TypedList
-    with _IntListMixin
+    with _IntListMixin<Int16List>
     implements Int16List {
-  // Method(s) implementing the List interface.
+  factory _ExternalInt16Array._uninstantiable() {
+    throw "Unreachable";
+  }
 
+  // Method(s) implementing the List interface.
   int operator [](int index) {
     if (index < 0 || index >= length) {
       throw new RangeError.index(index, this, "index");
@@ -2960,10 +3049,13 @@
 
 @pragma("vm:entry-point")
 class _ExternalUint16Array extends _TypedList
-    with _IntListMixin
+    with _IntListMixin<Uint16List>
     implements Uint16List {
-  // Method(s) implementing the List interface.
+  factory _ExternalUint16Array._uninstantiable() {
+    throw "Unreachable";
+  }
 
+  // Method(s) implementing the List interface.
   int operator [](int index) {
     if (index < 0 || index >= length) {
       throw new RangeError.index(index, this, "index");
@@ -2999,8 +3091,12 @@
 
 @pragma("vm:entry-point")
 class _ExternalInt32Array extends _TypedList
-    with _IntListMixin
+    with _IntListMixin<Int32List>
     implements Int32List {
+  factory _ExternalInt32Array._uninstantiable() {
+    throw "Unreachable";
+  }
+
   // Method(s) implementing the List interface.
   int operator [](int index) {
     if (index < 0 || index >= length) {
@@ -3037,10 +3133,13 @@
 
 @pragma("vm:entry-point")
 class _ExternalUint32Array extends _TypedList
-    with _IntListMixin
+    with _IntListMixin<Uint32List>
     implements Uint32List {
-  // Method(s) implementing the List interface.
+  factory _ExternalUint32Array._uninstantiable() {
+    throw "Unreachable";
+  }
 
+  // Method(s) implementing the List interface.
   int operator [](int index) {
     if (index < 0 || index >= length) {
       throw new RangeError.index(index, this, "index");
@@ -3076,10 +3175,13 @@
 
 @pragma("vm:entry-point")
 class _ExternalInt64Array extends _TypedList
-    with _IntListMixin
+    with _IntListMixin<Int64List>
     implements Int64List {
-  // Method(s) implementing the List interface.
+  factory _ExternalInt64Array._uninstantiable() {
+    throw "Unreachable";
+  }
 
+  // Method(s) implementing the List interface.
   int operator [](int index) {
     if (index < 0 || index >= length) {
       throw new RangeError.index(index, this, "index");
@@ -3115,10 +3217,13 @@
 
 @pragma("vm:entry-point")
 class _ExternalUint64Array extends _TypedList
-    with _IntListMixin
+    with _IntListMixin<Uint64List>
     implements Uint64List {
-  // Method(s) implementing the List interface.
+  factory _ExternalUint64Array._uninstantiable() {
+    throw "Unreachable";
+  }
 
+  // Method(s) implementing the List interface.
   int operator [](int index) {
     if (index < 0 || index >= length) {
       throw new RangeError.index(index, this, "index");
@@ -3154,10 +3259,13 @@
 
 @pragma("vm:entry-point")
 class _ExternalFloat32Array extends _TypedList
-    with _DoubleListMixin
+    with _DoubleListMixin<Float32List>
     implements Float32List {
-  // Method(s) implementing the List interface.
+  factory _ExternalFloat32Array._uninstantiable() {
+    throw "Unreachable";
+  }
 
+  // Method(s) implementing the List interface.
   double operator [](int index) {
     if (index < 0 || index >= length) {
       throw new RangeError.index(index, this, "index");
@@ -3193,10 +3301,13 @@
 
 @pragma("vm:entry-point")
 class _ExternalFloat64Array extends _TypedList
-    with _DoubleListMixin
+    with _DoubleListMixin<Float64List>
     implements Float64List {
-  // Method(s) implementing the List interface.
+  factory _ExternalFloat64Array._uninstantiable() {
+    throw "Unreachable";
+  }
 
+  // Method(s) implementing the List interface.
   double operator [](int index) {
     if (index < 0 || index >= length) {
       throw new RangeError.index(index, this, "index");
@@ -3234,8 +3345,11 @@
 class _ExternalFloat32x4Array extends _TypedList
     with _Float32x4ListMixin
     implements Float32x4List {
-  // Method(s) implementing the List interface.
+  factory _ExternalFloat32x4Array._uninstantiable() {
+    throw "Unreachable";
+  }
 
+  // Method(s) implementing the List interface.
   Float32x4 operator [](int index) {
     if (index < 0 || index >= length) {
       throw new RangeError.index(index, this, "index");
@@ -3273,8 +3387,11 @@
 class _ExternalInt32x4Array extends _TypedList
     with _Int32x4ListMixin
     implements Int32x4List {
-  // Method(s) implementing the List interface.
+  factory _ExternalInt32x4Array._uninstantiable() {
+    throw "Unreachable";
+  }
 
+  // Method(s) implementing the List interface.
   Int32x4 operator [](int index) {
     if (index < 0 || index >= length) {
       throw new RangeError.index(index, this, "index");
@@ -3312,6 +3429,10 @@
 class _ExternalFloat64x2Array extends _TypedList
     with _Float64x2ListMixin
     implements Float64x2List {
+  factory _ExternalFloat64x2Array._uninstantiable() {
+    throw "Unreachable";
+  }
+
   // Method(s) implementing the List interface.
   Float64x2 operator [](int index) {
     if (index < 0 || index >= length) {
@@ -3595,11 +3716,11 @@
 
 @pragma("vm:entry-point")
 class _Int8ArrayView extends _TypedListView
-    with _IntListMixin
+    with _IntListMixin<Int8List>
     implements Int8List {
   // Constructor.
   @pragma("vm:exact-result-type", _Int8ArrayView)
-  factory _Int8ArrayView(_TypedList buffer, int offsetInBytes, int length)
+  factory _Int8ArrayView._(_TypedList buffer, int offsetInBytes, int length)
       native "TypedDataView_Int8ArrayView_new";
 
   // Method(s) implementing List interface.
@@ -3632,11 +3753,11 @@
 
 @pragma("vm:entry-point")
 class _Uint8ArrayView extends _TypedListView
-    with _IntListMixin
+    with _IntListMixin<Uint8List>
     implements Uint8List {
   // Constructor.
   @pragma("vm:exact-result-type", _Uint8ArrayView)
-  factory _Uint8ArrayView(_TypedList buffer, int offsetInBytes, int length)
+  factory _Uint8ArrayView._(_TypedList buffer, int offsetInBytes, int length)
       native "TypedDataView_Uint8ArrayView_new";
 
   // Method(s) implementing List interface.
@@ -3669,11 +3790,11 @@
 
 @pragma("vm:entry-point")
 class _Uint8ClampedArrayView extends _TypedListView
-    with _IntListMixin
+    with _IntListMixin<Uint8ClampedList>
     implements Uint8ClampedList {
   // Constructor.
   @pragma("vm:exact-result-type", _Uint8ClampedArrayView)
-  factory _Uint8ClampedArrayView(_TypedList buffer, int offsetInBytes,
+  factory _Uint8ClampedArrayView._(_TypedList buffer, int offsetInBytes,
       int length) native "TypedDataView_Uint8ClampedArrayView_new";
 
   // Method(s) implementing List interface.
@@ -3706,11 +3827,11 @@
 
 @pragma("vm:entry-point")
 class _Int16ArrayView extends _TypedListView
-    with _IntListMixin
+    with _IntListMixin<Int16List>
     implements Int16List {
   // Constructor.
   @pragma("vm:exact-result-type", _Int16ArrayView)
-  factory _Int16ArrayView(_TypedList buffer, int offsetInBytes, int length)
+  factory _Int16ArrayView._(_TypedList buffer, int offsetInBytes, int length)
       native "TypedDataView_Int16ArrayView_new";
 
   // Method(s) implementing List interface.
@@ -3755,11 +3876,11 @@
 
 @pragma("vm:entry-point")
 class _Uint16ArrayView extends _TypedListView
-    with _IntListMixin
+    with _IntListMixin<Uint16List>
     implements Uint16List {
   // Constructor.
   @pragma("vm:exact-result-type", _Uint16ArrayView)
-  factory _Uint16ArrayView(_TypedList buffer, int offsetInBytes, int length)
+  factory _Uint16ArrayView._(_TypedList buffer, int offsetInBytes, int length)
       native "TypedDataView_Uint16ArrayView_new";
 
   // Method(s) implementing List interface.
@@ -3805,11 +3926,11 @@
 
 @pragma("vm:entry-point")
 class _Int32ArrayView extends _TypedListView
-    with _IntListMixin
+    with _IntListMixin<Int32List>
     implements Int32List {
   // Constructor.
   @pragma("vm:exact-result-type", _Int32ArrayView)
-  factory _Int32ArrayView(_TypedList buffer, int offsetInBytes, int length)
+  factory _Int32ArrayView._(_TypedList buffer, int offsetInBytes, int length)
       native "TypedDataView_Int32ArrayView_new";
 
   // Method(s) implementing List interface.
@@ -3842,11 +3963,11 @@
 
 @pragma("vm:entry-point")
 class _Uint32ArrayView extends _TypedListView
-    with _IntListMixin
+    with _IntListMixin<Uint32List>
     implements Uint32List {
   // Constructor.
   @pragma("vm:exact-result-type", _Uint32ArrayView)
-  factory _Uint32ArrayView(_TypedList buffer, int offsetInBytes, int length)
+  factory _Uint32ArrayView._(_TypedList buffer, int offsetInBytes, int length)
       native "TypedDataView_Uint32ArrayView_new";
 
   // Method(s) implementing List interface.
@@ -3879,11 +4000,11 @@
 
 @pragma("vm:entry-point")
 class _Int64ArrayView extends _TypedListView
-    with _IntListMixin
+    with _IntListMixin<Int64List>
     implements Int64List {
   // Constructor.
   @pragma("vm:exact-result-type", _Int64ArrayView)
-  factory _Int64ArrayView(_TypedList buffer, int offsetInBytes, int length)
+  factory _Int64ArrayView._(_TypedList buffer, int offsetInBytes, int length)
       native "TypedDataView_Int64ArrayView_new";
 
   // Method(s) implementing List interface.
@@ -3916,11 +4037,11 @@
 
 @pragma("vm:entry-point")
 class _Uint64ArrayView extends _TypedListView
-    with _IntListMixin
+    with _IntListMixin<Uint64List>
     implements Uint64List {
   // Constructor.
   @pragma("vm:exact-result-type", _Uint64ArrayView)
-  factory _Uint64ArrayView(_TypedList buffer, int offsetInBytes, int length)
+  factory _Uint64ArrayView._(_TypedList buffer, int offsetInBytes, int length)
       native "TypedDataView_Uint64ArrayView_new";
 
   // Method(s) implementing List interface.
@@ -3953,11 +4074,11 @@
 
 @pragma("vm:entry-point")
 class _Float32ArrayView extends _TypedListView
-    with _DoubleListMixin
+    with _DoubleListMixin<Float32List>
     implements Float32List {
   // Constructor.
   @pragma("vm:exact-result-type", _Float32ArrayView)
-  factory _Float32ArrayView(_TypedList buffer, int offsetInBytes, int length)
+  factory _Float32ArrayView._(_TypedList buffer, int offsetInBytes, int length)
       native "TypedDataView_Float32ArrayView_new";
 
   // Method(s) implementing List interface.
@@ -3990,11 +4111,11 @@
 
 @pragma("vm:entry-point")
 class _Float64ArrayView extends _TypedListView
-    with _DoubleListMixin
+    with _DoubleListMixin<Float64List>
     implements Float64List {
   // Constructor.
   @pragma("vm:exact-result-type", _Float64ArrayView)
-  factory _Float64ArrayView(_TypedList buffer, int offsetInBytes, int length)
+  factory _Float64ArrayView._(_TypedList buffer, int offsetInBytes, int length)
       native "TypedDataView_Float64ArrayView_new";
 
   // Method(s) implementing List interface.
@@ -4031,8 +4152,8 @@
     implements Float32x4List {
   // Constructor.
   @pragma("vm:exact-result-type", _Float32x4ArrayView)
-  factory _Float32x4ArrayView(_TypedList buffer, int offsetInBytes, int length)
-      native "TypedDataView_Float32x4ArrayView_new";
+  factory _Float32x4ArrayView._(_TypedList buffer, int offsetInBytes,
+      int length) native "TypedDataView_Float32x4ArrayView_new";
 
   // Method(s) implementing List interface.
   Float32x4 operator [](int index) {
@@ -4068,7 +4189,7 @@
     implements Int32x4List {
   // Constructor.
   @pragma("vm:exact-result-type", _Int32x4ArrayView)
-  factory _Int32x4ArrayView(_TypedList buffer, int offsetInBytes, int length)
+  factory _Int32x4ArrayView._(_TypedList buffer, int offsetInBytes, int length)
       native "TypedDataView_Int32x4ArrayView_new";
 
   // Method(s) implementing List interface.
@@ -4105,8 +4226,8 @@
     implements Float64x2List {
   // Constructor.
   @pragma("vm:exact-result-type", _Float64x2ArrayView)
-  factory _Float64x2ArrayView(_TypedList buffer, int offsetInBytes, int length)
-      native "TypedDataView_Float64x2ArrayView_new";
+  factory _Float64x2ArrayView._(_TypedList buffer, int offsetInBytes,
+      int length) native "TypedDataView_Float64x2ArrayView_new";
 
   // Method(s) implementing List interface.
   Float64x2 operator [](int index) {
@@ -4139,7 +4260,7 @@
 @pragma("vm:entry-point")
 class _ByteDataView implements ByteData {
   @pragma("vm:exact-result-type", _ByteDataView)
-  factory _ByteDataView(_TypedList buffer, int offsetInBytes, int length)
+  factory _ByteDataView._(_TypedList buffer, int offsetInBytes, int length)
       native "TypedDataView_ByteDataView_new";
 
   // Method(s) implementing TypedData interface.
diff --git a/runtime/observatory/lib/src/allocation_profile/allocation_profile.dart b/runtime/observatory/lib/src/allocation_profile/allocation_profile.dart
index fce8ce3..fa64125 100644
--- a/runtime/observatory/lib/src/allocation_profile/allocation_profile.dart
+++ b/runtime/observatory/lib/src/allocation_profile/allocation_profile.dart
@@ -16,8 +16,8 @@
   AllocationProfile(S.ServiceMap map, {Map/*<String, List<String>>*/ defaults})
       : lastAccumulatorReset = _intString2DateTime(map[_lastAccumulatorReset]),
         lastServiceGC = _intString2DateTime(map[_lastServiceGC]),
-        oldSpace = new S.HeapSpace()..update(map['heaps']['old']),
-        newSpace = new S.HeapSpace()..update(map['heaps']['new']),
+        oldSpace = new S.HeapSpace()..update(map['_heaps']['old']),
+        newSpace = new S.HeapSpace()..update(map['_heaps']['new']),
         members = _convertMembers(map['members'], defaults: defaults);
 
   static DateTime _intString2DateTime(String milliseconds) {
@@ -71,10 +71,10 @@
 
   ClassHeapStats(Map map)
       : clazz = map['class'],
-        oldSpace = new S.Allocations()..update(map['old']),
-        newSpace = new S.Allocations()..update(map['new']),
-        promotedInstances = map['promotedInstances'],
-        promotedBytes = map['promotedBytes'];
+        oldSpace = new S.Allocations()..update(map['_old']),
+        newSpace = new S.Allocations()..update(map['_new']),
+        promotedInstances = map['_promotedInstances'],
+        promotedBytes = map['_promotedBytes'];
 }
 
 class ClassesHeapStats implements M.ClassHeapStats {
diff --git a/runtime/observatory/lib/src/elements/strongly_reachable_instances.dart b/runtime/observatory/lib/src/elements/strongly_reachable_instances.dart
index 73c0f9e..a772497 100644
--- a/runtime/observatory/lib/src/elements/strongly_reachable_instances.dart
+++ b/runtime/observatory/lib/src/elements/strongly_reachable_instances.dart
@@ -92,7 +92,7 @@
     if (_result == null) {
       return [new SpanElement()..text = 'Loading...'];
     }
-    final content = _result.samples
+    final content = _result.instances
         .map<Element>((sample) => new DivElement()
           ..children = <Element>[
             anyRef(_isolate, sample, _objects, queue: _r.queue)
@@ -106,7 +106,7 @@
   }
 
   List<Element> _createShowMoreButton() {
-    final samples = _result.samples.toList();
+    final samples = _result.instances.toList();
     if (samples.length == _result.count) {
       return [];
     }
diff --git a/runtime/observatory/lib/src/models/objects/class.dart b/runtime/observatory/lib/src/models/objects/class.dart
index 464956c..9d1a7d1 100644
--- a/runtime/observatory/lib/src/models/objects/class.dart
+++ b/runtime/observatory/lib/src/models/objects/class.dart
@@ -69,7 +69,7 @@
 
 abstract class InstanceSet {
   int get count;
-  Iterable<ObjectRef> get samples;
+  Iterable<ObjectRef> get instances;
 }
 
 abstract class TopRetainedInstances {}
diff --git a/runtime/observatory/lib/src/repositories/allocation_profile.dart b/runtime/observatory/lib/src/repositories/allocation_profile.dart
index a1f364f..365a6fc 100644
--- a/runtime/observatory/lib/src/repositories/allocation_profile.dart
+++ b/runtime/observatory/lib/src/repositories/allocation_profile.dart
@@ -16,7 +16,7 @@
     assert(isolate != null);
     var params = {};
     if (gc) {
-      params['gc'] = 'full';
+      params['gc'] = 'true';
     }
     if (reset) {
       params['reset'] = true;
@@ -27,14 +27,14 @@
       defaults = await isolate.vm.invokeRpcNoUpgrade(_defaultsApi, {});
       defaults = defaults['map'];
     }
-    isolate.updateHeapsFromMap(response['heaps']);
+    isolate.updateHeapsFromMap(response['_heaps']);
     for (S.ServiceMap clsAllocations in response['members']) {
       S.Class cls = clsAllocations['class'];
       if (cls == null) {
         continue;
       }
-      cls.newSpace.update(clsAllocations['new']);
-      cls.oldSpace.update(clsAllocations['old']);
+      cls.newSpace.update(clsAllocations['_new']);
+      cls.oldSpace.update(clsAllocations['_old']);
     }
     return new AllocationProfile(response, defaults: defaults);
   }
diff --git a/runtime/observatory/lib/src/repositories/timeline.dart b/runtime/observatory/lib/src/repositories/timeline.dart
index a954bf6..c2695ef 100644
--- a/runtime/observatory/lib/src/repositories/timeline.dart
+++ b/runtime/observatory/lib/src/repositories/timeline.dart
@@ -7,21 +7,21 @@
 class TimelineRepository implements M.TimelineRepository {
   Future<M.TimelineFlags> getFlags(M.VMRef ref) async {
     S.VM vm = ref as S.VM;
-    S.ServiceMap response = await vm.invokeRpc('_getVMTimelineFlags', {});
+    S.ServiceMap response = await vm.invokeRpc('getVMTimelineFlags', {});
     return new S.TimelineFlags(response);
   }
 
   Future setRecordedStreams(M.VMRef ref, Iterable<M.TimelineStream> streams) {
     S.VM vm = ref as S.VM;
     assert(vm != null);
-    return vm.invokeRpc('_setVMTimelineFlags', {
+    return vm.invokeRpc('setVMTimelineFlags', {
       'recordedStreams': '[${streams.map((s) => s.name).join(', ')}]',
     });
   }
 
   Future clear(M.VMRef ref) {
     S.VM vm = ref as S.VM;
-    return vm.invokeRpc('_clearVMTimeline', {});
+    return vm.invokeRpc('clearVMTimeline', {});
   }
 
   Future<Map<String, dynamic>> getIFrameParams(M.VMRef ref) async {
diff --git a/runtime/observatory/lib/src/service/object.dart b/runtime/observatory/lib/src/service/object.dart
index dace8a9..124cbc2 100644
--- a/runtime/observatory/lib/src/service/object.dart
+++ b/runtime/observatory/lib/src/service/object.dart
@@ -1904,7 +1904,7 @@
       'classId': cls.id,
       'limit': limit.toString(),
     };
-    return invokeRpc('_getInstances', params);
+    return invokeRpc('getInstances', params);
   }
 
   Future<ServiceObject /*HeapObject*/ > getObjectByAddress(String address,
@@ -2569,8 +2569,8 @@
 
     var allocationStats = map['_allocationStats'];
     if (allocationStats != null) {
-      newSpace.update(allocationStats['new']);
-      oldSpace.update(allocationStats['old']);
+      newSpace.update(allocationStats['_new']);
+      oldSpace.update(allocationStats['_old']);
       promotedByLastNewGC.instances = allocationStats['promotedInstances'];
       promotedByLastNewGC.bytes = allocationStats['promotedBytes'];
     }
@@ -4049,7 +4049,7 @@
 class InstanceSet extends HeapObject implements M.InstanceSet {
   HeapObject dartOwner;
   int count;
-  Iterable<HeapObject> samples;
+  Iterable<HeapObject> instances;
 
   InstanceSet._empty(ServiceObjectOwner owner) : super._empty(owner);
 
@@ -4061,7 +4061,7 @@
       return;
     }
     count = map['totalCount'];
-    samples = new List<HeapObject>.from(map['samples']);
+    instances = new List<HeapObject>.from(map['instances']);
   }
 }
 
diff --git a/runtime/observatory/tests/service/get_allocation_profile_public_rpc_test.dart b/runtime/observatory/tests/service/get_allocation_profile_public_rpc_test.dart
new file mode 100644
index 0000000..85b2aff
--- /dev/null
+++ b/runtime/observatory/tests/service/get_allocation_profile_public_rpc_test.dart
@@ -0,0 +1,132 @@
+// Copyright (c) 2019, 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 'package:observatory/service_io.dart';
+import 'package:unittest/unittest.dart';
+
+import 'test_helper.dart';
+
+Future sleep(int milliseconds) {
+  Completer completer = new Completer();
+  Duration duration = new Duration(milliseconds: milliseconds);
+  new Timer(duration, () => completer.complete());
+  return completer.future;
+}
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    var params = {};
+    var result =
+        await isolate.invokeRpcNoUpgrade('getAllocationProfile', params);
+    expect(result['type'], equals('AllocationProfile'));
+    expect(result.containsKey('dateLastAccumulatorReset'), isFalse);
+    expect(result.containsKey('dateLastServiceGC'), isFalse);
+    expect(result.containsKey('_heaps'), isFalse);
+    expect(result['members'].length, isPositive);
+
+    var member = result['members'][0];
+    expect(member['type'], equals('ClassHeapStats'));
+    expect(member.containsKey('_new'), isFalse);
+    expect(member.containsKey('_old'), isFalse);
+    expect(member.containsKey('_promotedInstances'), isFalse);
+    expect(member.containsKey('_promotedBytes'), isFalse);
+    expect(member.containsKey('instancesAccumulated'), isTrue);
+    expect(member.containsKey('instancesCurrent'), isTrue);
+    expect(member.containsKey('bytesCurrent'), isTrue);
+    expect(member.containsKey('accumulatedSize'), isTrue);
+
+    // reset.
+    params = {
+      'reset': 'true',
+    };
+    result = await isolate.invokeRpcNoUpgrade('getAllocationProfile', params);
+    expect(result['type'], equals('AllocationProfile'));
+    var firstReset = result['dateLastAccumulatorReset'];
+    expect(firstReset, new isInstanceOf<String>());
+    expect(result.containsKey('dateLastServiceGC'), isFalse);
+    expect(result.containsKey('_heaps'), isFalse);
+    expect(result['members'].length, isPositive);
+
+    member = result['members'][0];
+    expect(member['type'], equals('ClassHeapStats'));
+    expect(member.containsKey('_new'), isFalse);
+    expect(member.containsKey('_old'), isFalse);
+    expect(member.containsKey('_promotedInstances'), isFalse);
+    expect(member.containsKey('_promotedBytes'), isFalse);
+    expect(member.containsKey('instancesAccumulated'), isTrue);
+    expect(member.containsKey('instancesCurrent'), isTrue);
+    expect(member.containsKey('bytesCurrent'), isTrue);
+    expect(member.containsKey('accumulatedSize'), isTrue);
+
+    await sleep(1000);
+
+    result = await isolate.invokeRpcNoUpgrade('getAllocationProfile', params);
+    var secondReset = result['dateLastAccumulatorReset'];
+    expect(secondReset, isNot(equals(firstReset)));
+
+    // gc.
+    params = {
+      'gc': 'true',
+    };
+    result = await isolate.invokeRpcNoUpgrade('getAllocationProfile', params);
+    expect(result['type'], equals('AllocationProfile'));
+    expect(result['dateLastAccumulatorReset'], equals(secondReset));
+    var firstGC = result['dateLastServiceGC'];
+    expect(firstGC, new isInstanceOf<String>());
+    expect(result.containsKey('_heaps'), isFalse);
+    expect(result['members'].length, isPositive);
+
+    member = result['members'][0];
+    expect(member['type'], equals('ClassHeapStats'));
+    expect(member.containsKey('_new'), isFalse);
+    expect(member.containsKey('_old'), isFalse);
+    expect(member.containsKey('_promotedInstances'), isFalse);
+    expect(member.containsKey('_promotedBytes'), isFalse);
+    expect(member.containsKey('instancesAccumulated'), isTrue);
+    expect(member.containsKey('instancesCurrent'), isTrue);
+    expect(member.containsKey('bytesCurrent'), isTrue);
+    expect(member.containsKey('accumulatedSize'), isTrue);
+
+    await sleep(1000);
+
+    result = await isolate.invokeRpcNoUpgrade('getAllocationProfile', params);
+    var secondGC = result['dateLastAccumulatorReset'];
+    expect(secondGC, isNot(equals(firstGC)));
+  },
+  (Isolate isolate) async {
+    var params = {
+      'reset': 'banana',
+    };
+    bool caughtException;
+    try {
+      await isolate.invokeRpcNoUpgrade('getAllocationProfile', params);
+      expect(false, isTrue, reason: 'Unreachable');
+    } on ServerRpcException catch (e) {
+      caughtException = true;
+      expect(e.code, equals(ServerRpcException.kInvalidParams));
+      expect(e.data['details'],
+          "getAllocationProfile: invalid \'reset\' parameter: banana");
+    }
+    expect(caughtException, isTrue);
+  },
+  (Isolate isolate) async {
+    var params = {
+      'gc': 'banana',
+    };
+    bool caughtException;
+    try {
+      await isolate.invokeRpcNoUpgrade('getAllocationProfile', params);
+      expect(false, isTrue, reason: 'Unreachable');
+    } on ServerRpcException catch (e) {
+      caughtException = true;
+      expect(e.code, equals(ServerRpcException.kInvalidParams));
+      expect(e.data['details'],
+          "getAllocationProfile: invalid \'gc\' parameter: banana");
+    }
+    expect(caughtException, isTrue);
+  },
+];
+
+main(args) async => runIsolateTests(args, tests);
diff --git a/runtime/observatory/tests/service/get_allocation_profile_rpc_test.dart b/runtime/observatory/tests/service/get_allocation_profile_rpc_test.dart
index bea52d2..a03cbfc 100644
--- a/runtime/observatory/tests/service/get_allocation_profile_rpc_test.dart
+++ b/runtime/observatory/tests/service/get_allocation_profile_rpc_test.dart
@@ -23,11 +23,21 @@
     expect(result['type'], equals('AllocationProfile'));
     expect(result.containsKey('dateLastAccumulatorReset'), isFalse);
     expect(result.containsKey('dateLastServiceGC'), isFalse);
-    expect(result['heaps'].length, isPositive);
-    expect(result['heaps']['new']['type'], equals('HeapSpace'));
-    expect(result['heaps']['old']['type'], equals('HeapSpace'));
+    expect(result['_heaps'].length, isPositive);
+    expect(result['_heaps']['new']['type'], equals('HeapSpace'));
+    expect(result['_heaps']['old']['type'], equals('HeapSpace'));
     expect(result['members'].length, isPositive);
-    expect(result['members'][0]['type'], equals('ClassHeapStats'));
+
+    var member = result['members'][0];
+    expect(member['type'], equals('ClassHeapStats'));
+    expect(member.containsKey('_new'), isTrue);
+    expect(member.containsKey('_old'), isTrue);
+    expect(member.containsKey('_promotedInstances'), isTrue);
+    expect(member.containsKey('_promotedBytes'), isTrue);
+    expect(member.containsKey('instancesAccumulated'), isTrue);
+    expect(member.containsKey('instancesCurrent'), isTrue);
+    expect(member.containsKey('bytesCurrent'), isTrue);
+    expect(member.containsKey('accumulatedSize'), isTrue);
 
     // reset.
     params = {
@@ -38,11 +48,21 @@
     var firstReset = result['dateLastAccumulatorReset'];
     expect(firstReset, new isInstanceOf<String>());
     expect(result.containsKey('dateLastServiceGC'), isFalse);
-    expect(result['heaps'].length, isPositive);
-    expect(result['heaps']['new']['type'], equals('HeapSpace'));
-    expect(result['heaps']['old']['type'], equals('HeapSpace'));
+    expect(result['_heaps'].length, isPositive);
+    expect(result['_heaps']['new']['type'], equals('HeapSpace'));
+    expect(result['_heaps']['old']['type'], equals('HeapSpace'));
     expect(result['members'].length, isPositive);
-    expect(result['members'][0]['type'], equals('ClassHeapStats'));
+
+    member = result['members'][0];
+    expect(member['type'], equals('ClassHeapStats'));
+    expect(member.containsKey('_new'), isTrue);
+    expect(member.containsKey('_old'), isTrue);
+    expect(member.containsKey('_promotedInstances'), isTrue);
+    expect(member.containsKey('_promotedBytes'), isTrue);
+    expect(member.containsKey('instancesAccumulated'), isTrue);
+    expect(member.containsKey('instancesCurrent'), isTrue);
+    expect(member.containsKey('bytesCurrent'), isTrue);
+    expect(member.containsKey('accumulatedSize'), isTrue);
 
     await sleep(1000);
 
@@ -52,18 +72,28 @@
 
     // gc.
     params = {
-      'gc': 'full',
+      'gc': 'true',
     };
     result = await isolate.invokeRpcNoUpgrade('_getAllocationProfile', params);
     expect(result['type'], equals('AllocationProfile'));
     expect(result['dateLastAccumulatorReset'], equals(secondReset));
     var firstGC = result['dateLastServiceGC'];
     expect(firstGC, new isInstanceOf<String>());
-    expect(result['heaps'].length, isPositive);
-    expect(result['heaps']['new']['type'], equals('HeapSpace'));
-    expect(result['heaps']['old']['type'], equals('HeapSpace'));
+    expect(result['_heaps'].length, isPositive);
+    expect(result['_heaps']['new']['type'], equals('HeapSpace'));
+    expect(result['_heaps']['old']['type'], equals('HeapSpace'));
     expect(result['members'].length, isPositive);
-    expect(result['members'][0]['type'], equals('ClassHeapStats'));
+
+    member = result['members'][0];
+    expect(member['type'], equals('ClassHeapStats'));
+    expect(member.containsKey('_new'), isTrue);
+    expect(member.containsKey('_old'), isTrue);
+    expect(member.containsKey('_promotedInstances'), isTrue);
+    expect(member.containsKey('_promotedBytes'), isTrue);
+    expect(member.containsKey('instancesAccumulated'), isTrue);
+    expect(member.containsKey('instancesCurrent'), isTrue);
+    expect(member.containsKey('bytesCurrent'), isTrue);
+    expect(member.containsKey('accumulatedSize'), isTrue);
 
     await sleep(1000);
 
diff --git a/runtime/observatory/tests/service/get_instances_rpc_test.dart b/runtime/observatory/tests/service/get_instances_rpc_test.dart
index 7f1bc22..814405c 100644
--- a/runtime/observatory/tests/service/get_instances_rpc_test.dart
+++ b/runtime/observatory/tests/service/get_instances_rpc_test.dart
@@ -31,25 +31,38 @@
   (Isolate isolate) async {
     var obj = await eval(isolate, 'global');
     var params = {
-      'classId': obj['class']['id'],
+      'objectId': obj['class']['id'],
       'limit': 4,
     };
-    var result = await isolate.invokeRpcNoUpgrade('_getInstances', params);
+    var result = await isolate.invokeRpcNoUpgrade('getInstances', params);
     expect(result['type'], equals('InstanceSet'));
     expect(result['totalCount'], equals(2));
-    expect(result['samples'].length, equals(2));
-    expect(result['samples'][0]['type'], equals('@Instance'));
+    expect(result['instances'].length, equals(2));
+    expect(result['instances'][0]['type'], equals('@Instance'));
 
     // Limit is respected.
     params = {
-      'classId': obj['class']['id'],
+      'objectId': obj['class']['id'],
       'limit': 1,
     };
-    result = await isolate.invokeRpcNoUpgrade('_getInstances', params);
+    result = await isolate.invokeRpcNoUpgrade('getInstances', params);
     expect(result['type'], equals('InstanceSet'));
     expect(result['totalCount'], equals(2));
-    expect(result['samples'].length, equals(1));
-    expect(result['samples'][0]['type'], equals('@Instance'));
+    expect(result['instances'].length, equals(1));
+    expect(result['instances'][0]['type'], equals('@Instance'));
+
+    // Try an object ID that isn't a class ID
+    params = {
+      'objectId': isolate.rootLibrary.id,
+      'limit': 1,
+    };
+    try {
+      await isolate.invokeRpcNoUpgrade('getInstances', params);
+    } on ServerRpcException catch (_) {
+      // Success.
+    } catch (e) {
+      fail('Failed with exception: $e');
+    }
   },
 ];
 
diff --git a/runtime/observatory/tests/service/get_version_rpc_test.dart b/runtime/observatory/tests/service/get_version_rpc_test.dart
index f5eb252..a5f9a4f 100644
--- a/runtime/observatory/tests/service/get_version_rpc_test.dart
+++ b/runtime/observatory/tests/service/get_version_rpc_test.dart
@@ -12,7 +12,7 @@
     var result = await vm.invokeRpcNoUpgrade('getVersion', {});
     expect(result['type'], equals('Version'));
     expect(result['major'], equals(3));
-    expect(result['minor'], equals(17));
+    expect(result['minor'], equals(20));
     expect(result['_privateMajor'], equals(0));
     expect(result['_privateMinor'], equals(0));
   },
diff --git a/runtime/observatory/tests/service/get_vm_timeline_rpc_test.dart b/runtime/observatory/tests/service/get_vm_timeline_rpc_test.dart
index 8a60ea3..cdd9344 100644
--- a/runtime/observatory/tests/service/get_vm_timeline_rpc_test.dart
+++ b/runtime/observatory/tests/service/get_vm_timeline_rpc_test.dart
@@ -94,14 +94,14 @@
     }
     Map arguments = event['args'];
     expect(arguments, new isInstanceOf<Map>());
-    expect(arguments['isolateNumber'], new isInstanceOf<String>());
+    expect(arguments['isolateId'], new isInstanceOf<String>());
   }
 }
 
 var tests = <VMTest>[
   (VM vm) async {
-    Map result = await vm.invokeRpcNoUpgrade('_getVMTimeline', {});
-    expect(result['type'], equals('_Timeline'));
+    Map result = await vm.invokeRpcNoUpgrade('getVMTimeline', {});
+    expect(result['type'], equals('Timeline'));
     expect(result['traceEvents'], new isInstanceOf<List>());
     final int numEvents = result['traceEvents'].length;
     List dartEvents = filterForDartEvents(result['traceEvents']);
@@ -121,7 +121,7 @@
     int origin = timeOrigin(dartEvents);
     int extent = timeDuration(dartEvents, origin);
     // Query for the timeline with the time window for Dart events.
-    result = await vm.invokeRpcNoUpgrade('_getVMTimeline',
+    result = await vm.invokeRpcNoUpgrade('getVMTimeline',
         {'timeOriginMicros': origin, 'timeExtentMicros': extent});
     // Verify that we received fewer events than before.
     expect(result['traceEvents'].length, lessThan(numEvents));
diff --git a/runtime/observatory/tests/service/vm_timeline_events_test.dart b/runtime/observatory/tests/service/vm_timeline_events_test.dart
index 9b2e550..9586771 100644
--- a/runtime/observatory/tests/service/vm_timeline_events_test.dart
+++ b/runtime/observatory/tests/service/vm_timeline_events_test.dart
@@ -45,7 +45,7 @@
   },
   (Isolate isolate) async {
     // Get the flags.
-    Map flags = await isolate.vm.invokeRpcNoUpgrade('_getVMTimelineFlags', {});
+    Map flags = await isolate.vm.invokeRpcNoUpgrade('getVMTimelineFlags', {});
     expect(flags['type'], 'TimelineFlags');
     // Confirm that 'Dart' is available.
     expect(flags['availableStreams'].contains('Dart'), isTrue);
@@ -54,7 +54,7 @@
   },
   (Isolate isolate) async {
     // Enable the Dart category.
-    await isolate.vm.invokeRpcNoUpgrade('_setVMTimelineFlags', {
+    await isolate.vm.invokeRpcNoUpgrade('setVMTimelineFlags', {
       "recordedStreams": ["Dart"]
     });
   },
diff --git a/runtime/observatory/tests/service/vm_timeline_flags_test.dart b/runtime/observatory/tests/service/vm_timeline_flags_test.dart
index 4e71d95..5f5365f 100644
--- a/runtime/observatory/tests/service/vm_timeline_flags_test.dart
+++ b/runtime/observatory/tests/service/vm_timeline_flags_test.dart
@@ -31,7 +31,7 @@
   hasStoppedAtBreakpoint,
   (Isolate isolate) async {
     // Get the flags.
-    Map flags = await isolate.vm.invokeRpcNoUpgrade('_getVMTimelineFlags', {});
+    Map flags = await isolate.vm.invokeRpcNoUpgrade('getVMTimelineFlags', {});
     expect(flags['type'], 'TimelineFlags');
     // Confirm that 'Dart' is available.
     expect(flags['availableStreams'].contains('Dart'), isTrue);
@@ -40,21 +40,21 @@
   },
   (Isolate isolate) async {
     // Get the timeline.
-    Map result = await isolate.vm.invokeRpcNoUpgrade('_getVMTimeline', {});
-    expect(result['type'], equals('_Timeline'));
+    Map result = await isolate.vm.invokeRpcNoUpgrade('getVMTimeline', {});
+    expect(result['type'], equals('Timeline'));
     expect(result['traceEvents'], new isInstanceOf<List>());
     // Confirm that it as no non-meta data events.
     expect(filterEvents(result['traceEvents'], isNotMetaData).length, 0);
   },
   (Isolate isolate) async {
     // Enable the Dart category.
-    await isolate.vm.invokeRpcNoUpgrade('_setVMTimelineFlags', {
+    await isolate.vm.invokeRpcNoUpgrade('setVMTimelineFlags', {
       "recordedStreams": ["Dart"]
     });
   },
   (Isolate isolate) async {
     // Get the flags.
-    Map flags = await isolate.vm.invokeRpcNoUpgrade('_getVMTimelineFlags', {});
+    Map flags = await isolate.vm.invokeRpcNoUpgrade('getVMTimelineFlags', {});
     expect(flags['type'], 'TimelineFlags');
     // Confirm that only Dart is being recorded.
     expect(flags['recordedStreams'].length, equals(1));
@@ -65,8 +65,8 @@
   hasStoppedAtBreakpoint,
   (Isolate isolate) async {
     // Get the timeline.
-    Map result = await isolate.vm.invokeRpcNoUpgrade('_getVMTimeline', {});
-    expect(result['type'], equals('_Timeline'));
+    Map result = await isolate.vm.invokeRpcNoUpgrade('getVMTimeline', {});
+    expect(result['type'], equals('Timeline'));
     expect(result['traceEvents'], new isInstanceOf<List>());
     print(result['traceEvents']);
     // Confirm that Dart events are added.
@@ -79,16 +79,16 @@
   (Isolate isolate) async {
     // Disable the Dart category.
     await isolate.vm
-        .invokeRpcNoUpgrade('_setVMTimelineFlags', {"recordedStreams": []});
+        .invokeRpcNoUpgrade('setVMTimelineFlags', {"recordedStreams": []});
     // Grab the timeline and remember the number of Dart events.
-    Map result = await isolate.vm.invokeRpcNoUpgrade('_getVMTimeline', {});
-    expect(result['type'], equals('_Timeline'));
+    Map result = await isolate.vm.invokeRpcNoUpgrade('getVMTimeline', {});
+    expect(result['type'], equals('Timeline'));
     expect(result['traceEvents'], new isInstanceOf<List>());
     dartEventCount = filterEvents(result['traceEvents'], isDart).length;
   },
   (Isolate isolate) async {
     // Get the flags.
-    Map flags = await isolate.vm.invokeRpcNoUpgrade('_getVMTimelineFlags', {});
+    Map flags = await isolate.vm.invokeRpcNoUpgrade('getVMTimelineFlags', {});
     expect(flags['type'], 'TimelineFlags');
     // Confirm that 'Dart' is not being recorded.
     expect(flags['recordedStreams'].length, equals(0));
@@ -97,8 +97,8 @@
   hasStoppedAtBreakpoint,
   (Isolate isolate) async {
     // Grab the timeline and verify that we haven't added any new Dart events.
-    Map result = await isolate.vm.invokeRpcNoUpgrade('_getVMTimeline', {});
-    expect(result['type'], equals('_Timeline'));
+    Map result = await isolate.vm.invokeRpcNoUpgrade('getVMTimeline', {});
+    expect(result['type'], equals('Timeline'));
     expect(result['traceEvents'], new isInstanceOf<List>());
     expect(filterEvents(result['traceEvents'], isDart).length, dartEventCount);
     // Confirm that zero non-Dart events are added.
diff --git a/runtime/observatory/tests/service/write_cpu_profile_timeline_rpc_test.dart b/runtime/observatory/tests/service/write_cpu_profile_timeline_rpc_test.dart
index ea0a6ef..fad0eda 100644
--- a/runtime/observatory/tests/service/write_cpu_profile_timeline_rpc_test.dart
+++ b/runtime/observatory/tests/service/write_cpu_profile_timeline_rpc_test.dart
@@ -27,8 +27,8 @@
     print(result);
     expect(result['type'], equals('Success'));
 
-    result = await isolate.vm.invokeRpcNoUpgrade('_getVMTimeline', {});
-    expect(result['type'], equals('_Timeline'));
+    result = await isolate.vm.invokeRpcNoUpgrade('getVMTimeline', {});
+    expect(result['type'], equals('Timeline'));
     expect(result['traceEvents'], new isInstanceOf<List>());
 
     var events = result['traceEvents'];
diff --git a/runtime/tests/vm/dart/appjit_cha_deopt_test.dart b/runtime/tests/vm/dart/appjit_cha_deopt_test.dart
index 104d431..37071f9 100644
--- a/runtime/tests/vm/dart/appjit_cha_deopt_test.dart
+++ b/runtime/tests/vm/dart/appjit_cha_deopt_test.dart
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 // OtherResources=appjit_cha_deopt_test_body.dart
-// VMOptions=--optimization-counter-threshold=100
+// VMOptions=--optimization-counter-threshold=100 --deterministic
 
 // Verify that app-jit snapshot contains dependencies between classes and CHA
 // optimized code.
diff --git a/runtime/tests/vm/dart/disassemble_determinism_test.dart b/runtime/tests/vm/dart/disassemble_determinism_test.dart
new file mode 100644
index 0000000..1ff1091
--- /dev/null
+++ b/runtime/tests/vm/dart/disassemble_determinism_test.dart
@@ -0,0 +1,57 @@
+// Copyright (c) 2019, 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.
+
+// Verify running with --disassemble --disassemble-relative produces
+// deterministic output. This is useful for removing noise when tracking down
+// the effects of compiler changes.
+
+import 'dart:async';
+import 'dart:io';
+import 'snapshot_test_helper.dart';
+
+import 'package:expect/expect.dart';
+
+int fib(int n) {
+  if (n <= 1) return 1;
+  return fib(n - 1) + fib(n - 2);
+}
+
+Future<void> main(List<String> args) async {
+  if (args.contains('--child')) {
+    print(fib(35));
+    return;
+  }
+
+  if (!Platform.script.toString().endsWith(".dart")) {
+    return; // Not running from source: skip for app-jit and app-aot.
+  }
+  if (Platform.executable.contains("Product")) {
+    return; // No disassembler in product mode.
+  }
+  if (Platform.executable.contains("IA32")) {
+    return; // Our IA32 code is not position independent.
+  }
+
+  final result1 = await runDart('GENERATE DISASSEMBLY 1', [
+    '--deterministic',
+    '--disassemble',
+    '--disassemble-relative',
+    Platform.script.toFilePath(),
+    '--child'
+  ]);
+  final asm1 = result1.processResult.stderr;
+
+  final result2 = await runDart('GENERATE DISASSEMBLY 2', [
+    '--deterministic',
+    '--disassemble',
+    '--disassemble-relative',
+    Platform.script.toFilePath(),
+    '--child'
+  ]);
+  final asm2 = result2.processResult.stderr;
+
+  Expect.isTrue(
+      asm1.contains("Code for function"), "Printed at least one function");
+  Expect.stringEquals(asm1, asm2);
+}
diff --git a/runtime/tests/vm/dart/print_flow_graph_determinism_test.dart b/runtime/tests/vm/dart/print_flow_graph_determinism_test.dart
new file mode 100644
index 0000000..7b2eae5
--- /dev/null
+++ b/runtime/tests/vm/dart/print_flow_graph_determinism_test.dart
@@ -0,0 +1,51 @@
+// Copyright (c) 2019, 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.
+
+// Verify running with --print-flow-graph produces deterministic output. This is
+// useful for removing noise when tracking down the effects of compiler changes.
+
+import 'dart:async';
+import 'dart:io';
+import 'snapshot_test_helper.dart';
+
+import 'package:expect/expect.dart';
+
+int fib(int n) {
+  if (n <= 1) return 1;
+  return fib(n - 1) + fib(n - 2);
+}
+
+Future<void> main(List<String> args) async {
+  if (args.contains('--child')) {
+    print(fib(35));
+    return;
+  }
+
+  if (!Platform.script.toString().endsWith(".dart")) {
+    return; // Not running from source: skip for app-jit and app-aot.
+  }
+  if (Platform.executable.contains("Product")) {
+    return; // No flow graph printer in product mode.
+  }
+
+  final result1 = await runDart('GENERATE CFG 1', [
+    '--deterministic',
+    '--print-flow-graph',
+    Platform.script.toFilePath(),
+    '--child'
+  ]);
+  final cfg1 = result1.processResult.stderr;
+
+  final result2 = await runDart('GENERATE CFG 2', [
+    '--deterministic',
+    '--print-flow-graph',
+    Platform.script.toFilePath(),
+    '--child'
+  ]);
+  final cfg2 = result2.processResult.stderr;
+
+  Expect.isTrue(
+      cfg1.contains("*** BEGIN CFG"), "Printed at least one function");
+  Expect.stringEquals(cfg1, cfg2);
+}
diff --git a/runtime/tests/vm/vm.status b/runtime/tests/vm/vm.status
index 5b8bc01..0c6a32b 100644
--- a/runtime/tests/vm/vm.status
+++ b/runtime/tests/vm/vm.status
@@ -12,16 +12,16 @@
 cc/IsolateReload_PendingUnqualifiedCall_InstanceToStatic: Fail # Issue 32981
 cc/IsolateReload_PendingUnqualifiedCall_StaticToInstance: Fail # Issue 32981
 cc/IsolateReload_RunNewFieldInitializersWithGenerics: Fail # Issue 32299
+cc/Profiler_StringInterpolation: Fail # Issue 37208
 dart/data_uri_import_test/none: SkipByDesign
-dart/snapshot_version_test: Skip # This test is a Dart1 test (script snapshot)
+dart/entrypoints/jit: Skip # Tests with brittle dependencies on usage counters - Issue 37144
 dart/slow_path_shared_stub_test: Pass, Slow # Uses --shared-slow-path-triggers-gc flag.
+dart/snapshot_version_test: Skip # This test is a Dart1 test (script snapshot)
 dart/stack_overflow_shared_test: Pass, Slow # Uses --shared-slow-path-triggers-gc flag.
 dart/use_bare_instructions_flag_test: Pass, Slow # Spawns several subprocesses
 
-cc/Profiler_StringInterpolation: Fail # Issue 37208
-
-[ $mode == debug ]
-dart/appjit_cha_deopt_test: Pass, Slow # Quite slow in debug mode, uses --optimization-counter-threshold=100
+[ $builder_tag == asan ]
+dart/transferable_throws_oom_test: SkipByDesign # This test tries to allocate too much memory on purpose. Still dartbug.com/37188
 
 [ $builder_tag == optimization_counter_threshold ]
 dart/appjit*: SkipByDesign # Test needs to a particular opt-counter value
@@ -31,44 +31,14 @@
 dart/redirection_type_shuffling_test/none: RuntimeError
 dart/snapshot_version_test: RuntimeError
 
-[ $hot_reload || $hot_reload_rollback ]
-dart/compilation_trace_test: Pass, Slow
-dart/type_feedback_test: Pass, Slow
-dart/issue_31959_31960_test: SkipSlow
-
-[ $compiler != dartk || ($arch != x64 && $arch != simarm && $arch != arm) || $hot_reload || $hot_reload_rollback ]
-dart/entrypoints/jit/*: SkipByDesign  # Only supported in the Dart 2 JIT and AOT, and test optimizations - hence disabled on hotreload bots.
-
-[ $compiler != dartkp || ($arch != x64 && $arch != simarm && $arch != arm) || $hot_reload || $hot_reload_rollback ]
-dart/entrypoints/aot/*: SkipByDesign  # Only supported in the Dart 2 JIT and AOT, and test optimizations - hence disabled on hotreload bots.
-
-[ ($compiler != dartk && $compiler != dartkp) || ($arch != x64 && $arch != simarm && $arch != arm) || $hot_reload || $hot_reload_rollback ]
-dart/entrypoints/*: SkipByDesign  # Only supported in the Dart 2 JIT and AOT, and test optimizations - hence disabled on hotreload bots.
-
 [ $compiler == dartk ]
 dart/entrypoints/aot/*: SkipByDesign
 
-[ ($compiler == dartk || $compiler == dartkb) ]
-cc/DartAPI_New: Fail # Issue #33041
-dart/redirection_type_shuffling_test/00: RuntimeError, Pass
-dart/redirection_type_shuffling_test/none: RuntimeError
+[ $compiler == dartkb ]
+cc/*: Skip # Bytecode modes are not properly hooked up in run_vm_tests.
 
-[ $runtime != vm && $runtime != dart_precompiled ]
-dart/catch_entry_state: SkipByDesign
-
-[ $builder_tag == asan ]
-dart/transferable_throws_oom_test: SkipByDesign # This test tries to allocate too much memory on purpose. Still dartbug.com/37188
-
-[ $system == macos ]
-dart/transferable_throws_oom_test: SkipByDesign # Allocating too much memory to cause OOM doesn't work on mac
-
-[ $compiler != dartk && $compiler != dartkb ]
-cc/IsolateReload_KernelIncrementalCompile: SkipByDesign
-cc/IsolateReload_KernelIncrementalCompileAppAndLib: SkipByDesign
-cc/IsolateReload_KernelIncrementalCompileExpression: SkipByDesign
-cc/IsolateReload_KernelIncrementalCompileGenerics: SkipByDesign
-cc/Mixin_PrivateSuperResolution: Skip
-cc/Mixin_PrivateSuperResolutionCrossLibraryShouldFail: Skip
+[ $compiler == dartkp ]
+dart/v8_snapshot_profile_writer_test: Pass, Slow # Can be slow due to re-invoking the precompiler.
 
 [ $compiler == fasta ]
 dart/data_uri_import_test/badencodeddate: CompileTimeError
@@ -78,53 +48,37 @@
 
 [ $mode == debug ]
 cc/CorelibIsolateStartup: SkipByDesign # This is a benchmark that is not informative in debug mode.
+cc/IRTest_TypedDataAOT_FunctionalGetSet: Skip # run_vm_tests contains JIT/AOT but FLAG_precompiled_mode is not always correct. This causes this test to fail in debug mode, hitting an assertion. See http://dartbug.com/36521
 cc/VerifyExplicit_Crash: Crash # Negative tests of VerifiedMemory should crash iff in DEBUG mode. TODO(koda): Improve support for negative tests.
 cc/VerifyImplicit_Crash: Crash # Negative tests of VerifiedMemory should crash iff in DEBUG mode. TODO(koda): Improve support for negative tests.
+dart/appjit_cha_deopt_test: Pass, Slow # Quite slow in debug mode, uses --optimization-counter-threshold=100
 dart/spawn_shutdown_test: Pass, Slow # VM Shutdown test, It can take some time for all the isolates to shutdown in a Debug build.
 
 [ $runtime == dart_precompiled ]
 dart/data_uri_spawn_test: SkipByDesign # Isolate.spawnUri
 dart/issue32950_test: SkipByDesign # uses spawnUri.
 
-[ $runtime == vm && $mode == product && $compiler == dartk ]
-cc/CorelibIsolateStartup: Timeout, Pass
-
-[ $runtime != vm && $runtime != dart_precompiled ]
-dart/*: SkipByDesign # VM specific tests
-
-[ $runtime != dart_precompiled  || $system == android ]
-dart/bare_instructions_trampolines_test: SkipByDesign # This test is for VM AOT only (android fails due to listing interfaces).
-
-[ $mode == debug || $runtime != dart_precompiled  || $system == android ]
-dart/use_bare_instructions_flag_test: SkipByDesign # This test is for VM AOT only and is quite slow (so we don't run it in debug mode).
-
 [ $system == fuchsia ]
 cc/CorelibIsolateStartup: Skip # OOM crash can bring down the OS.
 cc/Read: Fail # TODO(zra): Investigate, ../../dart/runtime/bin/file_test.cc: 34: error: expected: !file->WriteByte(1)
 dart/data_uri_spawn_test: Skip # TODO(zra): package:unittest is not in the image.
 dart/spawn_shutdown_test: Skip # OOM crash can bring down the OS.
 
+[ $system == macos ]
+dart/transferable_throws_oom_test: SkipByDesign # Allocating too much memory to cause OOM doesn't work on mac
+
 [ $system == windows ]
 cc/CorelibCompilerStats: Skip
-cc/GenKernelKernelLoadKernel: Skip  # Issue 34542.
-cc/GenKernelKernelReadAllBytecode: Skip  # Issue 34393.
-cc/GenKernelKernelCombined: Skip  # Issue 34393.
-cc/GenKernelKernelMaxRSS: Skip  # Issue 34393.
-dart/appjit_bytecode_simple_test: Skip  # Issue 34393.
-
-[ $arch == simarm || $arch == simdbc || $arch == simdbc64 || $arch == ia32 || $arch == arm ]
-cc/GenKernelKernelLoadKernel: SkipByDesign  # No interpreter support.
-cc/GenKernelKernelReadAllBytecode: SkipByDesign  # No interpreter support.
-cc/GenKernelKernelCombined: SkipByDesign  # No interpreter support.
-cc/GenKernelKernelMaxRSS: SkipByDesign  # No interpreter support.
+cc/GenKernelKernelCombined: Skip # Issue 34393.
+cc/GenKernelKernelLoadKernel: Skip # Issue 34542.
+cc/GenKernelKernelMaxRSS: Skip # Issue 34393.
+cc/GenKernelKernelReadAllBytecode: Skip # Issue 34393.
+dart/appjit_bytecode_simple_test: Skip # Issue 34393.
 
 [ !$strong ]
 dart/callee_side_type_checks_test: SkipByDesign
 
-[ !$checked && !$strong && $runtime == vm ]
-dart/redirection_type_shuffling_test/00: MissingCompileTimeError
-
-[ $arch != simarm && $arch != simarm64 && $arch != simdbc64 && ($compiler == dartk || $compiler == dartkb) && $hot_reload ]
+[ $arch != simarm && $arch != simarm64 && $arch != simdbc64 && $hot_reload && ($compiler == dartk || $compiler == dartkb) ]
 dart/data_uri_import_test/base64: Crash
 dart/data_uri_import_test/nocharset: Crash
 dart/data_uri_import_test/nomime: Crash
@@ -132,16 +86,60 @@
 dart/data_uri_import_test/utf16: Crash
 dart/data_uri_import_test/wrongmime: Crash
 
+[ $builder_tag == obfuscated && $compiler == dartkp ]
+dart/optimized_stacktrace_line_and_column_test: SkipByDesign # Looks for filenames in stacktrace output
+dart/optimized_stacktrace_line_test: SkipByDesign # Looks for filenames in stacktrace output
+
+[ $compiler == dartk && $mode == product && $runtime == vm ]
+cc/CorelibIsolateStartup: Timeout, Pass
+
+[ $compiler != dartk && $compiler != dartkb ]
+cc/IsolateReload_KernelIncrementalCompile: SkipByDesign
+cc/IsolateReload_KernelIncrementalCompileAppAndLib: SkipByDesign
+cc/IsolateReload_KernelIncrementalCompileExpression: SkipByDesign
+cc/IsolateReload_KernelIncrementalCompileGenerics: SkipByDesign
+cc/Mixin_PrivateSuperResolution: Skip
+cc/Mixin_PrivateSuperResolutionCrossLibraryShouldFail: Skip
+
+[ $compiler != dartk && $compiler != dartkb && $compiler != none ]
+dart/appjit*: SkipByDesign # Test needs to run from source
+dart/kernel_determinism_test: SkipByDesign # Test needs to run from source
+
+[ $compiler == dartkp && ($runtime == dart_precompiled || $runtime == vm) ]
+dart/issue32950_test: SkipByDesign # uses spawnUri.
+dart/redirection_type_shuffling_test: SkipByDesign # Includes dart:mirrors.
+dart/spawn_shutdown_test: SkipSlow
+
+[ $mode == debug && $system == windows ]
+dart/spawn_shutdown_test: Skip # Flaky crashes unable to start thread; likely low memory on the bot.
+
 # Enabling of dartk for sim{arm,arm64,dbc64} revelaed these test failures, which
 # are to be triaged.  Isolate tests are skipped on purpose due to the usage of
 # batch mode.
-[ ($compiler == dartk || $compiler == dartkb) && $mode == debug && ($arch == simarm || $arch == simarm64) ]
+[ $mode == debug && ($arch == simarm || $arch == simarm64) && ($compiler == dartk || $compiler == dartkb) ]
 cc/StackTraceMallocHookLengthTest: Fail # Please triage.
 
-[ ($compiler == dartk || $compiler == dartkb) && $mode == release && $runtime == vm ]
+[ $mode == product && $runtime == vm ]
+cc/DartAPI_IsolateSetCheckedMode: Fail, OK # Checked mode disabled in product mode.
+
+[ $mode == release && $runtime == vm && ($compiler == dartk || $compiler == dartkb) ]
 cc/CorelibIsolateStartup: Timeout, Pass
 
-[ ($compiler == dartk || $compiler == dartkb) && $runtime == vm ]
+[ $runtime == dart_precompiled && $minified ]
+dart/inline_stack_frame_test: Skip
+dart/optimized_stacktrace_line_test: Skip
+
+[ $runtime != dart_precompiled && $runtime != vm ]
+dart/*: SkipByDesign # VM specific tests
+dart/catch_entry_state: SkipByDesign
+
+[ $runtime == vm && $system == macos && ($compiler == dartk || $compiler == dartkb) ]
+cc/IsolateReload_LibraryLookup: Fail, Crash
+
+[ $runtime == vm && !$checked && !$strong ]
+dart/redirection_type_shuffling_test/00: MissingCompileTimeError
+
+[ $runtime == vm && ($compiler == dartk || $compiler == dartkb) ]
 cc/Class_ComputeEndTokenPos: Crash
 cc/DartAPI_LoadLibrary: Fail, Crash # Issue 33048.
 cc/DebuggerAPI_BreakpointStubPatching: Fail
@@ -157,19 +155,16 @@
 cc/IsolateReload_TypedefToNotTypedef: Fail
 dart/spawn_shutdown_test: SkipSlow
 
-[ ($compiler == dartk || $compiler == dartkb) && $runtime == vm && $system == macos ]
+[ $system == linux && ($compiler == dartk || $compiler == dartkb) ]
 cc/IsolateReload_LibraryLookup: Fail, Crash
 
-[ ($compiler == dartk || $compiler == dartkb) && $system == linux ]
+[ $system == windows && ($compiler == dartk || $compiler == dartkb) ]
 cc/IsolateReload_LibraryLookup: Fail, Crash
 
-[ ($compiler == dartk || $compiler == dartkb) && $system == windows ]
-cc/IsolateReload_LibraryLookup: Fail, Crash
-
-[ ($compiler == dartk || $compiler == dartkb) && $checked ]
+[ $checked && ($compiler == dartk || $compiler == dartkb) ]
 dart/redirection_type_shuffling_test/00: Pass # Works in --checked mode but not in --strong mode.
 
-[ ($compiler == dartk || $compiler == dartkb) && $strong ]
+[ $strong && ($compiler == dartk || $compiler == dartkb) ]
 cc/DartGeneratedArrayLiteralMessages: Crash # Issue 32190
 cc/FullSnapshot1: Crash # Issue 32190
 cc/IsolateReload_LibraryLookup: Fail, Crash # Issue 32190
@@ -177,14 +172,14 @@
 cc/ScriptSnapshot1: Fail, Crash, OK # Script snapshots not supported in Dart 2
 cc/ScriptSnapshotsUpdateSubclasses: Fail, Crash, OK # Script snapshots not supported in Dart 2
 
-[ ($compiler == dartk || $compiler == dartkb) && ($arch == simarm || $arch == simarm64 || $arch == simdbc || $arch == simdbc64) ]
+[ ($arch == simarm || $arch == simarm64 || $arch == simdbc || $arch == simdbc64) && ($compiler == dartk || $compiler == dartkb) ]
 dart/appjit*: SkipSlow # DFE too slow
 dart/issue_31959_31960_test: SkipSlow
 
 # Enabling of dartk for sim{arm,arm64,dbc64} revelaed these test failures, which
 # are to be triaged.  Isolate tests are skipped on purpose due to the usage of
 # batch mode.
-[ ($compiler == dartk || $compiler == dartkb) && ($arch == simarm || $arch == simarm64 || $arch == simdbc64) ]
+[ ($arch == simarm || $arch == simarm64 || $arch == simdbc64) && ($compiler == dartk || $compiler == dartkb) ]
 dart/data_uri_spawn_test: Skip # Please triage.
 dart/snapshot_version_test: RuntimeError # Please triage.
 
@@ -192,24 +187,14 @@
 dart/data_uri_spawn_test: Skip # Timeout
 dart/kernel_determinism_test: SkipSlow
 
-[ $compiler != dartk && $compiler != dartkb && $compiler != none ]
-dart/appjit*: SkipByDesign # Test needs to run from source
-dart/kernel_determinism_test: SkipByDesign # Test needs to run from source
+[ $arch == arm || $arch == arm64 || $compiler != dartkp ]
+dart/v8_snapshot_profile_writer_test: SkipByDesign # Only relevant for AOT. Doesn't work in cross-compilation (has to run on the host).
 
-[ $compiler == dartkp && ($runtime == dart_precompiled || $runtime == vm) ]
-dart/issue32950_test: SkipByDesign # uses spawnUri.
-dart/redirection_type_shuffling_test: SkipByDesign # Includes dart:mirrors.
-dart/spawn_shutdown_test: SkipSlow
-
-[ $mode == debug && $system == windows ]
-dart/spawn_shutdown_test: Skip # Flaky crashes unable to start thread; likely low memory on the bot.
-
-[ $mode == product && $runtime == vm ]
-cc/DartAPI_IsolateSetCheckedMode: Fail, OK # Checked mode disabled in product mode.
-
-[ $runtime == dart_precompiled && $minified ]
-dart/inline_stack_frame_test: Skip
-dart/optimized_stacktrace_line_test: Skip
+[ $arch == arm || $arch == ia32 || $arch == simarm || $arch == simdbc || $arch == simdbc64 ]
+cc/GenKernelKernelCombined: SkipByDesign # No interpreter support.
+cc/GenKernelKernelLoadKernel: SkipByDesign # No interpreter support.
+cc/GenKernelKernelMaxRSS: SkipByDesign # No interpreter support.
+cc/GenKernelKernelReadAllBytecode: SkipByDesign # No interpreter support.
 
 # Profiler is completely disabled in SIMDBC builds.
 # On the simluator stack traces produced by the Profiler do not match
@@ -261,31 +246,39 @@
 [ $compiler == dart2analyzer || $compiler == dart2js ]
 dart/data_uri*test: Skip # Data uri's not supported by dart2js or the analyzer.
 
+[ $compiler == dartk || $compiler == dartkb ]
+cc/DartAPI_New: Fail # Issue #33041
+dart/redirection_type_shuffling_test/00: RuntimeError, Pass
+dart/redirection_type_shuffling_test/none: RuntimeError
+
+[ $compiler != dartk || $hot_reload || $hot_reload_rollback || $arch != arm && $arch != simarm && $arch != x64 ]
+dart/entrypoints/jit/*: SkipByDesign # Only supported in the Dart 2 JIT and AOT, and test optimizations - hence disabled on hotreload bots.
+
+[ $compiler != dartkp || $hot_reload || $hot_reload_rollback || $arch != arm && $arch != simarm && $arch != x64 ]
+dart/entrypoints/aot/*: SkipByDesign # Only supported in the Dart 2 JIT and AOT, and test optimizations - hence disabled on hotreload bots.
+
 [ $compiler == precompiler || $mode == product ]
 cc/CoreSnapshotSize: SkipByDesign # Imports dart:mirrors
 cc/CreateMirrorSystem: SkipByDesign # Imports dart:mirrors
 cc/StandaloneSnapshotSize: SkipByDesign # Imports dart:mirrors
 dart/redirection_type_shuffling_test: SkipByDesign # Imports dart:mirrors
 
+[ $mode == debug || $runtime != dart_precompiled || $system == android ]
+dart/use_bare_instructions_flag_test: SkipByDesign # This test is for VM AOT only and is quite slow (so we don't run it in debug mode).
+
+[ $runtime != dart_precompiled || $system == android ]
+dart/bare_instructions_trampolines_test: SkipByDesign # This test is for VM AOT only (android fails due to listing interfaces).
+
 [ $hot_reload || $hot_reload_rollback ]
 dart/appjit*: SkipByDesign # Cannot reload with URI pointing to app snapshot.
+dart/compilation_trace_test: Pass, Slow
+dart/disassemble_determinism_test: SkipSlow # Runs expensive fibonacci(32) computation in 2 subprocesses
+dart/issue_31959_31960_test: SkipSlow
 dart/slow_path_shared_stub_test: SkipSlow # Too slow with --shared-slow-path-triggers-gc flag and not relevant outside precompiled.
 dart/spawn_infinite_loop_test: Skip # We can shutdown an isolate before it reloads.
 dart/spawn_shutdown_test: Skip # We can shutdown an isolate before it reloads.
 dart/stack_overflow_shared_test: SkipSlow # Too slow with --shared-slow-path-triggers-gc flag and not relevant outside precompiled.
+dart/type_feedback_test: Pass, Slow
 
-[ $builder_tag == obfuscated && $compiler == dartkp ]
-dart/optimized_stacktrace_line_and_column_test: SkipByDesign # Looks for filenames in stacktrace output
-dart/optimized_stacktrace_line_test: SkipByDesign # Looks for filenames in stacktrace output
-
-[ $compiler != dartkp || $arch == arm || $arch == arm64 ]
-dart/v8_snapshot_profile_writer_test: SkipByDesign # Only relevant for AOT. Doesn't work in cross-compilation (has to run on the host).
-
-[ $compiler == dartkp ]
-dart/v8_snapshot_profile_writer_test: Pass, Slow # Can be slow due to re-invoking the precompiler.
-
-[ $compiler == dartkb ]
-cc/*: Skip # Bytecode modes are not properly hooked up in run_vm_tests.
-
-[ $mode == debug ]
-cc/IRTest_TypedDataAOT_FunctionalGetSet: Skip # run_vm_tests contains JIT/AOT but FLAG_precompiled_mode is not always correct. This causes this test to fail in debug mode, hitting an assertion. See http://dartbug.com/36521
+[ $hot_reload || $hot_reload_rollback || $arch != arm && $arch != simarm && $arch != x64 || $compiler != dartk && $compiler != dartkp ]
+dart/entrypoints/*: SkipByDesign # Only supported in the Dart 2 JIT and AOT, and test optimizations - hence disabled on hotreload bots.
diff --git a/runtime/tools/dartfuzz/dartfuzz_test.dart b/runtime/tools/dartfuzz/dartfuzz_test.dart
index 5bdaadc..b41bb37 100644
--- a/runtime/tools/dartfuzz/dartfuzz_test.dart
+++ b/runtime/tools/dartfuzz/dartfuzz_test.dart
@@ -534,14 +534,10 @@
     'jit-debug-x64',
     'jit-debug-arm32',
     'jit-debug-arm64',
-    'jit-debug-dbc',
-    'jit-debug-dbc64',
     'jit-ia32',
     'jit-x64',
     'jit-arm32',
     'jit-arm64',
-    'jit-dbc',
-    'jit-dbc64',
     'aot-debug-x64',
     'aot-x64',
     'kbc-int-debug-x64',
@@ -554,6 +550,11 @@
 
   // Modes not used on cluster runs because they have outstanding issues.
   static const List<String> nonClusterModes = [
+    // Deprecated.
+    'jit-debug-dbc',
+    'jit-debug-dbc64',
+    'jit-dbc',
+    'jit-dbc64',
     // Times out often:
     'aot-debug-arm32',
     'aot-debug-arm64',
@@ -586,6 +587,8 @@
         help: 'number of shards used in cluster run', defaultsTo: '1')
     ..addOption('shard', help: 'shard id in cluster run', defaultsTo: '1')
     ..addOption('output_directory',
+        help: 'path to output (ignored)', defaultsTo: null)
+    ..addOption('output-directory',
         help: 'path to output (ignored)', defaultsTo: null);
 
   // Starts fuzz testing session.
diff --git a/runtime/vm/ast.h b/runtime/vm/ast.h
deleted file mode 100644
index c72e582..0000000
--- a/runtime/vm/ast.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright (c) 2012, 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.
-
-#ifndef RUNTIME_VM_AST_H_
-#define RUNTIME_VM_AST_H_
-
-#include "platform/assert.h"
-#include "vm/allocation.h"
-#include "vm/growable_array.h"
-#include "vm/object.h"
-#include "vm/scopes.h"
-#include "vm/token.h"
-#include "vm/token_position.h"
-
-namespace dart {
-
-#define FOR_EACH_NODE(V)                                                       \
-  V(Sequence)                                                                  \
-
-#define FORWARD_DECLARATION(BaseName) class BaseName##Node;
-FOR_EACH_NODE(FORWARD_DECLARATION)
-#undef FORWARD_DECLARATION
-
-#define DECLARE_COMMON_NODE_FUNCTIONS(type)                                    \
-  virtual type* As##type() { return this; }
-
-class AstNode : public ZoneAllocated {
- public:
-  explicit AstNode(TokenPosition token_pos) : token_pos_(token_pos) {
-    ASSERT(!token_pos_.IsClassifying() ||
-           (token_pos_ == TokenPosition::kMethodExtractor));
-  }
-  virtual ~AstNode() {}
-
-  TokenPosition token_pos() const { return token_pos_; }
-
-#define AST_TYPE_CHECK(BaseName)                                               \
-  bool Is##BaseName##Node() { return As##BaseName##Node() != NULL; }           \
-  virtual BaseName##Node* As##BaseName##Node() { return NULL; }
-
-  FOR_EACH_NODE(AST_TYPE_CHECK)
-#undef AST_TYPE_CHECK
-
- protected:
-  friend class ParsedFunction;
-
- private:
-  const TokenPosition token_pos_;
-  DISALLOW_COPY_AND_ASSIGN(AstNode);
-};
-
-class SequenceNode : public AstNode {
- public:
-  SequenceNode(TokenPosition token_pos, LocalScope* scope)
-      : AstNode(token_pos), scope_(scope), nodes_(4), label_(NULL) {}
-
-  LocalScope* scope() const { return scope_; }
-
-  SourceLabel* label() const { return label_; }
-  void set_label(SourceLabel* value) { label_ = value; }
-
-  DECLARE_COMMON_NODE_FUNCTIONS(SequenceNode);
-
- private:
-  LocalScope* scope_;
-  GrowableArray<AstNode*> nodes_;
-  SourceLabel* label_;
-
-  DISALLOW_COPY_AND_ASSIGN(SequenceNode);
-};
-
-}  // namespace dart
-
-#undef DECLARE_COMMON_NODE_FUNCTIONS
-
-#endif  // RUNTIME_VM_AST_H_
diff --git a/runtime/vm/bootstrap.cc b/runtime/vm/bootstrap.cc
index 8c05417..1b38ff7 100644
--- a/runtime/vm/bootstrap.cc
+++ b/runtime/vm/bootstrap.cc
@@ -99,18 +99,10 @@
 
   // Load the bootstrap libraries in order (see object_store.h).
   Library& library = Library::Handle(zone);
-  String& dart_name = String::Handle(zone);
   for (intptr_t i = 0; i < kBootstrapLibraryCount; ++i) {
     ObjectStore::BootstrapLibraryId id = bootstrap_libraries[i].index;
     library = isolate->object_store()->bootstrap_library(id);
-    dart_name = library.url();
-    for (intptr_t j = 0; j < program->library_count(); ++j) {
-      const String& kernel_name = loader.LibraryUri(j);
-      if (kernel_name.Equals(dart_name)) {
-        loader.LoadLibrary(j);
-        break;
-      }
-    }
+    loader.LoadLibrary(library);
   }
 
   // Finish bootstrapping, including class finalization.
@@ -125,8 +117,8 @@
   }
 
   // The builtin library should be registered with the VM.
-  dart_name = String::New("dart:_builtin");
-  library = Library::LookupLibrary(thread, dart_name);
+  const auto& dart_builtin = String::Handle(zone, String::New("dart:_builtin"));
+  library = Library::LookupLibrary(thread, dart_builtin);
   isolate->object_store()->set_builtin_library(library);
 
   return Error::null();
diff --git a/runtime/vm/class_finalizer.cc b/runtime/vm/class_finalizer.cc
index 5b3fe6d..688f192 100644
--- a/runtime/vm/class_finalizer.cc
+++ b/runtime/vm/class_finalizer.cc
@@ -1066,26 +1066,30 @@
   }
   cls.set_is_type_finalized();
 
+  RegisterClassInHierarchy(thread->zone(), cls);
+}
+
+void ClassFinalizer::RegisterClassInHierarchy(Zone* zone, const Class& cls) {
+  auto& type = AbstractType::Handle(zone, cls.super_type());
+  auto& other_cls = Class::Handle(zone);
   // Add this class to the direct subclasses of the superclass, unless the
   // superclass is Object.
-  if (!super_type.IsNull() && !super_type.IsObjectType()) {
-    ASSERT(!super_class.IsNull());
-    super_class.AddDirectSubclass(cls);
+  if (!type.IsNull() && !type.IsObjectType()) {
+    other_cls = cls.SuperClass();
+    ASSERT(!other_cls.IsNull());
+    other_cls.AddDirectSubclass(cls);
   }
 
   // Add this class as an implementor to the implemented interface's type
   // classes.
-  Zone* zone = thread->zone();
-  auto& interface_class = Class::Handle(zone);
-  const intptr_t mixin_index = cls.is_transformed_mixin_application()
-                                   ? interface_types.Length() - 1
-                                   : -1;
-  for (intptr_t i = 0; i < interface_types.Length(); ++i) {
-    interface_type ^= interface_types.At(i);
-    interface_class = interface_type.type_class();
-    MarkImplemented(thread->zone(), interface_class);
-    interface_class.AddDirectImplementor(cls,
-                                         /* is_mixin = */ i == mixin_index);
+  const auto& interfaces = Array::Handle(zone, cls.interfaces());
+  const intptr_t mixin_index =
+      cls.is_transformed_mixin_application() ? interfaces.Length() - 1 : -1;
+  for (intptr_t i = 0; i < interfaces.Length(); ++i) {
+    type ^= interfaces.At(i);
+    other_cls = type.type_class();
+    MarkImplemented(zone, other_cls);
+    other_cls.AddDirectImplementor(cls, /* is_mixin = */ i == mixin_index);
   }
 }
 
@@ -1113,9 +1117,14 @@
 
 #if !defined(DART_PRECOMPILED_RUNTIME)
   // If loading from a kernel, make sure that the class is fully loaded.
-  ASSERT(cls.IsTopLevel() || (cls.kernel_offset() > 0));
+  ASSERT(cls.IsTopLevel() || cls.is_declared_in_bytecode() ||
+         (cls.kernel_offset() > 0));
   if (!cls.is_loaded()) {
-    kernel::KernelLoader::FinishLoading(cls);
+    if (cls.is_declared_in_bytecode()) {
+      kernel::BytecodeReader::FinishClassLoading(cls);
+    } else {
+      kernel::KernelLoader::FinishLoading(cls);
+    }
     if (cls.is_finalized()) {
       return;
     }
@@ -1180,6 +1189,14 @@
   ASSERT(Thread::Current()->IsMutatorThread());
   LongJumpScope jump;
   if (setjmp(*jump.Set()) == 0) {
+#if !defined(DART_PRECOMPILED_RUNTIME)
+    if (!cls.is_declaration_loaded()) {
+      // Loading of class declaration can be postponed until needed
+      // if class comes from bytecode.
+      ASSERT(cls.is_declared_in_bytecode());
+      kernel::BytecodeReader::LoadClassDeclaration(cls);
+    }
+#endif
     // TODO(36584) : We expect is_type_finalized to be true for all classes
     // here, but with eager reading of the constant table we get into
     // situations where we see classes whose types have not been finalized yet,
@@ -1241,7 +1258,7 @@
                                       ->object_store()
                                       ->pending_unevaluated_const_fields());
 
-  ASSERT(enum_cls.kernel_offset() > 0);
+  ASSERT(enum_cls.is_declared_in_bytecode() || enum_cls.kernel_offset() > 0);
   Error& error = Error::Handle(zone);
   for (intptr_t i = 0; i < fields.Length(); i++) {
     field = Field::RawCast(fields.At(i));
@@ -1419,7 +1436,7 @@
       continue;
     }
     cls = table->At(cid);
-    if (cls.is_patch()) {
+    if (cls.is_patch() || !cls.is_declaration_loaded()) {
       continue;
     }
     if (cls.SuperClass() == I->object_store()->object_class()) {
diff --git a/runtime/vm/class_finalizer.h b/runtime/vm/class_finalizer.h
index 91292e9..4a82e1c 100644
--- a/runtime/vm/class_finalizer.h
+++ b/runtime/vm/class_finalizer.h
@@ -57,6 +57,9 @@
   // is an anonymous top level class).
   static void FinalizeTypesInClass(const Class& cls);
 
+  // Register class in the lists of direct subclasses and direct implementors.
+  static void RegisterClassInHierarchy(Zone* zone, const Class& cls);
+
   // Finalize the class including its fields and functions.
   static void FinalizeClass(const Class& cls);
 
diff --git a/runtime/vm/class_id.h b/runtime/vm/class_id.h
index f95d77f..4e23dea 100644
--- a/runtime/vm/class_id.h
+++ b/runtime/vm/class_id.h
@@ -36,6 +36,7 @@
   V(ExceptionHandlers)                                                         \
   V(Context)                                                                   \
   V(ContextScope)                                                              \
+  V(ParameterTypeCheck)                                                        \
   V(SingleTargetCache)                                                         \
   V(UnlinkedCall)                                                              \
   V(ICData)                                                                    \
diff --git a/runtime/vm/class_table.cc b/runtime/vm/class_table.cc
index 3d4ab6c..98e1421 100644
--- a/runtime/vm/class_table.cc
+++ b/runtime/vm/class_table.cc
@@ -367,42 +367,67 @@
 }
 
 void ClassHeapStats::PrintToJSONObject(const Class& cls,
-                                       JSONObject* obj) const {
+                                       JSONObject* obj,
+                                       bool internal) const {
   if (!FLAG_support_service) {
     return;
   }
   obj->AddProperty("type", "ClassHeapStats");
   obj->AddProperty("class", cls);
-  {
-    JSONArray new_stats(obj, "new");
-    new_stats.AddValue(pre_gc.new_count);
-    new_stats.AddValue(pre_gc.new_size + pre_gc.new_external_size);
-    new_stats.AddValue(post_gc.new_count);
-    new_stats.AddValue(post_gc.new_size + post_gc.new_external_size);
-    new_stats.AddValue(recent.new_count);
-    new_stats.AddValue(recent.new_size + recent.new_external_size);
-    new_stats.AddValue64(accumulated.new_count + recent.new_count -
-                         last_reset.new_count);
-    new_stats.AddValue64(accumulated.new_size + accumulated.new_external_size +
-                         recent.new_size + recent.new_external_size -
-                         last_reset.new_size - last_reset.new_external_size);
+  int64_t accumulated_new =
+      accumulated.new_count + recent.new_count - last_reset.new_count;
+  int64_t accumulated_old =
+      accumulated.old_count + recent.old_count - last_reset.old_count;
+  int64_t accumulated_new_size =
+      accumulated.new_size + accumulated.new_external_size + recent.new_size +
+      recent.new_external_size - last_reset.new_size -
+      last_reset.new_external_size;
+  int64_t accumulated_old_size =
+      accumulated.old_size + accumulated.old_external_size + recent.old_size +
+      recent.old_external_size - last_reset.old_size -
+      last_reset.old_external_size;
+  int64_t instances_new = post_gc.new_count + recent.new_count;
+  int64_t instances_old = post_gc.old_count + recent.old_count;
+  int64_t live_after_gc_size_new = post_gc.new_size + post_gc.new_external_size;
+  int64_t live_after_gc_size_old = post_gc.old_size + post_gc.old_external_size;
+  int64_t allocated_since_gc_size_new =
+      recent.new_size + recent.new_external_size;
+  int64_t allocated_since_gc_size_old =
+      recent.old_size + recent.old_external_size;
+  int64_t bytes_current = live_after_gc_size_new + live_after_gc_size_old +
+                          allocated_since_gc_size_new +
+                          allocated_since_gc_size_old;
+  if (internal) {
+    {
+      JSONArray new_stats(obj, "_new");
+      new_stats.AddValue(pre_gc.new_count);
+      new_stats.AddValue(pre_gc.new_size + pre_gc.new_external_size);
+      new_stats.AddValue(post_gc.new_count);
+      new_stats.AddValue64(live_after_gc_size_new);
+      new_stats.AddValue(recent.new_count);
+      new_stats.AddValue64(allocated_since_gc_size_new);
+      new_stats.AddValue64(accumulated_new);
+      new_stats.AddValue64(accumulated_new_size);
+    }
+    {
+      JSONArray old_stats(obj, "_old");
+      old_stats.AddValue(pre_gc.old_count);
+      old_stats.AddValue(pre_gc.old_size + pre_gc.old_external_size);
+      old_stats.AddValue(post_gc.old_count);
+      old_stats.AddValue64(live_after_gc_size_old);
+      old_stats.AddValue(recent.old_count);
+      old_stats.AddValue64(allocated_since_gc_size_old);
+      old_stats.AddValue64(accumulated_old);
+      old_stats.AddValue64(accumulated_old_size);
+    }
+    obj->AddProperty("_promotedInstances", promoted_count);
+    obj->AddProperty("_promotedBytes", promoted_size);
   }
-  {
-    JSONArray old_stats(obj, "old");
-    old_stats.AddValue(pre_gc.old_count);
-    old_stats.AddValue(pre_gc.old_size + pre_gc.old_external_size);
-    old_stats.AddValue(post_gc.old_count);
-    old_stats.AddValue(post_gc.old_size + post_gc.old_external_size);
-    old_stats.AddValue(recent.old_count);
-    old_stats.AddValue(recent.old_size + recent.old_external_size);
-    old_stats.AddValue64(accumulated.old_count + recent.old_count -
-                         last_reset.old_count);
-    old_stats.AddValue64(accumulated.old_size + accumulated.old_external_size +
-                         recent.old_size + recent.old_external_size -
-                         last_reset.old_size - last_reset.old_external_size);
-  }
-  obj->AddProperty("promotedInstances", promoted_count);
-  obj->AddProperty("promotedBytes", promoted_size);
+  obj->AddProperty64("instancesAccumulated", accumulated_new + accumulated_old);
+  obj->AddProperty64("accumulatedSize",
+                     accumulated_new_size + accumulated_old_size);
+  obj->AddProperty64("instancesCurrent", instances_new + instances_old);
+  obj->AddProperty64("bytesCurrent", bytes_current);
 }
 
 void ClassTable::UpdateAllocatedOldGC(intptr_t cid, intptr_t size) {
@@ -486,7 +511,7 @@
   return class_offset + size_field_offset;
 }
 
-void ClassTable::AllocationProfilePrintJSON(JSONStream* stream) {
+void ClassTable::AllocationProfilePrintJSON(JSONStream* stream, bool internal) {
   if (!FLAG_support_service) {
     return;
   }
@@ -506,11 +531,17 @@
                      isolate->last_allocationprofile_gc_timestamp());
   }
 
-  {
-    JSONObject heaps(&obj, "heaps");
+  if (internal) {
+    JSONObject heaps(&obj, "_heaps");
     { heap->PrintToJSONObject(Heap::kNew, &heaps); }
     { heap->PrintToJSONObject(Heap::kOld, &heaps); }
   }
+
+  {
+    JSONObject memory(&obj, "memoryUsage");
+    { heap->PrintMemoryUsageJSON(&memory); }
+  }
+
   {
     JSONArray arr(&obj, "members");
     Class& cls = Class::Handle();
@@ -519,7 +550,7 @@
       if (stats != NULL) {
         JSONObject obj(&arr);
         cls = At(i);
-        stats->PrintToJSONObject(cls, &obj);
+        stats->PrintToJSONObject(cls, &obj, internal);
       }
     }
   }
diff --git a/runtime/vm/class_table.h b/runtime/vm/class_table.h
index c851ef9..df677a5 100644
--- a/runtime/vm/class_table.h
+++ b/runtime/vm/class_table.h
@@ -157,7 +157,9 @@
   void UpdatePromotedAfterNewGC();
   void UpdateSize(intptr_t instance_size);
 #ifndef PRODUCT
-  void PrintToJSONObject(const Class& cls, JSONObject* obj) const;
+  void PrintToJSONObject(const Class& cls,
+                         JSONObject* obj,
+                         bool internal) const;
 #endif
   void Verify();
 
@@ -311,7 +313,7 @@
 
   ClassHeapStats* StatsWithUpdatedSize(intptr_t cid);
 
-  void AllocationProfilePrintJSON(JSONStream* stream);
+  void AllocationProfilePrintJSON(JSONStream* stream, bool internal);
   void ResetAllocationAccumulators();
 
   void PrintToJSONObject(JSONObject* object);
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index a71b9cb..eade6cc 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -6,6 +6,7 @@
 
 #include "platform/assert.h"
 #include "vm/bootstrap.h"
+#include "vm/class_id.h"
 #include "vm/compiler/backend/code_statistics.h"
 #include "vm/compiler/frontend/bytecode_reader.h"
 #include "vm/compiler/relocation.h"
@@ -177,7 +178,7 @@
     }
     s->WriteCid(class_id);
     if (s->kind() != Snapshot::kFullAOT) {
-      s->Write<int32_t>(cls->ptr()->kernel_offset_);
+      s->Write<uint32_t>(cls->ptr()->binary_declaration_);
     }
     s->Write<int32_t>(cls->ptr()->instance_size_in_words_);
     s->Write<int32_t>(cls->ptr()->next_field_offset_in_words_);
@@ -232,7 +233,7 @@
       cls->ptr()->id_ = class_id;
 #if !defined(DART_PRECOMPILED_RUNTIME)
       if (d->kind() != Snapshot::kFullAOT) {
-        cls->ptr()->kernel_offset_ = d->Read<int32_t>();
+        cls->ptr()->binary_declaration_ = d->Read<uint32_t>();
       }
 #endif
       if (!RawObject::IsInternalVMdefinedClassId(class_id)) {
@@ -263,7 +264,7 @@
       cls->ptr()->id_ = class_id;
 #if !defined(DART_PRECOMPILED_RUNTIME)
       if (d->kind() != Snapshot::kFullAOT) {
-        cls->ptr()->kernel_offset_ = d->Read<int32_t>();
+        cls->ptr()->binary_declaration_ = d->Read<uint32_t>();
       }
 #endif
       cls->ptr()->instance_size_in_words_ = d->Read<int32_t>();
@@ -768,6 +769,74 @@
 };
 
 #if !defined(DART_PRECOMPILED_RUNTIME)
+class FfiTrampolineDataSerializationCluster : public SerializationCluster {
+ public:
+  FfiTrampolineDataSerializationCluster()
+      : SerializationCluster("FfiTrampolineData") {}
+  ~FfiTrampolineDataSerializationCluster() {}
+
+  void Trace(Serializer* s, RawObject* object) {
+    RawFfiTrampolineData* data = FfiTrampolineData::RawCast(object);
+    objects_.Add(data);
+    PushFromTo(data);
+  }
+
+  void WriteAlloc(Serializer* s) {
+    s->WriteCid(kFfiTrampolineDataCid);
+    const intptr_t count = objects_.length();
+    s->WriteUnsigned(count);
+    for (intptr_t i = 0; i < count; i++) {
+      s->AssignRef(objects_[i]);
+    }
+  }
+
+  void WriteFill(Serializer* s) {
+    intptr_t count = objects_.length();
+    for (intptr_t i = 0; i < count; i++) {
+      RawFfiTrampolineData* const data = objects_[i];
+      AutoTraceObject(data);
+      WriteFromTo(data);
+
+      // TODO(37295): FFI callbacks shouldn't be written to a snapshot. They
+      // should only be referenced by the callback registry in Thread.
+      ASSERT(data->ptr()->callback_id_ == 0);
+    }
+  }
+
+ private:
+  GrowableArray<RawFfiTrampolineData*> objects_;
+};
+#endif  // !DART_PRECOMPILED_RUNTIME
+
+class FfiTrampolineDataDeserializationCluster : public DeserializationCluster {
+ public:
+  FfiTrampolineDataDeserializationCluster() {}
+  ~FfiTrampolineDataDeserializationCluster() {}
+
+  void ReadAlloc(Deserializer* d) {
+    start_index_ = d->next_index();
+    PageSpace* old_space = d->heap()->old_space();
+    intptr_t count = d->ReadUnsigned();
+    for (intptr_t i = 0; i < count; i++) {
+      d->AssignRef(
+          AllocateUninitialized(old_space, FfiTrampolineData::InstanceSize()));
+    }
+    stop_index_ = d->next_index();
+  }
+
+  void ReadFill(Deserializer* d) {
+    for (intptr_t id = start_index_; id < stop_index_; id++) {
+      RawFfiTrampolineData* data =
+          reinterpret_cast<RawFfiTrampolineData*>(d->Ref(id));
+      Deserializer::InitializeHeader(data, kFfiTrampolineDataCid,
+                                     FfiTrampolineData::InstanceSize());
+      ReadFromTo(data);
+      data->ptr()->callback_id_ = 0;
+    }
+  }
+};
+
+#if !defined(DART_PRECOMPILED_RUNTIME)
 class RedirectionDataSerializationCluster : public SerializationCluster {
  public:
   RedirectionDataSerializationCluster()
@@ -1107,7 +1176,7 @@
       s->Write<bool>(lib->ptr()->is_dart_scheme_);
       s->Write<bool>(lib->ptr()->debuggable_);
       if (s->kind() != Snapshot::kFullAOT) {
-        s->Write<int32_t>(lib->ptr()->kernel_offset_);
+        s->Write<uint32_t>(lib->ptr()->binary_declaration_);
       }
     }
   }
@@ -1147,7 +1216,7 @@
       lib->ptr()->is_in_fullsnapshot_ = true;
 #if !defined(DART_PRECOMPILED_RUNTIME)
       if (d->kind() != Snapshot::kFullAOT) {
-        lib->ptr()->kernel_offset_ = d->Read<int32_t>();
+        lib->ptr()->binary_declaration_ = d->Read<uint32_t>();
       }
 #endif
     }
@@ -1295,7 +1364,7 @@
       array = info.bytecode_component();
       if (!array.IsNull()) {
         kernel::BytecodeReader::UseBytecodeVersion(
-            kernel::BytecodeComponentData(array).GetVersion());
+            kernel::BytecodeComponentData(&array).GetVersion());
       }
     }
   }
@@ -1328,7 +1397,6 @@
       s->Push(code->ptr()->deopt_info_array_);
       s->Push(code->ptr()->static_calls_target_table_);
     }
-    NOT_IN_PRODUCT(s->Push(code->ptr()->await_token_positions_));
     NOT_IN_PRODUCT(s->Push(code->ptr()->return_address_metadata_));
   }
 
@@ -1390,7 +1458,6 @@
         WriteField(code, deopt_info_array_);
         WriteField(code, static_calls_target_table_);
       }
-      NOT_IN_PRODUCT(WriteField(code, await_token_positions_));
       NOT_IN_PRODUCT(WriteField(code, return_address_metadata_));
 
       s->Write<int32_t>(code->ptr()->state_bits_);
@@ -1505,8 +1572,6 @@
 #endif  // !DART_PRECOMPILED_RUNTIME
 
 #if !defined(PRODUCT)
-      code->ptr()->await_token_positions_ =
-          reinterpret_cast<RawArray*>(d->ReadRef());
       code->ptr()->return_address_metadata_ = d->ReadRef();
       code->ptr()->var_descriptors_ = LocalVarDescriptors::null();
       code->ptr()->comments_ = Array::null();
@@ -2104,6 +2169,71 @@
 };
 
 #if !defined(DART_PRECOMPILED_RUNTIME)
+class ParameterTypeCheckSerializationCluster : public SerializationCluster {
+ public:
+  ParameterTypeCheckSerializationCluster()
+      : SerializationCluster("ParameterTypeCheck") {}
+  ~ParameterTypeCheckSerializationCluster() {}
+
+  void Trace(Serializer* s, RawObject* object) {
+    RawParameterTypeCheck* unlinked = ParameterTypeCheck::RawCast(object);
+    objects_.Add(unlinked);
+    PushFromTo(unlinked);
+  }
+
+  void WriteAlloc(Serializer* s) {
+    s->WriteCid(kParameterTypeCheckCid);
+    intptr_t count = objects_.length();
+    s->WriteUnsigned(count);
+    for (intptr_t i = 0; i < count; i++) {
+      RawParameterTypeCheck* check = objects_[i];
+      s->AssignRef(check);
+    }
+  }
+
+  void WriteFill(Serializer* s) {
+    intptr_t count = objects_.length();
+    for (intptr_t i = 0; i < count; i++) {
+      RawParameterTypeCheck* check = objects_[i];
+      s->Write<intptr_t>(check->ptr()->index_);
+      WriteFromTo(check);
+    }
+  }
+
+ private:
+  GrowableArray<RawParameterTypeCheck*> objects_;
+};
+#endif  // !DART_PRECOMPILED_RUNTIME
+
+class ParameterTypeCheckDeserializationCluster : public DeserializationCluster {
+ public:
+  ParameterTypeCheckDeserializationCluster() {}
+  ~ParameterTypeCheckDeserializationCluster() {}
+
+  void ReadAlloc(Deserializer* d) {
+    start_index_ = d->next_index();
+    PageSpace* old_space = d->heap()->old_space();
+    intptr_t count = d->ReadUnsigned();
+    for (intptr_t i = 0; i < count; i++) {
+      d->AssignRef(
+          AllocateUninitialized(old_space, ParameterTypeCheck::InstanceSize()));
+    }
+    stop_index_ = d->next_index();
+  }
+
+  void ReadFill(Deserializer* d) {
+    for (intptr_t id = start_index_; id < stop_index_; id++) {
+      RawParameterTypeCheck* check =
+          reinterpret_cast<RawParameterTypeCheck*>(d->Ref(id));
+      Deserializer::InitializeHeader(check, kParameterTypeCheckCid,
+                                     ParameterTypeCheck::InstanceSize());
+      check->ptr()->index_ = d->Read<intptr_t>();
+      ReadFromTo(check);
+    }
+  }
+};
+
+#if !defined(DART_PRECOMPILED_RUNTIME)
 class UnlinkedCallSerializationCluster : public SerializationCluster {
  public:
   UnlinkedCallSerializationCluster() : SerializationCluster("UnlinkedCall") {}
@@ -4243,6 +4373,8 @@
       return new (Z) SignatureDataSerializationCluster();
     case kRedirectionDataCid:
       return new (Z) RedirectionDataSerializationCluster();
+    case kFfiTrampolineDataCid:
+      return new (Z) FfiTrampolineDataSerializationCluster();
     case kFieldCid:
       return new (Z) FieldSerializationCluster();
     case kScriptCid:
@@ -4275,6 +4407,8 @@
       return new (Z) ContextSerializationCluster();
     case kContextScopeCid:
       return new (Z) ContextScopeSerializationCluster();
+    case kParameterTypeCheckCid:
+      return new (Z) ParameterTypeCheckSerializationCluster();
     case kUnlinkedCallCid:
       return new (Z) UnlinkedCallSerializationCluster();
     case kICDataCid:
@@ -4712,6 +4846,8 @@
                 "<invoke field>");
   AddBaseObject(Object::nsm_dispatcher_bytecode().raw(), "Bytecode",
                 "<nsm dispatcher>");
+  AddBaseObject(Object::dynamic_invocation_forwarder_bytecode().raw(),
+                "Bytecode", "<dyn forwarder>");
 
   for (intptr_t i = 0; i < ArgumentsDescriptor::kCachedDescriptorCount; i++) {
     AddBaseObject(ArgumentsDescriptor::cached_args_descriptors_[i],
@@ -4875,6 +5011,8 @@
       return new (Z) SignatureDataDeserializationCluster();
     case kRedirectionDataCid:
       return new (Z) RedirectionDataDeserializationCluster();
+    case kFfiTrampolineDataCid:
+      return new (Z) FfiTrampolineDataDeserializationCluster();
     case kFieldCid:
       return new (Z) FieldDeserializationCluster();
     case kScriptCid:
@@ -4905,6 +5043,8 @@
       return new (Z) ContextDeserializationCluster();
     case kContextScopeCid:
       return new (Z) ContextScopeDeserializationCluster();
+    case kParameterTypeCheckCid:
+      return new (Z) ParameterTypeCheckDeserializationCluster();
     case kUnlinkedCallCid:
       return new (Z) UnlinkedCallDeserializationCluster();
     case kICDataCid:
@@ -5174,6 +5314,7 @@
   AddBaseObject(Object::invoke_closure_bytecode().raw());
   AddBaseObject(Object::invoke_field_bytecode().raw());
   AddBaseObject(Object::nsm_dispatcher_bytecode().raw());
+  AddBaseObject(Object::dynamic_invocation_forwarder_bytecode().raw());
 
   for (intptr_t i = 0; i < ArgumentsDescriptor::kCachedDescriptorCount; i++) {
     AddBaseObject(ArgumentsDescriptor::cached_args_descriptors_[i]);
diff --git a/runtime/vm/code_patcher.h b/runtime/vm/code_patcher.h
index 239a69d..ceacea6 100644
--- a/runtime/vm/code_patcher.h
+++ b/runtime/vm/code_patcher.h
@@ -49,12 +49,18 @@
   // in given code.
   static RawCode* GetStaticCallTargetAt(uword return_address, const Code& code);
 
-  // Get instance call information.  Returns the call target and sets each
-  // of the output parameters ic_data and arguments_descriptor if they are
-  // non-NULL.
+  // Get instance call information. Returns the call target and sets the output
+  // parameter data if non-NULL.
   static RawCode* GetInstanceCallAt(uword return_address,
-                                    const Code& code,
-                                    ICData* ic_data);
+                                    const Code& caller_code,
+                                    Object* data);
+
+  // Change the state of an instance call by patching the corresponding object
+  // pool entries (non-IA32) or instructions (IA32).
+  static void PatchInstanceCallAt(uword return_address,
+                                  const Code& caller_code,
+                                  const Object& data,
+                                  const Code& target);
 
   // Return target of an unoptimized static call and its ICData object
   // (calls target via a stub).
@@ -79,22 +85,22 @@
 
 #if defined(TARGET_ARCH_DBC)
   static NativeFunctionWrapper GetNativeCallAt(uword return_address,
-                                               const Code& code,
+                                               const Code& caller_code,
                                                NativeFunction* target);
 #else
   static RawCode* GetNativeCallAt(uword return_address,
-                                  const Code& code,
+                                  const Code& caller_code,
                                   NativeFunction* target);
 #endif
 
 #if defined(TARGET_ARCH_DBC)
   static void PatchNativeCallAt(uword return_address,
-                                const Code& code,
+                                const Code& caller_code,
                                 NativeFunction target,
                                 NativeFunctionWrapper trampoline);
 #else
   static void PatchNativeCallAt(uword return_address,
-                                const Code& code,
+                                const Code& caller_code,
                                 NativeFunction target,
                                 const Code& trampoline);
 #endif
diff --git a/runtime/vm/code_patcher_arm.cc b/runtime/vm/code_patcher_arm.cc
index 362b8b0..024a6ab 100644
--- a/runtime/vm/code_patcher_arm.cc
+++ b/runtime/vm/code_patcher_arm.cc
@@ -33,23 +33,33 @@
 }
 
 RawCode* CodePatcher::GetInstanceCallAt(uword return_address,
-                                        const Code& code,
-                                        ICData* ic_data) {
-  ASSERT(code.ContainsInstructionAt(return_address));
-  CallPattern call(return_address, code);
-  if (ic_data != NULL) {
-    *ic_data = call.IcData();
+                                        const Code& caller_code,
+                                        Object* data) {
+  ASSERT(caller_code.ContainsInstructionAt(return_address));
+  ICCallPattern call(return_address, caller_code);
+  if (data != NULL) {
+    *data = call.Data();
   }
   return call.TargetCode();
 }
 
+void CodePatcher::PatchInstanceCallAt(uword return_address,
+                                      const Code& caller_code,
+                                      const Object& data,
+                                      const Code& target) {
+  ASSERT(caller_code.ContainsInstructionAt(return_address));
+  ICCallPattern call(return_address, caller_code);
+  call.SetData(data);
+  call.SetTargetCode(target);
+}
+
 RawFunction* CodePatcher::GetUnoptimizedStaticCallAt(uword return_address,
-                                                     const Code& code,
+                                                     const Code& caller_code,
                                                      ICData* ic_data_result) {
-  ASSERT(code.ContainsInstructionAt(return_address));
-  CallPattern static_call(return_address, code);
+  ASSERT(caller_code.ContainsInstructionAt(return_address));
+  ICCallPattern static_call(return_address, caller_code);
   ICData& ic_data = ICData::Handle();
-  ic_data = static_call.IcData();
+  ic_data ^= static_call.Data();
   if (ic_data_result != NULL) {
     *ic_data_result = ic_data.raw();
   }
diff --git a/runtime/vm/code_patcher_arm64.cc b/runtime/vm/code_patcher_arm64.cc
index 7716090..ce3c4dc 100644
--- a/runtime/vm/code_patcher_arm64.cc
+++ b/runtime/vm/code_patcher_arm64.cc
@@ -68,23 +68,33 @@
 }
 
 RawCode* CodePatcher::GetInstanceCallAt(uword return_address,
-                                        const Code& code,
-                                        ICData* ic_data) {
-  ASSERT(code.ContainsInstructionAt(return_address));
-  CallPattern call(return_address, code);
-  if (ic_data != NULL) {
-    *ic_data = call.IcData();
+                                        const Code& caller_code,
+                                        Object* data) {
+  ASSERT(caller_code.ContainsInstructionAt(return_address));
+  ICCallPattern call(return_address, caller_code);
+  if (data != NULL) {
+    *data = call.Data();
   }
   return call.TargetCode();
 }
 
+void CodePatcher::PatchInstanceCallAt(uword return_address,
+                                      const Code& caller_code,
+                                      const Object& data,
+                                      const Code& target) {
+  ASSERT(caller_code.ContainsInstructionAt(return_address));
+  ICCallPattern call(return_address, caller_code);
+  call.SetData(data);
+  call.SetTargetCode(target);
+}
+
 RawFunction* CodePatcher::GetUnoptimizedStaticCallAt(uword return_address,
                                                      const Code& code,
                                                      ICData* ic_data_result) {
   ASSERT(code.ContainsInstructionAt(return_address));
-  CallPattern static_call(return_address, code);
+  ICCallPattern static_call(return_address, code);
   ICData& ic_data = ICData::Handle();
-  ic_data ^= static_call.IcData();
+  ic_data ^= static_call.Data();
   if (ic_data_result != NULL) {
     *ic_data_result = ic_data.raw();
   }
@@ -132,20 +142,20 @@
 }
 
 void CodePatcher::PatchNativeCallAt(uword return_address,
-                                    const Code& code,
+                                    const Code& caller_code,
                                     NativeFunction target,
                                     const Code& trampoline) {
-  ASSERT(code.ContainsInstructionAt(return_address));
-  NativeCallPattern call(return_address, code);
+  ASSERT(caller_code.ContainsInstructionAt(return_address));
+  NativeCallPattern call(return_address, caller_code);
   call.set_target(trampoline);
   call.set_native_function(target);
 }
 
 RawCode* CodePatcher::GetNativeCallAt(uword return_address,
-                                      const Code& code,
+                                      const Code& caller_code,
                                       NativeFunction* target) {
-  ASSERT(code.ContainsInstructionAt(return_address));
-  NativeCallPattern call(return_address, code);
+  ASSERT(caller_code.ContainsInstructionAt(return_address));
+  NativeCallPattern call(return_address, caller_code);
   *target = call.native_function();
   return call.target();
 }
diff --git a/runtime/vm/code_patcher_arm64_test.cc b/runtime/vm/code_patcher_arm64_test.cc
index c2437cf..a31c467 100644
--- a/runtime/vm/code_patcher_arm64_test.cc
+++ b/runtime/vm/code_patcher_arm64_test.cc
@@ -39,12 +39,22 @@
       ArgumentsDescriptor::New(kTypeArgsLen, kNumArgs, Object::null_array()));
   const ICData& ic_data = ICData::ZoneHandle(ICData::New(
       function, target_name, args_descriptor, 15, 1, ICData::kInstance));
+  const Code& stub = StubCode::OneArgCheckInlineCache();
 
   // Code accessing pp is generated, but not executed. Uninitialized pp is OK.
   __ set_constant_pool_allowed(true);
 
-  __ LoadObject(R5, ic_data);
-  __ BranchLinkPatchable(StubCode::OneArgCheckInlineCache());
+  ObjectPoolBuilder& op = __ object_pool_builder();
+  const intptr_t ic_data_index =
+      op.AddObject(ic_data, ObjectPool::Patchability::kPatchable);
+  const intptr_t stub_index =
+      op.AddObject(stub, ObjectPool::Patchability::kPatchable);
+  ASSERT((ic_data_index + 1) == stub_index);
+  __ LoadDoubleWordFromPoolOffset(R5, CODE_REG,
+                                  ObjectPool::element_offset(ic_data_index));
+  __ ldr(LR, FieldAddress(CODE_REG, Code::entry_point_offset(
+                                        Code::EntryKind::kMonomorphic)));
+  __ blr(LR);
   __ ret();
 }
 
diff --git a/runtime/vm/code_patcher_dbc.cc b/runtime/vm/code_patcher_dbc.cc
index 9c478ff..93bc87a 100644
--- a/runtime/vm/code_patcher_dbc.cc
+++ b/runtime/vm/code_patcher_dbc.cc
@@ -33,23 +33,33 @@
 }
 
 RawCode* CodePatcher::GetInstanceCallAt(uword return_address,
-                                        const Code& code,
-                                        ICData* ic_data) {
-  ASSERT(code.ContainsInstructionAt(return_address));
-  CallPattern call(return_address, code);
-  if (ic_data != NULL) {
-    *ic_data = call.IcData();
+                                        const Code& caller_code,
+                                        Object* cache) {
+  ASSERT(caller_code.ContainsInstructionAt(return_address));
+  CallPattern call(return_address, caller_code);
+  if (cache != NULL) {
+    *cache = call.Data();
   }
   return call.TargetCode();
 }
 
+void CodePatcher::PatchInstanceCallAt(uword return_address,
+                                      const Code& caller_code,
+                                      const Object& data,
+                                      const Code& target) {
+  ASSERT(caller_code.ContainsInstructionAt(return_address));
+  CallPattern call(return_address, caller_code);
+  call.SetData(data);
+  call.SetTargetCode(target);
+}
+
 RawFunction* CodePatcher::GetUnoptimizedStaticCallAt(uword return_address,
-                                                     const Code& code,
+                                                     const Code& caller_code,
                                                      ICData* ic_data_result) {
-  ASSERT(code.ContainsInstructionAt(return_address));
-  CallPattern static_call(return_address, code);
+  ASSERT(caller_code.ContainsInstructionAt(return_address));
+  CallPattern static_call(return_address, caller_code);
   ICData& ic_data = ICData::Handle();
-  ic_data ^= static_call.IcData();
+  ic_data ^= static_call.Data();
   if (ic_data_result != NULL) {
     *ic_data_result = ic_data.raw();
   }
@@ -91,10 +101,10 @@
 }
 
 NativeFunctionWrapper CodePatcher::GetNativeCallAt(uword return_address,
-                                                   const Code& code,
+                                                   const Code& caller_code,
                                                    NativeFunction* target) {
-  ASSERT(code.ContainsInstructionAt(return_address));
-  NativeCallPattern call(return_address, code);
+  ASSERT(caller_code.ContainsInstructionAt(return_address));
+  NativeCallPattern call(return_address, caller_code);
   *target = call.native_function();
   return call.target();
 }
diff --git a/runtime/vm/code_patcher_ia32.cc b/runtime/vm/code_patcher_ia32.cc
index ccc8c07..81fa738 100644
--- a/runtime/vm/code_patcher_ia32.cc
+++ b/runtime/vm/code_patcher_ia32.cc
@@ -72,17 +72,40 @@
   DISALLOW_IMPLICIT_CONSTRUCTORS(NativeCall);
 };
 
+// b9xxxxxxxx  mov ecx,<data>
+// bfyyyyyyyy  mov edi,<target>
+// ff5707      call [edi+<monomorphic-entry-offset>]
 class InstanceCall : public UnoptimizedCall {
  public:
   explicit InstanceCall(uword return_address)
       : UnoptimizedCall(return_address) {
 #if defined(DEBUG)
-    ICData& test_ic_data = ICData::Handle();
-    test_ic_data ^= ic_data();
-    ASSERT(test_ic_data.NumArgsTested() > 0);
+    Object& test_data = Object::Handle(data());
+    ASSERT(test_data.IsArray() || test_data.IsICData() ||
+           test_data.IsMegamorphicCache());
+    if (test_data.IsICData()) {
+      ASSERT(ICData::Cast(test_data).NumArgsTested() > 0);
+    }
 #endif  // DEBUG
   }
 
+  RawObject* data() const { return *reinterpret_cast<RawObject**>(start_ + 1); }
+  void set_data(const Object& data) const {
+    uword* cache_addr = reinterpret_cast<uword*>(start_ + 1);
+    uword imm = reinterpret_cast<uword>(data.raw());
+    *cache_addr = imm;
+  }
+
+  RawCode* target() const {
+    const uword imm = *reinterpret_cast<uword*>(start_ + 6);
+    return reinterpret_cast<RawCode*>(imm);
+  }
+  void set_target(const Code& target) const {
+    uword* target_addr = reinterpret_cast<uword*>(start_ + 6);
+    uword imm = reinterpret_cast<uword>(target.raw());
+    *target_addr = imm;
+  }
+
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(InstanceCall);
 };
@@ -168,20 +191,32 @@
 }
 
 RawCode* CodePatcher::GetInstanceCallAt(uword return_address,
-                                        const Code& code,
-                                        ICData* ic_data) {
-  ASSERT(code.ContainsInstructionAt(return_address));
+                                        const Code& caller_code,
+                                        Object* data) {
+  ASSERT(caller_code.ContainsInstructionAt(return_address));
   InstanceCall call(return_address);
-  if (ic_data != NULL) {
-    *ic_data ^= call.ic_data();
+  if (data != NULL) {
+    *data = call.data();
   }
-  return Code::null();
+  return call.target();
+}
+
+void CodePatcher::PatchInstanceCallAt(uword return_address,
+                                      const Code& caller_code,
+                                      const Object& data,
+                                      const Code& target) {
+  ASSERT(caller_code.ContainsInstructionAt(return_address));
+  const Instructions& instrs = Instructions::Handle(caller_code.instructions());
+  WritableInstructionsScope writable(instrs.PayloadStart(), instrs.Size());
+  InstanceCall call(return_address);
+  call.set_data(data);
+  call.set_target(target);
 }
 
 RawFunction* CodePatcher::GetUnoptimizedStaticCallAt(uword return_address,
-                                                     const Code& code,
+                                                     const Code& caller_code,
                                                      ICData* ic_data_result) {
-  ASSERT(code.ContainsInstructionAt(return_address));
+  ASSERT(caller_code.ContainsInstructionAt(return_address));
   UnoptimizedStaticCall static_call(return_address);
   ICData& ic_data = ICData::Handle();
   ic_data ^= static_call.ic_data();
@@ -214,14 +249,14 @@
 }
 
 void CodePatcher::PatchNativeCallAt(uword return_address,
-                                    const Code& code,
+                                    const Code& caller_code,
                                     NativeFunction target,
                                     const Code& trampoline) {
   UNREACHABLE();
 }
 
 RawCode* CodePatcher::GetNativeCallAt(uword return_address,
-                                      const Code& code,
+                                      const Code& caller_code,
                                       NativeFunction* target) {
   UNREACHABLE();
   return NULL;
diff --git a/runtime/vm/code_patcher_x64.cc b/runtime/vm/code_patcher_x64.cc
index f53cf2a..e67d187 100644
--- a/runtime/vm/code_patcher_x64.cc
+++ b/runtime/vm/code_patcher_x64.cc
@@ -77,8 +77,6 @@
 
   intptr_t argument_index() const { return argument_index_; }
 
-  RawObject* ic_data() const { return object_pool_.ObjectAt(argument_index()); }
-
   RawCode* target() const {
     Code& code = Code::Handle();
     code ^= object_pool_.ObjectAt(code_index_);
@@ -123,20 +121,29 @@
   InstanceCall(uword return_address, const Code& code)
       : UnoptimizedCall(return_address, code) {
 #if defined(DEBUG)
-    ICData& test_ic_data = ICData::Handle();
-    test_ic_data ^= ic_data();
-    ASSERT(test_ic_data.NumArgsTested() > 0);
+    Object& test_data = Object::Handle(data());
+    ASSERT(test_data.IsArray() || test_data.IsICData() ||
+           test_data.IsMegamorphicCache());
+    if (test_data.IsICData()) {
+      ASSERT(ICData::Cast(test_data).NumArgsTested() > 0);
+    }
 #endif  // DEBUG
   }
 
+  RawObject* data() const { return object_pool_.ObjectAt(argument_index()); }
+  void set_data(const Object& data) const {
+    ASSERT(data.IsArray() || data.IsICData() || data.IsMegamorphicCache());
+    object_pool_.SetObjectAt(argument_index(), data);
+  }
+
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(InstanceCall);
 };
 
 class UnoptimizedStaticCall : public UnoptimizedCall {
  public:
-  UnoptimizedStaticCall(uword return_address, const Code& code)
-      : UnoptimizedCall(return_address, code) {
+  UnoptimizedStaticCall(uword return_address, const Code& caller_code)
+      : UnoptimizedCall(return_address, caller_code) {
 #if defined(DEBUG)
     ICData& test_ic_data = ICData::Handle();
     test_ic_data ^= ic_data();
@@ -144,6 +151,8 @@
 #endif  // DEBUG
   }
 
+  RawObject* ic_data() const { return object_pool_.ObjectAt(argument_index()); }
+
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(UnoptimizedStaticCall);
 };
@@ -152,8 +161,8 @@
 // the object pool.
 class PoolPointerCall : public ValueObject {
  public:
-  explicit PoolPointerCall(uword return_address, const Code& code)
-      : object_pool_(ObjectPool::Handle(code.GetObjectPool())),
+  explicit PoolPointerCall(uword return_address, const Code& caller_code)
+      : object_pool_(ObjectPool::Handle(caller_code.GetObjectPool())),
         code_index_(-1) {
     uword pc = return_address;
 
@@ -424,25 +433,35 @@
 }
 
 RawCode* CodePatcher::GetInstanceCallAt(uword return_address,
-                                        const Code& code,
-                                        ICData* ic_data) {
-  ASSERT(code.ContainsInstructionAt(return_address));
-  InstanceCall call(return_address, code);
-  if (ic_data != NULL) {
-    *ic_data ^= call.ic_data();
+                                        const Code& caller_code,
+                                        Object* data) {
+  ASSERT(caller_code.ContainsInstructionAt(return_address));
+  InstanceCall call(return_address, caller_code);
+  if (data != NULL) {
+    *data = call.data();
   }
   return call.target();
 }
 
+void CodePatcher::PatchInstanceCallAt(uword return_address,
+                                      const Code& caller_code,
+                                      const Object& data,
+                                      const Code& target) {
+  ASSERT(caller_code.ContainsInstructionAt(return_address));
+  InstanceCall call(return_address, caller_code);
+  call.set_data(data);
+  call.set_target(target);
+}
+
 void CodePatcher::InsertDeoptimizationCallAt(uword start) {
   UNREACHABLE();
 }
 
 RawFunction* CodePatcher::GetUnoptimizedStaticCallAt(uword return_address,
-                                                     const Code& code,
+                                                     const Code& caller_code,
                                                      ICData* ic_data_result) {
-  ASSERT(code.ContainsInstructionAt(return_address));
-  UnoptimizedStaticCall static_call(return_address, code);
+  ASSERT(caller_code.ContainsInstructionAt(return_address));
+  UnoptimizedStaticCall static_call(return_address, caller_code);
   ICData& ic_data = ICData::Handle();
   ic_data ^= static_call.ic_data();
   if (ic_data_result != NULL) {
@@ -492,20 +511,20 @@
 }
 
 void CodePatcher::PatchNativeCallAt(uword return_address,
-                                    const Code& code,
+                                    const Code& caller_code,
                                     NativeFunction target,
                                     const Code& trampoline) {
-  ASSERT(code.ContainsInstructionAt(return_address));
-  NativeCall call(return_address, code);
+  ASSERT(caller_code.ContainsInstructionAt(return_address));
+  NativeCall call(return_address, caller_code);
   call.set_target(trampoline);
   call.set_native_function(target);
 }
 
 RawCode* CodePatcher::GetNativeCallAt(uword return_address,
-                                      const Code& code,
+                                      const Code& caller_code,
                                       NativeFunction* target) {
-  ASSERT(code.ContainsInstructionAt(return_address));
-  NativeCall call(return_address, code);
+  ASSERT(caller_code.ContainsInstructionAt(return_address));
+  NativeCall call(return_address, caller_code);
   *target = call.native_function();
   return call.target();
 }
diff --git a/runtime/vm/compiler/aot/aot_call_specializer.cc b/runtime/vm/compiler/aot/aot_call_specializer.cc
index b3c2ea3..196a9df 100644
--- a/runtime/vm/compiler/aot/aot_call_specializer.cc
+++ b/runtime/vm/compiler/aot/aot_call_specializer.cc
@@ -161,7 +161,8 @@
       Function::Handle(Z, call->ResolveForReceiverClass(cls));
   ASSERT(!function.IsNull());
   const Function& target = Function::ZoneHandle(Z, function.raw());
-  StaticCallInstr* static_call = StaticCallInstr::FromCall(Z, call, target);
+  StaticCallInstr* static_call =
+      StaticCallInstr::FromCall(Z, call, target, call->CallCount());
   static_call->SetResultType(Z, CompileType::FromCid(kTypeCid));
   call->ReplaceWith(static_call, current_iterator());
   return true;
@@ -849,7 +850,8 @@
       CallTargets* targets = CallTargets::Create(Z, unary_checks);
       ASSERT(targets->HasSingleTarget());
       const Function& target = targets->FirstTarget();
-      StaticCallInstr* call = StaticCallInstr::FromCall(Z, instr, target);
+      StaticCallInstr* call = StaticCallInstr::FromCall(
+          Z, instr, target, targets->AggregateCallCount());
       instr->ReplaceWith(call, current_iterator());
       return;
     }
@@ -911,7 +913,8 @@
         Function::Handle(Z, instr->ResolveForReceiverClass(receiver_class));
     if (!function.IsNull()) {
       const Function& target = Function::ZoneHandle(Z, function.raw());
-      StaticCallInstr* call = StaticCallInstr::FromCall(Z, instr, target);
+      StaticCallInstr* call =
+          StaticCallInstr::FromCall(Z, instr, target, instr->CallCount());
       instr->ReplaceWith(call, current_iterator());
       return;
     }
@@ -1024,7 +1027,8 @@
         // We have computed that there is only a single target for this call
         // within the whole hierarchy. Replace InstanceCall with StaticCall.
         const Function& target = Function::ZoneHandle(Z, single_target.raw());
-        StaticCallInstr* call = StaticCallInstr::FromCall(Z, instr, target);
+        StaticCallInstr* call =
+            StaticCallInstr::FromCall(Z, instr, target, instr->CallCount());
         instr->ReplaceWith(call, current_iterator());
         return;
       } else if ((ic_data.raw() != ICData::null()) &&
@@ -1181,7 +1185,8 @@
         Z, call->instance_call()->ResolveForReceiverClass(receiver_class));
     if (!function.IsNull()) {
       // Only one target. Replace by static call.
-      StaticCallInstr* new_call = StaticCallInstr::FromCall(Z, call, function);
+      StaticCallInstr* new_call =
+          StaticCallInstr::FromCall(Z, call, function, call->CallCount());
       call->ReplaceWith(new_call, current_iterator());
     }
   }
diff --git a/runtime/vm/compiler/assembler/assembler_arm.cc b/runtime/vm/compiler/assembler/assembler_arm.cc
index fa570c5..1c06796 100644
--- a/runtime/vm/compiler/assembler/assembler_arm.cc
+++ b/runtime/vm/compiler/assembler/assembler_arm.cc
@@ -3393,9 +3393,9 @@
   LeaveDartFrame();
 }
 
-// R0 receiver, R9 guarded cid as Smi.
+// R0 receiver, R9 ICData entries array
 // Preserve R4 (ARGS_DESC_REG), not required today, but maybe later.
-void Assembler::MonomorphicCheckedEntry() {
+void Assembler::MonomorphicCheckedEntryJIT() {
   has_single_entry_point_ = false;
 #if defined(TESTING) || defined(DEBUG)
   bool saved_use_far_branches = use_far_branches();
@@ -3404,19 +3404,65 @@
   intptr_t start = CodeSize();
 
   Comment("MonomorphicCheckedEntry");
-  ASSERT(CodeSize() - start == Instructions::kPolymorphicEntryOffset);
+  ASSERT(CodeSize() - start == Instructions::kMonomorphicEntryOffsetJIT);
+
+  const intptr_t cid_offset = target::Array::element_offset(0);
+  const intptr_t count_offset = target::Array::element_offset(1);
+
+  // Sadly this cannot use ldm because ldm takes no offset.
+  ldr(R1, FieldAddress(R9, cid_offset));
+  ldr(R2, FieldAddress(R9, count_offset));
+  LoadClassIdMayBeSmi(IP, R0);
+  add(R2, R2, Operand(target::ToRawSmi(1)));
+  cmp(R1, Operand(IP, LSL, 1));
+  Branch(Address(THR, Thread::monomorphic_miss_entry_offset()), NE);
+  str(R2, FieldAddress(R9, count_offset));
+  LoadImmediate(R4, 0);  // GC-safe for OptimizeInvokedFunction.
+
+  // Fall through to unchecked entry.
+  ASSERT(CodeSize() - start == Instructions::kPolymorphicEntryOffsetJIT);
+
+#if defined(TESTING) || defined(DEBUG)
+  set_use_far_branches(saved_use_far_branches);
+#endif
+}
+
+// R0 receiver, R9 guarded cid as Smi.
+// Preserve R4 (ARGS_DESC_REG), not required today, but maybe later.
+void Assembler::MonomorphicCheckedEntryAOT() {
+  has_single_entry_point_ = false;
+#if defined(TESTING) || defined(DEBUG)
+  bool saved_use_far_branches = use_far_branches();
+  set_use_far_branches(false);
+#endif
+  intptr_t start = CodeSize();
+
+  Comment("MonomorphicCheckedEntry");
+  ASSERT(CodeSize() - start == Instructions::kMonomorphicEntryOffsetAOT);
+
   LoadClassIdMayBeSmi(IP, R0);
   cmp(R9, Operand(IP, LSL, 1));
   Branch(Address(THR, Thread::monomorphic_miss_entry_offset()), NE);
 
   // Fall through to unchecked entry.
-  ASSERT(CodeSize() - start == Instructions::kMonomorphicEntryOffset);
+  ASSERT(CodeSize() - start == Instructions::kPolymorphicEntryOffsetAOT);
 
 #if defined(TESTING) || defined(DEBUG)
   set_use_far_branches(saved_use_far_branches);
 #endif
 }
 
+void Assembler::BranchOnMonomorphicCheckedEntryJIT(Label* label) {
+  has_single_entry_point_ = false;
+  while (CodeSize() < Instructions::kMonomorphicEntryOffsetJIT) {
+    bkpt(0);
+  }
+  b(label);
+  while (CodeSize() < Instructions::kPolymorphicEntryOffsetJIT) {
+    bkpt(0);
+  }
+}
+
 #ifndef PRODUCT
 void Assembler::MaybeTraceAllocation(Register stats_addr_reg, Label* trace) {
   ASSERT(stats_addr_reg != kNoRegister);
diff --git a/runtime/vm/compiler/assembler/assembler_arm.h b/runtime/vm/compiler/assembler/assembler_arm.h
index ceb1820..19ba44f 100644
--- a/runtime/vm/compiler/assembler/assembler_arm.h
+++ b/runtime/vm/compiler/assembler/assembler_arm.h
@@ -1058,7 +1058,9 @@
   void EnterStubFrame();
   void LeaveStubFrame();
 
-  void MonomorphicCheckedEntry();
+  void MonomorphicCheckedEntryJIT();
+  void MonomorphicCheckedEntryAOT();
+  void BranchOnMonomorphicCheckedEntryJIT(Label* label);
 
   // The register into which the allocation stats table is loaded with
   // LoadAllocationStatsAddress should be passed to MaybeTraceAllocation and
diff --git a/runtime/vm/compiler/assembler/assembler_arm64.cc b/runtime/vm/compiler/assembler/assembler_arm64.cc
index 5c1b902..d9a731c 100644
--- a/runtime/vm/compiler/assembler/assembler_arm64.cc
+++ b/runtime/vm/compiler/assembler/assembler_arm64.cc
@@ -1461,9 +1461,44 @@
   LeaveDartFrame();
 }
 
+// R0 receiver, R5 ICData entries array
+// Preserve R4 (ARGS_DESC_REG), not required today, but maybe later.
+void Assembler::MonomorphicCheckedEntryJIT() {
+  ASSERT(has_single_entry_point_);
+  has_single_entry_point_ = false;
+  const bool saved_use_far_branches = use_far_branches();
+  set_use_far_branches(false);
+
+  Label immediate, miss;
+  Bind(&miss);
+  ldr(IP0, Address(THR, Thread::monomorphic_miss_entry_offset()));
+  br(IP0);
+
+  Comment("MonomorphicCheckedEntry");
+  ASSERT(CodeSize() == Instructions::kMonomorphicEntryOffsetJIT);
+
+  const intptr_t cid_offset = target::Array::element_offset(0);
+  const intptr_t count_offset = target::Array::element_offset(1);
+
+  // Sadly this cannot use ldp because ldp requires aligned offsets.
+  ldr(R1, FieldAddress(R5, cid_offset));
+  ldr(R2, FieldAddress(R5, count_offset));
+  LoadClassIdMayBeSmi(IP0, R0);
+  add(R2, R2, Operand(target::ToRawSmi(1)));
+  cmp(R1, Operand(IP0, LSL, 1));
+  b(&miss, NE);
+  str(R2, FieldAddress(R5, count_offset));
+  LoadImmediate(R4, 0);  // GC-safe for OptimizeInvokedFunction.
+
+  // Fall through to unchecked entry.
+  ASSERT(CodeSize() == Instructions::kPolymorphicEntryOffsetJIT);
+
+  set_use_far_branches(saved_use_far_branches);
+}
+
 // R0 receiver, R5 guarded cid as Smi.
 // Preserve R4 (ARGS_DESC_REG), not required today, but maybe later.
-void Assembler::MonomorphicCheckedEntry() {
+void Assembler::MonomorphicCheckedEntryAOT() {
   ASSERT(has_single_entry_point_);
   has_single_entry_point_ = false;
   bool saved_use_far_branches = use_far_branches();
@@ -1477,17 +1512,28 @@
   br(IP0);
 
   Comment("MonomorphicCheckedEntry");
-  ASSERT(CodeSize() - start == Instructions::kPolymorphicEntryOffset);
+  ASSERT(CodeSize() - start == Instructions::kMonomorphicEntryOffsetAOT);
   LoadClassIdMayBeSmi(IP0, R0);
   cmp(R5, Operand(IP0, LSL, 1));
   b(&miss, NE);
 
   // Fall through to unchecked entry.
-  ASSERT(CodeSize() - start == Instructions::kMonomorphicEntryOffset);
+  ASSERT(CodeSize() - start == Instructions::kPolymorphicEntryOffsetAOT);
 
   set_use_far_branches(saved_use_far_branches);
 }
 
+void Assembler::BranchOnMonomorphicCheckedEntryJIT(Label* label) {
+  has_single_entry_point_ = false;
+  while (CodeSize() < Instructions::kMonomorphicEntryOffsetJIT) {
+    brk(0);
+  }
+  b(label);
+  while (CodeSize() < Instructions::kPolymorphicEntryOffsetJIT) {
+    brk(0);
+  }
+}
+
 #ifndef PRODUCT
 void Assembler::MaybeTraceAllocation(intptr_t cid,
                                      Register temp_reg,
diff --git a/runtime/vm/compiler/assembler/assembler_arm64.h b/runtime/vm/compiler/assembler/assembler_arm64.h
index cb9af12..b8808f5 100644
--- a/runtime/vm/compiler/assembler/assembler_arm64.h
+++ b/runtime/vm/compiler/assembler/assembler_arm64.h
@@ -1561,7 +1561,9 @@
   void EnterStubFrame();
   void LeaveStubFrame();
 
-  void MonomorphicCheckedEntry();
+  void MonomorphicCheckedEntryJIT();
+  void MonomorphicCheckedEntryAOT();
+  void BranchOnMonomorphicCheckedEntryJIT(Label* label);
 
   void UpdateAllocationStats(intptr_t cid);
 
diff --git a/runtime/vm/compiler/assembler/assembler_dbc.h b/runtime/vm/compiler/assembler/assembler_dbc.h
index b88795f..38a8920 100644
--- a/runtime/vm/compiler/assembler/assembler_dbc.h
+++ b/runtime/vm/compiler/assembler/assembler_dbc.h
@@ -39,7 +39,8 @@
   // Misc. functionality
   intptr_t prologue_offset() const { return 0; }
 
-  void MonomorphicCheckedEntry() {}
+  void MonomorphicCheckedEntryJIT() {}
+  void MonomorphicCheckedEntryAOT() {}
 
   // Debugging and bringup support.
   void Stop(const char* message) override;
diff --git a/runtime/vm/compiler/assembler/assembler_ia32.cc b/runtime/vm/compiler/assembler/assembler_ia32.cc
index 7311298..73031ba 100644
--- a/runtime/vm/compiler/assembler/assembler_ia32.cc
+++ b/runtime/vm/compiler/assembler/assembler_ia32.cc
@@ -2097,6 +2097,56 @@
 #endif
 }
 
+// EBX receiver, ECX ICData entries array
+// Preserve EDX (ARGS_DESC_REG), not required today, but maybe later.
+void Assembler::MonomorphicCheckedEntryJIT() {
+  has_single_entry_point_ = false;
+  intptr_t start = CodeSize();
+  Label have_cid, miss;
+  Bind(&miss);
+  jmp(Address(THR, Thread::monomorphic_miss_entry_offset()));
+
+  Comment("MonomorphicCheckedEntry");
+  ASSERT(CodeSize() - start == Instructions::kMonomorphicEntryOffsetJIT);
+
+  const intptr_t cid_offset = target::Array::element_offset(0);
+  const intptr_t count_offset = target::Array::element_offset(1);
+
+  movl(EAX, Immediate(kSmiCid << 1));
+  testl(EBX, Immediate(kSmiTagMask));
+  j(ZERO, &have_cid, kNearJump);
+  LoadClassId(EAX, EBX);
+  SmiTag(EAX);
+  Bind(&have_cid);
+  // EAX: cid as Smi
+
+  cmpl(EAX, FieldAddress(ECX, cid_offset));
+  j(NOT_EQUAL, &miss, Assembler::kNearJump);
+  addl(FieldAddress(ECX, count_offset), Immediate(target::ToRawSmi(1)));
+  xorl(EDX, EDX);  // GC-safe for OptimizeInvokedFunction.
+  nop(1);
+
+  // Fall through to unchecked entry.
+  ASSERT(CodeSize() - start == Instructions::kPolymorphicEntryOffsetJIT);
+}
+
+// EBX receiver, ECX guarded cid as Smi.
+// Preserve EDX (ARGS_DESC_REG), not required today, but maybe later.
+void Assembler::MonomorphicCheckedEntryAOT() {
+  UNIMPLEMENTED();
+}
+
+void Assembler::BranchOnMonomorphicCheckedEntryJIT(Label* label) {
+  has_single_entry_point_ = false;
+  while (CodeSize() < Instructions::kMonomorphicEntryOffsetJIT) {
+    int3();
+  }
+  jmp(label);
+  while (CodeSize() < Instructions::kPolymorphicEntryOffsetJIT) {
+    int3();
+  }
+}
+
 void Assembler::TransitionGeneratedToNative(Register destination_address,
                                             Register new_exit_frame,
                                             Register scratch) {
diff --git a/runtime/vm/compiler/assembler/assembler_ia32.h b/runtime/vm/compiler/assembler/assembler_ia32.h
index ad6149b..4bc1e6f 100644
--- a/runtime/vm/compiler/assembler/assembler_ia32.h
+++ b/runtime/vm/compiler/assembler/assembler_ia32.h
@@ -646,7 +646,9 @@
   void LeaveFrame();
   void ReserveAlignedFrameSpace(intptr_t frame_space);
 
-  void MonomorphicCheckedEntry() {}
+  void MonomorphicCheckedEntryJIT();
+  void MonomorphicCheckedEntryAOT();
+  void BranchOnMonomorphicCheckedEntryJIT(Label* label);
 
   // In debug mode, this generates code to check that:
   //   FP + kExitLinkSlotFromEntryFp == SP
@@ -744,8 +746,6 @@
   // Needs a temporary register.
   void MoveMemoryToMemory(Address to, Address from, Register tmp);
 
-  bool has_single_entry_point() const { return true; }
-
   // Set up a Dart frame on entry with a frame pointer and PC information to
   // enable easy access to the RawInstruction object of code corresponding
   // to this frame.
diff --git a/runtime/vm/compiler/assembler/assembler_x64.cc b/runtime/vm/compiler/assembler/assembler_x64.cc
index 1a6596e..a90db6d 100644
--- a/runtime/vm/compiler/assembler/assembler_x64.cc
+++ b/runtime/vm/compiler/assembler/assembler_x64.cc
@@ -1742,9 +1742,9 @@
   LeaveDartFrame();
 }
 
-// RDX receiver, RBX guarded cid as Smi.
+// RDX receiver, RBX ICData entries array
 // Preserve R10 (ARGS_DESC_REG), not required today, but maybe later.
-void Assembler::MonomorphicCheckedEntry() {
+void Assembler::MonomorphicCheckedEntryJIT() {
   has_single_entry_point_ = false;
   intptr_t start = CodeSize();
   Label have_cid, miss;
@@ -1756,7 +1756,38 @@
   nop(1);
 
   Comment("MonomorphicCheckedEntry");
-  ASSERT(CodeSize() - start == Instructions::kPolymorphicEntryOffset);
+  ASSERT(CodeSize() - start == Instructions::kMonomorphicEntryOffsetJIT);
+  ASSERT((CodeSize() & kSmiTagMask) == kSmiTag);
+
+  const intptr_t cid_offset = target::Array::element_offset(0);
+  const intptr_t count_offset = target::Array::element_offset(1);
+
+  LoadTaggedClassIdMayBeSmi(RAX, RDX);
+
+  cmpq(RAX, FieldAddress(RBX, cid_offset));
+  j(NOT_EQUAL, &miss, Assembler::kNearJump);
+  addl(FieldAddress(RBX, count_offset), Immediate(target::ToRawSmi(1)));
+  xorq(R10, R10);  // GC-safe for OptimizeInvokedFunction.
+  nop(1);
+
+  // Fall through to unchecked entry.
+  ASSERT(CodeSize() - start == Instructions::kPolymorphicEntryOffsetJIT);
+  ASSERT(((CodeSize() - start) & kSmiTagMask) == kSmiTag);
+}
+
+void Assembler::MonomorphicCheckedEntryAOT() {
+  has_single_entry_point_ = false;
+  intptr_t start = CodeSize();
+  Label have_cid, miss;
+  Bind(&miss);
+  jmp(Address(THR, Thread::monomorphic_miss_entry_offset()));
+
+  // Ensure the monomorphic entry is 2-byte aligned (so GC can see them if we
+  // store them in ICData / MegamorphicCache arrays)
+  nop(1);
+
+  Comment("MonomorphicCheckedEntry");
+  ASSERT(CodeSize() - start == Instructions::kMonomorphicEntryOffsetAOT);
   ASSERT((CodeSize() & kSmiTagMask) == kSmiTag);
 
   movq(RAX, Immediate(kSmiCid));
@@ -1774,10 +1805,21 @@
   nop(1);
 
   // Fall through to unchecked entry.
-  ASSERT(CodeSize() - start == Instructions::kMonomorphicEntryOffset);
+  ASSERT(CodeSize() - start == Instructions::kPolymorphicEntryOffsetAOT);
   ASSERT(((CodeSize() - start) & kSmiTagMask) == kSmiTag);
 }
 
+void Assembler::BranchOnMonomorphicCheckedEntryJIT(Label* label) {
+  has_single_entry_point_ = false;
+  while (CodeSize() < Instructions::kMonomorphicEntryOffsetJIT) {
+    int3();
+  }
+  jmp(label);
+  while (CodeSize() < Instructions::kPolymorphicEntryOffsetJIT) {
+    int3();
+  }
+}
+
 #ifndef PRODUCT
 void Assembler::MaybeTraceAllocation(intptr_t cid,
                                      Label* trace,
diff --git a/runtime/vm/compiler/assembler/assembler_x64.h b/runtime/vm/compiler/assembler/assembler_x64.h
index 4df9250..73cadd0 100644
--- a/runtime/vm/compiler/assembler/assembler_x64.h
+++ b/runtime/vm/compiler/assembler/assembler_x64.h
@@ -885,7 +885,9 @@
   void EnterStubFrame();
   void LeaveStubFrame();
 
-  void MonomorphicCheckedEntry();
+  void MonomorphicCheckedEntryJIT();
+  void MonomorphicCheckedEntryAOT();
+  void BranchOnMonomorphicCheckedEntryJIT(Label* label);
 
   void UpdateAllocationStats(intptr_t cid);
 
diff --git a/runtime/vm/compiler/assembler/disassembler.cc b/runtime/vm/compiler/assembler/disassembler.cc
index a2fe6a1..e39e721 100644
--- a/runtime/vm/compiler/assembler/disassembler.cc
+++ b/runtime/vm/compiler/assembler/disassembler.cc
@@ -28,8 +28,11 @@
                                              Object* object,
                                              uword pc) {
   static const int kHexColumnWidth = 23;
-  uint8_t* pc_ptr = reinterpret_cast<uint8_t*>(pc);
-  THR_Print("%p    %s", pc_ptr, hex_buffer);
+#if defined(TARGET_ARCH_IS_32_BIT)
+  THR_Print("0x%" Px32 "    %s", static_cast<uint32_t>(pc), hex_buffer);
+#else
+  THR_Print("0x%" Px64 "    %s", static_cast<uint64_t>(pc), hex_buffer);
+#endif
   int hex_length = strlen(hex_buffer);
   if (hex_length < kHexColumnWidth) {
     for (int i = kHexColumnWidth - hex_length; i > 0; i--) {
@@ -201,7 +204,8 @@
                       sizeof(human_buffer), &instruction_length, code, &object,
                       pc);
     formatter->ConsumeInstruction(hex_buffer, sizeof(hex_buffer), human_buffer,
-                                  sizeof(human_buffer), object, pc);
+                                  sizeof(human_buffer), object,
+                                  FLAG_disassemble_relative ? offset : pc);
     pc += instruction_length;
   }
 }
@@ -248,6 +252,7 @@
 
   const auto& instructions = Instructions::Handle(code.instructions());
   const uword start = instructions.PayloadStart();
+  const uword base = FLAG_disassemble_relative ? 0 : start;
 
 #if !defined(DART_PRECOMPILED_RUNTIME)
   const Array& deopt_table = Array::Handle(zone, code.deopt_info_array());
@@ -263,7 +268,7 @@
           DeoptTable::ReasonField::decode(reason_and_flags.Value());
       ASSERT((0 <= reason) && (reason < ICData::kDeoptNumReasons));
       THR_Print(
-          "%4" Pd ": 0x%" Px "  %s  (%s)\n", i, start + offset.Value(),
+          "%4" Pd ": 0x%" Px "  %s  (%s)\n", i, base + offset.Value(),
           DeoptInfo::ToCString(deopt_table, info),
           DeoptReasonToCString(static_cast<ICData::DeoptReasonId>(reason)));
     }
@@ -322,19 +327,20 @@
     THR_Print("Entry points for function '%s' {\n", function_fullname);
     THR_Print("  [code+0x%02" Px "] %" Px " kNormal\n",
               Code::entry_point_offset(CodeEntryKind::kNormal) - kHeapObjectTag,
-              Instructions::EntryPoint(instructions.raw()));
-    THR_Print(
-        "  [code+0x%02" Px "] %" Px " kUnchecked\n",
-        Code::entry_point_offset(CodeEntryKind::kUnchecked) - kHeapObjectTag,
-        Instructions::UncheckedEntryPoint(instructions.raw()));
+              Instructions::EntryPoint(instructions.raw()) - start + base);
     THR_Print(
         "  [code+0x%02" Px "] %" Px " kMonomorphic\n",
         Code::entry_point_offset(CodeEntryKind::kMonomorphic) - kHeapObjectTag,
-        Instructions::MonomorphicEntryPoint(instructions.raw()));
+        Instructions::MonomorphicEntryPoint(instructions.raw()) - start + base);
+    THR_Print(
+        "  [code+0x%02" Px "] %" Px " kUnchecked\n",
+        Code::entry_point_offset(CodeEntryKind::kUnchecked) - kHeapObjectTag,
+        Instructions::UncheckedEntryPoint(instructions.raw()) - start + base);
     THR_Print("  [code+0x%02" Px "] %" Px " kMonomorphicUnchecked\n",
               Code::entry_point_offset(CodeEntryKind::kMonomorphicUnchecked) -
                   kHeapObjectTag,
-              Instructions::MonomorphicUncheckedEntryPoint(instructions.raw()));
+              Instructions::MonomorphicUncheckedEntryPoint(instructions.raw()) -
+                  start + base);
     THR_Print("}\n");
   }
 
@@ -376,17 +382,15 @@
         if (function.IsNull()) {
           cls ^= code.owner();
           if (cls.IsNull()) {
-            THR_Print("  0x%" Px ": %s, %p (%s)%s\n", start + offset,
-                      code.QualifiedName(), code.raw(), skind, s_entry_point);
+            THR_Print("  0x%" Px ": %s, (%s)%s\n", base + offset,
+                      code.QualifiedName(), skind, s_entry_point);
           } else {
-            THR_Print("  0x%" Px ": allocation stub for %s, %p (%s)%s\n",
-                      start + offset, cls.ToCString(), code.raw(), skind,
-                      s_entry_point);
+            THR_Print("  0x%" Px ": allocation stub for %s, (%s)%s\n",
+                      base + offset, cls.ToCString(), skind, s_entry_point);
           }
         } else {
-          THR_Print("  0x%" Px ": %s, %p (%s)%s\n", start + offset,
-                    function.ToFullyQualifiedCString(), code.raw(), skind,
-                    s_entry_point);
+          THR_Print("  0x%" Px ": %s, (%s)%s\n", base + offset,
+                    function.ToFullyQualifiedCString(), skind, s_entry_point);
         }
       }
     }
diff --git a/runtime/vm/compiler/assembler/disassembler_arm.cc b/runtime/vm/compiler/assembler/disassembler_arm.cc
index f5266ae..888f97b 100644
--- a/runtime/vm/compiler/assembler/disassembler_arm.cc
+++ b/runtime/vm/compiler/assembler/disassembler_arm.cc
@@ -403,11 +403,17 @@
     case 'd': {
       if (format[1] == 'e') {  // 'dest: branch destination
         ASSERT(STRING_STARTS_WITH(format, "dest"));
-        int off = (instr->SImmed24Field() << 2) + 8;
-        uword destination = reinterpret_cast<uword>(instr) + off;
-        buffer_pos_ +=
-            Utils::SNPrint(current_position_in_buffer(),
-                           remaining_size_in_buffer(), "%#" Px "", destination);
+        const int32_t off = (instr->SImmed24Field() << 2) + 8;
+        if (FLAG_disassemble_relative) {
+          buffer_pos_ +=
+              Utils::SNPrint(current_position_in_buffer(),
+                             remaining_size_in_buffer(), "%+" Pd32 "", off);
+        } else {
+          uword destination = reinterpret_cast<uword>(instr) + off;
+          buffer_pos_ += Utils::SNPrint(current_position_in_buffer(),
+                                        remaining_size_in_buffer(), "%#" Px "",
+                                        destination);
+        }
         return 4;
       } else {
         return FormatDRegister(instr, format);
diff --git a/runtime/vm/compiler/assembler/disassembler_arm64.cc b/runtime/vm/compiler/assembler/disassembler_arm64.cc
index e074048..7ed5a44 100644
--- a/runtime/vm/compiler/assembler/disassembler_arm64.cc
+++ b/runtime/vm/compiler/assembler/disassembler_arm64.cc
@@ -395,29 +395,28 @@
       }
     }
     case 'd': {
+      int64_t off;
       if (format[4] == '2') {
         ASSERT(STRING_STARTS_WITH(format, "dest26"));
-        int64_t off = instr->SImm26Field() << 2;
+        off = instr->SImm26Field() << 2;
+      } else {
+        if (format[5] == '4') {
+          ASSERT(STRING_STARTS_WITH(format, "dest14"));
+          off = instr->SImm14Field() << 2;
+        } else {
+          ASSERT(STRING_STARTS_WITH(format, "dest19"));
+          off = instr->SImm19Field() << 2;
+        }
+      }
+      if (FLAG_disassemble_relative) {
+        buffer_pos_ +=
+            Utils::SNPrint(current_position_in_buffer(),
+                           remaining_size_in_buffer(), "%+" Pd64 "", off);
+      } else {
         uword destination = reinterpret_cast<uword>(instr) + off;
         buffer_pos_ +=
             Utils::SNPrint(current_position_in_buffer(),
                            remaining_size_in_buffer(), "%#" Px "", destination);
-      } else {
-        if (format[5] == '4') {
-          ASSERT(STRING_STARTS_WITH(format, "dest14"));
-          int64_t off = instr->SImm14Field() << 2;
-          uword destination = reinterpret_cast<uword>(instr) + off;
-          buffer_pos_ += Utils::SNPrint(current_position_in_buffer(),
-                                        remaining_size_in_buffer(), "%#" Px "",
-                                        destination);
-        } else {
-          ASSERT(STRING_STARTS_WITH(format, "dest19"));
-          int64_t off = instr->SImm19Field() << 2;
-          uword destination = reinterpret_cast<uword>(instr) + off;
-          buffer_pos_ += Utils::SNPrint(current_position_in_buffer(),
-                                        remaining_size_in_buffer(), "%#" Px "",
-                                        destination);
-        }
       }
       return 6;
     }
diff --git a/runtime/vm/compiler/assembler/disassembler_x86.cc b/runtime/vm/compiler/assembler/disassembler_x86.cc
index 756e5d4..7e4c016 100644
--- a/runtime/vm/compiler/assembler/disassembler_x86.cc
+++ b/runtime/vm/compiler/assembler/disassembler_x86.cc
@@ -324,6 +324,7 @@
   }
 
   void Print(const char* format, ...) PRINTF_ATTRIBUTE(2, 3);
+  void PrintJump(uint8_t* pc, int32_t disp);
   void PrintAddress(uint8_t* addr);
 
   int PrintOperands(const char* mnem, OperandType op_order, uint8_t* data);
@@ -772,6 +773,14 @@
   return advance;
 }
 
+void DisassemblerX64::PrintJump(uint8_t* pc, int32_t disp) {
+  if (FLAG_disassemble_relative) {
+    Print("%+d", disp);
+  } else {
+    PrintAddress(pc + disp);
+  }
+}
+
 void DisassemblerX64::PrintAddress(uint8_t* addr_byte_ptr) {
 #if defined(TARGET_ARCH_X64)
   Print("%#018" Px64 "", reinterpret_cast<uint64_t>(addr_byte_ptr));
@@ -790,9 +799,9 @@
 int DisassemblerX64::JumpShort(uint8_t* data) {
   ASSERT(0xEB == *data);
   uint8_t b = *(data + 1);
-  uint8_t* dest = data + static_cast<int8_t>(b) + 2;
+  int32_t disp = static_cast<int8_t>(b) + 2;
   Print("jmp ");
-  PrintAddress(dest);
+  PrintJump(data, disp);
   return 2;
 }
 
@@ -800,10 +809,10 @@
 int DisassemblerX64::JumpConditional(uint8_t* data) {
   ASSERT(0x0F == *data);
   uint8_t cond = *(data + 1) & 0x0F;
-  uint8_t* dest = data + *reinterpret_cast<int32_t*>(data + 2) + 6;
+  int32_t disp = *reinterpret_cast<int32_t*>(data + 2) + 6;
   const char* mnem = conditional_code_suffix[cond];
   Print("j%s ", mnem);
-  PrintAddress(dest);
+  PrintJump(data, disp);
   return 6;  // includes 0x0F
 }
 
@@ -811,10 +820,10 @@
 int DisassemblerX64::JumpConditionalShort(uint8_t* data) {
   uint8_t cond = *data & 0x0F;
   uint8_t b = *(data + 1);
-  uint8_t* dest = data + static_cast<int8_t>(b) + 2;
+  int32_t disp = static_cast<int8_t>(b) + 2;
   const char* mnem = conditional_code_suffix[cond];
   Print("j%s ", mnem);
-  PrintAddress(dest);
+  PrintJump(data, disp);
   return 2;
 }
 
@@ -1190,9 +1199,9 @@
     }
 
     case CALL_JUMP_INSTR: {
-      uint8_t* addr = *data + *reinterpret_cast<int32_t*>(*data + 1) + 5;
+      int32_t disp = *reinterpret_cast<int32_t*>(*data + 1) + 5;
       Print("%s ", idesc.mnem);
-      PrintAddress(addr);
+      PrintJump(*data, disp);
       (*data) += 5;
       break;
     }
diff --git a/runtime/vm/compiler/backend/constant_propagator.cc b/runtime/vm/compiler/backend/constant_propagator.cc
index dc78a0e..a3f6a6e 100644
--- a/runtime/vm/compiler/backend/constant_propagator.cc
+++ b/runtime/vm/compiler/backend/constant_propagator.cc
@@ -1453,7 +1453,8 @@
 static void RemovePushArguments(StaticCallInstr* call) {
   for (intptr_t i = 0; i < call->ArgumentCount(); ++i) {
     PushArgumentInstr* push = call->PushArgumentAt(i);
-    ASSERT(push->input_use_list() == nullptr);
+    ASSERT(push->input_use_list() == nullptr);           // no direct uses
+    push->ReplaceUsesWith(push->value()->definition());  // cleanup env uses
     push->RemoveFromGraph();
   }
 }
diff --git a/runtime/vm/compiler/backend/flow_graph.cc b/runtime/vm/compiler/backend/flow_graph.cc
index 0a590a3..fa4b35e 100644
--- a/runtime/vm/compiler/backend/flow_graph.cc
+++ b/runtime/vm/compiler/backend/flow_graph.cc
@@ -55,7 +55,6 @@
       loop_hierarchy_(nullptr),
       loop_invariant_loads_(nullptr),
       deferred_prefixes_(parsed_function.deferred_prefixes()),
-      await_token_positions_(nullptr),
       captured_parameters_(new (zone()) BitVector(zone(), variable_count())),
       inlining_id_(-1),
       should_print_(FlowGraphPrinter::ShouldPrint(parsed_function.function())) {
diff --git a/runtime/vm/compiler/backend/flow_graph.h b/runtime/vm/compiler/backend/flow_graph.h
index abbfcac..a606d26 100644
--- a/runtime/vm/compiler/backend/flow_graph.h
+++ b/runtime/vm/compiler/backend/flow_graph.h
@@ -366,15 +366,6 @@
   // 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,
@@ -535,7 +526,6 @@
   ZoneGrowableArray<BitVector*>* loop_invariant_loads_;
 
   ZoneGrowableArray<const LibraryPrefix*>* deferred_prefixes_;
-  ZoneGrowableArray<TokenPosition>* await_token_positions_;
   DirectChainedHashMap<ConstantPoolTrait> constant_instr_pool_;
   BitVector* captured_parameters_;
 
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler.cc b/runtime/vm/compiler/backend/flow_graph_compiler.cc
index cd2a8db..ddebec9 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler.cc
@@ -339,7 +339,8 @@
 
   // Intrinsification happened.
   if (parsed_function().function().IsDynamicFunction()) {
-    return Instructions::kMonomorphicEntryOffset;
+    return FLAG_precompiled_mode ? Instructions::kPolymorphicEntryOffsetAOT
+                                 : Instructions::kPolymorphicEntryOffsetJIT;
   }
 
   return 0;
@@ -1125,7 +1126,7 @@
     // complicated to factor out.
     // TODO(srdjan): Consider canonicalizing and reusing the local var
     // descriptor for IrregexpFunction.
-    ASSERT(parsed_function().node_sequence() == nullptr);
+    ASSERT(parsed_function().scope() == nullptr);
     var_descs = LocalVarDescriptors::New(1);
     RawLocalVarDescriptors::VarInfo info;
     info.set_kind(RawLocalVarDescriptors::kSavedCurrentContext);
@@ -1238,7 +1239,7 @@
           SpecialStatsBegin(CombinedCodeStatistics::kTagIntrinsics);
           GenerateGetterIntrinsic(compiler::target::Field::OffsetOf(field));
           SpecialStatsEnd(CombinedCodeStatistics::kTagIntrinsics);
-          return !isolate()->use_field_guards();
+          return true;
         }
         return false;
       }
@@ -1257,7 +1258,7 @@
             SpecialStatsBegin(CombinedCodeStatistics::kTagIntrinsics);
             GenerateSetterIntrinsic(compiler::target::Field::OffsetOf(field));
             SpecialStatsEnd(CombinedCodeStatistics::kTagIntrinsics);
-            return !isolate()->use_field_guards();
+            return true;
           }
           return false;
         }
@@ -1360,7 +1361,7 @@
   if (FLAG_precompiled_mode) {
     // TODO(#34162): Support unchecked entry-points in precompiled mode.
     ic_data = ic_data.AsUnaryClassChecks();
-    EmitSwitchableInstanceCall(ic_data, deopt_id, token_pos, locs, entry_kind);
+    EmitInstanceCallAOT(ic_data, deopt_id, token_pos, locs, entry_kind);
     return;
   }
   ASSERT(!ic_data.IsNull());
@@ -1382,8 +1383,8 @@
     return;
   }
 
-  EmitInstanceCall(StubEntryFor(ic_data, /*optimized=*/false), ic_data,
-                   deopt_id, token_pos, locs);
+  EmitInstanceCallJIT(StubEntryFor(ic_data, /*optimized=*/false), ic_data,
+                      deopt_id, token_pos, locs, entry_kind);
 }
 
 void FlowGraphCompiler::GenerateStaticCall(intptr_t deopt_id,
@@ -2087,7 +2088,7 @@
           zone(), original_call.ic_data()->AsUnaryClassChecks());
       // TODO(sjindel/entrypoints): Support skiping type checks on switchable
       // calls.
-      EmitSwitchableInstanceCall(unary_checks, deopt_id, token_pos, locs);
+      EmitInstanceCallAOT(unary_checks, deopt_id, token_pos, locs);
     }
   }
 }
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler.h b/runtime/vm/compiler/backend/flow_graph_compiler.h
index 28c369b..ca171b3 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler.h
+++ b/runtime/vm/compiler/backend/flow_graph_compiler.h
@@ -616,11 +616,12 @@
       LocationSummary* locs,
       Code::EntryKind entry_kind = Code::EntryKind::kNormal);
 
-  void EmitInstanceCall(const Code& stub,
-                        const ICData& ic_data,
-                        intptr_t deopt_id,
-                        TokenPosition token_pos,
-                        LocationSummary* locs);
+  void EmitInstanceCallJIT(const Code& stub,
+                           const ICData& ic_data,
+                           intptr_t deopt_id,
+                           TokenPosition token_pos,
+                           LocationSummary* locs,
+                           Code::EntryKind entry_kind);
 
   void EmitPolymorphicInstanceCall(
       const CallTargets& targets,
@@ -641,7 +642,7 @@
                                    intptr_t try_index,
                                    intptr_t slow_path_argument_count = 0);
 
-  void EmitSwitchableInstanceCall(
+  void EmitInstanceCallAOT(
       const ICData& ic_data,
       intptr_t deopt_id,
       TokenPosition token_pos,
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc b/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc
index 9018e54..ade4c9f 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc
@@ -843,7 +843,7 @@
   // LR: return address.
   // SP: receiver.
   // Sequence node has one return node, its input is load field node.
-  __ Comment("Inlined Getter");
+  __ Comment("Intrinsic Getter");
   __ ldr(R0, Address(SP, 0 * compiler::target::kWordSize));
   __ LoadFieldFromOffset(kWord, R0, R0, offset);
   __ Ret();
@@ -854,7 +854,7 @@
   // SP+1: receiver.
   // SP+0: value.
   // Sequence node has one store node and one return NULL node.
-  __ Comment("Inlined Setter");
+  __ Comment("Intrinsic Setter");
   __ ldr(R0, Address(SP, 1 * compiler::target::kWordSize));  // Receiver.
   __ ldr(R1, Address(SP, 0 * compiler::target::kWordSize));  // Value.
   __ StoreIntoObjectOffset(R0, offset, R1);
@@ -883,7 +883,8 @@
     }
     __ CompareImmediate(R3, GetOptimizationThreshold());
     ASSERT(function_reg == R8);
-    __ Branch(Address(THR, Thread::optimize_entry_offset()), GE);
+    __ Branch(Address(THR, compiler::target::Thread::optimize_entry_offset()),
+              GE);
   }
   __ Comment("Enter frame");
   if (flow_graph().IsCompiledForOsr()) {
@@ -1066,17 +1067,26 @@
   __ Drop(ic_data.CountWithTypeArgs());
 }
 
-void FlowGraphCompiler::EmitInstanceCall(const Code& stub,
-                                         const ICData& ic_data,
-                                         intptr_t deopt_id,
-                                         TokenPosition token_pos,
-                                         LocationSummary* locs) {
+void FlowGraphCompiler::EmitInstanceCallJIT(const Code& stub,
+                                            const ICData& ic_data,
+                                            intptr_t deopt_id,
+                                            TokenPosition token_pos,
+                                            LocationSummary* locs,
+                                            Code::EntryKind entry_kind) {
+  ASSERT(entry_kind == Code::EntryKind::kNormal ||
+         entry_kind == Code::EntryKind::kUnchecked);
   ASSERT(Array::Handle(zone(), ic_data.arguments_descriptor()).Length() > 0);
   __ LoadFromOffset(kWord, R0, SP,
                     (ic_data.CountWithoutTypeArgs() - 1) * kWordSize);
   __ LoadUniqueObject(R9, ic_data);
-  GenerateDartCall(deopt_id, token_pos, stub, RawPcDescriptors::kIcCall, locs,
-                   Code::EntryKind::kMonomorphic);
+  __ LoadUniqueObject(CODE_REG, stub);
+  const intptr_t entry_point_offset =
+      entry_kind == Code::EntryKind::kNormal
+          ? Code::entry_point_offset(Code::EntryKind::kMonomorphic)
+          : Code::entry_point_offset(Code::EntryKind::kMonomorphicUnchecked);
+  __ ldr(LR, FieldAddress(CODE_REG, entry_point_offset));
+  __ blx(LR);
+  EmitCallsiteMetadata(token_pos, deopt_id, RawPcDescriptors::kIcCall, locs);
   __ Drop(ic_data.CountWithTypeArgs());
 }
 
@@ -1129,17 +1139,17 @@
   __ Drop(args_desc.CountWithTypeArgs());
 }
 
-void FlowGraphCompiler::EmitSwitchableInstanceCall(const ICData& ic_data,
-                                                   intptr_t deopt_id,
-                                                   TokenPosition token_pos,
-                                                   LocationSummary* locs,
-                                                   Code::EntryKind entry_kind) {
+void FlowGraphCompiler::EmitInstanceCallAOT(const ICData& ic_data,
+                                            intptr_t deopt_id,
+                                            TokenPosition token_pos,
+                                            LocationSummary* locs,
+                                            Code::EntryKind entry_kind) {
   ASSERT(entry_kind == Code::EntryKind::kNormal ||
          entry_kind == Code::EntryKind::kUnchecked);
   ASSERT(ic_data.NumArgsTested() == 1);
   const Code& initial_stub = StubCode::ICCallThroughFunction();
 
-  __ Comment("SwitchableCall");
+  __ Comment("InstanceCallAOT");
   __ LoadFromOffset(
       kWord, R0, SP,
       (ic_data.CountWithoutTypeArgs() - 1) * compiler::target::kWordSize);
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc b/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc
index 505a96a..6da971a 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc
@@ -816,7 +816,7 @@
   // LR: return address.
   // SP: receiver.
   // Sequence node has one return node, its input is load field node.
-  __ Comment("Inlined Getter");
+  __ Comment("Intrinsic Getter");
   __ LoadFromOffset(R0, SP, 0 * kWordSize);
   __ LoadFieldFromOffset(R0, R0, offset);
   __ ret();
@@ -827,7 +827,7 @@
   // SP+1: receiver.
   // SP+0: value.
   // Sequence node has one store node and one return NULL node.
-  __ Comment("Inlined Setter");
+  __ Comment("Intrinsic Setter");
   __ LoadFromOffset(R0, SP, 1 * kWordSize);  // Receiver.
   __ LoadFromOffset(R1, SP, 0 * kWordSize);  // Value.
   __ StoreIntoObjectOffset(R0, offset, R1);
@@ -1032,16 +1032,32 @@
   __ Drop(ic_data.CountWithTypeArgs());
 }
 
-void FlowGraphCompiler::EmitInstanceCall(const Code& stub,
-                                         const ICData& ic_data,
-                                         intptr_t deopt_id,
-                                         TokenPosition token_pos,
-                                         LocationSummary* locs) {
+void FlowGraphCompiler::EmitInstanceCallJIT(const Code& stub,
+                                            const ICData& ic_data,
+                                            intptr_t deopt_id,
+                                            TokenPosition token_pos,
+                                            LocationSummary* locs,
+                                            Code::EntryKind entry_kind) {
+  ASSERT(entry_kind == Code::EntryKind::kNormal ||
+         entry_kind == Code::EntryKind::kUnchecked);
   ASSERT(Array::Handle(zone(), ic_data.arguments_descriptor()).Length() > 0);
   __ LoadFromOffset(R0, SP, (ic_data.CountWithoutTypeArgs() - 1) * kWordSize);
-  __ LoadUniqueObject(R5, ic_data);
-  GenerateDartCall(deopt_id, token_pos, stub, RawPcDescriptors::kIcCall, locs,
-                   Code::EntryKind::kMonomorphic);
+
+  ObjectPoolBuilder& op = __ object_pool_builder();
+  const intptr_t ic_data_index =
+      op.AddObject(ic_data, ObjectPool::Patchability::kPatchable);
+  const intptr_t stub_index =
+      op.AddObject(stub, ObjectPool::Patchability::kPatchable);
+  ASSERT((ic_data_index + 1) == stub_index);
+  __ LoadDoubleWordFromPoolOffset(R5, CODE_REG,
+                                  ObjectPool::element_offset(ic_data_index));
+  const intptr_t entry_point_offset =
+      entry_kind == Code::EntryKind::kNormal
+          ? Code::entry_point_offset(Code::EntryKind::kMonomorphic)
+          : Code::entry_point_offset(Code::EntryKind::kMonomorphicUnchecked);
+  __ ldr(LR, FieldAddress(CODE_REG, entry_point_offset));
+  __ blr(LR);
+  EmitCallsiteMetadata(token_pos, deopt_id, RawPcDescriptors::kIcCall, locs);
   __ Drop(ic_data.CountWithTypeArgs());
 }
 
@@ -1090,18 +1106,18 @@
   __ Drop(args_desc.CountWithTypeArgs());
 }
 
-void FlowGraphCompiler::EmitSwitchableInstanceCall(const ICData& ic_data,
-                                                   intptr_t deopt_id,
-                                                   TokenPosition token_pos,
-                                                   LocationSummary* locs,
-                                                   Code::EntryKind entry_kind) {
+void FlowGraphCompiler::EmitInstanceCallAOT(const ICData& ic_data,
+                                            intptr_t deopt_id,
+                                            TokenPosition token_pos,
+                                            LocationSummary* locs,
+                                            Code::EntryKind entry_kind) {
   // TODO(34162): Support multiple entry-points on ARM64.
   ASSERT(ic_data.NumArgsTested() == 1);
   const Code& initial_stub = StubCode::ICCallThroughFunction();
 
-  auto& op = __ object_pool_builder();
+  ObjectPoolBuilder& op = __ object_pool_builder();
 
-  __ Comment("SwitchableCall");
+  __ Comment("InstanceCallAOT");
   __ LoadFromOffset(R0, SP, (ic_data.CountWithoutTypeArgs() - 1) * kWordSize);
 
   const intptr_t ic_data_index =
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc b/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc
index 4cea80a..120c19d 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc
@@ -736,7 +736,7 @@
   // TOS: return address.
   // +1 : receiver.
   // Sequence node has one return node, its input is load field node.
-  __ Comment("Inlined Getter");
+  __ Comment("Intrinsic Getter");
   __ movl(EAX, Address(ESP, 1 * kWordSize));
   __ movl(EAX, FieldAddress(EAX, offset));
   __ ret();
@@ -747,7 +747,7 @@
   // +1 : value
   // +2 : receiver.
   // Sequence node has one store node and one return NULL node.
-  __ Comment("Inlined Setter");
+  __ Comment("Intrinsic Setter");
   __ movl(EAX, Address(ESP, 2 * kWordSize));  // Receiver.
   __ movl(EBX, Address(ESP, 1 * kWordSize));  // Value.
   __ StoreIntoObject(EAX, FieldAddress(EAX, offset), EBX);
@@ -922,17 +922,25 @@
   __ Drop(ic_data.CountWithTypeArgs());
 }
 
-void FlowGraphCompiler::EmitInstanceCall(const Code& stub,
-                                         const ICData& ic_data,
-                                         intptr_t deopt_id,
-                                         TokenPosition token_pos,
-                                         LocationSummary* locs) {
+void FlowGraphCompiler::EmitInstanceCallJIT(const Code& stub,
+                                            const ICData& ic_data,
+                                            intptr_t deopt_id,
+                                            TokenPosition token_pos,
+                                            LocationSummary* locs,
+                                            Code::EntryKind entry_kind) {
+  ASSERT(entry_kind == Code::EntryKind::kNormal ||
+         entry_kind == Code::EntryKind::kUnchecked);
   ASSERT(Array::Handle(ic_data.arguments_descriptor()).Length() > 0);
   // Load receiver into EBX.
   __ movl(EBX, Address(ESP, (ic_data.CountWithoutTypeArgs() - 1) * kWordSize));
-  __ LoadObject(ECX, ic_data);
-  GenerateDartCall(deopt_id, token_pos, stub, RawPcDescriptors::kIcCall, locs,
-                   Code::EntryKind::kMonomorphic);
+  __ LoadObject(ECX, ic_data, true);
+  __ LoadObject(CODE_REG, stub, true);
+  const intptr_t entry_point_offset =
+      entry_kind == Code::EntryKind::kNormal
+          ? Code::entry_point_offset(Code::EntryKind::kMonomorphic)
+          : Code::entry_point_offset(Code::EntryKind::kMonomorphicUnchecked);
+  __ call(FieldAddress(CODE_REG, entry_point_offset));
+  EmitCallsiteMetadata(token_pos, deopt_id, RawPcDescriptors::kIcCall, locs);
   __ Drop(ic_data.CountWithTypeArgs());
 }
 
@@ -972,11 +980,11 @@
   __ Drop(args_desc.CountWithTypeArgs());
 }
 
-void FlowGraphCompiler::EmitSwitchableInstanceCall(const ICData& ic_data,
-                                                   intptr_t deopt_id,
-                                                   TokenPosition token_pos,
-                                                   LocationSummary* locs,
-                                                   Code::EntryKind entry_kind) {
+void FlowGraphCompiler::EmitInstanceCallAOT(const ICData& ic_data,
+                                            intptr_t deopt_id,
+                                            TokenPosition token_pos,
+                                            LocationSummary* locs,
+                                            Code::EntryKind entry_kind) {
   // Only generated with precompilation.
   UNREACHABLE();
 }
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc b/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc
index 5542b19..406524b 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc
@@ -831,7 +831,7 @@
   // TOS: return address.
   // +1 : receiver.
   // Sequence node has one return node, its input is load field node.
-  __ Comment("Inlined Getter");
+  __ Comment("Intrinsic Getter");
   __ movq(RAX, Address(RSP, 1 * kWordSize));
   __ movq(RAX, FieldAddress(RAX, offset));
   __ ret();
@@ -842,7 +842,7 @@
   // +1 : value
   // +2 : receiver.
   // Sequence node has one store node and one return NULL node.
-  __ Comment("Inlined Setter");
+  __ Comment("Intrinsic Setter");
   __ movq(RAX, Address(RSP, 2 * kWordSize));  // Receiver.
   __ movq(RBX, Address(RSP, 1 * kWordSize));  // Value.
   __ StoreIntoObject(RAX, FieldAddress(RAX, offset), RBX);
@@ -1051,17 +1051,25 @@
   __ Drop(ic_data.CountWithTypeArgs(), RCX);
 }
 
-void FlowGraphCompiler::EmitInstanceCall(const Code& stub,
-                                         const ICData& ic_data,
-                                         intptr_t deopt_id,
-                                         TokenPosition token_pos,
-                                         LocationSummary* locs) {
+void FlowGraphCompiler::EmitInstanceCallJIT(const Code& stub,
+                                            const ICData& ic_data,
+                                            intptr_t deopt_id,
+                                            TokenPosition token_pos,
+                                            LocationSummary* locs,
+                                            Code::EntryKind entry_kind) {
+  ASSERT(entry_kind == Code::EntryKind::kNormal ||
+         entry_kind == Code::EntryKind::kUnchecked);
   ASSERT(Array::Handle(zone(), ic_data.arguments_descriptor()).Length() > 0);
   // Load receiver into RDX.
   __ movq(RDX, Address(RSP, (ic_data.CountWithoutTypeArgs() - 1) * kWordSize));
   __ LoadUniqueObject(RBX, ic_data);
-  GenerateDartCall(deopt_id, token_pos, stub, RawPcDescriptors::kIcCall, locs,
-                   Code::EntryKind::kMonomorphic);
+  __ LoadUniqueObject(CODE_REG, stub);
+  const intptr_t entry_point_offset =
+      entry_kind == Code::EntryKind::kNormal
+          ? Code::entry_point_offset(Code::EntryKind::kMonomorphic)
+          : Code::entry_point_offset(Code::EntryKind::kMonomorphicUnchecked);
+  __ call(FieldAddress(CODE_REG, entry_point_offset));
+  EmitCallsiteMetadata(token_pos, deopt_id, RawPcDescriptors::kIcCall, locs);
   __ Drop(ic_data.CountWithTypeArgs(), RCX);
 }
 
@@ -1107,24 +1115,24 @@
   __ Drop(args_desc.CountWithTypeArgs(), RCX);
 }
 
-void FlowGraphCompiler::EmitSwitchableInstanceCall(const ICData& ic_data,
-                                                   intptr_t deopt_id,
-                                                   TokenPosition token_pos,
-                                                   LocationSummary* locs,
-                                                   Code::EntryKind entry_kind) {
+void FlowGraphCompiler::EmitInstanceCallAOT(const ICData& ic_data,
+                                            intptr_t deopt_id,
+                                            TokenPosition token_pos,
+                                            LocationSummary* locs,
+                                            Code::EntryKind entry_kind) {
   ASSERT(entry_kind == Code::EntryKind::kNormal ||
          entry_kind == Code::EntryKind::kUnchecked);
   ASSERT(ic_data.NumArgsTested() == 1);
   const Code& initial_stub = StubCode::ICCallThroughFunction();
 
-  __ Comment("SwitchableCall");
+  __ Comment("InstanceCallAOT");
   __ movq(RDX, Address(RSP, (ic_data.CountWithoutTypeArgs() - 1) * kWordSize));
   if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
     // The AOT runtime will replace the slot in the object pool with the
     // entrypoint address - see clustered_snapshot.cc.
     __ LoadUniqueObject(RCX, initial_stub);
   } else {
-    intptr_t entry_point_offset =
+    const intptr_t entry_point_offset =
         entry_kind == Code::EntryKind::kNormal
             ? Code::entry_point_offset(Code::EntryKind::kMonomorphic)
             : Code::entry_point_offset(Code::EntryKind::kMonomorphicUnchecked);
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index 6c06e20..d6e6059 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -723,6 +723,23 @@
   return cids;
 }
 
+static intptr_t Usage(const Function& function) {
+  intptr_t count = function.usage_counter();
+  if (count < 0) {
+    if (function.HasCode()) {
+      // 'function' is queued for optimized compilation
+      count = FLAG_optimization_counter_threshold;
+    } else {
+      // 'function' is queued for unoptimized compilation
+      count = FLAG_compilation_counter_threshold;
+    }
+  } else if (Code::IsOptimized(function.CurrentCode())) {
+    // 'function' was optimized and stopped counting
+    count = FLAG_optimization_counter_threshold;
+  }
+  return count;
+}
+
 void Cids::CreateHelper(Zone* zone,
                         const ICData& ic_data,
                         int argument_number,
@@ -748,12 +765,38 @@
     }
     if (include_targets) {
       Function& function = Function::ZoneHandle(zone, ic_data.GetTargetAt(i));
-      cid_ranges_.Add(new (zone) TargetInfo(
-          id, id, &function, ic_data.GetCountAt(i), ic_data.GetExactnessAt(i)));
+      intptr_t count = ic_data.GetCountAt(i);
+      cid_ranges_.Add(new (zone) TargetInfo(id, id, &function, count,
+                                            ic_data.GetExactnessAt(i)));
     } else {
       cid_ranges_.Add(new (zone) CidRange(id, id));
     }
   }
+
+  if (ic_data.is_megamorphic()) {
+    const MegamorphicCache& cache =
+        MegamorphicCache::Handle(zone, ic_data.AsMegamorphicCache());
+    SafepointMutexLocker ml(Isolate::Current()->megamorphic_mutex());
+    MegamorphicCacheEntries entries(Array::Handle(zone, cache.buckets()));
+    for (intptr_t i = 0; i < entries.Length(); i++) {
+      const intptr_t id =
+          Smi::Value(entries[i].Get<MegamorphicCache::kClassIdIndex>());
+      if (id == kIllegalCid) {
+        continue;
+      }
+      if (include_targets) {
+        Function& function = Function::ZoneHandle(zone);
+        function ^= entries[i].Get<MegamorphicCache::kTargetFunctionIndex>();
+        const intptr_t filled_entry_count = cache.filled_entry_count();
+        ASSERT(filled_entry_count > 0);
+        cid_ranges_.Add(new (zone) TargetInfo(
+            id, id, &function, Usage(function) / filled_entry_count,
+            StaticTypeExactnessState::NotTracking()));
+      } else {
+        cid_ranges_.Add(new (zone) CidRange(id, id));
+      }
+    }
+  }
 }
 
 bool Cids::IsMonomorphic() const {
@@ -3824,6 +3867,14 @@
   Sort(OrderByFrequency);
 }
 
+void CallTargets::Print() const {
+  for (intptr_t i = 0; i < length(); i++) {
+    THR_Print("cid = [%" Pd ", %" Pd "], count = %" Pd ", target = %s\n",
+              TargetAt(i)->cid_start, TargetAt(i)->cid_end, TargetAt(i)->count,
+              TargetAt(i)->target->ToQualifiedCString());
+  }
+}
+
 // Shared code generation methods (EmitNativeCode and
 // MakeLocationSummary). Only assembly code that can be shared across all
 // architectures can be used. Machine specific register allocation and code
@@ -3914,7 +3965,11 @@
   const Function& function = compiler->parsed_function().function();
   if (function.IsDynamicFunction()) {
     compiler->SpecialStatsBegin(CombinedCodeStatistics::kTagCheckedEntry);
-    __ MonomorphicCheckedEntry();
+    if (!FLAG_precompiled_mode) {
+      __ MonomorphicCheckedEntryJIT();
+    } else {
+      __ MonomorphicCheckedEntryAOT();
+    }
     compiler->SpecialStatsEnd(CombinedCodeStatistics::kTagCheckedEntry);
   }
 
@@ -4349,8 +4404,8 @@
     }
     if (is_smi_two_args_op) {
       ASSERT(ArgumentCount() == 2);
-      compiler->EmitInstanceCall(stub, *call_ic_data, deopt_id(), token_pos(),
-                                 locs());
+      compiler->EmitInstanceCallJIT(stub, *call_ic_data, deopt_id(),
+                                    token_pos(), locs(), entry_kind());
     } else {
       compiler->GenerateInstanceCall(deopt_id(), token_pos(), locs(),
                                      *call_ic_data);
@@ -4514,10 +4569,15 @@
 Definition* InstanceCallInstr::Canonicalize(FlowGraph* flow_graph) {
   const intptr_t receiver_cid = Receiver()->Type()->ToCid();
 
-  // TODO(erikcorry): Even for cold call sites we could still try to look up
-  // methods when we know the receiver cid. We don't currently do this because
-  // it turns the InstanceCall into a PolymorphicInstanceCall which doesn't get
-  // recognized or inlined when it is cold.
+  // We could turn cold call sites for known receiver cids into a StaticCall.
+  // However, that keeps the ICData of the InstanceCall from being updated.
+  // This is fine if there is no later deoptimization, but if there is, then
+  // the InstanceCall with the updated ICData for this receiver may then be
+  // better optimized by the compiler.
+  //
+  // TODO(dartbug.com/37291): Allow this optimization, but accumulate affected
+  // InstanceCallInstrs and the corresponding reciever cids during compilation.
+  // After compilation, add receiver checks to the ICData for those call sites.
   if (ic_data()->NumberOfUsedChecks() == 0) return this;
 
   const CallTargets* new_target =
@@ -4532,8 +4592,8 @@
 
   ASSERT(new_target->HasSingleTarget());
   const Function& target = new_target->FirstTarget();
-  StaticCallInstr* specialized =
-      StaticCallInstr::FromCall(flow_graph->zone(), this, target);
+  StaticCallInstr* specialized = StaticCallInstr::FromCall(
+      flow_graph->zone(), this, target, new_target->AggregateCallCount());
   flow_graph->InsertBefore(this, specialized, env(), FlowGraph::kValue);
   return specialized;
 }
diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h
index 757c185..21d4bf9 100644
--- a/runtime/vm/compiler/backend/il.h
+++ b/runtime/vm/compiler/backend/il.h
@@ -622,6 +622,8 @@
   const Function& FirstTarget() const;
   const Function& MostPopularTarget() const;
 
+  void Print() const;
+
  private:
   void MergeIntoRanges();
 };
@@ -3982,7 +3984,8 @@
   template <class C>
   static StaticCallInstr* FromCall(Zone* zone,
                                    const C* call,
-                                   const Function& target) {
+                                   const Function& target,
+                                   intptr_t call_count) {
     PushArgumentsArray* args =
         new (zone) PushArgumentsArray(call->ArgumentCount());
     for (intptr_t i = 0; i < call->ArgumentCount(); i++) {
@@ -3991,7 +3994,7 @@
     StaticCallInstr* new_call = new (zone)
         StaticCallInstr(call->token_pos(), target, call->type_args_len(),
                         call->argument_names(), args, call->deopt_id(),
-                        call->CallCount(), ICData::kNoRebind);
+                        call_count, ICData::kNoRebind);
     if (call->result_type() != NULL) {
       new_call->result_type_ = call->result_type();
     }
diff --git a/runtime/vm/compiler/backend/il_arm.cc b/runtime/vm/compiler/backend/il_arm.cc
index c687ec4..d92ef09 100644
--- a/runtime/vm/compiler/backend/il_arm.cc
+++ b/runtime/vm/compiler/backend/il_arm.cc
@@ -3262,7 +3262,7 @@
 LocationSummary* CheckStackOverflowInstr::MakeLocationSummary(Zone* zone,
                                                               bool opt) const {
   const intptr_t kNumInputs = 0;
-  const intptr_t kNumTemps = 1;
+  const intptr_t kNumTemps = 2;
   const bool using_shared_stub = UseSharedSlowPathStub(opt);
   ASSERT((kReservedCpuRegisters & (1 << LR)) != 0);
   LocationSummary* summary = new (zone)
@@ -3270,6 +3270,7 @@
                       using_shared_stub ? LocationSummary::kCallOnSharedSlowPath
                                         : LocationSummary::kCallOnSlowPath);
   summary->set_temp(0, Location::RequiresRegister());
+  summary->set_temp(1, Location::RequiresRegister());
   return summary;
 }
 
@@ -3377,16 +3378,22 @@
   compiler->AddSlowPathCode(slow_path);
   __ b(slow_path->entry_label(), LS);
   if (compiler->CanOSRFunction() && in_loop()) {
-    const Register temp = locs()->temp(0).reg();
+    const Register function = locs()->temp(0).reg();
+    const Register count = locs()->temp(1).reg();
     // In unoptimized code check the usage counter to trigger OSR at loop
     // stack checks.  Use progressively higher thresholds for more deeply
     // nested loops to attempt to hit outer loops with OSR when possible.
-    __ LoadObject(temp, compiler->parsed_function().function());
+    __ LoadObject(function, compiler->parsed_function().function());
     intptr_t threshold =
         FLAG_optimization_counter_threshold * (loop_depth() + 1);
-    __ ldr(temp, FieldAddress(
-                     temp, compiler::target::Function::usage_counter_offset()));
-    __ CompareImmediate(temp, threshold);
+    __ ldr(count,
+           FieldAddress(function,
+                        compiler::target::Function::usage_counter_offset()));
+    __ add(count, count, Operand(1));
+    __ str(count,
+           FieldAddress(function,
+                        compiler::target::Function::usage_counter_offset()));
+    __ CompareImmediate(count, threshold);
     __ b(slow_path->osr_entry_label(), GE);
   }
   if (compiler->ForceSlowPathForStackOverflow()) {
diff --git a/runtime/vm/compiler/backend/il_arm64.cc b/runtime/vm/compiler/backend/il_arm64.cc
index 1b5b2c3..7f375ea 100644
--- a/runtime/vm/compiler/backend/il_arm64.cc
+++ b/runtime/vm/compiler/backend/il_arm64.cc
@@ -887,9 +887,6 @@
   __ set_constant_pool_allowed(false);
   __ EnterDartFrame(0, PP);
 
-  // Save the stack limit address.
-  __ PushRegister(CSP);
-
   // Make space for arguments and align the frame.
   __ ReserveAlignedFrameSpace(compiler::ffi::NumStackSlots(arg_locations_) *
                               kWordSize);
@@ -905,28 +902,30 @@
 
   // We need to copy a dummy return address up into the dummy stack frame so the
   // stack walker will know which safepoint to use.
-  __ adr(temp, Immediate(0));
+  //
+  // ADR loads relative to itself, so add kInstrSize to point to the next
+  // instruction.
+  __ adr(temp, Immediate(Instr::kInstrSize));
   compiler->EmitCallsiteMetadata(token_pos(), DeoptId::kNone,
                                  RawPcDescriptors::Kind::kOther, locs());
 
   __ StoreToOffset(temp, FPREG, kSavedCallerPcSlotFromFp * kWordSize);
 
+  // Update information in the thread object and enter a safepoint.
+  __ TransitionGeneratedToNative(branch, FPREG, temp);
+
   // We are entering runtime code, so the C stack pointer must be restored from
   // the stack limit to the top of the stack.
   __ mov(CSP, SP);
 
-  // Update information in the thread object and enter a safepoint.
-  __ TransitionGeneratedToNative(branch, FPREG, temp);
-
   __ blr(branch);
 
+  // Restore the Dart stack pointer.
+  __ mov(SP, CSP);
+
   // Update information in the thread object and leave the safepoint.
   __ TransitionNativeToGenerated(temp);
 
-  // Restore the Dart stack pointer and the saved C stack pointer.
-  __ mov(SP, CSP);
-  __ LoadFromOffset(CSP, FPREG, kFirstLocalSlotFromFp * kWordSize);
-
   // Refresh write barrier mask.
   __ ldr(BARRIER_MASK,
          Address(THR, compiler::target::Thread::write_barrier_mask_offset()));
@@ -3004,15 +3003,19 @@
   __ CompareRegisters(SP, TMP);
   __ b(slow_path->entry_label(), LS);
   if (compiler->CanOSRFunction() && in_loop()) {
-    const Register temp = locs()->temp(0).reg();
+    const Register function = locs()->temp(0).reg();
     // In unoptimized code check the usage counter to trigger OSR at loop
     // stack checks.  Use progressively higher thresholds for more deeply
     // nested loops to attempt to hit outer loops with OSR when possible.
-    __ LoadObject(temp, compiler->parsed_function().function());
+    __ LoadObject(function, compiler->parsed_function().function());
     intptr_t threshold =
         FLAG_optimization_counter_threshold * (loop_depth() + 1);
-    __ LoadFieldFromOffset(temp, temp, Function::usage_counter_offset(), kWord);
-    __ CompareImmediate(temp, threshold);
+    __ LoadFieldFromOffset(TMP, function, Function::usage_counter_offset(),
+                           kWord);
+    __ add(TMP, TMP, Operand(1));
+    __ StoreFieldToOffset(TMP, function, Function::usage_counter_offset(),
+                          kWord);
+    __ CompareImmediate(TMP, threshold);
     __ b(slow_path->osr_entry_label(), GE);
   }
   if (compiler->ForceSlowPathForStackOverflow()) {
diff --git a/runtime/vm/compiler/backend/il_dbc.cc b/runtime/vm/compiler/backend/il_dbc.cc
index c869600..c0a3402 100644
--- a/runtime/vm/compiler/backend/il_dbc.cc
+++ b/runtime/vm/compiler/backend/il_dbc.cc
@@ -77,6 +77,8 @@
     intptr_t num_temps = 0) {
   LocationSummary* locs =
       new (zone) LocationSummary(zone, num_inputs, num_temps, contains_call);
+  ASSERT(contains_call == LocationSummary::kNoCall ||
+         num_inputs <= kMaxNumberOfFixedInputRegistersUsedByIL);
   for (intptr_t i = 0; i < num_inputs; i++) {
     locs->set_in(i, (contains_call == LocationSummary::kNoCall)
                         ? Location::RequiresRegister()
@@ -1356,6 +1358,7 @@
       new (zone) LocationSummary(zone, 0, 0, LocationSummary::kCall);
   // TODO(vegorov) support allocating out registers for calls.
   // Currently we require them to be fixed.
+  ASSERT(0 < kMaxNumberOfFixedInputRegistersUsedByIL);
   result->set_out(0, Location::RegisterLocation(0));
   return result;
 }
diff --git a/runtime/vm/compiler/backend/il_ia32.cc b/runtime/vm/compiler/backend/il_ia32.cc
index 7268abe..77aee55 100644
--- a/runtime/vm/compiler/backend/il_ia32.cc
+++ b/runtime/vm/compiler/backend/il_ia32.cc
@@ -2858,6 +2858,7 @@
     __ LoadObject(EDI, compiler->parsed_function().function());
     intptr_t threshold =
         FLAG_optimization_counter_threshold * (loop_depth() + 1);
+    __ incl(FieldAddress(EDI, Function::usage_counter_offset()));
     __ cmpl(FieldAddress(EDI, Function::usage_counter_offset()),
             Immediate(threshold));
     __ j(GREATER_EQUAL, slow_path->osr_entry_label());
diff --git a/runtime/vm/compiler/backend/il_test_helper.h b/runtime/vm/compiler/backend/il_test_helper.h
index 0cad91f..019b0d5 100644
--- a/runtime/vm/compiler/backend/il_test_helper.h
+++ b/runtime/vm/compiler/backend/il_test_helper.h
@@ -297,8 +297,7 @@
     Zone* zone = thread->zone();
     ParsedFunction* parsed_function = new (zone) ParsedFunction(thread, func);
 
-    parsed_function->SetNodeSequence(new SequenceNode(
-        TokenPosition::kNoSource, new LocalScope(nullptr, 0, 0)));
+    parsed_function->set_scope(new LocalScope(nullptr, 0, 0));
 
     auto graph_entry =
         new GraphEntryInstr(*parsed_function, Compiler::kNoOSRDeoptId);
diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc
index c0d1f41..a3570c2 100644
--- a/runtime/vm/compiler/backend/il_x64.cc
+++ b/runtime/vm/compiler/backend/il_x64.cc
@@ -2989,6 +2989,7 @@
     __ LoadObject(temp, compiler->parsed_function().function());
     int32_t threshold =
         FLAG_optimization_counter_threshold * (loop_depth() + 1);
+    __ incl(FieldAddress(temp, Function::usage_counter_offset()));
     __ cmpl(FieldAddress(temp, Function::usage_counter_offset()),
             Immediate(threshold));
     __ j(GREATER_EQUAL, slow_path->osr_entry_label());
diff --git a/runtime/vm/compiler/backend/inliner.cc b/runtime/vm/compiler/backend/inliner.cc
index 5235005..a98ed66 100644
--- a/runtime/vm/compiler/backend/inliner.cc
+++ b/runtime/vm/compiler/backend/inliner.cc
@@ -54,7 +54,7 @@
             "Always inline functions containing threshold or fewer calls.");
 DEFINE_FLAG(int,
             inlining_callee_size_threshold,
-            80,
+            160,
             "Do not inline callees larger than threshold");
 DEFINE_FLAG(int,
             inlining_small_leaf_size_threshold,
@@ -65,21 +65,6 @@
             50000,
             "Stop inlining once caller reaches the threshold.");
 DEFINE_FLAG(int,
-            inlining_constant_arguments_count,
-            1,
-            "Inline function calls with sufficient constant arguments "
-            "and up to the increased threshold on instructions");
-DEFINE_FLAG(
-    int,
-    inlining_constant_arguments_max_size_threshold,
-    200,
-    "Do not inline callees larger than threshold if constant arguments");
-DEFINE_FLAG(int,
-            inlining_constant_arguments_min_size_threshold,
-            60,
-            "Inline function calls with sufficient constant arguments "
-            "and up to the increased threshold on instructions");
-DEFINE_FLAG(int,
             inlining_hotness,
             10,
             "Inline only hotter calls, in percents (0 .. 100); "
@@ -781,41 +766,34 @@
   // Inlining heuristics based on Cooper et al. 2008.
   InliningDecision ShouldWeInline(const Function& callee,
                                   intptr_t instr_count,
-                                  intptr_t call_site_count,
-                                  intptr_t const_arg_count) {
+                                  intptr_t call_site_count) {
+    // Pragma or size heuristics.
     if (inliner_->AlwaysInline(callee)) {
       return InliningDecision::Yes("AlwaysInline");
-    }
-    if (inlined_size_ > FLAG_inlining_caller_size_threshold) {
-      // Prevent methods becoming humongous and thus slow to compile.
+    } else if (inlined_size_ > FLAG_inlining_caller_size_threshold) {
+      // Prevent caller methods becoming humongous and thus slow to compile.
       return InliningDecision::No("--inlining-caller-size-threshold");
-    }
-    if (const_arg_count > 0) {
-      if (instr_count > FLAG_inlining_constant_arguments_max_size_threshold) {
-        return InliningDecision(
-            false, "--inlining-constant-arguments-max-size-threshold");
-      }
     } else if (instr_count > FLAG_inlining_callee_size_threshold) {
+      // Prevent inlining of callee methods that exceed certain size.
       return InliningDecision::No("--inlining-callee-size-threshold");
     }
-    int callee_inlining_depth = callee.inlining_depth();
-    if (callee_inlining_depth > 0 && callee_inlining_depth + inlining_depth_ >
-                                         FLAG_inlining_depth_threshold) {
+    // Inlining depth.
+    const int callee_inlining_depth = callee.inlining_depth();
+    if (callee_inlining_depth > 0 &&
+        ((callee_inlining_depth + inlining_depth_) >
+         FLAG_inlining_depth_threshold)) {
       return InliningDecision::No("--inlining-depth-threshold");
     }
-    // 'instr_count' can be 0 if it was not computed yet.
-    if ((instr_count != 0) && (instr_count <= FLAG_inlining_size_threshold)) {
+    // Situation instr_count == 0 denotes no counts have been computed yet.
+    // In that case, we say ok to the early heuristic and come back with the
+    // late heuristic.
+    if (instr_count == 0) {
+      return InliningDecision::Yes("need to count first");
+    } else if (instr_count <= FLAG_inlining_size_threshold) {
       return InliningDecision::Yes("--inlining-size-threshold");
-    }
-    if (call_site_count <= FLAG_inlining_callee_call_sites_threshold) {
+    } else if (call_site_count <= FLAG_inlining_callee_call_sites_threshold) {
       return InliningDecision::Yes("--inlining-callee-call-sites-threshold");
     }
-    if ((const_arg_count >= FLAG_inlining_constant_arguments_count) &&
-        (instr_count <= FLAG_inlining_constant_arguments_min_size_threshold)) {
-      return InliningDecision(true,
-                              "--inlining-constant-arguments-count and "
-                              "inlining-constant-arguments-min-size-threshold");
-    }
     return InliningDecision::No("default");
   }
 
@@ -955,11 +933,19 @@
       return false;
     }
 
+    // Apply early heuristics. For a specialized case
+    // (constants_arg_counts > 0), don't use a previously
+    // estimate of the call site and instruction counts.
+    // Note that at this point, optional constant parameters
+    // are not counted yet, which makes this decision approximate.
     GrowableArray<Value*>* arguments = call_data->arguments;
-    const intptr_t constant_arguments = CountConstants(*arguments);
-    InliningDecision decision = ShouldWeInline(
-        function, function.optimized_instruction_count(),
-        function.optimized_call_site_count(), constant_arguments);
+    const intptr_t constant_arg_count = CountConstants(*arguments);
+    const intptr_t instruction_count =
+        constant_arg_count == 0 ? function.optimized_instruction_count() : 0;
+    const intptr_t call_site_count =
+        constant_arg_count == 0 ? function.optimized_call_site_count() : 0;
+    InliningDecision decision =
+        ShouldWeInline(function, instruction_count, call_site_count);
     if (!decision.value) {
       TRACE_INLINING(
           THR_Print("     Bailout: early heuristics (%s) with "
@@ -967,9 +953,8 @@
                     "call sites: %" Pd ", "
                     "inlining depth of callee: %d, "
                     "const args: %" Pd "\n",
-                    decision.reason, function.optimized_instruction_count(),
-                    function.optimized_call_site_count(),
-                    function.inlining_depth(), constant_arguments));
+                    decision.reason, instruction_count, call_site_count,
+                    function.inlining_depth(), constant_arg_count));
       PRINT_INLINING_TREE("Early heuristic", &call_data->caller, &function,
                           call_data->call);
       return false;
@@ -1193,26 +1178,26 @@
           printer.PrintBlocks();
         }
 
-        // Collect information about the call site and caller graph.
-        // TODO(zerny): Do this after CP and dead code elimination.
+        // Collect information about the call site and caller graph. At this
+        // point, optional constant parameters are counted too, making the
+        // specialized vs. non-specialized decision accurate.
         intptr_t constants_count = 0;
-        for (intptr_t i = 0; i < param_stubs->length(); ++i) {
+        for (intptr_t i = 0, n = param_stubs->length(); i < n; ++i) {
           if ((*param_stubs)[i]->IsConstant()) ++constants_count;
         }
-
-        FlowGraphInliner::CollectGraphInfo(callee_graph);
-        const intptr_t size = function.optimized_instruction_count();
-        const intptr_t call_site_count = function.optimized_call_site_count();
+        intptr_t instruction_count = 0;
+        intptr_t call_site_count = 0;
+        FlowGraphInliner::CollectGraphInfo(callee_graph, constants_count,
+                                           /*force*/ false, &instruction_count,
+                                           &call_site_count);
 
         // Use heuristics do decide if this call should be inlined.
         InliningDecision decision =
-            ShouldWeInline(function, size, call_site_count, constants_count);
+            ShouldWeInline(function, instruction_count, call_site_count);
         if (!decision.value) {
           // If size is larger than all thresholds, don't consider it again.
-          if ((size > FLAG_inlining_size_threshold) &&
-              (call_site_count > FLAG_inlining_callee_call_sites_threshold) &&
-              (size > FLAG_inlining_constant_arguments_min_size_threshold) &&
-              (size > FLAG_inlining_constant_arguments_max_size_threshold)) {
+          if ((instruction_count > FLAG_inlining_size_threshold) &&
+              (call_site_count > FLAG_inlining_callee_call_sites_threshold)) {
             function.set_is_inlinable(false);
           }
           TRACE_INLINING(
@@ -1221,7 +1206,7 @@
                         "call sites: %" Pd ", "
                         "inlining depth of callee: %d, "
                         "const args: %" Pd "\n",
-                        decision.reason, size, call_site_count,
+                        decision.reason, instruction_count, call_site_count,
                         function.inlining_depth(), constants_count));
           PRINT_INLINING_TREE("Heuristic fail", &call_data->caller, &function,
                               call_data->call);
@@ -1231,6 +1216,7 @@
         // If requested, a stricter heuristic is applied to this inlining. This
         // heuristic always scans the method (rather than possibly reusing
         // cached results) to make sure all specializations are accounted for.
+        // TODO(ajcbik): with the now better bookkeeping, explore removing this
         if (stricter_heuristic) {
           if (!IsSmallLeaf(callee_graph)) {
             TRACE_INLINING(
@@ -1254,7 +1240,7 @@
 
         // Build succeeded so we restore the bailout jump.
         inlined_ = true;
-        inlined_size_ += size;
+        inlined_size_ += instruction_count;
         if (is_recursive_call) {
           inlined_recursive_call_ = true;
         }
@@ -1284,8 +1270,7 @@
         TRACE_INLINING(THR_Print("     Success\n"));
         TRACE_INLINING(THR_Print(
             "       with reason %s, code size %" Pd ", call sites: %" Pd "\n",
-            decision.reason, function.optimized_instruction_count(),
-            call_site_count));
+            decision.reason, instruction_count, call_site_count));
         PRINT_INLINING_TREE(NULL, &call_data->caller, &function, call);
         return true;
       } else {
@@ -1481,6 +1466,10 @@
   }
 
   bool InlineClosureCalls() {
+    // Under this flag, tear off testing closure calls appear before the
+    // StackOverflowInstr, which breaks assertions in our compiler when inlined.
+    // TODO(sjindel): move testing closure calls after first check
+    if (FLAG_enable_testing_pragmas) return false;  // keep all closures
     bool inlined = false;
     const GrowableArray<CallSites::ClosureCallInfo>& call_info =
         inlining_call_sites_->closure_calls();
@@ -2237,15 +2226,37 @@
       speculative_policy_(speculative_policy),
       precompiler_(precompiler) {}
 
-void FlowGraphInliner::CollectGraphInfo(FlowGraph* flow_graph, bool force) {
+void FlowGraphInliner::CollectGraphInfo(FlowGraph* flow_graph,
+                                        intptr_t constants_count,
+                                        bool force,
+                                        intptr_t* instruction_count,
+                                        intptr_t* call_site_count) {
   const Function& function = flow_graph->function();
+  // For OSR, don't even bother.
+  if (flow_graph->IsCompiledForOsr()) {
+    *instruction_count = 0;
+    *call_site_count = 0;
+    return;
+  }
+  // Specialized case: always recompute, never cache.
+  if (constants_count > 0) {
+    ASSERT(!force);
+    GraphInfoCollector info;
+    info.Collect(*flow_graph);
+    *instruction_count = info.instruction_count();
+    *call_site_count = info.call_site_count();
+    return;
+  }
+  // Non-specialized case: unless forced, only recompute on a cache miss.
+  ASSERT(constants_count == 0);
   if (force || (function.optimized_instruction_count() == 0)) {
     GraphInfoCollector info;
     info.Collect(*flow_graph);
-
     function.SetOptimizedInstructionCountClamped(info.instruction_count());
     function.SetOptimizedCallSiteCountClamped(info.call_site_count());
   }
+  *instruction_count = function.optimized_instruction_count();
+  *call_site_count = function.optimized_call_site_count();
 }
 
 // TODO(srdjan): This is only needed when disassembling and/or profiling.
@@ -2312,9 +2323,15 @@
 }
 
 int FlowGraphInliner::Inline() {
-  // Collect graph info and store it on the function.
-  // We might later use it for an early bailout from the inlining.
-  CollectGraphInfo(flow_graph_);
+  // Collect some early graph information assuming it is non-specialized
+  // so that the cached approximation may be used later for an early
+  // bailout from inlining.
+  intptr_t instruction_count = 0;
+  intptr_t call_site_count = 0;
+  FlowGraphInliner::CollectGraphInfo(flow_graph_,
+                                     /*constants_count*/ 0,
+                                     /*force*/ false, &instruction_count,
+                                     &call_site_count);
 
   const Function& top = flow_graph_->function();
   if ((FLAG_inlining_filter != NULL) &&
diff --git a/runtime/vm/compiler/backend/inliner.h b/runtime/vm/compiler/backend/inliner.h
index 34366cf..fecd8d7 100644
--- a/runtime/vm/compiler/backend/inliner.h
+++ b/runtime/vm/compiler/backend/inliner.h
@@ -96,8 +96,21 @@
   // depth that we inlined.
   int Inline();
 
-  // Compute graph info if it was not already computed or if 'force' is true.
-  static void CollectGraphInfo(FlowGraph* flow_graph, bool force = false);
+  // Computes graph information (instruction and call site count).
+  // For the non-specialized cases (num_constants_args == 0), the
+  // method uses a cache to avoid recomputing the counts (the cached
+  // value may still be approximate but close). The 'force' flag is
+  // used to update the cached value at the end of running the full pipeline
+  // on non-specialized cases. Specialized cases (num_constants_args > 0)
+  // always recompute the counts without caching.
+  //
+  // TODO(ajcbik): cache for specific constant argument combinations too?
+  static void CollectGraphInfo(FlowGraph* flow_graph,
+                               intptr_t num_constant_args,
+                               bool force,
+                               intptr_t* instruction_count,
+                               intptr_t* call_site_count);
+
   static void SetInliningId(FlowGraph* flow_graph, intptr_t inlining_id);
 
   bool AlwaysInline(const Function& function);
diff --git a/runtime/vm/compiler/backend/linearscan.cc b/runtime/vm/compiler/backend/linearscan.cc
index b5fea9c..b777be2 100644
--- a/runtime/vm/compiler/backend/linearscan.cc
+++ b/runtime/vm/compiler/backend/linearscan.cc
@@ -703,6 +703,12 @@
       AssignSafepoints(defn, range);
       range->finger()->Initialize(range);
       slot_index = kNumberOfCpuRegisters - 1 - slot_index;
+      if (slot_index < kMaxNumberOfFixedInputRegistersUsedByIL) {
+        // We ran out of registers for the catch block parameters.
+        // Bail out to unoptimized code
+        flow_graph_.parsed_function().Bailout("FlowGraphAllocator", "CATCH");
+        UNREACHABLE();
+      }
       range->set_assigned_location(Location::RegisterLocation(slot_index));
       SplitInitialDefinitionAt(range, block->lifetime_position() + 2);
       ConvertAllUses(range);
diff --git a/runtime/vm/compiler/backend/redundancy_elimination_test.cc b/runtime/vm/compiler/backend/redundancy_elimination_test.cc
index e22f5e4..732b1dc 100644
--- a/runtime/vm/compiler/backend/redundancy_elimination_test.cc
+++ b/runtime/vm/compiler/backend/redundancy_elimination_test.cc
@@ -83,7 +83,7 @@
   OptimizeCatchEntryStates(graph, /*is_aot=*/true);
 
   EXPECT_EQ(1, graph->graph_entry()->catch_entries().length());
-  auto scope = graph->parsed_function().node_sequence()->scope();
+  auto scope = graph->parsed_function().scope();
 
   GrowableArray<LocalVariable*> env;
   FlattenScopeIntoEnvironment(graph, scope, &env);
diff --git a/runtime/vm/compiler/backend/type_propagator.cc b/runtime/vm/compiler/backend/type_propagator.cc
index 413f0d7..1b93d0d 100644
--- a/runtime/vm/compiler/backend/type_propagator.cc
+++ b/runtime/vm/compiler/backend/type_propagator.cc
@@ -1031,8 +1031,8 @@
   }
 
   if (function.HasBytecode() &&
-      graph_entry->parsed_function().node_sequence() == nullptr) {
-    // TODO(alexmarkov): Consider adding node_sequence() and scope.
+      graph_entry->parsed_function().scope() == nullptr) {
+    // TODO(alexmarkov): Consider adding scope.
     return CompileType::Dynamic();
   }
 
@@ -1040,7 +1040,7 @@
       graph_entry->unchecked_entry() == block_;
 
   if (Isolate::Current()->can_use_strong_mode_types()) {
-    LocalScope* scope = graph_entry->parsed_function().node_sequence()->scope();
+    LocalScope* scope = graph_entry->parsed_function().scope();
     // Note: in catch-blocks we have ParameterInstr for each local variable
     // not only for normal parameters.
     if (index() < scope->num_variables()) {
diff --git a/runtime/vm/compiler/backend/type_propagator_test.cc b/runtime/vm/compiler/backend/type_propagator_test.cc
index 89bc20c..c3b9909 100644
--- a/runtime/vm/compiler/backend/type_propagator_test.cc
+++ b/runtime/vm/compiler/backend/type_propagator_test.cc
@@ -37,8 +37,7 @@
                         String::Handle(Symbols::New(thread, "v0")),
                         AbstractType::ZoneHandle(Type::IntType()));
   v0_var->set_type_check_mode(LocalVariable::kTypeCheckedByCaller);
-  H.flow_graph()->parsed_function().node_sequence()->scope()->AddVariable(
-      v0_var);
+  H.flow_graph()->parsed_function().scope()->AddVariable(v0_var);
 
   auto normal_entry = H.flow_graph()->graph_entry()->normal_entry();
 
diff --git a/runtime/vm/compiler/call_specializer.cc b/runtime/vm/compiler/call_specializer.cc
index de3c1b5..7cf2dc1 100644
--- a/runtime/vm/compiler/call_specializer.cc
+++ b/runtime/vm/compiler/call_specializer.cc
@@ -314,7 +314,8 @@
 
   ASSERT(targets->HasSingleTarget());
   const Function& target = targets->FirstTarget();
-  StaticCallInstr* specialized = StaticCallInstr::FromCall(Z, call, target);
+  StaticCallInstr* specialized =
+      StaticCallInstr::FromCall(Z, call, target, targets->AggregateCallCount());
   call->ReplaceWith(specialized, current_iterator());
 }
 
diff --git a/runtime/vm/compiler/compiler_pass.cc b/runtime/vm/compiler/compiler_pass.cc
index 340b149..d101470 100644
--- a/runtime/vm/compiler/compiler_pass.cc
+++ b/runtime/vm/compiler/compiler_pass.cc
@@ -217,6 +217,9 @@
   INVOKE_PASS(TypePropagation);
   INVOKE_PASS(ApplyICData);
   INVOKE_PASS(Canonicalize);
+  // Run constant propagation to make sure we specialize for
+  // (optional) constant arguments passed into the inlined method.
+  INVOKE_PASS(ConstantPropagation);
   // Optimize (a << b) & c patterns, merge instructions. Must occur
   // before 'SelectRepresentations' which inserts conversion nodes.
   INVOKE_PASS(TryOptimizePatterns);
@@ -475,10 +478,17 @@
               { WriteBarrierElimination(flow_graph); });
 
 COMPILER_PASS(FinalizeGraph, {
-  // Compute and store graph informations (call & instruction counts)
-  // to be later used by the inliner.
-  FlowGraphInliner::CollectGraphInfo(flow_graph, true);
+  // At the end of the pipeline, force recomputing and caching graph
+  // information (instruction and call site counts) for the (assumed)
+  // non-specialized case with better values, for future inlining.
+  intptr_t instruction_count = 0;
+  intptr_t call_site_count = 0;
+  FlowGraphInliner::CollectGraphInfo(flow_graph,
+                                     /*constants_count*/ 0,
+                                     /*force*/ true, &instruction_count,
+                                     &call_site_count);
   flow_graph->function().set_inlining_depth(state->inlining_depth);
+  // Remove redefinitions for the rest of the pipeline.
   flow_graph->RemoveRedefinitions();
 });
 
diff --git a/runtime/vm/compiler/ffi_dbc_trampoline_x64_linux_mac.S b/runtime/vm/compiler/ffi_dbc_trampoline_x64_linux_mac.S
index e8d1918..4f42279 100644
--- a/runtime/vm/compiler/ffi_dbc_trampoline_x64_linux_mac.S
+++ b/runtime/vm/compiler/ffi_dbc_trampoline_x64_linux_mac.S
@@ -12,20 +12,33 @@
 _FfiTrampolineCall:
 #endif
 
-push  rbx
+/* Save argument in scratch register. */
+push  rbx              /* Backup caller saved register. */
 mov   rbx,  rdi        /* Save argument in scratch register. */
 
+/* Enter frame. */
+push  rbp
+mov   rbp,  rsp
+
+/* Reserve framespace for arguments. */
+mov   rax,  [rbx+0x80] /* Load number of stack arguments. */
+shl   rax,  3          /* Multiply by size (8 bytes). */
+sub   rsp,  rax        /* Reserve num_stack_args stack slots. */
+
+/* Stack alignment. */
+and   rsp,  [rbx+0x78] /* Align stack with stack alignment mask. */
+
 /* Copy stack arguments. */
-mov   rax,  [rbx+0x78] /* Load number of stack arguments. */
 cmp   rax,  0x0        /* Check if number of stack arguments is 0. */
 jz    .done            /* Skip loop if no stack arguments. */
-add   rbx,  0x78       /* Offset RBX to point to stack arguments */
+add   rsp,  rax        /* Unreserve stack slots so we can push arguments. */
+add   rbx,  0x80       /* Offset RBX to point to stack arguments */
 .loop:                 /* Copy stack arguments loop. */
-push  [rbx+0x8*rax]    /* Push stack argument. */
-sub   rax,  0x1        /* Decrement stack argument iterator. */
+push  [rbx+rax]        /* Push stack argument. */
+sub   rax,  0x8        /* Decrement stack argument iterator. */
 cmp   rax,  0x0        /* Compare iterator with 0 */
 jnz   .loop            /* Loop while iterator is not 0 */
-sub   rbx,  0x78       /* Restore RBX to original value. */
+sub   rbx,  0x80       /* Restore RBX to original value. */
 .done:                 /* End stack arguments loop. */
 
 /* Copy registers and fpu registers. */
@@ -52,12 +65,13 @@
 mov   [rbx],   rax  /* Move integer result in kOffsetIntResult */
 movsd [rbx+8], xmm0 /* Move double result in kOffsetDoubleResult */
 
-/* Clean up stack arguments. */
-mov   rax,  [rbx+0x78] /* Load number of stack arguments. */
-imul  rax,  0x8        /* Multiply by stack argument size. */
-add   rsp,  rax        /* Clean up the stack. */
+/* leave frame */
+mov   rsp,  rbp
+pop   rbp
 
+/* Restore caller saved register. */
 pop   rbx
+
 ret
 
 #endif /* HOST_ARCH_X64 */
diff --git a/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.cc b/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.cc
index 82204fd..fd2dd5d 100644
--- a/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.cc
+++ b/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.cc
@@ -208,7 +208,7 @@
     }
     ASSERT(idx == num_locals);
 
-    ASSERT(parsed_function()->node_sequence() == nullptr);
+    ASSERT(parsed_function()->scope() == nullptr);
     parsed_function()->AllocateBytecodeVariables(num_locals);
   }
 }
diff --git a/runtime/vm/compiler/frontend/bytecode_reader.cc b/runtime/vm/compiler/frontend/bytecode_reader.cc
index c180689..5d3ba7d 100644
--- a/runtime/vm/compiler/frontend/bytecode_reader.cc
+++ b/runtime/vm/compiler/frontend/bytecode_reader.cc
@@ -11,6 +11,7 @@
 #include "vm/compiler/assembler/disassembler_kbc.h"
 #include "vm/compiler/frontend/bytecode_scope_builder.h"
 #include "vm/constants_kbc.h"
+#include "vm/dart_api_impl.h"  // For Api::IsFfiEnabled().
 #include "vm/dart_entry.h"
 #include "vm/debugger.h"
 #include "vm/longjump.h"
@@ -53,17 +54,20 @@
       (function.kind() != RawFunction::kImplicitStaticGetter) &&
       (function.kind() != RawFunction::kMethodExtractor) &&
       (function.kind() != RawFunction::kInvokeFieldDispatcher) &&
+      (function.kind() != RawFunction::kDynamicInvocationForwarder) &&
       (function.kind() != RawFunction::kNoSuchMethodDispatcher)) {
     return;
   }
 
   BytecodeComponentData bytecode_component(
-      Array::Handle(helper_->zone_, GetBytecodeComponent()));
+      &Array::Handle(helper_->zone_, GetBytecodeComponent()));
   BytecodeReaderHelper bytecode_reader(&H, active_class_, &bytecode_component);
 
   bytecode_reader.ParseBytecodeFunction(parsed_function, function);
 }
 
+static_assert(KernelBytecode::kMinSupportedBytecodeFormatVersion < 10,
+              "Cleanup support for old bytecode format versions");
 bool BytecodeMetadataHelper::ReadMembers(intptr_t node_offset,
                                          const Class& cls,
                                          bool discard_fields) {
@@ -79,18 +83,65 @@
   ASSERT(Thread::Current()->IsMutatorThread());
 
   BytecodeComponentData bytecode_component(
-      Array::Handle(helper_->zone_, GetBytecodeComponent()));
+      &Array::Handle(helper_->zone_, GetBytecodeComponent()));
   BytecodeReaderHelper bytecode_reader(&H, active_class_, &bytecode_component);
 
   AlternativeReadingScope alt(&bytecode_reader.reader(), md_offset);
 
-  intptr_t members_offset = bytecode_reader.reader().ReadUInt();
+  intptr_t members_offset = bytecode_component.GetMembersOffset() +
+                            bytecode_reader.reader().ReadUInt();
 
-  bytecode_reader.ReadMembers(cls, members_offset, discard_fields);
+  AlternativeReadingScope alt2(&bytecode_reader.reader(), members_offset);
+
+  bytecode_reader.ReadMembers(cls, discard_fields);
 
   return true;
 }
 
+bool BytecodeMetadataHelper::ReadLibraries() {
+  TIMELINE_DURATION(Thread::Current(), Compiler,
+                    "BytecodeMetadataHelper::ReadLibraries");
+  ASSERT(Thread::Current()->IsMutatorThread());
+
+  if (translation_helper_.GetBytecodeComponent() == Array::null()) {
+    return false;
+  }
+
+  BytecodeComponentData bytecode_component(
+      &Array::Handle(helper_->zone_, GetBytecodeComponent()));
+
+  static_assert(KernelBytecode::kMinSupportedBytecodeFormatVersion < 10,
+                "Cleanup condition");
+  if (bytecode_component.GetVersion() < 10) {
+    return false;
+  }
+
+  BytecodeReaderHelper bytecode_reader(&H, active_class_, &bytecode_component);
+  AlternativeReadingScope alt(&bytecode_reader.reader(),
+                              bytecode_component.GetLibraryIndexOffset());
+  bytecode_reader.ReadLibraryDeclarations(bytecode_component.GetNumLibraries());
+  return true;
+}
+
+void BytecodeMetadataHelper::ReadLibrary(const Library& library) {
+  TIMELINE_DURATION(Thread::Current(), Compiler,
+                    "BytecodeMetadataHelper::ReadLibrary");
+  ASSERT(Thread::Current()->IsMutatorThread());
+  ASSERT(!library.Loaded());
+
+  if (translation_helper_.GetBytecodeComponent() == Array::null()) {
+    return;
+  }
+
+  BytecodeComponentData bytecode_component(
+      &Array::Handle(helper_->zone_, GetBytecodeComponent()));
+  BytecodeReaderHelper bytecode_reader(&H, active_class_, &bytecode_component);
+  AlternativeReadingScope alt(&bytecode_reader.reader(),
+                              library.bytecode_offset());
+  bytecode_reader.FindAndReadSpecificLibrary(
+      library, bytecode_component.GetNumLibraries());
+}
+
 RawLibrary* BytecodeMetadataHelper::GetMainLibrary() {
   const intptr_t md_offset = GetComponentMetadataPayloadOffset();
   if (md_offset < 0) {
@@ -98,7 +149,7 @@
   }
 
   BytecodeComponentData bytecode_component(
-      Array::Handle(helper_->zone_, GetBytecodeComponent()));
+      &Array::Handle(helper_->zone_, GetBytecodeComponent()));
   const intptr_t main_offset = bytecode_component.GetMainOffset();
   if (main_offset == 0) {
     return Library::null();
@@ -265,8 +316,152 @@
       if (FLAG_dump_kernel_bytecode) {
         KernelBytecodeDisassembler::Disassemble(closure);
       }
+
+#if !defined(PRODUCT)
+      thread_->isolate()->debugger()->NotifyBytecodeLoaded(closure);
+#endif
     }
   }
+
+#if !defined(PRODUCT)
+  thread_->isolate()->debugger()->NotifyBytecodeLoaded(function);
+#endif
+}
+
+static intptr_t IndexFor(Zone* zone,
+                         const Function& function,
+                         const String& name) {
+  const Bytecode& bc = Bytecode::Handle(zone, function.bytecode());
+  const ObjectPool& pool = ObjectPool::Handle(zone, bc.object_pool());
+  const KBCInstr* pc = reinterpret_cast<const KBCInstr*>(bc.PayloadStart());
+
+  ASSERT(KernelBytecode::IsEntryOptionalOpcode(pc));
+  ASSERT(KernelBytecode::DecodeB(pc) ==
+         function.NumOptionalPositionalParameters());
+  ASSERT(KernelBytecode::DecodeC(pc) == function.NumOptionalNamedParameters());
+  pc = KernelBytecode::Next(pc);
+
+  const intptr_t num_opt_params = function.NumOptionalParameters();
+  const intptr_t num_fixed_params = function.num_fixed_parameters();
+  for (intptr_t i = 0; i < num_opt_params; i++) {
+    const KBCInstr* load_name = pc;
+    const KBCInstr* load_value = KernelBytecode::Next(load_name);
+    pc = KernelBytecode::Next(load_value);
+    ASSERT(KernelBytecode::IsLoadConstantOpcode(load_name));
+    ASSERT(KernelBytecode::IsLoadConstantOpcode(load_value));
+    if (pool.ObjectAt(KernelBytecode::DecodeE(load_name)) == name.raw()) {
+      return num_fixed_params + i;
+    }
+  }
+
+  UNREACHABLE();
+  return -1;
+}
+
+RawArray* BytecodeReaderHelper::CreateForwarderChecks(
+    const Function& function) {
+  ASSERT(function.kind() != RawFunction::kDynamicInvocationForwarder);
+  ASSERT(function.is_declared_in_bytecode());
+
+  TypeArguments& default_args = TypeArguments::Handle(Z);
+  if (function.bytecode_offset() != 0) {
+    AlternativeReadingScope alt(&reader_, function.bytecode_offset());
+
+    const intptr_t flags = reader_.ReadUInt();
+    const bool has_parameters_flags =
+        (flags & Code::kHasParameterFlagsFlag) != 0;
+    const bool has_forwarding_stub_target =
+        (flags & Code::kHasForwardingStubTargetFlag) != 0;
+    const bool has_default_function_type_args =
+        (flags & Code::kHasDefaultFunctionTypeArgsFlag) != 0;
+
+    if (has_parameters_flags) {
+      intptr_t num_params = reader_.ReadUInt();
+      ASSERT(num_params ==
+             function.NumParameters() - function.NumImplicitParameters());
+      for (intptr_t i = 0; i < num_params; ++i) {
+        reader_.ReadUInt();
+      }
+    }
+
+    if (has_forwarding_stub_target) {
+      reader_.ReadUInt();
+    }
+
+    if (has_default_function_type_args) {
+      const intptr_t index = reader_.ReadUInt();
+      const Bytecode& code = Bytecode::Handle(Z, function.bytecode());
+      const ObjectPool& pool = ObjectPool::Handle(Z, code.object_pool());
+      default_args ^= pool.ObjectAt(index);
+    }
+  }
+
+  auto& name = String::Handle(Z);
+  auto& check = ParameterTypeCheck::Handle(Z);
+  auto& checks = GrowableObjectArray::Handle(Z, GrowableObjectArray::New());
+
+  checks.Add(function);
+  checks.Add(default_args);
+
+  const auto& type_params =
+      TypeArguments::Handle(Z, function.type_parameters());
+  if (!type_params.IsNull()) {
+    auto& type_param = TypeParameter::Handle(Z);
+    auto& bound = AbstractType::Handle(Z);
+    for (intptr_t i = 0, n = type_params.Length(); i < n; ++i) {
+      type_param ^= type_params.TypeAt(i);
+      bound = type_param.bound();
+      if (!bound.IsTopType() && !type_param.IsGenericCovariantImpl()) {
+        name = type_param.name();
+        ASSERT(type_param.IsFinalized());
+        check = ParameterTypeCheck::New();
+        check.set_param(type_param);
+        check.set_type_or_bound(bound);
+        check.set_name(name);
+        checks.Add(check);
+      }
+    }
+  }
+
+  const intptr_t num_params = function.NumParameters();
+  const intptr_t num_pos_params = function.HasOptionalNamedParameters()
+                                      ? function.num_fixed_parameters()
+                                      : num_params;
+
+  BitVector is_covariant(Z, num_params);
+  BitVector is_generic_covariant_impl(Z, num_params);
+  ReadParameterCovariance(function, &is_covariant, &is_generic_covariant_impl);
+
+  auto& type = AbstractType::Handle(Z);
+  auto& cache = SubtypeTestCache::Handle(Z);
+  const bool has_optional_parameters = function.HasOptionalParameters();
+  for (intptr_t i = function.NumImplicitParameters(); i < num_params; ++i) {
+    type = function.ParameterTypeAt(i);
+    if (!type.IsTopType() && !is_generic_covariant_impl.Contains(i) &&
+        !is_covariant.Contains(i)) {
+      name = function.ParameterNameAt(i);
+      intptr_t index;
+      if (i >= num_pos_params) {
+        // Named parameter.
+        index = IndexFor(Z, function, name);
+      } else if (has_optional_parameters) {
+        // Fixed or optional parameter.
+        index = i;
+      } else {
+        // Fixed parameter.
+        index = -kKBCParamEndSlotFromFp - num_params + i;
+      }
+      check = ParameterTypeCheck::New();
+      check.set_index(index);
+      check.set_type_or_bound(type);
+      check.set_name(name);
+      cache = SubtypeTestCache::New();
+      check.set_cache(cache);
+      checks.Add(check);
+    }
+  }
+
+  return Array::MakeFixedLength(checks);
 }
 
 void BytecodeReaderHelper::ReadClosureDeclaration(const Function& function,
@@ -831,6 +1026,23 @@
   reader_.ReadUInt32();  // Skip main.numItems
   const intptr_t main_offset = start_offset + reader_.ReadUInt32();
 
+  intptr_t num_libraries = 0;
+  intptr_t library_index_offset = 0;
+  intptr_t libraries_offset = 0;
+  intptr_t classes_offset = 0;
+  static_assert(KernelBytecode::kMinSupportedBytecodeFormatVersion < 10,
+                "Cleanup condition");
+  if (version >= 10) {
+    num_libraries = reader_.ReadUInt32();
+    library_index_offset = start_offset + reader_.ReadUInt32();
+
+    reader_.ReadUInt32();  // Skip libraries.numItems
+    libraries_offset = start_offset + reader_.ReadUInt32();
+
+    reader_.ReadUInt32();  // Skip classes.numItems
+    classes_offset = start_offset + reader_.ReadUInt32();
+  }
+
   reader_.ReadUInt32();  // Skip members.numItems
   const intptr_t members_offset = start_offset + reader_.ReadUInt32();
 
@@ -838,7 +1050,19 @@
   const intptr_t codes_offset = start_offset + reader_.ReadUInt32();
 
   reader_.ReadUInt32();  // Skip sourcePositions.numItems
-  const intptr_t sources_positions_offset = start_offset + reader_.ReadUInt32();
+  const intptr_t source_positions_offset = start_offset + reader_.ReadUInt32();
+
+  intptr_t source_files_offset = 0;
+  intptr_t line_starts_offset = 0;
+  static_assert(KernelBytecode::kMinSupportedBytecodeFormatVersion < 10,
+                "Cleanup condition");
+  if (version >= 10) {
+    reader_.ReadUInt32();  // Skip sourceFiles.numItems
+    source_files_offset = start_offset + reader_.ReadUInt32();
+
+    reader_.ReadUInt32();  // Skip lineStarts.numItems
+    line_starts_offset = start_offset + reader_.ReadUInt32();
+  }
 
   intptr_t local_variables_offset = 0;
   static_assert(KernelBytecode::kMinSupportedBytecodeFormatVersion < 9,
@@ -867,14 +1091,16 @@
   const intptr_t objects_contents_offset = reader_.offset();
   reader_.set_offset(objects_contents_offset + objects_size);
 
-  const Array& bytecode_component_array = Array::Handle(
+  auto& bytecode_component_array = Array::Handle(
       Z, BytecodeComponentData::New(
              Z, version, num_objects, string_table_offset,
              strings_contents_offset, objects_contents_offset, main_offset,
-             members_offset, codes_offset, sources_positions_offset,
+             num_libraries, library_index_offset, libraries_offset,
+             classes_offset, members_offset, codes_offset,
+             source_positions_offset, source_files_offset, line_starts_offset,
              local_variables_offset, annotations_offset, Heap::kOld));
 
-  BytecodeComponentData bytecode_component(bytecode_component_array);
+  BytecodeComponentData bytecode_component(&bytecode_component_array);
 
   // Read object offsets.
   Smi& offs = Smi::Handle(Z);
@@ -931,6 +1157,8 @@
 RawObject* BytecodeReaderHelper::ReadObjectContents(uint32_t header) {
   ASSERT(((header & kReferenceBit) == 0));
 
+  static_assert(KernelBytecode::kMinSupportedBytecodeFormatVersion < 10,
+                "Cleanup obsolete object kinds");
   // Must be in sync with enum ObjectKind in
   // pkg/vm/lib/bytecode/object_table.dart.
   enum ObjectKind {
@@ -939,15 +1167,17 @@
     kClass,
     kMember,
     kClosure,
-    kSimpleType,
-    kTypeParameter,
-    kGenericType,
-    kFunctionType,
+    kSimpleType,     // obsolete in v10
+    kTypeParameter,  // obsolete in v10
+    kGenericType,    // obsolete in v10
+    kFunctionType,   // obsolete in v10
     kName,
     kTypeArguments,
-    kFinalizedGenericType,
+    kFinalizedGenericType,  // obsolete in v10
     kConstObject,
     kArgDesc,
+    kScript,
+    kType,
   };
 
   // Member flags, must be in sync with _MemberHandle constants in
@@ -960,6 +1190,8 @@
   const intptr_t kFlagIsDynamic = kFlagBit0;
   const intptr_t kFlagIsVoid = kFlagBit1;
 
+  static_assert(KernelBytecode::kMinSupportedBytecodeFormatVersion < 10,
+                "Cleanup old FunctionType flags");
   // FunctionType flags, must be in sync with _FunctionTypeHandle constants in
   // pkg/vm/lib/bytecode/object_table.dart.
   const int kFlagHasOptionalPositionalParams = kFlagBit0;
@@ -971,6 +1203,10 @@
   const int kFlagHasNamedArgs = kFlagBit0;
   const int kFlagHasTypeArgs = kFlagBit1;
 
+  // Script flags, must be in sync with _ScriptHandle constants in
+  // pkg/vm/lib/bytecode/object_table.dart.
+  const int kFlagHasSourceFile = kFlagBit0;
+
   const intptr_t kind = (header >> kKindShift) & kKindMask;
   const intptr_t flags = header & kFlagsMask;
 
@@ -979,7 +1215,14 @@
       UNREACHABLE();
       break;
     case kLibrary: {
-      const String& uri = String::Handle(Z, ReadString());
+      String& uri = String::Handle(Z);
+      static_assert(KernelBytecode::kMinSupportedBytecodeFormatVersion < 10,
+                    "Cleanup condition");
+      if (bytecode_component_->GetVersion() < 10) {
+        uri = ReadString();
+      } else {
+        uri ^= ReadObject();
+      }
       RawLibrary* library = Library::LookupLibrary(thread_, uri);
       if (library == Library::null()) {
         FATAL1("Unable to find library %s", uri.ToCString());
@@ -990,9 +1233,15 @@
       const Library& library = Library::CheckedHandle(Z, ReadObject());
       const String& class_name = String::CheckedHandle(Z, ReadObject());
       if (class_name.raw() == Symbols::Empty().raw()) {
-        return library.toplevel_class();
+        NoSafepointScope no_safepoint_scope(thread_);
+        RawClass* cls = library.toplevel_class();
+        if (cls == Class::null()) {
+          FATAL1("Unable to find toplevel class %s", library.ToCString());
+        }
+        return cls;
       }
       RawClass* cls = library.LookupClassAllowPrivate(class_name);
+      NoSafepointScope no_safepoint_scope(thread_);
       if (cls == Class::null()) {
         FATAL2("Unable to find class %s in %s", class_name.ToCString(),
                library.ToCString());
@@ -1004,6 +1253,7 @@
       String& name = String::CheckedHandle(Z, ReadObject());
       if ((flags & kFlagIsField) != 0) {
         RawField* field = cls.LookupFieldAllowPrivate(name);
+        NoSafepointScope no_safepoint_scope(thread_);
         if (field == Field::null()) {
           FATAL2("Unable to find field %s in %s", name.ToCString(),
                  cls.ToCString());
@@ -1019,6 +1269,10 @@
           return scoped_function_.raw();
         }
         RawFunction* function = cls.LookupFunctionAllowPrivate(name);
+        {
+          // To verify that it's OK to hold raw function pointer at this point.
+          NoSafepointScope no_safepoint_scope(thread_);
+        }
         if (function == Function::null()) {
           // When requesting a getter, also return method extractors.
           if (Field::IsGetterName(name)) {
@@ -1044,6 +1298,8 @@
       return closures_->At(closure_index);
     }
     case kSimpleType: {
+      static_assert(KernelBytecode::kMinSupportedBytecodeFormatVersion < 10,
+                    "Cleanup");
       const Class& cls = Class::CheckedHandle(Z, ReadObject());
       if ((flags & kFlagIsDynamic) != 0) {
         ASSERT(cls.IsNull());
@@ -1056,6 +1312,8 @@
       return cls.DeclarationType();
     }
     case kTypeParameter: {
+      static_assert(KernelBytecode::kMinSupportedBytecodeFormatVersion < 10,
+                    "Cleanup");
       Object& parent = Object::Handle(Z, ReadObject());
       const intptr_t index_in_parent = reader_.ReadUInt();
       TypeArguments& type_parameters = TypeArguments::Handle(Z);
@@ -1083,14 +1341,18 @@
       return ClassFinalizer::FinalizeType(*active_class_->klass, type);
     }
     case kGenericType: {
+      static_assert(KernelBytecode::kMinSupportedBytecodeFormatVersion < 10,
+                    "Cleanup");
       const Class& cls = Class::CheckedHandle(Z, ReadObject());
       const TypeArguments& type_arguments =
-          TypeArguments::Handle(Z, ReadTypeArguments(Class::Handle(Z)));
+          TypeArguments::Handle(Z, ReadTypeArguments());
       const Type& type = Type::Handle(
           Z, Type::New(cls, type_arguments, TokenPosition::kNoSource));
       return ClassFinalizer::FinalizeType(*active_class_->klass, type);
     }
     case kFunctionType: {
+      static_assert(KernelBytecode::kMinSupportedBytecodeFormatVersion < 10,
+                    "Cleanup");
       Function& signature_function = Function::ZoneHandle(
           Z, Function::NewSignatureFunction(*active_class_->klass,
                                             active_class_->enclosing != NULL
@@ -1115,9 +1377,11 @@
       }
     }
     case kTypeArguments: {
-      return ReadTypeArguments(Class::Handle(Z));
+      return ReadTypeArguments();
     }
     case kFinalizedGenericType: {
+      static_assert(KernelBytecode::kMinSupportedBytecodeFormatVersion < 10,
+                    "Cleanup");
       const Class& cls = Class::CheckedHandle(Z, ReadObject());
       const TypeArguments& type_arguments =
           TypeArguments::CheckedHandle(Z, ReadObject());
@@ -1147,6 +1411,24 @@
         return ArgumentsDescriptor::New(num_type_args, num_arguments, array);
       }
     }
+    case kScript: {
+      const String& uri = String::CheckedHandle(Z, ReadObject());
+      Script& script = Script::Handle(Z);
+      if ((flags & kFlagHasSourceFile) != 0) {
+        // TODO(alexmarkov): read source and line starts only when needed.
+        script =
+            ReadSourceFile(uri, bytecode_component_->GetSourceFilesOffset() +
+                                    reader_.ReadUInt());
+      } else {
+        script = Script::New(uri, Object::null_string(), RawScript::kKernelTag);
+      }
+      script.set_kernel_program_info(H.GetKernelProgramInfo());
+      return script.raw();
+    }
+    case kType: {
+      const intptr_t tag = flags / kFlagBit0;
+      return ReadType(tag);
+    }
     default:
       UNREACHABLE();
   }
@@ -1264,6 +1546,140 @@
   return Object::null();
 }
 
+RawObject* BytecodeReaderHelper::ReadType(intptr_t tag) {
+  // Must be in sync with enum TypeTag in
+  // pkg/vm/lib/bytecode/object_table.dart.
+  enum TypeTag {
+    kInvalid,
+    kDynamic,
+    kVoid,
+    kSimpleType,
+    kTypeParameter,
+    kGenericType,
+    kRecursiveGenericType,
+    kRecursiveTypeRef,
+    kFunctionType,
+  };
+
+  // FunctionType flags, must be in sync with _FunctionTypeHandle constants in
+  // pkg/vm/lib/bytecode/object_table.dart.
+  const int kFlagHasOptionalPositionalParams = 1 << 0;
+  const int kFlagHasOptionalNamedParams = 1 << 1;
+  const int kFlagHasTypeParams = 1 << 2;
+
+  switch (tag) {
+    case kInvalid:
+      UNREACHABLE();
+      break;
+    case kDynamic:
+      return AbstractType::dynamic_type().raw();
+    case kVoid:
+      return AbstractType::void_type().raw();
+    case kSimpleType: {
+      const Class& cls = Class::CheckedHandle(Z, ReadObject());
+      if (!cls.is_declaration_loaded()) {
+        ASSERT(cls.is_declared_in_bytecode());
+        BytecodeReader::LoadClassDeclaration(cls);
+      }
+      return cls.DeclarationType();
+    }
+    case kTypeParameter: {
+      Object& parent = Object::Handle(Z, ReadObject());
+      const intptr_t index_in_parent = reader_.ReadUInt();
+      TypeArguments& type_parameters = TypeArguments::Handle(Z);
+      if (parent.IsClass()) {
+        type_parameters = Class::Cast(parent).type_parameters();
+      } else if (parent.IsFunction()) {
+        if (Function::Cast(parent).IsFactory()) {
+          // For factory constructors VM uses type parameters of a class
+          // instead of constructor's type parameters.
+          parent = Function::Cast(parent).Owner();
+          type_parameters = Class::Cast(parent).type_parameters();
+        } else {
+          type_parameters = Function::Cast(parent).type_parameters();
+        }
+      } else if (parent.IsNull()) {
+        ASSERT(function_type_type_parameters_ != nullptr);
+        type_parameters = function_type_type_parameters_->raw();
+      } else {
+        UNREACHABLE();
+      }
+      AbstractType& type =
+          AbstractType::Handle(Z, type_parameters.TypeAt(index_in_parent));
+      // TODO(alexmarkov): skip type finalization
+      return ClassFinalizer::FinalizeType(*active_class_->klass, type);
+    }
+    case kGenericType: {
+      const Class& cls = Class::CheckedHandle(Z, ReadObject());
+      if (!cls.is_declaration_loaded()) {
+        ASSERT(cls.is_declared_in_bytecode());
+        BytecodeReader::LoadClassDeclaration(cls);
+      }
+      const TypeArguments& type_arguments =
+          TypeArguments::CheckedHandle(Z, ReadObject());
+      const Type& type = Type::Handle(
+          Z, Type::New(cls, type_arguments, TokenPosition::kNoSource));
+      type.SetIsFinalized();
+      return type.Canonicalize();
+    }
+    case kRecursiveGenericType: {
+      const intptr_t id = reader_.ReadUInt();
+      const Class& cls = Class::CheckedHandle(Z, ReadObject());
+      if (!cls.is_declaration_loaded()) {
+        ASSERT(cls.is_declared_in_bytecode());
+        BytecodeReader::LoadClassDeclaration(cls);
+      }
+      const auto saved_pending_recursive_types = pending_recursive_types_;
+      if (id == 0) {
+        pending_recursive_types_ = &GrowableObjectArray::Handle(
+            Z, GrowableObjectArray::New(Heap::kOld));
+      }
+      ASSERT(id == pending_recursive_types_->Length());
+      const auto& type_ref =
+          TypeRef::Handle(Z, TypeRef::New(AbstractType::null_abstract_type()));
+      pending_recursive_types_->Add(type_ref);
+
+      const TypeArguments& type_arguments =
+          TypeArguments::CheckedHandle(Z, ReadObject());
+
+      ASSERT(id == pending_recursive_types_->Length() - 1);
+      ASSERT(pending_recursive_types_->At(id) == type_ref.raw());
+      pending_recursive_types_->SetLength(id);
+      pending_recursive_types_ = saved_pending_recursive_types;
+
+      Type& type = Type::Handle(
+          Z, Type::New(cls, type_arguments, TokenPosition::kNoSource));
+      type_ref.set_type(type);
+      type.SetIsFinalized();
+      return type.Canonicalize();
+    }
+    case kRecursiveTypeRef: {
+      const intptr_t id = reader_.ReadUInt();
+      ASSERT(pending_recursive_types_ != nullptr);
+      ASSERT(pending_recursive_types_->Length() >= id);
+      return pending_recursive_types_->At(id);
+    }
+    case kFunctionType: {
+      const intptr_t flags = reader_.ReadUInt();
+      Function& signature_function = Function::ZoneHandle(
+          Z, Function::NewSignatureFunction(*active_class_->klass,
+                                            active_class_->enclosing != NULL
+                                                ? *active_class_->enclosing
+                                                : Function::null_function(),
+                                            TokenPosition::kNoSource));
+      // TODO(alexmarkov): skip type finalization
+      return ReadFunctionSignature(
+          signature_function, (flags & kFlagHasOptionalPositionalParams) != 0,
+          (flags & kFlagHasOptionalNamedParams) != 0,
+          (flags & kFlagHasTypeParams) != 0,
+          /* has_positional_param_names = */ false);
+    }
+    default:
+      UNREACHABLE();
+  }
+  return Object::null();
+}
+
 RawString* BytecodeReaderHelper::ReadString(bool is_canonical) {
   const int kFlagTwoByteString = 1;
   const int kHeaderFields = 2;
@@ -1314,8 +1730,42 @@
   }
 }
 
-RawTypeArguments* BytecodeReaderHelper::ReadTypeArguments(
-    const Class& instantiator) {
+RawScript* BytecodeReaderHelper::ReadSourceFile(const String& uri,
+                                                intptr_t offset) {
+  // SourceFile flags, must be in sync with SourceFile constants in
+  // pkg/vm/lib/bytecode/declarations.dart.
+  const int kHasLineStartsFlag = 1 << 0;
+  const int kHasSourceFlag = 1 << 1;
+
+  AlternativeReadingScope alt(&reader_, offset);
+
+  const intptr_t flags = reader_.ReadUInt();
+  const String& import_uri = String::CheckedHandle(Z, ReadObject());
+
+  TypedData& line_starts = TypedData::Handle(Z);
+  if ((flags & kHasLineStartsFlag) != 0) {
+    // TODO(alexmarkov): read line starts only when needed.
+    const intptr_t line_starts_offset =
+        bytecode_component_->GetLineStartsOffset() + reader_.ReadUInt();
+
+    AlternativeReadingScope alt(&reader_, line_starts_offset);
+
+    const intptr_t num_line_starts = reader_.ReadUInt();
+    line_starts = reader_.ReadLineStartsData(num_line_starts);
+  }
+
+  String& source = String::Handle(Z);
+  if ((flags & kHasSourceFlag) != 0) {
+    source = ReadString(/* is_canonical = */ false);
+  }
+
+  const Script& script = Script::Handle(
+      Z, Script::New(import_uri, uri, source, RawScript::kKernelTag));
+  script.set_line_starts(line_starts);
+  return script.raw();
+}
+
+RawTypeArguments* BytecodeReaderHelper::ReadTypeArguments() {
   const intptr_t length = reader_.ReadUInt();
   TypeArguments& type_arguments =
       TypeArguments::ZoneHandle(Z, TypeArguments::New(length));
@@ -1324,36 +1774,14 @@
     type ^= ReadObject();
     type_arguments.SetTypeAt(i, type);
   }
-
-  type_arguments = type_arguments.Canonicalize();
-
-  if (instantiator.IsNull()) {
-    return type_arguments.raw();
-  }
-
-  if (type_arguments.IsNull() && instantiator.NumTypeArguments() == length) {
-    return type_arguments.raw();
-  }
-
-  // We make a temporary [Type] object and use `ClassFinalizer::FinalizeType` to
-  // finalize the argument types.
-  // (This can for example make the [type_arguments] vector larger)
-  type = Type::New(instantiator, type_arguments, TokenPosition::kNoSource);
-  type = ClassFinalizer::FinalizeType(*active_class_->klass, type);
-  return type.arguments();
+  return type_arguments.Canonicalize();
 }
 
-void BytecodeReaderHelper::ReadMembers(const Class& cls,
-                                       intptr_t members_offset,
-                                       bool discard_fields) {
+void BytecodeReaderHelper::ReadMembers(const Class& cls, bool discard_fields) {
   ASSERT(Thread::Current()->IsMutatorThread());
   ASSERT(cls.is_type_finalized());
   ASSERT(!cls.is_loaded());
 
-  const intptr_t offset =
-      bytecode_component_->GetMembersOffset() + members_offset;
-  AlternativeReadingScope alt_md(&reader_, offset);
-
   const intptr_t num_functions = reader_.ReadUInt();
   functions_ = &Array::Handle(Z, Array::New(num_functions, Heap::kOld));
   function_index_ = 0;
@@ -1772,6 +2200,263 @@
   functions_ = nullptr;
 }
 
+void BytecodeReaderHelper::ReadClassDeclaration(const Class& cls) {
+  // Class flags, must be in sync with ClassDeclaration constants in
+  // pkg/vm/lib/bytecode/declarations.dart.
+  const int kIsAbstractFlag = 1 << 0;
+  const int kIsEnumFlag = 1 << 1;
+  const int kHasTypeParamsFlag = 1 << 2;
+  const int kHasTypeArgumentsFlag = 1 << 3;
+  const int kIsTransformedMixinApplicationFlag = 1 << 4;
+  const int kHasSourcePositionsFlag = 1 << 5;
+  const int kHasAnnotationsFlag = 1 << 6;
+  const int kHasPragmaFlag = 1 << 7;
+
+  // Class is allocated when reading library declaration in
+  // BytecodeReaderHelper::ReadLibraryDeclaration.
+  // Its cid is set in Class::New / Isolate::RegisterClass /
+  // ClassTable::Register, unless it was loaded for expression evaluation.
+  ASSERT(cls.is_declared_in_bytecode());
+  ASSERT(!cls.is_declaration_loaded());
+
+  const intptr_t flags = reader_.ReadUInt();
+  const bool has_pragma = (flags & kHasPragmaFlag) != 0;
+
+  // Set early to enable access to type_parameters().
+  cls.set_is_declaration_loaded();
+
+  const auto& script = Script::CheckedHandle(Z, ReadObject());
+  cls.set_script(script);
+
+  TokenPosition position = TokenPosition::kNoSource;
+  if ((flags & kHasSourcePositionsFlag) != 0) {
+    position = reader_.ReadPosition();
+    reader_.ReadPosition();  // end_position
+    cls.set_token_pos(position);
+  }
+
+  cls.set_has_pragma(has_pragma);
+
+  if ((flags & kIsAbstractFlag) != 0) {
+    cls.set_is_abstract();
+  }
+  if ((flags & kIsEnumFlag) != 0) {
+    cls.set_is_enum_class();
+  }
+  if ((flags & kIsTransformedMixinApplicationFlag) != 0) {
+    cls.set_is_transformed_mixin_application();
+  }
+
+  intptr_t num_type_arguments = 0;
+  if ((flags & kHasTypeArgumentsFlag) != 0) {
+    num_type_arguments = reader_.ReadUInt();
+  }
+  cls.set_num_type_arguments(num_type_arguments);
+
+  if ((flags & kHasTypeParamsFlag) != 0) {
+    ReadTypeParametersDeclaration(cls, Function::null_function());
+  }
+
+  auto& type = AbstractType::CheckedHandle(Z, ReadObject());
+  cls.set_super_type(type);
+
+  const intptr_t num_interfaces = reader_.ReadUInt();
+  if (num_interfaces > 0) {
+    const auto& interfaces =
+        Array::Handle(Z, Array::New(num_interfaces, Heap::kOld));
+    for (intptr_t i = 0; i < num_interfaces; ++i) {
+      type ^= ReadObject();
+      interfaces.SetAt(i, type);
+    }
+    cls.set_interfaces(interfaces);
+  }
+
+  if ((flags & kHasAnnotationsFlag) != 0) {
+    intptr_t annotations_offset =
+        reader_.ReadUInt() + bytecode_component_->GetAnnotationsOffset();
+    ASSERT(annotations_offset > 0);
+
+    if (FLAG_enable_mirrors || has_pragma) {
+      const auto& library = Library::Handle(Z, cls.library());
+      if (cls.IsTopLevel()) {
+        ASSERT(!has_pragma);
+        library.AddLibraryMetadata(cls, TokenPosition::kNoSource, 0,
+                                   annotations_offset);
+      } else {
+        const auto& top_level_class =
+            Class::Handle(Z, library.toplevel_class());
+
+        library.AddClassMetadata(cls, top_level_class, TokenPosition::kNoSource,
+                                 0, annotations_offset);
+        if (has_pragma) {
+          // TODO(alexmarkov): read annotations right away using
+          //  annotations_offset.
+          NoOOBMessageScope no_msg_scope(thread_);
+          NoReloadScope no_reload_scope(thread_->isolate(), thread_);
+          library.GetMetadata(cls);
+        }
+      }
+    }
+  }
+
+  const intptr_t members_offset = reader_.ReadUInt();
+  cls.set_bytecode_offset(members_offset +
+                          bytecode_component_->GetMembersOffset());
+
+  // All types are finalized if loading from bytecode.
+  cls.set_is_type_finalized();
+
+  // TODO(alexmarkov): move this to class finalization.
+  ClassFinalizer::RegisterClassInHierarchy(Z, cls);
+}
+
+void BytecodeReaderHelper::ReadLibraryDeclaration(const Library& library,
+                                                  bool lookup_classes) {
+  // Library flags, must be in sync with LibraryDeclaration constants in
+  // pkg/vm/lib/bytecode/declarations.dart.
+  const int kUsesDartMirrorsFlag = 1 << 0;
+  const int kUsesDartFfiFlag = 1 << 1;
+
+  ASSERT(library.is_declared_in_bytecode());
+  ASSERT(!library.Loaded());
+  ASSERT(library.toplevel_class() == Object::null());
+
+  // TODO(alexmarkov): fill in library.owned_scripts.
+  //
+  // TODO(alexmarkov): figure out if we need to finish class loading immediately
+  //  in case of 'loading_native_wrappers_library_ ' or '!register_class'.
+  //
+  // TODO(alexmarkov): support native extension libraries.
+  //
+
+  const intptr_t flags = reader_.ReadUInt();
+  if (((flags & kUsesDartMirrorsFlag) != 0) && !FLAG_enable_mirrors) {
+    H.ReportError("import of dart:mirrors with --enable-mirrors=false");
+  }
+  if (((flags & kUsesDartFfiFlag) != 0) && !Api::IsFfiEnabled()) {
+    H.ReportError("import of dart:ffi with --enable-ffi=false");
+  }
+
+  auto& name = String::CheckedHandle(Z, ReadObject());
+  library.SetName(name);
+
+  const auto& script = Script::CheckedHandle(Z, ReadObject());
+
+  // The bootstrapper will take care of creating the native wrapper classes,
+  // but we will add the synthetic constructors to them here.
+  if (name.raw() ==
+      Symbols::Symbol(Symbols::kDartNativeWrappersLibNameId).raw()) {
+    ASSERT(library.LoadInProgress());
+    loading_native_wrappers_library_ = true;
+  } else {
+    loading_native_wrappers_library_ = false;
+    library.SetLoadInProgress();
+  }
+
+  const bool register_class = !IsExpressionEvaluationLibrary(library);
+
+  const intptr_t num_classes = reader_.ReadUInt();
+  ASSERT(num_classes > 0);
+  auto& cls = Class::Handle(Z);
+
+  for (intptr_t i = 0; i < num_classes; ++i) {
+    name ^= ReadObject();
+    const intptr_t class_offset =
+        bytecode_component_->GetClassesOffset() + reader_.ReadUInt();
+
+    if (i == 0) {
+      ASSERT(name.raw() == Symbols::Empty().raw());
+      cls = Class::New(library, Symbols::TopLevel(), script,
+                       TokenPosition::kNoSource, register_class);
+      if (register_class) {
+        library.set_toplevel_class(cls);
+      }
+    } else {
+      if (lookup_classes) {
+        cls = library.LookupClassAllowPrivate(name);
+      }
+      if (lookup_classes && !cls.IsNull()) {
+        ASSERT(!cls.is_declaration_loaded());
+        cls.set_script(script);
+      } else {
+        cls = Class::New(library, name, script, TokenPosition::kNoSource,
+                         register_class);
+        if (register_class) {
+          library.AddClass(cls);
+        }
+      }
+    }
+
+    cls.set_is_declared_in_bytecode(true);
+    cls.set_bytecode_offset(class_offset);
+  }
+
+  ASSERT(!library.Loaded());
+  library.SetLoaded();
+
+  loading_native_wrappers_library_ = false;
+}
+
+void BytecodeReaderHelper::ReadLibraryDeclarations(intptr_t num_libraries) {
+  auto& library = Library::Handle(Z);
+  auto& uri = String::Handle(Z);
+
+  for (intptr_t i = 0; i < num_libraries; ++i) {
+    uri ^= ReadObject();
+    const intptr_t library_offset =
+        bytecode_component_->GetLibrariesOffset() + reader_.ReadUInt();
+
+    if (!FLAG_precompiled_mode && !I->should_load_vmservice()) {
+      if (uri.raw() == Symbols::DartVMServiceIO().raw()) {
+        continue;
+      }
+    }
+
+    bool lookup_classes = true;
+    library = Library::LookupLibrary(thread_, uri);
+    if (library.IsNull()) {
+      lookup_classes = false;
+      library = Library::New(uri);
+
+      if (uri.raw() == Symbols::EvalSourceUri().raw()) {
+        ASSERT(expression_evaluation_library_ == nullptr);
+        expression_evaluation_library_ = &Library::Handle(Z, library.raw());
+      } else {
+        library.Register(thread_);
+      }
+    }
+
+    if (library.Loaded()) {
+      continue;
+    }
+
+    library.set_is_declared_in_bytecode(true);
+    library.set_bytecode_offset(library_offset);
+
+    AlternativeReadingScope alt(&reader_, library_offset);
+    ReadLibraryDeclaration(library, lookup_classes);
+  }
+}
+
+void BytecodeReaderHelper::FindAndReadSpecificLibrary(const Library& library,
+                                                      intptr_t num_libraries) {
+  auto& uri = String::Handle(Z);
+  for (intptr_t i = 0; i < num_libraries; ++i) {
+    uri ^= ReadObject();
+    const intptr_t library_offset =
+        bytecode_component_->GetLibrariesOffset() + reader_.ReadUInt();
+
+    if (uri.raw() == library.url()) {
+      library.set_is_declared_in_bytecode(true);
+      library.set_bytecode_offset(library_offset);
+
+      AlternativeReadingScope alt(&reader_, library_offset);
+      ReadLibraryDeclaration(library, /* lookup_classes = */ true);
+      return;
+    }
+  }
+}
+
 void BytecodeReaderHelper::ReadParameterCovariance(
     const Function& function,
     BitVector* is_covariant,
@@ -2008,6 +2693,22 @@
   return Smi::Value(Smi::RawCast(data_.At(kMainOffset)));
 }
 
+intptr_t BytecodeComponentData::GetNumLibraries() const {
+  return Smi::Value(Smi::RawCast(data_.At(kNumLibraries)));
+}
+
+intptr_t BytecodeComponentData::GetLibraryIndexOffset() const {
+  return Smi::Value(Smi::RawCast(data_.At(kLibraryIndexOffset)));
+}
+
+intptr_t BytecodeComponentData::GetLibrariesOffset() const {
+  return Smi::Value(Smi::RawCast(data_.At(kLibrariesOffset)));
+}
+
+intptr_t BytecodeComponentData::GetClassesOffset() const {
+  return Smi::Value(Smi::RawCast(data_.At(kClassesOffset)));
+}
+
 intptr_t BytecodeComponentData::GetMembersOffset() const {
   return Smi::Value(Smi::RawCast(data_.At(kMembersOffset)));
 }
@@ -2020,6 +2721,14 @@
   return Smi::Value(Smi::RawCast(data_.At(kSourcePositionsOffset)));
 }
 
+intptr_t BytecodeComponentData::GetSourceFilesOffset() const {
+  return Smi::Value(Smi::RawCast(data_.At(kSourceFilesOffset)));
+}
+
+intptr_t BytecodeComponentData::GetLineStartsOffset() const {
+  return Smi::Value(Smi::RawCast(data_.At(kLineStartsOffset)));
+}
+
 intptr_t BytecodeComponentData::GetLocalVariablesOffset() const {
   return Smi::Value(Smi::RawCast(data_.At(kLocalVariablesOffset)));
 }
@@ -2043,9 +2752,15 @@
                                      intptr_t strings_contents_offset,
                                      intptr_t objects_contents_offset,
                                      intptr_t main_offset,
+                                     intptr_t num_libraries,
+                                     intptr_t library_index_offset,
+                                     intptr_t libraries_offset,
+                                     intptr_t classes_offset,
                                      intptr_t members_offset,
                                      intptr_t codes_offset,
                                      intptr_t source_positions_offset,
+                                     intptr_t source_files_offset,
+                                     intptr_t line_starts_offset,
                                      intptr_t local_variables_offset,
                                      intptr_t annotations_offset,
                                      Heap::Space space) {
@@ -2068,6 +2783,18 @@
   smi_handle = Smi::New(main_offset);
   data.SetAt(kMainOffset, smi_handle);
 
+  smi_handle = Smi::New(num_libraries);
+  data.SetAt(kNumLibraries, smi_handle);
+
+  smi_handle = Smi::New(library_index_offset);
+  data.SetAt(kLibraryIndexOffset, smi_handle);
+
+  smi_handle = Smi::New(libraries_offset);
+  data.SetAt(kLibrariesOffset, smi_handle);
+
+  smi_handle = Smi::New(classes_offset);
+  data.SetAt(kClassesOffset, smi_handle);
+
   smi_handle = Smi::New(members_offset);
   data.SetAt(kMembersOffset, smi_handle);
 
@@ -2077,6 +2804,12 @@
   smi_handle = Smi::New(source_positions_offset);
   data.SetAt(kSourcePositionsOffset, smi_handle);
 
+  smi_handle = Smi::New(source_files_offset);
+  data.SetAt(kSourceFilesOffset, smi_handle);
+
+  smi_handle = Smi::New(line_starts_offset);
+  data.SetAt(kLineStartsOffset, smi_handle);
+
   smi_handle = Smi::New(local_variables_offset);
   data.SetAt(kLocalVariablesOffset, smi_handle);
 
@@ -2096,7 +2829,7 @@
   VMTagScope tagScope(thread, VMTag::kLoadBytecodeTagId);
 
 #if defined(SUPPORT_TIMELINE)
-  TimelineDurationScope tds(Thread::Current(), Timeline::GetCompilerStream(),
+  TimelineDurationScope tds(thread, Timeline::GetCompilerStream(),
                             "BytecodeReader::ReadFunctionBytecode");
   // This increases bytecode reading time by ~7%, so only keep it around for
   // debugging.
@@ -2130,7 +2863,7 @@
         bytecode = Object::method_extractor_bytecode().raw();
         break;
       case RawFunction::kInvokeFieldDispatcher:
-        if (Class::Handle(function.Owner()).id() == kClosureCid) {
+        if (Class::Handle(zone, function.Owner()).id() == kClosureCid) {
           bytecode = Object::invoke_closure_bytecode().raw();
         } else {
           bytecode = Object::invoke_field_bytecode().raw();
@@ -2139,6 +2872,36 @@
       case RawFunction::kNoSuchMethodDispatcher:
         bytecode = Object::nsm_dispatcher_bytecode().raw();
         break;
+      case RawFunction::kDynamicInvocationForwarder: {
+        const Function& target =
+            Function::Handle(zone, function.ForwardingTarget());
+        if (!target.HasBytecode()) {
+          // The forwarder will use the target's bytecode to handle optional
+          // parameters.
+          const Error& error =
+              Error::Handle(zone, ReadFunctionBytecode(thread, target));
+          if (!error.IsNull()) {
+            return error.raw();
+          }
+        }
+        {
+          const Script& script = Script::Handle(zone, target.script());
+          TranslationHelper translation_helper(thread);
+          translation_helper.InitFromScript(script);
+
+          ActiveClass active_class;
+          BytecodeComponentData bytecode_component(
+              &Array::Handle(zone, translation_helper.GetBytecodeComponent()));
+          ASSERT(!bytecode_component.IsNull());
+          BytecodeReaderHelper bytecode_reader(
+              &translation_helper, &active_class, &bytecode_component);
+
+          const Array& checks = Array::Handle(
+              zone, bytecode_reader.CreateForwarderChecks(target));
+          function.SetForwardingChecks(checks);
+        }
+        bytecode = Object::dynamic_invocation_forwarder_bytecode().raw();
+      } break;
       default:
         break;
     }
@@ -2168,7 +2931,7 @@
                                                      zone);
 
         BytecodeComponentData bytecode_component(
-            Array::Handle(zone, translation_helper.GetBytecodeComponent()));
+            &Array::Handle(zone, translation_helper.GetBytecodeComponent()));
         ASSERT(!bytecode_component.IsNull());
         BytecodeReaderHelper bytecode_reader(&translation_helper, &active_class,
                                              &bytecode_component);
@@ -2176,13 +2939,6 @@
         bytecode_reader.ReadCode(function, code_offset);
       }
     }
-
-#if !defined(PRODUCT)
-    if (function.HasBytecode()) {
-      thread->isolate()->debugger()->NotifyBytecodeLoaded(function);
-    }
-#endif
-
     return Error::null();
   } else {
     return thread->StealStickyError();
@@ -2203,7 +2959,7 @@
   ActiveClass active_class;
 
   BytecodeComponentData bytecode_component(
-      Array::Handle(zone, translation_helper.GetBytecodeComponent()));
+      &Array::Handle(zone, translation_helper.GetBytecodeComponent()));
   ASSERT(!bytecode_component.IsNull());
   BytecodeReaderHelper bytecode_reader(&translation_helper, &active_class,
                                        &bytecode_component);
@@ -2214,6 +2970,65 @@
   return bytecode_reader.ReadObject();
 }
 
+void BytecodeReader::LoadClassDeclaration(const Class& cls) {
+  TIMELINE_DURATION(Thread::Current(), Compiler,
+                    "BytecodeReader::LoadClassDeclaration");
+
+  ASSERT(cls.is_declared_in_bytecode());
+  ASSERT(!cls.is_declaration_loaded());
+
+  Thread* thread = Thread::Current();
+  Zone* zone = thread->zone();
+  ASSERT(thread->IsMutatorThread());
+
+  const Script& script = Script::Handle(zone, cls.script());
+  TranslationHelper translation_helper(thread);
+  translation_helper.InitFromScript(script);
+
+  ActiveClass active_class;
+  ActiveClassScope active_class_scope(&active_class, &cls);
+
+  BytecodeComponentData bytecode_component(
+      &Array::Handle(zone, translation_helper.GetBytecodeComponent()));
+  ASSERT(!bytecode_component.IsNull());
+  BytecodeReaderHelper bytecode_reader(&translation_helper, &active_class,
+                                       &bytecode_component);
+
+  AlternativeReadingScope alt(&bytecode_reader.reader(), cls.bytecode_offset());
+
+  bytecode_reader.ReadClassDeclaration(cls);
+}
+
+void BytecodeReader::FinishClassLoading(const Class& cls) {
+  ASSERT(cls.is_declared_in_bytecode());
+
+  Thread* thread = Thread::Current();
+  Zone* zone = thread->zone();
+  ASSERT(thread->IsMutatorThread());
+
+  const Script& script = Script::Handle(zone, cls.script());
+  TranslationHelper translation_helper(thread);
+  translation_helper.InitFromScript(script);
+
+  ActiveClass active_class;
+  ActiveClassScope active_class_scope(&active_class, &cls);
+
+  BytecodeComponentData bytecode_component(
+      &Array::Handle(zone, translation_helper.GetBytecodeComponent()));
+  ASSERT(!bytecode_component.IsNull());
+  BytecodeReaderHelper bytecode_reader(&translation_helper, &active_class,
+                                       &bytecode_component);
+
+  AlternativeReadingScope alt(&bytecode_reader.reader(), cls.bytecode_offset());
+
+  // If this is a dart:internal.ClassID class ignore field declarations
+  // contained in the Kernel file and instead inject our own const
+  // fields.
+  const bool discard_fields = cls.InjectCIDFields();
+
+  bytecode_reader.ReadMembers(cls, discard_fields);
+}
+
 #if !defined(PRODUCT)
 RawLocalVarDescriptors* BytecodeReader::ComputeLocalVarDescriptors(
     Zone* zone,
@@ -2249,6 +3064,7 @@
             function.token_pos() <= var_info.end_pos) ||
            (function.token_pos() <= var_info.begin_pos &&
             var_info.begin_pos <= function.end_token_pos()))) {
+        var_info.scope_id++;  // One level higher in the context chain.
         vars.Add(
             VarDesc{&String::Handle(zone, parent_vars.GetName(i)), var_info});
       }
diff --git a/runtime/vm/compiler/frontend/bytecode_reader.h b/runtime/vm/compiler/frontend/bytecode_reader.h
index 753bff9..1a8440c 100644
--- a/runtime/vm/compiler/frontend/bytecode_reader.h
+++ b/runtime/vm/compiler/frontend/bytecode_reader.h
@@ -25,13 +25,18 @@
                                   ActiveClass* active_class);
 
   void ParseBytecodeFunction(ParsedFunction* parsed_function);
-  void ParseBytecodeImplicitClosureFunction(ParsedFunction* parsed_function);
 
   // Reads members associated with given [node_offset] and fills in [cls].
   // Discards fields if [discard_fields] is true.
   // Returns true if class members are loaded.
   bool ReadMembers(intptr_t node_offset, const Class& cls, bool discard_fields);
 
+  // Read all library declarations.
+  bool ReadLibraries();
+
+  // Read specific library declaration.
+  void ReadLibrary(const Library& library);
+
   RawLibrary* GetMainLibrary();
 
   RawArray* GetBytecodeComponent();
@@ -54,12 +59,17 @@
 
   void ReadCode(const Function& function, intptr_t code_offset);
 
-  void ReadMembers(const Class& cls,
-                   intptr_t members_offset,
-                   bool discard_fields);
+  RawArray* CreateForwarderChecks(const Function& function);
+
+  void ReadMembers(const Class& cls, bool discard_fields);
 
   void ReadFieldDeclarations(const Class& cls, bool discard_fields);
   void ReadFunctionDeclarations(const Class& cls);
+  void ReadClassDeclaration(const Class& cls);
+  void ReadLibraryDeclaration(const Library& library, bool lookup_classes);
+  void ReadLibraryDeclarations(intptr_t num_libraries);
+  void FindAndReadSpecificLibrary(const Library& library,
+                                  intptr_t num_libraries);
 
   void ParseBytecodeFunction(ParsedFunction* parsed_function,
                              const Function& function);
@@ -184,13 +194,20 @@
 
   RawObject* ReadObjectContents(uint32_t header);
   RawObject* ReadConstObject(intptr_t tag);
+  RawObject* ReadType(intptr_t tag);
   RawString* ReadString(bool is_canonical = true);
-  RawTypeArguments* ReadTypeArguments(const Class& instantiator);
+  RawScript* ReadSourceFile(const String& uri, intptr_t offset);
+  RawTypeArguments* ReadTypeArguments();
   RawPatchClass* GetPatchClass(const Class& cls, const Script& script);
   void ParseForwarderFunction(ParsedFunction* parsed_function,
                               const Function& function,
                               const Function& target);
 
+  bool IsExpressionEvaluationLibrary(const Library& library) const {
+    return expression_evaluation_library_ != nullptr &&
+           expression_evaluation_library_->raw() == library.raw();
+  }
+
   Reader reader_;
   TranslationHelper& translation_helper_;
   ActiveClass* const active_class_;
@@ -199,12 +216,15 @@
   BytecodeComponentData* const bytecode_component_;
   Array* closures_ = nullptr;
   const TypeArguments* function_type_type_parameters_ = nullptr;
+  GrowableObjectArray* pending_recursive_types_ = nullptr;
   PatchClass* patch_class_ = nullptr;
   Array* functions_ = nullptr;
   intptr_t function_index_ = 0;
   Function& scoped_function_;
   String& scoped_function_name_;
   Class& scoped_function_class_;
+  Library* expression_evaluation_library_ = nullptr;
+  bool loading_native_wrappers_library_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(BytecodeReaderHelper);
 };
@@ -217,24 +237,38 @@
     kStringsContentsOffset,
     kObjectsContentsOffset,
     kMainOffset,
+    kNumLibraries,
+    kLibraryIndexOffset,
+    kLibrariesOffset,
+    kClassesOffset,
     kMembersOffset,
     kCodesOffset,
     kSourcePositionsOffset,
+    kSourceFilesOffset,
+    kLineStartsOffset,
     kLocalVariablesOffset,
     kAnnotationsOffset,
     kNumFields
   };
 
-  explicit BytecodeComponentData(const Array& data) : data_(data) {}
+  explicit BytecodeComponentData(Array* data) : data_(*data) {}
+
+  void Init(const Array& data) { data_ = data.raw(); }
 
   intptr_t GetVersion() const;
   intptr_t GetStringsHeaderOffset() const;
   intptr_t GetStringsContentsOffset() const;
   intptr_t GetObjectsContentsOffset() const;
   intptr_t GetMainOffset() const;
+  intptr_t GetNumLibraries() const;
+  intptr_t GetLibraryIndexOffset() const;
+  intptr_t GetLibrariesOffset() const;
+  intptr_t GetClassesOffset() const;
   intptr_t GetMembersOffset() const;
   intptr_t GetCodesOffset() const;
   intptr_t GetSourcePositionsOffset() const;
+  intptr_t GetSourceFilesOffset() const;
+  intptr_t GetLineStartsOffset() const;
   intptr_t GetLocalVariablesOffset() const;
   intptr_t GetAnnotationsOffset() const;
   void SetObject(intptr_t index, const Object& obj) const;
@@ -249,15 +283,21 @@
                        intptr_t strings_contents_offset,
                        intptr_t objects_contents_offset,
                        intptr_t main_offset,
+                       intptr_t num_libraries,
+                       intptr_t library_index_offset,
+                       intptr_t libraries_offset,
+                       intptr_t classes_offset,
                        intptr_t members_offset,
                        intptr_t codes_offset,
                        intptr_t source_positions_offset,
+                       intptr_t source_files_offset,
+                       intptr_t line_starts_offset,
                        intptr_t local_variables_offset,
                        intptr_t annotations_offset,
                        Heap::Space space);
 
  private:
-  const Array& data_;
+  Array& data_;
 };
 
 class BytecodeReader : public AllStatic {
@@ -270,6 +310,15 @@
   // Read annotation for the given annotation field.
   static RawObject* ReadAnnotation(const Field& annotation_field);
 
+  // Read declaration of the given library.
+  static void LoadLibraryDeclaration(const Library& library);
+
+  // Read declaration of the given class.
+  static void LoadClassDeclaration(const Class& cls);
+
+  // Read members of the given class.
+  static void FinishClassLoading(const Class& cls);
+
 #if !defined(PRODUCT)
   // Compute local variable descriptors for [function] with [bytecode].
   static RawLocalVarDescriptors* ComputeLocalVarDescriptors(
diff --git a/runtime/vm/compiler/frontend/bytecode_scope_builder.cc b/runtime/vm/compiler/frontend/bytecode_scope_builder.cc
index d31e00a..e3d3f50c 100644
--- a/runtime/vm/compiler/frontend/bytecode_scope_builder.cc
+++ b/runtime/vm/compiler/frontend/bytecode_scope_builder.cc
@@ -19,7 +19,7 @@
       scope_(nullptr) {}
 
 void BytecodeScopeBuilder::BuildScopes() {
-  if (parsed_function_->node_sequence() != nullptr) {
+  if (parsed_function_->scope() != nullptr) {
     return;  // Scopes are already built.
   }
 
@@ -60,8 +60,7 @@
   context_var->set_is_forced_stack();
   scope_->AddVariable(context_var);
 
-  parsed_function_->SetNodeSequence(
-      new SequenceNode(TokenPosition::kNoSource, scope_));
+  parsed_function_->set_scope(scope_);
 
   switch (function.kind()) {
     case RawFunction::kImplicitClosureFunction: {
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index 706d2e6..d018655 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -432,7 +432,7 @@
          scopes()->yield_jump_variable->owner()->context_level());
 
   Fragment instructions;
-  LocalScope* scope = parsed_function()->node_sequence()->scope();
+  LocalScope* scope = parsed_function()->scope();
 
   const Function& target = Function::ZoneHandle(
       Z, I->object_store()->async_set_thread_stack_trace());
@@ -578,13 +578,13 @@
 Fragment StreamingFlowGraphBuilder::SetupCapturedParameters(
     const Function& dart_function) {
   Fragment body;
-  const LocalScope* scope = parsed_function()->node_sequence()->scope();
+  const LocalScope* scope = parsed_function()->scope();
   if (scope->num_context_variables() > 0) {
     body += flow_graph_builder_->PushContext(scope);
     LocalVariable* context = MakeTemporary();
 
     // Copy captured parameters from the stack into the context.
-    LocalScope* scope = parsed_function()->node_sequence()->scope();
+    LocalScope* scope = parsed_function()->scope();
     intptr_t parameter_count = dart_function.NumParameters();
     const ParsedFunction& pf = *flow_graph_builder_->parsed_function_;
     const Function& function = pf.function();
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index b8be355..8fa5799 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -724,9 +724,17 @@
          info.potential_natives() == GrowableObjectArray::null());
 #endif
 
+  auto& kernel_data = ExternalTypedData::Handle(Z);
+  intptr_t kernel_data_program_offset = 0;
+  if (!function.is_declared_in_bytecode()) {
+    kernel_data = function.KernelData();
+    kernel_data_program_offset = function.KernelDataProgramOffset();
+  }
+
+  // TODO(alexmarkov): refactor this - StreamingFlowGraphBuilder should not be
+  //  used for bytecode functions.
   StreamingFlowGraphBuilder streaming_flow_graph_builder(
-      this, ExternalTypedData::Handle(Z, function.KernelData()),
-      function.KernelDataProgramOffset());
+      this, kernel_data, kernel_data_program_offset);
   return streaming_flow_graph_builder.BuildGraph();
 }
 
@@ -2234,7 +2242,13 @@
                          function.IsImplicitSetterFunction();
   const bool is_method = !function.IsStaticFunction();
 
-  Field& field = Field::ZoneHandle(Z, function.accessor_field());
+  Field& field = Field::ZoneHandle(Z);
+  if (function.IsDynamicInvocationForwarder()) {
+    Function& target = Function::Handle(function.ForwardingTarget());
+    field = target.accessor_field();
+  } else {
+    field = function.accessor_field();
+  }
 
   graph_entry_ =
       new (Z) GraphEntryInstr(*parsed_function_, Compiler::kNoOSRDeoptId);
@@ -2320,8 +2334,7 @@
     body += CheckStackOverflowInPrologue(function.token_pos());
   }
 
-  ASSERT(parsed_function_->node_sequence()->scope()->num_context_variables() ==
-         0);
+  ASSERT(parsed_function_->scope()->num_context_variables() == 0);
 
   // Should never build a dynamic invocation forwarder for equality
   // operator.
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.cc b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
index b999e21..35c0178 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.cc
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
@@ -499,10 +499,9 @@
   ASSERT(IsLibrary(kernel_library) ||
          IsAdministrative(CanonicalNameParent(kernel_library)));
   {
-    NoSafepointScope no_safepoint_scope(thread_);
-    RawLibrary* raw_lib;
     name_index_handle_ = Smi::New(kernel_library);
-    raw_lib = info_.LookupLibrary(thread_, name_index_handle_);
+    RawLibrary* raw_lib = info_.LookupLibrary(thread_, name_index_handle_);
+    NoSafepointScope no_safepoint_scope(thread_);
     if (raw_lib != Library::null()) {
       return raw_lib;
     }
@@ -521,10 +520,9 @@
 RawClass* TranslationHelper::LookupClassByKernelClass(NameIndex kernel_class) {
   ASSERT(IsClass(kernel_class));
   {
-    NoSafepointScope no_safepoint_scope(thread_);
-    RawClass* raw_class;
     name_index_handle_ = Smi::New(kernel_class);
-    raw_class = info_.LookupClass(thread_, name_index_handle_);
+    RawClass* raw_class = info_.LookupClass(thread_, name_index_handle_);
+    NoSafepointScope no_safepoint_scope(thread_);
     if (raw_class != Class::null()) {
       return raw_class;
     }
@@ -2563,47 +2561,8 @@
   SetOffset(GetOffsetForSourceInfo(index));
   SkipBytes(ReadUInt());                         // skip uri.
   SkipBytes(ReadUInt());                         // skip source.
-  const intptr_t line_start_count = ReadUInt();  // read number of line start
-  // entries.
-  MallocGrowableArray<int32_t> line_starts_array;
-
-  intptr_t max_delta = 0;
-  for (intptr_t i = 0; i < line_start_count; ++i) {
-    int32_t delta = ReadUInt();
-    line_starts_array.Add(delta);
-    if (delta > max_delta) {
-      max_delta = delta;
-    }
-  }
-
-  intptr_t cid;
-  if (max_delta <= kMaxInt8) {
-    cid = kTypedDataInt8ArrayCid;
-  } else if (max_delta <= kMaxInt16) {
-    cid = kTypedDataInt16ArrayCid;
-  } else {
-    cid = kTypedDataInt32ArrayCid;
-  }
-
-  TypedData& line_starts_data =
-      TypedData::Handle(Z, TypedData::New(cid, line_start_count, Heap::kOld));
-  for (intptr_t j = 0; j < line_start_count; ++j) {
-    int32_t line_start = line_starts_array[j];
-    switch (cid) {
-      case kTypedDataInt8ArrayCid:
-        line_starts_data.SetInt8(j, static_cast<int8_t>(line_start));
-        break;
-      case kTypedDataInt16ArrayCid:
-        line_starts_data.SetInt16(j << 1, static_cast<int16_t>(line_start));
-        break;
-      case kTypedDataInt32ArrayCid:
-        line_starts_data.SetInt32(j << 2, line_start);
-        break;
-      default:
-        UNREACHABLE();
-    }
-  }
-  return line_starts_data.raw();
+  const intptr_t line_start_count = ReadUInt();
+  return reader_.ReadLineStartsData(line_start_count);
 }
 
 String& KernelReaderHelper::SourceTableImportUriFor(intptr_t index,
@@ -2789,8 +2748,7 @@
     } else {
       // Note that the type argument vector is not yet extended.
       result_ =
-          Type::New(klass, TypeArguments::Handle(Z, klass.type_parameters()),
-                    klass.token_pos());
+          Type::New(klass, Object::null_type_arguments(), klass.token_pos());
     }
     return;
   }
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.h b/runtime/vm/compiler/frontend/kernel_translation_helper.h
index ad4898c..1dc3f3c4 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.h
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.h
@@ -62,6 +62,7 @@
   RawGrowableObjectArray* EnsurePotentialPragmaFunctions();
 
   void SetKernelProgramInfo(const KernelProgramInfo& info);
+  const KernelProgramInfo& GetKernelProgramInfo() const { return info_; }
 
   intptr_t StringOffset(StringIndex index) const;
   intptr_t StringSize(StringIndex index) const;
diff --git a/runtime/vm/compiler/frontend/scope_builder.cc b/runtime/vm/compiler/frontend/scope_builder.cc
index 10f5dd6..eefed03 100644
--- a/runtime/vm/compiler/frontend/scope_builder.cc
+++ b/runtime/vm/compiler/frontend/scope_builder.cc
@@ -134,8 +134,7 @@
   context_var->set_is_forced_stack();
   scope_->AddVariable(context_var);
 
-  parsed_function_->SetNodeSequence(
-      new SequenceNode(TokenPosition::kNoSource, scope_));
+  parsed_function_->set_scope(scope_);
 
   helper_.SetOffset(function.kernel_offset());
 
diff --git a/runtime/vm/compiler/jit/compiler.cc b/runtime/vm/compiler/jit/compiler.cc
index d42a3cc..66e293f 100644
--- a/runtime/vm/compiler/jit/compiler.cc
+++ b/runtime/vm/compiler/jit/compiler.cc
@@ -330,11 +330,6 @@
   return !Thread::Current()->IsMutatorThread();
 }
 
-RawError* Compiler::Compile(const Library& library, const Script& script) {
-  UNREACHABLE();
-  return Error::null();
-}
-
 class CompileParsedFunctionHelper : public ValueObject {
  public:
   CompileParsedFunctionHelper(ParsedFunction* parsed_function,
@@ -394,30 +389,6 @@
       /*stats=*/nullptr));
   code.set_is_optimized(optimized());
   code.set_owner(function);
-#if !defined(PRODUCT)
-  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.set_await_token_positions(await_to_token_map);
-    }
-  }
-#endif  // !defined(PRODUCT)
 
   if (!function.IsOptimizable()) {
     // A function with huge unoptimized code can become non-optimizable
@@ -939,44 +910,6 @@
   return Object::null();
 }
 
-static RawError* ParseFunctionHelper(CompilationPipeline* pipeline,
-                                     const Function& function,
-                                     bool optimized,
-                                     intptr_t osr_id) {
-  ASSERT(!FLAG_precompiled_mode);
-  ASSERT(!optimized || function.WasCompiled());
-  LongJumpScope jump;
-  if (setjmp(*jump.Set()) == 0) {
-    Thread* const thread = Thread::Current();
-    StackZone stack_zone(thread);
-    Zone* const zone = stack_zone.GetZone();
-    const bool trace_compiler =
-        FLAG_trace_compiler || (FLAG_trace_optimizing_compiler && optimized);
-
-    if (trace_compiler) {
-      const intptr_t token_size =
-          function.end_token_pos().Pos() - function.token_pos().Pos();
-      THR_Print("Parsing %s%sfunction %s: '%s' @ token %s, size %" Pd "\n",
-                (osr_id == Compiler::kNoOSRDeoptId ? "" : "osr "),
-                (optimized ? "optimized " : ""),
-                (Compiler::IsBackgroundCompilation() ? "(background)" : ""),
-                function.ToFullyQualifiedCString(),
-                function.token_pos().ToCString(), token_size);
-    }
-    ParsedFunction* parsed_function = new (zone)
-        ParsedFunction(thread, Function::ZoneHandle(zone, function.raw()));
-    pipeline->ParseFunction(parsed_function);
-    return Error::null();
-  } else {
-    // We got an error during compilation or it is a bailout from background
-    // compilation (e.g., during parsing with EnsureIsFinalized).
-    // Unoptimized compilation or precompilation may encounter compile-time
-    // errors, but regular optimized compilation should not.
-    ASSERT(!optimized);
-    return Thread::Current()->StealStickyError();
-  }
-}
-
 RawObject* Compiler::CompileFunction(Thread* thread, const Function& function) {
 #if defined(DART_PRECOMPILER) && !defined(TARGET_ARCH_DBC) &&                  \
     !defined(TARGET_ARCH_IA32)
@@ -1012,25 +945,6 @@
   return CompileFunctionHelper(pipeline, function, optimized, kNoOSRDeoptId);
 }
 
-RawError* Compiler::ParseFunction(Thread* thread, const Function& function) {
-  VMTagScope tagScope(thread, VMTag::kCompileUnoptimizedTagId);
-  TIMELINE_FUNCTION_COMPILATION_DURATION(thread, "ParseFunction", function);
-
-  Isolate* isolate = thread->isolate();
-  if (!isolate->compilation_allowed()) {
-    FATAL3("Precompilation missed function %s (%s, %s)\n",
-           function.ToLibNamePrefixedQualifiedCString(),
-           function.token_pos().ToCString(),
-           Function::KindToCString(function.kind()));
-  }
-
-  CompilationPipeline* pipeline =
-      CompilationPipeline::New(thread->zone(), function);
-
-  return ParseFunctionHelper(pipeline, function,
-                             /* optimized = */ false, kNoOSRDeoptId);
-}
-
 RawError* Compiler::EnsureUnoptimizedCode(Thread* thread,
                                           const Function& function) {
   if (function.unoptimized_code() != Object::null()) {
@@ -1086,25 +1000,6 @@
                                osr_id);
 }
 
-// This is only used from unit tests.
-RawError* Compiler::CompileParsedFunction(ParsedFunction* parsed_function) {
-  LongJumpScope jump;
-  if (setjmp(*jump.Set()) == 0) {
-    // Non-optimized code generator.
-    DartCompilationPipeline pipeline;
-    CompileParsedFunctionHelper helper(parsed_function, false, kNoOSRDeoptId);
-    helper.Compile(&pipeline);
-    if (FLAG_disassemble) {
-      Code& code = Code::Handle(parsed_function->function().CurrentCode());
-      Disassembler::DisassembleCode(parsed_function->function(), code, false);
-    }
-    return Error::null();
-  } else {
-    // We got an error during compilation.
-    return Thread::Current()->StealStickyError();
-  }
-}
-
 void Compiler::ComputeLocalVarDescriptors(const Code& code) {
   ASSERT(!code.is_optimized());
   const Function& function = Function::Handle(code.function());
@@ -1140,8 +1035,8 @@
         /* not inlining */ NULL, false, Compiler::kNoOSRDeoptId);
     builder.BuildGraph();
 
-    const LocalVarDescriptors& var_descs = LocalVarDescriptors::Handle(
-        parsed_function->node_sequence()->scope()->GetVarDescriptors(
+    const LocalVarDescriptors& var_descs =
+        LocalVarDescriptors::Handle(parsed_function->scope()->GetVarDescriptors(
             function, context_level_array));
     ASSERT(!var_descs.IsNull());
     code.set_var_descriptors(var_descs);
@@ -1209,70 +1104,6 @@
   return Error::null();
 }
 
-RawObject* Compiler::ExecuteOnce(SequenceNode* fragment) {
-#if defined(DART_PRECOMPILER) && !defined(TARGET_ARCH_DBC) &&                  \
-    !defined(TARGET_ARCH_IA32)
-  if (FLAG_precompiled_mode) {
-    UNREACHABLE();
-  }
-#endif
-  LongJumpScope jump;
-  if (setjmp(*jump.Set()) == 0) {
-    Thread* const thread = Thread::Current();
-
-    // Don't allow message interrupts while executing constant
-    // expressions.  They can cause bogus recursive compilation.
-    NoOOBMessageScope no_msg_scope(thread);
-
-    // Don't allow reload requests to come in.
-    NoReloadScope no_reload_scope(thread->isolate(), thread);
-
-    // Create a dummy function object for the code generator.
-    // The function needs to be associated with a named Class: the interface
-    // Function fits the bill.
-    const char* kEvalConst = "eval_const";
-    const Function& func = Function::ZoneHandle(Function::New(
-        String::Handle(Symbols::New(thread, kEvalConst)),
-        RawFunction::kRegularFunction,
-        true,   // static function
-        false,  // not const function
-        false,  // not abstract
-        false,  // not external
-        false,  // not native
-        Class::Handle(Type::Handle(Type::DartFunctionType()).type_class()),
-        fragment->token_pos()));
-
-    func.set_result_type(Object::dynamic_type());
-    func.set_num_fixed_parameters(0);
-    func.SetNumOptionalParameters(0, true);
-    // Manually generated AST, do not recompile.
-    func.SetIsOptimizable(false);
-    func.set_is_debuggable(false);
-
-    // We compile the function here, even though InvokeFunction() below
-    // would compile func automatically. We are checking fewer invariants
-    // here.
-    ParsedFunction* parsed_function = new ParsedFunction(thread, func);
-    parsed_function->SetNodeSequence(fragment);
-    fragment->scope()->AddVariable(parsed_function->EnsureExpressionTemp());
-    fragment->scope()->AddVariable(parsed_function->current_context_var());
-    parsed_function->AllocateVariables();
-
-    // Non-optimized code generator.
-    DartCompilationPipeline pipeline;
-    CompileParsedFunctionHelper helper(parsed_function, false, kNoOSRDeoptId);
-    const Code& code = Code::Handle(helper.Compile(&pipeline));
-    if (!code.IsNull()) {
-      NOT_IN_PRODUCT(code.set_var_descriptors(Object::empty_var_descriptors()));
-      const Object& result = PassiveObject::Handle(
-          DartEntry::InvokeFunction(func, Object::empty_array()));
-      return result.raw();
-    }
-  }
-
-  return Thread::Current()->StealStickyError();
-}
-
 void Compiler::AbortBackgroundCompilation(intptr_t deopt_id, const char* msg) {
   if (FLAG_trace_compiler) {
     THR_Print("ABORT background compilation: %s\n", msg);
@@ -1534,8 +1365,7 @@
   if (running_ || !done_) return;
   running_ = true;
   done_ = false;
-  bool task_started =
-      Dart::thread_pool()->Run(new BackgroundCompilerTask(this));
+  bool task_started = Dart::thread_pool()->Run<BackgroundCompilerTask>(this);
   if (!task_started) {
     running_ = false;
     done_ = true;
@@ -1603,21 +1433,11 @@
   return false;
 }
 
-RawError* Compiler::Compile(const Library& library, const Script& script) {
-  FATAL1("Attempt to compile script %s", script.ToCString());
-  return Error::null();
-}
-
 RawObject* Compiler::CompileFunction(Thread* thread, const Function& function) {
   FATAL1("Attempt to compile function %s", function.ToCString());
   return Error::null();
 }
 
-RawError* Compiler::ParseFunction(Thread* thread, const Function& function) {
-  FATAL1("Attempt to parse function %s", function.ToCString());
-  return Error::null();
-}
-
 RawError* Compiler::EnsureUnoptimizedCode(Thread* thread,
                                           const Function& function) {
   FATAL1("Attempt to compile function %s", function.ToCString());
@@ -1631,12 +1451,6 @@
   return Error::null();
 }
 
-RawError* Compiler::CompileParsedFunction(ParsedFunction* parsed_function) {
-  FATAL1("Attempt to compile function %s",
-         parsed_function->function().ToCString());
-  return Error::null();
-}
-
 void Compiler::ComputeLocalVarDescriptors(const Code& code) {
   UNREACHABLE();
 }
@@ -1646,11 +1460,6 @@
   return Error::null();
 }
 
-RawObject* Compiler::ExecuteOnce(SequenceNode* fragment) {
-  UNREACHABLE();
-  return Object::null();
-}
-
 void Compiler::AbortBackgroundCompilation(intptr_t deopt_id, const char* msg) {
   UNREACHABLE();
 }
diff --git a/runtime/vm/compiler/jit/compiler.h b/runtime/vm/compiler/jit/compiler.h
index b804a70..10aeb8e 100644
--- a/runtime/vm/compiler/jit/compiler.h
+++ b/runtime/vm/compiler/jit/compiler.h
@@ -82,20 +82,12 @@
   // The result for a function may change if debugging gets turned on/off.
   static bool CanOptimizeFunction(Thread* thread, const Function& function);
 
-  // Extracts top level entities from the script and populates
-  // the class dictionary of the library.
-  //
-  // Returns Error::null() if there is no compilation error.
-  static RawError* Compile(const Library& library, const Script& script);
-
   // Generates code for given function without optimization and sets its code
   // field.
   //
   // Returns the raw code object if compilation succeeds.  Otherwise returns a
   // RawError.  Also installs the generated code on the function.
   static RawObject* CompileFunction(Thread* thread, const Function& function);
-  // Returns Error::null() if there is no compilation error.
-  static RawError* ParseFunction(Thread* thread, const Function& function);
 
   // Generates unoptimized code if not present, current code is unchanged.
   // Bytecode is considered unoptimized code.
@@ -113,20 +105,6 @@
                                              const Function& function,
                                              intptr_t osr_id = kNoOSRDeoptId);
 
-  // Generates code for given parsed function (without parsing it again) and
-  // sets its code field.
-  //
-  // Returns Error::null() if there is no compilation error.
-  static RawError* CompileParsedFunction(ParsedFunction* parsed_function);
-
-  // Generates and executes code for a given code fragment, e.g. a
-  // compile time constant expression. Returns the result returned
-  // by the fragment.
-  //
-  // The return value is either a RawInstance on success or a RawError
-  // on compilation failure.
-  static RawObject* ExecuteOnce(SequenceNode* fragment);
-
   // Generates local var descriptors and sets it in 'code'. Do not call if the
   // local var descriptor already exists.
   static void ComputeLocalVarDescriptors(const Code& code);
diff --git a/runtime/vm/compiler/jit/jit_call_specializer.cc b/runtime/vm/compiler/jit/jit_call_specializer.cc
index f073878..189ef8b 100644
--- a/runtime/vm/compiler/jit/jit_call_specializer.cc
+++ b/runtime/vm/compiler/jit/jit_call_specializer.cc
@@ -52,8 +52,10 @@
 
 void JitCallSpecializer::ReplaceWithStaticCall(InstanceCallInstr* instr,
                                                const ICData& unary_checks,
-                                               const Function& target) {
-  StaticCallInstr* call = StaticCallInstr::FromCall(Z, instr, target);
+                                               const Function& target,
+                                               intptr_t call_count) {
+  StaticCallInstr* call =
+      StaticCallInstr::FromCall(Z, instr, target, call_count);
   if (unary_checks.NumberOfChecks() == 1 &&
       unary_checks.GetExactnessAt(0).IsExact()) {
     if (unary_checks.GetExactnessAt(0).IsTriviallyExact()) {
@@ -143,7 +145,8 @@
         Function::ZoneHandle(Z, unary_checks.GetTargetAt(0));
     if (flow_graph()->CheckForInstanceCall(instr, target.kind()) ==
         FlowGraph::ToCheck::kNoCheck) {
-      ReplaceWithStaticCall(instr, unary_checks, target);
+      ReplaceWithStaticCall(instr, unary_checks, target,
+                            targets.AggregateCallCount());
       return;
     }
   }
@@ -168,7 +171,8 @@
     // Call can still deoptimize, do not detach environment from instr.
     const Function& target =
         Function::ZoneHandle(Z, unary_checks.GetTargetAt(0));
-    ReplaceWithStaticCall(instr, unary_checks, target);
+    ReplaceWithStaticCall(instr, unary_checks, target,
+                          targets.AggregateCallCount());
   } else {
     PolymorphicInstanceCallInstr* call =
         new (Z) PolymorphicInstanceCallInstr(instr, targets,
diff --git a/runtime/vm/compiler/jit/jit_call_specializer.h b/runtime/vm/compiler/jit/jit_call_specializer.h
index 77c72bd..00f7f67 100644
--- a/runtime/vm/compiler/jit/jit_call_specializer.h
+++ b/runtime/vm/compiler/jit/jit_call_specializer.h
@@ -37,7 +37,8 @@
 
   void ReplaceWithStaticCall(InstanceCallInstr* instr,
                              const ICData& unary_checks,
-                             const Function& target);
+                             const Function& target,
+                             intptr_t call_count);
 
   DISALLOW_COPY_AND_ASSIGN(JitCallSpecializer);
 };
diff --git a/runtime/vm/compiler/recognized_methods_list.h b/runtime/vm/compiler/recognized_methods_list.h
index ffd7255..2116ba9 100644
--- a/runtime/vm/compiler/recognized_methods_list.h
+++ b/runtime/vm/compiler/recognized_methods_list.h
@@ -47,21 +47,21 @@
   V(_ByteDataView, get:_typedData, ByteDataViewTypedData, 0x0)                 \
   V(_TypedListView, get:offsetInBytes, TypedDataViewOffsetInBytes, 0x0)        \
   V(_TypedListView, get:_typedData, TypedDataViewTypedData, 0x0)               \
-  V(_ByteDataView, ., TypedData_ByteDataView_factory, 0x0)                     \
-  V(_Int8ArrayView, ., TypedData_Int8ArrayView_factory, 0x0)                   \
-  V(_Uint8ArrayView, ., TypedData_Uint8ArrayView_factory, 0x0)                 \
-  V(_Uint8ClampedArrayView, ., TypedData_Uint8ClampedArrayView_factory, 0x0)   \
-  V(_Int16ArrayView, ., TypedData_Int16ArrayView_factory, 0x0)                 \
-  V(_Uint16ArrayView, ., TypedData_Uint16ArrayView_factory, 0x0)               \
-  V(_Int32ArrayView, ., TypedData_Int32ArrayView_factory, 0x0)                 \
-  V(_Uint32ArrayView, ., TypedData_Uint32ArrayView_factory, 0x0)               \
-  V(_Int64ArrayView, ., TypedData_Int64ArrayView_factory, 0x0)                 \
-  V(_Uint64ArrayView, ., TypedData_Uint64ArrayView_factory, 0x0)               \
-  V(_Float32ArrayView, ., TypedData_Float32ArrayView_factory, 0x0)             \
-  V(_Float64ArrayView, ., TypedData_Float64ArrayView_factory, 0x0)             \
-  V(_Float32x4ArrayView, ., TypedData_Float32x4ArrayView_factory, 0x0)         \
-  V(_Int32x4ArrayView, ., TypedData_Int32x4ArrayView_factory, 0x0)             \
-  V(_Float64x2ArrayView, ., TypedData_Float64x2ArrayView_factory, 0x0)         \
+  V(_ByteDataView, ._, TypedData_ByteDataView_factory, 0x0)                    \
+  V(_Int8ArrayView, ._, TypedData_Int8ArrayView_factory, 0x0)                  \
+  V(_Uint8ArrayView, ._, TypedData_Uint8ArrayView_factory, 0x0)                \
+  V(_Uint8ClampedArrayView, ._, TypedData_Uint8ClampedArrayView_factory, 0x0)  \
+  V(_Int16ArrayView, ._, TypedData_Int16ArrayView_factory, 0x0)                \
+  V(_Uint16ArrayView, ._, TypedData_Uint16ArrayView_factory, 0x0)              \
+  V(_Int32ArrayView, ._, TypedData_Int32ArrayView_factory, 0x0)                \
+  V(_Uint32ArrayView, ._, TypedData_Uint32ArrayView_factory, 0x0)              \
+  V(_Int64ArrayView, ._, TypedData_Int64ArrayView_factory, 0x0)                \
+  V(_Uint64ArrayView, ._, TypedData_Uint64ArrayView_factory, 0x0)              \
+  V(_Float32ArrayView, ._, TypedData_Float32ArrayView_factory, 0x0)            \
+  V(_Float64ArrayView, ._, TypedData_Float64ArrayView_factory, 0x0)            \
+  V(_Float32x4ArrayView, ._, TypedData_Float32x4ArrayView_factory, 0x0)        \
+  V(_Int32x4ArrayView, ._, TypedData_Int32x4ArrayView_factory, 0x0)            \
+  V(_Float64x2ArrayView, ._, TypedData_Float64x2ArrayView_factory, 0x0)        \
   V(::, _toClampedUint8, ConvertIntToClampedUint8, 0x564b0435)                 \
   V(_StringBase, _interpolate, StringBaseInterpolate, 0x01ecb15a)              \
   V(_IntegerImplementation, toDouble, IntegerToDouble, 0x05da96ed)             \
diff --git a/runtime/vm/compiler/runtime_api.cc b/runtime/vm/compiler/runtime_api.cc
index 79fff9b..0c35ced 100644
--- a/runtime/vm/compiler/runtime_api.cc
+++ b/runtime/vm/compiler/runtime_api.cc
@@ -458,7 +458,10 @@
     return clazz##_elements_start_offset + index * clazz##_element_size;       \
   }
 
-#define DEFINE_ARRAY_STRUCTFIELD(clazz, name, element_offset, field_offset)
+#define DEFINE_ARRAY_STRUCTFIELD(clazz, name, element_offset, field_offset)    \
+  word clazz::name(intptr_t index) {                                           \
+    return element_offset(index) + field_offset;                               \
+  }
 
 #define DEFINE_SIZEOF(clazz, name, what)                                       \
   word clazz::name() { return clazz##_##name; }
@@ -553,22 +556,6 @@
   return false;
 }
 
-#if !defined(PRODUCT)
-word ClassTable::StateOffsetFor(intptr_t cid) {
-  return dart::ClassTable::StateOffsetFor(cid);
-}
-
-word ClassTable::NewSpaceCounterOffsetFor(intptr_t index) {
-  return ClassOffsetFor(index) +
-         ClassHeapStats::allocated_since_gc_new_space_offset();
-}
-
-word ClassTable::NewSpaceSizeOffsetFor(intptr_t index) {
-  return ClassOffsetFor(index) +
-         ClassHeapStats::allocated_size_since_gc_new_space_offset();
-}
-#endif  // !defined(PRODUCT)
-
 static_assert(
     kSmiBits <= dart::kSmiBits,
     "Expected that size of Smi on HOST is at least as large as on target.");
diff --git a/runtime/vm/compiler/runtime_api.h b/runtime/vm/compiler/runtime_api.h
index 57f4312..5f0f76b 100644
--- a/runtime/vm/compiler/runtime_api.h
+++ b/runtime/vm/compiler/runtime_api.h
@@ -626,6 +626,7 @@
   static word dart_stream_offset();
   static word async_stack_trace_offset();
   static word predefined_symbols_address_offset();
+  static word optimize_entry_offset();
   static word deoptimize_entry_offset();
   static word megamorphic_call_checked_entry_offset();
   static word active_exception_offset();
@@ -676,7 +677,6 @@
   static word fix_allocation_stub_code_offset();
 
   static word monomorphic_miss_stub_offset();
-  static word ic_lookup_through_code_stub_offset();
   static word lazy_specialize_type_test_stub_offset();
   static word slow_type_test_stub_offset();
   static word call_to_runtime_stub_offset();
@@ -691,6 +691,7 @@
   static word stack_overflow_shared_with_fpu_regs_stub_offset();
   static word lazy_deopt_from_return_stub_offset();
   static word lazy_deopt_from_throw_stub_offset();
+  static word optimize_stub_offset();
   static word deoptimize_stub_offset();
   static word enter_safepoint_stub_offset();
   static word exit_safepoint_stub_offset();
@@ -775,8 +776,10 @@
 
 class Instructions : public AllStatic {
  public:
-  static const word kPolymorphicEntryOffset;
-  static const word kMonomorphicEntryOffset;
+  static const word kMonomorphicEntryOffsetJIT;
+  static const word kPolymorphicEntryOffsetJIT;
+  static const word kMonomorphicEntryOffsetAOT;
+  static const word kPolymorphicEntryOffsetAOT;
   static word HeaderSize();
   static word UnalignedHeaderSize();
 };
diff --git a/runtime/vm/compiler/runtime_offsets_extracted.h b/runtime/vm/compiler/runtime_offsets_extracted.h
index 761b30f..bf791bc 100644
--- a/runtime/vm/compiler/runtime_offsets_extracted.h
+++ b/runtime/vm/compiler/runtime_offsets_extracted.h
@@ -25,9 +25,13 @@
 static constexpr dart::compiler::target::word ClassTable_kSizeOfClassPairLog2 =
     3;
 static constexpr dart::compiler::target::word
-    Instructions_kMonomorphicEntryOffset = 20;
+    Instructions_kMonomorphicEntryOffsetJIT = 0;
 static constexpr dart::compiler::target::word
-    Instructions_kPolymorphicEntryOffset = 0;
+    Instructions_kPolymorphicEntryOffsetJIT = 40;
+static constexpr dart::compiler::target::word
+    Instructions_kMonomorphicEntryOffsetAOT = 0;
+static constexpr dart::compiler::target::word
+    Instructions_kPolymorphicEntryOffsetAOT = 20;
 static constexpr dart::compiler::target::word HeapPage_kBytesPerCardLog2 = 9;
 static constexpr dart::compiler::target::word
     NativeEntry_kNumCallWrapperArguments = 2;
@@ -187,7 +191,7 @@
 static constexpr dart::compiler::target::word String_length_offset = 4;
 static constexpr dart::compiler::target::word SubtypeTestCache_cache_offset = 4;
 static constexpr dart::compiler::target::word
-    Thread_AllocateArray_entry_point_offset = 284;
+    Thread_AllocateArray_entry_point_offset = 280;
 static constexpr dart::compiler::target::word Thread_active_exception_offset =
     612;
 static constexpr dart::compiler::target::word Thread_active_stacktrace_offset =
@@ -195,97 +199,98 @@
 static constexpr dart::compiler::target::word
     Thread_array_write_barrier_code_offset = 112;
 static constexpr dart::compiler::target::word
-    Thread_array_write_barrier_entry_point_offset = 196;
+    Thread_array_write_barrier_entry_point_offset = 192;
 static constexpr dart::compiler::target::word Thread_async_stack_trace_offset =
     84;
 static constexpr dart::compiler::target::word
-    Thread_auto_scope_native_wrapper_entry_point_offset = 244;
+    Thread_auto_scope_native_wrapper_entry_point_offset = 240;
 static constexpr dart::compiler::target::word Thread_bool_false_offset = 104;
 static constexpr dart::compiler::target::word Thread_bool_true_offset = 100;
 static constexpr dart::compiler::target::word
-    Thread_call_to_runtime_entry_point_offset = 200;
+    Thread_call_to_runtime_entry_point_offset = 196;
 static constexpr dart::compiler::target::word
     Thread_call_to_runtime_stub_offset = 132;
 static constexpr dart::compiler::target::word Thread_dart_stream_offset = 644;
+static constexpr dart::compiler::target::word Thread_optimize_entry_offset =
+    224;
+static constexpr dart::compiler::target::word Thread_optimize_stub_offset = 156;
 static constexpr dart::compiler::target::word Thread_deoptimize_entry_offset =
-    232;
+    228;
 static constexpr dart::compiler::target::word Thread_deoptimize_stub_offset =
-    164;
+    160;
 static constexpr dart::compiler::target::word Thread_double_abs_address_offset =
-    264;
+    260;
 static constexpr dart::compiler::target::word
-    Thread_double_negate_address_offset = 260;
+    Thread_double_negate_address_offset = 256;
 static constexpr dart::compiler::target::word Thread_end_offset = 60;
 static constexpr dart::compiler::target::word
-    Thread_enter_safepoint_stub_offset = 184;
+    Thread_enter_safepoint_stub_offset = 180;
 static constexpr dart::compiler::target::word Thread_execution_state_offset =
     628;
 static constexpr dart::compiler::target::word
-    Thread_exit_safepoint_stub_offset = 188;
+    Thread_exit_safepoint_stub_offset = 184;
 static constexpr dart::compiler::target::word
     Thread_fix_allocation_stub_code_offset = 120;
 static constexpr dart::compiler::target::word
     Thread_fix_callers_target_code_offset = 116;
 static constexpr dart::compiler::target::word
-    Thread_float_absolute_address_offset = 276;
+    Thread_float_absolute_address_offset = 272;
 static constexpr dart::compiler::target::word
-    Thread_float_negate_address_offset = 272;
+    Thread_float_negate_address_offset = 268;
 static constexpr dart::compiler::target::word Thread_float_not_address_offset =
-    268;
+    264;
 static constexpr dart::compiler::target::word
-    Thread_float_zerow_address_offset = 280;
+    Thread_float_zerow_address_offset = 276;
 static constexpr dart::compiler::target::word Thread_global_object_pool_offset =
     620;
 static constexpr dart::compiler::target::word
-    Thread_ic_lookup_through_code_stub_offset = 156;
-static constexpr dart::compiler::target::word
-    Thread_interpret_call_entry_point_offset = 248;
+    Thread_interpret_call_entry_point_offset = 244;
 static constexpr dart::compiler::target::word
     Thread_invoke_dart_code_from_bytecode_stub_offset = 128;
 static constexpr dart::compiler::target::word
     Thread_invoke_dart_code_stub_offset = 124;
 static constexpr dart::compiler::target::word Thread_isolate_offset = 48;
 static constexpr dart::compiler::target::word
-    Thread_lazy_deopt_from_return_stub_offset = 168;
+    Thread_lazy_deopt_from_return_stub_offset = 164;
 static constexpr dart::compiler::target::word
-    Thread_lazy_deopt_from_throw_stub_offset = 172;
+    Thread_lazy_deopt_from_throw_stub_offset = 168;
 static constexpr dart::compiler::target::word
-    Thread_lazy_specialize_type_test_stub_offset = 180;
+    Thread_lazy_specialize_type_test_stub_offset = 176;
 static constexpr dart::compiler::target::word
     Thread_marking_stack_block_offset = 72;
 static constexpr dart::compiler::target::word
-    Thread_megamorphic_call_checked_entry_offset = 220;
+    Thread_megamorphic_call_checked_entry_offset = 216;
 static constexpr dart::compiler::target::word
-    Thread_monomorphic_miss_entry_offset = 224;
+    Thread_monomorphic_miss_entry_offset = 220;
 static constexpr dart::compiler::target::word
     Thread_monomorphic_miss_stub_offset = 152;
 static constexpr dart::compiler::target::word
-    Thread_no_scope_native_wrapper_entry_point_offset = 240;
+    Thread_no_scope_native_wrapper_entry_point_offset = 236;
 static constexpr dart::compiler::target::word
-    Thread_null_error_shared_with_fpu_regs_entry_point_offset = 208;
+    Thread_null_error_shared_with_fpu_regs_entry_point_offset = 204;
 static constexpr dart::compiler::target::word
     Thread_null_error_shared_with_fpu_regs_stub_offset = 140;
 static constexpr dart::compiler::target::word
-    Thread_null_error_shared_without_fpu_regs_entry_point_offset = 204;
+    Thread_null_error_shared_without_fpu_regs_entry_point_offset = 200;
 static constexpr dart::compiler::target::word
     Thread_null_error_shared_without_fpu_regs_stub_offset = 136;
 static constexpr dart::compiler::target::word Thread_object_null_offset = 96;
 static constexpr dart::compiler::target::word
-    Thread_predefined_symbols_address_offset = 252;
+    Thread_predefined_symbols_address_offset = 248;
 static constexpr dart::compiler::target::word Thread_resume_pc_offset = 624;
 static constexpr dart::compiler::target::word Thread_safepoint_state_offset =
     632;
 static constexpr dart::compiler::target::word
-    Thread_slow_type_test_stub_offset = 176;
+    Thread_slow_type_test_stub_offset = 172;
 static constexpr dart::compiler::target::word Thread_stack_limit_offset = 36;
 static constexpr dart::compiler::target::word
     Thread_stack_overflow_flags_offset = 40;
 static constexpr dart::compiler::target::word
-    Thread_stack_overflow_shared_with_fpu_regs_entry_point_offset = 216;
+    Thread_stack_overflow_shared_with_fpu_regs_entry_point_offset = 212;
 static constexpr dart::compiler::target::word
     Thread_stack_overflow_shared_with_fpu_regs_stub_offset = 148;
 static constexpr dart::compiler::target::word
-    Thread_stack_overflow_shared_without_fpu_regs_entry_point_offset = 212;
+    Thread_stack_overflow_shared_without_fpu_regs_entry_point_offset = 208;
 static constexpr dart::compiler::target::word
     Thread_stack_overflow_shared_without_fpu_regs_stub_offset = 144;
 static constexpr dart::compiler::target::word Thread_store_buffer_block_offset =
@@ -300,11 +305,11 @@
 static constexpr dart::compiler::target::word Thread_write_barrier_code_offset =
     108;
 static constexpr dart::compiler::target::word
-    Thread_write_barrier_entry_point_offset = 192;
+    Thread_write_barrier_entry_point_offset = 188;
 static constexpr dart::compiler::target::word Thread_write_barrier_mask_offset =
     44;
 static constexpr dart::compiler::target::word
-    Thread_verify_callback_entry_offset = 236;
+    Thread_verify_callback_entry_offset = 232;
 static constexpr dart::compiler::target::word Thread_callback_code_offset = 636;
 static constexpr dart::compiler::target::word TimelineStream_enabled_offset = 8;
 static constexpr dart::compiler::target::word TwoByteString_data_offset = 12;
@@ -372,9 +377,13 @@
 static constexpr dart::compiler::target::word ClassTable_kSizeOfClassPairLog2 =
     4;
 static constexpr dart::compiler::target::word
-    Instructions_kMonomorphicEntryOffset = 32;
+    Instructions_kMonomorphicEntryOffsetJIT = 8;
 static constexpr dart::compiler::target::word
-    Instructions_kPolymorphicEntryOffset = 8;
+    Instructions_kPolymorphicEntryOffsetJIT = 40;
+static constexpr dart::compiler::target::word
+    Instructions_kMonomorphicEntryOffsetAOT = 8;
+static constexpr dart::compiler::target::word
+    Instructions_kPolymorphicEntryOffsetAOT = 32;
 static constexpr dart::compiler::target::word HeapPage_kBytesPerCardLog2 = 10;
 static constexpr dart::compiler::target::word
     NativeEntry_kNumCallWrapperArguments = 2;
@@ -535,7 +544,7 @@
 static constexpr dart::compiler::target::word String_length_offset = 8;
 static constexpr dart::compiler::target::word SubtypeTestCache_cache_offset = 8;
 static constexpr dart::compiler::target::word
-    Thread_AllocateArray_entry_point_offset = 560;
+    Thread_AllocateArray_entry_point_offset = 552;
 static constexpr dart::compiler::target::word Thread_active_exception_offset =
     1232;
 static constexpr dart::compiler::target::word Thread_active_stacktrace_offset =
@@ -543,97 +552,98 @@
 static constexpr dart::compiler::target::word
     Thread_array_write_barrier_code_offset = 216;
 static constexpr dart::compiler::target::word
-    Thread_array_write_barrier_entry_point_offset = 384;
+    Thread_array_write_barrier_entry_point_offset = 376;
 static constexpr dart::compiler::target::word Thread_async_stack_trace_offset =
     168;
 static constexpr dart::compiler::target::word
-    Thread_auto_scope_native_wrapper_entry_point_offset = 480;
+    Thread_auto_scope_native_wrapper_entry_point_offset = 472;
 static constexpr dart::compiler::target::word Thread_bool_false_offset = 200;
 static constexpr dart::compiler::target::word Thread_bool_true_offset = 192;
 static constexpr dart::compiler::target::word
-    Thread_call_to_runtime_entry_point_offset = 392;
+    Thread_call_to_runtime_entry_point_offset = 384;
 static constexpr dart::compiler::target::word
     Thread_call_to_runtime_stub_offset = 256;
 static constexpr dart::compiler::target::word Thread_dart_stream_offset = 1296;
+static constexpr dart::compiler::target::word Thread_optimize_entry_offset =
+    440;
+static constexpr dart::compiler::target::word Thread_optimize_stub_offset = 304;
 static constexpr dart::compiler::target::word Thread_deoptimize_entry_offset =
-    456;
+    448;
 static constexpr dart::compiler::target::word Thread_deoptimize_stub_offset =
-    320;
+    312;
 static constexpr dart::compiler::target::word Thread_double_abs_address_offset =
-    520;
+    512;
 static constexpr dart::compiler::target::word
-    Thread_double_negate_address_offset = 512;
+    Thread_double_negate_address_offset = 504;
 static constexpr dart::compiler::target::word Thread_end_offset = 120;
 static constexpr dart::compiler::target::word
-    Thread_enter_safepoint_stub_offset = 360;
+    Thread_enter_safepoint_stub_offset = 352;
 static constexpr dart::compiler::target::word Thread_execution_state_offset =
     1264;
 static constexpr dart::compiler::target::word
-    Thread_exit_safepoint_stub_offset = 368;
+    Thread_exit_safepoint_stub_offset = 360;
 static constexpr dart::compiler::target::word
     Thread_fix_allocation_stub_code_offset = 232;
 static constexpr dart::compiler::target::word
     Thread_fix_callers_target_code_offset = 224;
 static constexpr dart::compiler::target::word
-    Thread_float_absolute_address_offset = 544;
+    Thread_float_absolute_address_offset = 536;
 static constexpr dart::compiler::target::word
-    Thread_float_negate_address_offset = 536;
+    Thread_float_negate_address_offset = 528;
 static constexpr dart::compiler::target::word Thread_float_not_address_offset =
-    528;
+    520;
 static constexpr dart::compiler::target::word
-    Thread_float_zerow_address_offset = 552;
+    Thread_float_zerow_address_offset = 544;
 static constexpr dart::compiler::target::word Thread_global_object_pool_offset =
     1248;
 static constexpr dart::compiler::target::word
-    Thread_ic_lookup_through_code_stub_offset = 304;
-static constexpr dart::compiler::target::word
-    Thread_interpret_call_entry_point_offset = 488;
+    Thread_interpret_call_entry_point_offset = 480;
 static constexpr dart::compiler::target::word
     Thread_invoke_dart_code_from_bytecode_stub_offset = 248;
 static constexpr dart::compiler::target::word
     Thread_invoke_dart_code_stub_offset = 240;
 static constexpr dart::compiler::target::word Thread_isolate_offset = 96;
 static constexpr dart::compiler::target::word
-    Thread_lazy_deopt_from_return_stub_offset = 328;
+    Thread_lazy_deopt_from_return_stub_offset = 320;
 static constexpr dart::compiler::target::word
-    Thread_lazy_deopt_from_throw_stub_offset = 336;
+    Thread_lazy_deopt_from_throw_stub_offset = 328;
 static constexpr dart::compiler::target::word
-    Thread_lazy_specialize_type_test_stub_offset = 352;
+    Thread_lazy_specialize_type_test_stub_offset = 344;
 static constexpr dart::compiler::target::word
     Thread_marking_stack_block_offset = 144;
 static constexpr dart::compiler::target::word
-    Thread_megamorphic_call_checked_entry_offset = 432;
+    Thread_megamorphic_call_checked_entry_offset = 424;
 static constexpr dart::compiler::target::word
-    Thread_monomorphic_miss_entry_offset = 440;
+    Thread_monomorphic_miss_entry_offset = 432;
 static constexpr dart::compiler::target::word
     Thread_monomorphic_miss_stub_offset = 296;
 static constexpr dart::compiler::target::word
-    Thread_no_scope_native_wrapper_entry_point_offset = 472;
+    Thread_no_scope_native_wrapper_entry_point_offset = 464;
 static constexpr dart::compiler::target::word
-    Thread_null_error_shared_with_fpu_regs_entry_point_offset = 408;
+    Thread_null_error_shared_with_fpu_regs_entry_point_offset = 400;
 static constexpr dart::compiler::target::word
     Thread_null_error_shared_with_fpu_regs_stub_offset = 272;
 static constexpr dart::compiler::target::word
-    Thread_null_error_shared_without_fpu_regs_entry_point_offset = 400;
+    Thread_null_error_shared_without_fpu_regs_entry_point_offset = 392;
 static constexpr dart::compiler::target::word
     Thread_null_error_shared_without_fpu_regs_stub_offset = 264;
 static constexpr dart::compiler::target::word Thread_object_null_offset = 184;
 static constexpr dart::compiler::target::word
-    Thread_predefined_symbols_address_offset = 496;
+    Thread_predefined_symbols_address_offset = 488;
 static constexpr dart::compiler::target::word Thread_resume_pc_offset = 1256;
 static constexpr dart::compiler::target::word Thread_safepoint_state_offset =
     1272;
 static constexpr dart::compiler::target::word
-    Thread_slow_type_test_stub_offset = 344;
+    Thread_slow_type_test_stub_offset = 336;
 static constexpr dart::compiler::target::word Thread_stack_limit_offset = 72;
 static constexpr dart::compiler::target::word
     Thread_stack_overflow_flags_offset = 80;
 static constexpr dart::compiler::target::word
-    Thread_stack_overflow_shared_with_fpu_regs_entry_point_offset = 424;
+    Thread_stack_overflow_shared_with_fpu_regs_entry_point_offset = 416;
 static constexpr dart::compiler::target::word
     Thread_stack_overflow_shared_with_fpu_regs_stub_offset = 288;
 static constexpr dart::compiler::target::word
-    Thread_stack_overflow_shared_without_fpu_regs_entry_point_offset = 416;
+    Thread_stack_overflow_shared_without_fpu_regs_entry_point_offset = 408;
 static constexpr dart::compiler::target::word
     Thread_stack_overflow_shared_without_fpu_regs_stub_offset = 280;
 static constexpr dart::compiler::target::word Thread_store_buffer_block_offset =
@@ -648,11 +658,11 @@
 static constexpr dart::compiler::target::word Thread_write_barrier_code_offset =
     208;
 static constexpr dart::compiler::target::word
-    Thread_write_barrier_entry_point_offset = 376;
+    Thread_write_barrier_entry_point_offset = 368;
 static constexpr dart::compiler::target::word Thread_write_barrier_mask_offset =
     88;
 static constexpr dart::compiler::target::word
-    Thread_verify_callback_entry_offset = 464;
+    Thread_verify_callback_entry_offset = 456;
 static constexpr dart::compiler::target::word Thread_callback_code_offset =
     1280;
 static constexpr dart::compiler::target::word TimelineStream_enabled_offset =
@@ -722,9 +732,13 @@
 static constexpr dart::compiler::target::word ClassTable_kSizeOfClassPairLog2 =
     3;
 static constexpr dart::compiler::target::word
-    Instructions_kMonomorphicEntryOffset = 0;
+    Instructions_kMonomorphicEntryOffsetJIT = 6;
 static constexpr dart::compiler::target::word
-    Instructions_kPolymorphicEntryOffset = 0;
+    Instructions_kPolymorphicEntryOffsetJIT = 34;
+static constexpr dart::compiler::target::word
+    Instructions_kMonomorphicEntryOffsetAOT = 0;
+static constexpr dart::compiler::target::word
+    Instructions_kPolymorphicEntryOffsetAOT = 0;
 static constexpr dart::compiler::target::word HeapPage_kBytesPerCardLog2 = 9;
 static constexpr dart::compiler::target::word
     NativeEntry_kNumCallWrapperArguments = 2;
@@ -884,7 +898,7 @@
 static constexpr dart::compiler::target::word String_length_offset = 4;
 static constexpr dart::compiler::target::word SubtypeTestCache_cache_offset = 4;
 static constexpr dart::compiler::target::word
-    Thread_AllocateArray_entry_point_offset = 284;
+    Thread_AllocateArray_entry_point_offset = 280;
 static constexpr dart::compiler::target::word Thread_active_exception_offset =
     576;
 static constexpr dart::compiler::target::word Thread_active_stacktrace_offset =
@@ -892,97 +906,98 @@
 static constexpr dart::compiler::target::word
     Thread_array_write_barrier_code_offset = 112;
 static constexpr dart::compiler::target::word
-    Thread_array_write_barrier_entry_point_offset = 196;
+    Thread_array_write_barrier_entry_point_offset = 192;
 static constexpr dart::compiler::target::word Thread_async_stack_trace_offset =
     84;
 static constexpr dart::compiler::target::word
-    Thread_auto_scope_native_wrapper_entry_point_offset = 244;
+    Thread_auto_scope_native_wrapper_entry_point_offset = 240;
 static constexpr dart::compiler::target::word Thread_bool_false_offset = 104;
 static constexpr dart::compiler::target::word Thread_bool_true_offset = 100;
 static constexpr dart::compiler::target::word
-    Thread_call_to_runtime_entry_point_offset = 200;
+    Thread_call_to_runtime_entry_point_offset = 196;
 static constexpr dart::compiler::target::word
     Thread_call_to_runtime_stub_offset = 132;
 static constexpr dart::compiler::target::word Thread_dart_stream_offset = 608;
+static constexpr dart::compiler::target::word Thread_optimize_entry_offset =
+    224;
+static constexpr dart::compiler::target::word Thread_optimize_stub_offset = 156;
 static constexpr dart::compiler::target::word Thread_deoptimize_entry_offset =
-    232;
+    228;
 static constexpr dart::compiler::target::word Thread_deoptimize_stub_offset =
-    164;
+    160;
 static constexpr dart::compiler::target::word Thread_double_abs_address_offset =
-    264;
+    260;
 static constexpr dart::compiler::target::word
-    Thread_double_negate_address_offset = 260;
+    Thread_double_negate_address_offset = 256;
 static constexpr dart::compiler::target::word Thread_end_offset = 60;
 static constexpr dart::compiler::target::word
-    Thread_enter_safepoint_stub_offset = 184;
+    Thread_enter_safepoint_stub_offset = 180;
 static constexpr dart::compiler::target::word Thread_execution_state_offset =
     592;
 static constexpr dart::compiler::target::word
-    Thread_exit_safepoint_stub_offset = 188;
+    Thread_exit_safepoint_stub_offset = 184;
 static constexpr dart::compiler::target::word
     Thread_fix_allocation_stub_code_offset = 120;
 static constexpr dart::compiler::target::word
     Thread_fix_callers_target_code_offset = 116;
 static constexpr dart::compiler::target::word
-    Thread_float_absolute_address_offset = 276;
+    Thread_float_absolute_address_offset = 272;
 static constexpr dart::compiler::target::word
-    Thread_float_negate_address_offset = 272;
+    Thread_float_negate_address_offset = 268;
 static constexpr dart::compiler::target::word Thread_float_not_address_offset =
-    268;
+    264;
 static constexpr dart::compiler::target::word
-    Thread_float_zerow_address_offset = 280;
+    Thread_float_zerow_address_offset = 276;
 static constexpr dart::compiler::target::word Thread_global_object_pool_offset =
     584;
 static constexpr dart::compiler::target::word
-    Thread_ic_lookup_through_code_stub_offset = 156;
-static constexpr dart::compiler::target::word
-    Thread_interpret_call_entry_point_offset = 248;
+    Thread_interpret_call_entry_point_offset = 244;
 static constexpr dart::compiler::target::word
     Thread_invoke_dart_code_from_bytecode_stub_offset = 128;
 static constexpr dart::compiler::target::word
     Thread_invoke_dart_code_stub_offset = 124;
 static constexpr dart::compiler::target::word Thread_isolate_offset = 48;
 static constexpr dart::compiler::target::word
-    Thread_lazy_deopt_from_return_stub_offset = 168;
+    Thread_lazy_deopt_from_return_stub_offset = 164;
 static constexpr dart::compiler::target::word
-    Thread_lazy_deopt_from_throw_stub_offset = 172;
+    Thread_lazy_deopt_from_throw_stub_offset = 168;
 static constexpr dart::compiler::target::word
-    Thread_lazy_specialize_type_test_stub_offset = 180;
+    Thread_lazy_specialize_type_test_stub_offset = 176;
 static constexpr dart::compiler::target::word
     Thread_marking_stack_block_offset = 72;
 static constexpr dart::compiler::target::word
-    Thread_megamorphic_call_checked_entry_offset = 220;
+    Thread_megamorphic_call_checked_entry_offset = 216;
 static constexpr dart::compiler::target::word
-    Thread_monomorphic_miss_entry_offset = 224;
+    Thread_monomorphic_miss_entry_offset = 220;
 static constexpr dart::compiler::target::word
     Thread_monomorphic_miss_stub_offset = 152;
 static constexpr dart::compiler::target::word
-    Thread_no_scope_native_wrapper_entry_point_offset = 240;
+    Thread_no_scope_native_wrapper_entry_point_offset = 236;
 static constexpr dart::compiler::target::word
-    Thread_null_error_shared_with_fpu_regs_entry_point_offset = 208;
+    Thread_null_error_shared_with_fpu_regs_entry_point_offset = 204;
 static constexpr dart::compiler::target::word
     Thread_null_error_shared_with_fpu_regs_stub_offset = 140;
 static constexpr dart::compiler::target::word
-    Thread_null_error_shared_without_fpu_regs_entry_point_offset = 204;
+    Thread_null_error_shared_without_fpu_regs_entry_point_offset = 200;
 static constexpr dart::compiler::target::word
     Thread_null_error_shared_without_fpu_regs_stub_offset = 136;
 static constexpr dart::compiler::target::word Thread_object_null_offset = 96;
 static constexpr dart::compiler::target::word
-    Thread_predefined_symbols_address_offset = 252;
+    Thread_predefined_symbols_address_offset = 248;
 static constexpr dart::compiler::target::word Thread_resume_pc_offset = 588;
 static constexpr dart::compiler::target::word Thread_safepoint_state_offset =
     596;
 static constexpr dart::compiler::target::word
-    Thread_slow_type_test_stub_offset = 176;
+    Thread_slow_type_test_stub_offset = 172;
 static constexpr dart::compiler::target::word Thread_stack_limit_offset = 36;
 static constexpr dart::compiler::target::word
     Thread_stack_overflow_flags_offset = 40;
 static constexpr dart::compiler::target::word
-    Thread_stack_overflow_shared_with_fpu_regs_entry_point_offset = 216;
+    Thread_stack_overflow_shared_with_fpu_regs_entry_point_offset = 212;
 static constexpr dart::compiler::target::word
     Thread_stack_overflow_shared_with_fpu_regs_stub_offset = 148;
 static constexpr dart::compiler::target::word
-    Thread_stack_overflow_shared_without_fpu_regs_entry_point_offset = 212;
+    Thread_stack_overflow_shared_without_fpu_regs_entry_point_offset = 208;
 static constexpr dart::compiler::target::word
     Thread_stack_overflow_shared_without_fpu_regs_stub_offset = 144;
 static constexpr dart::compiler::target::word Thread_store_buffer_block_offset =
@@ -997,11 +1012,11 @@
 static constexpr dart::compiler::target::word Thread_write_barrier_code_offset =
     108;
 static constexpr dart::compiler::target::word
-    Thread_write_barrier_entry_point_offset = 192;
+    Thread_write_barrier_entry_point_offset = 188;
 static constexpr dart::compiler::target::word Thread_write_barrier_mask_offset =
     44;
 static constexpr dart::compiler::target::word
-    Thread_verify_callback_entry_offset = 236;
+    Thread_verify_callback_entry_offset = 232;
 static constexpr dart::compiler::target::word Thread_callback_code_offset = 600;
 static constexpr dart::compiler::target::word TimelineStream_enabled_offset = 8;
 static constexpr dart::compiler::target::word TwoByteString_data_offset = 12;
@@ -1065,9 +1080,13 @@
 static constexpr dart::compiler::target::word ClassTable_kSizeOfClassPairLog2 =
     4;
 static constexpr dart::compiler::target::word
-    Instructions_kMonomorphicEntryOffset = 28;
+    Instructions_kMonomorphicEntryOffsetJIT = 8;
 static constexpr dart::compiler::target::word
-    Instructions_kPolymorphicEntryOffset = 8;
+    Instructions_kPolymorphicEntryOffsetJIT = 48;
+static constexpr dart::compiler::target::word
+    Instructions_kMonomorphicEntryOffsetAOT = 8;
+static constexpr dart::compiler::target::word
+    Instructions_kPolymorphicEntryOffsetAOT = 28;
 static constexpr dart::compiler::target::word HeapPage_kBytesPerCardLog2 = 10;
 static constexpr dart::compiler::target::word
     NativeEntry_kNumCallWrapperArguments = 2;
@@ -1228,7 +1247,7 @@
 static constexpr dart::compiler::target::word String_length_offset = 8;
 static constexpr dart::compiler::target::word SubtypeTestCache_cache_offset = 8;
 static constexpr dart::compiler::target::word
-    Thread_AllocateArray_entry_point_offset = 560;
+    Thread_AllocateArray_entry_point_offset = 552;
 static constexpr dart::compiler::target::word Thread_active_exception_offset =
     1320;
 static constexpr dart::compiler::target::word Thread_active_stacktrace_offset =
@@ -1236,97 +1255,98 @@
 static constexpr dart::compiler::target::word
     Thread_array_write_barrier_code_offset = 216;
 static constexpr dart::compiler::target::word
-    Thread_array_write_barrier_entry_point_offset = 384;
+    Thread_array_write_barrier_entry_point_offset = 376;
 static constexpr dart::compiler::target::word Thread_async_stack_trace_offset =
     168;
 static constexpr dart::compiler::target::word
-    Thread_auto_scope_native_wrapper_entry_point_offset = 480;
+    Thread_auto_scope_native_wrapper_entry_point_offset = 472;
 static constexpr dart::compiler::target::word Thread_bool_false_offset = 200;
 static constexpr dart::compiler::target::word Thread_bool_true_offset = 192;
 static constexpr dart::compiler::target::word
-    Thread_call_to_runtime_entry_point_offset = 392;
+    Thread_call_to_runtime_entry_point_offset = 384;
 static constexpr dart::compiler::target::word
     Thread_call_to_runtime_stub_offset = 256;
 static constexpr dart::compiler::target::word Thread_dart_stream_offset = 1384;
+static constexpr dart::compiler::target::word Thread_optimize_entry_offset =
+    440;
+static constexpr dart::compiler::target::word Thread_optimize_stub_offset = 304;
 static constexpr dart::compiler::target::word Thread_deoptimize_entry_offset =
-    456;
+    448;
 static constexpr dart::compiler::target::word Thread_deoptimize_stub_offset =
-    320;
+    312;
 static constexpr dart::compiler::target::word Thread_double_abs_address_offset =
-    520;
+    512;
 static constexpr dart::compiler::target::word
-    Thread_double_negate_address_offset = 512;
+    Thread_double_negate_address_offset = 504;
 static constexpr dart::compiler::target::word Thread_end_offset = 120;
 static constexpr dart::compiler::target::word
-    Thread_enter_safepoint_stub_offset = 360;
+    Thread_enter_safepoint_stub_offset = 352;
 static constexpr dart::compiler::target::word Thread_execution_state_offset =
     1352;
 static constexpr dart::compiler::target::word
-    Thread_exit_safepoint_stub_offset = 368;
+    Thread_exit_safepoint_stub_offset = 360;
 static constexpr dart::compiler::target::word
     Thread_fix_allocation_stub_code_offset = 232;
 static constexpr dart::compiler::target::word
     Thread_fix_callers_target_code_offset = 224;
 static constexpr dart::compiler::target::word
-    Thread_float_absolute_address_offset = 544;
+    Thread_float_absolute_address_offset = 536;
 static constexpr dart::compiler::target::word
-    Thread_float_negate_address_offset = 536;
+    Thread_float_negate_address_offset = 528;
 static constexpr dart::compiler::target::word Thread_float_not_address_offset =
-    528;
+    520;
 static constexpr dart::compiler::target::word
-    Thread_float_zerow_address_offset = 552;
+    Thread_float_zerow_address_offset = 544;
 static constexpr dart::compiler::target::word Thread_global_object_pool_offset =
     1336;
 static constexpr dart::compiler::target::word
-    Thread_ic_lookup_through_code_stub_offset = 304;
-static constexpr dart::compiler::target::word
-    Thread_interpret_call_entry_point_offset = 488;
+    Thread_interpret_call_entry_point_offset = 480;
 static constexpr dart::compiler::target::word
     Thread_invoke_dart_code_from_bytecode_stub_offset = 248;
 static constexpr dart::compiler::target::word
     Thread_invoke_dart_code_stub_offset = 240;
 static constexpr dart::compiler::target::word Thread_isolate_offset = 96;
 static constexpr dart::compiler::target::word
-    Thread_lazy_deopt_from_return_stub_offset = 328;
+    Thread_lazy_deopt_from_return_stub_offset = 320;
 static constexpr dart::compiler::target::word
-    Thread_lazy_deopt_from_throw_stub_offset = 336;
+    Thread_lazy_deopt_from_throw_stub_offset = 328;
 static constexpr dart::compiler::target::word
-    Thread_lazy_specialize_type_test_stub_offset = 352;
+    Thread_lazy_specialize_type_test_stub_offset = 344;
 static constexpr dart::compiler::target::word
     Thread_marking_stack_block_offset = 144;
 static constexpr dart::compiler::target::word
-    Thread_megamorphic_call_checked_entry_offset = 432;
+    Thread_megamorphic_call_checked_entry_offset = 424;
 static constexpr dart::compiler::target::word
-    Thread_monomorphic_miss_entry_offset = 440;
+    Thread_monomorphic_miss_entry_offset = 432;
 static constexpr dart::compiler::target::word
     Thread_monomorphic_miss_stub_offset = 296;
 static constexpr dart::compiler::target::word
-    Thread_no_scope_native_wrapper_entry_point_offset = 472;
+    Thread_no_scope_native_wrapper_entry_point_offset = 464;
 static constexpr dart::compiler::target::word
-    Thread_null_error_shared_with_fpu_regs_entry_point_offset = 408;
+    Thread_null_error_shared_with_fpu_regs_entry_point_offset = 400;
 static constexpr dart::compiler::target::word
     Thread_null_error_shared_with_fpu_regs_stub_offset = 272;
 static constexpr dart::compiler::target::word
-    Thread_null_error_shared_without_fpu_regs_entry_point_offset = 400;
+    Thread_null_error_shared_without_fpu_regs_entry_point_offset = 392;
 static constexpr dart::compiler::target::word
     Thread_null_error_shared_without_fpu_regs_stub_offset = 264;
 static constexpr dart::compiler::target::word Thread_object_null_offset = 184;
 static constexpr dart::compiler::target::word
-    Thread_predefined_symbols_address_offset = 496;
+    Thread_predefined_symbols_address_offset = 488;
 static constexpr dart::compiler::target::word Thread_resume_pc_offset = 1344;
 static constexpr dart::compiler::target::word Thread_safepoint_state_offset =
     1360;
 static constexpr dart::compiler::target::word
-    Thread_slow_type_test_stub_offset = 344;
+    Thread_slow_type_test_stub_offset = 336;
 static constexpr dart::compiler::target::word Thread_stack_limit_offset = 72;
 static constexpr dart::compiler::target::word
     Thread_stack_overflow_flags_offset = 80;
 static constexpr dart::compiler::target::word
-    Thread_stack_overflow_shared_with_fpu_regs_entry_point_offset = 424;
+    Thread_stack_overflow_shared_with_fpu_regs_entry_point_offset = 416;
 static constexpr dart::compiler::target::word
     Thread_stack_overflow_shared_with_fpu_regs_stub_offset = 288;
 static constexpr dart::compiler::target::word
-    Thread_stack_overflow_shared_without_fpu_regs_entry_point_offset = 416;
+    Thread_stack_overflow_shared_without_fpu_regs_entry_point_offset = 408;
 static constexpr dart::compiler::target::word
     Thread_stack_overflow_shared_without_fpu_regs_stub_offset = 280;
 static constexpr dart::compiler::target::word Thread_store_buffer_block_offset =
@@ -1341,11 +1361,11 @@
 static constexpr dart::compiler::target::word Thread_write_barrier_code_offset =
     208;
 static constexpr dart::compiler::target::word
-    Thread_write_barrier_entry_point_offset = 376;
+    Thread_write_barrier_entry_point_offset = 368;
 static constexpr dart::compiler::target::word Thread_write_barrier_mask_offset =
     88;
 static constexpr dart::compiler::target::word
-    Thread_verify_callback_entry_offset = 464;
+    Thread_verify_callback_entry_offset = 456;
 static constexpr dart::compiler::target::word Thread_callback_code_offset =
     1368;
 static constexpr dart::compiler::target::word TimelineStream_enabled_offset =
@@ -1417,9 +1437,13 @@
 static constexpr dart::compiler::target::word ClassTable_kSizeOfClassPairLog2 =
     4;
 static constexpr dart::compiler::target::word
-    Instructions_kMonomorphicEntryOffset = 0;
+    Instructions_kMonomorphicEntryOffsetJIT = 0;
 static constexpr dart::compiler::target::word
-    Instructions_kPolymorphicEntryOffset = 0;
+    Instructions_kPolymorphicEntryOffsetJIT = 0;
+static constexpr dart::compiler::target::word
+    Instructions_kMonomorphicEntryOffsetAOT = 0;
+static constexpr dart::compiler::target::word
+    Instructions_kPolymorphicEntryOffsetAOT = 0;
 static constexpr dart::compiler::target::word HeapPage_kBytesPerCardLog2 = 10;
 static constexpr dart::compiler::target::word
     NativeEntry_kNumCallWrapperArguments = 2;
@@ -1582,23 +1606,23 @@
 static constexpr dart::compiler::target::word
     Thread_AllocateArray_entry_point_offset = 296;
 static constexpr dart::compiler::target::word Thread_active_exception_offset =
-    880;
-static constexpr dart::compiler::target::word Thread_active_stacktrace_offset =
     888;
+static constexpr dart::compiler::target::word Thread_active_stacktrace_offset =
+    896;
 static constexpr dart::compiler::target::word Thread_async_stack_trace_offset =
     168;
 static constexpr dart::compiler::target::word
     Thread_auto_scope_native_wrapper_entry_point_offset = 216;
 static constexpr dart::compiler::target::word Thread_bool_false_offset = 200;
 static constexpr dart::compiler::target::word Thread_bool_true_offset = 192;
-static constexpr dart::compiler::target::word Thread_dart_stream_offset = 944;
+static constexpr dart::compiler::target::word Thread_dart_stream_offset = 952;
 static constexpr dart::compiler::target::word Thread_double_abs_address_offset =
     256;
 static constexpr dart::compiler::target::word
     Thread_double_negate_address_offset = 248;
 static constexpr dart::compiler::target::word Thread_end_offset = 120;
 static constexpr dart::compiler::target::word Thread_execution_state_offset =
-    912;
+    920;
 static constexpr dart::compiler::target::word
     Thread_float_absolute_address_offset = 280;
 static constexpr dart::compiler::target::word
@@ -1608,7 +1632,7 @@
 static constexpr dart::compiler::target::word
     Thread_float_zerow_address_offset = 288;
 static constexpr dart::compiler::target::word Thread_global_object_pool_offset =
-    896;
+    904;
 static constexpr dart::compiler::target::word Thread_isolate_offset = 96;
 static constexpr dart::compiler::target::word
     Thread_marking_stack_block_offset = 144;
@@ -1617,9 +1641,9 @@
 static constexpr dart::compiler::target::word Thread_object_null_offset = 184;
 static constexpr dart::compiler::target::word
     Thread_predefined_symbols_address_offset = 232;
-static constexpr dart::compiler::target::word Thread_resume_pc_offset = 904;
+static constexpr dart::compiler::target::word Thread_resume_pc_offset = 912;
 static constexpr dart::compiler::target::word Thread_safepoint_state_offset =
-    920;
+    928;
 static constexpr dart::compiler::target::word Thread_stack_limit_offset = 72;
 static constexpr dart::compiler::target::word
     Thread_stack_overflow_flags_offset = 80;
@@ -1634,7 +1658,7 @@
 static constexpr dart::compiler::target::word Thread_vm_tag_offset = 160;
 static constexpr dart::compiler::target::word Thread_write_barrier_mask_offset =
     88;
-static constexpr dart::compiler::target::word Thread_callback_code_offset = 928;
+static constexpr dart::compiler::target::word Thread_callback_code_offset = 936;
 static constexpr dart::compiler::target::word TimelineStream_enabled_offset =
     16;
 static constexpr dart::compiler::target::word TwoByteString_data_offset = 16;
@@ -1698,9 +1722,13 @@
 static constexpr dart::compiler::target::word ClassTable_kSizeOfClassPairLog2 =
     3;
 static constexpr dart::compiler::target::word
-    Instructions_kMonomorphicEntryOffset = 0;
+    Instructions_kMonomorphicEntryOffsetJIT = 0;
 static constexpr dart::compiler::target::word
-    Instructions_kPolymorphicEntryOffset = 0;
+    Instructions_kPolymorphicEntryOffsetJIT = 0;
+static constexpr dart::compiler::target::word
+    Instructions_kMonomorphicEntryOffsetAOT = 0;
+static constexpr dart::compiler::target::word
+    Instructions_kPolymorphicEntryOffsetAOT = 0;
 static constexpr dart::compiler::target::word HeapPage_kBytesPerCardLog2 = 9;
 static constexpr dart::compiler::target::word
     NativeEntry_kNumCallWrapperArguments = 2;
@@ -1862,23 +1890,23 @@
 static constexpr dart::compiler::target::word
     Thread_AllocateArray_entry_point_offset = 152;
 static constexpr dart::compiler::target::word Thread_active_exception_offset =
-    444;
-static constexpr dart::compiler::target::word Thread_active_stacktrace_offset =
     448;
+static constexpr dart::compiler::target::word Thread_active_stacktrace_offset =
+    452;
 static constexpr dart::compiler::target::word Thread_async_stack_trace_offset =
     84;
 static constexpr dart::compiler::target::word
     Thread_auto_scope_native_wrapper_entry_point_offset = 112;
 static constexpr dart::compiler::target::word Thread_bool_false_offset = 104;
 static constexpr dart::compiler::target::word Thread_bool_true_offset = 100;
-static constexpr dart::compiler::target::word Thread_dart_stream_offset = 476;
+static constexpr dart::compiler::target::word Thread_dart_stream_offset = 480;
 static constexpr dart::compiler::target::word Thread_double_abs_address_offset =
     132;
 static constexpr dart::compiler::target::word
     Thread_double_negate_address_offset = 128;
 static constexpr dart::compiler::target::word Thread_end_offset = 60;
 static constexpr dart::compiler::target::word Thread_execution_state_offset =
-    460;
+    464;
 static constexpr dart::compiler::target::word
     Thread_float_absolute_address_offset = 144;
 static constexpr dart::compiler::target::word
@@ -1888,7 +1916,7 @@
 static constexpr dart::compiler::target::word
     Thread_float_zerow_address_offset = 148;
 static constexpr dart::compiler::target::word Thread_global_object_pool_offset =
-    452;
+    456;
 static constexpr dart::compiler::target::word Thread_isolate_offset = 48;
 static constexpr dart::compiler::target::word
     Thread_marking_stack_block_offset = 72;
@@ -1897,9 +1925,9 @@
 static constexpr dart::compiler::target::word Thread_object_null_offset = 96;
 static constexpr dart::compiler::target::word
     Thread_predefined_symbols_address_offset = 120;
-static constexpr dart::compiler::target::word Thread_resume_pc_offset = 456;
+static constexpr dart::compiler::target::word Thread_resume_pc_offset = 460;
 static constexpr dart::compiler::target::word Thread_safepoint_state_offset =
-    464;
+    468;
 static constexpr dart::compiler::target::word Thread_stack_limit_offset = 36;
 static constexpr dart::compiler::target::word
     Thread_stack_overflow_flags_offset = 40;
@@ -1914,7 +1942,7 @@
 static constexpr dart::compiler::target::word Thread_vm_tag_offset = 80;
 static constexpr dart::compiler::target::word Thread_write_barrier_mask_offset =
     44;
-static constexpr dart::compiler::target::word Thread_callback_code_offset = 468;
+static constexpr dart::compiler::target::word Thread_callback_code_offset = 472;
 static constexpr dart::compiler::target::word TimelineStream_enabled_offset = 8;
 static constexpr dart::compiler::target::word TwoByteString_data_offset = 12;
 static constexpr dart::compiler::target::word Type_arguments_offset = 16;
diff --git a/runtime/vm/compiler/runtime_offsets_list.h b/runtime/vm/compiler/runtime_offsets_list.h
index 007773a..74264ca 100644
--- a/runtime/vm/compiler/runtime_offsets_list.h
+++ b/runtime/vm/compiler/runtime_offsets_list.h
@@ -35,8 +35,10 @@
   CONSTANT(Array, kMaxElements)                                                \
   CONSTANT(Array, kMaxNewSpaceElements)                                        \
   CONSTANT(ClassTable, kSizeOfClassPairLog2)                                   \
-  CONSTANT(Instructions, kMonomorphicEntryOffset)                              \
-  CONSTANT(Instructions, kPolymorphicEntryOffset)                              \
+  CONSTANT(Instructions, kMonomorphicEntryOffsetJIT)                           \
+  CONSTANT(Instructions, kPolymorphicEntryOffsetJIT)                           \
+  CONSTANT(Instructions, kMonomorphicEntryOffsetAOT)                           \
+  CONSTANT(Instructions, kPolymorphicEntryOffsetAOT)                           \
   CONSTANT(HeapPage, kBytesPerCardLog2)                                        \
   CONSTANT(NativeEntry, kNumCallWrapperArguments)                              \
   CONSTANT(String, kMaxElements)                                               \
@@ -155,6 +157,8 @@
   NOT_IN_DBC(FIELD(Thread, call_to_runtime_entry_point_offset))                \
   NOT_IN_DBC(FIELD(Thread, call_to_runtime_stub_offset))                       \
   FIELD(Thread, dart_stream_offset)                                            \
+  NOT_IN_DBC(FIELD(Thread, optimize_entry_offset))                             \
+  NOT_IN_DBC(FIELD(Thread, optimize_stub_offset))                              \
   NOT_IN_DBC(FIELD(Thread, deoptimize_entry_offset))                           \
   NOT_IN_DBC(FIELD(Thread, deoptimize_stub_offset))                            \
   FIELD(Thread, double_abs_address_offset)                                     \
@@ -170,7 +174,6 @@
   FIELD(Thread, float_not_address_offset)                                      \
   FIELD(Thread, float_zerow_address_offset)                                    \
   FIELD(Thread, global_object_pool_offset)                                     \
-  NOT_IN_DBC(FIELD(Thread, ic_lookup_through_code_stub_offset))                \
   NOT_IN_DBC(FIELD(Thread, interpret_call_entry_point_offset))                 \
   NOT_IN_DBC(FIELD(Thread, invoke_dart_code_from_bytecode_stub_offset))        \
   NOT_IN_DBC(FIELD(Thread, invoke_dart_code_stub_offset))                      \
@@ -235,9 +238,8 @@
   NOT_IN_PRODUCT(ARRAY_STRUCTFIELD(                                            \
       ClassTable, NewSpaceCounterOffsetFor, ClassOffsetFor,                    \
       ClassHeapStats::allocated_since_gc_new_space_offset()))                  \
-  NOT_IN_PRODUCT(ARRAY_STRUCTFIELD(                                            \
-      ClassTable, StateOffsetFor, ClassOffsetFor,                              \
-      ClassHeapStats::allocated_since_gc_new_space_offset()))                  \
+  NOT_IN_PRODUCT(ARRAY_STRUCTFIELD(ClassTable, StateOffsetFor, ClassOffsetFor, \
+                                   ClassHeapStats::state_offset()))            \
   NOT_IN_PRODUCT(ARRAY_STRUCTFIELD(                                            \
       ClassTable, NewSpaceSizeOffsetFor, ClassOffsetFor,                       \
       ClassHeapStats::allocated_size_since_gc_new_space_offset()))             \
diff --git a/runtime/vm/compiler/stub_code_compiler_arm.cc b/runtime/vm/compiler/stub_code_compiler_arm.cc
index bdf0ec4..dd3b49f 100644
--- a/runtime/vm/compiler/stub_code_compiler_arm.cc
+++ b/runtime/vm/compiler/stub_code_compiler_arm.cc
@@ -574,6 +574,9 @@
 // (invalid because its function was optimized or deoptimized).
 // R4: arguments descriptor array.
 void StubCodeCompiler::GenerateFixCallersTargetStub(Assembler* assembler) {
+  Label monomorphic;
+  __ BranchOnMonomorphicCheckedEntryJIT(&monomorphic);
+
   // Load code pointer to this stub from the thread:
   // The one that is passed in, is not correct - it points to the code object
   // that needs to be replaced.
@@ -593,6 +596,29 @@
   // Jump to the dart function.
   __ mov(CODE_REG, Operand(R0));
   __ Branch(FieldAddress(R0, target::Code::entry_point_offset()));
+
+  __ Bind(&monomorphic);
+  // Load code pointer to this stub from the thread:
+  // The one that is passed in, is not correct - it points to the code object
+  // that needs to be replaced.
+  __ ldr(CODE_REG,
+         Address(THR, target::Thread::fix_callers_target_code_offset()));
+  // Create a stub frame as we are pushing some objects on the stack before
+  // calling into the runtime.
+  __ EnterStubFrame();
+  __ LoadImmediate(R1, 0);
+  __ Push(R9);  // Preserve cache (guarded CID as Smi).
+  __ Push(R0);  // Preserve receiver.
+  __ Push(R1);
+  __ CallRuntime(kFixCallersTargetMonomorphicRuntimeEntry, 0);
+  __ Pop(CODE_REG);
+  __ Pop(R0);  // Restore receiver.
+  __ Pop(R9);  // Restore cache (guarded CID as Smi).
+  // Remove the stub frame.
+  __ LeaveStubFrame();
+  // Jump to the dart function.
+  __ Branch(FieldAddress(
+      CODE_REG, target::Code::entry_point_offset(CodeEntryKind::kMonomorphic)));
 }
 
 // Called from object allocate instruction when the allocation stub has been
@@ -2414,6 +2440,22 @@
 #endif  // defined(PRODUCT)
 }
 
+void StubCodeCompiler::GenerateUnoptStaticCallBreakpointStub(
+    Assembler* assembler) {
+#if defined(PRODUCT)
+  __ Stop("No debugging in PRODUCT mode");
+#else
+  __ EnterStubFrame();
+  __ Push(R9);          // Preserve IC data.
+  __ PushImmediate(0);  // Space for result.
+  __ CallRuntime(kBreakpointRuntimeHandlerRuntimeEntry, 0);
+  __ Pop(CODE_REG);  // Original stub.
+  __ Pop(R9);        // Restore IC data.
+  __ LeaveStubFrame();
+  __ Branch(FieldAddress(CODE_REG, target::Code::entry_point_offset()));
+#endif  // defined(PRODUCT)
+}
+
 void StubCodeCompiler::GenerateRuntimeCallBreakpointStub(Assembler* assembler) {
 #if defined(PRODUCT)
   __ Stop("No debugging in PRODUCT mode");
@@ -2918,7 +2960,7 @@
 // R8: function to be reoptimized.
 // R4: argument descriptor (preserved).
 void StubCodeCompiler::GenerateOptimizeFunctionStub(Assembler* assembler) {
-  __ ldr(CODE_REG, Address(THR, Thread::optimize_stub_offset()));
+  __ ldr(CODE_REG, Address(THR, target::Thread::optimize_stub_offset()));
   __ EnterStubFrame();
   __ Push(R4);
   __ LoadImmediate(IP, 0);
@@ -3196,17 +3238,17 @@
 
   __ LoadImmediate(IP, 0);
   __ Push(IP);  // Result slot
-  __ Push(R0);  // Arg0: Receiver
-  __ Push(R9);  // Arg1: UnlinkedCall
-  __ CallRuntime(kUnlinkedCallRuntimeEntry, 2);
+  __ Push(IP);  // Arg0: stub out
+  __ Push(R0);  // Arg1: Receiver
+  __ Push(R9);  // Arg2: UnlinkedCall
+  __ CallRuntime(kUnlinkedCallRuntimeEntry, 3);
   __ Drop(2);
+  __ Pop(CODE_REG);  // result = stub
   __ Pop(R9);  // result = IC
 
   __ Pop(R0);  // Restore receiver.
   __ LeaveStubFrame();
 
-  __ ldr(CODE_REG,
-         Address(THR, target::Thread::ic_lookup_through_code_stub_offset()));
   __ Branch(FieldAddress(
       CODE_REG, target::Code::entry_point_offset(CodeEntryKind::kMonomorphic)));
 }
@@ -3239,16 +3281,16 @@
 
   __ LoadImmediate(IP, 0);
   __ Push(IP);  // Result slot
-  __ Push(R0);  // Arg0: Receiver
-  __ CallRuntime(kSingleTargetMissRuntimeEntry, 1);
+  __ Push(IP);  // Arg0: stub out
+  __ Push(R0);  // Arg1: Receiver
+  __ CallRuntime(kSingleTargetMissRuntimeEntry, 2);
   __ Drop(1);
+  __ Pop(CODE_REG);  // result = stub
   __ Pop(R9);  // result = IC
 
   __ Pop(R0);  // Restore receiver.
   __ LeaveStubFrame();
 
-  __ ldr(CODE_REG,
-         Address(THR, target::Thread::ic_lookup_through_code_stub_offset()));
   __ Branch(FieldAddress(
       CODE_REG, target::Code::entry_point_offset(CodeEntryKind::kMonomorphic)));
 }
@@ -3263,16 +3305,16 @@
 
   __ LoadImmediate(IP, 0);
   __ Push(IP);  // Result slot
-  __ Push(R0);  // Arg0: Receiver
-  __ CallRuntime(kMonomorphicMissRuntimeEntry, 1);
+  __ Push(IP);  // Arg0: stub out
+  __ Push(R0);  // Arg1: Receiver
+  __ CallRuntime(kMonomorphicMissRuntimeEntry, 2);
   __ Drop(1);
+  __ Pop(CODE_REG);  // result = stub
   __ Pop(R9);  // result = IC
 
   __ Pop(R0);  // Restore receiver.
   __ LeaveStubFrame();
 
-  __ ldr(CODE_REG,
-         Address(THR, target::Thread::ic_lookup_through_code_stub_offset()));
   __ Branch(FieldAddress(
       CODE_REG, target::Code::entry_point_offset(CodeEntryKind::kMonomorphic)));
 }
diff --git a/runtime/vm/compiler/stub_code_compiler_arm64.cc b/runtime/vm/compiler/stub_code_compiler_arm64.cc
index 31e9286..2f3b1b0 100644
--- a/runtime/vm/compiler/stub_code_compiler_arm64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_arm64.cc
@@ -208,34 +208,40 @@
 void StubCodeCompiler::GenerateEnterSafepointStub(Assembler* assembler) {
   RegisterSet all_registers;
   all_registers.AddAllGeneralRegisters();
-  __ PushRegisters(all_registers);
 
   __ EnterFrame(0);
+  __ PushRegisters(all_registers);
+
+  __ mov(CALLEE_SAVED_TEMP, SP);
   __ ReserveAlignedFrameSpace(0);
+
   __ mov(CSP, SP);
   __ ldr(R0, Address(THR, kEnterSafepointRuntimeEntry.OffsetFromThread()));
   __ blr(R0);
-  __ LeaveFrame();
+  __ mov(SP, CALLEE_SAVED_TEMP);
 
   __ PopRegisters(all_registers);
-  __ mov(CSP, SP);
+  __ LeaveFrame();
   __ Ret();
 }
 
 void StubCodeCompiler::GenerateExitSafepointStub(Assembler* assembler) {
   RegisterSet all_registers;
   all_registers.AddAllGeneralRegisters();
-  __ PushRegisters(all_registers);
 
   __ EnterFrame(0);
+  __ PushRegisters(all_registers);
+
+  __ mov(CALLEE_SAVED_TEMP, SP);
   __ ReserveAlignedFrameSpace(0);
+
   __ mov(CSP, SP);
   __ ldr(R0, Address(THR, kExitSafepointRuntimeEntry.OffsetFromThread()));
   __ blr(R0);
-  __ LeaveFrame();
+  __ mov(SP, CALLEE_SAVED_TEMP);
 
   __ PopRegisters(all_registers);
-  __ mov(CSP, SP);
+  __ LeaveFrame();
   __ Ret();
 }
 
@@ -618,6 +624,9 @@
 // (invalid because its function was optimized or deoptimized).
 // R4: arguments descriptor array.
 void StubCodeCompiler::GenerateFixCallersTargetStub(Assembler* assembler) {
+  Label monomorphic;
+  __ BranchOnMonomorphicCheckedEntryJIT(&monomorphic);
+
   // Load code pointer to this stub from the thread:
   // The one that is passed in, is not correct - it points to the code object
   // that needs to be replaced.
@@ -638,6 +647,30 @@
   // Jump to the dart function.
   __ LoadFieldFromOffset(R0, CODE_REG, target::Code::entry_point_offset());
   __ br(R0);
+
+  __ Bind(&monomorphic);
+  // Load code pointer to this stub from the thread:
+  // The one that is passed in, is not correct - it points to the code object
+  // that needs to be replaced.
+  __ ldr(CODE_REG,
+         Address(THR, target::Thread::fix_callers_target_code_offset()));
+  // Create a stub frame as we are pushing some objects on the stack before
+  // calling into the runtime.
+  __ EnterStubFrame();
+  __ Push(R5);  // Preserve cache (guarded CID as Smi).
+  __ Push(R0);  // Preserve receiver.
+  __ Push(ZR);
+  __ CallRuntime(kFixCallersTargetMonomorphicRuntimeEntry, 0);
+  __ Pop(CODE_REG);
+  __ Pop(R0);  // Restore receiver.
+  __ Pop(R5);  // Restore cache (guarded CID as Smi).
+  // Remove the stub frame.
+  __ LeaveStubFrame();
+  // Jump to the dart function.
+  __ LoadFieldFromOffset(
+      R1, CODE_REG,
+      target::Code::entry_point_offset(CodeEntryKind::kMonomorphic));
+  __ br(R1);
 }
 
 // Called from object allocate instruction when the allocation stub has been
@@ -2489,6 +2522,23 @@
 #endif  // defined(PRODUCT)
 }
 
+void StubCodeCompiler::GenerateUnoptStaticCallBreakpointStub(
+    Assembler* assembler) {
+#if defined(PRODUCT)
+  __ Stop("No debugging in PRODUCT mode");
+#else
+  __ EnterStubFrame();
+  __ Push(R5);  // Preserve IC data.
+  __ Push(ZR);  // Space for result.
+  __ CallRuntime(kBreakpointRuntimeHandlerRuntimeEntry, 0);
+  __ Pop(CODE_REG);  // Original stub.
+  __ Pop(R5);        // Restore IC data.
+  __ LeaveStubFrame();
+  __ LoadFieldFromOffset(TMP, CODE_REG, target::Code::entry_point_offset());
+  __ br(TMP);
+#endif  // defined(PRODUCT)
+}
+
 void StubCodeCompiler::GenerateRuntimeCallBreakpointStub(Assembler* assembler) {
 #if defined(PRODUCT)
   __ Stop("No debugging in PRODUCT mode");
@@ -2988,7 +3038,7 @@
 // R6: function to be re-optimized.
 // R4: argument descriptor (preserved).
 void StubCodeCompiler::GenerateOptimizeFunctionStub(Assembler* assembler) {
-  __ LoadFromOffset(CODE_REG, THR, Thread::optimize_stub_offset());
+  __ LoadFromOffset(CODE_REG, THR, target::Thread::optimize_stub_offset());
   __ EnterStubFrame();
   __ Push(R4);
   // Setup space on stack for the return value.
@@ -3265,17 +3315,17 @@
   __ Push(R0);  // Preserve receiver.
 
   __ Push(ZR);  // Result slot.
-  __ Push(R0);  // Arg0: Receiver
-  __ Push(R5);  // Arg1: UnlinkedCall
-  __ CallRuntime(kUnlinkedCallRuntimeEntry, 2);
+  __ Push(ZR);  // Arg0: stub out.
+  __ Push(R0);  // Arg1: Receiver
+  __ Push(R5);  // Arg2: UnlinkedCall
+  __ CallRuntime(kUnlinkedCallRuntimeEntry, 3);
   __ Drop(2);
+  __ Pop(CODE_REG);  // result = stub
   __ Pop(R5);  // result = IC
 
   __ Pop(R0);  // Restore receiver.
   __ LeaveStubFrame();
 
-  __ ldr(CODE_REG,
-         Address(THR, target::Thread::ic_lookup_through_code_stub_offset()));
   __ ldr(R1, FieldAddress(CODE_REG, target::Code::entry_point_offset(
                                         CodeEntryKind::kMonomorphic)));
   __ br(R1);
@@ -3309,16 +3359,16 @@
   __ Push(R0);  // Preserve receiver.
 
   __ Push(ZR);  // Result slot.
-  __ Push(R0);  // Arg0: Receiver
-  __ CallRuntime(kSingleTargetMissRuntimeEntry, 1);
+  __ Push(ZR);  // Arg0: Stub out.
+  __ Push(R0);  // Arg1: Receiver
+  __ CallRuntime(kSingleTargetMissRuntimeEntry, 2);
   __ Drop(1);
+  __ Pop(CODE_REG);  // result = stub
   __ Pop(R5);  // result = IC
 
   __ Pop(R0);  // Restore receiver.
   __ LeaveStubFrame();
 
-  __ ldr(CODE_REG,
-         Address(THR, target::Thread::ic_lookup_through_code_stub_offset()));
   __ ldr(R1, FieldAddress(CODE_REG, target::Code::entry_point_offset(
                                         CodeEntryKind::kMonomorphic)));
   __ br(R1);
@@ -3333,16 +3383,16 @@
   __ Push(R0);  // Preserve receiver.
 
   __ Push(ZR);  // Result slot.
-  __ Push(R0);  // Arg0: Receiver
-  __ CallRuntime(kMonomorphicMissRuntimeEntry, 1);
+  __ Push(ZR);  // Arg0: stub out
+  __ Push(R0);  // Arg1: Receiver
+  __ CallRuntime(kMonomorphicMissRuntimeEntry, 2);
   __ Drop(1);
+  __ Pop(CODE_REG);  // result = stub
   __ Pop(R5);  // result = IC
 
   __ Pop(R0);  // Restore receiver.
   __ LeaveStubFrame();
 
-  __ ldr(CODE_REG,
-         Address(THR, target::Thread::ic_lookup_through_code_stub_offset()));
   __ ldr(R1, FieldAddress(CODE_REG, target::Code::entry_point_offset(
                                         CodeEntryKind::kMonomorphic)));
   __ br(R1);
diff --git a/runtime/vm/compiler/stub_code_compiler_ia32.cc b/runtime/vm/compiler/stub_code_compiler_ia32.cc
index 8e59855..bb5aeccd 100644
--- a/runtime/vm/compiler/stub_code_compiler_ia32.cc
+++ b/runtime/vm/compiler/stub_code_compiler_ia32.cc
@@ -407,8 +407,10 @@
 // (invalid because its function was optimized or deoptimized).
 // EDX: arguments descriptor array.
 void StubCodeCompiler::GenerateFixCallersTargetStub(Assembler* assembler) {
-  // Create a stub frame as we are pushing some objects on the stack before
-  // calling into the runtime.
+  Label monomorphic;
+  __ BranchOnMonomorphicCheckedEntryJIT(&monomorphic);
+
+  // This was a static call.
   __ EnterStubFrame();
   __ pushl(EDX);           // Preserve arguments descriptor array.
   __ pushl(Immediate(0));  // Setup space on stack for return value.
@@ -419,6 +421,22 @@
   __ LeaveFrame();
   __ jmp(EAX);
   __ int3();
+
+  __ Bind(&monomorphic);
+  // This was a switchable call.
+  __ EnterStubFrame();
+  __ pushl(ECX);           // Preserve cache (guarded CID as Smi).
+  __ pushl(EBX);           // Preserve receiver.
+  __ pushl(Immediate(0));  // Result slot.
+  __ CallRuntime(kFixCallersTargetMonomorphicRuntimeEntry, 0);
+  __ popl(CODE_REG);  // Get Code object.
+  __ popl(EBX);       // Restore receiver.
+  __ popl(ECX);       // Restore cache (guarded CID as Smi).
+  __ movl(EAX, FieldAddress(CODE_REG, target::Code::entry_point_offset(
+                                          CodeEntryKind::kMonomorphic)));
+  __ LeaveFrame();
+  __ jmp(EAX);
+  __ int3();
 }
 
 // Called from object allocate instruction when the allocation stub has been
@@ -2119,6 +2137,23 @@
 #endif  // defined(PRODUCT)
 }
 
+void StubCodeCompiler::GenerateUnoptStaticCallBreakpointStub(
+    Assembler* assembler) {
+#if defined(PRODUCT)
+  __ Stop("No debugging in PRODUCT mode");
+#else
+  __ EnterStubFrame();
+  __ pushl(ECX);           // Preserve ICData.
+  __ pushl(Immediate(0));  // Room for result.
+  __ CallRuntime(kBreakpointRuntimeHandlerRuntimeEntry, 0);
+  __ popl(EAX);  // Code of original stub.
+  __ popl(ECX);  // Restore ICData.
+  __ LeaveFrame();
+  // Jump to original stub.
+  __ jmp(FieldAddress(EAX, target::Code::entry_point_offset()));
+#endif  // defined(PRODUCT)
+}
+
 void StubCodeCompiler::GenerateRuntimeCallBreakpointStub(Assembler* assembler) {
 #if defined(PRODUCT)
   __ Stop("No debugging in PRODUCT mode");
@@ -2449,7 +2484,7 @@
 // EBX: function to be reoptimized.
 // EDX: argument descriptor (preserved).
 void StubCodeCompiler::GenerateOptimizeFunctionStub(Assembler* assembler) {
-  __ movl(CODE_REG, Address(THR, Thread::optimize_stub_offset()));
+  __ movl(CODE_REG, Address(THR, target::Thread::optimize_stub_offset()));
   __ EnterStubFrame();
   __ pushl(EDX);
   __ pushl(Immediate(0));  // Setup space on stack for return value.
@@ -2661,7 +2696,23 @@
 }
 
 void StubCodeCompiler::GenerateMonomorphicMissStub(Assembler* assembler) {
-  __ int3();  // AOT only.
+  __ EnterStubFrame();
+  __ pushl(EBX);  // Preserve receiver.
+
+  __ pushl(Immediate(0));  // Result slot.
+  __ pushl(Immediate(0));  // Arg0: stub out
+  __ pushl(EBX);           // Arg1: Receiver
+  __ CallRuntime(kMonomorphicMissRuntimeEntry, 2);
+  __ popl(ECX);
+  __ popl(CODE_REG);  // result = stub
+  __ popl(ECX);       // result = IC
+
+  __ popl(EBX);  // Restore receiver.
+  __ LeaveFrame();
+
+  __ movl(EAX, FieldAddress(CODE_REG, target::Code::entry_point_offset(
+                                          CodeEntryKind::kMonomorphic)));
+  __ jmp(EAX);
 }
 
 void StubCodeCompiler::GenerateFrameAwaitingMaterializationStub(
diff --git a/runtime/vm/compiler/stub_code_compiler_x64.cc b/runtime/vm/compiler/stub_code_compiler_x64.cc
index 77e894f..414bedb 100644
--- a/runtime/vm/compiler/stub_code_compiler_x64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_x64.cc
@@ -571,6 +571,10 @@
 // (invalid because its function was optimized or deoptimized).
 // R10: arguments descriptor array.
 void StubCodeCompiler::GenerateFixCallersTargetStub(Assembler* assembler) {
+  Label monomorphic;
+  __ BranchOnMonomorphicCheckedEntryJIT(&monomorphic);
+
+  // This was a static call.
   // Load code pointer to this stub from the thread:
   // The one that is passed in, is not correct - it points to the code object
   // that needs to be replaced.
@@ -587,6 +591,27 @@
   __ LeaveStubFrame();
   __ jmp(RAX);
   __ int3();
+
+  __ Bind(&monomorphic);
+  // This was a switchable call.
+  // Load code pointer to this stub from the thread:
+  // The one that is passed in, is not correct - it points to the code object
+  // that needs to be replaced.
+  __ movq(CODE_REG,
+          Address(THR, target::Thread::fix_callers_target_code_offset()));
+  __ EnterStubFrame();
+  __ pushq(RBX);           // Preserve cache (guarded CID as Smi).
+  __ pushq(RDX);           // Preserve receiver.
+  __ pushq(Immediate(0));  // Result slot.
+  __ CallRuntime(kFixCallersTargetMonomorphicRuntimeEntry, 0);
+  __ popq(CODE_REG);  // Get Code object.
+  __ popq(RDX);       // Restore receiver.
+  __ popq(RBX);       // Restore cache (guarded CID as Smi).
+  __ movq(RAX, FieldAddress(CODE_REG, target::Code::entry_point_offset(
+                                          CodeEntryKind::kMonomorphic)));
+  __ LeaveStubFrame();
+  __ jmp(RAX);
+  __ int3();
 }
 
 // Called from object allocate instruction when the allocation stub has been
@@ -2500,6 +2525,26 @@
 #endif  // defined(PRODUCT)
 }
 
+void StubCodeCompiler::GenerateUnoptStaticCallBreakpointStub(
+    Assembler* assembler) {
+#if defined(PRODUCT)
+  __ Stop("No debugging in PRODUCT mode");
+#else
+  __ EnterStubFrame();
+  __ pushq(RDX);           // Preserve receiver.
+  __ pushq(RBX);           // Preserve IC data.
+  __ pushq(Immediate(0));  // Result slot.
+  __ CallRuntime(kBreakpointRuntimeHandlerRuntimeEntry, 0);
+  __ popq(CODE_REG);  // Original stub.
+  __ popq(RBX);       // Restore IC data.
+  __ popq(RDX);       // Restore receiver.
+  __ LeaveStubFrame();
+
+  __ movq(RAX, FieldAddress(CODE_REG, target::Code::entry_point_offset()));
+  __ jmp(RAX);  // Jump to original stub.
+#endif  // defined(PRODUCT)
+}
+
 //  TOS(0): return address (Dart code).
 void StubCodeCompiler::GenerateRuntimeCallBreakpointStub(Assembler* assembler) {
 #if defined(PRODUCT)
@@ -3001,7 +3046,7 @@
 // RDI: function to be reoptimized.
 // R10: argument descriptor (preserved).
 void StubCodeCompiler::GenerateOptimizeFunctionStub(Assembler* assembler) {
-  __ movq(CODE_REG, Address(THR, Thread::optimize_stub_offset()));
+  __ movq(CODE_REG, Address(THR, target::Thread::optimize_stub_offset()));
   __ EnterStubFrame();
   __ pushq(R10);           // Preserve args descriptor.
   __ pushq(Immediate(0));  // Result slot.
@@ -3284,18 +3329,18 @@
   __ pushq(RDX);  // Preserve receiver.
 
   __ pushq(Immediate(0));  // Result slot.
-  __ pushq(RDX);           // Arg0: Receiver
-  __ pushq(RBX);           // Arg1: UnlinkedCall
-  __ CallRuntime(kUnlinkedCallRuntimeEntry, 2);
+  __ pushq(Immediate(0));  // Arg0: stub out.
+  __ pushq(RDX);           // Arg1: Receiver
+  __ pushq(RBX);           // Arg2: UnlinkedCall
+  __ CallRuntime(kUnlinkedCallRuntimeEntry, 3);
   __ popq(RBX);
   __ popq(RBX);
+  __ popq(CODE_REG);  // result = stub
   __ popq(RBX);  // result = IC
 
   __ popq(RDX);  // Restore receiver.
   __ LeaveStubFrame();
 
-  __ movq(CODE_REG,
-          Address(THR, target::Thread::ic_lookup_through_code_stub_offset()));
   __ movq(RCX, FieldAddress(CODE_REG, target::Code::entry_point_offset(
                                           CodeEntryKind::kMonomorphic)));
   __ jmp(RCX);
@@ -3328,16 +3373,16 @@
   __ pushq(RDX);  // Preserve receiver.
 
   __ pushq(Immediate(0));  // Result slot.
-  __ pushq(RDX);           // Arg0: Receiver
-  __ CallRuntime(kSingleTargetMissRuntimeEntry, 1);
+  __ pushq(Immediate(0));  // Arg0: stub out
+  __ pushq(RDX);           // Arg1: Receiver
+  __ CallRuntime(kSingleTargetMissRuntimeEntry, 2);
   __ popq(RBX);
+  __ popq(CODE_REG);  // result = stub
   __ popq(RBX);  // result = IC
 
   __ popq(RDX);  // Restore receiver.
   __ LeaveStubFrame();
 
-  __ movq(CODE_REG,
-          Address(THR, target::Thread::ic_lookup_through_code_stub_offset()));
   __ movq(RCX, FieldAddress(CODE_REG, target::Code::entry_point_offset(
                                           CodeEntryKind::kMonomorphic)));
   __ jmp(RCX);
@@ -3352,16 +3397,16 @@
   __ pushq(RDX);  // Preserve receiver.
 
   __ pushq(Immediate(0));  // Result slot.
-  __ pushq(RDX);           // Arg0: Receiver
-  __ CallRuntime(kMonomorphicMissRuntimeEntry, 1);
+  __ pushq(Immediate(0));  // Arg0: stub out.
+  __ pushq(RDX);           // Arg1: Receiver
+  __ CallRuntime(kMonomorphicMissRuntimeEntry, 2);
   __ popq(RBX);
+  __ popq(CODE_REG);  // result = stub
   __ popq(RBX);  // result = IC
 
   __ popq(RDX);  // Restore receiver.
   __ LeaveStubFrame();
 
-  __ movq(CODE_REG,
-          Address(THR, target::Thread::ic_lookup_through_code_stub_offset()));
   __ movq(RCX, FieldAddress(CODE_REG, target::Code::entry_point_offset(
                                           CodeEntryKind::kMonomorphic)));
   __ jmp(RCX);
diff --git a/runtime/vm/constants_arm.h b/runtime/vm/constants_arm.h
index 36ae1dc..48378f7 100644
--- a/runtime/vm/constants_arm.h
+++ b/runtime/vm/constants_arm.h
@@ -49,15 +49,15 @@
 
 // iOS ABI
 // See "iOS ABI Function Call Guide"
-// R0-R1: Argument / result / volatile
-// R2-R3: Argument / volatile
-// R4-R6: Preserved
-// R7:    Frame pointer
-// R8-R9: Preserved
-// R12:   Volatile
-// R13:   Stack pointer
-// R14:   Link register
-// R15:   Program counter
+// R0-R1:  Argument / result / volatile
+// R2-R3:  Argument / volatile
+// R4-R6:  Preserved
+// R7:     Frame pointer
+// R8-R11: Preserved
+// R12:    Volatile
+// R13:    Stack pointer
+// R14:    Link register
+// R15:    Program counter
 // Stack alignment: 4 bytes always, 4 bytes at public interfaces
 
 // iOS passes floating point arguments in integer registers (softfp)
diff --git a/runtime/vm/constants_dbc.h b/runtime/vm/constants_dbc.h
index 6a8cd80..d05a74e 100644
--- a/runtime/vm/constants_dbc.h
+++ b/runtime/vm/constants_dbc.h
@@ -1134,6 +1134,11 @@
 const intptr_t kExceptionObjectReg = 0;
 const intptr_t kStackTraceObjectReg = 0;
 
+// The maximum number of fixed registers that are used by some
+// DBC instructions. The register allocator must avoid clashing
+// with these when assigning registers to catch parameters.
+const intptr_t kMaxNumberOfFixedInputRegistersUsedByIL = 3;
+
 enum FpuRegister {
   kNoFpuRegister = -1,
   kFakeFpuRegister,
diff --git a/runtime/vm/constants_kbc.h b/runtime/vm/constants_kbc.h
index 811f106..5c422d8 100644
--- a/runtime/vm/constants_kbc.h
+++ b/runtime/vm/constants_kbc.h
@@ -749,7 +749,7 @@
   // Maximum bytecode format version supported by VM.
   // The range of supported versions should include version produced by bytecode
   // generator (currentBytecodeFormatVersion in pkg/vm/lib/bytecode/dbc.dart).
-  static const intptr_t kMaxSupportedBytecodeFormatVersion = 9;
+  static const intptr_t kMaxSupportedBytecodeFormatVersion = 10;
 
   enum Opcode {
 #define DECLARE_BYTECODE(name, encoding, kind, op1, op2, op3) k##name,
@@ -999,12 +999,25 @@
   DART_FORCE_INLINE static bool IsDebugBreakCheckedOpcode(
       const KBCInstr* instr) {
     switch (DecodeOpcode(instr)) {
+      case KernelBytecode::kPopLocal:
+      case KernelBytecode::kPopLocal_Wide:
+      case KernelBytecode::kStoreLocal:
+      case KernelBytecode::kStoreLocal_Wide:
+      case KernelBytecode::kStoreStaticTOS:
+      case KernelBytecode::kStoreStaticTOS_Wide:
       case KernelBytecode::kCheckStack:
       case KernelBytecode::kDirectCall:
+      case KernelBytecode::kDirectCall_Wide:
       case KernelBytecode::kInterfaceCall:
+      case KernelBytecode::kInterfaceCall_Wide:
       case KernelBytecode::kUncheckedInterfaceCall:
+      case KernelBytecode::kUncheckedInterfaceCall_Wide:
       case KernelBytecode::kDynamicCall:
+      case KernelBytecode::kDynamicCall_Wide:
       case KernelBytecode::kReturnTOS:
+      case KernelBytecode::kThrow:
+      case KernelBytecode::kJump:
+      case KernelBytecode::kJump_Wide:
         return true;
       default:
         return false;
diff --git a/runtime/vm/dart.cc b/runtime/vm/dart.cc
index bf929e9..ce05348 100644
--- a/runtime/vm/dart.cc
+++ b/runtime/vm/dart.cc
@@ -113,7 +113,9 @@
                CHECK_RANGE, CHECK_CONSTANT, NOT_IN_PRECOMPILED_RUNTIME)
 
   if (!ok) {
-    FATAL("CheckOffsets failed.");
+    FATAL(
+        "CheckOffsets failed. Try updating offsets by running "
+        "./tools/run_offsets_extractor.sh");
   }
 #undef CHECK_FIELD
 #undef CHECK_ARRAY
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 4ebdac9..ee31194 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -1465,13 +1465,14 @@
   Thread* T = Thread::Current();
   Isolate* I = T->isolate();
   CHECK_ISOLATE(I);
-  NoSafepointScope no_safepoint_scope;
-  if (I->sticky_error() != Error::null()) {
-    TransitionNativeToVM transition(T);
-    Dart_Handle error = Api::NewHandle(T, I->sticky_error());
-    return error;
+  {
+    NoSafepointScope no_safepoint_scope;
+    if (I->sticky_error() == Error::null()) {
+      return Api::Null();
+    }
   }
-  return Dart_Null();
+  TransitionNativeToVM transition(T);
+  return Api::NewHandle(T, I->sticky_error());
 }
 
 DART_EXPORT void Dart_NotifyIdle(int64_t deadline) {
@@ -3695,7 +3696,8 @@
   intptr_t size_in_bytes = 0;
   void* data_tmp = NULL;
   bool external = false;
-  // If it is an external typed data object just return the data field.
+  T->IncrementNoSafepointScopeDepth();
+  START_NO_CALLBACK_SCOPE(T);
   if (RawObject::IsExternalTypedDataClassId(class_id)) {
     const ExternalTypedData& obj =
         Api::UnwrapExternalTypedDataHandle(Z, object);
@@ -3705,13 +3707,10 @@
     data_tmp = obj.DataAddr(0);
     external = true;
   } else if (RawObject::IsTypedDataClassId(class_id)) {
-    // Regular typed data object, set up some GC and API callback guards.
     const TypedData& obj = Api::UnwrapTypedDataHandle(Z, object);
     ASSERT(!obj.IsNull());
     length = obj.Length();
     size_in_bytes = length * TypedData::ElementSizeInBytes(class_id);
-    T->IncrementNoSafepointScopeDepth();
-    START_NO_CALLBACK_SCOPE(T);
     data_tmp = obj.DataAddr(0);
   } else {
     ASSERT(RawObject::IsTypedDataViewClassId(class_id));
@@ -3724,8 +3723,6 @@
     val = view_obj.offset_in_bytes();
     intptr_t offset_in_bytes = val.Value();
     const auto& obj = Instance::Handle(view_obj.typed_data());
-    T->IncrementNoSafepointScopeDepth();
-    START_NO_CALLBACK_SCOPE(T);
     if (TypedData::IsTypedData(obj)) {
       const TypedData& data_obj = TypedData::Cast(obj);
       data_tmp = data_obj.DataAddr(offset_in_bytes);
@@ -3769,10 +3766,8 @@
       !RawObject::IsTypedDataClassId(class_id)) {
     RETURN_TYPE_ERROR(Z, object, 'TypedData');
   }
-  if (!RawObject::IsExternalTypedDataClassId(class_id)) {
-    T->DecrementNoSafepointScopeDepth();
-    END_NO_CALLBACK_SCOPE(T);
-  }
+  T->DecrementNoSafepointScopeDepth();
+  END_NO_CALLBACK_SCOPE(T);
   if (FLAG_verify_acquired_data) {
     const Object& obj = Object::Handle(Z, Api::UnwrapHandle(object));
     WeakTable* table = I->api_state()->acquired_table();
diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc
index e500239..4841f0d 100644
--- a/runtime/vm/dart_api_impl_test.cc
+++ b/runtime/vm/dart_api_impl_test.cc
@@ -2337,6 +2337,7 @@
   void* data;
   intptr_t len;
   result = Dart_TypedDataAcquireData(array, &type, &data, &len);
+  EXPECT(!Thread::Current()->IsAtSafepoint());
   EXPECT_VALID(result);
   EXPECT_EQ(expected_type, type);
   EXPECT_EQ(kLength, len);
@@ -2345,14 +2346,12 @@
     EXPECT_EQ(i, dataP[i]);
   }
 
-  if (!is_external) {
-    // Now try allocating a string with outstanding Acquires and it should
-    // return an error.
-    result = NewString("We expect an error here");
-    EXPECT_ERROR(result,
-                 "Internal Dart data pointers have been acquired, "
-                 "please release them using Dart_TypedDataReleaseData.");
-  }
+  // Now try allocating a string with outstanding Acquires and it should
+  // return an error.
+  result = NewString("We expect an error here");
+  EXPECT_ERROR(result,
+               "Internal Dart data pointers have been acquired, "
+               "please release them using Dart_TypedDataReleaseData.");
 
   // Now modify the values in the directly accessible array and then check
   // it we see the changes back in dart.
@@ -2361,6 +2360,7 @@
   }
 
   // Release direct access to the typed data object.
+  EXPECT(!Thread::Current()->IsAtSafepoint());
   result = Dart_TypedDataReleaseData(array);
   EXPECT_VALID(result);
 
@@ -2369,6 +2369,29 @@
   EXPECT_VALID(result);
 }
 
+class BackgroundGCTask : public ThreadPool::Task {
+ public:
+  BackgroundGCTask(Isolate* isolate, Monitor* monitor, bool* done)
+      : isolate_(isolate), monitor_(monitor), done_(done) {}
+  virtual void Run() {
+    Thread::EnterIsolateAsHelper(isolate_, Thread::kUnknownTask);
+    for (intptr_t i = 0; i < 10; i++) {
+      Thread::Current()->heap()->CollectAllGarbage(Heap::kDebugging);
+    }
+    Thread::ExitIsolateAsHelper();
+    {
+      MonitorLocker ml(monitor_);
+      *done_ = true;
+      ml.Notify();
+    }
+  }
+
+ private:
+  Isolate* isolate_;
+  Monitor* monitor_;
+  bool* done_;
+};
+
 static void TestTypedDataDirectAccess1() {
   const char* kScriptChars =
       "import 'dart:typed_data';\n"
@@ -2397,20 +2420,35 @@
   // Create a test library and Load up a test script in it.
   Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
 
-  // Test with an regular typed data object.
-  Dart_Handle list_access_test_obj;
-  list_access_test_obj = Dart_Invoke(lib, NewString("main"), 0, NULL);
-  EXPECT_VALID(list_access_test_obj);
-  TestDirectAccess(lib, list_access_test_obj, Dart_TypedData_kInt8, false);
+  Monitor monitor;
+  bool done = false;
+  Dart::thread_pool()->Run<BackgroundGCTask>(Isolate::Current(), &monitor,
+                                             &done);
 
-  // Test with an external typed data object.
-  uint8_t data[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-  intptr_t data_length = ARRAY_SIZE(data);
-  Dart_Handle ext_list_access_test_obj;
-  ext_list_access_test_obj =
-      Dart_NewExternalTypedData(Dart_TypedData_kUint8, data, data_length);
-  EXPECT_VALID(ext_list_access_test_obj);
-  TestDirectAccess(lib, ext_list_access_test_obj, Dart_TypedData_kUint8, true);
+  for (intptr_t i = 0; i < 10; i++) {
+    // Test with an regular typed data object.
+    Dart_Handle list_access_test_obj;
+    list_access_test_obj = Dart_Invoke(lib, NewString("main"), 0, NULL);
+    EXPECT_VALID(list_access_test_obj);
+    TestDirectAccess(lib, list_access_test_obj, Dart_TypedData_kInt8, false);
+
+    // Test with an external typed data object.
+    uint8_t data[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    intptr_t data_length = ARRAY_SIZE(data);
+    Dart_Handle ext_list_access_test_obj;
+    ext_list_access_test_obj =
+        Dart_NewExternalTypedData(Dart_TypedData_kUint8, data, data_length);
+    EXPECT_VALID(ext_list_access_test_obj);
+    TestDirectAccess(lib, ext_list_access_test_obj, Dart_TypedData_kUint8,
+                     true);
+  }
+
+  {
+    MonitorLocker ml(&monitor);
+    while (!done) {
+      ml.Wait();
+    }
+  }
 }
 
 TEST_CASE(DartAPI_TypedDataDirectAccess1Unverified) {
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
index f79f581..af1ca5c 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -652,8 +652,8 @@
 void ActivationFrame::GetVarDescriptors() {
   if (var_descriptors_.IsNull()) {
     if (IsInterpreted()) {
-      // TODO(regis): Kernel bytecode does not yet provide var descriptors.
-      var_descriptors_ = Object::empty_var_descriptors().raw();
+      var_descriptors_ = bytecode().GetLocalVarDescriptors();
+      ASSERT(!var_descriptors_.IsNull());
       return;
     }
     Code& unoptimized_code = Code::Handle(function().unoptimized_code());
@@ -684,9 +684,14 @@
   OS::PrintErr("deopt_id_ %" Px "\n", deopt_id_);
   OS::PrintErr("context_level_ %" Px "\n", context_level_);
   DisassembleToStdout formatter;
-  if (IsInterpreted()) {
-    bytecode().Disassemble(&formatter);
-    PcDescriptors::Handle(bytecode().pc_descriptors()).Print();
+  if (function().is_declared_in_bytecode()) {
+#if !defined(DART_PRECOMPILED_RUNTIME)
+    ASSERT(function().HasBytecode());
+    const Bytecode& bytecode = Bytecode::Handle(function().bytecode());
+    bytecode.Disassemble(&formatter);
+#else
+    UNREACHABLE();
+#endif  // !defined(DART_PRECOMPILED_RUNTIME)
   } else {
     code().Disassemble(&formatter);
     PcDescriptors::Handle(code().pc_descriptors()).Print();
@@ -702,38 +707,97 @@
   OS::Abort();
 }
 
-// Calculate the context level at the current token index of the frame.
+// Calculate the context level at the current bytecode pc or code deopt id
+// of the frame.
 intptr_t ActivationFrame::ContextLevel() {
-  // TODO(regis): get context level information using
-  //  BytecodeLocalVariablesIterator for interpreted frames and compiled frames
-  //  with a function coming from bytecode (function.is_declared_in_bytecode())
   const Context& ctx = GetSavedCurrentContext();
   if (context_level_ < 0 && !ctx.IsNull()) {
-    ASSERT(!code_.is_optimized());
-
-    GetVarDescriptors();
-    intptr_t deopt_id = DeoptId();
-    if (deopt_id == DeoptId::kNone) {
-      PrintDescriptorsError("Missing deopt id");
-    }
-    intptr_t var_desc_len = var_descriptors_.Length();
-    bool found = false;
-    for (intptr_t cur_idx = 0; cur_idx < var_desc_len; cur_idx++) {
-      RawLocalVarDescriptors::VarInfo var_info;
-      var_descriptors_.GetInfo(cur_idx, &var_info);
-      const int8_t kind = var_info.kind();
-      if ((kind == RawLocalVarDescriptors::kContextLevel) &&
-          (deopt_id >= var_info.begin_pos.value()) &&
-          (deopt_id <= var_info.end_pos.value())) {
-        context_level_ = var_info.index();
-        found = true;
-        break;
+    ASSERT(IsInterpreted() || !code_.is_optimized());
+    if (function().is_declared_in_bytecode()) {
+#if !defined(DART_PRECOMPILED_RUNTIME)
+      // Although this activation frame may not have bytecode, its code was
+      // compiled from bytecode.
+      if (!IsInterpreted()) {
+        // TODO(regis): If this frame was compiled from bytecode, pc_ does not
+        // reflect a bytecode pc. How do we map to one? We should generate new
+        // LocalVarDescriptors for code compiled from bytecode so that they
+        // provide deopt_id to context level mapping.
+        UNIMPLEMENTED();
       }
+      ASSERT(function().HasBytecode());
+      Thread* thread = Thread::Current();
+      Zone* zone = thread->zone();
+      Bytecode& bytecode = Bytecode::Handle(zone, function().bytecode());
+      if (!bytecode.HasLocalVariablesInfo()) {
+        PrintDescriptorsError("Missing local variables info");
+      }
+      intptr_t pc_offset = pc_ - bytecode.PayloadStart();
+      kernel::BytecodeLocalVariablesIterator local_vars(zone, bytecode);
+      while (local_vars.MoveNext()) {
+        if (local_vars.Kind() ==
+            kernel::BytecodeLocalVariablesIterator::kScope) {
+          if (local_vars.StartPC() <= pc_offset &&
+              pc_offset < local_vars.EndPC()) {
+            context_level_ = local_vars.ContextLevel();
+            break;
+          }
+        }
+      }
+      if (context_level_ < 0 && function().IsClosureFunction()) {
+        // Obtain the context level from the parent function.
+        // TODO(alexmarkov): Define scope which includes the whole closure body.
+        Function& parent = Function::Handle(zone, function().parent_function());
+        intptr_t depth = 1;
+        do {
+          bytecode = parent.bytecode();
+          kernel::BytecodeLocalVariablesIterator local_vars(zone, bytecode);
+          while (local_vars.MoveNext()) {
+            if (local_vars.Kind() ==
+                kernel::BytecodeLocalVariablesIterator::kScope) {
+              if (local_vars.StartTokenPos() <= TokenPos() &&
+                  TokenPos() <= local_vars.EndTokenPos()) {
+                context_level_ = local_vars.ContextLevel() + depth;
+                break;
+              }
+            }
+          }
+          if (context_level_ >= 0) break;
+          parent = parent.parent_function();
+          depth++;
+        } while (!parent.IsNull());
+      }
+      if (context_level_ < 0) {
+        PrintDescriptorsError("Missing context level in local variables info");
+      }
+#else
+      UNREACHABLE();
+#endif  // !defined(DART_PRECOMPILED_RUNTIME)
+    } else {
+      ASSERT(!code_.is_optimized());
+      GetVarDescriptors();
+      intptr_t deopt_id = DeoptId();
+      if (deopt_id == DeoptId::kNone) {
+        PrintDescriptorsError("Missing deopt id");
+      }
+      intptr_t var_desc_len = var_descriptors_.Length();
+      bool found = false;
+      for (intptr_t cur_idx = 0; cur_idx < var_desc_len; cur_idx++) {
+        RawLocalVarDescriptors::VarInfo var_info;
+        var_descriptors_.GetInfo(cur_idx, &var_info);
+        const int8_t kind = var_info.kind();
+        if ((kind == RawLocalVarDescriptors::kContextLevel) &&
+            (deopt_id >= var_info.begin_pos.value()) &&
+            (deopt_id <= var_info.end_pos.value())) {
+          context_level_ = var_info.index();
+          found = true;
+          break;
+        }
+      }
+      if (!found) {
+        PrintDescriptorsError("Missing context level");
+      }
+      ASSERT(context_level_ >= 0);
     }
-    if (!found) {
-      PrintDescriptorsError("Missing context level");
-    }
-    ASSERT(context_level_ >= 0);
   }
   return context_level_;
 }
@@ -883,11 +947,7 @@
 }
 
 void ActivationFrame::ExtractTokenPositionFromAsyncClosure() {
-  // Attempt to determine the token position from the async closure.
-  if (IsInterpreted()) {
-    // TODO(regis): Implement.
-    return;
-  }
+  // Attempt to determine the token pos and try index from the async closure.
   Thread* thread = Thread::Current();
   Zone* zone = thread->zone();
   const Script& script = Script::Handle(zone, function().script());
@@ -896,16 +956,14 @@
   // This should only be called on frames that aren't active on the stack.
   ASSERT(fp() == 0);
 
+  ASSERT(script.kind() == RawScript::kKernelTag);
   const Array& await_to_token_map =
-      Array::Handle(zone, script.kind() == RawScript::kKernelTag
-                              ? script.yield_positions()
-                              : code_.await_token_positions());
+      Array::Handle(zone, script.yield_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++) {
@@ -923,28 +981,20 @@
   if (await_jump_var < 0) {
     return;
   }
-  intptr_t await_to_token_map_index =
-      script.kind() == RawScript::kKernelTag
-          ? await_jump_var - 1
-          :
-          // source script tokens array has first element duplicated
-          await_jump_var;
-
-  if (script.kind() == RawScript::kKernelTag) {
-    // yield_positions returns all yield positions for the script (in sorted
-    // order).
-    // We thus need to offset the function start to get the actual index.
-    if (!function_.token_pos().IsReal()) {
-      return;
-    }
-    const intptr_t function_start = function_.token_pos().value();
-    for (intptr_t i = 0;
-         i < await_to_token_map.Length() &&
-         Smi::Value(reinterpret_cast<RawSmi*>(await_to_token_map.At(i))) <
-             function_start;
-         i++) {
-      await_to_token_map_index++;
-    }
+  intptr_t await_to_token_map_index = await_jump_var - 1;
+  // yield_positions returns all yield positions for the script (in sorted
+  // order).
+  // We thus need to offset the function start to get the actual index.
+  if (!function_.token_pos().IsReal()) {
+    return;
+  }
+  const intptr_t function_start = function_.token_pos().value();
+  for (intptr_t i = 0;
+       i < await_to_token_map.Length() &&
+       Smi::Value(reinterpret_cast<RawSmi*>(await_to_token_map.At(i))) <
+           function_start;
+       i++) {
+    await_to_token_map_index++;
   }
 
   if (await_to_token_map_index >= await_to_token_map.Length()) {
@@ -959,6 +1009,30 @@
   ASSERT(token_pos.IsSmi());
   token_pos_ = TokenPosition(Smi::Cast(token_pos).Value());
   token_pos_initialized_ = true;
+  if (IsInterpreted()) {
+#if !defined(DART_PRECOMPILED_RUNTIME)
+    // In order to determine the try index, we need to map the token position
+    // to a pc offset, and then a pc offset to the try index.
+    // TODO(regis): Should we set the token position fields in pc descriptors?
+    uword pc_offset = kUwordMax;
+    kernel::BytecodeSourcePositionsIterator iter(zone, bytecode());
+    while (iter.MoveNext()) {
+      // PcOffsets are monotonic in source positions, so we get the lowest one.
+      if (iter.TokenPos() == token_pos_) {
+        pc_offset = iter.PcOffset();
+        break;
+      }
+    }
+    if (pc_offset < kUwordMax) {
+      try_index_ =
+          bytecode().GetTryIndexAtPc(bytecode().PayloadStart() + pc_offset);
+    }
+#else
+    UNREACHABLE();
+#endif  // !defined(DART_PRECOMPILED_RUNTIME)
+    return;
+  }
+  GetPcDescriptors();
   PcDescriptors::Iterator iter(pc_desc_, RawPcDescriptors::kAnyKind);
   while (iter.MoveNext()) {
     if (iter.TokenPos() == token_pos_) {
@@ -1135,10 +1209,22 @@
   return *reinterpret_cast<RawObject**>(addr);
 }
 
+// Caution: GetParameter only works for fixed parameters.
 RawObject* ActivationFrame::GetParameter(intptr_t index) {
   intptr_t num_parameters = function().num_fixed_parameters();
   ASSERT(0 <= index && index < num_parameters);
 
+  if (IsInterpreted()) {
+    if (function().NumOptionalParameters() > 0) {
+      // Note that we do not access optional but only fixed parameters, hence
+      // we do not need to replicate the logic of IndexFor() in bytecode reader.
+      return GetVariableValue(fp() + index * kWordSize);
+    } else {
+      return GetVariableValue(
+          fp() - (kKBCParamEndSlotFromFp + num_parameters - index) * kWordSize);
+    }
+  }
+
   if (function().NumOptionalParameters() > 0) {
     // If the function has optional parameters, the first positional parameter
     // can be in a number of places in the caller's frame depending on how many
@@ -1159,6 +1245,13 @@
 }
 
 RawObject* ActivationFrame::GetStackVar(VariableIndex variable_index) {
+  if (IsInterpreted()) {
+    intptr_t slot_index = -variable_index.value();
+    if (slot_index < 0) {
+      slot_index -= kKBCParamEndSlotFromFp;  // Accessing a parameter.
+    }
+    return GetVariableValue(fp() + slot_index * kWordSize);
+  }
   const intptr_t slot_index =
       runtime_frame_layout.FrameSlotForVariableIndex(variable_index.value());
   if (deopt_frame_.IsNull()) {
@@ -1860,18 +1953,26 @@
 // that inline the function that contains the newly created breakpoint.
 // We currently don't have this info so we deoptimize all functions.
 void Debugger::DeoptimizeWorld() {
+#if defined(DART_PRECOMPILED_RUNTIME)
+  UNREACHABLE();
+#else
   BackgroundCompiler::Stop(isolate_);
   if (FLAG_trace_deoptimization) {
     THR_Print("Deopt for debugger\n");
   }
+  isolate_->set_has_attempted_stepping(true);
+
   DeoptimizeFunctionsOnStack();
+
   // Iterate over all classes, deoptimize functions.
   // TODO(hausner): Could possibly be combined with RemoveOptimizedCode()
   const ClassTable& class_table = *isolate_->class_table();
-  Class& cls = Class::Handle();
-  Array& functions = Array::Handle();
-  GrowableObjectArray& closures = GrowableObjectArray::Handle();
-  Function& function = Function::Handle();
+  Zone* zone = Thread::Current()->zone();
+  Class& cls = Class::Handle(zone);
+  Array& functions = Array::Handle(zone);
+  GrowableObjectArray& closures = GrowableObjectArray::Handle(zone);
+  Function& function = Function::Handle(zone);
+  Code& code = Code::Handle(zone);
   intptr_t num_classes = class_table.NumCids();
   for (intptr_t i = 1; i < num_classes; i++) {
     if (class_table.HasValidClassAt(i)) {
@@ -1887,12 +1988,20 @@
           if (function.HasOptimizedCode()) {
             function.SwitchToUnoptimizedCode();
           }
+          code = function.unoptimized_code();
+          if (!code.IsNull()) {
+            code.ResetSwitchableCalls(zone);
+          }
           // Also disable any optimized implicit closure functions.
           if (function.HasImplicitClosureFunction()) {
             function = function.ImplicitClosureFunction();
             if (function.HasOptimizedCode()) {
               function.SwitchToUnoptimizedCode();
             }
+            code = function.unoptimized_code();
+            if (!code.IsNull()) {
+              code.ResetSwitchableCalls(zone);
+            }
           }
         }
       }
@@ -1908,7 +2017,12 @@
     if (function.HasOptimizedCode()) {
       function.SwitchToUnoptimizedCode();
     }
+    code = function.unoptimized_code();
+    if (!code.IsNull()) {
+      code.ResetSwitchableCalls(zone);
+    }
   }
+#endif  // defined(DART_PRECOMPILED_RUNTIME)
 }
 
 void Debugger::NotifySingleStepping(bool value) const {
@@ -2367,6 +2481,12 @@
     ActivationFrame* activation = new (zone) ActivationFrame(async_activation);
     activation->ExtractTokenPositionFromAsyncClosure();
     stack_trace->AddActivation(activation);
+    if (FLAG_trace_debugger_stacktrace) {
+      OS::PrintErr(
+          "CollectAwaiterReturnStackTrace: visiting awaiter return "
+          "closures:\n\t%s\n",
+          activation->function().ToFullyQualifiedCString());
+    }
     next_async_activation = activation->GetAsyncAwaiter();
     if (next_async_activation.IsNull()) {
       // No more awaiters. Extract the causal stack trace (if it exists).
@@ -2826,19 +2946,15 @@
     const TokenPosition begin_pos = best_fit_pos;
 
     TokenPosition end_of_line_pos;
-    if (script.kind() == RawScript::kKernelTag) {
-      if (best_line == -1) {
-        script.GetTokenLocation(begin_pos, &best_line, NULL);
-      }
-      ASSERT(best_line > 0);
-      TokenPosition ignored;
-      script.TokenRangeAtLine(best_line, &ignored, &end_of_line_pos);
-      if (end_of_line_pos < begin_pos) {
-        end_of_line_pos = begin_pos;
-      }
-    } else {
-      UNREACHABLE();
-      end_of_line_pos = TokenPosition::kNoSource;
+    ASSERT(script.kind() == RawScript::kKernelTag);
+    if (best_line == -1) {
+      script.GetTokenLocation(begin_pos, &best_line, NULL);
+    }
+    ASSERT(best_line > 0);
+    TokenPosition ignored;
+    script.TokenRangeAtLine(best_line, &ignored, &end_of_line_pos);
+    if (end_of_line_pos < begin_pos) {
+      end_of_line_pos = begin_pos;
     }
 
     uword lowest_pc_offset = kUwordMax;
@@ -4039,18 +4155,15 @@
     ASSERT(closure_or_null.IsInstance());
     ASSERT(Instance::Cast(closure_or_null).IsClosure());
     const Script& script = Script::Handle(zone, top_frame->SourceScript());
-    if (script.kind() == RawScript::kKernelTag) {
-      // Are we at a yield point (previous await)?
-      const Array& yields = Array::Handle(script.yield_positions());
-      intptr_t looking_for = top_frame->TokenPos().value();
-      Smi& value = Smi::Handle(zone);
-      for (int i = 0; i < yields.Length(); i++) {
-        value ^= yields.At(i);
-        if (value.Value() == looking_for) return true;
-      }
-      return false;
+    ASSERT(script.kind() == RawScript::kKernelTag);
+    // Are we at a yield point (previous await)?
+    const Array& yields = Array::Handle(script.yield_positions());
+    intptr_t looking_for = top_frame->TokenPos().value();
+    Smi& value = Smi::Handle(zone);
+    for (int i = 0; i < yields.Length(); i++) {
+      value ^= yields.At(i);
+      if (value.Value() == looking_for) return true;
     }
-    UNREACHABLE();
   }
   return false;
 }
@@ -4287,8 +4400,11 @@
 
 // Return innermost closure contained in 'function' that contains
 // the given token position.
+// Note: this should only be called for compiled functions and not for
+// bytecode functions.
 RawFunction* Debugger::FindInnermostClosure(const Function& function,
                                             TokenPosition token_pos) {
+  ASSERT(function.HasCode());
   Zone* zone = Thread::Current()->zone();
   const Script& outer_origin = Script::Handle(zone, function.script());
   const GrowableObjectArray& closures = GrowableObjectArray::Handle(
@@ -4345,7 +4461,6 @@
     script = loc->script();
     if (FunctionOverlaps(func, script, loc->token_pos(),
                          loc->end_token_pos())) {
-      Function& inner_function = Function::Handle(zone);
       TokenPosition token_pos = loc->token_pos();
       TokenPosition end_token_pos = loc->end_token_pos();
       if (token_pos != end_token_pos && loc->requested_column_number() >= 0) {
@@ -4357,23 +4472,31 @@
                                            loc->requested_column_number());
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
       }
-      // TODO(regis): Bytecode closures are not currently added to
-      // object_store()->closure_functions(). Should they? Revisit.
-      inner_function = FindInnermostClosure(func, token_pos);
-      if (!inner_function.IsNull()) {
-        // The local function of a function we just compiled cannot
-        // be compiled already.
-        ASSERT(!inner_function.HasCode());
-        if (FLAG_verbose_debug) {
-          OS::PrintErr("Pending BP remains unresolved in inner function '%s'\n",
-                       inner_function.ToFullyQualifiedCString());
+      if (bytecode_loaded) {
+        // func's bytecode was just loaded.
+        // If func has an inner closure, we got notified earlier.
+        // Therefore we will always resolve the breakpoint correctly to the
+        // innermost function without searching for it.
+      } else {
+        // func was just compiled.
+        const Function& inner_function =
+            Function::Handle(zone, FindInnermostClosure(func, token_pos));
+        if (!inner_function.IsNull()) {
+          // The local function of a function we just compiled cannot
+          // be compiled already.
+          ASSERT(!inner_function.HasCode());
+          if (FLAG_verbose_debug) {
+            OS::PrintErr(
+                "Pending BP remains unresolved in inner function '%s'\n",
+                inner_function.ToFullyQualifiedCString());
+          }
+          continue;
         }
-        continue;
-      }
 
-      // TODO(hausner): What should we do if function is optimized?
-      // Can we deoptimize the function?
-      ASSERT(!func.HasOptimizedCode());
+        // TODO(hausner): What should we do if function is optimized?
+        // Can we deoptimize the function?
+        ASSERT(!func.HasOptimizedCode());
+      }
 
       // There is no local function within func that contains the
       // breakpoint token position. Resolve the breakpoint if necessary
diff --git a/runtime/vm/debugger_arm.cc b/runtime/vm/debugger_arm.cc
index 3eafa74..15d95ba 100644
--- a/runtime/vm/debugger_arm.cc
+++ b/runtime/vm/debugger_arm.cc
@@ -24,9 +24,11 @@
   Code& stub_target = Code::Handle();
   switch (breakpoint_kind_) {
     case RawPcDescriptors::kIcCall:
-    case RawPcDescriptors::kUnoptStaticCall:
       stub_target = StubCode::ICCallBreakpoint().raw();
       break;
+    case RawPcDescriptors::kUnoptStaticCall:
+      stub_target = StubCode::UnoptStaticCallBreakpoint().raw();
+      break;
     case RawPcDescriptors::kRuntimeCall:
       stub_target = StubCode::RuntimeCallBreakpoint().raw();
       break;
diff --git a/runtime/vm/debugger_arm64.cc b/runtime/vm/debugger_arm64.cc
index 48ed9c2..e9748ca 100644
--- a/runtime/vm/debugger_arm64.cc
+++ b/runtime/vm/debugger_arm64.cc
@@ -21,22 +21,30 @@
 
 void CodeBreakpoint::PatchCode() {
   ASSERT(!is_enabled_);
-  Code& stub_target = Code::Handle();
+  const Code& code = Code::Handle(code_);
   switch (breakpoint_kind_) {
-    case RawPcDescriptors::kIcCall:
-    case RawPcDescriptors::kUnoptStaticCall:
-      stub_target = StubCode::ICCallBreakpoint().raw();
+    case RawPcDescriptors::kIcCall: {
+      Object& data = Object::Handle();
+      saved_value_ = CodePatcher::GetInstanceCallAt(pc_, code, &data);
+      CodePatcher::PatchInstanceCallAt(pc_, code, data,
+                                       StubCode::ICCallBreakpoint());
       break;
+    }
+    case RawPcDescriptors::kUnoptStaticCall: {
+      saved_value_ = CodePatcher::GetStaticCallTargetAt(pc_, code);
+      CodePatcher::PatchPoolPointerCallAt(
+          pc_, code, StubCode::UnoptStaticCallBreakpoint());
+      break;
+    }
     case RawPcDescriptors::kRuntimeCall: {
-      stub_target = StubCode::RuntimeCallBreakpoint().raw();
+      saved_value_ = CodePatcher::GetStaticCallTargetAt(pc_, code);
+      CodePatcher::PatchPoolPointerCallAt(pc_, code,
+                                          StubCode::RuntimeCallBreakpoint());
       break;
     }
     default:
       UNREACHABLE();
   }
-  const Code& code = Code::Handle(code_);
-  saved_value_ = CodePatcher::GetStaticCallTargetAt(pc_, code);
-  CodePatcher::PatchPoolPointerCallAt(pc_, code, stub_target);
   is_enabled_ = true;
 }
 
@@ -44,7 +52,13 @@
   ASSERT(is_enabled_);
   const Code& code = Code::Handle(code_);
   switch (breakpoint_kind_) {
-    case RawPcDescriptors::kIcCall:
+    case RawPcDescriptors::kIcCall: {
+      Object& data = Object::Handle();
+      CodePatcher::GetInstanceCallAt(pc_, code, &data);
+      CodePatcher::PatchInstanceCallAt(pc_, code, data,
+                                       Code::Handle(saved_value_));
+      break;
+    }
     case RawPcDescriptors::kUnoptStaticCall:
     case RawPcDescriptors::kRuntimeCall: {
       CodePatcher::PatchPoolPointerCallAt(pc_, code,
diff --git a/runtime/vm/debugger_ia32.cc b/runtime/vm/debugger_ia32.cc
index d74202c..10ee78f 100644
--- a/runtime/vm/debugger_ia32.cc
+++ b/runtime/vm/debugger_ia32.cc
@@ -31,11 +31,14 @@
   {
     WritableInstructionsScope writable(instrs.PayloadStart(), instrs.Size());
     switch (breakpoint_kind_) {
-      case RawPcDescriptors::kIcCall:
-      case RawPcDescriptors::kUnoptStaticCall: {
+      case RawPcDescriptors::kIcCall: {
         stub_target = StubCode::ICCallBreakpoint().raw();
         break;
       }
+      case RawPcDescriptors::kUnoptStaticCall: {
+        stub_target = StubCode::UnoptStaticCallBreakpoint().raw();
+        break;
+      }
       case RawPcDescriptors::kRuntimeCall: {
         saved_value_ = CodePatcher::GetStaticCallTargetAt(pc_, code);
         stub_target = StubCode::RuntimeCallBreakpoint().raw();
diff --git a/runtime/vm/debugger_x64.cc b/runtime/vm/debugger_x64.cc
index 39cf91c..883880f 100644
--- a/runtime/vm/debugger_x64.cc
+++ b/runtime/vm/debugger_x64.cc
@@ -26,9 +26,11 @@
   Code& stub_target = Code::Handle();
   switch (breakpoint_kind_) {
     case RawPcDescriptors::kIcCall:
-    case RawPcDescriptors::kUnoptStaticCall:
       stub_target = StubCode::ICCallBreakpoint().raw();
       break;
+    case RawPcDescriptors::kUnoptStaticCall:
+      stub_target = StubCode::UnoptStaticCallBreakpoint().raw();
+      break;
     case RawPcDescriptors::kRuntimeCall:
       stub_target = StubCode::RuntimeCallBreakpoint().raw();
       break;
diff --git a/runtime/vm/deferred_objects.cc b/runtime/vm/deferred_objects.cc
index 76ca7b7..7238899 100644
--- a/runtime/vm/deferred_objects.cc
+++ b/runtime/vm/deferred_objects.cc
@@ -124,15 +124,15 @@
   if (pc != 0) {
     // If the deoptimization happened at an IC call, update the IC data
     // to avoid repeated deoptimization at the same site next time around.
-    ICData& ic_data = ICData::Handle(zone);
-    CodePatcher::GetInstanceCallAt(pc, code, &ic_data);
-    if (!ic_data.IsNull()) {
-      ic_data.AddDeoptReason(deopt_context->deopt_reason());
-      // Propagate the reason to all ICData-s with same deopt_id since
-      // only unoptimized-code ICData (IC calls) are propagated.
-      function.SetDeoptReasonForAll(ic_data.deopt_id(),
-                                    deopt_context->deopt_reason());
-    }
+    // We cannot use CodePatcher::GetInstanceCallAt because the call site
+    // may have switched to from referencing an ICData to a target Code or
+    // MegamorphicCache.
+    ICData& ic_data = ICData::Handle(zone, function.FindICData(deopt_id_));
+    ic_data.AddDeoptReason(deopt_context->deopt_reason());
+    // Propagate the reason to all ICData-s with same deopt_id since
+    // only unoptimized-code ICData (IC calls) are propagated.
+    function.SetDeoptReasonForAll(ic_data.deopt_id(),
+                                  deopt_context->deopt_reason());
   } else {
     if (deopt_context->HasDeoptFlag(ICData::kHoisted)) {
       // Prevent excessive deoptimization.
diff --git a/runtime/vm/exceptions.cc b/runtime/vm/exceptions.cc
index f40c90b..a341cad 100644
--- a/runtime/vm/exceptions.cc
+++ b/runtime/vm/exceptions.cc
@@ -569,10 +569,6 @@
                              uword stack_pointer,
                              uword frame_pointer,
                              bool clear_deopt_at_target) {
-  uword fp_for_clearing =
-      (clear_deopt_at_target ? frame_pointer + 1 : frame_pointer);
-  ClearLazyDeopts(thread, fp_for_clearing);
-
 #if !defined(DART_PRECOMPILED_RUNTIME)
   // TODO(regis): We still possibly need to unwind interpreter frames if they
   // are callee frames of the C++ frame handling the exception.
@@ -585,6 +581,10 @@
   }
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
+  const uword fp_for_clearing =
+      (clear_deopt_at_target ? frame_pointer + 1 : frame_pointer);
+  ClearLazyDeopts(thread, fp_for_clearing);
+
 #if defined(USING_SIMULATOR)
   // Unwinding of the C++ frames and destroying of their stack resources is done
   // by the simulator, because the target stack_pointer is a simulated stack
diff --git a/runtime/vm/flag_list.h b/runtime/vm/flag_list.h
index a406f01..2852a43 100644
--- a/runtime/vm/flag_list.h
+++ b/runtime/vm/flag_list.h
@@ -79,6 +79,8 @@
   R(disable_alloc_stubs_after_gc, false, bool, false, "Stress testing flag.")  \
   R(disassemble, false, bool, false, "Disassemble dart code.")                 \
   R(disassemble_optimized, false, bool, false, "Disassemble optimized code.")  \
+  R(disassemble_relative, false, bool, false,                                  \
+    "Use offsets instead of absolute PCs")                                     \
   R(dump_megamorphic_stats, false, bool, false,                                \
     "Dump megamorphic cache statistics")                                       \
   R(dump_symbol_stats, false, bool, false, "Dump symbol table statistics")     \
diff --git a/runtime/vm/heap/compactor.cc b/runtime/vm/heap/compactor.cc
index 06ac415..45f566e 100644
--- a/runtime/vm/heap/compactor.cc
+++ b/runtime/vm/heap/compactor.cc
@@ -234,9 +234,9 @@
     intptr_t next_forwarding_task = 0;
 
     for (intptr_t task_index = 0; task_index < num_tasks; task_index++) {
-      Dart::thread_pool()->Run(new CompactorTask(
+      Dart::thread_pool()->Run<CompactorTask>(
           thread()->isolate(), this, &barrier, &next_forwarding_task,
-          heads[task_index], &tails[task_index], freelist));
+          heads[task_index], &tails[task_index], freelist);
     }
 
     // Plan pages.
diff --git a/runtime/vm/heap/heap.cc b/runtime/vm/heap/heap.cc
index ca9aa44..583119e 100644
--- a/runtime/vm/heap/heap.cc
+++ b/runtime/vm/heap/heap.cc
@@ -876,11 +876,15 @@
 }
 
 void Heap::PrintMemoryUsageJSON(JSONStream* stream) const {
-  JSONObject jsobj(stream);
-  jsobj.AddProperty("type", "MemoryUsage");
-  jsobj.AddProperty64("heapUsage", TotalUsedInWords() * kWordSize);
-  jsobj.AddProperty64("heapCapacity", TotalCapacityInWords() * kWordSize);
-  jsobj.AddProperty64("externalUsage", TotalExternalInWords() * kWordSize);
+  JSONObject obj(stream);
+  PrintMemoryUsageJSON(&obj);
+}
+
+void Heap::PrintMemoryUsageJSON(JSONObject* jsobj) const {
+  jsobj->AddProperty("type", "MemoryUsage");
+  jsobj->AddProperty64("heapUsage", TotalUsedInWords() * kWordSize);
+  jsobj->AddProperty64("heapCapacity", TotalCapacityInWords() * kWordSize);
+  jsobj->AddProperty64("externalUsage", TotalExternalInWords() * kWordSize);
 }
 #endif  // PRODUCT
 
diff --git a/runtime/vm/heap/heap.h b/runtime/vm/heap/heap.h
index 6f530b9..aa1e336 100644
--- a/runtime/vm/heap/heap.h
+++ b/runtime/vm/heap/heap.h
@@ -275,6 +275,7 @@
   // Returns a JSON object with total memory usage statistics for both new and
   // old space combined.
   void PrintMemoryUsageJSON(JSONStream* stream) const;
+  void PrintMemoryUsageJSON(JSONObject* jsobj) const;
 
   // The heap map contains the sizes and class ids for the objects in each page.
   void PrintHeapMapToJSONStream(Isolate* isolate, JSONStream* stream) {
diff --git a/runtime/vm/heap/marker.cc b/runtime/vm/heap/marker.cc
index ec644c0..01149d5 100644
--- a/runtime/vm/heap/marker.cc
+++ b/runtime/vm/heap/marker.cc
@@ -804,8 +804,8 @@
                                           &deferred_marking_stack_);
 
     // Begin marking on a helper thread.
-    bool result = Dart::thread_pool()->Run(
-        new ConcurrentMarkTask(this, isolate_, page_space, visitors_[i]));
+    bool result = Dart::thread_pool()->Run<ConcurrentMarkTask>(
+        this, isolate_, page_space, visitors_[i]);
     ASSERT(result);
   }
 
@@ -862,8 +862,8 @@
               isolate_, page_space, &marking_stack_, &deferred_marking_stack_);
         }
 
-        bool result = Dart::thread_pool()->Run(new ParallelMarkTask(
-            this, isolate_, &marking_stack_, &barrier, visitor, &num_busy));
+        bool result = Dart::thread_pool()->Run<ParallelMarkTask>(
+            this, isolate_, &marking_stack_, &barrier, visitor, &num_busy);
         ASSERT(result);
       }
       bool more_to_mark = false;
diff --git a/runtime/vm/heap/safepoint.cc b/runtime/vm/heap/safepoint.cc
index 3888cdf..d3e3771 100644
--- a/runtime/vm/heap/safepoint.cc
+++ b/runtime/vm/heap/safepoint.cc
@@ -113,8 +113,14 @@
         if (FLAG_trace_safepoint && num_attempts > 10) {
           // We have been waiting too long, start logging this as we might
           // have an issue where a thread is not checking in for a safepoint.
-          OS::PrintErr("Attempt:%" Pd " waiting for %d threads to check in\n",
-                       num_attempts, number_threads_not_at_safepoint_);
+          for (Thread* current = isolate()->thread_registry()->active_list();
+               current != NULL; current = current->next()) {
+            if (!current->IsAtSafepoint()) {
+              OS::PrintErr("Attempt:%" Pd
+                           " waiting for thread %s to check in\n",
+                           num_attempts, current->os_thread()->name());
+            }
+          }
         }
       }
     }
diff --git a/runtime/vm/heap/safepoint.h b/runtime/vm/heap/safepoint.h
index 906933a..4f3cfb8 100644
--- a/runtime/vm/heap/safepoint.h
+++ b/runtime/vm/heap/safepoint.h
@@ -290,7 +290,9 @@
   explicit TransitionNativeToVM(Thread* T) : TransitionSafepointState(T) {
     // We are about to execute vm code and so we are not at a safepoint anymore.
     ASSERT(T->execution_state() == Thread::kThreadInNative);
-    T->ExitSafepoint();
+    if (T->no_callback_scope_depth() == 0) {
+      T->ExitSafepoint();
+    }
     T->set_execution_state(Thread::kThreadInVM);
   }
 
@@ -298,7 +300,9 @@
     // We are returning to native code and so we are at a safepoint.
     ASSERT(thread()->execution_state() == Thread::kThreadInVM);
     thread()->set_execution_state(Thread::kThreadInNative);
-    thread()->EnterSafepoint();
+    if (thread()->no_callback_scope_depth() == 0) {
+      thread()->EnterSafepoint();
+    }
   }
 
  private:
diff --git a/runtime/vm/heap/sweeper.cc b/runtime/vm/heap/sweeper.cc
index b1e385f..1d267ac 100644
--- a/runtime/vm/heap/sweeper.cc
+++ b/runtime/vm/heap/sweeper.cc
@@ -182,8 +182,8 @@
                                 HeapPage* first,
                                 HeapPage* last,
                                 FreeList* freelist) {
-  bool result = Dart::thread_pool()->Run(new ConcurrentSweeperTask(
-      isolate, isolate->heap()->old_space(), first, last, freelist));
+  bool result = Dart::thread_pool()->Run<ConcurrentSweeperTask>(
+      isolate, isolate->heap()->old_space(), first, last, freelist);
   ASSERT(result);
 }
 
diff --git a/runtime/vm/instructions_arm.cc b/runtime/vm/instructions_arm.cc
index c8e8af9..c2be95c 100644
--- a/runtime/vm/instructions_arm.cc
+++ b/runtime/vm/instructions_arm.cc
@@ -18,20 +18,35 @@
 
 CallPattern::CallPattern(uword pc, const Code& code)
     : object_pool_(ObjectPool::Handle(code.GetObjectPool())),
-      end_(pc),
-      ic_data_load_end_(0),
-      target_code_pool_index_(-1),
-      ic_data_(ICData::Handle()) {
+      target_code_pool_index_(-1) {
   ASSERT(code.ContainsInstructionAt(pc));
   // Last instruction: blx lr.
-  ASSERT(*(reinterpret_cast<uword*>(end_) - 1) == 0xe12fff3e);
+  ASSERT(*(reinterpret_cast<uword*>(pc) - 1) == 0xe12fff3e);
 
   Register reg;
-  ic_data_load_end_ = InstructionPattern::DecodeLoadWordFromPool(
-      end_ - 2 * Instr::kInstrSize, &reg, &target_code_pool_index_);
+  InstructionPattern::DecodeLoadWordFromPool(pc - 2 * Instr::kInstrSize, &reg,
+                                             &target_code_pool_index_);
   ASSERT(reg == CODE_REG);
 }
 
+ICCallPattern::ICCallPattern(uword pc, const Code& code)
+    : object_pool_(ObjectPool::Handle(code.GetObjectPool())),
+      target_pool_index_(-1),
+      data_pool_index_(-1) {
+  ASSERT(code.ContainsInstructionAt(pc));
+  // Last instruction: blx lr.
+  ASSERT(*(reinterpret_cast<uword*>(pc) - 1) == 0xe12fff3e);
+
+  Register reg;
+  uword data_load_end = InstructionPattern::DecodeLoadWordFromPool(
+      pc - 2 * Instr::kInstrSize, &reg, &target_pool_index_);
+  ASSERT(reg == CODE_REG);
+
+  InstructionPattern::DecodeLoadWordFromPool(data_load_end, &reg,
+                                             &data_pool_index_);
+  ASSERT(reg == R9);
+}
+
 NativeCallPattern::NativeCallPattern(uword pc, const Code& code)
     : object_pool_(ObjectPool::Handle(code.GetObjectPool())),
       end_(pc),
@@ -245,16 +260,6 @@
   return false;
 }
 
-RawICData* CallPattern::IcData() {
-  if (ic_data_.IsNull()) {
-    Register reg;
-    InstructionPattern::DecodeLoadObject(ic_data_load_end_, object_pool_, &reg,
-                                         &ic_data_);
-    ASSERT(reg == R9);
-  }
-  return ic_data_.raw();
-}
-
 RawCode* CallPattern::TargetCode() const {
   return reinterpret_cast<RawCode*>(
       object_pool_.ObjectAt(target_code_pool_index_));
@@ -264,6 +269,23 @@
   object_pool_.SetObjectAt(target_code_pool_index_, target_code);
 }
 
+RawObject* ICCallPattern::Data() const {
+  return object_pool_.ObjectAt(data_pool_index_);
+}
+
+void ICCallPattern::SetData(const Object& data) const {
+  ASSERT(data.IsArray() || data.IsICData() || data.IsMegamorphicCache());
+  object_pool_.SetObjectAt(data_pool_index_, data);
+}
+
+RawCode* ICCallPattern::TargetCode() const {
+  return reinterpret_cast<RawCode*>(object_pool_.ObjectAt(target_pool_index_));
+}
+
+void ICCallPattern::SetTargetCode(const Code& target_code) const {
+  object_pool_.SetObjectAt(target_pool_index_, target_code);
+}
+
 SwitchableCallPatternBase::SwitchableCallPatternBase(const Code& code)
     : object_pool_(ObjectPool::Handle(code.GetObjectPool())),
       data_pool_index_(-1),
diff --git a/runtime/vm/instructions_arm.h b/runtime/vm/instructions_arm.h
index d3e1cdb..bbd0d6c 100644
--- a/runtime/vm/instructions_arm.h
+++ b/runtime/vm/instructions_arm.h
@@ -72,7 +72,23 @@
  public:
   CallPattern(uword pc, const Code& code);
 
-  RawICData* IcData();
+  RawCode* TargetCode() const;
+  void SetTargetCode(const Code& code) const;
+
+ private:
+  const ObjectPool& object_pool_;
+
+  intptr_t target_code_pool_index_;
+
+  DISALLOW_COPY_AND_ASSIGN(CallPattern);
+};
+
+class ICCallPattern : public ValueObject {
+ public:
+  ICCallPattern(uword pc, const Code& code);
+
+  RawObject* Data() const;
+  void SetData(const Object& data) const;
 
   RawCode* TargetCode() const;
   void SetTargetCode(const Code& code) const;
@@ -80,13 +96,10 @@
  private:
   const ObjectPool& object_pool_;
 
-  uword end_;
-  uword ic_data_load_end_;
+  intptr_t target_pool_index_;
+  intptr_t data_pool_index_;
 
-  intptr_t target_code_pool_index_;
-  ICData& ic_data_;
-
-  DISALLOW_COPY_AND_ASSIGN(CallPattern);
+  DISALLOW_COPY_AND_ASSIGN(ICCallPattern);
 };
 
 class NativeCallPattern : public ValueObject {
diff --git a/runtime/vm/instructions_arm64.cc b/runtime/vm/instructions_arm64.cc
index cc29bcd..a421c27 100644
--- a/runtime/vm/instructions_arm64.cc
+++ b/runtime/vm/instructions_arm64.cc
@@ -18,20 +18,36 @@
 
 CallPattern::CallPattern(uword pc, const Code& code)
     : object_pool_(ObjectPool::Handle(code.GetObjectPool())),
-      end_(pc),
-      ic_data_load_end_(0),
-      target_code_pool_index_(-1),
-      ic_data_(ICData::Handle()) {
+      target_code_pool_index_(-1) {
   ASSERT(code.ContainsInstructionAt(pc));
   // Last instruction: blr ip0.
-  ASSERT(*(reinterpret_cast<uint32_t*>(end_) - 1) == 0xd63f0200);
+  ASSERT(*(reinterpret_cast<uint32_t*>(pc) - 1) == 0xd63f0200);
 
   Register reg;
-  ic_data_load_end_ = InstructionPattern::DecodeLoadWordFromPool(
-      end_ - 2 * Instr::kInstrSize, &reg, &target_code_pool_index_);
+  InstructionPattern::DecodeLoadWordFromPool(pc - 2 * Instr::kInstrSize, &reg,
+                                             &target_code_pool_index_);
   ASSERT(reg == CODE_REG);
 }
 
+ICCallPattern::ICCallPattern(uword pc, const Code& code)
+    : object_pool_(ObjectPool::Handle(code.GetObjectPool())),
+      target_pool_index_(-1),
+      data_pool_index_(-1) {
+  ASSERT(code.ContainsInstructionAt(pc));
+  // Last instruction: blr lr.
+  ASSERT(*(reinterpret_cast<uint32_t*>(pc) - 1) == 0xd63f03c0);
+
+  Register data_reg, code_reg;
+  intptr_t pool_index;
+  InstructionPattern::DecodeLoadDoubleWordFromPool(
+      pc - 2 * Instr::kInstrSize, &data_reg, &code_reg, &pool_index);
+  ASSERT(data_reg == R5);
+  ASSERT(code_reg == CODE_REG);
+
+  data_pool_index_ = pool_index;
+  target_pool_index_ = pool_index + 1;
+}
+
 NativeCallPattern::NativeCallPattern(uword pc, const Code& code)
     : object_pool_(ObjectPool::Handle(code.GetObjectPool())),
       end_(pc),
@@ -353,16 +369,6 @@
   instr->SetInstructionBits(instr->InstructionBits() | B22);
 }
 
-RawICData* CallPattern::IcData() {
-  if (ic_data_.IsNull()) {
-    Register reg;
-    InstructionPattern::DecodeLoadObject(ic_data_load_end_, object_pool_, &reg,
-                                         &ic_data_);
-    ASSERT(reg == R5);
-  }
-  return ic_data_.raw();
-}
-
 RawCode* CallPattern::TargetCode() const {
   return reinterpret_cast<RawCode*>(
       object_pool_.ObjectAt(target_code_pool_index_));
@@ -373,6 +379,24 @@
   // No need to flush the instruction cache, since the code is not modified.
 }
 
+RawObject* ICCallPattern::Data() const {
+  return object_pool_.ObjectAt(data_pool_index_);
+}
+
+void ICCallPattern::SetData(const Object& data) const {
+  ASSERT(data.IsArray() || data.IsICData() || data.IsMegamorphicCache());
+  object_pool_.SetObjectAt(data_pool_index_, data);
+}
+
+RawCode* ICCallPattern::TargetCode() const {
+  return reinterpret_cast<RawCode*>(object_pool_.ObjectAt(target_pool_index_));
+}
+
+void ICCallPattern::SetTargetCode(const Code& target) const {
+  object_pool_.SetObjectAt(target_pool_index_, target);
+  // No need to flush the instruction cache, since the code is not modified.
+}
+
 SwitchableCallPatternBase::SwitchableCallPatternBase(const Code& code)
     : object_pool_(ObjectPool::Handle(code.GetObjectPool())),
       data_pool_index_(-1),
diff --git a/runtime/vm/instructions_arm64.h b/runtime/vm/instructions_arm64.h
index 8550643..6b8b4aa 100644
--- a/runtime/vm/instructions_arm64.h
+++ b/runtime/vm/instructions_arm64.h
@@ -82,7 +82,23 @@
  public:
   CallPattern(uword pc, const Code& code);
 
-  RawICData* IcData();
+  RawCode* TargetCode() const;
+  void SetTargetCode(const Code& target) const;
+
+ private:
+  const ObjectPool& object_pool_;
+
+  intptr_t target_code_pool_index_;
+
+  DISALLOW_COPY_AND_ASSIGN(CallPattern);
+};
+
+class ICCallPattern : public ValueObject {
+ public:
+  ICCallPattern(uword pc, const Code& caller_code);
+
+  RawObject* Data() const;
+  void SetData(const Object& data) const;
 
   RawCode* TargetCode() const;
   void SetTargetCode(const Code& target) const;
@@ -90,13 +106,10 @@
  private:
   const ObjectPool& object_pool_;
 
-  uword end_;
-  uword ic_data_load_end_;
+  intptr_t target_pool_index_;
+  intptr_t data_pool_index_;
 
-  intptr_t target_code_pool_index_;
-  ICData& ic_data_;
-
-  DISALLOW_COPY_AND_ASSIGN(CallPattern);
+  DISALLOW_COPY_AND_ASSIGN(ICCallPattern);
 };
 
 class NativeCallPattern : public ValueObject {
diff --git a/runtime/vm/instructions_dbc.cc b/runtime/vm/instructions_dbc.cc
index b303e95..6d5280b 100644
--- a/runtime/vm/instructions_dbc.cc
+++ b/runtime/vm/instructions_dbc.cc
@@ -54,15 +54,14 @@
 CallPattern::CallPattern(uword pc, const Code& code)
     : object_pool_(ObjectPool::Handle(code.GetObjectPool())),
       end_(pc),
-      ic_data_load_end_(0),
-      target_code_pool_index_(-1),
-      ic_data_(ICData::Handle()) {
+      data_pool_index_(-1),
+      target_pool_index_(-1) {
   ASSERT(code.ContainsInstructionAt(end_));
   const uword call_pc = end_ - sizeof(Instr);
   Instr call_instr = SimulatorBytecode::At(call_pc);
   ASSERT(SimulatorBytecode::IsCallOpcode(call_instr));
-  ic_data_load_end_ = call_pc;
-  target_code_pool_index_ = SimulatorBytecode::DecodeD(call_instr);
+  data_pool_index_ = SimulatorBytecode::DecodeD(call_instr);
+  target_pool_index_ = SimulatorBytecode::DecodeD(call_instr);
 }
 
 NativeCallPattern::NativeCallPattern(uword pc, const Code& code)
@@ -143,21 +142,20 @@
   return GetLoadedObjectAt(pc, pool, obj);
 }
 
-RawICData* CallPattern::IcData() {
-  if (ic_data_.IsNull()) {
-    bool found = GetLoadedObjectAt(ic_data_load_end_, object_pool_, &ic_data_);
-    ASSERT(found);
-  }
-  return ic_data_.raw();
+RawObject* CallPattern::Data() const {
+  return object_pool_.ObjectAt(data_pool_index_);
+}
+
+void CallPattern::SetData(const Object& data) const {
+  object_pool_.SetObjectAt(data_pool_index_, data);
 }
 
 RawCode* CallPattern::TargetCode() const {
-  return reinterpret_cast<RawCode*>(
-      object_pool_.ObjectAt(target_code_pool_index_));
+  return reinterpret_cast<RawCode*>(object_pool_.ObjectAt(target_pool_index_));
 }
 
-void CallPattern::SetTargetCode(const Code& target_code) const {
-  object_pool_.SetObjectAt(target_code_pool_index_, target_code);
+void CallPattern::SetTargetCode(const Code& target) const {
+  object_pool_.SetObjectAt(target_pool_index_, target);
 }
 
 void CallPattern::InsertDeoptCallAt(uword pc) {
diff --git a/runtime/vm/instructions_dbc.h b/runtime/vm/instructions_dbc.h
index 15ac846..3d465fa 100644
--- a/runtime/vm/instructions_dbc.h
+++ b/runtime/vm/instructions_dbc.h
@@ -49,9 +49,10 @@
 
 class CallPattern : public ValueObject {
  public:
-  CallPattern(uword pc, const Code& code);
+  CallPattern(uword pc, const Code& caller_code);
 
-  RawICData* IcData();
+  RawObject* Data() const;
+  void SetData(const Object& data) const;
 
   RawCode* TargetCode() const;
   void SetTargetCode(const Code& code) const;
@@ -62,10 +63,9 @@
   const ObjectPool& object_pool_;
 
   uword end_;
-  uword ic_data_load_end_;
 
-  intptr_t target_code_pool_index_;
-  ICData& ic_data_;
+  intptr_t data_pool_index_;
+  intptr_t target_pool_index_;
 
   DISALLOW_COPY_AND_ASSIGN(CallPattern);
 };
diff --git a/runtime/vm/interpreter.cc b/runtime/vm/interpreter.cc
index 3d9597e..c83e454 100644
--- a/runtime/vm/interpreter.cc
+++ b/runtime/vm/interpreter.cc
@@ -565,6 +565,12 @@
       USE(entrypoint);
       UNIMPLEMENTED();
 #elif defined(USING_SIMULATOR)
+      // We need to beware that bouncing between the interpreter and the
+      // simulator may exhaust the C stack before exhausting either the
+      // interpreter or simulator stacks.
+      if (!thread->os_thread()->HasStackHeadroom()) {
+        thread->SetStackLimit(-1);
+      }
       result = bit_copy<RawObject*, int64_t>(
           Simulator::Current()->Call(reinterpret_cast<intptr_t>(entrypoint),
                                      reinterpret_cast<intptr_t>(code),
@@ -1186,6 +1192,120 @@
   }
 #endif  // PRODUCT
 
+bool Interpreter::CopyParameters(Thread* thread,
+                                 const KBCInstr** pc,
+                                 RawObject*** FP,
+                                 RawObject*** SP,
+                                 const intptr_t num_fixed_params,
+                                 const intptr_t num_opt_pos_params,
+                                 const intptr_t num_opt_named_params) {
+  const intptr_t min_num_pos_args = num_fixed_params;
+  const intptr_t max_num_pos_args = num_fixed_params + num_opt_pos_params;
+
+  // Decode arguments descriptor.
+  const intptr_t arg_count = InterpreterHelpers::ArgDescArgCount(argdesc_);
+  const intptr_t pos_count = InterpreterHelpers::ArgDescPosCount(argdesc_);
+  const intptr_t named_count = (arg_count - pos_count);
+
+  // Check that got the right number of positional parameters.
+  if ((min_num_pos_args > pos_count) || (pos_count > max_num_pos_args)) {
+    return false;
+  }
+
+  // Copy all passed position arguments.
+  RawObject** first_arg = FrameArguments(*FP, arg_count);
+  memmove(*FP, first_arg, pos_count * kWordSize);
+
+  if (num_opt_named_params != 0) {
+    // This is a function with named parameters.
+    // Walk the list of named parameters and their
+    // default values encoded as pairs of LoadConstant instructions that
+    // follows the entry point and find matching values via arguments
+    // descriptor.
+    RawObject** argdesc_data = argdesc_->ptr()->data();
+
+    intptr_t i = 0;  // argument position
+    intptr_t j = 0;  // parameter position
+    while ((j < num_opt_named_params) && (i < named_count)) {
+      // Fetch formal parameter information: name, default value, target slot.
+      const KBCInstr* load_name = *pc;
+      const KBCInstr* load_value = KernelBytecode::Next(load_name);
+      *pc = KernelBytecode::Next(load_value);
+      ASSERT(KernelBytecode::IsLoadConstantOpcode(load_name));
+      ASSERT(KernelBytecode::IsLoadConstantOpcode(load_value));
+      const uint8_t reg = KernelBytecode::DecodeA(load_name);
+      ASSERT(reg == KernelBytecode::DecodeA(load_value));
+
+      RawString* name = static_cast<RawString*>(
+          LOAD_CONSTANT(KernelBytecode::DecodeE(load_name)));
+      if (name == argdesc_data[ArgumentsDescriptor::name_index(i)]) {
+        // Parameter was passed. Fetch passed value.
+        const intptr_t arg_index = Smi::Value(static_cast<RawSmi*>(
+            argdesc_data[ArgumentsDescriptor::position_index(i)]));
+        (*FP)[reg] = first_arg[arg_index];
+        ++i;  // Consume passed argument.
+      } else {
+        // Parameter was not passed. Fetch default value.
+        (*FP)[reg] = LOAD_CONSTANT(KernelBytecode::DecodeE(load_value));
+      }
+      ++j;  // Next formal parameter.
+    }
+
+    // If we have unprocessed formal parameters then initialize them all
+    // using default values.
+    while (j < num_opt_named_params) {
+      const KBCInstr* load_name = *pc;
+      const KBCInstr* load_value = KernelBytecode::Next(load_name);
+      *pc = KernelBytecode::Next(load_value);
+      ASSERT(KernelBytecode::IsLoadConstantOpcode(load_name));
+      ASSERT(KernelBytecode::IsLoadConstantOpcode(load_value));
+      const uint8_t reg = KernelBytecode::DecodeA(load_name);
+      ASSERT(reg == KernelBytecode::DecodeA(load_value));
+
+      (*FP)[reg] = LOAD_CONSTANT(KernelBytecode::DecodeE(load_value));
+      ++j;
+    }
+
+    // If we have unprocessed passed arguments that means we have mismatch
+    // between formal parameters and concrete arguments. This can only
+    // occur if the current function is a closure.
+    if (i < named_count) {
+      return false;
+    }
+
+    // SP points past copied arguments.
+    *SP = *FP + num_fixed_params + num_opt_named_params - 1;
+  } else {
+    ASSERT(num_opt_pos_params != 0);
+    if (named_count != 0) {
+      // Function can't have both named and optional positional parameters.
+      // This kind of mismatch can only occur if the current function
+      // is a closure.
+      return false;
+    }
+
+    // Process the list of default values encoded as a sequence of
+    // LoadConstant instructions after EntryOpt bytecode.
+    // Execute only those that correspond to parameters that were not passed.
+    for (intptr_t i = num_fixed_params; i < pos_count; ++i) {
+      ASSERT(KernelBytecode::IsLoadConstantOpcode(*pc));
+      *pc = KernelBytecode::Next(*pc);
+    }
+    for (intptr_t i = pos_count; i < max_num_pos_args; ++i) {
+      const KBCInstr* load_value = *pc;
+      *pc = KernelBytecode::Next(load_value);
+      ASSERT(KernelBytecode::IsLoadConstantOpcode(load_value));
+      ASSERT(KernelBytecode::DecodeA(load_value) == i);
+      (*FP)[i] = LOAD_CONSTANT(KernelBytecode::DecodeE(load_value));
+    }
+
+    // SP points past the last copied parameter.
+    *SP = *FP + max_num_pos_args - 1;
+  }
+
+  return true;
+}
+
 bool Interpreter::AssertAssignable(Thread* thread,
                                    const KBCInstr* pc,
                                    RawObject** FP,
@@ -1651,114 +1771,11 @@
 
   {
     BYTECODE(EntryOptional, A_B_C);
-    const intptr_t num_fixed_params = rA;
-    const intptr_t num_opt_pos_params = rB;
-    const intptr_t num_opt_named_params = rC;
-    const intptr_t min_num_pos_args = num_fixed_params;
-    const intptr_t max_num_pos_args = num_fixed_params + num_opt_pos_params;
-
-    // Decode arguments descriptor.
-    const intptr_t arg_count = InterpreterHelpers::ArgDescArgCount(argdesc_);
-    const intptr_t pos_count = InterpreterHelpers::ArgDescPosCount(argdesc_);
-    const intptr_t named_count = (arg_count - pos_count);
-
-    // Check that got the right number of positional parameters.
-    if ((min_num_pos_args > pos_count) || (pos_count > max_num_pos_args)) {
+    if (CopyParameters(thread, &pc, &FP, &SP, rA, rB, rC)) {
+      DISPATCH();
+    } else {
       goto NoSuchMethodFromPrologue;
     }
-
-    // Copy all passed position arguments.
-    RawObject** first_arg = FrameArguments(FP, arg_count);
-    memmove(FP, first_arg, pos_count * kWordSize);
-
-    if (num_opt_named_params != 0) {
-      // This is a function with named parameters.
-      // Walk the list of named parameters and their
-      // default values encoded as pairs of LoadConstant instructions that
-      // follows the entry point and find matching values via arguments
-      // descriptor.
-      RawObject** argdesc_data = argdesc_->ptr()->data();
-
-      intptr_t i = 0;  // argument position
-      intptr_t j = 0;  // parameter position
-      while ((j < num_opt_named_params) && (i < named_count)) {
-        // Fetch formal parameter information: name, default value, target slot.
-        const KBCInstr* load_name = pc;
-        const KBCInstr* load_value = KernelBytecode::Next(load_name);
-        pc = KernelBytecode::Next(load_value);
-        ASSERT(KernelBytecode::IsLoadConstantOpcode(load_name));
-        ASSERT(KernelBytecode::IsLoadConstantOpcode(load_value));
-        const uint8_t reg = KernelBytecode::DecodeA(load_name);
-        ASSERT(reg == KernelBytecode::DecodeA(load_value));
-
-        RawString* name = static_cast<RawString*>(
-            LOAD_CONSTANT(KernelBytecode::DecodeE(load_name)));
-        if (name == argdesc_data[ArgumentsDescriptor::name_index(i)]) {
-          // Parameter was passed. Fetch passed value.
-          const intptr_t arg_index = Smi::Value(static_cast<RawSmi*>(
-              argdesc_data[ArgumentsDescriptor::position_index(i)]));
-          FP[reg] = first_arg[arg_index];
-          ++i;  // Consume passed argument.
-        } else {
-          // Parameter was not passed. Fetch default value.
-          FP[reg] = LOAD_CONSTANT(KernelBytecode::DecodeE(load_value));
-        }
-        ++j;  // Next formal parameter.
-      }
-
-      // If we have unprocessed formal parameters then initialize them all
-      // using default values.
-      while (j < num_opt_named_params) {
-        const KBCInstr* load_name = pc;
-        const KBCInstr* load_value = KernelBytecode::Next(load_name);
-        pc = KernelBytecode::Next(load_value);
-        ASSERT(KernelBytecode::IsLoadConstantOpcode(load_name));
-        ASSERT(KernelBytecode::IsLoadConstantOpcode(load_value));
-        const uint8_t reg = KernelBytecode::DecodeA(load_name);
-        ASSERT(reg == KernelBytecode::DecodeA(load_value));
-
-        FP[reg] = LOAD_CONSTANT(KernelBytecode::DecodeE(load_value));
-        ++j;
-      }
-
-      // If we have unprocessed passed arguments that means we have mismatch
-      // between formal parameters and concrete arguments. This can only
-      // occur if the current function is a closure.
-      if (i < named_count) {
-        goto NoSuchMethodFromPrologue;
-      }
-
-      // SP points past copied arguments.
-      SP = FP + num_fixed_params + num_opt_named_params - 1;
-    } else {
-      ASSERT(num_opt_pos_params != 0);
-      if (named_count != 0) {
-        // Function can't have both named and optional positional parameters.
-        // This kind of mismatch can only occur if the current function
-        // is a closure.
-        goto NoSuchMethodFromPrologue;
-      }
-
-      // Process the list of default values encoded as a sequence of
-      // LoadConstant instructions after EntryOpt bytecode.
-      // Execute only those that correspond to parameters that were not passed.
-      for (intptr_t i = num_fixed_params; i < pos_count; ++i) {
-        ASSERT(KernelBytecode::IsLoadConstantOpcode(pc));
-        pc = KernelBytecode::Next(pc);
-      }
-      for (intptr_t i = pos_count; i < max_num_pos_args; ++i) {
-        const KBCInstr* load_value = pc;
-        pc = KernelBytecode::Next(load_value);
-        ASSERT(KernelBytecode::IsLoadConstantOpcode(load_value));
-        ASSERT(KernelBytecode::DecodeA(load_value) == i);
-        FP[i] = LOAD_CONSTANT(KernelBytecode::DecodeE(load_value));
-      }
-
-      // SP points past the last copied parameter.
-      SP = FP + max_num_pos_args - 1;
-    }
-
-    DISPATCH();
   }
 
   {
@@ -1887,6 +1904,7 @@
 
   {
     BYTECODE(Throw, A);
+    DEBUG_CHECK;
     {
       SP[1] = 0;  // Space for result.
       Exit(thread, FP, SP + 2, pc);
@@ -1951,12 +1969,14 @@
 
   {
     BYTECODE(StoreLocal, X);
+    DEBUG_CHECK;
     FP[rX] = *SP;
     DISPATCH();
   }
 
   {
     BYTECODE(PopLocal, X);
+    DEBUG_CHECK;
     FP[rX] = *SP--;
     DISPATCH();
   }
@@ -2321,6 +2341,7 @@
 
   {
     BYTECODE(StoreStaticTOS, D);
+    DEBUG_CHECK;
     RawField* field = reinterpret_cast<RawField*>(LOAD_CONSTANT(rD));
     RawInstance* value = static_cast<RawInstance*>(*SP--);
     field->StorePointer(&field->ptr()->value_.static_value_, value, thread);
@@ -2658,6 +2679,7 @@
 
   {
     BYTECODE(Jump, T);
+    DEBUG_CHECK;
     LOAD_JUMP_TARGET();
     DISPATCH();
   }
@@ -3464,8 +3486,101 @@
     RawFunction* function = FrameFunction(FP);
     ASSERT(Function::kind(function) ==
            RawFunction::kDynamicInvocationForwarder);
-    UNIMPLEMENTED();
-    DISPATCH();
+
+    BUMP_USAGE_COUNTER_ON_ENTRY(function);
+
+    RawArray* checks = Array::RawCast(function->ptr()->data_);
+    RawFunction* target = Function::RawCast(checks->ptr()->data()[0]);
+    ASSERT(Function::kind(target) != RawFunction::kDynamicInvocationForwarder);
+    RawBytecode* target_bytecode = target->ptr()->bytecode_;
+    ASSERT(target_bytecode != Bytecode::null());
+    ASSERT(target_bytecode->IsBytecode());
+
+    const KBCInstr* pc2 = reinterpret_cast<const KBCInstr*>(
+        target_bytecode->ptr()->instructions_);
+    if (KernelBytecode::IsEntryOptionalOpcode(pc2)) {
+      pp_ = target_bytecode->ptr()->object_pool_;
+      uint32_t rA, rB, rC;
+      rA = KernelBytecode::DecodeA(pc2);
+      rB = KernelBytecode::DecodeB(pc2);
+      rC = KernelBytecode::DecodeC(pc2);
+      pc2 = KernelBytecode::Next(pc2);
+      if (!CopyParameters(thread, &pc2, &FP, &SP, rA, rB, rC)) {
+        goto NoSuchMethodFromPrologue;
+      }
+    }
+
+    intptr_t len = Smi::Value(checks->ptr()->length_);
+    SP[1] = checks;
+    SP[2] = argdesc_;
+
+    const intptr_t type_args_len =
+        InterpreterHelpers::ArgDescTypeArgsLen(argdesc_);
+    const intptr_t receiver_idx = type_args_len > 0 ? 1 : 0;
+    const intptr_t argc =
+        InterpreterHelpers::ArgDescArgCount(argdesc_) + receiver_idx;
+
+    RawInstance* receiver =
+        Instance::RawCast(FrameArguments(FP, argc)[receiver_idx]);
+    SP[5] = InterpreterHelpers::GetTypeArguments(thread, receiver);
+
+    if (type_args_len > 0) {
+      SP[6] = FrameArguments(FP, argc)[0];
+    } else {
+      SP[6] = TypeArguments::RawCast(checks->ptr()->data()[1]);
+      if (SP[5] != null_value && SP[6] != null_value) {
+        SP[7] = SP[6];       // type_arguments
+        SP[8] = SP[5];       // instantiator_type_args
+        SP[9] = null_value;  // function_type_args
+        Exit(thread, FP, SP + 10, pc);
+        NativeArguments args(thread, 3, SP + 7, SP + 7);
+        INVOKE_RUNTIME(DRT_InstantiateTypeArguments, args);
+        SP[6] = SP[7];
+      }
+    }
+
+    for (intptr_t i = 2; i < len; i++) {
+      RawParameterTypeCheck* check =
+          ParameterTypeCheck::RawCast(checks->ptr()->data()[i]);
+
+      if (LIKELY(check->ptr()->index_ != 0)) {
+        ASSERT(&FP[check->ptr()->index_] <= SP);
+        SP[3] = Instance::RawCast(FP[check->ptr()->index_]);
+        if (SP[3] == null_value) {
+          continue;  // Not handled by AssertAssignable for some reason...
+        }
+        SP[4] = check->ptr()->type_or_bound_;
+        // SP[5]: Instantiator type args.
+        // SP[6]: Function type args.
+        SP[7] = check->ptr()->name_;
+        if (!AssertAssignable(thread, pc, FP, SP, SP + 3,
+                              check->ptr()->cache_)) {
+          HANDLE_EXCEPTION;
+        }
+      } else {
+        SP[3] = 0;
+        SP[4] = 0;
+        // SP[5]: Instantiator type args.
+        // SP[6]: Function type args.
+        SP[7] = check->ptr()->param_;
+        SP[8] = check->ptr()->type_or_bound_;
+        SP[9] = check->ptr()->name_;
+        SP[10] = 0;
+        Exit(thread, FP, SP + 11, pc);
+        NativeArguments native_args(thread, 5, SP + 5, SP + 10);
+        INVOKE_RUNTIME(DRT_SubtypeCheck, native_args);
+      }
+
+      checks = Array::RawCast(SP[1]);  // Reload after runtime call.
+    }
+
+    target = Function::RawCast(checks->ptr()->data()[0]);
+    argdesc_ = Array::RawCast(SP[2]);
+
+    SP = FP - 1;  // Unmarshall optional parameters.
+
+    SP[1] = target;
+    goto TailCallSP1;
   }
 
   {
diff --git a/runtime/vm/interpreter.h b/runtime/vm/interpreter.h
index a47eecc..a8e393f 100644
--- a/runtime/vm/interpreter.h
+++ b/runtime/vm/interpreter.h
@@ -209,6 +209,14 @@
                      RawObject*** SP,
                      bool optimized);
 
+  bool CopyParameters(Thread* thread,
+                      const KBCInstr** pc,
+                      RawObject*** FP,
+                      RawObject*** SP,
+                      const intptr_t num_fixed_params,
+                      const intptr_t num_opt_pos_params,
+                      const intptr_t num_opt_named_params);
+
   bool AssertAssignable(Thread* thread,
                         const KBCInstr* pc,
                         RawObject** FP,
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index e68d40c..11e65e9 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -892,8 +892,7 @@
           NOT_IN_PRODUCT("Isolate::type_canonicalization_mutex_")),
       constant_canonicalization_mutex_(
           NOT_IN_PRODUCT("Isolate::constant_canonicalization_mutex_")),
-      megamorphic_lookup_mutex_(
-          NOT_IN_PRODUCT("Isolate::megamorphic_lookup_mutex_")),
+      megamorphic_mutex_(NOT_IN_PRODUCT("Isolate::megamorphic_mutex_")),
       kernel_data_lib_cache_mutex_(
           NOT_IN_PRODUCT("Isolate::kernel_data_lib_cache_mutex_")),
       kernel_data_class_cache_mutex_(
@@ -979,7 +978,6 @@
       nullptr;  // Fail fast if we send messages to a dead isolate.
   ASSERT(deopt_context_ ==
          nullptr);  // No deopt in progress when isolate deleted.
-  delete spawn_state_;
   ASSERT(spawn_count_ == 0);
   delete safepoint_handler_;
   delete thread_registry_;
@@ -1758,11 +1756,6 @@
     }
   }
 
-#if !defined(PRODUCT)
-  // Clean up debugger resources.
-  debugger()->Shutdown();
-#endif
-
   // Close all the ports owned by this isolate.
   PortMap::ClosePorts(message_handler());
 
@@ -1861,6 +1854,9 @@
     HandleScope handle_scope(thread);
     ServiceIsolate::SendIsolateShutdownMessage();
     KernelIsolate::NotifyAboutIsolateShutdown(this);
+#if !defined(PRODUCT)
+    debugger()->Shutdown();
+#endif
   }
 
   if (heap_ != nullptr) {
@@ -2554,7 +2550,7 @@
     pause_loop_monitor_ = new Monitor();
   }
   Dart_EnterScope();
-  MonitorLocker ml(pause_loop_monitor_);
+  MonitorLocker ml(pause_loop_monitor_, false);
 
   Dart_MessageNotifyCallback saved_notify_callback = message_notify_callback();
   set_message_notify_callback(Isolate::WakePauseEventHandler);
diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h
index 914a40f..5d76a92 100644
--- a/runtime/vm/isolate.h
+++ b/runtime/vm/isolate.h
@@ -10,6 +10,7 @@
 #endif
 
 #include <memory>
+#include <utility>
 
 #include "include/dart_api.h"
 #include "platform/assert.h"
@@ -346,8 +347,10 @@
     isolate_flags_ = CompactionInProgressBit::update(value, isolate_flags_);
   }
 
-  IsolateSpawnState* spawn_state() const { return spawn_state_; }
-  void set_spawn_state(IsolateSpawnState* value) { spawn_state_ = value; }
+  IsolateSpawnState* spawn_state() const { return spawn_state_.get(); }
+  void set_spawn_state(std::unique_ptr<IsolateSpawnState> value) {
+    spawn_state_ = std::move(value);
+  }
 
   Mutex* mutex() { return &mutex_; }
   Mutex* symbols_mutex() { return &symbols_mutex_; }
@@ -355,7 +358,7 @@
   Mutex* constant_canonicalization_mutex() {
     return &constant_canonicalization_mutex_;
   }
-  Mutex* megamorphic_lookup_mutex() { return &megamorphic_lookup_mutex_; }
+  Mutex* megamorphic_mutex() { return &megamorphic_mutex_; }
 
   Mutex* kernel_data_lib_cache_mutex() { return &kernel_data_lib_cache_mutex_; }
   Mutex* kernel_data_class_cache_mutex() {
@@ -795,6 +798,13 @@
         UsingNewBytecodeInstructionsBit::update(value, isolate_flags_);
   }
 
+  bool has_attempted_stepping() const {
+    return HasAttemptedSteppingBit::decode(isolate_flags_);
+  }
+  void set_has_attempted_stepping(bool value) {
+    isolate_flags_ = HasAttemptedSteppingBit::update(value, isolate_flags_);
+  }
+
   static void KillAllIsolates(LibMsgId msg_id);
   static void KillIfExists(Isolate* isolate, LibMsgId msg_id);
 
@@ -909,6 +919,7 @@
   V(RemappingCids)                                                             \
   V(ResumeRequest)                                                             \
   V(HasAttemptedReload)                                                        \
+  V(HasAttemptedStepping)                                                      \
   V(ShouldPausePostServiceRequest)                                             \
   V(EnableTypeChecks)                                                          \
   V(EnableAsserts)                                                             \
@@ -1019,12 +1030,13 @@
   Mutex symbols_mutex_;  // Protects concurrent access to the symbol table.
   Mutex type_canonicalization_mutex_;      // Protects type canonicalization.
   Mutex constant_canonicalization_mutex_;  // Protects const canonicalization.
-  Mutex megamorphic_lookup_mutex_;         // Protects megamorphic table lookup.
+  Mutex megamorphic_mutex_;  // Protects the table of megamorphic caches and
+                             // their entries.
   Mutex kernel_data_lib_cache_mutex_;
   Mutex kernel_data_class_cache_mutex_;
   Mutex kernel_constants_mutex_;
   MessageHandler* message_handler_ = nullptr;
-  IsolateSpawnState* spawn_state_ = nullptr;
+  std::unique_ptr<IsolateSpawnState> spawn_state_;
   intptr_t defer_finalization_count_ = 0;
   MallocGrowableArray<PendingLazyDeopt>* pending_deopts_;
   DeoptContext* deopt_context_ = nullptr;
diff --git a/runtime/vm/isolate_reload.cc b/runtime/vm/isolate_reload.cc
index b153644..75273e8 100644
--- a/runtime/vm/isolate_reload.cc
+++ b/runtime/vm/isolate_reload.cc
@@ -1907,8 +1907,10 @@
         function = code.function();
         code = function.unoptimized_code();
         ASSERT(!code.IsNull());
+        code.ResetSwitchableCalls(zone);
         code.ResetICDatas(zone);
       } else {
+        code.ResetSwitchableCalls(zone);
         code.ResetICDatas(zone);
       }
     }
@@ -2029,6 +2031,7 @@
         if (!stub_code) {
           // We are preserving the unoptimized code, fill all ICData arrays with
           // the sentinel values so that we have no stale type feedback.
+          code.ResetSwitchableCalls(zone);
           code.ResetICDatas(zone);
         }
         if (!bytecode.IsNull()) {
diff --git a/runtime/vm/isolate_test.cc b/runtime/vm/isolate_test.cc
index b019598c..2b0f84f 100644
--- a/runtime/vm/isolate_test.cc
+++ b/runtime/vm/isolate_test.cc
@@ -130,7 +130,7 @@
                         isolate->heap()->barrier_done());
   // Start all tasks. They will busy-wait until interrupted in the first round.
   for (intptr_t task = 0; task < InterruptChecker::kTaskCount; task++) {
-    Dart::thread_pool()->Run(new InterruptChecker(thread, &barrier));
+    Dart::thread_pool()->Run<InterruptChecker>(thread, &barrier);
   }
   // Wait for all tasks to get ready for the first round.
   barrier.Sync();
diff --git a/runtime/vm/json_stream.cc b/runtime/vm/json_stream.cc
index aeaa9d0..30cddd2 100644
--- a/runtime/vm/json_stream.cc
+++ b/runtime/vm/json_stream.cc
@@ -136,6 +136,9 @@
       return "File system does not exist";
     case kFileDoesNotExist:
       return "File does not exist";
+    case kInvalidTimelineRequest:
+      return "The timeline related request could not be completed due to the "
+             "current configuration";
     default:
       return "Extension error";
   }
diff --git a/runtime/vm/json_stream.h b/runtime/vm/json_stream.h
index 98d39f3..b9fdef7 100644
--- a/runtime/vm/json_stream.h
+++ b/runtime/vm/json_stream.h
@@ -61,6 +61,7 @@
   kServiceAlreadyRegistered = 111,
   kServiceDisappeared = 112,
   kExpressionCompilationError = 113,
+  kInvalidTimelineRequest = 114,
 
   // Experimental (used in private rpcs).
   kFileSystemAlreadyExists = 1001,
diff --git a/runtime/vm/kernel.cc b/runtime/vm/kernel.cc
index ac4a5c7..160e8a9 100644
--- a/runtime/vm/kernel.cc
+++ b/runtime/vm/kernel.cc
@@ -11,6 +11,7 @@
 #include "vm/longjump.h"
 #include "vm/object_store.h"
 #include "vm/parser.h"  // For Parser::kParameter* constants.
+#include "vm/stack_frame.h"
 
 #if !defined(DART_PRECOMPILED_RUNTIME)
 
@@ -264,7 +265,6 @@
     Zone* zone,
     GrowableArray<intptr_t>* token_positions,
     GrowableArray<intptr_t>* yield_positions) {
-  ASSERT(bytecode.HasSourcePositions());
   BytecodeSourcePositionsIterator iter(zone, bytecode);
   while (iter.MoveNext()) {
     const TokenPosition pos = iter.TokenPos();
@@ -291,7 +291,7 @@
   }
   Bytecode& bytecode = Bytecode::Handle(zone, function.bytecode());
   ASSERT(!bytecode.IsNull());
-  if (bytecode.HasSourcePositions()) {
+  if (bytecode.HasSourcePositions() && !function.IsLocalFunction()) {
     CollectBytecodeTokenPositions(bytecode, zone, token_positions,
                                   yield_positions);
     // Find closure functions in the object pool.
@@ -307,10 +307,13 @@
       if (object.IsFunction()) {
         closure ^= object.raw();
         if ((closure.kind() == RawFunction::kClosureFunction) &&
-            (closure.raw() != function.raw())) {
+            (closure.IsLocalFunction())) {
           bytecode = closure.bytecode();
-          CollectBytecodeTokenPositions(bytecode, zone, token_positions,
-                                        yield_positions);
+          ASSERT(!bytecode.IsNull());
+          if (bytecode.HasSourcePositions()) {
+            CollectBytecodeTokenPositions(bytecode, zone, token_positions,
+                                          yield_positions);
+          }
         }
       }
     }
@@ -349,12 +352,22 @@
         if (klass.script() == interesting_script.raw()) {
           token_positions.Add(klass.token_pos().value());
         }
+        // If class is declared in bytecode, its members should be loaded
+        // (via class finalization) before their token positions could be
+        // collected.
+        if (klass.is_declared_in_bytecode() && !klass.is_finalized()) {
+          const Error& error =
+              Error::Handle(zone, klass.EnsureIsFinalized(thread));
+          if (!error.IsNull()) {
+            Exceptions::PropagateError(error);
+          }
+        }
         if (klass.is_finalized()) {
           temp_array = klass.fields();
           for (intptr_t i = 0; i < temp_array.Length(); ++i) {
             temp_field ^= temp_array.At(i);
-            // TODO(alexmarkov): collect token positions from bytecode
-            if (temp_field.is_declared_in_bytecode() ||
+            // TODO(regis): Factorize field handling code.
+            if (!temp_field.is_declared_in_bytecode() &&
                 temp_field.kernel_offset() <= 0) {
               // Skip artificially injected fields.
               continue;
@@ -363,16 +376,25 @@
             if (entry_script.raw() != interesting_script.raw()) {
               continue;
             }
-            data = temp_field.KernelData();
-            CollectKernelDataTokenPositions(
-                data, interesting_script, entry_script,
-                temp_field.kernel_offset(),
-                temp_field.KernelDataProgramOffset(), zone, &helper,
-                &token_positions, &yield_positions);
+            if (temp_field.is_declared_in_bytecode()) {
+              if (temp_field.is_static() && temp_field.has_initializer()) {
+                temp_function = temp_field.EnsureInitializerFunction();
+                CollectBytecodeFunctionTokenPositions(
+                    temp_function, &token_positions, &yield_positions);
+              }
+            } else {
+              data = temp_field.KernelData();
+              CollectKernelDataTokenPositions(
+                  data, interesting_script, entry_script,
+                  temp_field.kernel_offset(),
+                  temp_field.KernelDataProgramOffset(), zone, &helper,
+                  &token_positions, &yield_positions);
+            }
           }
           temp_array = klass.functions();
           for (intptr_t i = 0; i < temp_array.Length(); ++i) {
             temp_function ^= temp_array.At(i);
+            // TODO(regis): Factorize function handling code.
             entry_script = temp_function.script();
             if (entry_script.raw() != interesting_script.raw()) {
               continue;
@@ -391,6 +413,7 @@
           }
         } else {
           // Class isn't finalized yet: read the data attached to it.
+          ASSERT(!klass.is_declared_in_bytecode());
           ASSERT(klass.kernel_offset() > 0);
           data = lib.kernel_data();
           ASSERT(!data.IsNull());
@@ -409,6 +432,7 @@
         }
       } else if (entry.IsFunction()) {
         temp_function ^= entry.raw();
+        // TODO(regis): Factorize function handling code.
         entry_script = temp_function.script();
         if (entry_script.raw() != interesting_script.raw()) {
           continue;
@@ -426,8 +450,8 @@
         }
       } else if (entry.IsField()) {
         const Field& field = Field::Cast(entry);
-        // TODO(alexmarkov): collect token positions from bytecode
-        if (field.is_declared_in_bytecode() || field.kernel_offset() <= 0) {
+        // TODO(regis): Factorize field handling code.
+        if (!field.is_declared_in_bytecode() && field.kernel_offset() <= 0) {
           // Skip artificially injected fields.
           continue;
         }
@@ -435,11 +459,19 @@
         if (entry_script.raw() != interesting_script.raw()) {
           continue;
         }
-        data = field.KernelData();
-        CollectKernelDataTokenPositions(
-            data, interesting_script, entry_script, field.kernel_offset(),
-            field.KernelDataProgramOffset(), zone, &helper, &token_positions,
-            &yield_positions);
+        if (field.is_declared_in_bytecode()) {
+          if (field.is_static() && field.has_initializer()) {
+            temp_function = field.EnsureInitializerFunction();
+            CollectBytecodeFunctionTokenPositions(
+                temp_function, &token_positions, &yield_positions);
+          }
+        } else {
+          data = field.KernelData();
+          CollectKernelDataTokenPositions(
+              data, interesting_script, entry_script, field.kernel_offset(),
+              field.KernelDataProgramOffset(), zone, &helper, &token_positions,
+              &yield_positions);
+        }
       }
     }
   }
diff --git a/runtime/vm/kernel.h b/runtime/vm/kernel.h
index 4e66b89..32f975f 100644
--- a/runtime/vm/kernel.h
+++ b/runtime/vm/kernel.h
@@ -208,6 +208,11 @@
 // as such function already checks all of its parameters.
 bool NeedsDynamicInvocationForwarder(const Function& function);
 
+// Returns a list of ParameterTypeChecks needed by a dynamic invocation
+// forwarder that targets [function]. Indices in these checks correspond to
+// bytecode frame indices.
+RawArray* CollectDynamicInvocationChecks(const Function& function);
+
 ProcedureAttributesMetadata ProcedureAttributesOf(const Function& function,
                                                   Zone* zone);
 
diff --git a/runtime/vm/kernel_binary.cc b/runtime/vm/kernel_binary.cc
index d3f2838..d974b31 100644
--- a/runtime/vm/kernel_binary.cc
+++ b/runtime/vm/kernel_binary.cc
@@ -29,6 +29,50 @@
   return "Unknown";
 }
 
+RawTypedData* Reader::ReadLineStartsData(intptr_t line_start_count) {
+  TypedData& line_starts_data = TypedData::Handle(
+      TypedData::New(kTypedDataInt8ArrayCid, line_start_count, Heap::kOld));
+
+  const intptr_t start_offset = offset();
+  intptr_t i = 0;
+  for (; i < line_start_count; ++i) {
+    const intptr_t delta = ReadUInt();
+    if (delta > kMaxInt8) {
+      break;
+    }
+    line_starts_data.SetInt8(i, static_cast<int8_t>(delta));
+  }
+
+  if (i < line_start_count) {
+    // Slow path: choose representation between Int16 and Int32 typed data.
+    set_offset(start_offset);
+    intptr_t max_delta = 0;
+    for (intptr_t i = 0; i < line_start_count; ++i) {
+      const intptr_t delta = ReadUInt();
+      if (delta > max_delta) {
+        max_delta = delta;
+      }
+    }
+
+    ASSERT(max_delta > kMaxInt8);
+    const intptr_t cid = (max_delta <= kMaxInt16) ? kTypedDataInt16ArrayCid
+                                                  : kTypedDataInt32ArrayCid;
+    line_starts_data = TypedData::New(cid, line_start_count, Heap::kOld);
+
+    set_offset(start_offset);
+    for (intptr_t i = 0; i < line_start_count; ++i) {
+      const intptr_t delta = ReadUInt();
+      if (cid == kTypedDataInt16ArrayCid) {
+        line_starts_data.SetInt16(i << 1, static_cast<int16_t>(delta));
+      } else {
+        line_starts_data.SetInt32(i << 2, delta);
+      }
+    }
+  }
+
+  return line_starts_data.raw();
+}
+
 const char* kKernelInvalidFilesize =
     "File size is too small to be a valid kernel file";
 const char* kKernelInvalidMagicIdentifier = "Invalid magic identifier";
diff --git a/runtime/vm/kernel_binary.h b/runtime/vm/kernel_binary.h
index 760c6ac..00c119f 100644
--- a/runtime/vm/kernel_binary.h
+++ b/runtime/vm/kernel_binary.h
@@ -20,7 +20,7 @@
 
 // Both version numbers are inclusive.
 static const uint32_t kMinSupportedKernelFormatVersion = 18;
-static const uint32_t kMaxSupportedKernelFormatVersion = 25;
+static const uint32_t kMaxSupportedKernelFormatVersion = 26;
 
 // Keep in sync with package:kernel/lib/binary/tag.dart
 #define KERNEL_TAG_LIST(V)                                                     \
@@ -366,6 +366,8 @@
     return &buffer()[offset];
   }
 
+  RawTypedData* ReadLineStartsData(intptr_t line_start_count);
+
  private:
   const uint8_t* buffer() const {
     if (raw_buffer_ != NULL) {
diff --git a/runtime/vm/kernel_isolate.cc b/runtime/vm/kernel_isolate.cc
index abf5036..e1b8c7c 100644
--- a/runtime/vm/kernel_isolate.cc
+++ b/runtime/vm/kernel_isolate.cc
@@ -232,7 +232,7 @@
     KernelIsolate::InitializingFailed();
     return;
   }
-  bool task_started = Dart::thread_pool()->Run(new RunKernelTask());
+  bool task_started = Dart::thread_pool()->Run<RunKernelTask>();
   ASSERT(task_started);
 }
 
diff --git a/runtime/vm/kernel_loader.cc b/runtime/vm/kernel_loader.cc
index b5001d1..d37c67d 100644
--- a/runtime/vm/kernel_loader.cc
+++ b/runtime/vm/kernel_loader.cc
@@ -659,6 +659,7 @@
 
   for (intptr_t i = 0; i < length; ++i) {
     library ^= potential_extension_libraries_.At(i);
+    ASSERT(!library.is_declared_in_bytecode());
     helper_.SetOffset(library.kernel_offset());
 
     LibraryHelper library_helper(&helper_);
@@ -729,11 +730,17 @@
 
   LongJumpScope jump;
   if (setjmp(*jump.Set()) == 0) {
-    // Note that `problemsAsJson` on Component is implicitly skipped.
-    const intptr_t length = program_->library_count();
-    Object& last_library = Library::Handle(Z);
-    for (intptr_t i = 0; i < length; i++) {
-      last_library = LoadLibrary(i);
+    bool libraries_loaded = false;
+    if (FLAG_enable_interpreter || FLAG_use_bytecode_compiler) {
+      libraries_loaded = bytecode_metadata_helper_.ReadLibraries();
+    }
+
+    if (!libraries_loaded) {
+      // Note that `problemsAsJson` on Component is implicitly skipped.
+      const intptr_t length = program_->library_count();
+      for (intptr_t i = 0; i < length; i++) {
+        LoadLibrary(i);
+      }
     }
 
     if (process_pending_classes) {
@@ -797,6 +804,26 @@
   return Thread::Current()->StealStickyError();
 }
 
+void KernelLoader::LoadLibrary(const Library& library) {
+  ASSERT(!library.Loaded());
+
+  if (FLAG_enable_interpreter || FLAG_use_bytecode_compiler) {
+    bytecode_metadata_helper_.ReadLibrary(library);
+    if (library.Loaded()) {
+      return;
+    }
+  }
+  const auto& uri = String::Handle(Z, library.url());
+  const intptr_t num_libraries = program_->library_count();
+  for (intptr_t i = 0; i < num_libraries; ++i) {
+    const String& library_uri = LibraryUri(i);
+    if (library_uri.Equals(uri)) {
+      LoadLibrary(i);
+      return;
+    }
+  }
+}
+
 RawObject* KernelLoader::LoadExpressionEvaluationFunction(
     const String& library_url,
     const String& klass) {
@@ -1067,12 +1094,12 @@
   LoadLibraryImportsAndExports(&library, toplevel_class);
   library_helper.SetJustRead(LibraryHelper::kDependencies);
 
-  const GrowableObjectArray& classes =
-      GrowableObjectArray::Handle(Z, I->object_store()->pending_classes());
-
   // Everything up til the classes are skipped implicitly, and library_helper
   // is no longer used.
 
+  const GrowableObjectArray& classes =
+      GrowableObjectArray::Handle(Z, I->object_store()->pending_classes());
+
   // Load all classes.
   intptr_t next_class_offset = library_index.ClassOffset(0);
   Class& klass = Class::Handle(Z);
@@ -1092,12 +1119,10 @@
   if (FLAG_enable_mirrors && annotation_count > 0) {
     ASSERT(annotations_kernel_offset > 0);
     library.AddLibraryMetadata(toplevel_class, TokenPosition::kNoSource,
-                               annotations_kernel_offset);
+                               annotations_kernel_offset, 0);
   }
 
   if (register_class) {
-    classes.Add(toplevel_class, Heap::kOld);
-
     if (library_index.HasSourceReferences()) {
       helper_.SetOffset(library_index.SourceReferencesOffset());
       intptr_t count = helper_.ReadUInt();
@@ -1129,6 +1154,9 @@
   ActiveClassScope active_class_scope(&active_class_, &toplevel_class);
 
   if (FLAG_enable_interpreter || FLAG_use_bytecode_compiler) {
+    static_assert(KernelBytecode::kMinSupportedBytecodeFormatVersion < 10,
+                  "Cleanup support for old bytecode format versions");
+    ASSERT(!toplevel_class.is_declared_in_bytecode());
     if (bytecode_metadata_helper_.ReadMembers(library_kernel_offset_,
                                               toplevel_class, false)) {
       ASSERT(toplevel_class.is_loaded());
@@ -1342,6 +1370,10 @@
 void KernelLoader::LoadPreliminaryClass(ClassHelper* class_helper,
                                         intptr_t type_parameter_count) {
   const Class* klass = active_class_.klass;
+
+  // Enable access to type_parameters().
+  klass->set_is_declaration_loaded();
+
   // Note: This assumes that ClassHelper is exactly at the position where
   // the length of the type parameters have been read, and that the order in
   // the binary is as follows: [...], kTypeParameters, kSuperClass, kMixinType,
@@ -1379,8 +1411,6 @@
   if (class_helper->is_transformed_mixin_application()) {
     klass->set_is_transformed_mixin_application();
   }
-
-  klass->set_is_declaration_loaded();
 }
 
 void KernelLoader::LoadClass(const Library& library,
@@ -1443,7 +1473,7 @@
   if ((FLAG_enable_mirrors || has_pragma_annotation) && annotation_count > 0) {
     library.AddClassMetadata(*out_class, toplevel_class,
                              TokenPosition::kNoSource,
-                             class_offset - correction_offset_);
+                             class_offset - correction_offset_, 0);
   }
 
   // We do not register expression evaluation classes with the VM:
@@ -1474,17 +1504,15 @@
 
   ActiveClassScope active_class_scope(&active_class_, &klass);
 
-  bool discard_fields = false;
-  if (library.raw() == Library::InternalLibrary() &&
-      klass.Name() == Symbols::ClassID().raw()) {
-    // If this is a dart:internal.ClassID class ignore field declarations
-    // contained in the Kernel file and instead inject our own const
-    // fields.
-    klass.InjectCIDFields();
-    discard_fields = true;
-  }
+  // If this is a dart:internal.ClassID class ignore field declarations
+  // contained in the Kernel file and instead inject our own const
+  // fields.
+  const bool discard_fields = klass.InjectCIDFields();
 
   if (FLAG_enable_interpreter || FLAG_use_bytecode_compiler) {
+    static_assert(KernelBytecode::kMinSupportedBytecodeFormatVersion < 10,
+                  "Cleanup support for old bytecode format versions");
+    ASSERT(!klass.is_declared_in_bytecode());
     if (bytecode_metadata_helper_.ReadMembers(
             klass.kernel_offset() + library_kernel_offset_, klass,
             discard_fields)) {
@@ -1681,6 +1709,7 @@
 }
 
 void KernelLoader::FinishLoading(const Class& klass) {
+  ASSERT(!klass.is_declared_in_bytecode());
   ASSERT(klass.IsTopLevel() || (klass.kernel_offset() > 0));
 
   Zone* zone = Thread::Current()->zone();
@@ -2131,16 +2160,16 @@
   RawLibrary* result;
   name_index_handle_ = Smi::New(library);
   {
-    NoSafepointScope no_safepoint_scope(thread_);
     result = kernel_program_info_.LookupLibrary(thread_, name_index_handle_);
+    NoSafepointScope no_safepoint_scope(thread_);
     if (result != Library::null()) {
       return result;
     }
   }
   const String& url = H.DartString(H.CanonicalNameString(library));
   {
-    NoSafepointScope no_safepoint_scope(thread_);
     result = Library::LookupLibrary(thread_, url);
+    NoSafepointScope no_safepoint_scope(thread_);
     if (result == Library::null()) {
       return result;
     }
@@ -2154,9 +2183,9 @@
 RawLibrary* KernelLoader::LookupLibrary(NameIndex library) {
   name_index_handle_ = Smi::New(library);
   {
-    NoSafepointScope no_safepoint_scope(thread_);
     RawLibrary* result =
         kernel_program_info_.LookupLibrary(thread_, name_index_handle_);
+    NoSafepointScope no_safepoint_scope(thread_);
     if (result != Library::null()) {
       return result;
     }
@@ -2192,9 +2221,9 @@
 RawClass* KernelLoader::LookupClass(const Library& library, NameIndex klass) {
   name_index_handle_ = Smi::New(klass);
   {
-    NoSafepointScope no_safepoint_scope(thread_);
     RawClass* raw_class =
         kernel_program_info_.LookupClass(thread_, name_index_handle_);
+    NoSafepointScope no_safepoint_scope(thread_);
     if (raw_class != Class::null()) {
       return raw_class;
     }
@@ -2257,9 +2286,11 @@
   const PatchClass& initializer_owner =
       PatchClass::Handle(zone, PatchClass::New(field_owner, script));
   const Library& lib = Library::Handle(zone, field_owner.library());
-  initializer_owner.set_library_kernel_data(
-      ExternalTypedData::Handle(zone, lib.kernel_data()));
-  initializer_owner.set_library_kernel_offset(lib.kernel_offset());
+  if (!lib.is_declared_in_bytecode()) {
+    initializer_owner.set_library_kernel_data(
+        ExternalTypedData::Handle(zone, lib.kernel_data()));
+    initializer_owner.set_library_kernel_offset(lib.kernel_offset());
+  }
 
   // Create a static initializer.
   const Function& initializer_fun = Function::Handle(
diff --git a/runtime/vm/kernel_loader.h b/runtime/vm/kernel_loader.h
index 79c58b6..f6d7665 100644
--- a/runtime/vm/kernel_loader.h
+++ b/runtime/vm/kernel_loader.h
@@ -196,6 +196,9 @@
   // was no main procedure, or a failure object if there was an error.
   RawObject* LoadProgram(bool process_pending_classes = true);
 
+  // Load given library.
+  void LoadLibrary(const Library& library);
+
   // Returns the function which will evaluate the expression, or a failure
   // object if there was an error.
   RawObject* LoadExpressionEvaluationFunction(const String& library_url,
@@ -219,8 +222,6 @@
                                         intptr_t kernel_buffer_length,
                                         const String& url);
 
-  RawLibrary* LoadLibrary(intptr_t index);
-
   void FinishTopLevelClassLoading(const Class& toplevel_class,
                                   const Library& library,
                                   const LibraryIndex& library_index);
@@ -258,6 +259,16 @@
     return translation_helper_.DartSymbolObfuscate(index);
   }
 
+ private:
+  KernelLoader(const Script& script,
+               const ExternalTypedData& kernel_data,
+               intptr_t data_program_offset);
+
+  void InitializeFields(
+      DirectChainedHashMap<UriToSourceTableTrait>* uri_to_source_table);
+
+  RawLibrary* LoadLibrary(intptr_t index);
+
   const String& LibraryUri(intptr_t library_index) {
     return translation_helper_.DartSymbolPlain(
         translation_helper_.CanonicalNameString(
@@ -284,15 +295,6 @@
 
   uint8_t CharacterAt(StringIndex string_index, intptr_t index);
 
- private:
-  friend class BuildingTranslationHelper;
-
-  KernelLoader(const Script& script,
-               const ExternalTypedData& kernel_data,
-               intptr_t data_program_offset);
-
-  void InitializeFields(
-      DirectChainedHashMap<UriToSourceTableTrait>* uri_to_source_table);
   static void index_programs(kernel::Reader* reader,
                              GrowableArray<intptr_t>* subprogram_file_starts);
   void walk_incremental_kernel(BitVector* modified_libs,
@@ -459,6 +461,8 @@
   GrowableArray<const Function*> functions_;
   GrowableArray<const Field*> fields_;
 
+  friend class BuildingTranslationHelper;
+
   DISALLOW_COPY_AND_ASSIGN(KernelLoader);
 };
 
diff --git a/runtime/vm/lockers.cc b/runtime/vm/lockers.cc
index 7a43e4a..79f6f25 100644
--- a/runtime/vm/lockers.cc
+++ b/runtime/vm/lockers.cc
@@ -12,6 +12,11 @@
                                                           int64_t millis) {
   ASSERT(thread == Thread::Current());
   ASSERT(thread->execution_state() == Thread::kThreadInVM);
+#if defined(DEBUG)
+  if (no_safepoint_scope_) {
+    thread->DecrementNoSafepointScopeDepth();
+  }
+#endif
   thread->set_execution_state(Thread::kThreadInBlockedState);
   thread->EnterSafepoint();
   Monitor::WaitResult result = monitor_->Wait(millis);
@@ -26,6 +31,11 @@
     monitor_->Enter();
   }
   thread->set_execution_state(Thread::kThreadInVM);
+#if defined(DEBUG)
+  if (no_safepoint_scope_) {
+    thread->IncrementNoSafepointScopeDepth();
+  }
+#endif
   return result;
 }
 
diff --git a/runtime/vm/megamorphic_cache_table.cc b/runtime/vm/megamorphic_cache_table.cc
index c07452f..8b9bf6a 100644
--- a/runtime/vm/megamorphic_cache_table.cc
+++ b/runtime/vm/megamorphic_cache_table.cc
@@ -16,7 +16,7 @@
                                                    const String& name,
                                                    const Array& descriptor) {
   // Multiple compilation threads could access this lookup.
-  SafepointMutexLocker ml(isolate->megamorphic_lookup_mutex());
+  SafepointMutexLocker ml(isolate->megamorphic_mutex());
   ASSERT(name.IsSymbol());
   // TODO(rmacnak): ASSERT(descriptor.IsCanonical());
 
diff --git a/runtime/vm/message_handler.cc b/runtime/vm/message_handler.cc
index da32e9a..122fb58 100644
--- a/runtime/vm/message_handler.cc
+++ b/runtime/vm/message_handler.cc
@@ -64,9 +64,9 @@
       is_paused_on_exit_(false),
       paused_timestamp_(-1),
 #endif
+      task_running_(false),
       delete_me_(false),
       pool_(NULL),
-      task_(NULL),
       idle_start_time_(0),
       start_callback_(NULL),
       end_callback_(NULL),
@@ -81,7 +81,6 @@
   queue_ = NULL;
   oob_queue_ = NULL;
   pool_ = NULL;
-  task_ = NULL;
 }
 
 const char* MessageHandler::name() const {
@@ -102,7 +101,6 @@
                          StartCallback start_callback,
                          EndCallback end_callback,
                          CallbackData data) {
-  bool task_running;
   MonitorLocker ml(&monitor_);
   if (FLAG_trace_isolates) {
     OS::PrintErr(
@@ -116,15 +114,14 @@
   start_callback_ = start_callback;
   end_callback_ = end_callback;
   callback_data_ = data;
-  task_ = new MessageHandlerTask(this);
-  task_running = pool_->Run(task_);
-  ASSERT(task_running);
+  task_running_ = pool_->Run<MessageHandlerTask>(this);
+  ASSERT(task_running_);
 }
 
 void MessageHandler::PostMessage(std::unique_ptr<Message> message,
                                  bool before_events) {
   Message::Priority saved_priority;
-  bool task_running = true;
+
   {
     MonitorLocker ml(&monitor_);
     if (FLAG_trace_isolates) {
@@ -158,13 +155,12 @@
       ml.Notify();
     }
 
-    if ((pool_ != NULL) && (task_ == NULL)) {
+    if ((pool_ != NULL) && !task_running_) {
       ASSERT(!delete_me_);
-      task_ = new MessageHandlerTask(this);
-      task_running = pool_->Run(task_);
+      task_running_ = pool_->Run<MessageHandlerTask>(this);
+      ASSERT(task_running_);
     }
   }
-  ASSERT(task_running);
 
   // Invoke any custom message notification.
   MessageNotify(saved_priority);
@@ -279,7 +275,7 @@
 MessageHandler::MessageStatus MessageHandler::PauseAndHandleAllMessages(
     int64_t timeout_millis) {
   MonitorLocker ml(&monitor_);
-  ASSERT(task_ != NULL);
+  ASSERT(task_running_);
   ASSERT(!delete_me_);
 #if defined(DEBUG)
   CheckAccess();
@@ -287,7 +283,7 @@
   paused_for_messages_ = true;
   while (queue_->IsEmpty() && oob_queue_->IsEmpty()) {
     Monitor::WaitResult wr = ml.Wait(timeout_millis);
-    ASSERT(task_ != NULL);
+    ASSERT(task_running_);
     ASSERT(!delete_me_);
     if (wr == Monitor::kTimedOut) {
       break;
@@ -375,7 +371,7 @@
       if (ShouldPauseOnStart(status)) {
         // Still paused.
         ASSERT(oob_queue_->IsEmpty());
-        task_ = NULL;  // No task in queue.
+        task_running_ = false;  // No task in queue.
         return;
       } else {
         PausedOnStartLocked(&ml, false);
@@ -386,7 +382,7 @@
       if (ShouldPauseOnExit(status)) {
         // Still paused.
         ASSERT(oob_queue_->IsEmpty());
-        task_ = NULL;  // No task in queue.
+        task_running_ = false;  // No task in queue.
         return;
       } else {
         PausedOnExitLocked(&ml, false);
@@ -440,7 +436,7 @@
         if (ShouldPauseOnExit(status)) {
           // Still paused.
           ASSERT(oob_queue_->IsEmpty());
-          task_ = NULL;  // No task in queue.
+          task_running_ = false;  // No task in queue.
           return;
         } else {
           PausedOnExitLocked(&ml, false);
@@ -470,10 +466,10 @@
       delete_me = delete_me_;
     }
 
-    // Clear the task_ last.  This allows other tasks to potentially start
+    // Clear task_running_ last.  This allows other tasks to potentially start
     // for this message handler.
     ASSERT(oob_queue_->IsEmpty());
-    task_ = NULL;
+    task_running_ = false;
   }
 
   // The handler may have been deleted by another thread here if it is a native
@@ -562,7 +558,7 @@
   ASSERT(OwnedByPortMap());
   {
     MonitorLocker ml(&monitor_);
-    if (task_ != NULL) {
+    if (task_running_) {
       // This message handler currently has a task running on the thread pool.
       delete_me_ = true;
       return;
diff --git a/runtime/vm/message_handler.h b/runtime/vm/message_handler.h
index babc541..46a7c19 100644
--- a/runtime/vm/message_handler.h
+++ b/runtime/vm/message_handler.h
@@ -257,9 +257,9 @@
   bool is_paused_on_exit_;
   int64_t paused_timestamp_;
 #endif
+  bool task_running_;
   bool delete_me_;
   ThreadPool* pool_;
-  ThreadPool::Task* task_;
   int64_t idle_start_time_;
   StartCallback start_callback_;
   EndCallback end_callback_;
diff --git a/runtime/vm/native_arguments.h b/runtime/vm/native_arguments.h
index 7c637b3..ab948de 100644
--- a/runtime/vm/native_arguments.h
+++ b/runtime/vm/native_arguments.h
@@ -101,6 +101,14 @@
     return *arg_ptr;
   }
 
+  void SetArgAt(int index, const Object& value) const {
+    ASSERT(thread_->execution_state() == Thread::kThreadInVM);
+    ASSERT((index >= 0) && (index < ArgCount()));
+    RawObject** arg_ptr =
+        &(argv_[ReverseArgOrderBit::decode(argc_tag_) ? index : -index]);
+    *arg_ptr = value.raw();
+  }
+
   // Does not include hidden type arguments vector.
   int NativeArgCount() const {
     int function_bits = FunctionBits::decode(argc_tag_);
@@ -158,8 +166,6 @@
     return type_args.TypeAt(index);
   }
 
-  RawObject** ReturnValueAddress() const { return retval_; }
-
   void SetReturn(const Object& value) const {
     ASSERT(thread_->execution_state() == Thread::kThreadInVM);
     *retval_ = value.raw();
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index f914e28..19ceac8 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -153,6 +153,8 @@
     reinterpret_cast<RawClass*>(RAW_NULL);
 RawClass* Object::context_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
 RawClass* Object::context_scope_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
+RawClass* Object::dyncalltypecheck_class_ =
+    reinterpret_cast<RawClass*>(RAW_NULL);
 RawClass* Object::singletargetcache_class_ =
     reinterpret_cast<RawClass*>(RAW_NULL);
 RawClass* Object::unlinkedcall_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
@@ -648,6 +650,9 @@
   cls = Class::New<ContextScope>();
   context_scope_class_ = cls.raw();
 
+  cls = Class::New<ParameterTypeCheck>();
+  dyncalltypecheck_class_ = cls.raw();
+
   cls = Class::New<SingleTargetCache>();
   singletargetcache_class_ = cls.raw();
 
@@ -907,6 +912,9 @@
   *nsm_dispatcher_bytecode_ = CreateVMInternalBytecode(
       KernelBytecode::kVMInternal_NoSuchMethodDispatcher);
 
+  *dynamic_invocation_forwarder_bytecode_ = CreateVMInternalBytecode(
+      KernelBytecode::kVMInternal_ForwardDynamicInvocation);
+
   // Some thread fields need to be reinitialized as null constants have not been
   // initialized until now.
   Thread* thr = Thread::Current();
@@ -978,6 +986,8 @@
   ASSERT(invoke_field_bytecode_->IsBytecode());
   ASSERT(!nsm_dispatcher_bytecode_->IsSmi());
   ASSERT(nsm_dispatcher_bytecode_->IsBytecode());
+  ASSERT(!dynamic_invocation_forwarder_bytecode_->IsSmi());
+  ASSERT(dynamic_invocation_forwarder_bytecode_->IsBytecode());
 }
 
 void Object::FinishInit(Isolate* isolate) {
@@ -1021,6 +1031,7 @@
   exception_handlers_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
   context_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
   context_scope_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
+  dyncalltypecheck_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
   singletargetcache_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
   unlinkedcall_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
   icdata_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
@@ -1120,6 +1131,7 @@
   SET_CLASS_NAME(exception_handlers, ExceptionHandlers);
   SET_CLASS_NAME(context, Context);
   SET_CLASS_NAME(context_scope, ContextScope);
+  SET_CLASS_NAME(dyncalltypecheck, ParameterTypeCheck);
   SET_CLASS_NAME(singletargetcache, SingleTargetCache);
   SET_CLASS_NAME(unlinkedcall, UnlinkedCall);
   SET_CLASS_NAME(icdata, ICData);
@@ -1919,7 +1931,8 @@
     ASSERT(!lib.IsNull());
     cls = lib.LookupClassAllowPrivate(Symbols::ClassID());
     ASSERT(!cls.IsNull());
-    cls.InjectCIDFields();
+    const bool injected = cls.InjectCIDFields();
+    ASSERT(injected);
 
     isolate->object_store()->InitKnownObjects();
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
@@ -2127,6 +2140,7 @@
   ASSERT(Utils::IsAligned(size, kObjectAlignment));
   Thread* thread = Thread::Current();
   ASSERT(thread->execution_state() == Thread::kThreadInVM);
+  ASSERT(thread->no_safepoint_scope_depth() == 0);
   ASSERT(thread->no_callback_scope_depth() == 0);
   Heap* heap = thread->heap();
 
@@ -2298,7 +2312,8 @@
     // references, but do not recompute size.
     result.set_is_prefinalized();
   }
-  result.set_kernel_offset(-1);
+  NOT_IN_PRECOMPILED(result.set_is_declared_in_bytecode(false));
+  NOT_IN_PRECOMPILED(result.set_binary_declaration_offset(0));
   result.InitEmptyFields();
   Isolate::Current()->RegisterClass(result);
   return result.raw();
@@ -2580,12 +2595,13 @@
 
 void Class::set_type_parameters(const TypeArguments& value) const {
   ASSERT((num_type_arguments() == kUnknownNumTypeArguments) ||
-         is_prefinalized());
+         is_declared_in_bytecode() || is_prefinalized());
   StorePointer(&raw_ptr()->type_parameters_, value.raw());
 }
 
 intptr_t Class::NumTypeParameters(Thread* thread) const {
-  if (type_parameters() == TypeArguments::null()) {
+  if (!is_declaration_loaded()) {
+    ASSERT(is_prefinalized());
     const intptr_t cid = id();
     if ((cid == kArrayCid) || (cid == kImmutableArrayCid) ||
         (cid == kGrowableObjectArrayCid)) {
@@ -2593,6 +2609,9 @@
     }
     return 0;
   }
+  if (type_parameters() == TypeArguments::null()) {
+    return 0;
+  }
   REUSABLE_TYPE_ARGUMENTS_HANDLESCOPE(thread);
   TypeArguments& type_params = thread->TypeArgumentsHandle();
   type_params = type_parameters();
@@ -3058,6 +3077,10 @@
 
   forwarder.InheritBinaryDeclarationFrom(*this);
 
+  const Array& checks = Array::Handle(zone, Array::New(1));
+  checks.SetAt(0, *this);
+  forwarder.SetForwardingChecks(checks);
+
   return forwarder.raw();
 }
 
@@ -3579,7 +3602,12 @@
   SetFields(new_arr);
 }
 
-void Class::InjectCIDFields() const {
+bool Class::InjectCIDFields() const {
+  if (library() != Library::InternalLibrary() ||
+      Name() != Symbols::ClassID().raw()) {
+    return false;
+  }
+
   Thread* thread = Thread::Current();
   Zone* zone = thread->zone();
   Field& field = Field::Handle(zone);
@@ -3614,6 +3642,8 @@
   CLASS_LIST_TYPED_DATA(ADD_SET_FIELD)
 #undef ADD_SET_FIELD
 #undef CLASS_LIST_WITH_NULL
+
+  return true;
 }
 
 template <class FakeInstance>
@@ -3637,6 +3667,8 @@
   result.set_num_type_arguments(kUnknownNumTypeArguments);
   result.set_num_native_fields(0);
   result.set_state_bits(0);
+  NOT_IN_PRECOMPILED(result.set_is_declared_in_bytecode(false));
+  NOT_IN_PRECOMPILED(result.set_binary_declaration_offset(0));
   result.InitEmptyFields();
   return result.raw();
 }
@@ -3644,7 +3676,6 @@
 template <class FakeInstance>
 RawClass* Class::New(intptr_t index) {
   Class& result = Class::Handle(NewCommon<FakeInstance>(index));
-  result.set_kernel_offset(-1);
   Isolate::Current()->RegisterClass(result);
   return result.raw();
 }
@@ -3659,7 +3690,6 @@
   result.set_name(name);
   result.set_script(script);
   result.set_token_pos(token_pos);
-  result.set_kernel_offset(-1);
   if (register_class) {
     Isolate::Current()->RegisterClass(result);
   }
@@ -3690,7 +3720,6 @@
     cls.set_is_declaration_loaded();
     cls.set_is_type_finalized();
     cls.set_is_synthesized_class();
-    cls.set_kernel_offset(-1);
     library.AddClass(cls);
     return cls.raw();
   } else {
@@ -3893,6 +3922,8 @@
       return Symbols::Context().raw();
     case kContextScopeCid:
       return Symbols::ContextScope().raw();
+    case kParameterTypeCheckCid:
+      return Symbols::ParameterTypeCheck().raw();
     case kSingleTargetCacheCid:
       return Symbols::SingleTargetCache().raw();
     case kICDataCid:
@@ -3959,6 +3990,11 @@
   ASSERT(!scr.IsNull());
 
   if (scr.kind() == RawScript::kKernelTag) {
+    if (is_declared_in_bytecode()) {
+      // TODO(alexmarkov): keep end_token_pos in Class?
+      UNIMPLEMENTED();
+      return token_pos();
+    }
     ASSERT(kernel_offset() > 0);
     const Library& lib = Library::Handle(zone, library());
     const ExternalTypedData& kernel_data =
@@ -5517,8 +5553,8 @@
     return "TypeArguments: null";
   }
   Zone* zone = Thread::Current()->zone();
-  const char* prev_cstr = OS::SCreate(zone, "TypeArguments: (@%p H%" Px ")",
-                                      raw(), Smi::Value(raw_ptr()->hash_));
+  const char* prev_cstr = OS::SCreate(zone, "TypeArguments: (H%" Px ")",
+                                      Smi::Value(raw_ptr()->hash_));
   for (int i = 0; i < Length(); i++) {
     const AbstractType& type_at = AbstractType::Handle(zone, TypeAt(i));
     const char* type_cstr = type_at.IsNull() ? "null" : type_at.ToCString();
@@ -5655,6 +5691,7 @@
   }
   switch (kind()) {
     case RawFunction::kDynamicInvocationForwarder:
+      return is_declared_in_bytecode();
     case RawFunction::kImplicitClosureFunction:
     case RawFunction::kIrregexpFunction:
     case RawFunction::kFfiTrampoline:
@@ -5877,8 +5914,7 @@
   ASSERT(kind() == RawFunction::kImplicitGetter ||
          kind() == RawFunction::kImplicitSetter ||
          kind() == RawFunction::kImplicitStaticGetter ||
-         kind() == RawFunction::kStaticFieldInitializer ||
-         kind() == RawFunction::kDynamicInvocationForwarder);
+         kind() == RawFunction::kStaticFieldInitializer);
   return Field::RawCast(raw_ptr()->data_);
 }
 
@@ -6217,6 +6253,20 @@
   RedirectionData::Cast(obj).set_target(target);
 }
 
+RawFunction* Function::ForwardingTarget() const {
+  ASSERT(kind() == RawFunction::kDynamicInvocationForwarder);
+  Array& checks = Array::Handle();
+  checks ^= raw_ptr()->data_;
+  return Function::RawCast(checks.At(0));
+}
+
+void Function::SetForwardingChecks(const Array& checks) const {
+  ASSERT(kind() == RawFunction::kDynamicInvocationForwarder);
+  ASSERT(checks.Length() >= 1);
+  ASSERT(Object::Handle(checks.At(0)).IsFunction());
+  set_data(checks);
+}
+
 // This field is heavily overloaded:
 //   eval function:           Script expression source
 //   kernel eval function:    Array[0] = Script
@@ -6238,6 +6288,9 @@
 //                            Array[1] = Function implicit closure function
 //   regular function:        Function for implicit closure function
 //   ffi trampoline function: FfiTrampolineData  (Dart->C)
+//   dyn inv forwarder:       Array[0] = Function target
+//                            Array[1] = TypeArguments default type args
+//                            Array[i] = ParameterTypeCheck
 void Function::set_data(const Object& value) const {
   StorePointer(&raw_ptr()->data_, value.raw());
 }
@@ -6828,27 +6881,29 @@
     intptr_t reserve_len,
     bool with_lib,
     QualifiedFunctionLibKind lib_kind) {
-  const char* name = String::Handle(function.name()).ToCString();
+  Zone* zone = Thread::Current()->zone();
+  const char* name = String::Handle(zone, function.name()).ToCString();
   const char* function_format = (reserve_len == 0) ? "%s" : "%s_";
   reserve_len += Utils::SNPrint(NULL, 0, function_format, name);
-  const Function& parent = Function::Handle(function.parent_function());
+  const Function& parent = Function::Handle(zone, function.parent_function());
   intptr_t written = 0;
   if (parent.IsNull()) {
-    const Class& function_class = Class::Handle(function.Owner());
+    const Class& function_class = Class::Handle(zone, function.Owner());
     ASSERT(!function_class.IsNull());
-    const char* class_name = String::Handle(function_class.Name()).ToCString();
+    const char* class_name =
+        String::Handle(zone, function_class.Name()).ToCString();
     ASSERT(class_name != NULL);
-    const Library& library = Library::Handle(function_class.library());
-    ASSERT(!library.IsNull());
     const char* library_name = NULL;
     const char* lib_class_format = NULL;
     if (with_lib) {
+      const Library& library = Library::Handle(zone, function_class.library());
+      ASSERT(!library.IsNull());
       switch (lib_kind) {
         case kQualifiedFunctionLibKindLibUrl:
-          library_name = String::Handle(library.url()).ToCString();
+          library_name = String::Handle(zone, library.url()).ToCString();
           break;
         case kQualifiedFunctionLibKindLibName:
-          library_name = String::Handle(library.name()).ToCString();
+          library_name = String::Handle(zone, library.name()).ToCString();
           break;
         default:
           UNREACHABLE();
@@ -6862,7 +6917,7 @@
     reserve_len +=
         Utils::SNPrint(NULL, 0, lib_class_format, library_name, class_name);
     ASSERT(chars != NULL);
-    *chars = Thread::Current()->zone()->Alloc<char>(reserve_len + 1);
+    *chars = zone->Alloc<char>(reserve_len + 1);
     written = Utils::SNPrint(*chars, reserve_len + 1, lib_class_format,
                              library_name, class_name);
   } else {
@@ -7741,6 +7796,10 @@
 }
 
 intptr_t Function::KernelDataProgramOffset() const {
+  ASSERT(!is_declared_in_bytecode());
+  if (IsNoSuchMethodDispatcher() || IsInvokeFieldDispatcher()) {
+    return 0;
+  }
   Object& data = Object::Handle(raw_ptr()->data_);
   if (data.IsArray()) {
     Object& script = Object::Handle(Array::Cast(data).At(0));
@@ -7757,6 +7816,7 @@
   const Object& obj = Object::Handle(raw_ptr()->owner_);
   if (obj.IsClass()) {
     Library& lib = Library::Handle(Class::Cast(obj).library());
+    ASSERT(!lib.is_declared_in_bytecode());
     return lib.kernel_offset();
   }
   ASSERT(obj.IsPatchClass());
@@ -7971,6 +8031,18 @@
   set_ic_data_array(Array::null_array());
 }
 
+RawICData* Function::FindICData(intptr_t deopt_id) const {
+  const Array& array = Array::Handle(ic_data_array());
+  ICData& ic_data = ICData::Handle();
+  for (intptr_t i = 1; i < array.Length(); i++) {
+    ic_data ^= array.At(i);
+    if (ic_data.deopt_id() == deopt_id) {
+      return ic_data.raw();
+    }
+  }
+  return ICData::null();
+}
+
 void Function::SetDeoptReasonForAll(intptr_t deopt_id,
                                     ICData::DeoptReasonId reason) {
   const Array& array = Array::Handle(ic_data_array());
@@ -8014,6 +8086,10 @@
   const Object& result =
       Object::Handle(zone, Compiler::CompileFunction(thread, *this));
   if (result.IsError()) {
+    if (result.IsLanguageError()) {
+      Exceptions::ThrowCompileTimeError(LanguageError::Cast(result));
+      UNREACHABLE();
+    }
     Exceptions::PropagateError(Error::Cast(result));
     UNREACHABLE();
   }
@@ -8218,7 +8294,7 @@
       Object::Allocate(FfiTrampolineData::kClassId,
                        FfiTrampolineData::InstanceSize(), Heap::kOld);
   RawFfiTrampolineData* data = reinterpret_cast<RawFfiTrampolineData*>(raw);
-  data->ptr()->callback_id_ = -1;
+  data->ptr()->callback_id_ = 0;
   return data;
 }
 
@@ -8380,6 +8456,7 @@
 }
 
 intptr_t Field::KernelDataProgramOffset() const {
+  ASSERT(!is_declared_in_bytecode());
   const Object& obj = Object::Handle(raw_ptr()->owner_);
   // During background JIT compilation field objects are copied
   // and copy points to the original field via the owner field.
@@ -8387,6 +8464,7 @@
     return Field::Cast(obj).KernelDataProgramOffset();
   } else if (obj.IsClass()) {
     Library& lib = Library::Handle(Class::Cast(obj).library());
+    ASSERT(!lib.is_declared_in_bytecode());
     return lib.kernel_offset();
   }
   ASSERT(obj.IsPatchClass());
@@ -9941,14 +10019,15 @@
 void Library::AddClassMetadata(const Class& cls,
                                const Object& tl_owner,
                                TokenPosition token_pos,
-                               intptr_t kernel_offset) const {
+                               intptr_t kernel_offset,
+                               intptr_t bytecode_offset) const {
   Thread* thread = Thread::Current();
   Zone* zone = thread->zone();
   // We use the toplevel class as the owner of a class's metadata field because
   // a class's metadata is in scope of the library, not the class.
   AddMetadata(tl_owner,
               String::Handle(zone, MakeClassMetaName(thread, zone, cls)),
-              token_pos, kernel_offset, 0);
+              token_pos, kernel_offset, bytecode_offset);
 }
 
 void Library::AddFieldMetadata(const Field& field,
@@ -9985,8 +10064,10 @@
 
 void Library::AddLibraryMetadata(const Object& tl_owner,
                                  TokenPosition token_pos,
-                                 intptr_t kernel_offset) const {
-  AddMetadata(tl_owner, Symbols::TopLevel(), token_pos, kernel_offset, 0);
+                                 intptr_t kernel_offset,
+                                 intptr_t bytecode_offset) const {
+  AddMetadata(tl_owner, Symbols::TopLevel(), token_pos, kernel_offset,
+              bytecode_offset);
 }
 
 RawString* Library::MakeMetadataName(const Object& obj) const {
@@ -10915,7 +10996,8 @@
     result.set_debuggable(true);
   }
   result.set_is_dart_scheme(dart_scheme);
-  result.set_kernel_offset(-1);
+  NOT_IN_PRECOMPILED(result.set_is_declared_in_bytecode(false));
+  NOT_IN_PRECOMPILED(result.set_binary_declaration_offset(0));
   result.StoreNonPointer(&result.raw_ptr()->load_state_,
                          RawLibrary::kAllocated);
   result.StoreNonPointer(&result.raw_ptr()->index_, -1);
@@ -12565,17 +12647,24 @@
 }
 
 void ObjectPool::DebugPrint() const {
-  THR_Print("Object Pool: 0x%" Px "{\n", reinterpret_cast<uword>(raw()));
+  THR_Print("ObjectPool len:%" Pd " {\n", Length());
   for (intptr_t i = 0; i < Length(); i++) {
     intptr_t offset = OffsetFromIndex(i);
-    THR_Print("  %" Pd " PP+0x%" Px ": ", i, offset);
+    THR_Print("  [pp+0x%" Px "] ", offset);
     if ((TypeAt(i) == EntryType::kTaggedObject) ||
         (TypeAt(i) == EntryType::kNativeEntryData)) {
-      RawObject* obj = ObjectAt(i);
-      THR_Print("0x%" Px " %s (obj)\n", reinterpret_cast<uword>(obj),
-                Object::Handle(obj).ToCString());
+      const Object& obj = Object::Handle(ObjectAt(i));
+      THR_Print("%s (obj)\n", obj.ToCString());
     } else if (TypeAt(i) == EntryType::kNativeFunction) {
-      THR_Print("0x%" Px " (native function)\n", RawValueAt(i));
+      uword pc = RawValueAt(i);
+      uintptr_t start = 0;
+      char* name = NativeSymbolResolver::LookupSymbolName(pc, &start);
+      if (name != NULL) {
+        THR_Print("%s (native function)\n", name);
+        NativeSymbolResolver::FreeSymbolName(name);
+      } else {
+        THR_Print("0x%" Px " (native function)\n", pc);
+      }
     } else if (TypeAt(i) == EntryType::kNativeFunctionWrapper) {
       THR_Print("0x%" Px " (native function wrapper)\n", RawValueAt(i));
     } else {
@@ -13186,6 +13275,43 @@
 #undef FORMAT2
 }
 
+void ParameterTypeCheck::set_type_or_bound(const AbstractType& value) const {
+  StorePointer(&raw_ptr()->type_or_bound_, value.raw());
+}
+
+void ParameterTypeCheck::set_param(const AbstractType& value) const {
+  StorePointer(&raw_ptr()->param_, value.raw());
+}
+
+void ParameterTypeCheck::set_name(const String& value) const {
+  StorePointer(&raw_ptr()->name_, value.raw());
+}
+
+void ParameterTypeCheck::set_cache(const SubtypeTestCache& value) const {
+  StorePointer(&raw_ptr()->cache_, value.raw());
+}
+
+const char* ParameterTypeCheck::ToCString() const {
+  Zone* zone = Thread::Current()->zone();
+  return zone->PrintToString("ParameterTypeCheck(%" Pd " %s %s %s)", index(),
+                             Object::Handle(zone, param()).ToCString(),
+                             Object::Handle(zone, type_or_bound()).ToCString(),
+                             Object::Handle(zone, name()).ToCString());
+}
+
+RawParameterTypeCheck* ParameterTypeCheck::New() {
+  ParameterTypeCheck& result = ParameterTypeCheck::Handle();
+  {
+    RawObject* raw =
+        Object::Allocate(ParameterTypeCheck::kClassId,
+                         ParameterTypeCheck::InstanceSize(), Heap::kOld);
+    NoSafepointScope no_safepoint;
+    result ^= raw;
+  }
+  result.set_index(0);
+  return result.raw();
+}
+
 void SingleTargetCache::set_target(const Code& value) const {
   StorePointer(&raw_ptr()->target_, value.raw());
 }
@@ -13252,14 +13378,14 @@
 }
 
 const char* ICData::ToCString() const {
-  const String& name = String::Handle(target_name());
+  Zone* zone = Thread::Current()->zone();
+  const String& name = String::Handle(zone, target_name());
   const intptr_t num_args = NumArgsTested();
   const intptr_t num_checks = NumberOfChecks();
   const intptr_t type_args_len = TypeArgsLen();
-  return OS::SCreate(Thread::Current()->zone(),
-                     "ICData target:'%s' num-args: %" Pd " num-checks: %" Pd
-                     " type-args-len: %" Pd "",
-                     name.ToCString(), num_args, num_checks, type_args_len);
+  return zone->PrintToString(
+      "ICData(%s num-args: %" Pd " num-checks: %" Pd " type-args-len: %" Pd ")",
+      name.ToCString(), num_args, num_checks, type_args_len);
 }
 
 RawFunction* ICData::Owner() const {
@@ -14049,19 +14175,10 @@
   return result.raw();
 }
 
-bool ICData::AllTargetsHaveSameOwner(intptr_t owner_cid) const {
-  if (NumberOfChecksIs(0)) return false;
-  Class& cls = Class::Handle();
-  const intptr_t len = NumberOfChecks();
-  for (intptr_t i = 0; i < len; i++) {
-    if (IsUsedAt(i)) {
-      cls = Function::Handle(GetTargetAt(i)).Owner();
-      if (cls.id() != owner_cid) {
-        return false;
-      }
-    }
-  }
-  return true;
+RawMegamorphicCache* ICData::AsMegamorphicCache() const {
+  const String& name = String::Handle(target_name());
+  const Array& descriptor = Array::Handle(arguments_descriptor());
+  return MegamorphicCacheTable::Lookup(Isolate::Current(), name, descriptor);
 }
 
 bool ICData::HasReceiverClassId(intptr_t class_id) const {
@@ -14080,6 +14197,8 @@
 
 // Returns true if all targets are the same.
 // TODO(srdjan): if targets are native use their C_function to compare.
+// TODO(rmacnak): this question should only be asked against a CallTargets,
+// not an ICData.
 bool ICData::HasOneTarget() const {
   ASSERT(!NumberOfChecksIs(0));
   const Function& first_target = Function::Handle(GetTargetAt(0));
@@ -14089,6 +14208,23 @@
       return false;
     }
   }
+  if (is_megamorphic()) {
+    const MegamorphicCache& cache =
+        MegamorphicCache::Handle(AsMegamorphicCache());
+    SafepointMutexLocker ml(Isolate::Current()->megamorphic_mutex());
+    MegamorphicCacheEntries entries(Array::Handle(cache.buckets()));
+    for (intptr_t i = 0; i < entries.Length(); i++) {
+      const intptr_t id =
+          Smi::Value(entries[i].Get<MegamorphicCache::kClassIdIndex>());
+      if (id == kIllegalCid) {
+        continue;
+      }
+      if (entries[i].Get<MegamorphicCache::kTargetFunctionIndex>() !=
+          first_target.raw()) {
+        return false;
+      }
+    }
+  }
   return true;
 }
 
@@ -14247,6 +14383,7 @@
       AbstractType::Handle(from.receivers_static_type())));
   // Copy deoptimization reasons.
   result.SetDeoptReasons(from.DeoptReasons());
+  result.set_is_megamorphic(from.is_megamorphic());
   return result.raw();
 }
 
@@ -14271,6 +14408,7 @@
   result.set_entries(cloned_array);
   // Copy deoptimization reasons.
   result.SetDeoptReasons(from.DeoptReasons());
+  result.set_is_megamorphic(from.is_megamorphic());
   return result.raw();
 }
 #endif
@@ -15105,14 +15243,6 @@
   reader.DumpSourcePositions(PayloadStart());
 }
 
-RawArray* Code::await_token_positions() const {
-#if defined(PRODUCT)
-  return Array::null();
-#else
-  return raw_ptr()->await_token_positions_;
-#endif
-}
-
 void Bytecode::Disassemble(DisassemblyFormatter* formatter) const {
 #if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
 #if !defined(DART_PRECOMPILED_RUNTIME)
@@ -15200,7 +15330,7 @@
   while (iter.MoveNext()) {
     // PC descriptors for try blocks in bytecode are generated in pairs,
     // marking start and end of a try block.
-    // See BytecodeMetadataHelper::ReadExceptionsTable for details.
+    // See BytecodeReaderHelper::ReadExceptionsTable for details.
     const intptr_t current_try_index = iter.TryIndex();
     const uword start_pc = iter.PcOffset();
     if (pc_offset < start_pc) {
@@ -15348,7 +15478,7 @@
   }
 
   IndentN(indent);
-  THR_Print("Context@%p vars(%" Pd ") {\n", this->raw(), num_variables());
+  THR_Print("Context vars(%" Pd ") {\n", num_variables());
   Object& obj = Object::Handle();
   for (intptr_t i = 0; i < num_variables(); i++) {
     IndentN(indent + 2);
@@ -15570,7 +15700,14 @@
   return result.raw();
 }
 
-void MegamorphicCache::EnsureCapacity() const {
+void MegamorphicCache::Insert(const Smi& class_id, const Object& target) const {
+  SafepointMutexLocker ml(Isolate::Current()->megamorphic_mutex());
+  EnsureCapacityLocked();
+  InsertLocked(class_id, target);
+}
+
+void MegamorphicCache::EnsureCapacityLocked() const {
+  ASSERT(Isolate::Current()->megamorphic_mutex()->IsOwnedByCurrentThread());
   intptr_t old_capacity = mask() + 1;
   double load_limit = kLoadFactor * static_cast<double>(old_capacity);
   if (static_cast<double>(filled_entry_count() + 1) > load_limit) {
@@ -15594,13 +15731,16 @@
       class_id ^= GetClassId(old_buckets, i);
       if (class_id.Value() != kIllegalCid) {
         target = GetTargetFunction(old_buckets, i);
-        Insert(class_id, target);
+        InsertLocked(class_id, target);
       }
     }
   }
 }
 
-void MegamorphicCache::Insert(const Smi& class_id, const Object& target) const {
+void MegamorphicCache::InsertLocked(const Smi& class_id,
+                                    const Object& target) const {
+  ASSERT(Isolate::Current()->megamorphic_mutex()->IsOwnedByCurrentThread());
+  ASSERT(Thread::Current()->IsMutatorThread());
   ASSERT(static_cast<double>(filled_entry_count() + 1) <=
          (kLoadFactor * static_cast<double>(mask() + 1)));
   const Array& backing_array = Array::Handle(buckets());
@@ -17572,7 +17712,14 @@
         for (intptr_t i = 0; i < from_index; i++) {
           type_arg = type_args.TypeAt(i);
           other_type_arg = other_type_args.TypeAt(i);
-          ASSERT(type_arg.IsEquivalent(other_type_arg, trail));
+          // Type arguments may not match if they are TypeRefs without
+          // underlying type (which will be set later).
+          ASSERT(
+              type_arg.IsEquivalent(other_type_arg, trail) ||
+              (type_arg.IsTypeRef() &&
+               TypeRef::Cast(type_arg).type() == AbstractType::null()) ||
+              (other_type_arg.IsTypeRef() &&
+               TypeRef::Cast(other_type_arg).type() == AbstractType::null()));
         }
       }
 #endif
@@ -17948,8 +18095,8 @@
     return OS::SCreate(zone, "Type: class '%s'", class_name);
   } else if (IsFinalized() && IsRecursive()) {
     const intptr_t hash = Hash();
-    return OS::SCreate(zone, "Type: (@%p H%" Px ") class '%s', args:[%s]",
-                       raw(), hash, class_name, args_cstr);
+    return OS::SCreate(zone, "Type: (H%" Px ") class '%s', args:[%s]", hash,
+                       class_name, args_cstr);
   } else {
     return OS::SCreate(zone, "Type: class '%s', args:[%s]", class_name,
                        args_cstr);
@@ -18015,7 +18162,7 @@
 }
 
 void TypeRef::set_type(const AbstractType& value) const {
-  ASSERT(value.IsFunctionType() || value.HasTypeClass());
+  ASSERT(value.IsNull() || value.IsFunctionType() || value.HasTypeClass());
   ASSERT(!value.IsTypeRef());
   StorePointer(&raw_ptr()->type_, value.raw());
 }
@@ -18031,9 +18178,10 @@
   // TODO(regis): Try to reduce the number of nodes required to represent the
   // referenced recursive type.
   AbstractType& ref_type = AbstractType::Handle(type());
-  ASSERT(!ref_type.IsNull());
-  ref_type = ref_type.Canonicalize(trail);
-  set_type(ref_type);
+  if (!ref_type.IsNull()) {
+    ref_type = ref_type.Canonicalize(trail);
+    set_type(ref_type);
+  }
   return raw();
 }
 
@@ -18060,10 +18208,10 @@
 
 intptr_t TypeRef::Hash() const {
   // Do not calculate the hash of the referenced type to avoid divergence.
-  const AbstractType& ref_type = AbstractType::Handle(type());
-  ASSERT(!ref_type.IsNull());
-  const uint32_t result = Class::Handle(ref_type.type_class()).id();
-  return FinalizeHash(result, kHashBits);
+  // TypeRef can participate in type canonicalization even before referenced
+  // type is set, so its hash should not rely on referenced type.
+  const intptr_t kTypeRefHash = 37;
+  return kTypeRefHash;
 }
 
 RawTypeRef* TypeRef::New() {
@@ -18083,17 +18231,17 @@
 }
 
 const char* TypeRef::ToCString() const {
-  AbstractType& ref_type = AbstractType::Handle(type());
+  Zone* zone = Thread::Current()->zone();
+  AbstractType& ref_type = AbstractType::Handle(zone, type());
   if (ref_type.IsNull()) {
     return "TypeRef: null";
   }
-  const char* type_cstr = String::Handle(ref_type.Name()).ToCString();
+  const char* type_cstr = String::Handle(zone, ref_type.Name()).ToCString();
   if (ref_type.IsFinalized()) {
     const intptr_t hash = ref_type.Hash();
-    return OS::SCreate(Thread::Current()->zone(), "TypeRef: %s (@%p H%" Px ")",
-                       type_cstr, ref_type.raw(), hash);
+    return OS::SCreate(zone, "TypeRef: %s (H%" Px ")", type_cstr, hash);
   } else {
-    return OS::SCreate(Thread::Current()->zone(), "TypeRef: %s", type_cstr);
+    return OS::SCreate(zone, "TypeRef: %s", type_cstr);
   }
 }
 
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 33261da..58eacf7 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -403,6 +403,7 @@
   V(Bytecode, invoke_closure_bytecode)                                         \
   V(Bytecode, invoke_field_bytecode)                                           \
   V(Bytecode, nsm_dispatcher_bytecode)                                         \
+  V(Bytecode, dynamic_invocation_forwarder_bytecode)                           \
   V(Instance, sentinel)                                                        \
   V(Instance, transition_sentinel)                                             \
   V(Instance, unknown_constant)                                                \
@@ -468,6 +469,7 @@
     return unhandled_exception_class_;
   }
   static RawClass* unwind_error_class() { return unwind_error_class_; }
+  static RawClass* dyncalltypecheck_class() { return dyncalltypecheck_class_; }
   static RawClass* singletargetcache_class() {
     return singletargetcache_class_;
   }
@@ -705,6 +707,7 @@
   static RawClass* deopt_info_class_;          // Class of DeoptInfo.
   static RawClass* context_class_;        // Class of the Context vm object.
   static RawClass* context_scope_class_;  // Class of ContextScope vm object.
+  static RawClass* dyncalltypecheck_class_;     // Class of ParameterTypeCheck.
   static RawClass* singletargetcache_class_;    // Class of SingleTargetCache.
   static RawClass* unlinkedcall_class_;         // Class of UnlinkedCall.
   static RawClass* icdata_class_;               // Class of ICData.
@@ -897,6 +900,7 @@
   // The type parameters (and their bounds) are specified as an array of
   // TypeParameter.
   RawTypeArguments* type_parameters() const {
+    ASSERT(is_declaration_loaded());
     return raw_ptr()->type_parameters_;
   }
   void set_type_parameters(const TypeArguments& value) const;
@@ -962,7 +966,10 @@
   RawClass* SuperClass(bool original_classes = false) const;
 
   // Interfaces is an array of Types.
-  RawArray* interfaces() const { return raw_ptr()->interfaces_; }
+  RawArray* interfaces() const {
+    ASSERT(is_declaration_loaded());
+    return raw_ptr()->interfaces_;
+  }
   void set_interfaces(const Array& value) const;
 
   // Returns the list of classes directly implementing this class.
@@ -1047,7 +1054,10 @@
   void AddField(const Field& field) const;
   void AddFields(const GrowableArray<const Field*>& fields) const;
 
-  void InjectCIDFields() const;
+  // If this is a dart:internal.ClassID class, then inject our own const
+  // fields. Returns true if synthetic fields are injected and regular
+  // field declarations should be ignored.
+  bool InjectCIDFields() const;
 
   // Returns an array of all instance fields of this class and its superclasses
   // indexed by offset in words.
@@ -1189,18 +1199,72 @@
   RawCode* allocation_stub() const { return raw_ptr()->allocation_stub_; }
   void set_allocation_stub(const Code& value) const;
 
+#if !defined(DART_PRECOMPILED_RUNTIME)
+  intptr_t binary_declaration_offset() const {
+    return RawClass::BinaryDeclarationOffset::decode(
+        raw_ptr()->binary_declaration_);
+  }
+  void set_binary_declaration_offset(intptr_t value) const {
+    ASSERT(value >= 0);
+    StoreNonPointer(&raw_ptr()->binary_declaration_,
+                    RawClass::BinaryDeclarationOffset::update(
+                        value, raw_ptr()->binary_declaration_));
+  }
+#endif  // !defined(DART_PRECOMPILED_RUNTIME)
+
   intptr_t kernel_offset() const {
 #if defined(DART_PRECOMPILED_RUNTIME)
-    return -1;
+    return 0;
 #else
-    return raw_ptr()->kernel_offset_;
+    ASSERT(!is_declared_in_bytecode());
+    return binary_declaration_offset();
 #endif
   }
 
-  void set_kernel_offset(intptr_t offset) const {
-    NOT_IN_PRECOMPILED(StoreNonPointer(&raw_ptr()->kernel_offset_, offset));
+  void set_kernel_offset(intptr_t value) const {
+#if defined(DART_PRECOMPILED_RUNTIME)
+    UNREACHABLE();
+#else
+    ASSERT(!is_declared_in_bytecode());
+    set_binary_declaration_offset(value);
+#endif
   }
 
+  intptr_t bytecode_offset() const {
+#if defined(DART_PRECOMPILED_RUNTIME)
+    return 0;
+#else
+    ASSERT(is_declared_in_bytecode());
+    return binary_declaration_offset();
+#endif
+  }
+
+  void set_bytecode_offset(intptr_t value) const {
+#if defined(DART_PRECOMPILED_RUNTIME)
+    UNREACHABLE();
+#else
+    ASSERT(is_declared_in_bytecode());
+    set_binary_declaration_offset(value);
+#endif
+  }
+
+  bool is_declared_in_bytecode() const {
+#if defined(DART_PRECOMPILED_RUNTIME)
+    return false;
+#else
+    return RawClass::IsDeclaredInBytecode::decode(
+        raw_ptr()->binary_declaration_);
+#endif
+  }
+
+#if !defined(DART_PRECOMPILED_RUNTIME)
+  void set_is_declared_in_bytecode(bool value) const {
+    StoreNonPointer(&raw_ptr()->binary_declaration_,
+                    RawClass::IsDeclaredInBytecode::update(
+                        value, raw_ptr()->binary_declaration_));
+  }
+#endif  // !defined(DART_PRECOMPILED_RUNTIME)
+
   void DisableAllocationStub() const;
 
   RawArray* constants() const;
@@ -1394,9 +1458,10 @@
   static const intptr_t kUnknownNumTypeArguments = -1;
 
   int16_t num_type_arguments() const { return raw_ptr()->num_type_arguments_; }
-  void set_num_type_arguments(intptr_t value) const;
 
  public:
+  void set_num_type_arguments(intptr_t value) const;
+
   bool has_pragma() const {
     return HasPragmaBit::decode(raw_ptr()->state_bits_);
   }
@@ -1491,6 +1556,41 @@
   friend class Class;
 };
 
+class ParameterTypeCheck : public Object {
+ public:
+  // The FP-relative index of the parameter in a bytecode frame (after optional
+  // parameter marshalling) whose assignability needs to be checked, or 0 if
+  // this is a type parameter check.
+  intptr_t index() const { return raw_ptr()->index_; }
+  void set_index(intptr_t i) const { StoreNonPointer(&raw_ptr()->index_, i); }
+
+  // The type parameter to whose bound needs to be checked, or null if this is
+  // an ordinary parameter check.
+  RawAbstractType* param() const { return raw_ptr()->param_; }
+  void set_param(const AbstractType& t) const;
+
+  // FP[index] assignable to type, OR param is subtype of bound.
+  RawAbstractType* type_or_bound() const { return raw_ptr()->type_or_bound_; }
+  void set_type_or_bound(const AbstractType& t) const;
+
+  // The parameter or type parameter's name to use in an error message.
+  RawString* name() const { return raw_ptr()->name_; }
+  void set_name(const String& n) const;
+
+  RawSubtypeTestCache* cache() const { return raw_ptr()->cache_; }
+  void set_cache(const SubtypeTestCache& c) const;
+
+  static intptr_t InstanceSize() {
+    return RoundedAllocationSize(sizeof(RawParameterTypeCheck));
+  }
+
+  static RawParameterTypeCheck* New();
+
+ private:
+  FINAL_HEAP_OBJECT_IMPLEMENTATION(ParameterTypeCheck, Object);
+  friend class Class;
+};
+
 class SingleTargetCache : public Object {
  public:
   RawCode* target() const { return raw_ptr()->target_; }
@@ -1659,6 +1759,18 @@
   RebindRule rebind_rule() const;
   void set_rebind_rule(uint32_t rebind_rule) const;
 
+  // This bit is set when a call site becomes megamorphic and starts using a
+  // MegamorphicCache instead of ICData. It means that the entries in the
+  // ICData are incomplete and the MegamorphicCache needs to also be consulted
+  // to list the call site's observed receiver classes and targets.
+  bool is_megamorphic() const {
+    return MegamorphicBit::decode(raw_ptr()->state_bits_);
+  }
+  void set_is_megamorphic(bool value) const {
+    StoreNonPointer(&raw_ptr()->state_bits_,
+                    MegamorphicBit::update(value, raw_ptr()->state_bits_));
+  }
+
   // The length of the array. This includes all sentinel entries including
   // the final one.
   intptr_t Length() const;
@@ -1788,9 +1900,9 @@
   // Used for printing and optimizations.
   RawICData* AsUnaryClassChecksSortedByCount() const;
 
+  RawMegamorphicCache* AsMegamorphicCache() const;
+
   // Consider only used entries.
-  bool AllTargetsHaveSameOwner(intptr_t owner_cid) const;
-  bool AllReceiversAreNumbers() const;
   bool HasOneTarget() const;
   bool HasReceiverClassId(intptr_t class_id) const;
 
@@ -1851,13 +1963,13 @@
 
   intptr_t FindCheck(const GrowableArray<intptr_t>& cids) const;
 
- private:
-  static RawICData* New();
-
   RawArray* entries() const {
     return AtomicOperations::LoadAcquire(&raw_ptr()->entries_);
   }
 
+ private:
+  static RawICData* New();
+
   // Grows the array and also sets the argument to the index that should be used
   // for the new entry.
   RawArray* Grow(intptr_t* index) const;
@@ -1880,7 +1992,9 @@
     kDeoptReasonPos = kTrackingExactnessPos + kTrackingExactnessSize,
     kDeoptReasonSize = kLastRecordedDeoptReason + 1,
     kRebindRulePos = kDeoptReasonPos + kDeoptReasonSize,
-    kRebindRuleSize = 3
+    kRebindRuleSize = 3,
+    kMegamorphicPos = kRebindRulePos + kRebindRuleSize,
+    kMegamorphicSize = 1,
   };
 
   COMPILE_ASSERT(kNumRebindRules <= (1 << kRebindRuleSize));
@@ -1901,6 +2015,9 @@
                                          uint32_t,
                                          ICData::kRebindRulePos,
                                          ICData::kRebindRuleSize> {};
+  class MegamorphicBit
+      : public BitField<uint32_t, bool, kMegamorphicPos, kMegamorphicSize> {};
+
 #if defined(DEBUG)
   // Used in asserts to verify that a check is not added twice.
   bool HasCheck(const GrowableArray<intptr_t>& cids) const;
@@ -2249,6 +2366,9 @@
   RawFunction* RedirectionTarget() const;
   void SetRedirectionTarget(const Function& target) const;
 
+  RawFunction* ForwardingTarget() const;
+  void SetForwardingChecks(const Array& checks) const;
+
   RawFunction::Kind kind() const {
     return KindBits::decode(raw_ptr()->kind_tag_);
   }
@@ -2822,6 +2942,7 @@
 
   RawArray* ic_data_array() const;
   void ClearICDataArray() const;
+  RawICData* FindICData(intptr_t deopt_id) const;
 
   // Sets deopt reason in all ICData-s with given deopt_id.
   void SetDeoptReasonForAll(intptr_t deopt_id, ICData::DeoptReasonId reason);
@@ -3002,7 +3123,6 @@
   void set_num_optional_parameters(intptr_t value) const;  // Encoded value.
   void set_kind_tag(uint32_t value) const;
   void set_data(const Object& value) const;
-
   static RawFunction* New(Heap::Space space = Heap::kOld);
 
   RawString* QualifiedName(NameVisibility name_visibility) const;
@@ -3897,7 +4017,8 @@
   void AddClassMetadata(const Class& cls,
                         const Object& tl_owner,
                         TokenPosition token_pos,
-                        intptr_t kernel_offset) const;
+                        intptr_t kernel_offset,
+                        intptr_t bytecode_offset) const;
   void AddFieldMetadata(const Field& field,
                         TokenPosition token_pos,
                         intptr_t kernel_offset,
@@ -3908,7 +4029,8 @@
                            intptr_t bytecode_offset) const;
   void AddLibraryMetadata(const Object& tl_owner,
                           TokenPosition token_pos,
-                          intptr_t kernel_offset) const;
+                          intptr_t kernel_offset,
+                          intptr_t bytecode_offset) const;
   void AddTypeParameterMetadata(const TypeParameter& param,
                                 TokenPosition token_pos) const;
   void CloneMetadataFrom(const Library& from_library,
@@ -3999,17 +4121,72 @@
   RawExternalTypedData* kernel_data() const { return raw_ptr()->kernel_data_; }
   void set_kernel_data(const ExternalTypedData& data) const;
 
-  intptr_t kernel_offset() const {
 #if !defined(DART_PRECOMPILED_RUNTIME)
-    return raw_ptr()->kernel_offset_;
+  intptr_t binary_declaration_offset() const {
+    return RawLibrary::BinaryDeclarationOffset::decode(
+        raw_ptr()->binary_declaration_);
+  }
+  void set_binary_declaration_offset(intptr_t value) const {
+    ASSERT(value >= 0);
+    StoreNonPointer(&raw_ptr()->binary_declaration_,
+                    RawLibrary::BinaryDeclarationOffset::update(
+                        value, raw_ptr()->binary_declaration_));
+  }
+#endif  // !defined(DART_PRECOMPILED_RUNTIME)
+
+  intptr_t kernel_offset() const {
+#if defined(DART_PRECOMPILED_RUNTIME)
+    return 0;
 #else
-    return -1;
+    ASSERT(!is_declared_in_bytecode());
+    return binary_declaration_offset();
 #endif
   }
-  void set_kernel_offset(intptr_t offset) const {
-    NOT_IN_PRECOMPILED(StoreNonPointer(&raw_ptr()->kernel_offset_, offset));
+
+  void set_kernel_offset(intptr_t value) const {
+#if defined(DART_PRECOMPILED_RUNTIME)
+    UNREACHABLE();
+#else
+    ASSERT(!is_declared_in_bytecode());
+    set_binary_declaration_offset(value);
+#endif
   }
 
+  intptr_t bytecode_offset() const {
+#if defined(DART_PRECOMPILED_RUNTIME)
+    return 0;
+#else
+    ASSERT(is_declared_in_bytecode());
+    return binary_declaration_offset();
+#endif
+  }
+
+  void set_bytecode_offset(intptr_t value) const {
+#if defined(DART_PRECOMPILED_RUNTIME)
+    UNREACHABLE();
+#else
+    ASSERT(is_declared_in_bytecode());
+    set_binary_declaration_offset(value);
+#endif
+  }
+
+  bool is_declared_in_bytecode() const {
+#if defined(DART_PRECOMPILED_RUNTIME)
+    return false;
+#else
+    return RawLibrary::IsDeclaredInBytecode::decode(
+        raw_ptr()->binary_declaration_);
+#endif
+  }
+
+#if !defined(DART_PRECOMPILED_RUNTIME)
+  void set_is_declared_in_bytecode(bool value) const {
+    StoreNonPointer(&raw_ptr()->binary_declaration_,
+                    RawLibrary::IsDeclaredInBytecode::update(
+                        value, raw_ptr()->binary_declaration_));
+  }
+#endif  // !defined(DART_PRECOMPILED_RUNTIME)
+
   static RawLibrary* LookupLibrary(Thread* thread, const String& url);
   static RawLibrary* GetLibrary(intptr_t index);
 
@@ -4448,20 +4625,30 @@
   // Note: We keep the checked entrypoint offsets even (emitting NOPs if
   // necessary) to allow them to be seen as Smis by the GC.
 #if defined(TARGET_ARCH_IA32)
-  static const intptr_t kPolymorphicEntryOffset = 0;
-  static const intptr_t kMonomorphicEntryOffset = 0;
+  static const intptr_t kMonomorphicEntryOffsetJIT = 6;
+  static const intptr_t kPolymorphicEntryOffsetJIT = 34;
+  static const intptr_t kMonomorphicEntryOffsetAOT = 0;
+  static const intptr_t kPolymorphicEntryOffsetAOT = 0;
 #elif defined(TARGET_ARCH_X64)
-  static const intptr_t kPolymorphicEntryOffset = 8;
-  static const intptr_t kMonomorphicEntryOffset = 32;
+  static const intptr_t kMonomorphicEntryOffsetJIT = 8;
+  static const intptr_t kPolymorphicEntryOffsetJIT = 40;
+  static const intptr_t kMonomorphicEntryOffsetAOT = 8;
+  static const intptr_t kPolymorphicEntryOffsetAOT = 32;
 #elif defined(TARGET_ARCH_ARM)
-  static const intptr_t kPolymorphicEntryOffset = 0;
-  static const intptr_t kMonomorphicEntryOffset = 20;
+  static const intptr_t kMonomorphicEntryOffsetJIT = 0;
+  static const intptr_t kPolymorphicEntryOffsetJIT = 40;
+  static const intptr_t kMonomorphicEntryOffsetAOT = 0;
+  static const intptr_t kPolymorphicEntryOffsetAOT = 20;
 #elif defined(TARGET_ARCH_ARM64)
-  static const intptr_t kPolymorphicEntryOffset = 8;
-  static const intptr_t kMonomorphicEntryOffset = 28;
+  static const intptr_t kMonomorphicEntryOffsetJIT = 8;
+  static const intptr_t kPolymorphicEntryOffsetJIT = 48;
+  static const intptr_t kMonomorphicEntryOffsetAOT = 8;
+  static const intptr_t kPolymorphicEntryOffsetAOT = 28;
 #elif defined(TARGET_ARCH_DBC)
-  static const intptr_t kPolymorphicEntryOffset = 0;
-  static const intptr_t kMonomorphicEntryOffset = 0;
+  static const intptr_t kMonomorphicEntryOffsetJIT = 0;
+  static const intptr_t kPolymorphicEntryOffsetJIT = 0;
+  static const intptr_t kMonomorphicEntryOffsetAOT = 0;
+  static const intptr_t kPolymorphicEntryOffsetAOT = 0;
 #else
 #error Missing entry offsets for current architecture
 #endif
@@ -4469,7 +4656,8 @@
   static uword MonomorphicEntryPoint(const RawInstructions* instr) {
     uword entry = PayloadStart(instr);
     if (!HasSingleEntryPoint(instr)) {
-      entry += kPolymorphicEntryOffset;
+      entry += !FLAG_precompiled_mode ? kMonomorphicEntryOffsetJIT
+                                      : kMonomorphicEntryOffsetAOT;
     }
     return entry;
   }
@@ -4477,7 +4665,8 @@
   static uword EntryPoint(const RawInstructions* instr) {
     uword entry = PayloadStart(instr);
     if (!HasSingleEntryPoint(instr)) {
-      entry += kMonomorphicEntryOffset;
+      entry += !FLAG_precompiled_mode ? kPolymorphicEntryOffsetJIT
+                                      : kPolymorphicEntryOffsetAOT;
     }
     return entry;
   }
@@ -4486,7 +4675,8 @@
     uword entry =
         PayloadStart(instr) + instr->ptr()->unchecked_entrypoint_pc_offset_;
     if (!HasSingleEntryPoint(instr)) {
-      entry += kMonomorphicEntryOffset;
+      entry += !FLAG_precompiled_mode ? kPolymorphicEntryOffsetJIT
+                                      : kPolymorphicEntryOffsetAOT;
     }
     return entry;
   }
@@ -4495,7 +4685,8 @@
     uword entry =
         PayloadStart(instr) + instr->ptr()->unchecked_entrypoint_pc_offset_;
     if (!HasSingleEntryPoint(instr)) {
-      entry += kPolymorphicEntryOffset;
+      entry += !FLAG_precompiled_mode ? kMonomorphicEntryOffsetJIT
+                                      : kMonomorphicEntryOffsetAOT;
     }
     return entry;
   }
@@ -4998,6 +5189,10 @@
     return OptimizedBit::decode(raw_ptr()->state_bits_);
   }
   void set_is_optimized(bool value) const;
+  static bool IsOptimized(RawCode* code) {
+    return Code::OptimizedBit::decode(code->ptr()->state_bits_);
+  }
+
   bool is_alive() const { return AliveBit::decode(raw_ptr()->state_bits_); }
   void set_is_alive(bool value) const;
 
@@ -5044,12 +5239,10 @@
     StorePointer(&raw_ptr()->code_source_map_, code_source_map.raw());
   }
 
-  RawArray* await_token_positions() const;
-  void set_await_token_positions(const Array& await_token_positions) const;
-
   // Used during reloading (see object_reload.cc). Calls Reset on all ICDatas
   // that are embedded inside the Code or ObjecPool objects.
   void ResetICDatas(Zone* zone) const;
+  void ResetSwitchableCalls(Zone* zone) const;
 
   // Array of DeoptInfo objects.
   RawArray* deopt_info_array() const {
@@ -5377,10 +5570,6 @@
     DISALLOW_COPY_AND_ASSIGN(SlowFindRawCodeVisitor);
   };
 
-  static bool IsOptimized(RawCode* code) {
-    return Code::OptimizedBit::decode(code->ptr()->state_bits_);
-  }
-
   static const intptr_t kEntrySize = sizeof(int32_t);  // NOLINT
 
   void set_compile_timestamp(int64_t timestamp) const {
@@ -5731,6 +5920,12 @@
   static const intptr_t kSpreadFactor = 7;
   static const double kLoadFactor;
 
+  enum EntryType {
+    kClassIdIndex,
+    kTargetFunctionIndex,
+    kEntryLength,
+  };
+
   RawArray* buckets() const;
   void set_buckets(const Array& buckets) const;
 
@@ -5757,8 +5952,6 @@
   static RawMegamorphicCache* New(const String& target_name,
                                   const Array& arguments_descriptor);
 
-  void EnsureCapacity() const;
-
   void Insert(const Smi& class_id, const Object& target) const;
 
   void SwitchToBareInstructions();
@@ -5777,11 +5970,9 @@
   void set_target_name(const String& value) const;
   void set_arguments_descriptor(const Array& value) const;
 
-  enum {
-    kClassIdIndex,
-    kTargetFunctionIndex,
-    kEntryLength,
-  };
+  // The caller must hold Isolate::megamorphic_mutex().
+  void EnsureCapacityLocked() const;
+  void InsertLocked(const Smi& class_id, const Object& target) const;
 
   static inline void SetEntry(const Array& array,
                               intptr_t index,
@@ -9920,6 +10111,9 @@
                                                            TypeArguments,
                                                            TypeArguments>>;
 
+using MegamorphicCacheEntries =
+    ArrayOfTuplesView<MegamorphicCache::EntryType, std::tuple<Smi, Object>>;
+
 void DumpTypeTable(Isolate* isolate);
 void DumpTypeArgumentsTable(Isolate* isolate);
 
diff --git a/runtime/vm/object_reload.cc b/runtime/vm/object_reload.cc
index f9eb462..0dba671 100644
--- a/runtime/vm/object_reload.cc
+++ b/runtime/vm/object_reload.cc
@@ -4,10 +4,13 @@
 
 #include "vm/object.h"
 
+#include "vm/code_patcher.h"
 #include "vm/hash_table.h"
 #include "vm/isolate_reload.h"
 #include "vm/log.h"
+#include "vm/object_store.h"
 #include "vm/resolver.h"
+#include "vm/stub_code.h"
 #include "vm/symbols.h"
 
 namespace dart {
@@ -89,6 +92,63 @@
 #endif
 }
 
+void Code::ResetSwitchableCalls(Zone* zone) const {
+#if !defined(TARGET_ARCH_DBC)
+  if (is_optimized()) {
+    return;  // No switchable calls in optimized code.
+  }
+
+  const Object& owner = Object::Handle(zone, this->owner());
+  if (!owner.IsFunction()) {
+    return;  // No switchable calls in stub code.
+  }
+
+  const Array& ic_data_array =
+      Array::Handle(zone, Function::Cast(owner).ic_data_array());
+  if (ic_data_array.IsNull()) {
+    // The megamorphic miss stub and some recognized function doesn't populate
+    // their ic_data_array. Check this only happens for functions without IC
+    // calls.
+#if defined(DEBUG)
+    const PcDescriptors& descriptors =
+        PcDescriptors::Handle(zone, pc_descriptors());
+    PcDescriptors::Iterator iter(descriptors, RawPcDescriptors::kIcCall);
+    while (iter.MoveNext()) {
+      FATAL1("%s has IC calls but no ic_data_array\n", owner.ToCString());
+    }
+#endif
+    return;
+  }
+  ICData& ic_data = ICData::Handle(zone);
+  Object& data = Object::Handle(zone);
+  for (intptr_t i = 1; i < ic_data_array.Length(); i++) {
+    ic_data ^= ic_data_array.At(i);
+    if (ic_data.rebind_rule() != ICData::kInstance) {
+      continue;
+    }
+    if (ic_data.NumArgsTested() != 1) {
+      continue;
+    }
+    uword pc = GetPcForDeoptId(ic_data.deopt_id(), RawPcDescriptors::kIcCall);
+    CodePatcher::GetInstanceCallAt(pc, *this, &data);
+    // This check both avoids unnecessary patching to reduce log spam and
+    // prevents patching over breakpoint stubs.
+    if (!data.IsICData()) {
+      const Code& stub =
+          ic_data.is_tracking_exactness()
+              ? StubCode::OneArgCheckInlineCacheWithExactnessCheck()
+              : StubCode::OneArgCheckInlineCache();
+      CodePatcher::PatchInstanceCallAt(pc, *this, ic_data, stub);
+      if (FLAG_trace_ic) {
+        OS::PrintErr("Instance call at %" Px
+                     " resetting to polymorphic dispatch, %s\n",
+                     pc, ic_data.ToCString());
+      }
+    }
+  }
+#endif
+}
+
 void Bytecode::ResetICDatas(Zone* zone) const {
   // Iterate over the Bytecode's object pool and reset all ICDatas.
   const ObjectPool& pool = ObjectPool::Handle(zone, object_pool());
@@ -362,8 +422,10 @@
       PatchClass::Handle(PatchClass::New(*this, Script::Handle(script())));
   ASSERT(!patch.IsNull());
   const Library& lib = Library::Handle(library());
-  patch.set_library_kernel_data(ExternalTypedData::Handle(lib.kernel_data()));
-  patch.set_library_kernel_offset(lib.kernel_offset());
+  if (!lib.is_declared_in_bytecode()) {
+    patch.set_library_kernel_data(ExternalTypedData::Handle(lib.kernel_data()));
+    patch.set_library_kernel_offset(lib.kernel_offset());
+  }
 
   const Array& funcs = Array::Handle(functions());
   Function& func = Function::Handle();
diff --git a/runtime/vm/object_service.cc b/runtime/vm/object_service.cc
index 3616d4e..3786d8e 100644
--- a/runtime/vm/object_service.cc
+++ b/runtime/vm/object_service.cc
@@ -169,7 +169,7 @@
     const ClassHeapStats* stats = class_table->StatsWithUpdatedSize(id());
     if (stats != NULL) {
       JSONObject allocation_stats(&jsobj, "_allocationStats");
-      stats->PrintToJSONObject(*this, &allocation_stats);
+      stats->PrintToJSONObject(*this, &allocation_stats, /*internal*/ true);
     }
   }
 }
@@ -729,6 +729,10 @@
   Object::PrintJSONImpl(stream, ref);
 }
 
+void ParameterTypeCheck::PrintJSONImpl(JSONStream* stream, bool ref) const {
+  Object::PrintJSONImpl(stream, ref);
+}
+
 void SingleTargetCache::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   AddCommonObjectProperties(&jsobj, "Object", ref);
@@ -856,12 +860,6 @@
   PrintJSONInlineIntervals(&jsobj);
 }
 
-void Code::set_await_token_positions(const Array& await_token_positions) const {
-#if !defined(DART_PRECOMPILED_RUNTIME)
-  StorePointer(&raw_ptr()->await_token_positions_, await_token_positions.raw());
-#endif
-}
-
 void Bytecode::PrintJSONImpl(JSONStream* stream, bool ref) const {
   // N.B. This is polymorphic with Code.
 
diff --git a/runtime/vm/os_android.cc b/runtime/vm/os_android.cc
index 196620f..807b7c9 100644
--- a/runtime/vm/os_android.cc
+++ b/runtime/vm/os_android.cc
@@ -175,9 +175,13 @@
 // into a architecture specific file e.g: os_ia32_linux.cc
 intptr_t OS::ActivationFrameAlignment() {
 #if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64) ||                   \
-    defined(TARGET_ARCH_ARM64)
+    defined(TARGET_ARCH_ARM64) ||                                              \
+    defined(TARGET_ARCH_DBC) &&                                                \
+        (defined(HOST_ARCH_IA32) || defined(HOST_ARCH_X64) ||                  \
+         defined(HOST_ARCH_ARM64))
   const int kMinimumAlignment = 16;
-#elif defined(TARGET_ARCH_ARM) || defined(TARGET_ARCH_DBC)
+#elif defined(TARGET_ARCH_ARM) ||                                              \
+    defined(TARGET_ARCH_DBC) && defined(HOST_ARCH_ARM)
   const int kMinimumAlignment = 8;
 #else
 #error Unsupported architecture.
diff --git a/runtime/vm/os_fuchsia.cc b/runtime/vm/os_fuchsia.cc
index 869c5b2..517f3c4 100644
--- a/runtime/vm/os_fuchsia.cc
+++ b/runtime/vm/os_fuchsia.cc
@@ -130,9 +130,13 @@
 // into a architecture specific file e.g: os_ia32_fuchsia.cc
 intptr_t OS::ActivationFrameAlignment() {
 #if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64) ||                   \
-    defined(TARGET_ARCH_ARM64)
+    defined(TARGET_ARCH_ARM64) ||                                              \
+    defined(TARGET_ARCH_DBC) &&                                                \
+        (defined(HOST_ARCH_IA32) || defined(HOST_ARCH_X64) ||                  \
+         defined(HOST_ARCH_ARM64))
   const int kMinimumAlignment = 16;
-#elif defined(TARGET_ARCH_ARM) || defined(TARGET_ARCH_DBC)
+#elif defined(TARGET_ARCH_ARM) ||                                              \
+    defined(TARGET_ARCH_DBC) && defined(HOST_ARCH_ARM)
   const int kMinimumAlignment = 8;
 #else
 #error Unsupported architecture.
diff --git a/runtime/vm/os_linux.cc b/runtime/vm/os_linux.cc
index 2ca3bb6..fb65cd1 100644
--- a/runtime/vm/os_linux.cc
+++ b/runtime/vm/os_linux.cc
@@ -497,6 +497,7 @@
 intptr_t OS::ActivationFrameAlignment() {
 #if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64) ||                   \
     defined(TARGET_ARCH_ARM64) || defined(TARGET_ARCH_DBC)
+  // DBC alignment should be at least as much as host architecture.
   const int kMinimumAlignment = 16;
 #elif defined(TARGET_ARCH_ARM)
   const int kMinimumAlignment = 8;
diff --git a/runtime/vm/os_macos.cc b/runtime/vm/os_macos.cc
index be6455d..ddbd18f 100644
--- a/runtime/vm/os_macos.cc
+++ b/runtime/vm/os_macos.cc
@@ -16,7 +16,7 @@
 #include <sys/time.h>        // NOLINT
 #include <unistd.h>          // NOLINT
 #if HOST_OS_IOS
-#include <syslog.h>      // NOLINT
+#include <syslog.h>  // NOLINT
 #endif
 
 #include "platform/utils.h"
@@ -140,7 +140,7 @@
 #elif TARGET_ARCH_X64
   return 16;  // iOS simulator
 #elif TARGET_ARCH_DBC
-  return 16;
+  return 16;  // Should be at least as much as any host architecture.
 #else
 #error Unimplemented
 #endif
@@ -212,7 +212,6 @@
       __builtin_extract_return_addr(__builtin_return_address(0)));
 }
 
-
 void OS::Print(const char* format, ...) {
 #if HOST_OS_IOS
   va_list args;
diff --git a/runtime/vm/os_thread.h b/runtime/vm/os_thread.h
index 7d27cc8..ffbbd96 100644
--- a/runtime/vm/os_thread.h
+++ b/runtime/vm/os_thread.h
@@ -11,6 +11,7 @@
 #include "vm/allocation.h"
 #include "vm/globals.h"
 
+// On iOS, thread_local requires iOS 9+.
 #if !HOST_OS_IOS
 #define HAS_C11_THREAD_LOCAL 1
 #endif
diff --git a/runtime/vm/os_win.cc b/runtime/vm/os_win.cc
index 1ff4eb9..c0f8e39 100644
--- a/runtime/vm/os_win.cc
+++ b/runtime/vm/os_win.cc
@@ -175,9 +175,11 @@
 }
 
 intptr_t OS::ActivationFrameAlignment() {
-#if defined(TARGET_ARCH_ARM64)
+#if defined(TARGET_ARCH_ARM64) ||                                              \
+    defined(TARGET_ARCH_DBC) && defined(HOST_ARCH_ARM64)
   return 16;
-#elif defined(TARGET_ARCH_ARM)
+#elif defined(TARGET_ARCH_ARM) ||                                              \
+    defined(TARGET_ARCH_DBC) && defined(HOST_ARCH_ARM)
   return 8;
 #elif defined(_WIN64)
   // Windows 64-bit ABI requires the stack to be 16-byte aligned.
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc
index 17a4a79..6abaac0 100644
--- a/runtime/vm/parser.cc
+++ b/runtime/vm/parser.cc
@@ -49,7 +49,7 @@
     : thread_(thread),
       function_(function),
       code_(Code::Handle(zone(), function.unoptimized_code())),
-      node_sequence_(NULL),
+      scope_(NULL),
       regexp_compile_data_(NULL),
       function_type_arguments_(NULL),
       parent_type_arguments_(NULL),
@@ -177,12 +177,6 @@
   ASSERT(has_finally_return_temp_var());
 }
 
-void ParsedFunction::SetNodeSequence(SequenceNode* node_sequence) {
-  ASSERT(node_sequence_ == NULL);
-  ASSERT(node_sequence != NULL);
-  node_sequence_ = node_sequence;
-}
-
 void ParsedFunction::SetRegExpCompileData(
     RegExpCompileData* regexp_compile_data) {
   ASSERT(regexp_compile_data_ == NULL);
@@ -206,7 +200,7 @@
 
 void ParsedFunction::AllocateVariables() {
   ASSERT(!function().IsIrregexpFunction());
-  LocalScope* scope = node_sequence()->scope();
+  LocalScope* scope = this->scope();
   const intptr_t num_fixed_params = function().num_fixed_parameters();
   const intptr_t num_opt_params = function().NumOptionalParameters();
   const intptr_t num_params = num_fixed_params + num_opt_params;
diff --git a/runtime/vm/parser.h b/runtime/vm/parser.h
index efbb8f9..2328080 100644
--- a/runtime/vm/parser.h
+++ b/runtime/vm/parser.h
@@ -11,7 +11,6 @@
 #include "platform/assert.h"
 #include "platform/globals.h"
 #include "vm/allocation.h"
-#include "vm/ast.h"
 #include "vm/class_finalizer.h"
 #include "vm/hash_table.h"
 #include "vm/kernel.h"
@@ -56,8 +55,12 @@
   const Function& function() const { return function_; }
   const Code& code() const { return code_; }
 
-  SequenceNode* node_sequence() const { return node_sequence_; }
-  void SetNodeSequence(SequenceNode* node_sequence);
+  LocalScope* scope() const { return scope_; }
+  void set_scope(LocalScope* scope) {
+    ASSERT(scope_ == nullptr);
+    ASSERT(scope != nullptr);
+    scope_ = scope;
+  }
 
   RegExpCompileData* regexp_compile_data() const {
     return regexp_compile_data_;
@@ -211,8 +214,8 @@
 
   LocalVariable* ParameterVariable(intptr_t i) const {
     ASSERT((i >= 0) && (i < function_.NumParameters()));
-    ASSERT(node_sequence() != nullptr && node_sequence()->scope() != nullptr);
-    return node_sequence()->scope()->VariableAt(i);
+    ASSERT(scope() != nullptr);
+    return scope()->VariableAt(i);
   }
 
   void SetDefaultFunctionTypeArguments(const TypeArguments& value) {
@@ -227,7 +230,7 @@
   Thread* thread_;
   const Function& function_;
   Code& code_;
-  SequenceNode* node_sequence_;
+  LocalScope* scope_;
   RegExpCompileData* regexp_compile_data_;
   LocalVariable* function_type_arguments_;
   LocalVariable* parent_type_arguments_;
diff --git a/runtime/vm/program_visitor.cc b/runtime/vm/program_visitor.cc
index 42ef355..91c0267 100644
--- a/runtime/vm/program_visitor.cc
+++ b/runtime/vm/program_visitor.cc
@@ -631,13 +631,6 @@
           list_ = DedupList(list_);
           code_.set_deopt_info_array(list_);
         }
-#ifndef PRODUCT
-        list_ = code_.await_token_positions();
-        if (!list_.IsNull()) {
-          list_ = DedupList(list_);
-          code_.set_await_token_positions(list_);
-        }
-#endif  // !PRODUCT
         list_ = code_.static_calls_target_table();
         if (!list_.IsNull()) {
           list_ = DedupList(list_);
diff --git a/runtime/vm/raw_object.cc b/runtime/vm/raw_object.cc
index 0658a6d..24a79f8 100644
--- a/runtime/vm/raw_object.cc
+++ b/runtime/vm/raw_object.cc
@@ -438,6 +438,7 @@
 REGULAR_VISITOR(Library)
 REGULAR_VISITOR(LibraryPrefix)
 REGULAR_VISITOR(Namespace)
+REGULAR_VISITOR(ParameterTypeCheck)
 REGULAR_VISITOR(SingleTargetCache)
 REGULAR_VISITOR(UnlinkedCall)
 REGULAR_VISITOR(ICData)
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 1028af1..e63d22b 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -807,7 +807,12 @@
   int16_t num_type_arguments_;  // Number of type arguments in flattened vector.
   uint16_t num_native_fields_;
   uint32_t state_bits_;
-  NOT_IN_PRECOMPILED(intptr_t kernel_offset_);
+
+#if !defined(DART_PRECOMPILED_RUNTIME)
+  typedef BitField<uint32_t, bool, 0, 1> IsDeclaredInBytecode;
+  typedef BitField<uint32_t, uint32_t, 1, 31> BinaryDeclarationOffset;
+  uint32_t binary_declaration_;
+#endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
   friend class Instance;
   friend class Isolate;
@@ -1034,13 +1039,17 @@
   RawFunction* callback_target_;
 
   VISIT_TO(RawObject*, callback_target_);
+  RawObject** to_snapshot(Snapshot::Kind kind) { return to(); }
 
-  // Callback id for callbacks, otherwise 0.
+  // Callback id for callbacks.
   //
   // The callbacks ids are used so that native callbacks can lookup their own
   // code objects, since native code doesn't pass code objects into function
   // calls. The callback id is also used to for verifying that callbacks are
   // called on the correct isolate. See DLRT_VerifyCallbackIsolate for details.
+  //
+  // Will be 0 for non-callbacks. Check 'callback_target_' to determine if this
+  // is a callback or not.
   uint32_t callback_id_;
 };
 
@@ -1211,9 +1220,12 @@
   bool is_dart_scheme_;
   bool debuggable_;          // True if debugger can stop in library.
   bool is_in_fullsnapshot_;  // True if library is in a full snapshot.
-  NOT_IN_PRECOMPILED(intptr_t kernel_offset_);  // Offset of this library's
-                                                // kernel data in the overall
-                                                // kernel program.
+
+#if !defined(DART_PRECOMPILED_RUNTIME)
+  typedef BitField<uint32_t, bool, 0, 1> IsDeclaredInBytecode;
+  typedef BitField<uint32_t, uint32_t, 1, 31> BinaryDeclarationOffset;
+  uint32_t binary_declaration_;
+#endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
   friend class Class;
   friend class Isolate;
@@ -1318,7 +1330,6 @@
   NOT_IN_PRECOMPILED(RawArray* deopt_info_array_);
   // (code-offset, function, code) triples.
   NOT_IN_PRECOMPILED(RawArray* static_calls_target_table_);
-  NOT_IN_PRODUCT(RawArray* await_token_positions_);
   // If return_address_metadata_ is a Smi, it is the offset to the prologue.
   // Else, return_address_metadata_ is null.
   NOT_IN_PRODUCT(RawObject* return_address_metadata_);
@@ -1705,6 +1716,18 @@
   friend class SnapshotReader;
 };
 
+class RawParameterTypeCheck : public RawObject {
+  RAW_HEAP_OBJECT_IMPLEMENTATION(ParameterTypeCheck);
+  intptr_t index_;
+  VISIT_FROM(RawObject*, param_);
+  RawAbstractType* param_;
+  RawAbstractType* type_or_bound_;
+  RawString* name_;
+  RawSubtypeTestCache* cache_;
+  VISIT_TO(RawObject*, cache_);
+  RawObject** to_snapshot(Snapshot::Kind kind) { return to(); }
+};
+
 class RawSingleTargetCache : public RawObject {
   RAW_HEAP_OBJECT_IMPLEMENTATION(SingleTargetCache);
   VISIT_FROM(RawObject*, target_);
diff --git a/runtime/vm/raw_object_fields.cc b/runtime/vm/raw_object_fields.cc
index c4aac7c..35d4fdc 100644
--- a/runtime/vm/raw_object_fields.cc
+++ b/runtime/vm/raw_object_fields.cc
@@ -112,6 +112,10 @@
   F(Bytecode, closures_)                                                       \
   F(ExceptionHandlers, handled_types_data_)                                    \
   F(Context, parent_)                                                          \
+  F(ParameterTypeCheck, param_)                                                \
+  F(ParameterTypeCheck, type_or_bound_)                                        \
+  F(ParameterTypeCheck, name_)                                                 \
+  F(ParameterTypeCheck, cache_)                                                \
   F(SingleTargetCache, target_)                                                \
   F(UnlinkedCall, target_name_)                                                \
   F(UnlinkedCall, args_descriptor_)                                            \
diff --git a/runtime/vm/raw_object_snapshot.cc b/runtime/vm/raw_object_snapshot.cc
index d734dbb..22a7016 100644
--- a/runtime/vm/raw_object_snapshot.cc
+++ b/runtime/vm/raw_object_snapshot.cc
@@ -71,22 +71,6 @@
   }
 }
 
-RawAbstractType* AbstractType::ReadFrom(SnapshotReader* reader,
-                                        intptr_t object_id,
-                                        intptr_t tags,
-                                        Snapshot::Kind kind,
-                                        bool as_reference) {
-  UNREACHABLE();  // AbstractType is an abstract class.
-  return NULL;
-}
-
-void RawAbstractType::WriteTo(SnapshotWriter* writer,
-                              intptr_t object_id,
-                              Snapshot::Kind kind,
-                              bool as_reference) {
-  UNREACHABLE();  // AbstractType is an abstract class.
-}
-
 RawType* Type::ReadFrom(SnapshotReader* reader,
                         intptr_t object_id,
                         intptr_t tags,
@@ -364,22 +348,6 @@
   }
 }
 
-RawPatchClass* PatchClass::ReadFrom(SnapshotReader* reader,
-                                    intptr_t object_id,
-                                    intptr_t tags,
-                                    Snapshot::Kind kind,
-                                    bool as_reference) {
-  UNREACHABLE();
-  return PatchClass::null();
-}
-
-void RawPatchClass::WriteTo(SnapshotWriter* writer,
-                            intptr_t object_id,
-                            Snapshot::Kind kind,
-                            bool as_reference) {
-  UNREACHABLE();
-}
-
 RawClosure* Closure::ReadFrom(SnapshotReader* reader,
                               intptr_t object_id,
                               intptr_t tags,
@@ -407,326 +375,6 @@
   UNREACHABLE();
 }
 
-RawClosureData* ClosureData::ReadFrom(SnapshotReader* reader,
-                                      intptr_t object_id,
-                                      intptr_t tags,
-                                      Snapshot::Kind kind,
-                                      bool as_reference) {
-  UNREACHABLE();
-  return ClosureData::null();
-}
-
-void RawClosureData::WriteTo(SnapshotWriter* writer,
-                             intptr_t object_id,
-                             Snapshot::Kind kind,
-                             bool as_reference) {
-  UNREACHABLE();
-}
-
-RawSignatureData* SignatureData::ReadFrom(SnapshotReader* reader,
-                                          intptr_t object_id,
-                                          intptr_t tags,
-                                          Snapshot::Kind kind,
-                                          bool as_reference) {
-  UNREACHABLE();
-  return SignatureData::null();
-}
-
-void RawSignatureData::WriteTo(SnapshotWriter* writer,
-                               intptr_t object_id,
-                               Snapshot::Kind kind,
-                               bool as_reference) {
-  UNREACHABLE();
-}
-
-RawRedirectionData* RedirectionData::ReadFrom(SnapshotReader* reader,
-                                              intptr_t object_id,
-                                              intptr_t tags,
-                                              Snapshot::Kind kind,
-                                              bool as_reference) {
-  UNREACHABLE();
-  return RedirectionData::null();
-}
-
-void RawRedirectionData::WriteTo(SnapshotWriter* writer,
-                                 intptr_t object_id,
-                                 Snapshot::Kind kind,
-                                 bool as_reference) {
-  UNREACHABLE();
-}
-
-RawFfiTrampolineData* FfiTrampolineData::ReadFrom(SnapshotReader* reader,
-                                                  intptr_t object_id,
-                                                  intptr_t tags,
-                                                  Snapshot::Kind kind,
-                                                  bool as_reference) {
-  UNREACHABLE();
-  return FfiTrampolineData::null();
-}
-
-void RawFfiTrampolineData::WriteTo(SnapshotWriter* writer,
-                                   intptr_t object_id,
-                                   Snapshot::Kind kind,
-                                   bool as_reference) {
-  UNREACHABLE();
-}
-
-RawFunction* Function::ReadFrom(SnapshotReader* reader,
-                                intptr_t object_id,
-                                intptr_t tags,
-                                Snapshot::Kind kind,
-                                bool as_reference) {
-  UNREACHABLE();
-  return Function::null();
-}
-
-void RawFunction::WriteTo(SnapshotWriter* writer,
-                          intptr_t object_id,
-                          Snapshot::Kind kind,
-                          bool as_reference) {
-  UNREACHABLE();
-}
-
-RawField* Field::ReadFrom(SnapshotReader* reader,
-                          intptr_t object_id,
-                          intptr_t tags,
-                          Snapshot::Kind kind,
-                          bool as_reference) {
-  UNREACHABLE();
-  return Field::null();
-}
-
-void RawField::WriteTo(SnapshotWriter* writer,
-                       intptr_t object_id,
-                       Snapshot::Kind kind,
-                       bool as_reference) {
-  UNREACHABLE();
-}
-
-RawScript* Script::ReadFrom(SnapshotReader* reader,
-                            intptr_t object_id,
-                            intptr_t tags,
-                            Snapshot::Kind kind,
-                            bool as_reference) {
-  UNREACHABLE();
-  return Script::null();
-}
-
-void RawScript::WriteTo(SnapshotWriter* writer,
-                        intptr_t object_id,
-                        Snapshot::Kind kind,
-                        bool as_reference) {
-  UNREACHABLE();
-}
-
-RawLibrary* Library::ReadFrom(SnapshotReader* reader,
-                              intptr_t object_id,
-                              intptr_t tags,
-                              Snapshot::Kind kind,
-                              bool as_reference) {
-  UNREACHABLE();
-  return Library::null();
-}
-
-void RawLibrary::WriteTo(SnapshotWriter* writer,
-                         intptr_t object_id,
-                         Snapshot::Kind kind,
-                         bool as_reference) {
-  UNREACHABLE();
-}
-
-RawLibraryPrefix* LibraryPrefix::ReadFrom(SnapshotReader* reader,
-                                          intptr_t object_id,
-                                          intptr_t tags,
-                                          Snapshot::Kind kind,
-                                          bool as_reference) {
-  UNREACHABLE();
-  return LibraryPrefix::null();
-}
-
-void RawLibraryPrefix::WriteTo(SnapshotWriter* writer,
-                               intptr_t object_id,
-                               Snapshot::Kind kind,
-                               bool as_reference) {
-  UNREACHABLE();
-}
-
-RawNamespace* Namespace::ReadFrom(SnapshotReader* reader,
-                                  intptr_t object_id,
-                                  intptr_t tags,
-                                  Snapshot::Kind kind,
-                                  bool as_reference) {
-  UNREACHABLE();
-  return Namespace::null();
-}
-
-void RawNamespace::WriteTo(SnapshotWriter* writer,
-                           intptr_t object_id,
-                           Snapshot::Kind kind,
-                           bool as_reference) {
-  UNREACHABLE();
-}
-
-RawKernelProgramInfo* KernelProgramInfo::ReadFrom(SnapshotReader* reader,
-                                                  intptr_t object_id,
-                                                  intptr_t tags,
-                                                  Snapshot::Kind kind,
-                                                  bool as_reference) {
-  UNREACHABLE();
-  return KernelProgramInfo::null();
-}
-
-void RawKernelProgramInfo::WriteTo(SnapshotWriter* writer,
-                                   intptr_t object_id,
-                                   Snapshot::Kind kind,
-                                   bool as_reference) {
-  UNREACHABLE();
-}
-
-RawCode* Code::ReadFrom(SnapshotReader* reader,
-                        intptr_t object_id,
-                        intptr_t tags,
-                        Snapshot::Kind kind,
-                        bool as_reference) {
-  UNREACHABLE();
-  return Code::null();
-}
-
-void RawCode::WriteTo(SnapshotWriter* writer,
-                      intptr_t object_id,
-                      Snapshot::Kind kind,
-                      bool as_reference) {
-  UNREACHABLE();
-}
-
-RawBytecode* Bytecode::ReadFrom(SnapshotReader* reader,
-                                intptr_t object_id,
-                                intptr_t tags,
-                                Snapshot::Kind kind,
-                                bool as_reference) {
-  UNREACHABLE();
-  return Bytecode::null();
-}
-
-void RawBytecode::WriteTo(SnapshotWriter* writer,
-                          intptr_t object_id,
-                          Snapshot::Kind kind,
-                          bool as_reference) {
-  UNREACHABLE();
-}
-
-RawInstructions* Instructions::ReadFrom(SnapshotReader* reader,
-                                        intptr_t object_id,
-                                        intptr_t tags,
-                                        Snapshot::Kind kind,
-                                        bool as_reference) {
-  UNREACHABLE();
-  return Instructions::null();
-}
-
-void RawInstructions::WriteTo(SnapshotWriter* writer,
-                              intptr_t object_id,
-                              Snapshot::Kind kind,
-                              bool as_reference) {
-  UNREACHABLE();
-}
-
-RawObjectPool* ObjectPool::ReadFrom(SnapshotReader* reader,
-                                    intptr_t object_id,
-                                    intptr_t tags,
-                                    Snapshot::Kind kind,
-                                    bool as_reference) {
-  UNREACHABLE();
-  return ObjectPool::null();
-}
-
-void RawObjectPool::WriteTo(SnapshotWriter* writer,
-                            intptr_t object_id,
-                            Snapshot::Kind kind,
-                            bool as_reference) {
-  UNREACHABLE();
-}
-
-RawPcDescriptors* PcDescriptors::ReadFrom(SnapshotReader* reader,
-                                          intptr_t object_id,
-                                          intptr_t tags,
-                                          Snapshot::Kind kind,
-                                          bool as_reference) {
-  UNREACHABLE();
-  return PcDescriptors::null();
-}
-
-void RawPcDescriptors::WriteTo(SnapshotWriter* writer,
-                               intptr_t object_id,
-                               Snapshot::Kind kind,
-                               bool as_reference) {
-  UNREACHABLE();
-}
-
-RawCodeSourceMap* CodeSourceMap::ReadFrom(SnapshotReader* reader,
-                                          intptr_t object_id,
-                                          intptr_t tags,
-                                          Snapshot::Kind kind,
-                                          bool as_reference) {
-  UNREACHABLE();
-  return CodeSourceMap::null();
-}
-
-void RawCodeSourceMap::WriteTo(SnapshotWriter* writer,
-                               intptr_t object_id,
-                               Snapshot::Kind kind,
-                               bool as_reference) {
-  UNREACHABLE();
-}
-
-RawStackMap* StackMap::ReadFrom(SnapshotReader* reader,
-                                intptr_t object_id,
-                                intptr_t tags,
-                                Snapshot::Kind kind,
-                                bool as_reference) {
-  UNREACHABLE();
-  return StackMap::null();
-}
-
-void RawStackMap::WriteTo(SnapshotWriter* writer,
-                          intptr_t object_id,
-                          Snapshot::Kind kind,
-                          bool as_reference) {
-  UNREACHABLE();
-}
-
-RawLocalVarDescriptors* LocalVarDescriptors::ReadFrom(SnapshotReader* reader,
-                                                      intptr_t object_id,
-                                                      intptr_t tags,
-                                                      Snapshot::Kind kind,
-                                                      bool as_reference) {
-  UNREACHABLE();
-  return LocalVarDescriptors::null();
-}
-
-void RawLocalVarDescriptors::WriteTo(SnapshotWriter* writer,
-                                     intptr_t object_id,
-                                     Snapshot::Kind kind,
-                                     bool as_reference) {
-  UNREACHABLE();
-}
-
-RawExceptionHandlers* ExceptionHandlers::ReadFrom(SnapshotReader* reader,
-                                                  intptr_t object_id,
-                                                  intptr_t tags,
-                                                  Snapshot::Kind kind,
-                                                  bool as_reference) {
-  UNREACHABLE();
-  return ExceptionHandlers::null();
-}
-
-void RawExceptionHandlers::WriteTo(SnapshotWriter* writer,
-                                   intptr_t object_id,
-                                   Snapshot::Kind kind,
-                                   bool as_reference) {
-  UNREACHABLE();
-}
-
 RawContext* Context::ReadFrom(SnapshotReader* reader,
                               intptr_t object_id,
                               intptr_t tags,
@@ -836,101 +484,72 @@
   UNREACHABLE();
 }
 
-RawSingleTargetCache* SingleTargetCache::ReadFrom(SnapshotReader* reader,
-                                                  intptr_t object_id,
-                                                  intptr_t tags,
-                                                  Snapshot::Kind kind,
-                                                  bool as_reference) {
-  UNREACHABLE();
-  return SingleTargetCache::null();
-}
+#define MESSAGE_SNAPSHOT_UNREACHABLE(type)                                     \
+  Raw##type* type::ReadFrom(SnapshotReader* reader, intptr_t object_id,        \
+                            intptr_t tags, Snapshot::Kind kind,                \
+                            bool as_reference) {                               \
+    UNREACHABLE();                                                             \
+    return type::null();                                                       \
+  }                                                                            \
+  void Raw##type::WriteTo(SnapshotWriter* writer, intptr_t object_id,          \
+                          Snapshot::Kind kind, bool as_reference) {            \
+    UNREACHABLE();                                                             \
+  }
 
-void RawSingleTargetCache::WriteTo(SnapshotWriter* writer,
-                                   intptr_t object_id,
-                                   Snapshot::Kind kind,
-                                   bool as_reference) {
-  UNREACHABLE();
-}
+#define MESSAGE_SNAPSHOT_ILLEGAL(type)                                         \
+  Raw##type* type::ReadFrom(SnapshotReader* reader, intptr_t object_id,        \
+                            intptr_t tags, Snapshot::Kind kind,                \
+                            bool as_reference) {                               \
+    UNREACHABLE();                                                             \
+    return type::null();                                                       \
+  }                                                                            \
+  void Raw##type::WriteTo(SnapshotWriter* writer, intptr_t object_id,          \
+                          Snapshot::Kind kind, bool as_reference) {            \
+    writer->SetWriteException(Exceptions::kArgument,                           \
+                              "Illegal argument in isolate message"            \
+                              " : (object is a " #type ")");                   \
+  }
 
-RawUnlinkedCall* UnlinkedCall::ReadFrom(SnapshotReader* reader,
-                                        intptr_t object_id,
-                                        intptr_t tags,
-                                        Snapshot::Kind kind,
-                                        bool as_reference) {
-  UNREACHABLE();
-  return UnlinkedCall::null();
-}
+MESSAGE_SNAPSHOT_UNREACHABLE(AbstractType);
+MESSAGE_SNAPSHOT_UNREACHABLE(Bool);
+MESSAGE_SNAPSHOT_UNREACHABLE(Bytecode);
+MESSAGE_SNAPSHOT_UNREACHABLE(ClosureData);
+MESSAGE_SNAPSHOT_UNREACHABLE(Code);
+MESSAGE_SNAPSHOT_UNREACHABLE(CodeSourceMap);
+MESSAGE_SNAPSHOT_UNREACHABLE(Error);
+MESSAGE_SNAPSHOT_UNREACHABLE(ExceptionHandlers);
+MESSAGE_SNAPSHOT_UNREACHABLE(FfiTrampolineData);
+MESSAGE_SNAPSHOT_UNREACHABLE(Field);
+MESSAGE_SNAPSHOT_UNREACHABLE(Function);
+MESSAGE_SNAPSHOT_UNREACHABLE(ICData);
+MESSAGE_SNAPSHOT_UNREACHABLE(Instructions);
+MESSAGE_SNAPSHOT_UNREACHABLE(KernelProgramInfo);
+MESSAGE_SNAPSHOT_UNREACHABLE(Library);
+MESSAGE_SNAPSHOT_UNREACHABLE(LibraryPrefix);
+MESSAGE_SNAPSHOT_UNREACHABLE(LocalVarDescriptors);
+MESSAGE_SNAPSHOT_UNREACHABLE(MegamorphicCache);
+MESSAGE_SNAPSHOT_UNREACHABLE(Namespace);
+MESSAGE_SNAPSHOT_UNREACHABLE(ObjectPool);
+MESSAGE_SNAPSHOT_UNREACHABLE(ParameterTypeCheck);
+MESSAGE_SNAPSHOT_UNREACHABLE(PatchClass);
+MESSAGE_SNAPSHOT_UNREACHABLE(PcDescriptors);
+MESSAGE_SNAPSHOT_UNREACHABLE(RedirectionData);
+MESSAGE_SNAPSHOT_UNREACHABLE(Script);
+MESSAGE_SNAPSHOT_UNREACHABLE(SignatureData);
+MESSAGE_SNAPSHOT_UNREACHABLE(SingleTargetCache);
+MESSAGE_SNAPSHOT_UNREACHABLE(StackMap);
+MESSAGE_SNAPSHOT_UNREACHABLE(String);
+MESSAGE_SNAPSHOT_UNREACHABLE(SubtypeTestCache);
+MESSAGE_SNAPSHOT_UNREACHABLE(TypedDataBase);
+MESSAGE_SNAPSHOT_UNREACHABLE(UnlinkedCall);
+MESSAGE_SNAPSHOT_UNREACHABLE(UnwindError);
 
-void RawUnlinkedCall::WriteTo(SnapshotWriter* writer,
-                              intptr_t object_id,
-                              Snapshot::Kind kind,
-                              bool as_reference) {
-  UNREACHABLE();
-}
-
-RawICData* ICData::ReadFrom(SnapshotReader* reader,
-                            intptr_t object_id,
-                            intptr_t tags,
-                            Snapshot::Kind kind,
-                            bool as_reference) {
-  UNREACHABLE();
-  return ICData::null();
-}
-
-void RawICData::WriteTo(SnapshotWriter* writer,
-                        intptr_t object_id,
-                        Snapshot::Kind kind,
-                        bool as_reference) {
-  UNREACHABLE();
-}
-
-RawMegamorphicCache* MegamorphicCache::ReadFrom(SnapshotReader* reader,
-                                                intptr_t object_id,
-                                                intptr_t tags,
-                                                Snapshot::Kind kind,
-                                                bool as_reference) {
-  UNREACHABLE();
-  return MegamorphicCache::null();
-}
-
-void RawMegamorphicCache::WriteTo(SnapshotWriter* writer,
-                                  intptr_t object_id,
-                                  Snapshot::Kind kind,
-                                  bool as_reference) {
-  UNREACHABLE();
-}
-
-RawSubtypeTestCache* SubtypeTestCache::ReadFrom(SnapshotReader* reader,
-                                                intptr_t object_id,
-                                                intptr_t tags,
-                                                Snapshot::Kind kind,
-                                                bool as_reference) {
-  UNREACHABLE();
-  return SubtypeTestCache::null();
-}
-
-void RawSubtypeTestCache::WriteTo(SnapshotWriter* writer,
-                                  intptr_t object_id,
-                                  Snapshot::Kind kind,
-                                  bool as_reference) {
-  UNREACHABLE();
-}
-
-RawError* Error::ReadFrom(SnapshotReader* reader,
-                          intptr_t object_id,
-                          intptr_t tags,
-                          Snapshot::Kind kind,
-                          bool as_referenec) {
-  UNREACHABLE();
-  return Error::null();  // Error is an abstract class.
-}
-
-void RawError::WriteTo(SnapshotWriter* writer,
-                       intptr_t object_id,
-                       Snapshot::Kind kind,
-                       bool as_reference) {
-  UNREACHABLE();  // Error is an abstract class.
-}
+MESSAGE_SNAPSHOT_ILLEGAL(DynamicLibrary);
+MESSAGE_SNAPSHOT_ILLEGAL(MirrorReference);
+MESSAGE_SNAPSHOT_ILLEGAL(Pointer);
+MESSAGE_SNAPSHOT_ILLEGAL(ReceivePort);
+MESSAGE_SNAPSHOT_ILLEGAL(StackTrace);
+MESSAGE_SNAPSHOT_ILLEGAL(UserTag);
 
 RawApiError* ApiError::ReadFrom(SnapshotReader* reader,
                                 intptr_t object_id,
@@ -1047,22 +666,6 @@
   visitor.VisitPointers(from(), to());
 }
 
-RawUnwindError* UnwindError::ReadFrom(SnapshotReader* reader,
-                                      intptr_t object_id,
-                                      intptr_t tags,
-                                      Snapshot::Kind kind,
-                                      bool as_reference) {
-  UNREACHABLE();
-  return UnwindError::null();
-}
-
-void RawUnwindError::WriteTo(SnapshotWriter* writer,
-                             intptr_t object_id,
-                             Snapshot::Kind kind,
-                             bool as_reference) {
-  UNREACHABLE();
-}
-
 RawInstance* Instance::ReadFrom(SnapshotReader* reader,
                                 intptr_t object_id,
                                 intptr_t tags,
@@ -1196,22 +799,6 @@
   writer->WriteDouble(ptr()->value_);
 }
 
-RawString* String::ReadFrom(SnapshotReader* reader,
-                            intptr_t object_id,
-                            intptr_t tags,
-                            Snapshot::Kind kind,
-                            bool as_reference) {
-  UNREACHABLE();  // String is an abstract class.
-  return String::null();
-}
-
-void RawString::WriteTo(SnapshotWriter* writer,
-                        intptr_t object_id,
-                        Snapshot::Kind kind,
-                        bool as_reference) {
-  UNREACHABLE();  // String is an abstract class.
-}
-
 template <typename StringType, typename CharacterType, typename CallbackType>
 void String::ReadFromImpl(SnapshotReader* reader,
                           String* str_obj,
@@ -1365,22 +952,6 @@
                 ptr()->external_data_);
 }
 
-RawBool* Bool::ReadFrom(SnapshotReader* reader,
-                        intptr_t object_id,
-                        intptr_t tags,
-                        Snapshot::Kind kind,
-                        bool as_reference) {
-  UNREACHABLE();
-  return Bool::null();
-}
-
-void RawBool::WriteTo(SnapshotWriter* writer,
-                      intptr_t object_id,
-                      Snapshot::Kind kind,
-                      bool as_reference) {
-  UNREACHABLE();
-}
-
 RawArray* Array::ReadFrom(SnapshotReader* reader,
                           intptr_t object_id,
                           intptr_t tags,
@@ -1722,22 +1293,6 @@
   writer->Write<double>(ptr()->value_[1]);
 }
 
-RawTypedDataBase* TypedDataBase::ReadFrom(SnapshotReader* reader,
-                                          intptr_t object_id,
-                                          intptr_t tags,
-                                          Snapshot::Kind kind,
-                                          bool as_reference) {
-  UNREACHABLE();  // TypedDataBase is an abstract class.
-  return NULL;
-}
-
-void RawTypedDataBase::WriteTo(SnapshotWriter* writer,
-                               intptr_t object_id,
-                               Snapshot::Kind kind,
-                               bool as_reference) {
-  UNREACHABLE();  // TypedDataBase is an abstract class.
-}
-
 RawTypedData* TypedData::ReadFrom(SnapshotReader* reader,
                                   intptr_t object_id,
                                   intptr_t tags,
@@ -2021,38 +1576,6 @@
   return view.raw();
 }
 
-RawPointer* Pointer::ReadFrom(SnapshotReader* reader,
-                              intptr_t object_id,
-                              intptr_t tags,
-                              Snapshot::Kind kind,
-                              bool as_reference) {
-  FATAL("Snapshotting Pointers is not supported");
-  UNREACHABLE();
-}
-
-void RawPointer::WriteTo(SnapshotWriter* writer,
-                         intptr_t object_id,
-                         Snapshot::Kind kind,
-                         bool as_reference) {
-  FATAL("Snapshotting Pointers is not supported");
-}
-
-RawDynamicLibrary* DynamicLibrary::ReadFrom(SnapshotReader* reader,
-                                            intptr_t object_id,
-                                            intptr_t tags,
-                                            Snapshot::Kind kind,
-                                            bool as_reference) {
-  FATAL("Snapshotting DynamicLibraries is not supported");
-  UNREACHABLE();
-}
-
-void RawDynamicLibrary::WriteTo(SnapshotWriter* writer,
-                                intptr_t object_id,
-                                Snapshot::Kind kind,
-                                bool as_reference) {
-  FATAL("Snapshotting DynamicLibraries is not supported");
-}
-
 RawCapability* Capability::ReadFrom(SnapshotReader* reader,
                                     intptr_t object_id,
                                     intptr_t tags,
@@ -2080,29 +1603,6 @@
   writer->Write<uint64_t>(ptr()->id_);
 }
 
-RawReceivePort* ReceivePort::ReadFrom(SnapshotReader* reader,
-                                      intptr_t object_id,
-                                      intptr_t tags,
-                                      Snapshot::Kind kind,
-                                      bool as_reference) {
-  UNREACHABLE();
-  return ReceivePort::null();
-}
-
-void RawReceivePort::WriteTo(SnapshotWriter* writer,
-                             intptr_t object_id,
-                             Snapshot::Kind kind,
-                             bool as_reference) {
-  if (kind == Snapshot::kMessage) {
-    // We do not allow objects with native fields in an isolate message.
-    writer->SetWriteException(Exceptions::kArgument,
-                              "Illegal argument in isolate message"
-                              " : (object is a RawReceivePort)");
-  } else {
-    UNREACHABLE();
-  }
-}
-
 RawSendPort* SendPort::ReadFrom(SnapshotReader* reader,
                                 intptr_t object_id,
                                 intptr_t tags,
@@ -2196,25 +1696,6 @@
       });
 }
 
-RawStackTrace* StackTrace::ReadFrom(SnapshotReader* reader,
-                                    intptr_t object_id,
-                                    intptr_t tags,
-                                    Snapshot::Kind kind,
-                                    bool as_reference) {
-  UNREACHABLE();  // StackTraces are not sent in a snapshot.
-  return StackTrace::null();
-}
-
-void RawStackTrace::WriteTo(SnapshotWriter* writer,
-                            intptr_t object_id,
-                            Snapshot::Kind kind,
-                            bool as_reference) {
-  ASSERT(kind == Snapshot::kMessage);
-  writer->SetWriteException(Exceptions::kArgument,
-                            "Illegal argument in isolate message"
-                            " : (object is a stacktrace)");
-}
-
 RawRegExp* RegExp::ReadFrom(SnapshotReader* reader,
                             intptr_t object_id,
                             intptr_t tags,
@@ -2309,50 +1790,4 @@
   visitor.VisitPointers(from(), to());
 }
 
-RawMirrorReference* MirrorReference::ReadFrom(SnapshotReader* reader,
-                                              intptr_t object_id,
-                                              intptr_t tags,
-                                              Snapshot::Kind kind,
-                                              bool as_referenec) {
-  UNREACHABLE();
-  return MirrorReference::null();
-}
-
-void RawMirrorReference::WriteTo(SnapshotWriter* writer,
-                                 intptr_t object_id,
-                                 Snapshot::Kind kind,
-                                 bool as_reference) {
-  if (kind == Snapshot::kMessage) {
-    // We do not allow objects with native fields in an isolate message.
-    writer->SetWriteException(Exceptions::kArgument,
-                              "Illegal argument in isolate message"
-                              " : (object is a MirrorReference)");
-  } else {
-    UNREACHABLE();
-  }
-}
-
-RawUserTag* UserTag::ReadFrom(SnapshotReader* reader,
-                              intptr_t object_id,
-                              intptr_t tags,
-                              Snapshot::Kind kind,
-                              bool as_reference) {
-  UNREACHABLE();
-  return UserTag::null();
-}
-
-void RawUserTag::WriteTo(SnapshotWriter* writer,
-                         intptr_t object_id,
-                         Snapshot::Kind kind,
-                         bool as_reference) {
-  if (kind == Snapshot::kMessage) {
-    // We do not allow objects with native fields in an isolate message.
-    writer->SetWriteException(Exceptions::kArgument,
-                              "Illegal argument in isolate message"
-                              " : (object is a UserTag)");
-  } else {
-    UNREACHABLE();
-  }
-}
-
 }  // namespace dart
diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc
index 674623b..c886658 100644
--- a/runtime/vm/runtime_entry.cc
+++ b/runtime/vm/runtime_entry.cc
@@ -75,6 +75,15 @@
             NULL,
             "Deoptimize in named function on stack overflow checks");
 
+DEFINE_FLAG(bool,
+            unopt_monomorphic_calls,
+            true,
+            "Enable specializing monomorphic calls from unoptimized code.");
+DEFINE_FLAG(bool,
+            unopt_megamorphic_calls,
+            false,
+            "Enable specializing megamorphic calls from unoptimized code.");
+
 DECLARE_FLAG(int, reload_every);
 DECLARE_FLAG(bool, reload_every_optimized);
 DECLARE_FLAG(bool, reload_every_back_off);
@@ -356,7 +365,7 @@
       TypeArguments::CheckedHandle(zone, arguments.ArgAt(1));
   const TypeArguments& function_type_arguments =
       TypeArguments::CheckedHandle(zone, arguments.ArgAt(2));
-  ASSERT(!type.IsNull() && !type.IsInstantiated());
+  ASSERT(!type.IsNull());
   ASSERT(instantiator_type_arguments.IsNull() ||
          instantiator_type_arguments.IsInstantiated());
   ASSERT(function_type_arguments.IsNull() ||
@@ -1075,6 +1084,88 @@
   return result.raw();
 }
 
+static void TrySwitchInstanceCall(const ICData& ic_data,
+                                  const Function& target_function) {
+#if !defined(TARGET_ARCH_DBC) && !defined(DART_PRECOMPILED_RUNTIME)
+  // Monomorphic/megamorphic calls only check the receiver CID.
+  if (ic_data.NumArgsTested() != 1) return;
+
+  ASSERT(ic_data.rebind_rule() == ICData::kInstance);
+
+  // Monomorphic/megamorphic calls don't record exactness.
+  if (ic_data.is_tracking_exactness()) return;
+
+#if !defined(PRODUCT)
+  // Monomorphic/megamorphic do not check the isolate's stepping flag.
+  if (Isolate::Current()->has_attempted_stepping()) return;
+#endif
+
+  Thread* thread = Thread::Current();
+  DartFrameIterator iterator(thread,
+                             StackFrameIterator::kNoCrossThreadIteration);
+  StackFrame* caller_frame = iterator.NextFrame();
+  ASSERT(caller_frame->IsDartFrame());
+
+  // Monomorphic/megamorphic calls are only for unoptimized code.
+  if (caller_frame->is_interpreted()) return;
+  Zone* zone = thread->zone();
+  const Code& caller_code = Code::Handle(zone, caller_frame->LookupDartCode());
+  if (caller_code.is_optimized()) return;
+
+  // Code is detached from its function. This will prevent us from resetting
+  // the switchable call later because resets are function based and because
+  // the ic_data_array belongs to the function instead of the code. This should
+  // only happen because of reload, but it sometimes happens with KBC mixed mode
+  // probably through a race between foreground and background compilation.
+  const Function& caller_function =
+      Function::Handle(zone, caller_code.function());
+  if (caller_function.unoptimized_code() != caller_code.raw()) {
+    return;
+  }
+
+  intptr_t num_checks = ic_data.NumberOfChecks();
+
+  // Monomorphic call.
+  if (FLAG_unopt_monomorphic_calls && (num_checks == 1)) {
+    // A call site in the monomorphic state does not load the arguments
+    // descriptor, so do not allow transition to this state if the callee
+    // needs it.
+    if (target_function.HasOptionalParameters() ||
+        target_function.IsGeneric()) {
+      return;
+    }
+
+    const Array& data = Array::Handle(zone, ic_data.entries());
+    const Code& target = Code::Handle(zone, target_function.EnsureHasCode());
+    CodePatcher::PatchInstanceCallAt(caller_frame->pc(), caller_code, data,
+                                     target);
+    if (FLAG_trace_ic) {
+      OS::PrintErr("Instance call at %" Px
+                   " switching to monomorphic dispatch, %s\n",
+                   caller_frame->pc(), ic_data.ToCString());
+    }
+    return;  // Success.
+  }
+
+  // Megamorphic call.
+  if (FLAG_unopt_megamorphic_calls &&
+      (num_checks > FLAG_max_polymorphic_checks)) {
+    const MegamorphicCache& cache =
+        MegamorphicCache::Handle(zone, ic_data.AsMegamorphicCache());
+    ic_data.set_is_megamorphic(true);
+    CodePatcher::PatchInstanceCallAt(caller_frame->pc(), caller_code, cache,
+                                     StubCode::MegamorphicCall());
+    if (FLAG_trace_ic) {
+      OS::PrintErr("Instance call at %" Px
+                   " switching to megamorphic dispatch, %s\n",
+                   caller_frame->pc(), ic_data.ToCString());
+    }
+    return;  // Success.
+  }
+
+#endif  // !defined(TARGET_ARCH_DBC) && !defined(DART_PRECOMPILED_RUNTIME)
+}
+
 // Perform the subtype and return constant function based on the result.
 static RawFunction* ComputeTypeCheckTarget(const Instance& receiver,
                                            const AbstractType& type,
@@ -1091,7 +1182,8 @@
 
 static RawFunction* InlineCacheMissHandler(
     const GrowableArray<const Instance*>& args,  // Checked arguments only.
-    const ICData& ic_data) {
+    const ICData& ic_data,
+    intptr_t count = 1) {
   const Instance& receiver = *args[0];
   ArgumentsDescriptor arguments_descriptor(
       Array::Handle(ic_data.arguments_descriptor()));
@@ -1136,13 +1228,13 @@
                                        ic_data.receivers_static_type())),
                                    receiver);
       ic_data.AddReceiverCheck(
-          receiver.GetClassId(), target_function,
-          /*count=*/1, /*exactness=*/state.CollapseSuperTypeExactness());
+          receiver.GetClassId(), target_function, count,
+          /*exactness=*/state.CollapseSuperTypeExactness());
 #else
       UNREACHABLE();
 #endif
     } else {
-      ic_data.AddReceiverCheck(args[0]->GetClassId(), target_function);
+      ic_data.AddReceiverCheck(args[0]->GetClassId(), target_function, count);
     }
   } else {
     GrowableArray<intptr_t> class_ids(args.length());
@@ -1150,7 +1242,7 @@
     for (intptr_t i = 0; i < args.length(); i++) {
       class_ids.Add(args[i]->GetClassId());
     }
-    ic_data.AddCheck(class_ids, target_function);
+    ic_data.AddCheck(class_ids, target_function, count);
   }
   if (FLAG_trace_ic_miss_in_optimized || FLAG_trace_ic) {
     DartFrameIterator iterator(Thread::Current(),
@@ -1174,6 +1266,9 @@
                    receiver.GetClassId(), target_function.ToCString());
     }
   }
+
+  TrySwitchInstanceCall(ic_data, target_function);
+
   return target_function.raw();
 }
 
@@ -1289,14 +1384,15 @@
 #endif
 
 // Handle a miss of a single target cache.
-//   Arg0: Receiver.
+//   Arg1: Receiver.
+//   Arg0: Stub out.
 //   Returns: the ICData used to continue with a polymorphic call.
-DEFINE_RUNTIME_ENTRY(SingleTargetMiss, 1) {
+DEFINE_RUNTIME_ENTRY(SingleTargetMiss, 2) {
 #if defined(TARGET_ARCH_DBC)
   // DBC does not use switchable calls.
   UNREACHABLE();
 #else
-  const Instance& receiver = Instance::CheckedHandle(zone, arguments.ArgAt(0));
+  const Instance& receiver = Instance::CheckedHandle(zone, arguments.ArgAt(1));
 
   DartFrameIterator iterator(thread,
                              StackFrameIterator::kNoCrossThreadIteration);
@@ -1360,6 +1456,7 @@
       cache.set_upper_limit(upper);
       // Return the ICData. The single target stub will jump to continue in the
       // IC call stub.
+      arguments.SetArgAt(0, StubCode::ICCallThroughCode());
       arguments.SetReturn(ic_data);
       return;
     }
@@ -1373,18 +1470,24 @@
 
   // Return the ICData. The single target stub will jump to continue in the
   // IC call stub.
+  arguments.SetArgAt(0, stub);
   arguments.SetReturn(ic_data);
 #endif
 }
 
-DEFINE_RUNTIME_ENTRY(UnlinkedCall, 2) {
+// Handle the first use of an instance call
+//   Arg2: UnlinkedCall.
+//   Arg1: Receiver.
+//   Arg0: Stub out.
+//   Returns: the ICData used to continue with a polymorphic call.
+DEFINE_RUNTIME_ENTRY(UnlinkedCall, 3) {
 #if defined(TARGET_ARCH_DBC)
   // DBC does not use switchable calls.
   UNREACHABLE();
 #else
-  const Instance& receiver = Instance::CheckedHandle(zone, arguments.ArgAt(0));
+  const Instance& receiver = Instance::CheckedHandle(zone, arguments.ArgAt(1));
   const UnlinkedCall& unlinked =
-      UnlinkedCall::CheckedHandle(zone, arguments.ArgAt(1));
+      UnlinkedCall::CheckedHandle(zone, arguments.ArgAt(2));
 
   DartFrameIterator iterator(thread,
                              StackFrameIterator::kNoCrossThreadIteration);
@@ -1426,6 +1529,7 @@
 
     // Return the ICData. The miss stub will jump to continue in the IC call
     // stub.
+    arguments.SetArgAt(0, StubCode::ICCallThroughCode());
     arguments.SetReturn(ic_data);
     return;
   }
@@ -1438,19 +1542,41 @@
 
   // Return the ICData. The miss stub will jump to continue in the IC lookup
   // stub.
+  arguments.SetArgAt(0, stub);
   arguments.SetReturn(ic_data);
 #endif  // !DBC
 }
 
+#if !defined(DART_PRECOMPILED_RUNTIME) && !defined(TARGET_ARCH_DBC)
+static RawICData* FindICDataForInstanceCall(Zone* zone,
+                                            const Code& code,
+                                            uword pc) {
+  uword pc_offset = pc - code.PayloadStart();
+  const PcDescriptors& descriptors =
+      PcDescriptors::Handle(zone, code.pc_descriptors());
+  PcDescriptors::Iterator iter(descriptors, RawPcDescriptors::kIcCall);
+  intptr_t deopt_id = -1;
+  while (iter.MoveNext()) {
+    if (iter.PcOffset() == pc_offset) {
+      deopt_id = iter.DeoptId();
+      break;
+    }
+  }
+  ASSERT(deopt_id != -1);
+  return Function::Handle(zone, code.function()).FindICData(deopt_id);
+}
+#endif  // !defined(DART_PRECOMPILED_RUNTIME) && !defined(TARGET_ARCH_DBC)
+
 // Handle a miss of a megamorphic cache.
-//   Arg0: Receiver.
+//   Arg1: Receiver.
+//   Arg0: continuation Code (out parameter).
 //   Returns: the ICData used to continue with a polymorphic call.
-DEFINE_RUNTIME_ENTRY(MonomorphicMiss, 1) {
+DEFINE_RUNTIME_ENTRY(MonomorphicMiss, 2) {
 #if defined(TARGET_ARCH_DBC)
   // DBC does not use switchable calls.
   UNREACHABLE();
-#else
-  const Instance& receiver = Instance::CheckedHandle(zone, arguments.ArgAt(0));
+#elif defined(DART_PRECOMPILED_RUNTIME)
+  const Instance& receiver = Instance::CheckedHandle(zone, arguments.ArgAt(1));
 
   DartFrameIterator iterator(thread,
                              StackFrameIterator::kNoCrossThreadIteration);
@@ -1520,6 +1646,7 @@
                                          stub);
       // Return the ICData. The miss stub will jump to continue in the IC call
       // stub.
+      arguments.SetArgAt(0, StubCode::ICCallThroughCode());
       arguments.SetReturn(ic_data);
       return;
     }
@@ -1533,6 +1660,46 @@
 
   // Return the ICData. The miss stub will jump to continue in the IC lookup
   // stub.
+  arguments.SetArgAt(0, stub);
+  arguments.SetReturn(ic_data);
+#else   // JIT
+  DartFrameIterator iterator(thread,
+                             StackFrameIterator::kNoCrossThreadIteration);
+  StackFrame* caller_frame = iterator.NextFrame();
+  ASSERT(caller_frame->IsDartFrame());
+  ASSERT(!caller_frame->is_interpreted());
+  const Code& caller_code = Code::Handle(zone, caller_frame->LookupDartCode());
+  ASSERT(!caller_code.is_optimized());
+
+  const ICData& ic_data = ICData::Handle(
+      zone, FindICDataForInstanceCall(zone, caller_code, caller_frame->pc()));
+  RELEASE_ASSERT(!ic_data.IsNull());
+
+  ASSERT(ic_data.NumArgsTested() == 1);
+  const Code& stub = ic_data.is_tracking_exactness()
+                         ? StubCode::OneArgCheckInlineCacheWithExactnessCheck()
+                         : StubCode::OneArgCheckInlineCache();
+  CodePatcher::PatchInstanceCallAt(caller_frame->pc(), caller_code, ic_data,
+                                   stub);
+  if (FLAG_trace_ic) {
+    OS::PrintErr("Instance call at %" Px
+                 " switching to polymorphic dispatch, %s\n",
+                 caller_frame->pc(), ic_data.ToCString());
+  }
+
+  const Instance& receiver = Instance::CheckedHandle(zone, arguments.ArgAt(1));
+  // ICData can be shared between unoptimized and optimized code, so beware that
+  // the new receiver class may have already been added through the optimized
+  // code.
+  if (!ic_data.HasReceiverClassId(receiver.GetClassId())) {
+    GrowableArray<const Instance*> args(1);
+    args.Add(&receiver);
+    // Don't count during insertion because the IC stub we continue through will
+    // do an increment.
+    intptr_t count = 0;
+    InlineCacheMissHandler(args, ic_data, count);
+  }
+  arguments.SetArgAt(0, stub);
   arguments.SetReturn(ic_data);
 #endif  // !defined(TARGET_ARCH_DBC)
 }
@@ -1629,7 +1796,6 @@
   } else {
     const MegamorphicCache& cache = MegamorphicCache::Cast(ic_data_or_cache);
     // Insert function found into cache and return it.
-    cache.EnsureCapacity();
     const Smi& class_id = Smi::Handle(zone, Smi::New(cls.id()));
     cache.Insert(class_id, target_function);
   }
@@ -2077,7 +2243,7 @@
   // process the stack overflow now and leave the interrupt for next
   // time.
   // TODO(regis): Warning: IsCalleeFrameOf is overridden in stack_frame_dbc.h.
-  if (interpreter_stack_overflow ||
+  if (interpreter_stack_overflow || !thread->os_thread()->HasStackHeadroom() ||
       IsCalleeFrameOf(thread->saved_stack_limit(), stack_pos)) {
     // Use the preallocated stack overflow exception to avoid calling
     // into dart code.
@@ -2217,7 +2383,7 @@
   }
   ASSERT(frame->IsDartFrame());
   const Code& caller_code = Code::Handle(zone, frame->LookupDartCode());
-  ASSERT(caller_code.is_optimized());
+  RELEASE_ASSERT(caller_code.is_optimized());
   const Function& target_function = Function::Handle(
       zone, caller_code.GetStaticCallTargetFunctionAt(frame->pc()));
 
@@ -2241,6 +2407,52 @@
 #endif
 }
 
+// The caller must be a monomorphic call from unoptimized code.
+// Patch call to point to new target.
+DEFINE_RUNTIME_ENTRY(FixCallersTargetMonomorphic, 0) {
+#if !defined(DART_PRECOMPILED_RUNTIME)
+  StackFrameIterator iterator(ValidationPolicy::kDontValidateFrames, thread,
+                              StackFrameIterator::kNoCrossThreadIteration);
+  StackFrame* frame = iterator.NextFrame();
+  ASSERT(frame != NULL);
+  while (frame->IsStubFrame() || frame->IsExitFrame()) {
+    frame = iterator.NextFrame();
+    ASSERT(frame != NULL);
+  }
+  if (frame->IsEntryFrame()) {
+    // Since function's current code is always unpatched, the entry frame always
+    // calls to unpatched code.
+    UNREACHABLE();
+  }
+  ASSERT(frame->IsDartFrame());
+  const Code& caller_code = Code::Handle(zone, frame->LookupDartCode());
+  RELEASE_ASSERT(!caller_code.is_optimized());
+
+  Object& cache = Object::Handle(zone);
+  const Code& old_target_code = Code::Handle(
+      zone, CodePatcher::GetInstanceCallAt(frame->pc(), caller_code, &cache));
+  const Function& target_function =
+      Function::Handle(zone, old_target_code.function());
+  const Code& current_target_code =
+      Code::Handle(zone, target_function.EnsureHasCode());
+  CodePatcher::PatchInstanceCallAt(frame->pc(), caller_code, cache,
+                                   current_target_code);
+  if (FLAG_trace_patching) {
+    OS::PrintErr(
+        "FixCallersTargetMonomorphic: caller %#" Px
+        " "
+        "target '%s' -> %#" Px " (%s)\n",
+        frame->pc(), target_function.ToFullyQualifiedCString(),
+        current_target_code.EntryPoint(),
+        current_target_code.is_optimized() ? "optimized" : "unoptimized");
+  }
+  ASSERT(!current_target_code.IsDisabled());
+  arguments.SetReturn(current_target_code);
+#else
+  UNREACHABLE();
+#endif
+}
+
 // The caller tried to allocate an instance via an invalidated allocation
 // stub.
 DEFINE_RUNTIME_ENTRY(FixAllocationStubTarget, 0) {
@@ -2353,6 +2565,7 @@
 
     // N.B.: Update the pending deopt table before updating the frame. The
     // profiler may attempt a stack walk in between.
+    ASSERT(!frame->is_interpreted());
     thread->isolate()->AddPendingDeopt(frame->fp(), deopt_pc);
     frame->MarkForLazyDeopt();
 
diff --git a/runtime/vm/runtime_entry_list.h b/runtime/vm/runtime_entry_list.h
index b0c241e..f5f806b 100644
--- a/runtime/vm/runtime_entry_list.h
+++ b/runtime/vm/runtime_entry_list.h
@@ -18,6 +18,7 @@
   V(GetFieldForDispatch)                                                       \
   V(ResolveCallFunction)                                                       \
   V(FixCallersTarget)                                                          \
+  V(FixCallersTargetMonomorphic)                                               \
   V(FixAllocationStubTarget)                                                   \
   V(InlineCacheMissHandlerOneArg)                                              \
   V(InlineCacheMissHandlerTwoArgs)                                             \
diff --git a/runtime/vm/scopes.h b/runtime/vm/scopes.h
index 70c6287..b1e0c2c 100644
--- a/runtime/vm/scopes.h
+++ b/runtime/vm/scopes.h
@@ -42,7 +42,7 @@
 //    c) [LocalVariable]s referring to values on the expression stack. Those are
 //       assigned by the flow graph builder. The indices of those variables are
 //       assigned by the flow graph builder (it simulates the expression stack
-//       height), they go from -NumVariabables - ExpressionHeight.
+//       height), they go from -NumVariables - ExpressionHeight.
 //
 //       -> These variables participate only partially in SSA renaming and can
 //          therefore only be used with [LoadLocalInstr]s and with
diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc
index 1db81b5..a5f12e0 100644
--- a/runtime/vm/service.cc
+++ b/runtime/vm/service.cc
@@ -4,6 +4,9 @@
 
 #include "vm/service.h"
 
+#include <memory>
+#include <utility>
+
 #include "include/dart_api.h"
 #include "include/dart_native_api.h"
 #include "platform/globals.h"
@@ -13,6 +16,7 @@
 #include "vm/compiler/jit/compiler.h"
 #include "vm/cpu.h"
 #include "vm/dart_api_impl.h"
+#include "vm/dart_api_message.h"
 #include "vm/dart_api_state.h"
 #include "vm/dart_entry.h"
 #include "vm/debugger.h"
@@ -1164,14 +1168,11 @@
   json_cobj.value.as_string = const_cast<char*>(event->ToCString());
   list_values[1] = &json_cobj;
 
-  // In certain cases (e.g. in the implementation of Dart_IsolateMakeRunnable)
-  // we do not have a current isolate/thread.
-  auto thread = Thread::Current();
-  if (thread != nullptr) {
-    TransitionVMToNative transition(thread);
-    Dart_PostCObject(ServiceIsolate::Port(), &list_cobj);
-  } else {
-    Dart_PostCObject(ServiceIsolate::Port(), &list_cobj);
+  ApiMessageWriter writer;
+  std::unique_ptr<Message> msg = writer.WriteCMessage(
+      &list_cobj, ServiceIsolate::Port(), Message::kNormalPriority);
+  if (msg != nullptr) {
+    PortMap::PostMessage(std::move(msg));
   }
 }
 
@@ -2988,8 +2989,10 @@
 
 class GetInstancesVisitor : public ObjectGraph::Visitor {
  public:
-  GetInstancesVisitor(const Class& cls, const Array& storage)
-      : cls_(cls), storage_(storage), count_(0) {}
+  GetInstancesVisitor(const Class& cls,
+                      ZoneGrowableHandlePtrArray<Object>* storage,
+                      intptr_t limit)
+      : cls_(cls), storage_(storage), limit_(limit), count_(0) {}
 
   virtual Direction VisitObject(ObjectGraph::StackIterator* it) {
     RawObject* raw_obj = it->Get();
@@ -3001,8 +3004,8 @@
     Object& obj = thread->ObjectHandle();
     obj = raw_obj;
     if (obj.GetClassId() == cls_.id()) {
-      if (!storage_.IsNull() && count_ < storage_.Length()) {
-        storage_.SetAt(count_, obj);
+      if (count_ < limit_) {
+        storage_->Add(Object::Handle(raw_obj));
       }
       ++count_;
     }
@@ -3013,7 +3016,8 @@
 
  private:
   const Class& cls_;
-  const Array& storage_;
+  ZoneGrowableHandlePtrArray<Object>* storage_;
+  const intptr_t limit_;
   intptr_t count_;
 };
 
@@ -3022,9 +3026,9 @@
 };
 
 static bool GetInstances(Thread* thread, JSONStream* js) {
-  const char* target_id = js->LookupParam("classId");
-  if (target_id == NULL) {
-    PrintMissingParamError(js, "classId");
+  const char* object_id = js->LookupParam("objectId");
+  if (object_id == NULL) {
+    PrintMissingParamError(js, "objectId");
     return true;
   }
   const char* limit_cstr = js->LookupParam("limit");
@@ -3037,14 +3041,20 @@
     PrintInvalidParamError(js, "limit");
     return true;
   }
-  const Object& obj = Object::Handle(LookupHeapObject(thread, target_id, NULL));
+
+  const Object& obj = Object::Handle(LookupHeapObject(thread, object_id, NULL));
   if (obj.raw() == Object::sentinel().raw() || !obj.IsClass()) {
-    PrintInvalidParamError(js, "classId");
+    PrintInvalidParamError(js, "objectId");
     return true;
   }
   const Class& cls = Class::Cast(obj);
-  Array& storage = Array::Handle(Array::New(limit));
-  GetInstancesVisitor visitor(cls, storage);
+
+  // Ensure the array and handles created below are promptly destroyed.
+  StackZone zone(thread);
+  HANDLESCOPE(thread);
+
+  ZoneGrowableHandlePtrArray<Object> storage(thread->zone(), limit);
+  GetInstancesVisitor visitor(cls, &storage, limit);
   ObjectGraph graph(thread);
   HeapIterationScope iteration_scope(Thread::Current(), true);
   graph.IterateObjects(&visitor);
@@ -3053,20 +3063,11 @@
   jsobj.AddProperty("type", "InstanceSet");
   jsobj.AddProperty("totalCount", count);
   {
-    JSONArray samples(&jsobj, "samples");
-    for (int i = 0; (i < storage.Length()) && (i < count); i++) {
-      const Object& sample = Object::Handle(storage.At(i));
-      samples.AddValue(sample);
+    JSONArray samples(&jsobj, "instances");
+    for (int i = 0; (i < limit) && (i < count); i++) {
+      samples.AddValue(storage.At(i));
     }
   }
-
-  // We nil out the array after generating the response to prevent
-  // reporting spurious references when looking for inbound references
-  // after looking at allInstances.
-  for (intptr_t i = 0; i < storage.Length(); i++) {
-    storage.SetAt(i, Object::null_object());
-  }
-
   return true;
 }
 
@@ -3656,6 +3657,18 @@
   TimelineEventRecorder* timeline_recorder = Timeline::recorder();
   // TODO(johnmccutchan): Return an error.
   ASSERT(timeline_recorder != NULL);
+  const char* name = timeline_recorder->name();
+  if ((strcmp(name, FUCHSIA_RECORDER_NAME) == 0) ||
+      (strcmp(name, SYSTRACE_RECORDER_NAME) == 0)) {
+    js->PrintError(kInvalidTimelineRequest,
+                   "A recorder of type \"%s\" is "
+                   "currently in use. As a result, timeline events are handled "
+                   "by the OS rather than the VM. See the VM service "
+                   "documentation for more details on where timeline events "
+                   "can be found for this recorder type.",
+                   timeline_recorder->name());
+    return true;
+  }
   int64_t time_origin_micros =
       Int64Parameter::Parse(js->LookupParam("timeOriginMicros"));
   int64_t time_extent_micros =
@@ -3958,11 +3971,9 @@
   return true;
 }
 
-static const MethodParameter* get_allocation_profile_params[] = {
-    RUNNABLE_ISOLATE_PARAMETER, NULL,
-};
-
-static bool GetAllocationProfile(Thread* thread, JSONStream* js) {
+static bool GetAllocationProfileImpl(Thread* thread,
+                                     JSONStream* js,
+                                     bool internal) {
   bool should_reset_accumulator = false;
   bool should_collect = false;
   if (js->HasParam("reset")) {
@@ -3974,7 +3985,7 @@
     }
   }
   if (js->HasParam("gc")) {
-    if (js->ParamIs("gc", "full")) {
+    if (js->ParamIs("gc", "true")) {
       should_collect = true;
     } else {
       PrintInvalidParamError(js, "gc");
@@ -3990,10 +4001,23 @@
     isolate->UpdateLastAllocationProfileGCTimestamp();
     isolate->heap()->CollectAllGarbage();
   }
-  isolate->class_table()->AllocationProfilePrintJSON(js);
+  isolate->class_table()->AllocationProfilePrintJSON(js, internal);
   return true;
 }
 
+static const MethodParameter* get_allocation_profile_params[] = {
+    RUNNABLE_ISOLATE_PARAMETER,
+    NULL,
+};
+
+static bool GetAllocationProfilePublic(Thread* thread, JSONStream* js) {
+  return GetAllocationProfileImpl(thread, js, false);
+}
+
+static bool GetAllocationProfile(Thread* thread, JSONStream* js) {
+  return GetAllocationProfileImpl(thread, js, true);
+}
+
 static const MethodParameter* collect_all_garbage_params[] = {
     RUNNABLE_ISOLATE_PARAMETER, NULL,
 };
@@ -4871,7 +4895,7 @@
     build_expression_evaluation_scope_params },
   { "_clearCpuProfile", ClearCpuProfile,
     clear_cpu_profile_params },
-  { "_clearVMTimeline", ClearVMTimeline,
+  { "clearVMTimeline", ClearVMTimeline,
     clear_vm_timeline_params, },
   { "_compileExpression", CompileExpression, compile_expression_params },
   { "_enableProfiler", EnableProfiler,
@@ -4882,6 +4906,8 @@
     evaluate_in_frame_params },
   { "_getAllocationProfile", GetAllocationProfile,
     get_allocation_profile_params },
+  { "getAllocationProfile", GetAllocationProfilePublic,
+    get_allocation_profile_params },
   { "_getAllocationSamples", GetAllocationSamples,
       get_allocation_samples_params },
   { "_getNativeAllocationSamples", GetNativeAllocationSamples,
@@ -4900,7 +4926,7 @@
     get_heap_map_params },
   { "_getInboundReferences", GetInboundReferences,
     get_inbound_references_params },
-  { "_getInstances", GetInstances,
+  { "getInstances", GetInstances,
     get_instances_params },
   { "getIsolate", GetIsolate,
     get_isolate_params },
@@ -4946,9 +4972,9 @@
     get_vm_metric_params },
   { "_getVMMetricList", GetVMMetricList,
     get_vm_metric_list_params },
-  { "_getVMTimeline", GetVMTimeline,
+  { "getVMTimeline", GetVMTimeline,
     get_vm_timeline_params },
-  { "_getVMTimelineFlags", GetVMTimelineFlags,
+  { "getVMTimelineFlags", GetVMTimelineFlags,
     get_vm_timeline_flags_params },
   { "invoke", Invoke, invoke_params },
   { "kill", Kill, kill_params },
@@ -4978,7 +5004,7 @@
     set_trace_class_allocation_params },
   { "setVMName", SetVMName,
     set_vm_name_params },
-  { "_setVMTimelineFlags", SetVMTimelineFlags,
+  { "setVMTimelineFlags", SetVMTimelineFlags,
     set_vm_timeline_flags_params },
   { "_collectAllGarbage", CollectAllGarbage,
     collect_all_garbage_params },
diff --git a/runtime/vm/service.h b/runtime/vm/service.h
index 3d2cb73..15d30ff 100644
--- a/runtime/vm/service.h
+++ b/runtime/vm/service.h
@@ -15,7 +15,7 @@
 namespace dart {
 
 #define SERVICE_PROTOCOL_MAJOR_VERSION 3
-#define SERVICE_PROTOCOL_MINOR_VERSION 17
+#define SERVICE_PROTOCOL_MINOR_VERSION 20
 
 class Array;
 class EmbedderServiceHandler;
diff --git a/runtime/vm/service/service.md b/runtime/vm/service/service.md
index e993091..2c5731c 100644
--- a/runtime/vm/service/service.md
+++ b/runtime/vm/service/service.md
@@ -1,8 +1,8 @@
-# Dart VM Service Protocol 3.17
+# Dart VM Service Protocol 3.20
 
 > Please post feedback to the [observatory-discuss group][discuss-list]
 
-This document describes of _version 3.17_ of the Dart VM Service Protocol. This
+This document describes of _version 3.20_ of the Dart VM Service Protocol. This
 protocol is used to communicate with a running Dart Virtual Machine.
 
 To use the Service Protocol, start the VM with the *--observe* flag.
@@ -27,9 +27,12 @@
   - [addBreakpoint](#addbreakpoint)
   - [addBreakpointWithScriptUri](#addbreakpointwithscripturi)
   - [addBreakpointAtEntry](#addbreakpointatentry)
+  - [clearVMTimeline](#clearvmtimeline)
   - [evaluate](#evaluate)
   - [evaluateInFrame](#evaluateinframe)
+  - [getAllocationProfile](#getallocationprofile)
   - [getFlagList](#getflaglist)
+  - [getInstances](#getinstances)
   - [getIsolate](#getisolate)
   - [getMemoryUsage](#getmemoryusage)
   - [getScripts](#getscripts)
@@ -38,6 +41,8 @@
   - [getStack](#getstack)
   - [getVersion](#getversion)
   - [getVM](#getvm)
+  - [getVMTimeline](#getvmtimeline)
+  - [getVMTimelineFlags](#getvmtimelineflags)
   - [invoke](#invoke)
   - [pause](#pause)
   - [kill](#kill)
@@ -49,13 +54,16 @@
   - [setLibraryDebuggable](#setlibrarydebuggable)
   - [setName](#setname)
   - [setVMName](#setvmname)
+  - [setVMTimelineFlags](#setvmtimelineflags)
   - [streamCancel](#streamcancel)
   - [streamListen](#streamlisten)
 - [Public Types](#public-types)
+  - [AllocationProfile](#allocationprofile)
   - [BoundField](#boundfield)
   - [BoundVariable](#boundvariable)
   - [Breakpoint](#breakpoint)
   - [Class](#class)
+  - [ClassHeapStats](#classheapstats)
   - [ClassList](#classlist)
   - [Code](#code)
   - [CodeKind](#codekind)
@@ -72,6 +80,7 @@
   - [Frame](#frame)
   - [Function](#function)
   - [Instance](#instance)
+  - [InstanceSet](#instanceset)
   - [Isolate](#isolate)
   - [Library](#library)
   - [LibraryDependency](#librarydependency)
@@ -95,6 +104,9 @@
   - [Stack](#stack)
   - [StepOption](#stepoption)
   - [Success](#success)
+  - [Timeline](#timeline)
+  - [TimelineEvent](#timelineevent)
+  - [TimelineFlags](#timelineflags)
   - [TypeArguments](#typearguments)
   - [UresolvedSourceLocation](#unresolvedsourcelocation)
   - [Version](#version)
@@ -200,8 +212,7 @@
 111 | Service already registered | Service with such name has already been registered by this client
 112 | Service disappeared | Failed to fulfill service request, likely service handler is no longer available
 113 | Expression compilation error | Request to compile expression failed
-
-
+114 | Invalid timeline request | The timeline related request could not be completed due to the current configuration
 
 ## Events
 
@@ -476,6 +487,17 @@
 
 Note that breakpoints are added and removed on a per-isolate basis.
 
+
+### clearVMTimeline
+
+``` 
+Success clearVMTimeline()
+```
+
+Clears all VM timeline events.
+
+See [Success](#success).
+
 ### invoke
 
 ```
@@ -589,6 +611,24 @@
 If the expression is evaluated successfully, an [@Instance](#instance)
 reference will be returned.
 
+### getAllocationProfile
+
+```
+AllocationProfile getAllocationProfile(string isolateId,
+                                       boolean reset [optional],
+                                       boolean gc [optional])
+```
+
+The _getAllocationProfile_ RPC is used to retrieve allocation information for a
+given isolate.
+
+If _reset_ is provided and is set to true, the allocation accumulators will be reset
+before collecting allocation information.
+
+If _gc_ is provided and is set to true, a garbage collection will be attempted
+before collecting allocation information. There is no guarantee that a garbage
+collection will be actually be performed.
+
 ### getFlagList
 
 ```
@@ -600,6 +640,21 @@
 
 See [FlagList](#flaglist).
 
+### getInstances
+
+```
+InstanceSet getInstances(string objectId,
+                         int limit)
+```
+
+The _getInstances_ RPC is used to retrieve a set of instances which are of a specific type.
+
+_objectId_ is the ID of the `Class` to retrieve instances for. _objectId_ must be the ID of a `Class`, otherwise an error is returned.
+
+_limit_ is the maximum number of instances to be returned.
+
+See [InstanceSet](#instanceset).
+
 ### getIsolate
 
 ```
@@ -747,6 +802,43 @@
 
 See [VM](#vm).
 
+
+### getVMTimeline
+
+```
+Timeline getVMTimeline(int timeOriginMicros,
+                       int timeExtentMicros)
+```
+
+The _getVMTimeline_ RPC is used to retrieve an object which contains VM timeline
+events.
+
+The _timeOriginMicros_ parameter is the beginning of the time range used to filter
+timeline events. It uses the same monotonic clock as dart:developer's `Timeline.now`
+and the VM embedding API's `Dart_TimelineGetMicros`.
+
+The _timeExtentMicros_ parameter specifies how large the time range used to filter
+timeline events should be.
+
+For example, given _timeOriginMicros_ and _timeExtentMicros_, only timeline events
+from the following time range will be returned: `(timeOriginMicros, timeOriginMicros + timeExtentMicros)`.
+
+If _getVMTimeline_ is invoked while the current recorder is one of Fuchsia or
+Systrace, the _114_ error code, invalid timeline request, will be returned as
+timeline events are handled by the OS in these modes.
+
+### getVMTimelineFlags
+
+```
+TimelineFlags getVMTimelineFlags()
+```
+
+The _getVMTimelineFlags_ RPC returns information about the current VM timeline configuration.
+
+To change which timeline streams are currently enabled, see [setVMTimelineFlags](#setvmtimelineflags).
+
+See [TimelineFlags](#timelineflags).
+
 ### pause
 
 ```
@@ -773,7 +865,6 @@
 
 ### reloadSources
 
-
 ```
 ReloadReport reloadSources(string isolateId,
                            bool force [optional],
@@ -913,6 +1004,22 @@
 
 See [Success](#success).
 
+### setVMTimelineFlags
+
+```
+Success setVMTimelineFlags(string[] recordedStreams)
+```
+
+The _setVMTimelineFlags_ RPC is used to set which timeline streams are enabled.
+
+The _recordedStreams_ parameter is the list of all timeline streams which are
+to be enabled. Streams not explicitly specified will be disabled. Invalid stream
+names are ignored.
+
+To get the list of currently enabled timeline streams, see [getVMTimelineFlags](#getvmtimelineflags).
+
+See [Success](#success).
+
 ### streamCancel
 
 ```
@@ -1047,6 +1154,28 @@
 This means that _PermittedValues_ is a _string_ with two potential values,
 _Value1_ and _Value2_.
 
+### AllocationProfile
+
+```
+class AllocationProfile extends Response {
+  // Allocation information for all class types.
+  ClassHeapStats[] members;
+
+  // Information about memory usage for the isolate.
+  MemoryUsage memoryUsage;
+
+  // The timestamp of the last accumulator reset.
+  //
+  // If the accumulators have not been reset, this field is not present.
+  int dateLastAccumulatorReset [optional];
+
+  // The timestamp of the last manually triggered GC.
+  //
+  // If a GC has not been triggered manually, this field is not present.
+  int dateLastServiceGC [optional];
+}
+```
+
 ### BoundField
 
 ```
@@ -1186,6 +1315,29 @@
 
 A _Class_ provides information about a Dart language class.
 
+### ClassHeapStats
+
+```
+class ClassHeapStats extends Response {
+  // The class for which this memory information is associated.
+  @Class class;
+
+  // The number of bytes allocated for instances of class since the
+  // accumulator was last reset.
+  int accumulatedSize;
+
+  // The number of bytes currently allocated for instances of class.
+  int bytesCurrent;
+
+  // The number of instances of class which have been allocated since
+  // the accumulator was last reset.
+  int instancesAccumulated;
+
+  // The number of instances of class which are currently alive.
+  int instancesCurrent;
+}
+```
+
 ### ClassList
 
 ```
@@ -2158,6 +2310,20 @@
 
 An _Isolate_ object provides information about one isolate in the VM.
 
+### InstanceSet
+
+```
+class InstanceSet extends Response {
+  // The number of instances of the requested type currently allocated.
+  int totalCount;
+
+  // An array of instances of the requested type.
+  @Instance[] instances;
+}
+```
+
+See [getInstances](#getinstances).
+
 ### Library
 
 ```
@@ -2701,6 +2867,21 @@
 
 The _Success_ type is used to indicate that an operation completed successfully.
 
+### Timeline
+
+```
+class Timeline extends Response {
+  // A list of timeline events.
+  TimelineEvent[] traceEvents;
+
+  // The start of the period of time in which traceEvents were collected.
+  int timeOriginMicros;
+
+  // The duration of time covered by the timeline.
+  int timeExtentMicros;
+}
+```
+
 ### TimelineEvent
 
 ```
@@ -2710,6 +2891,23 @@
 
 An _TimelineEvent_ is an arbitrary map that contains a [Trace Event Format](https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview) event.
 
+### TimelineFlags
+
+```
+class TimelineFlags extends Response {
+  // The name of the recorder currently in use. Recorder types include, but are
+  // not limited to: Callback, Endless, Fuchsia, Ring, Startup, and Systrace.
+  // Set to "null" if no recorder is currently set. 
+  string recorderName;
+
+  // The list of all available timeline streams.
+  string[] availableStreams;
+
+  // The list of timeline streams that are currently enabled.
+  string[] recordedStreams;
+}
+```
+
 ### TypeArguments
 
 ```
@@ -2835,7 +3033,7 @@
 
 version | comments
 ------- | --------
-1.0 | initial revision
+1.0 | Initial revision
 2.0 | Describe protocol version 2.0.
 3.0 | Describe protocol version 3.0.  Added UnresolvedSourceLocation.  Added Sentinel return to getIsolate.  Add AddedBreakpointWithScriptUri.  Removed Isolate.entry. The type of VM.pid was changed from string to int.  Added VMUpdate events.  Add offset and count parameters to getObject() and offset and count fields to Instance. Added ServiceExtensionAdded event.
 3.1 | Add the getSourceReport RPC.  The getObject RPC now accepts offset and count for string objects.  String objects now contain length, offset, and count properties.
@@ -2855,5 +3053,8 @@
 3.15 | Added `disableBreakpoints` parameter to `invoke`, `evaluate`, and `evaluateInFrame`.
 3.16 | Add 'getMemoryUsage' RPC and 'MemoryUsage' object.
 3.17 | Add 'Logging' event kind and the LogRecord class.
+3.18 | Add 'getAllocationProfile' RPC and 'AllocationProfile' and 'ClassHeapStats' objects.
+3.19 | Add 'clearVMTimeline', 'getVMTimeline', 'getVMTimelineFlags', 'setVMTimelineFlags', 'Timeline', and 'TimelineFlags'.
+3.20 | Add 'getInstances' RPC and 'InstanceSet' object.
 
 [discuss-list]: https://groups.google.com/a/dartlang.org/forum/#!forum/observatory-discuss
diff --git a/runtime/vm/service/service_dev.md b/runtime/vm/service/service_dev.md
index 5daf941..14293eb 100644
--- a/runtime/vm/service/service_dev.md
+++ b/runtime/vm/service/service_dev.md
@@ -1,8 +1,8 @@
-# Dart VM Service Protocol 3.18-dev
+# Dart VM Service Protocol 3.21-dev
 
 > Please post feedback to the [observatory-discuss group][discuss-list]
 
-This document describes of _version 3.18-dev_ of the Dart VM Service Protocol. This
+This document describes of _version 3.21-dev_ of the Dart VM Service Protocol. This
 protocol is used to communicate with a running Dart Virtual Machine.
 
 To use the Service Protocol, start the VM with the *--observe* flag.
@@ -27,9 +27,12 @@
   - [addBreakpoint](#addbreakpoint)
   - [addBreakpointWithScriptUri](#addbreakpointwithscripturi)
   - [addBreakpointAtEntry](#addbreakpointatentry)
+  - [clearVMTimeline](#clearvmtimeline)
   - [evaluate](#evaluate)
   - [evaluateInFrame](#evaluateinframe)
+  - [getAllocationProfile](#getallocationprofile)
   - [getFlagList](#getflaglist)
+  - [getInstances](#getinstances)
   - [getIsolate](#getisolate)
   - [getMemoryUsage](#getmemoryusage)
   - [getScripts](#getscripts)
@@ -38,6 +41,8 @@
   - [getStack](#getstack)
   - [getVersion](#getversion)
   - [getVM](#getvm)
+  - [getVMTimeline](#getvmtimeline)
+  - [getVMTimelineFlags](#getvmtimelineflags)
   - [invoke](#invoke)
   - [pause](#pause)
   - [kill](#kill)
@@ -49,13 +54,16 @@
   - [setLibraryDebuggable](#setlibrarydebuggable)
   - [setName](#setname)
   - [setVMName](#setvmname)
+  - [setVMTimelineFlags](#setvmtimelineflags)
   - [streamCancel](#streamcancel)
   - [streamListen](#streamlisten)
 - [Public Types](#public-types)
+  - [AllocationProfile](#allocationprofile)
   - [BoundField](#boundfield)
   - [BoundVariable](#boundvariable)
   - [Breakpoint](#breakpoint)
   - [Class](#class)
+  - [ClassHeapStats](#classheapstats)
   - [ClassList](#classlist)
   - [Code](#code)
   - [CodeKind](#codekind)
@@ -72,6 +80,7 @@
   - [Frame](#frame)
   - [Function](#function)
   - [Instance](#instance)
+  - [InstanceSet](#instanceset)
   - [Isolate](#isolate)
   - [Library](#library)
   - [LibraryDependency](#librarydependency)
@@ -95,6 +104,9 @@
   - [Stack](#stack)
   - [StepOption](#stepoption)
   - [Success](#success)
+  - [Timeline](#timeline)
+  - [TimelineEvent](#timelineevent)
+  - [TimelineFlags](#timelineflags)
   - [TypeArguments](#typearguments)
   - [UresolvedSourceLocation](#unresolvedsourcelocation)
   - [Version](#version)
@@ -200,8 +212,7 @@
 111 | Service already registered | Service with such name has already been registered by this client
 112 | Service disappeared | Failed to fulfill service request, likely service handler is no longer available
 113 | Expression compilation error | Request to compile expression failed
-
-
+114 | Invalid timeline request | The timeline related request could not be completed due to the current configuration
 
 ## Events
 
@@ -476,6 +487,17 @@
 
 Note that breakpoints are added and removed on a per-isolate basis.
 
+
+### clearVMTimeline
+
+``` 
+Success clearVMTimeline()
+```
+
+Clears all VM timeline events.
+
+See [Success](#success).
+
 ### invoke
 
 ```
@@ -589,6 +611,24 @@
 If the expression is evaluated successfully, an [@Instance](#instance)
 reference will be returned.
 
+### getAllocationProfile
+
+```
+AllocationProfile getAllocationProfile(string isolateId,
+                                       boolean reset [optional],
+                                       boolean gc [optional])
+```
+
+The _getAllocationProfile_ RPC is used to retrieve allocation information for a
+given isolate.
+
+If _reset_ is provided and is set to true, the allocation accumulators will be reset
+before collecting allocation information.
+
+If _gc_ is provided and is set to true, a garbage collection will be attempted
+before collecting allocation information. There is no guarantee that a garbage
+collection will be actually be performed.
+
 ### getFlagList
 
 ```
@@ -600,6 +640,21 @@
 
 See [FlagList](#flaglist).
 
+### getInstances
+
+```
+InstanceSet getInstances(string objectId,
+                         int limit)
+```
+
+The _getInstances_ RPC is used to retrieve a set of instances which are of a specific type.
+
+_objectId_ is the ID of the `Class` to retrieve instances for. _objectId_ must be the ID of a `Class`, otherwise an error is returned.
+
+_limit_ is the maximum number of instances to be returned.
+
+See [InstanceSet](#instanceset).
+
 ### getIsolate
 
 ```
@@ -747,6 +802,43 @@
 
 See [VM](#vm).
 
+
+### getVMTimeline
+
+```
+Timeline getVMTimeline(int timeOriginMicros,
+                       int timeExtentMicros)
+```
+
+The _getVMTimeline_ RPC is used to retrieve an object which contains VM timeline
+events.
+
+The _timeOriginMicros_ parameter is the beginning of the time range used to filter
+timeline events. It uses the same monotonic clock as dart:developer's `Timeline.now`
+and the VM embedding API's `Dart_TimelineGetMicros`.
+
+The _timeExtentMicros_ parameter specifies how large the time range used to filter
+timeline events should be.
+
+For example, given _timeOriginMicros_ and _timeExtentMicros_, only timeline events
+from the following time range will be returned: `(timeOriginMicros, timeOriginMicros + timeExtentMicros)`.
+
+If _getVMTimeline_ is invoked while the current recorder is one of Fuchsia or
+Systrace, the _114_ error code, invalid timeline request, will be returned as
+timeline events are handled by the OS in these modes.
+
+### getVMTimelineFlags
+
+```
+TimelineFlags getVMTimelineFlags()
+```
+
+The _getVMTimelineFlags_ RPC returns information about the current VM timeline configuration.
+
+To change which timeline streams are currently enabled, see [setVMTimelineFlags](#setvmtimelineflags).
+
+See [TimelineFlags](#timelineflags).
+
 ### pause
 
 ```
@@ -773,7 +865,6 @@
 
 ### reloadSources
 
-
 ```
 ReloadReport reloadSources(string isolateId,
                            bool force [optional],
@@ -913,6 +1004,22 @@
 
 See [Success](#success).
 
+### setVMTimelineFlags
+
+```
+Success setVMTimelineFlags(string[] recordedStreams)
+```
+
+The _setVMTimelineFlags_ RPC is used to set which timeline streams are enabled.
+
+The _recordedStreams_ parameter is the list of all timeline streams which are
+to be enabled. Streams not explicitly specified will be disabled. Invalid stream
+names are ignored.
+
+To get the list of currently enabled timeline streams, see [getVMTimelineFlags](#getvmtimelineflags).
+
+See [Success](#success).
+
 ### streamCancel
 
 ```
@@ -1047,6 +1154,28 @@
 This means that _PermittedValues_ is a _string_ with two potential values,
 _Value1_ and _Value2_.
 
+### AllocationProfile
+
+```
+class AllocationProfile extends Response {
+  // Allocation information for all class types.
+  ClassHeapStats[] members;
+
+  // Information about memory usage for the isolate.
+  MemoryUsage memoryUsage;
+
+  // The timestamp of the last accumulator reset.
+  //
+  // If the accumulators have not been reset, this field is not present.
+  int dateLastAccumulatorReset [optional];
+
+  // The timestamp of the last manually triggered GC.
+  //
+  // If a GC has not been triggered manually, this field is not present.
+  int dateLastServiceGC [optional];
+}
+```
+
 ### BoundField
 
 ```
@@ -1186,6 +1315,29 @@
 
 A _Class_ provides information about a Dart language class.
 
+### ClassHeapStats
+
+```
+class ClassHeapStats extends Response {
+  // The class for which this memory information is associated.
+  @Class class;
+
+  // The number of bytes allocated for instances of class since the
+  // accumulator was last reset.
+  int accumulatedSize;
+
+  // The number of bytes currently allocated for instances of class.
+  int bytesCurrent;
+
+  // The number of instances of class which have been allocated since
+  // the accumulator was last reset.
+  int instancesAccumulated;
+
+  // The number of instances of class which are currently alive.
+  int instancesCurrent;
+}
+```
+
 ### ClassList
 
 ```
@@ -2158,6 +2310,20 @@
 
 An _Isolate_ object provides information about one isolate in the VM.
 
+### InstanceSet
+
+```
+class InstanceSet extends Response {
+  // The number of instances of the requested type currently allocated.
+  int totalCount;
+
+  // An array of instances of the requested type.
+  @Instance[] instances;
+}
+```
+
+See [getInstances](#getinstances).
+
 ### Library
 
 ```
@@ -2701,6 +2867,21 @@
 
 The _Success_ type is used to indicate that an operation completed successfully.
 
+### Timeline
+
+```
+class Timeline extends Response {
+  // A list of timeline events.
+  TimelineEvent[] traceEvents;
+
+  // The start of the period of time in which traceEvents were collected.
+  int timeOriginMicros;
+
+  // The duration of time covered by the timeline.
+  int timeExtentMicros;
+}
+```
+
 ### TimelineEvent
 
 ```
@@ -2710,6 +2891,23 @@
 
 An _TimelineEvent_ is an arbitrary map that contains a [Trace Event Format](https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview) event.
 
+### TimelineFlags
+
+```
+class TimelineFlags extends Response {
+  // The name of the recorder currently in use. Recorder types include, but are
+  // not limited to: Callback, Endless, Fuchsia, Ring, Startup, and Systrace.
+  // Set to "null" if no recorder is currently set. 
+  string recorderName;
+
+  // The list of all available timeline streams.
+  string[] availableStreams;
+
+  // The list of timeline streams that are currently enabled.
+  string[] recordedStreams;
+}
+```
+
 ### TypeArguments
 
 ```
@@ -2835,7 +3033,7 @@
 
 version | comments
 ------- | --------
-1.0 | initial revision
+1.0 | Initial revision
 2.0 | Describe protocol version 2.0.
 3.0 | Describe protocol version 3.0.  Added UnresolvedSourceLocation.  Added Sentinel return to getIsolate.  Add AddedBreakpointWithScriptUri.  Removed Isolate.entry. The type of VM.pid was changed from string to int.  Added VMUpdate events.  Add offset and count parameters to getObject() and offset and count fields to Instance. Added ServiceExtensionAdded event.
 3.1 | Add the getSourceReport RPC.  The getObject RPC now accepts offset and count for string objects.  String objects now contain length, offset, and count properties.
@@ -2855,5 +3053,8 @@
 3.15 | Added `disableBreakpoints` parameter to `invoke`, `evaluate`, and `evaluateInFrame`.
 3.16 | Add 'getMemoryUsage' RPC and 'MemoryUsage' object.
 3.17 | Add 'Logging' event kind and the LogRecord class.
+3.18 | Add 'getAllocationProfile' RPC and 'AllocationProfile' and 'ClassHeapStats' objects.
+3.19 | Add 'clearVMTimeline', 'getVMTimeline', 'getVMTimelineFlags', 'setVMTimelineFlags', 'Timeline', and 'TimelineFlags'.
+3.20 | Add 'getInstances' RPC and 'InstanceSet' object.
 
 [discuss-list]: https://groups.google.com/a/dartlang.org/forum/#!forum/observatory-discuss
diff --git a/runtime/vm/service_isolate.cc b/runtime/vm/service_isolate.cc
index 8a223ee..d76e551 100644
--- a/runtime/vm/service_isolate.cc
+++ b/runtime/vm/service_isolate.cc
@@ -483,7 +483,7 @@
     ServiceIsolate::InitializingFailed();
     return;
   }
-  bool task_started = Dart::thread_pool()->Run(new RunServiceTask());
+  bool task_started = Dart::thread_pool()->Run<RunServiceTask>();
   ASSERT(task_started);
 }
 
diff --git a/runtime/vm/snapshot.cc b/runtime/vm/snapshot.cc
index 3824991..65258de 100644
--- a/runtime/vm/snapshot.cc
+++ b/runtime/vm/snapshot.cc
@@ -1134,7 +1134,12 @@
 
 #define SNAPSHOT_WRITE(clazz) case kFfi##clazz##Cid:
 
-    CLASS_LIST_FFI(SNAPSHOT_WRITE) { UNREACHABLE(); }
+      CLASS_LIST_FFI(SNAPSHOT_WRITE) {
+        SetWriteException(Exceptions::kArgument,
+                          "Native objects (from dart:ffi) such as Pointers and "
+                          "Structs cannot be passed between isolates.");
+        UNREACHABLE();
+      }
 #undef SNAPSHOT_WRITE
     default:
       break;
diff --git a/runtime/vm/snapshot.h b/runtime/vm/snapshot.h
index 0939635e..5f6c929 100644
--- a/runtime/vm/snapshot.h
+++ b/runtime/vm/snapshot.h
@@ -5,6 +5,9 @@
 #ifndef RUNTIME_VM_SNAPSHOT_H_
 #define RUNTIME_VM_SNAPSHOT_H_
 
+#include <memory>
+#include <utility>
+
 #include "platform/assert.h"
 #include "vm/allocation.h"
 #include "vm/bitfield.h"
@@ -700,6 +703,7 @@
   friend class RawClosureData;
   friend class RawCode;
   friend class RawContextScope;
+  friend class RawDynamicLibrary;
   friend class RawExceptionHandlers;
   friend class RawField;
   friend class RawFunction;
@@ -711,6 +715,7 @@
   friend class RawLocalVarDescriptors;
   friend class RawMirrorReference;
   friend class RawObjectPool;
+  friend class RawPointer;
   friend class RawReceivePort;
   friend class RawRegExp;
   friend class RawScript;
@@ -718,10 +723,10 @@
   friend class RawSubtypeTestCache;
   friend class RawTransferableTypedData;
   friend class RawType;
-  friend class RawTypedDataView;
-  friend class RawTypeRef;
   friend class RawTypeArguments;
   friend class RawTypeParameter;
+  friend class RawTypeRef;
+  friend class RawTypedDataView;
   friend class RawUserTag;
   friend class SnapshotWriterVisitor;
   friend class WriteInlinedObjectVisitor;
diff --git a/runtime/vm/source_report.cc b/runtime/vm/source_report.cc
index 019cb7c..febc594 100644
--- a/runtime/vm/source_report.cc
+++ b/runtime/vm/source_report.cc
@@ -105,7 +105,11 @@
       func.IsRedirectingFactory() || func.is_no_such_method_forwarder()) {
     return true;
   }
-  if (func.IsNonImplicitClosureFunction() &&
+  // Note that context_scope() remains null for closures declared in bytecode,
+  // because the same information is retrieved from the parent's local variable
+  // descriptors.
+  // See IsLocalFunction() case in BytecodeReader::ComputeLocalVarDescriptors.
+  if (!func.is_declared_in_bytecode() && func.IsNonImplicitClosureFunction() &&
       (func.context_scope() == ContextScope::null())) {
     // TODO(iposva): This can arise if we attempt to compile an inner function
     // before we have compiled its enclosing function or if the enclosing
@@ -479,31 +483,53 @@
   // We skip compiled async functions.  Once an async function has
   // been compiled, there is another function with the same range which
   // actually contains the user code.
-  if (func.IsAsyncFunction() || func.IsAsyncGenerator() ||
-      func.IsSyncGenerator()) {
-    return;
+  if (!func.IsAsyncFunction() && !func.IsAsyncGenerator() &&
+      !func.IsSyncGenerator()) {
+    JSONObject range(jsarr);
+    range.AddProperty("scriptIndex", GetScriptIndex(script));
+    range.AddProperty("startPos", begin_pos);
+    range.AddProperty("endPos", end_pos);
+    range.AddProperty("compiled", true);  // bytecode or code.
+
+    if (IsReportRequested(kCallSites)) {
+      PrintCallSitesData(&range, func, code);
+    }
+    if (IsReportRequested(kCoverage)) {
+      PrintCoverageData(&range, func, code);
+    }
+    if (IsReportRequested(kPossibleBreakpoints)) {
+      PrintPossibleBreakpointsData(&range, func, code);
+    }
+    if (IsReportRequested(kProfile)) {
+      ProfileFunction* profile_function = profile_.FindFunction(func);
+      if ((profile_function != NULL) &&
+          (profile_function->NumSourcePositions() > 0)) {
+        PrintProfileData(&range, profile_function);
+      }
+    }
   }
 
-  JSONObject range(jsarr);
-  range.AddProperty("scriptIndex", GetScriptIndex(script));
-  range.AddProperty("startPos", begin_pos);
-  range.AddProperty("endPos", end_pos);
-  range.AddProperty("compiled", true);  // bytecode or code.
-
-  if (IsReportRequested(kCallSites)) {
-    PrintCallSitesData(&range, func, code);
-  }
-  if (IsReportRequested(kCoverage)) {
-    PrintCoverageData(&range, func, code);
-  }
-  if (IsReportRequested(kPossibleBreakpoints)) {
-    PrintPossibleBreakpointsData(&range, func, code);
-  }
-  if (IsReportRequested(kProfile)) {
-    ProfileFunction* profile_function = profile_.FindFunction(func);
-    if ((profile_function != NULL) &&
-        (profile_function->NumSourcePositions() > 0)) {
-      PrintProfileData(&range, profile_function);
+  // Visit the closures declared in a bytecode function by traversing its object
+  // pool, because they do not appear in the object store's list of closures.
+  // Since local functions share the object pool, only traverse the pool once,
+  // i.e. when func is the outermost function.
+  if (!bytecode.IsNull() && !func.IsLocalFunction()) {
+    const ObjectPool& pool = ObjectPool::Handle(zone(), bytecode.object_pool());
+    Object& object = Object::Handle(zone());
+    Function& closure = Function::Handle(zone());
+    for (intptr_t i = 0; i < pool.Length(); i++) {
+      ObjectPool::EntryType entry_type = pool.TypeAt(i);
+      if (entry_type != ObjectPool::EntryType::kTaggedObject) {
+        continue;
+      }
+      object = pool.ObjectAt(i);
+      if (object.IsFunction()) {
+        closure ^= object.raw();
+        if ((closure.kind() == RawFunction::kClosureFunction) &&
+            (closure.IsLocalFunction())) {
+          VisitFunction(jsarr, closure);
+        }
+      }
     }
   }
 }
@@ -566,6 +592,8 @@
 }
 
 void SourceReport::VisitClosures(JSONArray* jsarr) {
+  // Note that closures declared in bytecode are not visited here, but in
+  // VisitFunction while traversing the object pool of their owner functions.
   const GrowableObjectArray& closures = GrowableObjectArray::Handle(
       thread()->isolate()->object_store()->closure_functions());
 
diff --git a/runtime/vm/stub_code_list.h b/runtime/vm/stub_code_list.h
index 248c455..f0eff2c 100644
--- a/runtime/vm/stub_code_list.h
+++ b/runtime/vm/stub_code_list.h
@@ -46,6 +46,7 @@
   V(UnoptimizedIdenticalWithNumberCheck)                                       \
   V(OptimizedIdenticalWithNumberCheck)                                         \
   V(ICCallBreakpoint)                                                          \
+  V(UnoptStaticCallBreakpoint)                                                 \
   V(RuntimeCallBreakpoint)                                                     \
   V(OneArgCheckInlineCache)                                                    \
   V(TwoArgsCheckInlineCache)                                                   \
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index 5454049..fb281f7 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -18,8 +18,6 @@
 // One-character symbols are added implicitly.
 #define PREDEFINED_SYMBOLS_LIST(V)                                             \
   V(AbstractClassInstantiationError, "AbstractClassInstantiationError")        \
-  V(AddError, "addError")                                                      \
-  V(AddStream, "addStream")                                                    \
   V(AllocateInvocationMirror, "_allocateInvocationMirror")                     \
   V(AllocateInvocationMirrorForClosure, "_allocateInvocationMirrorForClosure") \
   V(AnonymousClosure, "<anonymous closure>")                                   \
@@ -29,26 +27,12 @@
   V(ArgumentError, "ArgumentError")                                            \
   V(AssertionError, "_AssertionError")                                         \
   V(AssignIndexToken, "[]=")                                                   \
-  V(Async, "async")                                                            \
-  V(AsyncAwaitHelper, "_awaitHelper")                                          \
-  V(AsyncCatchErrorCallback, ":async_op_catch_error")                          \
-  V(AsyncCatchHelper, "_asyncCatchHelper")                                     \
   V(AsyncCompleter, ":async_completer")                                        \
-  V(AsyncErrorWrapperHelper, "_asyncErrorWrapperHelper")                       \
   V(AsyncOperation, ":async_op")                                               \
-  V(AsyncOperationErrorParam, ":async_error_param")                            \
-  V(AsyncOperationParam, ":async_result")                                      \
-  V(AsyncOperationStackTraceParam, ":async_stack_trace_param")                 \
-  V(AsyncSavedTryCtxVarPrefix, ":async_saved_try_ctx_var_")                    \
-  V(AsyncStackTraceHelper, "_asyncStackTraceHelper")                           \
   V(AsyncStackTraceVar, ":async_stack_trace")                                  \
   V(AsyncStarMoveNextHelper, "_asyncStarMoveNextHelper")                       \
-  V(AsyncThenCallback, ":async_op_then")                                       \
-  V(AsyncThenWrapperHelper, "_asyncThenWrapperHelper")                         \
-  V(Await, "await")                                                            \
   V(AwaitContextVar, ":await_ctx_var")                                         \
   V(AwaitJumpVar, ":await_jump_var")                                           \
-  V(AwaitTempVarPrefix, ":await_temp_var_")                                    \
   V(Bool, "bool")                                                              \
   V(BooleanExpression, "boolean expression")                                   \
   V(BoundsCheckForPartialInstantiation, "_boundsCheckForPartialInstantiation") \
@@ -62,18 +46,13 @@
   V(Class, "Class")                                                            \
   V(ClassID, "ClassID")                                                        \
   V(ClearAsyncThreadStackTrace, "_clearAsyncThreadStackTrace")                 \
-  V(Close, "close")                                                            \
   V(ClosureData, "ClosureData")                                                \
   V(ClosureParameter, ":closure")                                              \
   V(Code, "Code")                                                              \
   V(CodeSourceMap, "CodeSourceMap")                                            \
-  V(ColonController, ":controller")                                            \
   V(ColonMatcher, ":matcher")                                                  \
-  V(ColonStream, ":stream")                                                    \
   V(CommaSpace, ", ")                                                          \
   V(Completer, "Completer")                                                    \
-  V(CompleterComplete, "complete")                                             \
-  V(CompleterCompleteError, "completeError")                                   \
   V(CompleterFuture, "future")                                                 \
   V(CompleterGetFuture, "get:future")                                          \
   V(CompleterSyncConstructor, "Completer.sync")                                \
@@ -94,7 +73,6 @@
   V(DartFfiLibName, "ffi")                                                     \
   V(DartIOLibName, "dart.io")                                                  \
   V(DartInternal, "dart:_internal")                                            \
-  V(DartInternalPackage, "package:dart_internal/")                             \
   V(DartIsVM, "dart.isVM")                                                     \
   V(DartIsolate, "dart:isolate")                                               \
   V(DartLibrary, "dart.library.")                                              \
@@ -107,12 +85,12 @@
   V(DartSchemePrivate, "dart:_")                                               \
   V(DartTypedData, "dart:typed_data")                                          \
   V(DartVMProduct, "dart.vm.product")                                          \
+  V(DartVMServiceIO, "dart:vmservice_io")                                      \
   V(DartVMService, "dart:_vmservice")                                          \
   V(DebugClassName, "#DebugClass")                                             \
   V(DebugProcedureName, ":Eval")                                               \
   V(Default, "Default")                                                        \
   V(DefaultLabel, ":L")                                                        \
-  V(DeoptInfo, "DeoptInfo")                                                    \
   V(DotCreate, "._create")                                                     \
   V(DotRange, ".range")                                                        \
   V(DotValue, ".value")                                                        \
@@ -159,7 +137,6 @@
   V(Float64List, "Float64List")                                                \
   V(Float64x2, "Float64x2")                                                    \
   V(Float64x2List, "Float64x2List")                                            \
-  V(ForInIter, ":for-in-iter")                                                 \
   V(FormatException, "FormatException")                                        \
   V(ForwardingCorpse, "ForwardingCorpse")                                      \
   V(FreeListElement, "FreeListElement")                                        \
@@ -181,12 +158,10 @@
   V(GrowRegExpStack, "_growRegExpStack")                                       \
   V(HandleExposedException, "_handleExposedException")                         \
   V(HaveSameRuntimeType, "_haveSameRuntimeType")                               \
-  V(Hide, "hide")                                                              \
   V(ICData, "ICData")                                                          \
   V(Identical, "identical")                                                    \
   V(ImmutableMap, "_ImmutableMap")                                             \
   V(ImmutableMapConstructor, "_ImmutableMap._create")                          \
-  V(ImplicitClosure, "<implicit closure>")                                     \
   V(InTypeCast, " in type cast")                                               \
   V(Index, "index")                                                            \
   V(IndexToken, "[]")                                                          \
@@ -216,7 +191,6 @@
   V(LibraryPrefix, "LibraryPrefix")                                            \
   V(List, "List")                                                              \
   V(ListFactory, "List.")                                                      \
-  V(ListLiteralElement, "list literal element")                                \
   V(ListLiteralFactory, "List._fromLiteral")                                   \
   V(LoadLibrary, "loadLibrary")                                                \
   V(LocalVarDescriptors, "LocalVarDescriptors")                                \
@@ -236,14 +210,13 @@
   V(Number, "num")                                                             \
   V(Object, "Object")                                                          \
   V(ObjectPool, "ObjectPool")                                                  \
-  V(Of, "of")                                                                  \
-  V(On, "on")                                                                  \
   V(OneByteString, "_OneByteString")                                           \
   V(OptimizedOut, "<optimized out>")                                           \
   V(OriginalParam, ":original:")                                               \
   V(Other, "other")                                                            \
   V(OutOfMemoryError, "OutOfMemoryError")                                      \
   V(PackageScheme, "package:")                                                 \
+  V(ParameterTypeCheck, "ParameterTypeCheck")                                  \
   V(Patch, "patch")                                                            \
   V(PatchClass, "PatchClass")                                                  \
   V(PcDescriptors, "PcDescriptors")                                            \
@@ -255,14 +228,11 @@
   V(RedirectionData, "RedirectionData")                                        \
   V(RegExp, "RegExp")                                                          \
   V(RightShiftOperator, ">>")                                                  \
-  V(SavedExceptionVar, ":saved_exception_var")                                 \
-  V(SavedStackTraceVar, ":saved_stack_trace_var")                              \
   V(SavedTryContextVar, ":saved_try_context_var")                              \
   V(Script, "Script")                                                          \
   V(Set, "set")                                                                \
   V(SetAsyncThreadStackTrace, "_setAsyncThreadStackTrace")                     \
   V(SetterPrefix, "set:")                                                      \
-  V(Show, "show")                                                              \
   V(SignatureData, "SignatureData")                                            \
   V(SingleTargetCache, "SingleTargetCache")                                    \
   V(SpaceExtendsSpace, " extends ")                                            \
@@ -281,8 +251,6 @@
   V(SwitchExpr, ":switch_expr")                                                \
   V(Symbol, "Symbol")                                                          \
   V(SymbolCtor, "Symbol.")                                                     \
-  V(Sync, "sync")                                                              \
-  V(TempParam, ":temp_param")                                                  \
   V(ThrowNew, "_throwNew")                                                     \
   V(ThrowNewInvocation, "_throwNewInvocation")                                 \
   V(TopLevel, "::")                                                            \
@@ -290,7 +258,6 @@
   V(TransferableTypedData, "TransferableTypedData")                            \
   V(TryFinallyReturnValue, ":try_finally_return_value")                        \
   V(TwoByteString, "_TwoByteString")                                           \
-  V(TwoNewlines, "\n\n")                                                       \
   V(TwoSpaces, "  ")                                                           \
   V(Type, "Type")                                                              \
   V(TypeArguments, "TypeArguments")                                            \
@@ -330,8 +297,6 @@
   V(_DeletedEnumPrefix, "Deleted enum value from ")                            \
   V(_DeletedEnumSentinel, "_deleted_enum_sentinel")                            \
   V(_Double, "_Double")                                                        \
-  V(_EnumHelper, "_EnumHelper")                                                \
-  V(_EnumNames, "_enum_names")                                                 \
   V(_ExternalFloat32Array, "_ExternalFloat32Array")                            \
   V(_ExternalFloat32x4Array, "_ExternalFloat32x4Array")                        \
   V(_ExternalFloat64Array, "_ExternalFloat64Array")                            \
@@ -453,7 +418,6 @@
   V(_stackTrace, "_stackTrace")                                                \
   V(_state, "_state")                                                          \
   V(_wordCharacterMap, "_wordCharacterMap")                                    \
-  V(_yieldEachIterable, "_yieldEachIterable")                                  \
   V(add, "add")                                                                \
   V(capture_length, ":capture_length")                                         \
   V(capture_start_index, ":capture_start_index")                               \
@@ -486,8 +450,7 @@
   V(vm_entry_point, "vm:entry-point")                                          \
   V(vm_exact_result_type, "vm:exact-result-type")                              \
   V(vm_non_nullable_result_type, "vm:non-nullable-result-type")                \
-  V(vm_trace_entrypoints, "vm:testing.unsafe.trace-entrypoints-fn")            \
-  V(word_character_map, ":word_character_map")
+  V(vm_trace_entrypoints, "vm:testing.unsafe.trace-entrypoints-fn")
 
 // Contains a list of frequently used strings in a canonicalized form. This
 // list is kept in the vm_isolate in order to share the copy across isolates
diff --git a/runtime/vm/thread.h b/runtime/vm/thread.h
index 44bb9e6..b13b149 100644
--- a/runtime/vm/thread.h
+++ b/runtime/vm/thread.h
@@ -112,8 +112,6 @@
   V(RawCode*, stack_overflow_shared_with_fpu_regs_stub_,                       \
     StubCode::StackOverflowSharedWithFPURegs().raw(), NULL)                    \
   V(RawCode*, monomorphic_miss_stub_, StubCode::MonomorphicMiss().raw(), NULL) \
-  V(RawCode*, ic_lookup_through_code_stub_,                                    \
-    StubCode::ICCallThroughCode().raw(), NULL)                                 \
   V(RawCode*, optimize_stub_, StubCode::OptimizeFunction().raw(), NULL)        \
   V(RawCode*, deoptimize_stub_, StubCode::Deoptimize().raw(), NULL)            \
   V(RawCode*, lazy_deopt_from_return_stub_,                                    \
@@ -738,6 +736,7 @@
   }
 
   void EnterSafepoint() {
+    ASSERT(no_safepoint_scope_depth() == 0);
     // First try a fast update of the thread state to indicate it is at a
     // safepoint.
     if (!TryEnterSafepoint()) {
@@ -767,6 +766,7 @@
   }
 
   void CheckForSafepoint() {
+    ASSERT(no_safepoint_scope_depth() == 0);
     if (IsSafepointRequested()) {
       BlockForSafepoint();
     }
diff --git a/runtime/vm/thread_barrier_test.cc b/runtime/vm/thread_barrier_test.cc
index 42bc53b..c937eeb2 100644
--- a/runtime/vm/thread_barrier_test.cc
+++ b/runtime/vm/thread_barrier_test.cc
@@ -45,7 +45,7 @@
   {
     ThreadBarrier barrier(kNumTasks + 1, monitor, monitor_done);
     for (intptr_t i = 0; i < kNumTasks; ++i) {
-      Dart::thread_pool()->Run(new FuzzTask(kNumRounds, &barrier, i + 1));
+      Dart::thread_pool()->Run<FuzzTask>(kNumRounds, &barrier, i + 1);
     }
     for (intptr_t i = 0; i < kNumRounds; ++i) {
       barrier.Sync();
diff --git a/runtime/vm/thread_pool.cc b/runtime/vm/thread_pool.cc
index 3a085ae..6f43097 100644
--- a/runtime/vm/thread_pool.cc
+++ b/runtime/vm/thread_pool.cc
@@ -30,7 +30,7 @@
   Shutdown();
 }
 
-bool ThreadPool::Run(Task* task) {
+bool ThreadPool::RunImpl(std::unique_ptr<Task> task) {
   Worker* worker = NULL;
   bool new_worker = false;
   {
@@ -63,7 +63,7 @@
 
   // Release ThreadPool::mutex_ before calling Worker functions.
   ASSERT(worker != NULL);
-  worker->SetTask(task);
+  worker->SetTask(std::move(task));
   if (new_worker) {
     // Call StartThread after we've assigned the first task.
     worker->StartThread();
@@ -314,7 +314,7 @@
 
 ThreadPool::Worker::Worker(ThreadPool* pool)
     : pool_(pool),
-      task_(NULL),
+      task_(nullptr),
       id_(OSThread::kInvalidThreadId),
       done_(false),
       owned_(false),
@@ -332,7 +332,7 @@
   // Must call SetTask before StartThread.
   {  // NOLINT
     MonitorLocker ml(&monitor_);
-    ASSERT(task_ != NULL);
+    ASSERT(task_ != nullptr);
   }
 #endif
   int result = OSThread::Start("Dart ThreadPool Worker", &Worker::Main,
@@ -342,10 +342,10 @@
   }
 }
 
-void ThreadPool::Worker::SetTask(Task* task) {
+void ThreadPool::Worker::SetTask(std::unique_ptr<Task> task) {
   MonitorLocker ml(&monitor_);
-  ASSERT(task_ == NULL);
-  task_ = task;
+  ASSERT(task_ == nullptr);
+  task_ = std::move(task);
   ml.Notify();
 }
 
@@ -372,18 +372,17 @@
   MonitorLocker ml(&monitor_);
   int64_t idle_start;
   while (true) {
-    ASSERT(task_ != NULL);
-    Task* task = task_;
-    task_ = NULL;
+    ASSERT(task_ != nullptr);
+    std::unique_ptr<Task> task = std::move(task_);
 
     // Release monitor while handling the task.
     ml.Exit();
     task->Run();
     ASSERT(Isolate::Current() == NULL);
-    delete task;
+    task.reset();
     ml.Enter();
 
-    ASSERT(task_ == NULL);
+    ASSERT(task_ == nullptr);
     if (IsDone()) {
       return false;
     }
@@ -392,7 +391,7 @@
     idle_start = OS::GetCurrentMonotonicMicros();
     while (true) {
       Monitor::WaitResult result = ml.WaitMicros(ComputeTimeout(idle_start));
-      if (task_ != NULL) {
+      if (task_ != nullptr) {
         // We've found a task.  Process it, regardless of whether the
         // worker is done_.
         break;
diff --git a/runtime/vm/thread_pool.h b/runtime/vm/thread_pool.h
index 6fbe0d7..5a1ff54 100644
--- a/runtime/vm/thread_pool.h
+++ b/runtime/vm/thread_pool.h
@@ -5,6 +5,9 @@
 #ifndef RUNTIME_VM_THREAD_POOL_H_
 #define RUNTIME_VM_THREAD_POOL_H_
 
+#include <memory>
+#include <utility>
+
 #include "vm/allocation.h"
 #include "vm/globals.h"
 #include "vm/os_thread.h"
@@ -35,7 +38,10 @@
   ~ThreadPool();
 
   // Runs a task on the thread pool.
-  bool Run(Task* task);
+  template <typename T, typename... Args>
+  bool Run(Args&&... args) {
+    return RunImpl(std::unique_ptr<Task>(new T(std::forward<Args>(args)...)));
+  }
 
   // Some simple stats.
   uint64_t workers_running() const { return count_running_; }
@@ -49,7 +55,7 @@
     explicit Worker(ThreadPool* pool);
 
     // Sets a task on the worker.
-    void SetTask(Task* task);
+    void SetTask(std::unique_ptr<Task> task);
 
     // Starts the thread for the worker.  This should only be called
     // after a task has been set by the initial call to SetTask().
@@ -76,7 +82,7 @@
     // Fields owned by Worker.
     Monitor monitor_;
     ThreadPool* pool_;
-    Task* task_;
+    std::unique_ptr<Task> task_;
     ThreadId id_;
     bool done_;
 
@@ -110,6 +116,7 @@
     DISALLOW_COPY_AND_ASSIGN(JoinList);
   };
 
+  bool RunImpl(std::unique_ptr<Task> task);
   void Shutdown();
 
   // Expensive.  Use only in assertions.
diff --git a/runtime/vm/thread_pool_test.cc b/runtime/vm/thread_pool_test.cc
index 1706a99..ab197f5 100644
--- a/runtime/vm/thread_pool_test.cc
+++ b/runtime/vm/thread_pool_test.cc
@@ -44,7 +44,7 @@
   ThreadPool thread_pool;
   Monitor sync;
   bool done = true;
-  thread_pool.Run(new TestTask(&sync, &done));
+  thread_pool.Run<TestTask>(&sync, &done);
   {
     MonitorLocker ml(&sync);
     done = false;
@@ -68,7 +68,7 @@
 
   for (int i = 0; i < kTaskCount; i++) {
     done[i] = true;
-    thread_pool.Run(new TestTask(&sync[i], &done[i]));
+    thread_pool.Run<TestTask>(&sync[i], &done[i]);
   }
   for (int i = 0; i < kTaskCount; i++) {
     MonitorLocker ml(&sync[i]);
@@ -124,7 +124,7 @@
 
   // Run a single task.
   for (int i = 0; i < kTaskCount; i++) {
-    thread_pool->Run(new SleepTask(&sync, &started_count, &slept_count, 2));
+    thread_pool->Run<SleepTask>(&sync, &started_count, &slept_count, 2);
   }
 
   {
@@ -162,7 +162,7 @@
   // Run a worker.
   Monitor sync;
   bool done = true;
-  thread_pool.Run(new TestTask(&sync, &done));
+  thread_pool.Run<TestTask>(&sync, &done);
   EXPECT_EQ(1U, thread_pool.workers_started());
   EXPECT_EQ(0U, thread_pool.workers_stopped());
   {
@@ -197,11 +197,10 @@
 
     // Spawn 0-2 children.
     if (todo_ > 0) {
-      pool_->Run(
-          new SpawnTask(pool_, sync_, todo_ - child_todo, total_, done_));
+      pool_->Run<SpawnTask>(pool_, sync_, todo_ - child_todo, total_, done_);
     }
     if (todo_ > 1) {
-      pool_->Run(new SpawnTask(pool_, sync_, child_todo, total_, done_));
+      pool_->Run<SpawnTask>(pool_, sync_, child_todo, total_, done_);
     }
 
     {
@@ -226,8 +225,8 @@
   Monitor sync;
   const int kTotalTasks = 500;
   int done = 0;
-  thread_pool.Run(
-      new SpawnTask(&thread_pool, &sync, kTotalTasks, kTotalTasks, &done));
+  thread_pool.Run<SpawnTask>(&thread_pool, &sync, kTotalTasks, kTotalTasks,
+                             &done);
   {
     MonitorLocker ml(&sync);
     while (done < kTotalTasks) {
diff --git a/runtime/vm/thread_test.cc b/runtime/vm/thread_test.cc
index 1030795..edbc632 100644
--- a/runtime/vm/thread_test.cc
+++ b/runtime/vm/thread_test.cc
@@ -180,8 +180,8 @@
   isolate->heap()->DisableGrowthControl();
   for (int i = 0; i < kTaskCount; i++) {
     done[i] = false;
-    Dart::thread_pool()->Run(
-        new TaskWithZoneAllocation(isolate, &sync[i], &done[i], i));
+    Dart::thread_pool()->Run<TaskWithZoneAllocation>(isolate, &sync[i],
+                                                     &done[i], i);
   }
   bool in_isolate = true;
   for (int i = 0; i < kTaskCount; i++) {
@@ -302,8 +302,8 @@
   EXPECT(isolate->heap()->GrowthControlState());
   isolate->heap()->DisableGrowthControl();
   for (intptr_t i = 0; i < kTaskCount; i++) {
-    Dart::thread_pool()->Run(new SimpleTaskWithZoneAllocation(
-        (i + 1), isolate, &threads[i], &sync, &monitor, &done_count, &wait));
+    Dart::thread_pool()->Run<SimpleTaskWithZoneAllocation>(
+        (i + 1), isolate, &threads[i], &sync, &monitor, &done_count, &wait);
   }
   // Wait until all spawned tasks finish their memory operations.
   {
@@ -497,8 +497,8 @@
   }
 
   for (int i = 0; i < ICDataTestTask::kTaskCount; i++) {
-    Dart::thread_pool()->Run(
-        new ICDataTestTask(isolate, ic_datas, &monitor, &exited, &done));
+    Dart::thread_pool()->Run<ICDataTestTask>(isolate, ic_datas, &monitor,
+                                             &exited, &done);
   }
 
   for (int i = 0; i < 0x10000; i++) {
@@ -630,8 +630,8 @@
   intptr_t total_done = 0;
   intptr_t exited = 0;
   for (int i = 0; i < SafepointTestTask::kTaskCount; i++) {
-    Dart::thread_pool()->Run(new SafepointTestTask(
-        isolate, &monitor, &expected_count, &total_done, &exited));
+    Dart::thread_pool()->Run<SafepointTestTask>(
+        isolate, &monitor, &expected_count, &total_done, &exited);
   }
 // Run Dart code on the main thread long enough to allow all helpers
 // to get their verification done and exit. Use a specific UserTag
@@ -681,8 +681,8 @@
   intptr_t total_done = 0;
   intptr_t exited = 0;
   for (int i = 0; i < SafepointTestTask::kTaskCount; i++) {
-    Dart::thread_pool()->Run(new SafepointTestTask(
-        isolate, &monitor, &expected_count, &total_done, &exited));
+    Dart::thread_pool()->Run<SafepointTestTask>(
+        isolate, &monitor, &expected_count, &total_done, &exited);
   }
   String& label = String::Handle(String::New("foo"));
   UserTag& tag = UserTag::Handle(UserTag::New(label));
@@ -802,8 +802,8 @@
   intptr_t total_done = 0;
   intptr_t exited = 0;
   for (int i = 0; i < SafepointTestTask::kTaskCount; i++) {
-    Dart::thread_pool()->Run(new SafepointTestTask(
-        isolate, &monitor, &expected_count, &total_done, &exited));
+    Dart::thread_pool()->Run<SafepointTestTask>(
+        isolate, &monitor, &expected_count, &total_done, &exited);
   }
   bool all_helpers = false;
   do {
@@ -833,8 +833,8 @@
   intptr_t total_done = 0;
   intptr_t exited = 0;
   for (int i = 0; i < SafepointTestTask::kTaskCount; i++) {
-    Dart::thread_pool()->Run(new SafepointTestTask(
-        isolate, &monitor, &expected_count, &total_done, &exited));
+    Dart::thread_pool()->Run<SafepointTestTask>(
+        isolate, &monitor, &expected_count, &total_done, &exited);
   }
   bool all_helpers = false;
   do {
@@ -899,7 +899,7 @@
   Monitor done_monitor;
   bool done = false;
   Isolate* isolate = thread->isolate();
-  Dart::thread_pool()->Run(new AllocAndGCTask(isolate, &done_monitor, &done));
+  Dart::thread_pool()->Run<AllocAndGCTask>(isolate, &done_monitor, &done);
   {
     while (true) {
       TransitionVMToBlocked transition(thread);
@@ -951,8 +951,8 @@
   Isolate* isolate = thread->isolate();
   for (int i = 0; i < NUMBER_TEST_THREADS; i++) {
     done[i] = false;
-    Dart::thread_pool()->Run(
-        new AllocateGlobsOfMemoryTask(isolate, &done_monitor[i], &done[i]));
+    Dart::thread_pool()->Run<AllocateGlobsOfMemoryTask>(
+        isolate, &done_monitor[i], &done[i]);
   }
 
   for (int i = 0; i < NUMBER_TEST_THREADS; i++) {
diff --git a/runtime/vm/timeline.cc b/runtime/vm/timeline.cc
index 329f488..8b9a644 100644
--- a/runtime/vm/timeline.cc
+++ b/runtime/vm/timeline.cc
@@ -17,6 +17,7 @@
 #include "vm/lockers.h"
 #include "vm/log.h"
 #include "vm/object.h"
+#include "vm/service.h"
 #include "vm/service_event.h"
 #include "vm/thread.h"
 
@@ -665,7 +666,7 @@
     if (isolate_id_ != ILLEGAL_PORT) {
       // If we have one, append the isolate id.
       stream->UncloseObject();
-      stream->PrintfProperty("isolateNumber", "%" Pd64 "",
+      stream->PrintfProperty("isolateId", ISOLATE_SERVICE_ID_FORMAT_STRING,
                              static_cast<int64_t>(isolate_id_));
       stream->CloseObject();
     }
@@ -677,7 +678,7 @@
     }
     if (isolate_id_ != ILLEGAL_PORT) {
       // If we have one, append the isolate id.
-      args.AddPropertyF("isolateNumber", "%" Pd64 "",
+      args.AddPropertyF("isolateId", ISOLATE_SERVICE_ID_FORMAT_STRING,
                         static_cast<int64_t>(isolate_id_));
     }
   }
@@ -1199,7 +1200,7 @@
     return;
   }
   JSONObject topLevel(js);
-  topLevel.AddProperty("type", "_Timeline");
+  topLevel.AddProperty("type", "Timeline");
   {
     JSONArray events(&topLevel, "traceEvents");
     PrintJSONMeta(&events);
@@ -1294,11 +1295,13 @@
     return;
   }
   JSONObject topLevel(js);
-  topLevel.AddProperty("type", "_Timeline");
+  topLevel.AddProperty("type", "Timeline");
   {
     JSONArray events(&topLevel, "traceEvents");
     PrintJSONMeta(&events);
   }
+  topLevel.AddPropertyTimeMicros("timeOriginMicros", TimeOriginMicros());
+  topLevel.AddPropertyTimeMicros("timeExtentMicros", TimeExtentMicros());
 }
 
 void TimelineEventCallbackRecorder::PrintTraceEvent(
@@ -1332,11 +1335,13 @@
     return;
   }
   JSONObject topLevel(js);
-  topLevel.AddProperty("type", "_Timeline");
+  topLevel.AddProperty("type", "Timeline");
   {
     JSONArray events(&topLevel, "traceEvents");
     PrintJSONMeta(&events);
   }
+  topLevel.AddPropertyTimeMicros("timeOriginMicros", TimeOriginMicros());
+  topLevel.AddPropertyTimeMicros("timeExtentMicros", TimeExtentMicros());
 }
 
 void TimelineEventPlatformRecorder::PrintTraceEvent(
@@ -1380,7 +1385,7 @@
     return;
   }
   JSONObject topLevel(js);
-  topLevel.AddProperty("type", "_Timeline");
+  topLevel.AddProperty("type", "Timeline");
   {
     JSONArray events(&topLevel, "traceEvents");
     PrintJSONMeta(&events);
diff --git a/runtime/vm/timeline.h b/runtime/vm/timeline.h
index 9d43824..68d85c2 100644
--- a/runtime/vm/timeline.h
+++ b/runtime/vm/timeline.h
@@ -36,6 +36,13 @@
 class VirtualMemory;
 class Zone;
 
+#define CALLBACK_RECORDER_NAME "Callback"
+#define ENDLESS_RECORDER_NAME "Endless"
+#define FUCHSIA_RECORDER_NAME "Fuchsia"
+#define RING_RECORDER_NAME "Ring"
+#define STARTUP_RECORDER_NAME "Startup"
+#define SYSTRACE_RECORDER_NAME "Systrace"
+
 // (name, fuchsia_name).
 #define TIMELINE_STREAM_LIST(V)                                                \
   V(API, "dart:api")                                                           \
@@ -787,7 +794,7 @@
       : TimelineEventFixedBufferRecorder(capacity) {}
   virtual ~TimelineEventRingRecorder() {}
 
-  const char* name() const { return "Ring"; }
+  const char* name() const { return RING_RECORDER_NAME; }
 
  protected:
   TimelineEventBlock* GetNewBlockLocked();
@@ -801,7 +808,7 @@
       : TimelineEventFixedBufferRecorder(capacity) {}
   virtual ~TimelineEventStartupRecorder() {}
 
-  const char* name() const { return "Startup"; }
+  const char* name() const { return STARTUP_RECORDER_NAME; }
 
  protected:
   TimelineEventBlock* GetNewBlockLocked();
@@ -823,7 +830,7 @@
   // |event| as it may be freed as soon as this function returns.
   virtual void OnEvent(TimelineEvent* event) = 0;
 
-  const char* name() const { return "Callback"; }
+  const char* name() const { return CALLBACK_RECORDER_NAME; }
 
  protected:
   TimelineEventBlock* GetNewBlockLocked() { return NULL; }
@@ -846,7 +853,7 @@
   void PrintTraceEvent(JSONStream* js, TimelineEventFilter* filter);
 #endif
 
-  const char* name() const { return "Endless"; }
+  const char* name() const { return ENDLESS_RECORDER_NAME; }
 
  protected:
   TimelineEvent* StartEvent();
@@ -919,7 +926,7 @@
   TimelineEventFuchsiaRecorder() {}
   virtual ~TimelineEventFuchsiaRecorder() {}
 
-  const char* name() const { return "Fuchsia"; }
+  const char* name() const { return FUCHSIA_RECORDER_NAME; }
 
  private:
   void OnEvent(TimelineEvent* event);
@@ -939,7 +946,7 @@
                                 char* buffer,
                                 intptr_t buffer_size);
 
-  const char* name() const { return "Systrace"; }
+  const char* name() const { return SYSTRACE_RECORDER_NAME; }
 
  private:
   void OnEvent(TimelineEvent* event);
diff --git a/runtime/vm/timeline_test.cc b/runtime/vm/timeline_test.cc
index eaf09f6..56313ab 100644
--- a/runtime/vm/timeline_test.cc
+++ b/runtime/vm/timeline_test.cc
@@ -256,8 +256,8 @@
   JSONStream js;
   TimelineEventFilter filter;
   recorder->PrintJSON(&js, &filter);
-  // Check the type. This test will fail if we ever make Timeline public.
-  EXPECT_SUBSTRING("\"type\":\"_Timeline\"", js.ToCString());
+  // Check the type.
+  EXPECT_SUBSTRING("\"type\":\"Timeline\"", js.ToCString());
   // Check that there is a traceEvents array.
   EXPECT_SUBSTRING("\"traceEvents\":[", js.ToCString());
 }
diff --git a/runtime/vm/unit_test.cc b/runtime/vm/unit_test.cc
index 880a945..b75e93b 100644
--- a/runtime/vm/unit_test.cc
+++ b/runtime/vm/unit_test.cc
@@ -747,17 +747,6 @@
 #endif  // !PRODUCT
 }
 
-bool CompilerTest::TestCompileScript(const Library& library,
-                                     const Script& script) {
-  Isolate* isolate = Isolate::Current();
-  ASSERT(isolate != NULL);
-  const Error& error = Error::Handle(Compiler::Compile(library, script));
-  if (!error.IsNull()) {
-    OS::PrintErr("Error compiling test script:\n%s\n", error.ToErrorCString());
-  }
-  return error.IsNull();
-}
-
 bool CompilerTest::TestCompileFunction(const Function& function) {
   Thread* thread = Thread::Current();
   ASSERT(thread != NULL);
diff --git a/runtime/vm/unit_test.h b/runtime/vm/unit_test.h
index bbdfea4..742e3a7 100644
--- a/runtime/vm/unit_test.h
+++ b/runtime/vm/unit_test.h
@@ -598,10 +598,6 @@
 
 class CompilerTest : public AllStatic {
  public:
-  // Test the Compiler::CompileScript functionality by checking the return
-  // value to see if no parse errors were reported.
-  static bool TestCompileScript(const Library& library, const Script& script);
-
   // Test the Compiler::CompileFunction functionality by checking the return
   // value to see if no parse errors were reported.
   static bool TestCompileFunction(const Function& function);
diff --git a/runtime/vm/v8_snapshot_writer.cc b/runtime/vm/v8_snapshot_writer.cc
index b8ef328..9c04c5a 100644
--- a/runtime/vm/v8_snapshot_writer.cc
+++ b/runtime/vm/v8_snapshot_writer.cc
@@ -31,14 +31,8 @@
   edge_types_.Insert({"element", kElement});
   edge_types_.Insert({"property", kProperty});
   edge_types_.Insert({"internal", kInternal});
-  edge_types_.Insert({"hidden", kHidden});
-  edge_types_.Insert({"shortcut", kShortcut});
-  edge_types_.Insert({"weak", kWeak});
-  edge_types_.Insert({"extra", kExtra});
 
   strings_.Insert({"<unknown>", kUnknownString});
-  strings_.Insert({"<object>", kObjectString});
-  strings_.Insert({"<property>", kPropertyString});
   strings_.Insert({"<artificial root>", kArtificialRootString});
 }
 
@@ -55,11 +49,11 @@
   intptr_t type_id = node_types_.LookupValue(type);
   ASSERT(info->type == kUnknown || info->type == type_id);
   info->type = type_id;
-
   if (name != nullptr) {
-    info->name = EnsureString(OS::SCreate(zone_, "[%s] %s", type, name));
+    info->name = EnsureString(name);
   } else {
-    info->name = EnsureString(type);
+    info->name =
+        EnsureString(OS::SCreate(zone_, "Unnamed [%s] %s", type, name));
   }
 }
 
@@ -245,7 +239,7 @@
     ObjectIdToNodeInfoTraits::Pair* entry = nullptr;
     auto roots_it = roots_.GetIterator();
     for (int i = 0; (entry = roots_it.Next()) != nullptr; ++i) {
-      WriteEdgeInfo(writer, {kElement, i, entry->key});
+      WriteEdgeInfo(writer, {kInternal, i, entry->key});
     }
 
     auto nodes_it = nodes_.GetIterator();
diff --git a/runtime/vm/v8_snapshot_writer.h b/runtime/vm/v8_snapshot_writer.h
index 63d0617..b3e5e09 100644
--- a/runtime/vm/v8_snapshot_writer.h
+++ b/runtime/vm/v8_snapshot_writer.h
@@ -61,9 +61,7 @@
 
   enum ConstantStrings {
     kUnknownString = 0,
-    kPropertyString = 1,
-    kObjectString = 2,
-    kArtificialRootString = 3,
+    kArtificialRootString = 1,
   };
 
 #if !defined(DART_PRECOMPILER)
diff --git a/runtime/vm/virtual_memory.h b/runtime/vm/virtual_memory.h
index 496e63c..a91f07a 100644
--- a/runtime/vm/virtual_memory.h
+++ b/runtime/vm/virtual_memory.h
@@ -99,10 +99,6 @@
 
   static uword page_size_;
 
-#if defined(HOST_OS_FUCHSIA)
-  static uword base_;  // Cached base of root vmar.
-#endif
-
   DISALLOW_IMPLICIT_CONSTRUCTORS(VirtualMemory);
 };
 
diff --git a/runtime/vm/virtual_memory_fuchsia.cc b/runtime/vm/virtual_memory_fuchsia.cc
index 3f934fd..91e9a78 100644
--- a/runtime/vm/virtual_memory_fuchsia.cc
+++ b/runtime/vm/virtual_memory_fuchsia.cc
@@ -39,22 +39,9 @@
 DECLARE_FLAG(bool, write_protect_code);
 
 uword VirtualMemory::page_size_ = 0;
-uword VirtualMemory::base_ = 0;
 
 void VirtualMemory::Init() {
   page_size_ = getpagesize();
-
-  // Cache the base of zx_vmar_root_self() which is used to align mappings.
-  zx_info_vmar_t buf[1];
-  size_t actual;
-  size_t avail;
-  zx_status_t status =
-      zx_object_get_info(zx_vmar_root_self(), ZX_INFO_VMAR, buf,
-                         sizeof(zx_info_vmar_t), &actual, &avail);
-  if (status != ZX_OK) {
-    FATAL1("zx_object_get_info failed: %s\n", zx_status_get_string(status));
-  }
-  base_ = buf[0].base;
 }
 
 static void Unmap(zx_handle_t vmar, uword start, uword end) {
@@ -70,48 +57,6 @@
   }
 }
 
-static void* MapAligned(zx_handle_t vmar,
-                        zx_handle_t vmo,
-                        zx_vm_option_t options,
-                        uword size,
-                        uword alignment,
-                        uword vmar_base,
-                        uword padded_size) {
-  // Allocate a larger mapping than needed in order to find a suitable aligned
-  // mapping within it.
-  uword base;
-  zx_status_t status =
-      zx_vmar_map(vmar, options, 0, vmo, 0u, padded_size, &base);
-  LOG_INFO("zx_vmar_map(%u, 0x%lx, 0x%lx)\n", options, base, padded_size);
-  if (status != ZX_OK) {
-    LOG_ERR("zx_vmar_map(%u, 0x%lx, 0x%lx) failed: %s\n", options, base,
-            padded_size, zx_status_get_string(status));
-    return NULL;
-  }
-
-  // Allocate a smaller aligned mapping inside the larger mapping.
-  const uword orig_base = base;
-  const uword aligned_base = Utils::RoundUp(base, alignment);
-  const zx_vm_option_t overwrite_options = options | ZX_VM_SPECIFIC_OVERWRITE;
-  status = zx_vmar_map(vmar, overwrite_options, aligned_base - vmar_base, vmo,
-                       0u, size, &base);
-  LOG_INFO("zx_vmar_map(%u, 0x%lx, 0x%lx)\n", overwrite_options,
-           aligned_base - vmar_base, size);
-
-  if (status != ZX_OK) {
-    LOG_ERR("zx_vmar_map(%u, 0x%lx, 0x%lx) failed: %s\n", overwrite_options,
-            aligned_base - vmar_base, size, zx_status_get_string(status));
-    return NULL;
-  }
-  ASSERT(base == aligned_base);
-
-  // Unmap the unused prefix and suffix.
-  Unmap(vmar, orig_base, base);
-  Unmap(vmar, base + size, orig_base + padded_size);
-
-  return reinterpret_cast<void*>(base);
-}
-
 VirtualMemory* VirtualMemory::AllocateAligned(intptr_t size,
                                               intptr_t alignment,
                                               bool is_executable,
@@ -127,7 +72,10 @@
   ASSERT(Utils::IsAligned(size, page_size_));
   ASSERT(Utils::IsPowerOfTwo(alignment));
   ASSERT(Utils::IsAligned(alignment, page_size_));
-  const intptr_t padded_size = size + alignment - page_size_;
+
+  const zx_vm_option_t align_flag = Utils::ShiftForPowerOfTwo(alignment)
+                                    << ZX_VM_ALIGN_BASE;
+  ASSERT((ZX_VM_ALIGN_1KB <= align_flag) && (align_flag <= ZX_VM_ALIGN_4GB));
 
   zx_handle_t vmar = zx_vmar_root_self();
   zx_handle_t vmo = ZX_HANDLE_INVALID;
@@ -154,27 +102,34 @@
   }
 
   const zx_vm_option_t region_options =
-      ZX_VM_PERM_READ | ZX_VM_PERM_WRITE |
+      ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | align_flag |
       ((is_executable && !FLAG_write_protect_code) ? ZX_VM_PERM_EXECUTE : 0);
-  void* region_ptr = MapAligned(vmar, vmo, region_options, size, alignment,
-                                base_, padded_size);
-  if (region_ptr == NULL) {
+  uword base;
+  status = zx_vmar_map(vmar, region_options, 0, vmo, 0u, size, &base);
+  LOG_INFO("zx_vmar_map(%u, 0x%lx, 0x%lx)\n", region_options, base, size);
+  if (status != ZX_OK) {
+    LOG_ERR("zx_vmar_map(%u, 0x%lx, 0x%lx) failed: %s\n", region_options, base,
+            size, zx_status_get_string(status));
     return NULL;
   }
+  void* region_ptr = reinterpret_cast<void*>(base);
   MemoryRegion region(region_ptr, size);
 
   VirtualMemory* result;
 
   if (dual_mapping) {
     // ZX_VM_PERM_EXECUTE is added later via VirtualMemory::Protect.
-    const zx_vm_option_t alias_options = ZX_VM_PERM_READ;
-    void* alias_ptr = MapAligned(vmar, vmo, alias_options, size, alignment,
-                                 base_, padded_size);
-    if (alias_ptr == NULL) {
+    const zx_vm_option_t alias_options = ZX_VM_PERM_READ | align_flag;
+    status = zx_vmar_map(vmar, alias_options, 0, vmo, 0u, size, &base);
+    LOG_INFO("zx_vmar_map(%u, 0x%lx, 0x%lx)\n", alias_options, base, size);
+    if (status != ZX_OK) {
+      LOG_ERR("zx_vmar_map(%u, 0x%lx, 0x%lx) failed: %s\n", alias_options, base,
+              size, zx_status_get_string(status));
       const uword region_base = reinterpret_cast<uword>(region_ptr);
       Unmap(vmar, region_base, region_base + size);
       return NULL;
     }
+    void* alias_ptr = reinterpret_cast<void*>(base);
     ASSERT(region_ptr != alias_ptr);
     MemoryRegion alias(alias_ptr, size);
     result = new VirtualMemory(region, alias, region);
diff --git a/runtime/vm/vm_sources.gni b/runtime/vm/vm_sources.gni
index a1f20bf..2a07f21 100644
--- a/runtime/vm/vm_sources.gni
+++ b/runtime/vm/vm_sources.gni
@@ -7,7 +7,6 @@
 vm_sources = [
   "allocation.cc",
   "allocation.h",
-  "ast.h",
   "base64.cc",
   "base64.h",
   "base_isolate.h",
diff --git a/samples/ffi/sample_ffi_functions.dart b/samples/ffi/sample_ffi_functions.dart
index 4a3cf98..9d326e9 100644
--- a/samples/ffi/sample_ffi_functions.dart
+++ b/samples/ffi/sample_ffi_functions.dart
@@ -18,7 +18,7 @@
 typedef NativeFunc4 = ffi.IntPtr Function(ffi.IntPtr);
 typedef NativeDoubleUnaryOp = ffi.Double Function(ffi.Double);
 typedef NativeFloatUnaryOp = ffi.Float Function(ffi.Float);
-typedef NativeOctenaryOp = ffi.IntPtr Function(
+typedef NativeDecenaryOp = ffi.IntPtr Function(
     ffi.IntPtr,
     ffi.IntPtr,
     ffi.IntPtr,
@@ -29,7 +29,7 @@
     ffi.IntPtr,
     ffi.IntPtr,
     ffi.IntPtr);
-typedef NativeDoubleOctenaryOp = ffi.Double Function(
+typedef NativeDoubleDecenaryOp = ffi.Double Function(
     ffi.Double,
     ffi.Double,
     ffi.Double,
@@ -65,9 +65,9 @@
     ffi.Pointer<ffi.Int64>);
 typedef QuadOp = int Function(int, int, int, int);
 typedef DoubleUnaryOp = double Function(double);
-typedef OctenaryOp = int Function(
+typedef DecenaryOp = int Function(
     int, int, int, int, int, int, int, int, int, int);
-typedef DoubleOctenaryOp = double Function(double, double, double, double,
+typedef DoubleDecenaryOp = double Function(double, double, double, double,
     double, double, double, double, double, double);
 typedef VigesimalOp = double Function(
     int,
@@ -162,8 +162,8 @@
 
   {
     // function with many arguments: arguments get passed in registers and stack
-    OctenaryOp sumManyInts = ffiTestFunctions
-        .lookupFunction<NativeOctenaryOp, OctenaryOp>("SumManyInts");
+    DecenaryOp sumManyInts = ffiTestFunctions
+        .lookupFunction<NativeDecenaryOp, DecenaryOp>("SumManyInts");
     var result = sumManyInts(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
     print(result);
     print(result.runtimeType);
@@ -171,8 +171,8 @@
 
   {
     // function with many double arguments
-    DoubleOctenaryOp sumManyDoubles = ffiTestFunctions.lookupFunction<
-        NativeDoubleOctenaryOp, DoubleOctenaryOp>("SumManyDoubles");
+    DoubleDecenaryOp sumManyDoubles = ffiTestFunctions.lookupFunction<
+        NativeDoubleDecenaryOp, DoubleDecenaryOp>("SumManyDoubles");
     var result =
         sumManyDoubles(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0);
     print(result);
diff --git a/samples/samples.status b/samples/samples.status
index 09ae4a3..f72f9e5 100644
--- a/samples/samples.status
+++ b/samples/samples.status
@@ -23,6 +23,9 @@
 [ !$preview_dart_2 && ($runtime == dart_precompiled || $runtime == vm) ]
 *: SkipByDesign # Deprecating all Dart1 modes of execution
 
+[ $arch != x64 || $compiler != dartk || $system != linux || $hot_reload || $hot_reload_rollback ]
+ffi/sqlite/test/sqlite_test: Skip # FFI not supported or libsqlite3.so not available.
+
 [ $compiler == app_jitk || $compiler == dartkb || $compiler == dartkp ]
 sample_extension/test/sample_extension_app_snapshot_test: SkipByDesign
 sample_extension/test/sample_extension_test: SkipByDesign
@@ -33,6 +36,3 @@
 
 [ $hot_reload || $hot_reload_rollback ]
 sample_extension/test/sample_extension_app_snapshot_test: SkipByDesign # Cannot reload with URI pointing to app snapshot.
-
-[ $arch != x64 || $system != linux || $compiler != dartk || $hot_reload || $hot_reload_rollback ]
-ffi/sqlite/test/sqlite_test: Skip  # FFI not supported or libsqlite3.so not available.
diff --git a/sdk/lib/_http/http.dart b/sdk/lib/_http/http.dart
index 1e2560f..a759348 100644
--- a/sdk/lib/_http/http.dart
+++ b/sdk/lib/_http/http.dart
@@ -912,60 +912,78 @@
 }
 
 /**
- * Representation of a cookie. For cookies received by the server as
- * Cookie header values only [:name:] and [:value:] fields will be
- * set. When building a cookie for the 'set-cookie' header in the server
- * and when receiving cookies in the client as 'set-cookie' headers all
- * fields can be used.
+ * Representation of a cookie. For cookies received by the server as Cookie
+ * header values only [name] and [value] properties will be set. When building a
+ * cookie for the 'set-cookie' header in the server and when receiving cookies
+ * in the client as 'set-cookie' headers all fields can be used.
  */
 abstract class Cookie {
   /**
-   * Gets and sets the name.
+   * The name of the cookie.
+   *
+   * Must be a `token` as specified in RFC 6265.
+   *
+   * The allowed characters in a `token` are the visible ASCII characters,
+   * U+0021 (`!`) through U+007E (`~`), except the separator characters:
+   * `(`, `)`, `<`, `>`, `@`, `,`, `;`, `:`, `\`, `"`, `/`, `[`, `]`, `?`, `=`,
+   * `{`, and `}`.
    */
   String name;
 
   /**
-   * Gets and sets the value.
+   * The value of the cookie.
+   *
+   * Must be a `cookie-value` as specified in RFC 6265.
+   *
+   * The allowed characters in a cookie value are the visible ASCII characters,
+   * U+0021 (`!`) through U+007E (`~`) except the characters:
+   * `"`, `,`, `;` and `\`.
+   * Cookie values may be wrapped in a single pair of double quotes
+   * (U+0022, `"`).
    */
   String value;
 
   /**
-   * Gets and sets the expiry date.
+   * The time at which the cookie expires.
    */
   DateTime expires;
 
   /**
-   * Gets and sets the max age. A value of [:0:] means delete cookie
-   * now.
+   * The number of seconds until the cookie expires. A zero or negative value
+   * means the cookie has expired.
    */
   int maxAge;
 
   /**
-   * Gets and sets the domain.
+   * The domain the cookie applies to.
    */
   String domain;
 
   /**
-   * Gets and sets the path.
+   * The path within the [domain] the cookie applies to.
    */
   String path;
 
   /**
-   * Gets and sets whether this cookie is secure.
+   * Whether to only send this cookie on secure connections.
    */
   bool secure;
 
   /**
-   * Gets and sets whether this cookie is HTTP only.
+   * Whether the cookie is only sent in the HTTP request and is not made
+   * available to client side scripts.
    */
   bool httpOnly;
 
   /**
-   * Creates a new cookie optionally setting the name and value.
+   * Creates a new cookie setting the name and value.
+   *
+   * [name] and [value] must be composed of valid characters according to RFC
+   * 6265.
    *
    * By default the value of `httpOnly` will be set to `true`.
    */
-  factory Cookie([String name, String value]) => new _Cookie(name, value);
+  factory Cookie(String name, String value) => new _Cookie(name, value);
 
   /**
    * Creates a new cookie by parsing a header value from a 'set-cookie'
diff --git a/sdk/lib/_http/http_headers.dart b/sdk/lib/_http/http_headers.dart
index dc9ff84..7601485 100644
--- a/sdk/lib/_http/http_headers.dart
+++ b/sdk/lib/_http/http_headers.dart
@@ -829,8 +829,8 @@
 }
 
 class _Cookie implements Cookie {
-  String name;
-  String value;
+  String _name;
+  String _value;
   DateTime expires;
   int maxAge;
   String domain;
@@ -838,10 +838,22 @@
   bool httpOnly = false;
   bool secure = false;
 
-  _Cookie([this.name, this.value]) {
-    // Default value of httponly is true.
-    httpOnly = true;
-    _validate();
+  _Cookie(String name, String value)
+      : _name = _validateName(name),
+        _value = _validateValue(value),
+        httpOnly = true;
+
+  String get name => _name;
+  String get value => _value;
+
+  set name(String newName) {
+    _validateName(newName);
+    _name = newName;
+  }
+
+  set value(String newValue) {
+    _validateValue(newValue);
+    _value = newValue;
   }
 
   _Cookie.fromSetCookieValue(String value) {
@@ -924,13 +936,12 @@
       }
     }
 
-    name = parseName();
-    if (done() || name.length == 0) {
+    _name = _validateName(parseName());
+    if (done() || _name.length == 0) {
       throw new HttpException("Failed to parse header value [$s]");
     }
     index++; // Skip the = character.
-    value = parseValue();
-    _validate();
+    _value = _validateValue(parseValue());
     if (done()) return;
     index++; // Skip the ; character.
     parseAttributes();
@@ -938,7 +949,7 @@
 
   String toString() {
     StringBuffer sb = new StringBuffer();
-    sb..write(name)..write("=")..write(value);
+    sb..write(_name)..write("=")..write(_value);
     if (expires != null) {
       sb..write("; Expires=")..write(HttpDate.format(expires));
     }
@@ -956,7 +967,7 @@
     return sb.toString();
   }
 
-  void _validate() {
+  static String _validateName(String newName) {
     const separators = const [
       "(",
       ")",
@@ -976,31 +987,36 @@
       "{",
       "}"
     ];
-    for (int i = 0; i < name.length; i++) {
-      int codeUnit = name.codeUnits[i];
+    if (newName == null) throw new ArgumentError.notNull("name");
+    for (int i = 0; i < newName.length; i++) {
+      int codeUnit = newName.codeUnits[i];
       if (codeUnit <= 32 ||
           codeUnit >= 127 ||
-          separators.indexOf(name[i]) >= 0) {
+          separators.indexOf(newName[i]) >= 0) {
         throw new FormatException(
             "Invalid character in cookie name, code unit: '$codeUnit'",
-            name,
+            newName,
             i);
       }
     }
+    return newName;
+  }
 
+  static String _validateValue(String newValue) {
+    if (newValue == null) throw new ArgumentError.notNull("value");
     // Per RFC 6265, consider surrounding "" as part of the value, but otherwise
     // double quotes are not allowed.
     int start = 0;
-    int end = value.length;
-    if (2 <= value.length &&
-        value.codeUnits[start] == 0x22 &&
-        value.codeUnits[end - 1] == 0x22) {
+    int end = newValue.length;
+    if (2 <= newValue.length &&
+        newValue.codeUnits[start] == 0x22 &&
+        newValue.codeUnits[end - 1] == 0x22) {
       start++;
       end--;
     }
 
     for (int i = start; i < end; i++) {
-      int codeUnit = value.codeUnits[i];
+      int codeUnit = newValue.codeUnits[i];
       if (!(codeUnit == 0x21 ||
           (codeUnit >= 0x23 && codeUnit <= 0x2B) ||
           (codeUnit >= 0x2D && codeUnit <= 0x3A) ||
@@ -1008,9 +1024,10 @@
           (codeUnit >= 0x5D && codeUnit <= 0x7E))) {
         throw new FormatException(
             "Invalid character in cookie value, code unit: '$codeUnit'",
-            value,
+            newValue,
             i);
       }
     }
+    return newValue;
   }
 }
diff --git a/sdk/lib/_http/http_impl.dart b/sdk/lib/_http/http_impl.dart
index 497516c..5c39ad4 100644
--- a/sdk/lib/_http/http_impl.dart
+++ b/sdk/lib/_http/http_impl.dart
@@ -85,14 +85,14 @@
     _buffer = newBuffer;
   }
 
-  List<int> takeBytes() {
+  Uint8List takeBytes() {
     if (_length == 0) return _emptyList;
     var buffer = new Uint8List.view(_buffer.buffer, 0, _length);
     clear();
     return buffer;
   }
 
-  List<int> toBytes() {
+  Uint8List toBytes() {
     if (_length == 0) return _emptyList;
     return new Uint8List.fromList(
         new Uint8List.view(_buffer.buffer, 0, _length));
diff --git a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart
index d4cec70..4ba9bc7 100644
--- a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart
+++ b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart
@@ -14,10 +14,6 @@
   JS('', 'dart.__trapRuntimeErrors = #', flag);
 }
 
-void ignoreWhitelistedErrors(bool flag) {
-  JS('', 'dart.__ignoreWhitelistedErrors = #', flag);
-}
-
 // TODO(jmesserly): remove this?
 void ignoreAllErrors(bool flag) {
   JS('', 'dart.__ignoreAllErrors = #', flag);
diff --git a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart
index 0c287ae..1525959 100644
--- a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart
+++ b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart
@@ -274,6 +274,7 @@
       originalTarget = f;
       $f = ${bindCall(f, _canonicalMember(f, 'call'))};
       $ftype = null;
+      $displayName = "call";
     }
     if ($f == null) return callNSM(
         "Dynamic call of object has no instance method 'call'.");
@@ -325,8 +326,8 @@
   return callNSM(errorMessage);
 })()''');
 
-dcall(f, args, [@undefined named]) =>
-    _checkAndCall(f, null, JS('', 'void 0'), null, args, named, 'call');
+dcall(f, args, [@undefined named]) => _checkAndCall(
+    f, null, JS('', 'void 0'), null, args, named, JS('', 'f.name'));
 
 dgcall(f, typeArgs, args, [@undefined named]) =>
     _checkAndCall(f, null, JS('', 'void 0'), typeArgs, args, named, 'call');
@@ -411,56 +412,6 @@
 dsetindex(obj, index, value) =>
     callMethod(obj, '_set', null, [index, value], null, '[]=');
 
-final _ignoreSubtypeCache = JS('', 'new Map()');
-
-/// Whether [t1] <: [t2], or if [isImplicit] is set and we should ignore the
-/// cast failure from t1 to t2.
-///
-/// See [_isSubtypeOrLegacySubtype] and [ignoreWhitelistedErrors].
-@notNull
-bool _isSubtypeOrIgnorableCastFailure(
-    Object t1, Object t2, @notNull bool isImplicit) {
-  var result = _isSubtypeOrLegacySubtype(t1, t2);
-  return result == true ||
-      result == null &&
-          isImplicit &&
-          JS<bool>('!', 'dart.__ignoreWhitelistedErrors') &&
-          _ignoreTypeFailure(t1, t2);
-}
-
-@notNull
-bool _ignoreTypeFailure(Object t1, Object t2) {
-  var map = JS('', '#.get(#)', _ignoreSubtypeCache, t1);
-  if (map != null) {
-    bool result = JS('', '#.get(#)', map, t2);
-    if (JS('!', '# !== void 0', result)) return result;
-  } else {
-    map = JS('', 'new Map()');
-    JS('', '#.set(#, #)', _ignoreSubtypeCache, t1, map);
-  }
-
-  // TODO(vsm): Remove this hack ...
-  // This is primarily due to the lack of generic methods,
-  // but we need to triage all the types.
-  @notNull
-  bool result;
-  if (_isFutureOr(t2)) {
-    // Ignore if we would ignore either side of union.
-    var typeArg = getGenericArgs(t2)[0];
-    var typeFuture = JS('', '#(#)', getGenericClass(Future), typeArg);
-    result =
-        _ignoreTypeFailure(t1, typeFuture) || _ignoreTypeFailure(t1, typeArg);
-  } else {
-    result = isSubtypeOf(t2, unwrapType(Iterable)) &&
-        isSubtypeOf(t1, unwrapType(Iterable));
-    if (result) {
-      _warn('Ignoring cast fail from ${typeName(t1)} to ${typeName(t2)}');
-    }
-  }
-  JS('', '#.set(#, #)', map, t2, result);
-  return result;
-}
-
 @notNull
 @JSExportName('is')
 bool instanceOf(obj, type) {
@@ -474,7 +425,7 @@
 cast(obj, type, @notNull bool isImplicit) {
   if (obj == null) return obj;
   var actual = getReifiedType(obj);
-  if (_isSubtypeOrIgnorableCastFailure(actual, type, isImplicit)) {
+  if (_isSubtypeOrLegacySubtype(actual, type) == true) {
     return obj;
   }
   return castError(obj, type, isImplicit);
@@ -624,7 +575,7 @@
 /// - nested values of the object are themselves already canonicalized.
 ///
 @JSExportName('const')
-const_(obj) => JS('', '''(() => {  
+const_(obj) => JS('', '''(() => {
   let names = $getOwnNamesAndSymbols($obj);
   let count = names.length;
   // Index by count.  All of the paths through this map
diff --git a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/runtime.dart b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/runtime.dart
index 3ec16c9..d79e835 100644
--- a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/runtime.dart
+++ b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/runtime.dart
@@ -92,7 +92,7 @@
     if (typeof $window.SourceBufferList == "undefined") {
       if ('MediaSource' in $window) {
         $window.SourceBufferList =
-          new $window.MediaSource().sourceBuffers.constructor; 
+          new $window.MediaSource().sourceBuffers.constructor;
       }
     }
     if (typeof $window.SpeechRecognition == "undefined") {
@@ -130,9 +130,6 @@
     let settings = 'ddcSettings' in globalState ? globalState.ddcSettings : {};
     $trapRuntimeErrors(
         'trapRuntimeErrors' in settings ? settings.trapRuntimeErrors : false);
-    $ignoreWhitelistedErrors(
-        'ignoreWhitelistedErrors' in settings ?
-            settings.ignoreWhitelistedErrors : false);
 
     $ignoreAllErrors(
         'ignoreAllErrors' in settings ? settings.ignoreAllErrors : false);
@@ -200,7 +197,6 @@
   for (var m in _cacheMaps) JS('', '#.clear()', m);
   _cacheMaps.clear();
   JS('', '#.clear()', constantMaps);
-  JS('', '#.clear()', _ignoreSubtypeCache);
 }
 
 /// Marks enqueuing an async operation.
diff --git a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart
index 41c3de7..3c9852c 100644
--- a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart
+++ b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart
@@ -472,8 +472,7 @@
       var actual = JS('', '#[#]', obj, _runtimeType);
       // If there's no actual type, it's a JS function.
       // Allow them to subtype all Dart function types.
-      if (actual == null ||
-          _isSubtypeOrIgnorableCastFailure(actual, this, isImplicit)) {
+      if (actual == null || _isSubtypeOrLegacySubtype(actual, this) == true) {
         return obj;
       }
     }
diff --git a/sdk/lib/_internal/js_dev_runtime/private/native_typed_data.dart b/sdk/lib/_internal/js_dev_runtime/private/native_typed_data.dart
index 4bc3ff5..5947ad1 100644
--- a/sdk/lib/_internal/js_dev_runtime/private/native_typed_data.dart
+++ b/sdk/lib/_internal/js_dev_runtime/private/native_typed_data.dart
@@ -171,7 +171,7 @@
     _storage[(index * 4) + 3] = value.w;
   }
 
-  List<Float32x4> sublist(int start, [int end]) {
+  Float32x4List sublist(int start, [int end]) {
     end = _checkValidRange(start, end, this.length);
     return NativeFloat32x4List._externalStorage(
         _storage.sublist(start * 4, end * 4));
@@ -249,7 +249,7 @@
     _storage[(index * 4) + 3] = value.w;
   }
 
-  List<Int32x4> sublist(int start, [int end]) {
+  Int32x4List sublist(int start, [int end]) {
     end = _checkValidRange(start, end, this.length);
     return NativeInt32x4List._externalStorage(
         _storage.sublist(start * 4, end * 4));
@@ -321,7 +321,7 @@
     _storage[(index * 2) + 1] = value.y;
   }
 
-  List<Float64x2> sublist(int start, [int end]) {
+  Float64x2List sublist(int start, [int end]) {
     end = _checkValidRange(start, end, this.length);
     return NativeFloat64x2List._externalStorage(
         _storage.sublist(start * 2, end * 2));
@@ -831,7 +831,7 @@
 
   Type get runtimeType => Float32List;
 
-  List<double> sublist(int start, [int end]) {
+  Float32List sublist(int start, [int end]) {
     end = _checkValidRange(start, end, this.length);
     var source = JS('NativeFloat32List', '#.subarray(#, #)', this, start, end);
     return _create1(source);
@@ -865,7 +865,7 @@
 
   Type get runtimeType => Float64List;
 
-  List<double> sublist(int start, [int end]) {
+  Float64List sublist(int start, [int end]) {
     end = _checkValidRange(start, end, this.length);
     var source = JS('NativeFloat64List', '#.subarray(#, #)', this, start, end);
     return _create1(source);
@@ -903,7 +903,7 @@
     return JS('int', '#[#]', this, index);
   }
 
-  List<int> sublist(int start, [int end]) {
+  Int16List sublist(int start, [int end]) {
     end = _checkValidRange(start, end, this.length);
     var source = JS('NativeInt16List', '#.subarray(#, #)', this, start, end);
     return _create1(source);
@@ -941,7 +941,7 @@
     return JS('int', '#[#]', this, index);
   }
 
-  List<int> sublist(int start, [int end]) {
+  Int32List sublist(int start, [int end]) {
     end = _checkValidRange(start, end, this.length);
     var source = JS('NativeInt32List', '#.subarray(#, #)', this, start, end);
     return _create1(source);
@@ -979,7 +979,7 @@
     return JS('int', '#[#]', this, index);
   }
 
-  List<int> sublist(int start, [int end]) {
+  Int8List sublist(int start, [int end]) {
     end = _checkValidRange(start, end, this.length);
     var source = JS('NativeInt8List', '#.subarray(#, #)', this, start, end);
     return _create1(source);
@@ -1017,7 +1017,7 @@
     return JS('int', '#[#]', this, index);
   }
 
-  List<int> sublist(int start, [int end]) {
+  Uint16List sublist(int start, [int end]) {
     end = _checkValidRange(start, end, this.length);
     var source = JS('NativeUint16List', '#.subarray(#, #)', this, start, end);
     return _create1(source);
@@ -1055,7 +1055,7 @@
     return JS('int', '#[#]', this, index);
   }
 
-  List<int> sublist(int start, [int end]) {
+  Uint32List sublist(int start, [int end]) {
     end = _checkValidRange(start, end, this.length);
     var source = JS('NativeUint32List', '#.subarray(#, #)', this, start, end);
     return _create1(source);
@@ -1096,7 +1096,7 @@
     return JS('int', '#[#]', this, index);
   }
 
-  List<int> sublist(int start, [int end]) {
+  Uint8ClampedList sublist(int start, [int end]) {
     end = _checkValidRange(start, end, this.length);
     var source =
         JS('NativeUint8ClampedList', '#.subarray(#, #)', this, start, end);
@@ -1145,7 +1145,7 @@
     return JS('int', '#[#]', this, index);
   }
 
-  List<int> sublist(int start, [int end]) {
+  Uint8List sublist(int start, [int end]) {
     end = _checkValidRange(start, end, this.length);
     var source = JS('NativeUint8List', '#.subarray(#, #)', this, start, end);
     return _create1(source);
diff --git a/sdk/lib/_internal/js_runtime/lib/native_typed_data.dart b/sdk/lib/_internal/js_runtime/lib/native_typed_data.dart
index 9fa9070..0a91641 100644
--- a/sdk/lib/_internal/js_runtime/lib/native_typed_data.dart
+++ b/sdk/lib/_internal/js_runtime/lib/native_typed_data.dart
@@ -163,7 +163,7 @@
     _storage[(index * 4) + 3] = value.w;
   }
 
-  List<Float32x4> sublist(int start, [int end]) {
+  Float32x4List sublist(int start, [int end]) {
     end = _checkValidRange(start, end, this.length);
     return new NativeFloat32x4List._externalStorage(
         _storage.sublist(start * 4, end * 4));
@@ -235,7 +235,7 @@
     _storage[(index * 4) + 3] = value.w;
   }
 
-  List<Int32x4> sublist(int start, [int end]) {
+  Int32x4List sublist(int start, [int end]) {
     end = _checkValidRange(start, end, this.length);
     return new NativeInt32x4List._externalStorage(
         _storage.sublist(start * 4, end * 4));
@@ -302,7 +302,7 @@
     _storage[(index * 2) + 1] = value.y;
   }
 
-  List<Float64x2> sublist(int start, [int end]) {
+  Float64x2List sublist(int start, [int end]) {
     end = _checkValidRange(start, end, this.length);
     return new NativeFloat64x2List._externalStorage(
         _storage.sublist(start * 2, end * 2));
@@ -757,7 +757,7 @@
 
   Type get runtimeType => Float32List;
 
-  List<double> sublist(int start, [int end]) {
+  Float32List sublist(int start, [int end]) {
     end = _checkValidRange(start, end, this.length);
     var source = JS('NativeFloat32List', '#.subarray(#, #)', this, start, end);
     return _create1(source);
@@ -791,7 +791,7 @@
 
   Type get runtimeType => Float64List;
 
-  List<double> sublist(int start, [int end]) {
+  Float64List sublist(int start, [int end]) {
     end = _checkValidRange(start, end, this.length);
     var source = JS('NativeFloat64List', '#.subarray(#, #)', this, start, end);
     return _create1(source);
@@ -829,7 +829,7 @@
     return JS('int', '#[#]', this, index);
   }
 
-  List<int> sublist(int start, [int end]) {
+  Int16List sublist(int start, [int end]) {
     end = _checkValidRange(start, end, this.length);
     var source = JS('NativeInt16List', '#.subarray(#, #)', this, start, end);
     return _create1(source);
@@ -867,7 +867,7 @@
     return JS('int', '#[#]', this, index);
   }
 
-  List<int> sublist(int start, [int end]) {
+  Int32List sublist(int start, [int end]) {
     end = _checkValidRange(start, end, this.length);
     var source = JS('NativeInt32List', '#.subarray(#, #)', this, start, end);
     return _create1(source);
@@ -905,7 +905,7 @@
     return JS('int', '#[#]', this, index);
   }
 
-  List<int> sublist(int start, [int end]) {
+  Int8List sublist(int start, [int end]) {
     end = _checkValidRange(start, end, this.length);
     var source = JS('NativeInt8List', '#.subarray(#, #)', this, start, end);
     return _create1(source);
@@ -943,7 +943,7 @@
     return JS('JSUInt31', '#[#]', this, index);
   }
 
-  List<int> sublist(int start, [int end]) {
+  Uint16List sublist(int start, [int end]) {
     end = _checkValidRange(start, end, this.length);
     var source = JS('NativeUint16List', '#.subarray(#, #)', this, start, end);
     return _create1(source);
@@ -981,7 +981,7 @@
     return JS('JSUInt32', '#[#]', this, index);
   }
 
-  List<int> sublist(int start, [int end]) {
+  Uint32List sublist(int start, [int end]) {
     end = _checkValidRange(start, end, this.length);
     var source = JS('NativeUint32List', '#.subarray(#, #)', this, start, end);
     return _create1(source);
@@ -1022,7 +1022,7 @@
     return JS('JSUInt31', '#[#]', this, index);
   }
 
-  List<int> sublist(int start, [int end]) {
+  Uint8ClampedList sublist(int start, [int end]) {
     end = _checkValidRange(start, end, this.length);
     var source =
         JS('NativeUint8ClampedList', '#.subarray(#, #)', this, start, end);
@@ -1071,7 +1071,7 @@
     return JS('JSUInt31', '#[#]', this, index);
   }
 
-  List<int> sublist(int start, [int end]) {
+  Uint8List sublist(int start, [int end]) {
     end = _checkValidRange(start, end, this.length);
     var source = JS('NativeUint8List', '#.subarray(#, #)', this, start, end);
     return _create1(source);
diff --git a/sdk/lib/_internal/js_runtime/lib/rti.dart b/sdk/lib/_internal/js_runtime/lib/rti.dart
index 574cd52..b53dcc8 100644
--- a/sdk/lib/_internal/js_runtime/lib/rti.dart
+++ b/sdk/lib/_internal/js_runtime/lib/rti.dart
@@ -5,9 +5,14 @@
 /// This library contains support for runtime type information.
 library rti;
 
-import 'dart:_foreign_helper' show JS;
+import 'dart:_foreign_helper'
+    show JS, JS_EMBEDDED_GLOBAL, RAW_DART_FUNCTION_REF;
 import 'dart:_interceptors' show JSArray, JSUnmodifiableArray;
 
+import 'dart:_js_embedded_names' show RtiUniverseFieldNames, RTI_UNIVERSE;
+
+import 'dart:_recipe_syntax';
+
 /// An Rti object represents both a type (e.g `Map<int, String>`) and a type
 /// environment (`Map<int, String>` binds `Map.K=int` and `Map.V=String`).
 ///
@@ -51,15 +56,20 @@
 
   /// Method called from generated code to evaluate a type environment recipe in
   /// `this` type environment.
-  Rti _eval(String recipe) => _rtiEval(this, recipe);
+  Rti _eval(String recipe) {
+    // TODO(sra): Clone the fast-path of _Universe.evalInEnvironment to here.
+    return _rtiEval(this, recipe);
+  }
 
-  /// Method called from generated code to extend `this` type environment with a
-  /// function type parameter.
-  Rti _bind1(Rti type) => _rtiBind1(this, type);
+  /// Method called from generated code to extend `this` type environment (an
+  /// interface or binding Rti) with function type arguments (a singleton
+  /// argument or tuple of arguments).
+  Rti _bind(Rti typeOrTuple) => _rtiBind(this, typeOrTuple);
 
-  /// Method called from generated code to extend `this` type environment with a
-  /// tuple of function type parameters.
-  Rti _bind(Rti typeTuple) => _rtiBind(this, typeTuple);
+  /// Method called from generated code to extend `this` type (as a singleton
+  /// type environment) with function type arguments (a singleton argument or
+  /// tuple of arguments).
+  Rti _bind1(Rti typeOrTuple) => _rtiBind1(this, typeOrTuple);
 
   // Precomputed derived types. These fields are used to hold derived types that
   // are computed eagerly.
@@ -100,6 +110,7 @@
   static const kindBinding = 9;
   static const kindFunction = 10;
   static const kindGenericFunction = 11;
+  static const kindGenericFunctionParameter = 12;
 
   /// Primary data associated with type.
   ///
@@ -107,7 +118,6 @@
   /// - Underlying type for unary terms.
   /// - Class part of a type environment inside a generic class, or `null` for
   ///   type tuple.
-  /// - Return type of function types.
   dynamic _primary;
 
   static Object _getPrimary(Rti rti) => rti._primary;
@@ -140,10 +150,30 @@
     return JS('JSUnmodifiableArray', '#', _getRest(rti));
   }
 
-  /// On [Rti]s that are type environments, derived types are cached on the
+  static Rti _getBindingBase(Rti rti) {
+    assert(_getKind(rti) == kindBinding);
+    return _castToRti(_getPrimary(rti));
+  }
+
+  static JSArray _getBindingArguments(rti) {
+    assert(_getKind(rti) == kindBinding);
+    return JS('JSUnmodifiableArray', '#', _getRest(rti));
+  }
+
+  static Rti _getFutureOrArgument(Rti rti) {
+    assert(_getKind(rti) == kindFutureOr);
+    return _castToRti(_getPrimary(rti));
+  }
+
+  /// On [Rti]s that are type environments*, derived types are cached on the
   /// environment to ensure fast canonicalization. Ground-term types (i.e. not
   /// dependent on class or function type parameters) are cached in the
   /// universe. This field starts as `null` and the cache is created on demand.
+  ///
+  /// *Any Rti can be a type environment, since we use the type for a function
+  /// type environment. The ambiguity between 'generic class is the environment'
+  /// and 'generic class is a singleton type argument' is resolved by using
+  /// different indexing in the recipe.
   Object _evalCache;
 
   static Object _getEvalCache(Rti rti) => rti._evalCache;
@@ -151,6 +181,22 @@
     rti._evalCache = value;
   }
 
+  /// On [Rti]s that are type environments*, extended environments are cached on
+  /// the base environment to ensure fast canonicalization.
+  ///
+  /// This field starts as `null` and the cache is created on demand.
+  ///
+  /// *This is valid only on kindInterface and kindBinding Rtis. The ambiguity
+  /// between 'generic class is the base environment' and 'generic class is a
+  /// singleton type argument' is resolved [TBD] (either (1) a bind1 cache, or
+  /// (2)using `env._eval("@<0>")._bind(args)` in place of `env._bind1(args)`).
+  Object _bindCache;
+
+  static Object _getBindCache(Rti rti) => rti._evalCache;
+  static void _setBindCache(Rti rti, value) {
+    rti._evalCache = value;
+  }
+
   static Rti allocate() {
     return new Rti();
   }
@@ -168,25 +214,66 @@
   }
 }
 
+Object _theUniverse() => JS_EMBEDDED_GLOBAL('', RTI_UNIVERSE);
+
 Rti _rtiEval(Rti environment, String recipe) {
-  throw UnimplementedError('_rtiEval');
+  return _Universe.evalInEnvironment(_theUniverse(), environment, recipe);
 }
 
-Rti _rtiBind1(Rti environment, Rti type) {
-  throw UnimplementedError('_rtiBind1');
+Rti _rtiBind1(Rti environment, Rti types) {
+  return _Universe.bind1(_theUniverse(), environment, types);
 }
 
-Rti _rtiBind(Rti environment, Rti typeTuple) {
-  throw UnimplementedError('_rtiBind');
+Rti _rtiBind(Rti environment, Rti types) {
+  return _Universe.bind(_theUniverse(), environment, types);
+}
+
+/// Evaluate a ground-term type.
+/// Called from generated code.
+Rti findType(String recipe) {
+  _Universe.eval(_theUniverse(), recipe);
 }
 
 Type getRuntimeType(object) {
   throw UnimplementedError('getRuntimeType');
 }
 
+/// Called from generated code.
+bool _generalIsTestImplementation(object) {
+  // This static method is installed on an Rti object as a JavaScript instance
+  // method. The Rti object is 'this'.
+  Rti testRti = _castToRti(JS('', 'this'));
+  throw UnimplementedError(
+      '${Error.safeToString(object)} is ${_rtiToString(testRti, null)}');
+}
+
+/// Called from generated code.
+_generalAsCheckImplementation(object) {
+  // This static method is installed on an Rti object as a JavaScript instance
+  // method. The Rti object is 'this'.
+  Rti testRti = _castToRti(JS('', 'this'));
+  throw UnimplementedError(
+      '${Error.safeToString(object)} as ${_rtiToString(testRti, null)}');
+}
+
+/// Called from generated code.
+_generalTypeCheckImplementation(object) {
+  // This static method is installed on an Rti object as a JavaScript instance
+  // method. The Rti object is 'this'.
+  Rti testRti = _castToRti(JS('', 'this'));
+  throw UnimplementedError(
+      '${Error.safeToString(object)} as ${_rtiToString(testRti, null)}'
+      ' (TypeError)');
+}
+
 String _rtiToString(Rti rti, List<String> genericContext) {
   int kind = Rti._getKind(rti);
+
   if (kind == Rti.kindDynamic) return 'dynamic';
+  if (kind == Rti.kindVoid) return 'void';
+  if (kind == Rti.kindNever) return 'Never';
+  if (kind == Rti.kindAny) return 'any';
+
   if (kind == Rti.kindInterface) {
     String name = Rti._getInterfaceName(rti);
     var arguments = Rti._getInterfaceTypeArguments(rti);
@@ -200,9 +287,46 @@
     }
     return name;
   }
+
   return '?';
 }
 
+String _rtiToDebugString(Rti rti) {
+  String arrayToString(Object array) {
+    String s = '[', sep = '';
+    for (int i = 0; i < _Utils.arrayLength(array); i++) {
+      s += sep + _rtiToDebugString(_castToRti(_Utils.arrayAt(array, i)));
+      sep = ', ';
+    }
+    return s + ']';
+  }
+
+  int kind = Rti._getKind(rti);
+
+  if (kind == Rti.kindDynamic) return 'dynamic';
+  if (kind == Rti.kindVoid) return 'void';
+  if (kind == Rti.kindNever) return 'Never';
+  if (kind == Rti.kindAny) return 'any';
+
+  if (kind == Rti.kindInterface) {
+    String name = Rti._getInterfaceName(rti);
+    var arguments = Rti._getInterfaceTypeArguments(rti);
+    if (_Utils.arrayLength(arguments) == 0) {
+      return 'interface("$name")';
+    } else {
+      return 'interface("$name", ${arrayToString(arguments)})';
+    }
+  }
+
+  if (kind == Rti.kindBinding) {
+    var base = Rti._getBindingBase(rti);
+    var arguments = Rti._getBindingArguments(rti);
+    return 'binding(${_rtiToDebugString(base)}, ${arrayToString(arguments)})';
+  }
+
+  return 'other(kind=$kind)';
+}
+
 /// Class of static methods for the universe of Rti objects.
 ///
 /// The universe is the manager object for the Rti instances.
@@ -216,26 +340,35 @@
 
   @pragma('dart2js:noInline')
   static Object create() {
-    // TODO(sra): For consistency, this expression should be a JS_BUILTIN that
-    // uses the same template as emitted by the emitter.
+    // This needs to be kept in sync with `FragmentEmitter.createRtiUniverse` in
+    // `fragment_emitter.dart`.
     return JS(
         '',
         '{'
-            'evalCache: new Map(),'
-            'unprocessedRules:[],'
-            'a0:[],' // shared empty array.
-            '}');
+            '#: new Map(),'
+            '#: {},'
+            '#: [],' // shared empty array.
+            '}',
+        RtiUniverseFieldNames.evalCache,
+        RtiUniverseFieldNames.typeRules,
+        RtiUniverseFieldNames.sharedEmptyArray);
   }
 
   // Field accessors.
 
-  static evalCache(universe) => JS('', '#.evalCache', universe);
+  static evalCache(universe) =>
+      JS('', '#.#', universe, RtiUniverseFieldNames.evalCache);
 
-  static void addRules(universe, String rules) {
-    JS('', '#.unprocessedRules.push(#)', universe, rules);
+  static findRule(universe, String targetType) =>
+      JS('', '#.#.#', universe, RtiUniverseFieldNames.typeRules, targetType);
+
+  static void addRule(universe, String targetType, rule) {
+    JS('', '#.#.# = #', universe, RtiUniverseFieldNames.typeRules, targetType,
+        rule);
   }
 
-  static Object sharedEmptyArray(universe) => JS('JSArray', '#.a0', universe);
+  static Object sharedEmptyArray(universe) =>
+      JS('JSArray', '#.#', universe, RtiUniverseFieldNames.sharedEmptyArray);
 
   /// Evaluates [recipe] in the global environment.
   static Rti eval(Object universe, String recipe) {
@@ -261,6 +394,31 @@
     return rti;
   }
 
+  static Rti bind(Object universe, Rti environment, Rti argumentsRti) {
+    var cache = Rti._getBindCache(environment);
+    if (cache == null) {
+      cache = JS('', 'new Map()');
+      Rti._setBindCache(environment, cache);
+    }
+    var argumentsRecipe = Rti._getCanonicalRecipe(argumentsRti);
+    var probe = _cacheGet(cache, argumentsRecipe);
+    if (probe != null) return _castToRti(probe);
+    var argumentsArray;
+    if (Rti._getKind(argumentsRti) == Rti.kindBinding) {
+      argumentsArray = Rti._getBindingArguments(argumentsRti);
+    } else {
+      argumentsArray = JS('', '[]');
+      _Utils.arrayPush(argumentsArray, argumentsRti);
+    }
+    var rti = _lookupBindingRti(universe, environment, argumentsArray);
+    _cacheSet(cache, argumentsRecipe, rti);
+    return rti;
+  }
+
+  static Rti bind1(Object universe, Rti environment, Rti argumentsRti) {
+    throw UnimplementedError('_Universe.bind1');
+  }
+
   static Rti evalTypeVariable(Object universe, Rti environment, String name) {
     throw UnimplementedError('_Universe.evalTypeVariable("$name")');
   }
@@ -283,12 +441,13 @@
     _cacheSet(evalCache(universe), key, rti);
 
     // Set up methods to type tests.
-    // TODO(sra): These are for `dynamic`. Install general functions and
-    // specializations.
-    var alwaysPasses = JS('', 'function(o) { return o; }');
-    Rti._setAsCheckFunction(rti, alwaysPasses);
-    Rti._setTypeCheckFunction(rti, alwaysPasses);
-    Rti._setIsTestFunction(rti, JS('', 'function(o) { return true; }'));
+    // TODO(sra): Install specializations.
+    Rti._setAsCheckFunction(
+        rti, RAW_DART_FUNCTION_REF(_generalAsCheckImplementation));
+    Rti._setTypeCheckFunction(
+        rti, RAW_DART_FUNCTION_REF(_generalTypeCheckImplementation));
+    Rti._setIsTestFunction(
+        rti, RAW_DART_FUNCTION_REF(_generalIsTestImplementation));
 
     return rti;
   }
@@ -302,34 +461,60 @@
   // * `createXXX` to create the type if it does not exist.
 
   static String _canonicalRecipeOfDynamic() => '@';
+  static String _canonicalRecipeOfVoid() => '~';
+  static String _canonicalRecipeOfNever() => '0&';
+  static String _canonicalRecipeOfAny() => '1&';
 
   static Rti _lookupDynamicRti(universe) {
-    var cache = evalCache(universe);
-    var probe = _cacheGet(cache, _canonicalRecipeOfDynamic());
-    if (probe != null) return _castToRti(probe);
-    return _createDynamicRti(universe);
+    return _lookupTerminalRti(
+        universe, Rti.kindDynamic, _canonicalRecipeOfDynamic());
   }
 
-  static Rti _createDynamicRti(Object universe) {
+  static Rti _lookupVoidRti(universe) {
+    return _lookupTerminalRti(universe, Rti.kindVoid, _canonicalRecipeOfVoid());
+  }
+
+  static Rti _lookupNeverRti(universe) {
+    return _lookupTerminalRti(
+        universe, Rti.kindNever, _canonicalRecipeOfNever());
+  }
+
+  static Rti _lookupAnyRti(universe) {
+    return _lookupTerminalRti(universe, Rti.kindAny, _canonicalRecipeOfAny());
+  }
+
+  static Rti _lookupTerminalRti(universe, int kind, String canonicalRecipe) {
+    var cache = evalCache(universe);
+    var probe = _cacheGet(cache, canonicalRecipe);
+    if (probe != null) return _castToRti(probe);
+    return _createTerminalRti(universe, kind, canonicalRecipe);
+  }
+
+  static Rti _createTerminalRti(universe, int kind, String canonicalRecipe) {
     var rti = Rti.allocate();
-    Rti._setKind(rti, Rti.kindDynamic);
-    Rti._setCanonicalRecipe(rti, _canonicalRecipeOfDynamic());
+    Rti._setKind(rti, kind);
+    Rti._setCanonicalRecipe(rti, canonicalRecipe);
     return _finishRti(universe, rti);
   }
 
+  static String _canonicalRecipeJoin(Object arguments) {
+    String s = '', sep = '';
+    int length = _Utils.arrayLength(arguments);
+    for (int i = 0; i < length; i++) {
+      Rti argument = _castToRti(_Utils.arrayAt(arguments, i));
+      String subrecipe = Rti._getCanonicalRecipe(argument);
+      s += sep + subrecipe;
+      sep = ',';
+    }
+    return s;
+  }
+
   static String _canonicalRecipeOfInterface(String name, Object arguments) {
     assert(_Utils.isString(name));
     String s = _Utils.asString(name);
     int length = _Utils.arrayLength(arguments);
     if (length != 0) {
-      s += '<';
-      for (int i = 0; i < length; i++) {
-        if (i > 0) s += ',';
-        Rti argument = _castToRti(_Utils.arrayAt(arguments, i));
-        String subrecipe = Rti._getCanonicalRecipe(argument);
-        s += subrecipe;
-      }
-      s += '>';
+      s += '<' + _canonicalRecipeJoin(arguments) + '>';
     }
     return s;
   }
@@ -352,6 +537,39 @@
     Rti._setCanonicalRecipe(rti, key);
     return _finishRti(universe, rti);
   }
+
+  static String _canonicalRecipeOfBinding(Rti base, Object arguments) {
+    String s = Rti._getCanonicalRecipe(base);
+    s += ';'; // TODO(sra): Omit when base encoding is Rti without ToType.
+    s += '<' + _canonicalRecipeJoin(arguments) + '>';
+    return s;
+  }
+
+  /// [arguments] becomes owned by the created Rti.
+  static Rti _lookupBindingRti(Object universe, Rti base, Object arguments) {
+    var newBase = base;
+    var newArguments = arguments;
+    if (Rti._getKind(base) == Rti.kindBinding) {
+      newBase = Rti._getBindingBase(base);
+      newArguments =
+          _Utils.arrayConcat(Rti._getBindingArguments(base), arguments);
+    }
+    String key = _canonicalRecipeOfBinding(newBase, newArguments);
+    var cache = evalCache(universe);
+    var probe = _cacheGet(cache, key);
+    if (probe != null) return _castToRti(probe);
+    return _createBindingRti(universe, newBase, newArguments, key);
+  }
+
+  static Rti _createBindingRti(
+      Object universe, Rti base, Object arguments, String key) {
+    var rti = Rti.allocate();
+    Rti._setKind(rti, Rti.kindBinding);
+    Rti._setPrimary(rti, base);
+    Rti._setRest(rti, arguments);
+    Rti._setCanonicalRecipe(rti, key);
+    return _finishRti(universe, rti);
+  }
 }
 
 /// Class of static methods implementing recipe parser.
@@ -369,23 +587,101 @@
 ///
 ///   Period may be in any position, including first and last e.g. `.x`.
 ///
-/// ',': ignored
+/// ',':  ---
+///
+///   Ignored. Used to separate elements.
+///
+/// ';': item  ---  ToType(item)
 ///
 ///   Used to separate elements.
 ///
 /// '@': --- dynamicType
 ///
+/// '~': --- voidType
+///
 /// '?':  type  ---  type?
 ///
+/// '&':  0  ---  NeverType
+/// '&':  1  ---  anyType
+///
+///   Escape op-code with small integer values for encoding rare operations.
+///
 /// '<':  --- position
 ///
 ///   Saves (pushes) position register, sets position register to end of stack.
 ///
 /// '>':  name saved-position type ... type  ---  name<type, ..., type>
+/// '>':  type saved-position type ... type  ---  binding(type, type, ..., type)
 ///
-///   Creates interface type from name types pushed since the position register
-///   was last set. Restores position register to previous saved value.
+///   When first element is a String: Creates interface type from string 'name'
+///   and the types pushed since the position register was last set. The types
+///   are converted with a ToType operation. Restores position register to
+///   previous saved value.
 ///
+///   When first element is an Rti: Creates binding Rti wrapping the first
+///   element. Binding Rtis are flattened: if the first element is a binding
+///   Rti, the new binding Rti has the concatentation of the first element
+///   bindings and new type.
+///
+///
+/// The ToType operation coerces an item to an Rti. This saves encoding looking
+/// up simple interface names and indexed variables.
+///
+///   ToType(string): Creates an interface Rti for the non-generic class.
+///   ToType(integer): Indexes into the environment.
+///   ToType(Rti): Same Rti
+///
+///
+/// Notes on enviroments and indexing.
+///
+/// To avoid creating a binding Rti for a single function type parameter, the
+/// type is passed without creating a 1-tuple object. This means that the
+/// interface Rti for, say, `Map<num,dynamic>` serves as two environments with
+/// different shapes. It is a class environment (K=num, V=dynamic) and a simple
+/// 1-tuple environment. This is supported by index '0' refering to the whole
+/// type, and '1 and '2' refering to K and V positionally:
+///
+///     interface("Map", [num,dynamic])
+///     0                 1   2
+///
+/// Thus the type expression `List<K>` encodes as `List<1>` and in this
+/// environment evaluates to `List<num>`. `List<Map<K,V>>` could be encoded as
+/// either `List<0>` or `List<Map<1,2>>` (and in this environment evaluates to
+/// `List<Map<num,dynamic>>`).
+///
+/// When `Map<num,dynamic>` is combined with a binding `<int,bool>` (e.g. inside
+/// the instance method `Map<K,V>.cast<RK,RV>()`), '0' refers to the base object
+/// of the binding, and then the numbering counts the bindings followed by the
+/// class parameters.
+///
+///     binding(interface("Map", [num,dynamic]), [int, bool])
+///             0                 3   4           1    2
+///
+/// Any environment can be reconstructed via a recipe. The above enviroment for
+/// method `cast` can be constructed as the ground term
+/// `Map<num,dynamic><int,bool>`, or (somewhat pointlessly) reconstructed via
+/// `0<1,2>` or `Map<3,4><1,2>`. The ability to construct an environment
+/// directly rather than via `bind` calls is used in folding sequences of `eval`
+/// and `bind` calls.
+///
+/// While a single type parameter is passed as the type, multiple type
+/// parameters are passed as a tuple. Tuples are encoded as a binding with an
+/// ignored base. `dynamic` can be used as the base, giving an encoding like
+/// `@<int,bool>`.
+///
+/// Bindings flatten, so `@<int><bool><num>` is the same as `@<int,bool,num>`.
+///
+/// The base of a binding does not have to have type parameters. Consider
+/// `CodeUnits`, which mixes in `ListMixin<int>`. The environment inside of
+/// `ListMixin.fold` (from the call `x.codeUnits.fold<bool>(...)`) would be
+///
+///     binding(interface("CodeUnits", []), [bool])
+///
+/// This can be encoded as `CodeUnits;<bool>` (note the `;` to force ToType to
+/// avoid creating an interface type Rti with a single class type
+/// argument). Metadata about the supertypes is used to resolve the recipe
+/// `ListMixin.E` to `int`.
+
 class _Parser {
   _Parser._() {
     throw UnimplementedError('_Parser is static methods only');
@@ -434,30 +730,42 @@
     int i = 0;
     while (i < source.length) {
       int ch = charCodeAt(source, i);
-      if (isDigit(ch)) {
+      if (Recipe.isDigit(ch)) {
         i = handleDigit(i + 1, ch, source, stack);
-      } else if (isIdentifierStart(ch)) {
+      } else if (Recipe.isIdentifierStart(ch)) {
         i = handleIdentifer(parser, i, source, stack, false);
-      } else if (ch == $PERIOD) {
+      } else if (ch == Recipe.period) {
         i = handleIdentifer(parser, i, source, stack, true);
       } else {
         i++;
         switch (ch) {
-          case $COMMA:
-            // ignored
+          case Recipe.noOp:
             break;
 
-          case $AT:
+          case Recipe.toType:
+            push(stack,
+                toType(universe(parser), environment(parser), pop(stack)));
+            break;
+
+          case Recipe.pushDynamic:
             push(stack, _Universe._lookupDynamicRti(universe(parser)));
             break;
 
-          case $LT:
+          case Recipe.pushVoid:
+            push(stack, _Universe._lookupVoidRti(universe(parser)));
+            break;
+
+          case Recipe.startTypeArguments:
             push(stack, position(parser));
             setPosition(parser, _Utils.arrayLength(stack));
             break;
 
-          case $GT:
-            handleGenericInterfaceType(parser, stack);
+          case Recipe.endTypeArguments:
+            handleTypeArguments(parser, stack);
+            break;
+
+          case Recipe.extensionOp:
+            handleExtendedOperations(parser, stack);
             break;
 
           default:
@@ -470,11 +778,11 @@
   }
 
   static int handleDigit(int i, int digit, String source, Object stack) {
-    int value = digit - $0;
+    int value = Recipe.digitValue(digit);
     for (; i < source.length; i++) {
       int ch = charCodeAt(source, i);
-      if (!isDigit(ch)) break;
-      value = value * 10 + ch - $0;
+      if (!Recipe.isDigit(ch)) break;
+      value = value * 10 + Recipe.digitValue(ch);
     }
     push(stack, value);
     return i;
@@ -485,10 +793,10 @@
     int i = start + 1;
     for (; i < source.length; i++) {
       int ch = charCodeAt(source, i);
-      if (ch == $PERIOD) {
+      if (ch == Recipe.period) {
         if (hasPeriod) break;
         hasPeriod = true;
-      } else if (isIdentifierStart(ch) || isDigit(ch)) {
+      } else if (Recipe.isIdentifierStart(ch) || Recipe.isDigit(ch)) {
         // Accept.
       } else {
         break;
@@ -506,13 +814,37 @@
     return i;
   }
 
-  static void handleGenericInterfaceType(Object parser, Object stack) {
+  static void handleTypeArguments(Object parser, Object stack) {
     var universe = _Parser.universe(parser);
-    var arguments = _Utils.arraySplice(stack, position(parser));
-    toTypes(universe, environment(parser), arguments);
+    var arguments = collectArray(parser, stack);
+    var head = pop(stack);
+    if (_Utils.isString(head)) {
+      String name = _Utils.asString(head);
+      push(stack, _Universe._lookupInterfaceRti(universe, name, arguments));
+    } else {
+      Rti base = toType(universe, environment(parser), head);
+      push(stack, _Universe._lookupBindingRti(universe, base, arguments));
+    }
+  }
+
+  static void handleExtendedOperations(Object parser, Object stack) {
+    var top = pop(stack);
+    if (0 == top) {
+      push(stack, _Universe._lookupNeverRti(universe(parser)));
+      return;
+    }
+    if (1 == top) {
+      push(stack, _Universe._lookupAnyRti(universe(parser)));
+      return;
+    }
+    throw AssertionError('Unexpected extended operation $top');
+  }
+
+  static Object collectArray(Object parser, Object stack) {
+    var array = _Utils.arraySplice(stack, position(parser));
+    toTypes(_Parser.universe(parser), environment(parser), array);
     setPosition(parser, _Utils.asInt(pop(stack)));
-    String name = _Utils.asString(pop(stack));
-    push(stack, _Universe._lookupInterfaceRti(universe, name, arguments));
+    return array;
   }
 
   /// Coerce a stack item into an Rti object. Strings are converted to interface
@@ -527,7 +859,7 @@
       return _Universe._lookupInterfaceRti(
           universe, name, _Universe.sharedEmptyArray(universe));
     } else if (_Utils.isNum(item)) {
-      return _Parser._indexToType(universe, environment, _Utils.asInt(item));
+      return _Parser.indexToType(universe, environment, _Utils.asInt(item));
     } else {
       return _castToRti(item);
     }
@@ -542,56 +874,167 @@
     }
   }
 
-  static Rti _indexToType(Object universe, Rti environment, int index) {
-    while (true) {
-      int kind = Rti._getKind(environment);
-      if (kind == Rti.kindInterface) {
-        var typeArguments = Rti._getInterfaceTypeArguments(environment);
-        int len = _Utils.arrayLength(typeArguments);
-        if (index < len) {
-          return _castToRti(_Utils.arrayAt(typeArguments, index));
-        }
-        throw AssertionError('Bad index $index for $environment');
+  static Rti indexToType(Object universe, Rti environment, int index) {
+    int kind = Rti._getKind(environment);
+    if (kind == Rti.kindBinding) {
+      if (index == 0) return Rti._getBindingBase(environment);
+      var typeArguments = Rti._getBindingArguments(environment);
+      int len = _Utils.arrayLength(typeArguments);
+      if (index <= len) {
+        return _castToRti(_Utils.arrayAt(typeArguments, index - 1));
       }
-      // TODO(sra): Binding environment.
-      throw AssertionError('Recipe cannot index Rti kind $kind');
+      // Is index into interface Rti in base.
+      index -= len;
+      environment = Rti._getBindingBase(environment);
+      kind = Rti._getKind(environment);
+    } else {
+      if (index == 0) return environment;
     }
+    if (kind != Rti.kindInterface) {
+      throw AssertionError('Indexed base must be an interface type');
+    }
+    var typeArguments = Rti._getInterfaceTypeArguments(environment);
+    int len = _Utils.arrayLength(typeArguments);
+    if (index <= len) {
+      return _castToRti(_Utils.arrayAt(typeArguments, index - 1));
+    }
+    throw AssertionError('Bad index $index for $environment');
+  }
+}
+
+/// Represents the set of supertypes and type variable bindings for a given
+/// target type. The target type itself is not stored on the [TypeRule].
+class TypeRule {
+  TypeRule._() {
+    throw UnimplementedError("TypeRule is static methods only.");
   }
 
-  static bool isDigit(int ch) => ch >= $0 && ch <= $9;
-  static bool isIdentifierStart(int ch) =>
-      (ch >= $A && ch <= $Z) ||
-      (ch >= $a && ch <= $z) ||
-      (ch == $_) ||
-      (ch == $$);
+  // TODO(fishythefish): Create whole rule at once rather than adding individual
+  // supertypes/type variables.
 
-  static const int $$ = 0x24;
-  static const int $COMMA = 0x2C;
-  static const int $PERIOD = 0x2E;
-  static const int $0 = 0x30;
-  static const int $9 = 0x39;
-  static const int $LT = 0x3C;
-  static const int $GT = 0x3E;
-  static const int $A = 0x41;
-  static const int $AT = 0x40;
-  static const int $Z = $A + 26 - 1;
-  static const int $a = $A + 32;
-  static const int $z = $Z + 32;
-  static const int $_ = 0x5F;
+  @pragma('dart2js:noInline')
+  static Object create() {
+    return JS('=Object', '{}');
+  }
+
+  static void addTypeVariable(rule, String typeVariable, String binding) {
+    JS('', '#.# = #', rule, typeVariable, binding);
+  }
+
+  static String lookupTypeVariable(rule, String typeVariable) =>
+      JS('String|Null', '#.#', rule, typeVariable);
+
+  static void addSupertype(rule, String supertype, Object supertypeArgs) {
+    JS('', '#.# = #', rule, supertype, supertypeArgs);
+  }
+
+  static JSArray lookupSupertype(rule, String supertype) =>
+      JS('JSArray|Null', '#.#', rule, supertype);
 }
 
 // -------- Subtype tests ------------------------------------------------------
 
 // Future entry point from compiled code.
-bool isSubtype(Rti s, Rti t) {
-  return _isSubtype(s, null, t, null);
+bool isSubtype(universe, Rti s, Rti t) {
+  return _isSubtype(universe, s, null, t, null);
 }
 
-bool _isSubtype(Rti s, var sEnv, Rti t, var tEnv) {
+bool _isSubtype(universe, Rti s, var sEnv, Rti t, var tEnv) {
+  // TODO(fishythefish): Update for NNBD. See
+  // https://github.com/dart-lang/language/blob/master/resources/type-system/subtyping.md#rules
+
+  // Subtyping is reflexive.
   if (_Utils.isIdentical(s, t)) return true;
-  int tKind = Rti._getKind(t);
-  if (tKind == Rti.kindDynamic) return true;
-  if (tKind == Rti.kindNever) return false;
+
+  if (isTopType(t)) return true;
+
+  if (isJsInteropType(s)) return true;
+
+  if (isTopType(s)) {
+    if (isGenericFunctionTypeParameter(t)) return false;
+    if (isFutureOrType(t)) {
+      // [t] is FutureOr<T>. Check [s] <: T.
+      var tTypeArgument = Rti._getFutureOrArgument(t);
+      return _isSubtype(universe, s, sEnv, tTypeArgument, tEnv);
+    }
+    return false;
+  }
+
+  // Generic function type parameters must match exactly, which would have
+  // exited earlier. The de Bruijn indexing ensures the representation as a
+  // small number can be used for type comparison.
+  // TODO(fishythefish): Use the bound of the type variable.
+  if (isGenericFunctionTypeParameter(s)) return false;
+  if (isGenericFunctionTypeParameter(t)) return false;
+
+  if (isNullType(s)) return true;
+
+  if (isFunctionType(t)) {
+    // TODO(fishythefish): Check if s is a function subtype of t.
+    throw UnimplementedError("isFunctionType(t)");
+  }
+
+  if (isFunctionType(s)) {
+    // TODO(fishythefish): Check if t is Function.
+    throw UnimplementedError("isFunctionType(s)");
+  }
+
+  if (isFutureOrType(t)) {
+    // [t] is FutureOr<T>.
+    var tTypeArgument = Rti._getFutureOrArgument(t);
+    if (isFutureOrType(s)) {
+      // [s] is FutureOr<S>. Check S <: T.
+      var sTypeArgument = Rti._getFutureOrArgument(s);
+      return _isSubtype(universe, sTypeArgument, sEnv, tTypeArgument, tEnv);
+    } else if (_isSubtype(universe, s, sEnv, tTypeArgument, tEnv)) {
+      // `true` because [s] <: T.
+      return true;
+    } else {
+      // TODO(fishythefish): Check [s] <: Future<T>.
+    }
+  }
+
+  assert(Rti._getKind(s) == Rti.kindInterface);
+  assert(Rti._getKind(t) == Rti.kindInterface);
+  String sName = Rti._getInterfaceName(s);
+  String tName = Rti._getInterfaceName(t);
+  // TODO(fishythefish): Handle identical names.
+
+  var rule = _Universe.findRule(universe, sName);
+  if (rule == null) return false;
+  var supertypeArgs = TypeRule.lookupSupertype(rule, tName);
+  if (supertypeArgs == null) return false;
+  var tArgs = Rti._getInterfaceTypeArguments(t);
+  int length = _Utils.arrayLength(supertypeArgs);
+  assert(length == _Utils.arrayLength(tArgs));
+  for (int i = 0; i < length; i++) {
+    String recipe = _Utils.arrayAt(supertypeArgs, i);
+    Rti supertypeArg = _Universe.evalInEnvironment(universe, s, recipe);
+    Rti tArg = _castToRti(_Utils.arrayAt(tArgs, i));
+    if (!isSubtype(universe, supertypeArg, tArg)) return false;
+  }
+
+  return true;
+}
+
+bool isTopType(Rti t) =>
+    isDynamicType(t) || isVoidType(t) || isObjectType(t) || isJsInteropType(t);
+
+bool isDynamicType(Rti t) => Rti._getKind(t) == Rti.kindDynamic;
+bool isVoidType(Rti t) => Rti._getKind(t) == Rti.kindVoid;
+bool isJsInteropType(Rti t) => Rti._getKind(t) == Rti.kindAny;
+bool isFutureOrType(Rti t) => Rti._getKind(t) == Rti.kindFutureOr;
+bool isFunctionType(Rti t) => Rti._getKind(t) == Rti.kindFunction;
+bool isGenericFunctionTypeParameter(Rti t) =>
+    Rti._getKind(t) == Rti.kindGenericFunctionParameter;
+
+bool isObjectType(Rti t) {
+  // TODO(fishythefish): Look up Object in universe and compare.
+  return false;
+}
+
+bool isNullType(Rti t) {
+  // TODO(fishythefish): Look up Null in universe and compare.
   return false;
 }
 
@@ -619,6 +1062,13 @@
   static JSArray arraySplice(Object array, int position) =>
       JS('JSArray', '#.splice(#)', array, position);
 
+  static JSArray arrayConcat(Object a1, Object a2) =>
+      JS('JSArray', '#.concat(#)', a1, a2);
+
+  static void arrayPush(Object array, Object value) {
+    JS('', '#.push(#)', array, value);
+  }
+
   static String substring(String s, int start, int end) =>
       JS('String', '#.substring(#, #)', s, start, end);
 
@@ -635,20 +1085,31 @@
 }
 
 String testingRtiToDebugString(rti) {
-  // TODO(sra): Create entty point for structural formatting of Rti tree.
-  return 'Rti';
+  return _rtiToDebugString(_castToRti(rti));
 }
 
 Object testingCreateUniverse() {
   return _Universe.create();
 }
 
-Object testingAddRules(universe, String rules) {
-  _Universe.addRules(universe, rules);
+Object testingCreateRule() {
+  return TypeRule.create();
 }
 
-bool testingIsSubtype(rti1, rti2) {
-  return isSubtype(_castToRti(rti1), _castToRti(rti2));
+void testingAddTypeVariable(rule, String typeVariable, String binding) {
+  TypeRule.addTypeVariable(rule, typeVariable, binding);
+}
+
+void testingAddSupertype(rule, String supertype, Object supertypeArgs) {
+  TypeRule.addSupertype(rule, supertype, supertypeArgs);
+}
+
+void testingAddRule(universe, String targetType, rule) {
+  _Universe.addRule(universe, targetType, rule);
+}
+
+bool testingIsSubtype(universe, rti1, rti2) {
+  return isSubtype(universe, _castToRti(rti1), _castToRti(rti2));
 }
 
 Object testingUniverseEval(universe, String recipe) {
@@ -658,3 +1119,8 @@
 Object testingEnvironmentEval(universe, environment, String recipe) {
   return _Universe.evalInEnvironment(universe, _castToRti(environment), recipe);
 }
+
+Object testingEnvironmentBind(universe, environment, arguments) {
+  return _Universe.bind(
+      universe, _castToRti(environment), _castToRti(arguments));
+}
diff --git a/sdk/lib/_internal/js_runtime/lib/shared/embedded_names.dart b/sdk/lib/_internal/js_runtime/lib/shared/embedded_names.dart
index bc10677..95231a0 100644
--- a/sdk/lib/_internal/js_runtime/lib/shared/embedded_names.dart
+++ b/sdk/lib/_internal/js_runtime/lib/shared/embedded_names.dart
@@ -192,6 +192,11 @@
 /// globals don't clash with it.
 const DEFERRED_INITIALIZED = 'deferredInitialized';
 
+/// A 'Universe' object used by 'dart:_rti'.
+///
+/// This embedded global is used for --experiment-new-rti.
+const RTI_UNIVERSE = 'typeUniverse';
+
 /// Returns a function that creates all precompiled functions (in particular
 /// constructors).
 ///
@@ -411,3 +416,10 @@
   ///                JsBuiltin.getType, index);
   getType,
 }
+
+/// Names of fields of the Rti Universe object.
+class RtiUniverseFieldNames {
+  static String evalCache = 'eC';
+  static String typeRules = 'tR';
+  static String sharedEmptyArray = 'sEA';
+}
diff --git a/sdk/lib/_internal/js_runtime/lib/shared/recipe_syntax.dart b/sdk/lib/_internal/js_runtime/lib/shared/recipe_syntax.dart
new file mode 100644
index 0000000..78d0975
--- /dev/null
+++ b/sdk/lib/_internal/js_runtime/lib/shared/recipe_syntax.dart
@@ -0,0 +1,67 @@
+// Copyright (c) 2019, 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.
+
+/// Constants and predicates used for encoding and decoding type recipes.
+///
+/// This library is shared between the compiler and the runtime system.
+library dart2js._recipe_syntax;
+
+abstract class Recipe {
+  Recipe._();
+
+  // Operators.
+
+  static const int noOp = _comma;
+
+  static const int toType = _semicolon;
+
+  static const int pushDynamic = _at;
+  static const int pushVoid = _tilde;
+  static const int wrapFutureOr = _formfeed;
+
+  static const int startTypeArguments = _lessThan;
+  static const int endTypeArguments = _greaterThan;
+
+  static const int extensionOp = _ampersand;
+
+  // Number and name components.
+
+  static bool isDigit(int code) => code >= _digit0 && code <= _digit9;
+  static int digitValue(int code) => code - _digit0;
+
+  static bool isIdentifierStart(int ch) =>
+      (((ch | 32) - _lowercaseA) & 0xffff) < 26 ||
+      (ch == _underscore) ||
+      (ch == _dollar);
+
+  static const int period = _period;
+
+  // Private names.
+
+  static const int _formfeed = 0x0C; // '\f' in string literal.
+
+  static const int _dollar = 0x24;
+  static const int _ampersand = 0x26;
+  static const int _plus = 0x2B;
+  static const int _comma = 0x2C;
+  static const int _period = 0x2E;
+  static const int _digit0 = 0x30;
+  static const int _digit9 = 0x39;
+  static const int _semicolon = 0x3B;
+  static const int _lessThan = 0x3C;
+  static const int _greaterThan = 0x3E;
+  static const int _question = 0x3f;
+  static const int _at = 0x40;
+
+  static const int _underscore = 0x5F;
+  static const int _lowercaseA = 0x61;
+  static const int _tilde = 0x7E;
+
+  static const int _leftParen = 0x28;
+  static const int _rightParen = 0x29;
+  static const int _leftBracket = 0x5B;
+  static const int _rightBracket = 0x5D;
+  static const int _leftBrace = 0x7B;
+  static const int _rightBrace = 0x7D;
+}
diff --git a/sdk/lib/_internal/sdk_library_metadata/lib/libraries.dart b/sdk/lib/_internal/sdk_library_metadata/lib/libraries.dart
index f848a09..cfd6fa6 100644
--- a/sdk/lib/_internal/sdk_library_metadata/lib/libraries.dart
+++ b/sdk/lib/_internal/sdk_library_metadata/lib/libraries.dart
@@ -181,6 +181,11 @@
       categories: "",
       documented: false,
       platforms: DART2JS_PLATFORM),
+  "_recipe_syntax": const LibraryInfo(
+      "_internal/js_runtime/lib/shared/recipe_syntax.dart",
+      categories: "",
+      documented: false,
+      platforms: DART2JS_PLATFORM),
   "_metadata": const LibraryInfo("html/html_common/metadata.dart",
       categories: "", documented: false, platforms: DART2JS_PLATFORM),
 };
@@ -293,26 +298,26 @@
       1,
       "Experimental",
       "This library is experimental and will likely change or be removed\n"
-      "in future versions.");
+          "in future versions.");
 
   static const Maturity UNSTABLE = const Maturity(
       2,
       "Unstable",
       "This library is in still changing and have not yet endured\n"
-      "sufficient real-world testing.\n"
-      "Backwards-compatibility is NOT guaranteed.");
+          "sufficient real-world testing.\n"
+          "Backwards-compatibility is NOT guaranteed.");
 
   static const Maturity WEB_STABLE = const Maturity(
       3,
       "Web Stable",
       "This library is tracking the DOM evolution as defined by WC3.\n"
-      "Backwards-compatibility is NOT guaranteed.");
+          "Backwards-compatibility is NOT guaranteed.");
 
   static const Maturity STABLE = const Maturity(
       4,
       "Stable",
       "The library is stable. API backwards-compatibility is guaranteed.\n"
-      "However implementation details might change.");
+          "However implementation details might change.");
 
   static const Maturity LOCKED = const Maturity(5, "Locked",
       "This library will not change except when serious bugs are encountered.");
diff --git a/sdk/lib/convert/utf.dart b/sdk/lib/convert/utf.dart
index 66c9cac..39eb135 100644
--- a/sdk/lib/convert/utf.dart
+++ b/sdk/lib/convert/utf.dart
@@ -75,7 +75,7 @@
   ///
   /// If [start] and [end] are provided, only the substring
   /// `string.substring(start, end)` is converted.
-  List<int> convert(String string, [int start = 0, int end]) {
+  Uint8List convert(String string, [int start = 0, int end]) {
     var stringLength = string.length;
     end = RangeError.checkValidRange(start, end, stringLength);
     var length = end - start;
@@ -117,7 +117,7 @@
 class _Utf8Encoder {
   int _carry = 0;
   int _bufferIndex = 0;
-  final List<int> _buffer;
+  final Uint8List _buffer;
 
   static const _DEFAULT_BYTE_BUFFER_SIZE = 1024;
 
@@ -127,7 +127,7 @@
       : _buffer = _createBuffer(bufferSize);
 
   /// Allow an implementation to pick the most efficient way of storing bytes.
-  static List<int> _createBuffer(int size) => Uint8List(size);
+  static Uint8List _createBuffer(int size) => Uint8List(size);
 
   /// Tries to combine the given [leadingSurrogate] with the [nextCodeUnit] and
   /// writes it to [_buffer].
diff --git a/sdk/lib/core/uri.dart b/sdk/lib/core/uri.dart
index 2c7d918..044c863 100644
--- a/sdk/lib/core/uri.dart
+++ b/sdk/lib/core/uri.dart
@@ -761,6 +761,9 @@
     // authority     = [ userinfo "@" ] host [ ":" port ]
     // userinfo      = *( unreserved / pct-encoded / sub-delims / ":" )
     // host          = IP-literal / IPv4address / reg-name
+    // IP-literal    = "[" ( IPv6address / IPv6addrz / IPvFuture ) "]"
+    // IPv6addrz     = IPv6address "%25" ZoneID
+    // ZoneID        = 1*( unreserved / pct-encoded )
     // port          = *DIGIT
     // reg-name      = *( unreserved / pct-encoded / sub-delims )
     //
@@ -1643,14 +1646,24 @@
       if (hostStart < authority.length &&
           authority.codeUnitAt(hostStart) == _LEFT_BRACKET) {
         // IPv6 host.
+        int escapeForZoneID = -1;
         for (; hostEnd < authority.length; hostEnd++) {
-          if (authority.codeUnitAt(hostEnd) == _RIGHT_BRACKET) break;
+          int char = authority.codeUnitAt(hostEnd);
+          if (char == _PERCENT && escapeForZoneID < 0) {
+            escapeForZoneID = hostEnd;
+            if (authority.startsWith("25", hostEnd + 1)) {
+              hostEnd += 2; // Might as well skip the already checked escape.
+            }
+          } else if (char == _RIGHT_BRACKET) {
+            break;
+          }
         }
         if (hostEnd == authority.length) {
           throw FormatException(
               "Invalid IPv6 host entry.", authority, hostStart);
         }
-        Uri.parseIPv6Address(authority, hostStart + 1, hostEnd);
+        Uri.parseIPv6Address(authority, hostStart + 1,
+            (escapeForZoneID < 0) ? hostEnd : escapeForZoneID);
         hostEnd++; // Skip the closing bracket.
         if (hostEnd != authority.length &&
             authority.codeUnitAt(hostEnd) != _COLON) {
@@ -1959,22 +1972,124 @@
       if (host.codeUnitAt(end - 1) != _RIGHT_BRACKET) {
         _fail(host, start, 'Missing end `]` to match `[` in host');
       }
-      Uri.parseIPv6Address(host, start + 1, end - 1);
+      String zoneID = "";
+      int index = _checkZoneID(host, start + 1, end - 1);
+      if (index < end - 1) {
+        int zoneIDstart =
+            (host.startsWith("25", index + 1)) ? index + 3 : index + 1;
+        zoneID = _normalizeZoneID(host, zoneIDstart, end - 1, "%25");
+      }
+      Uri.parseIPv6Address(host, start + 1, index);
       // RFC 5952 requires hex digits to be lower case.
-      return host.substring(start, end).toLowerCase();
+      return host.substring(start, index).toLowerCase() + zoneID + ']';
     }
     if (!strictIPv6) {
       // TODO(lrn): skip if too short to be a valid IPv6 address?
       for (int i = start; i < end; i++) {
         if (host.codeUnitAt(i) == _COLON) {
-          Uri.parseIPv6Address(host, start, end);
-          return '[$host]';
+          String zoneID = "";
+          int index = _checkZoneID(host, start, end);
+          if (index < end) {
+            int zoneIDstart =
+                (host.startsWith("25", index + 1)) ? index + 3 : index + 1;
+            zoneID = _normalizeZoneID(host, zoneIDstart, end, "%25");
+          }
+          Uri.parseIPv6Address(host, start, index);
+          return '[${host.substring(start, index)}' + zoneID + ']';
         }
       }
     }
     return _normalizeRegName(host, start, end);
   }
 
+  // RFC 6874 check for ZoneID
+  // Return the index of first appeared `%`.
+  static int _checkZoneID(String host, int start, int end) {
+    int index = host.indexOf('%', start);
+    index = (index >= start && index < end) ? index : end;
+    return index;
+  }
+
+  static bool _isZoneIDChar(int char) {
+    return char < 127 && (_zoneIDTable[char >> 4] & (1 << (char & 0xf))) != 0;
+  }
+
+  /**
+   * Validates and does case- and percent-encoding normalization.
+   *
+   * The same as [_normalizeOrSubstring]
+   * except this function does not convert characters to lower case.
+   * The [host] must be an RFC6874 "ZoneID".
+   * ZoneID = 1*(unreserved / pct-encoded)
+   */
+  static String _normalizeZoneID(String host, int start, int end,
+      [String prefix = '']) {
+    StringBuffer buffer;
+    if (prefix != '') {
+      buffer = StringBuffer(prefix);
+    }
+    int sectionStart = start;
+    int index = start;
+    // Whether all characters between sectionStart and index are normalized,
+    bool isNormalized = true;
+
+    while (index < end) {
+      int char = host.codeUnitAt(index);
+      if (char == _PERCENT) {
+        String replacement = _normalizeEscape(host, index, true);
+        if (replacement == null && isNormalized) {
+          index += 3;
+          continue;
+        }
+        buffer ??= StringBuffer();
+        String slice = host.substring(sectionStart, index);
+        buffer.write(slice);
+        int sourceLength = 3;
+        if (replacement == null) {
+          replacement = host.substring(index, index + 3);
+        } else if (replacement == "%") {
+          _fail(host, index, "ZoneID should not contain % anymore");
+        }
+        buffer.write(replacement);
+        index += sourceLength;
+        sectionStart = index;
+        isNormalized = true;
+      } else if (_isZoneIDChar(char)) {
+        if (isNormalized && _UPPER_CASE_A <= char && _UPPER_CASE_Z >= char) {
+          // Put initial slice in buffer and continue in non-normalized mode
+          buffer ??= StringBuffer();
+          if (sectionStart < index) {
+            buffer.write(host.substring(sectionStart, index));
+            sectionStart = index;
+          }
+          isNormalized = false;
+        }
+        index++;
+      } else {
+        int sourceLength = 1;
+        if ((char & 0xFC00) == 0xD800 && (index + 1) < end) {
+          int tail = host.codeUnitAt(index + 1);
+          if ((tail & 0xFC00) == 0xDC00) {
+            char = 0x10000 | ((char & 0x3ff) << 10) | (tail & 0x3ff);
+            sourceLength = 2;
+          }
+        }
+        buffer ??= StringBuffer();
+        String slice = host.substring(sectionStart, index);
+        buffer.write(slice);
+        buffer.write(_escapeChar(char));
+        index += sourceLength;
+        sectionStart = index;
+      }
+    }
+    if (buffer == null) return host.substring(start, end);
+    if (sectionStart < end) {
+      String slice = host.substring(sectionStart, end);
+      buffer.write(slice);
+    }
+    return buffer.toString();
+  }
+
   static bool _isRegNameChar(int char) {
     return char < 127 && (_regNameTable[char >> 4] & (1 << (char & 0xf))) != 0;
   }
@@ -3120,6 +3235,27 @@
     //                      pqrstuvwxyz   ~
     0x47ff, // 0x70 - 0x7f  1111111111100010
   ];
+
+  // Characters allowed in the ZoneID as of RFC 6874.
+  // ZoneID = 1*( unreserved / pct-encoded )
+  static const _zoneIDTable = <int>[
+    //                     LSB            MSB
+    //                      |              |
+    0x0000, // 0x00 - 0x0f  0000000000000000
+    0x0000, // 0x10 - 0x1f  0000000000000000
+    //                       !  $%&'()*+,-.
+    0x6000, // 0x20 - 0x2f  0000000000000110
+    //                      0123456789 ; =
+    0x03ff, // 0x30 - 0x3f  1111111111000000
+    //                       ABCDEFGHIJKLMNO
+    0xfffe, // 0x40 - 0x4f  0111111111111111
+    //                      PQRSTUVWXYZ    _
+    0x87ff, // 0x50 - 0x5f  1111111111100001
+    //                       abcdefghijklmno
+    0xfffe, // 0x60 - 0x6f  0111111111111111
+    //                      pqrstuvwxyz   ~
+    0x47ff, // 0x70 - 0x7f  1111111111100010
+  ];
 }
 
 // --------------------------------------------------------------------
diff --git a/sdk/lib/developer/timeline.dart b/sdk/lib/developer/timeline.dart
index b18ed93..72d28af 100644
--- a/sdk/lib/developer/timeline.dart
+++ b/sdk/lib/developer/timeline.dart
@@ -166,6 +166,9 @@
 
   /// The current time stamp from the clock used by the timeline. Units are
   /// microseconds.
+  ///
+  /// When run on the Dart VM, uses the same monotonic clock as the embedding
+  /// API's `Dart_TimelineGetMicros`.
   static int get now => _getTraceClock();
   static final List<_SyncBlock> _stack = new List<_SyncBlock>();
 }
diff --git a/sdk/lib/io/bytes_builder.dart b/sdk/lib/io/bytes_builder.dart
index 3962037..9113d44 100644
--- a/sdk/lib/io/bytes_builder.dart
+++ b/sdk/lib/io/bytes_builder.dart
@@ -48,14 +48,14 @@
    * The list returned is a view of the internal buffer, limited to the
    * [length].
    */
-  List<int> takeBytes();
+  Uint8List takeBytes();
 
   /**
    * Returns a copy of the current contents of the builder.
    *
    * Leaves the contents of the builder intact.
    */
-  List<int> toBytes();
+  Uint8List toBytes();
 
   /**
    * The number of bytes in the builder.
@@ -82,6 +82,7 @@
   // Start with 1024 bytes.
   static const int _initSize = 1024;
 
+  // Safe for reuse because a fixed-length empty list is immutable.
   static final _emptyList = new Uint8List(0);
 
   int _length = 0;
@@ -135,14 +136,14 @@
     _buffer = newBuffer;
   }
 
-  List<int> takeBytes() {
+  Uint8List takeBytes() {
     if (_length == 0) return _emptyList;
     var buffer = new Uint8List.view(_buffer.buffer, 0, _length);
     clear();
     return buffer;
   }
 
-  List<int> toBytes() {
+  Uint8List toBytes() {
     if (_length == 0) return _emptyList;
     return new Uint8List.fromList(
         new Uint8List.view(_buffer.buffer, 0, _length));
@@ -191,7 +192,7 @@
     _length++;
   }
 
-  List<int> takeBytes() {
+  Uint8List takeBytes() {
     if (_length == 0) return _CopyingBytesBuilder._emptyList;
     if (_chunks.length == 1) {
       var buffer = _chunks[0];
@@ -208,7 +209,7 @@
     return buffer;
   }
 
-  List<int> toBytes() {
+  Uint8List toBytes() {
     if (_length == 0) return _CopyingBytesBuilder._emptyList;
     var buffer = new Uint8List(_length);
     int offset = 0;
diff --git a/sdk/lib/io/file.dart b/sdk/lib/io/file.dart
index eb3db4c..861ce47 100644
--- a/sdk/lib/io/file.dart
+++ b/sdk/lib/io/file.dart
@@ -505,17 +505,17 @@
 
   /**
    * Read the entire file contents as a list of bytes. Returns a
-   * `Future<List<int>>` that completes with the list of bytes that
+   * `Future<Uint8List>` that completes with the list of bytes that
    * is the contents of the file.
    */
-  Future<List<int>> readAsBytes();
+  Future<Uint8List> readAsBytes();
 
   /**
    * Synchronously read the entire file contents as a list of bytes.
    *
    * Throws a [FileSystemException] if the operation fails.
    */
-  List<int> readAsBytesSync();
+  Uint8List readAsBytesSync();
 
   /**
    * Read the entire file contents as a string using the given
@@ -682,7 +682,7 @@
   /**
    * Reads [bytes] bytes from a file and returns the result as a list of bytes.
    */
-  Future<List<int>> read(int bytes);
+  Future<Uint8List> read(int bytes);
 
   /**
    * Synchronously reads a maximum of [bytes] bytes from a file and
@@ -690,7 +690,7 @@
    *
    * Throws a [FileSystemException] if the operation fails.
    */
-  List<int> readSync(int bytes);
+  Uint8List readSync(int bytes);
 
   /**
    * Reads into an existing [List<int>] from the file. If [start] is present,
diff --git a/sdk/lib/io/file_impl.dart b/sdk/lib/io/file_impl.dart
index 33c1352..68d8b1b 100644
--- a/sdk/lib/io/file_impl.dart
+++ b/sdk/lib/io/file_impl.dart
@@ -513,10 +513,10 @@
     return new IOSink(consumer, encoding: encoding);
   }
 
-  Future<List<int>> readAsBytes() {
-    Future<List<int>> readDataChunked(RandomAccessFile file) {
+  Future<Uint8List> readAsBytes() {
+    Future<Uint8List> readDataChunked(RandomAccessFile file) {
       var builder = new BytesBuilder(copy: false);
-      var completer = new Completer<List<int>>();
+      var completer = new Completer<Uint8List>();
       void read() {
         file.read(_blockSize).then((data) {
           if (data.length > 0) {
@@ -543,10 +543,10 @@
     });
   }
 
-  List<int> readAsBytesSync() {
+  Uint8List readAsBytesSync() {
     var opened = openSync();
     try {
-      List<int> data;
+      Uint8List data;
       var length = opened.lengthSync();
       if (length == 0) {
         // May be character device, try to read it in chunks.
@@ -741,19 +741,19 @@
     return result;
   }
 
-  Future<List<int>> read(int bytes) {
+  Future<Uint8List> read(int bytes) {
     ArgumentError.checkNotNull(bytes, 'bytes');
     return _dispatch(_IOService.fileRead, [null, bytes]).then((response) {
       if (_isErrorResponse(response)) {
         throw _exceptionFromResponse(response, "read failed", path);
       }
       _resourceInfo.addRead(response[1].length);
-      List<int> result = response[1];
+      Uint8List result = response[1];
       return result;
     });
   }
 
-  List<int> readSync(int bytes) {
+  Uint8List readSync(int bytes) {
     _checkAvailable();
     ArgumentError.checkNotNull(bytes, 'bytes');
     var result = _ops.read(bytes);
diff --git a/sdk/lib/io/file_system_entity.dart b/sdk/lib/io/file_system_entity.dart
index 17b05ce..16e99e6 100644
--- a/sdk/lib/io/file_system_entity.dart
+++ b/sdk/lib/io/file_system_entity.dart
@@ -81,9 +81,9 @@
   final DateTime accessed;
 
   /**
-   * The type of the object (file, directory, or link).
+   * The type of the underlying file system object.
    *
-   * If the call to stat() fails, the type of the returned object is notFound.
+   * [FileSystemEntityType.notFound] if [stat] or [statSync] failed.
    */
   final FileSystemEntityType type;
 
@@ -114,11 +114,11 @@
   external static _statSync(_Namespace namespace, String path);
 
   /**
-   * Calls the operating system's stat() function on [path].
+   * Calls the operating system's `stat()` function (or equivalent) on [path].
    *
-   * Returns a [FileStat] object containing the data returned by stat().
-   * If the call fails, returns a [FileStat] object with .type set to
-   * FileSystemEntityType.notFound and the other fields invalid.
+   * Returns a [FileStat] object containing the data returned by `stat()`.
+   * If the call fails, returns a [FileStat] object with [FileStat.type] set to
+   * [FileSystemEntityType.notFound] and the other fields invalid.
    */
   static FileStat statSync(String path) {
     final IOOverrides overrides = IOOverrides.current;
@@ -145,12 +145,10 @@
   }
 
   /**
-   * Asynchronously calls the operating system's stat() function on [path].
+   * Asynchronously calls the operating system's `stat()` function (or
+   * equivalent) on [path].
    *
-   * Returns a Future which completes with a [FileStat] object containing
-   * the data returned by stat(). If the call fails, completes the future with a
-   * [FileStat] object with `.type` set to FileSystemEntityType.notFound and
-   * the other fields invalid.
+   * Returns a [Future] which completes with the same results as [statSync].
    */
   static Future<FileStat> stat(String path) {
     final IOOverrides overrides = IOOverrides.current;
@@ -408,8 +406,8 @@
    * stat().
    *
    * If the call fails, completes the future with a [FileStat] object
-   * with .type set to
-   * FileSystemEntityType.notFound and the other fields invalid.
+   * with `.type` set to [FileSystemEntityType.notFound] and the other fields
+   * invalid.
    */
   Future<FileStat> stat() => FileStat.stat(path);
 
@@ -421,8 +419,8 @@
    *
    * Returns a [FileStat] object containing the data returned by stat().
    *
-   * If the call fails, returns a [FileStat] object with .type set to
-   * FileSystemEntityType.notFound and the other fields invalid.
+   * If the call fails, returns a [FileStat] object with `.type` set to
+   * [FileSystemEntityType.notFound] and the other fields invalid.
    */
   FileStat statSync() => FileStat.statSync(path);
 
@@ -670,15 +668,8 @@
   /**
    * Finds the type of file system object that a path points to.
    *
-   * Returns a [:Future<FileSystemEntityType>:] that completes with the result.
-   *
-   * [FileSystemEntityType] has the constant instances file, directory,
-   * link, and notFound.  [type] will return link only if the optional
-   * named argument [followLinks] is false, and [path] points to a link.
-   * If the path does not point to a file system object, or any other error
-   * occurs in looking up the path, notFound is returned.  The only
-   * error or exception that may be put on the returned future is ArgumentError,
-   * caused by passing the wrong type of arguments to the function.
+   * Returns a [:Future<FileSystemEntityType>:] that completes with the same
+   * results as [typeSync].
    */
   static Future<FileSystemEntityType> type(String path,
       {bool followLinks: true}) {
@@ -690,13 +681,11 @@
    *
    * Returns a [FileSystemEntityType].
    *
-   * [FileSystemEntityType] has the constant instances file, directory,
-   * link, and notFound.  [type] will return link only if the optional
-   * named argument [followLinks] is false, and [path] points to a link.
-   * If the path does not point to a file system object, or any other error
-   * occurs in looking up the path, notFound is returned.  The only
-   * error or exception that may be thrown is ArgumentError,
-   * caused by passing the wrong type of arguments to the function.
+   * Returns [FileSystemEntityType.link] only if [followLinks] is false and if
+   * [path] points to a link.
+   *
+   * Returns [FileSystemEntityType.notFound] if [path] does not point to a file
+   * system object or if any other error occurs in looking up the path.
    */
   static FileSystemEntityType typeSync(String path, {bool followLinks: true}) {
     return _getTypeSync(_toUtf8Array(path), followLinks);
diff --git a/sdk/lib/io/secure_socket.dart b/sdk/lib/io/secure_socket.dart
index 463a445..c37f75e 100644
--- a/sdk/lib/io/secure_socket.dart
+++ b/sdk/lib/io/secure_socket.dart
@@ -676,7 +676,7 @@
     _scheduleReadEvent();
   }
 
-  List<int> read([int length]) {
+  Uint8List read([int length]) {
     if (length != null && (length is! int || length < 0)) {
       throw new ArgumentError(
           "Invalid length parameter in SecureSocket.read (length: $length)");
@@ -1157,14 +1157,14 @@
     return size - end;
   }
 
-  List<int> read(int bytes) {
+  Uint8List read(int bytes) {
     if (bytes == null) {
       bytes = length;
     } else {
       bytes = min(bytes, length);
     }
     if (bytes == 0) return null;
-    List<int> result = new Uint8List(bytes);
+    Uint8List result = new Uint8List(bytes);
     int bytesRead = 0;
     // Loop over zero, one, or two linear data ranges.
     while (bytesRead < bytes) {
diff --git a/sdk/lib/io/socket.dart b/sdk/lib/io/socket.dart
index fdf6bbd..63b32b3 100644
--- a/sdk/lib/io/socket.dart
+++ b/sdk/lib/io/socket.dart
@@ -115,7 +115,7 @@
    * 4 or 16 byte long list. The returned list is a copy, making it possible
    * to change the list without modifying the [InternetAddress].
    */
-  List<int> get rawAddress;
+  Uint8List get rawAddress;
 
   /**
    * Returns true if the [InternetAddress] is a loopback address.
@@ -624,7 +624,7 @@
    * available for immediate reading. If no data is available [:null:]
    * is returned.
    */
-  List<int> read([int len]);
+  Uint8List read([int len]);
 
   /**
    * Writes up to [count] bytes of the buffer from [offset] buffer offset to
diff --git a/sdk/lib/libraries.json b/sdk/lib/libraries.json
index 4a0f788..30197aa2 100644
--- a/sdk/lib/libraries.json
+++ b/sdk/lib/libraries.json
@@ -205,6 +205,9 @@
       "html_common": {
         "uri": "html/html_common/html_common_dart2js.dart"
       },
+      "_recipe_syntax": {
+        "uri": "_internal/js_runtime/lib/shared/recipe_syntax.dart"
+      },
       "_native_typed_data": {
         "uri": "_internal/js_runtime/lib/native_typed_data.dart"
       },
@@ -386,48 +389,18 @@
   },
   "dart2js_server": {
     "libraries": {
-      "_native_typed_data": {
-        "uri": "_internal/js_runtime/lib/native_typed_data.dart"
-      },
-      "_js_names": {
-        "uri": "_internal/js_runtime/lib/js_names.dart"
-      },
-      "core": {
-        "patches": "_internal/js_runtime/lib/core_patch.dart",
-        "uri": "core/core.dart"
-      },
       "async": {
         "patches": "_internal/js_runtime/lib/async_patch.dart",
         "uri": "async/async.dart"
       },
-      "collection": {
-        "patches": "_internal/js_runtime/lib/collection_patch.dart",
-        "uri": "collection/collection.dart"
-      },
-      "js_util": {
-        "uri": "js_util/dart2js/js_util_dart2js.dart"
-      },
-      "typed_data": {
-        "patches": "_internal/js_runtime/lib/typed_data_patch.dart",
-        "uri": "typed_data/typed_data.dart"
-      },
-      "_interceptors": {
-        "uri": "_internal/js_runtime/lib/interceptors.dart"
-      },
-      "developer": {
-        "patches": "_internal/js_runtime/lib/developer_patch.dart",
-        "uri": "developer/developer.dart"
-      },
-      "isolate": {
-        "patches": "_internal/js_runtime/lib/isolate_patch.dart",
-        "supported": false,
-        "uri": "isolate/isolate.dart"
-      },
       "mirrors": {
         "patches": "_internal/js_runtime/lib/mirrors_patch_cfe.dart",
         "supported": false,
         "uri": "mirrors/mirrors.dart"
       },
+      "_interceptors": {
+        "uri": "_internal/js_runtime/lib/interceptors.dart"
+      },
       "_js_embedded_names": {
         "uri": "_internal/js_runtime/lib/shared/embedded_names.dart"
       },
@@ -440,13 +413,58 @@
         "patches": "_internal/js_runtime/lib/internal_patch.dart",
         "uri": "internal/internal.dart"
       },
+      "_async_await_error_codes": {
+        "uri": "_internal/js_runtime/lib/shared/async_await_error_codes.dart"
+      },
+      "_http": {
+        "uri": "_http/http.dart"
+      },
+      "_js_helper": {
+        "uri": "_internal/js_runtime/lib/js_helper.dart"
+      },
+      "_js_primitives": {
+        "uri": "_internal/js_runtime/lib/js_primitives.dart"
+      },
+      "js": {
+        "uri": "js/dart2js/js_dart2js.dart"
+      },
+      "_recipe_syntax": {
+        "uri": "_internal/js_runtime/lib/shared/recipe_syntax.dart"
+      },
+      "_native_typed_data": {
+        "uri": "_internal/js_runtime/lib/native_typed_data.dart"
+      },
+      "core": {
+        "patches": "_internal/js_runtime/lib/core_patch.dart",
+        "uri": "core/core.dart"
+      },
+      "_js_names": {
+        "uri": "_internal/js_runtime/lib/js_names.dart"
+      },
+      "js_util": {
+        "uri": "js_util/dart2js/js_util_dart2js.dart"
+      },
+      "collection": {
+        "patches": "_internal/js_runtime/lib/collection_patch.dart",
+        "uri": "collection/collection.dart"
+      },
+      "typed_data": {
+        "patches": "_internal/js_runtime/lib/typed_data_patch.dart",
+        "uri": "typed_data/typed_data.dart"
+      },
+      "isolate": {
+        "patches": "_internal/js_runtime/lib/isolate_patch.dart",
+        "supported": false,
+        "uri": "isolate/isolate.dart"
+      },
+      "developer": {
+        "patches": "_internal/js_runtime/lib/developer_patch.dart",
+        "uri": "developer/developer.dart"
+      },
       "_js": {
         "patches": "js/_js_server.dart",
         "uri": "js/_js.dart"
       },
-      "_async_await_error_codes": {
-        "uri": "_internal/js_runtime/lib/shared/async_await_error_codes.dart"
-      },
       "convert": {
         "patches": "_internal/js_runtime/lib/convert_patch.dart",
         "uri": "convert/convert.dart"
@@ -458,21 +476,9 @@
       "_foreign_helper": {
         "uri": "_internal/js_runtime/lib/foreign_helper.dart"
       },
-      "_http": {
-        "uri": "_http/http.dart"
-      },
-      "_js_primitives": {
-        "uri": "_internal/js_runtime/lib/js_primitives.dart"
-      },
-      "_js_helper": {
-        "uri": "_internal/js_runtime/lib/js_helper.dart"
-      },
       "_rti": {
         "uri": "_internal/js_runtime/lib/rti.dart"
-      },
-      "js": {
-        "uri": "js/dart2js/js_dart2js.dart"
       }
     }
   }
-}
+}
\ No newline at end of file
diff --git a/sdk/lib/libraries.yaml b/sdk/lib/libraries.yaml
index 7a5520c..8eea310 100644
--- a/sdk/lib/libraries.yaml
+++ b/sdk/lib/libraries.yaml
@@ -266,6 +266,9 @@
     _async_await_error_codes:
       uri: "_internal/js_runtime/lib/shared/async_await_error_codes.dart"
 
+    _recipe_syntax:
+      uri: "_internal/js_runtime/lib/shared/recipe_syntax.dart"
+
     _metadata:
       uri: "html/html_common/metadata.dart"
 
@@ -358,6 +361,9 @@
     _async_await_error_codes:
       uri: "_internal/js_runtime/lib/shared/async_await_error_codes.dart"
 
+    _recipe_syntax:
+      uri: "_internal/js_runtime/lib/shared/recipe_syntax.dart"
+
 dartdevc:
     libraries:
       _runtime:
diff --git a/sdk/lib/typed_data/typed_data.dart b/sdk/lib/typed_data/typed_data.dart
index 62e7ee8..89c9496 100644
--- a/sdk/lib/typed_data/typed_data.dart
+++ b/sdk/lib/typed_data/typed_data.dart
@@ -772,6 +772,31 @@
     return buffer.asInt8List(offsetInBytes, length);
   }
 
+  /**
+   * Returns a new list containing the elements between [start] and [end].
+   *
+   * The new list is an `Int8List` containing the elements of this list at
+   * positions greater than or equal to [start] and less than [end] in the same
+   * order as they occur in this list.
+   *
+   * ```dart
+   * var numbers = Int8List.fromList([0, 1, 2, 3, 4]);
+   * print(numbers.sublist(1, 3)); // [1, 2]
+   * print(numbers.sublist(1, 3).runtimeType); // Int8List
+   * ```
+   *
+   * If [end] is omitted, it defaults to the [length] of this list.
+   *
+   * ```dart
+   * print(numbers.sublist(1)); // [1, 2, 3, 4]
+   * ```
+   *
+   * The `start` and `end` positions must satisfy the relations
+   * 0 ≤ `start` ≤ `end` ≤ `this.length`
+   * If `end` is equal to `start`, then the returned list is empty.
+   */
+  Int8List sublist(int start, [int end]);
+
   static const int bytesPerElement = 1;
 }
 
@@ -829,6 +854,31 @@
    */
   List<int> operator +(List<int> other);
 
+  /**
+   * Returns a new list containing the elements between [start] and [end].
+   *
+   * The new list is a `Uint8List` containing the elements of this list at
+   * positions greater than or equal to [start] and less than [end] in the same
+   * order as they occur in this list.
+   *
+   * ```dart
+   * var numbers = Uint8List.fromList([0, 1, 2, 3, 4]);
+   * print(numbers.sublist(1, 3)); // [1, 2]
+   * print(numbers.sublist(1, 3).runtimeType); // Uint8List
+   * ```
+   *
+   * If [end] is omitted, it defaults to the [length] of this list.
+   *
+   * ```dart
+   * print(numbers.sublist(1)); // [1, 2, 3, 4]
+   * ```
+   *
+   * The `start` and `end` positions must satisfy the relations
+   * 0 ≤ `start` ≤ `end` ≤ `this.length`
+   * If `end` is equal to `start`, then the returned list is empty.
+   */
+  Uint8List sublist(int start, [int end]);
+
   static const int bytesPerElement = 1;
 }
 
@@ -878,6 +928,31 @@
     return buffer.asUint8ClampedList(offsetInBytes, length);
   }
 
+  /**
+   * Returns a new list containing the elements between [start] and [end].
+   *
+   * The new list is a `Uint8ClampedList` containing the elements of this
+   * list at positions greater than or equal to [start] and less than [end] in
+   * the same order as they occur in this list.
+   *
+   * ```dart
+   * var numbers = Uint8ClampedList.fromList([0, 1, 2, 3, 4]);
+   * print(numbers.sublist(1, 3)); // [1, 2]
+   * print(numbers.sublist(1, 3).runtimeType); // Uint8ClampedList
+   * ```
+   *
+   * If [end] is omitted, it defaults to the [length] of this list.
+   *
+   * ```dart
+   * print(numbers.sublist(1)); // [1, 2, 3, 4]
+   * ```
+   *
+   * The `start` and `end` positions must satisfy the relations
+   * 0 ≤ `start` ≤ `end` ≤ `this.length`
+   * If `end` is equal to `start`, then the returned list is empty.
+   */
+  Uint8ClampedList sublist(int start, [int end]);
+
   static const int bytesPerElement = 1;
 }
 
@@ -930,6 +1005,31 @@
     return buffer.asInt16List(offsetInBytes, length);
   }
 
+  /**
+   * Returns a new list containing the elements between [start] and [end].
+   *
+   * The new list is an `Int16List` containing the elements of this
+   * list at positions greater than or equal to [start] and less than [end] in
+   * the same order as they occur in this list.
+   *
+   * ```dart
+   * var numbers = Int16List.fromList([0, 1, 2, 3, 4]);
+   * print(numbers.sublist(1, 3)); // [1, 2]
+   * print(numbers.sublist(1, 3).runtimeType); // Int16List
+   * ```
+   *
+   * If [end] is omitted, it defaults to the [length] of this list.
+   *
+   * ```dart
+   * print(numbers.sublist(1)); // [1, 2, 3, 4]
+   * ```
+   *
+   * The `start` and `end` positions must satisfy the relations
+   * 0 ≤ `start` ≤ `end` ≤ `this.length`
+   * If `end` is equal to `start`, then the returned list is empty.
+   */
+  Int16List sublist(int start, [int end]);
+
   static const int bytesPerElement = 2;
 }
 
@@ -983,6 +1083,31 @@
     return buffer.asUint16List(offsetInBytes, length);
   }
 
+  /**
+   * Returns a new list containing the elements between [start] and [end].
+   *
+   * The new list is a `Uint16List` containing the elements of this
+   * list at positions greater than or equal to [start] and less than [end] in
+   * the same order as they occur in this list.
+   *
+   * ```dart
+   * var numbers = Uint16List.fromList([0, 1, 2, 3, 4]);
+   * print(numbers.sublist(1, 3)); // [1, 2]
+   * print(numbers.sublist(1, 3).runtimeType); // Uint16List
+   * ```
+   *
+   * If [end] is omitted, it defaults to the [length] of this list.
+   *
+   * ```dart
+   * print(numbers.sublist(1)); // [1, 2, 3, 4]
+   * ```
+   *
+   * The `start` and `end` positions must satisfy the relations
+   * 0 ≤ `start` ≤ `end` ≤ `this.length`
+   * If `end` is equal to `start`, then the returned list is empty.
+   */
+  Uint16List sublist(int start, [int end]);
+
   static const int bytesPerElement = 2;
 }
 
@@ -1035,6 +1160,31 @@
     return buffer.asInt32List(offsetInBytes, length);
   }
 
+  /**
+   * Returns a new list containing the elements between [start] and [end].
+   *
+   * The new list is an `Int32List` containing the elements of this
+   * list at positions greater than or equal to [start] and less than [end] in
+   * the same order as they occur in this list.
+   *
+   * ```dart
+   * var numbers = Int32List.fromList([0, 1, 2, 3, 4]);
+   * print(numbers.sublist(1, 3)); // [1, 2]
+   * print(numbers.sublist(1, 3).runtimeType); // Int32List
+   * ```
+   *
+   * If [end] is omitted, it defaults to the [length] of this list.
+   *
+   * ```dart
+   * print(numbers.sublist(1)); // [1, 2, 3, 4]
+   * ```
+   *
+   * The `start` and `end` positions must satisfy the relations
+   * 0 ≤ `start` ≤ `end` ≤ `this.length`
+   * If `end` is equal to `start`, then the returned list is empty.
+   */
+  Int32List sublist(int start, [int end]);
+
   static const int bytesPerElement = 4;
 }
 
@@ -1088,6 +1238,31 @@
     return buffer.asUint32List(offsetInBytes, length);
   }
 
+  /**
+   * Returns a new list containing the elements between [start] and [end].
+   *
+   * The new list is a `Uint32List` containing the elements of this
+   * list at positions greater than or equal to [start] and less than [end] in
+   * the same order as they occur in this list.
+   *
+   * ```dart
+   * var numbers = Uint32List.fromList([0, 1, 2, 3, 4]);
+   * print(numbers.sublist(1, 3)); // [1, 2]
+   * print(numbers.sublist(1, 3).runtimeType); // Uint32List
+   * ```
+   *
+   * If [end] is omitted, it defaults to the [length] of this list.
+   *
+   * ```dart
+   * print(numbers.sublist(1)); // [1, 2, 3, 4]
+   * ```
+   *
+   * The `start` and `end` positions must satisfy the relations
+   * 0 ≤ `start` ≤ `end` ≤ `this.length`
+   * If `end` is equal to `start`, then the returned list is empty.
+   */
+  Uint32List sublist(int start, [int end]);
+
   static const int bytesPerElement = 4;
 }
 
@@ -1140,6 +1315,31 @@
     return buffer.asInt64List(offsetInBytes, length);
   }
 
+  /**
+   * Returns a new list containing the elements between [start] and [end].
+   *
+   * The new list is an `Int64List` containing the elements of this
+   * list at positions greater than or equal to [start] and less than [end] in
+   * the same order as they occur in this list.
+   *
+   * ```dart
+   * var numbers = Int64List.fromList([0, 1, 2, 3, 4]);
+   * print(numbers.sublist(1, 3)); // [1, 2]
+   * print(numbers.sublist(1, 3).runtimeType); // Int64List
+   * ```
+   *
+   * If [end] is omitted, it defaults to the [length] of this list.
+   *
+   * ```dart
+   * print(numbers.sublist(1)); // [1, 2, 3, 4]
+   * ```
+   *
+   * The `start` and `end` positions must satisfy the relations
+   * 0 ≤ `start` ≤ `end` ≤ `this.length`
+   * If `end` is equal to `start`, then the returned list is empty.
+   */
+  Int64List sublist(int start, [int end]);
+
   static const int bytesPerElement = 8;
 }
 
@@ -1193,6 +1393,31 @@
     return buffer.asUint64List(offsetInBytes, length);
   }
 
+  /**
+   * Returns a new list containing the elements between [start] and [end].
+   *
+   * The new list is a `Uint64List` containing the elements of this
+   * list at positions greater than or equal to [start] and less than [end] in
+   * the same order as they occur in this list.
+   *
+   * ```dart
+   * var numbers = Uint64List.fromList([0, 1, 2, 3, 4]);
+   * print(numbers.sublist(1, 3)); // [1, 2]
+   * print(numbers.sublist(1, 3).runtimeType); // Uint64List
+   * ```
+   *
+   * If [end] is omitted, it defaults to the [length] of this list.
+   *
+   * ```dart
+   * print(numbers.sublist(1)); // [1, 2, 3, 4]
+   * ```
+   *
+   * The `start` and `end` positions must satisfy the relations
+   * 0 ≤ `start` ≤ `end` ≤ `this.length`
+   * If `end` is equal to `start`, then the returned list is empty.
+   */
+  Uint64List sublist(int start, [int end]);
+
   static const int bytesPerElement = 8;
 }
 
@@ -1246,6 +1471,31 @@
     return buffer.asFloat32List(offsetInBytes, length);
   }
 
+  /**
+   * Returns a new list containing the elements between [start] and [end].
+   *
+   * The new list is a `Float32List` containing the elements of this
+   * list at positions greater than or equal to [start] and less than [end] in
+   * the same order as they occur in this list.
+   *
+   * ```dart
+   * var numbers = Float32List.fromList([0, 1, 2, 3, 4]);
+   * print(numbers.sublist(1, 3)); // [1, 2]
+   * print(numbers.sublist(1, 3).runtimeType); // Float32List
+   * ```
+   *
+   * If [end] is omitted, it defaults to the [length] of this list.
+   *
+   * ```dart
+   * print(numbers.sublist(1)); // [1, 2, 3, 4]
+   * ```
+   *
+   * The `start` and `end` positions must satisfy the relations
+   * 0 ≤ `start` ≤ `end` ≤ `this.length`
+   * If `end` is equal to `start`, then the returned list is empty.
+   */
+  Float32List sublist(int start, [int end]);
+
   static const int bytesPerElement = 4;
 }
 
@@ -1292,6 +1542,31 @@
     return buffer.asFloat64List(offsetInBytes, length);
   }
 
+  /**
+   * Returns a new list containing the elements between [start] and [end].
+   *
+   * The new list is a `Float64List` containing the elements of this
+   * list at positions greater than or equal to [start] and less than [end] in
+   * the same order as they occur in this list.
+   *
+   * ```dart
+   * var numbers = Float64List.fromList([0, 1, 2, 3, 4]);
+   * print(numbers.sublist(1, 3)); // [1, 2]
+   * print(numbers.sublist(1, 3).runtimeType); // Float64List
+   * ```
+   *
+   * If [end] is omitted, it defaults to the [length] of this list.
+   *
+   * ```dart
+   * print(numbers.sublist(1)); // [1, 2, 3, 4]
+   * ```
+   *
+   * The `start` and `end` positions must satisfy the relations
+   * 0 ≤ `start` ≤ `end` ≤ `this.length`
+   * If `end` is equal to `start`, then the returned list is empty.
+   */
+  Float64List sublist(int start, [int end]);
+
   static const int bytesPerElement = 8;
 }
 
@@ -1345,6 +1620,31 @@
    */
   List<Float32x4> operator +(List<Float32x4> other);
 
+  /**
+   * Returns a new list containing the elements between [start] and [end].
+   *
+   * The new list is a `Float32x4List` containing the elements of this
+   * list at positions greater than or equal to [start] and less than [end] in
+   * the same order as they occur in this list.
+   *
+   * ```dart
+   * var numbers = Float32x4List.fromList([0, 1, 2, 3, 4]);
+   * print(numbers.sublist(1, 3)); // [1, 2]
+   * print(numbers.sublist(1, 3).runtimeType); // Float32x4List
+   * ```
+   *
+   * If [end] is omitted, it defaults to the [length] of this list.
+   *
+   * ```dart
+   * print(numbers.sublist(1)); // [1, 2, 3, 4]
+   * ```
+   *
+   * The `start` and `end` positions must satisfy the relations
+   * 0 ≤ `start` ≤ `end` ≤ `this.length`
+   * If `end` is equal to `start`, then the returned list is empty.
+   */
+  Float32x4List sublist(int start, [int end]);
+
   static const int bytesPerElement = 16;
 }
 
@@ -1398,6 +1698,31 @@
    */
   List<Int32x4> operator +(List<Int32x4> other);
 
+  /**
+   * Returns a new list containing the elements between [start] and [end].
+   *
+   * The new list is an `Int32x4list` containing the elements of this
+   * list at positions greater than or equal to [start] and less than [end] in
+   * the same order as they occur in this list.
+   *
+   * ```dart
+   * var numbers = Int32x4list.fromList([0, 1, 2, 3, 4]);
+   * print(numbers.sublist(1, 3)); // [1, 2]
+   * print(numbers.sublist(1, 3).runtimeType); // Int32x4list
+   * ```
+   *
+   * If [end] is omitted, it defaults to the [length] of this list.
+   *
+   * ```dart
+   * print(numbers.sublist(1)); // [1, 2, 3, 4]
+   * ```
+   *
+   * The `start` and `end` positions must satisfy the relations
+   * 0 ≤ `start` ≤ `end` ≤ `this.length`
+   * If `end` is equal to `start`, then the returned list is empty.
+   */
+  Int32x4List sublist(int start, [int end]);
+
   static const int bytesPerElement = 16;
 }
 
@@ -1451,6 +1776,31 @@
     return buffer.asFloat64x2List(offsetInBytes, length);
   }
 
+  /**
+   * Returns a new list containing the elements between [start] and [end].
+   *
+   * The new list is a `Float64x2List` containing the elements of this
+   * list at positions greater than or equal to [start] and less than [end] in
+   * the same order as they occur in this list.
+   *
+   * ```dart
+   * var numbers = Float64x2List.fromList([0, 1, 2, 3, 4]);
+   * print(numbers.sublist(1, 3)); // [1, 2]
+   * print(numbers.sublist(1, 3).runtimeType); // Float64x2List
+   * ```
+   *
+   * If [end] is omitted, it defaults to the [length] of this list.
+   *
+   * ```dart
+   * print(numbers.sublist(1)); // [1, 2, 3, 4]
+   * ```
+   *
+   * The `start` and `end` positions must satisfy the relations
+   * 0 ≤ `start` ≤ `end` ≤ `this.length`
+   * If `end` is equal to `start`, then the returned list is empty.
+   */
+  Float64x2List sublist(int start, [int end]);
+
   static const int bytesPerElement = 16;
 }
 
diff --git a/sdk/lib/typed_data/unmodifiable_typed_data.dart b/sdk/lib/typed_data/unmodifiable_typed_data.dart
index 4cc5af7..3549041 100644
--- a/sdk/lib/typed_data/unmodifiable_typed_data.dart
+++ b/sdk/lib/typed_data/unmodifiable_typed_data.dart
@@ -160,6 +160,16 @@
   int get lengthInBytes => _data.lengthInBytes;
 
   ByteBuffer get buffer => new UnmodifiableByteBufferView(_data.buffer);
+
+  L _createList(int length);
+
+  L sublist(int start, [int end]) {
+    end = RangeError.checkValidRange(start, end, length);
+    int sublistLength = end - start;
+    L result = _createList(sublistLength);
+    result.setRange(0, sublistLength, _list, start);
+    return result;
+  }
 }
 
 /**
@@ -170,6 +180,8 @@
     implements Uint8List {
   final Uint8List _list;
   UnmodifiableUint8ListView(Uint8List list) : _list = list;
+
+  Uint8List _createList(int length) => Uint8List(length);
 }
 
 /**
@@ -180,6 +192,8 @@
     implements Int8List {
   final Int8List _list;
   UnmodifiableInt8ListView(Int8List list) : _list = list;
+
+  Int8List _createList(int length) => Int8List(length);
 }
 
 /**
@@ -190,6 +204,8 @@
     implements Uint8ClampedList {
   final Uint8ClampedList _list;
   UnmodifiableUint8ClampedListView(Uint8ClampedList list) : _list = list;
+
+  Uint8ClampedList _createList(int length) => Uint8ClampedList(length);
 }
 
 /**
@@ -200,6 +216,8 @@
     implements Uint16List {
   final Uint16List _list;
   UnmodifiableUint16ListView(Uint16List list) : _list = list;
+
+  Uint16List _createList(int length) => Uint16List(length);
 }
 
 /**
@@ -210,6 +228,8 @@
     implements Int16List {
   final Int16List _list;
   UnmodifiableInt16ListView(Int16List list) : _list = list;
+
+  Int16List _createList(int length) => Int16List(length);
 }
 
 /**
@@ -220,6 +240,8 @@
     implements Uint32List {
   final Uint32List _list;
   UnmodifiableUint32ListView(Uint32List list) : _list = list;
+
+  Uint32List _createList(int length) => Uint32List(length);
 }
 
 /**
@@ -230,6 +252,8 @@
     implements Int32List {
   final Int32List _list;
   UnmodifiableInt32ListView(Int32List list) : _list = list;
+
+  Int32List _createList(int length) => Int32List(length);
 }
 
 /**
@@ -240,6 +264,8 @@
     implements Uint64List {
   final Uint64List _list;
   UnmodifiableUint64ListView(Uint64List list) : _list = list;
+
+  Uint64List _createList(int length) => Uint64List(length);
 }
 
 /**
@@ -250,6 +276,8 @@
     implements Int64List {
   final Int64List _list;
   UnmodifiableInt64ListView(Int64List list) : _list = list;
+
+  Int64List _createList(int length) => Int64List(length);
 }
 
 /**
@@ -260,6 +288,8 @@
     implements Int32x4List {
   final Int32x4List _list;
   UnmodifiableInt32x4ListView(Int32x4List list) : _list = list;
+
+  Int32x4List _createList(int length) => Int32x4List(length);
 }
 
 /**
@@ -270,6 +300,8 @@
     implements Float32x4List {
   final Float32x4List _list;
   UnmodifiableFloat32x4ListView(Float32x4List list) : _list = list;
+
+  Float32x4List _createList(int length) => Float32x4List(length);
 }
 
 /**
@@ -280,6 +312,8 @@
     implements Float64x2List {
   final Float64x2List _list;
   UnmodifiableFloat64x2ListView(Float64x2List list) : _list = list;
+
+  Float64x2List _createList(int length) => Float64x2List(length);
 }
 
 /**
@@ -290,6 +324,8 @@
     implements Float32List {
   final Float32List _list;
   UnmodifiableFloat32ListView(Float32List list) : _list = list;
+
+  Float32List _createList(int length) => Float32List(length);
 }
 
 /**
@@ -300,4 +336,6 @@
     implements Float64List {
   final Float64List _list;
   UnmodifiableFloat64ListView(Float64List list) : _list = list;
+
+  Float64List _createList(int length) => Float64List(length);
 }
diff --git a/sdk/lib/vmservice/vmservice.dart b/sdk/lib/vmservice/vmservice.dart
index 4237f28..636feb9 100644
--- a/sdk/lib/vmservice/vmservice.dart
+++ b/sdk/lib/vmservice/vmservice.dart
@@ -69,6 +69,7 @@
 const kServiceAlreadyRegistered = 111;
 const kServiceDisappeared = 112;
 const kExpressionCompilationError = 113;
+const kInvalidTimelineRequest = 114;
 
 // Experimental (used in private rpcs).
 const kFileSystemAlreadyExists = 1001;
@@ -87,6 +88,8 @@
   kServiceAlreadyRegistered: 'Service already registered',
   kServiceDisappeared: 'Service has disappeared',
   kExpressionCompilationError: 'Expression compilation error',
+  kInvalidTimelineRequest: 'The timeline related request could not be completed'
+      'due to the current configuration',
 };
 
 String encodeRpcError(Message message, int code, {String details}) {
diff --git a/tests/README b/tests/README
index bbc3ee4..41f95e6 100644
--- a/tests/README
+++ b/tests/README
@@ -22,7 +22,7 @@
 
 in
 
-  ../tools/testing/dart/test_suite.dart
+  ../pkg/test_runner/lib/src/test_suite.dart
 
 for the default test directory layout. By default test-file names must
 end in "_test.dart", but some test suites, such as ./co19, subclass
@@ -30,13 +30,13 @@
 
 See comments at the beginning of
 
-  ../tools/testing/dart/multitest.dart
+  ../pkg/test_runner/lib/src/multitest.dart
 
 for how to create tests that pass by failing with a known error. For
 example,
 
   ...
-  int x = "not an int"; /// 01: static type warning
+  int x = "not an int"; //# 01: static type warning
   ...
 
 as part of a test will only pass the "--compiler dart2analyzer" test if
diff --git a/tests/compiler/dart2js/analyses/analysis_helper.dart b/tests/compiler/dart2js/analyses/analysis_helper.dart
index 1628c6b..8dd2091 100644
--- a/tests/compiler/dart2js/analyses/analysis_helper.dart
+++ b/tests/compiler/dart2js/analyses/analysis_helper.dart
@@ -19,9 +19,9 @@
 import 'package:compiler/src/kernel/loader.dart';
 import 'package:compiler/src/util/uri_extras.dart';
 import 'package:expect/expect.dart';
-import 'package:front_end/src/api_unstable/dart2js.dart' as ir
-    show RedirectingFactoryBody;
 import 'package:front_end/src/api_prototype/constant_evaluator.dart' as ir;
+import 'package:front_end/src/api_unstable/dart2js.dart'
+    show isRedirectingFactory;
 import 'package:kernel/ast.dart' as ir;
 import 'package:kernel/class_hierarchy.dart' as ir;
 import 'package:kernel/core_types.dart' as ir;
@@ -97,11 +97,9 @@
 
   @override
   Null visitProcedure(ir.Procedure node) {
-    if (node.kind == ir.ProcedureKind.Factory) {
-      if (node.function.body is ir.RedirectingFactoryBody) {
-        // Don't visit redirecting factories.
-        return;
-      }
+    if (node.kind == ir.ProcedureKind.Factory && isRedirectingFactory(node)) {
+      // Don't visit redirecting factories.
+      return;
     }
     if (node.name.name.contains('#')) {
       // Skip synthetic .dill members.
diff --git a/tests/compiler/dart2js/async_await/async_await_js_transform_test.dart b/tests/compiler/dart2js/async_await/async_await_js_transform_test.dart
index d31c169..111e57d 100644
--- a/tests/compiler/dart2js/async_await/async_await_js_transform_test.dart
+++ b/tests/compiler/dart2js/async_await/async_await_js_transform_test.dart
@@ -52,20 +52,17 @@
 }
 
 main() {
-  testAsyncTransform(
-
-          /// 01: ok
-          r"""function() async {
+  testAsyncTransform( //# 01: ok
+      r"""function() async {
   var closures = [new A.main_closure()], v0 = await closures, v1 = 0, v2, v3;
   if (v1 < 0 || v1 >= v0.length)
     H.ioore(v0, v1);
   v2 = 4;
   v3 = 2;
   P.print(v0[v1].call$2(v2, v3));
-}""",
-
-          /// 01: ok
-          r"""function() {
+}"""
+, //# 01: ok
+      r"""function() {
   var __goto = 0,
     __completer = NewCompleter(CompleterType),
     closures, v0, v1, v2, v3;
@@ -93,10 +90,9 @@
       }
   });
   return startHelper(body, __completer);
-}""")
-
-      /// 01: ok
-      ;
+}"""
+    ) //# 01: ok
+  ;
 
   testAsyncTransform("""
 function(a) async {
@@ -439,7 +435,7 @@
   while (true) {
     switch(y) { // Switch with no awaits in case key expressions
       case 0:
-      case 1: 
+      case 1:
         await foo();
         continue; // Continue the loop, not the switch
       case 1: // Duplicate case
@@ -594,7 +590,7 @@
       return;
     }
     print(await(foo(i)));
-  } 
+  }
 }
 """, """
 function(g) {
diff --git a/tests/compiler/dart2js/codegen/is_function_test.dart b/tests/compiler/dart2js/codegen/is_function_test.dart
index 9965040..73b2d2f 100644
--- a/tests/compiler/dart2js/codegen/is_function_test.dart
+++ b/tests/compiler/dart2js/codegen/is_function_test.dart
@@ -9,6 +9,7 @@
 import 'package:compiler/src/commandline_options.dart';
 import 'package:compiler/src/compiler.dart';
 import 'package:compiler/src/js_emitter/model.dart';
+import 'package:compiler/src/js_model/js_strategy.dart';
 import 'package:expect/expect.dart';
 import '../helpers/memory_compiler.dart';
 
@@ -24,8 +25,10 @@
         memorySourceFiles: {'main.dart': SOURCE}, options: options);
     Expect.isTrue(result.isSuccess);
     Compiler compiler = result.compiler;
-    Program program = compiler.backend.emitterTask.emitter.programForTesting;
-    var name = compiler.backend.namerForTesting.operatorIs(
+    Program program =
+        compiler.backendStrategy.emitterTask.emitter.programForTesting;
+    JsBackendStrategy backendStrategy = compiler.backendStrategy;
+    var name = backendStrategy.namerForTesting.operatorIs(
         compiler.backendClosedWorldForTesting.commonElements.functionClass);
     for (Fragment fragment in program.fragments) {
       for (Library library in fragment.libraries) {
diff --git a/tests/compiler/dart2js/codegen/jsarray_indexof_test.dart b/tests/compiler/dart2js/codegen/jsarray_indexof_test.dart
index d5df20f..f24357b 100644
--- a/tests/compiler/dart2js/codegen/jsarray_indexof_test.dart
+++ b/tests/compiler/dart2js/codegen/jsarray_indexof_test.dart
@@ -12,6 +12,7 @@
 import 'package:compiler/src/elements/names.dart';
 import 'package:compiler/src/world.dart';
 import 'package:compiler/src/js_emitter/model.dart';
+import 'package:compiler/src/js_model/js_strategy.dart';
 import 'package:compiler/src/js/js.dart' as js;
 import 'package:compiler/src/universe/selector.dart';
 import 'package:expect/expect.dart';
@@ -44,14 +45,15 @@
       memorySourceFiles: {'main.dart': source}, options: options);
   Expect.isTrue(result.isSuccess);
   Compiler compiler = result.compiler;
+  JsBackendStrategy backendStrategy = compiler.backendStrategy;
   JClosedWorld closedWorld = compiler.backendClosedWorldForTesting;
   MemberEntity jsArrayIndexOf =
       findClassMember(closedWorld, 'JSArray', 'indexOf');
-  ProgramLookup programLookup = new ProgramLookup(result.compiler);
+  ProgramLookup programLookup = new ProgramLookup(backendStrategy);
 
   Selector getLengthSelector = new Selector.getter(const PublicName('length'));
   js.Name getLengthName =
-      compiler.backend.namerForTesting.invocationName(getLengthSelector);
+      backendStrategy.namerForTesting.invocationName(getLengthSelector);
 
   Method method = programLookup.getMethod(jsArrayIndexOf);
   int lengthCount = 0;
diff --git a/tests/compiler/dart2js/codegen/model_test.dart b/tests/compiler/dart2js/codegen/model_test.dart
index 47d0c5c..666ddff 100644
--- a/tests/compiler/dart2js/codegen/model_test.dart
+++ b/tests/compiler/dart2js/codegen/model_test.dart
@@ -84,7 +84,7 @@
       MemberEntity member,
       Compiler compiler,
       this._closureDataLookup)
-      : _programLookup = new ProgramLookup(compiler),
+      : _programLookup = new ProgramLookup(compiler.backendStrategy),
         super(reporter, actualMap);
 
   void registerCalls(Features features, String tag, js.Node node,
diff --git a/tests/compiler/dart2js/codegen/trust_type_annotations2_test.dart b/tests/compiler/dart2js/codegen/trust_type_annotations2_test.dart
index fc46ac1..6cd1e63 100644
--- a/tests/compiler/dart2js/codegen/trust_type_annotations2_test.dart
+++ b/tests/compiler/dart2js/codegen/trust_type_annotations2_test.dart
@@ -29,7 +29,7 @@
     var compiler = result.compiler;
     var element =
         compiler.backendClosedWorldForTesting.elementEnvironment.mainFunction;
-    var code = compiler.backend.getGeneratedCode(element);
+    var code = compiler.backendStrategy.getGeneratedCodeForTesting(element);
     Expect.isTrue(code.contains('+'), code);
   }
 
diff --git a/tests/compiler/dart2js/codegen/type_inference8_test.dart b/tests/compiler/dart2js/codegen/type_inference8_test.dart
index 7cb7040..9150a65 100644
--- a/tests/compiler/dart2js/codegen/type_inference8_test.dart
+++ b/tests/compiler/dart2js/codegen/type_inference8_test.dart
@@ -13,6 +13,7 @@
 import "package:compiler/src/inferrer/abstract_value_domain.dart";
 import "package:compiler/src/inferrer/types.dart";
 import 'package:compiler/src/inferrer/typemasks/masks.dart';
+import 'package:compiler/src/js_model/js_strategy.dart';
 import "package:compiler/src/world.dart";
 import "package:expect/expect.dart";
 import '../helpers/memory_compiler.dart';
@@ -43,6 +44,7 @@
       memorySourceFiles: {'main.dart': TEST1},
       options: [Flags.disableInlining]);
   Compiler compiler = result.compiler;
+  JsBackendStrategy backendStrategy = compiler.backendStrategy;
   GlobalTypeInferenceResults results =
       compiler.globalInference.resultsForTesting;
   JClosedWorld closedWorld = results.closedWorld;
@@ -63,7 +65,7 @@
     AbstractValue barArgMask = results.resultOfParameter(barArg);
     Expect.equals(falseType, barArgMask);
   });
-  String barCode = compiler.backend.getGeneratedCode(bar);
+  String barCode = backendStrategy.getGeneratedCodeForTesting(bar);
   Expect.isTrue(barCode.contains('"bbb"'));
   Expect.isFalse(barCode.contains('"aaa"'));
 }
@@ -93,6 +95,7 @@
       memorySourceFiles: {'main.dart': TEST2},
       options: [Flags.disableInlining]);
   Compiler compiler = result.compiler;
+  JsBackendStrategy backendStrategy = compiler.backendStrategy;
   GlobalTypeInferenceResults results =
       compiler.globalInference.resultsForTesting;
   JClosedWorld closedWorld = results.closedWorld;
@@ -111,7 +114,7 @@
     // The argument to bar should have the same type as the return type of foo
     Expect.identical(commonMasks.boolType, barArgMask);
   });
-  String barCode = compiler.backend.getGeneratedCode(bar);
+  String barCode = backendStrategy.getGeneratedCodeForTesting(bar);
   Expect.isTrue(barCode.contains('"bbb"'));
   // Still must output the print for "aaa"
   Expect.isTrue(barCode.contains('"aaa"'));
diff --git a/tests/compiler/dart2js/codegen/use_checks_test.dart b/tests/compiler/dart2js/codegen/use_checks_test.dart
index d22e26d..6f95ee9 100644
--- a/tests/compiler/dart2js/codegen/use_checks_test.dart
+++ b/tests/compiler/dart2js/codegen/use_checks_test.dart
@@ -31,7 +31,7 @@
     var compiler = result.compiler;
     var element =
         compiler.backendClosedWorldForTesting.elementEnvironment.mainFunction;
-    var code = compiler.backend.getGeneratedCode(element);
+    var code = compiler.backendStrategy.getGeneratedCodeForTesting(element);
     Expect.isTrue(code.contains('+'), code);
   }
 
diff --git a/tests/compiler/dart2js/codegen/value_range3_test.dart b/tests/compiler/dart2js/codegen/value_range3_test.dart
index 62286e7..73491c1 100644
--- a/tests/compiler/dart2js/codegen/value_range3_test.dart
+++ b/tests/compiler/dart2js/codegen/value_range3_test.dart
@@ -28,7 +28,7 @@
     var compiler = result.compiler;
     var element =
         compiler.backendClosedWorldForTesting.elementEnvironment.mainFunction;
-    var code = compiler.backend.getGeneratedCode(element);
+    var code = compiler.backendStrategy.getGeneratedCodeForTesting(element);
     Expect.isFalse(code.contains('ioore'));
   }
 
diff --git a/tests/compiler/dart2js/deferred/constant_emission_test_helper.dart b/tests/compiler/dart2js/deferred/constant_emission_test_helper.dart
index e8a0a7c..c5f20be 100644
--- a/tests/compiler/dart2js/deferred/constant_emission_test_helper.dart
+++ b/tests/compiler/dart2js/deferred/constant_emission_test_helper.dart
@@ -37,7 +37,7 @@
           ? ['${Flags.enableLanguageExperiments}=constant-update-2018']
           : ['${Flags.enableLanguageExperiments}=no-constant-update-2018']);
   Compiler compiler = result.compiler;
-  ProgramLookup lookup = new ProgramLookup(compiler);
+  ProgramLookup lookup = new ProgramLookup(compiler.backendStrategy);
   var closedWorld = compiler.backendClosedWorldForTesting;
   var elementEnvironment = closedWorld.elementEnvironment;
 
diff --git a/tests/compiler/dart2js/deferred/emit_type_checks_test.dart b/tests/compiler/dart2js/deferred/emit_type_checks_test.dart
index 2aea089..b26555b 100644
--- a/tests/compiler/dart2js/deferred/emit_type_checks_test.dart
+++ b/tests/compiler/dart2js/deferred/emit_type_checks_test.dart
@@ -8,7 +8,7 @@
 import 'package:async_helper/async_helper.dart';
 import 'package:compiler/compiler_new.dart';
 import 'package:compiler/src/compiler.dart';
-import 'package:compiler/src/js_backend/js_backend.dart' show JavaScriptBackend;
+import 'package:compiler/src/js_model/js_strategy.dart';
 import 'package:expect/expect.dart';
 import '../helpers/memory_compiler.dart';
 import '../helpers/output_collector.dart';
@@ -21,8 +21,9 @@
     Compiler compiler = result.compiler;
     String mainOutput = collector.getOutput('', OutputType.js);
     String deferredOutput = collector.getOutput('out_1', OutputType.jsPart);
-    JavaScriptBackend backend = compiler.backend;
-    String isPrefix = backend.namerForTesting.fixedNames.operatorIsPrefix;
+    JsBackendStrategy backendStrategy = compiler.backendStrategy;
+    String isPrefix =
+        backendStrategy.namerForTesting.fixedNames.operatorIsPrefix;
     Expect.isTrue(
         deferredOutput.contains('${isPrefix}A: 1'),
         "Deferred output doesn't contain '${isPrefix}A: 1':\n"
diff --git a/tests/compiler/dart2js/deferred/load_graph_segmentation_test.dart b/tests/compiler/dart2js/deferred/load_graph_segmentation_test.dart
index 45bd24b..4d0589c 100644
--- a/tests/compiler/dart2js/deferred/load_graph_segmentation_test.dart
+++ b/tests/compiler/dart2js/deferred/load_graph_segmentation_test.dart
@@ -28,8 +28,8 @@
     var outputUnitForClass = closedWorld.outputUnitData.outputUnitForClass;
 
     var mainOutputUnit = closedWorld.outputUnitData.mainOutputUnit;
-    var backend = compiler.backend;
-    var classes = backend.emitterTask.neededClasses;
+    var backendStrategy = compiler.backendStrategy;
+    var classes = backendStrategy.emitterTask.neededClasses;
     var inputElement = classes.where((e) => e.name == 'InputElement').single;
     dynamic lib1 = lookupLibrary("memory:lib1.dart");
     var foo1 = env.lookupLibraryMember(lib1, "foo1");
diff --git a/tests/compiler/dart2js/end_to_end/exit_code_test.dart b/tests/compiler/dart2js/end_to_end/exit_code_test.dart
index 3397753..969a3fe 100644
--- a/tests/compiler/dart2js/end_to_end/exit_code_test.dart
+++ b/tests/compiler/dart2js/end_to_end/exit_code_test.dart
@@ -10,6 +10,7 @@
 import 'package:expect/expect.dart';
 
 import 'package:compiler/compiler_new.dart' as api;
+import 'package:compiler/src/backend_strategy.dart';
 import 'package:compiler/src/commandline_options.dart';
 import 'package:compiler/src/common/codegen.dart';
 import 'package:compiler/src/common/work.dart';
@@ -19,9 +20,9 @@
 import 'package:compiler/src/diagnostics/invariant.dart';
 import 'package:compiler/src/diagnostics/messages.dart';
 import 'package:compiler/src/diagnostics/spannable.dart';
-import 'package:compiler/src/apiimpl.dart' as apiimpl;
+import 'package:compiler/src/apiimpl.dart';
 import 'package:compiler/src/elements/entities.dart';
-import 'package:compiler/src/js_backend/js_backend.dart';
+import 'package:compiler/src/js_model/js_strategy.dart';
 import 'package:compiler/src/null_compiler_output.dart';
 import 'package:compiler/src/serialization/serialization.dart';
 import 'package:compiler/src/options.dart' show CompilerOptions;
@@ -29,7 +30,7 @@
 import 'package:compiler/src/world.dart';
 import 'diagnostic_reporter_helper.dart';
 
-class TestCompiler extends apiimpl.CompilerImpl {
+class TestCompiler extends CompilerImpl {
   final String testMarker;
   final String testType;
   final Function onTest;
@@ -52,8 +53,8 @@
   }
 
   @override
-  JavaScriptBackend createBackend() {
-    return new TestBackend(this);
+  BackendStrategy createBackendStrategy() {
+    return new TestBackendStrategy(this);
   }
 
   @override
@@ -100,15 +101,12 @@
   }
 }
 
-class TestBackend extends JavaScriptBackend {
-  @override
+class TestBackendStrategy extends JsBackendStrategy {
   final TestCompiler compiler;
-  TestBackend(TestCompiler compiler)
+
+  TestBackendStrategy(TestCompiler compiler)
       : this.compiler = compiler,
-        super(compiler,
-            generateSourceMap: compiler.options.generateSourceMap,
-            useMultiSourceInfo: compiler.options.useMultiSourceInfo,
-            useNewSourceInfo: compiler.options.useNewSourceInfo);
+        super(compiler);
 
   @override
   WorldImpact generateCode(
diff --git a/tests/compiler/dart2js/end_to_end/generate_code_with_compile_time_errors_test.dart b/tests/compiler/dart2js/end_to_end/generate_code_with_compile_time_errors_test.dart
index 6ae5ab8..8521dd5 100644
--- a/tests/compiler/dart2js/end_to_end/generate_code_with_compile_time_errors_test.dart
+++ b/tests/compiler/dart2js/end_to_end/generate_code_with_compile_time_errors_test.dart
@@ -10,7 +10,7 @@
 import 'package:expect/expect.dart';
 import 'package:async_helper/async_helper.dart';
 import 'package:compiler/src/compiler.dart';
-import 'package:compiler/src/js_backend/js_backend.dart';
+import 'package:compiler/src/js_model/js_strategy.dart';
 import '../helpers/memory_compiler.dart';
 import '../helpers/output_collector.dart';
 
@@ -45,8 +45,8 @@
   Expect.equals(expectHint, collector.hints.isNotEmpty,
       "Unexpected hints: ${collector.warnings}");
 
-  JavaScriptBackend backend = compiler.backend;
-  bool isCodeGenerated = backend.generatedCode.isNotEmpty;
+  JsBackendStrategy backendStrategy = compiler.backendStrategy;
+  bool isCodeGenerated = backendStrategy.generatedCode.isNotEmpty;
   Expect.equals(
       expectedCodeGenerated,
       isCodeGenerated,
diff --git a/tests/compiler/dart2js/field_analysis/kfield_analysis_test.dart b/tests/compiler/dart2js/field_analysis/kfield_analysis_test.dart
index 72b427c..9fe7c7d 100644
--- a/tests/compiler/dart2js/field_analysis/kfield_analysis_test.dart
+++ b/tests/compiler/dart2js/field_analysis/kfield_analysis_test.dart
@@ -35,9 +35,9 @@
       Map<Id, ActualData<Features>> actualMap,
       {bool verbose: false}) {
     if (member.isField) {
-      KernelFrontEndStrategy frontendStrategy = compiler.frontendStrategy;
+      KernelFrontendStrategy frontendStrategy = compiler.frontendStrategy;
       KFieldAnalysis allocatorAnalysis =
-          compiler.backend.fieldAnalysisForTesting;
+          frontendStrategy.fieldAnalysisForTesting;
       ir.Member node = frontendStrategy.elementMap.getMemberNode(member);
       Features features = new Features();
       if (member.isInstanceMember) {
diff --git a/tests/compiler/dart2js/generic_methods/generic_method_test.dart b/tests/compiler/dart2js/generic_methods/generic_method_test.dart
index 65b2146..76ef8a7 100644
--- a/tests/compiler/dart2js/generic_methods/generic_method_test.dart
+++ b/tests/compiler/dart2js/generic_methods/generic_method_test.dart
@@ -8,6 +8,7 @@
 import 'package:compiler/src/compiler.dart';
 import 'package:compiler/src/elements/entities.dart';
 import 'package:compiler/src/js/js.dart' as js;
+import 'package:compiler/src/js_model/js_strategy.dart';
 import 'package:compiler/src/world.dart';
 import 'package:expect/expect.dart';
 import '../helpers/d8_helper.dart';
@@ -178,6 +179,7 @@
       Flags.disableRtiOptimization,
     ], expectedOutput: OUTPUT, printJs: args.contains('-v'));
     Compiler compiler = result.compilationResult.compiler;
+    JsBackendStrategy backendStrategy = compiler.backendStrategy;
     JClosedWorld closedWorld = compiler.backendClosedWorldForTesting;
     ElementEnvironment elementEnvironment = closedWorld.elementEnvironment;
 
@@ -195,7 +197,7 @@
             elementEnvironment.mainLibrary, methodName);
         Expect.isNotNull(method, "Method '$methodName' not found.");
       }
-      js.Fun fun = compiler.backend.generatedCode[method];
+      js.Fun fun = backendStrategy.generatedCode[method];
       Expect.equals(expectedParameterCount, fun.params.length,
           "Unexpected parameter count for $method:\n${js.nodeToString(fun)}");
     }
diff --git a/tests/compiler/dart2js/generic_methods/instantiation_stub_test.dart b/tests/compiler/dart2js/generic_methods/instantiation_stub_test.dart
index 850e1ff..f8c3281 100644
--- a/tests/compiler/dart2js/generic_methods/instantiation_stub_test.dart
+++ b/tests/compiler/dart2js/generic_methods/instantiation_stub_test.dart
@@ -68,7 +68,7 @@
     Expect.isTrue(result.isSuccess);
     Compiler compiler = result.compiler;
     JClosedWorld closedWorld = compiler.backendClosedWorldForTesting;
-    ProgramLookup programLookup = new ProgramLookup(compiler);
+    ProgramLookup programLookup = new ProgramLookup(compiler.backendStrategy);
 
     void checkStubs(ClassEntity element, List<String> expectedStubs) {
       Class cls = programLookup.getClass(element);
diff --git a/tests/compiler/dart2js/helpers/compiler_helper.dart b/tests/compiler/dart2js/helpers/compiler_helper.dart
index 9fab31d..a6c1deb 100644
--- a/tests/compiler/dart2js/helpers/compiler_helper.dart
+++ b/tests/compiler/dart2js/helpers/compiler_helper.dart
@@ -6,12 +6,12 @@
 
 import 'dart:async';
 import 'package:compiler/compiler_new.dart';
-import 'package:compiler/src/common_elements.dart';
-import 'package:compiler/src/elements/entities.dart';
-import 'package:compiler/src/js_backend/js_backend.dart' as js;
 import 'package:compiler/src/commandline_options.dart';
-import 'package:compiler/src/world.dart';
+import 'package:compiler/src/common_elements.dart';
 import 'package:compiler/src/compiler.dart' show Compiler;
+import 'package:compiler/src/elements/entities.dart';
+import 'package:compiler/src/js_model/js_strategy.dart';
+import 'package:compiler/src/world.dart';
 import 'package:expect/expect.dart';
 import 'package:front_end/src/fasta/util/link.dart' show Link;
 import 'memory_compiler.dart';
@@ -85,8 +85,8 @@
   LibraryEntity mainLibrary = elementEnvironment.mainLibrary;
   FunctionEntity element =
       elementEnvironment.lookupLibraryMember(mainLibrary, methodName);
-  js.JavaScriptBackend backend = compiler.backend;
-  String generated = backend.getGeneratedCode(element);
+  JsBackendStrategy backendStrategy = compiler.backendStrategy;
+  String generated = backendStrategy.getGeneratedCodeForTesting(element);
   if (check != null) {
     check(generated);
   }
diff --git a/tests/compiler/dart2js/helpers/program_lookup.dart b/tests/compiler/dart2js/helpers/program_lookup.dart
index 4f4d052..54124aa 100644
--- a/tests/compiler/dart2js/helpers/program_lookup.dart
+++ b/tests/compiler/dart2js/helpers/program_lookup.dart
@@ -4,11 +4,11 @@
 
 import 'package:expect/expect.dart';
 import 'package:compiler/src/common_elements.dart';
-import 'package:compiler/src/compiler.dart';
 import 'package:compiler/src/deferred_load.dart';
 import 'package:compiler/src/elements/entities.dart';
 import 'package:compiler/src/js_backend/namer.dart';
 import 'package:compiler/src/js_emitter/model.dart';
+import 'package:compiler/src/js_model/js_strategy.dart';
 import 'package:compiler/src/js/js.dart' as js;
 
 ClassEntity lookupClass(JElementEnvironment elementEnvironment, String name) {
@@ -42,9 +42,9 @@
   final Program program;
   final Namer namer;
 
-  ProgramLookup(Compiler compiler)
-      : this.program = compiler.backend.emitterTask.emitter.programForTesting,
-        this.namer = compiler.backend.namerForTesting;
+  ProgramLookup(JsBackendStrategy backendStrategy)
+      : this.program = backendStrategy.emitterTask.emitter.programForTesting,
+        this.namer = backendStrategy.namerForTesting;
 
   Fragment getFragment(OutputUnit outputUnit) {
     for (Fragment fragment in program.fragments) {
diff --git a/tests/compiler/dart2js/helpers/type_test_helper.dart b/tests/compiler/dart2js/helpers/type_test_helper.dart
index f95e6bb..3e38e24 100644
--- a/tests/compiler/dart2js/helpers/type_test_helper.dart
+++ b/tests/compiler/dart2js/helpers/type_test_helper.dart
@@ -75,7 +75,7 @@
     if (testBackendWorld) {
       return compiler.backendClosedWorldForTesting.dartTypes;
     } else {
-      KernelFrontEndStrategy frontendStrategy = compiler.frontendStrategy;
+      KernelFrontendStrategy frontendStrategy = compiler.frontendStrategy;
       return frontendStrategy.elementMap.types;
     }
   }
diff --git a/tests/compiler/dart2js/impact/impact_test.dart b/tests/compiler/dart2js/impact/impact_test.dart
index 38bfe17..ec60875 100644
--- a/tests/compiler/dart2js/impact/impact_test.dart
+++ b/tests/compiler/dart2js/impact/impact_test.dart
@@ -54,7 +54,7 @@
   void computeMemberData(Compiler compiler, MemberEntity member,
       Map<Id, ActualData<Features>> actualMap,
       {bool verbose: false}) {
-    KernelFrontEndStrategy frontendStrategy = compiler.frontendStrategy;
+    KernelFrontendStrategy frontendStrategy = compiler.frontendStrategy;
     WorldImpact impact = compiler.impactCache[member];
     ir.Member node = frontendStrategy.elementMap.getMemberNode(member);
     Features features = new Features();
diff --git a/tests/compiler/dart2js/inlining/inlining_test.dart b/tests/compiler/dart2js/inlining/inlining_test.dart
index 8e73762..bb4dcd2 100644
--- a/tests/compiler/dart2js/inlining/inlining_test.dart
+++ b/tests/compiler/dart2js/inlining/inlining_test.dart
@@ -9,8 +9,8 @@
 import 'package:compiler/src/compiler.dart';
 import 'package:compiler/src/diagnostics/diagnostic_listener.dart';
 import 'package:compiler/src/elements/entities.dart';
-import 'package:compiler/src/js_backend/backend.dart';
 import 'package:compiler/src/js_model/element_map.dart';
+import 'package:compiler/src/js_model/js_strategy.dart';
 import 'package:compiler/src/js_model/js_world.dart';
 import 'package:compiler/src/ssa/builder_kernel.dart';
 import 'package:compiler/src/universe/world_impact.dart';
@@ -40,7 +40,7 @@
     JsToElementMap elementMap = closedWorld.elementMap;
     MemberDefinition definition = elementMap.getMemberDefinition(member);
     new InliningIrComputer(compiler.reporter, actualMap, elementMap, member,
-            compiler.backend, closedWorld.closureDataLookup)
+            compiler.backendStrategy, closedWorld.closureDataLookup)
         .run(definition.node);
   }
 
@@ -50,7 +50,7 @@
 
 /// AST visitor for computing inference data for a member.
 class InliningIrComputer extends IrDataExtractor<String> {
-  final JavaScriptBackend backend;
+  final JsBackendStrategy _backendStrategy;
   final JsToElementMap _elementMap;
   final ClosureData _closureDataLookup;
   final InlineDataCache _inlineDataCache;
@@ -60,7 +60,7 @@
       Map<Id, ActualData<String>> actualMap,
       this._elementMap,
       MemberEntity member,
-      this.backend,
+      this._backendStrategy,
       this._closureDataLookup)
       : this._inlineDataCache = new InlineDataCache(enableUserAssertions: true),
         super(reporter, actualMap);
@@ -72,7 +72,7 @@
         constructorBody = getConstructorBody(member);
       }
       List<String> inlinedIn = <String>[];
-      backend.codegenImpactsForTesting
+      _backendStrategy.codegenImpactsForTesting
           .forEach((MemberEntity user, WorldImpact impact) {
         for (StaticUse use in impact.staticUses) {
           if (use.kind == StaticUseKind.INLINING) {
diff --git a/tests/compiler/dart2js/js/js_constant_test.dart b/tests/compiler/dart2js/js/js_constant_test.dart
index 0bc4b44..6d0613e 100644
--- a/tests/compiler/dart2js/js/js_constant_test.dart
+++ b/tests/compiler/dart2js/js/js_constant_test.dart
@@ -32,8 +32,8 @@
       var elementEnvironment = closedWorld.elementEnvironment;
 
       MemberEntity element = elementEnvironment.mainFunction;
-      var backend = compiler.backend;
-      String generated = backend.getGeneratedCode(element);
+      String generated =
+          compiler.backendStrategy.getGeneratedCodeForTesting(element);
       checkerForAbsentPresent(test)(generated);
     }
 
diff --git a/tests/compiler/dart2js/js/js_spec_optimization_test.dart b/tests/compiler/dart2js/js/js_spec_optimization_test.dart
index 3ea2c61..004030c 100644
--- a/tests/compiler/dart2js/js/js_spec_optimization_test.dart
+++ b/tests/compiler/dart2js/js/js_spec_optimization_test.dart
@@ -99,8 +99,8 @@
       var elementEnvironment = closedWorld.elementEnvironment;
 
       MemberEntity element = elementEnvironment.mainFunction;
-      var backend = compiler.backend;
-      String generated = backend.getGeneratedCode(element);
+      String generated =
+          compiler.backendStrategy.getGeneratedCodeForTesting(element);
       checker(generated);
     }
 
diff --git a/tests/compiler/dart2js/member_usage/member_usage_test.dart b/tests/compiler/dart2js/member_usage/member_usage_test.dart
index 4e5a2c0..1a5dd51 100644
--- a/tests/compiler/dart2js/member_usage/member_usage_test.dart
+++ b/tests/compiler/dart2js/member_usage/member_usage_test.dart
@@ -79,7 +79,7 @@
   void computeMemberData(Compiler compiler, MemberEntity member,
       Map<Id, ActualData<Features>> actualMap,
       {bool verbose: false}) {
-    KernelFrontEndStrategy frontendStrategy = compiler.frontendStrategy;
+    KernelFrontendStrategy frontendStrategy = compiler.frontendStrategy;
     ResolutionWorldBuilderImpl resolutionWorldBuilder =
         compiler.resolutionWorldBuilder;
     ir.Member node = frontendStrategy.elementMap.getMemberNode(member);
diff --git a/tests/compiler/dart2js/model/cfe_annotations_test.dart b/tests/compiler/dart2js/model/cfe_annotations_test.dart
index d5b0e0f..e01dba6 100644
--- a/tests/compiler/dart2js/model/cfe_annotations_test.dart
+++ b/tests/compiler/dart2js/model/cfe_annotations_test.dart
@@ -197,7 +197,7 @@
             ..addAll(options));
       Expect.isTrue(result.isSuccess);
       Compiler compiler = result.compiler;
-      KernelFrontEndStrategy frontendStrategy = compiler.frontendStrategy;
+      KernelFrontendStrategy frontendStrategy = compiler.frontendStrategy;
       KernelToElementMapImpl elementMap = frontendStrategy.elementMap;
       ir.Component component = elementMap.env.mainComponent;
       IrAnnotationData annotationData =
diff --git a/tests/compiler/dart2js/model/cfe_constant_evaluation_test.dart b/tests/compiler/dart2js/model/cfe_constant_evaluation_test.dart
index e75c916..1ed2501 100644
--- a/tests/compiler/dart2js/model/cfe_constant_evaluation_test.dart
+++ b/tests/compiler/dart2js/model/cfe_constant_evaluation_test.dart
@@ -536,7 +536,7 @@
       '${Flags.enableLanguageExperiments}=constant-update-2018',
     ]);
     Compiler compiler = result.compiler;
-    KernelFrontEndStrategy frontEndStrategy = compiler.frontendStrategy;
+    KernelFrontendStrategy frontEndStrategy = compiler.frontendStrategy;
     KernelToElementMapImpl elementMap = frontEndStrategy.elementMap;
     KElementEnvironment elementEnvironment =
         compiler.frontendStrategy.elementEnvironment;
diff --git a/tests/compiler/dart2js/model/no_such_method_enabled_test.dart b/tests/compiler/dart2js/model/no_such_method_enabled_test.dart
index 58d85b9..08a9bf9 100644
--- a/tests/compiler/dart2js/model/no_such_method_enabled_test.dart
+++ b/tests/compiler/dart2js/model/no_such_method_enabled_test.dart
@@ -261,7 +261,8 @@
 checkTest(Compiler compiler, NoSuchMethodTest test) {
   ElementEnvironment frontendEnvironment =
       compiler.frontendStrategy.elementEnvironment;
-  NoSuchMethodRegistryImpl registry = compiler.backend.noSuchMethodRegistry;
+  NoSuchMethodRegistryImpl registry =
+      compiler.frontendStrategy.noSuchMethodRegistry;
   NoSuchMethodResolver resolver = registry.internalResolverForTesting;
   FunctionEntity ObjectNSM = frontendEnvironment.lookupClassMember(
       compiler.frontendStrategy.commonElements.objectClass, 'noSuchMethod');
diff --git a/tests/compiler/dart2js/model/supermixin_test.dart b/tests/compiler/dart2js/model/supermixin_test.dart
index 3533fe7..fab668d 100644
--- a/tests/compiler/dart2js/model/supermixin_test.dart
+++ b/tests/compiler/dart2js/model/supermixin_test.dart
@@ -61,7 +61,7 @@
     MemberEntity method2 = lookupMember(elementEnvironment, 'Class.method2');
     Expect.equals(mixin, method2.enclosingClass);
 
-    ProgramLookup lookup = new ProgramLookup(result.compiler);
+    ProgramLookup lookup = new ProgramLookup(result.compiler.backendStrategy);
     ClassData data = lookup.getClassData(superClass);
     Expect.isNotNull(data.getMethod(method1));
     Expect.isNull(data.getMethod(method2));
diff --git a/tests/compiler/dart2js/optimization/optimization_test.dart b/tests/compiler/dart2js/optimization/optimization_test.dart
index 1f56a1a..a1bc198 100644
--- a/tests/compiler/dart2js/optimization/optimization_test.dart
+++ b/tests/compiler/dart2js/optimization/optimization_test.dart
@@ -10,8 +10,8 @@
 import 'package:compiler/src/compiler.dart';
 import 'package:compiler/src/diagnostics/diagnostic_listener.dart';
 import 'package:compiler/src/elements/entities.dart';
-import 'package:compiler/src/js_backend/backend.dart';
 import 'package:compiler/src/js_model/element_map.dart';
+import 'package:compiler/src/js_model/js_strategy.dart';
 import 'package:compiler/src/js_model/js_world.dart';
 import 'package:compiler/src/ssa/logging.dart';
 import 'package:compiler/src/ssa/ssa.dart';
@@ -142,7 +142,7 @@
     JsToElementMap elementMap = closedWorld.elementMap;
     MemberDefinition definition = elementMap.getMemberDefinition(member);
     new OptimizationIrComputer(compiler.reporter, actualMap, elementMap, member,
-            compiler.backend, closedWorld.closureDataLookup)
+            compiler.backendStrategy, closedWorld.closureDataLookup)
         .run(definition.node);
   }
 
@@ -153,7 +153,7 @@
 
 /// AST visitor for computing inference data for a member.
 class OptimizationIrComputer extends IrDataExtractor<OptimizationTestLog> {
-  final JavaScriptBackend backend;
+  final JsBackendStrategy _backendStrategy;
   final JsToElementMap _elementMap;
   final ClosureData _closureDataLookup;
 
@@ -162,12 +162,12 @@
       Map<Id, ActualData<OptimizationTestLog>> actualMap,
       this._elementMap,
       MemberEntity member,
-      this.backend,
+      this._backendStrategy,
       this._closureDataLookup)
       : super(reporter, actualMap);
 
   OptimizationTestLog getLog(MemberEntity member) {
-    SsaFunctionCompiler functionCompiler = backend.functionCompiler;
+    SsaFunctionCompiler functionCompiler = _backendStrategy.functionCompiler;
     return functionCompiler.optimizer.loggersForTesting[member];
   }
 
diff --git a/tests/compiler/dart2js/rti/backend_type_helper_test.dart b/tests/compiler/dart2js/rti/backend_type_helper_test.dart
index 8cf6de4..95dc812 100644
--- a/tests/compiler/dart2js/rti/backend_type_helper_test.dart
+++ b/tests/compiler/dart2js/rti/backend_type_helper_test.dart
@@ -20,7 +20,7 @@
     Expect.isTrue(result.isSuccess);
     Compiler compiler = result.compiler;
     JClosedWorld closedWorld = compiler.backendClosedWorldForTesting;
-    ProgramLookup programLookup = new ProgramLookup(compiler);
+    ProgramLookup programLookup = new ProgramLookup(compiler.backendStrategy);
 
     List<ClassEntity> found = <ClassEntity>[];
     for (ClassEntity element
diff --git a/tests/compiler/dart2js/rti/disable_rti_test.dart b/tests/compiler/dart2js/rti/disable_rti_test.dart
index c4749ad..9e7b1aa 100644
--- a/tests/compiler/dart2js/rti/disable_rti_test.dart
+++ b/tests/compiler/dart2js/rti/disable_rti_test.dart
@@ -79,7 +79,7 @@
     JClosedWorld closedWorld = compiler.backendClosedWorldForTesting;
     JElementEnvironment elementEnvironment = closedWorld.elementEnvironment;
     RuntimeTypesNeed rtiNeed = closedWorld.rtiNeed;
-    ProgramLookup programLookup = new ProgramLookup(compiler);
+    ProgramLookup programLookup = new ProgramLookup(compiler.backendStrategy);
 
     List<ClassEntity> closures = <ClassEntity>[];
 
diff --git a/tests/compiler/dart2js/rti/emission/fixed_type_argument_implements.dart b/tests/compiler/dart2js/rti/emission/fixed_type_argument_implements.dart
index 0b22968..61cf1c8 100644
--- a/tests/compiler/dart2js/rti/emission/fixed_type_argument_implements.dart
+++ b/tests/compiler/dart2js/rti/emission/fixed_type_argument_implements.dart
@@ -26,4 +26,7 @@
 }
 
 @pragma('dart2js:noInline')
-void test(C<A> c) {}
+void test(Object o) => test1(o);
+
+@pragma('dart2js:noInline')
+void test1(C<A> c) {}
diff --git a/tests/compiler/dart2js/rti/emission/list.dart b/tests/compiler/dart2js/rti/emission/list.dart
index fbe0937..14d539bf 100644
--- a/tests/compiler/dart2js/rti/emission/list.dart
+++ b/tests/compiler/dart2js/rti/emission/list.dart
@@ -2,16 +2,16 @@
 // 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.
 
-/*strong.class: global#JSArray:checkedInstance,checks=[$isIterable,$isList],instance*/
+/*strong.class: global#JSArray:checkedInstance,checks=[$isIterable],instance*/
 /*omit.class: global#JSArray:checkedInstance,checks=[$isIterable],instance*/
 
 /*class: global#Iterable:checkedInstance*/
 
-/*strong.class: A:checkedInstance,checkedTypeArgument,checks=[],typeArgument*/
+/*strong.class: A:checkedTypeArgument,checks=[],typeArgument*/
 /*omit.class: A:checkedTypeArgument,checks=[],typeArgument*/
 class A {}
 
-/*strong.class: B:checkedInstance,checks=[],typeArgument*/
+/*strong.class: B:checks=[],typeArgument*/
 /*omit.class: B:checks=[],typeArgument*/
 class B {}
 
diff --git a/tests/compiler/dart2js/rti/emission/map_literal.dart b/tests/compiler/dart2js/rti/emission/map_literal.dart
index acb2eb3..66a1309 100644
--- a/tests/compiler/dart2js/rti/emission/map_literal.dart
+++ b/tests/compiler/dart2js/rti/emission/map_literal.dart
@@ -2,7 +2,7 @@
 // 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.
 
-/*strong.class: global#Map:checkedInstance,instance*/
+/*strong.class: global#Map:instance*/
 
 /*class: global#LinkedHashMap:*/
 /*class: global#JsLinkedHashMap:checks=[],instance*/
diff --git a/tests/compiler/dart2js/rti/emission/static_argument.dart b/tests/compiler/dart2js/rti/emission/static_argument.dart
index 0e738ce..d840003 100644
--- a/tests/compiler/dart2js/rti/emission/static_argument.dart
+++ b/tests/compiler/dart2js/rti/emission/static_argument.dart
@@ -2,7 +2,7 @@
 // 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.
 
-/*strong.class: I1:checkedInstance*/
+/*strong.class: I1:*/
 /*omit.class: I1:*/
 class I1 {}
 
@@ -10,13 +10,11 @@
 /*omit.class: I2:checkedTypeArgument,checks=[],typeArgument*/
 class I2 {}
 
-// TODO(32954): Exclude $isI1 because foo is only called directly.
-/*strong.class: A:checks=[$isI1,$isI2],instance*/
+/*strong.class: A:checks=[$isI2],instance*/
 /*omit.class: A:checks=[$isI2],instance*/
 class A implements I1, I2 {}
 
-// TODO(32954): Exclude $isI1 because foo is only called directly.
-/*strong.class: B:checks=[$isI1,$isI2],instance*/
+/*strong.class: B:checks=[$isI2],instance*/
 /*omit.class: B:checks=[$isI2],instance*/
 class B implements I1, I2 {}
 
diff --git a/tests/compiler/dart2js/rti/factory_call_test.dart b/tests/compiler/dart2js/rti/factory_call_test.dart
index 392c989..f92fd7e 100644
--- a/tests/compiler/dart2js/rti/factory_call_test.dart
+++ b/tests/compiler/dart2js/rti/factory_call_test.dart
@@ -8,6 +8,7 @@
 import 'package:compiler/src/elements/entities.dart';
 import 'package:compiler/src/js_backend/runtime_types.dart';
 import 'package:compiler/src/js_emitter/model.dart';
+import 'package:compiler/src/js_model/js_strategy.dart';
 import 'package:compiler/src/js/js.dart' as js;
 import 'package:compiler/src/world.dart';
 import 'package:expect/expect.dart';
@@ -40,13 +41,14 @@
         await runCompiler(memorySourceFiles: {'main.dart': code});
     Expect.isTrue(result.isSuccess);
     Compiler compiler = result.compiler;
+    JsBackendStrategy backendStrategy = compiler.backendStrategy;
     JClosedWorld closedWorld = compiler.backendClosedWorldForTesting;
     RuntimeTypesNeed rtiNeed = closedWorld.rtiNeed;
     ElementEnvironment elementEnvironment = closedWorld.elementEnvironment;
-    ProgramLookup programLookup = new ProgramLookup(compiler);
+    ProgramLookup programLookup = new ProgramLookup(backendStrategy);
 
     js.Name getName(String name) {
-      return compiler.backend.namerForTesting
+      return backendStrategy.namerForTesting
           .globalPropertyNameForMember(lookupMember(elementEnvironment, name));
     }
 
diff --git a/tests/compiler/dart2js/rti/instance_call_test.dart b/tests/compiler/dart2js/rti/instance_call_test.dart
index cdbbf3e..41c88ea 100644
--- a/tests/compiler/dart2js/rti/instance_call_test.dart
+++ b/tests/compiler/dart2js/rti/instance_call_test.dart
@@ -10,6 +10,7 @@
 import 'package:compiler/src/elements/names.dart';
 import 'package:compiler/src/js_backend/runtime_types.dart';
 import 'package:compiler/src/js_emitter/model.dart';
+import 'package:compiler/src/js_model/js_strategy.dart';
 import 'package:compiler/src/js/js.dart' as js;
 import 'package:compiler/src/world.dart';
 import 'package:compiler/src/universe/call_structure.dart';
@@ -101,13 +102,14 @@
         options: [Flags.omitImplicitChecks]);
     Expect.isTrue(result.isSuccess);
     Compiler compiler = result.compiler;
+    JsBackendStrategy backendStrategy = compiler.backendStrategy;
     JClosedWorld closedWorld = compiler.backendClosedWorldForTesting;
     RuntimeTypesNeed rtiNeed = closedWorld.rtiNeed;
     ElementEnvironment elementEnvironment = closedWorld.elementEnvironment;
-    ProgramLookup programLookup = new ProgramLookup(compiler);
+    ProgramLookup programLookup = new ProgramLookup(backendStrategy);
 
     js.Name getName(String name, int typeArguments) {
-      return compiler.backend.namerForTesting.invocationName(new Selector.call(
+      return backendStrategy.namerForTesting.invocationName(new Selector.call(
           new PublicName(name),
           new CallStructure(1, const <String>[], typeArguments)));
     }
diff --git a/tests/compiler/dart2js/rti/rti_emission_test.dart b/tests/compiler/dart2js/rti/rti_emission_test.dart
index 0e580ca..fd96ed8 100644
--- a/tests/compiler/dart2js/rti/rti_emission_test.dart
+++ b/tests/compiler/dart2js/rti/rti_emission_test.dart
@@ -13,6 +13,7 @@
 import 'package:compiler/src/js_backend/runtime_types.dart';
 import 'package:compiler/src/js_emitter/model.dart';
 import 'package:compiler/src/js_model/element_map.dart';
+import 'package:compiler/src/js_model/js_strategy.dart';
 import 'package:compiler/src/js_model/js_world.dart';
 import 'package:compiler/src/util/features.dart';
 import 'package:kernel/ast.dart' as ir;
@@ -43,11 +44,13 @@
   Compiler get compiler;
   ProgramLookup lookup;
 
+  JsBackendStrategy get backendStrategy => compiler.backendStrategy;
+
   RuntimeTypesImpl get checksBuilder =>
-      compiler.backend.rtiChecksBuilderForTesting;
+      backendStrategy.rtiChecksBuilderForTesting;
 
   String getClassValue(ClassEntity element) {
-    lookup ??= new ProgramLookup(compiler);
+    lookup ??= new ProgramLookup(backendStrategy);
     Class cls = lookup.getClass(element);
     Features features = new Features();
     if (cls != null) {
diff --git a/tests/compiler/dart2js/rti/rti_need_test_helper.dart b/tests/compiler/dart2js/rti/rti_need_test_helper.dart
index 4c54942..d5c4e49 100644
--- a/tests/compiler/dart2js/rti/rti_need_test_helper.dart
+++ b/tests/compiler/dart2js/rti/rti_need_test_helper.dart
@@ -57,10 +57,11 @@
 abstract class ComputeValueMixin {
   Compiler get compiler;
 
+  KernelFrontendStrategy get frontendStrategy => compiler.frontendStrategy;
   ResolutionWorldBuilder get resolutionWorldBuilder =>
       compiler.resolutionWorldBuilder;
   RuntimeTypesNeedBuilderImpl get rtiNeedBuilder =>
-      compiler.frontendStrategy.runtimeTypesNeedBuilderForTesting;
+      frontendStrategy.runtimeTypesNeedBuilderForTesting;
   RuntimeTypesNeedImpl get rtiNeed =>
       compiler.backendClosedWorldForTesting.rtiNeed;
   ClassEntity getFrontendClass(ClassEntity cls);
@@ -308,7 +309,7 @@
     JsClosedWorld closedWorld = compiler.backendClosedWorldForTesting;
     ir.Node node = closedWorld.elementMap.getMemberDefinition(member).node;
     if (node is ir.FunctionDeclaration || node is ir.FunctionExpression) {
-      KernelFrontEndStrategy frontendStrategy = compiler.frontendStrategy;
+      KernelFrontendStrategy frontendStrategy = compiler.frontendStrategy;
       KernelToElementMap frontendElementMap = frontendStrategy.elementMap;
       return frontendElementMap.getLocalFunction(node);
     }
diff --git a/tests/compiler/dart2js/rti/type_representation_test.dart b/tests/compiler/dart2js/rti/type_representation_test.dart
index 0729ef9..e1c2e07 100644
--- a/tests/compiler/dart2js/rti/type_representation_test.dart
+++ b/tests/compiler/dart2js/rti/type_representation_test.dart
@@ -12,9 +12,9 @@
 import 'package:compiler/src/elements/types.dart';
 import 'package:compiler/src/js/js.dart';
 import 'package:compiler/src/elements/entities.dart';
-import 'package:compiler/src/js_backend/backend.dart' show JavaScriptBackend;
 import 'package:compiler/src/js_backend/runtime_types.dart'
     show RuntimeTypeTags, TypeRepresentationGenerator;
+import 'package:compiler/src/js_model/js_strategy.dart';
 import 'package:compiler/src/world.dart';
 import 'package:expect/expect.dart';
 import '../helpers/element_lookup.dart';
@@ -65,7 +65,7 @@
       await runCompiler(memorySourceFiles: {'main.dart': source});
   Expect.isTrue(result.isSuccess);
   Compiler compiler = result.compiler;
-  JavaScriptBackend backend = compiler.backend;
+  JsBackendStrategy backendStrategy = compiler.backendStrategy;
 
   RuntimeTypeTags rtiTags = const RuntimeTypeTags();
   TypeRepresentationGenerator typeRepresentation =
@@ -86,7 +86,7 @@
       [String expectedTypedefRepresentation]) {
     bool encodeTypedefName = false;
     Expression expression = typeRepresentation.getTypeRepresentation(
-        backend.emitterTask.emitter,
+        backendStrategy.emitterTask.emitter,
         type,
         onVariable,
         (x) => encodeTypedefName);
@@ -94,7 +94,7 @@
 
     encodeTypedefName = true;
     expression = typeRepresentation.getTypeRepresentation(
-        backend.emitterTask.emitter,
+        backendStrategy.emitterTask.emitter,
         type,
         onVariable,
         (x) => encodeTypedefName);
@@ -106,7 +106,7 @@
 
   String getJsName(Entity cls) {
     Expression name = typeRepresentation.getJavaScriptClassName(
-        cls, backend.emitterTask.emitter);
+        cls, backendStrategy.emitterTask.emitter);
     return stringify(name);
   }
 
diff --git a/tests/compiler/dart2js/sourcemaps/helpers/sourcemap_helper.dart b/tests/compiler/dart2js/sourcemaps/helpers/sourcemap_helper.dart
index 9e4273d..058750c 100644
--- a/tests/compiler/dart2js/sourcemaps/helpers/sourcemap_helper.dart
+++ b/tests/compiler/dart2js/sourcemaps/helpers/sourcemap_helper.dart
@@ -17,7 +17,7 @@
 import 'package:compiler/src/js/js.dart' as js;
 import 'package:compiler/src/js/js_debug.dart';
 import 'package:compiler/src/js/js_source_mapping.dart';
-import 'package:compiler/src/js_backend/js_backend.dart';
+import 'package:compiler/src/js_model/js_strategy.dart';
 import 'package:compiler/src/source_file_provider.dart';
 import '../../helpers/memory_compiler.dart';
 import '../../helpers/output_collector.dart';
@@ -342,29 +342,29 @@
         options: ['--out=$targetUri', '--source-map=$sourceMapFileUri']
           ..addAll(options),
         beforeRun: (compiler) {
-          JavaScriptBackend backend = compiler.backend;
+          JsBackendStrategy backendStrategy = compiler.backendStrategy;
           dynamic handler = compiler.handler;
           SourceFileProvider sourceFileProvider = handler.provider;
           sourceFileManager =
               new ProviderSourceFileManager(sourceFileProvider, outputProvider);
           RecordingSourceInformationStrategy strategy =
               new RecordingSourceInformationStrategy(
-                  backend.sourceInformationStrategy);
-          backend.sourceInformationStrategy = strategy;
+                  backendStrategy.sourceInformationStrategy);
+          backendStrategy.sourceInformationStrategy = strategy;
         });
     if (!result.isSuccess) {
       throw "Compilation failed.";
     }
 
     api.CompilerImpl compiler = result.compiler;
-    JavaScriptBackend backend = compiler.backend;
+    JsBackendStrategy backendStrategy = compiler.backendStrategy;
     RecordingSourceInformationStrategy strategy =
-        backend.sourceInformationStrategy;
+        backendStrategy.sourceInformationStrategy;
     SourceMapInfo mainSourceMapInfo;
     Map<MemberEntity, SourceMapInfo> elementSourceMapInfos =
         <MemberEntity, SourceMapInfo>{};
     if (perElement) {
-      backend.generatedCode.forEach((_element, js.Expression node) {
+      backendStrategy.generatedCode.forEach((_element, js.Expression node) {
         MemberEntity element = _element;
         RecordedSourceInformationProcess subProcess =
             strategy.subProcessForNode(node);
diff --git a/tests/compiler/dart2js/static_type/static_type_test.dart b/tests/compiler/dart2js/static_type/static_type_test.dart
index 369d14c..e530f19 100644
--- a/tests/compiler/dart2js/static_type/static_type_test.dart
+++ b/tests/compiler/dart2js/static_type/static_type_test.dart
@@ -48,7 +48,7 @@
   void computeMemberData(Compiler compiler, MemberEntity member,
       Map<Id, ActualData<String>> actualMap,
       {bool verbose: false}) {
-    KernelFrontEndStrategy frontendStrategy = compiler.frontendStrategy;
+    KernelFrontendStrategy frontendStrategy = compiler.frontendStrategy;
     KernelToElementMapImpl elementMap = frontendStrategy.elementMap;
     StaticTypeCache staticTypeCache = elementMap.getCachedStaticTypes(member);
     ir.Member node = elementMap.getMemberNode(member);
diff --git a/tests/compiler/dart2js/static_type/type_promotion_test.dart b/tests/compiler/dart2js/static_type/type_promotion_test.dart
index ca9dfd4..1367dd3 100644
--- a/tests/compiler/dart2js/static_type/type_promotion_test.dart
+++ b/tests/compiler/dart2js/static_type/type_promotion_test.dart
@@ -46,7 +46,7 @@
   void computeMemberData(Compiler compiler, MemberEntity member,
       Map<Id, ActualData<String>> actualMap,
       {bool verbose: false}) {
-    KernelFrontEndStrategy frontendStrategy = compiler.frontendStrategy;
+    KernelFrontendStrategy frontendStrategy = compiler.frontendStrategy;
     KernelToElementMapImpl elementMap = frontendStrategy.elementMap;
     Map<ir.Expression, TypeMap> typeMaps =
         elementMap.getTypeMapsForTesting(member);
diff --git a/tests/compiler/dart2js_extra/async_stacktrace_test.dart b/tests/compiler/dart2js_extra/async_stacktrace_test.dart
index 947e2d6..708706b 100644
--- a/tests/compiler/dart2js_extra/async_stacktrace_test.dart
+++ b/tests/compiler/dart2js_extra/async_stacktrace_test.dart
@@ -27,9 +27,8 @@
 }
 
 Future test1(Tracer tracer) {
-  foo() async*
-
-  /// asyncStar: ok
+  foo() async
+      * //# asyncStar: ok
   {
     var savedStackTrace;
     try {
@@ -50,17 +49,15 @@
     tracer.trace("f");
   }
 
-  return foo().toList()
-
-      /// asyncStar: continued
+  return foo()
+      .toList() //# asyncStar: continued
       ;
 }
 
 Future test2(Tracer tracer) {
   var savedStackTrace;
-  foo() async*
-
-  /// asyncStar: continued
+  foo() async
+      * //# asyncStar: continued
   {
     try {
       tracer.trace("a");
@@ -74,9 +71,8 @@
     tracer.trace("d");
   }
 
-  return foo().toList()
-
-      /// asyncStar: continued
+  return foo()
+      .toList() //# asyncStar: continued
       .catchError((e, st) {
     tracer.trace("e");
     Expect.equals(savedStackTrace.toString(), st.toString());
@@ -85,9 +81,8 @@
 
 Future test3(Tracer tracer) {
   var savedStackTrace;
-  foo() async*
-
-  /// asyncStar: continued
+  foo() async
+      * //# asyncStar: continued
   {
     try {
       tracer.trace("a");
@@ -99,9 +94,8 @@
     }
   }
 
-  return foo().toList()
-
-      /// asyncStar: continued
+  return foo()
+      .toList() //# asyncStar: continued
       .catchError((e, st) {
     tracer.trace("c");
     Expect.equals(savedStackTrace.toString(), st.toString());
diff --git a/tests/compiler/dart2js_extra/rti/bind_test.dart b/tests/compiler/dart2js_extra/rti/bind_test.dart
new file mode 100644
index 0000000..ca75c20
--- /dev/null
+++ b/tests/compiler/dart2js_extra/rti/bind_test.dart
@@ -0,0 +1,48 @@
+// Copyright (c) 2019, 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:_rti' as rti;
+import "package:expect/expect.dart";
+
+void checkRtiIdentical(Object rti1, Object rti2) {
+  var format = rti.testingRtiToString;
+  Expect.isTrue(
+      identical(rti1, rti2), 'identical(${format(rti1)}, ${format(rti2)}');
+}
+
+test1() {
+  var universe = rti.testingCreateUniverse();
+
+  // Extend environment in one step
+  var env1a = rti.testingUniverseEval(universe, 'Foo');
+  var args1 = rti.testingUniverseEval(universe, '@<aa,bb>');
+  var env1b = rti.testingEnvironmentBind(universe, env1a, args1);
+
+  var rti1 = rti.testingEnvironmentEval(universe, env1b, 'A<0,1,2>');
+  Expect.equals('A<Foo, aa, bb>', rti.testingRtiToString(rti1));
+
+  Expect.equals('binding(interface("Foo"), [interface("aa"), interface("bb")])',
+      rti.testingRtiToDebugString(env1b));
+
+  // Extend environment in two steps
+  var env2a = rti.testingUniverseEval(universe, 'Foo');
+  var args2a = rti.testingUniverseEval(universe, 'aa');
+  var env2b = rti.testingEnvironmentBind(universe, env2a, args2a);
+  var args2b = rti.testingUniverseEval(universe, 'bb');
+  var env2c = rti.testingEnvironmentBind(universe, env2b, args2b);
+
+  var rti2 = rti.testingEnvironmentEval(universe, env2c, 'A<0,1,2>');
+  Expect.equals('A<Foo, aa, bb>', rti.testingRtiToString(rti2));
+
+  Expect.equals('binding(interface("Foo"), [interface("aa")])',
+      rti.testingRtiToDebugString(env2b));
+  Expect.equals('binding(interface("Foo"), [interface("aa"), interface("bb")])',
+      rti.testingRtiToDebugString(env2c));
+
+  checkRtiIdentical(env1b, env2c);
+}
+
+main() {
+  test1();
+}
diff --git a/tests/compiler/dart2js_extra/rti/bound_environment_test.dart b/tests/compiler/dart2js_extra/rti/bound_environment_test.dart
new file mode 100644
index 0000000..5628d8a
--- /dev/null
+++ b/tests/compiler/dart2js_extra/rti/bound_environment_test.dart
@@ -0,0 +1,75 @@
+// Copyright (c) 2019, 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:_rti' as rti;
+import "package:expect/expect.dart";
+
+void checkRtiIdentical(Object rti1, Object rti2) {
+  var format = rti.testingRtiToString;
+  Expect.isTrue(
+      identical(rti1, rti2), 'identical(${format(rti1)}, ${format(rti2)}');
+}
+
+test1() {
+  var universe = rti.testingCreateUniverse();
+
+  var env = rti.testingUniverseEval(universe, 'Foo<bool><int>');
+  var rti1 = rti.testingUniverseEval(universe, 'int');
+  var rti2 = rti.testingEnvironmentEval(universe, env, '1');
+
+  Expect.equals('int', rti.testingRtiToString(rti1));
+  Expect.equals('int', rti.testingRtiToString(rti2));
+  checkRtiIdentical(rti1, rti2);
+
+  var rti3 = rti.testingEnvironmentEval(universe, env, 'A<0,1,2>');
+  Expect.equals('A<Foo<bool>, int, bool>', rti.testingRtiToString(rti3));
+}
+
+test2() {
+  var universe = rti.testingCreateUniverse();
+
+  // Generic class with one nested single-parameter function scope.
+  //   vs.
+  // Unparameterized class with two nested single-parameter function scopes.
+  var env1 = rti.testingUniverseEval(universe, 'Foo<bool><int>');
+  var env2 = rti.testingUniverseEval(universe, 'Foo;<bool><int>');
+
+  var rti1 = rti.testingEnvironmentEval(universe, env1, 'A<0,1,2>');
+  var rti2 = rti.testingEnvironmentEval(universe, env2, 'A<0,1,2>');
+  Expect.equals('A<Foo<bool>, int, bool>', rti.testingRtiToString(rti1));
+  Expect.equals('A<Foo, bool, int>', rti.testingRtiToString(rti2));
+}
+
+test3() {
+  var universe = rti.testingCreateUniverse();
+  var env = rti.testingUniverseEval(universe, 'C<aa,bb><cc,@>');
+  var rti1 = rti.testingEnvironmentEval(universe, env, 'A<0,1,2,3,4>');
+  Expect.equals(
+      'A<C<aa, bb>, cc, dynamic, aa, bb>', rti.testingRtiToString(rti1));
+}
+
+test4() {
+  var universe = rti.testingCreateUniverse();
+  var env = rti.testingUniverseEval(universe, '@<aa,bb>');
+  var rti1 = rti.testingEnvironmentEval(universe, env, 'A<0,1,2>');
+  Expect.equals('A<dynamic, aa, bb>', rti.testingRtiToString(rti1));
+}
+
+test5() {
+  var universe = rti.testingCreateUniverse();
+  var env1 = rti.testingUniverseEval(universe, '@<aa><bb><cc>');
+  var env2 = rti.testingUniverseEval(universe, '@;<aa><bb><cc>');
+  var rti1 = rti.testingEnvironmentEval(universe, env1, 'A<0,1,2,3>');
+  var rti2 = rti.testingEnvironmentEval(universe, env2, 'A<0,1,2,3>');
+  Expect.equals('A<dynamic, aa, bb, cc>', rti.testingRtiToString(rti1));
+  checkRtiIdentical(rti1, rti2);
+}
+
+main() {
+  test1();
+  test2();
+  test3();
+  test4();
+  test5();
+}
diff --git a/tests/compiler/dart2js_extra/rti/class_environment_test.dart b/tests/compiler/dart2js_extra/rti/class_environment_test.dart
index 0cad566..1651fdf 100644
--- a/tests/compiler/dart2js_extra/rti/class_environment_test.dart
+++ b/tests/compiler/dart2js_extra/rti/class_environment_test.dart
@@ -16,7 +16,7 @@
 
   var env = rti.testingUniverseEval(universe, 'Foo<int>');
   var rti1 = rti.testingUniverseEval(universe, 'int');
-  var rti2 = rti.testingEnvironmentEval(universe, env, '0');
+  var rti2 = rti.testingEnvironmentEval(universe, env, '1');
 
   Expect.equals('int', rti.testingRtiToString(rti1));
   Expect.equals('int', rti.testingRtiToString(rti2));
@@ -28,8 +28,8 @@
 
   var env = rti.testingUniverseEval(universe, 'Foo<int,List<int>>');
   var rti1 = rti.testingUniverseEval(universe, 'List<int>');
-  var rti2 = rti.testingEnvironmentEval(universe, env, '1');
-  var rti3 = rti.testingEnvironmentEval(universe, env, 'List<0>');
+  var rti2 = rti.testingEnvironmentEval(universe, env, '2');
+  var rti3 = rti.testingEnvironmentEval(universe, env, 'List<1>');
 
   Expect.equals('List<int>', rti.testingRtiToString(rti1));
   Expect.equals('List<int>', rti.testingRtiToString(rti2));
diff --git a/tests/compiler/dart2js_extra/rti/simple_2_test.dart b/tests/compiler/dart2js_extra/rti/simple_2_test.dart
index ce81c7b..e092311 100644
--- a/tests/compiler/dart2js_extra/rti/simple_2_test.dart
+++ b/tests/compiler/dart2js_extra/rti/simple_2_test.dart
@@ -8,25 +8,72 @@
 testDynamic1() {
   var universe = rti.testingCreateUniverse();
 
-  var dynamicRti1 = rti.testingUniverseEval(universe, 'dynamic');
-  var dynamicRti2 = rti.testingUniverseEval(universe, ',,dynamic,,');
+  var rti1 = rti.testingUniverseEval(universe, 'dynamic');
+  var rti2 = rti.testingUniverseEval(universe, ',,dynamic,,');
 
-  Expect.isTrue(
-      identical(dynamicRti1, dynamicRti2), 'dynamic should be identical');
-  Expect.isFalse(dynamicRti1 is String);
-  Expect.equals('dynamic', rti.testingRtiToString(dynamicRti1));
+  Expect.isTrue(identical(rti1, rti2), 'dynamic should be identical');
+  Expect.isFalse(rti1 is String);
+  Expect.equals('dynamic', rti.testingRtiToString(rti1));
 }
 
 testDynamic2() {
   var universe = rti.testingCreateUniverse();
 
-  var dynamicRti1 = rti.testingUniverseEval(universe, 'dynamic');
-  var dynamicRti2 = rti.testingUniverseEval(universe, ',,@,,');
+  var rti1 = rti.testingUniverseEval(universe, 'dynamic');
+  var rti2 = rti.testingUniverseEval(universe, ',,@,,');
 
-  Expect.isTrue(
-      identical(dynamicRti1, dynamicRti2), 'dynamic should be identical');
-  Expect.isFalse(dynamicRti1 is String);
-  Expect.equals('dynamic', rti.testingRtiToString(dynamicRti1));
+  Expect.isTrue(identical(rti1, rti2), 'dynamic should be identical');
+  Expect.isFalse(rti1 is String);
+  Expect.equals('dynamic', rti.testingRtiToString(rti1));
+}
+
+testVoid() {
+  var universe = rti.testingCreateUniverse();
+
+  var rti1 = rti.testingUniverseEval(universe, '~');
+  var rti2 = rti.testingUniverseEval(universe, ',,~,,');
+
+  Expect.isTrue(identical(rti1, rti2), 'void should be identical');
+  Expect.isFalse(rti1 is String);
+  Expect.equals('void', rti.testingRtiToString(rti1));
+}
+
+testNever() {
+  var universe = rti.testingCreateUniverse();
+
+  var rti1 = rti.testingUniverseEval(universe, '0&');
+  var rti2 = rti.testingUniverseEval(universe, '0&');
+
+  Expect.isTrue(identical(rti1, rti2), 'Never should be identical');
+  Expect.isFalse(rti1 is String);
+  Expect.equals('Never', rti.testingRtiToString(rti1));
+}
+
+testAny() {
+  var universe = rti.testingCreateUniverse();
+
+  var rti1 = rti.testingUniverseEval(universe, '1&');
+  var rti2 = rti.testingUniverseEval(universe, '1&');
+
+  Expect.isTrue(identical(rti1, rti2), "'any' should be identical");
+  Expect.isFalse(rti1 is String);
+  Expect.equals('any', rti.testingRtiToString(rti1));
+}
+
+testTerminal() {
+  var universe = rti.testingCreateUniverse();
+
+  var rti1 = rti.testingUniverseEval(universe, '@');
+  var rti2 = rti.testingUniverseEval(universe, '~');
+  var rti3 = rti.testingUniverseEval(universe, '0&');
+  var rti4 = rti.testingUniverseEval(universe, '1&');
+
+  Expect.isFalse(identical(rti1, rti2));
+  Expect.isFalse(identical(rti1, rti3));
+  Expect.isFalse(identical(rti1, rti4));
+  Expect.isFalse(identical(rti2, rti3));
+  Expect.isFalse(identical(rti2, rti4));
+  Expect.isFalse(identical(rti3, rti4));
 }
 
 testInterface1() {
@@ -76,6 +123,10 @@
 main() {
   testDynamic1();
   testDynamic2();
+  testVoid();
+  testNever();
+  testAny();
+  testTerminal();
   testInterface1();
   testInterface2();
   testInterface3();
diff --git a/tests/compiler/dart2js_extra/rti/subtype_test.dart b/tests/compiler/dart2js_extra/rti/subtype_test.dart
new file mode 100644
index 0000000..91fb5b5
--- /dev/null
+++ b/tests/compiler/dart2js_extra/rti/subtype_test.dart
@@ -0,0 +1,33 @@
+// Copyright (c) 2019, 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:_rti' as rti;
+import "package:expect/expect.dart";
+
+main() {
+  testCodeUnits();
+}
+
+void testCodeUnits() {
+  var universe = rti.testingCreateUniverse();
+
+  var intRule = rti.testingCreateRule();
+  rti.testingAddSupertype(intRule, 'num', []);
+
+  var listRule = rti.testingCreateRule();
+  rti.testingAddSupertype(listRule, 'Iterable', ['1']);
+
+  var codeUnitsRule = rti.testingCreateRule();
+  rti.testingAddSupertype(codeUnitsRule, 'List', ['int']);
+
+  rti.testingAddRule(universe, 'int', intRule);
+  rti.testingAddRule(universe, 'List', listRule);
+  rti.testingAddRule(universe, 'CodeUnits', codeUnitsRule);
+
+  var rti1 = rti.testingUniverseEval(universe, 'List<CodeUnits>');
+  var rti2 = rti.testingUniverseEval(universe, 'Iterable<List<int>>');
+
+  Expect.isTrue(rti.testingIsSubtype(universe, rti1, rti2));
+  Expect.isFalse(rti.testingIsSubtype(universe, rti2, rti1));
+}
diff --git a/tests/lib_2/html/debugger_test.dart b/tests/compiler/dartdevc_native/debugger/debugger_test.dart
similarity index 97%
rename from tests/lib_2/html/debugger_test.dart
rename to tests/compiler/dartdevc_native/debugger/debugger_test.dart
index cbe043a..da2e982 100644
--- a/tests/lib_2/html/debugger_test.dart
+++ b/tests/compiler/dartdevc_native/debugger/debugger_test.dart
@@ -127,8 +127,8 @@
   // Cache blocker is a workaround for:
   // https://code.google.com/p/dart/issues/detail?id=11834
   var cacheBlocker = new DateTime.now().millisecondsSinceEpoch;
-  var goldenUrl = '/root_dart/tests/lib_2/html/debugger_test_golden.txt'
-      '?cacheBlock=$cacheBlocker';
+  var goldenUrl = '/root_dart/tests/compiler/dartdevc_native/debugger/'
+      'debugger_test_golden.txt?cacheBlock=$cacheBlocker';
 
   String golden;
   try {
@@ -324,7 +324,7 @@
     if (actualStr != golden) {
       var helpMessage =
           'Debugger output does not match the golden data found in:\n'
-          'tests/lib_2/html/debugger_test_golden.txt\n'
+          'tests/compiler/dartdevc_native/debugger/debugger_test_golden.txt\n'
           'The new golden data is copied to the clipboard when you click on '
           'this window.\n'
           'Please update the golden file with the following output and review '
diff --git a/tests/lib_2/html/debugger_test_golden.txt b/tests/compiler/dartdevc_native/debugger/debugger_test_golden.txt
similarity index 97%
rename from tests/lib_2/html/debugger_test_golden.txt
rename to tests/compiler/dartdevc_native/debugger/debugger_test_golden.txt
index b18cfc5..0b12aa0 100644
--- a/tests/lib_2/html/debugger_test_golden.txt
+++ b/tests/compiler/dartdevc_native/debugger/debugger_test_golden.txt
@@ -1624,34 +1624,6 @@
             {
                 "style": "background-color: thistle; color: rgb(136, 19, 145); margin-right: -13px"
             },
-            "toString: "
-        ],
-        [
-            "span",
-            {
-                "style": "margin-left: 13px"
-            },
-            [
-                "object",
-                {
-                    "object": "<OBJECT>",
-                    "config": {
-                        "name": "none"
-                    }
-                }
-            ]
-        ]
-    ],
-    [
-        "li",
-        {
-            "style": "padding-left: 13px;"
-        },
-        [
-            "span",
-            {
-                "style": "background-color: thistle; color: rgb(136, 19, 145); margin-right: -13px"
-            },
             "where: "
         ],
         [
@@ -1708,34 +1680,6 @@
             {
                 "style": "background-color: thistle; color: rgb(136, 19, 145); margin-right: -13px"
             },
-            "_equals: "
-        ],
-        [
-            "span",
-            {
-                "style": "margin-left: 13px"
-            },
-            [
-                "object",
-                {
-                    "object": "<OBJECT>",
-                    "config": {
-                        "name": "none"
-                    }
-                }
-            ]
-        ]
-    ],
-    [
-        "li",
-        {
-            "style": "padding-left: 13px;"
-        },
-        [
-            "span",
-            {
-                "style": "background-color: thistle; color: rgb(136, 19, 145); margin-right: -13px"
-            },
             "_get: "
         ],
         [
@@ -3317,34 +3261,6 @@
             {
                 "style": "background-color: thistle; color: rgb(136, 19, 145); margin-right: -13px"
             },
-            "toString: "
-        ],
-        [
-            "span",
-            {
-                "style": "margin-left: 13px"
-            },
-            [
-                "object",
-                {
-                    "object": "<OBJECT>",
-                    "config": {
-                        "name": "none"
-                    }
-                }
-            ]
-        ]
-    ],
-    [
-        "li",
-        {
-            "style": "padding-left: 13px;"
-        },
-        [
-            "span",
-            {
-                "style": "background-color: thistle; color: rgb(136, 19, 145); margin-right: -13px"
-            },
             "where: "
         ],
         [
@@ -3401,34 +3317,6 @@
             {
                 "style": "background-color: thistle; color: rgb(136, 19, 145); margin-right: -13px"
             },
-            "_equals: "
-        ],
-        [
-            "span",
-            {
-                "style": "margin-left: 13px"
-            },
-            [
-                "object",
-                {
-                    "object": "<OBJECT>",
-                    "config": {
-                        "name": "none"
-                    }
-                }
-            ]
-        ]
-    ],
-    [
-        "li",
-        {
-            "style": "padding-left: 13px;"
-        },
-        [
-            "span",
-            {
-                "style": "background-color: thistle; color: rgb(136, 19, 145); margin-right: -13px"
-            },
             "_get: "
         ],
         [
diff --git a/tests/compiler/dartdevc_native/no_such_method_errors_test.dart b/tests/compiler/dartdevc_native/no_such_method_errors_test.dart
new file mode 100644
index 0000000..1298c38
--- /dev/null
+++ b/tests/compiler/dartdevc_native/no_such_method_errors_test.dart
@@ -0,0 +1,94 @@
+// Copyright (c) 2019, 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 "package:expect/expect.dart";
+
+_expectInErrorMessage(String expected, String actual) {
+  Expect.isTrue(
+      actual.contains(expected),
+      'Error message should contain "$expected", '
+      'but was ${actual.toString()}.');
+}
+
+class A {
+  int x = 42;
+  String arity1(int val) {
+    val += 10;
+    return val.toString();
+  }
+}
+
+String arity1(int val) {
+  val += 10;
+  return val.toString();
+}
+
+dynamic dynamicFunction = arity1;
+
+void main() {
+  dynamic instanceOfA = A();
+  // Call an instance of a class with no call() method.
+  try {
+    instanceOfA();
+  } on NoSuchMethodError catch (error) {
+    var message = error.toString();
+    _expectInErrorMessage("NoSuchMethodError: 'call'", message);
+    _expectInErrorMessage("Receiver: Instance of 'A'", message);
+  }
+
+  dynamic tearOff = instanceOfA.arity1;
+  // Dynamic call of a class method with too many arguments.
+  try {
+    tearOff(1, 2);
+  } on NoSuchMethodError catch (error) {
+    var message = error.toString();
+    _expectInErrorMessage("NoSuchMethodError: 'bound arity1'", message);
+    _expectInErrorMessage("too many arguments", message);
+  }
+
+  // Dynamic call of a class method with too few arguments.
+  try {
+    tearOff();
+  } on NoSuchMethodError catch (error) {
+    var message = error.toString();
+    _expectInErrorMessage("NoSuchMethodError: 'bound arity1'", message);
+    _expectInErrorMessage("too few arguments", message);
+  }
+
+  // Dynamic call of a top level funciton with too many arguments.
+  try {
+    dynamicFunction(1, 2);
+  } on NoSuchMethodError catch (error) {
+    var message = error.toString();
+    _expectInErrorMessage("NoSuchMethodError: 'arity1'", message);
+    _expectInErrorMessage("too many arguments", message);
+  }
+
+  // Dynamic call of a top level funciton with too few arguments.
+  try {
+    dynamicFunction();
+  } on NoSuchMethodError catch (error) {
+    var message = error.toString();
+    _expectInErrorMessage("NoSuchMethodError: 'arity1'", message);
+    _expectInErrorMessage("too few arguments", message);
+  }
+
+  // Function.apply() with too many arguments.
+  try {
+    Function.apply(dynamicFunction, [1, 2]);
+  } on NoSuchMethodError catch (error) {
+    var message = error.toString();
+    _expectInErrorMessage("NoSuchMethodError: 'arity1'", message);
+    _expectInErrorMessage("too many arguments", message);
+  }
+
+  // Function.apply() with too few arguments.
+  try {
+    Function.apply(dynamicFunction, []);
+  } on NoSuchMethodError catch (error) {
+    var message = error.toString();
+    _expectInErrorMessage("NoSuchMethodError: 'arity1'", message);
+    _expectInErrorMessage("too few arguments", message);
+  }
+}
diff --git a/tests/corelib_2/bigint_test.dart b/tests/corelib_2/bigint_test.dart
index 0b5b8c5..89b361b 100644
--- a/tests/corelib_2/bigint_test.dart
+++ b/tests/corelib_2/bigint_test.dart
@@ -1041,39 +1041,39 @@
 
 main() {
   for (int i = 0; i < 8; i++) {
-    Expect.equals(BigInt.parse("1234567890123456789"), foo()); /// 01: ok
-    Expect.equals(BigInt.parse("12345678901234567890"), bar()); /// 02: ok
-    testModPow(); /// 03: ok
-    testModInverse(); /// 04: ok
-    testGcd(); /// 05: ok
-    testSmiOverflow(); /// 06: ok
-    testBigintAnd(); /// 07: ok
-    testBigintOr(); /// 08: ok
-    testBigintXor(); /// 09: ok
-    testBigintAdd(); /// 10: ok
-    testBigintSub(); /// 11: ok
-    testBigintMul(); /// 12: ok
-    testBigintTruncDiv(); /// 12: ok
-    testBigintDiv(); /// 13: ok
-    testBigintModulo(); /// 14: ok
-    testBigintModPow(); /// 15: ok
-    testBigintModInverse(); /// 16: ok
-    testBigintGcd(); /// 17: ok
-    testBigintNegate(); /// 18: ok
-    testShiftAmount(); /// 19: ok
-    testPow(); /// 20: ok
-    testToRadixString(); /// 21: ok
-    testToString(); /// 22: ok
-    testFromToInt(); /// 23: ok
-    testFromToDouble(); /// 24: ok
-    Expect.equals(BigInt.parse("12345678901234567890"), /// 25: ok
-        BigInt.parse("12345678901234567890").abs()); /// 25: ok
-    Expect.equals(BigInt.parse("12345678901234567890"), /// 26: ok
-        BigInt.parse("-12345678901234567890").abs()); /// 26: ok
-    var a = BigInt.parse("10000000000000000000"); /// 27: ok
-    var b = BigInt.parse("10000000000000000001"); /// 27: ok
-    Expect.equals(false, a.hashCode == b.hashCode); /// 27: ok
-    Expect.equals(true, a.hashCode == (b - BigInt.one).hashCode); /// 27: ok
+    Expect.equals(BigInt.parse("1234567890123456789"), foo()); //# 01: ok
+    Expect.equals(BigInt.parse("12345678901234567890"), bar()); //# 02: ok
+    testModPow(); //# 03: ok
+    testModInverse(); //# 04: ok
+    testGcd(); //# 05: ok
+    testSmiOverflow(); //# 06: ok
+    testBigintAnd(); //# 07: ok
+    testBigintOr(); //# 08: ok
+    testBigintXor(); //# 09: ok
+    testBigintAdd(); //# 10: ok
+    testBigintSub(); //# 11: ok
+    testBigintMul(); //# 12: ok
+    testBigintTruncDiv(); //# 12: ok
+    testBigintDiv(); //# 13: ok
+    testBigintModulo(); //# 14: ok
+    testBigintModPow(); //# 15: ok
+    testBigintModInverse(); //# 16: ok
+    testBigintGcd(); //# 17: ok
+    testBigintNegate(); //# 18: ok
+    testShiftAmount(); //# 19: ok
+    testPow(); //# 20: ok
+    testToRadixString(); //# 21: ok
+    testToString(); //# 22: ok
+    testFromToInt(); //# 23: ok
+    testFromToDouble(); //# 24: ok
+    Expect.equals(BigInt.parse("12345678901234567890"), //# 25: ok
+        BigInt.parse("12345678901234567890").abs()); //# 25: ok
+    Expect.equals(BigInt.parse("12345678901234567890"), //# 26: ok
+        BigInt.parse("-12345678901234567890").abs()); //# 26: ok
+    var a = BigInt.parse("10000000000000000000"); //# 27: ok
+    var b = BigInt.parse("10000000000000000001"); //# 27: ok
+    Expect.equals(false, a.hashCode == b.hashCode); //# 27: ok
+    Expect.equals(true, a.hashCode == (b - BigInt.one).hashCode); //# 27: ok
 
     // Regression test for http://dartbug.com/36105
     var overbig = -BigInt.from(10).pow(309);
diff --git a/tests/corelib_2/uri_http_test.dart b/tests/corelib_2/uri_http_test.dart
index 60d9732..399c403 100644
--- a/tests/corelib_2/uri_http_test.dart
+++ b/tests/corelib_2/uri_http_test.dart
@@ -33,6 +33,11 @@
       new Uri.http("host", "/a/b", {"c=": "&d"}), "http://host/a/b?c%3D=%26d");
   check(new Uri.http("[::]", "a"), "http://[::]/a");
   check(new Uri.http("[::127.0.0.1]", "a"), "http://[::127.0.0.1]/a");
+  check(new Uri.http('[fe80::8eae:4c4d:fee9:8434%rename3]', ''),
+      'http://[fe80::8eae:4c4d:fee9:8434%25rename3]');
+  check(new Uri.http('[ff02::1%1%41]', ''), 'http://[ff02::1%251a]');
+  check(new Uri.http('[ff02::1%321]', ''), 'http://[ff02::1%25321]');
+  check(new Uri.http('[ff02::1%%321]', ''), 'http://[ff02::1%2521]');
 }
 
 testHttpsUri() {
diff --git a/tests/corelib_2/uri_ipv6_test.dart b/tests/corelib_2/uri_ipv6_test.dart
index e6a7a4d..e5dbb9df 100644
--- a/tests/corelib_2/uri_ipv6_test.dart
+++ b/tests/corelib_2/uri_ipv6_test.dart
@@ -104,6 +104,76 @@
   Expect.equals(80, uri.port);
   Expect.equals('', uri.path);
   Expect.equals(path.toLowerCase(), uri.toString());
+
+  // Checks for ZoneID in RFC 6874
+  path = 'https://[fe80::a%en1]:443/index.html';
+  uri = Uri.parse(path);
+  Expect.equals('https', uri.scheme);
+  Expect.equals('fe80::a%25en1', uri.host);
+  Expect.equals(443, uri.port);
+  Expect.equals('/index.html', uri.path);
+  Expect.equals('https://[fe80::a%25en1]/index.html', uri.toString());
+
+  path = 'https://[fe80::a%25eE1]:443/index.html';
+  uri = Uri.parse(path);
+  Expect.equals('https', uri.scheme);
+  Expect.equals('fe80::a%25eE1', uri.host);
+  Expect.equals(443, uri.port);
+  Expect.equals('/index.html', uri.path);
+  Expect.equals('https://[fe80::a%25eE1]/index.html', uri.toString());
+
+  // Recognize bare '%' and transform into '%25'
+  path = 'https://[fe80::a%1]:443/index.html';
+  uri = Uri.parse(path);
+  Expect.equals('https', uri.scheme);
+  Expect.equals('fe80::a%251', uri.host);
+  Expect.equals(443, uri.port);
+  Expect.equals('/index.html', uri.path);
+  Expect.equals('https://[fe80::a%251]/index.html', uri.toString());
+
+  path = 'https://[ff02::5678%pvc1.3]/index.html';
+  uri = Uri.parse(path);
+  Expect.equals('https', uri.scheme);
+  Expect.equals('ff02::5678%25pvc1.3', uri.host);
+  Expect.equals('/index.html', uri.path);
+  Expect.equals('https://[ff02::5678%25pvc1.3]/index.html', uri.toString());
+
+  // ZoneID contains percent encoded
+  path = 'https://[ff02::1%%321]/index.html';
+  uri = Uri.parse(path);
+  Expect.equals('https', uri.scheme);
+  Expect.equals('ff02::1%2521', uri.host);
+  Expect.equals('/index.html', uri.path);
+  Expect.equals('https://[ff02::1%2521]/index.html', uri.toString());
+
+  path = 'https://[ff02::1%321]/index.html';
+  uri = Uri.parse(path);
+  Expect.equals('https', uri.scheme);
+  Expect.equals('ff02::1%25321', uri.host);
+  Expect.equals('/index.html', uri.path);
+  Expect.equals('https://[ff02::1%25321]/index.html', uri.toString());
+
+  // Lower cases
+  path = 'https://[ff02::1%1%41]/index.html';
+  uri = Uri.parse(path);
+  Expect.equals('https', uri.scheme);
+  Expect.equals('ff02::1%251a', uri.host);
+  Expect.equals('/index.html', uri.path);
+  Expect.equals('https://[ff02::1%251a]/index.html', uri.toString());
+
+  path = 'https://[fe80::8eae:4c4d:fee9:8434%rename3]/index.html';
+  uri = Uri.parse(path);
+  Expect.equals('https', uri.scheme);
+  Expect.equals('fe80::8eae:4c4d:fee9:8434%25rename3', uri.host);
+  Expect.equals('/index.html', uri.path);
+  Expect.equals('https://[fe80::8eae:4c4d:fee9:8434%25rename3]/index.html',
+      uri.toString());
+
+  // Test construtors with host name
+  uri = Uri(scheme: 'https', host: '[ff02::5678%pvc1.3]');
+  uri = Uri(scheme: 'https', host: '[fe80::a%1]');
+  uri = Uri(scheme: 'https', host: '[fe80::a%25eE1]');
+  uri = Uri(scheme: 'https', host: '[fe80::a%en1]');
 }
 
 void testParseIPv6Address() {
diff --git a/tests/ffi/ffi.status b/tests/ffi/ffi.status
index 0e982e4..1fa9e5b 100644
--- a/tests/ffi/ffi.status
+++ b/tests/ffi/ffi.status
@@ -2,45 +2,44 @@
 # 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.
 
-[ $runtime != dart_precompiled && $runtime != vm ]
-*: SkipByDesign # FFI is a VM-only feature. (This test suite is part of the default set.)
+[ $arch == simdbc ]
+*: Skip # FFI not yet supported on SimDBC32: dartbug.com/36809
+
+[ $arch == simdbc64 ]
+function_callbacks_test: Skip # Issue 37140
 
 [ $builder_tag == asan ]
 data_not_asan_test: SkipByDesign # This test tries to allocate too much memory on purpose.
 
-# dartbug.com/35768: Structs not supported on 32-bit.
-[ $arch == ia32 || $arch == arm || $arch == simdbc ]
-function_structs_test: Skip
-structs_test: Skip
-
-# dartbug.com/35934
+# These tests trigger and catch an abort (intentionally) and terminate the VM
+# before it can generate a snapshot.
 [ $compiler == app_jitk ]
-dynamic_library_test: Skip
-function_callbacks_test: Skip
-function_structs_test: Skip
-function_test: Skip
-negative_function_test: Skip
-
-[ $arch == x64 || $arch == arm64 || $arch == simdbc64 ]
-enable_structs_test: SkipByDesign  # Tests that structs don't work on 32-bit systems.
+function_callbacks_test/01: Skip
+function_callbacks_test/02: Skip
+function_callbacks_test/03: Skip
 
 [ $runtime == dart_precompiled ]
 *: Skip # AOT is not yet supported: dartbug.com/35765
 
-[ $arch == simarm || $arch == simarm64 ]
-*: Skip # FFI not yet supported on the arm simulator.
-
-[ $arch == simdbc ]
-*: Skip # FFI not yet supported on SimDBC32: dartbug.com/36809
+[ $arch == arm && $system != android ]
+*: Skip # "hardfp" calling convention is not yet supported (iOS is also supported but not tested): dartbug.com/36309
 
 [ $arch == simdbc64 && $system != linux && $system != macos ]
 *: Skip # FFI not yet supported outside x64 Linux: dartbug.com/36809
 
+[ $runtime != dart_precompiled && $runtime != vm ]
+*: SkipByDesign # FFI is a VM-only feature. (This test suite is part of the default set.)
+
 [ $system != android && $system != linux && $system != macos && $system != windows ]
 *: Skip # FFI not yet supported on other OSes.
 
-[ $system != android && $arch == arm ]
-*: Skip # "hardfp" calling convention is not yet supported (iOS is also supported but not tested): dartbug.com/36309
+# dartbug.com/35768: Structs not supported on 32-bit.
+[ $arch == arm || $arch == ia32 || $arch == simdbc ]
+function_structs_test: Skip
+structs_test: Skip
 
-[ $arch == simdbc64 ]
-function_callbacks_test: Skip  # Issue 37140
+[ $arch == arm64 || $arch == simdbc64 || $arch == x64 ]
+enable_structs_test: SkipByDesign # Tests that structs don't work on 32-bit systems.
+
+[ $arch == simarm || $arch == simarm64 ]
+*: Skip # FFI not yet supported on the arm simulator.
diff --git a/tests/ffi/ffi_test_helpers.dart b/tests/ffi/ffi_test_helpers.dart
new file mode 100644
index 0000000..5d14d4a
--- /dev/null
+++ b/tests/ffi/ffi_test_helpers.dart
@@ -0,0 +1,16 @@
+// Copyright (c) 2019, 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.
+//
+// Helpers for tests which trigger GC in delicate places.
+
+import 'dart:ffi' as ffi;
+import 'dylib_utils.dart';
+
+typedef NativeNullaryOp = ffi.Void Function();
+typedef NullaryOpVoid = void Function();
+
+final ffi.DynamicLibrary ffiTestFunctions =
+    dlopenPlatformSpecific("ffi_test_functions");
+final triggerGc = ffiTestFunctions
+    .lookupFunction<NativeNullaryOp, NullaryOpVoid>("TriggerGC");
diff --git a/tests/ffi/function_callbacks_test.dart b/tests/ffi/function_callbacks_test.dart
index fc2e46c..b3ddc9a 100644
--- a/tests/ffi/function_callbacks_test.dart
+++ b/tests/ffi/function_callbacks_test.dart
@@ -4,6 +4,7 @@
 //
 // Dart test program for testing dart:ffi function pointers with callbacks.
 //
+// VMOptions=--enable-testing-pragmas
 // SharedObjects=ffi_test_functions
 
 library FfiTest;
@@ -15,6 +16,8 @@
 
 import "package:expect/expect.dart";
 
+import 'ffi_test_helpers.dart';
+
 typedef NativeCallbackTest = Int32 Function(Pointer);
 typedef NativeCallbackTestFn = int Function(Pointer);
 
@@ -147,6 +150,10 @@
 typedef ReturnVoid = Void Function();
 void returnVoid() {}
 
+void testGC() {
+  triggerGc();
+}
+
 final List<Test> testcases = [
   Test("SimpleAddition", fromFunction<SimpleAdditionType>(simpleAddition)),
   Test("IntComputation", fromFunction<IntComputationType>(intComputation)),
@@ -160,6 +167,7 @@
   Test("Store", fromFunction<StoreType>(store)),
   Test("NullPointers", fromFunction<NullPointersType>(nullPointers)),
   Test("ReturnNull", fromFunction<ReturnNullType>(returnNull)),
+  Test("GC", fromFunction<ReturnVoid>(testGC)),
 ];
 
 testCallbackWrongThread() =>
diff --git a/tests/ffi/function_gc_test.dart b/tests/ffi/function_gc_test.dart
index 1799ce1..8fa794d 100644
--- a/tests/ffi/function_gc_test.dart
+++ b/tests/ffi/function_gc_test.dart
@@ -16,6 +16,7 @@
 import 'dart:ffi' as ffi;
 import 'dylib_utils.dart';
 import "package:expect/expect.dart";
+import 'ffi_test_helpers.dart';
 
 main() async {
   testBoxInt64();
@@ -23,23 +24,32 @@
   testBoxDouble();
   testBoxPointer();
   testAllocateInNative();
-  testAllocateInDart();
+  testRegress37069();
 }
 
-ffi.DynamicLibrary ffiTestFunctions =
-    dlopenPlatformSpecific("ffi_test_functions");
-
 typedef NativeNullaryOp64 = ffi.Int64 Function();
 typedef NativeNullaryOp32 = ffi.Int32 Function();
 typedef NativeNullaryOpDouble = ffi.Double Function();
 typedef NativeNullaryOpPtr = ffi.Pointer<ffi.Void> Function();
-typedef NativeNullaryOp = ffi.Void Function();
 typedef NativeUnaryOp = ffi.Void Function(ffi.Uint64);
+typedef NativeUndenaryOp = ffi.Uint64 Function(
+    ffi.Uint64,
+    ffi.Uint64,
+    ffi.Uint64,
+    ffi.Uint64,
+    ffi.Uint64,
+    ffi.Uint64,
+    ffi.Uint64,
+    ffi.Uint64,
+    ffi.Uint64,
+    ffi.Uint64,
+    ffi.Uint64);
 typedef NullaryOp = int Function();
 typedef NullaryOpDbl = double Function();
 typedef NullaryOpPtr = ffi.Pointer<ffi.Void> Function();
 typedef UnaryOp = void Function(int);
-typedef NullaryOpVoid = void Function();
+typedef UndenaryOp = int Function(
+    int, int, int, int, int, int, int, int, int, int, int);
 
 //// These functions return values that require boxing into different types.
 
@@ -82,19 +92,17 @@
   }
 }
 
-final triggerGc = ffiTestFunctions
-    .lookupFunction<NativeNullaryOp, NullaryOpVoid>("TriggerGC");
-
 // Test GC in the FFI call path by calling a C function which triggers GC
 // directly.
 void testAllocateInNative() => triggerGc();
 
-@pragma("vm:entry-point", "call")
-void testAllocationsInDartHelper() => triggerGc();
+// This also works as a regression test for 37176.
 
-final allocateThroughDart = ffiTestFunctions
-    .lookupFunction<NativeNullaryOp, NullaryOpVoid>("AllocateThroughDart");
+final regress37069 = ffiTestFunctions
+    .lookupFunction<NativeUndenaryOp, UndenaryOp>("Regress37069");
 
-// Test GC in the FFI call path by calling a C function which allocates by
-// calling back into Dart ('testAllocationsInDartHelper').
-void testAllocateInDart() => allocateThroughDart();
+// Test GC in the FFI call path by calling a C function which triggers GC
+// directly.
+void testRegress37069() {
+  regress37069(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
+}
diff --git a/tests/ffi/function_test.dart b/tests/ffi/function_test.dart
index 014c66e..2865d30 100644
--- a/tests/ffi/function_test.dart
+++ b/tests/ffi/function_test.dart
@@ -29,6 +29,7 @@
     testNativeFunctionManyArguments1();
     testNativeFunctionManyArguments2();
     testNativeFunctionManyArguments3();
+    testNativeFunctionManyArguments4();
     testNativeFunctionPointer();
     testNullInt();
     testNullDouble();
@@ -225,7 +226,7 @@
   Expect.approxEquals(1337.0, times1_337Float(1000.0));
 }
 
-typedef NativeOctenaryOp = ffi.IntPtr Function(
+typedef NativeDecenaryOp = ffi.IntPtr Function(
     ffi.IntPtr,
     ffi.IntPtr,
     ffi.IntPtr,
@@ -236,17 +237,39 @@
     ffi.IntPtr,
     ffi.IntPtr,
     ffi.IntPtr);
-typedef OctenaryOp = int Function(
+typedef DecenaryOp = int Function(
     int, int, int, int, int, int, int, int, int, int);
 
-OctenaryOp sumManyInts = ffiTestFunctions
-    .lookupFunction<NativeOctenaryOp, OctenaryOp>("SumManyInts");
+DecenaryOp sumManyInts = ffiTestFunctions
+    .lookupFunction<NativeDecenaryOp, DecenaryOp>("SumManyInts");
 
 void testNativeFunctionManyArguments1() {
   Expect.equals(55, sumManyInts(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
 }
 
-typedef NativeDoubleOctenaryOp = ffi.Double Function(
+typedef NativeUndenaryOp = ffi.IntPtr Function(
+    ffi.IntPtr,
+    ffi.IntPtr,
+    ffi.IntPtr,
+    ffi.IntPtr,
+    ffi.IntPtr,
+    ffi.IntPtr,
+    ffi.IntPtr,
+    ffi.IntPtr,
+    ffi.IntPtr,
+    ffi.IntPtr,
+    ffi.IntPtr);
+typedef UndenaryOp = int Function(
+    int, int, int, int, int, int, int, int, int, int, int);
+
+UndenaryOp sumManyIntsOdd = ffiTestFunctions
+    .lookupFunction<NativeUndenaryOp, UndenaryOp>("SumManyIntsOdd");
+
+void testNativeFunctionManyArguments4() {
+  Expect.equals(66, sumManyIntsOdd(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11));
+}
+
+typedef NativeDoubleDecenaryOp = ffi.Double Function(
     ffi.Double,
     ffi.Double,
     ffi.Double,
@@ -257,11 +280,11 @@
     ffi.Double,
     ffi.Double,
     ffi.Double);
-typedef DoubleOctenaryOp = double Function(double, double, double, double,
+typedef DoubleDecenaryOp = double Function(double, double, double, double,
     double, double, double, double, double, double);
 
-DoubleOctenaryOp sumManyDoubles = ffiTestFunctions
-    .lookupFunction<NativeDoubleOctenaryOp, DoubleOctenaryOp>("SumManyDoubles");
+DoubleDecenaryOp sumManyDoubles = ffiTestFunctions
+    .lookupFunction<NativeDoubleDecenaryOp, DoubleDecenaryOp>("SumManyDoubles");
 
 void testNativeFunctionManyArguments2() {
   Expect.approxEquals(
diff --git a/tests/ffi/gc_helpers.dart b/tests/ffi/gc_helpers.dart
new file mode 100644
index 0000000..5d14d4a
--- /dev/null
+++ b/tests/ffi/gc_helpers.dart
@@ -0,0 +1,16 @@
+// Copyright (c) 2019, 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.
+//
+// Helpers for tests which trigger GC in delicate places.
+
+import 'dart:ffi' as ffi;
+import 'dylib_utils.dart';
+
+typedef NativeNullaryOp = ffi.Void Function();
+typedef NullaryOpVoid = void Function();
+
+final ffi.DynamicLibrary ffiTestFunctions =
+    dlopenPlatformSpecific("ffi_test_functions");
+final triggerGc = ffiTestFunctions
+    .lookupFunction<NativeNullaryOp, NullaryOpVoid>("TriggerGC");
diff --git a/tests/ffi/snapshot_test.dart b/tests/ffi/snapshot_test.dart
new file mode 100644
index 0000000..02b4cc4
--- /dev/null
+++ b/tests/ffi/snapshot_test.dart
@@ -0,0 +1,24 @@
+// Copyright (c) 2019, 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.
+//
+// Checks that the VM throws an appropriate exception when FFI objects are
+// passed between isolates.
+
+import 'dart:async';
+import 'dart:ffi';
+import 'dart:io';
+import 'dart:isolate';
+
+import 'package:expect/expect.dart';
+
+main(args) async {
+  try {
+    await Isolate.spawn(print, fromAddress<Pointer<Void>>(1));
+  } catch (e) {
+    Expect.type<ArgumentError>(e);
+    return;
+  }
+
+  throw "Test didn't throw an exception!";
+}
diff --git a/tests/language_2/compile_time_constant13_test.dart b/tests/language_2/compile_time_constant13_test.dart
index 970e805..189abbd 100644
--- a/tests/language_2/compile_time_constant13_test.dart
+++ b/tests/language_2/compile_time_constant13_test.dart
@@ -3,18 +3,18 @@
 // BSD-style license that can be found in the LICENSE file.
 
 class A {
-  final x; //      //# 01: ok
-  /// 02: compile-time error
-  var x; //        //# 03: compile-time error
+  final x; //# 01: ok
+  //# 02: compile-time error
+  var x; //# 03: compile-time error
   get x => null; //# 04: compile-time error
-  set x(v) {} //   //# 05: compile-time error
+  set x(v) {} //# 05: compile-time error
 
   const A()
-    : x = 'foo' // //# 01: continued
-    : x = 'foo' // //# 02: continued
-    : x = 'foo' // //# 03: continued
-    : x = 'foo' // //# 04: continued
-    : x = 'foo' // //# 05: continued
+    : x = 'foo' //# 01: continued
+    : x = 'foo' //# 02: continued
+    : x = 'foo' //# 03: continued
+    : x = 'foo' //# 04: continued
+    : x = 'foo' //# 05: continued
   ;
 }
 
diff --git a/tests/language_2/function_type_in_constant_test.dart b/tests/language_2/function_type_in_constant_test.dart
index 53aed9c..ca9db12 100644
--- a/tests/language_2/function_type_in_constant_test.dart
+++ b/tests/language_2/function_type_in_constant_test.dart
@@ -18,12 +18,6 @@
 
 main() {
   test(const A<int Function()>(), const A<String Function()>());
-
-  /// 01: ok
   test(const A<int>(), const A<String Function()>());
-
-  /// 02: ok
   test(const A<int Function()>(), const A<String>());
-
-  /// 03: ok
 }
diff --git a/tests/language_2/language_2.status b/tests/language_2/language_2.status
index 921c002..3741571 100644
--- a/tests/language_2/language_2.status
+++ b/tests/language_2/language_2.status
@@ -21,12 +21,6 @@
 mixin_class_from_core_library_test: Fail # Issue 34488
 nested_generic_closure_test: Fail # Issue 28515
 
-[ $compiler != compare_analyzer_cfe && $compiler != spec_parser ]
-mixin_constructor_forwarding/const_constructor_test/none: CompileTimeError # Issue 32223
-mixin_constructor_forwarding/const_constructor_with_field_test/none: CompileTimeError # Issue 32223
-mixin_constructor_forwarding/optional_named_parameters_test/none: CompileTimeError # Issue 31543
-mixin_constructor_forwarding/optional_positional_parameters_test/none: CompileTimeError # Issue 31543
-
 [ $compiler != dart2analyzer ]
 switch_case_warn_test: Skip # Analyzer only, see language_analyzer2.status
 
@@ -74,6 +68,12 @@
 compile_time_constant_static5_test/23: CompileTimeError # Issue 30546
 type_promotion_more_specific_test/04: CompileTimeError # Issue 30906.
 
+[ $compiler != compare_analyzer_cfe && $compiler != spec_parser ]
+mixin_constructor_forwarding/const_constructor_test/none: CompileTimeError # Issue 32223
+mixin_constructor_forwarding/const_constructor_with_field_test/none: CompileTimeError # Issue 32223
+mixin_constructor_forwarding/optional_named_parameters_test/none: CompileTimeError # Issue 31543
+mixin_constructor_forwarding/optional_positional_parameters_test/none: CompileTimeError # Issue 31543
+
 # Detection of compile-time errors that are related to constants can't be fully
 # done at the front end, because constants are evaluated at back ends.  So, some
 # errors aren't detected by fasta, but reported by back ends as compile-time
diff --git a/tests/language_2/language_2_dart2js.status b/tests/language_2/language_2_dart2js.status
index 6c03694..d6403c5 100644
--- a/tests/language_2/language_2_dart2js.status
+++ b/tests/language_2/language_2_dart2js.status
@@ -114,8 +114,8 @@
 issue31596_super_test/03: CompileTimeError
 left_shift_test: RuntimeError # non JS number semantics
 library_env_test/has_io_support: RuntimeError, OK # dart2js doesn't support io when compiling on --categories=Client
-library_env_test/has_mirror_support: Fail # mirrors not supported on web
 library_env_test/has_mirror_support: RuntimeError
+library_env_test/has_mirror_support: Fail # mirrors not supported on web
 library_env_test/has_no_html_support: RuntimeError, OK
 library_env_test/has_no_mirror_support: Pass # fails for the wrong reason.
 list_test: CompileTimeError, OK # Error if web int literal cannot be represented exactly, see http://dartbug.com/33351
diff --git a/tests/language_2/language_2_dartdevc.status b/tests/language_2/language_2_dartdevc.status
index 63b4104..26643dc 100644
--- a/tests/language_2/language_2_dartdevc.status
+++ b/tests/language_2/language_2_dartdevc.status
@@ -259,8 +259,8 @@
 regress_24283_test: RuntimeError # Expect.equals(expected: <-1>, actual: <4294967295>) fails.
 regress_29025_test: CompileTimeError
 regress_29405_test: CompileTimeError # Issue 31402 Error: A value of type '#lib2::Foo' can't be assigned to a variable of type '(#lib2::Foo) → void'.
-regress_30339_test: RuntimeError # Uncaught Expect.isTrue(false) fails.
 regress_30339_test: CompileTimeError
+regress_30339_test: RuntimeError # Uncaught Expect.isTrue(false) fails.
 setter_no_getter_test/01: CompileTimeError
 super_bound_closure_test/none: CompileTimeError # Issue 31533
 super_call4_test/01: MissingCompileTimeError
diff --git a/tests/language_2/language_2_kernel.status b/tests/language_2/language_2_kernel.status
index 1a54ab1..5953301 100644
--- a/tests/language_2/language_2_kernel.status
+++ b/tests/language_2/language_2_kernel.status
@@ -1035,10 +1035,12 @@
 [ ($arch == simarm || $arch == simarm64 || $arch == simdbc64) && ($compiler == dartk || $compiler == dartkb) ]
 least_upper_bound_expansive_test/none: RuntimeError # Please triage.
 
+[ ($compiler == app_jitk || $compiler == dartk || $compiler == dartkb || $compiler == dartkp) && ($runtime == dart_precompiled || $runtime == vm) ]
+type_alias_equality_test/04: RuntimeError # Issue 32783
+
 [ ($compiler == app_jitk || $compiler == dartk || $compiler == dartkp) && ($runtime == dart_precompiled || $runtime == vm) ]
 covariant_subtyping_with_mixin_test: RuntimeError # Issue 34321
 type_alias_equality_test/03: RuntimeError # Issue 32783
-type_alias_equality_test/04: RuntimeError # Issue 32783
 
 [ ($compiler == dartk || $compiler == dartkb) && ($hot_reload || $hot_reload_rollback) ]
 async_star_test/01: Skip # Timeout
diff --git a/tests/language_2/nnbd/static_errors/equals_parameter_made_nullable_at_invoke_test.dart b/tests/language_2/nnbd/static_errors/equals_parameter_made_nullable_at_invoke_test.dart
index 35e756e..9757f79 100644
--- a/tests/language_2/nnbd/static_errors/equals_parameter_made_nullable_at_invoke_test.dart
+++ b/tests/language_2/nnbd/static_errors/equals_parameter_made_nullable_at_invoke_test.dart
@@ -20,14 +20,14 @@
 // operator==, but it should not be an error to "assign null" to the parameter
 // of the comparison operator.
 main() {
-  Object o;
+  Object o = 0;
   // Valid comparison.
   o == null;
 
   // Caveat: it is NOT that the argument is promoted to non-null. Otherwise,
   // types which we can't cleanly promote, such as FutureOr<int?>, would not be
   // assignable in comparisons.
-  FutureOr<int?> foInt;
+  FutureOr<int?> foInt = Future.value(0);
 
   // Valid comparison.
   o == foInt;
diff --git a/tests/language_2/nnbd/static_errors/missing_default_value_for_parameter_test.dart b/tests/language_2/nnbd/static_errors/missing_default_value_for_parameter_test.dart
index ff664b7..0f72ece 100644
--- a/tests/language_2/nnbd/static_errors/missing_default_value_for_parameter_test.dart
+++ b/tests/language_2/nnbd/static_errors/missing_default_value_for_parameter_test.dart
@@ -30,7 +30,7 @@
 }
 typedef void f13({String});
 void printToLog(void f({String})) {}
-void Function({String s}) f14;
+void Function({String s})? f14;
 
 class B<T extends Object?> {
   // Potentially non-nullable types
diff --git a/tests/language_2/nnbd/static_errors/not_initialized_non_nullable_top_test.dart b/tests/language_2/nnbd/static_errors/not_initialized_non_nullable_top_test.dart
new file mode 100644
index 0000000..28e55e4
--- /dev/null
+++ b/tests/language_2/nnbd/static_errors/not_initialized_non_nullable_top_test.dart
@@ -0,0 +1,18 @@
+// Copyright (c) 2019, 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.
+
+// SharedOptions=--enable-experiment=non-nullable
+
+// Test that it is an error if a top level variable with non-nullable type has
+// no initializer expression.
+void main() {}
+
+int v; //# 01: compile-time error
+int v = 0; //# 02: ok
+int? v; //# 03: ok
+int? v = 0; //# 04: ok
+dynamic v; //# 05: ok
+var v; //# 06: ok
+void v; //# 07: ok
+Never v; //# 08: compile-time error
diff --git a/tests/language_2/nnbd/static_errors/not_initialized_potentially_non_nullable_local_test.dart b/tests/language_2/nnbd/static_errors/not_initialized_potentially_non_nullable_local_test.dart
new file mode 100644
index 0000000..d4b2207
--- /dev/null
+++ b/tests/language_2/nnbd/static_errors/not_initialized_potentially_non_nullable_local_test.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2019, 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.
+
+// SharedOptions=--enable-experiment=non-nullable
+
+// It is an error if a potentially non-nullable local variable which has no
+// initializer expression and is not marked `late` is used before it is
+// definitely assigned.
+// TODO(scheglov) Update once we implement definite assignment analysis.
+
+void main() {
+  int v; //# 01: compile-time error
+  int v = 0; //# 02: ok
+  late int v; //# 03: ok
+  late int v = 0; //# 04: ok
+  int? v; //# 05: ok
+  int? v = 0; //# 06: ok
+
+}
+
+f<T>(T a) {
+  T v; //# 07: compile-time error
+  T v = a; //# 08: ok
+  late T v; //# 09: ok
+  late T v = a; //# 10: ok
+  T? v; //# 11: ok
+  T? v = a; //# 12: ok
+}
diff --git a/tests/language_2/nnbd/static_errors/unchecked_use_of_nullable_test.dart b/tests/language_2/nnbd/static_errors/unchecked_use_of_nullable_test.dart
index a73735c..9043d0e 100644
--- a/tests/language_2/nnbd/static_errors/unchecked_use_of_nullable_test.dart
+++ b/tests/language_2/nnbd/static_errors/unchecked_use_of_nullable_test.dart
@@ -10,7 +10,7 @@
   bool? cond;
   List? list;
   Function? func;
-  List<Function?> funcList;
+  List<Function?> funcList = [];
   Stream? stream;
   x.isEven; //# 00: compile-time error
   x.round(); //# 01: compile-time error
@@ -53,11 +53,13 @@
   for(var i in list) {}; //# 36: compile-time error
   await for(var i in stream) {}; //# 37: compile-time error
   assert(cond); //# 38: compile-time error
+  [...list]; //# 39: compile-time error
+  [...?list]; //# 40: ok
 }
 
 generator() sync* {
   Iterable? iter;
-  yield* iter; //# 39: compile-time error
+  yield* iter; //# 41: compile-time error
 }
 
 void typeParametersNullableBounds<IQ extends int?, BQ extends bool?, LQ extends List?, FQ extends Function?, SQ extends Stream?>(
@@ -68,47 +70,47 @@
     List<FQ> funcList,
     SQ stream,
     ) async {
-  x.isEven; //# 40: compile-time error
-  x.round(); //# 41: compile-time error
-  x.toString(); //# 42: ok
-  x.hashCode; //# 43: ok
-  x.runtimeType; //# 44: ok
-  x.noSuchMethod(Invocation.method(#toString, [])); //# 45: ok
-  x + 1; //# 46: compile-time error
-  -x; //# 47: compile-time error
-  x++; //# 48: compile-time error
-  ++x; //# 49: compile-time error
-  x..isEven; //# 50: compile-time error
-  list[0]; //# 51: compile-time error
-  list[0] = 0; //# 52: compile-time error
-  x += 1; //# 53: compile-time error
-  x ??= x; //# 54: ok
-  x.round; //# 55: compile-time error
-  x.toString; //# 56: ok
-  x.noSuchMethod; //# 57: ok
-  func(); //# 58: compile-time error
-  funcList[0](); //# 59: compile-time error
-  funcList.single(); //# 60: compile-time error
-  throw x; //# 61: compile-time error
-  cond || true; //# 62: compile-time error
-  true || cond; //# 63: compile-time error
-  cond && true; //# 64: compile-time error
-  true && cond; //# 65: compile-time error
-  !cond; //# 66: compile-time error
-  cond ? null : null; //# 67: compile-time error
-  if (cond) {} //# 68: compile-time error
-  while (cond) {} //# 69: compile-time error
-  for (;cond;){} //# 70: compile-time error
-  do {} while (cond); //# 71: compile-time error
-  cond!; //# 72: ok
-  cond ?? null; //# 73: ok
-  cond == null; //# 74: ok
-  cond != null; //# 75: ok
-  x?.isEven; //# 76: ok
-  x?.round(); //# 77: ok
-  for(var i in list) {}; //# 78: compile-time error
-  await for(var i in stream) {}; //# 79: compile-time error
-  assert(cond); //# 39: compile-time error
+  x.isEven; //# 42: compile-time error
+  x.round(); //# 43: compile-time error
+  x.toString(); //# 44: ok
+  x.hashCode; //# 45: ok
+  x.runtimeType; //# 46: ok
+  x.noSuchMethod(Invocation.method(#toString, [])); //# 47: ok
+  x + 1; //# 48: compile-time error
+  -x; //# 49: compile-time error
+  x++; //# 50: compile-time error
+  ++x; //# 51: compile-time error
+  x..isEven; //# 52: compile-time error
+  list[0]; //# 53: compile-time error
+  list[0] = 0; //# 54: compile-time error
+  x += 1; //# 55: compile-time error
+  x ??= x; //# 56: ok
+  x.round; //# 57: compile-time error
+  x.toString; //# 58: ok
+  x.noSuchMethod; //# 59: ok
+  func(); //# 60: compile-time error
+  funcList[0](); //# 61: compile-time error
+  funcList.single(); //# 62: compile-time error
+  throw x; //# 63: compile-time error
+  cond || true; //# 64: compile-time error
+  true || cond; //# 65: compile-time error
+  cond && true; //# 66: compile-time error
+  true && cond; //# 67: compile-time error
+  !cond; //# 68: compile-time error
+  cond ? null : null; //# 69: compile-time error
+  if (cond) {} //# 70: compile-time error
+  while (cond) {} //# 71: compile-time error
+  for (;cond;){} //# 72: compile-time error
+  do {} while (cond); //# 73: compile-time error
+  cond!; //# 74: ok
+  cond ?? null; //# 75: ok
+  cond == null; //# 76: ok
+  cond != null; //# 77: ok
+  x?.isEven; //# 78: ok
+  x?.round(); //# 79: ok
+  for(var i in list) {}; //# 80: compile-time error
+  await for(var i in stream) {}; //# 81: compile-time error
+  assert(cond); //# 41: compile-time error
 }
 
 void typeParametersNullableUses<I extends int, B extends bool, L extends List, F extends Function, S extends Stream>(
@@ -119,88 +121,88 @@
     List<F?> funcList,
     S? stream,
     ) async {
-  x.isEven; //# 80: compile-time error
-  x.round(); //# 81: compile-time error
-  x.toString(); //# 82: ok
-  x.hashCode; //# 83: ok
-  x.runtimeType; //# 84: ok
-  x.noSuchMethod(Invocation.method(#toString, [])); //# 85: ok
-  x + 1; //# 86: compile-time error
-  -x; //# 87: compile-time error
-  x++; //# 88: compile-time error
-  ++x; //# 89: compile-time error
-  x..isEven; //# 90: compile-time error
-  list[0]; //# 91: compile-time error
-  list[0] = 0; //# 92: compile-time error
-  x += 1; //# 93: compile-time error
-  x ??= null; //# 94: ok
-  x.round; //# 95: compile-time error
-  x.toString; //# 96: ok
-  x.noSuchMethod; //# 97: ok
-  func(); //# 98: compile-time error
-  funcList[0](); //# 99: compile-time error
-  funcList.single(); //# 100: compile-time error
-  throw x; //# 101: compile-time error
-  cond || true; //# 102: compile-time error
-  true || cond; //# 103: compile-time error
-  cond && true; //# 104: compile-time error
-  true && cond; //# 105: compile-time error
-  !cond; //# 106: compile-time error
-  cond ? null : null; //# 107: compile-time error
-  if (cond) {} //# 108: compile-time error
-  while (cond) {} //# 109: compile-time error
-  for (;cond;) {} //# 110: compile-time error
-  do {} while (cond); //# 111: compile-time error
-  cond!; //# 112: ok
-  cond ?? null; //# 113: ok
-  cond == null; //# 114: ok
-  cond != null; //# 115: ok
-  x?.isEven; //# 116: ok
-  x?.round(); //# 117: ok
-  for(var i in list) {}; //# 118: compile-time error
-  await for(var i in stream) {}; //# 119: compile-time error
+  x.isEven; //# 82: compile-time error
+  x.round(); //# 83: compile-time error
+  x.toString(); //# 84: ok
+  x.hashCode; //# 85: ok
+  x.runtimeType; //# 86: ok
+  x.noSuchMethod(Invocation.method(#toString, [])); //# 87: ok
+  x + 1; //# 88: compile-time error
+  -x; //# 89: compile-time error
+  x++; //# 90: compile-time error
+  ++x; //# 91: compile-time error
+  x..isEven; //# 92: compile-time error
+  list[0]; //# 93: compile-time error
+  list[0] = 0; //# 94: compile-time error
+  x += 1; //# 95: compile-time error
+  x ??= null; //# 96: ok
+  x.round; //# 97: compile-time error
+  x.toString; //# 98: ok
+  x.noSuchMethod; //# 99: ok
+  func(); //# 100: compile-time error
+  funcList[0](); //# 101: compile-time error
+  funcList.single(); //# 102: compile-time error
+  throw x; //# 103: compile-time error
+  cond || true; //# 104: compile-time error
+  true || cond; //# 105: compile-time error
+  cond && true; //# 106: compile-time error
+  true && cond; //# 107: compile-time error
+  !cond; //# 108: compile-time error
+  cond ? null : null; //# 109: compile-time error
+  if (cond) {} //# 110: compile-time error
+  while (cond) {} //# 111: compile-time error
+  for (;cond;) {} //# 112: compile-time error
+  do {} while (cond); //# 113: compile-time error
+  cond!; //# 114: ok
+  cond ?? null; //# 115: ok
+  cond == null; //# 116: ok
+  cond != null; //# 117: ok
+  x?.isEven; //# 118: ok
+  x?.round(); //# 119: ok
+  for(var i in list) {}; //# 120: compile-time error
+  await for(var i in stream) {}; //# 121: compile-time error
 }
 
 void dynamicUses() async {
   dynamic dyn;
-  dyn.isEven; //# 120: ok
-  dyn.round(); //# 121: ok
-  dyn.toString(); //# 122: ok
-  dyn.hashCode; //# 123: ok
-  dyn.runtimeType; //# 124: ok
-  dyn.noSuchMethod(null); //# 125: ok
-  dyn + 1; //# 126: ok
-  -dyn; //# 127: ok
-  dyn++; //# 128: ok
-  ++dyn; //# 129: ok
-  dyn..isEven; //# 130: ok
-  dyn[0]; //# 131: ok
-  dyn[0] = 0; //# 132: ok
-  dyn += 1; //# 133: ok
-  dyn ??= null; //# 134: ok
-  dyn.round; //# 135: ok
-  dyn.toString; //# 136: ok
-  dyn.noSuchMethod; //# 137: ok
-  dyn(); //# 138: ok
-  dyn[0](); //# 139: ok
-  dyn.single(); //# 140: ok
-  throw dyn; //# 141: ok
-  dyn || true; //# 142: ok
-  true || dyn; //# 143: ok
-  dyn && true; //# 144: ok
-  true && dyn; //# 145: ok
-  !dyn; //# 146: ok
-  dyn ? null : null; //# 147: ok
-  if (dyn) {} //# 148: ok
-  while (dyn) {} //# 149: ok
-  for (;dyn;) {} //# 150: ok
-  do {} while (dyn); //# 151: ok
-  dyn!; //# 152: ok
-  dyn ?? null; //# 153: ok
-  dyn == null; //# 154: ok
-  dyn != null; //# 155: ok
-  dyn?.isEven; //# 156: ok
-  dyn?.round(); //# 157: ok
-  for(var i in dyn) {}; //# 158: ok
-  await for(var i in dyn) {}; //# 159: ok
+  dyn.isEven; //# 122: ok
+  dyn.round(); //# 123: ok
+  dyn.toString(); //# 124: ok
+  dyn.hashCode; //# 125: ok
+  dyn.runtimeType; //# 126: ok
+  dyn.noSuchMethod(null); //# 127: ok
+  dyn + 1; //# 128: ok
+  -dyn; //# 129: ok
+  dyn++; //# 130: ok
+  ++dyn; //# 131: ok
+  dyn..isEven; //# 132: ok
+  dyn[0]; //# 133: ok
+  dyn[0] = 0; //# 134: ok
+  dyn += 1; //# 135: ok
+  dyn ??= null; //# 136: ok
+  dyn.round; //# 137: ok
+  dyn.toString; //# 138: ok
+  dyn.noSuchMethod; //# 139: ok
+  dyn(); //# 140: ok
+  dyn[0](); //# 141: ok
+  dyn.single(); //# 142: ok
+  throw dyn; //# 143: ok
+  dyn || true; //# 144: ok
+  true || dyn; //# 145: ok
+  dyn && true; //# 146: ok
+  true && dyn; //# 147: ok
+  !dyn; //# 148: ok
+  dyn ? null : null; //# 149: ok
+  if (dyn) {} //# 150: ok
+  while (dyn) {} //# 151: ok
+  for (;dyn;) {} //# 152: ok
+  do {} while (dyn); //# 153: ok
+  dyn!; //# 154: ok
+  dyn ?? null; //# 155: ok
+  dyn == null; //# 156: ok
+  dyn != null; //# 157: ok
+  dyn?.isEven; //# 158: ok
+  dyn?.round(); //# 159: ok
+  for(var i in dyn) {}; //# 160: ok
+  await for(var i in dyn) {}; //# 161: ok
 }
diff --git a/tests/language_2/nnbd/syntax/null_assertion_ambiguous_test.dart b/tests/language_2/nnbd/syntax/null_assertion_ambiguous_test.dart
index 29200e7..68c6037 100644
--- a/tests/language_2/nnbd/syntax/null_assertion_ambiguous_test.dart
+++ b/tests/language_2/nnbd/syntax/null_assertion_ambiguous_test.dart
@@ -38,7 +38,7 @@
   // behavior by trying to assign to a non-nullable variable.  We check the
   // runtime behavior by verifying that the exception is thrown before an
   // assignment occurs.
-  Object x3;
+  Object x3 = 0;
   Expect.throws(() {
       x3 = a!;
   });
diff --git a/tests/language_2/nnbd/syntax/null_assertion_test.dart b/tests/language_2/nnbd/syntax/null_assertion_test.dart
index 4b27e00..6acea53 100644
--- a/tests/language_2/nnbd/syntax/null_assertion_test.dart
+++ b/tests/language_2/nnbd/syntax/null_assertion_test.dart
@@ -23,7 +23,7 @@
 Object? f() => null;
 
 main() {
-  List<Object> listOfObject;
+  List<Object> listOfObject = [];
 
   var x1 = [0!];
   listOfObject = x1;
diff --git a/tests/language_2/null_no_such_method_test.dart b/tests/language_2/null_no_such_method_test.dart
index 600c6bb..299ba2d 100644
--- a/tests/language_2/null_no_such_method_test.dart
+++ b/tests/language_2/null_no_such_method_test.dart
@@ -4,10 +4,10 @@
 
 import "package:expect/expect.dart";
 
-var array = <dynamic>[1];
-
 main() {
-  Expect.throwsNoSuchMethodError(() => -null);
+  Expect.throwsNoSuchMethodError(() => -(null as dynamic));
   // Make sure we have an untyped call to operator-.
   print(-array[0]);
 }
+
+var array = <dynamic>[1];
diff --git a/tests/language_2/vm/inline_heuristic_test.dart b/tests/language_2/vm/inline_heuristic_test.dart
new file mode 100755
index 0000000..ca25125
--- /dev/null
+++ b/tests/language_2/vm/inline_heuristic_test.dart
@@ -0,0 +1,303 @@
+// Copyright (c) 2019, 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=--deterministic --optimization_counter_threshold=10 --enable-inlining-annotations
+
+// Test on specialized vs non-specialized inlining.
+
+import 'dart:core';
+import "package:expect/expect.dart";
+
+const String NeverInline = 'NeverInline';
+
+// To inline or not to inline, that is the question?
+int foo(int k) {
+  switch (k) {
+    case 0:
+      return 1;
+    case 1:
+      return 2;
+    case 2:
+      return 3;
+    case 3:
+      return 4;
+    case 4:
+      return 5;
+    case 5:
+      return 6;
+    case 6:
+      return 7;
+    case 7:
+      return 8;
+    case 8:
+      return 9;
+    case 9:
+      return 10;
+    case 10:
+      return 11;
+    case 11:
+      return 12;
+    case 12:
+      return 13;
+    case 13:
+      return 14;
+    case 14:
+      return 15;
+    case 15:
+      return 16;
+    case 16:
+      return 17;
+    case 17:
+      return 18;
+    case 18:
+      return 19;
+    case 19:
+      return 20;
+    case 20:
+      return 21;
+    case 21:
+      return 22;
+    case 22:
+      return 23;
+    case 23:
+      return 24;
+    case 24:
+      return 25;
+    case 25:
+      return 26;
+    case 26:
+      return 27;
+    case 27:
+      return 28;
+    case 28:
+      return 29;
+    case 29:
+      return 30;
+    case 30:
+      return 31;
+    case 31:
+      return 32;
+    case 32:
+      return 33;
+    case 33:
+      return 34;
+    case 34:
+      return 35;
+    case 35:
+      return 36;
+    case 36:
+      return 37;
+    case 37:
+      return 38;
+    case 38:
+      return 39;
+    case 39:
+      return 40;
+    case 40:
+      return 41;
+    case 41:
+      return 42;
+    case 42:
+      return 43;
+    case 43:
+      return 44;
+    case 44:
+      return 45;
+    case 45:
+      return 46;
+    case 46:
+      return 47;
+    case 47:
+      return 48;
+    case 48:
+      return 49;
+    case 49:
+      return 50;
+    case 50:
+      return 51;
+    case 51:
+      return 52;
+    case 52:
+      return 53;
+    case 53:
+      return 54;
+    case 54:
+      return 55;
+    case 55:
+      return 56;
+    case 56:
+      return 57;
+    case 57:
+      return 58;
+    case 58:
+      return 59;
+    case 59:
+      return 60;
+    case 60:
+      return 61;
+    case 61:
+      return 62;
+    case 62:
+      return 63;
+    case 63:
+      return 64;
+    case 64:
+      return 65;
+    case 65:
+      return 66;
+    case 66:
+      return 67;
+    case 67:
+      return 68;
+    case 68:
+      return 69;
+    case 69:
+      return 70;
+    case 70:
+      return 71;
+    case 71:
+      return 72;
+    case 72:
+      return 73;
+    case 73:
+      return 74;
+    case 74:
+      return 75;
+    case 75:
+      return 76;
+    case 76:
+      return 77;
+    case 77:
+      return 78;
+    case 78:
+      return 79;
+    case 79:
+      return 80;
+    case 80:
+      return 81;
+    case 81:
+      return 82;
+    case 82:
+      return 83;
+    case 83:
+      return 84;
+    case 84:
+      return 85;
+    case 85:
+      return 86;
+    case 86:
+      return 87;
+    case 87:
+      return 88;
+    case 88:
+      return 89;
+    case 89:
+      return 90;
+    case 90:
+      return 91;
+    case 91:
+      return 92;
+    case 92:
+      return 93;
+    case 93:
+      return 94;
+    case 94:
+      return 95;
+    case 95:
+      return 96;
+    case 96:
+      return 97;
+    case 97:
+      return 98;
+    case 98:
+      return 99;
+    case 99:
+      return 100;
+    case 100:
+      return 101;
+    case 101:
+      return 102;
+    case 102:
+      return 103;
+    case 103:
+      return 104;
+    case 104:
+      return 105;
+    case 105:
+      return 106;
+    case 106:
+      return 107;
+    case 107:
+      return 108;
+    case 108:
+      return 109;
+    case 109:
+      return 110;
+    case 110:
+      return 111;
+    case 111:
+      return 112;
+    case 112:
+      return 113;
+    case 113:
+      return 114;
+    case 114:
+      return 115;
+    case 115:
+      return 116;
+    case 116:
+      return 117;
+    case 117:
+      return 118;
+    case 118:
+      return 119;
+    case 119:
+      return 120;
+    case 120:
+      return 121;
+    case 121:
+      return 122;
+    case 122:
+      return 123;
+    case 123:
+      return 124;
+    case 124:
+      return 125;
+    case 125:
+      return 126;
+    case 126:
+      return 127;
+    case 127:
+      return 128;
+    default:
+      return -1;
+  }
+}
+
+@NeverInline
+int bar() {
+  // Here we should inline! The inlined size is very small
+  // after specialization for the constant arguments.
+  return foo(1) + foo(12);
+}
+
+@NeverInline
+int baz(int i) {
+  // Here we should not inline! The inlined size is too large,
+  // just keep the original method. In fact, we can use the cached
+  // estimate of foo()'s size from the previous compilation at this
+  // point, which enables the "early" bail heuristic!
+  return foo(i);
+}
+
+main() {
+  // Repeat tests to enter JIT (when applicable).
+  for (int i = 0; i < 20; i++) {
+    Expect.equals(15, bar());
+    for (int i = -150; i <= 150; i++) {
+      int e = (i < 0 || i > 127) ? -1 : i + 1;
+      Expect.equals(e, baz(i));
+    }
+  }
+}
diff --git a/tests/language_2/vm/regress_33469_test.dart b/tests/language_2/vm/regress_33469_test.dart
index a18a6ad..831704e 100644
--- a/tests/language_2/vm/regress_33469_test.dart
+++ b/tests/language_2/vm/regress_33469_test.dart
@@ -8,8 +8,8 @@
 }
 
 void main() {
-  print(const X(1 << -1).x);  /// 01: compile-time error
-  print(const X(1 >> -1).x);  /// 02: compile-time error
-  print(const X(1 % 0).x);    /// 03: compile-time error
-  print(const X(1 ~/ 0).x);   /// 04: compile-time error
+  print(const X(1 << -1).x);  //# 01: compile-time error
+  print(const X(1 >> -1).x);  //# 02: compile-time error
+  print(const X(1 % 0).x);    //# 03: compile-time error
+  print(const X(1 ~/ 0).x);   //# 04: compile-time error
 }
diff --git a/tests/language_2/vm/regress_36977_test.dart b/tests/language_2/vm/regress_36977_test.dart
new file mode 100755
index 0000000..10a29f1
--- /dev/null
+++ b/tests/language_2/vm/regress_36977_test.dart
@@ -0,0 +1,469 @@
+// Copyright (c) 2019, 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=--deterministic
+//
+// Regression test, reduced case found by DartFuzz that crashed DBC register
+// allocator (https://github.com/dart-lang/sdk/issues/36977).
+
+import 'dart:async';
+import 'dart:cli';
+import 'dart:collection';
+import 'dart:convert';
+import 'dart:core';
+import 'dart:io';
+import 'dart:isolate';
+import 'dart:math';
+import 'dart:typed_data';
+
+double var0 = 0.9297828201722298;
+bool var1 = false;
+int var2 = -99;
+double var3 = 0.26752130449990763;
+String var4 = 'OJG';
+List<int> var5 = [0, -92, 49, 96];
+Set<int> var6 = {29, 9223372036854775807, 75};
+Map<int, String> var7 = {
+  56: '\u{1f600}D',
+  35: '62',
+  13: '',
+  14: '5k1\u{1f600}\u{1f600}'
+};
+
+double foo0(String par1) {
+  throw ({
+        51: MapBase.mapToString(var7),
+        9: '-Io',
+        65: ((var2 * var5[-77])).toRadixString(
+            ((false ? false : (true ? var1 : (!((!(var1))))))
+                ? (--var2)
+                : (-(-16)))),
+        61: ''
+      } ??
+      var7);
+}
+
+int foo1(Set<int> par1, Set<int> par2) {
+  return Int32x4.zxzy;
+}
+
+String foo2() {
+  {
+    int loc0 = 58;
+    while (--loc0 > 0) {
+      switch ((~((~((-((~(foo1((false ? var6 : var6), {
+        (~(-84)),
+        ((var1 ? var5[(loc0 + (var2--))] : -77) >>
+            ((var1 ? var2 : ((-(var2)) - loc0)) +
+                foo1(
+                    {
+                      -8,
+                      19,
+                      (true ? var2 : 70),
+                      39,
+                      loc0,
+                      Int32x4.wxyw,
+                      var5[var5[Int32x4.wzxz]],
+                      -12
+                    },
+                    (var6 ??
+                        {
+                          var5[(var1 ? var5[loc0] : var2)],
+                          var5[(loc0--)],
+                          loc0
+                        })))),
+        ((Int32x4.yzzw % loc0) % -87),
+        Int32x4.wxyz,
+        Float32x4.wzxz
+      })))))))))) {
+        case 3666713542:
+          {
+            var4 = (true
+                ? var4
+                : String.fromCharCode((var1
+                    ? ((true ? true : true) ? var5[(var2++)] : var2)
+                    : (((((false ? (true ? false : true) : false) ? 52 : var2) -
+                                foo1(var6, var6)) ~/
+                            var2) ^
+                        loc0))));
+          }
+          break;
+        case 3666713543:
+          {
+            var5 = [(Float32x4.xzyw << (Int32x4.zywz * var2))];
+          }
+          break;
+      }
+    }
+  }
+  return 'hLw';
+}
+
+class X0 {
+  bool fld0_0 = true;
+
+  Set<int> foo0_0(Map<int, String> par1, int par2) {
+    try {
+      if ((var5 != var5)) {
+        throw 0.2119336645634784;
+      }
+    } catch (exception, stackTrace) {
+      if (fld0_0) {
+        print({
+          ((Float32x4.xzzx %
+                  ((-(30)) *
+                      foo1(
+                          var6,
+                          ({
+                                (par2++),
+                                foo1({
+                                  var5[-83],
+                                  (true ? -58 : foo1(var6, var6)),
+                                  par2,
+                                  (var5[-49] - 46),
+                                  var2,
+                                  var5[76],
+                                  73
+                                }, (var6).difference(var6)),
+                                (((false ? var5[49] : 6442450943) > -98)
+                                    ? -92
+                                    : var5[7]),
+                                -93,
+                                var5[var5[27]],
+                                var5[-5],
+                                (-(((var1 ? false : true) ? var5[-87] : par2)))
+                              } ??
+                              {FileSystemEvent.MODIFY, 31, var2})))) +
+              var2),
+          (((++par2)).isEven
+              ? foo1(
+                  ((var6).toSet()).intersection((var1
+                      ? var6
+                      : (true
+                          ? (var6 ??
+                              {
+                                foo1({
+                                  (45 ^ -59),
+                                  var2,
+                                  -94,
+                                  Int32x4.yyzw,
+                                  var2,
+                                  44,
+                                  var5[foo1(var6, (var6 ?? {-91, var2}))]
+                                }, var6),
+                                ((++par2) % 70),
+                                88
+                              })
+                          : var6))),
+                  ({
+                        -85,
+                        (var2++),
+                        (++par2),
+                        45,
+                        par2,
+                        (--par2),
+                        (var5[par2] ??
+                            (-((~(foo1(var6, {
+                              foo1(
+                                  ({
+                                        (var2 -
+                                            (FileSystemEntity.isFileSync(
+                                                    'd)\u{1f600}+Sm')
+                                                ? (false ? 22 : var2)
+                                                : -15)),
+                                        var5[var2],
+                                        par2,
+                                        var5[(foo1(
+                                                var6,
+                                                ({
+                                                      var2,
+                                                      (~(-9223372030412324863)),
+                                                      (var2++),
+                                                      -18
+                                                    } ??
+                                                    var6)) &
+                                            Int32x4.yxwx)],
+                                        (fld0_0
+                                            ? (~((-(-50))))
+                                            : foo1({
+                                                foo1({-92, var5[81]}, var6),
+                                                var2,
+                                                76,
+                                                var5[(false
+                                                    ? (true
+                                                        ? var2
+                                                        : var5[Float32x4.yzxx])
+                                                    : var5[var5[var2]])],
+                                                Duration.minutesPerHour,
+                                                34
+                                              }, var6)),
+                                        -72
+                                      } ??
+                                      var6),
+                                  {25, Float32x4.wwww})
+                            })))))),
+                        (par2--)
+                      } ??
+                      var6))
+              : (-((SecurityContext.alpnSupported
+                  ? Float32x4.zwyz
+                  : foo1({
+                      14,
+                      var5[var2],
+                      (~(39)),
+                      93,
+                      Float32x4.xyxx,
+                      (-(var5[(-65 << -28)])),
+                      24,
+                      (~(-64))
+                    }, {
+                      -31,
+                      (foo1(var6, {
+                            Float32x4.zzyx,
+                            96,
+                            Float32x4.yyxw,
+                            var2,
+                            par2
+                          }) +
+                          Int32x4.wzyy),
+                      (~(52)),
+                      -13,
+                      par2,
+                      -83,
+                      ZLibOption.DEFAULT_MEM_LEVEL,
+                      (var2++)
+                    })))))
+        });
+      }
+    } finally {
+      for (int loc0 = 0; loc0 < 90; loc0++) {
+        if (false) {
+          var5 ??= [(~((++par2)))];
+          var0 += ((0.08377494829959586 - (-(var3))) *
+              (-(((true ? (var0 + 0.8491470957055424) : var0) /
+                  ((!(var1))
+                      ? var0
+                      : (var0 *
+                          ((([
+                                        -67,
+                                        6442450943,
+                                        -95,
+                                        var5[par2],
+                                        -30,
+                                        6442450945,
+                                        loc0
+                                      ] ??
+                                      [
+                                        (true ? 6442450943 : 16),
+                                        var5[var5[1]],
+                                        var2,
+                                        Float32x4.xwzy,
+                                        par2,
+                                        var5[28],
+                                        Int32x4.xxwx,
+                                        var5[-23]
+                                      ]) !=
+                                  [var5[var5[(--par2)]], Int32x4.yzxy])
+                              ? var3
+                              : 0.9197172211286759)))))));
+        } else {
+          print((var4).codeUnits);
+        }
+      }
+      var7 ??= {
+        9: (var7[(((!(fld0_0)) ? -27 : (Float32x4.xyzz).round()) ^
+                ((~(54)) +
+                    foo1({
+                      var2,
+                      28,
+                      -9223372028264841217,
+                      (par2--),
+                      (++par2),
+                      -1,
+                      (-(var5[var2]))
+                    }, var6)))])
+            .padLeft(68, (0.0875980111263257).toStringAsPrecision((++par2))),
+        88: (foo2() ?? '1rcZ'),
+        32: 'NeAEG',
+        5: ('\u{1f600}n)V\u2665#').replaceRange(
+            (-(-1)),
+            (-(par2)),
+            ((((!((var1 || (true ? var1 : (var7).isEmpty)))) ? var1 : false) &&
+                    (var7).isEmpty)
+                ? ((false ? false : var1) ? '!Tlx)' : par1[6442450943])
+                : var4)),
+        13: var7[(((!(((par1[-27] ?? 'AcM') != 'Gb')))
+                ? true
+                : (var6 ==
+                    ({
+                          13,
+                          96,
+                          var5[Int32x4.zxyw],
+                          -41,
+                          ((fld0_0 || true) ? var5[-45] : var5[95]),
+                          (--par2),
+                          -37
+                        } ??
+                        {
+                          8589934591,
+                          ((--var2) ??
+                              (var2 *
+                                  ((!(var1))
+                                      ? var5[Float32x4.yxxx]
+                                      : (var2++)))),
+                          (var5[var5[Float32x4.wwzz]] * -30),
+                          Int32x4.xzzw,
+                          (-((~(22)))),
+                          15
+                        })))
+            ? (fld0_0
+                ? var2
+                : foo1({
+                    var5[-9223372028264841217],
+                    foo1(
+                        {
+                          -82,
+                          var5[Float32x4.zzyx],
+                          par2,
+                          (~(var5[par2])),
+                          (par2++),
+                          63
+                        },
+                        (false
+                            ? {
+                                0,
+                                Uint16List.bytesPerElement,
+                                -41,
+                                8,
+                                Int32x4.xxyz,
+                                RawSocketOption.levelSocket
+                              }
+                            : var6)),
+                    foo1({var5[(~(-47))], (-(-46))},
+                        ({var5[2], (-(-28))} ?? var6)),
+                    (par2--),
+                    foo1(({var5[-69], 52}).difference(var6),
+                        {25, (-(var5[9223372034707292159])), 52}),
+                    (var2++),
+                    Int32x4.ywyz,
+                    (FileSystemEntity.isWatchSupported ? (++par2) : var2)
+                  }, {
+                    Float32x4.wxzx,
+                    var2,
+                    par2,
+                    var5[var2],
+                    par2,
+                    var2
+                  }))
+            : (++par2))]
+      };
+    }
+    return var6;
+  }
+
+  Map<int, String> foo0_1(double par1) {
+    fld0_0 = (0.8454395181150425).isInfinite;
+    return {
+      86: var4,
+      59: ((((par1 ?? 0.45641338431576617)).toStringAsExponential(
+                  (foo1({51, 35, (var1 ? (~(var2)) : -35)}, var6) ^
+                      Int32x4.xwzx)))
+              .trimLeft() +
+          'FX')
+    };
+  }
+
+  void run() {
+    {
+      int loc0 = 2;
+      while (--loc0 > 0) {
+        var2 %= (fld0_0 ? var2 : -26);
+      }
+    }
+    try {
+      switch ((true
+          ? (~((var1 ? 40 : foo1((var6).difference(var6), {77}))))
+          : (92 ?? Float32x4.zxxx))) {
+        case 583336190:
+          {
+            var7 = (foo0_1(((2 ^ var2)).truncateToDouble()) ??
+                ((FileSystemEntity.isFileSync('J u') && false)
+                    ? ({
+                          96: 'PRN',
+                          26: 'rayc(',
+                          91: '',
+                          69: 'NG',
+                          78: 'B7',
+                          53: 'KOQzI',
+                          85: 'ZuksL'
+                        } ??
+                        var7)
+                    : var7));
+          }
+          break;
+        case 583336197:
+          {
+            var7 = (((--var2) != (~(((var2--) ~/ (++var2)))))
+                ? ((true ? Map.from(Map.of(var7)) : var7) ?? var7)
+                : foo0_1(0.48262288106096063));
+          }
+          break;
+      }
+      for (int loc0 = 0; loc0 < 8; loc0++) {
+        /*
+         * Multi-line
+         * comment.
+         */
+        {
+          int loc1 = 22;
+          while (--loc1 > 0) {
+            var6 ??= var6;
+            var7[(FileSystemEntity.isWatchSupported
+                ? foo1(
+                    (foo0_0({49: '7asVc2S', 12: 'x\u2665\u{1f600}tI'}, loc0))
+                        .difference(((!(true))
+                            ? foo0_0({
+                                24: 'kB9',
+                                87: 'vIEqX@r',
+                                36: '5u',
+                                34: 'M8\u{1f600}Og\u2665',
+                                73: '-bMA\u{1f600}N',
+                                39: 'F((\u{1f600}Y',
+                                54: 'FHp!'
+                              }, 4294967297)
+                            : var6)),
+                    foo0_0({
+                      95: '!sL',
+                      30: '\u2665',
+                      51: 'E+jWt\u{1f600}',
+                      78: 'cCr#k',
+                      56: ')P-a'
+                    }, -19))
+                : 76)] = (var4 + 'n\u2665\u{1f600}j');
+          }
+        }
+      }
+    } catch (exception, stackTrace) {
+      var3 ??= ((!(true))
+          ? foo0(var7[(false
+              ? foo1(
+                  ((foo0_0({
+                            60: 'Nt2h',
+                            48: 'gWolH9',
+                            42: ')',
+                            15: 'n!YW\u2665',
+                            79: '7E\u{1f600}'
+                          }, 37) ??
+                          {19}) ??
+                      var6),
+                  {-17, 4294967297, -94, -63})
+              : (var2--))])
+          : ((-(double.nan)) / 0.7833677975390729));
+    }
+  }
+}
+
+main() {
+  new X0().run();
+}
diff --git a/tests/language_2/vm/regress_37149_test.dart b/tests/language_2/vm/regress_37149_test.dart
new file mode 100644
index 0000000..5907c5b
--- /dev/null
+++ b/tests/language_2/vm/regress_37149_test.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2019, 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.
+
+// This test verifies that typedef types used in constants are correctly
+// visited in the tree shaker.
+// Regression test for https://github.com/dart-lang/sdk/issues/37149.
+
+import 'dart:async' show FutureOr;
+
+typedef ComputeCallback<Q, R> = FutureOr<R> Function(Q message);
+typedef _ComputeImpl = Future<R> Function<Q, R>(
+    ComputeCallback<Q, R> callback, Q message,
+    {String debugLabel});
+
+Future<R> isolatesCompute<Q, R>(ComputeCallback<Q, R> callback, Q message,
+    {String debugLabel}) async {
+  return null;
+}
+
+const _ComputeImpl compute = isolatesCompute;
+
+main() {
+  compute.call(null, null);
+}
diff --git a/tests/language_2/vm/symbols_test.dart b/tests/language_2/vm/symbols_test.dart
index 15f9ed1..a68e2f4 100644
--- a/tests/language_2/vm/symbols_test.dart
+++ b/tests/language_2/vm/symbols_test.dart
@@ -5,15 +5,15 @@
 import 'package:expect/expect.dart';
 
 void main() {
-  print(const Symbol(null));                                /// 01: compile-time error
-  print(const Symbol(r''));                                 /// 02: ok
-  Expect.isTrue(identical(const Symbol(r'foo'), #foo));     /// 03: ok
-  Expect.isTrue(identical(const Symbol(r'$foo'), #$foo));   /// 03: ok
-  Expect.isTrue(identical(const Symbol(r'$_'), #$_));       /// 03: ok
-  Expect.isTrue(identical(const Symbol(r'+'), #+));         /// 03: ok
-  Expect.isTrue(identical(const Symbol(r'[]='), #[]=));     /// 03: ok
-  Expect.isTrue(identical(const Symbol(r'_foo'), #_foo));   /// 03: compile-time error
-  Expect.isTrue(identical(const Symbol(r'_$'), #_$));       /// 03: compile-time error
-  Expect.isTrue(identical(const Symbol(r'_foo$'), #_foo$)); /// 03: compile-time error
-  print(const Symbol(r'_+'));                               /// 03: compile-time error
+  print(const Symbol(null));                                //# 01: compile-time error
+  print(const Symbol(r''));                                 //# 02: ok
+  Expect.isTrue(identical(const Symbol(r'foo'), #foo));     //# 03: ok
+  Expect.isTrue(identical(const Symbol(r'$foo'), #$foo));   //# 03: ok
+  Expect.isTrue(identical(const Symbol(r'$_'), #$_));       //# 03: ok
+  Expect.isTrue(identical(const Symbol(r'+'), #+));         //# 03: ok
+  Expect.isTrue(identical(const Symbol(r'[]='), #[]=));     //# 03: ok
+  Expect.isTrue(identical(const Symbol(r'_foo'), #_foo));   //# 03: compile-time error
+  Expect.isTrue(identical(const Symbol(r'_$'), #_$));       //# 03: compile-time error
+  Expect.isTrue(identical(const Symbol(r'_foo$'), #_foo$)); //# 03: compile-time error
+  print(const Symbol(r'_+'));                               //# 03: compile-time error
 }
diff --git a/tests/lib_2/analyzer/analyze_library.status b/tests/lib_2/analyzer/analyze_library.status
index e34a9bb..0dd8e28 100644
--- a/tests/lib_2/analyzer/analyze_library.status
+++ b/tests/lib_2/analyzer/analyze_library.status
@@ -3,4 +3,4 @@
 # BSD-style license that can be found in the LICENSE file.
 
 [ $compiler == dart2analyzer ]
-*: Skip
\ No newline at end of file
+*: Skip
diff --git a/tests/lib_2/html/custom/attribute_changed_callback_test.html b/tests/lib_2/html/custom/attribute_changed_callback_test.html
index 8237473..b38ec6e 100644
--- a/tests/lib_2/html/custom/attribute_changed_callback_test.html
+++ b/tests/lib_2/html/custom/attribute_changed_callback_test.html
@@ -16,7 +16,7 @@
 <body>
   <h1> Running attribute_changed_callback_test </h1>
   <script type="text/javascript"
-      src="/root_dart/tools/testing/dart/test_controller.js"></script>
+      src="/root_dart/pkg/test_runner/lib/src/test_controller.js"></script>
   %TEST_SCRIPTS%
 </body>
 </html>
diff --git a/tests/lib_2/html/custom/constructor_calls_created_synchronously_test.html b/tests/lib_2/html/custom/constructor_calls_created_synchronously_test.html
index c18be7d..e77e7c7 100644
--- a/tests/lib_2/html/custom/constructor_calls_created_synchronously_test.html
+++ b/tests/lib_2/html/custom/constructor_calls_created_synchronously_test.html
@@ -16,7 +16,7 @@
 <body>
   <h1> Running entered_left_view_test </h1>
   <script type="text/javascript"
-      src="/root_dart/tools/testing/dart/test_controller.js"></script>
+      src="/root_dart/pkg/test_runner/lib/src/test_controller.js"></script>
   %TEST_SCRIPTS%
 </body>
 </html>
diff --git a/tests/lib_2/html/custom/created_callback_test.html b/tests/lib_2/html/custom/created_callback_test.html
index 892277b..ca6e193 100644
--- a/tests/lib_2/html/custom/created_callback_test.html
+++ b/tests/lib_2/html/custom/created_callback_test.html
@@ -16,7 +16,7 @@
 <body>
   <h1> Running created_callback_test </h1>
   <script type="text/javascript"
-      src="/root_dart/tools/testing/dart/test_controller.js"></script>
+      src="/root_dart/pkg/test_runner/lib/src/test_controller.js"></script>
   %TEST_SCRIPTS%
 </body>
 </html>
diff --git a/tests/lib_2/html/custom/document_register_basic_test.html b/tests/lib_2/html/custom/document_register_basic_test.html
index 10041c9..c4042b3 100644
--- a/tests/lib_2/html/custom/document_register_basic_test.html
+++ b/tests/lib_2/html/custom/document_register_basic_test.html
@@ -16,7 +16,7 @@
 <body>
   <h1> Running document_register_basic_test </h1>
   <script type="text/javascript"
-      src="/root_dart/tools/testing/dart/test_controller.js"></script>
+      src="/root_dart/pkg/test_runner/lib/src/test_controller.js"></script>
   %TEST_SCRIPTS%
 </body>
 </html>
diff --git a/tests/lib_2/html/custom/document_register_template_test.html b/tests/lib_2/html/custom/document_register_template_test.html
index 26f59bf..7c48c6a 100644
--- a/tests/lib_2/html/custom/document_register_template_test.html
+++ b/tests/lib_2/html/custom/document_register_template_test.html
@@ -16,7 +16,7 @@
 <body>
   <h1> Running document_register_basic_test </h1>
   <script type="text/javascript"
-      src="/root_dart/tools/testing/dart/test_controller.js"></script>
+      src="/root_dart/pkg/test_runner/lib/src/test_controller.js"></script>
   %TEST_SCRIPTS%
 </body>
 </html>
diff --git a/tests/lib_2/html/custom/document_register_type_extensions_test.html b/tests/lib_2/html/custom/document_register_type_extensions_test.html
index 75419cb..30c2138 100644
--- a/tests/lib_2/html/custom/document_register_type_extensions_test.html
+++ b/tests/lib_2/html/custom/document_register_type_extensions_test.html
@@ -16,7 +16,7 @@
 <body>
   <h1> Running document_register_type_extensions_test </h1>
   <script type="text/javascript"
-      src="/root_dart/tools/testing/dart/test_controller.js"></script>
+      src="/root_dart/pkg/test_runner/lib/src/test_controller.js"></script>
   %TEST_SCRIPTS%
 </body>
 </html>
diff --git a/tests/lib_2/html/custom/element_upgrade_test.html b/tests/lib_2/html/custom/element_upgrade_test.html
index 9ae03d3..e06cdbc 100644
--- a/tests/lib_2/html/custom/element_upgrade_test.html
+++ b/tests/lib_2/html/custom/element_upgrade_test.html
@@ -44,6 +44,6 @@
 </script>
 
   <script type="text/javascript"
-      src="/root_dart/tools/testing/dart/test_controller.js"></script>
+      src="/root_dart/pkg/test_runner/lib/src/test_controller.js"></script>
   %TEST_SCRIPTS%
 </body>
diff --git a/tests/lib_2/html/custom/entered_left_view_test.html b/tests/lib_2/html/custom/entered_left_view_test.html
index c18be7d..e77e7c7 100644
--- a/tests/lib_2/html/custom/entered_left_view_test.html
+++ b/tests/lib_2/html/custom/entered_left_view_test.html
@@ -16,7 +16,7 @@
 <body>
   <h1> Running entered_left_view_test </h1>
   <script type="text/javascript"
-      src="/root_dart/tools/testing/dart/test_controller.js"></script>
+      src="/root_dart/pkg/test_runner/lib/src/test_controller.js"></script>
   %TEST_SCRIPTS%
 </body>
 </html>
diff --git a/tests/lib_2/html/custom/js_custom_test.html b/tests/lib_2/html/custom/js_custom_test.html
index 18e254c..323f54c 100644
--- a/tests/lib_2/html/custom/js_custom_test.html
+++ b/tests/lib_2/html/custom/js_custom_test.html
@@ -16,7 +16,7 @@
 <body>
   <h1> Running js_custom_test </h1>
   <script type="text/javascript"
-      src="/root_dart/tools/testing/dart/test_controller.js"></script>
+      src="/root_dart/pkg/test_runner/lib/src/test_controller.js"></script>
   %TEST_SCRIPTS%
 </body>
 </html>
diff --git a/tests/lib_2/html/custom/mirrors_2_test.html b/tests/lib_2/html/custom/mirrors_2_test.html
index bab6ba3..225759b 100644
--- a/tests/lib_2/html/custom/mirrors_2_test.html
+++ b/tests/lib_2/html/custom/mirrors_2_test.html
@@ -16,7 +16,7 @@
 <body>
   <h1> Running mirrors_2_test </h1>
   <script type="text/javascript"
-      src="/root_dart/tools/testing/dart/test_controller.js"></script>
+      src="/root_dart/pkg/test_runner/lib/src/test_controller.js"></script>
   %TEST_SCRIPTS%
 </body>
 </html>
diff --git a/tests/lib_2/html/custom/mirrors_test.html b/tests/lib_2/html/custom/mirrors_test.html
index 76079b3..34d84b9 100644
--- a/tests/lib_2/html/custom/mirrors_test.html
+++ b/tests/lib_2/html/custom/mirrors_test.html
@@ -16,7 +16,7 @@
 <body>
   <h1> Running mirrors_test </h1>
   <script type="text/javascript"
-      src="/root_dart/tools/testing/dart/test_controller.js"></script>
+      src="/root_dart/pkg/test_runner/lib/src/test_controller.js"></script>
   %TEST_SCRIPTS%
 </body>
 </html>
diff --git a/tests/lib_2/html/custom_element_method_clash_test.html b/tests/lib_2/html/custom_element_method_clash_test.html
index a5e9625..1c216a5 100644
--- a/tests/lib_2/html/custom_element_method_clash_test.html
+++ b/tests/lib_2/html/custom_element_method_clash_test.html
@@ -16,7 +16,7 @@
 <body>
   <h1> Running custom_element_method_clash_test </h1>
   <script type="text/javascript"
-      src="/root_dart/tools/testing/dart/test_controller.js"></script>
+      src="/root_dart/pkg/test_runner/lib/src/test_controller.js"></script>
   %TEST_SCRIPTS%
 </body>
 </html>
diff --git a/tests/lib_2/html/custom_element_name_clash_test.html b/tests/lib_2/html/custom_element_name_clash_test.html
index f414794..289b371 100644
--- a/tests/lib_2/html/custom_element_name_clash_test.html
+++ b/tests/lib_2/html/custom_element_name_clash_test.html
@@ -16,7 +16,7 @@
 <body>
   <h1> Running custom_element_name_clash_test </h1>
   <script type="text/javascript"
-      src="/root_dart/tools/testing/dart/test_controller.js"></script>
+      src="/root_dart/pkg/test_runner/lib/src/test_controller.js"></script>
   %TEST_SCRIPTS%
 </body>
 </html>
diff --git a/tests/lib_2/html/custom_elements_23127_test.html b/tests/lib_2/html/custom_elements_23127_test.html
index b0b68b5..1384fa0 100644
--- a/tests/lib_2/html/custom_elements_23127_test.html
+++ b/tests/lib_2/html/custom_elements_23127_test.html
@@ -16,7 +16,7 @@
 <body>
   <h1> Running custom_elements_23127_test </h1>
   <script type="text/javascript"
-      src="/root_dart/tools/testing/dart/test_controller.js"></script>
+      src="/root_dart/pkg/test_runner/lib/src/test_controller.js"></script>
   %TEST_SCRIPTS%
 </body>
 </html>
diff --git a/tests/lib_2/html/custom_elements_test.html b/tests/lib_2/html/custom_elements_test.html
index 42c6ed6..a5751b8 100644
--- a/tests/lib_2/html/custom_elements_test.html
+++ b/tests/lib_2/html/custom_elements_test.html
@@ -16,7 +16,7 @@
 <body>
   <h1> Running custom_elements_test </h1>
   <script type="text/javascript"
-      src="/root_dart/tools/testing/dart/test_controller.js"></script>
+      src="/root_dart/pkg/test_runner/lib/src/test_controller.js"></script>
   %TEST_SCRIPTS%
 </body>
 </html>
diff --git a/tests/lib_2/html/js_dispatch_property_test.html b/tests/lib_2/html/js_dispatch_property_test.html
index ba98d98..630ed95 100644
--- a/tests/lib_2/html/js_dispatch_property_test.html
+++ b/tests/lib_2/html/js_dispatch_property_test.html
@@ -21,7 +21,7 @@
   <script type="text/javascript"
       src="/root_dart/tests/lib_2/html/js_dispatch_property_test_js.js"></script>
   <script type="text/javascript"
-      src="/root_dart/tools/testing/dart/test_controller.js"></script>
+      src="/root_dart/pkg/test_runner/lib/src/test_controller.js"></script>
   %TEST_SCRIPTS%
 </body>
 </html>
diff --git a/tests/lib_2/html/js_interop_constructor_name_test.html b/tests/lib_2/html/js_interop_constructor_name_test.html
index 4b21d8b..04399e4 100644
--- a/tests/lib_2/html/js_interop_constructor_name_test.html
+++ b/tests/lib_2/html/js_interop_constructor_name_test.html
@@ -16,7 +16,7 @@
   <script type="text/javascript"
       src="/root_dart/tests/html/js_interop_constructor_name_test_js.js"></script>
   <script type="text/javascript"
-      src="/root_dart/tools/testing/dart/test_controller.js"></script>
+      src="/root_dart/pkg/test_runner/lib/src/test_controller.js"></script>
   %TEST_SCRIPTS%
 </body>
 </html>
diff --git a/tests/lib_2/lib_2_analyzer.status b/tests/lib_2/lib_2_analyzer.status
index 165b9a3..a461729 100644
--- a/tests/lib_2/lib_2_analyzer.status
+++ b/tests/lib_2/lib_2_analyzer.status
@@ -3,7 +3,6 @@
 # BSD-style license that can be found in the LICENSE file.
 
 [ $compiler == dart2analyzer ]
-html/debugger_test: CompileTimeError # Issue 28969
 html/element_types_keygen_test: CompileTimeError # Chrome 57 keygen removed
 html/js_function_getter_trust_types_test: Skip # dart2js specific flags.
 html/js_typed_interop_default_arg_test/default_value: MissingCompileTimeError # Issue #25759
diff --git a/tests/lib_2/lib_2_dartdevc.status b/tests/lib_2/lib_2_dartdevc.status
index 4b47ff6..368c7ba 100644
--- a/tests/lib_2/lib_2_dartdevc.status
+++ b/tests/lib_2/lib_2_dartdevc.status
@@ -9,7 +9,6 @@
 
 [ $compiler == dartdevk ]
 async/slow_consumer_test: CompileTimeError
-html/debugger_test: CompileTimeError
 
 [ $runtime == chrome && ($compiler == dartdevc || $compiler == dartdevk) ]
 html/element_animate_test/timing_dict: RuntimeError # Issue 29922
diff --git a/tests/lib_2/lib_2_precompiled.status b/tests/lib_2/lib_2_precompiled.status
index 86461fe..9a1e896 100644
--- a/tests/lib_2/lib_2_precompiled.status
+++ b/tests/lib_2/lib_2_precompiled.status
@@ -3,21 +3,10 @@
 # BSD-style license that can be found in the LICENSE file.
 
 [ $compiler == precompiler ]
+async/async_no_await_zones_test: RuntimeError # Issue 33700
 convert/chunked_conversion_utf88_test: Pass, Timeout
 convert/utf85_test: Pass, Timeout
 html/*: SkipByDesign # dart:html not supported on AOT.
-mirrors/*: SkipByDesign # Mirrors not supported on AOT.
-async/async_no_await_zones_test: RuntimeError # Issue 33700
-
-[ $compiler == app_jit || $compiler == none || $compiler == precompiler ]
-async/future_or_strong_test: RuntimeError
-async/timer_not_available_test: SkipByDesign # only meant to test when there is no way to implement timer (currently only in d8)
-isolate/compile_time_error_test/01: Skip # Issue 12587
-isolate/ping_pause_test: Skip # Resolve test issues
-isolate/ping_test: Skip # Resolve test issues
-mirrors/symbol_validation_test: RuntimeError # Issue 13596
-
-[ $compiler == precompiler ]
 isolate/count_test: SkipByDesign
 isolate/cross_isolate_message_test: SkipByDesign
 isolate/illegal_msg_function_test: SkipByDesign
@@ -39,3 +28,12 @@
 js/datetime_roundtrip_test: CompileTimeError
 js/null_test: CompileTimeError
 js/prototype_access_test: CompileTimeError
+mirrors/*: SkipByDesign # Mirrors not supported on AOT.
+
+[ $compiler == app_jit || $compiler == none || $compiler == precompiler ]
+async/future_or_strong_test: RuntimeError
+async/timer_not_available_test: SkipByDesign # only meant to test when there is no way to implement timer (currently only in d8)
+isolate/compile_time_error_test/01: Skip # Issue 12587
+isolate/ping_pause_test: Skip # Resolve test issues
+isolate/ping_test: Skip # Resolve test issues
+mirrors/symbol_validation_test: RuntimeError # Issue 13596
diff --git a/tests/modular/constants_2018/def.dart b/tests/modular/constants_2018/def.dart
new file mode 100644
index 0000000..55e9f4d
--- /dev/null
+++ b/tests/modular/constants_2018/def.dart
@@ -0,0 +1,11 @@
+// Copyright (c) 2019, 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.
+
+const val = 5;
+const set1 = {if (val % 2 == 1) 0, if (val % 2 == 0) 1};
+const set2 = {
+  1,
+  ...[2, 3],
+  4
+};
diff --git a/tests/modular/constants_2018/main.dart b/tests/modular/constants_2018/main.dart
new file mode 100644
index 0000000..d10c331
--- /dev/null
+++ b/tests/modular/constants_2018/main.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2019, 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 'package:expect/expect.dart';
+
+import 'def.dart';
+
+const set3 = [...set1, ...set2];
+
+main() {
+  Expect.isTrue(set3.length == 5);
+  Expect.setEquals(set3, {0, 1, 2, 3, 4});
+}
diff --git a/tests/modular/constants_2018/modules.yaml b/tests/modular/constants_2018/modules.yaml
new file mode 100644
index 0000000..e5a6190
--- /dev/null
+++ b/tests/modular/constants_2018/modules.yaml
@@ -0,0 +1,10 @@
+# Copyright (c) 2019, 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.
+#
+# Regression test: integral numbers should be treated as int and not double
+# after serialization across modules.
+dependencies:
+  main: [def, expect]
+flags:
+  - constant-update-2018
diff --git a/tests/standalone_2/io/http_auth_digest_test.dart b/tests/standalone_2/io/http_auth_digest_test.dart
index 3b4418c..7161cf98 100644
--- a/tests/standalone_2/io/http_auth_digest_test.dart
+++ b/tests/standalone_2/io/http_auth_digest_test.dart
@@ -210,7 +210,7 @@
     client.authenticate = (Uri url, String scheme, String realm) {
       Expect.equals("Digest", scheme);
       Expect.equals("test", realm);
-      Completer completer = new Completer();
+      Completer completer = new Completer<bool>();
       new Timer(const Duration(milliseconds: 10), () {
         client.addCredentials(
             Uri.parse("http://127.0.0.1:${server.port}/digest"),
@@ -277,8 +277,7 @@
 }
 
 void testNextNonce() {
-  Server
-      .start("MD5", "auth", nonceStaleAfter: 2, useNextNonce: true)
+  Server.start("MD5", "auth", nonceStaleAfter: 2, useNextNonce: true)
       .then((server) {
     HttpClient client = new HttpClient();
 
diff --git a/tests/standalone_2/io/http_auth_test.dart b/tests/standalone_2/io/http_auth_test.dart
index 9ab8161..8d4ba43 100644
--- a/tests/standalone_2/io/http_auth_test.dart
+++ b/tests/standalone_2/io/http_auth_test.dart
@@ -162,7 +162,7 @@
       String username = url.path.substring(1, 6);
       String password = url.path.substring(1, 6);
       if (passwordChanged) password = "${password}1";
-      Completer completer = new Completer();
+      Completer completer = new Completer<bool>();
       new Timer(const Duration(milliseconds: 10), () {
         client.addCredentials(
             url, realm, new HttpClientBasicCredentials(username, password));
diff --git a/tests/standalone_2/io/http_headers_test.dart b/tests/standalone_2/io/http_headers_test.dart
index 50a5fc6..3b2f9cb 100644
--- a/tests/standalone_2/io/http_headers_test.dart
+++ b/tests/standalone_2/io/http_headers_test.dart
@@ -2,7 +2,7 @@
 // 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.
 
-library dart.http;
+library dart._http;
 
 import "dart:async";
 import "dart:collection";
diff --git a/tests/standalone_2/io/http_parser_test.dart b/tests/standalone_2/io/http_parser_test.dart
index 7cef3723..9dc9311 100644
--- a/tests/standalone_2/io/http_parser_test.dart
+++ b/tests/standalone_2/io/http_parser_test.dart
@@ -2,7 +2,7 @@
 // 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.
 
-library dart.http;
+library dart._http;
 
 import "dart:async";
 import "dart:collection";
diff --git a/tests/standalone_2/io/http_redirect_test.dart b/tests/standalone_2/io/http_redirect_test.dart
index 9155a42..1e54bb9 100644
--- a/tests/standalone_2/io/http_redirect_test.dart
+++ b/tests/standalone_2/io/http_redirect_test.dart
@@ -8,7 +8,7 @@
 import "dart:io";
 
 Future<HttpServer> setupServer() {
-  Completer completer = new Completer();
+  Completer completer = new Completer<HttpServer>();
   HttpServer.bind("127.0.0.1", 0).then((server) {
     var handlers = new Map<String, Function>();
     addRequestHandler(
@@ -426,8 +426,8 @@
     }
 
     client
-        .getUrl(Uri
-            .parse("http://127.0.0.1:${server.port}/some/relativeToAbsolute"))
+        .getUrl(Uri.parse(
+            "http://127.0.0.1:${server.port}/some/relativeToAbsolute"))
         .then((HttpClientRequest request) {
       request.followRedirects = false;
       return request.close();
diff --git a/tests/standalone_2/io/http_reuse_server_port_test.dart b/tests/standalone_2/io/http_reuse_server_port_test.dart
index e5a35b2..da90fe7 100644
--- a/tests/standalone_2/io/http_reuse_server_port_test.dart
+++ b/tests/standalone_2/io/http_reuse_server_port_test.dart
@@ -14,7 +14,7 @@
 import "package:expect/expect.dart";
 
 Future<int> runServer(int port, int connections, bool clean) {
-  var completer = new Completer();
+  var completer = new Completer<int>();
   HttpServer.bind("127.0.0.1", port).then((server) {
     int i = 0;
     server.listen((request) {
@@ -26,8 +26,7 @@
       }
     });
 
-    Future
-        .wait(new List.generate(connections, (_) {
+    Future.wait(new List.generate(connections, (_) {
       var client = new HttpClient();
       return client
           .get("127.0.0.1", server.port, "/")
@@ -36,8 +35,7 @@
           .catchError((e) {
         if (clean) throw e;
       });
-    }))
-        .then((_) {
+    })).then((_) {
       if (clean) {
         int port = server.port;
         server.close().then((_) => completer.complete(port));
diff --git a/tests/standalone_2/io/http_server_response_test.dart b/tests/standalone_2/io/http_server_response_test.dart
index c231c4a..baa729b 100644
--- a/tests/standalone_2/io/http_server_response_test.dart
+++ b/tests/standalone_2/io/http_server_response_test.dart
@@ -103,7 +103,7 @@
   }, bytes: bytes * 2);
 
   testServerRequest((server, request) {
-    var controller = new StreamController(sync: true);
+    var controller = new StreamController<List<int>>(sync: true);
     request.response.addStream(controller.stream).then((response) {
       response.close();
       response.done.then((_) => server.close());
diff --git a/tests/standalone_2/io/secure_socket_argument_test.dart b/tests/standalone_2/io/secure_socket_argument_test.dart
index 330b309..676801e 100644
--- a/tests/standalone_2/io/secure_socket_argument_test.dart
+++ b/tests/standalone_2/io/secure_socket_argument_test.dart
@@ -5,6 +5,8 @@
 import "package:expect/expect.dart";
 import "dart:io";
 
+const SERVER_ADDRESS = "127.0.0.1";
+
 void testServerSocketArguments() {
   Expect.throws(() => SecureServerSocket.bind(SERVER_ADDRESS, 65536, null));
   Expect.throws(() => SecureServerSocket.bind(SERVER_ADDRESS, -1, null));
diff --git a/tests/standalone_2/io/test_harness_analyzer_test.dart b/tests/standalone_2/io/test_harness_analyzer_test.dart
deleted file mode 100644
index 350cbd9..0000000
--- a/tests/standalone_2/io/test_harness_analyzer_test.dart
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright (c) 2013, 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 '../../../tools/testing/dart/main.dart' as test_dart;
-import '../../../tools/testing/dart/launch_browser.dart' as launch_browser;
-
-// The purpose of this test is to run the analyzer on it and make sure our
-// testing scripts are free of warnings/errors.
-
-void main() {}
diff --git a/tests/standalone_2/io/test_runner_exit_code_script.dart b/tests/standalone_2/io/test_runner_exit_code_script.dart
deleted file mode 100644
index e298430..0000000
--- a/tests/standalone_2/io/test_runner_exit_code_script.dart
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (c) 2012, 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.
-
-// Simulates a use of test_progress during a failing run of test.dart.
-
-import "dart:io";
-import "../../../tools/testing/dart/test_progress.dart";
-import "../../../tools/testing/dart/test_runner.dart";
-import "../../../tools/testing/dart/test_options.dart";
-
-main(List<String> arguments) {
-  var progressType = arguments[0];
-  // Build a progress indicator.
-  var startTime = new DateTime.now();
-  var progress = new ProgressIndicator.fromName(progressType, startTime, false);
-  if (progressType == 'buildbot') {
-    BuildbotProgressIndicator.stepName = 'myStepName';
-  }
-  // Build a dummy test case.
-  var configuration = new TestOptionsParser().parse(['--timeout', '2'])[0];
-  var dummyCommand = new Command("noop", []);
-  var testCase = new TestCase('failing_test.dart', [dummyCommand],
-      configuration, (_) => null, new Set<String>.from(['PASS']));
-
-  // Simulate the test.dart use of the progress indicator.
-  progress.testAdded();
-  progress.allTestsKnown();
-  progress.start(testCase);
-  new CommandOutput.fromCase(testCase, dummyCommand, 1, false, false, [], [],
-      new DateTime.now().difference(startTime), false);
-  progress.done(testCase);
-  progress.allDone();
-}
diff --git a/tests/standalone_2/io/web_socket_protocol_processor_test.dart b/tests/standalone_2/io/web_socket_protocol_processor_test.dart
index 271d141..75ac75a 100644
--- a/tests/standalone_2/io/web_socket_protocol_processor_test.dart
+++ b/tests/standalone_2/io/web_socket_protocol_processor_test.dart
@@ -2,7 +2,7 @@
 // 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.
 
-library dart.io;
+library dart._http;
 
 import "package:async_helper/async_helper.dart";
 import "package:expect/expect.dart";
@@ -99,7 +99,7 @@
     int messageCount = 0;
     // Use the same web socket protocol transformer for all frames.
     var transformer = new _WebSocketProtocolTransformer();
-    var controller = new StreamController(sync: true);
+    var controller = new StreamController<List<int>>(sync: true);
     WebSocketMessageCollector mc = new WebSocketMessageCollector(
         controller.stream.transform(transformer), message);
 
@@ -161,7 +161,7 @@
 void testFragmentedMessages() {
   // Use the same web socket protocol transformer for all frames.
   var transformer = new _WebSocketProtocolTransformer();
-  var controller = new StreamController(sync: true);
+  var controller = new StreamController<List<int>>(sync: true);
   WebSocketMessageCollector mc =
       new WebSocketMessageCollector(controller.stream.transform(transformer));
 
@@ -222,7 +222,7 @@
 
 void testUnmaskedMessage() {
   var transformer = new _WebSocketProtocolTransformer(true);
-  var controller = new StreamController(sync: true);
+  var controller = new StreamController<List<int>>(sync: true);
   asyncStart();
   controller.stream.transform(transformer).listen((_) {}, onError: (e) {
     asyncEnd();
diff --git a/tests/standalone_2/standalone_2.status b/tests/standalone_2/standalone_2.status
index 10647f5..559025f 100644
--- a/tests/standalone_2/standalone_2.status
+++ b/tests/standalone_2/standalone_2.status
@@ -111,18 +111,8 @@
 [ $compiler == dartk || $compiler == dartkb || $compiler == dartkp ]
 io/file_error_test: RuntimeError
 io/file_test: RuntimeError
-io/http_auth_digest_test: RuntimeError
-io/http_auth_test: RuntimeError
-io/http_cookie_date_test: CompileTimeError
-io/http_headers_test: CompileTimeError
-io/http_parser_test: CompileTimeError
-io/http_redirect_test: RuntimeError
-io/http_reuse_server_port_test: RuntimeError
-io/http_server_response_test: RuntimeError
 io/regress_10026_test: RuntimeError
-io/secure_socket_argument_test: CompileTimeError
 io/web_socket_pipe_test: RuntimeError
-io/web_socket_protocol_processor_test: CompileTimeError
 io/zlib_test: RuntimeError
 
 [ $mode == product || $runtime == dart_precompiled ]
@@ -144,7 +134,5 @@
 io/many_directory_operations_test: SkipSlow
 io/many_file_operations_test: SkipSlow
 io/raw_datagram_read_all_test: Pass, Fail # Timing dependent.
-io/skipping_dart2js_compilations_test: SkipSlow
-io/test_runner_test: SkipSlow
 package/*: SkipByDesign # Launches VMs in interesting ways.
 typed_data_isolate_test: SkipSlow
diff --git a/tests/standalone_2/standalone_2_analyzer.status b/tests/standalone_2/standalone_2_analyzer.status
index 6de6cbf..a5b12fb 100644
--- a/tests/standalone_2/standalone_2_analyzer.status
+++ b/tests/standalone_2/standalone_2_analyzer.status
@@ -5,15 +5,10 @@
 [ $compiler == dart2analyzer ]
 deferred_transitive_import_error_test: Skip # Contains intentional errors.
 io/directory_invalid_arguments_test: CompileTimeError
-io/http_cookie_date_test: Pass, StaticWarning, CompileTimeError # Issue 28843
-io/http_headers_test: Pass, StaticWarning, CompileTimeError # Issue 28843
-io/http_parser_test: Pass, StaticWarning, CompileTimeError # Issue 28843
 io/process_exit_negative_test: Skip
 io/process_invalid_arguments_test: CompileTimeError
 io/raw_secure_server_socket_argument_test: CompileTimeError
-io/secure_socket_argument_test: CompileTimeError
 io/stdout_bad_argument_test: CompileTimeError
-io/web_socket_protocol_processor_test: Pass, StaticWarning, CompileTimeError # Issue 28843
 package/package_isolate_test: CompileTimeError
 package/scenarios/both_dir_and_file/prefers_packages_file_test: CompileTimeError
 package/scenarios/invalid/invalid_package_name_test: Crash, OK # Analyzer exits on invalid package config
diff --git a/tests/standalone_2/standalone_2_kernel.status b/tests/standalone_2/standalone_2_kernel.status
index 1441048..5a8f844 100644
--- a/tests/standalone_2/standalone_2_kernel.status
+++ b/tests/standalone_2/standalone_2_kernel.status
@@ -17,21 +17,11 @@
 [ $compiler == app_jitk ]
 io/file_error_test: RuntimeError
 io/file_test: RuntimeError
-io/http_auth_digest_test: RuntimeError
-io/http_auth_test: RuntimeError
-io/http_cookie_date_test: CompileTimeError
-io/http_headers_test: CompileTimeError
-io/http_parser_test: CompileTimeError
-io/http_redirect_test: RuntimeError
-io/http_reuse_server_port_test: RuntimeError
-io/http_server_response_test: RuntimeError
 io/platform_test: RuntimeError
 io/regress_10026_test: RuntimeError
-io/secure_socket_argument_test: CompileTimeError
 io/test_extension_fail_test: RuntimeError
 io/test_extension_test: RuntimeError
 io/web_socket_pipe_test: RuntimeError
-io/web_socket_protocol_processor_test: CompileTimeError
 io/zlib_test: RuntimeError
 
 [ $compiler == dartkb ]
@@ -49,14 +39,6 @@
 
 [ $compiler == dartkp ]
 io/arguments_test: Fail # Test harness passes runtime arguments to the compiler
-io/test_runner_test: SkipByDesign # Is not relevant for AOT.
-
-[ $compiler == fasta ]
-io/http_cookie_date_test: CompileTimeError
-io/http_headers_test: CompileTimeError
-io/http_parser_test: CompileTimeError
-io/secure_socket_argument_test: CompileTimeError
-io/web_socket_protocol_processor_test: CompileTimeError
 
 [ $system == android ]
 entrypoints_verification_test: Skip # Requires shared objects which the test script doesn't "adb push".
@@ -90,7 +72,6 @@
 
 [ $compiler == dartkp && $mode == debug && $runtime == dart_precompiled ]
 io/raw_socket_test: Crash
-io/skipping_dart2js_compilations_test: Crash
 io/socket_exception_test: Pass, Crash
 io/socket_finalizer_test: Pass, Crash
 io/socket_info_ipv4_test: Pass, Crash
diff --git a/tests/standalone_2/standalone_2_precompiled.status b/tests/standalone_2/standalone_2_precompiled.status
index e17acac..38d1e2c 100644
--- a/tests/standalone_2/standalone_2_precompiled.status
+++ b/tests/standalone_2/standalone_2_precompiled.status
@@ -43,7 +43,6 @@
 io/regress_7679_test: Skip
 io/secure_unauthorized_test: Skip
 io/signals_test: Skip
-io/skipping_dart2js_compilations_test: RuntimeError # Issue 30008
 io/stdin_sync_test: Skip
 io/stdio_implicit_close_test: Skip
 io/stdio_nonblocking_test: Skip
@@ -60,7 +59,6 @@
 
 [ $runtime == dart_precompiled && $checked ]
 io/namespace_test: RuntimeError
-io/test_runner_test: RuntimeError
 
 [ $compiler == app_jit || $compiler == precompiler ]
 io/compile_all_test: Skip # Incompatible flag --compile_all
diff --git a/tests/standalone_2/standalone_2_vm.status b/tests/standalone_2/standalone_2_vm.status
index ee8622a..e77465f 100644
--- a/tests/standalone_2/standalone_2_vm.status
+++ b/tests/standalone_2/standalone_2_vm.status
@@ -18,12 +18,8 @@
 io/platform_test: Skip # Platform.executable
 io/test_extension_fail_test: Skip # Platform.executable
 io/test_extension_test: Skip # Platform.executable
-io/test_runner_test: RuntimeError # Issue 33168
 regress_26031_test: Skip # Platform.resolvedExecutable
 
-[ $runtime == vm ]
-io/test_runner_test: Skip # Spawns a process which runs in Dart2 mode.
-
 [ $system == android ]
 io/file_stat_test: Skip # Issue 26376
 io/file_system_watcher_test: Skip # Issue 26376
@@ -58,7 +54,6 @@
 
 [ $system == windows ]
 io/process_sync_test: Pass, Timeout # Issue 24596
-io/skipping_dart2js_compilations_test: Skip # Issue 19551.
 io/sleep_test: Pass, Fail # Issue 25757
 io/socket_info_ipv6_test: Skip
 verbose_gc_to_bmu_test: Skip
@@ -99,7 +94,6 @@
 io/dart_std_io_pipe_test: Timeout, Pass
 io/http_client_stays_alive_test: Skip # Spawns process in Dart2 mode.
 io/process_sync_test: Timeout, Pass
-io/skipping_dart2js_compilations_test: Skip # Spawns process in Dart2 mode.
 
 [ $arch == simdbc || $arch == simdbc64 ]
 full_coverage_test: Skip # TODO(vegorov) SIMDBC interpreter doesn't support coverage yet.
diff --git a/tools/VERSION b/tools/VERSION
index 57a80b5..1a2f417 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -31,9 +31,9 @@
 #
 CHANNEL dev
 MAJOR 2
-MINOR 4
+MINOR 5
 PATCH 0
 PRERELEASE 0
-PRERELEASE_PATCH 1
+PRERELEASE_PATCH 0
 ABI_VERSION 5
 OLDEST_SUPPORTED_ABI_VERSION 3
diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json
index 9eb3373..3530395 100644
--- a/tools/bots/test_matrix.json
+++ b/tools/bots/test_matrix.json
@@ -100,15 +100,11 @@
       "out/DebugIA32/",
       "out/DebugSIMARM/",
       "out/DebugSIMARM64/",
-      "out/DebugSIMDBC/",
-      "out/DebugSIMDBC64/",
       "out/DebugX64/",
       "out/ProductX64/",
       "out/ReleaseIA32/",
       "out/ReleaseSIMARM/",
       "out/ReleaseSIMARM64/",
-      "out/ReleaseSIMDBC/",
-      "out/ReleaseSIMDBC64/",
       "out/ReleaseX64/",
       "third_party/pkg/",
       "third_party/pkg_tested/",
@@ -192,6 +188,7 @@
       "pkg/pkg.status",
       "pkg/smith/",
       "pkg/status_file/",
+      "pkg/test_runner/",
       "pkg/vm/",
       "runtime/",
       "sdk/",
@@ -1778,6 +1775,11 @@
           "arguments": ["--fatal-warnings", "pkg/telemetry"]
         },
         {
+          "name": "analyze pkg/test_runner",
+          "script": "out/ReleaseX64/dart-sdk/bin/dartanalyzer",
+          "arguments": ["--fatal-warnings", "pkg/test_runner"]
+        },
+        {
           "name": "analyze pkg/testing",
           "script": "out/ReleaseX64/dart-sdk/bin/dartanalyzer",
           "arguments": ["--fatal-warnings", "pkg/testing"]
@@ -1802,12 +1804,6 @@
           "arguments": ["--fatal-warnings", "tools/gardening"]
         },
         {
-          "name": "analyze tools/testing/dart",
-          "script": "out/ReleaseX64/dart-sdk/bin/dartanalyzer",
-          "arguments": ["--fatal-warnings", "tools/testing/dart"]
-        },
-
-        {
           "name": "dartanalyzer --batch tests",
           "arguments": [
             "-nanalyzer-${system}"]
@@ -2063,7 +2059,7 @@
           "script": "tools/build.py",
           "arguments": [
             "--mode=debug,release",
-            "--arch=ia32,simarm,simdbc,simdbc64",
+            "--arch=ia32,simarm",
             "runtime"
           ]
         },
@@ -2106,12 +2102,13 @@
           "arguments": [
             "--mode=release",
             "--arch=x64",
-            "create_sdk"
+            "create_sdk_with_abi_versions"
           ]
         },
         {
           "name": "run tests",
-          "script": "tools/run_abi_tests.py"
+          "script": "tools/run_abi_tests.py",
+          "testRunner": true
         }
       ]
     }
diff --git a/tools/bots/try_benchmarks.sh b/tools/bots/try_benchmarks.sh
index a501a0d..5ec145e 100755
--- a/tools/bots/try_benchmarks.sh
+++ b/tools/bots/try_benchmarks.sh
@@ -89,6 +89,7 @@
       third_party/d8/linux/ia32/snapshot_blob.bin \
       out/ReleaseIA32/vm_outline_strong.dill \
       out/ReleaseIA32/vm_platform_strong.dill \
+      out/ReleaseIA32/gen/kernel_service.dill \
       third_party/firefox_jsshell/linux/ \
       out/ReleaseIA32/dart-sdk \
       tools/dart2js/angular2_testing_deps \
@@ -170,6 +171,7 @@
       third_party/d8/linux/ia32/snapshot_blob.bin \
       out/ReleaseIA32/vm_outline_strong.dill \
       out/ReleaseIA32/vm_platform_strong.dill \
+      out/ReleaseIA32/gen/kernel_service.dill \
       third_party/firefox_jsshell/linux/ \
       out/ReleaseIA32/dart-sdk \
       tools/dart2js/angular2_testing_deps \
@@ -216,6 +218,7 @@
     out/ReleaseIA32/dart --print_metrics pkg/analyzer_cli/bin/analyzer.dart --dart-sdk=sdk hello.dart
     out/ReleaseIA32/run_vm_tests InitialRSS
     out/ReleaseIA32/run_vm_tests GenKernelKernelLoadKernel
+    out/ReleaseIA32/run_vm_tests KernelServiceCompileAll
     cd ..
     rm -rf tmp
   elif [ "$command" = linux-x64-build ] ||
@@ -244,6 +247,7 @@
       third_party/d8/linux/x64/snapshot_blob.bin \
       out/ReleaseX64/vm_outline_strong.dill \
       out/ReleaseX64/vm_platform_strong.dill \
+      out/ReleaseX64/gen/kernel_service.dill \
       out/ReleaseX64/dart-sdk \
       $simdbc_dart \
       out/ReleaseX64/dart \
@@ -345,6 +349,7 @@
       third_party/d8/linux/x64/snapshot_blob.bin \
       out/ReleaseX64/vm_outline_strong.dill \
       out/ReleaseX64/vm_platform_strong.dill \
+      out/ReleaseX64/gen/kernel_service.dill \
       out/ReleaseX64/dart-sdk \
       $simdbc_dart \
       out/ReleaseX64/dart \
@@ -397,6 +402,7 @@
     out/ReleaseX64/dart --packages=.packages pkg/kernel/test/binary_bench.dart --golem AstFromBinaryLazy out/ReleaseX64/vm_platform_strong.dill
     out/ReleaseX64/run_vm_tests InitialRSS
     out/ReleaseX64/run_vm_tests GenKernelKernelLoadKernel
+    out/ReleaseX64/run_vm_tests KernelServiceCompileAll
     cd ..
     rm -rf tmp
   else
diff --git a/tools/dom/docs/docs.status b/tools/dom/docs/docs.status
index d873a78..a502066 100644
--- a/tools/dom/docs/docs.status
+++ b/tools/dom/docs/docs.status
@@ -3,5 +3,5 @@
 # BSD-style license that can be found in the LICENSE file.
 
 # docs.dart is a tool that only runs on the VM
-[ $compiler == dart2js]
+[ $compiler == dart2js ]
 *: Skip
diff --git a/tools/experimental_features.yaml b/tools/experimental_features.yaml
index 0fa456c..1a82f0f 100644
--- a/tools/experimental_features.yaml
+++ b/tools/experimental_features.yaml
@@ -8,6 +8,18 @@
 # developed and are not yet shipped. Experimental feature flags are expected
 # to be relatively short-lived.
 #
+# ### Code Generation
+#
+# When you change this file, run the following to update analyzer and kernel:
+#
+# analyzer:
+#   dart pkg/analyzer/tool/experiments/generate.dart
+#
+# kernel:
+#   pkg/front_end/tool/fasta generate-experimental-flags
+#
+# ### Overview
+#
 # Each entry in this map corresponds to an experiment,
 # and contains the following parts:
 #
diff --git a/tools/status_clean.dart b/tools/status_clean.dart
deleted file mode 100644
index 7d8bd2f..0000000
--- a/tools/status_clean.dart
+++ /dev/null
@@ -1,419 +0,0 @@
-// Copyright (c) 2014, 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.
-
-library status_clean;
-
-import "dart:async";
-import "dart:convert" show json, utf8;
-import "dart:io";
-import "testing/dart/multitest.dart";
-import "testing/dart/status_file_parser.dart";
-import "testing/dart/test_suite.dart"
-    show
-        multiHtmlTestGroupRegExp,
-        multiTestRegExp,
-        multiHtmlTestRegExp,
-        TestUtils;
-import "testing/dart/utils.dart" show Path;
-
-// [STATUS_TUPLES] is a list of (suite-name, directory, status-file)-tuples.
-final STATUS_TUPLES = [
-  ["corelib_2", "tests/corelib_2", "tests/corelib_2/corelib_2.status"],
-  ["standalone", "tests/standalone", "tests/standalone/standalone.status"],
-  ["pkg", "pkg", "pkg/pkg.status"],
-  ["utils", "tests/utils", "tests/utils/utils.status"],
-  ["samples", "samples", "samples/samples.status"],
-  ["analyze_library", "sdk", "tests/lib_2/analyzer/analyze_library.status"],
-  [
-    "dart2js_extra",
-    "tests/compiler/dart2js_extra",
-    "tests/compiler/dart2js_extra/dart2js_extra.status"
-  ],
-  [
-    "dart2js_native",
-    "tests/compiler/dart2js_native",
-    "tests/compiler/dart2js_native/dart2js_native.status"
-  ],
-  [
-    "dart2js",
-    "tests/compiler/dart2js",
-    "tests/compiler/dart2js/dart2js.status"
-  ],
-  [
-    "benchmark_smoke",
-    "tests/benchmark_smoke",
-    "tests/benchmark_smoke/benchmark_smoke.status"
-  ],
-];
-
-void main(List<String> args) {
-  TestUtils.setDartDirUri(Platform.script.resolve('..'));
-  usage() {
-    print("Usage: ${Platform.executable} <deflake|remove-nonexistent-tests>");
-    exit(1);
-  }
-
-  if (args.length == 0) usage();
-
-  if (args[0] == 'deflake') {
-    run(new StatusFileDeflaker());
-  } else if (args[0] == 'remove-nonexistent-tests') {
-    run(new StatusFileNonExistentTestRemover());
-  } else {
-    usage();
-  }
-}
-
-run(StatusFileProcessor processor) {
-  Future.forEach(STATUS_TUPLES, (List tuple) {
-    String suiteName = tuple[0];
-    String directory = tuple[1];
-    String filePath = tuple[2];
-    print("Processing $filePath");
-    return processor.run(suiteName, directory, filePath);
-  });
-}
-
-abstract class StatusFileProcessor {
-  Future run(String suiteName, String directory, String filePath);
-
-  Future<List<Section>> _readSections(String filePath) {
-    File file = new File(filePath);
-
-    if (file.existsSync()) {
-      var completer = new Completer();
-      List<Section> sections = new List<Section>();
-
-      ReadConfigurationInto(new Path(file.path), sections, () {
-        completer.complete(sections);
-      });
-      return completer.future;
-    }
-    return new Future.value([]);
-  }
-}
-
-class StatusFileNonExistentTestRemover extends StatusFileProcessor {
-  final MultiTestDetector multiTestDetector = new MultiTestDetector();
-  final TestFileLister testFileLister = new TestFileLister();
-
-  Future run(String suiteName, String directory, String filePath) {
-    return _readSections(filePath).then((List<Section> sections) {
-      Set<int> invalidLines = _analyzeStatusFile(directory, filePath, sections);
-      if (invalidLines.length > 0) {
-        return _writeFixedStatusFile(filePath, invalidLines);
-      }
-      return new Future.value();
-    });
-  }
-
-  bool _testExists(String filePath, List<String> testFiles, String directory,
-      TestRule rule) {
-    // TODO: Unify this regular expression matching with status_file_parser.dart
-    List<RegExp> getRuleRegex(String name) {
-      return name
-          .split("/")
-          .map((name) => new RegExp(name.replaceAll('*', '.*')))
-          .toList();
-    }
-
-    bool matchRegexp(List<RegExp> patterns, String str) {
-      var parts = str.split("/");
-      if (patterns.length > parts.length) {
-        return false;
-      }
-      // NOTE: patterns.length <= parts.length
-      for (var i = 0; i < patterns.length; i++) {
-        if (!patterns[i].hasMatch(parts[i])) {
-          return false;
-        }
-      }
-      return true;
-    }
-
-    var rulePattern = getRuleRegex(rule.name);
-    return testFiles.any((String file) {
-      // TODO: Use test_suite.dart's [buildTestCaseDisplayName] instead.
-      var filePath = new Path(file).relativeTo(new Path(directory));
-      String baseTestName = _concat(
-          "${filePath.directoryPath}", "${filePath.filenameWithoutExtension}");
-
-      List<String> testNames = [];
-      for (var name in multiTestDetector.getMultitestNames(file)) {
-        testNames.add(_concat(baseTestName, name));
-      }
-
-      // If it is not a multitest the testname is [baseTestName]
-      if (testNames.isEmpty) {
-        testNames.add(baseTestName);
-      }
-
-      return testNames
-          .any((String testName) => matchRegexp(rulePattern, testName));
-    });
-  }
-
-  Set<int> _analyzeStatusFile(
-      String directory, String filePath, List<Section> sections) {
-    var invalidLines = new Set<int>();
-    var dartFiles = testFileLister.listTestFiles(directory);
-    for (var section in sections) {
-      for (var rule in section.testRules) {
-        if (!_testExists(filePath, dartFiles, directory, rule)) {
-          print("Invalid rule: ${rule.name} in file "
-              "$filePath:${rule.lineNumber}");
-          invalidLines.add(rule.lineNumber);
-        }
-      }
-    }
-    return invalidLines;
-  }
-
-  _writeFixedStatusFile(String statusFilePath, Set<int> invalidLines) {
-    var lines = new File(statusFilePath).readAsLinesSync();
-    var outputLines = <String>[];
-    for (int i = 0; i < lines.length; i++) {
-      // The status file parser numbers lines starting with 1, not 0.
-      if (!invalidLines.contains(i + 1)) {
-        outputLines.add(lines[i]);
-      }
-    }
-    var outputFile = new File("$statusFilePath.fixed");
-    outputFile.writeAsStringSync(outputLines.join("\n"));
-  }
-
-  String _concat(String base, String part) {
-    if (base == "") return part;
-    if (part == "") return base;
-    return "$base/$part";
-  }
-}
-
-class StatusFileDeflaker extends StatusFileProcessor {
-  TestOutcomeFetcher _testOutcomeFetcher = new TestOutcomeFetcher();
-
-  Future run(String suiteName, String directory, String filePath) {
-    return _readSections(filePath).then((List<Section> sections) {
-      return _generatedDeflakedLines(suiteName, sections)
-          .then((Map<int, String> fixedLines) {
-        if (fixedLines.length > 0) {
-          return _writeFixedStatusFile(filePath, fixedLines);
-        }
-      });
-    });
-  }
-
-  Future _generatedDeflakedLines(String suiteName, List<Section> sections) {
-    var fixedLines = new Map<int, String>();
-    return Future.forEach(sections, (Section section) {
-      return Future.forEach(section.testRules, (rule) {
-        return _maybeFixStatusfileLine(suiteName, section, rule, fixedLines);
-      });
-    }).then((_) => fixedLines);
-  }
-
-  Future _maybeFixStatusfileLine(String suiteName, Section section,
-      TestRule rule, Map<int, String> fixedLines) {
-    print("Processing ${section.statusFile.location}: ${rule.lineNumber}");
-    // None of our status file lines have expressions, so we pass {} here.
-    var notedOutcomes = rule.expression
-        .evaluate({})
-        .map((name) => Expectation.byName(name))
-        .where((Expectation expectation) => !expectation.isMetaExpectation)
-        .toSet();
-
-    if (notedOutcomes.isEmpty) return new Future.value();
-
-    // TODO: [rule.name] is actually a pattern not just a testname. We should
-    // find all possible testnames this rule matches against and unify the
-    // outcomes of these tests.
-    return _testOutcomeFetcher
-        .outcomesOf(suiteName, section, rule.name)
-        .then((Set<Expectation> actualOutcomes) {
-      var outcomesThatNeverHappened = new Set<Expectation>();
-      for (Expectation notedOutcome in notedOutcomes) {
-        bool found = false;
-        for (Expectation actualOutcome in actualOutcomes) {
-          if (actualOutcome.canBeOutcomeOf(notedOutcome)) {
-            found = true;
-            break;
-          }
-        }
-        if (!found) {
-          outcomesThatNeverHappened.add(notedOutcome);
-        }
-      }
-
-      if (outcomesThatNeverHappened.length > 0 && actualOutcomes.length > 0) {
-        // Print the change to stdout.
-        print("${rule.name} "
-            "(${section.statusFile.location}:${rule.lineNumber}):");
-        print("   Actual outcomes:         ${actualOutcomes.toList()}");
-        print("   Outcomes in status file: ${notedOutcomes.toList()}");
-        print("   Outcomes in status file that never happened : "
-            "${outcomesThatNeverHappened.toList()}\n");
-
-        // Build the fixed status file line.
-        fixedLines[rule.lineNumber] =
-            '${rule.name}: ${actualOutcomes.join(', ')} '
-            '# before: ${notedOutcomes.join(', ')} / '
-            'never happened:  ${outcomesThatNeverHappened.join(', ')}';
-      }
-    });
-  }
-
-  _writeFixedStatusFile(String filePath, Map<int, String> fixedLines) {
-    var lines = new File(filePath).readAsLinesSync();
-    var outputLines = <String>[];
-    for (int i = 0; i < lines.length; i++) {
-      if (fixedLines.containsKey(i + 1)) {
-        outputLines.add(fixedLines[i + 1]);
-      } else {
-        outputLines.add(lines[i]);
-      }
-    }
-    var output = outputLines.join("\n");
-    var outputFile = new File("$filePath.deflaked");
-    outputFile.writeAsStringSync(output);
-  }
-}
-
-class MultiTestDetector {
-  final multiTestsCache = new Map<String, List<String>>();
-  final multiHtmlTestsCache = new Map<String, List<String>>();
-
-  List<String> getMultitestNames(String file) {
-    List<String> names = [];
-    names.addAll(getStandardMultitestNames(file));
-    names.addAll(getHtmlMultitestNames(file));
-    return names;
-  }
-
-  List<String> getStandardMultitestNames(String file) {
-    return multiTestsCache.putIfAbsent(file, () {
-      try {
-        var tests = new Map<String, String>();
-        var outcomes = new Map<String, Set<String>>();
-        if (multiTestRegExp.hasMatch(new File(file).readAsStringSync())) {
-          extractTestsFromMultitest(new Path(file), tests, outcomes);
-        }
-        return tests.keys.toList();
-      } catch (error) {
-        print("WARNING: Couldn't determine multitests in file ${file}: $error");
-        return [];
-      }
-    });
-  }
-
-  List<String> getHtmlMultitestNames(String file) {
-    return multiHtmlTestsCache.putIfAbsent(file, () {
-      try {
-        List<String> subtestNames = [];
-        var content = new File(file).readAsStringSync();
-
-        if (multiHtmlTestRegExp.hasMatch(content)) {
-          var matchesIter =
-              multiHtmlTestGroupRegExp.allMatches(content).iterator;
-          while (matchesIter.moveNext()) {
-            String fullMatch = matchesIter.current.group(0);
-            subtestNames.add(fullMatch.substring(fullMatch.indexOf("'") + 1));
-          }
-        }
-        return subtestNames;
-      } catch (error) {
-        print("WARNING: Couldn't determine multitests in file ${file}: $error");
-      }
-      return [];
-    });
-  }
-}
-
-class TestFileLister {
-  final Map<String, List<String>> _filesCache = {};
-
-  List<String> listTestFiles(String directory) {
-    return _filesCache.putIfAbsent(directory, () {
-      var dir = new Directory(directory);
-      // Cannot test for _test.dart because co19 tests don't have that ending.
-      var dartFiles = dir
-          .listSync(recursive: true)
-          .where((fe) => fe is File)
-          .where((file) =>
-              file.path.endsWith(".dart") || file.path.endsWith("_test.html"))
-          .map((file) => file.path)
-          .toList();
-      return dartFiles;
-    });
-  }
-}
-
-/*
- * [TestOutcomeFetcher] will fetch test results from a server using a REST-like
- * interface.
- */
-class TestOutcomeFetcher {
-  static String SERVER = '108.170.219.8';
-  static int PORT = 4540;
-
-  HttpClient _client = new HttpClient();
-
-  Future<Set<Expectation>> outcomesOf(
-      String suiteName, Section section, String testName) {
-    var pathComponents = [
-      'json',
-      'test-outcomes',
-      'outcomes',
-      Uri.encodeComponent("$suiteName/$testName")
-    ];
-    var path = pathComponents.join('/') + '/';
-    var url = new Uri(scheme: 'http', host: SERVER, port: PORT, path: path);
-
-    return _client
-        .getUrl(url)
-        .then((HttpClientRequest request) => request.close())
-        .then((HttpClientResponse response) {
-      return response
-          .transform(utf8.decoder)
-          .transform(json.decoder)
-          .first
-          .then((List testResults) {
-        var setOfActualOutcomes = new Set<Expectation>();
-
-        try {
-          for (var result in testResults) {
-            var config = result['configuration'];
-            var testResult = result['test_result'];
-            var outcome = testResult['outcome'];
-
-            // These variables are derived variables and will be set in
-            // tools/testing/dart/test_options.dart.
-            // [Mostly due to the fact that we don't have an unary !
-            //  operator in status file expressions.]
-            config['unchecked'] = !config['checked'];
-            config['unminified'] = !config['minified'];
-            config['nocsp'] = !config['csp'];
-            config['browser'] = TestUtils.isBrowserRuntime(config['runtime']);
-            config['analyzer'] =
-                TestUtils.isCommandLineAnalyzer(config['compiler']);
-            config['jscl'] =
-                TestUtils.isJsCommandLineRuntime(config['runtime']);
-
-            if (section.condition == null ||
-                section.condition.evaluate(config)) {
-              setOfActualOutcomes.add(Expectation.byName(outcome));
-            }
-          }
-          return setOfActualOutcomes;
-        } catch (error) {
-          print("Warning: Error occured while processing testoutcomes"
-              ": $error");
-          return [];
-        }
-      }).catchError((error) {
-        print("Warning: Error occured while fetching testoutcomes: $error");
-        return [];
-      });
-    });
-  }
-}
diff --git a/tools/test.py b/tools/test.py
index a847a35..18f40e3 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -13,9 +13,11 @@
 
 def Main():
   args = sys.argv[1:]
+
   tools_dir = os.path.dirname(os.path.realpath(__file__))
-  dart_test_script = string.join(
-      [tools_dir, 'testing', 'dart', 'main.dart'], os.sep)
+  repo_dir = os.path.dirname(tools_dir)
+  dart_test_script = os.path.join(
+      repo_dir, 'pkg', 'test_runner', 'bin', 'test_runner.dart')
   command = [utils.CheckedInSdkExecutable(), dart_test_script] + args
 
   # The testing script potentially needs the android platform tools in PATH so
diff --git a/tools/testing/__init__.py b/tools/testing/__init__.py
deleted file mode 100644
index 4df76e2..0000000
--- a/tools/testing/__init__.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright (c) 2011, 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 test_runner
-import utils
-
-
-# Constants used for test outcomes
-SKIP = 'skip'
-FAIL = 'fail'
-PASS = 'pass'
-OKAY = 'okay'
-TIMEOUT = 'timeout'
-CRASH = 'crash'
-SLOW = 'slow'
-
-HOST_CPUS = utils.GuessCpus()
-USE_DEFAULT_CPUS = -1
diff --git a/tools/testing/browser_README.txt b/tools/testing/browser_README.txt
deleted file mode 100644
index 2ec2f35..0000000
--- a/tools/testing/browser_README.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-Overview:
- These are the instructions to run a wide variety of browser tests using 
- test.dart or dart/tools/testing/perf_testing/run_perf_tests.py. Currently 
- the results of run_perf_tests are uploaded to
- https://dartperf.googleplex.com/. 
- 
-========= General Browser Setup ==========
-
-See instructions on:
-https://code.google.com/p/dart/wiki/BrowserTestSetup
-
-========= Proceed further only if you also want to run performance tests.======
-
-1) Pull down benchmarks from internal repo (Google only): goto/dartbrowsersetup
-
-2) Create a directory in called appengine-python in third_party. Download the 
-   Linux/Other Platforms .zip file, and place the contents in the directory 
-   you just created. 
-   http://code.google.com/appengine/downloads.html#Google_App_Engine_SDK_for_Python
-
-3) Run the tests! While standing in dart/tools/testing/perf_testing, run 
-   $> python run_perf_tests.py --forever --verbose 
-   to run all the tests (browser performance, language correctness in the
-   browser, command line performance, and self-hosted compile time and compiled
-   code size). 
-
-   You can run individual tests by adding the particular option (such as 
-   --language) when running create_graph.py. Type "create_graph.py -h" for a 
-   full list of the options.
diff --git a/tools/testing/dart/co19_test.dart b/tools/testing/dart/co19_test.dart
deleted file mode 100644
index fd5a7a3..0000000
--- a/tools/testing/dart/co19_test.dart
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (c) 2012, 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.
-
-/// Tool for running co19 tests. Used when updating co19.
-///
-/// Currently, this tool is merely a convenience around multiple
-/// invocations of test.dart. Long term, we hope to evolve this into a
-/// script that can automate most of the tasks necessary when updating
-/// co19.
-///
-/// Usage:
-/// [: dart tools/testing/dart/co19_test.dart :]
-import 'configuration.dart';
-import 'options.dart';
-import 'test_configurations.dart';
-
-const List<String> COMMON_ARGUMENTS = const <String>[
-  '--report',
-  '--progress=diff',
-  'co19'
-];
-
-const List<List<String>> COMMAND_LINES = const <List<String>>[
-  const <String>['-mrelease,debug', '-rvm', '-cnone'],
-  const <String>['-mrelease,debug', '-rvm', '-cnone', '--checked'],
-  const <String>['-mrelease', '-rnone', '-cdart2analyzer'],
-  const <String>['-mrelease', '-rd8', '-cdart2js', '--use-sdk'],
-  const <String>[
-    '-mrelease',
-    '-rd8,jsshell',
-    '-cdart2js',
-    '--use-sdk',
-    '--minified'
-  ],
-  const <String>[
-    '-mrelease',
-    '-rd8,jsshell',
-    '-cdart2js',
-    '--use-sdk',
-    '--checked'
-  ],
-  const <String>[
-    '-mrelease',
-    '-rd8,jsshell',
-    '-cdart2js',
-    '--use-sdk',
-    '--checked',
-    '--fast-startup'
-  ],
-];
-
-void main(List<String> args) {
-  var optionsParser = new OptionsParser();
-  var configurations = <TestConfiguration>[];
-  for (var commandLine in COMMAND_LINES) {
-    var arguments = <String>[];
-    arguments.addAll(COMMON_ARGUMENTS);
-    arguments.addAll(args);
-    arguments.addAll(commandLine);
-    configurations.addAll(optionsParser.parse(arguments));
-  }
-
-  if (configurations != null || configurations.length > 0) {
-    testConfigurations(configurations);
-  }
-}
diff --git a/tools/testing/dart/package_testing_support.dart b/tools/testing/dart/package_testing_support.dart
deleted file mode 100644
index 99876a2..0000000
--- a/tools/testing/dart/package_testing_support.dart
+++ /dev/null
@@ -1,17 +0,0 @@
-// 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.md file.
-
-import 'configuration.dart';
-import 'options.dart';
-import 'repository.dart';
-import 'test_configurations.dart';
-
-void main(List<String> arguments) {
-  Repository.uri = Uri.base;
-  var configurations = <TestConfiguration>[];
-  for (var argument in arguments) {
-    configurations.addAll(new OptionsParser().parse(argument.split(" ")));
-  }
-  testConfigurations(configurations);
-}
diff --git a/tools/testing/dart/pubspec.yaml b/tools/testing/dart/pubspec.yaml
deleted file mode 100644
index 4ddbba8..0000000
--- a/tools/testing/dart/pubspec.yaml
+++ /dev/null
@@ -1,7 +0,0 @@
-# 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.
-
-# This file is only here that so certain Dart editors recognize this is a Dart
-# project.
-name: testing_tools
diff --git a/tools/testing/dart/test_runner.dart b/tools/testing/dart/test_runner.dart
deleted file mode 100644
index bbe9a68..0000000
--- a/tools/testing/dart/test_runner.dart
+++ /dev/null
@@ -1,1778 +0,0 @@
-// Copyright (c) 2012, 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.
-
-/**
- * Classes and methods for executing tests.
- *
- * This module includes:
- * - Managing parallel execution of tests, including timeout checks.
- * - Evaluating the output of each test as pass/fail/crash/timeout.
- */
-import 'dart:async';
-import 'dart:collection';
-import 'dart:convert';
-// We need to use the 'io' prefix here, otherwise io.exitCode will shadow
-// CommandOutput.exitCode in subclasses of CommandOutput.
-import 'dart:io' as io;
-import 'dart:math' as math;
-
-import "package:status_file/expectation.dart";
-
-import 'android.dart';
-import 'browser_controller.dart';
-import 'command.dart';
-import 'command_output.dart';
-import 'configuration.dart';
-import 'dependency_graph.dart';
-import 'repository.dart';
-import 'runtime_configuration.dart';
-import 'test_progress.dart';
-import 'test_suite.dart';
-import 'utils.dart';
-
-const int unhandledCompilerExceptionExitCode = 253;
-const int parseFailExitCode = 245;
-const int slowTimeoutMultiplier = 4;
-const int extraSlowTimeoutMultiplier = 8;
-const int nonUtfFakeExitCode = 0xFFFD;
-
-const cannotOpenDisplayMessage = 'Gtk-WARNING **: cannot open display';
-const failedToRunCommandMessage = 'Failed to run command. return code=1';
-
-typedef void TestCaseEvent(TestCase testCase);
-typedef void ExitCodeEvent(int exitCode);
-typedef void EnqueueMoreWork(ProcessQueue queue);
-typedef void Action();
-typedef Future<AdbCommandResult> StepFunction();
-
-/// Some IO tests use these variables and get confused if the host environment
-/// variables are inherited so they are excluded.
-const _excludedEnvironmentVariables = const [
-  'http_proxy',
-  'https_proxy',
-  'no_proxy',
-  'HTTP_PROXY',
-  'HTTPS_PROXY',
-  'NO_PROXY'
-];
-
-/**
- * TestCase contains all the information needed to run a test and evaluate
- * its output.  Running a test involves starting a separate process, with
- * the executable and arguments given by the TestCase, and recording its
- * stdout and stderr output streams, and its exit code.  TestCase only
- * contains static information about the test; actually running the test is
- * performed by [ProcessQueue] using a [RunningProcess] object.
- *
- * The output information is stored in a [CommandOutput] instance contained
- * in TestCase.commandOutputs. The last CommandOutput instance is responsible
- * for evaluating if the test has passed, failed, crashed, or timed out, and the
- * TestCase has information about what the expected result of the test should
- * be.
- *
- * The TestCase has a callback function, [completedHandler], that is run when
- * the test is completed.
- */
-class TestCase extends UniqueObject {
-  // Flags set in _expectations from the optional argument info.
-  static final int HAS_RUNTIME_ERROR = 1 << 0;
-  static final int HAS_SYNTAX_ERROR = 1 << 1;
-  static final int HAS_COMPILE_ERROR = 1 << 2;
-  static final int HAS_STATIC_WARNING = 1 << 3;
-  static final int HAS_CRASH = 1 << 4;
-  /**
-   * A list of commands to execute. Most test cases have a single command.
-   * Dart2js tests have two commands, one to compile the source and another
-   * to execute it. Some isolate tests might even have three, if they require
-   * compiling multiple sources that are run in isolation.
-   */
-  List<Command> commands;
-  Map<Command, CommandOutput> commandOutputs =
-      new Map<Command, CommandOutput>();
-
-  TestConfiguration configuration;
-  String displayName;
-  int _expectations = 0;
-  int hash = 0;
-  Set<Expectation> expectedOutcomes;
-
-  TestCase(this.displayName, this.commands, this.configuration,
-      this.expectedOutcomes,
-      {TestInformation info}) {
-    // A test case should do something.
-    assert(commands.isNotEmpty);
-
-    if (info != null) {
-      _setExpectations(info);
-      hash = (info?.originTestPath?.relativeTo(Repository.dir)?.toString())
-          .hashCode;
-    }
-  }
-
-  void _setExpectations(TestInformation info) {
-    // We don't want to keep the entire (large) TestInformation structure,
-    // so we copy the needed bools into flags set in a single integer.
-    if (info.hasRuntimeError) _expectations |= HAS_RUNTIME_ERROR;
-    if (info.hasSyntaxError) _expectations |= HAS_SYNTAX_ERROR;
-    if (info.hasCrash) _expectations |= HAS_CRASH;
-    if (info.hasCompileError || info.hasSyntaxError) {
-      _expectations |= HAS_COMPILE_ERROR;
-    }
-    if (info.hasStaticWarning) _expectations |= HAS_STATIC_WARNING;
-  }
-
-  TestCase indexedCopy(int index) {
-    var newCommands = commands.map((c) => c.indexedCopy(index)).toList();
-    return new TestCase(
-        displayName, newCommands, configuration, expectedOutcomes)
-      .._expectations = _expectations
-      ..hash = hash;
-  }
-
-  bool get hasRuntimeError => _expectations & HAS_RUNTIME_ERROR != 0;
-  bool get hasStaticWarning => _expectations & HAS_STATIC_WARNING != 0;
-  bool get hasSyntaxError => _expectations & HAS_SYNTAX_ERROR != 0;
-  bool get hasCompileError => _expectations & HAS_COMPILE_ERROR != 0;
-  bool get hasCrash => _expectations & HAS_CRASH != 0;
-  bool get isNegative =>
-      hasCompileError ||
-      hasRuntimeError && configuration.runtime != Runtime.none ||
-      displayName.contains("negative_test");
-
-  bool get unexpectedOutput {
-    var outcome = this.result;
-    return !expectedOutcomes.any((expectation) {
-      return outcome.canBeOutcomeOf(expectation);
-    });
-  }
-
-  Expectation get result => lastCommandOutput.result(this);
-  Expectation get realResult => lastCommandOutput.realResult(this);
-  Expectation get realExpected {
-    if (hasCrash) {
-      return Expectation.crash;
-    }
-    if (configuration.compiler == Compiler.specParser) {
-      if (hasSyntaxError) {
-        return Expectation.syntaxError;
-      }
-    } else if (hasCompileError) {
-      if (hasRuntimeError && configuration.runtime != Runtime.none) {
-        return Expectation.fail;
-      }
-      return Expectation.compileTimeError;
-    }
-    if (hasRuntimeError) {
-      if (configuration.runtime != Runtime.none) {
-        return Expectation.runtimeError;
-      }
-      return Expectation.pass;
-    }
-    if (displayName.contains("negative_test")) {
-      return Expectation.fail;
-    }
-    if (configuration.compiler == Compiler.dart2analyzer && hasStaticWarning) {
-      return Expectation.staticWarning;
-    }
-    return Expectation.pass;
-  }
-
-  CommandOutput get lastCommandOutput {
-    if (commandOutputs.length == 0) {
-      throw new Exception("CommandOutputs is empty, maybe no command was run? ("
-          "displayName: '$displayName', "
-          "configurationString: '$configurationString')");
-    }
-    return commandOutputs[commands[commandOutputs.length - 1]];
-  }
-
-  Command get lastCommandExecuted {
-    if (commandOutputs.length == 0) {
-      throw new Exception("CommandOutputs is empty, maybe no command was run? ("
-          "displayName: '$displayName', "
-          "configurationString: '$configurationString')");
-    }
-    return commands[commandOutputs.length - 1];
-  }
-
-  int get timeout {
-    var result = configuration.timeout;
-    if (expectedOutcomes.contains(Expectation.slow)) {
-      result *= slowTimeoutMultiplier;
-    } else if (expectedOutcomes.contains(Expectation.extraSlow)) {
-      result *= extraSlowTimeoutMultiplier;
-    }
-    return result;
-  }
-
-  String get configurationString {
-    var compiler = configuration.compiler.name;
-    var runtime = configuration.runtime.name;
-    var mode = configuration.mode.name;
-    var arch = configuration.architecture.name;
-    var checked = configuration.isChecked ? '-checked' : '';
-    return "$compiler-$runtime$checked ${mode}_$arch";
-  }
-
-  List<String> get batchTestArguments {
-    assert(commands.last is ProcessCommand);
-    return (commands.last as ProcessCommand).arguments;
-  }
-
-  bool get isFlaky {
-    if (expectedOutcomes.contains(Expectation.skip) ||
-        expectedOutcomes.contains(Expectation.skipByDesign)) {
-      return false;
-    }
-
-    return expectedOutcomes
-            .where((expectation) => expectation.isOutcome)
-            .length >
-        1;
-  }
-
-  bool get isFinished {
-    return commandOutputs.length > 0 &&
-        (!lastCommandOutput.successful ||
-            commands.length == commandOutputs.length);
-  }
-}
-
-/**
- * An OutputLog records the output from a test, but truncates it if
- * it is longer than MAX_HEAD characters, and just keeps the head and
- * the last TAIL_LENGTH characters of the output.
- */
-class OutputLog implements StreamConsumer<List<int>> {
-  static const int MAX_HEAD = 500 * 1024;
-  static const int TAIL_LENGTH = 10 * 1024;
-  List<int> head = <int>[];
-  List<int> tail;
-  List<int> complete;
-  bool dataDropped = false;
-  bool hasNonUtf8 = false;
-  StreamSubscription _subscription;
-
-  OutputLog();
-
-  void add(List<int> data) {
-    if (complete != null) {
-      throw new StateError("Cannot add to OutputLog after calling toList");
-    }
-    if (tail == null) {
-      head.addAll(data);
-      if (head.length > MAX_HEAD) {
-        tail = head.sublist(MAX_HEAD);
-        head.length = MAX_HEAD;
-      }
-    } else {
-      tail.addAll(data);
-    }
-    if (tail != null && tail.length > 2 * TAIL_LENGTH) {
-      tail = _truncatedTail();
-      dataDropped = true;
-    }
-  }
-
-  List<int> _truncatedTail() => tail.length > TAIL_LENGTH
-      ? tail.sublist(tail.length - TAIL_LENGTH)
-      : tail;
-
-  void _checkUtf8(List<int> data) {
-    try {
-      utf8.decode(data, allowMalformed: false);
-    } on FormatException {
-      hasNonUtf8 = true;
-      String malformed = utf8.decode(data, allowMalformed: true);
-      data
-        ..clear()
-        ..addAll(utf8.encode(malformed))
-        ..addAll("""
-
-  *****************************************************************************
-
-  test.dart: The output of this test contained non-UTF8 formatted data.
-
-  *****************************************************************************
-
-  """
-            .codeUnits);
-    }
-  }
-
-  List<int> toList() {
-    if (complete == null) {
-      complete = head;
-      if (dataDropped) {
-        complete.addAll("""
-
-*****************************************************************************
-
-test.dart: Data was removed due to excessive length. If you need the limit to
-be increased, please contact dart-engprod or file an issue.
-
-*****************************************************************************
-
-"""
-            .codeUnits);
-        complete.addAll(_truncatedTail());
-      } else if (tail != null) {
-        complete.addAll(tail);
-      }
-      head = null;
-      tail = null;
-      _checkUtf8(complete);
-    }
-    return complete;
-  }
-
-  @override
-  Future addStream(Stream<List<int>> stream) {
-    _subscription = stream.listen(this.add);
-    return _subscription.asFuture();
-  }
-
-  @override
-  Future close() {
-    toList();
-    return _subscription?.cancel();
-  }
-
-  Future cancel() {
-    return _subscription?.cancel();
-  }
-}
-
-// An [OutputLog] that tees the output to a file as well.
-class FileOutputLog extends OutputLog {
-  io.File _outputFile;
-  io.IOSink _sink;
-
-  FileOutputLog(this._outputFile);
-
-  @override
-  void add(List<int> data) {
-    super.add(data);
-    _sink ??= _outputFile.openWrite();
-    _sink.add(data);
-  }
-
-  @override
-  Future close() {
-    return Future.wait([
-      super.close(),
-      if (_sink != null) _sink.flush().whenComplete(_sink.close)
-    ]);
-  }
-
-  @override
-  Future cancel() {
-    return Future.wait([
-      super.cancel(),
-      if (_sink != null) _sink.flush().whenComplete(_sink.close)
-    ]);
-  }
-}
-
-// Helper to get a list of all child pids for a parent process.
-// The first element of the list is the parent pid.
-Future<List<int>> _getPidList(int pid, List<String> diagnostics) async {
-  var pids = [pid];
-  List<String> lines;
-  var startLine = 0;
-  if (io.Platform.isLinux || io.Platform.isMacOS) {
-    var result =
-        await io.Process.run("pgrep", ["-P", "${pids[0]}"], runInShell: true);
-    lines = (result.stdout as String).split('\n');
-  } else if (io.Platform.isWindows) {
-    var result = await io.Process.run(
-        "wmic",
-        [
-          "process",
-          "where",
-          "(ParentProcessId=${pids[0]})",
-          "get",
-          "ProcessId"
-        ],
-        runInShell: true);
-    lines = (result.stdout as String).split('\n');
-    // Skip first line containing header "ProcessId".
-    startLine = 1;
-  } else {
-    assert(false);
-  }
-  if (lines.length > startLine) {
-    for (var i = startLine; i < lines.length; ++i) {
-      var pid = int.tryParse(lines[i]);
-      if (pid != null) pids.add(pid);
-    }
-  } else {
-    diagnostics.add("Could not find child pids");
-    diagnostics.addAll(lines);
-  }
-  return pids;
-}
-
-/**
- * A RunningProcess actually runs a test, getting the command lines from
- * its [TestCase], starting the test process (and first, a compilation
- * process if the TestCase is a [BrowserTestCase]), creating a timeout
- * timer, and recording the results in a new [CommandOutput] object, which it
- * attaches to the TestCase.  The lifetime of the RunningProcess is limited
- * to the time it takes to start the process, run the process, and record
- * the result; there are no pointers to it, so it should be available to
- * be garbage collected as soon as it is done.
- */
-class RunningProcess {
-  ProcessCommand command;
-  int timeout;
-  bool timedOut = false;
-  DateTime startTime;
-  int pid;
-  OutputLog stdout;
-  OutputLog stderr = OutputLog();
-  List<String> diagnostics = <String>[];
-  bool compilationSkipped = false;
-  Completer<CommandOutput> completer;
-  TestConfiguration configuration;
-
-  RunningProcess(this.command, this.timeout,
-      {this.configuration, io.File outputFile}) {
-    stdout = outputFile != null ? FileOutputLog(outputFile) : OutputLog();
-  }
-
-  Future<CommandOutput> run() {
-    completer = new Completer<CommandOutput>();
-    startTime = new DateTime.now();
-    _runCommand();
-    return completer.future;
-  }
-
-  void _runCommand() {
-    if (command.outputIsUpToDate) {
-      compilationSkipped = true;
-      _commandComplete(0);
-    } else {
-      var processEnvironment = _createProcessEnvironment();
-      var args = command.arguments;
-      var processFuture = io.Process.start(command.executable, args,
-          environment: processEnvironment,
-          workingDirectory: command.workingDirectory);
-      processFuture.then((io.Process process) {
-        var stdoutFuture = process.stdout.pipe(stdout);
-        var stderrFuture = process.stderr.pipe(stderr);
-        pid = process.pid;
-
-        // Close stdin so that tests that try to block on input will fail.
-        process.stdin.close();
-        timeoutHandler() async {
-          timedOut = true;
-          if (process != null) {
-            String executable;
-            if (io.Platform.isLinux) {
-              executable = 'eu-stack';
-            } else if (io.Platform.isMacOS) {
-              // Try to print stack traces of the timed out process.
-              // `sample` is a sampling profiler but we ask it sample for 1
-              // second with a 4 second delay between samples so that we only
-              // sample the threads once.
-              executable = '/usr/bin/sample';
-            } else if (io.Platform.isWindows) {
-              var isX64 = command.executable.contains("X64") ||
-                  command.executable.contains("SIMARM64");
-              if (configuration.windowsSdkPath != null) {
-                executable = configuration.windowsSdkPath +
-                    "\\Debuggers\\${isX64 ? 'x64' : 'x86'}\\cdb.exe";
-                diagnostics.add("Using $executable to print stack traces");
-              } else {
-                diagnostics.add("win_sdk_path not found");
-              }
-            } else {
-              diagnostics.add("Capturing stack traces on"
-                  "${io.Platform.operatingSystem} not supported");
-            }
-            if (executable != null) {
-              var pids = await _getPidList(process.pid, diagnostics);
-              diagnostics.add("Process list including children: $pids");
-              for (pid in pids) {
-                List<String> arguments;
-                if (io.Platform.isLinux) {
-                  arguments = ['-p $pid'];
-                } else if (io.Platform.isMacOS) {
-                  arguments = ['$pid', '1', '4000', '-mayDie'];
-                } else if (io.Platform.isWindows) {
-                  arguments = ['-p', '$pid', '-c', '!uniqstack;qd'];
-                } else {
-                  assert(false);
-                }
-                diagnostics.add("Trying to capture stack trace for pid $pid");
-                try {
-                  var result = await io.Process.run(executable, arguments);
-                  diagnostics.addAll((result.stdout as String).split('\n'));
-                  diagnostics.addAll((result.stderr as String).split('\n'));
-                } catch (error) {
-                  diagnostics.add("Unable to capture stack traces: $error");
-                }
-              }
-            }
-
-            if (!process.kill()) {
-              diagnostics.add("Unable to kill ${process.pid}");
-            }
-          }
-        }
-
-        // Wait for the process to finish or timeout
-        process.exitCode
-            .timeout(Duration(seconds: timeout), onTimeout: timeoutHandler)
-            .then((exitCode) {
-          // This timeout is used to close stdio to the subprocess once we got
-          // the exitCode. Sometimes descendants of the subprocess keep stdio
-          // handles alive even though the direct subprocess is dead.
-          Future.wait([stdoutFuture, stderrFuture]).timeout(MAX_STDIO_DELAY,
-              onTimeout: () async {
-            DebugLogger.warning(
-                "$MAX_STDIO_DELAY_PASSED_MESSAGE (command: $command)");
-            await stdout.cancel();
-            await stderr.cancel();
-            _commandComplete(exitCode);
-            return null;
-          }).then((_) {
-            if (stdout is FileOutputLog) {
-              // Prevent logging data that has already been written to a file
-              // and is unlikely too add value in the logs because the command
-              // succeeded.
-              stdout.complete = <int>[];
-            }
-            _commandComplete(exitCode);
-          });
-        });
-      }).catchError((e) {
-        // TODO(floitsch): should we try to report the stacktrace?
-        print("Process error:");
-        print("  Command: $command");
-        print("  Error: $e");
-        _commandComplete(-1);
-        return true;
-      });
-    }
-  }
-
-  void _commandComplete(int exitCode) {
-    var commandOutput = _createCommandOutput(command, exitCode);
-    completer.complete(commandOutput);
-  }
-
-  CommandOutput _createCommandOutput(ProcessCommand command, int exitCode) {
-    List<int> stdoutData = stdout.toList();
-    List<int> stderrData = stderr.toList();
-    if (stdout.hasNonUtf8 || stderr.hasNonUtf8) {
-      // If the output contained non-utf8 formatted data, then make the exit
-      // code non-zero if it isn't already.
-      if (exitCode == 0) {
-        exitCode = nonUtfFakeExitCode;
-      }
-    }
-    var commandOutput = createCommandOutput(
-        command,
-        exitCode,
-        timedOut,
-        stdoutData,
-        stderrData,
-        new DateTime.now().difference(startTime),
-        compilationSkipped,
-        pid);
-    commandOutput.diagnostics.addAll(diagnostics);
-    return commandOutput;
-  }
-
-  Map<String, String> _createProcessEnvironment() {
-    var environment = new Map<String, String>.from(io.Platform.environment);
-
-    if (command.environmentOverrides != null) {
-      for (var key in command.environmentOverrides.keys) {
-        environment[key] = command.environmentOverrides[key];
-      }
-    }
-    for (var excludedEnvironmentVariable in _excludedEnvironmentVariables) {
-      environment.remove(excludedEnvironmentVariable);
-    }
-
-    // TODO(terry): Needed for roll 50?
-    environment["GLIBCPP_FORCE_NEW"] = "1";
-    environment["GLIBCXX_FORCE_NEW"] = "1";
-
-    return environment;
-  }
-}
-
-class BatchRunnerProcess {
-  /// When true, the command line is passed to the test runner as a
-  /// JSON-encoded list of strings.
-  final bool _useJson;
-
-  Completer<CommandOutput> _completer;
-  ProcessCommand _command;
-  List<String> _arguments;
-  String _runnerType;
-
-  io.Process _process;
-  Map<String, String> _processEnvironmentOverrides;
-  Completer<Null> _stdoutCompleter;
-  Completer<Null> _stderrCompleter;
-  StreamSubscription<String> _stdoutSubscription;
-  StreamSubscription<String> _stderrSubscription;
-  Function _processExitHandler;
-
-  bool _currentlyRunning = false;
-  OutputLog _testStdout;
-  OutputLog _testStderr;
-  String _status;
-  DateTime _startTime;
-  Timer _timer;
-  int _testCount = 0;
-
-  BatchRunnerProcess({bool useJson: true}) : _useJson = useJson;
-
-  Future<CommandOutput> runCommand(String runnerType, ProcessCommand command,
-      int timeout, List<String> arguments) {
-    assert(_completer == null);
-    assert(!_currentlyRunning);
-
-    _completer = new Completer();
-    bool sameRunnerType = _runnerType == runnerType &&
-        _dictEquals(_processEnvironmentOverrides, command.environmentOverrides);
-    _runnerType = runnerType;
-    _currentlyRunning = true;
-    _command = command;
-    _arguments = arguments;
-    _processEnvironmentOverrides = command.environmentOverrides;
-
-    // TOOD(jmesserly): this restarts `dartdevc --batch` to work around a
-    // memory leak, see https://github.com/dart-lang/sdk/issues/30314.
-    var clearMemoryLeak = command is CompilationCommand &&
-        command.displayName == 'dartdevc' &&
-        ++_testCount % 100 == 0;
-    if (_process == null) {
-      // Start process if not yet started.
-      _startProcess(() {
-        doStartTest(command, timeout);
-      });
-    } else if (!sameRunnerType || clearMemoryLeak) {
-      // Restart this runner with the right executable for this test if needed.
-      _processExitHandler = (_) {
-        _startProcess(() {
-          doStartTest(command, timeout);
-        });
-      };
-      _process.kill();
-      _stdoutSubscription.cancel();
-      _stderrSubscription.cancel();
-    } else {
-      doStartTest(command, timeout);
-    }
-    return _completer.future;
-  }
-
-  Future<bool> terminate() {
-    if (_process == null) return new Future.value(true);
-    var terminateCompleter = new Completer<bool>();
-    _processExitHandler = (_) {
-      terminateCompleter.complete(true);
-    };
-    _process.kill();
-    _stdoutSubscription.cancel();
-    _stderrSubscription.cancel();
-
-    return terminateCompleter.future;
-  }
-
-  void doStartTest(Command command, int timeout) {
-    _startTime = new DateTime.now();
-    _testStdout = new OutputLog();
-    _testStderr = new OutputLog();
-    _status = null;
-    _stdoutCompleter = new Completer();
-    _stderrCompleter = new Completer();
-    _timer = new Timer(new Duration(seconds: timeout), _timeoutHandler);
-
-    var line = _createArgumentsLine(_arguments, timeout);
-    _process.stdin.write(line);
-    _stdoutSubscription.resume();
-    _stderrSubscription.resume();
-    Future.wait([_stdoutCompleter.future, _stderrCompleter.future])
-        .then((_) => _reportResult());
-  }
-
-  String _createArgumentsLine(List<String> arguments, int timeout) {
-    if (_useJson) {
-      return "${jsonEncode(arguments)}\n";
-    } else {
-      return arguments.join(' ') + '\n';
-    }
-  }
-
-  void _reportResult() {
-    if (!_currentlyRunning) return;
-    // _status == '>>> TEST {PASS, FAIL, OK, CRASH, TIMEOUT, PARSE_FAIL}'
-
-    var outcome = _status.split(" ")[2];
-    var exitCode = 0;
-    if (outcome == "CRASH") exitCode = unhandledCompilerExceptionExitCode;
-    if (outcome == "PARSE_FAIL") exitCode = parseFailExitCode;
-    if (outcome == "FAIL" || outcome == "TIMEOUT") exitCode = 1;
-    var output = createCommandOutput(
-        _command,
-        exitCode,
-        (outcome == "TIMEOUT"),
-        _testStdout.toList(),
-        _testStderr.toList(),
-        new DateTime.now().difference(_startTime),
-        false);
-    assert(_completer != null);
-    _completer.complete(output);
-    _completer = null;
-    _currentlyRunning = false;
-  }
-
-  ExitCodeEvent makeExitHandler(String status) {
-    void handler(int exitCode) {
-      if (_currentlyRunning) {
-        if (_timer != null) _timer.cancel();
-        _status = status;
-        _stdoutSubscription.cancel();
-        _stderrSubscription.cancel();
-        _startProcess(_reportResult);
-      } else {
-        // No active test case running.
-        _process = null;
-      }
-    }
-
-    return handler;
-  }
-
-  void _timeoutHandler() {
-    _processExitHandler = makeExitHandler(">>> TEST TIMEOUT");
-    _process.kill();
-  }
-
-  void _startProcess(Action callback) {
-    assert(_command is ProcessCommand);
-    var executable = _command.executable;
-    var arguments = _command.batchArguments.toList();
-    arguments.add('--batch');
-    var environment = new Map<String, String>.from(io.Platform.environment);
-    if (_processEnvironmentOverrides != null) {
-      for (var key in _processEnvironmentOverrides.keys) {
-        environment[key] = _processEnvironmentOverrides[key];
-      }
-    }
-    var processFuture =
-        io.Process.start(executable, arguments, environment: environment);
-    processFuture.then((io.Process p) {
-      _process = p;
-
-      Stream<String> _stdoutStream =
-          _process.stdout.transform(utf8.decoder).transform(new LineSplitter());
-      _stdoutSubscription = _stdoutStream.listen((String line) {
-        if (line.startsWith('>>> TEST')) {
-          _status = line;
-        } else if (line.startsWith('>>> BATCH')) {
-          // ignore
-        } else if (line.startsWith('>>> ')) {
-          throw new Exception("Unexpected command from batch runner: '$line'.");
-        } else {
-          _testStdout.add(encodeUtf8(line));
-          _testStdout.add("\n".codeUnits);
-        }
-        if (_status != null) {
-          _stdoutSubscription.pause();
-          _timer.cancel();
-          _stdoutCompleter.complete(null);
-        }
-      });
-      _stdoutSubscription.pause();
-
-      Stream<String> _stderrStream =
-          _process.stderr.transform(utf8.decoder).transform(new LineSplitter());
-      _stderrSubscription = _stderrStream.listen((String line) {
-        if (line.startsWith('>>> EOF STDERR')) {
-          _stderrSubscription.pause();
-          _stderrCompleter.complete(null);
-        } else {
-          _testStderr.add(encodeUtf8(line));
-          _testStderr.add("\n".codeUnits);
-        }
-      });
-      _stderrSubscription.pause();
-
-      _processExitHandler = makeExitHandler(">>> TEST CRASH");
-      _process.exitCode.then((exitCode) {
-        _processExitHandler(exitCode);
-      });
-
-      _process.stdin.done.catchError((err) {
-        print('Error on batch runner input stream stdin');
-        print('  Previous test\'s status: $_status');
-        print('  Error: $err');
-        throw err;
-      });
-      callback();
-    }).catchError((e) {
-      // TODO(floitsch): should we try to report the stacktrace?
-      print("Process error:");
-      print("  Command: $executable ${arguments.join(' ')} ($_arguments)");
-      print("  Error: $e");
-      // If there is an error starting a batch process, chances are that
-      // it will always fail. So rather than re-trying a 1000+ times, we
-      // exit.
-      io.exit(1);
-      return true;
-    });
-  }
-
-  bool _dictEquals(Map a, Map b) {
-    if (a == null) return b == null;
-    if (b == null) return false;
-    if (a.length != b.length) return false;
-    for (var key in a.keys) {
-      if (a[key] != b[key]) return false;
-    }
-    return true;
-  }
-}
-
-/**
- * [TestCaseEnqueuer] takes a list of TestSuites, generates TestCases and
- * builds a dependency graph of all commands in every TestSuite.
- *
- * It will maintain three helper data structures
- *  - command2node: A mapping from a [Command] to a node in the dependency graph
- *  - command2testCases: A mapping from [Command] to all TestCases that it is
- *    part of.
- *  - remainingTestCases: A set of TestCases that were enqueued but are not
- *    finished
- *
- * [Command] and it's subclasses all have hashCode/operator== methods defined
- * on them, so we can safely use them as keys in Map/Set objects.
- */
-class TestCaseEnqueuer {
-  final Graph<Command> graph;
-  final Function _onTestCaseAdded;
-
-  final command2node = <Command, Node<Command>>{};
-  final command2testCases = <Command, List<TestCase>>{};
-  final remainingTestCases = new Set<TestCase>();
-
-  TestCaseEnqueuer(this.graph, this._onTestCaseAdded);
-
-  void enqueueTestSuites(List<TestSuite> testSuites) {
-    // Cache information about test cases per test suite. For multiple
-    // configurations there is no need to repeatedly search the file
-    // system, generate tests, and search test files for options.
-    var testCache = <String, List<TestInformation>>{};
-
-    var iterator = testSuites.iterator;
-    void enqueueNextSuite() {
-      if (!iterator.moveNext()) {
-        // We're finished with building the dependency graph.
-        graph.seal();
-      } else {
-        iterator.current.forEachTest(_newTest, testCache, enqueueNextSuite);
-      }
-    }
-
-    enqueueNextSuite();
-  }
-
-  /// Adds a test case to the list of active test cases, and adds its commands
-  /// to the dependency graph of commands.
-  ///
-  /// If the repeat flag is > 1, replicates the test case and its commands,
-  /// adding an index field with a distinct value to each of the copies.
-  ///
-  /// Each copy of the test case depends on the previous copy of the test
-  /// case completing, with its first command having a dependency on the last
-  /// command of the previous copy of the test case. This dependency is
-  /// marked as a "timingDependency", so that it doesn't depend on the previous
-  /// test completing successfully, just on it completing.
-  void _newTest(TestCase testCase) {
-    Node<Command> lastNode;
-    for (int i = 0; i < testCase.configuration.repeat; ++i) {
-      if (i > 0) {
-        testCase = testCase.indexedCopy(i);
-      }
-      remainingTestCases.add(testCase);
-      bool isFirstCommand = true;
-      for (var command in testCase.commands) {
-        // Make exactly *one* node in the dependency graph for every command.
-        // This ensures that we never have two commands c1 and c2 in the graph
-        // with "c1 == c2".
-        var node = command2node[command];
-        if (node == null) {
-          var requiredNodes =
-              (lastNode != null) ? [lastNode] : <Node<Command>>[];
-          node = graph.add(command, requiredNodes,
-              timingDependency: isFirstCommand);
-          command2node[command] = node;
-          command2testCases[command] = <TestCase>[];
-        }
-        // Keep mapping from command to all testCases that refer to it
-        command2testCases[command].add(testCase);
-
-        lastNode = node;
-        isFirstCommand = false;
-      }
-      _onTestCaseAdded(testCase);
-    }
-  }
-}
-
-/*
- * [CommandEnqueuer] will
- *  - change node.state to NodeState.Enqueuing as soon as all dependencies have
- *    a state of NodeState.Successful
- *  - change node.state to NodeState.UnableToRun if one or more dependencies
- *    have a state of NodeState.Failed/NodeState.UnableToRun.
- */
-class CommandEnqueuer {
-  static const _initStates = const [NodeState.initialized, NodeState.waiting];
-
-  static const _finishedStates = const [
-    NodeState.successful,
-    NodeState.failed,
-    NodeState.unableToRun
-  ];
-
-  final Graph<Command> _graph;
-
-  CommandEnqueuer(this._graph) {
-    _graph.added.listen(_changeNodeStateIfNecessary);
-
-    _graph.changed.listen((event) {
-      if (event.from == NodeState.waiting ||
-          event.from == NodeState.processing) {
-        if (_finishedStates.contains(event.to)) {
-          for (var dependentNode in event.node.neededFor) {
-            _changeNodeStateIfNecessary(dependentNode);
-          }
-        }
-      }
-    });
-  }
-
-  // Called when either a new node was added or if one of it's dependencies
-  // changed it's state.
-  void _changeNodeStateIfNecessary(Node<Command> node) {
-    if (_initStates.contains(node.state)) {
-      bool allDependenciesFinished =
-          node.dependencies.every((dep) => _finishedStates.contains(dep.state));
-      bool anyDependenciesUnsuccessful = node.dependencies.any((dep) =>
-          [NodeState.failed, NodeState.unableToRun].contains(dep.state));
-      bool allDependenciesSuccessful =
-          node.dependencies.every((dep) => dep.state == NodeState.successful);
-
-      var newState = NodeState.waiting;
-      if (allDependenciesSuccessful ||
-          (allDependenciesFinished && node.timingDependency)) {
-        newState = NodeState.enqueuing;
-      } else if (anyDependenciesUnsuccessful) {
-        newState = NodeState.unableToRun;
-      }
-      if (node.state != newState) {
-        _graph.changeState(node, newState);
-      }
-    }
-  }
-}
-
-/*
- * [CommandQueue] will listen for nodes entering the NodeState.ENQUEUING state,
- * queue them up and run them. While nodes are processed they will be in the
- * NodeState.PROCESSING state. After running a command, the node will change
- * to a state of NodeState.Successful or NodeState.Failed.
- *
- * It provides a synchronous stream [completedCommands] which provides the
- * [CommandOutputs] for the finished commands.
- *
- * It provides a [done] future, which will complete once there are no more
- * nodes left in the states Initialized/Waiting/Enqueing/Processing
- * and the [executor] has cleaned up it's resources.
- */
-class CommandQueue {
-  final Graph<Command> graph;
-  final CommandExecutor executor;
-  final TestCaseEnqueuer enqueuer;
-
-  final Queue<Command> _runQueue = new Queue<Command>();
-  final _commandOutputStream = new StreamController<CommandOutput>(sync: true);
-  final _completer = new Completer<Null>();
-
-  int _numProcesses = 0;
-  int _maxProcesses;
-  int _numBrowserProcesses = 0;
-  int _maxBrowserProcesses;
-  bool _finishing = false;
-  bool _verbose = false;
-
-  CommandQueue(this.graph, this.enqueuer, this.executor, this._maxProcesses,
-      this._maxBrowserProcesses, this._verbose) {
-    graph.changed.listen((event) {
-      if (event.to == NodeState.enqueuing) {
-        assert(event.from == NodeState.initialized ||
-            event.from == NodeState.waiting);
-        graph.changeState(event.node, NodeState.processing);
-        var command = event.node.data;
-        if (event.node.dependencies.isNotEmpty) {
-          _runQueue.addFirst(command);
-        } else {
-          _runQueue.add(command);
-        }
-        Timer.run(() => _tryRunNextCommand());
-      } else if (event.to == NodeState.unableToRun) {
-        _checkDone();
-      }
-    });
-
-    // We're finished if the graph is sealed and all nodes are in a finished
-    // state (Successful, Failed or UnableToRun).
-    // So we're calling '_checkDone()' to check whether that condition is met
-    // and we can cleanup.
-    graph.sealed.listen((event) {
-      _checkDone();
-    });
-  }
-
-  Stream<CommandOutput> get completedCommands => _commandOutputStream.stream;
-
-  Future get done => _completer.future;
-
-  void _tryRunNextCommand() {
-    _checkDone();
-
-    if (_numProcesses < _maxProcesses && !_runQueue.isEmpty) {
-      Command command = _runQueue.removeFirst();
-      var isBrowserCommand = command is BrowserTestCommand;
-
-      if (isBrowserCommand && _numBrowserProcesses == _maxBrowserProcesses) {
-        // If there is no free browser runner, put it back into the queue.
-        _runQueue.add(command);
-        // Don't lose a process.
-        new Timer(new Duration(milliseconds: 100), _tryRunNextCommand);
-        return;
-      }
-
-      _numProcesses++;
-      if (isBrowserCommand) _numBrowserProcesses++;
-
-      var node = enqueuer.command2node[command];
-      Iterable<TestCase> testCases = enqueuer.command2testCases[command];
-      // If a command is part of many TestCases we set the timeout to be
-      // the maximum over all [TestCase.timeout]s. At some point, we might
-      // eliminate [TestCase.timeout] completely and move it to [Command].
-      int timeout = testCases
-          .map((TestCase test) => test.timeout)
-          .fold(0, (int a, b) => math.max(a, b));
-
-      if (_verbose) {
-        print('Running "${command.displayName}" command: $command');
-      }
-
-      executor.runCommand(node, command, timeout).then((CommandOutput output) {
-        assert(command == output.command);
-
-        _commandOutputStream.add(output);
-        if (output.canRunDependendCommands) {
-          graph.changeState(node, NodeState.successful);
-        } else {
-          graph.changeState(node, NodeState.failed);
-        }
-
-        _numProcesses--;
-        if (isBrowserCommand) _numBrowserProcesses--;
-
-        // Don't lose a process
-        Timer.run(() => _tryRunNextCommand());
-      });
-    }
-  }
-
-  void _checkDone() {
-    if (!_finishing &&
-        _runQueue.isEmpty &&
-        _numProcesses == 0 &&
-        graph.isSealed &&
-        graph.stateCount(NodeState.initialized) == 0 &&
-        graph.stateCount(NodeState.waiting) == 0 &&
-        graph.stateCount(NodeState.enqueuing) == 0 &&
-        graph.stateCount(NodeState.processing) == 0) {
-      _finishing = true;
-      executor.cleanup().then((_) {
-        _completer.complete();
-        _commandOutputStream.close();
-      });
-    }
-  }
-
-  void dumpState() {
-    print("");
-    print("CommandQueue state:");
-    print("  Processes: used: $_numProcesses max: $_maxProcesses");
-    print("  BrowserProcesses: used: $_numBrowserProcesses "
-        "max: $_maxBrowserProcesses");
-    print("  Finishing: $_finishing");
-    print("  Queue (length = ${_runQueue.length}):");
-    for (var command in _runQueue) {
-      print("      $command");
-    }
-  }
-}
-
-/*
- * [CommandExecutor] is responsible for executing commands. It will make sure
- * that the following two constraints are satisfied
- *  - [:numberOfProcessesUsed <= maxProcesses:]
- *  - [:numberOfBrowserProcessesUsed <= maxBrowserProcesses:]
- *
- * It provides a [runCommand] method which will complete with a
- * [CommandOutput] object.
- *
- * It provides a [cleanup] method to free all the allocated resources.
- */
-abstract class CommandExecutor {
-  Future cleanup();
-  // TODO(kustermann): The [timeout] parameter should be a property of Command
-  Future<CommandOutput> runCommand(
-      Node<Command> node, covariant Command command, int timeout);
-}
-
-class CommandExecutorImpl implements CommandExecutor {
-  final TestConfiguration globalConfiguration;
-  final int maxProcesses;
-  final int maxBrowserProcesses;
-  AdbDevicePool adbDevicePool;
-
-  // For dart2js and analyzer batch processing,
-  // we keep a list of batch processes.
-  final _batchProcesses = new Map<String, List<BatchRunnerProcess>>();
-  // We keep a BrowserTestRunner for every configuration.
-  final _browserTestRunners = new Map<TestConfiguration, BrowserTestRunner>();
-
-  bool _finishing = false;
-
-  CommandExecutorImpl(
-      this.globalConfiguration, this.maxProcesses, this.maxBrowserProcesses,
-      {this.adbDevicePool});
-
-  Future cleanup() {
-    assert(!_finishing);
-    _finishing = true;
-
-    Future _terminateBatchRunners() {
-      var futures = <Future>[];
-      for (var runners in _batchProcesses.values) {
-        futures.addAll(runners.map((runner) => runner.terminate()));
-      }
-      return Future.wait(futures);
-    }
-
-    Future _terminateBrowserRunners() {
-      var futures =
-          _browserTestRunners.values.map((runner) => runner.terminate());
-      return Future.wait(futures);
-    }
-
-    return Future.wait([
-      _terminateBatchRunners(),
-      _terminateBrowserRunners(),
-    ]);
-  }
-
-  Future<CommandOutput> runCommand(node, Command command, int timeout) {
-    assert(!_finishing);
-
-    Future<CommandOutput> runCommand(int retriesLeft) {
-      return _runCommand(command, timeout).then((CommandOutput output) {
-        if (retriesLeft > 0 && shouldRetryCommand(output)) {
-          DebugLogger.warning("Rerunning Command: ($retriesLeft "
-              "attempt(s) remains) [cmd: $command]");
-          return runCommand(retriesLeft - 1);
-        } else {
-          return new Future.value(output);
-        }
-      });
-    }
-
-    return runCommand(command.maxNumRetries);
-  }
-
-  Future<CommandOutput> _runCommand(Command command, int timeout) {
-    if (command is BrowserTestCommand) {
-      return _startBrowserControllerTest(command, timeout);
-    } else if (command is VMKernelCompilationCommand) {
-      // For now, we always run vm_compile_to_kernel in batch mode.
-      var name = command.displayName;
-      assert(name == 'vm_compile_to_kernel');
-      return _getBatchRunner(name)
-          .runCommand(name, command, timeout, command.arguments);
-    } else if (command is CompilationCommand &&
-        globalConfiguration.batchDart2JS &&
-        command.displayName == 'dart2js') {
-      return _getBatchRunner("dart2js")
-          .runCommand("dart2js", command, timeout, command.arguments);
-    } else if (command is AnalysisCommand && globalConfiguration.batch) {
-      return _getBatchRunner(command.displayName)
-          .runCommand(command.displayName, command, timeout, command.arguments);
-    } else if (command is CompilationCommand &&
-        (command.displayName == 'dartdevc' ||
-            command.displayName == 'dartdevk' ||
-            command.displayName == 'fasta') &&
-        globalConfiguration.batch) {
-      return _getBatchRunner(command.displayName)
-          .runCommand(command.displayName, command, timeout, command.arguments);
-    } else if (command is ScriptCommand) {
-      return command.run();
-    } else if (command is AdbPrecompilationCommand ||
-        command is AdbDartkCommand) {
-      assert(adbDevicePool != null);
-      return adbDevicePool.acquireDevice().then((AdbDevice device) async {
-        try {
-          if (command is AdbPrecompilationCommand) {
-            return await _runAdbPrecompilationCommand(device, command, timeout);
-          } else {
-            return await _runAdbDartkCommand(
-                device, command as AdbDartkCommand, timeout);
-          }
-        } finally {
-          await adbDevicePool.releaseDevice(device);
-        }
-      });
-    } else if (command is VmBatchCommand) {
-      var name = command.displayName;
-      return _getBatchRunner(command.displayName + command.dartFile)
-          .runCommand(name, command, timeout, command.arguments);
-    } else if (command is CompilationCommand &&
-        command.displayName == 'babel') {
-      return new RunningProcess(command, timeout,
-              configuration: globalConfiguration,
-              outputFile: io.File(command.outputFile))
-          .run();
-    } else if (command is ProcessCommand) {
-      return new RunningProcess(command, timeout,
-              configuration: globalConfiguration)
-          .run();
-    } else {
-      throw new ArgumentError("Unknown command type ${command.runtimeType}.");
-    }
-  }
-
-  Future<CommandOutput> _runAdbPrecompilationCommand(
-      AdbDevice device, AdbPrecompilationCommand command, int timeout) async {
-    var runner = command.precompiledRunnerFilename;
-    var processTest = command.processTestFilename;
-    var testdir = command.precompiledTestDirectory;
-    var arguments = command.arguments;
-    var devicedir = DartPrecompiledAdbRuntimeConfiguration.DeviceDir;
-    var deviceTestDir = DartPrecompiledAdbRuntimeConfiguration.DeviceTestDir;
-
-    // We copy all the files which the vm precompiler puts into the test
-    // directory.
-    List<String> files = new io.Directory(testdir)
-        .listSync()
-        .map((file) => file.path)
-        .map((path) => path.substring(path.lastIndexOf('/') + 1))
-        .toList();
-
-    var timeoutDuration = new Duration(seconds: timeout);
-
-    var steps = <StepFunction>[];
-
-    steps.add(() => device.runAdbShellCommand(['rm', '-Rf', deviceTestDir]));
-    steps.add(() => device.runAdbShellCommand(['mkdir', '-p', deviceTestDir]));
-    steps.add(() =>
-        device.pushCachedData(runner, '$devicedir/dart_precompiled_runtime'));
-    steps.add(
-        () => device.pushCachedData(processTest, '$devicedir/process_test'));
-    steps.add(() => device.runAdbShellCommand([
-          'chmod',
-          '777',
-          '$devicedir/dart_precompiled_runtime $devicedir/process_test'
-        ]));
-
-    for (var file in files) {
-      steps.add(() => device
-          .runAdbCommand(['push', '$testdir/$file', '$deviceTestDir/$file']));
-    }
-
-    steps.add(() => device.runAdbShellCommand(
-        [
-          '$devicedir/dart_precompiled_runtime',
-        ]..addAll(arguments),
-        timeout: timeoutDuration));
-
-    var stopwatch = new Stopwatch()..start();
-    var writer = new StringBuffer();
-
-    await device.waitForBootCompleted();
-    await device.waitForDevice();
-
-    AdbCommandResult result;
-    for (var i = 0; i < steps.length; i++) {
-      var fun = steps[i];
-      var commandStopwatch = new Stopwatch()..start();
-      result = await fun();
-
-      writer.writeln("Executing ${result.command}");
-      if (result.stdout.length > 0) {
-        writer.writeln("Stdout:\n${result.stdout.trim()}");
-      }
-      if (result.stderr.length > 0) {
-        writer.writeln("Stderr:\n${result.stderr.trim()}");
-      }
-      writer.writeln("ExitCode: ${result.exitCode}");
-      writer.writeln("Time: ${commandStopwatch.elapsed}");
-      writer.writeln("");
-
-      // If one command fails, we stop processing the others and return
-      // immediately.
-      if (result.exitCode != 0) break;
-    }
-    return createCommandOutput(command, result.exitCode, result.timedOut,
-        utf8.encode('$writer'), [], stopwatch.elapsed, false);
-  }
-
-  Future<CommandOutput> _runAdbDartkCommand(
-      AdbDevice device, AdbDartkCommand command, int timeout) async {
-    final String buildPath = command.buildPath;
-    final String processTest = command.processTestFilename;
-    final String hostKernelFile = command.kernelFile;
-    final List<String> arguments = command.arguments;
-    final String devicedir = DartkAdbRuntimeConfiguration.DeviceDir;
-    final String deviceTestDir = DartkAdbRuntimeConfiguration.DeviceTestDir;
-
-    final timeoutDuration = new Duration(seconds: timeout);
-
-    final steps = <StepFunction>[];
-
-    steps.add(() => device.runAdbShellCommand(['rm', '-Rf', deviceTestDir]));
-    steps.add(() => device.runAdbShellCommand(['mkdir', '-p', deviceTestDir]));
-    steps.add(
-        () => device.pushCachedData("${buildPath}/dart", '$devicedir/dart'));
-    steps.add(() => device
-        .runAdbCommand(['push', hostKernelFile, '$deviceTestDir/out.dill']));
-
-    for (final String lib in command.extraLibraries) {
-      final String libname = "lib${lib}.so";
-      steps.add(() => device.runAdbCommand(
-          ['push', '${buildPath}/$libname', '$deviceTestDir/$libname']));
-    }
-
-    steps.add(() => device.runAdbShellCommand(
-        [
-          'export LD_LIBRARY_PATH=\$LD_LIBRARY_PATH:$deviceTestDir;'
-              '$devicedir/dart',
-        ]..addAll(arguments),
-        timeout: timeoutDuration));
-
-    final stopwatch = new Stopwatch()..start();
-    final writer = new StringBuffer();
-
-    await device.waitForBootCompleted();
-    await device.waitForDevice();
-
-    AdbCommandResult result;
-    for (var i = 0; i < steps.length; i++) {
-      var step = steps[i];
-      var commandStopwatch = new Stopwatch()..start();
-      result = await step();
-
-      writer.writeln("Executing ${result.command}");
-      if (result.stdout.length > 0) {
-        writer.writeln("Stdout:\n${result.stdout.trim()}");
-      }
-      if (result.stderr.length > 0) {
-        writer.writeln("Stderr:\n${result.stderr.trim()}");
-      }
-      writer.writeln("ExitCode: ${result.exitCode}");
-      writer.writeln("Time: ${commandStopwatch.elapsed}");
-      writer.writeln("");
-
-      // If one command fails, we stop processing the others and return
-      // immediately.
-      if (result.exitCode != 0) break;
-    }
-    return createCommandOutput(command, result.exitCode, result.timedOut,
-        utf8.encode('$writer'), [], stopwatch.elapsed, false);
-  }
-
-  BatchRunnerProcess _getBatchRunner(String identifier) {
-    // Start batch processes if needed
-    var runners = _batchProcesses[identifier];
-    if (runners == null) {
-      runners = new List<BatchRunnerProcess>(maxProcesses);
-      for (int i = 0; i < maxProcesses; i++) {
-        runners[i] = new BatchRunnerProcess(useJson: identifier == "fasta");
-      }
-      _batchProcesses[identifier] = runners;
-    }
-
-    for (var runner in runners) {
-      if (!runner._currentlyRunning) return runner;
-    }
-    throw new Exception('Unable to find inactive batch runner.');
-  }
-
-  Future<CommandOutput> _startBrowserControllerTest(
-      BrowserTestCommand browserCommand, int timeout) {
-    var completer = new Completer<CommandOutput>();
-
-    var callback = (BrowserTestOutput output) {
-      completer.complete(new BrowserCommandOutput(browserCommand, output));
-    };
-
-    var browserTest = new BrowserTest(browserCommand.url, callback, timeout);
-    _getBrowserTestRunner(browserCommand.configuration).then((testRunner) {
-      testRunner.enqueueTest(browserTest);
-    });
-
-    return completer.future;
-  }
-
-  Future<BrowserTestRunner> _getBrowserTestRunner(
-      TestConfiguration configuration) async {
-    if (_browserTestRunners[configuration] == null) {
-      var testRunner = new BrowserTestRunner(
-          configuration, globalConfiguration.localIP, maxBrowserProcesses);
-      if (globalConfiguration.isVerbose) {
-        testRunner.logger = DebugLogger.info;
-      }
-      _browserTestRunners[configuration] = testRunner;
-      await testRunner.start();
-    }
-    return _browserTestRunners[configuration];
-  }
-}
-
-bool shouldRetryCommand(CommandOutput output) {
-  if (!output.successful) {
-    List<String> stdout, stderr;
-
-    decodeOutput() {
-      if (stdout == null && stderr == null) {
-        stdout = decodeUtf8(output.stderr).split("\n");
-        stderr = decodeUtf8(output.stderr).split("\n");
-      }
-    }
-
-    final command = output.command;
-
-    // The dartk batch compiler sometimes runs out of memory. In such a case we
-    // will retry running it.
-    if (command is VMKernelCompilationCommand) {
-      if (output.hasCrashed) {
-        bool containsOutOfMemoryMessage(String line) {
-          return line.contains('Exhausted heap space, trying to allocat');
-        }
-
-        decodeOutput();
-        if (stdout.any(containsOutOfMemoryMessage) ||
-            stderr.any(containsOutOfMemoryMessage)) {
-          return true;
-        }
-      }
-    }
-
-    if (io.Platform.operatingSystem == 'linux') {
-      decodeOutput();
-      // No matter which command we ran: If we get failures due to the
-      // "xvfb-run" issue 7564, try re-running the test.
-      bool containsFailureMsg(String line) {
-        return line.contains(cannotOpenDisplayMessage) ||
-            line.contains(failedToRunCommandMessage);
-      }
-
-      if (stdout.any(containsFailureMsg) || stderr.any(containsFailureMsg)) {
-        return true;
-      }
-    }
-  }
-  return false;
-}
-
-/*
- * [TestCaseCompleter] will listen for
- * NodeState.Processing -> NodeState.{Successful,Failed} state changes and
- * will complete a TestCase if it is finished.
- *
- * It provides a stream [finishedTestCases], which will stream all TestCases
- * once they're finished. After all TestCases are done, the stream will be
- * closed.
- */
-class TestCaseCompleter {
-  static const _completedStates = const [
-    NodeState.failed,
-    NodeState.successful
-  ];
-
-  final Graph<Command> _graph;
-  final TestCaseEnqueuer _enqueuer;
-  final CommandQueue _commandQueue;
-
-  final Map<Command, CommandOutput> _outputs = {};
-  final StreamController<TestCase> _controller = new StreamController();
-  bool _closed = false;
-
-  TestCaseCompleter(this._graph, this._enqueuer, this._commandQueue) {
-    var finishedRemainingTestCases = false;
-
-    // Store all the command outputs -- they will be delivered synchronously
-    // (i.e. before state changes in the graph)
-    _commandQueue.completedCommands.listen((CommandOutput output) {
-      _outputs[output.command] = output;
-    }, onDone: () {
-      _completeTestCasesIfPossible(new List.from(_enqueuer.remainingTestCases));
-      finishedRemainingTestCases = true;
-      assert(_enqueuer.remainingTestCases.isEmpty);
-      _checkDone();
-    });
-
-    // Listen for NodeState.Processing -> NodeState.{Successful,Failed}
-    // changes.
-    _graph.changed.listen((event) {
-      if (event.from == NodeState.processing && !finishedRemainingTestCases) {
-        var command = event.node.data;
-
-        assert(_completedStates.contains(event.to));
-        assert(_outputs[command] != null);
-
-        _completeTestCasesIfPossible(_enqueuer.command2testCases[command]);
-        _checkDone();
-      }
-    });
-
-    // Listen also for GraphSealedEvents. If there is not a single node in the
-    // graph, we still want to finish after the graph was sealed.
-    _graph.sealed.listen((_) {
-      if (!_closed && _enqueuer.remainingTestCases.isEmpty) {
-        _controller.close();
-        _closed = true;
-      }
-    });
-  }
-
-  Stream<TestCase> get finishedTestCases => _controller.stream;
-
-  void _checkDone() {
-    if (!_closed && _graph.isSealed && _enqueuer.remainingTestCases.isEmpty) {
-      _controller.close();
-      _closed = true;
-    }
-  }
-
-  void _completeTestCasesIfPossible(Iterable<TestCase> testCases) {
-    // Update TestCases with command outputs
-    for (TestCase test in testCases) {
-      for (var icommand in test.commands) {
-        var output = _outputs[icommand];
-        if (output != null) {
-          test.commandOutputs[icommand] = output;
-        }
-      }
-    }
-
-    void completeTestCase(TestCase testCase) {
-      if (_enqueuer.remainingTestCases.contains(testCase)) {
-        _controller.add(testCase);
-        _enqueuer.remainingTestCases.remove(testCase);
-      } else {
-        DebugLogger.error("${testCase.displayName} would be finished twice");
-      }
-    }
-
-    for (var testCase in testCases) {
-      // Ask the [testCase] if it's done. Note that we assume, that
-      // [TestCase.isFinished] will return true if all commands were executed
-      // or if a previous one failed.
-      if (testCase.isFinished) {
-        completeTestCase(testCase);
-      }
-    }
-  }
-}
-
-class ProcessQueue {
-  TestConfiguration _globalConfiguration;
-
-  Function _allDone;
-  final Graph<Command> _graph = new Graph();
-  List<EventListener> _eventListener;
-
-  ProcessQueue(
-      this._globalConfiguration,
-      int maxProcesses,
-      int maxBrowserProcesses,
-      DateTime startTime,
-      List<TestSuite> testSuites,
-      this._eventListener,
-      this._allDone,
-      [bool verbose = false,
-      AdbDevicePool adbDevicePool]) {
-    void setupForListing(TestCaseEnqueuer testCaseEnqueuer) {
-      _graph.sealed.listen((_) {
-        var testCases = testCaseEnqueuer.remainingTestCases.toList();
-        testCases.sort((a, b) => a.displayName.compareTo(b.displayName));
-
-        print("\nGenerating all matching test cases ....\n");
-
-        for (TestCase testCase in testCases) {
-          eventFinishedTestCase(testCase);
-          var outcomes = testCase.expectedOutcomes.map((o) => '$o').toList()
-            ..sort();
-          print("${testCase.displayName}   "
-              "Expectations: ${outcomes.join(', ')}   "
-              "Configuration: '${testCase.configurationString}'");
-        }
-        eventAllTestsKnown();
-      });
-    }
-
-    TestCaseEnqueuer testCaseEnqueuer;
-    CommandQueue commandQueue;
-
-    void setupForRunning(TestCaseEnqueuer testCaseEnqueuer) {
-      Timer _debugTimer;
-      // If we haven't seen a single test finishing during a 10 minute period
-      // something is definitely wrong, so we dump the debugging information.
-      final debugTimerDuration = const Duration(minutes: 10);
-
-      void cancelDebugTimer() {
-        if (_debugTimer != null) {
-          _debugTimer.cancel();
-        }
-      }
-
-      void resetDebugTimer() {
-        cancelDebugTimer();
-        _debugTimer = new Timer(debugTimerDuration, () {
-          print("The debug timer of test.dart expired. Please report this issue"
-              " to whesse@ and provide the following information:");
-          print("");
-          print("Graph is sealed: ${_graph.isSealed}");
-          print("");
-          _graph.dumpCounts();
-          print("");
-          var unfinishedNodeStates = [
-            NodeState.initialized,
-            NodeState.waiting,
-            NodeState.enqueuing,
-            NodeState.processing
-          ];
-
-          for (var nodeState in unfinishedNodeStates) {
-            if (_graph.stateCount(nodeState) > 0) {
-              print("Commands in state '$nodeState':");
-              print("=================================");
-              print("");
-              for (var node in _graph.nodes) {
-                if (node.state == nodeState) {
-                  var command = node.data;
-                  var testCases = testCaseEnqueuer.command2testCases[command];
-                  print("  Command: $command");
-                  for (var testCase in testCases) {
-                    print("    Enqueued by: ${testCase.configurationString} "
-                        "-- ${testCase.displayName}");
-                  }
-                  print("");
-                }
-              }
-              print("");
-              print("");
-            }
-          }
-
-          if (commandQueue != null) {
-            commandQueue.dumpState();
-          }
-        });
-      }
-
-      // When the graph building is finished, notify event listeners.
-      _graph.sealed.listen((_) {
-        eventAllTestsKnown();
-      });
-
-      // Queue commands as they become "runnable"
-      new CommandEnqueuer(_graph);
-
-      // CommandExecutor will execute commands
-      var executor = new CommandExecutorImpl(
-          _globalConfiguration, maxProcesses, maxBrowserProcesses,
-          adbDevicePool: adbDevicePool);
-
-      // Run "runnable commands" using [executor] subject to
-      // maxProcesses/maxBrowserProcesses constraint
-      commandQueue = new CommandQueue(_graph, testCaseEnqueuer, executor,
-          maxProcesses, maxBrowserProcesses, verbose);
-
-      // Finish test cases when all commands were run (or some failed)
-      var testCaseCompleter =
-          new TestCaseCompleter(_graph, testCaseEnqueuer, commandQueue);
-      testCaseCompleter.finishedTestCases.listen((TestCase finishedTestCase) {
-        resetDebugTimer();
-
-        eventFinishedTestCase(finishedTestCase);
-      }, onDone: () {
-        // Wait until the commandQueue/exectutor is done (it may need to stop
-        // batch runners, browser controllers, ....)
-        commandQueue.done.then((_) {
-          cancelDebugTimer();
-          eventAllTestsDone();
-        });
-      });
-
-      resetDebugTimer();
-    }
-
-    // Build up the dependency graph
-    testCaseEnqueuer = new TestCaseEnqueuer(_graph, (TestCase newTestCase) {
-      eventTestAdded(newTestCase);
-    });
-
-    // Either list or run the tests
-    if (_globalConfiguration.listTests) {
-      setupForListing(testCaseEnqueuer);
-    } else {
-      setupForRunning(testCaseEnqueuer);
-    }
-
-    // Start enqueing all TestCases
-    testCaseEnqueuer.enqueueTestSuites(testSuites);
-  }
-
-  void eventFinishedTestCase(TestCase testCase) {
-    for (var listener in _eventListener) {
-      listener.done(testCase);
-    }
-  }
-
-  void eventTestAdded(TestCase testCase) {
-    for (var listener in _eventListener) {
-      listener.testAdded();
-    }
-  }
-
-  void eventAllTestsKnown() {
-    for (var listener in _eventListener) {
-      listener.allTestsKnown();
-    }
-  }
-
-  void eventAllTestsDone() {
-    for (var listener in _eventListener) {
-      listener.allDone();
-    }
-    _allDone();
-  }
-}
diff --git a/tools/testing/extensions/chrome/ConsoleCollector.crx b/tools/testing/extensions/chrome/ConsoleCollector.crx
deleted file mode 100644
index f853917..0000000
--- a/tools/testing/extensions/chrome/ConsoleCollector.crx
+++ /dev/null
Binary files differ
diff --git a/tools/testing/extensions/chrome/ConsoleCollector/background.js b/tools/testing/extensions/chrome/ConsoleCollector/background.js
deleted file mode 100644
index 21ae577..0000000
--- a/tools/testing/extensions/chrome/ConsoleCollector/background.js
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright (c) 2012, 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.
-
-/**
- * This is the background window. It can access the necessary APIs to get
- * at the console messages. It can only communicate with the content
- * window through message passing.
- *
- * There is no way to query the console messages, as such, but we can
- * set up a handler that is called when there are console messages. This 
- * will be called with any console messages already present, so it can be set
- * up after the fact. However, if there are no messages it won't be called.
- * To handle the end of the messages (or no messages) we have to use a 
- * sentinel message that is logged by the content page.
- */
-var version = "1.0";
-var messages = []; // An array that we can put messages in.
-var debuggeeId; // An object that identifies the browser tab we are talking to.
-var callback; // For passing back the response to the content window.
-var timer; // To time out if no messages are available.
-
-/**
- * When we have collected all the messages, we send them back to the
- * content page via the callback, turn off message collection, and
- * detach the debugger from the browser tab.
- */
-function allDone() {
-  callback(messages); 
-  chrome.debugger.sendCommand(debuggeeId, "Console.clearMessages", {},
-      function() {
-        chrome.debugger.sendCommand(debuggeeId, "Console.disable", {},
-            function() {});
-        chrome.debugger.detach(debuggeeId, function() {});
-  });
-  messages = [];
-}
-
-/**
- * Debugger event handler. We only care about console.messageAdded
- * events, in which case we add a new message object with the fields
- * we care about to our messages array.
- */
-function onEvent(debuggeeId, method, params) {
-  var tabId = debuggeeId.tabId;
-  if (method == "Console.messageAdded") {
-    var msg = params.message;
-    // More fields are available if we want them later. See
-    // https://developers.google.com/chrome-developer-tools/docs/protocol/1.0/console#type-ConsoleMessage
-    if (msg.text == 'getMessages/end') {
-      allDone();
-    } else {
-      messages.push({"source":msg.url, "line": msg.line,
-          "category":msg.source, "message":msg.text });
-    }
-  }
-}
-
-/**
- * Handle requests sent by the content script. We save the callback,
- * get the window and tab that is currently active, attach the 
- * debugger to that tab, and then turn on console message event 
- * handling, which will result in onEvent calls for each console 
- * message, including the ones that are already present in the console.
- */
-function onRequest(request, sender, sendResponse) {
-  if (request.command == "getMessages") {
-    callback = sendResponse;
-    chrome.windows.getCurrent(function(win) {
-      chrome.tabs.getSelected(win.id, function(tab) {
-        debuggeeId = {tabId:tab.id};
-        chrome.debugger.attach(debuggeeId, version, function() {
-          if (chrome.extension.lastError) {
-            // Attach failed; send an empty response.
-            callback([]);
-          } else {
-            chrome.debugger.sendCommand(debuggeeId, "Console.enable", {},
-              function() {});
-            //timer = setTimeout(allDone, 1000);
-          }
-        });
-      });
-    });
-  }
-}
-
-// Set up the general handler for debug events.
-chrome.debugger.onEvent.addListener(onEvent);
-// Listen for the content script to send a message to the background page.
-chrome.extension.onRequest.addListener(onRequest);
diff --git a/tools/testing/extensions/chrome/ConsoleCollector/content.js b/tools/testing/extensions/chrome/ConsoleCollector/content.js
deleted file mode 100644
index 6339f81..0000000
--- a/tools/testing/extensions/chrome/ConsoleCollector/content.js
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright (c) 2012, 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.
-
-/**
- * This is the content page script. This runs in the context of the browser
- * page, and communicates with the background page by relaying a getMessages
- * request, and the forwarding the messages back to the browser page as a
- * gotMessages message.
- */
-window.addEventListener("message", function(event) {
-  if (event.source == window && event.data == "getMessages") {
-    // Log a special sentinel message to mark the end of the messages.
-    console.log('getMessages/end');
-    chrome.extension.sendRequest({command: "getMessages"}, function(messages) {
-      window.postMessage({ "type": "gotMessages", "messages" : messages}, "*");
-    });
-  }
-}, false);
diff --git a/tools/testing/extensions/chrome/ConsoleCollector/manifest.json b/tools/testing/extensions/chrome/ConsoleCollector/manifest.json
deleted file mode 100644
index 6f6b439..0000000
--- a/tools/testing/extensions/chrome/ConsoleCollector/manifest.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
-  "name": "Console Collector",
-  "version": "1.0",
-  "manifest_version": 2,
-  "description": "Allow querying of the Javascript console.",
-  "browser_action": {
-    "name": "ConsoleCollector"
-  },
-  "background": {
-    "scripts": ["background.js"],
-    "persistent": true
-  },
-  "content_scripts": [
-    {
-      "matches": ["http://*/*", "file://*" ],
-      "js": [ "content.js" ]
-    }
-  ],
-  "permissions": [
-    "tabs", "http://*/*", "file://*", "debugger"
-  ]
-}
diff --git a/tools/testing/extensions/firefox/ConsoleCollector.xpi b/tools/testing/extensions/firefox/ConsoleCollector.xpi
deleted file mode 100644
index 40c261e..0000000
--- a/tools/testing/extensions/firefox/ConsoleCollector.xpi
+++ /dev/null
Binary files differ
diff --git a/tools/testing/extensions/firefox/ConsoleCollector/Makefile b/tools/testing/extensions/firefox/ConsoleCollector/Makefile
deleted file mode 100644
index f6c8610..0000000
--- a/tools/testing/extensions/firefox/ConsoleCollector/Makefile
+++ /dev/null
@@ -1,2 +0,0 @@
-../ConsoleCollector.xpi: chrome.manifest install.rdf  chrome/content/console.js chrome/content/overlay.xul
-	zip -r ../ConsoleCollector.xpi chrome.manifest install.rdf  chrome/content/console.js chrome/content/overlay.xul
diff --git a/tools/testing/extensions/firefox/ConsoleCollector/chrome.manifest b/tools/testing/extensions/firefox/ConsoleCollector/chrome.manifest
deleted file mode 100644
index 5425ba6..0000000
--- a/tools/testing/extensions/firefox/ConsoleCollector/chrome.manifest
+++ /dev/null
@@ -1,2 +0,0 @@
-content   ConsoleCollector                chrome/content/

-overlay   chrome://browser/content/browser.xul   chrome://ConsoleCollector/content/overlay.xul

diff --git a/tools/testing/extensions/firefox/ConsoleCollector/chrome/content/console.js b/tools/testing/extensions/firefox/ConsoleCollector/chrome/content/console.js
deleted file mode 100644
index af46de4..0000000
--- a/tools/testing/extensions/firefox/ConsoleCollector/chrome/content/console.js
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright (c) 2012, 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.

-

-// This Firefox add-on exposes the Javascript console contents to Javascript

-// running in the browser. Once this is installed there will be a new

-// window.ConsoleCollector object with read() and clear() functions.

-

-var ConsoleCollector = {};

- 

-(function() {

-  // An array for collecting the messages.

-  var messages = [];

-

-  // Add a console message to the collection.

-  this.add = function(message) {

-    messages.push(message);

-  };

-

-  // Read the message collection. As a side effect we clear the message list.

-  this.read = function(type) {

-    var rtn = [];

-    for (var i = 0; i < messages.length; i++) {

-      var message = messages[i];

-      if (message.errorMessage) {

-        rtn.push({ 'time' : message.timeStamp,

-                 'source' : message.sourceName,

-                 'line': message.lineNumber,

-                 'column': message.columnNumber,

-                 'category': message.category,

-                 'message' : message.errorMessage });

-      }

-    }

-    messages = [];

-    return rtn;

-  };

-

-  // Clear the message list.

-  this.clear = function() {

-    messages = [];

-  };

-}).apply(ConsoleCollector);

-

-// A Console Listener.

-// See https://developer.mozilla.org/en-US/docs/Console_service for

-// details.

-(function() {

-

-  var consoleService;

-

-  var consoleListener = {

-    observe: function(e) {

-      try {

-        var message = e.QueryInterface(Components.interfaces.nsIScriptError);

-        ConsoleCollector.add(message);

-      } catch (exception) {

-        ConsoleCollector.add(e);

-      }      

-    },

-

-    QueryInterface: function (iid) {

-      if (!iid.equals(Components.interfaces.nsIConsoleListener) &&

-      !iid.equals(Components.interfaces.nsISupports)) {

-        throw Components.results.NS_ERROR_NO_INTERFACE;

-      }

-      return this;

-    }

-  };

-  

-  // Start collecting console messages.

-  function initialize(event) {

-    consoleService = Components.classes['@mozilla.org/consoleservice;1']

-                   .getService(Components.interfaces.nsIConsoleService);

-    if (consoleService) {

-      consoleService.registerListener(consoleListener); 

-    }                              

-    // Add the handler for hooking in to each page's DOM. This handler

-    // is for each "gBrowser", representing a tab/window.

-    window.getBrowser().addEventListener("load", onPageLoad, true);

-  }

-

-  // Stop collecting console messages.

-  function shutdown(event) {

-    window.getBrowser().removeEventListener("load", onPageLoad);

-    consoleService.unregisterListener(consoleListener);   

-    ConsoleCollector.clear();

-  }

-  

-  // Hook the ConsoleCollector into the DOM as window.ConsoleCollector.

-  var onPageLoad = function(e) {

-    var win = e.originalTarget.defaultView;

-    if (win) {

-      win.wrappedJSObject.ConsoleCollector = ConsoleCollector;

-    }

-  };

-

-  // Add the handlers to initialize the add-on and shut it down.

-  // These handlers are for the application as a whole.

-  window.addEventListener('load', initialize, false);

-  window.addEventListener('unload', shutdown, false);

-}());

-

-

-

diff --git a/tools/testing/extensions/firefox/ConsoleCollector/chrome/content/overlay.xul b/tools/testing/extensions/firefox/ConsoleCollector/chrome/content/overlay.xul
deleted file mode 100644
index 21347fb..0000000
--- a/tools/testing/extensions/firefox/ConsoleCollector/chrome/content/overlay.xul
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>

-<overlay id="ConsoleCollector-overlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

-  <script src="console.js"/>

-</overlay>

diff --git a/tools/testing/extensions/firefox/ConsoleCollector/install.rdf b/tools/testing/extensions/firefox/ConsoleCollector/install.rdf
deleted file mode 100644
index 3966143..0000000
--- a/tools/testing/extensions/firefox/ConsoleCollector/install.rdf
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" 
-    xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>ConsoleCollector@google.com</em:id>
-    <em:name>Console collector</em:name>
-    <em:version>0.2</em:version>    
-    <em:description>Exposes the Javascript console to each browser window.
-    </em:description>
-    <em:creator>Graham Wheeler</em:creator>
-    <em:homepageURL>http://www.dartlang.org</em:homepageURL>
-    <em:targetApplication>
-      <Description>
-        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> <!-- Firefox -->
-        <em:minVersion>3.0</em:minVersion>
-        <em:maxVersion>15.0</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-  </Description>
-</RDF>
diff --git a/tools/testing/run_selenium.py b/tools/testing/run_selenium.py
deleted file mode 100755
index e270619..0000000
--- a/tools/testing/run_selenium.py
+++ /dev/null
@@ -1,390 +0,0 @@
-#!/usr/bin/python
-
-# Copyright (c) 2011, 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.
-#
-
-"""Script to actually open a browser and perform the test, and reports back with
-the result. It uses Selenium WebDriver when possible for running the tests. It
-uses Selenium RC for Safari.
-
-If started with --batch this script runs a batch of in-browser tests in
-the same browser process.
-
-Normal mode:
-$ python run_selenium.py --browser=ff --timeout=60 path/to/test.html
-
-Exit code indicates pass or fail
-
-Batch mode:
-$ python run_selenium.py --batch
-stdin:  --browser=ff --timeout=60 path/to/test.html
-stdout: >>> TEST PASS
-stdin:  --browser=ff --timeout=60 path/to/test2.html
-stdout: >>> TEST FAIL
-stdin:  --terminate
-$
-"""
-
-import os
-import optparse
-import platform
-import selenium
-from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
-from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver
-from selenium.webdriver.support.ui import WebDriverWait
-import shutil
-import signal
-import socket
-import sys
-import time
-import urllib2
-import threading
-
-TIMEOUT_ERROR_MSG = 'FAIL (timeout)'
-CRASH_ERROR_MSG = 'CRASH'
-
-def correctness_test_done(source):
-  """Checks if test has completed."""
-  return ('PASS' in source) or ('FAIL' in source)
-
-def perf_test_done(source):
-  """Tests to see if our performance test is done by printing a score."""
-  #This code is written this way to work around a current instability in the
-  # python webdriver bindings if you call driver.get_element_by_id.
-  #TODO(efortuna): Access these elements in a nicer way using DOM parser.
-  string = '<div id="status">'
-  index = source.find(string)
-  end_index = source.find('</div>', index+1)
-  source = source[index + len(string):end_index]
-  return 'Score:' in source
-
-# TODO(vsm): Ideally, this wouldn't live in this file.
-CONFIGURATIONS = {
-    'correctness': correctness_test_done,
-    'perf': perf_test_done
-}
-
-def run_test_in_browser(browser, html_out, timeout, mode, refresh):
-  """Run the desired test in the browser using Selenium 2.0 WebDriver syntax,
-  and wait for the test to complete. This is the newer syntax, that currently
-  supports Firefox, Chrome, IE, Opera (and some mobile browsers)."""
-
-  if isinstance(browser, selenium.selenium):
-    return run_test_in_browser_selenium_rc(browser, html_out, timeout, mode,
-        refresh)
-
-  browser.get(html_out)
-  if refresh:
-    browser.refresh()
-  try:
-    def pythonTimeout():
-      # The builtin quit call for chrome will call close on the RemoteDriver
-      # which may hang. Explicitly call browser.service.stop()
-      if (type(browser) is selenium.webdriver.chrome.webdriver.WebDriver):
-        # Browser may be dead
-        try:
-          browser.service.stop()
-        except:
-          print("Trying to close browser that has already been closed")
-    # If the browser is crashing selenium may not time out.
-    # Explicitly catch this case with a python timer.
-    t = threading.Timer(timeout, pythonTimeout)
-    t.start()
-    test_done = CONFIGURATIONS[mode]
-    element = WebDriverWait(browser, float(timeout)).until(
-        lambda driver: test_done(driver.page_source))
-    t.cancel()
-    return browser.page_source
-  except selenium.common.exceptions.TimeoutException:
-    return TIMEOUT_ERROR_MSG
-  except:
-    return CRASH_ERROR_MSG
-
-def run_test_in_browser_selenium_rc(sel, html_out, timeout, mode, refresh):
-  """ Run the desired test in the browser using Selenium 1.0 syntax, and wait
-  for the test to complete. This is used for Safari, since it is not currently
-  supported on Selenium 2.0."""
-  sel.open(html_out)
-  if refresh:
-    sel.refresh()
-  source = sel.get_html_source()
-  end_condition = CONFIGURATIONS[mode]
-
-  elapsed = 0
-  while (not end_condition(source)) and elapsed <= timeout:
-    sec = .25
-    time.sleep(sec)
-    elapsed += sec
-    source = sel.get_html_source()
-  return source
-
-def parse_args(args=None):
-  parser = optparse.OptionParser()
-  parser.add_option('--out', dest='out',
-      help = 'The path for html output file that we will running our test from',
-      action = 'store', default = '')
-  parser.add_option('--browser', dest='browser',
-      help = 'The browser type (default = chrome)',
-      action = 'store', default = 'chrome')
-  parser.add_option('--executable', dest='executable',
-      help = 'The browser executable path (only for browser=dartium)',
-      action = 'store', default = None)
-  # TODO(efortuna): Put this back up to be more than the default timeout in
-  # test.dart. Right now it needs to be less than 60 so that when test.dart
-  # times out, this script also closes the browser windows.
-  parser.add_option('--timeout', dest = 'timeout',
-      help = 'Amount of time (seconds) to wait before timeout', type = 'int',
-      action = 'store', default=58)
-  parser.add_option('--mode', dest = 'mode',
-      help = 'The type of test we are running',
-      action = 'store', default='correctness')
-  parser.add_option('--force-refresh', dest='refresh',
-      help='Force the browser to refresh before getting results from this test '
-      '(used for browser multitests).', action='store_true', default=False)
-  args, _ = parser.parse_args(args=args)
-  args.out = args.out.strip('"')
-  if args.executable and args.browser != 'dartium':
-    print 'Executable path only supported when browser=dartium.'
-    sys.exit(1)
-  return (args.out, args.browser, args.executable, args.timeout, args.mode,
-      args.refresh)
-
-def print_server_error():
-  """Provide the user an informative error message if we attempt to connect to
-  the Selenium remote control server, but cannot access it. Then exit the
-  program."""
-  print ('ERROR: Could not connect to Selenium RC server. Are you running'
-      ' java -jar tools/testing/selenium-server-standalone-*.jar? If not, '
-      'start it before running this test.')
-  sys.exit(1)
-
-def start_browser(browser, executable_path, html_out):
-  if browser == 'chrome' or browser == 'dartium':
-    # Note: you need ChromeDriver *in your path* to run Chrome, in addition to
-    # installing Chrome. Also note that the build bot runs have a different path
-    # from a normal user -- check the build logs.
-    options = selenium.webdriver.chrome.options.Options()
-    if browser == 'dartium':
-      script_dir = os.path.dirname(os.path.abspath(__file__))
-      dartium_dir = os.path.join(script_dir, '..', '..', 'client', 'tests',
-                                 'dartium')
-      # enable ShadowDOM and style scoped for Dartium
-      options.add_argument('--enable-shadow-dom')
-      options.add_argument('--enable-style-scoped')
-      if executable_path is not None:
-        options.binary_location = executable_path
-      elif platform.system() == 'Windows':
-        options.binary_location = os.path.join(dartium_dir, 'chrome.exe')
-      elif platform.system() == 'Darwin':
-        options.binary_location = os.path.join(dartium_dir, 'Chromium.app',
-                                               'Contents', 'MacOS', 'Chromium')
-      else:
-        options.binary_location = os.path.join(dartium_dir, 'chrome')
-    return selenium.webdriver.Chrome(chrome_options=options)
-  elif browser == 'ff':
-    script_dir = os.path.dirname(os.path.abspath(__file__))
-    profile = selenium.webdriver.firefox.firefox_profile.FirefoxProfile()
-    profile.set_preference('dom.max_script_run_time', 0)
-    profile.set_preference('dom.max_chrome_script_run_time', 0)
-    profile.set_preference('app.update.auto', True)
-    profile.set_preference('app.update.enabled', True)
-    return selenium.webdriver.Firefox(firefox_profile=profile)
-  elif ((browser == 'ie9' or browser == 'ie10') and
-      platform.system() == 'Windows'):
-    return selenium.webdriver.Ie()
-  elif browser == 'safari' and platform.system() == 'Darwin':
-    # TODO(efortuna): Ensure our preferences (no pop-up blocking) file is the
-    # same (Safari auto-deletes when it has too many "crashes," or in our case,
-    # timeouts). Come up with a less hacky way to do this.
-    backup_safari_prefs = os.path.dirname(__file__) + '/com.apple.Safari.plist'
-    if os.path.exists(backup_safari_prefs):
-      shutil.copy(backup_safari_prefs,
-           '/Library/Preferences/com.apple.Safari.plist')
-    sel = selenium.selenium('localhost', 4444, "*safari", html_out)
-    try:
-      sel.start()
-      return sel
-    except socket.error:
-      print_server_error()
-  elif browser == 'opera':
-    try:
-      driver = RemoteWebDriver(desired_capabilities=DesiredCapabilities.OPERA)
-      # By default, Opera sets their script timeout (the amount of time they
-      # expect to hear back from the JavaScript file) to be 10 seconds. We just
-      # make it an impossibly large number so that it doesn't time out for this
-      # reason, so it behaves like all of the other browser drivers.
-      driver.set_script_timeout(9000)
-      # If the webpage contains document.onreadystatechanged = function() {...}
-      # page load event does not correctly get fired and caught (OperaDriver
-      # bug). This is a band-aid.
-      driver.set_page_load_timeout(1)
-      return driver
-    except urllib2.URLError:
-      print_server_error()
-  else:
-    raise Exception('Incompatible browser and platform combination.')
-
-def close_browser(browser):
-  if browser is None:
-    return
-  if isinstance(browser, selenium.selenium):
-    browser.stop()
-    return
-
-  # A timeout exception is thrown if nothing happens within the time limit.
-  if (type(browser) is not selenium.webdriver.chrome.webdriver.WebDriver and
-      type(browser) is not selenium.webdriver.ie.webdriver.WebDriver):
-    browser.close()
-
-  browser.quit()
-
-def report_results(mode, source, browser):
-  if mode != 'correctness':
-    # We're running a performance test.
-    print source.encode('utf8')
-    sys.stdout.flush()
-    if 'NaN' in source:
-      return 1
-    else:
-      return 0
-  else:
-    # We're running a correctness test. Mark test as passing if all individual
-    # test cases pass.
-    if 'FAIL' not in source and 'PASS' in source:
-      print 'Content-Type: text/plain\nPASS'
-      return 0
-    else:
-      #The hacky way to get document.getElementById('body').innerHTML for this
-      # webpage, without the JavaScript.
-      #TODO(efortuna): Access these elements in a nicer way using DOM parser.
-      index = source.find('<body>')
-      index += len('<body>')
-      end_index = source.find('</body')
-      print unicode(source[index : end_index]).encode("utf-8")
-      return 1
-
-
-def run_batch_tests():
-  '''
-  Runs a batch of in-browser tests in the same browser process. Batching
-  gives faster throughput and makes tests less subject to browser starting
-  flakiness, issues with too many browser processes running, etc.
-
-  When running this function, stdin/stdout is used to communicate with the test
-  framework. See BatchRunnerProcess in test_runner.dart for the other side of
-  this communication channel
-
-  Example of usage:
-  $ python run_selenium.py --batch
-  stdin:  --browser=ff --timeout=60 path/to/test.html
-  stdout: >>> TEST PASS
-  stdin:  --browser=ff --timeout=60 path/to/test2.html
-  stdout: >>> TEST FAIL
-  stdin:  --terminate
-  $
-  '''
-
-  print '>>> BATCH START'
-  browser = None
-  current_browser_name = None
-
-  # TODO(jmesserly): It'd be nice to shutdown gracefully in the event of a
-  # SIGTERM. Unfortunately dart:io cannot send SIGTERM, see dartbug.com/1756.
-  signal.signal(signal.SIGTERM, lambda number, frame: close_browser(browser))
-
-  try:
-    try:
-      while True:
-        line = sys.stdin.readline()
-        if line == '--terminate\n':
-          print("Terminating selenium driver")
-          break
-
-        (html_out, browser_name, executable_path,
-         timeout, mode, refresh) = parse_args(line.split())
-
-        # Sanity checks that test.dart is passing flags we can handle.
-        if mode != 'correctness':
-          print 'Batch test runner not compatible with perf testing'
-          return 1
-        if browser and current_browser_name != browser_name:
-          print('Batch test runner got multiple browsers: %s and %s'
-              % (current_browser_name, browser_name))
-          return 1
-
-        # Start the browser on the first run
-        if browser is None:
-          current_browser_name = browser_name
-          browser = start_browser(browser_name, executable_path, html_out)
-
-        source = run_test_in_browser(browser, html_out, timeout, mode, refresh)
-
-        # Test is done. Write end token to stderr and flush.
-        sys.stderr.write('>>> EOF STDERR\n')
-        sys.stderr.flush()
-
-        # print one of:
-        # >>> TEST {PASS, FAIL, OK, CRASH, FAIL, TIMEOUT}
-        status = report_results(mode, source, browser)
-        if status == 0:
-          print '>>> TEST PASS'
-        elif source == TIMEOUT_ERROR_MSG:
-          print '>>> TEST TIMEOUT'
-        elif source == CRASH_ERROR_MSG:
-          print '>>> TEST CRASH'
-          # The browser crashed, set the browser to None so that we will
-          # create a new instance on next iteration.
-          browser = None
-        else:
-          print '>>> TEST FAIL'
-        sys.stdout.flush()
-    except:
-      type, value, traceback = sys.exc_info()
-      print "run_selenium.py: Unexpected exception occured: "
-      print "    type: ", type
-      print "    value: ", value
-      print "    traceback: ", traceback
-      raise
-  finally:
-    sys.stdin.close()
-    print("Closing browser");
-
-    def close_output_streams():
-      sys.stdout.flush()
-      sys.stdout.close()
-      sys.stderr.flush()
-      sys.stderr.close()
-
-    def close_and_exit():
-      print("Timed out waiting for browser to close")
-      close_output_streams()
-      exit(1)
-
-    timer = threading.Timer(5.0, close_and_exit)
-    timer.start()
-    try:
-      close_browser(browser)
-      timer.cancel()
-    finally:
-      close_output_streams()
-
-def main(args):
-  # Run in batch mode if the --batch flag is passed.
-  # TODO(jmesserly): reconcile with the existing args parsing
-  if '--batch' in args:
-    return run_batch_tests()
-
-  # Run a single test
-  html_out, browser_name, executable_path, timeout, mode, refresh = parse_args()
-  browser = start_browser(browser_name, executable_path, html_out)
-
-  try:
-    output = run_test_in_browser(browser, html_out, timeout, mode, refresh)
-    return report_results(mode, output, browser)
-  finally:
-    close_browser(browser)
-
-if __name__ == "__main__":
-  sys.exit(main(sys.argv))
diff --git a/tools/testing/webdriver_test_setup.py b/tools/testing/webdriver_test_setup.py
deleted file mode 100755
index c68543b..0000000
--- a/tools/testing/webdriver_test_setup.py
+++ /dev/null
@@ -1,434 +0,0 @@
-#!/usr/bin/python
-
-# Copyright (c) 2012, 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.
-
-# Run to install the necessary components to run webdriver on the buildbots or
-# on your local machine.
-# Note: The setup steps can be done fairly easily by hand. This script is
-# intended to simply and reduce the time for setup since there are a fair number
-# of steps.
-
-# TODO(efortuna): Rewrite this script in Dart when the Process module has a
-# better high level API.
-import HTMLParser
-import optparse
-import os
-import platform
-import re
-import shutil
-import string
-import subprocess
-import sys
-import urllib
-import urllib2
-import zipfile
-
-def run_cmd(cmd, stdin=None):
-  """Run the command on the command line in the shell. We print the output of
-  the command.
-  """
-  print cmd
-  p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
-      stdin=subprocess.PIPE, shell=True)
-  output, stderr = p.communicate(input=stdin)
-  if output:
-    print output
-  if stderr:
-    print stderr
-
-def parse_args():
-  parser = optparse.OptionParser()
-  parser.add_option('--firefox', '-f', dest='firefox',
-      help="Don't install Firefox", action='store_true', default=False)
-  parser.add_option('--opera', '-o', dest='opera', default=False,
-      help="Don't install Opera", action='store_true')
-  parser.add_option('--chromedriver', '-c', dest='chromedriver',
-      help="Don't install chromedriver.", action='store_true', default=False)
-  parser.add_option('--iedriver', '-i', dest='iedriver',
-      help="Don't install iedriver (only used on Windows).",
-      action='store_true', default=False)
-  parser.add_option('--seleniumrc', '-s', dest='seleniumrc',
-      help="Don't install the Selenium RC server (used for Safari and Opera "
-           "tests).", action='store_true', default=False)
-  parser.add_option('--python', '-p', dest='python',
-      help="Don't install Selenium python bindings.", action='store_true',
-      default=False)
-  parser.add_option('--buildbot', '-b', dest='buildbot', action='store_true',
-      help='Perform a buildbot selenium setup (buildbots have a different' +
-      'location for their python executable).', default=False)
-  args, _ = parser.parse_args()
-  return args
-
-def find_depot_tools_location(is_buildbot):
-  """Depot_tools is our default install location for chromedriver, so we find
-  its location on the filesystem.
-  Arguments:
-  is_buildbot - True if we are running buildbot machine setup (we can't detect
-      this automatically because this script is not run at build time).
-  """
-  if is_buildbot:
-    depot_tools = os.sep + os.path.join('b', 'depot_tools')
-    if 'win32' in sys.platform or 'cygwin' in sys.platform:
-      depot_tools = os.path.join('e:', depot_tools)
-    return depot_tools
-  else:
-    path = os.environ['PATH'].split(os.pathsep)
-    for loc in path:
-      if 'depot_tools' in loc:
-        return loc
-  raise Exception("Could not find depot_tools in your path.")
-
-class GoogleBasedInstaller(object):
-  """Install a project from a Google source, pulling latest version."""
-
-  def __init__(self, project_name, destination, download_path_func):
-    """Create an object that will install the project.
-    Arguments:
-    project_name - Google code name of the project, such as "selenium" or
-    "chromedriver."
-    destination - Where to download the desired file on our filesystem.
-    download_path_func - A function that takes a dictionary (currently with keys
-    "os" and "version", but more can be added) that calculates the string
-    representing the path of the download we want.
-    """
-    self.project_name = project_name
-    self.destination = destination
-    self.download_path_func = download_path_func
-
-  @property
-  def get_os_str(self):
-    """The strings to indicate what OS a download is for."""
-    os_str = 'win'
-    if 'darwin' in sys.platform:
-      os_str = 'mac'
-    elif 'linux' in sys.platform:
-      os_str = 'linux32'
-      if '64bit' in platform.architecture()[0]:
-        os_str = 'linux64'
-    if self.project_name == 'chromedriver' and (
-        os_str == 'mac' or os_str == 'win'):
-      os_str += '32'
-    return os_str
-
-  def run(self):
-    """Download and install the project."""
-    print 'Installing %s' % self.project_name
-    os_str = self.get_os_str
-    version = self.find_latest_version()
-    download_path = self.download_path_func({'os': os_str, 'version': version})
-    download_name = os.path.basename(download_path)
-    urllib.urlretrieve(os.path.join(self.source_path(), download_path),
-        os.path.join(self.destination, download_name))
-    if download_name.endswith('.zip'):
-      if platform.system() != 'Windows':
-        # The Python zip utility does not preserve executable permissions, but
-        # this does not seem to be a problem for Windows, which does not have a
-        # built in zip utility. :-/
-        run_cmd('unzip -u %s -d %s' % (os.path.join(self.destination,
-                download_name), self.destination), stdin='y')
-      else:
-        z = zipfile.ZipFile(os.path.join(self.destination, download_name))
-        z.extractall(self.destination)
-        z.close()
-      os.remove(os.path.join(self.destination, download_name))
-    chrome_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),
-        'orig-chromedriver')
-    if self.project_name == 'chromedriver' and os.path.exists(chrome_path):
-      # We have one additional location to make sure chromedriver is updated.
-      # TODO(efortuna): Remove this. See move_chrome_driver_if_needed in
-      # perf_testing/run_perf_tests.py
-      driver = 'chromedriver'
-      if platform.system() == 'Windows':
-        driver += '.exe'
-      shutil.copy(os.path.join(self.destination, driver),
-          os.path.join(chrome_path, driver))
-
-class ChromeDriverInstaller(GoogleBasedInstaller):
-  """Install chromedriver from Google Storage."""
-
-  def __init__(self, destination):
-    """Create an object to install ChromeDriver
-    destination - Where to download the desired file on our filesystem.
-    """
-    super(ChromeDriverInstaller, self).__init__('chromedriver', destination,
-        lambda x: '%(version)s/chromedriver_%(os)s.zip' % x)
-
-  def find_latest_version(self):
-    """Find the latest version number of ChromeDriver."""
-    source_page = urllib2.urlopen(self.source_path())
-    source_text = source_page.read()
-    regex = re.compile('(?:<Key>)(\d+\.\d+)')
-    latest = max(regex.findall(source_text))
-    return latest
-
-  def source_path(self):
-    return 'http://chromedriver.storage.googleapis.com'
-
-class GoogleCodeInstaller(GoogleBasedInstaller):
-  """Install a project from Google Code."""
-
-  def google_code_downloads_page(self):
-    return 'http://code.google.com/p/%s/downloads/list' % self.project_name
-
-  def find_latest_version(self):
-    """Find the latest version number of some code available for download on a
-    Google code page. This was unfortunately done in an ad hoc manner because
-    Google Code does not seem to have an API for their list of current
-    downloads(!).
-    """
-    google_code_site = self.google_code_downloads_page()
-    f = urllib2.urlopen(google_code_site)
-    latest = ''
-
-    download_regex_str = self.download_path_func({'os': self.get_os_str,
-        'version': '.+'})
-
-    for line in f.readlines():
-      if re.search(download_regex_str, line):
-        suffix_index = line.find(
-            download_regex_str[download_regex_str.rfind('.'):])
-        name_end = download_regex_str.rfind('.+')
-        name = self.download_path_func({'os': self.get_os_str, 'version': ''})
-        name = name[:name.rfind('.')]
-        version_str = line[line.find(name) + len(name) : suffix_index]
-        orig_version_str = version_str
-        if version_str.count('.') == 0:
-          version_str = version_str.replace('_', '.')
-          version_str = re.compile(r'[^\d.]+').sub('', version_str)
-        if latest == '':
-          latest = '0.' * version_str.count('.')
-          latest += '0'
-          orig_latest_str = latest
-        else:
-          orig_latest_str = latest
-          latest = latest.replace('_', '.')
-          latest = re.compile(r'[^\d.]+').sub('', latest)
-        nums = version_str.split('.')
-        latest_nums = latest.split('.')
-        for (num, latest_num) in zip(nums, latest_nums):
-          if int(num) > int(latest_num):
-            latest = orig_version_str
-            break
-          else:
-            latest = orig_latest_str
-    if latest == '':
-      raise Exception("Couldn't find the desired download on " + \
-          ' %s.' % google_code_site)
-    return latest
-
-  def source_path(self):
-    return 'http://%s.googlecode.com/files/' % self.project_name
-
-
-class FirefoxInstaller(object):
-  """Installs the latest version of Firefox on the machine."""
-
-  def ff_download_site(self, os_name):
-    return 'http://releases.mozilla.org/pub/mozilla.org/firefox/releases/' + \
-        'latest/%s/en-US/' % os_name
-
-  @property
-  def get_os_str(self):
-    """Returns the string that Mozilla uses to denote which operating system a
-    Firefox binary is for."""
-    os_str = ('win32', '.exe')
-    if 'darwin' in sys.platform:
-      os_str = ('mac', '.dmg')
-    elif 'linux' in sys.platform:
-      os_str = ('linux-i686', '.tar.bz2')
-      if '64bit' in platform.architecture()[0]:
-        os_str = ('linux-x86_64', '.tar.bz2')
-    return os_str
-
-  def get_download_url(self):
-    """Parse the html on the page to determine what is the latest download
-    appropriate for our system."""
-    f = urllib2.urlopen(self.ff_download_site(self.get_os_str[0]))
-    download_name = ''
-    for line in f.readlines():
-      suffix = self.get_os_str[1]
-      if (suffix + '"') in line:
-        link_str = '<a href="'
-        download_name = line[line.find(link_str) + len(link_str) : \
-            line.find(suffix) + len(suffix)]
-        break
-    return '%s%s' % (self.ff_download_site(self.get_os_str[0]), download_name)
-
-  def run(self):
-    print 'Installing Firefox'
-    if 'darwin' in sys.platform:
-      urllib.urlretrieve(self.get_download_url(), 'firefox.dmg')
-      run_cmd('hdiutil mount firefox.dmg')
-      run_cmd('sudo cp -R /Volumes/firefox/Firefox.app /Applications')
-      run_cmd('hdiutil unmount /Volumes/firefox/')
-    elif 'win' in sys.platform:
-      urllib.urlretrieve(self.get_download_url(), 'firefox_install.exe')
-      run_cmd('firefox_install.exe -ms')
-    else:
-      run_cmd('wget -O - %s | tar -C ~ -jxv' % self.get_download_url())
-
-
-class SeleniumBindingsInstaller(object):
-  """Install the Selenium Webdriver bindings for Python."""
-
-  SETUPTOOLS_SITE = 'http://python-distribute.org/distribute_setup.py'
-  PIP_SITE = 'https://raw.github.com/pypa/pip/master/contrib/get-pip.py'
-  def __init__(self, is_buildbot):
-    self.is_buildbot = is_buildbot
-
-  def run(self):
-    print 'Installing Selenium Python Bindings'
-    admin_keyword = ''
-    python_cmd = 'python'
-    pip_cmd = 'pip'
-    if 'win32' not in sys.platform and 'cygwin' not in sys.platform:
-      admin_keyword = 'sudo'
-      pip_cmd = '/usr/local/bin/pip'
-    else:
-      # The python installation is "special" on Windows buildbots.
-      if self.is_buildbot:
-        python_loc = os.path.join(
-            find_depot_tools_location(self.is_buildbot), 'python_bin')
-        python_cmd = os.path.join(python_loc, 'python')
-        pip_cmd = os.path.join(python_loc, 'Scripts', pip_cmd)
-      else:
-        path = os.environ['PATH'].split(os.pathsep)
-        for loc in path:
-          if 'python' in loc or 'Python' in loc:
-            pip_cmd = os.path.join(loc, 'Scripts', pip_cmd)
-            break
-    page = urllib2.urlopen(self.SETUPTOOLS_SITE)
-    run_cmd('%s %s' % (admin_keyword, python_cmd), page.read())
-    page = urllib2.urlopen(self.PIP_SITE)
-    run_cmd('%s %s' % (admin_keyword, python_cmd), page.read())
-    run_cmd('%s %s install -U selenium' % (admin_keyword, pip_cmd))
-
-class OperaHtmlParser(HTMLParser.HTMLParser):
-  """A helper class to parse Opera pages listing available downloads to find the
-  correct download we want."""
-
-  def initialize(self, rejection_func, accept_func):
-    """Initialize some state for our parser.
-    Arguments:
-    rejection_func: A function that accepts the value of the URL and determines
-      if it is of the type we are looking for.
-    accept_func: A function that takes the URL and the "current best" URL and
-      determines if it is better than our current download url."""
-    self.latest = 0
-    self.rejection_func = rejection_func
-    self.accept_func = accept_func
-
-  def handle_starttag(self, tag, attrs):
-    """Find the latest version."""
-    if (tag == 'a' and attrs[0][0] == 'href' and
-        self.rejection_func(attrs[0][1])):
-      self.latest = self.accept_func(attrs[0][1], self.latest)
-
-class OperaInstaller(object):
-  """Install from the Opera FTP website."""
-
-  def find_latest_version(self, download_page, rejection_func, accept_func):
-    """Get the latest non-beta version.
-    Arguments:
-    download_page: The initial page that lists all the download options.
-    rejection_func: A function that accepts the value of the URL and determines
-      if it is of the type we are looking for.
-    accept_func: A function that takes the URL and the "current best" URL and
-      determines if it is better than our current download url."""
-    f = urllib2.urlopen(download_page)
-    parser = OperaHtmlParser()
-    parser.initialize(rejection_func, accept_func)
-    parser.feed(f.read())
-    return str(parser.latest)
-
-  def run(self):
-    """Download and install Opera."""
-    print 'Installing Opera'
-    os_str = self.get_os_str
-    download_name = 'http://ftp.opera.com/pub/opera/%s/' % os_str
-
-    def higher_revision(new_version_str, current):
-      version_string = new_version_str[:-1]
-      if int(version_string) > current:
-        return int(version_string)
-      return current
-
-    version = self.find_latest_version(
-        download_name,
-        lambda x: x[0] in string.digits and 'b' not in x and 'rc' not in x,
-        higher_revision)
-    download_name += version
-    if ('linux' in sys.platform and
-        platform.linux_distribution()[0] == 'Ubuntu'):
-      # Last time I tried, the .deb file you download directly from opera was
-      # not installing correctly on Ubuntu. This installs Opera more nicely.
-      os.system("sudo sh -c 'wget -O - http://deb.opera.com/archive.key | "
-          "apt-key add -'")
-      os.system("""sudo sh -c 'echo "deb http://deb.opera.com/opera/ """
-          """stable non-free" > /etc/apt/sources.list.d/opera.list'""")
-      run_cmd('sudo apt-get update')
-      run_cmd('sudo apt-get install opera', stdin='y')
-    else:
-      if 'darwin' in sys.platform:
-        dotted_version = '%s.%s' % (version[:2], version[2:])
-        download_name += '/Opera_%s_Setup_Intel.dmg' % dotted_version
-        urllib.urlretrieve(download_name, 'opera.dmg')
-        run_cmd('hdiutil mount opera.dmg', stdin='qY\n')
-        run_cmd('sudo cp -R /Volumes/Opera/Opera.app /Applications')
-        run_cmd('hdiutil unmount /Volumes/Opera/')
-      elif 'win' in sys.platform:
-        download_name += '/en/Opera_%s_en_Setup.exe' % version
-        urllib.urlretrieve(download_name, 'opera_install.exe')
-        run_cmd('opera_install.exe -ms')
-      else:
-        # For all other flavors of linux, download the tar.
-        download_name += '/'
-        extension = '.tar.bz2'
-        if '64bit' in platform.architecture()[0]:
-          platform_str = '.x86_64'
-        else:
-          platform_str = '.i386'
-        def get_acceptable_file(new_version_str, current):
-          return new_version_str
-        latest = self.find_latest_version(
-            download_name,
-            lambda x: x.startswith('opera') and x.endswith(extension)
-                and platform_str in x,
-            get_acceptable_file)
-        download_name += latest
-        run_cmd('wget -O - %s | tar -C ~ -jxv' % download_name)
-        print ('PLEASE MANUALLY RUN "~/%s/install" TO COMPLETE OPERA '
-            'INSTALLATION' %
-            download_name[download_name.rfind('/') + 1:-len(extension)])
-
-  @property
-  def get_os_str(self):
-    """The strings to indicate what OS a download is."""
-    os_str = 'win'
-    if 'darwin' in sys.platform:
-      os_str = 'mac'
-    elif 'linux' in sys.platform:
-      os_str = 'linux'
-    return os_str
-
-def main():
-  args = parse_args()
-  if not args.python:
-    SeleniumBindingsInstaller(args.buildbot).run()
-  if not args.chromedriver:
-    ChromeDriverInstaller(find_depot_tools_location(args.buildbot)).run()
-  if not args.seleniumrc:
-    GoogleCodeInstaller('selenium', os.path.dirname(os.path.abspath(__file__)),
-        lambda x: 'selenium-server-standalone-%(version)s.jar' % x).run()
-  if not args.iedriver and platform.system() == 'Windows':
-    GoogleCodeInstaller('selenium', find_depot_tools_location(args.buildbot),
-        lambda x: 'IEDriverServer_Win32_%(version)s.zip' % x).run()
-  if not args.firefox:
-    FirefoxInstaller().run()
-  if not args.opera:
-    OperaInstaller().run()
-
-if __name__ == '__main__':
-  main()
diff --git a/tools/utils.py b/tools/utils.py
index a8f96f6..25bd8e7 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -817,7 +817,8 @@
 class BaseCoreDumpArchiver(object):
   """This class reads coredumps file written by UnexpectedCrashDumpArchiver
   into the current working directory and uploads all cores and binaries
-  listed in it into Cloud Storage (see tools/testing/dart/test_progress.dart).
+  listed in it into Cloud Storage (see
+  pkg/test_runner/lib/src/test_progress.dart).
   """
 
   # test.dart will write a line for each unexpected crash into this file.
@@ -1156,7 +1157,7 @@
 
 def CoreDumpArchiver(args):
   enabled = '--copy-coredumps' in args
-  prefix = '--output_directory='
+  prefix = '--output-directory='
   output_directory = next((arg[len(prefix):] for arg in args if arg.startswith(prefix)), None)
 
   if not enabled:
diff --git a/utils/bazel/kernel_worker.dart b/utils/bazel/kernel_worker.dart
index 5cd62a0..3ca1729 100644
--- a/utils/bazel/kernel_worker.dart
+++ b/utils/bazel/kernel_worker.dart
@@ -11,6 +11,7 @@
 
 import 'dart:async';
 import 'dart:io';
+import 'dart:isolate';
 
 import 'package:args/args.dart';
 import 'package:bazel_worker/bazel_worker.dart';
@@ -24,7 +25,9 @@
 import 'package:vm/target/flutter_runner.dart';
 import 'package:compiler/src/kernel/dart2js_target.dart';
 
-main(List<String> args) async {
+/// [sendPort] may be passed in when started in an isolate. If provided, it is
+/// used for bazel worker communication instead of stdin/stdout.
+main(List<String> args, SendPort sendPort) async {
   args = preprocessArgs(args);
 
   if (args.contains('--persistent_worker')) {
@@ -32,7 +35,7 @@
       throw new StateError(
           "unexpected args, expected only --persistent-worker but got: $args");
     }
-    await new KernelWorker().run();
+    await new KernelWorker(sendPort: sendPort).run();
   } else {
     var result = await computeKernel(args);
     if (!result.succeeded) {
@@ -45,6 +48,14 @@
 class KernelWorker extends AsyncWorkerLoop {
   fe.InitializedCompilerState previousState;
 
+  /// If [sendPort] is provided it is used for bazel worker communication
+  /// instead of stdin/stdout.
+  KernelWorker({SendPort sendPort})
+      : super(
+            connection: sendPort == null
+                ? null
+                : SendPortAsyncWorkerConnection(sendPort));
+
   Future<WorkResponse> performRequest(WorkRequest request) async {
     var outputBuffer = new StringBuffer();
     var response = new WorkResponse()..exitCode = 0;