fix: replace Array.push(...spread) with loop to prevent stack overflow (#650)

* fix: replace Array.push(...spread) with loop to prevent stack overflow

On large codebases (78K+ C files), deferred arrays in
runChunkedParseAndResolve accumulate 100K+ entries. The spread
operator in push(...array) puts every element on the call stack
as a function argument, exceeding the maximum call stack size.

Replace all 11 occurrences of `arr.push(...other)` with
`for (const _item of other) arr.push(_item)` which uses
constant stack space regardless of array size.

Fixes #649

* fix: also replace push(...spread) in parsing-processor.ts

* style: format pipeline.ts to match prettier config

---------

Co-authored-by: tian kian tan <tan@example.com>
This commit is contained in:
tantk 2026-04-06 04:54:09 +08:00 • committed by GitHub
parent 0ee7fc8808
commit 8f61af220b
2 changed files with 23 additions and 22 deletions

View file

@ -143,17 +143,17 @@ const processParsingWithWorkers = async (
});
}
allImports.push(...result.imports);
allCalls.push(...result.calls);
allAssignments.push(...result.assignments);
allHeritage.push(...result.heritage);
allRoutes.push(...result.routes);
allFetchCalls.push(...result.fetchCalls);
allDecoratorRoutes.push(...result.decoratorRoutes);
allToolDefs.push(...result.toolDefs);
if (result.ormQueries) allORMQueries.push(...result.ormQueries);
allConstructorBindings.push(...result.constructorBindings);
allTypeEnvBindings.push(...result.typeEnvBindings);
for (const _item of result.imports) allImports.push(_item);
for (const _item of result.calls) allCalls.push(_item);
for (const _item of result.assignments) allAssignments.push(_item);
for (const _item of result.heritage) allHeritage.push(_item);
for (const _item of result.routes) allRoutes.push(_item);
for (const _item of result.fetchCalls) allFetchCalls.push(_item);
for (const _item of result.decoratorRoutes) allDecoratorRoutes.push(_item);
for (const _item of result.toolDefs) allToolDefs.push(_item);
if (result.ormQueries) for (const _item of result.ormQueries) allORMQueries.push(_item);
for (const _item of result.constructorBindings) allConstructorBindings.push(_item);
for (const _item of result.typeEnvBindings) allTypeEnvBindings.push(_item);
}
// Merge and log skipped languages from workers

View file

@ -879,11 +879,12 @@ async function runChunkedParseAndResolve(
);
}
}
deferredWorkerCalls.push(...chunkWorkerData.calls);
deferredWorkerHeritage.push(...chunkWorkerData.heritage);
deferredConstructorBindings.push(...chunkWorkerData.constructorBindings);
for (const _item of chunkWorkerData.calls) deferredWorkerCalls.push(_item);
for (const _item of chunkWorkerData.heritage) deferredWorkerHeritage.push(_item);
for (const _item of chunkWorkerData.constructorBindings)
deferredConstructorBindings.push(_item);
if (chunkWorkerData.assignments?.length) {
deferredAssignments.push(...chunkWorkerData.assignments);
for (const _item of chunkWorkerData.assignments) deferredAssignments.push(_item);
}
// Heritage + Routes — calls deferred until all chunks have contributed heritage
@ -918,23 +919,23 @@ async function runChunkedParseAndResolve(
]);
// Collect TypeEnv file-scope bindings for exported type enrichment
if (chunkWorkerData.typeEnvBindings?.length) {
workerTypeEnvBindings.push(...chunkWorkerData.typeEnvBindings);
for (const _item of chunkWorkerData.typeEnvBindings) workerTypeEnvBindings.push(_item);
}
// Collect fetch() calls for Next.js route matching
if (chunkWorkerData.fetchCalls?.length) {
allFetchCalls.push(...chunkWorkerData.fetchCalls);
for (const _item of chunkWorkerData.fetchCalls) allFetchCalls.push(_item);
}
if (chunkWorkerData.routes?.length) {
allExtractedRoutes.push(...chunkWorkerData.routes);
for (const _item of chunkWorkerData.routes) allExtractedRoutes.push(_item);
}
if (chunkWorkerData.decoratorRoutes?.length) {
allDecoratorRoutes.push(...chunkWorkerData.decoratorRoutes);
for (const _item of chunkWorkerData.decoratorRoutes) allDecoratorRoutes.push(_item);
}
if (chunkWorkerData.toolDefs?.length) {
allToolDefs.push(...chunkWorkerData.toolDefs);
for (const _item of chunkWorkerData.toolDefs) allToolDefs.push(_item);
}
if (chunkWorkerData.ormQueries?.length) {
allORMQueries.push(...chunkWorkerData.ormQueries);
for (const _item of chunkWorkerData.ormQueries) allORMQueries.push(_item);
}
} else {
await processImports(graph, chunkFiles, astCache, ctx, undefined, repoPath, allPaths);
@ -1023,7 +1024,7 @@ async function runChunkedParseAndResolve(
// Extract fetch() calls for Next.js route matching (sequential path)
const chunkFetchCalls = await extractFetchCallsFromFiles(chunkFiles, astCache);
if (chunkFetchCalls.length > 0) {
allFetchCalls.push(...chunkFetchCalls);
for (const _item of chunkFetchCalls) allFetchCalls.push(_item);
}
// Extract ORM queries (sequential path)
for (const f of chunkFiles) {