Version 2.14.0-392.0.dev

Merge commit 'd7c0271ac0d37f4c6391e77af3fcf987aaf47cfb' into 'dev'
diff --git a/DEPS b/DEPS
index e33bf57..d3facc6 100644
--- a/DEPS
+++ b/DEPS
@@ -133,7 +133,7 @@
   "mime_rev": "c931f4bed87221beaece356494b43731445ce7b8",
   "mockito_rev": "d39ac507483b9891165e422ec98d9fb480037c8b",
   "oauth2_rev": "7cd3284049fe5badbec9f2bea2afc41d14c01057",
-  "package_config_rev": "a84c0d45401f215fbe9384df923a38f4022a3c45",
+  "package_config_rev": "fb736aa12316dd2d882b202a438a6946a4b4bea0",
   "path_rev": "c20d73c3516d3a0061c90f14b761ff532b9bf707",
   "pedantic_rev": "66f2f6c27581c7936482e83be80b27be2719901c",
   "platform_rev": "c20e6fa315e9f8820e51c0ae721f63aff33b8e17",
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/remove_comparison.dart b/pkg/analysis_server/lib/src/services/correction/dart/remove_comparison.dart
index 77e8d88..d0d1f5f 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/remove_comparison.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/remove_comparison.dart
@@ -4,6 +4,7 @@
 
 import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
 import 'package:analysis_server/src/services/correction/fix.dart';
+import 'package:analysis_server/src/services/linter/lint_names.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/ast/token.dart';
 import 'package:analyzer/error/error.dart';
@@ -15,17 +16,30 @@
 
 class RemoveComparison extends CorrectionProducer {
   @override
+  bool canBeAppliedInBulk;
+
+  @override
+  bool canBeAppliedToFile;
+
+  RemoveComparison(this.canBeAppliedInBulk, this.canBeAppliedToFile);
+
+  @override
   FixKind get fixKind => DartFixKind.REMOVE_COMPARISON;
 
+  @override
+  FixKind get multiFixKind => DartFixKind.REMOVE_COMPARISON_MULTI;
+
   /// Return `true` if the null comparison will always return `false`.
   bool get _conditionIsFalse =>
       (diagnostic as AnalysisError).errorCode ==
       HintCode.UNNECESSARY_NULL_COMPARISON_FALSE;
 
   /// Return `true` if the null comparison will always return `true`.
-  bool get _conditionIsTrue =>
-      (diagnostic as AnalysisError).errorCode ==
-      HintCode.UNNECESSARY_NULL_COMPARISON_TRUE;
+  bool get _conditionIsTrue {
+    var errorCode = (diagnostic as AnalysisError).errorCode;
+    return errorCode == HintCode.UNNECESSARY_NULL_COMPARISON_TRUE ||
+        errorCode.name == LintNames.avoid_null_checks_in_equality_operators;
+  }
 
   @override
   Future<void> compute(ChangeBuilder builder) async {
@@ -108,5 +122,10 @@
   }
 
   /// Return an instance of this class. Used as a tear-off in `FixProcessor`.
-  static RemoveComparison newInstance() => RemoveComparison();
+  static RemoveComparison newInstance() => RemoveComparison(false, false);
+
+  /// Return an instance of this class that can apply bulk and in-file fixes.
+  /// Used as a tear-off in `FixProcessor`.
+  static RemoveComparison newInstanceBulkFixable() =>
+      RemoveComparison(true, true);
 }
diff --git a/pkg/analysis_server/lib/src/services/correction/fix.dart b/pkg/analysis_server/lib/src/services/correction/fix.dart
index ad30563..73c3779 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix.dart
@@ -126,8 +126,11 @@
       'dart.fix.add.neNull', DartFixKindPriority.DEFAULT, 'Add != null');
   static const ADD_NE_NULL_MULTI = FixKind('dart.fix.add.neNull.multi',
       DartFixKindPriority.IN_FILE, 'Add != null everywhere in file');
-  static const ADD_NULL_CHECK = FixKind('dart.fix.add.nullCheck',
-      DartFixKindPriority.DEFAULT - 1, 'Add a null check (!)',);
+  static const ADD_NULL_CHECK = FixKind(
+    'dart.fix.add.nullCheck',
+    DartFixKindPriority.DEFAULT - 1,
+    'Add a null check (!)',
+  );
   static const ADD_OVERRIDE = FixKind('dart.fix.add.override',
       DartFixKindPriority.DEFAULT, "Add '@override' annotation");
   static const ADD_OVERRIDE_MULTI = FixKind(
@@ -454,6 +457,10 @@
       DartFixKindPriority.IN_FILE, 'Remove awaits in file');
   static const REMOVE_COMPARISON = FixKind('dart.fix.remove.comparison',
       DartFixKindPriority.DEFAULT, 'Remove comparison');
+  static const REMOVE_COMPARISON_MULTI = FixKind(
+      'dart.fix.remove.comparison.multi',
+      DartFixKindPriority.IN_FILE,
+      'Remove comparisons in file');
   static const REMOVE_CONST = FixKind(
       'dart.fix.remove.const', DartFixKindPriority.DEFAULT, 'Remove const');
   static const REMOVE_DEAD_CODE = FixKind('dart.fix.remove.deadCode',
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index e4e975c..d5fd17e 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -344,6 +344,9 @@
     LintNames.avoid_init_to_null: [
       RemoveInitializer.newInstance,
     ],
+    LintNames.avoid_null_checks_in_equality_operators: [
+      RemoveComparison.newInstanceBulkFixable,
+    ],
     LintNames.avoid_private_typedef_functions: [
       InlineTypedef.newInstance,
     ],
diff --git a/pkg/analysis_server/lib/src/services/linter/lint_names.dart b/pkg/analysis_server/lib/src/services/linter/lint_names.dart
index 2d61eacc..0f5d7d0 100644
--- a/pkg/analysis_server/lib/src/services/linter/lint_names.dart
+++ b/pkg/analysis_server/lib/src/services/linter/lint_names.dart
@@ -16,6 +16,8 @@
   static const String avoid_function_literals_in_foreach_calls =
       'avoid_function_literals_in_foreach_calls';
   static const String avoid_init_to_null = 'avoid_init_to_null';
+  static const String avoid_null_checks_in_equality_operators =
+      'avoid_null_checks_in_equality_operators';
   static const String avoid_private_typedef_functions =
       'avoid_private_typedef_functions';
   static const String avoid_redundant_argument_values =
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_comparison_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_comparison_test.dart
index 5494406..2de13b2 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/remove_comparison_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_comparison_test.dart
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analysis_server/src/services/correction/fix.dart';
+import 'package:analysis_server/src/services/linter/lint_names.dart';
+import 'package:analyzer/src/error/codes.dart';
 import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -12,6 +14,8 @@
 void main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(RemoveComparisonTest);
+    defineReflectiveTests(RemoveNullCheckComparisonTest);
+    defineReflectiveTests(RemoveNullCheckComparisonBulkTest);
   });
 }
 
@@ -188,3 +192,219 @@
 ''');
   }
 }
+
+@reflectiveTest
+class RemoveNullCheckComparisonBulkTest extends BulkFixProcessorTest {
+  @override
+  String get lintCode => LintNames.avoid_null_checks_in_equality_operators;
+
+  Future<void> test_singleFile() async {
+    await resolveTestCode('''
+class Person {
+  final String name = '';
+
+  @override
+  operator ==(other) =>
+          other != null &&
+          other is Person &&
+          name == other.name;
+}
+
+class Person2 {
+  final String name = '';
+
+  @override
+  operator ==(other) =>
+          other != null &&
+          other is Person &&
+          name == other.name;
+}
+''');
+    await assertHasFix('''
+class Person {
+  final String name = '';
+
+  @override
+  operator ==(other) =>
+          other is Person &&
+          name == other.name;
+}
+
+class Person2 {
+  final String name = '';
+
+  @override
+  operator ==(other) =>
+          other is Person &&
+          name == other.name;
+}
+''');
+  }
+
+  @FailingTest(reason: 'Only the first comparison is removed')
+  Future<void> test_singleFile_overlapping() async {
+    await resolveTestCode('''
+class Person {
+  final String name = '';
+
+  @override
+  operator ==(other) =>
+          other != null &&
+          other != null &&
+          other is Person &&
+          name == other.name;
+}
+''');
+    await assertHasFix('''
+class Person {
+  final String name = '';
+
+  @override
+  operator ==(other) =>
+          other is Person &&
+          name == other.name;
+}
+''');
+  }
+}
+
+@reflectiveTest
+class RemoveNullCheckComparisonTest extends FixProcessorLintTest {
+  @override
+  FixKind get kind => DartFixKind.REMOVE_COMPARISON;
+
+  @override
+  String get lintCode => LintNames.avoid_null_checks_in_equality_operators;
+
+  Future<void> test_expressionBody() async {
+    await resolveTestCode('''
+class Person {
+  final String name = '';
+
+  @override
+  operator ==(other) =>
+          other != null &&
+          other is Person &&
+          name == other.name;
+}
+''');
+    await assertHasFix('''
+class Person {
+  final String name = '';
+
+  @override
+  operator ==(other) =>
+          other is Person &&
+          name == other.name;
+}
+''',
+        errorFilter: (error) =>
+            error.errorCode == HintCode.UNNECESSARY_NULL_COMPARISON_TRUE);
+  }
+
+  Future<void> test_functionBody() async {
+    await resolveTestCode('''
+class Person {
+  final String name = '';
+
+  @override
+  operator ==(other) {
+    return other != null &&
+          other is Person &&
+          name == other.name;
+  }
+}
+''');
+    await assertHasFix('''
+class Person {
+  final String name = '';
+
+  @override
+  operator ==(other) {
+    return other is Person &&
+          name == other.name;
+  }
+}
+''',
+        errorFilter: (error) =>
+            error.errorCode == HintCode.UNNECESSARY_NULL_COMPARISON_TRUE);
+  }
+
+  Future<void> test_ifNullAssignmentStatement() async {
+    await resolveTestCode('''
+class Person {
+  final String name = '';
+
+  @override
+  operator ==(other) {
+    if (other is! Person) return false;
+    other ??= Person();
+    return other.name == name;
+  }
+}
+''');
+    await assertNoFix();
+  }
+
+  /// todo(pq): consider implementing
+  @FailingTest(reason: 'Fix unimplemented')
+  Future<void> test_ifNullStatement() async {
+    await resolveTestCode('''
+class Person {
+  final String name = '';
+
+  @override
+  operator ==(other) {
+    if (other is! Person) return false;
+    final toCompare = other ?? Person();
+    return toCompare.name == name;
+  }
+}
+''');
+
+    await assertHasFix('''
+class Person {
+  final String name = '';
+
+  @override
+  operator ==(other) {
+    if (other is! Person) return false;
+    final toCompare = other;
+    return toCompare.name == name;
+  }
+}
+''',
+        errorFilter: (error) =>
+            error.errorCode == StaticWarningCode.DEAD_NULL_AWARE_EXPRESSION);
+  }
+
+  /// todo(pq): implement or remove the lint (see: https://github.com/dart-lang/linter/issues/2864)
+  @FailingTest(reason: 'Fix unimplemented')
+  Future<void> test_ifStatement() async {
+    await resolveTestCode('''
+class Person {
+  final String name = '';
+
+  @override
+  operator ==(other) {
+    if (other == null) return false;
+    return other is Person &&
+          name == other.name;
+  }
+}
+''');
+    await assertHasFix('''
+class Person {
+  final String name = '';
+
+  @override
+  operator ==(other) {
+    return other is Person &&
+          name == other.name;
+  }
+}
+''',
+        errorFilter: (error) =>
+            error.errorCode == HintCode.UNNECESSARY_NULL_COMPARISON_FALSE);
+  }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/const_eval_throws_exception_test.dart b/pkg/analyzer/test/src/diagnostics/const_eval_throws_exception_test.dart
index 077c88d..6c17ac2 100644
--- a/pkg/analyzer/test/src/diagnostics/const_eval_throws_exception_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/const_eval_throws_exception_test.dart
@@ -11,65 +11,47 @@
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(ConstEvalThrowsExceptionTest);
-    defineReflectiveTests(ConstEvalThrowsExceptionWithNullSafetyTest);
+    defineReflectiveTests(ConstEvalThrowsExceptionWithoutNullSafetyTest);
   });
 }
 
 @reflectiveTest
 class ConstEvalThrowsExceptionTest extends PubPackageResolutionTest
-    with WithoutNullSafetyMixin, ConstEvalThrowsExceptionTestCases {
-  test_binaryMinus_null() async {
+    with ConstEvalThrowsExceptionTestCases {
+  test_asExpression_typeParameter() async {
     await assertErrorsInCode('''
-const dynamic D = null;
-const C = D - 5;
-''', [
-      error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 34, 5),
-    ]);
-
-    await assertErrorsInCode('''
-const dynamic D = null;
-const C = 5 - D;
-''', [
-      error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 34, 5),
-    ]);
-  }
-
-  test_binaryPlus_null() async {
-    await assertErrorsInCode('''
-const dynamic D = null;
-const C = D + 5;
-''', [
-      error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 34, 5),
-    ]);
-
-    await assertErrorsInCode('''
-const dynamic D = null;
-const C = 5 + D;
-''', [
-      error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 34, 5),
-    ]);
-  }
-
-  test_eqEq_nonPrimitiveRightOperand() async {
-    await assertNoErrorsInCode('''
-const c = const T.eq(1, const Object());
-class T {
-  final Object value;
-  const T.eq(Object o1, Object o2) : value = o1 == o2;
+class C<T> {
+  final t;
+  const C(dynamic x) : t = x as T;
 }
-''');
-  }
-
-  test_fromEnvironment_ifElement() async {
-    await assertNoErrorsInCode('''
-const b = bool.fromEnvironment('foo');
 
 main() {
-  const l1 = [1, 2, 3];
-  const l2 = [if (b) ...l1];
-  print(l2);
+  const C<int>(0);
+  const C<int>('foo');
+  const C<int>(null);
 }
-''');
+''', [
+      error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 92, 19),
+      error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 115, 18),
+    ]);
+  }
+
+  test_asExpression_typeParameter_nested() async {
+    await assertErrorsInCode('''
+class C<T> {
+  final t;
+  const C(dynamic x) : t = x as List<T>;
+}
+
+main() {
+  const C<int>(<int>[]);
+  const C<int>(<num>[]);
+  const C<int>(null);
+}
+''', [
+      error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 104, 21),
+      error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 129, 18),
+    ]);
   }
 }
 
@@ -393,41 +375,60 @@
 }
 
 @reflectiveTest
-class ConstEvalThrowsExceptionWithNullSafetyTest
-    extends ConstEvalThrowsExceptionTest with WithNullSafetyMixin {
-  test_asExpression_typeParameter() async {
+class ConstEvalThrowsExceptionWithoutNullSafetyTest
+    extends PubPackageResolutionTest
+    with WithoutNullSafetyMixin, ConstEvalThrowsExceptionTestCases {
+  test_binaryMinus_null() async {
     await assertErrorsInCode('''
-class C<T> {
-  final t;
-  const C(dynamic x) : t = x as T;
-}
-
-main() {
-  const C<int>(0);
-  const C<int>('foo');
-  const C<int>(null);
-}
+const dynamic D = null;
+const C = D - 5;
 ''', [
-      error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 92, 19),
-      error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 115, 18),
+      error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 34, 5),
+    ]);
+
+    await assertErrorsInCode('''
+const dynamic D = null;
+const C = 5 - D;
+''', [
+      error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 34, 5),
     ]);
   }
 
-  test_asExpression_typeParameter_nested() async {
+  test_binaryPlus_null() async {
     await assertErrorsInCode('''
-class C<T> {
-  final t;
-  const C(dynamic x) : t = x as List<T>;
+const dynamic D = null;
+const C = D + 5;
+''', [
+      error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 34, 5),
+    ]);
+
+    await assertErrorsInCode('''
+const dynamic D = null;
+const C = 5 + D;
+''', [
+      error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 34, 5),
+    ]);
+  }
+
+  test_eqEq_nonPrimitiveRightOperand() async {
+    await assertNoErrorsInCode('''
+const c = const T.eq(1, const Object());
+class T {
+  final Object value;
+  const T.eq(Object o1, Object o2) : value = o1 == o2;
 }
+''');
+  }
+
+  test_fromEnvironment_ifElement() async {
+    await assertNoErrorsInCode('''
+const b = bool.fromEnvironment('foo');
 
 main() {
-  const C<int>(<int>[]);
-  const C<int>(<num>[]);
-  const C<int>(null);
+  const l1 = [1, 2, 3];
+  const l2 = [if (b) ...l1];
+  print(l2);
 }
-''', [
-      error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 104, 21),
-      error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 129, 18),
-    ]);
+''');
   }
 }
diff --git a/pkg/analyzer/test/src/diagnostics/const_not_initialized_test.dart b/pkg/analyzer/test/src/diagnostics/const_not_initialized_test.dart
index f96bfde..85b1174 100644
--- a/pkg/analyzer/test/src/diagnostics/const_not_initialized_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/const_not_initialized_test.dart
@@ -10,13 +10,15 @@
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(ConstNotInitializedTest);
-    defineReflectiveTests(ConstNotInitializedWithNullSafetyTest);
+    defineReflectiveTests(ConstNotInitializedWithoutNullSafetyTest);
   });
 }
 
 @reflectiveTest
 class ConstNotInitializedTest extends PubPackageResolutionTest
-    with WithoutNullSafetyMixin {
+    with ConstNotInitializedTestCases {}
+
+mixin ConstNotInitializedTestCases on PubPackageResolutionTest {
   test_extension_static() async {
     await assertErrorsInCode('''
 extension E on String {
@@ -57,5 +59,5 @@
 }
 
 @reflectiveTest
-class ConstNotInitializedWithNullSafetyTest extends ConstNotInitializedTest
-    with WithNullSafetyMixin {}
+class ConstNotInitializedWithoutNullSafetyTest extends PubPackageResolutionTest
+    with ConstNotInitializedTestCases, WithoutNullSafetyMixin {}
diff --git a/pkg/analyzer/test/src/diagnostics/implements_non_class_test.dart b/pkg/analyzer/test/src/diagnostics/implements_non_class_test.dart
index 7172e98..01890bb 100644
--- a/pkg/analyzer/test/src/diagnostics/implements_non_class_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/implements_non_class_test.dart
@@ -10,13 +10,23 @@
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(ImplementsNonClassTest);
-    defineReflectiveTests(ImplementsNonClassWithNullSafetyTest);
+    defineReflectiveTests(ImplementsNonClassWithoutNullSafetyTest);
   });
 }
 
 @reflectiveTest
 class ImplementsNonClassTest extends PubPackageResolutionTest
-    with WithoutNullSafetyMixin {
+    with ImplementsNonClassTestCases {
+  test_Never() async {
+    await assertErrorsInCode('''
+class A implements Never {}
+''', [
+      error(CompileTimeErrorCode.IMPLEMENTS_NON_CLASS, 19, 5),
+    ]);
+  }
+}
+
+mixin ImplementsNonClassTestCases on PubPackageResolutionTest {
   test_class() async {
     await assertErrorsInCode(r'''
 int A = 7;
@@ -56,13 +66,5 @@
 }
 
 @reflectiveTest
-class ImplementsNonClassWithNullSafetyTest extends ImplementsNonClassTest
-    with WithNullSafetyMixin {
-  test_Never() async {
-    await assertErrorsInCode('''
-class A implements Never {}
-''', [
-      error(CompileTimeErrorCode.IMPLEMENTS_NON_CLASS, 19, 5),
-    ]);
-  }
-}
+class ImplementsNonClassWithoutNullSafetyTest extends PubPackageResolutionTest
+    with ImplementsNonClassTestCases, WithoutNullSafetyMixin {}
diff --git a/pkg/analyzer/test/src/diagnostics/implicit_this_reference_in_initializer_test.dart b/pkg/analyzer/test/src/diagnostics/implicit_this_reference_in_initializer_test.dart
index 52e2b7c..3818269 100644
--- a/pkg/analyzer/test/src/diagnostics/implicit_this_reference_in_initializer_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/implicit_this_reference_in_initializer_test.dart
@@ -10,13 +10,61 @@
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(ImplicitThisReferenceInInitializerTest);
-    defineReflectiveTests(ImplicitThisReferenceInInitializerWithNullSafetyTest);
+    defineReflectiveTests(
+        ImplicitThisReferenceInInitializerWithoutNullSafetyTest);
   });
 }
 
 @reflectiveTest
 class ImplicitThisReferenceInInitializerTest extends PubPackageResolutionTest
-    with WithoutNullSafetyMixin {
+    with ImplicitThisReferenceInInitializerTestCases {
+  test_class_field_late_invokeInstanceMethod() async {
+    await assertNoErrorsInCode(r'''
+class A {
+  late int x = foo();
+  int foo() => 0;
+}
+''');
+  }
+
+  test_class_field_late_invokeStaticMethod() async {
+    await assertNoErrorsInCode(r'''
+class A {
+  late int x = foo();
+  static int foo() => 0;
+}
+''');
+  }
+
+  test_class_field_late_readInstanceField() async {
+    await assertNoErrorsInCode(r'''
+class A {
+  int a = 0;
+  late int x = a;
+}
+''');
+  }
+
+  test_class_field_late_readStaticField() async {
+    await assertNoErrorsInCode(r'''
+class A {
+  static int a = 0;
+  late int x = a;
+}
+''');
+  }
+
+  test_mixin_field_late_readInstanceField() async {
+    await assertNoErrorsInCode(r'''
+mixin M {
+  int a = 0;
+  late int x = a;
+}
+''');
+  }
+}
+
+mixin ImplicitThisReferenceInInitializerTestCases on PubPackageResolutionTest {
   test_class_field_commentReference_prefixedIdentifier() async {
     await assertNoErrorsInCode(r'''
 class A {
@@ -272,50 +320,6 @@
 }
 
 @reflectiveTest
-class ImplicitThisReferenceInInitializerWithNullSafetyTest
-    extends ImplicitThisReferenceInInitializerTest with WithNullSafetyMixin {
-  test_class_field_late_invokeInstanceMethod() async {
-    await assertNoErrorsInCode(r'''
-class A {
-  late int x = foo();
-  int foo() => 0;
-}
-''');
-  }
-
-  test_class_field_late_invokeStaticMethod() async {
-    await assertNoErrorsInCode(r'''
-class A {
-  late int x = foo();
-  static int foo() => 0;
-}
-''');
-  }
-
-  test_class_field_late_readInstanceField() async {
-    await assertNoErrorsInCode(r'''
-class A {
-  int a = 0;
-  late int x = a;
-}
-''');
-  }
-
-  test_class_field_late_readStaticField() async {
-    await assertNoErrorsInCode(r'''
-class A {
-  static int a = 0;
-  late int x = a;
-}
-''');
-  }
-
-  test_mixin_field_late_readInstanceField() async {
-    await assertNoErrorsInCode(r'''
-mixin M {
-  int a = 0;
-  late int x = a;
-}
-''');
-  }
-}
+class ImplicitThisReferenceInInitializerWithoutNullSafetyTest
+    extends PubPackageResolutionTest
+    with ImplicitThisReferenceInInitializerTestCases, WithoutNullSafetyMixin {}
diff --git a/pkg/analyzer/test/src/diagnostics/invalid_constant_test.dart b/pkg/analyzer/test/src/diagnostics/invalid_constant_test.dart
index 1eaf99d..78185db 100644
--- a/pkg/analyzer/test/src/diagnostics/invalid_constant_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/invalid_constant_test.dart
@@ -10,13 +10,24 @@
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(InvalidConstantTest);
-    defineReflectiveTests(InvalidConstantWithNullSafetyTest);
+    defineReflectiveTests(InvalidConstantWithoutNullSafetyTest);
   });
 }
 
 @reflectiveTest
 class InvalidConstantTest extends PubPackageResolutionTest
-    with WithoutNullSafetyMixin {
+    with InvalidConstantTestCases {
+  test_in_initializer_field_as() async {
+    await assertNoErrorsInCode('''
+class C<T> {
+  final l;
+  const C.test(dynamic x) : l = x as List<T>;
+}
+''');
+  }
+}
+
+mixin InvalidConstantTestCases on PubPackageResolutionTest {
   test_in_initializer_assert_condition() async {
     await assertErrorsInCode('''
 class A {
@@ -164,14 +175,5 @@
 }
 
 @reflectiveTest
-class InvalidConstantWithNullSafetyTest extends InvalidConstantTest
-    with WithNullSafetyMixin {
-  test_in_initializer_field_as() async {
-    await assertNoErrorsInCode('''
-class C<T> {
-  final l;
-  const C.test(dynamic x) : l = x as List<T>;
-}
-''');
-  }
-}
+class InvalidConstantWithoutNullSafetyTest extends PubPackageResolutionTest
+    with InvalidConstantTestCases, WithoutNullSafetyMixin {}
diff --git a/pkg/analyzer/test/src/diagnostics/invalid_implementation_override_test.dart b/pkg/analyzer/test/src/diagnostics/invalid_implementation_override_test.dart
index 7e0de60..a9578f5 100644
--- a/pkg/analyzer/test/src/diagnostics/invalid_implementation_override_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/invalid_implementation_override_test.dart
@@ -10,13 +10,15 @@
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(InvalidImplementationOverrideTest);
-    defineReflectiveTests(InvalidImplementationOverrideWithNullSafetyTest);
+    defineReflectiveTests(InvalidImplementationOverrideWithoutNullSafetyTest);
   });
 }
 
 @reflectiveTest
 class InvalidImplementationOverrideTest extends PubPackageResolutionTest
-    with WithoutNullSafetyMixin {
+    with InvalidImplementationOverrideTestCases {}
+
+mixin InvalidImplementationOverrideTestCases on PubPackageResolutionTest {
   test_getter_abstractOverridesConcrete() async {
     await assertErrorsInCode('''
 class A {
@@ -112,5 +114,6 @@
 }
 
 @reflectiveTest
-class InvalidImplementationOverrideWithNullSafetyTest
-    extends PubPackageResolutionTest with WithNullSafetyMixin {}
+class InvalidImplementationOverrideWithoutNullSafetyTest
+    extends PubPackageResolutionTest
+    with InvalidImplementationOverrideTestCases, WithoutNullSafetyMixin {}
diff --git a/pkg/analyzer/test/src/diagnostics/invalid_null_aware_operator_test.dart b/pkg/analyzer/test/src/diagnostics/invalid_null_aware_operator_test.dart
index 7c37ddd..a411785 100644
--- a/pkg/analyzer/test/src/diagnostics/invalid_null_aware_operator_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/invalid_null_aware_operator_test.dart
@@ -17,7 +17,7 @@
 
 @reflectiveTest
 class InvalidNullAwareOperatorAfterShortCircuitTest
-    extends PubPackageResolutionTest with WithNullSafetyMixin {
+    extends PubPackageResolutionTest {
   Future<void> test_getter_previousTarget() async {
     await assertErrorsInCode('''
 void f(String? s) {
diff --git a/pkg/analyzer/test/src/diagnostics/invalid_override_different_default_values_named_test.dart b/pkg/analyzer/test/src/diagnostics/invalid_override_different_default_values_named_test.dart
index 2c6d4f1..016d36b 100644
--- a/pkg/analyzer/test/src/diagnostics/invalid_override_different_default_values_named_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/invalid_override_different_default_values_named_test.dart
@@ -307,8 +307,7 @@
 
 @reflectiveTest
 class InvalidOverrideDifferentDefaultValuesNamedWithNullSafetyTest
-    extends InvalidOverrideDifferentDefaultValuesNamedTest
-    with WithNullSafetyMixin {
+    extends InvalidOverrideDifferentDefaultValuesNamedTest {
   test_concrete_equal_optIn_extends_optOut() async {
     newFile('$testPackageLibPath/a.dart', content: r'''
 // @dart = 2.7
diff --git a/pkg/analyzer/test/src/diagnostics/invalid_override_different_default_values_positional_test.dart b/pkg/analyzer/test/src/diagnostics/invalid_override_different_default_values_positional_test.dart
index c25b8e0..fb62381 100644
--- a/pkg/analyzer/test/src/diagnostics/invalid_override_different_default_values_positional_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/invalid_override_different_default_values_positional_test.dart
@@ -322,8 +322,7 @@
 
 @reflectiveTest
 class InvalidOverrideDifferentDefaultValuesPositionalWithNullSafetyTest
-    extends InvalidOverrideDifferentDefaultValuesPositionalTest
-    with WithNullSafetyMixin {
+    extends InvalidOverrideDifferentDefaultValuesPositionalTest {
   test_concrete_equal_optIn_extends_optOut() async {
     newFile('$testPackageLibPath/a.dart', content: r'''
 // @dart = 2.7
diff --git a/pkg/analyzer/test/src/diagnostics/invalid_reference_to_this_test.dart b/pkg/analyzer/test/src/diagnostics/invalid_reference_to_this_test.dart
index f58cb1a..b643469 100644
--- a/pkg/analyzer/test/src/diagnostics/invalid_reference_to_this_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/invalid_reference_to_this_test.dart
@@ -10,13 +10,39 @@
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(InvalidReferenceToThisTest);
-    defineReflectiveTests(InvalidReferenceToThisWithNullSafetyTest);
+    defineReflectiveTests(InvalidReferenceToThisWithoutNullSafetyTest);
   });
 }
 
 @reflectiveTest
 class InvalidReferenceToThisTest extends PubPackageResolutionTest
-    with WithoutNullSafetyMixin {
+    with InvalidReferenceToThisTestCases {
+  test_instanceVariableInitializer_inDeclaration_late() async {
+    await assertNoErrorsInCode(r'''
+class A {
+  late var f = this;
+}
+''');
+  }
+
+  test_mixinVariableInitializer_inDeclaration_late() async {
+    await assertNoErrorsInCode(r'''
+mixin A {
+  late var f = this;
+}
+''');
+  }
+
+  test_variableInitializer_late() async {
+    await assertErrorsInCode('''
+late var x = this;
+''', [
+      error(CompileTimeErrorCode.INVALID_REFERENCE_TO_THIS, 13, 4),
+    ]);
+  }
+}
+
+mixin InvalidReferenceToThisTestCases on PubPackageResolutionTest {
   test_class_constructor() async {
     await assertErrorsInCode(r'''
 class A {
@@ -121,29 +147,6 @@
 }
 
 @reflectiveTest
-class InvalidReferenceToThisWithNullSafetyTest
-    extends InvalidReferenceToThisTest with WithNullSafetyMixin {
-  test_instanceVariableInitializer_inDeclaration_late() async {
-    await assertNoErrorsInCode(r'''
-class A {
-  late var f = this;
-}
-''');
-  }
-
-  test_mixinVariableInitializer_inDeclaration_late() async {
-    await assertNoErrorsInCode(r'''
-mixin A {
-  late var f = this;
-}
-''');
-  }
-
-  test_variableInitializer_late() async {
-    await assertErrorsInCode('''
-late var x = this;
-''', [
-      error(CompileTimeErrorCode.INVALID_REFERENCE_TO_THIS, 13, 4),
-    ]);
-  }
-}
+class InvalidReferenceToThisWithoutNullSafetyTest
+    extends PubPackageResolutionTest
+    with InvalidReferenceToThisTestCases, WithoutNullSafetyMixin {}
diff --git a/pkg/analyzer/test/src/diagnostics/label_undefined_test.dart b/pkg/analyzer/test/src/diagnostics/label_undefined_test.dart
index abe2d6a..3231737 100644
--- a/pkg/analyzer/test/src/diagnostics/label_undefined_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/label_undefined_test.dart
@@ -10,13 +10,15 @@
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(LabelUndefinedTest);
-    defineReflectiveTests(LabelUndefinedWithNullSafetyTest);
+    defineReflectiveTests(LabelUndefinedWithoutNullSafetyTest);
   });
 }
 
 @reflectiveTest
 class LabelUndefinedTest extends PubPackageResolutionTest
-    with WithoutNullSafetyMixin {
+    with LabelUndefinedTestCases {}
+
+mixin LabelUndefinedTestCases on PubPackageResolutionTest {
   test_break() async {
     await assertErrorsInCode(r'''
 f() {
@@ -69,5 +71,5 @@
 }
 
 @reflectiveTest
-class LabelUndefinedWithNullSafetyTest extends LabelUndefinedTest
-    with WithNullSafetyMixin {}
+class LabelUndefinedWithoutNullSafetyTest extends PubPackageResolutionTest
+    with LabelUndefinedTestCases, WithoutNullSafetyMixin {}
diff --git a/pkg/dds/test/dap/integration/debug_exceptions_test.dart b/pkg/dds/test/dap/integration/debug_exceptions_test.dart
new file mode 100644
index 0000000..752ce5f
--- /dev/null
+++ b/pkg/dds/test/dap/integration/debug_exceptions_test.dart
@@ -0,0 +1,75 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:test/test.dart';
+
+import 'test_client.dart';
+import 'test_scripts.dart';
+import 'test_support.dart';
+
+main() {
+  group('debug mode', () {
+    late DapTestSession dap;
+    setUp(() async {
+      dap = await DapTestSession.setUp();
+    });
+    tearDown(() => dap.tearDown());
+
+    test('does not pause on exceptions if mode not set', () async {
+      final client = dap.client;
+      final testFile = dap.createTestFile(simpleThrowingProgram);
+
+      // Run the app and expect it to complete (it should not pause).
+      final outputEvents = await client.collectOutput(file: testFile);
+
+      // Expect error info printed to stderr.
+      final output = outputEvents
+          .where((e) => e.category == 'stderr')
+          .map((e) => e.output)
+          .join();
+      expectLinesStartWith(output, [
+        'Unhandled exception:',
+        'error',
+      ]);
+    });
+
+    test('pauses on uncaught exceptions when mode=Unhandled', () async {
+      final client = dap.client;
+      final testFile = dap.createTestFile(simpleThrowingProgram);
+
+      // Run and expect to pause on an exception.
+      await client.pauseOnException(
+        testFile,
+        exceptionPauseMode: 'Unhandled',
+      );
+    });
+
+    test('does not pauses on caught exceptions when mode=Unhandled', () async {
+      final client = dap.client;
+      final testFile = dap.createTestFile(simpleCaughtErrorProgram);
+
+      // Run the app and expect it to complete (it should not pause).
+      final outputEvents = await client.collectOutput(file: testFile);
+
+      // Expect error info printed to stderr.
+      final output = outputEvents
+          .where((e) => e.category == 'stdout')
+          .map((e) => e.output)
+          .join();
+      expectLines(output, ['Caught!']);
+    });
+
+    test('pauses on caught exceptions when mode=All', () async {
+      final client = dap.client;
+      final testFile = dap.createTestFile(simpleCaughtErrorProgram);
+
+      // Run and expect to pause on an exception.
+      await client.pauseOnException(
+        testFile,
+        exceptionPauseMode: 'All',
+      );
+    });
+    // These tests can be slow due to starting up the external server process.
+  }, timeout: Timeout.none);
+}
diff --git a/pkg/dds/test/dap/integration/test_client.dart b/pkg/dds/test/dap/integration/test_client.dart
index ffd3150..b7c9ecd 100644
--- a/pkg/dds/test/dap/integration/test_client.dart
+++ b/pkg/dds/test/dap/integration/test_client.dart
@@ -409,6 +409,31 @@
     return stop;
   }
 
+  /// Sets the exception pause mode to [pauseMode] and expects to pause after
+  /// running the script.
+  ///
+  /// Launch options can be customised by passing a custom [launch] function that
+  /// will be used instead of calling `launch(file.path)`.
+  Future<StoppedEventBody> pauseOnException(
+    File file, {
+    String? exceptionPauseMode, // All, Unhandled, None
+    Future<Response> Function()? launch,
+  }) async {
+    final stop = expectStop('exception', file: file);
+
+    await Future.wait([
+      initialize(),
+      sendRequest(
+        SetExceptionBreakpointsArguments(
+          filters: [if (exceptionPauseMode != null) exceptionPauseMode],
+        ),
+      ),
+      launch?.call() ?? this.launch(file.path),
+    ], eagerError: true);
+
+    return stop;
+  }
+
   /// Returns whether DDS is available for the VM Service the debug adapter
   /// is connected to.
   Future<bool> get ddsAvailable async {
diff --git a/pkg/dds/test/dap/integration/test_scripts.dart b/pkg/dds/test/dap/integration/test_scripts.dart
index 6b6a05b..0a5a492 100644
--- a/pkg/dds/test/dap/integration/test_scripts.dart
+++ b/pkg/dds/test/dap/integration/test_scripts.dart
@@ -15,3 +15,21 @@
     print('Hello!'); // BREAKPOINT
   }
 ''';
+
+/// A simple Dart script that throws an error and catches it in user code.
+const simpleCaughtErrorProgram = r'''
+  void main(List<String> args) async {
+    try {
+      throw 'error';
+    } catch (e) {
+      print('Caught!');
+    }
+  }
+''';
+
+/// A simple Dart script that throws in user code.
+const simpleThrowingProgram = r'''
+  void main(List<String> args) async {
+    throw 'error';
+  }
+''';
diff --git a/pkg/dds/test/dap/integration/test_support.dart b/pkg/dds/test/dap/integration/test_support.dart
index dc88974..d5d8372 100644
--- a/pkg/dds/test/dap/integration/test_support.dart
+++ b/pkg/dds/test/dap/integration/test_support.dart
@@ -31,9 +31,21 @@
 final vmServiceUriPattern = RegExp(r'Connecting to VM Service at ([^\s]+)\s');
 
 /// Expects [actual] to equal the lines [expected], ignoring differences in line
-/// endings.
+/// endings and trailing whitespace.
 void expectLines(String actual, List<String> expected) {
-  expect(actual.replaceAll('\r\n', '\n'), equals(expected.join('\n')));
+  expect(
+    actual.replaceAll('\r\n', '\n').trim(),
+    equals(expected.join('\n').trim()),
+  );
+}
+
+/// Expects [actual] starts with [expected], ignoring differences in line
+/// endings and trailing whitespace.
+void expectLinesStartWith(String actual, List<String> expected) {
+  expect(
+    actual.replaceAll('\r\n', '\n').trim(),
+    startsWith(expected.join('\n').trim()),
+  );
 }
 
 /// Returns the 1-base line in [file] that contains [searchText].
diff --git a/runtime/lib/isolate.cc b/runtime/lib/isolate.cc
index 0b72f7f..48cb890 100644
--- a/runtime/lib/isolate.cc
+++ b/runtime/lib/isolate.cc
@@ -299,8 +299,12 @@
   Object& validated_result = Object::Handle(zone);
   const Object& msg_obj = Object::Handle(zone, obj.ptr());
   validated_result = ValidateMessageObject(zone, isolate, msg_obj);
-  // msg_array = [<message>, <object-in-message-to-rehash>]
-  const Array& msg_array = Array::Handle(zone, Array::New(2));
+  // msg_array = [
+  //     <message>,
+  //     <collection-lib-objects-to-rehash>,
+  //     <core-lib-objects-to-rehash>,
+  // ]
+  const Array& msg_array = Array::Handle(zone, Array::New(3));
   msg_array.SetAt(0, msg_obj);
   if (validated_result.IsUnhandledException()) {
     Exceptions::PropagateError(Error::Cast(validated_result));
diff --git a/runtime/tests/vm/dart/isolates/fast_object_copy_test.dart b/runtime/tests/vm/dart/isolates/fast_object_copy_test.dart
index dda2b0f..4f71fae 100644
--- a/runtime/tests/vm/dart/isolates/fast_object_copy_test.dart
+++ b/runtime/tests/vm/dart/isolates/fast_object_copy_test.dart
@@ -2,7 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// VMOptions=
+// VMOptions=--no-enable-isolate-groups
 // VMOptions=--enable-isolate-groups --no-enable-fast-object-copy
 // VMOptions=--enable-isolate-groups --enable-fast-object-copy
 // VMOptions=--enable-isolate-groups --no-enable-fast-object-copy --gc-on-foc-slow-path --force-evacuation --verify-store-buffer
@@ -219,6 +219,8 @@
 
     await testFastOnly();
     await testSlowOnly();
+
+    await testWeakProperty();
   }
 
   Future testTransferrable() async {
@@ -579,6 +581,69 @@
           await sendReceive([notAllocatableInTLAB, smallContainer]));
     }
   }
+
+  Future testWeakProperty() async {
+    final key = Object();
+    final expando1 = Expando();
+    final expando2 = Expando();
+    final expando3 = Expando();
+    final expando4 = Expando();
+    expando1[key] = expando2;
+    expando2[expando2] = expando3;
+    expando3[expando3] = expando4;
+    expando4[expando4] = {'foo': 'bar'};
+
+    {
+      final result = await sendReceive([
+        key,
+        expando1,
+      ]);
+      final keyCopy = result[0];
+      final expando1Copy = result[1] as Expando;
+      final expando2Copy = expando1Copy[keyCopy] as Expando;
+      final expando3Copy = expando2Copy[expando2Copy] as Expando;
+      final expando4Copy = expando3Copy[expando3Copy] as Expando;
+      Expect.equals('bar', (expando4Copy[expando4Copy] as Map)['foo']);
+    }
+    {
+      final result = await sendReceive([
+        expando1,
+        key,
+      ]);
+      final expando1Copy = result[0] as Expando;
+      final keyCopy = result[1];
+      final expando2Copy = expando1Copy[keyCopy] as Expando;
+      final expando3Copy = expando2Copy[expando2Copy] as Expando;
+      final expando4Copy = expando3Copy[expando3Copy] as Expando;
+      Expect.equals('bar', (expando4Copy[expando4Copy] as Map)['foo']);
+    }
+    {
+      final result = await sendReceive([
+        expando1,
+        notAllocatableInTLAB,
+        key,
+      ]);
+      final expando1Copy = result[0] as Expando;
+      final keyCopy = result[2];
+      final expando2Copy = expando1Copy[keyCopy] as Expando;
+      final expando3Copy = expando2Copy[expando2Copy] as Expando;
+      final expando4Copy = expando3Copy[expando3Copy] as Expando;
+      Expect.equals('bar', (expando4Copy[expando4Copy] as Map)['foo']);
+    }
+    {
+      final result = await sendReceive([
+        key,
+        notAllocatableInTLAB,
+        expando1,
+      ]);
+      final keyCopy = result[0];
+      final expando1Copy = result[2] as Expando;
+      final expando2Copy = expando1Copy[keyCopy] as Expando;
+      final expando3Copy = expando2Copy[expando2Copy] as Expando;
+      final expando4Copy = expando3Copy[expando3Copy] as Expando;
+      Expect.equals('bar', (expando4Copy[expando4Copy] as Map)['foo']);
+    }
+  }
 }
 
 main() async {
diff --git a/runtime/tests/vm/dart_2/isolates/fast_object_copy_test.dart b/runtime/tests/vm/dart_2/isolates/fast_object_copy_test.dart
index 933283f..73b82f4 100644
--- a/runtime/tests/vm/dart_2/isolates/fast_object_copy_test.dart
+++ b/runtime/tests/vm/dart_2/isolates/fast_object_copy_test.dart
@@ -4,7 +4,7 @@
 
 // @dart = 2.9
 
-// VMOptions=
+// VMOptions=--no-enable-isolate-groups
 // VMOptions=--enable-isolate-groups --no-enable-fast-object-copy
 // VMOptions=--enable-isolate-groups --enable-fast-object-copy
 // VMOptions=--enable-isolate-groups --no-enable-fast-object-copy --gc-on-foc-slow-path --force-evacuation --verify-store-buffer
@@ -221,6 +221,8 @@
 
     await testFastOnly();
     await testSlowOnly();
+
+    await testWeakProperty();
   }
 
   Future testTransferrable() async {
@@ -581,6 +583,69 @@
           await sendReceive([notAllocatableInTLAB, smallContainer]));
     }
   }
+
+  Future testWeakProperty() async {
+    final key = Object();
+    final expando1 = Expando();
+    final expando2 = Expando();
+    final expando3 = Expando();
+    final expando4 = Expando();
+    expando1[key] = expando2;
+    expando2[expando2] = expando3;
+    expando3[expando3] = expando4;
+    expando4[expando4] = {'foo': 'bar'};
+
+    {
+      final result = await sendReceive([
+        key,
+        expando1,
+      ]);
+      final keyCopy = result[0];
+      final expando1Copy = result[1] as Expando;
+      final expando2Copy = expando1Copy[keyCopy] as Expando;
+      final expando3Copy = expando2Copy[expando2Copy] as Expando;
+      final expando4Copy = expando3Copy[expando3Copy] as Expando;
+      Expect.equals('bar', (expando4Copy[expando4Copy] as Map)['foo']);
+    }
+    {
+      final result = await sendReceive([
+        expando1,
+        key,
+      ]);
+      final expando1Copy = result[0] as Expando;
+      final keyCopy = result[1];
+      final expando2Copy = expando1Copy[keyCopy] as Expando;
+      final expando3Copy = expando2Copy[expando2Copy] as Expando;
+      final expando4Copy = expando3Copy[expando3Copy] as Expando;
+      Expect.equals('bar', (expando4Copy[expando4Copy] as Map)['foo']);
+    }
+    {
+      final result = await sendReceive([
+        expando1,
+        notAllocatableInTLAB,
+        key,
+      ]);
+      final expando1Copy = result[0] as Expando;
+      final keyCopy = result[2];
+      final expando2Copy = expando1Copy[keyCopy] as Expando;
+      final expando3Copy = expando2Copy[expando2Copy] as Expando;
+      final expando4Copy = expando3Copy[expando3Copy] as Expando;
+      Expect.equals('bar', (expando4Copy[expando4Copy] as Map)['foo']);
+    }
+    {
+      final result = await sendReceive([
+        key,
+        notAllocatableInTLAB,
+        expando1,
+      ]);
+      final keyCopy = result[0];
+      final expando1Copy = result[2] as Expando;
+      final expando2Copy = expando1Copy[keyCopy] as Expando;
+      final expando3Copy = expando2Copy[expando2Copy] as Expando;
+      final expando4Copy = expando3Copy[expando3Copy] as Expando;
+      Expect.equals('bar', (expando4Copy[expando4Copy] as Map)['foo']);
+    }
+  }
 }
 
 main() async {
diff --git a/runtime/vm/dart_entry.cc b/runtime/vm/dart_entry.cc
index f062909..8b2adff9 100644
--- a/runtime/vm/dart_entry.cc
+++ b/runtime/vm/dart_entry.cc
@@ -786,24 +786,36 @@
   return result.ptr();
 }
 
-ObjectPtr DartLibraryCalls::RehashObjects(
-    Thread* thread,
-    const Object& array_or_growable_array) {
+static ObjectPtr RehashObjects(Zone* zone,
+                               const Library& library,
+                               const Object& array_or_growable_array) {
   ASSERT(array_or_growable_array.IsArray() ||
          array_or_growable_array.IsGrowableObjectArray());
-
-  auto zone = thread->zone();
-  const Library& collections_lib =
-      Library::Handle(zone, Library::CollectionLibrary());
-  const Function& rehashing_function = Function::Handle(
-      zone,
-      collections_lib.LookupFunctionAllowPrivate(Symbols::_rehashObjects()));
+  const auto& rehashing_function = Function::Handle(
+      zone, library.LookupFunctionAllowPrivate(Symbols::_rehashObjects()));
   ASSERT(!rehashing_function.IsNull());
 
-  const Array& arguments = Array::Handle(zone, Array::New(1));
+  const auto& arguments = Array::Handle(zone, Array::New(1));
   arguments.SetAt(0, array_or_growable_array);
 
   return DartEntry::InvokeFunction(rehashing_function, arguments);
 }
 
+ObjectPtr DartLibraryCalls::RehashObjectsInDartCollection(
+    Thread* thread,
+    const Object& array_or_growable_array) {
+  auto zone = thread->zone();
+  const auto& collections_lib =
+      Library::Handle(zone, Library::CollectionLibrary());
+  return RehashObjects(zone, collections_lib, array_or_growable_array);
+}
+
+ObjectPtr DartLibraryCalls::RehashObjectsInDartCore(
+    Thread* thread,
+    const Object& array_or_growable_array) {
+  auto zone = thread->zone();
+  const auto& core_lib = Library::Handle(zone, Library::CoreLibrary());
+  return RehashObjects(zone, core_lib, array_or_growable_array);
+}
+
 }  // namespace dart
diff --git a/runtime/vm/dart_entry.h b/runtime/vm/dart_entry.h
index 5740337..c69d5c8 100644
--- a/runtime/vm/dart_entry.h
+++ b/runtime/vm/dart_entry.h
@@ -306,9 +306,15 @@
   // Returns null on success, an ErrorPtr on failure.
   static ObjectPtr EnsureScheduleImmediate();
 
-  // Runs the `_rehashObjects()` function.
-  static ObjectPtr RehashObjects(Thread* thread,
-                                 const Object& array_or_growable_array);
+  // Runs the `_rehashObjects()` function in `dart:collection`.
+  static ObjectPtr RehashObjectsInDartCollection(
+      Thread* thread,
+      const Object& array_or_growable_array);
+
+  // Runs the `_rehashObjects()` function in `dart:core`.
+  static ObjectPtr RehashObjectsInDartCore(
+      Thread* thread,
+      const Object& array_or_growable_array);
 };
 
 }  // namespace dart
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index b1a7315..458cc40 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -1329,14 +1329,29 @@
   // Parse the message.
   Object& msg_obj = Object::Handle(zone);
   if (message->IsPersistentHandle()) {
-    // msg_array = [<message>, <object-in-message-to-rehash>]
+    // msg_array = [
+    //     <message>,
+    //     <collection-lib-objects-to-rehash>,
+    //     <core-lib-objects-to-rehash>,
+    // ]
     const auto& msg_array = Array::Handle(
         zone, Array::RawCast(message->persistent_handle()->ptr()));
+    ASSERT(msg_array.Length() == 3);
     msg_obj = msg_array.At(0);
     if (msg_array.At(1) != Object::null()) {
       const auto& objects_to_rehash = Object::Handle(zone, msg_array.At(1));
-      const auto& result = Object::Handle(
-          zone, DartLibraryCalls::RehashObjects(thread, objects_to_rehash));
+      auto& result = Object::Handle(zone);
+      result = DartLibraryCalls::RehashObjectsInDartCollection(
+          thread, objects_to_rehash);
+      if (result.ptr() != Object::null()) {
+        msg_obj = result.ptr();
+      }
+    }
+    if (msg_array.At(2) != Object::null()) {
+      const auto& objects_to_rehash = Object::Handle(zone, msg_array.At(2));
+      auto& result = Object::Handle(zone);
+      result =
+          DartLibraryCalls::RehashObjectsInDartCore(thread, objects_to_rehash);
       if (result.ptr() != Object::null()) {
         msg_obj = result.ptr();
       }
diff --git a/runtime/vm/message_snapshot.cc b/runtime/vm/message_snapshot.cc
index b9400ee..c3ab4ec 100644
--- a/runtime/vm/message_snapshot.cc
+++ b/runtime/vm/message_snapshot.cc
@@ -483,7 +483,7 @@
   Array& maps = Array::Handle(d->zone(), d->refs());
   maps = maps.Slice(start_index_, stop_index_ - start_index_,
                     /*with_type_argument=*/false);
-  return DartLibraryCalls::RehashObjects(d->thread(), maps);
+  return DartLibraryCalls::RehashObjectsInDartCollection(d->thread(), maps);
 }
 
 class ClassMessageSerializationCluster : public MessageSerializationCluster {
@@ -916,18 +916,14 @@
     }
 
     if (cls_.ptr() == d->isolate_group()->object_store()->expando_class()) {
-      Instance& instance = Instance::Handle(d->zone());
-      const String& selector = Library::PrivateCoreLibName(Symbols::_rehash());
-      Array& args = Array::Handle(d->zone(), Array::New(1));
-      for (intptr_t i = start_index_; i < stop_index_; i++) {
+      const auto& expandos =
+          Array::Handle(d->zone(), Array::New(stop_index_ - start_index_));
+      auto& instance = Instance::Handle(d->zone());
+      for (intptr_t i = start_index_, j = 0; i < stop_index_; i++, j++) {
         instance ^= d->Ref(i);
-        args.SetAt(0, instance);
-        ObjectPtr error = instance.Invoke(selector, args, Object::empty_array(),
-                                          false, false);
-        if (error != Object::null()) {
-          return error;
-        }
+        expandos.SetAt(j, instance);
       }
+      return DartLibraryCalls::RehashObjectsInDartCore(d->thread(), expandos);
     }
     return nullptr;
   }
diff --git a/runtime/vm/object_graph_copy.cc b/runtime/vm/object_graph_copy.cc
index 7b9189f..ea1edf5 100644
--- a/runtime/vm/object_graph_copy.cc
+++ b/runtime/vm/object_graph_copy.cc
@@ -3,11 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 
 #include "vm/object_graph_copy.h"
+
 #include "vm/dart_api_state.h"
 #include "vm/flags.h"
 #include "vm/heap/weak_table.h"
 #include "vm/longjump.h"
 #include "vm/object.h"
+#include "vm/object_store.h"
 #include "vm/snapshot.h"
 #include "vm/symbols.h"
 
@@ -79,7 +81,6 @@
   V(UnlinkedCall)                                                              \
   V(UnwindError)                                                               \
   V(UserTag)                                                                   \
-  V(WeakProperty)                                                              \
   V(WeakSerializationReference)
 
 namespace dart {
@@ -353,7 +354,8 @@
       : ForwardMapBase(thread),
         raw_from_to_(thread->zone(), 20),
         raw_transferables_from_to_(thread->zone(), 0),
-        raw_objects_to_rehash_(thread->zone(), 0) {
+        raw_objects_to_rehash_(thread->zone(), 0),
+        raw_expandos_to_rehash_(thread->zone(), 0) {
     raw_from_to_.Resize(2);
     raw_from_to_[0] = Object::null();
     raw_from_to_[1] = Object::null();
@@ -381,11 +383,13 @@
     raw_transferables_from_to_.Add(from);
     raw_transferables_from_to_.Add(to);
   }
+  void AddWeakProperty(WeakPropertyPtr from) { raw_weak_properties_.Add(from); }
   void AddExternalTypedData(ExternalTypedDataPtr to) {
     raw_external_typed_data_to_.Add(to);
   }
 
   void AddObjectToRehash(ObjectPtr to) { raw_objects_to_rehash_.Add(to); }
+  void AddExpandoToRehash(ObjectPtr to) { raw_expandos_to_rehash_.Add(to); }
 
  private:
   friend class FastObjectCopy;
@@ -395,6 +399,8 @@
   GrowableArray<TransferableTypedDataPtr> raw_transferables_from_to_;
   GrowableArray<ExternalTypedDataPtr> raw_external_typed_data_to_;
   GrowableArray<ObjectPtr> raw_objects_to_rehash_;
+  GrowableArray<ObjectPtr> raw_expandos_to_rehash_;
+  GrowableArray<WeakPropertyPtr> raw_weak_properties_;
   intptr_t fill_cursor_ = 0;
 
   DISALLOW_COPY_AND_ASSIGN(FastForwardMap);
@@ -432,14 +438,18 @@
     transferables_from_to_.Add(&TransferableTypedData::Handle(from.ptr()));
     transferables_from_to_.Add(&TransferableTypedData::Handle(to.ptr()));
   }
-
+  void AddWeakProperty(const WeakProperty& from) {
+    weak_properties_.Add(&WeakProperty::Handle(from.ptr()));
+  }
   void AddExternalTypedData(ExternalTypedDataPtr to) {
     external_typed_data_.Add(&ExternalTypedData::Handle(to));
   }
-
   void AddObjectToRehash(const Object& to) {
     objects_to_rehash_.Add(&Object::Handle(to.ptr()));
   }
+  void AddExpandoToRehash(const Object& to) {
+    expandos_to_rehash_.Add(&Object::Handle(to.ptr()));
+  }
 
   void FinalizeTransferables() {
     for (intptr_t i = 0; i < transferables_from_to_.length(); i += 2) {
@@ -464,6 +474,8 @@
   GrowableArray<const TransferableTypedData*> transferables_from_to_;
   GrowableArray<const ExternalTypedData*> external_typed_data_;
   GrowableArray<const Object*> objects_to_rehash_;
+  GrowableArray<const Object*> expandos_to_rehash_;
+  GrowableArray<const WeakProperty*> weak_properties_;
   intptr_t fill_cursor_ = 0;
 
   DISALLOW_COPY_AND_ASSIGN(SlowForwardMap);
@@ -478,7 +490,9 @@
         heap_(thread->isolate_group()->heap()),
         class_table_(thread->isolate_group()->class_table()),
         new_space_(heap_->new_space()),
-        tmp_(Object::Handle(thread->zone())) {}
+        tmp_(Object::Handle(thread->zone())),
+        expando_cid_(Class::GetClassId(
+            thread->isolate_group()->object_store()->expando_class())) {}
   ~ObjectCopyBase() {}
 
  protected:
@@ -590,6 +604,7 @@
   ClassTable* class_table_;
   Scavenger* new_space_;
   Object& tmp_;
+  intptr_t expando_cid_;
 
   const char* exception_msg_ = nullptr;
 };
@@ -670,7 +685,7 @@
       return;
     }
 
-    auto to = Forward(tags, value.Decompress(heap_base_));
+    auto to = Forward(tags, value_decompressed);
     StoreCompressedPointerNoBarrier(dst, offset, to);
   }
 
@@ -708,9 +723,15 @@
                            TransferableTypedDataPtr to) {
     fast_forward_map_.AddTransferable(from, to);
   }
+  void EnqueueWeakProperty(WeakPropertyPtr from) {
+    fast_forward_map_.AddWeakProperty(from);
+  }
   void EnqueueObjectToRehash(ObjectPtr to) {
     fast_forward_map_.AddObjectToRehash(to);
   }
+  void EnqueueExpandoToRehash(ObjectPtr to) {
+    fast_forward_map_.AddExpandoToRehash(to);
+  }
 
   static void StoreCompressedArrayPointers(intptr_t array_length,
                                            ObjectPtr src,
@@ -888,9 +909,15 @@
                            const TransferableTypedData& to) {
     slow_forward_map_.AddTransferable(from, to);
   }
+  void EnqueueWeakProperty(const WeakProperty& from) {
+    slow_forward_map_.AddWeakProperty(from);
+  }
   void EnqueueObjectToRehash(const Object& to) {
     slow_forward_map_.AddObjectToRehash(to);
   }
+  void EnqueueExpandoToRehash(const Object& to) {
+    slow_forward_map_.AddExpandoToRehash(to);
+  }
 
   void StoreCompressedArrayPointers(intptr_t array_length,
                                     const Object& src,
@@ -1136,6 +1163,7 @@
     CopyLinkedHashBase<2, typename Types::LinkedHashMap>(
         from, to, UntagLinkedHashMap(from), UntagLinkedHashMap(to));
   }
+
   void CopyLinkedHashSet(typename Types::LinkedHashSet from,
                          typename Types::LinkedHashSet to) {
     CopyLinkedHashBase<1, typename Types::LinkedHashSet>(
@@ -1205,7 +1233,17 @@
     raw_to->offset_in_bytes_ = raw_from->offset_in_bytes_;
     raw_to->data_ = nullptr;
 
-    if (raw_to->typed_data_.Decompress(Base::heap_base_) == Object::null()) {
+    auto forwarded_backing_store =
+        raw_to->typed_data_.Decompress(Base::heap_base_);
+    if (forwarded_backing_store == Marker() ||
+        forwarded_backing_store == Object::null()) {
+      // Ensure the backing store is never "sentinel" - the scavenger doesn't
+      // like it.
+      Base::StoreCompressedPointerNoBarrier(
+          Types::GetTypedDataViewPtr(to),
+          OFFSET_OF(UntaggedTypedDataView, typed_data_), Object::null());
+      raw_to->length_ = 0;
+      raw_to->offset_in_bytes_ = 0;
       ASSERT(Base::exception_msg_ != nullptr);
       return;
     }
@@ -1257,6 +1295,20 @@
     Base::EnqueueTransferable(from, to);
   }
 
+  void CopyWeakProperty(typename Types::WeakProperty from,
+                        typename Types::WeakProperty to) {
+    // We store `null`s as keys/values and let the main algorithm know that
+    // we should check reachability of the key again after the fixpoint (if it
+    // became reachable, forward the key/value).
+    Base::StoreCompressedPointerNoBarrier(Types::GetWeakPropertyPtr(to),
+                                          OFFSET_OF(UntaggedWeakProperty, key_),
+                                          Object::null());
+    Base::StoreCompressedPointerNoBarrier(
+        Types::GetWeakPropertyPtr(to), OFFSET_OF(UntaggedWeakProperty, value_),
+        Object::null());
+    Base::EnqueueWeakProperty(from);
+  }
+
 #define DEFINE_UNSUPPORTED(clazz)                                              \
   void Copy##clazz(typename Types::clazz from, typename Types::clazz to) {     \
     FATAL("Objects of type " #clazz " should not occur in object graphs");     \
@@ -1291,33 +1343,85 @@
     if (root_copy == Marker()) {
       return root_copy;
     }
-    while (fast_forward_map_.fill_cursor_ <
-           fast_forward_map_.raw_from_to_.length()) {
-      const intptr_t index = fast_forward_map_.fill_cursor_;
-      ObjectPtr from = fast_forward_map_.raw_from_to_[index];
-      ObjectPtr to = fast_forward_map_.raw_from_to_[index + 1];
-      FastCopyObject(from, to);
-      if (exception_msg_ != nullptr) {
-        return root_copy;
+    auto& from_weak_property = WeakProperty::Handle(zone_);
+    auto& to_weak_property = WeakProperty::Handle(zone_);
+    auto& weak_property_key = Object::Handle(zone_);
+    while (true) {
+      if (fast_forward_map_.fill_cursor_ ==
+          fast_forward_map_.raw_from_to_.length()) {
+        break;
       }
-      fast_forward_map_.fill_cursor_ += 2;
+
+      // Run fixpoint to copy all objects.
+      while (fast_forward_map_.fill_cursor_ <
+             fast_forward_map_.raw_from_to_.length()) {
+        const intptr_t index = fast_forward_map_.fill_cursor_;
+        ObjectPtr from = fast_forward_map_.raw_from_to_[index];
+        ObjectPtr to = fast_forward_map_.raw_from_to_[index + 1];
+        FastCopyObject(from, to);
+        if (exception_msg_ != nullptr) {
+          return root_copy;
+        }
+        fast_forward_map_.fill_cursor_ += 2;
+      }
+
+      // Possibly forward values of [WeakProperty]s if keys became reachable.
+      intptr_t i = 0;
+      auto& weak_properties = fast_forward_map_.raw_weak_properties_;
+      while (i < weak_properties.length()) {
+        from_weak_property = weak_properties[i];
+        weak_property_key =
+            fast_forward_map_.ForwardedObject(from_weak_property.key());
+        if (weak_property_key.ptr() != Marker()) {
+          to_weak_property ^=
+              fast_forward_map_.ForwardedObject(from_weak_property.ptr());
+
+          // The key became reachable so we'll change the forwarded
+          // [WeakProperty]'s key to the new key (it is `null` at this point).
+          to_weak_property.set_key(weak_property_key);
+
+          // Since the key has become strongly reachable in the copied graph,
+          // we'll also need to forward the value.
+          ForwardCompressedPointer(from_weak_property.ptr(),
+                                   to_weak_property.ptr(),
+                                   OFFSET_OF(UntaggedWeakProperty, value_));
+
+          // We don't need to process this [WeakProperty] again.
+          const intptr_t last = weak_properties.length() - 1;
+          if (i < last) {
+            weak_properties[i] = weak_properties[last];
+            weak_properties.SetLength(last);
+            continue;
+          }
+        }
+        i++;
+      }
     }
     if (root_copy != Marker()) {
-      TryBuildArrayOfObjectsToRehash();
+      ObjectPtr array;
+      array = TryBuildArrayOfObjectsToRehash(
+          fast_forward_map_.raw_objects_to_rehash_);
+      if (array == Marker()) return root_copy;
+      raw_objects_to_rehash_ = Array::RawCast(array);
+
+      array = TryBuildArrayOfObjectsToRehash(
+          fast_forward_map_.raw_expandos_to_rehash_);
+      if (array == Marker()) return root_copy;
+      raw_expandos_to_rehash_ = Array::RawCast(array);
     }
     return root_copy;
   }
 
-  void TryBuildArrayOfObjectsToRehash() {
-    const auto& objects_to_rehash = fast_forward_map_.raw_objects_to_rehash_;
+  ObjectPtr TryBuildArrayOfObjectsToRehash(
+      const GrowableArray<ObjectPtr>& objects_to_rehash) {
     const intptr_t length = objects_to_rehash.length();
-    if (length == 0) return;
+    if (length == 0) return Object::null();
 
     const intptr_t size = Array::InstanceSize(length);
     const uword array_addr = new_space_->TryAllocate(thread_, size);
     if (array_addr == 0) {
       exception_msg_ = kFastAllocationFailed;
-      return;
+      return Marker();
     }
 
     const uword header_size =
@@ -1333,7 +1437,7 @@
     for (intptr_t i = 0; i < length; ++i) {
       array_data[i] = objects_to_rehash[i];
     }
-    raw_objects_to_rehash_ = array;
+    return array;
   }
 
  private:
@@ -1365,15 +1469,21 @@
 #else
     CopyUserdefinedInstance(Instance::RawCast(from), Instance::RawCast(to));
 #endif
+    if (cid == expando_cid_) {
+      EnqueueExpandoToRehash(to);
+    }
   }
 
   ArrayPtr raw_objects_to_rehash_ = Array::null();
+  ArrayPtr raw_expandos_to_rehash_ = Array::null();
 };
 
 class SlowObjectCopy : public ObjectCopy<SlowObjectCopyBase> {
  public:
   explicit SlowObjectCopy(Thread* thread)
-      : ObjectCopy(thread), objects_to_rehash_(Array::Handle(thread->zone())) {}
+      : ObjectCopy(thread),
+        objects_to_rehash_(Array::Handle(thread->zone())),
+        expandos_to_rehash_(Array::Handle(thread->zone())) {}
   ~SlowObjectCopy() {}
 
   ObjectPtr ContinueCopyGraphSlow(const Object& root,
@@ -1383,32 +1493,76 @@
       root_copy = Forward(TagsFromUntaggedObject(root.ptr().untag()), root);
     }
 
+    WeakProperty& weak_property = WeakProperty::Handle(Z);
     Object& from = Object::Handle(Z);
     Object& to = Object::Handle(Z);
-    while (slow_forward_map_.fill_cursor_ <
-           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();
-      CopyObject(from, to);
-      slow_forward_map_.fill_cursor_ += 2;
-      if (exception_msg_ != nullptr) {
-        return Marker();
+    while (true) {
+      if (slow_forward_map_.fill_cursor_ ==
+          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()) {
+        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();
+        CopyObject(from, to);
+        slow_forward_map_.fill_cursor_ += 2;
+        if (exception_msg_ != nullptr) {
+          return Marker();
+        }
+      }
+
+      // Possibly forward values of [WeakProperty]s if keys became reachable.
+      intptr_t i = 0;
+      auto& weak_properties = slow_forward_map_.weak_properties_;
+      while (i < weak_properties.length()) {
+        const auto& from_weak_property = *weak_properties[i];
+        to = slow_forward_map_.ForwardedObject(from_weak_property.key());
+        if (to.ptr() != Marker()) {
+          weak_property ^=
+              slow_forward_map_.ForwardedObject(from_weak_property.ptr());
+
+          // The key became reachable so we'll change the forwarded
+          // [WeakProperty]'s key to the new key (it is `null` at this point).
+          weak_property.set_key(to);
+
+          // Since the key has become strongly reachable in the copied graph,
+          // we'll also need to forward the value.
+          ForwardCompressedPointer(from_weak_property, weak_property,
+                                   OFFSET_OF(UntaggedWeakProperty, value_));
+
+          // We don't need to process this [WeakProperty] again.
+          const intptr_t last = weak_properties.length() - 1;
+          if (i < last) {
+            weak_properties[i] = weak_properties[last];
+            weak_properties.SetLength(last);
+            continue;
+          }
+        }
+        i++;
       }
     }
-    BuildArrayOfObjectsToRehash();
+
+    objects_to_rehash_ =
+        BuildArrayOfObjectsToRehash(slow_forward_map_.objects_to_rehash_);
+    expandos_to_rehash_ =
+        BuildArrayOfObjectsToRehash(slow_forward_map_.expandos_to_rehash_);
     return root_copy.ptr();
   }
 
-  void BuildArrayOfObjectsToRehash() {
-    const auto& objects_to_rehash = slow_forward_map_.objects_to_rehash_;
+  ArrayPtr BuildArrayOfObjectsToRehash(
+      const GrowableArray<const Object*>& objects_to_rehash) {
     const intptr_t length = objects_to_rehash.length();
-    if (length == 0) return;
+    if (length == 0) return Array::null();
 
-    objects_to_rehash_ = Array::New(length);
+    const auto& array = Array::Handle(zone_, Array::New(length));
     for (intptr_t i = 0; i < length; ++i) {
-      objects_to_rehash_.SetAt(i, *objects_to_rehash[i]);
+      array.SetAt(i, *objects_to_rehash[i]);
     }
+    return array.ptr();
   }
 
  private:
@@ -1429,9 +1583,13 @@
 #else
     CopyUserdefinedInstance(from, to);
 #endif
+    if (cid == expando_cid_) {
+      EnqueueExpandoToRehash(to);
+    }
   }
 
   Array& objects_to_rehash_;
+  Array& expandos_to_rehash_;
 };
 
 class ObjectGraphCopier {
@@ -1492,7 +1650,7 @@
  private:
   ObjectPtr CopyObjectGraphInternal(const Object& root,
                                     const char* volatile* exception_msg) {
-    const auto& result_array = Array::Handle(zone_, Array::New(2));
+    const auto& result_array = Array::Handle(zone_, Array::New(3));
     if (!root.ptr()->IsHeapObject()) {
       result_array.SetAt(0, root);
       return result_array.ptr();
@@ -1523,6 +1681,8 @@
             result_array.SetAt(0, result);
             fast_object_copy_.tmp_ = fast_object_copy_.raw_objects_to_rehash_;
             result_array.SetAt(1, fast_object_copy_.tmp_);
+            fast_object_copy_.tmp_ = fast_object_copy_.raw_expandos_to_rehash_;
+            result_array.SetAt(2, fast_object_copy_.tmp_);
             HandlifyExternalTypedData();
             HandlifyTransferables();
             return result_array.ptr();
@@ -1563,6 +1723,7 @@
 
     result_array.SetAt(0, result);
     result_array.SetAt(1, slow_object_copy_.objects_to_rehash_);
+    result_array.SetAt(2, slow_object_copy_.expandos_to_rehash_);
     return result_array.ptr();
   }
 
@@ -1572,8 +1733,10 @@
 
     MakeUninitializedNewSpaceObjectsGCSafe();
     HandlifyTransferables();
+    HandlifyWeakProperties();
     HandlifyExternalTypedData();
     HandlifyObjectsToReHash();
+    HandlifyExpandosToReHash();
     HandlifyFromToObjects();
     slow_forward_map.fill_cursor_ = fast_forward_map.fill_cursor_;
   }
@@ -1598,46 +1761,35 @@
     }
   }
   void HandlifyTransferables() {
-    auto& raw_transferables =
-        fast_object_copy_.fast_forward_map_.raw_transferables_from_to_;
-    const auto length = raw_transferables.length();
-    if (length > 0) {
-      auto& transferables =
-          slow_object_copy_.slow_forward_map_.transferables_from_to_;
-      transferables.Resize(length);
-      for (intptr_t i = 0; i < length; i++) {
-        transferables[i] =
-            &TransferableTypedData::Handle(Z, raw_transferables[i]);
-      }
-      raw_transferables.Clear();
-    }
+    Handlify(&fast_object_copy_.fast_forward_map_.raw_transferables_from_to_,
+             &slow_object_copy_.slow_forward_map_.transferables_from_to_);
+  }
+  void HandlifyWeakProperties() {
+    Handlify(&fast_object_copy_.fast_forward_map_.raw_weak_properties_,
+             &slow_object_copy_.slow_forward_map_.weak_properties_);
   }
   void HandlifyExternalTypedData() {
-    auto& raw_external_typed_data =
-        fast_object_copy_.fast_forward_map_.raw_external_typed_data_to_;
-    const auto length = raw_external_typed_data.length();
-    if (length > 0) {
-      auto& external_typed_data =
-          slow_object_copy_.slow_forward_map_.external_typed_data_;
-      external_typed_data.Resize(length);
-      for (intptr_t i = 0; i < length; i++) {
-        external_typed_data[i] =
-            &ExternalTypedData::Handle(Z, raw_external_typed_data[i]);
-      }
-      raw_external_typed_data.Clear();
-    }
+    Handlify(&fast_object_copy_.fast_forward_map_.raw_external_typed_data_to_,
+             &slow_object_copy_.slow_forward_map_.external_typed_data_);
   }
   void HandlifyObjectsToReHash() {
-    auto& fast_forward_map = fast_object_copy_.fast_forward_map_;
-    auto& slow_forward_map = slow_object_copy_.slow_forward_map_;
-    const auto length = fast_forward_map.raw_transferables_from_to_.length();
+    Handlify(&fast_object_copy_.fast_forward_map_.raw_objects_to_rehash_,
+             &slow_object_copy_.slow_forward_map_.objects_to_rehash_);
+  }
+  void HandlifyExpandosToReHash() {
+    Handlify(&fast_object_copy_.fast_forward_map_.raw_expandos_to_rehash_,
+             &slow_object_copy_.slow_forward_map_.expandos_to_rehash_);
+  }
+  template <typename RawType, typename HandleType>
+  void Handlify(GrowableArray<RawType>* from,
+                GrowableArray<const HandleType*>* to) {
+    const auto length = from->length();
     if (length > 0) {
-      slow_forward_map.objects_to_rehash_.Resize(length);
+      to->Resize(length);
       for (intptr_t i = 0; i < length; i++) {
-        slow_forward_map.objects_to_rehash_[i] =
-            &Object::Handle(Z, fast_forward_map.raw_objects_to_rehash_[i]);
+        (*to)[i] = &HandleType::Handle(Z, (*from)[i]);
       }
-      fast_forward_map.raw_objects_to_rehash_.Clear();
+      from->Clear();
     }
   }
   void HandlifyFromToObjects() {
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index e50a979..f99afbe 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -3206,6 +3206,8 @@
   friend class Scavenger;
   template <bool>
   friend class ScavengerVisitorBase;
+  friend class FastObjectCopy;  // For OFFSET_OF
+  friend class SlowObjectCopy;  // For OFFSET_OF
 };
 
 // MirrorReferences are used by mirrors to hold reflectees that are VM
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index 76d0070..03dd529 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -415,7 +415,6 @@
   V(_objectNoSuchMethod, "_objectNoSuchMethod")                                \
   V(_objectToString, "_objectToString")                                        \
   V(_onData, "_onData")                                                        \
-  V(_rehash, "_rehash")                                                        \
   V(_rehashObjects, "_rehashObjects")                                          \
   V(_resultOrListeners, "_resultOrListeners")                                  \
   V(_runExtension, "_runExtension")                                            \
diff --git a/runtime/vm/virtual_memory.h b/runtime/vm/virtual_memory.h
index 54ffc7b..c674d1c 100644
--- a/runtime/vm/virtual_memory.h
+++ b/runtime/vm/virtual_memory.h
@@ -90,6 +90,10 @@
   // can give back the virtual memory to the system. Returns true on success.
   static bool FreeSubSegment(void* address, intptr_t size);
 
+  static VirtualMemory* Reserve(intptr_t size, intptr_t alignment);
+  static void Commit(void* address, intptr_t size);
+  static void Decommit(void* address, intptr_t size);
+
   // These constructors are only used internally when reserving new virtual
   // spaces. They do not reserve any virtual address space on their own.
   VirtualMemory(const MemoryRegion& region,
@@ -112,6 +116,7 @@
   MemoryRegion reserved_;
 
   static uword page_size_;
+  static VirtualMemory* compressed_heap_;
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(VirtualMemory);
 };
diff --git a/runtime/vm/virtual_memory_posix.cc b/runtime/vm/virtual_memory_posix.cc
index 2d8757da..14ff79d 100644
--- a/runtime/vm/virtual_memory_posix.cc
+++ b/runtime/vm/virtual_memory_posix.cc
@@ -44,6 +44,7 @@
 #endif
 
 uword VirtualMemory::page_size_ = 0;
+VirtualMemory* VirtualMemory::compressed_heap_ = nullptr;
 
 static void unmap(uword start, uword end);
 
@@ -76,30 +77,22 @@
 }
 
 void VirtualMemory::Init() {
+  page_size_ = CalculatePageSize();
+
 #if defined(DART_COMPRESSED_POINTERS)
-  if (VirtualMemoryCompressedHeap::GetRegion() == nullptr) {
-    void* address = GenericMapAligned(
-        nullptr, PROT_NONE, kCompressedHeapSize, kCompressedHeapAlignment,
-        kCompressedHeapSize + kCompressedHeapAlignment,
-        MAP_PRIVATE | MAP_ANONYMOUS);
-    if (address == nullptr) {
+  if (compressed_heap_ == nullptr) {
+    compressed_heap_ = Reserve(kCompressedHeapSize, kCompressedHeapAlignment);
+    if (compressed_heap_ == nullptr) {
       int error = errno;
       const int kBufferSize = 1024;
       char error_buf[kBufferSize];
-      FATAL2("Failed to reserve region for compressed heap: %d (%s)", error,
-             Utils::StrError(error, error_buf, kBufferSize));
+      FATAL("Failed to reserve region for compressed heap: %d (%s)", error,
+            Utils::StrError(error, error_buf, kBufferSize));
     }
-    VirtualMemoryCompressedHeap::Init(address);
+    VirtualMemoryCompressedHeap::Init(compressed_heap_->address());
   }
 #endif  // defined(DART_COMPRESSED_POINTERS)
 
-  if (page_size_ != 0) {
-    // Already initialized.
-    return;
-  }
-
-  page_size_ = CalculatePageSize();
-
 #if defined(DUAL_MAPPING_SUPPORTED)
 // Perf is Linux-specific and the flags aren't defined in Product.
 #if defined(DART_TARGET_OS_LINUX) && !defined(PRODUCT)
@@ -159,9 +152,8 @@
 
 void VirtualMemory::Cleanup() {
 #if defined(DART_COMPRESSED_POINTERS)
-  uword heap_base =
-      reinterpret_cast<uword>(VirtualMemoryCompressedHeap::GetRegion());
-  unmap(heap_base, heap_base + kCompressedHeapSize);
+  delete compressed_heap_;
+  compressed_heap_ = nullptr;
   VirtualMemoryCompressedHeap::Cleanup();
 #endif  // defined(DART_COMPRESSED_POINTERS)
 }
@@ -259,7 +251,7 @@
     if (region.pointer() == nullptr) {
       return nullptr;
     }
-    mprotect(region.pointer(), region.size(), PROT_READ | PROT_WRITE);
+    Commit(region.pointer(), region.size());
     return new VirtualMemory(region, region);
   }
 #endif  // defined(DART_COMPRESSED_POINTERS)
@@ -359,10 +351,48 @@
   return new VirtualMemory(region, region);
 }
 
+VirtualMemory* VirtualMemory::Reserve(intptr_t size, intptr_t alignment) {
+  ASSERT(Utils::IsAligned(size, PageSize()));
+  ASSERT(Utils::IsPowerOfTwo(alignment));
+  ASSERT(Utils::IsAligned(alignment, PageSize()));
+  intptr_t allocated_size = size + alignment - PageSize();
+  void* address =
+      GenericMapAligned(nullptr, PROT_NONE, size, alignment, allocated_size,
+                        MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE);
+  if (address == nullptr) {
+    return nullptr;
+  }
+  MemoryRegion region(address, size);
+  return new VirtualMemory(region, region);
+}
+
+void VirtualMemory::Commit(void* address, intptr_t size) {
+  ASSERT(Utils::IsAligned(address, PageSize()));
+  ASSERT(Utils::IsAligned(size, PageSize()));
+  void* result = mmap(address, size, PROT_READ | PROT_WRITE,
+                      MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
+  if (result == MAP_FAILED) {
+    int error = errno;
+    FATAL("Failed to commit: %d\n", error);
+  }
+}
+
+void VirtualMemory::Decommit(void* address, intptr_t size) {
+  ASSERT(Utils::IsAligned(address, PageSize()));
+  ASSERT(Utils::IsAligned(size, PageSize()));
+  void* result =
+      mmap(address, size, PROT_NONE,
+           MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_FIXED, -1, 0);
+  if (result == MAP_FAILED) {
+    int error = errno;
+    FATAL("Failed to decommit: %d\n", error);
+  }
+}
+
 VirtualMemory::~VirtualMemory() {
 #if defined(DART_COMPRESSED_POINTERS)
   if (VirtualMemoryCompressedHeap::Contains(reserved_.pointer())) {
-    madvise(reserved_.pointer(), reserved_.size(), MADV_DONTNEED);
+    Decommit(reserved_.pointer(), reserved_.size());
     VirtualMemoryCompressedHeap::Free(reserved_.pointer(), reserved_.size());
     return;
   }
diff --git a/runtime/vm/virtual_memory_win.cc b/runtime/vm/virtual_memory_win.cc
index e352adc..2e380c0 100644
--- a/runtime/vm/virtual_memory_win.cc
+++ b/runtime/vm/virtual_memory_win.cc
@@ -17,6 +17,7 @@
 DECLARE_FLAG(bool, write_protect_code);
 
 uword VirtualMemory::page_size_ = 0;
+VirtualMemory* VirtualMemory::compressed_heap_ = nullptr;
 
 intptr_t VirtualMemory::CalculatePageSize() {
   SYSTEM_INFO info;
@@ -55,24 +56,21 @@
   page_size_ = CalculatePageSize();
 
 #if defined(DART_COMPRESSED_POINTERS)
-  if (VirtualMemoryCompressedHeap::GetRegion() == nullptr) {
-    void* address =
-        AllocateAlignedImpl(kCompressedHeapSize, kCompressedHeapAlignment,
-                            kCompressedHeapSize + kCompressedHeapAlignment,
-                            PAGE_READWRITE, nullptr);
-    if (address == nullptr) {
+  if (compressed_heap_ == nullptr) {
+    compressed_heap_ = Reserve(kCompressedHeapSize, kCompressedHeapAlignment);
+    if (compressed_heap_ == nullptr) {
       int error = GetLastError();
       FATAL("Failed to reserve region for compressed heap: %d", error);
     }
-    VirtualMemoryCompressedHeap::Init(address);
+    VirtualMemoryCompressedHeap::Init(compressed_heap_->address());
   }
 #endif  // defined(DART_COMPRESSED_POINTERS)
 }
 
 void VirtualMemory::Cleanup() {
 #if defined(DART_COMPRESSED_POINTERS)
-  void* heap_base = VirtualMemoryCompressedHeap::GetRegion();
-  VirtualFree(heap_base, kCompressedHeapSize, MEM_RELEASE);
+  delete compressed_heap_;
+  compressed_heap_ = nullptr;
   VirtualMemoryCompressedHeap::Cleanup();
 #endif  // defined(DART_COMPRESSED_POINTERS)
 }
@@ -99,6 +97,7 @@
     if (region.pointer() == nullptr) {
       return nullptr;
     }
+    Commit(region.pointer(), region.size());
     return new VirtualMemory(region, region);
   }
 #endif  // defined(DART_COMPRESSED_POINTERS)
@@ -120,6 +119,44 @@
   return new VirtualMemory(region, reserved);
 }
 
+VirtualMemory* VirtualMemory::Reserve(intptr_t size, intptr_t alignment) {
+  ASSERT(Utils::IsAligned(size, PageSize()));
+  ASSERT(Utils::IsPowerOfTwo(alignment));
+  ASSERT(Utils::IsAligned(alignment, PageSize()));
+  intptr_t reserved_size = size + alignment - PageSize();
+  void* reserved_address =
+      VirtualAlloc(nullptr, reserved_size, MEM_RESERVE, PAGE_NOACCESS);
+  if (reserved_address == nullptr) {
+    return nullptr;
+  }
+
+  void* aligned_address = reinterpret_cast<void*>(
+      Utils::RoundUp(reinterpret_cast<uword>(reserved_address), alignment));
+  MemoryRegion region(aligned_address, size);
+  MemoryRegion reserved(reserved_address, reserved_size);
+  return new VirtualMemory(region, reserved);
+}
+
+void VirtualMemory::Commit(void* address, intptr_t size) {
+  ASSERT(Utils::IsAligned(address, PageSize()));
+  ASSERT(Utils::IsAligned(size, PageSize()));
+  void* result = VirtualAlloc(address, size, MEM_COMMIT, PAGE_READWRITE);
+  if (result == nullptr) {
+    int error = GetLastError();
+    FATAL("Failed to commit: %d\n", error);
+  }
+}
+
+void VirtualMemory::Decommit(void* address, intptr_t size) {
+  ASSERT(Utils::IsAligned(address, PageSize()));
+  ASSERT(Utils::IsAligned(size, PageSize()));
+  bool result = VirtualFree(address, size, MEM_DECOMMIT);
+  if (!result) {
+    int error = GetLastError();
+    FATAL("Failed to decommit: %d\n", error);
+  }
+}
+
 VirtualMemory::~VirtualMemory() {
   // Note that the size of the reserved region might be set to 0 by
   // Truncate(0, true) but that does not actually release the mapping
@@ -127,6 +164,7 @@
   // with original base pointer and MEM_RELEASE.
 #if defined(DART_COMPRESSED_POINTERS)
   if (VirtualMemoryCompressedHeap::Contains(reserved_.pointer())) {
+    Decommit(reserved_.pointer(), reserved_.size());
     VirtualMemoryCompressedHeap::Free(reserved_.pointer(), reserved_.size());
     return;
   }
diff --git a/sdk/lib/_internal/vm/lib/expando_patch.dart b/sdk/lib/_internal/vm/lib/expando_patch.dart
index 36c65df..f707102 100644
--- a/sdk/lib/_internal/vm/lib/expando_patch.dart
+++ b/sdk/lib/_internal/vm/lib/expando_patch.dart
@@ -4,6 +4,16 @@
 
 // part of "core_patch.dart";
 
+// This function takes care of rehashing of the expandos in [objects]. We
+// do this eagerly after snapshot deserialization.
+@pragma("vm:entry-point", "call")
+void _rehashObjects(List objects) {
+  final int length = objects.length;
+  for (int i = 0; i < length; ++i) {
+    objects[i]._rehash();
+  }
+}
+
 @patch
 @pragma("vm:entry-point")
 class Expando<T> {
@@ -96,7 +106,6 @@
     this[object] = value; // Recursively add the value.
   }
 
-  @pragma("vm:entry-point", "call")
   _rehash() {
     // Determine the population count of the map to allocate an appropriately
     // sized map below.
diff --git a/tests/lib/isolate/weak_property_message_1_test.dart b/tests/lib/isolate/weak_property_message_1_test.dart
index 2f4d344..4946222 100644
--- a/tests/lib/isolate/weak_property_message_1_test.dart
+++ b/tests/lib/isolate/weak_property_message_1_test.dart
@@ -4,7 +4,9 @@
 
 // See https://github.com/dart-lang/sdk/issues/25559
 
-import "dart:async";
+// VMOptions=--no-enable-isolate-groups
+// VMOptions=--enable-isolate-groups
+
 import "dart:isolate";
 
 import "package:async_helper/async_helper.dart";
@@ -30,7 +32,6 @@
     asyncEnd();
   });
 
-
   var key1 = new Object();
   var key2 = new Object();
   var key3 = new Object();
diff --git a/tests/lib/isolate/weak_property_message_2_test.dart b/tests/lib/isolate/weak_property_message_2_test.dart
index 236bcfe3..fd0dbc6 100644
--- a/tests/lib/isolate/weak_property_message_2_test.dart
+++ b/tests/lib/isolate/weak_property_message_2_test.dart
@@ -4,12 +4,13 @@
 
 // See https://github.com/dart-lang/sdk/issues/25559
 
-import "dart:async";
+// VMOptions=--no-enable-isolate-groups
+// VMOptions=--enable-isolate-groups
+
 import "dart:developer";
 import "dart:isolate";
 
 import "package:async_helper/async_helper.dart";
-import "package:expect/expect.dart";
 
 main() {
   asyncStart();
@@ -23,12 +24,11 @@
     asyncEnd();
   });
 
-
   var unwrittenKey = new Object();
   var expando = new Expando();
   expando[unwrittenKey] = new UserTag("cant send this");
 
   port.sendPort.send(expando);
 
-  print(unwrittenKey);  // Ensure [unwrittenKey] is live during [send].
+  print(unwrittenKey); // Ensure [unwrittenKey] is live during [send].
 }
diff --git a/tests/lib_2/isolate/weak_property_message_1_test.dart b/tests/lib_2/isolate/weak_property_message_1_test.dart
index fa8a6d1..b58e261 100644
--- a/tests/lib_2/isolate/weak_property_message_1_test.dart
+++ b/tests/lib_2/isolate/weak_property_message_1_test.dart
@@ -6,7 +6,9 @@
 
 // @dart = 2.9
 
-import "dart:async";
+// VMOptions=--no-enable-isolate-groups
+// VMOptions=--enable-isolate-groups
+
 import "dart:isolate";
 
 import "package:async_helper/async_helper.dart";
@@ -32,7 +34,6 @@
     asyncEnd();
   });
 
-
   var key1 = new Object();
   var key2 = new Object();
   var key3 = new Object();
diff --git a/tests/lib_2/isolate/weak_property_message_2_test.dart b/tests/lib_2/isolate/weak_property_message_2_test.dart
index 7b5ad95..353a8f5 100644
--- a/tests/lib_2/isolate/weak_property_message_2_test.dart
+++ b/tests/lib_2/isolate/weak_property_message_2_test.dart
@@ -6,12 +6,13 @@
 
 // @dart = 2.9
 
-import "dart:async";
+// VMOptions=--no-enable-isolate-groups
+// VMOptions=--enable-isolate-groups
+
 import "dart:developer";
 import "dart:isolate";
 
 import "package:async_helper/async_helper.dart";
-import "package:expect/expect.dart";
 
 main() {
   asyncStart();
@@ -25,12 +26,11 @@
     asyncEnd();
   });
 
-
   var unwrittenKey = new Object();
   var expando = new Expando();
   expando[unwrittenKey] = new UserTag("cant send this");
 
   port.sendPort.send(expando);
 
-  print(unwrittenKey);  // Ensure [unwrittenKey] is live during [send].
+  print(unwrittenKey); // Ensure [unwrittenKey] is live during [send].
 }
diff --git a/tools/VERSION b/tools/VERSION
index d820e4a..766dd7a 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 14
 PATCH 0
-PRERELEASE 391
+PRERELEASE 392
 PRERELEASE_PATCH 0
\ No newline at end of file