mirror of
https://github.com/copy/v86.git
synced 2025-12-31 04:23:15 +00:00
203 lines
6.1 KiB
JavaScript
Executable file
203 lines
6.1 KiB
JavaScript
Executable file
#!/usr/bin/env node
|
|
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import assert from "node:assert/strict";
|
|
import url from "node:url";
|
|
import { spawnSync } from "node:child_process";
|
|
import wabt from "../../build/libwabt.cjs";
|
|
|
|
const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
|
|
|
|
const TEST_RELEASE_BUILD = +process.env.TEST_RELEASE_BUILD;
|
|
const { V86 } = await import(TEST_RELEASE_BUILD ? "../../build/libv86.mjs" : "../../src/main.js");
|
|
|
|
const libwabt = wabt();
|
|
|
|
const TEST_NAME = process.env.TEST_NAME;
|
|
|
|
const LOG_LEVEL = 0;
|
|
const MIN_MEMORY_OFFSET = 4096;
|
|
|
|
const GIT_DIFF_FLAGS = ["--no-index", "--patience", "--color=always"];
|
|
|
|
const TEST_DIR = path.join(__dirname, "tests");
|
|
const BUILD_DIR = path.join(TEST_DIR, "build");
|
|
|
|
function run_all()
|
|
{
|
|
const asm_files = fs.readdirSync(TEST_DIR).filter(filename => filename.endsWith(".asm"));
|
|
|
|
const files = asm_files.map(asm_file => {
|
|
const name = asm_file.slice(0, -4);
|
|
return {
|
|
name,
|
|
expect_file: path.relative(".", path.join(TEST_DIR, name + ".wast")),
|
|
actual_file: path.relative(".", path.join(BUILD_DIR, name + ".actual.wast")),
|
|
actual_wasm: path.relative(".", path.join(BUILD_DIR, name + ".wasm")),
|
|
asm_file: path.join(TEST_DIR, name + ".asm"),
|
|
executable_file: path.join(BUILD_DIR, name + ".bin"),
|
|
};
|
|
}).filter(({ name }) => !TEST_NAME || name === TEST_NAME);
|
|
|
|
next_test(0);
|
|
|
|
function next_test(i)
|
|
{
|
|
if(files[i])
|
|
{
|
|
run_test(files[i], () => next_test(i + 1));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove parts that may not be stable between multiple runs
|
|
function normalise_wast(wast)
|
|
{
|
|
return wast.replace(/offset=(\d+)/g, function(match, offset)
|
|
{
|
|
offset = Number(offset);
|
|
|
|
if(offset >= MIN_MEMORY_OFFSET)
|
|
{
|
|
return "offset={normalised output}";
|
|
}
|
|
else
|
|
{
|
|
return match;
|
|
}
|
|
}).replace(/memory \$[\w\.]+ \d+/g, "memory {normalised output}");
|
|
}
|
|
|
|
function run_test({ name, executable_file, expect_file, actual_file, actual_wasm, asm_file }, onfinished)
|
|
{
|
|
const emulator = new V86({
|
|
autostart: false,
|
|
memory_size: 2 * 1024 * 1024,
|
|
log_level: LOG_LEVEL,
|
|
});
|
|
|
|
const executable = fs.readFileSync(executable_file);
|
|
const asm = fs.readFileSync(asm_file);
|
|
|
|
const is_32 = asm.includes("BITS 32\n");
|
|
|
|
emulator.add_listener("emulator-loaded", function()
|
|
{
|
|
const cpu = emulator.v86.cpu;
|
|
|
|
const hook_not_called_timeout = setTimeout(() => {
|
|
throw new Error("Hook for code generation not called");
|
|
}, 1000);
|
|
|
|
cpu.test_hook_did_generate_wasm = function(wasm)
|
|
{
|
|
const wast = normalise_wast(disassemble_wasm(wasm));
|
|
|
|
clearTimeout(hook_not_called_timeout);
|
|
fs.writeFileSync(actual_file, wast);
|
|
fs.writeFileSync(actual_wasm, wasm);
|
|
|
|
cpu.test_hook_did_generate_wasm = function()
|
|
{
|
|
cpu.test_hook_did_generate_wasm = function() {};
|
|
throw new Error("Hook for wasm generation called multiple times");
|
|
};
|
|
|
|
if(!fs.existsSync(expect_file))
|
|
{
|
|
// enhanced workflow: If file doesn't exist yet print full diff
|
|
var expect_file_for_diff = "/dev/null";
|
|
}
|
|
else
|
|
{
|
|
expect_file_for_diff = expect_file;
|
|
}
|
|
|
|
const result = spawnSync("git",
|
|
[].concat(
|
|
"diff",
|
|
GIT_DIFF_FLAGS,
|
|
expect_file_for_diff,
|
|
actual_file
|
|
),
|
|
{ encoding: "utf8" });
|
|
|
|
if(result.status)
|
|
{
|
|
console.log(result.stdout);
|
|
console.log(result.stderr);
|
|
|
|
if(process.argv.includes("--accept-all"))
|
|
{
|
|
console.log(`Running: cp ${actual_file} ${expect_file}`);
|
|
fs.copyFileSync(actual_file, expect_file);
|
|
}
|
|
else
|
|
{
|
|
const failure_message = `${name}.asm failed:
|
|
The code generator produced different code. If you believe this change is intentional,
|
|
verify the diff above and run the following command to accept the change:
|
|
|
|
cp ${actual_file} ${expect_file}
|
|
|
|
When done, re-run this test to confirm that all expect-tests pass.
|
|
|
|
Hint: Use tests/expect/run.js --accept-all to accept all changes (use git diff to verify).
|
|
`;
|
|
|
|
console.log(failure_message);
|
|
|
|
process.exit(1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
console.log("%s ok", name);
|
|
assert(!result.stdout);
|
|
assert(!result.stderr);
|
|
}
|
|
|
|
onfinished();
|
|
};
|
|
|
|
if(is_32)
|
|
{
|
|
cpu.is_32[0] = true;
|
|
cpu.stack_size_32[0] = true;
|
|
}
|
|
|
|
const START_ADDRESS = 0x1000;
|
|
|
|
cpu.mem8.set(executable, START_ADDRESS);
|
|
cpu.update_state_flags();
|
|
cpu.jit_force_generate(START_ADDRESS);
|
|
});
|
|
}
|
|
|
|
function disassemble_wasm(wasm)
|
|
{
|
|
// Need to make a small copy otherwise libwabt goes nuts trying to copy
|
|
// the whole underlying buffer
|
|
wasm = wasm.slice();
|
|
|
|
try
|
|
{
|
|
var module = libwabt.readWasm(wasm, { readDebugNames: false });
|
|
module.generateNames();
|
|
module.applyNames();
|
|
return module.toText({ foldExprs: true, inlineExport: true });
|
|
}
|
|
catch(e)
|
|
{
|
|
console.error("Error while running libwabt: " + e.toString());
|
|
console.error("Did you forget an ending hlt instruction?\n");
|
|
throw e;
|
|
}
|
|
finally
|
|
{
|
|
module && module.destroy();
|
|
}
|
|
}
|
|
|
|
run_all();
|