bareCallReceiver joined the ref's line with the seven after it for every
bare-recorded call, then cut the text at the ref's column. On a minified
bundle that line is the whole file, so every call copied it: about 1 MB
per call on go-ethereum's graphiql.min.js, 9 GB over that repo's call
refs.
It now searches the ref's own line from the column first. Every match of
the call pattern starts with the name, so a match there with no earlier
occurrence of the name on the line is where the joined lines match first
too. With no match on the line (a call that continues on the next one),
or with the name earlier on it (an earlier `name[…` or `name<…` can run
past the line break and enclose the match), the joined lines decide as
before. The result is the same for every input.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Indexing pretix took 2.2x as long since #2163 (issue #2334), and the
bundled minified d3 was what #2344 left of CPython's 1.6.2 slowdown. Two
JS/TS checks that first shipped in 1.6.2 redid whole-file work for every
reference:
- matchDestructuredCallResult (#2163) ran for every bare call in a file
holding `const {` / `let {` / `var {`: per call it re-tested that
pattern on the source, then stripped, blanked and brace-walked every
line above the call and matched the binding regex over it.
- jsFunctionLocalScope (#2226) re-stripped the calling function's lines
above each reference. In a minified script every function's text runs
to the end of its one line, so each strip was the whole file.
CPU profiles, main -> this change: matchDestructuredCallResult 82.4 s
-> 1.7 s and jsFunctionLocalScope 12.3 s -> 5.2 s on pretix; 35.4 s ->
0.37 s and 23.3 s -> 1.0 s on CPython's Lib/profiling + Doc/_static.
Now:
- A file's `const { … } = f(…)` bindings are read once per resolution:
the blanked code, each binding's end, callee and keys, the block it
sits in and where that block closes, and the spans the blanker read as
regex literals. A call looks up its own name; only a call through a
destructured name scans the code between that binding and itself for
shadowing, and only once the callee is found to return the key (a
`require(…)` destructuring, for one, needs no scan).
- Blanking the text above a call on its own gives the file's blanked code
up to the call: the stripper and blanker look past a character only for
a comment opener's second character and for a regex literal's closing
`/`. So the prefix's bindings are the file's that end at or before the
call (a match never reads past its `(`, and one running past the call
holds no `{`…`(` to start another), and its brace stack follows from
where each block closes. For a call inside a span the file reads as a
regex literal (a division, in minified code) the prefix is the code up
to the `/` plus the short tail blanked on its own; where a binding
statement could run across that `/` (inside its `{ … }` or `< … >`), and
for out-of-range positions, the old per-call scan still runs.
- jsFunctionLocalScope strips a function's lines once and cuts at the
reference's line end; functions that start on the same line share the
regex answer by (file, first line, reference line, name).
- The new memos, and JS_FN_LOCAL_MEMO (never cleared before), drop with
the other per-file memos in clearNameMatcherMemos.
The summary line printed orchestrator.indexAll's durationMs, which covers
extraction only, beside node and edge totals recomputed after resolution:
the issue's 19.9 s run printed "in 1.7s". CodeGraph.indexAll and sync now
report the whole run's wall time, which `codegraph init`, `index` and
`sync`, the MCP auto-sync log and the telemetry duration bucket read.
Verification: nodes, edges and unresolved_refs dumps keyed by
file|kind|qualified_name|start_line are byte-identical to origin/main
8998697f (with #2357's new destructuring and Vue template refs) on all
24 runs: pretix, mealie and CPython's Lib/profiling + Doc/_static, 8
each; and to deac7710 (#2358, #2360, #2361) on one more round of each. A differential harness ran old and new matchDestructuredCallResult
on every call position of mealie (10,330) and the CPython assets (9,170,
742 inside misread regex literals) and on 16,000 random token-soup
sources (1.33M positions), and old and new jsFunctionLocalScope on ~500K
(function, line, name) triples: no difference; it reports one for each
deliberate mutation of the new code that is not provably equivalent.
Medians of 4 interleaved runs on a loaded 16-thread Windows box, process
CPU / wall / peak RSS: pretix 160.2 s / 75.9 s / 2.47 GB -> 88.7 s /
43.5 s / 1.93 GB; CPython assets 75.1 s / 37.6 s / 1.23 GB -> 32.1 s /
20.4 s / 0.81 GB; mealie (no bundles, the control) 39.2 s -> 38.6 s CPU.
__tests__/js-resolution-work.test.ts counts strips: a file with a
destructured binding and 40 bare calls is stripped for the check once
(41 on main), a 40-line function once (40 on main); each count fails when
only its own half of the fix is reverted. full-pipeline.test.ts requires
indexAll's and sync's durationMs to cover a linking pass delayed 250 ms
past the orchestrator's time (on main: 218 < 468).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>