Bug 1449213: Create WebAssembly.Global objects for imported globals that received a primitive; r=luke

--HG--
extra : histedit_source : 89c921ca195814b992b9f3a0e09a3d6e3b07bfe9%2C98d3ade79462e054c7ee2984182d47771a3b4cd2
This commit is contained in:
Benjamin Bouvier 2018-03-27 18:07:06 +02:00
Родитель 76ddd6f74d
Коммит 12c3f9f227
6 изменённых файлов: 202 добавлений и 164 удалений

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

@ -109,9 +109,9 @@ assertEq(module.f, module.tbl.get(1));
// Import/export rules.
if (typeof WebAssembly.Global === "undefined") {
wasmFailValidateText(`(module (import "globals" "x" (global (mut i32))))`,
/can't import.* mutable globals in the MVP/);
/can't import.* mutable globals in the MVP/);
wasmFailValidateText(`(module (global (mut i32) (i32.const 42)) (export "" global 0))`,
/can't .*export mutable globals in the MVP/);
/can't .*export mutable globals in the MVP/);
}
// Import/export semantics.
@ -130,6 +130,11 @@ if (typeof WebAssembly.Global === "function")
else
assertEq(module.value, 42);
assertEq(wasmEvalText(`(module
(global (import "a" "b") (mut i32))
(func (export "get") (result i32) get_global 0)
)`, { a: { b: 42 } }).exports.get(), 42);
// Can only import numbers (no implicit coercions).
module = new WebAssembly.Module(wasmTextToBinary(`(module
(global (import "globs" "i32") i32)
@ -229,18 +234,18 @@ function testInitExpr(type, initialValue, nextValue, coercion, assertFunc = asse
assertFunc(module.get1(), coercion(initialValue));
// See comment earlier about WebAssembly.Global
if (typeof WebAssembly.Global === "function")
assertFunc(Number(module.global_imm), coercion(initialValue));
assertFunc(Number(module.global_imm), coercion(initialValue));
else
assertFunc(module.global_imm, coercion(initialValue));
assertFunc(module.global_imm, coercion(initialValue));
assertEq(module.set1(coercion(nextValue)), undefined);
assertFunc(module.get1(), coercion(nextValue));
assertFunc(module.get0(), coercion(initialValue));
// See comment earlier about WebAssembly.Global
if (typeof WebAssembly.Global === "function")
assertFunc(Number(module.global_imm), coercion(initialValue));
assertFunc(Number(module.global_imm), coercion(initialValue));
else
assertFunc(module.global_imm, coercion(initialValue));
assertFunc(module.global_imm, coercion(initialValue));
assertFunc(module.get_cst(), coercion(initialValue));
}
@ -273,18 +278,18 @@ else {
// import it into another.
let i = new WebAssembly.Instance(
new WebAssembly.Module(
wasmTextToBinary(`(module
(global (export "g") i64 (i64.const 37))
(global (export "h") (mut i64) (i64.const 37)))`)));
new WebAssembly.Module(
wasmTextToBinary(`(module
(global (export "g") i64 (i64.const 37))
(global (export "h") (mut i64) (i64.const 37)))`)));
let j = new WebAssembly.Instance(
new WebAssembly.Module(
wasmTextToBinary(`(module
(import "globals" "g" (global i64))
(func (export "f") (result i32)
(i64.eq (get_global 0) (i64.const 37))))`)),
{globals: {g: i.exports.g}});
new WebAssembly.Module(
wasmTextToBinary(`(module
(import "globals" "g" (global i64))
(func (export "f") (result i32)
(i64.eq (get_global 0) (i64.const 37))))`)),
{globals: {g: i.exports.g}});
assertEq(j.exports.f(), 1);
@ -346,17 +351,17 @@ if (typeof WebAssembly.Global === "function") {
// These types should not work:
assertErrorMessage(() => new WebAssembly.Global({type: "i64"}),
TypeError,
/bad type for a WebAssembly.Global/);
TypeError,
/bad type for a WebAssembly.Global/);
assertErrorMessage(() => new WebAssembly.Global({}),
TypeError,
/bad type for a WebAssembly.Global/);
TypeError,
/bad type for a WebAssembly.Global/);
assertErrorMessage(() => new WebAssembly.Global({type: "fnord"}),
TypeError,
/bad type for a WebAssembly.Global/);
TypeError,
/bad type for a WebAssembly.Global/);
assertErrorMessage(() => new WebAssembly.Global(),
TypeError,
/WebAssembly.Global requires more than 0 arguments/);
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);
@ -366,12 +371,12 @@ if (typeof WebAssembly.Global === "function") {
assertEq((new WebAssembly.Global({type: "i32", value: NaN})).value, 0);
{
// "value" is enumerable
let x = new WebAssembly.Global({type: "i32"});
let s = "";
for ( let i in x )
s = s + i + ",";
assertEq(s, "value,");
// "value" is enumerable
let x = new WebAssembly.Global({type: "i32"});
let s = "";
for ( let i in x )
s = s + i + ",";
assertEq(s, "value,");
}
// "value" is defined on the prototype, not on the object
@ -379,142 +384,142 @@ if (typeof WebAssembly.Global === "function") {
// Can't set the value of an immutable global
assertErrorMessage(() => (new WebAssembly.Global({type: "i32"})).value = 10,
TypeError,
/can't set value of immutable global/);
TypeError,
/can't set value of immutable global/);
{
// Can set the value of a mutable global
let g = new WebAssembly.Global({type: "i32", mutable: true, value: 37});
g.value = 10;
assertEq(g.value, 10);
// Can set the value of a mutable global
let g = new WebAssembly.Global({type: "i32", mutable: true, value: 37});
g.value = 10;
assertEq(g.value, 10);
}
{
// Misc internal conversions
let g = new WebAssembly.Global({type: "i32", value: 42});
// Misc internal conversions
let g = new WebAssembly.Global({type: "i32", value: 42});
// valueOf
assertEq(g - 5, 37);
// valueOf
assertEq(g - 5, 37);
// @@toStringTag
assertEq(g.toString(), "[object WebAssembly.Global]");
// @@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)))`)));
// 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);
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 }});
// 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);
// And when it is then accessed it has the right value:
assertEq(j.exports.f(), 42);
}
// Identity of WebAssembly.Global objects (independent of mutablity).
{
// When a global is exported twice, the two objects are the same.
let i =
new WebAssembly.Instance(
new WebAssembly.Module(
wasmTextToBinary(`(module
(global i32 (i32.const 0))
(export "a" global 0)
(export "b" global 0))`)));
assertEq(i.exports.a, i.exports.b);
// When a global is exported twice, the two objects are the same.
let i =
new WebAssembly.Instance(
new WebAssembly.Module(
wasmTextToBinary(`(module
(global i32 (i32.const 0))
(export "a" global 0)
(export "b" global 0))`)));
assertEq(i.exports.a, i.exports.b);
// When a global is imported and then exported, the exported object is
// the same as the imported object.
let j =
new WebAssembly.Instance(
new WebAssembly.Module(
wasmTextToBinary(`(module
(import "" "a" (global i32))
(export "x" global 0))`)),
{ "": {a: i.exports.a}});
// When a global is imported and then exported, the exported object is
// the same as the imported object.
let j =
new WebAssembly.Instance(
new WebAssembly.Module(
wasmTextToBinary(`(module
(import "" "a" (global i32))
(export "x" global 0))`)),
{ "": {a: i.exports.a}});
assertEq(i.exports.a, j.exports.x);
assertEq(i.exports.a, j.exports.x);
// When a global is imported twice (ie aliased) and then exported twice,
// the exported objects are the same, and are also the same as the
// imported object.
let k =
new WebAssembly.Instance(
new WebAssembly.Module(
wasmTextToBinary(`(module
(import "" "a" (global i32))
(import "" "b" (global i32))
(export "x" global 0)
(export "y" global 1))`)),
{ "": {a: i.exports.a,
b: i.exports.a}});
// When a global is imported twice (ie aliased) and then exported twice,
// the exported objects are the same, and are also the same as the
// imported object.
let k =
new WebAssembly.Instance(
new WebAssembly.Module(
wasmTextToBinary(`(module
(import "" "a" (global i32))
(import "" "b" (global i32))
(export "x" global 0)
(export "y" global 1))`)),
{ "": {a: i.exports.a,
b: i.exports.a}});
assertEq(i.exports.a, k.exports.x);
assertEq(k.exports.x, k.exports.y);
assertEq(i.exports.a, k.exports.x);
assertEq(k.exports.x, k.exports.y);
}
// Mutability
{
let i =
new WebAssembly.Instance(
new WebAssembly.Module(
wasmTextToBinary(`(module
(global (export "g") (mut i32) (i32.const 37))
(func (export "getter") (result i32)
(get_global 0))
(func (export "setter") (param i32)
(set_global 0 (get_local 0))))`)));
let i =
new WebAssembly.Instance(
new WebAssembly.Module(
wasmTextToBinary(`(module
(global (export "g") (mut i32) (i32.const 37))
(func (export "getter") (result i32)
(get_global 0))
(func (export "setter") (param i32)
(set_global 0 (get_local 0))))`)));
let j =
new WebAssembly.Instance(
new WebAssembly.Module(
wasmTextToBinary(`(module
(import "" "g" (global (mut i32)))
(func (export "getter") (result i32)
(get_global 0))
(func (export "setter") (param i32)
(set_global 0 (get_local 0))))`)),
{"": {g: i.exports.g}});
let j =
new WebAssembly.Instance(
new WebAssembly.Module(
wasmTextToBinary(`(module
(import "" "g" (global (mut i32)))
(func (export "getter") (result i32)
(get_global 0))
(func (export "setter") (param i32)
(set_global 0 (get_local 0))))`)),
{"": {g: i.exports.g}});
// Initial values
assertEq(i.exports.g.value, 37);
assertEq(i.exports.getter(), 37);
assertEq(j.exports.getter(), 37);
// Initial values
assertEq(i.exports.g.value, 37);
assertEq(i.exports.getter(), 37);
assertEq(j.exports.getter(), 37);
// Set in i, observe everywhere
i.exports.setter(42);
// Set in i, observe everywhere
i.exports.setter(42);
assertEq(i.exports.g.value, 42);
assertEq(i.exports.getter(), 42);
assertEq(j.exports.getter(), 42);
assertEq(i.exports.g.value, 42);
assertEq(i.exports.getter(), 42);
assertEq(j.exports.getter(), 42);
// Set in j, observe everywhere
j.exports.setter(78);
// Set in j, observe everywhere
j.exports.setter(78);
assertEq(i.exports.g.value, 78);
assertEq(i.exports.getter(), 78);
assertEq(j.exports.getter(), 78);
assertEq(i.exports.g.value, 78);
assertEq(i.exports.getter(), 78);
assertEq(j.exports.getter(), 78);
// Set on global object, observe everywhere
i.exports.g.value = 197;
// Set on global object, observe everywhere
i.exports.g.value = 197;
assertEq(i.exports.g.value, 197);
assertEq(i.exports.getter(), 197);
assertEq(j.exports.getter(), 197);
assertEq(i.exports.g.value, 197);
assertEq(i.exports.getter(), 197);
assertEq(j.exports.getter(), 197);
}
// TEST THIS LAST

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

@ -455,10 +455,11 @@ Instance::Instance(JSContext* cx,
uint8_t* globalAddr = globalData() + global.offset();
switch (global.kind()) {
case GlobalKind::Import: {
size_t imported = global.importIndex();
if (global.isIndirect())
*(void**)globalAddr = globalObjs[global.importIndex()]->cell();
*(void**)globalAddr = globalObjs[imported]->cell();
else
globalImportValues[global.importIndex()].writePayload(globalAddr);
globalImportValues[imported].writePayload(globalAddr);
break;
}
case GlobalKind::Variable: {

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

@ -249,7 +249,7 @@ GetImports(JSContext* cx,
#if defined(ENABLE_WASM_GLOBAL) && defined(EARLY_BETA_OR_EARLIER)
if (v.isObject() && v.toObject().is<WasmGlobalObject>()) {
RootedWasmGlobalObject obj(cx, &v.toObject().as<WasmGlobalObject>());
if (globalObjs.length() <= index && !globalObjs.resize(index+1)) {
if (globalObjs.length() <= index && !globalObjs.resize(index + 1)) {
ReportOutOfMemory(cx);
return false;
}

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

@ -1018,40 +1018,69 @@ ExtractGlobalValue(const ValVector& globalImportValues, uint32_t globalIndex, co
MOZ_CRASH("Not a global value");
}
#if defined(ENABLE_WASM_GLOBAL) && defined(EARLY_BETA_OR_EARLIER)
static bool
EnsureGlobalObject(JSContext* cx, const ValVector& globalImportValues, size_t globalIndex,
const GlobalDesc& global, WasmGlobalObjectVector& globalObjs)
{
if (globalIndex < globalObjs.length() && globalObjs[globalIndex])
return true;
Val val = ExtractGlobalValue(globalImportValues, globalIndex, global);
RootedWasmGlobalObject go(cx, WasmGlobalObject::create(cx, val, global.isMutable()));
if (!go)
return false;
if (globalObjs.length() <= globalIndex && !globalObjs.resize(globalIndex + 1)) {
ReportOutOfMemory(cx);
return false;
}
globalObjs[globalIndex] = go;
return true;
}
#endif
bool
Module::instantiateGlobalExports(JSContext* cx,
const ValVector& globalImportValues,
WasmGlobalObjectVector& globalObjs) const
Module::instantiateGlobals(JSContext* cx, const ValVector& globalImportValues,
WasmGlobalObjectVector& globalObjs) const
{
#if defined(ENABLE_WASM_GLOBAL) && defined(EARLY_BETA_OR_EARLIER)
// If there are exported globals that aren't in the globalObjs because they
// If there are exported globals that aren't in globalObjs because they
// originate in this module or because they were immutable imports that came
// in as values (not cells) then we must create cells in the globalObjs for
// in as primitive values then we must create cells in the globalObjs for
// them here, as WasmInstanceObject::create() and CreateExportObject() will
// need the cells to exist.
const GlobalDescVector& globals = metadata().globals;
for (const Export& exp : exports_) {
if (exp.kind() == DefinitionKind::Global) {
unsigned globalIndex = exp.globalIndex();
if (globalIndex >= globalObjs.length() || !globalObjs[globalIndex]) {
const GlobalDesc& global = globals[globalIndex];
Val val = ExtractGlobalValue(globalImportValues, globalIndex, global);
RootedWasmGlobalObject go(cx, WasmGlobalObject::create(cx, val,
global.isMutable()));
if (!go)
return false;
if (globalObjs.length() <= globalIndex && !globalObjs.resize(globalIndex+1)) {
ReportOutOfMemory(cx);
return false;
}
globalObjs[globalIndex] = go;
}
}
if (exp.kind() != DefinitionKind::Global)
continue;
unsigned globalIndex = exp.globalIndex();
const GlobalDesc& global = globals[globalIndex];
if (!EnsureGlobalObject(cx, globalImportValues, globalIndex, global, globalObjs))
return false;
}
// Imported globals may also have received only a primitive value, thus
// they may need their own Global object, because the compiled code assumed
// they were indirect.
size_t numGlobalImports = 0;
for (const Import& import : imports_) {
if (import.kind != DefinitionKind::Global)
continue;
size_t globalIndex = numGlobalImports++;
const GlobalDesc& global = globals[globalIndex];
MOZ_ASSERT(global.importIndex() == globalIndex);
if (!global.isIndirect())
continue;
if (!EnsureGlobalObject(cx, globalImportValues, globalIndex, global, globalObjs))
return false;
}
MOZ_ASSERT_IF(!metadata().isAsmJS(),
numGlobalImports == globals.length() || !globals[numGlobalImports].isImport());
#endif
return true;
}
@ -1196,7 +1225,7 @@ Module::instantiate(JSContext* cx,
if (!instantiateTable(cx, &table, &tables))
return false;
if (!instantiateGlobalExports(cx, globalImportValues, globalObjs))
if (!instantiateGlobals(cx, globalImportValues, globalObjs))
return false;
UniqueTlsData tlsData = CreateTlsData(metadata().globalDataLength);

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

@ -149,9 +149,8 @@ class Module : public JS::WasmModule
bool instantiateTable(JSContext* cx,
MutableHandleWasmTableObject table,
SharedTableVector* tables) const;
bool instantiateGlobalExports(JSContext* cx,
const ValVector& globalImportValues,
WasmGlobalObjectVector& globalObjs) const;
bool instantiateGlobals(JSContext* cx, const ValVector& globalImportValues,
WasmGlobalObjectVector& globalObjs) const;
bool initSegments(JSContext* cx,
HandleWasmInstanceObject instance,
Handle<FunctionVector> funcImports,

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

@ -806,7 +806,11 @@ class GlobalDesc
//
// We don't want to indirect unless we must, so only mutable, exposed
// globals are indirected - in all other cases we copy values into and out
// of them module.
// of their module.
//
// Note that isIndirect() isn't equivalent to getting a WasmGlobalObject:
// an immutable exported global will still get an object, but will not be
// indirect.
bool isIndirect() const { return isMutable() && isWasm() && (isImport() || isExport()); }
ValType type() const {