From 8f61af220bf73fe58795de3770a26422bb922ef3 Mon Sep 17 00:00:00 2001 From: tantk Date: Mon, 6 Apr 2026 04:54:09 +0800 Subject: [PATCH] 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 --- .../src/core/ingestion/parsing-processor.ts | 22 +++++++++--------- gitnexus/src/core/ingestion/pipeline.ts | 23 ++++++++++--------- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/gitnexus/src/core/ingestion/parsing-processor.ts b/gitnexus/src/core/ingestion/parsing-processor.ts index ea4cecfb88..d9913e671a 100644 --- a/gitnexus/src/core/ingestion/parsing-processor.ts +++ b/gitnexus/src/core/ingestion/parsing-processor.ts @@ -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 diff --git a/gitnexus/src/core/ingestion/pipeline.ts b/gitnexus/src/core/ingestion/pipeline.ts index e53bcd14f9..e840e40255 100644 --- a/gitnexus/src/core/ingestion/pipeline.ts +++ b/gitnexus/src/core/ingestion/pipeline.ts @@ -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) {