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