88 lines
3.6 KiB
TypeScript
88 lines
3.6 KiB
TypeScript
/**
|
|
* WASM runtime flags — the workaround for the V8 turboshaft WASM Zone OOM
|
|
* (`Fatal process out of memory: Zone`) that crashed `codegraph index` on large
|
|
* polyglot repos under Node >= 22. See issues #293 and #298.
|
|
*
|
|
* The crash was reproduced with the real indexer on the bundled Node 24 runtime;
|
|
* empirically only `--liftoff-only` prevents it (`--no-wasm-tier-up` /
|
|
* `--no-wasm-dynamic-tiering` do not), and the flag must be on node's command
|
|
* line — `setFlagsFromString`, worker `execArgv`, and `NODE_OPTIONS` all fail.
|
|
* These tests pin that contract so it can't silently regress.
|
|
*/
|
|
import { describe, it, expect } from 'vitest';
|
|
import { spawnSync } from 'child_process';
|
|
import * as fs from 'fs';
|
|
import * as os from 'os';
|
|
import * as path from 'path';
|
|
import {
|
|
WASM_RUNTIME_FLAGS,
|
|
processHasWasmRuntimeFlags,
|
|
buildRelaunchArgv,
|
|
} from '../src/extraction/wasm-runtime-flags';
|
|
|
|
describe('WASM_RUNTIME_FLAGS', () => {
|
|
it('pins --liftoff-only (the only flag shown to stop the turboshaft Zone OOM)', () => {
|
|
// On Node 24, --no-wasm-tier-up and --no-wasm-dynamic-tiering both still
|
|
// crash; only --liftoff-only forces grammars onto the Liftoff baseline and
|
|
// off the optimizing tier. Pin it so it can't be swapped for an ineffective
|
|
// flag.
|
|
expect(WASM_RUNTIME_FLAGS).toContain('--liftoff-only');
|
|
});
|
|
|
|
it('every flag is a real, accepted flag on the running Node/V8 runtime', () => {
|
|
// node rejects unknown CLI flags at startup, so a renamed/removed flag would
|
|
// break the bundled launcher and make the relaunch guard a silent no-op.
|
|
// Prove each flag actually launches node here.
|
|
const res = spawnSync(
|
|
process.execPath,
|
|
[...WASM_RUNTIME_FLAGS, '-e', 'process.exit(0)'],
|
|
{ encoding: 'utf8' }
|
|
);
|
|
expect(res.status, `node rejected ${WASM_RUNTIME_FLAGS.join(' ')}:\n${res.stderr}`).toBe(0);
|
|
});
|
|
});
|
|
|
|
describe('processHasWasmRuntimeFlags', () => {
|
|
it('is true only when every required flag is present', () => {
|
|
expect(processHasWasmRuntimeFlags(['--liftoff-only'])).toBe(true);
|
|
expect(processHasWasmRuntimeFlags(['--liftoff-only', '--enable-source-maps'])).toBe(true);
|
|
});
|
|
|
|
it('is false when the flags are absent', () => {
|
|
expect(processHasWasmRuntimeFlags([])).toBe(false);
|
|
expect(processHasWasmRuntimeFlags(['--max-old-space-size=4096'])).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('buildRelaunchArgv', () => {
|
|
it('places the wasm flags first, then the script and its args', () => {
|
|
expect(buildRelaunchArgv('/x/codegraph.js', ['index', '/repo'], [])).toEqual([
|
|
'--liftoff-only',
|
|
'/x/codegraph.js',
|
|
'index',
|
|
'/repo',
|
|
]);
|
|
});
|
|
|
|
it('preserves other existing node flags without duplicating ours', () => {
|
|
expect(
|
|
buildRelaunchArgv('/x/codegraph.js', ['status'], ['--liftoff-only', '--enable-source-maps'])
|
|
).toEqual(['--liftoff-only', '--enable-source-maps', '/x/codegraph.js', 'status']);
|
|
});
|
|
|
|
it('produces an argv that actually launches node WITH the flag applied', () => {
|
|
// End-to-end proof of the delivery mechanism without needing the crash:
|
|
// run the constructed argv and confirm the child sees the flag in execArgv.
|
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-relaunch-'));
|
|
try {
|
|
const harness = path.join(dir, 'harness.cjs');
|
|
fs.writeFileSync(harness, 'process.stdout.write(JSON.stringify(process.execArgv));');
|
|
const res = spawnSync(process.execPath, buildRelaunchArgv(harness, []), { encoding: 'utf8' });
|
|
expect(res.status, res.stderr).toBe(0);
|
|
expect(JSON.parse(res.stdout)).toContain('--liftoff-only');
|
|
} finally {
|
|
fs.rmSync(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
});
|