mirror of
https://github.com/nodejs/node.git
synced 2025-05-15 22:04:26 +00:00

PR-URL: https://github.com/nodejs/node/pull/49639 Reviewed-By: Jiawen Geng <technicalcute@gmail.com> Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
420 lines
15 KiB
JavaScript
420 lines
15 KiB
JavaScript
// 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");
|
|
|
|
(function TestArrayNewElem() {
|
|
print(arguments.callee.name);
|
|
let builder = new WasmModuleBuilder();
|
|
let struct_type_index = builder.addStruct([makeField(kWasmI32, false)]);
|
|
let struct_type = wasmRefNullType(struct_type_index);
|
|
let array_type_index = builder.addArray(struct_type, true);
|
|
|
|
function makeStruct(element) {
|
|
return [...wasmI32Const(element),
|
|
kGCPrefix, kExprStructNew, struct_type_index];
|
|
}
|
|
|
|
let table = builder.addTable(kWasmAnyRef, 10, 10);
|
|
|
|
let elems = [10, -10, 42, 55];
|
|
|
|
let passive_segment = builder.addPassiveElementSegment(
|
|
[makeStruct(elems[0]), makeStruct(elems[1]),
|
|
[kExprRefNull, struct_type_index]],
|
|
struct_type);
|
|
|
|
let active_segment = builder.addActiveElementSegment(
|
|
table, wasmI32Const(0), [makeStruct(elems[2]), makeStruct(elems[3])],
|
|
struct_type);
|
|
|
|
function generator(name, segment) {
|
|
builder.addFunction(name, makeSig([kWasmI32, kWasmI32, kWasmI32], [kWasmI32]))
|
|
.addBody([
|
|
kExprLocalGet, 0, // offset
|
|
kExprLocalGet, 1, // length
|
|
kGCPrefix, kExprArrayNewElem, array_type_index, segment,
|
|
kExprLocalGet, 2, // index in the array
|
|
kGCPrefix, kExprArrayGet, array_type_index,
|
|
kGCPrefix, kExprStructGet, struct_type_index, 0])
|
|
.exportFunc()
|
|
}
|
|
|
|
// Respective segment elements should be pointer-identical.
|
|
builder.addFunction("identical", makeSig([kWasmI32, kWasmI32], [kWasmI32]))
|
|
.addBody([
|
|
kExprI32Const, 0, // offset
|
|
kExprLocalGet, 0, // length
|
|
kGCPrefix, kExprArrayNewElem, array_type_index, passive_segment,
|
|
kExprLocalGet, 1, // index in the array
|
|
kGCPrefix, kExprArrayGet, array_type_index,
|
|
|
|
kExprI32Const, 0, // offset
|
|
kExprLocalGet, 0, // length
|
|
kGCPrefix, kExprArrayNewElem, array_type_index, passive_segment,
|
|
kExprLocalGet, 1, // index in the array
|
|
kGCPrefix, kExprArrayGet, array_type_index,
|
|
|
|
kExprRefEq])
|
|
.exportFunc()
|
|
|
|
generator("init_and_get", passive_segment);
|
|
generator("init_and_get_active", active_segment);
|
|
|
|
builder.addFunction("drop", kSig_v_v)
|
|
.addBody([kNumericPrefix, kExprElemDrop, passive_segment])
|
|
.exportFunc();
|
|
|
|
let instance = builder.instantiate();
|
|
|
|
let init_and_get = instance.exports.init_and_get;
|
|
let init_and_get_active = instance.exports.init_and_get_active;
|
|
// Initializing from a passive segment works. The third element is null, so
|
|
// we get a null dereference.
|
|
assertEquals(elems[0], init_and_get(0, 3, 0));
|
|
assertEquals(elems[1], init_and_get(0, 3, 1));
|
|
assertTraps(kTrapNullDereference, () => init_and_get(0, 3, 2));
|
|
// The array has the correct length.
|
|
assertTraps(kTrapArrayOutOfBounds, () => init_and_get(0, 3, 3));
|
|
// Too large arrays are disallowed, in and out of Smi range.
|
|
assertTraps(kTrapArrayTooLarge, () => init_and_get(0, 1000000000, 10));
|
|
assertTraps(kTrapArrayTooLarge, () => init_and_get(0, 1 << 31, 10));
|
|
// Element is out of bounds.
|
|
assertTraps(kTrapElementSegmentOutOfBounds, () => init_and_get(0, 5, 0));
|
|
// Element index out of Smi range.
|
|
assertTraps(kTrapElementSegmentOutOfBounds,
|
|
() => init_and_get(0x80000000, 0, 0));
|
|
// Respective segment elements should be pointer-identical.
|
|
assertEquals(1, instance.exports.identical(3, 0));
|
|
// Now drop the segment.
|
|
instance.exports.drop();
|
|
// A 0-length array should still be created...
|
|
assertTraps(kTrapArrayOutOfBounds, () => init_and_get(0, 0, 0));
|
|
// ... but not a longer one.
|
|
assertTraps(kTrapElementSegmentOutOfBounds, () => init_and_get(0, 1, 0));
|
|
// Same holds for an active segment.
|
|
assertTraps(kTrapArrayOutOfBounds, () => init_and_get_active(0, 0, 0));
|
|
assertTraps(kTrapElementSegmentOutOfBounds,
|
|
() => init_and_get_active(0, 1, 0));
|
|
})();
|
|
|
|
// TODO(14034): Reenable when we have constant array.new_elem.
|
|
/*
|
|
(function TestArrayNewElemConstant() {
|
|
print(arguments.callee.name);
|
|
let builder = new WasmModuleBuilder();
|
|
let struct_type_index = builder.addStruct([makeField(kWasmI32, false)]);
|
|
let struct_type = wasmRefNullType(struct_type_index);
|
|
let array_type_index = builder.addArray(struct_type, true);
|
|
let array_type = wasmRefNullType(array_type_index);
|
|
|
|
function makeStruct(element) {
|
|
return [...wasmI32Const(element),
|
|
kGCPrefix, kExprStructNew, struct_type_index];
|
|
}
|
|
|
|
builder.addTable(kWasmAnyRef, 10, 10);
|
|
let table = 0;
|
|
|
|
let elems = [10, -10, 42, 55];
|
|
|
|
let passive_segment = builder.addPassiveElementSegment(
|
|
[makeStruct(elems[0]), makeStruct(elems[1]),
|
|
[kExprRefNull, struct_type_index]],
|
|
struct_type);
|
|
|
|
let active_segment = builder.addActiveElementSegment(
|
|
0, wasmI32Const(0), [makeStruct(elems[2]), makeStruct(elems[3])],
|
|
struct_type);
|
|
|
|
let array_segment = builder.addPassiveElementSegment(
|
|
[[...wasmI32Const(0), ...wasmI32Const(3),
|
|
kGCPrefix, kExprArrayNewElem,
|
|
array_type_index, passive_segment],
|
|
[...wasmI32Const(0), ...wasmI32Const(0),
|
|
kGCPrefix, kExprArrayNewElem,
|
|
array_type_index, active_segment]],
|
|
array_type);
|
|
|
|
builder.addFunction("init", kSig_v_v)
|
|
.addBody([kExprI32Const, 0, kExprI32Const, 0, kExprI32Const, 2,
|
|
kNumericPrefix, kExprTableInit, array_segment, table])
|
|
.exportFunc();
|
|
|
|
builder.addFunction("table_get", makeSig([kWasmI32, kWasmI32], [kWasmI32]))
|
|
.addBody([
|
|
kExprLocalGet, 0, // offset in table
|
|
kExprTableGet, table,
|
|
kGCPrefix, kExprRefAsArray,
|
|
kGCPrefix, kExprRefCast, array_type_index,
|
|
kExprLocalGet, 1, // index in the array
|
|
kGCPrefix, kExprArrayGet, array_type_index,
|
|
kGCPrefix, kExprStructGet, struct_type_index, 0])
|
|
.exportFunc()
|
|
|
|
builder.addFunction("drop", kSig_v_v)
|
|
.addBody([kNumericPrefix, kExprElemDrop, passive_segment])
|
|
.exportFunc();
|
|
|
|
let instance = builder.instantiate();
|
|
|
|
// First, initialize the table.
|
|
instance.exports.init();
|
|
let table_get = instance.exports.table_get;
|
|
// Initializing from a passive segment works. The third element is null, so
|
|
// we get a null dereference.
|
|
assertEquals(elems[0], table_get(0, 0));
|
|
assertEquals(elems[1], table_get(0, 1));
|
|
assertTraps(kTrapNullDereference, () => table_get(0, 2));
|
|
// The array has the correct length.
|
|
assertTraps(kTrapArrayOutOfBounds, () => table_get(0, 3));
|
|
// The array generated from the active segment should have length 0.
|
|
assertTraps(kTrapArrayOutOfBounds, () => table_get(1, 0));
|
|
// Now drop the segment with the array elements and reload the table.
|
|
instance.exports.drop();
|
|
instance.exports.init();
|
|
// Nothing should change: a constant expression should not observe the dropped
|
|
// segments.
|
|
assertEquals(elems[0], table_get(0, 0));
|
|
assertEquals(elems[1], table_get(0, 1));
|
|
assertTraps(kTrapNullDereference, () => table_get(0, 2));
|
|
assertTraps(kTrapArrayOutOfBounds, () => table_get(0, 3));
|
|
})();
|
|
*/
|
|
|
|
(function TestArrayNewElemMistypedSegment() {
|
|
print(arguments.callee.name);
|
|
let builder = new WasmModuleBuilder();
|
|
let struct_type_index = builder.addStruct([makeField(kWasmI32, false)]);
|
|
let struct_type = wasmRefNullType(struct_type_index);
|
|
let array_type_index = builder.addArray(struct_type, true);
|
|
|
|
let passive_segment = builder.addPassiveElementSegment([
|
|
[kExprRefNull, array_type_index]],
|
|
wasmRefNullType(array_type_index));
|
|
|
|
builder.addFunction("mistyped", makeSig([kWasmI32, kWasmI32], [kWasmI32]))
|
|
.addBody([
|
|
kExprI32Const, 0, // offset
|
|
kExprLocalGet, 0, // length
|
|
kGCPrefix, kExprArrayNewElem, array_type_index,
|
|
passive_segment,
|
|
kExprLocalGet, 1, // index in the array
|
|
kGCPrefix, kExprArrayGet, array_type_index,
|
|
kGCPrefix, kExprStructGet, struct_type_index, 0])
|
|
.exportFunc()
|
|
|
|
assertThrows(() => builder.instantiate(), WebAssembly.CompileError,
|
|
/segment type.*is not a subtype of array element type.*/);
|
|
})();
|
|
|
|
// TODO(14034): Reenable when we have constant array.new_elem.
|
|
/*
|
|
// Element segments are defined after globals, so currently it is not valid
|
|
// to refer to an element segment in the global section.
|
|
(function TestArrayNewFixedFromElemInGlobal() {
|
|
print(arguments.callee.name);
|
|
let builder = new WasmModuleBuilder();
|
|
let struct_type_index = builder.addStruct([makeField(kWasmI32, false)]);
|
|
let struct_type = wasmRefNullType(struct_type_index);
|
|
let array_type_index = builder.addArray(struct_type, true);
|
|
|
|
let passive_segment = builder.addPassiveElementSegment([
|
|
[kExprRefNull, struct_type_index]],
|
|
struct_type_index);
|
|
|
|
builder.addGlobal(
|
|
wasmRefNullType(array_type_index), false,
|
|
[...wasmI32Const(0), ...wasmI32Const(1),
|
|
kGCPrefix, kExprArrayNewElem,
|
|
array_type_index, passive_segment]);
|
|
|
|
assertThrows(() => builder.instantiate(), WebAssembly.CompileError,
|
|
/invalid element segment index/);
|
|
})();
|
|
|
|
(function TestArrayNewElemConstantArrayTooLarge() {
|
|
print(arguments.callee.name);
|
|
let builder = new WasmModuleBuilder();
|
|
let struct_type_index = builder.addStruct([makeField(kWasmI32, false)]);
|
|
let struct_type = wasmRefNullType(struct_type_index);
|
|
let array_type_index = builder.addArray(struct_type, true);
|
|
let array_type = wasmRefNullType(array_type_index);
|
|
|
|
function makeStruct(element) {
|
|
return [...wasmI32Const(element),
|
|
kGCPrefix, kExprStructNew, struct_type_index];
|
|
}
|
|
|
|
builder.addTable(kWasmAnyRef, 10, 10);
|
|
let table = 0;
|
|
|
|
let elems = [10, -10];
|
|
|
|
let passive_segment = builder.addPassiveElementSegment(
|
|
[makeStruct(elems[0]), makeStruct(elems[1]),
|
|
[kExprRefNull, struct_type_index]],
|
|
struct_type);
|
|
|
|
let array_segment = builder.addPassiveElementSegment(
|
|
[[...wasmI32Const(0), ...wasmI32Const(1 << 30),
|
|
kGCPrefix, kExprArrayNewElem,
|
|
array_type_index, passive_segment]],
|
|
array_type
|
|
);
|
|
|
|
builder.addFunction("init", kSig_v_v)
|
|
.addBody([kExprI32Const, 0, kExprI32Const, 0, kExprI32Const, 1,
|
|
kNumericPrefix, kExprTableInit, array_segment, table])
|
|
.exportFunc();
|
|
|
|
let instance = builder.instantiate();
|
|
assertTraps(kTrapArrayTooLarge, () => instance.exports.init());
|
|
})();
|
|
|
|
(function TestArrayNewElemConstantElementSegmentOutOfBounds() {
|
|
print(arguments.callee.name);
|
|
let builder = new WasmModuleBuilder();
|
|
let struct_type_index = builder.addStruct([makeField(kWasmI32, false)]);
|
|
let struct_type = wasmRefNullType(struct_type_index);
|
|
let array_type_index = builder.addArray(struct_type, true);
|
|
let array_type = wasmRefNullType(array_type_index);
|
|
|
|
function makeStruct(element) {
|
|
return [...wasmI32Const(element),
|
|
kGCPrefix, kExprStructNew, struct_type_index];
|
|
}
|
|
|
|
builder.addTable(kWasmAnyRef, 10, 10);
|
|
let table = 0;
|
|
|
|
let elems = [10, -10];
|
|
|
|
let passive_segment = builder.addPassiveElementSegment(
|
|
[makeStruct(elems[0]), makeStruct(elems[1]),
|
|
[kExprRefNull, struct_type_index]],
|
|
struct_type);
|
|
|
|
let array_segment = builder.addPassiveElementSegment(
|
|
[[...wasmI32Const(0), ...wasmI32Const(10),
|
|
kGCPrefix, kExprArrayNewElem,
|
|
array_type_index, passive_segment]],
|
|
array_type
|
|
);
|
|
|
|
builder.addFunction("init", kSig_v_v)
|
|
.addBody([kExprI32Const, 0, kExprI32Const, 0, kExprI32Const, 1,
|
|
kNumericPrefix, kExprTableInit, array_segment, table])
|
|
.exportFunc();
|
|
|
|
let instance = builder.instantiate();
|
|
assertTraps(kTrapElementSegmentOutOfBounds, () => instance.exports.init());
|
|
})();
|
|
|
|
(function TestArrayNewElemConstantActiveSegment() {
|
|
print(arguments.callee.name);
|
|
let builder = new WasmModuleBuilder();
|
|
let struct_type_index = builder.addStruct([makeField(kWasmI32, false)]);
|
|
let struct_type = wasmRefNullType(struct_type_index);
|
|
let array_type_index = builder.addArray(struct_type, true);
|
|
let array_type = wasmRefNullType(array_type_index);
|
|
|
|
function makeStruct(element) {
|
|
return [...wasmI32Const(element),
|
|
kGCPrefix, kExprStructNew, struct_type_index];
|
|
}
|
|
|
|
builder.addTable(kWasmAnyRef, 10, 10);
|
|
let table = 0;
|
|
|
|
let elems = [10, -10];
|
|
|
|
let active_segment = builder.addActiveElementSegment(
|
|
table, wasmI32Const(0),
|
|
[makeStruct(elems[0]), makeStruct(elems[1]),
|
|
[kExprRefNull, struct_type_index]],
|
|
struct_type);
|
|
|
|
let array_segment = builder.addPassiveElementSegment(
|
|
[[...wasmI32Const(0), ...wasmI32Const(3),
|
|
kGCPrefix, kExprArrayNewElem,
|
|
array_type_index, active_segment]],
|
|
array_type
|
|
);
|
|
|
|
builder.addFunction("init", kSig_v_v)
|
|
.addBody([kExprI32Const, 0, kExprI32Const, 0, kExprI32Const, 1,
|
|
kNumericPrefix, kExprTableInit, array_segment, table])
|
|
.exportFunc();
|
|
|
|
let instance = builder.instantiate();
|
|
// An active segment counts as having 0 length.
|
|
assertTraps(kTrapElementSegmentOutOfBounds, () => instance.exports.init());
|
|
})();
|
|
*/
|
|
|
|
(function TestArrayNewData() {
|
|
print(arguments.callee.name);
|
|
let builder = new WasmModuleBuilder();
|
|
let array_type_index = builder.addArray(kWasmI16, true);
|
|
|
|
let dummy_byte = 0xff;
|
|
let element_0 = 1000;
|
|
let element_1 = -2222;
|
|
|
|
let data_segment = builder.addPassiveDataSegment(
|
|
[dummy_byte, element_0 & 0xff, (element_0 >> 8) & 0xff,
|
|
element_1 & 0xff, (element_1 >> 8) & 0xff]);
|
|
|
|
// TODO(14034): Reenable when we have constant array.new_data.
|
|
/*
|
|
let global = builder.addGlobal(
|
|
wasmRefType(array_type_index), true,
|
|
[...wasmI32Const(1), ...wasmI32Const(2),
|
|
kGCPrefix, kExprArrayNewData, array_type_index, data_segment],
|
|
builder);
|
|
|
|
builder.addFunction("global_get", kSig_i_i)
|
|
.addBody([
|
|
kExprGlobalGet, global.index,
|
|
kExprLocalGet, 0,
|
|
kGCPrefix, kExprArrayGetS, array_type_index])
|
|
.exportFunc();
|
|
*/
|
|
|
|
// parameters: (segment offset, array length, array index)
|
|
builder.addFunction("init_from_data", kSig_i_iii)
|
|
.addBody([
|
|
kExprLocalGet, 0, kExprLocalGet, 1,
|
|
kGCPrefix, kExprArrayNewData,
|
|
array_type_index, data_segment,
|
|
kExprLocalGet, 2,
|
|
kGCPrefix, kExprArrayGetS, array_type_index])
|
|
.exportFunc();
|
|
|
|
builder.addFunction("drop_segment", kSig_v_v)
|
|
.addBody([kNumericPrefix, kExprDataDrop, data_segment])
|
|
.exportFunc();
|
|
|
|
let instance = builder.instantiate();
|
|
|
|
// TODO(14034): Reenable when we have constant array.new_elem.
|
|
// assertEquals(element_0, instance.exports.global_get(0));
|
|
// assertEquals(element_1, instance.exports.global_get(1));
|
|
|
|
let init = instance.exports.init_from_data;
|
|
|
|
assertEquals(element_0, init(1, 2, 0));
|
|
assertEquals(element_1, init(1, 2, 1));
|
|
|
|
assertTraps(kTrapArrayTooLarge, () => init(1, 1000000000, 0));
|
|
assertTraps(kTrapDataSegmentOutOfBounds, () => init(2, 2, 0));
|
|
|
|
instance.exports.drop_segment();
|
|
|
|
assertTraps(kTrapDataSegmentOutOfBounds, () => init(1, 2, 0));
|
|
})();
|