Bug 1732782 - Fix Wasm exceptions JS API for layout changes. r=rhunt

Fixes how exception payloads are written to and read from
exception objects using the JS API, which was broken when
the Wasm instructions transitioned to using an alignment-aware
tag type to find the offsets into the payload.

Differential Revision: https://phabricator.services.mozilla.com/D126853
This commit is contained in:
Asumu Takikawa 2021-10-01 17:53:22 +00:00
Родитель 8e59b5f49b
Коммит b0627b0ec2
8 изменённых файлов: 233 добавлений и 85 удалений

Просмотреть файл

@ -96,7 +96,7 @@ assertErrorMessage(
/first argument must be a WebAssembly.Tag/ /first argument must be a WebAssembly.Tag/
); );
const { tag1, tag2, tag3, tag4, tag5, tag6, tag7 } = wasmEvalText( const { tag1, tag2, tag3, tag4, tag5, tag6, tag7, tag8, tag9 } = wasmEvalText(
`(module `(module
(tag (export "tag1") (param)) (tag (export "tag1") (param))
(tag (export "tag2") (param i32)) (tag (export "tag2") (param i32))
@ -104,7 +104,9 @@ const { tag1, tag2, tag3, tag4, tag5, tag6, tag7 } = wasmEvalText(
(tag (export "tag4") (param i32 externref i32)) (tag (export "tag4") (param i32 externref i32))
(tag (export "tag5") (param i32 externref i32 externref)) (tag (export "tag5") (param i32 externref i32 externref))
(tag (export "tag6") (param funcref)) (tag (export "tag6") (param funcref))
(tag (export "tag7") (param i64)))` (tag (export "tag7") (param i64))
(tag (export "tag8") (param i32 f64))
(tag (export "tag9") (param externref funcref)))`
).exports; ).exports;
new WebAssembly.Exception(tag1, []); new WebAssembly.Exception(tag1, []);
@ -168,25 +170,23 @@ assertErrorMessage(
const exn2 = new WebAssembly.Exception(tag2, [3]); const exn2 = new WebAssembly.Exception(tag2, [3]);
assertEq(exn2.getArg(tag2, 0), 3); assertEq(exn2.getArg(tag2, 0), 3);
assertEq( assertEq(new WebAssembly.Exception(tag2, [undefined]).getArg(tag2, 0), 0);
new WebAssembly.Exception(tag2, [undefined]).getArg(tag2, 0),
0
);
const exn4 = new WebAssembly.Exception(tag4, [3, "foo", 4]); const exn4 = new WebAssembly.Exception(tag4, [3, "foo", 4]);
assertEq(exn4.getArg(tag4, 0), 3); assertEq(exn4.getArg(tag4, 0), 3);
assertEq(exn4.getArg(tag4, 1), "foo"); assertEq(exn4.getArg(tag4, 1), "foo");
assertEq(exn4.getArg(tag4, 2), 4); assertEq(exn4.getArg(tag4, 2), 4);
const exn5 = new WebAssembly.Exception( const exn5 = new WebAssembly.Exception(tag5, [3, "foo", 4, "bar"]);
tag5,
[3,
"foo",
4,
"bar"]
);
assertEq(exn5.getArg(tag5, 3), "bar"); assertEq(exn5.getArg(tag5, 3), "bar");
const { funcref } = wasmEvalText(
`(module (func (export "funcref")))`
).exports;
const exn9 = new WebAssembly.Exception(tag9, ["foo", funcref]);
assertEq(exn9.getArg(tag9, 0), "foo");
assertEq(exn9.getArg(tag9, 1), funcref);
assertErrorMessage( assertErrorMessage(
() => exn2.getArg(), () => exn2.getArg(),
TypeError, TypeError,
@ -257,6 +257,30 @@ assertEqArray(
[42, 5.5] [42, 5.5]
); );
assertEqArray(
wasmEvalText(
`(module
(import "m" "exn" (tag $exn (param i32 f64)))
(import "m" "f" (func $f))
(func (export "f") (result i32 f64)
try (result i32 f64)
call $f
(i32.const 0)
(f64.const 0)
catch $exn
end))`,
{
m: {
exn: tag8,
f: () => {
throw new WebAssembly.Exception(tag8, [9999, 9999]);
},
},
}
).exports.f(),
[9999, 9999]
);
assertEqArray( assertEqArray(
wasmEvalText( wasmEvalText(
`(module `(module
@ -282,6 +306,61 @@ assertEqArray(
[42, "foo", 42] [42, "foo", 42]
); );
assertEqArray(
wasmEvalText(
`(module
(import "m" "exn" (tag $exn (param i32 externref i32 externref)))
(import "m" "f" (func $f))
(func (export "f") (result i32 externref i32 externref)
try (result i32 externref i32 externref)
call $f
(i32.const 0)
(ref.null extern)
(i32.const 0)
(ref.null extern)
catch $exn
end))`,
{
m: {
exn: tag5,
f: () => {
throw new WebAssembly.Exception(tag5, [42, "foo", 42, "bar"]);
},
},
}
).exports.f(),
[42, "foo", 42, "bar"]
);
{
const { funcref } = wasmEvalText(
`(module (func (export "funcref")))`
).exports;
assertEqArray(
wasmEvalText(
`(module
(import "m" "exn" (tag $exn (param externref funcref)))
(import "m" "f" (func $f))
(func (export "f") (result externref funcref)
try (result externref funcref)
call $f
(ref.null extern)
(ref.null func)
catch $exn
end))`,
{
m: {
exn: tag9,
f: () => {
throw new WebAssembly.Exception(tag9, ["foo", funcref]);
},
},
}
).exports.f(),
["foo", funcref]
);
}
assertEq( assertEq(
wasmEvalText( wasmEvalText(
`(module `(module
@ -323,7 +402,7 @@ assertEq(
end))`, end))`,
{ {
m: { m: {
exn: exn, exn,
f: () => { f: () => {
throw new WebAssembly.Exception(exn, [42]); throw new WebAssembly.Exception(exn, [42]);
}, },
@ -362,3 +441,49 @@ assertEq(
1 1
); );
} }
// Test `getArg` on a Wasm-thrown exception.
assertEq(
(() => {
try {
wasmEvalText(
`(module
(import "m" "exn" (tag $exn (param i32 f64)))
(func (export "f")
(i32.const 9999)
(f64.const 9999)
throw $exn))`,
{ m: { exn: tag8 } }
).exports.f();
} catch (exn) {
return exn.getArg(tag8, 1);
}
})(),
9999
);
assertEqArray(
(() => {
try {
wasmEvalText(
`(module
(import "m" "exn" (tag $exn (param i32 externref i32 externref)))
(func (export "f") (param externref externref)
(i32.const 1)
(local.get 0)
(i32.const 2)
(local.get 1)
throw $exn))`,
{ m: { exn: tag5 } }
).exports.f("foo", "bar");
} catch (exn) {
return [
exn.getArg(tag5, 0),
exn.getArg(tag5, 1),
exn.getArg(tag5, 2),
exn.getArg(tag5, 3),
];
}
})(),
[1, "foo", 2, "bar"]
);

Просмотреть файл

@ -3574,9 +3574,9 @@ bool BaseCompiler::emitCatch() {
masm.bind(&tryCatch.catchInfos.back().label); masm.bind(&tryCatch.catchInfos.back().label);
// Extract the arguments in the exception package and push them. // Extract the arguments in the exception package and push them.
const TagDesc& tagDesc = moduleEnv_.tags[tagIndex]; const TagType& tagType = moduleEnv_.tags[tagIndex].type;
const ValTypeVector& params = tagDesc.argTypes; const ValTypeVector& params = tagType.argTypes;
const TagOffsetVector& offsets = tagDesc.argOffsets; const TagOffsetVector& offsets = tagType.argOffsets;
const uint32_t dataOffset = const uint32_t dataOffset =
NativeObject::getFixedSlotOffset(ArrayBufferObject::DATA_SLOT); NativeObject::getFixedSlotOffset(ArrayBufferObject::DATA_SLOT);
@ -3601,7 +3601,7 @@ bool BaseCompiler::emitCatch() {
masm.load32(Address(refs, NativeObject::offsetOfFixedElements() + masm.load32(Address(refs, NativeObject::offsetOfFixedElements() +
ObjectElements::offsetOfLength()), ObjectElements::offsetOfLength()),
scratch); scratch);
masm.branch32(Assembler::Equal, scratch, Imm32(tagDesc.refCount), &ok); masm.branch32(Assembler::Equal, scratch, Imm32(tagType.refCount), &ok);
masm.assumeUnreachable("Array length should be equal to exn ref count."); masm.assumeUnreachable("Array length should be equal to exn ref count.");
masm.bind(&ok); masm.bind(&ok);
freeI32(scratch); freeI32(scratch);
@ -3955,11 +3955,11 @@ bool BaseCompiler::emitThrow() {
const TagDesc& tagDesc = moduleEnv_.tags[exnIndex]; const TagDesc& tagDesc = moduleEnv_.tags[exnIndex];
const ResultType& params = tagDesc.resultType(); const ResultType& params = tagDesc.resultType();
const TagOffsetVector& offsets = tagDesc.argOffsets; const TagOffsetVector& offsets = tagDesc.type.argOffsets;
// Create the new exception object that we will throw. // Create the new exception object that we will throw.
pushI32(exnIndex); pushI32(exnIndex);
pushI32(tagDesc.bufferSize); pushI32(tagDesc.type.bufferSize);
if (!emitInstanceCall(lineOrBytecode, SASigExceptionNew)) { if (!emitInstanceCall(lineOrBytecode, SASigExceptionNew)) {
return false; return false;
} }

Просмотреть файл

@ -1463,7 +1463,7 @@ bool WasmModuleObject::imports(JSContext* cx, unsigned argc, Value* vp) {
case DefinitionKind::Tag: { case DefinitionKind::Tag: {
size_t tagIndex = numTagImport++; size_t tagIndex = numTagImport++;
const TagDesc& tag = metadata.tags[tagIndex]; const TagDesc& tag = metadata.tags[tagIndex];
typeObj = TagTypeToObject(cx, tag.argTypes); typeObj = TagTypeToObject(cx, tag.type.argTypes);
break; break;
} }
# endif // ENABLE_WASM_EXCEPTIONS # endif // ENABLE_WASM_EXCEPTIONS
@ -1568,7 +1568,7 @@ bool WasmModuleObject::exports(JSContext* cx, unsigned argc, Value* vp) {
# ifdef ENABLE_WASM_EXCEPTIONS # ifdef ENABLE_WASM_EXCEPTIONS
case DefinitionKind::Tag: { case DefinitionKind::Tag: {
const TagDesc& tag = metadata.tags[exp.tagIndex()]; const TagDesc& tag = metadata.tags[exp.tagIndex()];
typeObj = TagTypeToObject(cx, tag.argTypes); typeObj = TagTypeToObject(cx, tag.type.argTypes);
break; break;
} }
# endif // ENABLE_WASM_EXCEPTIONS # endif // ENABLE_WASM_EXCEPTIONS
@ -3710,7 +3710,7 @@ void WasmTagObject::finalize(JSFreeOp* fop, JSObject* obj) {
WasmTagObject& exnObj = obj->as<WasmTagObject>(); WasmTagObject& exnObj = obj->as<WasmTagObject>();
if (!exnObj.isNewborn()) { if (!exnObj.isNewborn()) {
fop->release(obj, &exnObj.tag(), MemoryUse::WasmTagTag); fop->release(obj, &exnObj.tag(), MemoryUse::WasmTagTag);
fop->delete_(obj, &exnObj.valueTypes(), MemoryUse::WasmTagType); fop->delete_(obj, &exnObj.tagType(), MemoryUse::WasmTagType);
} }
} }
@ -3745,6 +3745,15 @@ bool WasmTagObject::construct(JSContext* cx, unsigned argc, Value* vp) {
if (!ParseValTypes(cx, paramsVal, params)) { if (!ParseValTypes(cx, paramsVal, params)) {
return false; return false;
} }
TagOffsetVector offsets;
if (!offsets.resize(params.length())) {
ReportOutOfMemory(cx);
return false;
}
TagType tagType = TagType(std::move(params), std::move(offsets));
if (!tagType.computeLayout()) {
return false;
}
RootedObject proto(cx); RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_WasmTag, &proto)) { if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_WasmTag, &proto)) {
@ -3754,7 +3763,7 @@ bool WasmTagObject::construct(JSContext* cx, unsigned argc, Value* vp) {
proto = GlobalObject::getOrCreatePrototype(cx, JSProto_WasmTag); proto = GlobalObject::getOrCreatePrototype(cx, JSProto_WasmTag);
} }
RootedWasmTagObject tagObj(cx, WasmTagObject::create(cx, params, proto)); RootedWasmTagObject tagObj(cx, WasmTagObject::create(cx, tagType, proto));
if (!tagObj) { if (!tagObj) {
return false; return false;
} }
@ -3764,7 +3773,8 @@ bool WasmTagObject::construct(JSContext* cx, unsigned argc, Value* vp) {
} }
/* static */ /* static */
WasmTagObject* WasmTagObject::create(JSContext* cx, const ValTypeVector& type, WasmTagObject* WasmTagObject::create(JSContext* cx,
const wasm::TagType& tagType,
HandleObject proto) { HandleObject proto) {
AutoSetNewObjectMetadata metadata(cx); AutoSetNewObjectMetadata metadata(cx);
RootedWasmTagObject obj(cx, RootedWasmTagObject obj(cx,
@ -3783,13 +3793,12 @@ WasmTagObject* WasmTagObject::create(JSContext* cx, const ValTypeVector& type,
InitReservedSlot(obj, TAG_SLOT, tag.forget().take(), MemoryUse::WasmTagTag); InitReservedSlot(obj, TAG_SLOT, tag.forget().take(), MemoryUse::WasmTagTag);
wasm::ValTypeVector* newValueTypes = js_new<ValTypeVector>(); TagType* newType = js_new<TagType>();
for (auto t : type) { if (!newType || !newType->clone(tagType)) {
if (!newValueTypes->append(t)) { ReportOutOfMemory(cx);
return nullptr; return nullptr;
}
} }
InitReservedSlot(obj, TYPE_SLOT, newValueTypes, MemoryUse::WasmTagType); InitReservedSlot(obj, TYPE_SLOT, newType, MemoryUse::WasmTagType);
MOZ_ASSERT(!obj->isNewborn()); MOZ_ASSERT(!obj->isNewborn());
@ -3832,8 +3841,12 @@ const JSFunctionSpec WasmTagObject::methods[] = {
const JSFunctionSpec WasmTagObject::static_methods[] = {JS_FS_END}; const JSFunctionSpec WasmTagObject::static_methods[] = {JS_FS_END};
TagType& WasmTagObject::tagType() const {
return *(TagType*)getFixedSlot(TYPE_SLOT).toPrivate();
};
wasm::ValTypeVector& WasmTagObject::valueTypes() const { wasm::ValTypeVector& WasmTagObject::valueTypes() const {
return *(ValTypeVector*)getFixedSlot(TYPE_SLOT).toPrivate(); return tagType().argTypes;
}; };
wasm::ResultType WasmTagObject::resultType() const { wasm::ResultType WasmTagObject::resultType() const {
@ -3923,25 +3936,24 @@ bool WasmExceptionObject::construct(JSContext* cx, unsigned argc, Value* vp) {
return false; return false;
} }
wasm::ValTypeVector& params = exnTag->valueTypes(); const wasm::TagType& tagType = exnTag->tagType();
const wasm::ValTypeVector& params = tagType.argTypes;
const wasm::TagOffsetVector& offsets = tagType.argOffsets;
// This is pre-sizing the data buffer for the exception object. RootedArrayBufferObject buf(
size_t nbytes = 0; cx, ArrayBufferObject::createZeroed(cx, tagType.bufferSize));
for (const ValType param : params) {
if (!param.isReference()) {
nbytes += SizeOf(param);
}
}
RootedArrayBufferObject buf(cx, ArrayBufferObject::createZeroed(cx, nbytes));
if (!buf) { if (!buf) {
return false; return false;
} }
RootedArrayObject refs(cx, NewDenseEmptyArray(cx)); RootedArrayObject refs(cx, NewDenseFullyAllocatedArray(cx, tagType.refCount));
if (!refs) { if (!refs) {
return false; return false;
} }
refs->setDenseInitializedLength(tagType.refCount);
for (int i = 0; i < tagType.refCount; i++) {
refs->initDenseElement(i, UndefinedValue());
}
uint8_t* bufPtr = buf->dataPointer(); uint8_t* bufPtr = buf->dataPointer();
RootedValue nextArg(cx); RootedValue nextArg(cx);
@ -3961,18 +3973,19 @@ bool WasmExceptionObject::construct(JSContext* cx, unsigned argc, Value* vp) {
} }
if (params[i].isReference()) { if (params[i].isReference()) {
RootedObject objPtr(cx); ASSERT_ANYREF_IS_JSOBJECT;
if (!ToWebAssemblyValue(cx, nextArg, params[i], objPtr.address(), true)) { RootedAnyRef anyref(cx, AnyRef::null());
return false; if (!ToWebAssemblyValue(cx, nextArg, params[i],
} anyref.get().asJSObjectAddress(), true)) {
if (!NewbornArrayPush(cx, refs, ObjectValue(*objPtr))) {
return false; return false;
} }
refs->setDenseElement(offsets[i] / sizeof(Value),
ObjectValue(*anyref.get().asJSObject()));
} else { } else {
if (!ToWebAssemblyValue(cx, nextArg, params[i], bufPtr, true)) { if (!ToWebAssemblyValue(cx, nextArg, params[i], bufPtr + offsets[i],
true)) {
return false; return false;
} }
bufPtr += SizeOf(params[i]);
} }
} }
@ -4080,25 +4093,17 @@ bool WasmExceptionObject::getArgImpl(JSContext* cx, const CallArgs& args) {
return false; return false;
} }
uint32_t offset = exnTag->tagType().argOffsets[index];
RootedValue result(cx); RootedValue result(cx);
if (params[index].isReference()) { if (params[index].isReference()) {
uint32_t refIndex = 0; ASSERT_ANYREF_IS_JSOBJECT;
for (size_t i = 0; i < index; i++) { RootedValue val(cx, exnObj->refs().getDenseElement(offset / sizeof(Value)));
if (params[i].isReference()) { RootedAnyRef anyref(cx, AnyRef::fromJSObject(val.toObjectOrNull()));
refIndex++; if (!ToJSValue(cx, anyref.get().asJSObjectAddress(), params[index],
} &result)) {
}
JSObject* ref = &exnObj->refs().getDenseElement(refIndex).toObject();
if (!ToJSValue(cx, &ref, params[index], &result)) {
return false; return false;
} }
} else { } else {
uint32_t offset = 0;
for (size_t i = 0; i < index; i++) {
if (!params[i].isReference()) {
offset += SizeOf(params[i]);
}
}
if (!ToJSValue(cx, exnObj->values().dataPointer() + offset, params[index], if (!ToJSValue(cx, exnObj->values().dataPointer() + offset, params[index],
&result)) { &result)) {
return false; return false;

Просмотреть файл

@ -506,10 +506,11 @@ class WasmTagObject : public NativeObject {
static const JSFunctionSpec static_methods[]; static const JSFunctionSpec static_methods[];
static bool construct(JSContext*, unsigned, Value*); static bool construct(JSContext*, unsigned, Value*);
static WasmTagObject* create(JSContext* cx, const wasm::ValTypeVector& type, static WasmTagObject* create(JSContext* cx, const wasm::TagType& tagType,
HandleObject proto); HandleObject proto);
bool isNewborn() const; bool isNewborn() const;
wasm::TagType& tagType() const;
wasm::ValTypeVector& valueTypes() const; wasm::ValTypeVector& valueTypes() const;
wasm::ResultType resultType() const; wasm::ResultType resultType() const;
wasm::ExceptionTag& tag() const; wasm::ExceptionTag& tag() const;

Просмотреть файл

@ -840,8 +840,7 @@ bool Module::instantiateLocalTag(JSContext* cx, const TagDesc& ed,
// If the tag description is exported, create an export tag // If the tag description is exported, create an export tag
// object for it. // object for it.
RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmTag)); RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmTag));
RootedWasmTagObject tagObj(cx, RootedWasmTagObject tagObj(cx, WasmTagObject::create(cx, ed.type, proto));
WasmTagObject::create(cx, ed.argTypes, proto));
if (!tagObj) { if (!tagObj) {
return false; return false;
} }

Просмотреть файл

@ -181,7 +181,7 @@ size_t GlobalDesc::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const {
} }
#ifdef ENABLE_WASM_EXCEPTIONS #ifdef ENABLE_WASM_EXCEPTIONS
bool TagDesc::computeLayout() { bool TagType::computeLayout() {
StructLayout layout; StructLayout layout;
int32_t refCount = 0; int32_t refCount = 0;
for (const ValType argType : argTypes) { for (const ValType argType : argTypes) {

Просмотреть файл

@ -286,25 +286,43 @@ using GlobalDescVector = Vector<GlobalDesc, 0, SystemAllocPolicy>;
// offset in the elements of the exception's ArrayObject. // offset in the elements of the exception's ArrayObject.
using TagOffsetVector = Vector<int32_t, 0, SystemAllocPolicy>; using TagOffsetVector = Vector<int32_t, 0, SystemAllocPolicy>;
struct TagDesc { struct TagType {
TagKind kind;
ValTypeVector argTypes; ValTypeVector argTypes;
TagOffsetVector argOffsets; TagOffsetVector argOffsets;
int32_t bufferSize; int32_t bufferSize;
int32_t refCount; int32_t refCount;
bool isExport;
TagDesc(TagKind kind, ValTypeVector&& argTypes, TagOffsetVector&& argOffsets, TagType() : argTypes(), argOffsets(), bufferSize(0), refCount(0) {}
bool isExport = false) TagType(ValTypeVector&& argTypes, TagOffsetVector&& argOffsets)
: kind(kind), : argTypes(std::move(argTypes)),
argTypes(std::move(argTypes)),
argOffsets(std::move(argOffsets)), argOffsets(std::move(argOffsets)),
bufferSize(0), bufferSize(0),
refCount(0), refCount(0) {}
isExport(isExport) {}
[[nodiscard]] bool computeLayout(); [[nodiscard]] bool computeLayout();
ResultType resultType() const { return ResultType::Vector(argTypes); }
[[nodiscard]] bool clone(const TagType& src) {
MOZ_ASSERT(argTypes.empty());
MOZ_ASSERT(argOffsets.empty());
if (!argTypes.appendAll(src.argTypes) ||
!argOffsets.appendAll(src.argOffsets)) {
return false;
}
bufferSize = src.bufferSize;
refCount = src.refCount;
return true;
}
};
struct TagDesc {
TagKind kind;
TagType type;
bool isExport;
TagDesc(TagKind kind, TagType&& type, bool isExport = false)
: kind(kind), type(std::move(type)), isExport(isExport) {}
ResultType resultType() const { return ResultType::Vector(type.argTypes); }
}; };
using TagDescVector = Vector<TagDesc, 0, SystemAllocPolicy>; using TagDescVector = Vector<TagDesc, 0, SystemAllocPolicy>;

Просмотреть файл

@ -2055,14 +2055,14 @@ static bool DecodeImport(Decoder& d, ModuleEnvironment* env) {
if (!offsets.resize(tagArgs.length())) { if (!offsets.resize(tagArgs.length())) {
return false; return false;
} }
if (!env->tags.emplaceBack(tagKind, std::move(tagArgs), TagType type = TagType(std::move(tagArgs), std::move(offsets));
std::move(offsets))) { if (!env->tags.emplaceBack(tagKind, std::move(type))) {
return false; return false;
} }
if (env->tags.length() > MaxTags) { if (env->tags.length() > MaxTags) {
return d.fail("too many tags"); return d.fail("too many tags");
} }
if (!env->tags.back().computeLayout()) { if (!env->tags.back().type.computeLayout()) {
return false; return false;
} }
break; break;
@ -2286,9 +2286,9 @@ static bool DecodeTagSection(Decoder& d, ModuleEnvironment* env) {
if (!offsets.resize(tagArgs.length())) { if (!offsets.resize(tagArgs.length())) {
return false; return false;
} }
env->tags.infallibleEmplaceBack(tagKind, std::move(tagArgs), TagType type = TagType(std::move(tagArgs), std::move(offsets));
std::move(offsets)); env->tags.infallibleEmplaceBack(tagKind, std::move(type));
if (!env->tags.back().computeLayout()) { if (!env->tags.back().type.computeLayout()) {
return false; return false;
} }
} }
@ -2409,7 +2409,7 @@ static bool DecodeExport(Decoder& d, ModuleEnvironment* env,
} }
# ifdef WASM_PRIVATE_REFTYPES # ifdef WASM_PRIVATE_REFTYPES
if (!TagIsJSCompatible(d, env->tags[tagIndex].argTypes)) { if (!TagIsJSCompatible(d, env->tags[tagIndex].type.argTypes)) {
return false; return false;
} }
# endif # endif