diff --git a/js/src/jit-test/tests/wasm/gc/ref-global.js b/js/src/jit-test/tests/wasm/gc/ref-global.js new file mode 100644 index 000000000000..15ffcb61718d --- /dev/null +++ b/js/src/jit-test/tests/wasm/gc/ref-global.js @@ -0,0 +1,61 @@ +if (!wasmGcEnabled()) + quit(0); + +// Basic private-to-module functionality. At the moment all we have is null +// pointers, not very exciting. + +{ + let bin = wasmTextToBinary( + `(module + + (type $point (struct + (field $x f64) + (field $y f64))) + + (global $g1 (mut (ref $point)) (ref.null (ref $point))) + (global $g2 (mut (ref $point)) (ref.null (ref $point))) + (global $g3 (ref $point) (ref.null (ref $point))) + + (func (export "get") (result (ref $point)) + (get_global $g1)) + + (func (export "copy") + (set_global $g2 (get_global $g1))) + + (func (export "clear") + (set_global $g1 (get_global $g3)) + (set_global $g2 (ref.null (ref $point)))))`); + + let mod = new WebAssembly.Module(bin); + let ins = new WebAssembly.Instance(mod).exports; + + assertEq(ins.get(), null); + ins.copy(); // Should not crash + ins.clear(); // Should not crash +} + +// We can't import a global of a reference type because we don't have a good +// notion of structural type compatibility yet. +{ + let bin = wasmTextToBinary( + `(module + (type $box (struct (field $val i32))) + (import "m" "g" (global (mut (ref $box)))))`); + + assertErrorMessage(() => new WebAssembly.Module(bin), WebAssembly.CompileError, + /unexpected variable type in global import\/export/); +} + +// We can't export a global of a reference type because we can't later import +// it. (Once we can export it, the value setter must also perform the necessary +// subtype check, which implies we have some notion of exporting types, and we +// don't have that yet.) +{ + let bin = wasmTextToBinary( + `(module + (type $box (struct (field $val i32))) + (global $boxg (export "box") (mut (ref $box)) (ref.null (ref $box))))`); + + assertErrorMessage(() => new WebAssembly.Module(bin), WebAssembly.CompileError, + /unexpected variable type in global import\/export/); +} diff --git a/js/src/jit-test/tests/wasm/gc/ref.js b/js/src/jit-test/tests/wasm/gc/ref.js index 3b91a8c92d0b..2ccef1f0a68a 100644 --- a/js/src/jit-test/tests/wasm/gc/ref.js +++ b/js/src/jit-test/tests/wasm/gc/ref.js @@ -20,12 +20,6 @@ var bin = wasmTextToBinary( (field $x i32) (field $to_odd (ref $odd)))) - ;; No globals of reference type yet. - ;; - ;;(import "m" "g" (global (ref $cons))) - ;; - ;;(global $glob (ref $cons) (ref.null $cons)) - (import "m" "f" (func $imp (param (ref $cons)) (result (ref $odd)))) ;; The bodies do nothing since we have no operations on structs yet. diff --git a/js/src/wasm/WasmBaselineCompile.cpp b/js/src/wasm/WasmBaselineCompile.cpp index b5e8ff935115..2242d0a6de63 100644 --- a/js/src/wasm/WasmBaselineCompile.cpp +++ b/js/src/wasm/WasmBaselineCompile.cpp @@ -8359,6 +8359,7 @@ BaseCompiler::emitGetGlobal() case ValType::F64: pushF64(value.f64()); break; + case ValType::Ref: case ValType::AnyRef: pushRef(intptr_t(value.ptr())); break; @@ -8397,6 +8398,7 @@ BaseCompiler::emitGetGlobal() pushF64(rv); break; } + case ValType::Ref: case ValType::AnyRef: { RegPtr rv = needRef(); ScratchI32 tmp(*this); @@ -8454,6 +8456,7 @@ BaseCompiler::emitSetGlobal() break; } #ifdef ENABLE_WASM_GC + case ValType::Ref: case ValType::AnyRef: { RegPtr valueAddr(PreBarrierReg); needRef(valueAddr); diff --git a/js/src/wasm/WasmTypes.cpp b/js/src/wasm/WasmTypes.cpp index 422bcf9c9e79..a44d7b8c7561 100644 --- a/js/src/wasm/WasmTypes.cpp +++ b/js/src/wasm/WasmTypes.cpp @@ -68,8 +68,8 @@ Val::Val(const LitVal& val) case ValType::F32: u.f32_ = val.f32(); return; case ValType::I64: u.i64_ = val.i64(); return; case ValType::F64: u.f64_ = val.f64(); return; + case ValType::Ref: case ValType::AnyRef: u.ptr_ = val.ptr(); return; - case ValType::Ref: break; } MOZ_CRASH(); } diff --git a/js/src/wasm/WasmValidate.cpp b/js/src/wasm/WasmValidate.cpp index 45822e217810..0aad0149fee1 100644 --- a/js/src/wasm/WasmValidate.cpp +++ b/js/src/wasm/WasmValidate.cpp @@ -1644,7 +1644,7 @@ DecodeMemorySection(Decoder& d, ModuleEnvironment* env) static bool DecodeInitializerExpression(Decoder& d, HasGcTypes gcTypesEnabled, const GlobalDescVector& globals, - ValType expected, InitExpr* init) + ValType expected, uint32_t numTypes, InitExpr* init) { OpBytes op; if (!d.readOp(&op)) @@ -1683,12 +1683,18 @@ DecodeInitializerExpression(Decoder& d, HasGcTypes gcTypesEnabled, const GlobalD if (gcTypesEnabled == HasGcTypes::False) return d.fail("unexpected initializer expression"); uint8_t valType; - uint32_t unusedRefTypeIndex; - if (!d.readValType(&valType, &unusedRefTypeIndex)) + uint32_t refTypeIndex; + if (!d.readValType(&valType, &refTypeIndex)) return false; - if (valType != uint8_t(ValType::AnyRef)) - return d.fail("expected anyref as type for ref.null"); - *init = InitExpr(LitVal(ValType::AnyRef, nullptr)); + if (valType == uint8_t(ValType::AnyRef)) { + *init = InitExpr(LitVal(ValType::AnyRef, nullptr)); + } else if (valType == uint8_t(ValType::Ref)) { + if (refTypeIndex >= numTypes) + return d.fail("invalid reference type for ref.null"); + *init = InitExpr(LitVal(ValType(ValType::Ref, refTypeIndex), nullptr)); + } else { + return d.fail("expected anyref/ref as type for ref.null"); + } break; } case uint16_t(Op::GetGlobal): { @@ -1745,8 +1751,11 @@ DecodeGlobalSection(Decoder& d, ModuleEnvironment* env) return false; InitExpr initializer; - if (!DecodeInitializerExpression(d, env->gcTypesEnabled, env->globals, type, &initializer)) + if (!DecodeInitializerExpression(d, env->gcTypesEnabled, env->globals, type, + env->types.length(), &initializer)) + { return false; + } env->globals.infallibleAppend(GlobalDesc(initializer, isMutable)); } @@ -1927,8 +1936,10 @@ DecodeElemSection(Decoder& d, ModuleEnvironment* env) InitExpr offset; if (!DecodeInitializerExpression(d, env->gcTypesEnabled, env->globals, ValType::I32, - &offset)) + env->types.length(), &offset)) + { return false; + } uint32_t numElems; if (!d.readVarU32(&numElems)) @@ -2098,8 +2109,10 @@ DecodeDataSection(Decoder& d, ModuleEnvironment* env) DataSegment seg; if (!DecodeInitializerExpression(d, env->gcTypesEnabled, env->globals, ValType::I32, - &seg.offset)) + env->types.length(), &seg.offset)) + { return false; + } if (!d.readVarU32(&seg.length)) return d.fail("expected segment size");