Bug 1649247 - wasm: Implement eqref. r=lth

This commit adds support for the eqref value type. It shares the same
representation as externref, with the restriction that all values must
be TypedObject's. Dynamic type checks similar to funcref are added to
stubs.

As fallout from anyref removal, struct types were left as subtypes of
externref. This commit switches them to be subtypes of eqref.

As a small tweak, the ordering on cases when switching on refTypeKind()
were standardized to binary order (i.e. funcref, externref, eqref).

I think we can refactor some code to simplify the process of adding
new reftypes and would like to do that in the future.

Differential Revision: https://phabricator.services.mozilla.com/D93000
This commit is contained in:
Ryan Hunt 2020-10-14 21:17:44 +00:00
parent 2e476d423a
commit 385de45350
14 changed files with 171 additions and 42 deletions

View File

@ -444,6 +444,7 @@ MSG_DEF(JSMSG_WASM_BAD_IMPORT_ARG, 0, JSEXN_TYPEERR, "second argument mu
MSG_DEF(JSMSG_WASM_BAD_IMPORT_FIELD, 1, JSEXN_TYPEERR, "import object field '{0}' is not an Object")
MSG_DEF(JSMSG_WASM_BAD_REF_NONNULLABLE_VALUE, 0, JSEXN_TYPEERR, "cannot pass null to non-nullable WebAssembly reference")
MSG_DEF(JSMSG_WASM_BAD_FUNCREF_VALUE, 0, JSEXN_TYPEERR, "can only pass WebAssembly exported functions to funcref")
MSG_DEF(JSMSG_WASM_BAD_EQREF_VALUE, 0, JSEXN_TYPEERR, "can only pass a TypedObject to an eqref")
MSG_DEF(JSMSG_WASM_BAD_VAL_TYPE, 0, JSEXN_TYPEERR, "cannot pass v128 to or from JS")
MSG_DEF(JSMSG_WASM_BAD_GLOBAL_TYPE, 0, JSEXN_TYPEERR, "bad type for a WebAssembly.Global")
MSG_DEF(JSMSG_WASM_NO_TRANSFER, 0, JSEXN_TYPEERR, "cannot transfer WebAssembly/asm.js ArrayBuffer")

View File

@ -15324,6 +15324,7 @@ void CodeGenerator::emitIonToWasmCallBase(LIonToWasmCallBase<NumDefs>* lir) {
argMir = ToMIRType(sig.args()[i]);
break;
case wasm::RefType::Func:
case wasm::RefType::Eq:
case wasm::RefType::TypeIndex:
MOZ_CRASH("unexpected argument type when calling from ion to wasm");
}
@ -15389,12 +15390,13 @@ void CodeGenerator::emitIonToWasmCallBase(LIonToWasmCallBase<NumDefs>* lir) {
MOZ_CRASH("unexpected return type when calling from ion to wasm");
case wasm::ValType::Ref:
switch (results[0].refTypeKind()) {
case wasm::RefType::Extern:
case wasm::RefType::Func:
case wasm::RefType::Extern:
case wasm::RefType::Eq:
// The wasm stubs layer unboxes anything that needs to be unboxed
// and leaves it in a Value. A FuncRef we could in principle leave
// as a raw object pointer but for now it complicates the API to do
// so.
// and leaves it in a Value. A FuncRef/EqRef we could in principle
// leave it as a raw object pointer but for now it complicates the
// API to do so.
MOZ_ASSERT(lir->mir()->type() == MIRType::Value);
break;
case wasm::RefType::TypeIndex:

View File

@ -635,6 +635,7 @@ static int32_t CoerceInPlace_JitEntry(int funcExportIndex, TlsData* tlsData,
}
break;
case RefType::Func:
case RefType::Eq:
case RefType::TypeIndex:
// Guarded against by temporarilyUnsupportedReftypeForEntry()
MOZ_CRASH("unexpected input argument in CoerceInPlace_JitEntry");

View File

@ -65,6 +65,9 @@ enum class TypeCode {
// A reference to any host value.
ExternRef = 0x6f, // SLEB128(-0x11)
// A reference to a struct/array value.
EqRef = 0x6d, // SLEB128(-0x12)
// Type constructor for nullable reference types.
NullableRef = 0x6c, // SLEB128(-0x14)

View File

@ -161,8 +161,8 @@ static bool ToWebAssemblyValue_f64(JSContext* cx, HandleValue val,
return ok;
}
template <typename Debug = NoDebug>
static bool ToWebAssemblyValue_anyref(JSContext* cx, HandleValue val,
void** loc) {
static bool ToWebAssemblyValue_externref(JSContext* cx, HandleValue val,
void** loc) {
RootedAnyRef result(cx, AnyRef::null());
if (!BoxAnyRef(cx, val, &result)) {
return false;
@ -172,6 +172,17 @@ static bool ToWebAssemblyValue_anyref(JSContext* cx, HandleValue val,
return true;
}
template <typename Debug = NoDebug>
static bool ToWebAssemblyValue_eqref(JSContext* cx, HandleValue val,
void** loc) {
RootedAnyRef result(cx, AnyRef::null());
if (!CheckEqRefValue(cx, val, &result)) {
return false;
}
*loc = result.get().forCompiledCode();
Debug::print(*loc);
return true;
}
template <typename Debug = NoDebug>
static bool ToWebAssemblyValue_funcref(JSContext* cx, HandleValue val,
void** loc) {
RootedFunction fun(cx);
@ -218,7 +229,9 @@ static bool ToWebAssemblyValue(JSContext* cx, HandleValue val, ValType type,
case RefType::Func:
return ToWebAssemblyValue_funcref<Debug>(cx, val, (void**)loc);
case RefType::Extern:
return ToWebAssemblyValue_anyref<Debug>(cx, val, (void**)loc);
return ToWebAssemblyValue_externref<Debug>(cx, val, (void**)loc);
case RefType::Eq:
return ToWebAssemblyValue_eqref<Debug>(cx, val, (void**)loc);
case RefType::TypeIndex:
return ToWebAssemblyValue_typeref<Debug>(cx, val, (void**)loc);
}
@ -327,6 +340,9 @@ static bool ToJSValue(JSContext* cx, const void* src, ValType type,
case RefType::Extern:
return ToJSValue_anyref<Debug>(
cx, *reinterpret_cast<void* const*>(src), dst);
case RefType::Eq:
return ToJSValue_anyref<Debug>(
cx, *reinterpret_cast<void* const*>(src), dst);
case RefType::TypeIndex:
return ToJSValue_typeref<Debug>(
cx, *reinterpret_cast<void* const*>(src), dst);
@ -581,6 +597,7 @@ bool Instance::callImport(JSContext* cx, uint32_t funcImportIndex,
// dynamically in the callee. Code in the stubs layer must box up
// the FuncRef as a Value.
break;
case RefType::Eq:
case RefType::TypeIndex:
// Guarded by temporarilyUnsupportedReftypeForExit()
MOZ_CRASH("case guarded above");
@ -2129,7 +2146,8 @@ bool Instance::callExport(JSContext* cx, uint32_t funcIndex, CallArgs args) {
}
break;
}
case RefType::Extern: {
case RefType::Extern:
case RefType::Eq: {
RootedAnyRef ref(cx, AnyRef::fromCompiledCode(ptr));
ASSERT_ANYREF_IS_JSOBJECT;
if (!refs.emplaceBack(ref.get().asJSObject())) {

View File

@ -2667,6 +2667,7 @@ static bool EmitGetGlobal(FunctionCompiler& f) {
switch (value.type().refTypeKind()) {
case RefType::Func:
case RefType::Extern:
case RefType::Eq:
MOZ_ASSERT(value.ref().isNull());
result = f.nullRefConstant();
break;

View File

@ -467,6 +467,11 @@ bool wasm::CheckRefType(JSContext* cx, RefType targetType, HandleValue v,
return false;
}
break;
case RefType::Eq:
if (!CheckEqRefValue(cx, v, refval)) {
return false;
}
break;
case RefType::TypeIndex:
MOZ_CRASH("temporarily unsupported Ref type");
}
@ -518,6 +523,7 @@ static bool ToWebAssemblyValue(JSContext* cx, ValType targetType, HandleValue v,
case RefType::Func:
val.set(Val(RefType::func(), FuncRef::fromJSFunction(fun)));
return true;
case RefType::Eq:
case RefType::Extern:
val.set(Val(targetType.refType(), any));
return true;
@ -557,6 +563,7 @@ static bool ToJSValue(JSContext* cx, const Val& val, MutableHandleValue out) {
case RefType::Func:
out.set(UnboxFuncRef(FuncRef::fromAnyRefUnchecked(val.ref())));
return true;
case RefType::Eq:
case RefType::Extern:
out.set(UnboxAnyRef(val.ref()));
return true;
@ -2079,6 +2086,26 @@ bool wasm::CheckFuncRefValue(JSContext* cx, HandleValue v,
return false;
}
bool wasm::CheckEqRefValue(JSContext* cx, HandleValue v,
MutableHandleAnyRef vp) {
if (v.isNull()) {
vp.set(AnyRef::null());
return true;
}
if (v.isObject()) {
JSObject& obj = v.toObject();
if (obj.is<TypedObject>()) {
vp.set(AnyRef::fromJSObject(&obj.as<TypedObject>()));
return true;
}
}
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
JSMSG_WASM_BAD_EQREF_VALUE);
return false;
}
Instance& wasm::ExportedFunctionToInstance(JSFunction* fun) {
return ExportedFunctionToInstanceObject(fun)->instance();
}
@ -2666,6 +2693,15 @@ bool WasmTableObject::construct(JSContext* cx, unsigned argc, Value* vp) {
return false;
}
tableType = RefType::extern_();
#endif
#ifdef ENABLE_WASM_GC
} else if (StringEqualsLiteral(elementLinearStr, "eqref")) {
if (!GcTypesAvailable(cx)) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
JSMSG_WASM_BAD_ELEMENT);
return false;
}
tableType = RefType::eq();
#endif
} else {
#ifdef ENABLE_WASM_REFTYPES
@ -2875,16 +2911,14 @@ bool WasmTableObject::setImpl(JSContext* cx, const CallArgs& args) {
if (!CheckRefType(cx, table.elemType(), fillValue, &fun, &any)) {
return false;
}
switch (table.elemType().kind()) {
case RefType::Func:
switch (table.repr()) {
case TableRepr::Func:
MOZ_RELEASE_ASSERT(!table.isAsmJS());
table.fillFuncRef(index, 1, FuncRef::fromJSFunction(fun), cx);
break;
case RefType::Extern:
case TableRepr::Ref:
table.fillAnyRef(index, 1, any);
break;
case RefType::TypeIndex:
MOZ_CRASH("Ref NYI");
}
args.rval().setUndefined();
@ -3043,6 +3077,7 @@ void WasmGlobalObject::trace(JSTracer* trc, JSObject* obj) {
switch (global->type().refTypeKind()) {
case RefType::Func:
case RefType::Extern:
case RefType::Eq:
if (!global->cell()->ref.isNull()) {
// TODO/AnyRef-boxing: With boxed immediates and strings, the write
// barrier is going to have to be more complicated.
@ -3107,6 +3142,7 @@ WasmGlobalObject* WasmGlobalObject::create(JSContext* cx, HandleVal hval,
switch (val.type().refTypeKind()) {
case RefType::Func:
case RefType::Extern:
case RefType::Eq:
MOZ_ASSERT(cell->ref.isNull(), "no prebarriers needed");
cell->ref = val.ref();
if (!cell->ref.isNull()) {
@ -3201,6 +3237,11 @@ bool WasmGlobalObject::construct(JSContext* cx, unsigned argc, Value* vp) {
} else if (ReftypesAvailable(cx) &&
StringEqualsLiteral(typeLinearStr, "externref")) {
globalType = RefType::extern_();
#endif
#ifdef ENABLE_WASM_GC
} else if (GcTypesAvailable(cx) &&
StringEqualsLiteral(typeLinearStr, "eqref")) {
globalType = RefType::eq();
#endif
} else {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
@ -3238,6 +3279,9 @@ bool WasmGlobalObject::construct(JSContext* cx, unsigned argc, Value* vp) {
case RefType::Extern:
globalVal = Val(RefType::extern_(), AnyRef::null());
break;
case RefType::Eq:
globalVal = Val(RefType::eq(), AnyRef::null());
break;
case RefType::TypeIndex:
MOZ_CRASH("Ref NYI");
}
@ -3299,6 +3343,7 @@ bool WasmGlobalObject::valueGetterImpl(JSContext* cx, const CallArgs& args) {
args.thisv().toObject().as<WasmGlobalObject>().type().refTypeKind()) {
case RefType::Func:
case RefType::Extern:
case RefType::Eq:
args.thisv().toObject().as<WasmGlobalObject>().value(cx, args.rval());
return true;
case RefType::TypeIndex:
@ -3397,7 +3442,8 @@ void WasmGlobalObject::setVal(JSContext* cx, wasm::HandleVal hval) {
case ValType::Ref:
switch (this->type().refTypeKind()) {
case RefType::Func:
case RefType::Extern: {
case RefType::Extern:
case RefType::Eq: {
AnyRef prevPtr = cell->ref;
// TODO/AnyRef-boxing: With boxed immediates and strings, the write
// barrier is going to have to be more complicated.
@ -3482,6 +3528,9 @@ void WasmGlobalObject::val(MutableHandleVal outval) const {
case RefType::Extern:
outval.set(Val(RefType::extern_(), cell->ref));
return;
case RefType::Eq:
outval.set(Val(RefType::eq(), cell->ref));
return;
case RefType::TypeIndex:
MOZ_CRASH("Ref NYI");
}

View File

@ -163,6 +163,8 @@ MOZ_MUST_USE bool DeserializeModule(JSContext* cx, const Bytes& serialized,
bool IsWasmExportedFunction(JSFunction* fun);
MOZ_MUST_USE bool CheckFuncRefValue(JSContext* cx, HandleValue v,
MutableHandleFunction fun);
MOZ_MUST_USE bool CheckEqRefValue(JSContext* cx, HandleValue v,
MutableHandleAnyRef vp);
Instance& ExportedFunctionToInstance(JSFunction* fun);
WasmInstanceObject* ExportedFunctionToInstanceObject(JSFunction* fun);

View File

@ -1203,6 +1203,7 @@ static bool MakeStructField(JSContext* cx, const ValType& v, bool isMutable,
case ValType::Ref:
switch (v.refTypeKind()) {
case RefType::TypeIndex:
case RefType::Eq:
t = GlobalObject::getOrCreateReferenceTypeDescr(
cx, cx->global(), ReferenceType::TYPE_OBJECT);
break;

View File

@ -1176,6 +1176,7 @@ static bool GenerateJitEntry(MacroAssembler& masm, size_t funcExportIndex,
break;
}
case RefType::Func:
case RefType::Eq:
case RefType::TypeIndex: {
// Guarded against by temporarilyUnsupportedReftypeForEntry()
MOZ_CRASH("unexpected argument type when calling from the jit");
@ -1346,7 +1347,9 @@ static bool GenerateJitEntry(MacroAssembler& masm, size_t funcExportIndex,
case ValType::Ref: {
switch (results[0].refTypeKind()) {
case RefType::Func:
// For FuncRef use the AnyRef path for now, since that will work.
case RefType::Eq:
// For FuncRef and EqRef use the AnyRef path for now, since that
// will work.
case RefType::Extern:
// Per comment above, the call may have clobbered the Tls register,
// so reload since unboxing will need it.
@ -1642,7 +1645,9 @@ void wasm::GenerateDirectCallFromJit(MacroAssembler& masm, const FuncExport& fe,
case wasm::ValType::Ref:
switch (results[0].refTypeKind()) {
case wasm::RefType::Func:
// For FuncRef, use the AnyRef path for now, since that will work.
case wasm::RefType::Eq:
// For FuncRef and EqRef, use the AnyRef path for now, since that
// will work.
case wasm::RefType::Extern:
// The call to wasm above preserves the WasmTlsReg, we don't need to
// reload it here.
@ -2163,6 +2168,7 @@ static bool GenerateImportInterpExit(MacroAssembler& masm, const FuncImport& fi,
GenPrintPtr(DebugChannel::Import, masm, ReturnReg);
break;
case RefType::Extern:
case RefType::Eq:
masm.loadPtr(argv, ReturnReg);
GenPrintf(DebugChannel::Import, masm, "wasm-import[%u]; returns ",
funcImportIndex);
@ -2402,6 +2408,7 @@ static bool GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi,
GenPrintPtr(DebugChannel::Import, masm, ReturnReg);
break;
case RefType::Func:
case RefType::Eq:
case RefType::TypeIndex:
MOZ_CRASH("typed reference returned by import (jit exit) NYI");
}
@ -2507,6 +2514,7 @@ static bool GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi,
throwLabel);
break;
case RefType::Func:
case RefType::Eq:
case RefType::TypeIndex:
MOZ_CRASH("Unsupported convert type");
}

View File

@ -229,6 +229,7 @@ static bool IsImmediateType(ValType vt) {
switch (vt.refTypeKind()) {
case RefType::Func:
case RefType::Extern:
case RefType::Eq:
return true;
case RefType::TypeIndex:
return false;
@ -257,6 +258,8 @@ static unsigned EncodeImmediateType(ValType vt) {
return 5;
case RefType::Extern:
return 6;
case RefType::Eq:
return 7;
case RefType::TypeIndex:
break;
}
@ -986,11 +989,14 @@ UniqueChars wasm::ToString(ValType type) {
case ValType::Ref:
if (type.isNullable() && !type.isTypeIndex()) {
switch (type.refTypeKind()) {
case RefType::Func:
literal = "funcref";
break;
case RefType::Extern:
literal = "externref";
break;
case RefType::Func:
literal = "funcref";
case RefType::Eq:
literal = "eqref";
break;
case RefType::TypeIndex:
MOZ_ASSERT_UNREACHABLE();
@ -998,11 +1004,14 @@ UniqueChars wasm::ToString(ValType type) {
} else {
const char* heapType = nullptr;
switch (type.refTypeKind()) {
case RefType::Func:
heapType = "func";
break;
case RefType::Extern:
heapType = "extern";
break;
case RefType::Func:
heapType = "func";
case RefType::Eq:
heapType = "eq";
break;
case RefType::TypeIndex:
return JS_smprintf("(ref %s%d)", type.isNullable() ? "null " : "",

View File

@ -417,8 +417,9 @@ enum class TableRepr { Ref, Func };
class RefType {
public:
enum Kind {
Extern = uint8_t(TypeCode::ExternRef),
Func = uint8_t(TypeCode::FuncRef),
Extern = uint8_t(TypeCode::ExternRef),
Eq = uint8_t(TypeCode::EqRef),
TypeIndex = uint8_t(AbstractReferenceTypeIndexCode)
};
@ -430,6 +431,7 @@ class RefType {
switch (UnpackTypeCodeType(ptc_)) {
case TypeCode::FuncRef:
case TypeCode::ExternRef:
case TypeCode::EqRef:
MOZ_ASSERT(UnpackTypeCodeIndexUnchecked(ptc_) == NoRefTypeIndex);
return true;
case AbstractReferenceTypeIndexCode:
@ -470,21 +472,24 @@ class RefType {
PackedTypeCode packed() const { return ptc_; }
static RefType extern_() { return RefType(Extern, true); }
static RefType func() { return RefType(Func, true); }
static RefType extern_() { return RefType(Extern, true); }
static RefType eq() { return RefType(Eq, true); }
bool isExtern() const { return kind() == RefType::Extern; }
bool isFunc() const { return kind() == RefType::Func; }
bool isExtern() const { return kind() == RefType::Extern; }
bool isEq() const { return kind() == RefType::Eq; }
bool isTypeIndex() const { return kind() == RefType::TypeIndex; }
bool isNullable() const { return UnpackTypeCodeNullable(ptc_); }
TableRepr tableRepr() const {
switch (kind()) {
case RefType::Extern:
return TableRepr::Ref;
case RefType::Func:
return TableRepr::Func;
case RefType::Extern:
case RefType::Eq:
return TableRepr::Ref;
case RefType::TypeIndex:
MOZ_CRASH("NYI");
}
@ -510,8 +515,9 @@ class ValType {
case TypeCode::F32:
case TypeCode::F64:
case TypeCode::V128:
case TypeCode::ExternRef:
case TypeCode::FuncRef:
case TypeCode::ExternRef:
case TypeCode::EqRef:
case AbstractReferenceTypeIndexCode:
return true;
default:
@ -611,13 +617,15 @@ class ValType {
return PackedTypeCodeToBits(tc_);
}
bool isFuncRef() const {
return UnpackTypeCodeType(tc_) == TypeCode::FuncRef;
}
bool isExternRef() const {
return UnpackTypeCodeType(tc_) == TypeCode::ExternRef;
}
bool isFuncRef() const {
return UnpackTypeCodeType(tc_) == TypeCode::FuncRef;
}
bool isEqRef() const { return UnpackTypeCodeType(tc_) == TypeCode::EqRef; }
bool isNullable() const {
MOZ_ASSERT(isReference());
@ -660,8 +668,9 @@ class ValType {
// that may (temporarily) simplify some code.
bool isEncodedAsJSValueOnEscape() const {
switch (typeCode()) {
case TypeCode::ExternRef:
case TypeCode::FuncRef:
case TypeCode::ExternRef:
case TypeCode::EqRef:
return true;
default:
return false;
@ -1198,9 +1207,11 @@ class FuncType {
return false;
}
#endif
// For JS->wasm jit entries, AnyRef parameters and returns are allowed, as are
// all reference types apart from TypeIndex. V128 types are excluded per spec
// but are guarded against separately.
// For JS->wasm jit entries, temporarily disallow certain types until the
// stubs generator is improved.
// * ref params may be nullable externrefs
// * ref results may not be type indices
// V128 types are excluded per spec but are guarded against separately.
bool temporarilyUnsupportedReftypeForEntry() const {
for (ValType arg : args()) {
if (arg.isReference() && (!arg.isExternRef() || !arg.isNullable())) {
@ -1214,9 +1225,11 @@ class FuncType {
}
return false;
}
// For inlined JS->wasm jit entries, AnyRef parameters and returns are
// allowed, as are all reference types apart from TypeIndex. V128 types are
// excluded per spec but are guarded against separately.
// For inline JS->wasm jit entries, temporarily disallow certain types until
// the stubs generator is improved.
// * ref params may be nullable externrefs
// * ref results may not be type indices
// V128 types are excluded per spec but are guarded against separately.
bool temporarilyUnsupportedReftypeForInlineEntry() const {
for (ValType arg : args()) {
if (arg.isReference() && (!arg.isExternRef() || !arg.isNullable())) {
@ -1230,9 +1243,11 @@ class FuncType {
}
return false;
}
// For wasm->JS jit exits, AnyRef parameters and returns are allowed, as are
// reference type parameters of all types except TypeIndex. V128 types are
// excluded per spec but are guarded against separately.
// For wasm->JS jit exits, temporarily disallow certain types until
// the stubs generator is improved.
// * ref params may not be type indices
// * ref results may be nullable externrefs
// V128 types are excluded per spec but are guarded against separately.
bool temporarilyUnsupportedReftypeForExit() const {
for (ValType arg : args()) {
if (arg.isTypeIndex()) {
@ -3319,8 +3334,9 @@ class DebugFrame {
return;
case ValType::Ref:
switch (type.refTypeKind()) {
case RefType::Extern:
case RefType::Func:
case RefType::Extern:
case RefType::Eq:
case RefType::TypeIndex:
return;
}

View File

@ -1695,6 +1695,7 @@ static bool DecodeStructType(Decoder& d, ModuleEnvironment* env,
break;
case ValType::Ref:
switch (fields[i].type.refTypeKind()) {
case RefType::Eq:
case RefType::TypeIndex:
offset = layout.addReference(ReferenceType::TYPE_OBJECT);
break;
@ -1948,6 +1949,7 @@ static bool GlobalIsJSCompatible(Decoder& d, ValType type) {
switch (type.refTypeKind()) {
case RefType::Func:
case RefType::Extern:
case RefType::Eq:
break;
case RefType::TypeIndex:
#ifdef WASM_PRIVATE_REFTYPES

View File

@ -211,8 +211,8 @@ struct ModuleEnvironment {
# ifdef ENABLE_WASM_GC
// gc can only be enabled if function-references is enabled
if (gcTypesEnabled()) {
// Structs are subtypes of ExternRef.
if (isStructType(one) && two.isExtern()) {
// Structs are subtypes of EqRef.
if (isStructType(one) && two.isEq()) {
return true;
}
// Struct One is a subtype of struct Two if Two is a prefix of One.
@ -715,6 +715,14 @@ class Decoder {
*type = refType;
return true;
}
#endif
#ifdef ENABLE_WASM_GC
case uint8_t(TypeCode::EqRef):
if (!features.gcTypes) {
return fail("gc types not enabled");
}
*type = RefType::fromTypeCode(TypeCode(code), true);
return true;
#endif
default:
return fail("bad type");
@ -749,6 +757,14 @@ class Decoder {
case uint8_t(TypeCode::ExternRef):
*type = RefType::fromTypeCode(TypeCode(code), nullable);
return true;
#ifdef ENABLE_WASM_GC
case uint8_t(TypeCode::EqRef):
if (!features.gcTypes) {
return fail("gc types not enabled");
}
*type = RefType::fromTypeCode(TypeCode(code), nullable);
return true;
#endif
default:
return fail("invalid heap type");
}