Version 2.18.0-41.0.dev

Merge commit '6cea65bab7bba76b492b81a4372a5b6a4b07ea99' into 'dev'
diff --git a/DEPS b/DEPS
index f68d086..db75ce7 100644
--- a/DEPS
+++ b/DEPS
@@ -54,7 +54,7 @@
 
   # Checkout Android dependencies only on Mac and Linux.
   "download_android_deps":
-    "(host_os == mac or host_os == linux) and host_cpu == x64",
+    "host_os == mac or (host_os == linux and host_cpu == x64)",
 
   # Checkout extra javascript engines for testing or benchmarking.
   # d8, the V8 shell, is always checked out.
@@ -144,7 +144,7 @@
   "root_certificates_rev": "692f6d6488af68e0121317a9c2c9eb393eb0ee50",
   "rust_revision": "b7856f695d65a8ebc846754f97d15814bcb1c244",
   "shelf_packages_handler_rev": "78302e67c035047e6348e692b0c1182131f0fe35",
-  "shelf_proxy_rev": "ccd311f64d8689e7a145d703ba267975d6df9e28",
+  "shelf_proxy_rev": "124615d0614b38814970aa9638725d9d6b435268",
   "shelf_rev": "78ac724a7944700340a3cef28c84bccbe62e9367",
   "shelf_static_rev": "202ec1a53c9a830c17cf3b718d089cf7eba568ad",
   "shelf_web_socket_rev": "24fb8a04befa75a94ac63a27047b231d1a22aab4",
@@ -531,7 +531,7 @@
   Var("dart_root") + "/third_party/android_tools/ndk": {
       "packages": [
           {
-            "package": "flutter/android/ndk/${{platform}}",
+            "package": "flutter/android/ndk/${{os}}-amd64",
             "version": "version:r21.0.6113669"
           }
       ],
@@ -542,7 +542,7 @@
   Var("dart_root") + "/third_party/android_tools/sdk/build-tools": {
       "packages": [
           {
-            "package": "flutter/android/sdk/build-tools/${{platform}}",
+            "package": "flutter/android/sdk/build-tools/${{os}}-amd64",
             "version": "version:30.0.1"
           }
       ],
@@ -553,7 +553,7 @@
   Var("dart_root") + "/third_party/android_tools/sdk/platform-tools": {
      "packages": [
           {
-            "package": "flutter/android/sdk/platform-tools/${{platform}}",
+            "package": "flutter/android/sdk/platform-tools/${{os}}-amd64",
             "version": "version:29.0.2"
           }
       ],
@@ -575,7 +575,7 @@
   Var("dart_root") + "/third_party/android_tools/sdk/tools": {
       "packages": [
           {
-            "package": "flutter/android/sdk/tools/${{platform}}",
+            "package": "flutter/android/sdk/tools/${{os}}-amd64",
             "version": "version:26.1.1"
           }
       ],
diff --git a/benchmarks/Startup/dart/Startup.dart b/benchmarks/Startup/dart/Startup.dart
new file mode 100644
index 0000000..49889fb
--- /dev/null
+++ b/benchmarks/Startup/dart/Startup.dart
@@ -0,0 +1,85 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:compiler/src/dart2js.dart' as dart2js;
+
+Future<void> main(List<String> args) async {
+  if (args.contains('--child')) {
+    return;
+  }
+
+  // Include dart2js and prevent tree-shaking to make this program have a
+  // non-trival snapshot size.
+  if (args.contains('--train')) {
+    args.remove('--train');
+    return dart2js.main(args);
+  }
+
+  var tempDir;
+  var events;
+  try {
+    tempDir = await Directory.systemTemp.createTemp();
+    final timelinePath =
+        tempDir.uri.resolve('Startup-timeline.json').toFilePath();
+    final p = await Process.run(Platform.executable, [
+      ...Platform.executableArguments,
+      '--timeline_recorder=file:$timelinePath',
+      '--timeline_streams=VM,Isolate,Embedder',
+      Platform.script.toFilePath(),
+      '--child'
+    ]);
+    if (p.exitCode != 0) {
+      print(p.stdout);
+      print(p.stderr);
+      throw 'Child process failed: ${p.exitCode}';
+    }
+
+    events = jsonDecode(await File(timelinePath).readAsString());
+  } finally {
+    await tempDir.delete(recursive: true);
+  }
+
+  var mainIsolateId;
+  for (final event in events) {
+    if (event['name'] == 'InitializeIsolate' &&
+        event['args']['isolateName'] == 'main') {
+      mainIsolateId = event['args']['isolateId'];
+    }
+  }
+  if (mainIsolateId == null) {
+    throw 'Could not determine main isolate';
+  }
+
+  void report(String name, String isolateId) {
+    var filtered = events.where((event) => event['name'] == name);
+    if (isolateId != null) {
+      filtered =
+          filtered.where((event) => event['args']['isolateId'] == isolateId);
+    }
+    var micros;
+    final durations = filtered.where((event) => event['ph'] == 'X');
+    final begins = filtered.where((event) => event['ph'] == 'B');
+    final ends = filtered.where((event) => event['ph'] == 'E');
+    if (durations.length == 1 && begins.length == 0 && ends.length == 0) {
+      micros = durations.single['dur'];
+    } else if (durations.length == 0 &&
+        begins.length == 1 &&
+        ends.length == 1) {
+      micros = ends.single['ts'] - begins.single['ts'];
+    } else {
+      print(durations.toList());
+      print(begins.toList());
+      print(ends.toList());
+      throw '$name is missing or ambiguous';
+    }
+    print('Startup.$name(RunTime): $micros us.');
+  }
+
+  report('CreateIsolateGroupAndSetupHelper', null);
+  report('InitializeIsolate', mainIsolateId);
+  report('ReadProgramSnapshot', mainIsolateId);
+}
diff --git a/benchmarks/Startup/dart2/Startup.dart b/benchmarks/Startup/dart2/Startup.dart
new file mode 100644
index 0000000..26fb07b
--- /dev/null
+++ b/benchmarks/Startup/dart2/Startup.dart
@@ -0,0 +1,87 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart=2.9
+
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:compiler/src/dart2js.dart' as dart2js;
+
+Future<void> main(List<String> args) async {
+  if (args.contains('--child')) {
+    return;
+  }
+
+  // Include dart2js and prevent tree-shaking to make this program have a
+  // non-trival snapshot size.
+  if (args.contains('--train')) {
+    args.remove('--train');
+    return dart2js.main(args);
+  }
+
+  var tempDir;
+  var events;
+  try {
+    tempDir = await Directory.systemTemp.createTemp();
+    final timelinePath =
+        tempDir.uri.resolve('Startup-timeline.json').toFilePath();
+    final p = await Process.run(Platform.executable, [
+      ...Platform.executableArguments,
+      '--timeline_recorder=file:$timelinePath',
+      '--timeline_streams=VM,Isolate,Embedder',
+      Platform.script.toFilePath(),
+      '--child'
+    ]);
+    if (p.exitCode != 0) {
+      print(p.stdout);
+      print(p.stderr);
+      throw 'Child process failed: ${p.exitCode}';
+    }
+
+    events = jsonDecode(await File(timelinePath).readAsString());
+  } finally {
+    await tempDir.delete(recursive: true);
+  }
+
+  var mainIsolateId;
+  for (final event in events) {
+    if (event['name'] == 'InitializeIsolate' &&
+        event['args']['isolateName'] == 'main') {
+      mainIsolateId = event['args']['isolateId'];
+    }
+  }
+  if (mainIsolateId == null) {
+    throw 'Could not determine main isolate';
+  }
+
+  void report(String name, String isolateId) {
+    var filtered = events.where((event) => event['name'] == name);
+    if (isolateId != null) {
+      filtered =
+          filtered.where((event) => event['args']['isolateId'] == isolateId);
+    }
+    var micros;
+    final durations = filtered.where((event) => event['ph'] == 'X');
+    final begins = filtered.where((event) => event['ph'] == 'B');
+    final ends = filtered.where((event) => event['ph'] == 'E');
+    if (durations.length == 1 && begins.length == 0 && ends.length == 0) {
+      micros = durations.single['dur'];
+    } else if (durations.length == 0 &&
+        begins.length == 1 &&
+        ends.length == 1) {
+      micros = ends.single['ts'] - begins.single['ts'];
+    } else {
+      print(durations.toList());
+      print(begins.toList());
+      print(ends.toList());
+      throw '$name is missing or ambiguous';
+    }
+    print('Startup.$name(RunTime): $micros us.');
+  }
+
+  report('CreateIsolateGroupAndSetupHelper', null);
+  report('InitializeIsolate', mainIsolateId);
+  report('ReadProgramSnapshot', mainIsolateId);
+}
diff --git a/build/config/BUILDCONFIG.gn b/build/config/BUILDCONFIG.gn
index 3889bb4..afb3a74 100644
--- a/build/config/BUILDCONFIG.gn
+++ b/build/config/BUILDCONFIG.gn
@@ -369,13 +369,13 @@
 # default toolchain.
 
 if (is_win) {
-  # On windows we use the same toolchain for host and target by default.
   if (is_clang) {
-    host_toolchain = "//build/toolchain/win:clang_$current_cpu"
+    host_toolchain = "//build/toolchain/win:clang_$host_cpu"
+    set_default_toolchain("//build/toolchain/win:clang_$current_cpu")
   } else {
-    host_toolchain = "//build/toolchain/win:$current_cpu"
+    host_toolchain = "//build/toolchain/win:$host_cpu"
+    set_default_toolchain("//build/toolchain/win:$current_cpu")
   }
-  set_default_toolchain("$host_toolchain")
 } else if (is_android) {
   if (host_os == "linux") {
     if (host_cpu == "x86" || host_cpu == "x64") {
diff --git a/build/config/android/config.gni b/build/config/android/config.gni
index c61d41a..f5def03 100644
--- a/build/config/android/config.gni
+++ b/build/config/android/config.gni
@@ -26,6 +26,9 @@
   # architecture, which is different than the names GN uses.
   if (host_cpu == "x64" || host_cpu == "x86") {
     android_host_arch = "x86_64"
+  } else if (host_cpu == "arm64") {
+    # Run existing Android toolchain via Rosetta.
+    android_host_arch = "x86_64"
   } else {
     assert(false, "Need Android toolchain support for your build CPU arch.")
   }
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 6cfcf26..f2984fb 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -67,6 +67,18 @@
       "/GS",  # Enable buffer security checking.
       "/FS",  # Preserve previous PDB behavior.
     ]
+
+    if (is_clang) {
+      if (current_cpu == "x86") {
+        cflags += [ "-m32" ]
+      } else if (current_cpu == "x64") {
+        cflags += [ "-m64" ]
+      } else if (current_cpu == "arm64") {
+        cflags += [ "--target=arm64-windows" ]
+      } else {
+        assert(false, "Unknown current_cpu: $current_cpu")
+      }
+    }
   } else {
     # Common GCC compiler flags setup.
     # --------------------------------
diff --git a/build/config/win/BUILD.gn b/build/config/win/BUILD.gn
index daf26ed..98c2e23 100644
--- a/build/config/win/BUILD.gn
+++ b/build/config/win/BUILD.gn
@@ -34,14 +34,7 @@
 
 # Linker flags for Windows SDK setup, this is applied only to EXEs and DLLs.
 config("sdk_link") {
-  if (current_cpu == "x64") {
-    ldflags = [ "/MACHINE:X64" ]
-    lib_dirs = [
-      "$windows_sdk_path\Lib\winv6.3\um\x64",
-      "$visual_studio_path\VC\lib\amd64",
-      "$visual_studio_path\VC\atlmfc\lib\amd64",
-    ]
-  } else {
+  if (current_cpu == "x86") {
     ldflags = [
       "/MACHINE:X86",
       "/SAFESEH",  # Not compatible with x64 so use only for x86.
@@ -54,6 +47,29 @@
     if (!is_asan) {
       ldflags += [ "/largeaddressaware" ]
     }
+  } else if (current_cpu == "x64") {
+    ldflags = [ "/MACHINE:X64" ]
+    lib_dirs = [
+      "$windows_sdk_path\Lib\winv6.3\um\x64",
+      "$visual_studio_path\VC\lib\amd64",
+      "$visual_studio_path\VC\atlmfc\lib\amd64",
+    ]
+  } else if (current_cpu == "arm") {
+    ldflags = [ "/MACHINE:ARM" ]
+    lib_dirs = [
+      "$windows_sdk_path\Lib\winv6.3\um\arm",
+      "$visual_studio_path\VC\lib\arm",
+      "$visual_studio_path\VC\atlmfc\lib\arm",
+    ]
+  } else if (current_cpu == "arm64") {
+    ldflags = [ "/MACHINE:ARM64" ]
+    lib_dirs = [
+      "$windows_sdk_path\Lib\winv6.3\um\arm64",
+      "$visual_studio_path\VC\lib\arm64",
+      "$visual_studio_path\VC\atlmfc\lib\arm64",
+    ]
+  } else {
+    assert(false, "Unknown current_cpu: $current_cpu")
   }
 }
 
@@ -67,6 +83,7 @@
     "/ignore:4221",
     "/ignore:4197",  # Disable multiple Dart_True export warning.
     "/NXCOMPAT",
+    "/DYNAMICBASE",
 
     # Suggested by Microsoft Devrel to avoid
     #   LINK : fatal error LNK1248: image size (80000000)
@@ -74,30 +91,25 @@
     # which started happening more regularly after VS2013 Update 4.
     "/maxilksize:2147483647",
   ]
-
-  # ASLR makes debugging with windbg difficult because Chrome.exe and
-  # Chrome.dll share the same base name. As result, windbg will name the
-  # Chrome.dll module like chrome_<base address>, where <base address>
-  # typically changes with each launch. This in turn means that breakpoints in
-  # Chrome.dll don't stick from one launch to the next. For this reason, we
-  # turn ASLR off in debug builds.
-  if (is_debug) {
-    ldflags += [ "/DYNAMICBASE:NO" ]
-  } else {
-    ldflags += [ "/DYNAMICBASE" ]
-  }
 }
 
 # Subsystem -------------------------------------------------------------------
 
 # This is appended to the subsystem to specify a minimum version.
-if (current_cpu == "x64") {
+if (current_cpu == "x86") {
+  # 5.01 = Windows XP.
+  subsystem_version_suffix = ",5.01"
+} else if (current_cpu == "x64") {
   # The number after the comma is the minimum required OS version.
   # 5.02 = Windows Server 2003.
   subsystem_version_suffix = ",5.02"
+} else if (current_cpu == "arm") {
+  subsystem_version_suffix = ",6.02"
+} else if (current_cpu == "arm64") {
+  # Windows ARM64 requires Windows 10.
+  subsystem_version_suffix = ",10.0"
 } else {
-  # 5.01 = Windows XP.
-  subsystem_version_suffix = ",5.01"
+  assert(false, "Unknown current_cpu: $current_cpu")
 }
 
 config("console") {
diff --git a/build/toolchain/win/BUILD.gn b/build/toolchain/win/BUILD.gn
index f7b2587..d70d49e 100644
--- a/build/toolchain/win/BUILD.gn
+++ b/build/toolchain/win/BUILD.gn
@@ -3,12 +3,8 @@
 # found in the LICENSE file.
 
 declare_args() {
-  # Path to the directory containing the VC binaries for the right
-  # combination of host and target architectures. Currently only the
-  # 64-bit host toolchain is supported, with either 32-bit or 64-bit targets.
-  # If vc_bin_dir is not specified on the command line (and it normally
-  # isn't), we will dynamically determine the right value to use at runtime.
-  vc_bin_dir = ""
+  # Path to the clang toolchain.
+  clang_base_path = "//buildtools/win-x64/clang"
 }
 
 import("//build/config/win/visual_studio_version.gni")
@@ -17,53 +13,55 @@
 # Should only be running on Windows.
 assert(is_win)
 
+# Setup the Visual Studio state.
+#
+# Its arguments are the VS path and the compiler wrapper tool. It will write
+# "environment.x86" and "environment.x64" to the build directory and return a
+# list to us.
+
 # This tool will is used as a wrapper for various commands below.
 tool_wrapper_path = rebase_path("tool_wrapper.py", root_build_dir)
 
-# Setup the Visual Studio state.
-toolchain_data = exec_script("setup_toolchain.py",
-                             [
-                               visual_studio_path,
-                               windows_sdk_path,
-                               visual_studio_runtime_dirs,
-                               "win",
-                               current_cpu,
-                               "environment." + current_cpu,
-                             ],
-                             "scope")
-
-if (vc_bin_dir == "") {
-  vc_bin_dir = toolchain_data.vc_bin_dir
-}
-
 if (use_goma) {
   goma_prefix = "$goma_dir/gomacc.exe "
 } else {
   goma_prefix = ""
 }
 
-# Parameters:
-#  current_cpu: current_cpu to pass as a build arg
-#  environment: File name of environment file.
-template("msvc_toolchain") {
-  env = invoker.environment
-
+if (current_toolchain == default_toolchain) {
   if (is_debug) {
     configuration = "Debug"
   } else {
     configuration = "Release"
   }
-  exec_script("../../vs_toolchain.py",
+  exec_script("//build/vs_toolchain.py",
               [
                 "copy_dlls",
                 rebase_path(root_build_dir),
                 configuration,
-                invoker.current_cpu,
+                target_cpu,
               ])
+}
 
-  cl = invoker.cl
-
+# Parameters:
+#  toolchain_args: Settings for the toolchain, including at least:
+#     current_cpu: current_cpu to pass as a build arg
+#  environment: File name of environment file.
+template("msvc_toolchain") {
   toolchain(target_name) {
+    # When invoking this toolchain not as the default one, these args will be
+    # passed to the build. They are ignored when this is the default toolchain.
+    assert(defined(invoker.toolchain_args))
+    toolchain_args = {
+      if (defined(invoker.toolchain_args)) {
+        forward_variables_from(invoker.toolchain_args, "*")
+      }
+    }
+
+    env = invoker.environment
+
+    cl = invoker.cl
+
     # Make these apply to all tools below.
     lib_switch = ""
     lib_dir_switch = "/LIBPATH:"
@@ -74,11 +72,7 @@
       # TODO(brettw) enable this when GN support in the binary has been rolled.
       #precompiled_header_type = "msvc"
       pdbname = "{{target_out_dir}}/{{target_output_name}}_c.pdb"
-      flags = ""
-      if (invoker.current_cpu == "x86") {
-        flags = "-m32"
-      }
-      command = "ninja -t msvc -e $env -- $cl $flags /nologo /showIncludes /FC @$rspfile /c {{source}} /Fo{{output}} /Fd$pdbname"
+      command = "ninja -t msvc -e $env -- $cl /nologo /showIncludes /FC @$rspfile /c {{source}} /Fo{{output}} /Fd$pdbname"
       depsformat = "msvc"
       description = "CC {{output}}"
       outputs = [
@@ -96,7 +90,7 @@
       # The PDB name needs to be different between C and C++ compiled files.
       pdbname = "{{target_out_dir}}/{{target_output_name}}_cc.pdb"
       flags = ""
-      if (invoker.current_cpu == "x86") {
+      if (is_clang && invoker.current_cpu == "x86") {
         flags = "-m32"
       }
       command = "ninja -t msvc -e $env -- $cl $flags /nologo /showIncludes /FC @$rspfile /c {{source}} /Fo{{output}} /Fd$pdbname"
@@ -109,7 +103,8 @@
     }
 
     tool("rc") {
-      command = "$python_path $tool_wrapper_path rc-wrapper $env rc.exe {{defines}} {{include_dirs}} /fo{{output}} {{source}}"
+      command = "$python_path $tool_wrapper_path rc-wrapper $env rc.exe /nologo {{defines}} {{include_dirs}} /fo{{output}} {{source}}"
+      depsformat = "msvc"
       outputs = [
         "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.res",
       ]
@@ -117,14 +112,17 @@
     }
 
     tool("asm") {
-      if (invoker.current_cpu == "x64") {
-        ml = "ml64.exe"
-        x64 = "-D_ML64_X64"
+      if (toolchain_args.current_cpu == "x86") {
+        command = "$python_path $tool_wrapper_path asm-wrapper $env ml.exe {{defines}} {{include_dirs}} {{asmflags}} /c /Fo {{output}} {{source}}"
+      } else if (toolchain_args.current_cpu == "x64") {
+        command = "$python_path $tool_wrapper_path asm-wrapper $env ml64.exe -D_ML64_X64 {{defines}} {{include_dirs}} {{asmflags}} /c /Fo {{output}} {{source}}"
+      } else if (toolchain_args.current_cpu == "arm") {
+        command = "$python_path $tool_wrapper_path asm-wrapper $env armasm.exe {{include_dirs}} {{asmflags}} -o {{output}} {{source}}"
+      } else if (toolchain_args.current_cpu == "arm64") {
+        command = "$python_path $tool_wrapper_path asm-wrapper $env armasm64.exe {{include_dirs}} {{asmflags}} -o {{output}} {{source}}"
       } else {
-        ml = "ml.exe"
-        x64 = ""
+        assert(false, "Unknown current_cpu: ${toolchain_args.current_cpu}")
       }
-      command = "$python_path $tool_wrapper_path asm-wrapper $env $ml $x64 {{defines}} {{include_dirs}} {{asmflags}} /c /Fo {{output}} {{source}}"
       description = "ASM {{output}}"
       outputs = [
         "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.obj",
@@ -148,22 +146,17 @@
     }
 
     tool("solink") {
-      dllname = "{{root_out_dir}}/{{target_output_name}}{{output_extension}}"  # e.g.
-                                                                               # foo.dll
-      libname = "{{root_out_dir}}/{{target_output_name}}{{output_extension}}.lib"  # e.g.
-                                                                                   # foo.dll.lib
+      dllname = "{{root_out_dir}}/{{target_output_name}}{{output_extension}}"  # e.g. foo.dll
+      libname =
+          "{{root_out_dir}}/{{target_output_name}}{{output_extension}}.lib"  # e.g. foo.dll.lib
       rspfile = "${dllname}.rsp"
 
       link_command = "$python_path $tool_wrapper_path link-wrapper $env False link.exe /nologo /IMPLIB:$libname /DLL /OUT:$dllname /PDB:${dllname}.pdb @$rspfile"
 
       # TODO(brettw) support manifests
-      #manifest_command = "$python_path gyp-win-tool manifest-wrapper $env mt.exe -nologo -manifest $manifests -out:${dllname}.manifest"
+      #manifest_command = "$python_path $tool_wrapper_path manifest-wrapper $env mt.exe -nologo -manifest $manifests -out:${dllname}.manifest"
       #command = "cmd /c $link_command && $manifest_command"
-      # Force rebuild of the .lib-file(if it is being produced) because msvc
-      # linker seems to keep it untouched sometimes even when .obj-files are
-      # being updated.
-      cleanup_lib_command = "$python_path $tool_wrapper_path delete-file $libname"
-      command = "cmd /c $cleanup_lib_command && cmd /c $link_command"
+      command = link_command
 
       default_output_extension = ".dll"
       description = "LINK(DLL) {{output}}"
@@ -177,12 +170,12 @@
       # The use of inputs_newline is to work around a fixed per-line buffer
       # size in the linker.
       rspfile_content = "{{libs}} {{solibs}} {{inputs_newline}} {{ldflags}}"
+
+      restat = true
     }
 
     tool("solink_module") {
-      dllname =
-          "{{output_dir}}/{{target_output_name}}{{output_extension}}"  # e.g.
-                                                                       # foo.dll
+      dllname = "{{output_dir}}/{{target_output_name}}{{output_extension}}"  # e.g. foo.dll
       pdbname = "${dllname}.pdb"
       rspfile = "${dllname}.rsp"
 
@@ -207,11 +200,12 @@
       binary_output =
           "{{root_out_dir}}/{{target_output_name}}{{output_extension}}"
       rspfile = "$binary_output.rsp"
+      pdbfile = "$binary_output.pdb"
 
-      link_command = "$python_path $tool_wrapper_path link-wrapper $env False link.exe /nologo /OUT:$binary_output /PDB:$binary_output.pdb @$rspfile"
+      link_command = "$python_path $tool_wrapper_path link-wrapper $env False link.exe /nologo /OUT:$binary_output /PDB:$pdbfile @$rspfile"
 
       # TODO(brettw) support manifests
-      #manifest_command = "$python_path gyp-win-tool manifest-wrapper $env mt.exe -nologo -manifest $manifests -out:{{output}}.manifest"
+      #manifest_command = "$python_path $tool_wrapper_path manifest-wrapper $env mt.exe -nologo -manifest $manifests -out:{{output}}.manifest"
       #command = "cmd /c $link_command && $manifest_command"
       command = link_command
 
@@ -220,6 +214,7 @@
       outputs = [
         binary_output,
         "{{root_out_dir}}/{{target_output_name}}.lib",
+        pdbfile,
       ]
 
       # The use of inputs_newline is to work around a fixed per-line buffer
@@ -230,58 +225,61 @@
     }
 
     tool("stamp") {
-      command = "$python_path $tool_wrapper_path stamp {{output}}"
+      command = "cmd /c type nul > \"{{output}}\""
       description = "STAMP {{output}}"
     }
 
     tool("copy") {
-      command = "$python_path $tool_wrapper_path recursive-mirror {{source}} {{output}}"
+      command =
+          "$python_path $tool_wrapper_path recursive-mirror {{source}} {{output}}"
       description = "COPY {{source}} {{output}}"
     }
+  }
+}
 
-    # When invoking this toolchain not as the default one, these args will be
-    # passed to the build. They are ignored when this is the default toolchain.
+template("win_toolchains") {
+  assert(defined(invoker.toolchain_arch))
+  toolchain_arch = invoker.toolchain_arch
+
+  win_toolchain_data = exec_script("setup_toolchain.py",
+                                   [
+                                     visual_studio_path,
+                                     windows_sdk_path,
+                                     visual_studio_runtime_dirs,
+                                     toolchain_arch,
+                                   ],
+                                   "scope")
+
+  msvc_toolchain(target_name) {
+    environment = "environment." + toolchain_arch
+    cl = "${goma_prefix}\"${win_toolchain_data.vc_bin_dir}/cl.exe\""
     toolchain_args = {
-      current_cpu = invoker.current_cpu
-      if (defined(invoker.is_clang)) {
-        is_clang = invoker.is_clang
+      if (defined(invoker.toolchain_args)) {
+        forward_variables_from(invoker.toolchain_args, "*")
       }
+      current_cpu = toolchain_arch
+      is_clang = false
+    }
+  }
+  msvc_toolchain("clang_" + target_name) {
+    environment = "environment." + toolchain_arch
+    prefix = rebase_path("$clang_base_path/bin", root_build_dir)
+    cl = "${goma_prefix}$prefix/clang-cl.exe"
+    toolchain_args = {
+      if (defined(invoker.toolchain_args)) {
+        forward_variables_from(invoker.toolchain_args, "*")
+      }
+      current_cpu = toolchain_arch
+      is_clang = true
     }
   }
 }
 
-# TODO(dpranke): Declare both toolchains all of the time when we
-# get it sorted out how we want to support them both in a single build.
-# Right now only one of these can be enabled at a time because the
-# runtime libraries get copied to root_build_dir and would collide.
-if (current_cpu == "x86") {
-  msvc_toolchain("x86") {
-    environment = "environment.x86"
-    current_cpu = "x86"
-    cl = "${goma_prefix}\"${vc_bin_dir}/cl.exe\""
-    is_clang = false
-  }
-  msvc_toolchain("clang_x86") {
-    environment = "environment.x86"
-    current_cpu = "x86"
-    prefix = rebase_path("//buildtools/win-x64/clang/bin", root_build_dir)
-    cl = "${goma_prefix}$prefix/clang-cl.exe"
-    is_clang = true
-  }
+win_toolchains(target_cpu) {
+  toolchain_arch = target_cpu
 }
-
-if (current_cpu == "x64") {
-  msvc_toolchain("x64") {
-    environment = "environment.x64"
-    current_cpu = "x64"
-    cl = "${goma_prefix}\"${vc_bin_dir}/cl.exe\""
-    is_clang = false
-  }
-  msvc_toolchain("clang_x64") {
-    environment = "environment.x64"
-    current_cpu = "x64"
-    prefix = rebase_path("//buildtools/win-x64/clang/bin", root_build_dir)
-    cl = "${goma_prefix}$prefix/clang-cl.exe"
-    is_clang = true
+if (host_cpu != target_cpu) {
+  win_toolchains(host_cpu) {
+    toolchain_arch = host_cpu
   }
 }
diff --git a/build/toolchain/win/setup_toolchain.py b/build/toolchain/win/setup_toolchain.py
index ee3d8ee..d1d4fb5 100644
--- a/build/toolchain/win/setup_toolchain.py
+++ b/build/toolchain/win/setup_toolchain.py
@@ -10,319 +10,141 @@
 # win tool. The script assumes that the root build directory is the current dir
 # and the files will be written to the current directory.
 
-from __future__ import print_function
-
-import json
+import errno
 import os
 import re
 import subprocess
 import sys
 
-sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir))
-import gn_helpers
-
-SCRIPT_DIR = os.path.dirname(__file__)
 
 def _ExtractImportantEnvironment(output_of_set):
-    """Extracts environment variables required for the toolchain to run from
+  """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
-    # if not caught here.
-    if output_of_set.count('=') == 0:
-        raise Exception('Invalid output_of_set. Value is:\n%s' % output_of_set)
-    for line in output_of_set.splitlines():
-        for envvar in envvars_to_save:
-            if re.match(envvar + '=', line.lower()):
-                var, setting = line.split('=', 1)
-                if envvar == 'path':
-                    # Our own rules and actions in Chromium rely on python being in the
-                    # path. Add the path to this python here so that if it's not in the
-                    # path when ninja is run later, python will still be found.
-                    setting = os.path.dirname(
-                        sys.executable) + os.pathsep + setting
-                env[var.upper()] = setting
-                break
-    if sys.platform in ('win32', 'cygwin'):
-        for required in ('SYSTEMROOT', 'TEMP', 'TMP'):
-            if required not in env:
-                raise Exception('Environment variable "%s" '
-                                'required to be set to valid path' % required)
-    return env
+  envvars_to_save = (
+      'goma_.*', # TODO(scottmg): This is ugly, but needed for goma.
+      'include',
+      'lib',
+      'libpath',
+      'path',
+      'pathext',
+      'systemroot',
+      'temp',
+      'tmp',
+      )
+  env = {}
+  for line in output_of_set.splitlines():
+    for envvar in envvars_to_save:
+      if re.match(envvar + '=', line.decode().lower()):
+        var, setting = line.decode().split('=', 1)
+        if envvar == 'path':
+          # Our own rules (for running gyp-win-tool) and other actions in
+          # Chromium rely on python being in the path. Add the path to this
+          # python here so that if it's not in the path when ninja is run
+          # later, python will still be found.
+          setting = os.path.dirname(sys.executable) + os.pathsep + setting
+        env[var.upper()] = setting
+        break
+  for required in ('SYSTEMROOT', 'TEMP', 'TMP'):
+    if required not in env:
+      raise Exception('Environment variable "%s" '
+                      'required to be set to valid path' % required)
+  return env
 
 
-def _DetectVisualStudioPath():
-    """Return path to the installed Visual Studio.
-  """
-
-    # Use the code in build/vs_toolchain.py to avoid duplicating code.
-    chromium_dir = os.path.abspath(os.path.join(SCRIPT_DIR, '..', '..', '..'))
-    sys.path.append(os.path.join(chromium_dir, 'build'))
-    import vs_toolchain
-    return vs_toolchain.DetectVisualStudioPath()
-
-
-def _LoadEnvFromBat(args):
-    """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)
-    variables, _ = popen.communicate()
-    if popen.returncode != 0:
-        raise Exception('"%s" failed with error %d' % (args, popen.returncode))
-    return variables.decode(errors='ignore')
-
-
-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' 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 = 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(*([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.
-            sep = os.pathsep if k == 'PATH' else ';'
-            env[k] = sep.join(entries)
-        # PATH is a bit of a special case, it's in addition to the current PATH.
-        env['PATH'] = env['PATH'] + os.pathsep + os.environ['PATH']
-        # Augment with the current env to pick up TEMP and friends.
-        for k in os.environ:
-            if k not in env:
-                env[k] = os.environ[k]
-
-        varlines = []
-        for k in sorted(env.keys()):
-            varlines.append('%s=%s' % (str(k), str(env[k])))
-        variables = '\n'.join(varlines)
-
-        # 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'))
-            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()
-        # We only support x64-hosted tools.
-        script_path = os.path.normpath(
-            os.path.join(os.environ['GYP_MSVS_OVERRIDE_PATH'],
-                         'VC/vcvarsall.bat'))
-        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. 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'))
-            if not os.path.exists(other_path):
-                raise Exception(
-                    '%s is missing - make sure VC++ tools are installed.' %
-                    script_path)
-            script_path = other_path
-        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)
+def _SetupScript(target_cpu, sdk_dir):
+  """Returns a command (with arguments) to be used to set up the
+  environment."""
+  # Check if we are running in the SDK command line environment and use
+  # the setup script from the SDK if so.
+  assert target_cpu in ('x86', 'x64', 'arm64')
+  if bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', 1))) and sdk_dir:
+    return [os.path.normpath(os.path.join(sdk_dir, 'Bin/SetEnv.Cmd')),
+            '/' + target_cpu]
+  else:
+    # We only support x64-hosted tools.
+    # TODO(scottmg|dpranke): Non-depot_tools toolchain: need to get Visual
+    # Studio install location from registry.
+    return [os.path.normpath(os.path.join(os.environ['GYP_MSVS_OVERRIDE_PATH'],
+                                          'VC/Auxiliary/Build/vcvarsall.bat')),
+            'amd64_x86' if target_cpu == 'x86' else 'amd64']
 
 
 def _FormatAsEnvironmentBlock(envvar_dict):
-    """Format as an 'environment block' directly suitable for CreateProcess.
+  """Format as an 'environment block' directly suitable for CreateProcess.
   Briefly this is a list of key=value\0, terminated by an additional \0. See
   CreateProcess documentation for more details."""
-    block = ''
-    nul = '\0'
-    for key, value in envvar_dict.items():
-        block += key + '=' + value + nul
-    block += nul
-    return block
+  block = ''
+  nul = '\0'
+  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.
+def _CopyTool(source_path):
+  """Copies the given tool to the current directory, including a warning not
+  to edit it."""
+  with open(source_path) as source_file:
+    tool_source = source_file.readlines()
 
-  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 ''
+  # Add header and write it out to the current directory (which should be the
+  # root build dir).
+  with open("gyp-win-tool", 'w') as tool_file:
+    tool_file.write(''.join([tool_source[0],
+                             '# Generated by setup_toolchain.py do not edit.\n']
+                            + tool_source[1:]))
 
 
 def main():
-    if len(sys.argv) != 7:
-        print('Usage setup_toolchain.py '
-              '<visual studio path> <win sdk path> '
-              '<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]
+  if len(sys.argv) != 5:
+    print('Usage setup_toolchain.py '
+          '<visual studio path> <win sdk path> '
+          '<runtime dirs> <target_cpu>')
+    sys.exit(2)
+  win_sdk_path = sys.argv[2]
+  runtime_dirs = sys.argv[3]
+  target_cpu = sys.argv[4]
 
-    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 = ''
+  cpus = ('x86', 'x64', 'arm64')
+  assert target_cpu in cpus
+  vc_bin_dir = ''
 
-    if (target_os == 'winuwp'):
-        target_store = True
-    else:
-        target_store = False
+  # TODO(scottmg|goma): Do we need an equivalent of
+  # ninja_use_custom_environment_files?
 
-    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 = ''
+  for cpu in cpus:
+    # Extract environment variables for subprocesses.
+    args = _SetupScript(cpu, win_sdk_path)
+    args.extend(('&&', 'set'))
+    popen = subprocess.Popen(
+        args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+    variables, _ = popen.communicate()
+    env = _ExtractImportantEnvironment(variables)
+    env['PATH'] = runtime_dirs + ';' + env['PATH']
 
-    # TODO(scottmg|goma): Do we need an equivalent of
-    # ninja_use_custom_environment_files?
+    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
 
-    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
+    # The Windows SDK include directories must be first. They both have a sal.h,
+    # and the SDK one is newer and the SDK uses some newer features from it not
+    # present in the Visual Studio one.
 
-    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 + '"'
+    if win_sdk_path:
+      additional_includes = ('{sdk_dir}\\Include\\shared;' +
+                             '{sdk_dir}\\Include\\um;' +
+                             '{sdk_dir}\\Include\\winrt;').format(
+                                  sdk_dir=win_sdk_path)
+      env['INCLUDE'] = additional_includes + env['INCLUDE']
+    env_block = _FormatAsEnvironmentBlock(env)
+    with open('environment.' + cpu, 'wb') as f:
+      f.write(env_block.encode())
 
-    for cpu in cpus:
-        if cpu == target_cpu:
-            # 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 = list(map(relflag, include))
-
-            lib = [p.replace('"', r'\"') for p in env['LIB'].split(';') if p]
-            lib = list(map(relflag, lib))
-
-            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])
-
-            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))
-    assert 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))
+  assert vc_bin_dir
+  print('vc_bin_dir = "%s"' % vc_bin_dir)
 
 
 if __name__ == '__main__':
-    main()
+  main()
diff --git a/build/vs_toolchain.py b/build/vs_toolchain.py
index 5ac59ba..6314e5e 100644
--- a/build/vs_toolchain.py
+++ b/build/vs_toolchain.py
@@ -1,17 +1,18 @@
 #!/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.
 
-# For Dart developers:
+# For Dart/Flutter developers:
 # This file keeps the MSVC toolchain up-to-date for Google developers.
 # It is copied from Chromium:
 #   https://cs.chromium.org/chromium/src/build/vs_toolchain.py
-# with modifications that update paths and remove dependencies on gyp.
+# with modifications that update paths, and remove dependencies on gyp.
 # To update to a new MSVC toolchain, copy the updated script from the Chromium
-# tree, edit to make it work in the Dart tree by updating paths in the original script.
+# tree, and edit to make it work in the Dart tree by updating paths in the original script.
 
-from __future__ import print_function
+
 
 import collections
 import glob
@@ -25,49 +26,24 @@
 import subprocess
 import sys
 
+
 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__))
-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')
 
+sys.path.insert(0, os.path.join(script_dir, '..', 'tools'))
+
 # VS versions are listed in descending order of priority (highest first).
 MSVS_VERSIONS = collections.OrderedDict([
-    ('2019', '16.0'),
-    ('2017', '15.0'),
+  ('2017', '15.0'),
+  ('2019', '16.0'),
+  ('2022', '17.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
+  """Sets up os.environ to use the depot_tools VS toolchain with gyp, and
   returns the location of the VC runtime DLLs so they can be copied into
   the output directory after gyp generation.
 
@@ -75,70 +51,73 @@
   generated separately because there are multiple folders for the arm64 VC
   runtime.
   """
-    vs_runtime_dll_dirs = None
-    depot_tools_win_toolchain = \
-        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 ((_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)
-        with open(json_data_file, 'r') as tempf:
-            toolchain_data = json.load(tempf)
+  vs_runtime_dll_dirs = None
+  depot_tools_win_toolchain = \
+      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 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)
+    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')
-        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).
-        # http://crbug.com/345992
-        vs_runtime_dll_dirs = toolchain_data['runtime_dirs']
-        # The number of runtime_dirs in the toolchain_data was two (x64/x86) but
-        # changed to three (x64/x86/arm64) and this code needs to handle both
-        # possibilities, which can change independently from this code.
-        if len(vs_runtime_dll_dirs) == 2:
-            vs_runtime_dll_dirs.append('Arm64Unused')
+    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).
+    # http://crbug.com/345992
+    vs_runtime_dll_dirs = toolchain_data['runtime_dirs']
+    # The number of runtime_dirs in the toolchain_data was two (x64/x86) but
+    # changed to three (x64/x86/arm64) and this code needs to handle both
+    # possibilities, which can change independently from this code.
+    if len(vs_runtime_dll_dirs) == 2:
+      vs_runtime_dll_dirs.append('Arm64Unused')
 
-        os.environ['GYP_MSVS_OVERRIDE_PATH'] = toolchain
+    os.environ['GYP_MSVS_OVERRIDE_PATH'] = toolchain
+    os.environ['GYP_MSVS_VERSION'] = version
 
-        os.environ['WINDOWSSDKDIR'] = win_sdk
-        os.environ['WDK_DIR'] = wdk
-        # Include the VS runtime in the PATH in case it's not machine-installed.
-        runtime_path = os.path.pathsep.join(vs_runtime_dll_dirs)
-        os.environ['PATH'] = runtime_path + os.path.pathsep + os.environ['PATH']
-    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()
+    os.environ['WINDOWSSDKDIR'] = win_sdk
+    os.environ['WDK_DIR'] = wdk
+    # Include the VS runtime in the PATH in case it's not machine-installed.
+    runtime_path = os.path.pathsep.join(vs_runtime_dll_dirs)
+    os.environ['PATH'] = runtime_path + os.path.pathsep + os.environ['PATH']
+  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
-        # to create isolates or the mini_installer. Copying them to the output
-        # directory ensures that they are available when needed.
-        bitness = platform.architecture()[0]
-        # When running 64-bit python the x64 DLLs will be in System32
-        # ARM64 binaries will not be available in the system directories because we
-        # don't build on ARM64 machines.
-        x64_path = 'System32' if bitness == '64bit' else 'Sysnative'
-        x64_path = os.path.join(os.path.expandvars('%windir%'), x64_path)
-        vs_runtime_dll_dirs = [
-            x64_path,
-            os.path.join(os.path.expandvars('%windir%'), 'SysWOW64'),
-            'Arm64Unused'
-        ]
+    # 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
+    # to create isolates or the mini_installer. Copying them to the output
+    # directory ensures that they are available when needed.
+    bitness = platform.architecture()[0]
+    # When running 64-bit python the x64 DLLs will be in System32
+    # ARM64 binaries will not be available in the system directories because we
+    # don't build on ARM64 machines.
+    x64_path = 'System32' if bitness == '64bit' else 'Sysnative'
+    x64_path = os.path.join(os.path.expandvars('%windir%'), x64_path)
+    vs_runtime_dll_dirs = [x64_path,
+                           os.path.join(os.path.expandvars('%windir%'),
+                                        'SysWOW64'),
+                           'Arm64Unused']
 
-    return vs_runtime_dll_dirs
+  return vs_runtime_dll_dirs
 
 
 def _RegistryGetValueUsingWinReg(key, value):
-    """Use the _winreg module to obtain the value of a registry key.
+  """Use the _winreg module to obtain the value of a registry key.
 
   Args:
     key: The registry key.
@@ -147,249 +126,228 @@
     contents of the registry key's value, or None on failure.  Throws
     ImportError if _winreg is unavailable.
   """
-    import _winreg
-    try:
-        root, subkey = key.split('\\', 1)
-        assert root == 'HKLM'  # Only need HKLM for now.
-        with _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, subkey) as hkey:
-            return _winreg.QueryValueEx(hkey, value)[0]
-    except WindowsError:
-        return None
+  import winreg
+  try:
+    root, subkey = key.split('\\', 1)
+    assert root == 'HKLM'  # Only need HKLM for now.
+    with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, subkey) as hkey:
+      return winreg.QueryValueEx(hkey, value)[0]
+  except WindowsError:
+    return None
 
 
 def _RegistryGetValue(key, value):
-    try:
-        return _RegistryGetValueUsingWinReg(key, value)
-    except ImportError:
-        raise Exception('The python library _winreg not found.')
+  try:
+    return _RegistryGetValueUsingWinReg(key, value)
+  except ImportError:
+    raise Exception('The python library _winreg not found.')
 
 
 def GetVisualStudioVersion():
-    """Return best available version of Visual Studio.
+  """Return best available version of Visual Studio.
   """
-    supported_versions = list(MSVS_VERSIONS.keys())
 
-    # VS installed in depot_tools for Googlers
-    if bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1'))):
-        return supported_versions[0]
+  env_version = os.environ.get('GYP_MSVS_VERSION')
+  if env_version:
+    return env_version
 
-    # VS installed in system for external developers
-    supported_versions_str = ', '.join(
-        '{} ({})'.format(v, k) for k, v in MSVS_VERSIONS.items())
-    available_versions = []
-    for version in supported_versions:
-        # 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
+  supported_versions = list(MSVS_VERSIONS.keys())
 
-    if not available_versions:
-        raise Exception('No supported Visual Studio can be found.'
-                        ' Supported versions are: %s.' % supported_versions_str)
-    return available_versions[0]
+  # VS installed in depot_tools for Googlers
+  if bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1'))):
+    return list(supported_versions)[0]
+
+  # VS installed in system for external developers
+  supported_versions_str = ', '.join('{} ({})'.format(v,k)
+      for k,v in list(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),
+        os.path.expandvars('%ProgramFiles%' +
+                           '/Microsoft Visual Studio/%s' % version)):
+      if path and os.path.exists(path):
+        available_versions.append(version)
+        break
+
+  if not available_versions:
+    raise Exception('No supported Visual Studio can be found.'
+                    ' Supported versions are: %s.' % supported_versions_str)
+  return available_versions[0]
 
 
 def DetectVisualStudioPath():
-    """Return path to the installed Visual Studio.
+  """Return path to the GYP_MSVS_VERSION of Visual Studio.
   """
 
-    # Note that this code is used from
-    # build/toolchain/win/setup_toolchain.py as well.
-    version_as_year = GetVisualStudioVersion()
+  # Note that this code is used from
+  # build/toolchain/win/setup_toolchain.py as well.
+  version_as_year = GetVisualStudioVersion()
 
-    # The VC++ >=2017 install location needs to be located using COM instead of
-    # 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),
-                 os.path.expandvars('%ProgramFiles(x86)%' +
-                                    '/Microsoft Visual Studio/%s/BuildTools' %
-                                    version_as_year)):
-        if path and os.path.exists(path):
-            return path
+  # The VC++ >=2017 install location needs to be located using COM instead of
+  # 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.
+  possible_install_paths = (
+      os.path.expandvars('%s/Microsoft Visual Studio/%s/%s' %
+                         (program_path_var, version_as_year, product))
+      for program_path_var in ('%ProgramFiles%', '%ProgramFiles(x86)%')
+      for product in ('Enterprise', 'Professional', 'Community', 'Preview'))
+  for path in (
+      os.environ.get('vs%s_install' % version_as_year), *possible_install_paths):
+    if path and os.path.exists(path):
+      return path
 
-    raise Exception('Visual Studio Version %s not found.' % version_as_year)
+  raise Exception('Visual Studio Version %s (from GYP_MSVS_VERSION)'
+                  ' not found.' % version_as_year)
 
 
 def _CopyRuntimeImpl(target, source, verbose=True):
-    """Copy |source| to |target| if it doesn't already exist or if it needs to be
+  """Copy |source| to |target| if it doesn't already exist or if it needs to be
   updated (comparing last modified time as an approximate float match as for
   some reason the values tend to differ by ~1e-07 despite being copies of the
   same file... https://crbug.com/603603).
   """
-    if (os.path.isdir(os.path.dirname(target)) and
-        (not os.path.isfile(target) or
-         abs(os.stat(target).st_mtime - os.stat(source).st_mtime) >= 0.01)):
-        if verbose:
-            print('Copying %s to %s...' % (source, target))
-        if os.path.exists(target):
-            # Make the file writable so that we can delete it now, and keep it
-            # readable.
-            os.chmod(target, stat.S_IWRITE | stat.S_IREAD)
-            os.unlink(target)
-        shutil.copy2(source, target)
-        # Make the file writable so that we can overwrite or delete it later,
-        # keep it readable.
-        os.chmod(target, stat.S_IWRITE | stat.S_IREAD)
-
+  if (os.path.isdir(os.path.dirname(target)) and
+      (not os.path.isfile(target) or
+       abs(os.stat(target).st_mtime - os.stat(source).st_mtime) >= 0.01)):
+    if verbose:
+      print('Copying %s to %s...' % (source, target))
+    if os.path.exists(target):
+      # Make the file writable so that we can delete it now, and keep it
+      # readable.
+      os.chmod(target, stat.S_IWRITE | stat.S_IREAD)
+      os.unlink(target)
+    shutil.copy2(source, target)
+    # Make the file writable so that we can overwrite or delete it later,
+    # keep it readable.
+    os.chmod(target, stat.S_IWRITE | stat.S_IREAD)
 
 def _SortByHighestVersionNumberFirst(list_of_str_versions):
-    """This sorts |list_of_str_versions| according to version number rules
+  """This sorts |list_of_str_versions| according to version number rules
   so that version "1.12" is higher than version "1.9". Does not work
   with non-numeric versions like 1.4.a8 which will be higher than
   1.4.a12. It does handle the versions being embedded in file paths.
   """
+  def to_int_if_int(x):
+    try:
+      return int(x)
+    except ValueError:
+      return x
 
-    def to_int_if_int(x):
-        try:
-            return int(x)
-        except ValueError:
-            return x
+  def to_number_sequence(x):
+    part_sequence = re.split(r'[\\/\.]', x)
+    return [to_int_if_int(x) for x in part_sequence]
 
-    def to_number_sequence(x):
-        part_sequence = re.split(r'[\\/\.]', x)
-        return [to_int_if_int(x) for x in part_sequence]
+  list_of_str_versions.sort(key=to_number_sequence, reverse=True)
 
-    list_of_str_versions.sort(key=to_number_sequence, reverse=True)
-
-
-def _CopyUCRTRuntime(target_dir, source_dir, target_cpu, suffix):
-    """Copy both the msvcp and vccorlib runtime DLLs, only if the target doesn't
+def _CopyUCRTRuntime(target_dir, source_dir, target_cpu, dll_pattern, 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.VC14x.CRT/.
-        # Select VC toolset directory based on Visual Studio version
-        vc_redist_root = FindVCRedistRoot()
-        if suffix.startswith('.'):
-            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', 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)
-    # Copy the UCRT files from the Windows SDK. This location includes the
-    # api-ms-win-crt-*.dll files that are not found in the Windows directory.
-    # These files are needed for component builds. If WINDOWSSDKDIR is not set
-    # use the default SDK path. This will be the case when
-    # DEPOT_TOOLS_WIN_TOOLCHAIN=0 and vcvarsall.bat has not been run.
-    win_sdk_dir = os.path.normpath(
-        os.environ.get(
-            'WINDOWSSDKDIR',
-            os.path.expandvars('%ProgramFiles(x86)%'
-                               '\\Windows Kits\\10')))
-    # ARM64 doesn't have a redist for the ucrt DLLs because they are always
-    # present in the OS.
-    if target_cpu != 'arm64':
-        # Starting with the 10.0.17763 SDK the ucrt files are in a version-named
-        # directory - this handles both cases.
-        redist_dir = os.path.join(win_sdk_dir, 'Redist')
-        version_dirs = glob.glob(os.path.join(redist_dir, '10.*'))
-        if len(version_dirs) > 0:
-            _SortByHighestVersionNumberFirst(version_dirs)
-            redist_dir = version_dirs[0]
-        ucrt_dll_dirs = os.path.join(redist_dir, 'ucrt', 'DLLs', target_cpu)
-        ucrt_files = glob.glob(os.path.join(ucrt_dll_dirs, 'api-ms-win-*.dll'))
-        assert len(ucrt_files) > 0
-        for ucrt_src_file in ucrt_files:
-            file_part = os.path.basename(ucrt_src_file)
-            ucrt_dst_file = os.path.join(target_dir, file_part)
-            _CopyRuntimeImpl(ucrt_dst_file, ucrt_src_file, False)
-    # We must copy ucrtbase.dll for x64/x86, and ucrtbased.dll for all CPU types.
-    if target_cpu != 'arm64' or not suffix.startswith('.'):
-        if not suffix.startswith('.'):
-            # ucrtbased.dll is located at {win_sdk_dir}/bin/{a.b.c.d}/{target_cpu}/
-            # ucrt/.
-            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_bin_root, directory)
-                if not os.path.isdir(sdk_redist_root_version):
-                    continue
-                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))
+  if target_cpu == 'arm64':
+    # Windows ARM64 VCRuntime is located at {toolchain_root}/VC/Redist/MSVC/
+    # {x.y.z}/[debug_nonredist/]arm64/Microsoft.VC141.CRT/.
+    vc_redist_root = FindVCRedistRoot()
+    if suffix.startswith('.'):
+      source_dir = os.path.join(vc_redist_root,
+                                'arm64', 'Microsoft.VC141.CRT')
+    else:
+      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
+    target = os.path.join(target_dir, dll)
+    source = os.path.join(source_dir, dll)
+    _CopyRuntimeImpl(target, source)
+  # Copy the UCRT files from the Windows SDK. This location includes the
+  # api-ms-win-crt-*.dll files that are not found in the Windows directory.
+  # These files are needed for component builds. If WINDOWSSDKDIR is not set
+  # use the default SDK path. This will be the case when
+  # DEPOT_TOOLS_WIN_TOOLCHAIN=0 and vcvarsall.bat has not been run.
+  win_sdk_dir = os.path.normpath(
+      os.environ.get('WINDOWSSDKDIR',
+                     os.path.expandvars('%ProgramFiles(x86)%'
+                                        '\\Windows Kits\\10')))
+  # ARM64 doesn't have a redist for the ucrt DLLs because they are always
+  # present in the OS.
+  if target_cpu != 'arm64':
+    # Starting with the 10.0.17763 SDK the ucrt files are in a version-named
+    # directory - this handles both cases.
+    redist_dir = os.path.join(win_sdk_dir, 'Redist')
+    version_dirs = glob.glob(os.path.join(redist_dir, '10.*'))
+    if len(version_dirs) > 0:
+      _SortByHighestVersionNumberFirst(version_dirs)
+      redist_dir = version_dirs[0]
+    ucrt_dll_dirs = os.path.join(redist_dir, 'ucrt', 'DLLs', target_cpu)
+    ucrt_files = glob.glob(os.path.join(ucrt_dll_dirs, 'api-ms-win-*.dll'))
+    assert len(ucrt_files) > 0
+    for ucrt_src_file in ucrt_files:
+      file_part = os.path.basename(ucrt_src_file)
+      ucrt_dst_file = os.path.join(target_dir, file_part)
+      _CopyRuntimeImpl(ucrt_dst_file, ucrt_src_file, False)
+  # We must copy ucrtbase.dll for x64/x86, and ucrtbased.dll for all CPU types.
+  if target_cpu != 'arm64' or not suffix.startswith('.'):
+    if not suffix.startswith('.'):
+      # ucrtbased.dll is located at {win_sdk_dir}/bin/{a.b.c.d}/{target_cpu}/
+      # ucrt/.
+      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_bin_root, directory)
+        if not os.path.isdir(sdk_redist_root_version):
+          continue
+        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):
-    """Find the most recent Tools or Redist or other directory in an MSVC install.
+  """Find the most recent Tools or Redist or other directory in an MSVC install.
   Typical results are {toolchain_root}/VC/{component}/MSVC/{x.y.z}. The {x.y.z}
   version number part changes frequently so the highest version number found is
   used.
   """
 
-    SetEnvironmentAndGetRuntimeDllDirs()
-    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 = 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 os.path.isdir(directory):
-            return directory
-    raise Exception('Unable to find the VC %s directory.' % component)
+  SetEnvironmentAndGetRuntimeDllDirs()
+  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)
+  # 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)
+  raise Exception('Unable to find the VC %s directory.' % component)
 
 
 def FindVCRedistRoot():
-    """In >=VS2017, Redist binaries are located in
+  """In >=VS2017, Redist binaries are located in
   {toolchain_root}/VC/Redist/MSVC/{x.y.z}/{target_cpu}/.
 
   This returns the '{toolchain_root}/VC/Redist/MSVC/{x.y.z}/' path.
   """
-    return FindVCComponentRoot('Redist')
+  return FindVCComponentRoot('Redist')
 
 
 def _CopyRuntime(target_dir, source_dir, target_cpu, debug):
-    """Copy the VS runtime DLLs, only if the target doesn't exist, but the target
+  """Copy the VS runtime DLLs, only if the target doesn't exist, but the target
   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, suffix)
+  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)
 
 
 def CopyDlls(target_dir, configuration, target_cpu):
-    """Copy the VS runtime DLLs into the requested directory as needed.
+  """Copy the VS runtime DLLs into the requested directory as needed.
 
   configuration is one of 'Debug' or 'Release'.
   target_cpu is one of 'x86', 'x64' or 'arm64'.
@@ -397,27 +355,27 @@
   The debug configuration gets both the debug and release DLLs; the
   release config only the latter.
   """
-    vs_runtime_dll_dirs = SetEnvironmentAndGetRuntimeDllDirs()
-    if not vs_runtime_dll_dirs:
-        return
+  vs_runtime_dll_dirs = SetEnvironmentAndGetRuntimeDllDirs()
+  if not vs_runtime_dll_dirs:
+    return
 
-    x64_runtime, x86_runtime, arm64_runtime = vs_runtime_dll_dirs
-    if target_cpu == 'x64':
-        runtime_dir = x64_runtime
-    elif target_cpu == 'x86':
-        runtime_dir = x86_runtime
-    elif target_cpu == 'arm64':
-        runtime_dir = arm64_runtime
-    else:
-        raise Exception('Unknown target_cpu: ' + target_cpu)
-    _CopyRuntime(target_dir, runtime_dir, target_cpu, debug=False)
-    if configuration == 'Debug':
-        _CopyRuntime(target_dir, runtime_dir, target_cpu, debug=True)
-    _CopyDebugger(target_dir, target_cpu)
+  x64_runtime, x86_runtime, arm64_runtime = vs_runtime_dll_dirs
+  if target_cpu == 'x64':
+    runtime_dir = x64_runtime
+  elif target_cpu == 'x86':
+    runtime_dir = x86_runtime
+  elif target_cpu == 'arm64':
+    runtime_dir = arm64_runtime
+  else:
+    raise Exception('Unknown target_cpu: ' + target_cpu)
+  _CopyRuntime(target_dir, runtime_dir, target_cpu, debug=False)
+  if configuration == 'Debug':
+    _CopyRuntime(target_dir, runtime_dir, target_cpu, debug=True)
+  _CopyDebugger(target_dir, target_cpu)
 
 
 def _CopyDebugger(target_dir, target_cpu):
-    """Copy dbghelp.dll and dbgcore.dll into the requested directory as needed.
+  """Copy dbghelp.dll and dbgcore.dll into the requested directory as needed.
 
   target_cpu is one of 'x86', 'x64' or 'arm64'.
 
@@ -429,150 +387,148 @@
   dbgcore.dll is needed when using some functions from dbghelp.dll (like
   MinidumpWriteDump).
   """
-    win_sdk_dir = SetEnvironmentAndGetSDKDir()
-    if not win_sdk_dir:
-        return
+  win_sdk_dir = SetEnvironmentAndGetSDKDir()
+  if not win_sdk_dir:
+    return
 
-    # 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)
-        if not os.path.exists(full_path):
-            if is_optional:
-                continue
-            else:
-                raise Exception(
-                    '%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)
+  # 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)]
+  for debug_file, is_optional in debug_files:
+    full_path = os.path.join(win_sdk_dir, 'Debuggers', target_cpu, debug_file)
+    if not os.path.exists(full_path):
+      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))
+    target_path = os.path.join(target_dir, debug_file)
+    _CopyRuntimeImpl(target_path, full_path)
 
 
 def _GetDesiredVsToolchainHashes():
-    """Load a list of SHA1s corresponding to the toolchains that we want installed
+  """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)]
+    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)
 
 
 def ShouldUpdateToolchain():
-    """Check if the toolchain should be upgraded."""
-    if not os.path.exists(json_data_file):
-        return True
-    with open(json_data_file, 'r') as tempf:
-        toolchain_data = json.load(tempf)
-    version = toolchain_data['version']
-    env_version = GetVisualStudioVersion()
-    # If there's a mismatch between the version set in the environment and the one
-    # in the json file then the toolchain should be updated.
-    return version != env_version
+  """Check if the toolchain should be upgraded."""
+  if not os.path.exists(json_data_file):
+    return True
+  with open(json_data_file, 'r') as tempf:
+    toolchain_data = json.load(tempf)
+  version = toolchain_data['version']
+  env_version = GetVisualStudioVersion()
+  # If there's a mismatch between the version set in the environment and the one
+  # in the json file then the toolchain should be updated.
+  return version != env_version
 
 
 def Update(force=False, no_download=False):
-    """Requests an update of the toolchain to the specific hashes we have at
+  """Requests an update of the toolchain to the specific hashes we have at
   this revision. The update outputs a .json of the various configuration
   information required to pass to gyp which we use in |GetToolchainDir()|.
   If no_download is true then the toolchain will be configured if present but
   will not be downloaded.
   """
-    if force != False and force != '--force':
-        print('Unknown parameter "%s"' % force, file=sys.stderr)
-        return 1
-    if force == '--force' or os.path.exists(json_data_file):
-        force = True
+  if force != False and force != '--force':
+    print('Unknown parameter "%s"' % force, file=sys.stderr)
+    return 1
+  if force == '--force' or os.path.exists(json_data_file):
+    force = True
 
-    depot_tools_win_toolchain = \
-        bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1')))
-    if (_HostIsWindows() or force) and depot_tools_win_toolchain:
-        import find_depot_tools
-        depot_tools_path = find_depot_tools.add_depot_tools_to_path()
+  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):
+    import find_depot_tools
+    depot_tools_path = find_depot_tools.add_depot_tools_to_path()
 
-        # On Linux, the file system is usually case-sensitive while the Windows
-        # SDK only works on case-insensitive file systems.  If it doesn't already
-        # exist, set up a ciopfs fuse mount to put the SDK in a case-insensitive
-        # part of the file system.
-        toolchain_dir = os.path.join(depot_tools_path, 'win_toolchain',
-                                     'vs_files')
-        # For testing this block, unmount existing mounts with
-        # fusermount -u third_party/depot_tools/win_toolchain/vs_files
-        if sys.platform.startswith(
-                'linux') and not os.path.ismount(toolchain_dir):
-            import distutils.spawn
-            ciopfs = distutils.spawn.find_executable('ciopfs')
-            if not ciopfs:
-                # ciopfs not found in PATH; try the one downloaded from the DEPS hook.
-                ciopfs = os.path.join(script_dir, 'ciopfs')
-            if not os.path.isdir(toolchain_dir):
-                os.mkdir(toolchain_dir)
-            if not os.path.isdir(toolchain_dir + '.ciopfs'):
-                os.mkdir(toolchain_dir + '.ciopfs')
-            # Without use_ino, clang's #pragma once and Wnonportable-include-path
-            # both don't work right, see https://llvm.org/PR34931
-            # use_ino doesn't slow down builds, so it seems there's no drawback to
-            # just using it always.
-            subprocess.check_call([
-                ciopfs, '-o', 'use_ino', toolchain_dir + '.ciopfs',
-                toolchain_dir
-            ])
+    # On Linux, the file system is usually case-sensitive while the Windows
+    # SDK only works on case-insensitive file systems.  If it doesn't already
+    # exist, set up a ciopfs fuse mount to put the SDK in a case-insensitive
+    # part of the file system.
+    toolchain_dir = os.path.join(depot_tools_path, 'win_toolchain', 'vs_files')
+    # For testing this block, unmount existing mounts with
+    # fusermount -u third_party/depot_tools/win_toolchain/vs_files
+    if sys.platform.startswith('linux') and not os.path.ismount(toolchain_dir):
+      import distutils.spawn
+      ciopfs = distutils.spawn.find_executable('ciopfs')
+      if not ciopfs:
+        # ciopfs not found in PATH; try the one downloaded from the DEPS hook.
+        ciopfs = os.path.join(script_dir, 'ciopfs')
+      if not os.path.isdir(toolchain_dir):
+        os.mkdir(toolchain_dir)
+      if not os.path.isdir(toolchain_dir + '.ciopfs'):
+        os.mkdir(toolchain_dir + '.ciopfs')
+      # Without use_ino, clang's #pragma once and Wnonportable-include-path
+      # both don't work right, see https://llvm.org/PR34931
+      # use_ino doesn't slow down builds, so it seems there's no drawback to
+      # just using it always.
+      subprocess.check_call([
+          ciopfs, '-o', 'use_ino', toolchain_dir + '.ciopfs', toolchain_dir])
 
-        get_toolchain_args = [
-            # TODO(athom): use sys.executable (python3).
-            # Note: depot_tools contains python.bat not python.exe
-            # so for python to land on the first python in the PATH
-            # irrespective of its extension we pass shell=True below.
-            'python',
-            os.path.join(depot_tools_path, 'win_toolchain',
-                         'get_toolchain_if_necessary.py'),
-            '--output-json',
-            json_data_file,
-        ] + _GetDesiredVsToolchainHashes()
-        if force:
-            get_toolchain_args.append('--force')
-        if no_download:
-            get_toolchain_args.append('--no-download')
-        subprocess.check_call(get_toolchain_args, shell=True)
+    # 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,
+        os.path.join(depot_tools_path,
+                    'win_toolchain',
+                    'get_toolchain_if_necessary.py'),
+        '--output-json', json_data_file,
+      ] + _GetDesiredVsToolchainHashes()
+    if force:
+      get_toolchain_args.append('--force')
+    if no_download:
+      get_toolchain_args.append('--no-download')
+    subprocess.check_call(get_toolchain_args)
 
-    return 0
+  return 0
 
 
 def NormalizePath(path):
-    while path.endswith('\\'):
-        path = path[:-1]
-    return path
+  while path.endswith('\\'):
+    path = path[:-1]
+  return path
 
 
 def SetEnvironmentAndGetSDKDir():
-    """Gets location information about the current sdk (must have been
+  """Gets location information about the current sdk (must have been
   previously updated by 'update'). This is used for the GN build."""
-    SetEnvironmentAndGetRuntimeDllDirs()
+  SetEnvironmentAndGetRuntimeDllDirs()
 
-    # If WINDOWSSDKDIR is not set, search the default SDK path and set it.
-    if not 'WINDOWSSDKDIR' in os.environ:
-        default_sdk_path = os.path.expandvars('%ProgramFiles(x86)%'
-                                              '\\Windows Kits\\10')
-        if os.path.isdir(default_sdk_path):
-            os.environ['WINDOWSSDKDIR'] = default_sdk_path
+  # If WINDOWSSDKDIR is not set, search the default SDK path and set it.
+  if not 'WINDOWSSDKDIR' in os.environ:
+    default_sdk_path = os.path.expandvars('%ProgramFiles(x86)%'
+                                          '\\Windows Kits\\10')
+    if os.path.isdir(default_sdk_path):
+      os.environ['WINDOWSSDKDIR'] = default_sdk_path
 
-    return NormalizePath(os.environ['WINDOWSSDKDIR'])
+  return NormalizePath(os.environ['WINDOWSSDKDIR'])
 
 
 def GetToolchainDir():
-    """Gets location information about the current toolchain (must have been
+  """Gets location information about the current toolchain (must have been
   previously updated by 'update'). This is used for the GN build."""
-    runtime_dll_dirs = SetEnvironmentAndGetRuntimeDllDirs()
-    win_sdk_dir = SetEnvironmentAndGetSDKDir()
+  runtime_dll_dirs = SetEnvironmentAndGetRuntimeDllDirs()
+  win_sdk_dir = SetEnvironmentAndGetSDKDir()
 
-    print('''vs_path = %s
+  print('''vs_path = %s
 sdk_path = %s
 vs_version = %s
 wdk_dir = %s
@@ -584,16 +540,16 @@
 
 
 def main():
-    commands = {
-        'update': Update,
-        'get_toolchain_dir': GetToolchainDir,
-        'copy_dlls': CopyDlls,
-    }
-    if len(sys.argv) < 2 or sys.argv[1] not in commands:
-        print('Expected one of: %s' % ', '.join(commands), file=sys.stderr)
-        return 1
-    return commands[sys.argv[1]](*sys.argv[2:])
+  commands = {
+      'update': Update,
+      'get_toolchain_dir': GetToolchainDir,
+      'copy_dlls': CopyDlls,
+  }
+  if len(sys.argv) < 2 or sys.argv[1] not in commands:
+    print('Expected one of: %s' % ', '.join(commands), file=sys.stderr)
+    return 1
+  return commands[sys.argv[1]](*sys.argv[2:])
 
 
 if __name__ == '__main__':
-    sys.exit(main())
+  sys.exit(main())
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/api/code.dart b/pkg/_fe_analyzer_shared/lib/src/macros/api/code.dart
index e0a09f2..de81796 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/api/code.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/api/code.dart
@@ -251,7 +251,7 @@
           this[i],
           separator,
         ],
-        last,
+        if (isNotEmpty) last,
       ];
 }
 
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart b/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
index 59b7a26..18ceb87 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
@@ -34,7 +34,7 @@
       }
       constructorEntries.writeln('},');
     });
-    constructorEntries.writeln("}");
+    constructorEntries.writeln('},');
   });
   return template
       .replaceFirst(_importMarker, imports.toString())
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 830127a..36c0cb7 100644
--- a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
@@ -7024,6 +7024,67 @@
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Template<Message Function(String name)>
+    templateJsInteropStaticInteropTrustTypesUsageNotAllowed =
+    const Template<Message Function(String name)>(
+        problemMessageTemplate:
+            r"""JS interop class '#name' has an `@trustTypes` annotation, but `@trustTypes` is only supported within the sdk.""",
+        correctionMessageTemplate:
+            r"""Try removing the `@trustTypes` annotation.""",
+        withArguments:
+            _withArgumentsJsInteropStaticInteropTrustTypesUsageNotAllowed);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String name)>
+    codeJsInteropStaticInteropTrustTypesUsageNotAllowed =
+    const Code<Message Function(String name)>(
+  "JsInteropStaticInteropTrustTypesUsageNotAllowed",
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsJsInteropStaticInteropTrustTypesUsageNotAllowed(
+    String name) {
+  if (name.isEmpty) throw 'No name provided';
+  name = demangleMixinApplicationName(name);
+  return new Message(codeJsInteropStaticInteropTrustTypesUsageNotAllowed,
+      problemMessage:
+          """JS interop class '${name}' has an `@trustTypes` annotation, but `@trustTypes` is only supported within the sdk.""",
+      correctionMessage: """Try removing the `@trustTypes` annotation.""",
+      arguments: {'name': name});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<Message Function(String name)>
+    templateJsInteropStaticInteropTrustTypesUsedWithoutStaticInterop =
+    const Template<Message Function(String name)>(
+        problemMessageTemplate:
+            r"""JS interop class '#name' has an `@trustTypes` annotation, but no `@staticInterop` annotation.""",
+        correctionMessageTemplate:
+            r"""Try marking the class using `@staticInterop`.""",
+        withArguments:
+            _withArgumentsJsInteropStaticInteropTrustTypesUsedWithoutStaticInterop);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String name)>
+    codeJsInteropStaticInteropTrustTypesUsedWithoutStaticInterop =
+    const Code<Message Function(String name)>(
+  "JsInteropStaticInteropTrustTypesUsedWithoutStaticInterop",
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsJsInteropStaticInteropTrustTypesUsedWithoutStaticInterop(
+    String name) {
+  if (name.isEmpty) throw 'No name provided';
+  name = demangleMixinApplicationName(name);
+  return new Message(
+      codeJsInteropStaticInteropTrustTypesUsedWithoutStaticInterop,
+      problemMessage:
+          """JS interop class '${name}' has an `@trustTypes` annotation, but no `@staticInterop` annotation.""",
+      correctionMessage: """Try marking the class using `@staticInterop`.""",
+      arguments: {'name': name});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<Message Function(String name)>
     templateJsInteropStaticInteropWithInstanceMembers =
     const Template<Message Function(String name)>(
         problemMessageTemplate:
diff --git a/pkg/_js_interop_checks/lib/js_interop_checks.dart b/pkg/_js_interop_checks/lib/js_interop_checks.dart
index 13917c7..351c410 100644
--- a/pkg/_js_interop_checks/lib/js_interop_checks.dart
+++ b/pkg/_js_interop_checks/lib/js_interop_checks.dart
@@ -22,7 +22,9 @@
         templateJsInteropStaticInteropWithInstanceMembers,
         templateJsInteropStaticInteropWithNonStaticSupertype,
         templateJsInteropJSClassExtendsDartClass,
-        templateJsInteropNativeClassInAnnotation;
+        templateJsInteropNativeClassInAnnotation,
+        templateJsInteropStaticInteropTrustTypesUsageNotAllowed,
+        templateJsInteropStaticInteropTrustTypesUsedWithoutStaticInterop;
 
 import 'src/js_interop.dart';
 
@@ -39,7 +41,6 @@
   /// Libraries that use `external` to exclude from checks on external.
   static final Iterable<String> _pathsWithAllowedDartExternalUsage = <String>[
     '_foreign_helper', // for foreign helpers
-    '_late_helper', // for dart2js late variable utilities
     '_interceptors', // for ddc JS string
     '_native_typed_data',
     '_runtime', // for ddc types at runtime
@@ -67,6 +68,11 @@
     'generated_tests/web_2/native/native_test',
   ];
 
+  List<Pattern> _allowedTrustTypesTestPatterns = [
+    RegExp(r'(?<!generated_)tests/lib/js'),
+    RegExp(r'(?<!generated_)tests/lib_2/js'),
+  ];
+
   bool _libraryIsGlobalNamespace = false;
 
   JsInteropChecks(
@@ -104,6 +110,25 @@
     _classHasJSAnnotation = hasJSInteropAnnotation(cls);
     _classHasAnonymousAnnotation = hasAnonymousAnnotation(cls);
     _classHasStaticInteropAnnotation = hasStaticInteropAnnotation(cls);
+    bool classHasTrustTypesAnnotation = hasTrustTypesAnnotation(cls);
+    if (classHasTrustTypesAnnotation) {
+      if (!_isAllowedTrustTypesUsage(cls)) {
+        _diagnosticsReporter.report(
+            templateJsInteropStaticInteropTrustTypesUsageNotAllowed
+                .withArguments(cls.name),
+            cls.fileOffset,
+            cls.name.length,
+            cls.fileUri);
+      }
+      if (!_classHasStaticInteropAnnotation) {
+        _diagnosticsReporter.report(
+            templateJsInteropStaticInteropTrustTypesUsedWithoutStaticInterop
+                .withArguments(cls.name),
+            cls.fileOffset,
+            cls.name.length,
+            cls.fileUri);
+      }
+    }
     var superclass = cls.superclass;
     if (superclass != null && superclass != _coreTypes.objectClass) {
       var superHasJSAnnotation = hasJSInteropAnnotation(superclass);
@@ -357,6 +382,14 @@
     }
   }
 
+  /// Verifies that use of `@trustTypes` is allowed.
+  bool _isAllowedTrustTypesUsage(Class cls) {
+    Uri uri = cls.enclosingLibrary.importUri;
+    return uri.isScheme('dart') && uri.path == 'ui' ||
+        _allowedTrustTypesTestPatterns
+            .any((pattern) => uri.path.contains(pattern));
+  }
+
   /// Verifies given member is one of the allowed usages of external:
   /// a dart low level library, a foreign helper, a native test,
   /// or a from environment constructor.
diff --git a/pkg/_js_interop_checks/lib/src/js_interop.dart b/pkg/_js_interop_checks/lib/src/js_interop.dart
index 9874a1c..3f3c517 100644
--- a/pkg/_js_interop_checks/lib/src/js_interop.dart
+++ b/pkg/_js_interop_checks/lib/src/js_interop.dart
@@ -20,6 +20,11 @@
 bool hasStaticInteropAnnotation(Annotatable a) =>
     a.annotations.any(_isStaticInteropAnnotation);
 
+/// Returns true iff the node has an `@trustTypes` annotation from
+/// `package:js` or from the internal `dart:_js_annotations`.
+bool hasTrustTypesAnnotation(Annotatable a) =>
+    a.annotations.any(_isTrustTypesAnnotation);
+
 /// If [a] has a `@JS('...')` annotation, returns the value inside the
 /// parentheses.
 ///
@@ -80,6 +85,9 @@
 bool _isStaticInteropAnnotation(Expression value) =>
     _isInteropAnnotation(value, '_StaticInterop');
 
+bool _isTrustTypesAnnotation(Expression value) =>
+    _isInteropAnnotation(value, '_TrustTypes');
+
 /// Returns true if [value] is the `Native` annotation from `dart:_js_helper`.
 bool _isNativeAnnotation(Expression value) {
   var c = _annotationClass(value);
diff --git a/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart b/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart
index 57b3a31..5205099 100644
--- a/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart
+++ b/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart
@@ -7,16 +7,19 @@
 import 'package:kernel/core_types.dart';
 import 'package:kernel/type_environment.dart';
 
-import '../js_interop.dart' show getJSName;
+import '../js_interop.dart' show getJSName, hasTrustTypesAnnotation;
 
 /// Replaces js_util methods with inline calls to foreign_helper JS which
 /// emits the code as a JavaScript code fragment.
 class JsUtilOptimizer extends Transformer {
   final Procedure _callMethodTarget;
+  final Procedure _callMethodTrustTypeTarget;
   final List<Procedure> _callMethodUncheckedTargets;
+  final List<Procedure> _callMethodUncheckedTrustTypeTargets;
   final Procedure _callConstructorTarget;
   final List<Procedure> _callConstructorUncheckedTargets;
   final Procedure _getPropertyTarget;
+  final Procedure _getPropertyTrustTypeTarget;
   final Procedure _setPropertyTarget;
   final Procedure _setPropertyUncheckedTarget;
 
@@ -37,14 +40,21 @@
   final CoreTypes _coreTypes;
   final StatefulStaticTypeContext _staticTypeContext;
   Map<Reference, ExtensionMemberDescriptor>? _extensionMemberIndex;
+  late Set<Reference> _shouldTrustType;
 
   JsUtilOptimizer(this._coreTypes, ClassHierarchy hierarchy)
       : _callMethodTarget =
             _coreTypes.index.getTopLevelProcedure('dart:js_util', 'callMethod'),
+        _callMethodTrustTypeTarget = _coreTypes.index
+            .getTopLevelProcedure('dart:js_util', '_callMethodTrustType'),
         _callMethodUncheckedTargets = List<Procedure>.generate(
             5,
             (i) => _coreTypes.index.getTopLevelProcedure(
                 'dart:js_util', '_callMethodUnchecked$i')),
+        _callMethodUncheckedTrustTypeTargets = List<Procedure>.generate(
+            5,
+            (i) => _coreTypes.index.getTopLevelProcedure(
+                'dart:js_util', '_callMethodUncheckedTrustType$i')),
         _callConstructorTarget = _coreTypes.index
             .getTopLevelProcedure('dart:js_util', 'callConstructor'),
         _callConstructorUncheckedTargets = List<Procedure>.generate(
@@ -53,6 +63,8 @@
                 'dart:js_util', '_callConstructorUnchecked$i')),
         _getPropertyTarget = _coreTypes.index
             .getTopLevelProcedure('dart:js_util', 'getProperty'),
+        _getPropertyTrustTypeTarget = _coreTypes.index
+            .getTopLevelProcedure('dart:js_util', '_getPropertyTrustType'),
         _setPropertyTarget = _coreTypes.index
             .getTopLevelProcedure('dart:js_util', 'setProperty'),
         _setPropertyUncheckedTarget = _coreTypes.index
@@ -93,14 +105,16 @@
     if (node.isExternal && node.isExtensionMember) {
       var index = _extensionMemberIndex ??=
           _createExtensionMembersIndex(node.enclosingLibrary);
-      var nodeDescriptor = index[node.reference]!;
+      Reference reference = node.reference;
+      var nodeDescriptor = index[reference]!;
+      bool shouldTrustType = _shouldTrustType.contains(reference);
       if (!nodeDescriptor.isStatic) {
         if (nodeDescriptor.kind == ExtensionMemberKind.Getter) {
-          transformedBody = _getExternalGetterBody(node);
+          transformedBody = _getExternalGetterBody(node, shouldTrustType);
         } else if (nodeDescriptor.kind == ExtensionMemberKind.Setter) {
           transformedBody = _getExternalSetterBody(node);
         } else if (nodeDescriptor.kind == ExtensionMemberKind.Method) {
-          transformedBody = _getExternalMethodBody(node);
+          transformedBody = _getExternalMethodBody(node, shouldTrustType);
         }
       }
     }
@@ -120,9 +134,17 @@
   Map<Reference, ExtensionMemberDescriptor> _createExtensionMembersIndex(
       Library library) {
     _extensionMemberIndex = {};
-    library.extensions.forEach((extension) => extension.members.forEach(
-        (descriptor) =>
-            _extensionMemberIndex![descriptor.member] = descriptor));
+    _shouldTrustType = {};
+    library.extensions
+        .forEach((extension) => extension.members.forEach((descriptor) {
+              Reference reference = descriptor.member;
+              _extensionMemberIndex![reference] = descriptor;
+              DartType onType = extension.onType;
+              if (onType is InterfaceType &&
+                  hasTrustTypesAnnotation(onType.className.asClass)) {
+                _shouldTrustType.add(reference);
+              }
+            }));
     return _extensionMemberIndex!;
   }
 
@@ -130,11 +152,13 @@
   ///
   /// The new function body will call the optimized version of
   /// `js_util.getProperty` for the given external getter.
-  ReturnStatement _getExternalGetterBody(Procedure node) {
+  ReturnStatement _getExternalGetterBody(Procedure node, bool shouldTrustType) {
     var function = node.function;
     assert(function.positionalParameters.length == 1);
+    Procedure target =
+        shouldTrustType ? _getPropertyTrustTypeTarget : _getPropertyTarget;
     var getPropertyInvocation = StaticInvocation(
-        _getPropertyTarget,
+        target,
         Arguments([
           VariableGet(function.positionalParameters.first),
           StringLiteral(_getExtensionMemberName(node))
@@ -170,10 +194,12 @@
   ///
   /// The new function body will call the optimized version of
   /// `js_util.callMethod` for the given external method.
-  ReturnStatement _getExternalMethodBody(Procedure node) {
+  ReturnStatement _getExternalMethodBody(Procedure node, bool shouldTrustType) {
     var function = node.function;
+    Procedure target =
+        shouldTrustType ? _callMethodTrustTypeTarget : _callMethodTarget;
     var callMethodInvocation = StaticInvocation(
-        _callMethodTarget,
+        target,
         Arguments([
           VariableGet(function.positionalParameters.first),
           StringLiteral(_getExtensionMemberName(node)),
@@ -185,7 +211,8 @@
           function.returnType
         ]))
       ..fileOffset = node.fileOffset;
-    return ReturnStatement(_lowerCallMethod(callMethodInvocation));
+    return ReturnStatement(_lowerCallMethod(callMethodInvocation,
+        shouldTrustType: shouldTrustType));
   }
 
   /// Returns the extension member name.
@@ -214,7 +241,8 @@
     if (node.target == _setPropertyTarget) {
       node = _lowerSetProperty(node);
     } else if (node.target == _callMethodTarget) {
-      node = _lowerCallMethod(node);
+      // Never trust types on explicit `js_util` calls.
+      node = _lowerCallMethod(node, shouldTrustType: false);
     } else if (node.target == _callConstructorTarget) {
       node = _lowerCallConstructor(node);
     }
@@ -245,13 +273,17 @@
   /// Calls will be lowered when using a List literal or constant list with 0-4
   /// elements for the `callMethod` arguments, or the `List.empty()` factory.
   /// Removing the checks allows further inlining by the compilers.
-  StaticInvocation _lowerCallMethod(StaticInvocation node) {
+  StaticInvocation _lowerCallMethod(StaticInvocation node,
+      {required bool shouldTrustType}) {
     Arguments arguments = node.arguments;
     assert(arguments.positional.length == 3);
     assert(arguments.named.isEmpty);
+    List<Procedure> targets = shouldTrustType
+        ? _callMethodUncheckedTrustTypeTargets
+        : _callMethodUncheckedTargets;
 
     return _lowerToCallUnchecked(
-        node, _callMethodUncheckedTargets, arguments.positional.sublist(0, 2));
+        node, targets, arguments.positional.sublist(0, 2));
   }
 
   /// Lowers the given js_util `callConstructor` call to `_callConstructorUncheckedN`
diff --git a/pkg/analysis_server/test/integration/server/bazel_changes_test.dart b/pkg/analysis_server/test/integration/server/bazel_changes_test.dart
index 3a64173..5dd8d39 100644
--- a/pkg/analysis_server/test/integration/server/bazel_changes_test.dart
+++ b/pkg/analysis_server/test/integration/server/bazel_changes_test.dart
@@ -127,7 +127,9 @@
 
     await processedNotification.future;
     expect(errors, isNotEmpty);
-    expect(errors[0].message, contains('generated.dart'));
+    var error = errors.singleWhere((e) => e.code == 'uri_does_not_exist',
+        orElse: () => throw "'uri_does_not_exist' error was not found");
+    expect(error.message, contains('generated.dart'));
 
     // This seems to be necessary (at least when running the test from source),
     // because it takes a while for the watcher isolate to start.
diff --git a/pkg/analysis_server/test/lsp/diagnostic_test.dart b/pkg/analysis_server/test/lsp/diagnostic_test.dart
index c5024cb..0c6572e 100644
--- a/pkg/analysis_server/test/lsp/diagnostic_test.dart
+++ b/pkg/analysis_server/test/lsp/diagnostic_test.dart
@@ -5,6 +5,7 @@
 import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart' as plugin;
 import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
+import 'package:linter/src/rules.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -378,6 +379,71 @@
     }
   }
 
+  /// Tests that diagnostic ordering is stable when minor changes are made to
+  /// the file that does not alter the diagnostics besides extending their
+  /// range and adding to their messages.
+  ///
+  /// https://github.com/Dart-Code/Dart-Code/issues/3934
+  Future<void> test_stableOrder() async {
+    /// Helper to pad out the content in a way that has previously triggered
+    /// this issue.
+    String wrappedContent(String content) => '''
+//
+//
+//
+//
+
+void f() {
+  $content
+}
+''';
+
+    registerLintRules();
+    newFile(analysisOptionsPath, '''
+linter:
+  rules:
+    - prefer_typing_uninitialized_variables
+
+analyzer:
+  language:
+    strict-inference: true
+    ''');
+
+    newFile(mainFilePath, '');
+    await initialize();
+    await openFile(mainFileUri, '');
+
+    // Collect the initial set of diagnostic to compare against.
+    var docVersion = 1;
+    final originalDiagnosticsUpdate = waitForDiagnostics(mainFileUri);
+    await replaceFile(docVersion++, mainFileUri, wrappedContent('final bar;'));
+    final originalDiagnostics = await originalDiagnosticsUpdate;
+
+    // Helper to update the content and verify the same diagnostics are returned
+    // in the same order, despite the changes to offset/message altering
+    // hashcodes.
+    Future<void> verifyDiagnostics(String content) async {
+      final diagnosticsUpdate = waitForDiagnostics(mainFileUri);
+      await replaceFile(docVersion++, mainFileUri, wrappedContent(content));
+      final diagnostics = await diagnosticsUpdate;
+      expect(
+        diagnostics!.map((d) => d.code),
+        originalDiagnostics!.map((d) => d.code),
+      );
+    }
+
+    // These changes do not affect the errors being produced (besides offset/
+    // message text) but will cause hashcode changes that previously altered the
+    // returned order.
+    await verifyDiagnostics('final dbar;');
+    await verifyDiagnostics('final dybar;');
+    await verifyDiagnostics('final dynbar;');
+    await verifyDiagnostics('final dynabar;');
+    await verifyDiagnostics('final dynambar;');
+    await verifyDiagnostics('final dynamibar;');
+    await verifyDiagnostics('final dynamicbar;');
+  }
+
   Future<void> test_todos_boolean() async {
     // TODOs only show up if there's also some code in the file.
     const contents = '''
diff --git a/pkg/analyzer/lib/error/listener.dart b/pkg/analyzer/lib/error/listener.dart
index 88555b2..c21fe48 100644
--- a/pkg/analyzer/lib/error/listener.dart
+++ b/pkg/analyzer/lib/error/listener.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.
 
-import 'dart:collection';
-
 import 'package:analyzer/dart/ast/ast.dart'
     show AstNode, ConstructorDeclaration;
 import 'package:analyzer/dart/ast/token.dart';
@@ -257,7 +255,7 @@
 
   @override
   void onError(AnalysisError error) {
-    (_errors ??= HashSet<AnalysisError>()).add(error);
+    (_errors ??= {}).add(error);
   }
 }
 
diff --git a/pkg/analyzer/lib/src/summary2/macro_application.dart b/pkg/analyzer/lib/src/summary2/macro_application.dart
index 9f618ae..1931e3b 100644
--- a/pkg/analyzer/lib/src/summary2/macro_application.dart
+++ b/pkg/analyzer/lib/src/summary2/macro_application.dart
@@ -172,6 +172,30 @@
     );
   }
 
+  static macro.ParameterDeclarationImpl _buildFormalParameter(
+    FormalParameter node,
+  ) {
+    if (node is DefaultFormalParameter) {
+      node = node.parameter;
+    }
+
+    final macro.TypeAnnotationImpl typeAnnotation;
+    if (node is SimpleFormalParameter) {
+      typeAnnotation = _buildTypeAnnotation(node.type);
+    } else {
+      throw UnimplementedError('(${node.runtimeType}) $node');
+    }
+
+    return macro.ParameterDeclarationImpl(
+      id: macro.RemoteInstance.uniqueId,
+      identifier:
+          _buildIdentifier(node.identifier!), // TODO(scheglov) might be null
+      isNamed: node.isNamed,
+      isRequired: node.isRequired,
+      type: typeAnnotation,
+    );
+  }
+
   static macro.IdentifierImpl _buildIdentifier(Identifier node) {
     final String name;
     if (node is SimpleIdentifier) {
@@ -185,8 +209,27 @@
     );
   }
 
-  static macro.TypeAnnotationImpl _buildTypeAnnotation(TypeAnnotation node) {
-    if (node is NamedType) {
+  static macro.TypeAnnotationImpl _buildTypeAnnotation(TypeAnnotation? node) {
+    if (node == null) {
+      return macro.OmittedTypeAnnotationImpl(
+        id: macro.RemoteInstance.uniqueId,
+      );
+    } else if (node is GenericFunctionType) {
+      return macro.FunctionTypeAnnotationImpl(
+        id: macro.RemoteInstance.uniqueId,
+        isNullable: node.question != null,
+        namedParameters: node.parameters.parameters
+            .where((e) => e.isNamed)
+            .map(_buildFormalParameter)
+            .toList(),
+        positionalParameters: node.parameters.parameters
+            .where((e) => e.isPositional)
+            .map(_buildFormalParameter)
+            .toList(),
+        returnType: _buildTypeAnnotation(node.returnType),
+        typeParameters: _buildTypeParameters(node.typeParameters),
+      );
+    } else if (node is NamedType) {
       return macro.NamedTypeAnnotationImpl(
         id: macro.RemoteInstance.uniqueId,
         identifier: _buildIdentifier(node.name),
@@ -262,22 +305,38 @@
         return -operandValue;
       }
     } else if (node is SetOrMapLiteral) {
-      final result = <Object?, Object?>{};
-      for (final element in node.elements) {
-        if (element is! MapLiteralEntry) {
-          _throwError(element, 'MapLiteralEntry expected');
-        }
-        final key = evaluate(element.key);
-        final value = evaluate(element.value);
-        result[key] = value;
-      }
-      return result;
+      return _setOrMapLiteral(node);
     } else if (node is SimpleStringLiteral) {
       return node.value;
     }
     _throwError(node, 'Not supported: ${node.runtimeType}');
   }
 
+  Object _setOrMapLiteral(SetOrMapLiteral node) {
+    if (node.elements.every((e) => e is Expression)) {
+      final result = <Object?>{};
+      for (final element in node.elements) {
+        if (element is! Expression) {
+          _throwError(element, 'Expression expected');
+        }
+        final value = evaluate(element);
+        result.add(value);
+      }
+      return result;
+    }
+
+    final result = <Object?, Object?>{};
+    for (final element in node.elements) {
+      if (element is! MapLiteralEntry) {
+        _throwError(element, 'MapLiteralEntry expected');
+      }
+      final key = evaluate(element.key);
+      final value = evaluate(element.value);
+      result[key] = value;
+    }
+    return result;
+  }
+
   Never _throwError(AstNode node, String message) {
     throw ArgumentMacroApplicationError(
       annotationIndex: annotationIndex,
diff --git a/pkg/analyzer/lib/src/summary2/macro_application_error.dart b/pkg/analyzer/lib/src/summary2/macro_application_error.dart
index a7b9018..de56919 100644
--- a/pkg/analyzer/lib/src/summary2/macro_application_error.dart
+++ b/pkg/analyzer/lib/src/summary2/macro_application_error.dart
@@ -33,7 +33,8 @@
 
   @override
   String toStringForTest() {
-    return 'Argument(annotation: $annotationIndex, argument: $argumentIndex)';
+    return 'Argument(annotation: $annotationIndex, '
+        'argument: $argumentIndex, message: $message)';
   }
 
   @override
@@ -118,7 +119,7 @@
 
   @override
   String toStringForTest() {
-    return 'Unknown(annotation: $annotationIndex)';
+    return 'Unknown(annotation: $annotationIndex, message: $message)';
   }
 
   @override
diff --git a/pkg/analyzer/test/error/error_listener_test.dart b/pkg/analyzer/test/error/error_listener_test.dart
new file mode 100644
index 0000000..25a0b3b
--- /dev/null
+++ b/pkg/analyzer/test/error/error_listener_test.dart
@@ -0,0 +1,46 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analyzer/error/error.dart';
+import 'package:analyzer/error/listener.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(RecordingErrorListenerTest);
+  });
+}
+
+@reflectiveTest
+class RecordingErrorListenerTest {
+  test_orderedAsReported() {
+    final listener = RecordingErrorListener();
+    listener.onError(_MockAnalysisError(expectedIndex: 0, hashCode: 1));
+    listener.onError(_MockAnalysisError(expectedIndex: 1, hashCode: 10));
+    listener.onError(_MockAnalysisError(expectedIndex: 2, hashCode: -50));
+    listener.onError(_MockAnalysisError(expectedIndex: 3, hashCode: 20));
+    listener.onError(_MockAnalysisError(expectedIndex: 4, hashCode: 1));
+
+    // Expect the errors are returned in the order they are reported, and not
+    // affected by their hashcodes.
+    expect(
+      listener.errors.cast<_MockAnalysisError>().map((e) => e.expectedIndex),
+      [0, 1, 2, 3, 4],
+    );
+  }
+}
+
+/// An [AnalysisError] that allows setting an explicit hash code.
+class _MockAnalysisError implements AnalysisError {
+  @override
+  int hashCode;
+
+  int expectedIndex;
+
+  _MockAnalysisError({required this.expectedIndex, required this.hashCode});
+
+  @override
+  dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
+}
diff --git a/pkg/analyzer/test/error/test_all.dart b/pkg/analyzer/test/error/test_all.dart
index 19eca56..684e462 100644
--- a/pkg/analyzer/test/error/test_all.dart
+++ b/pkg/analyzer/test/error/test_all.dart
@@ -4,11 +4,13 @@
 
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
+import 'error_listener_test.dart' as error_listener;
 import 'error_reporter_test.dart' as error_reporter;
 import 'error_test.dart' as error_test;
 
 main() {
   defineReflectiveSuite(() {
+    error_listener.main();
     error_reporter.main();
     error_test.main();
   }, name: 'error');
diff --git a/pkg/analyzer/test/src/summary/macro/declaration_text.dart b/pkg/analyzer/test/src/summary/macro/declaration_text.dart
index 066c9b6..85ec1ed 100644
--- a/pkg/analyzer/test/src/summary/macro/declaration_text.dart
+++ b/pkg/analyzer/test/src/summary/macro/declaration_text.dart
@@ -131,15 +131,12 @@
   _TypeStringBuilder(this._sink);
 
   void write(TypeAnnotation type) {
-    if (type is NamedTypeAnnotation) {
-      _sink.write(type.identifier.name);
-      _sink.writeList(
-        elements: type.typeArguments,
-        write: write,
-        separator: ', ',
-        open: '<',
-        close: '>',
-      );
+    if (type is FunctionTypeAnnotation) {
+      _writeFunctionTypeAnnotation(type);
+    } else if (type is NamedTypeAnnotation) {
+      _writeNamedTypeAnnotation(type);
+    } else if (type is OmittedTypeAnnotation) {
+      _sink.write('OmittedType');
     } else {
       throw UnimplementedError('(${type.runtimeType}) $type');
     }
@@ -147,6 +144,80 @@
       _sink.write('?');
     }
   }
+
+  void _writeFormalParameter(ParameterDeclaration node) {
+    final String closeSeparator;
+    if (node.isNamed) {
+      _sink.write('{');
+      closeSeparator = '}';
+      if (node.isRequired) {
+        _sink.write('required ');
+      }
+    } else if (!node.isRequired) {
+      _sink.write('[');
+      closeSeparator = ']';
+    } else {
+      closeSeparator = '';
+    }
+
+    write(node.type);
+    _sink.write(' ');
+    _sink.write(node.identifier.name);
+
+    _sink.write(closeSeparator);
+  }
+
+  void _writeFunctionTypeAnnotation(FunctionTypeAnnotation type) {
+    write(type.returnType);
+    _sink.write(' Function');
+
+    _sink.writeList(
+      elements: type.typeParameters,
+      write: _writeTypeParameter,
+      separator: ', ',
+      open: '<',
+      close: '>',
+    );
+
+    _sink.write('(');
+    var hasFormalParameter = false;
+    for (final formalParameter in type.positionalParameters) {
+      if (hasFormalParameter) {
+        _sink.write(', ');
+      }
+      _writeFormalParameter(formalParameter);
+      hasFormalParameter = true;
+    }
+    for (final formalParameter in type.namedParameters) {
+      if (hasFormalParameter) {
+        _sink.write(', ');
+      }
+      _writeFormalParameter(formalParameter);
+      hasFormalParameter = true;
+    }
+    _sink.write(')');
+  }
+
+  void _writeNamedTypeAnnotation(NamedTypeAnnotation type) {
+    _sink.write(type.identifier.name);
+    _sink.writeList(
+      elements: type.typeArguments,
+      write: write,
+      separator: ', ',
+      open: '<',
+      close: '>',
+    );
+  }
+
+  void _writeTypeParameter(TypeParameterDeclaration node) {
+    _sink.write(node.identifier.name);
+
+    final bound = node.bound;
+    if (bound != null) {
+      _sink.write(' extends ');
+      write(bound);
+    }
+  }
 }
 
 extension on StringSink {
@@ -158,22 +229,24 @@
     String? close,
   }) {
     elements = elements.toList();
-    if (elements.isNotEmpty) {
-      if (open != null) {
-        this.write(open);
+    if (elements.isEmpty) {
+      return;
+    }
+
+    if (open != null) {
+      this.write(open);
+    }
+    var isFirst = true;
+    for (var element in elements) {
+      if (isFirst) {
+        isFirst = false;
+      } else {
+        this.write(separator);
       }
-      var isFirst = true;
-      for (var element in elements) {
-        if (isFirst) {
-          isFirst = false;
-        } else {
-          this.write(separator);
-        }
-        write(element);
-      }
-      if (close != null) {
-        this.write(close);
-      }
+      write(element);
+    }
+    if (close != null) {
+      this.write(close);
     }
   }
 }
diff --git a/pkg/analyzer/test/src/summary/macro_test.dart b/pkg/analyzer/test/src/summary/macro_test.dart
index 0c3f764..f21ac8a 100644
--- a/pkg/analyzer/test/src/summary/macro_test.dart
+++ b/pkg/analyzer/test/src/summary/macro_test.dart
@@ -5,6 +5,7 @@
 import 'package:_fe_analyzer_shared/src/macros/executor/multi_executor.dart'
     as macro;
 import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/summary2/kernel_compilation_service.dart';
 import 'package:analyzer/src/summary2/macro.dart';
@@ -83,7 +84,8 @@
       },
       constructorParametersCode: '(this.foo, this.bar)',
       argumentsCode: '(0, const Object())',
-      expectedErrors: 'Argument(annotation: 0, argument: 1)',
+      expectedErrors: 'Argument(annotation: 0, argument: 1, '
+          'message: Not supported: InstanceCreationExpressionImpl)',
     );
   }
 
@@ -235,6 +237,19 @@
     );
   }
 
+  test_arguments_typesPhase_type_set() async {
+    await _assertTypesPhaseArgumentsText(
+      fields: {
+        'foo': 'Set<Object?>',
+      },
+      constructorParametersCode: '(this.foo)',
+      argumentsCode: '({1, 2, 3})',
+      expected: r'''
+foo: {1, 2, 3}
+''',
+    );
+  }
+
   test_arguments_typesPhase_type_string() async {
     await _assertTypesPhaseArgumentsText(
       fields: {'foo': 'String'},
@@ -385,6 +400,88 @@
 ''');
   }
 
+  test_introspect_types_functionTypeAnnotation_formalParameters_namedOptional_simpleFormalParameter() async {
+    await _assertTypesPhaseIntrospectionText(r'''
+class A extends B<void Function(int a, {int? b, int? c})> {}
+''', r'''
+class A
+  superclass: B<void Function(int a, {int? b}, {int? c})>
+''');
+  }
+
+  test_introspect_types_functionTypeAnnotation_formalParameters_namedRequired_simpleFormalParameter() async {
+    await _assertTypesPhaseIntrospectionText(r'''
+class A extends B<void Function(int a, {required int b, required int c})> {}
+''', r'''
+class A
+  superclass: B<void Function(int a, {required int b}, {required int c})>
+''');
+  }
+
+  test_introspect_types_functionTypeAnnotation_formalParameters_positionalOptional_simpleFormalParameter() async {
+    await _assertTypesPhaseIntrospectionText(r'''
+class A extends B<void Function(int a, [int b, int c])> {}
+''', r'''
+class A
+  superclass: B<void Function(int a, [int b], [int c])>
+''');
+  }
+
+  /// TODO(scheglov) Tests for unnamed positional formal parameters.
+  test_introspect_types_functionTypeAnnotation_formalParameters_positionalRequired_simpleFormalParameter() async {
+    await _assertTypesPhaseIntrospectionText(r'''
+class A extends B<void Function(int a, double b)> {}
+''', r'''
+class A
+  superclass: B<void Function(int a, double b)>
+''');
+  }
+
+  test_introspect_types_functionTypeAnnotation_nullable() async {
+    await _assertTypesPhaseIntrospectionText(r'''
+class A extends B<void Function()?> {}
+''', r'''
+class A
+  superclass: B<void Function()?>
+''');
+  }
+
+  test_introspect_types_functionTypeAnnotation_returnType() async {
+    await _assertTypesPhaseIntrospectionText(r'''
+class A extends B<void Function()> {}
+''', r'''
+class A
+  superclass: B<void Function()>
+''');
+  }
+
+  test_introspect_types_functionTypeAnnotation_returnType_omitted() async {
+    await _assertTypesPhaseIntrospectionText(r'''
+class A extends B<Function()> {}
+''', r'''
+class A
+  superclass: B<OmittedType Function()>
+''');
+  }
+
+  test_introspect_types_functionTypeAnnotation_typeParameters() async {
+    await _assertTypesPhaseIntrospectionText(r'''
+class A extends B<void Function<T, U extends num>()> {}
+''', r'''
+class A
+  superclass: B<void Function<T, U extends num>()>
+''');
+  }
+
+  test_introspect_types_namedTypeAnnotation_prefixed() async {
+    await _assertTypesPhaseIntrospectionText(r'''
+class A extends prefix.B {}
+''', r'''
+class A
+  superclass: B
+''');
+  }
+
   test_macroFlag_class() async {
     var library = await buildLibrary(r'''
 macro class A {}
@@ -504,6 +601,14 @@
       {'package:test/arguments_text.dart'}
     ]);
 
+    final A = library.definingCompilationUnit.getType('A');
+    if (expectedErrors != null) {
+      expect(_errorsStrForClassElement(A), expectedErrors);
+      return;
+    } else {
+      _assertNoErrorsForClassElement(A);
+    }
+
     if (expected != null) {
       final x = library.parts.single.topLevelVariables.single;
       expect(x.name, 'x');
@@ -514,13 +619,6 @@
         print(actual);
       }
       expect(actual, expected);
-    } else if (expectedErrors != null) {
-      var A = library.definingCompilationUnit.getType('A');
-      A as ClassElementImpl;
-      expect(
-        A.macroApplicationErrors.map((e) => e.toStringForTest()).join('\n'),
-        expectedErrors,
-      );
     } else {
       fail("Either 'expected' or 'expectedErrors' must be provided.");
     }
@@ -561,10 +659,26 @@
       {'package:test/declaration_text.dart'}
     ]);
 
+    _assertNoErrorsForClassElement(
+      library.definingCompilationUnit.getType('A'),
+    );
+
     var x = library.parts.single.topLevelVariables.single;
     expect(x.name, 'x');
     x as ConstTopLevelVariableElementImpl;
     var x_literal = x.constantInitializer as SimpleStringLiteral;
     return x_literal.value;
   }
+
+  static void _assertNoErrorsForClassElement(ClassElement? element) {
+    var actual = _errorsStrForClassElement(element);
+    expect(actual, isEmpty);
+  }
+
+  static String _errorsStrForClassElement(ClassElement? element) {
+    element as ClassElementImpl;
+    return element.macroApplicationErrors.map((e) {
+      return e.toStringForTest();
+    }).join('\n');
+  }
 }
diff --git a/pkg/compiler/lib/compiler.dart b/pkg/compiler/lib/compiler.dart
index 9b2f5c1..5fd5ff1 100644
--- a/pkg/compiler/lib/compiler.dart
+++ b/pkg/compiler/lib/compiler.dart
@@ -10,7 +10,7 @@
 
 import 'package:front_end/src/api_unstable/dart2js.dart' as fe;
 
-import 'src/compiler.dart';
+import 'src/compiler.dart' show Compiler;
 import 'src/options.dart' show CompilerOptions;
 
 // Unless explicitly allowed, passing [:null:] for any argument to the
diff --git a/pkg/compiler/lib/src/common/elements.dart b/pkg/compiler/lib/src/common/elements.dart
index 6aedcca..2919892 100644
--- a/pkg/compiler/lib/src/common/elements.dart
+++ b/pkg/compiler/lib/src/common/elements.dart
@@ -247,7 +247,7 @@
   LibraryEntity get foreignLibrary =>
       _foreignLibrary ??= _env.lookupLibrary(Uris.dart__foreign_helper);
 
-  /// The dart:_rti library.
+  /// The dart:_internal library.
   LibraryEntity get rtiLibrary =>
       _rtiLibrary ??= _env.lookupLibrary(Uris.dart__rti, required: true);
 
@@ -926,24 +926,9 @@
 
   FunctionEntity get defineProperty => _findHelperFunction('defineProperty');
 
-  FunctionEntity get throwLateFieldNI =>
-      _findLateHelperFunction('throwLateFieldNI');
-
-  FunctionEntity get throwLateFieldAI =>
-      _findLateHelperFunction('throwLateFieldAI');
-
   FunctionEntity get throwLateFieldADI =>
       _findLateHelperFunction('throwLateFieldADI');
 
-  FunctionEntity get throwUnnamedLateFieldNI =>
-      _findLateHelperFunction('throwUnnamedLateFieldNI');
-
-  FunctionEntity get throwUnnamedLateFieldAI =>
-      _findLateHelperFunction('throwUnnamedLateFieldAI');
-
-  FunctionEntity get throwUnnamedLateFieldADI =>
-      _findLateHelperFunction('throwUnnamedLateFieldADI');
-
   bool isExtractTypeArguments(FunctionEntity member) {
     return member.name == 'extractTypeArguments' &&
         member.library == internalLibrary;
@@ -1135,9 +1120,6 @@
   /// Most foreign helpers are located in the `dart:_foreign_helper` library.
   bool isForeignHelper(MemberEntity member) {
     return member.library == foreignLibrary ||
-        isLateReadCheck(member) ||
-        isLateWriteOnceCheck(member) ||
-        isLateInitializeOnceCheck(member) ||
         isCreateInvocationMirrorHelper(member);
   }
 
@@ -1157,23 +1139,11 @@
       _isTopLevelFunctionNamed('isJsSentinel', member);
 
   /// Returns `true` if [member] is the `_lateReadCheck` function defined in
-  /// dart:_late_helper.
+  /// dart:_internal.
   bool isLateReadCheck(MemberEntity member) =>
       member.library == lateHelperLibrary &&
       _isTopLevelFunctionNamed('_lateReadCheck', member);
 
-  /// Returns `true` if [member] is the `_lateWriteOnceCheck` function defined
-  /// in dart:_late_helper.
-  bool isLateWriteOnceCheck(MemberEntity member) =>
-      member.library == lateHelperLibrary &&
-      _isTopLevelFunctionNamed('_lateWriteOnceCheck', member);
-
-  /// Returns `true` if [member] is the `_lateInitializeOnceCheck` function
-  /// defined in dart:_late_helper.
-  bool isLateInitializeOnceCheck(MemberEntity member) =>
-      member.library == lateHelperLibrary &&
-      _isTopLevelFunctionNamed('_lateInitializeOnceCheck', member);
-
   /// Returns `true` if [member] is the `createSentinel` function defined in
   /// dart:_internal.
   bool isCreateSentinel(MemberEntity member) =>
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index fc65993..93ca227 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -246,9 +246,8 @@
   /// Dumps a list of unused [ir.Library]'s in the [KernelResult]. This *must*
   /// be called before [setMainAndTrimComponent], because that method will
   /// discard the unused [ir.Library]s.
-  void dumpUnusedLibraries(ir.Component component, List<Uri> libraries) {
-    var usedUris = libraries.toSet();
-    bool isUnused(ir.Library l) => !usedUris.contains(l.importUri);
+  void dumpUnusedLibraries(ir.Component component, Set<Uri> libraries) {
+    bool isUnused(ir.Library l) => !libraries.contains(l.importUri);
     String libraryString(ir.Library library) {
       return '${library.importUri}(${library.fileUri})';
     }
@@ -270,7 +269,7 @@
 
   /// Trims a component down to only the provided library uris.
   ir.Component trimComponent(
-      ir.Component component, List<Uri> librariesToInclude) {
+      ir.Component component, Set<Uri> librariesToInclude) {
     var irLibraryMap = <Uri, ir.Library>{};
     var irLibraries = <ir.Library>[];
     for (var library in component.libraries) {
@@ -325,11 +324,8 @@
     }
   }
 
-  JClosedWorld computeClosedWorld(
-      ir.Component component,
-      List<ModuleData> moduleData,
-      Uri rootLibraryUri,
-      Iterable<Uri> libraries) {
+  JClosedWorld computeClosedWorld(ir.Component component, ModuleData moduleData,
+      Uri rootLibraryUri, Iterable<Uri> libraries) {
     frontendStrategy.registerLoadedLibraries(component, libraries);
     frontendStrategy.registerModuleData(moduleData);
     ResolutionEnqueuer resolutionEnqueuer = frontendStrategy
@@ -402,16 +398,31 @@
         untrimmedComponentForDumpInfo = component;
       }
       if (options.cfeOnly) {
+        // [ModuleData] must be deserialized with the full component, i.e.
+        // before trimming.
+        ModuleData moduleData;
+        if (options.modularAnalysisInputs != null) {
+          moduleData = await serializationTask.deserializeModuleData(component);
+        }
+
+        Set<Uri> includedLibraries = output.libraries.toSet();
         if (options.fromDill) {
-          List<Uri> libraries = output.libraries;
           if (options.dumpUnusedLibraries) {
-            dumpUnusedLibraries(component, libraries);
+            dumpUnusedLibraries(component, includedLibraries);
           }
           if (options.entryUri != null) {
-            component = trimComponent(component, libraries);
+            component = trimComponent(component, includedLibraries);
           }
         }
-        await serializationTask.serializeComponent(component);
+        if (moduleData == null) {
+          await serializationTask.serializeComponent(component);
+        } else {
+          // Trim [moduleData] down to only the included libraries.
+          moduleData.impactData
+              .removeWhere((uri, _) => !includedLibraries.contains(uri));
+          await serializationTask.serializeModuleData(
+              moduleData, component, includedLibraries);
+        }
       }
       return output.withNewComponent(component);
     } else {
@@ -434,7 +445,7 @@
         'runModularAnalysis', () async => modular_analysis.run(input));
   }
 
-  Future<List<ModuleData>> produceModuleData(load_kernel.Output output) async {
+  Future<ModuleData> produceModuleData(load_kernel.Output output) async {
     ir.Component component = output.component;
     if (options.modularMode) {
       Set<Uri> moduleLibraries = output.moduleLibraries.toSet();
@@ -444,7 +455,7 @@
         serializationTask.serializeModuleData(
             moduleData, component, moduleLibraries);
       }
-      return [moduleData];
+      return moduleData;
     } else {
       return await serializationTask.deserializeModuleData(component);
     }
@@ -530,7 +541,7 @@
   }
 
   Future<ClosedWorldAndIndices> produceClosedWorld(
-      load_kernel.Output output, List<ModuleData> moduleData) async {
+      load_kernel.Output output, ModuleData moduleData) async {
     ir.Component component = output.component;
     ClosedWorldAndIndices closedWorldAndIndices;
     if (options.readClosedWorldUri == null) {
@@ -626,7 +637,7 @@
 
     // Run modular analysis. This may be null if modular analysis was not
     // requested for this pipeline.
-    List<ModuleData> moduleData;
+    ModuleData moduleData;
     if (options.modularMode || options.hasModularAnalysisInputs) {
       moduleData = await produceModuleData(output);
     }
diff --git a/pkg/compiler/lib/src/dart2js.dart b/pkg/compiler/lib/src/dart2js.dart
index 6c6fd69..099cf5d 100644
--- a/pkg/compiler/lib/src/dart2js.dart
+++ b/pkg/compiler/lib/src/dart2js.dart
@@ -318,15 +318,13 @@
       fail("Cannot use ${Flags.writeModularAnalysis} "
           "and write serialized codegen simultaneously.");
     }
-    if (writeStrategy == WriteStrategy.toKernel) {
-      fail("Cannot use ${Flags.writeModularAnalysis} "
-          "and run the CFE simultaneously.");
-    }
     if (argument != Flags.writeModularAnalysis) {
       writeModularAnalysisUri =
           fe.nativeToUri(extractPath(argument, isDirectory: false));
     }
-    writeStrategy = WriteStrategy.toModularAnalysis;
+    writeStrategy = writeStrategy == WriteStrategy.toKernel
+        ? WriteStrategy.toKernelWithModularAnalysis
+        : WriteStrategy.toModularAnalysis;
   }
 
   void setReadData(String argument) {
@@ -371,10 +369,6 @@
   }
 
   void setCfeOnly(String argument) {
-    if (writeStrategy == WriteStrategy.toModularAnalysis) {
-      fail("Cannot use ${Flags.cfeOnly} "
-          "and write serialized modular analysis simultaneously.");
-    }
     if (writeStrategy == WriteStrategy.toClosedWorld) {
       fail("Cannot use ${Flags.cfeOnly} "
           "and write serialized closed world simultaneously.");
@@ -387,7 +381,9 @@
       fail("Cannot use ${Flags.cfeOnly} "
           "and write serialized codegen simultaneously.");
     }
-    writeStrategy = WriteStrategy.toKernel;
+    writeStrategy = writeStrategy == WriteStrategy.toModularAnalysis
+        ? WriteStrategy.toKernelWithModularAnalysis
+        : WriteStrategy.toKernel;
   }
 
   void setReadCodegen(String argument) {
@@ -825,6 +821,12 @@
             "and read serialized codegen simultaneously.");
       }
       break;
+    case WriteStrategy.toKernelWithModularAnalysis:
+      out ??= Uri.base.resolve('out.dill');
+      options.add(Flags.cfeOnly);
+      writeModularAnalysisUri ??= Uri.base.resolve('$out.mdata');
+      options.add('${Flags.writeModularAnalysis}=${writeModularAnalysisUri}');
+      break;
     case WriteStrategy.toModularAnalysis:
       writeModularAnalysisUri ??= Uri.base.resolve('$out.mdata');
       options.add('${Flags.writeModularAnalysis}=${writeModularAnalysisUri}');
@@ -1007,6 +1009,15 @@
         String output = fe.relativizeUri(Uri.base, out, Platform.isWindows);
         summary += 'compiled to dill: ${output}.';
         break;
+      case WriteStrategy.toKernelWithModularAnalysis:
+        processName = 'Compiled';
+        outputName = 'kernel and bytes data';
+        outputSize = outputProvider.totalDataWritten;
+        String output = fe.relativizeUri(Uri.base, out, Platform.isWindows);
+        String dataOutput = fe.relativizeUri(
+            Uri.base, writeModularAnalysisUri, Platform.isWindows);
+        summary += 'compiled to dill and data: ${output} and ${dataOutput}.';
+        break;
       case WriteStrategy.toModularAnalysis:
         processName = 'Serialized';
         outputName = 'bytes data';
@@ -1547,6 +1558,7 @@
 
 enum WriteStrategy {
   toKernel,
+  toKernelWithModularAnalysis,
   toModularAnalysis,
   toClosedWorld,
   toData,
diff --git a/pkg/compiler/lib/src/io/code_output.dart b/pkg/compiler/lib/src/io/code_output.dart
index 19d4300..b603c00 100644
--- a/pkg/compiler/lib/src/io/code_output.dart
+++ b/pkg/compiler/lib/src/io/code_output.dart
@@ -6,7 +6,7 @@
 
 library dart2js.code_output;
 
-import '../../compiler.dart';
+import '../../compiler.dart' as api show OutputSink;
 import 'code_output_listener.dart';
 export 'code_output_listener.dart';
 import 'source_information.dart';
@@ -223,7 +223,7 @@
 class StreamCodeOutput extends AbstractCodeOutput {
   @override
   int length = 0;
-  final OutputSink output;
+  final api.OutputSink output;
 
   StreamCodeOutput(this.output, [List<CodeOutputListener> listeners])
       : super(listeners);
diff --git a/pkg/compiler/lib/src/io/source_file.dart b/pkg/compiler/lib/src/io/source_file.dart
index be4708b..961127c 100644
--- a/pkg/compiler/lib/src/io/source_file.dart
+++ b/pkg/compiler/lib/src/io/source_file.dart
@@ -13,7 +13,7 @@
 import 'package:kernel/ast.dart' as kernel show Location, Source;
 
 import 'location_provider.dart' show LocationProvider;
-import '../../compiler.dart';
+import '../../compiler.dart' show Input, InputKind;
 
 /// Represents a file of source code. The content can be either a [String] or
 /// a UTF-8 encoded [List<int>] of bytes.
diff --git a/pkg/compiler/lib/src/ir/modular.dart b/pkg/compiler/lib/src/ir/modular.dart
index a5da488..5487173 100644
--- a/pkg/compiler/lib/src/ir/modular.dart
+++ b/pkg/compiler/lib/src/ir/modular.dart
@@ -46,27 +46,45 @@
       ir.Member node, EnumSet<PragmaAnnotation> pragmaAnnotations);
 }
 
-/// Data computed for an entire compilation module.
+/// [ModuleData] is the data computed modularly, i.e. modularly computed impact
+/// data. Currently, we aggregate this data when computing the closed world, so it
+/// reflects all of the modularly computed data across the entire program.
 class ModuleData {
   static const String tag = 'ModuleData';
 
   // TODO(joshualitt) Support serializing ModularMemberData;
-  final Map<ir.Member, ImpactBuilderData> impactData;
+  final Map<Uri, Map<ir.Member, ImpactBuilderData>> impactData;
 
-  ModuleData(this.impactData);
+  ModuleData([Map<Uri, Map<ir.Member, ImpactBuilderData>> impactData])
+      : this.impactData = impactData ?? {};
 
-  factory ModuleData.fromDataSource(DataSourceReader source) {
+  factory ModuleData.fromImpactData(
+          Map<Uri, Map<ir.Member, ImpactBuilderData>> impactData) =>
+      ModuleData(impactData);
+
+  ModuleData readMoreFromDataSource(DataSourceReader source) {
     source.begin(tag);
-    var impactData = source
-        .readMemberNodeMap(() => ImpactBuilderData.fromDataSource(source));
+    int uriCount = source.readInt();
+    for (int i = 0; i < uriCount; i++) {
+      Uri uri = source.readUri();
+      impactData[uri] = source
+          .readMemberNodeMap(() => ImpactBuilderData.fromDataSource(source));
+    }
     source.end(tag);
-    return ModuleData(impactData);
+    return this;
   }
 
+  factory ModuleData.fromDataSource(DataSourceReader source) =>
+      ModuleData().readMoreFromDataSource(source);
+
   void toDataSink(DataSinkWriter sink) {
     sink.begin(tag);
-    sink.writeMemberNodeMap<ImpactBuilderData>(
-        impactData, (e) => e.toDataSink(sink));
+    sink.writeInt(impactData.keys.length);
+    impactData.forEach((uri, data) {
+      sink.writeUri(uri);
+      sink.writeMemberNodeMap<ImpactBuilderData>(
+          data, (e) => e.toDataSink(sink));
+    });
     sink.end(tag);
   }
 }
diff --git a/pkg/compiler/lib/src/js_backend/backend_impact.dart b/pkg/compiler/lib/src/js_backend/backend_impact.dart
index f6dfa17..37855c0 100644
--- a/pkg/compiler/lib/src/js_backend/backend_impact.dart
+++ b/pkg/compiler/lib/src/js_backend/backend_impact.dart
@@ -779,28 +779,4 @@
       ], otherImpacts: [
         _needsString('Needed to encode the new RTI ruleset.')
       ]);
-
-  BackendImpact _lateFieldReadCheck;
-
-  BackendImpact get lateFieldReadCheck =>
-      _lateFieldReadCheck ??= BackendImpact(globalUses: [
-        _commonElements.throwUnnamedLateFieldNI,
-        _commonElements.throwLateFieldNI,
-      ]);
-
-  BackendImpact _lateFieldWriteOnceCheck;
-
-  BackendImpact get lateFieldWriteOnceCheck =>
-      _lateFieldWriteOnceCheck ??= BackendImpact(globalUses: [
-        _commonElements.throwUnnamedLateFieldAI,
-        _commonElements.throwLateFieldAI,
-      ]);
-
-  BackendImpact _lateFieldInitializeOnceCheck;
-
-  BackendImpact get lateFieldInitializeOnceCheck =>
-      _lateFieldInitializeOnceCheck ??= BackendImpact(globalUses: [
-        _commonElements.throwUnnamedLateFieldADI,
-        _commonElements.throwLateFieldADI,
-      ]);
 }
diff --git a/pkg/compiler/lib/src/js_backend/frequency_assignment.dart b/pkg/compiler/lib/src/js_backend/frequency_assignment.dart
index a145c9b..d01096c 100644
--- a/pkg/compiler/lib/src/js_backend/frequency_assignment.dart
+++ b/pkg/compiler/lib/src/js_backend/frequency_assignment.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.10
-
 import 'dart:collection' show SplayTreeMap;
 
 /// Assigns names from [nameSequence] to items using a naive algorithm.
@@ -63,7 +61,7 @@
   List<_Pool> pools = _Pool.makePools(names);
 
   // First cohort with unassigned items.
-  _Cohort firstCohort = _Cohort.makeCohorts(items, countOf);
+  _Cohort? firstCohort = _Cohort.makeCohorts(items, countOf);
 
   for (var pool in pools) {
     // Completely allocate smaller pools before allocating larger
@@ -125,7 +123,7 @@
 /// A [_Pool] is a set of identifiers of the same length from which names are
 /// allocated.
 class _Pool {
-  final List<String /*?*/ > _names = [];
+  final List<String?> _names = [];
 
   // Keep the unused (available) slots in an ordered set for efficiently finding
   // the next available slot (i.e. linear rehash).  We are concerned about
@@ -164,8 +162,7 @@
       (throw StateError('No entries left in pool'));
 
   String allocate(int slot) {
-    String name = _names[slot];
-    assert(name != null);
+    String name = _names[slot]!;
     _names[slot] = null;
     _availableSlots.remove(slot);
     return name;
@@ -178,23 +175,23 @@
 /// A [_Cohort] is a set of entities which occur with the same frequency. The
 /// entities are identified by integers.
 class _Cohort {
-  _Cohort next; // Next cohort in decreasing frequency.
+  _Cohort? next; // Next cohort in decreasing frequency.
   final int count; // This is the cohort of items occuring [count] times.
   Set<int> unassigned = Set();
 
   _Cohort(this.count);
 
-  _Cohort skipEmpty() {
-    _Cohort cohort = this;
+  _Cohort? skipEmpty() {
+    _Cohort? cohort = this;
     while (cohort != null && cohort.remaining == 0) cohort = cohort.next;
     return cohort;
   }
 
   int get remaining => unassigned.length;
 
-  static _Cohort makeCohorts(int items, int Function(int) countOf) {
+  static _Cohort? makeCohorts(int items, int Function(int) countOf) {
     // Build _Cohorts.
-    _Cohort first, current;
+    _Cohort? first, current;
     int lastCount = -1;
     for (int item = 0; item < items; item++) {
       int count = countOf(item);
@@ -208,7 +205,7 @@
         }
         current = next;
       }
-      current.unassigned.add(item);
+      current!.unassigned.add(item);
     }
     return first;
   }
diff --git a/pkg/compiler/lib/src/js_backend/name_sequence.dart b/pkg/compiler/lib/src/js_backend/name_sequence.dart
index 5f97bc8..1f7bdf1 100644
--- a/pkg/compiler/lib/src/js_backend/name_sequence.dart
+++ b/pkg/compiler/lib/src/js_backend/name_sequence.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.10
-
 import 'package:front_end/src/api_unstable/dart2js.dart'
     show $0, $9, $A, $Z, $_, $a, $z;
 
diff --git a/pkg/compiler/lib/src/js_backend/resolution_listener.dart b/pkg/compiler/lib/src/js_backend/resolution_listener.dart
index c6b8b41..88e96d3 100644
--- a/pkg/compiler/lib/src/js_backend/resolution_listener.dart
+++ b/pkg/compiler/lib/src/js_backend/resolution_listener.dart
@@ -292,19 +292,6 @@
       _registerBackendImpact(worldImpact, _impacts.noSuchMethodSupport);
     }
 
-    if (_commonElements.isLateReadCheck(member)) {
-      _registerBackendImpact(worldImpact, _impacts.lateFieldReadCheck);
-    }
-
-    if (_commonElements.isLateWriteOnceCheck(member)) {
-      _registerBackendImpact(worldImpact, _impacts.lateFieldWriteOnceCheck);
-    }
-
-    if (_commonElements.isLateInitializeOnceCheck(member)) {
-      _registerBackendImpact(
-          worldImpact, _impacts.lateFieldInitializeOnceCheck);
-    }
-
     if (member.isGetter && member.name == Identifiers.runtimeType_) {
       // Enable runtime type support if we discover a getter called
       // runtimeType. We have to enable runtime type before hitting the
diff --git a/pkg/compiler/lib/src/js_backend/string_abbreviation.dart b/pkg/compiler/lib/src/js_backend/string_abbreviation.dart
index b41e069..391bf6a 100644
--- a/pkg/compiler/lib/src/js_backend/string_abbreviation.dart
+++ b/pkg/compiler/lib/src/js_backend/string_abbreviation.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.10
-
 import 'package:front_end/src/api_unstable/dart2js.dart'
     show $0, $9, $A, $Z, $a, $z;
 
@@ -21,7 +19,7 @@
 
 class _Node {
   final String string;
-  String assignment;
+  late String assignment;
   _Node(this.string);
 }
 
@@ -47,7 +45,7 @@
     // Partition on the code unit at position [index], setting [terminating] if
     // some string ends at this length;
     Map<int, List<_Node>> partition = {};
-    _Node terminating;
+    _Node? terminating;
 
     for (final node in nodes) {
       String string = node.string;
@@ -71,8 +69,8 @@
       var keys = partition.keys.toList();
       var keyEncodings = _discriminators(keys, path.isEmpty);
       for (int key in keys) {
-        var children = partition[key];
-        var discriminator = keyEncodings[key];
+        var children = partition[key]!;
+        var discriminator = keyEncodings[key]!;
         _partition(children, minLength, [...path, discriminator], index + 1);
       }
       return;
diff --git a/pkg/compiler/lib/src/js_emitter/headers.dart b/pkg/compiler/lib/src/js_emitter/headers.dart
index 66df30c..90e4098 100644
--- a/pkg/compiler/lib/src/js_emitter/headers.dart
+++ b/pkg/compiler/lib/src/js_emitter/headers.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.10
-
 library dart2js.js_emitter.headers;
 
 import '../options.dart';
diff --git a/pkg/compiler/lib/src/js_emitter/sorter.dart b/pkg/compiler/lib/src/js_emitter/sorter.dart
index fa27579..0de3e33 100644
--- a/pkg/compiler/lib/src/js_emitter/sorter.dart
+++ b/pkg/compiler/lib/src/js_emitter/sorter.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.10
-
 library dart2js.js_emitter.sorter;
 
 import '../elements/entities.dart';
diff --git a/pkg/compiler/lib/src/kernel/dart2js_target.dart b/pkg/compiler/lib/src/kernel/dart2js_target.dart
index 9d05fc1..9430808 100644
--- a/pkg/compiler/lib/src/kernel/dart2js_target.dart
+++ b/pkg/compiler/lib/src/kernel/dart2js_target.dart
@@ -235,6 +235,14 @@
       const Dart2jsDartLibrarySupport();
 }
 
+const implicitlyUsedLibraries = <String>[
+  'dart:_foreign_helper',
+  'dart:_interceptors',
+  'dart:_js_helper',
+  'dart:_late_helper',
+  'dart:js_util'
+];
+
 // TODO(sigmund): this "extraRequiredLibraries" needs to be removed...
 // compile-platform should just specify which libraries to compile instead.
 const requiredLibraries = <String, List<String>>{
diff --git a/pkg/compiler/lib/src/kernel/kernel_strategy.dart b/pkg/compiler/lib/src/kernel/kernel_strategy.dart
index 94f87ab..cd1467f 100644
--- a/pkg/compiler/lib/src/kernel/kernel_strategy.dart
+++ b/pkg/compiler/lib/src/kernel/kernel_strategy.dart
@@ -229,7 +229,7 @@
     }
   }
 
-  void registerModuleData(List<ModuleData> data) {
+  void registerModuleData(ModuleData data) {
     if (data == null) {
       _modularStrategy = KernelModularStrategy(_compilerTask, _elementMap);
     } else {
@@ -452,9 +452,10 @@
   final Map<ir.Member, ImpactBuilderData> _cache = {};
 
   DeserializedModularStrategy(
-      this._compilerTask, this._elementMap, List<ModuleData> data) {
-    for (var module in data) {
-      _cache.addAll(module.impactData);
+      this._compilerTask, this._elementMap, ModuleData data) {
+    for (Map<ir.Member, ImpactBuilderData> moduleData
+        in data.impactData.values) {
+      _cache.addAll(moduleData);
     }
   }
 
diff --git a/pkg/compiler/lib/src/options.dart b/pkg/compiler/lib/src/options.dart
index e899bd1..ab584e3 100644
--- a/pkg/compiler/lib/src/options.dart
+++ b/pkg/compiler/lib/src/options.dart
@@ -208,7 +208,7 @@
   Uri? writeModularAnalysisUri;
 
   /// Helper to determine if compiler is being run just for modular analysis.
-  bool get modularMode => writeModularAnalysisUri != null;
+  bool get modularMode => writeModularAnalysisUri != null && !cfeOnly;
 
   List<Uri>? modularAnalysisInputs;
 
diff --git a/pkg/compiler/lib/src/phase/load_kernel.dart b/pkg/compiler/lib/src/phase/load_kernel.dart
index 57d15e7..f654265 100644
--- a/pkg/compiler/lib/src/phase/load_kernel.dart
+++ b/pkg/compiler/lib/src/phase/load_kernel.dart
@@ -18,7 +18,8 @@
 import '../commandline_options.dart';
 import '../common.dart';
 import '../kernel/front_end_adapter.dart';
-import '../kernel/dart2js_target.dart' show Dart2jsTarget;
+import '../kernel/dart2js_target.dart'
+    show Dart2jsTarget, implicitlyUsedLibraries;
 import '../kernel/transformations/clone_mixin_methods_with_super.dart'
     as transformMixins show transformLibraries;
 import '../options.dart';
@@ -164,9 +165,11 @@
   _inferNullSafetyMode(options, isStrongDill);
   _validateNullSafetyMode(options);
 
-  // Modular compiles do not include the platform on the input dill
-  // either.
-  if (options.platformBinaries != null) {
+  // When compiling modularly, a dill for the SDK will be provided. In those
+  // cases we ignore the implicit platform binary.
+  bool platformBinariesIncluded =
+      options.modularMode || options.hasModularAnalysisInputs;
+  if (options.platformBinaries != null && !platformBinariesIncluded) {
     var platformUri = options.platformBinaries
         .resolve(_getPlatformFilename(options, targetName));
     // Modular analysis can be run on the sdk by providing directly the
@@ -331,11 +334,20 @@
 
     search(root);
 
-    // Libraries dependencies do not show implicit imports to `dart:core`.
-    var dartCore = component.libraries.firstWhere((lib) {
-      return lib.importUri.isScheme('dart') && lib.importUri.path == 'core';
-    });
-    search(dartCore);
+    // Libraries dependencies do not show implicit imports to certain internal
+    // libraries.
+    const Set<String> alwaysInclude = {
+      'dart:_internal',
+      'dart:core',
+      'dart:async',
+      ...implicitlyUsedLibraries,
+    };
+    for (String uri in alwaysInclude) {
+      Library library = component.libraries.firstWhere((lib) {
+        return '${lib.importUri}' == uri;
+      });
+      search(library);
+    }
 
     libraries = libraries.where(seen.contains);
   }
diff --git a/pkg/compiler/lib/src/phase/modular_analysis.dart b/pkg/compiler/lib/src/phase/modular_analysis.dart
index 748e97c..a1cf19a 100644
--- a/pkg/compiler/lib/src/phase/modular_analysis.dart
+++ b/pkg/compiler/lib/src/phase/modular_analysis.dart
@@ -53,12 +53,12 @@
   return elementMap;
 }
 
-ModuleData run(Input input) {
-  final options = input.options;
-  final reporter = input.reporter;
-  final elementMap = _createElementMap(
-      options, reporter, input.environment, input.component, input.libraries);
-  final result = <ir.Member, ImpactBuilderData>{};
+Map<ir.Member, ImpactBuilderData> _computeForLibrary(
+    CompilerOptions options,
+    DiagnosticReporter reporter,
+    KernelToElementMap elementMap,
+    ir.Library library) {
+  Map<ir.Member, ImpactBuilderData> result = {};
   void computeForMember(ir.Member member) {
     final scopeModel = ScopeModel.from(member, elementMap.constantEvaluator);
     final annotations = processMemberAnnotations(
@@ -68,12 +68,23 @@
             .impactBuilderData;
   }
 
+  library.members.forEach(computeForMember);
+  for (final cls in library.classes) {
+    cls.members.forEach(computeForMember);
+  }
+  return result;
+}
+
+ModuleData run(Input input) {
+  final options = input.options;
+  final reporter = input.reporter;
+  final elementMap = _createElementMap(
+      options, reporter, input.environment, input.component, input.libraries);
+  Map<Uri, Map<ir.Member, ImpactBuilderData>> result = {};
   for (final library in input.component.libraries) {
     if (!input.moduleLibraries.contains(library.importUri)) continue;
-    library.members.forEach(computeForMember);
-    for (final cls in library.classes) {
-      cls.members.forEach(computeForMember);
-    }
+    result[library.importUri] =
+        _computeForLibrary(options, reporter, elementMap, library);
   }
-  return ModuleData(result);
+  return ModuleData.fromImpactData(result);
 }
diff --git a/pkg/compiler/lib/src/serialization/task.dart b/pkg/compiler/lib/src/serialization/task.dart
index 9c39ef3..6e5f7df 100644
--- a/pkg/compiler/lib/src/serialization/task.dart
+++ b/pkg/compiler/lib/src/serialization/task.dart
@@ -198,7 +198,6 @@
       //   DataSource source = new ObjectSource(encoding, useDataKinds: true);
       //   source.registerComponentLookup(new ComponentLookup(component));
       //   ModuleData.fromDataSource(source);
-
       BytesSink bytes = BytesSink();
       DataSinkWriter binarySink =
           DataSinkWriter(BinaryDataSink(bytes), useDataKinds: true);
@@ -211,17 +210,17 @@
     }
   }
 
-  Future<List<ModuleData>> deserializeModuleData(ir.Component component) async {
+  Future<ModuleData> deserializeModuleData(ir.Component component) async {
     return await measureIoSubtask('deserialize module data', () async {
       _reporter.log('Reading data from ${_options.modularAnalysisInputs}');
-      List<ModuleData> results = [];
+      final results = ModuleData();
       for (Uri uri in _options.modularAnalysisInputs) {
         api.Input<List<int>> dataInput =
             await _provider.readFromUri(uri, inputKind: api.InputKind.binary);
         DataSourceReader source =
             DataSourceReader(BinaryDataSource(dataInput.data));
         source.registerComponentLookup(ComponentLookup(component));
-        results.add(ModuleData.fromDataSource(source));
+        results.readMoreFromDataSource(source);
       }
       return results;
     });
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index 02d707c..c34f02f 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -4212,12 +4212,6 @@
       _handleForeignCreateJsSentinel(invocation);
     } else if (name == 'isJsSentinel') {
       _handleForeignIsJsSentinel(invocation);
-    } else if (name == '_lateReadCheck') {
-      _handleLateReadCheck(invocation);
-    } else if (name == '_lateWriteOnceCheck') {
-      _handleLateWriteOnceCheck(invocation);
-    } else if (name == '_lateInitializeOnceCheck') {
-      _handleLateInitializeOnceCheck(invocation);
     } else {
       reporter.internalError(
           _elementMap.getSpannable(targetElement, invocation),
@@ -4907,14 +4901,6 @@
   }
 
   void _handleForeignCreateJsSentinel(ir.StaticInvocation invocation) {
-    if (_unexpectedForeignArguments(invocation,
-        minPositional: 0, maxPositional: 0, typeArgumentCount: 1)) {
-      stack.add(
-          // Result expected on stack.
-          graph.addConstantNull(closedWorld));
-      return;
-    }
-
     SourceInformation sourceInformation =
         _sourceInformationBuilder.buildCall(invocation, invocation);
     stack.add(graph.addConstantLateSentinel(closedWorld,
@@ -4922,14 +4908,6 @@
   }
 
   void _handleForeignIsJsSentinel(ir.StaticInvocation invocation) {
-    if (_unexpectedForeignArguments(invocation,
-        minPositional: 1, maxPositional: 1)) {
-      stack.add(
-          // Result expected on stack.
-          graph.addConstantNull(closedWorld));
-      return;
-    }
-
     SourceInformation sourceInformation =
         _sourceInformationBuilder.buildCall(invocation, invocation);
     HInstruction checkedExpression =
@@ -4938,72 +4916,6 @@
       ..sourceInformation = sourceInformation);
   }
 
-  // TODO(fishythefish): Support specialization of late sentinels based on type.
-
-  void _handleLateReadCheck(ir.StaticInvocation invocation) {
-    if (_unexpectedForeignArguments(invocation,
-        minPositional: 2, maxPositional: 2, typeArgumentCount: 1)) {
-      stack.add(
-          // Result expected on stack.
-          graph.addConstantNull(closedWorld));
-      return;
-    }
-
-    SourceInformation sourceInformation =
-        _sourceInformationBuilder.buildCall(invocation, invocation);
-
-    List<HInstruction> arguments =
-        _visitPositionalArguments(invocation.arguments);
-    HInstruction value = arguments[0];
-    HInstruction name = options.omitLateNames ? null : arguments[1];
-
-    push(HLateReadCheck(value, name,
-        _abstractValueDomain.excludeLateSentinel(value.instructionType))
-      ..sourceInformation = sourceInformation);
-  }
-
-  void _handleLateWriteOnceCheck(ir.StaticInvocation invocation) {
-    if (_unexpectedForeignArguments(invocation,
-        minPositional: 2, maxPositional: 2)) {
-      stack.add(
-          // Result expected on stack.
-          graph.addConstantNull(closedWorld));
-      return;
-    }
-
-    SourceInformation sourceInformation =
-        _sourceInformationBuilder.buildCall(invocation, invocation);
-
-    List<HInstruction> arguments =
-        _visitPositionalArguments(invocation.arguments);
-    HInstruction value = arguments[0];
-    HInstruction name = options.omitLateNames ? null : arguments[1];
-
-    push(HLateWriteOnceCheck(value, name, _abstractValueDomain.dynamicType)
-      ..sourceInformation = sourceInformation);
-  }
-
-  void _handleLateInitializeOnceCheck(ir.StaticInvocation invocation) {
-    if (_unexpectedForeignArguments(invocation,
-        minPositional: 2, maxPositional: 2)) {
-      stack.add(
-          // Result expected on stack.
-          graph.addConstantNull(closedWorld));
-      return;
-    }
-
-    SourceInformation sourceInformation =
-        _sourceInformationBuilder.buildCall(invocation, invocation);
-
-    List<HInstruction> arguments =
-        _visitPositionalArguments(invocation.arguments);
-    HInstruction value = arguments[0];
-    HInstruction name = options.omitLateNames ? null : arguments[1];
-
-    push(HLateInitializeOnceCheck(value, name, _abstractValueDomain.dynamicType)
-      ..sourceInformation = sourceInformation);
-  }
-
   void _pushStaticInvocation(MemberEntity target, List<HInstruction> arguments,
       AbstractValue typeMask, List<DartType> typeArguments,
       {SourceInformation sourceInformation, InterfaceType instanceType}) {
diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart
index c394f07..dbdf302 100644
--- a/pkg/compiler/lib/src/ssa/codegen.dart
+++ b/pkg/compiler/lib/src/ssa/codegen.dart
@@ -729,8 +729,7 @@
           instruction is HAsCheck ||
           instruction is HAsCheckSimple ||
           instruction is HBoolConversion ||
-          instruction is HNullCheck ||
-          instruction is HLateReadCheck) {
+          instruction is HNullCheck) {
         String inputName = variableNames.getName(instruction.checkedInput);
         if (variableNames.getName(instruction) == inputName) {
           needsAssignment = false;
@@ -3018,94 +3017,6 @@
   }
 
   @override
-  void visitLateReadCheck(HLateReadCheck node) {
-    // We generate code roughly equivalent to invoking:
-    //
-    // T _lateReadCheck<T>(T value, String name) {
-    //   if (isSentinel(value)) throw LateError.fieldNI(name);
-    //   return value;
-    // }
-
-    assert(!node.isRedundant(_closedWorld));
-
-    final sourceInformation = node.sourceInformation;
-
-    _emitIsLateSentinel(node.checkedInput, sourceInformation);
-    final condition = pop();
-
-    if (node.hasName) {
-      use(node.name);
-      _pushCallStatic(
-          _commonElements.throwLateFieldNI, [pop()], sourceInformation);
-    } else {
-      _pushCallStatic(
-          _commonElements.throwUnnamedLateFieldNI, const [], sourceInformation);
-    }
-
-    final lateError =
-        pop().toStatement().withSourceInformation(sourceInformation);
-    pushStatement(js.If.noElse(condition, lateError)
-        .withSourceInformation(sourceInformation));
-  }
-
-  @override
-  void visitLateWriteOnceCheck(HLateWriteOnceCheck node) {
-    // We generate code roughly equivalent to invoking:
-    //
-    // void _lateWriteOnceCheck(Object? value, String name) {
-    //   if (!isSentinel(value)) throw LateError.fieldAI(name);
-    // }
-
-    assert(!node.isRedundant(_closedWorld));
-
-    final sourceInformation = node.sourceInformation;
-    _emitIsLateSentinel(node.checkedInput, sourceInformation, inverse: true);
-    final condition = pop();
-
-    if (node.hasName) {
-      use(node.name);
-      _pushCallStatic(
-          _commonElements.throwLateFieldAI, [pop()], sourceInformation);
-    } else {
-      _pushCallStatic(
-          _commonElements.throwUnnamedLateFieldAI, [], sourceInformation);
-    }
-    final lateError =
-        pop().toStatement().withSourceInformation(sourceInformation);
-    pushStatement(js.If.noElse(condition, lateError)
-        .withSourceInformation(sourceInformation));
-  }
-
-  @override
-  void visitLateInitializeOnceCheck(HLateInitializeOnceCheck node) {
-    // We generate code roughly equivalent to invoking:
-    //
-    // void _lateInitializeOnceCheck(Object? value, String name) {
-    //   if (!isSentinel(value)) throw LateError.fieldADI(name);
-    // }
-
-    assert(!node.isRedundant(_closedWorld));
-
-    final sourceInformation = node.sourceInformation;
-    _emitIsLateSentinel(node.checkedInput, sourceInformation, inverse: true);
-    final condition = pop();
-
-    if (node.hasName) {
-      use(node.name);
-      _pushCallStatic(
-          _commonElements.throwLateFieldADI, [pop()], sourceInformation);
-    } else {
-      _pushCallStatic(
-          _commonElements.throwUnnamedLateFieldADI, [], sourceInformation);
-    }
-
-    final lateError =
-        pop().toStatement().withSourceInformation(sourceInformation);
-    pushStatement(js.If.noElse(condition, lateError)
-        .withSourceInformation(sourceInformation));
-  }
-
-  @override
   void visitTypeKnown(HTypeKnown node) {
     // [HTypeKnown] instructions are removed before generating code.
     assert(false);
@@ -3388,9 +3299,9 @@
         StaticUse.directInvoke(method, selector.callStructure, null));
   }
 
-  _emitIsLateSentinel(HInstruction input, SourceInformation sourceInformation,
+  _emitIsLateSentinel(HIsLateSentinel node, SourceInformation sourceInformation,
       {inverse = false}) {
-    use(input);
+    use(node.inputs[0]);
     js.Expression value = pop();
     js.Expression sentinel =
         _emitter.constantReference(LateSentinelConstantValue());
@@ -3400,5 +3311,5 @@
 
   @override
   visitIsLateSentinel(HIsLateSentinel node) =>
-      _emitIsLateSentinel(node.inputs.single, node.sourceInformation);
+      _emitIsLateSentinel(node, node.sourceInformation);
 }
diff --git a/pkg/compiler/lib/src/ssa/codegen_helpers.dart b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
index ca162ab..600cbac 100644
--- a/pkg/compiler/lib/src/ssa/codegen_helpers.dart
+++ b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
@@ -778,19 +778,6 @@
   }
 
   @override
-  void visitLateReadCheck(HLateReadCheck instruction) {
-    // If the checked value is used, the input might still have one use
-    // (i.e. this HLateReadCheck), but it cannot be generated at use, since we
-    // will rely on non-generate-at-use to assign the value to a variable.
-    //
-    // However, if the checked value is unused then the input may be generated
-    // at use in the check.
-    if (instruction.usedBy.isEmpty) {
-      visitInstruction(instruction);
-    }
-  }
-
-  @override
   void visitTypeKnown(HTypeKnown instruction) {
     // [HTypeKnown] instructions are removed before code generation.
     assert(false);
diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart
index 6e8e464..16ac75c 100644
--- a/pkg/compiler/lib/src/ssa/nodes.dart
+++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -105,9 +105,6 @@
   R visitPrimitiveCheck(HPrimitiveCheck node);
   R visitBoolConversion(HBoolConversion node);
   R visitNullCheck(HNullCheck node);
-  R visitLateReadCheck(HLateReadCheck node);
-  R visitLateWriteOnceCheck(HLateWriteOnceCheck node);
-  R visitLateInitializeOnceCheck(HLateInitializeOnceCheck node);
   R visitTypeKnown(HTypeKnown node);
   R visitYield(HYield node);
 
@@ -594,13 +591,6 @@
   @override
   visitNullCheck(HNullCheck node) => visitCheck(node);
   @override
-  visitLateReadCheck(HLateReadCheck node) => visitCheck(node);
-  @override
-  visitLateWriteOnceCheck(HLateWriteOnceCheck node) => visitCheck(node);
-  @override
-  visitLateInitializeOnceCheck(HLateInitializeOnceCheck node) =>
-      visitCheck(node);
-  @override
   visitPrimitiveCheck(HPrimitiveCheck node) => visitCheck(node);
   @override
   visitTypeKnown(HTypeKnown node) => visitCheck(node);
@@ -1111,10 +1101,6 @@
   static const int STRING_CONCAT_TYPECODE = 59;
   static const int STRINGIFY_TYPECODE = 60;
 
-  static const int LATE_READ_CHECK_TYPECODE = 61;
-  static const int LATE_WRITE_ONCE_CHECK_TYPECODE = 62;
-  static const int LATE_INITIALIZE_ONCE_CHECK_TYPECODE = 63;
-
   HInstruction(this.inputs, this.instructionType) {
     assert(inputs.every((e) => e != null), "inputs: $inputs");
   }
@@ -3691,121 +3677,6 @@
   }
 }
 
-/// A check for a late sentinel to determine if a late field may be read from or
-/// written to.
-abstract class HLateCheck extends HCheck {
-  final HInstruction name;
-
-  HLateCheck(HInstruction input, this.name, AbstractValue type)
-      : super([input, if (name != null) name], type);
-
-  bool get hasName => name != null;
-
-  @override
-  bool isControlFlow() => true;
-
-  @override
-  bool isCodeMotionInvariant() => false;
-}
-
-/// A check that a late field has been initialized and can therefore be read.
-class HLateReadCheck extends HLateCheck {
-  HLateReadCheck(HInstruction input, HInstruction name, AbstractValue type)
-      : super(input, name, type);
-
-  @override
-  accept(HVisitor visitor) => visitor.visitLateReadCheck(this);
-
-  @override
-  int typeCode() => HInstruction.LATE_READ_CHECK_TYPECODE;
-
-  @override
-  bool typeEquals(HInstruction other) => other is HLateReadCheck;
-
-  @override
-  bool dataEquals(HLateReadCheck other) => true;
-
-  bool isRedundant(JClosedWorld closedWorld) {
-    AbstractValueDomain abstractValueDomain = closedWorld.abstractValueDomain;
-    AbstractValue inputType = checkedInput.instructionType;
-    return abstractValueDomain.isLateSentinel(inputType).isDefinitelyFalse;
-  }
-
-  @override
-  String toString() {
-    return 'HLateReadCheck($checkedInput)';
-  }
-}
-
-/// A check that a late final field has not been initialized yet and can
-/// therefore be written to.
-///
-/// The difference between [HLateWriteOnceCheck] and [HLateInitializeOnceCheck]
-/// is that the latter occurs on writes performed as part of the initializer
-/// expression.
-class HLateWriteOnceCheck extends HLateCheck {
-  HLateWriteOnceCheck(HInstruction input, HInstruction name, AbstractValue type)
-      : super(input, name, type);
-
-  @override
-  accept(HVisitor visitor) => visitor.visitLateWriteOnceCheck(this);
-
-  @override
-  int typeCode() => HInstruction.LATE_WRITE_ONCE_CHECK_TYPECODE;
-
-  @override
-  bool typeEquals(HInstruction other) => other is HLateWriteOnceCheck;
-
-  @override
-  bool dataEquals(HLateWriteOnceCheck other) => true;
-
-  bool isRedundant(JClosedWorld closedWorld) {
-    AbstractValueDomain abstractValueDomain = closedWorld.abstractValueDomain;
-    AbstractValue inputType = checkedInput.instructionType;
-    return abstractValueDomain.isLateSentinel(inputType).isDefinitelyTrue;
-  }
-
-  @override
-  String toString() {
-    return 'HLateWriteOnceCheck($checkedInput)';
-  }
-}
-
-/// A check that a late final field has not been initialized yet and can
-/// therefore be initialized.
-///
-/// The difference between [HLateWriteOnceCheck] and [HLateInitializeOnceCheck]
-/// is that the latter occurs on writes performed as part of the initializer
-/// expression.
-class HLateInitializeOnceCheck extends HLateCheck {
-  HLateInitializeOnceCheck(
-      HInstruction input, HInstruction name, AbstractValue type)
-      : super(input, name, type);
-
-  @override
-  accept(HVisitor visitor) => visitor.visitLateInitializeOnceCheck(this);
-
-  @override
-  int typeCode() => HInstruction.LATE_INITIALIZE_ONCE_CHECK_TYPECODE;
-
-  @override
-  bool typeEquals(HInstruction other) => other is HLateInitializeOnceCheck;
-
-  @override
-  bool dataEquals(HLateInitializeOnceCheck other) => true;
-
-  bool isRedundant(JClosedWorld closedWorld) {
-    AbstractValueDomain abstractValueDomain = closedWorld.abstractValueDomain;
-    AbstractValue inputType = checkedInput.instructionType;
-    return abstractValueDomain.isLateSentinel(inputType).isDefinitelyTrue;
-  }
-
-  @override
-  String toString() {
-    return 'HLateInitializeOnceCheck($checkedInput)';
-  }
-}
-
 /// The [HTypeKnown] instruction marks a value with a refined type.
 class HTypeKnown extends HCheck {
   AbstractValue knownType;
diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart
index 75f9512..81af4d3 100644
--- a/pkg/compiler/lib/src/ssa/optimize.dart
+++ b/pkg/compiler/lib/src/ssa/optimize.dart
@@ -1399,24 +1399,6 @@
   }
 
   @override
-  HInstruction visitLateReadCheck(HLateReadCheck node) {
-    if (node.isRedundant(_closedWorld)) return node.checkedInput;
-    return node;
-  }
-
-  @override
-  HInstruction visitLateWriteOnceCheck(HLateWriteOnceCheck node) {
-    if (node.isRedundant(_closedWorld)) return node.checkedInput;
-    return node;
-  }
-
-  @override
-  HInstruction visitLateInitializeOnceCheck(HLateInitializeOnceCheck node) {
-    if (node.isRedundant(_closedWorld)) return node.checkedInput;
-    return node;
-  }
-
-  @override
   HInstruction visitTypeKnown(HTypeKnown node) {
     return node.isRedundant(_closedWorld) ? node.checkedInput : node;
   }
@@ -3789,8 +3771,6 @@
   @override
   void visitNullCheck(HNullCheck instruction) {}
   @override
-  void visitLateReadCheck(HLateReadCheck instruction) {}
-  @override
   void visitParameterValue(HParameterValue instruction) {}
   @override
   void visitRelational(HRelational instruction) {}
diff --git a/pkg/compiler/lib/src/ssa/ssa_tracer.dart b/pkg/compiler/lib/src/ssa/ssa_tracer.dart
index 98f96f1..a47bd34 100644
--- a/pkg/compiler/lib/src/ssa/ssa_tracer.dart
+++ b/pkg/compiler/lib/src/ssa/ssa_tracer.dart
@@ -668,27 +668,6 @@
   }
 
   @override
-  String visitLateReadCheck(HLateReadCheck node) {
-    String checkedInput = temporaryId(node.checkedInput);
-    String comment = node.hasName ? "(${temporaryId(node.name)})" : "";
-    return "LateReadCheck$comment: $checkedInput";
-  }
-
-  @override
-  String visitLateWriteOnceCheck(HLateWriteOnceCheck node) {
-    String checkedInput = temporaryId(node.checkedInput);
-    String comment = node.hasName ? "(${temporaryId(node.name)})" : "";
-    return "LateWriteOnceCheck$comment: $checkedInput";
-  }
-
-  @override
-  String visitLateInitializeOnceCheck(HLateInitializeOnceCheck node) {
-    String checkedInput = temporaryId(node.checkedInput);
-    String comment = node.hasName ? "(${temporaryId(node.name)})" : "";
-    return "LateInitializeOnceCheck$comment: $checkedInput";
-  }
-
-  @override
   String visitTypeKnown(HTypeKnown node) {
     assert(node.inputs.length <= 2);
     String result =
diff --git a/pkg/compiler/lib/src/ssa/types_propagation.dart b/pkg/compiler/lib/src/ssa/types_propagation.dart
index 1ddc6f2..082183b 100644
--- a/pkg/compiler/lib/src/ssa/types_propagation.dart
+++ b/pkg/compiler/lib/src/ssa/types_propagation.dart
@@ -449,20 +449,6 @@
   }
 
   @override
-  AbstractValue visitLateReadCheck(HLateReadCheck instruction) {
-    HInstruction input = instruction.checkedInput;
-    AbstractValue inputType = input.instructionType;
-    AbstractValue outputType =
-        abstractValueDomain.excludeLateSentinel(inputType);
-    if (inputType != outputType) {
-      // Replace dominated uses of input with uses of this check so the uses
-      // benefit from the stronger type.
-      input.replaceAllUsersDominatedBy(instruction.next, instruction);
-    }
-    return outputType;
-  }
-
-  @override
   AbstractValue visitAsCheck(HAsCheck instruction) {
     return _narrowAsCheck(instruction, instruction.checkedInput,
         instruction.checkedType.abstractValue);
diff --git a/pkg/compiler/test/codegen/late_field_redundancy_test.dart b/pkg/compiler/test/codegen/late_field_redundancy_test.dart
deleted file mode 100644
index a0ebf05..0000000
--- a/pkg/compiler/test/codegen/late_field_redundancy_test.dart
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-// @dart = 2.10
-
-import 'package:async_helper/async_helper.dart';
-import 'package:expect/expect.dart';
-import '../helpers/compiler_helper.dart';
-
-const String TEST = r"""
-class Foo {
-  late int x;
-}
-
-void entry() {
-  final foo = Foo();
-  foo.x = 42;
-  test(foo);
-}
-
-@pragma('dart2js:noInline')
-void test(Foo foo) {
-  final a = foo.x;
-  final b = foo.x;
-  print([a, b]);
-}
-""";
-
-void main() {
-  asyncTest(() async {
-    await compile(TEST,
-        entry: 'entry',
-        methodName: 'test',
-        disableTypeInference: false,
-        disableInlining: false,
-        soundNullSafety: true, check: (String generated) {
-      RegExp regexp = new RegExp(r'=== \$');
-      Expect.equals(1, regexp.allMatches(generated).length);
-    });
-  });
-}
diff --git a/pkg/compiler/test/codegen/late_field_test.dart b/pkg/compiler/test/codegen/late_field_test.dart
deleted file mode 100644
index fa34ffe..0000000
--- a/pkg/compiler/test/codegen/late_field_test.dart
+++ /dev/null
@@ -1,60 +0,0 @@
-// 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.10
-
-import 'dart:async';
-import 'package:async_helper/async_helper.dart';
-import '../helpers/compiler_helper.dart';
-
-const String TEST_DIRECT = r"""
-class Foo {
-  late int x;
-}
-
-int test() {
-  final foo = Foo();
-  foo.x = 40;
-  return foo.x + 2;
-  // present: '42'
-  // absent: '+ 2'
-  // absent: 'add'
-}
-""";
-
-const String TEST_INDIRECT = r"""
-class Foo {
-  late int x;
-}
-
-int entry() {
-  final foo = Foo();
-  foo.x = 40;
-  return test(foo);
-}
-
-@pragma('dart2js:noInline')
-int test(Foo foo) {
-  return foo.x + 2;
-  // present: '+ 2'
-  // absent: 'add'
-}
-""";
-
-Future check(String test, {String entry: 'test'}) {
-  return compile(test,
-      entry: entry,
-      methodName: 'test',
-      check: checkerForAbsentPresent(test),
-      disableTypeInference: false,
-      disableInlining: false,
-      soundNullSafety: true);
-}
-
-void main() {
-  asyncTest(() async {
-    await check(TEST_DIRECT);
-    await check(TEST_INDIRECT, entry: 'entry');
-  });
-}
diff --git a/pkg/compiler/test/codegen/late_test.dart b/pkg/compiler/test/codegen/late_test.dart
new file mode 100644
index 0000000..af820fb
--- /dev/null
+++ b/pkg/compiler/test/codegen/late_test.dart
@@ -0,0 +1,38 @@
+// 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.10
+
+import 'dart:async';
+import 'package:async_helper/async_helper.dart';
+import '../helpers/compiler_helper.dart';
+
+const String TEST = r"""
+class Foo {
+  late int x;
+}
+
+int foo() {
+  final foo = Foo();
+  foo.x = 40;
+  return foo.x + 2;
+  // present: '+ 2'
+  // absent: 'add'
+}
+""";
+
+Future check(String test) {
+  return compile(test,
+      entry: 'foo',
+      check: checkerForAbsentPresent(test),
+      disableTypeInference: false,
+      disableInlining: false,
+      soundNullSafety: true);
+}
+
+void main() {
+  asyncTest(() async {
+    await check(TEST);
+  });
+}
diff --git a/pkg/compiler/test/dump_info/data/marker.options b/pkg/compiler/test/dump_info/data/marker.options
new file mode 100644
index 0000000..620f7d3
--- /dev/null
+++ b/pkg/compiler/test/dump_info/data/marker.options
@@ -0,0 +1,2 @@
+spec=pkg/compiler/test/dump_info/dump_info_test.dart
+
diff --git a/pkg/compiler/test/dump_info/data/members.dart b/pkg/compiler/test/dump_info/data/members.dart
new file mode 100644
index 0000000..11d6982
--- /dev/null
+++ b/pkg/compiler/test/dump_info/data/members.dart
@@ -0,0 +1,245 @@
+class C {
+  /*member: C.value:function=[{
+    "id": "field/memory:sdk/tests/web/native/main.dart::C.value",
+    "kind": "field",
+    "name": "value",
+    "size": 0,
+    "outputUnit": "outputUnit/main",
+    "parent": "class/memory:sdk/tests/web/native/main.dart::C",
+    "children": [],
+    "inferredType": "[exact=Error]",
+    "code": "",
+    "type": "dynamic"
+}]*/
+  final value;
+  /*member: C.counter:function=[{
+    "id": "field/memory:sdk/tests/web/native/main.dart::C.counter",
+    "kind": "field",
+    "name": "counter",
+    "size": 18,
+    "outputUnit": "outputUnit/main",
+    "parent": "class/memory:sdk/tests/web/native/main.dart::C",
+    "children": [],
+    "inferredType": "[subclass=JSPositiveInt]",
+    "code": "$.C_counter = 0;\n",
+    "type": "int"
+}]*/
+  static int counter = 0;
+  /*member: C.y:function=[{
+    "id": "field/memory:sdk/tests/web/native/main.dart::C.y",
+    "kind": "field",
+    "name": "y",
+    "size": 124,
+    "outputUnit": "outputUnit/main",
+    "parent": "class/memory:sdk/tests/web/native/main.dart::C",
+    "children": [],
+    "inferredType": "[null|exact=JSBool]",
+    "code": "_lazy($, \"C_y\", \"$get$C_y\", () => {\n      var t1 = $.C_counter + 1;\n      $.C_counter = t1;\n      return t1 === 4;\n    });\n",
+    "type": "bool"
+}]*/
+  static bool y = C.compute();
+  /*member: C.compute:function=[{
+    "id": "function/memory:sdk/tests/web/native/main.dart::C.compute",
+    "kind": "function",
+    "name": "compute",
+    "size": 0,
+    "outputUnit": "outputUnit/main",
+    "parent": "class/memory:sdk/tests/web/native/main.dart::C",
+    "children": [],
+    "modifiers": {
+        "static": true,
+        "const": false,
+        "factory": false,
+        "external": false
+    },
+    "returnType": "bool",
+    "inferredReturnType": "[exact=JSBool]",
+    "parameters": [],
+    "sideEffects": "SideEffects(reads static; writes static)",
+    "inlinedCount": 1,
+    "code": "",
+    "type": "bool Function()"
+}]*/
+  static bool compute() {
+    C.counter += 1;
+    return counter == 4;
+  }
+
+  /*member: C._default:function=[{
+    "id": "function/memory:sdk/tests/web/native/main.dart::C.C._default",
+    "kind": "function",
+    "name": "C._default",
+    "size": 0,
+    "outputUnit": "outputUnit/main",
+    "parent": "class/memory:sdk/tests/web/native/main.dart::C",
+    "children": [],
+    "modifiers": {
+        "static": false,
+        "const": false,
+        "factory": false,
+        "external": false
+    },
+    "returnType": "dynamic",
+    "inferredReturnType": "[exact=C]",
+    "parameters": [
+        {
+            "name": "message",
+            "type": "[exact=Error]",
+            "declaredType": "Object"
+        }
+    ],
+    "sideEffects": "SideEffects(reads nothing; writes nothing)",
+    "inlinedCount": 1,
+    "code": "",
+    "type": "dynamic Function(Object)"
+}]*/
+  C._default(Object message) : value = message;
+
+  /*member: C.create:function=[{
+    "id": "function/memory:sdk/tests/web/native/main.dart::C.C.create",
+    "kind": "function",
+    "name": "C.create",
+    "size": 0,
+    "outputUnit": "outputUnit/main",
+    "parent": "class/memory:sdk/tests/web/native/main.dart::C",
+    "children": [],
+    "modifiers": {
+        "static": false,
+        "const": false,
+        "factory": true,
+        "external": false
+    },
+    "returnType": "C",
+    "inferredReturnType": "[exact=C]",
+    "parameters": [
+        {
+            "name": "object",
+            "type": "[exact=JSUInt31]",
+            "declaredType": "dynamic"
+        }
+    ],
+    "sideEffects": "SideEffects(reads nothing; writes nothing)",
+    "inlinedCount": 1,
+    "code": "",
+    "type": "C Function(dynamic)"
+}]*/
+  factory C.create(object) {
+    return C._default(Error());
+  }
+}
+
+/*member: F:function=[{
+    "id": "function/memory:sdk/tests/web/native/main.dart::F",
+    "kind": "function",
+    "name": "F",
+    "size": 52,
+    "outputUnit": "outputUnit/main",
+    "parent": "library/memory:sdk/tests/web/native/main.dart::",
+    "children": [],
+    "modifiers": {
+        "static": false,
+        "const": false,
+        "factory": false,
+        "external": false
+    },
+    "returnType": "void",
+    "inferredReturnType": "[null]",
+    "parameters": [],
+    "sideEffects": "SideEffects(reads nothing; writes nothing)",
+    "inlinedCount": 0,
+    "code": "F() {\n    }\n_static_0(A, \"main__F$closure\", \"F\", 0);\n",
+    "type": "void Function()"
+}]*/
+void F() {}
+
+class B {
+  static void M() {}
+  static const int a = 2123;
+}
+
+class A {
+  /*member: A.a:function=[{
+    "id": "field/memory:sdk/tests/web/native/main.dart::A.a",
+    "kind": "field",
+    "name": "a",
+    "size": 0,
+    "outputUnit": "outputUnit/main",
+    "parent": "class/memory:sdk/tests/web/native/main.dart::A",
+    "children": [],
+    "inferredType": "Value([exact=JSString], value: \"hello\")",
+    "code": "",
+    "type": "dynamic"
+}]*/
+  final a;
+
+/*member: A.:function=[{
+    "id": "function/memory:sdk/tests/web/native/main.dart::A.A",
+    "kind": "function",
+    "name": "A",
+    "size": 0,
+    "outputUnit": "outputUnit/main",
+    "parent": "class/memory:sdk/tests/web/native/main.dart::A",
+    "children": [],
+    "modifiers": {
+        "static": false,
+        "const": true,
+        "factory": false,
+        "external": false
+    },
+    "returnType": "dynamic",
+    "inferredReturnType": "[exact=A]",
+    "parameters": [],
+    "sideEffects": "SideEffects(reads nothing; writes nothing)",
+    "inlinedCount": 1,
+    "code": "",
+    "type": "dynamic Function()"
+}]*/
+  const A() : a = "hello";
+}
+
+/*member: constList:function=[{
+    "id": "field/memory:sdk/tests/web/native/main.dart::constList",
+    "kind": "field",
+    "name": "constList",
+    "size": 0,
+    "outputUnit": "outputUnit/main",
+    "parent": "library/memory:sdk/tests/web/native/main.dart::",
+    "children": [],
+    "inferredType": "Container([exact=JSUnmodifiableArray], element: [exact=A], length: 1)",
+    "code": "",
+    "type": "List<A>"
+}]*/
+final constList = const [
+  const A(),
+];
+
+/*member: main:function=[{
+    "id": "function/memory:sdk/tests/web/native/main.dart::main",
+    "kind": "function",
+    "name": "main",
+    "size": 199,
+    "outputUnit": "outputUnit/main",
+    "parent": "library/memory:sdk/tests/web/native/main.dart::",
+    "children": [],
+    "modifiers": {
+        "static": false,
+        "const": false,
+        "factory": false,
+        "external": false
+    },
+    "returnType": "dynamic",
+    "inferredReturnType": "[null]",
+    "parameters": [],
+    "sideEffects": "SideEffects(reads anything; writes anything)",
+    "inlinedCount": 0,
+    "code": "main() {\n      null.add$1(0, [B.List_A, B.C_A, 2123, 2133, A.main__F$closure(), $.$get$C_y(), \"hello\"]);\n      null.add$1(0, B.C_A);\n      null.add$1(0, new A.C());\n      A.printString(\"null\");\n    }",
+    "type": "dynamic Function()"
+}]*/
+main() {
+  dynamic l = [constList, const A(), B.a, B.a + 10, F, C.y, A().a];
+  dynamic r;
+  r.add(l);
+  r.add(const A());
+  r.add(C.create(10));
+  print(r);
+}
diff --git a/pkg/compiler/test/dump_info/dump_info_test.dart b/pkg/compiler/test/dump_info/dump_info_test.dart
new file mode 100644
index 0000000..8410f9b
--- /dev/null
+++ b/pkg/compiler/test/dump_info/dump_info_test.dart
@@ -0,0 +1,189 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart = 2.7
+
+import 'dart:convert';
+import 'dart:io';
+import 'package:_fe_analyzer_shared/src/testing/features.dart';
+import 'package:async_helper/async_helper.dart';
+import 'package:compiler/src/compiler.dart';
+import 'package:compiler/src/dump_info.dart';
+import 'package:compiler/src/elements/entities.dart';
+import 'package:compiler/src/js_model/element_map.dart';
+import 'package:compiler/src/js_model/js_world.dart';
+import 'package:dart2js_info/info.dart' as info;
+import 'package:dart2js_info/json_info_codec.dart' as info;
+import 'package:kernel/ast.dart' as ir;
+import '../equivalence/id_equivalence.dart';
+import '../equivalence/id_equivalence_helper.dart';
+
+main(List<String> args) {
+  asyncTest(() async {
+    Directory dataDir = new Directory.fromUri(Platform.script.resolve('data'));
+    print('Testing output of dump-info');
+    print('==================================================================');
+    await checkTests(dataDir, const DumpInfoDataComputer(),
+        args: args, testedConfigs: allSpecConfigs, options: ['--dump-info']);
+  });
+}
+
+class Tags {
+  static const String library = 'library';
+  static const String clazz = 'class';
+  static const String classType = 'classType';
+  static const String function = 'function';
+  static const String typeDef = 'typedef';
+  static const String field = 'field';
+  static const String constant = 'constant';
+  static const String holding = 'holding';
+  static const String dependencies = 'dependencies';
+  static const String outputUnits = 'outputUnits';
+  static const String deferredFiles = 'deferredFiles';
+}
+
+class DumpInfoDataComputer extends DataComputer<Features> {
+  const DumpInfoDataComputer();
+
+  static const String wildcard = '%';
+
+  @override
+  void computeMemberData(Compiler compiler, MemberEntity member,
+      Map<Id, ActualData<Features>> actualMap,
+      {bool verbose: false}) {
+    JsonEncoder encoder = const JsonEncoder.withIndent('    ');
+    var converter = info.AllInfoToJsonConverter(
+        isBackwardCompatible: true, filterTreeshaken: false);
+    DumpInfoStateData dumpInfoState = compiler.dumpInfoStateForTesting;
+
+    Features features = new Features();
+    var functionInfo = dumpInfoState.entityToInfo[member];
+    if (functionInfo == null) return;
+
+    if (functionInfo is info.FunctionInfo) {
+      features.addElement(
+          Tags.function, encoder.convert(functionInfo.accept(converter)));
+    }
+
+    if (functionInfo is info.FieldInfo) {
+      features.addElement(
+          Tags.function, encoder.convert(functionInfo.accept(converter)));
+    }
+
+    JsClosedWorld closedWorld = compiler.backendClosedWorldForTesting;
+    JsToElementMap elementMap = closedWorld.elementMap;
+    ir.Member node = elementMap.getMemberDefinition(member).node;
+    Id id = computeMemberId(node);
+    ir.TreeNode nodeWithOffset = computeTreeNodeWithOffset(node);
+    actualMap[id] = new ActualData<Features>(id, features,
+        nodeWithOffset?.location?.file, nodeWithOffset?.fileOffset, member);
+  }
+
+  @override
+  DataInterpreter<Features> get dataValidator =>
+      const JsonFeaturesDataInterpreter(wildcard: wildcard);
+}
+
+/// Feature interpreter for Features with Json values.
+///
+/// The data annotation reader removes whitespace, but this fork adds them
+/// back for readability.
+class JsonFeaturesDataInterpreter implements DataInterpreter<Features> {
+  final String wildcard;
+
+  const JsonFeaturesDataInterpreter({this.wildcard});
+
+  @override
+  String isAsExpected(Features actualFeatures, String expectedData) {
+    JsonEncoder encoder = const JsonEncoder.withIndent('    ');
+
+    if (wildcard != null && expectedData == wildcard) {
+      return null;
+    } else if (expectedData == '') {
+      return actualFeatures.isNotEmpty ? "Expected empty data." : null;
+    } else {
+      List<String> errorsFound = [];
+      Features expectedFeatures = Features.fromText(expectedData);
+      Set<String> validatedFeatures = new Set<String>();
+      expectedFeatures.forEach((String key, Object expectedValue) {
+        validatedFeatures.add(key);
+        Object actualValue = actualFeatures[key];
+        if (!actualFeatures.containsKey(key)) {
+          errorsFound.add('No data found for $key');
+        } else if (expectedValue == '') {
+          if (actualValue != '') {
+            errorsFound.add('Non-empty data found for $key');
+          }
+        } else if (wildcard != null && expectedValue == wildcard) {
+          return;
+        } else if (expectedValue is List) {
+          if (actualValue is List) {
+            List actualList = actualValue.toList();
+            for (Object expectedObject in expectedValue) {
+              String expectedText =
+                  encoder.convert(jsonDecode('$expectedObject'));
+              bool matchFound = false;
+              if (wildcard != null && expectedText.endsWith(wildcard)) {
+                // Wildcard matcher.
+                String prefix =
+                    expectedText.substring(0, expectedText.indexOf(wildcard));
+                List matches = [];
+                for (Object actualObject in actualList) {
+                  if ('$actualObject'.startsWith(prefix)) {
+                    matches.add(actualObject);
+                    matchFound = true;
+                  }
+                }
+                for (Object match in matches) {
+                  actualList.remove(match);
+                }
+              } else {
+                for (Object actualObject in actualList) {
+                  if (expectedText == '$actualObject') {
+                    actualList.remove(actualObject);
+                    matchFound = true;
+                    break;
+                  }
+                }
+              }
+              if (!matchFound) {
+                errorsFound.add("No match found for $key=[$expectedText]");
+              }
+            }
+            if (actualList.isNotEmpty) {
+              errorsFound
+                  .add("Extra data found $key=[${actualList.join(',')}]");
+            }
+          } else {
+            errorsFound.add("List data expected for $key: "
+                "expected '$expectedValue', found '${actualValue}'");
+          }
+        } else if (expectedValue != actualValue) {
+          errorsFound.add("Mismatch for $key: expected '$expectedValue', "
+              "found '${actualValue}'");
+        }
+      });
+      actualFeatures.forEach((String key, Object value) {
+        if (!validatedFeatures.contains(key)) {
+          if (value == '') {
+            errorsFound.add("Extra data found '$key'");
+          } else {
+            errorsFound.add("Extra data found $key=$value");
+          }
+        }
+      });
+      return errorsFound.isNotEmpty ? errorsFound.join('\n ') : null;
+    }
+  }
+
+  @override
+  String getText(Features actualData, [String indentation]) {
+    return actualData.getText(indentation);
+  }
+
+  @override
+  bool isEmpty(Features actualData) {
+    return actualData == null || actualData.isEmpty;
+  }
+}
diff --git a/pkg/compiler/tool/modular_test_suite.dart b/pkg/compiler/tool/modular_test_suite.dart
index 430c81a..491d6ad 100644
--- a/pkg/compiler/tool/modular_test_suite.dart
+++ b/pkg/compiler/tool/modular_test_suite.dart
@@ -26,9 +26,7 @@
           FullDillCompilationStep(onlyOnSdk: true),
           ModularAnalysisStep(onlyOnSdk: true),
           ModularAnalysisStep(),
-          // TODO(joshualitt): Re-enable ConcatenateDillStep after it works
-          // correctly alongside modular analysis.
-          // ConcatenateDillsStep(useModularAnalysis: true),
+          ConcatenateDillsStep(useModularAnalysis: true),
           ComputeClosedWorldStep(useModularAnalysis: true),
           GlobalAnalysisStep(),
           Dart2jsCodegenStep(codeId0),
diff --git a/pkg/compiler/tool/modular_test_suite_helper.dart b/pkg/compiler/tool/modular_test_suite_helper.dart
index 91525b6..ac95cce 100644
--- a/pkg/compiler/tool/modular_test_suite_helper.dart
+++ b/pkg/compiler/tool/modular_test_suite_helper.dart
@@ -32,6 +32,7 @@
 const fullDillId = DataId("concatenate.dill");
 const modularUpdatedDillId = DataId("modular.dill");
 const modularDataId = DataId("modular.data");
+const modularFullDataId = DataId("concatenate.modular.data");
 const closedWorldId = DataId("world");
 const globalUpdatedDillId = DataId("global.dill");
 const globalDataId = DataId("global.data");
@@ -304,10 +305,16 @@
 
   DataId get idForDill => useModularAnalysis ? modularUpdatedDillId : dillId;
 
-  List<DataId> get dependencies => [idForDill];
+  List<DataId> get dependencies => [
+        idForDill,
+        if (useModularAnalysis) modularDataId,
+      ];
 
   @override
-  List<DataId> get resultData => const [fullDillId];
+  List<DataId> get resultData => [
+        fullDillId,
+        if (useModularAnalysis) modularFullDataId,
+      ];
 
   @override
   bool get needsSources => false;
@@ -344,6 +351,10 @@
       '${Flags.inputDill}=${toUri(module, dillId)}',
       for (String flag in flags) '--enable-experiment=$flag',
       '${Flags.dillDependencies}=${dillDependencies.join(',')}',
+      if (useModularAnalysis) ...[
+        '${Flags.readModularAnalysis}=${dataDependencies.join(',')}',
+        '${Flags.writeModularAnalysis}=${toUri(module, modularFullDataId)}',
+      ],
       '${Flags.cfeOnly}',
       '--out=${toUri(module, fullDillId)}',
     ];
@@ -364,14 +375,11 @@
 class ComputeClosedWorldStep extends IOModularStep {
   final bool useModularAnalysis;
 
-  DataId get idForDill =>
-      useModularAnalysis ? modularUpdatedDillId : fullDillId;
-
   ComputeClosedWorldStep({this.useModularAnalysis});
 
   List<DataId> get dependencies => [
-        idForDill,
-        if (useModularAnalysis) modularDataId,
+        fullDillId,
+        if (useModularAnalysis) modularFullDataId,
       ];
 
   @override
@@ -394,25 +402,16 @@
       List<String> flags) async {
     if (_options.verbose)
       print("\nstep: dart2js compute closed world on $module");
-    Set<Module> transitiveDependencies = computeTransitiveDependencies(module);
-    Iterable<String> dillDependencies =
-        transitiveDependencies.map((m) => '${toUri(m, idForDill)}');
-    List<String> dataDependencies = transitiveDependencies
-        .map((m) => '${toUri(m, modularDataId)}')
-        .toList();
-    dataDependencies.add('${toUri(module, modularDataId)}');
     List<String> args = [
       '--packages=${sdkRoot.toFilePath()}/$packageConfigJsonPath',
       _dart2jsScript,
       // TODO(sigmund): remove this dependency on libraries.json
       if (_options.useSdk) '--libraries-spec=$_librarySpecForSnapshot',
       '${Flags.entryUri}=$fakeRoot${module.mainSource}',
-      '${Flags.inputDill}=${toUri(module, idForDill)}',
+      '${Flags.inputDill}=${toUri(module, fullDillId)}',
       for (String flag in flags) '--enable-experiment=$flag',
-      if (useModularAnalysis) ...[
-        '${Flags.dillDependencies}=${dillDependencies.join(',')}',
-        '${Flags.readModularAnalysis}=${dataDependencies.join(',')}',
-      ],
+      if (useModularAnalysis)
+        '${Flags.readModularAnalysis}=${toUri(module, modularFullDataId)}',
       '${Flags.writeClosedWorld}=${toUri(module, closedWorldId)}',
       Flags.noClosedWorldInData,
       '--out=${toUri(module, globalUpdatedDillId)}',
diff --git a/pkg/front_end/lib/src/fasta/kernel/macro/identifiers.dart b/pkg/front_end/lib/src/fasta/kernel/macro/identifiers.dart
index 8ca4ce2..1ecd8aa 100644
--- a/pkg/front_end/lib/src/fasta/kernel/macro/identifiers.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/macro/identifiers.dart
@@ -141,14 +141,14 @@
     Uri? uri;
     String? staticScope;
     macro.IdentifierKind kind;
-    if (memberBuilder.isStatic || memberBuilder.isConstructor) {
+    if (memberBuilder.isTopLevel) {
+      uri = memberBuilder.libraryBuilder.importUri;
+      kind = macro.IdentifierKind.topLevelMember;
+    } else if (memberBuilder.isStatic || memberBuilder.isConstructor) {
       ClassBuilder classBuilder = memberBuilder.classBuilder!;
       staticScope = classBuilder.name;
       uri = classBuilder.libraryBuilder.importUri;
       kind = macro.IdentifierKind.staticInstanceMember;
-    } else if (memberBuilder.isTopLevel) {
-      uri = memberBuilder.libraryBuilder.importUri;
-      kind = macro.IdentifierKind.topLevelMember;
     } else {
       kind = macro.IdentifierKind.instanceMember;
     }
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
index 72cd04e..3ec34d7 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
@@ -3595,7 +3595,9 @@
 
   Expression _hoist(Expression expression, DartType type,
       List<VariableDeclaration>? hoistedExpressions) {
-    if (hoistedExpressions != null && expression is! ThisExpression) {
+    if (hoistedExpressions != null &&
+        expression is! ThisExpression &&
+        expression is! FunctionExpression) {
       VariableDeclaration variable = createVariable(expression, type);
       hoistedExpressions.add(variable);
       return createVariableGet(variable);
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index 3d82ad2..dae8e71 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -577,6 +577,10 @@
 JsInteropStaticInteropWithInstanceMembers/example: Fail # Web compiler specific
 JsInteropStaticInteropWithNonStaticSupertype/analyzerCode: Fail # Web compiler specific
 JsInteropStaticInteropWithNonStaticSupertype/example: Fail # Web compiler specific
+JsInteropStaticInteropTrustTypesUsageNotAllowed/analyzerCode: Fail # Web compiler specific
+JsInteropStaticInteropTrustTypesUsageNotAllowed/example: Fail # Web compiler specific
+JsInteropStaticInteropTrustTypesUsedWithoutStaticInterop/analyzerCode: Fail # Web compiler specific
+JsInteropStaticInteropTrustTypesUsedWithoutStaticInterop/example: Fail # Web compiler specific
 LanguageVersionInvalidInDotPackages/analyzerCode: Fail
 LanguageVersionMismatchInPart/analyzerCode: Fail
 LanguageVersionMismatchInPart/part_wrapped_script: Fail # Part in (now) part.
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index 2e1aa57..887e231 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -5197,6 +5197,14 @@
   problemMessage: "JS interop class '#name' has an `@staticInterop` annotation, but has supertype '#name2', which is non-static."
   correctionMessage: "Try marking the supertype as a static interop class using `@staticInterop`."
 
+JsInteropStaticInteropTrustTypesUsedWithoutStaticInterop:
+  problemMessage: "JS interop class '#name' has an `@trustTypes` annotation, but no `@staticInterop` annotation."
+  correctionMessage: "Try marking the class using `@staticInterop`."
+
+JsInteropStaticInteropTrustTypesUsageNotAllowed:
+  problemMessage: "JS interop class '#name' has an `@trustTypes` annotation, but `@trustTypes` is only supported within the sdk."
+  correctionMessage: "Try removing the `@trustTypes` annotation."
+
 DefaultListConstructorError:
   problemMessage: "Can't use the default List constructor."
   correctionMessage: "Try using List.filled instead."
diff --git a/pkg/front_end/test/spell_checking_list_messages.txt b/pkg/front_end/test/spell_checking_list_messages.txt
index 329dc17..00b69d3 100644
--- a/pkg/front_end/test/spell_checking_list_messages.txt
+++ b/pkg/front_end/test/spell_checking_list_messages.txt
@@ -87,6 +87,7 @@
 team
 this.namedconstructor
 this.x
+trusttypes
 type3.#name
 u
 unsound
diff --git a/pkg/front_end/testcases/extensions/issue40596.dart.weak.expect b/pkg/front_end/testcases/extensions/issue40596.dart.weak.expect
index ca760ff..23c03fe 100644
--- a/pkg/front_end/testcases/extensions/issue40596.dart.weak.expect
+++ b/pkg/front_end/testcases/extensions/issue40596.dart.weak.expect
@@ -11,9 +11,9 @@
 }
 static method main() → void {
   asy::StreamController<core::String> controller = asy::StreamController::•<core::String>();
-  let final asy::StreamController<core::String> #t1 = controller in let final (dynamic) → Null #t2 = (dynamic s) → Null {
+  let final asy::StreamController<core::String> #t1 = controller in self::Extension|call<core::String>(#t1.{asy::StreamController::stream}{asy::Stream<core::String>}, (dynamic s) → Null {
     core::print(s);
-  } in self::Extension|call<core::String>(#t1.{asy::StreamController::stream}{asy::Stream<core::String>}, #t2);
+  });
 }
 static method Extension|call<T extends core::Object? = dynamic>(lowered final asy::Stream<self::Extension|call::T%> #this, core::Function onData) → asy::StreamSubscription<self::Extension|call::T%> {
   return #this.{asy::Stream::listen}((self::Extension|call::T% d) → void {
diff --git a/pkg/front_end/testcases/extensions/issue40596.dart.weak.modular.expect b/pkg/front_end/testcases/extensions/issue40596.dart.weak.modular.expect
index ca760ff..23c03fe 100644
--- a/pkg/front_end/testcases/extensions/issue40596.dart.weak.modular.expect
+++ b/pkg/front_end/testcases/extensions/issue40596.dart.weak.modular.expect
@@ -11,9 +11,9 @@
 }
 static method main() → void {
   asy::StreamController<core::String> controller = asy::StreamController::•<core::String>();
-  let final asy::StreamController<core::String> #t1 = controller in let final (dynamic) → Null #t2 = (dynamic s) → Null {
+  let final asy::StreamController<core::String> #t1 = controller in self::Extension|call<core::String>(#t1.{asy::StreamController::stream}{asy::Stream<core::String>}, (dynamic s) → Null {
     core::print(s);
-  } in self::Extension|call<core::String>(#t1.{asy::StreamController::stream}{asy::Stream<core::String>}, #t2);
+  });
 }
 static method Extension|call<T extends core::Object? = dynamic>(lowered final asy::Stream<self::Extension|call::T%> #this, core::Function onData) → asy::StreamSubscription<self::Extension|call::T%> {
   return #this.{asy::Stream::listen}((self::Extension|call::T% d) → void {
diff --git a/pkg/front_end/testcases/extensions/issue40596.dart.weak.transformed.expect b/pkg/front_end/testcases/extensions/issue40596.dart.weak.transformed.expect
index ca760ff..23c03fe 100644
--- a/pkg/front_end/testcases/extensions/issue40596.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/extensions/issue40596.dart.weak.transformed.expect
@@ -11,9 +11,9 @@
 }
 static method main() → void {
   asy::StreamController<core::String> controller = asy::StreamController::•<core::String>();
-  let final asy::StreamController<core::String> #t1 = controller in let final (dynamic) → Null #t2 = (dynamic s) → Null {
+  let final asy::StreamController<core::String> #t1 = controller in self::Extension|call<core::String>(#t1.{asy::StreamController::stream}{asy::Stream<core::String>}, (dynamic s) → Null {
     core::print(s);
-  } in self::Extension|call<core::String>(#t1.{asy::StreamController::stream}{asy::Stream<core::String>}, #t2);
+  });
 }
 static method Extension|call<T extends core::Object? = dynamic>(lowered final asy::Stream<self::Extension|call::T%> #this, core::Function onData) → asy::StreamSubscription<self::Extension|call::T%> {
   return #this.{asy::Stream::listen}((self::Extension|call::T% d) → void {
diff --git a/pkg/front_end/testcases/general/constants/js_semantics/issue46123.dart.weak.expect b/pkg/front_end/testcases/general/constants/js_semantics/issue46123.dart.weak.expect
index c62f518..d5843e3 100644
--- a/pkg/front_end/testcases/general/constants/js_semantics/issue46123.dart.weak.expect
+++ b/pkg/front_end/testcases/general/constants/js_semantics/issue46123.dart.weak.expect
@@ -35,5 +35,5 @@
 
 Constructor coverage from constants:
 org-dartlang-testcase:///issue46123.dart:
-- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:21:9)
+- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:23:9)
 - Object. (from org-dartlang-sdk:///lib/core/object.dart:25:9)
diff --git a/pkg/front_end/testcases/general/constants/js_semantics/issue46123.dart.weak.modular.expect b/pkg/front_end/testcases/general/constants/js_semantics/issue46123.dart.weak.modular.expect
index c62f518..d5843e3 100644
--- a/pkg/front_end/testcases/general/constants/js_semantics/issue46123.dart.weak.modular.expect
+++ b/pkg/front_end/testcases/general/constants/js_semantics/issue46123.dart.weak.modular.expect
@@ -35,5 +35,5 @@
 
 Constructor coverage from constants:
 org-dartlang-testcase:///issue46123.dart:
-- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:21:9)
+- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:23:9)
 - Object. (from org-dartlang-sdk:///lib/core/object.dart:25:9)
diff --git a/pkg/front_end/testcases/general/constants/js_semantics/issue46123.dart.weak.transformed.expect b/pkg/front_end/testcases/general/constants/js_semantics/issue46123.dart.weak.transformed.expect
index c62f518..d5843e3 100644
--- a/pkg/front_end/testcases/general/constants/js_semantics/issue46123.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/general/constants/js_semantics/issue46123.dart.weak.transformed.expect
@@ -35,5 +35,5 @@
 
 Constructor coverage from constants:
 org-dartlang-testcase:///issue46123.dart:
-- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:21:9)
+- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:23:9)
 - Object. (from org-dartlang-sdk:///lib/core/object.dart:25:9)
diff --git a/pkg/front_end/testcases/general/constants/js_semantics/issue46123b.dart.weak.expect b/pkg/front_end/testcases/general/constants/js_semantics/issue46123b.dart.weak.expect
index 215e011..5ef5a68 100644
--- a/pkg/front_end/testcases/general/constants/js_semantics/issue46123b.dart.weak.expect
+++ b/pkg/front_end/testcases/general/constants/js_semantics/issue46123b.dart.weak.expect
@@ -37,5 +37,5 @@
 
 Constructor coverage from constants:
 org-dartlang-testcase:///issue46123b.dart:
-- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:21:9)
+- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:23:9)
 - Object. (from org-dartlang-sdk:///lib/core/object.dart:25:9)
diff --git a/pkg/front_end/testcases/general/constants/js_semantics/issue46123b.dart.weak.modular.expect b/pkg/front_end/testcases/general/constants/js_semantics/issue46123b.dart.weak.modular.expect
index 215e011..5ef5a68 100644
--- a/pkg/front_end/testcases/general/constants/js_semantics/issue46123b.dart.weak.modular.expect
+++ b/pkg/front_end/testcases/general/constants/js_semantics/issue46123b.dart.weak.modular.expect
@@ -37,5 +37,5 @@
 
 Constructor coverage from constants:
 org-dartlang-testcase:///issue46123b.dart:
-- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:21:9)
+- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:23:9)
 - Object. (from org-dartlang-sdk:///lib/core/object.dart:25:9)
diff --git a/pkg/front_end/testcases/general/constants/js_semantics/issue46123b.dart.weak.transformed.expect b/pkg/front_end/testcases/general/constants/js_semantics/issue46123b.dart.weak.transformed.expect
index 215e011..5ef5a68 100644
--- a/pkg/front_end/testcases/general/constants/js_semantics/issue46123b.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/general/constants/js_semantics/issue46123b.dart.weak.transformed.expect
@@ -37,5 +37,5 @@
 
 Constructor coverage from constants:
 org-dartlang-testcase:///issue46123b.dart:
-- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:21:9)
+- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:23:9)
 - Object. (from org-dartlang-sdk:///lib/core/object.dart:25:9)
diff --git a/pkg/frontend_server/lib/compute_kernel.dart b/pkg/frontend_server/lib/compute_kernel.dart
index 6e89be6..2c70e9d 100644
--- a/pkg/frontend_server/lib/compute_kernel.dart
+++ b/pkg/frontend_server/lib/compute_kernel.dart
@@ -317,7 +317,7 @@
     // TODO: Handle invalidation of precompiled macros.
     // TODO: Handle multiple macro libraries compiled to a single precompiled
     // kernel file.
-    var macroExecutor = state.options.macroExecutor;
+    var macroExecutor = state.processedOpts.macroExecutor;
     var format = parsedArgs['precompiled-macro-format'];
     for (var parts in (parsedArgs['precompiled-macro'] as List<String>)
         .map((arg) => arg.split(';'))) {
diff --git a/pkg/js/lib/js.dart b/pkg/js/lib/js.dart
index 5f41532..8c867ae 100644
--- a/pkg/js/lib/js.dart
+++ b/pkg/js/lib/js.dart
@@ -5,6 +5,8 @@
 /// Annotations to mark interfaces to JavaScript.
 library js;
 
+import 'package:meta/meta.dart';
+
 export 'dart:js' show allowInterop, allowInteropCaptureThis;
 
 /// An annotation that indicates a library, class, or member is implemented
@@ -45,3 +47,16 @@
 /// These classes should not contain any instance members, inherited or
 /// otherwise, and should instead use static extension members.
 const _StaticInterop staticInterop = _StaticInterop();
+
+/// NOTE: [trustTypes] is an experimental annotation that may disappear at any
+/// point in time. It exists solely to help users who wish to migrate classes
+/// from the older style of JS interop to the new static interop model but wish
+/// to preserve the older semantics for type checks. This annotation must be
+/// used alongside [staticInterop] and it affects any external methods in any
+/// extension to the static interop class.
+@experimental
+class _TrustTypes {
+  const _TrustTypes();
+}
+
+const _TrustTypes trustTypes = _TrustTypes();
diff --git a/pkg/js/pubspec.yaml b/pkg/js/pubspec.yaml
index 5f5000f..c8d12d3 100644
--- a/pkg/js/pubspec.yaml
+++ b/pkg/js/pubspec.yaml
@@ -6,5 +6,8 @@
 environment:
   sdk: ">=2.16.0-100.0.dev <3.0.0"
 
+dependencies:
+  meta: ^1.7.0
+
 dev_dependencies:
   lints: any
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index 6ef2967..f3f7728 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -3855,6 +3855,7 @@
     visitList(positionalParameters, v);
     visitList(namedParameters, v);
     returnType.accept(v);
+    futureValueType?.accept(v);
     body?.accept(v);
   }
 
@@ -3864,6 +3865,9 @@
     v.transformList(positionalParameters, this);
     v.transformList(namedParameters, this);
     returnType = v.visitDartType(returnType);
+    if (futureValueType != null) {
+      futureValueType = v.visitDartType(futureValueType!);
+    }
     if (body != null) {
       body = v.transform(body!);
       body?.parent = this;
@@ -3876,6 +3880,9 @@
     v.transformVariableDeclarationList(positionalParameters, this);
     v.transformVariableDeclarationList(namedParameters, this);
     returnType = v.visitDartType(returnType, cannotRemoveSentinel);
+    if (futureValueType != null) {
+      futureValueType = v.visitDartType(futureValueType!, cannotRemoveSentinel);
+    }
     if (body != null) {
       body = v.transformOrRemoveStatement(body!);
       body?.parent = this;
diff --git a/pkg/nnbd_migration/pubspec.yaml b/pkg/nnbd_migration/pubspec.yaml
index 19a70c2..4ed12f1 100644
--- a/pkg/nnbd_migration/pubspec.yaml
+++ b/pkg/nnbd_migration/pubspec.yaml
@@ -25,7 +25,7 @@
   analyzer_utilities:
     path: ../analyzer_utilities
   http: ^0.13.4
-  pedantic: ^1.9.0
+  lints: any
   test: ^1.6.4
   test_reflective_loader: ^0.2.0
 
diff --git a/pkg/test_runner/lib/src/output_log.dart b/pkg/test_runner/lib/src/output_log.dart
index fa8caae..7f04b1d 100644
--- a/pkg/test_runner/lib/src/output_log.dart
+++ b/pkg/test_runner/lib/src/output_log.dart
@@ -6,9 +6,9 @@
 import 'dart:convert';
 import 'dart:io';
 
-const _nonUtf8Error = '[test.dart: This test output contains non-UTF8 data]';
+const _nonUtf8Error = '[test.dart: This test output contains non-UTF8 data.]';
 const _truncatedError =
-    '[test.dart: This test output was too long and was truncated here.';
+    '[test.dart: This test output was too long and was truncated here.]';
 
 /// Records the output from a test.
 class OutputLog implements StreamConsumer<List<int>> {
@@ -37,6 +37,10 @@
 
     if (_data.length + data.length > _maxLength) {
       _data.addAll(data.take(_maxLength - _data.length));
+      final newline = utf8.encode("\n");
+      if (_data.last != newline.last) {
+        _data.addAll(newline);
+      }
       _data.addAll(utf8.encode(_truncatedError));
       _wasTruncated = true;
     } else {
@@ -56,6 +60,10 @@
       var malformed = utf8.decode(_data, allowMalformed: true);
       _data.clear();
       _data.addAll(utf8.encode(malformed));
+      final newline = utf8.encode("\n");
+      if (_data.last != newline.last) {
+        _data.addAll(newline);
+      }
       _data.addAll(utf8.encode(_nonUtf8Error));
       return true;
     }
diff --git a/runtime/platform/globals.h b/runtime/platform/globals.h
index 0f8cabf..d71457b 100644
--- a/runtime/platform/globals.h
+++ b/runtime/platform/globals.h
@@ -339,10 +339,10 @@
 #endif  // !defined(ARCH_IS_64_BIT) && !defined(FFI_UNIT_TESTS)
 #elif defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_ARM) ||                 \
     defined(TARGET_ARCH_RISCV32)
-#if defined(HOST_ARCH_X64) && defined(TARGET_ARCH_ARM)
-// This is simarm_x64, which is the only case where host/target architecture
-// mismatch is allowed. Unless, we're running FFI unit tests.
-#define IS_SIMARM_X64 1
+#if defined(ARCH_IS_64_BIT) && defined(TARGET_ARCH_ARM)
+// This is simarm_x64 or simarm_arm64, which is the only case where host/target
+// architecture mismatch is allowed. Unless, we're running FFI unit tests.
+#define IS_SIMARM_HOST64 1
 #elif !defined(ARCH_IS_32_BIT) && !defined(FFI_UNIT_TESTS)
 #error Mismatched Host/Target architectures.
 #endif  // !defined(ARCH_IS_32_BIT) && !defined(FFI_UNIT_TESTS)
@@ -360,7 +360,7 @@
 #elif defined(TARGET_ARCH_ARM)
 #if !defined(HOST_ARCH_ARM)
 #define TARGET_HOST_MISMATCH 1
-#if !defined(IS_SIMARM_X64)
+#if !defined(IS_SIMARM_HOST64)
 #define USING_SIMULATOR 1
 #endif
 #endif
diff --git a/runtime/vm/app_snapshot.cc b/runtime/vm/app_snapshot.cc
index e2b8a40..ccdda66 100644
--- a/runtime/vm/app_snapshot.cc
+++ b/runtime/vm/app_snapshot.cc
@@ -8199,7 +8199,6 @@
       TIMELINE_DURATION(thread(), Isolate, "ReadAlloc");
       for (intptr_t i = 0; i < num_clusters_; i++) {
         clusters_[i] = ReadCluster();
-        TIMELINE_DURATION(thread(), Isolate, clusters_[i]->name());
         clusters_[i]->ReadAlloc(this);
 #if defined(DEBUG)
         intptr_t serializers_next_ref_index_ = Read<int32_t>();
@@ -8215,7 +8214,6 @@
       TIMELINE_DURATION(thread(), Isolate, "ReadFill");
       SafepointWriteRwLocker ml(thread(), isolate_group()->program_lock());
       for (intptr_t i = 0; i < num_clusters_; i++) {
-        TIMELINE_DURATION(thread(), Isolate, clusters_[i]->name());
         clusters_[i]->ReadFill(this, primary);
 #if defined(DEBUG)
         int32_t section_marker = Read<int32_t>();
@@ -8247,7 +8245,6 @@
   {
     TIMELINE_DURATION(thread(), Isolate, "PostLoad");
     for (intptr_t i = 0; i < num_clusters_; i++) {
-      TIMELINE_DURATION(thread(), Isolate, clusters_[i]->name());
       clusters_[i]->PostLoad(this, refs, primary);
     }
   }
diff --git a/runtime/vm/compiler/assembler/disassembler_arm.cc b/runtime/vm/compiler/assembler/disassembler_arm.cc
index 51bb568..4d860a5 100644
--- a/runtime/vm/compiler/assembler/disassembler_arm.cc
+++ b/runtime/vm/compiler/assembler/disassembler_arm.cc
@@ -1512,14 +1512,14 @@
 
   *object = NULL;
   // TODO(36839): Make DecodeLoadObjectFromPoolOrThread work on simarm_x64.
-#if !defined(IS_SIMARM_X64)
+#if !defined(IS_SIMARM_HOST64)
   if (!code.IsNull()) {
     *object = &Object::Handle();
     if (!DecodeLoadObjectFromPoolOrThread(pc, code, *object)) {
       *object = NULL;
     }
   }
-#endif  // !defined(IS_SIMARM_X64)
+#endif  // !defined(IS_SIMARM_HOST64)
 }
 
 #endif  // !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index 367faf6..930ba5d 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -6534,14 +6534,13 @@
                                   const Register saved_fp,
                                   const Register temp0,
                                   const Register temp1) {
-  if (compiler::Assembler::EmittingComments()) {
-    __ Comment("EmitParamMoves");
-  }
+  __ Comment("EmitParamMoves");
 
   // Moves for return pointer.
   const auto& return_location =
       marshaller_.Location(compiler::ffi::kResultIndex);
   if (return_location.IsPointerToMemory()) {
+    __ Comment("return_location.IsPointerToMemory");
     const auto& pointer_location =
         return_location.AsPointerToMemory().pointer_location();
     const auto& pointer_register =
@@ -6568,10 +6567,13 @@
        arg_index++) {
     const intptr_t num_defs = marshaller_.NumDefinitions(arg_index);
     const auto& arg_target = marshaller_.Location(arg_index);
+    __ Comment("arg_index %" Pd " arg_target %s", arg_index,
+               arg_target.ToCString());
 
     // First deal with moving all individual definitions passed in to the
     // FfiCall to the right native location based on calling convention.
     for (intptr_t i = 0; i < num_defs; i++) {
+      __ Comment("  def_index %" Pd, def_index);
       const Location origin = rebase.Rebase(locs()->in(def_index));
       const Representation origin_rep =
           RequiredInputRepresentation(def_index) == kTagged
@@ -6594,6 +6596,23 @@
         // originate from parameters and thus are non-constant.
         UNREACHABLE();
       } else {
+#if defined(INCLUDE_IL_PRINTER)
+        __ Comment("def_target %s <- origin %s %s", def_target.ToCString(),
+                   origin.ToCString(), RepresentationToCString(origin_rep));
+#endif  // defined(INCLUDE_IL_PRINTER)
+#ifdef DEBUG
+        // Stack arguments split are in word-size chunks. These chunks can copy
+        // too much. However, that doesn't matter in practise because we process
+        // the stack in order.
+        // It only matters for the last chunk, it should not overwrite what was
+        // already on the stack.
+        if (def_target.IsStack()) {
+          const auto& def_target_stack = def_target.AsStack();
+          ASSERT(def_target_stack.offset_in_bytes() +
+                     def_target.payload_type().SizeInBytes() <=
+                 marshaller_.RequiredStackSpaceInBytes());
+        }
+#endif
         compiler->EmitMoveToNative(def_target, origin, origin_rep, &temp_alloc);
       }
       def_index++;
@@ -6604,6 +6623,7 @@
     // Note that the step above has already moved the pointer into the expected
     // native location.
     if (arg_target.IsPointerToMemory()) {
+      __ Comment("arg_target.IsPointerToMemory");
       NoTemporaryAllocator temp_alloc;
       const auto& pointer_loc =
           arg_target.AsPointerToMemory().pointer_location();
@@ -6635,13 +6655,13 @@
       const auto& src = compiler::ffi::NativeRegistersLocation(
           zone_, pointer_loc.payload_type(), pointer_loc.container_type(),
           temp0);
+      __ Comment("pointer_loc %s <- src %s", pointer_loc.ToCString(),
+                 src.ToCString());
       compiler->EmitNativeMove(pointer_loc, src, &temp_alloc);
     }
   }
 
-  if (compiler::Assembler::EmittingComments()) {
-    __ Comment("EmitParamMovesEnd");
-  }
+  __ Comment("EmitParamMovesEnd");
 }
 
 void FfiCallInstr::EmitReturnMoves(FlowGraphCompiler* compiler,
diff --git a/runtime/vm/compiler/ffi/marshaller.cc b/runtime/vm/compiler/ffi/marshaller.cc
index ca4f6d6..91497a7 100644
--- a/runtime/vm/compiler/ffi/marshaller.cc
+++ b/runtime/vm/compiler/ffi/marshaller.cc
@@ -453,6 +453,13 @@
   // First the native arguments are on the stack.
   // This is governed by the native ABI, the rest we can chose freely.
   stack_offset += native_calling_convention_.StackTopInBytes();
+#if (defined(DART_TARGET_OS_MACOS_IOS) || defined(DART_TARGET_OS_MACOS)) &&    \
+    defined(TARGET_ARCH_ARM64)
+  // Add extra padding for possibly non stack-aligned word-size writes.
+  // TODO(https://dartbug.com/48806): Re-engineer the moves to not over-
+  // approximate struct sizes on stack.
+  stack_offset += 4;
+#endif
   stack_offset = Utils::RoundUp(stack_offset, compiler::target::kWordSize);
   if (arg_index == kResultIndex) {
     return stack_offset;
diff --git a/runtime/vm/dart.cc b/runtime/vm/dart.cc
index bf4c995..2ccf215 100644
--- a/runtime/vm/dart.cc
+++ b/runtime/vm/dart.cc
@@ -155,7 +155,7 @@
 static DartInitializationState init_state_;
 
 static void CheckOffsets() {
-#if !defined(IS_SIMARM_X64)
+#if !defined(IS_SIMARM_HOST64)
   // These offsets are embedded in precompiled instructions. We need the
   // compiler and the runtime to agree.
   bool ok = true;
@@ -241,7 +241,7 @@
 #undef CHECK_CONSTANT
 #undef CHECK_OFFSET
 #undef CHECK_PAYLOAD_SIZEOF
-#endif  // !defined(IS_SIMARM_X64)
+#endif  // !defined(IS_SIMARM_HOST64)
 }
 
 char* Dart::DartInit(const Dart_InitializeParams* params) {
diff --git a/runtime/vm/dart_entry.cc b/runtime/vm/dart_entry.cc
index 2081ad0..0b79720 100644
--- a/runtime/vm/dart_entry.cc
+++ b/runtime/vm/dart_entry.cc
@@ -169,7 +169,7 @@
                                 Thread* thread) {
   ASSERT(!code.IsNull());
   ASSERT(thread->no_callback_scope_depth() == 0);
-  ASSERT(!IsolateGroup::Current()->null_safety_not_set());
+  ASSERT(!thread->isolate_group()->null_safety_not_set());
 
   const uword stub = StubCode::InvokeDartCode().EntryPoint();
   SuspendLongJumpScope suspend_long_jump_scope(thread);
diff --git a/runtime/vm/globals.h b/runtime/vm/globals.h
index c564a48..559dc44 100644
--- a/runtime/vm/globals.h
+++ b/runtime/vm/globals.h
@@ -112,7 +112,7 @@
 #define SUPPORT_TIMELINE 1
 #endif
 
-#if defined(ARCH_IS_64_BIT) && !defined(IS_SIMARM_X64)
+#if defined(ARCH_IS_64_BIT) && !defined(IS_SIMARM_HOST64)
 #define HASH_IN_OBJECT_HEADER 1
 #endif
 
diff --git a/runtime/vm/heap/heap.cc b/runtime/vm/heap/heap.cc
index 713bf97..68ba680 100644
--- a/runtime/vm/heap/heap.cc
+++ b/runtime/vm/heap/heap.cc
@@ -67,10 +67,9 @@
   }
 }
 
-uword Heap::AllocateNew(intptr_t size) {
-  ASSERT(Thread::Current()->no_safepoint_scope_depth() == 0);
-  CollectForDebugging();
-  Thread* thread = Thread::Current();
+uword Heap::AllocateNew(Thread* thread, intptr_t size) {
+  ASSERT(thread->no_safepoint_scope_depth() == 0);
+  CollectForDebugging(thread);
   uword addr = new_space_.TryAllocate(thread, size);
   if (LIKELY(addr != 0)) {
     return addr;
@@ -89,18 +88,17 @@
 
   // It is possible a GC doesn't clear enough space.
   // In that case, we must fall through and allocate into old space.
-  return AllocateOld(size, OldPage::kData);
+  return AllocateOld(thread, size, OldPage::kData);
 }
 
-uword Heap::AllocateOld(intptr_t size, OldPage::PageType type) {
-  ASSERT(Thread::Current()->no_safepoint_scope_depth() == 0);
+uword Heap::AllocateOld(Thread* thread, intptr_t size, OldPage::PageType type) {
+  ASSERT(thread->no_safepoint_scope_depth() == 0);
   if (old_space_.GrowthControlState()) {
-    CollectForDebugging();
+    CollectForDebugging(thread);
     uword addr = old_space_.TryAllocate(size, type);
     if (addr != 0) {
       return addr;
     }
-    Thread* thread = Thread::Current();
     // Wait for any GC tasks that are in progress.
     WaitForSweeperTasks(thread);
     addr = old_space_.TryAllocate(size, type);
@@ -135,7 +133,7 @@
   }
 
   if (old_space_.GrowthControlState()) {
-    WaitForSweeperTasks(Thread::Current());
+    WaitForSweeperTasks(thread);
     old_space_.TryReleaseReservation();
   } else {
     // We may or may not be a safepoint, so we don't know how to wait for the
@@ -428,7 +426,7 @@
 void Heap::CollectNewSpaceGarbage(Thread* thread,
                                   GCType type,
                                   GCReason reason) {
-  NoActiveIsolateScope no_active_isolate_scope;
+  NoActiveIsolateScope no_active_isolate_scope(thread);
   ASSERT(reason != GCReason::kPromotion);
   ASSERT(reason != GCReason::kFinalize);
   if (thread->isolate_group() == Dart::vm_isolate_group()) {
@@ -468,7 +466,7 @@
 void Heap::CollectOldSpaceGarbage(Thread* thread,
                                   GCType type,
                                   GCReason reason) {
-  NoActiveIsolateScope no_active_isolate_scope;
+  NoActiveIsolateScope no_active_isolate_scope(thread);
 
   ASSERT(type != GCType::kScavenge);
   ASSERT(reason != GCReason::kNewSpace);
@@ -704,9 +702,9 @@
   gc_on_nth_allocation_ = num_allocations;
 }
 
-void Heap::CollectForDebugging() {
+void Heap::CollectForDebugging(Thread* thread) {
   if (gc_on_nth_allocation_ == kNoForcedGarbageCollection) return;
-  if (Thread::Current()->IsAtSafepoint()) {
+  if (thread->IsAtSafepoint()) {
     // CollectAllGarbage is not supported when we are at a safepoint.
     // Allocating when at a safepoint is not a common case.
     return;
@@ -717,7 +715,7 @@
     gc_on_nth_allocation_ = kNoForcedGarbageCollection;
   } else {
     // Prevent generated code from using the TLAB fast path on next allocation.
-    new_space_.AbandonRemainingTLABForDebugging(Thread::Current());
+    new_space_.AbandonRemainingTLABForDebugging(thread);
   }
 }
 
diff --git a/runtime/vm/heap/heap.h b/runtime/vm/heap/heap.h
index 705e5a9..8248ee1 100644
--- a/runtime/vm/heap/heap.h
+++ b/runtime/vm/heap/heap.h
@@ -59,19 +59,19 @@
   Scavenger* new_space() { return &new_space_; }
   PageSpace* old_space() { return &old_space_; }
 
-  uword Allocate(intptr_t size, Space space) {
+  uword Allocate(Thread* thread, intptr_t size, Space space) {
     ASSERT(!read_only_);
     switch (space) {
       case kNew:
         // Do not attempt to allocate very large objects in new space.
         if (!IsAllocatableInNewSpace(size)) {
-          return AllocateOld(size, OldPage::kData);
+          return AllocateOld(thread, size, OldPage::kData);
         }
-        return AllocateNew(size);
+        return AllocateNew(thread, size);
       case kOld:
-        return AllocateOld(size, OldPage::kData);
+        return AllocateOld(thread, size, OldPage::kData);
       case kCode:
-        return AllocateOld(size, OldPage::kExecutable);
+        return AllocateOld(thread, size, OldPage::kExecutable);
       default:
         UNREACHABLE();
     }
@@ -325,8 +325,8 @@
        intptr_t max_new_gen_semi_words,  // Max capacity of new semi-space.
        intptr_t max_old_gen_words);
 
-  uword AllocateNew(intptr_t size);
-  uword AllocateOld(intptr_t size, OldPage::PageType type);
+  uword AllocateNew(Thread* thread, intptr_t size);
+  uword AllocateOld(Thread* thread, intptr_t size, OldPage::PageType type);
 
   // Visit all pointers. Caller must ensure concurrent sweeper is not running,
   // and the visitor must not allocate.
@@ -355,7 +355,7 @@
   void AddRegionsToObjectSet(ObjectSet* set) const;
 
   // Trigger major GC if 'gc_on_nth_allocation_' is set.
-  void CollectForDebugging();
+  void CollectForDebugging(Thread* thread);
 
   IsolateGroup* isolate_group_;
   bool is_vm_isolate_;
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index a8eb8b1..fc87889 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -538,6 +538,7 @@
 void Object::InitNullAndBool(IsolateGroup* isolate_group) {
   // Should only be run by the vm isolate.
   ASSERT(isolate_group == Dart::vm_isolate_group());
+  Thread* thread = Thread::Current();
   auto heap = isolate_group->heap();
 
   // TODO(iposva): NoSafepointScope needs to be added here.
@@ -547,7 +548,8 @@
   // 'null_' must be the first object allocated as it is used in allocation to
   // clear the object.
   {
-    uword address = heap->Allocate(Instance::InstanceSize(), Heap::kOld);
+    uword address =
+        heap->Allocate(thread, Instance::InstanceSize(), Heap::kOld);
     null_ = static_cast<InstancePtr>(address + kHeapObjectTag);
     // The call below is using 'null_' to initialize itself.
     InitializeObject(address, kNullCid, Instance::InstanceSize(),
@@ -561,14 +563,14 @@
   // otherwise identical.
   {
     // Allocate a dummy bool object to give true the desired alignment.
-    uword address = heap->Allocate(Bool::InstanceSize(), Heap::kOld);
+    uword address = heap->Allocate(thread, Bool::InstanceSize(), Heap::kOld);
     InitializeObject(address, kBoolCid, Bool::InstanceSize(),
                      Bool::ContainsCompressedPointers());
     static_cast<BoolPtr>(address + kHeapObjectTag)->untag()->value_ = false;
   }
   {
     // Allocate true.
-    uword address = heap->Allocate(Bool::InstanceSize(), Heap::kOld);
+    uword address = heap->Allocate(thread, Bool::InstanceSize(), Heap::kOld);
     true_ = static_cast<BoolPtr>(address + kHeapObjectTag);
     InitializeObject(address, kBoolCid, Bool::InstanceSize(),
                      Bool::ContainsCompressedPointers());
@@ -577,7 +579,7 @@
   }
   {
     // Allocate false.
-    uword address = heap->Allocate(Bool::InstanceSize(), Heap::kOld);
+    uword address = heap->Allocate(thread, Bool::InstanceSize(), Heap::kOld);
     false_ = static_cast<BoolPtr>(address + kHeapObjectTag);
     InitializeObject(address, kBoolCid, Bool::InstanceSize(),
                      Bool::ContainsCompressedPointers());
@@ -744,7 +746,7 @@
   // Allocate and initialize the class class.
   {
     intptr_t size = Class::InstanceSize();
-    uword address = heap->Allocate(size, Heap::kOld);
+    uword address = heap->Allocate(thread, size, Heap::kOld);
     class_class_ = static_cast<ClassPtr>(address + kHeapObjectTag);
     InitializeObject(address, Class::kClassId, size,
                      Class::ContainsCompressedPointers());
@@ -979,7 +981,7 @@
 
   // Allocate and initialize the empty_array instance.
   {
-    uword address = heap->Allocate(Array::InstanceSize(0), Heap::kOld);
+    uword address = heap->Allocate(thread, Array::InstanceSize(0), Heap::kOld);
     InitializeObject(address, kImmutableArrayCid, Array::InstanceSize(0),
                      Array::ContainsCompressedPointers());
     Array::initializeHandle(empty_array_,
@@ -991,7 +993,7 @@
   Smi& smi = Smi::Handle();
   // Allocate and initialize the zero_array instance.
   {
-    uword address = heap->Allocate(Array::InstanceSize(1), Heap::kOld);
+    uword address = heap->Allocate(thread, Array::InstanceSize(1), Heap::kOld);
     InitializeObject(address, kImmutableArrayCid, Array::InstanceSize(1),
                      Array::ContainsCompressedPointers());
     Array::initializeHandle(zero_array_,
@@ -1004,7 +1006,8 @@
 
   // Allocate and initialize the canonical empty context scope object.
   {
-    uword address = heap->Allocate(ContextScope::InstanceSize(0), Heap::kOld);
+    uword address =
+        heap->Allocate(thread, ContextScope::InstanceSize(0), Heap::kOld);
     InitializeObject(address, kContextScopeCid, ContextScope::InstanceSize(0),
                      ContextScope::ContainsCompressedPointers());
     ContextScope::initializeHandle(
@@ -1019,7 +1022,8 @@
 
   // Allocate and initialize the canonical empty object pool object.
   {
-    uword address = heap->Allocate(ObjectPool::InstanceSize(0), Heap::kOld);
+    uword address =
+        heap->Allocate(thread, ObjectPool::InstanceSize(0), Heap::kOld);
     InitializeObject(address, kObjectPoolCid, ObjectPool::InstanceSize(0),
                      ObjectPool::ContainsCompressedPointers());
     ObjectPool::initializeHandle(
@@ -1033,7 +1037,7 @@
   // Allocate and initialize the empty_compressed_stackmaps instance.
   {
     const intptr_t instance_size = CompressedStackMaps::InstanceSize(0);
-    uword address = heap->Allocate(instance_size, Heap::kOld);
+    uword address = heap->Allocate(thread, instance_size, Heap::kOld);
     InitializeObject(address, kCompressedStackMapsCid, instance_size,
                      CompressedStackMaps::ContainsCompressedPointers());
     CompressedStackMaps::initializeHandle(
@@ -1045,7 +1049,8 @@
 
   // Allocate and initialize the empty_descriptors instance.
   {
-    uword address = heap->Allocate(PcDescriptors::InstanceSize(0), Heap::kOld);
+    uword address =
+        heap->Allocate(thread, PcDescriptors::InstanceSize(0), Heap::kOld);
     InitializeObject(address, kPcDescriptorsCid, PcDescriptors::InstanceSize(0),
                      PcDescriptors::ContainsCompressedPointers());
     PcDescriptors::initializeHandle(
@@ -1058,8 +1063,8 @@
 
   // Allocate and initialize the canonical empty variable descriptor object.
   {
-    uword address =
-        heap->Allocate(LocalVarDescriptors::InstanceSize(0), Heap::kOld);
+    uword address = heap->Allocate(thread, LocalVarDescriptors::InstanceSize(0),
+                                   Heap::kOld);
     InitializeObject(address, kLocalVarDescriptorsCid,
                      LocalVarDescriptors::InstanceSize(0),
                      LocalVarDescriptors::ContainsCompressedPointers());
@@ -1076,7 +1081,7 @@
   // and can share this canonical descriptor.
   {
     uword address =
-        heap->Allocate(ExceptionHandlers::InstanceSize(0), Heap::kOld);
+        heap->Allocate(thread, ExceptionHandlers::InstanceSize(0), Heap::kOld);
     InitializeObject(address, kExceptionHandlersCid,
                      ExceptionHandlers::InstanceSize(0),
                      ExceptionHandlers::ContainsCompressedPointers());
@@ -1090,7 +1095,8 @@
 
   // Allocate and initialize the canonical empty type arguments object.
   {
-    uword address = heap->Allocate(TypeArguments::InstanceSize(0), Heap::kOld);
+    uword address =
+        heap->Allocate(thread, TypeArguments::InstanceSize(0), Heap::kOld);
     InitializeObject(address, kTypeArgumentsCid, TypeArguments::InstanceSize(0),
                      TypeArguments::ContainsCompressedPointers());
     TypeArguments::initializeHandle(
@@ -2666,7 +2672,7 @@
   ASSERT(thread->no_callback_scope_depth() == 0);
   Heap* heap = thread->heap();
 
-  uword address = heap->Allocate(size, space);
+  uword address = heap->Allocate(thread, size, space);
   if (UNLIKELY(address == 0)) {
     // SuspendLongJumpScope during Dart entry ensures that if a longjmp base is
     // available, it is the innermost error handler, so check for a longjmp base
@@ -2684,7 +2690,7 @@
       OUT_OF_MEMORY();
     }
   }
-  NoSafepointScope no_safepoint;
+  NoSafepointScope no_safepoint(thread);
   ObjectPtr raw_obj;
   InitializeObject(address, cls_id, size, compressed);
   raw_obj = static_cast<ObjectPtr>(address + kHeapObjectTag);
@@ -19714,6 +19720,11 @@
   if (cls.EnsureIsAllocateFinalized(thread) != Error::null()) {
     return Instance::null();
   }
+  return NewAlreadyFinalized(cls, space);
+}
+
+InstancePtr Instance::NewAlreadyFinalized(const Class& cls, Heap::Space space) {
+  ASSERT(cls.is_allocate_finalized());
   intptr_t instance_size = cls.host_instance_size();
   ASSERT(instance_size > 0);
   ObjectPtr raw = Object::Allocate(cls.id(), instance_size, space,
@@ -24306,7 +24317,8 @@
 ArrayPtr Array::Grow(const Array& source,
                      intptr_t new_length,
                      Heap::Space space) {
-  Zone* zone = Thread::Current()->zone();
+  Thread* thread = Thread::Current();
+  Zone* zone = thread->zone();
   const Array& result = Array::Handle(zone, Array::New(new_length, space));
   intptr_t len = 0;
   if (!source.IsNull()) {
@@ -24319,7 +24331,7 @@
   PassiveObject& obj = PassiveObject::Handle(zone);
   for (int i = 0; i < len; i++) {
     obj = source.At(i);
-    result.SetAt(i, obj);
+    result.SetAt(i, obj, thread);
   }
   return result.ptr();
 }
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 4d16d9c..7d9ffe0 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -7714,6 +7714,8 @@
   }
 
   static InstancePtr New(const Class& cls, Heap::Space space = Heap::kNew);
+  static InstancePtr NewAlreadyFinalized(const Class& cls,
+                                         Heap::Space space = Heap::kNew);
 
   // Array/list element address computations.
   static intptr_t DataOffsetFor(intptr_t cid);
@@ -10342,9 +10344,12 @@
   }
   template <std::memory_order order = std::memory_order_relaxed>
   void SetAt(intptr_t index, const Object& value) const {
-    // TODO(iposva): Add storing NoSafepointScope.
     untag()->set_element<order>(index, value.ptr());
   }
+  template <std::memory_order order = std::memory_order_relaxed>
+  void SetAt(intptr_t index, const Object& value, Thread* thread) const {
+    untag()->set_element<order>(index, value.ptr(), thread);
+  }
 
   // Access to the array with acquire release semantics.
   ObjectPtr AtAcquire(intptr_t index) const {
@@ -10479,9 +10484,11 @@
       memmove(const_cast<CompressedObjectPtr*>(to), from,
               count * kBytesPerElement);
     } else {
+      Thread* thread = Thread::Current();
       const uword heap_base = ptr()->heap_base();
       for (intptr_t i = 0; i < count; ++i) {
-        StoreArrayPointer(&to[i], from[i].Decompress(heap_base));
+        untag()->StoreArrayPointer(&to[i], from[i].Decompress(heap_base),
+                                   thread);
       }
     }
   }
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 89dae69..dc4fcf3 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -601,9 +601,7 @@
     }
   }
 
-  template <typename type,
-            typename compressed_type,
-            std::memory_order order = std::memory_order_relaxed>
+  template <typename type, typename compressed_type, std::memory_order order>
   void StoreCompressedArrayPointer(compressed_type const* addr, type value) {
     reinterpret_cast<std::atomic<compressed_type>*>(
         const_cast<compressed_type*>(addr))
@@ -613,6 +611,18 @@
     }
   }
 
+  template <typename type, typename compressed_type, std::memory_order order>
+  void StoreCompressedArrayPointer(compressed_type const* addr,
+                                   type value,
+                                   Thread* thread) {
+    reinterpret_cast<std::atomic<compressed_type>*>(
+        const_cast<compressed_type*>(addr))
+        ->store(static_cast<compressed_type>(value), order);
+    if (value->IsHeapObject()) {
+      CheckArrayPointerStore(addr, value, thread);
+    }
+  }
+
   template <typename type, typename compressed_type>
   void StoreCompressedArrayPointer(compressed_type const* addr,
                                    type value,
@@ -863,6 +873,10 @@
   void set_##accessor_name(intptr_t index, type value) {                       \
     StoreArrayPointer<type, order>(&array_name()[index], value);               \
   }                                                                            \
+  template <std::memory_order order = std::memory_order_relaxed>               \
+  void set_##accessor_name(intptr_t index, type value, Thread* thread) {       \
+    StoreArrayPointer<type, order>(&array_name()[index], value, thread);       \
+  }                                                                            \
                                                                                \
  protected:                                                                    \
   type* array_name() { OPEN_ARRAY_START(type, type); }                         \
@@ -881,6 +895,11 @@
     StoreCompressedArrayPointer<type, Compressed##type, order>(                \
         &array_name()[index], value);                                          \
   }                                                                            \
+  template <std::memory_order order = std::memory_order_relaxed>               \
+  void set_##accessor_name(intptr_t index, type value, Thread* thread) {       \
+    StoreCompressedArrayPointer<type, Compressed##type, order>(                \
+        &array_name()[index], value, thread);                                  \
+  }                                                                            \
                                                                                \
  protected:                                                                    \
   Compressed##type* array_name() {                                             \
diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc
index a249423..99601df 100644
--- a/runtime/vm/runtime_entry.cc
+++ b/runtime/vm/runtime_entry.cc
@@ -460,11 +460,9 @@
 // Return value: newly allocated object.
 DEFINE_RUNTIME_ENTRY(AllocateObject, 2) {
   const Class& cls = Class::CheckedHandle(zone, arguments.ArgAt(0));
-  const Error& error =
-      Error::Handle(zone, cls.EnsureIsAllocateFinalized(thread));
-  ThrowIfError(error);
-  const Instance& instance =
-      Instance::Handle(zone, Instance::New(cls, SpaceForRuntimeAllocation()));
+  ASSERT(cls.is_allocate_finalized());
+  const Instance& instance = Instance::Handle(
+      zone, Instance::NewAlreadyFinalized(cls, SpaceForRuntimeAllocation()));
 
   arguments.SetReturn(instance);
   if (cls.NumTypeArguments() == 0) {
@@ -1393,7 +1391,7 @@
 
 #if !defined(PRODUCT)
   // Monomorphic/megamorphic do not check the isolate's stepping flag.
-  if (Isolate::Current()->has_attempted_stepping()) return;
+  if (thread->isolate()->has_attempted_stepping()) return;
 #endif
 
   // Monomorphic/megamorphic calls are only for unoptimized code.
diff --git a/runtime/vm/thread.cc b/runtime/vm/thread.cc
index 9f3a5bf..e8ad780 100644
--- a/runtime/vm/thread.cc
+++ b/runtime/vm/thread.cc
@@ -437,7 +437,10 @@
   if ((interrupt_bits & kVMInterrupt) != 0) {
     CheckForSafepoint();
     if (isolate_group()->store_buffer()->Overflowed()) {
-      heap()->CollectGarbage(this, GCType::kScavenge, GCReason::kStoreBuffer);
+      // Evacuate: If the popular store buffer targets are copied instead of
+      // promoted, the store buffer won't shrink and a second scavenge will
+      // occur that does promote them.
+      heap()->CollectGarbage(this, GCType::kEvacuate, GCReason::kStoreBuffer);
     }
 
 #if !defined(PRODUCT)
diff --git a/sdk/lib/_internal/js_runtime/lib/late_helper.dart b/sdk/lib/_internal/js_runtime/lib/late_helper.dart
index 1643555..ceabcd7 100644
--- a/sdk/lib/_internal/js_runtime/lib/late_helper.dart
+++ b/sdk/lib/_internal/js_runtime/lib/late_helper.dart
@@ -6,20 +6,8 @@
 
 import 'dart:_internal' show LateError, createSentinel, isSentinel;
 
-@pragma('dart2js:tryInline')
-void throwLateFieldNI(String fieldName) => throw LateError.fieldNI(fieldName);
-@pragma('dart2js:tryInline')
-void throwLateFieldAI(String fieldName) => throw LateError.fieldAI(fieldName);
-@pragma('dart2js:tryInline')
 void throwLateFieldADI(String fieldName) => throw LateError.fieldADI(fieldName);
 
-@pragma('dart2js:tryInline')
-void throwUnnamedLateFieldNI() => throw LateError.fieldNI('');
-@pragma('dart2js:tryInline')
-void throwUnnamedLateFieldAI() => throw LateError.fieldAI('');
-@pragma('dart2js:tryInline')
-void throwUnnamedLateFieldADI() => throw LateError.fieldADI('');
-
 /// A boxed variable used for lowering uninitialized `late` variables when they
 /// are locals or statics.
 class _Cell {
@@ -125,8 +113,19 @@
 // Helpers for lowering late instance fields:
 // TODO(fishythefish): Support specialization of sentinels based on type.
 
-external T _lateReadCheck<T>(Object? value, String name);
+@pragma('dart2js:noInline')
+@pragma('dart2js:as:trust')
+T _lateReadCheck<T>(Object? value, String name) {
+  if (isSentinel(value)) throw LateError.fieldNI(name);
+  return value as T;
+}
 
-external void _lateWriteOnceCheck(Object? value, String name);
+@pragma('dart2js:noInline')
+void _lateWriteOnceCheck(Object? value, String name) {
+  if (!isSentinel(value)) throw LateError.fieldAI(name);
+}
 
-external void _lateInitializeOnceCheck(Object? value, String name);
+@pragma('dart2js:noInline')
+void _lateInitializeOnceCheck(Object? value, String name) {
+  if (!isSentinel(value)) throw LateError.fieldADI(name);
+}
diff --git a/sdk/lib/js_util/js_util.dart b/sdk/lib/js_util/js_util.dart
index 9af9ea6..d0db609 100644
--- a/sdk/lib/js_util/js_util.dart
+++ b/sdk/lib/js_util/js_util.dart
@@ -74,6 +74,10 @@
 T getProperty<T>(Object o, Object name) =>
     JS<dynamic>('Object|Null', '#[#]', o, name);
 
+/// Similar to [getProperty] but introduces an unsound implicit cast to `T`.
+T _getPropertyTrustType<T>(Object o, Object name) =>
+    JS<T>('Object|Null', '#[#]', o, name);
+
 // A CFE transformation may optimize calls to `setProperty`, when [value] is
 // statically known to be a non-function.
 T setProperty<T>(Object o, Object name, T? value) {
@@ -95,18 +99,38 @@
   return JS<dynamic>('Object|Null', '#[#].apply(#, #)', o, method, o, args);
 }
 
+/// Similar to [callMethod] but introduces an unsound implicit cast to `T`.
+T _callMethodTrustType<T>(Object o, String method, List<Object?> args) {
+  assertInteropArgs(args);
+  return JS<T>('Object|Null', '#[#].apply(#, #)', o, method, o, args);
+}
+
 /// Unchecked version for 0 arguments, only used in a CFE transformation.
 @pragma('dart2js:tryInline')
 T _callMethodUnchecked0<T>(Object o, String method) {
   return JS<dynamic>('Object|Null', '#[#]()', o, method);
 }
 
+/// Similar to [_callMethodUnchecked] but introduces an unsound implicit cast
+/// to `T`.
+@pragma('dart2js:tryInline')
+T _callMethodUncheckedTrustType0<T>(Object o, String method) {
+  return JS<T>('Object|Null', '#[#]()', o, method);
+}
+
 /// Unchecked version for 1 argument, only used in a CFE transformation.
 @pragma('dart2js:tryInline')
 T _callMethodUnchecked1<T>(Object o, String method, Object? arg1) {
   return JS<dynamic>('Object|Null', '#[#](#)', o, method, arg1);
 }
 
+/// Similar to [_callMethodUnchecked1] but introduces an unsound implicit cast
+/// to `T`.
+@pragma('dart2js:tryInline')
+T _callMethodUncheckedTrustType1<T>(Object o, String method, Object? arg1) {
+  return JS<T>('Object|Null', '#[#](#)', o, method, arg1);
+}
+
 /// Unchecked version for 2 arguments, only used in a CFE transformation.
 @pragma('dart2js:tryInline')
 T _callMethodUnchecked2<T>(
@@ -114,6 +138,14 @@
   return JS<dynamic>('Object|Null', '#[#](#, #)', o, method, arg1, arg2);
 }
 
+/// Similar to [_callMethodUnchecked2] but introduces an unsound implicit cast
+/// to `T`.
+@pragma('dart2js:tryInline')
+T _callMethodUncheckedTrustType2<T>(
+    Object o, String method, Object? arg1, Object? arg2) {
+  return JS<T>('Object|Null', '#[#](#, #)', o, method, arg1, arg2);
+}
+
 /// Unchecked version for 3 arguments, only used in a CFE transformation.
 @pragma('dart2js:tryInline')
 T _callMethodUnchecked3<T>(
@@ -122,6 +154,14 @@
       'Object|Null', '#[#](#, #, #)', o, method, arg1, arg2, arg3);
 }
 
+/// Similar to [_callMethodUnchecked3] but introduces an unsound implicit cast
+/// to `T`.
+@pragma('dart2js:tryInline')
+T _callMethodUncheckedTrustType3<T>(
+    Object o, String method, Object? arg1, Object? arg2, Object? arg3) {
+  return JS<T>('Object|Null', '#[#](#, #, #)', o, method, arg1, arg2, arg3);
+}
+
 /// Unchecked version for 4 arguments, only used in a CFE transformation.
 @pragma('dart2js:tryInline')
 T _callMethodUnchecked4<T>(Object o, String method, Object? arg1, Object? arg2,
@@ -130,6 +170,15 @@
       'Object|Null', '#[#](#, #, #, #)', o, method, arg1, arg2, arg3, arg4);
 }
 
+/// Similar to [_callMethodUnchecked4] but introduces an unsound implicit cast
+/// to `T`.
+@pragma('dart2js:tryInline')
+T _callMethodUncheckedTrustType4<T>(Object o, String method, Object? arg1,
+    Object? arg2, Object? arg3, Object? arg4) {
+  return JS<T>(
+      'Object|Null', '#[#](#, #, #, #)', o, method, arg1, arg2, arg3, arg4);
+}
+
 /// Check whether [o] is an instance of [type].
 ///
 /// The value in [type] is expected to be a JS-interop object that
diff --git a/tests/lib/js/static_interop_test/futurevaluetype_test.dart b/tests/lib/js/static_interop_test/futurevaluetype_test.dart
new file mode 100644
index 0000000..ac2a5df
--- /dev/null
+++ b/tests/lib/js/static_interop_test/futurevaluetype_test.dart
@@ -0,0 +1,30 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Test that `FunctionNode`'s `futureValueType`s are correctly transformed.
+// See https://github.com/dart-lang/sdk/issues/48835 for more details.
+
+@JS()
+library futurevaluetype_test;
+
+import 'dart:html';
+
+import 'package:js/js.dart';
+
+@JS()
+@staticInterop
+class JSWindow {}
+
+// `futureValueType` corresponds to the `JSWindow` type parameter in the return
+// value here. If this isn't correctly erased, we should see a runtime type
+// error when using this method, as we'll be attemting to return a `@Native`
+// type (`Window`) where a `package:js` type is expected instead of a
+// `JavaScriptObject`.
+Future<JSWindow> returnInteropType() async {
+  return window as JSWindow;
+}
+
+void main() async {
+  await returnInteropType();
+}
diff --git a/tests/lib/js/trust_types_annotation_test.dart b/tests/lib/js/trust_types_annotation_test.dart
new file mode 100644
index 0000000..7248ad5
--- /dev/null
+++ b/tests/lib/js/trust_types_annotation_test.dart
@@ -0,0 +1,11 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:js/js.dart';
+
+@JS()
+@trustTypes
+class Incorrect {}
+//    ^
+// [web] JS interop class 'Incorrect' has an `@trustTypes` annotation, but no `@staticInterop` annotation.
diff --git a/tests/lib/js/trust_types_test.dart b/tests/lib/js/trust_types_test.dart
new file mode 100644
index 0000000..2cbfb37
--- /dev/null
+++ b/tests/lib/js/trust_types_test.dart
@@ -0,0 +1,77 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Test that the [trustTypes] annotation has the same semantics as the older
+// style of js interop.
+
+import 'package:js/js.dart';
+import 'package:expect/expect.dart';
+
+class NonExistent {}
+
+@JS()
+@staticInterop
+@trustTypes
+class TrustMe {
+  external factory TrustMe();
+}
+
+extension TrustMeExtension on TrustMe {
+  external List<NonExistent> get foos;
+  external List<NonExistent> callFoos();
+  external List<NonExistent> callFoos1(int ignored);
+  external List<NonExistent> callFoos2(int ignored1, int ignored2);
+  external List<NonExistent> callFoos3(
+      int ignored1, int ignored2, int ignored3);
+  external List<NonExistent> callFoos4(
+      int ignored1, int ignored2, int ignored3, int ignored4);
+  external List<NonExistent> callFoos5(
+      int ignored1, int ignored2, int ignored3, int ignored4, int ignored5);
+  external String get fooPrimitive;
+}
+
+@JS()
+external void eval(String code);
+
+void main() {
+  eval(r'''
+      TrustMe = function TrustMe() {
+        this.foos = 'not a list 1';
+        this.fooPrimitive = 5;
+        this.callFoos = function() {
+          return 'not a list 2';
+        }
+        this.callFoos1 = function(a) {
+          return 'not a list 3';
+        }
+        this.callFoos2 = function(a, b) {
+          return 'not a list 4';
+        }
+        this.callFoos3 = function(a, b, c) {
+          return 'not a list 5';
+        }
+        this.callFoos4 = function(a, b, c, d) {
+          return 'not a list 6';
+        }
+        this.callFoos5 = function(a, b, c, d, e) {
+          return 'not a list 7';
+        }
+      }
+      ''');
+  final trusted = TrustMe();
+  Expect.equals('not a list 1', trusted.foos.toString());
+  Expect.equals('not a list 2', trusted.callFoos().toString());
+  Expect.equals('not a list 3', trusted.callFoos1(1).toString());
+  Expect.equals('not a list 4', trusted.callFoos2(1, 1).toString());
+  Expect.equals('not a list 5', trusted.callFoos3(1, 1, 1).toString());
+  Expect.equals('not a list 6', trusted.callFoos4(1, 1, 1, 1).toString());
+
+  final falseList = trusted.callFoos5(1, 1, 1, 1, 1);
+  Expect.equals('not a list 7', falseList.toString());
+  Expect.throws(() => falseList.removeAt(0));
+
+  final falseString = trusted.fooPrimitive;
+  Expect.equals(5, falseString);
+  Expect.throws(() => falseString.codeUnitAt(0));
+}
diff --git a/tests/lib/lib.status b/tests/lib/lib.status
index 0b262df..2f3fffb 100644
--- a/tests/lib/lib.status
+++ b/tests/lib/lib.status
@@ -12,7 +12,8 @@
 isolate/issue_24243_parent_isolate_test: Skip # Requires checked mode
 
 [ $runtime == d8 ]
-js/js_util/javascriptobject_extensions_test.dart: SkipByDesign # Uses dart:html.
+js/js_util/javascriptobject_extensions_test: SkipByDesign # Uses dart:html.
+js/static_interop_test/futurevaluetype_test: SkipByDesign # Uses dart:html.
 
 [ $runtime == dart_precompiled ]
 isolate/package_config_getter_test: SkipByDesign # AOT mode doesn't preserve package structure.
@@ -54,6 +55,7 @@
 js/method_call_on_object_test: SkipByDesign # Issue 42085.
 js/mock_test/*: SkipByDesign # Issue 42085.
 js/parameters_test: SkipByDesign # Issue 42085.
+js/trust_types_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code
 
 [ $compiler != dart2js && $compiler != dartdevk ]
 web/*: SkipByDesign
diff --git a/tests/lib_2/js/static_interop_test/futurevaluetype_test.dart b/tests/lib_2/js/static_interop_test/futurevaluetype_test.dart
new file mode 100644
index 0000000..ac2a5df
--- /dev/null
+++ b/tests/lib_2/js/static_interop_test/futurevaluetype_test.dart
@@ -0,0 +1,30 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Test that `FunctionNode`'s `futureValueType`s are correctly transformed.
+// See https://github.com/dart-lang/sdk/issues/48835 for more details.
+
+@JS()
+library futurevaluetype_test;
+
+import 'dart:html';
+
+import 'package:js/js.dart';
+
+@JS()
+@staticInterop
+class JSWindow {}
+
+// `futureValueType` corresponds to the `JSWindow` type parameter in the return
+// value here. If this isn't correctly erased, we should see a runtime type
+// error when using this method, as we'll be attemting to return a `@Native`
+// type (`Window`) where a `package:js` type is expected instead of a
+// `JavaScriptObject`.
+Future<JSWindow> returnInteropType() async {
+  return window as JSWindow;
+}
+
+void main() async {
+  await returnInteropType();
+}
diff --git a/tests/lib_2/js/trust_types_annotation_test.dart b/tests/lib_2/js/trust_types_annotation_test.dart
new file mode 100644
index 0000000..7248ad5
--- /dev/null
+++ b/tests/lib_2/js/trust_types_annotation_test.dart
@@ -0,0 +1,11 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:js/js.dart';
+
+@JS()
+@trustTypes
+class Incorrect {}
+//    ^
+// [web] JS interop class 'Incorrect' has an `@trustTypes` annotation, but no `@staticInterop` annotation.
diff --git a/tests/lib_2/js/trust_types_test.dart b/tests/lib_2/js/trust_types_test.dart
new file mode 100644
index 0000000..2cbfb37
--- /dev/null
+++ b/tests/lib_2/js/trust_types_test.dart
@@ -0,0 +1,77 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Test that the [trustTypes] annotation has the same semantics as the older
+// style of js interop.
+
+import 'package:js/js.dart';
+import 'package:expect/expect.dart';
+
+class NonExistent {}
+
+@JS()
+@staticInterop
+@trustTypes
+class TrustMe {
+  external factory TrustMe();
+}
+
+extension TrustMeExtension on TrustMe {
+  external List<NonExistent> get foos;
+  external List<NonExistent> callFoos();
+  external List<NonExistent> callFoos1(int ignored);
+  external List<NonExistent> callFoos2(int ignored1, int ignored2);
+  external List<NonExistent> callFoos3(
+      int ignored1, int ignored2, int ignored3);
+  external List<NonExistent> callFoos4(
+      int ignored1, int ignored2, int ignored3, int ignored4);
+  external List<NonExistent> callFoos5(
+      int ignored1, int ignored2, int ignored3, int ignored4, int ignored5);
+  external String get fooPrimitive;
+}
+
+@JS()
+external void eval(String code);
+
+void main() {
+  eval(r'''
+      TrustMe = function TrustMe() {
+        this.foos = 'not a list 1';
+        this.fooPrimitive = 5;
+        this.callFoos = function() {
+          return 'not a list 2';
+        }
+        this.callFoos1 = function(a) {
+          return 'not a list 3';
+        }
+        this.callFoos2 = function(a, b) {
+          return 'not a list 4';
+        }
+        this.callFoos3 = function(a, b, c) {
+          return 'not a list 5';
+        }
+        this.callFoos4 = function(a, b, c, d) {
+          return 'not a list 6';
+        }
+        this.callFoos5 = function(a, b, c, d, e) {
+          return 'not a list 7';
+        }
+      }
+      ''');
+  final trusted = TrustMe();
+  Expect.equals('not a list 1', trusted.foos.toString());
+  Expect.equals('not a list 2', trusted.callFoos().toString());
+  Expect.equals('not a list 3', trusted.callFoos1(1).toString());
+  Expect.equals('not a list 4', trusted.callFoos2(1, 1).toString());
+  Expect.equals('not a list 5', trusted.callFoos3(1, 1, 1).toString());
+  Expect.equals('not a list 6', trusted.callFoos4(1, 1, 1, 1).toString());
+
+  final falseList = trusted.callFoos5(1, 1, 1, 1, 1);
+  Expect.equals('not a list 7', falseList.toString());
+  Expect.throws(() => falseList.removeAt(0));
+
+  final falseString = trusted.fooPrimitive;
+  Expect.equals(5, falseString);
+  Expect.throws(() => falseString.codeUnitAt(0));
+}
diff --git a/tests/lib_2/lib_2.status b/tests/lib_2/lib_2.status
index cb20d17..1feee84 100644
--- a/tests/lib_2/lib_2.status
+++ b/tests/lib_2/lib_2.status
@@ -12,7 +12,8 @@
 isolate/issue_24243_parent_isolate_test: Skip # Requires checked mode
 
 [ $runtime == d8 ]
-js/js_util/javascriptobject_extensions_test.dart: SkipByDesign # Uses dart:html.
+js/js_util/javascriptobject_extensions_test: SkipByDesign # Uses dart:html.
+js/static_interop_test/futurevaluetype_test: SkipByDesign # Uses dart:html.
 
 [ $runtime == dart_precompiled ]
 isolate/package_config_getter_test: SkipByDesign # AOT mode doesn't preserve package structure.
@@ -54,6 +55,7 @@
 js/method_call_on_object_test: SkipByDesign # Issue 42085.
 js/mock_test/*: SkipByDesign # Issue 42085.
 js/parameters_test: SkipByDesign # Issue 42085.
+js/trust_types_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code
 
 [ $compiler != dart2js && $compiler != dartdevk ]
 web/*: SkipByDesign
diff --git a/tests/modular/js_interop/modules.yaml b/tests/modular/js_interop/modules.yaml
index 79290b9..9e9b3b6 100644
--- a/tests/modular/js_interop/modules.yaml
+++ b/tests/modular/js_interop/modules.yaml
@@ -6,5 +6,6 @@
 dependencies:
   main: log
   log: js
+  js: meta
 packages:
   js: ../../../pkg/js/lib
diff --git a/tests/modular/static_interop_erasure/modules.yaml b/tests/modular/static_interop_erasure/modules.yaml
index e356ba9..af71351 100644
--- a/tests/modular/static_interop_erasure/modules.yaml
+++ b/tests/modular/static_interop_erasure/modules.yaml
@@ -7,5 +7,6 @@
 dependencies:
   main: static_interop
   static_interop: js
+  js: meta
 packages:
   js: ../../../pkg/js/lib
diff --git a/tests/modular/unused_library_and_module/main.dart b/tests/modular/unused_library_and_module/main.dart
new file mode 100644
index 0000000..b808acc
--- /dev/null
+++ b/tests/modular/unused_library_and_module/main.dart
@@ -0,0 +1,11 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:expect/expect.dart';
+
+import 'used_module/used_library.dart';
+
+main() {
+  Expect.equals(usedConstant, 1);
+}
diff --git a/tests/modular/unused_library_and_module/modules.yaml b/tests/modular/unused_library_and_module/modules.yaml
new file mode 100644
index 0000000..1e0c315
--- /dev/null
+++ b/tests/modular/unused_library_and_module/modules.yaml
@@ -0,0 +1,8 @@
+# Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+#
+# Regression test: integral numbers should be treated as int and not double
+# after serialization across modules.
+dependencies:
+  main: [expect, used_module, unused_module]
diff --git a/tests/modular/unused_library_and_module/unused_module.dart b/tests/modular/unused_library_and_module/unused_module.dart
new file mode 100644
index 0000000..06d84dd
--- /dev/null
+++ b/tests/modular/unused_library_and_module/unused_module.dart
@@ -0,0 +1,4 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+const int unusedConstant2 = 3;
diff --git a/tests/modular/unused_library_and_module/used_module/unused_library.dart b/tests/modular/unused_library_and_module/used_module/unused_library.dart
new file mode 100644
index 0000000..5b0b178
--- /dev/null
+++ b/tests/modular/unused_library_and_module/used_module/unused_library.dart
@@ -0,0 +1,4 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+const int unusedConstant1 = 2;
diff --git a/tests/modular/unused_library_and_module/used_module/used_library.dart b/tests/modular/unused_library_and_module/used_module/used_library.dart
new file mode 100644
index 0000000..ea47365
--- /dev/null
+++ b/tests/modular/unused_library_and_module/used_module/used_library.dart
@@ -0,0 +1,4 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+const int usedConstant = 1;
diff --git a/tests/web/js_trust_types_disallowed_test.dart b/tests/web/js_trust_types_disallowed_test.dart
new file mode 100644
index 0000000..13dd1c6
--- /dev/null
+++ b/tests/web/js_trust_types_disallowed_test.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:js/js.dart';
+
+@JS()
+@staticInterop
+@trustTypes
+class Incorrect {}
+//    ^
+// [web] JS interop class 'Incorrect' has an `@trustTypes` annotation, but `@trustTypes` is only supported within the sdk.
diff --git a/tests/web_2/js_trust_types_disallowed_test.dart b/tests/web_2/js_trust_types_disallowed_test.dart
new file mode 100644
index 0000000..13dd1c6
--- /dev/null
+++ b/tests/web_2/js_trust_types_disallowed_test.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:js/js.dart';
+
+@JS()
+@staticInterop
+@trustTypes
+class Incorrect {}
+//    ^
+// [web] JS interop class 'Incorrect' has an `@trustTypes` annotation, but `@trustTypes` is only supported within the sdk.
diff --git a/tools/VERSION b/tools/VERSION
index 370264a..d376cc8 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 18
 PATCH 0
-PRERELEASE 40
+PRERELEASE 41
 PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json
index 1feea04..459362c 100644
--- a/tools/bots/test_matrix.json
+++ b/tools/bots/test_matrix.json
@@ -1783,10 +1783,75 @@
       },
       "steps": [
         {
-          "name": "build dart x64",
+          "name": "build dart debug arm64",
           "script": "tools/build.py",
           "arguments": [
-            "--mode=all",
+            "--mode=debug",
+            "--arch=arm64",
+            "--no-clang",
+            "--no-goma",
+            "dart_precompiled_runtime",
+            "gen_snapshot",
+            "runtime"
+          ]
+        },
+        {
+          "name": "build dart release arm64",
+          "script": "tools/build.py",
+          "arguments": [
+            "--mode=release",
+            "--arch=arm64",
+            "--no-clang",
+            "--no-goma",
+            "dart_precompiled_runtime",
+            "gen_snapshot",
+            "runtime"
+          ]
+        },
+        {
+          "name": "build dart product arm64",
+          "script": "tools/build.py",
+          "arguments": [
+            "--mode=product",
+            "--arch=arm64",
+            "--no-clang",
+            "--no-goma",
+            "dart_precompiled_runtime",
+            "gen_snapshot",
+            "runtime"
+          ]
+        },
+        {
+          "name": "build dart debug x64",
+          "script": "tools/build.py",
+          "arguments": [
+            "--mode=debug",
+            "--arch=x64",
+            "--no-clang",
+            "--no-goma",
+            "dart_precompiled_runtime",
+            "gen_snapshot",
+            "runtime"
+          ]
+        },
+        {
+          "name": "build dart release x64",
+          "script": "tools/build.py",
+          "arguments": [
+            "--mode=release",
+            "--arch=x64",
+            "--no-clang",
+            "--no-goma",
+            "dart_precompiled_runtime",
+            "gen_snapshot",
+            "runtime"
+          ]
+        },
+        {
+          "name": "build dart product x64",
+          "script": "tools/build.py",
+          "arguments": [
+            "--mode=product",
             "--arch=x64",
             "--no-clang",
             "--no-goma",
@@ -3064,7 +3129,7 @@
           "name": "build dart",
           "script": "tools/build.py",
           "arguments": [
-            "--arch=ia32,x64",
+            "--arch=ia32,x64,arm64",
             "--mode=release",
             "--check-clean",
             "create_sdk",
@@ -3075,7 +3140,7 @@
           "name": "upload sdk",
           "script": "tools/bots/dart_sdk.py",
           "arguments": [
-            "--arch=ia32,x64"
+            "--arch=ia32,x64,arm64"
           ]
         }
       ]
diff --git a/tools/gn.py b/tools/gn.py
index f5c01ad..a40302a 100755
--- a/tools/gn.py
+++ b/tools/gn.py
@@ -67,61 +67,76 @@
     return [merge(x, y) for x, y in gn_args.items()]
 
 
-# Runs true if the currently executing python interpreter is running under
-# Rosetta. I.e., python3 is an x64 executable and we're on an arm64 Mac.
-def IsRosetta():
-    if platform.system() == 'Darwin':
-        p = subprocess.Popen(['sysctl', '-in', 'sysctl.proc_translated'],
-                             stdout=subprocess.PIPE,
-                             stderr=subprocess.STDOUT)
-        output, _ = p.communicate()
-        return output.decode('utf-8').strip() == '1'
-    return False
-
-
+# The C compiler's host.
 def HostCpuForArch(arch):
-    # Check for Rosetta before checking platform.machine(), as the latter
-    # returns 'x86_64' when running under Rosetta.
-    if IsRosetta():
-        if arch in ['x64', 'x64c']:
-            # Without this case, we would try to build with
-            #   host_cpu="arm64"
-            #   target_cpu="x64"
-            #   dart_target_arch="x64"
-            # Which requires the VM to use an x64 simulator in the host
-            # arm64 binaries, and this simulator is unimplemented.
-            return 'x64'
-        else:
-            return 'arm64'
-
-    m = platform.machine()
-    if m == 'aarch64' or m == 'arm64':
-        return 'arm64'
-    if m == 'armv7l':
-        return 'arm'
-
-    if arch in ['ia32', 'arm', 'simarm', 'simarm_x64', 'riscv32', 'simriscv32']:
-        return 'x86'
-    if arch in [
-            'x64', 'arm64', 'simarm64', 'arm_x64', 'x64c', 'arm64c',
-            'simarm64c', 'riscv64', 'simriscv64'
-    ]:
+    if arch.endswith('_x64'):
         return 'x64'
+    if arch.endswith('_arm64'):
+        return 'arm64'
+    if arch.endswith('_riscv64'):
+        return 'riscv64'
+
+    # For each target architecture, we prefer in descending order
+    # - using the same architecture for the host (supports all architectures)
+    # - using a host architecture with the same word size (supports arm and riscv, which have simulators)
+    # - using a host architecture with a different word size (supports only AOT and only 32-bit target on 64-bit host)
+    if arch in ['ia32']:
+        candidates = ['x86']
+    elif arch in ['x64', 'x64c']:
+        candidates = ['x64']
+    elif arch in ['arm', 'simarm']:
+        candidates = ['arm', 'x86', 'riscv32', 'arm64', 'x64', 'riscv64']
+    elif arch in ['arm64', 'arm64c', 'simarm64', 'simarm64c']:
+        candidates = ['arm64', 'x64', 'riscv64']
+    elif arch in ['riscv32', 'simriscv32']:
+        candidates = ['riscv32', 'arm', 'x86', 'riscv64', 'arm64', 'x64']
+    elif arch in ['riscv64', 'simriscv64']:
+        candidates = ['riscv64', 'arm64', 'x64']
+    else:
+        raise Exception("Unknown Dart architecture: %s" % arch)
+
+    available = utils.HostArchitectures()
+    for candidate in candidates:
+        if candidate in available:
+            return candidate
+
+    raise Exception(
+        "Failed to find a C host architecture for %s. Need one of %s but only %s are available."
+        % (arch, candidates, available))
 
 
 # The C compiler's target.
 def TargetCpuForArch(arch, target_os):
-    if arch in ['ia32', 'simarm', 'simriscv32']:
+    # Real target architectures
+    if arch in ['ia32']:
         return 'x86'
-    if arch in [
-            'x64', 'simarm64', 'simarm_x64', 'simriscv64', 'x64c', 'simarm64c'
-    ]:
+    elif arch in ['x64', 'x64c']:
         return 'x64'
-    if arch == 'arm_x64':
+    elif arch in ['arm', 'arm_x64', 'arm_arm64', 'arm_riscv64']:
         return 'arm'
-    if arch == 'arm64c':
+    elif arch in ['arm64', 'arm64c']:
         return 'arm64'
-    return arch
+    elif arch in ['riscv32', 'riscv32_x64', 'riscv32_arm64', 'riscv32_riscv64']:
+        return 'riscv32'
+    elif arch in ['riscv64']:
+        return 'riscv64'
+
+    # Simulators
+    if arch in ['simarm', 'simriscv32']:
+        candidates = ['arm', 'riscv32', 'x86']
+    elif arch in ['simarm64', 'simarm64c', 'simriscv64']:
+        candidates = ['arm64', 'riscv64', 'x64']
+    else:
+        raise Exception("Unknown Dart architecture: %s" % arch)
+
+    available = utils.HostArchitectures()
+    for candidate in candidates:
+        if candidate in available:
+            return candidate
+
+    raise Exception(
+        "Failed to find a C target architecture for %s. Need one of %s but only %s are available."
+        % (arch, candidates, available))
 
 
 # The Dart compiler's target.
@@ -198,8 +213,9 @@
     gn_args['dart_use_compressed_pointers'] = IsCompressedPointerArch(arch)
 
     # Configure Crashpad library if it is used.
-    gn_args['dart_use_crashpad'] = (args.use_crashpad or
-                                    DART_USE_CRASHPAD in os.environ)
+    gn_args['dart_use_crashpad'] = ((args.use_crashpad or
+                                     DART_USE_CRASHPAD in os.environ) and
+                                    gn_args['target_cpu'] in ['x86', 'x64'])
     if gn_args['dart_use_crashpad']:
         # Tell Crashpad's BUILD files which checkout layout to use.
         gn_args['crashpad_dependencies'] = 'dart'
diff --git a/tools/utils.py b/tools/utils.py
index 2f42263..6209073 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -67,9 +67,11 @@
     'arm': 'arm',
     'arm64': 'arm',
     'arm_x64': 'arm',
+    'arm_arm64': 'arm',
     'simarm': 'ia32',
     'simarm64': 'ia32',
     'simarm_x64': 'ia32',
+    'simarm_arm64': 'arm',
     'x64c': 'ia32',
     'arm64c': 'arm',
     'simarm64c': 'ia32',
@@ -149,27 +151,44 @@
     return None
 
 
+# Runs true if the currently executing python interpreter is running under
+# Rosetta. I.e., python3 is an x64 executable and we're on an arm64 Mac.
+def IsRosetta():
+    if platform.system() == 'Darwin':
+        p = subprocess.Popen(['sysctl', '-in', 'sysctl.proc_translated'],
+                             stdout=subprocess.PIPE,
+                             stderr=subprocess.STDOUT)
+        output, _ = p.communicate()
+        return output.decode('utf-8').strip() == '1'
+    return False
+
+
+# Returns the architectures that can run on the current machine.
+def HostArchitectures():
+    m = platform.machine()
+    if platform.system() == 'Darwin':
+        if m == 'arm64' or IsRosetta():
+            # ARM64 Macs also support X64.
+            return ['arm64', 'x64']
+        if m == 'x86_64':
+            # X64 Macs no longer support IA32.
+            return ['x64']
+    else:
+        if m in ['aarch64', 'arm64', 'arm64e']:
+            return ['arm64']
+        if m in ['armv7l']:
+            return ['arm']
+        if m in ['i386', 'i686', 'ia32', 'x86']:
+            return ['x86']
+        if m in ['x64', 'x86-64', 'x86_64', 'AMD64']:
+            return ['x64', 'x86']
+    raise Exception('Failed to determine host architectures for %s %s',
+                    platform.machine(), platform.system())
+
+
 # Try to guess the host architecture.
 def GuessArchitecture():
-    os_id = platform.machine()
-    if os_id.startswith('aarch64') or os_id == 'arm64':
-        return 'arm64'
-    elif os_id.startswith('arm'):
-        return 'arm'
-    elif '64' in os_id:
-        return 'x64'
-    elif (not os_id) or (not re.match('(x|i[3-6])86', os_id) is None):
-        return 'ia32'
-    elif os_id == 'i86pc':
-        return 'ia32'
-
-    guess_os = GuessOS()
-    print('Warning: Guessing architecture {} based on os {}\n'.format(
-        os_id, guess_os))
-    if guess_os == 'win32':
-        return 'ia32'
-    return None
-
+    return HostArchitectures()[0]
 
 # Try to guess the number of cpus on this machine.
 def GuessCpus():
@@ -242,9 +261,15 @@
 
 
 def IsCrossBuild(target_os, arch):
-    host_arch = GuessArchitecture()
-    return ((GetArchFamily(host_arch) != GetArchFamily(arch)) or
-            (target_os != GuessOS()))
+    if target_os in [None, 'host']:
+        return False
+    if target_os != GuessOS():
+        return True
+    if arch.startswith('sim'):
+        return False
+    if arch in HostArchitectures():
+        return False
+    return True
 
 
 def GetBuildConf(mode, arch, conf_os=None, sanitizer=None):
@@ -253,9 +278,8 @@
                                arch.upper())
 
     # Ask for a cross build if the host and target architectures don't match.
-    host_arch = GuessArchitecture()
     cross_build = ''
-    if GetArchFamily(host_arch) != GetArchFamily(arch):
+    if IsCrossBuild(conf_os, arch):
         cross_build = 'X'
     return '{}{}{}{}'.format(GetBuildMode(mode), GetBuildSanitizer(sanitizer),
                              cross_build, arch.upper())