Skip to content

Commit 4b4545c

Browse files
committed
fix: free leaked WASM trees in native engine typeMap backfill
The typeMap backfill path in parseFilesAuto and backfillTypeMap called wasmExtractSymbols but never freed the returned WASM tree objects. Over repeated builds (benchmarks, watch mode), hundreds of trees accumulated in WASM linear memory, eventually corrupting V8 state and crashing the native addon with ACCESS_VIOLATION / has_exception(). Two fixes: 1. Free WASM trees immediately after extracting typeMap data in both backfillTypeMap() and the parseFilesAuto() bulk backfill loop. 2. Skip backfill entirely for JS files — only TS/TSX have type annotations that WASM can extract. The native engine already handles JS `new Expr()` patterns, so re-parsing all JS files with WASM was pure waste. Closes #530 Impact: 2 functions changed, 2 affected
1 parent 73d755f commit 4b4545c

1 file changed

Lines changed: 43 additions & 15 deletions

File tree

src/domain/parser.js

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -395,8 +395,22 @@ async function backfillTypeMap(filePath, source) {
395395
}
396396
const parsers = await createParsers();
397397
const extracted = wasmExtractSymbols(parsers, filePath, code);
398-
if (!extracted?.symbols?.typeMap) return { typeMap: [], backfilled: false };
398+
if (!extracted?.symbols?.typeMap) {
399+
// Free the WASM tree to prevent memory accumulation across repeated builds
400+
if (extracted?.tree && typeof extracted.tree.delete === 'function') {
401+
try {
402+
extracted.tree.delete();
403+
} catch {}
404+
}
405+
return { typeMap: [], backfilled: false };
406+
}
399407
const tm = extracted.symbols.typeMap;
408+
// Free the WASM tree — only the typeMap data is needed
409+
if (extracted.tree && typeof extracted.tree.delete === 'function') {
410+
try {
411+
extracted.tree.delete();
412+
} catch {}
413+
}
400414
return {
401415
typeMap: tm instanceof Map ? tm : new Map(tm.map((e) => [e.name, e.typeName])),
402416
backfilled: true,
@@ -486,21 +500,35 @@ export async function parseFilesAuto(filePaths, rootDir, opts = {}) {
486500
}
487501
// Backfill typeMap via WASM for native binaries that predate the type-map feature
488502
if (needsTypeMap.length > 0) {
489-
const parsers = await createParsers();
490-
for (const { filePath, relPath } of needsTypeMap) {
491-
try {
492-
const code = fs.readFileSync(filePath, 'utf-8');
493-
const extracted = wasmExtractSymbols(parsers, filePath, code);
494-
if (extracted?.symbols?.typeMap) {
495-
const symbols = result.get(relPath);
496-
symbols.typeMap =
497-
extracted.symbols.typeMap instanceof Map
498-
? extracted.symbols.typeMap
499-
: new Map(extracted.symbols.typeMap.map((e) => [e.name, e.typeName]));
500-
symbols._typeMapBackfilled = true;
503+
// Only backfill for languages where WASM extraction can produce typeMap
504+
// (TS/TSX have type annotations; JS only has `new Expr()` which native already handles)
505+
const TS_EXTS = new Set(['.ts', '.tsx']);
506+
const tsFiles = needsTypeMap.filter(({ filePath }) => TS_EXTS.has(path.extname(filePath)));
507+
if (tsFiles.length > 0) {
508+
const parsers = await createParsers();
509+
for (const { filePath, relPath } of tsFiles) {
510+
let extracted;
511+
try {
512+
const code = fs.readFileSync(filePath, 'utf-8');
513+
extracted = wasmExtractSymbols(parsers, filePath, code);
514+
if (extracted?.symbols?.typeMap) {
515+
const symbols = result.get(relPath);
516+
symbols.typeMap =
517+
extracted.symbols.typeMap instanceof Map
518+
? extracted.symbols.typeMap
519+
: new Map(extracted.symbols.typeMap.map((e) => [e.name, e.typeName]));
520+
symbols._typeMapBackfilled = true;
521+
}
522+
} catch {
523+
/* skip — typeMap is a best-effort backfill */
524+
} finally {
525+
// Free the WASM tree to prevent memory accumulation across repeated builds
526+
if (extracted?.tree && typeof extracted.tree.delete === 'function') {
527+
try {
528+
extracted.tree.delete();
529+
} catch {}
530+
}
501531
}
502-
} catch {
503-
/* skip — typeMap is a best-effort backfill */
504532
}
505533
}
506534
}

0 commit comments

Comments
 (0)