[wasm-gc] Add Table<any|eq|data|array>

This change adds support for new table element types besides the
existing support for func and extern.
The newly supported types are the generic types of the 'any' subtype
hierarchy: any, eq, data and array.
All these table types are also usable and accessible via JavaScript,
causing implicit internalization and externalization of the elements
on Table::get() and Table::set().

Bug: v8:7748
Change-Id: Ie85d8f5e1d70471360dd2fb8a39cd38efaac2c22
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3838729
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Matthias Liedtke <mliedtke@chromium.org>
Cr-Commit-Position: refs/heads/main@{#82643}
This commit is contained in:
Matthias Liedtke 2022-08-22 15:14:10 +00:00 committed by V8 LUCI CQ
parent 40901824d7
commit 3cc931543f
15 changed files with 332 additions and 125 deletions

View File

@ -1021,11 +1021,9 @@ Handle<ArrayList> AddWasmTableObjectInternalProperties(
int length = table->current_length();
Handle<FixedArray> entries = isolate->factory()->NewFixedArray(length);
for (int i = 0; i < length; ++i) {
Handle<Object> entry = WasmTableObject::Get(isolate, table, i);
if (entry->IsWasmInternalFunction()) {
entry = handle(Handle<WasmInternalFunction>::cast(entry)->external(),
isolate);
}
// TODO(mliedtke): Allow inspecting non-JS-exportable elements.
Handle<Object> entry =
WasmTableObject::Get(isolate, table, i, WasmTableObject::kJS);
entries->set(i, *entry);
}
Handle<JSArray> final_entries = isolate->factory()->NewJSArrayWithElements(

View File

@ -451,7 +451,8 @@ RUNTIME_FUNCTION(Runtime_WasmFunctionTableGet) {
return ThrowWasmError(isolate, MessageTemplate::kWasmTrapTableOutOfBounds);
}
return *WasmTableObject::Get(isolate, table, entry_index);
return *WasmTableObject::Get(isolate, table, entry_index,
WasmTableObject::kWasm);
}
RUNTIME_FUNCTION(Runtime_WasmFunctionTableSet) {
@ -475,7 +476,8 @@ RUNTIME_FUNCTION(Runtime_WasmFunctionTableSet) {
if (!WasmTableObject::IsInBounds(isolate, table, entry_index)) {
return ThrowWasmError(isolate, MessageTemplate::kWasmTrapTableOutOfBounds);
}
WasmTableObject::Set(isolate, table, entry_index, element);
WasmTableObject::Set(isolate, table, entry_index, element,
WasmTableObject::kWasm);
return ReadOnlyRoots(isolate).undefined_value();
}

View File

@ -1990,14 +1990,8 @@ auto Table::get(size_t index) const -> own<Ref> {
if (index >= static_cast<size_t>(table->current_length())) return own<Ref>();
i::Isolate* isolate = table->GetIsolate();
i::HandleScope handle_scope(isolate);
i::Handle<i::Object> result =
i::WasmTableObject::Get(isolate, table, static_cast<uint32_t>(index));
// TODO(jkummerow): If we support both JavaScript and the C-API at the same
// time, we need to handle Smis and other JS primitives here.
if (result->IsWasmInternalFunction()) {
result = handle(
i::Handle<i::WasmInternalFunction>::cast(result)->external(), isolate);
}
i::Handle<i::Object> result = i::WasmTableObject::Get(
isolate, table, static_cast<uint32_t>(index), i::WasmTableObject::kJS);
DCHECK(result->IsNull(isolate) || result->IsJSReceiver());
return V8RefValueToWasm(impl(this)->store(), result);
}
@ -2008,13 +2002,9 @@ auto Table::set(size_t index, const Ref* ref) -> bool {
i::Isolate* isolate = table->GetIsolate();
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();
}
i::WasmTableObject::Set(isolate, table, static_cast<uint32_t>(index), obj);
i::WasmTableObject::Set(isolate, table, static_cast<uint32_t>(index), obj,
i::WasmTableObject::kJS);
return true;
}

View File

@ -2000,7 +2000,8 @@ void InstanceBuilder::SetTableInitialValues(
for (uint32_t entry_index = 0; entry_index < table.initial_size;
entry_index++) {
WasmTableObject::Set(isolate_, table_object, entry_index,
to_value(result).to_ref());
to_value(result).to_ref(),
WasmTableObject::kWasm);
}
}
}
@ -2049,7 +2050,7 @@ base::Optional<MessageTemplate> LoadElemSegmentImpl(
zone, entry, elem_segment.type, isolate, instance);
if (is_error(result)) return to_error(result);
WasmTableObject::Set(isolate, table_object, entry_index,
to_value(result).to_ref());
to_value(result).to_ref(), WasmTableObject::kWasm);
}
}
return {};

View File

@ -1182,6 +1182,18 @@ void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) {
} else if (enabled_features.has_stringref() &&
string->StringEquals(v8_str(isolate, "stringref"))) {
type = i::wasm::kWasmStringRef;
} 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, "eqref"))) {
type = i::wasm::kWasmEqRef;
} 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 {
thrower.TypeError(
"Descriptor property 'element' must be a WebAssembly reference type");
@ -1232,13 +1244,9 @@ void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) {
"with the type of the new table.");
return;
}
// TODO(7748): Generalize this if other table types are allowed.
if (type == i::wasm::kWasmFuncRef && !element->IsNull()) {
element = i::WasmInternalFunction::FromExternal(element, i_isolate)
.ToHandleChecked();
}
for (uint32_t index = 0; index < static_cast<uint32_t>(initial); ++index) {
i::WasmTableObject::Set(i_isolate, table_obj, index, element);
i::WasmTableObject::Set(i_isolate, table_obj, index, element,
i::WasmTableObject::kJS);
}
} else if (initial > 0) {
switch (table_obj->type().heap_representation()) {
@ -2300,13 +2308,8 @@ void WebAssemblyTableGet(const v8::FunctionCallbackInfo<v8::Value>& args) {
return;
}
i::Handle<i::Object> result =
i::WasmTableObject::Get(i_isolate, receiver, index);
if (result->IsWasmInternalFunction()) {
result =
handle(i::Handle<i::WasmInternalFunction>::cast(result)->external(),
i_isolate);
}
i::Handle<i::Object> result = i::WasmTableObject::Get(
i_isolate, receiver, index, i::WasmTableObject::kJS);
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
return_value.Set(Utils::ToLocal(result));
@ -2342,14 +2345,8 @@ void WebAssemblyTableSet(const v8::FunctionCallbackInfo<v8::Value>& args) {
return;
}
i::Handle<i::Object> external_element;
// TODO(7748): Make sure externref tables don't convert any values.
bool is_external = table_object->type() != i::wasm::kWasmExternRef &&
i::WasmInternalFunction::FromExternal(element, i_isolate)
.ToHandle(&external_element);
i::WasmTableObject::Set(i_isolate, table_object, index,
is_external ? external_element : element);
i::WasmTableObject::Set(i_isolate, table_object, index, element,
i::WasmTableObject::kJS);
}
// WebAssembly.Table.type() -> TableType

View File

@ -619,7 +619,9 @@ struct WasmTable {
if (!type.is_object_reference()) return false;
HeapType heap_type = type.heap_type();
return heap_type == HeapType::kFunc || heap_type == HeapType::kExtern ||
heap_type == HeapType::kString ||
heap_type == HeapType::kAny || heap_type == HeapType::kData ||
heap_type == HeapType::kArray || heap_type == HeapType::kEq ||
heap_type == HeapType::kI31 || heap_type == HeapType::kString ||
heap_type == HeapType::kStringViewWtf8 ||
heap_type == HeapType::kStringViewWtf16 ||
heap_type == HeapType::kStringViewIter ||

View File

@ -306,7 +306,7 @@ int WasmTableObject::Grow(Isolate* isolate, Handle<WasmTableObject> table,
}
for (uint32_t entry = old_size; entry < new_size; ++entry) {
WasmTableObject::Set(isolate, table, entry, init_value);
WasmTableObject::Set(isolate, table, entry, init_value, ValueRepr::kWasm);
}
return old_size;
}
@ -333,16 +333,18 @@ bool WasmTableObject::IsValidElement(Isolate* isolate,
&error_message);
}
void WasmTableObject::SetFunctionTableEntry(Isolate* isolate,
Handle<WasmTableObject> table,
Handle<FixedArray> entries,
int entry_index,
Handle<Object> entry) {
void WasmTableObject::SetFunctionTableEntry(
Isolate* isolate, Handle<WasmTableObject> table, Handle<FixedArray> entries,
int entry_index, Handle<Object> entry, ValueRepr entry_repr) {
if (entry->IsNull(isolate)) {
ClearDispatchTables(isolate, table, entry_index); // Degenerate case.
entries->set(entry_index, ReadOnlyRoots(isolate).null_value());
return;
}
if (entry_repr == ValueRepr::kJS) {
entry =
i::WasmInternalFunction::FromExternal(entry, isolate).ToHandleChecked();
}
Handle<Object> external =
handle(Handle<WasmInternalFunction>::cast(entry)->external(), isolate);
@ -367,7 +369,8 @@ void WasmTableObject::SetFunctionTableEntry(Isolate* isolate,
}
void WasmTableObject::Set(Isolate* isolate, Handle<WasmTableObject> table,
uint32_t index, Handle<Object> entry) {
uint32_t index, Handle<Object> entry,
ValueRepr entry_repr) {
// Callers need to perform bounds checks, type check, and error handling.
DCHECK(IsInBounds(isolate, table, index));
DCHECK(IsValidElement(isolate, table, entry));
@ -385,15 +388,19 @@ void WasmTableObject::Set(Isolate* isolate, Handle<WasmTableObject> table,
entries->set(entry_index, *entry);
return;
case wasm::HeapType::kFunc:
SetFunctionTableEntry(isolate, table, entries, entry_index, entry);
SetFunctionTableEntry(isolate, table, entries, entry_index, entry,
entry_repr);
return;
case wasm::HeapType::kEq:
case wasm::HeapType::kData:
case wasm::HeapType::kArray:
case wasm::HeapType::kAny:
case wasm::HeapType::kI31:
// TODO(7748): Implement once we have struct/arrays/i31ref/string tables.
UNREACHABLE();
if (!i::FLAG_wasm_gc_js_interop && entry_repr == ValueRepr::kJS) {
i::wasm::TryUnpackObjectWrapper(isolate, entry);
}
entries->set(entry_index, *entry);
return;
case wasm::HeapType::kBottom:
UNREACHABLE();
default:
@ -402,14 +409,15 @@ void WasmTableObject::Set(Isolate* isolate, Handle<WasmTableObject> table,
DCHECK(WasmInstanceObject::cast(table->instance())
.module()
->has_signature(table->type().ref_index()));
SetFunctionTableEntry(isolate, table, entries, entry_index, entry);
SetFunctionTableEntry(isolate, table, entries, entry_index, entry,
entry_repr);
return;
}
}
Handle<Object> WasmTableObject::Get(Isolate* isolate,
Handle<WasmTableObject> table,
uint32_t index) {
uint32_t index, ValueRepr as_repr) {
Handle<FixedArray> entries(table->entries(), isolate);
// Callers need to perform bounds checks and error handling.
DCHECK(IsInBounds(isolate, table, index));
@ -424,23 +432,39 @@ Handle<Object> WasmTableObject::Get(Isolate* isolate,
}
switch (table->type().heap_representation()) {
case wasm::HeapType::kExtern:
case wasm::HeapType::kString:
case wasm::HeapType::kStringViewWtf8:
case wasm::HeapType::kStringViewWtf16:
case wasm::HeapType::kStringViewIter:
DCHECK(as_repr != ValueRepr::kJS); // No representation in JavaScript.
return entry;
case wasm::HeapType::kExtern:
case wasm::HeapType::kString:
return entry;
case wasm::HeapType::kFunc:
if (entry->IsWasmInternalFunction()) return entry;
break;
case wasm::HeapType::kEq:
case wasm::HeapType::kI31:
case wasm::HeapType::kData:
case wasm::HeapType::kArray:
case wasm::HeapType::kAny:
// TODO(7748): Implement once we have a story for struct/arrays/i31ref in
// JS.
UNIMPLEMENTED();
if (as_repr == ValueRepr::kJS && !FLAG_wasm_gc_js_interop &&
entry->IsWasmObject()) {
// Transform wasm object into JS-compliant representation.
Handle<JSObject> wrapper =
isolate->factory()->NewJSObject(isolate->object_function());
JSObject::AddProperty(isolate, wrapper,
isolate->factory()->wasm_wrapped_object_symbol(),
entry, NONE);
return wrapper;
}
return entry;
case wasm::HeapType::kFunc:
if (entry->IsWasmInternalFunction()) {
return as_repr == ValueRepr::kJS
? handle(
Handle<WasmInternalFunction>::cast(entry)->external(),
isolate)
: entry;
}
break;
case wasm::HeapType::kBottom:
UNREACHABLE();
default:
@ -449,7 +473,13 @@ Handle<Object> WasmTableObject::Get(Isolate* isolate,
DCHECK(WasmInstanceObject::cast(table->instance())
.module()
->has_signature(table->type().ref_index()));
if (entry->IsWasmInternalFunction()) return entry;
if (entry->IsWasmInternalFunction()) {
return as_repr == ValueRepr::kJS
? handle(
Handle<WasmInternalFunction>::cast(entry)->external(),
isolate)
: entry;
}
break;
}
@ -465,7 +495,8 @@ Handle<Object> WasmTableObject::Get(Isolate* isolate,
WasmInstanceObject::GetOrCreateWasmInternalFunction(isolate, instance,
function_index);
entries->set(entry_index, *internal);
return internal;
return as_repr == ValueRepr::kJS ? handle(internal->external(), isolate)
: internal;
}
void WasmTableObject::Fill(Isolate* isolate, Handle<WasmTableObject> table,
@ -477,7 +508,7 @@ void WasmTableObject::Fill(Isolate* isolate, Handle<WasmTableObject> table,
DCHECK_LE(start + count, table->current_length());
for (uint32_t i = 0; i < count; i++) {
WasmTableObject::Set(isolate, table, start + i, entry);
WasmTableObject::Set(isolate, table, start + i, entry, ValueRepr::kWasm);
}
}
@ -1365,8 +1396,10 @@ bool WasmInstanceObject::CopyTableEntries(Isolate* isolate,
for (uint32_t i = 0; i < count; ++i) {
uint32_t src_index = copy_backward ? (src + count - i - 1) : src + i;
uint32_t dst_index = copy_backward ? (dst + count - i - 1) : dst + i;
auto value = WasmTableObject::Get(isolate, table_src, src_index);
WasmTableObject::Set(isolate, table_dst, dst_index, value);
auto repr =
WasmTableObject::kWasm; // Do not externalize / internalize values.
auto value = WasmTableObject::Get(isolate, table_src, src_index, repr);
WasmTableObject::Set(isolate, table_dst, dst_index, value, repr);
}
return true;
}
@ -2323,7 +2356,11 @@ bool TypecheckJSObject(Isolate* isolate, const WasmModule* module,
// TODO(7748): Change this when we have a decision on the JS API for
// structs/arrays.
if (!FLAG_wasm_gc_js_interop) {
if (!TryUnpackObjectWrapper(isolate, value)) {
// The value can be a struct / array as this function is also used
// for checking objects not coming from JS (like data segments).
if (!value->IsSmi() && !value->IsWasmStruct() &&
!value->IsWasmArray() && !value->IsString() &&
!TryUnpackObjectWrapper(isolate, value)) {
*error_message =
"eqref/dataref/i31ref object must be null (if nullable) or "
"wrapped with the wasm object wrapper";
@ -2339,7 +2376,8 @@ bool TypecheckJSObject(Isolate* isolate, const WasmModule* module,
return true;
}
if (!((repr == HeapType::kEq && value->IsSmi()) ||
if (!(((repr == HeapType::kEq || repr == HeapType::kAny) &&
value->IsSmi()) ||
(repr != HeapType::kArray && value->IsWasmStruct()) ||
value->IsWasmArray())) {
*error_message = "object incompatible with wasm type";

View File

@ -196,13 +196,17 @@ class WasmTableObject
static bool IsValidElement(Isolate* isolate, Handle<WasmTableObject> table,
Handle<Object> entry);
enum ValueRepr { kJS, kWasm };
V8_EXPORT_PRIVATE static void Set(Isolate* isolate,
Handle<WasmTableObject> table,
uint32_t index, Handle<Object> entry);
uint32_t index, Handle<Object> entry,
ValueRepr entry_repr);
V8_EXPORT_PRIVATE static Handle<Object> Get(Isolate* isolate,
Handle<WasmTableObject> table,
uint32_t index);
uint32_t index,
ValueRepr as_repr);
V8_EXPORT_PRIVATE static void Fill(Isolate* isolate,
Handle<WasmTableObject> table,
@ -244,7 +248,7 @@ class WasmTableObject
static void SetFunctionTableEntry(Isolate* isolate,
Handle<WasmTableObject> table,
Handle<FixedArray> entries, int entry_index,
Handle<Object> entry);
Handle<Object> entry, ValueRepr entry_repr);
TQ_OBJECT_CONSTRUCTORS(WasmTableObject)
};
@ -1062,6 +1066,7 @@ namespace wasm {
bool TypecheckJSObject(Isolate* isolate, const WasmModule* module,
Handle<Object> value, ValueType expected,
const char** error_message);
bool TryUnpackObjectWrapper(Isolate* isolate, Handle<Object>& in_out_value);
} // namespace wasm
} // namespace internal

View File

@ -377,7 +377,8 @@ void CheckTable(Isolate* isolate, Handle<WasmTableObject> table, Args... args) {
CHECK_EQ(table->current_length(), args_length);
Handle<Object> handles[] = {args...};
for (uint32_t i = 0; i < args_length; ++i) {
CHECK(WasmTableObject::Get(isolate, table, i).is_identical_to(handles[i]));
CHECK(WasmTableObject::Get(isolate, table, i, WasmTableObject::kWasm)
.is_identical_to(handles[i]));
}
}
@ -572,11 +573,11 @@ void TestTableCopyElems(TestExecutionTier execution_tier, int table_dst,
r.builder().instance_object()->tables().get(table_dst)),
isolate);
r.CheckCallViaJS(0, 0, 0, kTableSize);
auto f0 = WasmTableObject::Get(isolate, table, 0);
auto f1 = WasmTableObject::Get(isolate, table, 1);
auto f2 = WasmTableObject::Get(isolate, table, 2);
auto f3 = WasmTableObject::Get(isolate, table, 3);
auto f4 = WasmTableObject::Get(isolate, table, 4);
auto f0 = WasmTableObject::Get(isolate, table, 0, WasmTableObject::kWasm);
auto f1 = WasmTableObject::Get(isolate, table, 1, WasmTableObject::kWasm);
auto f2 = WasmTableObject::Get(isolate, table, 2, WasmTableObject::kWasm);
auto f3 = WasmTableObject::Get(isolate, table, 3, WasmTableObject::kWasm);
auto f4 = WasmTableObject::Get(isolate, table, 4, WasmTableObject::kWasm);
if (table_dst == table_src) {
CheckTable(isolate, table, f0, f1, f2, f3, f4);
@ -722,11 +723,11 @@ void TestTableCopyOobWrites(TestExecutionTier execution_tier, int table_dst,
isolate);
// Fill the dst table with values from the src table, to make checks easier.
r.CheckCallViaJS(0, 0, 0, kTableSize);
auto f0 = WasmTableObject::Get(isolate, table, 0);
auto f1 = WasmTableObject::Get(isolate, table, 1);
auto f2 = WasmTableObject::Get(isolate, table, 2);
auto f3 = WasmTableObject::Get(isolate, table, 3);
auto f4 = WasmTableObject::Get(isolate, table, 4);
auto f0 = WasmTableObject::Get(isolate, table, 0, WasmTableObject::kWasm);
auto f1 = WasmTableObject::Get(isolate, table, 1, WasmTableObject::kWasm);
auto f2 = WasmTableObject::Get(isolate, table, 2, WasmTableObject::kWasm);
auto f3 = WasmTableObject::Get(isolate, table, 3, WasmTableObject::kWasm);
auto f4 = WasmTableObject::Get(isolate, table, 4, WasmTableObject::kWasm);
CheckTable(isolate, table, f0, f1, f2, f3, f4);

View File

@ -320,8 +320,8 @@ TEST(WrapperReplacement_IndirectExport) {
Handle<WasmTableObject> table(
WasmTableObject::cast(instance->tables().get(table_index)), isolate);
// Get the Wasm function through the exported table.
Handle<Object> function =
WasmTableObject::Get(isolate, table, function_index);
Handle<Object> function = WasmTableObject::Get(
isolate, table, function_index, WasmTableObject::kWasm);
Handle<WasmExportedFunction> indirect_function(
WasmExportedFunction::cast(
WasmInternalFunction::cast(*function).external()),

View File

@ -3702,8 +3702,8 @@ class WasmInterpreterInternals {
if (entry_index >= table_size) {
return DoTrap(kTrapTableOutOfBounds, pc);
}
Handle<Object> value =
WasmTableObject::Get(isolate_, table, entry_index);
Handle<Object> value = WasmTableObject::Get(
isolate_, table, entry_index, WasmTableObject::kWasm);
Push(WasmValue(value, table->type()));
len = 1 + imm.length;
break;
@ -3721,7 +3721,8 @@ class WasmInterpreterInternals {
if (entry_index >= table_size) {
return DoTrap(kTrapTableOutOfBounds, pc);
}
WasmTableObject::Set(isolate_, table, entry_index, value);
WasmTableObject::Set(isolate_, table, entry_index, value,
WasmTableObject::kWasm);
len = 1 + imm.length;
break;
}

View File

@ -6,7 +6,6 @@
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
/* TODO(7748): Re-enable these tests once any ref tables are supported.
(function TestArrayNewElemStatic() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
@ -19,7 +18,7 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
kGCPrefix, kExprStructNew, struct_type_index];
}
builder.addTable(kWasmAnyRef, 10, 10);
let table = builder.addTable(kWasmAnyRef, 10, 10);
let elems = [10, -10, 42, 55];
@ -29,7 +28,7 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
struct_type);
let active_segment = builder.addActiveElementSegment(
0, wasmI32Const(0), [makeStruct(elems[2]), makeStruct(elems[3])],
table, wasmI32Const(0), [makeStruct(elems[2]), makeStruct(elems[3])],
struct_type);
function generator(name, segment) {
@ -159,7 +158,6 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
assertTraps(kTrapNullDereference, () => table_get(0, 2));
assertTraps(kTrapArrayOutOfBounds, () => table_get(0, 3));
})();
*/
(function TestArrayNewElemStaticMistypedSegment() {
print(arguments.callee.name);
@ -210,7 +208,6 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
/invalid element segment index/);
})();
/* TODO(7748): Re-enable these tests once any ref tables are supported.
(function TestArrayNewElemStaticConstantArrayTooLarge() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
@ -329,4 +326,3 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
// An active segment counts as having 0 length.
assertTraps(kTrapElementSegmentOutOfBounds, () => instance.exports.init());
})();
*/

View File

@ -0,0 +1,183 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --experimental-wasm-gc
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
let tableTypes = {
"anyref": kWasmAnyRef,
"eqref": kWasmEqRef,
"dataref": kWasmDataRef,
"arrayref": kWasmArrayRef,
};
// Test table consistency check.
for (let [typeName, type] of Object.entries(tableTypes)) {
print("TestTableTypeCheck_" + typeName);
let builder = new WasmModuleBuilder();
const size = 10;
builder.addImportedTable("imports", "table", size, size, type);
for (let typeName2 in tableTypes) {
let table = new WebAssembly.Table({
initial: size, maximum: size, element: typeName2
});
if (typeName == typeName2) {
builder.instantiate({ imports: { table } });
} else {
let err = 'WebAssembly.Instance(): Import #0 module="imports" ' +
'function="table" error: imported table does not match the ' +
'expected type';
assertThrows(() => builder.instantiate({ imports: { table } }),
WebAssembly.LinkError,
err);
}
}
}
// Test table usage from JS and Wasm.
for (let [typeName, type] of Object.entries(tableTypes)) {
print("TestImportedTable_" + typeName);
let builder = new WasmModuleBuilder();
const size = 10;
let table = new WebAssembly.Table({
initial: size, maximum: size, element: typeName
});
let creatorSig = builder.addType(makeSig([], [type]));
let struct = builder.addStruct([makeField(kWasmI32, false)]);
let array = builder.addArray(kWasmI32, true);
builder.addImportedTable("imports", "table", size, size, type);
builder.addFunction("tableSet",
makeSig([kWasmI32, wasmRefType(creatorSig)], []))
.addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprCallRef,
kExprTableSet, 0,
])
.exportFunc();
builder.addFunction("tableGet", makeSig([kWasmI32], [kWasmExternRef]))
.addBody([
kExprLocalGet, 0, kExprTableGet, 0,
kGCPrefix, kExprExternExternalize,
])
.exportFunc();
let getValSig = makeSig([kWasmI32], [kWasmI32]);
builder.addFunction("tableGetStructVal", getValSig)
.addBody([
kExprLocalGet, 0, kExprTableGet, 0,
kGCPrefix, kExprRefAsData,
kGCPrefix, kExprRefCastStatic, struct,
kGCPrefix, kExprStructGet, struct, 0,
])
.exportFunc();
builder.addFunction("tableGetArrayVal", getValSig)
.addBody([
kExprLocalGet, 0, kExprTableGet, 0,
kGCPrefix, kExprRefAsData,
kGCPrefix, kExprRefCastStatic, array,
kExprI32Const, 0,
kGCPrefix, kExprArrayGet, array,
])
.exportFunc();
builder.addFunction("exported",
makeSig([wasmRefType(creatorSig)], [kWasmExternRef]))
.addBody([
kExprLocalGet, 0,
kExprCallRef,
kGCPrefix, kExprExternExternalize,
])
.exportFunc();
let blockSig = builder.addType(makeSig([kWasmAnyRef], [kWasmEqRef]));
let castExternToEqRef = [
kGCPrefix, kExprExternInternalize,
kExprBlock, blockSig,
kGCPrefix, kExprBrOnI31, 0,
kGCPrefix, kExprBrOnData, 0,
// non-data, non-i31
kExprUnreachable, // conversion failure
kExprEnd,
];
// TODO(7748): Directly compare the externrefs in JS once
// FLAG_wasm_gc_js_interop is supported.
builder.addFunction("eq",
makeSig([kWasmExternRef, kWasmExternRef], [kWasmI32]))
.addBody([
kExprLocalGet, 0,
...castExternToEqRef,
kExprLocalGet, 1,
...castExternToEqRef,
kExprRefEq,
])
.exportFunc();
builder.addFunction("createNull", creatorSig)
.addBody([kExprRefNull, kNullRefCode])
.exportFunc();
if (typeName != "dataref" && typeName != "arrayref") {
builder.addFunction("createI31", creatorSig)
.addBody([kExprI32Const, 12, kGCPrefix, kExprI31New])
.exportFunc();
}
if (typeName != "arrayref") {
builder.addFunction("createStruct", creatorSig)
.addBody([kExprI32Const, 12, kGCPrefix, kExprStructNew, struct])
.exportFunc();
}
builder.addFunction("createArray", creatorSig)
.addBody([
kExprI32Const, 12,
kGCPrefix, kExprArrayNewFixedStatic, array, 1
])
.exportFunc();
let instance = builder.instantiate({ imports: { table } });
let wasm = instance.exports;
// Set null.
table.set(0, null);
assertEquals(null, wasm.tableGet(0));
assertEquals(null, table.get(0));
wasm.tableSet(1, wasm.createNull);
assertEquals(null, wasm.tableGet(1));
assertEquals(null, table.get(1));
// Set i31.
if (typeName != "dataref" && typeName != "arrayref") {
table.set(2, wasm.exported(wasm.createI31));
assertEquals(1, wasm.eq(table.get(2), wasm.tableGet(2)));
wasm.tableSet(3, wasm.createI31);
assertEquals(1, wasm.eq(table.get(3), wasm.tableGet(3)));
assertEquals(1, wasm.eq(table.get(2), table.get(3))); // The same smi.
}
// Set struct.
if (typeName != "arrayref") {
table.set(4, wasm.exported(wasm.createStruct));
assertEquals(1, wasm.eq(table.get(4), wasm.tableGet(4)));
assertEquals(12, wasm.tableGetStructVal(4));
wasm.tableSet(5, wasm.createStruct);
assertEquals(1, wasm.eq(table.get(5), wasm.tableGet(5)));
assertEquals(12, wasm.tableGetStructVal(5));
assertEquals(0, wasm.eq(table.get(4), table.get(5))); // Not the same.
}
// Set array.
table.set(6, wasm.exported(wasm.createArray));
assertEquals(1, wasm.eq(table.get(6), wasm.tableGet(6)));
assertEquals(12, wasm.tableGetArrayVal(6));
wasm.tableSet(7, wasm.createArray);
assertEquals(1, wasm.eq(table.get(7), wasm.tableGet(7)));
assertEquals(12, wasm.tableGetArrayVal(7));
assertEquals(0, wasm.eq(table.get(6), table.get(7))); // Not the same.
// Ensure all objects are externalized, so they can be handled by JS.
for (let i = 0; i < size; ++i) {
JSON.stringify(table.get(i));
}
}

View File

@ -143,46 +143,43 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
assertEquals(22, instance.exports.table_test(1, 33, 11));
})();
/* TODO(7748): Re-enable this test once any ref tables are supported.
(function TestAnyRefTable() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let unary_type = builder.addType(kSig_i_i);
let binary_type = builder.addType(kSig_i_ii);
let array_type = builder.addArray(kWasmI32);
let struct_type = builder.addStruct([makeField(kWasmI32, false)]);
let successor = builder.addFunction('addition', unary_type)
.addBody([kExprLocalGet, 0, kExprI32Const, 1, kExprI32Add]);
let subtraction = builder.addFunction('subtraction', binary_type)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Sub])
let table = builder.addTable(kWasmAnyRef, 4, 4);
builder.addActiveElementSegment(
table, wasmI32Const(0),
[[kExprRefFunc, successor.index],
[kExprRefFunc, subtraction.index],
[[...wasmI32Const(111), ...wasmI32Const(222),
kGCPrefix, kExprArrayNewFixedStatic, array_type, 2],
[...wasmI32Const(-31), kGCPrefix, kExprI31New],
[...wasmI32Const(10), kGCPrefix, kExprStructNew, struct_type],
[kExprRefNull, kEqRefCode]],
kWasmAnyRef);
// return static_cast<i->i>(table[0])(local_0)
builder.addFunction("f0_getter", kSig_i_i)
// return ...static_cast<array_type>(table[0])(local_0)
builder.addFunction("array_getter", kSig_ii_i)
.addLocals(wasmRefNullType(array_type), 1)
.addBody([
kExprLocalGet, 0,
kExprI32Const, 0, kExprTableGet, 0,
kGCPrefix, kExprRefAsFunc, kGCPrefix, kExprRefCastStatic, unary_type,
kExprCallRef])
kGCPrefix, kExprRefAsArray,
kGCPrefix, kExprRefCastStatic, array_type,
kExprLocalSet, 1,
kExprLocalGet, 1,
...wasmI32Const(0), kGCPrefix, kExprArrayGet, array_type,
kExprLocalGet, 1,
...wasmI32Const(1), kGCPrefix, kExprArrayGet, array_type])
.exportFunc();
// return static_cast<(i,i)->i>(table[1])(local_0, local_1)
builder.addFunction("f1_getter", kSig_i_ii)
// return static_cast<i31>(table[1])(local_0, local_1)
builder.addFunction("i31_getter", kSig_i_v)
.addBody([
kExprLocalGet, 0, kExprLocalGet, 1,
kExprI32Const, 1, kExprTableGet, 0,
kGCPrefix, kExprRefAsFunc, kGCPrefix, kExprRefCastStatic, binary_type,
kExprCallRef])
kGCPrefix, kExprRefAsI31,
kGCPrefix, kExprI31GetS])
.exportFunc();
// return static_cast<struct_type>(table[2]).field_0
@ -202,9 +199,8 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
assertTrue(!!instance);
assertEquals(43, instance.exports.f0_getter(42));
assertEquals(-7, instance.exports.f1_getter(12, 19));
assertEquals([111, 222], instance.exports.array_getter(42));
assertEquals(-31, instance.exports.i31_getter(12, 19));
assertEquals(10, instance.exports.struct_getter());
assertEquals(1, instance.exports.null_getter());
})();
*/

View File

@ -2084,10 +2084,7 @@ TEST_F(WasmModuleVerifyTest, IllegalTableTypes) {
using Vec = std::vector<byte>;
static Vec table_types[] = {{kRefNullCode, 0},
{kRefNullCode, 1},
{kRefNullCode, kI31RefCode},
{kI31RefCode}};
static Vec table_types[] = {{kRefNullCode, 0}, {kRefNullCode, 1}};
for (Vec type : table_types) {
Vec data = {