Version 2.18.0-44.0.dev

Merge commit 'd31c848c8a6875ed06f2833da976f78120d3ab06' into 'dev'
diff --git a/DEPS b/DEPS
index 2e636f4..f2884cf 100644
--- a/DEPS
+++ b/DEPS
@@ -151,7 +151,7 @@
   "source_map_stack_trace_rev": "8eabd96b1811e30a11d3c54c9b4afae4fb72e98f",
   "source_maps_rev": "c07a01b8d5547ce3a47ee7a7a2b938a2bc09afe3",
   "source_span_rev": "8ae724c3e67f5afaacead44e93ff145bfb8775c7",
-  "sse_rev": "9084339389eb441d0c0518cddac211a097e78657",
+  "sse_rev": "9a54f1cdd91c8d79a6bf5ef8e849a12756607453",
   "stack_trace_rev": "5220580872625ddee41e9ca9a5f3364789b2f0f6",
   "stream_channel_rev": "3fa3e40c75c210d617b8b943b9b8f580e9866a89",
   "string_scanner_rev": "0e53bf9059e8e22a3b346aac7ec755a0f8314eb6",
@@ -168,7 +168,7 @@
   "web_components_rev": "8f57dac273412a7172c8ade6f361b407e2e4ed02",
   "web_socket_channel_rev": "99dbdc5769e19b9eeaf69449a59079153c6a8b1f",
   "WebCore_rev": "bcb10901266c884e7b3740abc597ab95373ab55c",
-  "webdev_rev": "832b096c0c24798d3df46faa7b7661fe930573c2",
+  "webdev_rev": "8c814f9d89915418d8abe354ff9befec8f2906b2",
   "webdriver_rev": "ff5ccb1522edf4bed578ead4d65e0cbc1f2c4f02",
   "webkit_inspection_protocol_rev": "dd6fb5d8b536e19cedb384d0bbf1f5631923f1e8",
   "yaml_edit_rev": "4fadb43801b07f90b3f0c6065dbce4efc6d8d55e",
diff --git a/pkg/_js_interop_checks/lib/src/transformations/js_util_wasm_optimizer.dart b/pkg/_js_interop_checks/lib/src/transformations/js_util_wasm_optimizer.dart
index 220d07b..23e0093 100644
--- a/pkg/_js_interop_checks/lib/src/transformations/js_util_wasm_optimizer.dart
+++ b/pkg/_js_interop_checks/lib/src/transformations/js_util_wasm_optimizer.dart
@@ -49,9 +49,18 @@
   final Procedure _getPropertyTarget;
   final Procedure _setPropertyTarget;
   final Procedure _jsifyTarget;
+  final Procedure _jsifyRawTarget;
   final Procedure _dartifyTarget;
   final Procedure _newObjectTarget;
+  final Procedure _wrapDartCallbackTarget;
+  final Procedure _allowInteropTarget;
   final Class _jsValueClass;
+  final Class _wasmAnyRefClass;
+  final Class _objectClass;
+  final Class _pragmaClass;
+  final Field _pragmaName;
+  final Field _pragmaOptions;
+  int _callbackTrampolineN = 1;
 
   final CoreTypes _coreTypes;
   final StatefulStaticTypeContext _staticTypeContext;
@@ -71,12 +80,23 @@
             .getTopLevelProcedure('dart:js_util_wasm', 'setProperty'),
         _jsifyTarget =
             _coreTypes.index.getTopLevelProcedure('dart:js_util_wasm', 'jsify'),
+        _jsifyRawTarget = _coreTypes.index
+            .getTopLevelProcedure('dart:js_util_wasm', 'jsifyRaw'),
         _dartifyTarget = _coreTypes.index
             .getTopLevelProcedure('dart:js_util_wasm', 'dartify'),
+        _wrapDartCallbackTarget = _coreTypes.index
+            .getTopLevelProcedure('dart:js_util_wasm', '_wrapDartCallback'),
         _newObjectTarget = _coreTypes.index
             .getTopLevelProcedure('dart:js_util_wasm', 'newObject'),
+        _allowInteropTarget = _coreTypes.index
+            .getTopLevelProcedure('dart:js_util_wasm', 'allowInterop'),
         _jsValueClass =
             _coreTypes.index.getClass('dart:js_util_wasm', 'JSValue'),
+        _wasmAnyRefClass = _coreTypes.index.getClass('dart:wasm', 'WasmAnyRef'),
+        _objectClass = _coreTypes.objectClass,
+        _pragmaClass = _coreTypes.pragmaClass,
+        _pragmaName = _coreTypes.pragmaName,
+        _pragmaOptions = _coreTypes.pragmaOptions,
         _staticTypeContext = StatefulStaticTypeContext.stacked(
             TypeEnvironment(_coreTypes, hierarchy)) {}
 
@@ -99,6 +119,16 @@
   }
 
   @override
+  StaticInvocation visitStaticInvocation(StaticInvocation node) {
+    if (node.target == _allowInteropTarget) {
+      Expression argument = node.arguments.positional.single;
+      DartType functionType = argument.getStaticType(_staticTypeContext);
+      return _allowInterop(node.target, functionType as FunctionType, argument);
+    }
+    return node;
+  }
+
+  @override
   Procedure visitProcedure(Procedure node) {
     _staticTypeContext.enterMember(node);
     Statement? transformedBody;
@@ -184,8 +214,98 @@
   DartType get _nonNullableJSValueType =>
       _jsValueClass.getThisType(_coreTypes, Nullability.nonNullable);
 
-  Expression _jsifyVariable(VariableDeclaration variable) =>
-      StaticInvocation(_jsifyTarget, Arguments([VariableGet(variable)]));
+  Expression _dartify(Expression expression) =>
+      StaticInvocation(_dartifyTarget, Arguments([expression]));
+
+  /// Creates a callback trampoline for the given [function]. This callback
+  /// trampoline expects a Dart callback as its first argument, followed by all
+  /// of the arguments to the Dart callback as Dart objects. The trampoline will
+  /// cast all incoming Dart objects to the appropriate types, dispatch, and
+  /// then `jsifyRaw` any returned value. [_createCallbackTrampoline] Returns a
+  /// [String] function name representing the name of the wrapping function.
+  /// TODO(joshualitt): Share callback trampolines if the [FunctionType]
+  /// matches.
+  String _createCallbackTrampoline(Procedure node, FunctionType function) {
+    int fileOffset = node.fileOffset;
+    Library library = node.enclosingLibrary;
+
+    // Create arguments for each positional parameter in the function. These
+    // arguments will be converted in JS to Dart objects. The generated wrapper
+    // will cast each argument to the correct type.  The first argument to this
+    // function will be the Dart callback, which will be cast to the supplied
+    // [FunctionType] before being invoked.
+    int parameterId = 1;
+    DartType nonNullableObjectType =
+        _objectClass.getThisType(_coreTypes, Nullability.nonNullable);
+    final callbackVariable =
+        VariableDeclaration('callback', type: nonNullableObjectType);
+    List<VariableDeclaration> positionalParameters = [callbackVariable];
+    List<Expression> callbackArguments = [];
+    DartType nullableObjectType =
+        _objectClass.getThisType(_coreTypes, Nullability.nullable);
+    for (DartType type in function.positionalParameters) {
+      VariableDeclaration variable =
+          VariableDeclaration('x${parameterId++}', type: nullableObjectType);
+      positionalParameters.add(variable);
+      callbackArguments.add(AsExpression(VariableGet(variable), type));
+    }
+
+    // Create a new procedure for the callback trampoline. This procedure will
+    // be exported from Wasm to JS so it can be called from JS. The argument
+    // returned from the supplied callback will be converted with `jsifyRaw` to
+    // a native JS value before being returned to JS.
+    DartType nullableWasmAnyRefType =
+        _wasmAnyRefClass.getThisType(_coreTypes, Nullability.nullable);
+    final callbackTrampolineName =
+        '|_callbackTrampoline${_callbackTrampolineN++}';
+    final callbackTrampolineImportName = '\$$callbackTrampolineName';
+    final callbackTrampoline = Procedure(
+        Name(callbackTrampolineName, library),
+        ProcedureKind.Method,
+        FunctionNode(
+            ReturnStatement(StaticInvocation(
+                _jsifyRawTarget,
+                Arguments([
+                  FunctionInvocation(
+                      FunctionAccessKind.FunctionType,
+                      AsExpression(VariableGet(callbackVariable), function),
+                      Arguments(callbackArguments),
+                      functionType: function),
+                ]))),
+            positionalParameters: positionalParameters,
+            returnType: nullableWasmAnyRefType)
+          ..fileOffset = fileOffset,
+        isStatic: true,
+        fileUri: node.fileUri)
+      ..fileOffset = fileOffset
+      ..isNonNullableByDefault = true;
+    callbackTrampoline.addAnnotation(
+        ConstantExpression(InstanceConstant(_pragmaClass.reference, [], {
+      _pragmaName.fieldReference: StringConstant('wasm:export'),
+      _pragmaOptions.fieldReference:
+          StringConstant(callbackTrampolineImportName)
+    })));
+    library.addProcedure(callbackTrampoline);
+    return callbackTrampolineImportName;
+  }
+
+  /// Lowers a [StaticInvocation] of `allowInterop` to
+  /// [_createCallbackTrampoline] followed by `_wrapDartCallback`.
+  StaticInvocation _allowInterop(
+      Procedure node, FunctionType type, Expression argument) {
+    String callbackTrampolineName = _createCallbackTrampoline(node, type);
+    return StaticInvocation(_wrapDartCallbackTarget,
+        Arguments([argument, StringLiteral(callbackTrampolineName)]));
+  }
+
+  Expression _jsifyVariable(Procedure node, VariableDeclaration variable) {
+    if (variable.type is FunctionType) {
+      return _allowInterop(
+          node, variable.type as FunctionType, VariableGet(variable));
+    } else {
+      return StaticInvocation(_jsifyTarget, Arguments([VariableGet(variable)]));
+    }
+  }
 
   StaticInvocation get _globalThis =>
       StaticInvocation(_globalThisTarget, Arguments([]));
@@ -233,16 +353,15 @@
           _globalThis,
           StringLiteral(constructorName),
           ListLiteral(
-              function.positionalParameters.map(_jsifyVariable).toList(),
+              function.positionalParameters
+                  .map((arg) => _jsifyVariable(node, arg))
+                  .toList(),
               typeArgument: _nonNullableJSValueType)
         ]))
       ..fileOffset = node.fileOffset;
     return ReturnStatement(callConstructorInvocation);
   }
 
-  Expression _dartify(Expression expression) =>
-      StaticInvocation(_dartifyTarget, Arguments([expression]));
-
   /// Returns a new [Expression] for the given [node] external getter.
   ///
   /// The new [Expression] is equivalent to:
@@ -274,8 +393,10 @@
   /// `js_util_wasm.setProperty([object], [setterName], [value])`.
   Expression _setProperty(Procedure node, Expression object, String setterName,
           VariableDeclaration value) =>
-      StaticInvocation(_setPropertyTarget,
-          Arguments([object, StringLiteral(setterName), _jsifyVariable(value)]))
+      StaticInvocation(
+          _setPropertyTarget,
+          Arguments(
+              [object, StringLiteral(setterName), _jsifyVariable(node, value)]))
         ..fileOffset = node.fileOffset;
 
   /// Returns a new function body for the given [node] external setter.
@@ -306,7 +427,8 @@
         Arguments([
           object,
           StringLiteral(methodName),
-          ListLiteral(values.map(_jsifyVariable).toList(),
+          ListLiteral(
+              values.map((value) => _jsifyVariable(node, value)).toList(),
               typeArgument: _nullableJSValueType)
         ])))
       ..fileOffset = node.fileOffset;
diff --git a/pkg/compiler/test/dump_info/data/members.dart b/pkg/compiler/test/dump_info/data/members.dart
index 11d6982..326e97c 100644
--- a/pkg/compiler/test/dump_info/data/members.dart
+++ b/pkg/compiler/test/dump_info/data/members.dart
@@ -1,64 +1,78 @@
 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"
-}]*/
+  /*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"
+}],
+   holding=[
+    {"id":"function/dart:_js_helper::throwCyclicInit","mask":null},
+    {"id":"function/dart:_late_helper::throwLateFieldADI","mask":null}]
+  */
   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"
+  "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"
-}]*/
+  /*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"
+}],
+   holding=[
+    {"id":"field/memory:sdk/tests/web/native/main.dart::C.counter","mask":null},
+    {"id":"field/memory:sdk/tests/web/native/main.dart::C.counter","mask":null},
+    {"id":"function/dart:_js_helper::throwCyclicInit","mask":null},
+    {"id":"function/dart:_late_helper::throwLateFieldADI","mask":null},
+    {"id":"function/memory:sdk/tests/web/native/main.dart::C.compute","mask":"inlined"},
+    {"id":"function/memory:sdk/tests/web/native/main.dart::C.compute","mask":null}]
+  */
   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()"
+  "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;
@@ -66,178 +80,194 @@
   }
 
   /*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)"
+  "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)"
-}]*/
+  /*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)"
+}],
+   holding=[
+    {"id":"function/dart:core::Error.Error","mask":"inlined"},
+    {"id":"function/memory:sdk/tests/web/native/main.dart::C.C._default","mask":"inlined"}]
+  */
   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()"
+  "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"
+  "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()"
+  /*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>"
+  "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()"
-}]*/
+/*member: main:
+ function=[{
+  "id": "function/memory:sdk/tests/web/native/main.dart::main",
+  "kind": "function",
+  "name": "main",
+  "size": 191,
+  "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      var r = [];\n      r.push([B.List_A, B.C_A, A.main__F$closure(), $.$get$C_y(), false, \"hello\"]);\n      r.push(B.C_A);\n      r.push(new A.C());\n      A.printString(A.S(r));\n    }",
+  "type": "dynamic Function()"
+}],
+ holding=[
+  {"id":"field/memory:sdk/tests/web/native/main.dart::C.y","mask":null},
+  {"id":"function/dart:_internal::printToConsole","mask":null},
+  {"id":"function/dart:_js_helper::S","mask":null},
+  {"id":"function/dart:_js_primitives::printString","mask":null},
+  {"id":"function/dart:_rti::findType","mask":null},
+  {"id":"function/dart:core::Error.Error","mask":null},
+  {"id":"function/dart:core::print","mask":"inlined"},
+  {"id":"function/dart:core::print","mask":null},
+  {"id":"function/memory:sdk/tests/web/native/main.dart::A.A","mask":"inlined"},
+  {"id":"function/memory:sdk/tests/web/native/main.dart::A.A","mask":null},
+  {"id":"function/memory:sdk/tests/web/native/main.dart::C.C._default","mask":null},
+  {"id":"function/memory:sdk/tests/web/native/main.dart::C.C.create","mask":"inlined"},
+  {"id":"function/memory:sdk/tests/web/native/main.dart::C.C.create","mask":null}]
+*/
 main() {
-  dynamic l = [constList, const A(), B.a, B.a + 10, F, C.y, A().a];
-  dynamic r;
+  dynamic l = [constList, const A(), F, C.y, false, A().a];
+  dynamic r = [];
   r.add(l);
   r.add(const A());
   r.add(C.create(10));
diff --git a/pkg/compiler/test/dump_info/dump_info_test.dart b/pkg/compiler/test/dump_info/dump_info_test.dart
index 8410f9b..9a36426 100644
--- a/pkg/compiler/test/dump_info/dump_info_test.dart
+++ b/pkg/compiler/test/dump_info/dump_info_test.dart
@@ -46,13 +46,15 @@
 class DumpInfoDataComputer extends DataComputer<Features> {
   const DumpInfoDataComputer();
 
+  final JsonEncoder encoder = const JsonEncoder();
+  final JsonEncoder indentedEncoder = const JsonEncoder.withIndent('  ');
+
   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;
@@ -62,13 +64,21 @@
     if (functionInfo == null) return;
 
     if (functionInfo is info.FunctionInfo) {
-      features.addElement(
-          Tags.function, encoder.convert(functionInfo.accept(converter)));
+      features.addElement(Tags.function,
+          indentedEncoder.convert(functionInfo.accept(converter)));
+      for (var use in functionInfo.uses) {
+        features.addElement(
+            Tags.holding, encoder.convert(converter.visitDependencyInfo(use)));
+      }
     }
 
     if (functionInfo is info.FieldInfo) {
-      features.addElement(
-          Tags.function, encoder.convert(functionInfo.accept(converter)));
+      features.addElement(Tags.function,
+          indentedEncoder.convert(functionInfo.accept(converter)));
+      for (var use in functionInfo.uses) {
+        features.addElement(
+            Tags.holding, encoder.convert(converter.visitDependencyInfo(use)));
+      }
     }
 
     JsClosedWorld closedWorld = compiler.backendClosedWorldForTesting;
@@ -87,17 +97,16 @@
 
 /// Feature interpreter for Features with Json values.
 ///
-/// The data annotation reader removes whitespace, but this fork adds them
-/// back for readability.
+/// The data annotation reader conserves whitespace visually while ignoring
+/// them during comparison.
 class JsonFeaturesDataInterpreter implements DataInterpreter<Features> {
   final String wildcard;
+  final JsonEncoder encoder = const JsonEncoder();
 
   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 == '') {
@@ -121,8 +130,7 @@
           if (actualValue is List) {
             List actualList = actualValue.toList();
             for (Object expectedObject in expectedValue) {
-              String expectedText =
-                  encoder.convert(jsonDecode('$expectedObject'));
+              String expectedText = encoder.convert(jsonDecode(expectedObject));
               bool matchFound = false;
               if (wildcard != null && expectedText.endsWith(wildcard)) {
                 // Wildcard matcher.
@@ -130,7 +138,9 @@
                     expectedText.substring(0, expectedText.indexOf(wildcard));
                 List matches = [];
                 for (Object actualObject in actualList) {
-                  if ('$actualObject'.startsWith(prefix)) {
+                  var formattedActualObject =
+                      encoder.convert(jsonDecode(actualObject));
+                  if (formattedActualObject.startsWith(prefix)) {
                     matches.add(actualObject);
                     matchFound = true;
                   }
@@ -140,7 +150,9 @@
                 }
               } else {
                 for (Object actualObject in actualList) {
-                  if (expectedText == '$actualObject') {
+                  var formattedActualObject =
+                      encoder.convert(jsonDecode(actualObject));
+                  if (expectedText == formattedActualObject) {
                     actualList.remove(actualObject);
                     matchFound = true;
                     break;
diff --git a/pkg/dart2js_info/lib/json_info_codec.dart b/pkg/dart2js_info/lib/json_info_codec.dart
index 202b038..647559f 100644
--- a/pkg/dart2js_info/lib/json_info_codec.dart
+++ b/pkg/dart2js_info/lib/json_info_codec.dart
@@ -372,7 +372,7 @@
       assert(info.parent == null);
       assert(info.code != null);
       // Instead, use the content of the code.
-      name = info.code.first.text;
+      name = info.code.first.text ?? '';
     } else {
       name = longName(info, useLibraryUri: true, forId: true);
     }
@@ -427,7 +427,7 @@
     };
   }
 
-  Map _visitDependencyInfo(DependencyInfo info) =>
+  Map visitDependencyInfo(DependencyInfo info) =>
       {'id': idFor(info.target).serializedId, 'mask': info.mask};
 
   Map _visitAllInfoHolding(AllInfo allInfo) {
@@ -435,7 +435,7 @@
     void helper(CodeInfo info) {
       if (info.uses.isEmpty) return;
       map[idFor(info).serializedId] = info.uses
-          .map(_visitDependencyInfo)
+          .map(visitDependencyInfo)
           .toList()
         ..sort((a, b) => a['id'].compareTo(b['id']));
     }
diff --git a/pkg/dart2wasm/bin/run_wasm.js b/pkg/dart2wasm/bin/run_wasm.js
index a0ba219..9aeb626 100644
--- a/pkg/dart2wasm/bin/run_wasm.js
+++ b/pkg/dart2wasm/bin/run_wasm.js
@@ -64,12 +64,29 @@
     return array;
 }
 
+// TODO(joshualitt): Once we can properly return functions, then we can also try
+// returning a regular closure with some custom keys and a special symbol to
+// disambiguate it from other functions. I suspect this will also be necessary
+// for CSP.
+class WrappedDartCallback extends Function {
+    constructor(dartCallback, exportFunctionName) {
+        super('dartCallback', '...args',
+            `return dartInstance.exports['${exportFunctionName}'](
+                dartCallback, ...args.map(dartify));`);
+        this.bound = this.bind(this, dartCallback);
+        this.bound.dartCallback = dartCallback;
+        return this.bound;
+    }
+}
+
 // Recursively converts a JS object into a Dart object.
 function dartify(object) {
     if (typeof object === "string") {
         return stringToDartString(object);
     } else if (object instanceof Array) {
         return arrayToDartList(object);
+    } else if (object instanceof WrappedDartCallback) {
+        return object.dartCallback;
     } else if (object instanceof Object) {
         return dartInstance.exports.$boxJSValue(object);
     } else {
@@ -104,6 +121,9 @@
     arrayToDartList: arrayToDartList,
     stringFromDartString: stringFromDartString,
     stringToDartString: stringToDartString,
+    wrapDartCallback: function(dartCallback, exportFunctionName) {
+        return new WrappedDartCallback(dartCallback, exportFunctionName);
+    },
     dartify: dartify,
     newObject: function() {
         return {};
diff --git a/pkg/dart2wasm/lib/target.dart b/pkg/dart2wasm/lib/target.dart
index e2a182e..2a2f425 100644
--- a/pkg/dart2wasm/lib/target.dart
+++ b/pkg/dart2wasm/lib/target.dart
@@ -50,15 +50,16 @@
         'dart:typed_data',
         'dart:nativewrappers',
         'dart:js_util_wasm',
-        "dart:js_wasm",
+        'dart:js_wasm',
       ];
 
   @override
   List<String> get extraIndexedLibraries => const <String>[
-        "dart:collection",
-        "dart:typed_data",
+        'dart:collection',
+        'dart:typed_data',
         'dart:js_util_wasm',
-        "dart:js_wasm",
+        'dart:js_wasm',
+        'dart:wasm',
       ];
 
   void _patchHostEndian(CoreTypes coreTypes) {
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 29a216f..65866a4 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
@@ -2287,8 +2287,7 @@
         explicitTypeArguments == null &&
         calleeTypeParameters.isNotEmpty;
     bool typeChecksNeeded = !isTopLevel;
-    bool useFormalAndActualTypes = inferenceNeeded ||
-        typeChecksNeeded ||
+    bool useFormalAndActualTypes = typeChecksNeeded ||
         isSpecialCasedBinaryOperator ||
         isSpecialCasedTernaryOperator;
 
@@ -2472,6 +2471,7 @@
         NamedExpression namedArgument = arguments.named[index];
         namedArgument.value = expression..parent = namedArgument;
       }
+      gatherer?.tryConstrainLower(formalType, inferredType);
       if (useFormalAndActualTypes) {
         formalTypes!.add(formalType);
         actualTypes!.add(inferredType);
@@ -2574,8 +2574,7 @@
     }
 
     if (inferenceNeeded) {
-      gatherer!.constrainArguments(formalTypes!, actualTypes!);
-      typeSchemaEnvironment.upwardsInfer(gatherer, calleeTypeParameters,
+      typeSchemaEnvironment.upwardsInfer(gatherer!, calleeTypeParameters,
           inferredTypes!, libraryBuilder.library);
       assert(inferredTypes.every((type) => isKnown(type)),
           "Unknown type(s) in inferred types: $inferredTypes.");
diff --git a/pkg/frontend_server/lib/frontend_server.dart b/pkg/frontend_server/lib/frontend_server.dart
index a2c2d4e..710ba19 100644
--- a/pkg/frontend_server/lib/frontend_server.dart
+++ b/pkg/frontend_server/lib/frontend_server.dart
@@ -57,6 +57,8 @@
       help: 'Whether dart:mirrors is supported. By default dart:mirrors is '
           'supported when --aot and --minimal-kernel are not used.',
       defaultsTo: null)
+  ..addFlag('compact-async',
+      help: 'Enable new compact async/await implementation.', defaultsTo: null)
   ..addFlag('tfa',
       help:
           'Enable global type flow analysis and related transformations in AOT mode.',
@@ -538,6 +540,7 @@
       nullSafety: compilerOptions.nnbdMode == NnbdMode.Strong,
       supportMirrors: options['support-mirrors'] ??
           !(options['aot'] || options['minimal-kernel']),
+      compactAsync: options['compact-async'] ?? false /*options['aot']*/,
     );
     if (compilerOptions.target == null) {
       print('Failed to create front-end target ${options['target']}.');
diff --git a/pkg/kernel/binary.md b/pkg/kernel/binary.md
index 2bffc62..895c2ab 100644
--- a/pkg/kernel/binary.md
+++ b/pkg/kernel/binary.md
@@ -147,7 +147,7 @@
 
 type ComponentFile {
   UInt32 magic = 0x90ABCDEF;
-  UInt32 formatVersion = 78;
+  UInt32 formatVersion = 79;
   Byte[10] shortSdkHash;
   List<String> problemsAsJson; // Described in problems.md.
   Library[] libraries;
@@ -1070,6 +1070,7 @@
 
 type AwaitExpression extends Expression {
   Byte tag = 51;
+  FileOffset fileOffset;
   Expression operand;
 }
 
diff --git a/pkg/kernel/lib/binary/ast_from_binary.dart b/pkg/kernel/lib/binary/ast_from_binary.dart
index b6bbf7b..bc9a385 100644
--- a/pkg/kernel/lib/binary/ast_from_binary.dart
+++ b/pkg/kernel/lib/binary/ast_from_binary.dart
@@ -2602,7 +2602,8 @@
   }
 
   Expression _readAwaitExpression() {
-    return new AwaitExpression(readExpression());
+    int offset = readOffset();
+    return new AwaitExpression(readExpression())..fileOffset = offset;
   }
 
   Expression _readFunctionExpression() {
diff --git a/pkg/kernel/lib/binary/ast_to_binary.dart b/pkg/kernel/lib/binary/ast_to_binary.dart
index ce11597..3d18bb1 100644
--- a/pkg/kernel/lib/binary/ast_to_binary.dart
+++ b/pkg/kernel/lib/binary/ast_to_binary.dart
@@ -1986,6 +1986,7 @@
   @override
   void visitAwaitExpression(AwaitExpression node) {
     writeByte(Tag.AwaitExpression);
+    writeOffset(node.fileOffset);
     writeNode(node.operand);
   }
 
diff --git a/pkg/kernel/lib/binary/tag.dart b/pkg/kernel/lib/binary/tag.dart
index e19e36c..f589ea5 100644
--- a/pkg/kernel/lib/binary/tag.dart
+++ b/pkg/kernel/lib/binary/tag.dart
@@ -176,7 +176,7 @@
   /// Internal version of kernel binary format.
   /// Bump it when making incompatible changes in kernel binaries.
   /// Keep in sync with runtime/vm/kernel_binary.h, pkg/kernel/binary.md.
-  static const int BinaryFormatVersion = 78;
+  static const int BinaryFormatVersion = 79;
 }
 
 abstract class ConstantTag {
diff --git a/pkg/kernel/lib/target/targets.dart b/pkg/kernel/lib/target/targets.dart
index 4fcb938..90f85e8 100644
--- a/pkg/kernel/lib/target/targets.dart
+++ b/pkg/kernel/lib/target/targets.dart
@@ -16,11 +16,13 @@
   final bool trackWidgetCreation;
   final bool enableNullSafety;
   final bool supportMirrors;
+  final bool compactAsync;
 
   const TargetFlags(
       {this.trackWidgetCreation = false,
       this.enableNullSafety = false,
-      this.supportMirrors = true});
+      this.supportMirrors = true,
+      this.compactAsync = false});
 
   @override
   bool operator ==(other) {
@@ -28,7 +30,8 @@
     return other is TargetFlags &&
         trackWidgetCreation == other.trackWidgetCreation &&
         enableNullSafety == other.enableNullSafety &&
-        supportMirrors == other.supportMirrors;
+        supportMirrors == other.supportMirrors &&
+        compactAsync == other.compactAsync;
   }
 
   @override
@@ -37,6 +40,7 @@
     hash = 0x3fffffff & (hash * 31 + (hash ^ trackWidgetCreation.hashCode));
     hash = 0x3fffffff & (hash * 31 + (hash ^ enableNullSafety.hashCode));
     hash = 0x3fffffff & (hash * 31 + (hash ^ supportMirrors.hashCode));
+    hash = 0x3fffffff & (hash * 31 + (hash ^ compactAsync.hashCode));
     return hash;
   }
 }
@@ -547,6 +551,8 @@
   Class? concreteDoubleLiteralClass(CoreTypes coreTypes, double value) => null;
   Class? concreteStringLiteralClass(CoreTypes coreTypes, String value) => null;
 
+  Class? concreteAsyncResultClass(CoreTypes coreTypes) => null;
+
   ConstantsBackend get constantsBackend;
 
   /// Returns an [DartLibrarySupport] the defines which, if any, of the
diff --git a/pkg/vm/lib/kernel_front_end.dart b/pkg/vm/lib/kernel_front_end.dart
index 79aaf36..9ced5db 100644
--- a/pkg/vm/lib/kernel_front_end.dart
+++ b/pkg/vm/lib/kernel_front_end.dart
@@ -80,6 +80,8 @@
       help: 'Whether dart:mirrors is supported. By default dart:mirrors is '
           'supported when --aot and --minimal-kernel are not used.',
       defaultsTo: null);
+  args.addFlag('compact-async',
+      help: 'Enable new compact async/await implementation.', defaultsTo: null);
   args.addOption('depfile', help: 'Path to output Ninja depfile');
   args.addOption('from-dill',
       help: 'Read existing dill file instead of compiling from sources',
@@ -200,6 +202,7 @@
   final String? manifestFilename = options['manifest'];
   final String? dataDir = options['component-name'] ?? options['data-dir'];
   final bool? supportMirrors = options['support-mirrors'];
+  final bool compactAsync = options['compact-async'] ?? false /*aot*/;
 
   final bool minimalKernel = options['minimal-kernel'];
   final bool treeShakeWriteOnlyFields = options['tree-shake-write-only-fields'];
@@ -283,7 +286,8 @@
   compilerOptions.target = createFrontEndTarget(targetName,
       trackWidgetCreation: options['track-widget-creation'],
       nullSafety: compilerOptions.nnbdMode == NnbdMode.Strong,
-      supportMirrors: supportMirrors ?? !(aot || minimalKernel));
+      supportMirrors: supportMirrors ?? !(aot || minimalKernel),
+      compactAsync: compactAsync);
   if (compilerOptions.target == null) {
     print('Failed to create front-end target $targetName.');
     return badUsageExitCode;
@@ -612,14 +616,16 @@
 Target? createFrontEndTarget(String targetName,
     {bool trackWidgetCreation = false,
     bool nullSafety = false,
-    bool supportMirrors = true}) {
+    bool supportMirrors = true,
+    bool compactAsync = false}) {
   // Make sure VM-specific targets are available.
   installAdditionalTargets();
 
   final TargetFlags targetFlags = new TargetFlags(
       trackWidgetCreation: trackWidgetCreation,
       enableNullSafety: nullSafety,
-      supportMirrors: supportMirrors);
+      supportMirrors: supportMirrors,
+      compactAsync: compactAsync);
   return getTarget(targetName, targetFlags);
 }
 
diff --git a/pkg/vm/lib/target/vm.dart b/pkg/vm/lib/target/vm.dart
index 487d337..7efa5d9 100644
--- a/pkg/vm/lib/target/vm.dart
+++ b/pkg/vm/lib/target/vm.dart
@@ -186,7 +186,7 @@
     bool productMode = environmentDefines!["dart.vm.product"] == "true";
     transformAsync.transformLibraries(
         new TypeEnvironment(coreTypes, hierarchy), libraries,
-        productMode: productMode);
+        productMode: productMode, desugarAsync: !flags.compactAsync);
     logger?.call("Transformed async methods");
 
     lowering.transformLibraries(
@@ -208,7 +208,7 @@
     bool productMode = environmentDefines!["dart.vm.product"] == "true";
     transformAsync.transformProcedure(
         new TypeEnvironment(coreTypes, hierarchy), procedure,
-        productMode: productMode);
+        productMode: productMode, desugarAsync: flags.supportMirrors);
     logger?.call("Transformed async functions");
 
     lowering.transformProcedure(
@@ -497,6 +497,10 @@
   }
 
   @override
+  Class? concreteAsyncResultClass(CoreTypes coreTypes) =>
+      coreTypes.futureImplClass;
+
+  @override
   ConstantsBackend get constantsBackend => const ConstantsBackend();
 
   @override
diff --git a/pkg/vm/lib/transformations/async.dart b/pkg/vm/lib/transformations/async.dart
index e117bc6..ecdec2e 100644
--- a/pkg/vm/lib/transformations/async.dart
+++ b/pkg/vm/lib/transformations/async.dart
@@ -618,7 +618,9 @@
   @override
   TreeNode visitFunctionNode(FunctionNode node) {
     var nestedRewriter = new RecursiveContinuationRewriter(
-        continuationRewriter.helper, _staticTypeContext);
+        continuationRewriter.helper,
+        _staticTypeContext,
+        continuationRewriter.desugarAsync);
     return nestedRewriter.transform(node);
   }
 
diff --git a/pkg/vm/lib/transformations/continuation.dart b/pkg/vm/lib/transformations/continuation.dart
index b5c3949..455b281 100644
--- a/pkg/vm/lib/transformations/continuation.dart
+++ b/pkg/vm/lib/transformations/continuation.dart
@@ -46,11 +46,11 @@
 
 void transformLibraries(
     TypeEnvironment typeEnvironment, List<Library> libraries,
-    {required bool productMode}) {
+    {required bool productMode, required bool desugarAsync}) {
   var helper =
       new HelperNodes.fromCoreTypes(typeEnvironment.coreTypes, productMode);
-  var rewriter = new RecursiveContinuationRewriter(
-      helper, new StatefulStaticTypeContext.stacked(typeEnvironment));
+  var rewriter = new RecursiveContinuationRewriter(helper,
+      new StatefulStaticTypeContext.stacked(typeEnvironment), desugarAsync);
   for (var library in libraries) {
     rewriter.rewriteLibrary(library);
   }
@@ -58,21 +58,21 @@
 
 Component transformComponent(
     TypeEnvironment typeEnvironment, Component component,
-    {required bool productMode}) {
+    {required bool productMode, required bool desugarAsync}) {
   var helper =
       new HelperNodes.fromCoreTypes(typeEnvironment.coreTypes, productMode);
-  var rewriter = new RecursiveContinuationRewriter(
-      helper, new StatefulStaticTypeContext.stacked(typeEnvironment));
+  var rewriter = new RecursiveContinuationRewriter(helper,
+      new StatefulStaticTypeContext.stacked(typeEnvironment), desugarAsync);
   return rewriter.rewriteComponent(component);
 }
 
 Procedure transformProcedure(
     TypeEnvironment typeEnvironment, Procedure procedure,
-    {required bool productMode}) {
+    {required bool productMode, required bool desugarAsync}) {
   var helper =
       new HelperNodes.fromCoreTypes(typeEnvironment.coreTypes, productMode);
-  var rewriter = new RecursiveContinuationRewriter(
-      helper, new StatefulStaticTypeContext.stacked(typeEnvironment));
+  var rewriter = new RecursiveContinuationRewriter(helper,
+      new StatefulStaticTypeContext.stacked(typeEnvironment), desugarAsync);
   return rewriter.transform(procedure);
 }
 
@@ -86,8 +86,12 @@
       new VariableDeclaration(ContinuationVariables.awaitContextVar);
 
   StatefulStaticTypeContext staticTypeContext;
+  final bool desugarAsync;
+  final bool desugarAwaitFor;
 
-  RecursiveContinuationRewriter(this.helper, this.staticTypeContext);
+  RecursiveContinuationRewriter(
+      this.helper, this.staticTypeContext, this.desugarAsync,
+      {this.desugarAwaitFor = false});
 
   Component rewriteComponent(Component node) {
     return transform(node);
@@ -135,17 +139,27 @@
     switch (node.asyncMarker) {
       case AsyncMarker.Sync:
       case AsyncMarker.SyncYielding:
-        node.transformOrRemoveChildren(
-            new RecursiveContinuationRewriter(helper, staticTypeContext));
+        node.transformOrRemoveChildren(new RecursiveContinuationRewriter(
+            helper, staticTypeContext, desugarAsync));
         return node;
       case AsyncMarker.SyncStar:
-        return new SyncStarFunctionRewriter(helper, node, staticTypeContext)
+        return new SyncStarFunctionRewriter(
+                helper, node, staticTypeContext, desugarAsync)
             .rewrite();
       case AsyncMarker.Async:
-        return new AsyncFunctionRewriter(helper, node, staticTypeContext)
-            .rewrite();
+        if (desugarAsync) {
+          return new AsyncFunctionRewriter(
+                  helper, node, staticTypeContext, desugarAsync)
+              .rewrite();
+        } else {
+          node.transformOrRemoveChildren(new RecursiveContinuationRewriter(
+              helper, staticTypeContext, desugarAsync,
+              desugarAwaitFor: true));
+          return node;
+        }
       case AsyncMarker.AsyncStar:
-        return new AsyncStarFunctionRewriter(helper, node, staticTypeContext)
+        return new AsyncStarFunctionRewriter(
+                helper, node, staticTypeContext, desugarAsync)
             .rewrite();
     }
   }
@@ -153,7 +167,125 @@
   @override
   TreeNode visitForInStatement(ForInStatement stmt, TreeNode? removalSentinel) {
     if (stmt.isAsync) {
-      return super.visitForInStatement(stmt, removalSentinel);
+      if (!desugarAwaitFor) {
+        return super.visitForInStatement(stmt, removalSentinel);
+      }
+      // Transform
+      //
+      //   await for (T variable in <stream-expression>) { ... }
+      //
+      // To (in product mode):
+      //
+      //   {
+      //     :stream = <stream-expression>;
+      //     _StreamIterator<T> :for-iterator = new _StreamIterator<T>(:stream);
+      //     try {
+      //       while (await :for-iterator.moveNext()) {
+      //         T <variable> = :for-iterator.current;
+      //         ...
+      //       }
+      //     } finally {
+      //       if (:for-iterator._subscription != null)
+      //           await :for-iterator.cancel();
+      //     }
+      //   }
+      //
+      // Or (in non-product mode):
+      //
+      //   {
+      //     :stream = <stream-expression>;
+      //     _StreamIterator<T> :for-iterator = new _StreamIterator<T>(:stream);
+      //     try {
+      //       while (let _ = _asyncStarMoveNextHelper(:stream) in
+      //           await :for-iterator.moveNext()) {
+      //         T <variable> = :for-iterator.current;
+      //         ...
+      //       }
+      //     } finally {
+      //       if (:for-iterator._subscription != null)
+      //           await :for-iterator.cancel();
+      //     }
+      //   }
+      var valueVariable = stmt.variable;
+
+      var streamVariable = new VariableDeclaration(ContinuationVariables.stream,
+          initializer: stmt.iterable,
+          type: stmt.iterable.getStaticType(staticTypeContext));
+
+      final streamIteratorType = new InterfaceType(helper.streamIteratorClass,
+          staticTypeContext.nullable, [valueVariable.type]);
+      var forIteratorVariable = VariableDeclaration(
+          ContinuationVariables.forIterator,
+          initializer: new ConstructorInvocation(
+              helper.streamIteratorConstructor,
+              new Arguments(<Expression>[new VariableGet(streamVariable)],
+                  types: [valueVariable.type])),
+          type: streamIteratorType);
+
+      // await :for-iterator.moveNext()
+      var condition = new AwaitExpression(new InstanceInvocation(
+          InstanceAccessKind.Instance,
+          VariableGet(forIteratorVariable),
+          helper.streamIteratorMoveNext.name,
+          new Arguments([]),
+          interfaceTarget: helper.streamIteratorMoveNext,
+          functionType:
+              helper.streamIteratorMoveNext.getterType as FunctionType))
+        ..fileOffset = stmt.fileOffset;
+
+      Expression whileCondition;
+      if (helper.productMode) {
+        whileCondition = condition;
+      } else {
+        // _asyncStarMoveNextHelper(:stream)
+        var asyncStarMoveNextCall = new StaticInvocation(
+            helper.asyncStarMoveNextHelper,
+            new Arguments([new VariableGet(streamVariable)]))
+          ..fileOffset = stmt.fileOffset;
+
+        // let _ = asyncStarMoveNextCall in (condition)
+        whileCondition = new Let(
+            new VariableDeclaration(null, initializer: asyncStarMoveNextCall),
+            condition);
+      }
+
+      // T <variable> = :for-iterator.current;
+      valueVariable.initializer = new InstanceGet(InstanceAccessKind.Instance,
+          VariableGet(forIteratorVariable), helper.streamIteratorCurrent.name,
+          interfaceTarget: helper.streamIteratorCurrent,
+          resultType: valueVariable.type)
+        ..fileOffset = stmt.bodyOffset;
+      valueVariable.initializer!.parent = valueVariable;
+
+      var whileBody = new Block(<Statement>[valueVariable, stmt.body]);
+      var tryBody = new WhileStatement(whileCondition, whileBody);
+
+      // if (:for-iterator._subscription != null) await :for-iterator.cancel();
+      final DartType subscriptionType =
+          Substitution.fromInterfaceType(streamIteratorType).substituteType(
+              helper.coreTypes.streamIteratorSubscription.getterType);
+      var tryFinalizer = new IfStatement(
+          new Not(new EqualsNull(new InstanceGet(
+              InstanceAccessKind.Instance,
+              VariableGet(forIteratorVariable),
+              helper.coreTypes.streamIteratorSubscription.name,
+              interfaceTarget: helper.coreTypes.streamIteratorSubscription,
+              resultType: subscriptionType))),
+          new ExpressionStatement(new AwaitExpression(new InstanceInvocation(
+              InstanceAccessKind.Instance,
+              VariableGet(forIteratorVariable),
+              helper.streamIteratorCancel.name,
+              new Arguments(<Expression>[]),
+              interfaceTarget: helper.streamIteratorCancel,
+              functionType:
+                  helper.streamIteratorCancel.getterType as FunctionType))),
+          null);
+
+      var tryFinally = new TryFinally(tryBody, tryFinalizer);
+
+      var block = new Block(
+          <Statement>[streamVariable, forIteratorVariable, tryFinally]);
+      return transform(block);
     }
 
     // Transform
@@ -238,8 +370,10 @@
   int capturedCatchDepth = 0; // Deepest yield point within a catch-block.
 
   ContinuationRewriterBase(HelperNodes helper, this.enclosingFunction,
-      StatefulStaticTypeContext staticTypeContext)
-      : super(helper, staticTypeContext);
+      StatefulStaticTypeContext staticTypeContext, bool desugarAsync,
+      {bool desugarAwaitFor = false})
+      : super(helper, staticTypeContext, desugarAsync,
+            desugarAwaitFor: desugarAwaitFor);
 
   /// Given a container [type], which is an instantiation of the given
   /// [containerClass] extract its element type.
@@ -394,7 +528,7 @@
   final VariableDeclaration iteratorParameter;
 
   SyncStarFunctionRewriter(HelperNodes helper, FunctionNode enclosingFunction,
-      StatefulStaticTypeContext staticTypeContext)
+      StatefulStaticTypeContext staticTypeContext, bool desugarAsync)
       : iteratorParameter =
             VariableDeclaration(ContinuationVariables.iteratorParam)
               ..type = InterfaceType(
@@ -404,7 +538,8 @@
                 // code.
                 const DynamicType(),
               ]),
-        super(helper, enclosingFunction, staticTypeContext);
+        super(helper, enclosingFunction, staticTypeContext, desugarAsync,
+            desugarAwaitFor: false);
 
   FunctionNode rewrite() {
     // We need to preserve the original parameters passed to the sync* function
@@ -548,7 +683,7 @@
   ExpressionLifter? expressionRewriter;
 
   AsyncRewriterBase(HelperNodes helper, FunctionNode enclosingFunction,
-      StatefulStaticTypeContext staticTypeContext)
+      StatefulStaticTypeContext staticTypeContext, bool desugarAsync)
       : nestedClosureVariable =
             VariableDeclaration(ContinuationVariables.asyncOp,
                 type: FunctionType([
@@ -567,7 +702,8 @@
                   helper.coreTypes
                       .stackTraceRawType(staticTypeContext.nonNullable),
                 ], const DynamicType(), staticTypeContext.nonNullable)),
-        super(helper, enclosingFunction, staticTypeContext) {}
+        super(helper, enclosingFunction, staticTypeContext, desugarAsync,
+            desugarAwaitFor: true) {}
 
   void setupAsyncContinuations(List<Statement> statements) {
     expressionRewriter = new ExpressionLifter(this);
@@ -978,132 +1114,6 @@
   }
 
   @override
-  TreeNode visitForInStatement(ForInStatement stmt, TreeNode? removalSentinel) {
-    if (stmt.isAsync) {
-      // Transform
-      //
-      //   await for (T variable in <stream-expression>) { ... }
-      //
-      // To (in product mode):
-      //
-      //   {
-      //     :stream = <stream-expression>;
-      //     _StreamIterator<T> :for-iterator = new _StreamIterator<T>(:stream);
-      //     try {
-      //       while (await :for-iterator.moveNext()) {
-      //         T <variable> = :for-iterator.current;
-      //         ...
-      //       }
-      //     } finally {
-      //       if (:for-iterator._subscription != null)
-      //           await :for-iterator.cancel();
-      //     }
-      //   }
-      //
-      // Or (in non-product mode):
-      //
-      //   {
-      //     :stream = <stream-expression>;
-      //     _StreamIterator<T> :for-iterator = new _StreamIterator<T>(:stream);
-      //     try {
-      //       while (let _ = _asyncStarMoveNextHelper(:stream) in
-      //           await :for-iterator.moveNext()) {
-      //         T <variable> = :for-iterator.current;
-      //         ...
-      //       }
-      //     } finally {
-      //       if (:for-iterator._subscription != null)
-      //           await :for-iterator.cancel();
-      //     }
-      //   }
-      var valueVariable = stmt.variable;
-
-      var streamVariable = new VariableDeclaration(ContinuationVariables.stream,
-          initializer: stmt.iterable,
-          type: stmt.iterable.getStaticType(staticTypeContext));
-
-      final streamIteratorType = new InterfaceType(helper.streamIteratorClass,
-          staticTypeContext.nullable, [valueVariable.type]);
-      var forIteratorVariable = VariableDeclaration(
-          ContinuationVariables.forIterator,
-          initializer: new ConstructorInvocation(
-              helper.streamIteratorConstructor,
-              new Arguments(<Expression>[new VariableGet(streamVariable)],
-                  types: [valueVariable.type])),
-          type: streamIteratorType);
-
-      // await :for-iterator.moveNext()
-      var condition = new AwaitExpression(new InstanceInvocation(
-          InstanceAccessKind.Instance,
-          VariableGet(forIteratorVariable),
-          helper.streamIteratorMoveNext.name,
-          new Arguments([]),
-          interfaceTarget: helper.streamIteratorMoveNext,
-          functionType:
-              helper.streamIteratorMoveNext.getterType as FunctionType))
-        ..fileOffset = stmt.fileOffset;
-
-      Expression whileCondition;
-      if (helper.productMode) {
-        whileCondition = condition;
-      } else {
-        // _asyncStarMoveNextHelper(:stream)
-        var asyncStarMoveNextCall = new StaticInvocation(
-            helper.asyncStarMoveNextHelper,
-            new Arguments([new VariableGet(streamVariable)]))
-          ..fileOffset = stmt.fileOffset;
-
-        // let _ = asyncStarMoveNextCall in (condition)
-        whileCondition = new Let(
-            new VariableDeclaration(null, initializer: asyncStarMoveNextCall),
-            condition);
-      }
-
-      // T <variable> = :for-iterator.current;
-      valueVariable.initializer = new InstanceGet(InstanceAccessKind.Instance,
-          VariableGet(forIteratorVariable), helper.streamIteratorCurrent.name,
-          interfaceTarget: helper.streamIteratorCurrent,
-          resultType: valueVariable.type)
-        ..fileOffset = stmt.bodyOffset;
-      valueVariable.initializer!.parent = valueVariable;
-
-      var whileBody = new Block(<Statement>[valueVariable, stmt.body]);
-      var tryBody = new WhileStatement(whileCondition, whileBody);
-
-      // if (:for-iterator._subscription != null) await :for-iterator.cancel();
-      final DartType subscriptionType =
-          Substitution.fromInterfaceType(streamIteratorType).substituteType(
-              helper.coreTypes.streamIteratorSubscription.getterType);
-      var tryFinalizer = new IfStatement(
-          new Not(new EqualsNull(new InstanceGet(
-              InstanceAccessKind.Instance,
-              VariableGet(forIteratorVariable),
-              helper.coreTypes.streamIteratorSubscription.name,
-              interfaceTarget: helper.coreTypes.streamIteratorSubscription,
-              resultType: subscriptionType))),
-          new ExpressionStatement(new AwaitExpression(new InstanceInvocation(
-              InstanceAccessKind.Instance,
-              VariableGet(forIteratorVariable),
-              helper.streamIteratorCancel.name,
-              new Arguments(<Expression>[]),
-              interfaceTarget: helper.streamIteratorCancel,
-              functionType:
-                  helper.streamIteratorCancel.getterType as FunctionType))),
-          null);
-
-      var tryFinally = new TryFinally(tryBody, tryFinalizer);
-
-      var block = new Block(
-          <Statement>[streamVariable, forIteratorVariable, tryFinally]);
-      transform<Statement>(block);
-      return removalSentinel ?? EmptyStatement();
-    } else {
-      super.visitForInStatement(stmt, removalSentinel);
-      return removalSentinel ?? EmptyStatement();
-    }
-  }
-
-  @override
   TreeNode visitSwitchStatement(
       SwitchStatement stmt, TreeNode? removalSentinel) {
     stmt.expression = expressionRewriter!.rewrite(stmt.expression, statements)
@@ -1200,8 +1210,8 @@
   VariableDeclaration? controllerVariable;
 
   AsyncStarFunctionRewriter(HelperNodes helper, FunctionNode enclosingFunction,
-      StatefulStaticTypeContext staticTypeContext)
-      : super(helper, enclosingFunction, staticTypeContext);
+      StatefulStaticTypeContext staticTypeContext, bool desugarAsync)
+      : super(helper, enclosingFunction, staticTypeContext, desugarAsync);
 
   FunctionNode rewrite() {
     var statements = <Statement>[];
@@ -1355,8 +1365,8 @@
   late bool canReturnFuture;
 
   AsyncFunctionRewriter(HelperNodes helper, FunctionNode enclosingFunction,
-      StatefulStaticTypeContext staticTypeContext)
-      : super(helper, enclosingFunction, staticTypeContext);
+      StatefulStaticTypeContext staticTypeContext, bool desugarAsync)
+      : super(helper, enclosingFunction, staticTypeContext, desugarAsync);
 
   FunctionNode rewrite() {
     var statements = <Statement>[];
diff --git a/pkg/vm/lib/transformations/type_flow/summary_collector.dart b/pkg/vm/lib/transformations/type_flow/summary_collector.dart
index a72ad85..7ce0b51 100644
--- a/pkg/vm/lib/transformations/type_flow/summary_collector.dart
+++ b/pkg/vm/lib/transformations/type_flow/summary_collector.dart
@@ -776,7 +776,25 @@
         _returnValue!.values.add(_boolType);
       }
 
-      _summary.result = _returnValue!;
+      switch (function.asyncMarker) {
+        case AsyncMarker.Async:
+          final Class? concreteClass =
+              target.concreteAsyncResultClass(_environment.coreTypes);
+          _summary.result = (concreteClass != null)
+              ? _entryPointsListener
+                  .addAllocatedClass(concreteClass)
+                  .cls
+                  .concreteType
+              : _typesBuilder.fromStaticType(function.returnType, false);
+          break;
+        case AsyncMarker.AsyncStar:
+        case AsyncMarker.SyncStar:
+          _summary.result =
+              _typesBuilder.fromStaticType(function.returnType, false);
+          break;
+        default:
+          _summary.result = _returnValue!;
+      }
     }
 
     member.annotations.forEach(_visit);
@@ -2266,6 +2284,12 @@
   TypeExpr visitConstantExpression(ConstantExpression node) {
     return constantAllocationCollector.typeFor(node.constant);
   }
+
+  @override
+  TypeExpr visitAwaitExpression(AwaitExpression node) {
+    _visit(node.operand);
+    return _staticType(node);
+  }
 }
 
 class RuntimeTypeTranslatorImpl extends DartTypeVisitor<TypeExpr>
diff --git a/runtime/tests/vm/vm.status b/runtime/tests/vm/vm.status
index 5b5e706..e9b73af 100644
--- a/runtime/tests/vm/vm.status
+++ b/runtime/tests/vm/vm.status
@@ -15,6 +15,7 @@
 dart/boxmint_test: Pass, Slow # Uses slow path
 dart/byte_array_optimized_test: Pass, Slow
 dart/data_uri_import_test/none: SkipByDesign
+dart/disassemble_aot_test: Pass, Slow # Spawns several subprocesses
 dart/emit_aot_size_info_flag_test: Pass, Slow # Spawns several subprocesses
 dart/hash_map_probes_limit_test: Pass, Slow # Test includes large program compilation.
 dart/isolates/*: Pass, Slow # Tests use many isolates and take a longer time.
@@ -30,6 +31,7 @@
 dart_2/boxmint_test: Pass, Slow # Uses slow path
 dart_2/byte_array_optimized_test: Pass, Slow
 dart_2/data_uri_import_test/none: SkipByDesign
+dart_2/disassemble_aot_test: Pass, Slow # Spawns several subprocesses
 dart_2/emit_aot_size_info_flag_test: Pass, Slow # Spawns several subprocesses
 dart_2/hash_map_probes_limit_test: Pass, Slow # Test includes large program compilation.
 dart_2/isolates/*: Pass, Slow # Tests use many isolates and take a longer time.
diff --git a/runtime/vm/compiler/runtime_offsets_extracted.h b/runtime/vm/compiler/runtime_offsets_extracted.h
index 78c6e5d..04a65b3 100644
--- a/runtime/vm/compiler/runtime_offsets_extracted.h
+++ b/runtime/vm/compiler/runtime_offsets_extracted.h
@@ -2815,11 +2815,11 @@
     36;
 static constexpr dart::compiler::target::word Finalizer_callback_offset = 32;
 static constexpr dart::compiler::target::word FinalizerBase_all_entries_offset =
-    20;
+    24;
 static constexpr dart::compiler::target::word FinalizerBase_detachments_offset =
-    16;
+    20;
 static constexpr dart::compiler::target::word
-    FinalizerBase_entries_collected_offset = 24;
+    FinalizerBase_entries_collected_offset = 28;
 static constexpr dart::compiler::target::word FinalizerBase_isolate_offset = 8;
 static constexpr dart::compiler::target::word FinalizerEntry_detach_offset = 12;
 static constexpr dart::compiler::target::word
@@ -3411,11 +3411,11 @@
     36;
 static constexpr dart::compiler::target::word Finalizer_callback_offset = 32;
 static constexpr dart::compiler::target::word FinalizerBase_all_entries_offset =
-    20;
+    24;
 static constexpr dart::compiler::target::word FinalizerBase_detachments_offset =
-    16;
+    20;
 static constexpr dart::compiler::target::word
-    FinalizerBase_entries_collected_offset = 24;
+    FinalizerBase_entries_collected_offset = 28;
 static constexpr dart::compiler::target::word FinalizerBase_isolate_offset = 8;
 static constexpr dart::compiler::target::word FinalizerEntry_detach_offset = 12;
 static constexpr dart::compiler::target::word
@@ -7542,11 +7542,11 @@
     36;
 static constexpr dart::compiler::target::word Finalizer_callback_offset = 32;
 static constexpr dart::compiler::target::word FinalizerBase_all_entries_offset =
-    20;
+    24;
 static constexpr dart::compiler::target::word FinalizerBase_detachments_offset =
-    16;
+    20;
 static constexpr dart::compiler::target::word
-    FinalizerBase_entries_collected_offset = 24;
+    FinalizerBase_entries_collected_offset = 28;
 static constexpr dart::compiler::target::word FinalizerBase_isolate_offset = 8;
 static constexpr dart::compiler::target::word FinalizerEntry_detach_offset = 12;
 static constexpr dart::compiler::target::word
@@ -8132,11 +8132,11 @@
     36;
 static constexpr dart::compiler::target::word Finalizer_callback_offset = 32;
 static constexpr dart::compiler::target::word FinalizerBase_all_entries_offset =
-    20;
+    24;
 static constexpr dart::compiler::target::word FinalizerBase_detachments_offset =
-    16;
+    20;
 static constexpr dart::compiler::target::word
-    FinalizerBase_entries_collected_offset = 24;
+    FinalizerBase_entries_collected_offset = 28;
 static constexpr dart::compiler::target::word FinalizerBase_isolate_offset = 8;
 static constexpr dart::compiler::target::word FinalizerEntry_detach_offset = 12;
 static constexpr dart::compiler::target::word
@@ -11957,11 +11957,11 @@
 static constexpr dart::compiler::target::word AOT_Finalizer_callback_offset =
     32;
 static constexpr dart::compiler::target::word
-    AOT_FinalizerBase_all_entries_offset = 20;
+    AOT_FinalizerBase_all_entries_offset = 24;
 static constexpr dart::compiler::target::word
-    AOT_FinalizerBase_detachments_offset = 16;
+    AOT_FinalizerBase_detachments_offset = 20;
 static constexpr dart::compiler::target::word
-    AOT_FinalizerBase_entries_collected_offset = 24;
+    AOT_FinalizerBase_entries_collected_offset = 28;
 static constexpr dart::compiler::target::word AOT_FinalizerBase_isolate_offset =
     8;
 static constexpr dart::compiler::target::word AOT_FinalizerEntry_detach_offset =
@@ -12625,11 +12625,11 @@
 static constexpr dart::compiler::target::word AOT_Finalizer_callback_offset =
     32;
 static constexpr dart::compiler::target::word
-    AOT_FinalizerBase_all_entries_offset = 20;
+    AOT_FinalizerBase_all_entries_offset = 24;
 static constexpr dart::compiler::target::word
-    AOT_FinalizerBase_detachments_offset = 16;
+    AOT_FinalizerBase_detachments_offset = 20;
 static constexpr dart::compiler::target::word
-    AOT_FinalizerBase_entries_collected_offset = 24;
+    AOT_FinalizerBase_entries_collected_offset = 28;
 static constexpr dart::compiler::target::word AOT_FinalizerBase_isolate_offset =
     8;
 static constexpr dart::compiler::target::word AOT_FinalizerEntry_detach_offset =
@@ -16612,11 +16612,11 @@
 static constexpr dart::compiler::target::word AOT_Finalizer_callback_offset =
     32;
 static constexpr dart::compiler::target::word
-    AOT_FinalizerBase_all_entries_offset = 20;
+    AOT_FinalizerBase_all_entries_offset = 24;
 static constexpr dart::compiler::target::word
-    AOT_FinalizerBase_detachments_offset = 16;
+    AOT_FinalizerBase_detachments_offset = 20;
 static constexpr dart::compiler::target::word
-    AOT_FinalizerBase_entries_collected_offset = 24;
+    AOT_FinalizerBase_entries_collected_offset = 28;
 static constexpr dart::compiler::target::word AOT_FinalizerBase_isolate_offset =
     8;
 static constexpr dart::compiler::target::word AOT_FinalizerEntry_detach_offset =
@@ -17273,11 +17273,11 @@
 static constexpr dart::compiler::target::word AOT_Finalizer_callback_offset =
     32;
 static constexpr dart::compiler::target::word
-    AOT_FinalizerBase_all_entries_offset = 20;
+    AOT_FinalizerBase_all_entries_offset = 24;
 static constexpr dart::compiler::target::word
-    AOT_FinalizerBase_detachments_offset = 16;
+    AOT_FinalizerBase_detachments_offset = 20;
 static constexpr dart::compiler::target::word
-    AOT_FinalizerBase_entries_collected_offset = 24;
+    AOT_FinalizerBase_entries_collected_offset = 28;
 static constexpr dart::compiler::target::word AOT_FinalizerBase_isolate_offset =
     8;
 static constexpr dart::compiler::target::word AOT_FinalizerEntry_detach_offset =
diff --git a/runtime/vm/kernel_binary.h b/runtime/vm/kernel_binary.h
index 4c6a11f..6d7b33e 100644
--- a/runtime/vm/kernel_binary.h
+++ b/runtime/vm/kernel_binary.h
@@ -20,8 +20,8 @@
 static const uint32_t kMagicProgramFile = 0x90ABCDEFu;
 
 // Both version numbers are inclusive.
-static const uint32_t kMinSupportedKernelFormatVersion = 78;
-static const uint32_t kMaxSupportedKernelFormatVersion = 78;
+static const uint32_t kMinSupportedKernelFormatVersion = 79;
+static const uint32_t kMaxSupportedKernelFormatVersion = 79;
 
 // Keep in sync with package:kernel/lib/binary/tag.dart
 #define KERNEL_TAG_LIST(V)                                                     \
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index dc4fcf3..8e50977 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -3364,21 +3364,22 @@
   // to null on isolate shutdown. See Isolate::finalizers_.
   Isolate* isolate_;
 
-  COMPRESSED_POINTER_FIELD(ObjectPtr, detachments)
-  VISIT_FROM(detachments)
-  COMPRESSED_POINTER_FIELD(LinkedHashSetPtr, all_entries)
-  COMPRESSED_POINTER_FIELD(FinalizerEntryPtr, entries_collected)
-
 // With compressed pointers, the first field in a subclass is at offset 28.
 // If the fields would be public, the first field in a subclass is at offset 32.
 // On Windows, it is always at offset 32, no matter public/private.
 // This makes it 32 for all OSes.
 // We can't use ALIGN8 on the first fields of the subclasses because they use
 // the COMPRESSED_POINTER_FIELD macro to define it.
+// Placed before the first fields so it is not included between from() and to().
 #ifdef DART_COMPRESSED_POINTERS
-  uint32_t align_next_field;
+  uint32_t align_first_field_in_subclass;
 #endif
 
+  COMPRESSED_POINTER_FIELD(ObjectPtr, detachments)
+  VISIT_FROM(detachments)
+  COMPRESSED_POINTER_FIELD(LinkedHashSetPtr, all_entries)
+  COMPRESSED_POINTER_FIELD(FinalizerEntryPtr, entries_collected)
+
   template <typename GCVisitorType>
   friend void MournFinalized(GCVisitorType* visitor);
   friend class GCMarker;
diff --git a/runtime/vm/raw_object_fields.cc b/runtime/vm/raw_object_fields.cc
index 49bae77..d62f9ef 100644
--- a/runtime/vm/raw_object_fields.cc
+++ b/runtime/vm/raw_object_fields.cc
@@ -195,6 +195,20 @@
   F(WeakProperty, value_)                                                      \
   F(WeakReference, target_)                                                    \
   F(WeakReference, type_arguments_)                                            \
+  F(Finalizer, detachments_)                                                   \
+  F(Finalizer, all_entries_)                                                   \
+  F(Finalizer, entries_collected_)                                             \
+  F(Finalizer, callback_)                                                      \
+  F(Finalizer, type_arguments_)                                                \
+  F(NativeFinalizer, detachments_)                                             \
+  F(NativeFinalizer, all_entries_)                                             \
+  F(NativeFinalizer, entries_collected_)                                       \
+  F(NativeFinalizer, callback_)                                                \
+  F(FinalizerEntry, value_)                                                    \
+  F(FinalizerEntry, detach_)                                                   \
+  F(FinalizerEntry, token_)                                                    \
+  F(FinalizerEntry, finalizer_)                                                \
+  F(FinalizerEntry, next_)                                                     \
   F(MirrorReference, referent_)                                                \
   F(UserTag, label_)                                                           \
   F(PointerBase, data_)                                                        \
diff --git a/sdk/lib/_internal/wasm/lib/js_util_wasm_patch.dart b/sdk/lib/_internal/wasm/lib/js_util_wasm_patch.dart
index 000f951..6fa4fc6 100644
--- a/sdk/lib/_internal/wasm/lib/js_util_wasm_patch.dart
+++ b/sdk/lib/_internal/wasm/lib/js_util_wasm_patch.dart
@@ -32,3 +32,6 @@
 @patch
 WasmAnyRef _jsObjectFromDartObject(Object object) =>
     unsafeCastOpaque<WasmAnyRef>(object);
+
+@patch
+JSValue allowInterop<F extends Function>(F f) => throw 'unreachable';
diff --git a/sdk/lib/js_util/js_util_wasm.dart b/sdk/lib/js_util/js_util_wasm.dart
index 49b4a90..88c419d 100644
--- a/sdk/lib/js_util/js_util_wasm.dart
+++ b/sdk/lib/js_util/js_util_wasm.dart
@@ -38,6 +38,15 @@
 @pragma("wasm:import", "dart2wasm.stringToDartString")
 external String _jsStringToDartString(WasmAnyRef string);
 
+@pragma("wasm:import", "dart2wasm.wrapDartCallback")
+external WasmAnyRef _wrapDartCallbackRaw(
+    WasmAnyRef callback, WasmAnyRef trampolineName);
+
+JSValue? _wrapDartCallback(Object callback, String trampolineName) {
+  return JSValue(_wrapDartCallbackRaw(
+      callback.toJS().toAnyRef(), trampolineName.toJS().toAnyRef()));
+}
+
 /// Raw public JS functions.
 /// These are public temporarily to give performance conscious users an escape
 /// hatch while we decide what this API will actually look like. They may
@@ -162,3 +171,11 @@
 JSValue? callMethodVarArgs(JSValue o, String method, List<JSValue?> args) =>
     JSValue.box(callMethodVarArgsRaw(
         o.toAnyRef(), method.toJS().toAnyRef(), args.toJS().toAnyRef()));
+
+/// Returns a wrapped version of [f] suitable for calling from JS. Use [dartify]
+/// to convert back to Dart.
+/// TODO(joshualitt): This is significantly different from the implementation of
+/// [allowInterop] on other web backends. We will need to come up with a unified
+/// semantics or Dart programs will not be able to work correctly across
+/// different web backends.
+external JSValue allowInterop<F extends Function>(F f);
diff --git a/tests/web/wasm/callback_test.dart b/tests/web/wasm/callback_test.dart
new file mode 100644
index 0000000..63e5ba1
--- /dev/null
+++ b/tests/web/wasm/callback_test.dart
@@ -0,0 +1,81 @@
+// 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:js_util_wasm';
+import 'dart:js_wasm';
+import 'dart:wasm';
+
+import 'package:expect/expect.dart';
+
+typedef SumStringCallback = String Function(String a, String b);
+
+@JS()
+@staticInterop
+class DartFromJSCallbackHelper {
+  external factory DartFromJSCallbackHelper.factory(SumStringCallback summer);
+}
+
+extension DartFromJSCallbackHelperMethods on DartFromJSCallbackHelper {
+  external String doSum1();
+  external String doSum2(String a, String b);
+  external String doSum3(SumStringCallback summer);
+}
+
+String sumString(String a, String b) {
+  return a + b;
+}
+
+void staticInteropCallbackTest() {
+  eval(r'''
+    globalThis.DartFromJSCallbackHelper = function(summer) {
+      this.a = 'hello ';
+      this.b = 'world!';
+      this.sum = null;
+      this.summer = summer;
+      this.doSum1 = () => {
+        return this.summer(this.a, this.b);
+      }
+      this.doSum2 = (a, b) => {
+        return this.summer(a, b);
+      }
+      this.doSum3 = (summer) => {
+        return summer(this.a, this.b);
+      }
+    }
+  ''');
+
+  final dartFromJSCallbackHelper = DartFromJSCallbackHelper.factory(sumString);
+  Expect.equals('hello world!', dartFromJSCallbackHelper.doSum1());
+  Expect.equals('foobar', dartFromJSCallbackHelper.doSum2('foo', 'bar'));
+  Expect.equals(
+      'hello world!', dartFromJSCallbackHelper.doSum3((a, b) => a + b));
+}
+
+void allowInteropCallbackTest() {
+  eval(r'''
+    globalThis.doSum1 = function(summer) {
+      return summer('foo', 'bar');
+    }
+    globalThis.doSum2 = function(a, b) {
+      return globalThis.summer(a, b);
+    }
+  ''');
+
+  final interopCallback = allowInterop<SumStringCallback>((a, b) => a + b);
+  Expect.equals('foobar',
+      callMethodVarArgs(globalThis(), 'doSum1', [interopCallback]).toString());
+  setProperty(globalThis(), 'summer', interopCallback);
+  Expect.equals(
+      'foobar',
+      callMethodVarArgs(globalThis(), 'doSum2', ['foo'.toJS(), 'bar'.toJS()])
+          .toString());
+  final roundTripCallback = getProperty(globalThis(), 'summer');
+  Expect.equals('foobar',
+      (dartify(roundTripCallback) as SumStringCallback)('foo', 'bar'));
+}
+
+void main() {
+  staticInteropCallbackTest();
+  allowInteropCallbackTest();
+}
diff --git a/tools/VERSION b/tools/VERSION
index c444be5..5c4a258 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 18
 PATCH 0
-PRERELEASE 43
+PRERELEASE 44
 PRERELEASE_PATCH 0
\ No newline at end of file