v8/test/mjsunit/wasm/reference-tables.js
Matthias Liedtke 168fcef9b0 [wasm-gc] Support Table<struct|array index>
Bug: v8:7748
Change-Id: I4057a9288fe3d2dc0df308ce51be92e417572bd1
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3865483
Reviewed-by: Manos Koukoutos <manoskouk@chromium.org>
Commit-Queue: Matthias Liedtke <mliedtke@chromium.org>
Cr-Commit-Position: refs/heads/main@{#82871}
2022-08-31 15:35:35 +00:00

547 lines
19 KiB
JavaScript

// Copyright 2020 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');
(function TestTables() {
print(arguments.callee.name);
var exporting_instance = (function() {
var builder = new WasmModuleBuilder();
var binary_type = builder.addType(kSig_i_ii);
builder.addFunction('addition', kSig_i_ii)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Add])
.exportFunc();
builder.addFunction('succ', kSig_i_i)
.addBody([kExprLocalGet, 0, kExprI32Const, 1, kExprI32Add])
.exportFunc();
builder.addTable(wasmRefNullType(binary_type), 1, 100).exportAs('table');
return builder.instantiate({});
})();
// Wrong type for imported table.
assertThrows(() => {
var builder = new WasmModuleBuilder();
var unary_type = builder.addType(kSig_i_i);
builder.addImportedTable(
'imports', 'table', 1, 100, wasmRefNullType(unary_type));
builder.instantiate({imports: {table: exporting_instance.exports.table}})
}, WebAssembly.LinkError, /imported table does not match the expected type/);
// Type for imported table must match exactly.
assertThrows(() => {
var builder = new WasmModuleBuilder();
builder.addImportedTable('imports', 'table', 1, 100, kWasmFuncRef);
builder.instantiate({imports: {table: exporting_instance.exports.table}})
}, WebAssembly.LinkError, /imported table does not match the expected type/);
var instance = (function() {
var builder = new WasmModuleBuilder();
var unary_type = builder.addType(kSig_i_i);
var binary_type = builder.addType(kSig_i_ii);
builder.addImportedTable(
'imports', 'table', 1, 100, wasmRefNullType(binary_type));
var table =
builder.addTable(wasmRefNullType(unary_type), 10).exportAs('table');
builder.addTable(kWasmFuncRef, 1).exportAs('generic_table');
builder
.addFunction(
'table_test', makeSig([wasmRefType(unary_type)], [kWasmI32]))
// Set table[0] to input function, then retrieve it and call it.
.addBody([
kExprI32Const, 0, kExprLocalGet, 0, kExprTableSet, table.index,
kExprI32Const, 42, kExprI32Const, 0, kExprTableGet, table.index,
kExprCallRef, unary_type,
])
.exportFunc();
// Same, but with table[1] and call_indirect
builder
.addFunction(
'table_indirect_test',
makeSig([wasmRefType(unary_type)], [kWasmI32]))
.addBody([
kExprI32Const, 1, kExprLocalGet, 0, kExprTableSet, table.index,
kExprI32Const, 42, kExprI32Const, 0, kExprCallIndirect, unary_type,
table.index
])
.exportFunc();
// Instantiate with a table of the correct type.
return builder.instantiate(
{imports: {table: exporting_instance.exports.table}});
})();
// This module is valid.
assertTrue(!!instance);
// The correct function reference is preserved when setting it to and getting
// it back from a table.
assertEquals(
43, instance.exports.table_test(exporting_instance.exports.succ));
// Same for call indirect (the indirect call tables are also set correctly).
assertEquals(
43,
instance.exports.table_indirect_test(exporting_instance.exports.succ));
// Setting from JS API respects types.
instance.exports.generic_table.set(0, exporting_instance.exports.succ);
instance.exports.table.set(0, exporting_instance.exports.succ);
assertThrows(
() => instance.exports.table.set(0, exporting_instance.exports.addition),
TypeError,
/Argument 1 is invalid for table of type \(ref null 0\)/);
})();
(function TestNonNullableTables() {
print(arguments.callee.name);
var builder = new WasmModuleBuilder();
var binary_type = builder.addType(kSig_i_ii);
var addition = builder.addFunction('addition', binary_type).addBody([
kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Add
]);
var subtraction =
builder.addFunction('subtraction', binary_type)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Sub])
.exportFunc();
var table = builder.addTable(wasmRefType(binary_type), 3, 3,
[kExprRefFunc, addition.index]);
builder.addFunction('init', kSig_v_v)
.addBody([
kExprI32Const, 1, kExprRefFunc, subtraction.index, kExprTableSet,
table.index
])
.exportFunc();
// (index, arg1, arg2) -> table[index](arg1, arg2)
builder.addFunction('table_test', kSig_i_iii)
.addBody([
kExprLocalGet, 1, kExprLocalGet, 2, kExprLocalGet, 0, kExprCallIndirect,
binary_type, table.index
])
.exportFunc();
var instance = builder.instantiate({});
assertTrue(!!instance);
instance.exports.init();
assertEquals(44, instance.exports.table_test(0, 33, 11));
assertEquals(22, instance.exports.table_test(1, 33, 11));
})();
(function TestAnyRefTable() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let array_type = builder.addArray(kWasmI32);
let struct_type = builder.addStruct([makeField(kWasmI32, false)]);
let table = builder.addTable(kWasmAnyRef, 4, 4);
builder.addActiveElementSegment(
table, wasmI32Const(0),
[[...wasmI32Const(111), ...wasmI32Const(222),
kGCPrefix, kExprArrayNewFixed, array_type, 2],
[...wasmI32Const(-31), kGCPrefix, kExprI31New],
[...wasmI32Const(10), kGCPrefix, kExprStructNew, struct_type],
[kExprRefNull, kEqRefCode]],
kWasmAnyRef);
// return ...static_cast<array_type>(table[0])
builder.addFunction("array_getter", kSig_ii_v)
.addLocals(wasmRefNullType(array_type), 1)
.addBody([
kExprI32Const, 0, kExprTableGet, 0,
kGCPrefix, kExprRefAsArray,
kGCPrefix, kExprRefCast, array_type,
kExprLocalSet, 0,
kExprLocalGet, 0,
...wasmI32Const(0), kGCPrefix, kExprArrayGet, array_type,
kExprLocalGet, 0,
...wasmI32Const(1), kGCPrefix, kExprArrayGet, array_type])
.exportFunc();
// return static_cast<i31>(table[1])
builder.addFunction("i31_getter", kSig_i_v)
.addBody([
kExprI32Const, 1, kExprTableGet, 0,
kGCPrefix, kExprRefAsI31,
kGCPrefix, kExprI31GetS])
.exportFunc();
// return static_cast<struct_type>(table[2]).field_0
builder.addFunction("struct_getter", kSig_i_v)
.addBody([
kExprI32Const, 2, kExprTableGet, 0,
kGCPrefix, kExprRefAsData, kGCPrefix, kExprRefCast, struct_type,
kGCPrefix, kExprStructGet, struct_type, 0])
.exportFunc();
// return table[3] == null
builder.addFunction("null_getter", kSig_i_v)
.addBody([kExprI32Const, 3, kExprTableGet, 0, kExprRefIsNull])
.exportFunc();
let instance = builder.instantiate({});
assertTrue(!!instance);
assertEquals([111, 222], instance.exports.array_getter());
assertEquals(-31, instance.exports.i31_getter());
assertEquals(10, instance.exports.struct_getter());
assertEquals(1, instance.exports.null_getter());
})();
(function TestAnyRefTableNotNull() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let array_type = builder.addArray(kWasmI32);
let struct_type = builder.addStruct([makeField(kWasmI32, false)]);
let table = builder.addTable(wasmRefType(kWasmAnyRef), 3, 6,
[...wasmI32Const(111), ...wasmI32Const(222),
kGCPrefix, kExprArrayNewFixed, array_type, 2])
.exportAs("table");
builder.addActiveElementSegment(
table, wasmI32Const(0),
[[...wasmI32Const(111), ...wasmI32Const(222),
kGCPrefix, kExprArrayNewFixed, array_type, 2],
[...wasmI32Const(-31), kGCPrefix, kExprI31New],
[...wasmI32Const(10), kGCPrefix, kExprStructNew, struct_type]],
wasmRefType(kWasmAnyRef));
// return ...cast<array_type>(table.get(0))
builder.addFunction("array_getter", kSig_ii_v)
.addLocals(wasmRefNullType(array_type), 1)
.addBody([
kExprI32Const, 0, kExprTableGet, 0,
kGCPrefix, kExprRefAsArray,
kGCPrefix, kExprRefCast, array_type,
kExprLocalSet, 0,
kExprLocalGet, 0,
...wasmI32Const(0), kGCPrefix, kExprArrayGet, array_type,
kExprLocalGet, 0,
...wasmI32Const(1), kGCPrefix, kExprArrayGet, array_type])
.exportFunc();
// return cast<i31>(table.get(1))
builder.addFunction("i31_getter", kSig_i_v)
.addBody([
kExprI32Const, 1, kExprTableGet, 0,
kGCPrefix, kExprRefAsI31,
kGCPrefix, kExprI31GetS])
.exportFunc();
// return cast<struct_type>(table.get(param<0>))[0]
builder.addFunction("struct_getter", kSig_i_i)
.addBody([
kExprLocalGet, 0, kExprTableGet, 0,
kGCPrefix, kExprRefAsData, kGCPrefix, kExprRefCast, struct_type,
kGCPrefix, kExprStructGet, struct_type, 0])
.exportFunc();
builder.addFunction("grow_table", kSig_v_v)
.addBody([
...wasmI32Const(20), kGCPrefix, kExprStructNew, struct_type,
kExprI32Const, 1,
kNumericPrefix, kExprTableGrow, 0,
kExprDrop,
])
.exportFunc();
builder.addFunction("create_struct", makeSig([kWasmI32], [kWasmExternRef]))
.addBody([
kExprLocalGet, 0,
kGCPrefix, kExprStructNew, struct_type,
kGCPrefix, kExprExternExternalize,
])
.exportFunc();
let instance = builder.instantiate({});
let wasmTable = instance.exports.table;
assertEquals([111, 222], instance.exports.array_getter());
assertEquals(-31, instance.exports.i31_getter());
assertEquals(10, instance.exports.struct_getter(2));
assertTraps(kTrapTableOutOfBounds, () => instance.exports.struct_getter(3));
instance.exports.grow_table();
assertEquals(20, instance.exports.struct_getter(3));
assertThrows(() => wasmTable.grow(1), TypeError,
/Argument 1 must be specified for non-nullable element type/);
wasmTable.grow(1, instance.exports.create_struct(33));
assertEquals(33, instance.exports.struct_getter(4));
assertThrows(() => wasmTable.set(4, undefined), TypeError,
/Argument 1 is invalid/);
assertThrows(() => wasmTable.set(4, null), TypeError,
/Argument 1 is invalid/);
})();
(function TestStructRefTable() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let struct_type = builder.addStruct([makeField(kWasmI32, false)]);
let table = builder.addTable(wasmRefNullType(struct_type), 4, 4);
builder.addActiveElementSegment(
table, wasmI32Const(0),
[[...wasmI32Const(10), kGCPrefix, kExprStructNew, struct_type],
[...wasmI32Const(11), kGCPrefix, kExprStructNew, struct_type],
[kExprRefNull, struct_type]],
wasmRefNullType(struct_type));
builder.addFunction("struct_getter", kSig_i_i)
.addBody([
kExprLocalGet, 0, kExprTableGet, 0,
kGCPrefix, kExprStructGet, struct_type, 0])
.exportFunc();
builder.addFunction("null_getter", kSig_i_i)
.addBody([kExprLocalGet, 0, kExprTableGet, 0, kExprRefIsNull])
.exportFunc();
let instance = builder.instantiate({});
assertTrue(!!instance);
assertEquals(10, instance.exports.struct_getter(0));
assertEquals(11, instance.exports.struct_getter(1));
assertEquals(1, instance.exports.null_getter(2));
})();
(function TestArrayRefTable() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let array_type = builder.addArray(kWasmI32);
let table = builder.addTable(wasmRefNullType(array_type), 4, 4);
builder.addActiveElementSegment(
table, wasmI32Const(0),
[[...wasmI32Const(10), ...wasmI32Const(11), ...wasmI32Const(12),
kGCPrefix, kExprArrayNewFixed, array_type, 3],
[kGCPrefix, kExprArrayNewFixed, array_type, 0],
[kExprRefNull, array_type]],
wasmRefNullType(array_type));
builder.addFunction("array_getter", kSig_i_ii)
.addBody([
kExprLocalGet, 0, kExprTableGet, 0,
kExprLocalGet, 1,
kGCPrefix, kExprArrayGet, array_type])
.exportFunc();
builder.addFunction("null_getter", kSig_i_i)
.addBody([kExprLocalGet, 0, kExprTableGet, 0, kExprRefIsNull])
.exportFunc();
let instance = builder.instantiate({});
assertTrue(!!instance);
assertEquals(10, instance.exports.array_getter(0, 0));
assertEquals(11, instance.exports.array_getter(0, 1));
assertEquals(12, instance.exports.array_getter(0, 2));
assertEquals(0, instance.exports.null_getter(1));
assertTraps(kTrapArrayOutOfBounds,
() => instance.exports.array_getter(1, 0));
assertEquals(1, instance.exports.null_getter(2));
})();
(function TestRefTableInvalidSegmentType() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let struct_type_a = builder.addStruct([makeField(kWasmI32, false)]);
let struct_type_b = builder.addStruct([makeField(kWasmI64, false)]);
let table = builder.addTable(wasmRefNullType(struct_type_a), 4, 4);
builder.addActiveElementSegment(
table, wasmI32Const(0),
[[...wasmI32Const(10), kGCPrefix, kExprStructNew, struct_type_b]],
wasmRefNullType(struct_type_b)); // Mismatches table type.
assertThrows(() => builder.instantiate({}),
WebAssembly.CompileError,
/Element segment .* is not a subtype of referenced table/);
})();
(function TestRefTableInvalidSegmentExpressionType() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let struct_type_a = builder.addStruct([makeField(kWasmI32, false)]);
let struct_type_b = builder.addStruct([makeField(kWasmI64, false)]);
let table = builder.addTable(wasmRefNullType(struct_type_a), 4, 4);
builder.addActiveElementSegment(
table, wasmI32Const(0),
[[...wasmI64Const(10), kGCPrefix, kExprStructNew, struct_type_b]],
wasmRefNullType(struct_type_a));
assertThrows(() => builder.instantiate({}),
WebAssembly.CompileError,
/expected \(ref null 0\), got \(ref 1\)/);
})();
(function TestMultiModuleRefTableEquivalentTypes() {
print(arguments.callee.name);
let exporting_instance = (() => {
let builder = new WasmModuleBuilder();
let struct_type = builder.addStruct([makeField(kWasmI32, false)]);
builder.addTable(wasmRefNullType(struct_type), 1, 100).exportAs('table');
return builder.instantiate({});
})();
// Mismatching struct definition.
assertThrows(() => {
let builder = new WasmModuleBuilder();
let struct_type = builder.addStruct([makeField(kWasmI64, false)]);
builder.addImportedTable(
'imports', 'table', 1, 100, wasmRefNullType(struct_type));
builder.instantiate({imports: {table: exporting_instance.exports.table}})
}, WebAssembly.LinkError, /imported table does not match the expected type/);
// Mismatching nullability.
assertThrows(() => {
let builder = new WasmModuleBuilder();
let struct_type = builder.addStruct([makeField(kWasmI32, false)]);
builder.addImportedTable(
'imports', 'table', 1, 100, wasmRefType(struct_type));
builder.instantiate({imports: {table: exporting_instance.exports.table}})
}, WebAssembly.LinkError, /imported table does not match the expected type/);
// Equivalent struct type.
let builder = new WasmModuleBuilder();
// Force type canonicalization for struct_type
builder.setSingletonRecGroups();
let struct_type = builder.addStruct([makeField(kWasmI32, false)]);
let struct_type_invalid = builder.addStruct([makeField(kWasmI64, false)]);
let struct_type_sub = builder.addStruct(
[makeField(kWasmI32, false), makeField(kWasmI32, false)], struct_type);
builder.addImportedTable(
'imports', 'table', 1, 100, wasmRefNullType(struct_type));
builder.addFunction("invalid_struct", makeSig([], [kWasmExternRef]))
.addBody([
kExprI64Const, 44,
kGCPrefix, kExprStructNew, struct_type_invalid,
kGCPrefix, kExprExternExternalize])
.exportFunc();
builder.addFunction("valid_struct", makeSig([], [kWasmExternRef]))
.addBody([
kExprI32Const, 44,
kGCPrefix, kExprStructNew, struct_type,
kGCPrefix, kExprExternExternalize])
.exportFunc();
builder.addFunction("valid_struct_sub", makeSig([], [kWasmExternRef]))
.addBody([
kExprI32Const, 55,
kExprI32Const, 66,
kGCPrefix, kExprStructNew, struct_type_sub,
kGCPrefix, kExprExternExternalize])
.exportFunc();
let table = exporting_instance.exports.table;
let instance = builder.instantiate({imports: {table}});
table.grow(5, undefined);
assertThrows(() => table.set(1, instance.exports.invalid_struct()),
TypeError);
table.set(1, instance.exports.valid_struct());
table.set(2, instance.exports.valid_struct_sub());
table.set(3, null);
assertThrows(() => table.set(1, undefined),
TypeError);
})();
(function TestMultiModuleRefTableSuperType() {
print(arguments.callee.name);
let exporting_instance = (() => {
let builder = new WasmModuleBuilder();
builder.setSingletonRecGroups();
let struct_type_base = builder.addStruct([makeField(kWasmI32, false)]);
let struct_type =
builder.addStruct([makeField(kWasmI32, false)], struct_type_base);
builder.addTable(wasmRefNullType(struct_type), 1, 100).exportAs('table');
return builder.instantiate({});
})();
let builder = new WasmModuleBuilder();
builder.setSingletonRecGroups();
let struct_type_base = builder.addStruct([makeField(kWasmI32, false)]);
let struct_type =
builder.addStruct([makeField(kWasmI32, false)], struct_type_base);
builder.addImportedTable(
'imports', 'table', 1, 100, wasmRefNullType(struct_type));
builder.addFunction("struct_base", makeSig([], [kWasmExternRef]))
.addBody([
kExprI32Const, 66,
kGCPrefix, kExprStructNew, struct_type_base,
kGCPrefix, kExprExternExternalize])
.exportFunc();
let table = exporting_instance.exports.table;
let instance = builder.instantiate({imports: {table}});
assertThrows(() => table.set(0, instance.exports.struct_base()), TypeError);
assertThrows(() => table.grow(1, instance.exports.struct_base()), TypeError);
})();
(function TestRefTableNotNull() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let struct_type = builder.addStruct([makeField(kWasmI32, false)]);
let table = builder.addTable(wasmRefType(struct_type), 2, 6,
[...wasmI32Const(111),
kGCPrefix, kExprStructNew, struct_type])
.exportAs("table");
builder.addFunction("create_struct", makeSig([kWasmI32], [kWasmExternRef]))
.addBody([
kExprLocalGet, 0,
kGCPrefix, kExprStructNew, struct_type,
kGCPrefix, kExprExternExternalize,
])
.exportFunc();
builder.addFunction("struct_getter", kSig_i_i)
.addBody([
kExprLocalGet, 0, kExprTableGet, 0,
kGCPrefix, kExprStructGet, struct_type, 0])
.exportFunc();
let instance = builder.instantiate({});
let wasmTable = instance.exports.table;
// Initial values.
assertEquals(111, instance.exports.struct_getter(0));
assertEquals(111, instance.exports.struct_getter(1));
assertTraps(kTrapTableOutOfBounds, () => instance.exports.struct_getter(2));
assertThrows(() => wasmTable.grow(1), TypeError,
/Argument 1 must be specified for non-nullable element type/);
wasmTable.grow(1, instance.exports.create_struct(222));
assertEquals(222, instance.exports.struct_getter(2));
assertThrows(() => wasmTable.set(2, undefined), TypeError,
/Argument 1 is invalid/);
assertThrows(() => wasmTable.set(2, null), TypeError,
/Argument 1 is invalid/);
wasmTable.set(2, instance.exports.create_struct(333));
assertEquals(333, instance.exports.struct_getter(2));
})();