Commit 1a3edfb7 authored by Andy Wingo's avatar Andy Wingo Committed by V8 LUCI CQ

[stringref] Add support for stringref globals, with JS interface

Bug: v8:12868
Change-Id: I955155db468b2ecd86fa6c5a73c616b0e4c66446
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3644949Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Andy Wingo <wingo@igalia.com>
Cr-Commit-Position: refs/heads/main@{#80584}
parent eb56775a
...@@ -1346,6 +1346,9 @@ bool GetValueType(Isolate* isolate, MaybeLocal<Value> maybe, ...@@ -1346,6 +1346,9 @@ bool GetValueType(Isolate* isolate, MaybeLocal<Value> maybe,
} else if (enabled_features.has_gc() && } else if (enabled_features.has_gc() &&
string->StringEquals(v8_str(isolate, "eqref"))) { string->StringEquals(v8_str(isolate, "eqref"))) {
*type = i::wasm::kWasmEqRef; *type = i::wasm::kWasmEqRef;
} else if (enabled_features.has_stringref() &&
string->StringEquals(v8_str(isolate, "stringref"))) {
*type = i::wasm::kWasmStringRef;
} else { } else {
// Unrecognized type. // Unrecognized type.
*type = i::wasm::kWasmVoid; *type = i::wasm::kWasmVoid;
...@@ -1523,13 +1526,28 @@ void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) { ...@@ -1523,13 +1526,28 @@ void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
} }
break; break;
} }
case i::wasm::HeapType::kString: {
if (args.Length() < 2) {
thrower.TypeError(
"Missing initial value when creating stringref global");
break;
}
DCHECK_EQ(type.nullability(), i::wasm::kNullable);
if (!value->IsNull() && !value->IsString()) {
thrower.TypeError(
"The value of stringref globals must be null or a string");
}
global_obj->SetStringRef(Utils::OpenHandle(*value));
break;
}
case internal::wasm::HeapType::kBottom: case internal::wasm::HeapType::kBottom:
UNREACHABLE(); UNREACHABLE();
case i::wasm::HeapType::kEq: case i::wasm::HeapType::kEq:
case internal::wasm::HeapType::kI31: case internal::wasm::HeapType::kI31:
case internal::wasm::HeapType::kData: case internal::wasm::HeapType::kData:
case internal::wasm::HeapType::kArray: case internal::wasm::HeapType::kArray:
case internal::wasm::HeapType::kString:
case internal::wasm::HeapType::kStringViewWtf8: case internal::wasm::HeapType::kStringViewWtf8:
case internal::wasm::HeapType::kStringViewWtf16: case internal::wasm::HeapType::kStringViewWtf16:
case internal::wasm::HeapType::kStringViewIter: case internal::wasm::HeapType::kStringViewIter:
...@@ -2434,6 +2452,7 @@ void WebAssemblyGlobalGetValueCommon( ...@@ -2434,6 +2452,7 @@ void WebAssemblyGlobalGetValueCommon(
case i::wasm::kOptRef: case i::wasm::kOptRef:
switch (receiver->type().heap_representation()) { switch (receiver->type().heap_representation()) {
case i::wasm::HeapType::kAny: case i::wasm::HeapType::kAny:
case i::wasm::HeapType::kString:
return_value.Set(Utils::ToLocal(receiver->GetRef())); return_value.Set(Utils::ToLocal(receiver->GetRef()));
break; break;
case i::wasm::HeapType::kFunc: { case i::wasm::HeapType::kFunc: {
...@@ -2446,16 +2465,21 @@ void WebAssemblyGlobalGetValueCommon( ...@@ -2446,16 +2465,21 @@ void WebAssemblyGlobalGetValueCommon(
return_value.Set(Utils::ToLocal(result)); return_value.Set(Utils::ToLocal(result));
break; break;
} }
case i::wasm::HeapType::kStringViewWtf8:
thrower.TypeError("stringview_wtf8 has no JS representation");
break;
case i::wasm::HeapType::kStringViewWtf16:
thrower.TypeError("stringview_wtf16 has no JS representation");
break;
case i::wasm::HeapType::kStringViewIter:
thrower.TypeError("stringview_iter has no JS representation");
break;
case i::wasm::HeapType::kBottom: case i::wasm::HeapType::kBottom:
UNREACHABLE(); UNREACHABLE();
case i::wasm::HeapType::kI31: case i::wasm::HeapType::kI31:
case i::wasm::HeapType::kData: case i::wasm::HeapType::kData:
case i::wasm::HeapType::kArray: case i::wasm::HeapType::kArray:
case i::wasm::HeapType::kEq: case i::wasm::HeapType::kEq:
case i::wasm::HeapType::kString:
case i::wasm::HeapType::kStringViewWtf8:
case i::wasm::HeapType::kStringViewWtf16:
case i::wasm::HeapType::kStringViewIter:
default: default:
// TODO(7748): Implement these. // TODO(7748): Implement these.
UNIMPLEMENTED(); UNIMPLEMENTED();
...@@ -2543,16 +2567,40 @@ void WebAssemblyGlobalSetValue( ...@@ -2543,16 +2567,40 @@ void WebAssemblyGlobalSetValue(
} }
break; break;
} }
case i::wasm::HeapType::kString: {
if (!args[0]->IsString()) {
if (args[0]->IsNull()) {
if (receiver->type().nullability() == i::wasm::kNonNullable) {
thrower.TypeError(
"Can't set non-nullable stringref global to null");
break;
}
} else {
thrower.TypeError(
receiver->type().nullability() == i::wasm::kNonNullable
? "Non-nullable stringref global can only hold a string"
: "Stringref global can only hold null or a string");
break;
}
}
receiver->SetStringRef(Utils::OpenHandle(*args[0]));
break;
}
case i::wasm::HeapType::kStringViewWtf8:
thrower.TypeError("stringview_wtf8 has no JS representation");
break;
case i::wasm::HeapType::kStringViewWtf16:
thrower.TypeError("stringview_wtf16 has no JS representation");
break;
case i::wasm::HeapType::kStringViewIter:
thrower.TypeError("stringview_iter has no JS representation");
break;
case i::wasm::HeapType::kBottom: case i::wasm::HeapType::kBottom:
UNREACHABLE(); UNREACHABLE();
case i::wasm::HeapType::kI31: case i::wasm::HeapType::kI31:
case i::wasm::HeapType::kData: case i::wasm::HeapType::kData:
case i::wasm::HeapType::kArray: case i::wasm::HeapType::kArray:
case i::wasm::HeapType::kEq: case i::wasm::HeapType::kEq:
case i::wasm::HeapType::kString:
case i::wasm::HeapType::kStringViewWtf8:
case i::wasm::HeapType::kStringViewWtf16:
case i::wasm::HeapType::kStringViewIter:
default: default:
// TODO(7748): Implement these. // TODO(7748): Implement these.
UNIMPLEMENTED(); UNIMPLEMENTED();
......
...@@ -145,7 +145,7 @@ double WasmGlobalObject::GetF64() { ...@@ -145,7 +145,7 @@ double WasmGlobalObject::GetF64() {
} }
Handle<Object> WasmGlobalObject::GetRef() { Handle<Object> WasmGlobalObject::GetRef() {
// We use this getter for externref and funcref. // We use this getter for externref, funcref, and stringref.
DCHECK(type().is_reference()); DCHECK(type().is_reference());
return handle(tagged_buffer().get(offset()), GetIsolate()); return handle(tagged_buffer().get(offset()), GetIsolate());
} }
...@@ -181,6 +181,12 @@ bool WasmGlobalObject::SetFuncRef(Isolate* isolate, Handle<Object> value) { ...@@ -181,6 +181,12 @@ bool WasmGlobalObject::SetFuncRef(Isolate* isolate, Handle<Object> value) {
return false; return false;
} }
void WasmGlobalObject::SetStringRef(Handle<Object> value) {
DCHECK_EQ(type(), wasm::kWasmStringRef);
DCHECK(value->IsNull() || value->IsString());
tagged_buffer().set(offset(), *value);
}
// WasmInstanceObject // WasmInstanceObject
SANDBOXED_POINTER_ACCESSORS(WasmInstanceObject, memory_start, byte*, SANDBOXED_POINTER_ACCESSORS(WasmInstanceObject, memory_start, byte*,
kMemoryStartOffset) kMemoryStartOffset)
......
...@@ -303,6 +303,7 @@ class WasmGlobalObject ...@@ -303,6 +303,7 @@ class WasmGlobalObject
inline void SetF64(double value); inline void SetF64(double value);
inline void SetExternRef(Handle<Object> value); inline void SetExternRef(Handle<Object> value);
inline bool SetFuncRef(Isolate* isolate, Handle<Object> value); inline bool SetFuncRef(Isolate* isolate, Handle<Object> value);
inline void SetStringRef(Handle<Object> value);
private: private:
// This function returns the address of the global's data in the // This function returns the address of the global's data in the
......
...@@ -94,6 +94,125 @@ function assertInvalid(fn, message) { ...@@ -94,6 +94,125 @@ function assertInvalid(fn, message) {
TypeError, "type incompatibility when transforming from/to JS"); TypeError, "type incompatibility when transforming from/to JS");
})(); })();
// TODO(wingo): Test stringref-valued globals (defined and imported). (function TestDefinedGlobals() {
let kSig_w_v = makeSig([], [kWasmStringRef]);
let kSig_v_w = makeSig([kWasmStringRef], []);
let kSig_x_v = makeSig([], [kWasmStringViewWtf8]);
let kSig_y_v = makeSig([], [kWasmStringViewWtf16]);
let kSig_z_v = makeSig([], [kWasmStringViewIter]);
let builder = new WasmModuleBuilder();
builder.addGlobal(kWasmStringRef, true).exportAs('w');
builder.addGlobal(kWasmStringViewWtf8, true).exportAs('x');
builder.addGlobal(kWasmStringViewWtf16, true).exportAs('y');
builder.addGlobal(kWasmStringViewIter, true).exportAs('z');
builder.addFunction("get_stringref", kSig_w_v)
.exportFunc()
.addBody([
kExprGlobalGet, 0,
]);
builder.addFunction("set_stringref", kSig_v_w)
.exportFunc()
.addBody([
kExprLocalGet, 0,
kExprGlobalSet, 0
]);
builder.addFunction("get_stringview_wtf8", kSig_x_v)
.exportFunc()
.addBody([
kExprGlobalGet, 1,
]);
builder.addFunction("get_stringview_wtf16", kSig_y_v)
.exportFunc()
.addBody([
kExprGlobalGet, 2,
]);
builder.addFunction("get_stringview_iter", kSig_z_v)
.exportFunc()
.addBody([
kExprGlobalGet, 3,
]);
let instance = builder.instantiate()
assertEquals(null, instance.exports.get_stringref());
instance.exports.set_stringref('foo');
assertEquals('foo', instance.exports.get_stringref());
assertEquals('foo', instance.exports.w.value);
instance.exports.w.value = 'bar';
assertEquals('bar', instance.exports.w.value);
assertEquals('bar', instance.exports.get_stringref());
assertThrows(()=>instance.exports.get_stringview_wtf8(),
TypeError, "type incompatibility when transforming from/to JS");
assertThrows(()=>instance.exports.get_stringview_wtf16(),
TypeError, "type incompatibility when transforming from/to JS");
assertThrows(()=>instance.exports.get_stringview_iter(),
TypeError, "type incompatibility when transforming from/to JS");
let unsupportedGlobalMessage = (mode, type) => {
return `${mode} WebAssembly.Global.value: ${type} has no JS representation`;
}
for (let [global, type] of [[instance.exports.x, 'stringview_wtf8'],
[instance.exports.y, 'stringview_wtf16'],
[instance.exports.z, 'stringview_iter']]) {
assertThrows(()=>global.value, TypeError,
unsupportedGlobalMessage('get', type));
assertThrows(()=>{global.value = null}, TypeError,
unsupportedGlobalMessage('set', type));
}
})();
(function TestImportedGlobals() {
for (let type of ['stringview_wtf8', 'stringview_wtf16',
'stringview_iter']) {
let msg = "WebAssembly.Global(): Descriptor property 'value' must be" +
" a WebAssembly type";
assertThrows(()=>new WebAssembly.Global({ mutable: true, value: type }),
TypeError, msg);
assertThrows(()=>new WebAssembly.Global({ mutable: true, value: type },
null),
TypeError, msg);
}
assertThrows(()=>new WebAssembly.Global({ value: 'stringref' }),
TypeError,
"WebAssembly.Global(): " +
"Missing initial value when creating stringref global");
let kSig_w_v = makeSig([], [kWasmStringRef]);
let kSig_v_w = makeSig([kWasmStringRef], []);
let builder = new WasmModuleBuilder();
builder.addImportedGlobal('env', 'w', kWasmStringRef, true)
builder.addFunction("get_stringref", kSig_w_v)
.exportFunc()
.addBody([
kExprGlobalGet, 0,
]);
builder.addFunction("set_stringref", kSig_v_w)
.exportFunc()
.addBody([
kExprLocalGet, 0,
kExprGlobalSet, 0
]);
let w = new WebAssembly.Global({ mutable: true, value: 'stringref' },
null);
let instance = builder.instantiate({env: {w: w}})
assertEquals(null, instance.exports.get_stringref());
instance.exports.set_stringref('foo');
assertEquals('foo', instance.exports.get_stringref());
assertEquals('foo', w.value);
w.value = 'bar';
assertEquals('bar', w.value);
assertEquals('bar', instance.exports.get_stringref());
})();
// TODO(wingo): Test calls from wasm to JS. // TODO(wingo): Test calls from wasm to JS.
// TODO(wingo): Test stringrefs in tables. // TODO(wingo): Test stringrefs in tables.
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