@optave/codegraph 3.11.1 → 3.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -8
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/migrations.js +7 -0
- package/dist/db/migrations.js.map +1 -1
- package/dist/domain/analysis/module-map.d.ts +2 -0
- package/dist/domain/analysis/module-map.d.ts.map +1 -1
- package/dist/domain/analysis/module-map.js +24 -2
- package/dist/domain/analysis/module-map.js.map +1 -1
- package/dist/domain/graph/builder/call-resolver.d.ts +73 -0
- package/dist/domain/graph/builder/call-resolver.d.ts.map +1 -0
- package/dist/domain/graph/builder/call-resolver.js +292 -0
- package/dist/domain/graph/builder/call-resolver.js.map +1 -0
- package/dist/domain/graph/builder/cha.d.ts +61 -0
- package/dist/domain/graph/builder/cha.d.ts.map +1 -0
- package/dist/domain/graph/builder/cha.js +143 -0
- package/dist/domain/graph/builder/cha.js.map +1 -0
- package/dist/domain/graph/builder/context.d.ts +3 -0
- package/dist/domain/graph/builder/context.d.ts.map +1 -1
- package/dist/domain/graph/builder/context.js +2 -0
- package/dist/domain/graph/builder/context.js.map +1 -1
- package/dist/domain/graph/builder/helpers.d.ts +17 -1
- package/dist/domain/graph/builder/helpers.d.ts.map +1 -1
- package/dist/domain/graph/builder/helpers.js +159 -5
- package/dist/domain/graph/builder/helpers.js.map +1 -1
- package/dist/domain/graph/builder/incremental.d.ts.map +1 -1
- package/dist/domain/graph/builder/incremental.js +147 -54
- package/dist/domain/graph/builder/incremental.js.map +1 -1
- package/dist/domain/graph/builder/stages/build-edges.d.ts +2 -0
- package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/build-edges.js +932 -110
- package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
- package/dist/domain/graph/builder/stages/detect-changes.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/detect-changes.js +2 -1
- package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
- package/dist/domain/graph/builder/stages/native-orchestrator.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/native-orchestrator.js +501 -14
- package/dist/domain/graph/builder/stages/native-orchestrator.js.map +1 -1
- package/dist/domain/graph/builder/stages/resolve-imports.d.ts +1 -0
- package/dist/domain/graph/builder/stages/resolve-imports.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/resolve-imports.js +9 -0
- package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
- package/dist/domain/graph/journal.js +1 -1
- package/dist/domain/graph/journal.js.map +1 -1
- package/dist/domain/graph/resolver/points-to.d.ts +53 -0
- package/dist/domain/graph/resolver/points-to.d.ts.map +1 -0
- package/dist/domain/graph/resolver/points-to.js +213 -0
- package/dist/domain/graph/resolver/points-to.js.map +1 -0
- package/dist/domain/graph/resolver/ts-resolver.d.ts +9 -0
- package/dist/domain/graph/resolver/ts-resolver.d.ts.map +1 -0
- package/dist/domain/graph/resolver/ts-resolver.js +476 -0
- package/dist/domain/graph/resolver/ts-resolver.js.map +1 -0
- package/dist/domain/graph/watcher.d.ts.map +1 -1
- package/dist/domain/graph/watcher.js +5 -2
- package/dist/domain/graph/watcher.js.map +1 -1
- package/dist/domain/parser.d.ts +10 -1
- package/dist/domain/parser.d.ts.map +1 -1
- package/dist/domain/parser.js +39 -7
- package/dist/domain/parser.js.map +1 -1
- package/dist/domain/wasm-worker-entry.js +25 -0
- package/dist/domain/wasm-worker-entry.js.map +1 -1
- package/dist/domain/wasm-worker-pool.d.ts.map +1 -1
- package/dist/domain/wasm-worker-pool.js +32 -0
- package/dist/domain/wasm-worker-pool.js.map +1 -1
- package/dist/domain/wasm-worker-protocol.d.ts +14 -1
- package/dist/domain/wasm-worker-protocol.d.ts.map +1 -1
- package/dist/extractors/c.js +3 -3
- package/dist/extractors/c.js.map +1 -1
- package/dist/extractors/clojure.js +1 -1
- package/dist/extractors/clojure.js.map +1 -1
- package/dist/extractors/cpp.js +3 -3
- package/dist/extractors/cpp.js.map +1 -1
- package/dist/extractors/csharp.d.ts.map +1 -1
- package/dist/extractors/csharp.js +37 -8
- package/dist/extractors/csharp.js.map +1 -1
- package/dist/extractors/cuda.js +3 -3
- package/dist/extractors/cuda.js.map +1 -1
- package/dist/extractors/elixir.js +6 -6
- package/dist/extractors/elixir.js.map +1 -1
- package/dist/extractors/fsharp.js +1 -1
- package/dist/extractors/fsharp.js.map +1 -1
- package/dist/extractors/go.js +5 -5
- package/dist/extractors/go.js.map +1 -1
- package/dist/extractors/haskell.js +1 -1
- package/dist/extractors/haskell.js.map +1 -1
- package/dist/extractors/java.js +2 -2
- package/dist/extractors/java.js.map +1 -1
- package/dist/extractors/javascript.d.ts +2 -0
- package/dist/extractors/javascript.d.ts.map +1 -1
- package/dist/extractors/javascript.js +1674 -64
- package/dist/extractors/javascript.js.map +1 -1
- package/dist/extractors/kotlin.js +5 -5
- package/dist/extractors/kotlin.js.map +1 -1
- package/dist/extractors/lua.js +1 -1
- package/dist/extractors/lua.js.map +1 -1
- package/dist/extractors/objc.js +3 -3
- package/dist/extractors/objc.js.map +1 -1
- package/dist/extractors/ocaml.js +1 -1
- package/dist/extractors/ocaml.js.map +1 -1
- package/dist/extractors/php.js +2 -2
- package/dist/extractors/php.js.map +1 -1
- package/dist/extractors/python.js +7 -7
- package/dist/extractors/python.js.map +1 -1
- package/dist/extractors/ruby.js +2 -2
- package/dist/extractors/ruby.js.map +1 -1
- package/dist/extractors/scala.js +1 -1
- package/dist/extractors/scala.js.map +1 -1
- package/dist/extractors/solidity.js +1 -1
- package/dist/extractors/solidity.js.map +1 -1
- package/dist/extractors/swift.js +4 -4
- package/dist/extractors/swift.js.map +1 -1
- package/dist/extractors/zig.js +4 -4
- package/dist/extractors/zig.js.map +1 -1
- package/dist/features/structure.d.ts.map +1 -1
- package/dist/features/structure.js +121 -16
- package/dist/features/structure.js.map +1 -1
- package/dist/infrastructure/config.d.ts +10 -0
- package/dist/infrastructure/config.d.ts.map +1 -1
- package/dist/infrastructure/config.js +15 -0
- package/dist/infrastructure/config.js.map +1 -1
- package/dist/infrastructure/native.d.ts +11 -0
- package/dist/infrastructure/native.d.ts.map +1 -1
- package/dist/infrastructure/native.js +78 -5
- package/dist/infrastructure/native.js.map +1 -1
- package/dist/presentation/queries-cli/overview.d.ts.map +1 -1
- package/dist/presentation/queries-cli/overview.js +5 -0
- package/dist/presentation/queries-cli/overview.js.map +1 -1
- package/dist/types.d.ts +184 -0
- package/dist/types.d.ts.map +1 -1
- package/grammars/tree-sitter-erlang.wasm +0 -0
- package/package.json +9 -9
- package/src/db/migrations.ts +7 -0
- package/src/domain/analysis/module-map.ts +29 -1
- package/src/domain/graph/builder/call-resolver.ts +351 -0
- package/src/domain/graph/builder/cha.ts +175 -0
- package/src/domain/graph/builder/context.ts +3 -0
- package/src/domain/graph/builder/helpers.ts +175 -5
- package/src/domain/graph/builder/incremental.ts +186 -66
- package/src/domain/graph/builder/stages/build-edges.ts +1146 -146
- package/src/domain/graph/builder/stages/detect-changes.ts +3 -1
- package/src/domain/graph/builder/stages/native-orchestrator.ts +583 -20
- package/src/domain/graph/builder/stages/resolve-imports.ts +14 -0
- package/src/domain/graph/journal.ts +1 -1
- package/src/domain/graph/resolver/points-to.ts +254 -0
- package/src/domain/graph/resolver/ts-resolver.ts +536 -0
- package/src/domain/graph/watcher.ts +4 -2
- package/src/domain/parser.ts +43 -5
- package/src/domain/wasm-worker-entry.ts +25 -0
- package/src/domain/wasm-worker-pool.ts +21 -0
- package/src/domain/wasm-worker-protocol.ts +14 -0
- package/src/extractors/c.ts +3 -3
- package/src/extractors/clojure.ts +1 -1
- package/src/extractors/cpp.ts +3 -3
- package/src/extractors/csharp.ts +33 -9
- package/src/extractors/cuda.ts +3 -3
- package/src/extractors/elixir.ts +6 -6
- package/src/extractors/fsharp.ts +1 -1
- package/src/extractors/go.ts +5 -5
- package/src/extractors/haskell.ts +1 -1
- package/src/extractors/java.ts +2 -2
- package/src/extractors/javascript.ts +1802 -66
- package/src/extractors/kotlin.ts +5 -5
- package/src/extractors/lua.ts +1 -1
- package/src/extractors/objc.ts +3 -3
- package/src/extractors/ocaml.ts +1 -1
- package/src/extractors/php.ts +2 -2
- package/src/extractors/python.ts +7 -7
- package/src/extractors/ruby.ts +2 -2
- package/src/extractors/scala.ts +1 -1
- package/src/extractors/solidity.ts +1 -1
- package/src/extractors/swift.ts +4 -4
- package/src/extractors/zig.ts +4 -4
- package/src/features/structure.ts +143 -23
- package/src/infrastructure/config.ts +15 -0
- package/src/infrastructure/native.ts +87 -5
- package/src/presentation/queries-cli/overview.ts +15 -1
- package/src/types.ts +194 -0
|
@@ -169,9 +169,23 @@ async function reparseBarrelFiles(
|
|
|
169
169
|
return added;
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
+
export function resolveBarrelExportCached(
|
|
173
|
+
ctx: PipelineContext,
|
|
174
|
+
barrelPath: string,
|
|
175
|
+
symbolName: string,
|
|
176
|
+
): string | null {
|
|
177
|
+
const cacheKey = `${barrelPath}|${symbolName}`;
|
|
178
|
+
if (ctx.barrelExportCache.has(cacheKey))
|
|
179
|
+
return ctx.barrelExportCache.get(cacheKey) as string | null;
|
|
180
|
+
const result = resolveBarrelExport(ctx, barrelPath, symbolName);
|
|
181
|
+
ctx.barrelExportCache.set(cacheKey, result);
|
|
182
|
+
return result;
|
|
183
|
+
}
|
|
184
|
+
|
|
172
185
|
export async function resolveImports(ctx: PipelineContext): Promise<void> {
|
|
173
186
|
const { fileSymbols, rootDir, aliases, allFiles, isFullBuild } = ctx;
|
|
174
187
|
const t0 = performance.now();
|
|
188
|
+
ctx.barrelExportCache = new Map();
|
|
175
189
|
|
|
176
190
|
const batchInputs: Array<{ fromFile: string; importSource: string }> = [];
|
|
177
191
|
for (const [relPath, symbols] of fileSymbols) {
|
|
@@ -235,7 +235,7 @@ interface JournalResult {
|
|
|
235
235
|
}
|
|
236
236
|
|
|
237
237
|
function parseJournalHeader(firstLine: string | undefined): number | null {
|
|
238
|
-
if (!firstLine
|
|
238
|
+
if (!firstLine?.startsWith(HEADER_PREFIX)) {
|
|
239
239
|
debug('Journal has malformed or missing header');
|
|
240
240
|
return null;
|
|
241
241
|
}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 8.3 — Lightweight field-based points-to analysis for JS/TS.
|
|
3
|
+
*
|
|
4
|
+
* Resolves higher-order function calls where a named variable is an alias for
|
|
5
|
+
* a function that the syntactic extractor cannot connect directly. Common
|
|
6
|
+
* patterns resolved:
|
|
7
|
+
*
|
|
8
|
+
* const fn = handler; arr.map(fn) → edge to handler
|
|
9
|
+
* const fn = obj.method; router.use(fn) → edge to obj.method
|
|
10
|
+
* const m = authMiddleware; app.use(m) → edge to authMiddleware
|
|
11
|
+
*
|
|
12
|
+
* Algorithm: Andersen-style inclusion-based analysis with allocation-site
|
|
13
|
+
* abstraction and fixed-point constraint propagation.
|
|
14
|
+
*
|
|
15
|
+
* Field-based (not field-sensitive): all instances of obj.field are treated as
|
|
16
|
+
* a single abstract location, matching ACG's sweet spot of 99% precision.
|
|
17
|
+
*
|
|
18
|
+
* Scope: intra-module + cross-module via importedNames (the importedNames map
|
|
19
|
+
* that build-edges.ts already builds per file is the cross-module link — if
|
|
20
|
+
* a variable aliases an imported name, resolveCallTargets follows it).
|
|
21
|
+
*/
|
|
22
|
+
import type {
|
|
23
|
+
ArrayCallbackBinding,
|
|
24
|
+
ArrayElemBinding,
|
|
25
|
+
FnRefBinding,
|
|
26
|
+
ForOfBinding,
|
|
27
|
+
ObjectPropBinding,
|
|
28
|
+
ObjectRestParamBinding,
|
|
29
|
+
ParamBinding,
|
|
30
|
+
SpreadArgBinding,
|
|
31
|
+
} from '../../../types.js';
|
|
32
|
+
|
|
33
|
+
export type PointsToMap = Map<string, Set<string>>;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Maximum fixed-point iterations before bailing out (prevents divergence).
|
|
37
|
+
* Mirrors `DEFAULTS.analysis.pointsToMaxIterations` in config.ts.
|
|
38
|
+
* TODO(Phase 8.3): thread config through buildPointsToMap so this can be tuned
|
|
39
|
+
* per-repo via `.codegraphrc.json` (tracked alongside typePropagationDepth).
|
|
40
|
+
*/
|
|
41
|
+
const MAX_SOLVER_ITERATIONS = 50;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Build a points-to map for one file.
|
|
45
|
+
*
|
|
46
|
+
* Seeds concrete function names (locally-defined functions + imported names),
|
|
47
|
+
* then propagates assignments through fixed-point iteration until stable.
|
|
48
|
+
*
|
|
49
|
+
* Each "concrete target" in a pts set is a name that `resolveCallTargets` can
|
|
50
|
+
* look up — either a locally-defined function name (found via byNameAndFile) or
|
|
51
|
+
* an imported name (found via importedNames → byNameAndFile in the source file).
|
|
52
|
+
*
|
|
53
|
+
* @param fnRefBindings - identifier/member-expr bindings from the extractor
|
|
54
|
+
* @param definitionNames - locally-defined callable names in this file
|
|
55
|
+
* @param importedNames - names imported into this file (name → resolved file)
|
|
56
|
+
* @param paramBindings - call-site arg→param bindings (Phase 8.3c)
|
|
57
|
+
* @param definitionParams - per-function ordered parameter names (Phase 8.3c)
|
|
58
|
+
* @param arrayElemBindings - array literal element bindings (Phase 8.3e)
|
|
59
|
+
* @param spreadArgBindings - spread-argument bindings (Phase 8.3e)
|
|
60
|
+
* @param forOfBindings - for-of iteration variable bindings (Phase 8.3e)
|
|
61
|
+
* @param arrayCallbackBindings - Array.from/callback bindings (Phase 8.3e)
|
|
62
|
+
*/
|
|
63
|
+
export function buildPointsToMap(
|
|
64
|
+
fnRefBindings: readonly FnRefBinding[],
|
|
65
|
+
definitionNames: ReadonlySet<string>,
|
|
66
|
+
importedNames: ReadonlyMap<string, string>,
|
|
67
|
+
paramBindings?: readonly ParamBinding[],
|
|
68
|
+
definitionParams?: ReadonlyMap<string, readonly string[]>,
|
|
69
|
+
arrayElemBindings?: readonly ArrayElemBinding[],
|
|
70
|
+
spreadArgBindings?: readonly SpreadArgBinding[],
|
|
71
|
+
forOfBindings?: readonly ForOfBinding[],
|
|
72
|
+
arrayCallbackBindings?: readonly ArrayCallbackBinding[],
|
|
73
|
+
objectRestParamBindings?: readonly ObjectRestParamBinding[],
|
|
74
|
+
objectPropBindings?: readonly ObjectPropBinding[],
|
|
75
|
+
): PointsToMap {
|
|
76
|
+
const pts: PointsToMap = new Map();
|
|
77
|
+
|
|
78
|
+
// Seed: each locally-defined function points to itself.
|
|
79
|
+
for (const name of definitionNames) {
|
|
80
|
+
pts.set(name, new Set([name]));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Seed: each imported name points to itself (importedNames resolves it to
|
|
84
|
+
// the source file when resolveCallTargets is called with that name).
|
|
85
|
+
for (const name of importedNames.keys()) {
|
|
86
|
+
if (!pts.has(name)) pts.set(name, new Set([name]));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Build constraint list: pts(lhs) ⊇ pts(rhsKey).
|
|
90
|
+
// For member expressions (const fn = obj.method), key is "obj.method".
|
|
91
|
+
// These composite keys won't be in pts unless a prior statement seeded them
|
|
92
|
+
// (e.g. handlers.auth = authMiddleware); they produce no flow otherwise,
|
|
93
|
+
// which is safe — no false edges.
|
|
94
|
+
const constraints: Array<{ lhs: string; rhsKey: string }> = fnRefBindings.map((b) => ({
|
|
95
|
+
lhs: b.lhs,
|
|
96
|
+
rhsKey: b.rhsReceiver ? `${b.rhsReceiver}.${b.rhs}` : b.rhs,
|
|
97
|
+
}));
|
|
98
|
+
|
|
99
|
+
// Phase 8.3c: parameter-flow constraints.
|
|
100
|
+
// For each call f(x) at argIndex i where f is locally defined, add
|
|
101
|
+
// constraint: pts(f::paramName_i) ⊇ pts(x). This makes the pts solver
|
|
102
|
+
// inter-procedural within the module so that `fn()` inside `f` resolves
|
|
103
|
+
// to the concrete function passed at each call site.
|
|
104
|
+
//
|
|
105
|
+
// Keys are scoped as "callee::paramName" to prevent name collisions: bare
|
|
106
|
+
// parameter names like `fn`, `cb`, and `callback` appear in many functions
|
|
107
|
+
// within the same file. Without scoping, pts(fn) from runA and runB would
|
|
108
|
+
// merge into a single set, producing spurious call edges. The scoped key is
|
|
109
|
+
// resolved in buildFileCallEdges by combining the enclosing caller's name
|
|
110
|
+
// with the call's name (see callerName::call.name lookup there).
|
|
111
|
+
//
|
|
112
|
+
// Scope: intra-module only (definitionParams contains local defs only).
|
|
113
|
+
if (paramBindings && definitionParams) {
|
|
114
|
+
for (const { callee, argIndex, argName } of paramBindings) {
|
|
115
|
+
const params = definitionParams.get(callee);
|
|
116
|
+
if (!params || argIndex >= params.length) continue;
|
|
117
|
+
const paramName = params[argIndex];
|
|
118
|
+
if (paramName) constraints.push({ lhs: `${callee}::${paramName}`, rhsKey: argName });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Phase 8.3e: array-element bindings — seed concrete elements and wildcard.
|
|
123
|
+
// `arr[0]` etc. are seeded from literal arrays; `arr[*]` collects all elements.
|
|
124
|
+
if (arrayElemBindings && arrayElemBindings.length > 0) {
|
|
125
|
+
for (const { arrayName, index, elemName } of arrayElemBindings) {
|
|
126
|
+
const elemKey = `${arrayName}[${index}]`;
|
|
127
|
+
const wildcardKey = `${arrayName}[*]`;
|
|
128
|
+
// Seed the per-index entry if the elemName is a concrete function.
|
|
129
|
+
if (!pts.has(elemKey)) pts.set(elemKey, new Set());
|
|
130
|
+
pts.get(elemKey)!.add(elemName);
|
|
131
|
+
// Wildcard: array[*] collects all element targets for imprecise spread/for-of.
|
|
132
|
+
constraints.push({ lhs: wildcardKey, rhsKey: elemKey });
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Phase 8.3e: spread-argument constraints.
|
|
137
|
+
// f(...arr) → pts[f::param_i] ⊇ pts[arr[i]] for each known element.
|
|
138
|
+
if (spreadArgBindings && spreadArgBindings.length > 0 && definitionParams) {
|
|
139
|
+
// Build a per-array index count from arrayElemBindings for precise per-index constraints.
|
|
140
|
+
const arrayMaxIndex = new Map<string, number>();
|
|
141
|
+
for (const { arrayName, index } of arrayElemBindings ?? []) {
|
|
142
|
+
const cur = arrayMaxIndex.get(arrayName) ?? -1;
|
|
143
|
+
if (index > cur) arrayMaxIndex.set(arrayName, index);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
for (const { callee, arrayName, startIndex } of spreadArgBindings) {
|
|
147
|
+
const params = definitionParams.get(callee);
|
|
148
|
+
if (!params) continue;
|
|
149
|
+
const maxIdx = arrayMaxIndex.get(arrayName) ?? -1;
|
|
150
|
+
if (maxIdx >= 0) {
|
|
151
|
+
// Precise: per-element constraints.
|
|
152
|
+
for (let i = 0; i <= maxIdx; i++) {
|
|
153
|
+
const paramIdx = startIndex + i;
|
|
154
|
+
if (paramIdx >= params.length) break;
|
|
155
|
+
constraints.push({ lhs: `${callee}::${params[paramIdx]}`, rhsKey: `${arrayName}[${i}]` });
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
// Unknown array size: all params at/after startIndex get the wildcard.
|
|
159
|
+
for (let j = startIndex; j < params.length; j++) {
|
|
160
|
+
constraints.push({ lhs: `${callee}::${params[j]}`, rhsKey: `${arrayName}[*]` });
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Phase 8.3e: for-of iteration constraints.
|
|
167
|
+
// `for (const x of arr)` inside `outer` → pts[outer::x] ⊇ pts[arr[*]]
|
|
168
|
+
if (forOfBindings) {
|
|
169
|
+
for (const { varName, sourceName, enclosingFunc } of forOfBindings) {
|
|
170
|
+
constraints.push({ lhs: `${enclosingFunc}::${varName}`, rhsKey: `${sourceName}[*]` });
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Phase 8.3e: Array.from / callback constraints.
|
|
175
|
+
// Array.from(source, cb) → pts[cb::param0] ⊇ pts[source[*]]
|
|
176
|
+
if (arrayCallbackBindings && definitionParams) {
|
|
177
|
+
for (const { sourceName, calleeName } of arrayCallbackBindings) {
|
|
178
|
+
const params = definitionParams.get(calleeName);
|
|
179
|
+
if (!params || params.length === 0) continue;
|
|
180
|
+
constraints.push({ lhs: `${calleeName}::${params[0]}`, rhsKey: `${sourceName}[*]` });
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Phase 8.3f: object-rest parameter dispatch.
|
|
185
|
+
// `function f({ ...rest }) {}` + `f(obj)` + `const obj = { prop: fn }` →
|
|
186
|
+
// seed pts["rest.prop"] = {"fn"} so that `rest.prop()` resolves to `fn`.
|
|
187
|
+
if (objectRestParamBindings && objectPropBindings && paramBindings) {
|
|
188
|
+
// Index paramBindings: "callee::argIndex" → argName[] (O(|paramBindings|) build,
|
|
189
|
+
// O(1) lookup — avoids scanning paramBindings for each rest binding).
|
|
190
|
+
const paramByCalleeIdx = new Map<string, string[]>();
|
|
191
|
+
for (const { callee, argIndex, argName } of paramBindings) {
|
|
192
|
+
const k = `${callee}::${argIndex}`;
|
|
193
|
+
const list = paramByCalleeIdx.get(k);
|
|
194
|
+
if (list) list.push(argName);
|
|
195
|
+
else paramByCalleeIdx.set(k, [argName]);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Index objectPropBindings: objectName → {propName, valueName}[]
|
|
199
|
+
const propsByObject = new Map<string, Array<{ propName: string; valueName: string }>>();
|
|
200
|
+
for (const { objectName, propName, valueName } of objectPropBindings) {
|
|
201
|
+
const list = propsByObject.get(objectName);
|
|
202
|
+
if (list) list.push({ propName, valueName });
|
|
203
|
+
else propsByObject.set(objectName, [{ propName, valueName }]);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
for (const { callee, restName, argIndex } of objectRestParamBindings) {
|
|
207
|
+
const argNames = paramByCalleeIdx.get(`${callee}::${argIndex}`) ?? [];
|
|
208
|
+
for (const argName of argNames) {
|
|
209
|
+
const props = propsByObject.get(argName) ?? [];
|
|
210
|
+
for (const { propName, valueName } of props) {
|
|
211
|
+
if (!definitionNames.has(valueName) && !importedNames.has(valueName)) continue;
|
|
212
|
+
const key = `${restName}.${propName}`;
|
|
213
|
+
if (!pts.has(key)) pts.set(key, new Set());
|
|
214
|
+
pts.get(key)!.add(valueName);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (constraints.length === 0) return pts;
|
|
221
|
+
|
|
222
|
+
// Fixed-point iteration: propagate pts sets until no new information flows.
|
|
223
|
+
for (let iter = 0; iter < MAX_SOLVER_ITERATIONS; iter++) {
|
|
224
|
+
let changed = false;
|
|
225
|
+
for (const { lhs, rhsKey } of constraints) {
|
|
226
|
+
const rhsPts = pts.get(rhsKey);
|
|
227
|
+
if (!rhsPts || rhsPts.size === 0) continue;
|
|
228
|
+
let lhsPts = pts.get(lhs);
|
|
229
|
+
if (!lhsPts) {
|
|
230
|
+
lhsPts = new Set();
|
|
231
|
+
pts.set(lhs, lhsPts);
|
|
232
|
+
}
|
|
233
|
+
const before = lhsPts.size;
|
|
234
|
+
for (const target of rhsPts) lhsPts.add(target);
|
|
235
|
+
if (lhsPts.size !== before) changed = true;
|
|
236
|
+
}
|
|
237
|
+
if (!changed) break;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return pts;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Return the concrete function names that `callName` flows to, excluding
|
|
245
|
+
* itself to prevent circular self-reference edges.
|
|
246
|
+
*
|
|
247
|
+
* Returns an empty array when callName is not in the pts map (i.e., it is
|
|
248
|
+
* not an alias — the caller should fall back to normal resolution failure).
|
|
249
|
+
*/
|
|
250
|
+
export function resolveViaPointsTo(callName: string, pts: PointsToMap): string[] {
|
|
251
|
+
const targets = pts.get(callName);
|
|
252
|
+
if (!targets) return [];
|
|
253
|
+
return [...targets].filter((t) => t !== callName);
|
|
254
|
+
}
|