mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
[dart2wasm] Coerce types on imports/exports to externref or funcref
Change-Id: Id7fe0775e14b7ed16c925819ffc9c41dea863e4d Cq-Include-Trybots: luci.dart.try:dart2wasm-linux-x64-d8-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/258001 Auto-Submit: Aske Simon Christensen <askesc@google.com> Reviewed-by: Joshua Litt <joshualitt@google.com> Commit-Queue: Joshua Litt <joshualitt@google.com>
This commit is contained in:
parent
7055da30a7
commit
9af959c167
|
@ -60,5 +60,5 @@ inst.exports.baz(2);
|
|||
In the signatures of imported and exported functions, use the following types:
|
||||
|
||||
- For numbers, use `double`.
|
||||
- For Dart objects, use the corresponding Dart type. The fields of the underlying representation can be accessed on the JS side as `.$field0`, `.$field1` etc., but there is currently no defined way of finding the field index of a particular Dart field, so this mechanism is mainly useful for special objects with known layout.
|
||||
- For JS objects, use the `WasmAnyRef` type (or `WasmAnyRef?` as applicable) from the `dart:wasm` package. These can be passed around and stored as opaque values on the Dart side.
|
||||
- For JS objects, use the `WasmExternRef?` type from the `dart:wasm` package, which translates to the Wasm `externref` type. These can be passed around and stored as opaque values on the Dart side.
|
||||
- For Dart objects, use the corresponding Dart type. This will be emitted as `externref` and automatically converted to and from the Dart type at the boundary.
|
||||
|
|
|
@ -333,6 +333,22 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
|||
typeLocals[typeParameters[i]] = paramLocals[typeParameterOffset + i];
|
||||
}
|
||||
|
||||
// For all parameters whose Wasm type has been forced to `externref` due to
|
||||
// this function being an export, internalize and cast the parameter to the
|
||||
// canonical representation type for its Dart type.
|
||||
locals.forEach((parameter, local) {
|
||||
DartType parameterType = parameter.type;
|
||||
if (local.type == w.RefType.extern(nullable: true) &&
|
||||
!(parameterType is InterfaceType &&
|
||||
parameterType.classNode == translator.wasmExternRefClass)) {
|
||||
w.Local newLocal = addLocal(translateType(parameterType));
|
||||
b.local_get(local);
|
||||
translator.convertType(function, local.type, newLocal.type);
|
||||
b.local_set(newLocal);
|
||||
locals[parameter] = newLocal;
|
||||
}
|
||||
});
|
||||
|
||||
closures.findCaptures(member);
|
||||
|
||||
if (hasThis) {
|
||||
|
@ -1408,7 +1424,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
|||
|
||||
@override
|
||||
w.ValueType visitEqualsNull(EqualsNull node, w.ValueType expectedType) {
|
||||
wrap(node.expression, const w.RefType.top(nullable: true));
|
||||
wrap(node.expression, const w.RefType.any(nullable: true));
|
||||
b.ref_is_null();
|
||||
return w.NumType.i32;
|
||||
}
|
||||
|
|
|
@ -219,12 +219,14 @@ class FunctionCollector extends MemberVisitor1<w.FunctionType, Reference> {
|
|||
List<w.ValueType> typeParameters = List.filled(typeParamCount,
|
||||
translator.classInfo[translator.typeClass]!.nonNullableType);
|
||||
|
||||
// The JS embedder will not accept Wasm struct types as parameter or return
|
||||
// types for functions called from JS. We need to use eqref instead.
|
||||
// The only reference types allowed as parameters and returns on imported
|
||||
// or exported functions for JS interop are `externref` and `funcref`.
|
||||
w.ValueType adjustExternalType(w.ValueType type) {
|
||||
if (isImportOrExport &&
|
||||
type.isSubtypeOf(translator.topInfo.nullableType)) {
|
||||
return w.RefType.eq(nullable: type.nullable);
|
||||
if (isImportOrExport && type is w.RefType) {
|
||||
if (type.heapType.isSubtypeOf(w.HeapType.func)) {
|
||||
return w.RefType.func(nullable: true);
|
||||
}
|
||||
return w.RefType.extern(nullable: true);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
|
|
@ -739,8 +739,8 @@ class Translator {
|
|||
}
|
||||
if (to != voidMarker) {
|
||||
if (to is w.RefType && to.nullable) {
|
||||
// This can happen when a void method has its return type overridden to
|
||||
// return a value, in which case the selector signature will have a
|
||||
// This can happen when a void method has its return type overridden
|
||||
// to return a value, in which case the selector signature will have a
|
||||
// non-void return type to encompass all possible return values.
|
||||
b.ref_null(to.heapType);
|
||||
} else {
|
||||
|
@ -752,6 +752,17 @@ class Translator {
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool fromIsExtern = from.isSubtypeOf(w.RefType.extern(nullable: true));
|
||||
bool toIsExtern = to.isSubtypeOf(w.RefType.extern(nullable: true));
|
||||
if (fromIsExtern && !toIsExtern) {
|
||||
b.extern_internalize();
|
||||
from = w.RefType.any(nullable: from.nullable);
|
||||
}
|
||||
if (!fromIsExtern && toIsExtern) {
|
||||
to = w.RefType.any(nullable: to.nullable);
|
||||
}
|
||||
|
||||
if (!from.isSubtypeOf(to)) {
|
||||
if (from is! w.RefType && to is w.RefType) {
|
||||
// Boxing
|
||||
|
@ -812,6 +823,10 @@ class Translator {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!fromIsExtern && toIsExtern) {
|
||||
b.extern_externalize();
|
||||
}
|
||||
}
|
||||
|
||||
w.FunctionType signatureFor(Reference target) {
|
||||
|
|
|
@ -53,20 +53,18 @@ import 'dart:_internal' show patch, scheduleCallback, unsafeCastOpaque;
|
|||
import 'dart:wasm';
|
||||
|
||||
@pragma("wasm:entry-point")
|
||||
Future<T> _asyncHelper<T>(WasmEqRef args) {
|
||||
Future<T> _asyncHelper<T>(WasmDataRef args) {
|
||||
Completer<T> completer = Completer();
|
||||
_callAsyncBridge(args, completer);
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
@pragma("wasm:import", "dart2wasm.callAsyncBridge")
|
||||
external void _callAsyncBridge(WasmEqRef args, Completer<Object?> completer);
|
||||
external void _callAsyncBridge(WasmDataRef args, Completer<Object?> completer);
|
||||
|
||||
@pragma("wasm:export", "\$asyncBridge")
|
||||
WasmAnyRef? _asyncBridge(
|
||||
WasmExternRef? stack, WasmExternRef? argsRef, WasmExternRef? completerRef) {
|
||||
WasmDataRef args = unsafeCastOpaque(argsRef!.internalize());
|
||||
Completer<Object?> completer = unsafeCastOpaque(completerRef!.internalize());
|
||||
WasmExternRef? stack, WasmDataRef args, Completer<Object?> completer) {
|
||||
try {
|
||||
Object? result = _asyncBridge2(args, stack);
|
||||
completer.complete(result);
|
||||
|
@ -87,9 +85,7 @@ class _FutureError {
|
|||
@pragma("wasm:entry-point")
|
||||
Object? _awaitHelper(Object? operand, WasmExternRef? stack) {
|
||||
if (operand is! Future) return operand;
|
||||
WasmExternRef futureRef = WasmAnyRef.fromObject(operand).externalize();
|
||||
Object? result =
|
||||
unsafeCastOpaque(_futurePromise(stack, futureRef).internalize());
|
||||
Object? result = _futurePromise(stack, operand);
|
||||
if (result is _FutureError) {
|
||||
// TODO(askesc): Combine stack traces
|
||||
throw result.exception;
|
||||
|
@ -98,11 +94,10 @@ Object? _awaitHelper(Object? operand, WasmExternRef? stack) {
|
|||
}
|
||||
|
||||
@pragma("wasm:import", "dart2wasm.futurePromise")
|
||||
external WasmExternRef? _futurePromise(
|
||||
WasmExternRef? stack, WasmExternRef? future);
|
||||
external Object? _futurePromise(WasmExternRef? stack, Future<Object?> future);
|
||||
|
||||
@pragma("wasm:export", "\$awaitCallback")
|
||||
void _awaitCallback(Future<Object?> future, WasmAnyRef resolve) {
|
||||
void _awaitCallback(Future<Object?> future, WasmExternRef? resolve) {
|
||||
future.then((value) {
|
||||
_callResolve(resolve, value);
|
||||
}, onError: (exception, stackTrace) {
|
||||
|
@ -111,4 +106,4 @@ void _awaitCallback(Future<Object?> future, WasmAnyRef resolve) {
|
|||
}
|
||||
|
||||
@pragma("wasm:import", "dart2wasm.callResolve")
|
||||
external void _callResolve(WasmAnyRef resolve, Object? result);
|
||||
external void _callResolve(WasmExternRef? resolve, Object? result);
|
||||
|
|
|
@ -7,14 +7,14 @@ import 'dart:wasm';
|
|||
import 'package:expect/expect.dart';
|
||||
|
||||
@pragma("wasm:import", "Object.create")
|
||||
external WasmAnyRef createObject(WasmAnyRef? prototype);
|
||||
external WasmExternRef? createObject(WasmExternRef? prototype);
|
||||
|
||||
@pragma("wasm:import", "Array.of")
|
||||
external WasmAnyRef singularArray(WasmAnyRef element);
|
||||
external WasmExternRef? singularArray(WasmExternRef? element);
|
||||
|
||||
@pragma("wasm:import", "Reflect.apply")
|
||||
external WasmAnyRef apply(
|
||||
WasmFuncRef target, WasmAnyRef thisArgument, WasmAnyRef argumentsList);
|
||||
external WasmExternRef? apply(WasmFuncRef? target, WasmExternRef? thisArgument,
|
||||
WasmExternRef? argumentsList);
|
||||
|
||||
WasmAnyRef? anyRef;
|
||||
WasmEqRef? eqRef;
|
||||
|
@ -32,7 +32,7 @@ test() {
|
|||
Object dartObject1 = "1";
|
||||
Object dartObject2 = true;
|
||||
Object dartObject3 = Object();
|
||||
WasmAnyRef jsObject1 = createObject(null);
|
||||
WasmAnyRef jsObject1 = createObject(null)!.internalize();
|
||||
|
||||
// A JS object is not a Dart object.
|
||||
Expect.isFalse(jsObject1.isObject);
|
||||
|
@ -77,7 +77,7 @@ test() {
|
|||
var dartObjectRef = WasmEqRef.fromObject("Dart object");
|
||||
var ff = WasmFunction.fromFunction(fun);
|
||||
ff.call(dartObjectRef);
|
||||
apply(ff, createObject(null), singularArray(dartObjectRef));
|
||||
apply(ff, createObject(null), singularArray(dartObjectRef.externalize()));
|
||||
|
||||
// Cast a typed function reference to a `funcref` and back.
|
||||
WasmFuncRef funcref = WasmFuncRef.fromWasmFunction(ff);
|
||||
|
@ -86,7 +86,7 @@ test() {
|
|||
|
||||
// Create a typed function reference from an import and call it.
|
||||
var createObjectFun = WasmFunction.fromFunction(createObject);
|
||||
WasmAnyRef jsObject3 = createObjectFun.call(null);
|
||||
WasmAnyRef jsObject3 = createObjectFun.call(null).internalize()!;
|
||||
Expect.isFalse(jsObject3.isObject);
|
||||
|
||||
Expect.equals(3, funCount);
|
||||
|
|
Loading…
Reference in a new issue