Version 2.14.0-9.0.dev

Merge commit 'b802aadf7150af606b8cc5ba87d7a8b39c102292' into 'dev'
diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json
index ef7591c..5bd7c51 100644
--- a/.dart_tool/package_config.json
+++ b/.dart_tool/package_config.json
@@ -11,7 +11,7 @@
     "constraint, update this by running tools/generate_package_config.dart."
   ],
   "configVersion": 2,
-  "generated": "2021-04-13T13:32:11.977579",
+  "generated": "2021-04-16T13:34:20.183158",
   "generator": "tools/generate_package_config.dart",
   "packages": [
     {
@@ -749,7 +749,7 @@
       "name": "vm_snapshot_analysis",
       "rootUri": "../pkg/vm_snapshot_analysis",
       "packageUri": "lib/",
-      "languageVersion": "2.8"
+      "languageVersion": "2.12"
     },
     {
       "name": "wasm",
diff --git a/.gn b/.gn
index fc54abe..a9e618f 100644
--- a/.gn
+++ b/.gn
@@ -12,3 +12,6 @@
 # GN build files are placed when they can not be placed directly
 # in the source tree, e.g. for third party source trees.
 secondary_source = "//build/secondary/"
+
+# Override the default script executable to always be python3.
+script_executable = "python3"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1a4558d..2b563cd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -152,7 +152,14 @@
 
 [#44211]: https://github.com/dart-lang/sdk/issues/44211
 
-## 2.12.3 - 2021-04-12
+## 2.12.4 - 2021-04-15
+
+This is a patch release that fixes a Dart VM compiler crashes when compiling
+initializers containing async closures (issue [#45306][]).
+
+[#45306]: https://github.com/dart-lang/sdk/issues/45306
+
+## 2.12.3 - 2021-04-14
 
 This is a patch release that fixes a vulnerability in `dart:html` related to
 DOM clobbering. Thanks again to **Vincenzo di Cicco** for finding and reporting
diff --git a/DEPS b/DEPS
index 5c44d05..9f97b1f 100644
--- a/DEPS
+++ b/DEPS
@@ -73,7 +73,7 @@
 
   # Revisions of /third_party/* dependencies.
   "args_rev": "d8fea36c10ef96797be02e3d132d572445cd86f4",
-  "async_rev": "376d418b1b535030fbe3369938d2ffdbb0340a77",
+  "async_rev": "376c0fe95fa6fe7a5a6c41ef7b4585085c826f89",
   "bazel_worker_rev": "0885637b037979afbf5bcd05fd748b309fd669c0",
   "benchmark_harness_rev": "c546dbd9f639f75cd2f75de8df2eb9f8ea15e8e7",
   "boolean_selector_rev": "665e6921ab246569420376f827bff4585dff0b14",
@@ -268,8 +268,8 @@
 
   Var("dart_root") + "/third_party/gsutil": {
       "packages": [{
-          "package": "infra/gsutil",
-          "version": "version:4.34",
+          "package": "infra/3pp/tools/gsutil",
+          "version": "version:4.58",
       }],
       "dep_type": "cipd",
   },
@@ -672,59 +672,56 @@
     # Pull Debian sysroot for i386 Linux
     'name': 'sysroot_i386',
     'pattern': '.',
-    'action': ['python', 'sdk/build/linux/sysroot_scripts/install-sysroot.py',
+    'action': ['python3', 'sdk/build/linux/sysroot_scripts/install-sysroot.py',
                '--arch', 'i386'],
   },
   {
     # Pull Debian sysroot for amd64 Linux
     'name': 'sysroot_amd64',
     'pattern': '.',
-    'action': ['python', 'sdk/build/linux/sysroot_scripts/install-sysroot.py',
+    'action': ['python3', 'sdk/build/linux/sysroot_scripts/install-sysroot.py',
                '--arch', 'amd64'],
   },
   {
     # Pull Debian sysroot for arm Linux
     'name': 'sysroot_amd64',
     'pattern': '.',
-    'action': ['python', 'sdk/build/linux/sysroot_scripts/install-sysroot.py',
+    'action': ['python3', 'sdk/build/linux/sysroot_scripts/install-sysroot.py',
                '--arch', 'arm'],
   },
   {
     # Pull Debian jessie sysroot for arm64 Linux
     'name': 'sysroot_amd64',
     'pattern': '.',
-    'action': ['python', 'sdk/build/linux/sysroot_scripts/install-sysroot.py',
+    'action': ['python3', 'sdk/build/linux/sysroot_scripts/install-sysroot.py',
                '--arch', 'arm64'],
   },
   {
     'name': 'buildtools',
     'pattern': '.',
-    'action': ['python', 'sdk/tools/buildtools/update.py'],
+    'action': ['python3', 'sdk/tools/buildtools/update.py'],
   },
   {
     # Update the Windows toolchain if necessary.
     'name': 'win_toolchain',
     'pattern': '.',
-    'action': ['python', 'sdk/build/vs_toolchain.py', 'update'],
+    'action': ['python3', 'sdk/build/vs_toolchain.py', 'update'],
+    'condition': 'checkout_win'
+  },
+  {
+    "name": "7zip",
+    "pattern": ".",
+    "action": [
+      "download_from_google_storage",
+      "--no_auth",
+      "--no_resume",
+      "--bucket",
+      "dart-dependencies",
+      "--platform=win32",
+      "--extract",
+      "-s",
+      Var('dart_root') + "/third_party/7zip.tar.gz.sha1",
+    ],
+    'condition': 'checkout_win'
   },
 ]
-
-hooks_os = {
-  "win": [
-    {
-      "name": "7zip",
-      "pattern": ".",
-      "action": [
-        "download_from_google_storage",
-        "--no_auth",
-        "--no_resume",
-        "--bucket",
-        "dart-dependencies",
-        "--platform=win32",
-        "--extract",
-        "-s",
-        Var('dart_root') + "/third_party/7zip.tar.gz.sha1",
-      ],
-    },
-  ]
-}
diff --git a/benchmarks/Richards/dart/Richards.dart b/benchmarks/Richards/dart/Richards.dart
new file mode 100644
index 0000000..f930651
--- /dev/null
+++ b/benchmarks/Richards/dart/Richards.dart
@@ -0,0 +1,446 @@
+// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Ported by the Dart team to Dart.
+
+// This is a Dart implementation of the Richards benchmark from:
+//
+//    http://www.cl.cam.ac.uk/~mr10/Bench.html
+//
+// The benchmark was originally implemented in BCPL by
+// Martin Richards.
+
+import 'package:benchmark_harness/benchmark_harness.dart';
+
+void main() {
+  const Richards().report();
+}
+
+/// Richards imulates the task dispatcher of an operating system.
+class Richards extends BenchmarkBase {
+  const Richards() : super('Richards');
+
+  @override
+  void run() {
+    final Scheduler scheduler = Scheduler();
+    scheduler.addIdleTask(ID_IDLE, 0, null, COUNT);
+
+    Packet queue = Packet(null, ID_WORKER, KIND_WORK);
+    queue = Packet(queue, ID_WORKER, KIND_WORK);
+    scheduler.addWorkerTask(ID_WORKER, 1000, queue);
+
+    queue = Packet(null, ID_DEVICE_A, KIND_DEVICE);
+    queue = Packet(queue, ID_DEVICE_A, KIND_DEVICE);
+    queue = Packet(queue, ID_DEVICE_A, KIND_DEVICE);
+    scheduler.addHandlerTask(ID_HANDLER_A, 2000, queue);
+
+    queue = Packet(null, ID_DEVICE_B, KIND_DEVICE);
+    queue = Packet(queue, ID_DEVICE_B, KIND_DEVICE);
+    queue = Packet(queue, ID_DEVICE_B, KIND_DEVICE);
+    scheduler.addHandlerTask(ID_HANDLER_B, 3000, queue);
+
+    scheduler.addDeviceTask(ID_DEVICE_A, 4000, null);
+
+    scheduler.addDeviceTask(ID_DEVICE_B, 5000, null);
+
+    scheduler.schedule();
+
+    if (scheduler.queueCount != EXPECTED_QUEUE_COUNT ||
+        scheduler.holdCount != EXPECTED_HOLD_COUNT) {
+      print('Error during execution: queueCount = ${scheduler.queueCount}'
+          ', holdCount = ${scheduler.holdCount}.');
+    }
+    if (EXPECTED_QUEUE_COUNT != scheduler.queueCount) {
+      throw 'bad scheduler queue-count';
+    }
+    if (EXPECTED_HOLD_COUNT != scheduler.holdCount) {
+      throw 'bad scheduler hold-count';
+    }
+  }
+
+  static const int DATA_SIZE = 4;
+  static const int COUNT = 1000;
+
+  /// These two constants specify how many times a packet is queued and
+  /// how many times a task is put on hold in a correct run of richards.
+  /// They don't have any meaning a such but are characteristic of a
+  /// correct run so if the actual queue or hold count is different from
+  /// the expected there must be a bug in the implementation.
+  static const int EXPECTED_QUEUE_COUNT = 2322;
+  static const int EXPECTED_HOLD_COUNT = 928;
+
+  static const int ID_IDLE = 0;
+  static const int ID_WORKER = 1;
+  static const int ID_HANDLER_A = 2;
+  static const int ID_HANDLER_B = 3;
+  static const int ID_DEVICE_A = 4;
+  static const int ID_DEVICE_B = 5;
+  static const int NUMBER_OF_IDS = 6;
+
+  static const int KIND_DEVICE = 0;
+  static const int KIND_WORK = 1;
+}
+
+/// A scheduler can be used to schedule a set of tasks based on their relative
+/// priorities.  Scheduling is done by maintaining a list of task control blocks
+/// which holds tasks and the data queue they are processing.
+class Scheduler {
+  int queueCount = 0;
+  int holdCount = 0;
+  TaskControlBlock? currentTcb;
+  int currentId = Richards.ID_IDLE;
+  TaskControlBlock? list;
+  final List<TaskControlBlock?> blocks =
+      List<TaskControlBlock?>.filled(Richards.NUMBER_OF_IDS, null);
+
+  /// Add an idle task to this scheduler.
+  void addIdleTask(int id, int priority, Packet? queue, int count) {
+    addRunningTask(id, priority, queue, IdleTask(this, 1, count));
+  }
+
+  /// Add a work task to this scheduler.
+  void addWorkerTask(int id, int priority, Packet? queue) {
+    addTask(id, priority, queue, WorkerTask(this, Richards.ID_HANDLER_A, 0));
+  }
+
+  /// Add a handler task to this scheduler.
+  void addHandlerTask(int id, int priority, Packet? queue) {
+    addTask(id, priority, queue, HandlerTask(this));
+  }
+
+  /// Add a handler task to this scheduler.
+  void addDeviceTask(int id, int priority, Packet? queue) {
+    addTask(id, priority, queue, DeviceTask(this));
+  }
+
+  /// Add the specified task and mark it as running.
+  void addRunningTask(int id, int priority, Packet? queue, Task task) {
+    addTask(id, priority, queue, task);
+    currentTcb!.setRunning();
+  }
+
+  /// Add the specified task to this scheduler.
+  void addTask(int id, int priority, Packet? queue, Task task) {
+    currentTcb = TaskControlBlock(list, id, priority, queue, task);
+    list = currentTcb;
+    blocks[id] = currentTcb;
+  }
+
+  /// Execute the tasks managed by this scheduler.
+  void schedule() {
+    currentTcb = list;
+    while (currentTcb != null) {
+      if (currentTcb!.isHeldOrSuspended()) {
+        currentTcb = currentTcb!.link;
+      } else {
+        currentId = currentTcb!.id;
+        currentTcb = currentTcb!.run();
+      }
+    }
+  }
+
+  /// Release a task that is currently blocked and return the next block to run.
+  TaskControlBlock? release(int id) {
+    final TaskControlBlock? tcb = blocks[id];
+    if (tcb == null) return tcb;
+    tcb.markAsNotHeld();
+    if (tcb.priority > currentTcb!.priority) return tcb;
+    return currentTcb;
+  }
+
+  /// Block the currently executing task and return the next task control block
+  /// to run.  The blocked task will not be made runnable until it is explicitly
+  /// released, even if new work is added to it.
+  TaskControlBlock? holdCurrent() {
+    holdCount++;
+    currentTcb!.markAsHeld();
+    return currentTcb!.link;
+  }
+
+  /// Suspend the currently executing task and return the next task
+  /// control block to run.
+  /// If new work is added to the suspended task it will be made runnable.
+  TaskControlBlock suspendCurrent() {
+    currentTcb!.markAsSuspended();
+    return currentTcb!;
+  }
+
+  /// Add the specified packet to the end of the worklist used by the task
+  /// associated with the packet and make the task runnable if it is currently
+  /// suspended.
+  TaskControlBlock? queue(Packet packet) {
+    final TaskControlBlock? t = blocks[packet.id];
+    if (t == null) return t;
+    queueCount++;
+    packet.link = null;
+    packet.id = currentId;
+    return t.checkPriorityAdd(currentTcb!, packet);
+  }
+}
+
+/// A task control block manages a task and the queue of work packages
+/// associated with it.
+class TaskControlBlock {
+  TaskControlBlock? link;
+  int id; // The id of this block.
+  int priority; // The priority of this block.
+  Packet? queue; // The queue of packages to be processed by the task.
+  Task task;
+  int state;
+
+  TaskControlBlock(this.link, this.id, this.priority, this.queue, this.task)
+      : state = queue == null ? STATE_SUSPENDED : STATE_SUSPENDED_RUNNABLE;
+
+  /// The task is running and is currently scheduled.
+  static const int STATE_RUNNING = 0;
+
+  /// The task has packets left to process.
+  static const int STATE_RUNNABLE = 1;
+
+  /// The task is not currently running. The task is not blocked as such and may
+  /// be started by the scheduler.
+  static const int STATE_SUSPENDED = 2;
+
+  /// The task is blocked and cannot be run until it is explicitly released.
+  static const int STATE_HELD = 4;
+
+  static const int STATE_SUSPENDED_RUNNABLE = STATE_SUSPENDED | STATE_RUNNABLE;
+  static const int STATE_NOT_HELD = ~STATE_HELD;
+
+  void setRunning() {
+    state = STATE_RUNNING;
+  }
+
+  void markAsNotHeld() {
+    state = state & STATE_NOT_HELD;
+  }
+
+  void markAsHeld() {
+    state = state | STATE_HELD;
+  }
+
+  bool isHeldOrSuspended() {
+    return (state & STATE_HELD) != 0 || (state == STATE_SUSPENDED);
+  }
+
+  void markAsSuspended() {
+    state = state | STATE_SUSPENDED;
+  }
+
+  void markAsRunnable() {
+    state = state | STATE_RUNNABLE;
+  }
+
+  /// Runs this task, if it is ready to be run, and returns the next
+  /// task to run.
+  TaskControlBlock? run() {
+    Packet? packet;
+    if (state == STATE_SUSPENDED_RUNNABLE) {
+      packet = queue;
+      queue = packet!.link;
+      state = queue == null ? STATE_RUNNING : STATE_RUNNABLE;
+    } else {
+      packet = null;
+    }
+    return task.run(packet);
+  }
+
+  /// Adds a packet to the worklist of this block's task, marks this as
+  /// runnable if necessary, and returns the next runnable object to run
+  /// (the one with the highest priority).
+  TaskControlBlock checkPriorityAdd(TaskControlBlock task, Packet packet) {
+    if (queue == null) {
+      queue = packet;
+      markAsRunnable();
+      if (priority > task.priority) return this;
+    } else {
+      queue = packet.addTo(queue);
+    }
+    return task;
+  }
+
+  @override
+  String toString() => 'tcb { $task@$state }';
+}
+
+///  Abstract task that manipulates work packets.
+abstract class Task {
+  Scheduler scheduler; // The scheduler that manages this task.
+
+  Task(this.scheduler);
+
+  TaskControlBlock? run(Packet? packet);
+}
+
+/// An idle task doesn't do any work itself but cycles control between the two
+/// device tasks.
+class IdleTask extends Task {
+  int v1; // A seed value that controls how the device tasks are scheduled.
+  int count; // The number of times this task should be scheduled.
+
+  IdleTask(Scheduler scheduler, this.v1, this.count) : super(scheduler);
+
+  @override
+  TaskControlBlock? run(Packet? packet) {
+    count--;
+    if (count == 0) return scheduler.holdCurrent();
+    if ((v1 & 1) == 0) {
+      v1 = v1 >> 1;
+      return scheduler.release(Richards.ID_DEVICE_A);
+    }
+    v1 = (v1 >> 1) ^ 0xD008;
+    return scheduler.release(Richards.ID_DEVICE_B);
+  }
+
+  @override
+  String toString() => 'IdleTask';
+}
+
+/// A task that suspends itself after each time it has been run to simulate
+/// waiting for data from an external device.
+class DeviceTask extends Task {
+  Packet? v1;
+
+  DeviceTask(Scheduler scheduler) : super(scheduler);
+
+  @override
+  TaskControlBlock? run(Packet? packet) {
+    if (packet == null) {
+      if (v1 == null) return scheduler.suspendCurrent();
+      final Packet v = v1!;
+      v1 = null;
+      return scheduler.queue(v);
+    }
+    v1 = packet;
+    return scheduler.holdCurrent();
+  }
+
+  @override
+  String toString() => 'DeviceTask';
+}
+
+/// A task that manipulates work packets.
+class WorkerTask extends Task {
+  int v1; // A seed used to specify how work packets are manipulated.
+  int v2; // Another seed used to specify how work packets are manipulated.
+
+  WorkerTask(Scheduler scheduler, this.v1, this.v2) : super(scheduler);
+
+  @override
+  TaskControlBlock? run(Packet? packet) {
+    if (packet == null) {
+      return scheduler.suspendCurrent();
+    }
+    if (v1 == Richards.ID_HANDLER_A) {
+      v1 = Richards.ID_HANDLER_B;
+    } else {
+      v1 = Richards.ID_HANDLER_A;
+    }
+    packet.id = v1;
+    packet.a1 = 0;
+    for (int i = 0; i < Richards.DATA_SIZE; i++) {
+      v2++;
+      if (v2 > 26) v2 = 1;
+      packet.a2[i] = v2;
+    }
+    return scheduler.queue(packet);
+  }
+
+  @override
+  String toString() => 'WorkerTask';
+}
+
+/// A task that manipulates work packets and then suspends itself.
+class HandlerTask extends Task {
+  Packet? v1;
+  Packet? v2;
+
+  HandlerTask(Scheduler scheduler) : super(scheduler);
+
+  @override
+  TaskControlBlock? run(Packet? packet) {
+    if (packet != null) {
+      if (packet.kind == Richards.KIND_WORK) {
+        v1 = packet.addTo(v1);
+      } else {
+        v2 = packet.addTo(v2);
+      }
+    }
+    if (v1 != null) {
+      final int count = v1!.a1;
+      if (count < Richards.DATA_SIZE) {
+        if (v2 != null) {
+          final Packet v = v2!;
+          v2 = v.link;
+          v.a1 = v1!.a2[count];
+          v1!.a1 = count + 1;
+          return scheduler.queue(v);
+        }
+      } else {
+        final Packet v = v1!;
+        v1 = v.link;
+        return scheduler.queue(v);
+      }
+    }
+    return scheduler.suspendCurrent();
+  }
+
+  @override
+  String toString() => 'HandlerTask';
+}
+
+/// A simple package of data that is manipulated by the tasks.  The exact layout
+/// of the payload data carried by a packet is not importaint, and neither is
+/// the nature of the work performed on packets by the tasks. Besides carrying
+/// data, packets form linked lists and are hence used both as data and
+/// worklists.
+class Packet {
+  Packet? link; // The tail of the linked list of packets.
+  int id; // An ID for this packet.
+  int kind; // The type of this packet.
+  int a1 = 0;
+
+  List<int> a2 = List<int>.filled(Richards.DATA_SIZE, 0);
+
+  Packet(this.link, this.id, this.kind);
+
+  /// Add this packet to the end of a worklist, and return the worklist.
+  Packet addTo(Packet? queue) {
+    link = null;
+    if (queue == null) return this;
+    Packet? peek;
+    Packet next = queue;
+    while ((peek = next.link) != null) {
+      next = peek!;
+    }
+    next.link = this;
+    return queue;
+  }
+
+  @override
+  String toString() => 'Packet';
+}
diff --git a/benchmarks/Richards/dart2/Richards.dart b/benchmarks/Richards/dart2/Richards.dart
new file mode 100644
index 0000000..f479a32
--- /dev/null
+++ b/benchmarks/Richards/dart2/Richards.dart
@@ -0,0 +1,449 @@
+// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Ported by the Dart team to Dart.
+
+// This is a Dart implementation of the Richards benchmark from:
+//
+//    http://www.cl.cam.ac.uk/~mr10/Bench.html
+//
+// The benchmark was originally implemented in BCPL by
+// Martin Richards.
+
+// @dart=2.9
+
+import 'package:benchmark_harness/benchmark_harness.dart';
+
+void main() {
+  const Richards().report();
+}
+
+/// Richards imulates the task dispatcher of an operating system.
+class Richards extends BenchmarkBase {
+  const Richards() : super('Richards');
+
+  @override
+  void run() {
+    final Scheduler scheduler = Scheduler();
+    scheduler.addIdleTask(ID_IDLE, 0, null, COUNT);
+
+    Packet queue = Packet(null, ID_WORKER, KIND_WORK);
+    queue = Packet(queue, ID_WORKER, KIND_WORK);
+    scheduler.addWorkerTask(ID_WORKER, 1000, queue);
+
+    queue = Packet(null, ID_DEVICE_A, KIND_DEVICE);
+    queue = Packet(queue, ID_DEVICE_A, KIND_DEVICE);
+    queue = Packet(queue, ID_DEVICE_A, KIND_DEVICE);
+    scheduler.addHandlerTask(ID_HANDLER_A, 2000, queue);
+
+    queue = Packet(null, ID_DEVICE_B, KIND_DEVICE);
+    queue = Packet(queue, ID_DEVICE_B, KIND_DEVICE);
+    queue = Packet(queue, ID_DEVICE_B, KIND_DEVICE);
+    scheduler.addHandlerTask(ID_HANDLER_B, 3000, queue);
+
+    scheduler.addDeviceTask(ID_DEVICE_A, 4000, null);
+
+    scheduler.addDeviceTask(ID_DEVICE_B, 5000, null);
+
+    scheduler.schedule();
+
+    if (scheduler.queueCount != EXPECTED_QUEUE_COUNT ||
+        scheduler.holdCount != EXPECTED_HOLD_COUNT) {
+      print('Error during execution: queueCount = ${scheduler.queueCount}'
+          ', holdCount = ${scheduler.holdCount}.');
+    }
+    if (EXPECTED_QUEUE_COUNT != scheduler.queueCount) {
+      throw 'bad scheduler queue-count';
+    }
+    if (EXPECTED_HOLD_COUNT != scheduler.holdCount) {
+      throw 'bad scheduler hold-count';
+    }
+  }
+
+  static const int DATA_SIZE = 4;
+  static const int COUNT = 1000;
+
+  /// These two constants specify how many times a packet is queued and
+  /// how many times a task is put on hold in a correct run of richards.
+  /// They don't have any meaning a such but are characteristic of a
+  /// correct run so if the actual queue or hold count is different from
+  /// the expected there must be a bug in the implementation.
+  static const int EXPECTED_QUEUE_COUNT = 2322;
+  static const int EXPECTED_HOLD_COUNT = 928;
+
+  static const int ID_IDLE = 0;
+  static const int ID_WORKER = 1;
+  static const int ID_HANDLER_A = 2;
+  static const int ID_HANDLER_B = 3;
+  static const int ID_DEVICE_A = 4;
+  static const int ID_DEVICE_B = 5;
+  static const int NUMBER_OF_IDS = 6;
+
+  static const int KIND_DEVICE = 0;
+  static const int KIND_WORK = 1;
+}
+
+/// A scheduler can be used to schedule a set of tasks based on their relative
+/// priorities.  Scheduling is done by maintaining a list of task control blocks
+/// which holds tasks and the data queue they are processing.
+class Scheduler {
+  int queueCount = 0;
+  int holdCount = 0;
+  TaskControlBlock currentTcb;
+  int currentId;
+  TaskControlBlock list;
+  List<TaskControlBlock> blocks =
+      List<TaskControlBlock>(Richards.NUMBER_OF_IDS);
+
+  /// Add an idle task to this scheduler.
+  void addIdleTask(int id, int priority, Packet queue, int count) {
+    addRunningTask(id, priority, queue, IdleTask(this, 1, count));
+  }
+
+  /// Add a work task to this scheduler.
+  void addWorkerTask(int id, int priority, Packet queue) {
+    addTask(id, priority, queue, WorkerTask(this, Richards.ID_HANDLER_A, 0));
+  }
+
+  /// Add a handler task to this scheduler.
+  void addHandlerTask(int id, int priority, Packet queue) {
+    addTask(id, priority, queue, HandlerTask(this));
+  }
+
+  /// Add a handler task to this scheduler.
+  void addDeviceTask(int id, int priority, Packet queue) {
+    addTask(id, priority, queue, DeviceTask(this));
+  }
+
+  /// Add the specified task and mark it as running.
+  void addRunningTask(int id, int priority, Packet queue, Task task) {
+    addTask(id, priority, queue, task);
+    currentTcb.setRunning();
+  }
+
+  /// Add the specified task to this scheduler.
+  void addTask(int id, int priority, Packet queue, Task task) {
+    currentTcb = TaskControlBlock(list, id, priority, queue, task);
+    list = currentTcb;
+    blocks[id] = currentTcb;
+  }
+
+  /// Execute the tasks managed by this scheduler.
+  void schedule() {
+    currentTcb = list;
+    while (currentTcb != null) {
+      if (currentTcb.isHeldOrSuspended()) {
+        currentTcb = currentTcb.link;
+      } else {
+        currentId = currentTcb.id;
+        currentTcb = currentTcb.run();
+      }
+    }
+  }
+
+  /// Release a task that is currently blocked and return the next block to run.
+  TaskControlBlock release(int id) {
+    final TaskControlBlock tcb = blocks[id];
+    if (tcb == null) return tcb;
+    tcb.markAsNotHeld();
+    if (tcb.priority > currentTcb.priority) return tcb;
+    return currentTcb;
+  }
+
+  /// Block the currently executing task and return the next task control block
+  /// to run.  The blocked task will not be made runnable until it is explicitly
+  /// released, even if new work is added to it.
+  TaskControlBlock holdCurrent() {
+    holdCount++;
+    currentTcb.markAsHeld();
+    return currentTcb.link;
+  }
+
+  /// Suspend the currently executing task and return the next task
+  /// control block to run.
+  /// If new work is added to the suspended task it will be made runnable.
+  TaskControlBlock suspendCurrent() {
+    currentTcb.markAsSuspended();
+    return currentTcb;
+  }
+
+  /// Add the specified packet to the end of the worklist used by the task
+  /// associated with the packet and make the task runnable if it is currently
+  /// suspended.
+  TaskControlBlock queue(Packet packet) {
+    final TaskControlBlock t = blocks[packet.id];
+    if (t == null) return t;
+    queueCount++;
+    packet.link = null;
+    packet.id = currentId;
+    return t.checkPriorityAdd(currentTcb, packet);
+  }
+}
+
+/// A task control block manages a task and the queue of work packages
+/// associated with it.
+class TaskControlBlock {
+  TaskControlBlock link;
+  int id; // The id of this block.
+  int priority; // The priority of this block.
+  Packet queue; // The queue of packages to be processed by the task.
+  Task task;
+  int state;
+
+  TaskControlBlock(this.link, this.id, this.priority, this.queue, this.task) {
+    state = queue == null ? STATE_SUSPENDED : STATE_SUSPENDED_RUNNABLE;
+  }
+
+  /// The task is running and is currently scheduled.
+  static const int STATE_RUNNING = 0;
+
+  /// The task has packets left to process.
+  static const int STATE_RUNNABLE = 1;
+
+  /// The task is not currently running. The task is not blocked as such and may
+  /// be started by the scheduler.
+  static const int STATE_SUSPENDED = 2;
+
+  /// The task is blocked and cannot be run until it is explicitly released.
+  static const int STATE_HELD = 4;
+
+  static const int STATE_SUSPENDED_RUNNABLE = STATE_SUSPENDED | STATE_RUNNABLE;
+  static const int STATE_NOT_HELD = ~STATE_HELD;
+
+  void setRunning() {
+    state = STATE_RUNNING;
+  }
+
+  void markAsNotHeld() {
+    state = state & STATE_NOT_HELD;
+  }
+
+  void markAsHeld() {
+    state = state | STATE_HELD;
+  }
+
+  bool isHeldOrSuspended() {
+    return (state & STATE_HELD) != 0 || (state == STATE_SUSPENDED);
+  }
+
+  void markAsSuspended() {
+    state = state | STATE_SUSPENDED;
+  }
+
+  void markAsRunnable() {
+    state = state | STATE_RUNNABLE;
+  }
+
+  /// Runs this task, if it is ready to be run, and returns the next
+  /// task to run.
+  TaskControlBlock run() {
+    Packet packet;
+    if (state == STATE_SUSPENDED_RUNNABLE) {
+      packet = queue;
+      queue = packet.link;
+      state = queue == null ? STATE_RUNNING : STATE_RUNNABLE;
+    } else {
+      packet = null;
+    }
+    return task.run(packet);
+  }
+
+  /// Adds a packet to the worklist of this block's task, marks this as
+  /// runnable if necessary, and returns the next runnable object to run
+  /// (the one with the highest priority).
+  TaskControlBlock checkPriorityAdd(TaskControlBlock task, Packet packet) {
+    if (queue == null) {
+      queue = packet;
+      markAsRunnable();
+      if (priority > task.priority) return this;
+    } else {
+      queue = packet.addTo(queue);
+    }
+    return task;
+  }
+
+  @override
+  String toString() => 'tcb { $task@$state }';
+}
+
+///  Abstract task that manipulates work packets.
+abstract class Task {
+  Scheduler scheduler; // The scheduler that manages this task.
+
+  Task(this.scheduler);
+
+  TaskControlBlock run(Packet packet);
+}
+
+/// An idle task doesn't do any work itself but cycles control between the two
+/// device tasks.
+class IdleTask extends Task {
+  int v1; // A seed value that controls how the device tasks are scheduled.
+  int count; // The number of times this task should be scheduled.
+
+  IdleTask(Scheduler scheduler, this.v1, this.count) : super(scheduler);
+
+  @override
+  TaskControlBlock run(Packet packet) {
+    count--;
+    if (count == 0) return scheduler.holdCurrent();
+    if ((v1 & 1) == 0) {
+      v1 = v1 >> 1;
+      return scheduler.release(Richards.ID_DEVICE_A);
+    }
+    v1 = (v1 >> 1) ^ 0xD008;
+    return scheduler.release(Richards.ID_DEVICE_B);
+  }
+
+  @override
+  String toString() => 'IdleTask';
+}
+
+/// A task that suspends itself after each time it has been run to simulate
+/// waiting for data from an external device.
+class DeviceTask extends Task {
+  Packet v1;
+
+  DeviceTask(Scheduler scheduler) : super(scheduler);
+
+  @override
+  TaskControlBlock run(Packet packet) {
+    if (packet == null) {
+      if (v1 == null) return scheduler.suspendCurrent();
+      final Packet v = v1;
+      v1 = null;
+      return scheduler.queue(v);
+    }
+    v1 = packet;
+    return scheduler.holdCurrent();
+  }
+
+  @override
+  String toString() => 'DeviceTask';
+}
+
+/// A task that manipulates work packets.
+class WorkerTask extends Task {
+  int v1; // A seed used to specify how work packets are manipulated.
+  int v2; // Another seed used to specify how work packets are manipulated.
+
+  WorkerTask(Scheduler scheduler, this.v1, this.v2) : super(scheduler);
+
+  @override
+  TaskControlBlock run(Packet packet) {
+    if (packet == null) {
+      return scheduler.suspendCurrent();
+    }
+    if (v1 == Richards.ID_HANDLER_A) {
+      v1 = Richards.ID_HANDLER_B;
+    } else {
+      v1 = Richards.ID_HANDLER_A;
+    }
+    packet.id = v1;
+    packet.a1 = 0;
+    for (int i = 0; i < Richards.DATA_SIZE; i++) {
+      v2++;
+      if (v2 > 26) v2 = 1;
+      packet.a2[i] = v2;
+    }
+    return scheduler.queue(packet);
+  }
+
+  @override
+  String toString() => 'WorkerTask';
+}
+
+/// A task that manipulates work packets and then suspends itself.
+class HandlerTask extends Task {
+  Packet v1;
+  Packet v2;
+
+  HandlerTask(Scheduler scheduler) : super(scheduler);
+
+  @override
+  TaskControlBlock run(Packet packet) {
+    if (packet != null) {
+      if (packet.kind == Richards.KIND_WORK) {
+        v1 = packet.addTo(v1);
+      } else {
+        v2 = packet.addTo(v2);
+      }
+    }
+    if (v1 != null) {
+      final int count = v1.a1;
+      Packet v;
+      if (count < Richards.DATA_SIZE) {
+        if (v2 != null) {
+          v = v2;
+          v2 = v2.link;
+          v.a1 = v1.a2[count];
+          v1.a1 = count + 1;
+          return scheduler.queue(v);
+        }
+      } else {
+        v = v1;
+        v1 = v1.link;
+        return scheduler.queue(v);
+      }
+    }
+    return scheduler.suspendCurrent();
+  }
+
+  @override
+  String toString() => 'HandlerTask';
+}
+
+/// A simple package of data that is manipulated by the tasks.  The exact layout
+/// of the payload data carried by a packet is not importaint, and neither is
+/// the nature of the work performed on packets by the tasks. Besides carrying
+/// data, packets form linked lists and are hence used both as data and
+/// worklists.
+class Packet {
+  Packet link; // The tail of the linked list of packets.
+  int id; // An ID for this packet.
+  int kind; // The type of this packet.
+  int a1 = 0;
+
+  List<int> a2 = List(Richards.DATA_SIZE);
+
+  Packet(this.link, this.id, this.kind);
+
+  /// Add this packet to the end of a worklist, and return the worklist.
+  Packet addTo(Packet queue) {
+    link = null;
+    if (queue == null) return this;
+    Packet peek, next = queue;
+    while ((peek = next.link) != null) {
+      next = peek;
+    }
+    next.link = this;
+    return queue;
+  }
+
+  @override
+  String toString() => 'Packet';
+}
diff --git a/benchmarks/Richards/javascript/Richards.js b/benchmarks/Richards/javascript/Richards.js
new file mode 100644
index 0000000..8a71f99
--- /dev/null
+++ b/benchmarks/Richards/javascript/Richards.js
@@ -0,0 +1,536 @@
+// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+// This is a JavaScript implementation of the Richards
+// benchmark from:
+//
+//    http://www.cl.cam.ac.uk/~mr10/Bench.html
+//
+// The benchmark was originally implemented in BCPL by
+// Martin Richards.
+
+
+/**
+ * The Richards benchmark simulates the task dispatcher of an
+ * operating system.
+ **/
+function runRichards() {
+  var scheduler = new Scheduler();
+  scheduler.addIdleTask(ID_IDLE, 0, null, COUNT);
+
+  var queue = new Packet(null, ID_WORKER, KIND_WORK);
+  queue = new Packet(queue,  ID_WORKER, KIND_WORK);
+  scheduler.addWorkerTask(ID_WORKER, 1000, queue);
+
+  queue = new Packet(null, ID_DEVICE_A, KIND_DEVICE);
+  queue = new Packet(queue,  ID_DEVICE_A, KIND_DEVICE);
+  queue = new Packet(queue,  ID_DEVICE_A, KIND_DEVICE);
+  scheduler.addHandlerTask(ID_HANDLER_A, 2000, queue);
+
+  queue = new Packet(null, ID_DEVICE_B, KIND_DEVICE);
+  queue = new Packet(queue,  ID_DEVICE_B, KIND_DEVICE);
+  queue = new Packet(queue,  ID_DEVICE_B, KIND_DEVICE);
+  scheduler.addHandlerTask(ID_HANDLER_B, 3000, queue);
+
+  scheduler.addDeviceTask(ID_DEVICE_A, 4000, null);
+
+  scheduler.addDeviceTask(ID_DEVICE_B, 5000, null);
+
+  scheduler.schedule();
+
+  if (scheduler.queueCount != EXPECTED_QUEUE_COUNT ||
+      scheduler.holdCount != EXPECTED_HOLD_COUNT) {
+    var msg =
+        "Error during execution: queueCount = " + scheduler.queueCount +
+        ", holdCount = " + scheduler.holdCount + ".";
+    throw new Error(msg);
+  }
+}
+
+var COUNT = 1000;
+
+/**
+ * These two constants specify how many times a packet is queued and
+ * how many times a task is put on hold in a correct run of richards.
+ * They don't have any meaning a such but are characteristic of a
+ * correct run so if the actual queue or hold count is different from
+ * the expected there must be a bug in the implementation.
+ **/
+var EXPECTED_QUEUE_COUNT = 2322;
+var EXPECTED_HOLD_COUNT = 928;
+
+
+/**
+ * A scheduler can be used to schedule a set of tasks based on their relative
+ * priorities.  Scheduling is done by maintaining a list of task control blocks
+ * which holds tasks and the data queue they are processing.
+ * @constructor
+ */
+function Scheduler() {
+  this.queueCount = 0;
+  this.holdCount = 0;
+  this.blocks = new Array(NUMBER_OF_IDS);
+  this.list = null;
+  this.currentTcb = null;
+  this.currentId = null;
+}
+
+var ID_IDLE       = 0;
+var ID_WORKER     = 1;
+var ID_HANDLER_A  = 2;
+var ID_HANDLER_B  = 3;
+var ID_DEVICE_A   = 4;
+var ID_DEVICE_B   = 5;
+var NUMBER_OF_IDS = 6;
+
+var KIND_DEVICE   = 0;
+var KIND_WORK     = 1;
+
+/**
+ * Add an idle task to this scheduler.
+ * @param {int} id the identity of the task
+ * @param {int} priority the task's priority
+ * @param {Packet} queue the queue of work to be processed by the task
+ * @param {int} count the number of times to schedule the task
+ */
+Scheduler.prototype.addIdleTask = function (id, priority, queue, count) {
+  this.addRunningTask(id, priority, queue, new IdleTask(this, 1, count));
+};
+
+/**
+ * Add a work task to this scheduler.
+ * @param {int} id the identity of the task
+ * @param {int} priority the task's priority
+ * @param {Packet} queue the queue of work to be processed by the task
+ */
+Scheduler.prototype.addWorkerTask = function (id, priority, queue) {
+  this.addTask(id, priority, queue, new WorkerTask(this, ID_HANDLER_A, 0));
+};
+
+/**
+ * Add a handler task to this scheduler.
+ * @param {int} id the identity of the task
+ * @param {int} priority the task's priority
+ * @param {Packet} queue the queue of work to be processed by the task
+ */
+Scheduler.prototype.addHandlerTask = function (id, priority, queue) {
+  this.addTask(id, priority, queue, new HandlerTask(this));
+};
+
+/**
+ * Add a handler task to this scheduler.
+ * @param {int} id the identity of the task
+ * @param {int} priority the task's priority
+ * @param {Packet} queue the queue of work to be processed by the task
+ */
+Scheduler.prototype.addDeviceTask = function (id, priority, queue) {
+  this.addTask(id, priority, queue, new DeviceTask(this))
+};
+
+/**
+ * Add the specified task and mark it as running.
+ * @param {int} id the identity of the task
+ * @param {int} priority the task's priority
+ * @param {Packet} queue the queue of work to be processed by the task
+ * @param {Task} task the task to add
+ */
+Scheduler.prototype.addRunningTask = function (id, priority, queue, task) {
+  this.addTask(id, priority, queue, task);
+  this.currentTcb.setRunning();
+};
+
+/**
+ * Add the specified task to this scheduler.
+ * @param {int} id the identity of the task
+ * @param {int} priority the task's priority
+ * @param {Packet} queue the queue of work to be processed by the task
+ * @param {Task} task the task to add
+ */
+Scheduler.prototype.addTask = function (id, priority, queue, task) {
+  this.currentTcb = new TaskControlBlock(this.list, id, priority, queue, task);
+  this.list = this.currentTcb;
+  this.blocks[id] = this.currentTcb;
+};
+
+/**
+ * Execute the tasks managed by this scheduler.
+ */
+Scheduler.prototype.schedule = function () {
+  this.currentTcb = this.list;
+  while (this.currentTcb != null) {
+    if (this.currentTcb.isHeldOrSuspended()) {
+      this.currentTcb = this.currentTcb.link;
+    } else {
+      this.currentId = this.currentTcb.id;
+      this.currentTcb = this.currentTcb.run();
+    }
+  }
+};
+
+/**
+ * Release a task that is currently blocked and return the next block to run.
+ * @param {int} id the id of the task to suspend
+ */
+Scheduler.prototype.release = function (id) {
+  var tcb = this.blocks[id];
+  if (tcb == null) return tcb;
+  tcb.markAsNotHeld();
+  if (tcb.priority > this.currentTcb.priority) {
+    return tcb;
+  } else {
+    return this.currentTcb;
+  }
+};
+
+/**
+ * Block the currently executing task and return the next task control block
+ * to run.  The blocked task will not be made runnable until it is explicitly
+ * released, even if new work is added to it.
+ */
+Scheduler.prototype.holdCurrent = function () {
+  this.holdCount++;
+  this.currentTcb.markAsHeld();
+  return this.currentTcb.link;
+};
+
+/**
+ * Suspend the currently executing task and return the next task control block
+ * to run.  If new work is added to the suspended task it will be made runnable.
+ */
+Scheduler.prototype.suspendCurrent = function () {
+  this.currentTcb.markAsSuspended();
+  return this.currentTcb;
+};
+
+/**
+ * Add the specified packet to the end of the worklist used by the task
+ * associated with the packet and make the task runnable if it is currently
+ * suspended.
+ * @param {Packet} packet the packet to add
+ */
+Scheduler.prototype.queue = function (packet) {
+  var t = this.blocks[packet.id];
+  if (t == null) return t;
+  this.queueCount++;
+  packet.link = null;
+  packet.id = this.currentId;
+  return t.checkPriorityAdd(this.currentTcb, packet);
+};
+
+/**
+ * A task control block manages a task and the queue of work packages associated
+ * with it.
+ * @param {TaskControlBlock} link the preceding block in the linked block list
+ * @param {int} id the id of this block
+ * @param {int} priority the priority of this block
+ * @param {Packet} queue the queue of packages to be processed by the task
+ * @param {Task} task the task
+ * @constructor
+ */
+function TaskControlBlock(link, id, priority, queue, task) {
+  this.link = link;
+  this.id = id;
+  this.priority = priority;
+  this.queue = queue;
+  this.task = task;
+  if (queue == null) {
+    this.state = STATE_SUSPENDED;
+  } else {
+    this.state = STATE_SUSPENDED_RUNNABLE;
+  }
+}
+
+/**
+ * The task is running and is currently scheduled.
+ */
+var STATE_RUNNING = 0;
+
+/**
+ * The task has packets left to process.
+ */
+var STATE_RUNNABLE = 1;
+
+/**
+ * The task is not currently running.  The task is not blocked as such and may
+* be started by the scheduler.
+ */
+var STATE_SUSPENDED = 2;
+
+/**
+ * The task is blocked and cannot be run until it is explicitly released.
+ */
+var STATE_HELD = 4;
+
+var STATE_SUSPENDED_RUNNABLE = STATE_SUSPENDED | STATE_RUNNABLE;
+var STATE_NOT_HELD = ~STATE_HELD;
+
+TaskControlBlock.prototype.setRunning = function () {
+  this.state = STATE_RUNNING;
+};
+
+TaskControlBlock.prototype.markAsNotHeld = function () {
+  this.state = this.state & STATE_NOT_HELD;
+};
+
+TaskControlBlock.prototype.markAsHeld = function () {
+  this.state = this.state | STATE_HELD;
+};
+
+TaskControlBlock.prototype.isHeldOrSuspended = function () {
+  return (this.state & STATE_HELD) != 0 || (this.state == STATE_SUSPENDED);
+};
+
+TaskControlBlock.prototype.markAsSuspended = function () {
+  this.state = this.state | STATE_SUSPENDED;
+};
+
+TaskControlBlock.prototype.markAsRunnable = function () {
+  this.state = this.state | STATE_RUNNABLE;
+};
+
+/**
+ * Runs this task, if it is ready to be run, and returns the next task to run.
+ */
+TaskControlBlock.prototype.run = function () {
+  var packet;
+  if (this.state == STATE_SUSPENDED_RUNNABLE) {
+    packet = this.queue;
+    this.queue = packet.link;
+    if (this.queue == null) {
+      this.state = STATE_RUNNING;
+    } else {
+      this.state = STATE_RUNNABLE;
+    }
+  } else {
+    packet = null;
+  }
+  return this.task.run(packet);
+};
+
+/**
+ * Adds a packet to the worklist of this block's task, marks this as runnable if
+ * necessary, and returns the next runnable object to run (the one
+ * with the highest priority).
+ */
+TaskControlBlock.prototype.checkPriorityAdd = function (task, packet) {
+  if (this.queue == null) {
+    this.queue = packet;
+    this.markAsRunnable();
+    if (this.priority > task.priority) return this;
+  } else {
+    this.queue = packet.addTo(this.queue);
+  }
+  return task;
+};
+
+TaskControlBlock.prototype.toString = function () {
+  return "tcb { " + this.task + "@" + this.state + " }";
+};
+
+/**
+ * An idle task doesn't do any work itself but cycles control between the two
+ * device tasks.
+ * @param {Scheduler} scheduler the scheduler that manages this task
+ * @param {int} v1 a seed value that controls how the device tasks are scheduled
+ * @param {int} count the number of times this task should be scheduled
+ * @constructor
+ */
+function IdleTask(scheduler, v1, count) {
+  this.scheduler = scheduler;
+  this.v1 = v1;
+  this.count = count;
+}
+
+IdleTask.prototype.run = function (packet) {
+  this.count--;
+  if (this.count == 0) return this.scheduler.holdCurrent();
+  if ((this.v1 & 1) == 0) {
+    this.v1 = this.v1 >> 1;
+    return this.scheduler.release(ID_DEVICE_A);
+  } else {
+    this.v1 = (this.v1 >> 1) ^ 0xD008;
+    return this.scheduler.release(ID_DEVICE_B);
+  }
+};
+
+IdleTask.prototype.toString = function () {
+  return "IdleTask"
+};
+
+/**
+ * A task that suspends itself after each time it has been run to simulate
+ * waiting for data from an external device.
+ * @param {Scheduler} scheduler the scheduler that manages this task
+ * @constructor
+ */
+function DeviceTask(scheduler) {
+  this.scheduler = scheduler;
+  this.v1 = null;
+}
+
+DeviceTask.prototype.run = function (packet) {
+  if (packet == null) {
+    if (this.v1 == null) return this.scheduler.suspendCurrent();
+    var v = this.v1;
+    this.v1 = null;
+    return this.scheduler.queue(v);
+  } else {
+    this.v1 = packet;
+    return this.scheduler.holdCurrent();
+  }
+};
+
+DeviceTask.prototype.toString = function () {
+  return "DeviceTask";
+};
+
+/**
+ * A task that manipulates work packets.
+ * @param {Scheduler} scheduler the scheduler that manages this task
+ * @param {int} v1 a seed used to specify how work packets are manipulated
+ * @param {int} v2 another seed used to specify how work packets are manipulated
+ * @constructor
+ */
+function WorkerTask(scheduler, v1, v2) {
+  this.scheduler = scheduler;
+  this.v1 = v1;
+  this.v2 = v2;
+}
+
+WorkerTask.prototype.run = function (packet) {
+  if (packet == null) {
+    return this.scheduler.suspendCurrent();
+  } else {
+    if (this.v1 == ID_HANDLER_A) {
+      this.v1 = ID_HANDLER_B;
+    } else {
+      this.v1 = ID_HANDLER_A;
+    }
+    packet.id = this.v1;
+    packet.a1 = 0;
+    for (var i = 0; i < DATA_SIZE; i++) {
+      this.v2++;
+      if (this.v2 > 26) this.v2 = 1;
+      packet.a2[i] = this.v2;
+    }
+    return this.scheduler.queue(packet);
+  }
+};
+
+WorkerTask.prototype.toString = function () {
+  return "WorkerTask";
+};
+
+/**
+ * A task that manipulates work packets and then suspends itself.
+ * @param {Scheduler} scheduler the scheduler that manages this task
+ * @constructor
+ */
+function HandlerTask(scheduler) {
+  this.scheduler = scheduler;
+  this.v1 = null;
+  this.v2 = null;
+}
+
+HandlerTask.prototype.run = function (packet) {
+  if (packet != null) {
+    if (packet.kind == KIND_WORK) {
+      this.v1 = packet.addTo(this.v1);
+    } else {
+      this.v2 = packet.addTo(this.v2);
+    }
+  }
+  if (this.v1 != null) {
+    var count = this.v1.a1;
+    var v;
+    if (count < DATA_SIZE) {
+      if (this.v2 != null) {
+        v = this.v2;
+        this.v2 = this.v2.link;
+        v.a1 = this.v1.a2[count];
+        this.v1.a1 = count + 1;
+        return this.scheduler.queue(v);
+      }
+    } else {
+      v = this.v1;
+      this.v1 = this.v1.link;
+      return this.scheduler.queue(v);
+    }
+  }
+  return this.scheduler.suspendCurrent();
+};
+
+HandlerTask.prototype.toString = function () {
+  return "HandlerTask";
+};
+
+/* --- *
+ * P a c k e t
+ * --- */
+
+var DATA_SIZE = 4;
+
+/**
+ * A simple package of data that is manipulated by the tasks.  The exact layout
+ * of the payload data carried by a packet is not importaint, and neither is the
+ * nature of the work performed on packets by the tasks.
+ *
+ * Besides carrying data, packets form linked lists and are hence used both as
+ * data and worklists.
+ * @param {Packet} link the tail of the linked list of packets
+ * @param {int} id an ID for this packet
+ * @param {int} kind the type of this packet
+ * @constructor
+ */
+function Packet(link, id, kind) {
+  this.link = link;
+  this.id = id;
+  this.kind = kind;
+  this.a1 = 0;
+  this.a2 = new Array(DATA_SIZE);
+}
+
+/**
+ * Add this packet to the end of a worklist, and return the worklist.
+ * @param {Packet} queue the worklist to add this packet to
+ */
+Packet.prototype.addTo = function (queue) {
+  this.link = null;
+  if (queue == null) return this;
+  var peek, next = queue;
+  while ((peek = next.link) != null)
+    next = peek;
+  next.link = this;
+  return queue;
+};
+
+Packet.prototype.toString = function () {
+  return "Packet";
+};
+
+Benchmark.report("Richards", runRichards);
diff --git a/build/config/linux/pkg-config.py b/build/config/linux/pkg-config.py
index 70cc608..a7334f7 100644
--- a/build/config/linux/pkg-config.py
+++ b/build/config/linux/pkg-config.py
@@ -34,7 +34,7 @@
 # success. This allows us to "kind of emulate" a Linux build from other
 # platforms.
 if sys.platform.find("linux") == -1:
-    print "[[],[],[],[],[]]"
+    print("[[],[],[],[],[]]")
     sys.exit(0)
 
 
@@ -50,7 +50,7 @@
     # Compute the library path name based on the architecture.
     arch = options.arch
     if sysroot and not arch:
-        print "You must specify an architecture via -a if using a sysroot."
+        print("You must specify an architecture via -a if using a sysroot.")
         sys.exit(1)
     if arch == 'x64':
         libpath = 'lib64'
@@ -138,9 +138,9 @@
         [options.pkg_config, "--atleast-version=" + options.atleast_version] +
             args,
             env=os.environ):
-        print "true"
+        print("true")
     else:
-        print "false"
+        print("false")
     sys.exit(0)
 
 if options.libdir:
@@ -148,7 +148,7 @@
         libdir = subprocess.check_output(
             [options.pkg_config, "--variable=libdir"] + args, env=os.environ)
     except:
-        print "Error from pkg-config."
+        print("Error from pkg-config.")
         sys.exit(1)
     sys.stdout.write(libdir.strip())
     sys.exit(0)
@@ -163,7 +163,7 @@
     # to happen in practice.
     all_flags = flag_string.strip().split(' ')
 except:
-    print "Could not run pkg-config."
+    print("Could not run pkg-config.")
     sys.exit(1)
 
 sysroot = options.sysroot
@@ -199,4 +199,4 @@
 # Output a GN array, the first one is the cflags, the second are the libs. The
 # JSON formatter prints GN compatible lists when everything is a list of
 # strings.
-print json.dumps([includes, cflags, libs, lib_dirs, ldflags])
+print(json.dumps([includes, cflags, libs, lib_dirs, ldflags]))
diff --git a/build/config/linux/sysroot_ld_path.py b/build/config/linux/sysroot_ld_path.py
index 53b094d..a62381f 100644
--- a/build/config/linux/sysroot_ld_path.py
+++ b/build/config/linux/sysroot_ld_path.py
@@ -12,9 +12,10 @@
 import sys
 
 if len(sys.argv) != 3:
-    print "Need two arguments"
+    print("Need two arguments")
     sys.exit(1)
 
-result = subprocess.check_output([sys.argv[1], sys.argv[2]]).strip()
+result = subprocess.check_output([sys.argv[1], sys.argv[2]],
+                                 universal_newlines=True).strip()
 
-print '"' + result + '"'
+print('"' + result + '"')
diff --git a/build/config/mac/mac_app.py b/build/config/mac/mac_app.py
index 8245967..3eec8c6 100644
--- a/build/config/mac/mac_app.py
+++ b/build/config/mac/mac_app.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 # Copyright (c) 2015 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/build/detect_host_arch.py b/build/detect_host_arch.py
index 1b179cb..5d939b7 100755
--- a/build/detect_host_arch.py
+++ b/build/detect_host_arch.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright 2014 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
@@ -48,4 +48,4 @@
 
 
 if __name__ == '__main__':
-    print DoMain([])
+    print(DoMain([]))
diff --git a/build/gn_run_binary.py b/build/gn_run_binary.py
index 0c2287d..9b3c32d 100755
--- a/build/gn_run_binary.py
+++ b/build/gn_run_binary.py
@@ -1,11 +1,11 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright 2014 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 """Helper script for GN to run an arbitrary binary. See compiled_action.gni.
 
 Run with:
-  python gn_run_binary.py <invoker> <binary_name> [args ...]
+  python3 gn_run_binary.py <invoker> <binary_name> [args ...]
 
 Where <invoker> is either "compiled_action" or "exec_script". If it is
 "compiled_action" the script has a non-zero exit code on a failure. If it is
@@ -25,10 +25,14 @@
         return 0
     except subprocess.CalledProcessError as e:
         return ("Command failed: " + ' '.join(command) + "\n" + "output: " +
-                e.output)
+                _decode(e.output))
     except OSError as e:
         return ("Command failed: " + ' '.join(command) + "\n" + "output: " +
-                e.strerror)
+                _decode(e.strerror))
+
+
+def _decode(bytes):
+    return bytes.decode("utf-8")
 
 
 def main(argv):
diff --git a/build/linux/sysroot_scripts/install-sysroot.py b/build/linux/sysroot_scripts/install-sysroot.py
index cbe8fff..5e2fffe 100755
--- a/build/linux/sysroot_scripts/install-sysroot.py
+++ b/build/linux/sysroot_scripts/install-sysroot.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2013 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
@@ -17,14 +17,12 @@
 
 import hashlib
 import json
-import platform
 import optparse
 import os
-import re
 import shutil
 import subprocess
 import sys
-import urllib2
+from urllib.request import urlopen
 
 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
 sys.path.append(os.path.dirname(os.path.dirname(SCRIPT_DIR)))
@@ -87,7 +85,7 @@
         return 0
 
     if not options.arch:
-        print 'You much specify either --arch or --running-as-hook'
+        print('You much specify either --arch or --running-as-hook')
         return 1
     InstallDefaultSysrootForArch(options.arch)
 
@@ -134,18 +132,18 @@
             if s.read() == url:
                 return
 
-    print 'Installing Debian %s %s root image: %s' % \
-        (target_platform, target_arch, sysroot)
+    print('Installing Debian %s %s root image: %s' % \
+        (target_platform, target_arch, sysroot))
     if os.path.isdir(sysroot):
         shutil.rmtree(sysroot)
     os.mkdir(sysroot)
     tarball = os.path.join(sysroot, tarball_filename)
-    print 'Downloading %s' % url
+    print('Downloading %s' % url)
     sys.stdout.flush()
     sys.stderr.flush()
     for _ in range(3):
         try:
-            response = urllib2.urlopen(url)
+            response = urlopen(url)
             with open(tarball, "wb") as f:
                 f.write(response.read())
             break
diff --git a/build/mac/change_mach_o_flags.py b/build/mac/change_mach_o_flags.py
index 427117c..4155dc6 100755
--- a/build/mac/change_mach_o_flags.py
+++ b/build/mac/change_mach_o_flags.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2011 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/build/mac/find_sdk.py b/build/mac/find_sdk.py
index 01d0673..d2bf424 100755
--- a/build/mac/find_sdk.py
+++ b/build/mac/find_sdk.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
@@ -6,7 +6,7 @@
 given minimum sdk version to standard output.
 
 Usage:
-  python find_sdk.py 10.6  # Ignores SDKs < 10.6
+  python3 find_sdk.py 10.6  # Ignores SDKs < 10.6
 """
 
 import os
@@ -54,7 +54,7 @@
 
 def parse_version(version_str):
     """'10.6' => [10, 6]"""
-    return map(int, re.findall(r'(\d+)', version_str))
+    return list(map(int, re.findall(r'(\d+)', version_str)))
 
 
 def main():
@@ -90,11 +90,12 @@
 
     job = subprocess.Popen(['xcode-select', '-print-path'],
                            stdout=subprocess.PIPE,
-                           stderr=subprocess.STDOUT)
+                           stderr=subprocess.STDOUT,
+                           universal_newlines=True)
     out, err = job.communicate()
     if job.returncode != 0:
-        print >> sys.stderr, out
-        print >> sys.stderr, err
+        print(out, file=sys.stderr)
+        print(err, file=sys.stderr)
         raise Exception((
             'Error %d running xcode-select, you might have to run '
             '|sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer| '
@@ -119,27 +120,26 @@
     best_sdk = sorted(sdks, key=parse_version)[0]
 
     if options.verify and best_sdk != min_sdk_version and not options.sdk_path:
-        print >> sys.stderr, ''
-        print >> sys.stderr, '                                           vvvvvvv'
-        print >> sys.stderr, ''
-        print >> sys.stderr, \
-            'This build requires the %s SDK, but it was not found on your system.' \
-            % min_sdk_version
-        print >> sys.stderr, \
-            'Either install it, or explicitly set mac_sdk in your GYP_DEFINES.'
-        print >> sys.stderr, ''
-        print >> sys.stderr, '                                           ^^^^^^^'
-        print >> sys.stderr, ''
+        print('''
+                                           vvvvvvv
+
+This build requires the %s SDK, but it was not found on your system.
+
+Either install it, or explicitly set mac_sdk in your GYP_DEFINES.
+
+                                           ^^^^^^^'
+''' % min_sdk_version,
+              file=sys.stderr)
         return min_sdk_version
 
     if options.print_sdk_path:
         sdk_path = subprocess.check_output(
-            ['xcodebuild', '-version', '-sdk', 'macosx' + best_sdk,
-             'Path']).strip()
+            ['xcodebuild', '-version', '-sdk', 'macosx' + best_sdk, 'Path'],
+            universal_newlines=True).strip()
         if options.create_symlink_at:
-            print CreateSymlinkForSDKAt(sdk_path, options.create_symlink_at)
+            print(CreateSymlinkForSDKAt(sdk_path, options.create_symlink_at))
         else:
-            print sdk_path
+            print(sdk_path)
 
     return best_sdk
 
@@ -147,4 +147,4 @@
 if __name__ == '__main__':
     if sys.platform != 'darwin':
         raise Exception("This script only runs on Mac")
-    print main()
+    print(main())
diff --git a/build/mac/strip_save_dsym b/build/mac/strip_save_dsym
index c9cf226..0b0bc7b 100755
--- a/build/mac/strip_save_dsym
+++ b/build/mac/strip_save_dsym
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 # Copyright (c) 2011 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
diff --git a/build/mac/tweak_info_plist.py b/build/mac/tweak_info_plist.py
index b2a0f0c..988a1b3 100755
--- a/build/mac/tweak_info_plist.py
+++ b/build/mac/tweak_info_plist.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
@@ -68,7 +68,8 @@
     if version:
         match = re.match('\d+\.\d+\.(\d+\.\d+)$', version)
         if not match:
-            print >> sys.stderr, 'Invalid version string specified: "%s"' % version
+            print('Invalid version string specified: "%s"' % version,
+                  file=sys.stderr)
             return False
 
         full_version = match.group(0)
@@ -129,7 +130,8 @@
     if scm_revision != None:
         plist['SCMRevision'] = scm_revision
     elif add_keys:
-        print >> sys.stderr, 'Could not determine SCM revision.  This may be OK.'
+        print('Could not determine SCM revision.  This may be OK.',
+              file=sys.stderr)
 
     return True
 
@@ -162,9 +164,9 @@
     components_len = len(components)
     combinations = 1 << components_len
     tag_suffixes = []
-    for combination in xrange(0, combinations):
+    for combination in range(0, combinations):
         tag_suffix = ''
-        for component_index in xrange(0, components_len):
+        for component_index in range(0, components_len):
             if combination & (1 << component_index):
                 tag_suffix += '-' + components[component_index]
         tag_suffixes.append(tag_suffix)
@@ -248,7 +250,7 @@
     (options, args) = parser.parse_args(argv)
 
     if len(args) > 0:
-        print >> sys.stderr, parser.get_usage()
+        print(parser.get_usage(), file=sys.stderr)
         return 1
 
     # Read the plist into its parsed format.
@@ -263,7 +265,7 @@
     # Add Breakpad if configured to do so.
     if options.use_breakpad:
         if options.branding is None:
-            print >> sys.stderr, 'Use of Breakpad requires branding.'
+            print('Use of Breakpad requires branding.', file=sys.stderr)
             return 1
         _AddBreakpadKeys(plist, options.branding)
         if options.breakpad_uploads:
@@ -282,7 +284,7 @@
     # Only add Keystone in Release builds.
     if options.use_keystone and env['CONFIGURATION'] == 'Release':
         if options.bundle_identifier is None:
-            print >> sys.stderr, 'Use of Keystone requires the bundle id.'
+            print('Use of Keystone requires the bundle id.', file=sys.stderr)
             return 1
         _AddKeystoneKeys(plist, options.bundle_identifier)
     else:
diff --git a/build/rust/run.py b/build/rust/run.py
index 6bdbea1..3041cf9 100755
--- a/build/rust/run.py
+++ b/build/rust/run.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 # Copyright (c) 2019, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
@@ -15,9 +15,9 @@
     bindir = os.path.dirname(cmd[0]);
     env = os.environ
     if 'PATH' in env:
-      env['PATH'] += os.pathsep + bindir
+        env['PATH'] += os.pathsep + bindir
     else:
-      env['PATH'] = bindir
+        env['PATH'] = bindir
     out = ''
     err = ''
     proc = subprocess.Popen(
@@ -29,7 +29,7 @@
         err += stderr
         proc.poll()
     if proc.returncode == 0:
-      return 0
+        return 0
     print(out)
     print(err)
     return proc.returncode
diff --git a/build/toolchain/get_concurrent_links.py b/build/toolchain/get_concurrent_links.py
index 45e3ff5..a322526 100644
--- a/build/toolchain/get_concurrent_links.py
+++ b/build/toolchain/get_concurrent_links.py
@@ -36,7 +36,7 @@
         stat = MEMORYSTATUSEX(dwLength=ctypes.sizeof(MEMORYSTATUSEX))
         ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(stat))
 
-        mem_limit = max(1, stat.ullTotalPhys / (4 * (2**30)))  # total / 4GB
+        mem_limit = max(1, stat.ullTotalPhys // (4 * (2**30)))  # total / 4GB
         hard_cap = max(1, int(os.getenv('GYP_LINK_CONCURRENCY_MAX', 2**32)))
         return min(mem_limit, hard_cap)
     elif sys.platform.startswith('linux'):
@@ -48,7 +48,7 @@
                     if not match:
                         continue
                     # Allow 8Gb per link on Linux because Gold is quite memory hungry
-                    return max(1, int(match.group(1)) / (8 * (2**20)))
+                    return max(1, int(match.group(1)) // (8 * (2**20)))
         return 1
     elif sys.platform == 'darwin':
         try:
@@ -56,7 +56,7 @@
                 subprocess.check_output(['sysctl', '-n', 'hw.memsize']))
             # A static library debug build of Chromium's unit_tests takes ~2.7GB, so
             # 4GB per ld process allows for some more bloat.
-            return max(1, avail_bytes / (4 * (2**30)))  # total / 4GB
+            return max(1, avail_bytes // (4 * (2**30)))  # total / 4GB
         except Exception:
             return 1
     else:
@@ -64,4 +64,4 @@
         return 1
 
 
-print GetDefaultConcurrentLinks()
+print(GetDefaultConcurrentLinks())
diff --git a/build/toolchain/win/BUILD.gn b/build/toolchain/win/BUILD.gn
index 16518a6..478e39c 100644
--- a/build/toolchain/win/BUILD.gn
+++ b/build/toolchain/win/BUILD.gn
@@ -26,7 +26,9 @@
                                visual_studio_path,
                                windows_sdk_path,
                                visual_studio_runtime_dirs,
+                               "win",
                                current_cpu,
+                               "environment." + current_cpu,
                              ],
                              "scope")
 
diff --git a/build/toolchain/win/setup_toolchain.py b/build/toolchain/win/setup_toolchain.py
index fde043b..ee3d8ee 100644
--- a/build/toolchain/win/setup_toolchain.py
+++ b/build/toolchain/win/setup_toolchain.py
@@ -10,7 +10,8 @@
 # win tool. The script assumes that the root build directory is the current dir
 # and the files will be written to the current directory.
 
-import errno
+from __future__ import print_function
+
 import json
 import os
 import re
@@ -22,20 +23,25 @@
 
 SCRIPT_DIR = os.path.dirname(__file__)
 
-
 def _ExtractImportantEnvironment(output_of_set):
     """Extracts environment variables required for the toolchain to run from
   a textual dump output by the cmd.exe 'set' command."""
     envvars_to_save = (
+        'cipd_cache_dir',  # needed by vpython
+        'homedrive',  # needed by vpython
+        'homepath',  # needed by vpython
         'goma_.*',  # TODO(scottmg): This is ugly, but needed for goma.
         'include',
         'lib',
         'libpath',
+        'luci_context',  # needed by vpython
         'path',
         'pathext',
         'systemroot',
         'temp',
         'tmp',
+        'userprofile',  # needed by vpython
+        'vpython_virtualenv_root'  # needed by vpython
     )
     env = {}
     # This occasionally happens and leads to misleading SYSTEMROOT error messages
@@ -52,7 +58,7 @@
                     # path when ninja is run later, python will still be found.
                     setting = os.path.dirname(
                         sys.executable) + os.pathsep + setting
-                env[var.upper()] = setting.lower()
+                env[var.upper()] = setting
                 break
     if sys.platform in ('win32', 'cygwin'):
         for required in ('SYSTEMROOT', 'TEMP', 'TMP'):
@@ -63,7 +69,7 @@
 
 
 def _DetectVisualStudioPath():
-    """Return path to the GYP_MSVS_VERSION of Visual Studio.
+    """Return path to the installed Visual Studio.
   """
 
     # Use the code in build/vs_toolchain.py to avoid duplicating code.
@@ -77,31 +83,36 @@
     """Given a bat command, runs it and returns env vars set by it."""
     args = args[:]
     args.extend(('&&', 'set'))
-    popen = subprocess.Popen(
-        args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+    popen = subprocess.Popen(args,
+                             shell=True,
+                             stdout=subprocess.PIPE,
+                             stderr=subprocess.STDOUT)
     variables, _ = popen.communicate()
     if popen.returncode != 0:
         raise Exception('"%s" failed with error %d' % (args, popen.returncode))
-    return variables
+    return variables.decode(errors='ignore')
 
 
-def _LoadToolchainEnv(cpu, sdk_dir):
+def _LoadToolchainEnv(cpu, toolchain_root, sdk_dir, target_store):
     """Returns a dictionary with environment variables that must be set while
   running binaries from the toolchain (e.g. INCLUDE and PATH for cl.exe)."""
     # Check if we are running in the SDK command line environment and use
     # the setup script from the SDK if so. |cpu| should be either
-    # 'x86' or 'x64'.
-    assert cpu in ('x86', 'x64')
+    # 'x86' or 'x64' or 'arm' or 'arm64'.
+    assert cpu in ('x86', 'x64', 'arm', 'arm64')
     if bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', 1))) and sdk_dir:
         # Load environment from json file.
-        env = os.path.normpath(
-            os.path.join(sdk_dir, 'bin/SetEnv.%s.json' % cpu))
+        env = os.path.normpath(os.path.join(sdk_dir,
+                                            'bin/SetEnv.%s.json' % cpu))
         env = json.load(open(env))['env']
+        if env['VSINSTALLDIR'] == [["..", "..\\"]]:
+            # Old-style paths were relative to the win_sdk\bin directory.
+            json_relative_dir = os.path.join(sdk_dir, 'bin')
+        else:
+            # New-style paths are relative to the toolchain directory.
+            json_relative_dir = toolchain_root
         for k in env:
-            entries = [
-                os.path.join(*([os.path.join(sdk_dir, 'bin')] + e))
-                for e in env[k]
-            ]
+            entries = [os.path.join(*([json_relative_dir] + e)) for e in env[k]]
             # clang-cl wants INCLUDE to be ;-separated even on non-Windows,
             # lld-link wants LIB to be ;-separated even on non-Windows.  Path gets :.
             # The separator for INCLUDE here must match the one used in main() below.
@@ -122,8 +133,11 @@
         # Check that the json file contained the same environment as the .cmd file.
         if sys.platform in ('win32', 'cygwin'):
             script = os.path.normpath(os.path.join(sdk_dir, 'Bin/SetEnv.cmd'))
-            assert _ExtractImportantEnvironment(variables) == \
-                   _ExtractImportantEnvironment(_LoadEnvFromBat([script, '/' + cpu]))
+            arg = '/' + cpu
+            json_env = _ExtractImportantEnvironment(variables)
+            cmd_env = _ExtractImportantEnvironment(
+                _LoadEnvFromBat([script, arg]))
+            assert _LowercaseDict(json_env) == _LowercaseDict(cmd_env)
     else:
         if 'GYP_MSVS_OVERRIDE_PATH' not in os.environ:
             os.environ['GYP_MSVS_OVERRIDE_PATH'] = _DetectVisualStudioPath()
@@ -134,9 +148,16 @@
         if not os.path.exists(script_path):
             # vcvarsall.bat for VS 2017 fails if run after running vcvarsall.bat from
             # VS 2013 or VS 2015. Fix this by clearing the vsinstalldir environment
-            # variable.
+            # variable. Since vcvarsall.bat appends to the INCLUDE, LIB, and LIBPATH
+            # environment variables we need to clear those to avoid getting double
+            # entries when vcvarsall.bat has been run before gn gen. vcvarsall.bat
+            # also adds to PATH, but there is no clean way of clearing that and it
+            # doesn't seem to cause problems.
             if 'VSINSTALLDIR' in os.environ:
                 del os.environ['VSINSTALLDIR']
+                del os.environ['INCLUDE']
+                del os.environ['LIB']
+                del os.environ['LIBPATH']
             other_path = os.path.normpath(
                 os.path.join(os.environ['GYP_MSVS_OVERRIDE_PATH'],
                              'VC/Auxiliary/Build/vcvarsall.bat'))
@@ -145,9 +166,21 @@
                     '%s is missing - make sure VC++ tools are installed.' %
                     script_path)
             script_path = other_path
-        # Chromium requires the 10.0.14393.0 SDK or higher - previous versions don't
-        # have all of the required declarations.
-        args = [script_path, 'amd64_x86' if cpu == 'x86' else 'amd64']
+        cpu_arg = "amd64"
+        if (cpu != 'x64'):
+            # x64 is default target CPU thus any other CPU requires a target set
+            cpu_arg += '_' + cpu
+        args = [
+            script_path,
+            cpu_arg,
+        ]
+        # Store target must come before any SDK version declaration
+        if (target_store):
+            args.append('store')
+        # Explicitly specifying the SDK version to build with to avoid accidentally
+        # building with a new and untested SDK. This should stay in sync with the
+        # packaged toolchain in build/vs_toolchain.py.
+        args.append('10.0.19041.0')
         variables = _LoadEnvFromBat(args)
     return _ExtractImportantEnvironment(variables)
 
@@ -158,68 +191,137 @@
   CreateProcess documentation for more details."""
     block = ''
     nul = '\0'
-    for key, value in envvar_dict.iteritems():
+    for key, value in envvar_dict.items():
         block += key + '=' + value + nul
     block += nul
     return block
 
 
+def _LowercaseDict(d):
+    """Returns a copy of `d` with both key and values lowercased.
+
+  Args:
+    d: dict to lowercase (e.g. {'A': 'BcD'}).
+
+  Returns:
+    A dict with both keys and values lowercased (e.g.: {'a': 'bcd'}).
+  """
+    return {k.lower(): d[k].lower() for k in d}
+
+
+def FindFileInEnvList(env, env_name, separator, file_name, optional=False):
+    parts = env[env_name].split(separator)
+    for path in parts:
+        if os.path.exists(os.path.join(path, file_name)):
+            return os.path.realpath(path)
+    assert optional, "%s is not found in %s:\n%s\nCheck if it is installed." % (
+        file_name, env_name, '\n'.join(parts))
+    return ''
+
+
 def main():
-    if len(sys.argv) != 5:
+    if len(sys.argv) != 7:
         print('Usage setup_toolchain.py '
               '<visual studio path> <win sdk path> '
-              '<runtime dirs> <target_cpu> <include prefix>')
+              '<runtime dirs> <target_os> <target_cpu> '
+              '<environment block name|none>')
         sys.exit(2)
+    # toolchain_root and win_sdk_path are only read if the hermetic Windows
+    # toolchain is set, that is if DEPOT_TOOLS_WIN_TOOLCHAIN is not set to 0.
+    # With the hermetic Windows toolchain, the visual studio path in argv[1]
+    # is the root of the Windows toolchain directory.
+    toolchain_root = sys.argv[1]
     win_sdk_path = sys.argv[2]
-    runtime_dirs = sys.argv[3]
-    target_cpu = sys.argv[4]
 
-    cpus = ('x86', 'x64')
+    runtime_dirs = sys.argv[3]
+    target_os = sys.argv[4]
+    target_cpu = sys.argv[5]
+    environment_block_name = sys.argv[6]
+    if (environment_block_name == 'none'):
+        environment_block_name = ''
+
+    if (target_os == 'winuwp'):
+        target_store = True
+    else:
+        target_store = False
+
+    cpus = ('x86', 'x64', 'arm', 'arm64')
     assert target_cpu in cpus
     vc_bin_dir = ''
+    vc_lib_path = ''
+    vc_lib_atlmfc_path = ''
+    vc_lib_um_path = ''
     include = ''
+    lib = ''
 
     # TODO(scottmg|goma): Do we need an equivalent of
     # ninja_use_custom_environment_files?
 
-    for cpu in cpus:
-        # Extract environment variables for subprocesses.
-        env = _LoadToolchainEnv(cpu, win_sdk_path)
-        env['PATH'] = runtime_dirs + os.pathsep + env['PATH']
+    def relflag(
+            s):  # Make s relative to builddir when cwd and sdk on same drive.
+        try:
+            return os.path.relpath(s)
+        except ValueError:
+            return s
 
+    def q(s):  # Quote s if it contains spaces or other weird characters.
+        return s if re.match(r'^[a-zA-Z0-9._/\\:-]*$', s) else '"' + s + '"'
+
+    for cpu in cpus:
         if cpu == target_cpu:
-            for path in env['PATH'].split(os.pathsep):
-                if os.path.exists(os.path.join(path, 'cl.exe')):
-                    vc_bin_dir = os.path.realpath(path)
-                    break
+            # Extract environment variables for subprocesses.
+            env = _LoadToolchainEnv(cpu, toolchain_root, win_sdk_path,
+                                    target_store)
+            env['PATH'] = runtime_dirs + os.pathsep + env['PATH']
+
+            vc_bin_dir = FindFileInEnvList(env, 'PATH', os.pathsep, 'cl.exe')
+            vc_lib_path = FindFileInEnvList(env, 'LIB', ';', 'msvcrt.lib')
+            vc_lib_atlmfc_path = FindFileInEnvList(env,
+                                                   'LIB',
+                                                   ';',
+                                                   'atls.lib',
+                                                   optional=True)
+            vc_lib_um_path = FindFileInEnvList(env, 'LIB', ';', 'user32.lib')
+
             # The separator for INCLUDE here must match the one used in
             # _LoadToolchainEnv() above.
             include = [
                 p.replace('"', r'\"') for p in env['INCLUDE'].split(';') if p
             ]
-            include_I = ' '.join(['"/I' + i + '"' for i in include])
-            include_imsvc = ' '.join(['"-imsvc' + i + '"' for i in include])
+            include = list(map(relflag, include))
 
-        env_block = _FormatAsEnvironmentBlock(env)
-        with open('environment.' + cpu, 'wb') as f:
-            f.write(env_block)
+            lib = [p.replace('"', r'\"') for p in env['LIB'].split(';') if p]
+            lib = list(map(relflag, lib))
 
-        # Create a store app version of the environment.
-        if 'LIB' in env:
-            env['LIB'] = env['LIB'].replace(r'\VC\LIB', r'\VC\LIB\STORE')
-        if 'LIBPATH' in env:
-            env['LIBPATH'] = env['LIBPATH'].replace(r'\VC\LIB',
-                                                    r'\VC\LIB\STORE')
-        env_block = _FormatAsEnvironmentBlock(env)
-        with open('environment.winrt_' + cpu, 'wb') as f:
-            f.write(env_block)
+            include_I = ' '.join([q('/I' + i) for i in include])
+            include_imsvc = ' '.join([q('-imsvc' + i) for i in include])
+            libpath_flags = ' '.join([q('-libpath:' + i) for i in lib])
 
-    assert vc_bin_dir
-    print 'vc_bin_dir = ' + gn_helpers.ToGNString(vc_bin_dir)
+            if (environment_block_name != ''):
+                env_block = _FormatAsEnvironmentBlock(env)
+                with open(environment_block_name, 'w') as f:
+                    f.write(env_block)
+
+    print('vc_bin_dir = ' + gn_helpers.ToGNString(vc_bin_dir))
     assert include_I
-    print 'include_flags_I = ' + gn_helpers.ToGNString(include_I)
+    print('include_flags_I = ' + gn_helpers.ToGNString(include_I))
     assert include_imsvc
-    print 'include_flags_imsvc = ' + gn_helpers.ToGNString(include_imsvc)
+    if bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN',
+                               1))) and win_sdk_path:
+        print('include_flags_imsvc = ' +
+              gn_helpers.ToGNString(q('/winsysroot' + relflag(toolchain_root))))
+    else:
+        print('include_flags_imsvc = ' + gn_helpers.ToGNString(include_imsvc))
+    print('vc_lib_path = ' + gn_helpers.ToGNString(vc_lib_path))
+    # Possible atlmfc library path gets introduced in the future for store thus
+    # output result if a result exists.
+    if (vc_lib_atlmfc_path != ''):
+        print('vc_lib_atlmfc_path = ' +
+              gn_helpers.ToGNString(vc_lib_atlmfc_path))
+    print('vc_lib_um_path = ' + gn_helpers.ToGNString(vc_lib_um_path))
+    print('paths = ' + gn_helpers.ToGNString(env['PATH']))
+    assert libpath_flags
+    print('libpath_flags = ' + gn_helpers.ToGNString(libpath_flags))
 
 
 if __name__ == '__main__':
diff --git a/build/toolchain/win/tool_wrapper.py b/build/toolchain/win/tool_wrapper.py
index fbe9248..b094b2bf 100644
--- a/build/toolchain/win/tool_wrapper.py
+++ b/build/toolchain/win/tool_wrapper.py
@@ -12,7 +12,6 @@
 import shutil
 import subprocess
 import stat
-import string
 import sys
 
 BASE_DIR = os.path.dirname(os.path.abspath(__file__))
@@ -137,19 +136,19 @@
         #   Popen(['/bin/sh', '-c', args[0], args[1], ...])"
         # For that reason, since going through the shell doesn't seem necessary on
         # non-Windows don't do that there.
-        link = subprocess.Popen(
-            args,
-            shell=sys.platform == 'win32',
-            env=env,
-            stdout=subprocess.PIPE,
-            stderr=subprocess.STDOUT)
+        link = subprocess.Popen(args,
+                                shell=sys.platform == 'win32',
+                                env=env,
+                                stdout=subprocess.PIPE,
+                                stderr=subprocess.STDOUT,
+                                universal_newlines=True)
         # Read output one line at a time as it shows up to avoid OOM failures when
         # GBs of output is produced.
         for line in link.stdout:
             if (not line.startswith('   Creating library ') and
                     not line.startswith('Generating code') and
                     not line.startswith('Finished generating code')):
-                print line,
+                print(line)
         return link.wait()
 
     def ExecMidlWrapper(self, arch, outdir, tlb, h, dlldata, iid, proxy, idl,
@@ -162,12 +161,12 @@
             iid, '/proxy', proxy, idl
         ]
         env = self._GetEnv(arch)
-        popen = subprocess.Popen(
-            args,
-            shell=True,
-            env=env,
-            stdout=subprocess.PIPE,
-            stderr=subprocess.STDOUT)
+        popen = subprocess.Popen(args,
+                                 shell=True,
+                                 env=env,
+                                 stdout=subprocess.PIPE,
+                                 stderr=subprocess.STDOUT,
+                                 universal_newlines=True)
         out, _ = popen.communicate()
         # Filter junk out of stdout, and write filtered versions. Output we want
         # to filter is pairs of lines that look like this:
@@ -179,18 +178,18 @@
             os.path.basename(x) for x in lines if x.startswith(prefixes))
         for line in lines:
             if not line.startswith(prefixes) and line not in processing:
-                print line
+                print(line)
         return popen.returncode
 
     def ExecAsmWrapper(self, arch, *args):
         """Filter logo banner from invocations of asm.exe."""
         env = self._GetEnv(arch)
-        popen = subprocess.Popen(
-            args,
-            shell=True,
-            env=env,
-            stdout=subprocess.PIPE,
-            stderr=subprocess.STDOUT)
+        popen = subprocess.Popen(args,
+                                 shell=True,
+                                 env=env,
+                                 stdout=subprocess.PIPE,
+                                 stderr=subprocess.STDOUT,
+                                 universal_newlines=True)
         out, _ = popen.communicate()
         for line in out.splitlines():
             # Split to avoid triggering license checks:
@@ -198,26 +197,26 @@
                                     ') Microsoft Corporation') and
                     not line.startswith('Microsoft (R) Macro Assembler') and
                     not line.startswith(' Assembling: ') and line):
-                print line
+                print(line)
         return popen.returncode
 
     def ExecRcWrapper(self, arch, *args):
         """Filter logo banner from invocations of rc.exe. Older versions of RC
     don't support the /nologo flag."""
         env = self._GetEnv(arch)
-        popen = subprocess.Popen(
-            args,
-            shell=True,
-            env=env,
-            stdout=subprocess.PIPE,
-            stderr=subprocess.STDOUT)
+        popen = subprocess.Popen(args,
+                                 shell=True,
+                                 env=env,
+                                 stdout=subprocess.PIPE,
+                                 stderr=subprocess.STDOUT,
+                                 universal_newlines=True)
         out, _ = popen.communicate()
         for line in out.splitlines():
             if (not line.startswith(
                     'Microsoft (R) Windows (R) Resource Compiler') and
                     not line.startswith('Copy' + 'right (C' +
                                         ') Microsoft Corporation') and line):
-                print line
+                print(line)
         return popen.returncode
 
     def ExecActionWrapper(self, arch, rspfile, *dirname):
diff --git a/build/vs_toolchain.py b/build/vs_toolchain.py
index 11376198..6c9f2d3 100644
--- a/build/vs_toolchain.py
+++ b/build/vs_toolchain.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright 2014 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
@@ -27,18 +27,44 @@
 
 from gn_helpers import ToGNString
 
+# VS 2019 16.61 with 10.0.19041 SDK, and 10.0.17134 version of
+# d3dcompiler_47.dll, with ARM64 libraries and UWP support.
+# See go/chromium-msvc-toolchain for instructions about how to update the
+# toolchain.
+#
+# When updating the toolchain, consider the following areas impacted by the
+# toolchain version:
+#
+# * //base/win/windows_version.cc NTDDI preprocessor check
+#   Triggers a compiler error if the available SDK is older than the minimum.
+# * //build/config/win/BUILD.gn NTDDI_VERSION value
+#   Affects the availability of APIs in the toolchain headers.
+# * //docs/windows_build_instructions.md mentions of VS or Windows SDK.
+#   Keeps the document consistent with the toolchain version.
+TOOLCHAIN_HASH = '20d5f2553f'
+
 script_dir = os.path.dirname(os.path.realpath(__file__))
-chrome_src = os.path.abspath(os.path.join(script_dir, os.pardir))
-SRC_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
-sys.path.insert(0, os.path.join(chrome_src, 'tools'))
+dart_src = os.path.abspath(os.path.join(script_dir, os.pardir))
+sys.path.insert(0, os.path.join(dart_src, 'tools'))
 json_data_file = os.path.join(script_dir, 'win_toolchain.json')
 
 # VS versions are listed in descending order of priority (highest first).
 MSVS_VERSIONS = collections.OrderedDict([
-    ('2017', '15.0'),
     ('2019', '16.0'),
+    ('2017', '15.0'),
 ])
 
+# List of preferred VC toolset version based on MSVS
+MSVC_TOOLSET_VERSION = {
+    '2019': 'VC142',
+    '2017': 'VC141',
+}
+
+
+def _HostIsWindows():
+    """Returns True if running on a Windows host (including under cygwin)."""
+    return sys.platform in ('win32', 'cygwin')
+
 
 def SetEnvironmentAndGetRuntimeDllDirs():
     """Sets up os.environ to use the depot_tools VS toolchain with gyp, and
@@ -54,24 +80,22 @@
         bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1')))
     # When running on a non-Windows host, only do this if the SDK has explicitly
     # been downloaded before (in which case json_data_file will exist).
-    if ((sys.platform in ('win32', 'cygwin') or os.path.exists(json_data_file))
-            and depot_tools_win_toolchain):
+    if ((_HostIsWindows() or os.path.exists(json_data_file)) and
+            depot_tools_win_toolchain):
         if ShouldUpdateToolchain():
             if len(sys.argv) > 1 and sys.argv[1] == 'update':
                 update_result = Update()
             else:
                 update_result = Update(no_download=True)
             if update_result != 0:
-                raise Exception(
-                    'Failed to update, error code %d.' % update_result)
+                raise Exception('Failed to update, error code %d.' %
+                                update_result)
         with open(json_data_file, 'r') as tempf:
             toolchain_data = json.load(tempf)
 
         toolchain = toolchain_data['path']
         version = toolchain_data['version']
         win_sdk = toolchain_data.get('win_sdk')
-        if not win_sdk:
-            win_sdk = toolchain_data['win8sdk']
         wdk = toolchain_data['wdk']
         # TODO(scottmg): The order unfortunately matters in these. They should be
         # split into separate keys for x64/x86/arm64. (See CopyDlls call below).
@@ -84,7 +108,6 @@
             vs_runtime_dll_dirs.append('Arm64Unused')
 
         os.environ['GYP_MSVS_OVERRIDE_PATH'] = toolchain
-        os.environ['GYP_MSVS_VERSION'] = version
 
         os.environ['WINDOWSSDKDIR'] = win_sdk
         os.environ['WDK_DIR'] = wdk
@@ -94,8 +117,6 @@
     elif sys.platform == 'win32' and not depot_tools_win_toolchain:
         if not 'GYP_MSVS_OVERRIDE_PATH' in os.environ:
             os.environ['GYP_MSVS_OVERRIDE_PATH'] = DetectVisualStudioPath()
-        if not 'GYP_MSVS_VERSION' in os.environ:
-            os.environ['GYP_MSVS_VERSION'] = GetVisualStudioVersion()
 
         # When using an installed toolchain these files aren't needed in the output
         # directory in order to run binaries locally, but they are needed in order
@@ -146,12 +167,7 @@
 def GetVisualStudioVersion():
     """Return best available version of Visual Studio.
   """
-
-    env_version = os.environ.get('GYP_MSVS_VERSION')
-    if env_version:
-        return env_version
-
-    supported_versions = MSVS_VERSIONS.keys()
+    supported_versions = list(MSVS_VERSIONS.keys())
 
     # VS installed in depot_tools for Googlers
     if bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1'))):
@@ -162,13 +178,23 @@
         '{} ({})'.format(v, k) for k, v in MSVS_VERSIONS.items())
     available_versions = []
     for version in supported_versions:
-        for path in (
-                os.environ.get('vs%s_install' % version),
-                os.path.expandvars('%ProgramFiles(x86)%' +
-                                   '/Microsoft Visual Studio/%s' % version)):
-            if path and os.path.exists(path):
-                available_versions.append(version)
-                break
+        # Checking vs%s_install environment variables.
+        # For example, vs2019_install could have the value
+        # "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community".
+        # Only vs2017_install and vs2019_install are supported.
+        path = os.environ.get('vs%s_install' % version)
+        if path and os.path.exists(path):
+            available_versions.append(version)
+            break
+        # Detecting VS under possible paths.
+        path = os.path.expandvars('%ProgramFiles(x86)%' +
+                                  '/Microsoft Visual Studio/%s' % version)
+        if path and any(
+                os.path.exists(os.path.join(path, edition))
+                for edition in ('Enterprise', 'Professional', 'Community',
+                                'Preview', 'BuildTools')):
+            available_versions.append(version)
+            break
 
     if not available_versions:
         raise Exception('No supported Visual Studio can be found.'
@@ -177,7 +203,7 @@
 
 
 def DetectVisualStudioPath():
-    """Return path to the GYP_MSVS_VERSION of Visual Studio.
+    """Return path to the installed Visual Studio.
   """
 
     # Note that this code is used from
@@ -188,25 +214,26 @@
     # the registry. For details see:
     # https://blogs.msdn.microsoft.com/heaths/2016/09/15/changes-to-visual-studio-15-setup/
     # For now we use a hardcoded default with an environment variable override.
-    for path in (
-            os.environ.get('vs%s_install' % version_as_year),
-            os.path.expandvars(
-                '%ProgramFiles(x86)%' +
-                '/Microsoft Visual Studio/%s/Enterprise' % version_as_year),
-            os.path.expandvars(
-                '%ProgramFiles(x86)%' +
-                '/Microsoft Visual Studio/%s/Professional' % version_as_year),
-            os.path.expandvars(
-                '%ProgramFiles(x86)%' +
-                '/Microsoft Visual Studio/%s/Community' % version_as_year),
-            os.path.expandvars(
-                '%ProgramFiles(x86)%' +
-                '/Microsoft Visual Studio/%s/Preview' % version_as_year)):
+    for path in (os.environ.get('vs%s_install' % version_as_year),
+                 os.path.expandvars('%ProgramFiles(x86)%' +
+                                    '/Microsoft Visual Studio/%s/Enterprise' %
+                                    version_as_year),
+                 os.path.expandvars('%ProgramFiles(x86)%' +
+                                    '/Microsoft Visual Studio/%s/Professional' %
+                                    version_as_year),
+                 os.path.expandvars('%ProgramFiles(x86)%' +
+                                    '/Microsoft Visual Studio/%s/Community' %
+                                    version_as_year),
+                 os.path.expandvars('%ProgramFiles(x86)%' +
+                                    '/Microsoft Visual Studio/%s/Preview' %
+                                    version_as_year),
+                 os.path.expandvars('%ProgramFiles(x86)%' +
+                                    '/Microsoft Visual Studio/%s/BuildTools' %
+                                    version_as_year)):
         if path and os.path.exists(path):
             return path
 
-    raise Exception('Visual Studio Version %s (from GYP_MSVS_VERSION)'
-                    ' not found.' % version_as_year)
+    raise Exception('Visual Studio Version %s not found.' % version_as_year)
 
 
 def _CopyRuntimeImpl(target, source, verbose=True):
@@ -251,21 +278,28 @@
     list_of_str_versions.sort(key=to_number_sequence, reverse=True)
 
 
-def _CopyUCRTRuntime(target_dir, source_dir, target_cpu, dll_pattern, suffix):
+def _CopyUCRTRuntime(target_dir, source_dir, target_cpu, suffix):
     """Copy both the msvcp and vccorlib runtime DLLs, only if the target doesn't
   exist, but the target directory does exist."""
     if target_cpu == 'arm64':
         # Windows ARM64 VCRuntime is located at {toolchain_root}/VC/Redist/MSVC/
-        # {x.y.z}/[debug_nonredist/]arm64/Microsoft.VC141.CRT/.
+        # {x.y.z}/[debug_nonredist/]arm64/Microsoft.VC14x.CRT/.
+        # Select VC toolset directory based on Visual Studio version
         vc_redist_root = FindVCRedistRoot()
         if suffix.startswith('.'):
-            source_dir = os.path.join(vc_redist_root, 'arm64',
-                                      'Microsoft.VC141.CRT')
+            vc_toolset_dir = 'Microsoft.{}.CRT' \
+               .format(MSVC_TOOLSET_VERSION[GetVisualStudioVersion()])
+            source_dir = os.path.join(vc_redist_root, 'arm64', vc_toolset_dir)
         else:
+            vc_toolset_dir = 'Microsoft.{}.DebugCRT' \
+               .format(MSVC_TOOLSET_VERSION[GetVisualStudioVersion()])
             source_dir = os.path.join(vc_redist_root, 'debug_nonredist',
-                                      'arm64', 'Microsoft.VC141.DebugCRT')
-    for file_part in ('msvcp', 'vccorlib', 'vcruntime'):
-        dll = dll_pattern % file_part
+                                      'arm64', vc_toolset_dir)
+    file_parts = ('msvcp140', 'vccorlib140', 'vcruntime140')
+    if target_cpu == 'x64' and GetVisualStudioVersion() != '2017':
+        file_parts = file_parts + ('vcruntime140_1',)
+    for file_part in file_parts:
+        dll = file_part + suffix
         target = os.path.join(target_dir, dll)
         source = os.path.join(source_dir, dll)
         _CopyRuntimeImpl(target, source)
@@ -301,22 +335,19 @@
         if not suffix.startswith('.'):
             # ucrtbased.dll is located at {win_sdk_dir}/bin/{a.b.c.d}/{target_cpu}/
             # ucrt/.
-            sdk_redist_root = os.path.join(win_sdk_dir, 'bin')
-            sdk_bin_sub_dirs = os.listdir(sdk_redist_root)
+            sdk_bin_root = os.path.join(win_sdk_dir, 'bin')
+            sdk_bin_sub_dirs = glob.glob(os.path.join(sdk_bin_root, '10.*'))
             # Select the most recent SDK if there are multiple versions installed.
             _SortByHighestVersionNumberFirst(sdk_bin_sub_dirs)
             for directory in sdk_bin_sub_dirs:
-                sdk_redist_root_version = os.path.join(sdk_redist_root,
-                                                       directory)
+                sdk_redist_root_version = os.path.join(sdk_bin_root, directory)
                 if not os.path.isdir(sdk_redist_root_version):
                     continue
-                if re.match(r'10\.\d+\.\d+\.\d+', directory):
-                    source_dir = os.path.join(sdk_redist_root_version,
-                                              target_cpu, 'ucrt')
-                    break
-        _CopyRuntimeImpl(
-            os.path.join(target_dir, 'ucrtbase' + suffix),
-            os.path.join(source_dir, 'ucrtbase' + suffix))
+                source_dir = os.path.join(sdk_redist_root_version, target_cpu,
+                                          'ucrt')
+                break
+        _CopyRuntimeImpl(os.path.join(target_dir, 'ucrtbase' + suffix),
+                         os.path.join(source_dir, 'ucrtbase' + suffix))
 
 
 def FindVCComponentRoot(component):
@@ -330,14 +361,13 @@
     assert ('GYP_MSVS_OVERRIDE_PATH' in os.environ)
     vc_component_msvc_root = os.path.join(os.environ['GYP_MSVS_OVERRIDE_PATH'],
                                           'VC', component, 'MSVC')
-    vc_component_msvc_contents = os.listdir(vc_component_msvc_root)
+    vc_component_msvc_contents = glob.glob(
+        os.path.join(vc_component_msvc_root, '14.*'))
     # Select the most recent toolchain if there are several.
     _SortByHighestVersionNumberFirst(vc_component_msvc_contents)
     for directory in vc_component_msvc_contents:
-        if not os.path.isdir(os.path.join(vc_component_msvc_root, directory)):
-            continue
-        if re.match(r'14\.\d+\.\d+', directory):
-            return os.path.join(vc_component_msvc_root, directory)
+        if os.path.isdir(directory):
+            return directory
     raise Exception('Unable to find the VC %s directory.' % component)
 
 
@@ -355,8 +385,7 @@
   directory does exist. Handles VS 2015, 2017 and 2019."""
     suffix = 'd.dll' if debug else '.dll'
     # VS 2015, 2017 and 2019 use the same CRT DLLs.
-    _CopyUCRTRuntime(target_dir, source_dir, target_cpu, '%s140' + suffix,
-                     suffix)
+    _CopyUCRTRuntime(target_dir, source_dir, target_cpu, suffix)
 
 
 def CopyDlls(target_dir, configuration, target_cpu):
@@ -407,6 +436,10 @@
     # List of debug files that should be copied, the first element of the tuple is
     # the name of the file and the second indicates if it's optional.
     debug_files = [('dbghelp.dll', False), ('dbgcore.dll', True)]
+    # The UCRT is not a redistributable component on arm64.
+    if target_cpu != 'arm64':
+        debug_files.extend([('api-ms-win-downlevel-kernel32-l2-1-0.dll', False),
+                            ('api-ms-win-eventing-provider-l1-1-0.dll', False)])
     for debug_file, is_optional in debug_files:
         full_path = os.path.join(win_sdk_dir, 'Debuggers', target_cpu,
                                  debug_file)
@@ -414,11 +447,11 @@
             if is_optional:
                 continue
             else:
-                # TODO(crbug.com/773476): remove version requirement.
                 raise Exception(
-                    '%s not found in "%s"\r\nYou must install the '
-                    '"Debugging Tools for Windows" feature from the Windows'
-                    ' 10 SDK.' % (debug_file, full_path))
+                    '%s not found in "%s"\r\nYou must install'
+                    'Windows 10 SDK version 10.0.19041.0 including the '
+                    '"Debugging Tools for Windows" feature.' %
+                    (debug_file, full_path))
         target_path = os.path.join(target_dir, debug_file)
         _CopyRuntimeImpl(target_path, full_path)
 
@@ -426,17 +459,10 @@
 def _GetDesiredVsToolchainHashes():
     """Load a list of SHA1s corresponding to the toolchains that we want installed
   to build with."""
-    env_version = GetVisualStudioVersion()
-    if env_version == '2017':
-        # VS 2017 Update 9 (15.9.12) with 10.0.18362 SDK, 10.0.17763 version of
-        # Debuggers, and 10.0.17134 version of d3dcompiler_47.dll, with ARM64
-        # libraries.
-        toolchain_hash = '418b3076791776573a815eb298c8aa590307af63'
-        # Third parties that do not have access to the canonical toolchain can map
-        # canonical toolchain version to their own toolchain versions.
-        toolchain_hash_mapping_key = 'GYP_MSVS_HASH_%s' % toolchain_hash
-        return [os.environ.get(toolchain_hash_mapping_key, toolchain_hash)]
-    raise Exception('Unsupported VS version %s' % env_version)
+    # Third parties that do not have access to the canonical toolchain can map
+    # canonical toolchain version to their own toolchain versions.
+    toolchain_hash_mapping_key = 'GYP_MSVS_HASH_%s' % TOOLCHAIN_HASH
+    return [os.environ.get(toolchain_hash_mapping_key, TOOLCHAIN_HASH)]
 
 
 def ShouldUpdateToolchain():
@@ -467,8 +493,7 @@
 
     depot_tools_win_toolchain = \
         bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1')))
-    if ((sys.platform in ('win32', 'cygwin') or force) and
-            depot_tools_win_toolchain):
+    if (_HostIsWindows() or force) and depot_tools_win_toolchain:
         import find_depot_tools
         depot_tools_path = find_depot_tools.add_depot_tools_to_path()
 
@@ -500,11 +525,9 @@
                 toolchain_dir
             ])
 
-        # Necessary so that get_toolchain_if_necessary.py will put the VS toolkit
-        # in the correct directory.
-        os.environ['GYP_MSVS_VERSION'] = GetVisualStudioVersion()
         get_toolchain_args = [
-            sys.executable,
+            # TODO(athom): use sys.executable (python3).
+            'python',
             os.path.join(depot_tools_path, 'win_toolchain',
                          'get_toolchain_if_necessary.py'),
             '--output-json',
diff --git a/build/win/importlibs/create_importlib_win.py b/build/win/importlibs/create_importlib_win.py
index 401b6e8..b23a7c0 100755
--- a/build/win/importlibs/create_importlib_win.py
+++ b/build/win/importlibs/create_importlib_win.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/build/win/importlibs/filter_export_list.py b/build/win/importlibs/filter_export_list.py
index 23fe362..b1fc122 100755
--- a/build/win/importlibs/filter_export_list.py
+++ b/build/win/importlibs/filter_export_list.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
@@ -79,7 +79,7 @@
         found_exports = set(master_mapping.values()) - set(found_exports)
 
     # Sort the found exports for tidy output.
-    print '\n'.join(sorted(found_exports))
+    print('\n'.join(sorted(found_exports)))
     return 0
 
 
diff --git a/build/win/reorder-imports.py b/build/win/reorder-imports.py
index f96a9ec..45c4c9a 100755
--- a/build/win/reorder-imports.py
+++ b/build/win/reorder-imports.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright 2014 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/build/win/use_ansi_codes.py b/build/win/use_ansi_codes.py
index b24ae24..c453c7b 100755
--- a/build/win/use_ansi_codes.py
+++ b/build/win/use_ansi_codes.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2015 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
@@ -7,4 +7,4 @@
 import os
 
 # Add more terminals here as needed.
-print 'ANSICON' in os.environ
+print('ANSICON' in os.environ)
diff --git a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
index bfa4d8d..f75600c 100644
--- a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
@@ -1261,28 +1261,6 @@
         r"""A constant constructor can't call a non-constant super constructor.""");
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Template<Message Function(String stringOKEmpty)>
-    templateConstEvalBadState =
-    const Template<Message Function(String stringOKEmpty)>(
-        messageTemplate: r"""Bad state: '#stringOKEmpty'""",
-        withArguments: _withArgumentsConstEvalBadState);
-
-// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Code<Message Function(String stringOKEmpty)> codeConstEvalBadState =
-    const Code<Message Function(String stringOKEmpty)>(
-  "ConstEvalBadState",
-);
-
-// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-Message _withArgumentsConstEvalBadState(String stringOKEmpty) {
-  // ignore: unnecessary_null_comparison
-  if (stringOKEmpty == null || stringOKEmpty.isEmpty) stringOKEmpty = '(empty)';
-  return new Message(codeConstEvalBadState,
-      message: """Bad state: '${stringOKEmpty}'""",
-      arguments: {'stringOKEmpty': stringOKEmpty});
-}
-
-// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Code<Null> codeConstEvalCircularity = messageConstEvalCircularity;
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
@@ -1561,6 +1539,29 @@
     message: r"""Couldn't evaluate constant expression.""");
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<Message Function(String stringOKEmpty)>
+    templateConstEvalUnhandledCoreException =
+    const Template<Message Function(String stringOKEmpty)>(
+        messageTemplate: r"""Unhandled core exception: #stringOKEmpty""",
+        withArguments: _withArgumentsConstEvalUnhandledCoreException);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String stringOKEmpty)>
+    codeConstEvalUnhandledCoreException =
+    const Code<Message Function(String stringOKEmpty)>(
+  "ConstEvalUnhandledCoreException",
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsConstEvalUnhandledCoreException(String stringOKEmpty) {
+  // ignore: unnecessary_null_comparison
+  if (stringOKEmpty == null || stringOKEmpty.isEmpty) stringOKEmpty = '(empty)';
+  return new Message(codeConstEvalUnhandledCoreException,
+      message: """Unhandled core exception: ${stringOKEmpty}""",
+      arguments: {'stringOKEmpty': stringOKEmpty});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Template<
     Message Function(
         String string,
@@ -3735,26 +3736,30 @@
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Template<
-    Message Function(String name)> templateFfiEmptyStruct = const Template<
-        Message Function(String name)>(
+    Message Function(
+        String string,
+        String
+            name)> templateFfiEmptyStruct = const Template<
+        Message Function(String string, String name)>(
     messageTemplate:
-        r"""Struct '#name' is empty. Empty structs are undefined behavior.""",
+        r"""#string '#name' is empty. Empty structs and unions are undefined behavior.""",
     withArguments: _withArgumentsFfiEmptyStruct);
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Code<Message Function(String name)> codeFfiEmptyStruct =
-    const Code<Message Function(String name)>(
+const Code<Message Function(String string, String name)> codeFfiEmptyStruct =
+    const Code<Message Function(String string, String name)>(
   "FfiEmptyStruct",
 );
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-Message _withArgumentsFfiEmptyStruct(String name) {
+Message _withArgumentsFfiEmptyStruct(String string, String name) {
+  if (string.isEmpty) throw 'No string provided';
   if (name.isEmpty) throw 'No name provided';
   name = demangleMixinApplicationName(name);
   return new Message(codeFfiEmptyStruct,
       message:
-          """Struct '${name}' is empty. Empty structs are undefined behavior.""",
-      arguments: {'name': name});
+          """${string} '${name}' is empty. Empty structs and unions are undefined behavior.""",
+      arguments: {'string': string, 'name': name});
 }
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
@@ -3802,7 +3807,7 @@
     Message Function(String name)> templateFfiFieldAnnotation = const Template<
         Message Function(String name)>(
     messageTemplate:
-        r"""Field '#name' requires exactly one annotation to declare its native type, which cannot be Void. dart:ffi Structs cannot have regular Dart fields.""",
+        r"""Field '#name' requires exactly one annotation to declare its native type, which cannot be Void. dart:ffi Structs and Unions cannot have regular Dart fields.""",
     withArguments: _withArgumentsFfiFieldAnnotation);
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
@@ -3817,33 +3822,36 @@
   name = demangleMixinApplicationName(name);
   return new Message(codeFfiFieldAnnotation,
       message:
-          """Field '${name}' requires exactly one annotation to declare its native type, which cannot be Void. dart:ffi Structs cannot have regular Dart fields.""",
+          """Field '${name}' requires exactly one annotation to declare its native type, which cannot be Void. dart:ffi Structs and Unions cannot have regular Dart fields.""",
       arguments: {'name': name});
 }
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Template<Message Function(String name, List<String> _names)>
-    templateFfiFieldCyclic =
-    const Template<Message Function(String name, List<String> _names)>(
-        messageTemplate: r"""Struct '#name' contains itself. Cycle elements:
+const Template<
+        Message Function(String string, String name, List<String> _names)>
+    templateFfiFieldCyclic = const Template<
+            Message Function(String string, String name, List<String> _names)>(
+        messageTemplate: r"""#string '#name' contains itself. Cycle elements:
 #names""", withArguments: _withArgumentsFfiFieldCyclic);
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Code<Message Function(String name, List<String> _names)>
-    codeFfiFieldCyclic =
-    const Code<Message Function(String name, List<String> _names)>(
+const Code<Message Function(String string, String name, List<String> _names)>
+    codeFfiFieldCyclic = const Code<
+        Message Function(String string, String name, List<String> _names)>(
   "FfiFieldCyclic",
 );
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-Message _withArgumentsFfiFieldCyclic(String name, List<String> _names) {
+Message _withArgumentsFfiFieldCyclic(
+    String string, String name, List<String> _names) {
+  if (string.isEmpty) throw 'No string provided';
   if (name.isEmpty) throw 'No name provided';
   name = demangleMixinApplicationName(name);
   if (_names.isEmpty) throw 'No names provided';
   String names = itemizeNames(_names);
   return new Message(codeFfiFieldCyclic,
-      message: """Struct '${name}' contains itself. Cycle elements:
-${names}""", arguments: {'name': name, 'names': _names});
+      message: """${string} '${name}' contains itself. Cycle elements:
+${names}""", arguments: {'string': string, 'name': name, 'names': _names});
 }
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
@@ -3904,7 +3912,7 @@
     Message Function(String name)> templateFfiFieldNull = const Template<
         Message Function(String name)>(
     messageTemplate:
-        r"""Field '#name' cannot have type 'Null', it must be `int`, `double`, `Pointer`, or a subtype of `Struct`.""",
+        r"""Field '#name' cannot have type 'Null', it must be `int`, `double`, `Pointer`, or a subtype of `Struct` or `Union`.""",
     withArguments: _withArgumentsFfiFieldNull);
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
@@ -3919,7 +3927,7 @@
   name = demangleMixinApplicationName(name);
   return new Message(codeFfiFieldNull,
       message:
-          """Field '${name}' cannot have type 'Null', it must be `int`, `double`, `Pointer`, or a subtype of `Struct`.""",
+          """Field '${name}' cannot have type 'Null', it must be `int`, `double`, `Pointer`, or a subtype of `Struct` or `Union`.""",
       arguments: {'name': name});
 }
 
@@ -4058,24 +4066,26 @@
 }
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Template<Message Function(String name)> templateFfiStructGeneric =
-    const Template<Message Function(String name)>(
-        messageTemplate: r"""Struct '#name' should not be generic.""",
+const Template<Message Function(String string, String name)>
+    templateFfiStructGeneric =
+    const Template<Message Function(String string, String name)>(
+        messageTemplate: r"""#string '#name' should not be generic.""",
         withArguments: _withArgumentsFfiStructGeneric);
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Code<Message Function(String name)> codeFfiStructGeneric =
-    const Code<Message Function(String name)>(
+const Code<Message Function(String string, String name)> codeFfiStructGeneric =
+    const Code<Message Function(String string, String name)>(
   "FfiStructGeneric",
 );
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-Message _withArgumentsFfiStructGeneric(String name) {
+Message _withArgumentsFfiStructGeneric(String string, String name) {
+  if (string.isEmpty) throw 'No string provided';
   if (name.isEmpty) throw 'No name provided';
   name = demangleMixinApplicationName(name);
   return new Message(codeFfiStructGeneric,
-      message: """Struct '${name}' should not be generic.""",
-      arguments: {'name': name});
+      message: """${string} '${name}' should not be generic.""",
+      arguments: {'string': string, 'name': name});
 }
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/argument_type_not_assignable_nullability_error.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/argument_type_not_assignable_nullability_error.dart
index a5c894b..3421716 100644
--- a/pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/argument_type_not_assignable_nullability_error.dart
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/argument_type_not_assignable_nullability_error.dart
@@ -13,9 +13,8 @@
 
 required_unnamed(C1 c) {
   if (c.bad == null) return;
-  c.f(
-      /*analyzer.notPromoted(propertyNotPromoted(target: member:C1.bad, type: int?))*/ c
-          . /*cfe.notPromoted(propertyNotPromoted(target: member:C1.bad, type: int?))*/ bad);
+  c.f(c.
+      /*notPromoted(propertyNotPromoted(target: member:C1.bad, type: int?))*/ bad);
 }
 
 class C2 {
@@ -25,9 +24,8 @@
 
 optional_unnamed(C2 c) {
   if (c.bad == null) return;
-  c.f(
-      /*analyzer.notPromoted(propertyNotPromoted(target: member:C2.bad, type: int?))*/ c
-          . /*cfe.notPromoted(propertyNotPromoted(target: member:C2.bad, type: int?))*/ bad);
+  c.f(c.
+      /*notPromoted(propertyNotPromoted(target: member:C2.bad, type: int?))*/ bad);
 }
 
 class C3 {
@@ -38,8 +36,8 @@
 required_named(C3 c) {
   if (c.bad == null) return;
   c.f(
-      /*analyzer.notPromoted(propertyNotPromoted(target: member:C3.bad, type: int?))*/ i:
-          c. /*cfe.notPromoted(propertyNotPromoted(target: member:C3.bad, type: int?))*/ bad);
+      i: c.
+          /*notPromoted(propertyNotPromoted(target: member:C3.bad, type: int?))*/ bad);
 }
 
 class C4 {
@@ -50,8 +48,8 @@
 optional_named(C4 c) {
   if (c.bad == null) return;
   c.f(
-      /*analyzer.notPromoted(propertyNotPromoted(target: member:C4.bad, type: int?))*/ i:
-          c. /*cfe.notPromoted(propertyNotPromoted(target: member:C4.bad, type: int?))*/ bad);
+      i: c.
+          /*notPromoted(propertyNotPromoted(target: member:C4.bad, type: int?))*/ bad);
 }
 
 class C5 {
@@ -61,9 +59,8 @@
 
 type_inferred(C5 c) {
   if (c.bad == null) return;
-  c.f(
-      /*analyzer.notPromoted(propertyNotPromoted(target: member:C5.bad, type: List<int>?))*/ c
-          . /*cfe.notPromoted(propertyNotPromoted(target: member:C5.bad, type: List<int>?))*/ bad);
+  c.f(c.
+      /*notPromoted(propertyNotPromoted(target: member:C5.bad, type: List<int>?))*/ bad);
 }
 
 class C6 {
@@ -73,9 +70,8 @@
 
 C6? constructor_with_implicit_new(C6 c) {
   if (c.bad == null) return null;
-  return C6(
-      /*analyzer.notPromoted(propertyNotPromoted(target: member:C6.bad, type: int?))*/ c
-          . /*cfe.notPromoted(propertyNotPromoted(target: member:C6.bad, type: int?))*/ bad);
+  return C6(c.
+      /*notPromoted(propertyNotPromoted(target: member:C6.bad, type: int?))*/ bad);
 }
 
 class C7 {
@@ -85,9 +81,8 @@
 
 C7? constructor_with_explicit_new(C7 c) {
   if (c.bad == null) return null;
-  return new C7(
-      /*analyzer.notPromoted(propertyNotPromoted(target: member:C7.bad, type: int?))*/ c
-          . /*cfe.notPromoted(propertyNotPromoted(target: member:C7.bad, type: int?))*/ bad);
+  return new C7(c.
+      /*notPromoted(propertyNotPromoted(target: member:C7.bad, type: int?))*/ bad);
 }
 
 class C8 {
@@ -97,8 +92,8 @@
 userDefinableBinaryOpRhs(C8 c) {
   if (c.bad == null) return;
   1 +
-      /*analyzer.notPromoted(propertyNotPromoted(target: member:C8.bad, type: int?))*/ c
-          . /*cfe.notPromoted(propertyNotPromoted(target: member:C8.bad, type: int?))*/ bad;
+      c.
+          /*notPromoted(propertyNotPromoted(target: member:C8.bad, type: int?))*/ bad;
 }
 
 class C9 {
@@ -140,13 +135,12 @@
 
 andOperand(C11 c, bool b) {
   if (c.bad == null) return;
-  c.f(
-      /*analyzer.notPromoted(propertyNotPromoted(target: member:C11.bad, type: bool?))*/ c
-              . /*cfe.notPromoted(propertyNotPromoted(target: member:C11.bad, type: bool?))*/ bad &&
-          b);
+  c.f(c.
+          /*notPromoted(propertyNotPromoted(target: member:C11.bad, type: bool?))*/ bad &&
+      b);
   c.f(b &&
-      /*analyzer.notPromoted(propertyNotPromoted(target: member:C11.bad, type: bool?))*/ c
-          . /*cfe.notPromoted(propertyNotPromoted(target: member:C11.bad, type: bool?))*/ bad);
+      c.
+          /*notPromoted(propertyNotPromoted(target: member:C11.bad, type: bool?))*/ bad);
 }
 
 class C12 {
@@ -156,13 +150,12 @@
 
 orOperand(C12 c, bool b) {
   if (c.bad == null) return;
-  c.f(
-      /*analyzer.notPromoted(propertyNotPromoted(target: member:C12.bad, type: bool?))*/ c
-              . /*cfe.notPromoted(propertyNotPromoted(target: member:C12.bad, type: bool?))*/ bad ||
-          b);
+  c.f(c.
+          /*notPromoted(propertyNotPromoted(target: member:C12.bad, type: bool?))*/ bad ||
+      b);
   c.f(b ||
-      /*analyzer.notPromoted(propertyNotPromoted(target: member:C12.bad, type: bool?))*/ c
-          . /*cfe.notPromoted(propertyNotPromoted(target: member:C12.bad, type: bool?))*/ bad);
+      c.
+          /*notPromoted(propertyNotPromoted(target: member:C12.bad, type: bool?))*/ bad);
 }
 
 class C13 {
@@ -171,18 +164,16 @@
 
 assertStatementCondition(C13 c) {
   if (c.bad == null) return;
-  assert(
-      /*analyzer.notPromoted(propertyNotPromoted(target: member:C13.bad, type: bool?))*/ c
-          . /*cfe.notPromoted(propertyNotPromoted(target: member:C13.bad, type: bool?))*/ bad);
+  assert(c.
+      /*notPromoted(propertyNotPromoted(target: member:C13.bad, type: bool?))*/ bad);
 }
 
 class C14 {
   bool? bad;
   C14.assertInitializerCondition(C14 c)
       : bad = c.bad!,
-        assert(
-            /*analyzer.notPromoted(propertyNotPromoted(target: member:C14.bad, type: bool?))*/ c
-                . /*cfe.notPromoted(propertyNotPromoted(target: member:C14.bad, type: bool?))*/ bad);
+        assert(c.
+            /*notPromoted(propertyNotPromoted(target: member:C14.bad, type: bool?))*/ bad);
 }
 
 class C15 {
@@ -192,9 +183,8 @@
 
 notOperand(C15 c) {
   if (c.bad == null) return;
-  c.f(!
-      /*analyzer.notPromoted(propertyNotPromoted(target: member:C15.bad, type: bool?))*/ c
-          . /*cfe.notPromoted(propertyNotPromoted(target: member:C15.bad, type: bool?))*/ bad);
+  c.f(!c.
+      /*notPromoted(propertyNotPromoted(target: member:C15.bad, type: bool?))*/ bad);
 }
 
 class C16 {
@@ -205,24 +195,24 @@
 forLoopCondition(C16 c) {
   if (c.bad == null) return;
   for (;
-      /*analyzer.notPromoted(propertyNotPromoted(target: member:C16.bad, type: bool?))*/ c
-          . /*cfe.notPromoted(propertyNotPromoted(target: member:C16.bad, type: bool?))*/ bad;) {}
+      c.
+          /*notPromoted(propertyNotPromoted(target: member:C16.bad, type: bool?))*/ bad;) {}
   [
     for (;
-        /*analyzer.notPromoted(propertyNotPromoted(target: member:C16.bad, type: bool?))*/ c
-            . /*cfe.notPromoted(propertyNotPromoted(target: member:C16.bad, type: bool?))*/ bad;)
+        c.
+            /*notPromoted(propertyNotPromoted(target: member:C16.bad, type: bool?))*/ bad;)
       null
   ];
   ({
     for (;
-        /*analyzer.notPromoted(propertyNotPromoted(target: member:C16.bad, type: bool?))*/ c
-            . /*cfe.notPromoted(propertyNotPromoted(target: member:C16.bad, type: bool?))*/ bad;)
+        c.
+            /*notPromoted(propertyNotPromoted(target: member:C16.bad, type: bool?))*/ bad;)
       null
   });
   ({
     for (;
-        /*analyzer.notPromoted(propertyNotPromoted(target: member:C16.bad, type: bool?))*/ c
-            . /*cfe.notPromoted(propertyNotPromoted(target: member:C16.bad, type: bool?))*/ bad;)
+        c.
+            /*notPromoted(propertyNotPromoted(target: member:C16.bad, type: bool?))*/ bad;)
       null: null
   });
 }
@@ -234,11 +224,10 @@
 
 conditionalExpressionCondition(C17 c) {
   if (c.bad == null) return;
-  c.f(
-      /*analyzer.notPromoted(propertyNotPromoted(target: member:C17.bad, type: bool?))*/ c
-              . /*cfe.notPromoted(propertyNotPromoted(target: member:C17.bad, type: bool?))*/ bad
-          ? 1
-          : 2);
+  c.f(c.
+          /*notPromoted(propertyNotPromoted(target: member:C17.bad, type: bool?))*/ bad
+      ? 1
+      : 2);
 }
 
 class C18 {
@@ -247,9 +236,8 @@
 
 doLoopCondition(C18 c) {
   if (c.bad == null) return;
-  do {} while (
-      /*analyzer.notPromoted(propertyNotPromoted(target: member:C18.bad, type: bool?))*/ c
-          . /*cfe.notPromoted(propertyNotPromoted(target: member:C18.bad, type: bool?))*/ bad);
+  do {} while (c.
+      /*notPromoted(propertyNotPromoted(target: member:C18.bad, type: bool?))*/ bad);
 }
 
 class C19 {
@@ -258,25 +246,21 @@
 
 ifCondition(C19 c) {
   if (c.bad == null) return;
-  if (
-      /*analyzer.notPromoted(propertyNotPromoted(target: member:C19.bad, type: bool?))*/ c
-          . /*cfe.notPromoted(propertyNotPromoted(target: member:C19.bad, type: bool?))*/ bad) {}
+  if (c.
+      /*notPromoted(propertyNotPromoted(target: member:C19.bad, type: bool?))*/ bad) {}
   [
-    if (
-    /*analyzer.notPromoted(propertyNotPromoted(target: member:C19.bad, type: bool?))*/ c
-        . /*cfe.notPromoted(propertyNotPromoted(target: member:C19.bad, type: bool?))*/ bad)
+    if (c.
+        /*notPromoted(propertyNotPromoted(target: member:C19.bad, type: bool?))*/ bad)
       null
   ];
   ({
-    if (
-    /*analyzer.notPromoted(propertyNotPromoted(target: member:C19.bad, type: bool?))*/ c
-        . /*cfe.notPromoted(propertyNotPromoted(target: member:C19.bad, type: bool?))*/ bad)
+    if (c.
+        /*notPromoted(propertyNotPromoted(target: member:C19.bad, type: bool?))*/ bad)
       null
   });
   ({
-    if (
-    /*analyzer.notPromoted(propertyNotPromoted(target: member:C19.bad, type: bool?))*/ c
-        . /*cfe.notPromoted(propertyNotPromoted(target: member:C19.bad, type: bool?))*/ bad)
+    if (c.
+        /*notPromoted(propertyNotPromoted(target: member:C19.bad, type: bool?))*/ bad)
       null: null
   });
 }
@@ -287,8 +271,8 @@
 
 whileCondition(C20 c) {
   if (c.bad == null) return;
-  while (/*analyzer.notPromoted(propertyNotPromoted(target: member:C20.bad, type: bool?))*/ c
-      . /*cfe.notPromoted(propertyNotPromoted(target: member:C20.bad, type: bool?))*/ bad) {}
+  while (c.
+      /*notPromoted(propertyNotPromoted(target: member:C20.bad, type: bool?))*/ bad) {}
 }
 
 class C21 {
@@ -297,9 +281,8 @@
 
 assignmentRhs(C21 c, int i) {
   if (c.bad == null) return;
-  i =
-      /*analyzer.notPromoted(propertyNotPromoted(target: member:C21.bad, type: int?))*/ c
-          . /*cfe.notPromoted(propertyNotPromoted(target: member:C21.bad, type: int?))*/ bad;
+  i = c.
+      /*notPromoted(propertyNotPromoted(target: member:C21.bad, type: int?))*/ bad;
 }
 
 class C22 {
@@ -308,9 +291,8 @@
 
 variableInitializer(C22 c) {
   if (c.bad == null) return;
-  int i =
-      /*analyzer.notPromoted(propertyNotPromoted(target: member:C22.bad, type: int?))*/ c
-          . /*cfe.notPromoted(propertyNotPromoted(target: member:C22.bad, type: int?))*/ bad;
+  int i = c.
+      /*notPromoted(propertyNotPromoted(target: member:C22.bad, type: int?))*/ bad;
 }
 
 class C23 {
@@ -319,9 +301,8 @@
   final int y;
   C23.constructorInitializer(C23 c)
       : x = c.bad!,
-        y =
-            /*analyzer.notPromoted(propertyNotPromoted(target: member:C23.bad, type: int?))*/ c
-                . /*cfe.notPromoted(propertyNotPromoted(target: member:C23.bad, type: int?))*/ bad;
+        y = c.
+            /*notPromoted(propertyNotPromoted(target: member:C23.bad, type: int?))*/ bad;
 }
 
 class C24 {
@@ -330,28 +311,24 @@
 
 forVariableInitializer(C24 c) {
   if (c.bad == null) return;
-  for (int i =
-          /*analyzer.notPromoted(propertyNotPromoted(target: member:C24.bad, type: int?))*/ c
-              . /*cfe.notPromoted(propertyNotPromoted(target: member:C24.bad, type: int?))*/ bad;
+  for (int i = c.
+          /*notPromoted(propertyNotPromoted(target: member:C24.bad, type: int?))*/ bad;
       false;) {}
   [
-    for (int i =
-            /*analyzer.notPromoted(propertyNotPromoted(target: member:C24.bad, type: int?))*/ c
-                . /*cfe.notPromoted(propertyNotPromoted(target: member:C24.bad, type: int?))*/ bad;
+    for (int i = c.
+            /*notPromoted(propertyNotPromoted(target: member:C24.bad, type: int?))*/ bad;
         false;)
       null
   ];
   ({
-    for (int i =
-            /*analyzer.notPromoted(propertyNotPromoted(target: member:C24.bad, type: int?))*/ c
-                . /*cfe.notPromoted(propertyNotPromoted(target: member:C24.bad, type: int?))*/ bad;
+    for (int i = c.
+            /*notPromoted(propertyNotPromoted(target: member:C24.bad, type: int?))*/ bad;
         false;)
       null
   });
   ({
-    for (int i =
-            /*analyzer.notPromoted(propertyNotPromoted(target: member:C24.bad, type: int?))*/ c
-                . /*cfe.notPromoted(propertyNotPromoted(target: member:C24.bad, type: int?))*/ bad;
+    for (int i = c.
+            /*notPromoted(propertyNotPromoted(target: member:C24.bad, type: int?))*/ bad;
         false;)
       null: null
   });
@@ -363,28 +340,24 @@
 
 forAssignmentInitializer(C25 c, int i) {
   if (c.bad == null) return;
-  for (i =
-          /*analyzer.notPromoted(propertyNotPromoted(target: member:C25.bad, type: int?))*/ c
-              . /*cfe.notPromoted(propertyNotPromoted(target: member:C25.bad, type: int?))*/ bad;
+  for (i = c.
+          /*notPromoted(propertyNotPromoted(target: member:C25.bad, type: int?))*/ bad;
       false;) {}
   [
-    for (i =
-            /*analyzer.notPromoted(propertyNotPromoted(target: member:C25.bad, type: int?))*/ c
-                . /*cfe.notPromoted(propertyNotPromoted(target: member:C25.bad, type: int?))*/ bad;
+    for (i = c.
+            /*notPromoted(propertyNotPromoted(target: member:C25.bad, type: int?))*/ bad;
         false;)
       null
   ];
   ({
-    for (i =
-            /*analyzer.notPromoted(propertyNotPromoted(target: member:C25.bad, type: int?))*/ c
-                . /*cfe.notPromoted(propertyNotPromoted(target: member:C25.bad, type: int?))*/ bad;
+    for (i = c.
+            /*notPromoted(propertyNotPromoted(target: member:C25.bad, type: int?))*/ bad;
         false;)
       null
   });
   ({
-    for (i =
-            /*analyzer.notPromoted(propertyNotPromoted(target: member:C25.bad, type: int?))*/ c
-                . /*cfe.notPromoted(propertyNotPromoted(target: member:C25.bad, type: int?))*/ bad;
+    for (i = c.
+            /*notPromoted(propertyNotPromoted(target: member:C25.bad, type: int?))*/ bad;
         false;)
       null: null
   });
@@ -397,9 +370,8 @@
 compoundAssignmentRhs(C26 c) {
   num n = 0;
   if (c.bad == null) return;
-  n +=
-      /*analyzer.notPromoted(propertyNotPromoted(target: member:C26.bad, type: int?))*/ c
-          . /*cfe.notPromoted(propertyNotPromoted(target: member:C26.bad, type: int?))*/ bad;
+  n += c.
+      /*notPromoted(propertyNotPromoted(target: member:C26.bad, type: int?))*/ bad;
 }
 
 class C27 {
@@ -408,9 +380,8 @@
 
 indexGet(C27 c, List<int> values) {
   if (c.bad == null) return;
-  values[
-      /*analyzer.notPromoted(propertyNotPromoted(target: member:C27.bad, type: int?))*/ c
-          . /*cfe.notPromoted(propertyNotPromoted(target: member:C27.bad, type: int?))*/ bad];
+  values[c.
+      /*notPromoted(propertyNotPromoted(target: member:C27.bad, type: int?))*/ bad];
 }
 
 class C28 {
@@ -419,9 +390,8 @@
 
 indexSet(C28 c, List<int> values) {
   if (c.bad == null) return;
-  values[
-      /*analyzer.notPromoted(propertyNotPromoted(target: member:C28.bad, type: int?))*/ c
-          . /*cfe.notPromoted(propertyNotPromoted(target: member:C28.bad, type: int?))*/ bad] = 0;
+  values[c.
+      /*notPromoted(propertyNotPromoted(target: member:C28.bad, type: int?))*/ bad] = 0;
 }
 
 class C29 {
@@ -430,9 +400,8 @@
 
 indexSetCompound(C29 c, List<int> values) {
   if (c.bad == null) return;
-  values[
-      /*analyzer.notPromoted(propertyNotPromoted(target: member:C29.bad, type: int?))*/ c
-          . /*cfe.notPromoted(propertyNotPromoted(target: member:C29.bad, type: int?))*/ bad] += 1;
+  values[c.
+      /*notPromoted(propertyNotPromoted(target: member:C29.bad, type: int?))*/ bad] += 1;
 }
 
 class C30 {
@@ -441,9 +410,8 @@
 
 indexSetIfNull(C30 c, List<int?> values) {
   if (c.bad == null) return;
-  values[
-      /*analyzer.notPromoted(propertyNotPromoted(target: member:C30.bad, type: int?))*/ c
-          . /*cfe.notPromoted(propertyNotPromoted(target: member:C30.bad, type: int?))*/ bad] ??= 1;
+  values[c.
+      /*notPromoted(propertyNotPromoted(target: member:C30.bad, type: int?))*/ bad] ??= 1;
 }
 
 class C31 {
@@ -452,12 +420,10 @@
 
 indexSetPreIncDec(C31 c, List<int> values) {
   if (c.bad == null) return;
-  ++values[
-      /*analyzer.notPromoted(propertyNotPromoted(target: member:C31.bad, type: int?))*/ c
-          . /*cfe.notPromoted(propertyNotPromoted(target: member:C31.bad, type: int?))*/ bad];
-  --values[
-      /*analyzer.notPromoted(propertyNotPromoted(target: member:C31.bad, type: int?))*/ c
-          . /*cfe.notPromoted(propertyNotPromoted(target: member:C31.bad, type: int?))*/ bad];
+  ++values[c.
+      /*notPromoted(propertyNotPromoted(target: member:C31.bad, type: int?))*/ bad];
+  --values[c.
+      /*notPromoted(propertyNotPromoted(target: member:C31.bad, type: int?))*/ bad];
 }
 
 class C32 {
@@ -466,10 +432,23 @@
 
 indexSetPostIncDec(C32 c, List<int> values) {
   if (c.bad == null) return;
-  values[
-      /*analyzer.notPromoted(propertyNotPromoted(target: member:C32.bad, type: int?))*/ c
-          . /*cfe.notPromoted(propertyNotPromoted(target: member:C32.bad, type: int?))*/ bad]++;
-  values[
-      /*analyzer.notPromoted(propertyNotPromoted(target: member:C32.bad, type: int?))*/ c
-          . /*cfe.notPromoted(propertyNotPromoted(target: member:C32.bad, type: int?))*/ bad]--;
+  values[c.
+      /*notPromoted(propertyNotPromoted(target: member:C32.bad, type: int?))*/ bad]++;
+  values[c.
+      /*notPromoted(propertyNotPromoted(target: member:C32.bad, type: int?))*/ bad]--;
+}
+
+extension E33 on int {
+  void f() {}
+}
+
+class C33 {
+  int? bad;
+}
+
+test(C33 c) {
+  if (c.bad == null) return;
+  E33(c.
+          /*notPromoted(propertyNotPromoted(target: member:C33.bad, type: int?))*/ bad)
+      .f();
 }
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/for_in_loop_type_not_iterable_nullability_error.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/for_in_loop_type_not_iterable_nullability_error.dart
index 5bbde08..a893bc5 100644
--- a/pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/for_in_loop_type_not_iterable_nullability_error.dart
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/for_in_loop_type_not_iterable_nullability_error.dart
@@ -13,17 +13,15 @@
 
 forStatement(C1 c) {
   if (c.bad == null) return;
-  for (var x
-      in /*analyzer.notPromoted(propertyNotPromoted(target: member:C1.bad, type: List<int>?))*/ c
-          . /*cfe.notPromoted(propertyNotPromoted(target: member:C1.bad, type: List<int>?))*/ bad) {}
+  for (var x in c.
+      /*notPromoted(propertyNotPromoted(target: member:C1.bad, type: List<int>?))*/ bad) {}
 }
 
 forElementInList(C1 c) {
   if (c.bad == null) return;
   [
-    for (var x
-        in /*analyzer.notPromoted(propertyNotPromoted(target: member:C1.bad, type: List<int>?))*/ c
-            . /*cfe.notPromoted(propertyNotPromoted(target: member:C1.bad, type: List<int>?))*/ bad)
+    for (var x in c.
+        /*notPromoted(propertyNotPromoted(target: member:C1.bad, type: List<int>?))*/ bad)
       null
   ];
 }
@@ -31,9 +29,8 @@
 forElementInSet(C1 c) {
   if (c.bad == null) return;
   <dynamic>{
-    for (var x
-        in /*analyzer.notPromoted(propertyNotPromoted(target: member:C1.bad, type: List<int>?))*/ c
-            . /*cfe.notPromoted(propertyNotPromoted(target: member:C1.bad, type: List<int>?))*/ bad)
+    for (var x in c.
+        /*notPromoted(propertyNotPromoted(target: member:C1.bad, type: List<int>?))*/ bad)
       null
   };
 }
@@ -41,9 +38,8 @@
 forElementInMap(C1 c) {
   if (c.bad == null) return;
   <dynamic, dynamic>{
-    for (var x
-        in /*analyzer.notPromoted(propertyNotPromoted(target: member:C1.bad, type: List<int>?))*/ c
-            . /*cfe.notPromoted(propertyNotPromoted(target: member:C1.bad, type: List<int>?))*/ bad)
+    for (var x in c.
+        /*notPromoted(propertyNotPromoted(target: member:C1.bad, type: List<int>?))*/ bad)
       null: null
   };
 }
@@ -51,9 +47,8 @@
 forElementInAmbiguousSet_resolvableDuringParsing(C1 c) {
   if (c.bad == null) return;
   ({
-    for (var x
-        in /*analyzer.notPromoted(propertyNotPromoted(target: member:C1.bad, type: List<int>?))*/ c
-            . /*cfe.notPromoted(propertyNotPromoted(target: member:C1.bad, type: List<int>?))*/ bad)
+    for (var x in c.
+        /*notPromoted(propertyNotPromoted(target: member:C1.bad, type: List<int>?))*/ bad)
       null
   });
 }
@@ -61,9 +56,8 @@
 forElementInAmbiguousMap_resolvableDuringParsing(C1 c) {
   if (c.bad == null) return;
   ({
-    for (var x
-        in /*analyzer.notPromoted(propertyNotPromoted(target: member:C1.bad, type: List<int>?))*/ c
-            . /*cfe.notPromoted(propertyNotPromoted(target: member:C1.bad, type: List<int>?))*/ bad)
+    for (var x in c.
+        /*notPromoted(propertyNotPromoted(target: member:C1.bad, type: List<int>?))*/ bad)
       null: null
   });
 }
@@ -71,9 +65,8 @@
 forElementInAmbiguousSet_notResolvableDuringParsing(C1 c, List list) {
   if (c.bad == null) return;
   ({
-    for (var x
-        in /*analyzer.notPromoted(propertyNotPromoted(target: member:C1.bad, type: List<int>?))*/ c
-            . /*cfe.notPromoted(propertyNotPromoted(target: member:C1.bad, type: List<int>?))*/ bad)
+    for (var x in c.
+        /*notPromoted(propertyNotPromoted(target: member:C1.bad, type: List<int>?))*/ bad)
       ...list
   });
 }
@@ -81,9 +74,8 @@
 forElementInAmbiguousMap_notResolvableDuringParsing(C1 c, Map map) {
   if (c.bad == null) return;
   ({
-    for (var x
-        in /*analyzer.notPromoted(propertyNotPromoted(target: member:C1.bad, type: List<int>?))*/ c
-            . /*cfe.notPromoted(propertyNotPromoted(target: member:C1.bad, type: List<int>?))*/ bad)
+    for (var x in c.
+        /*notPromoted(propertyNotPromoted(target: member:C1.bad, type: List<int>?))*/ bad)
       ...map
   });
 }
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/invalid_assignment_error_nullability_error.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/invalid_assignment_error_nullability_error.dart
index a0f0388..95c3b97 100644
--- a/pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/invalid_assignment_error_nullability_error.dart
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/invalid_assignment_error_nullability_error.dart
@@ -13,6 +13,6 @@
 
 test(C1 c) sync* {
   if (c.bad == null) return;
-  yield* /*analyzer.notPromoted(propertyNotPromoted(target: member:C1.bad, type: List<int>?))*/ c
-      . /*cfe.notPromoted(propertyNotPromoted(target: member:C1.bad, type: List<int>?))*/ bad;
+  yield* c.
+      /*notPromoted(propertyNotPromoted(target: member:C1.bad, type: List<int>?))*/ bad;
 }
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/nullable_spread_error.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/nullable_spread_error.dart
index 6d05759..ece5484 100644
--- a/pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/nullable_spread_error.dart
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/nullable_spread_error.dart
@@ -16,56 +16,56 @@
 list_from_list_question(C c) {
   if (c.listQuestion == null) return;
   return [
-    ... /*analyzer.notPromoted(propertyNotPromoted(target: member:C.listQuestion, type: List<int>?))*/ c
-        . /*cfe.notPromoted(propertyNotPromoted(target: member:C.listQuestion, type: List<int>?))*/ listQuestion
+    ...c.
+        /*notPromoted(propertyNotPromoted(target: member:C.listQuestion, type: List<int>?))*/ listQuestion
   ];
 }
 
 list_from_set_question(C c) {
   if (c.setQuestion == null) return;
   return [
-    ... /*analyzer.notPromoted(propertyNotPromoted(target: member:C.setQuestion, type: Set<int>?))*/ c
-        . /*cfe.notPromoted(propertyNotPromoted(target: member:C.setQuestion, type: Set<int>?))*/ setQuestion
+    ...c.
+        /*notPromoted(propertyNotPromoted(target: member:C.setQuestion, type: Set<int>?))*/ setQuestion
   ];
 }
 
 list_from_map_question(C c) {
   if (c.mapQuestion == null) return;
   return [
-    ... /*analyzer.notPromoted(propertyNotPromoted(target: member:C.mapQuestion, type: Map<int, int>?))*/ c
-        . /*cfe.notPromoted(propertyNotPromoted(target: member:C.mapQuestion, type: Map<int, int>?))*/ mapQuestion
+    ...c.
+        /*notPromoted(propertyNotPromoted(target: member:C.mapQuestion, type: Map<int, int>?))*/ mapQuestion
   ];
 }
 
 list_from_object_question(C c) {
   if (c.objectQuestion is! List<int>) return;
   return [
-    ... /*analyzer.notPromoted(propertyNotPromoted(target: member:C.objectQuestion, type: Object?))*/ c
-        . /*cfe.notPromoted(propertyNotPromoted(target: member:C.objectQuestion, type: Object?))*/ objectQuestion
+    ...c.
+        /*notPromoted(propertyNotPromoted(target: member:C.objectQuestion, type: Object?))*/ objectQuestion
   ];
 }
 
 set_from_list_question(C c) {
   if (c.listQuestion == null) return;
   return {
-    ... /*analyzer.notPromoted(propertyNotPromoted(target: member:C.listQuestion, type: List<int>?))*/ c
-        . /*cfe.notPromoted(propertyNotPromoted(target: member:C.listQuestion, type: List<int>?))*/ listQuestion
+    ...c.
+        /*notPromoted(propertyNotPromoted(target: member:C.listQuestion, type: List<int>?))*/ listQuestion
   };
 }
 
 set_from_set_question(C c) {
   if (c.setQuestion == null) return;
   return {
-    ... /*analyzer.notPromoted(propertyNotPromoted(target: member:C.setQuestion, type: Set<int>?))*/ c
-        . /*cfe.notPromoted(propertyNotPromoted(target: member:C.setQuestion, type: Set<int>?))*/ setQuestion
+    ...c.
+        /*notPromoted(propertyNotPromoted(target: member:C.setQuestion, type: Set<int>?))*/ setQuestion
   };
 }
 
 set_from_map_question(C c) {
   if (c.mapQuestion == null) return;
   return {
-    ... /*analyzer.notPromoted(propertyNotPromoted(target: member:C.mapQuestion, type: Map<int, int>?))*/ c
-        . /*cfe.notPromoted(propertyNotPromoted(target: member:C.mapQuestion, type: Map<int, int>?))*/ mapQuestion
+    ...c.
+        /*notPromoted(propertyNotPromoted(target: member:C.mapQuestion, type: Map<int, int>?))*/ mapQuestion
   };
 }
 
@@ -73,8 +73,8 @@
   if (c.objectQuestion is! Set<int>) return;
   return {
     null,
-    ... /*analyzer.notPromoted(propertyNotPromoted(target: member:C.objectQuestion, type: Object?))*/ c
-        . /*cfe.notPromoted(propertyNotPromoted(target: member:C.objectQuestion, type: Object?))*/ objectQuestion
+    ...c.
+        /*notPromoted(propertyNotPromoted(target: member:C.objectQuestion, type: Object?))*/ objectQuestion
   };
 }
 
@@ -82,40 +82,40 @@
   if (c.objectQuestion is! Set<int>) return;
   return {
     ...<int>{},
-    ... /*analyzer.notPromoted(propertyNotPromoted(target: member:C.objectQuestion, type: Object?))*/ c
-        . /*cfe.notPromoted(propertyNotPromoted(target: member:C.objectQuestion, type: Object?))*/ objectQuestion
+    ...c.
+        /*notPromoted(propertyNotPromoted(target: member:C.objectQuestion, type: Object?))*/ objectQuestion
   };
 }
 
 set_from_object_question_type_disambiguate_by_literal_args(C c) {
   if (c.objectQuestion is! Set<int>) return;
   return <int>{
-    ... /*analyzer.notPromoted(propertyNotPromoted(target: member:C.objectQuestion, type: Object?))*/ c
-        . /*cfe.notPromoted(propertyNotPromoted(target: member:C.objectQuestion, type: Object?))*/ objectQuestion
+    ...c.
+        /*notPromoted(propertyNotPromoted(target: member:C.objectQuestion, type: Object?))*/ objectQuestion
   };
 }
 
 map_from_list_question(C c) {
   if (c.listQuestion == null) return;
   return {
-    ... /*analyzer.notPromoted(propertyNotPromoted(target: member:C.listQuestion, type: List<int>?))*/ c
-        . /*cfe.notPromoted(propertyNotPromoted(target: member:C.listQuestion, type: List<int>?))*/ listQuestion
+    ...c.
+        /*notPromoted(propertyNotPromoted(target: member:C.listQuestion, type: List<int>?))*/ listQuestion
   };
 }
 
 map_from_set_question(C c) {
   if (c.setQuestion == null) return;
   return {
-    ... /*analyzer.notPromoted(propertyNotPromoted(target: member:C.setQuestion, type: Set<int>?))*/ c
-        . /*cfe.notPromoted(propertyNotPromoted(target: member:C.setQuestion, type: Set<int>?))*/ setQuestion
+    ...c.
+        /*notPromoted(propertyNotPromoted(target: member:C.setQuestion, type: Set<int>?))*/ setQuestion
   };
 }
 
 map_from_map_question(C c) {
   if (c.mapQuestion == null) return;
   return {
-    ... /*analyzer.notPromoted(propertyNotPromoted(target: member:C.mapQuestion, type: Map<int, int>?))*/ c
-        . /*cfe.notPromoted(propertyNotPromoted(target: member:C.mapQuestion, type: Map<int, int>?))*/ mapQuestion
+    ...c.
+        /*notPromoted(propertyNotPromoted(target: member:C.mapQuestion, type: Map<int, int>?))*/ mapQuestion
   };
 }
 
@@ -123,8 +123,8 @@
   if (c.objectQuestion is! Map<int, int>) return;
   return {
     null: null,
-    ... /*analyzer.notPromoted(propertyNotPromoted(target: member:C.objectQuestion, type: Object?))*/ c
-        . /*cfe.notPromoted(propertyNotPromoted(target: member:C.objectQuestion, type: Object?))*/ objectQuestion
+    ...c.
+        /*notPromoted(propertyNotPromoted(target: member:C.objectQuestion, type: Object?))*/ objectQuestion
   };
 }
 
@@ -132,8 +132,8 @@
   if (c.objectQuestion is! Map<int, int>) return;
   return {
     ...<int, int>{},
-    ... /*analyzer.notPromoted(propertyNotPromoted(target: member:C.objectQuestion, type: Object?))*/ c
-        . /*cfe.notPromoted(propertyNotPromoted(target: member:C.objectQuestion, type: Object?))*/ objectQuestion
+    ...c.
+        /*notPromoted(propertyNotPromoted(target: member:C.objectQuestion, type: Object?))*/ objectQuestion
   };
 }
 
@@ -144,8 +144,7 @@
   // the other.
   if (c.setQuestion == null) return;
   return <int, int>{
-    ...
-    /*analyzer.notPromoted(propertyNotPromoted(target: member:C.setQuestion, type: Set<int>?))*/ c
-        .setQuestion
+    ...c.
+        /*analyzer.notPromoted(propertyNotPromoted(target: member:C.setQuestion, type: Set<int>?))*/ setQuestion
   };
 }
diff --git a/pkg/_fe_analyzer_shared/test/inference/inferred_type_arguments/data/constructor_invocation.dart b/pkg/_fe_analyzer_shared/test/inference/inferred_type_arguments/data/constructor_invocation.dart
index 4575c37..79d2844 100644
--- a/pkg/_fe_analyzer_shared/test/inference/inferred_type_arguments/data/constructor_invocation.dart
+++ b/pkg/_fe_analyzer_shared/test/inference/inferred_type_arguments/data/constructor_invocation.dart
@@ -68,7 +68,7 @@
   MultipleArgs/*<dynamic,dynamic,dynamic>*/(dyn, dyn, dyn);
   MultipleArgs/*<int,String,bool>*/(0, true, "");
   MultipleArgs<int, bool, String>(0, true, "");
-  var multipleArgs1 = MultipleArgs();
+  var multipleArgs1 = MultipleArgs /*analyzer.<dynamic,dynamic,dynamic>*/ ();
   var multipleArgs2 = MultipleArgs/*<int,String,bool>*/(0, true, "");
   MultipleArgs multipleArgs3 =
       MultipleArgs/*<dynamic,dynamic,dynamic>*/(0, true, "");
diff --git a/pkg/_fe_analyzer_shared/test/inference/inferred_type_arguments/data/marker.options b/pkg/_fe_analyzer_shared/test/inference/inferred_type_arguments/data/marker.options
index b0f2c56..9384923 100644
--- a/pkg/_fe_analyzer_shared/test/inference/inferred_type_arguments/data/marker.options
+++ b/pkg/_fe_analyzer_shared/test/inference/inferred_type_arguments/data/marker.options
@@ -1 +1,2 @@
 cfe=pkg/front_end/test/id_tests/inferred_type_arguments_test.dart
+analyzer=pkg/analyzer/test/id_tests/inferred_type_arguments_test.dart
diff --git a/pkg/_fe_analyzer_shared/test/inference/inferred_type_arguments/data/static_invocation.dart b/pkg/_fe_analyzer_shared/test/inference/inferred_type_arguments/data/static_invocation.dart
index 70932b3..b9254ec 100644
--- a/pkg/_fe_analyzer_shared/test/inference/inferred_type_arguments/data/static_invocation.dart
+++ b/pkg/_fe_analyzer_shared/test/inference/inferred_type_arguments/data/static_invocation.dart
@@ -73,7 +73,7 @@
   int extendsNumReturnArg6 = extendsNumReturnArg/*<int>*/("");
   int extendsNumReturnArg7 = extendsNumReturnArg<num>(0);
 
-  multipleArgs();
+  multipleArgs /*analyzer.<dynamic,dynamic,dynamic>*/ ();
   multipleArgs/*<int,String,bool>*/(0, true, "");
   multipleArgs<int, bool, String>(0, true, "");
 }
diff --git a/pkg/_fe_analyzer_shared/test/inference/inferred_variable_types/data/marker.options b/pkg/_fe_analyzer_shared/test/inference/inferred_variable_types/data/marker.options
index 5669a4b..f732667 100644
--- a/pkg/_fe_analyzer_shared/test/inference/inferred_variable_types/data/marker.options
+++ b/pkg/_fe_analyzer_shared/test/inference/inferred_variable_types/data/marker.options
@@ -1 +1,2 @@
-cfe=pkg/front_end/test/id_tests/inferred_variable_types_test.dart
\ No newline at end of file
+cfe=pkg/front_end/test/id_tests/inferred_variable_types_test.dart
+analyzer=pkg/analyzer/test/id_tests/inferred_variable_types_test.dart
diff --git a/pkg/analysis_server/lib/lsp_protocol/protocol_custom_generated.dart b/pkg/analysis_server/lib/lsp_protocol/protocol_custom_generated.dart
index 4695a19..fd739c7 100644
--- a/pkg/analysis_server/lib/lsp_protocol/protocol_custom_generated.dart
+++ b/pkg/analysis_server/lib/lsp_protocol/protocol_custom_generated.dart
@@ -178,6 +178,10 @@
     if (DartCompletionItemResolutionInfo.canParse(json, nullLspJsonReporter)) {
       return DartCompletionItemResolutionInfo.fromJson(json);
     }
+    if (PubPackageCompletionItemResolutionInfo.canParse(
+        json, nullLspJsonReporter)) {
+      return PubPackageCompletionItemResolutionInfo.fromJson(json);
+    }
     final file = json['file'];
     final offset = json['offset'];
     return CompletionItemResolutionInfo(file: file, offset: offset);
@@ -1185,6 +1189,121 @@
   String toString() => jsonEncoder.convert(toJson());
 }
 
+class PubPackageCompletionItemResolutionInfo
+    implements CompletionItemResolutionInfo, ToJsonable {
+  static const jsonHandler = LspJsonHandler(
+      PubPackageCompletionItemResolutionInfo.canParse,
+      PubPackageCompletionItemResolutionInfo.fromJson);
+
+  PubPackageCompletionItemResolutionInfo(
+      {required this.packageName, required this.file, required this.offset});
+  static PubPackageCompletionItemResolutionInfo fromJson(
+      Map<String, dynamic> json) {
+    final packageName = json['packageName'];
+    final file = json['file'];
+    final offset = json['offset'];
+    return PubPackageCompletionItemResolutionInfo(
+        packageName: packageName, file: file, offset: offset);
+  }
+
+  final String file;
+  final num offset;
+  final String packageName;
+
+  Map<String, dynamic> toJson() {
+    var __result = <String, dynamic>{};
+    __result['packageName'] = packageName;
+    __result['file'] = file;
+    __result['offset'] = offset;
+    return __result;
+  }
+
+  static bool canParse(Object obj, LspJsonReporter reporter) {
+    if (obj is Map<String, dynamic>) {
+      reporter.push('packageName');
+      try {
+        if (!obj.containsKey('packageName')) {
+          reporter.reportError('must not be undefined');
+          return false;
+        }
+        if (obj['packageName'] == null) {
+          reporter.reportError('must not be null');
+          return false;
+        }
+        if (!(obj['packageName'] is String)) {
+          reporter.reportError('must be of type String');
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('file');
+      try {
+        if (!obj.containsKey('file')) {
+          reporter.reportError('must not be undefined');
+          return false;
+        }
+        if (obj['file'] == null) {
+          reporter.reportError('must not be null');
+          return false;
+        }
+        if (!(obj['file'] is String)) {
+          reporter.reportError('must be of type String');
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      reporter.push('offset');
+      try {
+        if (!obj.containsKey('offset')) {
+          reporter.reportError('must not be undefined');
+          return false;
+        }
+        if (obj['offset'] == null) {
+          reporter.reportError('must not be null');
+          return false;
+        }
+        if (!(obj['offset'] is num)) {
+          reporter.reportError('must be of type num');
+          return false;
+        }
+      } finally {
+        reporter.pop();
+      }
+      return true;
+    } else {
+      reporter.reportError(
+          'must be of type PubPackageCompletionItemResolutionInfo');
+      return false;
+    }
+  }
+
+  @override
+  bool operator ==(Object other) {
+    if (other is PubPackageCompletionItemResolutionInfo &&
+        other.runtimeType == PubPackageCompletionItemResolutionInfo) {
+      return packageName == other.packageName &&
+          file == other.file &&
+          offset == other.offset &&
+          true;
+    }
+    return false;
+  }
+
+  @override
+  int get hashCode {
+    var hash = 0;
+    hash = JenkinsSmiHash.combine(hash, packageName.hashCode);
+    hash = JenkinsSmiHash.combine(hash, file.hashCode);
+    hash = JenkinsSmiHash.combine(hash, offset.hashCode);
+    return JenkinsSmiHash.finish(hash);
+  }
+
+  @override
+  String toString() => jsonEncoder.convert(toJson());
+}
+
 class PublishClosingLabelsParams implements ToJsonable {
   static const jsonHandler = LspJsonHandler(
       PublishClosingLabelsParams.canParse, PublishClosingLabelsParams.fromJson);
diff --git a/pkg/analysis_server/lib/src/analysis_server_abstract.dart b/pkg/analysis_server/lib/src/analysis_server_abstract.dart
index 3288d8b..109b9c7 100644
--- a/pkg/analysis_server/lib/src/analysis_server_abstract.dart
+++ b/pkg/analysis_server/lib/src/analysis_server_abstract.dart
@@ -288,12 +288,13 @@
         return null;
       }
 
-      var unitElementResult = await driver.getUnitElement(file);
-      if (unitElementResult == null) {
+      var unitElementResult = await driver.getUnitElement2(file);
+      if (unitElementResult is! UnitElementResult) {
         return null;
       }
 
-      var element = findElementByNameOffset(unitElementResult.element, offset);
+      var element = findElementByNameOffset(
+          (unitElementResult as UnitElementResult).element, offset);
       if (element != null) {
         return element;
       }
@@ -361,7 +362,8 @@
     }
 
     return driver
-        .getResult(path, sendCachedToStream: sendCachedToStream)
+        .getResult2(path, sendCachedToStream: sendCachedToStream)
+        .then((value) => value is ResolvedUnitResult ? value : null)
         .catchError((e, st) {
       instrumentationService.logException(e, st);
       return null;
diff --git a/pkg/analysis_server/lib/src/domain_analysis.dart b/pkg/analysis_server/lib/src/domain_analysis.dart
index 0cec373..c557341 100644
--- a/pkg/analysis_server/lib/src/domain_analysis.dart
+++ b/pkg/analysis_server/lib/src/domain_analysis.dart
@@ -61,7 +61,7 @@
 
     // Prepare the resolved units.
     var result = await server.getResolvedUnit(file);
-    if (result.state != ResultState.VALID) {
+    if (result == null) {
       server.sendResponse(Response.fileNotAnalyzed(request, file));
       return;
     }
diff --git a/pkg/analysis_server/lib/src/domains/execution/completion.dart b/pkg/analysis_server/lib/src/domains/execution/completion.dart
index 825dce0..97ab5bf 100644
--- a/pkg/analysis_server/lib/src/domains/execution/completion.dart
+++ b/pkg/analysis_server/lib/src/domains/execution/completion.dart
@@ -7,6 +7,7 @@
 import 'package:analysis_server/src/services/completion/completion_core.dart';
 import 'package:analysis_server/src/services/completion/completion_performance.dart';
 import 'package:analysis_server/src/services/completion/dart/completion_manager.dart';
+import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/file_system/overlay_file_system.dart';
 import 'package:analyzer/src/dart/analysis/driver.dart';
 import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
@@ -25,7 +26,11 @@
       this.code, this.offset, this.contextPath, this.contextOffset);
 
   Future<RuntimeCompletionResult> compute() async {
-    var contextResult = await analysisDriver.getResult(contextPath);
+    var contextResult = await analysisDriver.getResult2(contextPath);
+    if (contextResult is! ResolvedUnitResult) {
+      return RuntimeCompletionResult([], []);
+    }
+
     var session = contextResult.session;
 
     const codeMarker = '__code_\_';
@@ -59,8 +64,11 @@
     // Update the context file content to include the code being completed.
     // Then resolve it, and restore the file to its initial state.
     var targetResult = await _withContextFileContent(targetCode, () async {
-      return await analysisDriver.getResult(contextPath);
+      return await analysisDriver.getResult2(contextPath);
     });
+    if (targetResult is! ResolvedUnitResult) {
+      return RuntimeCompletionResult([], []);
+    }
 
     var contributor = DartCompletionManager(
         // dartdocDirectiveInfo: server.getDartdocDirectiveInfoFor(targetResult)
diff --git a/pkg/analysis_server/lib/src/edit/edit_domain.dart b/pkg/analysis_server/lib/src/edit/edit_domain.dart
index f93c1ad..fec4a3f 100644
--- a/pkg/analysis_server/lib/src/edit/edit_domain.dart
+++ b/pkg/analysis_server/lib/src/edit/edit_domain.dart
@@ -859,8 +859,8 @@
           }
           // try RENAME
           {
-            var renameRefactoring =
-                RenameRefactoring(refactoringWorkspace, resolvedUnit, element);
+            var renameRefactoring = RenameRefactoring.create(
+                refactoringWorkspace, resolvedUnit, element);
             if (renameRefactoring != null) {
               kinds.add(RefactoringKind.RENAME);
             }
@@ -1197,7 +1197,7 @@
               RenameRefactoring.getElementToRename(node, element);
 
           // do create the refactoring
-          refactoring = RenameRefactoring(
+          refactoring = RenameRefactoring.create(
               refactoringWorkspace, resolvedUnit, renameElement.element);
           feedback = RenameFeedback(
               renameElement.offset, renameElement.length, 'kind', 'oldName');
diff --git a/pkg/analysis_server/lib/src/lsp/constants.dart b/pkg/analysis_server/lib/src/lsp/constants.dart
index 952a5c5..4afc13e 100644
--- a/pkg/analysis_server/lib/src/lsp/constants.dart
+++ b/pkg/analysis_server/lib/src/lsp/constants.dart
@@ -24,25 +24,17 @@
 
 /// Set the characters that will cause the editor to automatically
 /// trigger completion.
-/// TODO(dantup): There are several characters that we want to conditionally
-/// allow to trigger completion, but they can only be added when the completion
-/// provider is able to handle them in context:
-///
-///    {   trigger if being typed in a string immediately after a $
-///    '   trigger if the opening quote for an import/export
-///    "   trigger if the opening quote for an import/export
-///    /   trigger if as part of a path in an import/export
-///    \   trigger if as part of a path in an import/export
-///    :   don't trigger when typing case expressions (`case x:`)
-///
-/// Additionally, we need to prefix `filterText` on completion items
-/// with spaces for those that can follow whitespace (eg. `foo` in
-/// `myArg: foo`) to ensure they're not filtered away when the user
-/// types space.
-///
-/// See https://github.com/Dart-Code/Dart-Code/blob/68d1cd271e88a785570257d487adbdec17abd6a3/src/providers/dart_completion_item_provider.ts#L36-L64
-/// for the VS Code implementation of this.
-const dartCompletionTriggerCharacters = ['.', '=', '(', r'$'];
+const dartCompletionTriggerCharacters = [
+  '.',
+  '=',
+  '(',
+  r'$',
+  '"',
+  "'",
+  '{',
+  '/',
+  ':'
+];
 
 /// Characters that refresh signature help only if it's already open on the client.
 const dartSignatureHelpRetriggerCharacters = <String>[','];
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
index 13a5890..4f68075 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
@@ -6,6 +6,7 @@
 
 import 'dart:math' as math;
 
+import 'package:analysis_server/lsp_protocol/protocol_custom_generated.dart';
 import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
 import 'package:analysis_server/lsp_protocol/protocol_special.dart';
 import 'package:analysis_server/protocol/protocol_generated.dart';
@@ -24,11 +25,13 @@
 import 'package:analysis_server/src/services/completion/yaml/pubspec_generator.dart';
 import 'package:analysis_server/src/services/completion/yaml/yaml_completion_generator.dart';
 import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/dart/ast/ast.dart' as ast;
 import 'package:analyzer/source/line_info.dart';
 import 'package:analyzer/src/services/available_declarations.dart';
 import 'package:analyzer/src/util/file_paths.dart' as file_paths;
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
+import 'package:analyzer_plugin/src/utilities/completion/completion_target.dart';
 
 class CompletionHandler
     extends MessageHandler<CompletionParams, List<CompletionItem>>
@@ -51,6 +54,7 @@
     final includeSuggestionSets =
         suggestFromUnimportedLibraries && server.clientCapabilities.applyEdit;
 
+    final triggerCharacter = params.context?.triggerCharacter;
     final pos = params.position;
     final path = pathOfDoc(params.textDocument);
     final unit = await path.mapResult(requireResolvedUnit);
@@ -75,6 +79,7 @@
           includeSuggestionSets,
           unit.result,
           offset,
+          triggerCharacter,
           token,
         );
       } else if (fileExtension == '.yaml') {
@@ -189,6 +194,7 @@
     bool includeSuggestionSets,
     ResolvedUnitResult unit,
     int offset,
+    String triggerCharacter,
     CancellationToken token,
   ) async {
     final performance = CompletionPerformance();
@@ -204,6 +210,13 @@
       final dartCompletionRequest = await DartCompletionRequestImpl.from(
           perf, completionRequest, directiveInfo);
 
+      if (triggerCharacter != null) {
+        if (!_triggerCharacterValid(
+            offset, triggerCharacter, dartCompletionRequest.target)) {
+          return success([]);
+        }
+      }
+
       Set<ElementKind> includedElementKinds;
       Set<String> includedElementNames;
       List<IncludedSuggestionRelevanceTag> includedSuggestionRelevanceTags;
@@ -410,6 +423,17 @@
             suggestions.replacementLength,
             includeCommitCharacters: false,
             completeFunctionCalls: false,
+            // Add on any completion-kind-specific resolution data that will be
+            // used during resolve() calls to provide additional information.
+            resolutionData: item.kind == CompletionSuggestionKind.PACKAGE_NAME
+                ? PubPackageCompletionItemResolutionInfo(
+                    file: path,
+                    offset: offset,
+                    // The completion for package names may contain a trailing
+                    // ': ' for convenience, so if it's there, trim it off.
+                    packageName: item.completion.split(':').first,
+                  )
+                : null,
           ),
         )
         .toList();
@@ -444,4 +468,37 @@
       );
     });
   }
+
+  /// Checks whether the given [triggerCharacter] is valid for [target].
+  ///
+  /// Some trigger characters are only valid in certain locations, for example
+  /// a single quote ' is valid to trigger completion after typing an import
+  /// statement, but not when terminating a string. The client has no context
+  /// and sends the requests unconditionally.
+  bool _triggerCharacterValid(
+      int offset, String triggerCharacter, CompletionTarget target) {
+    final node = target.containingNode;
+
+    switch (triggerCharacter) {
+      // For quotes, it's only valid if we're right after the opening quote of a
+      // directive.
+      case '"':
+      case "'":
+        return node is ast.SimpleStringLiteral &&
+            node.parent is ast.Directive &&
+            offset == node.contentsOffset;
+      // Braces only for starting interpolated expressions.
+      case '{':
+        return node is ast.InterpolationExpression &&
+            node.expression.offset == offset;
+      // Slashes only as path separators in directives.
+      case '/':
+        return node is ast.SimpleStringLiteral &&
+            node.parent is ast.Directive &&
+            offset >= node.contentsOffset &&
+            offset <= node.contentsEnd;
+    }
+
+    return true; // Any other trigger character can be handled always.
+  }
 }
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart
index 95e8259..c183d33 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart
@@ -42,6 +42,8 @@
 
     if (resolutionInfo is DartCompletionItemResolutionInfo) {
       return resolveDartCompletion(item, resolutionInfo, token);
+    } else if (resolutionInfo is PubPackageCompletionItemResolutionInfo) {
+      return resolvePubPackageCompletion(item, resolutionInfo, token);
     } else {
       return success(item);
     }
@@ -215,4 +217,40 @@
       null,
     );
   }
+
+  Future<ErrorOr<CompletionItem>> resolvePubPackageCompletion(
+    CompletionItem item,
+    PubPackageCompletionItemResolutionInfo data,
+    CancellationToken token,
+  ) async {
+    // Fetch details for this package. This may come from the cache or trigger
+    // a real web request to the Pub API.
+    final packageDetails =
+        await server.pubPackageService.packageDetails(data.packageName);
+
+    if (token.isCancellationRequested) {
+      return cancelled();
+    }
+
+    return success(CompletionItem(
+      label: item.label,
+      kind: item.kind,
+      tags: item.tags,
+      detail: item.detail,
+      documentation: packageDetails?.description != null
+          ? Either2<String, MarkupContent>.t1(packageDetails.description)
+          : null,
+      deprecated: item.deprecated,
+      preselect: item.preselect,
+      sortText: item.sortText,
+      filterText: item.filterText,
+      insertText: item.insertText,
+      insertTextFormat: item.insertTextFormat,
+      textEdit: item.textEdit,
+      additionalTextEdits: item.additionalTextEdits,
+      commitCharacters: item.commitCharacters,
+      command: item.command,
+      data: item.data,
+    ));
+  }
 }
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_rename.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_rename.dart
index 20f59c0..caa7c78 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_rename.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_rename.dart
@@ -43,7 +43,7 @@
 
       final refactorDetails =
           RenameRefactoring.getElementToRename(node, element);
-      final refactoring = RenameRefactoring(
+      final refactoring = RenameRefactoring.create(
           server.refactoringWorkspace, unit.result, refactorDetails.element);
       if (refactoring == null) {
         return success(null);
@@ -116,7 +116,7 @@
 
       final refactorDetails =
           RenameRefactoring.getElementToRename(node, element);
-      final refactoring = RenameRefactoring(
+      final refactoring = RenameRefactoring.create(
           server.refactoringWorkspace, unit.result, refactorDetails.element);
       if (refactoring == null) {
         return success(null);
diff --git a/pkg/analysis_server/lib/src/lsp/server_capabilities_computer.dart b/pkg/analysis_server/lib/src/lsp/server_capabilities_computer.dart
index 72b8dbc..ad0241d 100644
--- a/pkg/analysis_server/lib/src/lsp/server_capabilities_computer.dart
+++ b/pkg/analysis_server/lib/src/lsp/server_capabilities_computer.dart
@@ -124,8 +124,8 @@
 
   final LspAnalysisServer _server;
 
-  /// Map from method name to current registration data.
-  Map<String, Registration> currentRegistrations = {};
+  /// List of current registrations.
+  Set<Registration> currentRegistrations = {};
   var _lastRegistrationId = 0;
 
   ServerCapabilitiesComputer(this._server);
@@ -306,8 +306,10 @@
     // Completion is supported for some synchronised files that we don't _fully_
     // support (eg. YAML). If these gain support for things like hover, we may
     // wish to move them to fullySupprtedTypes but add an exclusion for formatting.
-    final completionSupportedTypes = {
-      ...fullySupportedTypes,
+    final completionSupportedTypesExcludingDart = {
+      // Dart is excluded here at it's registered separately with trigger/commit
+      // characters.
+      ...pluginTypes,
       pubspecFile,
       analysisOptionsFile,
       fixDataFile,
@@ -349,11 +351,13 @@
           syncKind: TextDocumentSyncKind.Incremental,
           documentSelector: synchronisedTypes),
     );
+    // Trigger and commit characters are specific to Dart, so register them
+    // separately to the others.
     register(
       dynamicRegistrations.completion,
       Method.textDocument_completion,
       CompletionRegistrationOptions(
-        documentSelector: completionSupportedTypes,
+        documentSelector: [dartFiles],
         triggerCharacters: dartCompletionTriggerCharacters,
         allCommitCharacters:
             previewCommitCharacters ? dartCompletionCommitCharacters : null,
@@ -361,6 +365,14 @@
       ),
     );
     register(
+      dynamicRegistrations.completion,
+      Method.textDocument_completion,
+      CompletionRegistrationOptions(
+        documentSelector: completionSupportedTypesExcludingDart,
+        resolveProvider: true,
+      ),
+    );
+    register(
       dynamicRegistrations.hover,
       Method.textDocument_hover,
       TextDocumentRegistrationOptions(documentSelector: fullySupportedTypes),
@@ -464,51 +476,52 @@
     await _applyRegistrations(registrations);
   }
 
-  Future<void> _applyRegistrations(List<Registration> registrations) async {
-    final newRegistrationsByMethod = {
-      for (final registration in registrations)
-        registration.method: registration
-    };
+  Future<void> _applyRegistrations(List<Registration> newRegistrations) async {
+    // Compute a diff of old and new registrations to send the unregister or
+    // another register request. We compare registrations by their methods and
+    // the hashcode of their registration options to allow for multiple
+    // registrations of a single method.
 
-    final additionalRegistrations = List.of(registrations);
-    final removedRegistrations = <Unregistration>[];
+    String _registrationHash(Registration registration) =>
+        '${registration.method}${registration.registerOptions.hashCode}';
 
-    // compute a diff of old and new registrations to send the unregister or
-    // another register request. We assume that we'll only ever have one
-    // registration per LSP method name.
-    for (final entry in currentRegistrations.entries) {
-      final method = entry.key;
-      final registration = entry.value;
+    final newRegistrationsMap = Map.fromEntries(
+        newRegistrations.map((r) => MapEntry(r, _registrationHash(r))));
+    final newRegistrationsJsons = newRegistrationsMap.values.toSet();
+    final currentRegistrationsMap = Map.fromEntries(
+        currentRegistrations.map((r) => MapEntry(r, _registrationHash(r))));
+    final currentRegistrationJsons = currentRegistrationsMap.values.toSet();
 
-      final newRegistrationForMethod = newRegistrationsByMethod[method];
-      final entryRemovedOrChanged = newRegistrationForMethod?.registerOptions !=
-          registration.registerOptions;
+    final registrationsToAdd = newRegistrationsMap.entries
+        .where((entry) => !currentRegistrationJsons.contains(entry.value))
+        .map((entry) => entry.key)
+        .toList();
 
-      if (entryRemovedOrChanged) {
-        removedRegistrations.add(
-            Unregistration(id: registration.id, method: registration.method));
-      } else {
-        // Replace the registration in our new set with the original registration
-        // so that we retain the original ID sent to the client (otherwise we
-        // will try to unregister using an ID the client was never sent).
-        newRegistrationsByMethod[method] = registration;
-        additionalRegistrations.remove(newRegistrationForMethod);
-      }
-    }
+    final registrationsToRemove = currentRegistrationsMap.entries
+        .where((entry) => !newRegistrationsJsons.contains(entry.value))
+        .map((entry) => entry.key)
+        .toList();
 
-    currentRegistrations = newRegistrationsByMethod;
+    // Update the current list before we start sending requests since we
+    // go async.
+    currentRegistrations
+      ..removeAll(registrationsToRemove)
+      ..addAll(registrationsToAdd);
 
-    if (removedRegistrations.isNotEmpty) {
+    if (registrationsToRemove.isNotEmpty) {
+      final unregistrations = registrationsToRemove
+          .map((r) => Unregistration(id: r.id, method: r.method))
+          .toList();
       await _server.sendRequest(Method.client_unregisterCapability,
-          UnregistrationParams(unregisterations: removedRegistrations));
+          UnregistrationParams(unregisterations: unregistrations));
     }
 
     // Only send the registration request if we have at least one (since
     // otherwise we don't know that the client supports registerCapability).
-    if (additionalRegistrations.isNotEmpty) {
+    if (registrationsToAdd.isNotEmpty) {
       final registrationResponse = await _server.sendRequest(
         Method.client_registerCapability,
-        RegistrationParams(registrations: additionalRegistrations),
+        RegistrationParams(registrations: registrationsToAdd),
       );
 
       if (registrationResponse.error != null) {
diff --git a/pkg/analysis_server/lib/src/search/element_references.dart b/pkg/analysis_server/lib/src/search/element_references.dart
index e2bf228..f843776 100644
--- a/pkg/analysis_server/lib/src/search/element_references.dart
+++ b/pkg/analysis_server/lib/src/search/element_references.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analysis_server/src/protocol_server.dart'
     show SearchResult, newSearchResult_fromMatch;
 import 'package:analysis_server/src/services/search/hierarchy.dart';
diff --git a/pkg/analysis_server/lib/src/search/type_hierarchy.dart b/pkg/analysis_server/lib/src/search/type_hierarchy.dart
index 90c3866b..422be2e 100644
--- a/pkg/analysis_server/lib/src/search/type_hierarchy.dart
+++ b/pkg/analysis_server/lib/src/search/type_hierarchy.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'dart:collection';
 
 import 'package:analysis_server/src/protocol_server.dart'
@@ -18,23 +16,23 @@
   final SearchEngine _searchEngine;
 
   final Element _pivotElement;
-  LibraryElement _pivotLibrary;
-  ElementKind _pivotKind;
-  String _pivotName;
-  bool _pivotFieldFinal;
-  ClassElement _pivotClass;
+  final LibraryElement _pivotLibrary;
+  final ElementKind _pivotKind;
+  final String? _pivotName;
+  late bool _pivotFieldFinal;
+  ClassElement? _pivotClass;
 
   final List<TypeHierarchyItem> _items = <TypeHierarchyItem>[];
   final List<ClassElement> _itemClassElements = <ClassElement>[];
   final Map<Element, TypeHierarchyItem> _elementItemMap =
       HashMap<Element, TypeHierarchyItem>();
 
-  TypeHierarchyComputer(this._searchEngine, this._pivotElement) {
-    _pivotLibrary = _pivotElement.library;
-    _pivotKind = _pivotElement.kind;
-    _pivotName = _pivotElement.name;
+  TypeHierarchyComputer(this._searchEngine, this._pivotElement)
+      : _pivotLibrary = _pivotElement.library!,
+        _pivotKind = _pivotElement.kind,
+        _pivotName = _pivotElement.name {
     // try to find enclosing ClassElement
-    var element = _pivotElement;
+    Element? element = _pivotElement;
     if (_pivotElement is FieldElement) {
       _pivotFieldFinal = (_pivotElement as FieldElement).isFinal;
       element = _pivotElement.enclosingElement;
@@ -48,19 +46,21 @@
   }
 
   /// Returns the computed type hierarchy, maybe `null`.
-  Future<List<TypeHierarchyItem>> compute() async {
-    if (_pivotClass != null) {
-      _createSuperItem(_pivotClass, null);
-      await _createSubclasses(_items[0], 0, _pivotClass);
+  Future<List<TypeHierarchyItem>?> compute() async {
+    var pivotClass = _pivotClass;
+    if (pivotClass != null) {
+      _createSuperItem(pivotClass, null);
+      await _createSubclasses(_items[0], 0, pivotClass);
       return _items;
     }
     return null;
   }
 
   /// Returns the computed super type only type hierarchy, maybe `null`.
-  List<TypeHierarchyItem> computeSuper() {
-    if (_pivotClass != null) {
-      _createSuperItem(_pivotClass, null);
+  List<TypeHierarchyItem>? computeSuper() {
+    var pivotClass = _pivotClass;
+    if (pivotClass != null) {
+      _createSuperItem(pivotClass, null);
       return _items;
     }
     return null;
@@ -103,16 +103,17 @@
   }
 
   int _createSuperItem(
-      ClassElement classElement, List<DartType> typeArguments) {
+      ClassElement classElement, List<DartType>? typeArguments) {
     // check for recursion
-    var item = _elementItemMap[classElement];
-    if (item != null) {
-      return _items.indexOf(item);
+    var cachedItem = _elementItemMap[classElement];
+    if (cachedItem != null) {
+      return _items.indexOf(cachedItem);
     }
     // create an empty item now
+    TypeHierarchyItem item;
     int itemId;
     {
-      String displayName;
+      String? displayName;
       if (typeArguments != null && typeArguments.isNotEmpty) {
         var typeArgumentsStr = typeArguments
             .map((type) => type.getDisplayString(withNullability: false))
@@ -153,19 +154,23 @@
     return itemId;
   }
 
-  ExecutableElement _findMemberElement(ClassElement clazz) {
-    ExecutableElement result;
+  ExecutableElement? _findMemberElement(ClassElement clazz) {
+    var pivotName = _pivotName;
+    if (pivotName == null) {
+      return null;
+    }
+    ExecutableElement? result;
     // try to find in the class itself
     if (_pivotKind == ElementKind.METHOD) {
-      result = clazz.getMethod(_pivotName);
+      result = clazz.getMethod(pivotName);
     } else if (_pivotKind == ElementKind.GETTER) {
-      result = clazz.getGetter(_pivotName);
+      result = clazz.getGetter(pivotName);
     } else if (_pivotKind == ElementKind.SETTER) {
-      result = clazz.getSetter(_pivotName);
+      result = clazz.getSetter(pivotName);
     } else if (_pivotKind == ElementKind.FIELD) {
-      result = clazz.getGetter(_pivotName);
+      result = clazz.getGetter(pivotName);
       if (result == null && !_pivotFieldFinal) {
-        result = clazz.getSetter(_pivotName);
+        result = clazz.getSetter(pivotName);
       }
     }
     if (result != null && result.isAccessibleIn(_pivotLibrary)) {
@@ -175,15 +180,15 @@
     for (var mixin in clazz.mixins.reversed) {
       var mixinElement = mixin.element;
       if (_pivotKind == ElementKind.METHOD) {
-        result = mixinElement.lookUpMethod(_pivotName, _pivotLibrary);
+        result = mixinElement.lookUpMethod(pivotName, _pivotLibrary);
       } else if (_pivotKind == ElementKind.GETTER) {
-        result = mixinElement.lookUpGetter(_pivotName, _pivotLibrary);
+        result = mixinElement.lookUpGetter(pivotName, _pivotLibrary);
       } else if (_pivotKind == ElementKind.SETTER) {
-        result = mixinElement.lookUpSetter(_pivotName, _pivotLibrary);
+        result = mixinElement.lookUpSetter(pivotName, _pivotLibrary);
       } else if (_pivotKind == ElementKind.FIELD) {
-        result = mixinElement.lookUpGetter(_pivotName, _pivotLibrary);
+        result = mixinElement.lookUpGetter(pivotName, _pivotLibrary);
         if (result == null && !_pivotFieldFinal) {
-          result = mixinElement.lookUpSetter(_pivotName, _pivotLibrary);
+          result = mixinElement.lookUpSetter(pivotName, _pivotLibrary);
         }
       }
       if (result == _pivotElement) {
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart b/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
index 7c20027..66989f9 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
@@ -368,8 +368,8 @@
     var entity = target.entity;
 
     if (entity is Token) {
-      var prev = entity.previous!;
-      if (prev.end == offset && prev.isKeywordOrIdentifier) {
+      var prev = entity.previous;
+      if (prev != null && prev.end == offset && prev.isKeywordOrIdentifier) {
         return prev.lexeme;
       }
     }
diff --git a/pkg/analysis_server/lib/src/services/completion/yaml/pubspec_generator.dart b/pkg/analysis_server/lib/src/services/completion/yaml/pubspec_generator.dart
index a1e73c3..724bb6f 100644
--- a/pkg/analysis_server/lib/src/services/completion/yaml/pubspec_generator.dart
+++ b/pkg/analysis_server/lib/src/services/completion/yaml/pubspec_generator.dart
@@ -9,10 +9,13 @@
 import 'package:analyzer/file_system/file_system.dart';
 
 /// An object that represents the location of a package name.
-class PubPackageNameProducer extends Producer {
+class PubPackageNameProducer extends KeyValueProducer {
   const PubPackageNameProducer();
 
   @override
+  Producer producerForKey(String key) => PubPackageVersionProducer(key);
+
+  @override
   Iterable<CompletionSuggestion> suggestions(
       YamlCompletionRequest request) sync* {
     final cachedPackages = request.pubPackageService?.cachedPackages;
@@ -24,6 +27,32 @@
   }
 }
 
+/// An object that represents the location of the version number for a pub
+/// package.
+class PubPackageVersionProducer extends Producer {
+  final String package;
+
+  const PubPackageVersionProducer(this.package);
+
+  @override
+  Iterable<CompletionSuggestion> suggestions(
+      YamlCompletionRequest request) sync* {
+    // TOOD(dantup): Consider supporting async completion requests so this
+    // could call packageDetails() (with a short timeout, and pub retries
+    // disabled). A user that explicitly invokes completion in the location
+    // of a version may be prepared to wait a short period for a web request
+    // to get completion versions (this is also the only way for non-LSP
+    // clients to get them, since there are no resolve calls).
+    //
+    // Supporting this will require making the completion async further up.
+    final details = request.pubPackageService?.cachedPackageDetails(package);
+    final version = details?.latestVersion;
+    if (version != null) {
+      yield identifier('^$version');
+    }
+  }
+}
+
 /// A completion generator that can produce completion suggestions for pubspec
 /// files.
 class PubspecGenerator extends YamlCompletionGenerator {
diff --git a/pkg/analysis_server/lib/src/services/completion/yaml/yaml_completion_generator.dart b/pkg/analysis_server/lib/src/services/completion/yaml/yaml_completion_generator.dart
index 724475f..618ba40 100644
--- a/pkg/analysis_server/lib/src/services/completion/yaml/yaml_completion_generator.dart
+++ b/pkg/analysis_server/lib/src/services/completion/yaml/yaml_completion_generator.dart
@@ -127,9 +127,15 @@
     for (var i = 0; i < path.length - 1; i++) {
       var node = path[i];
       if (node is YamlMap && producer is KeyValueProducer) {
+        // Value producers are based on keys, so try to locate the key for the
+        // value that was next in the path.
         var key = node.keyAtValue(path[i + 1]);
         if (key is YamlScalar) {
           producer = producer.producerForKey(key.value);
+          // Otherwise, if the item next in the path was a key itself, use the
+          // current producer to provide completion for the key.
+        } else if (node.nodes.containsKey(path[i + 1])) {
+          return producer;
         } else {
           return null;
         }
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/create_method.dart b/pkg/analysis_server/lib/src/services/correction/dart/create_method.dart
index a697fff..a76ce2a 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/create_method.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/create_method.dart
@@ -5,6 +5,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/correction/util.dart';
+import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart' show Position;
@@ -147,7 +148,10 @@
       // use different utils
       var targetPath = targetClassElement.source.fullName;
       var targetResolveResult =
-          await resolvedResult.session.getResolvedUnit(targetPath);
+          await resolvedResult.session.getResolvedUnit2(targetPath);
+      if (targetResolveResult is! ResolvedUnitResult) {
+        return;
+      }
       utils = CorrectionUtils(targetResolveResult);
     }
     if (targetElement == null || targetNode == null) {
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/wrap_in_text.dart b/pkg/analysis_server/lib/src/services/correction/dart/wrap_in_text.dart
index 49fb849..53c87c90 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/wrap_in_text.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/wrap_in_text.dart
@@ -48,15 +48,17 @@
   static WrapInText newInstance() => WrapInText();
 
   static _Context? _extractContextInformation(AstNode node) {
-    if (node is NamedExpression) {
-      var expression = node.expression;
-      if (expression.typeOrThrow.isDartCoreString) {
-        var parameterElement = node.name.label.staticElement;
-        if (parameterElement is ParameterElement) {
-          return _Context(
-            stringExpression: expression,
-            parameterElement: parameterElement,
-          );
+    if (node is Expression) {
+      var parent = node.parent;
+      if (parent is NamedExpression) {
+        if (node.typeOrThrow.isDartCoreString) {
+          var parameterElement = parent.name.label.staticElement;
+          if (parameterElement is ParameterElement) {
+            return _Context(
+              stringExpression: node,
+              parameterElement: parameterElement,
+            );
+          }
         }
       }
     }
diff --git a/pkg/analysis_server/lib/src/services/correction/util.dart b/pkg/analysis_server/lib/src/services/correction/util.dart
index 2f4bec9..c77cc97 100644
--- a/pkg/analysis_server/lib/src/services/correction/util.dart
+++ b/pkg/analysis_server/lib/src/services/correction/util.dart
@@ -35,7 +35,12 @@
 Future<void> addLibraryImports(AnalysisSession session, SourceChange change,
     LibraryElement targetLibrary, Set<Source> libraries) async {
   var libraryPath = targetLibrary.source.fullName;
-  var resolveResult = await session.getResolvedUnit(libraryPath);
+
+  var resolveResult = await session.getResolvedUnit2(libraryPath);
+  if (resolveResult is! ResolvedUnitResult) {
+    return;
+  }
+
   var libUtils = CorrectionUtils(resolveResult);
   var eol = libUtils.endOfLine;
   // Prepare information about existing imports.
diff --git a/pkg/analysis_server/lib/src/services/pub/pub_api.dart b/pkg/analysis_server/lib/src/services/pub/pub_api.dart
index e52dc50..d89d165 100644
--- a/pkg/analysis_server/lib/src/services/pub/pub_api.dart
+++ b/pkg/analysis_server/lib/src/services/pub/pub_api.dart
@@ -17,6 +17,7 @@
 /// Failed requests will automatically be retried.
 class PubApi {
   static const packageNameListPath = '/api/package-name-completion-data';
+  static const packageInfoPath = '/api/packages';
 
   /// Maximum number of retries if requests fail.
   static const maxFailedRequests = 5;
@@ -66,6 +67,28 @@
     httpClient.close();
   }
 
+  /// Fetches package details from the Pub API.
+  ///
+  /// Failed requests will be retried a number of times. If no successful response
+  /// is received, will return null.
+  Future<PubApiPackageDetails?> packageInfo(String packageName) async {
+    final json = await _getJson('$_pubHostedUrl$packageInfoPath/$packageName');
+    if (json == null) {
+      return null;
+    }
+
+    final latest = json['latest'] as Map<String, Object?>?;
+    if (latest == null) {
+      return null;
+    }
+
+    final pubspec = latest['pubspec'] as Map<String, Object?>?;
+    final description =
+        pubspec != null ? pubspec['description'] as String? : null;
+    final version = latest['version'] as String?;
+    return PubApiPackageDetails(packageName, description, version);
+  }
+
   /// Calls a pub API and decodes the resulting JSON.
   ///
   /// Automatically retries the request for specific types of failures after
@@ -132,6 +155,14 @@
   PubApiPackage(this.packageName);
 }
 
+class PubApiPackageDetails {
+  final String packageName;
+  final String? description;
+  final String? latestVersion;
+
+  PubApiPackageDetails(this.packageName, this.description, this.latestVersion);
+}
+
 /// A wrapper over a package:http Client that does not pass on calls to [close].
 ///
 /// This is used to prevent the server closing a client that may be provided to
diff --git a/pkg/analysis_server/lib/src/services/pub/pub_package_service.dart b/pkg/analysis_server/lib/src/services/pub/pub_package_service.dart
index e958542..815711f 100644
--- a/pkg/analysis_server/lib/src/services/pub/pub_package_service.dart
+++ b/pkg/analysis_server/lib/src/services/pub/pub_package_service.dart
@@ -14,8 +14,15 @@
 /// Information about Pub packages that can be converted to/from JSON and
 /// cached to disk.
 class PackageDetailsCache {
-  static const cacheVersion = 2;
+  static const cacheVersion = 3;
   static const maxCacheAge = Duration(hours: 18);
+  static const maxPackageDetailsRequestsInFlight = 5;
+
+  /// Requests to write the cache from fetching packge details will be debounced
+  /// by this duration to prevent many writes while the user may be cursoring
+  /// though completion requests that will trigger fetching descriptions/versions.
+  static const _writeCacheDebounceDuration = Duration(seconds: 3);
+
   final Map<String, PubPackage> packages;
   DateTime lastUpdatedUtc;
 
@@ -71,7 +78,7 @@
       if (nameJson is! String) {
         return null;
       }
-      packages.add(PubPackage.fromJson(nameJson));
+      packages.add(PubPackage.fromJson(packageJson));
     }
 
     final packageMap = Map.fromEntries(
@@ -96,8 +103,18 @@
 /// Information about a single Pub package.
 class PubPackage {
   String packageName;
+  String? description;
+  String? latestVersion;
 
-  PubPackage.fromJson(this.packageName);
+  PubPackage.fromDetails(PubApiPackageDetails package)
+      : packageName = package.packageName,
+        description = package.description,
+        latestVersion = package.latestVersion;
+
+  PubPackage.fromJson(Map<String, Object?> json)
+      : packageName = json['packageName'] as String,
+        description = json['description'] as String?,
+        latestVersion = json['latestVersion'] as String?;
 
   PubPackage.fromName(PubApiPackage package)
       : packageName = package.packageName;
@@ -105,6 +122,8 @@
   Map<String, Object> toJson() {
     return {
       'packageName': packageName,
+      if (description != null) 'description': description!,
+      if (latestVersion != null) 'latestVersion': latestVersion!,
     };
   }
 }
@@ -115,7 +134,8 @@
 class PubPackageService {
   final InstrumentationService _instrumentationService;
   final PubApi _api;
-  Timer? _nextRequestTimer;
+  Timer? _nextPackageNameListRequestTimer;
+  Timer? _nextWriteDiskCacheTimer;
 
   /// [ResourceProvider] used for caching. This should generally be a
   /// [PhysicalResourceProvider] outside of tests.
@@ -126,6 +146,8 @@
   @visibleForTesting
   PackageDetailsCache? packageCache;
 
+  int _packageDetailsRequestsInFlight = 0;
+
   PubPackageService(
       this._instrumentationService, this.cacheResourceProvider, this._api);
 
@@ -133,7 +155,7 @@
   List<PubPackage> get cachedPackages =>
       packageCache?.packages.values.toList() ?? [];
 
-  bool get isRunning => _nextRequestTimer != null;
+  bool get isRunning => _nextPackageNameListRequestTimer != null;
 
   @visibleForTesting
   File get packageCacheFile {
@@ -153,10 +175,48 @@
     }
 
     // If there is no queued request, initialize one when the current cache expires.
-    _nextRequestTimer ??=
+    _nextPackageNameListRequestTimer ??=
         Timer(packageCache.cacheTimeRemaining, _fetchFromServer);
   }
 
+  /// Gets the cached package details for package [packageName].
+  ///
+  /// Returns null if no package details are cached.
+  PubPackage? cachedPackageDetails(String packageName) =>
+      packageCache?.packages[packageName];
+
+  /// Gets package details for package [packageName].
+  ///
+  /// If the package details are not cached, will call the Pub API and cache
+  /// the result. Results are cached for the same period as the main package
+  /// list cache - that is, when the package list cache expires, all cached
+  /// package details will go with it.
+  Future<PubPackage?> packageDetails(String packageName) async {
+    var packageData = packageCache?.packages[packageName];
+    // If we don't have the version for this package, we don't have its full details.
+    if (packageData?.latestVersion == null &&
+        // Limit the number of package details requests that can be in-flight at
+        // once since an editor may send many of these requests as the user
+        // cursors through the results (a good editor will cancel the resolve
+        // requests, but we may have already started the requests synchronously
+        // before handling a cancellation).
+        _packageDetailsRequestsInFlight <=
+            PackageDetailsCache.maxPackageDetailsRequestsInFlight) {
+      _packageDetailsRequestsInFlight++;
+      try {
+        final details = await _api.packageInfo(packageName);
+        if (details != null) {
+          packageData = PubPackage.fromDetails(details);
+          packageCache?.packages[packageName] = packageData;
+          _writeDiskCacheDebounced();
+        }
+      } finally {
+        _packageDetailsRequestsInFlight--;
+      }
+    }
+    return packageData;
+  }
+
   @visibleForTesting
   PackageDetailsCache? readDiskCache() {
     final file = packageCacheFile;
@@ -175,10 +235,14 @@
     }
   }
 
-  void shutdown() => _nextRequestTimer?.cancel();
+  void shutdown() => _nextPackageNameListRequestTimer?.cancel();
 
   @visibleForTesting
-  void writeDiskCache(PackageDetailsCache cache) {
+  void writeDiskCache([PackageDetailsCache? cache]) {
+    cache ??= packageCache;
+    if (cache == null) {
+      return;
+    }
     final file = packageCacheFile;
     file.writeAsStringSync(jsonEncode(cache.toJson()));
   }
@@ -194,12 +258,21 @@
 
       final packageCache = PackageDetailsCache.fromApiResults(packages);
       this.packageCache = packageCache;
-      writeDiskCache(packageCache);
+      writeDiskCache();
     } catch (e) {
       _instrumentationService.logError('Failed to fetch packages from Pub: $e');
     } finally {
-      _nextRequestTimer =
+      _nextPackageNameListRequestTimer =
           Timer(PackageDetailsCache.maxCacheAge, _fetchFromServer);
     }
   }
+
+  /// Writes the package cache to disk after
+  /// [PackageDetailsCache._writeCacheDebounceDuration] has elapsed, restarting
+  /// the timer each time this method is called.
+  void _writeDiskCacheDebounced() {
+    _nextWriteDiskCacheTimer?.cancel();
+    _nextWriteDiskCacheTimer =
+        Timer(PackageDetailsCache._writeCacheDebounceDuration, writeDiskCache);
+  }
 }
diff --git a/pkg/analysis_server/lib/src/services/refactoring/convert_getter_to_method.dart b/pkg/analysis_server/lib/src/services/refactoring/convert_getter_to_method.dart
index b2f9f13..565b9a8 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/convert_getter_to_method.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/convert_getter_to_method.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analysis_server/src/protocol_server.dart' hide Element;
 import 'package:analysis_server/src/services/correction/status.dart';
 import 'package:analysis_server/src/services/refactoring/refactoring.dart';
@@ -24,7 +22,7 @@
   final AnalysisSession session;
   final PropertyAccessorElement element;
 
-  SourceChange change;
+  late SourceChange change;
 
   ConvertGetterToMethodRefactoringImpl(
       this.searchEngine, this.session, this.element);
@@ -53,13 +51,13 @@
       await _updateElementReferences(element);
     }
     // method
-    if (element.enclosingElement is ClassElement) {
-      FieldElement field = element.variable;
+    var field = element.variable;
+    if (field is FieldElement && field.enclosingElement is ClassElement) {
       var elements = await getHierarchyMembers(searchEngine, field);
       await Future.forEach(elements, (ClassMemberElement member) async {
         if (member is FieldElement) {
           var getter = member.getter;
-          if (!getter.isSynthetic) {
+          if (getter != null && !getter.isSynthetic) {
             await _updateElementDeclaration(getter);
             return _updateElementReferences(getter);
           }
@@ -81,11 +79,11 @@
   Future<void> _updateElementDeclaration(
       PropertyAccessorElement element) async {
     // prepare "get" keyword
-    Token getKeyword;
+    Token? getKeyword;
     {
       var sessionHelper = AnalysisSessionHelper(session);
       var result = await sessionHelper.getElementDeclaration(element);
-      var declaration = result.node;
+      var declaration = result?.node;
       if (declaration is MethodDeclaration) {
         getKeyword = declaration.propertyKeyword;
       } else if (declaration is FunctionDeclaration) {
diff --git a/pkg/analysis_server/lib/src/services/refactoring/convert_method_to_getter.dart b/pkg/analysis_server/lib/src/services/refactoring/convert_method_to_getter.dart
index 7fb6210..95ce6f0 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/convert_method_to_getter.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/convert_method_to_getter.dart
@@ -2,14 +2,13 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analysis_server/src/protocol_server.dart' hide Element;
 import 'package:analysis_server/src/services/correction/status.dart';
 import 'package:analysis_server/src/services/refactoring/refactoring.dart';
 import 'package:analysis_server/src/services/refactoring/refactoring_internal.dart';
 import 'package:analysis_server/src/services/search/hierarchy.dart';
 import 'package:analysis_server/src/services/search/search_engine.dart';
+import 'package:analyzer/dart/analysis/session.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/src/dart/analysis/session_helper.dart';
@@ -23,10 +22,11 @@
   final AnalysisSessionHelper sessionHelper;
   final ExecutableElement element;
 
-  SourceChange change;
+  late SourceChange change;
 
-  ConvertMethodToGetterRefactoringImpl(this.searchEngine, this.element)
-      : sessionHelper = AnalysisSessionHelper(element.session);
+  ConvertMethodToGetterRefactoringImpl(
+      this.searchEngine, AnalysisSession session, this.element)
+      : sessionHelper = AnalysisSessionHelper(session);
 
   @override
   String get refactoringName => 'Convert Method To Getter';
@@ -50,7 +50,7 @@
           'Only class methods or top-level functions can be converted to getters.');
     }
     // returns a value
-    if (element.returnType != null && element.returnType.isVoid) {
+    if (element.returnType.isVoid) {
       return RefactoringStatus.fatal(
           'Cannot convert ${element.kind.displayName} returning void.');
     }
@@ -67,14 +67,14 @@
   Future<SourceChange> createChange() async {
     change = SourceChange(refactoringName);
     // FunctionElement
+    final element = this.element;
     if (element is FunctionElement) {
       await _updateElementDeclaration(element);
       await _updateElementReferences(element);
     }
     // MethodElement
     if (element is MethodElement) {
-      MethodElement method = element;
-      var elements = await getHierarchyMembers(searchEngine, method);
+      var elements = await getHierarchyMembers(searchEngine, element);
       await Future.forEach(elements, (Element element) async {
         await _updateElementDeclaration(element);
         return _updateElementReferences(element);
@@ -86,10 +86,10 @@
 
   Future<void> _updateElementDeclaration(Element element) async {
     // prepare parameters
-    FormalParameterList parameters;
+    FormalParameterList? parameters;
     {
       var result = await sessionHelper.getElementDeclaration(element);
-      var declaration = result.node;
+      var declaration = result?.node;
       if (declaration is MethodDeclaration) {
         parameters = declaration.parameters;
       } else if (declaration is FunctionDeclaration) {
@@ -98,6 +98,9 @@
         return;
       }
     }
+    if (parameters == null) {
+      return;
+    }
     // insert "get "
     {
       var edit = SourceEdit(element.nameOffset, 0, 'get ');
@@ -117,13 +120,13 @@
       var refElement = reference.element;
       var refRange = reference.range;
       // prepare invocation
-      MethodInvocation invocation;
+      MethodInvocation? invocation;
       {
         var resolvedUnit =
             await sessionHelper.getResolvedUnitByElement(refElement);
-        var refUnit = resolvedUnit.unit;
+        var refUnit = resolvedUnit?.unit;
         var refNode = NodeLocator(refRange.offset).searchWithin(refUnit);
-        invocation = refNode.thisOrAncestorOfType<MethodInvocation>();
+        invocation = refNode?.thisOrAncestorOfType<MethodInvocation>();
       }
       // we need invocation
       if (invocation != null) {
diff --git a/pkg/analysis_server/lib/src/services/refactoring/extract_local.dart b/pkg/analysis_server/lib/src/services/refactoring/extract_local.dart
index e6ffa45..16ca48b 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/extract_local.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/extract_local.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'dart:collection';
 
 import 'package:analysis_server/src/protocol_server.dart' hide Element;
@@ -34,10 +32,10 @@
   final ResolvedUnitResult resolveResult;
   final int selectionOffset;
   final int selectionLength;
-  SourceRange selectionRange;
-  CorrectionUtils utils;
+  late SourceRange selectionRange;
+  late CorrectionUtils utils;
 
-  String name;
+  late String name;
   bool extractAll = true;
   @override
   final List<int> coveringExpressionOffsets = <int>[];
@@ -50,9 +48,9 @@
   @override
   final List<int> lengths = <int>[];
 
-  FunctionBody coveringFunctionBody;
-  Expression singleExpression;
-  String stringLiteralPart;
+  FunctionBody? coveringFunctionBody;
+  Expression? singleExpression;
+  String? stringLiteralPart;
   final List<SourceRange> occurrences = <SourceRange>[];
   final Map<Element, int> elementIds = <Element, int>{};
   Set<String> excludedVariableNames = <String>{};
@@ -63,14 +61,14 @@
     utils = CorrectionUtils(resolveResult);
   }
 
-  String get file => resolveResult.path;
+  String get file => resolveResult.path!;
 
   @override
   String get refactoringName => 'Extract Local Variable';
 
-  CompilationUnit get unit => resolveResult.unit;
+  CompilationUnit get unit => resolveResult.unit!;
 
-  CompilationUnitElement get unitElement => unit.declaredElement;
+  CompilationUnitElement get unitElement => unit.declaredElement!;
 
   String get _declarationKeyword {
     if (_isPartOfConstantExpression(singleExpression)) {
@@ -132,7 +130,9 @@
     occurrences.sort((a, b) => a.offset - b.offset);
     // If the whole expression of a statement is selected, like '1 + 2',
     // then convert it into a variable declaration statement.
-    if (singleExpression?.parent is ExpressionStatement &&
+    var singleExpression = this.singleExpression;
+    if (singleExpression != null &&
+        singleExpression.parent is ExpressionStatement &&
         occurrences.length == 1) {
       var keyword = _declarationKeyword;
       var declarationSource = '$keyword $name = ';
@@ -173,7 +173,7 @@
         addPosition(edit.offset + nameOffsetInDeclarationCode);
         occurrencesShift = edit.replacement.length;
       } else if (target is ExpressionFunctionBody) {
-        var prefix = utils.getNodePrefix(target.parent);
+        var prefix = utils.getNodePrefix(target.parent!);
         var indent = utils.getIndent(1);
         var expr = target.expression;
         {
@@ -231,7 +231,7 @@
       return RefactoringStatus.fatal(
           'The selection offset must be greater than zero.');
     }
-    if (selectionOffset + selectionLength >= resolveResult.content.length) {
+    if (selectionOffset + selectionLength >= resolveResult.content!.length) {
       return RefactoringStatus.fatal(
           'The selection end offset must be less then the length of the file.');
     }
@@ -300,9 +300,7 @@
       if (node is MethodInvocation) {
         var invocation = node;
         var element = invocation.methodName.staticElement;
-        if (element is ExecutableElement &&
-            element.returnType != null &&
-            element.returnType.isVoid) {
+        if (element is ExecutableElement && element.returnType.isVoid) {
           if (singleExpression == null) {
             return RefactoringStatus.fatal(
                 'Cannot extract the void expression.',
@@ -338,7 +336,7 @@
     }
     // single node selected
     if (singleExpression != null) {
-      selectionRange = range.node(singleExpression);
+      selectionRange = range.node(singleExpression!);
       return RefactoringStatus();
     }
     // invalid selection
@@ -348,7 +346,7 @@
 
   /// Return an unique identifier for the given [Element], or `null` if
   /// [element] is `null`.
-  int _encodeElement(Element element) {
+  int? _encodeElement(Element? element) {
     if (element == null) {
       return null;
     }
@@ -367,10 +365,6 @@
   /// there are multiple variables with the same name are declared in the
   /// function we are searching occurrences in.
   String _encodeExpressionTokens(Expression expr, List<Token> tokens) {
-    // no expression, i.e. a part of a string
-    if (expr == null) {
-      return tokens.join(_TOKEN_SEPARATOR);
-    }
     // prepare Token -> LocalElement map
     Map<Token, Element> map = HashMap<Token, Element>(
         equals: (Token a, Token b) => a.lexeme == b.lexeme,
@@ -395,7 +389,7 @@
 
   /// Return the [AstNode] to defined the variable before.
   /// It should be accessible by all the given [occurrences].
-  AstNode _findDeclarationTarget(List<SourceRange> occurrences) {
+  AstNode? _findDeclarationTarget(List<SourceRange> occurrences) {
     var nodes = _findNodes(occurrences);
     var commonParent = getNearestCommonAncestor(nodes);
     // Block
@@ -405,14 +399,18 @@
       return firstParents[commonIndex + 1];
     }
     // ExpressionFunctionBody
-    AstNode expressionBody = _getEnclosingExpressionBody(commonParent);
+    var expressionBody = _getEnclosingExpressionBody(commonParent);
     if (expressionBody != null) {
       return expressionBody;
     }
     // single Statement
-    AstNode target = commonParent.thisOrAncestorOfType<Statement>();
-    while (target.parent is! Block) {
-      target = target.parent;
+    AstNode? target = commonParent?.thisOrAncestorOfType<Statement>();
+    while (target != null) {
+      var parent = target.parent;
+      if (parent is Block) {
+        break;
+      }
+      target = parent;
     }
     return target;
   }
@@ -421,7 +419,7 @@
   List<AstNode> _findNodes(List<SourceRange> ranges) {
     var nodes = <AstNode>[];
     for (var range in ranges) {
-      var node = NodeLocator(range.offset).searchWithin(unit);
+      var node = NodeLocator(range.offset).searchWithin(unit)!;
       nodes.add(node);
     }
     return nodes;
@@ -429,7 +427,7 @@
 
   /// Returns the [ExpressionFunctionBody] that encloses [node], or `null`
   /// if [node] is not enclosed with an [ExpressionFunctionBody].
-  ExpressionFunctionBody _getEnclosingExpressionBody(AstNode node) {
+  ExpressionFunctionBody? _getEnclosingExpressionBody(AstNode? node) {
     while (node != null) {
       if (node is Statement) {
         return null;
@@ -447,7 +445,10 @@
     return analysisOptions.isLintEnabled(name);
   }
 
-  bool _isPartOfConstantExpression(AstNode node) {
+  bool _isPartOfConstantExpression(AstNode? node) {
+    if (node == null) {
+      return false;
+    }
     if (node is TypedLiteral) {
       return node.isConst;
     }
@@ -468,6 +469,8 @@
 
   void _prepareNames() {
     names.clear();
+    final stringLiteralPart = this.stringLiteralPart;
+    final singleExpression = this.singleExpression;
     if (stringLiteralPart != null) {
       names.addAll(getVariableNameSuggestionsForText(
           stringLiteralPart, excludedVariableNames));
@@ -486,13 +489,14 @@
     elementIds.clear();
 
     // prepare selection
-    String selectionSource;
+    String? selectionSource;
+    var singleExpression = this.singleExpression;
     if (singleExpression != null) {
       var tokens = TokenUtils.getNodeTokens(singleExpression);
       selectionSource = _encodeExpressionTokens(singleExpression, tokens);
     }
     // visit function
-    coveringFunctionBody.accept(_OccurrencesVisitor(
+    coveringFunctionBody!.accept(_OccurrencesVisitor(
         this, occurrences, selectionSource, unit.featureSet));
   }
 
@@ -509,7 +513,7 @@
 class _OccurrencesVisitor extends GeneralizingAstVisitor<void> {
   final ExtractLocalRefactoringImpl ref;
   final List<SourceRange> occurrences;
-  final String selectionSource;
+  final String? selectionSource;
   final FeatureSet featureSet;
 
   _OccurrencesVisitor(
@@ -533,12 +537,13 @@
 
   @override
   void visitStringLiteral(StringLiteral node) {
-    if (ref.stringLiteralPart != null) {
-      var length = ref.stringLiteralPart.length;
+    var stringLiteralPart = ref.stringLiteralPart;
+    if (stringLiteralPart != null) {
+      var length = stringLiteralPart.length;
       var value = ref.utils.getNodeText(node);
       var lastIndex = 0;
       while (true) {
-        var index = value.indexOf(ref.stringLiteralPart, lastIndex);
+        var index = value.indexOf(stringLiteralPart, lastIndex);
         if (index == -1) {
           break;
         }
diff --git a/pkg/analysis_server/lib/src/services/refactoring/extract_method.dart b/pkg/analysis_server/lib/src/services/refactoring/extract_method.dart
index d9c1a15..a2c9a19 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/extract_method.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/extract_method.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analysis_server/src/protocol_server.dart' hide Element;
 import 'package:analysis_server/src/services/correction/name_suggestion.dart';
 import 'package:analysis_server/src/services/correction/selection_analyzer.dart';
@@ -17,6 +15,7 @@
 import 'package:analysis_server/src/services/refactoring/rename_unit_member.dart';
 import 'package:analysis_server/src/services/refactoring/visible_ranges_computer.dart';
 import 'package:analysis_server/src/services/search/search_engine.dart';
+import 'package:analysis_server/src/utilities/extensions/ast.dart';
 import 'package:analyzer/dart/analysis/features.dart';
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/ast/ast.dart';
@@ -34,7 +33,7 @@
 
 const String _TOKEN_SEPARATOR = '\uFFFF';
 
-Element _getLocalElement(SimpleIdentifier node) {
+Element? _getLocalElement(SimpleIdentifier node) {
   var element = node.writeOrReadElement;
   if (element is LocalVariableElement ||
       element is ParameterElement ||
@@ -72,14 +71,14 @@
   final ResolvedUnitResult resolveResult;
   final int selectionOffset;
   final int selectionLength;
-  SourceRange selectionRange;
-  CorrectionUtils utils;
+  late SourceRange selectionRange;
+  late CorrectionUtils utils;
   final Set<Source> librariesToImport = <Source>{};
 
   @override
   String returnType = '';
-  String variableType;
-  String name;
+  String? variableType;
+  late String name;
   bool extractAll = true;
   @override
   bool canCreateGetter = false;
@@ -92,7 +91,7 @@
   final List<int> lengths = <int>[];
 
   /// The map of local elements to their visibility ranges.
-  Map<LocalElement, SourceRange> _visibleRangeMap;
+  late Map<LocalElement, SourceRange> _visibleRangeMap;
 
   /// The map of local names to their visibility ranges.
   final Map<String, List<SourceRange>> _localNames =
@@ -108,12 +107,12 @@
   final Map<String, List<SourceRange>> _parameterReferencesMap =
       <String, List<SourceRange>>{};
   bool _hasAwait = false;
-  DartType _returnType;
-  String _returnVariableName;
-  AstNode _parentMember;
-  Expression _selectionExpression;
-  FunctionExpression _selectionFunctionExpression;
-  List<Statement> _selectionStatements;
+  DartType? _returnType;
+  String? _returnVariableName;
+  AstNode? _parentMember;
+  Expression? _selectionExpression;
+  FunctionExpression? _selectionFunctionExpression;
+  List<Statement>? _selectionStatements;
   final List<_Occurrence> _occurrences = [];
   bool _staticContext = false;
 
@@ -250,7 +249,7 @@
                 occurrence._parameterOldToOccurrenceName[_returnVariableName];
             // may be declare variable
             if (!_parametersMap.containsKey(_returnVariableName)) {
-              if (variableType.isEmpty) {
+              if (variableType!.isEmpty) {
                 sb.write('var ');
               } else {
                 sb.write(variableType);
@@ -298,12 +297,12 @@
       // add replace edit
       var edit = newSourceEdit_range(range, invocationSource);
       doSourceChange_addElementEdit(
-          change, resolveResult.unit.declaredElement, edit);
+          change, resolveResult.unit!.declaredElement!, edit);
     }
     // add method declaration
     {
       // prepare environment
-      var prefix = utils.getNodePrefix(_parentMember);
+      var prefix = utils.getNodePrefix(_parentMember!);
       var eol = utils.endOfLine;
       // prepare annotations
       var annotations = '';
@@ -314,14 +313,15 @@
         }
       }
       // prepare declaration source
-      String declarationSource;
+      String? declarationSource;
       {
         var returnExpressionSource = _getMethodBodySource();
         // closure
-        if (_selectionFunctionExpression != null) {
+        final selectionFunctionExpression = _selectionFunctionExpression;
+        if (selectionFunctionExpression != null) {
           var returnTypeCode = _getExpectedClosureReturnTypeCode();
           declarationSource = '$returnTypeCode$name$returnExpressionSource';
-          if (_selectionFunctionExpression.body is ExpressionFunctionBody) {
+          if (selectionFunctionExpression.body is ExpressionFunctionBody) {
             declarationSource += ';';
           }
         }
@@ -375,10 +375,10 @@
       }
       // insert declaration
       if (declarationSource != null) {
-        var offset = _parentMember.end;
+        var offset = _parentMember!.end;
         var edit = SourceEdit(offset, 0, '$eol$eol$prefix$declarationSource');
         doSourceChange_addElementEdit(
-            change, resolveResult.unit.declaredElement, edit);
+            change, resolveResult.unit!.declaredElement!, edit);
       }
     }
     // done
@@ -427,15 +427,15 @@
   /// elements.
   Future<RefactoringStatus> _checkPossibleConflicts() async {
     var result = RefactoringStatus();
-    var parent = _parentMember.parent;
+    var parent = _parentMember!.parent;
     // top-level function
     if (parent is CompilationUnit) {
-      var libraryElement = parent.declaredElement.library;
+      var libraryElement = parent.declaredElement!.library;
       return validateCreateFunction(searchEngine, libraryElement, name);
     }
     // method of class
     if (parent is ClassDeclaration) {
-      var classElement = parent.declaredElement;
+      var classElement = parent.declaredElement!;
       return validateCreateMethod(searchEngine,
           AnalysisSessionHelper(resolveResult.session), classElement, name);
     }
@@ -450,7 +450,7 @@
       return RefactoringStatus.fatal(
           'The selection offset must be greater than zero.');
     }
-    if (selectionOffset + selectionLength >= resolveResult.content.length) {
+    if (selectionOffset + selectionLength >= resolveResult.content!.length) {
       return RefactoringStatus.fatal(
           'The selection end offset must be less then the length of the file.');
     }
@@ -557,7 +557,7 @@
 
   /// If the [selectionRange] is associated with a [FunctionExpression], return
   /// this [FunctionExpression].
-  FunctionExpression _findFunctionExpression() {
+  FunctionExpression? _findFunctionExpression() {
     if (selectionRange.length != 0) {
       return null;
     }
@@ -567,21 +567,25 @@
     // Check for the parameter list of a FunctionExpression.
     {
       var function = node?.thisOrAncestorOfType<FunctionExpression>();
-      if (function != null &&
-          function.parameters != null &&
-          range.node(function.parameters).contains(offset)) {
-        return function;
+      if (function != null) {
+        var parameters = function.parameters;
+        if (parameters != null && range.node(parameters).contains(offset)) {
+          return function;
+        }
       }
     }
 
     // Check for the name of the named argument with the closure expression.
-    if (node is SimpleIdentifier &&
-        node.parent is Label &&
-        node.parent.parent is NamedExpression) {
-      NamedExpression namedExpression = node.parent.parent;
-      var expression = namedExpression.expression;
-      if (expression is FunctionExpression) {
-        return expression;
+    if (node is SimpleIdentifier) {
+      var label = node.parent;
+      if (label is Label) {
+        var namedExpression = label.parent;
+        if (namedExpression is NamedExpression) {
+          var expression = namedExpression.expression;
+          if (expression is FunctionExpression) {
+            return expression;
+          }
+        }
       }
     }
 
@@ -593,7 +597,7 @@
   /// function type has the return type specified, return this return type's
   /// code. Otherwise return the empty string.
   String _getExpectedClosureReturnTypeCode() {
-    Expression argument = _selectionFunctionExpression;
+    Expression argument = _selectionFunctionExpression!;
     if (argument.parent is NamedExpression) {
       argument = argument.parent as NamedExpression;
     }
@@ -629,19 +633,21 @@
     // apply replacements
     source = SourceEdit.applySequence(source, replaceEdits);
     // change indentation
-    if (_selectionFunctionExpression != null) {
-      AstNode baseNode =
-          _selectionFunctionExpression.thisOrAncestorOfType<Statement>();
+    final selectionFunctionExpression = _selectionFunctionExpression;
+    if (selectionFunctionExpression != null) {
+      var baseNode =
+          selectionFunctionExpression.thisOrAncestorOfType<Statement>();
       if (baseNode != null) {
         var baseIndent = utils.getNodePrefix(baseNode);
-        var targetIndent = utils.getNodePrefix(_parentMember);
+        var targetIndent = utils.getNodePrefix(_parentMember!);
         source = utils.replaceSourceIndent(source, baseIndent, targetIndent);
         source = source.trim();
       }
     }
-    if (_selectionStatements != null) {
-      var selectionIndent = utils.getNodePrefix(_selectionStatements[0]);
-      var targetIndent = utils.getNodePrefix(_parentMember) + '  ';
+    final selectionStatements = _selectionStatements;
+    if (selectionStatements != null) {
+      var selectionIndent = utils.getNodePrefix(selectionStatements[0]);
+      var targetIndent = utils.getNodePrefix(_parentMember!) + '  ';
       source = utils.replaceSourceIndent(source, selectionIndent, targetIndent);
     }
     // done
@@ -652,25 +658,25 @@
     var originalSource = utils.getText(range.offset, range.length);
     var pattern = _SourcePattern();
     var replaceEdits = <SourceEdit>[];
-    resolveResult.unit
+    resolveResult.unit!
         .accept(_GetSourcePatternVisitor(range, pattern, replaceEdits));
     replaceEdits = replaceEdits.reversed.toList();
     var source = SourceEdit.applySequence(originalSource, replaceEdits);
     pattern.normalizedSource =
-        _getNormalizedSource(source, resolveResult.unit.featureSet);
+        _getNormalizedSource(source, resolveResult.unit!.featureSet);
     return pattern;
   }
 
   String _getTypeCode(DartType type) {
-    return utils.getTypeSource(type, librariesToImport);
+    return utils.getTypeSource(type, librariesToImport)!;
   }
 
   void _initializeHasAwait() {
     var visitor = _HasAwaitVisitor();
     if (_selectionExpression != null) {
-      _selectionExpression.accept(visitor);
+      _selectionExpression!.accept(visitor);
     } else if (_selectionStatements != null) {
-      _selectionStatements.forEach((statement) {
+      _selectionStatements!.forEach((statement) {
         statement.accept(visitor);
       });
     }
@@ -685,7 +691,7 @@
     var patternToSelectionName =
         _inverseMap(selectionPattern.originalToPatternNames);
     // prepare an enclosing parent - class or unit
-    var enclosingMemberParent = _parentMember.parent;
+    var enclosingMemberParent = _parentMember!.parent!;
     // visit nodes which will able to access extracted method
     enclosingMemberParent.accept(_InitializeOccurrencesVisitor(
         this, selectionPattern, patternToSelectionName));
@@ -700,28 +706,30 @@
     var result = RefactoringStatus();
     var assignedUsedVariables = <VariableElement>[];
 
-    var unit = resolveResult.unit;
+    var unit = resolveResult.unit!;
     _visibleRangeMap = VisibleRangesComputer.forNode(unit);
     unit.accept(
       _InitializeParametersVisitor(this, assignedUsedVariables),
     );
 
     // single expression
-    if (_selectionExpression != null) {
-      _returnType = _selectionExpression.staticType;
+    final selectionExpression = _selectionExpression;
+    if (selectionExpression != null) {
+      _returnType = selectionExpression.typeOrThrow;
     }
     // verify that none or all execution flows end with a "return"
-    if (_selectionStatements != null) {
-      var hasReturn = _selectionStatements.any(_mayEndWithReturnStatement);
-      if (hasReturn && !ExitDetector.exits(_selectionStatements.last)) {
+    final selectionStatements = _selectionStatements;
+    if (selectionStatements != null) {
+      var hasReturn = selectionStatements.any(_mayEndWithReturnStatement);
+      if (hasReturn && !ExitDetector.exits(selectionStatements.last)) {
         result.addError(ERROR_EXITS);
       }
     }
     // maybe ends with "return" statement
-    if (_selectionStatements != null) {
+    if (selectionStatements != null) {
       var typeSystem = resolveResult.typeSystem;
       var returnTypeComputer = _ReturnTypeComputer(typeSystem);
-      _selectionStatements.forEach((statement) {
+      selectionStatements.forEach((statement) {
         statement.accept(returnTypeComputer);
       });
       _returnType = returnTypeComputer.returnType;
@@ -758,10 +766,11 @@
 
   Future<void> _initializeReturnType() async {
     var typeProvider = resolveResult.typeProvider;
+    final returnTypeObj = _returnType;
     if (_selectionFunctionExpression != null) {
       variableType = '';
       returnType = '';
-    } else if (_returnType == null) {
+    } else if (returnTypeObj == null) {
       variableType = null;
       if (_hasAwait) {
         var futureVoid = typeProvider.futureType(typeProvider.voidType);
@@ -769,7 +778,7 @@
       } else {
         returnType = 'void';
       }
-    } else if (_returnType.isDynamic) {
+    } else if (returnTypeObj.isDynamic) {
       variableType = '';
       if (_hasAwait) {
         returnType = _getTypeCode(typeProvider.futureDynamicType);
@@ -777,13 +786,13 @@
         returnType = '';
       }
     } else {
-      variableType = _getTypeCode(_returnType);
+      variableType = _getTypeCode(returnTypeObj);
       if (_hasAwait) {
-        if (_returnType.element != typeProvider.futureElement) {
-          returnType = _getTypeCode(typeProvider.futureType(_returnType));
+        if (returnTypeObj.element != typeProvider.futureElement) {
+          returnType = _getTypeCode(typeProvider.futureType(returnTypeObj));
         }
       } else {
-        returnType = variableType;
+        returnType = variableType!;
       }
     }
   }
@@ -805,7 +814,7 @@
     var name = parameter.name;
     var parameterRanges = _parameterReferencesMap[id];
     var otherRanges = _localNames[name];
-    for (var parameterRange in parameterRanges) {
+    for (var parameterRange in parameterRanges!) {
       if (otherRanges != null) {
         for (var otherRange in otherRanges) {
           if (parameterRange.intersects(otherRange)) {
@@ -823,7 +832,7 @@
   /// Checks if [element] is referenced after [selectionRange].
   bool _isUsedAfterSelection(Element element) {
     var visitor = _IsUsedAfterSelectionVisitor(this, element);
-    _parentMember.accept(visitor);
+    _parentMember!.accept(visitor);
     return visitor.result;
   }
 
@@ -831,15 +840,16 @@
   /// proposed as names of the extracted method.
   void _prepareExcludedNames() {
     _excludedNames.clear();
-    var localElements = getDefinedLocalElements(_parentMember);
-    _excludedNames.addAll(localElements.map((e) => e.name));
+    var localElements = getDefinedLocalElements(_parentMember!);
+    _excludedNames.addAll(localElements.map((e) => e.name!));
   }
 
   void _prepareNames() {
     names.clear();
-    if (_selectionExpression != null) {
+    final selectionExpression = _selectionExpression;
+    if (selectionExpression != null) {
       names.addAll(getVariableNameSuggestionsForExpression(
-          _selectionExpression.staticType, _selectionExpression, _excludedNames,
+          selectionExpression.typeOrThrow, selectionExpression, _excludedNames,
           isMethod: true));
     }
   }
@@ -854,7 +864,7 @@
   }
 
   /// Checks if the given [expression] is reasonable to extract as a getter.
-  static bool _isExpressionForGetter(Expression expression) {
+  static bool _isExpressionForGetter(Expression? expression) {
     if (expression is BinaryExpression) {
       return _isExpressionForGetter(expression.leftOperand) &&
           _isExpressionForGetter(expression.rightOperand);
@@ -906,18 +916,17 @@
   }
 
   @override
-  Object visitAssignmentExpression(AssignmentExpression node) {
+  void visitAssignmentExpression(AssignmentExpression node) {
     super.visitAssignmentExpression(node);
     var lhs = node.leftHandSide;
     if (_isFirstSelectedNode(lhs)) {
       invalidSelection('Cannot extract the left-hand side of an assignment.',
           newLocation_fromNode(lhs));
     }
-    return null;
   }
 
   @override
-  Object visitConstructorInitializer(ConstructorInitializer node) {
+  void visitConstructorInitializer(ConstructorInitializer node) {
     super.visitConstructorInitializer(node);
     if (_isFirstSelectedNode(node)) {
       invalidSelection(
@@ -925,17 +934,15 @@
           'Select expression part of initializer.',
           newLocation_fromNode(node));
     }
-    return null;
   }
 
   @override
-  Object visitForParts(ForParts node) {
+  void visitForParts(ForParts node) {
     node.visitChildren(this);
-    return null;
   }
 
   @override
-  Object visitForStatement(ForStatement node) {
+  void visitForStatement(ForStatement node) {
     super.visitForStatement(node);
     var forLoopParts = node.forLoopParts;
     if (forLoopParts is ForParts) {
@@ -947,20 +954,18 @@
         invalidSelection("Cannot extract increment part of a 'for' statement.");
       }
     }
-    return null;
   }
 
   @override
-  Object visitGenericFunctionType(GenericFunctionType node) {
+  void visitGenericFunctionType(GenericFunctionType node) {
     super.visitGenericFunctionType(node);
     if (_isFirstSelectedNode(node)) {
       invalidSelection('Cannot extract a single type reference.');
     }
-    return null;
   }
 
   @override
-  Object visitSimpleIdentifier(SimpleIdentifier node) {
+  void visitSimpleIdentifier(SimpleIdentifier node) {
     super.visitSimpleIdentifier(node);
     if (_isFirstSelectedNode(node)) {
       // name of declaration
@@ -978,20 +983,18 @@
         invalidSelection('Can not extract name part of a property access.');
       }
     }
-    return null;
   }
 
   @override
-  Object visitTypeName(TypeName node) {
+  void visitTypeName(TypeName node) {
     super.visitTypeName(node);
     if (_isFirstSelectedNode(node)) {
       invalidSelection('Cannot extract a single type reference.');
     }
-    return null;
   }
 
   @override
-  Object visitVariableDeclaration(VariableDeclaration node) {
+  void visitVariableDeclaration(VariableDeclaration node) {
     super.visitVariableDeclaration(node);
     if (_isFirstSelectedNode(node)) {
       invalidSelection(
@@ -999,17 +1002,15 @@
           'Select whole declaration statement.',
           newLocation_fromNode(node));
     }
-    return null;
   }
 
   void _checkParent(AstNode node) {
-    var firstParent = firstSelectedNode.parent;
-    do {
-      node = node.parent;
-      if (identical(node, firstParent)) {
+    var firstParent = firstSelectedNode!.parent;
+    for (var parent in node.withParents) {
+      if (identical(parent, firstParent)) {
         return;
       }
-    } while (node != null);
+    }
     invalidSelection(
         'Not all selected statements are enclosed by the same parent statement.');
   }
@@ -1056,7 +1057,7 @@
     if (element is FunctionElement) {
       return element.type;
     }
-    throw StateError('Unknown element type: ${element?.runtimeType}');
+    throw StateError('Unknown element type: ${element.runtimeType}');
   }
 }
 
@@ -1163,7 +1164,7 @@
       // prepare mapping of parameter names to the occurrence variables
       nodePattern.originalToPatternNames
           .forEach((String originalName, String patternName) {
-        var selectionName = patternToSelectionName[patternName];
+        var selectionName = patternToSelectionName[patternName]!;
         occurrence._parameterOldToOccurrenceName[selectionName] = originalName;
       });
       // update static
@@ -1179,7 +1180,7 @@
 
   void _visitStatements(List<Statement> statements) {
     var beginStatementIndex = 0;
-    var selectionCount = ref._selectionStatements.length;
+    var selectionCount = ref._selectionStatements!.length;
     while (beginStatementIndex + selectionCount <= statements.length) {
       var nodeRange = range.startEnd(statements[beginStatementIndex],
           statements[beginStatementIndex + selectionCount - 1]);
@@ -1219,11 +1220,14 @@
         // add parameter
         var parameter = ref._parametersMap[name];
         if (parameter == null) {
-          var parameterType = node.writeOrReadType;
+          var parameterType = node.writeOrReadType!;
           var parametersBuffer = StringBuffer();
           var parameterTypeCode = ref.utils.getTypeSource(
               parameterType, ref.librariesToImport,
               parametersBuffer: parametersBuffer);
+          if (parameterTypeCode == null) {
+            return;
+          }
           var parametersCode =
               parametersBuffer.isNotEmpty ? parametersBuffer.toString() : null;
           parameter = RefactoringMethodParameter(
@@ -1237,7 +1241,8 @@
       }
       // remember, if assigned and used after selection
       if (isLeftHandOfAssignment(node) && ref._isUsedAfterSelection(element)) {
-        if (!assignedUsedVariables.contains(element)) {
+        if (element is VariableElement &&
+            !assignedUsedVariables.contains(element)) {
           assignedUsedVariables.add(element);
         }
       }
@@ -1246,8 +1251,11 @@
     if (element is LocalElement) {
       // declared local elements
       if (node.inDeclarationContext()) {
-        ref._localNames.putIfAbsent(name, () => <SourceRange>[]);
-        ref._localNames[name].add(ref._visibleRangeMap[element]);
+        var range = ref._visibleRangeMap[element];
+        if (range != null) {
+          var ranges = ref._localNames.putIfAbsent(name, () => <SourceRange>[]);
+          ranges.add(range);
+        }
       }
     } else {
       // unqualified non-local names
@@ -1291,7 +1299,7 @@
 class _ReturnTypeComputer extends RecursiveAstVisitor<void> {
   final TypeSystem typeSystem;
 
-  DartType returnType;
+  DartType? returnType;
 
   _ReturnTypeComputer(this.typeSystem);
 
@@ -1306,18 +1314,22 @@
       return;
     }
     // prepare type
-    var type = expression.staticType;
+    var type = expression.typeOrThrow;
     if (type.isBottom) {
       return;
     }
     // combine types
+    returnType = _combine(returnType, type);
+  }
+
+  DartType _combine(DartType? returnType, DartType type) {
     if (returnType == null) {
-      returnType = type;
+      return type;
     } else {
       if (returnType is InterfaceType && type is InterfaceType) {
-        returnType = InterfaceType.getSmartLeastUpperBound(returnType, type);
+        return InterfaceType.getSmartLeastUpperBound(returnType, type);
       } else {
-        returnType = typeSystem.leastUpperBound(returnType, type);
+        return typeSystem.leastUpperBound(returnType, type);
       }
     }
   }
@@ -1328,7 +1340,7 @@
 /// pattern to the original variable names.
 class _SourcePattern {
   final List<DartType> parameterTypes = <DartType>[];
-  String normalizedSource;
+  late String normalizedSource;
   final Map<String, String> originalToPatternNames = {};
 
   bool isCompatible(_SourcePattern other) {
diff --git a/pkg/analysis_server/lib/src/services/refactoring/extract_widget.dart b/pkg/analysis_server/lib/src/services/refactoring/extract_widget.dart
index f7f67c7..36304e9 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/extract_widget.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/extract_widget.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analysis_server/src/protocol_server.dart' hide Element;
 import 'package:analysis_server/src/services/correction/status.dart';
 import 'package:analysis_server/src/services/correction/util.dart';
@@ -38,37 +36,37 @@
   final int offset;
   final int length;
 
-  CorrectionUtils utils;
+  late CorrectionUtils utils;
 
-  ClassElement classBuildContext;
-  ClassElement classKey;
-  ClassElement classStatelessWidget;
-  ClassElement classWidget;
-  PropertyAccessorElement accessorRequired;
+  ClassElement? classBuildContext;
+  ClassElement? classKey;
+  ClassElement? classStatelessWidget;
+  ClassElement? classWidget;
+  PropertyAccessorElement? accessorRequired;
 
   @override
-  String name;
+  late String name;
 
   /// If [offset] is in a class, the node of this class, `null` otherwise.
-  ClassDeclaration _enclosingClassNode;
+  ClassDeclaration? _enclosingClassNode;
 
   /// If [offset] is in a class, the element of this class, `null` otherwise.
-  ClassElement _enclosingClassElement;
+  ClassElement? _enclosingClassElement;
 
   /// The [CompilationUnitMember] that encloses the [offset].
-  CompilationUnitMember _enclosingUnitMember;
+  CompilationUnitMember? _enclosingUnitMember;
 
   /// The widget creation expression to extract.
-  InstanceCreationExpression _expression;
+  InstanceCreationExpression? _expression;
 
   /// The statements covered by [offset] and [length] to extract.
-  List<Statement> _statements;
+  List<Statement>? _statements;
 
   /// The [SourceRange] that covers [_statements].
-  SourceRange _statementsRange;
+  SourceRange? _statementsRange;
 
   /// The method returning widget to extract.
-  MethodDeclaration _method;
+  MethodDeclaration? _method;
 
   /// The parameters for the new widget class - referenced fields of the
   /// [_enclosingClassElement], local variables referenced by [_expression],
@@ -87,7 +85,7 @@
   }
 
   FeatureSet get _featureSet {
-    return resolveResult.unit.featureSet;
+    return resolveResult.unit!.featureSet;
   }
 
   Flutter get _flutter => Flutter.instance;
@@ -110,7 +108,7 @@
       return result;
     }
 
-    var astNode = _expression ?? _method ?? _statements.first;
+    var astNode = _expression ?? _method ?? _statements!.first;
     _enclosingUnitMember = astNode.thisOrAncestorMatching((n) {
       return n is CompilationUnitMember && n.parent is CompilationUnit;
     });
@@ -146,13 +144,15 @@
   Future<SourceChange> createChange() async {
     var builder =
         ChangeBuilder(session: sessionHelper.session, eol: utils.endOfLine);
-    await builder.addDartFileEdit(resolveResult.path, (builder) {
-      if (_expression != null) {
-        builder.addReplacement(range.node(_expression), (builder) {
+    await builder.addDartFileEdit(resolveResult.path!, (builder) {
+      final expression = _expression;
+      final statements = _statements;
+      if (expression != null) {
+        builder.addReplacement(range.node(expression), (builder) {
           _writeWidgetInstantiation(builder);
         });
-      } else if (_statements != null) {
-        builder.addReplacement(_statementsRange, (builder) {
+      } else if (statements != null) {
+        builder.addReplacement(_statementsRange!, (builder) {
           builder.write('return ');
           _writeWidgetInstantiation(builder);
           builder.write(';');
@@ -179,7 +179,7 @@
 
     // Treat single ReturnStatement as its expression.
     if (node is ReturnStatement) {
-      node = (node as ReturnStatement).expression;
+      node = node.expression;
     }
 
     // Find the enclosing class.
@@ -224,7 +224,7 @@
       }
       if (node is MethodDeclaration) {
         var returnType = node.returnType?.type;
-        if (_flutter.isWidgetType(returnType) && node.body != null) {
+        if (_flutter.isWidgetType(returnType)) {
           _method = node;
           return RefactoringStatus();
         }
@@ -240,7 +240,7 @@
   Future<RefactoringStatus> _initializeClasses() async {
     var result = RefactoringStatus();
 
-    Future<ClassElement> getClass(String name) async {
+    Future<ClassElement?> getClass(String name) async {
       var element = await sessionHelper.getClass(_flutter.widgetsUri, name);
       if (element == null) {
         result.addFatalError(
@@ -250,7 +250,8 @@
       return element;
     }
 
-    Future<PropertyAccessorElement> getAccessor(String uri, String name) async {
+    Future<PropertyAccessorElement?> getAccessor(
+        String uri, String name) async {
       var element = await sessionHelper.getTopLevelPropertyAccessor(uri, name);
       if (element == null) {
         result.addFatalError("Unable to find 'required' in $uri");
@@ -271,40 +272,48 @@
   /// Prepare referenced local variables and fields, that should be turned
   /// into the widget class fields and constructor parameters.
   Future<RefactoringStatus> _initializeParameters() async {
-    _ParametersCollector collector;
-    if (_expression != null) {
-      var localRange = range.node(_expression);
+    _ParametersCollector? collector;
+
+    final expression = _expression;
+    if (expression != null) {
+      var localRange = range.node(expression);
       collector = _ParametersCollector(_enclosingClassElement, localRange);
-      _expression.accept(collector);
+      expression.accept(collector);
     }
-    if (_statements != null) {
+
+    final statements = _statements;
+    if (statements != null) {
       collector =
-          _ParametersCollector(_enclosingClassElement, _statementsRange);
-      for (var statement in _statements) {
+          _ParametersCollector(_enclosingClassElement, _statementsRange!);
+      for (var statement in statements) {
         statement.accept(collector);
       }
     }
-    if (_method != null) {
-      var localRange = range.node(_method);
+
+    final method = _method;
+    if (method != null) {
+      var localRange = range.node(method);
       collector = _ParametersCollector(_enclosingClassElement, localRange);
-      _method.body.accept(collector);
+      method.body.accept(collector);
     }
 
     _parameters
       ..clear()
-      ..addAll(collector.parameters);
+      ..addAll(collector!.parameters);
 
     // We added fields, now add the method parameters.
-    if (_method != null) {
-      for (var parameter in _method.parameters.parameters) {
-        if (parameter is DefaultFormalParameter) {
-          DefaultFormalParameter defaultFormalParameter = parameter;
-          parameter = defaultFormalParameter.parameter;
-        }
-        if (parameter is NormalFormalParameter) {
-          _parameters.add(_Parameter(
-              parameter.identifier.name, parameter.declaredElement.type,
-              isMethodParameter: true));
+    if (method != null) {
+      var parameterList = method.parameters;
+      if (parameterList != null) {
+        for (var parameter in parameterList.parameters) {
+          if (parameter is DefaultFormalParameter) {
+            parameter = parameter.parameter;
+          }
+          if (parameter is NormalFormalParameter) {
+            _parameters.add(_Parameter(
+                parameter.identifier!.name, parameter.declaredElement!.type,
+                isMethodParameter: true));
+          }
         }
       }
     }
@@ -349,7 +358,7 @@
 
   /// Remove the [_method] declaration.
   void _removeMethodDeclaration(DartFileEditBuilder builder) {
-    var methodRange = range.node(_method);
+    var methodRange = range.node(_method!);
     var linesRange =
         utils.getLinesRange(methodRange, skipLeadingEmptyLines: true);
     builder.addDeletion(linesRange);
@@ -363,8 +372,8 @@
   /// Replace invocations of the [_method] with instantiations of the new
   /// widget class.
   void _replaceInvocationsWithInstantiations(DartFileEditBuilder builder) {
-    var collector = _MethodInvocationsCollector(_method.declaredElement);
-    _enclosingClassNode.accept(collector);
+    var collector = _MethodInvocationsCollector(_method!.declaredElement!);
+    _enclosingClassNode!.accept(collector);
     for (var invocation in collector.invocations) {
       List<Expression> arguments = invocation.argumentList.arguments;
       builder.addReplacement(range.node(invocation), (builder) {
@@ -382,7 +391,7 @@
           if (parameter.isMethodParameter) {
             var argument = arguments[argumentIndex++];
             if (argument is NamedExpression) {
-              argument = (argument as NamedExpression).expression;
+              argument = argument.expression;
             }
             builder.write(utils.getNodeText(argument));
           } else {
@@ -396,12 +405,12 @@
 
   /// Write declaration of the new widget class.
   void _writeWidgetDeclaration(DartFileEditBuilder builder) {
-    builder.addInsertion(_enclosingUnitMember.end, (builder) {
+    builder.addInsertion(_enclosingUnitMember!.end, (builder) {
       builder.writeln();
       builder.writeln();
       builder.writeClassDeclaration(
         name,
-        superclass: classStatelessWidget.instantiate(
+        superclass: classStatelessWidget!.instantiate(
           typeArguments: const [],
           nullabilitySuffix: NullabilitySuffix.none,
         ),
@@ -418,7 +427,7 @@
               builder.write('    ');
               builder.writeParameter(
                 'key',
-                type: classKey.instantiate(
+                type: classKey!.instantiate(
                   typeArguments: const [],
                   nullabilitySuffix: _isNonNullable
                       ? NullabilitySuffix.question
@@ -434,7 +443,7 @@
                   builder.write('required');
                 } else {
                   builder.write('@');
-                  builder.writeReference(accessorRequired);
+                  builder.writeReference(accessorRequired!);
                 }
                 builder.write(' ');
                 if (parameter.constructorName != parameter.name) {
@@ -481,25 +490,26 @@
           builder.write('  ');
           builder.writeFunctionDeclaration(
             'build',
-            returnType: classWidget.instantiate(
+            returnType: classWidget!.instantiate(
               typeArguments: const [],
               nullabilitySuffix: NullabilitySuffix.none,
             ),
             parameterWriter: () {
               builder.writeParameter(
                 'context',
-                type: classBuildContext.instantiate(
+                type: classBuildContext!.instantiate(
                   typeArguments: const [],
                   nullabilitySuffix: NullabilitySuffix.none,
                 ),
               );
             },
             bodyWriter: () {
-              if (_expression != null) {
-                var indentOld = utils.getLinePrefix(_expression.offset);
+              final expression = _expression;
+              if (expression != null) {
+                var indentOld = utils.getLinePrefix(expression.offset);
                 var indentNew = '    ';
 
-                var code = utils.getNodeText(_expression);
+                var code = utils.getNodeText(expression);
                 code = _replaceIndent(code, indentOld, indentNew);
 
                 builder.writeln('{');
@@ -510,10 +520,10 @@
 
                 builder.writeln('  }');
               } else if (_statements != null) {
-                var indentOld = utils.getLinePrefix(_statementsRange.offset);
+                var indentOld = utils.getLinePrefix(_statementsRange!.offset);
                 var indentNew = '    ';
 
-                var code = utils.getRangeText(_statementsRange);
+                var code = utils.getRangeText(_statementsRange!);
                 code = _replaceIndent(code, indentOld, indentNew);
 
                 builder.writeln('{');
@@ -524,7 +534,7 @@
 
                 builder.writeln('  }');
               } else {
-                var code = utils.getNodeText(_method.body);
+                var code = utils.getNodeText(_method!.body);
                 builder.writeln(code);
               }
             },
@@ -552,14 +562,14 @@
 }
 
 class _MethodInvocationsCollector extends RecursiveAstVisitor<void> {
-  final MethodElement methodElement;
+  final ExecutableElement methodElement;
   final List<MethodInvocation> invocations = [];
 
   _MethodInvocationsCollector(this.methodElement);
 
   @override
   void visitMethodInvocation(MethodInvocation node) {
-    if (node.methodName?.staticElement == methodElement) {
+    if (node.methodName.staticElement == methodElement) {
       invocations.add(node);
     } else {
       super.visitMethodInvocation(node);
@@ -579,20 +589,20 @@
 
   /// If the [name] is private, the public name to use in the new widget
   /// constructor. If the [name] is already public, then the [name].
-  String constructorName;
+  late String constructorName;
 
   _Parameter(this.name, this.type, {this.isMethodParameter = false});
 }
 
 class _ParametersCollector extends RecursiveAstVisitor<void> {
-  final ClassElement enclosingClass;
+  final ClassElement? enclosingClass;
   final SourceRange expressionRange;
 
   final RefactoringStatus status = RefactoringStatus();
   final Set<Element> uniqueElements = <Element>{};
   final List<_Parameter> parameters = [];
 
-  List<ClassElement> enclosingClasses;
+  List<ClassElement>? enclosingClasses;
 
   _ParametersCollector(this.enclosingClass, this.expressionRange);
 
@@ -604,7 +614,7 @@
     }
     var elementName = element.displayName;
 
-    DartType type;
+    DartType? type;
     if (element is MethodElement) {
       if (_isMemberOfEnclosingClass(element)) {
         status.addError(
@@ -638,8 +648,9 @@
   /// Return `true` if the given [element] is a member of the [enclosingClass]
   /// or one of its supertypes, interfaces, or mixins.
   bool _isMemberOfEnclosingClass(Element element) {
+    final enclosingClass = this.enclosingClass;
     if (enclosingClass != null) {
-      enclosingClasses ??= <ClassElement>[]
+      final enclosingClasses = this.enclosingClasses ??= <ClassElement>[]
         ..add(enclosingClass)
         ..addAll(enclosingClass.allSupertypes.map((t) => t.element));
       return enclosingClasses.contains(element.enclosingElement);
diff --git a/pkg/analysis_server/lib/src/services/refactoring/inline_local.dart b/pkg/analysis_server/lib/src/services/refactoring/inline_local.dart
index bbfccb9..12afe4d 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/inline_local.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/inline_local.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analysis_server/src/protocol_server.dart' hide Element;
 import 'package:analysis_server/src/services/correction/status.dart';
 import 'package:analysis_server/src/services/correction/util.dart';
@@ -25,34 +23,24 @@
   final SearchEngine searchEngine;
   final ResolvedUnitResult resolveResult;
   final int offset;
-  CorrectionUtils utils;
+  final CorrectionUtils utils;
 
-  Element _variableElement;
-  VariableDeclaration _variableNode;
-  List<SearchMatch> _references;
+  _InitialState? _initialState;
 
-  InlineLocalRefactoringImpl(
-      this.searchEngine, this.resolveResult, this.offset) {
-    utils = CorrectionUtils(resolveResult);
-  }
+  InlineLocalRefactoringImpl(this.searchEngine, this.resolveResult, this.offset)
+      : utils = CorrectionUtils(resolveResult);
 
   @override
   String get refactoringName => 'Inline Local Variable';
 
   @override
   int get referenceCount {
-    if (_references == null) {
-      return 0;
-    }
-    return _references.length;
+    return _initialState?.references.length ?? 0;
   }
 
   @override
-  String get variableName {
-    if (_variableElement == null) {
-      return null;
-    }
-    return _variableElement.name;
+  String? get variableName {
+    return _initialState?.element.name;
   }
 
   @override
@@ -63,78 +51,93 @@
 
   @override
   Future<RefactoringStatus> checkInitialConditions() async {
-    var result = RefactoringStatus();
     // prepare variable
-    {
-      var offsetNode = NodeLocator(offset).searchWithin(resolveResult.unit);
-      if (offsetNode is SimpleIdentifier) {
-        var element = offsetNode.staticElement;
-        if (element is LocalVariableElement) {
-          _variableElement = element;
-          var declarationResult =
-              await AnalysisSessionHelper(resolveResult.session)
-                  .getElementDeclaration(element);
-          _variableNode = declarationResult.node;
-        }
-      }
+    var offsetNode = NodeLocator(offset).searchWithin(resolveResult.unit);
+    if (offsetNode is! SimpleIdentifier) {
+      return _noLocalVariableStatus();
+    }
+
+    var element = offsetNode.staticElement;
+    if (element is! LocalVariableElement) {
+      return _noLocalVariableStatus();
+    }
+
+    var helper = AnalysisSessionHelper(resolveResult.session);
+    var declarationResult = await helper.getElementDeclaration(element);
+    var node = declarationResult?.node;
+    if (node is! VariableDeclaration) {
+      return _noLocalVariableStatus();
     }
     // validate node declaration
-    if (!_isVariableDeclaredInStatement()) {
-      result = RefactoringStatus.fatal(
-          'Local variable declaration or reference must be selected '
-          'to activate this refactoring.');
-      return Future<RefactoringStatus>.value(result);
+    var declarationStatement = _declarationStatement(node);
+    if (declarationStatement == null) {
+      return _noLocalVariableStatus();
     }
     // should have initializer at declaration
-    if (_variableNode.initializer == null) {
+    var initializer = node.initializer;
+    if (initializer == null) {
       var message = format(
-          "Local variable '{0}' is not initialized at declaration.",
-          _variableElement.displayName);
-      result =
-          RefactoringStatus.fatal(message, newLocation_fromNode(_variableNode));
-      return Future<RefactoringStatus>.value(result);
+        "Local variable '{0}' is not initialized at declaration.",
+        element.displayName,
+      );
+      return RefactoringStatus.fatal(
+        message,
+        newLocation_fromNode(node),
+      );
     }
     // prepare references
-    _references = await searchEngine.searchReferences(_variableElement);
+    var references = await searchEngine.searchReferences(element);
     // should not have assignments
-    for (var reference in _references) {
+    for (var reference in references) {
       if (reference.kind != MatchKind.READ) {
-        var message = format("Local variable '{0}' is assigned more than once.",
-            [_variableElement.displayName]);
+        var message = format(
+          "Local variable '{0}' is assigned more than once.",
+          [element.displayName],
+        );
         return RefactoringStatus.fatal(
-            message, newLocation_fromMatch(reference));
+          message,
+          newLocation_fromMatch(reference),
+        );
       }
     }
     // done
-    return result;
+    _initialState = _InitialState(
+      element: element,
+      node: node,
+      initializer: initializer,
+      declarationStatement: declarationStatement,
+      references: references,
+    );
+    return RefactoringStatus();
   }
 
   @override
   Future<SourceChange> createChange() {
     var change = SourceChange(refactoringName);
+    var unitElement = resolveResult.unit!.declaredElement!;
+    var state = _initialState!;
     // remove declaration
     {
-      Statement declarationStatement =
-          _variableNode.thisOrAncestorOfType<VariableDeclarationStatement>();
-      var range = utils.getLinesRangeStatements([declarationStatement]);
-      doSourceChange_addElementEdit(change, resolveResult.unit.declaredElement,
-          newSourceEdit_range(range, ''));
+      var range = utils.getLinesRangeStatements([(state.declarationStatement)]);
+      doSourceChange_addElementEdit(
+          change, unitElement, newSourceEdit_range(range, ''));
     }
     // prepare initializer
-    var initializer = _variableNode.initializer;
+    var initializer = state.initializer;
     var initializerCode = utils.getNodeText(initializer);
     // replace references
-    for (var reference in _references) {
+    for (var reference in state.references) {
       var editRange = reference.sourceRange;
       // prepare context
       var offset = editRange.offset;
-      var node = utils.findNode(offset);
+      var node = utils.findNode(offset)!;
       var parent = node.parent;
       // prepare code
       String codeForReference;
       if (parent is InterpolationExpression) {
-        StringInterpolation target = parent.parent;
-        if (initializer is SingleStringLiteral &&
+        var target = parent.parent;
+        if (target is StringInterpolation &&
+            initializer is SingleStringLiteral &&
             !initializer.isRaw &&
             initializer.isSingleQuoted == target.isSingleQuoted &&
             (!initializer.isMultiline || target.isMultiline)) {
@@ -154,26 +157,34 @@
         codeForReference = initializerCode;
       }
       // do replace
-      doSourceChange_addElementEdit(change, resolveResult.unit.declaredElement,
+      doSourceChange_addElementEdit(change, unitElement,
           newSourceEdit_range(editRange, codeForReference));
     }
     // done
     return Future.value(change);
   }
 
-  bool _isVariableDeclaredInStatement() {
-    if (_variableNode == null) {
-      return false;
-    }
-    var parent = _variableNode.parent;
-    if (parent is VariableDeclarationList) {
-      parent = parent.parent;
-      if (parent is VariableDeclarationStatement) {
-        parent = parent.parent;
-        return parent is Block || parent is SwitchCase;
+  RefactoringStatus _noLocalVariableStatus() {
+    return RefactoringStatus.fatal(
+      'Local variable declaration or reference must be selected '
+      'to activate this refactoring.',
+    );
+  }
+
+  static VariableDeclarationStatement? _declarationStatement(
+    VariableDeclaration declaration,
+  ) {
+    var declarationList = declaration.parent;
+    if (declarationList is VariableDeclarationList) {
+      var statement = declarationList.parent;
+      if (statement is VariableDeclarationStatement) {
+        var parent = statement.parent;
+        if (parent is Block || parent is SwitchCase) {
+          return statement;
+        }
       }
     }
-    return false;
+    return null;
   }
 
   static bool _shouldBeExpressionInterpolation(
@@ -204,3 +215,19 @@
     return false;
   }
 }
+
+class _InitialState {
+  final LocalVariableElement element;
+  final VariableDeclaration node;
+  final Expression initializer;
+  final VariableDeclarationStatement declarationStatement;
+  final List<SearchMatch> references;
+
+  _InitialState({
+    required this.element,
+    required this.node,
+    required this.initializer,
+    required this.declarationStatement,
+    required this.references,
+  });
+}
diff --git a/pkg/analysis_server/lib/src/services/refactoring/inline_method.dart b/pkg/analysis_server/lib/src/services/refactoring/inline_method.dart
index 0b6f4a2..47362ca 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/inline_method.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/inline_method.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analysis_server/src/protocol_server.dart' hide Element;
 import 'package:analysis_server/src/services/correction/status.dart';
 import 'package:analysis_server/src/services/correction/util.dart';
@@ -47,14 +45,14 @@
     _SourcePart part,
     CorrectionUtils utils,
     AstNode contextNode,
-    Expression targetExpression,
+    Expression? targetExpression,
     List<Expression> arguments) {
   // prepare edits to replace parameters with arguments
   var edits = <SourceEdit>[];
   part._parameters.forEach(
       (ParameterElement parameter, List<_ParameterOccurrence> occurrences) {
     // prepare argument
-    Expression argument;
+    Expression? argument;
     for (var arg in arguments) {
       if (arg.staticParameterElement == parameter) {
         argument = arg;
@@ -62,11 +60,11 @@
       }
     }
     if (argument is NamedExpression) {
-      argument = (argument as NamedExpression).expression;
+      argument = argument.expression;
     }
     // prepare argument properties
     Precedence argumentPrecedence;
-    String argumentSource;
+    String? argumentSource;
     if (argument != null) {
       argumentPrecedence = getExpressionPrecedence(argument);
       argumentSource = utils.getNodeText(argument);
@@ -149,7 +147,7 @@
   // local variables and functions
   {
     var localsRange = _getLocalsConflictingRange(node);
-    var enclosingExecutable = getEnclosingExecutableNode(node);
+    var enclosingExecutable = getEnclosingExecutableNode(node)!;
     var visibleRangeMap = VisibleRangesComputer.forNode(enclosingExecutable);
     visibleRangeMap.forEach((element, elementRange) {
       if (elementRange.intersects(localsRange)) {
@@ -183,23 +181,23 @@
   final ResolvedUnitResult resolveResult;
   final int offset;
   final AnalysisSessionHelper sessionHelper;
-  CorrectionUtils utils;
-  SourceChange change;
+  late CorrectionUtils utils;
+  late SourceChange change;
 
   @override
   bool isDeclaration = false;
   bool deleteSource = false;
   bool inlineAll = true;
 
-  ExecutableElement _methodElement;
-  CompilationUnit _methodUnit;
-  CorrectionUtils _methodUtils;
-  AstNode _methodNode;
-  FormalParameterList _methodParameters;
-  FunctionBody _methodBody;
-  Expression _methodExpression;
-  _SourcePart _methodExpressionPart;
-  _SourcePart _methodStatementsPart;
+  ExecutableElement? _methodElement;
+  late CompilationUnit _methodUnit;
+  late CorrectionUtils _methodUtils;
+  late AstNode _methodNode;
+  FormalParameterList? _methodParameters;
+  FunctionBody? _methodBody;
+  Expression? _methodExpression;
+  _SourcePart? _methodExpressionPart;
+  _SourcePart? _methodStatementsPart;
   final List<_ReferenceProcessor> _referenceProcessors = [];
   final Set<Element> _alreadyMadeAsync = <Element>{};
 
@@ -210,11 +208,8 @@
   }
 
   @override
-  String get className {
-    if (_methodElement == null) {
-      return null;
-    }
-    var classElement = _methodElement.enclosingElement;
+  String? get className {
+    var classElement = _methodElement?.enclosingElement;
     if (classElement is ClassElement) {
       return classElement.displayName;
     }
@@ -222,11 +217,8 @@
   }
 
   @override
-  String get methodName {
-    if (_methodElement == null) {
-      return null;
-    }
-    return _methodElement.displayName;
+  String? get methodName {
+    return _methodElement?.displayName;
   }
 
   @override
@@ -256,7 +248,7 @@
       var linesRange =
           _methodUtils.getLinesRange(methodRange, skipLeadingEmptyLines: true);
       doSourceChange_addElementEdit(
-          change, _methodElement, newSourceEdit_range(linesRange, ''));
+          change, _methodElement!, newSourceEdit_range(linesRange, ''));
     }
     // done
     return Future.value(result);
@@ -271,19 +263,19 @@
       return Future<RefactoringStatus>.value(result);
     }
     // maybe operator
-    if (_methodElement.isOperator) {
+    if (_methodElement!.isOperator) {
       result = RefactoringStatus.fatal('Cannot inline operator.');
       return Future<RefactoringStatus>.value(result);
     }
     // maybe [a]sync*
-    if (_methodElement.isGenerator) {
+    if (_methodElement!.isGenerator) {
       result = RefactoringStatus.fatal('Cannot inline a generator.');
       return Future<RefactoringStatus>.value(result);
     }
     // analyze method body
     result.addStatus(_prepareMethodParts());
     // process references
-    var references = await searchEngine.searchReferences(_methodElement);
+    var references = await searchEngine.searchReferences(_methodElement!);
     _referenceProcessors.clear();
     for (var reference in references) {
       var processor = _ReferenceProcessor(this, reference);
@@ -303,7 +295,7 @@
     var prefix = getLinePrefix(source);
     var result = _SourcePart(range.offset, source, prefix);
     // remember parameters and variables occurrences
-    _methodUnit.accept(_VariablesVisitor(_methodElement, range, result));
+    _methodUnit.accept(_VariablesVisitor(_methodElement!, range, result));
     // done
     return result;
   }
@@ -319,11 +311,10 @@
     var fatalStatus = RefactoringStatus.fatal(
         'Method declaration or reference must be selected to activate this refactoring.');
     // prepare selected SimpleIdentifier
-    var node = NodeLocator(offset).searchWithin(resolveResult.unit);
-    if (node is! SimpleIdentifier) {
+    var identifier = NodeLocator(offset).searchWithin(resolveResult.unit);
+    if (identifier is! SimpleIdentifier) {
       return fatalStatus;
     }
-    var identifier = node as SimpleIdentifier;
     // prepare selected ExecutableElement
     var element = identifier.writeOrReadElement;
     if (element is! ExecutableElement) {
@@ -332,14 +323,14 @@
     if (element.isSynthetic) {
       return fatalStatus;
     }
-    _methodElement = element as ExecutableElement;
+    _methodElement = element;
 
-    var declaration = await sessionHelper.getElementDeclaration(_methodElement);
-    var methodNode = declaration.node;
+    var declaration = await sessionHelper.getElementDeclaration(element);
+    var methodNode = declaration!.node;
     _methodNode = methodNode;
 
-    var resolvedUnit = declaration.resolvedUnit;
-    _methodUnit = resolvedUnit.unit;
+    var resolvedUnit = declaration.resolvedUnit!;
+    _methodUnit = resolvedUnit.unit!;
     _methodUtils = CorrectionUtils(resolvedUnit);
 
     if (methodNode is MethodDeclaration) {
@@ -353,7 +344,7 @@
     }
 
     isDeclaration = resolveResult.uri == element.source.uri &&
-        node.offset == element.nameOffset;
+        identifier.offset == element.nameOffset;
     deleteSource = isDeclaration;
     inlineAll = deleteSource;
     return RefactoringStatus();
@@ -366,7 +357,7 @@
     if (_methodBody is ExpressionFunctionBody) {
       var body = _methodBody as ExpressionFunctionBody;
       _methodExpression = body.expression;
-      var methodExpressionRange = range.node(_methodExpression);
+      var methodExpressionRange = range.node(_methodExpression!);
       _methodExpressionPart = _createSourcePart(methodExpressionRange);
     } else if (_methodBody is BlockFunctionBody) {
       var body = (_methodBody as BlockFunctionBody).block;
@@ -377,7 +368,7 @@
         if (lastStatement is ReturnStatement) {
           _methodExpression = lastStatement.expression;
           if (_methodExpression != null) {
-            var methodExpressionRange = range.node(_methodExpression);
+            var methodExpressionRange = range.node(_methodExpression!);
             _methodExpressionPart = _createSourcePart(methodExpressionRange);
           }
           // exclude "return" statement from statements
@@ -411,11 +402,11 @@
   final InlineMethodRefactoringImpl ref;
   final SearchMatch reference;
 
-  Element refElement;
-  CorrectionUtils _refUtils;
-  SimpleIdentifier _node;
-  SourceRange _refLineRange;
-  String _refPrefix;
+  late Element refElement;
+  late CorrectionUtils _refUtils;
+  late SimpleIdentifier _node;
+  SourceRange? _refLineRange;
+  late String _refPrefix;
 
   _ReferenceProcessor(this.ref, this.reference);
 
@@ -424,10 +415,11 @@
 
     // prepare CorrectionUtils
     var result = await ref.sessionHelper.getResolvedUnitByElement(refElement);
-    _refUtils = CorrectionUtils(result);
+    _refUtils = CorrectionUtils(result!);
 
     // prepare node and environment
-    _node = _refUtils.findNode(reference.sourceRange.offset);
+    _node =
+        _refUtils.findNode(reference.sourceRange.offset) as SimpleIdentifier;
     var refStatement = _node.thisOrAncestorOfType<Statement>();
     if (refStatement != null) {
       _refLineRange = _refUtils.getLinesRangeStatements([refStatement]);
@@ -454,7 +446,7 @@
     }
     // analyze point of invocation
     var parent = usage.parent;
-    var parent2 = parent.parent;
+    var parent2 = parent?.parent;
     // OK, if statement in block
     if (parent is Statement) {
       return parent2 is Block;
@@ -484,7 +476,7 @@
   }
 
   void _inlineMethodInvocation(RefactoringStatus status, Expression usage,
-      bool cascaded, Expression target, List<Expression> arguments) {
+      bool cascaded, Expression? target, List<Expression> arguments) {
     // we don't support cascade
     if (cascaded) {
       status.addError(
@@ -496,20 +488,20 @@
       if (ref._methodStatementsPart != null) {
         // prepare statements source for invocation
         var source = _getMethodSourceForInvocation(status,
-            ref._methodStatementsPart, _refUtils, usage, target, arguments);
+            ref._methodStatementsPart!, _refUtils, usage, target, arguments);
         source = _refUtils.replaceSourceIndent(
-            source, ref._methodStatementsPart._prefix, _refPrefix);
+            source, ref._methodStatementsPart!._prefix, _refPrefix);
         // do insert
         var edit =
-            newSourceEdit_range(SourceRange(_refLineRange.offset, 0), source);
+            newSourceEdit_range(SourceRange(_refLineRange!.offset, 0), source);
         _addRefEdit(edit);
       }
       // replace invocation with return expression
       if (ref._methodExpressionPart != null) {
         // prepare expression source for invocation
         var source = _getMethodSourceForInvocation(status,
-            ref._methodExpressionPart, _refUtils, usage, target, arguments);
-        if (getExpressionPrecedence(ref._methodExpression) <
+            ref._methodExpressionPart!, _refUtils, usage, target, arguments);
+        if (getExpressionPrecedence(ref._methodExpression!) <
             getExpressionParentPrecedence(usage)) {
           source = '($source)';
         }
@@ -518,7 +510,7 @@
         var edit = newSourceEdit_range(methodUsageRange, source);
         _addRefEdit(edit);
       } else {
-        var edit = newSourceEdit_range(_refLineRange, '');
+        var edit = newSourceEdit_range(_refLineRange!, '');
         _addRefEdit(edit);
       }
       return;
@@ -527,7 +519,7 @@
     String source;
     {
       source = ref._methodUtils.getRangeText(range.startEnd(
-          ref._methodParameters.leftParenthesis, ref._methodNode));
+          ref._methodParameters!.leftParenthesis, ref._methodNode));
       var methodPrefix = ref._methodUtils.getLinePrefix(ref._methodNode.offset);
       source = _refUtils.replaceSourceIndent(source, methodPrefix, _refPrefix);
       source = source.trim();
@@ -545,7 +537,7 @@
     }
     // If the element being inlined is async, ensure that the function
     // body that encloses the method is also async.
-    if (ref._methodElement.isAsynchronous) {
+    if (ref._methodElement!.isAsynchronous) {
       var body = _node.thisOrAncestorOfType<FunctionBody>();
       if (body != null) {
         if (body.isSynchronous) {
@@ -587,7 +579,7 @@
       // PropertyAccessorElement
       if (ref._methodElement is PropertyAccessorElement) {
         Expression usage = _node;
-        Expression target;
+        Expression? target;
         var cascade = false;
         if (nodeParent is PrefixedIdentifier) {
           var propertyAccess = nodeParent;
@@ -604,7 +596,7 @@
         // prepare arguments
         var arguments = <Expression>[];
         if (_node.inSetterContext()) {
-          var assignment = _node.thisOrAncestorOfType<AssignmentExpression>();
+          var assignment = _node.thisOrAncestorOfType<AssignmentExpression>()!;
           arguments.add(assignment.rightHandSide);
         }
         // inline body
@@ -615,13 +607,13 @@
       String source;
       {
         source = ref._methodUtils.getRangeText(range.startEnd(
-            ref._methodParameters.leftParenthesis, ref._methodNode));
+            ref._methodParameters!.leftParenthesis, ref._methodNode));
         var methodPrefix =
             ref._methodUtils.getLinePrefix(ref._methodNode.offset);
         source =
             _refUtils.replaceSourceIndent(source, methodPrefix, _refPrefix);
         source = source.trim();
-        source = removeEnd(source, ';');
+        source = removeEnd(source, ';')!;
       }
       // do insert
       var edit = newSourceEdit_range(range.node(_node), source);
@@ -700,15 +692,13 @@
 
   void addParameterOccurrence(ParameterElement parameter,
       SourceRange identifierRange, Precedence precedence) {
-    if (parameter != null) {
-      var occurrences = _parameters[parameter];
-      if (occurrences == null) {
-        occurrences = [];
-        _parameters[parameter] = occurrences;
-      }
-      identifierRange = range.offsetBy(identifierRange, -_base);
-      occurrences.add(_ParameterOccurrence(precedence, identifierRange));
+    var occurrences = _parameters[parameter];
+    if (occurrences == null) {
+      occurrences = [];
+      _parameters[parameter] = occurrences;
     }
+    identifierRange = range.offsetBy(identifierRange, -_base);
+    occurrences.add(_ParameterOccurrence(precedence, identifierRange));
   }
 
   void addVariable(VariableElement element, SourceRange identifierRange) {
@@ -733,8 +723,6 @@
   /// The [_SourcePart] to record reference into.
   final _SourcePart result;
 
-  int offset;
-
   _VariablesVisitor(this.methodElement, this.bodyRange, this.result);
 
   @override
@@ -766,23 +754,28 @@
 
   void _addMemberQualifier(SimpleIdentifier node) {
     // should be unqualified
-    AstNode qualifier = getNodeQualifier(node);
+    var qualifier = getNodeQualifier(node);
     if (qualifier != null) {
       return;
     }
     // should be a method or field reference
     var element = node.writeOrReadElement;
-    if (!(element is MethodElement || element is PropertyAccessorElement)) {
+    if (element is ExecutableElement) {
+      if (element is MethodElement || element is PropertyAccessorElement) {
+        // OK
+      } else {
+        return;
+      }
+    } else {
       return;
     }
     if (element.enclosingElement is! ClassElement) {
       return;
     }
     // record the implicit static or instance reference
-    ExecutableElement member = element;
     var offset = node.offset;
-    if (member.isStatic) {
-      var className = member.enclosingElement.displayName;
+    if (element.isStatic) {
+      var className = element.enclosingElement.displayName;
       result.addImplicitClassNameOffset(className, offset);
     } else {
       result.addImplicitThisOffset(offset);
@@ -807,7 +800,7 @@
   }
 
   void _addVariable(SimpleIdentifier node) {
-    VariableElement variableElement = getLocalVariableElement(node);
+    var variableElement = getLocalVariableElement(node);
     if (variableElement != null) {
       var nodeRange = range.node(node);
       result.addVariable(variableElement, nodeRange);
diff --git a/pkg/analysis_server/lib/src/services/refactoring/move_file.dart b/pkg/analysis_server/lib/src/services/refactoring/move_file.dart
index 93a7520..397af5c 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/move_file.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/move_file.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'dart:core';
 
 import 'package:analysis_server/src/protocol_server.dart' hide Element;
@@ -17,6 +15,7 @@
 import 'package:analyzer/src/generated/source.dart';
 import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
 import 'package:analyzer_plugin/utilities/range_factory.dart';
+import 'package:collection/collection.dart';
 import 'package:path/path.dart' as pathos;
 
 /// [MoveFileRefactoring] implementation.
@@ -26,10 +25,10 @@
   final pathos.Context pathContext;
   final RefactoringWorkspace refactoringWorkspace;
   final ResolvedUnitResult resolvedUnit;
-  AnalysisDriver driver;
+  late AnalysisDriver driver;
 
-  String oldFile;
-  String newFile;
+  late String oldFile;
+  late String newFile;
 
   final packagePrefixedStringPattern = RegExp(r'''^r?['"]+package:''');
 
@@ -43,7 +42,7 @@
   @override
   Future<RefactoringStatus> checkFinalConditions() async {
     for (var driver in refactoringWorkspace.drivers) {
-      var rootPath = driver.analysisContext.contextRoot.root.path;
+      var rootPath = driver.analysisContext!.contextRoot.root.path;
       if (pathContext.equals(rootPath, oldFile)) {
         return RefactoringStatus.fatal(
             'Renaming an analysis root is not supported ($oldFile)');
@@ -72,13 +71,12 @@
   @override
   Future<SourceChange> createChange() async {
     var changeBuilder = ChangeBuilder(session: resolvedUnit.session);
-    var element = resolvedUnit.unit.declaredElement;
+    var element = resolvedUnit.unit!.declaredElement;
     if (element == null) {
       return changeBuilder.sourceChange;
     }
 
     var libraryElement = element.library;
-    var libraryPath = libraryElement.source.fullName;
 
     final oldDir = pathContext.dirname(oldFile);
     final newDir = pathContext.dirname(newFile);
@@ -88,34 +86,33 @@
       // Handle part-of directives in this library
       var libraryResult = await driver.currentSession
           .getResolvedLibraryByElement(libraryElement);
-      ResolvedUnitResult definingUnitResult;
-      for (var result in libraryResult.units) {
+      var definingUnitResult = libraryResult.units!.first;
+      for (var result in libraryResult.units!) {
         if (result.isPart) {
-          var partOfs = result.unit.directives
+          var partOfs = result.unit!.directives
               .whereType<PartOfDirective>()
-              .where(
-                  (po) => po.uri != null && _isRelativeUri(po.uri.stringValue));
+              .map((e) => e.uri)
+              .whereNotNull()
+              .where((uri) => _isRelativeUri(uri.stringValue));
           if (partOfs.isNotEmpty) {
             await changeBuilder.addDartFileEdit(
-                result.unit.declaredElement.source.fullName, (builder) {
-              partOfs.forEach((po) {
+                result.unit!.declaredElement!.source.fullName, (builder) {
+              partOfs.forEach((uri) {
                 var newLocation =
                     pathContext.join(newDir, pathos.basename(newFile));
                 var newUri = _getRelativeUri(newLocation, oldDir);
                 builder.addSimpleReplacement(
-                    SourceRange(po.uri.offset, po.uri.length), "'$newUri'");
+                    SourceRange(uri.offset, uri.length), "'$newUri'");
               });
             });
           }
         }
-        if (result.path == libraryPath) {
-          definingUnitResult = result;
-        }
       }
 
       if (newDir != oldDir) {
-        await changeBuilder.addDartFileEdit(definingUnitResult.path, (builder) {
-          for (var directive in definingUnitResult.unit.directives) {
+        await changeBuilder.addDartFileEdit(definingUnitResult.path!,
+            (builder) {
+          for (var directive in definingUnitResult.unit!.directives) {
             if (directive is UriBasedDirective) {
               _updateUriReference(builder, directive, oldDir, newDir);
             }
@@ -124,17 +121,19 @@
       }
     } else if (newDir != oldDir) {
       // Otherwise, we need to update any relative part-of references.
-      var partOfs = resolvedUnit.unit.directives
+      var partOfs = resolvedUnit.unit!.directives
           .whereType<PartOfDirective>()
-          .where((po) => po.uri != null && _isRelativeUri(po.uri.stringValue));
+          .map((e) => e.uri)
+          .whereNotNull()
+          .where((uri) => _isRelativeUri(uri.stringValue));
 
       if (partOfs.isNotEmpty) {
         await changeBuilder.addDartFileEdit(element.source.fullName, (builder) {
-          partOfs.forEach((po) {
-            var oldLocation = pathContext.join(oldDir, po.uri.stringValue);
+          partOfs.forEach((uri) {
+            var oldLocation = pathContext.join(oldDir, uri.stringValue);
             var newUri = _getRelativeUri(oldLocation, newDir);
             builder.addSimpleReplacement(
-                SourceRange(po.uri.offset, po.uri.length), "'$newUri'");
+                SourceRange(uri.offset, uri.length), "'$newUri'");
           });
         });
       }
@@ -178,7 +177,7 @@
   }
 
   bool _isPackageReference(SourceReference reference) {
-    var source = reference.element.source;
+    var source = reference.element.source!;
     var quotedImportUri = source.contents.data.substring(reference.range.offset,
         reference.range.offset + reference.range.length);
     return packagePrefixedStringPattern.hasMatch(quotedImportUri);
@@ -189,7 +188,10 @@
   /// The following URI's are not relative:
   ///    `/absolute/path/file.dart`
   ///    `dart:math`
-  bool _isRelativeUri(String path) {
+  bool _isRelativeUri(String? path) {
+    if (path == null) {
+      return false;
+    }
     // absolute URI
     if (Uri.parse(path).isAbsolute) {
       return false;
diff --git a/pkg/analysis_server/lib/src/services/refactoring/refactoring.dart b/pkg/analysis_server/lib/src/services/refactoring/refactoring.dart
index fbd4d3b..31e5302 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/refactoring.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/refactoring.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analysis_server/src/services/correction/status.dart';
 import 'package:analysis_server/src/services/refactoring/convert_getter_to_method.dart';
 import 'package:analysis_server/src/services/refactoring/convert_method_to_getter.dart';
@@ -48,7 +46,7 @@
   /// [element] and all the corresponding hierarchy elements.
   factory ConvertMethodToGetterRefactoring(SearchEngine searchEngine,
       AnalysisSession session, ExecutableElement element) {
-    return ConvertMethodToGetterRefactoringImpl(searchEngine, element);
+    return ConvertMethodToGetterRefactoringImpl(searchEngine, session, element);
   }
 }
 
@@ -211,7 +209,7 @@
   int get referenceCount;
 
   /// Returns the name of the variable being inlined.
-  String get variableName;
+  String? get variableName;
 }
 
 /// [Refactoring] to inline an [ExecutableElement].
@@ -224,7 +222,7 @@
 
   /// The name of the class enclosing the method being inlined.
   /// If not a class member is being inlined, then `null`.
-  String get className;
+  String? get className;
 
   /// True if the method being inlined should be removed.
   /// It is an error if this field is `true` and [inlineAll] is `false`.
@@ -239,7 +237,7 @@
   bool get isDeclaration;
 
   /// The name of the method (or function) being inlined.
-  String get methodName;
+  String? get methodName;
 }
 
 /// [Refactoring] to move/rename a file.
@@ -300,65 +298,26 @@
 
   /// Whether the [element] is defined in a file that is in a context root.
   bool containsElement(Element element) {
-    return containsFile(element.source.fullName);
+    return containsFile(element.source!.fullName);
   }
 
   /// Whether the file with the given [path] is in a context root.
   bool containsFile(String path) {
     return drivers.any((driver) {
-      return driver.analysisContext.contextRoot.isAnalyzed(path);
+      return driver.analysisContext!.contextRoot.isAnalyzed(path);
     });
   }
 
   /// Returns the drivers that have [path] in a context root.
   Iterable<AnalysisDriver> driversContaining(String path) {
     return drivers.where((driver) {
-      return driver.analysisContext.contextRoot.isAnalyzed(path);
+      return driver.analysisContext!.contextRoot.isAnalyzed(path);
     });
   }
 }
 
 /// Abstract [Refactoring] for renaming some [Element].
 abstract class RenameRefactoring implements Refactoring {
-  /// Returns a new [RenameRefactoring] instance for renaming [element],
-  /// maybe `null` if there is no support for renaming [Element]s of the given
-  /// type.
-  factory RenameRefactoring(RefactoringWorkspace workspace,
-      ResolvedUnitResult resolvedUnit, Element element) {
-    var session = resolvedUnit.session;
-    if (element == null) {
-      return null;
-    }
-    if (element is PropertyAccessorElement) {
-      element = (element as PropertyAccessorElement).variable;
-    }
-    if (element.enclosingElement is CompilationUnitElement) {
-      return RenameUnitMemberRefactoringImpl(workspace, resolvedUnit, element);
-    }
-    if (element is ConstructorElement) {
-      return RenameConstructorRefactoringImpl(workspace, session, element);
-    }
-    if (element is ImportElement) {
-      return RenameImportRefactoringImpl(workspace, session, element);
-    }
-    if (element is LabelElement) {
-      return RenameLabelRefactoringImpl(workspace, element);
-    }
-    if (element is LibraryElement) {
-      return RenameLibraryRefactoringImpl(workspace, element);
-    }
-    if (element is LocalElement) {
-      return RenameLocalRefactoringImpl(workspace, element);
-    }
-    if (element.enclosingElement is ClassElement) {
-      return RenameClassMemberRefactoringImpl(workspace, session, element);
-    }
-    if (element.enclosingElement is ExtensionElement) {
-      return RenameExtensionMemberRefactoringImpl(workspace, session, element);
-    }
-    return null;
-  }
-
   /// Returns the human-readable description of the kind of element being
   /// renamed (such as “class” or “function type alias”).
   String get elementKindName;
@@ -378,10 +337,52 @@
   /// this level of checking.
   RefactoringStatus checkNewName();
 
+  /// Returns a new [RenameRefactoring] instance for renaming [element],
+  /// maybe `null` if there is no support for renaming [Element]s of the given
+  /// type.
+  static RenameRefactoring? create(RefactoringWorkspace workspace,
+      ResolvedUnitResult resolvedUnit, Element? element) {
+    var session = resolvedUnit.session;
+    if (element == null) {
+      return null;
+    }
+    if (element is PropertyAccessorElement) {
+      element = element.variable;
+    }
+    var enclosingElement = element.enclosingElement;
+    if (enclosingElement is CompilationUnitElement) {
+      return RenameUnitMemberRefactoringImpl(workspace, resolvedUnit, element);
+    }
+    if (element is ConstructorElement) {
+      return RenameConstructorRefactoringImpl(workspace, session, element);
+    }
+    if (element is ImportElement) {
+      return RenameImportRefactoringImpl(workspace, session, element);
+    }
+    if (element is LabelElement) {
+      return RenameLabelRefactoringImpl(workspace, element);
+    }
+    if (element is LibraryElement) {
+      return RenameLibraryRefactoringImpl(workspace, element);
+    }
+    if (element is LocalElement) {
+      return RenameLocalRefactoringImpl(workspace, session, element);
+    }
+    if (enclosingElement is ClassElement) {
+      return RenameClassMemberRefactoringImpl(
+          workspace, session, enclosingElement, element);
+    }
+    if (enclosingElement is ExtensionElement) {
+      return RenameExtensionMemberRefactoringImpl(
+          workspace, session, enclosingElement, element);
+    }
+    return null;
+  }
+
   /// Given a node/element, finds the best element to rename (for example
   /// the class when on the `new` keyword).
-  static RenameRefactoringElement getElementToRename(
-      AstNode node, Element element) {
+  static RenameRefactoringElement? getElementToRename(
+      AstNode node, Element? element) {
     var offset = node.offset;
     var length = node.length;
 
@@ -390,14 +391,15 @@
     }
 
     if (element is FieldFormalParameterElement) {
-      element = (element as FieldFormalParameterElement).field;
+      element = element.field;
     }
 
     // Use the prefix offset/length when renaming an import directive.
     if (node is ImportDirective && element is ImportElement) {
-      if (node.prefix != null) {
-        offset = node.prefix.offset;
-        length = node.prefix.length;
+      var prefix = node.prefix;
+      if (prefix != null) {
+        offset = prefix.offset;
+        length = prefix.length;
       } else {
         // -1 means the name does not exist yet.
         offset = -1;
@@ -414,6 +416,10 @@
       length = typeIdentifier.length;
     }
 
+    if (element == null) {
+      return null;
+    }
+
     return RenameRefactoringElement(element, offset, length);
   }
 }
diff --git a/pkg/analysis_server/lib/src/services/refactoring/refactoring_internal.dart b/pkg/analysis_server/lib/src/services/refactoring/refactoring_internal.dart
index dd05a9b..d34c39e 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/refactoring_internal.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/refactoring_internal.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'dart:collection';
 
 import 'package:analysis_server/src/protocol_server.dart' hide Element;
@@ -89,13 +87,13 @@
   }
 
   /// Adds the [SourceEdit] to replace this reference.
-  void addEdit(SourceChange change, String newText, {String id}) {
+  void addEdit(SourceChange change, String newText, {String? id}) {
     var edit = createEdit(newText, id: id);
     doSourceChange_addSourceEdit(change, unitSource, edit);
   }
 
   /// Returns the [SourceEdit] to replace this reference.
-  SourceEdit createEdit(String newText, {String id}) {
+  SourceEdit createEdit(String newText, {String? id}) {
     return newSourceEdit_range(range, newText, id: id);
   }
 
diff --git a/pkg/analysis_server/lib/src/services/refactoring/rename.dart b/pkg/analysis_server/lib/src/services/refactoring/rename.dart
index 35d547b..4f2271f 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/rename.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/rename.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analysis_server/src/protocol_server.dart' hide Element;
 import 'package:analysis_server/src/services/correction/status.dart';
 import 'package:analysis_server/src/services/correction/util.dart';
@@ -23,7 +21,7 @@
   RenameProcessor(this.workspace, this.change, this.newName);
 
   /// Add the edit that updates the [element] declaration.
-  void addDeclarationEdit(Element element) {
+  void addDeclarationEdit(Element? element) {
     if (element != null && workspace.containsElement(element)) {
       var edit = newSourceEdit_range(range.elementName(element), newName);
       doSourceChange_addElementEdit(change, element, edit);
@@ -60,9 +58,9 @@
   final String elementKindName;
   @override
   final String oldName;
-  SourceChange change;
+  late SourceChange change;
 
-  String newName;
+  late String newName;
 
   RenameRefactoringImpl(this.workspace, Element element)
       : searchEngine = workspace.searchEngine,
@@ -75,7 +73,7 @@
   @override
   Future<RefactoringStatus> checkInitialConditions() {
     var result = RefactoringStatus();
-    if (element.source.isInSystemLibrary) {
+    if (element.source!.isInSystemLibrary) {
       var message = format(
           "The {0} '{1}' is defined in the SDK, so cannot be renamed.",
           getElementKindName(element),
diff --git a/pkg/analysis_server/lib/src/services/refactoring/rename_class_member.dart b/pkg/analysis_server/lib/src/services/refactoring/rename_class_member.dart
index eb2ab3c..422d944 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/rename_class_member.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/rename_class_member.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analysis_server/src/protocol_server.dart'
     hide Element, ElementKind;
 import 'package:analysis_server/src/services/correction/status.dart';
@@ -30,7 +28,7 @@
     AnalysisSessionHelper sessionHelper,
     ClassElement classElement,
     String name) {
-  return _ClassMemberValidator.forCreate(
+  return _CreateClassMemberValidator(
           searchEngine, sessionHelper, classElement, name)
       .validate();
 }
@@ -38,11 +36,12 @@
 /// A [Refactoring] for renaming class member [Element]s.
 class RenameClassMemberRefactoringImpl extends RenameRefactoringImpl {
   final AnalysisSessionHelper sessionHelper;
+  final ClassElement classElement;
 
-  _ClassMemberValidator _validator;
+  late _RenameClassMemberValidator _validator;
 
-  RenameClassMemberRefactoringImpl(
-      RefactoringWorkspace workspace, AnalysisSession session, Element element)
+  RenameClassMemberRefactoringImpl(RefactoringWorkspace workspace,
+      AnalysisSession session, this.classElement, Element element)
       : sessionHelper = AnalysisSessionHelper(session),
         super(workspace, element);
 
@@ -59,8 +58,8 @@
 
   @override
   Future<RefactoringStatus> checkFinalConditions() {
-    _validator = _ClassMemberValidator.forRename(
-        searchEngine, sessionHelper, element, newName);
+    _validator = _RenameClassMemberValidator(
+        searchEngine, sessionHelper, classElement, element, newName);
     return _validator.validate();
   }
 
@@ -126,37 +125,27 @@
   }
 }
 
-/// Helper to check if the created or renamed [Element] will cause any
-/// conflicts.
-class _ClassMemberValidator {
+/// The base class for the create and rename validators.
+class _BaseClassMemberValidator {
   final SearchEngine searchEngine;
   final AnalysisSessionHelper sessionHelper;
-  final LibraryElement library;
-  final Element element;
   final ClassElement elementClass;
   final ElementKind elementKind;
   final String name;
-  final bool isRename;
 
   final RefactoringStatus result = RefactoringStatus();
-  Set<Element> elements = <Element>{};
-  List<SearchMatch> references = <SearchMatch>[];
 
-  _ClassMemberValidator.forCreate(
-      this.searchEngine, this.sessionHelper, this.elementClass, this.name)
-      : isRename = false,
-        library = null,
-        element = null,
-        elementKind = ElementKind.METHOD;
+  _BaseClassMemberValidator(
+    this.searchEngine,
+    this.sessionHelper,
+    this.elementClass,
+    this.elementKind,
+    this.name,
+  );
 
-  _ClassMemberValidator.forRename(
-      this.searchEngine, this.sessionHelper, this.element, this.name)
-      : isRename = true,
-        library = element.library,
-        elementClass = element.enclosingElement,
-        elementKind = element.kind;
+  LibraryElement get library => elementClass.library;
 
-  Future<RefactoringStatus> validate() async {
+  void _checkClassAlreadyDeclares() {
     // check if there is a member with "newName" in the same ClassElement
     for (var newNameMember in getChildren(elementClass, name)) {
       result.addError(
@@ -167,47 +156,13 @@
               name),
           newLocation_fromElement(newNameMember));
     }
-    // do chained computations
-    var superClasses = getSuperClasses(elementClass);
-    await _prepareReferences();
-    var subClasses = await searchEngine.searchAllSubtypes(elementClass);
-    // check shadowing of class names
-    if (element != null) {
-      for (var element in elements) {
-        ClassElement clazz = element.enclosingElement;
-        if (clazz.name == name) {
-          result.addError(
-              format(
-                  "Renamed {0} has the same name as the declaring class '{1}'.",
-                  elementKind.displayName,
-                  name),
-              newLocation_fromElement(element));
-        }
-      }
-    } else {
-      if (elementClass.name == name) {
-        result.addError(
-            format(
-                "Created {0} has the same name as the declaring class '{1}'.",
-                elementKind.displayName,
-                name),
-            newLocation_fromElement(elementClass));
-      }
-    }
-    // usage of the renamed Element is shadowed by a local element
-    {
-      var conflict = await _getShadowingLocalElement();
-      if (conflict != null) {
-        var localElement = conflict.localElement;
-        result.addError(
-            format(
-                "Usage of renamed {0} will be shadowed by {1} '{2}'.",
-                elementKind.displayName,
-                getElementKindName(localElement),
-                localElement.displayName),
-            newLocation_fromMatch(conflict.match));
-      }
-    }
+  }
+
+  Future<void> _checkHierarchy({
+    required bool isRename,
+    required Set<ClassElement> superClasses,
+    required Set<ClassElement> subClasses,
+  }) async {
     // check shadowing in the hierarchy
     var declarations = await searchEngine.searchMemberDeclarations(name);
     for (var declaration in declarations) {
@@ -236,25 +191,152 @@
             newLocation_fromElement(nameElement));
       }
     }
-    // visibility
-    if (isRename) {
-      _validateWillBeInvisible();
+  }
+}
+
+/// Helper to check if the created element will cause any conflicts.
+class _CreateClassMemberValidator extends _BaseClassMemberValidator {
+  _CreateClassMemberValidator(
+      SearchEngine searchEngine,
+      AnalysisSessionHelper sessionHelper,
+      ClassElement elementClass,
+      String name)
+      : super(
+          searchEngine,
+          sessionHelper,
+          elementClass,
+          ElementKind.METHOD,
+          name,
+        );
+
+  Future<RefactoringStatus> validate() async {
+    _checkClassAlreadyDeclares();
+    // do chained computations
+    var superClasses = getSuperClasses(elementClass);
+    var subClasses = await searchEngine.searchAllSubtypes(elementClass);
+    // check shadowing of class names
+    if (elementClass.name == name) {
+      result.addError(
+          format("Created {0} has the same name as the declaring class '{1}'.",
+              elementKind.displayName, name),
+          newLocation_fromElement(elementClass));
     }
+    // check shadowing in the hierarchy
+    await _checkHierarchy(
+      isRename: false,
+      superClasses: superClasses,
+      subClasses: subClasses,
+    );
+    // done
+    return result;
+  }
+}
+
+class _LocalElementsCollector extends GeneralizingAstVisitor<void> {
+  final String name;
+  final List<LocalElement> elements = [];
+
+  _LocalElementsCollector(this.name);
+
+  @override
+  void visitSimpleIdentifier(SimpleIdentifier node) {
+    var element = node.staticElement;
+    if (element is LocalElement && element.name == name) {
+      elements.add(element);
+    }
+  }
+}
+
+class _MatchShadowedByLocal {
+  final SearchMatch match;
+  final LocalElement localElement;
+
+  _MatchShadowedByLocal(this.match, this.localElement);
+}
+
+/// Helper to check if the renamed [element] will cause any conflicts.
+class _RenameClassMemberValidator extends _BaseClassMemberValidator {
+  final Element element;
+
+  Set<Element> elements = <Element>{};
+  List<SearchMatch> references = <SearchMatch>[];
+
+  _RenameClassMemberValidator(
+    SearchEngine searchEngine,
+    AnalysisSessionHelper sessionHelper,
+    ClassElement elementClass,
+    this.element,
+    String name,
+  ) : super(searchEngine, sessionHelper, elementClass, element.kind, name);
+
+  Future<RefactoringStatus> validate() async {
+    _checkClassAlreadyDeclares();
+    // do chained computations
+    var superClasses = getSuperClasses(elementClass);
+    await _prepareReferences();
+    var subClasses = await searchEngine.searchAllSubtypes(elementClass);
+    // check shadowing of class names
+    for (var element in elements) {
+      var enclosingElement = element.enclosingElement;
+      if (enclosingElement is ClassElement && enclosingElement.name == name) {
+        result.addError(
+          format(
+            "Renamed {0} has the same name as the declaring class '{1}'.",
+            elementKind.displayName,
+            name,
+          ),
+          newLocation_fromElement(element),
+        );
+      }
+    }
+    // usage of the renamed Element is shadowed by a local element
+    {
+      var conflict = await _getShadowingLocalElement();
+      if (conflict != null) {
+        var localElement = conflict.localElement;
+        result.addError(
+            format(
+                "Usage of renamed {0} will be shadowed by {1} '{2}'.",
+                elementKind.displayName,
+                getElementKindName(localElement),
+                localElement.displayName),
+            newLocation_fromMatch(conflict.match));
+      }
+    }
+    // check shadowing in the hierarchy
+    await _checkHierarchy(
+      isRename: true,
+      superClasses: superClasses,
+      subClasses: subClasses,
+    );
+    // visibility
+    _validateWillBeInvisible();
     // done
     return result;
   }
 
-  Future<_MatchShadowedByLocal> _getShadowingLocalElement() async {
+  Future<_MatchShadowedByLocal?> _getShadowingLocalElement() async {
     var localElementMap = <CompilationUnitElement, List<LocalElement>>{};
     var visibleRangeMap = <LocalElement, SourceRange>{};
 
     Future<List<LocalElement>> getLocalElements(Element element) async {
       var unitElement = element.thisOrAncestorOfType<CompilationUnitElement>();
+      if (unitElement == null) {
+        return const [];
+      }
+
       var localElements = localElementMap[unitElement];
 
       if (localElements == null) {
         var result = await sessionHelper.getResolvedUnitByElement(element);
+        if (result == null) {
+          return const [];
+        }
+
         var unit = result.unit;
+        if (unit == null) {
+          return const [];
+        }
 
         var collector = _LocalElementsCollector(name);
         unit.accept(collector);
@@ -287,6 +369,7 @@
 
   /// Fills [elements] with [Element]s to rename.
   Future _prepareElements() async {
+    final element = this.element;
     if (element is ClassMemberElement) {
       elements = await getHierarchyMembers(searchEngine, element);
     } else {
@@ -296,9 +379,6 @@
 
   /// Fills [references] with all references to [elements].
   Future _prepareReferences() async {
-    if (!isRename) {
-      return Future.value();
-    }
     await _prepareElements();
     await Future.forEach(elements, (Element element) async {
       var elementReferences = await searchEngine.searchReferences(element);
@@ -313,7 +393,7 @@
     }
     for (var reference in references) {
       var refElement = reference.element;
-      var refLibrary = refElement.library;
+      var refLibrary = refElement.library!;
       if (refLibrary != library) {
         var message = format("Renamed {0} will be invisible in '{1}'.",
             getElementKindName(element), getElementQualifiedName(refLibrary));
@@ -322,25 +402,3 @@
     }
   }
 }
-
-class _LocalElementsCollector extends GeneralizingAstVisitor<void> {
-  final String name;
-  final List<LocalElement> elements = [];
-
-  _LocalElementsCollector(this.name);
-
-  @override
-  void visitSimpleIdentifier(SimpleIdentifier node) {
-    var element = node.staticElement;
-    if (element is LocalElement && element.name == name) {
-      elements.add(element);
-    }
-  }
-}
-
-class _MatchShadowedByLocal {
-  final SearchMatch match;
-  final LocalElement localElement;
-
-  _MatchShadowedByLocal(this.match, this.localElement);
-}
diff --git a/pkg/analysis_server/lib/src/services/refactoring/rename_constructor.dart b/pkg/analysis_server/lib/src/services/refactoring/rename_constructor.dart
index 3d6edf8..dd17e71 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/rename_constructor.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/rename_constructor.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analysis_server/src/protocol_server.dart' hide Element;
 import 'package:analysis_server/src/services/correction/status.dart';
 import 'package:analysis_server/src/services/correction/util.dart';
@@ -48,9 +46,7 @@
   RefactoringStatus checkNewName() {
     var result = super.checkNewName();
     result.addStatus(validateConstructorName(newName));
-    if (newName != null) {
-      _analyzePossibleConflicts(result);
-    }
+    _analyzePossibleConflicts(result);
     return result;
   }
 
@@ -90,10 +86,11 @@
   SourceReference _createDeclarationReference() {
     SourceRange sourceRange;
     var offset = element.periodOffset;
+    var nameEnd = element.nameEnd!;
     if (offset != null) {
-      sourceRange = range.startOffsetEndOffset(offset, element.nameEnd);
+      sourceRange = range.startOffsetEndOffset(offset, nameEnd);
     } else {
-      sourceRange = SourceRange(element.nameEnd, 0);
+      sourceRange = SourceRange(nameEnd, 0);
     }
     return SourceReference(SearchMatchImpl(
         element.source.fullName,
@@ -112,17 +109,35 @@
 
     var result = await AnalysisSessionHelper(session)
         .getElementDeclaration(classElement);
-    ClassDeclaration classNode = result.node;
-    var utils = CorrectionUtils(result.resolvedUnit);
+    if (result == null) {
+      return;
+    }
+
+    var classNode = result.node;
+    if (classNode is! ClassDeclaration) {
+      return;
+    }
+
+    var resolvedUnit = result.resolvedUnit;
+    if (resolvedUnit == null) {
+      return;
+    }
+
+    var utils = CorrectionUtils(resolvedUnit);
     var location = utils.prepareNewConstructorLocation(classNode);
+    if (location == null) {
+      return;
+    }
+
+    var header = '${classElement.name}.$newName();';
     doSourceChange_addElementEdit(
-        change,
-        classElement,
-        SourceEdit(
-            location.offset,
-            0,
-            location.prefix +
-                '${classElement.name}.$newName();' +
-                location.suffix));
+      change,
+      classElement,
+      SourceEdit(
+        location.offset,
+        0,
+        location.prefix + header + location.suffix,
+      ),
+    );
   }
 }
diff --git a/pkg/analysis_server/lib/src/services/refactoring/rename_extension_member.dart b/pkg/analysis_server/lib/src/services/refactoring/rename_extension_member.dart
index 2665ac7..5367235 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/rename_extension_member.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/rename_extension_member.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analysis_server/src/protocol_server.dart'
     hide Element, ElementKind;
 import 'package:analysis_server/src/services/correction/status.dart';
@@ -25,11 +23,12 @@
 /// A [Refactoring] for renaming extension member [Element]s.
 class RenameExtensionMemberRefactoringImpl extends RenameRefactoringImpl {
   final AnalysisSessionHelper sessionHelper;
+  final ExtensionElement extensionElement;
 
-  _ExtensionMemberValidator _validator;
+  late _ExtensionMemberValidator _validator;
 
-  RenameExtensionMemberRefactoringImpl(
-      RefactoringWorkspace workspace, AnalysisSession session, Element element)
+  RenameExtensionMemberRefactoringImpl(RefactoringWorkspace workspace,
+      AnalysisSession session, this.extensionElement, Element element)
       : sessionHelper = AnalysisSessionHelper(session),
         super(workspace, element);
 
@@ -47,7 +46,7 @@
   @override
   Future<RefactoringStatus> checkFinalConditions() {
     _validator = _ExtensionMemberValidator.forRename(
-        searchEngine, sessionHelper, element, newName);
+        searchEngine, sessionHelper, extensionElement, element, newName);
     return _validator.validate();
   }
 
@@ -105,11 +104,10 @@
   final RefactoringStatus result = RefactoringStatus();
   final List<SearchMatch> references = <SearchMatch>[];
 
-  _ExtensionMemberValidator.forRename(
-      this.searchEngine, this.sessionHelper, this.element, this.name)
+  _ExtensionMemberValidator.forRename(this.searchEngine, this.sessionHelper,
+      this.elementExtension, this.element, this.name)
       : isRename = true,
-        library = element.library,
-        elementExtension = element.enclosingElement,
+        library = elementExtension.library,
         elementKind = element.kind;
 
   Future<RefactoringStatus> validate() async {
@@ -148,17 +146,28 @@
     return result;
   }
 
-  Future<_MatchShadowedByLocal> _getShadowingLocalElement() async {
+  Future<_MatchShadowedByLocal?> _getShadowingLocalElement() async {
     var localElementMap = <CompilationUnitElement, List<LocalElement>>{};
     var visibleRangeMap = <LocalElement, SourceRange>{};
 
     Future<List<LocalElement>> getLocalElements(Element element) async {
       var unitElement = element.thisOrAncestorOfType<CompilationUnitElement>();
+      if (unitElement == null) {
+        return const [];
+      }
+
       var localElements = localElementMap[unitElement];
 
       if (localElements == null) {
         var result = await sessionHelper.getResolvedUnitByElement(element);
+        if (result == null) {
+          return const [];
+        }
+
         var unit = result.unit;
+        if (unit == null) {
+          return const [];
+        }
 
         var collector = _LocalElementsCollector(name);
         unit.accept(collector);
diff --git a/pkg/analysis_server/lib/src/services/refactoring/rename_import.dart b/pkg/analysis_server/lib/src/services/refactoring/rename_import.dart
index 04eeea2..5de4b4b 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/rename_import.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/rename_import.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analysis_server/src/protocol_server.dart';
 import 'package:analysis_server/src/services/correction/status.dart';
 import 'package:analysis_server/src/services/refactoring/naming_conventions.dart';
@@ -53,6 +51,11 @@
       var prefix = element.prefix;
       SourceEdit edit;
       if (newName.isEmpty) {
+        // We should not get `prefix == null` because we check in
+        // `checkNewName` that the new name is different.
+        if (prefix == null) {
+          return;
+        }
         var node = _findNode();
         var uriEnd = node.uri.end;
         var prefixEnd = element.prefixOffset + prefix.nameLength;
@@ -69,9 +72,7 @@
           edit = newSourceEdit_range(SourceRange(offset, length), newName);
         }
       }
-      if (edit != null) {
-        doSourceChange_addElementEdit(change, element, edit);
-      }
+      doSourceChange_addElementEdit(change, element, edit);
     }
     // update references
     var matches = await searchEngine.searchReferences(element);
@@ -108,8 +109,8 @@
   /// If the given [reference] is before an interpolated [SimpleIdentifier] in
   /// an [InterpolationExpression] without surrounding curly brackets, return
   /// it. Otherwise return `null`.
-  SimpleIdentifier _getInterpolationIdentifier(SourceReference reference) {
-    var source = reference.element.source;
+  SimpleIdentifier? _getInterpolationIdentifier(SourceReference reference) {
+    var source = reference.element.source!;
     var unit = session.getParsedUnit(source.fullName).unit;
     var nodeLocator = NodeLocator(reference.range.offset);
     var node = nodeLocator.searchWithin(unit);
diff --git a/pkg/analysis_server/lib/src/services/refactoring/rename_label.dart b/pkg/analysis_server/lib/src/services/refactoring/rename_label.dart
index 86873b0..d32922a 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/rename_label.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/rename_label.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analysis_server/src/services/correction/status.dart';
 import 'package:analysis_server/src/services/refactoring/naming_conventions.dart';
 import 'package:analysis_server/src/services/refactoring/refactoring.dart';
diff --git a/pkg/analysis_server/lib/src/services/refactoring/rename_library.dart b/pkg/analysis_server/lib/src/services/refactoring/rename_library.dart
index 1338bbc..68b8849 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/rename_library.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/rename_library.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analysis_server/src/services/correction/status.dart';
 import 'package:analysis_server/src/services/refactoring/naming_conventions.dart';
 import 'package:analysis_server/src/services/refactoring/refactoring.dart';
diff --git a/pkg/analysis_server/lib/src/services/refactoring/rename_local.dart b/pkg/analysis_server/lib/src/services/refactoring/rename_local.dart
index 04fa336..eb2745e 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/rename_local.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/rename_local.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analysis_server/src/protocol_server.dart'
     hide Element, ElementKind;
 import 'package:analysis_server/src/services/correction/status.dart';
@@ -13,6 +11,7 @@
 import 'package:analysis_server/src/services/refactoring/rename.dart';
 import 'package:analysis_server/src/services/refactoring/visible_ranges_computer.dart';
 import 'package:analysis_server/src/services/search/hierarchy.dart';
+import 'package:analyzer/dart/analysis/session.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/ast/visitor.dart';
 import 'package:analyzer/dart/element/element.dart';
@@ -25,9 +24,9 @@
 
   List<LocalElement> elements = [];
 
-  RenameLocalRefactoringImpl(
-      RefactoringWorkspace workspace, LocalElement element)
-      : sessionHelper = AnalysisSessionHelper(element.session),
+  RenameLocalRefactoringImpl(RefactoringWorkspace workspace,
+      AnalysisSession session, LocalElement element)
+      : sessionHelper = AnalysisSessionHelper(session),
         super(workspace, element);
 
   @override
@@ -50,8 +49,8 @@
     await _prepareElements();
     for (var element in elements) {
       var resolvedUnit = await sessionHelper.getResolvedUnitByElement(element);
-      var unit = resolvedUnit.unit;
-      unit.accept(
+      var unit = resolvedUnit?.unit;
+      unit?.accept(
         _ConflictValidatorVisitor(
           result,
           newName,
@@ -94,7 +93,7 @@
 
   /// Fills [elements] with [Element]s to rename.
   Future _prepareElements() async {
-    Element element = this.element;
+    var element = this.element;
     if (element is ParameterElement && element.isNamed) {
       elements = await getHierarchyNamedParameters(searchEngine, element);
     } else {
@@ -141,7 +140,7 @@
         nodeElement = getSyntheticAccessorVariable(nodeElement);
         var nodeKind = nodeElement.kind.displayName;
         var nodeName = getElementQualifiedName(nodeElement);
-        var nameElementSourceName = nodeElement.source.shortName;
+        var nameElementSourceName = nodeElement.source!.shortName;
         var refKind = target.kind.displayName;
         var message = 'Usage of $nodeKind "$nodeName" declared in '
             '"$nameElementSourceName" will be shadowed by renamed $refKind.';
@@ -150,7 +149,7 @@
     }
   }
 
-  SourceRange _getVisibleRange(LocalElement element) {
+  SourceRange? _getVisibleRange(LocalElement element) {
     return visibleRangeMap[element];
   }
 
@@ -167,6 +166,7 @@
   }
 
   static bool _isNamedExpressionName(SimpleIdentifier node) {
-    return node.parent is Label && node.parent.parent is NamedExpression;
+    var parent = node.parent;
+    return parent is Label && parent.parent is NamedExpression;
   }
 }
diff --git a/pkg/analysis_server/lib/src/services/refactoring/rename_unit_member.dart b/pkg/analysis_server/lib/src/services/refactoring/rename_unit_member.dart
index 2afbf50..113f8d8 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/rename_unit_member.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/rename_unit_member.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analysis_server/src/protocol_server.dart'
     show newLocation_fromElement, newLocation_fromMatch;
 import 'package:analysis_server/src/services/correction/status.dart';
@@ -23,7 +21,7 @@
 /// will cause any conflicts.
 Future<RefactoringStatus> validateCreateFunction(
     SearchEngine searchEngine, LibraryElement library, String name) {
-  return _RenameUnitMemberValidator.forCreate(
+  return _CreateUnitMemberValidator(
           searchEngine, library, ElementKind.FUNCTION, name)
       .validate();
 }
@@ -32,8 +30,7 @@
 /// will cause any conflicts.
 Future<RefactoringStatus> validateRenameTopLevel(
     SearchEngine searchEngine, Element element, String name) {
-  return _RenameUnitMemberValidator.forRename(searchEngine, element, name)
-      .validate();
+  return _RenameUnitMemberValidator(searchEngine, element, name).validate();
 }
 
 /// A [Refactoring] for renaming compilation unit member [Element]s.
@@ -42,10 +39,10 @@
 
   /// If the [element] is a Flutter `StatefulWidget` declaration, this is the
   /// corresponding `State` declaration.
-  ClassElement _flutterWidgetState;
+  ClassElement? _flutterWidgetState;
 
   /// If [_flutterWidgetState] is set, this is the new name of it.
-  String _flutterWidgetStateNewName;
+  String? _flutterWidgetStateNewName;
 
   RenameUnitMemberRefactoringImpl(
       RefactoringWorkspace workspace, this.resolvedUnit, Element element)
@@ -68,13 +65,15 @@
   @override
   Future<RefactoringStatus> checkFinalConditions() async {
     var status = await validateRenameTopLevel(searchEngine, element, newName);
-    if (_flutterWidgetState != null) {
+    final flutterWidgetState = _flutterWidgetState;
+    final flutterWidgetStateNewName = _flutterWidgetStateNewName;
+    if (flutterWidgetState != null && flutterWidgetStateNewName != null) {
       _updateFlutterWidgetStateName();
       status.addStatus(
         await validateRenameTopLevel(
           searchEngine,
-          _flutterWidgetState,
-          _flutterWidgetStateNewName,
+          flutterWidgetState,
+          flutterWidgetStateNewName,
         ),
       );
     }
@@ -130,74 +129,55 @@
     }
 
     // If a StatefulWidget is being renamed, rename also its State.
-    if (_flutterWidgetState != null) {
+    final flutterWidgetState = _flutterWidgetState;
+    if (flutterWidgetState != null) {
       _updateFlutterWidgetStateName();
       await RenameProcessor(
         workspace,
         change,
-        _flutterWidgetStateNewName,
-      ).renameElement(_flutterWidgetState);
+        _flutterWidgetStateNewName!,
+      ).renameElement(flutterWidgetState);
     }
   }
 
   void _findFlutterStateClass() {
     if (Flutter.instance.isStatefulWidgetDeclaration(element)) {
       var oldStateName = oldName + 'State';
-      _flutterWidgetState = element.library.getType(oldStateName) ??
-          element.library.getType('_' + oldStateName);
+      var library = element.library!;
+      _flutterWidgetState =
+          library.getType(oldStateName) ?? library.getType('_' + oldStateName);
     }
   }
 
   void _updateFlutterWidgetStateName() {
-    if (_flutterWidgetState != null) {
-      _flutterWidgetStateNewName = newName + 'State';
+    final flutterWidgetState = _flutterWidgetState;
+    if (flutterWidgetState != null) {
+      var flutterWidgetStateNewName = newName + 'State';
       // If the State was private, ensure that it stays private.
-      if (_flutterWidgetState.name.startsWith('_') &&
-          !_flutterWidgetStateNewName.startsWith('_')) {
-        _flutterWidgetStateNewName = '_' + _flutterWidgetStateNewName;
+      if (flutterWidgetState.name.startsWith('_') &&
+          !flutterWidgetStateNewName.startsWith('_')) {
+        flutterWidgetStateNewName = '_' + flutterWidgetStateNewName;
       }
+      _flutterWidgetStateNewName = flutterWidgetStateNewName;
     }
   }
 }
 
-/// Helper to check if the created or renamed [Element] will cause any
-/// conflicts.
-class _RenameUnitMemberValidator {
+/// The base class for the create and rename validators.
+class _BaseUnitMemberValidator {
   final SearchEngine searchEngine;
-  LibraryElement library;
-  Element element;
-  ElementKind elementKind;
+  final LibraryElement library;
+  final ElementKind elementKind;
   final String name;
-  final bool isRename;
-  List<SearchMatch> references = <SearchMatch>[];
 
   final RefactoringStatus result = RefactoringStatus();
 
-  _RenameUnitMemberValidator.forCreate(
-      this.searchEngine, this.library, this.elementKind, this.name)
-      : isRename = false;
-
-  _RenameUnitMemberValidator.forRename(
-      this.searchEngine, this.element, this.name)
-      : isRename = true {
-    library = element.library;
-    elementKind = element.kind;
-  }
-
-  Future<RefactoringStatus> validate() async {
-    _validateWillConflict();
-    if (isRename) {
-      references = await searchEngine.searchReferences(element);
-      _validateWillBeInvisible();
-      _validateWillBeShadowed();
-    }
-    await _validateWillShadow();
-    return result;
-  }
+  _BaseUnitMemberValidator(
+      this.searchEngine, this.library, this.elementKind, this.name);
 
   /// Returns `true` if [element] is visible at the given [SearchMatch].
   bool _isVisibleAt(Element element, SearchMatch at) {
-    var atLibrary = at.element.library;
+    var atLibrary = at.element.library!;
     // may be the same library
     if (library == atLibrary) {
       return true;
@@ -217,6 +197,90 @@
     return false;
   }
 
+  /// Validates if an element with the [name] will conflict with another
+  /// top-level [Element] in the same library.
+  void _validateWillConflict() {
+    visitLibraryTopLevelElements(library, (element) {
+      if (hasDisplayName(element, name)) {
+        var message = format("Library already declares {0} with name '{1}'.",
+            getElementKindName(element), name);
+        result.addError(message, newLocation_fromElement(element));
+      }
+    });
+  }
+
+  /// Validates if renamed [element] will shadow any [Element] named [name].
+  Future _validateWillShadow(Element? element) async {
+    var declarations = await searchEngine.searchMemberDeclarations(name);
+    for (var declaration in declarations) {
+      var member = declaration.element;
+      var declaringClass = member.enclosingElement as ClassElement;
+      var memberReferences = await searchEngine.searchReferences(member);
+      for (var memberReference in memberReferences) {
+        var refElement = memberReference.element;
+        // cannot be shadowed if qualified
+        if (memberReference.isQualified) {
+          continue;
+        }
+        // cannot be shadowed if declared in the same class as reference
+        var refClass = refElement.thisOrAncestorOfType<ClassElement>();
+        if (refClass == declaringClass) {
+          continue;
+        }
+        // ignore if not visible
+        if (element != null && !_isVisibleAt(element, memberReference)) {
+          continue;
+        }
+        // OK, reference will be shadowed be the element being renamed
+        var message = format(
+            element != null
+                ? "Renamed {0} will shadow {1} '{2}'."
+                : "Created {0} will shadow {1} '{2}'.",
+            elementKind.displayName,
+            getElementKindName(member),
+            getElementQualifiedName(member));
+        result.addError(message, newLocation_fromMatch(memberReference));
+      }
+    }
+  }
+}
+
+/// Helper to check if the created element will cause any conflicts.
+class _CreateUnitMemberValidator extends _BaseUnitMemberValidator {
+  _CreateUnitMemberValidator(
+    SearchEngine searchEngine,
+    LibraryElement library,
+    ElementKind elementKind,
+    String name,
+  ) : super(searchEngine, library, elementKind, name);
+
+  Future<RefactoringStatus> validate() async {
+    _validateWillConflict();
+    await _validateWillShadow(null);
+    return result;
+  }
+}
+
+/// Helper to check if the renamed element will cause any conflicts.
+class _RenameUnitMemberValidator extends _BaseUnitMemberValidator {
+  final Element element;
+  List<SearchMatch> references = <SearchMatch>[];
+
+  _RenameUnitMemberValidator(
+    SearchEngine searchEngine,
+    this.element,
+    String name,
+  ) : super(searchEngine, element.library!, element.kind, name);
+
+  Future<RefactoringStatus> validate() async {
+    _validateWillConflict();
+    references = await searchEngine.searchReferences(element);
+    _validateWillBeInvisible();
+    _validateWillBeShadowed();
+    await _validateWillShadow(element);
+    return result;
+  }
+
   /// Validates if any usage of [element] renamed to [name] will be invisible.
   void _validateWillBeInvisible() {
     if (!Identifier.isPrivateName(name)) {
@@ -224,7 +288,7 @@
     }
     for (var reference in references) {
       var refElement = reference.element;
-      var refLibrary = refElement.library;
+      var refLibrary = refElement.library!;
       if (refLibrary != library) {
         var message = format("Renamed {0} will be invisible in '{1}'.",
             getElementKindName(element), getElementQualifiedName(refLibrary));
@@ -253,51 +317,4 @@
       }
     }
   }
-
-  /// Validates if [element] renamed to [name] will conflict with another
-  /// top-level [Element] in the same library.
-  void _validateWillConflict() {
-    visitLibraryTopLevelElements(library, (element) {
-      if (hasDisplayName(element, name)) {
-        var message = format("Library already declares {0} with name '{1}'.",
-            getElementKindName(element), name);
-        result.addError(message, newLocation_fromElement(element));
-      }
-    });
-  }
-
-  /// Validates if renamed [element] will shadow any [Element] named [name].
-  Future _validateWillShadow() async {
-    var declarations = await searchEngine.searchMemberDeclarations(name);
-    for (var declaration in declarations) {
-      var member = declaration.element;
-      ClassElement declaringClass = member.enclosingElement;
-      var memberReferences = await searchEngine.searchReferences(member);
-      for (var memberReference in memberReferences) {
-        var refElement = memberReference.element;
-        // cannot be shadowed if qualified
-        if (memberReference.isQualified) {
-          continue;
-        }
-        // cannot be shadowed if declared in the same class as reference
-        var refClass = refElement.thisOrAncestorOfType<ClassElement>();
-        if (refClass == declaringClass) {
-          continue;
-        }
-        // ignore if not visible
-        if (!_isVisibleAt(element, memberReference)) {
-          continue;
-        }
-        // OK, reference will be shadowed be the element being renamed
-        var message = format(
-            isRename
-                ? "Renamed {0} will shadow {1} '{2}'."
-                : "Created {0} will shadow {1} '{2}'.",
-            elementKind.displayName,
-            getElementKindName(member),
-            getElementQualifiedName(member));
-        result.addError(message, newLocation_fromMatch(memberReference));
-      }
-    }
-  }
 }
diff --git a/pkg/analysis_server/lib/src/status/diagnostics.dart b/pkg/analysis_server/lib/src/status/diagnostics.dart
index bfb39c7..86da984 100644
--- a/pkg/analysis_server/lib/src/status/diagnostics.dart
+++ b/pkg/analysis_server/lib/src/status/diagnostics.dart
@@ -24,6 +24,7 @@
 import 'package:analysis_server/src/status/pages.dart';
 import 'package:analysis_server/src/utilities/profiling.dart';
 import 'package:analyzer/dart/analysis/context_root.dart';
+import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/src/context/source.dart';
 import 'package:analyzer/src/dart/sdk/sdk.dart';
 import 'package:analyzer/src/dartdoc/dartdoc_directive_info.dart';
@@ -242,17 +243,16 @@
           raw: true);
       return;
     }
-    var result = await driver.getResult(filePath);
-    if (result == null) {
+    var result = await driver.getResult2(filePath);
+    if (result is ResolvedUnitResult) {
+      var writer = AstWriter(buf);
+      result.unit.accept(writer);
+    } else {
       p(
           'An AST could not be produced for the file '
           '<code>${escape(filePath)}</code>.',
           raw: true);
-      return;
     }
-
-    var writer = AstWriter(buf);
-    result.unit.accept(writer);
   }
 
   @override
@@ -829,17 +829,16 @@
           raw: true);
       return;
     }
-    var result = await driver.getResult(filePath);
-    if (result == null) {
+    var result = await driver.getResult2(filePath);
+    if (result is ResolvedUnitResult) {
+      var writer = ElementWriter(buf);
+      result.unit.declaredElement.accept(writer);
+    } else {
       p(
           'An element model could not be produced for the file '
           '<code>${escape(filePath)}</code>.',
           raw: true);
-      return;
     }
-
-    var writer = ElementWriter(buf);
-    result.unit.declaredElement.accept(writer);
   }
 
   @override
diff --git a/pkg/analysis_server/lib/src/status/pages.dart b/pkg/analysis_server/lib/src/status/pages.dart
index 61f4704..e626e02 100644
--- a/pkg/analysis_server/lib/src/status/pages.dart
+++ b/pkg/analysis_server/lib/src/status/pages.dart
@@ -118,7 +118,7 @@
     buf.writeln('</pre>');
   }
 
-  void prettyJson(Map<String, dynamic> data) {
+  void prettyJson(Object? data) {
     const jsonEncoder = JsonEncoder.withIndent('  ');
     pre(() {
       buf.write(jsonEncoder.convert(data));
diff --git a/pkg/analysis_server/lib/src/utilities/extensions/yaml.dart b/pkg/analysis_server/lib/src/utilities/extensions/yaml.dart
index dfe4983..474b711 100644
--- a/pkg/analysis_server/lib/src/utilities/extensions/yaml.dart
+++ b/pkg/analysis_server/lib/src/utilities/extensions/yaml.dart
@@ -23,16 +23,22 @@
         }
       }
     } else if (node is YamlMap) {
-      for (var entry in node.nodes.entries) {
+      var entries = node.nodes.entries.toList();
+      for (var i = 0; i < entries.length; i++) {
+        var entry = entries[i];
+        var nextEntryOffset = i + 1 < entries.length
+            ? (entries[i + 1].key as YamlNode).span.start.offset
+            : null;
         if ((entry.key as YamlNode).containsOffset(offset)) {
           return entry.key;
         }
         var value = entry.value;
         if (value.containsOffset(offset) ||
-            (value is YamlScalar && value.value == null)) {
-          // TODO(brianwilkerson) Testing for a null value probably gets
-          //  confused when there are multiple null values or when there is a
-          //  null value before the node that actually contains the offset.
+            (value is YamlScalar &&
+                value.value == null &&
+                // To match a null, we need to be the last node, or the offset
+                // needs to be before the next key.
+                (nextEntryOffset == null || offset < nextEntryOffset))) {
           return entry.value;
         }
       }
diff --git a/pkg/analysis_server/lib/src/utilities/flutter.dart b/pkg/analysis_server/lib/src/utilities/flutter.dart
index adaef36..cb513a3 100644
--- a/pkg/analysis_server/lib/src/utilities/flutter.dart
+++ b/pkg/analysis_server/lib/src/utilities/flutter.dart
@@ -266,7 +266,7 @@
   /// Return the instance creation expression that surrounds the given
   /// [node], if any, else null. The [node] may be the instance creation
   /// expression itself or the identifier that names the constructor.
-  InstanceCreationExpression? identifyNewExpression(AstNode node) {
+  InstanceCreationExpression? identifyNewExpression(AstNode? node) {
     InstanceCreationExpression? newExpr;
     if (node is SimpleIdentifier) {
       var parent = node.parent;
diff --git a/pkg/analysis_server/test/abstract_context.dart b/pkg/analysis_server/test/abstract_context.dart
index eeb1471..b2db401 100644
--- a/pkg/analysis_server/test/abstract_context.dart
+++ b/pkg/analysis_server/test/abstract_context.dart
@@ -90,7 +90,7 @@
     var analysisContext = contextFor(testPackageRootPath);
     var files = analysisContext.contextRoot.analyzedFiles().toList();
     for (var path in files) {
-      await analysisContext.currentSession.getResolvedUnit(path);
+      await analysisContext.currentSession.getResolvedUnit2(path);
     }
   }
 
@@ -171,7 +171,8 @@
 
   Future<ResolvedUnitResult> resolveFile(String path) async {
     path = convertPath(path);
-    return contextFor(path).currentSession.getResolvedUnit(path);
+    var session = contextFor(path).currentSession;
+    return await session.getResolvedUnit2(path) as ResolvedUnitResult;
   }
 
   @mustCallSuper
diff --git a/pkg/analysis_server/test/abstract_single_unit.dart b/pkg/analysis_server/test/abstract_single_unit.dart
index a889acf..e479d9c 100644
--- a/pkg/analysis_server/test/abstract_single_unit.dart
+++ b/pkg/analysis_server/test/abstract_single_unit.dart
@@ -70,7 +70,7 @@
   }
 
   Future<void> resolveTestFile() async {
-    var result = await session.getResolvedUnit(testFile);
+    var result = await session.getResolvedUnit2(testFile) as ResolvedUnitResult;
     testAnalysisResult = result;
     testCode = result.content!;
     testUnit = result.unit!;
diff --git a/pkg/analysis_server/test/domain_completion_test.dart b/pkg/analysis_server/test/domain_completion_test.dart
index e31fc32..8da9476 100644
--- a/pkg/analysis_server/test/domain_completion_test.dart
+++ b/pkg/analysis_server/test/domain_completion_test.dart
@@ -485,7 +485,7 @@
     expect(suggestions, isEmpty);
   }
 
-  Future<void> test_inComment_endOfFile() async {
+  Future<void> test_inComment_endOfFile_withNewline() async {
     addTestFile('''
     // text ^
   ''');
@@ -493,6 +493,12 @@
     expect(suggestions, isEmpty);
   }
 
+  Future<void> test_inComment_endOfFile_withoutNewline() async {
+    addTestFile('// text ^');
+    await getSuggestions();
+    expect(suggestions, isEmpty);
+  }
+
   Future<void> test_inComment_endOfLine_beforeNode() async {
     addTestFile('''
   main(aaa, bbb) {
diff --git a/pkg/analysis_server/test/edit/refactoring_test.dart b/pkg/analysis_server/test/edit/refactoring_test.dart
index 24d8e02..9baa5b3 100644
--- a/pkg/analysis_server/test/edit/refactoring_test.dart
+++ b/pkg/analysis_server/test/edit/refactoring_test.dart
@@ -2127,7 +2127,7 @@
   print(otherName);
 }
 ''');
-    server.getAnalysisDriver(testFile).getResult(testFile);
+    server.getAnalysisDriver(testFile).getResult2(testFile);
     // send the second request, with the same kind, file and offset
     await waitForTasksFinished();
     result = await getRefactoringResult(() {
diff --git a/pkg/analysis_server/test/lsp/completion.dart b/pkg/analysis_server/test/lsp/completion.dart
index 2e1e1e2..7d8dd01 100644
--- a/pkg/analysis_server/test/lsp/completion.dart
+++ b/pkg/analysis_server/test/lsp/completion.dart
@@ -13,7 +13,7 @@
   int sortTextSorter(CompletionItem item1, CompletionItem item2) =>
       (item1.sortText ?? item1.label).compareTo(item2.sortText ?? item2.label);
 
-  Future<void> verifyCompletions(
+  Future<String> verifyCompletions(
     Uri fileUri,
     String content, {
     List<String> expectCompletions,
@@ -22,6 +22,7 @@
     String expectedContent,
     String expectedContentIfInserting,
     bool verifyInsertReplaceRanges = false,
+    bool openCloseFile = true,
   }) async {
     // If verifyInsertReplaceRanges is true, we need both expected contents.
     assert(verifyInsertReplaceRanges == false ||
@@ -38,9 +39,13 @@
       await initialize(textDocumentCapabilities: textDocCapabilities);
     }
 
-    await openFile(fileUri, withoutMarkers(content));
+    if (openCloseFile) {
+      await openFile(fileUri, withoutMarkers(content));
+    }
     final res = await getCompletion(fileUri, positionFromMarker(content));
-    await closeFile(fileUri);
+    if (openCloseFile) {
+      await closeFile(fileUri);
+    }
 
     // Sort the completions by sortText and filter to those we expect, so the ordering
     // can be compared.
@@ -60,14 +65,16 @@
         item = await resolveCompletion(item);
       }
 
+      String updatedContent;
       if (verifyInsertReplaceRanges &&
           expectedContent != expectedContentIfInserting) {
         // Replacing.
-        final replaced = applyTextEdits(
+        updatedContent = applyTextEdits(
           withoutMarkers(content),
           [textEditForReplace(item.textEdit)],
         );
-        expect(withCaret(replaced, insertFormat), equals(expectedContent));
+        expect(
+            withCaret(updatedContent, insertFormat), equals(expectedContent));
 
         // Inserting.
         final inserted = applyTextEdits(
@@ -77,13 +84,19 @@
         expect(withCaret(inserted, insertFormat),
             equals(expectedContentIfInserting));
       } else {
-        final updated = applyTextEdits(
+        updatedContent = applyTextEdits(
           withoutMarkers(content),
           [toTextEdit(item.textEdit)],
         );
-        expect(withCaret(updated, insertFormat), equals(expectedContent));
+        if (expectedContent != null) {
+          expect(
+              withCaret(updatedContent, insertFormat), equals(expectedContent));
+        }
       }
+      return updatedContent;
     }
+
+    return null;
   }
 
   /// Replaces the LSP snippet placeholder '${0:}' with '^' for easier verifying
diff --git a/pkg/analysis_server/test/lsp/completion_dart_test.dart b/pkg/analysis_server/test/lsp/completion_dart_test.dart
index 07e00ed..faacf84 100644
--- a/pkg/analysis_server/test/lsp/completion_dart_test.dart
+++ b/pkg/analysis_server/test/lsp/completion_dart_test.dart
@@ -44,6 +44,42 @@
     );
   }
 
+  Future<void> test_comment() async {
+    final content = '''
+    // foo ^
+    main() {}
+    ''';
+
+    await initialize();
+    await openFile(mainFileUri, withoutMarkers(content));
+    final res = await getCompletion(mainFileUri, positionFromMarker(content));
+    expect(res, isEmpty);
+  }
+
+  Future<void> test_comment_endOfFile_withNewline() async {
+    // Checks for a previous bug where invoking completion inside a comment
+    // at the end of a file would return results.
+    final content = '''
+    // foo ^
+    ''';
+
+    await initialize();
+    await openFile(mainFileUri, withoutMarkers(content));
+    final res = await getCompletion(mainFileUri, positionFromMarker(content));
+    expect(res, isEmpty);
+  }
+
+  Future<void> test_comment_endOfFile_withoutNewline() async {
+    // Checks for a previous bug where invoking completion inside a comment
+    // at the very end of a file with no trailing newline would return results.
+    final content = '// foo ^';
+
+    await initialize();
+    await openFile(mainFileUri, withoutMarkers(content));
+    final res = await getCompletion(mainFileUri, positionFromMarker(content));
+    expect(res, isEmpty);
+  }
+
   Future<void> test_commitCharacter_completionItem() async {
     await provideConfig(
       () => initialize(
@@ -89,7 +125,7 @@
     );
 
     Registration registration(Method method) =>
-        registrationFor(registrations, method);
+        registrationForDart(registrations, method);
 
     // By default, there should be no commit characters.
     var reg = registration(Method.textDocument_completion);
@@ -344,6 +380,81 @@
     );
   }
 
+  Future<void> test_completionTrigger_brace_block() async {
+    // Brace should not trigger completion if a normal code block.
+    final content = r'''
+    main () {^}
+    ''';
+    await _checkResultsForTriggerCharacters(content, ['{'], isEmpty);
+  }
+
+  Future<void>
+      test_completionTrigger_brace_interpolatedStringExpression() async {
+    // Brace should trigger completion if at the start of an interpolated expression
+    final content = r'''
+    var a = '${^';
+    ''';
+    await _checkResultsForTriggerCharacters(content, [r'{'], isNotEmpty);
+  }
+
+  Future<void> test_completionTrigger_brace_rawString() async {
+    // Brace should not trigger completion if in a raw string.
+    final content = r'''
+    var a = r'${^';
+    ''';
+    await _checkResultsForTriggerCharacters(content, [r'{'], isEmpty);
+  }
+
+  Future<void> test_completionTrigger_brace_string() async {
+    // Brace should not trigger completion if not at the start of an interpolated
+    // expression.
+    final content = r'''
+    var a = '{^';
+    ''';
+    await _checkResultsForTriggerCharacters(content, [r'{'], isEmpty);
+  }
+
+  Future<void> test_completionTrigger_quotes_endingString() async {
+    // Completion triggered by a quote ending a string should not return results.
+    final content = "foo(''^);";
+    await _checkResultsForTriggerCharacters(content, ["'", '"'], isEmpty);
+  }
+
+  Future<void> test_completionTrigger_quotes_startingImport() async {
+    // Completion triggered by a quote for import should return results.
+    final content = "import '^'";
+    await _checkResultsForTriggerCharacters(content, ["'", '"'], isNotEmpty);
+  }
+
+  Future<void> test_completionTrigger_quotes_startingString() async {
+    // Completion triggered by a quote for normal string should not return results.
+    final content = "foo('^');";
+    await _checkResultsForTriggerCharacters(content, ["'", '"'], isEmpty);
+  }
+
+  Future<void> test_completionTrigger_quotes_terminatingImport() async {
+    // Completion triggered by a quote ending an import should not return results.
+    final content = "import ''^";
+    await _checkResultsForTriggerCharacters(content, ["'", '"'], isEmpty);
+  }
+
+  Future<void> test_completionTrigger_slash_directivePath() async {
+    // Slashes should trigger completion when typing in directive paths, eg.
+    // after typing 'package:foo/' completion should give the next folder segments.
+    final content = r'''
+    import 'package:test/^';
+    ''';
+    await _checkResultsForTriggerCharacters(content, [r'/'], isNotEmpty);
+  }
+
+  Future<void> test_completionTrigger_slash_divide() async {
+    // Slashes should not trigger completion when typing in a normal expression.
+    final content = r'''
+    var a = 1 /^
+    ''';
+    await _checkResultsForTriggerCharacters(content, [r'/'], isEmpty);
+  }
+
   Future<void> test_completionTriggerKinds_invalidParams() async {
     await initialize();
 
@@ -898,11 +1009,12 @@
     );
   }
 
-  Future<void> test_nonDartFile() async {
-    newFile(pubspecFilePath, content: simplePubspecContent);
+  Future<void> test_nonAnalyzedFile() async {
+    final readmeFilePath = convertPath(join(projectFolderPath, 'README.md'));
+    newFile(readmeFilePath, content: '');
     await initialize();
 
-    final res = await getCompletion(pubspecFileUri, startOfDocPos);
+    final res = await getCompletion(Uri.file(readmeFilePath), startOfDocPos);
     expect(res, isEmpty);
   }
 
@@ -1761,6 +1873,21 @@
     );
     expect(updated, contains('a.abcdefghij'));
   }
+
+  Future<void> _checkResultsForTriggerCharacters(String content,
+      List<String> triggerCharacters, Matcher expectedResults) async {
+    await initialize();
+    await openFile(mainFileUri, withoutMarkers(content));
+
+    for (final triggerCharacter in triggerCharacters) {
+      final context = CompletionContext(
+          triggerKind: CompletionTriggerKind.TriggerCharacter,
+          triggerCharacter: triggerCharacter);
+      final res = await getCompletion(mainFileUri, positionFromMarker(content),
+          context: context);
+      expect(res, expectedResults);
+    }
+  }
 }
 
 @reflectiveTest
diff --git a/pkg/analysis_server/test/lsp/completion_yaml_test.dart b/pkg/analysis_server/test/lsp/completion_yaml_test.dart
index a00dce5..878d367 100644
--- a/pkg/analysis_server/test/lsp/completion_yaml_test.dart
+++ b/pkg/analysis_server/test/lsp/completion_yaml_test.dart
@@ -7,6 +7,7 @@
 import 'package:analysis_server/src/services/pub/pub_api.dart';
 import 'package:http/http.dart';
 import 'package:linter/src/rules.dart';
+import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
 import 'completion.dart';
@@ -192,10 +193,14 @@
 @reflectiveTest
 class PubspecCompletionTest extends AbstractLspAnalysisServerTest
     with CompletionTestMixin {
+  /// Sample package name list JSON in the same format as the API:
+  /// https://pub.dev/api/package-name-completion-data
   static const samplePackageList = '''
   { "packages": ["one", "two", "three"] }
   ''';
 
+  /// Sample package details JSON in the same format as the API:
+  /// https://pub.dev/api/packages/devtools
   static const samplePackageDetails = '''
   {
     "name":"package",
@@ -297,6 +302,41 @@
     );
   }
 
+  Future<void> test_package_description() async {
+    httpClient.sendHandler = (BaseRequest request) async {
+      if (request.url.path.startsWith(PubApi.packageNameListPath)) {
+        return Response(samplePackageList, 200);
+      } else if (request.url.path.startsWith(PubApi.packageInfoPath)) {
+        return Response(samplePackageDetails, 200);
+      } else {
+        throw UnimplementedError();
+      }
+    };
+
+    final content = '''
+name: foo
+version: 1.0.0
+
+dependencies:
+  ^''';
+
+    await initialize();
+    await openFile(pubspecFileUri, withoutMarkers(content));
+    await pumpEventQueue();
+
+    // Descriptions are included in the documentation field that is only added
+    // when completions are resolved.
+    final completion = await getResolvedCompletion(
+      pubspecFileUri,
+      positionFromMarker(content),
+      'one: ',
+    );
+    expect(
+      completion.documentation.valueEquals('Description of package'),
+      isTrue,
+    );
+  }
+
   Future<void> test_package_names() async {
     httpClient.sendHandler = (BaseRequest request) async {
       if (request.url.toString().endsWith(PubApi.packageNameListPath)) {
@@ -329,6 +369,57 @@
     );
   }
 
+  Future<void> test_package_version() async {
+    httpClient.sendHandler = (BaseRequest request) async {
+      if (request.url.path.startsWith(PubApi.packageNameListPath)) {
+        return Response(samplePackageList, 200);
+      } else if (request.url.path.startsWith(PubApi.packageInfoPath)) {
+        return Response(samplePackageDetails, 200);
+      } else {
+        throw UnimplementedError();
+      }
+    };
+
+    final content = '''
+name: foo
+version: 1.0.0
+
+dependencies:
+  ^''';
+
+    final expected = '''
+name: foo
+version: 1.0.0
+
+dependencies:
+  one: ^1.2.3''';
+
+    await initialize();
+    await openFile(pubspecFileUri, withoutMarkers(content));
+
+    // Versions are currently only available if we've previously resolved on the
+    // package name, so first complete/resolve that.
+    final newContent = await verifyCompletions(
+      pubspecFileUri,
+      content,
+      expectCompletions: ['one: '],
+      resolve: true,
+      applyEditsFor: 'one: ',
+      openCloseFile: false,
+    );
+    await replaceFile(222, pubspecFileUri, newContent);
+
+    await verifyCompletions(
+      pubspecFileUri,
+      newContent.replaceFirst(
+          'one: ', 'one: ^'), // Insert caret at new location
+      expectCompletions: ['^1.2.3'],
+      applyEditsFor: '^1.2.3',
+      expectedContent: expected,
+      openCloseFile: false,
+    );
+  }
+
   Future<void> test_topLevel() async {
     final content = '''
 version: 1.0.0
diff --git a/pkg/analysis_server/test/lsp/initialization_test.dart b/pkg/analysis_server/test/lsp/initialization_test.dart
index fc6b1fc..93ecc62 100644
--- a/pkg/analysis_server/test/lsp/initialization_test.dart
+++ b/pkg/analysis_server/test/lsp/initialization_test.dart
@@ -31,6 +31,45 @@
         registrationFor(registrations, method)?.registerOptions);
   }
 
+  Future<void> test_completionRegistrations_triggerCharacters() async {
+    final registrations = <Registration>[];
+    final initResponse = await monitorDynamicRegistrations(
+      registrations,
+      () => initialize(
+        // Support dynamic registration for everything we support.
+        textDocumentCapabilities:
+            withAllSupportedTextDocumentDynamicRegistrations(
+                emptyTextDocumentClientCapabilities),
+        workspaceCapabilities: withAllSupportedWorkspaceDynamicRegistrations(
+            emptyWorkspaceClientCapabilities),
+      ),
+    );
+
+    final initResult = InitializeResult.fromJson(initResponse.result);
+    expect(initResult.capabilities, isNotNull);
+
+    // Check Dart-only registration.
+    final dartRegistration =
+        registrationForDart(registrations, Method.textDocument_completion);
+    final dartOptions = CompletionRegistrationOptions.fromJson(
+        dartRegistration.registerOptions);
+    expect(dartOptions.documentSelector, hasLength(1));
+    expect(dartOptions.documentSelector[0].language, dartLanguageId);
+    expect(dartOptions.triggerCharacters, isNotEmpty);
+
+    // Check non-Dart registration.
+    final nonDartRegistration = registrations.singleWhere((r) =>
+        r.method == Method.textDocument_completion.toJson() &&
+        r != dartRegistration);
+    final nonDartOptions = CompletionRegistrationOptions.fromJson(
+        nonDartRegistration.registerOptions);
+    final otherLanguages = nonDartOptions.documentSelector
+        .map((selector) => selector.language)
+        .toList();
+    expect(otherLanguages, isNot(contains('dart')));
+    expect(nonDartOptions.triggerCharacters, isNull);
+  }
+
   Future<void> test_dynamicRegistration_containsAppropriateSettings() async {
     // Basic check that the server responds with the capabilities we'd expect,
     // for ex including analysis_options.yaml in text synchronization but not
@@ -192,11 +231,22 @@
 
     // Ensure all expected dynamic registrations.
     for (final expectedRegistration in ClientDynamicRegistrations.supported) {
+      // We have two completion registrations (to handle different trigger
+      // characters), so exclude that here and check it manually below.
+      if (expectedRegistration == Method.textDocument_completion) {
+        continue;
+      }
+
       final registration =
           registrationOptionsFor(registrations, expectedRegistration);
       expect(registration, isNotNull,
           reason: 'Missing dynamic registration for $expectedRegistration');
     }
+
+    // Check the were two completion registrations.
+    final completionRegistrations = registrations
+        .where((reg) => reg.method == Method.textDocument_completion.toJson());
+    expect(completionRegistrations, hasLength(2));
   }
 
   Future<void> test_dynamicRegistration_unregistersOutdatedAfterChange() async {
diff --git a/pkg/analysis_server/test/lsp/server_abstract.dart b/pkg/analysis_server/test/lsp/server_abstract.dart
index b99c4fe..8091172 100644
--- a/pkg/analysis_server/test/lsp/server_abstract.dart
+++ b/pkg/analysis_server/test/lsp/server_abstract.dart
@@ -118,6 +118,21 @@
         orElse: () => null);
   }
 
+  /// Finds the registration for a given LSP method with Dart in its
+  /// documentSelector.
+  Registration registrationForDart(
+    List<Registration> registrations,
+    Method method,
+  ) {
+    return registrations.singleWhere(
+        (r) =>
+            r.method == method.toJson() &&
+            TextDocumentRegistrationOptions.fromJson(r.registerOptions)
+                .documentSelector
+                .any((selector) => selector.language == dartLanguageId),
+        orElse: () => null);
+  }
+
   void resetContextBuildCounter() {
     _previousContextBuilds = server.contextBuilds;
   }
diff --git a/pkg/analysis_server/test/mocks_lsp.dart b/pkg/analysis_server/test/mocks_lsp.dart
index 868b648..fdb402e 100644
--- a/pkg/analysis_server/test/mocks_lsp.dart
+++ b/pkg/analysis_server/test/mocks_lsp.dart
@@ -124,7 +124,7 @@
   Future<lsp.ResponseMessage> sendRequestToServer(lsp.RequestMessage request) {
     // No further requests should be sent after the connection is closed.
     if (_closed.isCompleted) {
-      throw Exception('sendLspRequest after connection closed');
+      throw Exception('${request.method} request sent after connection closed');
     }
 
     request = _convertJson(request, lsp.RequestMessage.fromJson);
diff --git a/pkg/analysis_server/test/services/completion/dart/completion_contributor_util.dart b/pkg/analysis_server/test/services/completion/dart/completion_contributor_util.dart
index c92a7b2..0a651c2 100644
--- a/pkg/analysis_server/test/services/completion/dart/completion_contributor_util.dart
+++ b/pkg/analysis_server/test/services/completion/dart/completion_contributor_util.dart
@@ -536,7 +536,7 @@
       DartCompletionRequest request);
 
   Future computeSuggestions({int times = 200}) async {
-    result = await session.getResolvedUnit(testFile);
+    result = await session.getResolvedUnit2(testFile) as ResolvedUnitResult;
     var baseRequest = CompletionRequestImpl(
         result, completionOffset, CompletionPerformance());
 
diff --git a/pkg/analysis_server/test/services/completion/dart/completion_manager_test.dart b/pkg/analysis_server/test/services/completion/dart/completion_manager_test.dart
index 0f6f5e7..4dd13ee 100644
--- a/pkg/analysis_server/test/services/completion/dart/completion_manager_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/completion_manager_test.dart
@@ -9,6 +9,7 @@
 import 'package:analysis_server/src/services/completion/completion_performance.dart';
 import 'package:analysis_server/src/services/completion/dart/completion_manager.dart';
 import 'package:analysis_server/src/services/completion/dart/imported_reference_contributor.dart';
+import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/src/dartdoc/dartdoc_directive_info.dart';
 import 'package:test/test.dart';
@@ -49,7 +50,7 @@
 
     // Build the request
     var baseRequest = CompletionRequestImpl(
-        await session.getResolvedUnit(testFile),
+        await session.getResolvedUnit2(testFile) as ResolvedUnitResult,
         completionOffset,
         CompletionPerformance());
     await baseRequest.performance.runRequestOperation((performance) async {
diff --git a/pkg/analysis_server/test/services/correction/organize_directives_test.dart b/pkg/analysis_server/test/services/correction/organize_directives_test.dart
index 56a370b..a147a80 100644
--- a/pkg/analysis_server/test/services/correction/organize_directives_test.dart
+++ b/pkg/analysis_server/test/services/correction/organize_directives_test.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analysis_server/src/services/correction/organize_imports.dart';
+import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/error/error.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart'
     hide AnalysisError;
@@ -581,7 +582,7 @@
 
   Future<void> _computeUnitAndErrors(String code) async {
     addTestSource(code);
-    var result = await session.getResolvedUnit(testFile);
+    var result = await session.getResolvedUnit2(testFile) as ResolvedUnitResult;
     testUnit = result.unit!;
     testErrors = result.errors;
   }
diff --git a/pkg/analysis_server/test/services/refactoring/abstract_refactoring.dart b/pkg/analysis_server/test/services/refactoring/abstract_refactoring.dart
index 7e982701..1ab4b27 100644
--- a/pkg/analysis_server/test/services/refactoring/abstract_refactoring.dart
+++ b/pkg/analysis_server/test/services/refactoring/abstract_refactoring.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analysis_server/src/services/correction/status.dart';
 import 'package:analysis_server/src/services/refactoring/refactoring.dart';
 import 'package:analysis_server/src/services/search/search_engine.dart';
@@ -34,9 +32,9 @@
 /// The base class for all [Refactoring] tests.
 abstract class RefactoringTest extends AbstractSingleUnitTest
     with WithNonFunctionTypeAliasesMixin {
-  SearchEngine searchEngine;
+  late SearchEngine searchEngine;
 
-  SourceChange refactoringChange;
+  late SourceChange refactoringChange;
 
   Refactoring get refactoring;
 
@@ -48,7 +46,9 @@
     }
     // prepare FileEdit
     var fileEdit = refactoringChange.getFileEdit(convertPath(path));
-    expect(fileEdit, isNotNull, reason: 'No file edit for $path');
+    if (fileEdit == null) {
+      fail('No file edit for $path');
+    }
     // validate resulting code
     var file = getFile(path);
     var ini = file.readAsStringSync();
@@ -79,26 +79,28 @@
 
   /// Asserts that [status] has expected severity and message.
   void assertRefactoringStatus(
-      RefactoringStatus status, RefactoringProblemSeverity expectedSeverity,
-      {String expectedMessage,
-      SourceRange expectedContextRange,
-      String expectedContextSearch}) {
+      RefactoringStatus status, RefactoringProblemSeverity? expectedSeverity,
+      {String? expectedMessage,
+      SourceRange? expectedContextRange,
+      String? expectedContextSearch}) {
     expect(status.severity, expectedSeverity, reason: status.toString());
     if (expectedSeverity != null) {
-      var problem = status.problem;
+      var problem = status.problem!;
       expect(problem.severity, expectedSeverity);
       if (expectedMessage != null) {
         expect(problem.message, expectedMessage);
       }
       if (expectedContextRange != null) {
-        expect(problem.location.offset, expectedContextRange.offset);
-        expect(problem.location.length, expectedContextRange.length);
+        var location = problem.location!;
+        expect(location.offset, expectedContextRange.offset);
+        expect(location.length, expectedContextRange.length);
       }
       if (expectedContextSearch != null) {
+        var location = problem.location!;
         var expectedOffset = findOffset(expectedContextSearch);
         var expectedLength = findIdentifierLength(expectedContextSearch);
-        expect(problem.location.offset, expectedOffset);
-        expect(problem.location.length, expectedLength);
+        expect(location.offset, expectedOffset);
+        expect(location.length, expectedLength);
       }
     }
   }
@@ -125,7 +127,9 @@
     }
     // prepare FileEdit
     var fileEdit = refactoringChange.getFileEdit(testFile);
-    expect(fileEdit, isNotNull);
+    if (fileEdit == null) {
+      fail('No file edit for $testFile');
+    }
     // validate resulting code
     var actualCode = SourceEdit.applySequence(testCode, fileEdit.edits);
     expect(actualCode, expectedCode);
diff --git a/pkg/analysis_server/test/services/refactoring/abstract_rename.dart b/pkg/analysis_server/test/services/refactoring/abstract_rename.dart
index 1437541..26676b3 100644
--- a/pkg/analysis_server/test/services/refactoring/abstract_rename.dart
+++ b/pkg/analysis_server/test/services/refactoring/abstract_rename.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analysis_server/src/services/correction/namespace.dart';
 import 'package:analysis_server/src/services/refactoring/refactoring.dart';
 import 'package:analyzer/dart/ast/ast.dart';
@@ -19,7 +17,7 @@
 /// The base class for all [RenameRefactoring] tests.
 class RenameRefactoringTest extends RefactoringTest {
   @override
-  RenameRefactoring refactoring;
+  late RenameRefactoring refactoring;
 
   /// Asserts that [refactoring] has potential edits in [testFile] at offset
   /// of the given [searches].
@@ -52,13 +50,17 @@
 
   /// Creates a new [RenameRefactoring] in [refactoring] for [element].
   /// Fails if no [RenameRefactoring] can be created.
-  void createRenameRefactoringForElement(Element element) {
+  void createRenameRefactoringForElement(Element? element) {
     var workspace = RefactoringWorkspace(
       [driverFor(testFile)],
       searchEngine,
     );
-    refactoring = RenameRefactoring(workspace, testAnalysisResult, element);
-    expect(refactoring, isNotNull, reason: "No refactoring for '$element'.");
+    var refactoring =
+        RenameRefactoring.create(workspace, testAnalysisResult, element);
+    if (refactoring == null) {
+      fail("No refactoring for '$element'.");
+    }
+    this.refactoring = refactoring;
   }
 
   /// Returns the [Edit] with the given [id], maybe `null`.
@@ -70,6 +72,6 @@
         }
       }
     }
-    return null;
+    fail('No edit with id: $id');
   }
 }
diff --git a/pkg/analysis_server/test/services/refactoring/convert_getter_to_method_test.dart b/pkg/analysis_server/test/services/refactoring/convert_getter_to_method_test.dart
index ff2de57..b8cc8eb 100644
--- a/pkg/analysis_server/test/services/refactoring/convert_getter_to_method_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/convert_getter_to_method_test.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analysis_server/src/services/refactoring/refactoring.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart' hide ElementKind;
@@ -20,7 +18,7 @@
 @reflectiveTest
 class ConvertGetterToMethodTest extends RefactoringTest {
   @override
-  ConvertGetterToMethodRefactoring refactoring;
+  late ConvertGetterToMethodRefactoring refactoring;
 
   Future<void> test_change_function() async {
     await indexTestUnit('''
@@ -149,7 +147,7 @@
     assertTestChangeResult(expectedCode);
   }
 
-  void _createRefactoringForElement(ExecutableElement element) {
+  void _createRefactoringForElement(PropertyAccessorElement element) {
     refactoring = ConvertGetterToMethodRefactoring(
         searchEngine, testAnalysisResult.session, element);
   }
diff --git a/pkg/analysis_server/test/services/refactoring/convert_method_to_getter_test.dart b/pkg/analysis_server/test/services/refactoring/convert_method_to_getter_test.dart
index d7434390..c95f9e0 100644
--- a/pkg/analysis_server/test/services/refactoring/convert_method_to_getter_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/convert_method_to_getter_test.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analysis_server/src/services/refactoring/refactoring.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart'
@@ -21,7 +19,7 @@
 @reflectiveTest
 class ConvertMethodToGetterTest extends RefactoringTest {
   @override
-  ConvertMethodToGetterRefactoring refactoring;
+  late ConvertMethodToGetterRefactoring refactoring;
 
   Future<void> test_change_function() async {
     await indexTestUnit('''
diff --git a/pkg/analysis_server/test/services/refactoring/extract_local_test.dart b/pkg/analysis_server/test/services/refactoring/extract_local_test.dart
index 215745e..bbe2a84 100644
--- a/pkg/analysis_server/test/services/refactoring/extract_local_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/extract_local_test.dart
@@ -2,13 +2,10 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'dart:convert';
 
 import 'package:analysis_server/src/services/linter/lint_names.dart';
 import 'package:analysis_server/src/services/refactoring/extract_local.dart';
-import 'package:analysis_server/src/services/refactoring/refactoring.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -24,7 +21,7 @@
 @reflectiveTest
 class ExtractLocalTest extends RefactoringTest {
   @override
-  ExtractLocalRefactoringImpl refactoring;
+  late ExtractLocalRefactoringImpl refactoring;
 
   Future<void> test_checkFinalConditions_sameVariable_after() async {
     await indexTestUnit('''
@@ -196,11 +193,6 @@
 ''');
     _createRefactoringForString('1 + 2');
     expect(refactoring.refactoringName, 'Extract Local Variable');
-    // null
-    refactoring.name = null;
-    assertRefactoringStatus(
-        refactoring.checkName(), RefactoringProblemSeverity.FATAL,
-        expectedMessage: 'Variable name must not be null.');
     // empty
     refactoring.name = '';
     assertRefactoringStatus(
@@ -1365,8 +1357,11 @@
             'Expression must be selected to activate this refactoring.');
   }
 
-  void _assertSingleLinkedEditGroup(
-      {int length, List<int> offsets, List<String> names}) {
+  void _assertSingleLinkedEditGroup({
+    required int length,
+    required List<int> offsets,
+    required List<String> names,
+  }) {
     var positions =
         offsets.map((offset) => {'file': testFile, 'offset': offset});
     var suggestions = names.map((name) => {'value': name, 'kind': 'VARIABLE'});
@@ -1394,7 +1389,8 @@
   }
 
   void _createRefactoring(int offset, int length) {
-    refactoring = ExtractLocalRefactoring(testAnalysisResult, offset, length);
+    refactoring =
+        ExtractLocalRefactoringImpl(testAnalysisResult, offset, length);
     refactoring.name = 'res';
   }
 
diff --git a/pkg/analysis_server/test/services/refactoring/extract_method_test.dart b/pkg/analysis_server/test/services/refactoring/extract_method_test.dart
index 06b2131..fec1997 100644
--- a/pkg/analysis_server/test/services/refactoring/extract_method_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/extract_method_test.dart
@@ -2,10 +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.
 
-// @dart = 2.9
-
 import 'package:analysis_server/src/services/refactoring/extract_method.dart';
-import 'package:analysis_server/src/services/refactoring/refactoring.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -21,7 +18,7 @@
 @reflectiveTest
 class ExtractMethodTest extends RefactoringTest {
   @override
-  ExtractMethodRefactoringImpl refactoring;
+  late ExtractMethodRefactoringImpl refactoring;
 
   Future<void> test_bad_assignmentLeftHandSide() async {
     await indexTestUnit('''
@@ -791,11 +788,6 @@
 }
 ''');
     _createRefactoringForString('1 + 2');
-    // null
-    refactoring.name = null;
-    assertRefactoringStatus(
-        refactoring.checkName(), RefactoringProblemSeverity.FATAL,
-        expectedMessage: 'Method name must not be null.');
     // empty
     refactoring.name = '';
     assertRefactoringStatus(
@@ -2919,7 +2911,7 @@
   }
 
   void _createRefactoring(int offset, int length) {
-    refactoring = ExtractMethodRefactoring(
+    refactoring = ExtractMethodRefactoringImpl(
         searchEngine, testAnalysisResult, offset, length);
     refactoring.name = 'res';
   }
diff --git a/pkg/analysis_server/test/services/refactoring/extract_widget_test.dart b/pkg/analysis_server/test/services/refactoring/extract_widget_test.dart
index 8ceaf78..6106c93 100644
--- a/pkg/analysis_server/test/services/refactoring/extract_widget_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/extract_widget_test.dart
@@ -2,10 +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.
 
-// @dart = 2.9
-
 import 'package:analysis_server/src/services/refactoring/extract_widget.dart';
-import 'package:analysis_server/src/services/refactoring/refactoring.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -21,7 +18,7 @@
 @reflectiveTest
 class ExtractWidgetTest extends RefactoringTest {
   @override
-  ExtractWidgetRefactoringImpl refactoring;
+  late ExtractWidgetRefactoringImpl refactoring;
 
   @override
   void setUp() {
@@ -55,12 +52,6 @@
 ''');
     _createRefactoringForStringOffset('new Text');
 
-    // null
-    refactoring.name = null;
-    assertRefactoringStatus(
-        refactoring.checkName(), RefactoringProblemSeverity.FATAL,
-        expectedMessage: 'Class name must not be null.');
-
     // empty
     refactoring.name = '';
     assertRefactoringStatus(
@@ -1232,7 +1223,7 @@
   }
 
   void _createRefactoring(int offset, int length) {
-    refactoring = ExtractWidgetRefactoring(
+    refactoring = ExtractWidgetRefactoringImpl(
         searchEngine, testAnalysisResult, offset, length);
     refactoring.name = 'Test';
   }
diff --git a/pkg/analysis_server/test/services/refactoring/inline_local_test.dart b/pkg/analysis_server/test/services/refactoring/inline_local_test.dart
index 80920b8..1285872 100644
--- a/pkg/analysis_server/test/services/refactoring/inline_local_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/inline_local_test.dart
@@ -2,11 +2,8 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analysis_server/src/services/correction/status.dart';
 import 'package:analysis_server/src/services/refactoring/inline_local.dart';
-import 'package:analysis_server/src/services/refactoring/refactoring.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart' hide Element;
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -22,7 +19,7 @@
 @reflectiveTest
 class InlineLocalTest extends RefactoringTest {
   @override
-  InlineLocalRefactoringImpl refactoring;
+  late InlineLocalRefactoringImpl refactoring;
 
   Future<void> test_access() async {
     await indexTestUnit('''
@@ -639,7 +636,7 @@
 
   void _createRefactoring(String search) {
     var offset = findOffset(search);
-    refactoring = InlineLocalRefactoring(
+    refactoring = InlineLocalRefactoringImpl(
       searchEngine,
       testAnalysisResult,
       offset,
diff --git a/pkg/analysis_server/test/services/refactoring/inline_method_test.dart b/pkg/analysis_server/test/services/refactoring/inline_method_test.dart
index 204df5c..281b5f0 100644
--- a/pkg/analysis_server/test/services/refactoring/inline_method_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/inline_method_test.dart
@@ -2,10 +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.
 
-// @dart = 2.9
-
 import 'package:analysis_server/src/services/refactoring/inline_method.dart';
-import 'package:analysis_server/src/services/refactoring/refactoring.dart';
 import 'package:analyzer/src/generated/source.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart' hide Element;
 import 'package:test/test.dart';
@@ -22,9 +19,9 @@
 @reflectiveTest
 class InlineMethodTest extends RefactoringTest {
   @override
-  InlineMethodRefactoringImpl refactoring;
-  bool deleteSource;
-  bool inlineAll;
+  late InlineMethodRefactoringImpl refactoring;
+  bool? deleteSource;
+  bool? inlineAll;
 
   Future<void> test_access_FunctionElement() async {
     await indexTestUnit(r'''
@@ -1763,9 +1760,11 @@
     var status = await refactoring.checkInitialConditions();
     assertRefactoringStatusOK(status);
     // configure
+    var deleteSource = this.deleteSource;
     if (deleteSource != null) {
       refactoring.deleteSource = deleteSource;
     }
+    var inlineAll = this.inlineAll;
     if (inlineAll != null) {
       refactoring.inlineAll = inlineAll;
     }
@@ -1780,7 +1779,7 @@
 
   void _createRefactoring(String search) {
     var offset = findOffset(search);
-    refactoring = InlineMethodRefactoring(
+    refactoring = InlineMethodRefactoringImpl(
       searchEngine,
       testAnalysisResult,
       offset,
diff --git a/pkg/analysis_server/test/services/refactoring/move_file_test.dart b/pkg/analysis_server/test/services/refactoring/move_file_test.dart
index 8d8fa21..ad4b7ef 100644
--- a/pkg/analysis_server/test/services/refactoring/move_file_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/move_file_test.dart
@@ -2,10 +2,9 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analysis_server/src/protocol_server.dart';
 import 'package:analysis_server/src/services/refactoring/refactoring.dart';
+import 'package:analyzer/dart/analysis/results.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -20,7 +19,7 @@
 @reflectiveTest
 class MoveFileTest extends RefactoringTest {
   @override
-  MoveFileRefactoring refactoring;
+  late MoveFileRefactoring refactoring;
 
   Future<void> test_file_containing_imports_exports_parts() async {
     var pathA = convertPath('/home/test/000/1111/a.dart');
@@ -67,7 +66,8 @@
     // Since the file being refactored isn't the test source, we set the
     // testAnalysisResult manually here, the path is referenced through the
     // referenced File object to run on Windows:
-    testAnalysisResult = await session.getResolvedUnit(file.path);
+    testAnalysisResult =
+        await session.getResolvedUnit2(file.path) as ResolvedUnitResult;
 
     _createRefactoring('/home/test/lib/222/new_name.dart', oldFile: file.path);
     await _assertSuccessfulRefactoring();
@@ -89,7 +89,8 @@
     // Since the file being refactored isn't the test source, we set the
     // testAnalysisResult manually here, the path is referenced through the
     // referenced File object to run on Windows:
-    testAnalysisResult = await session.getResolvedUnit(file.path);
+    testAnalysisResult =
+        await session.getResolvedUnit2(file.path) as ResolvedUnitResult;
 
     _createRefactoring('/home/test0/test1/test3/lib/111/name.dart',
         oldFile: file.path);
@@ -112,7 +113,8 @@
     // Since the file being refactored isn't the test source, we set the
     // testAnalysisResult manually here, the path is referenced through the
     // referenced File object to run on Windows:
-    testAnalysisResult = await session.getResolvedUnit(file.path);
+    testAnalysisResult =
+        await session.getResolvedUnit2(file.path) as ResolvedUnitResult;
 
     _createRefactoring('/home/test0/test1/test2/test3/lib/111/name.dart',
         oldFile: file.path);
@@ -135,7 +137,8 @@
     // Since the file being refactored isn't the test source, we set the
     // testAnalysisResult manually here, the path is referenced through the
     // referenced File object to run on Windows:
-    testAnalysisResult = await session.getResolvedUnit(file.path);
+    testAnalysisResult =
+        await session.getResolvedUnit2(file.path) as ResolvedUnitResult;
 
     _createRefactoring('/home/test0/test1/lib/111/name.dart',
         oldFile: file.path);
@@ -156,7 +159,8 @@
     // Since the file being refactored isn't the test source, we set the
     // testAnalysisResult manually here, the path is referenced through the
     // referenced File object to run on Windows:
-    testAnalysisResult = await session.getResolvedUnit(file.path);
+    testAnalysisResult =
+        await session.getResolvedUnit2(file.path) as ResolvedUnitResult;
 
     _createRefactoring('/home/test/lib/222/new_name.dart', oldFile: file.path);
     await _assertSuccessfulRefactoring();
@@ -176,7 +180,8 @@
     // Since the file being refactored isn't the test source, we set the
     // testAnalysisResult manually here, the path is referenced through the
     // referenced File object to run on Windows:
-    testAnalysisResult = await session.getResolvedUnit(file.path);
+    testAnalysisResult =
+        await session.getResolvedUnit2(file.path) as ResolvedUnitResult;
 
     _createRefactoring('/home/test/lib/new_name.dart', oldFile: file.path);
     await _assertSuccessfulRefactoring();
@@ -492,7 +497,7 @@
   }
 
   Future _assertFailedRefactoring(RefactoringProblemSeverity expectedSeverity,
-      {String expectedMessage}) async {
+      {String? expectedMessage}) async {
     var status = await refactoring.checkAllConditions();
     assertRefactoringStatus(status, expectedSeverity,
         expectedMessage: expectedMessage);
@@ -504,7 +509,7 @@
     refactoringChange = await refactoring.createChange();
   }
 
-  void _createRefactoring(String newFile, {String oldFile}) {
+  void _createRefactoring(String newFile, {String? oldFile}) {
     var refactoringWorkspace =
         RefactoringWorkspace([driverFor(testFile)], searchEngine);
     // Allow passing an oldName for when we don't want to rename testSource,
diff --git a/pkg/analysis_server/test/services/refactoring/naming_conventions_test.dart b/pkg/analysis_server/test/services/refactoring/naming_conventions_test.dart
index 810ec9b..8441368 100644
--- a/pkg/analysis_server/test/services/refactoring/naming_conventions_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/naming_conventions_test.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analysis_server/src/services/correction/status.dart';
 import 'package:analysis_server/src/services/refactoring/naming_conventions.dart';
 import 'package:analysis_server/src/services/refactoring/refactoring.dart';
@@ -22,7 +20,7 @@
 @reflectiveTest
 class NamingConventionsTest extends RefactoringTest {
   @override
-  Refactoring get refactoring => null;
+  Refactoring get refactoring => throw UnimplementedError();
 
   void test_validateClassName_doesNotStartWithLowerCase() {
     assertRefactoringStatus(
@@ -60,12 +58,6 @@
         expectedMessage: 'Class name should start with an uppercase letter.');
   }
 
-  void test_validateClassName_null() {
-    assertRefactoringStatus(
-        validateClassName(null), RefactoringProblemSeverity.FATAL,
-        expectedMessage: 'Class name must not be null.');
-  }
-
   void test_validateClassName_OK() {
     assertRefactoringStatusOK(validateClassName('NewName'));
   }
@@ -177,12 +169,6 @@
             'Field name must begin with a lowercase letter or underscore.');
   }
 
-  void test_validateFieldName_null() {
-    assertRefactoringStatus(
-        validateFieldName(null), RefactoringProblemSeverity.FATAL,
-        expectedMessage: 'Field name must not be null.');
-  }
-
   void test_validateFieldName_OK() {
     assertRefactoringStatusOK(validateFieldName('newName'));
   }
@@ -242,12 +228,6 @@
             'Function name must begin with a lowercase letter or underscore.');
   }
 
-  void test_validateFunctionName_null() {
-    assertRefactoringStatus(
-        validateFunctionName(null), RefactoringProblemSeverity.FATAL,
-        expectedMessage: 'Function name must not be null.');
-  }
-
   void test_validateFunctionName_OK() {
     assertRefactoringStatusOK(validateFunctionName('newName'));
   }
@@ -375,12 +355,6 @@
             'Label name must begin with a lowercase letter or underscore.');
   }
 
-  void test_validateLabelName_null() {
-    assertRefactoringStatus(
-        validateLabelName(null), RefactoringProblemSeverity.FATAL,
-        expectedMessage: 'Label name must not be null.');
-  }
-
   void test_validateLabelName_OK() {
     assertRefactoringStatusOK(validateLabelName('newName'));
   }
@@ -517,12 +491,6 @@
             'Method name must begin with a lowercase letter or underscore.');
   }
 
-  void test_validateMethodName_null() {
-    assertRefactoringStatus(
-        validateMethodName(null), RefactoringProblemSeverity.FATAL,
-        expectedMessage: 'Method name must not be null.');
-  }
-
   void test_validateMethodName_OK() {
     assertRefactoringStatusOK(validateMethodName('newName'));
   }
@@ -587,12 +555,6 @@
             'Parameter name must begin with a lowercase letter or underscore.');
   }
 
-  void test_validateParameterName_null() {
-    assertRefactoringStatus(
-        validateParameterName(null), RefactoringProblemSeverity.FATAL,
-        expectedMessage: 'Parameter name must not be null.');
-  }
-
   void test_validateParameterName_OK() {
     assertRefactoringStatusOK(validateParameterName('newName'));
   }
@@ -653,12 +615,6 @@
             'Type alias name should start with an uppercase letter.');
   }
 
-  void test_validateTypeAliasName_null() {
-    assertRefactoringStatus(
-        validateTypeAliasName(null), RefactoringProblemSeverity.FATAL,
-        expectedMessage: 'Type alias name must not be null.');
-  }
-
   void test_validateTypeAliasName_OK() {
     assertRefactoringStatusOK(validateTypeAliasName('NewName'));
   }
@@ -722,12 +678,6 @@
             'Variable name must begin with a lowercase letter or underscore.');
   }
 
-  void test_validateVariableName_null() {
-    assertRefactoringStatus(
-        validateVariableName(null), RefactoringProblemSeverity.FATAL,
-        expectedMessage: 'Variable name must not be null.');
-  }
-
   void test_validateVariableName_OK() {
     assertRefactoringStatusOK(validateVariableName('newName'));
   }
diff --git a/pkg/analysis_server/test/services/refactoring/rename_class_member_test.dart b/pkg/analysis_server/test/services/refactoring/rename_class_member_test.dart
index 47a6c12..74a2fee 100644
--- a/pkg/analysis_server/test/services/refactoring/rename_class_member_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/rename_class_member_test.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analyzer/src/generated/source.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:test/test.dart';
@@ -441,11 +439,6 @@
 }
 ''');
     createRenameRefactoringAtString('test = 0;');
-    // null
-    refactoring.newName = null;
-    assertRefactoringStatus(
-        refactoring.checkNewName(), RefactoringProblemSeverity.FATAL,
-        expectedMessage: 'Field name must not be null.');
     // OK
     refactoring.newName = 'newName';
     assertRefactoringStatusOK(refactoring.checkNewName());
@@ -458,11 +451,6 @@
 }
 ''');
     createRenameRefactoringAtString('test() {}');
-    // null
-    refactoring.newName = null;
-    assertRefactoringStatus(
-        refactoring.checkNewName(), RefactoringProblemSeverity.FATAL,
-        expectedMessage: 'Method name must not be null.');
     // empty
     refactoring.newName = '';
     assertRefactoringStatus(
diff --git a/pkg/analysis_server/test/services/refactoring/rename_constructor_test.dart b/pkg/analysis_server/test/services/refactoring/rename_constructor_test.dart
index 2cb9270..97c6102 100644
--- a/pkg/analysis_server/test/services/refactoring/rename_constructor_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/rename_constructor_test.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analysis_server/src/services/refactoring/refactoring.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:test/test.dart';
@@ -42,11 +40,6 @@
 ''');
     createRenameRefactoringAtString('test() {}');
     expect(refactoring.oldName, 'test');
-    // null
-    refactoring.newName = null;
-    assertRefactoringStatus(
-        refactoring.checkNewName(), RefactoringProblemSeverity.FATAL,
-        expectedMessage: 'Constructor name must not be null.');
     // same
     refactoring.newName = 'test';
     assertRefactoringStatus(
@@ -239,7 +232,8 @@
   Future<void> test_newInstance_nullElement() async {
     await indexTestUnit('');
     var workspace = RefactoringWorkspace([driverFor(testFile)], searchEngine);
-    var refactoring = RenameRefactoring(workspace, testAnalysisResult, null);
+    var refactoring =
+        RenameRefactoring.create(workspace, testAnalysisResult, null);
     expect(refactoring, isNull);
   }
 
diff --git a/pkg/analysis_server/test/services/refactoring/rename_extension_member_test.dart b/pkg/analysis_server/test/services/refactoring/rename_extension_member_test.dart
index 36f2bc3..b7e3770 100644
--- a/pkg/analysis_server/test/services/refactoring/rename_extension_member_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/rename_extension_member_test.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -139,12 +137,12 @@
 }
 ''');
     createRenameRefactoringAtString('test =>');
-    // null
-    refactoring.newName = null;
+    // empty
+    refactoring.newName = '';
     assertRefactoringStatus(
       refactoring.checkNewName(),
       RefactoringProblemSeverity.FATAL,
-      expectedMessage: 'Field name must not be null.',
+      expectedMessage: 'Field name must not be empty.',
     );
 
     // OK
@@ -160,14 +158,6 @@
 ''');
     createRenameRefactoringAtString('test() {}');
 
-    // null
-    refactoring.newName = null;
-    assertRefactoringStatus(
-      refactoring.checkNewName(),
-      RefactoringProblemSeverity.FATAL,
-      expectedMessage: 'Method name must not be null.',
-    );
-
     // empty
     refactoring.newName = '';
     assertRefactoringStatus(
diff --git a/pkg/analysis_server/test/services/refactoring/rename_import_test.dart b/pkg/analysis_server/test/services/refactoring/rename_import_test.dart
index 0e90484..998fc74 100644
--- a/pkg/analysis_server/test/services/refactoring/rename_import_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/rename_import_test.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -22,11 +20,6 @@
     await indexTestUnit("import 'dart:async' as test;");
     _createRefactoring("import 'dart:");
     expect(refactoring.oldName, 'test');
-    // null
-    refactoring.newName = null;
-    assertRefactoringStatus(
-        refactoring.checkNewName(), RefactoringProblemSeverity.FATAL,
-        expectedMessage: 'Import prefix name must not be null.');
     // same
     refactoring.newName = 'test';
     assertRefactoringStatus(
@@ -41,6 +34,22 @@
     assertRefactoringStatusOK(refactoring.checkNewName());
   }
 
+  Future<void> test_checkNewName_sameName_empty() async {
+    await indexTestUnit('''
+import 'dart:math';
+void f(Random r) {}
+''');
+
+    _createRefactoring("import 'dart:math");
+
+    refactoring.newName = '';
+    assertRefactoringStatus(
+      refactoring.checkNewName(),
+      RefactoringProblemSeverity.FATAL,
+      expectedMessage: 'The new name must be different than the current name.',
+    );
+  }
+
   Future<void> test_createChange_add() async {
     await indexTestUnit('''
 import 'dart:async';
diff --git a/pkg/analysis_server/test/services/refactoring/rename_label_test.dart b/pkg/analysis_server/test/services/refactoring/rename_label_test.dart
index 98f5cb9..3c824f2 100644
--- a/pkg/analysis_server/test/services/refactoring/rename_label_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/rename_label_test.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -28,11 +26,6 @@
 }
 ''');
     createRenameRefactoringAtString('test:');
-    // null
-    refactoring.newName = null;
-    assertRefactoringStatus(
-        refactoring.checkNewName(), RefactoringProblemSeverity.FATAL,
-        expectedMessage: 'Label name must not be null.');
     // empty
     refactoring.newName = '';
     assertRefactoringStatus(
diff --git a/pkg/analysis_server/test/services/refactoring/rename_library_test.dart b/pkg/analysis_server/test/services/refactoring/rename_library_test.dart
index 3b22091..2f37e06 100644
--- a/pkg/analysis_server/test/services/refactoring/rename_library_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/rename_library_test.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -23,11 +21,6 @@
 library my.app;
 ''');
     _createRenameRefactoring();
-    // null
-    refactoring.newName = null;
-    assertRefactoringStatus(
-        refactoring.checkNewName(), RefactoringProblemSeverity.FATAL,
-        expectedMessage: 'Library name must not be null.');
     // empty
     refactoring.newName = '';
     assertRefactoringStatus(
diff --git a/pkg/analysis_server/test/services/refactoring/rename_local_test.dart b/pkg/analysis_server/test/services/refactoring/rename_local_test.dart
index c620f97..c0ea209 100644
--- a/pkg/analysis_server/test/services/refactoring/rename_local_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/rename_local_test.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -242,11 +240,11 @@
 }
 ''');
     createRenameRefactoringAtString('test() => 0;');
-    // null
-    refactoring.newName = null;
+    // empty
+    refactoring.newName = '';
     assertRefactoringStatus(
         refactoring.checkNewName(), RefactoringProblemSeverity.FATAL,
-        expectedMessage: 'Function name must not be null.');
+        expectedMessage: 'Function name must not be empty.');
     // OK
     refactoring.newName = 'newName';
     assertRefactoringStatusOK(refactoring.checkNewName());
@@ -259,11 +257,6 @@
 }
 ''');
     createRenameRefactoringAtString('test = 0;');
-    // null
-    refactoring.newName = null;
-    assertRefactoringStatus(
-        refactoring.checkNewName(), RefactoringProblemSeverity.FATAL,
-        expectedMessage: 'Variable name must not be null.');
     // empty
     refactoring.newName = '';
     assertRefactoringStatus(
@@ -280,11 +273,11 @@
 }
 ''');
     createRenameRefactoringAtString('test) {');
-    // null
-    refactoring.newName = null;
+    // empty
+    refactoring.newName = '';
     assertRefactoringStatus(
         refactoring.checkNewName(), RefactoringProblemSeverity.FATAL,
-        expectedMessage: 'Parameter name must not be null.');
+        expectedMessage: 'Parameter name must not be empty.');
     // OK
     refactoring.newName = 'newName';
     assertRefactoringStatusOK(refactoring.checkNewName());
diff --git a/pkg/analysis_server/test/services/refactoring/rename_unit_member_test.dart b/pkg/analysis_server/test/services/refactoring/rename_unit_member_test.dart
index 84b916e..a3dcbb8 100644
--- a/pkg/analysis_server/test/services/refactoring/rename_unit_member_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/rename_unit_member_test.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -267,11 +265,6 @@
 class Test {}
 ''');
     createRenameRefactoringAtString('Test {}');
-    // null
-    refactoring.newName = null;
-    assertRefactoringStatus(
-        refactoring.checkNewName(), RefactoringProblemSeverity.FATAL,
-        expectedMessage: 'Class name must not be null.');
     // empty
     refactoring.newName = '';
     assertRefactoringStatus(
@@ -293,11 +286,6 @@
 test() {}
 ''');
     createRenameRefactoringAtString('test() {}');
-    // null
-    refactoring.newName = null;
-    assertRefactoringStatus(
-        refactoring.checkNewName(), RefactoringProblemSeverity.FATAL,
-        expectedMessage: 'Function name must not be null.');
     // empty
     refactoring.newName = '';
     assertRefactoringStatus(
@@ -313,11 +301,6 @@
 var test;
 ''');
     createRenameRefactoringAtString('test;');
-    // null
-    refactoring.newName = null;
-    assertRefactoringStatus(
-        refactoring.checkNewName(), RefactoringProblemSeverity.FATAL,
-        expectedMessage: 'Variable name must not be null.');
     // empty
     refactoring.newName = '';
     assertRefactoringStatus(
@@ -333,11 +316,11 @@
 typedef Test = void Function();
 ''');
     createRenameRefactoringAtString('Test =');
-    // null
-    refactoring.newName = null;
+    // empty
+    refactoring.newName = '';
     assertRefactoringStatus(
         refactoring.checkNewName(), RefactoringProblemSeverity.FATAL,
-        expectedMessage: 'Type alias name must not be null.');
+        expectedMessage: 'Type alias name must not be empty.');
     // OK
     refactoring.newName = 'NewName';
     assertRefactoringStatusOK(refactoring.checkNewName());
@@ -348,11 +331,11 @@
 typedef Test = List<int>;
 ''');
     createRenameRefactoringAtString('Test =');
-    // null
-    refactoring.newName = null;
+    // empty
+    refactoring.newName = '';
     assertRefactoringStatus(
         refactoring.checkNewName(), RefactoringProblemSeverity.FATAL,
-        expectedMessage: 'Type alias name must not be null.');
+        expectedMessage: 'Type alias name must not be empty.');
     // OK
     refactoring.newName = 'NewName';
     assertRefactoringStatusOK(refactoring.checkNewName());
@@ -363,11 +346,11 @@
 typedef Test();
 ''');
     createRenameRefactoringAtString('Test();');
-    // null
-    refactoring.newName = null;
+    // empty
+    refactoring.newName = '';
     assertRefactoringStatus(
         refactoring.checkNewName(), RefactoringProblemSeverity.FATAL,
-        expectedMessage: 'Type alias name must not be null.');
+        expectedMessage: 'Type alias name must not be empty.');
     // OK
     refactoring.newName = 'NewName';
     assertRefactoringStatusOK(refactoring.checkNewName());
diff --git a/pkg/analysis_server/test/services/refactoring/test_all.dart b/pkg/analysis_server/test/services/refactoring/test_all.dart
index 9dc8a8e..d48e879 100644
--- a/pkg/analysis_server/test/services/refactoring/test_all.dart
+++ b/pkg/analysis_server/test/services/refactoring/test_all.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
 import 'convert_getter_to_method_test.dart' as convert_getter_to_method_test;
diff --git a/pkg/analysis_server/test/services/search/search_engine_test.dart b/pkg/analysis_server/test/services/search/search_engine_test.dart
index 664c8fd..a352332 100644
--- a/pkg/analysis_server/test/services/search/search_engine_test.dart
+++ b/pkg/analysis_server/test/services/search/search_engine_test.dart
@@ -427,7 +427,7 @@
       var contextRoot = driver.analysisContext!.contextRoot;
       for (var file in contextRoot.analyzedFiles()) {
         if (file.endsWith('.dart')) {
-          await driver.getUnitElement(file);
+          await driver.getUnitElement2(file);
         }
       }
     }
diff --git a/pkg/analysis_server/test/src/cider/completion_test.dart b/pkg/analysis_server/test/src/cider/completion_test.dart
index 5e15e31..d96e72b 100644
--- a/pkg/analysis_server/test/src/cider/completion_test.dart
+++ b/pkg/analysis_server/test/src/cider/completion_test.dart
@@ -500,8 +500,7 @@
 ''');
 
     _assertHasClass(text: 'int');
-    // TODO(scheglov) would be nice to have it
-    _assertNoClass(text: 'A');
+    _assertHasClass(text: 'A');
   }
 
   Future<void> test_limitedResolution_inPart_partOfUri() async {
diff --git a/pkg/analysis_server/test/src/computer/closing_labels_computer_test.dart b/pkg/analysis_server/test/src/computer/closing_labels_computer_test.dart
index c79006b..eff0c79 100644
--- a/pkg/analysis_server/test/src/computer/closing_labels_computer_test.dart
+++ b/pkg/analysis_server/test/src/computer/closing_labels_computer_test.dart
@@ -4,6 +4,7 @@
 
 import 'package:analysis_server/protocol/protocol_generated.dart';
 import 'package:analysis_server/src/computer/computer_closingLabels.dart';
+import 'package:analyzer/dart/analysis/results.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -396,7 +397,8 @@
 
   Future<List<ClosingLabel>> _computeElements(String sourceContent) async {
     newFile(sourcePath, content: sourceContent);
-    var result = await session.getResolvedUnit(sourcePath);
+    var result =
+        await session.getResolvedUnit2(sourcePath) as ResolvedUnitResult;
     var computer = DartUnitClosingLabelsComputer(result.lineInfo, result.unit!);
     return computer.compute();
   }
diff --git a/pkg/analysis_server/test/src/computer/folding_computer_test.dart b/pkg/analysis_server/test/src/computer/folding_computer_test.dart
index 2ef0286..94372c5 100644
--- a/pkg/analysis_server/test/src/computer/folding_computer_test.dart
+++ b/pkg/analysis_server/test/src/computer/folding_computer_test.dart
@@ -4,6 +4,7 @@
 
 import 'package:analysis_server/src/computer/computer_folding.dart';
 import 'package:analysis_server/src/protocol_server.dart';
+import 'package:analyzer/dart/analysis/results.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -555,7 +556,8 @@
 
   Future<List<FoldingRegion>> _computeRegions(String sourceContent) async {
     newFile(sourcePath, content: sourceContent);
-    var result = await session.getResolvedUnit(sourcePath);
+    var result =
+        await session.getResolvedUnit2(sourcePath) as ResolvedUnitResult;
     var computer = DartUnitFoldingComputer(result.lineInfo, result.unit!);
     return computer.compute();
   }
diff --git a/pkg/analysis_server/test/src/computer/highlights_computer_test.dart b/pkg/analysis_server/test/src/computer/highlights_computer_test.dart
index 08682eb..2012268 100644
--- a/pkg/analysis_server/test/src/computer/highlights_computer_test.dart
+++ b/pkg/analysis_server/test/src/computer/highlights_computer_test.dart
@@ -4,6 +4,7 @@
 
 import 'package:analysis_server/src/computer/computer_highlights.dart';
 import 'package:analysis_server/src/protocol_server.dart';
+import 'package:analyzer/dart/analysis/results.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -112,7 +113,8 @@
   }) async {
     this.content = content;
     newFile(sourcePath, content: content);
-    var result = await session.getResolvedUnit(sourcePath);
+    var result =
+        await session.getResolvedUnit2(sourcePath) as ResolvedUnitResult;
 
     if (hasErrors) {
       expect(result.errors, isNotEmpty);
diff --git a/pkg/analysis_server/test/src/computer/import_elements_computer_test.dart b/pkg/analysis_server/test/src/computer/import_elements_computer_test.dart
index 19bb89e..ebdde35 100644
--- a/pkg/analysis_server/test/src/computer/import_elements_computer_test.dart
+++ b/pkg/analysis_server/test/src/computer/import_elements_computer_test.dart
@@ -4,6 +4,7 @@
 
 import 'package:analysis_server/protocol/protocol_generated.dart';
 import 'package:analysis_server/src/computer/import_elements_computer.dart';
+import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/src/test_utilities/package_config_file_builder.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:test/test.dart';
@@ -49,7 +50,7 @@
   Future<void> createBuilder(String content) async {
     originalContent = content;
     newFile(path, content: content);
-    var result = await session.getResolvedUnit(path);
+    var result = await session.getResolvedUnit2(path) as ResolvedUnitResult;
     computer = ImportElementsComputer(resourceProvider, result);
   }
 
diff --git a/pkg/analysis_server/test/src/computer/imported_elements_computer_test.dart b/pkg/analysis_server/test/src/computer/imported_elements_computer_test.dart
index 9f55abb..7057898 100644
--- a/pkg/analysis_server/test/src/computer/imported_elements_computer_test.dart
+++ b/pkg/analysis_server/test/src/computer/imported_elements_computer_test.dart
@@ -6,6 +6,7 @@
 
 import 'package:analysis_server/protocol/protocol_generated.dart';
 import 'package:analysis_server/src/computer/imported_elements_computer.dart';
+import 'package:analyzer/dart/analysis/results.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -471,7 +472,8 @@
   Future<void> _computeElements(String content, String selection) async {
     // TODO(brianwilkerson) Automatically extract the selection from the content.
     newFile(sourcePath, content: content);
-    var result = await session.getResolvedUnit(sourcePath);
+    var result =
+        await session.getResolvedUnit2(sourcePath) as ResolvedUnitResult;
     var computer = ImportedElementsComputer(
         result.unit, content.indexOf(selection), selection.length);
     importedElements = computer.compute();
diff --git a/pkg/analysis_server/test/src/computer/outline_computer_test.dart b/pkg/analysis_server/test/src/computer/outline_computer_test.dart
index 08abb6d..da4b72a 100644
--- a/pkg/analysis_server/test/src/computer/outline_computer_test.dart
+++ b/pkg/analysis_server/test/src/computer/outline_computer_test.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analysis_server/src/computer/computer_outline.dart';
+import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -30,7 +31,8 @@
   Future<Outline> _computeOutline(String code) async {
     testCode = code;
     newFile(testPath, content: code);
-    var resolveResult = await session.getResolvedUnit(testPath);
+    var resolveResult =
+        await session.getResolvedUnit2(testPath) as ResolvedUnitResult;
     return DartUnitOutlineComputer(
       resolveResult,
       withBasicFlutter: true,
diff --git a/pkg/analysis_server/test/src/flutter/flutter_outline_computer_test.dart b/pkg/analysis_server/test/src/flutter/flutter_outline_computer_test.dart
index cad89c1..1cd6ba2 100644
--- a/pkg/analysis_server/test/src/flutter/flutter_outline_computer_test.dart
+++ b/pkg/analysis_server/test/src/flutter/flutter_outline_computer_test.dart
@@ -516,7 +516,8 @@
   Future<FlutterOutline> _computeOutline(String code) async {
     testCode = code;
     newFile(testPath, content: code);
-    resolveResult = await session.getResolvedUnit(testPath);
+    resolveResult =
+        await session.getResolvedUnit2(testPath) as ResolvedUnitResult;
     computer = FlutterOutlineComputer(resolveResult);
     return computer.compute();
   }
diff --git a/pkg/analysis_server/test/src/services/completion/yaml/pubspec_generator_test.dart b/pkg/analysis_server/test/src/services/completion/yaml/pubspec_generator_test.dart
index 3df9b93..4f4923f 100644
--- a/pkg/analysis_server/test/src/services/completion/yaml/pubspec_generator_test.dart
+++ b/pkg/analysis_server/test/src/services/completion/yaml/pubspec_generator_test.dart
@@ -50,6 +50,17 @@
     assertSuggestion('name: ');
   }
 
+  void test_emptyPreviousSibling() {
+    // Ensure handling of nulls does not pick up nulls from previous siblings
+    getCompletions('''
+flutter:
+  assets:
+  fonts:
+    ^
+''');
+    assertSuggestion('family: ');
+  }
+
   void test_environment() {
     getCompletions('''
 environment:
@@ -279,6 +290,8 @@
   }
 
   void test_packageName() async {
+    /// Sample package name list JSON in the same format as the API:
+    /// https://pub.dev/api/package-name-completion-data
     const samplePackageList = '''
   { "packages": ["one", "two", "three"] }
   ''';
diff --git a/pkg/analysis_server/test/stress/completion/completion_runner.dart b/pkg/analysis_server/test/stress/completion/completion_runner.dart
index 4a5cf3b..542d8fe 100644
--- a/pkg/analysis_server/test/stress/completion/completion_runner.dart
+++ b/pkg/analysis_server/test/stress/completion/completion_runner.dart
@@ -7,6 +7,7 @@
 import 'package:analysis_server/src/services/completion/dart/completion_manager.dart';
 import 'package:analysis_server/src/utilities/null_string_sink.dart';
 import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
+import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/ast/visitor.dart';
 import 'package:analyzer/file_system/overlay_file_system.dart';
@@ -79,7 +80,8 @@
         }
         fileCount++;
         output.write('.');
-        var result = await context.currentSession.getResolvedUnit(path);
+        var result = await context.currentSession.getResolvedUnit2(path)
+            as ResolvedUnitResult;
         var content = result.content!;
         var lineInfo = result.lineInfo;
         var identifiers = _identifiersIn(result.unit!);
@@ -92,7 +94,8 @@
                 content.substring(identifier.end);
             resourceProvider.setOverlay(path,
                 content: modifiedContent, modificationStamp: stamp++);
-            result = await context.currentSession.getResolvedUnit(path);
+            result = await context.currentSession.getResolvedUnit2(path)
+                as ResolvedUnitResult;
           }
 
           timer.start();
diff --git a/pkg/analysis_server/test/tool/lsp_spec/dart_test.dart b/pkg/analysis_server/test/tool/lsp_spec/dart_test.dart
index ea05eed..7ead849 100644
--- a/pkg/analysis_server/test/tool/lsp_spec/dart_test.dart
+++ b/pkg/analysis_server/test/tool/lsp_spec/dart_test.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:test/test.dart';
 
 import '../../../tool/lsp_spec/typescript_parser.dart' as ast;
diff --git a/pkg/analysis_server/test/tool/lsp_spec/generated_classes_test.dart b/pkg/analysis_server/test/tool/lsp_spec/generated_classes_test.dart
index 3b034df..a506de9 100644
--- a/pkg/analysis_server/test/tool/lsp_spec/generated_classes_test.dart
+++ b/pkg/analysis_server/test/tool/lsp_spec/generated_classes_test.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
 import 'package:analysis_server/lsp_protocol/protocol_special.dart';
 import 'package:test/test.dart';
diff --git a/pkg/analysis_server/test/tool/lsp_spec/json_test.dart b/pkg/analysis_server/test/tool/lsp_spec/json_test.dart
index e0b9720..add3ab4 100644
--- a/pkg/analysis_server/test/tool/lsp_spec/json_test.dart
+++ b/pkg/analysis_server/test/tool/lsp_spec/json_test.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'dart:convert';
 
 import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
@@ -389,6 +387,10 @@
   });
 
   test('objects with lists can round-trip through to json and back', () {
+    final workspaceFolders = [
+      WorkspaceFolder(uri: '!uri1', name: '!name1'),
+      WorkspaceFolder(uri: '!uri2', name: '!name2'),
+    ];
     final obj = InitializeParams(
       processId: 1,
       clientInfo:
@@ -396,21 +398,17 @@
       rootPath: '!root',
       capabilities: ClientCapabilities(),
       trace: 'off',
-      workspaceFolders: [
-        WorkspaceFolder(uri: '!uri1', name: '!name1'),
-        WorkspaceFolder(uri: '!uri2', name: '!name2'),
-      ],
+      workspaceFolders: workspaceFolders,
     );
     final json = jsonEncode(obj);
     final restoredObj = InitializeParams.fromJson(jsonDecode(json));
+    final restoredWorkspaceFolders = restoredObj.workspaceFolders!;
 
-    expect(
-        restoredObj.workspaceFolders, hasLength(obj.workspaceFolders.length));
-    for (var i = 0; i < obj.workspaceFolders.length; i++) {
-      expect(restoredObj.workspaceFolders[i].name,
-          equals(obj.workspaceFolders[i].name));
-      expect(restoredObj.workspaceFolders[i].uri,
-          equals(obj.workspaceFolders[i].uri));
+    expect(restoredWorkspaceFolders, hasLength(workspaceFolders.length));
+    for (var i = 0; i < workspaceFolders.length; i++) {
+      expect(
+          restoredWorkspaceFolders[i].name, equals(workspaceFolders[i].name));
+      expect(restoredWorkspaceFolders[i].uri, equals(workspaceFolders[i].uri));
     }
   });
 
@@ -444,7 +442,7 @@
 
     expect(restoredObj.documentChanges, equals(obj.documentChanges));
     expect(restoredObj.changes, equals(obj.changes));
-    expect(restoredObj.changes.keys, equals(obj.changes.keys));
-    expect(restoredObj.changes.values, equals(obj.changes.values));
+    expect(restoredObj.changes!.keys, equals(obj.changes!.keys));
+    expect(restoredObj.changes!.values, equals(obj.changes!.values));
   });
 }
diff --git a/pkg/analysis_server/test/tool/lsp_spec/matchers.dart b/pkg/analysis_server/test/tool/lsp_spec/matchers.dart
index 8fa3f4f..a424ae1 100644
--- a/pkg/analysis_server/test/tool/lsp_spec/matchers.dart
+++ b/pkg/analysis_server/test/tool/lsp_spec/matchers.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
 import 'package:matcher/matcher.dart';
 
@@ -11,7 +9,7 @@
 
 Matcher isArrayOf(Matcher matcher) => ArrayTypeMatcher(wrapMatcher(matcher));
 
-Matcher isLiteralOf(Matcher typeMatcher, Object value) =>
+Matcher isLiteralOf(Matcher typeMatcher, String value) =>
     LiteralTypeMatcher(typeMatcher, value);
 
 Matcher isMapOf(Matcher indexMatcher, Matcher valueMatcher) =>
diff --git a/pkg/analysis_server/test/tool/lsp_spec/test_all.dart b/pkg/analysis_server/test/tool/lsp_spec/test_all.dart
index 0b98b69..07a4247 100644
--- a/pkg/analysis_server/test/tool/lsp_spec/test_all.dart
+++ b/pkg/analysis_server/test/tool/lsp_spec/test_all.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
 import 'dart_test.dart' as dart_test;
diff --git a/pkg/analysis_server/test/tool/lsp_spec/typescript_test.dart b/pkg/analysis_server/test/tool/lsp_spec/typescript_test.dart
index 3c69370..2fbe495 100644
--- a/pkg/analysis_server/test/tool/lsp_spec/typescript_test.dart
+++ b/pkg/analysis_server/test/tool/lsp_spec/typescript_test.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:test/test.dart';
 
 import '../../../tool/lsp_spec/typescript_parser.dart';
@@ -26,13 +24,13 @@
       final output = parseString(input);
       expect(output, hasLength(1));
       expect(output[0], const TypeMatcher<Interface>());
-      final Interface interface = output[0];
+      final interface = output[0] as Interface;
       expect(interface.name, equals('SomeOptions'));
       expect(interface.commentText, equals('Some options.'));
       expect(interface.baseTypes, hasLength(0));
       expect(interface.members, hasLength(1));
       expect(interface.members[0], const TypeMatcher<Field>());
-      final Field field = interface.members[0];
+      final field = interface.members[0] as Field;
       expect(field.name, equals('options'));
       expect(field.commentText, equals('''Options used by something.'''));
       expect(field.allowsNull, isFalse);
@@ -54,11 +52,11 @@
 
       // Check there was a full fabricarted interface for this type.
       expect(output[0], const TypeMatcher<Interface>());
-      Interface interface = output[0];
+      var interface = output[0] as Interface;
       expect(interface.name, equals('CapabilitiesTextDoc'));
       expect(interface.members, hasLength(1));
       expect(interface.members[0], const TypeMatcher<Field>());
-      Field field = interface.members[0];
+      var field = interface.members[0] as Field;
       expect(field.name, equals('deprecated'));
       expect(field.allowsNull, isFalse);
       expect(field.allowsUndefined, isTrue);
@@ -66,11 +64,11 @@
       expect(field.allowsUndefined, isTrue);
 
       expect(output[1], const TypeMatcher<Interface>());
-      interface = output[1];
+      interface = output[1] as Interface;
       expect(interface.name, equals('Capabilities'));
       expect(interface.members, hasLength(1));
       expect(interface.members[0], const TypeMatcher<Field>());
-      field = interface.members[0];
+      field = interface.members[0] as Field;
       expect(field.name, equals('textDoc'));
       expect(field.allowsNull, isFalse);
       expect(field.type, isSimpleType('CapabilitiesTextDoc'));
@@ -92,11 +90,11 @@
       final output = parseString(input);
       expect(output, hasLength(1));
       expect(output[0], const TypeMatcher<Interface>());
-      final Interface interface = output[0];
+      final interface = output[0] as Interface;
       expect(interface.members, hasLength(2));
       [0, 1].forEach((i) {
         expect(interface.members[i], const TypeMatcher<Field>());
-        final Field field = interface.members[i];
+        final field = interface.members[i] as Field;
         expect(field.name, equals('options$i'));
         expect(field.commentText, equals('''Options$i used by something.'''));
       });
@@ -111,9 +109,9 @@
       final output = parseString(input);
       expect(output, hasLength(1));
       expect(output[0], const TypeMatcher<Interface>());
-      final Interface interface = output[0];
+      final interface = output[0] as Interface;
       expect(interface.members, hasLength(1));
-      final Field field = interface.members.first;
+      final field = interface.members.first as Field;
       expect(field, const TypeMatcher<Field>());
       expect(field.name, equals('data'));
       expect(field.allowsUndefined, isTrue);
@@ -133,16 +131,16 @@
       final output = parseString(input);
       expect(output, hasLength(1));
       expect(output[0], const TypeMatcher<Interface>());
-      final Interface interface = output[0];
+      final interface = output[0] as Interface;
       expect(interface.members, hasLength(1));
-      final Field field = interface.members.first;
+      final field = interface.members.first as Field;
       expect(field, const TypeMatcher<Field>());
       expect(field.name, equals('params'));
       expect(field.commentText, equals('''The method's params.'''));
       expect(field.allowsUndefined, isTrue);
       expect(field.allowsNull, isFalse);
       expect(field.type, const TypeMatcher<UnionType>());
-      UnionType union = field.type;
+      final union = field.type as UnionType;
       expect(union.types, hasLength(2));
       expect(union.types[0], isArrayOf(isSimpleType('any')));
       expect(union.types[1], isSimpleType('string'));
@@ -157,9 +155,9 @@
       final output = parseString(input);
       expect(output, hasLength(1));
       expect(output[0], const TypeMatcher<Interface>());
-      final Interface interface = output[0];
+      final interface = output[0] as Interface;
       expect(interface.members, hasLength(1));
-      final Field field = interface.members.first;
+      final field = interface.members.first as Field;
       expect(field, const TypeMatcher<Field>());
       expect(field.name, equals('changes'));
       expect(field.type,
@@ -176,13 +174,13 @@
 }
     ''';
       final output = parseString(input);
-      final Interface interface = output[0];
+      final interface = output[0] as Interface;
       expect(interface.members, hasLength(4));
       interface.members.forEach((m) => expect(m, const TypeMatcher<Field>()));
-      final Field canBeBoth = interface.members[0],
-          canBeNeither = interface.members[1],
-          canBeNull = interface.members[2],
-          canBeUndefined = interface.members[3];
+      final canBeBoth = interface.members[0] as Field,
+          canBeNeither = interface.members[1] as Field,
+          canBeNull = interface.members[2] as Field,
+          canBeUndefined = interface.members[3] as Field;
       expect(canBeNeither.allowsNull, isFalse);
       expect(canBeNeither.allowsUndefined, isFalse);
       expect(canBeNull.allowsNull, isTrue);
@@ -216,7 +214,7 @@
 }
     ''';
       final output = parseString(input);
-      final Interface interface = output[0];
+      final interface = output[0] as Interface;
       expect(interface.commentText, equals('''
 Describes the what this class in lots of words that wrap onto multiple lines that will need re-wrapping to format nicely when converted into Dart.
 
@@ -239,7 +237,7 @@
       final output = parseString(input);
       expect(output, hasLength(1));
       expect(output[0], const TypeMatcher<TypeAlias>());
-      final TypeAlias typeAlias = output[0];
+      final typeAlias = output[0] as TypeAlias;
       expect(typeAlias.name, equals('DocumentSelector'));
       expect(typeAlias.baseType, isArrayOf(isSimpleType('DocumentFilter')));
     });
@@ -254,24 +252,24 @@
       // Results should be the two inline interfaces followed by the type alias.
 
       expect(output[0], const TypeMatcher<InlineInterface>());
-      final InlineInterface interface1 = output[0];
+      final interface1 = output[0] as InlineInterface;
       expect(interface1.name, equals('NameOrLength1'));
       expect(interface1.members, hasLength(1));
       expect(interface1.members[0].name, equals('name'));
 
       expect(output[1], const TypeMatcher<InlineInterface>());
-      final InlineInterface interface2 = output[1];
+      final interface2 = output[1] as InlineInterface;
       expect(interface2.name, equals('NameOrLength2'));
       expect(interface2.members, hasLength(1));
       expect(interface2.members[0].name, equals('length'));
 
       expect(output[2], const TypeMatcher<TypeAlias>());
-      final TypeAlias typeAlias = output[2];
+      final typeAlias = output[2] as TypeAlias;
       expect(typeAlias.name, equals('NameOrLength'));
       expect(typeAlias.baseType, const TypeMatcher<UnionType>());
 
       // The type alias should be a union of the two types above.
-      UnionType union = typeAlias.baseType;
+      final union = typeAlias.baseType as UnionType;
       expect(union.types, hasLength(2));
       expect(union.types[0], isSimpleType(interface1.name));
       expect(union.types[1], isSimpleType(interface2.name));
@@ -299,12 +297,12 @@
       final output = parseString(input);
       expect(output, hasLength(1));
       expect(output[0], const TypeMatcher<Namespace>());
-      final Namespace namespace = output[0];
+      final namespace = output[0] as Namespace;
       expect(namespace.members, hasLength(3));
       namespace.members.forEach((m) => expect(m, const TypeMatcher<Const>()));
-      final Const create = namespace.members[0],
-          delete = namespace.members[1],
-          rename = namespace.members[2];
+      final create = namespace.members[0] as Const,
+          delete = namespace.members[1] as Const,
+          rename = namespace.members[2] as Const;
       expect(create.name, equals('Create'));
       expect(create.type, isSimpleType('ResourceOperationKind'));
       expect(create.commentText,
@@ -346,13 +344,13 @@
       final output = parseString(input);
       expect(output, hasLength(1));
       expect(output[0], const TypeMatcher<Interface>());
-      final Interface interface = output[0];
+      final interface = output[0] as Interface;
       expect(interface.members, hasLength(1));
-      final Field field = interface.members.first;
+      final field = interface.members.first as Field;
       expect(field, const TypeMatcher<Field>());
       expect(field.name, equals('label'));
       expect(field.type, const TypeMatcher<UnionType>());
-      UnionType union = field.type;
+      final union = field.type as UnionType;
       expect(union.types, hasLength(2));
       expect(union.types[0], isSimpleType('string'));
       expect(union.types[1], isArrayOf(isSimpleType('number')));
@@ -367,9 +365,9 @@
       final output = parseString(input);
       expect(output, hasLength(1));
       expect(output[0], const TypeMatcher<Interface>());
-      final Interface interface = output[0];
+      final interface = output[0] as Interface;
       expect(interface.members, hasLength(1));
-      final Field field = interface.members.first;
+      final field = interface.members.first as Field;
       expect(field, const TypeMatcher<Field>());
       expect(field.name, equals('label'));
       expect(field.type, isSimpleType('object'));
@@ -384,7 +382,7 @@
     ''';
       final output = parseString(input);
       expect(output, hasLength(1));
-      expect(output[0].commentNode.token.lexeme, equals('''// This is line 1
+      expect(output[0].commentNode!.token.lexeme, equals('''// This is line 1
 // This is line 2'''));
     });
 
@@ -397,11 +395,11 @@
       final output = parseString(input);
       expect(output, hasLength(1));
       expect(output[0], const TypeMatcher<Interface>());
-      final Interface interface = output[0];
+      final interface = output[0] as Interface;
       expect(interface.name, equals('MyType'));
       expect(interface.members, hasLength(1));
       expect(interface.members[0], const TypeMatcher<Field>());
-      final Field field = interface.members[0];
+      final field = interface.members[0] as Field;
       expect(field.name, equals('kind'));
       expect(field.allowsNull, isFalse);
       expect(field.allowsUndefined, isFalse);
@@ -417,16 +415,16 @@
       final output = parseString(input);
       expect(output, hasLength(1));
       expect(output[0], const TypeMatcher<Interface>());
-      final Interface interface = output[0];
+      final interface = output[0] as Interface;
       expect(interface.name, equals('MyType'));
       expect(interface.members, hasLength(1));
       expect(interface.members[0], const TypeMatcher<Field>());
-      final Field field = interface.members[0];
+      final field = interface.members[0] as Field;
       expect(field.name, equals('kind'));
       expect(field.allowsNull, isFalse);
       expect(field.allowsUndefined, isFalse);
       expect(field.type, const TypeMatcher<LiteralUnionType>());
-      LiteralUnionType union = field.type;
+      final union = field.type as LiteralUnionType;
       expect(union.types, hasLength(2));
       expect(union.types[0], isLiteralOf(isSimpleType('string'), "'one'"));
       expect(union.types[1], isLiteralOf(isSimpleType('string'), "'two'"));
diff --git a/pkg/analysis_server/test/tool/test_all.dart b/pkg/analysis_server/test/tool/test_all.dart
index 4852a64..8d2910e 100644
--- a/pkg/analysis_server/test/tool/test_all.dart
+++ b/pkg/analysis_server/test/tool/test_all.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
 import 'completion_metrics/test_all.dart' as completion_metrics;
diff --git a/pkg/analysis_server/tool/code_completion/code_metrics.dart b/pkg/analysis_server/tool/code_completion/code_metrics.dart
index 27585c6..5ff17b9 100644
--- a/pkg/analysis_server/tool/code_completion/code_metrics.dart
+++ b/pkg/analysis_server/tool/code_completion/code_metrics.dart
@@ -1394,11 +1394,11 @@
       if (file_paths.isDart(pathContext, filePath)) {
         try {
           var resolvedUnitResult =
-              await context.currentSession.getResolvedUnit(filePath);
+              await context.currentSession.getResolvedUnit2(filePath);
           //
           // Check for errors that cause the file to be skipped.
           //
-          if (resolvedUnitResult.state != ResultState.VALID) {
+          if (resolvedUnitResult is! ResolvedUnitResult) {
             print('File $filePath skipped because it could not be analyzed.');
             print('');
             continue;
diff --git a/pkg/analysis_server/tool/code_completion/completion_metrics.dart b/pkg/analysis_server/tool/code_completion/completion_metrics.dart
index f9c0503..31237c3 100644
--- a/pkg/analysis_server/tool/code_completion/completion_metrics.dart
+++ b/pkg/analysis_server/tool/code_completion/completion_metrics.dart
@@ -1114,8 +1114,8 @@
     for (var filePath in context.contextRoot.analyzedFiles()) {
       if (file_paths.isDart(pathContext, filePath)) {
         try {
-          _resolvedUnitResult =
-              await context.currentSession.getResolvedUnit(filePath);
+          _resolvedUnitResult = await context.currentSession
+              .getResolvedUnit2(filePath) as ResolvedUnitResult;
 
           var analysisError = getFirstErrorOrNull(_resolvedUnitResult);
           if (analysisError != null) {
@@ -1144,8 +1144,8 @@
                   content: overlayContents,
                   modificationStamp: overlayModificationStamp++);
               context.driver.changeFile(filePath);
-              resolvedUnitResult =
-                  await context.currentSession.getResolvedUnit(filePath);
+              resolvedUnitResult = await context.currentSession
+                  .getResolvedUnit2(filePath) as ResolvedUnitResult;
             }
 
             // As this point the completion suggestions are computed,
diff --git a/pkg/analysis_server/tool/code_completion/flutter_metrics.dart b/pkg/analysis_server/tool/code_completion/flutter_metrics.dart
index 9942472..ee267a5 100644
--- a/pkg/analysis_server/tool/code_completion/flutter_metrics.dart
+++ b/pkg/analysis_server/tool/code_completion/flutter_metrics.dart
@@ -207,11 +207,11 @@
       if (file_paths.isDart(pathContext, filePath)) {
         try {
           var resolvedUnitResult =
-              await context.currentSession.getResolvedUnit(filePath);
+              await context.currentSession.getResolvedUnit2(filePath);
           //
           // Check for errors that cause the file to be skipped.
           //
-          if (resolvedUnitResult.state != ResultState.VALID) {
+          if (resolvedUnitResult is! ResolvedUnitResult) {
             print('');
             print('File $filePath skipped because it could not be analyzed.');
             continue;
diff --git a/pkg/analysis_server/tool/code_completion/implicit_type_declarations.dart b/pkg/analysis_server/tool/code_completion/implicit_type_declarations.dart
index baa7300..6b0b3c1 100644
--- a/pkg/analysis_server/tool/code_completion/implicit_type_declarations.dart
+++ b/pkg/analysis_server/tool/code_completion/implicit_type_declarations.dart
@@ -184,11 +184,11 @@
       if (file_paths.isDart(pathContext, filePath)) {
         try {
           var resolvedUnitResult =
-              await context.currentSession.getResolvedUnit(filePath);
+              await context.currentSession.getResolvedUnit2(filePath);
           //
           // Check for errors that cause the file to be skipped.
           //
-          if (resolvedUnitResult.state != ResultState.VALID) {
+          if (resolvedUnitResult is! ResolvedUnitResult) {
             print('File $filePath skipped because it could not be analyzed.');
             if (verbose) {
               print('');
diff --git a/pkg/analysis_server/tool/code_completion/relevance_metrics.dart b/pkg/analysis_server/tool/code_completion/relevance_metrics.dart
index 47717d2..0975c8a 100644
--- a/pkg/analysis_server/tool/code_completion/relevance_metrics.dart
+++ b/pkg/analysis_server/tool/code_completion/relevance_metrics.dart
@@ -1940,11 +1940,11 @@
       if (file_paths.isDart(pathContext, filePath)) {
         try {
           var resolvedUnitResult =
-              await context.currentSession.getResolvedUnit(filePath);
+              await context.currentSession.getResolvedUnit2(filePath);
           //
           // Check for errors that cause the file to be skipped.
           //
-          if (resolvedUnitResult.state != ResultState.VALID) {
+          if (resolvedUnitResult is! ResolvedUnitResult) {
             print('File $filePath skipped because it could not be analyzed.');
             if (verbose) {
               print('');
diff --git a/pkg/analysis_server/tool/code_completion/relevance_table_generator.dart b/pkg/analysis_server/tool/code_completion/relevance_table_generator.dart
index e5848fa..52b219e 100644
--- a/pkg/analysis_server/tool/code_completion/relevance_table_generator.dart
+++ b/pkg/analysis_server/tool/code_completion/relevance_table_generator.dart
@@ -1459,11 +1459,11 @@
       if (file_paths.isDart(pathContext, filePath)) {
         try {
           var resolvedUnitResult =
-              await context.currentSession.getResolvedUnit(filePath);
+              await context.currentSession.getResolvedUnit2(filePath);
           //
           // Check for errors that cause the file to be skipped.
           //
-          if (resolvedUnitResult.state != ResultState.VALID) {
+          if (resolvedUnitResult is! ResolvedUnitResult) {
             print('File $filePath skipped because it could not be analyzed.');
             if (verbose) {
               print('');
diff --git a/pkg/analysis_server/tool/lsp_spec/generate_all.dart b/pkg/analysis_server/tool/lsp_spec/generate_all.dart
index e6bd416..1cd4b7b 100644
--- a/pkg/analysis_server/tool/lsp_spec/generate_all.dart
+++ b/pkg/analysis_server/tool/lsp_spec/generate_all.dart
@@ -271,6 +271,13 @@
       ],
       baseType: 'CompletionItemResolutionInfo',
     ),
+    interface(
+      'PubPackageCompletionItemResolutionInfo',
+      [
+        field('packageName', type: 'string'),
+      ],
+      baseType: 'CompletionItemResolutionInfo',
+    ),
     // Custom types for experimental SnippetTextEdits
     // https://github.com/rust-analyzer/rust-analyzer/blob/b35559a2460e7f0b2b79a7029db0c5d4e0acdb44/docs/dev/lsp-extensions.md#snippet-textedit
     interface(
diff --git a/pkg/analyzer/CHANGELOG.md b/pkg/analyzer/CHANGELOG.md
index d692e51..aed9fad 100644
--- a/pkg/analyzer/CHANGELOG.md
+++ b/pkg/analyzer/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 1.5.0-dev
+* Deprecated `AnalysisSession.getUnitElement()`.
+  Use `AnalysisSession.getUnitElement2()` instead.
+* Deprecated `AnalysisSession.getResolvedUnit()`.
+  Use `AnalysisSession.getResolvedUnit2()` instead.
+
 ## 1.4.0
 * Deprecated `TypeProvider.nonSubtypableClasses`.
   Use `TypeProvider.isNonSubtypableClass` instead.
diff --git a/pkg/analyzer/doc/tutorial/ast.md b/pkg/analyzer/doc/tutorial/ast.md
index e39a89b..2adab31 100644
--- a/pkg/analyzer/doc/tutorial/ast.md
+++ b/pkg/analyzer/doc/tutorial/ast.md
@@ -65,8 +65,10 @@
 
 ```dart
 void processFile(AnalysisSession session, String path) async {
-  ResolvedUnitResult result = await session.getResolvedUnit(path);
-  CompilationUnit unit = result.unit;
+  var result = await session.getResolvedUnit2(path);
+  if (result is ResolvedUnitResult) {
+    CompilationUnit unit = result.unit;
+  }
 }
 ```
 
diff --git a/pkg/analyzer/doc/tutorial/element.md b/pkg/analyzer/doc/tutorial/element.md
index 5464d12..b0169e4 100644
--- a/pkg/analyzer/doc/tutorial/element.md
+++ b/pkg/analyzer/doc/tutorial/element.md
@@ -46,8 +46,10 @@
 
 ```dart
 void analyzeSingleFile(AnalysisSession session, String path) async {
-  UnitElementResult result = await session.getUnitElement(path);
-  CompilationUnitElement element = result.element;
+  var result = await session.getUnitElement2(path);
+  if (result is UnitElementResult) {
+    CompilationUnitElement element = result.element;
+  }
 }
 ```
 
diff --git a/pkg/analyzer/lib/dart/analysis/results.dart b/pkg/analyzer/lib/dart/analysis/results.dart
index 4a17594..ca07131 100644
--- a/pkg/analyzer/lib/dart/analysis/results.dart
+++ b/pkg/analyzer/lib/dart/analysis/results.dart
@@ -81,6 +81,29 @@
   LineInfo get lineInfo;
 }
 
+/// The type of [InvalidResult] returned when the given file path is invalid,
+/// for example is not absolute and normalized.
+///
+/// Clients may not extend, implement or mix-in this class.
+class InvalidPathResult
+    implements InvalidResult, SomeResolvedUnitResult, SomeUnitElementResult {}
+
+/// The base class for any invalid result.
+///
+/// Clients may not extend, implement or mix-in this class.
+abstract class InvalidResult {}
+
+/// The type of [InvalidResult] returned when the given file path does not
+/// represent the corresponding URI.
+///
+/// This usually happens in Bazel workspaces, when a URI is resolved to
+/// a generated file, but there is also a writable file to which this URI
+/// would be resolved, if there were no generated file.
+///
+/// Clients may not extend, implement or mix-in this class.
+class NotPathOfUriResult
+    implements InvalidResult, SomeResolvedUnitResult, SomeUnitElementResult {}
+
 /// The result of building parsed AST(s) for the whole library.
 ///
 /// Clients may not extend, implement or mix-in this class.
@@ -152,10 +175,14 @@
 /// include both syntactic and semantic errors.
 ///
 /// Clients may not extend, implement or mix-in this class.
-abstract class ResolvedUnitResult implements AnalysisResultWithErrors {
+abstract class ResolvedUnitResult
+    implements SomeResolvedUnitResult, AnalysisResultWithErrors {
   /// The content of the file that was scanned, parsed and resolved.
   String? get content;
 
+  /// Return `true` if the file exists.
+  bool get exists;
+
   /// The element representing the library containing the compilation [unit].
   LibraryElement get libraryElement;
 
@@ -192,10 +219,28 @@
   VALID
 }
 
+/// The result of building a resolved AST for a single file. The errors returned
+/// include both syntactic and semantic errors.
+///
+/// Clients may not extend, implement or mix-in this class.
+///
+/// There are existing implementations of this class.
+/// [ResolvedUnitResult] represents a valid result.
+abstract class SomeResolvedUnitResult {}
+
 /// The result of building the element model for a single file.
 ///
 /// Clients may not extend, implement or mix-in this class.
-abstract class UnitElementResult implements AnalysisResult {
+///
+/// There are existing implementations of this class.
+/// [UnitElementResult] represents a valid result.
+abstract class SomeUnitElementResult {}
+
+/// The result of building the element model for a single file.
+///
+/// Clients may not extend, implement or mix-in this class.
+abstract class UnitElementResult
+    implements SomeUnitElementResult, AnalysisResult {
   /// The element of the file.
   CompilationUnitElement get element;
 
diff --git a/pkg/analyzer/lib/dart/analysis/session.dart b/pkg/analyzer/lib/dart/analysis/session.dart
index 9fe33e3..ff10b22 100644
--- a/pkg/analyzer/lib/dart/analysis/session.dart
+++ b/pkg/analyzer/lib/dart/analysis/session.dart
@@ -80,8 +80,13 @@
 
   /// Return a future that will complete with information about the results of
   /// resolving the file with the given absolute, normalized [path].
+  @Deprecated('Use getResolvedUnit2() instead')
   Future<ResolvedUnitResult> getResolvedUnit(String path);
 
+  /// Return a future that will complete with information about the results of
+  /// resolving the file with the given absolute, normalized [path].
+  Future<SomeResolvedUnitResult> getResolvedUnit2(String path);
+
   /// Return a future that will complete with the source kind of the file with
   /// the given absolute, normalized [path]. If the path does not represent a
   /// file or if the kind of the file cannot be determined, then the future will
@@ -93,12 +98,18 @@
   /// Return a future that will complete with information about the results of
   /// building the element model for the file with the given absolute,
   /// normalized [path].
+  @Deprecated('Use getUnitElement2() instead')
   Future<UnitElementResult> getUnitElement(String path);
 
+  /// Return a future that will complete with information about the results of
+  /// building the element model for the file with the given absolute,
+  /// normalized [path].
+  Future<SomeUnitElementResult> getUnitElement2(String path);
+
   /// Return a future that will complete with the signature for the file with
   /// the given absolute, normalized [path], or `null` if the file cannot be
   /// analyzed. This is the same signature returned in the result from
-  /// [getUnitElement].
+  /// [getUnitElement2].
   ///
   /// The signature is based on the APIs of the files of the library (including
   /// the file itself), and the transitive closure of files imported and
diff --git a/pkg/analyzer/lib/dart/analysis/utilities.dart b/pkg/analyzer/lib/dart/analysis/utilities.dart
index 801d224..50e0677 100644
--- a/pkg/analyzer/lib/dart/analysis/utilities.dart
+++ b/pkg/analyzer/lib/dart/analysis/utilities.dart
@@ -113,6 +113,7 @@
 /// create one or more contexts and use those contexts to resolve the files.
 ///
 /// TODO(migration): should not be nullable
+@Deprecated('Use resolveFile2() instead')
 Future<ResolvedUnitResult?> resolveFile(
     {required String path, ResourceProvider? resourceProvider}) async {
   AnalysisContext context =
@@ -120,6 +121,20 @@
   return await context.currentSession.getResolvedUnit(path);
 }
 
+/// Return the result of resolving the file at the given [path].
+///
+/// If a [resourceProvider] is given, it will be used to access the file system.
+///
+/// Note that if more than one file is going to be resolved then this function
+/// is inefficient. Clients should instead use [AnalysisContextCollection] to
+/// create one or more contexts and use those contexts to resolve the files.
+Future<SomeResolvedUnitResult> resolveFile2(
+    {required String path, ResourceProvider? resourceProvider}) async {
+  AnalysisContext context =
+      _createAnalysisContext(path: path, resourceProvider: resourceProvider);
+  return await context.currentSession.getResolvedUnit2(path);
+}
+
 /// Return a newly create analysis context in which the file at the given [path]
 /// can be analyzed.
 ///
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index 6024381..ca35727 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -151,7 +151,7 @@
   final _priorityFiles = <String>{};
 
   /// The mapping from the files for which analysis was requested using
-  /// [getResult] to the [Completer]s to report the result.
+  /// [getResult2] to the [Completer]s to report the result.
   final _requestedFiles = <String, List<Completer<ResolvedUnitResult>>>{};
 
   /// The mapping from the files for which analysis was requested using
@@ -193,18 +193,18 @@
   final _unitElementSignatureParts = <String, List<Completer<String>>>{};
 
   /// The mapping from the files for which the unit element was requested using
-  /// [getUnitElement] to the [Completer]s to report the result.
+  /// [getUnitElement2] to the [Completer]s to report the result.
   final _unitElementRequestedFiles =
       <String, List<Completer<UnitElementResult>>>{};
 
   /// The mapping from the files for which the unit element was requested using
-  /// [getUnitElement], and which were found to be parts without known libraries,
-  /// to the [Completer]s to report the result.
+  /// [getUnitElement2], and which were found to be parts without known
+  /// libraries, to the [Completer]s to report the result.
   final _unitElementRequestedParts =
       <String, List<Completer<UnitElementResult>>>{};
 
   /// The mapping from the files for which analysis was requested using
-  /// [getResult], and which were found to be parts without known libraries,
+  /// [getResult2], and which were found to be parts without known libraries,
   /// to the [Completer]s to report the result.
   final _requestedParts = <String, List<Completer<ResolvedUnitResult>>>{};
 
@@ -360,7 +360,7 @@
   /// client does not change the state of the files.
   ///
   /// Results might be produced even for files that have never been added
-  /// using [addFile], for example when [getResult] was called for a file.
+  /// using [addFile], for example when [getResult2] was called for a file.
   Stream<ResolvedUnitResult> get results => _onResults;
 
   /// Return the search support for the driver.
@@ -465,7 +465,7 @@
   /// transitions to "idle".
   ///
   /// Invocation of this method will not prevent a [Future] returned from
-  /// [getResult] from completing with a result, but the result is not
+  /// [getResult2] from completing with a result, but the result is not
   /// guaranteed to be consistent with the new current file state after this
   /// [changeFile] invocation.
   void changeFile(String path) {
@@ -632,7 +632,12 @@
           throw ArgumentError('$uri is not a library.');
         }
 
-        var unitResult = await getUnitElement(file.path);
+        var unitResult = await getUnitElement2(file.path);
+        // TODO(scheglov) this method should also return a result hierarchy
+        if (unitResult is! UnitElementResult) {
+          throw ArgumentError('$uri has no valid result.');
+        }
+
         return unitResult.element.library;
       },
       (externalLibrary) async {
@@ -789,12 +794,46 @@
   /// it, which is consistent with the current file state (including new states
   /// of the files previously reported using [changeFile]), prior to the next
   /// time the analysis state transitions to "idle".
+  @Deprecated('Use getResult2() instead')
   Future<ResolvedUnitResult> getResult(String path,
-      {bool sendCachedToStream = false}) {
+      {bool sendCachedToStream = false}) async {
     _throwIfNotAbsolutePath(path);
+
+    var result = await getResult2(path, sendCachedToStream: sendCachedToStream);
+    if (result is NotPathOfUriResult) {
+      return NotValidResolvedUnitResultImpl(ResultState.NOT_FILE_OF_URI);
+    }
+
+    return result as ResolvedUnitResult;
+  }
+
+  /// Return a [Future] that completes with a [SomeResolvedUnitResult] for the
+  /// Dart file with the given [path].
+  ///
+  /// The [path] must be absolute and normalized.
+  ///
+  /// The [path] can be any file - explicitly or implicitly analyzed, or neither.
+  ///
+  /// If the driver has the cached analysis result for the file, it is returned.
+  /// If [sendCachedToStream] is `true`, then the result is also reported into
+  /// the [results] stream, just as if it were freshly computed.
+  ///
+  /// Otherwise causes the analysis state to transition to "analyzing" (if it is
+  /// not in that state already), the driver will produce the analysis result for
+  /// it, which is consistent with the current file state (including new states
+  /// of the files previously reported using [changeFile]), prior to the next
+  /// time the analysis state transitions to "idle".
+  Future<SomeResolvedUnitResult> getResult2(String path,
+      {bool sendCachedToStream = false}) {
+    if (!_isAbsolutePath(path)) {
+      return Future.value(
+        InvalidPathResult(),
+      );
+    }
+
     if (!_fsState.hasUri(path)) {
       return Future.value(
-        NotValidResolvedUnitResultImpl(ResultState.NOT_FILE_OF_URI),
+        NotPathOfUriResult(),
       );
     }
 
@@ -834,13 +873,35 @@
 
   /// Return a [Future] that completes with the [UnitElementResult] for the
   /// file with the given [path].
-  Future<UnitElementResult> getUnitElement(String path) {
+  @Deprecated('Use getUnitElement2() instead')
+  Future<UnitElementResult> getUnitElement(String path) async {
     _throwIfNotAbsolutePath(path);
-    if (!_fsState.hasUri(path)) {
+    var result = await getUnitElement2(path);
+
+    if (result is NotPathOfUriResult) {
       return Future.value(
         NotValidUnitElementResultImpl(ResultState.NOT_FILE_OF_URI),
       );
     }
+
+    return result as UnitElementResult;
+  }
+
+  /// Return a [Future] that completes with the [SomeUnitElementResult]
+  /// for the file with the given [path].
+  Future<SomeUnitElementResult> getUnitElement2(String path) {
+    if (!_isAbsolutePath(path)) {
+      return Future.value(
+        InvalidPathResult(),
+      );
+    }
+
+    if (!_fsState.hasUri(path)) {
+      return Future.value(
+        NotPathOfUriResult(),
+      );
+    }
+
     var completer = Completer<UnitElementResult>();
     _unitElementRequestedFiles
         .putIfAbsent(path, () => <Completer<UnitElementResult>>[])
@@ -1656,6 +1717,10 @@
     );
   }
 
+  bool _isAbsolutePath(String path) {
+    return _resourceProvider.pathContext.isAbsolute(path);
+  }
+
   /// We detected that one of the required `dart` libraries is missing.
   /// Return the empty analysis result with the error.
   AnalysisResult _newMissingDartLibraryResult(
@@ -1773,7 +1838,7 @@
   /// The driver supports only absolute paths, this method is used to validate
   /// any input paths to prevent errors later.
   void _throwIfNotAbsolutePath(String path) {
-    if (!_resourceProvider.pathContext.isAbsolute(path)) {
+    if (!_isAbsolutePath(path)) {
       throw ArgumentError('Only absolute paths are supported: $path');
     }
   }
diff --git a/pkg/analyzer/lib/src/dart/analysis/results.dart b/pkg/analyzer/lib/src/dart/analysis/results.dart
index c2e5b28..2ddf619 100644
--- a/pkg/analyzer/lib/src/dart/analysis/results.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/results.dart
@@ -133,6 +133,11 @@
   }
 
   @override
+  bool get exists {
+    throw StateError('This result is not valid');
+  }
+
+  @override
   LibraryElement get libraryElement {
     throw StateError('This result is not valid');
   }
@@ -331,7 +336,7 @@
 
 class ResolvedUnitResultImpl extends FileResultImpl
     implements ResolvedUnitResult {
-  /// Return `true` if the file exists.
+  @override
   final bool exists;
 
   @override
diff --git a/pkg/analyzer/lib/src/dart/analysis/search.dart b/pkg/analyzer/lib/src/dart/analysis/search.dart
index 9e3c4b0..c460b9e 100644
--- a/pkg/analyzer/lib/src/dart/analysis/search.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/search.dart
@@ -54,8 +54,8 @@
     List<String> files = await _driver.getFilesDefiningClassMemberName(name);
     for (String file in files) {
       if (searchedFiles.add(file, this)) {
-        var unitResult = await _driver.getUnitElement(file);
-        if (unitResult.state == ResultState.VALID) {
+        var unitResult = await _driver.getUnitElement2(file);
+        if (unitResult is UnitElementResult) {
           unitResult.element.types.forEach(addElements);
           unitResult.element.mixins.forEach(addElements);
         }
@@ -174,8 +174,8 @@
 
     List<FileState> knownFiles = _driver.fsState.knownFiles.toList();
     for (FileState file in knownFiles) {
-      var unitResult = await _driver.getUnitElement(file.path);
-      if (unitResult.state == ResultState.VALID) {
+      var unitResult = await _driver.getUnitElement2(file.path);
+      if (unitResult is UnitElementResult) {
         CompilationUnitElement unitElement = unitResult.element;
         unitElement.accessors.forEach(addElement);
         unitElement.enums.forEach(addElement);
@@ -283,8 +283,8 @@
   }
 
   Future<CompilationUnitElement?> _getUnitElement(String file) async {
-    var result = await _driver.getUnitElement(file);
-    return result.state == ResultState.VALID ? result.element : null;
+    var result = await _driver.getUnitElement2(file);
+    return result is UnitElementResult ? result.element : null;
   }
 
   Future<List<SearchResult>> _searchReferences(
@@ -378,11 +378,12 @@
     LibraryElement libraryElement = element.library;
     for (CompilationUnitElement unitElement in libraryElement.units) {
       String unitPath = unitElement.source.fullName;
-      ResolvedUnitResult unitResult = await _driver.getResult(unitPath);
-      _ImportElementReferencesVisitor visitor =
-          _ImportElementReferencesVisitor(element, unitElement);
-      unitResult.unit!.accept(visitor);
-      results.addAll(visitor.results);
+      var unitResult = await _driver.getResult2(unitPath);
+      if (unitResult is ResolvedUnitResult) {
+        var visitor = _ImportElementReferencesVisitor(element, unitElement);
+        unitResult.unit!.accept(visitor);
+        results.addAll(visitor.results);
+      }
     }
     return results;
   }
@@ -397,17 +398,22 @@
     List<SearchResult> results = <SearchResult>[];
     for (CompilationUnitElement unitElement in element.units) {
       String unitPath = unitElement.source.fullName;
-      ResolvedUnitResult unitResult = await _driver.getResult(unitPath);
-      CompilationUnit unit = unitResult.unit!;
-      for (Directive directive in unit.directives) {
-        if (directive is PartOfDirective && directive.element == element) {
-          results.add(SearchResult._(
-              unit.declaredElement!,
-              SearchResultKind.REFERENCE,
-              directive.libraryName!.offset,
-              directive.libraryName!.length,
-              true,
-              false));
+      var unitResult = await _driver.getResult2(unitPath);
+      if (unitResult is ResolvedUnitResult) {
+        CompilationUnit unit = unitResult.unit!;
+        for (Directive directive in unit.directives) {
+          if (directive is PartOfDirective && directive.element == element) {
+            results.add(
+              SearchResult._(
+                unit.declaredElement!,
+                SearchResultKind.REFERENCE,
+                directive.libraryName!.offset,
+                directive.libraryName!.length,
+                true,
+                false,
+              ),
+            );
+          }
         }
       }
     }
@@ -422,7 +428,10 @@
     }
 
     // Prepare the unit.
-    ResolvedUnitResult unitResult = await _driver.getResult(path);
+    var unitResult = await _driver.getResult2(path);
+    if (unitResult is! ResolvedUnitResult) {
+      return const <SearchResult>[];
+    }
     var unit = unitResult.unit;
     if (unit == null) {
       return const <SearchResult>[];
@@ -475,11 +484,12 @@
     LibraryElement libraryElement = element.library;
     for (CompilationUnitElement unitElement in libraryElement.units) {
       String unitPath = unitElement.source.fullName;
-      ResolvedUnitResult unitResult = await _driver.getResult(unitPath);
-      _LocalReferencesVisitor visitor =
-          _LocalReferencesVisitor(element, unitElement);
-      unitResult.unit!.accept(visitor);
-      results.addAll(visitor.results);
+      var unitResult = await _driver.getResult2(unitPath);
+      if (unitResult is ResolvedUnitResult) {
+        var visitor = _LocalReferencesVisitor(element, unitElement);
+        unitResult.unit!.accept(visitor);
+        results.addAll(visitor.results);
+      }
     }
     return results;
   }
diff --git a/pkg/analyzer/lib/src/dart/analysis/session.dart b/pkg/analyzer/lib/src/dart/analysis/session.dart
index 7153b15..9af60f9 100644
--- a/pkg/analyzer/lib/src/dart/analysis/session.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/session.dart
@@ -114,6 +114,7 @@
     return _driver.getResolvedLibraryByUri(element.source.uri);
   }
 
+  @Deprecated('Use getResolvedUnit2() instead')
   @override
   Future<ResolvedUnitResult> getResolvedUnit(String path) {
     _checkConsistency();
@@ -121,11 +122,18 @@
   }
 
   @override
+  Future<SomeResolvedUnitResult> getResolvedUnit2(String path) {
+    _checkConsistency();
+    return _driver.getResult2(path);
+  }
+
+  @override
   Future<SourceKind?> getSourceKind(String path) {
     _checkConsistency();
     return _driver.getSourceKind(path);
   }
 
+  @Deprecated('Use getUnitElement2() instead')
   @override
   Future<UnitElementResult> getUnitElement(String path) {
     _checkConsistency();
@@ -133,6 +141,12 @@
   }
 
   @override
+  Future<SomeUnitElementResult> getUnitElement2(String path) {
+    _checkConsistency();
+    return _driver.getUnitElement2(path);
+  }
+
+  @override
   Future<String> getUnitElementSignature(String path) {
     _checkConsistency();
     return _driver.getUnitElementSignature(path);
diff --git a/pkg/analyzer/lib/src/dart/micro/analysis_context.dart b/pkg/analyzer/lib/src/dart/micro/analysis_context.dart
index 5e45d1e7..df022bf 100644
--- a/pkg/analyzer/lib/src/dart/micro/analysis_context.dart
+++ b/pkg/analyzer/lib/src/dart/micro/analysis_context.dart
@@ -208,7 +208,7 @@
   }
 
   @override
-  Future<ResolvedUnitResult> getResolvedUnit(String path) async {
+  Future<SomeResolvedUnitResult> getResolvedUnit2(String path) async {
     return analysisContext.fileResolver.resolve(path: path);
   }
 
diff --git a/pkg/analyzer/lib/src/dart/micro/library_graph.dart b/pkg/analyzer/lib/src/dart/micro/library_graph.dart
index f4cce34..2c10c45 100644
--- a/pkg/analyzer/lib/src/dart/micro/library_graph.dart
+++ b/pkg/analyzer/lib/src/dart/micro/library_graph.dart
@@ -28,6 +28,7 @@
 import 'package:analyzer/src/summary/idl.dart';
 import 'package:analyzer/src/summary/link.dart' as graph
     show DependencyWalker, Node;
+import 'package:analyzer/src/util/file_paths.dart' as file_paths;
 import 'package:analyzer/src/util/performance/operation_performance.dart';
 import 'package:analyzer/src/workspace/workspace.dart';
 import 'package:collection/collection.dart';
@@ -211,6 +212,7 @@
   }
 
   void refresh({
+    FileState? containingLibrary,
     required OperationPerformanceImpl performance,
   }) {
     _fsState.testView.refreshedFiles.add(path);
@@ -282,6 +284,7 @@
     }
     for (var uri in unlinked2.parts) {
       var file = _fileForRelativeUri(
+        containingLibrary: this,
         relativeUri: uri,
         performance: performance,
       );
@@ -290,15 +293,24 @@
       }
     }
     if (unlinked2.hasPartOfDirective) {
-      var uri = unlinked2.partOfUri;
-      if (uri.isNotEmpty) {
-        partOfLibrary = _fileForRelativeUri(
-          relativeUri: uri,
-          performance: performance,
-        );
-        if (partOfLibrary != null) {
-          directReferencedFiles.add(partOfLibrary!);
+      if (containingLibrary == null) {
+        _fsState.testView.partsDiscoveredLibraries.add(path);
+        var libraryName = unlinked2.partOfName;
+        var libraryUri = unlinked2.partOfUri;
+        partOfLibrary = null;
+        if (libraryName.isNotEmpty) {
+          _findPartOfNameLibrary(performance: performance);
+        } else if (libraryUri.isNotEmpty) {
+          partOfLibrary = _fileForRelativeUri(
+            relativeUri: libraryUri,
+            performance: performance,
+          );
         }
+      } else {
+        partOfLibrary = containingLibrary;
+      }
+      if (partOfLibrary != null) {
+        directReferencedFiles.add(partOfLibrary!);
       }
     }
     libraryFiles.add(this);
@@ -318,6 +330,7 @@
   }
 
   FileState? _fileForRelativeUri({
+    FileState? containingLibrary,
     required String relativeUri,
     required OperationPerformanceImpl performance,
   }) {
@@ -333,6 +346,7 @@
     }
 
     var file = _fsState.getFileForUri(
+      containingLibrary: containingLibrary,
       uri: absoluteUri,
       performance: performance,
     );
@@ -344,6 +358,35 @@
     return file;
   }
 
+  /// This file has a `part of some.library;` directive. Because it does not
+  /// specify the URI of the library, we don't know the library for sure.
+  /// But usually the library is one of the sibling files.
+  void _findPartOfNameLibrary({
+    required OperationPerformanceImpl performance,
+  }) {
+    var resourceProvider = _fsState._resourceProvider;
+    var pathContext = resourceProvider.pathContext;
+
+    var children = <Resource>[];
+    try {
+      var parent = resourceProvider.getFile(path).parent2;
+      children = parent.getChildren();
+    } catch (_) {}
+
+    for (var siblingFile in children) {
+      if (file_paths.isDart(pathContext, siblingFile.path)) {
+        var childState = _fsState.getFileForPath(
+          path: siblingFile.path,
+          performance: performance,
+        );
+        if (childState.partedFiles.contains(this)) {
+          partOfLibrary = childState;
+          break;
+        }
+      }
+    }
+  }
+
   void _prefetchDirectReferences(UnlinkedUnit2 unlinkedUnit2) {
     if (_fsState.prefetchFiles == null) {
       return;
@@ -388,6 +431,7 @@
     var hasLibraryDirective = false;
     var hasPartOfDirective = false;
     var partOfUriStr = '';
+    var partOfName = '';
     for (var directive in unit.directives) {
       if (directive is ExportDirective) {
         var builder = _serializeNamespaceDirective(directive);
@@ -405,8 +449,12 @@
         parts.add(uriStr ?? '');
       } else if (directive is PartOfDirective) {
         hasPartOfDirective = true;
-        if (directive.uri != null) {
-          partOfUriStr = directive.uri!.stringValue!;
+        var libraryName = directive.libraryName;
+        var uriStr = directive.uri?.stringValue;
+        if (libraryName != null) {
+          partOfName = libraryName.components.map((e) => e.name).join('.');
+        } else if (uriStr != null) {
+          partOfUriStr = uriStr;
         }
       }
     }
@@ -424,6 +472,7 @@
       parts: parts,
       hasLibraryDirective: hasLibraryDirective,
       hasPartOfDirective: hasPartOfDirective,
+      partOfName: partOfName,
       partOfUri: partOfUriStr,
       lineStarts: unit.lineInfo!.lineStarts,
     );
@@ -583,6 +632,7 @@
   }
 
   FileState? getFileForUri({
+    FileState? containingLibrary,
     required Uri uri,
     required OperationPerformanceImpl performance,
   }) {
@@ -605,6 +655,7 @@
       _uriToFile[uri] = file;
 
       file.refresh(
+        containingLibrary: containingLibrary,
         performance: performance,
       );
     }
@@ -646,6 +697,7 @@
 
 class FileSystemStateTestView {
   final List<String> refreshedFiles = [];
+  final List<String> partsDiscoveredLibraries = [];
   Set<String> removedPaths = {};
 }
 
diff --git a/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart
index 93cc5b6..c5013b7 100644
--- a/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart
@@ -2,6 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart';
 import 'package:analyzer/dart/analysis/features.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/ast/syntactic_entity.dart';
@@ -119,7 +120,8 @@
   }
 
   /// Perform upward inference for the override.
-  void resolveOverride(ExtensionOverride node) {
+  void resolveOverride(ExtensionOverride node,
+      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList) {
     var nodeImpl = node as ExtensionOverrideImpl;
     var element = node.staticElement!;
     var typeParameters = element.typeParameters;
@@ -173,10 +175,14 @@
       _errorReporter.reportErrorForNode(
           CompileTimeErrorCode.USE_OF_VOID_RESULT, receiverExpression);
     } else if (!_typeSystem.isAssignableTo(receiverType, node.extendedType!)) {
+      var whyNotPromoted =
+          whyNotPromotedList.isEmpty ? null : whyNotPromotedList[0];
       _errorReporter.reportErrorForNode(
         CompileTimeErrorCode.EXTENSION_OVERRIDE_ARGUMENT_NOT_ASSIGNABLE,
         receiverExpression,
         [receiverType, node.extendedType],
+        _resolver.computeWhyNotPromotedMessages(
+            receiverExpression, whyNotPromoted?.call()),
       );
     }
   }
diff --git a/pkg/analyzer/lib/src/error/unused_local_elements_verifier.dart b/pkg/analyzer/lib/src/error/unused_local_elements_verifier.dart
index c8b4452..45c9c66 100644
--- a/pkg/analyzer/lib/src/error/unused_local_elements_verifier.dart
+++ b/pkg/analyzer/lib/src/error/unused_local_elements_verifier.dart
@@ -687,6 +687,11 @@
   }
 
   void addMember(Element? element) {
+    // Store un-parameterized members.
+    if (element is ExecutableMember) {
+      element = element.declaration;
+    }
+
     if (element != null) {
       members.add(element);
     }
diff --git a/pkg/analyzer/lib/src/generated/error_detection_helpers.dart b/pkg/analyzer/lib/src/generated/error_detection_helpers.dart
index ce69393..b53515f 100644
--- a/pkg/analyzer/lib/src/generated/error_detection_helpers.dart
+++ b/pkg/analyzer/lib/src/generated/error_detection_helpers.dart
@@ -56,7 +56,7 @@
       {bool promoteParameterToNullable = false,
       Map<DartType, NonPromotionReason> Function()? whyNotPromoted}) {
     _checkForArgumentTypeNotAssignableForArgument2(
-      argument: argument,
+      argument: argument is NamedExpression ? argument.expression : argument,
       parameter: argument.staticParameterElement,
       promoteParameterToNullable: promoteParameterToNullable,
       whyNotPromoted: whyNotPromoted,
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index b878793..a6f3800 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -1456,7 +1456,7 @@
         whyNotPromotedList: whyNotPromotedList);
 
     node.accept(elementResolver);
-    node.accept(typeAnalyzer);
+    extensionResolver.resolveOverride(node, whyNotPromotedList);
   }
 
   @override
diff --git a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
index ead332f..54b29b2 100644
--- a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
+++ b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
@@ -128,7 +128,8 @@
 
   @override
   void visitExtensionOverride(ExtensionOverride node) {
-    _resolver.extensionResolver.resolveOverride(node);
+    assert(false,
+        'Resolver should call extensionResolver.resolveOverride directly');
   }
 
   /// The Dart Language Specification, 12.9: <blockquote>The static type of a function literal of the
diff --git a/pkg/analyzer/lib/src/lint/project.dart b/pkg/analyzer/lib/src/lint/project.dart
index ff937d9..c98e7bd 100644
--- a/pkg/analyzer/lib/src/lint/project.dart
+++ b/pkg/analyzer/lib/src/lint/project.dart
@@ -134,16 +134,18 @@
     for (Source source in sources!) {
       String path = source.uri.path;
       if (path.startsWith(libDir) && !path.startsWith(libSrcDir)) {
-        ResolvedUnitResult result = await driver.getResult(source.fullName);
-        LibraryElement library = result.libraryElement;
+        var result = await driver.getResult2(source.fullName);
+        if (result is ResolvedUnitResult) {
+          LibraryElement library = result.libraryElement;
 
-        NamespaceBuilder namespaceBuilder = NamespaceBuilder();
-        Namespace exports =
-            namespaceBuilder.createExportNamespaceForLibrary(library);
-        Namespace public =
-            namespaceBuilder.createPublicNamespaceForLibrary(library);
-        elements.addAll(exports.definedNames.values);
-        elements.addAll(public.definedNames.values);
+          NamespaceBuilder namespaceBuilder = NamespaceBuilder();
+          Namespace exports =
+              namespaceBuilder.createExportNamespaceForLibrary(library);
+          Namespace public =
+              namespaceBuilder.createPublicNamespaceForLibrary(library);
+          elements.addAll(exports.definedNames.values);
+          elements.addAll(public.definedNames.values);
+        }
       }
     }
   }
diff --git a/pkg/analyzer/lib/src/summary/format.dart b/pkg/analyzer/lib/src/summary/format.dart
index c98c9d6..edf1d2f 100644
--- a/pkg/analyzer/lib/src/summary/format.dart
+++ b/pkg/analyzer/lib/src/summary/format.dart
@@ -4444,6 +4444,7 @@
   bool? _hasPartOfDirective;
   List<UnlinkedNamespaceDirectiveBuilder>? _imports;
   List<int>? _lineStarts;
+  String? _partOfName;
   String? _partOfUri;
   List<String>? _parts;
 
@@ -4501,9 +4502,17 @@
   }
 
   @override
+  String get partOfName => _partOfName ??= '';
+
+  /// The library name of the `part of my.name;` directive.
+  set partOfName(String value) {
+    this._partOfName = value;
+  }
+
+  @override
   String get partOfUri => _partOfUri ??= '';
 
-  /// URI of the `part of` directive.
+  /// URI of the `part of 'uri';` directive.
   set partOfUri(String value) {
     this._partOfUri = value;
   }
@@ -4523,6 +4532,7 @@
       bool? hasPartOfDirective,
       List<UnlinkedNamespaceDirectiveBuilder>? imports,
       List<int>? lineStarts,
+      String? partOfName,
       String? partOfUri,
       List<String>? parts})
       : _apiSignature = apiSignature,
@@ -4531,6 +4541,7 @@
         _hasPartOfDirective = hasPartOfDirective,
         _imports = imports,
         _lineStarts = lineStarts,
+        _partOfName = partOfName,
         _partOfUri = partOfUri,
         _parts = parts;
 
@@ -4582,6 +4593,7 @@
     }
     signatureSink.addBool(this._hasLibraryDirective == true);
     signatureSink.addString(this._partOfUri ?? '');
+    signatureSink.addString(this._partOfName ?? '');
   }
 
   List<int> toBuffer() {
@@ -4594,6 +4606,7 @@
     fb.Offset? offset_exports;
     fb.Offset? offset_imports;
     fb.Offset? offset_lineStarts;
+    fb.Offset? offset_partOfName;
     fb.Offset? offset_partOfUri;
     fb.Offset? offset_parts;
     var apiSignature = _apiSignature;
@@ -4614,6 +4627,10 @@
     if (!(lineStarts == null || lineStarts.isEmpty)) {
       offset_lineStarts = fbBuilder.writeListUint32(lineStarts);
     }
+    var partOfName = _partOfName;
+    if (partOfName != null) {
+      offset_partOfName = fbBuilder.writeString(partOfName);
+    }
     var partOfUri = _partOfUri;
     if (partOfUri != null) {
       offset_partOfUri = fbBuilder.writeString(partOfUri);
@@ -4638,6 +4655,9 @@
     if (offset_lineStarts != null) {
       fbBuilder.addOffset(5, offset_lineStarts);
     }
+    if (offset_partOfName != null) {
+      fbBuilder.addOffset(8, offset_partOfName);
+    }
     if (offset_partOfUri != null) {
       fbBuilder.addOffset(7, offset_partOfUri);
     }
@@ -4675,6 +4695,7 @@
   bool? _hasPartOfDirective;
   List<idl.UnlinkedNamespaceDirective>? _imports;
   List<int>? _lineStarts;
+  String? _partOfName;
   String? _partOfUri;
   List<String>? _parts;
 
@@ -4717,6 +4738,12 @@
   }
 
   @override
+  String get partOfName {
+    return _partOfName ??=
+        const fb.StringReader().vTableGet(_bc, _bcOffset, 8, '');
+  }
+
+  @override
   String get partOfUri {
     return _partOfUri ??=
         const fb.StringReader().vTableGet(_bc, _bcOffset, 7, '');
@@ -4759,6 +4786,10 @@
     if (local_lineStarts.isNotEmpty) {
       _result["lineStarts"] = local_lineStarts;
     }
+    var local_partOfName = partOfName;
+    if (local_partOfName != '') {
+      _result["partOfName"] = local_partOfName;
+    }
     var local_partOfUri = partOfUri;
     if (local_partOfUri != '') {
       _result["partOfUri"] = local_partOfUri;
@@ -4778,6 +4809,7 @@
         "hasPartOfDirective": hasPartOfDirective,
         "imports": imports,
         "lineStarts": lineStarts,
+        "partOfName": partOfName,
         "partOfUri": partOfUri,
         "parts": parts,
       };
diff --git a/pkg/analyzer/lib/src/summary/format.fbs b/pkg/analyzer/lib/src/summary/format.fbs
index bdce0bf..43b6eb4 100644
--- a/pkg/analyzer/lib/src/summary/format.fbs
+++ b/pkg/analyzer/lib/src/summary/format.fbs
@@ -498,7 +498,10 @@
   /// Offsets of the first character of each line in the source code.
   lineStarts:[uint] (id: 5);
 
-  /// URI of the `part of` directive.
+  /// The library name of the `part of my.name;` directive.
+  partOfName:string (id: 8);
+
+  /// URI of the `part of 'uri';` directive.
   partOfUri:string (id: 7);
 
   /// URIs of `part` directives.
diff --git a/pkg/analyzer/lib/src/summary/idl.dart b/pkg/analyzer/lib/src/summary/idl.dart
index d66803a..58625f0 100644
--- a/pkg/analyzer/lib/src/summary/idl.dart
+++ b/pkg/analyzer/lib/src/summary/idl.dart
@@ -666,7 +666,11 @@
   @Id(5)
   List<int> get lineStarts;
 
-  /// URI of the `part of` directive.
+  /// The library name of the `part of my.name;` directive.
+  @Id(8)
+  String get partOfName;
+
+  /// URI of the `part of 'uri';` directive.
   @Id(7)
   String get partOfUri;
 
diff --git a/pkg/analyzer/lib/src/util/ast_data_extractor.dart b/pkg/analyzer/lib/src/util/ast_data_extractor.dart
index 4ae6f75..18dec74 100644
--- a/pkg/analyzer/lib/src/util/ast_data_extractor.dart
+++ b/pkg/analyzer/lib/src/util/ast_data_extractor.dart
@@ -59,6 +59,12 @@
     registerValue(uri, _nodeOffset(node), id, value, node);
   }
 
+  void computeForFormalParameter(FormalParameter node, NodeId? id) {
+    if (id == null) return;
+    T? value = computeNodeValue(id, node);
+    registerValue(uri, _nodeOffset(node), id, value, node);
+  }
+
   void computeForLibrary(LibraryElement library, Id? id) {
     if (id == null) return;
     T? value = computeElementValue(id, library);
@@ -77,6 +83,12 @@
     registerValue(uri, _nodeOffset(node), id, value, node);
   }
 
+  void computeForVariableDeclaration(VariableDeclaration node, NodeId? id) {
+    if (id == null) return;
+    T? value = computeNodeValue(id, node);
+    registerValue(uri, _nodeOffset(node), id, value, node);
+  }
+
   /// Implement this to compute the data corresponding to [node].
   ///
   /// If `null` is returned, [node] has no associated data.
@@ -145,6 +157,12 @@
   }
 
   @override
+  void visitFormalParameter(FormalParameter node) {
+    computeForFormalParameter(node, computeDefaultNodeId(node));
+    super.visitFormalParameter(node);
+  }
+
+  @override
   void visitFunctionDeclaration(FunctionDeclaration node) {
     if (node.parent is CompilationUnit) {
       computeForMember(node, createMemberId(node));
@@ -174,6 +192,8 @@
       computeForMember(node, createMemberId(node));
     } else if (node.parent!.parent is FieldDeclaration) {
       computeForMember(node, createMemberId(node));
+    } else {
+      computeForVariableDeclaration(node, computeDefaultNodeId(node));
     }
     super.visitVariableDeclaration(node);
   }
@@ -184,6 +204,12 @@
       offset = node.question.offset;
     } else if (node is BinaryExpression) {
       offset = node.operator.offset;
+    } else if (node is InstanceCreationExpression) {
+      offset = node.argumentList.leftParenthesis.offset;
+    } else if (node is InvocationExpression) {
+      offset = node.argumentList.leftParenthesis.offset;
+    } else if (node is PrefixedIdentifier) {
+      offset = node.identifier.offset;
     } else {
       offset = node.offset;
     }
diff --git a/pkg/analyzer/pubspec.yaml b/pkg/analyzer/pubspec.yaml
index 10eb85c..ae88729 100644
--- a/pkg/analyzer/pubspec.yaml
+++ b/pkg/analyzer/pubspec.yaml
@@ -1,10 +1,10 @@
 name: analyzer
-version: 1.4.0
+version: 1.5.0-dev
 description: This package provides a library that performs static analysis of Dart code.
 homepage: https://github.com/dart-lang/sdk/tree/master/pkg/analyzer
 
 environment:
-  sdk: '>=2.12.0-0 <3.0.0'
+  sdk: '>=2.12.0 <3.0.0'
 
 dependencies:
   _fe_analyzer_shared: ^20.0.0
@@ -29,5 +29,4 @@
   linter: any
   matcher: ^0.12.10
   test: ^1.16.0
-  test_api: ^0.2.19
   test_reflective_loader: ^0.2.0
diff --git a/pkg/analyzer/test/id_tests/inferred_type_arguments_test.dart b/pkg/analyzer/test/id_tests/inferred_type_arguments_test.dart
new file mode 100644
index 0000000..224d689
--- /dev/null
+++ b/pkg/analyzer/test/id_tests/inferred_type_arguments_test.dart
@@ -0,0 +1,110 @@
+// 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 'dart:io';
+
+import 'package:_fe_analyzer_shared/src/testing/id.dart' show ActualData, Id;
+import 'package:_fe_analyzer_shared/src/testing/id_testing.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/type.dart';
+import 'package:analyzer/src/dart/analysis/testing_data.dart';
+import 'package:analyzer/src/util/ast_data_extractor.dart';
+
+import '../util/id_testing_helper.dart';
+
+main(List<String> args) async {
+  Directory dataDir = Directory.fromUri(
+      Platform.script.resolve('../../../_fe_analyzer_shared/test/inference/'
+          'inferred_type_arguments/data'));
+  return runTests<List<DartType>>(dataDir,
+      args: args,
+      createUriForFileName: createUriForFileName,
+      onFailure: onFailure,
+      runTest: runTestFor(
+          const _InferredTypeArgumentsDataComputer(), [analyzerNnbdConfig]));
+}
+
+class _InferredTypeArgumentsDataComputer extends DataComputer<List<DartType>> {
+  const _InferredTypeArgumentsDataComputer();
+
+  @override
+  DataInterpreter<List<DartType>> get dataValidator =>
+      const _InferredTypeArgumentsDataInterpreter();
+
+  @override
+  bool get supportsErrors => true;
+
+  @override
+  void computeUnitData(TestingData testingData, CompilationUnit unit,
+      Map<Id, ActualData<List<DartType>>> actualMap) {
+    _InferredTypeArgumentsDataExtractor(
+            unit.declaredElement!.source.uri, actualMap)
+        .run(unit);
+  }
+}
+
+class _InferredTypeArgumentsDataExtractor
+    extends AstDataExtractor<List<DartType>> {
+  _InferredTypeArgumentsDataExtractor(
+      Uri uri, Map<Id, ActualData<List<DartType>>> actualMap)
+      : super(uri, actualMap);
+
+  @override
+  List<DartType>? computeNodeValue(Id id, AstNode node) {
+    TypeArgumentList? typeArguments;
+    List<DartType> typeArgumentTypes;
+    if (node is InstanceCreationExpression) {
+      typeArguments = node.constructorName.type.typeArguments;
+      typeArgumentTypes =
+          (node.constructorName.type.type as InterfaceType).typeArguments;
+    } else if (node is InvocationExpression) {
+      typeArguments = node.typeArguments;
+      typeArgumentTypes = node.typeArgumentTypes!;
+    } else if (node is TypedLiteral) {
+      typeArguments = node.typeArguments;
+      typeArgumentTypes = (node.staticType as InterfaceType).typeArguments;
+    } else {
+      return null;
+    }
+    if (typeArguments == null && typeArgumentTypes.isNotEmpty) {
+      return typeArgumentTypes;
+    }
+    return null;
+  }
+}
+
+class _InferredTypeArgumentsDataInterpreter
+    implements DataInterpreter<List<DartType>> {
+  const _InferredTypeArgumentsDataInterpreter();
+
+  @override
+  String getText(List<DartType> actualData, [String? indentation]) {
+    StringBuffer sb = StringBuffer();
+    if (actualData.isNotEmpty) {
+      sb.write('<');
+      for (int i = 0; i < actualData.length; i++) {
+        if (i > 0) {
+          sb.write(',');
+        }
+        sb.write(actualData[i].getDisplayString(withNullability: true));
+      }
+      sb.write('>');
+    }
+    return sb.toString();
+  }
+
+  @override
+  String? isAsExpected(List<DartType> actualData, String? expectedData) {
+    var actualDataText = getText(actualData);
+    if (actualDataText == expectedData) {
+      return null;
+    } else {
+      return 'Expected $expectedData, got $actualDataText';
+    }
+  }
+
+  @override
+  bool isEmpty(List<DartType>? actualData) =>
+      actualData == null || actualData.isEmpty;
+}
diff --git a/pkg/analyzer/test/id_tests/inferred_variable_types_test.dart b/pkg/analyzer/test/id_tests/inferred_variable_types_test.dart
new file mode 100644
index 0000000..7f312ca
--- /dev/null
+++ b/pkg/analyzer/test/id_tests/inferred_variable_types_test.dart
@@ -0,0 +1,101 @@
+// 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 'dart:io';
+
+import 'package:_fe_analyzer_shared/src/testing/id.dart' show ActualData, Id;
+import 'package:_fe_analyzer_shared/src/testing/id_testing.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/type.dart';
+import 'package:analyzer/src/dart/analysis/testing_data.dart';
+import 'package:analyzer/src/util/ast_data_extractor.dart';
+
+import '../util/id_testing_helper.dart';
+
+main(List<String> args) async {
+  Directory dataDir = Directory.fromUri(
+      Platform.script.resolve('../../../_fe_analyzer_shared/test/inference/'
+          'inferred_variable_types/data'));
+  return runTests<DartType>(dataDir,
+      args: args,
+      createUriForFileName: createUriForFileName,
+      onFailure: onFailure,
+      runTest: runTestFor(
+          const _InferredVariableTypesDataComputer(), [analyzerNnbdConfig]));
+}
+
+class _InferredVariableTypesDataComputer extends DataComputer<DartType> {
+  const _InferredVariableTypesDataComputer();
+
+  @override
+  DataInterpreter<DartType> get dataValidator =>
+      const _InferredVariableTypesDataInterpreter();
+
+  @override
+  bool get supportsErrors => true;
+
+  @override
+  void computeUnitData(TestingData testingData, CompilationUnit unit,
+      Map<Id, ActualData<DartType>> actualMap) {
+    _InferredVariableTypesDataExtractor(
+            unit.declaredElement!.source.uri, actualMap)
+        .run(unit);
+  }
+}
+
+class _InferredVariableTypesDataExtractor extends AstDataExtractor<DartType> {
+  _InferredVariableTypesDataExtractor(
+      Uri uri, Map<Id, ActualData<DartType>> actualMap)
+      : super(uri, actualMap);
+
+  @override
+  DartType? computeNodeValue(Id id, AstNode node) {
+    if (node is VariableDeclaration) {
+      var element = node.declaredElement!;
+      if (element.hasImplicitType) {
+        return element.type;
+      }
+    } else if (node is FormalParameter) {
+      var element = node.declaredElement!;
+      if (element.hasImplicitType) {
+        return element.type;
+      }
+    } else if (node is FunctionDeclarationStatement) {
+      var element = node.functionDeclaration.declaredElement!;
+      if (element.hasImplicitReturnType) {
+        return element.returnType;
+      }
+    } else if (node is FunctionExpression &&
+        node.parent is! FunctionDeclaration) {
+      var element = node.declaredElement!;
+      if (element.hasImplicitReturnType) {
+        return element.returnType;
+      }
+    }
+    return null;
+  }
+}
+
+class _InferredVariableTypesDataInterpreter
+    implements DataInterpreter<DartType> {
+  const _InferredVariableTypesDataInterpreter();
+
+  @override
+  String getText(DartType actualData, [String? indentation]) {
+    return actualData.getDisplayString(withNullability: true);
+  }
+
+  @override
+  String? isAsExpected(DartType actualData, String? expectedData) {
+    var actualDataText = getText(actualData);
+    if (actualDataText == expectedData) {
+      return null;
+    } else {
+      return 'Expected $expectedData, got $actualDataText';
+    }
+  }
+
+  @override
+  bool isEmpty(DartType? actualData) => actualData == null;
+}
diff --git a/pkg/analyzer/test/src/dart/analysis/driver_test.dart b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
index 9ddda1e..fd84578 100644
--- a/pkg/analyzer/test/src/dart/analysis/driver_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
@@ -194,7 +194,7 @@
     driver1.priorityFiles = [a];
     driver2.priorityFiles = [a];
 
-    ResolvedUnitResult result = await driver2.getResult(b);
+    var result = await driver2.getResult2(b) as ResolvedUnitResult;
     expect(result.path, b);
 
     await scheduler.status.firstWhere((status) => status.isIdle);
@@ -443,9 +443,9 @@
 part of 'lib.dart';
 ''');
 
-    ResolvedUnitResult libResult = await driver.getResult(lib);
-    ResolvedUnitResult partResult1 = await driver.getResult(part1);
-    ResolvedUnitResult partResult2 = await driver.getResult(part2);
+    ResolvedUnitResult libResult = await driver.getResultValid(lib);
+    ResolvedUnitResult partResult1 = await driver.getResultValid(part1);
+    ResolvedUnitResult partResult2 = await driver.getResultValid(part2);
 
     CompilationUnit libUnit = libResult.unit!;
     CompilationUnit partUnit1 = partResult1.unit!;
@@ -490,7 +490,7 @@
 
     driver.addFile(lib);
 
-    ResolvedUnitResult libResult = await driver.getResult(lib);
+    ResolvedUnitResult libResult = await driver.getResultValid(lib);
     List<AnalysisError> errors = libResult.errors;
     expect(errors, hasLength(1));
     expect(errors[0].errorCode, CompileTimeErrorCode.PART_OF_UNNAMED_LIBRARY);
@@ -509,7 +509,7 @@
 
     driver.addFile(lib);
 
-    ResolvedUnitResult libResult = await driver.getResult(lib);
+    ResolvedUnitResult libResult = await driver.getResultValid(lib);
     List<AnalysisError> errors = libResult.errors;
     expect(errors, hasLength(1));
     expect(errors[0].errorCode, CompileTimeErrorCode.PART_OF_DIFFERENT_LIBRARY);
@@ -528,7 +528,7 @@
 
     driver.addFile(lib);
 
-    ResolvedUnitResult libResult = await driver.getResult(lib);
+    ResolvedUnitResult libResult = await driver.getResultValid(lib);
     List<AnalysisError> errors = libResult.errors;
     expect(errors, hasLength(1));
     expect(errors[0].errorCode, CompileTimeErrorCode.PART_OF_DIFFERENT_LIBRARY);
@@ -547,7 +547,7 @@
 
     driver.addFile(lib);
 
-    ResolvedUnitResult libResult = await driver.getResult(lib);
+    ResolvedUnitResult libResult = await driver.getResultValid(lib);
     List<AnalysisError> errors = libResult.errors;
     expect(errors, hasLength(1));
     expect(errors[0].errorCode, CompileTimeErrorCode.PART_OF_NON_PART);
@@ -559,7 +559,7 @@
 
     driver.priorityFiles = [a];
 
-    ResolvedUnitResult result1 = await driver.getResult(a);
+    ResolvedUnitResult result1 = await driver.getResultValid(a);
     expect(driver.test.priorityResults, containsPair(a, result1));
 
     await waitForIdleWithoutExceptions();
@@ -567,15 +567,14 @@
 
     // Get the (cached) result, not reported to the stream.
     {
-      ResolvedUnitResult result2 = await driver.getResult(a);
+      ResolvedUnitResult result2 = await driver.getResultValid(a);
       expect(result2, same(result1));
       expect(allResults, isEmpty);
     }
 
     // Get the (cached) result, reported to the stream.
     {
-      ResolvedUnitResult result2 =
-          await driver.getResult(a, sendCachedToStream: true);
+      var result2 = await driver.getResult2(a, sendCachedToStream: true);
       expect(result2, same(result1));
 
       expect(allResults, hasLength(1));
@@ -591,21 +590,21 @@
 
     driver.priorityFiles = [a];
 
-    ResolvedUnitResult result1 = await driver.getResult(a);
+    ResolvedUnitResult result1 = await driver.getResultValid(a);
     expect(driver.test.priorityResults, containsPair(a, result1));
 
     // Change a file.
     // The cache is flushed.
     driver.changeFile(a);
     expect(driver.test.priorityResults, isEmpty);
-    ResolvedUnitResult result2 = await driver.getResult(a);
+    ResolvedUnitResult result2 = await driver.getResultValid(a);
     expect(driver.test.priorityResults, containsPair(a, result2));
 
     // Add a file.
     // The cache is flushed.
     driver.addFile(b);
     expect(driver.test.priorityResults, isEmpty);
-    ResolvedUnitResult result3 = await driver.getResult(a);
+    ResolvedUnitResult result3 = await driver.getResultValid(a);
     expect(driver.test.priorityResults, containsPair(a, result3));
 
     // Remove a file.
@@ -622,7 +621,7 @@
 
     driver.priorityFiles = [a];
 
-    ResolvedUnitResult result1 = await driver.getResult(a);
+    ResolvedUnitResult result1 = await driver.getResultValid(a);
     expect(driver.test.priorityResults, hasLength(1));
     expect(driver.test.priorityResults, containsPair(a, result1));
 
@@ -633,7 +632,7 @@
     expect(driver.test.priorityResults, containsPair(a, result1));
 
     // Get the result for "b".
-    ResolvedUnitResult result2 = await driver.getResult(b);
+    ResolvedUnitResult result2 = await driver.getResultValid(b);
     expect(driver.test.priorityResults, hasLength(2));
     expect(driver.test.priorityResults, containsPair(a, result1));
     expect(driver.test.priorityResults, containsPair(b, result2));
@@ -649,11 +648,11 @@
     var a = convertPath('/test/bin/a.dart');
     newFile(a, content: 'var a = 1;');
 
-    ResolvedUnitResult result1 = await driver.getResult(a);
+    ResolvedUnitResult result1 = await driver.getResultValid(a);
     expect(driver.test.priorityResults, isEmpty);
 
     // The file is not priority, so its result is not cached.
-    ResolvedUnitResult result2 = await driver.getResult(a);
+    ResolvedUnitResult result2 = await driver.getResultValid(a);
     expect(result2, isNot(same(result1)));
   }
 
@@ -823,7 +822,7 @@
 @A(5)
 class C {}
 ''');
-    var result = await driver.getResult(testFile);
+    var result = await driver.getResultValid(testFile);
     var atD = AstFinder.getClass(result.unit!, 'C').metadata[0];
     var atDI = atD.elementAnnotation as ElementAnnotationImpl;
     var value = atDI.evaluationResult!.value;
@@ -840,7 +839,7 @@
   final value;
 }
 ''');
-    var result = await driver.getResult(testFile);
+    var result = await driver.getResultValid(testFile);
     var atD = AstFinder.getClass(result.unit!, 'C').metadata[0];
     var atDI = atD.elementAnnotation as ElementAnnotationImpl;
     var value = atDI.evaluationResult!.value!;
@@ -856,7 +855,7 @@
 const x = 1;
 @x class C {}
 ''');
-    var result = await driver.getResult(testFile);
+    var result = await driver.getResultValid(testFile);
     Annotation at_x = AstFinder.getClass(result.unit!, 'C').metadata[0];
     expect(at_x.elementAnnotation!.computeConstantValue()!.toIntValue(), 1);
   }
@@ -866,7 +865,7 @@
 const x = y + 1;
 const y = x + 1;
 ''');
-    var result = await driver.getResult(testFile);
+    var result = await driver.getResultValid(testFile);
     var x = AstFinder.getTopLevelVariableElement(result.unit!, 'x')
         as TopLevelVariableElementImpl;
     _expectCircularityError(x.evaluationResult!);
@@ -877,7 +876,7 @@
 const x = y + 1;
 const y = 1;
 ''');
-    var result = await driver.getResult(testFile);
+    var result = await driver.getResultValid(testFile);
     var x = AstFinder.getTopLevelVariableElement(result.unit!, 'x');
     var y = AstFinder.getTopLevelVariableElement(result.unit!, 'y');
     expect(x.computeConstantValue()!.toIntValue(), 2);
@@ -894,7 +893,7 @@
 
 class B {}
 ''');
-    var result = await driver.getResult(testFile);
+    var result = await driver.getResultValid(testFile);
     var x = AstFinder.getTopLevelVariableElement(result.unit!, 'x');
     expect(x.computeConstantValue(), isNotNull);
   }
@@ -919,7 +918,7 @@
 const c = C.WARNING;
 const d = D.WARNING;
 ''');
-    ResolvedUnitResult result = await driver.getResult(b);
+    ResolvedUnitResult result = await driver.getResultValid(b);
     expect(result.errors, isEmpty);
   }
 
@@ -948,7 +947,7 @@
   const C();
 }
 ''');
-    ResolvedUnitResult result = await driver.getResult(b);
+    ResolvedUnitResult result = await driver.getResultValid(b);
     expect(result.errors, isEmpty);
   }
 
@@ -960,7 +959,7 @@
 }
 const x = const Derived();
 ''');
-    var result = await driver.getResult(testFile);
+    var result = await driver.getResultValid(testFile);
     var x = AstFinder.getTopLevelVariableElement(result.unit!, 'x');
     expect(x.computeConstantValue(), isNotNull);
   }
@@ -969,7 +968,7 @@
     addTestFile('''
 const x = 1;
 ''');
-    var result = await driver.getResult(testFile);
+    var result = await driver.getResultValid(testFile);
     var x = AstFinder.getTopLevelVariableElement(result.unit!, 'x');
     expect(x.computeConstantValue()!.toIntValue(), 1);
   }
@@ -978,14 +977,14 @@
     var a = convertPath('/a.dart');
 
     newFile(a, content: 'var V = 1;');
-    await driver.getResult(a);
+    await driver.getResultValid(a);
 
     var session1 = driver.currentSession;
     expect(session1, isNotNull);
 
     modifyFile(a, 'var V = 2;');
     driver.changeFile(a);
-    await driver.getResult(a);
+    await driver.getResultValid(a);
 
     var session2 = driver.currentSession;
     expect(session2, isNotNull);
@@ -1046,7 +1045,7 @@
 export 'foo.dart';
 ''');
 
-    ResolvedUnitResult result = await driver.getResult(testFile);
+    ResolvedUnitResult result = await driver.getResultValid(testFile);
     List<AnalysisError> errors = result.errors;
     expect(errors, hasLength(1));
     expect(errors[0].errorCode, CompileTimeErrorCode.URI_DOES_NOT_EXIST);
@@ -1057,7 +1056,7 @@
 import 'foo.dart';
 ''');
 
-    ResolvedUnitResult result = await driver.getResult(testFile);
+    ResolvedUnitResult result = await driver.getResultValid(testFile);
     List<AnalysisError> errors = result.errors;
     expect(errors, hasLength(1));
     expect(errors[0].errorCode, CompileTimeErrorCode.URI_DOES_NOT_EXIST);
@@ -1071,7 +1070,7 @@
 }
 ''', priority: true);
 
-    ResolvedUnitResult result = await driver.getResult(testFile);
+    ResolvedUnitResult result = await driver.getResultValid(testFile);
     List<AnalysisError> errors = result.errors;
     expect(errors, hasLength(1));
     expect(errors[0].errorCode, CompileTimeErrorCode.URI_DOES_NOT_EXIST);
@@ -1083,12 +1082,13 @@
 part 'foo.dart';
 ''');
 
-    ResolvedUnitResult result = await driver.getResult(testFile);
+    ResolvedUnitResult result = await driver.getResultValid(testFile);
     List<AnalysisError> errors = result.errors;
     expect(errors, hasLength(1));
     expect(errors[0].errorCode, CompileTimeErrorCode.URI_DOES_NOT_EXIST);
   }
 
+  @deprecated
   test_generatedFile() async {
     Uri uri = Uri.parse('package:aaa/foo.dart');
     String templatePath = convertPath('/aaa/lib/foo.dart');
@@ -1127,11 +1127,82 @@
     expect(allExceptions, isEmpty);
     expect(allResults, isEmpty);
 
-    var elementResult = await driver.getUnitElement(templatePath);
-    expect(elementResult.state, ResultState.NOT_FILE_OF_URI);
+    {
+      var elementResult = await driver.getUnitElement(templatePath);
+      expect(elementResult.state, ResultState.NOT_FILE_OF_URI);
+      expect(allExceptions, isEmpty);
+      expect(allResults, isEmpty);
+    }
+
+    {
+      var elementResult = await driver.getUnitElement2(templatePath);
+      expect(elementResult, isA<NotPathOfUriResult>());
+      expect(allExceptions, isEmpty);
+      expect(allResults, isEmpty);
+    }
+
+    driver.priorityFiles = [templatePath];
+    driver.changeFile(templatePath);
+    await waitForIdleWithoutExceptions();
     expect(allExceptions, isEmpty);
     expect(allResults, isEmpty);
 
+    expect(driver.knownFiles, isNot(contains(templatePath)));
+  }
+
+  test_generatedFile2() async {
+    Uri uri = Uri.parse('package:aaa/foo.dart');
+    String templatePath = convertPath('/aaa/lib/foo.dart');
+    String generatedPath = convertPath('/generated/aaa/lib/foo.dart');
+
+    newFile(templatePath, content: r'''
+a() {}
+b() {}
+''');
+
+    newFile(generatedPath, content: r'''
+aaa() {}
+bbb() {}
+''');
+
+    Source generatedSource = _SourceMock(generatedPath, uri);
+
+    generatedUriResolver.resolveAbsoluteFunction = (uri) => generatedSource;
+    generatedUriResolver.restoreAbsoluteFunction = (Source source) {
+      String path = source.fullName;
+      if (path == templatePath || path == generatedPath) {
+        return uri;
+      } else {
+        return null;
+      }
+    };
+
+    driver.addFile(templatePath);
+
+    await waitForIdleWithoutExceptions();
+    expect(allExceptions, isEmpty);
+    expect(allResults, isEmpty);
+
+    var result = await driver.getResult2(templatePath);
+    expect(result, isA<NotPathOfUriResult>());
+    expect(allExceptions, isEmpty);
+    expect(allResults, isEmpty);
+
+    {
+      // ignore: deprecated_member_use_from_same_package
+      var elementResult = await driver.getUnitElement(templatePath);
+      expect(elementResult.state, ResultState.NOT_FILE_OF_URI);
+      expect(allExceptions, isEmpty);
+      expect(allResults, isEmpty);
+    }
+
+    {
+      var elementResult = await driver.getUnitElement2(templatePath);
+      expect(elementResult, isA<NotPathOfUriResult>());
+      expect(allExceptions, isEmpty);
+      expect(allResults, isEmpty);
+    }
+
     driver.priorityFiles = [templatePath];
     driver.changeFile(templatePath);
     await waitForIdleWithoutExceptions();
@@ -1148,7 +1219,7 @@
     expect(driver.getCachedResult(a), isNull);
 
     driver.priorityFiles = [a];
-    ResolvedUnitResult result = await driver.getResult(a);
+    ResolvedUnitResult result = await driver.getResultValid(a);
 
     expect(driver.getCachedResult(a), same(result));
   }
@@ -1285,7 +1356,7 @@
 
     // Ensure that [a.dart] library cycle is loaded.
     // So, `a.dart` is in the library context.
-    await driver.getResult(a);
+    await driver.getResultValid(a);
 
     // Update the file, changing its API signature.
     // Note that we don't call `changeFile`.
@@ -1299,14 +1370,14 @@
     expect(driver.getFileSync(a).lineInfo.lineCount, 1);
 
     // We have not read `a.dart`, so `A` is still not declared.
-    expect((await driver.getResult(b)).errors, isNotEmpty);
+    expect((await driver.getResultValid(b)).errors, isNotEmpty);
 
     // Notify the driver that the file was changed.
     driver.changeFile(a);
 
     // So, `class A {}` is declared now.
     expect(driver.getFileSync(a).lineInfo.lineCount, 2);
-    expect((await driver.getResult(b)).errors, isEmpty);
+    expect((await driver.getResultValid(b)).errors, isEmpty);
   }
 
   test_getFileSync_library() async {
@@ -1395,7 +1466,7 @@
     String content = 'int f() => 42;';
     addTestFile(content, priority: true);
 
-    ResolvedUnitResult result = await driver.getResult(testFile);
+    ResolvedUnitResult result = await driver.getResultValid(testFile);
     expect(result.path, testFile);
     expect(result.uri.toString(), 'package:test/test.dart');
     expect(result.state, ResultState.VALID);
@@ -1412,6 +1483,11 @@
     expect(allResults, [result]);
   }
 
+  test_getResult2_notAbsolutePath() async {
+    var result = await driver.getResult2('not_absolute.dart');
+    expect(result, isA<InvalidPathResult>());
+  }
+
   test_getResult_constants_defaultParameterValue_localFunction() async {
     var a = convertPath('/test/bin/a.dart');
     var b = convertPath('/test/bin/b.dart');
@@ -1427,14 +1503,14 @@
     driver.addFile(b);
     await waitForIdleWithoutExceptions();
 
-    ResolvedUnitResult result = await driver.getResult(b);
+    ResolvedUnitResult result = await driver.getResultValid(b);
     expect(result.errors, isEmpty);
   }
 
   test_getResult_doesNotExist() async {
     var a = convertPath('/test/lib/a.dart');
 
-    ResolvedUnitResult result = await driver.getResult(a);
+    ResolvedUnitResult result = await driver.getResultValid(a);
     expect(result.path, a);
     expect(result.uri.toString(), 'package:test/a.dart');
     expect(result.state, ResultState.NOT_A_FILE);
@@ -1445,7 +1521,7 @@
     String content = 'main() { int vv; }';
     addTestFile(content, priority: true);
 
-    ResolvedUnitResult result = await driver.getResult(testFile);
+    ResolvedUnitResult result = await driver.getResultValid(testFile);
     expect(result.path, testFile);
     expect(result.errors, hasLength(1));
     {
@@ -1468,7 +1544,7 @@
 class B {}
 ''');
 
-    ResolvedUnitResult result = await driver.getResult(testFile);
+    ResolvedUnitResult result = await driver.getResultValid(testFile);
     expect(result.path, testFile);
   }
 
@@ -1480,7 +1556,7 @@
 ''';
     addTestFile(content, priority: true);
 
-    var result = await driver.getResult(testFile);
+    var result = await driver.getResultValid(testFile);
     expect(result.errors, isEmpty);
   }
 
@@ -1499,7 +1575,7 @@
 
     // No errors in b.dart
     {
-      ResolvedUnitResult result = await driver.getResult(b);
+      ResolvedUnitResult result = await driver.getResultValid(b);
       expect(result.errors, isEmpty);
     }
 
@@ -1509,7 +1585,7 @@
 
     // The unresolved URI error must be reported.
     {
-      ResolvedUnitResult result = await driver.getResult(b);
+      ResolvedUnitResult result = await driver.getResultValid(b);
       expect(
           result.errors,
           contains(predicate((AnalysisError e) =>
@@ -1522,7 +1598,7 @@
 
     // No errors in b.dart again.
     {
-      ResolvedUnitResult result = await driver.getResult(b);
+      ResolvedUnitResult result = await driver.getResultValid(b);
       expect(result.errors, isEmpty);
     }
   }
@@ -1535,7 +1611,7 @@
 ''', priority: true);
     await waitForIdleWithoutExceptions();
 
-    ResolvedUnitResult result = await driver.getResult(testFile);
+    ResolvedUnitResult result = await driver.getResultValid(testFile);
     _assertClassFieldType(result.unit!, 'C', 'f', 'int');
   }
 
@@ -1550,7 +1626,7 @@
 ''', priority: true);
     await waitForIdleWithoutExceptions();
 
-    ResolvedUnitResult result = await driver.getResult(testFile);
+    ResolvedUnitResult result = await driver.getResultValid(testFile);
     _assertClassMethodReturnType(result.unit!, 'A', 'm', 'int');
     _assertClassMethodReturnType(result.unit!, 'B', 'm', 'int');
   }
@@ -1563,7 +1639,7 @@
 class C {}
 ''', priority: true);
 
-    ResolvedUnitResult result = await driver.getResult(testFile);
+    ResolvedUnitResult result = await driver.getResultValid(testFile);
     ClassDeclaration c = result.unit!.declarations[1] as ClassDeclaration;
     Annotation a = c.metadata[0];
     expect(a.name.name, 'fff');
@@ -1590,7 +1666,7 @@
 ''';
     addTestFile(content);
 
-    ResolvedUnitResult result = await driver.getResult(testFile);
+    ResolvedUnitResult result = await driver.getResultValid(testFile);
     expect(result.path, testFile);
   }
 
@@ -1602,7 +1678,7 @@
 ''';
     addTestFile(content, priority: true);
 
-    ResolvedUnitResult result = await driver.getResult(testFile);
+    ResolvedUnitResult result = await driver.getResultValid(testFile);
     expect(result.path, testFile);
     // Has only exports for valid URIs.
     List<ExportElement> imports = result.libraryElement.exports;
@@ -1619,7 +1695,7 @@
 ''';
     addTestFile(content, priority: true);
 
-    ResolvedUnitResult result = await driver.getResult(testFile);
+    ResolvedUnitResult result = await driver.getResultValid(testFile);
     expect(result.path, testFile);
     // Has only imports for valid URIs.
     List<ImportElement> imports = result.libraryElement.imports;
@@ -1640,7 +1716,7 @@
 part '';
 ''';
     addTestFile(content);
-    await driver.getResult(testFile);
+    await driver.getResultValid(testFile);
   }
 
   test_getResult_languageVersion() async {
@@ -1650,7 +1726,7 @@
 class A{}
 ''');
 
-    var result = await driver.getResult(path);
+    var result = await driver.getResultValid(path);
     var languageVersion = result.unit!.languageVersionToken!;
     expect(languageVersion.major, 2);
     expect(languageVersion.minor, 7);
@@ -1687,7 +1763,7 @@
     // package:my_pkg/c.dart's import is erroneous, causing y's reference to z
     // to be unresolved (and therefore have type dynamic).
     {
-      ResolvedUnitResult result = await driver.getResult(a);
+      ResolvedUnitResult result = await driver.getResultValid(a);
       expect(result.errors, isEmpty);
     }
 
@@ -1697,7 +1773,7 @@
     // successfully imports file:///my_pkg/test/d.dart, causing y to have an
     // inferred type of String.
     {
-      ResolvedUnitResult result = await driver.getResult(b);
+      ResolvedUnitResult result = await driver.getResultValid(b);
       List<AnalysisError> errors = result.errors;
       expect(errors, hasLength(1));
       expect(errors[0].errorCode, CompileTimeErrorCode.INVALID_ASSIGNMENT);
@@ -1711,7 +1787,7 @@
 var V;
 ''';
     addTestFile(content);
-    await driver.getResult(testFile);
+    await driver.getResultValid(testFile);
   }
 
   test_getResult_nameConflict_local_typeInference() async {
@@ -1725,9 +1801,10 @@
 }
 ''';
     addTestFile(content);
-    await driver.getResult(testFile);
+    await driver.getResultValid(testFile);
   }
 
+  @deprecated
   test_getResult_notAbsolutePath() async {
     expect(() async {
       await driver.getResult('not_absolute.dart');
@@ -1738,7 +1815,7 @@
     var path = convertPath('/test/lib/test.txt');
     newFile(path, content: 'class A {}');
 
-    ResolvedUnitResult result = await driver.getResult(path);
+    ResolvedUnitResult result = await driver.getResultValid(path);
     expect(result, isNotNull);
     expect(result.unit!.declaredElement!.types.map((e) => e.name), ['A']);
   }
@@ -1750,7 +1827,7 @@
 ''';
     addTestFile(content);
     // Should not throw exceptions.
-    await driver.getResult(testFile);
+    await driver.getResultValid(testFile);
   }
 
   test_getResult_sameFile_twoUris() async {
@@ -1772,14 +1849,14 @@
     await waitForIdleWithoutExceptions();
 
     {
-      ResolvedUnitResult result = await driver.getResult(b);
+      ResolvedUnitResult result = await driver.getResultValid(b);
       expect(_getImportSource(result.unit!, 0).uri.toString(),
           'package:test/a.dart');
       _assertTopLevelVarType(result.unit!, 'VB', 'A<int>');
     }
 
     {
-      ResolvedUnitResult result = await driver.getResult(c);
+      ResolvedUnitResult result = await driver.getResultValid(c);
       expect(
         _getImportSource(result.unit!, 0).uri,
         toUri('/test/lib/a.dart'),
@@ -1806,7 +1883,7 @@
     await waitForIdleWithoutExceptions();
 
     {
-      ResolvedUnitResult result = await driver.getResult(a);
+      ResolvedUnitResult result = await driver.getResultValid(a);
       _assertTopLevelVarType(result.unit!, 'A1', 'int');
       _assertTopLevelVarType(result.unit!, 'A2', 'int');
     }
@@ -1826,7 +1903,7 @@
     driver.changeFile(a);
 
     {
-      ResolvedUnitResult result = await driver.getResult(a);
+      ResolvedUnitResult result = await driver.getResultValid(a);
       _assertTopLevelVarType(result.unit!, 'A1', 'double');
       _assertTopLevelVarType(result.unit!, 'A2', 'double');
     }
@@ -1835,7 +1912,7 @@
   test_getResult_thenRemove() async {
     addTestFile('main() {}', priority: true);
 
-    var resultFuture = driver.getResult(testFile);
+    var resultFuture = driver.getResultValid(testFile);
     driver.removeFile(testFile);
 
     var result = await resultFuture;
@@ -1847,8 +1924,8 @@
     String content = 'main() {}';
     addTestFile(content, priority: true);
 
-    var future1 = driver.getResult(testFile);
-    var future2 = driver.getResult(testFile);
+    var future1 = driver.getResultValid(testFile);
+    var future2 = driver.getResultValid(testFile);
 
     // Both futures complete, with the same result.
     ResolvedUnitResult result1 = await future1;
@@ -1871,7 +1948,7 @@
 
     // Ensure that [a.dart] library cycle is loaded.
     // So, `a.dart` is in the library context.
-    await driver.getResult(a);
+    await driver.getResultValid(a);
 
     // Update the file, changing its API signature.
     // Note that we don't call `changeFile`.
@@ -1885,14 +1962,14 @@
     expect(await driver.getSourceKind(a), SourceKind.PART);
 
     // We have not read `a.dart`, so `A` is still not declared.
-    expect((await driver.getResult(b)).errors, isNotEmpty);
+    expect((await driver.getResultValid(b)).errors, isNotEmpty);
 
     // Notify the driver that the file was changed.
     driver.changeFile(a);
 
     // So, `class A {}` is declared now.
     expect(await driver.getSourceKind(a), SourceKind.LIBRARY);
-    expect((await driver.getResult(b)).errors, isEmpty);
+    expect((await driver.getResultValid(b)).errors, isEmpty);
   }
 
   test_getSourceKind_changeFile() async {
@@ -1937,6 +2014,7 @@
     expect(await driver.getSourceKind(path), SourceKind.PART);
   }
 
+  @deprecated
   test_getUnitElement() async {
     String content = r'''
 foo(int p) {}
@@ -1953,6 +2031,49 @@
         unorderedEquals(['foo', 'main']));
   }
 
+  test_getUnitElement2() async {
+    String content = r'''
+foo(int p) {}
+main() {
+  foo(42);
+}
+''';
+    addTestFile(content);
+
+    var unitResult = await driver.getUnitElement2(testFile);
+    unitResult as UnitElementResult;
+    CompilationUnitElement unitElement = unitResult.element;
+    expect(unitElement.source.fullName, testFile);
+    expect(unitElement.functions.map((c) => c.name),
+        unorderedEquals(['foo', 'main']));
+  }
+
+  test_getUnitElement2_doesNotExist_afterResynthesized() async {
+    var a = convertPath('/test/lib/a.dart');
+    var b = convertPath('/test/lib/b.dart');
+
+    newFile(a, content: r'''
+import 'package:test/b.dart';
+''');
+
+    await driver.getResolvedLibrary(a);
+    await driver.getUnitElement2(b);
+  }
+
+  test_getUnitElement2_notAbsolutePath() async {
+    var result = await driver.getUnitElement2('not_absolute.dart');
+    expect(result, isA<InvalidPathResult>());
+  }
+
+  test_getUnitElement2_notDart() async {
+    var path = convertPath('/test.txt');
+    newFile(path, content: 'class A {}');
+    var unitResult = await driver.getUnitElement2(path);
+    unitResult as UnitElementResult;
+    expect(unitResult.element.types.map((e) => e.name), ['A']);
+  }
+
+  @deprecated
   test_getUnitElement_doesNotExist_afterResynthesized() async {
     var a = convertPath('/test/lib/a.dart');
     var b = convertPath('/test/lib/b.dart');
@@ -1965,12 +2086,14 @@
     await driver.getUnitElement(b);
   }
 
+  @deprecated
   test_getUnitElement_notAbsolutePath() async {
     expect(() async {
       await driver.getUnitElement('not_absolute.dart');
     }, throwsArgumentError);
   }
 
+  @deprecated
   test_getUnitElement_notDart() async {
     var path = convertPath('/test.txt');
     newFile(path, content: 'class A {}');
@@ -1978,6 +2101,7 @@
     expect(unitResult.element.types.map((e) => e.name), ['A']);
   }
 
+  @deprecated
   test_getUnitElementSignature() async {
     var a = convertPath('/test/lib/a.dart');
 
@@ -1998,6 +2122,27 @@
     expect(signature2, isNot(signature));
   }
 
+  test_getUnitElementSignature2() async {
+    var a = convertPath('/test/lib/a.dart');
+
+    newFile(a, content: 'foo() {}');
+
+    String signature = await driver.getUnitElementSignature(a);
+    expect(signature, isNotNull);
+
+    var unitResult = await driver.getUnitElement2(a);
+    unitResult as UnitElementResult;
+    expect(unitResult.path, a);
+    expect(unitResult.signature, signature);
+
+    modifyFile(a, 'bar() {}');
+    driver.changeFile(a);
+
+    String signature2 = await driver.getUnitElementSignature(a);
+    expect(signature2, isNotNull);
+    expect(signature2, isNot(signature));
+  }
+
   test_hasFilesToAnalyze() async {
     // No files yet, nothing to analyze.
     expect(driver.hasFilesToAnalyze, isFalse);
@@ -2011,7 +2156,7 @@
     expect(driver.hasFilesToAnalyze, isFalse);
 
     // Ask to analyze the file, so there is a file to analyze.
-    var future = driver.getResult(testFile);
+    var future = driver.getResultValid(testFile);
     expect(driver.hasFilesToAnalyze, isTrue);
 
     // Once analysis is done, there is nothing to analyze.
@@ -2048,7 +2193,7 @@
     driver.addFile(a);
     driver.addFile(b);
 
-    await driver.getResult(b);
+    await driver.getResultValid(b);
 
     // Modify the library, but don't notify the driver.
     // The driver should use the previous library content and elements.
@@ -2060,7 +2205,7 @@
 }
 ''');
 
-    var result = await driver.getResult(b);
+    var result = await driver.getResultValid(b);
     var c = _getTopLevelVar(result.unit!, 'c');
     var typeC = c.declaredElement!.type as InterfaceType;
     // The class C has an old field 'foo', not the new 'bar'.
@@ -2082,7 +2227,7 @@
     driver.addFile(a);
     driver.addFile(b);
 
-    ResolvedUnitResult result = await driver.getResult(a);
+    ResolvedUnitResult result = await driver.getResultValid(a);
     expect(result.errors, isEmpty);
     _assertTopLevelVarType(result.unit!, 'b', 'B');
   }
@@ -2107,10 +2252,10 @@
 ''');
 
     // This ensures that `a.dart` linked library is cached.
-    await driver.getResult(a);
+    await driver.getResultValid(a);
 
     // Should not fail because of considering `b.dart` part as `a.dart` library.
-    await driver.getResult(c);
+    await driver.getResultValid(c);
   }
 
   test_instantiateToBounds_invalid() async {
@@ -2245,7 +2390,7 @@
 
     // Ensure that [a.dart] library cycle is loaded.
     // So, `a.dart` is in the library context.
-    await driver.getResult(a);
+    await driver.getResultValid(a);
 
     // Update the file, changing its API signature.
     // Note that we don't call `changeFile`.
@@ -2263,7 +2408,7 @@
 
     // We have not read `a.dart`, so `A` is still not declared.
     {
-      var bResult = await driver.getResult(b);
+      var bResult = await driver.getResultValid(b);
       expect(bResult.errors, isNotEmpty);
     }
 
@@ -2276,7 +2421,7 @@
       expect(parseResult.unit.declarations, hasLength(1));
     }
     {
-      var bResult = await driver.getResult(b);
+      var bResult = await driver.getResultValid(b);
       expect(bResult.errors, isEmpty);
     }
   }
@@ -2309,7 +2454,7 @@
 
     // Ensure that [a.dart] library cycle is loaded.
     // So, `a.dart` is in the library context.
-    await driver.getResult(a);
+    await driver.getResultValid(a);
 
     // Update the file, changing its API signature.
     // Note that we don't call `changeFile`.
@@ -2327,7 +2472,7 @@
 
     // We have not read `a.dart`, so `A` is still not declared.
     {
-      var bResult = await driver.getResult(b);
+      var bResult = await driver.getResultValid(b);
       expect(bResult.errors, isNotEmpty);
     }
 
@@ -2340,7 +2485,7 @@
       expect(parseResult.unit.declarations, hasLength(1));
     }
     {
-      var bResult = await driver.getResult(b);
+      var bResult = await driver.getResultValid(b);
       expect(bResult.errors, isEmpty);
     }
   }
@@ -2519,14 +2664,14 @@
 
     // Process a.dart so that we know that it's a library for c.dart later.
     {
-      ResolvedUnitResult result = await driver.getResult(a);
+      ResolvedUnitResult result = await driver.getResultValid(a);
       expect(result.errors, isEmpty);
       _assertTopLevelVarType(result.unit!, 'c', 'C');
     }
 
     // Now c.dart can be resolved without errors in the context of a.dart
     {
-      ResolvedUnitResult result = await driver.getResult(c);
+      ResolvedUnitResult result = await driver.getResultValid(c);
       expect(result.errors, isEmpty);
       _assertTopLevelVarType(result.unit!, 'a', 'A');
       _assertTopLevelVarType(result.unit!, 'b', 'B');
@@ -2558,7 +2703,7 @@
 
     // b.dart will be analyzed after a.dart is analyzed.
     // So, A and B references are resolved.
-    ResolvedUnitResult result = await driver.getResult(c);
+    ResolvedUnitResult result = await driver.getResultValid(c);
     expect(result.errors, isEmpty);
     _assertTopLevelVarType(result.unit!, 'a', 'A');
     _assertTopLevelVarType(result.unit!, 'b', 'B');
@@ -2574,7 +2719,7 @@
     driver.addFile(a);
 
     // Analyze the library without the part.
-    await driver.getResult(a);
+    await driver.getResultValid(a);
 
     // Create the part file.
     // This should invalidate library file state (specifically the library
@@ -2586,7 +2731,7 @@
     driver.changeFile(b);
 
     // This should not crash.
-    var result = await driver.getResult(b);
+    var result = await driver.getResultValid(b);
     expect(result.errors, isEmpty);
   }
 
@@ -2603,11 +2748,114 @@
 
     // There is no library which c.dart is a part of, so it has unresolved
     // A and B references.
-    ResolvedUnitResult result = await driver.getResult(c);
+    ResolvedUnitResult result = await driver.getResultValid(c);
     expect(result.errors, isNotEmpty);
     expect(result.unit!, isNotNull);
   }
 
+  test_part_getUnitElement2_afterLibrary() async {
+    var a = convertPath('/test/lib/a.dart');
+    var b = convertPath('/test/lib/b.dart');
+    var c = convertPath('/test/lib/c.dart');
+    newFile(a, content: r'''
+library a;
+import 'b.dart';
+part 'c.dart';
+class A {}
+var c = new C();
+''');
+    newFile(b, content: 'class B {}');
+    newFile(c, content: r'''
+part of a;
+class C {}
+var a = new A();
+var b = new B();
+''');
+
+    driver.addFile(a);
+    driver.addFile(b);
+    driver.addFile(c);
+
+    // Process a.dart so that we know that it's a library for c.dart later.
+    await driver.getResultValid(a);
+
+    // c.dart is resolve in the context of a.dart, knows 'A' and 'B'.
+    {
+      var result = await driver.getUnitElement2(c);
+      result as UnitElementResult;
+      var partUnit = result.element;
+
+      assertType(partUnit.topLevelVariables[0].type, 'A');
+      assertType(partUnit.topLevelVariables[1].type, 'B');
+
+      var libraryUnit = partUnit.library.definingCompilationUnit;
+      assertType(libraryUnit.topLevelVariables[0].type, 'C');
+    }
+  }
+
+  test_part_getUnitElement2_beforeLibrary() async {
+    var a = convertPath('/test/lib/a.dart');
+    var b = convertPath('/test/lib/b.dart');
+    var c = convertPath('/test/lib/c.dart');
+    newFile(a, content: r'''
+library a;
+import 'b.dart';
+part 'c.dart';
+class A {}
+var c = new C();
+''');
+    newFile(b, content: 'class B {}');
+    newFile(c, content: r'''
+part of a;
+class C {}
+var a = new A();
+var b = new B();
+''');
+
+    driver.addFile(a);
+    driver.addFile(b);
+    driver.addFile(c);
+
+    // c.dart is resolve in the context of a.dart, knows 'A' and 'B'.
+    {
+      var result = await driver.getUnitElement2(c);
+      result as UnitElementResult;
+      var partUnit = result.element;
+
+      assertType(partUnit.topLevelVariables[0].type, 'A');
+      assertType(partUnit.topLevelVariables[1].type, 'B');
+
+      var libraryUnit = partUnit.library.definingCompilationUnit;
+      assertType(libraryUnit.topLevelVariables[0].type, 'C');
+    }
+  }
+
+  test_part_getUnitElement2_noLibrary() async {
+    var c = convertPath('/test/lib/c.dart');
+    newFile(c, content: r'''
+part of a;
+var a = new A();
+var b = new B();
+''');
+
+    driver.addFile(c);
+
+    // We don't know the library of c.dart, but we should get a result.
+    // The types "A" and "B" are unresolved.
+    {
+      var result = await driver.getUnitElement2(c);
+      result as UnitElementResult;
+      var partUnit = result.element;
+
+      expect(partUnit.topLevelVariables[0].name, 'a');
+      assertType(partUnit.topLevelVariables[0].type, 'dynamic');
+
+      expect(partUnit.topLevelVariables[1].name, 'b');
+      assertType(partUnit.topLevelVariables[1].type, 'dynamic');
+    }
+  }
+
+  @deprecated
   test_part_getUnitElement_afterLibrary() async {
     var a = convertPath('/test/lib/a.dart');
     var b = convertPath('/test/lib/b.dart');
@@ -2632,7 +2880,7 @@
     driver.addFile(c);
 
     // Process a.dart so that we know that it's a library for c.dart later.
-    await driver.getResult(a);
+    await driver.getResultValid(a);
 
     // c.dart is resolve in the context of a.dart, knows 'A' and 'B'.
     {
@@ -2647,6 +2895,7 @@
     }
   }
 
+  @deprecated
   test_part_getUnitElement_beforeLibrary() async {
     var a = convertPath('/test/lib/a.dart');
     var b = convertPath('/test/lib/b.dart');
@@ -2683,6 +2932,7 @@
     }
   }
 
+  @deprecated
   test_part_getUnitElement_noLibrary() async {
     var c = convertPath('/test/lib/c.dart');
     newFile(c, content: r'''
@@ -2734,7 +2984,7 @@
     String signatureBefore = await driver.getUnitElementSignature(c);
 
     // Process a.dart so that we know that it's a library for c.dart later.
-    await driver.getResult(a);
+    await driver.getResultValid(a);
 
     // The before and after signatures must be the same.
     String signatureAfter = await driver.getUnitElementSignature(c);
@@ -3356,3 +3606,9 @@
     throw StateError('Unexpected invocation of ${invocation.memberName}');
   }
 }
+
+extension on AnalysisDriver {
+  Future<ResolvedUnitResult> getResultValid(String path) async {
+    return await getResult2(path) as ResolvedUnitResult;
+  }
+}
diff --git a/pkg/analyzer/test/src/dart/analysis/search_test.dart b/pkg/analyzer/test/src/dart/analysis/search_test.dart
index 10ac334..210b9b9 100644
--- a/pkg/analyzer/test/src/dart/analysis/search_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/search_test.dart
@@ -2,6 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/ast/ast.dart' hide Declaration;
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/src/dart/analysis/driver.dart';
@@ -376,7 +377,8 @@
 ''');
     var element = findElement.unnamedConstructor('A');
 
-    CompilationUnit otherUnit = (await driver.getResult(other)).unit!;
+    var otherUnitResult = await driver.getResult2(other) as ResolvedUnitResult;
+    CompilationUnit otherUnit = otherUnitResult.unit!;
     Element main = otherUnit.declaredElement!.functions[0];
     var expected = [
       ExpectedResult(main, SearchResultKind.REFERENCE,
diff --git a/pkg/analyzer/test/src/dart/analysis/session_test.dart b/pkg/analyzer/test/src/dart/analysis/session_test.dart
index f1235ca..7812b9d 100644
--- a/pkg/analyzer/test/src/dart/analysis/session_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/session_test.dart
@@ -51,6 +51,32 @@
     expect(result.uri.toString(), 'package:dart.my/a.dart');
   }
 
+  void test_getResolvedUnit2_notFileOfUri() async {
+    var relPath = 'dart/my/lib/a.dart';
+    newFile('$workspaceRootPath/bazel-bin/$relPath');
+
+    var path = convertPath('$workspaceRootPath/$relPath');
+    var session = contextFor(path).currentSession;
+    var result = await session.getResolvedUnit2(path);
+    expect(result, isA<NotPathOfUriResult>());
+  }
+
+  void test_getResolvedUnit2_valid() async {
+    var file = newFile(
+      '$workspaceRootPath/dart/my/lib/a.dart',
+      content: 'class A {}',
+    );
+
+    var session = contextFor(file.path).currentSession;
+    var result =
+        await session.getResolvedUnit2(file.path) as ResolvedUnitResult;
+    expect(result.state, ResultState.VALID);
+    expect(result.path, file.path);
+    expect(result.errors, isEmpty);
+    expect(result.uri.toString(), 'package:dart.my/a.dart');
+  }
+
+  @deprecated
   void test_getResolvedUnit_notFileOfUri() async {
     var relPath = 'dart/my/lib/a.dart';
     newFile('$workspaceRootPath/bazel-bin/$relPath');
@@ -62,6 +88,7 @@
     expect(() => result.errors, throwsStateError);
   }
 
+  @deprecated
   void test_getResolvedUnit_valid() async {
     var file = newFile(
       '$workspaceRootPath/dart/my/lib/a.dart',
@@ -76,6 +103,32 @@
     expect(result.uri.toString(), 'package:dart.my/a.dart');
   }
 
+  void test_getUnitElement2_notPathOfUri() async {
+    var relPath = 'dart/my/lib/a.dart';
+    newFile('$workspaceRootPath/bazel-bin/$relPath');
+
+    var path = convertPath('$workspaceRootPath/$relPath');
+    var session = contextFor(path).currentSession;
+    var result = await session.getUnitElement2(path);
+    expect(result, isA<NotPathOfUriResult>());
+  }
+
+  void test_getUnitElement2_valid() async {
+    var file = newFile(
+      '$workspaceRootPath/dart/my/lib/a.dart',
+      content: 'class A {}',
+    );
+
+    var session = contextFor(file.path).currentSession;
+    var result = await session.getUnitElement2(file.path);
+    result as UnitElementResult;
+    expect(result.state, ResultState.VALID);
+    expect(result.path, file.path);
+    expect(result.element.types, hasLength(1));
+    expect(result.uri.toString(), 'package:dart.my/a.dart');
+  }
+
+  @deprecated
   void test_getUnitElement_notFileOfUri() async {
     var relPath = 'dart/my/lib/a.dart';
     newFile('$workspaceRootPath/bazel-bin/$relPath');
@@ -87,6 +140,7 @@
     expect(() => result.element, throwsStateError);
   }
 
+  @deprecated
   void test_getUnitElement_valid() async {
     var file = newFile(
       '$workspaceRootPath/dart/my/lib/a.dart',
@@ -200,6 +254,7 @@
     expect(node.length, 10);
   }
 
+  @deprecated
   test_getParsedLibrary_getElementDeclaration_notThisLibrary() async {
     newFile(testPath, content: '');
 
@@ -214,6 +269,21 @@
     }, throwsArgumentError);
   }
 
+  test_getParsedLibrary_getElementDeclaration_notThisLibrary2() async {
+    newFile(testPath, content: '');
+
+    var resolvedUnit =
+        await session.getResolvedUnit2(testPath) as ResolvedUnitResult;
+    var typeProvider = resolvedUnit.typeProvider;
+    var intClass = typeProvider.intType.element;
+
+    var parsedLibrary = session.getParsedLibrary(testPath);
+
+    expect(() {
+      parsedLibrary.getElementDeclaration(intClass);
+    }, throwsArgumentError);
+  }
+
   test_getParsedLibrary_getElementDeclaration_synthetic() async {
     newFile(testPath, content: r'''
 int foo = 0;
@@ -221,6 +291,32 @@
 
     var parsedLibrary = session.getParsedLibrary(testPath);
 
+    var unitResult = await session.getUnitElement2(testPath);
+    var unitElement = (unitResult as UnitElementResult).element;
+    var fooElement = unitElement.topLevelVariables[0];
+    expect(fooElement.name, 'foo');
+
+    // We can get the variable element declaration.
+    var fooDeclaration = parsedLibrary.getElementDeclaration(fooElement)!;
+    var fooNode = fooDeclaration.node as VariableDeclaration;
+    expect(fooNode.name.name, 'foo');
+    expect(fooNode.offset, 4);
+    expect(fooNode.length, 7);
+    expect(fooNode.name.staticElement, isNull);
+
+    // Synthetic elements don't have nodes.
+    expect(parsedLibrary.getElementDeclaration(fooElement.getter!), isNull);
+    expect(parsedLibrary.getElementDeclaration(fooElement.setter!), isNull);
+  }
+
+  @deprecated
+  test_getParsedLibrary_getElementDeclaration_synthetic_deprecated() async {
+    newFile(testPath, content: r'''
+int foo = 0;
+''');
+
+    var parsedLibrary = session.getParsedLibrary(testPath);
+
     var unitElement = (await session.getUnitElement(testPath)).element;
     var fooElement = unitElement.topLevelVariables[0];
     expect(fooElement.name, 'foo');
@@ -525,6 +621,7 @@
     }, throwsArgumentError);
   }
 
+  @deprecated
   test_getResolvedUnit() async {
     newFile(testPath, content: r'''
 class A {}
@@ -540,6 +637,22 @@
     expect(unitResult.libraryElement, isNotNull);
   }
 
+  test_getResolvedUnit2() async {
+    newFile(testPath, content: r'''
+class A {}
+class B {}
+''');
+
+    var unitResult =
+        await session.getResolvedUnit2(testPath) as ResolvedUnitResult;
+    expect(unitResult.session, session);
+    expect(unitResult.path, testPath);
+    expect(unitResult.uri, Uri.parse('package:test/test.dart'));
+    expect(unitResult.unit!.declarations, hasLength(2));
+    expect(unitResult.typeProvider, isNotNull);
+    expect(unitResult.libraryElement, isNotNull);
+  }
+
   test_getSourceKind() async {
     newFile(testPath, content: 'class C {}');
 
@@ -554,6 +667,7 @@
     expect(kind, SourceKind.PART);
   }
 
+  @deprecated
   test_getUnitElement() async {
     newFile(testPath, content: r'''
 class A {}
@@ -570,6 +684,23 @@
     expect(unitResult.signature, signature);
   }
 
+  test_getUnitElement2() async {
+    newFile(testPath, content: r'''
+class A {}
+class B {}
+''');
+
+    var unitResult = await session.getUnitElement2(testPath);
+    unitResult as UnitElementResult;
+    expect(unitResult.session, session);
+    expect(unitResult.path, testPath);
+    expect(unitResult.uri, Uri.parse('package:test/test.dart'));
+    expect(unitResult.element.types, hasLength(2));
+
+    var signature = await session.getUnitElementSignature(testPath);
+    expect(unitResult.signature, signature);
+  }
+
   test_resourceProvider() async {
     expect(session.resourceProvider, resourceProvider);
   }
diff --git a/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart b/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart
index aa6dfb3..e82b7ca 100644
--- a/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart
+++ b/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart
@@ -628,7 +628,49 @@
     _assertRemovedPaths(isEmpty);
   }
 
-  test_resolve_part_of() async {
+  test_resolve_libraryWithPart_noLibraryDiscovery() async {
+    var partPath = '/workspace/dart/test/lib/a.dart';
+    newFile(partPath, content: r'''
+part of 'test.dart';
+
+class A {}
+''');
+
+    await assertNoErrorsInCode(r'''
+part 'a.dart';
+
+void f(A a) {}
+''');
+
+    // We started resolution from the library, and then followed to the part.
+    // So, the part knows its library, there is no need to discover it.
+    _assertDiscoveredLibraryForParts([]);
+  }
+
+  test_resolve_part_of_name() async {
+    newFile('/workspace/dart/test/lib/a.dart', content: r'''
+library my.lib;
+
+part 'test.dart';
+
+class A {
+  int m;
+}
+''');
+
+    await assertNoErrorsInCode(r'''
+part of my.lib;
+
+void func() {
+  var a = A();
+  print(a.m);
+}
+''');
+
+    _assertDiscoveredLibraryForParts([result.path!]);
+  }
+
+  test_resolve_part_of_uri() async {
     newFile('/workspace/dart/test/lib/a.dart', content: r'''
 part 'test.dart';
 
@@ -645,6 +687,8 @@
   print(a.m);
 }
 ''');
+
+    _assertDiscoveredLibraryForParts([result.path!]);
   }
 
   test_resolveFile_cache() async {
@@ -790,6 +834,10 @@
     ]);
   }
 
+  void _assertDiscoveredLibraryForParts(List<String> expected) {
+    expect(fileResolver.fsState!.testView.partsDiscoveredLibraries, expected);
+  }
+
   void _assertRemovedPaths(Matcher matcher) {
     expect(fileResolver.fsState!.testView.removedPaths, matcher);
   }
diff --git a/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart b/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart
index 2b7598a..64e9563 100644
--- a/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart
+++ b/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart
@@ -189,7 +189,7 @@
   Future<ResolvedUnitResult> resolveFile(String path) async {
     var analysisContext = contextFor(pathForContextSelection ?? path);
     var session = analysisContext.currentSession;
-    return await session.getResolvedUnit(path);
+    return await session.getResolvedUnit2(path) as ResolvedUnitResult;
   }
 
   @mustCallSuper
diff --git a/pkg/analyzer/test/src/diagnostics/ambiguous_import_test.dart b/pkg/analyzer/test/src/diagnostics/ambiguous_import_test.dart
index dd7b71f..81c91fd 100644
--- a/pkg/analyzer/test/src/diagnostics/ambiguous_import_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/ambiguous_import_test.dart
@@ -5,7 +5,7 @@
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/src/error/codes.dart';
 import 'package:matcher/src/core_matchers.dart';
-import 'package:test_api/src/frontend/expect.dart';
+import 'package:test/test.dart' show expect;
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
 import '../../generated/test_support.dart';
diff --git a/pkg/analyzer/test/src/diagnostics/argument_type_not_assignable_test.dart b/pkg/analyzer/test/src/diagnostics/argument_type_not_assignable_test.dart
index 096fcdc..e177d5e 100644
--- a/pkg/analyzer/test/src/diagnostics/argument_type_not_assignable_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/argument_type_not_assignable_test.dart
@@ -398,7 +398,7 @@
 main() {
   f(p: 42);
 }''', [
-      error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 35, 5),
+      error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 38, 2),
     ]);
   }
 
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 b71deef..926a7b6 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
@@ -238,7 +238,7 @@
       error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 9, 29),
       error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 36, 1),
       error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 49, 48),
-      error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 81, 15),
+      error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 95, 1),
     ]);
   }
 
@@ -250,7 +250,7 @@
 var b = const bool.fromEnvironment('x', defaultValue: 1);
 ''', [
       error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 8, 48),
-      error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 40, 15),
+      error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 54, 1),
     ]);
   }
 
diff --git a/pkg/analyzer/test/src/diagnostics/unused_element_test.dart b/pkg/analyzer/test/src/diagnostics/unused_element_test.dart
index c3ca7ec..105d62b 100644
--- a/pkg/analyzer/test/src/diagnostics/unused_element_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/unused_element_test.dart
@@ -712,6 +712,78 @@
 ''');
   }
 
+  test_method_isUsed_privateExtension_generic_binaryOperator() async {
+    await assertNoErrorsInCode(r'''
+class A<T> {}
+extension _A<T> on A<T> {
+  int operator -(int other) => other;
+}
+void f(A<int> a) {
+  a - 3;
+}
+''');
+  }
+
+  test_method_isUsed_privateExtension_generic_indexEqOperator() async {
+    await assertNoErrorsInCode(r'''
+class A<T> {}
+extension _A<T> on A<T> {
+  void operator []=(int index, T value) {
+}}
+void f(A<int> a) {
+  a[0] = 1;
+}
+''');
+  }
+
+  test_method_isUsed_privateExtension_generic_indexOperator() async {
+    await assertNoErrorsInCode(r'''
+class A<T> {}
+extension _A<T> on A<T> {
+  A<T> operator [](int index) => throw 0;
+}
+void f(A<int> a) {
+  a[0];
+}
+''');
+  }
+
+  test_method_isUsed_privateExtension_generic_method() async {
+    await assertNoErrorsInCode(r'''
+class A<T> {}
+extension _A<T> on A<T> {
+  A<T> foo() => throw 0;
+}
+void f(A<int> a) {
+  a.foo();
+}
+''');
+  }
+
+  test_method_isUsed_privateExtension_generic_postfixOperator() async {
+    await assertNoErrorsInCode(r'''
+class A<T> {}
+extension _A<T> on A<T> {
+  A<T> operator -(int i) => throw 0;
+}
+void f(A<int> a) {
+  a--;
+}
+''');
+  }
+
+  test_method_isUsed_privateExtension_generic_prefixOperator() async {
+    await assertNoErrorsInCode(r'''
+class A<T> {}
+extension _A<T> on A<T> {
+  T operator ~() => throw 0;
+}
+void f(A<int> a) {
+  ~a;
+}
+''');
+  }
+
   test_method_isUsed_privateExtension_indexEqOperator() async {
     await assertNoErrorsInCode(r'''
 extension _A on bool {
diff --git a/pkg/analyzer/test/src/summary/top_level_inference_test.dart b/pkg/analyzer/test/src/summary/top_level_inference_test.dart
index 25b90f1..5675f3f 100644
--- a/pkg/analyzer/test/src/summary/top_level_inference_test.dart
+++ b/pkg/analyzer/test/src/summary/top_level_inference_test.dart
@@ -2,6 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/src/error/codes.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -2745,7 +2746,8 @@
 
     var path = convertPath(testFilePath);
     var analysisSession = contextFor(path).currentSession;
-    var result = await analysisSession.getUnitElement(path);
+    var result = await analysisSession.getUnitElement2(path);
+    result as UnitElementResult;
     return result.element.library;
   }
 }
diff --git a/pkg/analyzer/test/src/task/strong/checker_test.dart b/pkg/analyzer/test/src/task/strong/checker_test.dart
index d7bc492..6a5a5cc 100644
--- a/pkg/analyzer/test/src/task/strong/checker_test.dart
+++ b/pkg/analyzer/test/src/task/strong/checker_test.dart
@@ -2292,10 +2292,10 @@
       error(CompileTimeErrorCode.UNDEFINED_NAMED_PARAMETER, 254, 1),
       error(CompileTimeErrorCode.UNDEFINED_NAMED_PARAMETER, 271, 1),
       error(CompileTimeErrorCode.UNDEFINED_NAMED_PARAMETER, 327, 1),
-      error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 345, 4),
-      error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 351, 4),
+      error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 348, 1),
+      error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 354, 1),
       error(CompileTimeErrorCode.UNDEFINED_NAMED_PARAMETER, 357, 1),
-      error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 374, 4),
+      error(CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 377, 1),
     ]);
   }
 
diff --git a/pkg/analyzer/test/util/id_testing_helper.dart b/pkg/analyzer/test/util/id_testing_helper.dart
index 19c16a4..e8c4e79 100644
--- a/pkg/analyzer/test/util/id_testing_helper.dart
+++ b/pkg/analyzer/test/util/id_testing_helper.dart
@@ -173,7 +173,7 @@
   var results = <Uri, ResolvedUnitResult>{};
   for (var testUri in testUris) {
     var path = resourceProvider.convertPath(testUri.path);
-    var result = await driver.getResult(path);
+    var result = await driver.getResult2(path) as ResolvedUnitResult;
     var errors =
         result.errors.where((e) => e.severity == Severity.error).toList();
     if (errors.isNotEmpty) {
diff --git a/pkg/analyzer/tool/fasta_migration_progress.sh b/pkg/analyzer/tool/fasta_migration_progress.sh
index 042e0db..ec3d9eb 100644
--- a/pkg/analyzer/tool/fasta_migration_progress.sh
+++ b/pkg/analyzer/tool/fasta_migration_progress.sh
@@ -44,7 +44,7 @@
   echo "  Log file: $logfile"
   # TODO: delete by default and stop logging the location of the file.
   # delete=1
-  python tools/test.py -m release --checked --use-sdk \
+  python3 tools/test.py -m release --checked --use-sdk \
      --vm-options="-DuseFastaParser=true" \
      --print_passing_stdout \
      pkg/analy > $logfile
diff --git a/pkg/analyzer_cli/lib/src/analyzer_impl.dart b/pkg/analyzer_cli/lib/src/analyzer_impl.dart
index 1486580..a3e1fd9 100644
--- a/pkg/analyzer_cli/lib/src/analyzer_impl.dart
+++ b/pkg/analyzer_cli/lib/src/analyzer_impl.dart
@@ -206,7 +206,8 @@
   Future<LibraryElement> _resolveLibrary() async {
     var libraryPath = libraryFile.path;
     analysisDriver.priorityFiles = [libraryPath];
-    var elementResult = await analysisDriver.getUnitElement(libraryPath);
+    var elementResult =
+        await analysisDriver.getUnitElement2(libraryPath) as UnitElementResult;
     return elementResult.element.library;
   }
 
diff --git a/pkg/analyzer_plugin/lib/plugin/plugin.dart b/pkg/analyzer_plugin/lib/plugin/plugin.dart
index 0a5fa70..14ede54 100644
--- a/pkg/analyzer_plugin/lib/plugin/plugin.dart
+++ b/pkg/analyzer_plugin/lib/plugin/plugin.dart
@@ -161,9 +161,8 @@
       throw RequestFailure(
           RequestErrorFactory.pluginError('Failed to analyze $path', null));
     }
-    var result = await driver.getResult(path);
-    var state = result.state;
-    if (state != ResultState.VALID) {
+    var result = await driver.getResult2(path);
+    if (result is! ResolvedUnitResult) {
       // Return an error from the request.
       throw RequestFailure(
           RequestErrorFactory.pluginError('Failed to analyze $path', null));
diff --git a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart
index eb1cded..8341d33 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart
@@ -193,14 +193,13 @@
     }
 
     var session = workspace.getSession(path);
-    var result = await session?.getResolvedUnit(path);
-    var state = result?.state ?? ResultState.INVALID_FILE_TYPE;
-    if (state == ResultState.INVALID_FILE_TYPE) {
+    var result = await session?.getResolvedUnit2(path);
+    if (result is! ResolvedUnitResult) {
       throw AnalysisException('Cannot analyze "$path"');
     }
-    var timeStamp = state == ResultState.VALID ? 0 : -1;
+    var timeStamp = result.exists ? 0 : -1;
 
-    var declaredUnit = result!.unit?.declaredElement;
+    var declaredUnit = result.unit?.declaredElement;
     var libraryUnit = declaredUnit?.library.definingCompilationUnit;
 
     DartFileEditBuilderImpl? libraryEditBuilder;
diff --git a/pkg/analyzer_plugin/lib/src/utilities/completion/completion_target.dart b/pkg/analyzer_plugin/lib/src/utilities/completion/completion_target.dart
index 4781140..3c3d9c4 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/completion/completion_target.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/completion/completion_target.dart
@@ -506,7 +506,10 @@
     if (token == null) {
       return null;
     }
-    if (offset >= token.offset) {
+    // Usually if the offset is greater than the token it can't be in the comment
+    // but for EOF this is not the case - the offset at EOF could still be inside
+    // the comment if EOF is on the same line as the comment.
+    if (token.type != TokenType.EOF && offset >= token.offset) {
       return null;
     }
     token = token.precedingComments;
diff --git a/pkg/analyzer_plugin/pubspec.yaml b/pkg/analyzer_plugin/pubspec.yaml
index 083c022..06f98e5 100644
--- a/pkg/analyzer_plugin/pubspec.yaml
+++ b/pkg/analyzer_plugin/pubspec.yaml
@@ -8,7 +8,7 @@
   sdk: '>=2.12.0 <3.0.0'
 
 dependencies:
-  analyzer: ^1.4.0
+  analyzer: ^1.5.0
   collection: ^1.15.0
   dart_style: ^2.0.0
   pub_semver: ^2.0.0
diff --git a/pkg/analyzer_plugin/test/support/abstract_context.dart b/pkg/analyzer_plugin/test/support/abstract_context.dart
index e0776c3..6bd18116 100644
--- a/pkg/analyzer_plugin/test/support/abstract_context.dart
+++ b/pkg/analyzer_plugin/test/support/abstract_context.dart
@@ -97,7 +97,8 @@
   }
 
   Future<ResolvedUnitResult> resolveFile(String path) async {
-    return contextFor(path).currentSession.getResolvedUnit(path);
+    var session = contextFor(path).currentSession;
+    return await session.getResolvedUnit2(path) as ResolvedUnitResult;
   }
 
   void setUp() {
diff --git a/pkg/compiler/lib/src/common/codegen.dart b/pkg/compiler/lib/src/common/codegen.dart
index a17ef22..9ea7506 100644
--- a/pkg/compiler/lib/src/common/codegen.dart
+++ b/pkg/compiler/lib/src/common/codegen.dart
@@ -551,9 +551,6 @@
         case ModularNameKind.operatorIs:
           name.value = namer.operatorIs(name.data);
           break;
-        case ModularNameKind.operatorIsType:
-          name.value = namer.operatorIsType(name.data);
-          break;
         case ModularNameKind.instanceMethod:
           name.value = namer.instanceMethodName(name.data);
           break;
@@ -626,7 +623,6 @@
   staticClosure,
   methodProperty,
   operatorIs,
-  operatorIsType,
   instanceMethod,
   instanceField,
   invocation,
@@ -674,9 +670,6 @@
       case ModularNameKind.globalPropertyNameForMember:
         data = source.readMember();
         break;
-      case ModularNameKind.operatorIsType:
-        data = source.readDartType();
-        break;
       case ModularNameKind.invocation:
         data = Selector.readFromDataSource(source);
         break;
@@ -720,9 +713,6 @@
       case ModularNameKind.globalPropertyNameForMember:
         sink.writeMember(data);
         break;
-      case ModularNameKind.operatorIsType:
-        sink.writeDartType(data);
-        break;
       case ModularNameKind.invocation:
         Selector selector = data;
         selector.writeToDataSink(sink);
@@ -1247,6 +1237,11 @@
   }
 
   @override
+  void visitDeferredStatement(js.DeferredStatement node) {
+    throw new UnsupportedError('JsNodeSerializer.visitDeferredStatement');
+  }
+
+  @override
   void visitDeferredNumber(js.DeferredNumber node) {
     throw new UnsupportedError('JsNodeSerializer.visitDeferredNumber');
   }
diff --git a/pkg/compiler/lib/src/ir/scope_visitor.dart b/pkg/compiler/lib/src/ir/scope_visitor.dart
index 3004f39..abaff07 100644
--- a/pkg/compiler/lib/src/ir/scope_visitor.dart
+++ b/pkg/compiler/lib/src/ir/scope_visitor.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:kernel/ast.dart' as ir;
+import 'package:kernel/core_types.dart' as ir;
 import 'package:kernel/type_environment.dart' as ir;
 
 import '../ir/constants.dart';
@@ -18,6 +19,9 @@
   final Dart2jsConstantEvaluator _constantEvaluator;
   ir.StaticTypeContext _staticTypeContext;
 
+  ir.TypeEnvironment get _typeEnvironment => _constantEvaluator.typeEnvironment;
+  ir.CoreTypes get _coreTypes => _typeEnvironment.coreTypes;
+
   final ClosureScopeModel _model = new ClosureScopeModel();
 
   /// A map of each visited call node with the associated information about what
@@ -87,8 +91,7 @@
           initializerComplexity: const EvaluationComplexity.lazy());
     }
 
-    _staticTypeContext =
-        new ir.StaticTypeContext(node, _constantEvaluator.typeEnvironment);
+    _staticTypeContext = new ir.StaticTypeContext(node, _typeEnvironment);
     if (node is ir.Constructor) {
       _hasThisLocal = true;
     } else if (node is ir.Procedure && node.kind == ir.ProcedureKind.Factory) {
@@ -958,9 +961,7 @@
     }
 
     EvaluationComplexity complexity = visitArguments(node.arguments);
-    if (complexity.isConstant &&
-        node.target ==
-            _staticTypeContext.typeEnvironment.coreTypes.identicalProcedure) {
+    if (complexity.isConstant && node.target == _coreTypes.identicalProcedure) {
       return _evaluateImplicitConstant(node);
     }
     return node.isConst
@@ -982,6 +983,14 @@
   @override
   EvaluationComplexity visitConstructorInvocation(
       ir.ConstructorInvocation node) {
+    // TODO(45681): Investigate if other initializers should be made eager.
+
+    // Lazily invoking the `_Cell` constructor pessimizes certain uses of late
+    // variables, so we ensure it gets called eagerly.
+    if (node.target == _coreTypes.cellConstructor) {
+      return EvaluationComplexity.eager();
+    }
+
     if (node.arguments.types.isNotEmpty) {
       visitNodesInContext(node.arguments.types,
           new VariableUse.constructorTypeArgument(node.target));
diff --git a/pkg/compiler/lib/src/js/rewrite_async.dart b/pkg/compiler/lib/src/js/rewrite_async.dart
index 53e156a..ab8d742 100644
--- a/pkg/compiler/lib/src/js/rewrite_async.dart
+++ b/pkg/compiler/lib/src/js/rewrite_async.dart
@@ -1265,6 +1265,9 @@
   js.Expression visitDeferredExpression(js.DeferredExpression node) => node;
 
   @override
+  visitDeferredStatement(js.DeferredStatement node) => unsupported(node);
+
+  @override
   js.Expression visitDeferredNumber(js.DeferredNumber node) => node;
 
   @override
@@ -2677,6 +2680,11 @@
   }
 
   @override
+  bool visitDeferredStatement(js.DeferredStatement node) {
+    return unsupported(node);
+  }
+
+  @override
   bool visitDeferredNumber(js.DeferredNumber node) {
     return false;
   }
diff --git a/pkg/compiler/lib/src/js/size_estimator.dart b/pkg/compiler/lib/src/js/size_estimator.dart
index e8ec19f..e2b8a9b 100644
--- a/pkg/compiler/lib/src/js/size_estimator.dart
+++ b/pkg/compiler/lib/src/js/size_estimator.dart
@@ -830,6 +830,16 @@
     }
   }
 
+  @override
+  visitDeferredStatement(DeferredStatement node) {
+    if (node.isFinalized) {
+      // Continue printing with the statement value.
+      node.statement.accept(this);
+    } else {
+      sizeEstimate(node);
+    }
+  }
+
   outputNumberWithRequiredWhitespace(String number) {
     int charCode = number.codeUnitAt(0);
     if (charCode == charCodes.$MINUS) {
diff --git a/pkg/compiler/lib/src/js_backend/namer.dart b/pkg/compiler/lib/src/js_backend/namer.dart
index 82b1481..71a11cf 100644
--- a/pkg/compiler/lib/src/js_backend/namer.dart
+++ b/pkg/compiler/lib/src/js_backend/namer.dart
@@ -436,7 +436,6 @@
   static const String _callPrefixDollar = r'call$';
 
   static final jsAst.Name _literalDollar = new StringBackedName(r'$');
-  static final jsAst.Name _literalUnderscore = new StringBackedName('_');
   static final jsAst.Name literalPlus = new StringBackedName('+');
   static final jsAst.Name _literalDynamic = new StringBackedName("dynamic");
 
@@ -1507,20 +1506,6 @@
   }
 
   @override
-  jsAst.Name operatorIsType(DartType type) {
-    if (type is FunctionType) {
-      // TODO(erikcorry): Reduce from $isx to ix when we are minifying.
-      return new CompoundName([
-        new StringBackedName(fixedNames.operatorIsPrefix),
-        _literalUnderscore,
-        getFunctionTypeName(type)
-      ]);
-    }
-    InterfaceType interfaceType = type;
-    return operatorIs(interfaceType.element);
-  }
-
-  @override
   jsAst.Name operatorIs(ClassEntity element) {
     // TODO(erikcorry): Reduce from $isx to ix when we are minifying.
     return new CompoundName([
@@ -2413,9 +2398,6 @@
   /// [element].
   jsAst.Name operatorIs(ClassEntity element);
 
-  /// Return the name of the `isX` property for classes that implement [type].
-  jsAst.Name operatorIsType(DartType type);
-
   /// Returns the name of the lazy initializer for the static field [element].
   jsAst.Name lazyInitializerName(FieldEntity element);
 
@@ -2660,14 +2642,6 @@
   }
 
   @override
-  jsAst.Name operatorIsType(DartType type) {
-    jsAst.Name name =
-        new ModularName(ModularNameKind.operatorIsType, data: type);
-    _registry.registerModularName(name);
-    return name;
-  }
-
-  @override
   jsAst.Name instanceMethodName(FunctionEntity method) {
     jsAst.Name name =
         new ModularName(ModularNameKind.instanceMethod, data: method);
diff --git a/pkg/compiler/lib/src/kernel/dart2js_target.dart b/pkg/compiler/lib/src/kernel/dart2js_target.dart
index d0b3617..96cd1ad 100644
--- a/pkg/compiler/lib/src/kernel/dart2js_target.dart
+++ b/pkg/compiler/lib/src/kernel/dart2js_target.dart
@@ -62,8 +62,6 @@
 
 /// Late lowerings which the frontend performs for dart2js.
 const List<int> _allEnabledLateLowerings = [
-  LateLowering.initializedNonFinalStaticField,
-  LateLowering.initializedFinalStaticField,
   LateLowering.uninitializedNonFinalInstanceField,
   LateLowering.uninitializedFinalInstanceField,
   LateLowering.initializedNonFinalInstanceField,
diff --git a/pkg/compiler/lib/src/kernel/transformations/late_lowering.dart b/pkg/compiler/lib/src/kernel/transformations/late_lowering.dart
index eacb5ae..ebac6bb 100644
--- a/pkg/compiler/lib/src/kernel/transformations/late_lowering.dart
+++ b/pkg/compiler/lib/src/kernel/transformations/late_lowering.dart
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:kernel/ast.dart';
-import 'package:kernel/library_index.dart';
+import 'package:kernel/core_types.dart';
 import 'package:kernel/type_algebra.dart';
 
 bool _shouldLowerVariable(VariableDeclaration node) => node.isLate;
@@ -15,7 +15,7 @@
     _shouldLowerVariable(node) && node.initializer != null;
 
 bool _shouldLowerField(Field node) =>
-    node.initializer == null && node.isStatic && node.isLate;
+    node.isLate && node.isStatic && node.initializer == null;
 
 class _Reader {
   final Procedure _procedure;
@@ -28,53 +28,24 @@
 }
 
 class LateLowering {
-  final Class _cellClass;
-  final Constructor _cellConstructor;
-
-  final Class _initializedCellClass;
-  final Constructor _initializedCellConstructor;
+  final CoreTypes _coreTypes;
 
   final _Reader _readLocal;
   final _Reader _readField;
   final _Reader _readInitialized;
   final _Reader _readInitializedFinal;
 
-  final Procedure _setValue;
-  final Procedure _setFinalLocalValue;
-  final Procedure _setFinalFieldValue;
-  final Procedure _setInitializedValue;
-  final Procedure _setInitializedFinalValue;
-
   // TODO(fishythefish): Remove cells when exiting their scope.
   final Map<VariableDeclaration, VariableDeclaration> _variableCells = {};
   final Map<Field, Field> _fieldCells = {};
 
   Member _contextMember;
 
-  LateLowering(LibraryIndex index)
-      : _cellClass = index.getClass('dart:_late_helper', '_Cell'),
-        _cellConstructor = index.getMember('dart:_late_helper', '_Cell', ''),
-        _initializedCellClass =
-            index.getClass('dart:_late_helper', '_InitializedCell'),
-        _initializedCellConstructor =
-            index.getMember('dart:_late_helper', '_InitializedCell', ''),
-        _readLocal =
-            _Reader(index.getMember('dart:_late_helper', '_Cell', 'readLocal')),
-        _readField =
-            _Reader(index.getMember('dart:_late_helper', '_Cell', 'readField')),
-        _readInitialized = _Reader(
-            index.getMember('dart:_late_helper', '_InitializedCell', 'read')),
-        _readInitializedFinal = _Reader(index.getMember(
-            'dart:_late_helper', '_InitializedCell', 'readFinal')),
-        _setValue = index.getMember('dart:_late_helper', '_Cell', 'set:value'),
-        _setFinalLocalValue = index.getMember(
-            'dart:_late_helper', '_Cell', 'set:finalLocalValue'),
-        _setFinalFieldValue = index.getMember(
-            'dart:_late_helper', '_Cell', 'set:finalFieldValue'),
-        _setInitializedValue = index.getMember(
-            'dart:_late_helper', '_InitializedCell', 'set:value'),
-        _setInitializedFinalValue = index.getMember(
-            'dart:_late_helper', '_InitializedCell', 'set:finalValue');
+  LateLowering(this._coreTypes)
+      : _readLocal = _Reader(_coreTypes.cellReadLocal),
+        _readField = _Reader(_coreTypes.cellReadField),
+        _readInitialized = _Reader(_coreTypes.initializedCellRead),
+        _readInitializedFinal = _Reader(_coreTypes.initializedCellReadFinal);
 
   void transformAdditionalExports(Library node) {
     List<Reference> additionalExports = node.additionalExports;
@@ -89,13 +60,13 @@
   }
 
   ConstructorInvocation _callCellConstructor(int fileOffset) =>
-      ConstructorInvocation(
-          _cellConstructor, Arguments.empty()..fileOffset = fileOffset)
+      ConstructorInvocation(_coreTypes.cellConstructor,
+          Arguments.empty()..fileOffset = fileOffset)
         ..fileOffset = fileOffset;
 
   ConstructorInvocation _callInitializedCellConstructor(
           Expression initializer, int fileOffset) =>
-      ConstructorInvocation(_initializedCellConstructor,
+      ConstructorInvocation(_coreTypes.initializedCellConstructor,
           Arguments([initializer])..fileOffset = fileOffset)
         ..fileOffset = fileOffset;
 
@@ -127,8 +98,8 @@
       int fileOffset = variable.fileOffset;
       return VariableDeclaration(variable.name,
           initializer: _callCellConstructor(fileOffset),
-          type: InterfaceType(
-              _cellClass, _contextMember.enclosingLibrary.nonNullable),
+          type: InterfaceType(_coreTypes.cellClass,
+              _contextMember.enclosingLibrary.nonNullable),
           isFinal: true)
         ..fileOffset = fileOffset;
     });
@@ -152,7 +123,7 @@
           initializer: _callInitializedCellConstructor(
               _initializerClosure(variable.initializer, variable.type),
               fileOffset),
-          type: InterfaceType(_initializedCellClass,
+          type: InterfaceType(_coreTypes.initializedCellClass,
               _contextMember.enclosingLibrary.nonNullable),
           isFinal: true)
         ..fileOffset = fileOffset;
@@ -207,8 +178,12 @@
     int fileOffset = node.fileOffset;
     VariableGet cell = _variableCellAccess(variable, fileOffset);
     Procedure setter = variable.initializer == null
-        ? (variable.isFinal ? _setFinalLocalValue : _setValue)
-        : (variable.isFinal ? _setInitializedFinalValue : _setInitializedValue);
+        ? (variable.isFinal
+            ? _coreTypes.cellFinalLocalValueSetter
+            : _coreTypes.cellValueSetter)
+        : (variable.isFinal
+            ? _coreTypes.initializedCellFinalValueSetter
+            : _coreTypes.initializedCellValueSetter);
     return _callSetter(setter, cell, node.value, fileOffset);
   }
 
@@ -219,7 +194,8 @@
       field.getterReference.canonicalName?.unbind();
       field.setterReference?.canonicalName?.unbind();
       return Field.immutable(field.name,
-          type: InterfaceType(_cellClass, field.enclosingLibrary.nonNullable),
+          type: InterfaceType(
+              _coreTypes.cellClass, field.enclosingLibrary.nonNullable),
           initializer: _callCellConstructor(fileOffset),
           isFinal: true,
           isStatic: true,
@@ -260,7 +236,9 @@
     if (target is Field && _shouldLowerField(target)) {
       int fileOffset = node.fileOffset;
       StaticGet cell = _fieldCellAccess(target, fileOffset);
-      Procedure setter = target.isFinal ? _setFinalFieldValue : _setValue;
+      Procedure setter = target.isFinal
+          ? _coreTypes.cellFinalFieldValueSetter
+          : _coreTypes.cellValueSetter;
       return _callSetter(setter, cell, node.value, fileOffset);
     } else {
       return node;
diff --git a/pkg/compiler/lib/src/kernel/transformations/lowering.dart b/pkg/compiler/lib/src/kernel/transformations/lowering.dart
index ccc4d7e..3885486 100644
--- a/pkg/compiler/lib/src/kernel/transformations/lowering.dart
+++ b/pkg/compiler/lib/src/kernel/transformations/lowering.dart
@@ -33,7 +33,7 @@
 
   _Lowering(CoreTypes coreTypes, ClassHierarchy hierarchy)
       : factorySpecializer = FactorySpecializer(coreTypes, hierarchy),
-        _lateLowering = LateLowering(coreTypes.index);
+        _lateLowering = LateLowering(coreTypes);
 
   void transformAdditionalExports(Library node) {
     _lateLowering.transformAdditionalExports(node);
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index a729f53..0051870 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -48,7 +48,6 @@
 import '../universe/feature.dart';
 import '../universe/member_usage.dart' show MemberAccess;
 import '../universe/selector.dart';
-import '../universe/side_effects.dart' show SideEffects;
 import '../universe/target_checks.dart' show TargetChecks;
 import '../universe/use.dart' show ConstantUse, StaticUse, TypeUse;
 import '../world.dart';
@@ -4168,10 +4167,6 @@
       _handleForeignDartClosureToJs(invocation, 'DART_CLOSURE_TO_JS');
     } else if (name == 'RAW_DART_FUNCTION_REF') {
       _handleForeignRawFunctionRef(invocation, 'RAW_DART_FUNCTION_REF');
-    } else if (name == 'JS_SET_STATIC_STATE') {
-      _handleForeignJsSetStaticState(invocation);
-    } else if (name == 'JS_GET_STATIC_STATE') {
-      _handleForeignJsGetStaticState(invocation);
     } else if (name == 'JS_GET_NAME') {
       _handleForeignJsGetName(invocation);
     } else if (name == 'JS_EMBEDDED_GLOBAL') {
@@ -4499,37 +4494,6 @@
     return;
   }
 
-  void _handleForeignJsSetStaticState(ir.StaticInvocation invocation) {
-    if (_unexpectedForeignArguments(invocation,
-        minPositional: 1, maxPositional: 1)) {
-      // Result expected on stack.
-      stack.add(graph.addConstantNull(closedWorld));
-      return;
-    }
-
-    List<HInstruction> inputs = _visitPositionalArguments(invocation.arguments);
-
-    String isolateName = _namer.staticStateHolder;
-    SideEffects sideEffects = new SideEffects.empty();
-    sideEffects.setAllSideEffects();
-    push(new HForeignCode(js.js.parseForeignJS("$isolateName = #"),
-        _abstractValueDomain.dynamicType, inputs,
-        nativeBehavior: NativeBehavior.CHANGES_OTHER, effects: sideEffects));
-  }
-
-  void _handleForeignJsGetStaticState(ir.StaticInvocation invocation) {
-    if (_unexpectedForeignArguments(invocation,
-        minPositional: 0, maxPositional: 0)) {
-      // Result expected on stack.
-      stack.add(graph.addConstantNull(closedWorld));
-      return;
-    }
-
-    push(new HForeignCode(js.js.parseForeignJS(_namer.staticStateHolder),
-        _abstractValueDomain.dynamicType, <HInstruction>[],
-        nativeBehavior: NativeBehavior.DEPENDS_OTHER));
-  }
-
   void _handleForeignJsGetName(ir.StaticInvocation invocation) {
     if (_unexpectedForeignArguments(invocation,
         minPositional: 1, maxPositional: 1)) {
diff --git a/pkg/compiler/test/inference/data/foreign.dart b/pkg/compiler/test/inference/data/foreign.dart
index e0b7078..7ae6e76 100644
--- a/pkg/compiler/test/inference/data/foreign.dart
+++ b/pkg/compiler/test/inference/data/foreign.dart
@@ -23,8 +23,6 @@
   jsEmbeddedGlobal_getTypeFromName();
 
   jsStringConcat();
-
-  jsGetStaticState();
 }
 
 /*member: jsCallEmpty:[null|subclass=Object]*/
@@ -46,6 +44,3 @@
 
 /*member: jsStringConcat:[exact=JSString]*/
 jsStringConcat() => JS_STRING_CONCAT('a', 'b');
-
-/*member: jsGetStaticState:[null|subclass=Object]*/
-jsGetStaticState() => JS_GET_STATIC_STATE();
diff --git a/pkg/compiler/test/inference/side_effects/foreign.dart b/pkg/compiler/test/inference/side_effects/foreign.dart
index 9884d38..68ff097 100644
--- a/pkg/compiler/test/inference/side_effects/foreign.dart
+++ b/pkg/compiler/test/inference/side_effects/foreign.dart
@@ -34,9 +34,6 @@
 /*member: jsStringConcat:SideEffects(reads nothing; writes nothing)*/
 jsStringConcat() => JS_STRING_CONCAT('a', 'b');
 
-/*member: jsGetStaticState:SideEffects(reads nothing; writes anything)*/
-jsGetStaticState() => JS_GET_STATIC_STATE();
-
 /*member: main:SideEffects(reads anything; writes anything)*/
 main() {
   jsCallInt();
@@ -47,6 +44,4 @@
   jsEmbeddedGlobal_getTypeFromName();
 
   jsStringConcat();
-
-  jsGetStaticState();
 }
diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart
index 88ec1fb..a01a3b9 100644
--- a/pkg/dev_compiler/lib/src/kernel/compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart
@@ -5109,6 +5109,7 @@
     // If we know that the left type uses identity for equality, we can
     // sometimes emit better code, either `===` or `==`.
     var isEnum = leftType is InterfaceType && leftType.classNode.isEnum;
+
     var usesIdentity = _typeRep.isPrimitive(leftType) ||
         isEnum ||
         _isNull(left) ||
diff --git a/pkg/dev_compiler/lib/src/kernel/native_types.dart b/pkg/dev_compiler/lib/src/kernel/native_types.dart
index dd7bdc8..5a81641 100644
--- a/pkg/dev_compiler/lib/src/kernel/native_types.dart
+++ b/pkg/dev_compiler/lib/src/kernel/native_types.dart
@@ -49,6 +49,8 @@
     _addExtensionType(coreTypes.doubleClass, true);
     _addExtensionType(coreTypes.boolClass, true);
     _addExtensionType(coreTypes.stringClass, true);
+    // Allow `Function.==` to be recognized as a symbolized member.
+    _addExtensionType(coreTypes.functionClass, false);
 
     var sdk = coreTypes.index;
     _addExtensionTypes(sdk.getLibrary('dart:_interceptors'));
diff --git a/pkg/front_end/lib/src/fasta/builder/class_builder.dart b/pkg/front_end/lib/src/fasta/builder/class_builder.dart
index 8d2d698..c528f5f 100644
--- a/pkg/front_end/lib/src/fasta/builder/class_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/class_builder.dart
@@ -705,7 +705,7 @@
     Set<ClassBuilder> implemented = new Set<ClassBuilder>();
     for (TypeBuilder type in interfaceBuilders) {
       if (type is NamedTypeBuilder) {
-        int charOffset = -1; // TODO(ahe): Get offset from type.
+        int charOffset = type.charOffset;
         TypeDeclarationBuilder typeDeclaration = type.declaration;
         TypeDeclarationBuilder decl;
         TypeAliasBuilder aliasBuilder; // Non-null if a type alias is used.
diff --git a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
index f6cceab..5d38a56 100644
--- a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
@@ -52,7 +52,6 @@
         messageNonAgnosticConstant,
         messageNotAConstantExpression,
         noLength,
-        templateConstEvalBadState,
         templateConstEvalCaseImplementsEqual,
         templateConstEvalDeferredLibrary,
         templateConstEvalDuplicateElement,
@@ -71,6 +70,7 @@
         templateConstEvalInvalidSymbolName,
         templateConstEvalKeyImplementsEqual,
         templateConstEvalNonConstantVariableGet,
+        templateConstEvalUnhandledCoreException,
         templateConstEvalUnhandledException,
         templateConstEvalZeroDivisor;
 
@@ -1012,8 +1012,9 @@
         if (value is Constant) {
           message = templateConstEvalUnhandledException.withArguments(
               value, isNonNullableByDefault);
-        } else if (value is StateError) {
-          message = templateConstEvalBadState.withArguments(value.message);
+        } else if (value is Error) {
+          message = templateConstEvalUnhandledCoreException
+              .withArguments(value.toString());
         }
         assert(message != null);
 
@@ -3604,6 +3605,7 @@
     Constant result;
     if (node.expression != null) {
       result = evaluate(node.expression);
+      if (result is AbortConstant) return new AbortStatus(result);
     }
     return new ReturnStatus(result);
   }
diff --git a/pkg/front_end/lib/src/fasta/kernel/type_algorithms.dart b/pkg/front_end/lib/src/fasta/kernel/type_algorithms.dart
index 6e79f8f..a20b83a 100644
--- a/pkg/front_end/lib/src/fasta/kernel/type_algorithms.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/type_algorithms.dart
@@ -29,6 +29,7 @@
 import '../fasta_codes.dart'
     show
         LocatedMessage,
+        Message,
         templateBoundIssueViaCycleNonSimplicity,
         templateBoundIssueViaLoopNonSimplicity,
         templateBoundIssueViaRawTypeWithNonSimpleBounds,
@@ -664,8 +665,9 @@
 /// [TypeDeclarationBuilder] for the type variable from [variables] that has raw
 /// generic types with inbound references in its bound.  The second element of
 /// the triplet is the error message.  The third element is the context.
-List<Object> getInboundReferenceIssues(List<TypeVariableBuilder> variables) {
-  List<Object> issues = <Object>[];
+List<NonSimplicityIssue> getInboundReferenceIssues(
+    List<TypeVariableBuilder> variables) {
+  List<NonSimplicityIssue> issues = <NonSimplicityIssue>[];
   for (TypeVariableBuilder variable in variables) {
     if (variable.bound != null) {
       List<Object> rawTypesAndMutualDependencies =
@@ -678,23 +680,25 @@
           TypeVariableBuilder dependent = variablesAndDependencies[j];
           List<NamedTypeBuilder> dependencies = variablesAndDependencies[j + 1];
           for (NamedTypeBuilder dependency in dependencies) {
-            issues.add(variable);
-            issues.add(templateBoundIssueViaRawTypeWithNonSimpleBounds
-                .withArguments(type.declaration.name));
-            issues.add(<LocatedMessage>[
-              templateNonSimpleBoundViaVariable
-                  .withArguments(dependency.declaration.name)
-                  .withLocation(dependent.fileUri, dependent.charOffset,
-                      dependent.name.length)
-            ]);
+            issues.add(new NonSimplicityIssue(
+                variable,
+                templateBoundIssueViaRawTypeWithNonSimpleBounds
+                    .withArguments(type.declaration.name),
+                <LocatedMessage>[
+                  templateNonSimpleBoundViaVariable
+                      .withArguments(dependency.declaration.name)
+                      .withLocation(dependent.fileUri, dependent.charOffset,
+                          dependent.name.length)
+                ]));
           }
         }
         if (variablesAndDependencies.length == 0) {
           // The inbound references are in a compiled declaration in a .dill.
-          issues.add(variable);
-          issues.add(templateBoundIssueViaRawTypeWithNonSimpleBounds
-              .withArguments(type.declaration.name));
-          issues.add(const <LocatedMessage>[]);
+          issues.add(new NonSimplicityIssue(
+              variable,
+              templateBoundIssueViaRawTypeWithNonSimpleBounds
+                  .withArguments(type.declaration.name),
+              const <LocatedMessage>[]));
         }
       }
     }
@@ -708,12 +712,13 @@
 /// [TypeDeclarationBuilder] for the type variable from [variables] that has raw
 /// generic types with inbound references in its bound.  The second element of
 /// the triplet is the error message.  The third element is the context.
-List<Object> getInboundReferenceIssuesInType(TypeBuilder typeBuilder) {
+List<NonSimplicityIssue> getInboundReferenceIssuesInType(
+    TypeBuilder typeBuilder) {
   List<FunctionTypeBuilder> genericFunctionTypeBuilders =
       <FunctionTypeBuilder>[];
   findUnaliasedGenericFunctionTypes(typeBuilder,
       result: genericFunctionTypeBuilders);
-  List<Object> issues = <Object>[];
+  List<NonSimplicityIssue> issues = <NonSimplicityIssue>[];
   for (FunctionTypeBuilder genericFunctionTypeBuilder
       in genericFunctionTypeBuilders) {
     List<TypeVariableBuilder> typeVariables =
@@ -725,32 +730,34 @@
 
 /// Finds raw type paths starting from those in [start] and ending with [end].
 ///
-/// Returns list of found paths.  Each path is represented as a list of
-/// alternating builders of the raw generic types from the path and builders of
-/// type variables of the immediately preceding types that contain the reference
-/// to the next raw generic type in the path.  The list ends with the type
-/// builder for [end].
+/// Returns list of found paths consisting of [RawTypeCycleElement]s. The list
+/// ends with the type builder for [end].
 ///
 /// The reason for putting the type variables into the paths as well as for
 /// using type for [start], and not the corresponding type declaration,
 /// is better error reporting.
-List<List<Object>> findRawTypePathsToDeclaration(
+List<List<RawTypeCycleElement>> findRawTypePathsToDeclaration(
     TypeBuilder start, TypeDeclarationBuilder end,
     [Set<TypeDeclarationBuilder> visited]) {
   visited ??= new Set<TypeDeclarationBuilder>.identity();
-  List<List<Object>> paths = <List<Object>>[];
+  List<List<RawTypeCycleElement>> paths = <List<RawTypeCycleElement>>[];
   if (start is NamedTypeBuilder) {
     TypeDeclarationBuilder declaration = start.declaration;
     if (start.arguments == null) {
       if (start.declaration == end) {
-        paths.add(<Object>[start]);
+        paths.add(<RawTypeCycleElement>[new RawTypeCycleElement(start, null)]);
       } else if (visited.add(start.declaration)) {
         if (declaration is ClassBuilder && declaration.typeVariables != null) {
           for (TypeVariableBuilder variable in declaration.typeVariables) {
             if (variable.bound != null) {
-              for (List<Object> path in findRawTypePathsToDeclaration(
-                  variable.bound, end, visited)) {
-                paths.add(<Object>[start, variable]..addAll(path));
+              for (List<RawTypeCycleElement> path
+                  in findRawTypePathsToDeclaration(
+                      variable.bound, end, visited)) {
+                if (path.isNotEmpty) {
+                  paths.add(<RawTypeCycleElement>[
+                    new RawTypeCycleElement(start, null)
+                  ]..addAll(path..first.typeVariable = variable));
+                }
               }
             }
           }
@@ -758,10 +765,14 @@
           if (declaration.typeVariables != null) {
             for (TypeVariableBuilder variable in declaration.typeVariables) {
               if (variable.bound != null) {
-                for (List<Object> dependencyPath
+                for (List<RawTypeCycleElement> dependencyPath
                     in findRawTypePathsToDeclaration(
                         variable.bound, end, visited)) {
-                  paths.add(<Object>[start, variable]..addAll(dependencyPath));
+                  if (dependencyPath.isNotEmpty) {
+                    paths.add(<RawTypeCycleElement>[
+                      new RawTypeCycleElement(start, null)
+                    ]..addAll(dependencyPath..first.typeVariable = variable));
+                  }
                 }
               }
             }
@@ -771,11 +782,14 @@
             if (type.typeVariables != null) {
               for (TypeVariableBuilder variable in type.typeVariables) {
                 if (variable.bound != null) {
-                  for (List<Object> dependencyPath
+                  for (List<RawTypeCycleElement> dependencyPath
                       in findRawTypePathsToDeclaration(
                           variable.bound, end, visited)) {
-                    paths
-                        .add(<Object>[start, variable]..addAll(dependencyPath));
+                    if (dependencyPath.isNotEmpty) {
+                      paths.add(<RawTypeCycleElement>[
+                        new RawTypeCycleElement(start, null)
+                      ]..addAll(dependencyPath..first.typeVariable = variable));
+                    }
                   }
                 }
               }
@@ -814,22 +828,24 @@
 
 /// Finds raw generic type cycles ending and starting with [declaration].
 ///
-/// Returns list of found cycles.  Each cycle is represented as a list of
-/// alternating raw generic types from the cycle and type variables of the
-/// immediately preceding type that reference the next type in the cycle.  The
+/// Returns list of found cycles consisting of [RawTypeCycleElement]s. The
 /// cycle starts with a type variable from [declaration] and ends with a type
 /// that has [declaration] as its declaration.
 ///
 /// The reason for putting the type variables into the cycles is better error
 /// reporting.
-List<List<Object>> findRawTypeCycles(TypeDeclarationBuilder declaration) {
-  List<List<Object>> cycles = <List<Object>>[];
+List<List<RawTypeCycleElement>> findRawTypeCycles(
+    TypeDeclarationBuilder declaration) {
+  List<List<RawTypeCycleElement>> cycles = <List<RawTypeCycleElement>>[];
   if (declaration is ClassBuilder && declaration.typeVariables != null) {
     for (TypeVariableBuilder variable in declaration.typeVariables) {
       if (variable.bound != null) {
-        for (List<Object> path
+        for (List<RawTypeCycleElement> path
             in findRawTypePathsToDeclaration(variable.bound, declaration)) {
-          cycles.add(<Object>[variable]..addAll(path));
+          if (path.isNotEmpty) {
+            path.first.typeVariable = variable;
+            cycles.add(path);
+          }
         }
       }
     }
@@ -837,9 +853,12 @@
     if (declaration.typeVariables != null) {
       for (TypeVariableBuilder variable in declaration.typeVariables) {
         if (variable.bound != null) {
-          for (List<Object> dependencyPath
+          for (List<RawTypeCycleElement> dependencyPath
               in findRawTypePathsToDeclaration(variable.bound, declaration)) {
-            cycles.add(<Object>[variable]..addAll(dependencyPath));
+            if (dependencyPath.isNotEmpty) {
+              dependencyPath.first.typeVariable = variable;
+              cycles.add(dependencyPath);
+            }
           }
         }
       }
@@ -849,9 +868,12 @@
       if (type.typeVariables != null) {
         for (TypeVariableBuilder variable in type.typeVariables) {
           if (variable.bound != null) {
-            for (List<Object> dependencyPath
+            for (List<RawTypeCycleElement> dependencyPath
                 in findRawTypePathsToDeclaration(variable.bound, declaration)) {
-              cycles.add(<Object>[variable]..addAll(dependencyPath));
+              if (dependencyPath.isNotEmpty) {
+                dependencyPath.first.typeVariable = variable;
+                cycles.add(dependencyPath);
+              }
             }
           }
         }
@@ -870,34 +892,35 @@
 /// [TypeDeclarationBuilder] for the type variable from [variables] that has raw
 /// generic types with inbound references in its bound.  The second element of
 /// the triplet is the error message.  The third element is the context.
-List<Object> convertRawTypeCyclesIntoIssues(
-    TypeDeclarationBuilder declaration, List<List<Object>> cycles) {
-  List<Object> issues = <Object>[];
-  for (List<Object> cycle in cycles) {
-    if (cycle.length == 2) {
+List<NonSimplicityIssue> convertRawTypeCyclesIntoIssues(
+    TypeDeclarationBuilder declaration,
+    List<List<RawTypeCycleElement>> cycles) {
+  List<NonSimplicityIssue> issues = <NonSimplicityIssue>[];
+  for (List<RawTypeCycleElement> cycle in cycles) {
+    if (cycle.length == 1) {
       // Loop.
-      TypeVariableBuilder variable = cycle[0];
-      NamedTypeBuilder type = cycle[1];
-      issues.add(variable);
-      issues.add(templateBoundIssueViaLoopNonSimplicity
-          .withArguments(type.declaration.name));
-      issues.add(null); // Context.
-    } else {
+      issues.add(new NonSimplicityIssue(
+          cycle.single.typeVariable,
+          templateBoundIssueViaLoopNonSimplicity
+              .withArguments(cycle.single.type.declaration.name),
+          null));
+    } else if (cycle.isNotEmpty) {
+      assert(cycle.length > 1);
       List<LocatedMessage> context = <LocatedMessage>[];
-      for (int i = 0; i < cycle.length; i += 2) {
-        TypeVariableBuilder variable = cycle[i];
-        NamedTypeBuilder type = cycle[i + 1];
+      for (RawTypeCycleElement cycleElement in cycle) {
         context.add(templateNonSimpleBoundViaReference
-            .withArguments(type.declaration.name)
+            .withArguments(cycleElement.type.declaration.name)
             .withLocation(
-                variable.fileUri, variable.charOffset, variable.name.length));
+                cycleElement.typeVariable.fileUri,
+                cycleElement.typeVariable.charOffset,
+                cycleElement.typeVariable.name.length));
       }
-      NamedTypeBuilder firstEncounteredType = cycle[1];
 
-      issues.add(declaration);
-      issues.add(templateBoundIssueViaCycleNonSimplicity.withArguments(
-          declaration.name, firstEncounteredType.declaration.name));
-      issues.add(context);
+      issues.add(new NonSimplicityIssue(
+          declaration,
+          templateBoundIssueViaCycleNonSimplicity.withArguments(
+              declaration.name, cycle.first.type.declaration.name),
+          context));
     }
   }
   return issues;
@@ -912,9 +935,9 @@
 /// first element in the triplet is the type declaration that has the issue.
 /// The second element in the triplet is the error message.  The third element
 /// in the triplet is the context.
-List<Object> getNonSimplicityIssuesForTypeVariables(
+List<NonSimplicityIssue> getNonSimplicityIssuesForTypeVariables(
     List<TypeVariableBuilder> variables) {
-  if (variables == null) return <Object>[];
+  if (variables == null) return <NonSimplicityIssue>[];
   return getInboundReferenceIssues(variables);
 }
 
@@ -928,31 +951,31 @@
 /// first element in the triplet is the type declaration that has the issue.
 /// The second element in the triplet is the error message.  The third element
 /// in the triplet is the context.
-List<Object> getNonSimplicityIssuesForDeclaration(
+List<NonSimplicityIssue> getNonSimplicityIssuesForDeclaration(
     TypeDeclarationBuilder declaration,
     {bool performErrorRecovery: true}) {
-  List<Object> issues = <Object>[];
+  List<NonSimplicityIssue> issues = <NonSimplicityIssue>[];
   if (declaration is ClassBuilder && declaration.typeVariables != null) {
     issues.addAll(getInboundReferenceIssues(declaration.typeVariables));
   } else if (declaration is TypeAliasBuilder &&
       declaration.typeVariables != null) {
     issues.addAll(getInboundReferenceIssues(declaration.typeVariables));
   }
-  List<List<Object>> cyclesToReport = <List<Object>>[];
-  for (List<Object> cycle in findRawTypeCycles(declaration)) {
+  List<List<RawTypeCycleElement>> cyclesToReport =
+      <List<RawTypeCycleElement>>[];
+  for (List<RawTypeCycleElement> cycle in findRawTypeCycles(declaration)) {
     // To avoid reporting the same error for each element of the cycle, we only
     // do so if it comes the first in the lexicographical order.  Note that
     // one-element cycles shouldn't be checked, as they are loops.
-    if (cycle.length == 2) {
+    if (cycle.length == 1) {
       cyclesToReport.add(cycle);
     } else {
       String declarationPathAndName =
           "${declaration.fileUri}:${declaration.name}";
       String lexMinPathAndName = null;
-      for (int i = 1; i < cycle.length; i += 2) {
-        NamedTypeBuilder type = cycle[i];
-        String pathAndName =
-            "${type.declaration.fileUri}:${type.declaration.name}";
+      for (RawTypeCycleElement cycleElement in cycle) {
+        String pathAndName = "${cycleElement.type.declaration.fileUri}:"
+            "${cycleElement.type.declaration.name}";
         if (lexMinPathAndName == null ||
             lexMinPathAndName.compareTo(pathAndName) > 0) {
           lexMinPathAndName = pathAndName;
@@ -963,7 +986,7 @@
       }
     }
   }
-  List<Object> rawTypeCyclesAsIssues =
+  List<NonSimplicityIssue> rawTypeCyclesAsIssues =
       convertRawTypeCyclesIntoIssues(declaration, cyclesToReport);
   issues.addAll(rawTypeCyclesAsIssues);
 
@@ -978,10 +1001,11 @@
 ///
 /// The [cycles] are expected to be in the format specified for the return value
 /// of [findRawTypeCycles].
-void breakCycles(List<List<Object>> cycles) {
-  for (List<Object> cycle in cycles) {
-    TypeVariableBuilder variable = cycle[0];
-    variable.bound = null;
+void breakCycles(List<List<RawTypeCycleElement>> cycles) {
+  for (List<RawTypeCycleElement> cycle in cycles) {
+    if (cycle.isNotEmpty) {
+      cycle.first.typeVariable.bound = null;
+    }
   }
 }
 
@@ -1123,3 +1147,60 @@
     return anyTypeVariables(node.typeArguments);
   }
 }
+
+/// A representation of a found non-simplicity issue in bounds
+///
+/// The following are the examples of generic declarations with non-simple
+/// bounds:
+///
+///   // `A` has a non-simple bound.
+///   class A<X extends A<X>> {}
+///
+///   // Error: A type with non-simple bounds is used raw in another bound.
+///   class B<Y extends A> {}
+///
+///   // Error: Checking if a type has non-simple bounds leads back to the type,
+///   // so the process is infinite. In that case, the type is deemed as having
+///   // non-simple bounds.
+///   class C<U extends D> {} // `C` has a non-simple bound.
+///   class D<V extends C> {} // `D` has a non-simple bound.
+///
+/// See section 15.3.1 Auxiliary Concepts for Instantiation to Bound.
+class NonSimplicityIssue {
+  /// The generic declaration that has a non-simplicity issue.
+  final TypeDeclarationBuilder declaration;
+
+  /// The non-simplicity error message.
+  final Message message;
+
+  /// The context for the error message, containing the locations of all of the
+  /// elements from the cycle.
+  final List<LocatedMessage> context;
+
+  NonSimplicityIssue(this.declaration, this.message, this.context);
+}
+
+/// Represents an element of a non-simple raw type cycle
+///
+/// Such cycles appear when the process of checking if a type has a non-simple
+/// bound leads back to that type. The cycle that goes through other types and
+/// type variables in-between them is recorded for better error reporting. An
+/// example of such cycle is the following:
+///
+///   // Error: Checking if a type has non-simple bounds leads back to the type,
+///   // so the process is infinite. In that case, the type is deemed as having
+///   // non-simple bounds.
+///   class C<U extends D> {} // `C` has a non-simple bound.
+///   class D<V extends C> {} // `D` has a non-simple bound.
+///
+/// See section 15.3.1 Auxiliary Concepts for Instantiation to Bound.
+class RawTypeCycleElement {
+  /// The type that is on a non-simple raw type cycle.
+  final TypeBuilder type;
+
+  /// The type variable that connects [type] to the next element in the
+  /// non-simple raw type cycle.
+  TypeVariableBuilder typeVariable;
+
+  RawTypeCycleElement(this.type, this.typeVariable);
+}
diff --git a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
index 94ed0ca..aca996f 100644
--- a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
@@ -93,6 +93,7 @@
 
 import '../kernel/type_algorithms.dart'
     show
+        NonSimplicityIssue,
         calculateBounds,
         computeTypeVariableBuilderVariance,
         findGenericFunctionTypes,
@@ -3078,24 +3079,20 @@
       return variables.length;
     }
 
-    void reportIssues(List<Object> issues) {
-      for (int i = 0; i < issues.length; i += 3) {
-        TypeDeclarationBuilder declaration = issues[i];
-        Message message = issues[i + 1];
-        List<LocatedMessage> context = issues[i + 2];
-
-        addProblem(message, declaration.charOffset, declaration.name.length,
-            declaration.fileUri,
-            context: context);
+    void reportIssues(List<NonSimplicityIssue> issues) {
+      for (NonSimplicityIssue issue in issues) {
+        addProblem(issue.message, issue.declaration.charOffset,
+            issue.declaration.name.length, issue.declaration.fileUri,
+            context: issue.context);
       }
     }
 
     for (Builder declaration in libraryDeclaration.members.values) {
       if (declaration is ClassBuilder) {
         {
-          List<Object> issues = getNonSimplicityIssuesForDeclaration(
-              declaration,
-              performErrorRecovery: true);
+          List<NonSimplicityIssue> issues =
+              getNonSimplicityIssuesForDeclaration(declaration,
+                  performErrorRecovery: true);
           reportIssues(issues);
           count += computeDefaultTypesForVariables(declaration.typeVariables,
               inErrorRecovery: issues.isNotEmpty);
@@ -3417,7 +3414,11 @@
       DartType superBoundedAttempt,
       DartType superBoundedAttemptInverted}) {
     List<LocatedMessage> context;
-    if (typeParameter != null && typeParameter.fileOffset != -1) {
+    // Skip reporting location for function-type type parameters as it's a
+    // limitation of Kernel.
+    if (typeParameter != null &&
+        typeParameter.fileOffset != -1 &&
+        typeParameter.parent != null) {
       // It looks like when parameters come from patch files, they don't
       // have a reportable location.
       (context ??= <LocatedMessage>[]).add(
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart b/pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart
index bdf54c8..5e8e77f 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart
@@ -10,10 +10,13 @@
 
 import 'package:kernel/core_types.dart' show CoreTypes;
 
-import 'package:kernel/type_algebra.dart' show Substitution;
+import 'package:kernel/type_algebra.dart'
+    show FreshTypeParameters, Substitution, getFreshTypeParameters, substitute;
 
 import 'package:kernel/type_environment.dart';
 
+import 'package:kernel/src/bounds_checks.dart' show calculateBounds;
+
 import 'package:kernel/src/hierarchy_based_type_environment.dart'
     show HierarchyBasedTypeEnvironment;
 
@@ -357,6 +360,42 @@
             preferUpwardsInference: !typeParam.isLegacyCovariant);
       }
     }
+
+    if (!downwardsInferPhase) {
+      assert(typeParametersToInfer.length == inferredTypes.length);
+      FreshTypeParameters freshTypeParameters =
+          getFreshTypeParameters(typeParametersToInfer);
+      List<TypeParameter> helperTypeParameters =
+          freshTypeParameters.freshTypeParameters;
+
+      Map<TypeParameter, DartType> inferredSubstitution =
+          <TypeParameter, DartType>{};
+      for (int i = 0; i < helperTypeParameters.length; ++i) {
+        if (inferredTypes[i] is UnknownType) {
+          inferredSubstitution[helperTypeParameters[i]] =
+              new TypeParameterType.forAlphaRenaming(
+                  helperTypeParameters[i], helperTypeParameters[i]);
+        } else {
+          assert(isKnown(inferredTypes[i]));
+          inferredSubstitution[helperTypeParameters[i]] = inferredTypes[i];
+        }
+      }
+      for (int i = 0; i < helperTypeParameters.length; ++i) {
+        if (inferredTypes[i] is UnknownType) {
+          helperTypeParameters[i].bound =
+              substitute(helperTypeParameters[i].bound, inferredSubstitution);
+        } else {
+          helperTypeParameters[i].bound = inferredTypes[i];
+        }
+      }
+      List<DartType> instantiatedTypes = calculateBounds(
+          helperTypeParameters, coreTypes.objectClass, clientLibrary);
+      for (int i = 0; i < instantiatedTypes.length; ++i) {
+        if (inferredTypes[i] is UnknownType) {
+          inferredTypes[i] = instantiatedTypes[i];
+        }
+      }
+    }
   }
 
   @override
@@ -423,7 +462,9 @@
   /// of constraints.
   ///
   /// If [grounded] is `true`, then the returned type is guaranteed to be a
-  /// known type (i.e. it will not contain any instances of `?`).
+  /// known type (i.e. it will not contain any instances of `?`) if it is
+  /// constrained at all.  The returned type for unconstrained variables is
+  /// [UnknownType].
   ///
   /// If [isContravariant] is `true`, then we are solving for a contravariant
   /// type parameter which means we choose the upper bound rather than the
@@ -449,7 +490,7 @@
             ? greatestClosure(constraint.upper, topType, bottomType)
             : constraint.upper;
       } else {
-        return grounded ? const DynamicType() : const UnknownType();
+        return const UnknownType();
       }
     } else {
       // Prefer the known bound, if any.
@@ -467,7 +508,7 @@
             ? leastClosure(constraint.lower, topType, bottomType)
             : constraint.lower;
       } else {
-        return grounded ? bottomType : const UnknownType();
+        return const UnknownType();
       }
     }
   }
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index 99253fc..d15884e 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -92,8 +92,6 @@
 ConstConstructorLateFinalFieldWarning/example: Fail
 ConstConstructorNonFinalField/example: Fail
 ConstConstructorRedirectionToNonConst/analyzerCode: Fail # The analyzer doesn't report this error.
-ConstEvalBadState/analyzerCode: Fail
-ConstEvalBadState/example: Fail
 ConstEvalCaseImplementsEqual/analyzerCode: Fail
 ConstEvalCaseImplementsEqual/example: Fail
 ConstEvalCircularity/example: Fail
@@ -139,6 +137,8 @@
 ConstEvalTruncateError/example: Fail
 ConstEvalUnevaluated/analyzerCode: Fail
 ConstEvalUnevaluated/example: Fail
+ConstEvalUnhandledCoreException/analyzerCode: Fail
+ConstEvalUnhandledCoreException/example: Fail
 ConstEvalUnhandledException/analyzerCode: Fail
 ConstEvalUnhandledException/example: Fail
 ConstEvalZeroDivisor/example: Fail
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index 6b392cf..dafcd5f 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -106,9 +106,6 @@
 ConstEvalStartingPoint:
   template: "Constant evaluation error:"
 
-ConstEvalBadState:
-  template: "Bad state: '#stringOKEmpty'"
-
 ConstEvalContext:
   template: "While analyzing:"
 
@@ -225,6 +222,9 @@
 ConstEvalUnevaluated:
   template: "Couldn't evaluate constant expression."
 
+ConstEvalUnhandledCoreException:
+  template: "Unhandled core exception: #stringOKEmpty"
+
 ConstEvalUnhandledException:
   template: "Unhandled exception: #constant"
 
@@ -4366,7 +4366,7 @@
 
 FfiEmptyStruct:
   # Used by dart:ffi
-  template: "Struct '#name' is empty. Empty structs are undefined behavior."
+  template: "#string '#name' is empty. Empty structs and unions are undefined behavior."
   external: test/ffi_test.dart
 
 FfiTypeInvalid:
@@ -4376,12 +4376,12 @@
 
 FfiFieldNull:
   # Used by dart:ffi
-  template: "Field '#name' cannot have type 'Null', it must be `int`, `double`, `Pointer`, or a subtype of `Struct`."
+  template: "Field '#name' cannot have type 'Null', it must be `int`, `double`, `Pointer`, or a subtype of `Struct` or `Union`."
   external: test/ffi_test.dart
 
 FfiFieldAnnotation:
   # Used by dart:ffi
-  template: "Field '#name' requires exactly one annotation to declare its native type, which cannot be Void. dart:ffi Structs cannot have regular Dart fields."
+  template: "Field '#name' requires exactly one annotation to declare its native type, which cannot be Void. dart:ffi Structs and Unions cannot have regular Dart fields."
   external: test/ffi_test.dart
 
 FfiFieldNoAnnotation:
@@ -4392,7 +4392,7 @@
 FfiFieldCyclic:
   # Used by dart:ffi
   template: |
-    Struct '#name' contains itself. Cycle elements:
+    #string '#name' contains itself. Cycle elements:
     #names
   external: test/ffi_test.dart
 
@@ -4439,7 +4439,7 @@
 
 FfiStructGeneric:
   # Used by dart:ffi
-  template: "Struct '#name' should not be generic."
+  template: "#string '#name' should not be generic."
   external: test/ffi_test.dart
 
 FfiDartTypeMismatch:
diff --git a/pkg/front_end/test/fasta/type_inference/type_schema_environment_nnbd_test.dart b/pkg/front_end/test/fasta/type_inference/type_schema_environment_nnbd_test.dart
index 67cfacc..a1448b7 100644
--- a/pkg/front_end/test/fasta/type_inference/type_schema_environment_nnbd_test.dart
+++ b/pkg/front_end/test/fasta/type_inference/type_schema_environment_nnbd_test.dart
@@ -1403,8 +1403,10 @@
     // Solve(? <: T <: ?) => ?
     checkConstraintSolving("", "UNKNOWN", grounded: false);
 
-    // Solve(? <: T <: ?, grounded) => dynamic
-    checkConstraintSolving("", "dynamic", grounded: true);
+    // Solve(? <: T <: ?, grounded) => ?
+    // Fully unconstrained variables are inferred via instantiate-to-bounds
+    // rather than constraint solving.
+    checkConstraintSolving("", "UNKNOWN", grounded: true);
 
     // Solve(A <: T <: ?) => A
     checkConstraintSolving(":> A<dynamic>*", "A<dynamic>*", grounded: false);
diff --git a/pkg/front_end/test/fasta/type_inference/type_schema_environment_test.dart b/pkg/front_end/test/fasta/type_inference/type_schema_environment_test.dart
index ba48bb9..027bac2 100644
--- a/pkg/front_end/test/fasta/type_inference/type_schema_environment_test.dart
+++ b/pkg/front_end/test/fasta/type_inference/type_schema_environment_test.dart
@@ -493,8 +493,10 @@
     // Solve(? <: T <: ?) => ?
     checkConstraintSolving("", "UNKNOWN", grounded: false);
 
-    // Solve(? <: T <: ?, grounded) => dynamic
-    checkConstraintSolving("", "dynamic", grounded: true);
+    // Solve(? <: T <: ?, grounded) => ?
+    // Fully unconstrained variables are inferred via instantiate-to-bounds
+    // rather than constraint solving.
+    checkConstraintSolving("", "UNKNOWN", grounded: true);
 
     // Solve(A <: T <: ?) => A
     checkConstraintSolving(":> A*", "A*", grounded: false);
diff --git a/pkg/front_end/test/language_versioning/language_versioning_up_to_date_git_test.dart b/pkg/front_end/test/language_versioning/language_versioning_up_to_date_git_test.dart
index 16a442b..d79b1fd 100644
--- a/pkg/front_end/test/language_versioning/language_versioning_up_to_date_git_test.dart
+++ b/pkg/front_end/test/language_versioning/language_versioning_up_to_date_git_test.dart
@@ -18,7 +18,7 @@
 
 main(List<String> args) async {
   ProcessResult result = await Process.run(
-      "python", ["tools/make_version.py", "--no_git", "-q"],
+      "python3", ["tools/make_version.py", "--no_git", "-q"],
       workingDirectory: repoDir);
 
   String stdout = result.stdout.toString();
diff --git a/pkg/front_end/test/spell_checking_list_common.txt b/pkg/front_end/test/spell_checking_list_common.txt
index 912054f..f6a69db 100644
--- a/pkg/front_end/test/spell_checking_list_common.txt
+++ b/pkg/front_end/test/spell_checking_list_common.txt
@@ -415,6 +415,7 @@
 causes
 causing
 caveats
+cell
 certain
 chain
 chained
@@ -561,6 +562,7 @@
 computing
 concatenated
 concatenation
+concepts
 conclude
 conclusion
 concrete
@@ -587,6 +589,7 @@
 connect
 connected
 connection
+connects
 consequence
 consequently
 conservative
@@ -1723,6 +1726,7 @@
 like
 likely
 limit
+limitation
 limited
 line
 linear
@@ -3181,6 +3185,7 @@
 uninitialized
 union
 unioned
+unions
 unique
 unit
 unite
diff --git a/pkg/front_end/test/spell_checking_list_messages.txt b/pkg/front_end/test/spell_checking_list_messages.txt
index be9adce..4d51529 100644
--- a/pkg/front_end/test/spell_checking_list_messages.txt
+++ b/pkg/front_end/test/spell_checking_list_messages.txt
@@ -19,6 +19,7 @@
 compilercontext.runincontext
 compilesdk
 constructor(s)
+core
 count.#count
 d
 dart.dev
diff --git a/pkg/front_end/test/spell_checking_list_tests.txt b/pkg/front_end/test/spell_checking_list_tests.txt
index 17ee5f0..2a418f9 100644
--- a/pkg/front_end/test/spell_checking_list_tests.txt
+++ b/pkg/front_end/test/spell_checking_list_tests.txt
@@ -135,6 +135,7 @@
 class5c
 class5d
 cloneable
+cm
 cmd
 cmp
 cnn
diff --git a/pkg/front_end/testcases/const_functions/const_functions_list_error.dart.strong.expect b/pkg/front_end/testcases/const_functions/const_functions_list_error.dart.strong.expect
index 4024724..8f1cc49 100644
--- a/pkg/front_end/testcases/const_functions/const_functions_list_error.dart.strong.expect
+++ b/pkg/front_end/testcases/const_functions/const_functions_list_error.dart.strong.expect
@@ -11,28 +11,28 @@
 // pkg/front_end/testcases/const_functions/const_functions_list_error.dart:9:24: Error: Constant evaluation error:
 // const firstException = firstExceptionFn();
 //                        ^
-// pkg/front_end/testcases/const_functions/const_functions_list_error.dart:12:12: Context: Bad state: 'No element'
+// pkg/front_end/testcases/const_functions/const_functions_list_error.dart:12:12: Context: Unhandled core exception: Bad state: No element
 //   return x.first;
 //            ^
 //
 // pkg/front_end/testcases/const_functions/const_functions_list_error.dart:15:23: Error: Constant evaluation error:
 // const lastException = lastExceptionFn();
 //                       ^
-// pkg/front_end/testcases/const_functions/const_functions_list_error.dart:18:12: Context: Bad state: 'No element'
+// pkg/front_end/testcases/const_functions/const_functions_list_error.dart:18:12: Context: Unhandled core exception: Bad state: No element
 //   return x.last;
 //            ^
 //
 // pkg/front_end/testcases/const_functions/const_functions_list_error.dart:21:25: Error: Constant evaluation error:
 // const singleException = singleExceptionFn();
 //                         ^
-// pkg/front_end/testcases/const_functions/const_functions_list_error.dart:24:12: Context: Bad state: 'No element'
+// pkg/front_end/testcases/const_functions/const_functions_list_error.dart:24:12: Context: Unhandled core exception: Bad state: No element
 //   return x.single;
 //            ^
 //
 // pkg/front_end/testcases/const_functions/const_functions_list_error.dart:27:30: Error: Constant evaluation error:
 // const singleExceptionMulti = singleExceptionMultiFn();
 //                              ^
-// pkg/front_end/testcases/const_functions/const_functions_list_error.dart:30:12: Context: Bad state: 'Too many elements'
+// pkg/front_end/testcases/const_functions/const_functions_list_error.dart:30:12: Context: Unhandled core exception: Bad state: Too many elements
 //   return x.single;
 //            ^
 //
@@ -41,10 +41,10 @@
 
 import "package:expect/expect.dart";
 
-static const field core::int firstException = invalid-expression "Bad state: 'No element'";
-static const field core::int lastException = invalid-expression "Bad state: 'No element'";
-static const field core::int singleException = invalid-expression "Bad state: 'No element'";
-static const field core::int singleExceptionMulti = invalid-expression "Bad state: 'Too many elements'";
+static const field core::int firstException = invalid-expression "Unhandled core exception: Bad state: No element";
+static const field core::int lastException = invalid-expression "Unhandled core exception: Bad state: No element";
+static const field core::int singleException = invalid-expression "Unhandled core exception: Bad state: No element";
+static const field core::int singleExceptionMulti = invalid-expression "Unhandled core exception: Bad state: Too many elements";
 static const field core::int invalidProperty = invalid-expression "pkg/front_end/testcases/const_functions/const_functions_list_error.dart:36:12: Error: The getter 'invalidProperty' isn't defined for the class 'List<int>'.
  - 'List' is from 'dart:core'.
 Try correcting the name to the name of an existing getter, or defining a getter or field named 'invalidProperty'.
diff --git a/pkg/front_end/testcases/const_functions/const_functions_list_error.dart.strong.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_list_error.dart.strong.transformed.expect
index 6cf44d0..8ec1037 100644
--- a/pkg/front_end/testcases/const_functions/const_functions_list_error.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/const_functions/const_functions_list_error.dart.strong.transformed.expect
@@ -11,28 +11,28 @@
 // pkg/front_end/testcases/const_functions/const_functions_list_error.dart:9:24: Error: Constant evaluation error:
 // const firstException = firstExceptionFn();
 //                        ^
-// pkg/front_end/testcases/const_functions/const_functions_list_error.dart:12:12: Context: Bad state: 'No element'
+// pkg/front_end/testcases/const_functions/const_functions_list_error.dart:12:12: Context: Unhandled core exception: Bad state: No element
 //   return x.first;
 //            ^
 //
 // pkg/front_end/testcases/const_functions/const_functions_list_error.dart:15:23: Error: Constant evaluation error:
 // const lastException = lastExceptionFn();
 //                       ^
-// pkg/front_end/testcases/const_functions/const_functions_list_error.dart:18:12: Context: Bad state: 'No element'
+// pkg/front_end/testcases/const_functions/const_functions_list_error.dart:18:12: Context: Unhandled core exception: Bad state: No element
 //   return x.last;
 //            ^
 //
 // pkg/front_end/testcases/const_functions/const_functions_list_error.dart:21:25: Error: Constant evaluation error:
 // const singleException = singleExceptionFn();
 //                         ^
-// pkg/front_end/testcases/const_functions/const_functions_list_error.dart:24:12: Context: Bad state: 'No element'
+// pkg/front_end/testcases/const_functions/const_functions_list_error.dart:24:12: Context: Unhandled core exception: Bad state: No element
 //   return x.single;
 //            ^
 //
 // pkg/front_end/testcases/const_functions/const_functions_list_error.dart:27:30: Error: Constant evaluation error:
 // const singleExceptionMulti = singleExceptionMultiFn();
 //                              ^
-// pkg/front_end/testcases/const_functions/const_functions_list_error.dart:30:12: Context: Bad state: 'Too many elements'
+// pkg/front_end/testcases/const_functions/const_functions_list_error.dart:30:12: Context: Unhandled core exception: Bad state: Too many elements
 //   return x.single;
 //            ^
 //
@@ -41,10 +41,10 @@
 
 import "package:expect/expect.dart";
 
-static const field core::int firstException = invalid-expression "Bad state: 'No element'";
-static const field core::int lastException = invalid-expression "Bad state: 'No element'";
-static const field core::int singleException = invalid-expression "Bad state: 'No element'";
-static const field core::int singleExceptionMulti = invalid-expression "Bad state: 'Too many elements'";
+static const field core::int firstException = invalid-expression "Unhandled core exception: Bad state: No element";
+static const field core::int lastException = invalid-expression "Unhandled core exception: Bad state: No element";
+static const field core::int singleException = invalid-expression "Unhandled core exception: Bad state: No element";
+static const field core::int singleExceptionMulti = invalid-expression "Unhandled core exception: Bad state: Too many elements";
 static const field core::int invalidProperty = invalid-expression "pkg/front_end/testcases/const_functions/const_functions_list_error.dart:36:12: Error: The getter 'invalidProperty' isn't defined for the class 'List<int>'.
  - 'List' is from 'dart:core'.
 Try correcting the name to the name of an existing getter, or defining a getter or field named 'invalidProperty'.
diff --git a/pkg/front_end/testcases/const_functions/const_functions_list_error.dart.weak.expect b/pkg/front_end/testcases/const_functions/const_functions_list_error.dart.weak.expect
index fea30ee..6d2f2a4 100644
--- a/pkg/front_end/testcases/const_functions/const_functions_list_error.dart.weak.expect
+++ b/pkg/front_end/testcases/const_functions/const_functions_list_error.dart.weak.expect
@@ -11,28 +11,28 @@
 // pkg/front_end/testcases/const_functions/const_functions_list_error.dart:9:24: Error: Constant evaluation error:
 // const firstException = firstExceptionFn();
 //                        ^
-// pkg/front_end/testcases/const_functions/const_functions_list_error.dart:12:12: Context: Bad state: 'No element'
+// pkg/front_end/testcases/const_functions/const_functions_list_error.dart:12:12: Context: Unhandled core exception: Bad state: No element
 //   return x.first;
 //            ^
 //
 // pkg/front_end/testcases/const_functions/const_functions_list_error.dart:15:23: Error: Constant evaluation error:
 // const lastException = lastExceptionFn();
 //                       ^
-// pkg/front_end/testcases/const_functions/const_functions_list_error.dart:18:12: Context: Bad state: 'No element'
+// pkg/front_end/testcases/const_functions/const_functions_list_error.dart:18:12: Context: Unhandled core exception: Bad state: No element
 //   return x.last;
 //            ^
 //
 // pkg/front_end/testcases/const_functions/const_functions_list_error.dart:21:25: Error: Constant evaluation error:
 // const singleException = singleExceptionFn();
 //                         ^
-// pkg/front_end/testcases/const_functions/const_functions_list_error.dart:24:12: Context: Bad state: 'No element'
+// pkg/front_end/testcases/const_functions/const_functions_list_error.dart:24:12: Context: Unhandled core exception: Bad state: No element
 //   return x.single;
 //            ^
 //
 // pkg/front_end/testcases/const_functions/const_functions_list_error.dart:27:30: Error: Constant evaluation error:
 // const singleExceptionMulti = singleExceptionMultiFn();
 //                              ^
-// pkg/front_end/testcases/const_functions/const_functions_list_error.dart:30:12: Context: Bad state: 'Too many elements'
+// pkg/front_end/testcases/const_functions/const_functions_list_error.dart:30:12: Context: Unhandled core exception: Bad state: Too many elements
 //   return x.single;
 //            ^
 //
@@ -41,10 +41,10 @@
 
 import "package:expect/expect.dart";
 
-static const field core::int firstException = invalid-expression "Bad state: 'No element'";
-static const field core::int lastException = invalid-expression "Bad state: 'No element'";
-static const field core::int singleException = invalid-expression "Bad state: 'No element'";
-static const field core::int singleExceptionMulti = invalid-expression "Bad state: 'Too many elements'";
+static const field core::int firstException = invalid-expression "Unhandled core exception: Bad state: No element";
+static const field core::int lastException = invalid-expression "Unhandled core exception: Bad state: No element";
+static const field core::int singleException = invalid-expression "Unhandled core exception: Bad state: No element";
+static const field core::int singleExceptionMulti = invalid-expression "Unhandled core exception: Bad state: Too many elements";
 static const field core::int invalidProperty = invalid-expression "pkg/front_end/testcases/const_functions/const_functions_list_error.dart:36:12: Error: The getter 'invalidProperty' isn't defined for the class 'List<int>'.
  - 'List' is from 'dart:core'.
 Try correcting the name to the name of an existing getter, or defining a getter or field named 'invalidProperty'.
diff --git a/pkg/front_end/testcases/const_functions/const_functions_list_error.dart.weak.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_list_error.dart.weak.transformed.expect
index 95805b6..54d1e2b 100644
--- a/pkg/front_end/testcases/const_functions/const_functions_list_error.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/const_functions/const_functions_list_error.dart.weak.transformed.expect
@@ -11,28 +11,28 @@
 // pkg/front_end/testcases/const_functions/const_functions_list_error.dart:9:24: Error: Constant evaluation error:
 // const firstException = firstExceptionFn();
 //                        ^
-// pkg/front_end/testcases/const_functions/const_functions_list_error.dart:12:12: Context: Bad state: 'No element'
+// pkg/front_end/testcases/const_functions/const_functions_list_error.dart:12:12: Context: Unhandled core exception: Bad state: No element
 //   return x.first;
 //            ^
 //
 // pkg/front_end/testcases/const_functions/const_functions_list_error.dart:15:23: Error: Constant evaluation error:
 // const lastException = lastExceptionFn();
 //                       ^
-// pkg/front_end/testcases/const_functions/const_functions_list_error.dart:18:12: Context: Bad state: 'No element'
+// pkg/front_end/testcases/const_functions/const_functions_list_error.dart:18:12: Context: Unhandled core exception: Bad state: No element
 //   return x.last;
 //            ^
 //
 // pkg/front_end/testcases/const_functions/const_functions_list_error.dart:21:25: Error: Constant evaluation error:
 // const singleException = singleExceptionFn();
 //                         ^
-// pkg/front_end/testcases/const_functions/const_functions_list_error.dart:24:12: Context: Bad state: 'No element'
+// pkg/front_end/testcases/const_functions/const_functions_list_error.dart:24:12: Context: Unhandled core exception: Bad state: No element
 //   return x.single;
 //            ^
 //
 // pkg/front_end/testcases/const_functions/const_functions_list_error.dart:27:30: Error: Constant evaluation error:
 // const singleExceptionMulti = singleExceptionMultiFn();
 //                              ^
-// pkg/front_end/testcases/const_functions/const_functions_list_error.dart:30:12: Context: Bad state: 'Too many elements'
+// pkg/front_end/testcases/const_functions/const_functions_list_error.dart:30:12: Context: Unhandled core exception: Bad state: Too many elements
 //   return x.single;
 //            ^
 //
@@ -41,10 +41,10 @@
 
 import "package:expect/expect.dart";
 
-static const field core::int firstException = invalid-expression "Bad state: 'No element'";
-static const field core::int lastException = invalid-expression "Bad state: 'No element'";
-static const field core::int singleException = invalid-expression "Bad state: 'No element'";
-static const field core::int singleExceptionMulti = invalid-expression "Bad state: 'Too many elements'";
+static const field core::int firstException = invalid-expression "Unhandled core exception: Bad state: No element";
+static const field core::int lastException = invalid-expression "Unhandled core exception: Bad state: No element";
+static const field core::int singleException = invalid-expression "Unhandled core exception: Bad state: No element";
+static const field core::int singleExceptionMulti = invalid-expression "Unhandled core exception: Bad state: Too many elements";
 static const field core::int invalidProperty = invalid-expression "pkg/front_end/testcases/const_functions/const_functions_list_error.dart:36:12: Error: The getter 'invalidProperty' isn't defined for the class 'List<int>'.
  - 'List' is from 'dart:core'.
 Try correcting the name to the name of an existing getter, or defining a getter or field named 'invalidProperty'.
diff --git a/pkg/front_end/testcases/const_functions/const_functions_return.dart b/pkg/front_end/testcases/const_functions/const_functions_return.dart
index 6a2eb8d..661c217 100644
--- a/pkg/front_end/testcases/const_functions/const_functions_return.dart
+++ b/pkg/front_end/testcases/const_functions/const_functions_return.dart
@@ -22,9 +22,19 @@
   return null;
 }
 
+const var5 = fn5();
+int fn5() {
+  try {
+    return throw 1;
+  } on int {
+    return 2;
+  }
+}
+
 void main() {
   Expect.equals((var1 as dynamic), null);
   Expect.equals((var2 as dynamic), null);
   Expect.equals(var3, null);
   Expect.equals(var4, null);
+  Expect.equals(var5, 2);
 }
diff --git a/pkg/front_end/testcases/const_functions/const_functions_return.dart.strong.expect b/pkg/front_end/testcases/const_functions/const_functions_return.dart.strong.expect
index 3253656..4ea38f7 100644
--- a/pkg/front_end/testcases/const_functions/const_functions_return.dart.strong.expect
+++ b/pkg/front_end/testcases/const_functions/const_functions_return.dart.strong.expect
@@ -9,6 +9,7 @@
 static const field void var2 = #C1;
 static const field core::int? var3 = #C1;
 static const field core::int? var4 = #C1;
+static const field core::int var5 = #C2;
 static method fn() → void {}
 static method fn2() → void {
   return;
@@ -18,13 +19,23 @@
 static method fn4() → core::int? {
   return null;
 }
+static method fn5() → core::int {
+  try {
+    return throw 1;
+  }
+  on core::int catch(no-exception-var) {
+    return 2;
+  }
+}
 static method main() → void {
   exp::Expect::equals((#C1) as{ForNonNullableByDefault} dynamic, null);
   exp::Expect::equals((#C1) as{ForNonNullableByDefault} dynamic, null);
   exp::Expect::equals(#C1, null);
   exp::Expect::equals(#C1, null);
+  exp::Expect::equals(#C2, 2);
 }
 
 constants  {
   #C1 = null
+  #C2 = 2
 }
diff --git a/pkg/front_end/testcases/const_functions/const_functions_return.dart.strong.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_return.dart.strong.transformed.expect
index 59de3d1..16743c6 100644
--- a/pkg/front_end/testcases/const_functions/const_functions_return.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/const_functions/const_functions_return.dart.strong.transformed.expect
@@ -9,6 +9,7 @@
 static const field void var2 = #C1;
 static const field core::int? var3 = #C1;
 static const field core::int? var4 = #C1;
+static const field core::int var5 = #C2;
 static method fn() → void {}
 static method fn2() → void {
   return;
@@ -18,18 +19,28 @@
 static method fn4() → core::int? {
   return null;
 }
+static method fn5() → core::int {
+  try {
+    return throw 1;
+  }
+  on core::int catch(no-exception-var) {
+    return 2;
+  }
+}
 static method main() → void {
   exp::Expect::equals((#C1) as{ForNonNullableByDefault} dynamic, null);
   exp::Expect::equals((#C1) as{ForNonNullableByDefault} dynamic, null);
   exp::Expect::equals(#C1, null);
   exp::Expect::equals(#C1, null);
+  exp::Expect::equals(#C2, 2);
 }
 
 constants  {
   #C1 = null
+  #C2 = 2
 }
 
 Extra constant evaluation status:
-Evaluated: AsExpression @ org-dartlang-testcase:///const_functions_return.dart:26:23 -> NullConstant(null)
-Evaluated: AsExpression @ org-dartlang-testcase:///const_functions_return.dart:27:23 -> NullConstant(null)
-Extra constant evaluation: evaluated: 6, effectively constant: 2
+Evaluated: AsExpression @ org-dartlang-testcase:///const_functions_return.dart:35:23 -> NullConstant(null)
+Evaluated: AsExpression @ org-dartlang-testcase:///const_functions_return.dart:36:23 -> NullConstant(null)
+Extra constant evaluation: evaluated: 8, effectively constant: 2
diff --git a/pkg/front_end/testcases/const_functions/const_functions_return.dart.textual_outline.expect b/pkg/front_end/testcases/const_functions/const_functions_return.dart.textual_outline.expect
index cf82ba3..4232e88 100644
--- a/pkg/front_end/testcases/const_functions/const_functions_return.dart.textual_outline.expect
+++ b/pkg/front_end/testcases/const_functions/const_functions_return.dart.textual_outline.expect
@@ -8,4 +8,6 @@
 int? fn3() => null;
 const var4 = fn4();
 int? fn4() {}
+const var5 = fn5();
+int fn5() {}
 void main() {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_return.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/const_functions/const_functions_return.dart.textual_outline_modelled.expect
index 06ba06e..2800612 100644
--- a/pkg/front_end/testcases/const_functions/const_functions_return.dart.textual_outline_modelled.expect
+++ b/pkg/front_end/testcases/const_functions/const_functions_return.dart.textual_outline_modelled.expect
@@ -4,8 +4,10 @@
 const var2 = fn2();
 const var3 = fn3();
 const var4 = fn4();
+const var5 = fn5();
 int? fn3() => null;
 int? fn4() {}
+int fn5() {}
 void fn() {}
 void fn2() {}
 void main() {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_return.dart.weak.expect b/pkg/front_end/testcases/const_functions/const_functions_return.dart.weak.expect
index 3253656..4ea38f7 100644
--- a/pkg/front_end/testcases/const_functions/const_functions_return.dart.weak.expect
+++ b/pkg/front_end/testcases/const_functions/const_functions_return.dart.weak.expect
@@ -9,6 +9,7 @@
 static const field void var2 = #C1;
 static const field core::int? var3 = #C1;
 static const field core::int? var4 = #C1;
+static const field core::int var5 = #C2;
 static method fn() → void {}
 static method fn2() → void {
   return;
@@ -18,13 +19,23 @@
 static method fn4() → core::int? {
   return null;
 }
+static method fn5() → core::int {
+  try {
+    return throw 1;
+  }
+  on core::int catch(no-exception-var) {
+    return 2;
+  }
+}
 static method main() → void {
   exp::Expect::equals((#C1) as{ForNonNullableByDefault} dynamic, null);
   exp::Expect::equals((#C1) as{ForNonNullableByDefault} dynamic, null);
   exp::Expect::equals(#C1, null);
   exp::Expect::equals(#C1, null);
+  exp::Expect::equals(#C2, 2);
 }
 
 constants  {
   #C1 = null
+  #C2 = 2
 }
diff --git a/pkg/front_end/testcases/const_functions/const_functions_return.dart.weak.outline.expect b/pkg/front_end/testcases/const_functions/const_functions_return.dart.weak.outline.expect
index 1587dfd..913960f 100644
--- a/pkg/front_end/testcases/const_functions/const_functions_return.dart.weak.outline.expect
+++ b/pkg/front_end/testcases/const_functions/const_functions_return.dart.weak.outline.expect
@@ -8,6 +8,7 @@
 static const field void var2 = self::fn2();
 static const field core::int? var3 = self::fn3();
 static const field core::int? var4 = self::fn4();
+static const field core::int var5 = self::fn5();
 static method fn() → void
   ;
 static method fn2() → void
@@ -16,5 +17,7 @@
   ;
 static method fn4() → core::int?
   ;
+static method fn5() → core::int
+  ;
 static method main() → void
   ;
diff --git a/pkg/front_end/testcases/const_functions/const_functions_return.dart.weak.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_return.dart.weak.transformed.expect
index 59de3d1..16743c6 100644
--- a/pkg/front_end/testcases/const_functions/const_functions_return.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/const_functions/const_functions_return.dart.weak.transformed.expect
@@ -9,6 +9,7 @@
 static const field void var2 = #C1;
 static const field core::int? var3 = #C1;
 static const field core::int? var4 = #C1;
+static const field core::int var5 = #C2;
 static method fn() → void {}
 static method fn2() → void {
   return;
@@ -18,18 +19,28 @@
 static method fn4() → core::int? {
   return null;
 }
+static method fn5() → core::int {
+  try {
+    return throw 1;
+  }
+  on core::int catch(no-exception-var) {
+    return 2;
+  }
+}
 static method main() → void {
   exp::Expect::equals((#C1) as{ForNonNullableByDefault} dynamic, null);
   exp::Expect::equals((#C1) as{ForNonNullableByDefault} dynamic, null);
   exp::Expect::equals(#C1, null);
   exp::Expect::equals(#C1, null);
+  exp::Expect::equals(#C2, 2);
 }
 
 constants  {
   #C1 = null
+  #C2 = 2
 }
 
 Extra constant evaluation status:
-Evaluated: AsExpression @ org-dartlang-testcase:///const_functions_return.dart:26:23 -> NullConstant(null)
-Evaluated: AsExpression @ org-dartlang-testcase:///const_functions_return.dart:27:23 -> NullConstant(null)
-Extra constant evaluation: evaluated: 6, effectively constant: 2
+Evaluated: AsExpression @ org-dartlang-testcase:///const_functions_return.dart:35:23 -> NullConstant(null)
+Evaluated: AsExpression @ org-dartlang-testcase:///const_functions_return.dart:36:23 -> NullConstant(null)
+Extra constant evaluation: evaluated: 8, effectively constant: 2
diff --git a/pkg/front_end/testcases/dart2js/late_statics.dart.strong.expect b/pkg/front_end/testcases/dart2js/late_statics.dart.strong.expect
index 0a73391..766e7f1 100644
--- a/pkg/front_end/testcases/dart2js/late_statics.dart.strong.expect
+++ b/pkg/front_end/testcases/dart2js/late_statics.dart.strong.expect
@@ -1,7 +1,6 @@
 library /*isNonNullableByDefault*/;
 import self as self;
 import "dart:core" as core;
-import "dart:_internal" as _in;
 import "late_statics_lib.dart" as lat;
 additionalExports = (lat::a,
   lat::a,
@@ -17,17 +16,11 @@
 class Statics extends core::Object {
   late static field core::int a;
   late static final [setter] field core::int b;
-  static field core::int? _#c = null;
-  static field core::int? _#d = null;
+  late static field core::int c = 1.{core::int::unary-}(){() → core::int};
+  late static final field core::int d = 1.{core::int::unary-}(){() → core::int};
   synthetic constructor •() → self::Statics
     : super core::Object::•()
     ;
-  static get c() → core::int
-    return let final core::int? #t1 = self::Statics::_#c in #t1 == null ?{core::int} self::Statics::_#c = 1.{core::int::unary-}(){() → core::int} : #t1{core::int};
-  static set c(core::int #t2) → void
-    self::Statics::_#c = #t2;
-  static get d() → core::int
-    return let final core::int? #t3 = self::Statics::_#d in #t3 == null ?{core::int} let final core::int #t4 = 1.{core::int::unary-}(){() → core::int} in self::Statics::_#d == null ?{core::int} self::Statics::_#d = #t4 : throw new _in::LateError::fieldADI("d") : #t3{core::int};
 }
 static method main() → void {
   self::testUninitializedNonFinalStaticField();
@@ -79,15 +72,8 @@
 library /*isNonNullableByDefault*/;
 import self as lat;
 import "dart:core" as core;
-import "dart:_internal" as _in;
 
 late static field core::int a;
 late static final [setter] field core::int b;
-static field core::int? _#c = null;
-static field core::int? _#d = null;
-static get c() → core::int
-  return let final core::int? #t5 = lat::_#c in #t5 == null ?{core::int} lat::_#c = 1.{core::int::unary-}(){() → core::int} : #t5{core::int};
-static set c(core::int #t6) → void
-  lat::_#c = #t6;
-static get d() → core::int
-  return let final core::int? #t7 = lat::_#d in #t7 == null ?{core::int} let final core::int #t8 = 1.{core::int::unary-}(){() → core::int} in lat::_#d == null ?{core::int} lat::_#d = #t8 : throw new _in::LateError::fieldADI("d") : #t7{core::int};
+late static field core::int c = 1.{core::int::unary-}(){() → core::int};
+late static final field core::int d = 1.{core::int::unary-}(){() → core::int};
diff --git a/pkg/front_end/testcases/dart2js/late_statics.dart.strong.transformed.expect b/pkg/front_end/testcases/dart2js/late_statics.dart.strong.transformed.expect
index f251c18..30448db 100644
--- a/pkg/front_end/testcases/dart2js/late_statics.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/dart2js/late_statics.dart.strong.transformed.expect
@@ -1,7 +1,6 @@
 library /*isNonNullableByDefault*/;
 import self as self;
 import "dart:core" as core;
-import "dart:_internal" as _in;
 import "dart:_late_helper" as _la;
 import "late_statics_lib.dart" as lat;
 additionalExports = (lat::c,
@@ -16,17 +15,11 @@
 class Statics extends core::Object {
   static final field _la::_Cell a = new _la::_Cell::•();
   static final field _la::_Cell b = new _la::_Cell::•();
-  static field core::int? _#c = null;
-  static field core::int? _#d = null;
+  late static field core::int c = 1.{core::int::unary-}(){() → core::int};
+  late static final field core::int d = 1.{core::int::unary-}(){() → core::int};
   synthetic constructor •() → self::Statics
     : super core::Object::•()
     ;
-  static get c() → core::int
-    return let final core::int? #t1 = self::Statics::_#c in #t1 == null ?{core::int} self::Statics::_#c = 1.{core::int::unary-}(){() → core::int} : #t1{core::int};
-  static set c(core::int #t2) → void
-    self::Statics::_#c = #t2;
-  static get d() → core::int
-    return let final core::int? #t3 = self::Statics::_#d in #t3 == null ?{core::int} let final core::int #t4 = 1.{core::int::unary-}(){() → core::int} in self::Statics::_#d == null ?{core::int} self::Statics::_#d = #t4 : throw new _in::LateError::fieldADI("d") : #t3{core::int};
 }
 static method main() → void {
   self::testUninitializedNonFinalStaticField();
@@ -77,27 +70,18 @@
 
 library /*isNonNullableByDefault*/;
 import self as lat;
-import "dart:core" as core;
-import "dart:_internal" as _in;
 import "dart:_late_helper" as _la;
+import "dart:core" as core;
 
 static final field _la::_Cell a = new _la::_Cell::•();
 static final field _la::_Cell b = new _la::_Cell::•();
-static field core::int? _#c = null;
-static field core::int? _#d = null;
-static get c() → core::int
-  return let final core::int? #t5 = lat::_#c in #t5 == null ?{core::int} lat::_#c = 1.{core::int::unary-}(){() → core::int} : #t5{core::int};
-static set c(core::int #t6) → void
-  lat::_#c = #t6;
-static get d() → core::int
-  return let final core::int? #t7 = lat::_#d in #t7 == null ?{core::int} let final core::int #t8 = 1.{core::int::unary-}(){() → core::int} in lat::_#d == null ?{core::int} lat::_#d = #t8 : throw new _in::LateError::fieldADI("d") : #t7{core::int};
+late static field core::int c = 1.{core::int::unary-}(){() → core::int};
+late static final field core::int d = 1.{core::int::unary-}(){() → core::int};
 
 
 Extra constant evaluation status:
 Evaluated: InstanceInvocation @ org-dartlang-testcase:///late_statics.dart:22:23 -> DoubleConstant(-1.0)
 Evaluated: InstanceInvocation @ org-dartlang-testcase:///late_statics.dart:23:29 -> DoubleConstant(-1.0)
-Evaluated: VariableGet @ org-dartlang-testcase:///late_statics.dart:23:25 -> DoubleConstant(-1.0)
 Evaluated: InstanceInvocation @ org-dartlang-testcase:///late_statics_lib.dart:7:14 -> DoubleConstant(-1.0)
 Evaluated: InstanceInvocation @ org-dartlang-testcase:///late_statics_lib.dart:8:20 -> DoubleConstant(-1.0)
-Evaluated: VariableGet @ org-dartlang-testcase:///late_statics_lib.dart:8:16 -> DoubleConstant(-1.0)
-Extra constant evaluation: evaluated: 108, effectively constant: 6
+Extra constant evaluation: evaluated: 62, effectively constant: 4
diff --git a/pkg/front_end/testcases/dart2js/late_statics.dart.weak.expect b/pkg/front_end/testcases/dart2js/late_statics.dart.weak.expect
index c53c058..766e7f1 100644
--- a/pkg/front_end/testcases/dart2js/late_statics.dart.weak.expect
+++ b/pkg/front_end/testcases/dart2js/late_statics.dart.weak.expect
@@ -1,7 +1,6 @@
 library /*isNonNullableByDefault*/;
 import self as self;
 import "dart:core" as core;
-import "dart:_internal" as _in;
 import "late_statics_lib.dart" as lat;
 additionalExports = (lat::a,
   lat::a,
@@ -17,17 +16,11 @@
 class Statics extends core::Object {
   late static field core::int a;
   late static final [setter] field core::int b;
-  static field core::int? _#c = _in::createSentinel<core::int>();
-  static field core::int? _#d = _in::createSentinel<core::int>();
+  late static field core::int c = 1.{core::int::unary-}(){() → core::int};
+  late static final field core::int d = 1.{core::int::unary-}(){() → core::int};
   synthetic constructor •() → self::Statics
     : super core::Object::•()
     ;
-  static get c() → core::int
-    return let final core::int? #t1 = self::Statics::_#c in _in::isSentinel(#t1) ?{core::int} self::Statics::_#c = 1.{core::int::unary-}(){() → core::int} : #t1{core::int};
-  static set c(core::int #t2) → void
-    self::Statics::_#c = #t2;
-  static get d() → core::int
-    return let final core::int #t3 = self::Statics::_#d in _in::isSentinel(#t3) ?{core::int} let final core::int #t4 = 1.{core::int::unary-}(){() → core::int} in _in::isSentinel(self::Statics::_#d) ?{core::int} self::Statics::_#d = #t4 : throw new _in::LateError::fieldADI("d") : #t3;
 }
 static method main() → void {
   self::testUninitializedNonFinalStaticField();
@@ -79,15 +72,8 @@
 library /*isNonNullableByDefault*/;
 import self as lat;
 import "dart:core" as core;
-import "dart:_internal" as _in;
 
 late static field core::int a;
 late static final [setter] field core::int b;
-static field core::int? _#c = _in::createSentinel<core::int>();
-static field core::int? _#d = _in::createSentinel<core::int>();
-static get c() → core::int
-  return let final core::int? #t5 = lat::_#c in _in::isSentinel(#t5) ?{core::int} lat::_#c = 1.{core::int::unary-}(){() → core::int} : #t5{core::int};
-static set c(core::int #t6) → void
-  lat::_#c = #t6;
-static get d() → core::int
-  return let final core::int #t7 = lat::_#d in _in::isSentinel(#t7) ?{core::int} let final core::int #t8 = 1.{core::int::unary-}(){() → core::int} in _in::isSentinel(lat::_#d) ?{core::int} lat::_#d = #t8 : throw new _in::LateError::fieldADI("d") : #t7;
+late static field core::int c = 1.{core::int::unary-}(){() → core::int};
+late static final field core::int d = 1.{core::int::unary-}(){() → core::int};
diff --git a/pkg/front_end/testcases/dart2js/late_statics.dart.weak.outline.expect b/pkg/front_end/testcases/dart2js/late_statics.dart.weak.outline.expect
index 21ef816..76913dc 100644
--- a/pkg/front_end/testcases/dart2js/late_statics.dart.weak.outline.expect
+++ b/pkg/front_end/testcases/dart2js/late_statics.dart.weak.outline.expect
@@ -16,13 +16,10 @@
 class Statics extends core::Object {
   late static field core::int a;
   late static final [setter] field core::int b;
-  static field core::int? _#c;
-  static field core::int? _#d;
+  late static field core::int c;
+  late static final field core::int d;
   synthetic constructor •() → self::Statics
     ;
-  static get c() → core::int;
-  static set c(core::int #t1) → void;
-  static get d() → core::int;
 }
 static method main() → void
   ;
@@ -49,8 +46,5 @@
 
 late static field core::int a;
 late static final [setter] field core::int b;
-static field core::int? _#c;
-static field core::int? _#d;
-static get c() → core::int;
-static set c(core::int #t2) → void;
-static get d() → core::int;
+late static field core::int c;
+late static final field core::int d;
diff --git a/pkg/front_end/testcases/dart2js/late_statics.dart.weak.transformed.expect b/pkg/front_end/testcases/dart2js/late_statics.dart.weak.transformed.expect
index 3806163..30448db 100644
--- a/pkg/front_end/testcases/dart2js/late_statics.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/dart2js/late_statics.dart.weak.transformed.expect
@@ -1,7 +1,6 @@
 library /*isNonNullableByDefault*/;
 import self as self;
 import "dart:core" as core;
-import "dart:_internal" as _in;
 import "dart:_late_helper" as _la;
 import "late_statics_lib.dart" as lat;
 additionalExports = (lat::c,
@@ -16,17 +15,11 @@
 class Statics extends core::Object {
   static final field _la::_Cell a = new _la::_Cell::•();
   static final field _la::_Cell b = new _la::_Cell::•();
-  static field core::int? _#c = _in::createSentinel<core::int>();
-  static field core::int? _#d = _in::createSentinel<core::int>();
+  late static field core::int c = 1.{core::int::unary-}(){() → core::int};
+  late static final field core::int d = 1.{core::int::unary-}(){() → core::int};
   synthetic constructor •() → self::Statics
     : super core::Object::•()
     ;
-  static get c() → core::int
-    return let final core::int? #t1 = self::Statics::_#c in _in::isSentinel(#t1) ?{core::int} self::Statics::_#c = 1.{core::int::unary-}(){() → core::int} : #t1{core::int};
-  static set c(core::int #t2) → void
-    self::Statics::_#c = #t2;
-  static get d() → core::int
-    return let final core::int #t3 = self::Statics::_#d in _in::isSentinel(#t3) ?{core::int} let final core::int #t4 = 1.{core::int::unary-}(){() → core::int} in _in::isSentinel(self::Statics::_#d) ?{core::int} self::Statics::_#d = #t4 : throw new _in::LateError::fieldADI("d") : #t3;
 }
 static method main() → void {
   self::testUninitializedNonFinalStaticField();
@@ -77,27 +70,18 @@
 
 library /*isNonNullableByDefault*/;
 import self as lat;
-import "dart:core" as core;
-import "dart:_internal" as _in;
 import "dart:_late_helper" as _la;
+import "dart:core" as core;
 
 static final field _la::_Cell a = new _la::_Cell::•();
 static final field _la::_Cell b = new _la::_Cell::•();
-static field core::int? _#c = _in::createSentinel<core::int>();
-static field core::int? _#d = _in::createSentinel<core::int>();
-static get c() → core::int
-  return let final core::int? #t5 = lat::_#c in _in::isSentinel(#t5) ?{core::int} lat::_#c = 1.{core::int::unary-}(){() → core::int} : #t5{core::int};
-static set c(core::int #t6) → void
-  lat::_#c = #t6;
-static get d() → core::int
-  return let final core::int #t7 = lat::_#d in _in::isSentinel(#t7) ?{core::int} let final core::int #t8 = 1.{core::int::unary-}(){() → core::int} in _in::isSentinel(lat::_#d) ?{core::int} lat::_#d = #t8 : throw new _in::LateError::fieldADI("d") : #t7;
+late static field core::int c = 1.{core::int::unary-}(){() → core::int};
+late static final field core::int d = 1.{core::int::unary-}(){() → core::int};
 
 
 Extra constant evaluation status:
 Evaluated: InstanceInvocation @ org-dartlang-testcase:///late_statics.dart:22:23 -> DoubleConstant(-1.0)
 Evaluated: InstanceInvocation @ org-dartlang-testcase:///late_statics.dart:23:29 -> DoubleConstant(-1.0)
-Evaluated: VariableGet @ org-dartlang-testcase:///late_statics.dart:23:25 -> DoubleConstant(-1.0)
 Evaluated: InstanceInvocation @ org-dartlang-testcase:///late_statics_lib.dart:7:14 -> DoubleConstant(-1.0)
 Evaluated: InstanceInvocation @ org-dartlang-testcase:///late_statics_lib.dart:8:20 -> DoubleConstant(-1.0)
-Evaluated: VariableGet @ org-dartlang-testcase:///late_statics_lib.dart:8:16 -> DoubleConstant(-1.0)
-Extra constant evaluation: evaluated: 112, effectively constant: 6
+Extra constant evaluation: evaluated: 62, effectively constant: 4
diff --git a/pkg/front_end/testcases/general/issue45598.dart b/pkg/front_end/testcases/general/issue45598.dart
new file mode 100644
index 0000000..ad18575
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45598.dart
@@ -0,0 +1,11 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart=2.9
+
+foo(Function<X extends Z, Y, Z>({Map<Y, Z> m}) bar, Map<String, String> m) {
+  bar(m: m);
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/general/issue45598.dart.textual_outline.expect b/pkg/front_end/testcases/general/issue45598.dart.textual_outline.expect
new file mode 100644
index 0000000..734e138
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45598.dart.textual_outline.expect
@@ -0,0 +1,3 @@
+// @dart = 2.9
+foo(Function<X extends Z, Y, Z>({Map<Y, Z> m}) bar, Map<String, String> m) {}
+main() {}
diff --git a/pkg/front_end/testcases/general/issue45598.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/general/issue45598.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..734e138
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45598.dart.textual_outline_modelled.expect
@@ -0,0 +1,3 @@
+// @dart = 2.9
+foo(Function<X extends Z, Y, Z>({Map<Y, Z> m}) bar, Map<String, String> m) {}
+main() {}
diff --git a/pkg/front_end/testcases/general/issue45598.dart.weak.expect b/pkg/front_end/testcases/general/issue45598.dart.weak.expect
new file mode 100644
index 0000000..fc68d7b
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45598.dart.weak.expect
@@ -0,0 +1,8 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+static method foo(<X extends Z* = dynamic, Y extends core::Object* = dynamic, Z extends core::Object* = dynamic>({m: core::Map<Y*, Z*>*}) →* dynamic bar, core::Map<core::String*, core::String*>* m) → dynamic {
+  bar.call<core::String*, core::String*, core::String*>(m: m);
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/general/issue45598.dart.weak.outline.expect b/pkg/front_end/testcases/general/issue45598.dart.weak.outline.expect
new file mode 100644
index 0000000..829a44c
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45598.dart.weak.outline.expect
@@ -0,0 +1,8 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+static method foo(<X extends Z* = dynamic, Y extends core::Object* = dynamic, Z extends core::Object* = dynamic>({m: core::Map<Y*, Z*>*}) →* dynamic bar, core::Map<core::String*, core::String*>* m) → dynamic
+  ;
+static method main() → dynamic
+  ;
diff --git a/pkg/front_end/testcases/general/issue45598.dart.weak.transformed.expect b/pkg/front_end/testcases/general/issue45598.dart.weak.transformed.expect
new file mode 100644
index 0000000..fc68d7b
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45598.dart.weak.transformed.expect
@@ -0,0 +1,8 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+static method foo(<X extends Z* = dynamic, Y extends core::Object* = dynamic, Z extends core::Object* = dynamic>({m: core::Map<Y*, Z*>*}) →* dynamic bar, core::Map<core::String*, core::String*>* m) → dynamic {
+  bar.call<core::String*, core::String*, core::String*>(m: m);
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/general/issue45598_2.dart b/pkg/front_end/testcases/general/issue45598_2.dart
new file mode 100644
index 0000000..61a731e
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45598_2.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart=2.9
+
+foo(Map<String, String> m) {
+  void bar<X extends Z, Y, Z>({Map<Y, Z> m}) {}
+  bar(m: m);
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/general/issue45598_2.dart.textual_outline.expect b/pkg/front_end/testcases/general/issue45598_2.dart.textual_outline.expect
new file mode 100644
index 0000000..983b1d0
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45598_2.dart.textual_outline.expect
@@ -0,0 +1,3 @@
+// @dart = 2.9
+foo(Map<String, String> m) {}
+main() {}
diff --git a/pkg/front_end/testcases/general/issue45598_2.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/general/issue45598_2.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..983b1d0
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45598_2.dart.textual_outline_modelled.expect
@@ -0,0 +1,3 @@
+// @dart = 2.9
+foo(Map<String, String> m) {}
+main() {}
diff --git a/pkg/front_end/testcases/general/issue45598_2.dart.weak.expect b/pkg/front_end/testcases/general/issue45598_2.dart.weak.expect
new file mode 100644
index 0000000..d8d1fd0
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45598_2.dart.weak.expect
@@ -0,0 +1,13 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+static method foo(core::Map<core::String*, core::String*>* m) → dynamic {
+  function bar<X extends Z* = dynamic, Y extends core::Object* = dynamic, Z extends core::Object* = dynamic>({core::Map<Y*, Z*>* m = #C1}) → void {}
+  bar.call<core::String*, core::String*, core::String*>(m: m);
+}
+static method main() → dynamic {}
+
+constants  {
+  #C1 = null
+}
diff --git a/pkg/front_end/testcases/general/issue45598_2.dart.weak.outline.expect b/pkg/front_end/testcases/general/issue45598_2.dart.weak.outline.expect
new file mode 100644
index 0000000..9efffa9a
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45598_2.dart.weak.outline.expect
@@ -0,0 +1,8 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+static method foo(core::Map<core::String*, core::String*>* m) → dynamic
+  ;
+static method main() → dynamic
+  ;
diff --git a/pkg/front_end/testcases/general/issue45598_2.dart.weak.transformed.expect b/pkg/front_end/testcases/general/issue45598_2.dart.weak.transformed.expect
new file mode 100644
index 0000000..d8d1fd0
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45598_2.dart.weak.transformed.expect
@@ -0,0 +1,13 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+static method foo(core::Map<core::String*, core::String*>* m) → dynamic {
+  function bar<X extends Z* = dynamic, Y extends core::Object* = dynamic, Z extends core::Object* = dynamic>({core::Map<Y*, Z*>* m = #C1}) → void {}
+  bar.call<core::String*, core::String*, core::String*>(m: m);
+}
+static method main() → dynamic {}
+
+constants  {
+  #C1 = null
+}
diff --git a/pkg/front_end/testcases/general/issue45660.dart b/pkg/front_end/testcases/general/issue45660.dart
new file mode 100644
index 0000000..9850a81
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45660.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart=2.9
+
+T Function<T extends num>(T) extendsNumReturnArg = <S extends num>(S s) => s;
+
+functionInvocations() {
+  extendsNumReturnArg/*<Null>*/(null);
+  extendsNumReturnArg/*<String>*/("");
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/general/issue45660.dart.textual_outline.expect b/pkg/front_end/testcases/general/issue45660.dart.textual_outline.expect
new file mode 100644
index 0000000..377eb18
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45660.dart.textual_outline.expect
@@ -0,0 +1,4 @@
+// @dart = 2.9
+T Function<T extends num>(T) extendsNumReturnArg = <S extends num>(S s) => s;
+functionInvocations() {}
+main() {}
diff --git a/pkg/front_end/testcases/general/issue45660.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/general/issue45660.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..377eb18
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45660.dart.textual_outline_modelled.expect
@@ -0,0 +1,4 @@
+// @dart = 2.9
+T Function<T extends num>(T) extendsNumReturnArg = <S extends num>(S s) => s;
+functionInvocations() {}
+main() {}
diff --git a/pkg/front_end/testcases/general/issue45660.dart.weak.expect b/pkg/front_end/testcases/general/issue45660.dart.weak.expect
new file mode 100644
index 0000000..cd197e9
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45660.dart.weak.expect
@@ -0,0 +1,18 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/general/issue45660.dart:11:22: Error: Inferred type argument 'String' doesn't conform to the bound 'num' of the type variable 'T' on 'call'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+//   extendsNumReturnArg/*<String>*/("");
+//                      ^
+//
+import self as self;
+import "dart:core" as core;
+
+static field <T extends core::num* = dynamic>(T*) →* T* extendsNumReturnArg = <S extends core::num* = core::num*>(S* s) → S* => s;
+static method functionInvocations() → dynamic {
+  self::extendsNumReturnArg.call<Null>(null);
+  self::extendsNumReturnArg.call<core::String*>("");
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/general/issue45660.dart.weak.outline.expect b/pkg/front_end/testcases/general/issue45660.dart.weak.outline.expect
new file mode 100644
index 0000000..db6f621
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45660.dart.weak.outline.expect
@@ -0,0 +1,9 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+static field <T extends core::num* = dynamic>(T*) →* T* extendsNumReturnArg;
+static method functionInvocations() → dynamic
+  ;
+static method main() → dynamic
+  ;
diff --git a/pkg/front_end/testcases/general/issue45660.dart.weak.transformed.expect b/pkg/front_end/testcases/general/issue45660.dart.weak.transformed.expect
new file mode 100644
index 0000000..cd197e9
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45660.dart.weak.transformed.expect
@@ -0,0 +1,18 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/general/issue45660.dart:11:22: Error: Inferred type argument 'String' doesn't conform to the bound 'num' of the type variable 'T' on 'call'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+//   extendsNumReturnArg/*<String>*/("");
+//                      ^
+//
+import self as self;
+import "dart:core" as core;
+
+static field <T extends core::num* = dynamic>(T*) →* T* extendsNumReturnArg = <S extends core::num* = core::num*>(S* s) → S* => s;
+static method functionInvocations() → dynamic {
+  self::extendsNumReturnArg.call<Null>(null);
+  self::extendsNumReturnArg.call<core::String*>("");
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.expect
index d997209..b92f5af 100644
--- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.expect
@@ -25,4 +25,4 @@
 
 Constructor coverage from constants:
 org-dartlang-testcase:///ffi_struct_inline_array.dart:
-- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:134:9)
+- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:135:9)
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.transformed.expect
index 49368b2..8b2f96c 100644
--- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.transformed.expect
@@ -48,4 +48,4 @@
 
 Constructor coverage from constants:
 org-dartlang-testcase:///ffi_struct_inline_array.dart:
-- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:134:9)
+- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:135:9)
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.expect
index f04b75d..792f6e0 100644
--- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.expect
@@ -25,4 +25,4 @@
 
 Constructor coverage from constants:
 org-dartlang-testcase:///ffi_struct_inline_array.dart:
-- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:134:9)
+- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:135:9)
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.transformed.expect
index 0164237..4d17ccf 100644
--- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.transformed.expect
@@ -48,4 +48,4 @@
 
 Constructor coverage from constants:
 org-dartlang-testcase:///ffi_struct_inline_array.dart:
-- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:134:9)
+- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:135:9)
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.expect
index 391f4bc..b4d5461 100644
--- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.expect
@@ -33,4 +33,4 @@
 
 Constructor coverage from constants:
 org-dartlang-testcase:///ffi_struct_inline_array_multi_dimensional.dart:
-- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:134:9)
+- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:135:9)
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.transformed.expect
index 6b9936f..5b4a08c 100644
--- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.transformed.expect
@@ -80,4 +80,4 @@
 
 Constructor coverage from constants:
 org-dartlang-testcase:///ffi_struct_inline_array_multi_dimensional.dart:
-- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:134:9)
+- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:135:9)
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.expect
index 32510af..03a5224 100644
--- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.expect
@@ -33,4 +33,4 @@
 
 Constructor coverage from constants:
 org-dartlang-testcase:///ffi_struct_inline_array_multi_dimensional.dart:
-- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:134:9)
+- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:135:9)
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.transformed.expect
index cff35ac..e502690 100644
--- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.transformed.expect
@@ -80,4 +80,4 @@
 
 Constructor coverage from constants:
 org-dartlang-testcase:///ffi_struct_inline_array_multi_dimensional.dart:
-- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:134:9)
+- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:135:9)
diff --git a/pkg/front_end/testcases/nnbd/issue45598.dart b/pkg/front_end/testcases/nnbd/issue45598.dart
new file mode 100644
index 0000000..8c8267e
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue45598.dart
@@ -0,0 +1,9 @@
+// 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.
+
+foo(Function<X extends Z, Y, Z>({Map<Y, Z> m}) bar, Map<String, String> m) {
+  bar(m: m);
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/nnbd/issue45598.dart.strong.expect b/pkg/front_end/testcases/nnbd/issue45598.dart.strong.expect
new file mode 100644
index 0000000..a1055bf
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue45598.dart.strong.expect
@@ -0,0 +1,8 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static method foo(<X extends Z% = dynamic, Y extends core::Object? = dynamic, Z extends core::Object? = dynamic>({m: core::Map<Y%, Z%>}) → dynamic bar, core::Map<core::String, core::String> m) → dynamic {
+  bar.call<core::String, core::String, core::String>(m: m);
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/issue45598.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/issue45598.dart.strong.transformed.expect
new file mode 100644
index 0000000..a1055bf
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue45598.dart.strong.transformed.expect
@@ -0,0 +1,8 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static method foo(<X extends Z% = dynamic, Y extends core::Object? = dynamic, Z extends core::Object? = dynamic>({m: core::Map<Y%, Z%>}) → dynamic bar, core::Map<core::String, core::String> m) → dynamic {
+  bar.call<core::String, core::String, core::String>(m: m);
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/issue45598.dart.textual_outline.expect b/pkg/front_end/testcases/nnbd/issue45598.dart.textual_outline.expect
new file mode 100644
index 0000000..50b6ae2
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue45598.dart.textual_outline.expect
@@ -0,0 +1,2 @@
+foo(Function<X extends Z, Y, Z>({Map<Y, Z> m}) bar, Map<String, String> m) {}
+main() {}
diff --git a/pkg/front_end/testcases/nnbd/issue45598.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/nnbd/issue45598.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..50b6ae2
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue45598.dart.textual_outline_modelled.expect
@@ -0,0 +1,2 @@
+foo(Function<X extends Z, Y, Z>({Map<Y, Z> m}) bar, Map<String, String> m) {}
+main() {}
diff --git a/pkg/front_end/testcases/nnbd/issue45598.dart.weak.expect b/pkg/front_end/testcases/nnbd/issue45598.dart.weak.expect
new file mode 100644
index 0000000..a1055bf
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue45598.dart.weak.expect
@@ -0,0 +1,8 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static method foo(<X extends Z% = dynamic, Y extends core::Object? = dynamic, Z extends core::Object? = dynamic>({m: core::Map<Y%, Z%>}) → dynamic bar, core::Map<core::String, core::String> m) → dynamic {
+  bar.call<core::String, core::String, core::String>(m: m);
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/issue45598.dart.weak.outline.expect b/pkg/front_end/testcases/nnbd/issue45598.dart.weak.outline.expect
new file mode 100644
index 0000000..9ad166e
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue45598.dart.weak.outline.expect
@@ -0,0 +1,8 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static method foo(<X extends Z% = dynamic, Y extends core::Object? = dynamic, Z extends core::Object? = dynamic>({m: core::Map<Y%, Z%>}) → dynamic bar, core::Map<core::String, core::String> m) → dynamic
+  ;
+static method main() → dynamic
+  ;
diff --git a/pkg/front_end/testcases/nnbd/issue45598.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/issue45598.dart.weak.transformed.expect
new file mode 100644
index 0000000..a1055bf
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue45598.dart.weak.transformed.expect
@@ -0,0 +1,8 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static method foo(<X extends Z% = dynamic, Y extends core::Object? = dynamic, Z extends core::Object? = dynamic>({m: core::Map<Y%, Z%>}) → dynamic bar, core::Map<core::String, core::String> m) → dynamic {
+  bar.call<core::String, core::String, core::String>(m: m);
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/issue45598_2.dart b/pkg/front_end/testcases/nnbd/issue45598_2.dart
new file mode 100644
index 0000000..49350a9
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue45598_2.dart
@@ -0,0 +1,10 @@
+// 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.
+
+foo(Map<String, String> m) {
+  void bar<X extends Z, Y, Z>({required Map<Y, Z> m}) {}
+  bar(m: m);
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/nnbd/issue45598_2.dart.strong.expect b/pkg/front_end/testcases/nnbd/issue45598_2.dart.strong.expect
new file mode 100644
index 0000000..9c145d3
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue45598_2.dart.strong.expect
@@ -0,0 +1,13 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static method foo(core::Map<core::String, core::String> m) → dynamic {
+  function bar<X extends Z% = dynamic, Y extends core::Object? = dynamic, Z extends core::Object? = dynamic>({required core::Map<Y%, Z%> m = #C1}) → void {}
+  bar.call<core::String, core::String, core::String>(m: m);
+}
+static method main() → dynamic {}
+
+constants  {
+  #C1 = null
+}
diff --git a/pkg/front_end/testcases/nnbd/issue45598_2.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/issue45598_2.dart.strong.transformed.expect
new file mode 100644
index 0000000..9c145d3
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue45598_2.dart.strong.transformed.expect
@@ -0,0 +1,13 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static method foo(core::Map<core::String, core::String> m) → dynamic {
+  function bar<X extends Z% = dynamic, Y extends core::Object? = dynamic, Z extends core::Object? = dynamic>({required core::Map<Y%, Z%> m = #C1}) → void {}
+  bar.call<core::String, core::String, core::String>(m: m);
+}
+static method main() → dynamic {}
+
+constants  {
+  #C1 = null
+}
diff --git a/pkg/front_end/testcases/nnbd/issue45598_2.dart.textual_outline.expect b/pkg/front_end/testcases/nnbd/issue45598_2.dart.textual_outline.expect
new file mode 100644
index 0000000..853274f
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue45598_2.dart.textual_outline.expect
@@ -0,0 +1,2 @@
+foo(Map<String, String> m) {}
+main() {}
diff --git a/pkg/front_end/testcases/nnbd/issue45598_2.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/nnbd/issue45598_2.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..853274f
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue45598_2.dart.textual_outline_modelled.expect
@@ -0,0 +1,2 @@
+foo(Map<String, String> m) {}
+main() {}
diff --git a/pkg/front_end/testcases/nnbd/issue45598_2.dart.weak.expect b/pkg/front_end/testcases/nnbd/issue45598_2.dart.weak.expect
new file mode 100644
index 0000000..9c145d3
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue45598_2.dart.weak.expect
@@ -0,0 +1,13 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static method foo(core::Map<core::String, core::String> m) → dynamic {
+  function bar<X extends Z% = dynamic, Y extends core::Object? = dynamic, Z extends core::Object? = dynamic>({required core::Map<Y%, Z%> m = #C1}) → void {}
+  bar.call<core::String, core::String, core::String>(m: m);
+}
+static method main() → dynamic {}
+
+constants  {
+  #C1 = null
+}
diff --git a/pkg/front_end/testcases/nnbd/issue45598_2.dart.weak.outline.expect b/pkg/front_end/testcases/nnbd/issue45598_2.dart.weak.outline.expect
new file mode 100644
index 0000000..1e7263b
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue45598_2.dart.weak.outline.expect
@@ -0,0 +1,8 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static method foo(core::Map<core::String, core::String> m) → dynamic
+  ;
+static method main() → dynamic
+  ;
diff --git a/pkg/front_end/testcases/nnbd/issue45598_2.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/issue45598_2.dart.weak.transformed.expect
new file mode 100644
index 0000000..9c145d3
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue45598_2.dart.weak.transformed.expect
@@ -0,0 +1,13 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static method foo(core::Map<core::String, core::String> m) → dynamic {
+  function bar<X extends Z% = dynamic, Y extends core::Object? = dynamic, Z extends core::Object? = dynamic>({required core::Map<Y%, Z%> m = #C1}) → void {}
+  bar.call<core::String, core::String, core::String>(m: m);
+}
+static method main() → dynamic {}
+
+constants  {
+  #C1 = null
+}
diff --git a/pkg/front_end/testcases/nnbd_mixed/mock_http_headers.dart.weak.outline.expect b/pkg/front_end/testcases/nnbd_mixed/mock_http_headers.dart.weak.outline.expect
index e3bc4c3..e9f1bbe 100644
--- a/pkg/front_end/testcases/nnbd_mixed/mock_http_headers.dart.weak.outline.expect
+++ b/pkg/front_end/testcases/nnbd_mixed/mock_http_headers.dart.weak.outline.expect
@@ -170,8 +170,8 @@
 Evaluated: SymbolLiteral @ org-dartlang-testcase:///mock_http_headers.dart:13:7 -> SymbolConstant(#noFolding)
 Evaluated: ListLiteral @ org-dartlang-testcase:///mock_http_headers.dart:13:7 -> ListConstant(const <Type*>[])
 Evaluated: MapLiteral @ org-dartlang-testcase:///mock_http_headers.dart:13:7 -> InstanceConstant(const _ImmutableMap<Symbol*, dynamic>{_ImmutableMap._kvPairs: const <dynamic>[]})
-Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:761:8 -> SymbolConstant(#clear)
-Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:761:8 -> ListConstant(const <Type*>[])
-Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:761:8 -> ListConstant(const <dynamic>[])
-Evaluated: MapLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:761:8 -> InstanceConstant(const _ImmutableMap<Symbol*, dynamic>{_ImmutableMap._kvPairs: const <dynamic>[]})
+Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:762:8 -> SymbolConstant(#clear)
+Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:762:8 -> ListConstant(const <Type*>[])
+Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:762:8 -> ListConstant(const <dynamic>[])
+Evaluated: MapLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:762:8 -> InstanceConstant(const _ImmutableMap<Symbol*, dynamic>{_ImmutableMap._kvPairs: const <dynamic>[]})
 Extra constant evaluation: evaluated: 268, effectively constant: 91
diff --git a/pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart b/pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart
new file mode 100644
index 0000000..ae2b59d
--- /dev/null
+++ b/pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart
@@ -0,0 +1,22 @@
+// 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 'issue45626.dart' as self;
+
+class C {}
+typedef CAlias = C;
+
+class D implements C, C {}
+class D2 implements C, CAlias {}
+class D3 implements CAlias, C {}
+class D4 implements C, self.C {}
+class D5 implements self.C, C {}
+
+mixin CM on C, C {}
+mixin CM2 on C, CAlias {}
+mixin CM3 on CAlias, C {}
+mixin CM4 on self.C, C {}
+mixin CM5 on C, self.C {}
+
+main() {}
diff --git a/pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart.strong.expect b/pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart.strong.expect
new file mode 100644
index 0000000..84c660a
--- /dev/null
+++ b/pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart.strong.expect
@@ -0,0 +1,126 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:10:23: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// class D implements C, C {}
+//                       ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:11:24: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// class D2 implements C, CAlias {}
+//                        ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:12:29: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// class D3 implements CAlias, C {}
+//                             ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:13:24: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// class D4 implements C, self.C {}
+//                        ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:14:29: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// class D5 implements self.C, C {}
+//                             ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:16:16: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// mixin CM on C, C {}
+//                ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:17:17: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// mixin CM2 on C, CAlias {}
+//                 ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:18:22: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// mixin CM3 on CAlias, C {}
+//                      ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:19:22: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// mixin CM4 on self.C, C {}
+//                      ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:20:17: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// mixin CM5 on C, self.C {}
+//                 ^
+//
+import self as self;
+import "dart:core" as core;
+
+import "org-dartlang-testcase:///issue45626.dart" as self;
+
+typedef CAlias = self::C;
+class C extends core::Object {
+  synthetic constructor •() → self::C
+    : super core::Object::•()
+    ;
+}
+class D extends core::Object implements self::C, self::C {
+  synthetic constructor •() → self::D
+    : super core::Object::•()
+    ;
+}
+class D2 extends core::Object implements self::C, self::C {
+  synthetic constructor •() → self::D2
+    : super core::Object::•()
+    ;
+}
+class D3 extends core::Object implements self::C, self::C {
+  synthetic constructor •() → self::D3
+    : super core::Object::•()
+    ;
+}
+class D4 extends core::Object implements self::C, self::C {
+  synthetic constructor •() → self::D4
+    : super core::Object::•()
+    ;
+}
+class D5 extends core::Object implements self::C, self::C {
+  synthetic constructor •() → self::D5
+    : super core::Object::•()
+    ;
+}
+abstract class _CM&C&C extends core::Object implements self::C, self::C /*isAnonymousMixin*/  {
+  synthetic constructor •() → self::_CM&C&C
+    : super core::Object::•()
+    ;
+}
+abstract class CM extends self::_CM&C&C /*isMixinDeclaration*/  {
+}
+abstract class _CM2&C&CAlias extends core::Object implements self::C, self::C /*isAnonymousMixin*/  {
+  synthetic constructor •() → self::_CM2&C&CAlias
+    : super core::Object::•()
+    ;
+}
+abstract class CM2 extends self::_CM2&C&CAlias /*isMixinDeclaration*/  {
+}
+abstract class _CM3&CAlias&C extends core::Object implements self::C, self::C /*isAnonymousMixin*/  {
+  synthetic constructor •() → self::_CM3&CAlias&C
+    : super core::Object::•()
+    ;
+}
+abstract class CM3 extends self::_CM3&CAlias&C /*isMixinDeclaration*/  {
+}
+abstract class _CM4&C&C extends core::Object implements self::C, self::C /*isAnonymousMixin*/  {
+  synthetic constructor •() → self::_CM4&C&C
+    : super core::Object::•()
+    ;
+}
+abstract class CM4 extends self::_CM4&C&C /*isMixinDeclaration*/  {
+}
+abstract class _CM5&C&C extends core::Object implements self::C, self::C /*isAnonymousMixin*/  {
+  synthetic constructor •() → self::_CM5&C&C
+    : super core::Object::•()
+    ;
+}
+abstract class CM5 extends self::_CM5&C&C /*isMixinDeclaration*/  {
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart.strong.transformed.expect b/pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart.strong.transformed.expect
new file mode 100644
index 0000000..84c660a
--- /dev/null
+++ b/pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart.strong.transformed.expect
@@ -0,0 +1,126 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:10:23: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// class D implements C, C {}
+//                       ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:11:24: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// class D2 implements C, CAlias {}
+//                        ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:12:29: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// class D3 implements CAlias, C {}
+//                             ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:13:24: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// class D4 implements C, self.C {}
+//                        ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:14:29: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// class D5 implements self.C, C {}
+//                             ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:16:16: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// mixin CM on C, C {}
+//                ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:17:17: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// mixin CM2 on C, CAlias {}
+//                 ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:18:22: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// mixin CM3 on CAlias, C {}
+//                      ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:19:22: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// mixin CM4 on self.C, C {}
+//                      ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:20:17: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// mixin CM5 on C, self.C {}
+//                 ^
+//
+import self as self;
+import "dart:core" as core;
+
+import "org-dartlang-testcase:///issue45626.dart" as self;
+
+typedef CAlias = self::C;
+class C extends core::Object {
+  synthetic constructor •() → self::C
+    : super core::Object::•()
+    ;
+}
+class D extends core::Object implements self::C, self::C {
+  synthetic constructor •() → self::D
+    : super core::Object::•()
+    ;
+}
+class D2 extends core::Object implements self::C, self::C {
+  synthetic constructor •() → self::D2
+    : super core::Object::•()
+    ;
+}
+class D3 extends core::Object implements self::C, self::C {
+  synthetic constructor •() → self::D3
+    : super core::Object::•()
+    ;
+}
+class D4 extends core::Object implements self::C, self::C {
+  synthetic constructor •() → self::D4
+    : super core::Object::•()
+    ;
+}
+class D5 extends core::Object implements self::C, self::C {
+  synthetic constructor •() → self::D5
+    : super core::Object::•()
+    ;
+}
+abstract class _CM&C&C extends core::Object implements self::C, self::C /*isAnonymousMixin*/  {
+  synthetic constructor •() → self::_CM&C&C
+    : super core::Object::•()
+    ;
+}
+abstract class CM extends self::_CM&C&C /*isMixinDeclaration*/  {
+}
+abstract class _CM2&C&CAlias extends core::Object implements self::C, self::C /*isAnonymousMixin*/  {
+  synthetic constructor •() → self::_CM2&C&CAlias
+    : super core::Object::•()
+    ;
+}
+abstract class CM2 extends self::_CM2&C&CAlias /*isMixinDeclaration*/  {
+}
+abstract class _CM3&CAlias&C extends core::Object implements self::C, self::C /*isAnonymousMixin*/  {
+  synthetic constructor •() → self::_CM3&CAlias&C
+    : super core::Object::•()
+    ;
+}
+abstract class CM3 extends self::_CM3&CAlias&C /*isMixinDeclaration*/  {
+}
+abstract class _CM4&C&C extends core::Object implements self::C, self::C /*isAnonymousMixin*/  {
+  synthetic constructor •() → self::_CM4&C&C
+    : super core::Object::•()
+    ;
+}
+abstract class CM4 extends self::_CM4&C&C /*isMixinDeclaration*/  {
+}
+abstract class _CM5&C&C extends core::Object implements self::C, self::C /*isAnonymousMixin*/  {
+  synthetic constructor •() → self::_CM5&C&C
+    : super core::Object::•()
+    ;
+}
+abstract class CM5 extends self::_CM5&C&C /*isMixinDeclaration*/  {
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart.textual_outline.expect b/pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart.textual_outline.expect
new file mode 100644
index 0000000..9083f89
--- /dev/null
+++ b/pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart.textual_outline.expect
@@ -0,0 +1,22 @@
+import 'issue45626.dart' as self;
+
+class C {}
+
+typedef CAlias = C;
+
+class D implements C, C {}
+
+class D2 implements C, CAlias {}
+
+class D3 implements CAlias, C {}
+
+class D4 implements C, self.C {}
+
+class D5 implements self.C, C {}
+
+mixin CM on C, C {}
+mixin CM2 on C, CAlias {}
+mixin CM3 on CAlias, C {}
+mixin CM4 on self.C, C {}
+mixin CM5 on C, self.C {}
+main() {}
diff --git a/pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..2c192dd
--- /dev/null
+++ b/pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart.textual_outline_modelled.expect
@@ -0,0 +1,21 @@
+import 'issue45626.dart' as self;
+
+class C {}
+
+class D implements C, C {}
+
+class D2 implements C, CAlias {}
+
+class D3 implements CAlias, C {}
+
+class D4 implements C, self.C {}
+
+class D5 implements self.C, C {}
+
+main() {}
+mixin CM on C, C {}
+mixin CM2 on C, CAlias {}
+mixin CM3 on CAlias, C {}
+mixin CM4 on self.C, C {}
+mixin CM5 on C, self.C {}
+typedef CAlias = C;
diff --git a/pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart.weak.expect b/pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart.weak.expect
new file mode 100644
index 0000000..84c660a
--- /dev/null
+++ b/pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart.weak.expect
@@ -0,0 +1,126 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:10:23: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// class D implements C, C {}
+//                       ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:11:24: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// class D2 implements C, CAlias {}
+//                        ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:12:29: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// class D3 implements CAlias, C {}
+//                             ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:13:24: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// class D4 implements C, self.C {}
+//                        ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:14:29: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// class D5 implements self.C, C {}
+//                             ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:16:16: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// mixin CM on C, C {}
+//                ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:17:17: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// mixin CM2 on C, CAlias {}
+//                 ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:18:22: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// mixin CM3 on CAlias, C {}
+//                      ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:19:22: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// mixin CM4 on self.C, C {}
+//                      ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:20:17: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// mixin CM5 on C, self.C {}
+//                 ^
+//
+import self as self;
+import "dart:core" as core;
+
+import "org-dartlang-testcase:///issue45626.dart" as self;
+
+typedef CAlias = self::C;
+class C extends core::Object {
+  synthetic constructor •() → self::C
+    : super core::Object::•()
+    ;
+}
+class D extends core::Object implements self::C, self::C {
+  synthetic constructor •() → self::D
+    : super core::Object::•()
+    ;
+}
+class D2 extends core::Object implements self::C, self::C {
+  synthetic constructor •() → self::D2
+    : super core::Object::•()
+    ;
+}
+class D3 extends core::Object implements self::C, self::C {
+  synthetic constructor •() → self::D3
+    : super core::Object::•()
+    ;
+}
+class D4 extends core::Object implements self::C, self::C {
+  synthetic constructor •() → self::D4
+    : super core::Object::•()
+    ;
+}
+class D5 extends core::Object implements self::C, self::C {
+  synthetic constructor •() → self::D5
+    : super core::Object::•()
+    ;
+}
+abstract class _CM&C&C extends core::Object implements self::C, self::C /*isAnonymousMixin*/  {
+  synthetic constructor •() → self::_CM&C&C
+    : super core::Object::•()
+    ;
+}
+abstract class CM extends self::_CM&C&C /*isMixinDeclaration*/  {
+}
+abstract class _CM2&C&CAlias extends core::Object implements self::C, self::C /*isAnonymousMixin*/  {
+  synthetic constructor •() → self::_CM2&C&CAlias
+    : super core::Object::•()
+    ;
+}
+abstract class CM2 extends self::_CM2&C&CAlias /*isMixinDeclaration*/  {
+}
+abstract class _CM3&CAlias&C extends core::Object implements self::C, self::C /*isAnonymousMixin*/  {
+  synthetic constructor •() → self::_CM3&CAlias&C
+    : super core::Object::•()
+    ;
+}
+abstract class CM3 extends self::_CM3&CAlias&C /*isMixinDeclaration*/  {
+}
+abstract class _CM4&C&C extends core::Object implements self::C, self::C /*isAnonymousMixin*/  {
+  synthetic constructor •() → self::_CM4&C&C
+    : super core::Object::•()
+    ;
+}
+abstract class CM4 extends self::_CM4&C&C /*isMixinDeclaration*/  {
+}
+abstract class _CM5&C&C extends core::Object implements self::C, self::C /*isAnonymousMixin*/  {
+  synthetic constructor •() → self::_CM5&C&C
+    : super core::Object::•()
+    ;
+}
+abstract class CM5 extends self::_CM5&C&C /*isMixinDeclaration*/  {
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart.weak.outline.expect b/pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart.weak.outline.expect
new file mode 100644
index 0000000..d12c6e1
--- /dev/null
+++ b/pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart.weak.outline.expect
@@ -0,0 +1,116 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:10:23: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// class D implements C, C {}
+//                       ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:11:24: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// class D2 implements C, CAlias {}
+//                        ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:12:29: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// class D3 implements CAlias, C {}
+//                             ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:13:24: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// class D4 implements C, self.C {}
+//                        ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:14:29: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// class D5 implements self.C, C {}
+//                             ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:16:16: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// mixin CM on C, C {}
+//                ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:17:17: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// mixin CM2 on C, CAlias {}
+//                 ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:18:22: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// mixin CM3 on CAlias, C {}
+//                      ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:19:22: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// mixin CM4 on self.C, C {}
+//                      ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:20:17: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// mixin CM5 on C, self.C {}
+//                 ^
+//
+import self as self;
+import "dart:core" as core;
+
+import "org-dartlang-testcase:///issue45626.dart" as self;
+
+typedef CAlias = self::C;
+class C extends core::Object {
+  synthetic constructor •() → self::C
+    ;
+}
+class D extends core::Object implements self::C, self::C {
+  synthetic constructor •() → self::D
+    ;
+}
+class D2 extends core::Object implements self::C, self::C {
+  synthetic constructor •() → self::D2
+    ;
+}
+class D3 extends core::Object implements self::C, self::C {
+  synthetic constructor •() → self::D3
+    ;
+}
+class D4 extends core::Object implements self::C, self::C {
+  synthetic constructor •() → self::D4
+    ;
+}
+class D5 extends core::Object implements self::C, self::C {
+  synthetic constructor •() → self::D5
+    ;
+}
+abstract class _CM&C&C extends core::Object implements self::C, self::C /*isAnonymousMixin*/  {
+  synthetic constructor •() → self::_CM&C&C
+    ;
+}
+abstract class CM extends self::_CM&C&C /*isMixinDeclaration*/  {
+}
+abstract class _CM2&C&CAlias extends core::Object implements self::C, self::C /*isAnonymousMixin*/  {
+  synthetic constructor •() → self::_CM2&C&CAlias
+    ;
+}
+abstract class CM2 extends self::_CM2&C&CAlias /*isMixinDeclaration*/  {
+}
+abstract class _CM3&CAlias&C extends core::Object implements self::C, self::C /*isAnonymousMixin*/  {
+  synthetic constructor •() → self::_CM3&CAlias&C
+    ;
+}
+abstract class CM3 extends self::_CM3&CAlias&C /*isMixinDeclaration*/  {
+}
+abstract class _CM4&C&C extends core::Object implements self::C, self::C /*isAnonymousMixin*/  {
+  synthetic constructor •() → self::_CM4&C&C
+    ;
+}
+abstract class CM4 extends self::_CM4&C&C /*isMixinDeclaration*/  {
+}
+abstract class _CM5&C&C extends core::Object implements self::C, self::C /*isAnonymousMixin*/  {
+  synthetic constructor •() → self::_CM5&C&C
+    ;
+}
+abstract class CM5 extends self::_CM5&C&C /*isMixinDeclaration*/  {
+}
+static method main() → dynamic
+  ;
diff --git a/pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart.weak.transformed.expect b/pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart.weak.transformed.expect
new file mode 100644
index 0000000..84c660a
--- /dev/null
+++ b/pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart.weak.transformed.expect
@@ -0,0 +1,126 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:10:23: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// class D implements C, C {}
+//                       ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:11:24: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// class D2 implements C, CAlias {}
+//                        ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:12:29: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// class D3 implements CAlias, C {}
+//                             ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:13:24: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// class D4 implements C, self.C {}
+//                        ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:14:29: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// class D5 implements self.C, C {}
+//                             ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:16:16: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// mixin CM on C, C {}
+//                ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:17:17: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// mixin CM2 on C, CAlias {}
+//                 ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:18:22: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// mixin CM3 on CAlias, C {}
+//                      ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:19:22: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// mixin CM4 on self.C, C {}
+//                      ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45626.dart:20:17: Error: 'C' can only be implemented once.
+// Try removing 1 of the occurrences.
+// mixin CM5 on C, self.C {}
+//                 ^
+//
+import self as self;
+import "dart:core" as core;
+
+import "org-dartlang-testcase:///issue45626.dart" as self;
+
+typedef CAlias = self::C;
+class C extends core::Object {
+  synthetic constructor •() → self::C
+    : super core::Object::•()
+    ;
+}
+class D extends core::Object implements self::C, self::C {
+  synthetic constructor •() → self::D
+    : super core::Object::•()
+    ;
+}
+class D2 extends core::Object implements self::C, self::C {
+  synthetic constructor •() → self::D2
+    : super core::Object::•()
+    ;
+}
+class D3 extends core::Object implements self::C, self::C {
+  synthetic constructor •() → self::D3
+    : super core::Object::•()
+    ;
+}
+class D4 extends core::Object implements self::C, self::C {
+  synthetic constructor •() → self::D4
+    : super core::Object::•()
+    ;
+}
+class D5 extends core::Object implements self::C, self::C {
+  synthetic constructor •() → self::D5
+    : super core::Object::•()
+    ;
+}
+abstract class _CM&C&C extends core::Object implements self::C, self::C /*isAnonymousMixin*/  {
+  synthetic constructor •() → self::_CM&C&C
+    : super core::Object::•()
+    ;
+}
+abstract class CM extends self::_CM&C&C /*isMixinDeclaration*/  {
+}
+abstract class _CM2&C&CAlias extends core::Object implements self::C, self::C /*isAnonymousMixin*/  {
+  synthetic constructor •() → self::_CM2&C&CAlias
+    : super core::Object::•()
+    ;
+}
+abstract class CM2 extends self::_CM2&C&CAlias /*isMixinDeclaration*/  {
+}
+abstract class _CM3&CAlias&C extends core::Object implements self::C, self::C /*isAnonymousMixin*/  {
+  synthetic constructor •() → self::_CM3&CAlias&C
+    : super core::Object::•()
+    ;
+}
+abstract class CM3 extends self::_CM3&CAlias&C /*isMixinDeclaration*/  {
+}
+abstract class _CM4&C&C extends core::Object implements self::C, self::C /*isAnonymousMixin*/  {
+  synthetic constructor •() → self::_CM4&C&C
+    : super core::Object::•()
+    ;
+}
+abstract class CM4 extends self::_CM4&C&C /*isMixinDeclaration*/  {
+}
+abstract class _CM5&C&C extends core::Object implements self::C, self::C /*isAnonymousMixin*/  {
+  synthetic constructor •() → self::_CM5&C&C
+    : super core::Object::•()
+    ;
+}
+abstract class CM5 extends self::_CM5&C&C /*isMixinDeclaration*/  {
+}
+static method main() → dynamic {}
diff --git a/pkg/js_ast/lib/src/equivalence_visitor.dart b/pkg/js_ast/lib/src/equivalence_visitor.dart
index d06f50f..191b1e0 100644
--- a/pkg/js_ast/lib/src/equivalence_visitor.dart
+++ b/pkg/js_ast/lib/src/equivalence_visitor.dart
@@ -209,6 +209,13 @@
   }
 
   @override
+  bool visitDeferredStatement(DeferredStatement node, Node arg) {
+    if (arg is! DeferredStatement) return failAt(node, arg);
+    DeferredStatement other = arg;
+    return testNodes(node.statement, other.statement);
+  }
+
+  @override
   bool visitFun(Fun node, Node arg) {
     if (arg is! Fun) return failAt(node, arg);
     Fun other = arg;
diff --git a/pkg/js_ast/lib/src/nodes.dart b/pkg/js_ast/lib/src/nodes.dart
index b9cc51f..2868346 100644
--- a/pkg/js_ast/lib/src/nodes.dart
+++ b/pkg/js_ast/lib/src/nodes.dart
@@ -49,6 +49,7 @@
   T visitNamedFunction(NamedFunction node);
   T visitFun(Fun node);
 
+  T visitDeferredStatement(DeferredStatement node);
   T visitDeferredExpression(DeferredExpression node);
   T visitDeferredNumber(DeferredNumber node);
   T visitDeferredString(DeferredString node);
@@ -152,6 +153,7 @@
 
   T visitToken(DeferredToken node) => visitExpression(node);
 
+  T visitDeferredStatement(DeferredStatement node) => visitStatement(node);
   T visitDeferredExpression(DeferredExpression node) => visitExpression(node);
   T visitDeferredNumber(DeferredNumber node) => visitToken(node);
   T visitDeferredString(DeferredString node) => visitToken(node);
@@ -243,6 +245,7 @@
   R visitNamedFunction(NamedFunction node, A arg);
   R visitFun(Fun node, A arg);
 
+  R visitDeferredStatement(DeferredStatement node, A arg);
   R visitDeferredExpression(DeferredExpression node, A arg);
   R visitDeferredNumber(DeferredNumber node, A arg);
   R visitDeferredString(DeferredString node, A arg);
@@ -355,6 +358,8 @@
 
   R visitToken(DeferredToken node, A arg) => visitExpression(node, arg);
 
+  R visitDeferredStatement(DeferredStatement node, A arg) =>
+      visitStatement(node, arg);
   R visitDeferredExpression(DeferredExpression node, A arg) =>
       visitExpression(node, arg);
   R visitDeferredNumber(DeferredNumber node, A arg) => visitToken(node, arg);
@@ -477,6 +482,25 @@
   Statement toStatement() => this;
 }
 
+/// Interface for a deferred [Statement] value. An implementation has to provide
+/// a value via the [statement] getter the latest when the ast is printed.
+abstract class DeferredStatement extends Statement {
+  T accept<T>(NodeVisitor<T> visitor) => visitor.visitDeferredStatement(this);
+
+  R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
+      visitor.visitDeferredStatement(this, arg);
+
+  void visitChildren<T>(NodeVisitor<T> visitor) {
+    statement.accept(visitor);
+  }
+
+  void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
+    statement.accept1(visitor, arg);
+  }
+
+  Statement get statement;
+}
+
 class Block extends Statement {
   final List<Statement> statements;
 
diff --git a/pkg/js_ast/lib/src/printer.dart b/pkg/js_ast/lib/src/printer.dart
index 0d04a21..d851cb8 100644
--- a/pkg/js_ast/lib/src/printer.dart
+++ b/pkg/js_ast/lib/src/printer.dart
@@ -299,12 +299,17 @@
     return false;
   }
 
+  /// Elide Blocks and DeferredStatements with Blocks as children.
   void blockOutWithoutBraces(Node node) {
     if (node is Block) {
       startNode(node);
       Block block = node;
       block.statements.forEach(blockOutWithoutBraces);
       endNode(node);
+    } else if (node is DeferredStatement) {
+      startNode(node);
+      blockOutWithoutBraces(node.statement);
+      endNode(node);
     } else {
       visit(node);
     }
@@ -1101,6 +1106,13 @@
     node.value.accept(this);
   }
 
+  @override
+  visitDeferredStatement(DeferredStatement node) {
+    startNode(node);
+    visit(node.statement);
+    endNode(node);
+  }
+
   outputNumberWithRequiredWhitespace(String number) {
     int charCode = number.codeUnitAt(0);
     if (charCode == charCodes.$MINUS && lastCharCode == charCodes.$MINUS) {
@@ -1414,6 +1426,10 @@
   bool visitBlock(Block node) => false;
   bool visitExpressionStatement(ExpressionStatement node) => false;
   bool visitEmptyStatement(EmptyStatement node) => false;
+  bool visitDeferredStatement(DeferredStatement node) {
+    return node.statement.accept(this);
+  }
+
   bool visitIf(If node) {
     if (!node.hasElse) return true;
     return node.otherwise.accept(this);
diff --git a/pkg/js_ast/lib/src/template.dart b/pkg/js_ast/lib/src/template.dart
index 3cfc133..5aa45ba 100644
--- a/pkg/js_ast/lib/src/template.dart
+++ b/pkg/js_ast/lib/src/template.dart
@@ -661,6 +661,8 @@
 
   Instantiator visitDeferredExpression(DeferredExpression node) => same(node);
 
+  Instantiator visitDeferredStatement(DeferredStatement node) => same(node);
+
   Instantiator visitDeferredNumber(DeferredNumber node) => same(node);
 
   Instantiator visitDeferredString(DeferredString node) => (arguments) => node;
diff --git a/pkg/js_ast/test/deferred_statement_test.dart b/pkg/js_ast/test/deferred_statement_test.dart
new file mode 100644
index 0000000..9628cdf
--- /dev/null
+++ b/pkg/js_ast/test/deferred_statement_test.dart
@@ -0,0 +1,50 @@
+// 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:expect/expect.dart';
+import 'package:js_ast/js_ast.dart';
+
+class _DeferredStatement extends DeferredStatement {
+  final Statement statement;
+
+  _DeferredStatement(this.statement);
+}
+
+main() {
+  // Defering a statement should not change how it prints.
+  var undeferredStatement = js.statement('var x = 3');
+  var deferredStatement = _DeferredStatement(undeferredStatement);
+  Expect.equals(DebugPrint(undeferredStatement), DebugPrint(deferredStatement));
+
+  // Printing a non-finalized DeferredStatement throws.
+  Expect.throws(() => DebugPrint(_DeferredStatement(null)));
+
+  // DeferredStatement with empty Block puts braces.
+  Expect.equals(DebugPrint(_DeferredStatement(Block.empty())), '{\n}\n');
+
+  // DeferredStatement in block with nested block gets elided.
+  Expect.equals(
+      DebugPrint(Block([_DeferredStatement(Block.empty())])), '{\n}\n');
+  Expect.equals(
+      DebugPrint(Block([
+        _DeferredStatement(
+            _DeferredStatement(_DeferredStatement(Block.empty())))
+      ])),
+      '{\n}\n');
+
+  // Nested Blocks in DeferredStatements are elided.
+  Expect.equals(
+      DebugPrint(Block([
+        _DeferredStatement(Block([
+          _DeferredStatement(Block.empty()),
+          Block.empty(),
+          Block([_DeferredStatement(Block.empty()), Block.empty()]),
+          _DeferredStatement(_DeferredStatement(Block.empty()))
+        ]))
+      ])),
+      '{\n}\n');
+
+  // DeferredStatement with empty Statement prints semicolon and a newline.
+  Expect.equals(DebugPrint(_DeferredStatement(EmptyStatement())), ';\n');
+}
diff --git a/pkg/kernel/lib/core_types.dart b/pkg/kernel/lib/core_types.dart
index 98160cf..63ad64c 100644
--- a/pkg/kernel/lib/core_types.dart
+++ b/pkg/kernel/lib/core_types.dart
@@ -306,6 +306,67 @@
   late final Procedure isSentinelMethod =
       index.getTopLevelMember('dart:_internal', 'isSentinel') as Procedure;
 
+  late final Constructor
+      lateInitializationFieldAssignedDuringInitializationConstructor =
+      index.getMember('dart:_internal', 'LateError', 'fieldADI') as Constructor;
+
+  late final Constructor
+      lateInitializationLocalAssignedDuringInitializationConstructor =
+      index.getMember('dart:_internal', 'LateError', 'localADI') as Constructor;
+
+  late final Constructor lateInitializationFieldNotInitializedConstructor =
+      index.getMember('dart:_internal', 'LateError', 'fieldNI') as Constructor;
+
+  late final Constructor lateInitializationLocalNotInitializedConstructor =
+      index.getMember('dart:_internal', 'LateError', 'localNI') as Constructor;
+
+  late final Constructor lateInitializationFieldAlreadyInitializedConstructor =
+      index.getMember('dart:_internal', 'LateError', 'fieldAI') as Constructor;
+
+  late final Constructor lateInitializationLocalAlreadyInitializedConstructor =
+      index.getMember('dart:_internal', 'LateError', 'localAI') as Constructor;
+
+  late final Constructor reachabilityErrorConstructor =
+      index.getMember('dart:_internal', 'ReachabilityError', '') as Constructor;
+
+  late final Class cellClass = index.getClass('dart:_late_helper', '_Cell');
+
+  late final Constructor cellConstructor =
+      index.getMember('dart:_late_helper', '_Cell', '') as Constructor;
+
+  late final Class initializedCellClass =
+      index.getClass('dart:_late_helper', '_InitializedCell');
+
+  late final Constructor initializedCellConstructor = index.getMember(
+      'dart:_late_helper', '_InitializedCell', '') as Constructor;
+
+  late final Procedure cellReadLocal =
+      index.getMember('dart:_late_helper', '_Cell', 'readLocal') as Procedure;
+
+  late final Procedure cellReadField =
+      index.getMember('dart:_late_helper', '_Cell', 'readField') as Procedure;
+
+  late final Procedure initializedCellRead = index.getMember(
+      'dart:_late_helper', '_InitializedCell', 'read') as Procedure;
+
+  late final Procedure initializedCellReadFinal = index.getMember(
+      'dart:_late_helper', '_InitializedCell', 'readFinal') as Procedure;
+
+  late final Procedure cellValueSetter =
+      index.getMember('dart:_late_helper', '_Cell', 'set:value') as Procedure;
+
+  late final Procedure cellFinalLocalValueSetter = index.getMember(
+      'dart:_late_helper', '_Cell', 'set:finalLocalValue') as Procedure;
+
+  late final Procedure cellFinalFieldValueSetter = index.getMember(
+      'dart:_late_helper', '_Cell', 'set:finalFieldValue') as Procedure;
+
+  late final Procedure initializedCellValueSetter = index.getMember(
+      'dart:_late_helper', '_InitializedCell', 'set:value') as Procedure;
+
+  late final Procedure initializedCellFinalValueSetter = index.getMember(
+      'dart:_late_helper', '_InitializedCell', 'set:finalValue') as Procedure;
+
   InterfaceType get objectLegacyRawType {
     return _objectLegacyRawType ??= _legacyRawTypes[objectClass] ??=
         new InterfaceType(objectClass, Nullability.legacy, const <DartType>[]);
@@ -1028,29 +1089,6 @@
     return result;
   }
 
-  late final Constructor
-      lateInitializationFieldAssignedDuringInitializationConstructor =
-      index.getMember('dart:_internal', 'LateError', 'fieldADI') as Constructor;
-
-  late final Constructor
-      lateInitializationLocalAssignedDuringInitializationConstructor =
-      index.getMember('dart:_internal', 'LateError', 'localADI') as Constructor;
-
-  late final Constructor lateInitializationFieldNotInitializedConstructor =
-      index.getMember('dart:_internal', 'LateError', 'fieldNI') as Constructor;
-
-  late final Constructor lateInitializationLocalNotInitializedConstructor =
-      index.getMember('dart:_internal', 'LateError', 'localNI') as Constructor;
-
-  late final Constructor lateInitializationFieldAlreadyInitializedConstructor =
-      index.getMember('dart:_internal', 'LateError', 'fieldAI') as Constructor;
-
-  late final Constructor lateInitializationLocalAlreadyInitializedConstructor =
-      index.getMember('dart:_internal', 'LateError', 'localAI') as Constructor;
-
-  late final Constructor reachabilityErrorConstructor =
-      index.getMember('dart:_internal', 'ReachabilityError', '') as Constructor;
-
   InterfaceType bottomInterfaceType(Class klass, Nullability nullability) {
     InterfaceType? result = _bottomInterfaceTypes[klass];
     if (result == null) {
diff --git a/pkg/kernel/lib/src/bounds_checks.dart b/pkg/kernel/lib/src/bounds_checks.dart
index a0d9fb8..17b0a06 100644
--- a/pkg/kernel/lib/src/bounds_checks.dart
+++ b/pkg/kernel/lib/src/bounds_checks.dart
@@ -165,13 +165,18 @@
       new List<DartType>.filled(typeParameters.length, dummyDartType);
   for (int i = 0; i < typeParameters.length; i++) {
     DartType? bound = typeParameters[i].bound;
+    bool isContravariant = typeParameters[i].variance == Variance.contravariant;
     if (bound == null) {
-      bound = const DynamicType();
+      bound = isNonNullableByDefault && isContravariant
+          ? const NeverType.nonNullable()
+          : const DynamicType();
     } else if (bound is InterfaceType && bound.classNode == objectClass) {
       DartType defaultType = typeParameters[i].defaultType!;
       if (!(defaultType is InterfaceType &&
           defaultType.classNode == objectClass)) {
-        bound = const DynamicType();
+        bound = isNonNullableByDefault && isContravariant
+            ? const NeverType.nonNullable()
+            : const DynamicType();
       }
     }
     bounds[i] = bound;
@@ -193,8 +198,10 @@
     Substitution substitution =
         Substitution.fromUpperAndLowerBounds(upperBounds, lowerBounds);
     for (int typeParameterIndex in component) {
-      bounds[typeParameterIndex] =
-          substitution.substituteType(bounds[typeParameterIndex]);
+      bounds[typeParameterIndex] = substitution.substituteType(
+          bounds[typeParameterIndex],
+          contravariant: typeParameters[typeParameterIndex].variance ==
+              Variance.contravariant);
     }
   }
 
@@ -206,7 +213,8 @@
     Substitution substitution =
         Substitution.fromUpperAndLowerBounds(upperBounds, lowerBounds);
     for (int j = 0; j < typeParameters.length; j++) {
-      bounds[j] = substitution.substituteType(bounds[j]);
+      bounds[j] = substitution.substituteType(bounds[j],
+          contravariant: typeParameters[j].variance == Variance.contravariant);
     }
   }
 
diff --git a/pkg/nnbd_migration/lib/migration_cli.dart b/pkg/nnbd_migration/lib/migration_cli.dart
index 9298730..0590138 100644
--- a/pkg/nnbd_migration/lib/migration_cli.dart
+++ b/pkg/nnbd_migration/lib/migration_cli.dart
@@ -950,7 +950,7 @@
     _dartFixListener.reset();
     _fixCodeProcessor.prepareToRerun();
     var analysisResult = await _fixCodeProcessor.runFirstPhase();
-    if (analysisResult.hasErrors) {
+    if (analysisResult.hasErrors && !options.ignoreErrors) {
       _logErrors(analysisResult);
       return MigrationState(
           _fixCodeProcessor._task.migration,
@@ -1088,11 +1088,10 @@
     }
 
     for (var path in pathsToProcess.difference(pathsProcessed)) {
-      var result = await driver.getResolvedUnit(path);
-      if (result == null || result.unit == null) {
-        continue;
+      var result = await driver.getResolvedUnit2(path);
+      if (result is ResolvedUnitResult) {
+        await process(result);
       }
-      await process(result);
     }
   }
 
diff --git a/pkg/nnbd_migration/lib/src/edge_builder.dart b/pkg/nnbd_migration/lib/src/edge_builder.dart
index d64b16b..316b4b2 100644
--- a/pkg/nnbd_migration/lib/src/edge_builder.dart
+++ b/pkg/nnbd_migration/lib/src/edge_builder.dart
@@ -2043,8 +2043,7 @@
           '(${expression.toSource()}) offset=${expression.offset}');
     }
     var origin = _makeEdgeOrigin(sourceType, expression);
-    var hard = _postDominatedLocals.isReferenceInScope(expression) ||
-        expression.unParenthesized is AsExpression;
+    var hard = _shouldUseHardEdge(expression);
     var edge = _graph.makeNonNullable(sourceType.node, origin,
         hard: hard, guards: _guards);
     if (origin is ExpressionChecksOrigin) {
@@ -2407,8 +2406,7 @@
               FixReasonTarget.root,
               source: destinationType,
               destination: _createNonNullableType(compoundOperatorInfo),
-              hard: _postDominatedLocals
-                  .isReferenceInScope(assignmentExpression.leftHandSide));
+              hard: _shouldUseHardEdge(assignmentExpression.leftHandSide));
           DecoratedType compoundOperatorType = getOrComputeElementType(
               compoundOperatorInfo, compoundOperatorMethod,
               targetType: destinationType,
@@ -2417,7 +2415,7 @@
           _checkAssignment(edgeOrigin, FixReasonTarget.root,
               source: sourceType,
               destination: compoundOperatorType.positionalParameters[0],
-              hard: _postDominatedLocals.isReferenceInScope(expression),
+              hard: _shouldUseHardEdge(expression),
               sourceIsFunctionLiteral: expression is FunctionExpression);
           sourceType = _fixNumericTypes(
               compoundOperatorType.returnType, compoundOperatorInfo.staticType);
@@ -2452,13 +2450,8 @@
             return methodInvocationType.withNode(newNode);
           };
         } else {
-          var unwrappedExpression = expression.unParenthesized;
-          var hard = (questionAssignNode == null &&
-                  _postDominatedLocals.isReferenceInScope(expression)) ||
-              // An edge from a cast should be hard, so that the cast type
-              // annotation is appropriately made nullable according to the
-              // destination type.
-              unwrappedExpression is AsExpression;
+          var hard = _shouldUseHardEdge(expression,
+              isConditionallyExecuted: questionAssignNode != null);
           _checkAssignment(edgeOrigin, FixReasonTarget.root,
               source: sourceType,
               destination: destinationType,
@@ -3266,6 +3259,37 @@
     return node;
   }
 
+  /// Determines whether uses of [expression] should cause hard edges to be
+  /// created in the nullability graph.
+  ///
+  /// If [isConditionallyExecuted] is `true`, that indicates that [expression]
+  /// appears in a context where it might not get executed (e.g. on the RHS of
+  /// a `??=`).
+  bool _shouldUseHardEdge(Expression expression,
+      {bool isConditionallyExecuted = false}) {
+    expression = expression.unParenthesized;
+    if (expression is ListLiteral || expression is SetOrMapLiteral) {
+      // List, set, and map literals have either explicit or implicit type
+      // arguments.  If supplying a nullable type for one of these type
+      // arguments would lead to an error (e.g. `f(<int?>[])` where `f` requires
+      // a `List<int>`), then we should use a hard edge, to ensure that the
+      // migrated type argument will be non-nullable.
+      return true;
+    } else if (expression is AsExpression) {
+      // "as" expressions have an explicit type.  If making this type nullable
+      // would lead to an error (e.g. `f(x as int?)` where `f` requires an
+      // `int`),then we should use a hard edge, to ensure that the migrated type
+      // will be non-nullable.
+      return true;
+    }
+    // For other expressions, we should use a hard edge only if (a) the
+    // expression is unconditionally executed, and (b) the expression is a
+    // reference to a local variable or parameter and it post-dominates the
+    // declaration of that local variable or parameter.
+    return !isConditionallyExecuted &&
+        _postDominatedLocals.isReferenceInScope(expression);
+  }
+
   DecoratedType _thisOrSuper(Expression node) {
     if (_currentClassOrExtension == null) {
       return null;
@@ -3439,7 +3463,8 @@
     _checkAssignment_recursion(origin, edgeTarget,
         source: source,
         destination: destination,
-        sourceIsFunctionLiteral: sourceIsFunctionLiteral);
+        sourceIsFunctionLiteral: sourceIsFunctionLiteral,
+        hard: hard);
   }
 
   /// Does the recursive part of [_checkAssignment], visiting all of the types
@@ -3449,7 +3474,8 @@
   void _checkAssignment_recursion(EdgeOrigin origin, FixReasonTarget edgeTarget,
       {@required DecoratedType source,
       @required DecoratedType destination,
-      bool sourceIsFunctionLiteral = false}) {
+      bool sourceIsFunctionLiteral = false,
+      bool hard = false}) {
     var sourceType = source.type;
     var destinationType = destination.type;
     assert(_typeSystem.isSubtypeOf(sourceType, destinationType));
@@ -3554,7 +3580,7 @@
         _checkAssignment(origin, edgeTarget.typeArgument(i),
             source: rewrittenSource.typeArguments[i],
             destination: destination.typeArguments[i],
-            hard: false,
+            hard: hard,
             checkable: false);
       }
     } else if (sourceType is FunctionType && destinationType is FunctionType) {
diff --git a/pkg/nnbd_migration/lib/src/nullability_node.dart b/pkg/nnbd_migration/lib/src/nullability_node.dart
index 85fa200..668b8ad 100644
--- a/pkg/nnbd_migration/lib/src/nullability_node.dart
+++ b/pkg/nnbd_migration/lib/src/nullability_node.dart
@@ -141,7 +141,9 @@
 
   @override
   bool get isHard =>
-      _kind == _NullabilityEdgeKind.hard || _kind == _NullabilityEdgeKind.union;
+      _kind == _NullabilityEdgeKind.hard ||
+      _kind == _NullabilityEdgeKind.union ||
+      _kind == _NullabilityEdgeKind.uncheckableHard;
 
   @override
   bool get isSatisfied {
@@ -177,6 +179,9 @@
       case _NullabilityEdgeKind.uncheckable:
         json['kind'] = 'uncheckable';
         break;
+      case _NullabilityEdgeKind.uncheckableHard:
+        json['kind'] = 'uncheckableHard';
+        break;
       case _NullabilityEdgeKind.hard:
         json['kind'] = 'hard';
         break;
@@ -205,6 +210,9 @@
       case _NullabilityEdgeKind.uncheckable:
         edgeDecorations.add('uncheckable');
         break;
+      case _NullabilityEdgeKind.uncheckableHard:
+        edgeDecorations.add('uncheckableHard');
+        break;
       case _NullabilityEdgeKind.hard:
         edgeDecorations.add('hard');
         break;
@@ -228,6 +236,8 @@
     switch (kind) {
       case 'uncheckable':
         return _NullabilityEdgeKind.uncheckable;
+      case 'uncheckableHard':
+        return _NullabilityEdgeKind.uncheckableHard;
       case 'hard':
         return _NullabilityEdgeKind.hard;
       case 'union':
@@ -295,7 +305,9 @@
       List<NullabilityNode> guards = const []}) {
     var upstreamNodes = [sourceNode, ...guards];
     var kind = hard
-        ? _NullabilityEdgeKind.hard
+        ? checkable
+            ? _NullabilityEdgeKind.hard
+            : _NullabilityEdgeKind.uncheckableHard
         : checkable
             ? _NullabilityEdgeKind.soft
             : _NullabilityEdgeKind.uncheckable;
@@ -1297,6 +1309,11 @@
   /// nullability.
   union,
 
+  /// Uncheckable hard edge.  Propagates nullability downstream and
+  /// non-nullability upstream.  May not be overridden by suggestions that the
+  /// user intends non-nullability.
+  uncheckableHard,
+
   /// Dummy edge.  Indicates that two edges are connected in a way that should
   /// not propagate (non-)nullability in either direction.
   dummy,
diff --git a/pkg/nnbd_migration/test/abstract_single_unit.dart b/pkg/nnbd_migration/test/abstract_single_unit.dart
index 2e1ea9b..1edbb80 100644
--- a/pkg/nnbd_migration/test/abstract_single_unit.dart
+++ b/pkg/nnbd_migration/test/abstract_single_unit.dart
@@ -37,7 +37,8 @@
 
   Future<void> resolveTestUnit(String code) async {
     addTestSource(code, testUri);
-    testAnalysisResult = await session.getResolvedUnit(testFile);
+    testAnalysisResult =
+        await session.getResolvedUnit2(testFile) as ResolvedUnitResult;
     testUnit = testAnalysisResult.unit;
     if (verifyNoTestUnitErrors) {
       expect(testAnalysisResult.errors.where((AnalysisError error) {
diff --git a/pkg/nnbd_migration/test/api_test.dart b/pkg/nnbd_migration/test/api_test.dart
index c069791..7f5df1c 100644
--- a/pkg/nnbd_migration/test/api_test.dart
+++ b/pkg/nnbd_migration/test/api_test.dart
@@ -714,6 +714,102 @@
     await _checkSingleFileChanges(content, expected, warnOnWeakCode: true);
   }
 
+  Future<void> test_collection_literal_typed_list() async {
+    var content = '''
+void f(int/*?*/ x, int/*?*/ y) {
+  g(<int>[x, y]);
+}
+g(List<int/*!*/>/*!*/ z) {}
+''';
+    var expected = '''
+void f(int? x, int? y) {
+  g(<int>[x!, y!]);
+}
+g(List<int> z) {}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
+  Future<void> test_collection_literal_typed_map() async {
+    var content = '''
+void f(int/*?*/ x, int/*?*/ y) {
+  g(<int, int>{x: y});
+}
+g(Map<int/*!*/, int/*!*/>/*!*/ z) {}
+''';
+    var expected = '''
+void f(int? x, int? y) {
+  g(<int, int>{x!: y!});
+}
+g(Map<int, int> z) {}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
+  Future<void> test_collection_literal_typed_set() async {
+    var content = '''
+void f(int/*?*/ x, int/*?*/ y) {
+  g(<int>{x, y});
+}
+g(Set<int/*!*/>/*!*/ z) {}
+''';
+    var expected = '''
+void f(int? x, int? y) {
+  g(<int>{x!, y!});
+}
+g(Set<int> z) {}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
+  Future<void> test_collection_literal_untyped_list() async {
+    var content = '''
+void f(int/*?*/ x, int/*?*/ y) {
+  g([x, y]);
+}
+g(List<int/*!*/>/*!*/ z) {}
+''';
+    var expected = '''
+void f(int? x, int? y) {
+  g([x!, y!]);
+}
+g(List<int> z) {}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
+  Future<void> test_collection_literal_untyped_map() async {
+    var content = '''
+void f(int/*?*/ x, int/*?*/ y) {
+  g({x: y});
+}
+g(Map<int/*!*/, int/*!*/>/*!*/ z) {}
+''';
+    var expected = '''
+void f(int? x, int? y) {
+  g({x!: y!});
+}
+g(Map<int, int> z) {}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
+  Future<void> test_collection_literal_untyped_set() async {
+    var content = '''
+void f(int/*?*/ x, int/*?*/ y) {
+  g({x, y});
+}
+g(Set<int/*!*/>/*!*/ z) {}
+''';
+    var expected = '''
+void f(int? x, int? y) {
+  g({x!, y!});
+}
+g(Set<int> z) {}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
   Future<void> test_comment_bang_implies_non_null_intent() async {
     var content = '''
 void f(int/*!*/ i) {}
@@ -1577,15 +1673,9 @@
 }
 g({List<int/*!*/>/*!*/ named}) {}
 ''';
-    // Note: this test is to ensure that we don't produce
-    // `g((named: <int?>[x, y]) as List<int>)` (which would be a parse error).
-    // The migration we produce (`g(named: <int?>[x, y])`) still isn't great,
-    // because it's a type mismatch, but it's better.
-    //
-    // TODO(paulberry): a better migration would be `g(named: <int>[x!, y!])`.
     var expected = '''
 void f(int? x, int? y) {
-  g(named: <int?>[x, y]);
+  g(named: <int>[x!, y!]);
 }
 g({required List<int> named}) {}
 ''';
diff --git a/pkg/nnbd_migration/test/edge_builder_test.dart b/pkg/nnbd_migration/test/edge_builder_test.dart
index e50b091..0771b0e 100644
--- a/pkg/nnbd_migration/test/edge_builder_test.dart
+++ b/pkg/nnbd_migration/test/edge_builder_test.dart
@@ -220,7 +220,7 @@
     assign(t1, t2, hard: true);
     assertEdge(t1.node, t2.node, hard: true);
     assertEdge(t1.typeArguments[0].node, t2.typeArguments[0].node,
-        hard: false, checkable: false);
+        hard: true, checkable: false);
   }
 
   void test_future_or_int_to_future_int() {
@@ -320,7 +320,7 @@
     assign(t1, t2, hard: true);
     assertEdge(t1.node, t2.node, hard: true);
     assertEdge(t1.typeArguments[0].node, t2.typeArguments[0].node,
-        hard: false, checkable: false);
+        hard: true, checkable: false);
   }
 
   void test_generic_to_generic_upcast() {
@@ -712,7 +712,7 @@
         hard: true);
     assertEdge(decoratedTypeAnnotation('bool>;').node,
         decoratedTypeAnnotation('bool> f').node,
-        hard: false, checkable: false);
+        hard: true, checkable: false);
     assertNoEdge(anyNode, decoratedTypeAnnotation('bool>;').node);
     assertNoEdge(anyNode, decoratedTypeAnnotation('int> a').node);
     // int> a should be connected to the bound of T in A<T>, but nothing else.
@@ -803,7 +803,7 @@
     // the return type `FutureOr<List<int?>>`.
     assertEdge(decoratedTypeAnnotation('int>> x').node,
         decoratedTypeAnnotation('int>> f').node,
-        hard: false, checkable: false);
+        hard: true, checkable: false);
     assertNoEdge(decoratedTypeAnnotation('int>> x').node,
         decoratedTypeAnnotation('List<int>> f').node);
     assertNoEdge(decoratedTypeAnnotation('int>> x').node,
@@ -831,7 +831,7 @@
         substitutionNode(
             decoratedTypeAnnotation('int> x').node, inSet(pointsToNever)),
         decoratedTypeAnnotation('int> f').node,
-        hard: false,
+        hard: true,
         checkable: false);
     assertNoEdge(decoratedTypeAnnotation('int> x').node,
         decoratedTypeAnnotation('FutureOr<int>').node);
@@ -1237,7 +1237,7 @@
     assertEdge(
         substitutionNode(listInt.typeArguments[0].node, inSet(pointsToNever)),
         iterableInt.typeArguments[0].node,
-        hard: false,
+        hard: true,
         checkable: false);
   }
 
@@ -2032,7 +2032,7 @@
         hard: true);
     assertEdge(decoratedTypeAnnotation('int>/*2*/').node,
         constructorParameterType.typeArguments[0].node,
-        hard: false, checkable: false);
+        hard: true, checkable: false);
     assertUnion(constructorParameterType.node,
         decoratedTypeAnnotation('MyList<int>/*1*/').node);
     assertUnion(constructorParameterType.typeArguments[0].node,
@@ -3507,7 +3507,7 @@
     assertNoUpstreamNullability(decoratedTypeAnnotation('List<int/*1*/>').node);
     assertEdge(decoratedTypeAnnotation('int/*2*/').node,
         decoratedTypeAnnotation('int/*1*/').node,
-        hard: false, checkable: false);
+        hard: true, checkable: false);
   }
 
   Future<void>
@@ -3847,7 +3847,7 @@
         decoratedTypeAnnotation('String> j').node,
         substitutionNode(decoratedTypeAnnotation('String> d').node,
             decoratedTypeAnnotation('V>>').node),
-        hard: false,
+        hard: true,
         checkable: false);
     assertEdge(
         decoratedTypeAnnotation('List<String> j').node,
@@ -4293,7 +4293,7 @@
         decoratedTypeAnnotation('int> x').node,
         substitutionNode(
             inferredTypeArgument, decoratedTypeAnnotation('T> x').node),
-        hard: false,
+        hard: true,
         checkable: false);
   }
 
@@ -4636,7 +4636,7 @@
     var typeArgForReturnType = decoratedTypeAnnotation('String> ').node;
     assertNoUpstreamNullability(typeArgForLiteral);
     assertEdge(typeArgForLiteral, typeArgForReturnType,
-        hard: false, checkable: false);
+        hard: true, checkable: false);
   }
 
   Future<void> test_listLiteral_typeArgument_nullableElement() async {
@@ -5118,7 +5118,7 @@
 
     assertEdge(decoratedTypeAnnotation('int/*3*/').node,
         decoratedTypeAnnotation('int/*1*/').node,
-        hard: false, checkable: false);
+        hard: true, checkable: false);
     assertNullCheck(
         checkExpression('c/*check*/'),
         assertEdge(decoratedTypeAnnotation('C<int/*3*/>/*4*/').node,
@@ -7129,7 +7129,7 @@
         decoratedTypeAnnotation('int g').node,
         // TODO(40621): This should be a checkable edge.
         assertEdge(anyNode, decoratedTypeAnnotation('int>').node,
-                hard: false, checkable: false)
+                hard: true, checkable: false)
             .sourceNode,
         hard: false,
         // TODO(40621): This should be a checkable edge.
@@ -7188,7 +7188,7 @@
 ''');
     var lubNodeMatcher = anyNode;
     assertEdge(lubNodeMatcher, decoratedTypeAnnotation('Object').node,
-        hard: false, checkable: false);
+        hard: true, checkable: false);
     var lubNode = lubNodeMatcher.matchingNode as NullabilityNodeForLUB;
     expect(lubNode.left, same(decoratedTypeAnnotation('int> x').node));
     expect(lubNode.right, same(decoratedTypeAnnotation('FutureOr<int>').node));
@@ -7201,7 +7201,7 @@
 ''');
     assertEdge(decoratedTypeAnnotation('List<int>').node,
         decoratedTypeAnnotation('Object').node,
-        hard: false, checkable: false);
+        hard: true, checkable: false);
   }
 
   Future<void> test_return_from_async_null() async {
@@ -7298,11 +7298,11 @@
     var mapNode = decoratedTypeAnnotation('Map').node;
 
     assertNoUpstreamNullability(mapNode);
-    var keyEdge = assertEdge(anyNode, keyNode, hard: false, checkable: false);
+    var keyEdge = assertEdge(anyNode, keyNode, hard: true, checkable: false);
     assertNoUpstreamNullability(keyEdge.sourceNode);
     expect(keyEdge.sourceNode.displayName, 'map key type (test.dart:2:10)');
     var valueEdge =
-        assertEdge(anyNode, valueNode, hard: false, checkable: false);
+        assertEdge(anyNode, valueNode, hard: true, checkable: false);
     assertNoUpstreamNullability(valueEdge.sourceNode);
     expect(valueEdge.sourceNode.displayName, 'map value type (test.dart:2:10)');
   }
@@ -7319,10 +7319,10 @@
 
     assertNoUpstreamNullability(mapNode);
     assertEdge(inSet(alwaysPlus),
-        assertEdge(anyNode, keyNode, hard: false, checkable: false).sourceNode,
+        assertEdge(anyNode, keyNode, hard: true, checkable: false).sourceNode,
         hard: false);
     assertNoUpstreamNullability(
-        assertEdge(anyNode, valueNode, hard: false, checkable: false)
+        assertEdge(anyNode, valueNode, hard: true, checkable: false)
             .sourceNode);
   }
 
@@ -7339,12 +7339,10 @@
 
     assertNoUpstreamNullability(mapNode);
     assertEdge(inSet(alwaysPlus),
-        assertEdge(anyNode, keyNode, hard: false, checkable: false).sourceNode,
+        assertEdge(anyNode, keyNode, hard: true, checkable: false).sourceNode,
         hard: false);
-    assertEdge(
-        inSet(alwaysPlus),
-        assertEdge(anyNode, valueNode, hard: false, checkable: false)
-            .sourceNode,
+    assertEdge(inSet(alwaysPlus),
+        assertEdge(anyNode, valueNode, hard: true, checkable: false).sourceNode,
         hard: false);
   }
 
@@ -7360,11 +7358,9 @@
 
     assertNoUpstreamNullability(mapNode);
     assertNoUpstreamNullability(
-        assertEdge(anyNode, keyNode, hard: false, checkable: false).sourceNode);
-    assertEdge(
-        inSet(alwaysPlus),
-        assertEdge(anyNode, valueNode, hard: false, checkable: false)
-            .sourceNode,
+        assertEdge(anyNode, keyNode, hard: true, checkable: false).sourceNode);
+    assertEdge(inSet(alwaysPlus),
+        assertEdge(anyNode, valueNode, hard: true, checkable: false).sourceNode,
         hard: false);
   }
 
@@ -7380,13 +7376,13 @@
     var keyForLiteral = decoratedTypeAnnotation('String, int>{').node;
     var keyForReturnType = decoratedTypeAnnotation('String, int> ').node;
     assertNoUpstreamNullability(keyForLiteral);
-    assertEdge(keyForLiteral, keyForReturnType, hard: false, checkable: false);
+    assertEdge(keyForLiteral, keyForReturnType, hard: true, checkable: false);
 
     var valueForLiteral = decoratedTypeAnnotation('int>{').node;
     var valueForReturnType = decoratedTypeAnnotation('int> ').node;
     assertNoUpstreamNullability(valueForLiteral);
     assertEdge(valueForLiteral, valueForReturnType,
-        hard: false, checkable: false);
+        hard: true, checkable: false);
   }
 
   Future<void> test_setOrMapLiteral_map_typeArguments_nullableKey() async {
@@ -7438,7 +7434,7 @@
     var setNode = decoratedTypeAnnotation('Set').node;
 
     assertNoUpstreamNullability(setNode);
-    var edge = assertEdge(anyNode, valueNode, hard: false, checkable: false);
+    var edge = assertEdge(anyNode, valueNode, hard: true, checkable: false);
     assertNoUpstreamNullability(edge.sourceNode);
     expect(edge.sourceNode.displayName, 'set element type (test.dart:2:10)');
   }
@@ -7453,10 +7449,8 @@
     var setNode = decoratedTypeAnnotation('Set').node;
 
     assertNoUpstreamNullability(setNode);
-    assertEdge(
-        inSet(alwaysPlus),
-        assertEdge(anyNode, valueNode, hard: false, checkable: false)
-            .sourceNode,
+    assertEdge(inSet(alwaysPlus),
+        assertEdge(anyNode, valueNode, hard: true, checkable: false).sourceNode,
         hard: false);
   }
 
@@ -7472,7 +7466,7 @@
     var typeArgForReturnType = decoratedTypeAnnotation('String> ').node;
     assertNoUpstreamNullability(typeArgForLiteral);
     assertEdge(typeArgForLiteral, typeArgForReturnType,
-        hard: false, checkable: false);
+        hard: true, checkable: false);
   }
 
   Future<void> test_setOrMapLiteral_set_typeArgument_nullableElement() async {
@@ -7737,7 +7731,7 @@
     assertEdge(
         substitutionNode(decoratedTypeAnnotation('int> ints').node, anyNode),
         decoratedTypeAnnotation('int>[').node,
-        hard: false,
+        hard: true,
         checkable: false);
   }
 
@@ -7763,7 +7757,7 @@
     assertEdge(
         substitutionNode(decoratedTypeAnnotation('int> ints').node, anyNode),
         decoratedTypeAnnotation('int>[').node,
-        hard: false,
+        hard: true,
         checkable: false);
   }
 
@@ -7778,10 +7772,10 @@
         hard: true);
     assertEdge(decoratedTypeAnnotation('String, int> map').node,
         decoratedTypeAnnotation('String, int>{').node,
-        hard: false, checkable: false);
+        hard: true, checkable: false);
     assertEdge(decoratedTypeAnnotation('int> map').node,
         decoratedTypeAnnotation('int>{').node,
-        hard: false, checkable: false);
+        hard: true, checkable: false);
   }
 
   Future<void> test_spread_element_set() async {
@@ -7795,7 +7789,7 @@
     assertEdge(
         substitutionNode(decoratedTypeAnnotation('int> ints').node, anyNode),
         decoratedTypeAnnotation('int>{').node,
-        hard: false,
+        hard: true,
         checkable: false);
   }
 
@@ -7813,7 +7807,7 @@
         substitutionNode(decoratedTypeAnnotation('int> ints').node,
             decoratedTypeAnnotation('R> {}').node),
         decoratedTypeAnnotation('int>[').node,
-        hard: false,
+        hard: true,
         checkable: false);
   }
 
diff --git a/pkg/nnbd_migration/test/front_end/info_builder_test.dart b/pkg/nnbd_migration/test/front_end/info_builder_test.dart
index 45292fd..625672b 100644
--- a/pkg/nnbd_migration/test/front_end/info_builder_test.dart
+++ b/pkg/nnbd_migration/test/front_end/info_builder_test.dart
@@ -25,7 +25,7 @@
 @reflectiveTest
 class BuildEnclosingMemberDescriptionTest extends AbstractAnalysisTest {
   Future<ResolvedUnitResult> resolveTestFile() async {
-    return await session.getResolvedUnit(testFile);
+    return await session.getResolvedUnit2(testFile) as ResolvedUnitResult;
   }
 
   Future<void> test_classConstructor_named() async {
diff --git a/pkg/nnbd_migration/test/front_end/nnbd_migration_test_base.dart b/pkg/nnbd_migration/test/front_end/nnbd_migration_test_base.dart
index 2622ed5..f9d7b6e 100644
--- a/pkg/nnbd_migration/test/front_end/nnbd_migration_test_base.dart
+++ b/pkg/nnbd_migration/test/front_end/nnbd_migration_test_base.dart
@@ -241,7 +241,8 @@
     Future<void> _forEachPath(
         void Function(ResolvedUnitResult) callback) async {
       for (var testPath in testPaths) {
-        var result = await driver.currentSession.getResolvedUnit(testPath);
+        var result = await driver.currentSession.getResolvedUnit2(testPath)
+            as ResolvedUnitResult;
         callback(result);
       }
     }
diff --git a/pkg/nnbd_migration/test/instrumentation_test.dart b/pkg/nnbd_migration/test/instrumentation_test.dart
index 6e8cd37..605f928 100644
--- a/pkg/nnbd_migration/test/instrumentation_test.dart
+++ b/pkg/nnbd_migration/test/instrumentation_test.dart
@@ -2,6 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/src/generated/source.dart';
@@ -147,7 +148,8 @@
         instrumentation: _InstrumentationClient(this),
         removeViaComments: removeViaComments,
         warnOnWeakCode: warnOnWeakCode);
-    var result = await session.getResolvedUnit(sourcePath);
+    var result =
+        await session.getResolvedUnit2(sourcePath) as ResolvedUnitResult;
     source = result.unit.declaredElement.source;
     findNode = FindNode(content, result.unit);
     migration.prepareInput(result);
diff --git a/pkg/nnbd_migration/test/migration_cli_test.dart b/pkg/nnbd_migration/test/migration_cli_test.dart
index 5758e9a..36a85b2 100644
--- a/pkg/nnbd_migration/test/migration_cli_test.dart
+++ b/pkg/nnbd_migration/test/migration_cli_test.dart
@@ -1272,6 +1272,32 @@
     });
   }
 
+  test_lifecycle_preview_rerun_with_ignore_errors() async {
+    var origSourceText = 'void f(int i) {}';
+    var projectContents = simpleProject(sourceText: origSourceText);
+    var projectDir = createProjectDir(projectContents);
+    var cli = _createCli();
+    await runWithPreviewServer(cli, ['--ignore-errors', projectDir],
+        (url) async {
+      await assertPreviewServerResponsive(url);
+      var uri = Uri.parse(url);
+      var testPath =
+          resourceProvider.pathContext.join(projectDir, 'lib', 'test.dart');
+      resourceProvider.getFile(testPath).writeAsStringSync('void f(int? i) {}');
+      // We haven't rerun, so getting the file details from the server should
+      // still yield the original source text, with informational space.
+      expect(await getSourceFromServer(uri, testPath), 'void f(int  i) {}');
+      var response = await httpPost(uri.replace(path: 'rerun-migration'),
+          headers: {'Content-Type': 'application/json; charset=UTF-8'});
+      assertHttpSuccess(response);
+      var body = jsonDecode(response.body);
+      expect(body['success'], isTrue);
+      expect(body['errors'], isNull);
+      // Now that we've rerun, the server should yield the new source text
+      expect(await getSourceFromServer(uri, testPath), 'void f(int?  i) {}');
+    });
+  }
+
   test_lifecycle_preview_rerun_with_new_analysis_errors() async {
     var origSourceText = 'void f(int i) {}';
     var projectContents = simpleProject(sourceText: origSourceText);
diff --git a/pkg/nnbd_migration/tool/trial_migration.dart b/pkg/nnbd_migration/tool/trial_migration.dart
index 2d0769d..4c14791 100644
--- a/pkg/nnbd_migration/tool/trial_migration.dart
+++ b/pkg/nnbd_migration/tool/trial_migration.dart
@@ -10,6 +10,7 @@
 
 import 'dart:io';
 
+import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/diagnostic/diagnostic.dart';
 import 'package:analyzer/src/dart/analysis/analysis_context_collection.dart';
@@ -68,7 +69,8 @@
       var migration =
           NullabilityMigration(listener, getLineInfo, permissive: true);
       for (var file in localFiles) {
-        var resolvedUnit = await session.getResolvedUnit(file);
+        var resolvedUnit =
+            await session.getResolvedUnit2(file) as ResolvedUnitResult;
         if (!resolvedUnit.errors.any((e) => e.severity == Severity.error)) {
           migration.prepareInput(resolvedUnit);
         } else {
@@ -76,13 +78,15 @@
         }
       }
       for (var file in localFiles) {
-        var resolvedUnit = await session.getResolvedUnit(file);
+        var resolvedUnit =
+            await session.getResolvedUnit2(file) as ResolvedUnitResult;
         if (!resolvedUnit.errors.any((e) => e.severity == Severity.error)) {
           migration.processInput(resolvedUnit);
         }
       }
       for (var file in localFiles) {
-        var resolvedUnit = await session.getResolvedUnit(file);
+        var resolvedUnit =
+            await session.getResolvedUnit2(file) as ResolvedUnitResult;
         if (!resolvedUnit.errors.any((e) => e.severity == Severity.error)) {
           migration.finalizeInput(resolvedUnit);
         }
diff --git a/pkg/test_runner/lib/bot_results.dart b/pkg/test_runner/lib/bot_results.dart
index b5032f6..8f68b04 100644
--- a/pkg/test_runner/lib/bot_results.dart
+++ b/pkg/test_runner/lib/bot_results.dart
@@ -71,7 +71,7 @@
 Future<String> runGsutil(List<String> arguments) async {
   return gsutilPool.withResource(() async {
     var processResult = await Process.run(
-        "python", [gsutilPy]..addAll(arguments),
+        "python3", [gsutilPy]..addAll(arguments),
         runInShell: Platform.isWindows);
     var stderr = processResult.stderr as String;
     if (processResult.exitCode != 0) {
@@ -79,14 +79,14 @@
           stderr.contains("One or more URLs matched no objects")) {
         return null;
       }
-      var error = "Failed to run: python $gsutilPy $arguments\n"
+      var error = "Failed to run: python3 $gsutilPy $arguments\n"
           "exitCode: ${processResult.exitCode}\n"
           "stdout:\n${processResult.stdout}\n"
           "stderr:\n${processResult.stderr}";
       if (processResult.exitCode == 1 &&
           stderr.contains("401 Anonymous caller")) {
         error =
-            "\n\nYou need to authenticate by running:\npython $gsutilPy config\n";
+            "\n\nYou need to authenticate by running:\npython3 $gsutilPy config\n";
       }
       throw Exception(error);
     }
diff --git a/pkg/test_runner/lib/src/build_configurations.dart b/pkg/test_runner/lib/src/build_configurations.dart
index 6179997..b6c8aa8b 100644
--- a/pkg/test_runner/lib/src/build_configurations.dart
+++ b/pkg/test_runner/lib/src/build_configurations.dart
@@ -49,8 +49,8 @@
     ...osFlags,
     ...buildTargets
   ];
-  print('Running command: python ${command.join(' ')}');
-  final process = await Process.start('python', command);
+  print('Running command: python3 ${command.join(' ')}');
+  final process = await Process.start('python3', command);
   stdout.nonBlocking.addStream(process.stdout);
   stderr.nonBlocking.addStream(process.stderr);
   final exitCode = await process.exitCode;
diff --git a/pkg/test_runner/lib/src/status_reporter.dart b/pkg/test_runner/lib/src/status_reporter.dart
index 36e0414..e795d96 100644
--- a/pkg/test_runner/lib/src/status_reporter.dart
+++ b/pkg/test_runner/lib/src/status_reporter.dart
@@ -77,9 +77,9 @@
     'runtime'
   ];
 
-  print('Running: python ${args.join(" ")}');
+  print('Running: python3 ${args.join(" ")}');
 
-  var result = Process.runSync('python', args);
+  var result = Process.runSync('python3', args);
 
   if (result.exitCode != 0) {
     print('ERROR');
@@ -149,7 +149,7 @@
             '--report-in-json',
             '--use-sdk'
           ];
-          var result = Process.runSync('python', args);
+          var result = Process.runSync('python3', args);
           if (result.exitCode != 0) {
             print(result.stdout);
             print(result.stderr);
diff --git a/pkg/test_runner/lib/src/test_progress.dart b/pkg/test_runner/lib/src/test_progress.dart
index 850c492..a089688 100644
--- a/pkg/test_runner/lib/src/test_progress.dart
+++ b/pkg/test_runner/lib/src/test_progress.dart
@@ -622,7 +622,7 @@
   if (Platform.isFuchsia) {
     arguments = [Platform.executable, Platform.script.path];
   } else {
-    arguments = ['python', 'tools/test.py'];
+    arguments = ['python3', 'tools/test.py'];
   }
   arguments.addAll(test.configuration.reproducingArguments);
   arguments.add(test.displayName);
diff --git a/pkg/test_runner/lib/test_runner.dart b/pkg/test_runner/lib/test_runner.dart
index 33f93ba..59fcb9f 100644
--- a/pkg/test_runner/lib/test_runner.dart
+++ b/pkg/test_runner/lib/test_runner.dart
@@ -482,7 +482,7 @@
     print("Running tests");
     print("".padLeft(80, "="));
     await runProcessInheritStdio(
-        "python",
+        "python3",
         [
           "tools/test.py",
           "--named-configuration=${configurationsToRun.join(",")}",
@@ -607,7 +607,7 @@
     ];
 
     await runProcessInheritStdio(
-        "python", ["tools/test.py", ...deflakeArguments],
+        "python3", ["tools/test.py", ...deflakeArguments],
         runInShell: Platform.isWindows);
     deflakingResultsPaths.add("${deflakeDirectory.path}/results.json");
   }
diff --git a/pkg/vm/lib/transformations/ffi.dart b/pkg/vm/lib/transformations/ffi.dart
index bd70fef..b6c00a8 100644
--- a/pkg/vm/lib/transformations/ffi.dart
+++ b/pkg/vm/lib/transformations/ffi.dart
@@ -231,6 +231,7 @@
   final Class pointerClass;
   final Class compoundClass;
   final Class structClass;
+  final Class unionClass;
   final Class ffiStructLayoutClass;
   final Field ffiStructLayoutTypesField;
   final Field ffiStructLayoutPackingField;
@@ -247,7 +248,10 @@
   final Procedure addressGetter;
   final Procedure structPointerRef;
   final Procedure structPointerElemAt;
+  final Procedure unionPointerRef;
+  final Procedure unionPointerElemAt;
   final Procedure structArrayElemAt;
+  final Procedure unionArrayElemAt;
   final Procedure arrayArrayElemAt;
   final Procedure arrayArrayAssignAt;
   final Procedure asFunctionMethod;
@@ -264,6 +268,7 @@
   final Field arrayNestedDimensionsFirst;
   final Field arrayNestedDimensionsRest;
   final Constructor structFromTypedDataBase;
+  final Constructor unionFromTypedDataBase;
   final Constructor arrayConstructor;
   final Procedure fromAddressInternal;
   final Procedure libraryLookupMethod;
@@ -333,6 +338,7 @@
         pointerClass = index.getClass('dart:ffi', 'Pointer'),
         compoundClass = index.getClass('dart:ffi', '_Compound'),
         structClass = index.getClass('dart:ffi', 'Struct'),
+        unionClass = index.getClass('dart:ffi', 'Union'),
         ffiStructLayoutClass = index.getClass('dart:ffi', '_FfiStructLayout'),
         ffiStructLayoutTypesField =
             index.getMember('dart:ffi', '_FfiStructLayout', 'fieldTypes'),
@@ -369,6 +375,8 @@
             index.getMember('dart:ffi', 'Array', '_nestedDimensionsRest'),
         structFromTypedDataBase =
             index.getMember('dart:ffi', 'Struct', '_fromTypedDataBase'),
+        unionFromTypedDataBase =
+            index.getMember('dart:ffi', 'Union', '_fromTypedDataBase'),
         arrayConstructor = index.getMember('dart:ffi', 'Array', '_'),
         fromAddressInternal =
             index.getTopLevelMember('dart:ffi', '_fromAddress'),
@@ -376,7 +384,11 @@
             index.getMember('dart:ffi', 'StructPointer', 'get:ref'),
         structPointerElemAt =
             index.getMember('dart:ffi', 'StructPointer', '[]'),
+        unionPointerRef =
+            index.getMember('dart:ffi', 'UnionPointer', 'get:ref'),
+        unionPointerElemAt = index.getMember('dart:ffi', 'UnionPointer', '[]'),
         structArrayElemAt = index.getMember('dart:ffi', 'StructArray', '[]'),
+        unionArrayElemAt = index.getMember('dart:ffi', 'UnionArray', '[]'),
         arrayArrayElemAt = index.getMember('dart:ffi', 'ArrayArray', '[]'),
         arrayArrayAssignAt = index.getMember('dart:ffi', 'ArrayArray', '[]='),
         asFunctionMethod =
@@ -478,7 +490,7 @@
       return nativeType;
     }
     if (hierarchy.isSubclassOf(nativeClass, compoundClass)) {
-      if (nativeClass == structClass) {
+      if (nativeClass == structClass || nativeClass == unionClass) {
         return null;
       }
       return allowCompounds ? nativeType : null;
@@ -749,11 +761,13 @@
       return false;
     }
     if (type is InterfaceType) {
-      if (type.classNode == structClass) {
+      if (type.classNode == structClass || type.classNode == unionClass) {
         return false;
       }
     }
-    return env.isSubtypeOf(type, InterfaceType(structClass, Nullability.legacy),
+    return env.isSubtypeOf(
+        type,
+        InterfaceType(compoundClass, Nullability.legacy),
         SubtypeCheckMode.ignoringNullabilities);
   }
 }
diff --git a/pkg/vm/lib/transformations/ffi_definitions.dart b/pkg/vm/lib/transformations/ffi_definitions.dart
index 732a676..6dc8a46 100644
--- a/pkg/vm/lib/transformations/ffi_definitions.dart
+++ b/pkg/vm/lib/transformations/ffi_definitions.dart
@@ -146,8 +146,8 @@
       if (report) {
         component.forEach((Class e) {
           diagnosticReporter.report(
-              templateFfiFieldCyclic.withArguments(
-                  e.name, component.map((e) => e.name).toList()),
+              templateFfiFieldCyclic.withArguments(e.superclass.name, e.name,
+                  component.map((e) => e.name).toList()),
               e.fileOffset,
               e.name.length,
               e.fileUri);
@@ -175,7 +175,8 @@
   visitClass(Class node) {
     if (!hierarchy.isSubclassOf(node, compoundClass) ||
         node == compoundClass ||
-        node == structClass) {
+        node == structClass ||
+        node == unionClass) {
       return node;
     }
 
@@ -203,39 +204,41 @@
   int _checkCompoundClass(Class node) {
     if (node.typeParameters.length > 0) {
       diagnosticReporter.report(
-          templateFfiStructGeneric.withArguments(node.name),
+          templateFfiStructGeneric.withArguments(
+              node.superclass.name, node.name),
           node.fileOffset,
           1,
           node.location.file);
     }
 
-    if (node.superclass != structClass) {
-      // Not a struct, but extends a struct. The error will be emitted by
-      // _FfiUseSiteTransformer.
+    if (node.superclass != structClass && node.superclass != unionClass) {
+      // Not a struct or union, but extends a struct or union.
+      // The error will be emitted by _FfiUseSiteTransformer.
       return null;
     }
 
-    final packingAnnotations = _getPackedAnnotations(node);
-    if (packingAnnotations.length > 1) {
-      diagnosticReporter.report(
-          templateFfiPackedAnnotation.withArguments(node.name),
-          node.fileOffset,
-          node.name.length,
-          node.location.file);
-    }
-    if (packingAnnotations.isNotEmpty) {
-      final packing = packingAnnotations.first;
-      if (!(packing == 1 ||
-          packing == 2 ||
-          packing == 4 ||
-          packing == 8 ||
-          packing == 16)) {
-        diagnosticReporter.report(messageFfiPackedAnnotationAlignment,
-            node.fileOffset, node.name.length, node.location.file);
+    if (node.superclass == structClass) {
+      final packingAnnotations = _getPackedAnnotations(node);
+      if (packingAnnotations.length > 1) {
+        diagnosticReporter.report(
+            templateFfiPackedAnnotation.withArguments(node.name),
+            node.fileOffset,
+            node.name.length,
+            node.location.file);
       }
-      return packing;
+      if (packingAnnotations.isNotEmpty) {
+        final packing = packingAnnotations.first;
+        if (!(packing == 1 ||
+            packing == 2 ||
+            packing == 4 ||
+            packing == 8 ||
+            packing == 16)) {
+          diagnosticReporter.report(messageFfiPackedAnnotationAlignment,
+              node.fileOffset, node.name.length, node.location.file);
+        }
+        return packing;
+      }
     }
-
     return null;
   }
 
@@ -440,7 +443,10 @@
         name: name,
         initializers: [
           SuperInitializer(
-              structFromTypedDataBase, Arguments([VariableGet(typedDataBase)]))
+              node.superclass == structClass
+                  ? structFromTypedDataBase
+                  : unionFromTypedDataBase,
+              Arguments([VariableGet(typedDataBase)]))
         ],
         fileUri: node.fileUri,
         reference: referenceFrom?.reference)
@@ -511,12 +517,17 @@
 
     _annoteCompoundWithFields(node, types, packing);
     if (types.isEmpty) {
-      diagnosticReporter.report(templateFfiEmptyStruct.withArguments(node.name),
-          node.fileOffset, node.name.length, node.location.file);
+      diagnosticReporter.report(
+          templateFfiEmptyStruct.withArguments(node.superclass.name, node.name),
+          node.fileOffset,
+          node.name.length,
+          node.location.file);
       emptyCompounds.add(node);
     }
 
-    final compoundType = StructNativeTypeCfe(node, types, packing: packing);
+    final compoundType = node.superclass == structClass
+        ? StructNativeTypeCfe(node, types, packing: packing)
+        : UnionNativeTypeCfe(node, types);
     compoundCache[node] = compoundType;
     final compoundLayout = compoundType.layout;
 
@@ -1090,6 +1101,34 @@
   }
 }
 
+class UnionNativeTypeCfe extends CompoundNativeTypeCfe {
+  factory UnionNativeTypeCfe(Class clazz, List<NativeTypeCfe> members) {
+    final layout = Map.fromEntries(
+        Abi.values.map((abi) => MapEntry(abi, _calculateLayout(members, abi))));
+    return UnionNativeTypeCfe._(clazz, members, layout);
+  }
+
+  UnionNativeTypeCfe._(
+      Class clazz, List<NativeTypeCfe> members, Map<Abi, CompoundLayout> layout)
+      : super._(clazz, members, layout);
+
+  // Keep consistent with runtime/vm/compiler/ffi/native_type.cc
+  // NativeUnionType::FromNativeTypes.
+  static CompoundLayout _calculateLayout(List<NativeTypeCfe> types, Abi abi) {
+    int unionSize = 1;
+    int unionAlignment = 1;
+    for (int i = 0; i < types.length; i++) {
+      final int size = types[i].size[abi];
+      int alignment = types[i].alignment[abi];
+      unionSize = math.max(unionSize, size);
+      unionAlignment = math.max(unionAlignment, alignment);
+    }
+    final int size = _alignOffset(unionSize, unionAlignment);
+    return CompoundLayout(
+        size, unionAlignment, List.filled(Abi.values.length, 0));
+  }
+}
+
 class ArrayNativeTypeCfe implements NativeTypeCfe {
   final NativeTypeCfe elementType;
   final int length;
diff --git a/pkg/vm/lib/transformations/ffi_use_sites.dart b/pkg/vm/lib/transformations/ffi_use_sites.dart
index 44d56f1..a4383ed 100644
--- a/pkg/vm/lib/transformations/ffi_use_sites.dart
+++ b/pkg/vm/lib/transformations/ffi_use_sites.dart
@@ -176,13 +176,16 @@
 
     final Member target = node.target;
     try {
-      if (target == structPointerRef || target == structPointerElemAt) {
+      if (target == structPointerRef ||
+          target == structPointerElemAt ||
+          target == unionPointerRef ||
+          target == unionPointerElemAt) {
         final DartType nativeType = node.arguments.types[0];
 
         _ensureNativeTypeValid(nativeType, node, allowCompounds: true);
 
         return _replaceRef(node);
-      } else if (target == structArrayElemAt) {
+      } else if (target == structArrayElemAt || target == unionArrayElemAt) {
         final DartType nativeType = node.arguments.types[0];
 
         _ensureNativeTypeValid(nativeType, node, allowCompounds: true);
@@ -228,7 +231,8 @@
           final returnType = dartType.returnType;
           if (returnType is InterfaceType) {
             final clazz = returnType.classNode;
-            if (clazz.superclass == structClass) {
+            if (clazz.superclass == structClass ||
+                clazz.superclass == unionClass) {
               return _invokeCompoundConstructor(replacement, clazz);
             }
           }
@@ -256,7 +260,8 @@
           final returnType = dartType.returnType;
           if (returnType is InterfaceType) {
             final clazz = returnType.classNode;
-            if (clazz.superclass == structClass) {
+            if (clazz.superclass == structClass ||
+                clazz.superclass == unionClass) {
               return _invokeCompoundConstructor(replacement, clazz);
             }
           }
@@ -286,7 +291,8 @@
         if (expectedReturn == NativeType.kVoid ||
             expectedReturn == NativeType.kPointer ||
             expectedReturn == NativeType.kHandle ||
-            expectedReturnClass.superclass == structClass) {
+            expectedReturnClass.superclass == structClass ||
+            expectedReturnClass.superclass == unionClass) {
           if (node.arguments.positional.length > 1) {
             diagnosticReporter.report(
                 templateFfiExpectedNoExceptionalReturn.withArguments(
@@ -351,7 +357,8 @@
         final compoundClasses = funcType.positionalParameters
             .whereType<InterfaceType>()
             .map((t) => t.classNode)
-            .where((c) => c.superclass == structClass)
+            .where((c) =>
+                c.superclass == structClass || c.superclass == unionClass)
             .toList();
         return _invokeCompoundConstructors(replacement, compoundClasses);
       } else if (target == allocateMethod) {
@@ -778,7 +785,8 @@
       if (hierarchy.isSubclassOf(nativeClass, compoundClass)) {
         if (emptyCompounds.contains(nativeClass)) {
           diagnosticReporter.report(
-              templateFfiEmptyStruct.withArguments(nativeClass.name),
+              templateFfiEmptyStruct.withArguments(
+                  nativeClass.superclass.name, nativeClass.name),
               node.fileOffset,
               1,
               node.location.file);
@@ -830,13 +838,14 @@
         klass == compoundClass ||
         klass == opaqueClass ||
         klass == structClass ||
+        klass == unionClass ||
         nativeTypesClasses.contains(klass)) {
       return null;
     }
 
     // The Opaque and Struct classes can be extended, but subclasses
     // cannot be (nor implemented).
-    final onlyDirectExtendsClasses = [opaqueClass, structClass];
+    final onlyDirectExtendsClasses = [opaqueClass, structClass, unionClass];
     final superClass = klass.superclass;
     for (final onlyDirectExtendsClass in onlyDirectExtendsClasses) {
       if (hierarchy.isSubtypeOf(klass, onlyDirectExtendsClass)) {
diff --git a/pkg/vm_snapshot_analysis/lib/ascii_table.dart b/pkg/vm_snapshot_analysis/lib/ascii_table.dart
index a0e5404..3721814 100644
--- a/pkg/vm_snapshot_analysis/lib/ascii_table.dart
+++ b/pkg/vm_snapshot_analysis/lib/ascii_table.dart
@@ -16,7 +16,7 @@
   /// Note: there is a border on the left and right of each column
   /// plus whitespace around it.
   static int totalWidth(List<int> widths) =>
-      widths.fold(0, (sum, width) => sum + width + 3) + 1;
+      widths.fold<int>(0, (sum, width) => sum + width + 3) + 1;
 }
 
 enum Separator {
@@ -91,7 +91,7 @@
   final String value;
   final AlignmentDirection direction;
 
-  Text({this.value, this.direction});
+  Text({required this.value, required this.direction});
   Text.left(String value)
       : this(value: value, direction: AlignmentDirection.Left);
   Text.right(String value)
@@ -113,7 +113,6 @@
         final diff = width - value.length;
         return ' ' * (diff ~/ 2) + value + (' ' * (diff - diff ~/ 2));
     }
-    return null; // Make analyzer happy.
   }
 
   int get length => value.length;
@@ -126,7 +125,7 @@
 
   final List<Row> rows = <Row>[];
 
-  AsciiTable({List<dynamic> header, this.maxWidth = unlimitedWidth}) {
+  AsciiTable({List<dynamic>? header, this.maxWidth = unlimitedWidth}) {
     if (header != null) {
       addSeparator();
       addRow(header);
diff --git a/pkg/vm_snapshot_analysis/lib/instruction_sizes.dart b/pkg/vm_snapshot_analysis/lib/instruction_sizes.dart
index 635f8dd..a1db07f 100644
--- a/pkg/vm_snapshot_analysis/lib/instruction_sizes.dart
+++ b/pkg/vm_snapshot_analysis/lib/instruction_sizes.dart
@@ -40,16 +40,20 @@
 
   /// If this instructions object originated from a function then [libraryUri]
   /// will contain uri of the library of that function.
-  final String libraryUri;
+  final String? libraryUri;
 
   /// If this instructions object originated from a function then [className]
   /// would contain name of the class owning that function.
-  final String className;
+  final String? className;
 
   /// Size of the instructions object in bytes.
   final int size;
 
-  SymbolInfo({String name, this.libraryUri, this.className, this.size})
+  SymbolInfo(
+      {required String name,
+      this.libraryUri,
+      this.className,
+      required this.size})
       : name = Name(name);
 
   static SymbolInfo _fromJson(Map<String, dynamic> map) {
@@ -74,37 +78,41 @@
   final program = ProgramInfo();
   for (var sym in symbols) {
     final scrubbed = sym.name.scrubbed;
-    if (sym.libraryUri == null) {
+    final libraryUri = sym.libraryUri;
+
+    // Handle stubs specially.
+    if (libraryUri == null) {
       assert(sym.name.isStub);
       final node = program.makeNode(
           name: scrubbed, parent: program.stubs, type: NodeType.functionNode);
       assert(node.size == null || sym.name.isTypeTestingStub);
       node.size = (node.size ?? 0) + sym.size;
-    } else {
-      // Split the name into components (names of individual functions).
-      final path = sym.name.components;
-
-      var node = program.root;
-      final package = packageOf(sym.libraryUri);
-      if (package != sym.libraryUri) {
-        node = program.makeNode(
-            name: package, parent: node, type: NodeType.packageNode);
-      }
-      node = program.makeNode(
-          name: sym.libraryUri, parent: node, type: NodeType.libraryNode);
-      node = program.makeNode(
-          name: sym.className, parent: node, type: NodeType.classNode);
-      node = program.makeNode(
-          name: path.first, parent: node, type: NodeType.functionNode);
-      for (var name in path.skip(1)) {
-        if (collapseAnonymousClosures) {
-          name = Name.collapse(name);
-        }
-        node = program.makeNode(
-            name: name, parent: node, type: NodeType.functionNode);
-      }
-      node.size = (node.size ?? 0) + sym.size;
+      continue;
     }
+
+    // Split the name into components (names of individual functions).
+    final path = sym.name.components;
+
+    var node = program.root;
+    final package = packageOf(libraryUri);
+    if (package != libraryUri) {
+      node = program.makeNode(
+          name: package, parent: node, type: NodeType.packageNode);
+    }
+    node = program.makeNode(
+        name: libraryUri, parent: node, type: NodeType.libraryNode);
+    node = program.makeNode(
+        name: sym.className!, parent: node, type: NodeType.classNode);
+    node = program.makeNode(
+        name: path.first, parent: node, type: NodeType.functionNode);
+    for (var name in path.skip(1)) {
+      if (collapseAnonymousClosures) {
+        name = Name.collapse(name);
+      }
+      node = program.makeNode(
+          name: name, parent: node, type: NodeType.functionNode);
+    }
+    node.size = (node.size ?? 0) + sym.size;
   }
 
   return program;
diff --git a/pkg/vm_snapshot_analysis/lib/name.dart b/pkg/vm_snapshot_analysis/lib/name.dart
index 3b17bd1..5d4e569 100644
--- a/pkg/vm_snapshot_analysis/lib/name.dart
+++ b/pkg/vm_snapshot_analysis/lib/name.dart
@@ -15,9 +15,6 @@
   /// Raw textual representation of the name as it occurred in the output
   /// of the AOT compiler.
   final String raw;
-  String _scrubbed;
-
-  Name(this.raw);
 
   /// Pretty version of the name, with some of the irrelevant information
   /// removed from it.
@@ -26,16 +23,18 @@
   /// so we are not removing any details that are used for disambiguation.
   /// The only exception are type testing stubs, these refer to type names
   /// and types names are not bound to be unique between compilations.
-  String get scrubbed => _scrubbed ??=
+  late final String scrubbed =
       raw.replaceAll(isStub ? _stubScrubbingRe : _scrubbingRe, '');
 
+  Name(this.raw);
+
   /// Returns true if this name refers to a stub.
   bool get isStub => raw.startsWith('[Stub] ');
 
   /// Returns true if this name refers to an allocation stub.
   bool get isAllocationStub => raw.startsWith('[Stub] Allocate ');
 
-  /// Returns ture if this name refers to a type testing stub.
+  /// Returns true if this name refers to a type testing stub.
   bool get isTypeTestingStub => raw.startsWith('[Stub] Type Test ');
 
   /// Split this name into individual '.' separated components (e.g. names of
diff --git a/pkg/vm_snapshot_analysis/lib/precompiler_trace.dart b/pkg/vm_snapshot_analysis/lib/precompiler_trace.dart
index 54d86fe..3c9843c 100644
--- a/pkg/vm_snapshot_analysis/lib/precompiler_trace.dart
+++ b/pkg/vm_snapshot_analysis/lib/precompiler_trace.dart
@@ -11,7 +11,8 @@
 
 /// Build [CallGraph] based on the trace written by `--trace-precompiler-to`
 /// flag.
-CallGraph loadTrace(Object inputJson) => _TraceReader(inputJson).readTrace();
+CallGraph loadTrace(Object inputJson) =>
+    _TraceReader(inputJson as Map<String, dynamic>).readTrace();
 
 /// [CallGraphNode] represents a node of the call-graph. It can either be:
 ///
@@ -39,7 +40,7 @@
   /// Dominator of this node.
   ///
   /// Computed by [CallGraph.computeDominators].
-  CallGraphNode dominator;
+  late CallGraphNode dominator;
 
   /// Nodes dominated by this node.
   ///
@@ -99,13 +100,13 @@
 
   // Mapping from [ProgramInfoNode] to a corresponding [CallGraphNode] (if any)
   // via [ProgramInfoNode.id].
-  final List<CallGraphNode> _graphNodeByEntityId;
+  final List<CallGraphNode?> _graphNodeByEntityId;
 
   CallGraph._(this.program, this.nodes, this._graphNodeByEntityId);
 
   CallGraphNode get root => nodes.first;
 
-  CallGraphNode lookup(ProgramInfoNode node) => _graphNodeByEntityId[node.id];
+  CallGraphNode lookup(ProgramInfoNode node) => _graphNodeByEntityId[node.id]!;
 
   Iterable<CallGraphNode> get dynamicCalls =>
       nodes.where((n) => n.isDynamicCallNode);
@@ -113,7 +114,7 @@
   /// Compute a collapsed version of the call-graph, where
   CallGraph collapse(NodeType type, {bool dropCallNodes = false}) {
     final graphNodesByData = <Object, CallGraphNode>{};
-    final graphNodeByEntityId = <CallGraphNode>[];
+    final graphNodeByEntityId = <CallGraphNode?>[];
 
     ProgramInfoNode collapsed(ProgramInfoNode nn) {
       // Root always collapses onto itself.
@@ -127,7 +128,7 @@
       // hitting the root node.
       var n = nn;
       while (n.parent != program.root && n.type != type) {
-        n = n.parent;
+        n = n.parent!;
       }
       return n;
     }
@@ -186,14 +187,14 @@
 ///
 /// See README.md for description of the format.
 class _TraceReader {
-  final List<Object> trace;
-  final List<Object> strings;
-  final List<Object> entities;
+  final List<dynamic> trace;
+  final List<String> strings;
+  final List<dynamic> entities;
 
   final program = ProgramInfo();
 
   /// Mapping between entity ids and corresponding [ProgramInfoNode] nodes.
-  final entityById = List<ProgramInfoNode>.filled(1024, null, growable: true);
+  final entityById = List<ProgramInfoNode?>.filled(1024, null, growable: true);
 
   /// Mapping between functions (represented as [ProgramInfoNode]s) and
   /// their selector ids.
@@ -203,21 +204,22 @@
   final dynamicFunctions = Set<ProgramInfoNode>();
 
   _TraceReader(Map<String, dynamic> data)
-      : strings = data['strings'],
+      : strings = (data['strings'] as List<dynamic>).cast<String>(),
         entities = data['entities'],
         trace = data['trace'];
 
   /// Read all trace events and construct the call graph based on them.
   CallGraph readTrace() {
     var pos = 0; // Position in the [trace] array.
-    CallGraphNode currentNode;
+    late CallGraphNode currentNode;
+    int maxId = 0;
 
     final nodes = <CallGraphNode>[];
-    final nodeByEntityId = <CallGraphNode>[];
+    final nodeByEntityId = <CallGraphNode?>[];
     final callNodesBySelector = <dynamic, CallGraphNode>{};
     final allocated = Set<ProgramInfoNode>();
 
-    Object next() => trace[pos++];
+    T next<T>() => trace[pos++] as T;
 
     CallGraphNode makeNode({dynamic data}) {
       final n = CallGraphNode(nodes.length, data: data);
@@ -232,6 +234,9 @@
       if (nodeByEntityId.length <= n.id) {
         nodeByEntityId.length = n.id * 2 + 1;
       }
+      if (n.id > maxId) {
+        maxId = n.id;
+      }
       return nodeByEntityId[n.id] ??= makeNode(data: n);
     }
 
@@ -363,10 +368,13 @@
   /// Read the entity at the given [index] in [entities].
   ProgramInfoNode readEntityAt(int index) {
     final type = entities[index];
+    final idx0 = entities[index + 1] as int;
+    final idx1 = entities[index + 2] as int;
+    final idx2 = entities[index + 3] as int;
     switch (type) {
       case 'C': // Class: 'C', <library-uri-idx>, <name-idx>, 0
-        final libraryUri = strings[entities[index + 1]];
-        final className = strings[entities[index + 2]];
+        final libraryUri = strings[idx0];
+        final className = strings[idx1];
 
         return program.makeNode(
             name: className,
@@ -375,9 +383,9 @@
 
       case 'S':
       case 'F': // Function: 'F'|'S', <class-idx>, <name-idx>, <selector-id>
-        final classNode = getEntityAt(entities[index + 1]);
-        final functionName = strings[entities[index + 2]];
-        final int selectorId = entities[index + 3];
+        final classNode = getEntityAt(idx0);
+        final functionName = strings[idx1];
+        final int selectorId = idx2;
 
         final path = Name(functionName).rawComponents;
         if (path.last == 'FfiTrampoline') {
@@ -398,8 +406,8 @@
         return node;
 
       case 'V': // Field: 'V', <class-idx>, <name-idx>, 0
-        final classNode = getEntityAt(entities[index + 1]);
-        final fieldName = strings[entities[index + 2]];
+        final classNode = getEntityAt(idx0);
+        final fieldName = strings[idx1];
 
         return program.makeNode(
             name: fieldName, parent: classNode, type: NodeType.other);
diff --git a/pkg/vm_snapshot_analysis/lib/program_info.dart b/pkg/vm_snapshot_analysis/lib/program_info.dart
index e243048..cca2bca 100644
--- a/pkg/vm_snapshot_analysis/lib/program_info.dart
+++ b/pkg/vm_snapshot_analysis/lib/program_info.dart
@@ -5,8 +5,6 @@
 /// Classes for representing information about the program structure.
 library vm_snapshot_analysis.program_info;
 
-import 'package:meta/meta.dart';
-
 import 'package:vm_snapshot_analysis/v8_profile.dart';
 
 /// Represents information about compiled program.
@@ -22,7 +20,7 @@
 
   /// V8 snapshot profile if this [ProgramInfo] object was created from an
   /// output of `--write-v8-snapshot-profile-to=...` flag.
-  SnapshotInfo snapshotInfo;
+  SnapshotInfo? snapshotInfo;
 
   ProgramInfo._(this.root, this.stubs, this.unknown);
 
@@ -45,24 +43,22 @@
   }
 
   ProgramInfoNode makeNode(
-      {@required String name,
-      @required ProgramInfoNode parent,
-      @required NodeType type}) {
-    return parent.children.putIfAbsent(name, () {
-      final node = ProgramInfoNode._(
-          id: _nextId++, name: name, parent: parent ?? root, type: type);
-      node.parent.children[name] = node;
-      return node;
-    });
+      {required String name,
+      required ProgramInfoNode parent,
+      required NodeType type}) {
+    return parent.children.putIfAbsent(
+        name,
+        () => ProgramInfoNode._(
+            id: _nextId++, name: name, parent: parent, type: type));
   }
 
   /// Recursively visit all function nodes, which have [FunctionInfo.info]
   /// populated.
   void visit(
-      void Function(
-              String pkg, String lib, String cls, String fun, ProgramInfoNode n)
+      void Function(String? pkg, String lib, String? cls, String? fun,
+              ProgramInfoNode n)
           callback) {
-    final context = List<String>.filled(NodeType.values.length, null);
+    final context = List<String?>.filled(NodeType.values.length, null);
 
     void recurse(ProgramInfoNode node) {
       final prevContext = context[node._type];
@@ -72,11 +68,11 @@
         context[node._type] = node.name;
       }
 
-      final String pkg = context[NodeType.packageNode.index];
-      final String lib = context[NodeType.libraryNode.index];
-      final String cls = context[NodeType.classNode.index];
-      final String mem = context[NodeType.functionNode.index];
-      callback(pkg, lib, cls, mem, node);
+      final pkg = context[NodeType.packageNode.index];
+      final lib = context[NodeType.libraryNode.index];
+      final cls = context[NodeType.classNode.index];
+      final mem = context[NodeType.functionNode.index];
+      callback(pkg, lib!, cls, mem, node);
 
       for (var child in node.children.values) {
         recurse(child);
@@ -96,12 +92,14 @@
   Map<String, dynamic> toJson() => root.toJson();
 
   /// Lookup a node in the program given a path to it.
-  ProgramInfoNode lookup(List<String> path) {
+  ProgramInfoNode? lookup(List<String> path) {
     var n = root;
     for (var p in path) {
-      if ((n = n.children[p]) == null) {
-        break;
+      final next = n.children[p];
+      if (next == null) {
+        return null;
       }
+      n = next;
     }
     return n;
   }
@@ -121,12 +119,12 @@
       NodeType.classNode: 'class',
       NodeType.functionNode: 'function',
       NodeType.other: 'other',
-    }[type];
+    }[type]!;
 
 class ProgramInfoNode {
   final int id;
   final String name;
-  final ProgramInfoNode parent;
+  final ProgramInfoNode? parent;
   final Map<String, ProgramInfoNode> children = {};
   final int _type;
 
@@ -151,13 +149,13 @@
   /// Assuming that both `C.f` and `C.g` are included into AOT snapshot
   /// and string `"something"` does not occur anywhere else in the program
   /// then the size of `"something"` is going to be attributed to `C`.
-  int size;
+  int? size;
 
   ProgramInfoNode._(
-      {@required this.id,
-      @required this.name,
-      @required this.parent,
-      @required NodeType type})
+      {required this.id,
+      required this.name,
+      required this.parent,
+      required NodeType type})
       : _type = type.index;
 
   NodeType get type => NodeType.values[_type];
@@ -175,9 +173,10 @@
     var prefix = '';
     // Do not include root name or package name (library uri already contains
     // package name).
-    if (parent?.parent != null && parent?.type != NodeType.packageNode) {
-      prefix = parent.qualifiedName;
-      if (parent.type != NodeType.libraryNode) {
+    final p = parent;
+    if (p != null && p.parent != null && p.type != NodeType.packageNode) {
+      prefix = p.qualifiedName;
+      if (p.type != NodeType.libraryNode) {
         prefix += '.';
       } else {
         prefix += '::';
@@ -198,7 +197,7 @@
     var n = this;
     while (n.parent != null) {
       result.add(n.name);
-      n = n.parent;
+      n = n.parent!;
     }
     return result.reversed.toList();
   }
@@ -215,24 +214,24 @@
   final programDiff = ProgramInfo();
 
   var path = <Object>[];
-  void recurse(ProgramInfoNode oldNode, ProgramInfoNode newNode) {
+  void recurse(ProgramInfoNode? oldNode, ProgramInfoNode? newNode) {
     if (oldNode?.size != newNode?.size) {
       var diffNode = programDiff.root;
       for (var i = 0; i < path.length; i += 2) {
-        final name = path[i];
-        final type = path[i + 1];
+        final name = path[i] as String;
+        final type = path[i + 1] as NodeType;
         diffNode =
             programDiff.makeNode(name: name, parent: diffNode, type: type);
       }
-      diffNode.size ??= 0;
-      diffNode.size += (newNode?.size ?? 0) - (oldNode?.size ?? 0);
+      diffNode.size =
+          (diffNode.size ?? 0) + (newNode?.size ?? 0) - (oldNode?.size ?? 0);
     }
 
     for (var key in _allKeys(newNode?.children, oldNode?.children)) {
       final newChildNode = newNode != null ? newNode.children[key] : null;
       final oldChildNode = oldNode != null ? oldNode.children[key] : null;
       path.add(key);
-      path.add(oldChildNode?.type ?? newChildNode?.type);
+      path.add((oldChildNode?.type ?? newChildNode?.type)!);
       recurse(oldChildNode, newChildNode);
       path.removeLast();
       path.removeLast();
@@ -244,7 +243,7 @@
   return programDiff;
 }
 
-Iterable<T> _allKeys<T>(Map<T, dynamic> a, Map<T, dynamic> b) {
+Iterable<T> _allKeys<T>(Map<T, dynamic>? a, Map<T, dynamic>? b) {
   return <T>{...?a?.keys, ...?b?.keys};
 }
 
@@ -267,14 +266,14 @@
 
   Histogram._(this.bucketInfo, this.buckets)
       : bySize = buckets.keys.toList(growable: false)
-          ..sort((a, b) => buckets[b] - buckets[a]),
+          ..sort((a, b) => buckets[b]! - buckets[a]!),
         totalSize = buckets.values.fold(0, (sum, size) => sum + size);
 
   static Histogram fromIterable<T>(
     Iterable<T> entries, {
-    @required int Function(T) sizeOf,
-    @required String Function(T) bucketFor,
-    @required BucketInfo bucketInfo,
+    required int Function(T) sizeOf,
+    required String Function(T) bucketFor,
+    required BucketInfo bucketInfo,
   }) {
     final buckets = <String, int>{};
 
@@ -290,20 +289,26 @@
   /// Rebuckets the histogram given the new bucketing rule.
   Histogram map(String Function(String) bucketFor) {
     return Histogram.fromIterable(buckets.keys,
-        sizeOf: (key) => buckets[key],
+        sizeOf: (key) => buckets[key]!,
         bucketFor: bucketFor,
         bucketInfo: bucketInfo);
   }
 }
 
 /// Construct the histogram of specific [type] given a [ProgramInfo].
+///
+/// [filter] glob can be provided to skip some of the nodes in the [info]:
+/// a string is created which contains library name, class name and function
+/// name for the given node and if this string does not match the [filter]
+/// glob then this node is ignored.
 Histogram computeHistogram(ProgramInfo info, HistogramType type,
-    {String filter}) {
-  bool Function(String, String, String) matchesFilter;
+    {String? filter}) {
+  bool Function(String, String?, String?) matchesFilter;
 
   if (filter != null) {
     final re = RegExp(filter.replaceAll('*', '.*'), caseSensitive: false);
-    matchesFilter = (lib, cls, fun) => re.hasMatch("${lib}::${cls}.${fun}");
+    matchesFilter =
+        (lib, cls, fun) => re.hasMatch("${lib}::${cls ?? ''}.${fun ?? ''}");
   } else {
     matchesFilter = (_, __, ___) => true;
   }
@@ -318,10 +323,12 @@
       });
     }
 
+    final snapshotInfo = info.snapshotInfo!;
+
     return Histogram.fromIterable<Node>(
-        info.snapshotInfo.snapshot.nodes.where((n) =>
+        snapshotInfo.snapshot.nodes.where((n) =>
             filter == null ||
-            filteredNodes.contains(info.snapshotInfo.ownerOf(n).id)),
+            filteredNodes.contains(snapshotInfo.ownerOf(n).id)),
         sizeOf: (n) {
           return n.selfSize;
         },
@@ -329,17 +336,18 @@
         bucketInfo: const BucketInfo(nameComponents: ['Type']));
   } else {
     final buckets = <String, int>{};
-    final bucketing = Bucketing._forType[type];
+    final bucketing = Bucketing._forType[type]!;
 
     info.visit((pkg, lib, cls, fun, node) {
-      if (node.size == null || node.size == 0) {
+      final sz = node.size;
+      if (sz == null || sz == 0) {
         return;
       }
       if (!matchesFilter(lib, cls, fun)) {
         return;
       }
       final bucket = bucketing.bucketFor(pkg, lib, cls, fun);
-      buckets[bucket] = (buckets[bucket] ?? 0) + node.size;
+      buckets[bucket] = (buckets[bucket] ?? 0) + sz;
     });
 
     return Histogram._(bucketing, buckets);
@@ -363,15 +371,15 @@
   /// one returned by [nameComponents]).
   List<String> namesFromBucket(String bucket) => [bucket];
 
-  const BucketInfo({@required this.nameComponents});
+  const BucketInfo({required this.nameComponents});
 }
 
 abstract class Bucketing extends BucketInfo {
   /// Constructs the bucket name from the given library name [lib], class name
   /// [cls] and function name [fun].
-  String bucketFor(String pkg, String lib, String cls, String fun);
+  String bucketFor(String? pkg, String lib, String? cls, String? fun);
 
-  const Bucketing({@required List<String> nameComponents})
+  const Bucketing({required List<String> nameComponents})
       : super(nameComponents: nameComponents);
 
   static const _forType = {
@@ -387,11 +395,11 @@
 
 class _BucketBySymbol extends Bucketing {
   @override
-  String bucketFor(String pkg, String lib, String cls, String fun) {
+  String bucketFor(String? pkg, String lib, String? cls, String? fun) {
     if (fun == null) {
       return '@other${_nameSeparator}';
     }
-    return '$lib${_nameSeparator}${cls}${cls != '' ? '.' : ''}${fun}';
+    return '$lib${_nameSeparator}${cls ?? ''}${cls != '' && cls != null ? '.' : ''}${fun}';
   }
 
   @override
@@ -402,7 +410,7 @@
 
 class _BucketByClass extends Bucketing {
   @override
-  String bucketFor(String pkg, String lib, String cls, String fun) {
+  String bucketFor(String? pkg, String lib, String? cls, String? fun) {
     if (cls == null) {
       return '@other${_nameSeparator}';
     }
@@ -417,14 +425,14 @@
 
 class _BucketByLibrary extends Bucketing {
   @override
-  String bucketFor(String pkg, String lib, String cls, String fun) => '$lib';
+  String bucketFor(String? pkg, String lib, String? cls, String? fun) => '$lib';
 
   const _BucketByLibrary() : super(nameComponents: const ['Library']);
 }
 
 class _BucketByPackage extends Bucketing {
   @override
-  String bucketFor(String pkg, String lib, String cls, String fun) =>
+  String bucketFor(String? pkg, String lib, String? cls, String? fun) =>
       pkg ?? lib;
 
   const _BucketByPackage() : super(nameComponents: const ['Package']);
diff --git a/pkg/vm_snapshot_analysis/lib/src/commands/compare.dart b/pkg/vm_snapshot_analysis/lib/src/commands/compare.dart
index 4a9442e..bb2bdaf 100644
--- a/pkg/vm_snapshot_analysis/lib/src/commands/compare.dart
+++ b/pkg/vm_snapshot_analysis/lib/src/commands/compare.dart
@@ -64,23 +64,25 @@
 
   @override
   Future<void> run() async {
-    if (argResults.rest.length != 2) {
+    final args = argResults!;
+
+    if (args.rest.length != 2) {
       usageException('Need to provide path to old.json and new.json reports.');
     }
 
-    final columnWidth = argResults['column-width'];
+    final columnWidth = args['column-width'];
     final maxWidth = int.tryParse(columnWidth);
     if (maxWidth == null) {
       usageException(
           'Specified column width (${columnWidth}) is not an integer');
     }
 
-    final oldJsonPath = _checkExists(argResults.rest[0]);
-    final newJsonPath = _checkExists(argResults.rest[1]);
+    final oldJsonPath = _checkExists(args.rest[0]);
+    final newJsonPath = _checkExists(args.rest[1]);
     printComparison(oldJsonPath, newJsonPath,
         maxWidth: maxWidth,
-        granularity: _parseHistogramType(argResults['by']),
-        collapseAnonymousClosures: argResults['collapse-anonymous-closures']);
+        granularity: _parseHistogramType(args['by']),
+        collapseAnonymousClosures: args['collapse-anonymous-closures']);
   }
 
   HistogramType _parseHistogramType(String value) {
@@ -93,8 +95,9 @@
         return HistogramType.byLibrary;
       case 'package':
         return HistogramType.byPackage;
+      default:
+        usageException('Unrecognized histogram type $value');
     }
-    return null;
   }
 
   File _checkExists(String path) {
@@ -136,10 +139,10 @@
     printHistogram(diff, histogram,
         sizeHeader: 'Diff (Bytes)',
         prefix: histogram.bySize
-            .where((k) => histogram.buckets[k] > 0)
+            .where((k) => histogram.buckets[k]! > 0)
             .take(numLargerSymbolsToReport),
         suffix: histogram.bySize.reversed
-            .where((k) => histogram.buckets[k] < 0)
+            .where((k) => histogram.buckets[k]! < 0)
             .take(numSmallerSymbolsToReport)
             .toList()
             .reversed,
@@ -159,7 +162,7 @@
       final newTypeHistogram =
           computeHistogram(newSizes, HistogramType.byNodeType);
 
-      final diffTypeHistogram = Histogram.fromIterable(
+      final diffTypeHistogram = Histogram.fromIterable<String>(
           Set<String>()
             ..addAll(oldTypeHistogram.buckets.keys)
             ..addAll(newTypeHistogram.buckets.keys),
diff --git a/pkg/vm_snapshot_analysis/lib/src/commands/explain.dart b/pkg/vm_snapshot_analysis/lib/src/commands/explain.dart
index 33c1418..e81833f 100644
--- a/pkg/vm_snapshot_analysis/lib/src/commands/explain.dart
+++ b/pkg/vm_snapshot_analysis/lib/src/commands/explain.dart
@@ -53,13 +53,15 @@
 
   @override
   Future<void> run() async {
-    final sizesJson = File(argResults.rest[0]);
+    final args = argResults!;
+
+    final sizesJson = File(args.rest[0]);
     if (!sizesJson.existsSync()) {
       usageException('Size profile ${sizesJson.path} does not exist!');
     }
     final sizesJsonRaw = await loadJsonFromFile(sizesJson);
 
-    final traceJson = File(argResults.rest[1]);
+    final traceJson = File(args.rest[1]);
     if (!traceJson.existsSync()) {
       usageException('Size profile ${traceJson.path} does not exist!');
     }
@@ -103,7 +105,7 @@
     }, bucketInfo: BucketInfo(nameComponents: ['Selector']));
 
     printHistogram(programInfo, histogram,
-        prefix: histogram.bySize.where((key) => histogram.buckets[key] > 0));
+        prefix: histogram.bySize.where((key) => histogram.buckets[key]! > 0));
 
     // For top 10 dynamic selectors print the functions which contain these
     // dynamic calls.
diff --git a/pkg/vm_snapshot_analysis/lib/src/commands/summary.dart b/pkg/vm_snapshot_analysis/lib/src/commands/summary.dart
index 13854d2..90b87b9 100644
--- a/pkg/vm_snapshot_analysis/lib/src/commands/summary.dart
+++ b/pkg/vm_snapshot_analysis/lib/src/commands/summary.dart
@@ -12,7 +12,6 @@
 import 'dart:math' as math;
 
 import 'package:args/command_runner.dart';
-import 'package:meta/meta.dart';
 
 import 'package:vm_snapshot_analysis/ascii_table.dart';
 import 'package:vm_snapshot_analysis/precompiler_trace.dart';
@@ -95,18 +94,20 @@
 
   @override
   Future<void> run() async {
-    if (argResults.rest.length != 1) {
+    final args = argResults!;
+
+    if (args.rest.length != 1) {
       usageException('Need to specify input JSON.');
     }
 
-    final input = File(argResults.rest[0]);
+    final input = File(args.rest[0]);
     if (!input.existsSync()) {
       usageException('Input file ${input.path} does not exist!');
     }
 
-    final granularity = _parseHistogramType(argResults['by']);
+    final granularity = _parseHistogramType(args['by']);
 
-    final traceJson = argResults['precompiler-trace'];
+    final traceJson = args['precompiler-trace'];
     if (traceJson != null) {
       if (!File(traceJson).existsSync()) {
         usageException('Trace ${traceJson} does not exist!');
@@ -119,21 +120,21 @@
       }
     }
 
-    final columnWidth = argResults['column-width'];
+    final columnWidth = args['column-width'];
     final maxWidth = int.tryParse(columnWidth);
     if (maxWidth == null) {
       usageException(
           'Specified column width (${columnWidth}) is not an integer');
     }
 
-    final depsStartDepthStr = argResults['deps-start-depth'];
+    final depsStartDepthStr = args['deps-start-depth'];
     final depsStartDepth = int.tryParse(depsStartDepthStr);
     if (depsStartDepth == null) {
       usageException('Specified depsStartDepth (${depsStartDepthStr})'
           ' is not an integer');
     }
 
-    final depsDisplayDepthStr = argResults['deps-display-depth'];
+    final depsDisplayDepthStr = args['deps-display-depth'];
     final depsDisplayDepth = int.tryParse(depsDisplayDepthStr);
     if (depsDisplayDepth == null) {
       usageException('Specified depsDisplayDepth (${depsStartDepthStr})'
@@ -143,14 +144,14 @@
     await outputSummary(input,
         maxWidth: maxWidth,
         granularity: granularity,
-        collapseAnonymousClosures: argResults['collapse-anonymous-closures'],
-        filter: argResults['where'],
+        collapseAnonymousClosures: args['collapse-anonymous-closures'],
+        filter: args['where'],
         traceJson: traceJson != null ? File(traceJson) : null,
         depsStartDepth: depsStartDepth,
         depsDisplayDepth: depsDisplayDepth);
   }
 
-  static HistogramType _parseHistogramType(String value) {
+  HistogramType _parseHistogramType(String value) {
     switch (value) {
       case 'method':
         return HistogramType.bySymbol;
@@ -160,17 +161,18 @@
         return HistogramType.byLibrary;
       case 'package':
         return HistogramType.byPackage;
+      default:
+        usageException('Unrecognized histogram type $value');
     }
-    return null;
   }
 }
 
-void outputSummary(File input,
+Future<void> outputSummary(File input,
     {int maxWidth = 0,
     bool collapseAnonymousClosures = false,
     HistogramType granularity = HistogramType.bySymbol,
-    String filter,
-    File traceJson,
+    String? filter,
+    File? traceJson,
     int depsStartDepth = 2,
     int depsDisplayDepth = 4,
     int topToReport = 30}) async {
@@ -182,7 +184,7 @@
 
   // If precompiler trace is provided, collapse entries based on the dependency
   // graph (dominator tree) extracted from the trace.
-  void Function() printDependencyTrees;
+  void Function()? printDependencyTrees;
   if (traceJson != null &&
       (granularity == HistogramType.byLibrary ||
           granularity == HistogramType.byPackage)) {
@@ -235,8 +237,8 @@
       var totalCount = 1;
       for (var n in node.dominated) {
         computeTotalsRecursively(n);
-        totalSize += totalSizes[n.data.name];
-        totalCount += totalCounts[n.data.name];
+        totalSize += totalSizes[n.data.name]!;
+        totalCount += totalCounts[n.data.name]!;
       }
       totalSizes[node.data.name] = totalSize;
       totalCounts[node.data.name] = totalCount;
@@ -255,7 +257,7 @@
       final collapsedEntries = histogram.bySize
           .take(topToReport)
           .map((k) => collapsed[k])
-          .where((n) => n != null);
+          .whereType<CallGraphNode>();
       if (collapsedEntries.isNotEmpty) {
         print('\bDependency trees:');
         for (var n in collapsedEntries) {
@@ -302,9 +304,9 @@
 void _printDominatedNodes(CallGraphNode node,
     {int displayDepth = 4,
     int maxChildrenToPrint = 10,
-    List<bool> isLastPerLevel,
-    @required Map<String, int> totalSizes,
-    @required Map<String, int> totalCounts}) {
+    List<bool>? isLastPerLevel,
+    required Map<String, int> totalSizes,
+    required Map<String, int> totalCounts}) {
   isLastPerLevel ??= [];
 
   // Subtract one to account for the parent node that is printed before the
@@ -315,8 +317,8 @@
 
   final sizes = node.dominated.map((n) => totalSizes[n.data.name]).toList();
   final order = List.generate(node.dominated.length, (i) => i)
-    ..sort((a, b) => sizes[b] - sizes[a]);
-  final lastIndex = order.lastIndexWhere((i) => sizes[i] > 0);
+    ..sort((a, b) => sizes[b]! - sizes[a]!);
+  final lastIndex = order.lastIndexWhere((i) => sizes[i]! > 0);
 
   for (var j = 0, n = math.min(maxChildrenToPrint - 1, lastIndex);
       j <= n;
@@ -339,7 +341,7 @@
   if (maxChildrenToPrint < lastIndex) {
     isLastPerLevel.add(true);
     print(
-        '${_treeLines(isLastPerLevel)} ... (+${totalCounts[node.data.name] - 1} deps)');
+        '${_treeLines(isLastPerLevel)} ... (+${totalCounts[node.data.name]! - 1} deps)');
     isLastPerLevel.removeLast();
   }
 }
diff --git a/pkg/vm_snapshot_analysis/lib/src/commands/treemap.dart b/pkg/vm_snapshot_analysis/lib/src/commands/treemap.dart
index 059d353..2de0694 100644
--- a/pkg/vm_snapshot_analysis/lib/src/commands/treemap.dart
+++ b/pkg/vm_snapshot_analysis/lib/src/commands/treemap.dart
@@ -56,24 +56,27 @@
 
   @override
   Future<void> run() async {
-    if (argResults.rest.length != 2) {
+    final args = argResults!;
+
+    if (args.rest.length != 2) {
       usageException('Need to specify input JSON and output directory.');
     }
 
-    final input = File(argResults.rest[0]);
-    final outputDir = Directory(argResults.rest[1]);
+    final input = File(args.rest[0]);
+    final outputDir = Directory(args.rest[1]);
 
     if (!input.existsSync()) {
       usageException('Input file ${input.path} does not exist!');
     }
 
-    if (outputDir.existsSync() && !argResults['force']) {
+    if (outputDir.existsSync() && !args['force']) {
       usageException(
           'Output directory ${outputDir.path} already exists, specify --force to ignore.');
     }
 
     await generateTreeMap(input, outputDir,
-        format: _formatFromString[argResults['format']]);
+        format: _formatFromString[args['format']] ??
+            (throw 'Unrecognized format ${args['format']}'));
   }
 
   // Note: the first key in this map is the default format.
@@ -95,8 +98,8 @@
   // Create output directory and copy all auxiliary files from binary_size tool.
   await outputDir.create(recursive: true);
 
-  final assetsUri = await Isolate.resolvePackageUri(
-      Uri.parse('package:vm_snapshot_analysis/src/assets'));
+  final assetsUri = (await Isolate.resolvePackageUri(
+      Uri.parse('package:vm_snapshot_analysis/src/assets')))!;
   final assetsDir = assetsUri.toFilePath();
   final d3SrcDir = p.join(assetsDir, 'd3', 'src');
 
@@ -114,7 +117,7 @@
   final dataJsPath = p.join(outputDir.path, 'data.js');
   final sink = File(dataJsPath).openWrite();
   sink.write('var tree_data=');
-  await sink.addStream(Stream<Object>.fromIterable([tree])
+  await sink.addStream(Stream<Object?>.fromIterable([tree])
       .transform(json.encoder.fuse(utf8.encoder)));
   await sink.close();
 
diff --git a/pkg/vm_snapshot_analysis/lib/src/commands/utils.dart b/pkg/vm_snapshot_analysis/lib/src/commands/utils.dart
index f6a5bbe..c73dab8 100644
--- a/pkg/vm_snapshot_analysis/lib/src/commands/utils.dart
+++ b/pkg/vm_snapshot_analysis/lib/src/commands/utils.dart
@@ -6,9 +6,9 @@
 import 'dart:io';
 
 Future<Object> loadJsonFromFile(File input) async {
-  return await input
+  return (await input
       .openRead()
       .transform(utf8.decoder)
       .transform(json.decoder)
-      .first;
+      .first)!;
 }
diff --git a/pkg/vm_snapshot_analysis/lib/src/dominators.dart b/pkg/vm_snapshot_analysis/lib/src/dominators.dart
index 5e9078f..92504f3 100644
--- a/pkg/vm_snapshot_analysis/lib/src/dominators.dart
+++ b/pkg/vm_snapshot_analysis/lib/src/dominators.dart
@@ -13,16 +13,16 @@
 /// native compiler (see runtime/vm/compiler/backend/flow_graph.cc).
 @pragma('vm:prefer-inline')
 List<int> computeDominators({
-  int size,
-  int root,
-  Iterable<int> succ(int n),
-  Iterable<int> predOf(int n),
-  void handleEdge(int from, int to),
+  required int size,
+  required int root,
+  required Iterable<int> Function(int) succ,
+  required Iterable<int> Function(int) predOf,
+  required void handleEdge(int from, int to),
 }) {
   // Compute preorder numbering for the graph using DFS.
   final parent = List<int>.filled(size, -1);
-  final preorder = List<int>.filled(size, null);
-  final preorderNumber = List<int>.filled(size, null);
+  final preorder = List<int>.filled(size, -1);
+  final preorderNumber = List<int>.filled(size, -1);
 
   var N = 0;
   void dfs() {
@@ -32,7 +32,7 @@
       final p = s.p;
       final n = s.n;
       handleEdge(s.n, s.p);
-      if (preorderNumber[n] == null) {
+      if (preorderNumber[n] == -1) {
         preorderNumber[n] = N;
         preorder[preorderNumber[n]] = n;
         parent[preorderNumber[n]] = p;
@@ -122,5 +122,5 @@
 class _DfsState {
   final int p;
   final int n;
-  _DfsState({this.p, this.n});
+  _DfsState({required this.p, required this.n});
 }
diff --git a/pkg/vm_snapshot_analysis/lib/treemap.dart b/pkg/vm_snapshot_analysis/lib/treemap.dart
index c0535e7..2d6da72 100644
--- a/pkg/vm_snapshot_analysis/lib/treemap.dart
+++ b/pkg/vm_snapshot_analysis/lib/treemap.dart
@@ -82,10 +82,11 @@
   final root = {'n': '', 'children': {}, 'k': kindPath, 'maxDepth': 0};
 
   if (v8_profile.Snapshot.isV8HeapSnapshot(inputJson)) {
-    _treemapFromSnapshot(root, v8_profile.Snapshot.fromJson(inputJson),
+    _treemapFromSnapshot(
+        root, v8_profile.Snapshot.fromJson(inputJson as Map<String, dynamic>),
         format: format);
   } else {
-    final symbols = instruction_sizes.fromJson(inputJson);
+    final symbols = instruction_sizes.fromJson(inputJson as List<dynamic>);
     for (var symbol in symbols) {
       _addSymbol(root, _treePath(symbol), symbol.name.scrubbed, symbol.size);
     }
@@ -163,20 +164,22 @@
     return;
   }
 
+  final snapshotInfo = info.snapshotInfo!;
+
   final ownerPathCache =
-      List<String>.filled(info.snapshotInfo.infoNodes.length, null);
+      List<String?>.filled(snapshotInfo.infoNodes.length, null);
   ownerPathCache[info.root.id] = info.root.name;
 
   String ownerPath(ProgramInfoNode n) {
-    return ownerPathCache[n.id] ??=
-        ((n.parent != info.root) ? '${ownerPath(n.parent)}/${n.name}' : n.name);
+    return ownerPathCache[n.id] ??= ((n.parent != info.root)
+        ? '${ownerPath(n.parent!)}/${n.name}'
+        : n.name);
   }
 
-  final nameFormatter = _nameFormatters[format];
-
+  final nameFormatter = _nameFormatters[format]!;
   for (var node in snap.nodes) {
     if (node.selfSize > 0) {
-      final owner = info.snapshotInfo.ownerOf(node);
+      final owner = snapshotInfo.ownerOf(node);
 
       final name = nameFormatter(node);
       final path = ownerPath(owner);
@@ -227,7 +230,7 @@
 }
 
 /// Add the given symbol to the tree.
-void _addSymbol(Map<String, dynamic> root, String path, String name, int size,
+void _addSymbol(Map<String, dynamic> root, String path, String name, int? size,
     {String symbolType = symbolTypeGlobalText}) {
   if (size == null || size == 0) {
     return;
diff --git a/pkg/vm_snapshot_analysis/lib/utils.dart b/pkg/vm_snapshot_analysis/lib/utils.dart
index abf3eb3..40ef457 100644
--- a/pkg/vm_snapshot_analysis/lib/utils.dart
+++ b/pkg/vm_snapshot_analysis/lib/utils.dart
@@ -13,10 +13,11 @@
 ProgramInfo loadProgramInfoFromJson(Object json,
     {bool collapseAnonymousClosures = false}) {
   if (v8_profile.Snapshot.isV8HeapSnapshot(json)) {
-    return v8_profile.toProgramInfo(v8_profile.Snapshot.fromJson(json),
+    return v8_profile.toProgramInfo(
+        v8_profile.Snapshot.fromJson(json as Map<String, dynamic>),
         collapseAnonymousClosures: collapseAnonymousClosures);
   } else {
-    return instruction_sizes.loadProgramInfo(json,
+    return instruction_sizes.loadProgramInfo(json as List<dynamic>,
         collapseAnonymousClosures: collapseAnonymousClosures);
   }
 }
@@ -62,7 +63,7 @@
 
   final visibleRows = [prefix, suffix].expand((l) => l).toList();
   final visibleSize =
-      visibleRows.fold(0, (sum, key) => sum + histogram.buckets[key]);
+      visibleRows.fold<int>(0, (sum, key) => sum + histogram.buckets[key]!);
   final numRestRows = histogram.length - (suffix.length + prefix.length);
   final hiddenRows = Set<String>.from(histogram.bySize)
       .difference(Set<String>.from(visibleRows));
@@ -71,7 +72,7 @@
 
   if (prefix.isNotEmpty) {
     for (var key in prefix) {
-      final size = histogram.buckets[key];
+      final size = histogram.buckets[key]!;
       table.addRow([
         ...histogram.bucketInfo.namesFromBucket(key),
         size.toString(),
@@ -99,7 +100,7 @@
       table.addRow([
         ...histogram.bucketInfo.namesFromBucket(key),
         histogram.buckets[key].toString(),
-        formatPercent(histogram.buckets[key], histogram.totalSize),
+        formatPercent(histogram.buckets[key]!, histogram.totalSize),
       ]);
     }
     table.addSeparator(Separator.Line);
diff --git a/pkg/vm_snapshot_analysis/lib/v8_profile.dart b/pkg/vm_snapshot_analysis/lib/v8_profile.dart
index cbbe717..7dde867 100644
--- a/pkg/vm_snapshot_analysis/lib/v8_profile.dart
+++ b/pkg/vm_snapshot_analysis/lib/v8_profile.dart
@@ -6,7 +6,7 @@
 /// produced by `--write-v8-snapshot-profile-to` VM flag.
 library vm_snapshot_analysis.v8_profile;
 
-import 'package:meta/meta.dart';
+import 'package:collection/collection.dart';
 
 import 'package:vm_snapshot_analysis/src/dominators.dart' as dominators;
 import 'package:vm_snapshot_analysis/name.dart';
@@ -35,7 +35,7 @@
   /// for the given node index.
   final List<int> _edgesStartIndexForNode;
 
-  List<int> _dominators;
+  late final List<int> _dominators = _computeDominators(this);
 
   final List strings;
 
@@ -53,7 +53,6 @@
 
   /// Return dominator node for the given node [n].
   Node dominatorOf(Node n) {
-    _dominators ??= _computeDominators(this);
     return nodeAt(_dominators[n.index]);
   }
 
@@ -67,7 +66,7 @@
     // Extract meta information first.
     final meta = Meta._fromJson(m['snapshot']['meta']);
 
-    final nodes = m['nodes'];
+    final nodes = (m['nodes'] as List<dynamic>).cast<int>();
 
     // Build an array of starting indexes of edges for each node.
     final edgesStartIndexForNode = <int>[0];
@@ -130,18 +129,18 @@
   final List<String> edgeTypes;
 
   Meta._(
-      {this.nodeTypeIndex,
-      this.nodeNameIndex,
-      this.nodeIdIndex,
-      this.nodeSelfSizeIndex,
-      this.nodeEdgeCountIndex,
-      this.nodeFieldCount,
-      this.edgeTypeIndex,
-      this.edgeNameOrIndexIndex,
-      this.edgeToNodeIndex,
-      this.edgeFieldCount,
-      this.nodeTypes,
-      this.edgeTypes});
+      {required this.nodeTypeIndex,
+      required this.nodeNameIndex,
+      required this.nodeIdIndex,
+      required this.nodeSelfSizeIndex,
+      required this.nodeEdgeCountIndex,
+      required this.nodeFieldCount,
+      required this.edgeTypeIndex,
+      required this.edgeNameOrIndexIndex,
+      required this.edgeToNodeIndex,
+      required this.edgeFieldCount,
+      required this.nodeTypes,
+      required this.edgeTypes});
 
   factory Meta._fromJson(Map<String, dynamic> m) {
     final nodeFields = m['node_fields'];
@@ -171,7 +170,7 @@
   /// Index of this [Edge] within the [snapshot].
   final int index;
 
-  Edge._({this.snapshot, this.index});
+  Edge._({required this.snapshot, required this.index});
 
   String get type => snapshot
       .meta.edgeTypes[snapshot._edges[_offset + snapshot.meta.edgeTypeIndex]];
@@ -212,7 +211,7 @@
   /// Index of this [Node] within the [snapshot].
   final int index;
 
-  Node._({this.snapshot, this.index});
+  Node._({required this.snapshot, required this.index});
 
   int get edgeCount =>
       snapshot._nodes[_offset + snapshot.meta.nodeEdgeCountIndex];
@@ -248,10 +247,8 @@
   }
 
   /// Returns the target of an outgoing edge with the given name (if any).
-  Node operator [](String edgeName) => this
-      .edges
-      .firstWhere((e) => e.name == edgeName, orElse: () => null)
-      ?.target;
+  Node? operator [](String edgeName) =>
+      this.edges.firstWhereOrNull((e) => e.name == edgeName)?.target;
 
   @override
   bool operator ==(Object other) {
@@ -317,7 +314,7 @@
   /// See [findCommonAncestor] method.
   final Map<int, int> commonAncestorCache = {};
 
-  _ProgramInfoBuilder({this.collapseAnonymousClosures});
+  _ProgramInfoBuilder({required this.collapseAnonymousClosures});
 
   /// Recover [ProgramInfo] structure from the snapshot profile.
   ///
@@ -372,7 +369,7 @@
     return program;
   }
 
-  ProgramInfoNode getInfoNodeFor(Node node) {
+  ProgramInfoNode? getInfoNodeFor(Node node) {
     var info = infoNodeByIndex[node.index];
     if (info == null) {
       info = createInfoNodeFor(node);
@@ -390,7 +387,7 @@
         switch (node.type) {
           case 'Code':
             // Freeze ownership of the Instructions object.
-            final instructions = node['<instructions>'];
+            final instructions = node['<instructions>']!;
             nodesWithFrozenOwner.add(instructions.index);
             ownerOf[instructions.index] =
                 findCommonAncestor(ownerOf[instructions.index], info.id);
@@ -403,7 +400,7 @@
                 if (e.target.type == 'Script') {
                   nodesWithFrozenOwner.add(e.target.index);
                   ownerOf[e.target.index] =
-                      findCommonAncestor(ownerOf[e.target.index], info.id);
+                      findCommonAncestor(ownerOf[e.target.index]!, info.id);
                 }
               }
             }
@@ -414,13 +411,13 @@
     return info;
   }
 
-  ProgramInfoNode createInfoNodeFor(Node node) {
+  ProgramInfoNode? createInfoNodeFor(Node node) {
     switch (node.type) {
       case 'Code':
-        var owner = node['owner_'];
+        final owner = node['owner_']!;
         if (owner.type != 'Type') {
           final ownerNode =
-              owner.type == 'Null' ? program.stubs : getInfoNodeFor(owner);
+              owner.type == 'Null' ? program.stubs : getInfoNodeFor(owner)!;
           if (owner.type == 'Function') {
             // For normal functions we just attribute Code object and all
             // objects dominated by it to the function itself.
@@ -436,29 +433,29 @@
 
       case 'Function':
         if (node.name != '<anonymous signature>') {
-          var owner = node['owner_'];
+          var owner = node['owner_']!;
 
           // Artificial nodes may not have a data_ field.
           var data = node['data_'];
-          if (data?.type == 'ClosureData') {
-            owner = data['parent_function_'];
+          if (data != null && data.type == 'ClosureData') {
+            owner = data['parent_function_']!;
           }
           return makeInfoNode(node.index,
               name: node.name,
-              parent: getInfoNodeFor(owner),
+              parent: getInfoNodeFor(owner)!,
               type: NodeType.functionNode);
         }
         break;
 
       case 'PatchClass':
-        return getInfoNodeFor(node['patched_class_']);
+        return getInfoNodeFor(node['patched_class_']!);
 
       case 'Class':
         // Default to root node. Some builtin classes (void, dynamic) don't have
         // any information about their library written out.
         var ownerNode = program.root;
         if (node['library_'] != null) {
-          ownerNode = getInfoNodeFor(node['library_']) ?? ownerNode;
+          ownerNode = getInfoNodeFor(node['library_']!) ?? ownerNode;
         }
 
         return makeInfoNode(node.index,
@@ -477,20 +474,16 @@
       case 'Field':
         return makeInfoNode(node.index,
             name: node.name,
-            parent: getInfoNodeFor(node['owner_']),
+            parent: getInfoNodeFor(node['owner_']!)!,
             type: NodeType.other);
     }
     return null;
   }
 
-  ProgramInfoNode makeInfoNode(int index,
-      {@required ProgramInfoNode parent,
-      @required String name,
-      @required NodeType type}) {
-    assert(parent != null,
-        'Trying to create node of type ${type} with ${name} and no parent.');
-    assert(name != null);
-
+  ProgramInfoNode makeInfoNode(int? index,
+      {required ProgramInfoNode parent,
+      required String name,
+      required NodeType type}) {
     name = Name(name).scrubbed;
     if (collapseAnonymousClosures) {
       name = Name.collapse(name);
@@ -527,10 +520,10 @@
   }
 
   /// Returns id of a common ancestor between [ProgramInfoNode] with [idA] and
-  /// [idB].
-  int findCommonAncestor(int idA, int idB) {
+  /// [idB]. At least either [idA] or [idB] are expected to be not null.
+  int findCommonAncestor(int? idA, int? idB) {
     if (idA == null) {
-      return idB;
+      return idB!;
     }
     if (idB == null) {
       return idA;
@@ -558,9 +551,8 @@
 
   static List<ProgramInfoNode> pathToRoot(ProgramInfoNode node) {
     final path = <ProgramInfoNode>[];
-    while (node != null) {
-      path.add(node);
-      node = node.parent;
+    for (ProgramInfoNode? n = node; n != null; n = n.parent) {
+      path.add(n);
     }
     return path;
   }
@@ -609,14 +601,15 @@
 /// The code for dominator tree computation is taken verbatim from the
 /// native compiler (see runtime/vm/compiler/backend/flow_graph.cc).
 List<int> _computeDominators(Snapshot snap) {
-  final predecessors = List<Object>.filled(snap.nodeCount, null);
+  final predecessors = List<Object?>.filled(snap.nodeCount, null);
   void addPred(int n, int p) {
-    if (predecessors[n] == null) {
+    final pred = predecessors[n];
+    if (pred == null) {
       predecessors[n] = p;
-    } else if (predecessors[n] is int) {
-      predecessors[n] = <int>[predecessors[n], p];
+    } else if (pred is int) {
+      predecessors[n] = <int>[pred, p];
     } else {
-      (predecessors[n] as List<int>).add(p);
+      (pred as List<int>).add(p);
     }
   }
 
diff --git a/pkg/vm_snapshot_analysis/pubspec.yaml b/pkg/vm_snapshot_analysis/pubspec.yaml
index 5b75e91..3a37d38 100644
--- a/pkg/vm_snapshot_analysis/pubspec.yaml
+++ b/pkg/vm_snapshot_analysis/pubspec.yaml
@@ -5,7 +5,7 @@
 homepage: https://github.com/dart-lang/sdk/tree/master/pkg/vm_snapshot_analysis
 
 environment:
-  sdk: '>=2.8.0 <3.0.0'
+  sdk: '>=2.12.0 <3.0.0'
 
 executables:
   snapshot_analysis: analyse
@@ -13,7 +13,7 @@
 dependencies:
   args: ^2.0.0
   path: ^1.8.0
-  meta: ^1.3.0
+  collection: ^1.15.0
 
 dev_dependencies:
   pedantic: ^1.11.0
diff --git a/pkg/vm_snapshot_analysis/test/instruction_sizes_test.dart b/pkg/vm_snapshot_analysis/test/instruction_sizes_test.dart
index 5347acc..16b2504 100644
--- a/pkg/vm_snapshot_analysis/test/instruction_sizes_test.dart
+++ b/pkg/vm_snapshot_analysis/test/instruction_sizes_test.dart
@@ -5,6 +5,8 @@
 import 'dart:io';
 
 import 'package:test/test.dart';
+import 'package:collection/collection.dart';
+
 import 'package:vm_snapshot_analysis/instruction_sizes.dart'
     as instruction_sizes;
 import 'package:vm_snapshot_analysis/program_info.dart';
@@ -242,7 +244,7 @@
     test('basic-parsing', () async {
       await withSymbolSizes(testSource, (sizesJson) async {
         final json = await loadJson(File(sizesJson));
-        final symbols = instruction_sizes.fromJson(json);
+        final symbols = instruction_sizes.fromJson(json as List<dynamic>);
         expect(symbols, isNotNull,
             reason: 'Sizes file was successfully parsed');
         expect(symbols.length, greaterThan(0),
@@ -254,7 +256,7 @@
         final symbolScrubbedNames = <String, Map<String, Set<String>>>{};
 
         Set<String> getSetOfNames(Map<String, Map<String, Set<String>>> map,
-            String libraryUri, String className) {
+            String? libraryUri, String? className) {
           return map
               .putIfAbsent(libraryUri ?? '', () => {})
               .putIfAbsent(className ?? '', () => {});
@@ -280,10 +282,12 @@
         expect(inputDartSymbolNames, isNotNull,
             reason: 'Symbols from input.dart are included into sizes output');
 
+        inputDartSymbolNames!; // Checked above. Promote the variable type.
+
         expect(inputDartSymbolNames[''], isNotNull,
             reason: 'Should include top-level members from input.dart');
         expect(inputDartSymbolNames[''], contains('makeSomeClosures'));
-        final closures = inputDartSymbolNames[''].where(
+        final closures = inputDartSymbolNames['']!.where(
             (name) => name.startsWith('makeSomeClosures.<anonymous closure'));
         expect(closures.length, 3,
             reason: 'There are three closures inside makeSomeClosure');
@@ -311,7 +315,7 @@
         expect(inputDartSymbolNames['D'], contains('[tear-off] tornOff'));
 
         // Check that output does not contain '[unknown stub]'
-        expect(symbolRawNames[''][''], isNot(contains('[unknown stub]')),
+        expect(symbolRawNames['']![''], isNot(contains('[unknown stub]')),
             reason: 'All stubs must be named');
       });
     });
@@ -324,19 +328,18 @@
         expect(info.root.children, contains('dart:typed_data'));
         expect(info.root.children, contains('package:input'));
 
-        final inputLib = info.root.children['package:input']
-            .children['package:input/input.dart'];
-        expect(inputLib, isNotNull);
+        final inputLib = info.root.children['package:input']!
+            .children['package:input/input.dart']!;
         expect(inputLib.children, contains('')); // Top-level class.
         expect(inputLib.children, contains('A'));
         expect(inputLib.children, contains('B'));
         expect(inputLib.children, contains('C'));
         expect(inputLib.children, contains('D'));
 
-        final topLevel = inputLib.children[''];
+        final topLevel = inputLib.children['']!;
         expect(topLevel.children, contains('makeSomeClosures'));
         expect(
-            topLevel.children['makeSomeClosures'].children.length, equals(3));
+            topLevel.children['makeSomeClosures']!.children.length, equals(3));
 
         for (var name in [
           '[tear-off] tornOff',
@@ -344,8 +347,8 @@
           'Allocate A',
           '[tear-off-extractor] get:tornOff'
         ]) {
-          expect(inputLib.children['A'].children, contains(name));
-          expect(inputLib.children['A'].children[name].children, isEmpty);
+          expect(inputLib.children['A']!.children, contains(name));
+          expect(inputLib.children['A']!.children[name]!.children, isEmpty);
         }
 
         for (var name in [
@@ -354,13 +357,13 @@
           'Allocate B',
           '[tear-off-extractor] get:tornOff'
         ]) {
-          expect(inputLib.children['B'].children, contains(name));
-          expect(inputLib.children['B'].children[name].children, isEmpty);
+          expect(inputLib.children['B']!.children, contains(name));
+          expect(inputLib.children['B']!.children[name]!.children, isEmpty);
         }
 
         for (var name in ['tornOff{body}', 'tornOff', '[tear-off] tornOff']) {
-          expect(inputLib.children['C'].children, contains(name));
-          expect(inputLib.children['C'].children[name].children, isEmpty);
+          expect(inputLib.children['C']!.children, contains(name));
+          expect(inputLib.children['C']!.children[name]!.children, isEmpty);
         }
 
         for (var name in [
@@ -369,8 +372,8 @@
           'tornOff',
           '[tear-off] tornOff'
         ]) {
-          expect(inputLib.children['D'].children, contains(name));
-          expect(inputLib.children['D'].children[name].children, isEmpty);
+          expect(inputLib.children['D']!.children, contains(name));
+          expect(inputLib.children['D']!.children[name]!.children, isEmpty);
         }
       });
     });
@@ -435,6 +438,64 @@
       });
     });
 
+    test('histograms-filter', () async {
+      await withSymbolSizes(testSource, (sizesJson) async {
+        final json = await loadJson(File(sizesJson));
+        final info = loadProgramInfoFromJson(json);
+
+        {
+          final h = computeHistogram(info, HistogramType.bySymbol,
+              filter: 'A.tornOff');
+          expect(
+              h.buckets,
+              contains(h.bucketFor('package:input', 'package:input/input.dart',
+                  'A', 'tornOff')));
+          expect(
+              h.buckets,
+              isNot(contains(h.bucketFor('package:input',
+                  'package:input/input.dart', 'B', 'tornOff'))));
+        }
+
+        {
+          final h = computeHistogram(info, HistogramType.bySymbol,
+              filter: '::A*tornOff');
+          expect(
+              h.buckets,
+              contains(h.bucketFor('package:input', 'package:input/input.dart',
+                  'A', 'tornOff')));
+          expect(
+              h.buckets,
+              isNot(contains(h.bucketFor('package:input',
+                  'package:input/input.dart', 'B', 'tornOff'))));
+        }
+
+        {
+          final h = computeHistogram(info, HistogramType.bySymbol,
+              filter: 'input.dart*tornOff');
+          expect(
+              h.buckets,
+              contains(h.bucketFor('package:input', 'package:input/input.dart',
+                  'A', 'tornOff')));
+          expect(
+              h.buckets,
+              contains(h.bucketFor('package:input', 'package:input/input.dart',
+                  'B', 'tornOff')));
+        }
+
+        {
+          final h =
+              computeHistogram(info, HistogramType.byPackage, filter: 'A');
+          expect(
+              h.buckets,
+              contains(h.bucketFor(
+                  'package:input',
+                  'package:input/does-not-matter.dart',
+                  'does-not-matter',
+                  'does-not-matter')));
+        }
+      });
+    });
+
     test('diff', () async {
       await withSymbolSizes(testSource, (sizesJson) async {
         await withSymbolSizes(testSourceModified, (modifiedSizesJson) async {
@@ -534,18 +595,17 @@
         expect(info.root.children, contains('dart:typed_data'));
         expect(info.root.children, contains('package:input'));
 
-        final inputLib = info.root.children['package:input']
-            .children['package:input/input.dart'];
-        expect(inputLib, isNotNull);
+        final inputLib = info.root.children['package:input']!
+            .children['package:input/input.dart']!;
         expect(inputLib.children, contains('::')); // Top-level class.
         expect(inputLib.children, contains('A'));
         expect(inputLib.children, contains('B'));
         expect(inputLib.children, contains('C'));
 
-        final topLevel = inputLib.children['::'];
+        final topLevel = inputLib.children['::']!;
         expect(topLevel.children, contains('makeSomeClosures'));
         expect(
-            topLevel.children['makeSomeClosures'].children.values
+            topLevel.children['makeSomeClosures']!.children.values
                 .where((child) => child.type == NodeType.functionNode)
                 .length,
             equals(3));
@@ -555,9 +615,9 @@
           'Allocate A',
           '[tear-off-extractor] get:tornOff'
         ]) {
-          expect(inputLib.children['A'].children, contains(name));
+          expect(inputLib.children['A']!.children, contains(name));
         }
-        expect(inputLib.children['A'].children['tornOff'].children,
+        expect(inputLib.children['A']!.children['tornOff']!.children,
             contains('[tear-off] tornOff'));
 
         for (var name in [
@@ -565,35 +625,35 @@
           'Allocate B',
           '[tear-off-extractor] get:tornOff'
         ]) {
-          expect(inputLib.children['B'].children, contains(name));
+          expect(inputLib.children['B']!.children, contains(name));
         }
-        expect(inputLib.children['B'].children['tornOff'].children,
+        expect(inputLib.children['B']!.children['tornOff']!.children,
             contains('[tear-off] tornOff'));
 
-        final classC = inputLib.children['C'];
+        final classC = inputLib.children['C']!;
         expect(classC.children, contains('tornOff'));
         for (var name in ['tornOff{body}', '[tear-off] tornOff']) {
-          expect(classC.children['tornOff'].children, contains(name));
+          expect(classC.children['tornOff']!.children, contains(name));
         }
 
         // Verify that [ProgramInfoNode] owns its corresponding snapshot [Node].
-        final classesOwnedByC = info.snapshotInfo.snapshot.nodes
-            .where((n) => info.snapshotInfo.ownerOf(n) == classC)
+        final classesOwnedByC = info.snapshotInfo!.snapshot.nodes
+            .where((n) => info.snapshotInfo!.ownerOf(n) == classC)
             .where((n) => n.type == 'Class')
             .map((n) => n.name);
         expect(classesOwnedByC, equals(['C']));
 
-        final classD = inputLib.children['D'];
+        final classD = inputLib.children['D']!;
         expect(classD.children, contains('tornOff'));
         for (var name in ['tornOff{body}', '[tear-off] tornOff']) {
-          expect(classD.children['tornOff'].children, contains(name));
+          expect(classD.children['tornOff']!.children, contains(name));
         }
-        expect(classD.children['tornOff'].children['tornOff{body}'].children,
+        expect(classD.children['tornOff']!.children['tornOff{body}']!.children,
             contains('tornOff{body depth 2}'));
 
         // Verify that [ProgramInfoNode] owns its corresponding snapshot [Node].
-        final classesOwnedByD = info.snapshotInfo.snapshot.nodes
-            .where((n) => info.snapshotInfo.ownerOf(n) == classD)
+        final classesOwnedByD = info.snapshotInfo!.snapshot.nodes
+            .where((n) => info.snapshotInfo!.ownerOf(n) == classD)
             .where((n) => n.type == 'Class')
             .map((n) => n.name);
         expect(classesOwnedByD, equals(['D']));
@@ -616,20 +676,20 @@
             '::',
             'makeSomeClosures'
           ]);
-          final codeNode = fromProfile.snapshotInfo.snapshot.nodes.firstWhere(
+          final codeNode = fromProfile.snapshotInfo!.snapshot.nodes.firstWhere(
               (n) =>
                   n.type == 'Code' &&
-                  fromProfile.snapshotInfo.ownerOf(n) == functionNode &&
+                  fromProfile.snapshotInfo!.ownerOf(n) == functionNode &&
                   n.name.contains('makeSomeClosures'));
           expect(codeNode['<instructions>'], isNotNull);
-          final instructionsSize = codeNode['<instructions>'].selfSize;
+          final instructionsSize = codeNode['<instructions>']!.selfSize;
           final symbolSize = fromSymbolSizes.lookup([
             'package:input',
             'package:input/input.dart',
             '',
             'makeSomeClosures'
-          ]).size;
-          expect(instructionsSize - symbolSize, equals(0));
+          ])!.size;
+          expect(instructionsSize - symbolSize!, equals(0));
         });
       });
     });
@@ -786,9 +846,10 @@
 
         String nameOf(Map<String, dynamic> node) => node['n'];
 
-        Map<String, dynamic> findChild(Map<String, dynamic> node, String name) {
+        Map<String, dynamic>? findChild(
+            Map<String, dynamic> node, String name) {
           return childrenOf(node)
-              .firstWhere((child) => nameOf(child) == name, orElse: () => null);
+              .firstWhereOrNull((child) => nameOf(child) == name);
         }
 
         Set<String> childrenNames(Map<String, dynamic> node) {
@@ -802,7 +863,7 @@
           // for some reason.
           expect(findChild(treemap, 'package:input/input.dart'), isNotNull);
         } else {
-          expect(childrenNames(findChild(treemap, 'package:input')),
+          expect(childrenNames(findChild(treemap, 'package:input')!),
               equals({'main.dart', 'input.dart'}));
         }
       });
@@ -813,7 +874,7 @@
         // Note: computing dominators also verifies that we don't have
         // unreachable nodes in the snapshot.
         final infoJson = await loadJson(File(profileJson));
-        final snapshot = Snapshot.fromJson(infoJson);
+        final snapshot = Snapshot.fromJson(infoJson as Map<String, dynamic>);
         for (var n in snapshot.nodes.skip(1)) {
           expect(snapshot.dominatorOf(n), isNotNull);
         }
@@ -839,7 +900,7 @@
 // simply ignore entry point library (main.dart).
 // Additionally this function removes all nodes with the size below
 // the given threshold.
-Map<String, dynamic> diffToJson(ProgramInfo diff,
+Map<String, dynamic>? diffToJson(ProgramInfo diff,
     {bool keepOnlyInputPackage = false}) {
   final diffJson = diff.toJson();
   diffJson.removeWhere((key, _) =>
@@ -847,7 +908,7 @@
 
   // Rebuild the diff JSON discarding all nodes with size below threshold.
   const smallChangeThreshold = 16;
-  Map<String, dynamic> discardSmallChanges(Map<String, dynamic> map) {
+  Map<String, dynamic>? discardSmallChanges(Map<String, dynamic> map) {
     final result = <String, dynamic>{};
 
     // First recursively process all children (skipping #type and #size keys).
diff --git a/pkg/vm_snapshot_analysis/test/precompiler_trace_test.dart b/pkg/vm_snapshot_analysis/test/precompiler_trace_test.dart
index 2392171..f00e977 100644
--- a/pkg/vm_snapshot_analysis/test/precompiler_trace_test.dart
+++ b/pkg/vm_snapshot_analysis/test/precompiler_trace_test.dart
@@ -75,7 +75,7 @@
         callGraph.computeDominators();
 
         final main = callGraph.program
-            .lookup(['package:input', 'package:input/input.dart', '', 'main']);
+            .lookup(['package:input', 'package:input/input.dart', '', 'main'])!;
         final mainNode = callGraph.lookup(main);
 
         final retainedClasses = mainNode.dominated
diff --git a/pkg/vm_snapshot_analysis/test/utils.dart b/pkg/vm_snapshot_analysis/test/utils.dart
index 830ca06..1163ae7 100644
--- a/pkg/vm_snapshot_analysis/test/utils.dart
+++ b/pkg/vm_snapshot_analysis/test/utils.dart
@@ -24,7 +24,7 @@
   final String outputBinary;
   final String sizesJson;
 
-  AotSnapshot({this.outputBinary, this.sizesJson});
+  AotSnapshot({required this.outputBinary, required this.sizesJson});
 }
 
 Future withFlag(
@@ -33,7 +33,7 @@
 }
 
 Future withFlagImpl(
-    Map<String, String> source, String flag, Future Function(AotSnapshot) f) {
+    Map<String, String> source, String? flag, Future Function(AotSnapshot) f) {
   return withTempDir((dir) async {
     final snapshot = AotSnapshot(
       outputBinary: path.join(dir, 'output.exe'),
@@ -90,7 +90,8 @@
   });
 }
 
-const keepTempKey = 'KEEP_TEMPORARY_DIRECTORIES';
+late final shouldKeepTemporaryDirectories =
+    Platform.environment['KEEP_TEMPORARY_DIRECTORIES']?.isNotEmpty == true;
 
 Future withTempDir(Future Function(String dir) f) async {
   final tempDir =
@@ -98,17 +99,16 @@
   try {
     await f(tempDir.path);
   } finally {
-    if (!Platform.environment.containsKey(keepTempKey) ||
-        Platform.environment[keepTempKey].isEmpty) {
+    if (shouldKeepTemporaryDirectories) {
       tempDir.deleteSync(recursive: true);
     }
   }
 }
 
 Future<Object> loadJson(File input) async {
-  return await input
+  return (await input
       .openRead()
       .transform(utf8.decoder)
       .transform(json.decoder)
-      .first;
+      .first)!;
 }
diff --git a/runtime/bin/ffi_test/ffi_test_functions_generated.cc b/runtime/bin/ffi_test/ffi_test_functions_generated.cc
index 6d3a2cf..8a23e24 100644
--- a/runtime/bin/ffi_test/ffi_test_functions_generated.cc
+++ b/runtime/bin/ffi_test/ffi_test_functions_generated.cc
@@ -518,7 +518,33 @@
   Struct5BytesPackedMixed a0[3];
 };
 
-// Used for testing structs by value.
+union Union4BytesMixed {
+  uint32_t a0;
+  float a1;
+};
+
+union Union8BytesNestedFloat {
+  double a0;
+  Struct8BytesHomogeneousFloat a1;
+};
+
+union Union9BytesNestedInt {
+  Struct8BytesInt a0;
+  Struct9BytesHomogeneousUint8 a1;
+};
+
+union Union16BytesNestedInlineArrayFloat {
+  float a0[4];
+  Struct16BytesHomogeneousFloat a1;
+};
+
+union Union16BytesNestedFloat {
+  Struct8BytesHomogeneousFloat a0;
+  Struct12BytesHomogeneousFloat a1;
+  Struct16BytesHomogeneousFloat a2;
+};
+
+// Used for testing structs and unions by value.
 // Smallest struct with data.
 // 10 struct arguments will exhaust available registers.
 DART_EXPORT int64_t PassStruct1ByteIntx10(Struct1ByteInt a0,
@@ -559,7 +585,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Not a multiple of word size, not a power of two.
 // 10 struct arguments will exhaust available registers.
 DART_EXPORT int64_t
@@ -635,7 +661,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Not a multiple of word size, not a power of two.
 // With alignment rules taken into account size is 4 bytes.
 // 10 struct arguments will exhaust available registers.
@@ -691,7 +717,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Exactly word size on 32-bit architectures.
 // 10 struct arguments will exhaust available registers.
 DART_EXPORT int64_t
@@ -742,7 +768,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Sub word size on 64 bit architectures.
 // 10 struct arguments will exhaust available registers.
 DART_EXPORT int64_t
@@ -874,7 +900,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Sub word size on 64 bit architectures.
 // With alignment rules taken into account size is 8 bytes.
 // 10 struct arguments will exhaust available registers.
@@ -942,7 +968,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Exactly word size struct on 64bit architectures.
 // 10 struct arguments will exhaust available registers.
 DART_EXPORT int64_t PassStruct8BytesIntx10(Struct8BytesInt a0,
@@ -1005,7 +1031,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Arguments passed in FP registers as long as they fit.
 // 10 struct arguments will exhaust available registers.
 DART_EXPORT float PassStruct8BytesHomogeneousFloatx10(
@@ -1056,7 +1082,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // On x64, arguments go in int registers because it is not only float.
 // 10 struct arguments will exhaust available registers.
 DART_EXPORT float PassStruct8BytesMixedx10(Struct8BytesMixed a0,
@@ -1119,7 +1145,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Argument is a single byte over a multiple of word size.
 // 10 struct arguments will exhaust available registers.
 // Struct only has 1-byte aligned fields to test struct alignment itself.
@@ -1284,7 +1310,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Argument is a single byte over a multiple of word size.
 // With alignment rules taken into account size is 12 or 16 bytes.
 // 10 struct arguments will exhaust available registers.
@@ -1341,7 +1367,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Arguments in FPU registers on arm hardfp and arm64.
 // Struct arguments will exhaust available registers, and leave some empty.
 // The last argument is to test whether arguments are backfilled.
@@ -1387,7 +1413,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // On Linux x64 argument is transferred on stack because it is over 16 bytes.
 // Arguments in FPU registers on arm hardfp and arm64.
 // 5 struct arguments will exhaust available registers.
@@ -1434,7 +1460,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // On x64, arguments are split over FP and int registers.
 // On x64, it will exhaust the integer registers with the 6th argument.
 // The rest goes on the stack.
@@ -1486,7 +1512,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // On x64, arguments are split over FP and int registers.
 // On x64, it will exhaust the integer registers with the 6th argument.
 // The rest goes on the stack.
@@ -1564,7 +1590,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Arguments are passed as pointer to copy on arm64.
 // Tests that the memory allocated for copies are rounded up to word size.
 DART_EXPORT int64_t PassStruct17BytesIntx10(Struct17BytesInt a0,
@@ -1630,7 +1656,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // The minimum alignment of this struct is only 1 byte based on its fields.
 // Test that the memory backing these structs is extended to the right size.
 //
@@ -1943,7 +1969,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Argument too big to go into integer registers on arm64.
 // The arguments are passed as pointers to copies.
 // The amount of arguments exhausts the number of integer registers, such that
@@ -2034,7 +2060,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Argument too big to go into FPU registers in hardfp and arm64.
 DART_EXPORT float PassStruct20BytesHomogeneousFloat(
     Struct20BytesHomogeneousFloat a0) {
@@ -2056,7 +2082,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Arguments in FPU registers on arm64.
 // 5 struct arguments will exhaust available registers.
 DART_EXPORT double PassStruct32BytesHomogeneousDoublex5(
@@ -2102,7 +2128,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Argument too big to go into FPU registers in arm64.
 DART_EXPORT double PassStruct40BytesHomogeneousDouble(
     Struct40BytesHomogeneousDouble a0) {
@@ -2124,7 +2150,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test 1kb struct.
 DART_EXPORT uint64_t
 PassStruct1024BytesHomogeneousUint64(Struct1024BytesHomogeneousUint64 a0) {
@@ -2304,7 +2330,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Tests the alignment of structs in FPU registers and backfilling.
 DART_EXPORT float PassFloatStruct16BytesHomogeneousFloatFloatStruct1(
     float a0,
@@ -2354,7 +2380,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Tests the alignment of structs in FPU registers and backfilling.
 DART_EXPORT double PassFloatStruct32BytesHomogeneousDoubleFloatStruct(
     float a0,
@@ -2404,7 +2430,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Tests the alignment of structs in integers registers and on the stack.
 // Arm32 aligns this struct at 8.
 // Also, arm32 allocates the second struct partially in registers, partially
@@ -2449,7 +2475,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // On Linux x64, it will exhaust xmm registers first, after 6 doubles and 2
 // structs. The rest of the structs will go on the stack.
 // The int will be backfilled into the int register.
@@ -2494,7 +2520,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // On Linux x64, it will exhaust int registers first.
 // The rest of the structs will go on the stack.
 // The double will be backfilled into the xmm register.
@@ -2535,7 +2561,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // On various architectures, first struct is allocated on stack.
 // Check that the other two arguments are allocated on registers.
 DART_EXPORT double PassStruct40BytesHomogeneousDoubleStruct4BytesHomo(
@@ -2565,7 +2591,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test alignment and padding of 16 byte int within struct.
 DART_EXPORT double PassInt32x8Doublex8Int64Int8Struct1ByteIntInt64Int(
     int32_t a0,
@@ -2690,7 +2716,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test alignment and padding of 16 byte int within struct.
 DART_EXPORT int64_t PassStructAlignmentInt16(StructAlignmentInt16 a0) {
   std::cout << "PassStructAlignmentInt16"
@@ -2709,7 +2735,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test alignment and padding of 32 byte int within struct.
 DART_EXPORT int64_t PassStructAlignmentInt32(StructAlignmentInt32 a0) {
   std::cout << "PassStructAlignmentInt32"
@@ -2728,7 +2754,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test alignment and padding of 64 byte int within struct.
 DART_EXPORT int64_t PassStructAlignmentInt64(StructAlignmentInt64 a0) {
   std::cout << "PassStructAlignmentInt64"
@@ -2747,7 +2773,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Simple nested struct. No alignment gaps on any architectures.
 // 10 arguments exhaust registers on all platforms.
 DART_EXPORT int64_t PassStruct8BytesNestedIntx10(Struct8BytesNestedInt a0,
@@ -2825,7 +2851,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Simple nested struct. No alignment gaps on any architectures.
 // 10 arguments exhaust fpu registers on all platforms.
 DART_EXPORT float PassStruct8BytesNestedFloatx10(Struct8BytesNestedFloat a0,
@@ -2877,7 +2903,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Simple nested struct. No alignment gaps on any architectures.
 // 10 arguments exhaust fpu registers on all platforms.
 // The nesting is irregular, testing homogenous float rules on arm and arm64,
@@ -2930,7 +2956,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Simple nested struct. No alignment gaps on any architectures.
 // 10 arguments exhaust all registers on all platforms.
 DART_EXPORT double PassStruct8BytesNestedMixedx10(Struct8BytesNestedMixed a0,
@@ -2995,7 +3021,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Deeper nested struct to test recursive member access.
 DART_EXPORT int64_t PassStruct16BytesNestedIntx2(Struct16BytesNestedInt a0,
                                                  Struct16BytesNestedInt a1) {
@@ -3033,7 +3059,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Even deeper nested struct to test recursive member access.
 DART_EXPORT int64_t PassStruct32BytesNestedIntx2(Struct32BytesNestedInt a0,
                                                  Struct32BytesNestedInt a1) {
@@ -3096,7 +3122,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test alignment and padding of nested struct with 16 byte int.
 DART_EXPORT int64_t PassStructNestedIntStructAlignmentInt16(
     StructNestedIntStructAlignmentInt16 a0) {
@@ -3121,7 +3147,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test alignment and padding of nested struct with 32 byte int.
 DART_EXPORT int64_t PassStructNestedIntStructAlignmentInt32(
     StructNestedIntStructAlignmentInt32 a0) {
@@ -3146,7 +3172,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test alignment and padding of nested struct with 64 byte int.
 DART_EXPORT int64_t PassStructNestedIntStructAlignmentInt64(
     StructNestedIntStructAlignmentInt64 a0) {
@@ -3171,7 +3197,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Return big irregular struct as smoke test.
 DART_EXPORT double PassStructNestedIrregularEvenBiggerx4(
     StructNestedIrregularEvenBigger a0,
@@ -3380,7 +3406,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Simple struct with inline array.
 DART_EXPORT int32_t
 PassStruct8BytesInlineArrayIntx4(Struct8BytesInlineArrayInt a0,
@@ -3453,7 +3479,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Irregular struct with inline array.
 DART_EXPORT int32_t
 PassStructInlineArrayIrregularx4(StructInlineArrayIrregular a0,
@@ -3503,7 +3529,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Regular larger struct with inline array.
 DART_EXPORT int32_t
 PassStructInlineArray100Bytes(StructInlineArray100Bytes a0) {
@@ -3686,7 +3712,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Arguments in FPU registers on arm hardfp and arm64.
 // 5 struct arguments will exhaust available registers.
 DART_EXPORT float PassStructStruct16BytesHomogeneousFloat2x5(
@@ -3734,7 +3760,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Arguments in FPU registers on arm64.
 // 5 struct arguments will exhaust available registers.
 DART_EXPORT double PassStructStruct32BytesHomogeneousDouble2x5(
@@ -3782,7 +3808,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // On x64, arguments are split over FP and int registers.
 // On x64, it will exhaust the integer registers with the 6th argument.
 // The rest goes on the stack.
@@ -3892,7 +3918,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test multi dimensional inline array struct as argument.
 DART_EXPORT uint32_t PassUint8Struct32BytesInlineArrayMultiDimensionalI(
     uint8_t a0,
@@ -4017,7 +4043,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test struct in multi dimensional inline array.
 DART_EXPORT uint32_t PassUint8Struct4BytesInlineArrayMultiDimensionalIn(
     uint8_t a0,
@@ -4046,7 +4072,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Small struct with mis-aligned member.
 DART_EXPORT int64_t PassStruct3BytesPackedIntx10(Struct3BytesPackedInt a0,
                                                  Struct3BytesPackedInt a1,
@@ -4099,7 +4125,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Struct with mis-aligned member.
 DART_EXPORT int64_t PassStruct8BytesPackedIntx10(Struct8BytesPackedInt a0,
                                                  Struct8BytesPackedInt a1,
@@ -4202,7 +4228,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Struct with mis-aligned member.
 // Tests backfilling of CPU and FPU registers.
 DART_EXPORT double PassStruct9BytesPackedMixedx10DoubleInt32(
@@ -4262,7 +4288,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // This packed struct happens to have only aligned members.
 DART_EXPORT double PassStruct5BytesPackedMixed(Struct5BytesPackedMixed a0) {
   std::cout << "PassStruct5BytesPackedMixed"
@@ -4279,7 +4305,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Check alignment of packed struct in non-packed struct.
 DART_EXPORT double PassStructNestedAlignmentStruct5BytesPackedMixed(
     StructNestedAlignmentStruct5BytesPackedMixed a0) {
@@ -4299,7 +4325,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Check alignment of packed struct array in non-packed struct.
 DART_EXPORT double PassStruct6BytesInlineArrayInt(
     Struct6BytesInlineArrayInt a0) {
@@ -4321,7 +4347,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Check alignment of packed struct array in non-packed struct.
 DART_EXPORT double PassStruct15BytesInlineArrayMixed(
     Struct15BytesInlineArrayMixed a0) {
@@ -4346,7 +4372,377 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
+// Check placement of mixed integer/float union.
+DART_EXPORT double PassUnion4BytesMixedx10(Union4BytesMixed a0,
+                                           Union4BytesMixed a1,
+                                           Union4BytesMixed a2,
+                                           Union4BytesMixed a3,
+                                           Union4BytesMixed a4,
+                                           Union4BytesMixed a5,
+                                           Union4BytesMixed a6,
+                                           Union4BytesMixed a7,
+                                           Union4BytesMixed a8,
+                                           Union4BytesMixed a9) {
+  std::cout << "PassUnion4BytesMixedx10"
+            << "((" << a0.a0 << ", " << a0.a1 << "), (" << a1.a0 << ", "
+            << a1.a1 << "), (" << a2.a0 << ", " << a2.a1 << "), (" << a3.a0
+            << ", " << a3.a1 << "), (" << a4.a0 << ", " << a4.a1 << "), ("
+            << a5.a0 << ", " << a5.a1 << "), (" << a6.a0 << ", " << a6.a1
+            << "), (" << a7.a0 << ", " << a7.a1 << "), (" << a8.a0 << ", "
+            << a8.a1 << "), (" << a9.a0 << ", " << a9.a1 << "))"
+            << "\n";
+
+  double result = 0;
+
+  result += a0.a0;
+  result += a1.a0;
+  result += a2.a0;
+  result += a3.a0;
+  result += a4.a0;
+  result += a5.a0;
+  result += a6.a0;
+  result += a7.a0;
+  result += a8.a0;
+  result += a9.a0;
+
+  std::cout << "result = " << result << "\n";
+
+  return result;
+}
+
+// Used for testing structs and unions by value.
+// Check placement of mixed floats union.
+DART_EXPORT double PassUnion8BytesNestedFloatx10(Union8BytesNestedFloat a0,
+                                                 Union8BytesNestedFloat a1,
+                                                 Union8BytesNestedFloat a2,
+                                                 Union8BytesNestedFloat a3,
+                                                 Union8BytesNestedFloat a4,
+                                                 Union8BytesNestedFloat a5,
+                                                 Union8BytesNestedFloat a6,
+                                                 Union8BytesNestedFloat a7,
+                                                 Union8BytesNestedFloat a8,
+                                                 Union8BytesNestedFloat a9) {
+  std::cout << "PassUnion8BytesNestedFloatx10"
+            << "((" << a0.a0 << ", (" << a0.a1.a0 << ", " << a0.a1.a1 << ")), ("
+            << a1.a0 << ", (" << a1.a1.a0 << ", " << a1.a1.a1 << ")), ("
+            << a2.a0 << ", (" << a2.a1.a0 << ", " << a2.a1.a1 << ")), ("
+            << a3.a0 << ", (" << a3.a1.a0 << ", " << a3.a1.a1 << ")), ("
+            << a4.a0 << ", (" << a4.a1.a0 << ", " << a4.a1.a1 << ")), ("
+            << a5.a0 << ", (" << a5.a1.a0 << ", " << a5.a1.a1 << ")), ("
+            << a6.a0 << ", (" << a6.a1.a0 << ", " << a6.a1.a1 << ")), ("
+            << a7.a0 << ", (" << a7.a1.a0 << ", " << a7.a1.a1 << ")), ("
+            << a8.a0 << ", (" << a8.a1.a0 << ", " << a8.a1.a1 << ")), ("
+            << a9.a0 << ", (" << a9.a1.a0 << ", " << a9.a1.a1 << ")))"
+            << "\n";
+
+  double result = 0;
+
+  result += a0.a0;
+  result += a1.a0;
+  result += a2.a0;
+  result += a3.a0;
+  result += a4.a0;
+  result += a5.a0;
+  result += a6.a0;
+  result += a7.a0;
+  result += a8.a0;
+  result += a9.a0;
+
+  std::cout << "result = " << result << "\n";
+
+  return result;
+}
+
+// Used for testing structs and unions by value.
+// Mixed-size union argument.
+DART_EXPORT double PassUnion9BytesNestedIntx10(Union9BytesNestedInt a0,
+                                               Union9BytesNestedInt a1,
+                                               Union9BytesNestedInt a2,
+                                               Union9BytesNestedInt a3,
+                                               Union9BytesNestedInt a4,
+                                               Union9BytesNestedInt a5,
+                                               Union9BytesNestedInt a6,
+                                               Union9BytesNestedInt a7,
+                                               Union9BytesNestedInt a8,
+                                               Union9BytesNestedInt a9) {
+  std::cout << "PassUnion9BytesNestedIntx10"
+            << "(((" << a0.a0.a0 << ", " << a0.a0.a1 << ", " << a0.a0.a2
+            << "), (" << static_cast<int>(a0.a1.a0) << ", "
+            << static_cast<int>(a0.a1.a1) << ", " << static_cast<int>(a0.a1.a2)
+            << ", " << static_cast<int>(a0.a1.a3) << ", "
+            << static_cast<int>(a0.a1.a4) << ", " << static_cast<int>(a0.a1.a5)
+            << ", " << static_cast<int>(a0.a1.a6) << ", "
+            << static_cast<int>(a0.a1.a7) << ", " << static_cast<int>(a0.a1.a8)
+            << ")), ((" << a1.a0.a0 << ", " << a1.a0.a1 << ", " << a1.a0.a2
+            << "), (" << static_cast<int>(a1.a1.a0) << ", "
+            << static_cast<int>(a1.a1.a1) << ", " << static_cast<int>(a1.a1.a2)
+            << ", " << static_cast<int>(a1.a1.a3) << ", "
+            << static_cast<int>(a1.a1.a4) << ", " << static_cast<int>(a1.a1.a5)
+            << ", " << static_cast<int>(a1.a1.a6) << ", "
+            << static_cast<int>(a1.a1.a7) << ", " << static_cast<int>(a1.a1.a8)
+            << ")), ((" << a2.a0.a0 << ", " << a2.a0.a1 << ", " << a2.a0.a2
+            << "), (" << static_cast<int>(a2.a1.a0) << ", "
+            << static_cast<int>(a2.a1.a1) << ", " << static_cast<int>(a2.a1.a2)
+            << ", " << static_cast<int>(a2.a1.a3) << ", "
+            << static_cast<int>(a2.a1.a4) << ", " << static_cast<int>(a2.a1.a5)
+            << ", " << static_cast<int>(a2.a1.a6) << ", "
+            << static_cast<int>(a2.a1.a7) << ", " << static_cast<int>(a2.a1.a8)
+            << ")), ((" << a3.a0.a0 << ", " << a3.a0.a1 << ", " << a3.a0.a2
+            << "), (" << static_cast<int>(a3.a1.a0) << ", "
+            << static_cast<int>(a3.a1.a1) << ", " << static_cast<int>(a3.a1.a2)
+            << ", " << static_cast<int>(a3.a1.a3) << ", "
+            << static_cast<int>(a3.a1.a4) << ", " << static_cast<int>(a3.a1.a5)
+            << ", " << static_cast<int>(a3.a1.a6) << ", "
+            << static_cast<int>(a3.a1.a7) << ", " << static_cast<int>(a3.a1.a8)
+            << ")), ((" << a4.a0.a0 << ", " << a4.a0.a1 << ", " << a4.a0.a2
+            << "), (" << static_cast<int>(a4.a1.a0) << ", "
+            << static_cast<int>(a4.a1.a1) << ", " << static_cast<int>(a4.a1.a2)
+            << ", " << static_cast<int>(a4.a1.a3) << ", "
+            << static_cast<int>(a4.a1.a4) << ", " << static_cast<int>(a4.a1.a5)
+            << ", " << static_cast<int>(a4.a1.a6) << ", "
+            << static_cast<int>(a4.a1.a7) << ", " << static_cast<int>(a4.a1.a8)
+            << ")), ((" << a5.a0.a0 << ", " << a5.a0.a1 << ", " << a5.a0.a2
+            << "), (" << static_cast<int>(a5.a1.a0) << ", "
+            << static_cast<int>(a5.a1.a1) << ", " << static_cast<int>(a5.a1.a2)
+            << ", " << static_cast<int>(a5.a1.a3) << ", "
+            << static_cast<int>(a5.a1.a4) << ", " << static_cast<int>(a5.a1.a5)
+            << ", " << static_cast<int>(a5.a1.a6) << ", "
+            << static_cast<int>(a5.a1.a7) << ", " << static_cast<int>(a5.a1.a8)
+            << ")), ((" << a6.a0.a0 << ", " << a6.a0.a1 << ", " << a6.a0.a2
+            << "), (" << static_cast<int>(a6.a1.a0) << ", "
+            << static_cast<int>(a6.a1.a1) << ", " << static_cast<int>(a6.a1.a2)
+            << ", " << static_cast<int>(a6.a1.a3) << ", "
+            << static_cast<int>(a6.a1.a4) << ", " << static_cast<int>(a6.a1.a5)
+            << ", " << static_cast<int>(a6.a1.a6) << ", "
+            << static_cast<int>(a6.a1.a7) << ", " << static_cast<int>(a6.a1.a8)
+            << ")), ((" << a7.a0.a0 << ", " << a7.a0.a1 << ", " << a7.a0.a2
+            << "), (" << static_cast<int>(a7.a1.a0) << ", "
+            << static_cast<int>(a7.a1.a1) << ", " << static_cast<int>(a7.a1.a2)
+            << ", " << static_cast<int>(a7.a1.a3) << ", "
+            << static_cast<int>(a7.a1.a4) << ", " << static_cast<int>(a7.a1.a5)
+            << ", " << static_cast<int>(a7.a1.a6) << ", "
+            << static_cast<int>(a7.a1.a7) << ", " << static_cast<int>(a7.a1.a8)
+            << ")), ((" << a8.a0.a0 << ", " << a8.a0.a1 << ", " << a8.a0.a2
+            << "), (" << static_cast<int>(a8.a1.a0) << ", "
+            << static_cast<int>(a8.a1.a1) << ", " << static_cast<int>(a8.a1.a2)
+            << ", " << static_cast<int>(a8.a1.a3) << ", "
+            << static_cast<int>(a8.a1.a4) << ", " << static_cast<int>(a8.a1.a5)
+            << ", " << static_cast<int>(a8.a1.a6) << ", "
+            << static_cast<int>(a8.a1.a7) << ", " << static_cast<int>(a8.a1.a8)
+            << ")), ((" << a9.a0.a0 << ", " << a9.a0.a1 << ", " << a9.a0.a2
+            << "), (" << static_cast<int>(a9.a1.a0) << ", "
+            << static_cast<int>(a9.a1.a1) << ", " << static_cast<int>(a9.a1.a2)
+            << ", " << static_cast<int>(a9.a1.a3) << ", "
+            << static_cast<int>(a9.a1.a4) << ", " << static_cast<int>(a9.a1.a5)
+            << ", " << static_cast<int>(a9.a1.a6) << ", "
+            << static_cast<int>(a9.a1.a7) << ", " << static_cast<int>(a9.a1.a8)
+            << ")))"
+            << "\n";
+
+  double result = 0;
+
+  result += a0.a0.a0;
+  result += a0.a0.a1;
+  result += a0.a0.a2;
+  result += a1.a0.a0;
+  result += a1.a0.a1;
+  result += a1.a0.a2;
+  result += a2.a0.a0;
+  result += a2.a0.a1;
+  result += a2.a0.a2;
+  result += a3.a0.a0;
+  result += a3.a0.a1;
+  result += a3.a0.a2;
+  result += a4.a0.a0;
+  result += a4.a0.a1;
+  result += a4.a0.a2;
+  result += a5.a0.a0;
+  result += a5.a0.a1;
+  result += a5.a0.a2;
+  result += a6.a0.a0;
+  result += a6.a0.a1;
+  result += a6.a0.a2;
+  result += a7.a0.a0;
+  result += a7.a0.a1;
+  result += a7.a0.a2;
+  result += a8.a0.a0;
+  result += a8.a0.a1;
+  result += a8.a0.a2;
+  result += a9.a0.a0;
+  result += a9.a0.a1;
+  result += a9.a0.a2;
+
+  std::cout << "result = " << result << "\n";
+
+  return result;
+}
+
+// Used for testing structs and unions by value.
+// Union with homogenous floats.
+DART_EXPORT double PassUnion16BytesNestedInlineArrayFloatx10(
+    Union16BytesNestedInlineArrayFloat a0,
+    Union16BytesNestedInlineArrayFloat a1,
+    Union16BytesNestedInlineArrayFloat a2,
+    Union16BytesNestedInlineArrayFloat a3,
+    Union16BytesNestedInlineArrayFloat a4,
+    Union16BytesNestedInlineArrayFloat a5,
+    Union16BytesNestedInlineArrayFloat a6,
+    Union16BytesNestedInlineArrayFloat a7,
+    Union16BytesNestedInlineArrayFloat a8,
+    Union16BytesNestedInlineArrayFloat a9) {
+  std::cout << "PassUnion16BytesNestedInlineArrayFloatx10"
+            << "(([" << a0.a0[0] << ", " << a0.a0[1] << ", " << a0.a0[2] << ", "
+            << a0.a0[3] << "], (" << a0.a1.a0 << ", " << a0.a1.a1 << ", "
+            << a0.a1.a2 << ", " << a0.a1.a3 << ")), ([" << a1.a0[0] << ", "
+            << a1.a0[1] << ", " << a1.a0[2] << ", " << a1.a0[3] << "], ("
+            << a1.a1.a0 << ", " << a1.a1.a1 << ", " << a1.a1.a2 << ", "
+            << a1.a1.a3 << ")), ([" << a2.a0[0] << ", " << a2.a0[1] << ", "
+            << a2.a0[2] << ", " << a2.a0[3] << "], (" << a2.a1.a0 << ", "
+            << a2.a1.a1 << ", " << a2.a1.a2 << ", " << a2.a1.a3 << ")), (["
+            << a3.a0[0] << ", " << a3.a0[1] << ", " << a3.a0[2] << ", "
+            << a3.a0[3] << "], (" << a3.a1.a0 << ", " << a3.a1.a1 << ", "
+            << a3.a1.a2 << ", " << a3.a1.a3 << ")), ([" << a4.a0[0] << ", "
+            << a4.a0[1] << ", " << a4.a0[2] << ", " << a4.a0[3] << "], ("
+            << a4.a1.a0 << ", " << a4.a1.a1 << ", " << a4.a1.a2 << ", "
+            << a4.a1.a3 << ")), ([" << a5.a0[0] << ", " << a5.a0[1] << ", "
+            << a5.a0[2] << ", " << a5.a0[3] << "], (" << a5.a1.a0 << ", "
+            << a5.a1.a1 << ", " << a5.a1.a2 << ", " << a5.a1.a3 << ")), (["
+            << a6.a0[0] << ", " << a6.a0[1] << ", " << a6.a0[2] << ", "
+            << a6.a0[3] << "], (" << a6.a1.a0 << ", " << a6.a1.a1 << ", "
+            << a6.a1.a2 << ", " << a6.a1.a3 << ")), ([" << a7.a0[0] << ", "
+            << a7.a0[1] << ", " << a7.a0[2] << ", " << a7.a0[3] << "], ("
+            << a7.a1.a0 << ", " << a7.a1.a1 << ", " << a7.a1.a2 << ", "
+            << a7.a1.a3 << ")), ([" << a8.a0[0] << ", " << a8.a0[1] << ", "
+            << a8.a0[2] << ", " << a8.a0[3] << "], (" << a8.a1.a0 << ", "
+            << a8.a1.a1 << ", " << a8.a1.a2 << ", " << a8.a1.a3 << ")), (["
+            << a9.a0[0] << ", " << a9.a0[1] << ", " << a9.a0[2] << ", "
+            << a9.a0[3] << "], (" << a9.a1.a0 << ", " << a9.a1.a1 << ", "
+            << a9.a1.a2 << ", " << a9.a1.a3 << ")))"
+            << "\n";
+
+  double result = 0;
+
+  result += a0.a0[0];
+  result += a0.a0[1];
+  result += a0.a0[2];
+  result += a0.a0[3];
+  result += a1.a0[0];
+  result += a1.a0[1];
+  result += a1.a0[2];
+  result += a1.a0[3];
+  result += a2.a0[0];
+  result += a2.a0[1];
+  result += a2.a0[2];
+  result += a2.a0[3];
+  result += a3.a0[0];
+  result += a3.a0[1];
+  result += a3.a0[2];
+  result += a3.a0[3];
+  result += a4.a0[0];
+  result += a4.a0[1];
+  result += a4.a0[2];
+  result += a4.a0[3];
+  result += a5.a0[0];
+  result += a5.a0[1];
+  result += a5.a0[2];
+  result += a5.a0[3];
+  result += a6.a0[0];
+  result += a6.a0[1];
+  result += a6.a0[2];
+  result += a6.a0[3];
+  result += a7.a0[0];
+  result += a7.a0[1];
+  result += a7.a0[2];
+  result += a7.a0[3];
+  result += a8.a0[0];
+  result += a8.a0[1];
+  result += a8.a0[2];
+  result += a8.a0[3];
+  result += a9.a0[0];
+  result += a9.a0[1];
+  result += a9.a0[2];
+  result += a9.a0[3];
+
+  std::cout << "result = " << result << "\n";
+
+  return result;
+}
+
+// Used for testing structs and unions by value.
+// Union with homogenous floats.
+DART_EXPORT double PassUnion16BytesNestedFloatx10(Union16BytesNestedFloat a0,
+                                                  Union16BytesNestedFloat a1,
+                                                  Union16BytesNestedFloat a2,
+                                                  Union16BytesNestedFloat a3,
+                                                  Union16BytesNestedFloat a4,
+                                                  Union16BytesNestedFloat a5,
+                                                  Union16BytesNestedFloat a6,
+                                                  Union16BytesNestedFloat a7,
+                                                  Union16BytesNestedFloat a8,
+                                                  Union16BytesNestedFloat a9) {
+  std::cout << "PassUnion16BytesNestedFloatx10"
+            << "(((" << a0.a0.a0 << ", " << a0.a0.a1 << "), (" << a0.a1.a0
+            << ", " << a0.a1.a1 << ", " << a0.a1.a2 << "), (" << a0.a2.a0
+            << ", " << a0.a2.a1 << ", " << a0.a2.a2 << ", " << a0.a2.a3
+            << ")), ((" << a1.a0.a0 << ", " << a1.a0.a1 << "), (" << a1.a1.a0
+            << ", " << a1.a1.a1 << ", " << a1.a1.a2 << "), (" << a1.a2.a0
+            << ", " << a1.a2.a1 << ", " << a1.a2.a2 << ", " << a1.a2.a3
+            << ")), ((" << a2.a0.a0 << ", " << a2.a0.a1 << "), (" << a2.a1.a0
+            << ", " << a2.a1.a1 << ", " << a2.a1.a2 << "), (" << a2.a2.a0
+            << ", " << a2.a2.a1 << ", " << a2.a2.a2 << ", " << a2.a2.a3
+            << ")), ((" << a3.a0.a0 << ", " << a3.a0.a1 << "), (" << a3.a1.a0
+            << ", " << a3.a1.a1 << ", " << a3.a1.a2 << "), (" << a3.a2.a0
+            << ", " << a3.a2.a1 << ", " << a3.a2.a2 << ", " << a3.a2.a3
+            << ")), ((" << a4.a0.a0 << ", " << a4.a0.a1 << "), (" << a4.a1.a0
+            << ", " << a4.a1.a1 << ", " << a4.a1.a2 << "), (" << a4.a2.a0
+            << ", " << a4.a2.a1 << ", " << a4.a2.a2 << ", " << a4.a2.a3
+            << ")), ((" << a5.a0.a0 << ", " << a5.a0.a1 << "), (" << a5.a1.a0
+            << ", " << a5.a1.a1 << ", " << a5.a1.a2 << "), (" << a5.a2.a0
+            << ", " << a5.a2.a1 << ", " << a5.a2.a2 << ", " << a5.a2.a3
+            << ")), ((" << a6.a0.a0 << ", " << a6.a0.a1 << "), (" << a6.a1.a0
+            << ", " << a6.a1.a1 << ", " << a6.a1.a2 << "), (" << a6.a2.a0
+            << ", " << a6.a2.a1 << ", " << a6.a2.a2 << ", " << a6.a2.a3
+            << ")), ((" << a7.a0.a0 << ", " << a7.a0.a1 << "), (" << a7.a1.a0
+            << ", " << a7.a1.a1 << ", " << a7.a1.a2 << "), (" << a7.a2.a0
+            << ", " << a7.a2.a1 << ", " << a7.a2.a2 << ", " << a7.a2.a3
+            << ")), ((" << a8.a0.a0 << ", " << a8.a0.a1 << "), (" << a8.a1.a0
+            << ", " << a8.a1.a1 << ", " << a8.a1.a2 << "), (" << a8.a2.a0
+            << ", " << a8.a2.a1 << ", " << a8.a2.a2 << ", " << a8.a2.a3
+            << ")), ((" << a9.a0.a0 << ", " << a9.a0.a1 << "), (" << a9.a1.a0
+            << ", " << a9.a1.a1 << ", " << a9.a1.a2 << "), (" << a9.a2.a0
+            << ", " << a9.a2.a1 << ", " << a9.a2.a2 << ", " << a9.a2.a3 << ")))"
+            << "\n";
+
+  double result = 0;
+
+  result += a0.a0.a0;
+  result += a0.a0.a1;
+  result += a1.a0.a0;
+  result += a1.a0.a1;
+  result += a2.a0.a0;
+  result += a2.a0.a1;
+  result += a3.a0.a0;
+  result += a3.a0.a1;
+  result += a4.a0.a0;
+  result += a4.a0.a1;
+  result += a5.a0.a0;
+  result += a5.a0.a1;
+  result += a6.a0.a0;
+  result += a6.a0.a1;
+  result += a7.a0.a0;
+  result += a7.a0.a1;
+  result += a8.a0.a0;
+  result += a8.a0.a1;
+  result += a9.a0.a0;
+  result += a9.a0.a1;
+
+  std::cout << "result = " << result << "\n";
+
+  return result;
+}
+
+// Used for testing structs and unions by value.
 // Smallest struct with data.
 DART_EXPORT Struct1ByteInt ReturnStruct1ByteInt(int8_t a0) {
   std::cout << "ReturnStruct1ByteInt"
@@ -4364,7 +4760,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Smaller than word size return value on all architectures.
 DART_EXPORT Struct3BytesHomogeneousUint8
 ReturnStruct3BytesHomogeneousUint8(uint8_t a0, uint8_t a1, uint8_t a2) {
@@ -4388,7 +4784,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Smaller than word size return value on all architectures.
 // With alignment rules taken into account size is 4 bytes.
 DART_EXPORT Struct3BytesInt2ByteAligned
@@ -4409,7 +4805,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Word size return value on 32 bit architectures..
 DART_EXPORT Struct4BytesHomogeneousInt16
 ReturnStruct4BytesHomogeneousInt16(int16_t a0, int16_t a1) {
@@ -4429,7 +4825,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Non-wordsize return value.
 DART_EXPORT Struct7BytesHomogeneousUint8
 ReturnStruct7BytesHomogeneousUint8(uint8_t a0,
@@ -4469,7 +4865,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Non-wordsize return value.
 // With alignment rules taken into account size is 8 bytes.
 DART_EXPORT Struct7BytesInt4ByteAligned
@@ -4492,7 +4888,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Return value in integer registers on many architectures.
 DART_EXPORT Struct8BytesInt ReturnStruct8BytesInt(int16_t a0,
                                                   int16_t a1,
@@ -4514,7 +4910,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Return value in FP registers on many architectures.
 DART_EXPORT Struct8BytesHomogeneousFloat
 ReturnStruct8BytesHomogeneousFloat(float a0, float a1) {
@@ -4534,7 +4930,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Return value split over FP and integer register in x64.
 DART_EXPORT Struct8BytesMixed ReturnStruct8BytesMixed(float a0,
                                                       int16_t a1,
@@ -4556,7 +4952,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // The minimum alignment of this struct is only 1 byte based on its fields.
 // Test that the memory backing these structs is the right size and that
 // dart:ffi trampolines do not write outside this size.
@@ -4605,7 +5001,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Return value in two integer registers on x64.
 // With alignment rules taken into account size is 12 or 16 bytes.
 DART_EXPORT Struct9BytesInt4Or8ByteAligned
@@ -4626,7 +5022,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Return value in FPU registers, but does not use all registers on arm hardfp
 // and arm64.
 DART_EXPORT Struct12BytesHomogeneousFloat
@@ -4648,7 +5044,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Return value in FPU registers on arm hardfp and arm64.
 DART_EXPORT Struct16BytesHomogeneousFloat
 ReturnStruct16BytesHomogeneousFloat(float a0, float a1, float a2, float a3) {
@@ -4671,7 +5067,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Return value split over FP and integer register in x64.
 DART_EXPORT Struct16BytesMixed ReturnStruct16BytesMixed(double a0, int64_t a1) {
   std::cout << "ReturnStruct16BytesMixed"
@@ -4690,7 +5086,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Return value split over FP and integer register in x64.
 // The integer register contains half float half int.
 DART_EXPORT Struct16BytesMixed2 ReturnStruct16BytesMixed2(float a0,
@@ -4716,7 +5112,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Rerturn value returned in preallocated space passed by pointer on most ABIs.
 // Is non word size on purpose, to test that structs are rounded up to word size
 // on all ABIs.
@@ -4741,7 +5137,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // The minimum alignment of this struct is only 1 byte based on its fields.
 // Test that the memory backing these structs is the right size and that
 // dart:ffi trampolines do not write outside this size.
@@ -4825,7 +5221,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Return value too big to go in cpu registers on arm64.
 DART_EXPORT Struct20BytesHomogeneousInt32
 ReturnStruct20BytesHomogeneousInt32(int32_t a0,
@@ -4854,7 +5250,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Return value too big to go in FPU registers on x64, arm hardfp and arm64.
 DART_EXPORT Struct20BytesHomogeneousFloat
 ReturnStruct20BytesHomogeneousFloat(float a0,
@@ -4883,7 +5279,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Return value in FPU registers on arm64.
 DART_EXPORT Struct32BytesHomogeneousDouble
 ReturnStruct32BytesHomogeneousDouble(double a0,
@@ -4909,7 +5305,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Return value too big to go in FPU registers on arm64.
 DART_EXPORT Struct40BytesHomogeneousDouble
 ReturnStruct40BytesHomogeneousDouble(double a0,
@@ -4938,7 +5334,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test 1kb struct.
 DART_EXPORT Struct1024BytesHomogeneousUint64
 ReturnStruct1024BytesHomogeneousUint64(uint64_t a0,
@@ -5282,7 +5678,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Small struct with mis-aligned member.
 DART_EXPORT Struct3BytesPackedInt ReturnStruct3BytesPackedInt(int8_t a0,
                                                               int16_t a1) {
@@ -5302,7 +5698,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Struct with mis-aligned member.
 DART_EXPORT Struct8BytesPackedInt ReturnStruct8BytesPackedInt(uint8_t a0,
                                                               uint32_t a1,
@@ -5333,7 +5729,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Struct with mis-aligned member.
 // Tests backfilling of CPU and FPU registers.
 DART_EXPORT Struct9BytesPackedMixed ReturnStruct9BytesPackedMixed(uint8_t a0,
@@ -5354,7 +5750,97 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
+// Returning a mixed integer/float union.
+DART_EXPORT Union4BytesMixed ReturnUnion4BytesMixed(uint32_t a0) {
+  std::cout << "ReturnUnion4BytesMixed"
+            << "(" << a0 << ")"
+            << "\n";
+
+  Union4BytesMixed result;
+
+  result.a0 = a0;
+
+  std::cout << "result = "
+            << "(" << result.a0 << ", " << result.a1 << ")"
+            << "\n";
+
+  return result;
+}
+
+// Used for testing structs and unions by value.
+// Returning a floating point only union.
+DART_EXPORT Union8BytesNestedFloat ReturnUnion8BytesNestedFloat(double a0) {
+  std::cout << "ReturnUnion8BytesNestedFloat"
+            << "(" << a0 << ")"
+            << "\n";
+
+  Union8BytesNestedFloat result;
+
+  result.a0 = a0;
+
+  std::cout << "result = "
+            << "(" << result.a0 << ", (" << result.a1.a0 << ", " << result.a1.a1
+            << "))"
+            << "\n";
+
+  return result;
+}
+
+// Used for testing structs and unions by value.
+// Returning a mixed-size union.
+DART_EXPORT Union9BytesNestedInt
+ReturnUnion9BytesNestedInt(Struct8BytesInt a0) {
+  std::cout << "ReturnUnion9BytesNestedInt"
+            << "((" << a0.a0 << ", " << a0.a1 << ", " << a0.a2 << "))"
+            << "\n";
+
+  Union9BytesNestedInt result;
+
+  result.a0.a0 = a0.a0;
+  result.a0.a1 = a0.a1;
+  result.a0.a2 = a0.a2;
+
+  std::cout << "result = "
+            << "((" << result.a0.a0 << ", " << result.a0.a1 << ", "
+            << result.a0.a2 << "), (" << static_cast<int>(result.a1.a0) << ", "
+            << static_cast<int>(result.a1.a1) << ", "
+            << static_cast<int>(result.a1.a2) << ", "
+            << static_cast<int>(result.a1.a3) << ", "
+            << static_cast<int>(result.a1.a4) << ", "
+            << static_cast<int>(result.a1.a5) << ", "
+            << static_cast<int>(result.a1.a6) << ", "
+            << static_cast<int>(result.a1.a7) << ", "
+            << static_cast<int>(result.a1.a8) << "))"
+            << "\n";
+
+  return result;
+}
+
+// Used for testing structs and unions by value.
+// Returning union with homogenous floats.
+DART_EXPORT Union16BytesNestedFloat
+ReturnUnion16BytesNestedFloat(Struct8BytesHomogeneousFloat a0) {
+  std::cout << "ReturnUnion16BytesNestedFloat"
+            << "((" << a0.a0 << ", " << a0.a1 << "))"
+            << "\n";
+
+  Union16BytesNestedFloat result;
+
+  result.a0.a0 = a0.a0;
+  result.a0.a1 = a0.a1;
+
+  std::cout << "result = "
+            << "((" << result.a0.a0 << ", " << result.a0.a1 << "), ("
+            << result.a1.a0 << ", " << result.a1.a1 << ", " << result.a1.a2
+            << "), (" << result.a2.a0 << ", " << result.a2.a1 << ", "
+            << result.a2.a2 << ", " << result.a2.a3 << "))"
+            << "\n";
+
+  return result;
+}
+
+// Used for testing structs and unions by value.
 // Test that a struct passed in as argument can be returned.
 // Especially for ffi callbacks.
 // Struct is passed in int registers in most ABIs.
@@ -5373,7 +5859,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test that a struct passed in as argument can be returned.
 // Especially for ffi callbacks.
 // Struct is passed on stack on all ABIs.
@@ -5402,7 +5888,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test that a struct passed in as argument can be returned.
 // Especially for ffi callbacks.
 // Struct is passed in float registers in most ABIs.
@@ -5422,7 +5908,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // On arm64, both argument and return value are passed in by pointer.
 DART_EXPORT Struct20BytesHomogeneousInt32
 ReturnStructArgumentStruct20BytesHomogeneousInt32(
@@ -5442,7 +5928,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // On arm64, both argument and return value are passed in by pointer.
 // Ints exhaust registers, so that pointer is passed on stack.
 DART_EXPORT Struct20BytesHomogeneousInt32
@@ -5472,7 +5958,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test returning struct with inline array.
 DART_EXPORT Struct8BytesInlineArrayInt
 ReturnStructArgumentStruct8BytesInlineArrayInt(Struct8BytesInlineArrayInt a0) {
@@ -5501,7 +5987,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Return value in FPU registers on arm hardfp and arm64.
 DART_EXPORT StructStruct16BytesHomogeneousFloat2
 ReturnStructArgumentStructStruct16BytesHomogeneous(
@@ -5521,7 +6007,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Return value in FPU registers on arm64.
 DART_EXPORT StructStruct32BytesHomogeneousDouble2
 ReturnStructArgumentStructStruct32BytesHomogeneous(
@@ -5541,7 +6027,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // On x64 Linux, return value is split over FP and int registers.
 DART_EXPORT StructStruct16BytesMixed3
 ReturnStructArgumentStructStruct16BytesMixed3(StructStruct16BytesMixed3 a0) {
@@ -5562,7 +6048,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test alignment and padding of 16 byte int within struct.
 DART_EXPORT StructAlignmentInt16 ReturnStructAlignmentInt16(int8_t a0,
                                                             int16_t a1,
@@ -5586,7 +6072,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test alignment and padding of 32 byte int within struct.
 DART_EXPORT StructAlignmentInt32 ReturnStructAlignmentInt32(int8_t a0,
                                                             int32_t a1,
@@ -5610,7 +6096,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test alignment and padding of 64 byte int within struct.
 DART_EXPORT StructAlignmentInt64 ReturnStructAlignmentInt64(int8_t a0,
                                                             int64_t a1,
@@ -5634,7 +6120,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Simple nested struct.
 DART_EXPORT Struct8BytesNestedInt
 ReturnStruct8BytesNestedInt(Struct4BytesHomogeneousInt16 a0,
@@ -5659,7 +6145,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Simple nested struct with floats.
 DART_EXPORT Struct8BytesNestedFloat
 ReturnStruct8BytesNestedFloat(Struct4BytesFloat a0, Struct4BytesFloat a1) {
@@ -5679,7 +6165,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // The nesting is irregular, testing homogenous float rules on arm and arm64,
 // and the fpu register usage on x64.
 DART_EXPORT Struct8BytesNestedFloat2
@@ -5700,7 +6186,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Simple nested struct with mixed members.
 DART_EXPORT Struct8BytesNestedMixed
 ReturnStruct8BytesNestedMixed(Struct4BytesHomogeneousInt16 a0,
@@ -5723,7 +6209,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Deeper nested struct to test recursive member access.
 DART_EXPORT Struct16BytesNestedInt
 ReturnStruct16BytesNestedInt(Struct8BytesNestedInt a0,
@@ -5755,7 +6241,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Even deeper nested struct to test recursive member access.
 DART_EXPORT Struct32BytesNestedInt
 ReturnStruct32BytesNestedInt(Struct16BytesNestedInt a0,
@@ -5804,7 +6290,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test alignment and padding of nested struct with 16 byte int.
 DART_EXPORT StructNestedIntStructAlignmentInt16
 ReturnStructNestedIntStructAlignmentInt16(StructAlignmentInt16 a0,
@@ -5834,7 +6320,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test alignment and padding of nested struct with 32 byte int.
 DART_EXPORT StructNestedIntStructAlignmentInt32
 ReturnStructNestedIntStructAlignmentInt32(StructAlignmentInt32 a0,
@@ -5864,7 +6350,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test alignment and padding of nested struct with 64 byte int.
 DART_EXPORT StructNestedIntStructAlignmentInt64
 ReturnStructNestedIntStructAlignmentInt64(StructAlignmentInt64 a0,
@@ -5894,7 +6380,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Return big irregular struct as smoke test.
 DART_EXPORT StructNestedIrregularEvenBigger
 ReturnStructNestedIrregularEvenBigger(uint64_t a0,
@@ -5977,7 +6463,7 @@
   return result;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Smallest struct with data.
 // 10 struct arguments will exhaust available registers.
 DART_EXPORT intptr_t TestPassStruct1ByteIntx10(
@@ -6047,7 +6533,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Not a multiple of word size, not a power of two.
 // 10 struct arguments will exhaust available registers.
 DART_EXPORT intptr_t TestPassStruct3BytesHomogeneousUint8x10(
@@ -6151,7 +6637,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Not a multiple of word size, not a power of two.
 // With alignment rules taken into account size is 4 bytes.
 // 10 struct arguments will exhaust available registers.
@@ -6235,7 +6721,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Exactly word size on 32-bit architectures.
 // 10 struct arguments will exhaust available registers.
 DART_EXPORT intptr_t TestPassStruct4BytesHomogeneousInt16x10(
@@ -6314,7 +6800,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Sub word size on 64 bit architectures.
 // 10 struct arguments will exhaust available registers.
 DART_EXPORT intptr_t TestPassStruct7BytesHomogeneousUint8x10(
@@ -6474,7 +6960,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Sub word size on 64 bit architectures.
 // With alignment rules taken into account size is 8 bytes.
 // 10 struct arguments will exhaust available registers.
@@ -6570,7 +7056,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Exactly word size struct on 64bit architectures.
 // 10 struct arguments will exhaust available registers.
 DART_EXPORT intptr_t TestPassStruct8BytesIntx10(
@@ -6662,7 +7148,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Arguments passed in FP registers as long as they fit.
 // 10 struct arguments will exhaust available registers.
 DART_EXPORT intptr_t TestPassStruct8BytesHomogeneousFloatx10(
@@ -6741,7 +7227,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // On x64, arguments go in int registers because it is not only float.
 // 10 struct arguments will exhaust available registers.
 DART_EXPORT intptr_t TestPassStruct8BytesMixedx10(
@@ -6833,7 +7319,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Argument is a single byte over a multiple of word size.
 // 10 struct arguments will exhaust available registers.
 // Struct only has 1-byte aligned fields to test struct alignment itself.
@@ -7026,7 +7512,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Argument is a single byte over a multiple of word size.
 // With alignment rules taken into account size is 12 or 16 bytes.
 // 10 struct arguments will exhaust available registers.
@@ -7111,7 +7597,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Arguments in FPU registers on arm hardfp and arm64.
 // Struct arguments will exhaust available registers, and leave some empty.
 // The last argument is to test whether arguments are backfilled.
@@ -7181,7 +7667,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // On Linux x64 argument is transferred on stack because it is over 16 bytes.
 // Arguments in FPU registers on arm hardfp and arm64.
 // 5 struct arguments will exhaust available registers.
@@ -7251,7 +7737,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // On x64, arguments are split over FP and int registers.
 // On x64, it will exhaust the integer registers with the 6th argument.
 // The rest goes on the stack.
@@ -7332,7 +7818,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // On x64, arguments are split over FP and int registers.
 // On x64, it will exhaust the integer registers with the 6th argument.
 // The rest goes on the stack.
@@ -7439,7 +7925,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Arguments are passed as pointer to copy on arm64.
 // Tests that the memory allocated for copies are rounded up to word size.
 DART_EXPORT intptr_t TestPassStruct17BytesIntx10(
@@ -7534,7 +8020,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // The minimum alignment of this struct is only 1 byte based on its fields.
 // Test that the memory backing these structs is extended to the right size.
 //
@@ -7875,7 +8361,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Argument too big to go into integer registers on arm64.
 // The arguments are passed as pointers to copies.
 // The amount of arguments exhausts the number of integer registers, such that
@@ -7994,7 +8480,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Argument too big to go into FPU registers in hardfp and arm64.
 DART_EXPORT intptr_t TestPassStruct20BytesHomogeneousFloat(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -8035,7 +8521,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Arguments in FPU registers on arm64.
 // 5 struct arguments will exhaust available registers.
 DART_EXPORT intptr_t TestPassStruct32BytesHomogeneousDoublex5(
@@ -8104,7 +8590,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Argument too big to go into FPU registers in arm64.
 DART_EXPORT intptr_t TestPassStruct40BytesHomogeneousDouble(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -8145,7 +8631,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test 1kb struct.
 DART_EXPORT intptr_t TestPassStruct1024BytesHomogeneousUint64(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -8344,7 +8830,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Tests the alignment of structs in FPU registers and backfilling.
 DART_EXPORT intptr_t TestPassFloatStruct16BytesHomogeneousFloatFloatStruct1(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -8421,7 +8907,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Tests the alignment of structs in FPU registers and backfilling.
 DART_EXPORT intptr_t TestPassFloatStruct32BytesHomogeneousDoubleFloatStruct(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -8498,7 +8984,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Tests the alignment of structs in integers registers and on the stack.
 // Arm32 aligns this struct at 8.
 // Also, arm32 allocates the second struct partially in registers, partially
@@ -8570,7 +9056,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // On Linux x64, it will exhaust xmm registers first, after 6 doubles and 2
 // structs. The rest of the structs will go on the stack.
 // The int will be backfilled into the int register.
@@ -8645,7 +9131,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // On Linux x64, it will exhaust int registers first.
 // The rest of the structs will go on the stack.
 // The double will be backfilled into the xmm register.
@@ -8714,7 +9200,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // On various architectures, first struct is allocated on stack.
 // Check that the other two arguments are allocated on registers.
 DART_EXPORT intptr_t TestPassStruct40BytesHomogeneousDoubleStruct4BytesHomo(
@@ -8765,7 +9251,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test alignment and padding of 16 byte int within struct.
 DART_EXPORT intptr_t TestPassInt32x8Doublex8Int64Int8Struct1ByteIntInt64Int(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -8955,7 +9441,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test alignment and padding of 16 byte int within struct.
 DART_EXPORT intptr_t TestPassStructAlignmentInt16(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -8994,7 +9480,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test alignment and padding of 32 byte int within struct.
 DART_EXPORT intptr_t TestPassStructAlignmentInt32(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -9033,7 +9519,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test alignment and padding of 64 byte int within struct.
 DART_EXPORT intptr_t TestPassStructAlignmentInt64(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -9072,7 +9558,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Simple nested struct. No alignment gaps on any architectures.
 // 10 arguments exhaust registers on all platforms.
 DART_EXPORT intptr_t TestPassStruct8BytesNestedIntx10(
@@ -9179,7 +9665,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Simple nested struct. No alignment gaps on any architectures.
 // 10 arguments exhaust fpu registers on all platforms.
 DART_EXPORT intptr_t TestPassStruct8BytesNestedFloatx10(
@@ -9260,7 +9746,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Simple nested struct. No alignment gaps on any architectures.
 // 10 arguments exhaust fpu registers on all platforms.
 // The nesting is irregular, testing homogenous float rules on arm and arm64,
@@ -9342,7 +9828,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Simple nested struct. No alignment gaps on any architectures.
 // 10 arguments exhaust all registers on all platforms.
 DART_EXPORT intptr_t TestPassStruct8BytesNestedMixedx10(
@@ -9436,7 +9922,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Deeper nested struct to test recursive member access.
 DART_EXPORT intptr_t TestPassStruct16BytesNestedIntx2(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -9494,7 +9980,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Even deeper nested struct to test recursive member access.
 DART_EXPORT intptr_t TestPassStruct32BytesNestedIntx2(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -9577,7 +10063,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test alignment and padding of nested struct with 16 byte int.
 DART_EXPORT intptr_t TestPassStructNestedIntStructAlignmentInt16(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -9621,7 +10107,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test alignment and padding of nested struct with 32 byte int.
 DART_EXPORT intptr_t TestPassStructNestedIntStructAlignmentInt32(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -9665,7 +10151,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test alignment and padding of nested struct with 64 byte int.
 DART_EXPORT intptr_t TestPassStructNestedIntStructAlignmentInt64(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -9709,7 +10195,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Return big irregular struct as smoke test.
 DART_EXPORT intptr_t TestPassStructNestedIrregularEvenBiggerx4(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -9940,7 +10426,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Simple struct with inline array.
 DART_EXPORT intptr_t TestPassStruct8BytesInlineArrayIntx4(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -10035,7 +10521,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Irregular struct with inline array.
 DART_EXPORT intptr_t TestPassStructInlineArrayIrregularx4(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -10107,7 +10593,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Regular larger struct with inline array.
 DART_EXPORT intptr_t TestPassStructInlineArray100Bytes(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -10309,7 +10795,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Arguments in FPU registers on arm hardfp and arm64.
 // 5 struct arguments will exhaust available registers.
 DART_EXPORT intptr_t TestPassStructStruct16BytesHomogeneousFloat2x5(
@@ -10380,7 +10866,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Arguments in FPU registers on arm64.
 // 5 struct arguments will exhaust available registers.
 DART_EXPORT intptr_t TestPassStructStruct32BytesHomogeneousDouble2x5(
@@ -10451,7 +10937,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // On x64, arguments are split over FP and int registers.
 // On x64, it will exhaust the integer registers with the 6th argument.
 // The rest goes on the stack.
@@ -10589,7 +11075,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test multi dimensional inline array struct as argument.
 DART_EXPORT intptr_t TestPassUint8Struct32BytesInlineArrayMultiDimensionalI(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -10739,7 +11225,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test struct in multi dimensional inline array.
 DART_EXPORT intptr_t TestPassUint8Struct4BytesInlineArrayMultiDimensionalIn(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -10789,7 +11275,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Small struct with mis-aligned member.
 DART_EXPORT intptr_t TestPassStruct3BytesPackedIntx10(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -10871,7 +11357,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Struct with mis-aligned member.
 DART_EXPORT intptr_t TestPassStruct8BytesPackedIntx10(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -11003,7 +11489,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Struct with mis-aligned member.
 // Tests backfilling of CPU and FPU registers.
 DART_EXPORT intptr_t TestPassStruct9BytesPackedMixedx10DoubleInt32(
@@ -11093,7 +11579,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // This packed struct happens to have only aligned members.
 DART_EXPORT intptr_t TestPassStruct5BytesPackedMixed(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -11130,7 +11616,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Check alignment of packed struct in non-packed struct.
 DART_EXPORT intptr_t TestPassStructNestedAlignmentStruct5BytesPackedMixed(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -11169,7 +11655,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Check alignment of packed struct array in non-packed struct.
 DART_EXPORT intptr_t TestPassStruct6BytesInlineArrayInt(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -11210,7 +11696,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Check alignment of packed struct array in non-packed struct.
 DART_EXPORT intptr_t TestPassStruct15BytesInlineArrayMixed(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -11254,7 +11740,521 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
+// Check placement of mixed integer/float union.
+DART_EXPORT intptr_t TestPassUnion4BytesMixedx10(
+    // NOLINTNEXTLINE(whitespace/parens)
+    double (*f)(Union4BytesMixed a0,
+                Union4BytesMixed a1,
+                Union4BytesMixed a2,
+                Union4BytesMixed a3,
+                Union4BytesMixed a4,
+                Union4BytesMixed a5,
+                Union4BytesMixed a6,
+                Union4BytesMixed a7,
+                Union4BytesMixed a8,
+                Union4BytesMixed a9)) {
+  Union4BytesMixed a0;
+  Union4BytesMixed a1;
+  Union4BytesMixed a2;
+  Union4BytesMixed a3;
+  Union4BytesMixed a4;
+  Union4BytesMixed a5;
+  Union4BytesMixed a6;
+  Union4BytesMixed a7;
+  Union4BytesMixed a8;
+  Union4BytesMixed a9;
+
+  a0.a0 = 1;
+  a1.a0 = 2;
+  a2.a0 = 3;
+  a3.a0 = 4;
+  a4.a0 = 5;
+  a5.a0 = 6;
+  a6.a0 = 7;
+  a7.a0 = 8;
+  a8.a0 = 9;
+  a9.a0 = 10;
+
+  std::cout << "Calling TestPassUnion4BytesMixedx10("
+            << "((" << a0.a0 << ", " << a0.a1 << "), (" << a1.a0 << ", "
+            << a1.a1 << "), (" << a2.a0 << ", " << a2.a1 << "), (" << a3.a0
+            << ", " << a3.a1 << "), (" << a4.a0 << ", " << a4.a1 << "), ("
+            << a5.a0 << ", " << a5.a1 << "), (" << a6.a0 << ", " << a6.a1
+            << "), (" << a7.a0 << ", " << a7.a1 << "), (" << a8.a0 << ", "
+            << a8.a1 << "), (" << a9.a0 << ", " << a9.a1 << "))"
+            << ")\n";
+
+  double result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  std::cout << "result = " << result << "\n";
+
+  CHECK_APPROX(55.0, result);
+
+  // Pass argument that will make the Dart callback throw.
+  a0.a0 = 42;
+
+  result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  CHECK_APPROX(0.0, result);
+
+  // Pass argument that will make the Dart callback return null.
+  a0.a0 = 84;
+
+  result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  CHECK_APPROX(0.0, result);
+
+  return 0;
+}
+
+// Used for testing structs and unions by value.
+// Check placement of mixed floats union.
+DART_EXPORT intptr_t TestPassUnion8BytesNestedFloatx10(
+    // NOLINTNEXTLINE(whitespace/parens)
+    double (*f)(Union8BytesNestedFloat a0,
+                Union8BytesNestedFloat a1,
+                Union8BytesNestedFloat a2,
+                Union8BytesNestedFloat a3,
+                Union8BytesNestedFloat a4,
+                Union8BytesNestedFloat a5,
+                Union8BytesNestedFloat a6,
+                Union8BytesNestedFloat a7,
+                Union8BytesNestedFloat a8,
+                Union8BytesNestedFloat a9)) {
+  Union8BytesNestedFloat a0;
+  Union8BytesNestedFloat a1;
+  Union8BytesNestedFloat a2;
+  Union8BytesNestedFloat a3;
+  Union8BytesNestedFloat a4;
+  Union8BytesNestedFloat a5;
+  Union8BytesNestedFloat a6;
+  Union8BytesNestedFloat a7;
+  Union8BytesNestedFloat a8;
+  Union8BytesNestedFloat a9;
+
+  a0.a0 = -1.0;
+  a1.a0 = 2.0;
+  a2.a0 = -3.0;
+  a3.a0 = 4.0;
+  a4.a0 = -5.0;
+  a5.a0 = 6.0;
+  a6.a0 = -7.0;
+  a7.a0 = 8.0;
+  a8.a0 = -9.0;
+  a9.a0 = 10.0;
+
+  std::cout << "Calling TestPassUnion8BytesNestedFloatx10("
+            << "((" << a0.a0 << ", (" << a0.a1.a0 << ", " << a0.a1.a1 << ")), ("
+            << a1.a0 << ", (" << a1.a1.a0 << ", " << a1.a1.a1 << ")), ("
+            << a2.a0 << ", (" << a2.a1.a0 << ", " << a2.a1.a1 << ")), ("
+            << a3.a0 << ", (" << a3.a1.a0 << ", " << a3.a1.a1 << ")), ("
+            << a4.a0 << ", (" << a4.a1.a0 << ", " << a4.a1.a1 << ")), ("
+            << a5.a0 << ", (" << a5.a1.a0 << ", " << a5.a1.a1 << ")), ("
+            << a6.a0 << ", (" << a6.a1.a0 << ", " << a6.a1.a1 << ")), ("
+            << a7.a0 << ", (" << a7.a1.a0 << ", " << a7.a1.a1 << ")), ("
+            << a8.a0 << ", (" << a8.a1.a0 << ", " << a8.a1.a1 << ")), ("
+            << a9.a0 << ", (" << a9.a1.a0 << ", " << a9.a1.a1 << ")))"
+            << ")\n";
+
+  double result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  std::cout << "result = " << result << "\n";
+
+  CHECK_APPROX(5.0, result);
+
+  // Pass argument that will make the Dart callback throw.
+  a0.a0 = 42;
+
+  result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  CHECK_APPROX(0.0, result);
+
+  // Pass argument that will make the Dart callback return null.
+  a0.a0 = 84;
+
+  result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  CHECK_APPROX(0.0, result);
+
+  return 0;
+}
+
+// Used for testing structs and unions by value.
+// Mixed-size union argument.
+DART_EXPORT intptr_t TestPassUnion9BytesNestedIntx10(
+    // NOLINTNEXTLINE(whitespace/parens)
+    double (*f)(Union9BytesNestedInt a0,
+                Union9BytesNestedInt a1,
+                Union9BytesNestedInt a2,
+                Union9BytesNestedInt a3,
+                Union9BytesNestedInt a4,
+                Union9BytesNestedInt a5,
+                Union9BytesNestedInt a6,
+                Union9BytesNestedInt a7,
+                Union9BytesNestedInt a8,
+                Union9BytesNestedInt a9)) {
+  Union9BytesNestedInt a0;
+  Union9BytesNestedInt a1;
+  Union9BytesNestedInt a2;
+  Union9BytesNestedInt a3;
+  Union9BytesNestedInt a4;
+  Union9BytesNestedInt a5;
+  Union9BytesNestedInt a6;
+  Union9BytesNestedInt a7;
+  Union9BytesNestedInt a8;
+  Union9BytesNestedInt a9;
+
+  a0.a0.a0 = -1;
+  a0.a0.a1 = 2;
+  a0.a0.a2 = -3;
+  a1.a0.a0 = 4;
+  a1.a0.a1 = -5;
+  a1.a0.a2 = 6;
+  a2.a0.a0 = -7;
+  a2.a0.a1 = 8;
+  a2.a0.a2 = -9;
+  a3.a0.a0 = 10;
+  a3.a0.a1 = -11;
+  a3.a0.a2 = 12;
+  a4.a0.a0 = -13;
+  a4.a0.a1 = 14;
+  a4.a0.a2 = -15;
+  a5.a0.a0 = 16;
+  a5.a0.a1 = -17;
+  a5.a0.a2 = 18;
+  a6.a0.a0 = -19;
+  a6.a0.a1 = 20;
+  a6.a0.a2 = -21;
+  a7.a0.a0 = 22;
+  a7.a0.a1 = -23;
+  a7.a0.a2 = 24;
+  a8.a0.a0 = -25;
+  a8.a0.a1 = 26;
+  a8.a0.a2 = -27;
+  a9.a0.a0 = 28;
+  a9.a0.a1 = -29;
+  a9.a0.a2 = 30;
+
+  std::cout << "Calling TestPassUnion9BytesNestedIntx10("
+            << "(((" << a0.a0.a0 << ", " << a0.a0.a1 << ", " << a0.a0.a2
+            << "), (" << static_cast<int>(a0.a1.a0) << ", "
+            << static_cast<int>(a0.a1.a1) << ", " << static_cast<int>(a0.a1.a2)
+            << ", " << static_cast<int>(a0.a1.a3) << ", "
+            << static_cast<int>(a0.a1.a4) << ", " << static_cast<int>(a0.a1.a5)
+            << ", " << static_cast<int>(a0.a1.a6) << ", "
+            << static_cast<int>(a0.a1.a7) << ", " << static_cast<int>(a0.a1.a8)
+            << ")), ((" << a1.a0.a0 << ", " << a1.a0.a1 << ", " << a1.a0.a2
+            << "), (" << static_cast<int>(a1.a1.a0) << ", "
+            << static_cast<int>(a1.a1.a1) << ", " << static_cast<int>(a1.a1.a2)
+            << ", " << static_cast<int>(a1.a1.a3) << ", "
+            << static_cast<int>(a1.a1.a4) << ", " << static_cast<int>(a1.a1.a5)
+            << ", " << static_cast<int>(a1.a1.a6) << ", "
+            << static_cast<int>(a1.a1.a7) << ", " << static_cast<int>(a1.a1.a8)
+            << ")), ((" << a2.a0.a0 << ", " << a2.a0.a1 << ", " << a2.a0.a2
+            << "), (" << static_cast<int>(a2.a1.a0) << ", "
+            << static_cast<int>(a2.a1.a1) << ", " << static_cast<int>(a2.a1.a2)
+            << ", " << static_cast<int>(a2.a1.a3) << ", "
+            << static_cast<int>(a2.a1.a4) << ", " << static_cast<int>(a2.a1.a5)
+            << ", " << static_cast<int>(a2.a1.a6) << ", "
+            << static_cast<int>(a2.a1.a7) << ", " << static_cast<int>(a2.a1.a8)
+            << ")), ((" << a3.a0.a0 << ", " << a3.a0.a1 << ", " << a3.a0.a2
+            << "), (" << static_cast<int>(a3.a1.a0) << ", "
+            << static_cast<int>(a3.a1.a1) << ", " << static_cast<int>(a3.a1.a2)
+            << ", " << static_cast<int>(a3.a1.a3) << ", "
+            << static_cast<int>(a3.a1.a4) << ", " << static_cast<int>(a3.a1.a5)
+            << ", " << static_cast<int>(a3.a1.a6) << ", "
+            << static_cast<int>(a3.a1.a7) << ", " << static_cast<int>(a3.a1.a8)
+            << ")), ((" << a4.a0.a0 << ", " << a4.a0.a1 << ", " << a4.a0.a2
+            << "), (" << static_cast<int>(a4.a1.a0) << ", "
+            << static_cast<int>(a4.a1.a1) << ", " << static_cast<int>(a4.a1.a2)
+            << ", " << static_cast<int>(a4.a1.a3) << ", "
+            << static_cast<int>(a4.a1.a4) << ", " << static_cast<int>(a4.a1.a5)
+            << ", " << static_cast<int>(a4.a1.a6) << ", "
+            << static_cast<int>(a4.a1.a7) << ", " << static_cast<int>(a4.a1.a8)
+            << ")), ((" << a5.a0.a0 << ", " << a5.a0.a1 << ", " << a5.a0.a2
+            << "), (" << static_cast<int>(a5.a1.a0) << ", "
+            << static_cast<int>(a5.a1.a1) << ", " << static_cast<int>(a5.a1.a2)
+            << ", " << static_cast<int>(a5.a1.a3) << ", "
+            << static_cast<int>(a5.a1.a4) << ", " << static_cast<int>(a5.a1.a5)
+            << ", " << static_cast<int>(a5.a1.a6) << ", "
+            << static_cast<int>(a5.a1.a7) << ", " << static_cast<int>(a5.a1.a8)
+            << ")), ((" << a6.a0.a0 << ", " << a6.a0.a1 << ", " << a6.a0.a2
+            << "), (" << static_cast<int>(a6.a1.a0) << ", "
+            << static_cast<int>(a6.a1.a1) << ", " << static_cast<int>(a6.a1.a2)
+            << ", " << static_cast<int>(a6.a1.a3) << ", "
+            << static_cast<int>(a6.a1.a4) << ", " << static_cast<int>(a6.a1.a5)
+            << ", " << static_cast<int>(a6.a1.a6) << ", "
+            << static_cast<int>(a6.a1.a7) << ", " << static_cast<int>(a6.a1.a8)
+            << ")), ((" << a7.a0.a0 << ", " << a7.a0.a1 << ", " << a7.a0.a2
+            << "), (" << static_cast<int>(a7.a1.a0) << ", "
+            << static_cast<int>(a7.a1.a1) << ", " << static_cast<int>(a7.a1.a2)
+            << ", " << static_cast<int>(a7.a1.a3) << ", "
+            << static_cast<int>(a7.a1.a4) << ", " << static_cast<int>(a7.a1.a5)
+            << ", " << static_cast<int>(a7.a1.a6) << ", "
+            << static_cast<int>(a7.a1.a7) << ", " << static_cast<int>(a7.a1.a8)
+            << ")), ((" << a8.a0.a0 << ", " << a8.a0.a1 << ", " << a8.a0.a2
+            << "), (" << static_cast<int>(a8.a1.a0) << ", "
+            << static_cast<int>(a8.a1.a1) << ", " << static_cast<int>(a8.a1.a2)
+            << ", " << static_cast<int>(a8.a1.a3) << ", "
+            << static_cast<int>(a8.a1.a4) << ", " << static_cast<int>(a8.a1.a5)
+            << ", " << static_cast<int>(a8.a1.a6) << ", "
+            << static_cast<int>(a8.a1.a7) << ", " << static_cast<int>(a8.a1.a8)
+            << ")), ((" << a9.a0.a0 << ", " << a9.a0.a1 << ", " << a9.a0.a2
+            << "), (" << static_cast<int>(a9.a1.a0) << ", "
+            << static_cast<int>(a9.a1.a1) << ", " << static_cast<int>(a9.a1.a2)
+            << ", " << static_cast<int>(a9.a1.a3) << ", "
+            << static_cast<int>(a9.a1.a4) << ", " << static_cast<int>(a9.a1.a5)
+            << ", " << static_cast<int>(a9.a1.a6) << ", "
+            << static_cast<int>(a9.a1.a7) << ", " << static_cast<int>(a9.a1.a8)
+            << ")))"
+            << ")\n";
+
+  double result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  std::cout << "result = " << result << "\n";
+
+  CHECK_APPROX(15.0, result);
+
+  // Pass argument that will make the Dart callback throw.
+  a0.a0.a0 = 42;
+
+  result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  CHECK_APPROX(0.0, result);
+
+  // Pass argument that will make the Dart callback return null.
+  a0.a0.a0 = 84;
+
+  result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  CHECK_APPROX(0.0, result);
+
+  return 0;
+}
+
+// Used for testing structs and unions by value.
+// Union with homogenous floats.
+DART_EXPORT intptr_t TestPassUnion16BytesNestedInlineArrayFloatx10(
+    // NOLINTNEXTLINE(whitespace/parens)
+    double (*f)(Union16BytesNestedInlineArrayFloat a0,
+                Union16BytesNestedInlineArrayFloat a1,
+                Union16BytesNestedInlineArrayFloat a2,
+                Union16BytesNestedInlineArrayFloat a3,
+                Union16BytesNestedInlineArrayFloat a4,
+                Union16BytesNestedInlineArrayFloat a5,
+                Union16BytesNestedInlineArrayFloat a6,
+                Union16BytesNestedInlineArrayFloat a7,
+                Union16BytesNestedInlineArrayFloat a8,
+                Union16BytesNestedInlineArrayFloat a9)) {
+  Union16BytesNestedInlineArrayFloat a0;
+  Union16BytesNestedInlineArrayFloat a1;
+  Union16BytesNestedInlineArrayFloat a2;
+  Union16BytesNestedInlineArrayFloat a3;
+  Union16BytesNestedInlineArrayFloat a4;
+  Union16BytesNestedInlineArrayFloat a5;
+  Union16BytesNestedInlineArrayFloat a6;
+  Union16BytesNestedInlineArrayFloat a7;
+  Union16BytesNestedInlineArrayFloat a8;
+  Union16BytesNestedInlineArrayFloat a9;
+
+  a0.a0[0] = -1.0;
+  a0.a0[1] = 2.0;
+  a0.a0[2] = -3.0;
+  a0.a0[3] = 4.0;
+  a1.a0[0] = -5.0;
+  a1.a0[1] = 6.0;
+  a1.a0[2] = -7.0;
+  a1.a0[3] = 8.0;
+  a2.a0[0] = -9.0;
+  a2.a0[1] = 10.0;
+  a2.a0[2] = -11.0;
+  a2.a0[3] = 12.0;
+  a3.a0[0] = -13.0;
+  a3.a0[1] = 14.0;
+  a3.a0[2] = -15.0;
+  a3.a0[3] = 16.0;
+  a4.a0[0] = -17.0;
+  a4.a0[1] = 18.0;
+  a4.a0[2] = -19.0;
+  a4.a0[3] = 20.0;
+  a5.a0[0] = -21.0;
+  a5.a0[1] = 22.0;
+  a5.a0[2] = -23.0;
+  a5.a0[3] = 24.0;
+  a6.a0[0] = -25.0;
+  a6.a0[1] = 26.0;
+  a6.a0[2] = -27.0;
+  a6.a0[3] = 28.0;
+  a7.a0[0] = -29.0;
+  a7.a0[1] = 30.0;
+  a7.a0[2] = -31.0;
+  a7.a0[3] = 32.0;
+  a8.a0[0] = -33.0;
+  a8.a0[1] = 34.0;
+  a8.a0[2] = -35.0;
+  a8.a0[3] = 36.0;
+  a9.a0[0] = -37.0;
+  a9.a0[1] = 38.0;
+  a9.a0[2] = -39.0;
+  a9.a0[3] = 40.0;
+
+  std::cout << "Calling TestPassUnion16BytesNestedInlineArrayFloatx10("
+            << "(([" << a0.a0[0] << ", " << a0.a0[1] << ", " << a0.a0[2] << ", "
+            << a0.a0[3] << "], (" << a0.a1.a0 << ", " << a0.a1.a1 << ", "
+            << a0.a1.a2 << ", " << a0.a1.a3 << ")), ([" << a1.a0[0] << ", "
+            << a1.a0[1] << ", " << a1.a0[2] << ", " << a1.a0[3] << "], ("
+            << a1.a1.a0 << ", " << a1.a1.a1 << ", " << a1.a1.a2 << ", "
+            << a1.a1.a3 << ")), ([" << a2.a0[0] << ", " << a2.a0[1] << ", "
+            << a2.a0[2] << ", " << a2.a0[3] << "], (" << a2.a1.a0 << ", "
+            << a2.a1.a1 << ", " << a2.a1.a2 << ", " << a2.a1.a3 << ")), (["
+            << a3.a0[0] << ", " << a3.a0[1] << ", " << a3.a0[2] << ", "
+            << a3.a0[3] << "], (" << a3.a1.a0 << ", " << a3.a1.a1 << ", "
+            << a3.a1.a2 << ", " << a3.a1.a3 << ")), ([" << a4.a0[0] << ", "
+            << a4.a0[1] << ", " << a4.a0[2] << ", " << a4.a0[3] << "], ("
+            << a4.a1.a0 << ", " << a4.a1.a1 << ", " << a4.a1.a2 << ", "
+            << a4.a1.a3 << ")), ([" << a5.a0[0] << ", " << a5.a0[1] << ", "
+            << a5.a0[2] << ", " << a5.a0[3] << "], (" << a5.a1.a0 << ", "
+            << a5.a1.a1 << ", " << a5.a1.a2 << ", " << a5.a1.a3 << ")), (["
+            << a6.a0[0] << ", " << a6.a0[1] << ", " << a6.a0[2] << ", "
+            << a6.a0[3] << "], (" << a6.a1.a0 << ", " << a6.a1.a1 << ", "
+            << a6.a1.a2 << ", " << a6.a1.a3 << ")), ([" << a7.a0[0] << ", "
+            << a7.a0[1] << ", " << a7.a0[2] << ", " << a7.a0[3] << "], ("
+            << a7.a1.a0 << ", " << a7.a1.a1 << ", " << a7.a1.a2 << ", "
+            << a7.a1.a3 << ")), ([" << a8.a0[0] << ", " << a8.a0[1] << ", "
+            << a8.a0[2] << ", " << a8.a0[3] << "], (" << a8.a1.a0 << ", "
+            << a8.a1.a1 << ", " << a8.a1.a2 << ", " << a8.a1.a3 << ")), (["
+            << a9.a0[0] << ", " << a9.a0[1] << ", " << a9.a0[2] << ", "
+            << a9.a0[3] << "], (" << a9.a1.a0 << ", " << a9.a1.a1 << ", "
+            << a9.a1.a2 << ", " << a9.a1.a3 << ")))"
+            << ")\n";
+
+  double result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  std::cout << "result = " << result << "\n";
+
+  CHECK_APPROX(20.0, result);
+
+  // Pass argument that will make the Dart callback throw.
+  a0.a0[0] = 42;
+
+  result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  CHECK_APPROX(0.0, result);
+
+  // Pass argument that will make the Dart callback return null.
+  a0.a0[0] = 84;
+
+  result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  CHECK_APPROX(0.0, result);
+
+  return 0;
+}
+
+// Used for testing structs and unions by value.
+// Union with homogenous floats.
+DART_EXPORT intptr_t TestPassUnion16BytesNestedFloatx10(
+    // NOLINTNEXTLINE(whitespace/parens)
+    double (*f)(Union16BytesNestedFloat a0,
+                Union16BytesNestedFloat a1,
+                Union16BytesNestedFloat a2,
+                Union16BytesNestedFloat a3,
+                Union16BytesNestedFloat a4,
+                Union16BytesNestedFloat a5,
+                Union16BytesNestedFloat a6,
+                Union16BytesNestedFloat a7,
+                Union16BytesNestedFloat a8,
+                Union16BytesNestedFloat a9)) {
+  Union16BytesNestedFloat a0;
+  Union16BytesNestedFloat a1;
+  Union16BytesNestedFloat a2;
+  Union16BytesNestedFloat a3;
+  Union16BytesNestedFloat a4;
+  Union16BytesNestedFloat a5;
+  Union16BytesNestedFloat a6;
+  Union16BytesNestedFloat a7;
+  Union16BytesNestedFloat a8;
+  Union16BytesNestedFloat a9;
+
+  a0.a0.a0 = -1.0;
+  a0.a0.a1 = 2.0;
+  a1.a0.a0 = -3.0;
+  a1.a0.a1 = 4.0;
+  a2.a0.a0 = -5.0;
+  a2.a0.a1 = 6.0;
+  a3.a0.a0 = -7.0;
+  a3.a0.a1 = 8.0;
+  a4.a0.a0 = -9.0;
+  a4.a0.a1 = 10.0;
+  a5.a0.a0 = -11.0;
+  a5.a0.a1 = 12.0;
+  a6.a0.a0 = -13.0;
+  a6.a0.a1 = 14.0;
+  a7.a0.a0 = -15.0;
+  a7.a0.a1 = 16.0;
+  a8.a0.a0 = -17.0;
+  a8.a0.a1 = 18.0;
+  a9.a0.a0 = -19.0;
+  a9.a0.a1 = 20.0;
+
+  std::cout << "Calling TestPassUnion16BytesNestedFloatx10("
+            << "(((" << a0.a0.a0 << ", " << a0.a0.a1 << "), (" << a0.a1.a0
+            << ", " << a0.a1.a1 << ", " << a0.a1.a2 << "), (" << a0.a2.a0
+            << ", " << a0.a2.a1 << ", " << a0.a2.a2 << ", " << a0.a2.a3
+            << ")), ((" << a1.a0.a0 << ", " << a1.a0.a1 << "), (" << a1.a1.a0
+            << ", " << a1.a1.a1 << ", " << a1.a1.a2 << "), (" << a1.a2.a0
+            << ", " << a1.a2.a1 << ", " << a1.a2.a2 << ", " << a1.a2.a3
+            << ")), ((" << a2.a0.a0 << ", " << a2.a0.a1 << "), (" << a2.a1.a0
+            << ", " << a2.a1.a1 << ", " << a2.a1.a2 << "), (" << a2.a2.a0
+            << ", " << a2.a2.a1 << ", " << a2.a2.a2 << ", " << a2.a2.a3
+            << ")), ((" << a3.a0.a0 << ", " << a3.a0.a1 << "), (" << a3.a1.a0
+            << ", " << a3.a1.a1 << ", " << a3.a1.a2 << "), (" << a3.a2.a0
+            << ", " << a3.a2.a1 << ", " << a3.a2.a2 << ", " << a3.a2.a3
+            << ")), ((" << a4.a0.a0 << ", " << a4.a0.a1 << "), (" << a4.a1.a0
+            << ", " << a4.a1.a1 << ", " << a4.a1.a2 << "), (" << a4.a2.a0
+            << ", " << a4.a2.a1 << ", " << a4.a2.a2 << ", " << a4.a2.a3
+            << ")), ((" << a5.a0.a0 << ", " << a5.a0.a1 << "), (" << a5.a1.a0
+            << ", " << a5.a1.a1 << ", " << a5.a1.a2 << "), (" << a5.a2.a0
+            << ", " << a5.a2.a1 << ", " << a5.a2.a2 << ", " << a5.a2.a3
+            << ")), ((" << a6.a0.a0 << ", " << a6.a0.a1 << "), (" << a6.a1.a0
+            << ", " << a6.a1.a1 << ", " << a6.a1.a2 << "), (" << a6.a2.a0
+            << ", " << a6.a2.a1 << ", " << a6.a2.a2 << ", " << a6.a2.a3
+            << ")), ((" << a7.a0.a0 << ", " << a7.a0.a1 << "), (" << a7.a1.a0
+            << ", " << a7.a1.a1 << ", " << a7.a1.a2 << "), (" << a7.a2.a0
+            << ", " << a7.a2.a1 << ", " << a7.a2.a2 << ", " << a7.a2.a3
+            << ")), ((" << a8.a0.a0 << ", " << a8.a0.a1 << "), (" << a8.a1.a0
+            << ", " << a8.a1.a1 << ", " << a8.a1.a2 << "), (" << a8.a2.a0
+            << ", " << a8.a2.a1 << ", " << a8.a2.a2 << ", " << a8.a2.a3
+            << ")), ((" << a9.a0.a0 << ", " << a9.a0.a1 << "), (" << a9.a1.a0
+            << ", " << a9.a1.a1 << ", " << a9.a1.a2 << "), (" << a9.a2.a0
+            << ", " << a9.a2.a1 << ", " << a9.a2.a2 << ", " << a9.a2.a3 << ")))"
+            << ")\n";
+
+  double result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  std::cout << "result = " << result << "\n";
+
+  CHECK_APPROX(10.0, result);
+
+  // Pass argument that will make the Dart callback throw.
+  a0.a0.a0 = 42;
+
+  result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  CHECK_APPROX(0.0, result);
+
+  // Pass argument that will make the Dart callback return null.
+  a0.a0.a0 = 84;
+
+  result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  CHECK_APPROX(0.0, result);
+
+  return 0;
+}
+
+// Used for testing structs and unions by value.
 // Smallest struct with data.
 DART_EXPORT intptr_t TestReturnStruct1ByteInt(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -11292,7 +12292,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Smaller than word size return value on all architectures.
 DART_EXPORT intptr_t TestReturnStruct3BytesHomogeneousUint8(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -11343,7 +12343,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Smaller than word size return value on all architectures.
 // With alignment rules taken into account size is 4 bytes.
 DART_EXPORT intptr_t TestReturnStruct3BytesInt2ByteAligned(
@@ -11387,7 +12387,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Word size return value on 32 bit architectures..
 DART_EXPORT intptr_t TestReturnStruct4BytesHomogeneousInt16(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -11430,7 +12430,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Non-wordsize return value.
 DART_EXPORT intptr_t TestReturnStruct7BytesHomogeneousUint8(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -11513,7 +12513,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Non-wordsize return value.
 // With alignment rules taken into account size is 8 bytes.
 DART_EXPORT intptr_t TestReturnStruct7BytesInt4ByteAligned(
@@ -11563,7 +12563,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Return value in integer registers on many architectures.
 DART_EXPORT intptr_t TestReturnStruct8BytesInt(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -11611,7 +12611,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Return value in FP registers on many architectures.
 DART_EXPORT intptr_t TestReturnStruct8BytesHomogeneousFloat(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -11654,7 +12654,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Return value split over FP and integer register in x64.
 DART_EXPORT intptr_t TestReturnStruct8BytesMixed(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -11702,7 +12702,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // The minimum alignment of this struct is only 1 byte based on its fields.
 // Test that the memory backing these structs is the right size and that
 // dart:ffi trampolines do not write outside this size.
@@ -11802,7 +12802,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Return value in two integer registers on x64.
 // With alignment rules taken into account size is 12 or 16 bytes.
 DART_EXPORT intptr_t TestReturnStruct9BytesInt4Or8ByteAligned(
@@ -11846,7 +12846,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Return value in FPU registers, but does not use all registers on arm hardfp
 // and arm64.
 DART_EXPORT intptr_t TestReturnStruct12BytesHomogeneousFloat(
@@ -11895,7 +12895,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Return value in FPU registers on arm hardfp and arm64.
 DART_EXPORT intptr_t TestReturnStruct16BytesHomogeneousFloat(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -11950,7 +12950,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Return value split over FP and integer register in x64.
 DART_EXPORT intptr_t TestReturnStruct16BytesMixed(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -11993,7 +12993,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Return value split over FP and integer register in x64.
 // The integer register contains half float half int.
 DART_EXPORT intptr_t TestReturnStruct16BytesMixed2(
@@ -12048,7 +13048,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Rerturn value returned in preallocated space passed by pointer on most ABIs.
 // Is non word size on purpose, to test that structs are rounded up to word size
 // on all ABIs.
@@ -12099,7 +13099,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // The minimum alignment of this struct is only 1 byte based on its fields.
 // Test that the memory backing these structs is the right size and that
 // dart:ffi trampolines do not write outside this size.
@@ -12278,7 +13278,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Return value too big to go in cpu registers on arm64.
 DART_EXPORT intptr_t TestReturnStruct20BytesHomogeneousInt32(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -12339,7 +13339,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Return value too big to go in FPU registers on x64, arm hardfp and arm64.
 DART_EXPORT intptr_t TestReturnStruct20BytesHomogeneousFloat(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -12400,7 +13400,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Return value in FPU registers on arm64.
 DART_EXPORT intptr_t TestReturnStruct32BytesHomogeneousDouble(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -12455,7 +13455,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Return value too big to go in FPU registers on arm64.
 DART_EXPORT intptr_t TestReturnStruct40BytesHomogeneousDouble(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -12516,7 +13516,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test 1kb struct.
 DART_EXPORT intptr_t TestReturnStruct1024BytesHomogeneousUint64(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -13414,7 +14414,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Small struct with mis-aligned member.
 DART_EXPORT intptr_t TestReturnStruct3BytesPackedInt(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -13457,7 +14457,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Struct with mis-aligned member.
 DART_EXPORT intptr_t TestReturnStruct8BytesPackedInt(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -13521,7 +14521,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Struct with mis-aligned member.
 // Tests backfilling of CPU and FPU registers.
 DART_EXPORT intptr_t TestReturnStruct9BytesPackedMixed(
@@ -13565,7 +14565,184 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
+// Returning a mixed integer/float union.
+DART_EXPORT intptr_t TestReturnUnion4BytesMixed(
+    // NOLINTNEXTLINE(whitespace/parens)
+    Union4BytesMixed (*f)(uint32_t a0)) {
+  uint32_t a0;
+
+  a0 = 1;
+
+  std::cout << "Calling TestReturnUnion4BytesMixed("
+            << "(" << a0 << ")"
+            << ")\n";
+
+  Union4BytesMixed result = f(a0);
+
+  std::cout << "result = "
+            << "(" << result.a0 << ", " << result.a1 << ")"
+            << "\n";
+
+  CHECK_EQ(a0, result.a0);
+
+  // Pass argument that will make the Dart callback throw.
+  a0 = 42;
+
+  result = f(a0);
+
+  CHECK_EQ(0, result.a0);
+
+  // Pass argument that will make the Dart callback return null.
+  a0 = 84;
+
+  result = f(a0);
+
+  CHECK_EQ(0, result.a0);
+
+  return 0;
+}
+
+// Used for testing structs and unions by value.
+// Returning a floating point only union.
+DART_EXPORT intptr_t TestReturnUnion8BytesNestedFloat(
+    // NOLINTNEXTLINE(whitespace/parens)
+    Union8BytesNestedFloat (*f)(double a0)) {
+  double a0;
+
+  a0 = -1.0;
+
+  std::cout << "Calling TestReturnUnion8BytesNestedFloat("
+            << "(" << a0 << ")"
+            << ")\n";
+
+  Union8BytesNestedFloat result = f(a0);
+
+  std::cout << "result = "
+            << "(" << result.a0 << ", (" << result.a1.a0 << ", " << result.a1.a1
+            << "))"
+            << "\n";
+
+  CHECK_APPROX(a0, result.a0);
+
+  // Pass argument that will make the Dart callback throw.
+  a0 = 42;
+
+  result = f(a0);
+
+  CHECK_APPROX(0.0, result.a0);
+
+  // Pass argument that will make the Dart callback return null.
+  a0 = 84;
+
+  result = f(a0);
+
+  CHECK_APPROX(0.0, result.a0);
+
+  return 0;
+}
+
+// Used for testing structs and unions by value.
+// Returning a mixed-size union.
+DART_EXPORT intptr_t TestReturnUnion9BytesNestedInt(
+    // NOLINTNEXTLINE(whitespace/parens)
+    Union9BytesNestedInt (*f)(Struct8BytesInt a0)) {
+  Struct8BytesInt a0;
+
+  a0.a0 = -1;
+  a0.a1 = 2;
+  a0.a2 = -3;
+
+  std::cout << "Calling TestReturnUnion9BytesNestedInt("
+            << "((" << a0.a0 << ", " << a0.a1 << ", " << a0.a2 << "))"
+            << ")\n";
+
+  Union9BytesNestedInt result = f(a0);
+
+  std::cout << "result = "
+            << "((" << result.a0.a0 << ", " << result.a0.a1 << ", "
+            << result.a0.a2 << "), (" << static_cast<int>(result.a1.a0) << ", "
+            << static_cast<int>(result.a1.a1) << ", "
+            << static_cast<int>(result.a1.a2) << ", "
+            << static_cast<int>(result.a1.a3) << ", "
+            << static_cast<int>(result.a1.a4) << ", "
+            << static_cast<int>(result.a1.a5) << ", "
+            << static_cast<int>(result.a1.a6) << ", "
+            << static_cast<int>(result.a1.a7) << ", "
+            << static_cast<int>(result.a1.a8) << "))"
+            << "\n";
+
+  CHECK_EQ(a0.a0, result.a0.a0);
+  CHECK_EQ(a0.a1, result.a0.a1);
+  CHECK_EQ(a0.a2, result.a0.a2);
+
+  // Pass argument that will make the Dart callback throw.
+  a0.a0 = 42;
+
+  result = f(a0);
+
+  CHECK_EQ(0, result.a0.a0);
+  CHECK_EQ(0, result.a0.a1);
+  CHECK_EQ(0, result.a0.a2);
+
+  // Pass argument that will make the Dart callback return null.
+  a0.a0 = 84;
+
+  result = f(a0);
+
+  CHECK_EQ(0, result.a0.a0);
+  CHECK_EQ(0, result.a0.a1);
+  CHECK_EQ(0, result.a0.a2);
+
+  return 0;
+}
+
+// Used for testing structs and unions by value.
+// Returning union with homogenous floats.
+DART_EXPORT intptr_t TestReturnUnion16BytesNestedFloat(
+    // NOLINTNEXTLINE(whitespace/parens)
+    Union16BytesNestedFloat (*f)(Struct8BytesHomogeneousFloat a0)) {
+  Struct8BytesHomogeneousFloat a0;
+
+  a0.a0 = -1.0;
+  a0.a1 = 2.0;
+
+  std::cout << "Calling TestReturnUnion16BytesNestedFloat("
+            << "((" << a0.a0 << ", " << a0.a1 << "))"
+            << ")\n";
+
+  Union16BytesNestedFloat result = f(a0);
+
+  std::cout << "result = "
+            << "((" << result.a0.a0 << ", " << result.a0.a1 << "), ("
+            << result.a1.a0 << ", " << result.a1.a1 << ", " << result.a1.a2
+            << "), (" << result.a2.a0 << ", " << result.a2.a1 << ", "
+            << result.a2.a2 << ", " << result.a2.a3 << "))"
+            << "\n";
+
+  CHECK_APPROX(a0.a0, result.a0.a0);
+  CHECK_APPROX(a0.a1, result.a0.a1);
+
+  // Pass argument that will make the Dart callback throw.
+  a0.a0 = 42;
+
+  result = f(a0);
+
+  CHECK_APPROX(0.0, result.a0.a0);
+  CHECK_APPROX(0.0, result.a0.a1);
+
+  // Pass argument that will make the Dart callback return null.
+  a0.a0 = 84;
+
+  result = f(a0);
+
+  CHECK_APPROX(0.0, result.a0.a0);
+  CHECK_APPROX(0.0, result.a0.a1);
+
+  return 0;
+}
+
+// Used for testing structs and unions by value.
 // Test that a struct passed in as argument can be returned.
 // Especially for ffi callbacks.
 // Struct is passed in int registers in most ABIs.
@@ -13605,7 +14782,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test that a struct passed in as argument can be returned.
 // Especially for ffi callbacks.
 // Struct is passed on stack on all ABIs.
@@ -13671,7 +14848,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test that a struct passed in as argument can be returned.
 // Especially for ffi callbacks.
 // Struct is passed in float registers in most ABIs.
@@ -13715,7 +14892,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // On arm64, both argument and return value are passed in by pointer.
 DART_EXPORT intptr_t TestReturnStructArgumentStruct20BytesHomogeneousInt32(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -13771,7 +14948,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // On arm64, both argument and return value are passed in by pointer.
 // Ints exhaust registers, so that pointer is passed on stack.
 DART_EXPORT intptr_t TestReturnStructArgumentInt32x8Struct20BytesHomogeneou(
@@ -13853,7 +15030,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test returning struct with inline array.
 DART_EXPORT intptr_t TestReturnStructArgumentStruct8BytesInlineArrayInt(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -13916,7 +15093,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Return value in FPU registers on arm hardfp and arm64.
 DART_EXPORT intptr_t TestReturnStructArgumentStructStruct16BytesHomogeneous(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -13972,7 +15149,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Return value in FPU registers on arm64.
 DART_EXPORT intptr_t TestReturnStructArgumentStructStruct32BytesHomogeneous(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -14028,7 +15205,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // On x64 Linux, return value is split over FP and int registers.
 DART_EXPORT intptr_t TestReturnStructArgumentStructStruct16BytesMixed3(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -14099,7 +15276,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test alignment and padding of 16 byte int within struct.
 DART_EXPORT intptr_t TestReturnStructAlignmentInt16(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -14149,7 +15326,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test alignment and padding of 32 byte int within struct.
 DART_EXPORT intptr_t TestReturnStructAlignmentInt32(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -14199,7 +15376,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test alignment and padding of 64 byte int within struct.
 DART_EXPORT intptr_t TestReturnStructAlignmentInt64(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -14249,7 +15426,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Simple nested struct.
 DART_EXPORT intptr_t TestReturnStruct8BytesNestedInt(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -14303,7 +15480,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Simple nested struct with floats.
 DART_EXPORT intptr_t TestReturnStruct8BytesNestedFloat(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -14346,7 +15523,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // The nesting is irregular, testing homogenous float rules on arm and arm64,
 // and the fpu register usage on x64.
 DART_EXPORT intptr_t TestReturnStruct8BytesNestedFloat2(
@@ -14390,7 +15567,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Simple nested struct with mixed members.
 DART_EXPORT intptr_t TestReturnStruct8BytesNestedMixed(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -14439,7 +15616,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Deeper nested struct to test recursive member access.
 DART_EXPORT intptr_t TestReturnStruct16BytesNestedInt(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -14512,7 +15689,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Even deeper nested struct to test recursive member access.
 DART_EXPORT intptr_t TestReturnStruct32BytesNestedInt(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -14626,7 +15803,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test alignment and padding of nested struct with 16 byte int.
 DART_EXPORT intptr_t TestReturnStructNestedIntStructAlignmentInt16(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -14691,7 +15868,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test alignment and padding of nested struct with 32 byte int.
 DART_EXPORT intptr_t TestReturnStructNestedIntStructAlignmentInt32(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -14756,7 +15933,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Test alignment and padding of nested struct with 64 byte int.
 DART_EXPORT intptr_t TestReturnStructNestedIntStructAlignmentInt64(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -14821,7 +15998,7 @@
   return 0;
 }
 
-// Used for testing structs by value.
+// Used for testing structs and unions by value.
 // Return big irregular struct as smoke test.
 DART_EXPORT intptr_t TestReturnStructNestedIrregularEvenBigger(
     // NOLINTNEXTLINE(whitespace/parens)
diff --git a/runtime/docs/dwarf_stack_traces.md b/runtime/docs/dwarf_stack_traces.md
index afddbc7..e1f6c5e 100644
--- a/runtime/docs/dwarf_stack_traces.md
+++ b/runtime/docs/dwarf_stack_traces.md
@@ -46,7 +46,7 @@
 `--dwarf-stack-traces` in a 64-bit Linux development environment:
 
 ```bash
-$ python tools/build.py -a x64 -m release runtime_kernel runtime_precompiled
+$ python3 tools/build.py -a x64 -m release runtime_kernel runtime_precompiled
 
 $ pkg/vm/tool/gen_kernel --platform out/ReleaseX64/vm_platform_strong.dill -o throws.dill throws.dart
 
diff --git a/runtime/observatory/update_sources.py b/runtime/observatory/update_sources.py
index 39f6ad3..a9eaef7 100755
--- a/runtime/observatory/update_sources.py
+++ b/runtime/observatory/update_sources.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
diff --git a/runtime/observatory_2/update_sources.py b/runtime/observatory_2/update_sources.py
index 39f6ad3..a9eaef7 100755
--- a/runtime/observatory_2/update_sources.py
+++ b/runtime/observatory_2/update_sources.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
diff --git a/runtime/tests/vm/dart/regress_45631_test.dart b/runtime/tests/vm/dart/regress_45631_test.dart
new file mode 100644
index 0000000..b6166cd
--- /dev/null
+++ b/runtime/tests/vm/dart/regress_45631_test.dart
@@ -0,0 +1,21 @@
+// 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.
+
+// Regression test for https://github.com/dart-lang/sdk/issues/45631.
+// Verifies that compiler doesn't reuse the same Slot for captured local
+// variables with the same name and offset.
+
+void main() {
+  for (int loc0 in [1]) {
+    () {
+      ~loc0;
+    }.call();
+  }
+  {
+    String loc0 = 'hi';
+    () {
+      loc0 = 'bye';
+    }.call();
+  }
+}
diff --git a/runtime/tests/vm/dart/regress_45691_test.dart b/runtime/tests/vm/dart/regress_45691_test.dart
new file mode 100644
index 0000000..f1c3d18
--- /dev/null
+++ b/runtime/tests/vm/dart/regress_45691_test.dart
@@ -0,0 +1,29 @@
+// 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.
+
+// Regression test for https://github.com/dart-lang/sdk/issues/45691.
+// Verifies that materialization of objects with uninitialized late fields
+// doesn't crash.
+
+// VMOptions=--optimization-counter-threshold=100 --deterministic
+
+import 'package:expect/expect.dart';
+
+class A {
+  late int x = int.parse('42');
+  int y = 10;
+}
+
+@pragma("vm:never-inline")
+void foo(num deopt) {
+  A a = A();
+  deopt + 1;
+  Expect.equals(10, a.y);
+}
+
+void main() {
+  for (int i = 0; i < 150; ++i) {
+    foo(i > 100 ? 1.0 : 2);
+  }
+}
diff --git a/runtime/tests/vm/dart_2/regress_45631_test.dart b/runtime/tests/vm/dart_2/regress_45631_test.dart
new file mode 100644
index 0000000..b6166cd
--- /dev/null
+++ b/runtime/tests/vm/dart_2/regress_45631_test.dart
@@ -0,0 +1,21 @@
+// 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.
+
+// Regression test for https://github.com/dart-lang/sdk/issues/45631.
+// Verifies that compiler doesn't reuse the same Slot for captured local
+// variables with the same name and offset.
+
+void main() {
+  for (int loc0 in [1]) {
+    () {
+      ~loc0;
+    }.call();
+  }
+  {
+    String loc0 = 'hi';
+    () {
+      loc0 = 'bye';
+    }.call();
+  }
+}
diff --git a/runtime/third_party/binary_size/src/explain_binary_size_delta.py b/runtime/third_party/binary_size/src/explain_binary_size_delta.py
index b6a0270..ed9c689 100755
--- a/runtime/third_party/binary_size/src/explain_binary_size_delta.py
+++ b/runtime/third_party/binary_size/src/explain_binary_size_delta.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright 2014 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
@@ -362,7 +362,7 @@
     for section in sections:
         allFiles = allFiles | section.sources
     allFiles = allFiles | maybe_unchanged_sources
-    print 'Source stats:'
+    print('Source stats:')
     print('  %d sources encountered.' % len(allFiles))
     print('  %d completely new.' % len(new_sources))
     print('  %d removed completely.' % len(removed_sources))
@@ -374,7 +374,7 @@
 
     if not showsources:
         return  # Per-source analysis, only if requested
-    print 'Per-source Analysis:'
+    print('Per-source Analysis:')
     delta_by_path = {}
     for section in sections:
         for path in section.symbols_by_path:
@@ -409,10 +409,10 @@
         header = ' %s - Source: %s - (gained %d, lost %d)' % (DeltaStr(delta),
                                                               path, gain, loss)
         divider = '-' * len(header)
-        print ''
-        print divider
-        print header
-        print divider
+        print('')
+        print(divider)
+        print(header)
+        print(divider)
         if showsymbols:
 
             def ExtractNewSize(tup):
@@ -424,7 +424,7 @@
                 return symbol_delta.old_size
 
             if path in new_symbols.symbols_by_path:
-                print '  New symbols:'
+                print('  New symbols:')
                 for symbol_name, symbol_type, symbol_delta in \
                     sorted(new_symbols.symbols_by_path[path],
                            key=ExtractNewSize,
@@ -434,7 +434,7 @@
                            symbol_type, symbol_delta.new_size,
                            SharedInfoStr(symbol_delta)))
             if path in removed_symbols.symbols_by_path:
-                print '  Removed symbols:'
+                print('  Removed symbols:')
                 for symbol_name, symbol_type, symbol_delta in \
                     sorted(removed_symbols.symbols_by_path[path],
                            key=ExtractOldSize):
@@ -446,7 +446,7 @@
                  type_str) in [(grown_symbols.symbols_by_path, "Grown"),
                                (shrunk_symbols.symbols_by_path, "Shrunk")]:
                 if path in changed_symbols_by_path:
-                    print '  %s symbols:' % type_str
+                    print('  %s symbols:' % type_str)
 
                     def changed_symbol_sortkey(item):
                         symbol_name, _symbol_type, symbol_delta = item
@@ -505,9 +505,9 @@
         parser.error('--nm2 is required')
     symbols = []
     for path in [opts.nm1, opts.nm2]:
-        with file(path, 'r') as nm_input:
+        with open(path, 'r') as nm_input:
             if opts.verbose:
-                print 'parsing ' + path + '...'
+                print('parsing ' + path + '...')
             symbols.append(list(binary_size_utils.ParseNm(nm_input)))
     (added, removed, changed, unchanged) = Compare(symbols[0], symbols[1])
     CrunchStats(added, removed, changed, unchanged,
diff --git a/runtime/third_party/binary_size/src/run_binary_size_analysis.py b/runtime/third_party/binary_size/src/run_binary_size_analysis.py
index d78e02d..ac41f4a 100755
--- a/runtime/third_party/binary_size/src/run_binary_size_analysis.py
+++ b/runtime/third_party/binary_size/src/run_binary_size_analysis.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright 2014 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
@@ -9,7 +9,6 @@
 you desire.
 """
 
-import collections
 import json
 import logging
 import multiprocessing
@@ -336,7 +335,7 @@
         user_interrupted = True
         print('Patience you must have my young padawan.')
 
-    print ''
+    print('')
 
     if user_interrupted:
         print('Skipping the rest of the file mapping. '
@@ -379,9 +378,9 @@
 
     if nm_process.returncode != 0:
         if err_output:
-            raise Exception, err_output
+            raise Exception(err_output)
         else:
-            raise Exception, process_output
+            raise Exception(process_output)
 
     return process_output
 
@@ -393,15 +392,15 @@
             outfile = tempfile.NamedTemporaryFile(delete=False).name
 
         if verbose:
-            print 'Running parallel addr2line, dumping symbols to ' + outfile
+            print('Running parallel addr2line, dumping symbols to ' + outfile)
         RunElfSymbolizer(outfile, library, addr2line_binary, nm_binary, jobs,
                          disambiguate, src_path)
 
         nm_infile = outfile
 
     elif verbose:
-        print 'Using nm input from ' + nm_infile
-    with file(nm_infile, 'r') as infile:
+        print('Using nm input from ' + nm_infile)
+    with open(nm_infile, 'r') as infile:
         return list(binary_size_utils.ParseNm(infile))
 
 
@@ -624,12 +623,12 @@
         (not opts.nm_in)) or (opts.library and opts.nm_in):
         parser.error('exactly one of --library or --nm-in is required')
     if opts.nm_out:
-        print >> sys.stderr, (
-            'WARNING: --nm-out is deprecated and has no effect.')
+        print('WARNING: --nm-out is deprecated and has no effect.',
+              file=sys.stderr)
     if (opts.nm_in):
         if opts.jobs:
-            print >> sys.stderr, ('WARNING: --jobs has no effect '
-                                  'when used with --nm-in')
+            print('WARNING: --jobs has no effect when used with --nm-in',
+                  file=sys.stderr)
     if not opts.destdir:
         parser.error('--destdir is a required argument')
     if not opts.jobs:
@@ -666,7 +665,7 @@
 
     # Prepare output directory and report guts
     if not os.path.exists(opts.destdir):
-        os.makedirs(opts.destdir, 0755)
+        os.makedirs(opts.destdir, 0o755)
     nm_out = os.path.join(opts.destdir, 'nm.out')
     if opts.no_nm_out:
         nm_out = None
@@ -677,7 +676,7 @@
     data_js_file_name = os.path.join(opts.destdir, 'data.js')
     d3_out = os.path.join(opts.destdir, 'd3')
     if not os.path.exists(d3_out):
-        os.makedirs(d3_out, 0755)
+        os.makedirs(d3_out, 0o755)
     d3_src = os.path.join(os.path.dirname(__file__), '..', '..', 'd3', 'src')
     template_src = os.path.join(os.path.dirname(__file__), 'template')
     shutil.copy(os.path.join(d3_src, 'LICENSE'), d3_out)
@@ -701,7 +700,7 @@
         symbol_path_origin_dir = os.path.abspath(os.getcwd())
     # Dump JSON for the HTML report.
     DumpCompactTree(symbols, symbol_path_origin_dir, data_js_file_name)
-    print 'Report saved to ' + opts.destdir + '/index.html'
+    print('Report saved to ' + opts.destdir + '/index.html')
 
 
 if __name__ == '__main__':
diff --git a/runtime/tools/android_finder.py b/runtime/tools/android_finder.py
index c3d8249..6c8078c 100755
--- a/runtime/tools/android_finder.py
+++ b/runtime/tools/android_finder.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 # Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
diff --git a/runtime/tools/benchmark.py b/runtime/tools/benchmark.py
index ec0b5e6..0f6cd72 100755
--- a/runtime/tools/benchmark.py
+++ b/runtime/tools/benchmark.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
@@ -36,7 +36,8 @@
 def ReadBenchmarkList(mode, path, core):
     filename = GetBenchmarkFile([path])
     benchmarks = dict()
-    execfile(filename, benchmarks)
+    with open(filename) as infile:
+        exec(infile.read(), benchmarks)
     if (mode == "release") and not core:
         return benchmarks['SUPPORTED_BENCHMARKS']
     else:
@@ -91,11 +92,11 @@
     options.arch = options.arch.split(',')
     for mode in options.mode:
         if not mode in ['debug', 'release']:
-            print "Unknown mode %s" % mode
+            print("Unknown mode %s" % mode)
             return False
     for arch in options.arch:
         if not arch in ['ia32', 'x64', 'simarm', 'arm', 'dartc']:
-            print "Unknown arch %s" % arch
+            print("Unknown arch %s" % arch)
             return False
     return True
 
@@ -138,7 +139,7 @@
                     GetBenchmarkFile([benchmark, 'dart', benchmark + '.dart']),
                 ]
                 if options.verbose:
-                    print ' '.join(command)
+                    print(' '.join(command))
                 subprocess.call(command)
     return 0
 
diff --git a/runtime/tools/bin_to_assembly.py b/runtime/tools/bin_to_assembly.py
index 9ec13e7..b66c285 100755
--- a/runtime/tools/bin_to_assembly.py
+++ b/runtime/tools/bin_to_assembly.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (c) 2017, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
diff --git a/runtime/tools/bin_to_coff.py b/runtime/tools/bin_to_coff.py
index 1925bcd..f98f051f 100644
--- a/runtime/tools/bin_to_coff.py
+++ b/runtime/tools/bin_to_coff.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
@@ -54,8 +54,8 @@
 # } SCNHDR;
 SECTION_HEADER_FORMAT = '8sIIIIIIHHI'
 SECTION_HEADER_SIZE = calcsize(SECTION_HEADER_FORMAT)
-SECTION_NAME_RODATA = '.rodata'
-SECTION_NAME_TEXT = '.text'
+SECTION_NAME_RODATA = b'.rodata'
+SECTION_NAME_TEXT = b'.text'
 SECTION_PADDR = 0x0
 SECTION_VADDR = 0x0
 SECTION_RAW_DATA_PTR = (
@@ -130,13 +130,13 @@
     includes_size_name = (args.size_name != None)
 
     # Symbols on x86 are prefixed with '_'
-    symbol_prefix = '' if args.use_64_bit else '_'
+    symbol_prefix = b'' if args.use_64_bit else b'_'
     num_symbols = 2 if includes_size_name else 1
-    symbol_name = symbol_prefix + args.symbol_name
+    symbol_name = symbol_prefix + args.symbol_name.encode()
     size_symbol_name = None
     if (includes_size_name):
         size_symbol = args.size_name if args.size_name else args.symbol_name + "Size"
-        size_symbol_name = symbol_prefix + size_symbol
+        size_symbol_name = symbol_prefix + size_symbol.encode()
 
     size_symbol_format = SIZE_SYMBOL_FORMAT_X64 if args.use_64_bit else SIZE_FORMAT
     size_symbol_size = SIZE_SYMBOL_LENGTH_X64 if args.use_64_bit else SIZE_LENGTH
@@ -237,14 +237,14 @@
         symbol_len = len(symbol_name)
         buff[offset:offset + symbol_len] = symbol_name
         offset += symbol_len
-        buff[offset] = '\0'
+        buff[offset] = b'\0'
         offset += 1
 
     if includes_size_name and long_size_symbol_name:
         symbol_len = len(size_symbol_name)
         buff[offset:offset + symbol_len] = size_symbol_name
         offset += symbol_len
-        buff[offset] = '\0'
+        buff[offset] = b'\0'
         offset += 1
 
     with open(args.output, 'wb') as f:
diff --git a/runtime/tools/compiler_layering_check.py b/runtime/tools/compiler_layering_check.py
index e686ce9..06d73d9 100755
--- a/runtime/tools/compiler_layering_check.py
+++ b/runtime/tools/compiler_layering_check.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (c) 2019, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
@@ -127,6 +127,6 @@
 
 if __name__ == '__main__':
     errors = DoCheck('.')
-    print '\n'.join(errors)
+    print('\n'.join(errors))
     if errors:
         sys.exit(-1)
diff --git a/runtime/tools/create_archive.py b/runtime/tools/create_archive.py
index 7bc93d5..40d6813 100755
--- a/runtime/tools/create_archive.py
+++ b/runtime/tools/create_archive.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2015, 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.
diff --git a/runtime/tools/create_snapshot_bin.py b/runtime/tools/create_snapshot_bin.py
index 93c5b69..65c35a8 100755
--- a/runtime/tools/create_snapshot_bin.py
+++ b/runtime/tools/create_snapshot_bin.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
diff --git a/runtime/tools/create_snapshot_file.py b/runtime/tools/create_snapshot_file.py
index 4216366..2354acb 100755
--- a/runtime/tools/create_snapshot_file.py
+++ b/runtime/tools/create_snapshot_file.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
diff --git a/runtime/tools/create_string_literal.py b/runtime/tools/create_string_literal.py
index 68a969b..3065e7d 100755
--- a/runtime/tools/create_string_literal.py
+++ b/runtime/tools/create_string_literal.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
diff --git a/runtime/tools/embedder_layering_check.py b/runtime/tools/embedder_layering_check.py
index 8e33bf0..775040e 100644
--- a/runtime/tools/embedder_layering_check.py
+++ b/runtime/tools/embedder_layering_check.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (c) 2019, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
@@ -75,6 +75,6 @@
 
 if __name__ == '__main__':
     errors = DoCheck('.')
-    print '\n'.join(errors)
+    print('\n'.join(errors))
     if errors:
         sys.exit(-1)
diff --git a/runtime/tools/gen_library_src_paths.py b/runtime/tools/gen_library_src_paths.py
index 97f4ae1..79ef9d8 100755
--- a/runtime/tools/gen_library_src_paths.py
+++ b/runtime/tools/gen_library_src_paths.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
diff --git a/runtime/tools/utils.py b/runtime/tools/utils.py
index 8cec062..a70681d 100644
--- a/runtime/tools/utils.py
+++ b/runtime/tools/utils.py
@@ -5,17 +5,17 @@
 # This file contains a set of utilities functions used by other Python-based
 # scripts.
 
-import commands
 import os
 import platform
-import Queue
+import queue
 import re
-import StringIO
 import subprocess
 import sys
 import threading
 import time
 
+from io import StringIO
+from subprocess import getoutput
 
 # Try to guess the host operating system.
 def GuessOS():
@@ -55,11 +55,11 @@
 def GuessCpus():
     if os.path.exists("/proc/cpuinfo"):
         return int(
-            commands.getoutput(
+            getoutput(
                 "GREP_OPTIONS= grep -E '^processor' /proc/cpuinfo | wc -l"))
     if os.path.exists("/usr/bin/hostinfo"):
         return int(
-            commands.getoutput(
+            getoutput(
                 '/usr/bin/hostinfo | GREP_OPTIONS= grep "processors are logically available." | awk "{ print \$1 }"'
             ))
     win_cpu_count = os.getenv("NUMBER_OF_PROCESSORS")
@@ -197,14 +197,14 @@
             stdout=subprocess.PIPE,
             stderr=subprocess.PIPE)
     except OSError as e:
-        if not isinstance(command, basestring):
+        if not isinstance(command, str):
             command = ' '.join(command)
         if printErrorInfo:
             sys.stderr.write("Command failed: '%s'\n" % command)
         raise Error(e)
 
     def StartThread(out):
-        queue = Queue.Queue()
+        queue = queue.Queue()
 
         def EnqueueOutput(out, queue):
             for line in iter(out.readline, b''):
@@ -226,7 +226,7 @@
                 out.write(line)
                 if out2 != None:
                     out2.write(line)
-        except Queue.Empty:
+        except queue.Empty:
             pass
 
     outBuf = StringIO.StringIO()
@@ -252,7 +252,7 @@
     out = outBuf.getvalue()
     error = errorBuf.getvalue()
     if returncode:
-        if not isinstance(command, basestring):
+        if not isinstance(command, str):
             command = ' '.join(command)
         if printErrorInfo:
             sys.stderr.write("Command failed: '%s'\n" % command)
@@ -266,10 +266,10 @@
 
 
 def Main(argv):
-    print "GuessOS() -> ", GuessOS()
-    print "GuessArchitecture() -> ", GuessArchitecture()
-    print "GuessCpus() -> ", GuessCpus()
-    print "IsWindows() -> ", IsWindows()
+    print("GuessOS() -> ", GuessOS())
+    print("GuessArchitecture() -> ", GuessArchitecture())
+    print("GuessCpus() -> ", GuessCpus())
+    print("IsWindows() -> ", IsWindows())
 
 
 class Error(Exception):
diff --git a/runtime/tools/valgrind.py b/runtime/tools/valgrind.py
index f82d6d3..ec0c886 100755
--- a/runtime/tools/valgrind.py
+++ b/runtime/tools/valgrind.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 #
 # Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
diff --git a/runtime/vm/compiler/backend/slot.cc b/runtime/vm/compiler/backend/slot.cc
index eaf30b0..3cb6371 100644
--- a/runtime/vm/compiler/backend/slot.cc
+++ b/runtime/vm/compiler/backend/slot.cc
@@ -392,8 +392,10 @@
       return true;
 
     case Kind::kCapturedVariable:
-      return (flags_ == other.flags_) && (DataAs<const String>()->ptr() ==
-                                          other.DataAs<const String>()->ptr());
+      return (flags_ == other.flags_) &&
+             (DataAs<const String>()->ptr() ==
+              other.DataAs<const String>()->ptr()) &&
+             static_type_->Equals(*(other.static_type_));
 
     case Kind::kDartField:
       return other.DataAs<const Field>()->Original() ==
diff --git a/runtime/vm/compiler/ffi/native_type.h b/runtime/vm/compiler/ffi/native_type.h
index de69bd5..2ee284a 100644
--- a/runtime/vm/compiler/ffi/native_type.h
+++ b/runtime/vm/compiler/ffi/native_type.h
@@ -33,7 +33,7 @@
 class NativeCompoundType;
 
 // NativeTypes are the types used in calling convention specifications:
-// integers, floats, and composites.
+// integers, floats, and compounds.
 //
 // NativeTypes exclude C types which are not discussed in calling conventions:
 // pointer types (they are lowered to integers).
diff --git a/runtime/vm/constants_arm.h b/runtime/vm/constants_arm.h
index 1b7fe4c..be35d85 100644
--- a/runtime/vm/constants_arm.h
+++ b/runtime/vm/constants_arm.h
@@ -547,7 +547,7 @@
   static constexpr AlignmentStrategy kArgumentStackAlignment =
       kAlignedToWordSizeBut8AlignedTo8;
 
-  // How fields in composites are aligned.
+  // How fields in compounds are aligned.
 #if defined(TARGET_OS_MACOS_IOS)
   static constexpr AlignmentStrategy kFieldAlignment =
       kAlignedToValueSizeBut8AlignedTo4;
diff --git a/runtime/vm/constants_arm64.h b/runtime/vm/constants_arm64.h
index a596a10..cf35be9 100644
--- a/runtime/vm/constants_arm64.h
+++ b/runtime/vm/constants_arm64.h
@@ -421,7 +421,7 @@
       kAlignedToWordSize;
 #endif
 
-  // How fields in composites are aligned.
+  // How fields in compounds are aligned.
   static constexpr AlignmentStrategy kFieldAlignment = kAlignedToValueSize;
 
   // Whether 1 or 2 byte-sized arguments or return values are passed extended
diff --git a/runtime/vm/constants_ia32.h b/runtime/vm/constants_ia32.h
index a8baa9d..33096bb 100644
--- a/runtime/vm/constants_ia32.h
+++ b/runtime/vm/constants_ia32.h
@@ -315,7 +315,7 @@
   static constexpr AlignmentStrategy kArgumentStackAlignment =
       kAlignedToWordSize;
 
-  // How fields in composites are aligned.
+  // How fields in compounds are aligned.
 #if defined(TARGET_OS_WINDOWS)
   static constexpr AlignmentStrategy kFieldAlignment = kAlignedToValueSize;
 #else
diff --git a/runtime/vm/constants_x64.h b/runtime/vm/constants_x64.h
index e5f647e..ac85998 100644
--- a/runtime/vm/constants_x64.h
+++ b/runtime/vm/constants_x64.h
@@ -388,7 +388,7 @@
   static constexpr AlignmentStrategy kArgumentStackAlignment =
       kAlignedToWordSize;
 
-  // How fields in composites are aligned.
+  // How fields in compounds are aligned.
   static constexpr AlignmentStrategy kFieldAlignment = kAlignedToValueSize;
 
   // Whether 1 or 2 byte-sized arguments or return values are passed extended
@@ -453,7 +453,7 @@
   static constexpr AlignmentStrategy kArgumentStackAlignment =
       kAlignedToWordSize;
 
-  // How fields in composites are aligned.
+  // How fields in compounds are aligned.
   static constexpr AlignmentStrategy kFieldAlignment = kAlignedToValueSize;
 
   // Whether 1 or 2 byte-sized arguments or return values are passed extended
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index a87928a..852f3b5 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -991,17 +991,17 @@
   obj1_ref->set_ptr(obj2_ref);
 }
 
-// TODO(https://dartbug.com/38491): Reject Unions here as well.
-static bool IsFfiStruct(Thread* T, const Object& obj) {
+static bool IsFfiCompound(Thread* T, const Object& obj) {
   if (obj.IsNull()) {
     return false;
   }
 
-  // CFE guarantees we can only have direct subclasses of `Struct`
+  // CFE guarantees we can only have direct subclasses of `Struct` and `Union`
   // (no implementations or indirect subclasses are allowed).
   const auto& klass = Class::Handle(Z, obj.clazz());
   const auto& super_klass = Class::Handle(Z, klass.SuperClass());
-  if (super_klass.Name() != Symbols::Struct().ptr()) {
+  if (super_klass.Name() != Symbols::Struct().ptr() &&
+      super_klass.Name() != Symbols::Union().ptr()) {
     return false;
   }
   const auto& library = Library::Handle(Z, super_klass.library());
@@ -1020,7 +1020,7 @@
   if (ref.IsPointer()) {
     return NULL;
   }
-  if (IsFfiStruct(thread, ref)) {
+  if (IsFfiCompound(thread, ref)) {
     return NULL;
   }
 
@@ -1054,7 +1054,7 @@
   if (ref.IsPointer()) {
     return NULL;
   }
-  if (IsFfiStruct(thread, ref)) {
+  if (IsFfiCompound(thread, ref)) {
     return NULL;
   }
 
diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc
index 6046c6e..4f333c6 100644
--- a/runtime/vm/dart_api_impl_test.cc
+++ b/runtime/vm/dart_api_impl_test.cc
@@ -3378,15 +3378,20 @@
       Dart_NewWeakPersistentHandle(obj3, nullptr, 0, FinalizableHandleCallback);
   EXPECT_EQ(ref3, static_cast<void*>(nullptr));
 
-  // Subtype of Struct object.
+  // Subtype of Struct or Union object.
   const char* kScriptChars = R"(
       import 'dart:ffi';
 
       class MyStruct extends Struct {
         external Pointer notEmpty;
       }
+
+      class MyUnion extends Union {
+        external Pointer notEmpty;
+      }
   )";
   Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
+
   Dart_Handle my_struct_type =
       Dart_GetNonNullableType(lib, NewString("MyStruct"), 0, NULL);
   Dart_Handle obj4 = Dart_Allocate(my_struct_type);
@@ -3395,7 +3400,13 @@
       Dart_NewWeakPersistentHandle(obj4, nullptr, 0, FinalizableHandleCallback);
   EXPECT_EQ(ref4, static_cast<void*>(nullptr));
 
-  // TODO(https://dartbug.com/38491): Reject Unions here as well.
+  Dart_Handle my_union_type =
+      Dart_GetNonNullableType(lib, NewString("MyUnion"), 0, NULL);
+  Dart_Handle obj5 = Dart_Allocate(my_union_type);
+  EXPECT_VALID(obj5);
+  Dart_WeakPersistentHandle ref5 =
+      Dart_NewWeakPersistentHandle(obj4, nullptr, 0, FinalizableHandleCallback);
+  EXPECT_EQ(ref5, static_cast<void*>(nullptr));
 
   Dart_ExitScope();
 }
@@ -3427,15 +3438,20 @@
       Dart_NewFinalizableHandle(obj3, nullptr, 0, FinalizableHandleCallback);
   EXPECT_EQ(ref3, static_cast<void*>(nullptr));
 
-  // Subtype of Struct object.
+  // Subtype of Struct or Union object.
   const char* kScriptChars = R"(
       import 'dart:ffi';
 
       class MyStruct extends Struct {
         external Pointer notEmpty;
       }
+
+      class MyUnion extends Union {
+        external Pointer notEmpty;
+      }
   )";
   Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
+
   Dart_Handle my_struct_type =
       Dart_GetNonNullableType(lib, NewString("MyStruct"), 0, NULL);
   Dart_Handle obj4 = Dart_Allocate(my_struct_type);
@@ -3444,6 +3460,14 @@
       Dart_NewFinalizableHandle(obj4, nullptr, 0, FinalizableHandleCallback);
   EXPECT_EQ(ref4, static_cast<void*>(nullptr));
 
+  Dart_Handle my_union_type =
+      Dart_GetNonNullableType(lib, NewString("MyUnion"), 0, NULL);
+  Dart_Handle obj5 = Dart_Allocate(my_union_type);
+  EXPECT_VALID(obj5);
+  Dart_FinalizableHandle ref5 =
+      Dart_NewFinalizableHandle(obj4, nullptr, 0, FinalizableHandleCallback);
+  EXPECT_EQ(ref5, static_cast<void*>(nullptr));
+
   Dart_ExitScope();
 }
 
diff --git a/runtime/vm/deferred_objects.cc b/runtime/vm/deferred_objects.cc
index 27edd7a..8fa9865 100644
--- a/runtime/vm/deferred_objects.cc
+++ b/runtime/vm/deferred_objects.cc
@@ -440,7 +440,9 @@
           offset ^= GetFieldOffset(i);
           field ^= offset_map.At(offset.Value() / kWordSize);
           value = GetValue(i);
-          if (!field.IsNull()) {
+          ASSERT((value.ptr() != Object::sentinel().ptr()) ||
+                 (!field.IsNull() && field.is_late()));
+          if (!field.IsNull() && (value.ptr() != Object::sentinel().ptr())) {
             obj.SetField(field, value);
             if (FLAG_trace_deoptimization_verbose) {
               OS::PrintErr("    %s <- %s\n",
@@ -455,8 +457,11 @@
             ASSERT(offset.Value() < cls.host_instance_size());
             obj.SetFieldAtOffset(offset.Value(), value);
             if (FLAG_trace_deoptimization_verbose) {
-              OS::PrintErr("    null Field @ offset(%" Pd ") <- %s\n",
-                           offset.Value(), value.ToCString());
+              OS::PrintErr(
+                  "    %s @ offset(%" Pd ") <- %s\n",
+                  (field.IsNull() ? "null Field"
+                                  : String::Handle(field.name()).ToCString()),
+                  offset.Value(), value.ToCString());
             }
           }
         }
diff --git a/runtime/vm/kernel_loader.cc b/runtime/vm/kernel_loader.cc
index 26329ce..45a4edd 100644
--- a/runtime/vm/kernel_loader.cc
+++ b/runtime/vm/kernel_loader.cc
@@ -1662,7 +1662,7 @@
     // optimized. We immediately set the guarded_cid_ to kDynamicCid, which
     // is effectively the same as calling this method first with Pointer and
     // subsequently with TypedData with field guards.
-    if (klass.Name() == Symbols::Compound().ptr() &&
+    if (klass.UserVisibleName() == Symbols::Compound().ptr() &&
         Library::Handle(Z, klass.library()).url() == Symbols::DartFfi().ptr()) {
       ASSERT(fields_.length() == 1);
       ASSERT(String::Handle(Z, fields_[0]->name())
diff --git a/samples-dev/swarm/appengine/dev.html b/samples-dev/swarm/appengine/dev.html
index 2204ec9..672ec9c8 100644
--- a/samples-dev/swarm/appengine/dev.html
+++ b/samples-dev/swarm/appengine/dev.html
@@ -18,7 +18,7 @@
     for UI development using something like: <pre>file:///Users/jimhug/dart-all/dart/samples/swarm/swarm.html</pre>.</p>
 
   <p>When you are ready to test your new UI on this live server, first you
-    need to run <pre>python update.py</pre> from your
+    need to run <pre>python3 update.py</pre> from your
     <pre>dart/samples/swarm</pre> directory.  This will build
     both a self-contained html file for both js and dart code.  Then,
     use the link below to upload your files to this server.  If you are
diff --git a/samples-dev/swarm/appengine/main.py b/samples-dev/swarm/appengine/main.py
index f047c47..4216fe8 100644
--- a/samples-dev/swarm/appengine/main.py
+++ b/samples-dev/swarm/appengine/main.py
@@ -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.
 
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 import re, base64, logging, pickle, httplib2, time, urlparse, urllib2, urllib, StringIO, gzip, zipfile
 
diff --git a/samples-dev/swarm/buildapp.py b/samples-dev/swarm/buildapp.py
index 12d2a6a..b8198e1 100755
--- a/samples-dev/swarm/buildapp.py
+++ b/samples-dev/swarm/buildapp.py
@@ -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.
 
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 
 # This script builds a Chrome App file (.crx) for Swarm
@@ -65,14 +65,14 @@
     dartiumResult = createChromeApp(buildRoot, 'build_dart_app', 'swarm.crx')
     dartCResult = createChromeApp(buildRoot, 'build_js_app', 'swarm-js.crx')
 
-    print '''
+    print('''
 Successfully created Chrome apps!
   Dartium:  file://%s
 
   DartC/JS: file://%s
 
 To install, open this URL in Chrome and select Continue at the bottom.
-''' % (dartiumResult, dartCResult)
+''' % (dartiumResult, dartCResult))
     return 0
 
 
diff --git a/samples-dev/swarm/cacheimages.py b/samples-dev/swarm/cacheimages.py
index 0a8d1d3..2324125 100755
--- a/samples-dev/swarm/cacheimages.py
+++ b/samples-dev/swarm/cacheimages.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
@@ -28,9 +28,9 @@
             infile,
             verbose=options.verbose,
             encode_images=options.inline_images)
-        print 'Converted ' + infile
-    except BaseException, e:
-        print 'Caught error: %s' % e
+        print('Converted ' + infile)
+    except BaseException as e:
+        print('Caught error: %s' % e)
 
 
 def Flags():
@@ -53,13 +53,13 @@
     global options
     parser = Flags()
     options, args = parser.parse_args()
-    print "args: %s" % args
+    print("args: %s" % args)
     if len(args) < 1 or 'help' in args[0]:
-        print 'Usage: %s DIRECTORY' % basename(sys.argv[0])
+        print('Usage: %s DIRECTORY' % basename(sys.argv[0]))
         return 1
 
     dirname = args[0]
-    print 'Searching directory ' + dirname
+    print('Searching directory ' + dirname)
 
     files = []
     for root, dirs, fnames in os.walk(dirname):
diff --git a/samples-dev/swarm/gen_manifest.py b/samples-dev/swarm/gen_manifest.py
index 4cc1280..d75356d 100755
--- a/samples-dev/swarm/gen_manifest.py
+++ b/samples-dev/swarm/gen_manifest.py
@@ -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.
 
-#!/usr/bin/python2.6
+#!/usr/bin/env python3
 #
 """
 Usage: gen_manifest.py DIRECTORY EXTENSIONS CACHE-FILE HTML-FILES...
@@ -30,7 +30,7 @@
 htmlFiles = sys.argv[4:]
 
 os.chdir(cacheDir)
-print "Generating manifest from root path: " + cacheDir
+print("Generating manifest from root path: " + cacheDir)
 
 patterns = extensions + htmlFiles
 
@@ -68,7 +68,7 @@
 with open(manifestName, 'w') as f:
     f.writelines(m + '\n' for m in manifest)
 
-print "Created manifest file: " + manifestName
+print("Created manifest file: " + manifestName)
 
 for htmlFile in htmlFiles:
     cachedHtmlFile = htmlFile.replace('.html', '-cache.html')
@@ -76,6 +76,6 @@
     text = text.replace('<html>', '<html manifest="%s">' % manifestName, 1)
     with open(cachedHtmlFile, 'w') as output:
         output.write(text)
-    print "Processed html file: %s -> %s" % (htmlFile, cachedHtmlFile)
+    print("Processed html file: %s -> %s" % (htmlFile, cachedHtmlFile))
 
-print "Successfully generated manifest and html files"
+print("Successfully generated manifest and html files")
diff --git a/samples-dev/swarm/update.py b/samples-dev/swarm/update.py
index 33526b0..5ff7000 100755
--- a/samples-dev/swarm/update.py
+++ b/samples-dev/swarm/update.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
@@ -24,7 +24,7 @@
 def convertOne(infile, options):
     outDirBase = 'outcode'
     outfile = join(outDirBase, infile)
-    print 'converting %s to %s' % (infile, outfile)
+    print('converting %s to %s' % (infile, outfile))
 
     if 'dart' in options.target:
         htmlconverter.convertForDartium(infile, outDirBase,
diff --git a/sdk/lib/_http/embedder_config.dart b/sdk/lib/_http/embedder_config.dart
new file mode 100644
index 0000000..730bc2dd
--- /dev/null
+++ b/sdk/lib/_http/embedder_config.dart
@@ -0,0 +1,11 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+part of dart._http;
+
+/// Embedder-specific `dart:_http` configuration.
+
+/// [HttpClient] will disallow HTTP URLs if this value is set to `false`.
+@pragma("vm:entry-point")
+bool _embedderAllowsHttp = true;
diff --git a/sdk/lib/_http/http.dart b/sdk/lib/_http/http.dart
index c4344d6..c20759f 100644
--- a/sdk/lib/_http/http.dart
+++ b/sdk/lib/_http/http.dart
@@ -24,6 +24,7 @@
 import 'dart:typed_data';
 
 part 'crypto.dart';
+part 'embedder_config.dart';
 part 'http_date.dart';
 part 'http_headers.dart';
 part 'http_impl.dart';
diff --git a/sdk/lib/_http/http_impl.dart b/sdk/lib/_http/http_impl.dart
index 1f36df2..bd16cdc 100644
--- a/sdk/lib/_http/http_impl.dart
+++ b/sdk/lib/_http/http_impl.dart
@@ -2620,6 +2620,40 @@
 
   set findProxy(String f(Uri uri)?) => _findProxy = f;
 
+  static void _startRequestTimelineEvent(
+      TimelineTask? timeline, String method, Uri uri) {
+    timeline?.start('HTTP CLIENT ${method.toUpperCase()}', arguments: {
+      'method': method.toUpperCase(),
+      'uri': uri.toString(),
+    });
+  }
+
+  /// Whether HTTP requests are currently allowed.
+  ///
+  /// If the [Zone] variable `#dart.library.io.allow_http` is set to a boolean,
+  /// it determines whether the HTTP protocol is allowed. If the zone variable
+  /// is set to any other non-null value, HTTP is not allowed.
+  /// Otherwise, if the `dart.library.io.allow_http` environment flag
+  /// is set to `false`, HTTP is not allowed.
+  /// Otherwise, [_embedderAllowsHttp] determines the result.
+  bool get _isHttpAllowed {
+    final zoneOverride = Zone.current[#dart.library.io.allow_http];
+    if (zoneOverride != null) return true == zoneOverride;
+    bool envOverride =
+        bool.fromEnvironment("dart.library.io.allow_http", defaultValue: true);
+    return envOverride && _embedderAllowsHttp;
+  }
+
+  bool _isLoopback(String host) {
+    if (host.isEmpty) return false;
+    if ("localhost" == host) return true;
+    try {
+      return InternetAddress(host).isLoopback;
+    } on ArgumentError {
+      return false;
+    }
+  }
+
   Future<_HttpClientRequest> _openUrl(String method, Uri uri) {
     if (_closing) {
       throw new StateError("Client is closed");
@@ -2641,9 +2675,11 @@
     }
 
     bool isSecure = uri.isScheme("https");
-    if (!isSecure && !isInsecureConnectionAllowed(uri.host)) {
-      throw new StateError("Insecure HTTP is not allowed by platform: $uri");
+    if (!_isHttpAllowed && !isSecure && !_isLoopback(uri.host)) {
+      throw new StateError(
+          "Insecure HTTP is not allowed by the current platform: $uri");
     }
+
     int port = uri.port;
     if (port == 0) {
       port =
diff --git a/sdk/lib/_internal/js_runtime/lib/foreign_helper.dart b/sdk/lib/_internal/js_runtime/lib/foreign_helper.dart
index 3c9aed9..6e8cd72 100644
--- a/sdk/lib/_internal/js_runtime/lib/foreign_helper.dart
+++ b/sdk/lib/_internal/js_runtime/lib/foreign_helper.dart
@@ -194,9 +194,6 @@
 /// https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi.
 external RAW_DART_FUNCTION_REF(Function function);
 
-/// Sets the current static state to [staticState].
-external void JS_SET_STATIC_STATE(staticState);
-
 /// Returns the interceptor for class [type].  The interceptor is the type's
 /// constructor's `prototype` property.  [type] will typically be the class, not
 /// an interface, e.g. `JS_INTERCEPTOR_CONSTANT(JSInt)`, not
@@ -213,9 +210,6 @@
 /// Calls are replaced with a [HLoadType] SSA instruction.
 external Object getJSArrayInteropRti();
 
-/// Returns the object corresponding to Namer.staticStateHolder.
-external JS_GET_STATIC_STATE();
-
 /// Returns the JS name for [name] from the Namer.
 external String JS_GET_NAME(JsGetName name);
 
diff --git a/sdk/lib/_internal/vm/lib/core_patch.dart b/sdk/lib/_internal/vm/lib/core_patch.dart
index d79e07d..b6587ef 100644
--- a/sdk/lib/_internal/vm/lib/core_patch.dart
+++ b/sdk/lib/_internal/vm/lib/core_patch.dart
@@ -49,7 +49,7 @@
 
 import "dart:convert" show ascii, Encoding, json, latin1, utf8;
 
-import "dart:ffi" show Pointer, Struct;
+import "dart:ffi" show Pointer, Struct, Union;
 
 import "dart:isolate" show Isolate;
 
diff --git a/sdk/lib/_internal/vm/lib/expando_patch.dart b/sdk/lib/_internal/vm/lib/expando_patch.dart
index 868e6238e..cacbc02 100644
--- a/sdk/lib/_internal/vm/lib/expando_patch.dart
+++ b/sdk/lib/_internal/vm/lib/expando_patch.dart
@@ -142,10 +142,10 @@
         (object is num) ||
         (object is String) ||
         (object is Pointer) ||
-        (object is Struct)) {
-      // TODO(https://dartbug.com/38491): Reject Unions here as well.
+        (object is Struct) ||
+        (object is Union)) {
       throw new ArgumentError.value(object,
-          "Expandos are not allowed on strings, numbers, booleans, null, Pointers or Structs.");
+          "Expandos are not allowed on strings, numbers, booleans, null, Pointers, Structs or Unions.");
     }
   }
 
diff --git a/sdk/lib/_internal/vm/lib/ffi_patch.dart b/sdk/lib/_internal/vm/lib/ffi_patch.dart
index c9c3861..4290185 100644
--- a/sdk/lib/_internal/vm/lib/ffi_patch.dart
+++ b/sdk/lib/_internal/vm/lib/ffi_patch.dart
@@ -714,6 +714,16 @@
       throw "UNREACHABLE: This case should have been rewritten in the CFE.";
 }
 
+extension UnionPointer<T extends Union> on Pointer<T> {
+  @patch
+  T get ref =>
+      throw "UNREACHABLE: This case should have been rewritten in the CFE.";
+
+  @patch
+  T operator [](int index) =>
+      throw "UNREACHABLE: This case should have been rewritten in the CFE.";
+}
+
 extension PointerArray<T extends NativeType> on Array<Pointer<T>> {
   @patch
   Pointer<T> operator [](int index) =>
@@ -742,6 +752,13 @@
   }
 }
 
+extension UnionArray<T extends Union> on Array<T> {
+  @patch
+  T operator [](int index) {
+    throw ArgumentError("S ($T) should be a subtype of Union at compile-time.");
+  }
+}
+
 extension NativePort on SendPort {
   @patch
   int get nativePort native "SendPortImpl_get_id";
diff --git a/sdk/lib/_internal/vm/lib/ffi_struct_patch.dart b/sdk/lib/_internal/vm/lib/ffi_struct_patch.dart
index 1dd4726..00279f3 100644
--- a/sdk/lib/_internal/vm/lib/ffi_struct_patch.dart
+++ b/sdk/lib/_internal/vm/lib/ffi_struct_patch.dart
@@ -9,7 +9,13 @@
 import 'dart:isolate';
 
 @pragma("vm:entry-point")
-abstract class Struct extends NativeType {}
+abstract class _Compound extends NativeType {}
+
+@pragma("vm:entry-point")
+abstract class Struct extends _Compound {}
+
+@pragma("vm:entry-point")
+abstract class Union extends _Compound {}
 
 @pragma("vm:entry-point")
 class _FfiStructLayout {
diff --git a/sdk/lib/core/expando.dart b/sdk/lib/core/expando.dart
index c081df8..bcdee49 100644
--- a/sdk/lib/core/expando.dart
+++ b/sdk/lib/core/expando.dart
@@ -6,8 +6,8 @@
 
 /// An [Expando] allows adding new properties to objects.
 ///
-/// Does not work on numbers, strings, booleans, `null`, `dart:ffi` pointers
-/// or `dart:ffi` structs.
+/// Does not work on numbers, strings, booleans, `null`, `dart:ffi` pointers,
+/// `dart:ffi` structs, or `dart:ffi` unions.
 ///
 /// An `Expando` does not hold on to the added property value after an object
 /// becomes inaccessible.
@@ -40,7 +40,7 @@
   /// `null`.
   ///
   /// The object must not be a number, a string, a boolean, `null`, a
-  /// `dart:ffi` pointer, or a `dart:ffi` struct.
+  /// `dart:ffi` pointer, a `dart:ffi` struct, or a `dart:ffi` union.
   external T? operator [](Object object);
 
   /// Sets the value of this [Expando]'s property on the given
@@ -48,6 +48,6 @@
   /// their value to `null`.
   ///
   /// The object must not be a number, a string, a boolean, `null`, a
-  /// `dart:ffi` pointer, or a `dart:ffi` struct.
+  /// `dart:ffi` pointer, a `dart:ffi` struct, or a `dart:ffi` union.
   external void operator []=(Object object, T? value);
 }
diff --git a/sdk/lib/ffi/ffi.dart b/sdk/lib/ffi/ffi.dart
index 733c8fe..369b389 100644
--- a/sdk/lib/ffi/ffi.dart
+++ b/sdk/lib/ffi/ffi.dart
@@ -19,6 +19,7 @@
 part "annotations.dart";
 part "dynamic_library.dart";
 part "struct.dart";
+part "union.dart";
 
 /// Number of bytes used by native type T.
 ///
@@ -692,6 +693,27 @@
   external T operator [](int index);
 }
 
+/// Extension on [Pointer] specialized for the type argument [Union].
+extension UnionPointer<T extends Union> on Pointer<T> {
+  /// Creates a reference to access the fields of this union backed by native
+  /// memory at [address].
+  ///
+  /// The [address] must be aligned according to the union alignment rules of
+  /// the platform.
+  ///
+  /// This extension method must be invoked with a compile-time constant [T].
+  external T get ref;
+
+  /// Creates a reference to access the fields of this union backed by native
+  /// memory at `address + sizeOf<T>() * index`.
+  ///
+  /// The [address] must be aligned according to the union alignment rules of
+  /// the platform.
+  ///
+  /// This extension method must be invoked with a compile-time constant [T].
+  external T operator [](int index);
+}
+
 /// Bounds checking indexing methods on [Array]s of [Pointer].
 extension PointerArray<T extends NativeType> on Array<Pointer<T>> {
   external Pointer<T> operator [](int index);
@@ -705,6 +727,12 @@
   external T operator [](int index);
 }
 
+/// Bounds checking indexing methods on [Array]s of [Union].
+extension UnionArray<T extends Union> on Array<T> {
+  /// This extension method must be invoked with a compile-time constant [T].
+  external T operator [](int index);
+}
+
 /// Bounds checking indexing methods on [Array]s of [Array].
 extension ArrayArray<T extends NativeType> on Array<Array<T>> {
   external Array<T> operator [](int index);
diff --git a/sdk/lib/ffi/ffi_sources.gni b/sdk/lib/ffi/ffi_sources.gni
index db03071..4152f91 100644
--- a/sdk/lib/ffi/ffi_sources.gni
+++ b/sdk/lib/ffi/ffi_sources.gni
@@ -11,4 +11,5 @@
   "dynamic_library.dart",
   "native_type.dart",
   "struct.dart",
+  "union.dart",
 ]
diff --git a/sdk/lib/ffi/union.dart b/sdk/lib/ffi/union.dart
new file mode 100644
index 0000000..d0c7a66
--- /dev/null
+++ b/sdk/lib/ffi/union.dart
@@ -0,0 +1,58 @@
+// 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.
+
+part of dart.ffi;
+
+/// The supertype of all FFI union types.
+///
+/// FFI union types should extend this class and declare fields corresponding
+/// to the underlying native union.
+///
+/// Field declarations in a [Union] subclass declaration are automatically
+/// given a setter and getter implementation which accesses the native union's
+/// field in memory.
+///
+/// All field declarations in a [Union] subclass declaration must either have
+/// type [int] or [float] and be annotated with a [NativeType] representing the
+/// native type, or must be of type [Pointer]. For example:
+///
+/// ```c
+/// typedef union {
+///  int a;
+///  float b;
+///  void* c;
+/// } my_union;
+/// ```
+///
+/// ```dart
+/// class MyUnion extends Union {
+///   @Int32()
+///   external int a;
+///
+///   @Float()
+///   external double b;
+///
+///   external Pointer<Void> c;
+/// }
+/// ```
+///
+/// All field declarations in a [Union] subclass declaration must be marked
+/// `external`. You cannot create instances of the class, only have it point to
+/// existing native memory, so there is no memory in which to store non-native
+/// fields. External fields also cannot be initialized by constructors since no
+/// Dart object is being created.
+///
+/// Instances of a subclass of [Union] have reference semantics and are backed
+/// by native memory. The may allocated via allocation or loaded from a
+/// [Pointer], but cannot be created by a generative constructor.
+abstract class Union extends _Compound {
+  /// Construct a reference to the [nullptr].
+  ///
+  /// Use [UnionPointer]'s `.ref` to gain references to native memory backed
+  /// unions.
+  Union() : super._();
+
+  Union._fromTypedDataBase(Object typedDataBase)
+      : super._fromTypedDataBase(typedDataBase);
+}
diff --git a/sdk/lib/io/embedder_config.dart b/sdk/lib/io/embedder_config.dart
index fcac6d1..fd5bb24 100644
--- a/sdk/lib/io/embedder_config.dart
+++ b/sdk/lib/io/embedder_config.dart
@@ -36,13 +36,18 @@
   /// to all domains.
   ///
   /// This setting can be overridden by per-domain policies.
+  @Deprecated(
+      "To be re-implemented in https://github.com/flutter/flutter/issues/54448")
   @pragma('vm:entry-point')
   static bool _mayInsecurelyConnectToAllDomains = true;
 
   /// Domain network policies set by embedder.
+  @Deprecated(
+      "To be re-implemented in https://github.com/flutter/flutter/issues/54448")
   @pragma('vm:entry-point')
   static void _setDomainPolicies(String domainNetworkPolicyJson) {
-    _domainPolicies = _constructDomainPolicies(domainNetworkPolicyJson);
+    // Doesn't do anything because the implementation has been reverted in
+    // https://github.com/flutter/flutter/issues/72723.
   }
 
   // TODO(zra): Consider adding:
diff --git a/sdk/lib/io/network_policy.dart b/sdk/lib/io/network_policy.dart
index 9fa586b..ef00ffc 100644
--- a/sdk/lib/io/network_policy.dart
+++ b/sdk/lib/io/network_policy.dart
@@ -6,6 +6,9 @@
 
 /// Whether insecure connections to [host] are allowed.
 ///
+/// This API is deprecated and always returns true. See
+/// https://github.com/flutter/flutter/issues/72723 for more details.
+///
 /// [host] must be a [String] or [InternetAddress].
 ///
 /// If any of the domain policies match [host], the matching policy will make
@@ -14,179 +17,7 @@
 /// used.
 ///
 /// Loopback addresses are always allowed.
+@Deprecated("See https://github.com/flutter/flutter/issues/54448 for followup")
 bool isInsecureConnectionAllowed(dynamic host) {
-  String hostString;
-  if (host is String) {
-    try {
-      if ("localhost" == host || InternetAddress(host).isLoopback) return true;
-    } on ArgumentError {
-      // Assume not loopback.
-    }
-    hostString = host;
-  } else if (host is InternetAddress) {
-    if (host.isLoopback) return true;
-    hostString = host.host;
-  } else {
-    throw ArgumentError.value(
-        host, "host", "Must be a String or InternetAddress");
-  }
-  final topMatchedPolicy = _findBestDomainNetworkPolicy(hostString);
-  final envOverride = bool.fromEnvironment(
-      "dart.library.io.may_insecurely_connect_to_all_domains",
-      defaultValue: true);
-  return topMatchedPolicy?.allowInsecureConnections ??
-      (envOverride && _EmbedderConfig._mayInsecurelyConnectToAllDomains);
-}
-
-/// Policy for a specific domain.
-///
-/// [_DomainNetworkPolicy] can be used to create exceptions to the global
-/// network policy.
-class _DomainNetworkPolicy {
-  /// https://tools.ietf.org/html/rfc1034#:~:text=Name%20space%20specifications
-  ///
-  /// We specifically do not allow IP addresses.
-  static final _domainMatcher = RegExp(
-      r"^(?:[a-z\d-]{1,63}\.)+[a-z][a-z\d-]{0,62}$",
-      caseSensitive: false);
-
-  /// The domain on which the policy is being set.
-  ///
-  /// This cannot be a numeric IP address.
-  ///
-  /// For example: `example.com`.
-  final String domain;
-
-  /// Whether to allow insecure socket connections for this domain.
-  final bool allowInsecureConnections;
-
-  /// Whether this domain policy covers sub-domains as well.
-  ///
-  /// If this is true, all subdomains inherit the same policy. For instance,
-  /// a policy set on `example.com` would apply to `*.example.com` such as
-  /// `subdomain.example.com` or `www.example.com`.
-  final bool includesSubDomains;
-
-  /// Creates a new domain exception in the network policy.
-  ///
-  /// [domain] is the domain on which the policy is being set.
-  ///
-  /// [includesSubDomains] determines whether the policy applies to
-  /// all sub domains. If this is set to true, all subdomains inherit the
-  /// same policy. For instance, a policy set on `example.com` would apply to
-  /// `*.example.com` such as `subdomain.example.com` or `www.example.com`.
-  ///
-  /// [allowInsecureConnections] determines whether to allow insecure socket
-  /// connections for this [domain].
-  _DomainNetworkPolicy(this.domain,
-      {this.includesSubDomains = false,
-      this.allowInsecureConnections = false}) {
-    if (domain.length > 255 || !_domainMatcher.hasMatch(domain)) {
-      throw ArgumentError.value(domain, "domain", "Invalid domain name");
-    }
-  }
-
-  /// Calculates how well the policy matches to a given host string.
-  ///
-  /// A host matches a [policy] if it ends with its [domain].
-  ///
-  /// A score is given to such a match depending on the specificity of the
-  /// [domain]:
-  ///
-  /// * A longer domain receives a higher score.
-  /// * A domain that does not allow sub domains receives a higher score.
-  ///
-  /// Returns -1 if the policy does not match.
-  int matchScore(String host) {
-    final domainLength = domain.length;
-    final hostLength = host.length;
-    final lengthDelta = hostLength - domainLength;
-    if (host.endsWith(domain) &&
-        (lengthDelta == 0 ||
-            includesSubDomains && host.codeUnitAt(lengthDelta - 1) == 0x2e)) {
-      return domainLength * 2 + (includesSubDomains ? 0 : 1);
-    }
-    return -1;
-  }
-
-  /// Checks whether the [policy] to be added conflicts with existing policies.
-  ///
-  /// Returns [true] if policy is safe to add to existing policy set and [false]
-  ///     if policy can safely be ignored.
-  ///
-  /// Throws [ArgumentError] if a conflict is detected.
-  bool checkConflict(List<_DomainNetworkPolicy> existingPolicies) {
-    for (final existingPolicy in existingPolicies) {
-      if (includesSubDomains == existingPolicy.includesSubDomains &&
-          domain == existingPolicy.domain) {
-        if (allowInsecureConnections ==
-            existingPolicy.allowInsecureConnections) {
-          // This is a duplicate policy
-          return false;
-        }
-        throw StateError("Contradiction in the domain security policies: "
-            "'$this' contradicts '$existingPolicy'");
-      }
-    }
-    return true;
-  }
-
-  /// This is used for encoding information about the policy in user visible
-  /// errors.
-  @override
-  String toString() {
-    final subDomainPrefix = includesSubDomains ? '*.' : '';
-    final insecureConnectionPermission =
-        allowInsecureConnections ? 'Allows' : 'Disallows';
-    return "$subDomainPrefix$domain: "
-        "$insecureConnectionPermission insecure connections";
-  }
-}
-
-/// Finds the top [DomainNetworkPolicy] instance that match given a single
-/// [domain].
-///
-/// We order the policies according to how specific they are. The final policy
-/// for a given [domain] is determined by the top matching
-/// [DomainNetworkPolicy].
-///
-/// Returns null if there's no matching policy.
-_DomainNetworkPolicy? _findBestDomainNetworkPolicy(String domain) {
-  var topScore = 0;
-  _DomainNetworkPolicy? topPolicy;
-  for (final _DomainNetworkPolicy policy in _domainPolicies) {
-    final score = policy.matchScore(domain);
-    if (score > topScore) {
-      topScore = score;
-      topPolicy = policy;
-    }
-  }
-  return topPolicy;
-}
-
-/// Domain level policies that dart:io is enforcing.
-late List<_DomainNetworkPolicy> _domainPolicies =
-    _constructDomainPolicies(null);
-
-List<_DomainNetworkPolicy> _constructDomainPolicies(
-    String? domainPoliciesString) {
-  final domainPolicies = <_DomainNetworkPolicy>[];
-  domainPoliciesString ??= String.fromEnvironment(
-      "dart.library.io.domain_network_policies",
-      defaultValue: "");
-  if (domainPoliciesString.isNotEmpty) {
-    final List<dynamic> policiesJson = json.decode(domainPoliciesString);
-    for (final List<dynamic> policyJson in policiesJson) {
-      assert(policyJson.length == 3);
-      final policy = _DomainNetworkPolicy(
-        policyJson[0],
-        includesSubDomains: policyJson[1],
-        allowInsecureConnections: policyJson[2],
-      );
-      if (policy.checkConflict(domainPolicies)) {
-        domainPolicies.add(policy);
-      }
-    }
-  }
-  return domainPolicies;
+  return true;
 }
diff --git a/sdk/lib/libraries.yaml b/sdk/lib/libraries.yaml
index 87beca8..6262221 100644
--- a/sdk/lib/libraries.yaml
+++ b/sdk/lib/libraries.yaml
@@ -5,7 +5,7 @@
 # Note: if you edit this file, you must also generate libraries.json in this
 # directory:
 #
-#     python ./tools/yaml2json.py sdk/lib/libraries.yaml sdk/lib/libraries.json
+#     python3 ./tools/yaml2json.py sdk/lib/libraries.yaml sdk/lib/libraries.json
 #
 # We currently have several different files that needs to be updated when
 # changing libraries, sources, and patch files.  See
diff --git a/sdk/lib/vmservice_libraries.yaml b/sdk/lib/vmservice_libraries.yaml
index 169d6af..c30ac8f 100644
--- a/sdk/lib/vmservice_libraries.yaml
+++ b/sdk/lib/vmservice_libraries.yaml
@@ -5,7 +5,7 @@
 # Note: if you edit this file, you must also edit libraries.json in this
 # directory:
 #
-#     python ./tools/yaml2json.py sdk/lib/vmservice_libraries.yaml sdk/lib/vmservice_libraries.json
+#     python3 ./tools/yaml2json.py sdk/lib/vmservice_libraries.yaml sdk/lib/vmservice_libraries.json
 #
 # We currently have several different files that needs to be updated when
 # changing libraries, sources, and patch files.  See
diff --git a/tests/corelib/type_tostring_test.dart b/tests/corelib/type_tostring_test.dart
index 8e1c2d1..8f2fa68 100644
--- a/tests/corelib/type_tostring_test.dart
+++ b/tests/corelib/type_tostring_test.dart
@@ -128,7 +128,6 @@
 typedef G4 = S Function<T>(S, T) Function<S>(S);
 typedef G5 = S Function<S, T>(S, T);
 
-typedef Weird = Function Function<Function>(
-    Function Function<Function>(Function));
+typedef Weird = X Function<X>(X Function<X>(X));
 
 RegExp re(String source) => RegExp(source);
diff --git a/tests/corelib_2/type_tostring_test.dart b/tests/corelib_2/type_tostring_test.dart
index 8e1c2d1..8f2fa68 100644
--- a/tests/corelib_2/type_tostring_test.dart
+++ b/tests/corelib_2/type_tostring_test.dart
@@ -128,7 +128,6 @@
 typedef G4 = S Function<T>(S, T) Function<S>(S);
 typedef G5 = S Function<S, T>(S, T);
 
-typedef Weird = Function Function<Function>(
-    Function Function<Function>(Function));
+typedef Weird = X Function<X>(X Function<X>(X));
 
 RegExp re(String source) => RegExp(source);
diff --git a/tests/ffi/ffi.status b/tests/ffi/ffi.status
index f157962..82349e8 100644
--- a/tests/ffi/ffi.status
+++ b/tests/ffi/ffi.status
@@ -11,6 +11,9 @@
 [ $system == android ]
 *: Pass, Slow # https://github.com/dart-lang/sdk/issues/38489
 
+[ $arch == arm && $system == linux ]
+function_callbacks_structs_by_value_test: Slow # QEMU https://dartbug.com/45007
+
 [ $compiler != dart2analyzer && $compiler != fasta && $runtime != dart_precompiled && $runtime != vm ]
 *: SkipByDesign # FFI is a VM-only feature. (This test suite is part of the default set.)
 
diff --git a/tests/ffi/function_callbacks_structs_by_value_generated_test.dart b/tests/ffi/function_callbacks_structs_by_value_generated_test.dart
index 09648be..d13cf55 100644
--- a/tests/ffi/function_callbacks_structs_by_value_generated_test.dart
+++ b/tests/ffi/function_callbacks_structs_by_value_generated_test.dart
@@ -321,6 +321,31 @@
           passStruct15BytesInlineArrayMixed, 0.0),
       passStruct15BytesInlineArrayMixedAfterCallback),
   CallbackTest.withCheck(
+      "PassUnion4BytesMixedx10",
+      Pointer.fromFunction<PassUnion4BytesMixedx10Type>(
+          passUnion4BytesMixedx10, 0.0),
+      passUnion4BytesMixedx10AfterCallback),
+  CallbackTest.withCheck(
+      "PassUnion8BytesNestedFloatx10",
+      Pointer.fromFunction<PassUnion8BytesNestedFloatx10Type>(
+          passUnion8BytesNestedFloatx10, 0.0),
+      passUnion8BytesNestedFloatx10AfterCallback),
+  CallbackTest.withCheck(
+      "PassUnion9BytesNestedIntx10",
+      Pointer.fromFunction<PassUnion9BytesNestedIntx10Type>(
+          passUnion9BytesNestedIntx10, 0.0),
+      passUnion9BytesNestedIntx10AfterCallback),
+  CallbackTest.withCheck(
+      "PassUnion16BytesNestedInlineArrayFloatx10",
+      Pointer.fromFunction<PassUnion16BytesNestedInlineArrayFloatx10Type>(
+          passUnion16BytesNestedInlineArrayFloatx10, 0.0),
+      passUnion16BytesNestedInlineArrayFloatx10AfterCallback),
+  CallbackTest.withCheck(
+      "PassUnion16BytesNestedFloatx10",
+      Pointer.fromFunction<PassUnion16BytesNestedFloatx10Type>(
+          passUnion16BytesNestedFloatx10, 0.0),
+      passUnion16BytesNestedFloatx10AfterCallback),
+  CallbackTest.withCheck(
       "ReturnStruct1ByteInt",
       Pointer.fromFunction<ReturnStruct1ByteIntType>(returnStruct1ByteInt),
       returnStruct1ByteIntAfterCallback),
@@ -443,6 +468,25 @@
           returnStruct9BytesPackedMixed),
       returnStruct9BytesPackedMixedAfterCallback),
   CallbackTest.withCheck(
+      "ReturnUnion4BytesMixed",
+      Pointer.fromFunction<ReturnUnion4BytesMixedType>(returnUnion4BytesMixed),
+      returnUnion4BytesMixedAfterCallback),
+  CallbackTest.withCheck(
+      "ReturnUnion8BytesNestedFloat",
+      Pointer.fromFunction<ReturnUnion8BytesNestedFloatType>(
+          returnUnion8BytesNestedFloat),
+      returnUnion8BytesNestedFloatAfterCallback),
+  CallbackTest.withCheck(
+      "ReturnUnion9BytesNestedInt",
+      Pointer.fromFunction<ReturnUnion9BytesNestedIntType>(
+          returnUnion9BytesNestedInt),
+      returnUnion9BytesNestedIntAfterCallback),
+  CallbackTest.withCheck(
+      "ReturnUnion16BytesNestedFloat",
+      Pointer.fromFunction<ReturnUnion16BytesNestedFloatType>(
+          returnUnion16BytesNestedFloat),
+      returnUnion16BytesNestedFloatAfterCallback),
+  CallbackTest.withCheck(
       "ReturnStructArgumentStruct1ByteInt",
       Pointer.fromFunction<ReturnStructArgumentStruct1ByteIntType>(
           returnStructArgumentStruct1ByteInt),
@@ -6840,6 +6884,582 @@
   Expect.approxEquals(3.0, result);
 }
 
+typedef PassUnion4BytesMixedx10Type = Double Function(
+    Union4BytesMixed,
+    Union4BytesMixed,
+    Union4BytesMixed,
+    Union4BytesMixed,
+    Union4BytesMixed,
+    Union4BytesMixed,
+    Union4BytesMixed,
+    Union4BytesMixed,
+    Union4BytesMixed,
+    Union4BytesMixed);
+
+// Global variables to be able to test inputs after callback returned.
+Union4BytesMixed passUnion4BytesMixedx10_a0 = Union4BytesMixed();
+Union4BytesMixed passUnion4BytesMixedx10_a1 = Union4BytesMixed();
+Union4BytesMixed passUnion4BytesMixedx10_a2 = Union4BytesMixed();
+Union4BytesMixed passUnion4BytesMixedx10_a3 = Union4BytesMixed();
+Union4BytesMixed passUnion4BytesMixedx10_a4 = Union4BytesMixed();
+Union4BytesMixed passUnion4BytesMixedx10_a5 = Union4BytesMixed();
+Union4BytesMixed passUnion4BytesMixedx10_a6 = Union4BytesMixed();
+Union4BytesMixed passUnion4BytesMixedx10_a7 = Union4BytesMixed();
+Union4BytesMixed passUnion4BytesMixedx10_a8 = Union4BytesMixed();
+Union4BytesMixed passUnion4BytesMixedx10_a9 = Union4BytesMixed();
+
+// Result variable also global, so we can delete it after the callback.
+double passUnion4BytesMixedx10Result = 0.0;
+
+double passUnion4BytesMixedx10CalculateResult() {
+  double result = 0;
+
+  result += passUnion4BytesMixedx10_a0.a0;
+  result += passUnion4BytesMixedx10_a1.a0;
+  result += passUnion4BytesMixedx10_a2.a0;
+  result += passUnion4BytesMixedx10_a3.a0;
+  result += passUnion4BytesMixedx10_a4.a0;
+  result += passUnion4BytesMixedx10_a5.a0;
+  result += passUnion4BytesMixedx10_a6.a0;
+  result += passUnion4BytesMixedx10_a7.a0;
+  result += passUnion4BytesMixedx10_a8.a0;
+  result += passUnion4BytesMixedx10_a9.a0;
+
+  passUnion4BytesMixedx10Result = result;
+
+  return result;
+}
+
+/// Check placement of mixed integer/float union.
+double passUnion4BytesMixedx10(
+    Union4BytesMixed a0,
+    Union4BytesMixed a1,
+    Union4BytesMixed a2,
+    Union4BytesMixed a3,
+    Union4BytesMixed a4,
+    Union4BytesMixed a5,
+    Union4BytesMixed a6,
+    Union4BytesMixed a7,
+    Union4BytesMixed a8,
+    Union4BytesMixed a9) {
+  print(
+      "passUnion4BytesMixedx10(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0 == 42 || a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("PassUnion4BytesMixedx10 throwing on purpose!");
+  }
+
+  passUnion4BytesMixedx10_a0 = a0;
+  passUnion4BytesMixedx10_a1 = a1;
+  passUnion4BytesMixedx10_a2 = a2;
+  passUnion4BytesMixedx10_a3 = a3;
+  passUnion4BytesMixedx10_a4 = a4;
+  passUnion4BytesMixedx10_a5 = a5;
+  passUnion4BytesMixedx10_a6 = a6;
+  passUnion4BytesMixedx10_a7 = a7;
+  passUnion4BytesMixedx10_a8 = a8;
+  passUnion4BytesMixedx10_a9 = a9;
+
+  final result = passUnion4BytesMixedx10CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passUnion4BytesMixedx10AfterCallback() {
+  final result = passUnion4BytesMixedx10CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.approxEquals(55.0, result);
+}
+
+typedef PassUnion8BytesNestedFloatx10Type = Double Function(
+    Union8BytesNestedFloat,
+    Union8BytesNestedFloat,
+    Union8BytesNestedFloat,
+    Union8BytesNestedFloat,
+    Union8BytesNestedFloat,
+    Union8BytesNestedFloat,
+    Union8BytesNestedFloat,
+    Union8BytesNestedFloat,
+    Union8BytesNestedFloat,
+    Union8BytesNestedFloat);
+
+// Global variables to be able to test inputs after callback returned.
+Union8BytesNestedFloat passUnion8BytesNestedFloatx10_a0 =
+    Union8BytesNestedFloat();
+Union8BytesNestedFloat passUnion8BytesNestedFloatx10_a1 =
+    Union8BytesNestedFloat();
+Union8BytesNestedFloat passUnion8BytesNestedFloatx10_a2 =
+    Union8BytesNestedFloat();
+Union8BytesNestedFloat passUnion8BytesNestedFloatx10_a3 =
+    Union8BytesNestedFloat();
+Union8BytesNestedFloat passUnion8BytesNestedFloatx10_a4 =
+    Union8BytesNestedFloat();
+Union8BytesNestedFloat passUnion8BytesNestedFloatx10_a5 =
+    Union8BytesNestedFloat();
+Union8BytesNestedFloat passUnion8BytesNestedFloatx10_a6 =
+    Union8BytesNestedFloat();
+Union8BytesNestedFloat passUnion8BytesNestedFloatx10_a7 =
+    Union8BytesNestedFloat();
+Union8BytesNestedFloat passUnion8BytesNestedFloatx10_a8 =
+    Union8BytesNestedFloat();
+Union8BytesNestedFloat passUnion8BytesNestedFloatx10_a9 =
+    Union8BytesNestedFloat();
+
+// Result variable also global, so we can delete it after the callback.
+double passUnion8BytesNestedFloatx10Result = 0.0;
+
+double passUnion8BytesNestedFloatx10CalculateResult() {
+  double result = 0;
+
+  result += passUnion8BytesNestedFloatx10_a0.a0;
+  result += passUnion8BytesNestedFloatx10_a1.a0;
+  result += passUnion8BytesNestedFloatx10_a2.a0;
+  result += passUnion8BytesNestedFloatx10_a3.a0;
+  result += passUnion8BytesNestedFloatx10_a4.a0;
+  result += passUnion8BytesNestedFloatx10_a5.a0;
+  result += passUnion8BytesNestedFloatx10_a6.a0;
+  result += passUnion8BytesNestedFloatx10_a7.a0;
+  result += passUnion8BytesNestedFloatx10_a8.a0;
+  result += passUnion8BytesNestedFloatx10_a9.a0;
+
+  passUnion8BytesNestedFloatx10Result = result;
+
+  return result;
+}
+
+/// Check placement of mixed floats union.
+double passUnion8BytesNestedFloatx10(
+    Union8BytesNestedFloat a0,
+    Union8BytesNestedFloat a1,
+    Union8BytesNestedFloat a2,
+    Union8BytesNestedFloat a3,
+    Union8BytesNestedFloat a4,
+    Union8BytesNestedFloat a5,
+    Union8BytesNestedFloat a6,
+    Union8BytesNestedFloat a7,
+    Union8BytesNestedFloat a8,
+    Union8BytesNestedFloat a9) {
+  print(
+      "passUnion8BytesNestedFloatx10(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0 == 42 || a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("PassUnion8BytesNestedFloatx10 throwing on purpose!");
+  }
+
+  passUnion8BytesNestedFloatx10_a0 = a0;
+  passUnion8BytesNestedFloatx10_a1 = a1;
+  passUnion8BytesNestedFloatx10_a2 = a2;
+  passUnion8BytesNestedFloatx10_a3 = a3;
+  passUnion8BytesNestedFloatx10_a4 = a4;
+  passUnion8BytesNestedFloatx10_a5 = a5;
+  passUnion8BytesNestedFloatx10_a6 = a6;
+  passUnion8BytesNestedFloatx10_a7 = a7;
+  passUnion8BytesNestedFloatx10_a8 = a8;
+  passUnion8BytesNestedFloatx10_a9 = a9;
+
+  final result = passUnion8BytesNestedFloatx10CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passUnion8BytesNestedFloatx10AfterCallback() {
+  final result = passUnion8BytesNestedFloatx10CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.approxEquals(5.0, result);
+}
+
+typedef PassUnion9BytesNestedIntx10Type = Double Function(
+    Union9BytesNestedInt,
+    Union9BytesNestedInt,
+    Union9BytesNestedInt,
+    Union9BytesNestedInt,
+    Union9BytesNestedInt,
+    Union9BytesNestedInt,
+    Union9BytesNestedInt,
+    Union9BytesNestedInt,
+    Union9BytesNestedInt,
+    Union9BytesNestedInt);
+
+// Global variables to be able to test inputs after callback returned.
+Union9BytesNestedInt passUnion9BytesNestedIntx10_a0 = Union9BytesNestedInt();
+Union9BytesNestedInt passUnion9BytesNestedIntx10_a1 = Union9BytesNestedInt();
+Union9BytesNestedInt passUnion9BytesNestedIntx10_a2 = Union9BytesNestedInt();
+Union9BytesNestedInt passUnion9BytesNestedIntx10_a3 = Union9BytesNestedInt();
+Union9BytesNestedInt passUnion9BytesNestedIntx10_a4 = Union9BytesNestedInt();
+Union9BytesNestedInt passUnion9BytesNestedIntx10_a5 = Union9BytesNestedInt();
+Union9BytesNestedInt passUnion9BytesNestedIntx10_a6 = Union9BytesNestedInt();
+Union9BytesNestedInt passUnion9BytesNestedIntx10_a7 = Union9BytesNestedInt();
+Union9BytesNestedInt passUnion9BytesNestedIntx10_a8 = Union9BytesNestedInt();
+Union9BytesNestedInt passUnion9BytesNestedIntx10_a9 = Union9BytesNestedInt();
+
+// Result variable also global, so we can delete it after the callback.
+double passUnion9BytesNestedIntx10Result = 0.0;
+
+double passUnion9BytesNestedIntx10CalculateResult() {
+  double result = 0;
+
+  result += passUnion9BytesNestedIntx10_a0.a0.a0;
+  result += passUnion9BytesNestedIntx10_a0.a0.a1;
+  result += passUnion9BytesNestedIntx10_a0.a0.a2;
+  result += passUnion9BytesNestedIntx10_a1.a0.a0;
+  result += passUnion9BytesNestedIntx10_a1.a0.a1;
+  result += passUnion9BytesNestedIntx10_a1.a0.a2;
+  result += passUnion9BytesNestedIntx10_a2.a0.a0;
+  result += passUnion9BytesNestedIntx10_a2.a0.a1;
+  result += passUnion9BytesNestedIntx10_a2.a0.a2;
+  result += passUnion9BytesNestedIntx10_a3.a0.a0;
+  result += passUnion9BytesNestedIntx10_a3.a0.a1;
+  result += passUnion9BytesNestedIntx10_a3.a0.a2;
+  result += passUnion9BytesNestedIntx10_a4.a0.a0;
+  result += passUnion9BytesNestedIntx10_a4.a0.a1;
+  result += passUnion9BytesNestedIntx10_a4.a0.a2;
+  result += passUnion9BytesNestedIntx10_a5.a0.a0;
+  result += passUnion9BytesNestedIntx10_a5.a0.a1;
+  result += passUnion9BytesNestedIntx10_a5.a0.a2;
+  result += passUnion9BytesNestedIntx10_a6.a0.a0;
+  result += passUnion9BytesNestedIntx10_a6.a0.a1;
+  result += passUnion9BytesNestedIntx10_a6.a0.a2;
+  result += passUnion9BytesNestedIntx10_a7.a0.a0;
+  result += passUnion9BytesNestedIntx10_a7.a0.a1;
+  result += passUnion9BytesNestedIntx10_a7.a0.a2;
+  result += passUnion9BytesNestedIntx10_a8.a0.a0;
+  result += passUnion9BytesNestedIntx10_a8.a0.a1;
+  result += passUnion9BytesNestedIntx10_a8.a0.a2;
+  result += passUnion9BytesNestedIntx10_a9.a0.a0;
+  result += passUnion9BytesNestedIntx10_a9.a0.a1;
+  result += passUnion9BytesNestedIntx10_a9.a0.a2;
+
+  passUnion9BytesNestedIntx10Result = result;
+
+  return result;
+}
+
+/// Mixed-size union argument.
+double passUnion9BytesNestedIntx10(
+    Union9BytesNestedInt a0,
+    Union9BytesNestedInt a1,
+    Union9BytesNestedInt a2,
+    Union9BytesNestedInt a3,
+    Union9BytesNestedInt a4,
+    Union9BytesNestedInt a5,
+    Union9BytesNestedInt a6,
+    Union9BytesNestedInt a7,
+    Union9BytesNestedInt a8,
+    Union9BytesNestedInt a9) {
+  print(
+      "passUnion9BytesNestedIntx10(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("PassUnion9BytesNestedIntx10 throwing on purpose!");
+  }
+
+  passUnion9BytesNestedIntx10_a0 = a0;
+  passUnion9BytesNestedIntx10_a1 = a1;
+  passUnion9BytesNestedIntx10_a2 = a2;
+  passUnion9BytesNestedIntx10_a3 = a3;
+  passUnion9BytesNestedIntx10_a4 = a4;
+  passUnion9BytesNestedIntx10_a5 = a5;
+  passUnion9BytesNestedIntx10_a6 = a6;
+  passUnion9BytesNestedIntx10_a7 = a7;
+  passUnion9BytesNestedIntx10_a8 = a8;
+  passUnion9BytesNestedIntx10_a9 = a9;
+
+  final result = passUnion9BytesNestedIntx10CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passUnion9BytesNestedIntx10AfterCallback() {
+  final result = passUnion9BytesNestedIntx10CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.approxEquals(15.0, result);
+}
+
+typedef PassUnion16BytesNestedInlineArrayFloatx10Type = Double Function(
+    Union16BytesNestedInlineArrayFloat,
+    Union16BytesNestedInlineArrayFloat,
+    Union16BytesNestedInlineArrayFloat,
+    Union16BytesNestedInlineArrayFloat,
+    Union16BytesNestedInlineArrayFloat,
+    Union16BytesNestedInlineArrayFloat,
+    Union16BytesNestedInlineArrayFloat,
+    Union16BytesNestedInlineArrayFloat,
+    Union16BytesNestedInlineArrayFloat,
+    Union16BytesNestedInlineArrayFloat);
+
+// Global variables to be able to test inputs after callback returned.
+Union16BytesNestedInlineArrayFloat
+    passUnion16BytesNestedInlineArrayFloatx10_a0 =
+    Union16BytesNestedInlineArrayFloat();
+Union16BytesNestedInlineArrayFloat
+    passUnion16BytesNestedInlineArrayFloatx10_a1 =
+    Union16BytesNestedInlineArrayFloat();
+Union16BytesNestedInlineArrayFloat
+    passUnion16BytesNestedInlineArrayFloatx10_a2 =
+    Union16BytesNestedInlineArrayFloat();
+Union16BytesNestedInlineArrayFloat
+    passUnion16BytesNestedInlineArrayFloatx10_a3 =
+    Union16BytesNestedInlineArrayFloat();
+Union16BytesNestedInlineArrayFloat
+    passUnion16BytesNestedInlineArrayFloatx10_a4 =
+    Union16BytesNestedInlineArrayFloat();
+Union16BytesNestedInlineArrayFloat
+    passUnion16BytesNestedInlineArrayFloatx10_a5 =
+    Union16BytesNestedInlineArrayFloat();
+Union16BytesNestedInlineArrayFloat
+    passUnion16BytesNestedInlineArrayFloatx10_a6 =
+    Union16BytesNestedInlineArrayFloat();
+Union16BytesNestedInlineArrayFloat
+    passUnion16BytesNestedInlineArrayFloatx10_a7 =
+    Union16BytesNestedInlineArrayFloat();
+Union16BytesNestedInlineArrayFloat
+    passUnion16BytesNestedInlineArrayFloatx10_a8 =
+    Union16BytesNestedInlineArrayFloat();
+Union16BytesNestedInlineArrayFloat
+    passUnion16BytesNestedInlineArrayFloatx10_a9 =
+    Union16BytesNestedInlineArrayFloat();
+
+// Result variable also global, so we can delete it after the callback.
+double passUnion16BytesNestedInlineArrayFloatx10Result = 0.0;
+
+double passUnion16BytesNestedInlineArrayFloatx10CalculateResult() {
+  double result = 0;
+
+  result += passUnion16BytesNestedInlineArrayFloatx10_a0.a0[0];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a0.a0[1];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a0.a0[2];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a0.a0[3];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a1.a0[0];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a1.a0[1];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a1.a0[2];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a1.a0[3];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a2.a0[0];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a2.a0[1];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a2.a0[2];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a2.a0[3];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a3.a0[0];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a3.a0[1];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a3.a0[2];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a3.a0[3];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a4.a0[0];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a4.a0[1];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a4.a0[2];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a4.a0[3];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a5.a0[0];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a5.a0[1];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a5.a0[2];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a5.a0[3];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a6.a0[0];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a6.a0[1];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a6.a0[2];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a6.a0[3];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a7.a0[0];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a7.a0[1];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a7.a0[2];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a7.a0[3];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a8.a0[0];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a8.a0[1];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a8.a0[2];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a8.a0[3];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a9.a0[0];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a9.a0[1];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a9.a0[2];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a9.a0[3];
+
+  passUnion16BytesNestedInlineArrayFloatx10Result = result;
+
+  return result;
+}
+
+/// Union with homogenous floats.
+double passUnion16BytesNestedInlineArrayFloatx10(
+    Union16BytesNestedInlineArrayFloat a0,
+    Union16BytesNestedInlineArrayFloat a1,
+    Union16BytesNestedInlineArrayFloat a2,
+    Union16BytesNestedInlineArrayFloat a3,
+    Union16BytesNestedInlineArrayFloat a4,
+    Union16BytesNestedInlineArrayFloat a5,
+    Union16BytesNestedInlineArrayFloat a6,
+    Union16BytesNestedInlineArrayFloat a7,
+    Union16BytesNestedInlineArrayFloat a8,
+    Union16BytesNestedInlineArrayFloat a9) {
+  print(
+      "passUnion16BytesNestedInlineArrayFloatx10(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0[0] == 42 || a0.a0[0] == 84) {
+    print("throwing!");
+    throw Exception(
+        "PassUnion16BytesNestedInlineArrayFloatx10 throwing on purpose!");
+  }
+
+  passUnion16BytesNestedInlineArrayFloatx10_a0 = a0;
+  passUnion16BytesNestedInlineArrayFloatx10_a1 = a1;
+  passUnion16BytesNestedInlineArrayFloatx10_a2 = a2;
+  passUnion16BytesNestedInlineArrayFloatx10_a3 = a3;
+  passUnion16BytesNestedInlineArrayFloatx10_a4 = a4;
+  passUnion16BytesNestedInlineArrayFloatx10_a5 = a5;
+  passUnion16BytesNestedInlineArrayFloatx10_a6 = a6;
+  passUnion16BytesNestedInlineArrayFloatx10_a7 = a7;
+  passUnion16BytesNestedInlineArrayFloatx10_a8 = a8;
+  passUnion16BytesNestedInlineArrayFloatx10_a9 = a9;
+
+  final result = passUnion16BytesNestedInlineArrayFloatx10CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passUnion16BytesNestedInlineArrayFloatx10AfterCallback() {
+  final result = passUnion16BytesNestedInlineArrayFloatx10CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.approxEquals(20.0, result);
+}
+
+typedef PassUnion16BytesNestedFloatx10Type = Double Function(
+    Union16BytesNestedFloat,
+    Union16BytesNestedFloat,
+    Union16BytesNestedFloat,
+    Union16BytesNestedFloat,
+    Union16BytesNestedFloat,
+    Union16BytesNestedFloat,
+    Union16BytesNestedFloat,
+    Union16BytesNestedFloat,
+    Union16BytesNestedFloat,
+    Union16BytesNestedFloat);
+
+// Global variables to be able to test inputs after callback returned.
+Union16BytesNestedFloat passUnion16BytesNestedFloatx10_a0 =
+    Union16BytesNestedFloat();
+Union16BytesNestedFloat passUnion16BytesNestedFloatx10_a1 =
+    Union16BytesNestedFloat();
+Union16BytesNestedFloat passUnion16BytesNestedFloatx10_a2 =
+    Union16BytesNestedFloat();
+Union16BytesNestedFloat passUnion16BytesNestedFloatx10_a3 =
+    Union16BytesNestedFloat();
+Union16BytesNestedFloat passUnion16BytesNestedFloatx10_a4 =
+    Union16BytesNestedFloat();
+Union16BytesNestedFloat passUnion16BytesNestedFloatx10_a5 =
+    Union16BytesNestedFloat();
+Union16BytesNestedFloat passUnion16BytesNestedFloatx10_a6 =
+    Union16BytesNestedFloat();
+Union16BytesNestedFloat passUnion16BytesNestedFloatx10_a7 =
+    Union16BytesNestedFloat();
+Union16BytesNestedFloat passUnion16BytesNestedFloatx10_a8 =
+    Union16BytesNestedFloat();
+Union16BytesNestedFloat passUnion16BytesNestedFloatx10_a9 =
+    Union16BytesNestedFloat();
+
+// Result variable also global, so we can delete it after the callback.
+double passUnion16BytesNestedFloatx10Result = 0.0;
+
+double passUnion16BytesNestedFloatx10CalculateResult() {
+  double result = 0;
+
+  result += passUnion16BytesNestedFloatx10_a0.a0.a0;
+  result += passUnion16BytesNestedFloatx10_a0.a0.a1;
+  result += passUnion16BytesNestedFloatx10_a1.a0.a0;
+  result += passUnion16BytesNestedFloatx10_a1.a0.a1;
+  result += passUnion16BytesNestedFloatx10_a2.a0.a0;
+  result += passUnion16BytesNestedFloatx10_a2.a0.a1;
+  result += passUnion16BytesNestedFloatx10_a3.a0.a0;
+  result += passUnion16BytesNestedFloatx10_a3.a0.a1;
+  result += passUnion16BytesNestedFloatx10_a4.a0.a0;
+  result += passUnion16BytesNestedFloatx10_a4.a0.a1;
+  result += passUnion16BytesNestedFloatx10_a5.a0.a0;
+  result += passUnion16BytesNestedFloatx10_a5.a0.a1;
+  result += passUnion16BytesNestedFloatx10_a6.a0.a0;
+  result += passUnion16BytesNestedFloatx10_a6.a0.a1;
+  result += passUnion16BytesNestedFloatx10_a7.a0.a0;
+  result += passUnion16BytesNestedFloatx10_a7.a0.a1;
+  result += passUnion16BytesNestedFloatx10_a8.a0.a0;
+  result += passUnion16BytesNestedFloatx10_a8.a0.a1;
+  result += passUnion16BytesNestedFloatx10_a9.a0.a0;
+  result += passUnion16BytesNestedFloatx10_a9.a0.a1;
+
+  passUnion16BytesNestedFloatx10Result = result;
+
+  return result;
+}
+
+/// Union with homogenous floats.
+double passUnion16BytesNestedFloatx10(
+    Union16BytesNestedFloat a0,
+    Union16BytesNestedFloat a1,
+    Union16BytesNestedFloat a2,
+    Union16BytesNestedFloat a3,
+    Union16BytesNestedFloat a4,
+    Union16BytesNestedFloat a5,
+    Union16BytesNestedFloat a6,
+    Union16BytesNestedFloat a7,
+    Union16BytesNestedFloat a8,
+    Union16BytesNestedFloat a9) {
+  print(
+      "passUnion16BytesNestedFloatx10(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("PassUnion16BytesNestedFloatx10 throwing on purpose!");
+  }
+
+  passUnion16BytesNestedFloatx10_a0 = a0;
+  passUnion16BytesNestedFloatx10_a1 = a1;
+  passUnion16BytesNestedFloatx10_a2 = a2;
+  passUnion16BytesNestedFloatx10_a3 = a3;
+  passUnion16BytesNestedFloatx10_a4 = a4;
+  passUnion16BytesNestedFloatx10_a5 = a5;
+  passUnion16BytesNestedFloatx10_a6 = a6;
+  passUnion16BytesNestedFloatx10_a7 = a7;
+  passUnion16BytesNestedFloatx10_a8 = a8;
+  passUnion16BytesNestedFloatx10_a9 = a9;
+
+  final result = passUnion16BytesNestedFloatx10CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passUnion16BytesNestedFloatx10AfterCallback() {
+  final result = passUnion16BytesNestedFloatx10CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.approxEquals(10.0, result);
+}
+
 typedef ReturnStruct1ByteIntType = Struct1ByteInt Function(Int8);
 
 // Global variables to be able to test inputs after callback returned.
@@ -9089,6 +9709,216 @@
   calloc.free(returnStruct9BytesPackedMixedResultPointer);
 }
 
+typedef ReturnUnion4BytesMixedType = Union4BytesMixed Function(Uint32);
+
+// Global variables to be able to test inputs after callback returned.
+int returnUnion4BytesMixed_a0 = 0;
+
+// Result variable also global, so we can delete it after the callback.
+Pointer<Union4BytesMixed> returnUnion4BytesMixedResultPointer = nullptr;
+
+Union4BytesMixed returnUnion4BytesMixedCalculateResult() {
+  final resultPointer = calloc<Union4BytesMixed>();
+  final result = resultPointer.ref;
+
+  result.a0 = returnUnion4BytesMixed_a0;
+
+  returnUnion4BytesMixedResultPointer = resultPointer;
+
+  return result;
+}
+
+/// Returning a mixed integer/float union.
+Union4BytesMixed returnUnion4BytesMixed(int a0) {
+  print("returnUnion4BytesMixed(${a0})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0 == 42 || a0 == 84) {
+    print("throwing!");
+    throw Exception("ReturnUnion4BytesMixed throwing on purpose!");
+  }
+
+  returnUnion4BytesMixed_a0 = a0;
+
+  final result = returnUnion4BytesMixedCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnUnion4BytesMixedAfterCallback() {
+  calloc.free(returnUnion4BytesMixedResultPointer);
+
+  final result = returnUnion4BytesMixedCalculateResult();
+
+  print("after callback result = $result");
+
+  calloc.free(returnUnion4BytesMixedResultPointer);
+}
+
+typedef ReturnUnion8BytesNestedFloatType = Union8BytesNestedFloat Function(
+    Double);
+
+// Global variables to be able to test inputs after callback returned.
+double returnUnion8BytesNestedFloat_a0 = 0.0;
+
+// Result variable also global, so we can delete it after the callback.
+Pointer<Union8BytesNestedFloat> returnUnion8BytesNestedFloatResultPointer =
+    nullptr;
+
+Union8BytesNestedFloat returnUnion8BytesNestedFloatCalculateResult() {
+  final resultPointer = calloc<Union8BytesNestedFloat>();
+  final result = resultPointer.ref;
+
+  result.a0 = returnUnion8BytesNestedFloat_a0;
+
+  returnUnion8BytesNestedFloatResultPointer = resultPointer;
+
+  return result;
+}
+
+/// Returning a floating point only union.
+Union8BytesNestedFloat returnUnion8BytesNestedFloat(double a0) {
+  print("returnUnion8BytesNestedFloat(${a0})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0 == 42 || a0 == 84) {
+    print("throwing!");
+    throw Exception("ReturnUnion8BytesNestedFloat throwing on purpose!");
+  }
+
+  returnUnion8BytesNestedFloat_a0 = a0;
+
+  final result = returnUnion8BytesNestedFloatCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnUnion8BytesNestedFloatAfterCallback() {
+  calloc.free(returnUnion8BytesNestedFloatResultPointer);
+
+  final result = returnUnion8BytesNestedFloatCalculateResult();
+
+  print("after callback result = $result");
+
+  calloc.free(returnUnion8BytesNestedFloatResultPointer);
+}
+
+typedef ReturnUnion9BytesNestedIntType = Union9BytesNestedInt Function(
+    Struct8BytesInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct8BytesInt returnUnion9BytesNestedInt_a0 = Struct8BytesInt();
+
+// Result variable also global, so we can delete it after the callback.
+Pointer<Union9BytesNestedInt> returnUnion9BytesNestedIntResultPointer = nullptr;
+
+Union9BytesNestedInt returnUnion9BytesNestedIntCalculateResult() {
+  final resultPointer = calloc<Union9BytesNestedInt>();
+  final result = resultPointer.ref;
+
+  result.a0.a0 = returnUnion9BytesNestedInt_a0.a0;
+  result.a0.a1 = returnUnion9BytesNestedInt_a0.a1;
+  result.a0.a2 = returnUnion9BytesNestedInt_a0.a2;
+
+  returnUnion9BytesNestedIntResultPointer = resultPointer;
+
+  return result;
+}
+
+/// Returning a mixed-size union.
+Union9BytesNestedInt returnUnion9BytesNestedInt(Struct8BytesInt a0) {
+  print("returnUnion9BytesNestedInt(${a0})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0 == 42 || a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("ReturnUnion9BytesNestedInt throwing on purpose!");
+  }
+
+  returnUnion9BytesNestedInt_a0 = a0;
+
+  final result = returnUnion9BytesNestedIntCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnUnion9BytesNestedIntAfterCallback() {
+  calloc.free(returnUnion9BytesNestedIntResultPointer);
+
+  final result = returnUnion9BytesNestedIntCalculateResult();
+
+  print("after callback result = $result");
+
+  calloc.free(returnUnion9BytesNestedIntResultPointer);
+}
+
+typedef ReturnUnion16BytesNestedFloatType = Union16BytesNestedFloat Function(
+    Struct8BytesHomogeneousFloat);
+
+// Global variables to be able to test inputs after callback returned.
+Struct8BytesHomogeneousFloat returnUnion16BytesNestedFloat_a0 =
+    Struct8BytesHomogeneousFloat();
+
+// Result variable also global, so we can delete it after the callback.
+Pointer<Union16BytesNestedFloat> returnUnion16BytesNestedFloatResultPointer =
+    nullptr;
+
+Union16BytesNestedFloat returnUnion16BytesNestedFloatCalculateResult() {
+  final resultPointer = calloc<Union16BytesNestedFloat>();
+  final result = resultPointer.ref;
+
+  result.a0.a0 = returnUnion16BytesNestedFloat_a0.a0;
+  result.a0.a1 = returnUnion16BytesNestedFloat_a0.a1;
+
+  returnUnion16BytesNestedFloatResultPointer = resultPointer;
+
+  return result;
+}
+
+/// Returning union with homogenous floats.
+Union16BytesNestedFloat returnUnion16BytesNestedFloat(
+    Struct8BytesHomogeneousFloat a0) {
+  print("returnUnion16BytesNestedFloat(${a0})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0 == 42 || a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("ReturnUnion16BytesNestedFloat throwing on purpose!");
+  }
+
+  returnUnion16BytesNestedFloat_a0 = a0;
+
+  final result = returnUnion16BytesNestedFloatCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnUnion16BytesNestedFloatAfterCallback() {
+  calloc.free(returnUnion16BytesNestedFloatResultPointer);
+
+  final result = returnUnion16BytesNestedFloatCalculateResult();
+
+  print("after callback result = $result");
+
+  calloc.free(returnUnion16BytesNestedFloatResultPointer);
+}
+
 typedef ReturnStructArgumentStruct1ByteIntType = Struct1ByteInt Function(
     Struct1ByteInt);
 
diff --git a/tests/ffi/function_structs_by_value_generated_test.dart b/tests/ffi/function_structs_by_value_generated_test.dart
index d52c0d3..e830631 100644
--- a/tests/ffi/function_structs_by_value_generated_test.dart
+++ b/tests/ffi/function_structs_by_value_generated_test.dart
@@ -77,6 +77,11 @@
     testPassStructNestedAlignmentStruct5BytesPackedMixed();
     testPassStruct6BytesInlineArrayInt();
     testPassStruct15BytesInlineArrayMixed();
+    testPassUnion4BytesMixedx10();
+    testPassUnion8BytesNestedFloatx10();
+    testPassUnion9BytesNestedIntx10();
+    testPassUnion16BytesNestedInlineArrayFloatx10();
+    testPassUnion16BytesNestedFloatx10();
     testReturnStruct1ByteInt();
     testReturnStruct3BytesHomogeneousUint8();
     testReturnStruct3BytesInt2ByteAligned();
@@ -102,6 +107,10 @@
     testReturnStruct3BytesPackedInt();
     testReturnStruct8BytesPackedInt();
     testReturnStruct9BytesPackedMixed();
+    testReturnUnion4BytesMixed();
+    testReturnUnion8BytesNestedFloat();
+    testReturnUnion9BytesNestedInt();
+    testReturnUnion16BytesNestedFloat();
     testReturnStructArgumentStruct1ByteInt();
     testReturnStructArgumentInt32x8Struct1ByteInt();
     testReturnStructArgumentStruct8BytesHomogeneousFloat();
@@ -1283,6 +1292,52 @@
   String toString() => "(${[for (var i0 = 0; i0 < 3; i0 += 1) a0[i0]]})";
 }
 
+class Union4BytesMixed extends Union {
+  @Uint32()
+  external int a0;
+
+  @Float()
+  external double a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+class Union8BytesNestedFloat extends Union {
+  @Double()
+  external double a0;
+
+  external Struct8BytesHomogeneousFloat a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+class Union9BytesNestedInt extends Union {
+  external Struct8BytesInt a0;
+
+  external Struct9BytesHomogeneousUint8 a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+class Union16BytesNestedInlineArrayFloat extends Union {
+  @Array(4)
+  external Array<Float> a0;
+
+  external Struct16BytesHomogeneousFloat a1;
+
+  String toString() => "(${[for (var i0 = 0; i0 < 4; i0 += 1) a0[i0]]}, ${a1})";
+}
+
+class Union16BytesNestedFloat extends Union {
+  external Struct8BytesHomogeneousFloat a0;
+
+  external Struct12BytesHomogeneousFloat a1;
+
+  external Struct16BytesHomogeneousFloat a2;
+
+  String toString() => "(${a0}, ${a1}, ${a2})";
+}
+
 final passStruct1ByteIntx10 = ffiTestFunctions.lookupFunction<
     Int64 Function(
         Struct1ByteInt,
@@ -5916,6 +5971,453 @@
   calloc.free(a0Pointer);
 }
 
+final passUnion4BytesMixedx10 = ffiTestFunctions.lookupFunction<
+    Double Function(
+        Union4BytesMixed,
+        Union4BytesMixed,
+        Union4BytesMixed,
+        Union4BytesMixed,
+        Union4BytesMixed,
+        Union4BytesMixed,
+        Union4BytesMixed,
+        Union4BytesMixed,
+        Union4BytesMixed,
+        Union4BytesMixed),
+    double Function(
+        Union4BytesMixed,
+        Union4BytesMixed,
+        Union4BytesMixed,
+        Union4BytesMixed,
+        Union4BytesMixed,
+        Union4BytesMixed,
+        Union4BytesMixed,
+        Union4BytesMixed,
+        Union4BytesMixed,
+        Union4BytesMixed)>("PassUnion4BytesMixedx10");
+
+/// Check placement of mixed integer/float union.
+void testPassUnion4BytesMixedx10() {
+  final a0Pointer = calloc<Union4BytesMixed>();
+  final Union4BytesMixed a0 = a0Pointer.ref;
+  final a1Pointer = calloc<Union4BytesMixed>();
+  final Union4BytesMixed a1 = a1Pointer.ref;
+  final a2Pointer = calloc<Union4BytesMixed>();
+  final Union4BytesMixed a2 = a2Pointer.ref;
+  final a3Pointer = calloc<Union4BytesMixed>();
+  final Union4BytesMixed a3 = a3Pointer.ref;
+  final a4Pointer = calloc<Union4BytesMixed>();
+  final Union4BytesMixed a4 = a4Pointer.ref;
+  final a5Pointer = calloc<Union4BytesMixed>();
+  final Union4BytesMixed a5 = a5Pointer.ref;
+  final a6Pointer = calloc<Union4BytesMixed>();
+  final Union4BytesMixed a6 = a6Pointer.ref;
+  final a7Pointer = calloc<Union4BytesMixed>();
+  final Union4BytesMixed a7 = a7Pointer.ref;
+  final a8Pointer = calloc<Union4BytesMixed>();
+  final Union4BytesMixed a8 = a8Pointer.ref;
+  final a9Pointer = calloc<Union4BytesMixed>();
+  final Union4BytesMixed a9 = a9Pointer.ref;
+
+  a0.a0 = 1;
+  a1.a0 = 2;
+  a2.a0 = 3;
+  a3.a0 = 4;
+  a4.a0 = 5;
+  a5.a0 = 6;
+  a6.a0 = 7;
+  a7.a0 = 8;
+  a8.a0 = 9;
+  a9.a0 = 10;
+
+  final result =
+      passUnion4BytesMixedx10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  print("result = $result");
+
+  Expect.approxEquals(55.0, result);
+
+  calloc.free(a0Pointer);
+  calloc.free(a1Pointer);
+  calloc.free(a2Pointer);
+  calloc.free(a3Pointer);
+  calloc.free(a4Pointer);
+  calloc.free(a5Pointer);
+  calloc.free(a6Pointer);
+  calloc.free(a7Pointer);
+  calloc.free(a8Pointer);
+  calloc.free(a9Pointer);
+}
+
+final passUnion8BytesNestedFloatx10 = ffiTestFunctions.lookupFunction<
+    Double Function(
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat),
+    double Function(
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat)>("PassUnion8BytesNestedFloatx10");
+
+/// Check placement of mixed floats union.
+void testPassUnion8BytesNestedFloatx10() {
+  final a0Pointer = calloc<Union8BytesNestedFloat>();
+  final Union8BytesNestedFloat a0 = a0Pointer.ref;
+  final a1Pointer = calloc<Union8BytesNestedFloat>();
+  final Union8BytesNestedFloat a1 = a1Pointer.ref;
+  final a2Pointer = calloc<Union8BytesNestedFloat>();
+  final Union8BytesNestedFloat a2 = a2Pointer.ref;
+  final a3Pointer = calloc<Union8BytesNestedFloat>();
+  final Union8BytesNestedFloat a3 = a3Pointer.ref;
+  final a4Pointer = calloc<Union8BytesNestedFloat>();
+  final Union8BytesNestedFloat a4 = a4Pointer.ref;
+  final a5Pointer = calloc<Union8BytesNestedFloat>();
+  final Union8BytesNestedFloat a5 = a5Pointer.ref;
+  final a6Pointer = calloc<Union8BytesNestedFloat>();
+  final Union8BytesNestedFloat a6 = a6Pointer.ref;
+  final a7Pointer = calloc<Union8BytesNestedFloat>();
+  final Union8BytesNestedFloat a7 = a7Pointer.ref;
+  final a8Pointer = calloc<Union8BytesNestedFloat>();
+  final Union8BytesNestedFloat a8 = a8Pointer.ref;
+  final a9Pointer = calloc<Union8BytesNestedFloat>();
+  final Union8BytesNestedFloat a9 = a9Pointer.ref;
+
+  a0.a0 = -1.0;
+  a1.a0 = 2.0;
+  a2.a0 = -3.0;
+  a3.a0 = 4.0;
+  a4.a0 = -5.0;
+  a5.a0 = 6.0;
+  a6.a0 = -7.0;
+  a7.a0 = 8.0;
+  a8.a0 = -9.0;
+  a9.a0 = 10.0;
+
+  final result =
+      passUnion8BytesNestedFloatx10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  print("result = $result");
+
+  Expect.approxEquals(5.0, result);
+
+  calloc.free(a0Pointer);
+  calloc.free(a1Pointer);
+  calloc.free(a2Pointer);
+  calloc.free(a3Pointer);
+  calloc.free(a4Pointer);
+  calloc.free(a5Pointer);
+  calloc.free(a6Pointer);
+  calloc.free(a7Pointer);
+  calloc.free(a8Pointer);
+  calloc.free(a9Pointer);
+}
+
+final passUnion9BytesNestedIntx10 = ffiTestFunctions.lookupFunction<
+    Double Function(
+        Union9BytesNestedInt,
+        Union9BytesNestedInt,
+        Union9BytesNestedInt,
+        Union9BytesNestedInt,
+        Union9BytesNestedInt,
+        Union9BytesNestedInt,
+        Union9BytesNestedInt,
+        Union9BytesNestedInt,
+        Union9BytesNestedInt,
+        Union9BytesNestedInt),
+    double Function(
+        Union9BytesNestedInt,
+        Union9BytesNestedInt,
+        Union9BytesNestedInt,
+        Union9BytesNestedInt,
+        Union9BytesNestedInt,
+        Union9BytesNestedInt,
+        Union9BytesNestedInt,
+        Union9BytesNestedInt,
+        Union9BytesNestedInt,
+        Union9BytesNestedInt)>("PassUnion9BytesNestedIntx10");
+
+/// Mixed-size union argument.
+void testPassUnion9BytesNestedIntx10() {
+  final a0Pointer = calloc<Union9BytesNestedInt>();
+  final Union9BytesNestedInt a0 = a0Pointer.ref;
+  final a1Pointer = calloc<Union9BytesNestedInt>();
+  final Union9BytesNestedInt a1 = a1Pointer.ref;
+  final a2Pointer = calloc<Union9BytesNestedInt>();
+  final Union9BytesNestedInt a2 = a2Pointer.ref;
+  final a3Pointer = calloc<Union9BytesNestedInt>();
+  final Union9BytesNestedInt a3 = a3Pointer.ref;
+  final a4Pointer = calloc<Union9BytesNestedInt>();
+  final Union9BytesNestedInt a4 = a4Pointer.ref;
+  final a5Pointer = calloc<Union9BytesNestedInt>();
+  final Union9BytesNestedInt a5 = a5Pointer.ref;
+  final a6Pointer = calloc<Union9BytesNestedInt>();
+  final Union9BytesNestedInt a6 = a6Pointer.ref;
+  final a7Pointer = calloc<Union9BytesNestedInt>();
+  final Union9BytesNestedInt a7 = a7Pointer.ref;
+  final a8Pointer = calloc<Union9BytesNestedInt>();
+  final Union9BytesNestedInt a8 = a8Pointer.ref;
+  final a9Pointer = calloc<Union9BytesNestedInt>();
+  final Union9BytesNestedInt a9 = a9Pointer.ref;
+
+  a0.a0.a0 = -1;
+  a0.a0.a1 = 2;
+  a0.a0.a2 = -3;
+  a1.a0.a0 = 4;
+  a1.a0.a1 = -5;
+  a1.a0.a2 = 6;
+  a2.a0.a0 = -7;
+  a2.a0.a1 = 8;
+  a2.a0.a2 = -9;
+  a3.a0.a0 = 10;
+  a3.a0.a1 = -11;
+  a3.a0.a2 = 12;
+  a4.a0.a0 = -13;
+  a4.a0.a1 = 14;
+  a4.a0.a2 = -15;
+  a5.a0.a0 = 16;
+  a5.a0.a1 = -17;
+  a5.a0.a2 = 18;
+  a6.a0.a0 = -19;
+  a6.a0.a1 = 20;
+  a6.a0.a2 = -21;
+  a7.a0.a0 = 22;
+  a7.a0.a1 = -23;
+  a7.a0.a2 = 24;
+  a8.a0.a0 = -25;
+  a8.a0.a1 = 26;
+  a8.a0.a2 = -27;
+  a9.a0.a0 = 28;
+  a9.a0.a1 = -29;
+  a9.a0.a2 = 30;
+
+  final result =
+      passUnion9BytesNestedIntx10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  print("result = $result");
+
+  Expect.approxEquals(15.0, result);
+
+  calloc.free(a0Pointer);
+  calloc.free(a1Pointer);
+  calloc.free(a2Pointer);
+  calloc.free(a3Pointer);
+  calloc.free(a4Pointer);
+  calloc.free(a5Pointer);
+  calloc.free(a6Pointer);
+  calloc.free(a7Pointer);
+  calloc.free(a8Pointer);
+  calloc.free(a9Pointer);
+}
+
+final passUnion16BytesNestedInlineArrayFloatx10 =
+    ffiTestFunctions.lookupFunction<
+            Double Function(
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat),
+            double Function(
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat)>(
+        "PassUnion16BytesNestedInlineArrayFloatx10");
+
+/// Union with homogenous floats.
+void testPassUnion16BytesNestedInlineArrayFloatx10() {
+  final a0Pointer = calloc<Union16BytesNestedInlineArrayFloat>();
+  final Union16BytesNestedInlineArrayFloat a0 = a0Pointer.ref;
+  final a1Pointer = calloc<Union16BytesNestedInlineArrayFloat>();
+  final Union16BytesNestedInlineArrayFloat a1 = a1Pointer.ref;
+  final a2Pointer = calloc<Union16BytesNestedInlineArrayFloat>();
+  final Union16BytesNestedInlineArrayFloat a2 = a2Pointer.ref;
+  final a3Pointer = calloc<Union16BytesNestedInlineArrayFloat>();
+  final Union16BytesNestedInlineArrayFloat a3 = a3Pointer.ref;
+  final a4Pointer = calloc<Union16BytesNestedInlineArrayFloat>();
+  final Union16BytesNestedInlineArrayFloat a4 = a4Pointer.ref;
+  final a5Pointer = calloc<Union16BytesNestedInlineArrayFloat>();
+  final Union16BytesNestedInlineArrayFloat a5 = a5Pointer.ref;
+  final a6Pointer = calloc<Union16BytesNestedInlineArrayFloat>();
+  final Union16BytesNestedInlineArrayFloat a6 = a6Pointer.ref;
+  final a7Pointer = calloc<Union16BytesNestedInlineArrayFloat>();
+  final Union16BytesNestedInlineArrayFloat a7 = a7Pointer.ref;
+  final a8Pointer = calloc<Union16BytesNestedInlineArrayFloat>();
+  final Union16BytesNestedInlineArrayFloat a8 = a8Pointer.ref;
+  final a9Pointer = calloc<Union16BytesNestedInlineArrayFloat>();
+  final Union16BytesNestedInlineArrayFloat a9 = a9Pointer.ref;
+
+  a0.a0[0] = -1.0;
+  a0.a0[1] = 2.0;
+  a0.a0[2] = -3.0;
+  a0.a0[3] = 4.0;
+  a1.a0[0] = -5.0;
+  a1.a0[1] = 6.0;
+  a1.a0[2] = -7.0;
+  a1.a0[3] = 8.0;
+  a2.a0[0] = -9.0;
+  a2.a0[1] = 10.0;
+  a2.a0[2] = -11.0;
+  a2.a0[3] = 12.0;
+  a3.a0[0] = -13.0;
+  a3.a0[1] = 14.0;
+  a3.a0[2] = -15.0;
+  a3.a0[3] = 16.0;
+  a4.a0[0] = -17.0;
+  a4.a0[1] = 18.0;
+  a4.a0[2] = -19.0;
+  a4.a0[3] = 20.0;
+  a5.a0[0] = -21.0;
+  a5.a0[1] = 22.0;
+  a5.a0[2] = -23.0;
+  a5.a0[3] = 24.0;
+  a6.a0[0] = -25.0;
+  a6.a0[1] = 26.0;
+  a6.a0[2] = -27.0;
+  a6.a0[3] = 28.0;
+  a7.a0[0] = -29.0;
+  a7.a0[1] = 30.0;
+  a7.a0[2] = -31.0;
+  a7.a0[3] = 32.0;
+  a8.a0[0] = -33.0;
+  a8.a0[1] = 34.0;
+  a8.a0[2] = -35.0;
+  a8.a0[3] = 36.0;
+  a9.a0[0] = -37.0;
+  a9.a0[1] = 38.0;
+  a9.a0[2] = -39.0;
+  a9.a0[3] = 40.0;
+
+  final result = passUnion16BytesNestedInlineArrayFloatx10(
+      a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  print("result = $result");
+
+  Expect.approxEquals(20.0, result);
+
+  calloc.free(a0Pointer);
+  calloc.free(a1Pointer);
+  calloc.free(a2Pointer);
+  calloc.free(a3Pointer);
+  calloc.free(a4Pointer);
+  calloc.free(a5Pointer);
+  calloc.free(a6Pointer);
+  calloc.free(a7Pointer);
+  calloc.free(a8Pointer);
+  calloc.free(a9Pointer);
+}
+
+final passUnion16BytesNestedFloatx10 = ffiTestFunctions.lookupFunction<
+    Double Function(
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat),
+    double Function(
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat)>("PassUnion16BytesNestedFloatx10");
+
+/// Union with homogenous floats.
+void testPassUnion16BytesNestedFloatx10() {
+  final a0Pointer = calloc<Union16BytesNestedFloat>();
+  final Union16BytesNestedFloat a0 = a0Pointer.ref;
+  final a1Pointer = calloc<Union16BytesNestedFloat>();
+  final Union16BytesNestedFloat a1 = a1Pointer.ref;
+  final a2Pointer = calloc<Union16BytesNestedFloat>();
+  final Union16BytesNestedFloat a2 = a2Pointer.ref;
+  final a3Pointer = calloc<Union16BytesNestedFloat>();
+  final Union16BytesNestedFloat a3 = a3Pointer.ref;
+  final a4Pointer = calloc<Union16BytesNestedFloat>();
+  final Union16BytesNestedFloat a4 = a4Pointer.ref;
+  final a5Pointer = calloc<Union16BytesNestedFloat>();
+  final Union16BytesNestedFloat a5 = a5Pointer.ref;
+  final a6Pointer = calloc<Union16BytesNestedFloat>();
+  final Union16BytesNestedFloat a6 = a6Pointer.ref;
+  final a7Pointer = calloc<Union16BytesNestedFloat>();
+  final Union16BytesNestedFloat a7 = a7Pointer.ref;
+  final a8Pointer = calloc<Union16BytesNestedFloat>();
+  final Union16BytesNestedFloat a8 = a8Pointer.ref;
+  final a9Pointer = calloc<Union16BytesNestedFloat>();
+  final Union16BytesNestedFloat a9 = a9Pointer.ref;
+
+  a0.a0.a0 = -1.0;
+  a0.a0.a1 = 2.0;
+  a1.a0.a0 = -3.0;
+  a1.a0.a1 = 4.0;
+  a2.a0.a0 = -5.0;
+  a2.a0.a1 = 6.0;
+  a3.a0.a0 = -7.0;
+  a3.a0.a1 = 8.0;
+  a4.a0.a0 = -9.0;
+  a4.a0.a1 = 10.0;
+  a5.a0.a0 = -11.0;
+  a5.a0.a1 = 12.0;
+  a6.a0.a0 = -13.0;
+  a6.a0.a1 = 14.0;
+  a7.a0.a0 = -15.0;
+  a7.a0.a1 = 16.0;
+  a8.a0.a0 = -17.0;
+  a8.a0.a1 = 18.0;
+  a9.a0.a0 = -19.0;
+  a9.a0.a1 = 20.0;
+
+  final result =
+      passUnion16BytesNestedFloatx10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  print("result = $result");
+
+  Expect.approxEquals(10.0, result);
+
+  calloc.free(a0Pointer);
+  calloc.free(a1Pointer);
+  calloc.free(a2Pointer);
+  calloc.free(a3Pointer);
+  calloc.free(a4Pointer);
+  calloc.free(a5Pointer);
+  calloc.free(a6Pointer);
+  calloc.free(a7Pointer);
+  calloc.free(a8Pointer);
+  calloc.free(a9Pointer);
+}
+
 final returnStruct1ByteInt = ffiTestFunctions.lookupFunction<
     Struct1ByteInt Function(Int8),
     Struct1ByteInt Function(int)>("ReturnStruct1ByteInt");
@@ -7406,6 +7908,88 @@
   Expect.approxEquals(a1, result.a1);
 }
 
+final returnUnion4BytesMixed = ffiTestFunctions.lookupFunction<
+    Union4BytesMixed Function(Uint32),
+    Union4BytesMixed Function(int)>("ReturnUnion4BytesMixed");
+
+/// Returning a mixed integer/float union.
+void testReturnUnion4BytesMixed() {
+  int a0;
+
+  a0 = 1;
+
+  final result = returnUnion4BytesMixed(a0);
+
+  print("result = $result");
+
+  Expect.equals(a0, result.a0);
+}
+
+final returnUnion8BytesNestedFloat = ffiTestFunctions.lookupFunction<
+    Union8BytesNestedFloat Function(Double),
+    Union8BytesNestedFloat Function(double)>("ReturnUnion8BytesNestedFloat");
+
+/// Returning a floating point only union.
+void testReturnUnion8BytesNestedFloat() {
+  double a0;
+
+  a0 = -1.0;
+
+  final result = returnUnion8BytesNestedFloat(a0);
+
+  print("result = $result");
+
+  Expect.approxEquals(a0, result.a0);
+}
+
+final returnUnion9BytesNestedInt = ffiTestFunctions.lookupFunction<
+    Union9BytesNestedInt Function(Struct8BytesInt),
+    Union9BytesNestedInt Function(
+        Struct8BytesInt)>("ReturnUnion9BytesNestedInt");
+
+/// Returning a mixed-size union.
+void testReturnUnion9BytesNestedInt() {
+  final a0Pointer = calloc<Struct8BytesInt>();
+  final Struct8BytesInt a0 = a0Pointer.ref;
+
+  a0.a0 = -1;
+  a0.a1 = 2;
+  a0.a2 = -3;
+
+  final result = returnUnion9BytesNestedInt(a0);
+
+  print("result = $result");
+
+  Expect.equals(a0.a0, result.a0.a0);
+  Expect.equals(a0.a1, result.a0.a1);
+  Expect.equals(a0.a2, result.a0.a2);
+
+  calloc.free(a0Pointer);
+}
+
+final returnUnion16BytesNestedFloat = ffiTestFunctions.lookupFunction<
+    Union16BytesNestedFloat Function(Struct8BytesHomogeneousFloat),
+    Union16BytesNestedFloat Function(
+        Struct8BytesHomogeneousFloat)>("ReturnUnion16BytesNestedFloat");
+
+/// Returning union with homogenous floats.
+void testReturnUnion16BytesNestedFloat() {
+  final a0Pointer = calloc<Struct8BytesHomogeneousFloat>();
+  final Struct8BytesHomogeneousFloat a0 = a0Pointer.ref;
+
+  a0.a0 = -1.0;
+  a0.a1 = 2.0;
+
+  final result = returnUnion16BytesNestedFloat(a0);
+
+  print("result = $result");
+
+  Expect.approxEquals(a0.a0, result.a0.a0);
+  Expect.approxEquals(a0.a1, result.a0.a1);
+
+  calloc.free(a0Pointer);
+}
+
 final returnStructArgumentStruct1ByteInt = ffiTestFunctions.lookupFunction<
     Struct1ByteInt Function(Struct1ByteInt),
     Struct1ByteInt Function(
diff --git a/tests/ffi/generator/c_types.dart b/tests/ffi/generator/c_types.dart
index df3b44b..7236aed 100644
--- a/tests/ffi/generator/c_types.dart
+++ b/tests/ffi/generator/c_types.dart
@@ -2,6 +2,8 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+import 'dart:math' as math;
+
 import 'utils.dart';
 
 const int8 = FundamentalType(PrimitiveType.int8);
@@ -152,36 +154,70 @@
   return result;
 }
 
-class StructType extends CType {
+abstract class CompositeType extends CType {
   final List<Member> members;
 
-  final int? packing;
-
   /// To disambiguate same size structs.
   final String suffix;
 
   /// To override names.
   final String overrideName;
 
-  StructType(List<CType> memberTypes, {int? this.packing})
+  CompositeType(List<CType> memberTypes)
       : this.members = generateMemberNames(memberTypes),
         this.suffix = "",
         this.overrideName = "";
-  StructType.disambiguate(List<CType> memberTypes, this.suffix,
-      {int? this.packing})
+  CompositeType.disambiguate(List<CType> memberTypes, this.suffix)
       : this.members = generateMemberNames(memberTypes),
         this.overrideName = "";
-  StructType.override(List<CType> memberTypes, this.overrideName,
-      {int? this.packing})
+  CompositeType.override(List<CType> memberTypes, this.overrideName)
       : this.members = generateMemberNames(memberTypes),
         this.suffix = "";
 
   List<CType> get memberTypes => members.map((a) => a.type).toList();
 
+  String get name;
+
   String get cType => name;
   String get dartCType => name;
   String get dartType => name;
   String get dartStructFieldAnnotation => "";
+  String get cKeyword;
+  String get dartSuperClass;
+
+  bool get isOnlyFloatingPoint =>
+      !memberTypes.map((e) => e.isOnlyFloatingPoint).contains(false);
+  bool get isOnlyInteger =>
+      !memberTypes.map((e) => e.isOnlyInteger).contains(false);
+
+  bool get isMixed => !isOnlyInteger && !isOnlyFloatingPoint;
+
+  bool get hasNestedStructs =>
+      members.map((e) => e.type is StructType).contains(true);
+
+  bool get hasInlineArrays =>
+      members.map((e) => e.type is FixedLengthArrayType).contains(true);
+
+  bool get hasMultiDimensionalInlineArrays => members
+      .map((e) => e.type)
+      .whereType<FixedLengthArrayType>()
+      .where((e) => e.isMulti)
+      .isNotEmpty;
+}
+
+class StructType extends CompositeType {
+  final int? packing;
+
+  StructType(List<CType> memberTypes, {int? this.packing}) : super(memberTypes);
+  StructType.disambiguate(List<CType> memberTypes, String suffix,
+      {int? this.packing})
+      : super.disambiguate(memberTypes, suffix);
+  StructType.override(List<CType> memberTypes, String overrideName,
+      {int? this.packing})
+      : super.override(memberTypes, overrideName);
+
+  String get cKeyword => "struct";
+  String get dartSuperClass => "Struct";
 
   bool get hasSize =>
       !memberTypes.map((e) => e.hasSize).contains(false) && !hasPadding;
@@ -201,30 +237,11 @@
     return members[0].type.size < members[1].type.size;
   }
 
-  bool get hasNestedStructs =>
-      members.map((e) => e.type is StructType).contains(true);
-
-  bool get hasInlineArrays =>
-      members.map((e) => e.type is FixedLengthArrayType).contains(true);
-
-  bool get hasMultiDimensionalInlineArrays => members
-      .map((e) => e.type)
-      .whereType<FixedLengthArrayType>()
-      .where((e) => e.isMulti)
-      .isNotEmpty;
-
   /// All members have the same type.
   bool get isHomogeneous => memberTypes.toSet().length == 1;
 
-  bool get isOnlyFloatingPoint =>
-      !memberTypes.map((e) => e.isOnlyFloatingPoint).contains(false);
-  bool get isOnlyInteger =>
-      !memberTypes.map((e) => e.isOnlyInteger).contains(false);
-
-  bool get isMixed => !isOnlyInteger && !isOnlyFloatingPoint;
-
   String get name {
-    String result = "Struct";
+    String result = dartSuperClass;
     if (overrideName != "") {
       return result + overrideName;
     }
@@ -264,6 +281,46 @@
   }
 }
 
+class UnionType extends CompositeType {
+  UnionType(List<CType> memberTypes) : super(memberTypes);
+
+  String get cKeyword => "union";
+  String get dartSuperClass => "Union";
+
+  bool get hasSize => !memberTypes.map((e) => e.hasSize).contains(false);
+  int get size => memberTypes.fold(0, (int acc, e) => math.max(acc, e.size));
+
+  String get name {
+    String result = dartSuperClass;
+    if (overrideName != "") {
+      return result + overrideName;
+    }
+    if (hasSize) {
+      result += "${size}Byte" + (size != 1 ? "s" : "");
+    }
+    if (hasNestedStructs) {
+      result += "Nested";
+    }
+    if (hasInlineArrays) {
+      result += "InlineArray";
+      if (hasMultiDimensionalInlineArrays) {
+        result += "MultiDimensional";
+      }
+    }
+    if (members.length == 0) {
+      // No suffix.
+    } else if (isOnlyFloatingPoint) {
+      result += "Float";
+    } else if (isOnlyInteger) {
+      result += "Int";
+    } else {
+      result += "Mixed";
+    }
+    result += suffix;
+    return result;
+  }
+}
+
 class FixedLengthArrayType extends CType {
   final CType elementType;
   final int length;
@@ -360,15 +417,20 @@
   /// A suitable name based on the signature.
   String get cName {
     String result = "";
-    if (arguments.containsStructs && returnValue is FundamentalType) {
+    if (arguments.containsComposites && returnValue is FundamentalType) {
       result = "Pass";
     } else if (returnValue is StructType &&
         argumentTypes.contains(returnValue)) {
       result = "ReturnStructArgument";
+    } else if (returnValue is UnionType &&
+        argumentTypes.contains(returnValue)) {
+      result = "ReturnUnionArgument";
     } else if (returnValue is StructType) {
       if (arguments.length == (returnValue as StructType).members.length) {
         return "Return${returnValue.dartCType}";
       }
+    } else if (returnValue is UnionType && arguments.length == 1) {
+      return "Return${returnValue.dartCType}";
     } else {
       result = "Uncategorized";
     }
@@ -392,5 +454,6 @@
 }
 
 extension MemberList on List<Member> {
-  bool get containsStructs => map((m) => m.type is StructType).contains(true);
+  bool get containsComposites =>
+      map((m) => m.type is CompositeType).contains(true);
 }
diff --git a/tests/ffi/generator/structs_by_value_tests_configuration.dart b/tests/ffi/generator/structs_by_value_tests_configuration.dart
index 1ad3c11..07b5fd1 100644
--- a/tests/ffi/generator/structs_by_value_tests_configuration.dart
+++ b/tests/ffi/generator/structs_by_value_tests_configuration.dart
@@ -5,6 +5,12 @@
 import 'c_types.dart';
 
 final functions = [
+  ...functionsStructArguments,
+  ...functionsStructReturn,
+  ...functionsReturnArgument,
+];
+
+final functionsStructArguments = [
   FunctionType(List.filled(10, struct1byteInt), int64, """
 Smallest struct with data.
 10 struct arguments will exhaust available registers."""),
@@ -358,6 +364,19 @@
       double_,
       """
 Check alignment of packed struct array in non-packed struct."""),
+  FunctionType(List.filled(10, union4bytesMixed), double_, """
+Check placement of mixed integer/float union."""),
+  FunctionType(List.filled(10, union8bytesFloat), double_, """
+Check placement of mixed floats union."""),
+  FunctionType(List.filled(10, union12bytesInt), double_, """
+Mixed-size union argument."""),
+  FunctionType(List.filled(10, union16bytesFloat), double_, """
+Union with homogenous floats."""),
+  FunctionType(List.filled(10, union16bytesFloat2), double_, """
+Union with homogenous floats."""),
+];
+
+final functionsStructReturn = [
   FunctionType(struct1byteInt.memberTypes, struct1byteInt, """
 Smallest struct with data."""),
   FunctionType(struct3bytesInt.memberTypes, struct3bytesInt, """
@@ -421,6 +440,29 @@
 Struct with mis-aligned member.
 Tests backfilling of CPU and FPU registers."""),
   FunctionType(
+      [union4bytesMixed.memberTypes.first],
+      union4bytesMixed,
+      """
+Returning a mixed integer/float union."""),
+  FunctionType(
+      [union8bytesFloat.memberTypes.first],
+      union8bytesFloat,
+      """
+Returning a floating point only union."""),
+  FunctionType(
+      [union12bytesInt.memberTypes.first],
+      union12bytesInt,
+      """
+Returning a mixed-size union."""),
+  FunctionType(
+      [union16bytesFloat2.memberTypes.first],
+      union16bytesFloat2,
+      """
+Returning union with homogenous floats."""),
+];
+
+final functionsReturnArgument = [
+  FunctionType(
       [struct1byteInt],
       struct1byteInt,
       """
@@ -515,7 +557,7 @@
 Return big irregular struct as smoke test."""),
 ];
 
-final structs = [
+final compounds = [
   struct1byteInt,
   struct3bytesInt,
   struct3bytesInt2,
@@ -574,6 +616,11 @@
   struct8bytesPacked,
   struct9bytesPacked,
   struct15bytesPacked,
+  union4bytesMixed,
+  union8bytesFloat,
+  union12bytesInt,
+  union16bytesFloat,
+  union16bytesFloat2,
 ];
 
 final struct1byteInt = StructType([int8]);
@@ -741,3 +788,23 @@
 /// inline array, but not in the subsequent ones.
 final struct15bytesPacked =
     StructType([FixedLengthArrayType(struct5bytesPacked, 3)]);
+
+/// Mixed integer and float. Tests whether calling conventions put this in
+/// integer registers or not.
+final union4bytesMixed = UnionType([uint32, float]);
+
+/// Different types of float. Tests whether calling conventions put this in
+/// FPU registers or not.
+final union8bytesFloat = UnionType([double_, struct8bytesFloat]);
+
+/// This union has a size of 12, because of the 4-byte alignment of the first
+/// member.
+final union12bytesInt = UnionType([struct8bytesInt, struct9bytesInt]);
+
+/// This union has homogenous floats of the same sizes.
+final union16bytesFloat =
+    UnionType([FixedLengthArrayType(float, 4), struct16bytesFloat]);
+
+/// This union has homogenous floats of different sizes.
+final union16bytesFloat2 =
+    UnionType([struct8bytesFloat, struct12bytesFloat, struct16bytesFloat]);
diff --git a/tests/ffi/generator/structs_by_value_tests_generator.dart b/tests/ffi/generator/structs_by_value_tests_generator.dart
index c66dbea..0ab7739 100644
--- a/tests/ffi/generator/structs_by_value_tests_generator.dart
+++ b/tests/ffi/generator/structs_by_value_tests_generator.dart
@@ -24,14 +24,19 @@
 
 extension on FunctionType {
   TestType get testType {
-    if (arguments.containsStructs && returnValue is FundamentalType) {
+    if (arguments.containsComposites && returnValue is FundamentalType) {
       return TestType.structArguments;
     }
-    if (returnValue is StructType && argumentTypes.contains(returnValue)) {
+    if (returnValue is CompositeType && argumentTypes.contains(returnValue)) {
       return TestType.structReturnArgument;
     }
     if (returnValue is StructType) {
-      if (arguments.length == (returnValue as StructType).members.length) {
+      if (arguments.length == (returnValue as CompositeType).members.length) {
+        return TestType.structReturn;
+      }
+    }
+    if (returnValue is UnionType) {
+      if (arguments.length == 1) {
         return TestType.structReturn;
       }
     }
@@ -52,7 +57,8 @@
         return "<< $variableName";
 
       case StructType:
-        final this_ = this as StructType;
+      case UnionType:
+        final this_ = this as CompositeType;
         return this_.members.coutExpression("$variableName.");
 
       case FixedLengthArrayType:
@@ -102,6 +108,12 @@
         final this_ = this as StructType;
         return this_.members.addToResultStatements("$variableName.");
 
+      case UnionType:
+        final this_ = this as UnionType;
+        final member = this_.members.first;
+        return member.type
+            .addToResultStatements("$variableName.${member.name}");
+
       case FixedLengthArrayType:
         final this_ = this as FixedLengthArrayType;
         final indices = [for (var i = 0; i < this_.length; i += 1) i];
@@ -139,6 +151,12 @@
         final this_ = this as StructType;
         return this_.members.assignValueStatements(a, "$variableName.");
 
+      case UnionType:
+        final this_ = this as UnionType;
+        final member = this_.members.first;
+        return member.type
+            .assignValueStatements(a, "$variableName.${member.name}");
+
       case FixedLengthArrayType:
         final this_ = this as FixedLengthArrayType;
         final indices = [for (var i = 0; i < this_.length; i += 1) i];
@@ -224,6 +242,7 @@
         return "${dartType} ${variableName};\n";
 
       case StructType:
+      case UnionType:
         return """
 final ${variableName}Pointer = calloc<$dartType>();
 final ${dartType} ${variableName} = ${variableName}Pointer.ref;
@@ -245,6 +264,7 @@
         return "${dartType} ${variableName} = 0.0;\n";
 
       case StructType:
+      case UnionType:
         if (structsAsPointers) {
           return "Pointer<${dartType}> ${variableName}Pointer = nullptr;\n";
         } else {
@@ -278,6 +298,7 @@
         return "";
 
       case StructType:
+      case UnionType:
         return "calloc.free(${variableName}Pointer);\n";
     }
 
@@ -298,6 +319,7 @@
     switch (this.runtimeType) {
       case FundamentalType:
       case StructType:
+      case UnionType:
         return "${cType} ${variableName};\n";
     }
 
@@ -432,7 +454,8 @@
         return variableName;
 
       case StructType:
-        final this_ = this as StructType;
+      case UnionType:
+        final this_ = this as CompositeType;
         return this_.members.firstArgumentName("$variableName.");
 
       case FixedLengthArrayType:
@@ -453,9 +476,12 @@
   }
 }
 
-extension on StructType {
+extension on CompositeType {
   String dartClass(bool nnbd) {
-    final packingAnnotation = hasPacking ? "@Packed(${packing})" : "";
+    final self = this;
+    final packingAnnotation = (self is StructType) && self.hasPacking
+        ? "@Packed(${self.packing})"
+        : "";
     String dartFields = "";
     for (final member in members) {
       dartFields += "${member.dartStructField(nnbd)}\n\n";
@@ -479,7 +505,7 @@
     }).join(", ");
     return """
     $packingAnnotation
-    class $name extends Struct {
+    class $name extends $dartSuperClass {
       $dartFields
 
       String toString() => "($toStringBody)";
@@ -488,16 +514,20 @@
   }
 
   String get cDefinition {
-    final packingPragmaPush =
-        hasPacking ? "#pragma pack(push, ${packing})" : "";
-    final packingPragmaPop = hasPacking ? "#pragma pack(pop)" : "";
+    final self = this;
+    final packingPragmaPush = (self is StructType) && self.hasPacking
+        ? "#pragma pack(push, ${self.packing})"
+        : "";
+    final packingPragmaPop =
+        (self is StructType) && self.hasPacking ? "#pragma pack(pop)" : "";
+
     String cFields = "";
     for (final member in members) {
       cFields += "  ${member.cStructField}\n";
     }
     return """
     $packingPragmaPush
-    struct $name {
+    $cKeyword $name {
       $cFields
     };
     $packingPragmaPop
@@ -727,7 +757,7 @@
         arguments.map((e) => "${e.type.cType} ${e.name}").join(", ");
 
     return """
-    // Used for testing structs by value.
+    // Used for testing structs and unions by value.
     ${reason.makeCComment()}
     DART_EXPORT ${returnValue.cType} $cName($argumentss) {
       std::cout << \"$cName\" ${arguments.coutExpression()} << \"\\n\";
@@ -779,7 +809,7 @@
     }
 
     return """
-    // Used for testing structs by value.
+    // Used for testing structs and unions by value.
     ${reason.makeCComment()}
     DART_EXPORT intptr_t
     Test$cName(
@@ -859,7 +889,7 @@
       }
     }
     """);
-    buffer.writeAll(structs.map((e) => e.dartClass(nnbd)));
+    buffer.writeAll(compounds.map((e) => e.dartClass(nnbd)));
     buffer.writeAll(functions.map((e) => e.dartCallCode));
 
     final path = callTestPath(nnbd);
@@ -984,7 +1014,7 @@
   final StringBuffer buffer = StringBuffer();
   buffer.write(headerC);
 
-  buffer.writeAll(structs.map((e) => e.cDefinition));
+  buffer.writeAll(compounds.map((e) => e.cDefinition));
   buffer.writeAll(functions.map((e) => e.cCallCode));
   buffer.writeAll(functions.map((e) => e.cCallbackCode));
 
diff --git a/tests/ffi_2/ffi_2.status b/tests/ffi_2/ffi_2.status
index f157962..82349e8 100644
--- a/tests/ffi_2/ffi_2.status
+++ b/tests/ffi_2/ffi_2.status
@@ -11,6 +11,9 @@
 [ $system == android ]
 *: Pass, Slow # https://github.com/dart-lang/sdk/issues/38489
 
+[ $arch == arm && $system == linux ]
+function_callbacks_structs_by_value_test: Slow # QEMU https://dartbug.com/45007
+
 [ $compiler != dart2analyzer && $compiler != fasta && $runtime != dart_precompiled && $runtime != vm ]
 *: SkipByDesign # FFI is a VM-only feature. (This test suite is part of the default set.)
 
diff --git a/tests/ffi_2/function_callbacks_structs_by_value_generated_test.dart b/tests/ffi_2/function_callbacks_structs_by_value_generated_test.dart
index c063d7f..c65a2d2 100644
--- a/tests/ffi_2/function_callbacks_structs_by_value_generated_test.dart
+++ b/tests/ffi_2/function_callbacks_structs_by_value_generated_test.dart
@@ -321,6 +321,31 @@
           passStruct15BytesInlineArrayMixed, 0.0),
       passStruct15BytesInlineArrayMixedAfterCallback),
   CallbackTest.withCheck(
+      "PassUnion4BytesMixedx10",
+      Pointer.fromFunction<PassUnion4BytesMixedx10Type>(
+          passUnion4BytesMixedx10, 0.0),
+      passUnion4BytesMixedx10AfterCallback),
+  CallbackTest.withCheck(
+      "PassUnion8BytesNestedFloatx10",
+      Pointer.fromFunction<PassUnion8BytesNestedFloatx10Type>(
+          passUnion8BytesNestedFloatx10, 0.0),
+      passUnion8BytesNestedFloatx10AfterCallback),
+  CallbackTest.withCheck(
+      "PassUnion9BytesNestedIntx10",
+      Pointer.fromFunction<PassUnion9BytesNestedIntx10Type>(
+          passUnion9BytesNestedIntx10, 0.0),
+      passUnion9BytesNestedIntx10AfterCallback),
+  CallbackTest.withCheck(
+      "PassUnion16BytesNestedInlineArrayFloatx10",
+      Pointer.fromFunction<PassUnion16BytesNestedInlineArrayFloatx10Type>(
+          passUnion16BytesNestedInlineArrayFloatx10, 0.0),
+      passUnion16BytesNestedInlineArrayFloatx10AfterCallback),
+  CallbackTest.withCheck(
+      "PassUnion16BytesNestedFloatx10",
+      Pointer.fromFunction<PassUnion16BytesNestedFloatx10Type>(
+          passUnion16BytesNestedFloatx10, 0.0),
+      passUnion16BytesNestedFloatx10AfterCallback),
+  CallbackTest.withCheck(
       "ReturnStruct1ByteInt",
       Pointer.fromFunction<ReturnStruct1ByteIntType>(returnStruct1ByteInt),
       returnStruct1ByteIntAfterCallback),
@@ -443,6 +468,25 @@
           returnStruct9BytesPackedMixed),
       returnStruct9BytesPackedMixedAfterCallback),
   CallbackTest.withCheck(
+      "ReturnUnion4BytesMixed",
+      Pointer.fromFunction<ReturnUnion4BytesMixedType>(returnUnion4BytesMixed),
+      returnUnion4BytesMixedAfterCallback),
+  CallbackTest.withCheck(
+      "ReturnUnion8BytesNestedFloat",
+      Pointer.fromFunction<ReturnUnion8BytesNestedFloatType>(
+          returnUnion8BytesNestedFloat),
+      returnUnion8BytesNestedFloatAfterCallback),
+  CallbackTest.withCheck(
+      "ReturnUnion9BytesNestedInt",
+      Pointer.fromFunction<ReturnUnion9BytesNestedIntType>(
+          returnUnion9BytesNestedInt),
+      returnUnion9BytesNestedIntAfterCallback),
+  CallbackTest.withCheck(
+      "ReturnUnion16BytesNestedFloat",
+      Pointer.fromFunction<ReturnUnion16BytesNestedFloatType>(
+          returnUnion16BytesNestedFloat),
+      returnUnion16BytesNestedFloatAfterCallback),
+  CallbackTest.withCheck(
       "ReturnStructArgumentStruct1ByteInt",
       Pointer.fromFunction<ReturnStructArgumentStruct1ByteIntType>(
           returnStructArgumentStruct1ByteInt),
@@ -7068,6 +7112,602 @@
   Expect.approxEquals(3.0, result);
 }
 
+typedef PassUnion4BytesMixedx10Type = Double Function(
+    Union4BytesMixed,
+    Union4BytesMixed,
+    Union4BytesMixed,
+    Union4BytesMixed,
+    Union4BytesMixed,
+    Union4BytesMixed,
+    Union4BytesMixed,
+    Union4BytesMixed,
+    Union4BytesMixed,
+    Union4BytesMixed);
+
+// Global variables to be able to test inputs after callback returned.
+Union4BytesMixed passUnion4BytesMixedx10_a0 = Union4BytesMixed();
+Union4BytesMixed passUnion4BytesMixedx10_a1 = Union4BytesMixed();
+Union4BytesMixed passUnion4BytesMixedx10_a2 = Union4BytesMixed();
+Union4BytesMixed passUnion4BytesMixedx10_a3 = Union4BytesMixed();
+Union4BytesMixed passUnion4BytesMixedx10_a4 = Union4BytesMixed();
+Union4BytesMixed passUnion4BytesMixedx10_a5 = Union4BytesMixed();
+Union4BytesMixed passUnion4BytesMixedx10_a6 = Union4BytesMixed();
+Union4BytesMixed passUnion4BytesMixedx10_a7 = Union4BytesMixed();
+Union4BytesMixed passUnion4BytesMixedx10_a8 = Union4BytesMixed();
+Union4BytesMixed passUnion4BytesMixedx10_a9 = Union4BytesMixed();
+
+// Result variable also global, so we can delete it after the callback.
+double passUnion4BytesMixedx10Result = 0.0;
+
+double passUnion4BytesMixedx10CalculateResult() {
+  double result = 0;
+
+  result += passUnion4BytesMixedx10_a0.a0;
+  result += passUnion4BytesMixedx10_a1.a0;
+  result += passUnion4BytesMixedx10_a2.a0;
+  result += passUnion4BytesMixedx10_a3.a0;
+  result += passUnion4BytesMixedx10_a4.a0;
+  result += passUnion4BytesMixedx10_a5.a0;
+  result += passUnion4BytesMixedx10_a6.a0;
+  result += passUnion4BytesMixedx10_a7.a0;
+  result += passUnion4BytesMixedx10_a8.a0;
+  result += passUnion4BytesMixedx10_a9.a0;
+
+  passUnion4BytesMixedx10Result = result;
+
+  return result;
+}
+
+/// Check placement of mixed integer/float union.
+double passUnion4BytesMixedx10(
+    Union4BytesMixed a0,
+    Union4BytesMixed a1,
+    Union4BytesMixed a2,
+    Union4BytesMixed a3,
+    Union4BytesMixed a4,
+    Union4BytesMixed a5,
+    Union4BytesMixed a6,
+    Union4BytesMixed a7,
+    Union4BytesMixed a8,
+    Union4BytesMixed a9) {
+  print(
+      "passUnion4BytesMixedx10(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9})");
+
+  // In legacy mode, possibly return null.
+  if (a0.a0 == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0 == 42 || a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("PassUnion4BytesMixedx10 throwing on purpose!");
+  }
+
+  passUnion4BytesMixedx10_a0 = a0;
+  passUnion4BytesMixedx10_a1 = a1;
+  passUnion4BytesMixedx10_a2 = a2;
+  passUnion4BytesMixedx10_a3 = a3;
+  passUnion4BytesMixedx10_a4 = a4;
+  passUnion4BytesMixedx10_a5 = a5;
+  passUnion4BytesMixedx10_a6 = a6;
+  passUnion4BytesMixedx10_a7 = a7;
+  passUnion4BytesMixedx10_a8 = a8;
+  passUnion4BytesMixedx10_a9 = a9;
+
+  final result = passUnion4BytesMixedx10CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passUnion4BytesMixedx10AfterCallback() {
+  final result = passUnion4BytesMixedx10CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.approxEquals(55.0, result);
+}
+
+typedef PassUnion8BytesNestedFloatx10Type = Double Function(
+    Union8BytesNestedFloat,
+    Union8BytesNestedFloat,
+    Union8BytesNestedFloat,
+    Union8BytesNestedFloat,
+    Union8BytesNestedFloat,
+    Union8BytesNestedFloat,
+    Union8BytesNestedFloat,
+    Union8BytesNestedFloat,
+    Union8BytesNestedFloat,
+    Union8BytesNestedFloat);
+
+// Global variables to be able to test inputs after callback returned.
+Union8BytesNestedFloat passUnion8BytesNestedFloatx10_a0 =
+    Union8BytesNestedFloat();
+Union8BytesNestedFloat passUnion8BytesNestedFloatx10_a1 =
+    Union8BytesNestedFloat();
+Union8BytesNestedFloat passUnion8BytesNestedFloatx10_a2 =
+    Union8BytesNestedFloat();
+Union8BytesNestedFloat passUnion8BytesNestedFloatx10_a3 =
+    Union8BytesNestedFloat();
+Union8BytesNestedFloat passUnion8BytesNestedFloatx10_a4 =
+    Union8BytesNestedFloat();
+Union8BytesNestedFloat passUnion8BytesNestedFloatx10_a5 =
+    Union8BytesNestedFloat();
+Union8BytesNestedFloat passUnion8BytesNestedFloatx10_a6 =
+    Union8BytesNestedFloat();
+Union8BytesNestedFloat passUnion8BytesNestedFloatx10_a7 =
+    Union8BytesNestedFloat();
+Union8BytesNestedFloat passUnion8BytesNestedFloatx10_a8 =
+    Union8BytesNestedFloat();
+Union8BytesNestedFloat passUnion8BytesNestedFloatx10_a9 =
+    Union8BytesNestedFloat();
+
+// Result variable also global, so we can delete it after the callback.
+double passUnion8BytesNestedFloatx10Result = 0.0;
+
+double passUnion8BytesNestedFloatx10CalculateResult() {
+  double result = 0;
+
+  result += passUnion8BytesNestedFloatx10_a0.a0;
+  result += passUnion8BytesNestedFloatx10_a1.a0;
+  result += passUnion8BytesNestedFloatx10_a2.a0;
+  result += passUnion8BytesNestedFloatx10_a3.a0;
+  result += passUnion8BytesNestedFloatx10_a4.a0;
+  result += passUnion8BytesNestedFloatx10_a5.a0;
+  result += passUnion8BytesNestedFloatx10_a6.a0;
+  result += passUnion8BytesNestedFloatx10_a7.a0;
+  result += passUnion8BytesNestedFloatx10_a8.a0;
+  result += passUnion8BytesNestedFloatx10_a9.a0;
+
+  passUnion8BytesNestedFloatx10Result = result;
+
+  return result;
+}
+
+/// Check placement of mixed floats union.
+double passUnion8BytesNestedFloatx10(
+    Union8BytesNestedFloat a0,
+    Union8BytesNestedFloat a1,
+    Union8BytesNestedFloat a2,
+    Union8BytesNestedFloat a3,
+    Union8BytesNestedFloat a4,
+    Union8BytesNestedFloat a5,
+    Union8BytesNestedFloat a6,
+    Union8BytesNestedFloat a7,
+    Union8BytesNestedFloat a8,
+    Union8BytesNestedFloat a9) {
+  print(
+      "passUnion8BytesNestedFloatx10(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9})");
+
+  // In legacy mode, possibly return null.
+  if (a0.a0 == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0 == 42 || a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("PassUnion8BytesNestedFloatx10 throwing on purpose!");
+  }
+
+  passUnion8BytesNestedFloatx10_a0 = a0;
+  passUnion8BytesNestedFloatx10_a1 = a1;
+  passUnion8BytesNestedFloatx10_a2 = a2;
+  passUnion8BytesNestedFloatx10_a3 = a3;
+  passUnion8BytesNestedFloatx10_a4 = a4;
+  passUnion8BytesNestedFloatx10_a5 = a5;
+  passUnion8BytesNestedFloatx10_a6 = a6;
+  passUnion8BytesNestedFloatx10_a7 = a7;
+  passUnion8BytesNestedFloatx10_a8 = a8;
+  passUnion8BytesNestedFloatx10_a9 = a9;
+
+  final result = passUnion8BytesNestedFloatx10CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passUnion8BytesNestedFloatx10AfterCallback() {
+  final result = passUnion8BytesNestedFloatx10CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.approxEquals(5.0, result);
+}
+
+typedef PassUnion9BytesNestedIntx10Type = Double Function(
+    Union9BytesNestedInt,
+    Union9BytesNestedInt,
+    Union9BytesNestedInt,
+    Union9BytesNestedInt,
+    Union9BytesNestedInt,
+    Union9BytesNestedInt,
+    Union9BytesNestedInt,
+    Union9BytesNestedInt,
+    Union9BytesNestedInt,
+    Union9BytesNestedInt);
+
+// Global variables to be able to test inputs after callback returned.
+Union9BytesNestedInt passUnion9BytesNestedIntx10_a0 = Union9BytesNestedInt();
+Union9BytesNestedInt passUnion9BytesNestedIntx10_a1 = Union9BytesNestedInt();
+Union9BytesNestedInt passUnion9BytesNestedIntx10_a2 = Union9BytesNestedInt();
+Union9BytesNestedInt passUnion9BytesNestedIntx10_a3 = Union9BytesNestedInt();
+Union9BytesNestedInt passUnion9BytesNestedIntx10_a4 = Union9BytesNestedInt();
+Union9BytesNestedInt passUnion9BytesNestedIntx10_a5 = Union9BytesNestedInt();
+Union9BytesNestedInt passUnion9BytesNestedIntx10_a6 = Union9BytesNestedInt();
+Union9BytesNestedInt passUnion9BytesNestedIntx10_a7 = Union9BytesNestedInt();
+Union9BytesNestedInt passUnion9BytesNestedIntx10_a8 = Union9BytesNestedInt();
+Union9BytesNestedInt passUnion9BytesNestedIntx10_a9 = Union9BytesNestedInt();
+
+// Result variable also global, so we can delete it after the callback.
+double passUnion9BytesNestedIntx10Result = 0.0;
+
+double passUnion9BytesNestedIntx10CalculateResult() {
+  double result = 0;
+
+  result += passUnion9BytesNestedIntx10_a0.a0.a0;
+  result += passUnion9BytesNestedIntx10_a0.a0.a1;
+  result += passUnion9BytesNestedIntx10_a0.a0.a2;
+  result += passUnion9BytesNestedIntx10_a1.a0.a0;
+  result += passUnion9BytesNestedIntx10_a1.a0.a1;
+  result += passUnion9BytesNestedIntx10_a1.a0.a2;
+  result += passUnion9BytesNestedIntx10_a2.a0.a0;
+  result += passUnion9BytesNestedIntx10_a2.a0.a1;
+  result += passUnion9BytesNestedIntx10_a2.a0.a2;
+  result += passUnion9BytesNestedIntx10_a3.a0.a0;
+  result += passUnion9BytesNestedIntx10_a3.a0.a1;
+  result += passUnion9BytesNestedIntx10_a3.a0.a2;
+  result += passUnion9BytesNestedIntx10_a4.a0.a0;
+  result += passUnion9BytesNestedIntx10_a4.a0.a1;
+  result += passUnion9BytesNestedIntx10_a4.a0.a2;
+  result += passUnion9BytesNestedIntx10_a5.a0.a0;
+  result += passUnion9BytesNestedIntx10_a5.a0.a1;
+  result += passUnion9BytesNestedIntx10_a5.a0.a2;
+  result += passUnion9BytesNestedIntx10_a6.a0.a0;
+  result += passUnion9BytesNestedIntx10_a6.a0.a1;
+  result += passUnion9BytesNestedIntx10_a6.a0.a2;
+  result += passUnion9BytesNestedIntx10_a7.a0.a0;
+  result += passUnion9BytesNestedIntx10_a7.a0.a1;
+  result += passUnion9BytesNestedIntx10_a7.a0.a2;
+  result += passUnion9BytesNestedIntx10_a8.a0.a0;
+  result += passUnion9BytesNestedIntx10_a8.a0.a1;
+  result += passUnion9BytesNestedIntx10_a8.a0.a2;
+  result += passUnion9BytesNestedIntx10_a9.a0.a0;
+  result += passUnion9BytesNestedIntx10_a9.a0.a1;
+  result += passUnion9BytesNestedIntx10_a9.a0.a2;
+
+  passUnion9BytesNestedIntx10Result = result;
+
+  return result;
+}
+
+/// Mixed-size union argument.
+double passUnion9BytesNestedIntx10(
+    Union9BytesNestedInt a0,
+    Union9BytesNestedInt a1,
+    Union9BytesNestedInt a2,
+    Union9BytesNestedInt a3,
+    Union9BytesNestedInt a4,
+    Union9BytesNestedInt a5,
+    Union9BytesNestedInt a6,
+    Union9BytesNestedInt a7,
+    Union9BytesNestedInt a8,
+    Union9BytesNestedInt a9) {
+  print(
+      "passUnion9BytesNestedIntx10(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9})");
+
+  // In legacy mode, possibly return null.
+  if (a0.a0.a0 == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("PassUnion9BytesNestedIntx10 throwing on purpose!");
+  }
+
+  passUnion9BytesNestedIntx10_a0 = a0;
+  passUnion9BytesNestedIntx10_a1 = a1;
+  passUnion9BytesNestedIntx10_a2 = a2;
+  passUnion9BytesNestedIntx10_a3 = a3;
+  passUnion9BytesNestedIntx10_a4 = a4;
+  passUnion9BytesNestedIntx10_a5 = a5;
+  passUnion9BytesNestedIntx10_a6 = a6;
+  passUnion9BytesNestedIntx10_a7 = a7;
+  passUnion9BytesNestedIntx10_a8 = a8;
+  passUnion9BytesNestedIntx10_a9 = a9;
+
+  final result = passUnion9BytesNestedIntx10CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passUnion9BytesNestedIntx10AfterCallback() {
+  final result = passUnion9BytesNestedIntx10CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.approxEquals(15.0, result);
+}
+
+typedef PassUnion16BytesNestedInlineArrayFloatx10Type = Double Function(
+    Union16BytesNestedInlineArrayFloat,
+    Union16BytesNestedInlineArrayFloat,
+    Union16BytesNestedInlineArrayFloat,
+    Union16BytesNestedInlineArrayFloat,
+    Union16BytesNestedInlineArrayFloat,
+    Union16BytesNestedInlineArrayFloat,
+    Union16BytesNestedInlineArrayFloat,
+    Union16BytesNestedInlineArrayFloat,
+    Union16BytesNestedInlineArrayFloat,
+    Union16BytesNestedInlineArrayFloat);
+
+// Global variables to be able to test inputs after callback returned.
+Union16BytesNestedInlineArrayFloat
+    passUnion16BytesNestedInlineArrayFloatx10_a0 =
+    Union16BytesNestedInlineArrayFloat();
+Union16BytesNestedInlineArrayFloat
+    passUnion16BytesNestedInlineArrayFloatx10_a1 =
+    Union16BytesNestedInlineArrayFloat();
+Union16BytesNestedInlineArrayFloat
+    passUnion16BytesNestedInlineArrayFloatx10_a2 =
+    Union16BytesNestedInlineArrayFloat();
+Union16BytesNestedInlineArrayFloat
+    passUnion16BytesNestedInlineArrayFloatx10_a3 =
+    Union16BytesNestedInlineArrayFloat();
+Union16BytesNestedInlineArrayFloat
+    passUnion16BytesNestedInlineArrayFloatx10_a4 =
+    Union16BytesNestedInlineArrayFloat();
+Union16BytesNestedInlineArrayFloat
+    passUnion16BytesNestedInlineArrayFloatx10_a5 =
+    Union16BytesNestedInlineArrayFloat();
+Union16BytesNestedInlineArrayFloat
+    passUnion16BytesNestedInlineArrayFloatx10_a6 =
+    Union16BytesNestedInlineArrayFloat();
+Union16BytesNestedInlineArrayFloat
+    passUnion16BytesNestedInlineArrayFloatx10_a7 =
+    Union16BytesNestedInlineArrayFloat();
+Union16BytesNestedInlineArrayFloat
+    passUnion16BytesNestedInlineArrayFloatx10_a8 =
+    Union16BytesNestedInlineArrayFloat();
+Union16BytesNestedInlineArrayFloat
+    passUnion16BytesNestedInlineArrayFloatx10_a9 =
+    Union16BytesNestedInlineArrayFloat();
+
+// Result variable also global, so we can delete it after the callback.
+double passUnion16BytesNestedInlineArrayFloatx10Result = 0.0;
+
+double passUnion16BytesNestedInlineArrayFloatx10CalculateResult() {
+  double result = 0;
+
+  result += passUnion16BytesNestedInlineArrayFloatx10_a0.a0[0];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a0.a0[1];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a0.a0[2];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a0.a0[3];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a1.a0[0];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a1.a0[1];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a1.a0[2];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a1.a0[3];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a2.a0[0];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a2.a0[1];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a2.a0[2];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a2.a0[3];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a3.a0[0];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a3.a0[1];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a3.a0[2];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a3.a0[3];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a4.a0[0];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a4.a0[1];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a4.a0[2];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a4.a0[3];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a5.a0[0];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a5.a0[1];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a5.a0[2];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a5.a0[3];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a6.a0[0];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a6.a0[1];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a6.a0[2];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a6.a0[3];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a7.a0[0];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a7.a0[1];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a7.a0[2];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a7.a0[3];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a8.a0[0];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a8.a0[1];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a8.a0[2];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a8.a0[3];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a9.a0[0];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a9.a0[1];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a9.a0[2];
+  result += passUnion16BytesNestedInlineArrayFloatx10_a9.a0[3];
+
+  passUnion16BytesNestedInlineArrayFloatx10Result = result;
+
+  return result;
+}
+
+/// Union with homogenous floats.
+double passUnion16BytesNestedInlineArrayFloatx10(
+    Union16BytesNestedInlineArrayFloat a0,
+    Union16BytesNestedInlineArrayFloat a1,
+    Union16BytesNestedInlineArrayFloat a2,
+    Union16BytesNestedInlineArrayFloat a3,
+    Union16BytesNestedInlineArrayFloat a4,
+    Union16BytesNestedInlineArrayFloat a5,
+    Union16BytesNestedInlineArrayFloat a6,
+    Union16BytesNestedInlineArrayFloat a7,
+    Union16BytesNestedInlineArrayFloat a8,
+    Union16BytesNestedInlineArrayFloat a9) {
+  print(
+      "passUnion16BytesNestedInlineArrayFloatx10(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9})");
+
+  // In legacy mode, possibly return null.
+  if (a0.a0[0] == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0[0] == 42 || a0.a0[0] == 84) {
+    print("throwing!");
+    throw Exception(
+        "PassUnion16BytesNestedInlineArrayFloatx10 throwing on purpose!");
+  }
+
+  passUnion16BytesNestedInlineArrayFloatx10_a0 = a0;
+  passUnion16BytesNestedInlineArrayFloatx10_a1 = a1;
+  passUnion16BytesNestedInlineArrayFloatx10_a2 = a2;
+  passUnion16BytesNestedInlineArrayFloatx10_a3 = a3;
+  passUnion16BytesNestedInlineArrayFloatx10_a4 = a4;
+  passUnion16BytesNestedInlineArrayFloatx10_a5 = a5;
+  passUnion16BytesNestedInlineArrayFloatx10_a6 = a6;
+  passUnion16BytesNestedInlineArrayFloatx10_a7 = a7;
+  passUnion16BytesNestedInlineArrayFloatx10_a8 = a8;
+  passUnion16BytesNestedInlineArrayFloatx10_a9 = a9;
+
+  final result = passUnion16BytesNestedInlineArrayFloatx10CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passUnion16BytesNestedInlineArrayFloatx10AfterCallback() {
+  final result = passUnion16BytesNestedInlineArrayFloatx10CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.approxEquals(20.0, result);
+}
+
+typedef PassUnion16BytesNestedFloatx10Type = Double Function(
+    Union16BytesNestedFloat,
+    Union16BytesNestedFloat,
+    Union16BytesNestedFloat,
+    Union16BytesNestedFloat,
+    Union16BytesNestedFloat,
+    Union16BytesNestedFloat,
+    Union16BytesNestedFloat,
+    Union16BytesNestedFloat,
+    Union16BytesNestedFloat,
+    Union16BytesNestedFloat);
+
+// Global variables to be able to test inputs after callback returned.
+Union16BytesNestedFloat passUnion16BytesNestedFloatx10_a0 =
+    Union16BytesNestedFloat();
+Union16BytesNestedFloat passUnion16BytesNestedFloatx10_a1 =
+    Union16BytesNestedFloat();
+Union16BytesNestedFloat passUnion16BytesNestedFloatx10_a2 =
+    Union16BytesNestedFloat();
+Union16BytesNestedFloat passUnion16BytesNestedFloatx10_a3 =
+    Union16BytesNestedFloat();
+Union16BytesNestedFloat passUnion16BytesNestedFloatx10_a4 =
+    Union16BytesNestedFloat();
+Union16BytesNestedFloat passUnion16BytesNestedFloatx10_a5 =
+    Union16BytesNestedFloat();
+Union16BytesNestedFloat passUnion16BytesNestedFloatx10_a6 =
+    Union16BytesNestedFloat();
+Union16BytesNestedFloat passUnion16BytesNestedFloatx10_a7 =
+    Union16BytesNestedFloat();
+Union16BytesNestedFloat passUnion16BytesNestedFloatx10_a8 =
+    Union16BytesNestedFloat();
+Union16BytesNestedFloat passUnion16BytesNestedFloatx10_a9 =
+    Union16BytesNestedFloat();
+
+// Result variable also global, so we can delete it after the callback.
+double passUnion16BytesNestedFloatx10Result = 0.0;
+
+double passUnion16BytesNestedFloatx10CalculateResult() {
+  double result = 0;
+
+  result += passUnion16BytesNestedFloatx10_a0.a0.a0;
+  result += passUnion16BytesNestedFloatx10_a0.a0.a1;
+  result += passUnion16BytesNestedFloatx10_a1.a0.a0;
+  result += passUnion16BytesNestedFloatx10_a1.a0.a1;
+  result += passUnion16BytesNestedFloatx10_a2.a0.a0;
+  result += passUnion16BytesNestedFloatx10_a2.a0.a1;
+  result += passUnion16BytesNestedFloatx10_a3.a0.a0;
+  result += passUnion16BytesNestedFloatx10_a3.a0.a1;
+  result += passUnion16BytesNestedFloatx10_a4.a0.a0;
+  result += passUnion16BytesNestedFloatx10_a4.a0.a1;
+  result += passUnion16BytesNestedFloatx10_a5.a0.a0;
+  result += passUnion16BytesNestedFloatx10_a5.a0.a1;
+  result += passUnion16BytesNestedFloatx10_a6.a0.a0;
+  result += passUnion16BytesNestedFloatx10_a6.a0.a1;
+  result += passUnion16BytesNestedFloatx10_a7.a0.a0;
+  result += passUnion16BytesNestedFloatx10_a7.a0.a1;
+  result += passUnion16BytesNestedFloatx10_a8.a0.a0;
+  result += passUnion16BytesNestedFloatx10_a8.a0.a1;
+  result += passUnion16BytesNestedFloatx10_a9.a0.a0;
+  result += passUnion16BytesNestedFloatx10_a9.a0.a1;
+
+  passUnion16BytesNestedFloatx10Result = result;
+
+  return result;
+}
+
+/// Union with homogenous floats.
+double passUnion16BytesNestedFloatx10(
+    Union16BytesNestedFloat a0,
+    Union16BytesNestedFloat a1,
+    Union16BytesNestedFloat a2,
+    Union16BytesNestedFloat a3,
+    Union16BytesNestedFloat a4,
+    Union16BytesNestedFloat a5,
+    Union16BytesNestedFloat a6,
+    Union16BytesNestedFloat a7,
+    Union16BytesNestedFloat a8,
+    Union16BytesNestedFloat a9) {
+  print(
+      "passUnion16BytesNestedFloatx10(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9})");
+
+  // In legacy mode, possibly return null.
+  if (a0.a0.a0 == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("PassUnion16BytesNestedFloatx10 throwing on purpose!");
+  }
+
+  passUnion16BytesNestedFloatx10_a0 = a0;
+  passUnion16BytesNestedFloatx10_a1 = a1;
+  passUnion16BytesNestedFloatx10_a2 = a2;
+  passUnion16BytesNestedFloatx10_a3 = a3;
+  passUnion16BytesNestedFloatx10_a4 = a4;
+  passUnion16BytesNestedFloatx10_a5 = a5;
+  passUnion16BytesNestedFloatx10_a6 = a6;
+  passUnion16BytesNestedFloatx10_a7 = a7;
+  passUnion16BytesNestedFloatx10_a8 = a8;
+  passUnion16BytesNestedFloatx10_a9 = a9;
+
+  final result = passUnion16BytesNestedFloatx10CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passUnion16BytesNestedFloatx10AfterCallback() {
+  final result = passUnion16BytesNestedFloatx10CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.approxEquals(10.0, result);
+}
+
 typedef ReturnStruct1ByteIntType = Struct1ByteInt Function(Int8);
 
 // Global variables to be able to test inputs after callback returned.
@@ -9417,6 +10057,232 @@
   calloc.free(returnStruct9BytesPackedMixedResultPointer);
 }
 
+typedef ReturnUnion4BytesMixedType = Union4BytesMixed Function(Uint32);
+
+// Global variables to be able to test inputs after callback returned.
+int returnUnion4BytesMixed_a0 = 0;
+
+// Result variable also global, so we can delete it after the callback.
+Pointer<Union4BytesMixed> returnUnion4BytesMixedResultPointer = nullptr;
+
+Union4BytesMixed returnUnion4BytesMixedCalculateResult() {
+  final resultPointer = calloc<Union4BytesMixed>();
+  final result = resultPointer.ref;
+
+  result.a0 = returnUnion4BytesMixed_a0;
+
+  returnUnion4BytesMixedResultPointer = resultPointer;
+
+  return result;
+}
+
+/// Returning a mixed integer/float union.
+Union4BytesMixed returnUnion4BytesMixed(int a0) {
+  print("returnUnion4BytesMixed(${a0})");
+
+  // In legacy mode, possibly return null.
+  if (a0 == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0 == 42 || a0 == 84) {
+    print("throwing!");
+    throw Exception("ReturnUnion4BytesMixed throwing on purpose!");
+  }
+
+  returnUnion4BytesMixed_a0 = a0;
+
+  final result = returnUnion4BytesMixedCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnUnion4BytesMixedAfterCallback() {
+  calloc.free(returnUnion4BytesMixedResultPointer);
+
+  final result = returnUnion4BytesMixedCalculateResult();
+
+  print("after callback result = $result");
+
+  calloc.free(returnUnion4BytesMixedResultPointer);
+}
+
+typedef ReturnUnion8BytesNestedFloatType = Union8BytesNestedFloat Function(
+    Double);
+
+// Global variables to be able to test inputs after callback returned.
+double returnUnion8BytesNestedFloat_a0 = 0.0;
+
+// Result variable also global, so we can delete it after the callback.
+Pointer<Union8BytesNestedFloat> returnUnion8BytesNestedFloatResultPointer =
+    nullptr;
+
+Union8BytesNestedFloat returnUnion8BytesNestedFloatCalculateResult() {
+  final resultPointer = calloc<Union8BytesNestedFloat>();
+  final result = resultPointer.ref;
+
+  result.a0 = returnUnion8BytesNestedFloat_a0;
+
+  returnUnion8BytesNestedFloatResultPointer = resultPointer;
+
+  return result;
+}
+
+/// Returning a floating point only union.
+Union8BytesNestedFloat returnUnion8BytesNestedFloat(double a0) {
+  print("returnUnion8BytesNestedFloat(${a0})");
+
+  // In legacy mode, possibly return null.
+  if (a0 == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0 == 42 || a0 == 84) {
+    print("throwing!");
+    throw Exception("ReturnUnion8BytesNestedFloat throwing on purpose!");
+  }
+
+  returnUnion8BytesNestedFloat_a0 = a0;
+
+  final result = returnUnion8BytesNestedFloatCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnUnion8BytesNestedFloatAfterCallback() {
+  calloc.free(returnUnion8BytesNestedFloatResultPointer);
+
+  final result = returnUnion8BytesNestedFloatCalculateResult();
+
+  print("after callback result = $result");
+
+  calloc.free(returnUnion8BytesNestedFloatResultPointer);
+}
+
+typedef ReturnUnion9BytesNestedIntType = Union9BytesNestedInt Function(
+    Struct8BytesInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct8BytesInt returnUnion9BytesNestedInt_a0 = Struct8BytesInt();
+
+// Result variable also global, so we can delete it after the callback.
+Pointer<Union9BytesNestedInt> returnUnion9BytesNestedIntResultPointer = nullptr;
+
+Union9BytesNestedInt returnUnion9BytesNestedIntCalculateResult() {
+  final resultPointer = calloc<Union9BytesNestedInt>();
+  final result = resultPointer.ref;
+
+  result.a0.a0 = returnUnion9BytesNestedInt_a0.a0;
+  result.a0.a1 = returnUnion9BytesNestedInt_a0.a1;
+  result.a0.a2 = returnUnion9BytesNestedInt_a0.a2;
+
+  returnUnion9BytesNestedIntResultPointer = resultPointer;
+
+  return result;
+}
+
+/// Returning a mixed-size union.
+Union9BytesNestedInt returnUnion9BytesNestedInt(Struct8BytesInt a0) {
+  print("returnUnion9BytesNestedInt(${a0})");
+
+  // In legacy mode, possibly return null.
+  if (a0.a0 == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0 == 42 || a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("ReturnUnion9BytesNestedInt throwing on purpose!");
+  }
+
+  returnUnion9BytesNestedInt_a0 = a0;
+
+  final result = returnUnion9BytesNestedIntCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnUnion9BytesNestedIntAfterCallback() {
+  calloc.free(returnUnion9BytesNestedIntResultPointer);
+
+  final result = returnUnion9BytesNestedIntCalculateResult();
+
+  print("after callback result = $result");
+
+  calloc.free(returnUnion9BytesNestedIntResultPointer);
+}
+
+typedef ReturnUnion16BytesNestedFloatType = Union16BytesNestedFloat Function(
+    Struct8BytesHomogeneousFloat);
+
+// Global variables to be able to test inputs after callback returned.
+Struct8BytesHomogeneousFloat returnUnion16BytesNestedFloat_a0 =
+    Struct8BytesHomogeneousFloat();
+
+// Result variable also global, so we can delete it after the callback.
+Pointer<Union16BytesNestedFloat> returnUnion16BytesNestedFloatResultPointer =
+    nullptr;
+
+Union16BytesNestedFloat returnUnion16BytesNestedFloatCalculateResult() {
+  final resultPointer = calloc<Union16BytesNestedFloat>();
+  final result = resultPointer.ref;
+
+  result.a0.a0 = returnUnion16BytesNestedFloat_a0.a0;
+  result.a0.a1 = returnUnion16BytesNestedFloat_a0.a1;
+
+  returnUnion16BytesNestedFloatResultPointer = resultPointer;
+
+  return result;
+}
+
+/// Returning union with homogenous floats.
+Union16BytesNestedFloat returnUnion16BytesNestedFloat(
+    Struct8BytesHomogeneousFloat a0) {
+  print("returnUnion16BytesNestedFloat(${a0})");
+
+  // In legacy mode, possibly return null.
+  if (a0.a0 == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0 == 42 || a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("ReturnUnion16BytesNestedFloat throwing on purpose!");
+  }
+
+  returnUnion16BytesNestedFloat_a0 = a0;
+
+  final result = returnUnion16BytesNestedFloatCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnUnion16BytesNestedFloatAfterCallback() {
+  calloc.free(returnUnion16BytesNestedFloatResultPointer);
+
+  final result = returnUnion16BytesNestedFloatCalculateResult();
+
+  print("after callback result = $result");
+
+  calloc.free(returnUnion16BytesNestedFloatResultPointer);
+}
+
 typedef ReturnStructArgumentStruct1ByteIntType = Struct1ByteInt Function(
     Struct1ByteInt);
 
diff --git a/tests/ffi_2/function_structs_by_value_generated_test.dart b/tests/ffi_2/function_structs_by_value_generated_test.dart
index 48c7b59..156fc07 100644
--- a/tests/ffi_2/function_structs_by_value_generated_test.dart
+++ b/tests/ffi_2/function_structs_by_value_generated_test.dart
@@ -77,6 +77,11 @@
     testPassStructNestedAlignmentStruct5BytesPackedMixed();
     testPassStruct6BytesInlineArrayInt();
     testPassStruct15BytesInlineArrayMixed();
+    testPassUnion4BytesMixedx10();
+    testPassUnion8BytesNestedFloatx10();
+    testPassUnion9BytesNestedIntx10();
+    testPassUnion16BytesNestedInlineArrayFloatx10();
+    testPassUnion16BytesNestedFloatx10();
     testReturnStruct1ByteInt();
     testReturnStruct3BytesHomogeneousUint8();
     testReturnStruct3BytesInt2ByteAligned();
@@ -102,6 +107,10 @@
     testReturnStruct3BytesPackedInt();
     testReturnStruct8BytesPackedInt();
     testReturnStruct9BytesPackedMixed();
+    testReturnUnion4BytesMixed();
+    testReturnUnion8BytesNestedFloat();
+    testReturnUnion9BytesNestedInt();
+    testReturnUnion16BytesNestedFloat();
     testReturnStructArgumentStruct1ByteInt();
     testReturnStructArgumentInt32x8Struct1ByteInt();
     testReturnStructArgumentStruct8BytesHomogeneousFloat();
@@ -1283,6 +1292,52 @@
   String toString() => "(${[for (var i0 = 0; i0 < 3; i0 += 1) a0[i0]]})";
 }
 
+class Union4BytesMixed extends Union {
+  @Uint32()
+  int a0;
+
+  @Float()
+  double a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+class Union8BytesNestedFloat extends Union {
+  @Double()
+  double a0;
+
+  Struct8BytesHomogeneousFloat a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+class Union9BytesNestedInt extends Union {
+  Struct8BytesInt a0;
+
+  Struct9BytesHomogeneousUint8 a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+class Union16BytesNestedInlineArrayFloat extends Union {
+  @Array(4)
+  Array<Float> a0;
+
+  Struct16BytesHomogeneousFloat a1;
+
+  String toString() => "(${[for (var i0 = 0; i0 < 4; i0 += 1) a0[i0]]}, ${a1})";
+}
+
+class Union16BytesNestedFloat extends Union {
+  Struct8BytesHomogeneousFloat a0;
+
+  Struct12BytesHomogeneousFloat a1;
+
+  Struct16BytesHomogeneousFloat a2;
+
+  String toString() => "(${a0}, ${a1}, ${a2})";
+}
+
 final passStruct1ByteIntx10 = ffiTestFunctions.lookupFunction<
     Int64 Function(
         Struct1ByteInt,
@@ -5916,6 +5971,453 @@
   calloc.free(a0Pointer);
 }
 
+final passUnion4BytesMixedx10 = ffiTestFunctions.lookupFunction<
+    Double Function(
+        Union4BytesMixed,
+        Union4BytesMixed,
+        Union4BytesMixed,
+        Union4BytesMixed,
+        Union4BytesMixed,
+        Union4BytesMixed,
+        Union4BytesMixed,
+        Union4BytesMixed,
+        Union4BytesMixed,
+        Union4BytesMixed),
+    double Function(
+        Union4BytesMixed,
+        Union4BytesMixed,
+        Union4BytesMixed,
+        Union4BytesMixed,
+        Union4BytesMixed,
+        Union4BytesMixed,
+        Union4BytesMixed,
+        Union4BytesMixed,
+        Union4BytesMixed,
+        Union4BytesMixed)>("PassUnion4BytesMixedx10");
+
+/// Check placement of mixed integer/float union.
+void testPassUnion4BytesMixedx10() {
+  final a0Pointer = calloc<Union4BytesMixed>();
+  final Union4BytesMixed a0 = a0Pointer.ref;
+  final a1Pointer = calloc<Union4BytesMixed>();
+  final Union4BytesMixed a1 = a1Pointer.ref;
+  final a2Pointer = calloc<Union4BytesMixed>();
+  final Union4BytesMixed a2 = a2Pointer.ref;
+  final a3Pointer = calloc<Union4BytesMixed>();
+  final Union4BytesMixed a3 = a3Pointer.ref;
+  final a4Pointer = calloc<Union4BytesMixed>();
+  final Union4BytesMixed a4 = a4Pointer.ref;
+  final a5Pointer = calloc<Union4BytesMixed>();
+  final Union4BytesMixed a5 = a5Pointer.ref;
+  final a6Pointer = calloc<Union4BytesMixed>();
+  final Union4BytesMixed a6 = a6Pointer.ref;
+  final a7Pointer = calloc<Union4BytesMixed>();
+  final Union4BytesMixed a7 = a7Pointer.ref;
+  final a8Pointer = calloc<Union4BytesMixed>();
+  final Union4BytesMixed a8 = a8Pointer.ref;
+  final a9Pointer = calloc<Union4BytesMixed>();
+  final Union4BytesMixed a9 = a9Pointer.ref;
+
+  a0.a0 = 1;
+  a1.a0 = 2;
+  a2.a0 = 3;
+  a3.a0 = 4;
+  a4.a0 = 5;
+  a5.a0 = 6;
+  a6.a0 = 7;
+  a7.a0 = 8;
+  a8.a0 = 9;
+  a9.a0 = 10;
+
+  final result =
+      passUnion4BytesMixedx10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  print("result = $result");
+
+  Expect.approxEquals(55.0, result);
+
+  calloc.free(a0Pointer);
+  calloc.free(a1Pointer);
+  calloc.free(a2Pointer);
+  calloc.free(a3Pointer);
+  calloc.free(a4Pointer);
+  calloc.free(a5Pointer);
+  calloc.free(a6Pointer);
+  calloc.free(a7Pointer);
+  calloc.free(a8Pointer);
+  calloc.free(a9Pointer);
+}
+
+final passUnion8BytesNestedFloatx10 = ffiTestFunctions.lookupFunction<
+    Double Function(
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat),
+    double Function(
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat,
+        Union8BytesNestedFloat)>("PassUnion8BytesNestedFloatx10");
+
+/// Check placement of mixed floats union.
+void testPassUnion8BytesNestedFloatx10() {
+  final a0Pointer = calloc<Union8BytesNestedFloat>();
+  final Union8BytesNestedFloat a0 = a0Pointer.ref;
+  final a1Pointer = calloc<Union8BytesNestedFloat>();
+  final Union8BytesNestedFloat a1 = a1Pointer.ref;
+  final a2Pointer = calloc<Union8BytesNestedFloat>();
+  final Union8BytesNestedFloat a2 = a2Pointer.ref;
+  final a3Pointer = calloc<Union8BytesNestedFloat>();
+  final Union8BytesNestedFloat a3 = a3Pointer.ref;
+  final a4Pointer = calloc<Union8BytesNestedFloat>();
+  final Union8BytesNestedFloat a4 = a4Pointer.ref;
+  final a5Pointer = calloc<Union8BytesNestedFloat>();
+  final Union8BytesNestedFloat a5 = a5Pointer.ref;
+  final a6Pointer = calloc<Union8BytesNestedFloat>();
+  final Union8BytesNestedFloat a6 = a6Pointer.ref;
+  final a7Pointer = calloc<Union8BytesNestedFloat>();
+  final Union8BytesNestedFloat a7 = a7Pointer.ref;
+  final a8Pointer = calloc<Union8BytesNestedFloat>();
+  final Union8BytesNestedFloat a8 = a8Pointer.ref;
+  final a9Pointer = calloc<Union8BytesNestedFloat>();
+  final Union8BytesNestedFloat a9 = a9Pointer.ref;
+
+  a0.a0 = -1.0;
+  a1.a0 = 2.0;
+  a2.a0 = -3.0;
+  a3.a0 = 4.0;
+  a4.a0 = -5.0;
+  a5.a0 = 6.0;
+  a6.a0 = -7.0;
+  a7.a0 = 8.0;
+  a8.a0 = -9.0;
+  a9.a0 = 10.0;
+
+  final result =
+      passUnion8BytesNestedFloatx10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  print("result = $result");
+
+  Expect.approxEquals(5.0, result);
+
+  calloc.free(a0Pointer);
+  calloc.free(a1Pointer);
+  calloc.free(a2Pointer);
+  calloc.free(a3Pointer);
+  calloc.free(a4Pointer);
+  calloc.free(a5Pointer);
+  calloc.free(a6Pointer);
+  calloc.free(a7Pointer);
+  calloc.free(a8Pointer);
+  calloc.free(a9Pointer);
+}
+
+final passUnion9BytesNestedIntx10 = ffiTestFunctions.lookupFunction<
+    Double Function(
+        Union9BytesNestedInt,
+        Union9BytesNestedInt,
+        Union9BytesNestedInt,
+        Union9BytesNestedInt,
+        Union9BytesNestedInt,
+        Union9BytesNestedInt,
+        Union9BytesNestedInt,
+        Union9BytesNestedInt,
+        Union9BytesNestedInt,
+        Union9BytesNestedInt),
+    double Function(
+        Union9BytesNestedInt,
+        Union9BytesNestedInt,
+        Union9BytesNestedInt,
+        Union9BytesNestedInt,
+        Union9BytesNestedInt,
+        Union9BytesNestedInt,
+        Union9BytesNestedInt,
+        Union9BytesNestedInt,
+        Union9BytesNestedInt,
+        Union9BytesNestedInt)>("PassUnion9BytesNestedIntx10");
+
+/// Mixed-size union argument.
+void testPassUnion9BytesNestedIntx10() {
+  final a0Pointer = calloc<Union9BytesNestedInt>();
+  final Union9BytesNestedInt a0 = a0Pointer.ref;
+  final a1Pointer = calloc<Union9BytesNestedInt>();
+  final Union9BytesNestedInt a1 = a1Pointer.ref;
+  final a2Pointer = calloc<Union9BytesNestedInt>();
+  final Union9BytesNestedInt a2 = a2Pointer.ref;
+  final a3Pointer = calloc<Union9BytesNestedInt>();
+  final Union9BytesNestedInt a3 = a3Pointer.ref;
+  final a4Pointer = calloc<Union9BytesNestedInt>();
+  final Union9BytesNestedInt a4 = a4Pointer.ref;
+  final a5Pointer = calloc<Union9BytesNestedInt>();
+  final Union9BytesNestedInt a5 = a5Pointer.ref;
+  final a6Pointer = calloc<Union9BytesNestedInt>();
+  final Union9BytesNestedInt a6 = a6Pointer.ref;
+  final a7Pointer = calloc<Union9BytesNestedInt>();
+  final Union9BytesNestedInt a7 = a7Pointer.ref;
+  final a8Pointer = calloc<Union9BytesNestedInt>();
+  final Union9BytesNestedInt a8 = a8Pointer.ref;
+  final a9Pointer = calloc<Union9BytesNestedInt>();
+  final Union9BytesNestedInt a9 = a9Pointer.ref;
+
+  a0.a0.a0 = -1;
+  a0.a0.a1 = 2;
+  a0.a0.a2 = -3;
+  a1.a0.a0 = 4;
+  a1.a0.a1 = -5;
+  a1.a0.a2 = 6;
+  a2.a0.a0 = -7;
+  a2.a0.a1 = 8;
+  a2.a0.a2 = -9;
+  a3.a0.a0 = 10;
+  a3.a0.a1 = -11;
+  a3.a0.a2 = 12;
+  a4.a0.a0 = -13;
+  a4.a0.a1 = 14;
+  a4.a0.a2 = -15;
+  a5.a0.a0 = 16;
+  a5.a0.a1 = -17;
+  a5.a0.a2 = 18;
+  a6.a0.a0 = -19;
+  a6.a0.a1 = 20;
+  a6.a0.a2 = -21;
+  a7.a0.a0 = 22;
+  a7.a0.a1 = -23;
+  a7.a0.a2 = 24;
+  a8.a0.a0 = -25;
+  a8.a0.a1 = 26;
+  a8.a0.a2 = -27;
+  a9.a0.a0 = 28;
+  a9.a0.a1 = -29;
+  a9.a0.a2 = 30;
+
+  final result =
+      passUnion9BytesNestedIntx10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  print("result = $result");
+
+  Expect.approxEquals(15.0, result);
+
+  calloc.free(a0Pointer);
+  calloc.free(a1Pointer);
+  calloc.free(a2Pointer);
+  calloc.free(a3Pointer);
+  calloc.free(a4Pointer);
+  calloc.free(a5Pointer);
+  calloc.free(a6Pointer);
+  calloc.free(a7Pointer);
+  calloc.free(a8Pointer);
+  calloc.free(a9Pointer);
+}
+
+final passUnion16BytesNestedInlineArrayFloatx10 =
+    ffiTestFunctions.lookupFunction<
+            Double Function(
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat),
+            double Function(
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat,
+                Union16BytesNestedInlineArrayFloat)>(
+        "PassUnion16BytesNestedInlineArrayFloatx10");
+
+/// Union with homogenous floats.
+void testPassUnion16BytesNestedInlineArrayFloatx10() {
+  final a0Pointer = calloc<Union16BytesNestedInlineArrayFloat>();
+  final Union16BytesNestedInlineArrayFloat a0 = a0Pointer.ref;
+  final a1Pointer = calloc<Union16BytesNestedInlineArrayFloat>();
+  final Union16BytesNestedInlineArrayFloat a1 = a1Pointer.ref;
+  final a2Pointer = calloc<Union16BytesNestedInlineArrayFloat>();
+  final Union16BytesNestedInlineArrayFloat a2 = a2Pointer.ref;
+  final a3Pointer = calloc<Union16BytesNestedInlineArrayFloat>();
+  final Union16BytesNestedInlineArrayFloat a3 = a3Pointer.ref;
+  final a4Pointer = calloc<Union16BytesNestedInlineArrayFloat>();
+  final Union16BytesNestedInlineArrayFloat a4 = a4Pointer.ref;
+  final a5Pointer = calloc<Union16BytesNestedInlineArrayFloat>();
+  final Union16BytesNestedInlineArrayFloat a5 = a5Pointer.ref;
+  final a6Pointer = calloc<Union16BytesNestedInlineArrayFloat>();
+  final Union16BytesNestedInlineArrayFloat a6 = a6Pointer.ref;
+  final a7Pointer = calloc<Union16BytesNestedInlineArrayFloat>();
+  final Union16BytesNestedInlineArrayFloat a7 = a7Pointer.ref;
+  final a8Pointer = calloc<Union16BytesNestedInlineArrayFloat>();
+  final Union16BytesNestedInlineArrayFloat a8 = a8Pointer.ref;
+  final a9Pointer = calloc<Union16BytesNestedInlineArrayFloat>();
+  final Union16BytesNestedInlineArrayFloat a9 = a9Pointer.ref;
+
+  a0.a0[0] = -1.0;
+  a0.a0[1] = 2.0;
+  a0.a0[2] = -3.0;
+  a0.a0[3] = 4.0;
+  a1.a0[0] = -5.0;
+  a1.a0[1] = 6.0;
+  a1.a0[2] = -7.0;
+  a1.a0[3] = 8.0;
+  a2.a0[0] = -9.0;
+  a2.a0[1] = 10.0;
+  a2.a0[2] = -11.0;
+  a2.a0[3] = 12.0;
+  a3.a0[0] = -13.0;
+  a3.a0[1] = 14.0;
+  a3.a0[2] = -15.0;
+  a3.a0[3] = 16.0;
+  a4.a0[0] = -17.0;
+  a4.a0[1] = 18.0;
+  a4.a0[2] = -19.0;
+  a4.a0[3] = 20.0;
+  a5.a0[0] = -21.0;
+  a5.a0[1] = 22.0;
+  a5.a0[2] = -23.0;
+  a5.a0[3] = 24.0;
+  a6.a0[0] = -25.0;
+  a6.a0[1] = 26.0;
+  a6.a0[2] = -27.0;
+  a6.a0[3] = 28.0;
+  a7.a0[0] = -29.0;
+  a7.a0[1] = 30.0;
+  a7.a0[2] = -31.0;
+  a7.a0[3] = 32.0;
+  a8.a0[0] = -33.0;
+  a8.a0[1] = 34.0;
+  a8.a0[2] = -35.0;
+  a8.a0[3] = 36.0;
+  a9.a0[0] = -37.0;
+  a9.a0[1] = 38.0;
+  a9.a0[2] = -39.0;
+  a9.a0[3] = 40.0;
+
+  final result = passUnion16BytesNestedInlineArrayFloatx10(
+      a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  print("result = $result");
+
+  Expect.approxEquals(20.0, result);
+
+  calloc.free(a0Pointer);
+  calloc.free(a1Pointer);
+  calloc.free(a2Pointer);
+  calloc.free(a3Pointer);
+  calloc.free(a4Pointer);
+  calloc.free(a5Pointer);
+  calloc.free(a6Pointer);
+  calloc.free(a7Pointer);
+  calloc.free(a8Pointer);
+  calloc.free(a9Pointer);
+}
+
+final passUnion16BytesNestedFloatx10 = ffiTestFunctions.lookupFunction<
+    Double Function(
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat),
+    double Function(
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat,
+        Union16BytesNestedFloat)>("PassUnion16BytesNestedFloatx10");
+
+/// Union with homogenous floats.
+void testPassUnion16BytesNestedFloatx10() {
+  final a0Pointer = calloc<Union16BytesNestedFloat>();
+  final Union16BytesNestedFloat a0 = a0Pointer.ref;
+  final a1Pointer = calloc<Union16BytesNestedFloat>();
+  final Union16BytesNestedFloat a1 = a1Pointer.ref;
+  final a2Pointer = calloc<Union16BytesNestedFloat>();
+  final Union16BytesNestedFloat a2 = a2Pointer.ref;
+  final a3Pointer = calloc<Union16BytesNestedFloat>();
+  final Union16BytesNestedFloat a3 = a3Pointer.ref;
+  final a4Pointer = calloc<Union16BytesNestedFloat>();
+  final Union16BytesNestedFloat a4 = a4Pointer.ref;
+  final a5Pointer = calloc<Union16BytesNestedFloat>();
+  final Union16BytesNestedFloat a5 = a5Pointer.ref;
+  final a6Pointer = calloc<Union16BytesNestedFloat>();
+  final Union16BytesNestedFloat a6 = a6Pointer.ref;
+  final a7Pointer = calloc<Union16BytesNestedFloat>();
+  final Union16BytesNestedFloat a7 = a7Pointer.ref;
+  final a8Pointer = calloc<Union16BytesNestedFloat>();
+  final Union16BytesNestedFloat a8 = a8Pointer.ref;
+  final a9Pointer = calloc<Union16BytesNestedFloat>();
+  final Union16BytesNestedFloat a9 = a9Pointer.ref;
+
+  a0.a0.a0 = -1.0;
+  a0.a0.a1 = 2.0;
+  a1.a0.a0 = -3.0;
+  a1.a0.a1 = 4.0;
+  a2.a0.a0 = -5.0;
+  a2.a0.a1 = 6.0;
+  a3.a0.a0 = -7.0;
+  a3.a0.a1 = 8.0;
+  a4.a0.a0 = -9.0;
+  a4.a0.a1 = 10.0;
+  a5.a0.a0 = -11.0;
+  a5.a0.a1 = 12.0;
+  a6.a0.a0 = -13.0;
+  a6.a0.a1 = 14.0;
+  a7.a0.a0 = -15.0;
+  a7.a0.a1 = 16.0;
+  a8.a0.a0 = -17.0;
+  a8.a0.a1 = 18.0;
+  a9.a0.a0 = -19.0;
+  a9.a0.a1 = 20.0;
+
+  final result =
+      passUnion16BytesNestedFloatx10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  print("result = $result");
+
+  Expect.approxEquals(10.0, result);
+
+  calloc.free(a0Pointer);
+  calloc.free(a1Pointer);
+  calloc.free(a2Pointer);
+  calloc.free(a3Pointer);
+  calloc.free(a4Pointer);
+  calloc.free(a5Pointer);
+  calloc.free(a6Pointer);
+  calloc.free(a7Pointer);
+  calloc.free(a8Pointer);
+  calloc.free(a9Pointer);
+}
+
 final returnStruct1ByteInt = ffiTestFunctions.lookupFunction<
     Struct1ByteInt Function(Int8),
     Struct1ByteInt Function(int)>("ReturnStruct1ByteInt");
@@ -7406,6 +7908,88 @@
   Expect.approxEquals(a1, result.a1);
 }
 
+final returnUnion4BytesMixed = ffiTestFunctions.lookupFunction<
+    Union4BytesMixed Function(Uint32),
+    Union4BytesMixed Function(int)>("ReturnUnion4BytesMixed");
+
+/// Returning a mixed integer/float union.
+void testReturnUnion4BytesMixed() {
+  int a0;
+
+  a0 = 1;
+
+  final result = returnUnion4BytesMixed(a0);
+
+  print("result = $result");
+
+  Expect.equals(a0, result.a0);
+}
+
+final returnUnion8BytesNestedFloat = ffiTestFunctions.lookupFunction<
+    Union8BytesNestedFloat Function(Double),
+    Union8BytesNestedFloat Function(double)>("ReturnUnion8BytesNestedFloat");
+
+/// Returning a floating point only union.
+void testReturnUnion8BytesNestedFloat() {
+  double a0;
+
+  a0 = -1.0;
+
+  final result = returnUnion8BytesNestedFloat(a0);
+
+  print("result = $result");
+
+  Expect.approxEquals(a0, result.a0);
+}
+
+final returnUnion9BytesNestedInt = ffiTestFunctions.lookupFunction<
+    Union9BytesNestedInt Function(Struct8BytesInt),
+    Union9BytesNestedInt Function(
+        Struct8BytesInt)>("ReturnUnion9BytesNestedInt");
+
+/// Returning a mixed-size union.
+void testReturnUnion9BytesNestedInt() {
+  final a0Pointer = calloc<Struct8BytesInt>();
+  final Struct8BytesInt a0 = a0Pointer.ref;
+
+  a0.a0 = -1;
+  a0.a1 = 2;
+  a0.a2 = -3;
+
+  final result = returnUnion9BytesNestedInt(a0);
+
+  print("result = $result");
+
+  Expect.equals(a0.a0, result.a0.a0);
+  Expect.equals(a0.a1, result.a0.a1);
+  Expect.equals(a0.a2, result.a0.a2);
+
+  calloc.free(a0Pointer);
+}
+
+final returnUnion16BytesNestedFloat = ffiTestFunctions.lookupFunction<
+    Union16BytesNestedFloat Function(Struct8BytesHomogeneousFloat),
+    Union16BytesNestedFloat Function(
+        Struct8BytesHomogeneousFloat)>("ReturnUnion16BytesNestedFloat");
+
+/// Returning union with homogenous floats.
+void testReturnUnion16BytesNestedFloat() {
+  final a0Pointer = calloc<Struct8BytesHomogeneousFloat>();
+  final Struct8BytesHomogeneousFloat a0 = a0Pointer.ref;
+
+  a0.a0 = -1.0;
+  a0.a1 = 2.0;
+
+  final result = returnUnion16BytesNestedFloat(a0);
+
+  print("result = $result");
+
+  Expect.approxEquals(a0.a0, result.a0.a0);
+  Expect.approxEquals(a0.a1, result.a0.a1);
+
+  calloc.free(a0Pointer);
+}
+
 final returnStructArgumentStruct1ByteInt = ffiTestFunctions.lookupFunction<
     Struct1ByteInt Function(Struct1ByteInt),
     Struct1ByteInt Function(
diff --git a/tests/ffi_2/generator/c_types.dart b/tests/ffi_2/generator/c_types.dart
index df3b44b..7236aed 100644
--- a/tests/ffi_2/generator/c_types.dart
+++ b/tests/ffi_2/generator/c_types.dart
@@ -2,6 +2,8 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+import 'dart:math' as math;
+
 import 'utils.dart';
 
 const int8 = FundamentalType(PrimitiveType.int8);
@@ -152,36 +154,70 @@
   return result;
 }
 
-class StructType extends CType {
+abstract class CompositeType extends CType {
   final List<Member> members;
 
-  final int? packing;
-
   /// To disambiguate same size structs.
   final String suffix;
 
   /// To override names.
   final String overrideName;
 
-  StructType(List<CType> memberTypes, {int? this.packing})
+  CompositeType(List<CType> memberTypes)
       : this.members = generateMemberNames(memberTypes),
         this.suffix = "",
         this.overrideName = "";
-  StructType.disambiguate(List<CType> memberTypes, this.suffix,
-      {int? this.packing})
+  CompositeType.disambiguate(List<CType> memberTypes, this.suffix)
       : this.members = generateMemberNames(memberTypes),
         this.overrideName = "";
-  StructType.override(List<CType> memberTypes, this.overrideName,
-      {int? this.packing})
+  CompositeType.override(List<CType> memberTypes, this.overrideName)
       : this.members = generateMemberNames(memberTypes),
         this.suffix = "";
 
   List<CType> get memberTypes => members.map((a) => a.type).toList();
 
+  String get name;
+
   String get cType => name;
   String get dartCType => name;
   String get dartType => name;
   String get dartStructFieldAnnotation => "";
+  String get cKeyword;
+  String get dartSuperClass;
+
+  bool get isOnlyFloatingPoint =>
+      !memberTypes.map((e) => e.isOnlyFloatingPoint).contains(false);
+  bool get isOnlyInteger =>
+      !memberTypes.map((e) => e.isOnlyInteger).contains(false);
+
+  bool get isMixed => !isOnlyInteger && !isOnlyFloatingPoint;
+
+  bool get hasNestedStructs =>
+      members.map((e) => e.type is StructType).contains(true);
+
+  bool get hasInlineArrays =>
+      members.map((e) => e.type is FixedLengthArrayType).contains(true);
+
+  bool get hasMultiDimensionalInlineArrays => members
+      .map((e) => e.type)
+      .whereType<FixedLengthArrayType>()
+      .where((e) => e.isMulti)
+      .isNotEmpty;
+}
+
+class StructType extends CompositeType {
+  final int? packing;
+
+  StructType(List<CType> memberTypes, {int? this.packing}) : super(memberTypes);
+  StructType.disambiguate(List<CType> memberTypes, String suffix,
+      {int? this.packing})
+      : super.disambiguate(memberTypes, suffix);
+  StructType.override(List<CType> memberTypes, String overrideName,
+      {int? this.packing})
+      : super.override(memberTypes, overrideName);
+
+  String get cKeyword => "struct";
+  String get dartSuperClass => "Struct";
 
   bool get hasSize =>
       !memberTypes.map((e) => e.hasSize).contains(false) && !hasPadding;
@@ -201,30 +237,11 @@
     return members[0].type.size < members[1].type.size;
   }
 
-  bool get hasNestedStructs =>
-      members.map((e) => e.type is StructType).contains(true);
-
-  bool get hasInlineArrays =>
-      members.map((e) => e.type is FixedLengthArrayType).contains(true);
-
-  bool get hasMultiDimensionalInlineArrays => members
-      .map((e) => e.type)
-      .whereType<FixedLengthArrayType>()
-      .where((e) => e.isMulti)
-      .isNotEmpty;
-
   /// All members have the same type.
   bool get isHomogeneous => memberTypes.toSet().length == 1;
 
-  bool get isOnlyFloatingPoint =>
-      !memberTypes.map((e) => e.isOnlyFloatingPoint).contains(false);
-  bool get isOnlyInteger =>
-      !memberTypes.map((e) => e.isOnlyInteger).contains(false);
-
-  bool get isMixed => !isOnlyInteger && !isOnlyFloatingPoint;
-
   String get name {
-    String result = "Struct";
+    String result = dartSuperClass;
     if (overrideName != "") {
       return result + overrideName;
     }
@@ -264,6 +281,46 @@
   }
 }
 
+class UnionType extends CompositeType {
+  UnionType(List<CType> memberTypes) : super(memberTypes);
+
+  String get cKeyword => "union";
+  String get dartSuperClass => "Union";
+
+  bool get hasSize => !memberTypes.map((e) => e.hasSize).contains(false);
+  int get size => memberTypes.fold(0, (int acc, e) => math.max(acc, e.size));
+
+  String get name {
+    String result = dartSuperClass;
+    if (overrideName != "") {
+      return result + overrideName;
+    }
+    if (hasSize) {
+      result += "${size}Byte" + (size != 1 ? "s" : "");
+    }
+    if (hasNestedStructs) {
+      result += "Nested";
+    }
+    if (hasInlineArrays) {
+      result += "InlineArray";
+      if (hasMultiDimensionalInlineArrays) {
+        result += "MultiDimensional";
+      }
+    }
+    if (members.length == 0) {
+      // No suffix.
+    } else if (isOnlyFloatingPoint) {
+      result += "Float";
+    } else if (isOnlyInteger) {
+      result += "Int";
+    } else {
+      result += "Mixed";
+    }
+    result += suffix;
+    return result;
+  }
+}
+
 class FixedLengthArrayType extends CType {
   final CType elementType;
   final int length;
@@ -360,15 +417,20 @@
   /// A suitable name based on the signature.
   String get cName {
     String result = "";
-    if (arguments.containsStructs && returnValue is FundamentalType) {
+    if (arguments.containsComposites && returnValue is FundamentalType) {
       result = "Pass";
     } else if (returnValue is StructType &&
         argumentTypes.contains(returnValue)) {
       result = "ReturnStructArgument";
+    } else if (returnValue is UnionType &&
+        argumentTypes.contains(returnValue)) {
+      result = "ReturnUnionArgument";
     } else if (returnValue is StructType) {
       if (arguments.length == (returnValue as StructType).members.length) {
         return "Return${returnValue.dartCType}";
       }
+    } else if (returnValue is UnionType && arguments.length == 1) {
+      return "Return${returnValue.dartCType}";
     } else {
       result = "Uncategorized";
     }
@@ -392,5 +454,6 @@
 }
 
 extension MemberList on List<Member> {
-  bool get containsStructs => map((m) => m.type is StructType).contains(true);
+  bool get containsComposites =>
+      map((m) => m.type is CompositeType).contains(true);
 }
diff --git a/tests/ffi_2/generator/structs_by_value_tests_configuration.dart b/tests/ffi_2/generator/structs_by_value_tests_configuration.dart
index 1ad3c11..07b5fd1 100644
--- a/tests/ffi_2/generator/structs_by_value_tests_configuration.dart
+++ b/tests/ffi_2/generator/structs_by_value_tests_configuration.dart
@@ -5,6 +5,12 @@
 import 'c_types.dart';
 
 final functions = [
+  ...functionsStructArguments,
+  ...functionsStructReturn,
+  ...functionsReturnArgument,
+];
+
+final functionsStructArguments = [
   FunctionType(List.filled(10, struct1byteInt), int64, """
 Smallest struct with data.
 10 struct arguments will exhaust available registers."""),
@@ -358,6 +364,19 @@
       double_,
       """
 Check alignment of packed struct array in non-packed struct."""),
+  FunctionType(List.filled(10, union4bytesMixed), double_, """
+Check placement of mixed integer/float union."""),
+  FunctionType(List.filled(10, union8bytesFloat), double_, """
+Check placement of mixed floats union."""),
+  FunctionType(List.filled(10, union12bytesInt), double_, """
+Mixed-size union argument."""),
+  FunctionType(List.filled(10, union16bytesFloat), double_, """
+Union with homogenous floats."""),
+  FunctionType(List.filled(10, union16bytesFloat2), double_, """
+Union with homogenous floats."""),
+];
+
+final functionsStructReturn = [
   FunctionType(struct1byteInt.memberTypes, struct1byteInt, """
 Smallest struct with data."""),
   FunctionType(struct3bytesInt.memberTypes, struct3bytesInt, """
@@ -421,6 +440,29 @@
 Struct with mis-aligned member.
 Tests backfilling of CPU and FPU registers."""),
   FunctionType(
+      [union4bytesMixed.memberTypes.first],
+      union4bytesMixed,
+      """
+Returning a mixed integer/float union."""),
+  FunctionType(
+      [union8bytesFloat.memberTypes.first],
+      union8bytesFloat,
+      """
+Returning a floating point only union."""),
+  FunctionType(
+      [union12bytesInt.memberTypes.first],
+      union12bytesInt,
+      """
+Returning a mixed-size union."""),
+  FunctionType(
+      [union16bytesFloat2.memberTypes.first],
+      union16bytesFloat2,
+      """
+Returning union with homogenous floats."""),
+];
+
+final functionsReturnArgument = [
+  FunctionType(
       [struct1byteInt],
       struct1byteInt,
       """
@@ -515,7 +557,7 @@
 Return big irregular struct as smoke test."""),
 ];
 
-final structs = [
+final compounds = [
   struct1byteInt,
   struct3bytesInt,
   struct3bytesInt2,
@@ -574,6 +616,11 @@
   struct8bytesPacked,
   struct9bytesPacked,
   struct15bytesPacked,
+  union4bytesMixed,
+  union8bytesFloat,
+  union12bytesInt,
+  union16bytesFloat,
+  union16bytesFloat2,
 ];
 
 final struct1byteInt = StructType([int8]);
@@ -741,3 +788,23 @@
 /// inline array, but not in the subsequent ones.
 final struct15bytesPacked =
     StructType([FixedLengthArrayType(struct5bytesPacked, 3)]);
+
+/// Mixed integer and float. Tests whether calling conventions put this in
+/// integer registers or not.
+final union4bytesMixed = UnionType([uint32, float]);
+
+/// Different types of float. Tests whether calling conventions put this in
+/// FPU registers or not.
+final union8bytesFloat = UnionType([double_, struct8bytesFloat]);
+
+/// This union has a size of 12, because of the 4-byte alignment of the first
+/// member.
+final union12bytesInt = UnionType([struct8bytesInt, struct9bytesInt]);
+
+/// This union has homogenous floats of the same sizes.
+final union16bytesFloat =
+    UnionType([FixedLengthArrayType(float, 4), struct16bytesFloat]);
+
+/// This union has homogenous floats of different sizes.
+final union16bytesFloat2 =
+    UnionType([struct8bytesFloat, struct12bytesFloat, struct16bytesFloat]);
diff --git a/tests/ffi_2/generator/structs_by_value_tests_generator.dart b/tests/ffi_2/generator/structs_by_value_tests_generator.dart
index c66dbea..0ab7739 100644
--- a/tests/ffi_2/generator/structs_by_value_tests_generator.dart
+++ b/tests/ffi_2/generator/structs_by_value_tests_generator.dart
@@ -24,14 +24,19 @@
 
 extension on FunctionType {
   TestType get testType {
-    if (arguments.containsStructs && returnValue is FundamentalType) {
+    if (arguments.containsComposites && returnValue is FundamentalType) {
       return TestType.structArguments;
     }
-    if (returnValue is StructType && argumentTypes.contains(returnValue)) {
+    if (returnValue is CompositeType && argumentTypes.contains(returnValue)) {
       return TestType.structReturnArgument;
     }
     if (returnValue is StructType) {
-      if (arguments.length == (returnValue as StructType).members.length) {
+      if (arguments.length == (returnValue as CompositeType).members.length) {
+        return TestType.structReturn;
+      }
+    }
+    if (returnValue is UnionType) {
+      if (arguments.length == 1) {
         return TestType.structReturn;
       }
     }
@@ -52,7 +57,8 @@
         return "<< $variableName";
 
       case StructType:
-        final this_ = this as StructType;
+      case UnionType:
+        final this_ = this as CompositeType;
         return this_.members.coutExpression("$variableName.");
 
       case FixedLengthArrayType:
@@ -102,6 +108,12 @@
         final this_ = this as StructType;
         return this_.members.addToResultStatements("$variableName.");
 
+      case UnionType:
+        final this_ = this as UnionType;
+        final member = this_.members.first;
+        return member.type
+            .addToResultStatements("$variableName.${member.name}");
+
       case FixedLengthArrayType:
         final this_ = this as FixedLengthArrayType;
         final indices = [for (var i = 0; i < this_.length; i += 1) i];
@@ -139,6 +151,12 @@
         final this_ = this as StructType;
         return this_.members.assignValueStatements(a, "$variableName.");
 
+      case UnionType:
+        final this_ = this as UnionType;
+        final member = this_.members.first;
+        return member.type
+            .assignValueStatements(a, "$variableName.${member.name}");
+
       case FixedLengthArrayType:
         final this_ = this as FixedLengthArrayType;
         final indices = [for (var i = 0; i < this_.length; i += 1) i];
@@ -224,6 +242,7 @@
         return "${dartType} ${variableName};\n";
 
       case StructType:
+      case UnionType:
         return """
 final ${variableName}Pointer = calloc<$dartType>();
 final ${dartType} ${variableName} = ${variableName}Pointer.ref;
@@ -245,6 +264,7 @@
         return "${dartType} ${variableName} = 0.0;\n";
 
       case StructType:
+      case UnionType:
         if (structsAsPointers) {
           return "Pointer<${dartType}> ${variableName}Pointer = nullptr;\n";
         } else {
@@ -278,6 +298,7 @@
         return "";
 
       case StructType:
+      case UnionType:
         return "calloc.free(${variableName}Pointer);\n";
     }
 
@@ -298,6 +319,7 @@
     switch (this.runtimeType) {
       case FundamentalType:
       case StructType:
+      case UnionType:
         return "${cType} ${variableName};\n";
     }
 
@@ -432,7 +454,8 @@
         return variableName;
 
       case StructType:
-        final this_ = this as StructType;
+      case UnionType:
+        final this_ = this as CompositeType;
         return this_.members.firstArgumentName("$variableName.");
 
       case FixedLengthArrayType:
@@ -453,9 +476,12 @@
   }
 }
 
-extension on StructType {
+extension on CompositeType {
   String dartClass(bool nnbd) {
-    final packingAnnotation = hasPacking ? "@Packed(${packing})" : "";
+    final self = this;
+    final packingAnnotation = (self is StructType) && self.hasPacking
+        ? "@Packed(${self.packing})"
+        : "";
     String dartFields = "";
     for (final member in members) {
       dartFields += "${member.dartStructField(nnbd)}\n\n";
@@ -479,7 +505,7 @@
     }).join(", ");
     return """
     $packingAnnotation
-    class $name extends Struct {
+    class $name extends $dartSuperClass {
       $dartFields
 
       String toString() => "($toStringBody)";
@@ -488,16 +514,20 @@
   }
 
   String get cDefinition {
-    final packingPragmaPush =
-        hasPacking ? "#pragma pack(push, ${packing})" : "";
-    final packingPragmaPop = hasPacking ? "#pragma pack(pop)" : "";
+    final self = this;
+    final packingPragmaPush = (self is StructType) && self.hasPacking
+        ? "#pragma pack(push, ${self.packing})"
+        : "";
+    final packingPragmaPop =
+        (self is StructType) && self.hasPacking ? "#pragma pack(pop)" : "";
+
     String cFields = "";
     for (final member in members) {
       cFields += "  ${member.cStructField}\n";
     }
     return """
     $packingPragmaPush
-    struct $name {
+    $cKeyword $name {
       $cFields
     };
     $packingPragmaPop
@@ -727,7 +757,7 @@
         arguments.map((e) => "${e.type.cType} ${e.name}").join(", ");
 
     return """
-    // Used for testing structs by value.
+    // Used for testing structs and unions by value.
     ${reason.makeCComment()}
     DART_EXPORT ${returnValue.cType} $cName($argumentss) {
       std::cout << \"$cName\" ${arguments.coutExpression()} << \"\\n\";
@@ -779,7 +809,7 @@
     }
 
     return """
-    // Used for testing structs by value.
+    // Used for testing structs and unions by value.
     ${reason.makeCComment()}
     DART_EXPORT intptr_t
     Test$cName(
@@ -859,7 +889,7 @@
       }
     }
     """);
-    buffer.writeAll(structs.map((e) => e.dartClass(nnbd)));
+    buffer.writeAll(compounds.map((e) => e.dartClass(nnbd)));
     buffer.writeAll(functions.map((e) => e.dartCallCode));
 
     final path = callTestPath(nnbd);
@@ -984,7 +1014,7 @@
   final StringBuffer buffer = StringBuffer();
   buffer.write(headerC);
 
-  buffer.writeAll(structs.map((e) => e.cDefinition));
+  buffer.writeAll(compounds.map((e) => e.cDefinition));
   buffer.writeAll(functions.map((e) => e.cCallCode));
   buffer.writeAll(functions.map((e) => e.cCallbackCode));
 
diff --git a/tests/language/const_functions/const_functions_return_test.dart b/tests/language/const_functions/const_functions_return_test.dart
index 793c629..ffc1667 100644
--- a/tests/language/const_functions/const_functions_return_test.dart
+++ b/tests/language/const_functions/const_functions_return_test.dart
@@ -32,9 +32,22 @@
   return null;
 }
 
+const var5 = fn5();
+//           ^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+// [web] Constant evaluation error:
+int fn5() {
+  try {
+    return throw 1;
+  } on int {
+    return 2;
+  }
+}
+
 void main() {
   Expect.equals((var1 as dynamic), null);
   Expect.equals((var2 as dynamic), null);
   Expect.equals(var3, null);
   Expect.equals(var4, null);
+  Expect.equals(var5, 2);
 }
diff --git a/tests/language/function/regress_45601_test.dart b/tests/language/function/regress_45601_test.dart
new file mode 100644
index 0000000..0a096f0
--- /dev/null
+++ b/tests/language/function/regress_45601_test.dart
@@ -0,0 +1,60 @@
+// 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:expect/expect.dart";
+
+// Regression test for https://github.com/dart-lang/sdk/issues/45601
+// When comparing values of type `Function` for equality DDC was generating
+// code that would throw at runtime.
+
+class Wrapper {
+  Wrapper(this.function);
+
+  final Function function;
+
+  @override
+  bool operator ==(Object other) =>
+      other is Wrapper && function == other.function;
+
+  @override
+  int get hashCode => function.hashCode;
+}
+
+void main() {
+  final map = <Wrapper, int>{};
+  final ref = Wrapper(main);
+  map[ref] = 42;
+  Expect.equals(42, map[ref]);
+
+  testStaticEquality();
+  testDynamicEquality();
+}
+
+void fn<T>(T t) => null;
+
+/// Ensure `==` calls on function values that are statically typed as `Function`
+///  or `Function?` work as expected.
+void testStaticEquality() {
+  Function staticFunction = fn;
+  Expect.isTrue(staticFunction == fn);
+  Expect.isFalse(staticFunction == main);
+
+  Function? staticFunction2 = null;
+  Expect.isFalse(staticFunction2 == staticFunction);
+  staticFunction2 = fn;
+  Expect.isTrue(staticFunction2 == staticFunction);
+}
+
+/// Ensure `==` calls on function values that are statically typed as `dynamic`
+/// work as expected.
+void testDynamicEquality() {
+  dynamic dynamicFunction = fn;
+  Expect.isTrue(dynamicFunction == fn);
+  Expect.isFalse(dynamicFunction == main);
+
+  dynamic dynamicFunction2 = null;
+  Expect.isFalse(dynamicFunction2 == dynamicFunction);
+  dynamicFunction2 = fn;
+  Expect.isTrue(dynamicFunction2 == dynamicFunction);
+}
diff --git a/tests/language/interface/duplicate_interface_implements_test.dart b/tests/language/interface/duplicate_interface_implements_test.dart
index 4564aad..0c8555e 100644
--- a/tests/language/interface/duplicate_interface_implements_test.dart
+++ b/tests/language/interface/duplicate_interface_implements_test.dart
@@ -10,6 +10,7 @@
     , alib.InterfA
     //^^^^^^^^^^^^
     // [analyzer] COMPILE_TIME_ERROR.IMPLEMENTS_REPEATED
+    // [cfe] 'InterfA' can only be implemented once.
 {}
 
 main() {
diff --git a/tests/language/regress/regress34488_test.dart b/tests/language/regress/regress34488_test.dart
index d41f9407..ff76512 100644
--- a/tests/language/regress/regress34488_test.dart
+++ b/tests/language/regress/regress34488_test.dart
@@ -32,9 +32,8 @@
   // [analyzer] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
   // [cfe] The argument type 'String' can't be assigned to the parameter type 'int'.
   d.h(i: 'bad');
-  //  ^^^^^^^^
+  //     ^^^^^
   // [analyzer] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
-  //     ^
   // [cfe] The argument type 'String' can't be assigned to the parameter type 'int'.
   Object x = d.f(1);
   //           ^
diff --git a/tests/language/why_not_promoted/argument_type_not_assignable_nullability_error_test.dart b/tests/language/why_not_promoted/argument_type_not_assignable_nullability_error_test.dart
index e4a8ce7..c74a8b2 100644
--- a/tests/language/why_not_promoted/argument_type_not_assignable_nullability_error_test.dart
+++ b/tests/language/why_not_promoted/argument_type_not_assignable_nullability_error_test.dart
@@ -9,8 +9,8 @@
 class C1 {
   int? bad;
   //   ^^^
-  // [context 21] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 47] 'bad' refers to a property so it couldn't be promoted.
+  // [context 22] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
+  // [context 48] 'bad' refers to a property so it couldn't be promoted.
   f(int i) {}
 }
 
@@ -18,16 +18,16 @@
   if (c.bad == null) return;
   c.f(c.bad);
   //  ^^^^^
-  // [analyzer 21] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
+  // [analyzer 22] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
   //    ^
-  // [cfe 47] The argument type 'int?' can't be assigned to the parameter type 'int' because 'int?' is nullable and 'int' isn't.
+  // [cfe 48] The argument type 'int?' can't be assigned to the parameter type 'int' because 'int?' is nullable and 'int' isn't.
 }
 
 class C2 {
   int? bad;
   //   ^^^
-  // [context 43] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 48] 'bad' refers to a property so it couldn't be promoted.
+  // [context 39] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
+  // [context 49] 'bad' refers to a property so it couldn't be promoted.
   f([int i = 0]) {}
 }
 
@@ -35,50 +35,50 @@
   if (c.bad == null) return;
   c.f(c.bad);
   //  ^^^^^
-  // [analyzer 43] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
+  // [analyzer 39] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
   //    ^
-  // [cfe 48] The argument type 'int?' can't be assigned to the parameter type 'int' because 'int?' is nullable and 'int' isn't.
+  // [cfe 49] The argument type 'int?' can't be assigned to the parameter type 'int' because 'int?' is nullable and 'int' isn't.
 }
 
 class C3 {
   int? bad;
   //   ^^^
-  // [context 7] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 49] 'bad' refers to a property so it couldn't be promoted.
+  // [context 6] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
+  // [context 50] 'bad' refers to a property so it couldn't be promoted.
   f({required int i}) {}
 }
 
 required_named(C3 c) {
   if (c.bad == null) return;
   c.f(i: c.bad);
-  //  ^^^^^^^^
-  // [analyzer 7] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
+  //     ^^^^^
+  // [analyzer 6] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
   //       ^
-  // [cfe 49] The argument type 'int?' can't be assigned to the parameter type 'int' because 'int?' is nullable and 'int' isn't.
+  // [cfe 50] The argument type 'int?' can't be assigned to the parameter type 'int' because 'int?' is nullable and 'int' isn't.
 }
 
 class C4 {
   int? bad;
   //   ^^^
-  // [context 17] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 50] 'bad' refers to a property so it couldn't be promoted.
+  // [context 15] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
+  // [context 51] 'bad' refers to a property so it couldn't be promoted.
   f({int i = 0}) {}
 }
 
 optional_named(C4 c) {
   if (c.bad == null) return;
   c.f(i: c.bad);
-  //  ^^^^^^^^
-  // [analyzer 17] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
+  //     ^^^^^
+  // [analyzer 15] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
   //       ^
-  // [cfe 50] The argument type 'int?' can't be assigned to the parameter type 'int' because 'int?' is nullable and 'int' isn't.
+  // [cfe 51] The argument type 'int?' can't be assigned to the parameter type 'int' because 'int?' is nullable and 'int' isn't.
 }
 
 class C5 {
   List<int>? bad;
   //         ^^^
-  // [context 38] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 51] 'bad' refers to a property so it couldn't be promoted.
+  // [context 37] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
+  // [context 52] 'bad' refers to a property so it couldn't be promoted.
   f<T>(List<T> x) {}
 }
 
@@ -86,16 +86,16 @@
   if (c.bad == null) return;
   c.f(c.bad);
   //  ^^^^^
-  // [analyzer 38] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
+  // [analyzer 37] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
   //    ^
-  // [cfe 51] The argument type 'List<int>?' can't be assigned to the parameter type 'List<int>' because 'List<int>?' is nullable and 'List<int>' isn't.
+  // [cfe 52] The argument type 'List<int>?' can't be assigned to the parameter type 'List<int>' because 'List<int>?' is nullable and 'List<int>' isn't.
 }
 
 class C6 {
   int? bad;
   //   ^^^
-  // [context 5] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 52] 'bad' refers to a property so it couldn't be promoted.
+  // [context 4] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
+  // [context 53] 'bad' refers to a property so it couldn't be promoted.
   C6(int i);
 }
 
@@ -103,16 +103,16 @@
   if (c.bad == null) return null;
   return C6(c.bad);
   //        ^^^^^
-  // [analyzer 5] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
+  // [analyzer 4] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
   //          ^
-  // [cfe 52] The argument type 'int?' can't be assigned to the parameter type 'int' because 'int?' is nullable and 'int' isn't.
+  // [cfe 53] The argument type 'int?' can't be assigned to the parameter type 'int' because 'int?' is nullable and 'int' isn't.
 }
 
 class C7 {
   int? bad;
   //   ^^^
-  // [context 24] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 53] 'bad' refers to a property so it couldn't be promoted.
+  // [context 25] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
+  // [context 54] 'bad' refers to a property so it couldn't be promoted.
   C7(int i);
 }
 
@@ -120,16 +120,16 @@
   if (c.bad == null) return null;
   return new C7(c.bad);
   //            ^^^^^
-  // [analyzer 24] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
+  // [analyzer 25] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
   //              ^
-  // [cfe 53] The argument type 'int?' can't be assigned to the parameter type 'int' because 'int?' is nullable and 'int' isn't.
+  // [cfe 54] The argument type 'int?' can't be assigned to the parameter type 'int' because 'int?' is nullable and 'int' isn't.
 }
 
 class C8 {
   int? bad;
   //   ^^^
   // [context 32] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 54] 'bad' refers to a property so it couldn't be promoted.
+  // [context 55] 'bad' refers to a property so it couldn't be promoted.
 }
 
 userDefinableBinaryOpRhs(C8 c) {
@@ -138,7 +138,7 @@
   //  ^^^^^
   // [analyzer 32] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
   //    ^
-  // [cfe 54] A value of type 'int?' can't be assigned to a variable of type 'num' because 'int?' is nullable and 'num' isn't.
+  // [cfe 55] A value of type 'int?' can't be assigned to a variable of type 'num' because 'int?' is nullable and 'num' isn't.
 }
 
 class C9 {
@@ -180,10 +180,10 @@
 class C11 {
   bool? bad;
   //    ^^^
-  // [context 15] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 16] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 55] 'bad' refers to a property so it couldn't be promoted.
+  // [context 13] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
+  // [context 18] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
   // [context 56] 'bad' refers to a property so it couldn't be promoted.
+  // [context 57] 'bad' refers to a property so it couldn't be promoted.
   f(bool b) {}
 }
 
@@ -191,23 +191,23 @@
   if (c.bad == null) return;
   c.f(c.bad && b);
   //  ^^^^^
-  // [analyzer 16] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
+  // [analyzer 18] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
   //    ^
-  // [cfe 55] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
+  // [cfe 56] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
   c.f(b && c.bad);
   //       ^^^^^
-  // [analyzer 15] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
+  // [analyzer 13] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
   //         ^
-  // [cfe 56] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
+  // [cfe 57] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
 }
 
 class C12 {
   bool? bad;
   //    ^^^
-  // [context 4] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 33] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 57] 'bad' refers to a property so it couldn't be promoted.
+  // [context 5] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
+  // [context 36] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
   // [context 58] 'bad' refers to a property so it couldn't be promoted.
+  // [context 59] 'bad' refers to a property so it couldn't be promoted.
   f(bool b) {}
 }
 
@@ -215,51 +215,51 @@
   if (c.bad == null) return;
   c.f(c.bad || b);
   //  ^^^^^
-  // [analyzer 4] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
+  // [analyzer 5] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
   //    ^
-  // [cfe 57] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
+  // [cfe 58] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
   c.f(b || c.bad);
   //       ^^^^^
-  // [analyzer 33] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
+  // [analyzer 36] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
   //         ^
-  // [cfe 58] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
+  // [cfe 59] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
 }
 
 class C13 {
   bool? bad;
   //    ^^^
-  // [context 3] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 59] 'bad' refers to a property so it couldn't be promoted.
+  // [context 2] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
+  // [context 60] 'bad' refers to a property so it couldn't be promoted.
 }
 
 assertStatementCondition(C13 c) {
   if (c.bad == null) return;
   assert(c.bad);
   //     ^^^^^
-  // [analyzer 3] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
+  // [analyzer 2] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
   //       ^
-  // [cfe 59] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
+  // [cfe 60] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
 }
 
 class C14 {
   bool? bad;
   //    ^^^
-  // [context 13] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 60] 'bad' refers to a property so it couldn't be promoted.
+  // [context 8] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
+  // [context 61] 'bad' refers to a property so it couldn't be promoted.
   C14.assertInitializerCondition(C14 c)
       : bad = c.bad!,
         assert(c.bad);
         //     ^^^^^
-        // [analyzer 13] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
+        // [analyzer 8] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
         //       ^
-        // [cfe 60] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
+        // [cfe 61] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
 }
 
 class C15 {
   bool? bad;
   //    ^^^
-  // [context 46] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 61] 'bad' refers to a property so it couldn't be promoted.
+  // [context 47] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
+  // [context 62] 'bad' refers to a property so it couldn't be promoted.
   f(bool b) {}
 }
 
@@ -267,53 +267,53 @@
   if (c.bad == null) return;
   c.f(!c.bad);
   //   ^^^^^
-  // [analyzer 46] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
+  // [analyzer 47] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
   //     ^
-  // [cfe 61] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
+  // [cfe 62] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
 }
 
 class C16 {
   bool? bad;
   //    ^^^
   // [context 9] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 10] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 11] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 12] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 62] 'bad' refers to a property so it couldn't be promoted.
+  // [context 16] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
+  // [context 17] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
+  // [context 19] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
   // [context 63] 'bad' refers to a property so it couldn't be promoted.
   // [context 64] 'bad' refers to a property so it couldn't be promoted.
   // [context 65] 'bad' refers to a property so it couldn't be promoted.
+  // [context 66] 'bad' refers to a property so it couldn't be promoted.
 }
 
 forLoopCondition(C16 c) {
   if (c.bad == null) return;
   for (; c.bad;) {}
   //     ^^^^^
-  // [analyzer 10] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
+  // [analyzer 9] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
   //       ^
-  // [cfe 62] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
+  // [cfe 63] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
   [for (; c.bad;) null];
   //      ^^^^^
-  // [analyzer 11] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
+  // [analyzer 17] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
   //        ^
-  // [cfe 63] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
+  // [cfe 64] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
   ({for (; c.bad;) null});
   //       ^^^^^
-  // [analyzer 12] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
-  //         ^
-  // [cfe 64] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
-  ({for (; c.bad;) null: null});
-  //       ^^^^^
-  // [analyzer 9] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
+  // [analyzer 19] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
   //         ^
   // [cfe 65] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
+  ({for (; c.bad;) null: null});
+  //       ^^^^^
+  // [analyzer 16] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
+  //         ^
+  // [cfe 66] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
 }
 
 class C17 {
   bool? bad;
   //    ^^^
-  // [context 28] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 66] 'bad' refers to a property so it couldn't be promoted.
+  // [context 46] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
+  // [context 67] 'bad' refers to a property so it couldn't be promoted.
   f(int i) {}
 }
 
@@ -321,207 +321,207 @@
   if (c.bad == null) return;
   c.f(c.bad ? 1 : 2);
   //  ^^^^^
-  // [analyzer 28] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
+  // [analyzer 46] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
   //    ^
-  // [cfe 66] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
+  // [cfe 67] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
 }
 
 class C18 {
   bool? bad;
   //    ^^^
-  // [context 1] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 67] 'bad' refers to a property so it couldn't be promoted.
+  // [context 7] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
+  // [context 68] 'bad' refers to a property so it couldn't be promoted.
 }
 
 doLoopCondition(C18 c) {
   if (c.bad == null) return;
   do {} while (c.bad);
   //           ^^^^^
-  // [analyzer 1] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
+  // [analyzer 7] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
   //             ^
-  // [cfe 67] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
+  // [cfe 68] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
 }
 
 class C19 {
   bool? bad;
   //    ^^^
-  // [context 14] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
+  // [context 11] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
+  // [context 12] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
   // [context 23] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 27] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 44] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 68] 'bad' refers to a property so it couldn't be promoted.
+  // [context 28] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
   // [context 69] 'bad' refers to a property so it couldn't be promoted.
   // [context 70] 'bad' refers to a property so it couldn't be promoted.
   // [context 71] 'bad' refers to a property so it couldn't be promoted.
+  // [context 72] 'bad' refers to a property so it couldn't be promoted.
 }
 
 ifCondition(C19 c) {
   if (c.bad == null) return;
   if (c.bad) {}
   //  ^^^^^
-  // [analyzer 44] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
+  // [analyzer 23] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
   //    ^
-  // [cfe 68] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
+  // [cfe 69] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
   [if (c.bad) null];
   //   ^^^^^
-  // [analyzer 27] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
+  // [analyzer 28] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
   //     ^
-  // [cfe 69] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
+  // [cfe 70] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
   ({if (c.bad) null});
   //    ^^^^^
-  // [analyzer 23] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
-  //      ^
-  // [cfe 70] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
-  ({if (c.bad) null: null});
-  //    ^^^^^
-  // [analyzer 14] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
+  // [analyzer 11] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
   //      ^
   // [cfe 71] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
+  ({if (c.bad) null: null});
+  //    ^^^^^
+  // [analyzer 12] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
+  //      ^
+  // [cfe 72] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
 }
 
 class C20 {
   bool? bad;
   //    ^^^
-  // [context 19] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 72] 'bad' refers to a property so it couldn't be promoted.
+  // [context 21] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
+  // [context 73] 'bad' refers to a property so it couldn't be promoted.
 }
 
 whileCondition(C20 c) {
   if (c.bad == null) return;
   while (c.bad) {}
   //     ^^^^^
-  // [analyzer 19] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
+  // [analyzer 21] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
   //       ^
-  // [cfe 72] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
+  // [cfe 73] A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
 }
 
 class C21 {
   int? bad;
   //   ^^^
-  // [context 35] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 73] 'bad' refers to a property so it couldn't be promoted.
+  // [context 45] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
+  // [context 74] 'bad' refers to a property so it couldn't be promoted.
 }
 
 assignmentRhs(C21 c, int i) {
   if (c.bad == null) return;
   i = c.bad;
   //  ^^^^^
-  // [analyzer 35] COMPILE_TIME_ERROR.INVALID_ASSIGNMENT
+  // [analyzer 45] COMPILE_TIME_ERROR.INVALID_ASSIGNMENT
   //    ^
-  // [cfe 73] A value of type 'int?' can't be assigned to a variable of type 'int' because 'int?' is nullable and 'int' isn't.
+  // [cfe 74] A value of type 'int?' can't be assigned to a variable of type 'int' because 'int?' is nullable and 'int' isn't.
 }
 
 class C22 {
   int? bad;
   //   ^^^
-  // [context 34] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 74] 'bad' refers to a property so it couldn't be promoted.
+  // [context 43] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
+  // [context 75] 'bad' refers to a property so it couldn't be promoted.
 }
 
 variableInitializer(C22 c) {
   if (c.bad == null) return;
   int i = c.bad;
   //      ^^^^^
-  // [analyzer 34] COMPILE_TIME_ERROR.INVALID_ASSIGNMENT
+  // [analyzer 43] COMPILE_TIME_ERROR.INVALID_ASSIGNMENT
   //        ^
-  // [cfe 74] A value of type 'int?' can't be assigned to a variable of type 'int' because 'int?' is nullable and 'int' isn't.
+  // [cfe 75] A value of type 'int?' can't be assigned to a variable of type 'int' because 'int?' is nullable and 'int' isn't.
 }
 
 class C23 {
   int? bad;
   //   ^^^
-  // [context 25] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 75] 'bad' refers to a property so it couldn't be promoted.
+  // [context 24] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
+  // [context 76] 'bad' refers to a property so it couldn't be promoted.
   final int x;
   final int y;
   C23.constructorInitializer(C23 c)
       : x = c.bad!,
         y = c.bad;
         //  ^^^^^
-        // [analyzer 25] COMPILE_TIME_ERROR.FIELD_INITIALIZER_NOT_ASSIGNABLE
+        // [analyzer 24] COMPILE_TIME_ERROR.FIELD_INITIALIZER_NOT_ASSIGNABLE
         //    ^
-        // [cfe 75] A value of type 'int?' can't be assigned to a variable of type 'int' because 'int?' is nullable and 'int' isn't.
+        // [cfe 76] A value of type 'int?' can't be assigned to a variable of type 'int' because 'int?' is nullable and 'int' isn't.
 }
 
 class C24 {
   int? bad;
   //   ^^^
-  // [context 6] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 8] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 29] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 36] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 76] 'bad' refers to a property so it couldn't be promoted.
+  // [context 3] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
+  // [context 30] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
+  // [context 33] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
+  // [context 34] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
   // [context 77] 'bad' refers to a property so it couldn't be promoted.
   // [context 78] 'bad' refers to a property so it couldn't be promoted.
   // [context 79] 'bad' refers to a property so it couldn't be promoted.
+  // [context 80] 'bad' refers to a property so it couldn't be promoted.
 }
 
 forVariableInitializer(C24 c) {
   if (c.bad == null) return;
   for (int i = c.bad; false;) {}
   //           ^^^^^
-  // [analyzer 36] COMPILE_TIME_ERROR.INVALID_ASSIGNMENT
+  // [analyzer 34] COMPILE_TIME_ERROR.INVALID_ASSIGNMENT
   //             ^
-  // [cfe 76] A value of type 'int?' can't be assigned to a variable of type 'int' because 'int?' is nullable and 'int' isn't.
+  // [cfe 77] A value of type 'int?' can't be assigned to a variable of type 'int' because 'int?' is nullable and 'int' isn't.
   [for (int i = c.bad; false;) null];
   //            ^^^^^
-  // [analyzer 29] COMPILE_TIME_ERROR.INVALID_ASSIGNMENT
+  // [analyzer 33] COMPILE_TIME_ERROR.INVALID_ASSIGNMENT
   //              ^
-  // [cfe 77] A value of type 'int?' can't be assigned to a variable of type 'int' because 'int?' is nullable and 'int' isn't.
+  // [cfe 78] A value of type 'int?' can't be assigned to a variable of type 'int' because 'int?' is nullable and 'int' isn't.
   ({for (int i = c.bad; false;) null});
   //             ^^^^^
-  // [analyzer 8] COMPILE_TIME_ERROR.INVALID_ASSIGNMENT
-  //               ^
-  // [cfe 78] A value of type 'int?' can't be assigned to a variable of type 'int' because 'int?' is nullable and 'int' isn't.
-  ({for (int i = c.bad; false;) null: null});
-  //             ^^^^^
-  // [analyzer 6] COMPILE_TIME_ERROR.INVALID_ASSIGNMENT
+  // [analyzer 30] COMPILE_TIME_ERROR.INVALID_ASSIGNMENT
   //               ^
   // [cfe 79] A value of type 'int?' can't be assigned to a variable of type 'int' because 'int?' is nullable and 'int' isn't.
+  ({for (int i = c.bad; false;) null: null});
+  //             ^^^^^
+  // [analyzer 3] COMPILE_TIME_ERROR.INVALID_ASSIGNMENT
+  //               ^
+  // [cfe 80] A value of type 'int?' can't be assigned to a variable of type 'int' because 'int?' is nullable and 'int' isn't.
 }
 
 class C25 {
   int? bad;
   //   ^^^
-  // [context 30] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 39] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
+  // [context 35] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
   // [context 40] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 45] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 80] 'bad' refers to a property so it couldn't be promoted.
+  // [context 41] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
+  // [context 44] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
   // [context 81] 'bad' refers to a property so it couldn't be promoted.
   // [context 82] 'bad' refers to a property so it couldn't be promoted.
   // [context 83] 'bad' refers to a property so it couldn't be promoted.
+  // [context 84] 'bad' refers to a property so it couldn't be promoted.
 }
 
 forAssignmentInitializer(C25 c, int i) {
   if (c.bad == null) return;
   for (i = c.bad; false;) {}
   //       ^^^^^
-  // [analyzer 30] COMPILE_TIME_ERROR.INVALID_ASSIGNMENT
+  // [analyzer 35] COMPILE_TIME_ERROR.INVALID_ASSIGNMENT
   //         ^
-  // [cfe 80] A value of type 'int?' can't be assigned to a variable of type 'int' because 'int?' is nullable and 'int' isn't.
+  // [cfe 81] A value of type 'int?' can't be assigned to a variable of type 'int' because 'int?' is nullable and 'int' isn't.
   [for (i = c.bad; false;) null];
   //        ^^^^^
-  // [analyzer 45] COMPILE_TIME_ERROR.INVALID_ASSIGNMENT
+  // [analyzer 44] COMPILE_TIME_ERROR.INVALID_ASSIGNMENT
   //          ^
-  // [cfe 81] A value of type 'int?' can't be assigned to a variable of type 'int' because 'int?' is nullable and 'int' isn't.
-  ({for (i = c.bad; false;) null});
-  //         ^^^^^
-  // [analyzer 39] COMPILE_TIME_ERROR.INVALID_ASSIGNMENT
-  //           ^
   // [cfe 82] A value of type 'int?' can't be assigned to a variable of type 'int' because 'int?' is nullable and 'int' isn't.
-  ({for (i = c.bad; false;) null: null});
+  ({for (i = c.bad; false;) null});
   //         ^^^^^
   // [analyzer 40] COMPILE_TIME_ERROR.INVALID_ASSIGNMENT
   //           ^
   // [cfe 83] A value of type 'int?' can't be assigned to a variable of type 'int' because 'int?' is nullable and 'int' isn't.
+  ({for (i = c.bad; false;) null: null});
+  //         ^^^^^
+  // [analyzer 41] COMPILE_TIME_ERROR.INVALID_ASSIGNMENT
+  //           ^
+  // [cfe 84] A value of type 'int?' can't be assigned to a variable of type 'int' because 'int?' is nullable and 'int' isn't.
 }
 
 class C26 {
   int? bad;
   //   ^^^
   // [context 26] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 84] 'bad' refers to a property so it couldn't be promoted.
+  // [context 85] 'bad' refers to a property so it couldn't be promoted.
 }
 
 compoundAssignmentRhs(C26 c) {
@@ -531,30 +531,30 @@
   //   ^^^^^
   // [analyzer 26] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
   //     ^
-  // [cfe 84] A value of type 'int?' can't be assigned to a variable of type 'num' because 'int?' is nullable and 'num' isn't.
+  // [cfe 85] A value of type 'int?' can't be assigned to a variable of type 'num' because 'int?' is nullable and 'num' isn't.
 }
 
 class C27 {
   int? bad;
   //   ^^^
-  // [context 42] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 85] 'bad' refers to a property so it couldn't be promoted.
+  // [context 38] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
+  // [context 86] 'bad' refers to a property so it couldn't be promoted.
 }
 
 indexGet(C27 c, List<int> values) {
   if (c.bad == null) return;
   values[c.bad];
   //     ^^^^^
-  // [analyzer 42] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
+  // [analyzer 38] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
   //       ^
-  // [cfe 85] A value of type 'int?' can't be assigned to a variable of type 'int' because 'int?' is nullable and 'int' isn't.
+  // [cfe 86] A value of type 'int?' can't be assigned to a variable of type 'int' because 'int?' is nullable and 'int' isn't.
 }
 
 class C28 {
   int? bad;
   //   ^^^
   // [context 31] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 86] 'bad' refers to a property so it couldn't be promoted.
+  // [context 87] 'bad' refers to a property so it couldn't be promoted.
 }
 
 indexSet(C28 c, List<int> values) {
@@ -563,83 +563,103 @@
   //     ^^^^^
   // [analyzer 31] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
   //       ^
-  // [cfe 86] A value of type 'int?' can't be assigned to a variable of type 'int' because 'int?' is nullable and 'int' isn't.
+  // [cfe 87] A value of type 'int?' can't be assigned to a variable of type 'int' because 'int?' is nullable and 'int' isn't.
 }
 
 class C29 {
   int? bad;
   //   ^^^
-  // [context 22] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 87] 'bad' refers to a property so it couldn't be promoted.
+  // [context 14] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
+  // [context 88] 'bad' refers to a property so it couldn't be promoted.
 }
 
 indexSetCompound(C29 c, List<int> values) {
   if (c.bad == null) return;
   values[c.bad] += 1;
   //     ^^^^^
-  // [analyzer 22] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
+  // [analyzer 14] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
   //       ^
-  // [cfe 87] A value of type 'int?' can't be assigned to a variable of type 'int' because 'int?' is nullable and 'int' isn't.
+  // [cfe 88] A value of type 'int?' can't be assigned to a variable of type 'int' because 'int?' is nullable and 'int' isn't.
 }
 
 class C30 {
   int? bad;
   //   ^^^
-  // [context 18] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 88] 'bad' refers to a property so it couldn't be promoted.
+  // [context 27] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
+  // [context 89] 'bad' refers to a property so it couldn't be promoted.
 }
 
 indexSetIfNull(C30 c, List<int?> values) {
   if (c.bad == null) return;
   values[c.bad] ??= 1;
   //     ^^^^^
-  // [analyzer 18] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
+  // [analyzer 27] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
   //       ^
-  // [cfe 88] A value of type 'int?' can't be assigned to a variable of type 'int' because 'int?' is nullable and 'int' isn't.
+  // [cfe 89] A value of type 'int?' can't be assigned to a variable of type 'int' because 'int?' is nullable and 'int' isn't.
 }
 
 class C31 {
   int? bad;
   //   ^^^
+  // [context 1] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
   // [context 20] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 41] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 89] 'bad' refers to a property so it couldn't be promoted.
   // [context 90] 'bad' refers to a property so it couldn't be promoted.
+  // [context 91] 'bad' refers to a property so it couldn't be promoted.
 }
 
 indexSetPreIncDec(C31 c, List<int> values) {
   if (c.bad == null) return;
   ++values[c.bad];
   //       ^^^^^
-  // [analyzer 41] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
-  //         ^
-  // [cfe 89] A value of type 'int?' can't be assigned to a variable of type 'int' because 'int?' is nullable and 'int' isn't.
-  --values[c.bad];
-  //       ^^^^^
   // [analyzer 20] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
   //         ^
   // [cfe 90] A value of type 'int?' can't be assigned to a variable of type 'int' because 'int?' is nullable and 'int' isn't.
+  --values[c.bad];
+  //       ^^^^^
+  // [analyzer 1] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
+  //         ^
+  // [cfe 91] A value of type 'int?' can't be assigned to a variable of type 'int' because 'int?' is nullable and 'int' isn't.
 }
 
 class C32 {
   int? bad;
   //   ^^^
-  // [context 2] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 37] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
-  // [context 91] 'bad' refers to a property so it couldn't be promoted.
+  // [context 29] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
+  // [context 42] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
   // [context 92] 'bad' refers to a property so it couldn't be promoted.
+  // [context 93] 'bad' refers to a property so it couldn't be promoted.
 }
 
 indexSetPostIncDec(C32 c, List<int> values) {
   if (c.bad == null) return;
   values[c.bad]++;
   //     ^^^^^
-  // [analyzer 2] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
-  //       ^
-  // [cfe 91] A value of type 'int?' can't be assigned to a variable of type 'int' because 'int?' is nullable and 'int' isn't.
-  values[c.bad]--;
-  //     ^^^^^
-  // [analyzer 37] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
+  // [analyzer 29] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
   //       ^
   // [cfe 92] A value of type 'int?' can't be assigned to a variable of type 'int' because 'int?' is nullable and 'int' isn't.
+  values[c.bad]--;
+  //     ^^^^^
+  // [analyzer 42] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
+  //       ^
+  // [cfe 93] A value of type 'int?' can't be assigned to a variable of type 'int' because 'int?' is nullable and 'int' isn't.
+}
+
+extension E33 on int {
+  void f() {}
+}
+
+class C33 {
+  int? bad;
+  //   ^^^
+  // [context 10] 'bad' refers to a property so it couldn't be promoted.  See http://dart.dev/go/non-promo-property
+  // [context 94] 'bad' refers to a property so it couldn't be promoted.
+}
+
+test(C33 c) {
+  if (c.bad == null) return;
+  E33(c.bad).f();
+  //  ^^^^^
+  // [analyzer 10] COMPILE_TIME_ERROR.EXTENSION_OVERRIDE_ARGUMENT_NOT_ASSIGNABLE
+  //    ^
+  // [cfe 94] The argument type 'int?' can't be assigned to the parameter type 'int' because 'int?' is nullable and 'int' isn't.
 }
diff --git a/tests/language_2/function/regress_45601_test.dart b/tests/language_2/function/regress_45601_test.dart
new file mode 100644
index 0000000..e1a8c19
--- /dev/null
+++ b/tests/language_2/function/regress_45601_test.dart
@@ -0,0 +1,42 @@
+// 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:expect/expect.dart";
+
+// Regression test for https://github.com/dart-lang/sdk/issues/45601
+// When comparing values of type `Function` for equality DDC was generating
+// code that would throw at runtime.
+
+void fn<T>(T t) => null;
+
+void main() {
+  testStaticEquality();
+  testDynamicEquality();
+}
+
+/// Ensure `==` calls on function values that are statically typed as `Function`
+/// work as expected.
+void testStaticEquality() {
+  Function staticFunction = fn;
+  Expect.isTrue(staticFunction == fn);
+  Expect.isFalse(staticFunction == main);
+
+  Function staticFunction2 = null;
+  Expect.isFalse(staticFunction2 == staticFunction);
+  staticFunction2 = fn;
+  Expect.isTrue(staticFunction2 == staticFunction);
+}
+
+/// Ensure `==` calls on function values that are statically typed as `dynamic`
+/// work as expected.
+void testDynamicEquality() {
+  dynamic dynamicFunction = fn;
+  Expect.isTrue(dynamicFunction == fn);
+  Expect.isFalse(dynamicFunction == main);
+
+  dynamic dynamicFunction2 = null;
+  Expect.isFalse(dynamicFunction2 == dynamicFunction);
+  dynamicFunction2 = fn;
+  Expect.isTrue(dynamicFunction2 == dynamicFunction);
+}
diff --git a/tests/language_2/interface/duplicate_interface_implements_test.dart b/tests/language_2/interface/duplicate_interface_implements_test.dart
index 4564aad..0c8555e 100644
--- a/tests/language_2/interface/duplicate_interface_implements_test.dart
+++ b/tests/language_2/interface/duplicate_interface_implements_test.dart
@@ -10,6 +10,7 @@
     , alib.InterfA
     //^^^^^^^^^^^^
     // [analyzer] COMPILE_TIME_ERROR.IMPLEMENTS_REPEATED
+    // [cfe] 'InterfA' can only be implemented once.
 {}
 
 main() {
diff --git a/tests/language_2/regress/regress34488_test.dart b/tests/language_2/regress/regress34488_test.dart
index 0930f68..357087c 100644
--- a/tests/language_2/regress/regress34488_test.dart
+++ b/tests/language_2/regress/regress34488_test.dart
@@ -32,9 +32,8 @@
   // [analyzer] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
   // [cfe] The argument type 'String' can't be assigned to the parameter type 'int'.
   d.h(i: 'bad');
-  //  ^^^^^^^^
+  //     ^^^^^
   // [analyzer] COMPILE_TIME_ERROR.ARGUMENT_TYPE_NOT_ASSIGNABLE
-  //     ^
   // [cfe] The argument type 'String' can't be assigned to the parameter type 'int'.
   Object x = d.f(1);
   //           ^
diff --git a/tests/standalone/io/http_ban_http_allowed_cases_test.dart b/tests/standalone/io/http_ban_http_allowed_cases_test.dart
deleted file mode 100644
index 92e23c7..0000000
--- a/tests/standalone/io/http_ban_http_allowed_cases_test.dart
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-// This test file disallows VM from accepting insecure connections to all
-// domains and tests that HTTP connections to non-localhost targets fail.
-// HTTPS connections and localhost connections should still succeed.
-
-// SharedOptions=-Ddart.library.io.may_insecurely_connect_to_all_domains=false
-
-import "dart:async";
-import "dart:io";
-
-import "package:async_helper/async_helper.dart";
-
-import "http_bind_test.dart";
-
-Future<String> getLocalHostIP() async {
-  final interfaces = await NetworkInterface.list(
-      includeLoopback: false, type: InternetAddressType.IPv4);
-  return interfaces.first.addresses.first.address;
-}
-
-Future<void> testBanHttp(String serverHost,
-    Future<void> testCode(HttpClient client, Uri uri)) async {
-  final httpClient = new HttpClient();
-  final server = await HttpServer.bind(serverHost, 0);
-  final uri = Uri(scheme: 'http', host: serverHost, port: server.port);
-  try {
-    await testCode(httpClient, uri);
-  } finally {
-    httpClient.close(force: true);
-    await server.close();
-  }
-}
-
-Future<void> testWithLoopback() async {
-  await testBanHttp("127.0.0.1", (httpClient, uri) async {
-    await asyncTest(() async =>
-        await httpClient.getUrl(Uri.parse('http://localhost:${uri.port}')));
-    await asyncTest(() async =>
-        await httpClient.getUrl(Uri.parse('http://127.0.0.1:${uri.port}')));
-  });
-}
-
-Future<void> testWithIPv6() async {
-  if (await supportsIPV6()) {
-    await testBanHttp("::1", (httpClient, uri) async {
-      await asyncTest(() => httpClient.getUrl(uri));
-    });
-  }
-}
-
-Future<void> testWithHttps() async {
-  await testBanHttp(await getLocalHostIP(), (httpClient, uri) async {
-    asyncExpectThrows(
-        () => httpClient.getUrl(Uri(
-              scheme: 'https',
-              host: uri.host,
-              port: uri.port,
-            )),
-        (e) => e is SocketException || e is HandshakeException);
-  });
-}
-
-main() {
-  asyncStart();
-  Future.wait(<Future>[
-    testWithLoopback(),
-    testWithIPv6(),
-    testWithHttps(),
-  ]).then((_) => asyncEnd());
-}
diff --git a/tests/standalone/io/http_ban_http_embedder_test.dart b/tests/standalone/io/http_ban_http_embedder_test.dart
new file mode 100644
index 0000000..7fd3cb3
--- /dev/null
+++ b/tests/standalone/io/http_ban_http_embedder_test.dart
@@ -0,0 +1,69 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// SharedOptions=-Ddart.library.io.allow_http=false
+
+import 'dart:async';
+import 'dart:io';
+
+import "package:async_helper/async_helper.dart";
+
+import 'http_ban_http_normal_test.dart';
+import 'http_bind_test.dart';
+
+Future<void> testWithHostname() async {
+  await testBanHttp(await getLocalHostIP(), (httpClient, httpUri) async {
+    asyncExpectThrows(
+        () async => await httpClient.getUrl(httpUri), (e) => e is StateError);
+    asyncExpectThrows(
+        () async => await runZoned(() => httpClient.getUrl(httpUri),
+            zoneValues: {#dart.library.io.allow_http: 'foo'}),
+        (e) => e is StateError);
+    asyncExpectThrows(
+        () async => await runZoned(() => httpClient.getUrl(httpUri),
+            zoneValues: {#dart.library.io.allow_http: false}),
+        (e) => e is StateError);
+    await asyncTest(() => runZoned(() => httpClient.getUrl(httpUri),
+        zoneValues: {#dart.library.io.allow_http: true}));
+  });
+}
+
+Future<void> testWithLoopback() async {
+  await testBanHttp("127.0.0.1", (httpClient, uri) async {
+    await asyncTest(
+        () => httpClient.getUrl(Uri.parse('http://localhost:${uri.port}')));
+    await asyncTest(
+        () => httpClient.getUrl(Uri.parse('http://127.0.0.1:${uri.port}')));
+  });
+}
+
+Future<void> testWithIPv6() async {
+  if (await supportsIPV6()) {
+    await testBanHttp("::1", (httpClient, uri) async {
+      await asyncTest(() => httpClient.getUrl(uri));
+    });
+  }
+}
+
+Future<void> testWithHTTPS() async {
+  await testBanHttp(await getLocalHostIP(), (httpClient, uri) async {
+    asyncExpectThrows(
+        () => httpClient.getUrl(Uri(
+              scheme: 'https',
+              host: uri.host,
+              port: uri.port,
+            )),
+        (e) => e is SocketException || e is HandshakeException);
+  });
+}
+
+main() {
+  asyncStart();
+  Future.wait(<Future>[
+    testWithHostname(),
+    testWithLoopback(),
+    testWithIPv6(),
+    testWithHTTPS(),
+  ]).then((_) => asyncEnd());
+}
diff --git a/tests/standalone/io/http_ban_http_normal_test.dart b/tests/standalone/io/http_ban_http_normal_test.dart
new file mode 100644
index 0000000..118dd4c
--- /dev/null
+++ b/tests/standalone/io/http_ban_http_normal_test.dart
@@ -0,0 +1,44 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:io';
+
+import "package:async_helper/async_helper.dart";
+
+Future<String> getLocalHostIP() async {
+  final interfaces = await NetworkInterface.list(
+      includeLoopback: false, type: InternetAddressType.IPv4);
+  return interfaces.first.addresses.first.address;
+}
+
+Future<void> testBanHttp(String serverHost,
+    Future<void> testCode(HttpClient client, Uri uri)) async {
+  final httpClient = new HttpClient();
+  final server = await HttpServer.bind(serverHost, 0);
+  final uri = Uri(scheme: 'http', host: serverHost, port: server.port);
+  try {
+    await testCode(httpClient, uri);
+  } finally {
+    httpClient.close(force: true);
+    await server.close();
+  }
+}
+
+main() async {
+  await asyncTest(() async {
+    final host = await getLocalHostIP();
+    // Normal HTTP request succeeds.
+    await testBanHttp(host, (httpClient, uri) async {
+      await asyncTest(() => httpClient.getUrl(uri));
+    });
+    // We can ban HTTP explicitly.
+    await testBanHttp(host, (httpClient, uri) async {
+      asyncExpectThrows(
+          () async => await runZoned(() => httpClient.getUrl(uri),
+              zoneValues: {#dart.library.io.allow_http: false}),
+          (e) => e is StateError);
+    });
+  });
+}
diff --git a/tests/standalone/io/http_ban_insecure_connections_test.dart b/tests/standalone/io/http_ban_insecure_connections_test.dart
deleted file mode 100644
index ae51a37..0000000
--- a/tests/standalone/io/http_ban_insecure_connections_test.dart
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-// This test file disallows VM from accepting insecure connections to all
-// domains and tests that HTTP connections to non-localhost targets fail.
-// HTTPS connections and localhost connections should still succeed.
-
-// SharedOptions=-Ddart.library.io.may_insecurely_connect_to_all_domains=false
-
-import "dart:async";
-import "dart:io";
-
-import "package:async_helper/async_helper.dart";
-
-Future<void> testWithHostname() async {
-  final httpClient = new HttpClient();
-  final uri = Uri(scheme: 'http', host: 'domain.invalid', port: 12345);
-  asyncExpectThrows(
-      () async => await httpClient.getUrl(uri),
-      (e) =>
-          e is StateError &&
-          e.message.contains("Insecure HTTP is not allowed by platform"));
-}
-
-main() {
-  asyncStart();
-  testWithHostname().then((_) => asyncEnd());
-}
diff --git a/tests/standalone/io/http_cookie_date_test.dart b/tests/standalone/io/http_cookie_date_test.dart
index f750fdf..dc1787d 100644
--- a/tests/standalone/io/http_cookie_date_test.dart
+++ b/tests/standalone/io/http_cookie_date_test.dart
@@ -19,6 +19,7 @@
     show Since, valueOfNonNullableParamWithDefault, HttpStatus;
 
 part "../../../sdk/lib/_http/crypto.dart";
+part "../../../sdk/lib/_http/embedder_config.dart";
 part "../../../sdk/lib/_http/http_impl.dart";
 part "../../../sdk/lib/_http/http_date.dart";
 part "../../../sdk/lib/_http/http_parser.dart";
diff --git a/tests/standalone/io/http_headers_test.dart b/tests/standalone/io/http_headers_test.dart
index 860b0b1..11e0f89 100644
--- a/tests/standalone/io/http_headers_test.dart
+++ b/tests/standalone/io/http_headers_test.dart
@@ -19,6 +19,7 @@
     show Since, valueOfNonNullableParamWithDefault, HttpStatus;
 
 part "../../../sdk/lib/_http/crypto.dart";
+part "../../../sdk/lib/_http/embedder_config.dart";
 part "../../../sdk/lib/_http/http_impl.dart";
 part "../../../sdk/lib/_http/http_date.dart";
 part "../../../sdk/lib/_http/http_parser.dart";
diff --git a/tests/standalone/io/http_parser_test.dart b/tests/standalone/io/http_parser_test.dart
index c689a0c..300ed63 100644
--- a/tests/standalone/io/http_parser_test.dart
+++ b/tests/standalone/io/http_parser_test.dart
@@ -19,6 +19,7 @@
     show Since, valueOfNonNullableParamWithDefault, HttpStatus;
 
 part "../../../sdk/lib/_http/crypto.dart";
+part "../../../sdk/lib/_http/embedder_config.dart";
 part "../../../sdk/lib/_http/http_impl.dart";
 part "../../../sdk/lib/_http/http_date.dart";
 part "../../../sdk/lib/_http/http_parser.dart";
diff --git a/tests/standalone/io/network_policy_configuration_test.dart b/tests/standalone/io/network_policy_configuration_test.dart
deleted file mode 100644
index 49e6c56..0000000
--- a/tests/standalone/io/network_policy_configuration_test.dart
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-// SharedOptions=-Ddart.library.io.domain_network_policies=[["foobar.com",true,true],["foobar.com",true,true],["baz.foobar.com",true,true],["baz.foobar.com",false,false]] -Ddart.library.io.may_insecurely_connect_to_all_domains=false
-
-import 'dart:io';
-
-import "package:expect/expect.dart";
-
-void _checkAllows(List<String> domains) {
-  for (final domain in domains) {
-    Expect.isTrue(
-        isInsecureConnectionAllowed(domain), "$domain should be allowed.");
-  }
-}
-
-void _checkDenies(List<String> domains) {
-  for (final domain in domains) {
-    Expect.isFalse(
-        isInsecureConnectionAllowed(domain), "$domain should not be allowed.");
-  }
-}
-
-void main() {
-  // These have no policy but the default is false.
-  _checkDenies([
-    "mailfoobar.com",
-    "abc.com",
-    "oobar.com",
-    "foobar.co",
-    "128.221.55.31",
-    "fe80::4607:0bff:fea0:7747%invalid",
-  ]);
-  // These are explicitly denied.
-  _checkDenies(["baz.foobar.com"]);
-  _checkAllows(
-      ["foobar.com", "test.baz.foobar.com", "test2.test.baz.foobar.com"]);
-  _checkAllows(["::1", "localhost"]);
-}
diff --git a/tests/standalone/io/network_policy_invalid_domain_test.dart b/tests/standalone/io/network_policy_invalid_domain_test.dart
deleted file mode 100644
index 971996c..0000000
--- a/tests/standalone/io/network_policy_invalid_domain_test.dart
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-// SharedOptions=-Ddart.library.io.domain_network_policies=[["com",true,true]]
-
-import 'dart:io';
-
-import "package:expect/expect.dart";
-
-// This test passes in an invalid domain as a network policy and checks that
-// loading the policies throws.
-void main() {
-  Expect.throwsArgumentError(() => isInsecureConnectionAllowed("test.com"));
-}
diff --git a/tests/standalone/io/network_policy_test.dart b/tests/standalone/io/network_policy_test.dart
new file mode 100644
index 0000000..2f9878d
--- /dev/null
+++ b/tests/standalone/io/network_policy_test.dart
@@ -0,0 +1,32 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:io';
+
+import "package:expect/expect.dart";
+
+void _checkAllows(List<String> domains) {
+  for (final domain in domains) {
+    Expect.isTrue(
+        isInsecureConnectionAllowed(domain), "$domain should be allowed.");
+  }
+}
+
+void main() {
+  // All domains and addresses are allowed.
+  _checkAllows([
+    "mailfoobar.com",
+    "abc.com",
+    "oobar.com",
+    "foobar.co",
+    "128.221.55.31",
+    "fe80::4607:0bff:fea0:7747%invalid",
+    "baz.foobar.com",
+    "foobar.com",
+    "test.baz.foobar.com",
+    "test2.test.baz.foobar.com",
+    "::1",
+    "localhost",
+  ]);
+}
diff --git a/tests/standalone/io/network_policy_tie_breaker_test.dart b/tests/standalone/io/network_policy_tie_breaker_test.dart
deleted file mode 100644
index c25d3ac..0000000
--- a/tests/standalone/io/network_policy_tie_breaker_test.dart
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-// SharedOptions=-Ddart.library.io.domain_network_policies=[["baz.foobar.com",true,true],["baz.foobar.com",false,false]]
-
-import 'dart:io';
-
-import "package:expect/expect.dart";
-
-void main() {
-  Expect.isFalse(isInsecureConnectionAllowed("baz.foobar.com"));
-  Expect.isTrue(isInsecureConnectionAllowed("test.baz.foobar.com"));
-}
diff --git a/tests/standalone/io/web_socket_protocol_processor_test.dart b/tests/standalone/io/web_socket_protocol_processor_test.dart
index 71e7e45..344a0b9 100644
--- a/tests/standalone/io/web_socket_protocol_processor_test.dart
+++ b/tests/standalone/io/web_socket_protocol_processor_test.dart
@@ -20,6 +20,7 @@
     show Since, valueOfNonNullableParamWithDefault, HttpStatus;
 
 part "../../../sdk/lib/_http/crypto.dart";
+part "../../../sdk/lib/_http/embedder_config.dart";
 part "../../../sdk/lib/_http/http_impl.dart";
 part "../../../sdk/lib/_http/http_date.dart";
 part "../../../sdk/lib/_http/http_parser.dart";
diff --git a/tests/standalone/standalone_kernel.status b/tests/standalone/standalone_kernel.status
index cf3bf1f..a628ff4 100644
--- a/tests/standalone/standalone_kernel.status
+++ b/tests/standalone/standalone_kernel.status
@@ -12,10 +12,8 @@
 
 [ $system == android ]
 entrypoints_verification_test: Skip # Requires shared objects which the test script doesn't "adb push".
-io/http_ban_http_allowed_cases_test: Skip # Depends on grabbing local hostname which isn't supported.
-io/network_policy_configuration_test: Skip # Can't pass -D params containing quotes to adb.
-io/network_policy_invalid_domain_test: Skip # Can't pass -D params containing quotes to adb.
-io/network_policy_tie_breaker_test: Skip # Can't pass -D params containing quotes to adb.
+io/http_ban_http_embedder_test: Skip # Requires http server bound to non-loopback; not provided by system.
+io/http_ban_http_normal_test: Skip # Requires http server bound to non-loopback; not provided by system.
 
 [ $arch == ia32 && $builder_tag == optimization_counter_threshold ]
 io/file_lock_test: SkipSlow # Timeout
diff --git a/tests/standalone_2/io/http_ban_http_allowed_cases_test.dart b/tests/standalone_2/io/http_ban_http_allowed_cases_test.dart
deleted file mode 100644
index 92e23c7..0000000
--- a/tests/standalone_2/io/http_ban_http_allowed_cases_test.dart
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-// This test file disallows VM from accepting insecure connections to all
-// domains and tests that HTTP connections to non-localhost targets fail.
-// HTTPS connections and localhost connections should still succeed.
-
-// SharedOptions=-Ddart.library.io.may_insecurely_connect_to_all_domains=false
-
-import "dart:async";
-import "dart:io";
-
-import "package:async_helper/async_helper.dart";
-
-import "http_bind_test.dart";
-
-Future<String> getLocalHostIP() async {
-  final interfaces = await NetworkInterface.list(
-      includeLoopback: false, type: InternetAddressType.IPv4);
-  return interfaces.first.addresses.first.address;
-}
-
-Future<void> testBanHttp(String serverHost,
-    Future<void> testCode(HttpClient client, Uri uri)) async {
-  final httpClient = new HttpClient();
-  final server = await HttpServer.bind(serverHost, 0);
-  final uri = Uri(scheme: 'http', host: serverHost, port: server.port);
-  try {
-    await testCode(httpClient, uri);
-  } finally {
-    httpClient.close(force: true);
-    await server.close();
-  }
-}
-
-Future<void> testWithLoopback() async {
-  await testBanHttp("127.0.0.1", (httpClient, uri) async {
-    await asyncTest(() async =>
-        await httpClient.getUrl(Uri.parse('http://localhost:${uri.port}')));
-    await asyncTest(() async =>
-        await httpClient.getUrl(Uri.parse('http://127.0.0.1:${uri.port}')));
-  });
-}
-
-Future<void> testWithIPv6() async {
-  if (await supportsIPV6()) {
-    await testBanHttp("::1", (httpClient, uri) async {
-      await asyncTest(() => httpClient.getUrl(uri));
-    });
-  }
-}
-
-Future<void> testWithHttps() async {
-  await testBanHttp(await getLocalHostIP(), (httpClient, uri) async {
-    asyncExpectThrows(
-        () => httpClient.getUrl(Uri(
-              scheme: 'https',
-              host: uri.host,
-              port: uri.port,
-            )),
-        (e) => e is SocketException || e is HandshakeException);
-  });
-}
-
-main() {
-  asyncStart();
-  Future.wait(<Future>[
-    testWithLoopback(),
-    testWithIPv6(),
-    testWithHttps(),
-  ]).then((_) => asyncEnd());
-}
diff --git a/tests/standalone_2/io/http_ban_http_embedder_test.dart b/tests/standalone_2/io/http_ban_http_embedder_test.dart
new file mode 100644
index 0000000..7fd3cb3
--- /dev/null
+++ b/tests/standalone_2/io/http_ban_http_embedder_test.dart
@@ -0,0 +1,69 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// SharedOptions=-Ddart.library.io.allow_http=false
+
+import 'dart:async';
+import 'dart:io';
+
+import "package:async_helper/async_helper.dart";
+
+import 'http_ban_http_normal_test.dart';
+import 'http_bind_test.dart';
+
+Future<void> testWithHostname() async {
+  await testBanHttp(await getLocalHostIP(), (httpClient, httpUri) async {
+    asyncExpectThrows(
+        () async => await httpClient.getUrl(httpUri), (e) => e is StateError);
+    asyncExpectThrows(
+        () async => await runZoned(() => httpClient.getUrl(httpUri),
+            zoneValues: {#dart.library.io.allow_http: 'foo'}),
+        (e) => e is StateError);
+    asyncExpectThrows(
+        () async => await runZoned(() => httpClient.getUrl(httpUri),
+            zoneValues: {#dart.library.io.allow_http: false}),
+        (e) => e is StateError);
+    await asyncTest(() => runZoned(() => httpClient.getUrl(httpUri),
+        zoneValues: {#dart.library.io.allow_http: true}));
+  });
+}
+
+Future<void> testWithLoopback() async {
+  await testBanHttp("127.0.0.1", (httpClient, uri) async {
+    await asyncTest(
+        () => httpClient.getUrl(Uri.parse('http://localhost:${uri.port}')));
+    await asyncTest(
+        () => httpClient.getUrl(Uri.parse('http://127.0.0.1:${uri.port}')));
+  });
+}
+
+Future<void> testWithIPv6() async {
+  if (await supportsIPV6()) {
+    await testBanHttp("::1", (httpClient, uri) async {
+      await asyncTest(() => httpClient.getUrl(uri));
+    });
+  }
+}
+
+Future<void> testWithHTTPS() async {
+  await testBanHttp(await getLocalHostIP(), (httpClient, uri) async {
+    asyncExpectThrows(
+        () => httpClient.getUrl(Uri(
+              scheme: 'https',
+              host: uri.host,
+              port: uri.port,
+            )),
+        (e) => e is SocketException || e is HandshakeException);
+  });
+}
+
+main() {
+  asyncStart();
+  Future.wait(<Future>[
+    testWithHostname(),
+    testWithLoopback(),
+    testWithIPv6(),
+    testWithHTTPS(),
+  ]).then((_) => asyncEnd());
+}
diff --git a/tests/standalone_2/io/http_ban_http_normal_test.dart b/tests/standalone_2/io/http_ban_http_normal_test.dart
new file mode 100644
index 0000000..118dd4c
--- /dev/null
+++ b/tests/standalone_2/io/http_ban_http_normal_test.dart
@@ -0,0 +1,44 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:io';
+
+import "package:async_helper/async_helper.dart";
+
+Future<String> getLocalHostIP() async {
+  final interfaces = await NetworkInterface.list(
+      includeLoopback: false, type: InternetAddressType.IPv4);
+  return interfaces.first.addresses.first.address;
+}
+
+Future<void> testBanHttp(String serverHost,
+    Future<void> testCode(HttpClient client, Uri uri)) async {
+  final httpClient = new HttpClient();
+  final server = await HttpServer.bind(serverHost, 0);
+  final uri = Uri(scheme: 'http', host: serverHost, port: server.port);
+  try {
+    await testCode(httpClient, uri);
+  } finally {
+    httpClient.close(force: true);
+    await server.close();
+  }
+}
+
+main() async {
+  await asyncTest(() async {
+    final host = await getLocalHostIP();
+    // Normal HTTP request succeeds.
+    await testBanHttp(host, (httpClient, uri) async {
+      await asyncTest(() => httpClient.getUrl(uri));
+    });
+    // We can ban HTTP explicitly.
+    await testBanHttp(host, (httpClient, uri) async {
+      asyncExpectThrows(
+          () async => await runZoned(() => httpClient.getUrl(uri),
+              zoneValues: {#dart.library.io.allow_http: false}),
+          (e) => e is StateError);
+    });
+  });
+}
diff --git a/tests/standalone_2/io/http_ban_insecure_connections_test.dart b/tests/standalone_2/io/http_ban_insecure_connections_test.dart
deleted file mode 100644
index ae51a37..0000000
--- a/tests/standalone_2/io/http_ban_insecure_connections_test.dart
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-// This test file disallows VM from accepting insecure connections to all
-// domains and tests that HTTP connections to non-localhost targets fail.
-// HTTPS connections and localhost connections should still succeed.
-
-// SharedOptions=-Ddart.library.io.may_insecurely_connect_to_all_domains=false
-
-import "dart:async";
-import "dart:io";
-
-import "package:async_helper/async_helper.dart";
-
-Future<void> testWithHostname() async {
-  final httpClient = new HttpClient();
-  final uri = Uri(scheme: 'http', host: 'domain.invalid', port: 12345);
-  asyncExpectThrows(
-      () async => await httpClient.getUrl(uri),
-      (e) =>
-          e is StateError &&
-          e.message.contains("Insecure HTTP is not allowed by platform"));
-}
-
-main() {
-  asyncStart();
-  testWithHostname().then((_) => asyncEnd());
-}
diff --git a/tests/standalone_2/io/network_policy_configuration_test.dart b/tests/standalone_2/io/network_policy_configuration_test.dart
deleted file mode 100644
index 49e6c56..0000000
--- a/tests/standalone_2/io/network_policy_configuration_test.dart
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-// SharedOptions=-Ddart.library.io.domain_network_policies=[["foobar.com",true,true],["foobar.com",true,true],["baz.foobar.com",true,true],["baz.foobar.com",false,false]] -Ddart.library.io.may_insecurely_connect_to_all_domains=false
-
-import 'dart:io';
-
-import "package:expect/expect.dart";
-
-void _checkAllows(List<String> domains) {
-  for (final domain in domains) {
-    Expect.isTrue(
-        isInsecureConnectionAllowed(domain), "$domain should be allowed.");
-  }
-}
-
-void _checkDenies(List<String> domains) {
-  for (final domain in domains) {
-    Expect.isFalse(
-        isInsecureConnectionAllowed(domain), "$domain should not be allowed.");
-  }
-}
-
-void main() {
-  // These have no policy but the default is false.
-  _checkDenies([
-    "mailfoobar.com",
-    "abc.com",
-    "oobar.com",
-    "foobar.co",
-    "128.221.55.31",
-    "fe80::4607:0bff:fea0:7747%invalid",
-  ]);
-  // These are explicitly denied.
-  _checkDenies(["baz.foobar.com"]);
-  _checkAllows(
-      ["foobar.com", "test.baz.foobar.com", "test2.test.baz.foobar.com"]);
-  _checkAllows(["::1", "localhost"]);
-}
diff --git a/tests/standalone_2/io/network_policy_invalid_domain_test.dart b/tests/standalone_2/io/network_policy_invalid_domain_test.dart
deleted file mode 100644
index 971996c..0000000
--- a/tests/standalone_2/io/network_policy_invalid_domain_test.dart
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-// SharedOptions=-Ddart.library.io.domain_network_policies=[["com",true,true]]
-
-import 'dart:io';
-
-import "package:expect/expect.dart";
-
-// This test passes in an invalid domain as a network policy and checks that
-// loading the policies throws.
-void main() {
-  Expect.throwsArgumentError(() => isInsecureConnectionAllowed("test.com"));
-}
diff --git a/tests/standalone_2/io/network_policy_test.dart b/tests/standalone_2/io/network_policy_test.dart
new file mode 100644
index 0000000..2f9878d
--- /dev/null
+++ b/tests/standalone_2/io/network_policy_test.dart
@@ -0,0 +1,32 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:io';
+
+import "package:expect/expect.dart";
+
+void _checkAllows(List<String> domains) {
+  for (final domain in domains) {
+    Expect.isTrue(
+        isInsecureConnectionAllowed(domain), "$domain should be allowed.");
+  }
+}
+
+void main() {
+  // All domains and addresses are allowed.
+  _checkAllows([
+    "mailfoobar.com",
+    "abc.com",
+    "oobar.com",
+    "foobar.co",
+    "128.221.55.31",
+    "fe80::4607:0bff:fea0:7747%invalid",
+    "baz.foobar.com",
+    "foobar.com",
+    "test.baz.foobar.com",
+    "test2.test.baz.foobar.com",
+    "::1",
+    "localhost",
+  ]);
+}
diff --git a/tests/standalone_2/io/network_policy_tie_breaker_test.dart b/tests/standalone_2/io/network_policy_tie_breaker_test.dart
deleted file mode 100644
index c25d3ac..0000000
--- a/tests/standalone_2/io/network_policy_tie_breaker_test.dart
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-// SharedOptions=-Ddart.library.io.domain_network_policies=[["baz.foobar.com",true,true],["baz.foobar.com",false,false]]
-
-import 'dart:io';
-
-import "package:expect/expect.dart";
-
-void main() {
-  Expect.isFalse(isInsecureConnectionAllowed("baz.foobar.com"));
-  Expect.isTrue(isInsecureConnectionAllowed("test.baz.foobar.com"));
-}
diff --git a/tests/standalone_2/standalone_2_kernel.status b/tests/standalone_2/standalone_2_kernel.status
index f5fb8ac..548f99c 100644
--- a/tests/standalone_2/standalone_2_kernel.status
+++ b/tests/standalone_2/standalone_2_kernel.status
@@ -14,10 +14,8 @@
 
 [ $system == android ]
 entrypoints_verification_test: Skip # Requires shared objects which the test script doesn't "adb push".
-io/http_ban_http_allowed_cases_test: Skip # Depends on grabbing local hostname which isn't supported.
-io/network_policy_configuration_test: Skip # Can't pass -D params containing quotes to adb.
-io/network_policy_invalid_domain_test: Skip # Can't pass -D params containing quotes to adb.
-io/network_policy_tie_breaker_test: Skip # Can't pass -D params containing quotes to adb.
+io/http_ban_http_embedder_test: Skip # Requires http server bound to non-loopback; not provided by system.
+io/http_ban_http_normal_test: Skip # Requires http server bound to non-loopback; not provided by system.
 
 [ $arch == ia32 && $builder_tag == optimization_counter_threshold ]
 io/file_lock_test: SkipSlow # Timeout
diff --git a/tools/VERSION b/tools/VERSION
index 8d5df1f..1916272 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 14
 PATCH 0
-PRERELEASE 8
+PRERELEASE 9
 PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/bots/bot_utils.py b/tools/bots/bot_utils.py
index 29f840b..5bd718b 100755
--- a/tools/bots/bot_utils.py
+++ b/tools/bots/bot_utils.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
@@ -7,8 +7,6 @@
 import hashlib
 import imp
 import os
-import platform
-import string
 import subprocess
 import sys
 
@@ -176,20 +174,21 @@
 
 
 def run(command, env=None, shell=False, throw_on_error=True):
-    print "Running command: ", command
+    print("Running command: ", command)
 
-    p = subprocess.Popen(
-        command,
-        stdout=subprocess.PIPE,
-        stderr=subprocess.PIPE,
-        env=env,
-        shell=shell)
+    p = subprocess.Popen(command,
+                         stdout=subprocess.PIPE,
+                         stderr=subprocess.PIPE,
+                         env=env,
+                         shell=shell,
+                         universal_newlines=True)
     (stdout, stderr) = p.communicate()
     if throw_on_error and p.returncode != 0:
-        print >> sys.stderr, "Failed to execute '%s'. Exit code: %s." % (
-            command, p.returncode)
-        print >> sys.stderr, "stdout: ", stdout
-        print >> sys.stderr, "stderr: ", stderr
+        print("Failed to execute '%s'. Exit code: %s." %
+              (command, p.returncode),
+              file=sys.stderr)
+        print("stdout: ", stdout, file=sys.stderr)
+        print("stderr: ", stderr, file=sys.stderr)
         raise Exception("Failed to execute %s." % command)
     return (stdout, stderr, p.returncode)
 
@@ -307,7 +306,7 @@
     with open(checksum_filename, 'w') as f:
         f.write('%s *%s' % (checksum, mangled_filename))
 
-    print "MD5 checksum of %s is %s" % (filename, checksum)
+    print("MD5 checksum of %s is %s" % (filename, checksum))
     return checksum_filename
 
 
@@ -322,14 +321,14 @@
     with open(checksum_filename, 'w') as f:
         f.write('%s *%s' % (checksum, mangled_filename))
 
-    print "SHA256 checksum of %s is %s" % (filename, checksum)
+    print("SHA256 checksum of %s is %s" % (filename, checksum))
     return checksum_filename
 
 
 def GetChannelFromName(name):
     """Get the channel from the name. Bleeding edge builders don't
       have a suffix."""
-    channel_name = string.split(name, '-').pop()
+    channel_name = name.split('-').pop()
     if channel_name in Channel.ALL_CHANNELS:
         return channel_name
     return Channel.BLEEDING_EDGE
@@ -337,7 +336,7 @@
 
 def GetSystemFromName(name):
     """Get the system from the name."""
-    for part in string.split(name, '-'):
+    for part in name.split('-'):
         if part in SYSTEM_RENAMES: return SYSTEM_RENAMES[part]
 
     raise ValueError(
diff --git a/tools/bots/dart_sdk.py b/tools/bots/dart_sdk.py
index 8e7f6f9..0193969 100755
--- a/tools/bots/dart_sdk.py
+++ b/tools/bots/dart_sdk.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
@@ -233,8 +233,8 @@
 
 
 def Run(command, env=None):
-    print "Running %s" % ' '.join(command)
-    print "Environment %s" % env
+    print("Running %s" % ' '.join(command))
+    print("Environment %s" % env)
     sys.stdout.flush()
     exit_code = subprocess.call(command)
     if exit_code != 0:
diff --git a/tools/bots/linux_distribution_support.py b/tools/bots/linux_distribution_support.py
index 789fce8..04fb693 100644
--- a/tools/bots/linux_distribution_support.py
+++ b/tools/bots/linux_distribution_support.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 
 # Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
@@ -43,7 +43,7 @@
 
 
 def Run(command):
-    print "Running: %s" % ' '.join(command)
+    print("Running: %s" % ' '.join(command))
     sys.stdout.flush()
     no_color_env = dict(os.environ)
     no_color_env['TERM'] = 'nocolor'
@@ -59,11 +59,11 @@
     for path in paths:
         if os.path.exists(path):
             if not assume_installed:
-                print 'Assumed not installed, found %s' % path
+                print('Assumed not installed, found %s' % path)
                 sys.exit(1)
         else:
             if assume_installed:
-                print 'Assumed installed, but could not find %s' % path
+                print('Assumed installed, but could not find %s' % path)
                 sys.exit(1)
 
 
@@ -77,23 +77,23 @@
     tarfilename = 'dart-%s.tar.gz' % version
     tarfile = os.path.join(builddir, tarfilename)
 
-    print 'Validating that we are on debian jessie'
+    print('Validating that we are on debian jessie')
     args = ['cat', '/etc/os-release']
     (stdout, stderr, exitcode) = bot_utils.run(args)
     if exitcode != 0:
-        print "Could not find linux system, exiting"
+        print("Could not find linux system, exiting")
         sys.exit(1)
     if not "jessie" in stdout:
-        print "Trying to build debian bits but not on debian Jessie"
-        print "You can't fix this, please contact dart-engprod@"
+        print("Trying to build debian bits but not on debian Jessie")
+        print("You can't fix this, please contact dart-engprod@")
         sys.exit(1)
 
-    print 'Building src tarball'
+    print('Building src tarball')
     Run([
         sys.executable, './tools/create_tarball.py', '--tar_filename', tarfile
     ])
 
-    print 'Building Debian packages'
+    print('Building Debian packages')
     Run([
         sys.executable, './tools/create_debian_packages.py', '--tar_filename',
         tarfile, '--out_dir', builddir
@@ -101,7 +101,7 @@
 
     if os.path.exists('/usr/bin/dart') or os.path.exists(
             '/usr/lib/dart/bin/dart2js'):
-        print "Dart already installed, removing"
+        print("Dart already installed, removing")
         UninstallDart()
     TestInstallation(assume_installed=False)
 
diff --git a/tools/bots/pub_integration_test.py b/tools/bots/pub_integration_test.py
index 6d6ef43..7264279 100755
--- a/tools/bots/pub_integration_test.py
+++ b/tools/bots/pub_integration_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2018, 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.
diff --git a/tools/bots/upload_debian_packages.py b/tools/bots/upload_debian_packages.py
index 4ee7642..3fc08d4 100755
--- a/tools/bots/upload_debian_packages.py
+++ b/tools/bots/upload_debian_packages.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 
 # Copyright (c) 2019, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
@@ -36,7 +36,7 @@
 if __name__ == '__main__':
     bot_name = os.environ.get('BUILDBOT_BUILDERNAME')
     channel = bot_utils.GetChannelFromName(bot_name)
-    if channel != bot_utils.Channel.BLEEDING_EDGE:
+    if channel not in (bot_utils.Channel.BLEEDING_EDGE, bot_utils.Channel.TRY):
         builddir = os.path.join(bot_utils.DART_DIR, utils.GetBuildDir(HOST_OS),
                                 'src_and_installation')
         version = utils.GetVersion()
@@ -44,4 +44,4 @@
         tarfile = os.path.join(builddir, tarfilename)
         ArchiveArtifacts(tarfile, builddir, channel)
     else:
-        print 'Not uploading artifacts on bleeding edge'
+        print('Not uploading artifacts on bleeding edge')
diff --git a/tools/build.py b/tools/build.py
index 513bef8..fb17356 100755
--- a/tools/build.py
+++ b/tools/build.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (c) 2017, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
@@ -143,7 +143,7 @@
         return False
     goma_ctl = os.path.join(goma_dir, 'goma_ctl.py')
     goma_ctl_command = [
-        'python',
+        'python3',
         goma_ctl,
         'ensure_start',
     ]
diff --git a/tools/buildtools/update.py b/tools/buildtools/update.py
index a398225..bafeab1 100755
--- a/tools/buildtools/update.py
+++ b/tools/buildtools/update.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright 2017 The Dart project authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
@@ -27,9 +27,9 @@
     downloader_script = os.path.join(DEPOT_PATH,
                                      'download_from_google_storage.py')
     download_cmd = [
-        'python', downloader_script, '--no_auth', '--no_resume', '--quiet',
-        '--platform=win', '--bucket', 'chromium-clang-format', '-s', sha1_file,
-        '-o', output_dir
+        sys.executable, downloader_script, '--no_auth', '--no_resume',
+        '--quiet', '--platform=win', '--bucket', 'chromium-clang-format', '-s',
+        sha1_file, '-o', output_dir
     ]
     return subprocess.call(download_cmd)
 
@@ -37,7 +37,7 @@
 def CreateSymlink(symlink, link_name):
     try:
         os.symlink(symlink, link_name)
-    except OSError, e:
+    except OSError as e:
         if e.errno == errno.EEXIST:
             os.remove(link_name)
             os.symlink(symlink, link_name)
@@ -57,7 +57,7 @@
         tools = 'linux64'
         toolchain = 'linux-x64'
     else:
-        print 'Unknown platform: ' + sys.platform
+        print('Unknown platform: ' + sys.platform)
         return 1
 
     clang_format = os.path.join(BUILDTOOLS, toolchain, 'clang', 'bin',
diff --git a/tools/copy_dart.py b/tools/copy_dart.py
index dbb7ecf..fedc29b 100755
--- a/tools/copy_dart.py
+++ b/tools/copy_dart.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python2.7
+#!/usr/bin/env python3
 # Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
@@ -80,14 +80,14 @@
 
 def main(outdir=None, *inputs):
     if not outdir or not inputs:
-        print "Usage: %s OUTDIR INPUTS" % sys.argv[0]
-        print "  OUTDIR is the war directory to copy to"
-        print "  INPUTS is a list of files or patterns used to specify the input"
-        print "   .dart files"
-        print "This script should be run from the client root directory."
-        print "Files will be merged and copied to: OUTDIR/relative-path-of-file,"
-        print "except for dart files with absolute paths, which will be copied to"
-        print " OUTDIR/absolute-path-as-directories"
+        print("""Usage: %s OUTDIR INPUTS
+  OUTDIR is the war directory to copy to
+  INPUTS is a list of files or patterns used to specify the input
+   .dart files
+This script should be run from the client root directory.
+Files will be merged and copied to: OUTDIR/relative-path-of-file,
+except for dart files with absolute paths, which will be copied to
+ OUTDIR/absolute-path-as-directories""" % sys.argv[0])
         return 1
 
     entry_libraries = []
diff --git a/tools/copy_tree.py b/tools/copy_tree.py
index f5c5c56..8c63308 100755
--- a/tools/copy_tree.py
+++ b/tools/copy_tree.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2017, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
diff --git a/tools/create_debian_chroot.sh b/tools/create_debian_chroot.sh
index 4376f3b..4e98093 100755
--- a/tools/create_debian_chroot.sh
+++ b/tools/create_debian_chroot.sh
@@ -72,7 +72,7 @@
     jessie $CHROOT http://http.us.debian.org/debian/
 chroot $CHROOT apt-get update
 chroot $CHROOT apt-get -y install \
-    debhelper python git gcc sudo make
+    debhelper python3 git gcc sudo make
 
 # Add chrome-bot user.
 chroot $CHROOT groupadd --gid 1001 chrome-bot
diff --git a/tools/create_debian_packages.py b/tools/create_debian_packages.py
index e0e81bb1..74b8a77 100755
--- a/tools/create_debian_packages.py
+++ b/tools/create_debian_packages.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
@@ -59,7 +59,7 @@
     origtarname = 'dart_%s.orig.tar.gz' % version
 
     if not exists(tarball):
-        print 'Source tarball not found'
+        print('Source tarball not found')
         return -1
 
     with utils.TempDir() as temp_dir:
@@ -70,30 +70,30 @@
             tar.extractall(path=temp_dir)
 
         # Build source package.
-        print "Building source package"
+        print("Building source package")
         RunBuildPackage(['-S', '-us', '-uc'], join(temp_dir, tarroot))
 
         # Build 32-bit binary package.
         if 'ia32' in arch:
-            print "Building i386 package"
+            print("Building i386 package")
             RunBuildPackage(['-B', '-ai386', '-us', '-uc'],
                             join(temp_dir, tarroot))
 
         # Build 64-bit binary package.
         if 'x64' in arch:
-            print "Building amd64 package"
+            print("Building amd64 package")
             RunBuildPackage(['-B', '-aamd64', '-us', '-uc'],
                             join(temp_dir, tarroot))
 
         # Build armhf binary package.
         if 'armhf' in arch:
-            print "Building armhf package"
+            print("Building armhf package")
             RunBuildPackage(['-B', '-aarmhf', '-us', '-uc'],
                             join(temp_dir, tarroot), toolchain)
 
         # Build armel binary package.
         if 'armel' in arch:
-            print "Building armel package"
+            print("Building armel package")
             RunBuildPackage(['-B', '-aarmel', '-us', '-uc'],
                             join(temp_dir, tarroot), toolchain)
 
@@ -127,7 +127,7 @@
 
 def Main():
     if HOST_OS != 'linux':
-        print 'Debian build only supported on linux'
+        print('Debian build only supported on linux')
         return -1
 
     options, args = BuildOptions().parse_args()
diff --git a/tools/create_pkg_manifest.py b/tools/create_pkg_manifest.py
index 0448ca9..0233b44 100755
--- a/tools/create_pkg_manifest.py
+++ b/tools/create_pkg_manifest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright 2016 The Dart project authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/create_tarball.py b/tools/create_tarball.py
index ee5630a..6844cad 100755
--- a/tools/create_tarball.py
+++ b/tools/create_tarball.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
@@ -80,7 +80,7 @@
     # out-of-the-box.
     tar_info.name = join(versiondir, 'dart', original_name)
     if verbose:
-        print 'Adding %s as %s' % (original_name, tar_info.name)
+        print('Adding %s as %s' % (original_name, tar_info.name))
     return tar_info
 
 
@@ -129,7 +129,7 @@
     builddir = utils.GetBuildDir(HOST_OS)
     ignoredPaths.append(builddir)
 
-    print 'Creating tarball: %s' % tarfilename
+    print('Creating tarball: %s' % tarfilename)
     with tarfile.open(tarfilename, mode='w:gz') as tar:
         for f in listdir(DART_DIR):
             tar.add(join(DART_DIR, f), filter=Filter)
@@ -165,7 +165,7 @@
 
 def Main():
     if HOST_OS != 'linux':
-        print 'Tarball can only be created on linux'
+        print('Tarball can only be created on linux')
         return -1
 
     # Parse the options.
diff --git a/tools/create_timestamp_file.py b/tools/create_timestamp_file.py
index 8f449a6..bb48e7f 100755
--- a/tools/create_timestamp_file.py
+++ b/tools/create_timestamp_file.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
diff --git a/tools/dom/dom.py b/tools/dom/dom.py
index 8fa8254..f986d3f 100755
--- a/tools/dom/dom.py
+++ b/tools/dom/dom.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 
 # Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
@@ -172,14 +172,14 @@
 
 
 def call(args):
-    print ' '.join(args)
+    print(' '.join(args))
     pipe = subprocess.Popen(
         args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
     output, error = pipe.communicate()
     if output:
-        print output
+        print(output)
     if error:
-        print error
+        print(error)
     return pipe.returncode
 
 
diff --git a/tools/dom/new_scripts/code_generator_dart.py b/tools/dom/new_scripts/code_generator_dart.py
index 6fdd2a9..2a92923 100644
--- a/tools/dom/new_scripts/code_generator_dart.py
+++ b/tools/dom/new_scripts/code_generator_dart.py
@@ -238,10 +238,9 @@
                             world['callbacks'].append(idl_world['callback'])
                     idl_pickle_file.close()
 
-        world['interfaces'] = sorted(
-            world['interfaces'], key=lambda (x): x['name'])
-        world['callbacks'] = sorted(
-            world['callbacks'], key=lambda (x): x['name'])
+        world['interfaces'] = sorted(world['interfaces'],
+                                     key=lambda x: x['name'])
+        world['callbacks'] = sorted(world['callbacks'], key=lambda x: x['name'])
 
         template_contents = world
         template_contents['code_generator'] = module_pyname
@@ -299,7 +298,7 @@
         cache_dir = argv[1]
         dummy_filename = argv[2]
     except IndexError as err:
-        print 'Usage: %s OUTPUT_DIR DUMMY_FILENAME' % argv[0]
+        print('Usage: %s OUTPUT_DIR DUMMY_FILENAME' % argv[0])
         return 1
 
     # Cache templates
diff --git a/tools/dom/new_scripts/compiler.py b/tools/dom/new_scripts/compiler.py
index 9a27581..7eb7270 100755
--- a/tools/dom/new_scripts/compiler.py
+++ b/tools/dom/new_scripts/compiler.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 # Copyright (C) 2014 Google Inc. All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
diff --git a/tools/dom/new_scripts/dart_compiler.py b/tools/dom/new_scripts/dart_compiler.py
index 5dd2b34..5a17629 100755
--- a/tools/dom/new_scripts/dart_compiler.py
+++ b/tools/dom/new_scripts/dart_compiler.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 # Copyright (C) 2013 Google Inc. All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
diff --git a/tools/dom/scripts/all_tests.py b/tools/dom/scripts/all_tests.py
index 40a5a49..f0e30ce 100755
--- a/tools/dom/scripts/all_tests.py
+++ b/tools/dom/scripts/all_tests.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 # Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
diff --git a/tools/dom/scripts/css_code_generator.py b/tools/dom/scripts/css_code_generator.py
index 9b5ccc5..717630b 100644
--- a/tools/dom/scripts/css_code_generator.py
+++ b/tools/dom/scripts/css_code_generator.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 #
 # Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
diff --git a/tools/dom/scripts/dartdomgenerator.py b/tools/dom/scripts/dartdomgenerator.py
index bf2662d..6e529a4 100755
--- a/tools/dom/scripts/dartdomgenerator.py
+++ b/tools/dom/scripts/dartdomgenerator.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 # Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
@@ -96,8 +96,8 @@
                          update_dom_metadata=False,
                          logging_level=logging.WARNING,
                          dart_js_interop=False):
-    print '\n ----- Accessing DOM using %s -----\n' % (
-        'dart:js' if dart_js_interop else 'C++')
+    print('\n ----- Accessing DOM using %s -----\n' %
+          ('dart:js' if dart_js_interop else 'C++'))
 
     start_time = time.time()
 
@@ -130,8 +130,8 @@
     renamer = HtmlRenamer(webkit_database, metadata)
     type_registry = TypeRegistry(webkit_database, renamer)
 
-    print 'GenerateFromDatabase %s seconds' % round(
-        (time.time() - start_time), 2)
+    print('GenerateFromDatabase %s seconds' % round(
+        (time.time() - start_time), 2))
 
     def RunGenerator(dart_libraries, dart_output_dir, template_loader,
                      backend_factory, dart_js_interop):
@@ -178,14 +178,14 @@
             if file.endswith('darttemplate'):
                 dart_libraries._libraries['html'].AddFile(file)
 
-        print '\nGenerating dart2js:\n'
+        print('\nGenerating dart2js:\n')
         start_time = time.time()
 
         RunGenerator(dart_libraries, dart_output_dir, template_loader,
                      backend_factory, dart_js_interop)
 
-        print 'Generated dart2js in %s seconds' % round(
-            time.time() - start_time, 2)
+        print('Generated dart2js in %s seconds' %
+              round(time.time() - start_time, 2))
 
     emitters.Flush()
 
@@ -351,13 +351,13 @@
                 source, os.path.join('..', '..', '..', 'sdk', 'lib',
                                      library_name, 'dart2js'))
 
-    print '\nGenerating single file %s seconds' % round(
-        time.time() - file_generation_start_time, 2)
+    print('\nGenerating single file %s seconds' %
+          round(time.time() - file_generation_start_time, 2))
 
     end_time = time.time()
 
-    print '\nDone (dartdomgenerator) %s seconds' % round(
-        end_time - start_time, 2)
+    print('\nDone (dartdomgenerator) %s seconds' %
+          round(end_time - start_time, 2))
 
 
 if __name__ == '__main__':
diff --git a/tools/dom/scripts/dartgenerator.py b/tools/dom/scripts/dartgenerator.py
index eb5674e..ceb213e 100755
--- a/tools/dom/scripts/dartgenerator.py
+++ b/tools/dom/scripts/dartgenerator.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 # Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
diff --git a/tools/dom/scripts/dartgenerator_test.py b/tools/dom/scripts/dartgenerator_test.py
index cfd7b2e..818d66e 100755
--- a/tools/dom/scripts/dartgenerator_test.py
+++ b/tools/dom/scripts/dartgenerator_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 # Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
diff --git a/tools/dom/scripts/dartmetadata.py b/tools/dom/scripts/dartmetadata.py
index 71f7602..4909c41 100644
--- a/tools/dom/scripts/dartmetadata.py
+++ b/tools/dom/scripts/dartmetadata.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 # Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
diff --git a/tools/dom/scripts/database.py b/tools/dom/scripts/database.py
index 2ec7589..75d8d2e 100755
--- a/tools/dom/scripts/database.py
+++ b/tools/dom/scripts/database.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 # Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
@@ -306,7 +306,7 @@
         if type_def_name in self._all_type_defs:
             raise RuntimeError('Typedef %s already exists' % type_def_name)
         self._all_type_defs[type_def_name] = type_def
-        print '  Added typedef %s' % type_def_name
+        print('  Added typedef %s' % type_def_name)
 
     def TransitiveSecondaryParents(self, interface, propagate_event_target):
         """Returns a list of all non-primary parents.
diff --git a/tools/dom/scripts/database_test.py b/tools/dom/scripts/database_test.py
index 85c6e3e..a82ed24 100755
--- a/tools/dom/scripts/database_test.py
+++ b/tools/dom/scripts/database_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 # Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
diff --git a/tools/dom/scripts/databasebuilder.py b/tools/dom/scripts/databasebuilder.py
index e768ee4..c5327a7 100755
--- a/tools/dom/scripts/databasebuilder.py
+++ b/tools/dom/scripts/databasebuilder.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 # Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
@@ -92,11 +92,11 @@
         idl_definition = build.idl_compiler.compile_file(idl_file_fullpath)
         return idl_definition
     except Exception as err:
-        print 'ERROR: idl_compiler.py: ' + os.path.basename(file_name)
-        print err
-        print
-        print 'Stack Dump:'
-        print format_exception(err)
+        print('ERROR: idl_compiler.py: ' + os.path.basename(file_name))
+        print(err)
+        print()
+        print('Stack Dump:')
+        print(format_exception(err))
 
     return 1
 
@@ -110,11 +110,11 @@
         idl_definition = new_asts[name]
         return IDLFile(idl_definition, file_name)
     except Exception as err:
-        print 'ERROR: loading AST from cache: ' + os.path.basename(file_name)
-        print err
-        print
-        print 'Stack Dump:'
-        print format_exception(err)
+        print('ERROR: loading AST from cache: ' + os.path.basename(file_name))
+        print(err)
+        print()
+        print('Stack Dump:')
+        print(format_exception(err))
 
     return 1
 
@@ -159,11 +159,11 @@
             idl_file_fullpath = os.path.realpath(idl_file)
             self.idl_compiler.compile_file(idl_file_fullpath)
         except Exception as err:
-            print 'ERROR: idl_compiler.py: ' + os.path.basename(idl_file)
-            print err
-            print
-            print 'Stack Dump:'
-            print self.format_exception(err)
+            print('ERROR: idl_compiler.py: ' + os.path.basename(idl_file))
+            print(err)
+            print()
+            print('Stack Dump:')
+            print(self.format_exception(err))
 
             return 1
 
@@ -593,8 +593,8 @@
                 self._info_collector.collect_info(file_path)
 
             end_time = time.time()
-            print 'Compute dependencies %s seconds' % round(
-                (end_time - start_time), 2)
+            print('Compute dependencies %s seconds' % round(
+                (end_time - start_time), 2))
         else:
             # Compute the interface_info for dart.idl for implements defined.  This
             # file is special in that more than one interface can exist in this file.
@@ -614,14 +614,14 @@
                 os.path.splitext(os.path.basename(file_path))[0], ast)
 
         end_time = time.time()
-        print 'Compiled %s IDL files in %s seconds' % (
-            len(file_paths), round((end_time - start_time), 2))
+        print('Compiled %s IDL files in %s seconds' %
+              (len(file_paths), round((end_time - start_time), 2)))
 
     def _process_ast(self, filename, ast):
         if len(ast) == 1:
             ast = ast.values()[0]
         else:
-            print 'ERROR: Processing AST: ' + os.path.basename(file_name)
+            print('ERROR: Processing AST: ' + os.path.basename(file_name))
         new_asts[filename] = ast
 
     def import_idl_files(self, file_paths, import_options, is_dart_idl):
@@ -642,8 +642,8 @@
         for warning in report_unions_to_any():
             _logger.warning(warning)
 
-        print 'Total %s files %sprocessed in databasebuilder in %s seconds' % \
-        (len(file_paths), '', round((end_time - start_time), 2))
+        print('Total %s files %sprocessed in databasebuilder in %s seconds' % \
+        (len(file_paths), '', round((end_time - start_time), 2)))
 
     def _process_idl_file(self, idl_file, import_options, dart_idl=False):
         # TODO(terry): strip_ext_attributes on an idl_file does nothing.
@@ -862,27 +862,30 @@
         # Report all interface marked with NoInterfaceObject and their usage.
         self._output_examination(check_dictionaries=False)
 
-        print '\nKey:'
-        print '  (READ-ONLY) - read-only attribute has relationship'
-        print '  (GET/SET)   - attribute has relationship'
-        print '  RETURN      - operation\'s returned value has relationship'
-        print '  (ARGUMENT)  - operation\'s argument(s) has relationship'
-        print ''
-        print '  (New)       - After dictionary name if constructor(s) exist'
-        print '  (Ops,Props,New) after a NoInterfaceObject name is defined as:'
-        print '    Ops       - number of operations for a NoInterfaceObject'
-        print '    Props     - number of properties for a NoInterfaceObject'
-        print '    New       - T(#) number constructors for a NoInterfaceObject'
-        print '                F no constructors for a NoInterfaceObject'
-        print '                e.g., an interface 5 operations, 3 properties and 2'
-        print '                      constructors would display (5,3,T(2))'
+        print('''
+Key:
+  (READ-ONLY) - read-only attribute has relationship
+  (GET/SET)   - attribute has relationship
+  RETURN      - operation\'s returned value has relationship
+  (ARGUMENT)  - operation\'s argument(s) has relationship
 
-        print '\n\nExamination Complete\n'
+  (New)       - After dictionary name if constructor(s) exist
+  (Ops,Props,New) after a NoInterfaceObject name is defined as:
+    Ops       - number of operations for a NoInterfaceObject
+    Props     - number of properties for a NoInterfaceObject
+    New       - T(#) number constructors for a NoInterfaceObject
+                F no constructors for a NoInterfaceObject
+                e.g., an interface 5 operations, 3 properties and 2
+                      constructors would display (5,3,T(2))
+
+
+Examination Complete
+''')
 
     def _output_examination(self, check_dictionaries=True):
         # Output diagnostics. First columns is Dictionary or NoInterfaceObject e.g.,
         # |  Dictionary  |  Used In Interface  |  Usage Operation/Attribute  |
-        print '\n\n'
+        print('\n\n')
         title_bar = ['Dictionary', 'Used In Interface', 'Usage Operation/Attribute'] if check_dictionaries \
                     else ['NoInterfaceObject (Ops,Props,New)', 'Used In Interface', 'Usage Operation/Attribute']
         self._tabulate_title(title_bar)
@@ -993,7 +996,8 @@
                         return
 
             # If we get to this point, the IDL dictionary was never defined ... oops.
-            print 'DIAGNOSE_ERROR: IDL Dictionary %s doesn\'t exist.' % dictionary_id
+            print('DIAGNOSE_ERROR: IDL Dictionary %s doesn\'t exist.' %
+                  dictionary_id)
 
     # Iterator function to look for any IDLType that is an interface marked with
     # NoInterfaceObject then remember that interface.
diff --git a/tools/dom/scripts/databasebuilder_test.py b/tools/dom/scripts/databasebuilder_test.py
index c1132a1..7467b3e 100755
--- a/tools/dom/scripts/databasebuilder_test.py
+++ b/tools/dom/scripts/databasebuilder_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 # Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
diff --git a/tools/dom/scripts/emitter.py b/tools/dom/scripts/emitter.py
index 7d45fbe..21f4a17 100755
--- a/tools/dom/scripts/emitter.py
+++ b/tools/dom/scripts/emitter.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 # Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
diff --git a/tools/dom/scripts/emitter_test.py b/tools/dom/scripts/emitter_test.py
index f1d168e..eb50c2a 100755
--- a/tools/dom/scripts/emitter_test.py
+++ b/tools/dom/scripts/emitter_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 # Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
diff --git a/tools/dom/scripts/fremontcutbuilder.py b/tools/dom/scripts/fremontcutbuilder.py
index a9ce07c..bd5fcad 100755
--- a/tools/dom/scripts/fremontcutbuilder.py
+++ b/tools/dom/scripts/fremontcutbuilder.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 # Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
@@ -155,7 +155,7 @@
                         sorted(unknown_conditionals))
         _logger.warning('Please update fremontcutbuilder.py')
 
-    print 'Merging interfaces %s seconds' % round(time.time() - start_time, 2)
+    print('Merging interfaces %s seconds' % round(time.time() - start_time, 2))
 
     return db
 
diff --git a/tools/dom/scripts/generate_blink_file.py b/tools/dom/scripts/generate_blink_file.py
index 6414f7a..4e9c2a4 100644
--- a/tools/dom/scripts/generate_blink_file.py
+++ b/tools/dom/scripts/generate_blink_file.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 #
 # Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
@@ -199,7 +199,7 @@
 bool TRACK_STATS = true;
 dumpStats() {
   print("------------ STATS ----------------");
-  print(Blink_JsNative_DomException.getPropertyStats.toString()); 
+  print(Blink_JsNative_DomException.getPropertyStats.toString());
   print(Blink_JsNative_DomException.setPropertyStats.toString());
   print(Blink_JsNative_DomException.callMethodStats.toString());
   print(Blink_JsNative_DomException.constructorStats.toString());
@@ -209,8 +209,8 @@
 clearStats() {
   Blink_JsNative_DomException.getPropertyStats.clear();
   Blink_JsNative_DomException.setPropertyStats.clear();
-  Blink_JsNative_DomException.callMethodStats.clear();  
-  Blink_JsNative_DomException.constructorStats.clear();  
+  Blink_JsNative_DomException.callMethodStats.clear();
+  Blink_JsNative_DomException.constructorStats.clear();
 }
 
 class Blink_JsNative_DomException {
@@ -388,7 +388,7 @@
 
 def ConstantOutputOrder(a, b):
     """Canonical output ordering for constants."""
-    return cmp(a.id, b.id)
+    return (a.id > b.id) - (a.id < b.id)
 
 
 def generate_parameter_entries(param_infos):
@@ -617,8 +617,8 @@
                     Select_Stub(OPERATION_2, is_native) % (name, interface.id,
                                                            name))
             else:
-                print "FATAL ERROR: _blink emitter operator %s.%s" % (
-                    interface.id, name)
+                print("FATAL ERROR: _blink emitter operator %s.%s" %
+                      (interface.id, name))
                 exit
 
         return
diff --git a/tools/dom/scripts/generator.py b/tools/dom/scripts/generator.py
index 9f64192..0a07e20 100644
--- a/tools/dom/scripts/generator.py
+++ b/tools/dom/scripts/generator.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 # Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
@@ -937,7 +937,7 @@
 
 def ConstantOutputOrder(a, b):
     """Canonical output ordering for constants."""
-    return cmp(a.id, b.id)
+    return (a.id > b.id) - (a.id < b.id)
 
 
 def _FormatNameList(names):
@@ -2098,7 +2098,8 @@
                 # It's a typedef (implied union)
                 return self.TypeInfo('any')
             else:
-                print "ERROR: Unexpected interface, or type not found. %s" % type_name
+                print("ERROR: Unexpected interface, or type not found. %s" %
+                      type_name)
 
             if 'Callback' in interface.ext_attrs:
                 return CallbackIDLTypeInfo(
diff --git a/tools/dom/scripts/htmldartgenerator.py b/tools/dom/scripts/htmldartgenerator.py
index 08c65a8..7891aa3 100644
--- a/tools/dom/scripts/htmldartgenerator.py
+++ b/tools/dom/scripts/htmldartgenerator.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 # Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
@@ -760,8 +760,9 @@
                 if not (param_list.endswith(', mapArg') or
                         param_list.endswith(', options') or
                         param_list == mapArg):
-                    print "ERROR: %s.%s - Last parameter or only parameter %s is not of type Map" % (
-                        self._interface.id, html_name, mapArg)
+                    print(
+                        "ERROR: %s.%s - Last parameter or only parameter %s is not of type Map"
+                        % (self._interface.id, html_name, mapArg))
                 param_list = '%s_dict' % param_list
 
                 if mapArgOptional:
diff --git a/tools/dom/scripts/htmleventgenerator.py b/tools/dom/scripts/htmleventgenerator.py
index fcad117..d614e88 100644
--- a/tools/dom/scripts/htmleventgenerator.py
+++ b/tools/dom/scripts/htmleventgenerator.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 # Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
diff --git a/tools/dom/scripts/htmlrenamer.py b/tools/dom/scripts/htmlrenamer.py
index 014e811..96cdf8a 100644
--- a/tools/dom/scripts/htmlrenamer.py
+++ b/tools/dom/scripts/htmlrenamer.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 # Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
diff --git a/tools/dom/scripts/idlnode.py b/tools/dom/scripts/idlnode.py
index 4518982..40e86e5 100644
--- a/tools/dom/scripts/idlnode.py
+++ b/tools/dom/scripts/idlnode.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 # Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
@@ -81,7 +81,7 @@
                                          ('%s %s' % (self.id, extras)).strip(),
                                          hash(self))
             return '<%s %s 0x%x>' % (type(self).__name__, extras, hash(self))
-        except Exception, e:
+        except Exception as e:
             return "can't convert to string: %s" % e
 
     def _extra_repr(self):
@@ -264,7 +264,7 @@
         }
         result = label_field.get(label)
         if result != '' and not (result):
-            print 'FATAL ERROR: AST mapping name not found %s.' % label
+            print('FATAL ERROR: AST mapping name not found %s.' % label)
         return result if result else ''
 
     def _convert_all(self, ast, label, idlnode_ctor):
@@ -708,7 +708,7 @@
                     # should consider synthesizing a new interface (e.g., UnionType) that's
                     # both Type1 and Type2.
         if not self.id:
-            print '>>>> __module__ %s' % ast.__module__
+            print('>>>> __module__ %s' % ast.__module__)
             raise SyntaxError('Could not parse type %s' % (ast))
 
     def _label_to_type(self, label, ast):
diff --git a/tools/dom/scripts/idlnode_test.py b/tools/dom/scripts/idlnode_test.py
index 8c21546..61de65a 100755
--- a/tools/dom/scripts/idlnode_test.py
+++ b/tools/dom/scripts/idlnode_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 # Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
diff --git a/tools/dom/scripts/idlrenderer.py b/tools/dom/scripts/idlrenderer.py
index 40edc3d..ba095dd 100755
--- a/tools/dom/scripts/idlrenderer.py
+++ b/tools/dom/scripts/idlrenderer.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 # Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
diff --git a/tools/dom/scripts/idlrenderer_test.py b/tools/dom/scripts/idlrenderer_test.py
index dd057c2..000ef2a 100755
--- a/tools/dom/scripts/idlrenderer_test.py
+++ b/tools/dom/scripts/idlrenderer_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 # Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
diff --git a/tools/dom/scripts/idlsync.py b/tools/dom/scripts/idlsync.py
index 4402849..f95c0b9 100755
--- a/tools/dom/scripts/idlsync.py
+++ b/tools/dom/scripts/idlsync.py
@@ -10,14 +10,14 @@
 #
 # To update all *.idl, *.py, LICENSE files, and IDLExtendedAttributes.txt:
 #      > cd sdk
-#      > python tools/dom/scripts/idlsync.py
+#      > python3 tools/dom/scripts/idlsync.py
 #
 # Display blink files to delete, copy, update, and collisions to review:
-#      > python tools/dom/scripts/idlsync.py --check
+#      > python3 tools/dom/scripts/idlsync.py --check
 #
 # Bring over all blink files to dart/third_party/WebCore (*.py, *.idl, and
 # IDLExtendedAttributes.txt):
-#      > python tools/dom/scripts/idlsync.py
+#      > python3 tools/dom/scripts/idlsync.py
 #
 # Update the DEPS file SHA for "WebCore_rev" with the committed changes of files
 # in WebCore e.g.,   "WebCore_rev": "@NNNNNNNNNNNNNNNNNNNNNNNNN"
@@ -117,14 +117,14 @@
 def RunCommand(cmd, valid_exits=[0]):
     """Executes a shell command and return its stdout."""
     if isVerbose():
-        print ' '.join(cmd)
+        print(' '.join(cmd))
     pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
     output = pipe.communicate()
     if pipe.returncode in valid_exits:
         return output[0]
     else:
-        print output[1]
-        print 'FAILED. RET_CODE=%d' % pipe.returncode
+        print(output[1])
+        print('FAILED. RET_CODE=%d' % pipe.returncode)
         sys.exit(pipe.returncode)
 
 
@@ -298,7 +298,8 @@
             remotes_list[1] == GIT_REMOTES_CHROMIUM):
         return True
 
-    print 'ERROR: Unable to find dart/dartium/src repository %s' % GIT_REMOTES_CHROMIUM
+    print('ERROR: Unable to find dart/dartium/src repository %s' %
+          GIT_REMOTES_CHROMIUM)
     return False
 
 
@@ -348,25 +349,25 @@
     # Get the SHA for the Chromium/WebKit changes for Dartium.
     #revision = url[len(url_base):]
     revision = foundIt.group(1)[1:]
-    print '%s' % revision
+    print('%s' % revision)
     return revision
 
 
 def copy_subdir(src, src_prefix, dest, subdir):
     idls_deleted = remove_obsolete_webcore_files(dest, src, subdir)
-    print "%s files removed in WebCore %s" % (idls_deleted.__len__(), subdir)
+    print("%s files removed in WebCore %s" % (idls_deleted.__len__(), subdir))
     if isVerbose():
         for delete_file in idls_deleted:
-            print "    %s" % delete_file
+            print("    %s" % delete_file)
 
     idls_copied, py_copied, other_copied = copy_files(
         os.path.join(src, subdir), src_prefix, dest)
     if idls_copied > 0:
-        print "Copied %s IDLs to %s" % (idls_copied, subdir)
+        print("Copied %s IDLs to %s" % (idls_copied, subdir))
     if py_copied > 0:
-        print "Copied %s PYs to %s" % (py_copied, subdir)
+        print("Copied %s PYs to %s" % (py_copied, subdir))
     if other_copied > 0:
-        print "Copied %s other to %s\n" % (other_copied, subdir)
+        print("Copied %s other to %s\n" % (other_copied, subdir))
 
 
 def main():
@@ -375,7 +376,8 @@
 
     current_dir = os.path.dirname(os.path.abspath(__file__))
     if not current_dir.endswith(SOURCE_FILE_DIR):
-        print 'ERROR: idlsync.py not run in proper directory (%s)\n', current_dir
+        print('ERROR: idlsync.py not run in proper directory (%s)\n',
+              current_dir)
 
     base_directory = current_dir[:current_dir.rfind(SOURCE_FILE_DIR)]
 
@@ -384,8 +386,8 @@
     webcore_revision = GetDEPSWebCoreGitRevision(deps, 'webkit')
     chromium_sha = getChromiumSHA()
     if webcore_revision == chromium_sha:
-        print "ERROR: Nothing to update in WebCore, WebCore_rev SHA in DEPS " \
-              "matches Chromium GIT master SHA in %s" % options['webkit_dir']
+        print("ERROR: Nothing to update in WebCore, WebCore_rev SHA in DEPS "
+              "matches Chromium GIT master SHA in %s" % options['webkit_dir'])
         return
 
     start_time = time.time()
@@ -417,12 +419,14 @@
 
     end_time = time.time()
 
-    print 'WARNING: File(s) contain FIXMEDART and are NOT "git add " please review:'
+    print(
+        'WARNING: File(s) contain FIXMEDART and are NOT "git add " please review:'
+    )
     for warning in warning_messages:
-        print '    %s' % warning
+        print('    %s' % warning)
 
-    print '\nDone idlsync completed in %s seconds' % round(
-        end_time - start_time, 2)
+    print('\nDone idlsync completed in %s seconds' %
+          round(end_time - start_time, 2))
 
 
 if __name__ == '__main__':
diff --git a/tools/dom/scripts/mdnreader.py b/tools/dom/scripts/mdnreader.py
index 9fe43a3..b142b7a 100644
--- a/tools/dom/scripts/mdnreader.py
+++ b/tools/dom/scripts/mdnreader.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 # Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
diff --git a/tools/dom/scripts/monitored.py b/tools/dom/scripts/monitored.py
index 55d4e11..1b3babe 100644
--- a/tools/dom/scripts/monitored.py
+++ b/tools/dom/scripts/monitored.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 # Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
diff --git a/tools/dom/scripts/multiemitter.py b/tools/dom/scripts/multiemitter.py
index 64a8b2d..a897a16 100644
--- a/tools/dom/scripts/multiemitter.py
+++ b/tools/dom/scripts/multiemitter.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 # Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
diff --git a/tools/dom/scripts/multiemitter_test.py b/tools/dom/scripts/multiemitter_test.py
index 0b114a5..17c360d 100644
--- a/tools/dom/scripts/multiemitter_test.py
+++ b/tools/dom/scripts/multiemitter_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 # Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
diff --git a/tools/dom/scripts/systemhtml.py b/tools/dom/scripts/systemhtml.py
index 3b35750..5749cd9 100644
--- a/tools/dom/scripts/systemhtml.py
+++ b/tools/dom/scripts/systemhtml.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 # Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
@@ -2024,7 +2024,9 @@
                                                                                 -
                                                                                 1]
                     else:
-                        print "ERROR: Arguments exceede 20 - please fix Python code to handle more."
+                        print(
+                            "ERROR: Arguments exceede 20 - please fix Python code to handle more."
+                        )
                 self._members_emitter.Emit(
                     '  $RENAME$METADATA$MODIFIERS$TYPE$TARGET($PARAMS) =>\n'
                     '      promiseToFuture(JS("", "#.$JSNAME($HASH_STR)", this$CALLING_PARAMS));\n',
diff --git a/tools/dom/scripts/systemnative.py b/tools/dom/scripts/systemnative.py
index 296bd67..2c2ab7c 100644
--- a/tools/dom/scripts/systemnative.py
+++ b/tools/dom/scripts/systemnative.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 # Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
diff --git a/tools/dom/scripts/templateloader.py b/tools/dom/scripts/templateloader.py
index fcbebe8..7e69a8e 100644
--- a/tools/dom/scripts/templateloader.py
+++ b/tools/dom/scripts/templateloader.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 # Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
diff --git a/tools/dom/scripts/templateloader_test.py b/tools/dom/scripts/templateloader_test.py
index edcd759..4b16df7 100755
--- a/tools/dom/scripts/templateloader_test.py
+++ b/tools/dom/scripts/templateloader_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 # Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
diff --git a/tools/find_depot_tools.py b/tools/find_depot_tools.py
index ed06c94..413a12f 100644
--- a/tools/find_depot_tools.py
+++ b/tools/find_depot_tools.py
@@ -6,10 +6,11 @@
 imports breakpad.
 """
 
+from __future__ import print_function
+
 import os
 import sys
 
-
 def IsRealDepotTools(path):
     return os.path.isfile(os.path.join(path, 'gclient.py'))
 
@@ -35,7 +36,7 @@
             return i
         previous_dir = root_dir
         root_dir = os.path.dirname(root_dir)
-    print >> sys.stderr, 'Failed to find depot_tools'
+    print('Failed to find depot_tools', file=sys.stderr)
     return None
 
 
diff --git a/tools/fuchsia/find_resources.py b/tools/fuchsia/find_resources.py
index 9d38053..f8b0518 100755
--- a/tools/fuchsia/find_resources.py
+++ b/tools/fuchsia/find_resources.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
@@ -24,34 +24,37 @@
 
 
 def listFiles(path):
-  allFiles = []
-  for dirpath, dirs, files in os.walk(join(DART_DIR, path)):
-    allFiles += [relpath(abspath(join(dirpath, p)), DART_DIR) for p in files]
-  return allFiles
+    allFiles = []
+    for dirpath, dirs, files in os.walk(join(DART_DIR, path)):
+        allFiles += [
+            relpath(abspath(join(dirpath, p)), DART_DIR) for p in files
+        ]
+    return allFiles
 
 
 def printOutput(files):
-  print('[')
-  print(',\n'.join([
-    '  {\n    "path": "%s",\n    "dest": "data/%s"\n  }' % (f, f) for f in files
-  ]))
-  print(']')
+    print('[')
+    print(',\n'.join([
+        '  {\n    "path": "%s",\n    "dest": "data/%s"\n  }' % (f, f)
+        for f in files
+    ]))
+    print(']')
 
 
 def main():
-  if len(sys.argv) < 2:
-    print('Expected at least 1 arg, the paths to search.')
-    return 1
-  allFiles = []
-  for directory in sys.argv[1:]:
-    files = listFiles(directory)
-    if len(files) == 0:
-      print('Did not find any files in the directory: ' + directory)
-      return 2
-    allFiles += files
-  printOutput(sorted(allFiles))
-  return 0
+    if len(sys.argv) < 2:
+        print('Expected at least 1 arg, the paths to search.')
+        return 1
+    allFiles = []
+    for directory in sys.argv[1:]:
+        files = listFiles(directory)
+        if len(files) == 0:
+            print('Did not find any files in the directory: ' + directory)
+            return 2
+        allFiles += files
+    printOutput(sorted(allFiles))
+    return 0
 
 
 if __name__ == '__main__':
-  sys.exit(main())
+    sys.exit(main())
diff --git a/tools/gen_fuchsia_test_manifest.py b/tools/gen_fuchsia_test_manifest.py
index fa754b5..d521e36 100755
--- a/tools/gen_fuchsia_test_manifest.py
+++ b/tools/gen_fuchsia_test_manifest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright 2017 The Dart project authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/generate_buildfiles.py b/tools/generate_buildfiles.py
index 1de6936..693de54 100755
--- a/tools/generate_buildfiles.py
+++ b/tools/generate_buildfiles.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright 2016 The Dart project authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
@@ -29,7 +29,7 @@
     if not HOST_OS in ['linux', 'macos']:
         return 0
     gn_command = [
-        'python',
+        'python3',
         os.path.join(DART_ROOT, 'tools', 'gn.py'),
         '-m',
         'all',
@@ -48,7 +48,7 @@
     if HOST_OS != 'linux':
         return 0
     gn_command = [
-        'python',
+        'python3',
         os.path.join(DART_ROOT, 'tools', 'gn.py'),
         '-m',
         'all',
@@ -63,7 +63,7 @@
 
 def RunHostGn(options):
     gn_command = [
-        'python',
+        'python3',
         os.path.join(DART_ROOT, 'tools', 'gn.py'),
         '-m',
         'all',
diff --git a/tools/generate_idefiles.py b/tools/generate_idefiles.py
index d84bcb2..7809c64 100755
--- a/tools/generate_idefiles.py
+++ b/tools/generate_idefiles.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 #
 # Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
@@ -42,7 +42,7 @@
     fname = os.path.join(options.dir, "compile_commands.json")
 
     if os.path.isfile(fname) and not options.force:
-        print fname + " already exists, use --force to override"
+        print(fname + " already exists, use --force to override")
         return
 
     gn_result = generate_buildfiles.RunGn(options)
@@ -132,7 +132,7 @@
     fname = os.path.join(options.dir, "analysis_options.yaml")
 
     if os.path.isfile(fname) and not options.force:
-        print fname + " already exists, use --force to override"
+        print(fname + " already exists, use --force to override")
         return
 
     with open(fname, "w") as f:
diff --git a/tools/get_dot_git_folder.py b/tools/get_dot_git_folder.py
index 443addbf..482616c 100755
--- a/tools/get_dot_git_folder.py
+++ b/tools/get_dot_git_folder.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
@@ -8,7 +8,6 @@
 
 import sys
 import subprocess
-import os
 import utils
 
 
@@ -24,13 +23,15 @@
                                        stdout=subprocess.PIPE,
                                        stderr=subprocess.PIPE,
                                        stdin=subprocess.PIPE,
-                                       shell=True)
+                                       shell=True,
+                                       universal_newlines=True)
         else:
             process = subprocess.Popen(args,
                                        stdout=subprocess.PIPE,
                                        stderr=subprocess.PIPE,
                                        stdin=subprocess.PIPE,
-                                       shell=False)
+                                       shell=False,
+                                       universal_newlines=True)
 
         outs, _ = process.communicate()
 
diff --git a/tools/gn.py b/tools/gn.py
index 6f2b0c1..dc355cf 100755
--- a/tools/gn.py
+++ b/tools/gn.py
@@ -1,11 +1,10 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright 2016 The Dart project authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
 import argparse
 import os
-import shutil
 import subprocess
 import sys
 import time
@@ -64,7 +63,7 @@
             return '%s=%d' % (key, value)
         return '%s="%s"' % (key, value)
 
-    return [merge(x, y) for x, y in gn_args.iteritems()]
+    return [merge(x, y) for x, y in gn_args.items()]
 
 
 def HostCpuForArch(arch):
diff --git a/tools/linux_dist_support/debian/control b/tools/linux_dist_support/debian/control
index 04484ab..2043c95 100644
--- a/tools/linux_dist_support/debian/control
+++ b/tools/linux_dist_support/debian/control
@@ -4,7 +4,7 @@
 Priority: optional
 Standards-Version: 3.9.2
 Build-Depends: debhelper (>= 9),
-	python,
+	python3,
 
 Package: dart
 Architecture: amd64
diff --git a/tools/linux_dist_support/debian/rules b/tools/linux_dist_support/debian/rules
index da3ff23..1ccacf5 100755
--- a/tools/linux_dist_support/debian/rules
+++ b/tools/linux_dist_support/debian/rules
@@ -53,11 +53,11 @@
 	find . -name *.Makefile -execdir rm -f {} \;
 
 override_dh_auto_configure:
-	python dart/tools/generate_buildfiles.py
+	python3 dart/tools/generate_buildfiles.py
 
 override_dh_auto_build:
 	cd dart; \
-	python tools/build.py -v -m release -a $(ARCH) $(TOOLCHAIN) create_sdk; \
+	python3 tools/build.py -v -m release -a $(ARCH) $(TOOLCHAIN) create_sdk; \
 	cd ..
 
 # Building the Dart SDK will already strip all binaries.
diff --git a/tools/list_dart_files.py b/tools/list_dart_files.py
index cc131de..ec9da09 100755
--- a/tools/list_dart_files.py
+++ b/tools/list_dart_files.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
@@ -9,7 +9,7 @@
 produces absolute paths.
 
 Usage:
-  python tools/list_dart_files.py {absolute, relative} <directory> <pattern>
+  python3 tools/list_dart_files.py {absolute, relative} <directory> <pattern>
 """
 
 import os
@@ -48,7 +48,7 @@
                 else:
                     fullname = os.path.relpath(os.path.join(root, filename))
                 fullname = fullname.replace(os.sep, '/')
-                print (fullname)
+                print(fullname)
 
 
 if __name__ == '__main__':
diff --git a/tools/make_version.py b/tools/make_version.py
index 0e6e8be..c99511b 100755
--- a/tools/make_version.py
+++ b/tools/make_version.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
@@ -11,7 +11,6 @@
 import hashlib
 import os
 import sys
-import time
 import utils
 
 # When these files change, snapshots created by the VM are potentially no longer
@@ -85,7 +84,7 @@
         version_time = utils.GetGitTimestamp()
     if version_time == None:
         version_time = 'Unknown timestamp'
-    version = version.replace('{{COMMIT_TIME}}', version_time.decode('utf-8'))
+    version = version.replace('{{COMMIT_TIME}}', version_time)
 
     snapshot_hash = MakeSnapshotHashString()
     version = version.replace('{{SNAPSHOT_HASH}}', snapshot_hash)
diff --git a/tools/patches/flutter-engine/apply.sh b/tools/patches/flutter-engine/apply.sh
index 91918a1..b5b7e0c5 100755
--- a/tools/patches/flutter-engine/apply.sh
+++ b/tools/patches/flutter-engine/apply.sh
@@ -57,7 +57,7 @@
   # referencing.
   # Normally gclient sync would update the cache - but we are bypassing
   # it here.
-  git_cache=$(python -c 'import imp; config = imp.load_source("config", ".gclient"); print getattr(config, "cache_dir", "")')
+  git_cache=$(python3 -c 'import imp; config = imp.load_source("config", ".gclient"); print(getattr(config, "cache_dir", ""))')
 
   # DEPS file might have been patched with new version of packages that
   # Dart SDK depends on. Get information about dependencies from the
diff --git a/tools/promote.py b/tools/promote.py
old mode 100644
new mode 100755
index 7b57fb3..966b735
--- a/tools/promote.py
+++ b/tools/promote.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
@@ -6,10 +6,8 @@
 
 # Dart SDK promote tools.
 
-import imp
 import optparse
 import os
-import subprocess
 import sys
 import time
 import urllib
@@ -28,7 +26,7 @@
               locations.
 
     Example: Promote version 2.5.0 on the stable channel:
-        python editor/build/promote.py promote --channel=stable --version=2.5.0
+        python3 tools/promote.py promote --channel=stable --version=2.5.0
   """
 
     result = optparse.OptionParser(usage=usage)
@@ -64,7 +62,7 @@
     (options, args) = parser.parse_args()
 
     def die(msg):
-        print msg
+        print(msg)
         parser.print_help()
         sys.exit(1)
 
@@ -97,14 +95,14 @@
 
 def UpdateDocs():
     try:
-        print 'Updating docs'
+        print('Updating docs')
         url = 'http://api.dartlang.org/docs/releases/latest/?force_reload=true'
         f = urllib.urlopen(url)
         f.read()
-        print 'Successfully updated api docs'
+        print('Successfully updated api docs')
     except Exception as e:
-        print 'Could not update api docs, please manually update them'
-        print 'Failed with: %s' % e
+        print('Could not update api docs, please manually update them')
+        print('Failed with: %s' % e)
 
 
 def _PromoteDartArchiveBuild(channel, source_channel, revision):
@@ -187,7 +185,7 @@
     gsutilTool = join(DART_PATH, 'third_party', 'gsutil', 'gsutil')
     command = [sys.executable, gsutilTool] + cmd
     if DRY_RUN:
-        print 'DRY runnning: %s' % command
+        print('DRY runnning: %s' % command)
         return (None, None, 0)
     return bot_utils.run(command, throw_on_error=throw_on_error)
 
diff --git a/tools/publish_pkg.py b/tools/publish_pkg.py
index 2ecf2ba..6cd50f0 100755
--- a/tools/publish_pkg.py
+++ b/tools/publish_pkg.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
@@ -25,7 +25,7 @@
 
     pubspec = os.path.join(HOME, argv[1], 'pubspec.yaml')
     if not os.path.exists(pubspec):
-        print 'Error: did not find pubspec.yaml at ' + pubspec
+        print('Error: did not find pubspec.yaml at ' + pubspec)
         return -1
 
     with open(pubspec) as pubspecFile:
@@ -45,16 +45,17 @@
             version = line[len('version:'):].strip()
         if inDependencies:
             if line.endswith(': any'):
-                print 'Error in %s: should not use "any" version constraint: %s' % (
-                    pubspec, line)
+                print(
+                    'Error in %s: should not use "any" version constraint: %s' %
+                    (pubspec, line))
                 return -1
 
     if not version:
-        print 'Error in %s: did not find package version.' % pubspec
+        print('Error in %s: did not find package version.' % pubspec)
         return -1
 
     if not foundSdkConstraint:
-        print 'Error in %s: did not find SDK version constraint.' % pubspec
+        print('Error in %s: did not find SDK version constraint.' % pubspec)
         return -1
 
     tmpDir = tempfile.mkdtemp()
@@ -95,7 +96,7 @@
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ''')
 
-    print 'publishing version ' + version + ' of ' + argv[1] + ' to pub.\n'
+    print('publishing version ' + version + ' of ' + argv[1] + ' to pub.\n')
 
     # TODO(jmesserly): this code puts things in the pub cache. Useful for testing
     # without actually uploading.
diff --git a/tools/run_debian_build.sh b/tools/run_debian_build.sh
index e0d54a1..6fb1866 100755
--- a/tools/run_debian_build.sh
+++ b/tools/run_debian_build.sh
@@ -6,10 +6,11 @@
 
 ninja=$(which ninja)
 depot_tools=$(dirname $ninja)
-cmd="sed -i /jessie-updates/d /etc/apt/sources.list\
-    && apt-get update && apt-get -y install build-essential debhelper git python\
-    && PATH=\"$depot_tools:\$PATH\"\
-    python tools/bots/linux_distribution_support.py"
+cmd="sed -i /jessie-updates/d /etc/apt/sources.list \
+    && apt-get update \
+    && apt-get -y install build-essential debhelper git python3 \
+    && PATH=\"$depot_tools:\$PATH\" \
+    python3 tools/bots/linux_distribution_support.py"
 image="launcher.gcr.io/google/debian8:latest"
 docker run -e BUILDBOT_BUILDERNAME -v $depot_tools:$depot_tools\
     -v `pwd`:`pwd` -w `pwd` -i --rm $image bash -c "$cmd"
diff --git a/tools/spec_parse.py b/tools/spec_parse.py
index 90a97e8..d9238f5 100755
--- a/tools/spec_parse.py
+++ b/tools/spec_parse.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (c) 2017, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
diff --git a/tools/spec_parser/Dart.g b/tools/spec_parser/Dart.g
index e13e789..0da4bd2 100644
--- a/tools/spec_parser/Dart.g
+++ b/tools/spec_parser/Dart.g
@@ -360,23 +360,6 @@
     |    constructorSignature
     ;
 
-// https://github.com/dart-lang/sdk/issues/29501 reports on the problem which
-// was solved by adding a case for redirectingFactoryConstructorSignature.
-// TODO(eernst): Close that issue when this is integrated into the spec.
-
-// https://github.com/dart-lang/sdk/issues/29502 reports on the problem that
-// than external const factory constructor declaration cannot be derived by
-// the spec grammar (and also not by this grammar). The following fixes were
-// introduced for that: Added the 'factoryConstructorSignature' case below in
-// 'declaration'; also added 'CONST?' in the 'factoryConstructorSignature'
-// rule, such that const factories in general are allowed.
-// TODO(eernst): Close that issue when this is integrated into the spec.
-
-// TODO(eernst): Note that `EXTERNAL? STATIC? functionSignature` includes
-// `STATIC functionSignature`, but a static function cannot be abstract.
-// We might want to make that a syntax error rather than a static semantic
-// check.
-
 declaration
     :    EXTERNAL factoryConstructorSignature
     |    EXTERNAL constantConstructorSignature
@@ -901,27 +884,7 @@
 
 identifierNotFUNCTION
     :    IDENTIFIER
-    |    ABSTRACT // Built-in identifier.
-    |    AS // Built-in identifier.
-    |    COVARIANT // Built-in identifier.
-    |    DEFERRED // Built-in identifier.
-    |    DYNAMIC // Built-in identifier.
-    |    EXPORT // Built-in identifier.
-    |    EXTERNAL // Built-in identifier.
-    |    FACTORY // Built-in identifier.
-    |    GET // Built-in identifier.
-    |    IMPLEMENTS // Built-in identifier.
-    |    IMPORT // Built-in identifier.
-    |    INTERFACE // Built-in identifier.
-    |    LATE // Built-in identifier.
-    |    LIBRARY // Built-in identifier.
-    |    MIXIN // Built-in identifier.
-    |    OPERATOR // Built-in identifier.
-    |    PART // Built-in identifier.
-    |    REQUIRED // Built-in identifier.
-    |    SET // Built-in identifier.
-    |    STATIC // Built-in identifier.
-    |    TYPEDEF // Built-in identifier.
+    |    builtInIdentifier
     |    ASYNC // Not a built-in identifier.
     |    HIDE // Not a built-in identifier.
     |    OF // Not a built-in identifier.
@@ -1219,7 +1182,7 @@
     ;
 
 typeAlias
-    :    TYPEDEF typeIdentifier typeParameters? '=' functionType ';'
+    :    TYPEDEF typeIdentifier typeParameters? '=' type ';'
     |    TYPEDEF functionTypeAlias
     ;
 
@@ -1258,8 +1221,8 @@
     ;
 
 normalParameterType
-    :    typedIdentifier
-    |    type
+    :    metadata typedIdentifier
+    |    metadata type
     ;
 
 optionalParameterTypes
@@ -1276,7 +1239,7 @@
     ;
 
 namedParameterType
-    :    REQUIRED? typedIdentifier
+    :    metadata REQUIRED? typedIdentifier
     ;
 
 typedIdentifier
@@ -1290,7 +1253,7 @@
     ;
 
 symbolLiteral
-    :    '#' (operator | (identifier ('.' identifier)*))
+    :    '#' (operator | (identifier ('.' identifier)*) | VOID)
     ;
 
 // Not used in the specification (needed here for <uri>).
@@ -1324,6 +1287,68 @@
          MULTI_LINE_STRING_DQ_MID_END
     ;
 
+reservedWord
+    :    ASSERT
+    |    BREAK
+    |    CASE
+    |    CATCH
+    |    CLASS
+    |    CONST
+    |    CONTINUE
+    |    DEFAULT
+    |    DO
+    |    ELSE
+    |    ENUM
+    |    EXTENDS
+    |    FALSE
+    |    FINAL
+    |    FINALLY
+    |    FOR
+    |    IF
+    |    IN
+    |    IS
+    |    NEW
+    |    NULL
+    |    RETHROW
+    |    RETURN
+    |    SUPER
+    |    SWITCH
+    |    THIS
+    |    THROW
+    |    TRUE
+    |    TRY
+    |    VAR
+    |    VOID
+    |    WHILE
+    |    WITH
+    ;
+
+builtInIdentifier
+    :    ABSTRACT
+    |    AS
+    |    COVARIANT
+    |    DEFERRED
+    |    DYNAMIC
+    |    EXPORT
+    |    EXTENSION
+    |    EXTERNAL
+    |    FACTORY
+    |    FUNCTION
+    |    GET
+    |    IMPLEMENTS
+    |    IMPORT
+    |    INTERFACE
+    |    LATE
+    |    LIBRARY
+    |    OPERATOR
+    |    MIXIN
+    |    PART
+    |    REQUIRED
+    |    SET
+    |    STATIC
+    |    TYPEDEF
+    ;
+
 // ---------------------------------------- Lexer rules.
 
 fragment
diff --git a/tools/task_kill.py b/tools/task_kill.py
index 3c36acc..8281475 100755
--- a/tools/task_kill.py
+++ b/tools/task_kill.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
@@ -105,8 +105,11 @@
     # Sample output:
     # 1 /sbin/launchd
     # 80943 /Applications/Safari.app/Contents/MacOS/Safari
-    p = subprocess.Popen(
-        cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
+    p = subprocess.Popen(cmd,
+                         stdout=subprocess.PIPE,
+                         stderr=subprocess.PIPE,
+                         shell=True,
+                         universal_newlines=True)
     output, stderr = p.communicate()
     results = []
     lines = output.splitlines()
@@ -123,8 +126,11 @@
     cmd = 'tasklist /FI "IMAGENAME eq %s" /NH' % process_name
     # Sample output:
     # dart.exe    4356 Console            1      6,800 K
-    p = subprocess.Popen(
-        cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
+    p = subprocess.Popen(cmd,
+                         stdout=subprocess.PIPE,
+                         stderr=subprocess.PIPE,
+                         shell=True,
+                         universal_newlines=True)
     output, stderr = p.communicate()
     results = []
     lines = output.splitlines()
@@ -146,44 +152,44 @@
 def PrintPidStackInfo(pid):
     command_pattern = STACK_INFO_COMMAND.get(os_name, False)
     if command_pattern:
-        p = subprocess.Popen(
-            command_pattern % pid,
-            stdout=subprocess.PIPE,
-            stderr=subprocess.PIPE,
-            shell=True)
+        p = subprocess.Popen(command_pattern % pid,
+                             stdout=subprocess.PIPE,
+                             stderr=subprocess.PIPE,
+                             shell=True,
+                             universal_newlines=True)
         stdout, stderr = p.communicate()
         stdout = stdout.splitlines()
         stderr = stderr.splitlines()
 
-        print "  Stack:"
+        print("  Stack:")
         for line in stdout:
-            print "    %s" % line
+            print("    %s" % line)
         if stderr:
-            print "  Stack (stderr):"
+            print("  Stack (stderr):")
             for line in stderr:
-                print "    %s" % line
+                print("    %s" % line)
 
 
 def PrintPidInfo(pid, dump_stacks):
     # We assume that the list command will return lines in the format:
     # EXECUTABLE_PATH ARGS
     # There may be blank strings in the output
-    p = subprocess.Popen(
-        INFO_COMMAND[os_name] % pid,
-        stdout=subprocess.PIPE,
-        stderr=subprocess.PIPE,
-        shell=True)
+    p = subprocess.Popen(INFO_COMMAND[os_name] % pid,
+                         stdout=subprocess.PIPE,
+                         stderr=subprocess.PIPE,
+                         shell=True,
+                         universal_newlines=True)
     output, stderr = p.communicate()
     lines = output.splitlines()
 
     # Pop the header
     lines.pop(0)
 
-    print "Hanging process info:"
-    print "  PID: %s" % pid
+    print("Hanging process info:")
+    print("  PID: %s" % pid)
     for line in lines:
         # wmic will output a bunch of empty strings, we ignore these
-        if line: print "  Command line: %s" % line
+        if line: print("  Command line: %s" % line)
 
     if dump_stacks:
         PrintPidStackInfo(pid)
@@ -200,8 +206,11 @@
 def KillWindows(pid):
     # os.kill is not available until python 2.7
     cmd = "taskkill /F /PID %s" % pid
-    p = subprocess.Popen(
-        cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
+    p = subprocess.Popen(cmd,
+                         stdout=subprocess.PIPE,
+                         stderr=subprocess.PIPE,
+                         shell=True,
+                         universal_newlines=True)
     p.communicate()
 
 
diff --git a/tools/test.py b/tools/test.py
index b106500..3e676fc 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -1,8 +1,9 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
 
+from contextlib import ExitStack
 import os
 import string
 import subprocess
@@ -35,7 +36,9 @@
                                          android_platform_tools)
 
     with utils.FileDescriptorLimitIncreaser():
-        with utils.CoreDumpArchiver(args):
+        with ExitStack() as stack:
+            for ctx in utils.CoreDumpArchiver(args):
+                stack.enter_context(ctx)
             exit_code = subprocess.call(command)
 
     if cleanup_dart:
diff --git a/tools/utils.py b/tools/utils.py
index dcb68a5..9a0c720 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -192,82 +192,6 @@
     return 2
 
 
-def GetWindowsRegistryKeyName(name):
-    import win32process
-    # Check if python process is 64-bit or if it's 32-bit running in 64-bit OS.
-    # We need to know whether host is 64-bit so that we are looking in right
-    # registry for Visual Studio path.
-    if sys.maxsize > 2**32 or win32process.IsWow64Process():
-        wow6432Node = 'Wow6432Node\\'
-    else:
-        wow6432Node = ''
-    return r'SOFTWARE\{}{}'.format(wow6432Node, name)
-
-
-# Try to guess Visual Studio location when buiding on Windows.
-def GuessVisualStudioPath():
-    defaultPath = r'C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7' \
-                  r'\IDE'
-    defaultExecutable = 'devenv.com'
-
-    if not IsWindows():
-        return (defaultPath, defaultExecutable)
-
-    keyNamesAndExecutables = [
-        # Pair for non-Express editions.
-        (GetWindowsRegistryKeyName(r'Microsoft\VisualStudio'), 'devenv.com'),
-        # Pair for 2012 Express edition.
-        (GetWindowsRegistryKeyName(r'Microsoft\VSWinExpress'),
-         'VSWinExpress.exe'),
-        # Pair for pre-2012 Express editions.
-        (GetWindowsRegistryKeyName(r'Microsoft\VCExpress'), 'VCExpress.exe')
-    ]
-
-    bestGuess = (0.0, (defaultPath, defaultExecutable))
-
-    import _winreg
-    for (keyName, executable) in keyNamesAndExecutables:
-        try:
-            key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, keyName)
-        except WindowsError:
-            # Can't find this key - moving on the next one.
-            continue
-
-        try:
-            subkeyCounter = 0
-            while True:
-                try:
-                    subkeyName = _winreg.EnumKey(key, subkeyCounter)
-                    subkeyCounter += 1
-                except WindowsError:
-                    # Reached end of enumeration. Moving on the next key.
-                    break
-
-                match = re.match(r'^\d+\.\d+$', subkeyName)
-                if match:
-                    with _winreg.OpenKey(key, subkeyName) as subkey:
-                        try:
-                            (installDir, registrytype) = _winreg.QueryValueEx(
-                                subkey, 'InstallDir')
-                        except WindowsError:
-                            # Can't find value under the key - continue to the next key.
-                            continue
-                        isExpress = executable != 'devenv.com'
-                        if not isExpress and subkeyName == '14.0':
-                            # Stop search since if we found non-Express VS2015 version
-                            # installed, which is preferred version.
-                            return installDir, executable
-
-                        version = float(subkeyName)
-                        # We prefer higher version of Visual Studio and given equal
-                        # version numbers we prefer non-Express edition.
-                        if version > bestGuess[0]:
-                            bestGuess = (version, (installDir, executable))
-        finally:
-            _winreg.CloseKey(key)
-    return bestGuess[1]
-
-
 # Returns true if we're running under Windows.
 def IsWindows():
     return GuessOS() == 'win32'
@@ -612,22 +536,6 @@
     return None
 
 
-def Daemonize():
-    """
-    Create a detached background process (daemon). Returns True for
-    the daemon, False for the parent process.
-    See: http://www.faqs.org/faqs/unix-faq/programmer/faq/
-    "1.7 How do I get my program to act like a daemon?"
-    """
-    if os.fork() > 0:
-        return False
-    os.setsid()
-    if os.fork() > 0:
-        exit(0)
-        # TODO: What is this supposed to do? Didn't we already exit?
-        raise
-    return True
-
 
 def CheckedUnlink(name):
     """Unlink a file without throwing an exception."""
@@ -666,11 +574,6 @@
                 ' '.join(command), exit_code, exit_code & 0xffffffff))
 
 
-def Touch(name):
-    with file(name, 'a'):
-        os.utime(name, None)
-
-
 def ExecuteCommand(cmd):
     """Execute a command in a subprocess."""
     print('Executing: ' + ' '.join(cmd))
@@ -1206,21 +1109,19 @@
         (arg[len(prefix):] for arg in args if arg.startswith(prefix)), None)
 
     if not enabled:
-        return NooptContextManager()
+        return (NooptContextManager(),)
 
     osname = GuessOS()
     if osname == 'linux':
-        return contextlib.nested(LinuxCoreDumpEnabler(),
-                                 LinuxCoreDumpArchiver(output_directory))
+        return (LinuxCoreDumpEnabler(), LinuxCoreDumpArchiver(output_directory))
     elif osname == 'macos':
-        return contextlib.nested(PosixCoreDumpEnabler(),
-                                 MacOSCoreDumpArchiver(output_directory))
+        return (PosixCoreDumpEnabler(), MacOSCoreDumpArchiver(output_directory))
     elif osname == 'win32':
-        return contextlib.nested(WindowsCoreDumpEnabler(),
-                                 WindowsCoreDumpArchiver(output_directory))
+        return (WindowsCoreDumpEnabler(),
+                WindowsCoreDumpArchiver(output_directory))
 
     # We don't have support for MacOS yet.
-    return NooptContextManager()
+    return (NooptContextManager(),)
 
 
 def FileDescriptorLimitIncreaser():
@@ -1238,7 +1139,6 @@
     print('GuessArchitecture() -> ', GuessArchitecture())
     print('GuessCpus() -> ', GuessCpus())
     print('IsWindows() -> ', IsWindows())
-    print('GuessVisualStudioPath() -> ', GuessVisualStudioPath())
     print('GetGitRevision() -> ', GetGitRevision())
     print('GetGitTimestamp() -> ', GetGitTimestamp())
     print('GetVersionFileContent() -> ', GetVersionFileContent())
diff --git a/tools/write_dartdoc_options_file.py b/tools/write_dartdoc_options_file.py
index b1d23f3..7e6567a 100755
--- a/tools/write_dartdoc_options_file.py
+++ b/tools/write_dartdoc_options_file.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2019, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
diff --git a/tools/write_revision_file.py b/tools/write_revision_file.py
index db85841..3b9dd7d 100755
--- a/tools/write_revision_file.py
+++ b/tools/write_revision_file.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2017, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
diff --git a/tools/write_version_file.py b/tools/write_version_file.py
index 75cbc09..aea586c 100755
--- a/tools/write_version_file.py
+++ b/tools/write_version_file.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2017, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
diff --git a/tools/yaml2json.py b/tools/yaml2json.py
index f9f3519..251f657 100755
--- a/tools/yaml2json.py
+++ b/tools/yaml2json.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2017, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
diff --git a/utils/compiler/create_snapshot_entry.dart b/utils/compiler/create_snapshot_entry.dart
index 5cf11a8..360debe 100644
--- a/utils/compiler/create_snapshot_entry.dart
+++ b/utils/compiler/create_snapshot_entry.dart
@@ -11,7 +11,7 @@
 
 Future<String> getVersion(var rootPath) {
   var printVersionScript = rootPath.resolve("tools/make_version.py");
-  return Process.run("python", [printVersionScript.toFilePath(), "--quiet"],
+  return Process.run("python3", [printVersionScript.toFilePath(), "--quiet"],
           runInShell: true)
       .then((result) {
     if (result.exitCode != 0) {