gitnexus 1.6.8-rc.2 → 1.6.8-rc.21
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 +20 -0
- package/dist/_shared/scope-resolution/parsed-file.d.ts +21 -0
- package/dist/_shared/scope-resolution/parsed-file.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/symbol-definition.d.ts +4 -0
- package/dist/_shared/scope-resolution/symbol-definition.d.ts.map +1 -1
- package/dist/cli/ai-context.js +1 -0
- package/dist/cli/analyze-config.js +40 -0
- package/dist/cli/analyze.d.ts +22 -0
- package/dist/cli/analyze.js +36 -6
- package/dist/cli/clean.d.ts +1 -0
- package/dist/cli/clean.js +43 -1
- package/dist/cli/eval-server.js +43 -0
- package/dist/cli/help-i18n.js +11 -0
- package/dist/cli/i18n/en.d.ts +13 -0
- package/dist/cli/i18n/en.js +13 -0
- package/dist/cli/i18n/resources.d.ts +26 -0
- package/dist/cli/i18n/zh-CN.d.ts +13 -0
- package/dist/cli/i18n/zh-CN.js +13 -0
- package/dist/cli/index.js +19 -0
- package/dist/cli/list.js +12 -0
- package/dist/cli/optional-grammars.d.ts +6 -8
- package/dist/cli/optional-grammars.js +8 -11
- package/dist/cli/skill-gen.js +1 -0
- package/dist/cli/status.js +26 -5
- package/dist/cli/tool.d.ts +12 -1
- package/dist/cli/tool.js +41 -1
- package/dist/core/embeddings/embedder.js +4 -0
- package/dist/core/embeddings/embedding-pipeline.js +27 -16
- package/dist/core/embeddings/onnxruntime-common-resolver.d.ts +6 -0
- package/dist/core/embeddings/onnxruntime-common-resolver.js +130 -0
- package/dist/core/graph/import-cycles.d.ts +10 -0
- package/dist/core/graph/import-cycles.js +103 -0
- package/dist/core/group/extractors/grpc-patterns/proto.js +10 -6
- package/dist/core/group/extractors/http-patterns/java.js +3 -50
- package/dist/core/group/extractors/http-patterns/kotlin.js +7 -7
- package/dist/core/group/extractors/include-extractor.js +7 -7
- package/dist/core/ingestion/cfg/cfg-builder.d.ts +64 -0
- package/dist/core/ingestion/cfg/cfg-builder.js +129 -0
- package/dist/core/ingestion/cfg/collect.d.ts +30 -0
- package/dist/core/ingestion/cfg/collect.js +34 -0
- package/dist/core/ingestion/cfg/control-flow-context.d.ts +97 -0
- package/dist/core/ingestion/cfg/control-flow-context.js +113 -0
- package/dist/core/ingestion/cfg/emit.d.ts +144 -0
- package/dist/core/ingestion/cfg/emit.js +315 -0
- package/dist/core/ingestion/cfg/reaching-defs.d.ts +90 -0
- package/dist/core/ingestion/cfg/reaching-defs.js +364 -0
- package/dist/core/ingestion/cfg/traversal-result.d.ts +20 -0
- package/dist/core/ingestion/cfg/traversal-result.js +2 -0
- package/dist/core/ingestion/cfg/types.d.ts +231 -0
- package/dist/core/ingestion/cfg/types.js +13 -0
- package/dist/core/ingestion/cfg/visitors/typescript-harvest.d.ts +144 -0
- package/dist/core/ingestion/cfg/visitors/typescript-harvest.js +1032 -0
- package/dist/core/ingestion/cfg/visitors/typescript.d.ts +66 -0
- package/dist/core/ingestion/cfg/visitors/typescript.js +584 -0
- package/dist/core/ingestion/language-provider.d.ts +36 -2
- package/dist/core/ingestion/languages/c-cpp.js +11 -4
- package/dist/core/ingestion/languages/cpp/arity-metadata.js +6 -2
- package/dist/core/ingestion/languages/cpp/captures.js +24 -1
- package/dist/core/ingestion/languages/cpp/query.js +23 -0
- package/dist/core/ingestion/languages/java.js +3 -0
- package/dist/core/ingestion/languages/kotlin/query.js +3 -2
- package/dist/core/ingestion/languages/kotlin.js +5 -1
- package/dist/core/ingestion/languages/typescript.js +5 -0
- package/dist/core/ingestion/method-extractors/configs/c-cpp.js +6 -13
- package/dist/core/ingestion/method-extractors/generic.js +1 -0
- package/dist/core/ingestion/method-types.d.ts +2 -0
- package/dist/core/ingestion/model/symbol-table.d.ts +1 -0
- package/dist/core/ingestion/model/symbol-table.js +1 -0
- package/dist/core/ingestion/parsing-processor.js +22 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +18 -1
- package/dist/core/ingestion/pipeline-phases/routes.js +64 -14
- package/dist/core/ingestion/pipeline.d.ts +57 -0
- package/dist/core/ingestion/route-extractors/spring-shared.d.ts +50 -0
- package/dist/core/ingestion/route-extractors/spring-shared.js +80 -0
- package/dist/core/ingestion/route-extractors/spring.d.ts +35 -0
- package/dist/core/ingestion/route-extractors/spring.js +136 -0
- package/dist/core/ingestion/scope-extractor.js +3 -0
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +12 -0
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +145 -24
- package/dist/core/ingestion/scope-resolution/pipeline/phase.js +6 -0
- package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.js +43 -3
- package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +21 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.js +207 -0
- package/dist/core/ingestion/scope-resolution/resolution-outcome.d.ts +1 -1
- package/dist/core/ingestion/taint/emit.d.ts +124 -0
- package/dist/core/ingestion/taint/emit.js +204 -0
- package/dist/core/ingestion/taint/match.d.ts +153 -0
- package/dist/core/ingestion/taint/match.js +278 -0
- package/dist/core/ingestion/taint/path-codec.d.ts +134 -0
- package/dist/core/ingestion/taint/path-codec.js +190 -0
- package/dist/core/ingestion/taint/propagate.d.ts +216 -0
- package/dist/core/ingestion/taint/propagate.js +664 -0
- package/dist/core/ingestion/taint/site-safety.d.ts +29 -0
- package/dist/core/ingestion/taint/site-safety.js +98 -0
- package/dist/core/ingestion/taint/source-sink-config.d.ts +94 -23
- package/dist/core/ingestion/taint/source-sink-config.js +11 -11
- package/dist/core/ingestion/taint/source-sink-registry.d.ts +6 -4
- package/dist/core/ingestion/taint/source-sink-registry.js +6 -4
- package/dist/core/ingestion/taint/typescript-model.d.ts +38 -0
- package/dist/core/ingestion/taint/typescript-model.js +102 -0
- package/dist/core/ingestion/utils/method-props.js +1 -0
- package/dist/core/ingestion/workers/clone-safety.d.ts +109 -0
- package/dist/core/ingestion/workers/clone-safety.js +465 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +11 -0
- package/dist/core/ingestion/workers/parse-worker.js +70 -53
- package/dist/core/ingestion/workers/post-result.d.ts +22 -0
- package/dist/core/ingestion/workers/post-result.js +87 -0
- package/dist/core/ingestion/workers/result-merge.d.ts +20 -0
- package/dist/core/ingestion/workers/result-merge.js +43 -0
- package/dist/core/ingestion/workers/worker-pool.d.ts +15 -0
- package/dist/core/ingestion/workers/worker-pool.js +33 -16
- package/dist/core/lbug/lbug-adapter.d.ts +19 -0
- package/dist/core/lbug/lbug-adapter.js +56 -1
- package/dist/core/run-analyze.d.ts +87 -0
- package/dist/core/run-analyze.js +280 -25
- package/dist/core/tree-sitter/parser-loader.js +5 -4
- package/dist/core/tree-sitter/vendored-grammars.d.ts +39 -0
- package/dist/core/tree-sitter/vendored-grammars.js +57 -0
- package/dist/mcp/core/embedder.js +4 -0
- package/dist/mcp/local/local-backend.d.ts +86 -2
- package/dist/mcp/local/local-backend.js +657 -27
- package/dist/mcp/resources.js +1 -0
- package/dist/mcp/tools.d.ts +9 -0
- package/dist/mcp/tools.js +109 -0
- package/dist/server/analyze-launch.d.ts +29 -0
- package/dist/server/analyze-launch.js +137 -0
- package/dist/server/analyze-upload.d.ts +33 -0
- package/dist/server/analyze-upload.js +123 -0
- package/dist/server/analyze-worker-ipc.d.ts +58 -0
- package/dist/server/analyze-worker-ipc.js +16 -0
- package/dist/server/analyze-worker.d.ts +17 -1
- package/dist/server/analyze-worker.js +7 -1
- package/dist/server/api.js +46 -140
- package/dist/server/git-clone.d.ts +1 -0
- package/dist/server/git-clone.js +1 -1
- package/dist/server/middleware.d.ts +11 -0
- package/dist/server/middleware.js +27 -0
- package/dist/server/upload-ingest.d.ts +56 -0
- package/dist/server/upload-ingest.js +276 -0
- package/dist/server/upload-paths.d.ts +31 -0
- package/dist/server/upload-paths.js +51 -0
- package/dist/server/upload-sweep.d.ts +20 -0
- package/dist/server/upload-sweep.js +57 -0
- package/dist/storage/branch-index.d.ts +52 -0
- package/dist/storage/branch-index.js +65 -0
- package/dist/storage/git.d.ts +11 -0
- package/dist/storage/git.js +28 -0
- package/dist/storage/parse-cache.d.ts +22 -1
- package/dist/storage/parse-cache.js +32 -10
- package/dist/storage/repo-manager.d.ts +122 -10
- package/dist/storage/repo-manager.js +162 -21
- package/hooks/antigravity/gitnexus-antigravity-hook.cjs +42 -8
- package/hooks/claude/gitnexus-hook.cjs +36 -8
- package/hooks/claude/hook-db-lock-probe.cjs +123 -2
- package/package.json +4 -2
- package/scripts/assert-publish-grammar-coverage.cjs +31 -0
- package/scripts/build-tree-sitter-grammars.cjs +16 -10
- package/skills/gitnexus-guide.md +11 -0
- package/vendor/tree-sitter-c/package.json +1 -1
- package/vendor/tree-sitter-dart/package.json +1 -1
- package/vendor/tree-sitter-kotlin/package.json +1 -1
- package/vendor/tree-sitter-proto/package.json +1 -1
- package/vendor/tree-sitter-swift/package.json +1 -1
- package/web/assets/{agent-CKPMqImC.js → agent-ay4LD70X.js} +1 -1
- package/web/assets/{architectureDiagram-UL44E2DR-D1EXI0zA.js → architectureDiagram-UL44E2DR-Dc-viYhd.js} +1 -1
- package/web/assets/{chunk-LCXTWHL2-Dfmux4m1.js → chunk-LCXTWHL2-4rpojOyj.js} +1 -1
- package/web/assets/{chunk-RG4AUYOV-CAkzcoRj.js → chunk-RG4AUYOV-BnOy944n.js} +1 -1
- package/web/assets/{classDiagram-KGZ6W3CR-Bv93af_b.js → classDiagram-KGZ6W3CR-Bsgpy98Q.js} +1 -1
- package/web/assets/{classDiagram-v2-72OJOZXJ-CLfEqHUa.js → classDiagram-v2-72OJOZXJ-D5atDGjc.js} +1 -1
- package/web/assets/{diagram-3NCE3AQN-BqAtKUpW.js → diagram-3NCE3AQN-Cz1OEMVi.js} +1 -1
- package/web/assets/{diagram-GF46GFSD-CaBG6n6o.js → diagram-GF46GFSD-CWYwzfP8.js} +1 -1
- package/web/assets/{diagram-QXG6HAR7-CZ-O3rcV.js → diagram-QXG6HAR7-DtBObr8L.js} +1 -1
- package/web/assets/{diagram-WEQXMOUZ-P4lSL4GH.js → diagram-WEQXMOUZ-BUV44Ov_.js} +1 -1
- package/web/assets/{erDiagram-L5TCEMPS-QN2eEP1e.js → erDiagram-L5TCEMPS-Ds9s-sRF.js} +1 -1
- package/web/assets/{flowDiagram-H6V6AXG4-CXbXImlN.js → flowDiagram-H6V6AXG4-CupStHQb.js} +1 -1
- package/web/assets/index-COMMmbxW.css +2 -0
- package/web/assets/{index-CG6q8eTs.js → index-Do8AE5yF.js} +85 -85
- package/web/assets/{infoDiagram-3YFTVSEB-DmLICZx1.js → infoDiagram-3YFTVSEB-DPTXSbgu.js} +1 -1
- package/web/assets/{ishikawaDiagram-BNXS4ZKH-6LKOvBfp.js → ishikawaDiagram-BNXS4ZKH-CeK-Qhet.js} +1 -1
- package/web/assets/{kanban-definition-75IXJCU3-DgDi9oJT.js → kanban-definition-75IXJCU3-D4tx6QQ-.js} +1 -1
- package/web/assets/{mindmap-definition-2TDM6QVE-CwR5sBB-.js → mindmap-definition-2TDM6QVE-Bcto_kGa.js} +1 -1
- package/web/assets/{pieDiagram-CU6KROY3-By8g6f6B.js → pieDiagram-CU6KROY3-DPIeBTG6.js} +1 -1
- package/web/assets/{requirementDiagram-JXO7QTGE-7oDcJ1_J.js → requirementDiagram-JXO7QTGE-DDGXjVKO.js} +1 -1
- package/web/assets/{sequenceDiagram-VS2MUI6T-FySeKCUy.js → sequenceDiagram-VS2MUI6T-BzDqpcVW.js} +1 -1
- package/web/assets/{stateDiagram-7D4R322I-CB2nABwH.js → stateDiagram-7D4R322I-Bx__als3.js} +1 -1
- package/web/assets/{stateDiagram-v2-36443NZ5-COBGd2RL.js → stateDiagram-v2-36443NZ5-QqOh2yO-.js} +1 -1
- package/web/assets/{timeline-definition-O6YCAMPW-Ds2CnVZK.js → timeline-definition-O6YCAMPW-C1eogTOG.js} +1 -1
- package/web/assets/{vennDiagram-MWXL3ELB-DUIEwXWp.js → vennDiagram-MWXL3ELB-D20F4rSW.js} +1 -1
- package/web/assets/{wardleyDiagram-CUQ6CDDI-DEiFPQih.js → wardleyDiagram-CUQ6CDDI-BeBHg7ST.js} +1 -1
- package/web/assets/{xychartDiagram-N2JHSOCM-BUbayhST.js → xychartDiagram-N2JHSOCM-CdOWeoNI.js} +1 -1
- package/web/index.html +2 -2
- package/scripts/materialize-vendor-grammars.cjs +0 -97
- package/web/assets/index-BKWA-m7o.css +0 -2
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
export class CfgBuilder {
|
|
2
|
+
filePath;
|
|
3
|
+
functionStartLine;
|
|
4
|
+
functionEndLine;
|
|
5
|
+
functionStartColumn;
|
|
6
|
+
blocks = [];
|
|
7
|
+
edges = [];
|
|
8
|
+
edgeKeys = new Set();
|
|
9
|
+
entryIndex;
|
|
10
|
+
exitIndex;
|
|
11
|
+
constructor(filePath, functionStartLine, functionEndLine,
|
|
12
|
+
/** Start column of the owning function — disambiguates same-line functions
|
|
13
|
+
* in the BasicBlock ids (see {@link FunctionCfg.functionStartColumn}).
|
|
14
|
+
* Defaults to 0 for hand-built test CFGs that don't model columns. */
|
|
15
|
+
functionStartColumn = 0) {
|
|
16
|
+
this.filePath = filePath;
|
|
17
|
+
this.functionStartLine = functionStartLine;
|
|
18
|
+
this.functionEndLine = functionEndLine;
|
|
19
|
+
this.functionStartColumn = functionStartColumn;
|
|
20
|
+
this.entryIndex = this.newBlock(functionStartLine, functionStartLine, '', 'entry');
|
|
21
|
+
this.exitIndex = this.newBlock(functionEndLine, functionEndLine, '', 'exit');
|
|
22
|
+
}
|
|
23
|
+
/** Create a block and return its index. */
|
|
24
|
+
newBlock(startLine, endLine, text, kind = 'normal', facts) {
|
|
25
|
+
this.blocks.push({
|
|
26
|
+
startLine,
|
|
27
|
+
endLine,
|
|
28
|
+
textParts: text ? [text] : [],
|
|
29
|
+
kind,
|
|
30
|
+
statements: facts ? [facts] : [],
|
|
31
|
+
});
|
|
32
|
+
return this.blocks.length - 1;
|
|
33
|
+
}
|
|
34
|
+
/** Add a single edge (idempotent on from+to+kind). */
|
|
35
|
+
edge(from, to, kind) {
|
|
36
|
+
const key = `${from}->${to}:${kind}`;
|
|
37
|
+
if (this.edgeKeys.has(key))
|
|
38
|
+
return;
|
|
39
|
+
this.edgeKeys.add(key);
|
|
40
|
+
this.edges.push({ from, to, kind });
|
|
41
|
+
}
|
|
42
|
+
/** Wire a set of dangling exits to a single target block with one kind. */
|
|
43
|
+
connect(exits, to, kind = 'seq') {
|
|
44
|
+
for (const from of exits)
|
|
45
|
+
this.edge(from, to, kind);
|
|
46
|
+
}
|
|
47
|
+
/** Extend a block's end line as more statements accrue to it. */
|
|
48
|
+
extendBlock(index, endLine, appendText, facts) {
|
|
49
|
+
const b = this.blocks[index];
|
|
50
|
+
if (!b)
|
|
51
|
+
return;
|
|
52
|
+
if (endLine > b.endLine)
|
|
53
|
+
b.endLine = endLine;
|
|
54
|
+
if (appendText)
|
|
55
|
+
b.textParts.push(appendText);
|
|
56
|
+
if (facts)
|
|
57
|
+
b.statements.push(facts);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Attach a facts-only statement record to a block WITHOUT touching its text
|
|
61
|
+
* or line span (#2082 M2 U1) — bench fingerprints and CFG snapshots include
|
|
62
|
+
* block text, so harvesting must never perturb it (ENTRY-block param defs
|
|
63
|
+
* are the canonical use; records that must precede a walked body get their
|
|
64
|
+
* own facts-only block instead, see the catch-param handling in visitTry).
|
|
65
|
+
*/
|
|
66
|
+
attachFacts(index, facts) {
|
|
67
|
+
const b = this.blocks[index];
|
|
68
|
+
if (!b)
|
|
69
|
+
return;
|
|
70
|
+
b.statements.push(facts);
|
|
71
|
+
}
|
|
72
|
+
get blockCount() {
|
|
73
|
+
return this.blocks.length;
|
|
74
|
+
}
|
|
75
|
+
/** Produce the serializable CFG. Caller is responsible for having wired the
|
|
76
|
+
* function's dangling exits to {@link exitIndex} before calling.
|
|
77
|
+
*
|
|
78
|
+
* Pass `bindings` (the function's binding table, possibly empty) to emit
|
|
79
|
+
* statement facts (#2082 M2 U1) — every block then carries a `statements`
|
|
80
|
+
* array. Omit it (hand-built test CFGs, pre-M2 producers) and both fields
|
|
81
|
+
* are absent, which the reaching-defs solver reports as `no-facts`. */
|
|
82
|
+
finish(bindings) {
|
|
83
|
+
const withFacts = bindings !== undefined;
|
|
84
|
+
return {
|
|
85
|
+
filePath: this.filePath,
|
|
86
|
+
functionStartLine: this.functionStartLine,
|
|
87
|
+
functionEndLine: this.functionEndLine,
|
|
88
|
+
functionStartColumn: this.functionStartColumn,
|
|
89
|
+
entryIndex: this.entryIndex,
|
|
90
|
+
exitIndex: this.exitIndex,
|
|
91
|
+
blocks: this.blocks.map((b, index) => ({
|
|
92
|
+
index,
|
|
93
|
+
startLine: b.startLine,
|
|
94
|
+
endLine: b.endLine,
|
|
95
|
+
text: b.textParts.join('\n'),
|
|
96
|
+
kind: b.kind,
|
|
97
|
+
...(withFacts ? { statements: b.statements } : {}),
|
|
98
|
+
})),
|
|
99
|
+
edges: [...this.edges],
|
|
100
|
+
...(withFacts ? { bindings } : {}),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Block indices reachable from `entryIndex` by following edges. Backs the
|
|
106
|
+
* reachability property tests (R9) over hand-built and visitor-produced CFGs.
|
|
107
|
+
*/
|
|
108
|
+
export const reachableBlocks = (cfg) => {
|
|
109
|
+
const adj = new Map();
|
|
110
|
+
for (const e of cfg.edges) {
|
|
111
|
+
const list = adj.get(e.from);
|
|
112
|
+
if (list)
|
|
113
|
+
list.push(e.to);
|
|
114
|
+
else
|
|
115
|
+
adj.set(e.from, [e.to]);
|
|
116
|
+
}
|
|
117
|
+
const seen = new Set([cfg.entryIndex]);
|
|
118
|
+
const stack = [cfg.entryIndex];
|
|
119
|
+
while (stack.length) {
|
|
120
|
+
const n = stack.pop();
|
|
121
|
+
for (const next of adj.get(n) ?? []) {
|
|
122
|
+
if (!seen.has(next)) {
|
|
123
|
+
seen.add(next);
|
|
124
|
+
stack.push(next);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return seen;
|
|
129
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* collectFunctionCfgs (issue #2081, M1).
|
|
3
|
+
*
|
|
4
|
+
* Walks a parsed file's tree-sitter tree and builds one {@link FunctionCfg} per
|
|
5
|
+
* CFG-bearing function via the language's {@link CfgVisitor}. Runs IN THE PARSE
|
|
6
|
+
* WORKER (where the AST lives — KTD1/KTD7); the result rides on
|
|
7
|
+
* `ParsedFile.cfgSideChannel` across the worker→main boundary.
|
|
8
|
+
*
|
|
9
|
+
* Nested functions are enumerated independently — each gets its own CFG, and
|
|
10
|
+
* appears as an opaque straight-line block in its enclosing function's CFG (the
|
|
11
|
+
* visitor does not descend into nested function bodies). `maxFunctionLines`
|
|
12
|
+
* bounds per-function cost: a function whose source span exceeds the cap is
|
|
13
|
+
* skipped (and counted) rather than walked, so a pathological mega-function
|
|
14
|
+
* cannot blow up worker time/memory. A cap of `0` means no limit.
|
|
15
|
+
*/
|
|
16
|
+
import type { SyntaxNode } from '../utils/ast-helpers.js';
|
|
17
|
+
import type { CfgVisitor, FunctionCfg } from './types.js';
|
|
18
|
+
/**
|
|
19
|
+
* Default per-function source-line cap used by the worker when the `--pdg` run
|
|
20
|
+
* does not specify `pdgMaxFunctionLines`. A function longer than this (almost
|
|
21
|
+
* always minified/generated code) is skipped rather than walked — its CFG is
|
|
22
|
+
* both expensive and low-value. Overridable via `PipelineOptions.pdgMaxFunctionLines`.
|
|
23
|
+
*/
|
|
24
|
+
export declare const DEFAULT_PDG_MAX_FUNCTION_LINES = 2000;
|
|
25
|
+
export interface CollectedCfgs {
|
|
26
|
+
readonly cfgs: readonly FunctionCfg[];
|
|
27
|
+
/** Functions skipped for exceeding `maxFunctionLines` (0 ⇒ none skipped). */
|
|
28
|
+
readonly skipped: number;
|
|
29
|
+
}
|
|
30
|
+
export declare function collectFunctionCfgs(root: SyntaxNode, visitor: CfgVisitor<SyntaxNode>, filePath: string, maxFunctionLines?: number): CollectedCfgs;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default per-function source-line cap used by the worker when the `--pdg` run
|
|
3
|
+
* does not specify `pdgMaxFunctionLines`. A function longer than this (almost
|
|
4
|
+
* always minified/generated code) is skipped rather than walked — its CFG is
|
|
5
|
+
* both expensive and low-value. Overridable via `PipelineOptions.pdgMaxFunctionLines`.
|
|
6
|
+
*/
|
|
7
|
+
export const DEFAULT_PDG_MAX_FUNCTION_LINES = 2000;
|
|
8
|
+
export function collectFunctionCfgs(root, visitor, filePath, maxFunctionLines = 0) {
|
|
9
|
+
const cfgs = [];
|
|
10
|
+
let skipped = 0;
|
|
11
|
+
const stack = [root];
|
|
12
|
+
while (stack.length) {
|
|
13
|
+
const node = stack.pop();
|
|
14
|
+
if (visitor.isFunction(node)) {
|
|
15
|
+
const lines = node.endPosition.row - node.startPosition.row + 1;
|
|
16
|
+
if (maxFunctionLines > 0 && lines > maxFunctionLines) {
|
|
17
|
+
skipped++;
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
const cfg = visitor.buildFunctionCfg(node, filePath);
|
|
21
|
+
if (cfg)
|
|
22
|
+
cfgs.push(cfg);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// Descend regardless (a skipped mega-function may still contain small
|
|
26
|
+
// nested functions that are worth a CFG of their own).
|
|
27
|
+
for (let i = node.namedChildCount - 1; i >= 0; i--) {
|
|
28
|
+
const child = node.namedChild(i);
|
|
29
|
+
if (child)
|
|
30
|
+
stack.push(child);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return { cfgs, skipped };
|
|
34
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ControlFlowContext (issue #2081 M1; finalizer frames added by #2082 M2 U2).
|
|
3
|
+
*
|
|
4
|
+
* Resolves the targets of `break`/`continue` (plain and labeled) as the visitor
|
|
5
|
+
* descends through loops and switches. Loops and switches push a target frame
|
|
6
|
+
* on entry and pop it on exit; a labeled statement attaches its label to the
|
|
7
|
+
* frame of the construct it labels, so `break outer` / `continue outer` resolve
|
|
8
|
+
* against the right enclosing loop/switch rather than the nearest one.
|
|
9
|
+
*
|
|
10
|
+
* M2 adds FINALIZER frames, interleaved on the SAME stack as loop/switch frames
|
|
11
|
+
* — interleaving is load-bearing: a jump must route through exactly the
|
|
12
|
+
* `finally` bodies lexically BETWEEN it and its target (target-relative
|
|
13
|
+
* threading). A `break` whose loop lives entirely inside the `try` crosses no
|
|
14
|
+
* finally and must keep its direct edge; re-routing it anyway would force the
|
|
15
|
+
* only path to the in-try continuation through the finally, letting a finally
|
|
16
|
+
* redefinition falsely KILL in-loop definitions for the downstream
|
|
17
|
+
* reaching-defs pass (a taint false negative). A parallel stack cannot express
|
|
18
|
+
* that between-ness, which is why the frames live here.
|
|
19
|
+
*/
|
|
20
|
+
import type { CfgBuilder } from './cfg-builder.js';
|
|
21
|
+
import type { CfgEdgeKind } from './types.js';
|
|
22
|
+
/** A `finally` whose body any crossing jump must route through. */
|
|
23
|
+
export interface FinalizerFrame {
|
|
24
|
+
readonly kind: 'finalizer';
|
|
25
|
+
/** Entry block of the finally body. */
|
|
26
|
+
readonly entry: number;
|
|
27
|
+
/**
|
|
28
|
+
* Completion legs registered by jumps that crossed this finally: once the
|
|
29
|
+
* owning try pops the frame, it wires `finally-exits → to` with `kind` for
|
|
30
|
+
* each entry. Mutated by the jump handlers via {@link ControlFlowContext}.
|
|
31
|
+
*/
|
|
32
|
+
readonly pending: {
|
|
33
|
+
to: number;
|
|
34
|
+
kind: CfgEdgeKind;
|
|
35
|
+
}[];
|
|
36
|
+
}
|
|
37
|
+
/** A resolved jump: its ultimate target + the finallys it crosses (inner→outer). */
|
|
38
|
+
export interface JumpResolution {
|
|
39
|
+
readonly target: number;
|
|
40
|
+
readonly finalizers: readonly FinalizerFrame[];
|
|
41
|
+
}
|
|
42
|
+
export declare class ControlFlowContext {
|
|
43
|
+
private readonly stack;
|
|
44
|
+
pushLoop(continueTo: number, breakTo: number, labels?: readonly string[]): void;
|
|
45
|
+
pushSwitch(breakTo: number, labels?: readonly string[]): void;
|
|
46
|
+
/** Push a labeled non-loop statement's break-target frame. */
|
|
47
|
+
pushLabeledBlock(breakTo: number, labels: readonly string[]): void;
|
|
48
|
+
/**
|
|
49
|
+
* Push a finalizer frame and return it — the owning `visitTry` keeps the
|
|
50
|
+
* reference to wire {@link FinalizerFrame.pending} after popping it.
|
|
51
|
+
*/
|
|
52
|
+
pushFinalizer(entry: number): FinalizerFrame;
|
|
53
|
+
pop(): void;
|
|
54
|
+
/**
|
|
55
|
+
* Resolve a `break`: the nearest enclosing loop/switch frame (or, with a
|
|
56
|
+
* label, the nearest frame carrying that label) plus every finalizer frame
|
|
57
|
+
* stacked ABOVE it — i.e. exactly the finallys the jump crosses, innermost
|
|
58
|
+
* first. Returns `undefined` if there is no valid target (malformed input or
|
|
59
|
+
* an unmodeled label) — the caller falls back to its conservative routing and
|
|
60
|
+
* threads nothing.
|
|
61
|
+
*/
|
|
62
|
+
resolveBreak(label?: string): JumpResolution | undefined;
|
|
63
|
+
/** Resolve a `continue`: like {@link resolveBreak} but only loop frames match. */
|
|
64
|
+
resolveContinue(label?: string): JumpResolution | undefined;
|
|
65
|
+
/** Every active finalizer, innermost first — what a `return` must cross. */
|
|
66
|
+
finalizersForReturn(): readonly FinalizerFrame[];
|
|
67
|
+
/**
|
|
68
|
+
* Target block for a `break` (no finalizer info) — see {@link resolveBreak}.
|
|
69
|
+
* Prefer `resolveBreak` + {@link wireJumpThroughFinalizers} in visitors: a
|
|
70
|
+
* target-only lookup silently loses finalizer threading (the M2 soundness
|
|
71
|
+
* fix). Kept for target-shape assertions in tests.
|
|
72
|
+
*/
|
|
73
|
+
breakTarget(label?: string): number | undefined;
|
|
74
|
+
/** Target block for a `continue` — same caveat as {@link breakTarget}. */
|
|
75
|
+
continueTarget(label?: string): number | undefined;
|
|
76
|
+
private resolve;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Wire a jump from `from` to `target`, routing through the finallys it
|
|
80
|
+
* crosses (innermost first). The first leg keeps the bare jump `kind`
|
|
81
|
+
* (preserving the "kind ⟹ source-block terminator" invariant in types.ts);
|
|
82
|
+
* each finally's completion leg is registered as pending on its frame with the
|
|
83
|
+
* matching `finally-*` kind and wired by the owning try via
|
|
84
|
+
* {@link drainFinalizerPending} once the finally's exits are known.
|
|
85
|
+
*
|
|
86
|
+
* Language-agnostic on purpose (#2082 M2): the threading protocol encodes
|
|
87
|
+
* three subtle invariants every future language visitor needs identically —
|
|
88
|
+
* keeping it here means a new visitor cannot drift on any of them.
|
|
89
|
+
*/
|
|
90
|
+
export declare function wireJumpThroughFinalizers(builder: CfgBuilder, from: number, finalizers: readonly FinalizerFrame[], target: number, kind: 'return' | 'break' | 'continue'): void;
|
|
91
|
+
/**
|
|
92
|
+
* Wire a popped finalizer frame's pending completion legs from the finally's
|
|
93
|
+
* exit blocks. A finally that itself always jumps (`finally { return 2; }`)
|
|
94
|
+
* has no exits — its pending legs wire nowhere, matching JS's
|
|
95
|
+
* finally-override semantics.
|
|
96
|
+
*/
|
|
97
|
+
export declare function drainFinalizerPending(builder: CfgBuilder, frame: FinalizerFrame, finallyExits: readonly number[]): void;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
export class ControlFlowContext {
|
|
2
|
+
stack = [];
|
|
3
|
+
pushLoop(continueTo, breakTo, labels = []) {
|
|
4
|
+
this.stack.push({ kind: 'loop', continueTo, breakTo, labels });
|
|
5
|
+
}
|
|
6
|
+
pushSwitch(breakTo, labels = []) {
|
|
7
|
+
this.stack.push({ kind: 'switch', breakTo, labels });
|
|
8
|
+
}
|
|
9
|
+
/** Push a labeled non-loop statement's break-target frame. */
|
|
10
|
+
pushLabeledBlock(breakTo, labels) {
|
|
11
|
+
this.stack.push({ kind: 'block', breakTo, labels });
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Push a finalizer frame and return it — the owning `visitTry` keeps the
|
|
15
|
+
* reference to wire {@link FinalizerFrame.pending} after popping it.
|
|
16
|
+
*/
|
|
17
|
+
pushFinalizer(entry) {
|
|
18
|
+
const frame = { kind: 'finalizer', entry, pending: [] };
|
|
19
|
+
this.stack.push(frame);
|
|
20
|
+
return frame;
|
|
21
|
+
}
|
|
22
|
+
pop() {
|
|
23
|
+
this.stack.pop();
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Resolve a `break`: the nearest enclosing loop/switch frame (or, with a
|
|
27
|
+
* label, the nearest frame carrying that label) plus every finalizer frame
|
|
28
|
+
* stacked ABOVE it — i.e. exactly the finallys the jump crosses, innermost
|
|
29
|
+
* first. Returns `undefined` if there is no valid target (malformed input or
|
|
30
|
+
* an unmodeled label) — the caller falls back to its conservative routing and
|
|
31
|
+
* threads nothing.
|
|
32
|
+
*/
|
|
33
|
+
resolveBreak(label) {
|
|
34
|
+
return this.resolve((f) => label === undefined
|
|
35
|
+
? f.kind !== 'block' // an unlabeled break never targets a labeled block
|
|
36
|
+
: f.labels.includes(label));
|
|
37
|
+
}
|
|
38
|
+
/** Resolve a `continue`: like {@link resolveBreak} but only loop frames match. */
|
|
39
|
+
resolveContinue(label) {
|
|
40
|
+
return this.resolve((f) => f.kind === 'loop' && (label === undefined || f.labels.includes(label)), (f) => f.continueTo);
|
|
41
|
+
}
|
|
42
|
+
/** Every active finalizer, innermost first — what a `return` must cross. */
|
|
43
|
+
finalizersForReturn() {
|
|
44
|
+
const fins = [];
|
|
45
|
+
for (let i = this.stack.length - 1; i >= 0; i--) {
|
|
46
|
+
const f = this.stack[i];
|
|
47
|
+
if (f.kind === 'finalizer')
|
|
48
|
+
fins.push(f);
|
|
49
|
+
}
|
|
50
|
+
return fins;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Target block for a `break` (no finalizer info) — see {@link resolveBreak}.
|
|
54
|
+
* Prefer `resolveBreak` + {@link wireJumpThroughFinalizers} in visitors: a
|
|
55
|
+
* target-only lookup silently loses finalizer threading (the M2 soundness
|
|
56
|
+
* fix). Kept for target-shape assertions in tests.
|
|
57
|
+
*/
|
|
58
|
+
breakTarget(label) {
|
|
59
|
+
return this.resolveBreak(label)?.target;
|
|
60
|
+
}
|
|
61
|
+
/** Target block for a `continue` — same caveat as {@link breakTarget}. */
|
|
62
|
+
continueTarget(label) {
|
|
63
|
+
return this.resolveContinue(label)?.target;
|
|
64
|
+
}
|
|
65
|
+
resolve(matches, targetOf = (f) => f.breakTo) {
|
|
66
|
+
const crossed = [];
|
|
67
|
+
for (let i = this.stack.length - 1; i >= 0; i--) {
|
|
68
|
+
const f = this.stack[i];
|
|
69
|
+
if (f.kind === 'finalizer') {
|
|
70
|
+
crossed.push(f);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (matches(f))
|
|
74
|
+
return { target: targetOf(f), finalizers: crossed };
|
|
75
|
+
}
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Wire a jump from `from` to `target`, routing through the finallys it
|
|
81
|
+
* crosses (innermost first). The first leg keeps the bare jump `kind`
|
|
82
|
+
* (preserving the "kind ⟹ source-block terminator" invariant in types.ts);
|
|
83
|
+
* each finally's completion leg is registered as pending on its frame with the
|
|
84
|
+
* matching `finally-*` kind and wired by the owning try via
|
|
85
|
+
* {@link drainFinalizerPending} once the finally's exits are known.
|
|
86
|
+
*
|
|
87
|
+
* Language-agnostic on purpose (#2082 M2): the threading protocol encodes
|
|
88
|
+
* three subtle invariants every future language visitor needs identically —
|
|
89
|
+
* keeping it here means a new visitor cannot drift on any of them.
|
|
90
|
+
*/
|
|
91
|
+
export function wireJumpThroughFinalizers(builder, from, finalizers, target, kind) {
|
|
92
|
+
if (finalizers.length === 0) {
|
|
93
|
+
builder.edge(from, target, kind);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const completionKind = `finally-${kind}`;
|
|
97
|
+
builder.edge(from, finalizers[0].entry, kind);
|
|
98
|
+
for (let i = 0; i < finalizers.length; i++) {
|
|
99
|
+
const to = i + 1 < finalizers.length ? finalizers[i + 1].entry : target;
|
|
100
|
+
finalizers[i].pending.push({ to, kind: completionKind });
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Wire a popped finalizer frame's pending completion legs from the finally's
|
|
105
|
+
* exit blocks. A finally that itself always jumps (`finally { return 2; }`)
|
|
106
|
+
* has no exits — its pending legs wire nowhere, matching JS's
|
|
107
|
+
* finally-override semantics.
|
|
108
|
+
*/
|
|
109
|
+
export function drainFinalizerPending(builder, frame, finallyExits) {
|
|
110
|
+
for (const p of frame.pending) {
|
|
111
|
+
builder.connect(finallyExits, p.to, p.kind);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cfg/emit.ts (issue #2081, M1) — serialized side-channel → graph.
|
|
3
|
+
*
|
|
4
|
+
* Pure helper: given a file's per-function CFGs (off `ParsedFile.cfgSideChannel`,
|
|
5
|
+
* produced by the worker in U3), emit one persisted `BasicBlock` node per block
|
|
6
|
+
* and one `CFG` edge per edge into the {@link KnowledgeGraph}. Invoked from
|
|
7
|
+
* scope-resolution (run.ts Phase 4) while the disk-backed ParsedFile store is
|
|
8
|
+
* still live — the only window where the worker-built CFGs are loaded (KTD1/
|
|
9
|
+
* KTD5). Default (`--pdg` off) runs never call this, so the emitted graph stays
|
|
10
|
+
* byte-identical to a pre-#2081 run.
|
|
11
|
+
*
|
|
12
|
+
* BasicBlock id: `BasicBlock:<filePath>:<functionStartLine>:<functionStartColumn>:<blockIndex>`
|
|
13
|
+
* (KTD3). The function start line+column segments disambiguate blocks across
|
|
14
|
+
* multiple functions in one file — including same-line functions — since each
|
|
15
|
+
* function's block indices restart at 0; blocks carry no `name` (the
|
|
16
|
+
* BasicBlock table has no such column). The edge KIND
|
|
17
|
+
* (`seq`/`cond-true`/…) rides in the relationship `reason` — CFG edges are
|
|
18
|
+
* values of the single `CodeRelation` table's `type` column (`'CFG'`), so the
|
|
19
|
+
* kind cannot be its own edge type and is queried via `reason`.
|
|
20
|
+
*/
|
|
21
|
+
import type { KnowledgeGraph } from '../../graph/types.js';
|
|
22
|
+
import type { BindingEntry, FunctionCfg } from './types.js';
|
|
23
|
+
/**
|
|
24
|
+
* Default per-function CFG edge cap. A pathological generated function could
|
|
25
|
+
* otherwise emit an unbounded edge set; the cap bounds graph growth and is
|
|
26
|
+
* overridable via `--pdg` options. `0` (in options) means no cap (unlimited
|
|
27
|
+
* — see the `cap` mapping in {@link emitFileCfgs}); `undefined` means this
|
|
28
|
+
* default.
|
|
29
|
+
*/
|
|
30
|
+
export declare const DEFAULT_MAX_CFG_EDGES_PER_FUNCTION = 5000;
|
|
31
|
+
/**
|
|
32
|
+
* Default per-function REACHING_DEF edge cap (#2082 M2 KTD9). 4000 mirrors
|
|
33
|
+
* Joern's per-method `maxNumberOfDefinitions` — the closest production prior
|
|
34
|
+
* art — but truncates-and-warns instead of silently skipping the function.
|
|
35
|
+
* Counts (defBlock, useBlock, binding) DEDUPED edges, not statement-level
|
|
36
|
+
* facts. `0` ⇒ unlimited; `undefined` ⇒ this default.
|
|
37
|
+
*/
|
|
38
|
+
export declare const DEFAULT_PDG_MAX_REACHING_DEF_EDGES_PER_FUNCTION = 4000;
|
|
39
|
+
/**
|
|
40
|
+
* Fact-materialization headroom over the edge cap (#2082 M2 U3/F3): facts are
|
|
41
|
+
* O(defs×uses) BY SPEC in merge-heavy code, and the edge cap alone bounds the
|
|
42
|
+
* GRAPH, not the per-function memory spike of materializing facts before
|
|
43
|
+
* dedup. {@link emitFileReachingDefs} hands `edgeCap × this` to
|
|
44
|
+
* `computeReachingDefs` as `maxFacts` (unlimited when the edge cap is 0) —
|
|
45
|
+
* single source of truth; the DEFAULT constant below is derived, never the
|
|
46
|
+
* mechanism.
|
|
47
|
+
*/
|
|
48
|
+
export declare const REACHING_DEF_FACTS_PER_EDGE_CAP = 4;
|
|
49
|
+
/** Derived emit-path fact limit at the default edge cap (bench/doc anchor). */
|
|
50
|
+
export declare const DEFAULT_PDG_MAX_REACHING_DEF_FACTS_PER_FUNCTION: number;
|
|
51
|
+
export interface CfgEmitResult {
|
|
52
|
+
blocks: number;
|
|
53
|
+
edges: number;
|
|
54
|
+
/** Edges dropped because a function's edge count exceeded the cap. */
|
|
55
|
+
droppedEdges: number;
|
|
56
|
+
/** Number of functions that hit the cap. */
|
|
57
|
+
cappedFunctions: number;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The single BasicBlock id template (module doc). Exported for the M3 taint
|
|
61
|
+
* emit path (taint/emit.ts), whose TAINTED/SANITIZES edges must address the
|
|
62
|
+
* SAME persisted block nodes — a re-derived copy of this template would
|
|
63
|
+
* silently dangle the moment either drifted.
|
|
64
|
+
*/
|
|
65
|
+
export declare const basicBlockId: (filePath: string, functionStartLine: number, functionStartColumn: number, blockIndex: number) => string;
|
|
66
|
+
/**
|
|
67
|
+
* Whether an untrusted `cfgSideChannel` element is safe to feed to
|
|
68
|
+
* {@link emitFileCfgs}. Deliberately NOT full FunctionCfg validation — it
|
|
69
|
+
* checks exactly the fields whose corruption is SILENT given emit's
|
|
70
|
+
* mechanics: {@link basicBlockId} string-templates every id-anchor value
|
|
71
|
+
* (filePath, function start line/column, block index, edge endpoints) and
|
|
72
|
+
* the graph's addNode/addRelationship are no-throw Map inserts. Unchecked,
|
|
73
|
+
* a missing anchor field cross-wires same-`undefined`-id blocks across
|
|
74
|
+
* functions (addNode is first-writer-wins), and an edge endpoint that
|
|
75
|
+
* matches no block index becomes a dangling `BasicBlock:…:<n>` edge that
|
|
76
|
+
* detonates much later at DB bulk-load instead of throwing here — so
|
|
77
|
+
* endpoints are checked for MEMBERSHIP in the block-index set, not just
|
|
78
|
+
* integer-ness. Lives in this module so the guard evolves with the id
|
|
79
|
+
* templating it defends (#2099 F4; M2 fields that join the id path must
|
|
80
|
+
* join this check).
|
|
81
|
+
*/
|
|
82
|
+
export declare const isEmitSafeCfg: (cfg: FunctionCfg | undefined | null) => cfg is FunctionCfg;
|
|
83
|
+
/**
|
|
84
|
+
* Whether a structurally-valid CFG's M2 statement facts are safe to feed to
|
|
85
|
+
* the reaching-defs solver + REACHING_DEF id templating (#2082 U1/U4): the
|
|
86
|
+
* binding table's name/declLine/declColumn template into edge ids, and
|
|
87
|
+
* statement def/use indices must stay IN RANGE of the table (an escaping
|
|
88
|
+
* index would fabricate `undefined`-keyed ids). Deliberately SEPARATE from
|
|
89
|
+
* {@link isEmitSafeCfg}: malformed facts must cost only the function's
|
|
90
|
+
* REACHING_DEF projection — degrading to M1 behavior (CFG emitted, no facts)
|
|
91
|
+
* — never the BasicBlock/CFG layer itself.
|
|
92
|
+
*/
|
|
93
|
+
export declare const hasEmitSafeFacts: (cfg: FunctionCfg) => boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Emit BasicBlock nodes + CFG edges for every function CFG in `cfgs`.
|
|
96
|
+
*
|
|
97
|
+
* `maxEdgesPerFunction` caps edges per function. On overflow we stop emitting
|
|
98
|
+
* that function's remaining edges and call `onWarn` naming the dropped count —
|
|
99
|
+
* no silent truncation (KTD6/R6). Block nodes are always fully emitted (their
|
|
100
|
+
* count is bounded by the function's statement count); only edges are capped.
|
|
101
|
+
*/
|
|
102
|
+
export declare function emitFileCfgs(graph: KnowledgeGraph, cfgs: readonly FunctionCfg[], maxEdgesPerFunction?: number, onWarn?: (message: string) => void): CfgEmitResult;
|
|
103
|
+
export interface ReachingDefEmitResult {
|
|
104
|
+
/** Deduped (defBlock, useBlock, binding) edges persisted. */
|
|
105
|
+
edges: number;
|
|
106
|
+
/** Deduped edges dropped by the per-function edge cap. */
|
|
107
|
+
droppedEdges: number;
|
|
108
|
+
cappedFunctions: number;
|
|
109
|
+
/** Functions whose FACT materialization hit the solver's maxFacts limit. */
|
|
110
|
+
truncatedFunctions: number;
|
|
111
|
+
/** Functions whose facts failed {@link hasEmitSafeFacts} (CFG kept, facts skipped). */
|
|
112
|
+
malformedFactFunctions: number;
|
|
113
|
+
/** Total statement-level facts the solver produced (pre-dedup telemetry). */
|
|
114
|
+
facts: number;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Stable identity for a binding inside edge ids (#2082 M2 KTD3/KTD9):
|
|
118
|
+
* `name:declLine:declCol` for declared bindings, `name@module` for synthetic
|
|
119
|
+
* ones. Distinct same-name bindings never share a key; identifier characters
|
|
120
|
+
* cannot contain the id separators. Exported for the M3 taint emit path —
|
|
121
|
+
* TAINTED/SANITIZES ids key bindings with the same discipline.
|
|
122
|
+
*/
|
|
123
|
+
export declare const bindingKey: (b: BindingEntry) => string;
|
|
124
|
+
/**
|
|
125
|
+
* Compute reaching definitions per function and persist the bounded
|
|
126
|
+
* REACHING_DEF projection (#2082 M2 U4).
|
|
127
|
+
*
|
|
128
|
+
* Facts are DEDUPED to (defBlock, useBlock, binding) before budgeting — the
|
|
129
|
+
* persisted columns (`from,to,type,confidence,reason,step`; relationship ids
|
|
130
|
+
* are in-memory-only, the CodeRelation table has no id column) cannot
|
|
131
|
+
* distinguish finer rows, so statement-indexed ids would only manufacture
|
|
132
|
+
* byte-identical duplicate rows that burn budget. Statement granularity lives
|
|
133
|
+
* in the in-memory {@link computeReachingDefs} result, which the M3 taint
|
|
134
|
+
* engine recomputes on demand — the budget here governs only this projection
|
|
135
|
+
* and can never drop a taint fact.
|
|
136
|
+
*
|
|
137
|
+
* R7 (no silent truncation) covers BOTH layers: the per-function edge cap AND
|
|
138
|
+
* the solver's fact-materialization limit (which can fire without the edge
|
|
139
|
+
* cap ever being reached, since dedup is many-to-one) each produce one
|
|
140
|
+
* unconditional `onWarn`. The edge-cap warn names the top bindings by fact
|
|
141
|
+
* count — overflow is almost always one variable, which is exactly the datum
|
|
142
|
+
* M3 tuning wants.
|
|
143
|
+
*/
|
|
144
|
+
export declare function emitFileReachingDefs(graph: KnowledgeGraph, cfgs: readonly FunctionCfg[], maxEdgesPerFunction?: number, onWarn?: (message: string) => void): ReachingDefEmitResult;
|