[dart2wasm] Avoid implicitly internalizing WasmExternRef.

CoreLibraryReviewExempt: Wasm only changes.
Change-Id: I0e0a6cddffa1c7f2d7c74779f480dd10bf509c1c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/279268
Commit-Queue: Joshua Litt <joshualitt@google.com>
Reviewed-by: Aske Simon Christensen <askesc@google.com>
This commit is contained in:
Joshua Litt 2023-02-10 16:22:47 +00:00 committed by Commit Queue
parent d4a9f2b32e
commit 130903685d
9 changed files with 56 additions and 34 deletions

View file

@ -1051,6 +1051,10 @@ class Intrinsifier {
codeGen.wrap(value, w.RefType.extern(nullable: true));
b.extern_internalize();
return w.RefType.any(nullable: true);
case "_wasmExternRefIsNull":
codeGen.wrap(value, w.RefType.extern(nullable: true));
b.ref_is_null();
return w.NumType.i32;
}
}
@ -1746,6 +1750,12 @@ class Intrinsifier {
return true;
}
if (member.enclosingClass == translator.wasmExternRefClass &&
name == "nullRef") {
b.ref_null(w.HeapType.noextern);
return true;
}
return false;
}
}

View file

@ -93,7 +93,7 @@ const jsRuntimeBlobPart2 = r'''
// Initialize async bridge.
asyncBridge = new WebAssembly.Function(
{parameters: ['externref', 'externref'], results: ['externref']},
{parameters: ['anyref', 'anyref'], results: ['externref']},
dartInstance.exports.$asyncBridge,
{promising: 'first'});
return dartInstance;

View file

@ -120,6 +120,7 @@ class _JSLowerer extends Transformer {
final Procedure _jsifyRawTarget;
final Procedure _isDartFunctionWrappedTarget;
final Procedure _wrapDartFunctionTarget;
final Procedure _jsObjectFromDartObjectTarget;
final Procedure _jsValueBoxTarget;
final Procedure _jsValueUnboxTarget;
final Procedure _allowInteropTarget;
@ -151,6 +152,8 @@ class _JSLowerer extends Transformer {
.getTopLevelProcedure('dart:_js_helper', '_isDartFunctionWrapped'),
_wrapDartFunctionTarget = _coreTypes.index
.getTopLevelProcedure('dart:_js_helper', '_wrapDartFunction'),
_jsObjectFromDartObjectTarget = _coreTypes.index
.getTopLevelProcedure('dart:_js_helper', 'jsObjectFromDartObject'),
_jsValueConstructor = _coreTypes.index
.getClass('dart:_js_helper', 'JSValue')
.constructors
@ -606,7 +609,11 @@ class _JSLowerer extends Transformer {
Arguments([
VariableGet(v),
StaticInvocation(
jsWrapperFunction, Arguments([VariableGet(v)])),
jsWrapperFunction,
Arguments([
StaticInvocation(_jsObjectFromDartObjectTarget,
Arguments([VariableGet(v)]))
])),
], types: [
type
])),
@ -625,7 +632,12 @@ class _JSLowerer extends Transformer {
return ConstructorInvocation(
_jsValueConstructor,
Arguments([
StaticInvocation(jsWrapperFunction, Arguments([argument]))
StaticInvocation(
jsWrapperFunction,
Arguments([
StaticInvocation(
_jsObjectFromDartObjectTarget, Arguments([argument]))
]))
]));
}

View file

@ -372,7 +372,7 @@ class Translator with KernelNodes {
w.DefinedFunction generateGetMain(Procedure mainFunction) {
w.DefinedFunction getMain = m.addFunction(
m.addFunctionType(const [], const [w.RefType.extern(nullable: true)]));
m.addFunctionType(const [], const [w.RefType.any(nullable: true)]));
constants.instantiateConstant(getMain, getMain.body,
StaticTearOffConstant(mainFunction), getMain.type.outputs.single);
getMain.body.end();
@ -568,17 +568,22 @@ class Translator with KernelNodes {
}
/// Translate a Dart type as it should appear on parameters and returns of
/// imported and exported functions. The only reference types allowed here
/// for JS interop are `externref` and `funcref`.
///
/// imported and exported functions. All wasm types are allowed on the interop
/// boundary, but in order to be compatible with the `--closed-world` mode of
/// Binaryen, we coerce all reference types to abstract reference types
/// (`anyref`, `funcref` or `externref`).
/// This function can be called before the class info is built.
w.ValueType translateExternalType(DartType type) {
bool isPotentiallyNullable = type.isPotentiallyNullable;
if (type is InterfaceType) {
Class cls = type.classNode;
if (cls == wasmFuncRefClass || cls == wasmFunctionClass) {
return w.RefType.func(nullable: true);
return w.RefType.func(nullable: isPotentiallyNullable);
}
if (!type.isPotentiallyNullable) {
if (cls == wasmExternRefClass) {
return w.RefType.extern(nullable: isPotentiallyNullable);
}
if (!isPotentiallyNullable) {
w.StorageType? builtin = builtinTypes[cls];
if (builtin != null && builtin.isPrimitive) {
return builtin as w.ValueType;
@ -588,7 +593,9 @@ class Translator with KernelNodes {
}
}
}
return w.RefType.extern(nullable: true);
// TODO(joshualitt): We'd like to use the potential nullability here too,
// but unfortunately this seems to break things.
return w.RefType.any(nullable: true);
}
w.DefinedGlobal makeFunctionRef(w.BaseFunction f) {
@ -773,16 +780,6 @@ class Translator with KernelNodes {
}
}
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) {
if (from.withNullability(false).isSubtypeOf(to)) {
@ -813,10 +810,6 @@ class Translator with KernelNodes {
throw "Conversion between non-reference types";
}
}
if (!fromIsExtern && toIsExtern) {
b.extern_externalize();
}
}
w.FunctionType signatureFor(Reference target) {

View file

@ -147,7 +147,7 @@ class RefType extends ValueType {
const RefType.common({required bool nullable})
: this._(HeapType.common, nullable);
/// A (possibly nullable) reference to the `any` heap type.
/// A (possibly nullable) reference to the `extern` heap type.
const RefType.extern({required bool nullable})
: this._(HeapType.extern, nullable);

View file

@ -70,7 +70,7 @@ void _callAsyncBridge(WasmStructRef args, Completer<Object?> completer) =>
"(args, completer) => asyncBridge(args, completer)", args, completer);
@pragma("wasm:export", "\$asyncBridge")
WasmAnyRef? _asyncBridge(
WasmExternRef? _asyncBridge(
WasmExternRef? stack, WasmStructRef args, Completer<Object?> completer) {
try {
Object? result = _asyncBridge2(args, stack);
@ -112,7 +112,7 @@ Object? _awaitHelper(Object? operand, WasmExternRef? stack) {
Object? _futurePromise(WasmExternRef? stack, Future<Object?> future) =>
JS<Object?>("""new WebAssembly.Function(
{parameters: ['externref', 'externref'], results: ['externref']},
{parameters: ['externref', 'anyref'], results: ['anyref']},
function(future) {
return new Promise(function (resolve, reject) {
dartInstance.exports.\$awaitCallback(future, resolve);

View file

@ -80,7 +80,7 @@ extension ObjectToJS on Object {
}
// For now both `null` and `undefined` in JS map to `null` in Dart.
bool isDartNull(WasmExternRef? ref) => ref == null || isJSUndefined(ref);
bool isDartNull(WasmExternRef? ref) => ref.isNull || isJSUndefined(ref);
// Extensions for [JSArray] and [JSObject].
// TODO(joshualitt): Rewrite using JS types.
@ -328,7 +328,7 @@ WasmExternRef? jsArrayBufferFromDartByteBuffer(ByteBuffer buffer) {
WasmExternRef? jsifyRaw(Object? object) {
if (object == null) {
return null;
return WasmExternRef.nullRef;
} else if (object is bool) {
return toJSBoolean(object);
} else if (object is Function) {
@ -370,10 +370,10 @@ WasmExternRef? jsifyRaw(Object? object) {
}
}
bool isWasmGCStruct(WasmExternRef ref) => ref.internalize().isObject;
bool isWasmGCStruct(WasmExternRef? ref) => ref.internalize()?.isObject ?? false;
Object? dartifyRaw(WasmExternRef? ref) {
if (ref == null) {
if (ref.isNull) {
return null;
} else if (isJSUndefined(ref)) {
// TODO(joshualitt): Introduce a `JSUndefined` type.
@ -413,7 +413,7 @@ Object? dartifyRaw(WasmExternRef? ref) {
} else if (isWasmGCStruct(ref)) {
return jsObjectToDartObject(ref);
} else {
return JSValue(ref);
return JSValue.box(ref);
}
}

View file

@ -46,10 +46,15 @@ extension ExternalizeNullable on WasmAnyRef? {
/// The Wasm `externref` type.
@pragma("wasm:entry-point")
class WasmExternRef extends _WasmBase {
// To avoid conflating the null externref with Dart's null, we provide a
// special getter for the null externref.
external static WasmExternRef? get nullRef;
WasmAnyRef internalize() => _internalizeNonNullable(this);
}
extension InternalizeNullable on WasmExternRef? {
bool get isNull => _wasmExternRefIsNull(this);
WasmAnyRef? internalize() => _internalizeNullable(this);
}
@ -57,6 +62,7 @@ external WasmExternRef _externalizeNonNullable(WasmAnyRef ref);
external WasmExternRef? _externalizeNullable(WasmAnyRef? ref);
external WasmAnyRef _internalizeNonNullable(WasmExternRef ref);
external WasmAnyRef? _internalizeNullable(WasmExternRef? ref);
external bool _wasmExternRefIsNull(WasmExternRef? ref);
/// The Wasm `funcref` type.
@pragma("wasm:entry-point")

View file

@ -32,7 +32,7 @@ test() {
Object dartObject1 = "1";
Object dartObject2 = true;
Object dartObject3 = Object();
WasmAnyRef jsObject1 = createObject(null)!.internalize();
WasmAnyRef jsObject1 = createObject(WasmExternRef.nullRef)!.internalize();
// A JS object is not a Dart object.
Expect.isFalse(jsObject1.isObject);
@ -86,7 +86,8 @@ test() {
// Create a typed function reference from an import and call it.
var createObjectFun = WasmFunction.fromFunction(createObject);
WasmAnyRef jsObject3 = createObjectFun.call(null).internalize()!;
WasmAnyRef jsObject3 =
createObjectFun.call(WasmExternRef.nullRef).internalize()!;
Expect.isFalse(jsObject3.isObject);
Expect.equals(3, funCount);