javascript: call-graph engine for the JavaScript front end (on reshape) - #472
Merged
Merged
Conversation
…in-repo parser, a JavaScript bundle adapter and vocabulary, bin/axiom-graph --language javascript Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… to graph/test/javascript, the command is bin/axiomcode-graph
… handed to a call and a handler reached from emit are in the graph; case 09 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… alias from the caller's cwd, which failed every solve started outside the repository Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… over constructed subclasses, prototype mixins, rest arrays, literal keys, Reflect.apply, emitter chaining, handler arguments, sort comparators Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… Object.create, express handlers), the overwrite flag read the right way round, a CommonJS require binding holds module.exports, library parameters untracked; a real express application scored against execution with node_modules instrumented Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ODE_MODULES_DEPTH); the scorer identifies staged library methods so client->library edges score like client->client Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ion callees, numeric/template/computed keys, new-returning-object, property descriptors and the OBJECT_DEFINE_PROPERTY export, promises, generators, JSDoc @this/casts/Map/Set/Promise generics, platform models, export * as ns, the anonymous default class, dotted heritage text, getters as values; ten probe cases join the suite Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* README: the front page a reader expects — tagline, quick start first, what you get, how it works, accuracy, layout, development The substance is unchanged (every measured number is kept); the order and shape follow what established projects do: what it is in one line, how to run it in three, then depth. Two open-source project names and two library names are dropped from the public text. * README: what it does and why coding agents need it, in plain language; the three trust properties; languages with their maturity
…ds Sup` takes the class the callers passed; scope_enclosing_method walks the block chain to the owning function Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… badge; language support with logos, parser/engine columns and maturity (#493)
…gets, dotted superclasses, every re-export's import, destructuring paths (#494) Fixes #484, #479, #487, #459; the parser half of #483. Parser suite 39/39, corpus 41/41, TypeScript 46/46, Python 21/21, gen_decls --check OK, scrub 0 hits, engine tsc clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…heritage link, missing extends expression, getters), the parser-blocked residue under 'What is not built' Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Sep 14, 2026
…on-formats table with parser/engine status (#495)
…ges move to a Status section (they cannot render while the repository is private) (#496)
…ava, C#); Status section follows Language support
…s walked from a value (pattern variables, pattern-parameter references, a destructured require's dotted importedName), type_super through the linked extends expression, Object.entries as pairs, the default-export recovery joins retired; five known-missing lists emptied Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
swapnilpaliwal-sd
force-pushed
the
main
branch
from
September 14, 2026 07:41
f1827c7 to
febf8b4
Compare
This was referenced Sep 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #577.
Tracking #456. Implements #457, #458, #459, #460, #461, #462, #463, #464, #467, the probe batch #480, #481, #482, #484, #485, #486, #488, #489, #490, #491, and the engine halves of #479, #483, #487 on top of the parser fix #494; #465 lists what is left.
Draft on purpose: nothing here merges until the engine is code-complete and the held-out evaluation is settled. The branch is the integration point; this PR carries the running evaluation.
What this is
A call-graph engine for the JavaScript front end, under
graph/javascript/, with its oracle, scorer, evaluation harness and regression suite undergraph/test/javascript/.JavaScript declares no types, so the engine is a may-analysis over values, not a type lookup: every expression, binding, parameter, property and return carries the set of values it may hold, a function, a class, an instance, a prototype, a module surface, an object literal, an array, or the platform, and a call site resolves through the callee's value. An untyped receiver produces nothing; the site is a declared unknown with its reason attached.
graph/javascript/README.mdhas the layer order and the decisions;PARSER-DEFECTS.mdthe ten IR gaps the rules met, six of them since fixed in the parser and their workarounds retired.How it is measured
checker.getResolvedSignaturefrom ats.Programbuilt withallowJs+checkJsover the project's own files, per site. Sites the compiler types asany(about half) enter no rate. Scoring is per site: EXACT / SOUND_SUPERSET / WRONG / MISSED over compiler-decided client→client sites; platform targets, implicit constructors, JSDoc-type targets and targets outside the IR are bucketed apart.Results (production code, per-site, compiler-decided client→client)
Development corpus, 15 projects, 9,867 decided sites:
Held-out corpus, 4 projects, 9,649 decided sites, validation only, no rule was derived from it:
The generalisation gap is 4.7 points of exact and 0.9 of recall, and it is almost entirely WIDTH on project C: 417 of its sites are sound supersets, nearly all 2-wide (the self-dispatch fan over a deep component hierarchy). WRONG is 7 sites in 9,649, and 0 in 9,867 on the development corpus.
The probe batch (#480–#491)
Ten synthetic probe programs, each written to break one part of the value model, were scored against both oracles before and after: class field initializers, getters as values, every heritage form, re-exports, promises, generators, JSDoc generics and casts, property descriptors, five callee spellings, a dozen platform models. After: every probe WRONG = 0;
i-fieldsand the qualified-heritage probe 1.000; the rest 0.83–0.98 exact with every remaining miss attributed to a parser gap. The parser then closed those gaps (#494: binding paths, every re-export's import row, the linkedextendsexpression, anonymous default targets) and the engine halves followed, a path walker for destructuring,type_superthrough theextendsexpression's value, the default-export recovery joins retired. Every site the probes listed now resolves except a setter's parameter (no SETTER_INVOCATION site exists) and a concatenated key. The two rounds moved the held-out corpus by +0.3 exact / +0.4 recall without a rule derived from it.Project B is the one that did not terminate before #467: the fan cap took it from >40 minutes to 9 seconds with development rates unchanged to three decimals. A second, smaller merge point surfaced after it, a write through a
@param {Object}-typed value landed on the ONE identity every such parameter shares, and is closed the same way (the platform's properties are not tracked).What is deliberately not resolved
Sites the compiler types as
any, 7,319 on the development corpus, 18,272 held-out, enter no rate. Platform targets (LIB_AMBIENT_OK6,040 / 5,561) are classified from the value reached, not the syntax; theLIB_MISSEDresidue (562 / 547) is almost entirely string/number/promise methods on the result of a project call, which nothing here types (#465). A fan wider than--dispatch-capis refused and declared (fan_capped), as in the other rule sets.A second oracle: execution
graph/test/javascript/torture/is a hand-written program exercising every invocation form the language has (classes, prototypes, mixins, closures, callbacks, events, promises, timers, registries,Proxy,Reflect, tagged templates, the module graph).instrument.mjsrewrites every function body to record(caller → callee)at entry with the caller carried in anAsyncLocalStorage, so a.thencontinuation, a timer callback or an event handler attributes to the function that registered it. Every executed edge is then checked against the engine's graph:known-missing.txt: an inner destructured default (PD-JS-5), twoProxytraps (the computed-key registries resolve since #490)callback_registered/event_dispatch/ self-dispatch supersets, none a fabricationWhat the runtime oracle found and this PR fixed: self dispatch (
this.area()in a base method runs the subclass override,thisnow fans over constructed subclasses, RTA-bounded), prototype mixins viaObject.assign, rest parameters as arrays,obj['name']with a literal key,Reflect.apply, emitter method chaining,emitarguments reaching handler parameters,sortcomparators. Callbacks and events are edges now: every function literal passed as an argument has an incomingcallback_registerededge (1,486 dev / 2,842 held-out, from 10% before), andemit('x')reaches itson('x', h)handlers (event_dispatch, 388 / 483 rows).The compiler and execution disagree on purpose in one place, for
this.area()in a base method tsc names the base declaration while execution names the override, so self dispatch costs 0.4 points of compiler-exact and is a sound superset there.A real application with its real dependencies
graph/test/javascript/realapp/is an the web framework service (routes, a param handler, middleware, an error handler, an EventEmitter-backed store, async handlers, the utility library, debug) with a pinnednode_modules. All 68 packages are parsed in place and staged as--library; the app is exercised end to end under the tracer withnode_modulesinstrumented too, so the edges FROM the web framework INTO the app's handlers are recorded.Object.definePropertygetter resolves since #489)What it took, all in
resolution/frameworks.dland value-flow:merge-descriptorsandsetprototypeofas assign-/inherit-like models (<subject>()gets its members by reflection,Router()its prototype);Object.createas an allocation; string literals as values (proto[method] = …over themethodspackage's array); arequire()binding holdingmodule.exports; the export-table overwrite flag read the right way round (it marks the LIVEmodule.exports =row, the engine had been dropping exactly that row); library parameters untracked (a utility library's parameters had merged every caller into 31,533 values per export); and an the web framework handler model,req/res/nextas values in the staged the web framework IR, stated as facts and checked here against what ran.The honest caveat: framework indirection is MODELLED, not inferred. The web framework is covered; another framework needs its own facts, which is how every practical JavaScript call-graph tool works.
Regression suite
Nineteen hand-written cases, nine by construction, ten converted from the probe programs, with edges and compiler adjudication pinned; the four sites still missing (an inner destructured default, a setter's parameter, a concatenated key) are each named in a
known-missingfile so they flip to a failure the day the IR carries them;--blesscannot bless away a MISSED or WRONG line.graph/test/javascript/run-tests.sh --oracle: 19/19.