mirror of
https://github.com/nodejs/node.git
synced 2025-05-15 16:01:52 +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>
280 lines
7.2 KiB
JavaScript
280 lines
7.2 KiB
JavaScript
// Copyright 2023 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: --wasm-to-js-generic-wrapper --expose-gc
|
|
|
|
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
|
|
|
const debug = false;
|
|
// Use consecutive values as parameters for easier debugging.
|
|
const easyValues = false;
|
|
|
|
// This test tests the wasm-to-js wrapper with random signatures and random
|
|
// values. The test creates a WebAssembly function with the random signature.
|
|
// The WebAssembly function passes the incoming parameters directly to an
|
|
// imported JavaScript function. The JavaScript function checks that the
|
|
// incoming parameters match the original values passed to the WebAssembly
|
|
// function. The imported JavaScript function then returns random return values,
|
|
// which the WebAssembly function again forwards to the original caller. There
|
|
// the return values of the WebAssembly function are compared with the original
|
|
// random return values. `RunTest` is the core function, which runs the test
|
|
// case for a specific signature and value mapping. `RunTest` can be used well
|
|
// for debugging a specific signature. `GenerateAndRunTest` additionally
|
|
// generates the inputs for `RunTest`, which is good for fuzzing the js-to-wasm
|
|
// wrappers.
|
|
|
|
// Some documentation if the test fails in the CQ.
|
|
console.log("This test is a fuzzer, it tests the generic wasm-to-js wrapper");
|
|
console.log("with random signatures. If this test fails, then it may not fail");
|
|
console.log("for the CL that actually introduced the issue, but for a");
|
|
console.log("later CL in the CQ. You may want to use flako to identify that");
|
|
console.log("actual culprit.");
|
|
|
|
const kPossibleTypes = [kWasmI32, kWasmI64, kWasmF32, kWasmF64, kWasmExternRef];
|
|
|
|
function getRandomInt(max) {
|
|
return Math.floor(Math.random() * max);
|
|
}
|
|
|
|
function TypeString(type) {
|
|
switch (type) {
|
|
case kWasmI32:
|
|
return 'kWasmI32';
|
|
case kWasmI64:
|
|
return 'kWasmI64';
|
|
case kWasmF32:
|
|
return 'kWasmF32';
|
|
case kWasmF64:
|
|
return 'kWasmF64';
|
|
case kWasmExternRef:
|
|
return 'kWasmExternRef';
|
|
}
|
|
}
|
|
|
|
// A set of interesting values that will be used as parameters.
|
|
let interestingParams = {};
|
|
interestingParams[kWasmI32] = [
|
|
-1500000000, -0x4000_0000, -0x3fff_ffff, -1, 0, 1, 2, 0x3fff_ffff,
|
|
0x4000_0000, 1500000000, 0x7fff_ffff, {valueOf: () => 15}, {
|
|
toString: () => {
|
|
gc();
|
|
return '17';
|
|
}
|
|
}
|
|
];
|
|
|
|
interestingParams[kWasmI64] = [
|
|
-0x7fff_ffff_ffff_ffffn, -0xffff_ffffn, -0x8000_0000n, -1500000000n,
|
|
-0x4000_0000n, -0x3fff_ffffn, -1n, 0n, 1n, 2n, 0x3fff_ffffn, 0x4000_0000n,
|
|
1500000000n, 0x7fff_ffffn, 0x8000_0000n, 0xffff_ffffn, 0x7fff_ffff_ffff_ffffn,
|
|
{valueOf: () => 15n}, {
|
|
toString: () => {
|
|
gc();
|
|
return '17';
|
|
}
|
|
}
|
|
];
|
|
|
|
interestingParams[kWasmF32] = [
|
|
-Infinity,
|
|
-(2 ** 65),
|
|
-(2 ** 64),
|
|
-(2 ** 63),
|
|
-(2 ** 62),
|
|
-(2 ** 29),
|
|
-(2 ** 33),
|
|
-(2 ** 32),
|
|
-(2 ** 31),
|
|
-(2 ** 30),
|
|
-(2 ** 29),
|
|
-1.5,
|
|
-1,
|
|
0,
|
|
-0,
|
|
1,
|
|
1.5,
|
|
2,
|
|
2 ** 29,
|
|
2 ** 30,
|
|
2 ** 31,
|
|
2 ** 32,
|
|
2 ** 33,
|
|
2 ** 62,
|
|
2 ** 63,
|
|
2 ** 64,
|
|
2 ** 65,
|
|
{valueOf: () => 16},
|
|
{
|
|
toString: () => {
|
|
gc();
|
|
return '18';
|
|
}
|
|
},
|
|
Infinity,
|
|
NaN
|
|
];
|
|
|
|
interestingParams[kWasmF64] = [
|
|
-Infinity,
|
|
-(2 ** 65),
|
|
-(2 ** 64),
|
|
-(2 ** 63),
|
|
-(2 ** 62),
|
|
-(2 ** 29),
|
|
-(2 ** 33),
|
|
-(2 ** 32),
|
|
-(2 ** 31),
|
|
-(2 ** 30),
|
|
-(2 ** 29),
|
|
-1.5,
|
|
-1,
|
|
0,
|
|
-0,
|
|
1,
|
|
1.5,
|
|
2,
|
|
// 28 set bits, too many for float32.
|
|
0xfffffff,
|
|
2 ** 29,
|
|
2 ** 30,
|
|
2 ** 31,
|
|
2 ** 32,
|
|
2 ** 33,
|
|
2 ** 62,
|
|
2 ** 63,
|
|
2 ** 64,
|
|
2 ** 65,
|
|
{valueOf: () => 14},
|
|
{
|
|
toString: () => {
|
|
gc();
|
|
return '19';
|
|
}
|
|
},
|
|
Infinity,
|
|
NaN
|
|
];
|
|
|
|
interestingParams[kWasmExternRef] = [
|
|
17, 12.5, 17n, 0x7fff_ffff, {foo: 18}, [1, 2, 3], 'bar', () => 38, null,
|
|
undefined
|
|
];
|
|
|
|
function GenerateValueArray(params) {
|
|
let result = [];
|
|
let nextValue = 1;
|
|
for (let param of params) {
|
|
if (easyValues) {
|
|
switch (param) {
|
|
case kWasmI32:
|
|
case kWasmF32:
|
|
case kWasmF64:
|
|
result.push(nextValue++);
|
|
break;
|
|
case kWasmI64:
|
|
result.push(BigInt(nextValue++));
|
|
break;
|
|
case kWasmExternRef:
|
|
const val = nextValue++;
|
|
result.push({ val: val });
|
|
}
|
|
} else {
|
|
result.push(interestingParams[param][getRandomInt(
|
|
interestingParams[param].length)]);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function assertValueArray(original, transformed) {
|
|
assertEquals(transformed.length, original.length);
|
|
for (let i = 0; i < transformed.length; ++i) {
|
|
const arg = transformed[i];
|
|
if (typeof arg === 'bigint') {
|
|
// For values of type I64.
|
|
assertEquals(BigInt(original[i]), arg);
|
|
} else if (typeof arg === 'number') {
|
|
// For values of type I32, F32, and F64.
|
|
assertEquals(Number(original[i]), arg);
|
|
} else {
|
|
// For values of type externref.
|
|
assertEquals(original[i], arg);
|
|
}
|
|
}
|
|
}
|
|
|
|
function RunTest(params, returns) {
|
|
if (debug) {
|
|
let testcase = 'RunTest([';
|
|
for (let i = 0; i < params.length; ++i) {
|
|
testcase += (i == 0 ? '' : ', ') + TypeString(params[i]);
|
|
}
|
|
testcase += '], [';
|
|
if (returns.length > 0) {
|
|
testcase += TypeString(returns[0]);
|
|
for (let i = 1; i < returns.length; ++i) {
|
|
testcase += ', ' + TypeString(returns[i]);
|
|
}
|
|
}
|
|
testcase += ']);';
|
|
console.log(testcase);
|
|
}
|
|
|
|
const builder = new WasmModuleBuilder();
|
|
const impSig = makeSig(params, returns);
|
|
const impIndex = builder.addImport('m', 'f', impSig);
|
|
const paramValues = GenerateValueArray(params);
|
|
const returnValues = GenerateValueArray(returns);
|
|
let body = [];
|
|
for (let i = 0; i < params.length; ++i) {
|
|
body.push(kExprLocalGet, i);
|
|
}
|
|
body.push(kExprCallFunction, impIndex);
|
|
|
|
function impFunction() {
|
|
assertValueArray(paramValues, arguments);
|
|
if (returns.length === 0) return;
|
|
if (returns.length === 1) return returnValues[0];
|
|
return returnValues;
|
|
};
|
|
|
|
const sig = makeSig(params, returns);
|
|
builder.addFunction('main', sig).addBody(body).exportFunc();
|
|
const instance = builder.instantiate({ m: { f: impFunction } });
|
|
let result = instance.exports.main(...paramValues);
|
|
if (returns.length === 0) return;
|
|
if (returns.length === 1) result = [result];
|
|
assertValueArray(returnValues, result);
|
|
}
|
|
|
|
function GenerateAndRunTest() {
|
|
// Generate signature.
|
|
const kMaxParams = 20;
|
|
const kMaxReturns = 10;
|
|
const numParams = getRandomInt(kMaxParams) + 1;
|
|
const numReturns = getRandomInt(kMaxReturns + 1);
|
|
// The array of parameter types.
|
|
const params = [];
|
|
// The array of return types.
|
|
const returns = [];
|
|
for (let i = 0; i < numParams; ++i) {
|
|
params.push(kPossibleTypes[getRandomInt(kPossibleTypes.length)]);
|
|
}
|
|
for (let i = 0; i < numReturns; ++i) {
|
|
returns.push(kPossibleTypes[getRandomInt(kPossibleTypes.length)]);
|
|
}
|
|
RunTest(params, returns);
|
|
}
|
|
// Regression tests:
|
|
RunTest(
|
|
[
|
|
kWasmF32, kWasmF64, kWasmF64, kWasmF32, kWasmI32, kWasmI64, kWasmF32,
|
|
kWasmF64, kWasmF64, kWasmF64, kWasmF32, kWasmF32
|
|
],
|
|
[kWasmI64, kWasmI64, kWasmI32, kWasmF32]);
|
|
|
|
for (let i = 0; i < (debug ? 200 : 2); ++i) {
|
|
GenerateAndRunTest();
|
|
}
|