PR-URL: https://github.com/nodejs/node/pull/58070 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Darshan Sen <raisinten@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
251 lines
6.9 KiB
JavaScript
251 lines
6.9 KiB
JavaScript
// Copyright 2024 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: --allow-natives-syntax --experimental-wasm-growable-stacks
|
|
// Flags: --expose-gc --wasm-stack-switching-stack-size=4
|
|
// Flags: --wasm-staging --stack-size=400
|
|
|
|
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
|
|
|
|
function flatRange(upperBound, gen) {
|
|
const res = [];
|
|
for (let i = 0; i < upperBound; i++) {
|
|
res.push(gen(i));
|
|
}
|
|
return res.flat();
|
|
}
|
|
|
|
function growAndShrinkTwice(depth, paramType, constFn, addOp, result, heavy = false) {
|
|
const builder = new WasmModuleBuilder();
|
|
builder.addGlobal(kWasmI32, true).exportAs('depth');
|
|
const numIntArgs = 10;
|
|
const numTypes = Array(numIntArgs).fill(paramType);
|
|
var sig = makeSig(numTypes, numTypes);
|
|
builder.addImport('m', 'import', sig);
|
|
let sig_if = builder.addType(makeSig([], numTypes));
|
|
let deep_calc = builder.addFunction("deep_calc", sig);
|
|
deep_calc
|
|
.addLocals(kWasmI64, heavy ? 512 : 0) // make a frame bigger than 4kb
|
|
.addBody([
|
|
// decrement global
|
|
kExprGlobalGet, 0,
|
|
kExprI32Const, 1,
|
|
kExprI32Sub,
|
|
kExprGlobalSet, 0,
|
|
// should we recurse deeper?
|
|
kExprGlobalGet, 0,
|
|
kExprIf, sig_if,
|
|
// then
|
|
kExprLocalGet, 9,
|
|
...constFn(1),
|
|
addOp,
|
|
...flatRange(numIntArgs - 1, i => [kExprLocalGet, i]),
|
|
kExprCallFunction, deep_calc.index,
|
|
// else
|
|
kExprElse,
|
|
...flatRange(numIntArgs, i => [kExprLocalGet, i]),
|
|
kExprCallFunction, 0,
|
|
kExprEnd
|
|
])
|
|
builder.addFunction("test", makeSig([], numTypes))
|
|
.addBody([
|
|
// do stack expensive job
|
|
...flatRange(numIntArgs, constFn),
|
|
kExprCallFunction, deep_calc.index,
|
|
...flatRange(numIntArgs, () => kExprDrop),
|
|
// Reset the recursion budget.
|
|
...wasmI32Const(depth),
|
|
kExprGlobalSet, 0,
|
|
// do more stack expensive job
|
|
// to check limits was updated properly on shrink
|
|
...flatRange(numIntArgs, constFn),
|
|
kExprCallFunction, deep_calc.index,
|
|
]).exportFunc();
|
|
const js_import = new WebAssembly.Suspending((...args) => {
|
|
return Promise.resolve(args)
|
|
})
|
|
const instance = builder.instantiate({ m: { import: js_import } });
|
|
const wrapper = WebAssembly.promising(instance.exports.test);
|
|
instance.exports.depth.value = depth;
|
|
assertPromiseResult(
|
|
wrapper(),
|
|
r => result.forEach((v, i) => assertEquals(r[i], v))
|
|
);
|
|
}
|
|
|
|
(function TestStackGrowI32() {
|
|
print(arguments.callee.name);
|
|
growAndShrinkTwice(
|
|
430,
|
|
kWasmI32,
|
|
wasmI32Const,
|
|
kExprI32Add,
|
|
[44, 45, 46, 47, 48, 49, 50, 51, 52, 42]
|
|
);
|
|
})();
|
|
|
|
(function TestStackGrowI64() {
|
|
print(arguments.callee.name);
|
|
growAndShrinkTwice(
|
|
430,
|
|
kWasmI64,
|
|
wasmI64Const,
|
|
kExprI64Add,
|
|
[44n, 45n, 46n, 47n, 48n, 49n, 50n, 51n, 52n, 42n]
|
|
);
|
|
})();
|
|
|
|
(function TestStackGrowF32() {
|
|
print(arguments.callee.name);
|
|
growAndShrinkTwice(
|
|
430,
|
|
kWasmF32,
|
|
wasmF32Const,
|
|
kExprF32Add,
|
|
[44, 45, 46, 47, 48, 49, 50, 51, 52, 42]
|
|
);
|
|
})();
|
|
|
|
(function TestStackGrowF64() {
|
|
print(arguments.callee.name);
|
|
growAndShrinkTwice(
|
|
430,
|
|
kWasmF64,
|
|
wasmF64Const,
|
|
kExprF64Add,
|
|
[44, 45, 46, 47, 48, 49, 50, 51, 52, 42]
|
|
);
|
|
})();
|
|
|
|
(function TestStackGrowHeavyFrameI32() {
|
|
print(arguments.callee.name);
|
|
growAndShrinkTwice(
|
|
20,
|
|
kWasmI32,
|
|
wasmI32Const,
|
|
kExprI32Add,
|
|
[3, 4, 5, 6, 7, 8, 9, 10, 11, 1],
|
|
true
|
|
);
|
|
})();
|
|
|
|
(function TestStackGrowHeavyFrameI64() {
|
|
print(arguments.callee.name);
|
|
growAndShrinkTwice(
|
|
20,
|
|
kWasmI64,
|
|
wasmI64Const,
|
|
kExprI64Add,
|
|
[3n, 4n, 5n, 6n, 7n, 8n, 9n, 10n, 11n, 1n],
|
|
true
|
|
);
|
|
})();
|
|
|
|
(function TestStackGrowHeavyFrameF32() {
|
|
print(arguments.callee.name);
|
|
growAndShrinkTwice(
|
|
20,
|
|
kWasmF32,
|
|
wasmF32Const,
|
|
kExprF32Add,
|
|
[3, 4, 5, 6, 7, 8, 9, 10, 11, 1],
|
|
true
|
|
);
|
|
})();
|
|
|
|
(function TestStackGrowHeavyFrameF64() {
|
|
print(arguments.callee.name);
|
|
growAndShrinkTwice(
|
|
20,
|
|
kWasmF64,
|
|
wasmF64Const,
|
|
kExprF64Add,
|
|
[3, 4, 5, 6, 7, 8, 9, 10, 11, 1],
|
|
true
|
|
);
|
|
})();
|
|
|
|
(function TestStackGrowWithGCAndRefParams() {
|
|
print(arguments.callee.name);
|
|
const builder = new WasmModuleBuilder();
|
|
const call_stack_depth = 430;
|
|
builder.addGlobal(kWasmI32, true).exportAs('depth');
|
|
const numRefArgs = 10;
|
|
const refTypes = Array(numRefArgs).fill(kWasmExternRef);
|
|
var sig = makeSig(refTypes, refTypes);
|
|
const imp = builder.addImport('m', 'import', sig);
|
|
let sig_if = builder.addType(makeSig([], refTypes));
|
|
let deep_calc = builder.addFunction("deep_calc", sig);
|
|
deep_calc.addBody([
|
|
// decrement global
|
|
kExprGlobalGet, 0,
|
|
kExprI32Const, 1,
|
|
kExprI32Sub,
|
|
kExprGlobalSet, 0,
|
|
// should we recurse deeper?
|
|
kExprGlobalGet, 0,
|
|
kExprIf, sig_if,
|
|
// then
|
|
...flatRange(numRefArgs, i => [kExprLocalGet, i]),
|
|
kExprCallFunction, deep_calc.index,
|
|
// else
|
|
kExprElse,
|
|
...flatRange(numRefArgs, i => [kExprLocalGet, i]),
|
|
kExprCallFunction, imp,
|
|
kExprEnd
|
|
]);
|
|
builder.addFunction("test", makeSig([kWasmExternRef], refTypes))
|
|
.addBody([
|
|
// do stack expensive job
|
|
...flatRange(numRefArgs, () => [kExprLocalGet, 0]),
|
|
kExprCallFunction, deep_calc.index,
|
|
...flatRange(numRefArgs, () => kExprDrop),
|
|
// Reset the recursion budget.
|
|
...wasmI32Const(call_stack_depth),
|
|
kExprGlobalSet, 0,
|
|
// do more stack expensive job
|
|
// to check limits was updated properly on shrink
|
|
...flatRange(numRefArgs, () => [kExprLocalGet, 0]),
|
|
kExprCallFunction, deep_calc.index,
|
|
]).exportFunc();
|
|
const js_import = new WebAssembly.Suspending((...args) => {
|
|
gc();
|
|
return Promise.resolve(args)
|
|
});
|
|
const instance = builder.instantiate({ m: { import: js_import } });
|
|
const wrapper = WebAssembly.promising(instance.exports.test);
|
|
instance.exports.depth.value = call_stack_depth;
|
|
const ref = {};
|
|
assertPromiseResult(
|
|
wrapper(ref),
|
|
res => res.forEach(r => assertEquals(r, ref))
|
|
);
|
|
})();
|
|
|
|
(function TestStackOverflow() {
|
|
print(arguments.callee.name);
|
|
let builder = new WasmModuleBuilder();
|
|
builder.addFunction("test", kSig_i_v)
|
|
.addBody([
|
|
kExprCallFunction, 0
|
|
]).exportFunc();
|
|
let instance = builder.instantiate();
|
|
let wrapper = WebAssembly.promising(instance.exports.test);
|
|
assertThrowsAsync(wrapper(), RangeError, /Maximum call stack size exceeded/);
|
|
})();
|
|
|
|
(function TestStackOverflowWithHeavyFrames() {
|
|
print(arguments.callee.name);
|
|
let builder = new WasmModuleBuilder();
|
|
builder.addFunction("test", kSig_i_r)
|
|
.addLocals(kWasmI64, 512) // make a frame bigger than 4kb
|
|
.addBody([
|
|
kExprLocalGet, 0,
|
|
kExprCallFunction, 0
|
|
]).exportFunc();
|
|
let instance = builder.instantiate();
|
|
let wrapper = WebAssembly.promising(instance.exports.test);
|
|
assertThrowsAsync(wrapper(), RangeError, /Maximum call stack size exceeded/);
|
|
})();
|