Version 3.0.0-54.0.dev

Merge 2dffe31edd413b39f024cf3d5a1c4335fdb674a4 into dev
diff --git a/DEPS b/DEPS
index 0e6387f..3a64473 100644
--- a/DEPS
+++ b/DEPS
@@ -53,7 +53,7 @@
   # hashes.
   "co19_rev": "be8f8ae44d3f5222713ab1c51e7dbf2296db0719",
   # This line prevents conflicts when both packages are rolled simultaneously.
-  "co19_2_rev": "f691647f1c2cc4f1019fb619932c52d0a1adbf2c",
+  "co19_2_rev": "8ba5eb65399044d20370c8cfd115e1f6bc51acb1",
 
   # The internal benchmarks to use. See go/dart-benchmarks-internal
   "benchmarks_internal_rev": "599aa474a03c37be146f82dfbad85f34f25ffa47",
@@ -66,7 +66,7 @@
   # Checkout extra javascript engines for testing or benchmarking.
   # d8, the V8 shell, is always checked out.
   "checkout_javascript_engines": False,
-  "d8_tag": "version:10.9.192",
+  "d8_tag": "version:11.1.11",
   "jsshell_tag": "version:95.0",
 
   # As Flutter does, we use Fuchsia's GN and Clang toolchain. These revision
@@ -111,7 +111,7 @@
   "csslib_rev": "34203c09f073ed8267f5d6e333daddb02e6ff609",
 
   # Prefer to use hashes of binaryen that have been reviewed & rolled into g3.
-  "binaryen_rev" : "7769139efbe818c7ba36d1a382db5114ebee9df8",
+  "binaryen_rev" : "12ad604c17407f6b36d52c6404f2dab32e5c7960",
 
   # Note: Updates to dart_style have to be coordinated with the infrastructure
   # team so that the internal formatter `tools/sdks/dart-sdk/bin/dart format`
diff --git a/pkg/dart2wasm/lib/code_generator.dart b/pkg/dart2wasm/lib/code_generator.dart
index bec4d6d..cc4854d 100644
--- a/pkg/dart2wasm/lib/code_generator.dart
+++ b/pkg/dart2wasm/lib/code_generator.dart
@@ -2660,8 +2660,7 @@
     translator.convertType(
         function, argLocal.type, info.struct.fields[fieldIndex].type.unpacked);
     b.struct_set(info.struct, fieldIndex);
-    translator.constants.instantiateConstant(
-        function, b, NullConstant(), translator.topInfo.nullableType);
+    b.local_get(argLocal);
     b.end();
   }
 
diff --git a/pkg/dart2wasm/lib/functions.dart b/pkg/dart2wasm/lib/functions.dart
index 916394b..96ce691 100644
--- a/pkg/dart2wasm/lib/functions.dart
+++ b/pkg/dart2wasm/lib/functions.dart
@@ -82,6 +82,18 @@
     String? exportName =
         translator.getPragma(member, "wasm:export", member.name.text);
     if (exportName != null) {
+      if (member is Procedure) {
+        // Although we don't need type unification for the types of exported
+        // functions, we still place these types in singleton recursion groups,
+        // since Binaryen's `--closed-world` optimization mode requires all
+        // publicly exposed types to be defined in separate recursion groups
+        // from GC types.
+        m.splitRecursionGroup();
+        _makeFunctionType(
+            translator, member.reference, member.function.returnType, null,
+            isImportOrExport: true);
+        m.splitRecursionGroup();
+      }
       addExport(member.reference, exportName);
     }
   }
diff --git a/pkg/dart2wasm/lib/translator.dart b/pkg/dart2wasm/lib/translator.dart
index 9f9d9c1..74a7223 100644
--- a/pkg/dart2wasm/lib/translator.dart
+++ b/pkg/dart2wasm/lib/translator.dart
@@ -84,6 +84,7 @@
   final Map<TypeParameter, int> typeParameterIndex = {};
   final Map<Reference, ParameterInfo> staticParamInfo = {};
   final Map<Field, w.DefinedTable> declaredTables = {};
+  final List<_ClosureTrampolineGenerator> pendingClosureTrampolines = [];
   late Procedure mainFunction;
   late final w.Module m;
   late final w.DefinedFunction initFunction;
@@ -287,6 +288,12 @@
       }
     }
 
+    // Use an indexed loop to handle pending closure trampolines, since new
+    // entries might be added during iteration.
+    for (int i = 0; i < pendingClosureTrampolines.length; i++) {
+      pendingClosureTrampolines[i].generate(this);
+    }
+
     dispatchTable.output();
     initFunction.body.end();
 
@@ -595,54 +602,20 @@
 
     w.DefinedFunction makeTrampoline(
         w.FunctionType signature, int posArgCount, List<String> argNames) {
-      w.DefinedFunction function = m.addFunction(signature, name);
-      w.Instructions b = function.body;
-      int targetIndex = 0;
-      if (takesContextOrReceiver) {
-        w.Local receiver = function.locals[0];
-        b.local_get(receiver);
-        convertType(function, receiver.type, target.type.inputs[targetIndex++]);
-      }
-      int argIndex = 1;
-      for (int i = 0; i < typeCount; i++) {
-        b.local_get(function.locals[argIndex++]);
-        targetIndex++;
-      }
-      for (int i = 0; i < paramInfo.positional.length; i++) {
-        if (i < posArgCount) {
-          w.Local arg = function.locals[argIndex++];
-          b.local_get(arg);
-          convertType(function, arg.type, target.type.inputs[targetIndex++]);
-        } else {
-          constants.instantiateConstant(function, b, paramInfo.positional[i]!,
-              target.type.inputs[targetIndex++]);
-        }
-      }
-      int argNameIndex = 0;
-      for (int i = 0; i < paramInfo.names.length; i++) {
-        String argName = paramInfo.names[i];
-        if (argNameIndex < argNames.length &&
-            argNames[argNameIndex] == argName) {
-          w.Local arg = function.locals[argIndex++];
-          b.local_get(arg);
-          convertType(function, arg.type, target.type.inputs[targetIndex++]);
-          argNameIndex++;
-        } else {
-          constants.instantiateConstant(function, b, paramInfo.named[argName]!,
-              target.type.inputs[targetIndex++]);
-        }
-      }
-      assert(argIndex == signature.inputs.length);
-      assert(targetIndex == target.type.inputs.length);
-      assert(argNameIndex == argNames.length);
+      w.DefinedFunction trampoline = m.addFunction(signature, name);
 
-      b.call(target);
+      // Defer generation of the trampoline body to avoid cyclic dependency when
+      // a tear-off constant is used as default value in the torn-off function.
+      pendingClosureTrampolines.add(_ClosureTrampolineGenerator(
+          trampoline,
+          target,
+          typeCount,
+          posArgCount,
+          argNames,
+          paramInfo,
+          takesContextOrReceiver));
 
-      convertType(function, outputOrVoid(target.type.outputs),
-          outputOrVoid(signature.outputs));
-      b.end();
-
-      return function;
+      return trampoline;
     }
 
     void fillVtableEntry(
@@ -890,6 +863,77 @@
   }
 }
 
+class _ClosureTrampolineGenerator {
+  final w.DefinedFunction trampoline;
+  final w.BaseFunction target;
+  final int typeCount;
+  final int posArgCount;
+  final List<String> argNames;
+  final ParameterInfo paramInfo;
+  final bool takesContextOrReceiver;
+
+  _ClosureTrampolineGenerator(
+      this.trampoline,
+      this.target,
+      this.typeCount,
+      this.posArgCount,
+      this.argNames,
+      this.paramInfo,
+      this.takesContextOrReceiver);
+
+  void generate(Translator translator) {
+    w.Instructions b = trampoline.body;
+    int targetIndex = 0;
+    if (takesContextOrReceiver) {
+      w.Local receiver = trampoline.locals[0];
+      b.local_get(receiver);
+      translator.convertType(
+          trampoline, receiver.type, target.type.inputs[targetIndex++]);
+    }
+    int argIndex = 1;
+    for (int i = 0; i < typeCount; i++) {
+      b.local_get(trampoline.locals[argIndex++]);
+      targetIndex++;
+    }
+    for (int i = 0; i < paramInfo.positional.length; i++) {
+      if (i < posArgCount) {
+        w.Local arg = trampoline.locals[argIndex++];
+        b.local_get(arg);
+        translator.convertType(
+            trampoline, arg.type, target.type.inputs[targetIndex++]);
+      } else {
+        translator.constants.instantiateConstant(trampoline, b,
+            paramInfo.positional[i]!, target.type.inputs[targetIndex++]);
+      }
+    }
+    int argNameIndex = 0;
+    for (int i = 0; i < paramInfo.names.length; i++) {
+      String argName = paramInfo.names[i];
+      if (argNameIndex < argNames.length && argNames[argNameIndex] == argName) {
+        w.Local arg = trampoline.locals[argIndex++];
+        b.local_get(arg);
+        translator.convertType(
+            trampoline, arg.type, target.type.inputs[targetIndex++]);
+        argNameIndex++;
+      } else {
+        translator.constants.instantiateConstant(trampoline, b,
+            paramInfo.named[argName]!, target.type.inputs[targetIndex++]);
+      }
+    }
+    assert(argIndex == trampoline.type.inputs.length);
+    assert(targetIndex == target.type.inputs.length);
+    assert(argNameIndex == argNames.length);
+
+    b.call(target);
+
+    translator.convertType(
+        trampoline,
+        translator.outputOrVoid(target.type.outputs),
+        translator.outputOrVoid(trampoline.type.outputs));
+    b.end();
+  }
+}
+
 class NodeCounter extends Visitor<void> with VisitorVoidMixin {
   int count = 0;
 
diff --git a/sdk/lib/_internal/wasm/lib/bool.dart b/sdk/lib/_internal/wasm/lib/bool.dart
index 2aeab66..a949961 100644
--- a/sdk/lib/_internal/wasm/lib/bool.dart
+++ b/sdk/lib/_internal/wasm/lib/bool.dart
@@ -4,6 +4,18 @@
 
 part of "core_patch.dart";
 
+@patch
+class bool {
+  // Note: this needs to be in `bool`, cannot be overridden in `_BoxedBool`. I
+  // suspect the problem is there's an assumption in the front-end that `bool`
+  // has one implementation class (unlike `double`, `int`, `String`) which is
+  // the `bool` class itself. So when `runtimeType` is not overridden in
+  // `bool`, in code like `x.runtimeType` where `x` is `bool`, direct call
+  // metadata says that the member is `Object.runtimeType`.
+  @override
+  Type get runtimeType => bool;
+}
+
 @pragma("wasm:entry-point")
 class _BoxedBool extends bool {
   // A boxed bool contains an unboxed bool.
diff --git a/tools/VERSION b/tools/VERSION
index ee0fc81..a077dcd 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 3
 MINOR 0
 PATCH 0
-PRERELEASE 53
+PRERELEASE 54
 PRERELEASE_PATCH 0