Version 2.18.0-162.0.dev

Merge commit 'f6e4242a5de8b944a740d8de988b249b2d841b66' into 'dev'
diff --git a/DEPS b/DEPS
index cc10c05..797f60b 100644
--- a/DEPS
+++ b/DEPS
@@ -120,7 +120,7 @@
   "icu_rev": "81d656878ec611cb0b42d52c82e9dae93920d9ba",
   "intl_rev": "9145f308f1458f37630a1ffce3b7d3b471ebbc56",
   "jinja2_rev": "2222b31554f03e62600cd7e383376a7c187967a1",
-  "json_rpc_2_rev": "7e00f893440a72de0637970325e4ea44bd1e8c8e",
+  "json_rpc_2_rev": "2de9a1f9821807fa2c85fd48e2f70b9cbcddcb67",
   "linter_rev": "a8529c6692922b45bc287543b355c90d7b1286d3", # 1.24.0
   "lints_rev": "8294e5648ab49474541527e2911e72e4c5aefe55",
   "logging_rev": "f6979e3bc3b6e1847a08335b7eb6304e18986195",
@@ -155,7 +155,7 @@
   "test_rev": "d54846bc2b5cfa4e1445fda85c5e48a00940aa68",
   "typed_data_rev": "8b19e29bcf4077147de4d67adeabeb48270c65eb",
   "usage_rev": "e85d575d6decb921c57a43b9844bba3607479f56",
-  "vector_math_rev": "0cbed0914d49a6a44555e6d5444c438a4a4c3fc1",
+  "vector_math_rev": "1c72944e8c2f02340a1d90b32aab2e3836cef8cc",
   "watcher_rev": "f76997ab0c857dc5537ac0975a9ada92b54ef949",
   "web_components_rev": "8f57dac273412a7172c8ade6f361b407e2e4ed02",
   "web_socket_channel_rev": "99dbdc5769e19b9eeaf69449a59079153c6a8b1f",
diff --git a/benchmarks/IsolateSendExit/dart/IsolateSendExitLatency.dart b/benchmarks/IsolateSendExit/dart/IsolateSendExitLatency.dart
new file mode 100644
index 0000000..8d00b01
--- /dev/null
+++ b/benchmarks/IsolateSendExit/dart/IsolateSendExitLatency.dart
@@ -0,0 +1,45 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+//
+// This test ensures that there are no long pauses when sending large objects
+// via exit/send.
+
+import 'dart:async';
+import 'dart:typed_data';
+import 'dart:math' as math;
+import 'dart:isolate';
+
+import 'latency.dart';
+
+main() async {
+  final statsFuture =
+      measureEventLoopLatency(const Duration(milliseconds: 1), 4000, work: () {
+    // Every 1 ms we allocate some objects which may trigger GC some time.
+    for (int i = 0; i < 32; i++) {
+      List.filled(32 * 1024 ~/ 8, null);
+    }
+  });
+
+  final result = await compute(() {
+    final l = <dynamic>[];
+    for (int i = 0; i < 10 * 1000 * 1000; ++i) {
+      l.add(Object());
+    }
+    return l;
+  });
+  if (result.length != 10 * 1000 * 1000) throw 'failed';
+
+  final stats = await statsFuture;
+  stats.report('IsolateSendExitLatency');
+}
+
+Future<T> compute<T>(T Function() fun) {
+  final rp = ReceivePort();
+  final sp = rp.sendPort;
+  Isolate.spawn((_) {
+    final value = fun();
+    Isolate.exit(sp, value);
+  }, null);
+  return rp.first.then((t) => t as T);
+}
diff --git a/benchmarks/IsolateSendExit/dart/latency.dart b/benchmarks/IsolateSendExit/dart/latency.dart
new file mode 100644
index 0000000..0a5054f
--- /dev/null
+++ b/benchmarks/IsolateSendExit/dart/latency.dart
@@ -0,0 +1,134 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:io';
+import 'dart:math' as math;
+import 'dart:typed_data';
+
+/// Measures event loop responsiveness.
+///
+/// Schedules new timer events, [tickDuration] in the future, and measures how
+/// long it takes for these events to actually arrive.
+///
+/// Runs [numberOfTicks] times before completing with [EventLoopLatencyStats].
+Future<EventLoopLatencyStats> measureEventLoopLatency(
+    Duration tickDuration, int numberOfTicks,
+    {void Function()? work}) {
+  final completer = Completer<EventLoopLatencyStats>();
+
+  final tickDurationInUs = tickDuration.inMicroseconds;
+  final buffer = _TickLatencies(numberOfTicks);
+  final sw = Stopwatch()..start();
+  int lastTimestamp = 0;
+
+  void trigger() {
+    final int currentTimestamp = sw.elapsedMicroseconds;
+
+    // Every tick we missed to schedule we'll add with difference to when we
+    // would've scheduled it and when we became responsive again.
+    bool done = false;
+    while (!done && lastTimestamp < (currentTimestamp - tickDurationInUs)) {
+      done = !buffer.add(currentTimestamp - lastTimestamp - tickDurationInUs);
+      lastTimestamp += tickDurationInUs;
+    }
+
+    if (work != null) {
+      work();
+    }
+
+    if (!done) {
+      lastTimestamp = currentTimestamp;
+      Timer(tickDuration, trigger);
+    } else {
+      completer.complete(buffer.makeStats());
+    }
+  }
+
+  Timer(tickDuration, trigger);
+
+  return completer.future;
+}
+
+/// Result of the event loop latency measurement.
+class EventLoopLatencyStats {
+  /// Minimum latency between scheduling a tick and it's arrival (in ms).
+  final double minLatency;
+
+  /// Average latency between scheduling a tick and it's arrival (in ms).
+  final double avgLatency;
+
+  /// Maximum latency between scheduling a tick and it's arrival (in ms).
+  final double maxLatency;
+
+  /// The 50th percentile (median) (in ms).
+  final double percentile50th;
+
+  /// The 90th percentile (in ms).
+  final double percentile90th;
+
+  /// The 95th percentile (in ms).
+  final double percentile95th;
+
+  /// The 99th percentile (in ms).
+  final double percentile99th;
+
+  EventLoopLatencyStats(
+      this.minLatency,
+      this.avgLatency,
+      this.maxLatency,
+      this.percentile50th,
+      this.percentile90th,
+      this.percentile95th,
+      this.percentile99th);
+
+  void report(String name) {
+    print('$name.Min(RunTimeRaw): $minLatency ms.');
+    print('$name.Avg(RunTimeRaw): $avgLatency ms.');
+    print('$name.Percentile50(RunTimeRaw): $percentile50th ms.');
+    print('$name.Percentile90(RunTimeRaw): $percentile90th ms.');
+    print('$name.Percentile95(RunTimeRaw): $percentile95th ms.');
+    print('$name.Percentile99(RunTimeRaw): $percentile99th ms.');
+    print('$name.Max(RunTimeRaw): $maxLatency ms.');
+  }
+}
+
+/// Accumulates tick latencies and makes statistics for it.
+class _TickLatencies {
+  final Uint64List _timestamps;
+  int _index = 0;
+
+  _TickLatencies(int numberOfTicks) : _timestamps = Uint64List(numberOfTicks);
+
+  /// Returns `true` while the buffer has not been filled yet.
+  bool add(int latencyInUs) {
+    _timestamps[_index++] = latencyInUs;
+    return _index < _timestamps.length;
+  }
+
+  EventLoopLatencyStats makeStats() {
+    if (_index != _timestamps.length) {
+      throw 'Buffer has not been fully filled yet.';
+    }
+
+    _timestamps.sort();
+    final length = _timestamps.length;
+    final double avg = _timestamps.fold(0, (int a, int b) => a + b) / length;
+    final int min = _timestamps.fold(0x7fffffffffffffff, math.min);
+    final int max = _timestamps.fold(0, math.max);
+    final percentile50th = _timestamps[50 * length ~/ 100];
+    final percentile90th = _timestamps[90 * length ~/ 100];
+    final percentile95th = _timestamps[95 * length ~/ 100];
+    final percentile99th = _timestamps[99 * length ~/ 100];
+
+    return EventLoopLatencyStats(
+        min / 1000,
+        avg / 1000,
+        max / 1000,
+        percentile50th / 1000,
+        percentile90th / 1000,
+        percentile95th / 1000,
+        percentile99th / 1000);
+  }
+}
diff --git a/benchmarks/IsolateSendExit/dart2/IsolateSendExitLatency.dart b/benchmarks/IsolateSendExit/dart2/IsolateSendExitLatency.dart
new file mode 100644
index 0000000..c165c73
--- /dev/null
+++ b/benchmarks/IsolateSendExit/dart2/IsolateSendExitLatency.dart
@@ -0,0 +1,47 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+//
+// This test ensures that there are no long pauses when sending large objects
+// via exit/send.
+
+// @dart=2.9
+
+import 'dart:async';
+import 'dart:typed_data';
+import 'dart:math' as math;
+import 'dart:isolate';
+
+import 'latency.dart';
+
+main() async {
+  final statsFuture =
+      measureEventLoopLatency(const Duration(milliseconds: 1), 4000, work: () {
+    // Every 1 ms we allocate some objects which may trigger GC some time.
+    for (int i = 0; i < 32; i++) {
+      List.filled(32 * 1024 ~/ 8, null);
+    }
+  });
+
+  final result = await compute(() {
+    final l = <dynamic>[];
+    for (int i = 0; i < 10 * 1000 * 1000; ++i) {
+      l.add(Object());
+    }
+    return l;
+  });
+  if (result.length != 10 * 1000 * 1000) throw 'failed';
+
+  final stats = await statsFuture;
+  stats.report('IsolateSendExitLatency');
+}
+
+Future<T> compute<T>(T Function() fun) {
+  final rp = ReceivePort();
+  final sp = rp.sendPort;
+  Isolate.spawn((_) {
+    final value = fun();
+    Isolate.exit(sp, value);
+  }, null);
+  return rp.first.then((t) => t as T);
+}
diff --git a/benchmarks/IsolateSendExit/dart2/latency.dart b/benchmarks/IsolateSendExit/dart2/latency.dart
new file mode 100644
index 0000000..8b3352c
--- /dev/null
+++ b/benchmarks/IsolateSendExit/dart2/latency.dart
@@ -0,0 +1,136 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart=2.9
+
+import 'dart:async';
+import 'dart:io';
+import 'dart:math' as math;
+import 'dart:typed_data';
+
+/// Measures event loop responsiveness.
+///
+/// Schedules new timer events, [tickDuration] in the future, and measures how
+/// long it takes for these events to actually arrive.
+///
+/// Runs [numberOfTicks] times before completing with [EventLoopLatencyStats].
+Future<EventLoopLatencyStats> measureEventLoopLatency(
+    Duration tickDuration, int numberOfTicks,
+    {void Function() work}) {
+  final completer = Completer<EventLoopLatencyStats>();
+
+  final tickDurationInUs = tickDuration.inMicroseconds;
+  final buffer = _TickLatencies(numberOfTicks);
+  final sw = Stopwatch()..start();
+  int lastTimestamp = 0;
+
+  void trigger() {
+    final int currentTimestamp = sw.elapsedMicroseconds;
+
+    // Every tick we missed to schedule we'll add with difference to when we
+    // would've scheduled it and when we became responsive again.
+    bool done = false;
+    while (!done && lastTimestamp < (currentTimestamp - tickDurationInUs)) {
+      done = !buffer.add(currentTimestamp - lastTimestamp - tickDurationInUs);
+      lastTimestamp += tickDurationInUs;
+    }
+
+    if (work != null) {
+      work();
+    }
+
+    if (!done) {
+      lastTimestamp = currentTimestamp;
+      Timer(tickDuration, trigger);
+    } else {
+      completer.complete(buffer.makeStats());
+    }
+  }
+
+  Timer(tickDuration, trigger);
+
+  return completer.future;
+}
+
+/// Result of the event loop latency measurement.
+class EventLoopLatencyStats {
+  /// Minimum latency between scheduling a tick and it's arrival (in ms).
+  final double minLatency;
+
+  /// Average latency between scheduling a tick and it's arrival (in ms).
+  final double avgLatency;
+
+  /// Maximum latency between scheduling a tick and it's arrival (in ms).
+  final double maxLatency;
+
+  /// The 50th percentile (median) (in ms).
+  final double percentile50th;
+
+  /// The 90th percentile (in ms).
+  final double percentile90th;
+
+  /// The 95th percentile (in ms).
+  final double percentile95th;
+
+  /// The 99th percentile (in ms).
+  final double percentile99th;
+
+  EventLoopLatencyStats(
+      this.minLatency,
+      this.avgLatency,
+      this.maxLatency,
+      this.percentile50th,
+      this.percentile90th,
+      this.percentile95th,
+      this.percentile99th);
+
+  void report(String name) {
+    print('$name.Min(RunTimeRaw): $minLatency ms.');
+    print('$name.Avg(RunTimeRaw): $avgLatency ms.');
+    print('$name.Percentile50(RunTimeRaw): $percentile50th ms.');
+    print('$name.Percentile90(RunTimeRaw): $percentile90th ms.');
+    print('$name.Percentile95(RunTimeRaw): $percentile95th ms.');
+    print('$name.Percentile99(RunTimeRaw): $percentile99th ms.');
+    print('$name.Max(RunTimeRaw): $maxLatency ms.');
+  }
+}
+
+/// Accumulates tick latencies and makes statistics for it.
+class _TickLatencies {
+  final Uint64List _timestamps;
+  int _index = 0;
+
+  _TickLatencies(int numberOfTicks) : _timestamps = Uint64List(numberOfTicks);
+
+  /// Returns `true` while the buffer has not been filled yet.
+  bool add(int latencyInUs) {
+    _timestamps[_index++] = latencyInUs;
+    return _index < _timestamps.length;
+  }
+
+  EventLoopLatencyStats makeStats() {
+    if (_index != _timestamps.length) {
+      throw 'Buffer has not been fully filled yet.';
+    }
+
+    _timestamps.sort();
+    final length = _timestamps.length;
+    final double avg = _timestamps.fold(0, (int a, int b) => a + b) / length;
+    final int min = _timestamps.fold(0x7fffffffffffffff, math.min);
+    final int max = _timestamps.fold(0, math.max);
+    final percentile50th = _timestamps[50 * length ~/ 100];
+    final percentile90th = _timestamps[90 * length ~/ 100];
+    final percentile95th = _timestamps[95 * length ~/ 100];
+    final percentile99th = _timestamps[99 * length ~/ 100];
+
+    return EventLoopLatencyStats(
+        min / 1000,
+        avg / 1000,
+        max / 1000,
+        percentile50th / 1000,
+        percentile90th / 1000,
+        percentile95th / 1000,
+        percentile99th / 1000);
+  }
+}
diff --git a/pkg/analysis_server/lib/src/lsp/json_parsing.dart b/pkg/analysis_server/lib/src/lsp/json_parsing.dart
index f396dde..5b33fea 100644
--- a/pkg/analysis_server/lib/src/lsp/json_parsing.dart
+++ b/pkg/analysis_server/lib/src/lsp/json_parsing.dart
@@ -5,6 +5,7 @@
 import 'dart:collection';
 
 final nullLspJsonReporter = _NullLspJsonReporter();
+final throwingLspJsonReporter = _ThrowingLspJsonReporter();
 
 /// Tracks a path through a JSON object during validation to allow reporting
 /// validation errors with user-friendly paths to the invalid fields.
@@ -51,3 +52,11 @@
   @override
   void reportError(String message) {}
 }
+
+class _ThrowingLspJsonReporter extends LspJsonReporter {
+  @override
+  void reportError(String message) {
+    super.reportError(message);
+    throw errors.join('\n');
+  }
+}
diff --git a/pkg/analysis_server/test/src/cider/cider_service.dart b/pkg/analysis_server/test/src/cider/cider_service.dart
index fbc50e9..35e01af 100644
--- a/pkg/analysis_server/test/src/cider/cider_service.dart
+++ b/pkg/analysis_server/test/src/cider/cider_service.dart
@@ -5,6 +5,7 @@
 import 'dart:convert';
 
 import 'package:analyzer/src/dart/analysis/performance_logger.dart';
+import 'package:analyzer/src/dart/micro/cider_byte_store.dart';
 import 'package:analyzer/src/dart/micro/resolve_file.dart';
 import 'package:analyzer/src/dart/sdk/sdk.dart';
 import 'package:analyzer/src/test_utilities/mock_sdk.dart';
@@ -36,12 +37,13 @@
     )!;
 
     fileResolver = FileResolver(
-      logger,
-      resourceProvider,
-      workspace.createSourceFactory(sdk, null),
-      (String path) => _getDigest(path),
-      null,
+      logger: logger,
+      resourceProvider: resourceProvider,
+      sourceFactory: workspace.createSourceFactory(sdk, null),
+      getFileDigest: (String path) => _getDigest(path),
+      prefetchFiles: null,
       workspace: workspace,
+      byteStore: CiderCachedByteStore(20 * 1024 * 1024 /* 20 MB */),
     );
     fileResolver.testView = FileResolverTestView();
   }
diff --git a/pkg/analysis_server/test/tool/lsp_spec/json_test.dart b/pkg/analysis_server/test/tool/lsp_spec/json_test.dart
index b411ff6..9275e6d 100644
--- a/pkg/analysis_server/test/tool/lsp_spec/json_test.dart
+++ b/pkg/analysis_server/test/tool/lsp_spec/json_test.dart
@@ -130,7 +130,7 @@
 
     test('canParse returns true for in-spec (restricted) enum values', () {
       expect(
-        MarkupKind.canParse('plaintext', nullLspJsonReporter),
+        MarkupKind.canParse('plaintext', throwingLspJsonReporter),
         isTrue,
       );
     });
@@ -138,7 +138,7 @@
     test('canParse returns true for out-of-spec (unrestricted) enum values',
         () {
       expect(
-        SymbolKind.canParse(-1, nullLspJsonReporter),
+        SymbolKind.canParse(-1, throwingLspJsonReporter),
         isTrue,
       );
     });
@@ -150,7 +150,7 @@
         'processId': null,
         'rootUri': null,
         'capabilities': <String, Object>{}
-      }, nullLspJsonReporter);
+      }, throwingLspJsonReporter);
       expect(canParse, isTrue);
     });
 
@@ -160,7 +160,7 @@
       final canParse = CreateFile.canParse({
         'kind': 'create',
         'uri': 'file:///temp/foo',
-      }, nullLspJsonReporter);
+      }, throwingLspJsonReporter);
       expect(canParse, isTrue);
     });
 
@@ -185,14 +185,17 @@
         null: true,
         'invalid': false,
       };
-      for (final testValue in testTraceValues.keys) {
-        final expected = testTraceValues[testValue];
+      for (final entry in testTraceValues.entries) {
+        final testValue = entry.key;
+        final expected = entry.value;
+        final reporter =
+            expected ? throwingLspJsonReporter : nullLspJsonReporter;
         final canParse = InitializeParams.canParse({
           'processId': null,
           'rootUri': null,
           'capabilities': <String, Object>{},
           'trace': testValue,
-        }, nullLspJsonReporter);
+        }, reporter);
         expect(canParse, expected,
             reason: 'InitializeParams.canParse returned $canParse with a '
                 '"trace" value of "$testValue" but expected $expected');
@@ -201,11 +204,12 @@
 
     test('canParse validates optional fields', () {
       expect(
-        RenameFileOptions.canParse(<String, Object>{}, nullLspJsonReporter),
+        RenameFileOptions.canParse(<String, Object>{}, throwingLspJsonReporter),
         isTrue,
       );
       expect(
-        RenameFileOptions.canParse({'overwrite': true}, nullLspJsonReporter),
+        RenameFileOptions.canParse(
+            {'overwrite': true}, throwingLspJsonReporter),
         isTrue,
       );
       expect(
@@ -217,7 +221,7 @@
     test('canParse ignores fields not in the spec', () {
       expect(
         RenameFileOptions.canParse(
-            {'overwrite': true, 'invalidField': true}, nullLspJsonReporter),
+            {'overwrite': true, 'invalidField': true}, throwingLspJsonReporter),
         isTrue,
       );
       expect(
@@ -398,6 +402,15 @@
       expect(message.params, isNull);
       expect(message.jsonrpc, equals('test'));
     });
+
+    test('parses JSON with integers in double fields', () {
+      final input = '{"alpha":1.0,"blue":0,"green":1,"red":1.5}';
+      final message = Color.fromJson(jsonDecode(input) as Map<String, Object?>);
+      expect(message.alpha, 1.0);
+      expect(message.blue, 0.0);
+      expect(message.green, 1.0);
+      expect(message.red, 1.5);
+    });
   });
 
   test('objects with lists can round-trip through to json and back', () {
diff --git a/pkg/analyzer/lib/dart/analysis/results.dart b/pkg/analyzer/lib/dart/analysis/results.dart
index 97198d9..604cf8f 100644
--- a/pkg/analyzer/lib/dart/analysis/results.dart
+++ b/pkg/analyzer/lib/dart/analysis/results.dart
@@ -120,6 +120,17 @@
         SomeResolvedLibraryResult {}
 
 /// The type of [InvalidResult] returned when the given file is not a library,
+/// but an augmentation of a library.
+///
+/// Clients may not extend, implement or mix-in this class.
+class NotLibraryButAugmentationResult
+    implements
+        InvalidResult,
+        SomeLibraryElementResult,
+        SomeParsedLibraryResult,
+        SomeResolvedLibraryResult {}
+
+/// The type of [InvalidResult] returned when the given file is not a library,
 /// but a part of a library.
 ///
 /// Clients may not extend, implement or mix-in this class.
@@ -311,6 +322,8 @@
 /// The result of building the element model for a single file.
 ///
 /// Clients may not extend, implement or mix-in this class.
+///
+/// TODO(scheglov) Stop implementing [FileResult].
 abstract class UnitElementResult implements SomeUnitElementResult, FileResult {
   /// The element of the file.
   CompilationUnitElement get element;
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index 3aa510f..8b58a3c 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -741,8 +741,14 @@
           return CannotResolveUriResult();
         }
 
-        if (file.isPart) {
+        final kind = file.kind;
+        if (kind is LibraryFileStateKind) {
+        } else if (kind is AugmentationFileStateKind) {
+          return NotLibraryButAugmentationResult();
+        } else if (kind is PartFileStateKind) {
           return NotLibraryButPartResult();
+        } else {
+          throw UnimplementedError('(${kind.runtimeType}) $kind');
         }
 
         var unitResult = await getUnitElement(file.path);
@@ -789,10 +795,15 @@
       return NotPathOfUriResult();
     }
 
-    FileState file = _fsState.getFileForPath(path);
-
-    if (file.isPart) {
+    final file = _fsState.getFileForPath(path);
+    final kind = file.kind;
+    if (kind is LibraryFileStateKind) {
+    } else if (kind is AugmentationFileStateKind) {
+      return NotLibraryButAugmentationResult();
+    } else if (kind is PartFileStateKind) {
       return NotLibraryButPartResult();
+    } else {
+      throw UnimplementedError('(${kind.runtimeType}) $kind');
     }
 
     var units = <ParsedUnitResult>[];
@@ -816,9 +827,6 @@
         if (file == null) {
           return CannotResolveUriResult();
         }
-        if (file.isPart) {
-          return NotLibraryButPartResult();
-        }
         return getParsedLibrary(file.path);
       },
       (externalLibrary) {
@@ -841,25 +849,24 @@
   /// state (including new states of the files previously reported using
   /// [changeFile]), prior to the next time the analysis state transitions
   /// to "idle".
-  Future<SomeResolvedLibraryResult> getResolvedLibrary(String path) {
+  Future<SomeResolvedLibraryResult> getResolvedLibrary(String path) async {
     if (!_isAbsolutePath(path)) {
-      return Future.value(
-        InvalidPathResult(),
-      );
+      return InvalidPathResult();
     }
 
     if (!_fsState.hasUri(path)) {
-      return Future.value(
-        NotPathOfUriResult(),
-      );
+      return NotPathOfUriResult();
     }
 
-    FileState file = _fsState.getFileForPath(path);
-
-    if (file.isPart) {
-      return Future.value(
-        NotLibraryButPartResult(),
-      );
+    final file = _fsState.getFileForPath(path);
+    final kind = file.kind;
+    if (kind is LibraryFileStateKind) {
+    } else if (kind is AugmentationFileStateKind) {
+      return NotLibraryButAugmentationResult();
+    } else if (kind is PartFileStateKind) {
+      return NotLibraryButPartResult();
+    } else {
+      throw UnimplementedError('(${kind.runtimeType}) $kind');
     }
 
     // Schedule analysis.
@@ -888,9 +895,6 @@
         if (file == null) {
           return CannotResolveUriResult();
         }
-        if (file.isPart) {
-          return NotLibraryButPartResult();
-        }
         return getResolvedLibrary(file.path);
       },
       (externalLibrary) async {
@@ -996,23 +1000,6 @@
     return completer.future;
   }
 
-  /// Return `true` is the file with the given absolute [uri] is a library,
-  /// or `false` if it is a part. More specifically, return `true` if the file
-  /// is not known to be a part.
-  ///
-  /// Correspondingly, return `true` if the [uri] does not correspond to a file,
-  /// for any reason, e.g. the file does not exist, or the [uri] cannot be
-  /// resolved to a file path, or the [uri] is invalid, e.g. a `package:` URI
-  /// without a package name. In these cases we cannot prove that the file is
-  /// not a part, so it must be a library.
-  bool isLibraryByUri(Uri uri) {
-    var fileOr = _fsState.getFileForUri(uri);
-    return fileOr.map(
-      (file) => file == null || !file.isPart,
-      (uri) => false,
-    );
-  }
-
   /// Return a [Future] that completes with a [ParsedUnitResult] for the file
   /// with the given [path].
   ///
diff --git a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
index 198bb5b..3b94d9b 100644
--- a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
+++ b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
@@ -42,9 +42,6 @@
 import 'package:meta/meta.dart';
 import 'package:yaml/yaml.dart';
 
-const M = 1024 * 1024 /*1 MiB*/;
-const memoryCacheSize = 200 * M;
-
 class CiderSearchInfo {
   final CharacterLocation startPosition;
   final int length;
@@ -129,24 +126,18 @@
   @visibleForTesting
   final Map<String, ResolvedLibraryResult> cachedResults = {};
 
-  FileResolver(
-    PerformanceLog logger,
-    ResourceProvider resourceProvider,
-    SourceFactory sourceFactory,
-    String Function(String path) getFileDigest,
-    void Function(List<String> paths)? prefetchFiles, {
-    required Workspace workspace,
-    bool Function(String path)? isGenerated,
-  }) : this.from(
-          logger: logger,
-          resourceProvider: resourceProvider,
-          sourceFactory: sourceFactory,
-          getFileDigest: getFileDigest,
-          prefetchFiles: prefetchFiles,
-          workspace: workspace,
-          isGenerated: isGenerated,
-        );
+  FileResolver({
+    required this.logger,
+    required this.resourceProvider,
+    required this.sourceFactory,
+    required this.getFileDigest,
+    required this.prefetchFiles,
+    required this.workspace,
+    this.isGenerated,
+    required this.byteStore,
+  });
 
+  @Deprecated('Use the unnamed constructor instead')
   FileResolver.from({
     required this.logger,
     required this.resourceProvider,
@@ -155,8 +146,8 @@
     required this.prefetchFiles,
     required this.workspace,
     this.isGenerated,
-    CiderByteStore? byteStore,
-  }) : byteStore = byteStore ?? CiderCachedByteStore(memoryCacheSize);
+    required this.byteStore,
+  });
 
   /// Update the resolver to reflect the fact that the file with the given
   /// [path] was changed. We need to make sure that when this file, of any file
diff --git a/pkg/analyzer/test/src/dart/analysis/driver_test.dart b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
index 7cc3008..ca5fff1 100644
--- a/pkg/analyzer/test/src/dart/analysis/driver_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
@@ -35,6 +35,7 @@
   defineReflectiveSuite(() {
     defineReflectiveTests(AnalysisDriverSchedulerTest);
     defineReflectiveTests(AnalysisDriverTest);
+    defineReflectiveTests(AnalysisDriver_PubPackageTest);
     defineReflectiveTests(AnalysisDriver_BazelWorkspaceTest);
   });
 }
@@ -102,6 +103,135 @@
 }
 
 @reflectiveTest
+class AnalysisDriver_PubPackageTest extends PubPackageResolutionTest {
+  test_getLibraryByUri_cannotResolveUri() async {
+    final driver = driverFor(testFile.path);
+    expect(
+      await driver.getLibraryByUri('foo:bar'),
+      isA<CannotResolveUriResult>(),
+    );
+  }
+
+  test_getLibraryByUri_notLibrary_augmentation() async {
+    final a = newFile('$testPackageLibPath/a.dart', r'''
+library augment 'b.dart';
+''');
+
+    final driver = driverFor(a.path);
+    expect(
+      await driver.getLibraryByUri('package:test/a.dart'),
+      isA<NotLibraryButAugmentationResult>(),
+    );
+  }
+
+  test_getLibraryByUri_notLibrary_part() async {
+    final a = newFile('$testPackageLibPath/a.dart', r'''
+part of 'b.dart';
+''');
+
+    final driver = driverFor(a.path);
+    expect(
+      await driver.getLibraryByUri('package:test/a.dart'),
+      isA<NotLibraryButPartResult>(),
+    );
+  }
+
+  test_getParsedLibraryByUri_cannotResolveUri() async {
+    final driver = driverFor(testFile.path);
+    final uri = Uri.parse('foo:bar');
+    expect(
+      driver.getParsedLibraryByUri(uri),
+      isA<CannotResolveUriResult>(),
+    );
+  }
+
+  test_getParsedLibraryByUri_notLibrary_augmentation() async {
+    final a = newFile('$testPackageLibPath/a.dart', r'''
+library augment 'b.dart';
+''');
+
+    final driver = driverFor(a.path);
+    final uri = Uri.parse('package:test/a.dart');
+    expect(
+      driver.getParsedLibraryByUri(uri),
+      isA<NotLibraryButAugmentationResult>(),
+    );
+  }
+
+  test_getParsedLibraryByUri_notLibrary_part() async {
+    final a = newFile('$testPackageLibPath/a.dart', r'''
+part of 'b.dart';
+''');
+
+    final driver = driverFor(a.path);
+    final uri = Uri.parse('package:test/a.dart');
+    expect(
+      driver.getParsedLibraryByUri(uri),
+      isA<NotLibraryButPartResult>(),
+    );
+  }
+
+  test_getResolvedLibrary_notLibrary_augmentation() async {
+    final a = newFile('$testPackageLibPath/a.dart', r'''
+library augment 'b.dart';
+''');
+
+    final driver = driverFor(a.path);
+    expect(
+      await driver.getResolvedLibrary(a.path),
+      isA<NotLibraryButAugmentationResult>(),
+    );
+  }
+
+  test_getResolvedLibrary_notLibrary_part() async {
+    final a = newFile('$testPackageLibPath/a.dart', r'''
+part of 'b.dart';
+''');
+
+    final driver = driverFor(a.path);
+    expect(
+      await driver.getResolvedLibrary(a.path),
+      isA<NotLibraryButPartResult>(),
+    );
+  }
+
+  test_getResolvedLibraryByUri_cannotResolveUri() async {
+    final driver = driverFor(testFile.path);
+    final uri = Uri.parse('foo:bar');
+    expect(
+      await driver.getResolvedLibraryByUri(uri),
+      isA<CannotResolveUriResult>(),
+    );
+  }
+
+  test_getResolvedLibraryByUri_notLibrary_augmentation() async {
+    final a = newFile('$testPackageLibPath/a.dart', r'''
+library augment 'b.dart';
+''');
+
+    final driver = driverFor(a.path);
+    final uri = Uri.parse('package:test/a.dart');
+    expect(
+      await driver.getResolvedLibraryByUri(uri),
+      isA<NotLibraryButAugmentationResult>(),
+    );
+  }
+
+  test_getResolvedLibraryByUri_notLibrary_part() async {
+    final a = newFile('$testPackageLibPath/a.dart', r'''
+part of 'b.dart';
+''');
+
+    final driver = driverFor(a.path);
+    final uri = Uri.parse('package:test/a.dart');
+    expect(
+      await driver.getResolvedLibraryByUri(uri),
+      isA<NotLibraryButPartResult>(),
+    );
+  }
+}
+
+@reflectiveTest
 class AnalysisDriverSchedulerTest with ResourceProviderMixin {
   final ByteStore byteStore = MemoryByteStore();
 
@@ -2329,16 +2459,6 @@
     await waitForIdleWithoutExceptions();
   }
 
-  test_isLibraryByUri_doesNotExist() async {
-    var uri = Uri.parse('file:///test.dart');
-    expect(driver.isLibraryByUri(uri), isTrue);
-  }
-
-  test_isLibraryByUri_invalidUri() async {
-    var uri = Uri.parse('package:aaa');
-    expect(driver.isLibraryByUri(uri), isTrue);
-  }
-
   test_issue34619() async {
     var a = convertPath('/test/lib/a.dart');
     newFile(a, r'''
diff --git a/pkg/analyzer/test/src/dart/analysis/session_test.dart b/pkg/analyzer/test/src/dart/analysis/session_test.dart
index ee74d7f..3a7a851 100644
--- a/pkg/analyzer/test/src/dart/analysis/session_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/session_test.dart
@@ -268,6 +268,26 @@
     );
   }
 
+  test_getLibraryByUri_notLibrary_augmentation() async {
+    newFile(testFilePath, r'''
+library augment 'a.dart';
+''');
+
+    final session = contextFor(testFilePath).currentSession;
+    final result = await session.getLibraryByUri('package:test/test.dart');
+    expect(result, isA<NotLibraryButAugmentationResult>());
+  }
+
+  test_getLibraryByUri_notLibrary_part() async {
+    newFile(testFilePath, r'''
+part of 'a.dart';
+''');
+
+    final session = contextFor(testFilePath).currentSession;
+    final result = await session.getLibraryByUri('package:test/test.dart');
+    expect(result, isA<NotLibraryButPartResult>());
+  }
+
   test_getLibraryByUri_unresolvedUri() async {
     var session = contextFor(testFilePath).currentSession;
     var result = await session.getLibraryByUri('package:foo/foo.dart');
@@ -402,6 +422,16 @@
     expect(session.getParsedLibrary(test.path), isA<NotLibraryButPartResult>());
   }
 
+  test_getParsedLibrary_notLibrary_augmentation() async {
+    newFile(testFilePath, r'''
+library augment 'a.dart';
+''');
+
+    final session = contextFor(testFilePath).currentSession;
+    final result = session.getParsedLibrary(testFilePath);
+    expect(result, isA<NotLibraryButAugmentationResult>());
+  }
+
   test_getParsedLibrary_parts() async {
     var aContent = r'''
 part 'b.dart';
@@ -661,7 +691,17 @@
     expect(result, isA<InvalidPathResult>());
   }
 
-  test_getResolvedLibrary_notLibrary() async {
+  test_getResolvedLibrary_notLibrary_augmentation() async {
+    newFile(testFilePath, r'''
+library augment of 'a.dart';
+''');
+
+    final session = contextFor(testFilePath).currentSession;
+    final result = await session.getResolvedLibrary(testFilePath);
+    expect(result, isA<NotLibraryButAugmentationResult>());
+  }
+
+  test_getResolvedLibrary_notLibrary_part() async {
     var test = newFile(testFilePath, 'part of "a.dart";');
 
     var session = contextFor(testFilePath).currentSession;
diff --git a/pkg/analyzer/test/src/dart/micro/file_resolution.dart b/pkg/analyzer/test/src/dart/micro/file_resolution.dart
index 51c7429..d9c67b6 100644
--- a/pkg/analyzer/test/src/dart/micro/file_resolution.dart
+++ b/pkg/analyzer/test/src/dart/micro/file_resolution.dart
@@ -50,7 +50,7 @@
     )!;
 
     byteStore.testView = CiderByteStoreTestView();
-    fileResolver = FileResolver.from(
+    fileResolver = FileResolver(
       logger: logger,
       resourceProvider: resourceProvider,
       byteStore: byteStore,
diff --git a/pkg/compiler/test/analyses/dart2js_allowed.json b/pkg/compiler/test/analyses/dart2js_allowed.json
index 2d86d0b..4ae46c3 100644
--- a/pkg/compiler/test/analyses/dart2js_allowed.json
+++ b/pkg/compiler/test/analyses/dart2js_allowed.json
@@ -104,11 +104,10 @@
     "Dynamic access of 'index'.": 1
   },
   "pkg/dart2js_info/lib/src/util.dart": {
-    "Dynamic access of 'name'.": 1,
     "Dynamic invocation of '-'.": 1
   },
   "pkg/js_ast/lib/src/template.dart": {
     "Dynamic invocation of '[]'.": 9,
     "Dynamic invocation of 'toStatement'.": 1
   }
-}
\ No newline at end of file
+}
diff --git a/pkg/dart2js_info/lib/src/util.dart b/pkg/dart2js_info/lib/src/util.dart
index f509ea6..48bb5d1 100644
--- a/pkg/dart2js_info/lib/src/util.dart
+++ b/pkg/dart2js_info/lib/src/util.dart
@@ -74,7 +74,7 @@
   final infoPath = <Info>[];
   Info? localInfo = info;
   while (localInfo != null) {
-    infoPath.add(info);
+    infoPath.add(localInfo);
     localInfo = localInfo.parent;
   }
   var sb = StringBuffer();
diff --git a/runtime/vm/message_snapshot.cc b/runtime/vm/message_snapshot.cc
index 48f6aec..a24ad62 100644
--- a/runtime/vm/message_snapshot.cc
+++ b/runtime/vm/message_snapshot.cc
@@ -2848,7 +2848,7 @@
     const intptr_t count = d->ReadUnsigned();
     for (intptr_t i = 0; i < count; i++) {
       intptr_t length = d->ReadUnsigned();
-      d->AssignRef(Array::New(cid_, length));
+      d->AssignRef(Array::NewUninitialized(cid_, length));
     }
   }
 
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 46d3e1b..52de6b1 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -2638,7 +2638,21 @@
         initial_value |= initial_value << 32;
       }
 #endif
-      needs_init = true;
+      if (class_id == kArrayCid) {
+        // If the size is greater than both kNewAllocatableSize and
+        // kAllocatablePageSize, the object must have been allocated to a new
+        // large page, which must already have been zero initialized by the OS.
+        // Zero is a GC-safe value. The caller will initialize the fields to
+        // null with safepoint checks to avoid blocking for the full duration of
+        // initializing this array.
+        needs_init = Heap::IsAllocatableInNewSpace(size) ||
+                     Heap::IsAllocatableViaFreeLists(size);
+        if (!needs_init) {
+          initial_value = 0;  // For ASSERT below.
+        }
+      } else {
+        needs_init = true;
+      }
     }
     if (needs_init) {
       while (cur < end) {
@@ -24321,17 +24335,6 @@
   return hash;
 }
 
-ArrayPtr Array::New(intptr_t len, Heap::Space space) {
-  ASSERT(IsolateGroup::Current()->object_store()->array_class() !=
-         Class::null());
-  ArrayPtr result = New(kClassId, len, space);
-  if (UseCardMarkingForAllocation(len)) {
-    ASSERT(result->IsOldObject());
-    result->untag()->SetCardRememberedBitUnsynchronized();
-  }
-  return result;
-}
-
 ArrayPtr Array::New(intptr_t len,
                     const AbstractType& element_type,
                     Heap::Space space) {
@@ -24345,7 +24348,9 @@
   return result.ptr();
 }
 
-ArrayPtr Array::New(intptr_t class_id, intptr_t len, Heap::Space space) {
+ArrayPtr Array::NewUninitialized(intptr_t class_id,
+                                 intptr_t len,
+                                 Heap::Space space) {
   if (!IsValidLength(len)) {
     // This should be caught before we reach here.
     FATAL1("Fatal error in Array::New: invalid len %" Pd "\n", len);
@@ -24356,23 +24361,56 @@
                          Array::ContainsCompressedPointers()));
     NoSafepointScope no_safepoint;
     raw->untag()->set_length(Smi::New(len));
+    if (UseCardMarkingForAllocation(len)) {
+      ASSERT(raw->IsOldObject());
+      raw->untag()->SetCardRememberedBitUnsynchronized();
+    }
     return raw;
   }
 }
 
+ArrayPtr Array::New(intptr_t class_id, intptr_t len, Heap::Space space) {
+  if (!UseCardMarkingForAllocation(len)) {
+    return NewUninitialized(class_id, len, space);
+  }
+
+  Thread* thread = Thread::Current();
+  Array& result =
+      Array::Handle(thread->zone(), NewUninitialized(class_id, len, space));
+  result.SetTypeArguments(Object::null_type_arguments());
+  for (intptr_t i = 0; i < len; i++) {
+    result.SetAt(i, Object::null_object(), thread);
+    if (((i + 1) % KB) == 0) {
+      thread->CheckForSafepoint();
+    }
+  }
+  return result.ptr();
+}
+
 ArrayPtr Array::Slice(intptr_t start,
                       intptr_t count,
                       bool with_type_argument) const {
-  // TODO(vegorov) introduce an array allocation method that fills newly
-  // allocated array with values from the given source array instead of
-  // null-initializing all elements.
-  Array& dest = Array::Handle(Array::New(count));
-  dest.StoreArrayPointers(dest.ObjectAddr(0), ObjectAddr(start), count);
-
+  Thread* thread = Thread::Current();
+  Zone* zone = thread->zone();
+  const Array& dest = Array::Handle(zone, Array::NewUninitialized(count));
   if (with_type_argument) {
-    dest.SetTypeArguments(TypeArguments::Handle(GetTypeArguments()));
+    dest.SetTypeArguments(TypeArguments::Handle(zone, GetTypeArguments()));
+  } else {
+    dest.SetTypeArguments(Object::null_type_arguments());
   }
-
+  if (!UseCardMarkingForAllocation(count)) {
+    NoSafepointScope no_safepoint(thread);
+    for (int i = 0; i < count; i++) {
+      dest.untag()->set_element(i, untag()->element(i + start), thread);
+    }
+  } else {
+    for (int i = 0; i < count; i++) {
+      dest.untag()->set_element(i, untag()->element(i + start), thread);
+      if (((i + 1) % KB) == 0) {
+        thread->CheckForSafepoint();
+      }
+    }
+  }
   return dest.ptr();
 }
 
@@ -24397,19 +24435,30 @@
                      Heap::Space space) {
   Thread* thread = Thread::Current();
   Zone* zone = thread->zone();
-  const Array& result = Array::Handle(zone, Array::New(new_length, space));
+  const Array& result =
+      Array::Handle(zone, Array::NewUninitialized(new_length, space));
   intptr_t len = 0;
   if (!source.IsNull()) {
     len = source.Length();
     result.SetTypeArguments(
         TypeArguments::Handle(zone, source.GetTypeArguments()));
+  } else {
+    result.SetTypeArguments(Object::null_type_arguments());
   }
   ASSERT(new_length >= len);  // Cannot copy 'source' into new array.
   ASSERT(new_length != len);  // Unnecessary copying of array.
-  PassiveObject& obj = PassiveObject::Handle(zone);
-  for (int i = 0; i < len; i++) {
-    obj = source.At(i);
-    result.SetAt(i, obj, thread);
+  if (!UseCardMarkingForAllocation(len)) {
+    NoSafepointScope no_safepoint(thread);
+    for (int i = 0; i < len; i++) {
+      result.untag()->set_element(i, source.untag()->element(i), thread);
+    }
+  } else {
+    for (int i = 0; i < len; i++) {
+      result.untag()->set_element(i, source.untag()->element(i), thread);
+      if (((i + 1) % KB) == 0) {
+        thread->CheckForSafepoint();
+      }
+    }
   }
   return result.ptr();
 }
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 6f226c3..17080ec 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -10419,7 +10419,15 @@
   // to ImmutableArray.
   void MakeImmutable() const;
 
-  static ArrayPtr New(intptr_t len, Heap::Space space = Heap::kNew);
+  static ArrayPtr New(intptr_t len, Heap::Space space = Heap::kNew) {
+    return New(kArrayCid, len, space);
+  }
+  // The result's type arguments and elements are GC-safe but not initialized to
+  // null.
+  static ArrayPtr NewUninitialized(intptr_t len,
+                                   Heap::Space space = Heap::kNew) {
+    return NewUninitialized(kArrayCid, len, space);
+  }
   static ArrayPtr New(intptr_t len,
                       const AbstractType& element_type,
                       Heap::Space space = Heap::kNew);
@@ -10457,6 +10465,9 @@
   static ArrayPtr New(intptr_t class_id,
                       intptr_t len,
                       Heap::Space space = Heap::kNew);
+  static ArrayPtr NewUninitialized(intptr_t class_id,
+                                   intptr_t len,
+                                   Heap::Space space = Heap::kNew);
 
  private:
   CompressedObjectPtr const* ObjectAddr(intptr_t index) const {
@@ -10477,25 +10488,6 @@
     ptr()->untag()->StoreArrayPointer<type, order, value_type>(addr, value);
   }
 
-  // Store a range of pointers [from, from + count) into [to, to + count).
-  // TODO(koda): Use this to fix Object::Clone's broken store buffer logic.
-  void StoreArrayPointers(CompressedObjectPtr const* to,
-                          CompressedObjectPtr const* from,
-                          intptr_t count) {
-    ASSERT(Contains(reinterpret_cast<uword>(to)));
-    if (ptr()->IsNewObject()) {
-      memmove(const_cast<CompressedObjectPtr*>(to), from,
-              count * kBytesPerElement);
-    } else {
-      Thread* thread = Thread::Current();
-      const uword heap_base = ptr()->heap_base();
-      for (intptr_t i = 0; i < count; ++i) {
-        untag()->StoreArrayPointer(&to[i], from[i].Decompress(heap_base),
-                                   thread);
-      }
-    }
-  }
-
   FINAL_HEAP_OBJECT_IMPLEMENTATION(Array, Instance);
   friend class Class;
   friend class ImmutableArray;
diff --git a/runtime/vm/object_graph_copy.cc b/runtime/vm/object_graph_copy.cc
index 43c35b3..560a7a7 100644
--- a/runtime/vm/object_graph_copy.cc
+++ b/runtime/vm/object_graph_copy.cc
@@ -471,27 +471,30 @@
  public:
   explicit SlowForwardMap(Thread* thread)
       : ForwardMapBase(thread),
-        from_to_(thread->zone(), 20),
+        from_to_transition_(thread->zone(), 2),
+        from_to_(GrowableObjectArray::Handle(thread->zone(),
+                                             GrowableObjectArray::New(2))),
         transferables_from_to_(thread->zone(), 0) {
-    from_to_.Resize(2);
-    from_to_[0] = &Object::null_object();
-    from_to_[1] = &Object::null_object();
+    from_to_transition_.Resize(2);
+    from_to_transition_[0] = &PassiveObject::Handle();
+    from_to_transition_[1] = &PassiveObject::Handle();
+    from_to_.Add(Object::null_object());
+    from_to_.Add(Object::null_object());
     fill_cursor_ = 2;
   }
 
   ObjectPtr ForwardedObject(ObjectPtr object) {
     const intptr_t id = GetObjectId(object);
     if (id == 0) return Marker();
-    return from_to_[id + 1]->ptr();
+    return from_to_.At(id + 1);
   }
 
-  void Insert(ObjectPtr from, ObjectPtr to, intptr_t size) {
-    ASSERT(ForwardedObject(from) == Marker());
-    const auto id = from_to_.length();
-    SetObjectId(from, id);
-    from_to_.Resize(id + 2);
-    from_to_[id] = &Object::Handle(Z, from);
-    from_to_[id + 1] = &Object::Handle(Z, to);
+  void Insert(const Object& from, const Object& to, intptr_t size) {
+    ASSERT(ForwardedObject(from.ptr()) == Marker());
+    const auto id = from_to_.Length();
+    SetObjectId(from.ptr(), id);
+    from_to_.Add(from);
+    from_to_.Add(to);
     allocated_bytes += size;
   }
 
@@ -537,7 +540,8 @@
   friend class SlowObjectCopy;
   friend class ObjectGraphCopier;
 
-  GrowableArray<const Object*> from_to_;
+  GrowableArray<const PassiveObject*> from_to_transition_;
+  GrowableObjectArray& from_to_;
   GrowableArray<const TransferableTypedData*> transferables_from_to_;
   GrowableArray<const ExternalTypedData*> external_typed_data_;
   GrowableArray<const Object*> objects_to_rehash_;
@@ -560,6 +564,7 @@
         class_table_(thread->isolate_group()->class_table()),
         new_space_(heap_->new_space()),
         tmp_(Object::Handle(thread->zone())),
+        to_(Object::Handle(thread->zone())),
         expando_cid_(Class::GetClassId(
             thread->isolate_group()->object_store()->expando_class())) {}
   ~ObjectCopyBase() {}
@@ -681,6 +686,7 @@
   ClassTable* class_table_;
   Scavenger* new_space_;
   Object& tmp_;
+  Object& to_;
   intptr_t expando_cid_;
 
   const char* exception_msg_ = nullptr;
@@ -990,9 +996,10 @@
     if (size == 0) {
       size = from.ptr().untag()->HeapSize();
     }
-    ObjectPtr to = AllocateObject(cid, size);
-    slow_forward_map_.Insert(from.ptr(), to, size);
-    UpdateLengthField(cid, from.ptr(), to);
+    to_ = AllocateObject(cid, size);
+    UpdateLengthField(cid, from.ptr(), to_.ptr());
+    slow_forward_map_.Insert(from, to_, size);  // SAFEPOINT
+    ObjectPtr to = to_.ptr();
     if (cid == kArrayCid && !Heap::IsAllocatableInNewSpace(size)) {
       to.untag()->SetCardRememberedBitUnsynchronized();
     }
@@ -1678,16 +1685,16 @@
     Object& to = Object::Handle(Z);
     while (true) {
       if (slow_forward_map_.fill_cursor_ ==
-          slow_forward_map_.from_to_.length()) {
+          slow_forward_map_.from_to_.Length()) {
         break;
       }
 
       // Run fixpoint to copy all objects.
       while (slow_forward_map_.fill_cursor_ <
-             slow_forward_map_.from_to_.length()) {
+             slow_forward_map_.from_to_.Length()) {
         const intptr_t index = slow_forward_map_.fill_cursor_;
-        from = slow_forward_map_.from_to_[index]->ptr();
-        to = slow_forward_map_.from_to_[index + 1]->ptr();
+        from = slow_forward_map_.from_to_.At(index);
+        to = slow_forward_map_.from_to_.At(index + 1);
         CopyObject(from, to);
         slow_forward_map_.fill_cursor_ += 2;
         if (exception_msg_ != nullptr) {
@@ -1915,6 +1922,8 @@
                                            /*compact=*/true);
       }
 
+      ObjectifyFromToObjects();
+
       // Fast copy failed due to
       //   - either failure to allocate into new space
       //   - or failure to copy object which we cannot copy
@@ -2021,20 +2030,28 @@
   void HandlifyFromToObjects() {
     auto& fast_forward_map = fast_object_copy_.fast_forward_map_;
     auto& slow_forward_map = slow_object_copy_.slow_forward_map_;
-
-    const intptr_t cursor = fast_forward_map.fill_cursor_;
     const intptr_t length = fast_forward_map.raw_from_to_.length();
-
-    slow_forward_map.from_to_.Resize(length);
-    for (intptr_t i = 2; i < length; i += 2) {
-      slow_forward_map.from_to_[i] =
-          i < cursor ? nullptr
-                     : &Object::Handle(Z, fast_forward_map.raw_from_to_[i]);
-      slow_forward_map.from_to_[i + 1] =
-          &Object::Handle(Z, fast_forward_map.raw_from_to_[i + 1]);
+    slow_forward_map.from_to_transition_.Resize(length);
+    for (intptr_t i = 0; i < length; i++) {
+      slow_forward_map.from_to_transition_[i] =
+          &PassiveObject::Handle(Z, fast_forward_map.raw_from_to_[i]);
     }
+    ASSERT(slow_forward_map.from_to_transition_.length() == length);
     fast_forward_map.raw_from_to_.Clear();
   }
+  void ObjectifyFromToObjects() {
+    auto& from_to_transition =
+        slow_object_copy_.slow_forward_map_.from_to_transition_;
+    auto& from_to = slow_object_copy_.slow_forward_map_.from_to_;
+    intptr_t length = from_to_transition.length();
+    from_to = GrowableObjectArray::New(length, Heap::kOld);
+    for (intptr_t i = 0; i < length; i++) {
+      from_to.Add(*from_to_transition[i]);
+    }
+    ASSERT(from_to.Length() == length);
+    from_to_transition.Clear();
+  }
+
   void ThrowException(const char* exception_msg) {
     const auto& msg_obj = String::Handle(Z, String::New(exception_msg));
     const auto& args = Array::Handle(Z, Array::New(1));
diff --git a/tools/VERSION b/tools/VERSION
index ca2aee4..02213d8 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 18
 PATCH 0
-PRERELEASE 161
+PRERELEASE 162
 PRERELEASE_PATCH 0
\ No newline at end of file