Commit 2747d0e6 authored by Ben Smith's avatar Ben Smith Committed by Commit Bot

[wasm] Fix some bugs in mut global implementation

* If the mutability of the global object doesn't match the module, then
  it should throw a LinkError.
* There was a missing `return` when importing a Number as a mutable
  global.
* All globals were being exported as immutable.
* Attempting to set the value of an immutable global should throw a
  TypeError.
* The length of the setter function should be 1.

Bug: v8:7625
Change-Id: I08d6a428506a18db15eecadf4cbcee89e0658924
Reviewed-on: https://chromium-review.googlesource.com/1031626Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Commit-Queue: Ben Smith <binji@chromium.org>
Cr-Commit-Position: refs/heads/master@{#52865}
parent 2a7b2d6f
...@@ -2359,6 +2359,12 @@ int InstanceBuilder::ProcessImports(Handle<WasmInstanceObject> instance) { ...@@ -2359,6 +2359,12 @@ int InstanceBuilder::ProcessImports(Handle<WasmInstanceObject> instance) {
module_name, import_name); module_name, import_name);
return -1; return -1;
} }
if (global_object->is_mutable() != global.mutability) {
ReportLinkError(
"imported global does not match the expected mutability",
index, module_name, import_name);
return -1;
}
if (global.mutability) { if (global.mutability) {
Handle<JSArrayBuffer> buffer(global_object->array_buffer(), Handle<JSArrayBuffer> buffer(global_object->array_buffer(),
isolate_); isolate_);
...@@ -2378,6 +2384,7 @@ int InstanceBuilder::ProcessImports(Handle<WasmInstanceObject> instance) { ...@@ -2378,6 +2384,7 @@ int InstanceBuilder::ProcessImports(Handle<WasmInstanceObject> instance) {
ReportLinkError( ReportLinkError(
"imported mutable global must be a WebAssembly.Global object", "imported mutable global must be a WebAssembly.Global object",
index, module_name, import_name); index, module_name, import_name);
return -1;
} }
WriteGlobalValue(global, value->Number()); WriteGlobalValue(global, value->Number());
} else { } else {
...@@ -2608,14 +2615,13 @@ void InstanceBuilder::ProcessExports( ...@@ -2608,14 +2615,13 @@ void InstanceBuilder::ProcessExports(
case kExternalGlobal: { case kExternalGlobal: {
WasmGlobal& global = module_->globals[exp.index]; WasmGlobal& global = module_->globals[exp.index];
if (FLAG_experimental_wasm_mut_global) { if (FLAG_experimental_wasm_mut_global) {
const bool is_mutable = false;
Handle<JSArrayBuffer> globals_buffer(instance->globals_buffer(), Handle<JSArrayBuffer> globals_buffer(instance->globals_buffer(),
isolate_); isolate_);
// Since the global's array buffer is always provided, allocation // Since the global's array buffer is always provided, allocation
// should never fail. // should never fail.
Handle<WasmGlobalObject> global_obj = Handle<WasmGlobalObject> global_obj =
WasmGlobalObject::New(isolate_, globals_buffer, global.type, WasmGlobalObject::New(isolate_, globals_buffer, global.type,
global.offset, is_mutable) global.offset, global.mutability)
.ToHandleChecked(); .ToHandleChecked();
desc.set_value(global_obj); desc.set_value(global_obj);
} else { } else {
......
...@@ -1033,6 +1033,11 @@ void WebAssemblyGlobalSetValue( ...@@ -1033,6 +1033,11 @@ void WebAssemblyGlobalSetValue(
ScheduledErrorThrower thrower(i_isolate, "set WebAssembly.Global.value"); ScheduledErrorThrower thrower(i_isolate, "set WebAssembly.Global.value");
EXTRACT_THIS(receiver, WasmGlobalObject); EXTRACT_THIS(receiver, WasmGlobalObject);
if (!receiver->is_mutable()) {
thrower.TypeError("Can't set the value of an immutable global.");
return;
}
switch (receiver->type()) { switch (receiver->type()) {
case i::wasm::kWasmI32: { case i::wasm::kWasmI32: {
int32_t i32_value = 0; int32_t i32_value = 0;
...@@ -1125,6 +1130,7 @@ void InstallGetterSetter(Isolate* isolate, Handle<JSObject> object, ...@@ -1125,6 +1130,7 @@ void InstallGetterSetter(Isolate* isolate, Handle<JSObject> object,
CreateFunc(isolate, GetterName(isolate, name), getter); CreateFunc(isolate, GetterName(isolate, name), getter);
Handle<JSFunction> setter_func = Handle<JSFunction> setter_func =
CreateFunc(isolate, SetterName(isolate, name), setter); CreateFunc(isolate, SetterName(isolate, name), setter);
setter_func->shared()->set_length(1);
v8::PropertyAttribute attributes = v8::PropertyAttribute attributes =
static_cast<v8::PropertyAttribute>(v8::DontEnum); static_cast<v8::PropertyAttribute>(v8::DontEnum);
......
...@@ -21,13 +21,14 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); ...@@ -21,13 +21,14 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
let global_builder = builder.addGlobal(type, false).exportAs(name); let global_builder = builder.addGlobal(type, false).exportAs(name);
if (value) global_builder.init = value; if (value) global_builder.init = value;
} }
var module = builder.instantiate(); var instance = builder.instantiate();
for (let [type, name, value] of globals) { for (let [type, name, value] of globals) {
let obj = module.exports[name]; let obj = instance.exports[name];
assertEquals("object", typeof obj, name); assertEquals("object", typeof obj, name);
assertTrue(obj instanceof WebAssembly.Global, name); assertTrue(obj instanceof WebAssembly.Global, name);
assertEquals(value || 0, obj.value, name); assertEquals(value || 0, obj.value, name);
assertThrows(() => obj.value = 0);
} }
})(); })();
...@@ -67,19 +68,19 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); ...@@ -67,19 +68,19 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
.addBody([kExprGetLocal, 0, kExprSetGlobal, index]) .addBody([kExprGetLocal, 0, kExprSetGlobal, index])
.exportFunc(); .exportFunc();
} }
var module = builder.instantiate(); var instance = builder.instantiate();
for (let [type, name, value] of globals) { for (let [type, name, value] of globals) {
let obj = module.exports[name]; let obj = instance.exports[name];
assertEquals(value || 0, obj.value, name); assertEquals(value || 0, obj.value, name);
// Changing the exported global should change the module's global. // Changing the exported global should change the instance's global.
obj.value = 1001; obj.value = 1001;
assertEquals(1001, module.exports['get ' + name](), name); assertEquals(1001, instance.exports['get ' + name](), name);
// Changing the module's global should change the exported global. // Changing the instance's global should change the exported global.
module.exports['set ' + name](112358); instance.exports['set ' + name](112358);
assertEquals(112358, obj.value, name); assertEquals(112358, obj.value, name);
} }
})(); })();
...@@ -23,13 +23,30 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); ...@@ -23,13 +23,30 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
let global = new WebAssembly.Global({type: 'f32'}, 1); let global = new WebAssembly.Global({type: 'f32'}, 1);
let builder = new WasmModuleBuilder(); let builder = new WasmModuleBuilder();
builder.addImportedGlobal("mod", "g", kWasmI32); builder.addImportedGlobal("mod", "g", kWasmI32);
builder.addFunction("main", kSig_i_v)
.addBody([kExprGetGlobal, 0])
.exportAs("main");
assertThrows(() => builder.instantiate({mod: {g: global}})); assertThrows(() => builder.instantiate({mod: {g: global}}));
})(); })();
(function TestMutableMismatch() {
let global = new WebAssembly.Global({type: 'f64'}, 1);
let builder = new WasmModuleBuilder();
builder.addImportedGlobal("mod", "g", kWasmF64, true);
assertThrows(() => builder.instantiate({mod: {g: global}}));
global = new WebAssembly.Global({type: 'i32', mutable: true}, 1);
builder = new WasmModuleBuilder();
builder.addImportedGlobal("mod", "g", kWasmI32);
assertThrows(() => builder.instantiate({mod: {g: global}}));
})();
(function TestImportMutableAsNumber() {
let builder = new WasmModuleBuilder();
builder.addImportedGlobal("mod", "g", kWasmF32, true);
assertThrows(() => builder.instantiate({mod: {g: 1234}}));
})();
(function TestImportI64AsNumber() { (function TestImportI64AsNumber() {
let builder = new WasmModuleBuilder(); let builder = new WasmModuleBuilder();
builder.addImportedGlobal("mod", "g", kWasmI64); builder.addImportedGlobal("mod", "g", kWasmI64);
......
...@@ -214,16 +214,24 @@ const f64_values = [ ...@@ -214,16 +214,24 @@ const f64_values = [
NaN NaN
]; ];
function GlobalI32(value) { function GlobalI32(value, mutable = false) {
return new WebAssembly.Global({type: 'i32'}, value); return new WebAssembly.Global({type: 'i32', mutable}, value);
} }
function GlobalF32(value) { function GlobalI64(mutable = false) {
return new WebAssembly.Global({type: 'f32'}, value); let builder = new WasmModuleBuilder();
builder.addGlobal(kWasm64, mutable).exportAs('i64');
let module = new WebAssembly.Module(builder.toBuffer());
let instance = new WebAssembly.Instance(module);
return instance.exports.i64;
} }
function GlobalF64(value) { function GlobalF32(value, mutable = false) {
return new WebAssembly.Global({type: 'f64'}, value); return new WebAssembly.Global({type: 'f32', mutable}, value);
}
function GlobalF64(value, mutable = false) {
return new WebAssembly.Global({type: 'f64', mutable}, value);
} }
(function TestDefaultValue() { (function TestDefaultValue() {
...@@ -233,6 +241,8 @@ function GlobalF64(value) { ...@@ -233,6 +241,8 @@ function GlobalF64(value) {
})(); })();
(function TestValueOf() { (function TestValueOf() {
assertTrue(WebAssembly.Global.prototype.valueOf instanceof Function);
assertSame(0, WebAssembly.Global.prototype.valueOf.length);
for (let u32_value of u32_values) { for (let u32_value of u32_values) {
let i32_value = u32_value | 0; let i32_value = u32_value | 0;
...@@ -241,6 +251,8 @@ function GlobalF64(value) { ...@@ -241,6 +251,8 @@ function GlobalF64(value) {
assertSame(i32_value, GlobalI32(i32_value).valueOf()); assertSame(i32_value, GlobalI32(i32_value).valueOf());
} }
assertThrows(() => GlobalI64().valueOf());
for (let f32_value of f32_values) { for (let f32_value of f32_values) {
assertSame(Math.fround(f32_value), GlobalF32(f32_value).valueOf()); assertSame(Math.fround(f32_value), GlobalF32(f32_value).valueOf());
} }
...@@ -248,10 +260,14 @@ function GlobalF64(value) { ...@@ -248,10 +260,14 @@ function GlobalF64(value) {
for (let f64_value of f64_values) { for (let f64_value of f64_values) {
assertSame(f64_value, GlobalF64(f64_value).valueOf()); assertSame(f64_value, GlobalF64(f64_value).valueOf());
} }
})(); })();
(function TestGetValue() { (function TestGetValue() {
let getter =
Object.getOwnPropertyDescriptor(WebAssembly.Global.prototype, 'value')
.get;
assertTrue(getter instanceof Function);
assertSame(0, getter.length);
for (let u32_value of u32_values) { for (let u32_value of u32_values) {
let i32_value = u32_value | 0; let i32_value = u32_value | 0;
...@@ -259,6 +275,8 @@ function GlobalF64(value) { ...@@ -259,6 +275,8 @@ function GlobalF64(value) {
assertSame(i32_value, GlobalI32(i32_value).value); assertSame(i32_value, GlobalI32(i32_value).value);
} }
assertThrows(() => GlobalI64().value);
for (let f32_value of f32_values) { for (let f32_value of f32_values) {
assertSame(Math.fround(f32_value), GlobalF32(f32_value).value); assertSame(Math.fround(f32_value), GlobalF32(f32_value).value);
} }
...@@ -266,15 +284,26 @@ function GlobalF64(value) { ...@@ -266,15 +284,26 @@ function GlobalF64(value) {
for (let f64_value of f64_values) { for (let f64_value of f64_values) {
assertSame(f64_value, GlobalF64(f64_value).value); assertSame(f64_value, GlobalF64(f64_value).value);
} }
})();
(function TestSetValueImmutable() {
assertThrows(() => GlobalI32().value = 0);
assertThrows(() => GlobalI64().value = 0);
assertThrows(() => GlobalF32().value = 0);
assertThrows(() => GlobalF64().value = 0);
})(); })();
(function TestSetValue() { (function TestSetValue() {
let setter =
Object.getOwnPropertyDescriptor(WebAssembly.Global.prototype, 'value')
.set;
assertTrue(setter instanceof Function);
assertSame(1, setter.length);
for (let u32_value of u32_values) { for (let u32_value of u32_values) {
let i32_value = u32_value | 0; let i32_value = u32_value | 0;
let global = GlobalI32(); let global = GlobalI32(0, true);
global.value = u32_value; global.value = u32_value;
assertSame(i32_value, global.value); assertSame(i32_value, global.value);
...@@ -282,16 +311,17 @@ function GlobalF64(value) { ...@@ -282,16 +311,17 @@ function GlobalF64(value) {
assertSame(i32_value, global.value); assertSame(i32_value, global.value);
} }
assertThrows(() => GlobalI64(true).value = 0);
for (let f32_value of f32_values) { for (let f32_value of f32_values) {
let global = GlobalF32(); let global = GlobalF32(0, true);
global.value = f32_value; global.value = f32_value;
assertSame(Math.fround(f32_value), global.value); assertSame(Math.fround(f32_value), global.value);
} }
for (let f64_value of f64_values) { for (let f64_value of f64_values) {
let global = GlobalF64(); let global = GlobalF64(0, true);
global.value = f64_value; global.value = f64_value;
assertSame(f64_value, global.value); assertSame(f64_value, global.value);
} }
})(); })();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment