| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687 |
- /**
- * 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 });
- }
- });
- });
|