Version 2.14.0-289.0.dev

Merge commit 'd37dc1649b1cc3c0bb9ba347251923d081f54c59' into 'dev'
diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json
index fb51c91..204df98 100644
--- a/.dart_tool/package_config.json
+++ b/.dart_tool/package_config.json
@@ -11,7 +11,7 @@
     "constraint, update this by running tools/generate_package_config.dart."
   ],
   "configVersion": 2,
-  "generated": "2021-06-24T14:38:26.203286",
+  "generated": "2021-07-02T15:48:57.033703",
   "generator": "tools/generate_package_config.dart",
   "packages": [
     {
@@ -569,7 +569,7 @@
       "name": "scrape",
       "rootUri": "../pkg/scrape",
       "packageUri": "lib/",
-      "languageVersion": "2.10"
+      "languageVersion": "2.13"
     },
     {
       "name": "sdk_library_metadata",
diff --git a/pkg/_fe_analyzer_shared/pubspec.yaml b/pkg/_fe_analyzer_shared/pubspec.yaml
index a0d6ba6..8b5f6ac 100644
--- a/pkg/_fe_analyzer_shared/pubspec.yaml
+++ b/pkg/_fe_analyzer_shared/pubspec.yaml
@@ -1,5 +1,5 @@
 name: _fe_analyzer_shared
-version: 22.0.0
+version: 23.0.0
 description: Logic that is shared between the front_end and analyzer packages.
 homepage: https://github.com/dart-lang/sdk/tree/master/pkg/_fe_analyzer_shared
 
diff --git a/pkg/analyzer/CHANGELOG.md b/pkg/analyzer/CHANGELOG.md
index dfc8654..6ccdf32 100644
--- a/pkg/analyzer/CHANGELOG.md
+++ b/pkg/analyzer/CHANGELOG.md
@@ -1,4 +1,4 @@
-## 2.0.0-dev (Not yet released - breaking changes)
+## 2.0.0
 * Removed deprecated `Scope.lookup2()`.
 * Removed deprecated setters in API of AST.
 * Removed deprecated `FunctionTypeAliasElement`.
@@ -11,8 +11,6 @@
 * Changed `DartObject.type` from `ParameterizedType?` to `DartType?`.
 * Changed `FunctionType` to implement `DartType`, not `ParameterizedType`.
 * Removed `FunctionType.element` and `FunctionType.typeArguments`.
-
-## 1.8.0-dev
 * Added `StringInterpolation.firstString` and `lastString`, to express
   explicitly  that there are always (possibly empty) strings as the first
   and the last elements of an interpolation.
diff --git a/pkg/analyzer/pubspec.yaml b/pkg/analyzer/pubspec.yaml
index 8d9ff79..d52b622 100644
--- a/pkg/analyzer/pubspec.yaml
+++ b/pkg/analyzer/pubspec.yaml
@@ -1,5 +1,5 @@
 name: analyzer
-version: 2.0.0-dev
+version: 2.0.0
 description: This package provides a library that performs static analysis of Dart code.
 homepage: https://github.com/dart-lang/sdk/tree/master/pkg/analyzer
 
@@ -7,13 +7,13 @@
   sdk: '>=2.12.0 <3.0.0'
 
 dependencies:
-  _fe_analyzer_shared: ^22.0.0
+  _fe_analyzer_shared: ^23.0.0
   cli_util: ^0.3.0
   collection: ^1.15.0
   convert: ^3.0.0
   crypto: ^3.0.0
   glob: ^2.0.0
-  meta: ^1.4.0
+  meta: ^1.7.0
   package_config: ^2.0.0
   path: ^1.8.0
   pub_semver: ^2.0.0
diff --git a/pkg/dev_compiler/lib/src/kernel/asset_file_system.dart b/pkg/dev_compiler/lib/src/kernel/asset_file_system.dart
new file mode 100644
index 0000000..4d73fb2
--- /dev/null
+++ b/pkg/dev_compiler/lib/src/kernel/asset_file_system.dart
@@ -0,0 +1,113 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart = 2.9
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:async/async.dart';
+import 'package:dev_compiler/src/kernel/retry_timeout_client.dart';
+import 'package:front_end/src/api_prototype/file_system.dart';
+import 'package:pedantic/pedantic.dart';
+
+/// A wrapper around asset server that redirects file read requests
+/// to http get requests to the asset server.
+class AssetFileSystem implements FileSystem {
+  FileSystem original;
+  final String server;
+  final String port;
+
+  AssetFileSystem(this.original, this.server, this.port);
+
+  /// Convert the uri to a server uri.
+  Uri _resourceUri(Uri uri) => Uri.parse('http://$server:$port/${uri.path}');
+
+  @override
+  FileSystemEntity entityForUri(Uri uri) {
+    if (uri.scheme == 'file') {
+      return original.entityForUri(uri);
+    }
+
+    // Pass the uri to the asset server in the debugger.
+    return AssetFileSystemEntity(this, _resourceUri(uri));
+  }
+}
+
+class AssetFileSystemEntity implements FileSystemEntity {
+  AssetFileSystem fileSystem;
+
+  @override
+  Uri uri;
+
+  AssetFileSystemEntity(this.fileSystem, this.uri);
+
+  @override
+  Future<bool> exists() async {
+    return _runWithClient((httpClient) async {
+      var response = await httpClient.headUrl(uri);
+      unawaited(_ignore(response));
+      return response.statusCode == HttpStatus.ok;
+    });
+  }
+
+  @override
+  Future<bool> existsAsyncIfPossible() => exists();
+
+  @override
+  Future<List<int>> readAsBytes() async {
+    return _runWithClient((httpClient) async {
+      var response = await httpClient.getUrl(uri);
+      if (response.statusCode != HttpStatus.ok) {
+        unawaited(_ignore(response));
+        throw FileSystemException(
+            uri, 'Asset rerver returned ${response.statusCode}');
+      }
+      return await collectBytes(response);
+    });
+  }
+
+  @override
+  Future<List<int>> readAsBytesAsyncIfPossible() => readAsBytes();
+
+  @override
+  Future<String> readAsString() async {
+    return _runWithClient((httpClient) async {
+      var response = await httpClient.getUrl(uri);
+      if (response.statusCode != HttpStatus.ok) {
+        unawaited(_ignore(response));
+        throw FileSystemException(
+            uri, 'Asset server returned ${response.statusCode}');
+      }
+      return await response.transform(utf8.decoder).join();
+    });
+  }
+
+  /// Execute the [body] with the new http client.
+  ///
+  /// Throws a [FileSystemException] on failure,
+  /// and cleans up the client on return or error.
+  Future<T> _runWithClient<T>(
+      Future<T> Function(RetryTimeoutClient httpClient) body) async {
+    RetryTimeoutClient httpClient;
+    try {
+      httpClient = RetryTimeoutClient(HttpClient(), retries: 4);
+      return await body(httpClient);
+    } on Exception catch (e, s) {
+      throw FileSystemException(uri, '$e:$s');
+    } finally {
+      httpClient?.close(force: true);
+    }
+  }
+
+  /// Make sure the response stream is listened to so that we don't leave
+  /// dangling connections, suppress errors.
+  Future<void> _ignore(HttpClientResponse response) {
+    return response
+        .listen((_) {}, cancelOnError: true)
+        .cancel()
+        .catchError((_) {});
+  }
+}
diff --git a/pkg/dev_compiler/lib/src/kernel/expression_compiler_worker.dart b/pkg/dev_compiler/lib/src/kernel/expression_compiler_worker.dart
index a8b8b85..1cdbd66 100644
--- a/pkg/dev_compiler/lib/src/kernel/expression_compiler_worker.dart
+++ b/pkg/dev_compiler/lib/src/kernel/expression_compiler_worker.dart
@@ -21,9 +21,9 @@
     show duplicateLibrariesReachable;
 import 'package:kernel/target/targets.dart' show TargetFlags;
 import 'package:meta/meta.dart';
-import 'package:vm/http_filesystem.dart';
 
 import '../compiler/js_names.dart';
+import 'asset_file_system.dart';
 import 'command.dart';
 
 /// The service that handles expression compilation requests from
@@ -304,7 +304,8 @@
     // Note that this doesn't actually re-load it if it's already fully loaded.
     if (!await _loadAndUpdateComponent(
         _fullModules[moduleName], moduleName, false)) {
-      throw ArgumentError('Failed to load full dill for module $moduleName');
+      throw ArgumentError('Failed to load full dill for module $moduleName: '
+          '${_fullModules[moduleName]}');
     }
 
     var originalComponent = _moduleCache.componentForModuleName[moduleName];
@@ -568,29 +569,6 @@
   }
 }
 
-/// A wrapper around asset server that redirects file read requests
-/// to http get requests to the asset server.
-class AssetFileSystem extends HttpAwareFileSystem {
-  final String server;
-  final String port;
-
-  AssetFileSystem(FileSystem original, this.server, this.port)
-      : super(original);
-
-  Uri resourceUri(Uri uri) =>
-      Uri.parse('http://$server:$port/getResource?uri=${uri.toString()}');
-
-  @override
-  FileSystemEntity entityForUri(Uri uri) {
-    if (uri.scheme == 'file') {
-      return super.entityForUri(uri);
-    }
-
-    // Pass the uri to the asset server in the debugger.
-    return HttpFileSystemEntity(this, resourceUri(uri));
-  }
-}
-
 /// Module cache used to load modules and look up loaded libraries.
 ///
 /// After each build, the cache is updated with summaries for
diff --git a/pkg/dev_compiler/lib/src/kernel/retry_timeout_client.dart b/pkg/dev_compiler/lib/src/kernel/retry_timeout_client.dart
new file mode 100644
index 0000000..36a768e
--- /dev/null
+++ b/pkg/dev_compiler/lib/src/kernel/retry_timeout_client.dart
@@ -0,0 +1,114 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart=2.9
+
+import 'dart:async';
+import 'dart:io';
+import 'dart:math';
+
+import 'package:pedantic/pedantic.dart';
+
+/// An HTTP client wrapper that times out connections and requests and
+/// automatically retries failing requests.
+class RetryTimeoutClient {
+  /// The wrapped client.
+  final HttpClient _inner;
+
+  /// The number of times a request should be retried.
+  final int _retries;
+
+  /// The callback that determines whether a request should be retried.
+  final bool Function(HttpClientResponse) _when;
+
+  /// The callback that determines whether a request when an error is thrown.
+  final bool Function(Object, StackTrace) _whenError;
+
+  /// The callback that determines how long to wait before retrying a request.
+  final Duration Function(int) _delay;
+
+  /// The callback that determines when to cancel a connection.
+  final Duration Function(int) _connectionTimeout;
+
+  /// The callback that determines when to cancel a request.
+  final Duration Function(int) _reponseTimeout;
+
+  /// The callback to call to indicate that a request is being retried.
+  final void Function(Uri, HttpClientResponse, int) _onRetry;
+
+  /// Creates a client wrapping [_inner] that retries HTTP requests.
+  RetryTimeoutClient(
+    this._inner, {
+    int retries = 3,
+    bool Function(HttpClientResponse) when = _defaultWhen,
+    bool Function(Object, StackTrace) whenError = _defaultWhenError,
+    Duration Function(int retryCount) delay = _defaultDelay,
+    Duration Function(int retryCount) connectionTimeout = _defaultTimeout,
+    Duration Function(int retryCount) responseTimeout = _defaultTimeout,
+    void Function(Uri, HttpClientResponse, int retryCount) onRetry,
+  })  : _retries = retries,
+        _when = when,
+        _whenError = whenError,
+        _delay = delay,
+        _connectionTimeout = connectionTimeout,
+        _reponseTimeout = responseTimeout,
+        _onRetry = onRetry {
+    RangeError.checkNotNegative(_retries, 'retries');
+  }
+
+  Future<HttpClientResponse> headUrl(Uri url) {
+    return _retry(url, _inner.headUrl);
+  }
+
+  Future<HttpClientResponse> getUrl(Uri url) {
+    return _retry(url, _inner.getUrl);
+  }
+
+  Future<HttpClientResponse> _retry(
+      Uri url, Future<HttpClientRequest> Function(Uri) method) async {
+    var i = 0;
+    for (;;) {
+      HttpClientResponse response;
+      try {
+        _inner.connectionTimeout = _connectionTimeout(i);
+        var request = await method(url).timeout(
+          _reponseTimeout(i),
+          onTimeout: () =>
+              throw TimeoutException('$url, retry:$i', _reponseTimeout(i)),
+        );
+        response = await request.close();
+      } catch (error, stackTrace) {
+        if (i == _retries || !_whenError(error, stackTrace)) rethrow;
+      }
+
+      if (response != null) {
+        if (i == _retries || !_when(response)) return response;
+
+        // Make sure the response stream is listened to so that we don't leave
+        // dangling connections.
+        unawaited(response.listen((_) {}).cancel().catchError((_) {}));
+      }
+
+      await Future.delayed(_delay(i));
+      _onRetry?.call(url, response, i);
+      i++;
+    }
+  }
+
+  void close({bool force = false}) => _inner.close(force: force);
+}
+
+bool _defaultWhen(HttpClientResponse response) => response.statusCode == 503;
+
+bool _defaultWhenError(Object error, StackTrace stackTrace) =>
+    error is OSError ||
+    error is HttpException ||
+    error is SocketException ||
+    error is TimeoutException;
+
+Duration _defaultDelay(int retryCount) =>
+    const Duration(milliseconds: 500) * pow(1.5, retryCount);
+
+Duration _defaultTimeout(int retryCount) =>
+    const Duration(milliseconds: 5000) * pow(1.5, retryCount);
diff --git a/pkg/dev_compiler/pubspec.yaml b/pkg/dev_compiler/pubspec.yaml
index b0f3974..89ca585 100644
--- a/pkg/dev_compiler/pubspec.yaml
+++ b/pkg/dev_compiler/pubspec.yaml
@@ -11,6 +11,7 @@
   _js_interop_checks:
     path: ../_js_interop_checks
   args: any
+  async: any
   bazel_worker: any
   build_integration:
     path: ../build_integration
@@ -22,10 +23,9 @@
   meta:
     path: ../meta
   path: any
+  pedantic: ^1.11.0
   source_maps: any
   source_span: any
-  vm:
-    path: ../vm
 
 dev_dependencies:
   browser_launcher: ^0.1.9
@@ -38,7 +38,6 @@
   modular_test:
     path: ../modular_test
   package_config: any
-  pedantic: ^1.11.0
   sourcemap_testing:
     path: ../sourcemap_testing
   stack_trace: any
@@ -47,5 +46,7 @@
   test: any
   testing:
     path: ../testing
+  vm:
+    path: ../vm
   webkit_inspection_protocol: ^0.7.4
 
diff --git a/pkg/dev_compiler/test/expression_compiler/asset_file_system_test.dart b/pkg/dev_compiler/test/expression_compiler/asset_file_system_test.dart
index a3483ed..a11668a 100644
--- a/pkg/dev_compiler/test/expression_compiler/asset_file_system_test.dart
+++ b/pkg/dev_compiler/test/expression_compiler/asset_file_system_test.dart
@@ -9,7 +9,7 @@
 import 'dart:io' show HttpServer;
 
 import 'package:browser_launcher/browser_launcher.dart';
-import 'package:dev_compiler/src/kernel/expression_compiler_worker.dart';
+import 'package:dev_compiler/src/kernel/asset_file_system.dart';
 import 'package:front_end/src/api_prototype/file_system.dart';
 import 'package:front_end/src/api_prototype/standard_file_system.dart';
 import 'package:http_multi_server/http_multi_server.dart';
@@ -17,16 +17,18 @@
 import 'package:shelf/shelf_io.dart';
 import 'package:test/test.dart';
 
-const _existingFile = 'http://localhost/existingFile';
-const _nonExistingFile = 'http://localhost/nonExistingFile';
+const _existingFile = 'existingFile';
+const _nonExistingFile = 'nonExistingFile';
 
 const _smallFileContents = 'Hello world!';
+List<int> _smallFileBytes = utf8.encode(_smallFileContents);
 
 String _largeFileContents() =>
-    List.filled(10000, _smallFileContents).join('/n');
+    List.filled(10000, _smallFileContents).join('\n');
+List<int> _largeFileBytes() => utf8.encode(_largeFileContents());
 
 FutureOr<Response> handler(Request request) {
-  final uri = request.requestedUri.queryParameters['uri'];
+  final uri = request.requestedUri;
   final headers = {
     'content-length': '${utf8.encode(_smallFileContents).length}',
     ...request.headers,
@@ -34,21 +36,21 @@
 
   if (request.method == 'HEAD') {
     // 'exists'
-    return uri == _existingFile
+    return uri.pathSegments.last == _existingFile
         ? Response.ok(null, headers: headers)
-        : Response.notFound(uri);
+        : Response.notFound(uri.toString());
   }
   if (request.method == 'GET') {
     // 'readAsBytes'
-    return uri == _existingFile
+    return uri.pathSegments.last == _existingFile
         ? Response.ok(_smallFileContents, headers: headers)
-        : Response.notFound(uri);
+        : Response.notFound(uri.toString());
   }
   return Response.internalServerError();
 }
 
 FutureOr<Response> noisyHandler(Request request) {
-  final uri = request.requestedUri.queryParameters['uri'];
+  final uri = request.requestedUri;
   final contents = _largeFileContents();
   final headers = {
     'content-length': '${utf8.encode(contents).length}',
@@ -57,9 +59,9 @@
 
   if (request.method == 'HEAD' || request.method == 'GET') {
     // 'exists' or 'readAsBytes'
-    return uri == _existingFile
+    return uri.pathSegments.last == _existingFile
         ? Response.ok(contents, headers: headers)
-        : Response.notFound(uri);
+        : Response.notFound(uri.toString());
   }
   return Response.internalServerError();
 }
@@ -93,16 +95,37 @@
       expect(await entity.exists(), false);
     });
 
-    test('can read existing file', () async {
+    test('can read existing file using readAsBytes', () async {
+      var entity = fileSystem.entityForUri(Uri.parse(_existingFile));
+      expect(await entity.readAsBytes(), _smallFileBytes);
+    });
+
+    test('can read and decode existing file using readAsBytes', () async {
       var entity = fileSystem.entityForUri(Uri.parse(_existingFile));
       expect(utf8.decode(await entity.readAsBytes()), _smallFileContents);
     });
 
+    test('can read existing file using readAsString', () async {
+      var entity = fileSystem.entityForUri(Uri.parse(_existingFile));
+      expect(await entity.readAsString(), _smallFileContents);
+    });
+
     test('cannot read non-existing file', () async {
       var entity = fileSystem.entityForUri(Uri.parse(_nonExistingFile));
       await expectLater(
           entity.readAsBytes(), throwsA(isA<FileSystemException>()));
     });
+
+    test('can read a lot of files concurrently', () async {
+      var entity = fileSystem.entityForUri(Uri.parse(_existingFile));
+      var futures = [
+        for (var i = 0; i < 512; i++) entity.readAsBytes(),
+      ];
+      var results = await Future.wait(futures);
+      for (var result in results) {
+        expect(utf8.decode(result), _smallFileContents);
+      }
+    }, timeout: const Timeout.factor(2));
   });
 
   group('AssetFileSystem with a noisy server', () {
@@ -131,15 +154,57 @@
       expect(await entity.exists(), false);
     });
 
-    test('can read existing file', () async {
+    test('can read existing file using readAsBytes', () async {
+      var entity = fileSystem.entityForUri(Uri.parse(_existingFile));
+      expect(await entity.readAsBytes(), _largeFileBytes());
+    });
+
+    test('can read and decode existing file using readAsBytes', () async {
       var entity = fileSystem.entityForUri(Uri.parse(_existingFile));
       expect(utf8.decode(await entity.readAsBytes()), _largeFileContents());
     });
 
+    test('can read existing file using readAsString', () async {
+      var entity = fileSystem.entityForUri(Uri.parse(_existingFile));
+      expect(await entity.readAsString(), _largeFileContents());
+    });
+
     test('cannot read non-existing file', () async {
       var entity = fileSystem.entityForUri(Uri.parse(_nonExistingFile));
       await expectLater(
           entity.readAsBytes(), throwsA(isA<FileSystemException>()));
     });
+
+    test('readAsString is faster than decoding result of readAsBytes',
+        () async {
+      var entity = fileSystem.entityForUri(Uri.parse(_existingFile));
+
+      var elapsedReadAsString = () async {
+        var stopwatch = Stopwatch()..start();
+        await expectLater(entity.readAsString(), isNotNull);
+        return stopwatch.elapsedMilliseconds;
+      };
+
+      var elapsedReadAsBytesAndDecode = () async {
+        var stopwatch = Stopwatch()..start();
+        await expectLater(utf8.decode(await entity.readAsBytes()), isNotNull);
+        return stopwatch.elapsedMilliseconds;
+      };
+
+      await expectLater(await elapsedReadAsString(),
+          lessThan(await elapsedReadAsBytesAndDecode()));
+    });
+
+    test('can read a lot of files concurrently', () async {
+      var entity = fileSystem.entityForUri(Uri.parse(_existingFile));
+      var futures = [
+        for (var i = 0; i < 512; i++) entity.readAsBytes(),
+      ];
+      var results = await Future.wait(futures);
+      var fileContents = _largeFileContents();
+      for (var result in results) {
+        expect(utf8.decode(result), fileContents);
+      }
+    }, timeout: const Timeout.factor(2));
   });
 }
diff --git a/pkg/dev_compiler/test/expression_compiler/expression_compiler_worker_test.dart b/pkg/dev_compiler/test/expression_compiler/expression_compiler_worker_test.dart
index 7141bb4..9a06c77 100644
--- a/pkg/dev_compiler/test/expression_compiler/expression_compiler_worker_test.dart
+++ b/pkg/dev_compiler/test/expression_compiler/expression_compiler_worker_test.dart
@@ -6,20 +6,510 @@
 
 import 'dart:async';
 import 'dart:convert';
-import 'dart:io' show Directory, File, Platform, Process, stderr, stdout;
+import 'dart:io'
+    show Directory, File, HttpServer, Platform, Process, stderr, stdout;
 import 'dart:isolate';
 
+import 'package:browser_launcher/browser_launcher.dart';
 import 'package:build_integration/file_system/multi_root.dart';
+import 'package:dev_compiler/src/kernel/asset_file_system.dart';
 import 'package:dev_compiler/src/kernel/expression_compiler_worker.dart';
+import 'package:front_end/src/api_prototype/file_system.dart';
 import 'package:front_end/src/api_prototype/standard_file_system.dart';
 import 'package:front_end/src/compute_platform_binaries_location.dart';
+import 'package:http_multi_server/http_multi_server.dart';
 import 'package:path/path.dart' as p;
 import 'package:pedantic/pedantic.dart';
+import 'package:shelf/shelf.dart';
+import 'package:shelf/shelf_io.dart';
 import 'package:test/test.dart';
 
 /// Verbose mode for debugging
 bool get verbose => false;
 
+void main() async {
+  for (var moduleFormat in ['amd', 'ddc']) {
+    group('$moduleFormat module format -', () {
+      for (var soundNullSafety in [true, false]) {
+        group('${soundNullSafety ? "sound" : "unsound"} null safety -', () {
+          group('expression compiler worker on startup', () {
+            Directory tempDir;
+            ReceivePort receivePort;
+
+            setUp(() async {
+              tempDir = Directory.systemTemp.createTempSync('foo bar');
+              receivePort = ReceivePort();
+            });
+
+            tearDown(() async {
+              tempDir.deleteSync(recursive: true);
+              receivePort.close();
+            });
+
+            test('reports failure to consumer', () async {
+              expect(
+                  receivePort,
+                  emitsInOrder([
+                    equals(isA<SendPort>()),
+                    equals({
+                      'succeeded': false,
+                      'stackTrace': isNotNull,
+                      'exception': contains('Could not load SDK component'),
+                    }),
+                  ]));
+
+              try {
+                var badPath = 'file:///path/does/not/exist';
+                await ExpressionCompilerWorker.createAndStart(
+                  [
+                    '--libraries-file',
+                    badPath,
+                    '--dart-sdk-summary',
+                    badPath,
+                    '--module-format',
+                    moduleFormat,
+                    soundNullSafety
+                        ? '--sound-null-safety'
+                        : '--no-sound-null-safety',
+                    if (verbose) '--verbose',
+                  ],
+                  sendPort: receivePort.sendPort,
+                );
+              } catch (e) {
+                throwsA(contains('Could not load SDK component'));
+              }
+            });
+          });
+
+          group('reading assets using standard file system - ', () {
+            runExpressionCompilationTests(
+                StandardFileSystemTestDriver(soundNullSafety, moduleFormat));
+          });
+
+          group('reading assets using multiroot file system - ', () {
+            runExpressionCompilationTests(
+                MultiRootFileSystemTestDriver(soundNullSafety, moduleFormat));
+          });
+
+          group('reading assets using asset file system -', () {
+            runExpressionCompilationTests(
+                AssetFileSystemTestDriver(soundNullSafety, moduleFormat));
+          });
+        });
+      }
+    });
+  }
+}
+
+void runExpressionCompilationTests(TestDriver driver) {
+  group('expression compiler worker', () {
+    setUpAll(() async {
+      await driver.setUpAll();
+    });
+
+    tearDownAll(() async {
+      await driver.tearDownAll();
+    });
+
+    setUp(() async {
+      await driver.setUp();
+    });
+
+    tearDown(() async {
+      await driver.tearDown();
+    });
+
+    test('can compile expressions in sdk', () async {
+      driver.requestController.add({
+        'command': 'UpdateDeps',
+        'inputs': driver.inputs,
+      });
+
+      driver.requestController.add({
+        'command': 'CompileExpression',
+        'expression': 'other',
+        'line': 107,
+        'column': 1,
+        'jsModules': {},
+        'jsScope': {'other': 'other'},
+        'libraryUri': 'dart:collection',
+        'moduleName': 'dart_sdk',
+      });
+
+      expect(
+          driver.responseController.stream,
+          emitsInOrder([
+            equals({
+              'succeeded': true,
+            }),
+            equals({
+              'succeeded': true,
+              'errors': isEmpty,
+              'warnings': isEmpty,
+              'infos': isEmpty,
+              'compiledProcedure': contains('return other;'),
+            })
+          ]));
+    }, skip: 'Evaluating expressions in SDK is not supported yet');
+
+    test('can compile expressions in a library', () async {
+      driver.requestController.add({
+        'command': 'UpdateDeps',
+        'inputs': driver.inputs,
+      });
+
+      driver.requestController.add({
+        'command': 'CompileExpression',
+        'expression': 'formal',
+        'line': 5,
+        'column': 1,
+        'jsModules': {},
+        'jsScope': {'formal': 'formal'},
+        'libraryUri': driver.config.testModule.libraryUri,
+        'moduleName': driver.config.testModule.moduleName,
+      });
+
+      expect(
+          driver.responseController.stream,
+          emitsInOrder([
+            equals({
+              'succeeded': true,
+            }),
+            equals({
+              'succeeded': true,
+              'errors': isEmpty,
+              'warnings': isEmpty,
+              'infos': isEmpty,
+              'compiledProcedure': contains('return formal;'),
+            })
+          ]));
+    });
+
+    test('can compile expressions in main', () async {
+      driver.requestController.add({
+        'command': 'UpdateDeps',
+        'inputs': driver.inputs,
+      });
+
+      driver.requestController.add({
+        'command': 'CompileExpression',
+        'expression': 'count',
+        'line': 9,
+        'column': 1,
+        'jsModules': {},
+        'jsScope': {'count': 'count'},
+        'libraryUri': driver.config.mainModule.libraryUri,
+        'moduleName': driver.config.mainModule.moduleName,
+      });
+
+      expect(
+          driver.responseController.stream,
+          emitsInOrder([
+            equals({
+              'succeeded': true,
+            }),
+            equals({
+              'succeeded': true,
+              'errors': isEmpty,
+              'warnings': isEmpty,
+              'infos': isEmpty,
+              'compiledProcedure': contains('return count;'),
+            })
+          ]));
+    });
+
+    test('can compile expressions in main (extension method)', () async {
+      driver.requestController.add({
+        'command': 'UpdateDeps',
+        'inputs': driver.inputs,
+      });
+
+      driver.requestController.add({
+        'command': 'CompileExpression',
+        'expression': 'ret',
+        'line': 19,
+        'column': 1,
+        'jsModules': {},
+        'jsScope': {'ret': 'ret'},
+        'libraryUri': driver.config.mainModule.libraryUri,
+        'moduleName': driver.config.mainModule.moduleName,
+      });
+
+      expect(
+          driver.responseController.stream,
+          emitsInOrder([
+            equals({
+              'succeeded': true,
+            }),
+            equals({
+              'succeeded': true,
+              'errors': isEmpty,
+              'warnings': isEmpty,
+              'infos': isEmpty,
+              'compiledProcedure': contains('return ret;'),
+            })
+          ]));
+    });
+
+    test('can compile transitive expressions in main', () async {
+      driver.requestController.add({
+        'command': 'UpdateDeps',
+        'inputs': driver.inputs,
+      });
+
+      driver.requestController.add({
+        'command': 'CompileExpression',
+        'expression': 'B().c().getNumber()',
+        'line': 9,
+        'column': 1,
+        'jsModules': {},
+        'jsScope': {},
+        'libraryUri': driver.config.mainModule.libraryUri,
+        'moduleName': driver.config.mainModule.moduleName,
+      });
+
+      expect(
+          driver.responseController.stream,
+          emitsInOrder([
+            equals({
+              'succeeded': true,
+            }),
+            equals({
+              'succeeded': true,
+              'errors': isEmpty,
+              'warnings': isEmpty,
+              'infos': isEmpty,
+              'compiledProcedure':
+                  contains('new test_library.B.new().c().getNumber()'),
+            })
+          ]));
+    });
+
+    test('can compile series of expressions in various libraries', () async {
+      driver.requestController.add({
+        'command': 'UpdateDeps',
+        'inputs': driver.inputs,
+      });
+
+      driver.requestController.add({
+        'command': 'CompileExpression',
+        'expression': 'B().c().getNumber()',
+        'line': 8,
+        'column': 1,
+        'jsModules': {},
+        'jsScope': {},
+        'libraryUri': driver.config.mainModule.libraryUri,
+        'moduleName': driver.config.mainModule.moduleName,
+      });
+
+      driver.requestController.add({
+        'command': 'CompileExpression',
+        'expression': 'formal',
+        'line': 5,
+        'column': 1,
+        'jsModules': {},
+        'jsScope': {'formal': 'formal'},
+        'libraryUri': driver.config.testModule.libraryUri,
+        'moduleName': driver.config.testModule.moduleName,
+      });
+
+      driver.requestController.add({
+        'command': 'CompileExpression',
+        'expression': 'formal',
+        'line': 3,
+        'column': 1,
+        'jsModules': {},
+        'jsScope': {'formal': 'formal'},
+        'libraryUri': driver.config.testModule2.libraryUri,
+        'moduleName': driver.config.testModule2.moduleName,
+      });
+
+      driver.requestController.add({
+        'command': 'CompileExpression',
+        'expression': 'formal',
+        'line': 3,
+        'column': 1,
+        'jsModules': {},
+        'jsScope': {'formal': 'formal'},
+        'libraryUri': driver.config.testModule3.libraryUri,
+        'moduleName': driver.config.testModule3.moduleName,
+      });
+
+      driver.requestController.add({
+        'command': 'CompileExpression',
+        'expression': 'B().printNumber()',
+        'line': 9,
+        'column': 1,
+        'jsModules': {},
+        'jsScope': {},
+        'libraryUri': driver.config.mainModule.libraryUri,
+        'moduleName': driver.config.mainModule.moduleName,
+      });
+
+      expect(
+          driver.responseController.stream,
+          emitsInOrder([
+            equals({
+              'succeeded': true,
+            }),
+            equals({
+              'succeeded': true,
+              'errors': isEmpty,
+              'warnings': isEmpty,
+              'infos': isEmpty,
+              'compiledProcedure':
+                  contains('new test_library.B.new().c().getNumber()'),
+            }),
+            equals({
+              'succeeded': true,
+              'errors': isEmpty,
+              'warnings': isEmpty,
+              'infos': isEmpty,
+              'compiledProcedure': contains('return formal;'),
+            }),
+            equals({
+              'succeeded': true,
+              'errors': isEmpty,
+              'warnings': isEmpty,
+              'infos': isEmpty,
+              'compiledProcedure': contains('return formal;'),
+            }),
+            equals({
+              'succeeded': true,
+              'errors': isEmpty,
+              'warnings': isEmpty,
+              'infos': isEmpty,
+              'compiledProcedure': contains('return formal;'),
+            }),
+            equals({
+              'succeeded': true,
+              'errors': isEmpty,
+              'warnings': isEmpty,
+              'infos': isEmpty,
+              'compiledProcedure':
+                  contains('test_library.B.new().printNumber()'),
+            })
+          ]));
+    });
+
+    test('can compile after dependency update', () async {
+      driver.requestController.add({
+        'command': 'UpdateDeps',
+        'inputs': driver.inputs,
+      });
+
+      driver.requestController.add({
+        'command': 'CompileExpression',
+        'expression': 'B().c().getNumber()',
+        'line': 8,
+        'column': 1,
+        'jsModules': {},
+        'jsScope': {},
+        'libraryUri': driver.config.mainModule.libraryUri,
+        'moduleName': driver.config.mainModule.moduleName,
+      });
+
+      driver.requestController.add({
+        'command': 'CompileExpression',
+        'expression': 'formal',
+        'line': 5,
+        'column': 1,
+        'jsModules': {},
+        'jsScope': {'formal': 'formal'},
+        'libraryUri': driver.config.testModule.libraryUri,
+        'moduleName': driver.config.testModule.moduleName,
+      });
+
+      driver.requestController.add({
+        'command': 'CompileExpression',
+        'expression': 'B().printNumber()',
+        'line': 9,
+        'column': 1,
+        'jsModules': {},
+        'jsScope': {},
+        'libraryUri': driver.config.mainModule.libraryUri,
+        'moduleName': driver.config.mainModule.moduleName,
+      });
+
+      driver.requestController.add({
+        'command': 'UpdateDeps',
+        'inputs': driver.inputs,
+      });
+
+      driver.requestController.add({
+        'command': 'CompileExpression',
+        'expression': 'B().c().getNumber()',
+        'line': 8,
+        'column': 1,
+        'jsModules': {},
+        'jsScope': {},
+        'libraryUri': driver.config.mainModule.libraryUri,
+        'moduleName': driver.config.mainModule.moduleName,
+      });
+
+      driver.requestController.add({
+        'command': 'CompileExpression',
+        'expression': 'formal',
+        'line': 3,
+        'column': 1,
+        'jsModules': {},
+        'jsScope': {'formal': 'formal'},
+        'libraryUri': driver.config.testModule3.libraryUri,
+        'moduleName': driver.config.testModule3.moduleName,
+      });
+
+      expect(
+          driver.responseController.stream,
+          emitsInOrder([
+            equals({
+              'succeeded': true,
+            }),
+            equals({
+              'succeeded': true,
+              'errors': isEmpty,
+              'warnings': isEmpty,
+              'infos': isEmpty,
+              'compiledProcedure':
+                  contains('new test_library.B.new().c().getNumber()'),
+            }),
+            equals({
+              'succeeded': true,
+              'errors': isEmpty,
+              'warnings': isEmpty,
+              'infos': isEmpty,
+              'compiledProcedure': contains('return formal;'),
+            }),
+            equals({
+              'succeeded': true,
+              'errors': isEmpty,
+              'warnings': isEmpty,
+              'infos': isEmpty,
+              'compiledProcedure':
+                  contains('test_library.B.new().printNumber()'),
+            }),
+            equals({
+              'succeeded': true,
+            }),
+            equals({
+              'succeeded': true,
+              'errors': isEmpty,
+              'warnings': isEmpty,
+              'infos': isEmpty,
+              'compiledProcedure':
+                  contains('new test_library.B.new().c().getNumber()'),
+            }),
+            equals({
+              'succeeded': true,
+              'errors': isEmpty,
+              'warnings': isEmpty,
+              'infos': isEmpty,
+              'compiledProcedure': contains('return formal;'),
+            }),
+          ]));
+    });
+  });
+}
+
 class ModuleConfiguration {
   final Uri root;
   final String outputDir;
@@ -38,9 +528,18 @@
       this.fullDillFileName,
       this.summaryDillFileName});
 
-  Uri get jsPath => root.resolve('$outputDir/$jsFileName');
-  Uri get fullDillPath => root.resolve('$outputDir/$fullDillFileName');
-  Uri get summaryDillPath => root.resolve('$outputDir/$summaryDillFileName');
+  Uri get jsUri => root.resolve('$outputDir/$jsFileName');
+  Uri get multiRootFullDillUri =>
+      Uri.parse('org-dartlang-app:///$outputDir/$fullDillFileName');
+  Uri get multiRootSummaryUri =>
+      Uri.parse('org-dartlang-app:///$outputDir/$summaryDillFileName');
+
+  Uri get relativeFullDillUri => Uri.parse('$outputDir/$fullDillFileName');
+  Uri get realtiveSummaryUri => Uri.parse('$outputDir/$summaryDillFileName');
+
+  String get fullDillPath => root.resolve('$outputDir/$fullDillFileName').path;
+  String get summaryDillPath =>
+      root.resolve('$outputDir/$summaryDillFileName').path;
 }
 
 class TestProjectConfiguration {
@@ -102,6 +601,75 @@
       : sdkRoot.resolve('ddc_sdk.dill');
   Uri get librariesPath => sdkRoot.resolve('lib/libraries.json');
 
+  List get inputUris => [
+        {
+          'path': '${mainModule.multiRootFullDillUri}',
+          'summaryPath': '${mainModule.multiRootSummaryUri}',
+          'moduleName': mainModule.moduleName
+        },
+        {
+          'path': '${testModule.multiRootFullDillUri}',
+          'summaryPath': '${testModule.multiRootSummaryUri}',
+          'moduleName': testModule.moduleName
+        },
+        {
+          'path': '${testModule2.multiRootFullDillUri}',
+          'summaryPath': '${testModule2.multiRootSummaryUri}',
+          'moduleName': testModule2.moduleName
+        },
+        {
+          'path': '${testModule3.multiRootFullDillUri}',
+          'summaryPath': '${testModule3.multiRootSummaryUri}',
+          'moduleName': testModule3.moduleName
+        },
+      ];
+
+  List get inputRelativeUris => [
+        {
+          'path': '${mainModule.multiRootFullDillUri}',
+          'summaryPath': '${mainModule.multiRootSummaryUri}',
+          'moduleName': mainModule.moduleName
+        },
+        {
+          'path': '${testModule.multiRootFullDillUri}',
+          'summaryPath': '${testModule.multiRootSummaryUri}',
+          'moduleName': testModule.moduleName
+        },
+        {
+          'path': '${testModule2.multiRootFullDillUri}',
+          'summaryPath': '${testModule2.multiRootSummaryUri}',
+          'moduleName': testModule2.moduleName
+        },
+        {
+          'path': '${testModule3.multiRootFullDillUri}',
+          'summaryPath': '${testModule3.multiRootSummaryUri}',
+          'moduleName': testModule3.moduleName
+        },
+      ];
+
+  List get inputPaths => [
+        {
+          'path': mainModule.fullDillPath,
+          'summaryPath': mainModule.summaryDillPath,
+          'moduleName': mainModule.moduleName
+        },
+        {
+          'path': testModule.fullDillPath,
+          'summaryPath': testModule.summaryDillPath,
+          'moduleName': testModule.moduleName
+        },
+        {
+          'path': testModule2.fullDillPath,
+          'summaryPath': testModule2.summaryDillPath,
+          'moduleName': testModule2.moduleName
+        },
+        {
+          'path': testModule3.fullDillPath,
+          'summaryPath': testModule3.summaryDillPath,
+          'moduleName': testModule3.moduleName
+        },
+      ];
+
   void createTestProject() {
     var pubspec = root.resolve('pubspec.yaml');
     File.fromUri(pubspec)
@@ -211,539 +779,171 @@
   }
 }
 
-void main() async {
-  for (var moduleFormat in ['amd', 'ddc']) {
-    group('$moduleFormat module format -', () {
-      for (var soundNullSafety in [true, false]) {
-        group('${soundNullSafety ? "sound" : "unsound"} null safety -', () {
-          group('expression compiler worker on startup', () {
-            Directory tempDir;
-            ReceivePort receivePort;
+abstract class TestDriver {
+  final bool soundNullSafety;
+  final String moduleFormat;
 
-            setUp(() async {
-              tempDir = Directory.systemTemp.createTempSync('foo bar');
-              receivePort = ReceivePort();
-            });
+  FileSystem fileSystem;
+  FileSystem assetFileSystem;
 
-            tearDown(() async {
-              tempDir.deleteSync(recursive: true);
-              receivePort.close();
-            });
+  Directory tempDir;
+  TestProjectConfiguration config;
+  List inputs;
 
-            test('reports failure to consumer', () async {
-              expect(
-                  receivePort,
-                  emitsInOrder([
-                    equals(isA<SendPort>()),
-                    equals({
-                      'succeeded': false,
-                      'stackTrace': isNotNull,
-                      'exception': contains('Could not load SDK component'),
-                    }),
-                  ]));
+  StreamController<Map<String, dynamic>> requestController;
+  StreamController<Map<String, dynamic>> responseController;
+  ExpressionCompilerWorker worker;
+  Future<void> workerDone;
 
-              try {
-                var badPath = 'file:///path/does/not/exist';
-                await ExpressionCompilerWorker.createAndStart(
-                  [
-                    '--libraries-file',
-                    badPath,
-                    '--dart-sdk-summary',
-                    badPath,
-                    '--module-format',
-                    moduleFormat,
-                    soundNullSafety
-                        ? '--sound-null-safety'
-                        : '--no-sound-null-safety',
-                    if (verbose) '--verbose',
-                  ],
-                  sendPort: receivePort.sendPort,
-                );
-              } catch (e) {
-                throwsA(contains('Could not load SDK component'));
-              }
-            });
-          });
+  TestDriver(this.soundNullSafety, this.moduleFormat);
 
-          for (var summarySupport in [true, false]) {
-            group('${summarySupport ? "" : "no "}debugger summary support -',
-                () {
-              group('expression compiler worker', () {
-                ExpressionCompilerWorker worker;
-                Future workerDone;
-                StreamController<Map<String, dynamic>> requestController;
-                StreamController<Map<String, dynamic>> responseController;
-                Directory tempDir;
-                TestProjectConfiguration config;
-                List inputs;
+  /// Initialize file systems, inputs, and start servers if needed.
+  Future<void> start();
 
-                setUpAll(() async {
-                  tempDir = Directory.systemTemp.createTempSync('foo bar');
-                  config = TestProjectConfiguration(
-                      tempDir, soundNullSafety, moduleFormat);
+  Future<void> stop() => workerDone;
 
-                  // simulate webdev
-                  config.createTestProject();
-                  var kernelGenerator = DDCKernelGenerator(config);
-                  await kernelGenerator.generate();
+  Future<void> setUpAll() async {
+    tempDir = Directory.systemTemp.createTempSync('foo bar');
+    config = TestProjectConfiguration(tempDir, soundNullSafety, moduleFormat);
 
-                  inputs = [
-                    {
-                      'path': config.mainModule.fullDillPath.path,
-                      if (summarySupport)
-                        'summaryPath': config.mainModule.summaryDillPath.path,
-                      'moduleName': config.mainModule.moduleName
-                    },
-                    {
-                      'path': config.testModule.fullDillPath.path,
-                      if (summarySupport)
-                        'summaryPath': config.testModule.summaryDillPath.path,
-                      'moduleName': config.testModule.moduleName
-                    },
-                    {
-                      'path': config.testModule2.fullDillPath.path,
-                      if (summarySupport)
-                        'summaryPath': config.testModule2.summaryDillPath.path,
-                      'moduleName': config.testModule2.moduleName
-                    },
-                    {
-                      'path': config.testModule3.fullDillPath.path,
-                      if (summarySupport)
-                        'summaryPath': config.testModule3.summaryDillPath.path,
-                      'moduleName': config.testModule3.moduleName
-                    },
-                  ];
-                });
+    await start();
 
-                tearDownAll(() async {
-                  tempDir.deleteSync(recursive: true);
-                });
+    // Build the project.
+    config.createTestProject();
+    var kernelGenerator = DDCKernelGenerator(config);
+    await kernelGenerator.generate();
+  }
 
-                setUp(() async {
-                  var fileSystem = MultiRootFileSystem('org-dartlang-app',
-                      [tempDir.uri], StandardFileSystem.instance);
+  Future<void> tearDownAll() async {
+    await stop();
+    tempDir.deleteSync(recursive: true);
+  }
 
-                  requestController = StreamController<Map<String, dynamic>>();
-                  responseController = StreamController<Map<String, dynamic>>();
-                  worker = await ExpressionCompilerWorker.create(
-                    librariesSpecificationUri: config.librariesPath,
-                    // We should be able to load everything from dill and not
-                    // require source parsing. Webdev and google3 integration
-                    // currently rely on that. Make the test fail on source
-                    // reading by not providing a packages file.
-                    packagesFile: null,
-                    sdkSummary: config.sdkSummaryPath,
-                    fileSystem: fileSystem,
-                    requestStream: requestController.stream,
-                    sendResponse: responseController.add,
-                    soundNullSafety: soundNullSafety,
-                    verbose: verbose,
-                  );
-                  workerDone = worker.start();
-                });
+  Future<void> setUp() async {
+    requestController = StreamController<Map<String, dynamic>>();
+    responseController = StreamController<Map<String, dynamic>>();
+    worker = await ExpressionCompilerWorker.create(
+      librariesSpecificationUri: config.librariesPath,
+      // We should be able to load everything from dill and not
+      // require source parsing. Webdev and google3 integration
+      // currently rely on that. Make the test fail on source
+      // reading by not providing a packages file.
+      packagesFile: null,
+      sdkSummary: config.sdkSummaryPath,
+      fileSystem: assetFileSystem,
+      requestStream: requestController.stream,
+      sendResponse: responseController.add,
+      soundNullSafety: soundNullSafety,
+      verbose: verbose,
+    );
+    workerDone = worker.start();
+  }
 
-                tearDown(() async {
-                  unawaited(requestController.close());
-                  await workerDone;
-                  unawaited(responseController.close());
-                });
+  Future<void> tearDown() async {
+    unawaited(requestController.close());
+    await workerDone;
+    unawaited(responseController.close());
+  }
+}
 
-                test('can compile expressions in sdk', () async {
-                  requestController.add({
-                    'command': 'UpdateDeps',
-                    'inputs': inputs,
-                  });
+class StandardFileSystemTestDriver extends TestDriver {
+  StandardFileSystemTestDriver(bool soundNullSafety, String moduleFormat)
+      : super(soundNullSafety, moduleFormat);
 
-                  requestController.add({
-                    'command': 'CompileExpression',
-                    'expression': 'other',
-                    'line': 107,
-                    'column': 1,
-                    'jsModules': {},
-                    'jsScope': {'other': 'other'},
-                    'libraryUri': 'dart:collection',
-                    'moduleName': 'dart_sdk',
-                  });
+  @override
+  Future<void> start() async {
+    inputs = config.inputPaths;
+    fileSystem = MultiRootFileSystem(
+        'org-dartlang-app', [tempDir.uri], StandardFileSystem.instance);
+    assetFileSystem = StandardFileSystem.instance;
+  }
+}
 
-                  expect(
-                      responseController.stream,
-                      emitsInOrder([
-                        equals({
-                          'succeeded': true,
-                        }),
-                        equals({
-                          'succeeded': true,
-                          'errors': isEmpty,
-                          'warnings': isEmpty,
-                          'infos': isEmpty,
-                          'compiledProcedure': contains('return other;'),
-                        })
-                      ]));
-                }, skip: 'Evaluating expressions in SDK is not supported yet');
+class MultiRootFileSystemTestDriver extends TestDriver {
+  MultiRootFileSystemTestDriver(bool soundNullSafety, String moduleFormat)
+      : super(soundNullSafety, moduleFormat);
 
-                test('can compile expressions in a library', () async {
-                  requestController.add({
-                    'command': 'UpdateDeps',
-                    'inputs': inputs,
-                  });
+  @override
+  Future<void> start() async {
+    inputs = config.inputUris;
+    fileSystem = MultiRootFileSystem(
+        'org-dartlang-app', [tempDir.uri], StandardFileSystem.instance);
+    assetFileSystem = fileSystem;
+  }
+}
 
-                  requestController.add({
-                    'command': 'CompileExpression',
-                    'expression': 'formal',
-                    'line': 5,
-                    'column': 1,
-                    'jsModules': {},
-                    'jsScope': {'formal': 'formal'},
-                    'libraryUri': config.testModule.libraryUri,
-                    'moduleName': config.testModule.moduleName,
-                  });
+class AssetFileSystemTestDriver extends TestDriver {
+  TestAssetServer server;
+  int port;
 
-                  expect(
-                      responseController.stream,
-                      emitsInOrder([
-                        equals({
-                          'succeeded': true,
-                        }),
-                        equals({
-                          'succeeded': true,
-                          'errors': isEmpty,
-                          'warnings': isEmpty,
-                          'infos': isEmpty,
-                          'compiledProcedure': contains('return formal;'),
-                        })
-                      ]));
-                });
+  AssetFileSystemTestDriver(bool soundNullSafety, String moduleFormat)
+      : super(soundNullSafety, moduleFormat);
 
-                test('can compile expressions in main', () async {
-                  requestController.add({
-                    'command': 'UpdateDeps',
-                    'inputs': inputs,
-                  });
+  @override
+  Future<void> start() async {
+    inputs = config.inputRelativeUris;
+    fileSystem = MultiRootFileSystem(
+        'org-dartlang-app', [tempDir.uri], StandardFileSystem.instance);
+    port = await findUnusedPort();
+    server = TestAssetServer(fileSystem);
+    assetFileSystem = AssetFileSystem(fileSystem, 'localhost', '$port');
+    await server.start('localhost', port);
+  }
 
-                  requestController.add({
-                    'command': 'CompileExpression',
-                    'expression': 'count',
-                    'line': 9,
-                    'column': 1,
-                    'jsModules': {},
-                    'jsScope': {'count': 'count'},
-                    'libraryUri': config.mainModule.libraryUri,
-                    'moduleName': config.mainModule.moduleName,
-                  });
+  @override
+  Future<void> stop() async {
+    server.stop();
+    await super.stop();
+  }
+}
 
-                  expect(
-                      responseController.stream,
-                      emitsInOrder([
-                        equals({
-                          'succeeded': true,
-                        }),
-                        equals({
-                          'succeeded': true,
-                          'errors': isEmpty,
-                          'warnings': isEmpty,
-                          'infos': isEmpty,
-                          'compiledProcedure': contains('return count;'),
-                        })
-                      ]));
-                });
+class TestAssetServer {
+  FileSystem fileSystem;
+  HttpServer server;
 
-                test('can compile expressions in main (extension method)',
-                    () async {
-                  requestController.add({
-                    'command': 'UpdateDeps',
-                    'inputs': inputs,
-                  });
+  TestAssetServer(this.fileSystem);
 
-                  requestController.add({
-                    'command': 'CompileExpression',
-                    'expression': 'ret',
-                    'line': 19,
-                    'column': 1,
-                    'jsModules': {},
-                    'jsScope': {'ret': 'ret'},
-                    'libraryUri': config.mainModule.libraryUri,
-                    'moduleName': config.mainModule.moduleName,
-                  });
+  FutureOr<Response> handler(Request request) async {
+    var requested = request.requestedUri.path;
+    final uri = Uri.parse('org-dartlang-app:/$requested');
 
-                  expect(
-                      responseController.stream,
-                      emitsInOrder([
-                        equals({
-                          'succeeded': true,
-                        }),
-                        equals({
-                          'succeeded': true,
-                          'errors': isEmpty,
-                          'warnings': isEmpty,
-                          'infos': isEmpty,
-                          'compiledProcedure': contains('return ret;'),
-                        })
-                      ]));
-                });
+    assert(requested.startsWith('/'));
+    final path = requested.substring(1);
 
-                test('can compile transitive expressions in main', () async {
-                  requestController.add({
-                    'command': 'UpdateDeps',
-                    'inputs': inputs,
-                  });
+    try {
+      var entity = fileSystem.entityForUri(uri);
+      if (await entity.existsAsyncIfPossible()) {
+        if (request.method == 'HEAD') {
+          var headers = {
+            'content-length': null,
+            ...request.headers,
+          };
+          return Response.ok(null, headers: headers);
+        }
 
-                  requestController.add({
-                    'command': 'CompileExpression',
-                    'expression': 'B().c().getNumber()',
-                    'line': 9,
-                    'column': 1,
-                    'jsModules': {},
-                    'jsScope': {},
-                    'libraryUri': config.mainModule.libraryUri,
-                    'moduleName': config.mainModule.moduleName,
-                  });
-
-                  expect(
-                      responseController.stream,
-                      emitsInOrder([
-                        equals({
-                          'succeeded': true,
-                        }),
-                        equals({
-                          'succeeded': true,
-                          'errors': isEmpty,
-                          'warnings': isEmpty,
-                          'infos': isEmpty,
-                          'compiledProcedure': contains(
-                              'new test_library.B.new().c().getNumber()'),
-                        })
-                      ]));
-                });
-
-                test('can compile series of expressions in various libraries',
-                    () async {
-                  requestController.add({
-                    'command': 'UpdateDeps',
-                    'inputs': inputs,
-                  });
-
-                  requestController.add({
-                    'command': 'CompileExpression',
-                    'expression': 'B().c().getNumber()',
-                    'line': 8,
-                    'column': 1,
-                    'jsModules': {},
-                    'jsScope': {},
-                    'libraryUri': config.mainModule.libraryUri,
-                    'moduleName': config.mainModule.moduleName,
-                  });
-
-                  requestController.add({
-                    'command': 'CompileExpression',
-                    'expression': 'formal',
-                    'line': 5,
-                    'column': 1,
-                    'jsModules': {},
-                    'jsScope': {'formal': 'formal'},
-                    'libraryUri': config.testModule.libraryUri,
-                    'moduleName': config.testModule.moduleName,
-                  });
-
-                  requestController.add({
-                    'command': 'CompileExpression',
-                    'expression': 'formal',
-                    'line': 3,
-                    'column': 1,
-                    'jsModules': {},
-                    'jsScope': {'formal': 'formal'},
-                    'libraryUri': config.testModule2.libraryUri,
-                    'moduleName': config.testModule2.moduleName,
-                  });
-
-                  requestController.add({
-                    'command': 'CompileExpression',
-                    'expression': 'formal',
-                    'line': 3,
-                    'column': 1,
-                    'jsModules': {},
-                    'jsScope': {'formal': 'formal'},
-                    'libraryUri': config.testModule3.libraryUri,
-                    'moduleName': config.testModule3.moduleName,
-                  });
-
-                  requestController.add({
-                    'command': 'CompileExpression',
-                    'expression': 'B().printNumber()',
-                    'line': 9,
-                    'column': 1,
-                    'jsModules': {},
-                    'jsScope': {},
-                    'libraryUri': config.mainModule.libraryUri,
-                    'moduleName': config.mainModule.moduleName,
-                  });
-
-                  expect(
-                      responseController.stream,
-                      emitsInOrder([
-                        equals({
-                          'succeeded': true,
-                        }),
-                        equals({
-                          'succeeded': true,
-                          'errors': isEmpty,
-                          'warnings': isEmpty,
-                          'infos': isEmpty,
-                          'compiledProcedure': contains(
-                              'new test_library.B.new().c().getNumber()'),
-                        }),
-                        equals({
-                          'succeeded': true,
-                          'errors': isEmpty,
-                          'warnings': isEmpty,
-                          'infos': isEmpty,
-                          'compiledProcedure': contains('return formal;'),
-                        }),
-                        equals({
-                          'succeeded': true,
-                          'errors': isEmpty,
-                          'warnings': isEmpty,
-                          'infos': isEmpty,
-                          'compiledProcedure': contains('return formal;'),
-                        }),
-                        equals({
-                          'succeeded': true,
-                          'errors': isEmpty,
-                          'warnings': isEmpty,
-                          'infos': isEmpty,
-                          'compiledProcedure': contains('return formal;'),
-                        }),
-                        equals({
-                          'succeeded': true,
-                          'errors': isEmpty,
-                          'warnings': isEmpty,
-                          'infos': isEmpty,
-                          'compiledProcedure':
-                              contains('test_library.B.new().printNumber()'),
-                        })
-                      ]));
-                });
-
-                test('can compile after dependency update', () async {
-                  requestController.add({
-                    'command': 'UpdateDeps',
-                    'inputs': inputs,
-                  });
-
-                  requestController.add({
-                    'command': 'CompileExpression',
-                    'expression': 'B().c().getNumber()',
-                    'line': 8,
-                    'column': 1,
-                    'jsModules': {},
-                    'jsScope': {},
-                    'libraryUri': config.mainModule.libraryUri,
-                    'moduleName': config.mainModule.moduleName,
-                  });
-
-                  requestController.add({
-                    'command': 'CompileExpression',
-                    'expression': 'formal',
-                    'line': 5,
-                    'column': 1,
-                    'jsModules': {},
-                    'jsScope': {'formal': 'formal'},
-                    'libraryUri': config.testModule.libraryUri,
-                    'moduleName': config.testModule.moduleName,
-                  });
-
-                  requestController.add({
-                    'command': 'CompileExpression',
-                    'expression': 'B().printNumber()',
-                    'line': 9,
-                    'column': 1,
-                    'jsModules': {},
-                    'jsScope': {},
-                    'libraryUri': config.mainModule.libraryUri,
-                    'moduleName': config.mainModule.moduleName,
-                  });
-
-                  requestController.add({
-                    'command': 'UpdateDeps',
-                    'inputs': inputs,
-                  });
-
-                  requestController.add({
-                    'command': 'CompileExpression',
-                    'expression': 'B().c().getNumber()',
-                    'line': 8,
-                    'column': 1,
-                    'jsModules': {},
-                    'jsScope': {},
-                    'libraryUri': config.mainModule.libraryUri,
-                    'moduleName': config.mainModule.moduleName,
-                  });
-
-                  requestController.add({
-                    'command': 'CompileExpression',
-                    'expression': 'formal',
-                    'line': 3,
-                    'column': 1,
-                    'jsModules': {},
-                    'jsScope': {'formal': 'formal'},
-                    'libraryUri': config.testModule3.libraryUri,
-                    'moduleName': config.testModule3.moduleName,
-                  });
-
-                  expect(
-                      responseController.stream,
-                      emitsInOrder([
-                        equals({
-                          'succeeded': true,
-                        }),
-                        equals({
-                          'succeeded': true,
-                          'errors': isEmpty,
-                          'warnings': isEmpty,
-                          'infos': isEmpty,
-                          'compiledProcedure': contains(
-                              'new test_library.B.new().c().getNumber()'),
-                        }),
-                        equals({
-                          'succeeded': true,
-                          'errors': isEmpty,
-                          'warnings': isEmpty,
-                          'infos': isEmpty,
-                          'compiledProcedure': contains('return formal;'),
-                        }),
-                        equals({
-                          'succeeded': true,
-                          'errors': isEmpty,
-                          'warnings': isEmpty,
-                          'infos': isEmpty,
-                          'compiledProcedure':
-                              contains('test_library.B.new().printNumber()'),
-                        }),
-                        equals({
-                          'succeeded': true,
-                        }),
-                        equals({
-                          'succeeded': true,
-                          'errors': isEmpty,
-                          'warnings': isEmpty,
-                          'infos': isEmpty,
-                          'compiledProcedure': contains(
-                              'new test_library.B.new().c().getNumber()'),
-                        }),
-                        equals({
-                          'succeeded': true,
-                          'errors': isEmpty,
-                          'warnings': isEmpty,
-                          'infos': isEmpty,
-                          'compiledProcedure': contains('return formal;'),
-                        }),
-                      ]));
-                });
-              });
-            });
-          }
-        });
+        if (request.method == 'GET') {
+          // 'readAsBytes'
+          var contents = await entity.readAsBytesAsyncIfPossible();
+          var headers = {
+            'content-length': '${contents.length}',
+            ...request.headers,
+          };
+          return Response.ok(contents, headers: headers);
+        }
       }
-    });
+      return Response.notFound(path);
+    } catch (e, s) {
+      return Response.internalServerError(body: '$e:$s');
+    }
+  }
+
+  Future<void> start(String hostname, int port) async {
+    server = await HttpMultiServer.bind(hostname, port);
+    serveRequests(server, handler);
+  }
+
+  void stop() {
+    server?.close(force: true);
   }
 }
 
@@ -766,7 +966,7 @@
       dartdevc,
       config.testModule3.libraryUri,
       '-o',
-      config.testModule3.jsPath.toFilePath(),
+      config.testModule3.jsUri.toFilePath(),
       '--source-map',
       '--experimental-emit-debug-metadata',
       '--emit-debug-symbols',
@@ -795,7 +995,7 @@
       dartdevc,
       config.testModule2.libraryUri,
       '-o',
-      config.testModule2.jsPath.toFilePath(),
+      config.testModule2.jsUri.toFilePath(),
       '--source-map',
       '--experimental-emit-debug-metadata',
       '--emit-debug-symbols',
@@ -824,9 +1024,10 @@
       dartdevc,
       config.testModule.libraryUri,
       '--summary',
-      '${config.testModule2.summaryDillPath}=${config.testModule2.moduleName}',
+      '${config.testModule2.multiRootSummaryUri}='
+          '${config.testModule2.moduleName}',
       '-o',
-      config.testModule.jsPath.toFilePath(),
+      config.testModule.jsUri.toFilePath(),
       '--source-map',
       '--experimental-emit-debug-metadata',
       '--emit-debug-symbols',
@@ -855,11 +1056,13 @@
       dartdevc,
       config.mainModule.libraryUri,
       '--summary',
-      '${config.testModule3.summaryDillPath}=${config.testModule3.moduleName}',
+      '${config.testModule3.multiRootSummaryUri}='
+          '${config.testModule3.moduleName}',
       '--summary',
-      '${config.testModule.summaryDillPath}=${config.testModule.moduleName}',
+      '${config.testModule.multiRootSummaryUri}='
+          '${config.testModule.moduleName}',
       '-o',
-      config.mainModule.jsPath.toFilePath(),
+      config.mainModule.jsUri.toFilePath(),
       '--source-map',
       '--experimental-emit-debug-metadata',
       '--emit-debug-symbols',
diff --git a/pkg/meta/CHANGELOG.md b/pkg/meta/CHANGELOG.md
index f038761..15da3e3 100644
--- a/pkg/meta/CHANGELOG.md
+++ b/pkg/meta/CHANGELOG.md
@@ -1,4 +1,9 @@
-## 2.0.0
+## 1.7.0
+
+* Restore `TargetKindExtension` and `get displayString`.
+  We published `analyzer 1.7.2` that is compatible with `TargetKindExtension`.
+
+## 2.0.0 - removed
 
 * Restore `TargetKindExtension` and `get displayString`.
 
diff --git a/pkg/meta/pubspec.yaml b/pkg/meta/pubspec.yaml
index b407064..b7e00f8 100644
--- a/pkg/meta/pubspec.yaml
+++ b/pkg/meta/pubspec.yaml
@@ -1,5 +1,6 @@
 name: meta
-version: 2.0.0
+# Note, because version `2.0.0` was mistakenly released, the next major version must be `3.x.y`.
+version: 1.7.0
 homepage: https://github.com/dart-lang/sdk/tree/master/pkg/meta
 description: >-
  Annotations that developers can use to express the intentions that otherwise
diff --git a/pkg/scrape/example/build_control_flow.dart b/pkg/scrape/example/build_control_flow.dart
index bdd851a..04f6ed5 100644
--- a/pkg/scrape/example/build_control_flow.dart
+++ b/pkg/scrape/example/build_control_flow.dart
@@ -86,7 +86,7 @@
     }
   }
 
-  bool _isReturn(Statement statement) {
+  bool _isReturn(Statement? statement) {
     // Ignore empty "else" clauses.
     if (statement == null) return true;
 
@@ -99,7 +99,7 @@
     return false;
   }
 
-  bool _isAdd(Statement statement) {
+  bool _isAdd(Statement? statement) {
     // Ignore empty "else" clauses.
     if (statement == null) return true;
 
diff --git a/pkg/scrape/example/class_reuse.dart b/pkg/scrape/example/class_reuse.dart
index 8fb60c6..6f5e766 100644
--- a/pkg/scrape/example/class_reuse.dart
+++ b/pkg/scrape/example/class_reuse.dart
@@ -24,20 +24,23 @@
       record('Declarations', 'class');
     }
 
-    if (node.extendsClause != null) {
+    var extendsClause = node.extendsClause;
+    if (extendsClause != null) {
       record('Uses', 'extend');
-      record('Superclass names', node.extendsClause.superclass.toString());
+      record('Superclass names', extendsClause.superclass.toString());
     }
 
-    if (node.withClause != null) {
-      for (var mixin in node.withClause.mixinTypes) {
+    var withClause = node.withClause;
+    if (withClause != null) {
+      for (var mixin in withClause.mixinTypes) {
         record('Uses', 'mixin');
         record('Mixin names', mixin.toString());
       }
     }
 
-    if (node.implementsClause != null) {
-      for (var type in node.implementsClause.interfaces) {
+    var implementsClause = node.implementsClause;
+    if (implementsClause != null) {
+      for (var type in implementsClause.interfaces) {
         record('Uses', 'implement');
         record('Superinterface names', type.toString());
       }
diff --git a/pkg/scrape/example/generic_classes.dart b/pkg/scrape/example/generic_classes.dart
index 09ecc32..d58a714 100644
--- a/pkg/scrape/example/generic_classes.dart
+++ b/pkg/scrape/example/generic_classes.dart
@@ -23,7 +23,7 @@
     if (node.typeParameters == null) {
       record('Classes', 0);
     } else {
-      record('Classes', node.typeParameters.typeParameters.length);
+      record('Classes', node.typeParameters!.typeParameters.length);
     }
     super.visitClassDeclaration(node);
   }
diff --git a/pkg/scrape/example/nesting.dart b/pkg/scrape/example/nesting.dart
index 6541d6d..93c11da 100644
--- a/pkg/scrape/example/nesting.dart
+++ b/pkg/scrape/example/nesting.dart
@@ -34,7 +34,7 @@
     ..runCommandLine(arguments);
 
   var methods = buildMethods.keys.toList();
-  methods.sort((a, b) => buildMethods[b].compareTo(buildMethods[a]));
+  methods.sort((a, b) => buildMethods[b]!.compareTo(buildMethods[a]!));
   for (var method in methods) {
     print('${buildMethods[method].toString().padLeft(3)}: $method');
   }
@@ -48,7 +48,7 @@
   bool _pushed = false;
   int _deepestNesting = 0;
 
-  NestingVisitor({bool allCode}) : _allCode = allCode ?? false;
+  NestingVisitor({bool? allCode}) : _allCode = allCode ?? false;
 
   @override
   void beforeVisitBuildMethod(Declaration node) {
@@ -65,7 +65,7 @@
   void visitArgumentList(ArgumentList node) {
     // Only argument lists with trailing commas get indentation.
     if (node.arguments.isNotEmpty &&
-        node.arguments.last.endToken.next.type == TokenType.COMMA) {
+        node.arguments.last.endToken.next!.type == TokenType.COMMA) {
       String name;
       var parent = node.parent;
       if (parent is MethodInvocation) {
diff --git a/pkg/scrape/example/null_aware.dart b/pkg/scrape/example/null_aware.dart
index 4437bd2..d2902c2 100644
--- a/pkg/scrape/example/null_aware.dart
+++ b/pkg/scrape/example/null_aware.dart
@@ -22,7 +22,7 @@
   @override
   void visitMethodInvocation(MethodInvocation node) {
     if (node.operator != null &&
-        node.operator.type == TokenType.QUESTION_PERIOD) {
+        node.operator!.type == TokenType.QUESTION_PERIOD) {
       _nullAware(node);
     }
 
@@ -52,7 +52,7 @@
             parent.target == node ||
         parent is MethodInvocation &&
             parent.operator != null &&
-            parent.operator.type == TokenType.QUESTION_PERIOD &&
+            parent.operator!.type == TokenType.QUESTION_PERIOD &&
             parent.target == node) {
       // This node is not the root of a null-aware chain, so skip it.
       return;
@@ -60,15 +60,15 @@
 
     // This node is the root of a null-aware chain. See how long the chain is.
     var length = 0;
-    var chain = node;
+    AstNode? chain = node;
     while (true) {
       if (chain is PropertyAccess &&
           chain.operator.type == TokenType.QUESTION_PERIOD) {
-        chain = (chain as PropertyAccess).target;
+        chain = chain.target;
       } else if (chain is MethodInvocation &&
           chain.operator != null &&
-          chain.operator.type == TokenType.QUESTION_PERIOD) {
-        chain = (chain as MethodInvocation).target;
+          chain.operator!.type == TokenType.QUESTION_PERIOD) {
+        chain = chain.target;
       } else {
         break;
       }
@@ -242,14 +242,14 @@
 
     // Find the surrounding statement containing the null-aware.
     while (node is Expression) {
-      node = node.parent;
+      node = node.parent!;
     }
 
     printNode(node);
   }
 
   void _checkCondition(AstNode node) {
-    String expression;
+    String? expression;
 
     // Look at the expression that immediately wraps the null-aware to see if
     // it deals with it somehow, like "foo?.bar ?? otherwise".
@@ -262,17 +262,16 @@
             parent.operator.type == TokenType.QUESTION_QUESTION) &&
         (parent.rightOperand is NullLiteral ||
             parent.rightOperand is BooleanLiteral)) {
-      var binary = parent as BinaryExpression;
-      expression = 'foo?.bar ${binary.operator} ${binary.rightOperand}';
+      expression = 'foo?.bar ${parent.operator} ${parent.rightOperand}';
 
       // This does handle it, so see the context where it appears.
-      node = parent as Expression;
+      node = parent;
       if (node is ParenthesizedExpression) node = node.parent as Expression;
       parent = node.parent;
       if (parent is ParenthesizedExpression) parent = parent.parent;
     }
 
-    String context;
+    String? context;
     if (parent is IfStatement && node == parent.condition) {
       context = 'if';
     } else if (parent is BinaryExpression &&
diff --git a/pkg/scrape/example/spread_sizes.dart b/pkg/scrape/example/spread_sizes.dart
index d1257e1..af39416 100644
--- a/pkg/scrape/example/spread_sizes.dart
+++ b/pkg/scrape/example/spread_sizes.dart
@@ -34,13 +34,13 @@
     super.visitMethodInvocation(node);
   }
 
-  void _countCall(Expression node, SimpleIdentifier name, Expression target,
+  void _countCall(Expression node, SimpleIdentifier name, Expression? target,
       ArgumentList args) {
     if (name.name != 'addAll') return;
 
     // See if the target is a collection literal.
     while (target is MethodInvocation) {
-      target = (target as MethodInvocation).target;
+      target = target.target;
     }
 
     if (target is ListLiteral || target is SetOrMapLiteral) {
diff --git a/pkg/scrape/example/strings.dart b/pkg/scrape/example/strings.dart
index a89fcef..a0454ed 100644
--- a/pkg/scrape/example/strings.dart
+++ b/pkg/scrape/example/strings.dart
@@ -54,7 +54,7 @@
 
   @override
   void visitPartOfDirective(PartOfDirective node) {
-    if (node.uri != null) _record('Directive', node.uri);
+    if (node.uri != null) _record('Directive', node.uri!);
     // Don't recurse so that we don't treat the URI as a string expression.
   }
 
diff --git a/pkg/scrape/lib/scrape.dart b/pkg/scrape/lib/scrape.dart
index 32ab6715..1665006 100644
--- a/pkg/scrape/lib/scrape.dart
+++ b/pkg/scrape/lib/scrape.dart
@@ -25,7 +25,7 @@
   final List<ScrapeVisitor Function()> _visitorFactories = [];
 
   /// What percent of files should be processed.
-  int _percent;
+  int? _percent;
 
   /// Process package test files.
   bool _includeTests = true;
@@ -82,9 +82,9 @@
   /// that many times.
   void addHistogram(String name,
       {SortOrder order = SortOrder.descending,
-      bool showBar,
-      bool showAll,
-      int minCount}) {
+      bool? showBar,
+      bool? showAll,
+      int? minCount}) {
     _histograms.putIfAbsent(
         name,
         () => Histogram(
@@ -96,7 +96,7 @@
 
   /// Add an occurrence of [item] to [histogram].
   void record(String histogram, Object item) {
-    _histograms[histogram].add(item);
+    _histograms[histogram]!.add(item);
   }
 
   /// Run the scrape using the given set of command line arguments.
@@ -247,10 +247,10 @@
         if (entry.path.endsWith('.pbenum.dart')) continue;
       }
 
-      if (_percent != null && random.nextInt(100) >= _percent) continue;
+      if (_percent != null && random.nextInt(100) >= _percent!) continue;
 
       var relative = p.relative(entry.path, from: path);
-      _parseFile(entry as File, relative);
+      _parseFile(entry, relative);
     }
   }
 
diff --git a/pkg/scrape/lib/src/histogram.dart b/pkg/scrape/lib/src/histogram.dart
index 12f2b9f..74e35df 100644
--- a/pkg/scrape/lib/src/histogram.dart
+++ b/pkg/scrape/lib/src/histogram.dart
@@ -27,7 +27,7 @@
 
   int get totalCount => _counts.values.fold(0, (a, b) => a + b);
 
-  Histogram({SortOrder order, bool showBar, bool showAll, int minCount})
+  Histogram({SortOrder? order, bool? showBar, bool? showAll, int? minCount})
       : _order = order ?? SortOrder.descending,
         _showBar = showBar ?? true,
         _showAll = showAll ?? false,
@@ -35,7 +35,7 @@
 
   void add(Object item) {
     _counts.putIfAbsent(item, () => 0);
-    _counts[item]++;
+    _counts[item] = _counts[item]! + 1;
   }
 
   void printCounts(String label) {
@@ -46,10 +46,10 @@
     var keys = _counts.keys.toList();
     switch (_order) {
       case SortOrder.ascending:
-        keys.sort((a, b) => _counts[a].compareTo(_counts[b]));
+        keys.sort((a, b) => _counts[a]!.compareTo(_counts[b]!));
         break;
       case SortOrder.descending:
-        keys.sort((a, b) => _counts[b].compareTo(_counts[a]));
+        keys.sort((a, b) => _counts[b]!.compareTo(_counts[a]!));
         break;
       case SortOrder.alphabetical:
         keys.sort();
@@ -68,7 +68,7 @@
     var shown = 0;
     var skipped = 0;
     for (var object in keys) {
-      var count = _counts[object];
+      var count = _counts[object]!;
       var countString = count.toString().padLeft(7);
       var percent = 100 * count / total;
       var percentString = percent.toStringAsFixed(3).padLeft(7);
@@ -91,7 +91,7 @@
     // If we're counting numeric keys, show other statistics too.
     if (_order == SortOrder.numeric && keys.isNotEmpty) {
       var sum = keys.fold<int>(
-          0, (result, key) => result + (key as int) * _counts[key]);
+          0, (result, key) => result + (key as int) * _counts[key]!);
       var average = sum / total;
       var median = _counts[keys[keys.length ~/ 2]];
       print('Sum $sum, average ${average.toStringAsFixed(3)}, median $median');
diff --git a/pkg/scrape/lib/src/scrape_visitor.dart b/pkg/scrape/lib/src/scrape_visitor.dart
index 0b8a405..38ca2c5 100644
--- a/pkg/scrape/lib/src/scrape_visitor.dart
+++ b/pkg/scrape/lib/src/scrape_visitor.dart
@@ -17,7 +17,6 @@
 /// need to define a pass-through constructor.
 void bindVisitor(ScrapeVisitor visitor, Scrape scrape, String path,
     String source, LineInfo info) {
-  assert(visitor._scrape == null, 'Should only bind once.');
   visitor._scrape = scrape;
   visitor._path = path;
   visitor._source = source;
@@ -26,11 +25,11 @@
 
 /// Base Visitor class with some utility functionality.
 class ScrapeVisitor extends RecursiveAstVisitor<void> {
-  // These final-ish fields are initialized by [bindVisitor()].
-  Scrape _scrape;
-  String _path;
-  String _source;
-  LineInfo lineInfo;
+  // These are initialized by [bindVisitor()].
+  late final Scrape _scrape;
+  late final String _path;
+  late final String _source;
+  late final LineInfo lineInfo;
 
   /// How many levels deep the visitor is currently nested inside build methods.
   int _inFlutterBuildMethods = 0;
@@ -47,8 +46,8 @@
   /// "BuildContext context".
   bool get isInFlutterBuildMethod => _inFlutterBuildMethods > 0;
 
-  bool _isBuildMethod(TypeAnnotation returnType, SimpleIdentifier name,
-      FormalParameterList parameters) {
+  bool _isBuildMethod(TypeAnnotation? returnType, SimpleIdentifier name,
+      FormalParameterList? parameters) {
     var parameterString = parameters.toString();
 
     if (returnType.toString() == 'void') return false;
@@ -79,8 +78,8 @@
     var startLine = lineInfo.getLocation(node.offset).lineNumber;
     var endLine = lineInfo.getLocation(node.end).lineNumber;
 
-    startLine = startLine.clamp(0, lineInfo.lineCount - 1) as int;
-    endLine = endLine.clamp(0, lineInfo.lineCount - 1) as int;
+    startLine = startLine.clamp(0, lineInfo.lineCount - 1);
+    endLine = endLine.clamp(0, lineInfo.lineCount - 1);
 
     var buffer = StringBuffer();
     buffer.writeln('// $path:$startLine');
diff --git a/pkg/scrape/pubspec.yaml b/pkg/scrape/pubspec.yaml
index ffdaff1..c250123 100644
--- a/pkg/scrape/pubspec.yaml
+++ b/pkg/scrape/pubspec.yaml
@@ -4,10 +4,10 @@
 publish_to: none
 
 environment:
-  sdk: ^2.10.0
+  sdk: ^2.13.0
 
 dependencies:
-  args: ^1.6.0
+  args: ^2.1.1
   analyzer:
     path: ../analyzer
   path: ^1.7.0
diff --git a/runtime/vm/object_graph.cc b/runtime/vm/object_graph.cc
index ed7fb28..1bd42fa 100644
--- a/runtime/vm/object_graph.cc
+++ b/runtime/vm/object_graph.cc
@@ -168,7 +168,7 @@
   }
 }
 
-intptr_t ObjectGraph::StackIterator::OffsetFromParentInWords() const {
+intptr_t ObjectGraph::StackIterator::OffsetFromParent() const {
   intptr_t parent_index = stack_->Parent(index_);
   if (parent_index == Stack::kNoParent) {
     return -1;
@@ -179,8 +179,7 @@
   uword child_ptr_addr = reinterpret_cast<uword>(child.ptr);
   intptr_t offset = child_ptr_addr - parent_start;
   if (offset > 0 && offset < parent.obj->untag()->HeapSize()) {
-    ASSERT(Utils::IsAligned(offset, kWordSize));
-    return offset >> kWordSizeLog2;
+    return offset;
   } else {
     // Some internal VM objects visit pointers not contained within the parent.
     // For instance, UntaggedCode::VisitCodePointers visits pointers in
@@ -435,7 +434,7 @@
         if (!path_.IsNull() && offset_index < path_.Length()) {
           current = it->Get();
           path_.SetAt(obj_index, current);
-          offset_from_parent = Smi::New(it->OffsetFromParentInWords());
+          offset_from_parent = Smi::New(it->OffsetFromParent());
           path_.SetAt(offset_index, offset_from_parent);
         }
         ++length_;
@@ -509,8 +508,7 @@
           uword current_ptr_addr = reinterpret_cast<uword>(current_ptr);
           intptr_t offset = current_ptr_addr - source_start;
           if (offset > 0 && offset < source_->untag()->HeapSize()) {
-            ASSERT(Utils::IsAligned(offset, kWordSize));
-            *scratch_ = Smi::New(offset >> kWordSizeLog2);
+            *scratch_ = Smi::New(offset);
           } else {
             // Some internal VM objects visit pointers not contained within the
             // parent. For instance, UntaggedCode::VisitCodePointers visits
@@ -543,8 +541,7 @@
           uword current_ptr_addr = reinterpret_cast<uword>(current_ptr);
           intptr_t offset = current_ptr_addr - source_start;
           if (offset > 0 && offset < source_->untag()->HeapSize()) {
-            ASSERT(Utils::IsAligned(offset, kWordSize));
-            *scratch_ = Smi::New(offset >> kWordSizeLog2);
+            *scratch_ = Smi::New(offset);
           } else {
             // Some internal VM objects visit pointers not contained within the
             // parent. For instance, UntaggedCode::VisitCodePointers visits
@@ -1192,7 +1189,7 @@
             intptr_t flags = 1;  // Strong.
             WriteUnsigned(flags);
             intptr_t offset = OffsetsTable::offsets_table[j].offset;
-            intptr_t index = (offset - min_offset) / kWordSize;
+            intptr_t index = (offset - min_offset) / kCompressedWordSize;
             ASSERT(index >= 0);
             WriteUnsigned(index);
             WriteUtf8(OffsetsTable::offsets_table[j].field_name);
@@ -1208,7 +1205,8 @@
                 if (field.is_instance()) {
                   intptr_t flags = 1;  // Strong.
                   WriteUnsigned(flags);
-                  intptr_t index = field.HostOffset() / kWordSize - 1;
+                  intptr_t index = (field.HostOffset() / kCompressedWordSize) -
+                                   (kWordSize / kCompressedWordSize);
                   ASSERT(index >= 0);
                   WriteUnsigned(index);
                   str = field.name();
diff --git a/runtime/vm/object_graph.h b/runtime/vm/object_graph.h
index d034705..55dc815 100644
--- a/runtime/vm/object_graph.h
+++ b/runtime/vm/object_graph.h
@@ -35,7 +35,7 @@
     // Returns false if there is no parent.
     bool MoveToParent();
     // Offset into parent for the pointer to current object. -1 if no parent.
-    intptr_t OffsetFromParentInWords() const;
+    intptr_t OffsetFromParent() const;
 
    private:
     StackIterator(const Stack* stack, intptr_t index)
diff --git a/runtime/vm/object_graph_test.cc b/runtime/vm/object_graph_test.cc
index cfb022e..8294897 100644
--- a/runtime/vm/object_graph_test.cc
+++ b/runtime/vm/object_graph_test.cc
@@ -126,14 +126,12 @@
       // c is the first element in b.
       Smi& offset_from_parent = Smi::Handle();
       offset_from_parent ^= path.At(1);
-      EXPECT_EQ(Array::element_offset(0),
-                offset_from_parent.Value() * kWordSize);
+      EXPECT_EQ(Array::element_offset(0), offset_from_parent.Value());
       Array& expected_b = Array::Handle();
       expected_b ^= path.At(2);
       // b is the element with index 10 in a.
       offset_from_parent ^= path.At(3);
-      EXPECT_EQ(Array::element_offset(10),
-                offset_from_parent.Value() * kWordSize);
+      EXPECT_EQ(Array::element_offset(10), offset_from_parent.Value());
       Array& expected_a = Array::Handle();
       expected_a ^= path.At(4);
       EXPECT(expected_c.ptr() == c.ptr());
diff --git a/runtime/vm/raw_object.cc b/runtime/vm/raw_object.cc
index a370775..d7a1beb 100644
--- a/runtime/vm/raw_object.cc
+++ b/runtime/vm/raw_object.cc
@@ -396,30 +396,30 @@
   uword next_field_offset = isolate_group->GetClassForHeapWalkAt(class_id)
                                 ->untag()
                                 ->host_next_field_offset_in_words_
-                            << kWordSizeLog2;
+                            << kCompressedWordSizeLog2;
   ASSERT(next_field_offset > 0);
   uword obj_addr = UntaggedObject::ToAddr(this);
   uword from = obj_addr + sizeof(UntaggedObject);
-  uword to = obj_addr + next_field_offset - kWordSize;
-  const auto first = reinterpret_cast<ObjectPtr*>(from);
-  const auto last = reinterpret_cast<ObjectPtr*>(to);
+  uword to = obj_addr + next_field_offset - kCompressedWordSize;
+  const auto first = reinterpret_cast<CompressedObjectPtr*>(from);
+  const auto last = reinterpret_cast<CompressedObjectPtr*>(to);
 
 #if defined(SUPPORT_UNBOXED_INSTANCE_FIELDS)
   const auto unboxed_fields_bitmap =
       visitor->shared_class_table()->GetUnboxedFieldsMapAt(class_id);
 
   if (!unboxed_fields_bitmap.IsEmpty()) {
-    intptr_t bit = sizeof(UntaggedObject) / kWordSize;
-    for (ObjectPtr* current = first; current <= last; current++) {
+    intptr_t bit = sizeof(UntaggedObject) / kCompressedWordSize;
+    for (CompressedObjectPtr* current = first; current <= last; current++) {
       if (!unboxed_fields_bitmap.Get(bit++)) {
-        visitor->VisitPointer(current);
+        visitor->VisitCompressedPointers(heap_base(), current, current);
       }
     }
   } else {
-    visitor->VisitPointers(first, last);
+    visitor->VisitCompressedPointers(heap_base(), first, last);
   }
 #else
-  visitor->VisitPointers(first, last);
+  visitor->VisitCompressedPointers(heap_base(), first, last);
 #endif  // defined(SUPPORT_UNBOXED_INSTANCE_FIELDS)
 }
 
diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc
index 51d5a00..238bc27 100644
--- a/runtime/vm/service.cc
+++ b/runtime/vm/service.cc
@@ -2136,15 +2136,16 @@
       jselement.AddProperty("source", source);
       if (source.IsArray()) {
         intptr_t element_index =
-            slot_offset.Value() - (Array::element_offset(0) >> kWordSizeLog2);
+            (slot_offset.Value() - Array::element_offset(0)) /
+            Array::kBytesPerElement;
         jselement.AddProperty("parentListIndex", element_index);
       } else {
         if (source.IsInstance()) {
           source_class = source.clazz();
           parent_field_map = source_class.OffsetToFieldMap();
-          intptr_t offset = slot_offset.Value();
-          if (offset > 0 && offset < parent_field_map.Length()) {
-            field ^= parent_field_map.At(offset);
+          intptr_t index = slot_offset.Value() >> kCompressedWordSizeLog2;
+          if (index > 0 && index < parent_field_map.Length()) {
+            field ^= parent_field_map.At(index);
             if (!field.IsNull()) {
               jselement.AddProperty("parentField", field);
               continue;
@@ -2152,7 +2153,7 @@
           }
         }
         const char* field_name = offsets_table.FieldNameForOffset(
-            source.GetClassId(), slot_offset.Value() * kWordSize);
+            source.GetClassId(), slot_offset.Value());
         if (field_name != nullptr) {
           jselement.AddProperty("_parentWordOffset", slot_offset.Value());
           // TODO(vm-service): Adjust RPC type to allow returning a field name
@@ -2161,8 +2162,8 @@
           // jselement.AddProperty("_parentFieldName", field_name);
         } else if (source.IsContext()) {
           intptr_t element_index =
-              slot_offset.Value() -
-              (Context::variable_offset(0) >> kWordSizeLog2);
+              (slot_offset.Value() - Context::variable_offset(0)) /
+              Context::kBytesPerElement;
           jselement.AddProperty("parentListIndex", element_index);
         } else {
           jselement.AddProperty("_parentWordOffset", slot_offset.Value());
@@ -2254,13 +2255,15 @@
       slot_offset ^= path.At((i * 2) - 1);
       if (element.IsArray() || element.IsGrowableObjectArray()) {
         intptr_t element_index =
-            slot_offset.Value() - (Array::element_offset(0) >> kWordSizeLog2);
+            (slot_offset.Value() - Array::element_offset(0)) /
+            Array::kBytesPerElement;
         jselement.AddProperty("parentListIndex", element_index);
       } else if (element.IsLinkedHashMap()) {
         map = static_cast<LinkedHashMapPtr>(path.At(i * 2));
         map_data = map.data();
         intptr_t element_index =
-            slot_offset.Value() - (Array::element_offset(0) >> kWordSizeLog2);
+            (slot_offset.Value() - Array::element_offset(0)) /
+            Array::kBytesPerElement;
         LinkedHashMap::Iterator iterator(map);
         while (iterator.MoveNext()) {
           if (iterator.CurrentKey() == map_data.At(element_index) ||
@@ -2278,9 +2281,9 @@
         if (element.IsInstance()) {
           element_class = element.clazz();
           element_field_map = element_class.OffsetToFieldMap();
-          intptr_t offset = slot_offset.Value();
-          if ((offset > 0) && (offset < element_field_map.Length())) {
-            field ^= element_field_map.At(offset);
+          intptr_t index = slot_offset.Value() >> kCompressedWordSizeLog2;
+          if ((index > 0) && (index < element_field_map.Length())) {
+            field ^= element_field_map.At(index);
             if (!field.IsNull()) {
               name ^= field.name();
               jselement.AddProperty("parentField", name.ToCString());
@@ -2289,13 +2292,13 @@
           }
         }
         const char* field_name = offsets_table.FieldNameForOffset(
-            element.GetClassId(), slot_offset.Value() * kWordSize);
+            element.GetClassId(), slot_offset.Value());
         if (field_name != nullptr) {
           jselement.AddProperty("parentField", field_name);
         } else if (element.IsContext()) {
           intptr_t element_index =
-              slot_offset.Value() -
-              (Context::variable_offset(0) >> kWordSizeLog2);
+              (slot_offset.Value() - Context::variable_offset(0)) /
+              Context::kBytesPerElement;
           jselement.AddProperty("parentListIndex", element_index);
         } else {
           jselement.AddProperty("_parentWordOffset", slot_offset.Value());
diff --git a/tools/VERSION b/tools/VERSION
index f206cb0..e08126e 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 14
 PATCH 0
-PRERELEASE 288
+PRERELEASE 289
 PRERELEASE_PATCH 0
\ No newline at end of file