Version 2.15.0-159.0.dev

Merge commit 'fa428da3477061de50edab069384359ab677332d' into 'dev'
diff --git a/build/config/clang/clang.gni b/build/config/clang/clang.gni
index 50bdb34..c2ba3f7 100644
--- a/build/config/clang/clang.gni
+++ b/build/config/clang/clang.gni
@@ -2,8 +2,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+_toolchain_cpu = host_cpu
+if (host_os == "mac") {
+  # TODO(https://fxbug.dev/73385): Use arm64 toolchain on arm64 when it exists.
+  _toolchain_cpu = "x64"
+}
+
 default_clang_prefix =
-    rebase_path("//buildtools/${host_os}-${host_cpu}/clang/bin", root_build_dir)
+    rebase_path("//buildtools/${host_os}-${_toolchain_cpu}/clang/bin",
+                root_build_dir)
 
 declare_args() {
   clang_prefix = default_clang_prefix
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_stateful_widget.dart b/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_stateful_widget.dart
index 6c9e4d0..34ecd3a 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_stateful_widget.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_stateful_widget.dart
@@ -92,8 +92,7 @@
             }
           }
         }
-      }
-      if (member is MethodDeclaration && !member.isStatic) {
+      } else if (member is MethodDeclaration && !member.isStatic) {
         nodesToMove.add(member);
         elementsToMove.add(member.declaredElement!);
       }
@@ -177,8 +176,9 @@
       for (var node in widgetClass.members) {
         if (nodesToMove.contains(node)) {
           if (replaceOffset == 0) {
-            var linesRange = utils.getLinesRange(range.node(node));
-            replaceOffset = linesRange.offset;
+            var comments = node.beginToken.precedingComments;
+            var start = comments ?? node;
+            replaceOffset = utils.getLineContentStart(start.offset);
           }
           if (node == buildMethod) {
             hasBuildMethod = true;
@@ -236,6 +236,14 @@
           if (writeEmptyLine) {
             builder.writeln();
           }
+
+          var comments = member.beginToken.precedingComments;
+          if (comments != null) {
+            var offset = utils.getLineContentStart(comments.offset);
+            var length = comments.end - offset;
+            builder.writeln(utils.getText(offset, length));
+          }
+
           var text = rewriteWidgetMemberReferences(member);
           builder.write(text);
           // Write empty lines between members, but not before the first.
diff --git a/pkg/analysis_server/test/src/services/correction/assist/flutter_convert_to_stateful_widget_test.dart b/pkg/analysis_server/test/src/services/correction/assist/flutter_convert_to_stateful_widget_test.dart
index 02ce651..dfb00eb 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/flutter_convert_to_stateful_widget_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/flutter_convert_to_stateful_widget_test.dart
@@ -27,6 +27,96 @@
     );
   }
 
+  Future<void> test_comment() async {
+    await resolveTestCode(r'''
+import 'package:flutter/material.dart';
+
+class /*caret*/MyWidget extends StatelessWidget {
+  // something for a
+  final bool a = false;
+
+  const MyWidget();
+
+  // another for b
+  final bool b = true;
+
+  @override
+  Widget build(BuildContext context) {
+    return Container();
+  }
+}
+''');
+    await assertHasAssist('''
+import 'package:flutter/material.dart';
+
+class MyWidget extends StatefulWidget {
+
+  const MyWidget();
+
+  @override
+  State<MyWidget> createState() => _MyWidgetState();
+}
+
+class _MyWidgetState extends State<MyWidget> {
+  // something for a
+  final bool a = false;
+
+  // another for b
+  final bool b = true;
+
+  @override
+  Widget build(BuildContext context) {
+    return Container();
+  }
+}
+''');
+  }
+
+  Future<void> test_comment_documentation() async {
+    await resolveTestCode(r'''
+import 'package:flutter/material.dart';
+
+class /*caret*/MyWidget extends StatelessWidget {
+  /// something for a
+  final bool a = false;
+
+  const MyWidget();
+
+  /// another for b
+  final bool b = true;
+
+  @override
+  Widget build(BuildContext context) {
+    return Container();
+  }
+}
+''');
+    await assertHasAssist('''
+import 'package:flutter/material.dart';
+
+class MyWidget extends StatefulWidget {
+
+  const MyWidget();
+
+  @override
+  State<MyWidget> createState() => _MyWidgetState();
+}
+
+class _MyWidgetState extends State<MyWidget> {
+  /// something for a
+  final bool a = false;
+
+  /// another for b
+  final bool b = true;
+
+  @override
+  Widget build(BuildContext context) {
+    return Container();
+  }
+}
+''');
+  }
+
   Future<void> test_empty() async {
     await resolveTestCode(r'''
 import 'package:flutter/material.dart';
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index 1255129..474f80d 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -80,7 +80,7 @@
 /// TODO(scheglov) Clean up the list of implicitly analyzed files.
 class AnalysisDriver implements AnalysisDriverGeneric {
   /// The version of data format, should be incremented on every format change.
-  static const int DATA_VERSION = 181;
+  static const int DATA_VERSION = 182;
 
   /// The number of exception contexts allowed to write. Once this field is
   /// zero, we stop writing any new exception contexts in this process.
diff --git a/pkg/analyzer/lib/src/dart/error/syntactic_errors.dart b/pkg/analyzer/lib/src/dart/error/syntactic_errors.dart
index a509482..7aea6d5 100644
--- a/pkg/analyzer/lib/src/dart/error/syntactic_errors.dart
+++ b/pkg/analyzer/lib/src/dart/error/syntactic_errors.dart
@@ -869,7 +869,7 @@
    * operator.
    */
   static const ParserErrorCode TYPE_PARAMETER_ON_OPERATOR = ParserErrorCode(
-      'TYPE_PARAMETERS_ON_OPERATOR',
+      'TYPE_PARAMETER_ON_OPERATOR',
       "Types parameters aren't allowed when defining an operator.",
       correction: "Try removing the type parameters.");
 
diff --git a/pkg/analyzer_utilities/lib/tools.dart b/pkg/analyzer_utilities/lib/tools.dart
index f28fc4e..abb3375 100644
--- a/pkg/analyzer_utilities/lib/tools.dart
+++ b/pkg/analyzer_utilities/lib/tools.dart
@@ -228,10 +228,14 @@
   }
 
   static String formatText(String text) {
-    var file = File(join(Directory.systemTemp.path, 'gen.dart'));
+    var tmpDir = Directory.systemTemp.createTempSync('format');
+    var file = File(join(tmpDir.path, 'gen.dart'));
     file.writeAsStringSync(text);
     formatFile(file);
-    return file.readAsStringSync();
+    var result = file.readAsStringSync();
+    file.deleteSync();
+    tmpDir.deleteSync();
+    return result;
   }
 
   static void _throwIfExitCode(ProcessResult result) {
diff --git a/pkg/native_stack_traces/CHANGELOG.md b/pkg/native_stack_traces/CHANGELOG.md
index 7c60f28..7a0f2b5 100644
--- a/pkg/native_stack_traces/CHANGELOG.md
+++ b/pkg/native_stack_traces/CHANGELOG.md
@@ -1,5 +1,9 @@
 # Changelog
 
+## 0.4.4
+
+- Added handling of dynamic tables for testing.
+
 ## 0.4.3
 
 - Exported some more of the ELF utilities for use in Dart tests.
diff --git a/pkg/native_stack_traces/lib/elf.dart b/pkg/native_stack_traces/lib/elf.dart
index 3b1624b..3316655 100644
--- a/pkg/native_stack_traces/lib/elf.dart
+++ b/pkg/native_stack_traces/lib/elf.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.
 
-export 'src/elf.dart' show Elf, Section, Symbol;
+export 'src/elf.dart' show DynamicTable, DynamicTableTag, Elf, Section, Symbol;
 export 'src/constants.dart'
     show
         isolateDataSymbolName,
diff --git a/pkg/native_stack_traces/lib/src/elf.dart b/pkg/native_stack_traces/lib/src/elf.dart
index 548b5ac..268d5d80 100644
--- a/pkg/native_stack_traces/lib/src/elf.dart
+++ b/pkg/native_stack_traces/lib/src/elf.dart
@@ -214,7 +214,10 @@
   static const _ELFDATA2MSB = 0x02;
 
   void writeToStringBuffer(StringBuffer buffer) {
-    buffer..write('Format is ')..write(wordSize * 8)..write(' bits');
+    buffer
+      ..write('Format is ')
+      ..write(wordSize * 8)
+      ..write(' bits');
     switch (endian) {
       case Endian.little:
         buffer..writeln(' and little-endian');
@@ -342,6 +345,15 @@
   int get length => _entries.length;
   ProgramHeaderEntry operator [](int index) => _entries[index];
 
+  ProgramHeaderEntry? loadSegmentFor(int address) {
+    for (final entry in _entries) {
+      if (entry.vaddr <= address && address <= entry.vaddr + entry.memsz) {
+        return entry;
+      }
+    }
+    return null;
+  }
+
   static ProgramHeader fromReader(Reader reader, ElfHeader header) {
     final programReader = reader.refocusedCopy(
         header.programHeaderOffset, header.programHeaderSize);
@@ -352,7 +364,10 @@
 
   void writeToStringBuffer(StringBuffer buffer) {
     for (var i = 0; i < length; i++) {
-      if (i != 0) buffer..writeln()..writeln();
+      if (i != 0)
+        buffer
+          ..writeln()
+          ..writeln();
       buffer
         ..write('Entry ')
         ..write(i)
@@ -422,6 +437,17 @@
   static const _SHT_NOBITS = 8;
   static const _SHT_DYNSYM = 11;
 
+  // sh_flags constants from ELF specification.
+  static const _SHF_WRITE = 0x1;
+  static const _SHF_ALLOC = 0x2;
+  static const _SHF_EXECINSTR = 0x4;
+
+  bool get isWritable => flags & _SHF_WRITE != 0;
+  bool get isAllocated => flags & _SHF_ALLOC != 0;
+  bool get isExecutable => flags & _SHF_EXECINSTR != 0;
+
+  bool get hasBits => type != _SHT_NOBITS;
+
   void setName(StringTable nameTable) {
     name = nameTable[nameIndex]!;
   }
@@ -495,7 +521,10 @@
 
   void writeToStringBuffer(StringBuffer buffer) {
     for (var i = 0; i < entries.length; i++) {
-      if (i != 0) buffer..writeln()..writeln();
+      if (i != 0)
+        buffer
+          ..writeln()
+          ..writeln();
       buffer
         ..write('Entry ')
         ..write(i)
@@ -536,6 +565,8 @@
         return SymbolTable.fromReader(reader, entry);
       case SectionHeaderEntry._SHT_NOTE:
         return Note.fromReader(reader, entry);
+      case SectionHeaderEntry._SHT_DYNAMIC:
+        return DynamicTable.fromReader(reader, entry);
       default:
         return Section._(entry);
     }
@@ -713,7 +744,10 @@
   SymbolVisibility get visibility => SymbolVisibility.values[other & 0x03];
 
   void writeToStringBuffer(StringBuffer buffer) {
-    buffer..write('"')..write(name)..write('" =>');
+    buffer
+      ..write('"')
+      ..write(name)
+      ..write('" =>');
     switch (bind) {
       case SymbolBinding.STB_GLOBAL:
         buffer..write(' a global');
@@ -794,6 +828,109 @@
   }
 }
 
+/// Represents d_tag constants from ELF specification.
+enum DynamicTableTag {
+  DT_NULL,
+  DT_NEEDED,
+  DT_PLTRELSZ,
+  DT_PLTGOT,
+  DT_HASH,
+  DT_STRTAB,
+  DT_SYMTAB,
+  DT_RELA,
+  DT_RELASZ,
+  DT_RELAENT,
+  DT_STRSZ,
+  DT_SYMENT,
+  // Later d_tag values are not currently used in Dart ELF files.
+}
+
+/// The dynamic table, which contains entries pointing to various relocated
+/// addresses.
+class DynamicTable extends Section {
+  // We don't use DynamicTableTag for the key so that we can handle ELF files
+  // that may use unknown (to us) tags.
+  final Map<int, int> _entries;
+  final _wordSize;
+
+  DynamicTable._(SectionHeaderEntry entry, this._entries, this._wordSize)
+      : super._(entry);
+
+  static DynamicTable fromReader(Reader reader, SectionHeaderEntry entry) {
+    final sectionReader = reader.refocusedCopy(entry.offset, entry.size);
+    final entries = <int, int>{};
+    while (true) {
+      // Each entry is a tag and a value, both native word sized.
+      final tag = _readElfNative(sectionReader);
+      final value = _readElfNative(sectionReader);
+      // A DT_NULL entry signfies the end of entries.
+      if (tag == DynamicTableTag.DT_NULL.index) break;
+      entries[tag] = value;
+    }
+    return DynamicTable._(entry, entries, sectionReader.wordSize);
+  }
+
+  int? operator [](DynamicTableTag tag) => _entries[tag.index];
+  bool containsKey(DynamicTableTag tag) => _entries.containsKey(tag.index);
+
+  // To avoid depending on EnumName.name from 2.15.
+  static const _tagStrings = {
+    DynamicTableTag.DT_NULL: 'DT_NULL',
+    DynamicTableTag.DT_NEEDED: 'DT_NEEDED',
+    DynamicTableTag.DT_PLTRELSZ: 'DT_PLTRELSZ',
+    DynamicTableTag.DT_PLTGOT: 'DT_PLTGOT',
+    DynamicTableTag.DT_HASH: 'DT_HASH',
+    DynamicTableTag.DT_STRTAB: 'DT_STRTAB',
+    DynamicTableTag.DT_SYMTAB: 'DT_SYMTAB',
+    DynamicTableTag.DT_RELA: 'DT_RELA',
+    DynamicTableTag.DT_RELASZ: 'DT_RELASZ',
+    DynamicTableTag.DT_STRSZ: 'DT_STRSZ',
+    DynamicTableTag.DT_SYMENT: 'DT_SYMENT',
+  };
+  static final _maxTagStringLength = (_tagStrings.values.toList()
+        ..sort((s1, s2) => s2.length - s1.length))
+      .first
+      .length;
+
+  @override
+  void writeToStringBuffer(StringBuffer buffer) {
+    buffer
+      ..write('Section "')
+      ..write(headerEntry.name)
+      ..writeln('" is a dynamic table:');
+    for (var kv in _entries.entries) {
+      buffer.write(' ');
+      if (kv.key < DynamicTableTag.values.length) {
+        final tag = DynamicTableTag.values[kv.key];
+        buffer
+          ..write(_tagStrings[tag]?.padRight(_maxTagStringLength))
+          ..write(' => ');
+        switch (tag) {
+          // These are relocated addresses.
+          case DynamicTableTag.DT_HASH:
+          case DynamicTableTag.DT_PLTGOT:
+          case DynamicTableTag.DT_SYMTAB:
+          case DynamicTableTag.DT_STRTAB:
+          case DynamicTableTag.DT_RELA:
+            buffer
+              ..write('0x')
+              ..writeln(paddedHex(kv.value, _wordSize));
+            break;
+          // Other entries are just values or offsets.
+          default:
+            buffer.writeln(kv.value);
+        }
+      } else {
+        buffer
+          ..write("Unknown tag ")
+          ..write(kv.key)
+          ..write(' => ')
+          ..writeln(kv.value);
+      }
+    }
+  }
+}
+
 /// Information parsed from an Executable and Linking Format (ELF) file.
 class Elf {
   final ElfHeader _header;
@@ -819,6 +956,21 @@
   Iterable<Section> namedSections(String name) =>
       _sectionsByName[name] ?? <Section>[];
 
+  /// Checks that the contents of a given section have valid addresses when the
+  /// file contents for the corresponding segment is loaded into memory.
+  ///
+  /// Returns false for sections that are not allocated or where the address
+  /// does not correspond to file contents (i.e., NOBITS sections).
+  bool sectionHasValidSegmentAddresses(Section section) {
+    final headerEntry = section.headerEntry;
+    if (!headerEntry.isAllocated || !headerEntry.hasBits) return false;
+    final segment = _programHeader.loadSegmentFor(headerEntry.addr);
+    if (segment == null) return false;
+    return (headerEntry.addr < (segment.vaddr + segment.filesz)) &&
+        (headerEntry.addr + headerEntry.size) <=
+            (segment.vaddr + segment.filesz);
+  }
+
   /// Lookup of a dynamic symbol by name.
   ///
   /// Returns -1 if there is no dynamic symbol that matches [name].
diff --git a/pkg/native_stack_traces/pubspec.yaml b/pkg/native_stack_traces/pubspec.yaml
index bdf1f94..7dbc3b2 100644
--- a/pkg/native_stack_traces/pubspec.yaml
+++ b/pkg/native_stack_traces/pubspec.yaml
@@ -1,6 +1,6 @@
 name: native_stack_traces
 description: Utilities for working with non-symbolic stack traces.
-version: 0.4.3
+version: 0.4.4
 
 homepage: https://github.com/dart-lang/sdk/tree/master/pkg/native_stack_traces
 
diff --git a/runtime/tests/vm/dart/use_save_debugging_info_flag_test.dart b/runtime/tests/vm/dart/use_save_debugging_info_flag_test.dart
index 091087d..2bd55a2 100644
--- a/runtime/tests/vm/dart/use_save_debugging_info_flag_test.dart
+++ b/runtime/tests/vm/dart/use_save_debugging_info_flag_test.dart
@@ -13,6 +13,7 @@
 import "dart:typed_data";
 
 import 'package:expect/expect.dart';
+import 'package:native_stack_traces/elf.dart';
 import 'package:native_stack_traces/native_stack_traces.dart';
 import 'package:path/path.dart' as path;
 
@@ -63,6 +64,7 @@
       '--elf=$scriptWholeSnapshot',
       scriptDill,
     ]);
+    checkElf(scriptWholeSnapshot);
 
     final scriptStrippedOnlySnapshot = path.join(tempDir, 'stripped_only.so');
     await run(genSnapshot, <String>[
@@ -72,6 +74,7 @@
       '--strip',
       scriptDill,
     ]);
+    checkElf(scriptStrippedOnlySnapshot);
 
     final scriptStrippedSnapshot = path.join(tempDir, 'stripped.so');
     final scriptDebuggingInfo = path.join(tempDir, 'debug.so');
@@ -83,6 +86,8 @@
       '--save-debugging-info=$scriptDebuggingInfo',
       scriptDill,
     ]);
+    checkElf(scriptStrippedSnapshot);
+    checkElf(scriptDebuggingInfo);
 
     // Run the resulting scripts, saving the stack traces.
     final wholeTrace = await runError(aotRuntime, <String>[
@@ -184,3 +189,46 @@
     }
   }
 }
+
+void checkElf(String filename) {
+  print("Checking ELF file $filename:");
+  final elf = Elf.fromFile(filename);
+  Expect.isNotNull(elf);
+  final dynamic = elf!.namedSections(".dynamic").single as DynamicTable;
+
+  // Check the dynamic string table information.
+  Expect.isTrue(
+      dynamic.containsKey(DynamicTableTag.DT_STRTAB), "no string table entry");
+  final dynstr = elf.namedSections(".dynstr").single;
+  print(".dynstr address = ${dynamic[DynamicTableTag.DT_STRTAB]}");
+  Expect.isTrue(elf.sectionHasValidSegmentAddresses(dynstr),
+      "string table addresses are invalid");
+  Expect.equals(dynamic[DynamicTableTag.DT_STRTAB], dynstr.headerEntry.addr);
+  Expect.isTrue(dynamic.containsKey(DynamicTableTag.DT_STRSZ),
+      "no string table size entry");
+  print(".dynstr size = ${dynamic[DynamicTableTag.DT_STRSZ]}");
+  Expect.equals(dynamic[DynamicTableTag.DT_STRSZ], dynstr.headerEntry.size);
+
+  // Check the dynamic symbol table information.
+  Expect.isTrue(
+      dynamic.containsKey(DynamicTableTag.DT_SYMTAB), "no symbol table entry");
+  print(".dynsym address = ${dynamic[DynamicTableTag.DT_SYMTAB]}");
+  final dynsym = elf.namedSections(".dynsym").single;
+  Expect.isTrue(elf.sectionHasValidSegmentAddresses(dynsym),
+      "string table addresses are invalid");
+  Expect.equals(dynamic[DynamicTableTag.DT_SYMTAB], dynsym.headerEntry.addr);
+  Expect.isTrue(dynamic.containsKey(DynamicTableTag.DT_SYMENT),
+      "no symbol table entry size entry");
+  print(".dynsym entry size = ${dynamic[DynamicTableTag.DT_SYMENT]}");
+  Expect.equals(
+      dynamic[DynamicTableTag.DT_SYMENT], dynsym.headerEntry.entrySize);
+
+  // Check the hash table information.
+  Expect.isTrue(
+      dynamic.containsKey(DynamicTableTag.DT_HASH), "no hash table entry");
+  print(".hash address = ${dynamic[DynamicTableTag.DT_HASH]}");
+  final hash = elf.namedSections(".hash").single;
+  Expect.isTrue(elf.sectionHasValidSegmentAddresses(hash),
+      "hash table addresses are invalid");
+  Expect.equals(dynamic[DynamicTableTag.DT_HASH], hash.headerEntry.addr);
+}
diff --git a/runtime/tests/vm/dart_2/use_save_debugging_info_flag_test.dart b/runtime/tests/vm/dart_2/use_save_debugging_info_flag_test.dart
index cc8f9b4..db548f4 100644
--- a/runtime/tests/vm/dart_2/use_save_debugging_info_flag_test.dart
+++ b/runtime/tests/vm/dart_2/use_save_debugging_info_flag_test.dart
@@ -15,6 +15,7 @@
 import "dart:typed_data";
 
 import 'package:expect/expect.dart';
+import 'package:native_stack_traces/elf.dart';
 import 'package:native_stack_traces/native_stack_traces.dart';
 import 'package:path/path.dart' as path;
 
@@ -65,6 +66,7 @@
       '--elf=$scriptWholeSnapshot',
       scriptDill,
     ]);
+    checkElf(scriptWholeSnapshot);
 
     final scriptStrippedOnlySnapshot = path.join(tempDir, 'stripped_only.so');
     await run(genSnapshot, <String>[
@@ -74,6 +76,7 @@
       '--strip',
       scriptDill,
     ]);
+    checkElf(scriptStrippedOnlySnapshot);
 
     final scriptStrippedSnapshot = path.join(tempDir, 'stripped.so');
     final scriptDebuggingInfo = path.join(tempDir, 'debug.so');
@@ -85,6 +88,8 @@
       '--save-debugging-info=$scriptDebuggingInfo',
       scriptDill,
     ]);
+    checkElf(scriptStrippedSnapshot);
+    checkElf(scriptDebuggingInfo);
 
     // Run the resulting scripts, saving the stack traces.
     final wholeTrace = await runError(aotRuntime, <String>[
@@ -186,3 +191,46 @@
     }
   }
 }
+
+void checkElf(String filename) {
+  print("Checking ELF file $filename:");
+  final elf = Elf.fromFile(filename);
+  Expect.isNotNull(elf);
+  final dynamic = elf.namedSections(".dynamic").single as DynamicTable;
+
+  // Check the dynamic string table information.
+  Expect.isTrue(
+      dynamic.containsKey(DynamicTableTag.DT_STRTAB), "no string table entry");
+  final dynstr = elf.namedSections(".dynstr").single;
+  print(".dynstr address = ${dynamic[DynamicTableTag.DT_STRTAB]}");
+  Expect.isTrue(elf.sectionHasValidSegmentAddresses(dynstr),
+      "string table addresses are invalid");
+  Expect.equals(dynamic[DynamicTableTag.DT_STRTAB], dynstr.headerEntry.addr);
+  Expect.isTrue(dynamic.containsKey(DynamicTableTag.DT_STRSZ),
+      "no string table size entry");
+  print(".dynstr size = ${dynamic[DynamicTableTag.DT_STRSZ]}");
+  Expect.equals(dynamic[DynamicTableTag.DT_STRSZ], dynstr.headerEntry.size);
+
+  // Check the dynamic symbol table information.
+  Expect.isTrue(
+      dynamic.containsKey(DynamicTableTag.DT_SYMTAB), "no symbol table entry");
+  print(".dynsym address = ${dynamic[DynamicTableTag.DT_SYMTAB]}");
+  final dynsym = elf.namedSections(".dynsym").single;
+  Expect.isTrue(elf.sectionHasValidSegmentAddresses(dynsym),
+      "string table addresses are invalid");
+  Expect.equals(dynamic[DynamicTableTag.DT_SYMTAB], dynsym.headerEntry.addr);
+  Expect.isTrue(dynamic.containsKey(DynamicTableTag.DT_SYMENT),
+      "no symbol table entry size entry");
+  print(".dynsym entry size = ${dynamic[DynamicTableTag.DT_SYMENT]}");
+  Expect.equals(
+      dynamic[DynamicTableTag.DT_SYMENT], dynsym.headerEntry.entrySize);
+
+  // Check the hash table information.
+  Expect.isTrue(
+      dynamic.containsKey(DynamicTableTag.DT_HASH), "no hash table entry");
+  print(".hash address = ${dynamic[DynamicTableTag.DT_HASH]}");
+  final hash = elf.namedSections(".hash").single;
+  Expect.isTrue(elf.sectionHasValidSegmentAddresses(hash),
+      "hash table addresses are invalid");
+  Expect.equals(dynamic[DynamicTableTag.DT_HASH], hash.headerEntry.addr);
+}
diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc
index 5537222..b3c92e0 100644
--- a/runtime/vm/compiler/backend/il_x64.cc
+++ b/runtime/vm/compiler/backend/il_x64.cc
@@ -92,7 +92,7 @@
   // No addressing mode will ignore the upper bits. Cannot use the shorter `orl`
   // to clear the upper bits as this instructions uses negative indices as part
   // of FP-relative loads.
-  // TODO(compressed-pointers): Can we guarentee the index is already
+  // TODO(compressed-pointers): Can we guarantee the index is already
   // sign-extended if always comes for an args-descriptor load?
   __ movsxd(index, index);
 #endif
@@ -123,7 +123,7 @@
   // No addressing mode will ignore the upper bits. Cannot use the shorter `orl`
   // to clear the upper bits as this instructions uses negative indices as part
   // of FP-relative stores.
-  // TODO(compressed-pointers): Can we guarentee the index is already
+  // TODO(compressed-pointers): Can we guarantee the index is already
   // sign-extended if always comes for an args-descriptor load?
   __ movsxd(index, index);
 #endif
diff --git a/runtime/vm/compiler/backend/inliner.cc b/runtime/vm/compiler/backend/inliner.cc
index d4748c4..2523166 100644
--- a/runtime/vm/compiler/backend/inliner.cc
+++ b/runtime/vm/compiler/backend/inliner.cc
@@ -1347,6 +1347,7 @@
     // changes while compiling. Propagate that 'error' and retry compilation
     // later.
     ASSERT(CompilerState::Current().is_aot() ||
+           (error.ptr() == Object::out_of_memory_error().ptr()) ||
            Compiler::IsBackgroundCompilation() || error.IsUnhandledException());
     Thread::Current()->long_jump_base()->Jump(1, error);
     UNREACHABLE();
diff --git a/runtime/vm/elf.cc b/runtime/vm/elf.cc
index a4f550f..28e8224 100644
--- a/runtime/vm/elf.cc
+++ b/runtime/vm/elf.cc
@@ -173,6 +173,8 @@
   }
   bool IsWritable() const { return (flags & elf::SHF_WRITE) == elf::SHF_WRITE; }
 
+  bool HasBits() const { return type != elf::SectionHeaderType::SHT_NOBITS; }
+
   // Returns whether the size of a section can change.
   bool HasBeenFinalized() const {
     // Sections can grow or shrink up until Elf::ComputeOffsets has been run,
@@ -1559,8 +1561,27 @@
   // We now do several passes over the collected sections to reorder them in
   // a way that minimizes segments (and thus padding) in the resulting snapshot.
 
-  // If a build ID was created, we put it after the program table so it can
-  // be read with a minimum number of bytes from the ELF file.
+  auto add_sections_matching =
+      [&](const std::function<bool(Section*)>& should_add) {
+        // We order the sections in a segment so all non-NOBITS sections come
+        // before NOBITS sections, since the former sections correspond to the
+        // file contents for the segment.
+        for (auto* const section : sections_) {
+          if (!section->HasBits()) continue;
+          if (should_add(section)) {
+            add_to_reordered_sections(section);
+          }
+        }
+        for (auto* const section : sections_) {
+          if (section->HasBits()) continue;
+          if (should_add(section)) {
+            add_to_reordered_sections(section);
+          }
+        }
+      };
+
+  // If a build ID was created, we put it right after the program table so it
+  // can be read with a minimum number of bytes from the ELF file.
   auto* const build_id = Find(Elf::kBuildIdNoteName);
   if (build_id != nullptr) {
     ASSERT(build_id->type == elf::SectionHeaderType::SHT_NOTE);
@@ -1568,38 +1589,31 @@
   }
 
   // Now add the other non-writable, non-executable allocated sections.
-  for (auto* const section : sections_) {
-    if (section == build_id) continue;  // Already added.
-    if (section->IsAllocated() && !section->IsWritable() &&
-        !section->IsExecutable()) {
-      add_to_reordered_sections(section);
-    }
-  }
+  add_sections_matching([&](Section* section) -> bool {
+    if (section == build_id) return false;  // Already added.
+    return section->IsAllocated() && !section->IsWritable() &&
+           !section->IsExecutable();
+  });
 
   // Now add the executable sections in a new segment.
-  for (auto* const section : sections_) {
-    if (section->IsExecutable()) {  // Implies IsAllocated() && !IsWritable()
-      add_to_reordered_sections(section);
-    }
-  }
+  add_sections_matching([](Section* section) -> bool {
+    return section->IsExecutable();  // Implies IsAllocated() && !IsWritable()
+  });
 
   // Now add all the writable sections.
-  for (auto* const section : sections_) {
-    if (section->IsWritable()) {  // Implies IsAllocated() && !IsExecutable()
-      add_to_reordered_sections(section);
-    }
-  }
+  add_sections_matching([](Section* section) -> bool {
+    return section->IsWritable();  // Implies IsAllocated() && !IsExecutable()
+  });
 
   // We put all non-reserved unallocated sections last. Otherwise, they would
   // affect the file offset but not the memory offset of any following allocated
   // sections. Doing it in this order makes it easier to keep file and memory
   // offsets page-aligned with respect to each other, which is required for
   // some loaders.
-  for (intptr_t i = 1; i < num_sections; i++) {
-    auto* const section = sections_[i];
-    if (section->IsAllocated()) continue;
-    add_to_reordered_sections(section);
-  }
+  add_sections_matching([](Section* section) -> bool {
+    // Don't re-add the initial reserved section.
+    return !section->IsReservedSection() && !section->IsAllocated();
+  });
 
   // All sections should have been accounted for in the loops above.
   ASSERT_EQUAL(sections_.length(), reordered_sections.length());
diff --git a/runtime/vm/heap/become.cc b/runtime/vm/heap/become.cc
index 4bb646d..2a4e0aa 100644
--- a/runtime/vm/heap/become.cc
+++ b/runtime/vm/heap/become.cc
@@ -217,6 +217,27 @@
 };
 #endif
 
+Become::Become() {
+  IsolateGroup* group = Thread::Current()->isolate_group();
+  ASSERT(group->become() == nullptr);  // Only one outstanding become at a time.
+  group->set_become(this);
+}
+
+Become::~Become() {
+  Thread::Current()->isolate_group()->set_become(nullptr);
+}
+
+void Become::Add(const Object& before, const Object& after) {
+  pointers_.Add(before.ptr());
+  pointers_.Add(after.ptr());
+}
+
+void Become::VisitObjectPointers(ObjectPointerVisitor* visitor) {
+  if (pointers_.length() != 0) {
+    visitor->VisitPointers(&pointers_[0], pointers_.length());
+  }
+}
+
 void Become::MakeDummyObject(const Instance& instance) {
   // Make the forward pointer point to itself.
   // This is needed to distinguish it from a real forward object.
@@ -228,7 +249,7 @@
   return GetForwardedObject(object) == object;
 }
 
-void Become::CrashDump(ObjectPtr before_obj, ObjectPtr after_obj) {
+static void CrashDump(ObjectPtr before_obj, ObjectPtr after_obj) {
   OS::PrintErr("DETECTED FATAL ISSUE IN BECOME MAPPINGS\n");
 
   OS::PrintErr("BEFORE ADDRESS: %#" Px "\n", static_cast<uword>(before_obj));
@@ -256,7 +277,7 @@
   }
 }
 
-void Become::ElementsForwardIdentity(const Array& before, const Array& after) {
+void Become::Forward() {
   Thread* thread = Thread::Current();
   auto heap = thread->isolate_group()->heap();
 
@@ -264,10 +285,9 @@
   HeapIterationScope his(thread);
 
   // Setup forwarding pointers.
-  ASSERT(before.Length() == after.Length());
-  for (intptr_t i = 0; i < before.Length(); i++) {
-    ObjectPtr before_obj = before.At(i);
-    ObjectPtr after_obj = after.At(i);
+  for (intptr_t i = 0; i < pointers_.length(); i += 2) {
+    ObjectPtr before_obj = pointers_[i];
+    ObjectPtr after_obj = pointers_[i + 1];
 
     if (before_obj == after_obj) {
       FATAL("become: Cannot self-forward");
@@ -304,10 +324,11 @@
   FollowForwardingPointers(thread);
 
 #if defined(DEBUG)
-  for (intptr_t i = 0; i < before.Length(); i++) {
-    ASSERT(before.At(i) == after.At(i));
+  for (intptr_t i = 0; i < pointers_.length(); i += 2) {
+    ASSERT(pointers_[i] == pointers_[i + 1]);
   }
 #endif
+  pointers_.Clear();
 }
 
 void Become::FollowForwardingPointers(Thread* thread) {
diff --git a/runtime/vm/heap/become.h b/runtime/vm/heap/become.h
index 9914056..4b945bf 100644
--- a/runtime/vm/heap/become.h
+++ b/runtime/vm/heap/become.h
@@ -6,6 +6,7 @@
 #define RUNTIME_VM_HEAP_BECOME_H_
 
 #include "platform/atomic.h"
+#include "platform/growable_array.h"
 #include "vm/allocation.h"
 #include "vm/raw_object.h"
 
@@ -68,15 +69,39 @@
   DISALLOW_IMPLICIT_CONSTRUCTORS(ForwardingCorpse);
 };
 
-// TODO(johnmccutchan): Refactor this class so that it is not all static and
-// provides utility methods for building the mapping of before and after.
-class Become : public AllStatic {
+// Forward/exchange object identity within pairs of objects.
+//
+// Forward: Redirects all pointers to each 'before' object to the corresponding
+// 'after' object. Every 'before' object is guaranteed to be unreachable after
+// the operation. The identity hash of the 'before' object is retained.
+//
+// This is useful for atomically applying behavior and schema changes, which can
+// be done by allocating fresh objects with the new schema and forwarding the
+// identity of the old objects to the new objects.
+//
+// Exchange: Redirect all pointers to each 'before' object to the corresponding
+// 'after' object and vice versa. Both objects remain reachable after the
+// operation.
+//
+// This is useful for implementing certain types of proxies. For example, an
+// infrequently accessed object may be written to disk and swapped with a
+// so-called "husk", and swapped back when it is later accessed.
+//
+// This operation is named 'become' after its original in Smalltalk:
+//   x become: y             "exchange identity for one pair"
+//   x becomeForward: y      "forward identity for one pair"
+//   #(x ...) elementsExchangeIdentityWith: #(y ...)
+//   #(x ...) elementsForwardIdentityTo: #(y ...)
+class Become {
  public:
-  // Smalltalk's one-way bulk become (Array>>#elementsForwardIdentityTo:).
-  // Redirects all pointers to elements of 'before' to the corresponding element
-  // in 'after'. Every element in 'before' is guaranteed to be not reachable.
-  // Useful for atomically applying behavior and schema changes.
-  static void ElementsForwardIdentity(const Array& before, const Array& after);
+  Become();
+  ~Become();
+
+  void Add(const Object& before, const Object& after);
+  void Forward();
+  void Exchange() { UNIMPLEMENTED(); }
+
+  void VisitObjectPointers(ObjectPointerVisitor* visitor);
 
   // Convert and instance object into a dummy object,
   // making the instance independent of its class.
@@ -88,7 +113,8 @@
   static void FollowForwardingPointers(Thread* thread);
 
  private:
-  static void CrashDump(ObjectPtr before_obj, ObjectPtr after_obj);
+  MallocGrowableArray<ObjectPtr> pointers_;
+  DISALLOW_COPY_AND_ASSIGN(Become);
 };
 
 }  // namespace dart
diff --git a/runtime/vm/heap/become_test.cc b/runtime/vm/heap/become_test.cc
index 031e497b..76f325a 100644
--- a/runtime/vm/heap/become_test.cc
+++ b/runtime/vm/heap/become_test.cc
@@ -13,24 +13,25 @@
 namespace dart {
 
 void TestBecomeForward(Heap::Space before_space, Heap::Space after_space) {
+  // Allocate the container in old space to test the remembered set.
+  const Array& container = Array::Handle(Array::New(1, Heap::kOld));
+
   const String& before_obj = String::Handle(String::New("old", before_space));
   const String& after_obj = String::Handle(String::New("new", after_space));
-
+  container.SetAt(0, before_obj);
   EXPECT(before_obj.ptr() != after_obj.ptr());
 
-  // Allocate the arrays in old space to test the remembered set.
-  const Array& before = Array::Handle(Array::New(1, Heap::kOld));
-  before.SetAt(0, before_obj);
-  const Array& after = Array::Handle(Array::New(1, Heap::kOld));
-  after.SetAt(0, after_obj);
-
-  Become::ElementsForwardIdentity(before, after);
+  Become become;
+  become.Add(before_obj, after_obj);
+  become.Forward();
 
   EXPECT(before_obj.ptr() == after_obj.ptr());
+  EXPECT(container.At(0) == after_obj.ptr());
 
   GCTestHelper::CollectAllGarbage();
 
   EXPECT(before_obj.ptr() == after_obj.ptr());
+  EXPECT(container.At(0) == after_obj.ptr());
 }
 
 ISOLATE_UNIT_TEST_CASE(BecomeFowardOldToOld) {
@@ -62,11 +63,9 @@
   EXPECT_EQ(peer, heap->GetPeer(before_obj.ptr()));
   EXPECT_EQ(no_peer, heap->GetPeer(after_obj.ptr()));
 
-  const Array& before = Array::Handle(Array::New(1, Heap::kOld));
-  before.SetAt(0, before_obj);
-  const Array& after = Array::Handle(Array::New(1, Heap::kOld));
-  after.SetAt(0, after_obj);
-  Become::ElementsForwardIdentity(before, after);
+  Become become;
+  become.Add(before_obj, after_obj);
+  become.Forward();
 
   EXPECT(before_obj.ptr() == after_obj.ptr());
   EXPECT_EQ(peer, heap->GetPeer(before_obj.ptr()));
@@ -86,11 +85,9 @@
   EXPECT_EQ(id, heap->GetObjectId(before_obj.ptr()));
   EXPECT_EQ(no_id, heap->GetObjectId(after_obj.ptr()));
 
-  const Array& before = Array::Handle(Array::New(1, Heap::kOld));
-  before.SetAt(0, before_obj);
-  const Array& after = Array::Handle(Array::New(1, Heap::kOld));
-  after.SetAt(0, after_obj);
-  Become::ElementsForwardIdentity(before, after);
+  Become become;
+  become.Add(before_obj, after_obj);
+  become.Forward();
 
   EXPECT(before_obj.ptr() == after_obj.ptr());
   EXPECT_EQ(id, heap->GetObjectId(before_obj.ptr()));
@@ -114,11 +111,9 @@
   EXPECT_EQ(no_id,
             isolate->forward_table_old()->GetValueExclusive(after_obj.ptr()));
 
-  const Array& before = Array::Handle(Array::New(1, Heap::kOld));
-  before.SetAt(0, before_obj);
-  const Array& after = Array::Handle(Array::New(1, Heap::kOld));
-  after.SetAt(0, after_obj);
-  Become::ElementsForwardIdentity(before, after);
+  Become become;
+  become.Add(before_obj, after_obj);
+  become.Forward();
 
   EXPECT(before_obj.ptr() == after_obj.ptr());
   EXPECT_EQ(id,
@@ -142,12 +137,9 @@
 
   EXPECT(before_obj.ptr() != after_obj.ptr());
 
-  const Array& before = Array::Handle(Array::New(1, Heap::kOld));
-  before.SetAt(0, before_obj);
-  const Array& after = Array::Handle(Array::New(1, Heap::kOld));
-  after.SetAt(0, after_obj);
-
-  Become::ElementsForwardIdentity(before, after);
+  Become become;
+  become.Add(before_obj, after_obj);
+  become.Forward();
 
   EXPECT(before_obj.ptr() == after_obj.ptr());
   EXPECT(!after_obj.ptr()->untag()->IsRemembered());
@@ -174,11 +166,9 @@
                  Object::Handle(card_remembered_array.At(0)).ToCString());
   }
 
-  const Array& before = Array::Handle(Array::New(1, Heap::kOld));
-  before.SetAt(0, old_element);
-  const Array& after = Array::Handle(Array::New(1, Heap::kOld));
-  after.SetAt(0, new_element);
-  Become::ElementsForwardIdentity(before, after);
+  Become become;
+  become.Add(old_element, new_element);
+  become.Forward();
 
   EXPECT(old_element.ptr() == new_element.ptr());
   EXPECT(old_element.ptr()->IsNewObject());
diff --git a/runtime/vm/heap/marker.cc b/runtime/vm/heap/marker.cc
index 46fc02f..74ca4cc 100644
--- a/runtime/vm/heap/marker.cc
+++ b/runtime/vm/heap/marker.cc
@@ -187,7 +187,7 @@
       // processing here is idempotent, so repeated visits only hurt performance
       // but not correctness. Duplicatation is expected to be low.
       // By the absence of a special case, we are treating WeakProperties as
-      // strong references here. This guarentees a WeakProperty will only be
+      // strong references here. This guarantees a WeakProperty will only be
       // added to the delayed_weak_properties_ list of the worker that
       // encounters it during ordinary marking. This is in the same spirit as
       // the eliminated write barrier, which would have added the newly written
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index 5386b13..940b893 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -2899,6 +2899,10 @@
     visitor->VisitPointer(
         reinterpret_cast<ObjectPtr*>(&(source()->loaded_blobs_)));
   }
+
+  if (become() != nullptr) {
+    become()->VisitObjectPointers(visitor);
+  }
 }
 
 void IsolateGroup::VisitStackPointers(ObjectPointerVisitor* visitor,
diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h
index d6555b3..21cd81c 100644
--- a/runtime/vm/isolate.h
+++ b/runtime/vm/isolate.h
@@ -46,6 +46,7 @@
 // Forward declarations.
 class ApiState;
 class BackgroundCompiler;
+class Become;
 class Capability;
 class CodeIndexTable;
 class Debugger;
@@ -687,6 +688,9 @@
 #endif
   }
 
+  Become* become() const { return become_; }
+  void set_become(Become* become) { become_ = become; }
+
   uint64_t id() const { return id_; }
 
   static void Init();
@@ -829,6 +833,7 @@
   RelaxedAtomic<intptr_t> reload_every_n_stack_overflow_checks_;
   ProgramReloadContext* program_reload_context_ = nullptr;
 #endif
+  Become* become_ = nullptr;
 
 #define ISOLATE_METRIC_VARIABLE(type, variable, name, unit)                    \
   type metric_##variable##_;
diff --git a/runtime/vm/isolate_reload.cc b/runtime/vm/isolate_reload.cc
index 71e5d04..9ad8617 100644
--- a/runtime/vm/isolate_reload.cc
+++ b/runtime/vm/isolate_reload.cc
@@ -178,8 +178,7 @@
       shared_class_table_(shared_class_table),
       mapping_(mapping),
       new_fields_offsets_(new_fields_offsets),
-      before_(zone, 16),
-      after_(zone, 16) {}
+      before_(zone, 16) {}
 
 void InstanceMorpher::AddObject(ObjectPtr object) {
   ASSERT(object->GetClassId() == cid_);
@@ -187,65 +186,65 @@
   before_.Add(&instance);
 }
 
-InstancePtr InstanceMorpher::Morph(const Instance& instance) const {
-  // Code can reference constants / canonical objects either directly in the
-  // instruction stream (ia32) or via an object pool.
-  //
-  // We have the following invariants:
-  //
-  //    a) Those canonical objects don't change state (i.e. are not mutable):
-  //       our optimizer can e.g. execute loads of such constants at
-  //       compile-time.
-  //
-  //       => We ensure that const-classes with live constants cannot be
-  //          reloaded to become non-const classes (see Class::CheckReload).
-  //
-  //    b) Those canonical objects live in old space: e.g. on ia32 the scavenger
-  //       does not make the RX pages writable and therefore cannot update
-  //       pointers embedded in the instruction stream.
-  //
-  // In order to maintain these invariants we ensure to always morph canonical
-  // objects to old space.
-  const bool is_canonical = instance.IsCanonical();
-  const Heap::Space space = is_canonical ? Heap::kOld : Heap::kNew;
-  const auto& result = Instance::Handle(
-      Z, Instance::NewFromCidAndSize(shared_class_table_, cid_, space));
+void InstanceMorpher::CreateMorphedCopies(Become* become) {
+  Instance& after = Instance::Handle(Z);
+  Object& value = Object::Handle(Z);
+  for (intptr_t i = 0; i < before_.length(); i++) {
+    const Instance& before = *before_.At(i);
 
-  // We preserve the canonical bit of the object, since this object is present
-  // in the class's constants.
-  if (is_canonical) {
-    result.SetCanonical();
-  }
+    // Code can reference constants / canonical objects either directly in the
+    // instruction stream (ia32) or via an object pool.
+    //
+    // We have the following invariants:
+    //
+    //    a) Those canonical objects don't change state (i.e. are not mutable):
+    //       our optimizer can e.g. execute loads of such constants at
+    //       compile-time.
+    //
+    //       => We ensure that const-classes with live constants cannot be
+    //          reloaded to become non-const classes (see Class::CheckReload).
+    //
+    //    b) Those canonical objects live in old space: e.g. on ia32 the
+    //       scavenger does not make the RX pages writable and therefore cannot
+    //       update pointers embedded in the instruction stream.
+    //
+    // In order to maintain these invariants we ensure to always morph canonical
+    // objects to old space.
+    const bool is_canonical = before.IsCanonical();
+    const Heap::Space space = is_canonical ? Heap::kOld : Heap::kNew;
+    after = Instance::NewFromCidAndSize(shared_class_table_, cid_, space);
+
+    // We preserve the canonical bit of the object, since this object is present
+    // in the class's constants.
+    if (is_canonical) {
+      after.SetCanonical();
+    }
 #if defined(HASH_IN_OBJECT_HEADER)
-  const uint32_t hash = Object::GetCachedHash(instance.ptr());
-  Object::SetCachedHashIfNotSet(result.ptr(), hash);
+    const uint32_t hash = Object::GetCachedHash(before.ptr());
+    Object::SetCachedHashIfNotSet(after.ptr(), hash);
 #endif
 
-  // Morph the context from instance to result using mapping_.
-  Object& value = Object::Handle(Z);
-  for (intptr_t i = 0; i < mapping_->length(); i += 2) {
-    intptr_t from_offset = mapping_->At(i);
-    intptr_t to_offset = mapping_->At(i + 1);
-    ASSERT(from_offset > 0);
-    ASSERT(to_offset > 0);
-    value = instance.RawGetFieldAtOffset(from_offset);
-    result.RawSetFieldAtOffset(to_offset, value);
-  }
+    // Morph the context from [before] to [after] using mapping_.
+    for (intptr_t i = 0; i < mapping_->length(); i += 2) {
+      intptr_t from_offset = mapping_->At(i);
+      intptr_t to_offset = mapping_->At(i + 1);
+      ASSERT(from_offset > 0);
+      ASSERT(to_offset > 0);
+      value = before.RawGetFieldAtOffset(from_offset);
+      after.RawSetFieldAtOffset(to_offset, value);
+    }
 
-  for (intptr_t i = 0; i < new_fields_offsets_->length(); i++) {
-    const intptr_t field_offset = new_fields_offsets_->At(i);
-    result.RawSetFieldAtOffset(field_offset, Object::sentinel());
-  }
+    for (intptr_t i = 0; i < new_fields_offsets_->length(); i++) {
+      const intptr_t field_offset = new_fields_offsets_->At(i);
+      after.RawSetFieldAtOffset(field_offset, Object::sentinel());
+    }
 
-  // Convert the instance into a filler object.
-  Become::MakeDummyObject(instance);
-  return result.ptr();
-}
+    // Convert the old instance into a filler object. We will switch to the new
+    // class table before the next heap walk, so there must be no instances of
+    // any class with the old size.
+    Become::MakeDummyObject(before);
 
-void InstanceMorpher::CreateMorphedCopies() {
-  for (intptr_t i = 0; i < before_.length(); i++) {
-    const Instance& copy = Instance::Handle(Z, Morph(*before_.At(i)));
-    after_.Add(&copy);
+    become->Add(before, after);
   }
 }
 
@@ -372,34 +371,6 @@
   static uword Hash(const Object& obj) { return Library::Cast(obj).UrlHash(); }
 };
 
-class BecomeMapTraits {
- public:
-  static bool ReportStats() { return false; }
-  static const char* Name() { return "BecomeMapTraits"; }
-
-  static bool IsMatch(const Object& a, const Object& b) {
-    return a.ptr() == b.ptr();
-  }
-
-  static uword Hash(const Object& obj) {
-    if (obj.IsLibrary()) {
-      return Library::Cast(obj).UrlHash();
-    } else if (obj.IsClass()) {
-      return String::HashRawSymbol(Class::Cast(obj).Name());
-    } else if (obj.IsField()) {
-      return String::HashRawSymbol(Field::Cast(obj).name());
-    } else if (obj.IsClosure()) {
-      return String::HashRawSymbol(
-          Function::Handle(Closure::Cast(obj).function()).name());
-    } else if (obj.IsLibraryPrefix()) {
-      return String::HashRawSymbol(LibraryPrefix::Cast(obj).name());
-    } else {
-      FATAL1("Unexpected type in become: %s\n", obj.ToCString());
-    }
-    return 0;
-  }
-};
-
 bool ProgramReloadContext::IsSameClass(const Class& a, const Class& b) {
   // TODO(turnidge): We need to look at generic type arguments for
   // synthetic mixin classes.  Their names are not necessarily unique
@@ -461,8 +432,6 @@
       removed_class_set_storage_(Array::null()),
       old_libraries_set_storage_(Array::null()),
       library_map_storage_(Array::null()),
-      become_map_storage_(Array::null()),
-      become_enum_mappings_(GrowableObjectArray::null()),
       saved_root_library_(Library::null()),
       saved_libraries_(GrowableObjectArray::null()) {
   // NOTE: DO NOT ALLOCATE ANY RAW OBJECTS HERE. The ProgramReloadContext is not
@@ -793,10 +762,7 @@
           // active.
           ASSERT(HasNoTasks(heap));
 
-          const Array& before = Array::Handle(Z, Array::New(count));
-          const Array& after = Array::Handle(Z, Array::New(count));
-
-          MorphInstancesPhase1Allocate(&locator, before, after);
+          MorphInstancesPhase1Allocate(&locator, IG->become());
           {
             // Apply the new class table before "become". Become will replace
             // all the instances of the old size with forwarding corpses, then
@@ -811,7 +777,7 @@
             IG->program_reload_context()->DiscardSavedClassTable(
                 /*is_rollback=*/false);
           }
-          MorphInstancesPhase2Become(before, after);
+          MorphInstancesPhase2Become(IG->become());
 
           discard_class_tables = false;
         }
@@ -1044,10 +1010,6 @@
       HashTables::New<UnorderedHashSet<LibraryMapTraits> >(4);
   library_map_storage_ =
       HashTables::New<UnorderedHashMap<LibraryMapTraits> >(4);
-  become_map_storage_ = HashTables::New<UnorderedHashMap<BecomeMapTraits> >(4);
-  // Keep a separate array for enum mappings to avoid having to invoke
-  // hashCode on the instances.
-  become_enum_mappings_ = GrowableObjectArray::New(Heap::kOld);
 
   // While reloading everything we do must be reversible so that we can abort
   // safely if the reload fails. This function stashes things to the side and
@@ -1639,39 +1601,7 @@
 }
 
 void ProgramReloadContext::CommitAfterInstanceMorphing() {
-  {
-    const GrowableObjectArray& become_enum_mappings =
-        GrowableObjectArray::Handle(become_enum_mappings_);
-    UnorderedHashMap<BecomeMapTraits> become_map(become_map_storage_);
-    intptr_t replacement_count =
-        become_map.NumOccupied() + become_enum_mappings.Length() / 2;
-    const Array& before =
-        Array::Handle(Array::New(replacement_count, Heap::kOld));
-    const Array& after =
-        Array::Handle(Array::New(replacement_count, Heap::kOld));
-    Object& obj = Object::Handle();
-    intptr_t replacement_index = 0;
-    UnorderedHashMap<BecomeMapTraits>::Iterator it(&become_map);
-    while (it.MoveNext()) {
-      const intptr_t entry = it.Current();
-      obj = become_map.GetKey(entry);
-      before.SetAt(replacement_index, obj);
-      obj = become_map.GetPayload(entry, 0);
-      after.SetAt(replacement_index, obj);
-      replacement_index++;
-    }
-    for (intptr_t i = 0; i < become_enum_mappings.Length(); i += 2) {
-      obj = become_enum_mappings.At(i);
-      before.SetAt(replacement_index, obj);
-      obj = become_enum_mappings.At(i + 1);
-      after.SetAt(replacement_index, obj);
-      replacement_index++;
-    }
-    ASSERT(replacement_index == replacement_count);
-    become_map.Release();
-
-    Become::ElementsForwardIdentity(before, after);
-  }
+  become_.Forward();
 
   // Rehash constants map for all classes. Constants are hashed by content, and
   // content may have changed from fields being added or removed.
@@ -1747,8 +1677,7 @@
 
 void IsolateGroupReloadContext::MorphInstancesPhase1Allocate(
     ObjectLocator* locator,
-    const Array& before,
-    const Array& after) {
+    Become* become) {
   ASSERT(HasInstanceMorphers());
 
   if (FLAG_trace_reload) {
@@ -1764,27 +1693,14 @@
             (count > 1) ? "s" : "");
 
   for (intptr_t i = 0; i < instance_morphers_.length(); i++) {
-    instance_morphers_.At(i)->CreateMorphedCopies();
+    instance_morphers_.At(i)->CreateMorphedCopies(become);
   }
-
-  // Create the inputs for Become.
-  intptr_t index = 0;
-  for (intptr_t i = 0; i < instance_morphers_.length(); i++) {
-    InstanceMorpher* morpher = instance_morphers_.At(i);
-    for (intptr_t j = 0; j < morpher->before()->length(); j++) {
-      before.SetAt(index, *morpher->before()->At(j));
-      after.SetAt(index, *morpher->after()->At(j));
-      index++;
-    }
-  }
-  ASSERT(index == count);
 }
 
-void IsolateGroupReloadContext::MorphInstancesPhase2Become(const Array& before,
-                                                           const Array& after) {
+void IsolateGroupReloadContext::MorphInstancesPhase2Become(Become* become) {
   ASSERT(HasInstanceMorphers());
 
-  Become::ElementsForwardIdentity(before, after);
+  become->Forward();
   // The heap now contains only instances with the new size. Ordinary GC is safe
   // again.
 }
@@ -2550,26 +2466,12 @@
                                                  const Field& new_field) {
   ASSERT(old_field.is_static());
   ASSERT(new_field.is_static());
-
   AddBecomeMapping(old_field, new_field);
 }
 
 void ProgramReloadContext::AddBecomeMapping(const Object& old,
                                             const Object& neu) {
-  ASSERT(become_map_storage_ != Array::null());
-  UnorderedHashMap<BecomeMapTraits> become_map(become_map_storage_);
-  bool update = become_map.UpdateOrInsert(old, neu);
-  ASSERT(!update);
-  become_map_storage_ = become_map.Release().ptr();
-}
-
-void ProgramReloadContext::AddEnumBecomeMapping(const Object& old,
-                                                const Object& neu) {
-  const GrowableObjectArray& become_enum_mappings =
-      GrowableObjectArray::Handle(become_enum_mappings_);
-  become_enum_mappings.Add(old);
-  become_enum_mappings.Add(neu);
-  ASSERT((become_enum_mappings.Length() % 2) == 0);
+  become_.Add(old, neu);
 }
 
 void ProgramReloadContext::RebuildDirectSubclasses() {
diff --git a/runtime/vm/isolate_reload.h b/runtime/vm/isolate_reload.h
index 69a399c..939f512 100644
--- a/runtime/vm/isolate_reload.h
+++ b/runtime/vm/isolate_reload.h
@@ -13,6 +13,7 @@
 #include "vm/globals.h"
 #include "vm/growable_array.h"
 #include "vm/hash_map.h"
+#include "vm/heap/become.h"
 #include "vm/log.h"
 #include "vm/object.h"
 
@@ -68,24 +69,15 @@
                   ZoneGrowableArray<intptr_t>* new_fields_offsets);
   virtual ~InstanceMorpher() {}
 
-  // Called on each instance that needs to be morphed.
-  InstancePtr Morph(const Instance& instance) const;
-
   // Adds an object to be morphed.
   void AddObject(ObjectPtr object);
 
   // Create the morphed objects based on the before() list.
-  void CreateMorphedCopies();
+  void CreateMorphedCopies(Become* become);
 
   // Append the morper info to JSON array.
   void AppendTo(JSONArray* array);
 
-  // Returns the list of objects that need to be morphed.
-  const GrowableArray<const Instance*>* before() const { return &before_; }
-
-  // Returns the list of morphed objects (matches order in before()).
-  const GrowableArray<const Instance*>* after() const { return &after_; }
-
   // Returns the cid associated with the from_ and to_ class.
   intptr_t cid() const { return cid_; }
 
@@ -100,7 +92,6 @@
   ZoneGrowableArray<intptr_t>* new_fields_offsets_;
 
   GrowableArray<const Instance*> before_;
-  GrowableArray<const Instance*> after_;
 };
 
 class ReasonForCancelling : public ZoneAllocated {
@@ -220,10 +211,8 @@
 
   void CheckpointSharedClassTable();
 
-  void MorphInstancesPhase1Allocate(ObjectLocator* locator,
-                                    const Array& before,
-                                    const Array& after);
-  void MorphInstancesPhase2Become(const Array& before, const Array& after);
+  void MorphInstancesPhase1Allocate(ObjectLocator* locator, Become* become);
+  void MorphInstancesPhase2Become(Become* become);
 
   void ForEachIsolate(std::function<void(Isolate*)> callback);
 
@@ -401,9 +390,10 @@
                          const Library& original);
   void AddStaticFieldMapping(const Field& old_field, const Field& new_field);
   void AddBecomeMapping(const Object& old, const Object& neu);
-  void AddEnumBecomeMapping(const Object& old, const Object& neu);
   void RebuildDirectSubclasses();
 
+  Become become_;
+
   ObjectPtr* from() {
     return reinterpret_cast<ObjectPtr*>(&old_classes_set_storage_);
   }
@@ -412,8 +402,6 @@
   ArrayPtr removed_class_set_storage_;
   ArrayPtr old_libraries_set_storage_;
   ArrayPtr library_map_storage_;
-  ArrayPtr become_map_storage_;
-  GrowableObjectArrayPtr become_enum_mappings_;
   LibraryPtr saved_root_library_;
   GrowableObjectArrayPtr saved_libraries_;
   ObjectPtr* to() { return reinterpret_cast<ObjectPtr*>(&saved_libraries_); }
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index e9ed8fb..d793156 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -1177,7 +1177,7 @@
       LanguageError::New(error_str, Report::kBailout, Heap::kOld);
   error_str = String::New("Out of memory", Heap::kOld);
   *out_of_memory_error_ =
-      LanguageError::New(error_str, Report::kBailout, Heap::kOld);
+      LanguageError::New(error_str, Report::kError, Heap::kOld);
 
   // Allocate the parameter arrays for method extractor types and names.
   *extractor_parameter_types_ = Array::New(1, Heap::kOld);
@@ -2639,9 +2639,7 @@
     } else if (thread->top_exit_frame_info() != 0) {
       // Use the preallocated out of memory exception to avoid calling
       // into dart code or allocating any code.
-      const Instance& exception = Instance::Handle(
-          thread->isolate_group()->object_store()->out_of_memory());
-      Exceptions::Throw(thread, exception);
+      Exceptions::ThrowOOM();
       UNREACHABLE();
     } else {
       // Nowhere to propagate an exception to.
@@ -10089,6 +10087,10 @@
   const Object& result =
       Object::Handle(zone, Compiler::CompileFunction(thread, *this));
   if (result.IsError()) {
+    if (result.ptr() == Object::out_of_memory_error().ptr()) {
+      Exceptions::ThrowOOM();
+      UNREACHABLE();
+    }
     if (result.IsLanguageError()) {
       Exceptions::ThrowCompileTimeError(LanguageError::Cast(result));
       UNREACHABLE();
@@ -10097,7 +10099,7 @@
     UNREACHABLE();
   }
   // Compiling in unoptimized mode should never fail if there are no errors.
-  ASSERT(HasCode());
+  RELEASE_ASSERT(HasCode());
   ASSERT(ForceOptimize() || unoptimized_code() == result.ptr());
   return CurrentCode();
 }
@@ -10867,8 +10869,9 @@
 void Field::set_guarded_list_length_in_object_offset_unsafe(
     intptr_t list_length_offset) const {
   ASSERT(IsOriginal());
-  StoreNonPointer(&untag()->guarded_list_length_in_object_offset_,
-                  static_cast<int8_t>(list_length_offset - kHeapObjectTag));
+  StoreNonPointer<int8_t, int8_t, std::memory_order_relaxed>(
+      &untag()->guarded_list_length_in_object_offset_,
+      static_cast<int8_t>(list_length_offset - kHeapObjectTag));
   ASSERT(guarded_list_length_in_object_offset() == list_length_offset);
 }
 
diff --git a/runtime/vm/object_reload.cc b/runtime/vm/object_reload.cc
index b2037d9..cc089f2 100644
--- a/runtime/vm/object_reload.cc
+++ b/runtime/vm/object_reload.cc
@@ -397,7 +397,7 @@
                    enum_ident.ToCString());
         bool removed = enum_map.Remove(enum_ident);
         ASSERT(removed);
-        reload_context->AddEnumBecomeMapping(old_enum_value, enum_value);
+        reload_context->AddBecomeMapping(old_enum_value, enum_value);
       }
     }
     enums_deleted = enum_map.NumOccupied() > 0;
@@ -409,13 +409,13 @@
   // Map the old E.values array to the new E.values array.
   ASSERT(!old_enum_values.IsNull());
   ASSERT(!enum_values.IsNull());
-  reload_context->AddEnumBecomeMapping(old_enum_values, enum_values);
+  reload_context->AddBecomeMapping(old_enum_values, enum_values);
 
   // Map the old E._deleted_enum_sentinel to the new E._deleted_enum_sentinel.
   ASSERT(!old_deleted_enum_sentinel.IsNull());
   ASSERT(!deleted_enum_sentinel.IsNull());
-  reload_context->AddEnumBecomeMapping(old_deleted_enum_sentinel,
-                                       deleted_enum_sentinel);
+  reload_context->AddBecomeMapping(old_deleted_enum_sentinel,
+                                   deleted_enum_sentinel);
 
   if (enums_deleted) {
     // Map all deleted enums to the deleted enum sentinel value.
@@ -432,8 +432,7 @@
       ASSERT(!enum_ident.IsNull());
       old_enum_value ^= enum_map.GetOrNull(enum_ident);
       VTIR_Print("Element `%s` was deleted\n", enum_ident.ToCString());
-      reload_context->AddEnumBecomeMapping(old_enum_value,
-                                           deleted_enum_sentinel);
+      reload_context->AddBecomeMapping(old_enum_value, deleted_enum_sentinel);
     }
     enum_map.Release();
   }
diff --git a/runtime/vm/thread.cc b/runtime/vm/thread.cc
index 6ca5b69..8c41eee 100644
--- a/runtime/vm/thread.cc
+++ b/runtime/vm/thread.cc
@@ -466,10 +466,7 @@
             "\tisolate:    %s\n",
             isolate()->name());
       }
-      NoSafepointScope no_safepoint;
-      ErrorPtr error = Thread::Current()->StealStickyError();
-      ASSERT(error->IsUnwindError());
-      return error;
+      return StealStickyError();
     }
   }
   return Error::null();
diff --git a/runtime/vm/type_testing_stubs.cc b/runtime/vm/type_testing_stubs.cc
index 8b57841..5742615 100644
--- a/runtime/vm/type_testing_stubs.cc
+++ b/runtime/vm/type_testing_stubs.cc
@@ -181,6 +181,14 @@
       if (!code.IsNull()) {
         return code.ptr();
       }
+      const Error& error = Error::Handle(Thread::Current()->StealStickyError());
+      if (!error.IsNull()) {
+        if (error.ptr() == Object::out_of_memory_error().ptr()) {
+          Exceptions::ThrowOOM();
+        } else {
+          UNREACHABLE();
+        }
+      }
 
       // Fall back to default.
 #else
@@ -220,6 +228,9 @@
       if (error.ptr() == Object::branch_offset_error().ptr()) {
         ASSERT(!use_far_branches);
         use_far_branches = true;
+      } else if (error.ptr() == Object::out_of_memory_error().ptr()) {
+        thread->set_sticky_error(error);
+        return Code::null();
       } else {
         UNREACHABLE();
       }
diff --git a/runtime/vm/virtual_memory_compressed.cc b/runtime/vm/virtual_memory_compressed.cc
index a53007a..2f39734 100644
--- a/runtime/vm/virtual_memory_compressed.cc
+++ b/runtime/vm/virtual_memory_compressed.cc
@@ -53,7 +53,7 @@
   ASSERT(Utils::IsAligned(base_, kCompressedHeapPageSize));
   ASSERT(Utils::IsAligned(size_, kCompressedHeapPageSize));
   // base_ is not necessarily 4GB-aligned, because on some systems we can't make
-  // a large enough reservation to guarentee it. Instead, we have only the
+  // a large enough reservation to guarantee it. Instead, we have only the
   // weaker property that all addresses in [base_, base_ + size_) have the same
   // same upper 32 bits, which is what we really need for compressed pointers.
   intptr_t mask = ~(kCompressedHeapAlignment - 1);
diff --git a/tests/language/operator/invalid_operators_test.dart b/tests/language/operator/invalid_operators_test.dart
index fba4e06..066f059 100644
--- a/tests/language/operator/invalid_operators_test.dart
+++ b/tests/language/operator/invalid_operators_test.dart
@@ -496,97 +496,97 @@
   //       ^
   // [cfe] Declared type variables of 'Operators7.==' doesn't match those on overridden method 'Object.=='.
   //         ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //          ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator ><T>(a) => true;
   //        ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //         ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator <=<T>(a) => true;
   //         ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //          ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator >=<T>(a) => true;
   //         ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //          ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator -<T>() => true;
   //        ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //         ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator -<T>(a) => true;
   //        ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //         ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator +<T>(a) => true;
   //        ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //         ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator /<T>(a) => true;
   //        ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //         ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator ~/<T>(a) => true;
   //         ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //          ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator *<T>(a) => true;
   //        ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //         ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator %<T>(a) => true;
   //        ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //         ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator |<T>(a) => true;
   //        ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //         ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator ^<T>(a) => true;
   //        ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //         ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator &<T>(a) => true;
   //        ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //         ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator <<<T>(a) => true;
   //         ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //          ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator >><T>(a) => true;
   //         ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //          ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator []=<T>(a, b) => true;
   //          ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //           ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator []<T>(a) => true;
   //         ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //          ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator ~<T, S>() => true;
   //        ^^^^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //         ^
   // [cfe] Types parameters aren't allowed when defining an operator.
 }
diff --git a/tests/language/operator/operator_triple_shift_error_test.dart b/tests/language/operator/operator_triple_shift_error_test.dart
index 5f0ad8a..36416d1 100644
--- a/tests/language/operator/operator_triple_shift_error_test.dart
+++ b/tests/language/operator/operator_triple_shift_error_test.dart
@@ -49,7 +49,7 @@
   //                    ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   //                   ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
 }
 
 // Operators cannot be static.
diff --git a/tests/language_2/language_2_dartdevc.status b/tests/language_2/language_2_dartdevc.status
index 0c012d1..dbb1e79 100644
--- a/tests/language_2/language_2_dartdevc.status
+++ b/tests/language_2/language_2_dartdevc.status
@@ -16,6 +16,9 @@
 [ $compiler == dartdevk && !$checked ]
 assert/initializer_const_error2_test/*: SkipByDesign # DDC does not support non-checked mode.
 
+[ ($compiler == dartdevc || $compiler == dartdevk) && ($runtime == ff || $runtime == firefox) ]
+async/return_throw_test: Skip # Flaky but not enough to be detected. Re-enable pending a decision on the correct behavior. https://github.com/dart-lang/sdk/issues/44395
+
 [ $compiler == dartdevc || $compiler == dartdevk ]
 async_star/throw_in_catch_test: Skip # Times out. Issue 29920
 external_abstract_fields/external_fields_test: SkipByDesign # Non-JS-interop external members are not supported
diff --git a/tests/language_2/operator/invalid_operators_test.dart b/tests/language_2/operator/invalid_operators_test.dart
index 977c644..fdea2ee 100644
--- a/tests/language_2/operator/invalid_operators_test.dart
+++ b/tests/language_2/operator/invalid_operators_test.dart
@@ -497,97 +497,97 @@
   //       ^
   // [cfe] Declared type variables of 'Operators7.==' doesn't match those on overridden method 'Object.=='.
   //         ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //          ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator ><T>(a) => true;
   //        ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //         ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator <=<T>(a) => true;
   //         ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //          ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator >=<T>(a) => true;
   //         ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //          ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator -<T>() => true;
   //        ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //         ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator -<T>(a) => true;
   //        ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //         ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator +<T>(a) => true;
   //        ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //         ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator /<T>(a) => true;
   //        ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //         ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator ~/<T>(a) => true;
   //         ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //          ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator *<T>(a) => true;
   //        ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //         ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator %<T>(a) => true;
   //        ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //         ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator |<T>(a) => true;
   //        ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //         ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator ^<T>(a) => true;
   //        ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //         ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator &<T>(a) => true;
   //        ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //         ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator <<<T>(a) => true;
   //         ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //          ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator >><T>(a) => true;
   //         ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //          ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator []=<T>(a, b) => true;
   //          ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //           ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator []<T>(a) => true;
   //         ^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //          ^
   // [cfe] Types parameters aren't allowed when defining an operator.
   operator ~<T, S>() => true;
   //        ^^^^^^
-  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETERS_ON_OPERATOR
+  // [analyzer] SYNTACTIC_ERROR.TYPE_PARAMETER_ON_OPERATOR
   //         ^
   // [cfe] Types parameters aren't allowed when defining an operator.
 }
diff --git a/tests/standalone/check_for_aot_snapshot_jit_test.dart b/tests/standalone/check_for_aot_snapshot_jit_test.dart
index 92f39cd..27e8311 100644
--- a/tests/standalone/check_for_aot_snapshot_jit_test.dart
+++ b/tests/standalone/check_for_aot_snapshot_jit_test.dart
@@ -19,10 +19,7 @@
       "Can't locate gen_kernel$_batchSuffix on this platform");
   Expect.isTrue(File(genKernel).existsSync(),
       "Can't locate gen_kernel$_batchSuffix on this platform");
-  // Currently gen_snapshot is only in buildDir/clang_x64 on Mac ARM64.
-  final genSnapshot = Platform.isMacOS && buildDir.endsWith('XARM64')
-      ? path.join(buildDir, 'clang_x64', 'gen_snapshot$_execSuffix')
-      : path.join(buildDir, 'gen_snapshot$_execSuffix');
+  final genSnapshot = path.join(buildDir, 'gen_snapshot$_execSuffix');
   Expect.isTrue(File(genSnapshot).existsSync(),
       "Can't locate gen_snapshot$_execSuffix on this platform");
 
diff --git a/tests/standalone_2/check_for_aot_snapshot_jit_test.dart b/tests/standalone_2/check_for_aot_snapshot_jit_test.dart
index d446a7b..521fa86 100644
--- a/tests/standalone_2/check_for_aot_snapshot_jit_test.dart
+++ b/tests/standalone_2/check_for_aot_snapshot_jit_test.dart
@@ -19,10 +19,7 @@
       path.join(sdkDir, 'pkg', 'vm', 'tool', 'gen_kernel$_batchSuffix');
   Expect.isTrue(File(genKernel).existsSync(),
       "Can't locate gen_kernel$_batchSuffix on this platform");
-  // Currently gen_snapshot is only in buildDir/clang_x64 on Mac ARM64.
-  final genSnapshot = Platform.isMacOS && buildDir.endsWith('XARM64')
-      ? path.join(buildDir, 'clang_x64', 'gen_snapshot$_execSuffix')
-      : path.join(buildDir, 'gen_snapshot$_execSuffix');
+  final genSnapshot = path.join(buildDir, 'gen_snapshot$_execSuffix');
   Expect.isTrue(File(genSnapshot).existsSync(),
       "Can't locate gen_snapshot$_execSuffix on this platform");
 
diff --git a/tools/VERSION b/tools/VERSION
index 2253081..e31f9b6 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 15
 PATCH 0
-PRERELEASE 158
+PRERELEASE 159
 PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json
index 50dc881..892e142 100644
--- a/tools/bots/test_matrix.json
+++ b/tools/bots/test_matrix.json
@@ -2936,7 +2936,7 @@
             "create_sdk"
           ],
           "environment": {
-            "DART_GN_ARGS": "mac_use_goma_rbe=true dart_snapshot_kind=\"app-jit\""
+            "DART_GN_ARGS": "mac_use_goma_rbe=true"
           }
         },
         {
diff --git a/tools/gn.py b/tools/gn.py
index 108adca..cecd030 100755
--- a/tools/gn.py
+++ b/tools/gn.py
@@ -5,6 +5,7 @@
 
 import argparse
 import os
+import platform
 import subprocess
 import sys
 import time
@@ -66,7 +67,39 @@
     return [merge(x, y) for x, y in gn_args.items()]
 
 
+# Runs true if the currently executing python interpreter is running under
+# Rosetta. I.e., python3 is an x64 executable and we're on an arm64 Mac.
+def IsRosetta():
+    if platform.system() == 'Darwin':
+        p = subprocess.Popen(['sysctl', '-in', 'sysctl.proc_translated'],
+                             stdout=subprocess.PIPE,
+                             stderr=subprocess.STDOUT)
+        output, _ = p.communicate()
+        return output.decode('utf-8').strip() == '1'
+    return False
+
+
 def HostCpuForArch(arch):
+    # Check for Rosetta before checking platform.machine(), as the latter
+    # returns 'x86_64' when running under Rosetta.
+    if IsRosetta():
+        if arch in ['x64', 'x64c']:
+            # Without this case, we would try to build with
+            #   host_cpu="arm64"
+            #   target_cpu="x64"
+            #   dart_target_arch="x64"
+            # Which requires the VM to use an x64 simulator in the host
+            # arm64 binaries, and this simulator is unimplemented.
+            return 'x64'
+        else:
+            return 'arm64'
+
+    m = platform.machine()
+    if m == 'aarch64' or m == 'arm64':
+        return 'arm64'
+    if m == 'armv7l' or m == 'armv6l':
+        return 'arm'
+
     if arch in ['ia32', 'arm', 'armv6', 'simarm', 'simarmv6', 'simarm_x64']:
         return 'x86'
     if arch in [