From 8165571c95f15461e7d9e9d2d2197c3ec53a80bf Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Wed, 4 Mar 2026 19:28:41 -0700 Subject: [PATCH 1/5] perf: skip ensureWasmTrees when native engine provides complete data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before calling ensureWasmTrees, check whether native engine already supplies CFG and dataflow data for all files. When it does, skip the WASM pre-parse entirely — avoiding a full WASM parse of every file on native builds where the data is already available. Impact: 1 functions changed, 14 affected --- src/builder.js | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/src/builder.js b/src/builder.js index c5019b4..28637dc 100644 --- a/src/builder.js +++ b/src/builder.js @@ -1317,16 +1317,44 @@ export async function buildGraph(rootDir, opts = {}) { _t.complexityMs = performance.now() - _t.complexity0; // Pre-parse files missing WASM trees (native builds) so CFG + dataflow - // share a single parse pass instead of each creating parsers independently + // share a single parse pass instead of each creating parsers independently. + // Skip entirely when native engine already provides CFG + dataflow data. if (opts.cfg !== false || opts.dataflow !== false) { - _t.wasmPre0 = performance.now(); - try { - const { ensureWasmTrees } = await import('./parser.js'); - await ensureWasmTrees(astComplexitySymbols, rootDir); - } catch (err) { - debug(`WASM pre-parse failed: ${err.message}`); + const needsCfg = opts.cfg !== false; + const needsDataflow = opts.dataflow !== false; + + let needsWasmTrees = false; + for (const [, symbols] of astComplexitySymbols) { + if (symbols._tree) continue; // already has a tree + // CFG: need tree if any function/method def lacks native CFG + if (needsCfg) { + const fnDefs = symbols.definitions.filter( + (d) => (d.kind === 'function' || d.kind === 'method') && d.line, + ); + if (fnDefs.length > 0 && !fnDefs.every((d) => d.cfg?.blocks?.length)) { + needsWasmTrees = true; + break; + } + } + // Dataflow: need tree if file lacks native dataflow + if (needsDataflow && !symbols.dataflow) { + needsWasmTrees = true; + break; + } + } + + if (needsWasmTrees) { + _t.wasmPre0 = performance.now(); + try { + const { ensureWasmTrees } = await import('./parser.js'); + await ensureWasmTrees(astComplexitySymbols, rootDir); + } catch (err) { + debug(`WASM pre-parse failed: ${err.message}`); + } + _t.wasmPreMs = performance.now() - _t.wasmPre0; + } else { + _t.wasmPreMs = 0; } - _t.wasmPreMs = performance.now() - _t.wasmPre0; } // CFG analysis (skip with --no-cfg) From c061308f3983a55c16bb0fd578a0cc59dd47babb Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Wed, 4 Mar 2026 20:09:58 -0700 Subject: [PATCH 2/5] =?UTF-8?q?fix:=20address=20PR=20#344=20review=20comme?= =?UTF-8?q?nts=20=E2=80=94=20TODO=20for=20constant=20exclusion,=20complete?= =?UTF-8?q?=20changelog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 7 +++++++ tests/integration/build-parity.test.js | 2 ++ 2 files changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4573de3..3770c71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,16 @@ All notable changes to this project will be documented in this file. See [commit ## [3.0.3](https://github.com/optave/codegraph/compare/v3.0.2...v3.0.3) (2026-03-04) +> **Note:** 3.0.2 was an internal/unpublished version used during development. + ### Performance * **ast:** use single transaction for AST node insertion — astMs drops from ~3600ms to ~350ms (native) and ~547ms (WASM), reducing overall native build from 24.9 to 8.5 ms/file ([#333](https://github.com/optave/codegraph/pull/333)) +* **builder:** skip `ensureWasmTrees` when native engine provides complete CFG + dataflow data ([#344](https://github.com/optave/codegraph/pull/344)) + +### Bug Fixes + +* **native:** fix function-scoped `const` declarations being incorrectly extracted as top-level constants ([#344](https://github.com/optave/codegraph/pull/344)) ## [3.0.2](https://github.com/optave/codegraph/compare/v3.0.1...v3.0.2) (2026-03-04) diff --git a/tests/integration/build-parity.test.js b/tests/integration/build-parity.test.js index 566d201..d2f2e8a 100644 --- a/tests/integration/build-parity.test.js +++ b/tests/integration/build-parity.test.js @@ -35,6 +35,8 @@ function readGraph(dbPath) { // Exclude constant nodes — the native engine has a known scope bug where it // extracts local `const` variables inside functions as top-level constants, // while WASM correctly limits constant extraction to program-level declarations. + // TODO: Remove kind != 'constant' exclusion once native binary >= 3.0.4 ships + // Fix: crates/codegraph-core/src/extractors/javascript.rs (find_parent_of_types guard) const nodes = db .prepare( "SELECT name, kind, file, line FROM nodes WHERE kind != 'constant' ORDER BY name, kind, file, line", From 37ee518b11fd5c49d637eabacdf2badf265f4184 Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Wed, 4 Mar 2026 20:24:41 -0700 Subject: [PATCH 3/5] fix: add null guard on symbols.definitions in pre-parse check Impact: 1 functions changed, 0 affected --- src/builder.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/builder.js b/src/builder.js index 28637dc..c921c78 100644 --- a/src/builder.js +++ b/src/builder.js @@ -1328,7 +1328,7 @@ export async function buildGraph(rootDir, opts = {}) { if (symbols._tree) continue; // already has a tree // CFG: need tree if any function/method def lacks native CFG if (needsCfg) { - const fnDefs = symbols.definitions.filter( + const fnDefs = (symbols.definitions || []).filter( (d) => (d.kind === 'function' || d.kind === 'method') && d.line, ); if (fnDefs.length > 0 && !fnDefs.every((d) => d.cfg?.blocks?.length)) { From 2f4a7d837bfb18340cf1435c4a5c318d9db8ceb2 Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Wed, 4 Mar 2026 23:00:38 -0700 Subject: [PATCH 4/5] fix: treat empty cfg.blocks array as valid native CFG d.cfg?.blocks?.length evaluates to 0 (falsy) when the native engine returns cfg: { blocks: [] } for trivial functions, spuriously triggering needsWasmTrees. Use Array.isArray(d.cfg?.blocks) instead, and preserve d.cfg === null for nodes that intentionally have no CFG (e.g. interface members). Impact: 1 functions changed, 1 affected --- src/builder.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/builder.js b/src/builder.js index c921c78..c707877 100644 --- a/src/builder.js +++ b/src/builder.js @@ -1331,7 +1331,7 @@ export async function buildGraph(rootDir, opts = {}) { const fnDefs = (symbols.definitions || []).filter( (d) => (d.kind === 'function' || d.kind === 'method') && d.line, ); - if (fnDefs.length > 0 && !fnDefs.every((d) => d.cfg?.blocks?.length)) { + if (fnDefs.length > 0 && !fnDefs.every((d) => d.cfg === null || Array.isArray(d.cfg?.blocks))) { needsWasmTrees = true; break; } From f3700aa1983f9154b18b0de06c9e73afcd3fdb5d Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Wed, 4 Mar 2026 23:00:51 -0700 Subject: [PATCH 5/5] style: format cfg check per biome rules Impact: 1 functions changed, 0 affected --- src/builder.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/builder.js b/src/builder.js index c707877..5eb48d7 100644 --- a/src/builder.js +++ b/src/builder.js @@ -1331,7 +1331,10 @@ export async function buildGraph(rootDir, opts = {}) { const fnDefs = (symbols.definitions || []).filter( (d) => (d.kind === 'function' || d.kind === 'method') && d.line, ); - if (fnDefs.length > 0 && !fnDefs.every((d) => d.cfg === null || Array.isArray(d.cfg?.blocks))) { + if ( + fnDefs.length > 0 && + !fnDefs.every((d) => d.cfg === null || Array.isArray(d.cfg?.blocks)) + ) { needsWasmTrees = true; break; }