[stringref] Add support for stringrefs in tables
Bug: v8:12868 Change-Id: I42ef3e15b2a7fd2ef157aa0e657ddf98973e8d79 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3644956 Reviewed-by: Jakob Kummerow <jkummerow@chromium.org> Commit-Queue: Andy Wingo <wingo@igalia.com> Cr-Commit-Position: refs/heads/main@{#80605}
This commit is contained in:
parent
9137ecb400
commit
010de10eb8
@ -460,13 +460,6 @@ RUNTIME_FUNCTION(Runtime_WasmFunctionTableSet) {
|
||||
DCHECK_LT(table_index, instance->tables().length());
|
||||
auto table = handle(
|
||||
WasmTableObject::cast(instance->tables().get(table_index)), isolate);
|
||||
// We only use the runtime call for function references.
|
||||
DCHECK(
|
||||
table->instance().IsUndefined()
|
||||
? table->type() == wasm::kWasmFuncRef
|
||||
: IsSubtypeOf(table->type(), wasm::kWasmFuncRef,
|
||||
WasmInstanceObject::cast(table->instance()).module()));
|
||||
|
||||
if (!WasmTableObject::IsInBounds(isolate, table, entry_index)) {
|
||||
return ThrowWasmError(isolate, MessageTemplate::kWasmTrapTableOutOfBounds);
|
||||
}
|
||||
|
@ -2005,6 +2005,7 @@ auto Table::set(size_t index, const Ref* ref) -> bool {
|
||||
i::HandleScope handle_scope(isolate);
|
||||
i::Handle<i::Object> obj = WasmRefToV8(isolate, ref);
|
||||
// TODO(7748): Generalize the condition if other table types are allowed.
|
||||
// TODO(12868): Enforce type restrictions for stringref tables.
|
||||
if ((table->type() == i::wasm::kWasmFuncRef || table->type().has_index()) &&
|
||||
!obj->IsNull()) {
|
||||
obj = i::WasmInternalFunction::FromExternal(obj, isolate).ToHandleChecked();
|
||||
|
@ -1164,6 +1164,9 @@ void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
} else if (string->StringEquals(v8_str(isolate, "externref"))) {
|
||||
// externref is known as anyref as of wasm-gc.
|
||||
type = i::wasm::kWasmAnyRef;
|
||||
} else if (enabled_features.has_stringref() &&
|
||||
string->StringEquals(v8_str(isolate, "stringref"))) {
|
||||
type = i::wasm::kWasmStringRef;
|
||||
} else {
|
||||
thrower.TypeError(
|
||||
"Descriptor property 'element' must be a WebAssembly reference type");
|
||||
@ -1222,6 +1225,24 @@ void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
for (uint32_t index = 0; index < static_cast<uint32_t>(initial); ++index) {
|
||||
i::WasmTableObject::Set(i_isolate, table_obj, index, element);
|
||||
}
|
||||
} else if (initial > 0) {
|
||||
switch (table_obj->type().heap_representation()) {
|
||||
case i::wasm::HeapType::kString:
|
||||
thrower.TypeError(
|
||||
"Missing initial value when creating stringref table");
|
||||
return;
|
||||
case i::wasm::HeapType::kStringViewWtf8:
|
||||
thrower.TypeError("stringview_wtf8 has no JS representation");
|
||||
return;
|
||||
case i::wasm::HeapType::kStringViewWtf16:
|
||||
thrower.TypeError("stringview_wtf16 has no JS representation");
|
||||
return;
|
||||
case i::wasm::HeapType::kStringViewIter:
|
||||
thrower.TypeError("stringview_iter has no JS representation");
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
|
||||
return_value.Set(Utils::ToLocal(i::Handle<i::JSObject>::cast(table_obj)));
|
||||
@ -2072,6 +2093,19 @@ void WebAssemblyTableGet(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (receiver->type() == i::wasm::kWasmStringViewWtf8) {
|
||||
thrower.TypeError("stringview_wtf8 has no JS representation");
|
||||
return;
|
||||
}
|
||||
if (receiver->type() == i::wasm::kWasmStringViewWtf16) {
|
||||
thrower.TypeError("stringview_wtf16 has no JS representation");
|
||||
return;
|
||||
}
|
||||
if (receiver->type() == i::wasm::kWasmStringViewIter) {
|
||||
thrower.TypeError("stringview_iter has no JS representation");
|
||||
return;
|
||||
}
|
||||
|
||||
i::Handle<i::Object> result =
|
||||
i::WasmTableObject::Get(i_isolate, receiver, index);
|
||||
if (result->IsWasmInternalFunction()) {
|
||||
|
@ -618,6 +618,10 @@ struct WasmTable {
|
||||
if (!type.is_object_reference()) return false;
|
||||
HeapType heap_type = type.heap_type();
|
||||
return heap_type == HeapType::kFunc || heap_type == HeapType::kAny ||
|
||||
heap_type == HeapType::kString ||
|
||||
heap_type == HeapType::kStringViewWtf8 ||
|
||||
heap_type == HeapType::kStringViewWtf16 ||
|
||||
heap_type == HeapType::kStringViewIter ||
|
||||
(module != nullptr && heap_type.is_index() &&
|
||||
module->has_signature(heap_type.ref_index()));
|
||||
}
|
||||
|
@ -381,6 +381,10 @@ void WasmTableObject::Set(Isolate* isolate, Handle<WasmTableObject> table,
|
||||
|
||||
switch (table->type().heap_representation()) {
|
||||
case wasm::HeapType::kAny:
|
||||
case wasm::HeapType::kString:
|
||||
case wasm::HeapType::kStringViewWtf8:
|
||||
case wasm::HeapType::kStringViewWtf16:
|
||||
case wasm::HeapType::kStringViewIter:
|
||||
entries->set(entry_index, *entry);
|
||||
return;
|
||||
case wasm::HeapType::kFunc:
|
||||
@ -390,10 +394,6 @@ void WasmTableObject::Set(Isolate* isolate, Handle<WasmTableObject> table,
|
||||
case wasm::HeapType::kData:
|
||||
case wasm::HeapType::kArray:
|
||||
case wasm::HeapType::kI31:
|
||||
case wasm::HeapType::kString:
|
||||
case wasm::HeapType::kStringViewWtf8:
|
||||
case wasm::HeapType::kStringViewWtf16:
|
||||
case wasm::HeapType::kStringViewIter:
|
||||
// TODO(7748): Implement once we have struct/arrays/i31ref/string tables.
|
||||
UNREACHABLE();
|
||||
case wasm::HeapType::kBottom:
|
||||
@ -427,6 +427,10 @@ Handle<Object> WasmTableObject::Get(Isolate* isolate,
|
||||
|
||||
switch (table->type().heap_representation()) {
|
||||
case wasm::HeapType::kAny:
|
||||
case wasm::HeapType::kString:
|
||||
case wasm::HeapType::kStringViewWtf8:
|
||||
case wasm::HeapType::kStringViewWtf16:
|
||||
case wasm::HeapType::kStringViewIter:
|
||||
return entry;
|
||||
case wasm::HeapType::kFunc:
|
||||
if (entry->IsWasmInternalFunction()) return entry;
|
||||
@ -2268,7 +2272,22 @@ bool TypecheckJSObject(Isolate* isolate, const WasmModule* module,
|
||||
DCHECK(expected.is_reference());
|
||||
switch (expected.kind()) {
|
||||
case kOptRef:
|
||||
if (value->IsNull(isolate)) return true;
|
||||
if (value->IsNull(isolate)) {
|
||||
HeapType::Representation repr = expected.heap_representation();
|
||||
switch (repr) {
|
||||
case HeapType::kStringViewWtf8:
|
||||
*error_message = "stringview_wtf8 has no JS representation";
|
||||
return false;
|
||||
case HeapType::kStringViewWtf16:
|
||||
*error_message = "stringview_wtf16 has no JS representation";
|
||||
return false;
|
||||
case HeapType::kStringViewIter:
|
||||
*error_message = "stringview_iter has no JS representation";
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
V8_FALLTHROUGH;
|
||||
case kRef: {
|
||||
HeapType::Representation repr = expected.heap_representation();
|
||||
@ -2316,6 +2335,19 @@ bool TypecheckJSObject(Isolate* isolate, const WasmModule* module,
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case HeapType::kString:
|
||||
if (value->IsString()) return true;
|
||||
*error_message = "wrong type (expected a string)";
|
||||
return false;
|
||||
case HeapType::kStringViewWtf8:
|
||||
*error_message = "stringview_wtf8 has no JS representation";
|
||||
return false;
|
||||
case HeapType::kStringViewWtf16:
|
||||
*error_message = "stringview_wtf16 has no JS representation";
|
||||
return false;
|
||||
case HeapType::kStringViewIter:
|
||||
*error_message = "stringview_iter has no JS representation";
|
||||
return false;
|
||||
default:
|
||||
if (module == nullptr) {
|
||||
*error_message =
|
||||
|
@ -232,4 +232,129 @@ function assertInvalid(fn, message) {
|
||||
assertEquals('bar', instance.exports.get_stringref());
|
||||
})();
|
||||
|
||||
// TODO(wingo): Test stringrefs in tables.
|
||||
(function TestDefinedTables() {
|
||||
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.addTable(kWasmStringRef, 1).exportAs('w');
|
||||
builder.addTable(kWasmStringViewWtf8, 1).exportAs('x');
|
||||
builder.addTable(kWasmStringViewWtf16, 1).exportAs('y');
|
||||
builder.addTable(kWasmStringViewIter, 1).exportAs('z');
|
||||
|
||||
builder.addFunction("get_stringref", kSig_w_v)
|
||||
.exportFunc()
|
||||
.addBody([
|
||||
kExprI32Const, 0,
|
||||
kExprTableGet, 0,
|
||||
]);
|
||||
builder.addFunction("set_stringref", kSig_v_w)
|
||||
.exportFunc()
|
||||
.addBody([
|
||||
kExprI32Const, 0,
|
||||
kExprLocalGet, 0,
|
||||
kExprTableSet, 0,
|
||||
]);
|
||||
|
||||
builder.addFunction("get_stringview_wtf8", kSig_x_v)
|
||||
.exportFunc()
|
||||
.addBody([
|
||||
kExprI32Const, 0,
|
||||
kExprTableGet, 1,
|
||||
]);
|
||||
builder.addFunction("get_stringview_wtf16", kSig_y_v)
|
||||
.exportFunc()
|
||||
.addBody([
|
||||
kExprI32Const, 0,
|
||||
kExprTableGet, 2,
|
||||
]);
|
||||
builder.addFunction("get_stringview_iter", kSig_z_v)
|
||||
.exportFunc()
|
||||
.addBody([
|
||||
kExprI32Const, 0,
|
||||
kExprTableGet, 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.get(0));
|
||||
instance.exports.w.set(0, 'bar');
|
||||
assertEquals('bar', instance.exports.w.get(0));
|
||||
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");
|
||||
|
||||
for (let [table, type] of [[instance.exports.x, 'stringview_wtf8'],
|
||||
[instance.exports.y, 'stringview_wtf16'],
|
||||
[instance.exports.z, 'stringview_iter']]) {
|
||||
let unsupportedGetMessage =
|
||||
`WebAssembly.Table.get(): ${type} has no JS representation`;
|
||||
let unsupportedSetMessage =
|
||||
'WebAssembly.Table.set(): Argument 1 is invalid for table of type '
|
||||
+ `${type}ref`;
|
||||
assertThrows(()=>table.get(0), TypeError, unsupportedGetMessage);
|
||||
assertThrows(()=>{table.set(0, null);}, TypeError, unsupportedSetMessage);
|
||||
}
|
||||
})();
|
||||
|
||||
(function TestImportedTables() {
|
||||
for (let type of ['stringview_wtf8', 'stringview_wtf16',
|
||||
'stringview_iter']) {
|
||||
let msg = "WebAssembly.Table(): Descriptor property 'element' must be" +
|
||||
" a WebAssembly reference type";
|
||||
|
||||
assertThrows(()=>new WebAssembly.Table({ element: type, initial: 1 }),
|
||||
TypeError, msg);
|
||||
assertThrows(()=>new WebAssembly.Table({ element: type, initial: 1 },
|
||||
null),
|
||||
TypeError, msg);
|
||||
}
|
||||
|
||||
assertThrows(()=>new WebAssembly.Table({ element: 'stringref', initial: 1 }),
|
||||
TypeError,
|
||||
"WebAssembly.Table(): " +
|
||||
"Missing initial value when creating stringref table");
|
||||
|
||||
let kSig_w_v = makeSig([], [kWasmStringRef]);
|
||||
let kSig_v_w = makeSig([kWasmStringRef], []);
|
||||
let builder = new WasmModuleBuilder();
|
||||
builder.addImportedTable('env', 't', 0, undefined, kWasmStringRef);
|
||||
builder.addFunction("get_stringref", kSig_w_v)
|
||||
.exportFunc()
|
||||
.addBody([
|
||||
kExprI32Const, 0,
|
||||
kExprTableGet, 0,
|
||||
]);
|
||||
builder.addFunction("set_stringref", kSig_v_w)
|
||||
.exportFunc()
|
||||
.addBody([
|
||||
kExprI32Const, 0,
|
||||
kExprLocalGet, 0,
|
||||
kExprTableSet, 0,
|
||||
]);
|
||||
|
||||
let t = new WebAssembly.Table({ element: 'stringref', initial: 1 },
|
||||
null);
|
||||
let instance = builder.instantiate({env: {t: t}})
|
||||
|
||||
assertEquals(null, instance.exports.get_stringref());
|
||||
instance.exports.set_stringref('foo');
|
||||
assertEquals('foo', instance.exports.get_stringref());
|
||||
|
||||
assertEquals('foo', t.get(0));
|
||||
t.set(0, 'bar');
|
||||
assertEquals('bar', t.get(0));
|
||||
assertEquals('bar', instance.exports.get_stringref());
|
||||
})();
|
||||
|
@ -35,8 +35,7 @@ for (let [name, code] of [['string', kWasmStringRef],
|
||||
assertValid(b => b.addArray(code, true));
|
||||
assertValid(b => b.addType(makeSig([], [code])));
|
||||
assertValid(b => b.addGlobal(code, true, default_init));
|
||||
// TODO(wingo): Table of strings not yet implemented.
|
||||
// assertValid(b => b.addTable(code, 0));
|
||||
assertValid(b => b.addTable(code, 0));
|
||||
assertValid(b => b.addPassiveElementSegment([default_init], code));
|
||||
assertValid(b => b.addTag(makeSig([code], [])));
|
||||
assertValid(
|
||||
|
Loading…
Reference in New Issue
Block a user