diff --git a/js/src/jit-test/tests/wasm/function-references/reftype-parse.js b/js/src/jit-test/tests/wasm/function-references/reftype-parse.js new file mode 100644 index 000000000000..a233d10d8f33 --- /dev/null +++ b/js/src/jit-test/tests/wasm/function-references/reftype-parse.js @@ -0,0 +1,35 @@ +// |jit-test| skip-if: !wasmFunctionReferencesEnabled() + +// RefType/ValueType as a simple string +const t01 = new WebAssembly.Table({element: 'funcref', initial: 3}); +const t02 = new WebAssembly.Table({element: 'externref', initial: 8}); +const g01 = new WebAssembly.Global({value: 'funcref', mutable: true}, null); +const g02 = new WebAssembly.Global({value: 'externref', mutable: true}, null); + +// Specify ToString() equivalents +const t05 = new WebAssembly.Table({element: {toString() { return 'funcref' },}, initial: 11}); +const t06 = new WebAssembly.Table({element: ['externref'], initial: 7}); + +assertErrorMessage( + () => new WebAssembly.Table({element: 'foo', initial: 1}), + TypeError, /bad value type/); +assertErrorMessage( + () => new WebAssembly.Table({element: true, initial: 1}), + TypeError, /bad value type/); + +// RefType/ValueType can be specified as an {ref: 'func', ...} object +const t11 = new WebAssembly.Table({element: {ref: 'func', nullable: true}, initial: 3}); +const t12 = new WebAssembly.Table({element: {ref: 'extern', nullable: true}, initial: 3}); +// TODO new WebAssembly.Table({element: {ref: 'func', nullable: false}, initial: 3}), + +assertErrorMessage( + () => new WebAssembly.Table({element: {ref: 'bar', nullable: true}, initial: 1}), + TypeError, /bad value type/); + +const g11 = new WebAssembly.Global({value: {ref: 'func', nullable: true}, mutable: true}); +const g12 = new WebAssembly.Global({value: {ref: 'extern', nullable: true}, mutable: true}); +// TODO new WebAssembly.Global({value: {ref: 'extern', nullable: false}, mutable: true}); + +assertErrorMessage( + () => new WebAssembly.Global({value: {ref: 'bar', nullable: true}, mutable: true}), + TypeError, /bad value type/); diff --git a/js/src/wasm/WasmJS.cpp b/js/src/wasm/WasmJS.cpp index 9b5e5120525b..5ee22b5637a8 100644 --- a/js/src/wasm/WasmJS.cpp +++ b/js/src/wasm/WasmJS.cpp @@ -3109,18 +3109,8 @@ bool WasmTableObject::construct(JSContext* cx, unsigned argc, Value* vp) { return false; } - RootedString elementStr(cx, ToString(cx, elementVal)); - if (!elementStr) { - return false; - } - - Rooted elementLinearStr(cx, elementStr->ensureLinear(cx)); - if (!elementLinearStr) { - return false; - } - RefType tableType; - if (!ToRefType(cx, elementLinearStr, &tableType)) { + if (!ToRefType(cx, elementVal, &tableType)) { return false; } diff --git a/js/src/wasm/WasmValType.cpp b/js/src/wasm/WasmValType.cpp index d20e24d9f6a8..c3f26487c40e 100644 --- a/js/src/wasm/WasmValType.cpp +++ b/js/src/wasm/WasmValType.cpp @@ -23,13 +23,136 @@ #include "js/friend/ErrorMessages.h" // JSMSG_* #include "js/Printf.h" #include "js/Value.h" + +#include "vm/JSAtom.h" +#include "vm/JSObject.h" #include "vm/StringType.h" #include "wasm/WasmJS.h" +#include "vm/JSAtom-inl.h" +#include "vm/JSObject-inl.h" + using namespace js; using namespace js::wasm; +static bool ToRefType(JSContext* cx, JSLinearString* typeLinearStr, + RefType* out) { + if (StringEqualsLiteral(typeLinearStr, "anyfunc") || + StringEqualsLiteral(typeLinearStr, "funcref")) { + // The JS API uses "anyfunc" uniformly as the external name of funcref. We + // also allow "funcref" for compatibility with code we've already shipped. + *out = RefType::func(); + } else if (StringEqualsLiteral(typeLinearStr, "externref")) { + *out = RefType::extern_(); +#ifdef ENABLE_WASM_GC + } else if (GcAvailable(cx) && StringEqualsLiteral(typeLinearStr, "anyref")) { + *out = RefType::any(); + } else if (GcAvailable(cx) && StringEqualsLiteral(typeLinearStr, "eqref")) { + *out = RefType::eq(); + } else if (GcAvailable(cx) && + StringEqualsLiteral(typeLinearStr, "structref")) { + *out = RefType::struct_(); + } else if (GcAvailable(cx) && + StringEqualsLiteral(typeLinearStr, "arrayref")) { + *out = RefType::array(); +#endif + } else { + JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, + JSMSG_WASM_BAD_STRING_VAL_TYPE); + return false; + } + + return true; +} + +enum class RefTypeResult { + Failure, + Parsed, + Unparsed, +}; + +static RefTypeResult MaybeToRefType(JSContext* cx, HandleObject obj, + RefType* out) { +#ifdef ENABLE_WASM_FUNCTION_REFERENCES + if (!wasm::FunctionReferencesAvailable(cx)) { + return RefTypeResult::Unparsed; + } + + JSAtom* refAtom = Atomize(cx, "ref", strlen("ref")); + if (!refAtom) { + return RefTypeResult::Failure; + } + RootedId refId(cx, AtomToId(refAtom)); + + RootedValue refVal(cx); + if (!GetProperty(cx, obj, obj, refId, &refVal)) { + return RefTypeResult::Failure; + } + + RootedString typeStr(cx, ToString(cx, refVal)); + if (!typeStr) { + return RefTypeResult::Failure; + } + + Rooted typeLinearStr(cx, typeStr->ensureLinear(cx)); + if (!typeLinearStr) { + return RefTypeResult::Failure; + } + + if (StringEqualsLiteral(typeLinearStr, "func")) { + *out = RefType::func(); + } else if (StringEqualsLiteral(typeLinearStr, "extern")) { + *out = RefType::extern_(); +# ifdef ENABLE_WASM_GC + } else if (GcAvailable(cx) && StringEqualsLiteral(typeLinearStr, "any")) { + *out = RefType::any(); + } else if (GcAvailable(cx) && StringEqualsLiteral(typeLinearStr, "eq")) { + *out = RefType::eq(); + } else if (GcAvailable(cx) && StringEqualsLiteral(typeLinearStr, "struct")) { + *out = RefType::struct_(); + } else if (GcAvailable(cx) && StringEqualsLiteral(typeLinearStr, "array")) { + *out = RefType::array(); +# endif + } else { + return RefTypeResult::Unparsed; + } + + JSAtom* nullableAtom = Atomize(cx, "nullable", strlen("nullable")); + if (!nullableAtom) { + return RefTypeResult::Failure; + } + RootedId nullableId(cx, AtomToId(nullableAtom)); + RootedValue nullableVal(cx); + if (!GetProperty(cx, obj, obj, nullableId, &nullableVal)) { + return RefTypeResult::Failure; + } + + bool nullable = ToBoolean(nullableVal); + if (!nullable) { + *out = out->asNonNullable(); + } + MOZ_ASSERT(out->isNullable() == nullable); + return RefTypeResult::Parsed; +#else + return RefTypeResult::Unparsed; +#endif +} + bool wasm::ToValType(JSContext* cx, HandleValue v, ValType* out) { + if (v.isObject()) { + RootedObject obj(cx, &v.toObject()); + RefType refType; + switch (MaybeToRefType(cx, obj, &refType)) { + case RefTypeResult::Failure: + return false; + case RefTypeResult::Parsed: + *out = ValType(refType); + return true; + case RefTypeResult::Unparsed: + break; + } + } + RootedString typeStr(cx, ToString(cx, v)); if (!typeStr) { return false; @@ -65,34 +188,30 @@ bool wasm::ToValType(JSContext* cx, HandleValue v, ValType* out) { return true; } -bool wasm::ToRefType(JSContext* cx, JSLinearString* typeLinearStr, - RefType* out) { - if (StringEqualsLiteral(typeLinearStr, "anyfunc") || - StringEqualsLiteral(typeLinearStr, "funcref")) { - // The JS API uses "anyfunc" uniformly as the external name of funcref. We - // also allow "funcref" for compatibility with code we've already shipped. - *out = RefType::func(); - } else if (StringEqualsLiteral(typeLinearStr, "externref")) { - *out = RefType::extern_(); -#ifdef ENABLE_WASM_GC - } else if (GcAvailable(cx) && StringEqualsLiteral(typeLinearStr, "anyref")) { - *out = RefType::any(); - } else if (GcAvailable(cx) && StringEqualsLiteral(typeLinearStr, "eqref")) { - *out = RefType::eq(); - } else if (GcAvailable(cx) && - StringEqualsLiteral(typeLinearStr, "structref")) { - *out = RefType::struct_(); - } else if (GcAvailable(cx) && - StringEqualsLiteral(typeLinearStr, "arrayref")) { - *out = RefType::array(); -#endif - } else { - JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, - JSMSG_WASM_BAD_STRING_VAL_TYPE); +bool wasm::ToRefType(JSContext* cx, HandleValue v, RefType* out) { + if (v.isObject()) { + RootedObject obj(cx, &v.toObject()); + switch (MaybeToRefType(cx, obj, out)) { + case RefTypeResult::Failure: + return false; + case RefTypeResult::Parsed: + return true; + case RefTypeResult::Unparsed: + break; + } + } + + RootedString typeStr(cx, ToString(cx, v)); + if (!typeStr) { return false; } - return true; + Rooted typeLinearStr(cx, typeStr->ensureLinear(cx)); + if (!typeLinearStr) { + return false; + } + + return ToRefType(cx, typeLinearStr, out); } UniqueChars wasm::ToString(RefType type, const TypeContext* types) { diff --git a/js/src/wasm/WasmValType.h b/js/src/wasm/WasmValType.h index 92e309cd4377..6214c78de6f3 100644 --- a/js/src/wasm/WasmValType.h +++ b/js/src/wasm/WasmValType.h @@ -844,8 +844,7 @@ using ValTypeVector = Vector; // ValType utilities extern bool ToValType(JSContext* cx, HandleValue v, ValType* out); -extern bool ToRefType(JSContext* cx, JSLinearString* typeLinearStr, - RefType* out); +extern bool ToRefType(JSContext* cx, HandleValue v, RefType* out); extern UniqueChars ToString(RefType type, const TypeContext* types); extern UniqueChars ToString(ValType type, const TypeContext* types);