mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 03:45:46 +00:00
Bug 1412238 - Implement WebAssembly.Global for immutables. r=luke
--HG-- extra : rebase_source : 95f927424504041f5e08cce8f233f2cbbc1a641f
This commit is contained in:
parent
6b0c25d28f
commit
3d9e46b95b
@ -119,7 +119,12 @@ module = wasmEvalText(`(module
|
|||||||
)`, { globals: {x: 42} }).exports;
|
)`, { globals: {x: 42} }).exports;
|
||||||
|
|
||||||
assertEq(module.getter(), 42);
|
assertEq(module.getter(), 42);
|
||||||
assertEq(module.value, 42);
|
// Adapt to ongoing experiment with WebAssembly.Global.
|
||||||
|
// assertEq() will not trigger @@toPrimitive, so we must have a cast here.
|
||||||
|
if (typeof WebAssembly.Global === "function")
|
||||||
|
assertEq(Number(module.value), 42);
|
||||||
|
else
|
||||||
|
assertEq(module.value, 42);
|
||||||
|
|
||||||
// Can only import numbers (no implicit coercions).
|
// Can only import numbers (no implicit coercions).
|
||||||
module = new WebAssembly.Module(wasmTextToBinary(`(module
|
module = new WebAssembly.Module(wasmTextToBinary(`(module
|
||||||
@ -174,8 +179,14 @@ module = wasmEvalText(`(module
|
|||||||
(export "defined" global 1)
|
(export "defined" global 1)
|
||||||
)`, { globals: {x: 42} }).exports;
|
)`, { globals: {x: 42} }).exports;
|
||||||
|
|
||||||
assertEq(module.imported, 42);
|
// See comment earlier about WebAssembly.Global
|
||||||
assertEq(module.defined, 1337);
|
if (typeof WebAssembly.Global === "function") {
|
||||||
|
assertEq(Number(module.imported), 42);
|
||||||
|
assertEq(Number(module.defined), 1337);
|
||||||
|
} else {
|
||||||
|
assertEq(module.imported, 42);
|
||||||
|
assertEq(module.defined, 1337);
|
||||||
|
}
|
||||||
|
|
||||||
// Initializer expressions can reference an imported immutable global.
|
// Initializer expressions can reference an imported immutable global.
|
||||||
wasmFailValidateText(`(module (global f32 (f32.const 13.37)) (global i32 (get_global 0)))`, /must reference a global immutable import/);
|
wasmFailValidateText(`(module (global f32 (f32.const 13.37)) (global i32 (get_global 0)))`, /must reference a global immutable import/);
|
||||||
@ -212,12 +223,20 @@ function testInitExpr(type, initialValue, nextValue, coercion, assertFunc = asse
|
|||||||
|
|
||||||
assertFunc(module.get0(), coercion(initialValue));
|
assertFunc(module.get0(), coercion(initialValue));
|
||||||
assertFunc(module.get1(), coercion(initialValue));
|
assertFunc(module.get1(), coercion(initialValue));
|
||||||
assertFunc(module.global_imm, coercion(initialValue));
|
// See comment earlier about WebAssembly.Global
|
||||||
|
if (typeof WebAssembly.Global === "function")
|
||||||
|
assertFunc(Number(module.global_imm), coercion(initialValue));
|
||||||
|
else
|
||||||
|
assertFunc(module.global_imm, coercion(initialValue));
|
||||||
|
|
||||||
assertEq(module.set1(coercion(nextValue)), undefined);
|
assertEq(module.set1(coercion(nextValue)), undefined);
|
||||||
assertFunc(module.get1(), coercion(nextValue));
|
assertFunc(module.get1(), coercion(nextValue));
|
||||||
assertFunc(module.get0(), coercion(initialValue));
|
assertFunc(module.get0(), coercion(initialValue));
|
||||||
assertFunc(module.global_imm, coercion(initialValue));
|
// See comment earlier about WebAssembly.Global
|
||||||
|
if (typeof WebAssembly.Global === "function")
|
||||||
|
assertFunc(Number(module.global_imm), coercion(initialValue));
|
||||||
|
else
|
||||||
|
assertFunc(module.global_imm, coercion(initialValue));
|
||||||
|
|
||||||
assertFunc(module.get_cst(), coercion(initialValue));
|
assertFunc(module.get_cst(), coercion(initialValue));
|
||||||
}
|
}
|
||||||
@ -270,3 +289,64 @@ wasmAssert(`(module
|
|||||||
dv.setFloat32(0, module.nan32, true);
|
dv.setFloat32(0, module.nan32, true);
|
||||||
assertEq(dv.getUint32(0, true), 0x7fc00000);
|
assertEq(dv.getUint32(0, true), 0x7fc00000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WebAssembly.Global experiment
|
||||||
|
|
||||||
|
if (typeof WebAssembly.Global === "function") {
|
||||||
|
|
||||||
|
// These types should work:
|
||||||
|
assertEq(new WebAssembly.Global({type: "i32"}) instanceof WebAssembly.Global, true);
|
||||||
|
assertEq(new WebAssembly.Global({type: "f32"}) instanceof WebAssembly.Global, true);
|
||||||
|
assertEq(new WebAssembly.Global({type: "f64"}) instanceof WebAssembly.Global, true);
|
||||||
|
|
||||||
|
// These types should not work:
|
||||||
|
assertErrorMessage(() => new WebAssembly.Global({type: "i64"}),
|
||||||
|
TypeError,
|
||||||
|
/bad type for a WebAssembly.Global/);
|
||||||
|
assertErrorMessage(() => new WebAssembly.Global({}),
|
||||||
|
TypeError,
|
||||||
|
/bad type for a WebAssembly.Global/);
|
||||||
|
assertErrorMessage(() => new WebAssembly.Global({type: "fnord"}),
|
||||||
|
TypeError,
|
||||||
|
/bad type for a WebAssembly.Global/);
|
||||||
|
assertErrorMessage(() => new WebAssembly.Global(),
|
||||||
|
TypeError,
|
||||||
|
/WebAssembly.Global requires more than 0 arguments/);
|
||||||
|
|
||||||
|
// Coercion of init value; ".value" accessor
|
||||||
|
assertEq((new WebAssembly.Global({type: "i32", value: 3.14})).value, 3);
|
||||||
|
assertEq((new WebAssembly.Global({type: "f32", value: { valueOf: () => 33.5 }})).value, 33.5);
|
||||||
|
|
||||||
|
// Misc internal conversions
|
||||||
|
let g = new WebAssembly.Global({type: "i32", value: 42});
|
||||||
|
|
||||||
|
// @@toPrimitive
|
||||||
|
assertEq(g - 5, 37);
|
||||||
|
assertEq(String(g), "42");
|
||||||
|
|
||||||
|
// @@toStringTag
|
||||||
|
assertEq(g.toString(), "[object WebAssembly.Global]");
|
||||||
|
|
||||||
|
// An exported global should appear as a WebAssembly.Global instance:
|
||||||
|
let i =
|
||||||
|
new WebAssembly.Instance(
|
||||||
|
new WebAssembly.Module(
|
||||||
|
wasmTextToBinary(`(module (global (export "g") i32 (i32.const 42)))`)));
|
||||||
|
|
||||||
|
assertEq(typeof i.exports.g, "object");
|
||||||
|
assertEq(i.exports.g instanceof WebAssembly.Global, true);
|
||||||
|
|
||||||
|
// An exported global can be imported into another instance even if
|
||||||
|
// it is an object:
|
||||||
|
let j =
|
||||||
|
new WebAssembly.Instance(
|
||||||
|
new WebAssembly.Module(
|
||||||
|
wasmTextToBinary(`(module
|
||||||
|
(global (import "" "g") i32)
|
||||||
|
(func (export "f") (result i32)
|
||||||
|
(get_global 0)))`)),
|
||||||
|
{ "": { "g": i.exports.g }});
|
||||||
|
|
||||||
|
// And when it is then accessed it has the right value:
|
||||||
|
assertEq(j.exports.f(), 42);
|
||||||
|
}
|
||||||
|
@ -222,6 +222,15 @@ function get(instance, name) {
|
|||||||
if (instance.isError())
|
if (instance.isError())
|
||||||
return instance;
|
return instance;
|
||||||
|
|
||||||
|
// Experimental API change. We try to export WebAssembly.Global instances,
|
||||||
|
// not primitive values. In that case the Number() cast is necessary here
|
||||||
|
// to convert the Global to a value: the harness examines types carefully
|
||||||
|
// and will not trigger the @@toPrimitive hook on Global, unlike most user
|
||||||
|
// code.
|
||||||
|
|
||||||
|
if (typeof WebAssembly.Global === "function")
|
||||||
|
return ValueResult(Number(instance.value.exports[name]));
|
||||||
|
|
||||||
return ValueResult(instance.value.exports[name]);
|
return ValueResult(instance.value.exports[name]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -387,10 +387,12 @@ 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_IMPORT_FIELD, 1, JSEXN_TYPEERR, "import object field '{0}' is not an Object")
|
||||||
MSG_DEF(JSMSG_WASM_BAD_TABLE_VALUE, 0, JSEXN_TYPEERR, "can only assign WebAssembly exported functions to Table")
|
MSG_DEF(JSMSG_WASM_BAD_TABLE_VALUE, 0, JSEXN_TYPEERR, "can only assign WebAssembly exported functions to Table")
|
||||||
MSG_DEF(JSMSG_WASM_BAD_I64_TYPE, 0, JSEXN_TYPEERR, "cannot pass i64 to or from JS")
|
MSG_DEF(JSMSG_WASM_BAD_I64_TYPE, 0, JSEXN_TYPEERR, "cannot pass i64 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")
|
MSG_DEF(JSMSG_WASM_NO_TRANSFER, 0, JSEXN_TYPEERR, "cannot transfer WebAssembly/asm.js ArrayBuffer")
|
||||||
MSG_DEF(JSMSG_WASM_STREAM_ERROR, 0, JSEXN_TYPEERR, "stream error during WebAssembly compilation")
|
MSG_DEF(JSMSG_WASM_STREAM_ERROR, 0, JSEXN_TYPEERR, "stream error during WebAssembly compilation")
|
||||||
MSG_DEF(JSMSG_WASM_TEXT_FAIL, 1, JSEXN_SYNTAXERR, "wasm text error: {0}")
|
MSG_DEF(JSMSG_WASM_TEXT_FAIL, 1, JSEXN_SYNTAXERR, "wasm text error: {0}")
|
||||||
MSG_DEF(JSMSG_WASM_MISSING_MAXIMUM, 0, JSEXN_TYPEERR, "'shared' is true but maximum is not specified")
|
MSG_DEF(JSMSG_WASM_MISSING_MAXIMUM, 0, JSEXN_TYPEERR, "'shared' is true but maximum is not specified")
|
||||||
|
MSG_DEF(JSMSG_WASM_GLOBAL_IMMUTABLE, 0, JSEXN_TYPEERR, "can't set value of immutable global")
|
||||||
|
|
||||||
// Proxy
|
// Proxy
|
||||||
MSG_DEF(JSMSG_BAD_TRAP_RETURN_VALUE, 2, JSEXN_TYPEERR,"trap {1} for {0} returned a primitive value")
|
MSG_DEF(JSMSG_BAD_TRAP_RETURN_VALUE, 2, JSEXN_TYPEERR,"trap {1} for {0} returned a primitive value")
|
||||||
|
@ -127,6 +127,7 @@ IF_SAB(real,imaginary)(Atomics, InitAtomicsClass, OCLASP(Atomics)) \
|
|||||||
imaginary(WasmInstance, dummy, dummy) \
|
imaginary(WasmInstance, dummy, dummy) \
|
||||||
imaginary(WasmMemory, dummy, dummy) \
|
imaginary(WasmMemory, dummy, dummy) \
|
||||||
imaginary(WasmTable, dummy, dummy) \
|
imaginary(WasmTable, dummy, dummy) \
|
||||||
|
imaginary(WasmGlobal, dummy, dummy) \
|
||||||
|
|
||||||
#define JS_FOR_EACH_PROTOTYPE(macro) JS_FOR_PROTOTYPES(macro,macro)
|
#define JS_FOR_EACH_PROTOTYPE(macro) JS_FOR_PROTOTYPES(macro,macro)
|
||||||
|
|
||||||
|
@ -652,6 +652,11 @@ if CONFIG['NIGHTLY_BUILD']:
|
|||||||
DEFINES['ENABLE_SIMD'] = True
|
DEFINES['ENABLE_SIMD'] = True
|
||||||
DEFINES['ENABLE_WASM_SIGNEXTEND_OPS'] = True
|
DEFINES['ENABLE_WASM_SIGNEXTEND_OPS'] = True
|
||||||
DEFINES['ENABLE_WASM_THREAD_OPS'] = True
|
DEFINES['ENABLE_WASM_THREAD_OPS'] = True
|
||||||
|
# An experiment we want to run on Nightly: Can we change the JS
|
||||||
|
# representation of an exported global from the global's value
|
||||||
|
# to an instance of WebAssembly.Global without breaking existing
|
||||||
|
# wasm content?
|
||||||
|
DEFINES['ENABLE_WASM_GLOBAL'] = True
|
||||||
|
|
||||||
if CONFIG['JS_BUILD_BINAST']:
|
if CONFIG['JS_BUILD_BINAST']:
|
||||||
# Using SOURCES as UNIFIED_SOURCES causes mysterious bugs on 32-bit platforms.
|
# Using SOURCES as UNIFIED_SOURCES causes mysterious bugs on 32-bit platforms.
|
||||||
|
@ -98,6 +98,61 @@ wasm::HasSupport(JSContext* cx)
|
|||||||
return cx->options().wasm() && HasCompilerSupport(cx);
|
return cx->options().wasm() && HasCompilerSupport(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
wasm::ToWebAssemblyValue(JSContext* cx, ValType targetType, HandleValue v, Val* val)
|
||||||
|
{
|
||||||
|
switch (targetType) {
|
||||||
|
case ValType::I32: {
|
||||||
|
int32_t i32;
|
||||||
|
if (!ToInt32(cx, v, &i32))
|
||||||
|
return false;
|
||||||
|
*val = Val(uint32_t(i32));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case ValType::F32: {
|
||||||
|
double d;
|
||||||
|
if (!ToNumber(cx, v, &d))
|
||||||
|
return false;
|
||||||
|
*val = Val(float(d));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case ValType::F64: {
|
||||||
|
double d;
|
||||||
|
if (!ToNumber(cx, v, &d))
|
||||||
|
return false;
|
||||||
|
*val = Val(d);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
MOZ_CRASH("unexpected import value type, caller must guard");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
wasm::ToJSValue(const Val& val, MutableHandleValue value)
|
||||||
|
{
|
||||||
|
switch (val.type()) {
|
||||||
|
case ValType::I32: {
|
||||||
|
value.set(Int32Value(val.i32()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case ValType::F32: {
|
||||||
|
float f = val.f32();
|
||||||
|
value.set(DoubleValue(JS::CanonicalizeNaN(double(f))));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case ValType::F64: {
|
||||||
|
double d = val.f64();
|
||||||
|
value.set(DoubleValue(JS::CanonicalizeNaN(d)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
MOZ_CRASH("unexpected type when translating to a JS value");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Imports
|
// Imports
|
||||||
|
|
||||||
@ -187,42 +242,22 @@ GetImports(JSContext* cx,
|
|||||||
const GlobalDesc& global = globals[globalIndex++];
|
const GlobalDesc& global = globals[globalIndex++];
|
||||||
MOZ_ASSERT(global.importIndex() == globalIndex - 1);
|
MOZ_ASSERT(global.importIndex() == globalIndex - 1);
|
||||||
MOZ_ASSERT(!global.isMutable());
|
MOZ_ASSERT(!global.isMutable());
|
||||||
switch (global.type()) {
|
|
||||||
case ValType::I32: {
|
#ifdef ENABLE_WASM_GLOBAL
|
||||||
if (!v.isNumber())
|
if (v.isObject() && v.toObject().is<WasmGlobalObject>())
|
||||||
return ThrowBadImportType(cx, import.field.get(), "Number");
|
v.set(v.toObject().as<WasmGlobalObject>().value());
|
||||||
int32_t i32;
|
#endif
|
||||||
if (!ToInt32(cx, v, &i32))
|
|
||||||
return false;
|
if (global.type() == ValType::I64) {
|
||||||
val = Val(uint32_t(i32));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ValType::I64: {
|
|
||||||
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_I64_LINK);
|
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_I64_LINK);
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
case ValType::F32: {
|
|
||||||
if (!v.isNumber())
|
|
||||||
return ThrowBadImportType(cx, import.field.get(), "Number");
|
|
||||||
double d;
|
|
||||||
if (!ToNumber(cx, v, &d))
|
|
||||||
return false;
|
|
||||||
val = Val(float(d));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ValType::F64: {
|
|
||||||
if (!v.isNumber())
|
|
||||||
return ThrowBadImportType(cx, import.field.get(), "Number");
|
|
||||||
double d;
|
|
||||||
if (!ToNumber(cx, v, &d))
|
|
||||||
return false;
|
|
||||||
val = Val(d);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
MOZ_CRASH("unexpected import value type");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (!v.isNumber())
|
||||||
|
return ThrowBadImportType(cx, import.field.get(), "Number");
|
||||||
|
|
||||||
|
if (!ToWebAssemblyValue(cx, global.type(), v, &val))
|
||||||
|
return false;
|
||||||
|
|
||||||
if (!globalImports->append(val))
|
if (!globalImports->append(val))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1884,6 +1919,204 @@ WasmTableObject::table() const
|
|||||||
return *(Table*)getReservedSlot(TABLE_SLOT).toPrivate();
|
return *(Table*)getReservedSlot(TABLE_SLOT).toPrivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// WebAssembly.global class and methods
|
||||||
|
|
||||||
|
#ifdef ENABLE_WASM_GLOBAL
|
||||||
|
|
||||||
|
const ClassOps WasmGlobalObject::classOps_ =
|
||||||
|
{
|
||||||
|
nullptr, /* addProperty */
|
||||||
|
nullptr, /* delProperty */
|
||||||
|
nullptr, /* enumerate */
|
||||||
|
nullptr, /* newEnumerate */
|
||||||
|
nullptr, /* resolve */
|
||||||
|
nullptr, /* mayResolve */
|
||||||
|
nullptr, /* finalize */
|
||||||
|
nullptr, /* call */
|
||||||
|
nullptr, /* hasInstance */
|
||||||
|
nullptr, /* construct */
|
||||||
|
nullptr /* trace */
|
||||||
|
};
|
||||||
|
|
||||||
|
const Class WasmGlobalObject::class_ =
|
||||||
|
{
|
||||||
|
"WebAssembly.Global",
|
||||||
|
JSCLASS_HAS_RESERVED_SLOTS(WasmGlobalObject::RESERVED_SLOTS),
|
||||||
|
&WasmGlobalObject::classOps_
|
||||||
|
};
|
||||||
|
|
||||||
|
/* static */ WasmGlobalObject*
|
||||||
|
WasmGlobalObject::create(JSContext* cx, wasm::ValType type, bool isMutable, HandleValue val)
|
||||||
|
{
|
||||||
|
RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmGlobal).toObject());
|
||||||
|
|
||||||
|
AutoSetNewObjectMetadata metadata(cx);
|
||||||
|
Rooted<WasmGlobalObject*> obj(cx, NewObjectWithGivenProto<WasmGlobalObject>(cx, proto));
|
||||||
|
if (!obj)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
obj->initReservedSlot(TYPE_SLOT, Int32Value(int32_t(type)));
|
||||||
|
obj->initReservedSlot(MUTABLE_SLOT, JS::BooleanValue(isMutable));
|
||||||
|
obj->initReservedSlot(VALUE_SLOT, val);
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
WasmGlobalObject::construct(JSContext* cx, unsigned argc, Value* vp)
|
||||||
|
{
|
||||||
|
CallArgs args = CallArgsFromVp(argc, vp);
|
||||||
|
|
||||||
|
if (!ThrowIfNotConstructing(cx, args, "Global"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!args.requireAtLeast(cx, "WebAssembly.Global", 1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!args.get(0).isObject()) {
|
||||||
|
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_DESC_ARG, "global");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RootedObject obj(cx, &args[0].toObject());
|
||||||
|
|
||||||
|
RootedValue typeVal(cx);
|
||||||
|
if (!JS_GetProperty(cx, obj, "type", &typeVal))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
RootedString typeStr(cx, ToString(cx, typeVal));
|
||||||
|
if (!typeStr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
RootedLinearString typeLinearStr(cx, typeStr->ensureLinear(cx));
|
||||||
|
if (!typeLinearStr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ValType globalType;
|
||||||
|
if (StringEqualsAscii(typeLinearStr, "i32")) {
|
||||||
|
globalType = ValType::I32;
|
||||||
|
} else if (StringEqualsAscii(typeLinearStr, "f32")) {
|
||||||
|
globalType = ValType::F32;
|
||||||
|
} else if (StringEqualsAscii(typeLinearStr, "f64")) {
|
||||||
|
globalType = ValType::F64;
|
||||||
|
} else {
|
||||||
|
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_GLOBAL_TYPE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RootedValue mutableVal(cx);
|
||||||
|
if (!JS_GetProperty(cx, obj, "mutable", &mutableVal))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bool isMutable = ToBoolean(mutableVal);
|
||||||
|
|
||||||
|
RootedValue valueVal(cx);
|
||||||
|
if (!JS_GetProperty(cx, obj, "value", &valueVal))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Val globalVal;
|
||||||
|
if (!ToWebAssemblyValue(cx, globalType, valueVal, &globalVal))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
RootedValue globalValue(cx);
|
||||||
|
ToJSValue(globalVal, &globalValue);
|
||||||
|
|
||||||
|
Rooted<WasmGlobalObject*> global(cx, WasmGlobalObject::create(cx, globalType, isMutable,
|
||||||
|
globalValue));
|
||||||
|
if (!global)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
args.rval().setObject(*global);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
IsGlobal(HandleValue v)
|
||||||
|
{
|
||||||
|
return v.isObject() && v.toObject().is<WasmGlobalObject>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
WasmGlobalObject::valueGetterImpl(JSContext* cx, const CallArgs& args)
|
||||||
|
{
|
||||||
|
switch (args.thisv().toObject().as<WasmGlobalObject>().type()) {
|
||||||
|
case ValType::I32:
|
||||||
|
case ValType::F32:
|
||||||
|
case ValType::F64:
|
||||||
|
args.rval().set(args.thisv().toObject().as<WasmGlobalObject>().value());
|
||||||
|
return true;
|
||||||
|
case ValType::I64:
|
||||||
|
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_I64_TYPE);
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
MOZ_CRASH();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
WasmGlobalObject::valueGetter(JSContext* cx, unsigned argc, Value* vp)
|
||||||
|
{
|
||||||
|
CallArgs args = CallArgsFromVp(argc, vp);
|
||||||
|
return CallNonGenericMethod<IsGlobal, valueGetterImpl>(cx, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
WasmGlobalObject::valueSetterImpl(JSContext* cx, const CallArgs& args)
|
||||||
|
{
|
||||||
|
if (!args.thisv().toObject().as<WasmGlobalObject>().isMutable()) {
|
||||||
|
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_GLOBAL_IMMUTABLE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO - implement this, we probably need a different representation for
|
||||||
|
// mutable globals.
|
||||||
|
JS_ReportErrorASCII(cx, "Value setter not yet implemented");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
WasmGlobalObject::valueSetter(JSContext* cx, unsigned argc, Value* vp)
|
||||||
|
{
|
||||||
|
CallArgs args = CallArgsFromVp(argc, vp);
|
||||||
|
return CallNonGenericMethod<IsGlobal, valueSetterImpl>(cx, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
const JSPropertySpec WasmGlobalObject::properties[] =
|
||||||
|
{
|
||||||
|
JS_PSGS("value", WasmGlobalObject::valueGetter, WasmGlobalObject::valueSetter, 0),
|
||||||
|
JS_PS_END
|
||||||
|
};
|
||||||
|
|
||||||
|
const JSFunctionSpec WasmGlobalObject::methods[] =
|
||||||
|
{
|
||||||
|
JS_SYM_FN(toPrimitive, WasmGlobalObject::valueGetter, 1, JSPROP_READONLY),
|
||||||
|
JS_FS_END
|
||||||
|
};
|
||||||
|
|
||||||
|
const JSFunctionSpec WasmGlobalObject::static_methods[] =
|
||||||
|
{ JS_FS_END };
|
||||||
|
|
||||||
|
ValType
|
||||||
|
WasmGlobalObject::type() const
|
||||||
|
{
|
||||||
|
return static_cast<ValType>(getReservedSlot(TYPE_SLOT).toInt32());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
WasmGlobalObject::isMutable() const
|
||||||
|
{
|
||||||
|
return getReservedSlot(MUTABLE_SLOT).toBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
|
Value
|
||||||
|
WasmGlobalObject::value() const
|
||||||
|
{
|
||||||
|
return getReservedSlot(VALUE_SLOT);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ENABLE_WASM_GLOBAL
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// WebAssembly class and static methods
|
// WebAssembly class and static methods
|
||||||
|
|
||||||
@ -2717,6 +2950,9 @@ js::InitWebAssemblyClass(JSContext* cx, HandleObject obj)
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
RootedObject moduleProto(cx), instanceProto(cx), memoryProto(cx), tableProto(cx);
|
RootedObject moduleProto(cx), instanceProto(cx), memoryProto(cx), tableProto(cx);
|
||||||
|
#ifdef ENABLE_WASM_GLOBAL
|
||||||
|
RootedObject globalProto(cx);
|
||||||
|
#endif
|
||||||
if (!InitConstructor<WasmModuleObject>(cx, wasm, "Module", &moduleProto))
|
if (!InitConstructor<WasmModuleObject>(cx, wasm, "Module", &moduleProto))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
if (!InitConstructor<WasmInstanceObject>(cx, wasm, "Instance", &instanceProto))
|
if (!InitConstructor<WasmInstanceObject>(cx, wasm, "Instance", &instanceProto))
|
||||||
@ -2725,6 +2961,10 @@ js::InitWebAssemblyClass(JSContext* cx, HandleObject obj)
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
if (!InitConstructor<WasmTableObject>(cx, wasm, "Table", &tableProto))
|
if (!InitConstructor<WasmTableObject>(cx, wasm, "Table", &tableProto))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
#ifdef ENABLE_WASM_GLOBAL
|
||||||
|
if (!InitConstructor<WasmGlobalObject>(cx, wasm, "Global", &globalProto))
|
||||||
|
return nullptr;
|
||||||
|
#endif
|
||||||
if (!InitErrorClass(cx, wasm, "CompileError", JSEXN_WASMCOMPILEERROR))
|
if (!InitErrorClass(cx, wasm, "CompileError", JSEXN_WASMCOMPILEERROR))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
if (!InitErrorClass(cx, wasm, "LinkError", JSEXN_WASMLINKERROR))
|
if (!InitErrorClass(cx, wasm, "LinkError", JSEXN_WASMLINKERROR))
|
||||||
@ -2744,6 +2984,9 @@ js::InitWebAssemblyClass(JSContext* cx, HandleObject obj)
|
|||||||
global->setPrototype(JSProto_WasmInstance, ObjectValue(*instanceProto));
|
global->setPrototype(JSProto_WasmInstance, ObjectValue(*instanceProto));
|
||||||
global->setPrototype(JSProto_WasmMemory, ObjectValue(*memoryProto));
|
global->setPrototype(JSProto_WasmMemory, ObjectValue(*memoryProto));
|
||||||
global->setPrototype(JSProto_WasmTable, ObjectValue(*tableProto));
|
global->setPrototype(JSProto_WasmTable, ObjectValue(*tableProto));
|
||||||
|
#ifdef ENABLE_WASM_GLOBAL
|
||||||
|
global->setPrototype(JSProto_WasmGlobal, ObjectValue(*globalProto));
|
||||||
|
#endif
|
||||||
global->setConstructor(JSProto_WebAssembly, ObjectValue(*wasm));
|
global->setConstructor(JSProto_WebAssembly, ObjectValue(*wasm));
|
||||||
|
|
||||||
MOZ_ASSERT(global->isStandardClassResolved(JSProto_WebAssembly));
|
MOZ_ASSERT(global->isStandardClassResolved(JSProto_WebAssembly));
|
||||||
|
@ -43,6 +43,15 @@ HasCompilerSupport(JSContext* cx);
|
|||||||
bool
|
bool
|
||||||
HasSupport(JSContext* cx);
|
HasSupport(JSContext* cx);
|
||||||
|
|
||||||
|
// ToWebAssemblyValue and ToJSValue are conversion functions defined in
|
||||||
|
// the Wasm JS API spec.
|
||||||
|
|
||||||
|
bool
|
||||||
|
ToWebAssemblyValue(JSContext* cx, ValType targetType, HandleValue v, Val* val);
|
||||||
|
|
||||||
|
void
|
||||||
|
ToJSValue(const Val& val, MutableHandleValue v);
|
||||||
|
|
||||||
// Compiles the given binary wasm module given the ArrayBufferObject
|
// Compiles the given binary wasm module given the ArrayBufferObject
|
||||||
// and links the module's imports with the given import object.
|
// and links the module's imports with the given import object.
|
||||||
|
|
||||||
@ -275,6 +284,42 @@ class WasmTableObject : public NativeObject
|
|||||||
wasm::Table& table() const;
|
wasm::Table& table() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef ENABLE_WASM_GLOBAL
|
||||||
|
|
||||||
|
// The class of WebAssembly.Global. A WasmGlobalObject holds either the value
|
||||||
|
// of an immutable wasm global or the cell of a mutable wasm global.
|
||||||
|
|
||||||
|
class WasmGlobalObject : public NativeObject
|
||||||
|
{
|
||||||
|
static const unsigned TYPE_SLOT = 0;
|
||||||
|
static const unsigned MUTABLE_SLOT = 1;
|
||||||
|
static const unsigned VALUE_SLOT = 2;
|
||||||
|
|
||||||
|
static const ClassOps classOps_;
|
||||||
|
|
||||||
|
static bool valueGetterImpl(JSContext* cx, const CallArgs& args);
|
||||||
|
static bool valueGetter(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
static bool valueSetterImpl(JSContext* cx, const CallArgs& args);
|
||||||
|
static bool valueSetter(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const unsigned RESERVED_SLOTS = 3;
|
||||||
|
static const Class class_;
|
||||||
|
static const JSPropertySpec properties[];
|
||||||
|
static const JSFunctionSpec methods[];
|
||||||
|
static const JSFunctionSpec static_methods[];
|
||||||
|
static bool construct(JSContext*, unsigned, Value*);
|
||||||
|
|
||||||
|
static WasmGlobalObject* create(JSContext* cx, wasm::ValType type, bool isMutable,
|
||||||
|
HandleValue value);
|
||||||
|
|
||||||
|
wasm::ValType type() const;
|
||||||
|
bool isMutable() const;
|
||||||
|
Value value() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ENABLE_WASM_GLOBAL
|
||||||
|
|
||||||
} // namespace js
|
} // namespace js
|
||||||
|
|
||||||
#endif // wasm_js_h
|
#endif // wasm_js_h
|
||||||
|
@ -1052,30 +1052,21 @@ GetGlobalExport(JSContext* cx, const GlobalDescVector& globals, uint32_t globalI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (global.type()) {
|
if (val.type() == ValType::I64) {
|
||||||
case ValType::I32: {
|
|
||||||
jsval.set(Int32Value(val.i32()));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case ValType::I64: {
|
|
||||||
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_I64_LINK);
|
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_I64_LINK);
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
case ValType::F32: {
|
|
||||||
float f = val.f32();
|
|
||||||
jsval.set(DoubleValue(JS::CanonicalizeNaN(double(f))));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case ValType::F64: {
|
|
||||||
double d = val.f64();
|
|
||||||
jsval.set(DoubleValue(JS::CanonicalizeNaN(d)));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
MOZ_CRASH("unexpected type when creating global exports");
|
|
||||||
|
ToJSValue(val, jsval);
|
||||||
|
|
||||||
|
#ifdef ENABLE_WASM_GLOBAL
|
||||||
|
Rooted<WasmGlobalObject*> go(cx, WasmGlobalObject::create(cx, ValType::I32, false, jsval));
|
||||||
|
if (!go)
|
||||||
|
return false;
|
||||||
|
jsval.setObject(*go);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
@ -222,6 +222,15 @@ function get(instance, name) {
|
|||||||
if (instance.isError())
|
if (instance.isError())
|
||||||
return instance;
|
return instance;
|
||||||
|
|
||||||
|
// Experimental API change. We try to export WebAssembly.Global instances,
|
||||||
|
// not primitive values. In that case the Number() cast is necessary here
|
||||||
|
// to convert the Global to a value: the harness examines types carefully
|
||||||
|
// and will not trigger the @@toPrimitive hook on Global, unlike most user
|
||||||
|
// code.
|
||||||
|
|
||||||
|
if (typeof WebAssembly.Global === "function")
|
||||||
|
return ValueResult(Number(instance.value.exports[name]));
|
||||||
|
|
||||||
return ValueResult(instance.value.exports[name]);
|
return ValueResult(instance.value.exports[name]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user