[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:
parent
40901824d7
commit
3cc931543f
@ -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(
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 {};
|
||||
|
@ -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
|
||||
|
@ -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 ||
|
||||
|
@ -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";
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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()),
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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());
|
||||
})();
|
||||
*/
|
||||
|
183
test/mjsunit/wasm/reference-table-js-interop.js
Normal file
183
test/mjsunit/wasm/reference-table-js-interop.js
Normal 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));
|
||||
}
|
||||
}
|
@ -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());
|
||||
})();
|
||||
*/
|
||||
|
@ -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 = {
|
||||
|
Loading…
Reference in New Issue
Block a user