[wasm-gc] Support non-function ref globals via WebAssembly.Global

Bug: v8:7748
Change-Id: Ibb43799319f8032d69adcaaeebb48ec8e4e6078c
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3869146
Commit-Queue: Matthias Liedtke <mliedtke@chromium.org>
Reviewed-by: Manos Koukoutos <manoskouk@chromium.org>
Cr-Commit-Position: refs/heads/main@{#82936}
This commit is contained in:
Matthias Liedtke 2022-09-01 17:08:14 +02:00 committed by V8 LUCI CQ
parent 04224d8cb4
commit 2f95d10f6b
4 changed files with 296 additions and 3 deletions

View File

@ -1180,6 +1180,7 @@ void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) {
string->StringEquals(v8_str(isolate, "arrayref"))) {
type = i::wasm::kWasmArrayRef;
} else {
// TODO(7748): Add "i31ref".
thrower.TypeError(
"Descriptor property 'element' must be a WebAssembly reference type");
return;
@ -1385,8 +1386,15 @@ bool GetValueType(Isolate* isolate, MaybeLocal<Value> maybe,
} else if (enabled_features.has_gc() &&
string->StringEquals(v8_str(isolate, "anyref"))) {
*type = i::wasm::kWasmAnyRef;
} else if (enabled_features.has_gc() &&
string->StringEquals(v8_str(isolate, "dataref"))) {
*type = i::wasm::kWasmDataRef;
} else if (enabled_features.has_gc() &&
string->StringEquals(v8_str(isolate, "arrayref"))) {
*type = i::wasm::kWasmArrayRef;
} else {
// Unrecognized type.
// TODO(7748): Add "i31ref".
*type = i::wasm::kWasmVoid;
}
return true;
@ -1432,6 +1440,24 @@ bool ToF64(Local<v8::Value> value, Local<Context> context, double* f64_value) {
return true;
}
bool checkAndUnpackAnyRef(i::Isolate* i_isolate,
i::Handle<i::Object>& in_out_value,
i::wasm::ValueType type, ErrorThrower& thrower) {
const char* error_message = nullptr;
auto module = nullptr;
bool valid = internal::wasm::TypecheckJSObject(
i_isolate, module, in_out_value, type, &error_message);
if (!valid) {
DCHECK(error_message != nullptr);
thrower.TypeError("%s", error_message);
return false;
}
if (!internal::v8_flags.wasm_gc_js_interop) {
internal::wasm::TryUnpackObjectWrapper(i_isolate, in_out_value);
}
return true;
}
} // namespace
// WebAssembly.Global
@ -1573,6 +1599,7 @@ void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (!value->IsNull() && !value->IsString()) {
thrower.TypeError(
"The value of stringref globals must be null or a string");
break;
}
global_obj->SetStringRef(Utils::OpenHandle(*value));
@ -1584,7 +1611,14 @@ void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
case internal::wasm::HeapType::kI31:
case internal::wasm::HeapType::kData:
case internal::wasm::HeapType::kArray:
case internal::wasm::HeapType::kAny:
case internal::wasm::HeapType::kAny: {
internal::Handle<internal::Object> value_handle =
Utils::OpenHandle(*value);
if (checkAndUnpackAnyRef(i_isolate, value_handle, type, thrower)) {
global_obj->SetAnyRef(value_handle);
}
break;
}
case internal::wasm::HeapType::kStringViewWtf8:
case internal::wasm::HeapType::kStringViewWtf16:
case internal::wasm::HeapType::kStringViewIter:
@ -2683,8 +2717,22 @@ void WebAssemblyGlobalGetValueCommon(
case i::wasm::HeapType::kI31:
case i::wasm::HeapType::kData:
case i::wasm::HeapType::kArray:
case i::wasm::HeapType::kAny:
case i::wasm::HeapType::kEq:
case i::wasm::HeapType::kAny: {
i::Handle<i::Object> result = receiver->GetRef();
if (!i::v8_flags.wasm_gc_js_interop && result->IsWasmObject()) {
// Transform wasm object into JS-compliant representation.
i::Handle<i::JSObject> wrapper =
i_isolate->factory()->NewJSObject(i_isolate->object_function());
i::JSObject::AddProperty(
i_isolate, wrapper,
i_isolate->factory()->wasm_wrapped_object_symbol(), result,
i::NONE);
result = wrapper;
}
return_value.Set(Utils::ToLocal(result));
break;
}
default:
// TODO(7748): Implement these.
UNIMPLEMENTED();
@ -2805,8 +2853,16 @@ void WebAssemblyGlobalSetValue(
case i::wasm::HeapType::kI31:
case i::wasm::HeapType::kData:
case i::wasm::HeapType::kArray:
case i::wasm::HeapType::kAny:
case i::wasm::HeapType::kEq:
case i::wasm::HeapType::kAny: {
internal::Handle<internal::Object> value_handle =
Utils::OpenHandle(*args[0]);
if (checkAndUnpackAnyRef(i_isolate, value_handle, receiver->type(),
thrower)) {
receiver->SetAnyRef(value_handle);
}
break;
}
default:
// TODO(7748): Implement these.
UNIMPLEMENTED();

View File

@ -171,6 +171,15 @@ void WasmGlobalObject::SetExternRef(Handle<Object> value) {
tagged_buffer().set(offset(), *value);
}
void WasmGlobalObject::SetAnyRef(Handle<Object> value) {
DCHECK(type().is_reference_to(wasm::HeapType::kAny) ||
type().is_reference_to(wasm::HeapType::kEq) ||
type().is_reference_to(wasm::HeapType::kData) ||
type().is_reference_to(wasm::HeapType::kArray) ||
type().is_reference_to(wasm::HeapType::kI31));
tagged_buffer().set(offset(), *value);
}
bool WasmGlobalObject::SetFuncRef(Isolate* isolate, Handle<Object> value) {
DCHECK_EQ(type(), wasm::kWasmFuncRef);
if (value->IsNull() ||

View File

@ -315,6 +315,7 @@ class WasmGlobalObject
inline void SetF32(float value);
inline void SetF64(double value);
inline void SetExternRef(Handle<Object> value);
inline void SetAnyRef(Handle<Object> value);
inline bool SetFuncRef(Isolate* isolate, Handle<Object> value);
inline void SetStringRef(Handle<Object> value);

View File

@ -283,3 +283,230 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
assertEquals(null, instance.exports.get_string2());
assertEquals("Content of any", instance.exports.get_any());
})();
(function TestAnyRefGlobalFromJS() {
print(arguments.callee.name);
let anyref_global = new WebAssembly.Global(
{ value: "anyref", mutable: true }, "initial value");
assertEquals("initial value", anyref_global.value);
let builder = new WasmModuleBuilder();
builder.addImportedGlobal("imports", "anyref_global", kWasmAnyRef, true);
let struct_type = builder.addStruct([makeField(kWasmI32, false)]);
let array_type = builder.addArray(kWasmI32);
builder.addFunction("get_extern", makeSig([], [kWasmExternRef]))
.addBody([kExprGlobalGet, 0, kGCPrefix, kExprExternExternalize])
.exportFunc();
builder.addFunction("get_struct_val", makeSig([], [kWasmI32]))
.addBody([
kExprGlobalGet, 0,
kGCPrefix, kExprRefAsData,
kGCPrefix, kExprRefCast, struct_type,
kGCPrefix, kExprStructGet, struct_type, 0,
])
.exportFunc();
builder.addFunction("get_array_val", makeSig([], [kWasmI32]))
.addBody([
kExprGlobalGet, 0,
kGCPrefix, kExprRefAsData,
kGCPrefix, kExprRefCast, array_type,
kExprI32Const, 0,
kGCPrefix, kExprArrayGet, array_type,
])
.exportFunc();
builder.addFunction("create_struct", makeSig([kWasmI32], [kWasmExternRef]))
.addBody([
kExprLocalGet, 0,
kGCPrefix, kExprStructNew, struct_type,
kGCPrefix, kExprExternExternalize])
.exportFunc();
builder.addFunction("create_array", makeSig([kWasmI32], [kWasmExternRef]))
.addBody([
kExprLocalGet, 0,
kGCPrefix, kExprArrayNewFixed, array_type, 1,
kGCPrefix, kExprExternExternalize])
.exportFunc();
let instance = builder.instantiate({imports : {anyref_global}});
let wasm = instance.exports;
anyref_global.value = "Set anyref from string";
assertEquals("Set anyref from string", anyref_global.value);
assertEquals("Set anyref from string", wasm.get_extern());
anyref_global.value = wasm.create_struct(42);
assertEquals(42, wasm.get_struct_val());
anyref_global.value = wasm.create_array(43);
assertEquals(43, wasm.get_array_val());
anyref_global.value = null;
assertEquals(null, anyref_global.value);
assertEquals(null, wasm.get_extern());
anyref_global.value = 12345;
assertEquals(12345, wasm.get_extern());
assertThrows(() => anyref_global.value = {}, TypeError);
assertThrows(() => anyref_global.value = undefined, TypeError);
})();
(function TestEqRefGlobalFromJS() {
print(arguments.callee.name);
let eqref_global = new WebAssembly.Global(
{ value: "eqref", mutable: true }, null);
assertEquals(null, eqref_global.value);
let builder = new WasmModuleBuilder();
builder.addImportedGlobal("imports", "eqref_global", kWasmEqRef, true);
let struct_type = builder.addStruct([makeField(kWasmI32, false)]);
let array_type = builder.addArray(kWasmI32);
builder.addFunction("get_extern", makeSig([], [kWasmExternRef]))
.addBody([kExprGlobalGet, 0, kGCPrefix, kExprExternExternalize])
.exportFunc();
builder.addFunction("get_struct_val", makeSig([], [kWasmI32]))
.addBody([
kExprGlobalGet, 0,
kGCPrefix, kExprRefAsData,
kGCPrefix, kExprRefCast, struct_type,
kGCPrefix, kExprStructGet, struct_type, 0,
])
.exportFunc();
builder.addFunction("get_array_val", makeSig([], [kWasmI32]))
.addBody([
kExprGlobalGet, 0,
kGCPrefix, kExprRefAsData,
kGCPrefix, kExprRefCast, array_type,
kExprI32Const, 0,
kGCPrefix, kExprArrayGet, array_type,
])
.exportFunc();
builder.addFunction("create_struct", makeSig([kWasmI32], [kWasmExternRef]))
.addBody([
kExprLocalGet, 0,
kGCPrefix, kExprStructNew, struct_type,
kGCPrefix, kExprExternExternalize])
.exportFunc();
builder.addFunction("create_array", makeSig([kWasmI32], [kWasmExternRef]))
.addBody([
kExprLocalGet, 0,
kGCPrefix, kExprArrayNewFixed, array_type, 1,
kGCPrefix, kExprExternExternalize])
.exportFunc();
let instance = builder.instantiate({imports : {eqref_global}});
let wasm = instance.exports;
eqref_global.value = wasm.create_struct(42);
assertEquals(42, wasm.get_struct_val());
eqref_global.value = wasm.create_array(43);
assertEquals(43, wasm.get_array_val());
eqref_global.value = null;
assertEquals(null, eqref_global.value);
assertEquals(null, wasm.get_extern());
eqref_global.value = 12345;
assertEquals(12345, wasm.get_extern());
assertThrows(() => eqref_global.value = {}, TypeError);
assertThrows(() => eqref_global.value = undefined, TypeError);
assertThrows(() => eqref_global.value = "string", TypeError);
})();
(function TestDataRefGlobalFromJS() {
print(arguments.callee.name);
let dataref_global = new WebAssembly.Global(
{ value: "dataref", mutable: true }, null);
assertNull(dataref_global.value);
let builder = new WasmModuleBuilder();
builder.addImportedGlobal("imports", "dataref_global", kWasmDataRef, true);
let struct_type = builder.addStruct([makeField(kWasmI32, false)]);
let array_type = builder.addArray(kWasmI32);
builder.addFunction("get_struct_val", makeSig([], [kWasmI32]))
.addBody([
kExprGlobalGet, 0,
kGCPrefix, kExprRefAsData,
kGCPrefix, kExprRefCast, struct_type,
kGCPrefix, kExprStructGet, struct_type, 0,
])
.exportFunc();
builder.addFunction("get_array_val", makeSig([], [kWasmI32]))
.addBody([
kExprGlobalGet, 0,
kGCPrefix, kExprRefAsData,
kGCPrefix, kExprRefCast, array_type,
kExprI32Const, 0,
kGCPrefix, kExprArrayGet, array_type,
])
.exportFunc();
builder.addFunction("create_struct", makeSig([kWasmI32], [kWasmExternRef]))
.addBody([
kExprLocalGet, 0,
kGCPrefix, kExprStructNew, struct_type,
kGCPrefix, kExprExternExternalize])
.exportFunc();
builder.addFunction("create_array", makeSig([kWasmI32], [kWasmExternRef]))
.addBody([
kExprLocalGet, 0,
kGCPrefix, kExprArrayNewFixed, array_type, 1,
kGCPrefix, kExprExternExternalize])
.exportFunc();
let instance = builder.instantiate({imports : {dataref_global}});
let wasm = instance.exports;
dataref_global.value = wasm.create_struct(42);
assertEquals(42, wasm.get_struct_val());
dataref_global.value = wasm.create_array(43);
assertEquals(43, wasm.get_array_val());
dataref_global.value = null;
assertEquals(null, dataref_global.value);
assertThrows(() => dataref_global.value = undefined, TypeError);
assertThrows(() => dataref_global.value = "string", TypeError);
})();
(function TestArrayRefGlobalFromJS() {
print(arguments.callee.name);
let arrayref_global = new WebAssembly.Global(
{ value: "arrayref", mutable: true }, null);
assertNull(arrayref_global.value);
let builder = new WasmModuleBuilder();
builder.addImportedGlobal("imports", "arrayref_global", kWasmArrayRef, true);
let struct_type = builder.addStruct([makeField(kWasmI32, false)]);
let array_type = builder.addArray(kWasmI32);
builder.addFunction("get_array_val", makeSig([], [kWasmI32]))
.addBody([
kExprGlobalGet, 0,
kGCPrefix, kExprRefAsData,
kGCPrefix, kExprRefCast, array_type,
kExprI32Const, 0,
kGCPrefix, kExprArrayGet, array_type,
])
.exportFunc();
builder.addFunction("create_struct", makeSig([kWasmI32], [kWasmExternRef]))
.addBody([
kExprLocalGet, 0,
kGCPrefix, kExprStructNew, struct_type,
kGCPrefix, kExprExternExternalize])
.exportFunc();
builder.addFunction("create_array", makeSig([kWasmI32], [kWasmExternRef]))
.addBody([
kExprLocalGet, 0,
kGCPrefix, kExprArrayNewFixed, array_type, 1,
kGCPrefix, kExprExternExternalize])
.exportFunc();
let instance = builder.instantiate({imports : {arrayref_global}});
let wasm = instance.exports;
arrayref_global.value = wasm.create_array(43);
assertEquals(43, wasm.get_array_val());
arrayref_global.value = null;
assertEquals(null, arrayref_global.value);
assertThrows(() => arrayref_global.value = undefined, TypeError);
assertThrows(() => arrayref_global.value = "string", TypeError);
assertThrows(() => arrayref_global.value = wasm.create_struct(1), TypeError);
})();