circle-ir 3.8.4 → 3.9.8
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 +82 -5
- package/dist/analysis/dfg-verifier.d.ts +3 -14
- package/dist/analysis/dfg-verifier.js +43 -74
- package/dist/analysis/dfg-verifier.js.map +1 -1
- package/dist/analysis/interprocedural.d.ts +5 -1
- package/dist/analysis/interprocedural.js +62 -60
- package/dist/analysis/interprocedural.js.map +1 -1
- package/dist/analysis/metrics/index.d.ts +2 -0
- package/dist/analysis/metrics/index.js +2 -0
- package/dist/analysis/metrics/index.js.map +1 -0
- package/dist/analysis/metrics/metric-pass.d.ts +27 -0
- package/dist/analysis/metrics/metric-pass.js +2 -0
- package/dist/analysis/metrics/metric-pass.js.map +1 -0
- package/dist/analysis/metrics/metric-runner.d.ts +21 -0
- package/dist/analysis/metrics/metric-runner.js +47 -0
- package/dist/analysis/metrics/metric-runner.js.map +1 -0
- package/dist/analysis/metrics/passes/cohesion-metrics-pass.d.ts +21 -0
- package/dist/analysis/metrics/passes/cohesion-metrics-pass.js +100 -0
- package/dist/analysis/metrics/passes/cohesion-metrics-pass.js.map +1 -0
- package/dist/analysis/metrics/passes/complexity-metrics-pass.d.ts +15 -0
- package/dist/analysis/metrics/passes/complexity-metrics-pass.js +76 -0
- package/dist/analysis/metrics/passes/complexity-metrics-pass.js.map +1 -0
- package/dist/analysis/metrics/passes/composite-metrics-pass.d.ts +17 -0
- package/dist/analysis/metrics/passes/composite-metrics-pass.js +77 -0
- package/dist/analysis/metrics/passes/composite-metrics-pass.js.map +1 -0
- package/dist/analysis/metrics/passes/coupling-metrics-pass.d.ts +19 -0
- package/dist/analysis/metrics/passes/coupling-metrics-pass.js +94 -0
- package/dist/analysis/metrics/passes/coupling-metrics-pass.js.map +1 -0
- package/dist/analysis/metrics/passes/data-flow-metrics-pass.d.ts +14 -0
- package/dist/analysis/metrics/passes/data-flow-metrics-pass.js +25 -0
- package/dist/analysis/metrics/passes/data-flow-metrics-pass.js.map +1 -0
- package/dist/analysis/metrics/passes/documentation-metrics-pass.d.ts +15 -0
- package/dist/analysis/metrics/passes/documentation-metrics-pass.js +64 -0
- package/dist/analysis/metrics/passes/documentation-metrics-pass.js.map +1 -0
- package/dist/analysis/metrics/passes/halstead-metrics-pass.d.ts +16 -0
- package/dist/analysis/metrics/passes/halstead-metrics-pass.js +95 -0
- package/dist/analysis/metrics/passes/halstead-metrics-pass.js.map +1 -0
- package/dist/analysis/metrics/passes/inheritance-metrics-pass.d.ts +18 -0
- package/dist/analysis/metrics/passes/inheritance-metrics-pass.js +73 -0
- package/dist/analysis/metrics/passes/inheritance-metrics-pass.js.map +1 -0
- package/dist/analysis/metrics/passes/size-metrics-pass.d.ts +11 -0
- package/dist/analysis/metrics/passes/size-metrics-pass.js +64 -0
- package/dist/analysis/metrics/passes/size-metrics-pass.js.map +1 -0
- package/dist/analysis/passes/circular-dependency-pass.d.ts +18 -0
- package/dist/analysis/passes/circular-dependency-pass.js +39 -0
- package/dist/analysis/passes/circular-dependency-pass.js.map +1 -0
- package/dist/analysis/passes/constant-propagation-pass.d.ts +22 -0
- package/dist/analysis/passes/constant-propagation-pass.js +44 -0
- package/dist/analysis/passes/constant-propagation-pass.js.map +1 -0
- package/dist/analysis/passes/cross-file-pass.d.ts +27 -0
- package/dist/analysis/passes/cross-file-pass.js +102 -0
- package/dist/analysis/passes/cross-file-pass.js.map +1 -0
- package/dist/analysis/passes/dead-code-pass.d.ts +25 -0
- package/dist/analysis/passes/dead-code-pass.js +117 -0
- package/dist/analysis/passes/dead-code-pass.js.map +1 -0
- package/dist/analysis/passes/deep-inheritance-pass.d.ts +30 -0
- package/dist/analysis/passes/deep-inheritance-pass.js +82 -0
- package/dist/analysis/passes/deep-inheritance-pass.js.map +1 -0
- package/dist/analysis/passes/dependency-fan-out-pass.d.ts +19 -0
- package/dist/analysis/passes/dependency-fan-out-pass.js +35 -0
- package/dist/analysis/passes/dependency-fan-out-pass.js.map +1 -0
- package/dist/analysis/passes/infinite-loop-pass.d.ts +31 -0
- package/dist/analysis/passes/infinite-loop-pass.js +126 -0
- package/dist/analysis/passes/infinite-loop-pass.js.map +1 -0
- package/dist/analysis/passes/interprocedural-pass.d.ts +29 -0
- package/dist/analysis/passes/interprocedural-pass.js +169 -0
- package/dist/analysis/passes/interprocedural-pass.js.map +1 -0
- package/dist/analysis/passes/language-sources-pass.d.ts +76 -0
- package/dist/analysis/passes/language-sources-pass.js +491 -0
- package/dist/analysis/passes/language-sources-pass.js.map +1 -0
- package/dist/analysis/passes/leaked-global-pass.d.ts +34 -0
- package/dist/analysis/passes/leaked-global-pass.js +108 -0
- package/dist/analysis/passes/leaked-global-pass.js.map +1 -0
- package/dist/analysis/passes/missing-await-pass.d.ts +29 -0
- package/dist/analysis/passes/missing-await-pass.js +90 -0
- package/dist/analysis/passes/missing-await-pass.js.map +1 -0
- package/dist/analysis/passes/missing-public-doc-pass.d.ts +35 -0
- package/dist/analysis/passes/missing-public-doc-pass.js +148 -0
- package/dist/analysis/passes/missing-public-doc-pass.js.map +1 -0
- package/dist/analysis/passes/n-plus-one-pass.d.ts +29 -0
- package/dist/analysis/passes/n-plus-one-pass.js +100 -0
- package/dist/analysis/passes/n-plus-one-pass.js.map +1 -0
- package/dist/analysis/passes/null-deref-pass.d.ts +32 -0
- package/dist/analysis/passes/null-deref-pass.js +130 -0
- package/dist/analysis/passes/null-deref-pass.js.map +1 -0
- package/dist/analysis/passes/orphan-module-pass.d.ts +21 -0
- package/dist/analysis/passes/orphan-module-pass.js +38 -0
- package/dist/analysis/passes/orphan-module-pass.js.map +1 -0
- package/dist/analysis/passes/react-inline-jsx-pass.d.ts +36 -0
- package/dist/analysis/passes/react-inline-jsx-pass.js +140 -0
- package/dist/analysis/passes/react-inline-jsx-pass.js.map +1 -0
- package/dist/analysis/passes/redundant-loop-pass.d.ts +30 -0
- package/dist/analysis/passes/redundant-loop-pass.js +146 -0
- package/dist/analysis/passes/redundant-loop-pass.js.map +1 -0
- package/dist/analysis/passes/resource-leak-pass.d.ts +43 -0
- package/dist/analysis/passes/resource-leak-pass.js +156 -0
- package/dist/analysis/passes/resource-leak-pass.js.map +1 -0
- package/dist/analysis/passes/serial-await-pass.d.ts +36 -0
- package/dist/analysis/passes/serial-await-pass.js +132 -0
- package/dist/analysis/passes/serial-await-pass.js.map +1 -0
- package/dist/analysis/passes/sink-filter-pass.d.ts +39 -0
- package/dist/analysis/passes/sink-filter-pass.js +231 -0
- package/dist/analysis/passes/sink-filter-pass.js.map +1 -0
- package/dist/analysis/passes/stale-doc-ref-pass.d.ts +21 -0
- package/dist/analysis/passes/stale-doc-ref-pass.js +96 -0
- package/dist/analysis/passes/stale-doc-ref-pass.js.map +1 -0
- package/dist/analysis/passes/string-concat-loop-pass.d.ts +26 -0
- package/dist/analysis/passes/string-concat-loop-pass.js +87 -0
- package/dist/analysis/passes/string-concat-loop-pass.js.map +1 -0
- package/dist/analysis/passes/sync-io-async-pass.d.ts +28 -0
- package/dist/analysis/passes/sync-io-async-pass.js +80 -0
- package/dist/analysis/passes/sync-io-async-pass.js.map +1 -0
- package/dist/analysis/passes/taint-matcher-pass.d.ts +24 -0
- package/dist/analysis/passes/taint-matcher-pass.js +71 -0
- package/dist/analysis/passes/taint-matcher-pass.js.map +1 -0
- package/dist/analysis/passes/taint-propagation-pass.d.ts +22 -0
- package/dist/analysis/passes/taint-propagation-pass.js +266 -0
- package/dist/analysis/passes/taint-propagation-pass.js.map +1 -0
- package/dist/analysis/passes/todo-in-prod-pass.d.ts +28 -0
- package/dist/analysis/passes/todo-in-prod-pass.js +71 -0
- package/dist/analysis/passes/todo-in-prod-pass.js.map +1 -0
- package/dist/analysis/passes/unbounded-collection-pass.d.ts +32 -0
- package/dist/analysis/passes/unbounded-collection-pass.js +128 -0
- package/dist/analysis/passes/unbounded-collection-pass.js.map +1 -0
- package/dist/analysis/passes/unchecked-return-pass.d.ts +34 -0
- package/dist/analysis/passes/unchecked-return-pass.js +106 -0
- package/dist/analysis/passes/unchecked-return-pass.js.map +1 -0
- package/dist/analysis/passes/unused-variable-pass.d.ts +36 -0
- package/dist/analysis/passes/unused-variable-pass.js +150 -0
- package/dist/analysis/passes/unused-variable-pass.js.map +1 -0
- package/dist/analysis/passes/variable-shadowing-pass.d.ts +41 -0
- package/dist/analysis/passes/variable-shadowing-pass.js +211 -0
- package/dist/analysis/passes/variable-shadowing-pass.js.map +1 -0
- package/dist/analysis/path-finder.d.ts +3 -13
- package/dist/analysis/path-finder.js +48 -63
- package/dist/analysis/path-finder.js.map +1 -1
- package/dist/analysis/taint-matcher.js +8 -1
- package/dist/analysis/taint-matcher.js.map +1 -1
- package/dist/analysis/taint-propagation.d.ts +5 -1
- package/dist/analysis/taint-propagation.js +44 -41
- package/dist/analysis/taint-propagation.js.map +1 -1
- package/dist/analyzer.d.ts +48 -1
- package/dist/analyzer.js +252 -1476
- package/dist/analyzer.js.map +1 -1
- package/dist/browser/circle-ir.js +3952 -1270
- package/dist/core/circle-ir-core.cjs +360 -106
- package/dist/core/circle-ir-core.js +360 -106
- package/dist/core/extractors/imports.js +18 -0
- package/dist/core/extractors/imports.js.map +1 -1
- package/dist/graph/analysis-pass.d.ts +68 -0
- package/dist/graph/analysis-pass.js +51 -0
- package/dist/graph/analysis-pass.js.map +1 -0
- package/dist/graph/code-graph.d.ts +92 -0
- package/dist/graph/code-graph.js +262 -0
- package/dist/graph/code-graph.js.map +1 -0
- package/dist/graph/dominator-graph.d.ts +53 -0
- package/dist/graph/dominator-graph.js +256 -0
- package/dist/graph/dominator-graph.js.map +1 -0
- package/dist/graph/import-graph.d.ts +33 -0
- package/dist/graph/import-graph.js +170 -0
- package/dist/graph/import-graph.js.map +1 -0
- package/dist/graph/index.d.ts +5 -0
- package/dist/graph/index.js +6 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/project-graph.d.ts +43 -0
- package/dist/graph/project-graph.js +80 -0
- package/dist/graph/project-graph.js.map +1 -0
- package/dist/graph/scope-graph.d.ts +63 -0
- package/dist/graph/scope-graph.js +89 -0
- package/dist/graph/scope-graph.js.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/resolution/cross-file.js +52 -19
- package/dist/resolution/cross-file.js.map +1 -1
- package/dist/types/index.d.ts +151 -0
- package/docs/SPEC.md +10 -6
- package/package.json +1 -1
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DominatorGraph
|
|
3
|
+
*
|
|
4
|
+
* Computes the dominator tree for a control-flow graph using the
|
|
5
|
+
* Cooper et al. "A Simple, Fast Dominance Algorithm" (2001).
|
|
6
|
+
*
|
|
7
|
+
* Chosen over Lengauer-Tarjan because typical intra-procedural CFGs have
|
|
8
|
+
* fewer than 100 blocks — O(n²) worst-case is negligible and the algorithm
|
|
9
|
+
* is straightforward to verify correct.
|
|
10
|
+
*
|
|
11
|
+
* Reference:
|
|
12
|
+
* Cooper, K.D., Harvey, T.J., Kennedy, K. (2001). "A Simple, Fast Dominance
|
|
13
|
+
* Algorithm". Software Practice & Experience, 4, 1–10.
|
|
14
|
+
*
|
|
15
|
+
* Design invariants:
|
|
16
|
+
* - No Node.js-specific APIs. Browser + Node.js + Cloudflare Workers safe.
|
|
17
|
+
* - Does not mutate the input CFG.
|
|
18
|
+
* - Unreachable blocks are excluded from all dominator queries.
|
|
19
|
+
*/
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Internal helpers
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
/**
|
|
24
|
+
* Compute Reverse Post-Order starting from `entryId`.
|
|
25
|
+
* Returns the RPO traversal order and a map from blockId → RPO position.
|
|
26
|
+
* Blocks unreachable from `entryId` are excluded.
|
|
27
|
+
*/
|
|
28
|
+
function computeRPO(cfg, entryId) {
|
|
29
|
+
// Build outgoing adjacency
|
|
30
|
+
const outgoing = new Map();
|
|
31
|
+
for (const edge of cfg.edges) {
|
|
32
|
+
const list = outgoing.get(edge.from) ?? [];
|
|
33
|
+
list.push(edge.to);
|
|
34
|
+
outgoing.set(edge.from, list);
|
|
35
|
+
}
|
|
36
|
+
// Iterative DFS to compute post-order, then reverse
|
|
37
|
+
const visited = new Set();
|
|
38
|
+
const postOrder = [];
|
|
39
|
+
const stack = [{ id: entryId, childIndex: 0 }];
|
|
40
|
+
visited.add(entryId);
|
|
41
|
+
while (stack.length > 0) {
|
|
42
|
+
const top = stack[stack.length - 1];
|
|
43
|
+
const children = outgoing.get(top.id) ?? [];
|
|
44
|
+
// Find next unvisited child
|
|
45
|
+
let pushed = false;
|
|
46
|
+
while (top.childIndex < children.length) {
|
|
47
|
+
const child = children[top.childIndex++];
|
|
48
|
+
if (!visited.has(child)) {
|
|
49
|
+
visited.add(child);
|
|
50
|
+
stack.push({ id: child, childIndex: 0 });
|
|
51
|
+
pushed = true;
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (!pushed) {
|
|
56
|
+
// All children visited — add to post-order
|
|
57
|
+
postOrder.push(top.id);
|
|
58
|
+
stack.pop();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const rpoOrder = postOrder.reverse();
|
|
62
|
+
const rpoIndex = new Map();
|
|
63
|
+
for (let i = 0; i < rpoOrder.length; i++) {
|
|
64
|
+
rpoIndex.set(rpoOrder[i], i);
|
|
65
|
+
}
|
|
66
|
+
return { rpoOrder, rpoIndex };
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Cooper et al. intersect function.
|
|
70
|
+
* Walks up the idom tree from b1 and b2 until a common ancestor is found.
|
|
71
|
+
* Uses RPO positions for comparisons (smaller RPO index = earlier in RPO = closer to entry).
|
|
72
|
+
*
|
|
73
|
+
* IMPORTANT: The entry block must have idom[entry] = entry (self-loop sentinel)
|
|
74
|
+
* so the walk terminates. This sentinel is removed from the public idom map after
|
|
75
|
+
* computation, but must be present during computation.
|
|
76
|
+
*/
|
|
77
|
+
function intersect(b1, b2, idom, rpoIndex) {
|
|
78
|
+
let finger1 = b1;
|
|
79
|
+
let finger2 = b2;
|
|
80
|
+
while (finger1 !== finger2) {
|
|
81
|
+
// Walk finger1 up while its RPO position is deeper (larger index) than finger2
|
|
82
|
+
while ((rpoIndex.get(finger1) ?? Number.MAX_SAFE_INTEGER) >
|
|
83
|
+
(rpoIndex.get(finger2) ?? Number.MAX_SAFE_INTEGER)) {
|
|
84
|
+
const parent = idom.get(finger1);
|
|
85
|
+
if (parent === undefined || parent === finger1)
|
|
86
|
+
break; // at root or self-loop
|
|
87
|
+
finger1 = parent;
|
|
88
|
+
}
|
|
89
|
+
// Walk finger2 up while its RPO position is deeper than finger1
|
|
90
|
+
while ((rpoIndex.get(finger2) ?? Number.MAX_SAFE_INTEGER) >
|
|
91
|
+
(rpoIndex.get(finger1) ?? Number.MAX_SAFE_INTEGER)) {
|
|
92
|
+
const parent = idom.get(finger2);
|
|
93
|
+
if (parent === undefined || parent === finger2)
|
|
94
|
+
break; // at root or self-loop
|
|
95
|
+
finger2 = parent;
|
|
96
|
+
}
|
|
97
|
+
// If neither changed (both at root or unreachable), break
|
|
98
|
+
if (finger1 === finger2)
|
|
99
|
+
break;
|
|
100
|
+
// Safety: if both are at positions we can't walk further, break
|
|
101
|
+
const rpo1 = rpoIndex.get(finger1) ?? Number.MAX_SAFE_INTEGER;
|
|
102
|
+
const rpo2 = rpoIndex.get(finger2) ?? Number.MAX_SAFE_INTEGER;
|
|
103
|
+
if (rpo1 === rpo2 && finger1 !== finger2)
|
|
104
|
+
break; // can't converge further
|
|
105
|
+
}
|
|
106
|
+
return finger1;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Cooper et al. iterative idom computation.
|
|
110
|
+
* Requires rpoOrder and rpoIndex from computeRPO.
|
|
111
|
+
* Returns a map: blockId → immediate dominator blockId.
|
|
112
|
+
* Entry block has idom[entry] = entry (sentinel) during computation;
|
|
113
|
+
* callers remove this after the function returns.
|
|
114
|
+
*/
|
|
115
|
+
function computeIdom(cfg, rpoOrder, rpoIndex, entryId) {
|
|
116
|
+
// Build incoming adjacency
|
|
117
|
+
const incoming = new Map();
|
|
118
|
+
for (const edge of cfg.edges) {
|
|
119
|
+
const list = incoming.get(edge.to) ?? [];
|
|
120
|
+
list.push(edge.from);
|
|
121
|
+
incoming.set(edge.to, list);
|
|
122
|
+
}
|
|
123
|
+
const idom = new Map();
|
|
124
|
+
// Initialise: entry block idom = itself (sentinel for termination)
|
|
125
|
+
idom.set(entryId, entryId);
|
|
126
|
+
// Iterate until stable
|
|
127
|
+
let changed = true;
|
|
128
|
+
while (changed) {
|
|
129
|
+
changed = false;
|
|
130
|
+
// Process in RPO order, skip entry (index 0)
|
|
131
|
+
for (let i = 1; i < rpoOrder.length; i++) {
|
|
132
|
+
const b = rpoOrder[i];
|
|
133
|
+
const preds = incoming.get(b) ?? [];
|
|
134
|
+
// Pick the first predecessor that already has an idom computed
|
|
135
|
+
// (i.e., already processed in RPO order)
|
|
136
|
+
let newIdom;
|
|
137
|
+
for (const p of preds) {
|
|
138
|
+
if (idom.has(p)) {
|
|
139
|
+
newIdom = p;
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (newIdom === undefined)
|
|
144
|
+
continue;
|
|
145
|
+
// Intersect with all other processed predecessors
|
|
146
|
+
for (const p of preds) {
|
|
147
|
+
if (p === newIdom)
|
|
148
|
+
continue;
|
|
149
|
+
if (idom.has(p)) {
|
|
150
|
+
newIdom = intersect(p, newIdom, idom, rpoIndex);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (idom.get(b) !== newIdom) {
|
|
154
|
+
idom.set(b, newIdom);
|
|
155
|
+
changed = true;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return idom;
|
|
160
|
+
}
|
|
161
|
+
// ---------------------------------------------------------------------------
|
|
162
|
+
// Public class
|
|
163
|
+
// ---------------------------------------------------------------------------
|
|
164
|
+
/**
|
|
165
|
+
* Dominator tree for a CFG, computed with the Cooper et al. algorithm.
|
|
166
|
+
*
|
|
167
|
+
* Entry blocks have no immediate dominator (immediateDominator returns null).
|
|
168
|
+
* Only blocks reachable from the entry are included.
|
|
169
|
+
*/
|
|
170
|
+
export class DominatorGraph {
|
|
171
|
+
idom;
|
|
172
|
+
rpoIndex;
|
|
173
|
+
entryId;
|
|
174
|
+
/** Cached reverse map: blockId → all blockIds it strictly dominates. */
|
|
175
|
+
_dominated = null;
|
|
176
|
+
constructor(cfg, entryId) {
|
|
177
|
+
if (cfg.blocks.length === 0) {
|
|
178
|
+
this.entryId = entryId ?? 0;
|
|
179
|
+
this.idom = new Map();
|
|
180
|
+
this.rpoIndex = new Map();
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
// Determine entry: prefer provided entryId, then type='entry' block,
|
|
184
|
+
// then the block with the smallest id.
|
|
185
|
+
this.entryId =
|
|
186
|
+
entryId ??
|
|
187
|
+
cfg.blocks.find(b => b.type === 'entry')?.id ??
|
|
188
|
+
cfg.blocks.reduce((a, b) => (a.id < b.id ? a : b)).id;
|
|
189
|
+
const { rpoOrder, rpoIndex } = computeRPO(cfg, this.entryId);
|
|
190
|
+
this.rpoIndex = rpoIndex;
|
|
191
|
+
this.idom = computeIdom(cfg, rpoOrder, rpoIndex, this.entryId);
|
|
192
|
+
// Remove the sentinel self-reference for the entry block
|
|
193
|
+
// so immediateDominator(entryId) returns null
|
|
194
|
+
this.idom.delete(this.entryId);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Returns true if block `a` dominates block `b`.
|
|
198
|
+
* A block dominates itself (reflexive).
|
|
199
|
+
*/
|
|
200
|
+
dominates(a, b) {
|
|
201
|
+
if (a === b)
|
|
202
|
+
return true;
|
|
203
|
+
return this.strictlyDominates(a, b);
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Returns true if block `a` strictly dominates block `b` (a ≠ b and a dom b).
|
|
207
|
+
*/
|
|
208
|
+
strictlyDominates(a, b) {
|
|
209
|
+
if (a === b)
|
|
210
|
+
return false;
|
|
211
|
+
// Walk up the idom chain from b; if we reach a, then a dom b.
|
|
212
|
+
const visited = new Set();
|
|
213
|
+
let cur = this.idom.get(b);
|
|
214
|
+
while (cur !== undefined && !visited.has(cur)) {
|
|
215
|
+
if (cur === a)
|
|
216
|
+
return true;
|
|
217
|
+
visited.add(cur);
|
|
218
|
+
cur = this.idom.get(cur);
|
|
219
|
+
}
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Returns the immediate dominator of `blockId`, or null for the entry block
|
|
224
|
+
* (or any block not in the dominator tree).
|
|
225
|
+
*/
|
|
226
|
+
immediateDominator(blockId) {
|
|
227
|
+
return this.idom.get(blockId) ?? null;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Returns all block IDs strictly dominated by `blockId`.
|
|
231
|
+
* (Computed lazily and cached on first call.)
|
|
232
|
+
*/
|
|
233
|
+
dominated(blockId) {
|
|
234
|
+
if (!this._dominated) {
|
|
235
|
+
this._dominated = new Map();
|
|
236
|
+
for (const [child, parent] of this.idom.entries()) {
|
|
237
|
+
// Walk up from child to find all ancestors (blocks that dominate child)
|
|
238
|
+
const ancestors = [];
|
|
239
|
+
const seen = new Set();
|
|
240
|
+
let cur = parent;
|
|
241
|
+
while (cur !== undefined && !seen.has(cur)) {
|
|
242
|
+
seen.add(cur);
|
|
243
|
+
ancestors.push(cur);
|
|
244
|
+
cur = this.idom.get(cur);
|
|
245
|
+
}
|
|
246
|
+
for (const anc of ancestors) {
|
|
247
|
+
const list = this._dominated.get(anc) ?? [];
|
|
248
|
+
list.push(child);
|
|
249
|
+
this._dominated.set(anc, list);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return this._dominated.get(blockId) ?? [];
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
//# sourceMappingURL=dominator-graph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dominator-graph.js","sourceRoot":"","sources":["../../src/graph/dominator-graph.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ImportGraph
|
|
3
|
+
*
|
|
4
|
+
* Builds a directed file→file import graph from a ProjectGraph.
|
|
5
|
+
* Only relative imports (from_package starting with '.') are resolved to edges —
|
|
6
|
+
* stdlib and npm package imports are ignored.
|
|
7
|
+
*
|
|
8
|
+
* Used by CircularDependencyPass and OrphanModulePass.
|
|
9
|
+
*/
|
|
10
|
+
import type { ProjectGraph } from './project-graph.js';
|
|
11
|
+
export declare class ImportGraph {
|
|
12
|
+
/** file → set of files it imports */
|
|
13
|
+
private readonly outEdges;
|
|
14
|
+
/** file → set of files that import it */
|
|
15
|
+
private readonly inEdges;
|
|
16
|
+
/** All known file paths */
|
|
17
|
+
private readonly allFiles;
|
|
18
|
+
constructor(projectGraph: ProjectGraph);
|
|
19
|
+
/** Files directly imported by `filePath`. */
|
|
20
|
+
edgesFrom(filePath: string): string[];
|
|
21
|
+
/** Files that directly import `filePath`. */
|
|
22
|
+
edgesTo(filePath: string): string[];
|
|
23
|
+
/**
|
|
24
|
+
* Tarjan's SCC — returns groups of files that form import cycles.
|
|
25
|
+
* Each returned Set has size ≥ 2 (only actual cycles).
|
|
26
|
+
*/
|
|
27
|
+
findCycles(): Set<string>[];
|
|
28
|
+
/**
|
|
29
|
+
* Returns file paths with zero incoming import edges that are not entry points.
|
|
30
|
+
* Entry points: filename base (without extension) matches /^(index|main|app|server|mod)$/i
|
|
31
|
+
*/
|
|
32
|
+
findOrphans(): string[];
|
|
33
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ImportGraph
|
|
3
|
+
*
|
|
4
|
+
* Builds a directed file→file import graph from a ProjectGraph.
|
|
5
|
+
* Only relative imports (from_package starting with '.') are resolved to edges —
|
|
6
|
+
* stdlib and npm package imports are ignored.
|
|
7
|
+
*
|
|
8
|
+
* Used by CircularDependencyPass and OrphanModulePass.
|
|
9
|
+
*/
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Path helpers (no Node.js 'path' module — must run in browser too)
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
function dirname(filePath) {
|
|
14
|
+
const idx = filePath.lastIndexOf('/');
|
|
15
|
+
return idx >= 0 ? filePath.slice(0, idx) : '';
|
|
16
|
+
}
|
|
17
|
+
function normalizePath(p) {
|
|
18
|
+
const isAbsolute = p.startsWith('/');
|
|
19
|
+
const parts = p.split('/');
|
|
20
|
+
const result = [];
|
|
21
|
+
for (const part of parts) {
|
|
22
|
+
if (part === '' || part === '.')
|
|
23
|
+
continue;
|
|
24
|
+
if (part === '..') {
|
|
25
|
+
result.pop();
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
result.push(part);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return (isAbsolute ? '/' : '') + result.join('/');
|
|
32
|
+
}
|
|
33
|
+
const EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.py', '.java', '.rs', ''];
|
|
34
|
+
// Entry-point filenames (base without extension) — not treated as orphans
|
|
35
|
+
const ENTRY_POINT_NAMES = /^(index|main|app|server|mod|cli|bin|start|run|entry|init)$/i;
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// ImportGraph
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
export class ImportGraph {
|
|
40
|
+
/** file → set of files it imports */
|
|
41
|
+
outEdges;
|
|
42
|
+
/** file → set of files that import it */
|
|
43
|
+
inEdges;
|
|
44
|
+
/** All known file paths */
|
|
45
|
+
allFiles;
|
|
46
|
+
constructor(projectGraph) {
|
|
47
|
+
this.outEdges = new Map();
|
|
48
|
+
this.inEdges = new Map();
|
|
49
|
+
this.allFiles = new Set(projectGraph.filePaths);
|
|
50
|
+
// Initialize edge sets for all files
|
|
51
|
+
for (const file of this.allFiles) {
|
|
52
|
+
this.outEdges.set(file, new Set());
|
|
53
|
+
this.inEdges.set(file, new Set());
|
|
54
|
+
}
|
|
55
|
+
// Build edges from imports
|
|
56
|
+
for (const filePath of projectGraph.filePaths) {
|
|
57
|
+
const ir = projectGraph.getIR(filePath);
|
|
58
|
+
if (!ir)
|
|
59
|
+
continue;
|
|
60
|
+
const dir = dirname(filePath);
|
|
61
|
+
for (const imp of ir.imports) {
|
|
62
|
+
const pkg = imp.from_package;
|
|
63
|
+
if (!pkg || !pkg.startsWith('.'))
|
|
64
|
+
continue; // skip stdlib/npm
|
|
65
|
+
// Resolve relative path
|
|
66
|
+
const rawCandidate = dir ? `${dir}/${pkg}` : pkg;
|
|
67
|
+
const base = normalizePath(rawCandidate);
|
|
68
|
+
// Try with each extension
|
|
69
|
+
let resolved = null;
|
|
70
|
+
for (const ext of EXTENSIONS) {
|
|
71
|
+
const candidate = base + ext;
|
|
72
|
+
if (this.allFiles.has(candidate)) {
|
|
73
|
+
resolved = candidate;
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// TypeScript ESM convention: `import './foo.js'` refers to `./foo.ts` on disk.
|
|
78
|
+
// Strip the .js suffix and retry with TypeScript extensions.
|
|
79
|
+
if (!resolved && base.endsWith('.js')) {
|
|
80
|
+
const stripped = base.slice(0, -3);
|
|
81
|
+
for (const ext of ['.ts', '.tsx', '.js']) {
|
|
82
|
+
const candidate = stripped + ext;
|
|
83
|
+
if (this.allFiles.has(candidate)) {
|
|
84
|
+
resolved = candidate;
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (resolved && resolved !== filePath) {
|
|
90
|
+
this.outEdges.get(filePath).add(resolved);
|
|
91
|
+
this.inEdges.get(resolved).add(filePath);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/** Files directly imported by `filePath`. */
|
|
97
|
+
edgesFrom(filePath) {
|
|
98
|
+
return [...(this.outEdges.get(filePath) ?? [])];
|
|
99
|
+
}
|
|
100
|
+
/** Files that directly import `filePath`. */
|
|
101
|
+
edgesTo(filePath) {
|
|
102
|
+
return [...(this.inEdges.get(filePath) ?? [])];
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Tarjan's SCC — returns groups of files that form import cycles.
|
|
106
|
+
* Each returned Set has size ≥ 2 (only actual cycles).
|
|
107
|
+
*/
|
|
108
|
+
findCycles() {
|
|
109
|
+
const index = new Map();
|
|
110
|
+
const lowlink = new Map();
|
|
111
|
+
const onStack = new Map();
|
|
112
|
+
const stack = [];
|
|
113
|
+
const cycles = [];
|
|
114
|
+
let counter = 0;
|
|
115
|
+
const strongConnect = (v) => {
|
|
116
|
+
index.set(v, counter);
|
|
117
|
+
lowlink.set(v, counter);
|
|
118
|
+
counter++;
|
|
119
|
+
stack.push(v);
|
|
120
|
+
onStack.set(v, true);
|
|
121
|
+
for (const w of (this.outEdges.get(v) ?? [])) {
|
|
122
|
+
if (!index.has(w)) {
|
|
123
|
+
strongConnect(w);
|
|
124
|
+
lowlink.set(v, Math.min(lowlink.get(v), lowlink.get(w)));
|
|
125
|
+
}
|
|
126
|
+
else if (onStack.get(w)) {
|
|
127
|
+
lowlink.set(v, Math.min(lowlink.get(v), index.get(w)));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (lowlink.get(v) === index.get(v)) {
|
|
131
|
+
// Root of SCC — pop the component
|
|
132
|
+
const scc = new Set();
|
|
133
|
+
let w;
|
|
134
|
+
do {
|
|
135
|
+
w = stack.pop();
|
|
136
|
+
onStack.set(w, false);
|
|
137
|
+
scc.add(w);
|
|
138
|
+
} while (w !== v);
|
|
139
|
+
if (scc.size > 1) {
|
|
140
|
+
cycles.push(scc);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
for (const v of this.allFiles) {
|
|
145
|
+
if (!index.has(v)) {
|
|
146
|
+
strongConnect(v);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return cycles;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Returns file paths with zero incoming import edges that are not entry points.
|
|
153
|
+
* Entry points: filename base (without extension) matches /^(index|main|app|server|mod)$/i
|
|
154
|
+
*/
|
|
155
|
+
findOrphans() {
|
|
156
|
+
const orphans = [];
|
|
157
|
+
for (const file of this.allFiles) {
|
|
158
|
+
if ((this.inEdges.get(file)?.size ?? 0) > 0)
|
|
159
|
+
continue;
|
|
160
|
+
// Check if entry point
|
|
161
|
+
const base = file.split('/').pop() ?? '';
|
|
162
|
+
const baseName = base.includes('.') ? base.slice(0, base.lastIndexOf('.')) : base;
|
|
163
|
+
if (ENTRY_POINT_NAMES.test(baseName))
|
|
164
|
+
continue;
|
|
165
|
+
orphans.push(file);
|
|
166
|
+
}
|
|
167
|
+
return orphans.sort();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=import-graph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import-graph.js","sourceRoot":"","sources":["../../src/graph/import-graph.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { CodeGraph } from './code-graph.js';
|
|
2
|
+
export { ProjectGraph } from './project-graph.js';
|
|
3
|
+
export { ImportGraph } from './import-graph.js';
|
|
4
|
+
export { DominatorGraph } from './dominator-graph.js';
|
|
5
|
+
export { AnalysisPipeline, type AnalysisPass, type PassContext, type PipelineRunResult, } from './analysis-pass.js';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { CodeGraph } from './code-graph.js';
|
|
2
|
+
export { ProjectGraph } from './project-graph.js';
|
|
3
|
+
export { ImportGraph } from './import-graph.js';
|
|
4
|
+
export { DominatorGraph } from './dominator-graph.js';
|
|
5
|
+
export { AnalysisPipeline, } from './analysis-pass.js';
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/graph/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EACL,gBAAgB,GAIjB,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ProjectGraph
|
|
3
|
+
*
|
|
4
|
+
* Wraps multiple per-file CodeGraph instances with lazy cross-file resolution
|
|
5
|
+
* infrastructure (SymbolTable, TypeHierarchyResolver, CrossFileResolver).
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* const pg = new ProjectGraph();
|
|
9
|
+
* pg.addFile('/src/A.java', graphA);
|
|
10
|
+
* pg.addFile('/src/B.java', graphB);
|
|
11
|
+
* const flows = pg.resolver.findCrossFileTaintFlows();
|
|
12
|
+
*/
|
|
13
|
+
import type { CircleIR } from '../types/index.js';
|
|
14
|
+
import { CodeGraph } from './code-graph.js';
|
|
15
|
+
import { SymbolTable, TypeHierarchyResolver, CrossFileResolver } from '../resolution/index.js';
|
|
16
|
+
export declare class ProjectGraph {
|
|
17
|
+
private readonly files;
|
|
18
|
+
private _symbolTable;
|
|
19
|
+
private _typeHierarchy;
|
|
20
|
+
private _resolver;
|
|
21
|
+
/**
|
|
22
|
+
* Register a file's CodeGraph. Invalidates all lazy caches.
|
|
23
|
+
*/
|
|
24
|
+
addFile(filePath: string, graph: CodeGraph): void;
|
|
25
|
+
/** Registered file paths in insertion order. */
|
|
26
|
+
get filePaths(): string[];
|
|
27
|
+
/** Total number of registered files. */
|
|
28
|
+
get fileCount(): number;
|
|
29
|
+
/** Retrieve a file's CodeGraph, or undefined if not registered. */
|
|
30
|
+
getGraph(filePath: string): CodeGraph | undefined;
|
|
31
|
+
/** Retrieve a file's CircleIR, or undefined if not registered. */
|
|
32
|
+
getIR(filePath: string): CircleIR | undefined;
|
|
33
|
+
/** Lazily-built SymbolTable covering all registered files. */
|
|
34
|
+
get symbolTable(): SymbolTable;
|
|
35
|
+
/** Lazily-built TypeHierarchyResolver covering all registered files. */
|
|
36
|
+
get typeHierarchy(): TypeHierarchyResolver;
|
|
37
|
+
/**
|
|
38
|
+
* Lazily-built CrossFileResolver.
|
|
39
|
+
* Accesses `this.symbolTable` and `this.typeHierarchy` (also lazy) so all
|
|
40
|
+
* three are computed together on the first call after any `addFile()`.
|
|
41
|
+
*/
|
|
42
|
+
get resolver(): CrossFileResolver;
|
|
43
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ProjectGraph
|
|
3
|
+
*
|
|
4
|
+
* Wraps multiple per-file CodeGraph instances with lazy cross-file resolution
|
|
5
|
+
* infrastructure (SymbolTable, TypeHierarchyResolver, CrossFileResolver).
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* const pg = new ProjectGraph();
|
|
9
|
+
* pg.addFile('/src/A.java', graphA);
|
|
10
|
+
* pg.addFile('/src/B.java', graphB);
|
|
11
|
+
* const flows = pg.resolver.findCrossFileTaintFlows();
|
|
12
|
+
*/
|
|
13
|
+
import { SymbolTable, TypeHierarchyResolver, CrossFileResolver, } from '../resolution/index.js';
|
|
14
|
+
export class ProjectGraph {
|
|
15
|
+
files = new Map();
|
|
16
|
+
// Lazy caches — nulled whenever a new file is added
|
|
17
|
+
_symbolTable = null;
|
|
18
|
+
_typeHierarchy = null;
|
|
19
|
+
_resolver = null;
|
|
20
|
+
/**
|
|
21
|
+
* Register a file's CodeGraph. Invalidates all lazy caches.
|
|
22
|
+
*/
|
|
23
|
+
addFile(filePath, graph) {
|
|
24
|
+
this.files.set(filePath, { graph, ir: graph.ir });
|
|
25
|
+
this._symbolTable = null;
|
|
26
|
+
this._typeHierarchy = null;
|
|
27
|
+
this._resolver = null;
|
|
28
|
+
}
|
|
29
|
+
/** Registered file paths in insertion order. */
|
|
30
|
+
get filePaths() {
|
|
31
|
+
return [...this.files.keys()];
|
|
32
|
+
}
|
|
33
|
+
/** Total number of registered files. */
|
|
34
|
+
get fileCount() {
|
|
35
|
+
return this.files.size;
|
|
36
|
+
}
|
|
37
|
+
/** Retrieve a file's CodeGraph, or undefined if not registered. */
|
|
38
|
+
getGraph(filePath) {
|
|
39
|
+
return this.files.get(filePath)?.graph;
|
|
40
|
+
}
|
|
41
|
+
/** Retrieve a file's CircleIR, or undefined if not registered. */
|
|
42
|
+
getIR(filePath) {
|
|
43
|
+
return this.files.get(filePath)?.ir;
|
|
44
|
+
}
|
|
45
|
+
/** Lazily-built SymbolTable covering all registered files. */
|
|
46
|
+
get symbolTable() {
|
|
47
|
+
if (!this._symbolTable) {
|
|
48
|
+
const st = new SymbolTable();
|
|
49
|
+
for (const [path, { ir }] of this.files)
|
|
50
|
+
st.addFromIR(ir, path);
|
|
51
|
+
this._symbolTable = st;
|
|
52
|
+
}
|
|
53
|
+
return this._symbolTable;
|
|
54
|
+
}
|
|
55
|
+
/** Lazily-built TypeHierarchyResolver covering all registered files. */
|
|
56
|
+
get typeHierarchy() {
|
|
57
|
+
if (!this._typeHierarchy) {
|
|
58
|
+
const th = new TypeHierarchyResolver();
|
|
59
|
+
for (const [path, { ir }] of this.files)
|
|
60
|
+
th.addFromIR(ir, path);
|
|
61
|
+
this._typeHierarchy = th;
|
|
62
|
+
}
|
|
63
|
+
return this._typeHierarchy;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Lazily-built CrossFileResolver.
|
|
67
|
+
* Accesses `this.symbolTable` and `this.typeHierarchy` (also lazy) so all
|
|
68
|
+
* three are computed together on the first call after any `addFile()`.
|
|
69
|
+
*/
|
|
70
|
+
get resolver() {
|
|
71
|
+
if (!this._resolver) {
|
|
72
|
+
const r = new CrossFileResolver(this.symbolTable, this.typeHierarchy);
|
|
73
|
+
for (const [path, { ir }] of this.files)
|
|
74
|
+
r.addFile(path, ir);
|
|
75
|
+
this._resolver = r;
|
|
76
|
+
}
|
|
77
|
+
return this._resolver;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=project-graph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-graph.js","sourceRoot":"","sources":["../../src/graph/project-graph.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,EACL,WAAW,EACX,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,wBAAwB,CAAC;AAEhC,MAAM,OAAO,YAAY;IACN,KAAK,GAAG,IAAI,GAAG,EAA8C,CAAC;IAE/E,oDAAoD;IAC5C,YAAY,GAAyB,IAAI,CAAC;IAC1C,cAAc,GAAiC,IAAI,CAAC;IACpD,SAAS,GAAkC,IAAI,CAAC;IAExD;;OAEG;IACH,OAAO,CAAC,QAAgB,EAAE,KAAgB;QACxC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QAClD,IAAI,CAAC,YAAY,GAAK,IAAI,CAAC;QAC3B,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC3B,IAAI,CAAC,SAAS,GAAQ,IAAI,CAAC;IAC7B,CAAC;IAED,gDAAgD;IAChD,IAAI,SAAS;QACX,OAAO,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IAChC,CAAC;IAED,wCAAwC;IACxC,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACzB,CAAC;IAED,mEAAmE;IACnE,QAAQ,CAAC,QAAgB;QACvB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC;IACzC,CAAC;IAED,kEAAkE;IAClE,KAAK,CAAC,QAAgB;QACpB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;IACtC,CAAC;IAED,8DAA8D;IAC9D,IAAI,WAAW;QACb,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;YACvB,MAAM,EAAE,GAAG,IAAI,WAAW,EAAE,CAAC;YAC7B,KAAK,MAAM,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,KAAK;gBAAE,EAAE,CAAC,SAAS,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;YAChE,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC;QACzB,CAAC;QACD,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED,wEAAwE;IACxE,IAAI,aAAa;QACf,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;YACzB,MAAM,EAAE,GAAG,IAAI,qBAAqB,EAAE,CAAC;YACvC,KAAK,MAAM,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,KAAK;gBAAE,EAAE,CAAC,SAAS,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;YAChE,IAAI,CAAC,cAAc,GAAG,EAAE,CAAC;QAC3B,CAAC;QACD,OAAO,IAAI,CAAC,cAAc,CAAC;IAC7B,CAAC;IAED;;;;OAIG;IACH,IAAI,QAAQ;QACV,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,MAAM,CAAC,GAAG,IAAI,iBAAiB,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;YACtE,KAAK,MAAM,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,KAAK;gBAAE,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAC7D,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC;QACrB,CAAC;QACD,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;CACF"}
|