[dart2wasm] Implement the three-pronged WasmGC type hierarchy.
This splits the type hierarchy into three separate hierarchies with
the top types `any`, `func` and `extern`.
Update d8 to 10.6.91, which switches to the new type hierarchy.
Also, all ref shorthands for abstract heap types are now nullable, so
the type emitter in the `wasm_builder` is updated to follow that scheme.
To reduce confusion about the nullability of abstract reference types,
these now all require the nullability to be specified explicitly.
Change-Id: I4774d08cbed18307e481c466b2e3402a8d8fb6bd
Cq-Include-Trybots: luci.dart.try:dart2wasm-linux-x64-d8-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/255060
Reviewed-by: Joshua Litt <joshualitt@google.com>
diff --git a/DEPS b/DEPS
index 52c78ae..b8815654 100644
--- a/DEPS
+++ b/DEPS
@@ -58,7 +58,7 @@
# Checkout extra javascript engines for testing or benchmarking.
# d8, the V8 shell, is always checked out.
"checkout_javascript_engines": False,
- "d8_tag": "version:10.6.51",
+ "d8_tag": "version:10.6.91",
"jsshell_tag": "version:95.0",
# As Flutter does, we use Fuchsia's GN and Clang toolchain. These revision
diff --git a/pkg/dart2wasm/lib/class_info.dart b/pkg/dart2wasm/lib/class_info.dart
index 4d6538e..204ab35 100644
--- a/pkg/dart2wasm/lib/class_info.dart
+++ b/pkg/dart2wasm/lib/class_info.dart
@@ -282,7 +282,7 @@
if (field.isInstanceMember) {
w.ValueType wasmType = translator.translateType(field.type);
// TODO(askesc): Generalize this check for finer nullability control
- if (wasmType != w.RefType.data()) {
+ if (wasmType != w.RefType.data(nullable: false)) {
wasmType = wasmType.withNullability(true);
}
translator.fieldIndex[field] = info.struct.fields.length;
diff --git a/pkg/dart2wasm/lib/code_generator.dart b/pkg/dart2wasm/lib/code_generator.dart
index 426af54..ebd3a2d 100644
--- a/pkg/dart2wasm/lib/code_generator.dart
+++ b/pkg/dart2wasm/lib/code_generator.dart
@@ -339,11 +339,13 @@
Class cls = member.enclosingClass!;
ClassInfo info = translator.classInfo[cls]!;
thisLocal = paramLocals[0];
+ assert(!thisLocal!.type.nullable);
w.RefType thisType = info.nonNullableType;
if (translator.needsConversion(paramLocals[0].type, thisType) &&
!(cls == translator.objectInfo.cls ||
cls == translator.ffiPointerClass ||
- translator.isFfiCompound(cls))) {
+ translator.isFfiCompound(cls) ||
+ translator.isWasmType(cls))) {
preciseThisLocal = addLocal(thisType);
b.local_get(paramLocals[0]);
b.ref_cast(info.struct);
@@ -1405,7 +1407,7 @@
@override
w.ValueType visitEqualsNull(EqualsNull node, w.ValueType expectedType) {
- wrap(node.expression, const w.RefType.any());
+ wrap(node.expression, const w.RefType.any(nullable: true));
b.ref_is_null();
return w.NumType.i32;
}
diff --git a/pkg/dart2wasm/lib/dispatch_table.dart b/pkg/dart2wasm/lib/dispatch_table.dart
index 1e00b2d..0c744c7 100644
--- a/pkg/dart2wasm/lib/dispatch_table.dart
+++ b/pkg/dart2wasm/lib/dispatch_table.dart
@@ -298,7 +298,7 @@
}
}
- wasmTable = m.addTable(w.RefType.func(), table.length);
+ wasmTable = m.addTable(w.RefType.func(nullable: true), table.length);
}
void output() {
diff --git a/pkg/dart2wasm/lib/functions.dart b/pkg/dart2wasm/lib/functions.dart
index 09e3dfa..10bdcd5 100644
--- a/pkg/dart2wasm/lib/functions.dart
+++ b/pkg/dart2wasm/lib/functions.dart
@@ -28,7 +28,7 @@
// allocation of that class is encountered
final Map<int, List<Reference>> _pendingAllocation = {};
- final w.ValueType asyncStackType = const w.RefType.any(nullable: true);
+ final w.ValueType asyncStackType = const w.RefType.extern(nullable: true);
late final w.FunctionType asyncStubFunctionType = m.addFunctionType(
[const w.RefType.data(nullable: false), asyncStackType],
diff --git a/pkg/dart2wasm/lib/intrinsics.dart b/pkg/dart2wasm/lib/intrinsics.dart
index 7a06523..098c19f 100644
--- a/pkg/dart2wasm/lib/intrinsics.dart
+++ b/pkg/dart2wasm/lib/intrinsics.dart
@@ -347,8 +347,7 @@
}
// WasmAnyRef.toObject
- if (cls == translator.wasmAnyRefClass) {
- assert(name == "toObject");
+ if (cls == translator.wasmAnyRefClass && name == "toObject") {
w.Label succeed = b.block(const [], [translator.topInfo.nonNullableType]);
w.Label fail = b.block(const [], const [w.RefType.any(nullable: false)]);
codeGen.wrap(receiver, w.RefType.any(nullable: false));
@@ -957,21 +956,12 @@
}
// (WasmFuncRef|WasmFunction).fromRef constructors
- if ((cls == translator.wasmFuncRefClass ||
- cls == translator.wasmFunctionClass) &&
- name == "fromRef") {
+ if (cls == translator.wasmFunctionClass && name == "fromFuncRef") {
Expression ref = node.arguments.positional[0];
w.RefType resultType = typeOfExp(node) as w.RefType;
w.Label succeed = b.block(const [], [resultType]);
- w.Label fail =
- b.block(const [], const [w.RefType.any(nullable: false)]);
- codeGen.wrap(ref, w.RefType.any(nullable: false));
- b.br_on_non_func(fail);
- if (cls == translator.wasmFunctionClass) {
- b.br_on_cast_fail(fail, resultType.heapType as w.FunctionType);
- }
- b.br(succeed);
- b.end(); // fail
+ codeGen.wrap(ref, w.RefType.func(nullable: false));
+ b.br_on_cast(succeed, resultType.heapType as w.FunctionType);
codeGen.throwWasmRefError("a function with the expected signature");
b.end(); // succeed
return resultType;
diff --git a/pkg/dart2wasm/lib/translator.dart b/pkg/dart2wasm/lib/translator.dart
index 4647f1c..99dda3a 100644
--- a/pkg/dart2wasm/lib/translator.dart
+++ b/pkg/dart2wasm/lib/translator.dart
@@ -66,6 +66,7 @@
late final Class wasmTypesBaseClass;
late final Class wasmArrayBaseClass;
late final Class wasmAnyRefClass;
+ late final Class wasmExternRefClass;
late final Class wasmFuncRefClass;
late final Class wasmEqRefClass;
late final Class wasmDataRefClass;
@@ -191,6 +192,7 @@
wasmTypesBaseClass = lookupWasm("_WasmBase");
wasmArrayBaseClass = lookupWasm("_WasmArray");
wasmAnyRefClass = lookupWasm("WasmAnyRef");
+ wasmExternRefClass = lookupWasm("WasmExternRef");
wasmFuncRefClass = lookupWasm("WasmFuncRef");
wasmEqRefClass = lookupWasm("WasmEqRef");
wasmDataRefClass = lookupWasm("WasmDataRef");
@@ -289,6 +291,7 @@
coreTypes.intClass: w.NumType.i64,
coreTypes.doubleClass: w.NumType.f64,
wasmAnyRefClass: const w.RefType.any(nullable: false),
+ wasmExternRefClass: const w.RefType.extern(nullable: false),
wasmFuncRefClass: const w.RefType.func(nullable: false),
wasmEqRefClass: const w.RefType.eq(nullable: false),
wasmDataRefClass: const w.RefType.data(nullable: false),
@@ -500,7 +503,7 @@
}
if (isWasmType(cls)) {
if (builtin.isPrimitive) throw "Wasm numeric types can't be nullable";
- return (builtin as w.RefType).withNullability(true);
+ return (builtin as w.RefType).withNullability(nullable);
}
if (cls == ffiPointerClass) throw "FFI types can't be nullable";
Class? boxedClass = boxedClasses[builtin];
@@ -602,7 +605,7 @@
w.FunctionType closureFunctionType(int parameterCount) {
return m.addFunctionType([
- w.RefType.data(),
+ w.RefType.data(nullable: false),
...List<w.ValueType>.filled(parameterCount, topInfo.nullableType)
], [
topInfo.nullableType
@@ -717,7 +720,6 @@
}
var heapType = (to as w.RefType).heapType;
if (heapType is w.FunctionType) {
- b.ref_as_func();
b.ref_cast(heapType);
return;
}
diff --git a/pkg/wasm_builder/lib/src/instructions.dart b/pkg/wasm_builder/lib/src/instructions.dart
index 0e034f5..69c210b 100644
--- a/pkg/wasm_builder/lib/src/instructions.dart
+++ b/pkg/wasm_builder/lib/src/instructions.dart
@@ -223,7 +223,7 @@
}
ValueType get _topOfStack {
- if (!reachable) return RefType.any();
+ if (!reachable) return RefType.top(nullable: true);
if (_stackTypes.isEmpty) _reportError("Stack underflow");
return _stackTypes.last;
}
@@ -896,7 +896,8 @@
/// Emit a `ref.is_null` instruction.
void ref_is_null() {
- assert(_verifyTypes(const [RefType.any()], const [NumType.i32],
+ assert(_verifyTypes(
+ const [RefType.top(nullable: true)], const [NumType.i32],
trace: const ['ref.is_null']));
writeByte(0xD1);
}
@@ -911,16 +912,16 @@
/// Emit a `ref.as_non_null` instruction.
void ref_as_non_null() {
- assert(_verifyTypes(
- const [RefType.any()], [_topOfStack.withNullability(false)],
+ assert(_verifyTypes(const [RefType.top(nullable: true)],
+ [_topOfStack.withNullability(false)],
trace: const ['ref.as_non_null']));
writeByte(0xD3);
}
/// Emit a `br_on_null` instruction.
void br_on_null(Label label) {
- assert(_verifyTypes(
- const [RefType.any()], [_topOfStack.withNullability(false)],
+ assert(_verifyTypes(const [RefType.top(nullable: true)],
+ [_topOfStack.withNullability(false)],
trace: ['br_on_null', label]));
assert(_verifyBranchTypes(label, 1));
writeByte(0xD4);
@@ -929,7 +930,9 @@
/// Emit a `ref.eq` instruction.
void ref_eq() {
- assert(_verifyTypes(const [RefType.eq(), RefType.eq()], const [NumType.i32],
+ assert(_verifyTypes(
+ const [RefType.eq(nullable: true), RefType.eq(nullable: true)],
+ const [NumType.i32],
trace: const ['ref.eq']));
writeByte(0xD5);
}
@@ -937,7 +940,7 @@
/// Emit a `br_on_non_null` instruction.
void br_on_non_null(Label label) {
assert(_verifyBranchTypes(label, 1, [_topOfStack.withNullability(false)]));
- assert(_verifyTypes(const [RefType.any()], const [],
+ assert(_verifyTypes(const [RefType.top(nullable: true)], const [],
trace: ['br_on_non_null', label]));
writeByte(0xD6);
_writeLabel(label);
@@ -1104,21 +1107,24 @@
/// Emit an `i31.new` instruction.
void i31_new() {
- assert(_verifyTypes(const [NumType.i32], const [RefType.i31()],
+ assert(_verifyTypes(
+ const [NumType.i32], const [RefType.i31(nullable: false)],
trace: const ['i31.new']));
writeBytes(const [0xFB, 0x20]);
}
/// Emit an `i31.get_s` instruction.
void i31_get_s() {
- assert(_verifyTypes(const [RefType.i31()], const [NumType.i32],
+ assert(_verifyTypes(
+ const [RefType.i31(nullable: false)], const [NumType.i32],
trace: const ['i31.get_s']));
writeBytes(const [0xFB, 0x21]);
}
/// Emit an `i31.get_u` instruction.
void i31_get_u() {
- assert(_verifyTypes(const [RefType.i31()], const [NumType.i32],
+ assert(_verifyTypes(
+ const [RefType.i31(nullable: false)], const [NumType.i32],
trace: const ['i31.get_u']));
writeBytes(const [0xFB, 0x22]);
}
@@ -1175,63 +1181,41 @@
writeSigned(targetType.index);
}
- /// Emit a `ref.is_func` instruction.
- void ref_is_func() {
- assert(_verifyTypes(const [RefType.any()], const [NumType.i32],
- trace: const ['ref.is_func']));
- writeBytes(const [0xFB, 0x50]);
- }
-
/// Emit a `ref.is_data` instruction.
void ref_is_data() {
- assert(_verifyTypes(const [RefType.any()], const [NumType.i32],
+ assert(_verifyTypes(
+ const [RefType.any(nullable: true)], const [NumType.i32],
trace: const ['ref.is_data']));
writeBytes(const [0xFB, 0x51]);
}
/// Emit a `ref.is_i31` instruction.
void ref_is_i31() {
- assert(_verifyTypes(const [RefType.any()], const [NumType.i32],
+ assert(_verifyTypes(
+ const [RefType.any(nullable: true)], const [NumType.i32],
trace: const ['ref.is_i31']));
writeBytes(const [0xFB, 0x52]);
}
- /// Emit a `ref.as_func` instruction.
- void ref_as_func() {
- assert(_verifyTypes(
- const [RefType.any()], const [RefType.func(nullable: false)],
- trace: const ['ref.as_func']));
- writeBytes(const [0xFB, 0x58]);
- }
-
/// Emit a `ref.as_data` instruction.
void ref_as_data() {
- assert(_verifyTypes(
- const [RefType.any()], const [RefType.data(nullable: false)],
+ assert(_verifyTypes(const [RefType.any(nullable: true)],
+ const [RefType.data(nullable: false)],
trace: const ['ref.as_data']));
writeBytes(const [0xFB, 0x59]);
}
/// Emit a `ref.as_i31` instruction.
void ref_as_i31() {
- assert(_verifyTypes(
- const [RefType.any()], const [RefType.i31(nullable: false)],
+ assert(_verifyTypes(const [RefType.any(nullable: true)],
+ const [RefType.i31(nullable: false)],
trace: const ['ref.as_i31']));
writeBytes(const [0xFB, 0x5A]);
}
- /// Emit a `br_on_func` instruction.
- void br_on_func(Label label) {
- assert(_verifyTypes(const [RefType.any()], [_topOfStack],
- trace: ['br_on_func', label]));
- assert(_verifyBranchTypes(label, 1, const [RefType.func(nullable: false)]));
- writeBytes(const [0xFB, 0x60]);
- _writeLabel(label);
- }
-
/// Emit a `br_on_data` instruction.
void br_on_data(Label label) {
- assert(_verifyTypes(const [RefType.any()], [_topOfStack],
+ assert(_verifyTypes(const [RefType.any(nullable: true)], [_topOfStack],
trace: ['br_on_data', label]));
assert(_verifyBranchTypes(label, 1, const [RefType.data(nullable: false)]));
writeBytes(const [0xFB, 0x61]);
@@ -1240,28 +1224,18 @@
/// Emit a `br_on_i31` instruction.
void br_on_i31(Label label) {
- assert(_verifyTypes(const [RefType.any()], [_topOfStack],
+ assert(_verifyTypes(const [RefType.any(nullable: true)], [_topOfStack],
trace: ['br_on_i31', label]));
assert(_verifyBranchTypes(label, 1, const [RefType.i31(nullable: false)]));
writeBytes(const [0xFB, 0x62]);
_writeLabel(label);
}
- /// Emit a `br_on_non_func` instruction.
- void br_on_non_func(Label label) {
- assert(_verifyBranchTypes(label, 1, [_topOfStack]));
- assert(_verifyTypes(
- const [RefType.any()], const [RefType.func(nullable: false)],
- trace: ['br_on_non_func', label]));
- writeBytes(const [0xFB, 0x63]);
- _writeLabel(label);
- }
-
/// Emit a `br_on_non_data` instruction.
void br_on_non_data(Label label) {
assert(_verifyBranchTypes(label, 1, [_topOfStack]));
- assert(_verifyTypes(
- const [RefType.any()], const [RefType.data(nullable: false)],
+ assert(_verifyTypes(const [RefType.any(nullable: true)],
+ const [RefType.data(nullable: false)],
trace: ['br_on_non_data', label]));
writeBytes(const [0xFB, 0x64]);
_writeLabel(label);
@@ -1270,8 +1244,8 @@
/// Emit a `br_on_non_i31` instruction.
void br_on_non_i31(Label label) {
assert(_verifyBranchTypes(label, 1, [_topOfStack]));
- assert(_verifyTypes(
- const [RefType.any()], const [RefType.i31(nullable: false)],
+ assert(_verifyTypes(const [RefType.any(nullable: true)],
+ const [RefType.i31(nullable: false)],
trace: ['br_on_non_i31', label]));
writeBytes(const [0xFB, 0x65]);
_writeLabel(label);
diff --git a/pkg/wasm_builder/lib/src/module.dart b/pkg/wasm_builder/lib/src/module.dart
index 7187a39..ced6ed4 100644
--- a/pkg/wasm_builder/lib/src/module.dart
+++ b/pkg/wasm_builder/lib/src/module.dart
@@ -449,7 +449,7 @@
: elements = List.filled(minSize, null);
void setElement(int index, BaseFunction function) {
- assert(type == RefType.func(),
+ assert(type == RefType.func(nullable: true),
"Elements are only supported for funcref tables");
elements[index] = function;
}
diff --git a/pkg/wasm_builder/lib/src/types.dart b/pkg/wasm_builder/lib/src/types.dart
index d25ab81..498978d 100644
--- a/pkg/wasm_builder/lib/src/types.dart
+++ b/pkg/wasm_builder/lib/src/types.dart
@@ -143,25 +143,29 @@
const RefType._(this.heapType, this.nullable);
+ /// Internal top type above any, func and extern. Not a real Wasm ref type.
+ const RefType.top({required bool nullable}) : this._(HeapType.top, nullable);
+
/// A (possibly nullable) reference to the `any` heap type.
- const RefType.any({bool nullable = AnyHeapType.defaultNullability})
- : this._(HeapType.any, nullable);
+ const RefType.extern({required bool nullable})
+ : this._(HeapType.extern, nullable);
+
+ /// A (possibly nullable) reference to the `any` heap type.
+ const RefType.any({required bool nullable}) : this._(HeapType.any, nullable);
/// A (possibly nullable) reference to the `eq` heap type.
- const RefType.eq({bool nullable = EqHeapType.defaultNullability})
- : this._(HeapType.eq, nullable);
+ const RefType.eq({required bool nullable}) : this._(HeapType.eq, nullable);
/// A (possibly nullable) reference to the `func` heap type.
- const RefType.func({bool nullable = FuncHeapType.defaultNullability})
+ const RefType.func({required bool nullable})
: this._(HeapType.func, nullable);
/// A (possibly nullable) reference to the `data` heap type.
- const RefType.data({bool nullable = DataHeapType.defaultNullability})
+ const RefType.data({required bool nullable})
: this._(HeapType.data, nullable);
/// A (possibly nullable) reference to the `i31` heap type.
- const RefType.i31({bool nullable = I31HeapType.defaultNullability})
- : this._(HeapType.i31, nullable);
+ const RefType.i31({required bool nullable}) : this._(HeapType.i31, nullable);
/// A (possibly nullable) reference to a custom heap type.
RefType.def(DefType defType, {required bool nullable})
@@ -209,6 +213,12 @@
abstract class HeapType implements Serializable {
const HeapType();
+ /// Internal top type above any, func and extern. Not a real Wasm heap type.
+ static const top = TopHeapType._();
+
+ /// The `extern` heap type.
+ static const extern = ExternHeapType._();
+
/// The `any` heap type.
static const any = AnyHeapType._();
@@ -237,6 +247,46 @@
bool isStructuralSubtypeOf(HeapType other) => isSubtypeOf(other);
}
+/// Internal top type above any, func and extern. This is only used to specify
+/// input constraints for instructions that are polymorphic across the three
+/// type hierarchies. It's not a real Wasm heap type.
+class TopHeapType extends HeapType {
+ const TopHeapType._();
+
+ @override
+ bool? get nullableByDefault => null;
+
+ @override
+ bool isSubtypeOf(HeapType other) => other == HeapType.top;
+
+ @override
+ void serialize(Serializer s) =>
+ throw "Attempt to serialize internal top type";
+
+ @override
+ String toString() => "#top";
+}
+
+/// The `extern` heap type.
+class ExternHeapType extends HeapType {
+ const ExternHeapType._();
+
+ static const defaultNullability = true;
+
+ @override
+ bool? get nullableByDefault => defaultNullability;
+
+ @override
+ bool isSubtypeOf(HeapType other) =>
+ other == HeapType.top || other == HeapType.extern;
+
+ @override
+ void serialize(Serializer s) => s.writeByte(0x6F);
+
+ @override
+ String toString() => "extern";
+}
+
/// The `any` heap type.
class AnyHeapType extends HeapType {
const AnyHeapType._();
@@ -247,10 +297,11 @@
bool? get nullableByDefault => defaultNullability;
@override
- bool isSubtypeOf(HeapType other) => other == HeapType.any;
+ bool isSubtypeOf(HeapType other) =>
+ other == HeapType.top || other == HeapType.any;
@override
- void serialize(Serializer s) => s.writeByte(0x6F);
+ void serialize(Serializer s) => s.writeByte(0x6E);
@override
String toString() => "any";
@@ -267,7 +318,7 @@
@override
bool isSubtypeOf(HeapType other) =>
- other == HeapType.any || other == HeapType.eq;
+ other == HeapType.top || other == HeapType.any || other == HeapType.eq;
@override
void serialize(Serializer s) => s.writeByte(0x6D);
@@ -287,7 +338,7 @@
@override
bool isSubtypeOf(HeapType other) =>
- other == HeapType.any || other == HeapType.func;
+ other == HeapType.top || other == HeapType.func;
@override
void serialize(Serializer s) => s.writeByte(0x70);
@@ -300,14 +351,17 @@
class DataHeapType extends HeapType {
const DataHeapType._();
- static const defaultNullability = false;
+ static const defaultNullability = true;
@override
bool? get nullableByDefault => defaultNullability;
@override
bool isSubtypeOf(HeapType other) =>
- other == HeapType.any || other == HeapType.eq || other == HeapType.data;
+ other == HeapType.top ||
+ other == HeapType.any ||
+ other == HeapType.eq ||
+ other == HeapType.data;
@override
void serialize(Serializer s) => s.writeByte(0x67);
@@ -320,14 +374,17 @@
class I31HeapType extends HeapType {
const I31HeapType._();
- static const defaultNullability = false;
+ static const defaultNullability = true;
@override
bool? get nullableByDefault => defaultNullability;
@override
bool isSubtypeOf(HeapType other) =>
- other == HeapType.any || other == HeapType.eq || other == HeapType.i31;
+ other == HeapType.top ||
+ other == HeapType.any ||
+ other == HeapType.eq ||
+ other == HeapType.i31;
@override
void serialize(Serializer s) => s.writeByte(0x6A);
@@ -401,7 +458,7 @@
@override
bool isStructuralSubtypeOf(HeapType other) {
- if (other == HeapType.any || other == HeapType.func) return true;
+ if (other == HeapType.top || other == HeapType.func) return true;
if (other is! FunctionType) return false;
if (inputs.length != other.inputs.length) return false;
if (outputs.length != other.outputs.length) return false;
@@ -455,7 +512,8 @@
@override
bool isStructuralSubtypeOf(HeapType other) {
- if (other == HeapType.any ||
+ if (other == HeapType.top ||
+ other == HeapType.any ||
other == HeapType.eq ||
other == HeapType.data) {
return true;
@@ -492,7 +550,8 @@
@override
bool isStructuralSubtypeOf(HeapType other) {
- if (other == HeapType.any ||
+ if (other == HeapType.top ||
+ other == HeapType.any ||
other == HeapType.eq ||
other == HeapType.data) {
return true;
diff --git a/sdk/lib/_internal/wasm/lib/async_patch.dart b/sdk/lib/_internal/wasm/lib/async_patch.dart
index 8d112bd..ba75660 100644
--- a/sdk/lib/_internal/wasm/lib/async_patch.dart
+++ b/sdk/lib/_internal/wasm/lib/async_patch.dart
@@ -64,7 +64,7 @@
@pragma("wasm:export", "\$asyncBridge")
WasmAnyRef? _asyncBridge(
- WasmAnyRef? stack, WasmDataRef args, Completer<Object?> completer) {
+ WasmExternRef? stack, WasmDataRef args, Completer<Object?> completer) {
try {
Object? result = _asyncBridge2(args, stack);
completer.complete(result);
@@ -73,7 +73,7 @@
}
}
-external Object? _asyncBridge2(WasmDataRef args, WasmAnyRef? stack);
+external Object? _asyncBridge2(WasmDataRef args, WasmExternRef? stack);
class _FutureError {
final Object exception;
@@ -83,10 +83,11 @@
}
@pragma("wasm:entry-point")
-Object? _awaitHelper(Object? operand, WasmAnyRef? stack) {
+Object? _awaitHelper(Object? operand, WasmExternRef? stack) {
if (operand is! Future) return operand;
- WasmAnyRef futureRef = WasmAnyRef.fromObject(operand);
- Object? result = unsafeCastOpaque(_futurePromise(stack, futureRef));
+ WasmExternRef futureRef = WasmAnyRef.fromObject(operand).externalize();
+ Object? result =
+ unsafeCastOpaque(_futurePromise(stack, futureRef).internalize());
if (result is _FutureError) {
// TODO(askesc): Combine stack traces
throw result.exception;
@@ -95,7 +96,8 @@
}
@pragma("wasm:import", "dart2wasm.futurePromise")
-external WasmAnyRef? _futurePromise(WasmAnyRef? stack, WasmAnyRef? future);
+external WasmExternRef? _futurePromise(
+ WasmExternRef? stack, WasmExternRef? future);
@pragma("wasm:export", "\$awaitCallback")
void _awaitCallback(Future<Object?> future, WasmAnyRef resolve) {
diff --git a/sdk/lib/wasm/wasm_types.dart b/sdk/lib/wasm/wasm_types.dart
index 8139271..0508a0a 100644
--- a/sdk/lib/wasm/wasm_types.dart
+++ b/sdk/lib/wasm/wasm_types.dart
@@ -34,18 +34,40 @@
///
/// Will throw if the reference is not a Dart object.
external Object toObject();
+
+ WasmExternRef externalize() => _externalizeNonNullable(this);
}
+extension ExternalizeNullable on WasmAnyRef? {
+ WasmExternRef? externalize() => _externalizeNullable(this);
+}
+
+/// The Wasm `externref` type.
+@pragma("wasm:entry-point")
+class WasmExternRef extends _WasmBase {
+ WasmAnyRef internalize() => _internalizeNonNullable(this);
+}
+
+extension InternalizeNullable on WasmExternRef? {
+ WasmAnyRef? internalize() => _internalizeNullable(this);
+}
+
+// TODO(askesc): Add intrinsics for these when the `intern.externalize` and
+// `extern.internalize` instructions are implemented in V8.
+@pragma("wasm:import", "dart2wasm.roundtrip")
+external WasmExternRef _externalizeNonNullable(WasmAnyRef ref);
+@pragma("wasm:import", "dart2wasm.roundtrip")
+external WasmExternRef? _externalizeNullable(WasmAnyRef? ref);
+@pragma("wasm:import", "dart2wasm.roundtrip")
+external WasmAnyRef _internalizeNonNullable(WasmExternRef ref);
+@pragma("wasm:import", "dart2wasm.roundtrip")
+external WasmAnyRef? _internalizeNullable(WasmExternRef? ref);
+
/// The Wasm `funcref` type.
@pragma("wasm:entry-point")
-class WasmFuncRef extends WasmAnyRef {
+class WasmFuncRef extends _WasmBase {
/// Upcast typed function reference to `funcref`
external factory WasmFuncRef.fromWasmFunction(WasmFunction<Function> fun);
-
- /// Downcast `anyref` to `funcref`.
- ///
- /// Will throw if the reference is not a `funcref`.
- external factory WasmFuncRef.fromRef(WasmAnyRef ref);
}
/// The Wasm `eqref` type.
@@ -143,10 +165,10 @@
/// parameters and no type parameters.
external factory WasmFunction.fromFunction(F f);
- /// Downcast `anyref` to a typed function reference.
+ /// Downcast `funcref` to a typed function reference.
///
/// Will throw if the reference is not a function with the expected signature.
- external factory WasmFunction.fromRef(WasmAnyRef ref);
+ external factory WasmFunction.fromFuncRef(WasmFuncRef ref);
/// Call the function referred to by this typed function reference.
@pragma("wasm:entry-point")
diff --git a/tests/web/wasm/table_test.dart b/tests/web/wasm/table_test.dart
index fb6c31c..df95963 100644
--- a/tests/web/wasm/table_test.dart
+++ b/tests/web/wasm/table_test.dart
@@ -25,12 +25,13 @@
funcrefTable[2.toWasmI32()] = WasmFunction.fromFunction(f3);
// Reading and calling functions in untyped function table
- WasmFunction<void Function()>.fromRef(funcrefTable[0.toWasmI32()]!).call();
- WasmFunction<void Function(int)>.fromRef(funcrefTable[1.toWasmI32()]!)
+ WasmFunction<void Function()>.fromFuncRef(funcrefTable[0.toWasmI32()]!)
+ .call();
+ WasmFunction<void Function(int)>.fromFuncRef(funcrefTable[1.toWasmI32()]!)
.call(4);
Expect.equals(
6,
- WasmFunction<int Function(int)>.fromRef(funcrefTable[2.toWasmI32()]!)
+ WasmFunction<int Function(int)>.fromFuncRef(funcrefTable[2.toWasmI32()]!)
.call(5));
// Calling functions in untyped function table with callIndirect
diff --git a/tests/web/wasm/wasm_types_test.dart b/tests/web/wasm/wasm_types_test.dart
index 5538605..0ce54a3 100644
--- a/tests/web/wasm/wasm_types_test.dart
+++ b/tests/web/wasm/wasm_types_test.dart
@@ -14,7 +14,7 @@
@pragma("wasm:import", "Reflect.apply")
external WasmAnyRef apply(
- WasmAnyRef target, WasmAnyRef thisArgument, WasmAnyRef argumentsList);
+ WasmFuncRef target, WasmAnyRef thisArgument, WasmAnyRef argumentsList);
WasmAnyRef? anyRef;
WasmEqRef? eqRef;
@@ -78,18 +78,11 @@
var ff = WasmFunction.fromFunction(fun);
ff.call(dartObjectRef);
apply(ff, createObject(null), singularArray(dartObjectRef));
- Expect.isFalse(ff.isObject);
// Cast a typed function reference to a `funcref` and back.
WasmFuncRef funcref = WasmFuncRef.fromWasmFunction(ff);
- var ff2 = WasmFunction<void Function(WasmEqRef)>.fromRef(funcref);
+ var ff2 = WasmFunction<void Function(WasmEqRef)>.fromFuncRef(funcref);
ff2.call(dartObjectRef);
- Expect.isFalse(ff2.isObject);
-
- // Casting a non-function JS object to a typed function reference throws.
- Expect.throws(() {
- WasmFunction<double Function(double)>.fromRef(jsObject1);
- }, (_) => true);
// Create a typed function reference from an import and call it.
var createObjectFun = WasmFunction.fromFunction(createObject);