gitnexus 1.6.8-rc.31 → 1.6.8-rc.32
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/dist/_shared/graph/types.d.ts +15 -1
- package/dist/_shared/graph/types.d.ts.map +1 -1
- package/dist/_shared/lbug/schema-constants.d.ts +1 -1
- package/dist/_shared/lbug/schema-constants.d.ts.map +1 -1
- package/dist/_shared/lbug/schema-constants.js +6 -0
- package/dist/_shared/lbug/schema-constants.js.map +1 -1
- package/dist/cli/ai-context.d.ts +31 -1
- package/dist/cli/ai-context.js +14 -15
- package/dist/cli/analyze.js +1 -0
- package/dist/core/ingestion/cfg/control-dependence.d.ts +47 -0
- package/dist/core/ingestion/cfg/control-dependence.js +120 -0
- package/dist/core/ingestion/cfg/emit.d.ts +59 -0
- package/dist/core/ingestion/cfg/emit.js +127 -0
- package/dist/core/ingestion/cfg/post-dominators.d.ts +79 -0
- package/dist/core/ingestion/cfg/post-dominators.js +176 -0
- package/dist/core/ingestion/pipeline.d.ts +9 -0
- package/dist/core/ingestion/scope-resolution/pipeline/phase.js +1 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +3 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.js +16 -1
- package/dist/core/run-analyze.d.ts +5 -1
- package/dist/core/run-analyze.js +8 -1
- package/dist/mcp/local/local-backend.d.ts +42 -0
- package/dist/mcp/local/local-backend.js +264 -65
- package/dist/mcp/resources.js +11 -0
- package/dist/mcp/tools.d.ts +2 -0
- package/dist/mcp/tools.js +56 -1
- package/dist/storage/repo-manager.d.ts +8 -0
- package/package.json +1 -1
- package/skills/gitnexus-guide.md +11 -0
- package/skills/gitnexus-pdg-query.md +89 -0
- package/web/assets/{agent-C01BpbCi.js → agent-lSrfPH05.js} +1 -1
- package/web/assets/{architectureDiagram-UL44E2DR-BETib0Ig.js → architectureDiagram-UL44E2DR-CgHRMZJ8.js} +1 -1
- package/web/assets/{chunk-LCXTWHL2-ECvRIdTA.js → chunk-LCXTWHL2-03IqIAdZ.js} +1 -1
- package/web/assets/{chunk-RG4AUYOV-htm8QilK.js → chunk-RG4AUYOV-DLFjj1rt.js} +1 -1
- package/web/assets/{classDiagram-KGZ6W3CR-vJcJG7SW.js → classDiagram-KGZ6W3CR-BTr7xsR8.js} +1 -1
- package/web/assets/{classDiagram-v2-72OJOZXJ-BCJH9y5K.js → classDiagram-v2-72OJOZXJ-CakV709X.js} +1 -1
- package/web/assets/{diagram-3NCE3AQN-DgbGdjct.js → diagram-3NCE3AQN-BW6iSZhw.js} +1 -1
- package/web/assets/{diagram-GF46GFSD-DkQKRTzf.js → diagram-GF46GFSD-5IsY7kfK.js} +1 -1
- package/web/assets/{diagram-QXG6HAR7-fO9iaqTW.js → diagram-QXG6HAR7-Cia81gsm.js} +1 -1
- package/web/assets/{diagram-WEQXMOUZ-BFGWbiEm.js → diagram-WEQXMOUZ-CLfT1sdo.js} +1 -1
- package/web/assets/{erDiagram-L5TCEMPS-IErmkD3i.js → erDiagram-L5TCEMPS-DqEAcwC_.js} +1 -1
- package/web/assets/{flowDiagram-H6V6AXG4-BOtRGXXs.js → flowDiagram-H6V6AXG4-BUEpeW9m.js} +1 -1
- package/web/assets/{index-DKmSSk0F.js → index-64YMDMOE.js} +6 -6
- package/web/assets/{infoDiagram-3YFTVSEB-DKRlh1as.js → infoDiagram-3YFTVSEB-hHbd_QCD.js} +1 -1
- package/web/assets/{ishikawaDiagram-BNXS4ZKH-B07zhx7A.js → ishikawaDiagram-BNXS4ZKH-XxO6eDu4.js} +1 -1
- package/web/assets/{kanban-definition-75IXJCU3-_saj34_J.js → kanban-definition-75IXJCU3-DeSVq5eO.js} +1 -1
- package/web/assets/{mindmap-definition-2TDM6QVE-iyCoM6pR.js → mindmap-definition-2TDM6QVE-CgEl9e61.js} +1 -1
- package/web/assets/{pieDiagram-CU6KROY3-DOeaMPvU.js → pieDiagram-CU6KROY3-E3RA4hXq.js} +1 -1
- package/web/assets/{requirementDiagram-JXO7QTGE-DMhAsO2o.js → requirementDiagram-JXO7QTGE-B70bTqxy.js} +1 -1
- package/web/assets/{sequenceDiagram-VS2MUI6T-CQZ5FzAV.js → sequenceDiagram-VS2MUI6T-CLt7nidU.js} +1 -1
- package/web/assets/{stateDiagram-7D4R322I-kYVKDLkw.js → stateDiagram-7D4R322I-DfdeOa9A.js} +1 -1
- package/web/assets/{stateDiagram-v2-36443NZ5-DvvdUOce.js → stateDiagram-v2-36443NZ5-C-c52-8M.js} +1 -1
- package/web/assets/{timeline-definition-O6YCAMPW-CxBfIWWp.js → timeline-definition-O6YCAMPW-8DBKNdJr.js} +1 -1
- package/web/assets/{vennDiagram-MWXL3ELB-D0m2r7IU.js → vennDiagram-MWXL3ELB-DhqnfFx6.js} +1 -1
- package/web/assets/{wardleyDiagram-CUQ6CDDI-yBZJHfwp.js → wardleyDiagram-CUQ6CDDI-DcliVPWw.js} +1 -1
- package/web/assets/{xychartDiagram-N2JHSOCM-CQJMSAIO.js → xychartDiagram-N2JHSOCM-CYmW296g.js} +1 -1
- package/web/index.html +1 -1
|
@@ -79,7 +79,21 @@ export type RelationshipType = 'CONTAINS' | 'CALLS' | 'INHERITS' | 'METHOD_OVERR
|
|
|
79
79
|
| 'SANITIZES'
|
|
80
80
|
/** Materialized source→sink taint path. Working name — final name/representation
|
|
81
81
|
* is confirmed when M3/M4 emits it; no persisted edge exists before then. */
|
|
82
|
-
| 'TAINT_PATH'
|
|
82
|
+
| 'TAINT_PATH'
|
|
83
|
+
/** Control-dependence edge (PDG, issue #2085 M5): block `dependent` (target)
|
|
84
|
+
* executes only because the branch at block `controller` (source) took a
|
|
85
|
+
* given side. The branch sense (`'T'` | `'F'`) rides the relation's existing
|
|
86
|
+
* `reason` column — mirroring how `CFG` stores its edge kind there — since
|
|
87
|
+
* the single `CodeRelation` table has no dedicated label column. */
|
|
88
|
+
| 'CDG'
|
|
89
|
+
/** Debug-only post-dominator-tree edge (#2085 M5): a block → its immediate
|
|
90
|
+
* post-dominator, emitted behind the `GITNEXUS_PDG_EMIT_POST_DOMINATE` env
|
|
91
|
+
* flag for inspection. Never emitted in a normal `--pdg` run. Note: as a
|
|
92
|
+
* member of this exported union it is a forward-compatibility commitment —
|
|
93
|
+
* removing it later is a breaking schema change — and it is deliberately
|
|
94
|
+
* excluded from `VALID_RELATION_TYPES` so it never enters impact-style
|
|
95
|
+
* symbol-space traversal (same posture as the taint substrate edges). */
|
|
96
|
+
| 'POST_DOMINATE';
|
|
83
97
|
export interface GraphNode {
|
|
84
98
|
id: string;
|
|
85
99
|
label: NodeLabel;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/graph/types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/graph/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
export declare const NODE_TABLES: readonly ["File", "Folder", "Function", "Class", "Interface", "Method", "CodeElement", "Community", "Process", "Section", "Struct", "Enum", "Macro", "Typedef", "Union", "Namespace", "Trait", "Impl", "TypeAlias", "Const", "Static", "Variable", "Property", "Record", "Delegate", "Annotation", "Constructor", "Template", "Module", "Route", "Tool", "BasicBlock"];
|
|
11
11
|
export type NodeTableName = (typeof NODE_TABLES)[number];
|
|
12
12
|
export declare const REL_TABLE_NAME = "CodeRelation";
|
|
13
|
-
export declare const REL_TYPES: readonly ["CONTAINS", "DEFINES", "IMPORTS", "CALLS", "EXTENDS", "IMPLEMENTS", "HAS_METHOD", "HAS_PROPERTY", "ACCESSES", "METHOD_OVERRIDES", "OVERRIDES", "METHOD_IMPLEMENTS", "MEMBER_OF", "STEP_IN_PROCESS", "HANDLES_ROUTE", "FETCHES", "HANDLES_TOOL", "ENTRY_POINT_OF", "WRAPS", "QUERIES", "CFG", "REACHING_DEF", "TAINTED", "SANITIZES", "TAINT_PATH"];
|
|
13
|
+
export declare const REL_TYPES: readonly ["CONTAINS", "DEFINES", "IMPORTS", "CALLS", "EXTENDS", "IMPLEMENTS", "HAS_METHOD", "HAS_PROPERTY", "ACCESSES", "METHOD_OVERRIDES", "OVERRIDES", "METHOD_IMPLEMENTS", "MEMBER_OF", "STEP_IN_PROCESS", "HANDLES_ROUTE", "FETCHES", "HANDLES_TOOL", "ENTRY_POINT_OF", "WRAPS", "QUERIES", "CFG", "REACHING_DEF", "TAINTED", "SANITIZES", "TAINT_PATH", "CDG", "POST_DOMINATE"];
|
|
14
14
|
export type RelType = (typeof REL_TYPES)[number];
|
|
15
15
|
export declare const EMBEDDING_TABLE_NAME = "CodeEmbedding";
|
|
16
16
|
//# sourceMappingURL=schema-constants.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-constants.d.ts","sourceRoot":"","sources":["../../src/lbug/schema-constants.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,eAAO,MAAM,WAAW,wWAkCd,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD,eAAO,MAAM,cAAc,iBAAiB,CAAC;AAE7C,eAAO,MAAM,SAAS,
|
|
1
|
+
{"version":3,"file":"schema-constants.d.ts","sourceRoot":"","sources":["../../src/lbug/schema-constants.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,eAAO,MAAM,WAAW,wWAkCd,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD,eAAO,MAAM,cAAc,iBAAiB,CAAC;AAE7C,eAAO,MAAM,SAAS,sXAmCZ,CAAC;AAEX,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjD,eAAO,MAAM,oBAAoB,kBAAkB,CAAC"}
|
|
@@ -72,6 +72,12 @@ export const REL_TYPES = [
|
|
|
72
72
|
'TAINTED',
|
|
73
73
|
'SANITIZES',
|
|
74
74
|
'TAINT_PATH',
|
|
75
|
+
// Control dependence (PDG, issue #2085 M5) — CDG carries its 'T'|'F' branch
|
|
76
|
+
// label in the relation's `reason` column; POST_DOMINATE is debug-only
|
|
77
|
+
// (behind GITNEXUS_PDG_EMIT_POST_DOMINATE). Both are BasicBlock→BasicBlock,
|
|
78
|
+
// reusing the existing FROM BasicBlock TO BasicBlock pair in RELATION_SCHEMA.
|
|
79
|
+
'CDG',
|
|
80
|
+
'POST_DOMINATE',
|
|
75
81
|
];
|
|
76
82
|
export const EMBEDDING_TABLE_NAME = 'CodeEmbedding';
|
|
77
83
|
//# sourceMappingURL=schema-constants.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-constants.js","sourceRoot":"","sources":["../../src/lbug/schema-constants.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,MAAM;IACN,QAAQ;IACR,UAAU;IACV,OAAO;IACP,WAAW;IACX,QAAQ;IACR,aAAa;IACb,WAAW;IACX,SAAS;IACT,SAAS;IACT,QAAQ;IACR,MAAM;IACN,OAAO;IACP,SAAS;IACT,OAAO;IACP,WAAW;IACX,OAAO;IACP,MAAM;IACN,WAAW;IACX,OAAO;IACP,QAAQ;IACR,UAAU;IACV,UAAU;IACV,QAAQ;IACR,UAAU;IACV,YAAY;IACZ,aAAa;IACb,UAAU;IACV,QAAQ;IACR,OAAO;IACP,MAAM;IACN,2EAA2E;IAC3E,YAAY;CACJ,CAAC;AAIX,MAAM,CAAC,MAAM,cAAc,GAAG,cAAc,CAAC;AAE7C,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,UAAU;IACV,SAAS;IACT,SAAS;IACT,OAAO;IACP,SAAS;IACT,YAAY;IACZ,YAAY;IACZ,cAAc;IACd,UAAU;IACV,kBAAkB;IAClB,WAAW,EAAE,mEAAmE;IAChF,mBAAmB;IACnB,WAAW;IACX,iBAAiB;IACjB,eAAe;IACf,SAAS;IACT,cAAc;IACd,gBAAgB;IAChB,OAAO;IACP,SAAS;IACT,yEAAyE;IACzE,yEAAyE;IACzE,6EAA6E;IAC7E,KAAK;IACL,cAAc;IACd,SAAS;IACT,WAAW;IACX,YAAY;
|
|
1
|
+
{"version":3,"file":"schema-constants.js","sourceRoot":"","sources":["../../src/lbug/schema-constants.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,MAAM;IACN,QAAQ;IACR,UAAU;IACV,OAAO;IACP,WAAW;IACX,QAAQ;IACR,aAAa;IACb,WAAW;IACX,SAAS;IACT,SAAS;IACT,QAAQ;IACR,MAAM;IACN,OAAO;IACP,SAAS;IACT,OAAO;IACP,WAAW;IACX,OAAO;IACP,MAAM;IACN,WAAW;IACX,OAAO;IACP,QAAQ;IACR,UAAU;IACV,UAAU;IACV,QAAQ;IACR,UAAU;IACV,YAAY;IACZ,aAAa;IACb,UAAU;IACV,QAAQ;IACR,OAAO;IACP,MAAM;IACN,2EAA2E;IAC3E,YAAY;CACJ,CAAC;AAIX,MAAM,CAAC,MAAM,cAAc,GAAG,cAAc,CAAC;AAE7C,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,UAAU;IACV,SAAS;IACT,SAAS;IACT,OAAO;IACP,SAAS;IACT,YAAY;IACZ,YAAY;IACZ,cAAc;IACd,UAAU;IACV,kBAAkB;IAClB,WAAW,EAAE,mEAAmE;IAChF,mBAAmB;IACnB,WAAW;IACX,iBAAiB;IACjB,eAAe;IACf,SAAS;IACT,cAAc;IACd,gBAAgB;IAChB,OAAO;IACP,SAAS;IACT,yEAAyE;IACzE,yEAAyE;IACzE,6EAA6E;IAC7E,KAAK;IACL,cAAc;IACd,SAAS;IACT,WAAW;IACX,YAAY;IACZ,4EAA4E;IAC5E,uEAAuE;IACvE,4EAA4E;IAC5E,8EAA8E;IAC9E,KAAK;IACL,eAAe;CACP,CAAC;AAIX,MAAM,CAAC,MAAM,oBAAoB,GAAG,eAAe,CAAC"}
|
package/dist/cli/ai-context.d.ts
CHANGED
|
@@ -24,6 +24,12 @@ export interface AIContextOptions {
|
|
|
24
24
|
* plain caller that omits it gets "main", preserving prior behavior.
|
|
25
25
|
*/
|
|
26
26
|
defaultBranch?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Whether the index was built with `--pdg` (#2086 M6). Gates the `pdg_query`
|
|
29
|
+
* line in the generated block — without the PDG layer the tool only returns a
|
|
30
|
+
* "no PDG layer" note, so advertising it on a non-`--pdg` index is noise.
|
|
31
|
+
*/
|
|
32
|
+
hasPdg?: boolean;
|
|
27
33
|
}
|
|
28
34
|
/**
|
|
29
35
|
* Strip backticks from a branch name before it is embedded in a Markdown
|
|
@@ -32,7 +38,31 @@ export interface AIContextOptions {
|
|
|
32
38
|
* the generation sink so the embedding is provably safe regardless of caller.
|
|
33
39
|
*/
|
|
34
40
|
export declare function markdownSafeBranch(branch: string): string;
|
|
35
|
-
|
|
41
|
+
/** Options for {@link generateGitNexusContent} (collapsed from positional
|
|
42
|
+
* params, #2188 review — six `undefined`s to reach `hasPdg` was the smell). */
|
|
43
|
+
export interface GitNexusContentOptions {
|
|
44
|
+
generatedSkills?: GeneratedSkillInfo[];
|
|
45
|
+
groupNames?: string[];
|
|
46
|
+
noStats?: boolean;
|
|
47
|
+
skipSkills?: boolean;
|
|
48
|
+
/** Project-relative path to the runner `gitnexus analyze` drops next to the
|
|
49
|
+
* index (#1945). Referenced by docs so a single CLI-neutral command resolves
|
|
50
|
+
* the available runner (global `gitnexus` → `pnpm dlx` → `npx`) at call time. */
|
|
51
|
+
runnerPath?: string;
|
|
52
|
+
/** Default branch for the regression-compare example (#243). Configurable so
|
|
53
|
+
* projects on `develop`/`master`/etc. don't get `base_ref: "main"` rewritten
|
|
54
|
+
* back over their fix on every analyze. The value is embedded inside a
|
|
55
|
+
* Markdown inline-code span: validateBranchName rejects backticks upstream,
|
|
56
|
+
* and `markdownSafeBranch` strips any remaining backtick here as defense in
|
|
57
|
+
* depth, so JSON.stringify's quote/escape handling is sufficient and the
|
|
58
|
+
* branch cannot break out of the span (#1996 tri-review P1). */
|
|
59
|
+
defaultBranch?: string;
|
|
60
|
+
/** Whether the index was built with `--pdg` (#2086 M6). Gates the pdg_query
|
|
61
|
+
* line below — false (default) omits it, so a non-pdg index doesn't advertise
|
|
62
|
+
* a tool that only returns a "no PDG layer" note. */
|
|
63
|
+
hasPdg?: boolean;
|
|
64
|
+
}
|
|
65
|
+
export declare function generateGitNexusContent(projectName: string, stats: RepoStats, opts?: GitNexusContentOptions): string;
|
|
36
66
|
/**
|
|
37
67
|
* Generate AI context files after indexing
|
|
38
68
|
*/
|
package/dist/cli/ai-context.js
CHANGED
|
@@ -77,19 +77,8 @@ async function findGroupsContainingRegistryName(registryName) {
|
|
|
77
77
|
export function markdownSafeBranch(branch) {
|
|
78
78
|
return branch.replace(/`/g, '');
|
|
79
79
|
}
|
|
80
|
-
export function generateGitNexusContent(projectName, stats,
|
|
81
|
-
|
|
82
|
-
// index (#1945). Referenced by docs so a single CLI-neutral command resolves
|
|
83
|
-
// the available runner (global `gitnexus` → `pnpm dlx` → `npx`) at call time.
|
|
84
|
-
runnerPath = '.gitnexus/run.cjs',
|
|
85
|
-
// Default branch for the regression-compare example (#243). Configurable so
|
|
86
|
-
// projects on `develop`/`master`/etc. don't get `base_ref: "main"` rewritten
|
|
87
|
-
// back over their fix on every analyze. The value is embedded inside a
|
|
88
|
-
// Markdown inline-code span: validateBranchName rejects backticks upstream,
|
|
89
|
-
// and `markdownSafeBranch` strips any remaining backtick here as defense in
|
|
90
|
-
// depth, so JSON.stringify's quote/escape handling is sufficient and the
|
|
91
|
-
// branch cannot break out of the span (#1996 tri-review P1).
|
|
92
|
-
defaultBranch = 'main') {
|
|
80
|
+
export function generateGitNexusContent(projectName, stats, opts = {}) {
|
|
81
|
+
const { generatedSkills, groupNames, noStats, skipSkills, runnerPath = '.gitnexus/run.cjs', defaultBranch = 'main', hasPdg = false, } = opts;
|
|
93
82
|
const generatedRows = generatedSkills && generatedSkills.length > 0
|
|
94
83
|
? generatedSkills
|
|
95
84
|
.map((s) => `| Work in the ${s.label} area (${s.symbolCount} symbols) | \`.claude/skills/generated/${s.name}/SKILL.md\` |`)
|
|
@@ -136,7 +125,9 @@ This project is indexed by GitNexus as **${projectName}**${noStats ? '' : ` (${s
|
|
|
136
125
|
- **MUST warn the user** if impact analysis returns HIGH or CRITICAL risk before proceeding with edits.
|
|
137
126
|
- When exploring unfamiliar code, use \`query({search_query: "concept"})\` to find execution flows instead of grepping. It returns process-grouped results ranked by relevance.
|
|
138
127
|
- When you need full context on a specific symbol — callers, callees, which execution flows it participates in — use \`context({name: "symbolName"})\`.
|
|
139
|
-
- For security review, \`explain({target: "fileOrSymbol"})\` lists taint findings (source→sink flows; needs \`analyze --pdg\`)
|
|
128
|
+
- For security review, \`explain({target: "fileOrSymbol"})\` lists taint findings (source→sink flows; needs \`analyze --pdg\`).${hasPdg
|
|
129
|
+
? `\n- For control/data dependence, \`pdg_query({mode: "controls", target: "fileOrSymbol"})\` answers "under what condition does X run?" (CDG, incl. guard clauses) and \`pdg_query({mode: "flows", target, variable})\` traces "where does variable Y flow?" (REACHING_DEF). \`--pdg\` layer.`
|
|
130
|
+
: ''}
|
|
140
131
|
|
|
141
132
|
## Never Do
|
|
142
133
|
|
|
@@ -349,7 +340,15 @@ export async function generateAIContextFiles(repoPath, storagePath, projectName,
|
|
|
349
340
|
catch (err) {
|
|
350
341
|
logger.warn(`Could not write GitNexus runner to ${runnerPath}: ${String(err)}`);
|
|
351
342
|
}
|
|
352
|
-
const content = generateGitNexusContent(projectName, stats,
|
|
343
|
+
const content = generateGitNexusContent(projectName, stats, {
|
|
344
|
+
generatedSkills,
|
|
345
|
+
groupNames,
|
|
346
|
+
noStats: options?.noStats,
|
|
347
|
+
skipSkills: options?.skipSkills,
|
|
348
|
+
runnerPath,
|
|
349
|
+
defaultBranch: options?.defaultBranch ?? 'main',
|
|
350
|
+
hasPdg: options?.hasPdg ?? false,
|
|
351
|
+
});
|
|
353
352
|
const createdFiles = [];
|
|
354
353
|
if (!options?.skipAgentsMd) {
|
|
355
354
|
// Create AGENTS.md (standard for Cursor, Windsurf, OpenCode, Cline, etc.)
|
package/dist/cli/analyze.js
CHANGED
|
@@ -1073,6 +1073,7 @@ const analyzeCommandImpl = async (inputPath, cliOptions) => {
|
|
|
1073
1073
|
// Mirror runFullAnalysis `noStats` bridge (#1477) — same expression;
|
|
1074
1074
|
// exercised on the `--skills` path by analyze-no-stats-bridge.test.ts.
|
|
1075
1075
|
noStats: options.stats === false,
|
|
1076
|
+
hasPdg: options.pdg === true,
|
|
1076
1077
|
});
|
|
1077
1078
|
}
|
|
1078
1079
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Control dependence (#2085 M5 U3) — Ferrante, Ottenstein & Warren §3.1.1 over
|
|
3
|
+
* the post-dominator tree. A block `dependent` is control-dependent on a branch
|
|
4
|
+
* block `controller` when `controller` decides whether `dependent` executes:
|
|
5
|
+
* formally, there is a CFG edge `controller → B` such that `dependent`
|
|
6
|
+
* post-dominates `B` but does NOT strictly post-dominate `controller`.
|
|
7
|
+
*
|
|
8
|
+
* Construction (§3.1.1): for each CFG edge `(A, B)` where `B` does NOT
|
|
9
|
+
* post-dominate `A`, walk UP the post-dom tree from `B` to (but not including)
|
|
10
|
+
* `ipdom(A)`; every block on that path is control-dependent on `A`. The branch
|
|
11
|
+
* SENSE of the edge ('T' | 'F') becomes the edge label (KTD4 / KTD3 — it rides
|
|
12
|
+
* the persisted relation's `reason` column).
|
|
13
|
+
*
|
|
14
|
+
* PURE AND DETERMINISTIC (mirrors post-dominators.ts / reaching-defs.ts): no
|
|
15
|
+
* graph, no logger, importable outside the worker; output is deduped per
|
|
16
|
+
* (controller, dependent, label) and sorted, so snapshot tests and
|
|
17
|
+
* content-derived edge ids are stable. The loop header legitimately appears as
|
|
18
|
+
* control-dependent on ITSELF (`controller === dependent`) — the loop predicate
|
|
19
|
+
* gates its own re-execution; this is standard PDG behavior, not a bug.
|
|
20
|
+
*/
|
|
21
|
+
import { type PostDomTree } from './post-dominators.js';
|
|
22
|
+
import type { FunctionCfg } from './types.js';
|
|
23
|
+
export type CdgLabel = 'T' | 'F';
|
|
24
|
+
export interface ControlDepEdge {
|
|
25
|
+
/** The branch block whose outcome controls `dependentBlock`. */
|
|
26
|
+
readonly controllerBlock: number;
|
|
27
|
+
/** The block that executes only because `controllerBlock` took `label`. */
|
|
28
|
+
readonly dependentBlock: number;
|
|
29
|
+
/** Branch sense of the controlling CFG edge — see {@link branchSense}. */
|
|
30
|
+
readonly label: CdgLabel;
|
|
31
|
+
}
|
|
32
|
+
export interface ControlDepResult {
|
|
33
|
+
/** Deduped, sorted (controller, dependent, label) control-dependence edges. */
|
|
34
|
+
readonly edges: readonly ControlDepEdge[];
|
|
35
|
+
/**
|
|
36
|
+
* True when the `maxEdges` ceiling was reached; `edges` is then a
|
|
37
|
+
* deterministic prefix (CFG-edge iteration order, sorted), never a silent
|
|
38
|
+
* drop. Mirrors {@link computeReachingDefs}'s `truncated`.
|
|
39
|
+
*/
|
|
40
|
+
readonly truncated: boolean;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Compute control-dependence edges for one function's CFG. `postDom` may be
|
|
44
|
+
* supplied to reuse an already-built tree; otherwise it is computed. See the
|
|
45
|
+
* module doc for the purity/determinism contract.
|
|
46
|
+
*/
|
|
47
|
+
export declare function computeControlDependence(cfg: FunctionCfg, postDom?: PostDomTree, maxEdges?: number): ControlDepResult;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Control dependence (#2085 M5 U3) — Ferrante, Ottenstein & Warren §3.1.1 over
|
|
3
|
+
* the post-dominator tree. A block `dependent` is control-dependent on a branch
|
|
4
|
+
* block `controller` when `controller` decides whether `dependent` executes:
|
|
5
|
+
* formally, there is a CFG edge `controller → B` such that `dependent`
|
|
6
|
+
* post-dominates `B` but does NOT strictly post-dominate `controller`.
|
|
7
|
+
*
|
|
8
|
+
* Construction (§3.1.1): for each CFG edge `(A, B)` where `B` does NOT
|
|
9
|
+
* post-dominate `A`, walk UP the post-dom tree from `B` to (but not including)
|
|
10
|
+
* `ipdom(A)`; every block on that path is control-dependent on `A`. The branch
|
|
11
|
+
* SENSE of the edge ('T' | 'F') becomes the edge label (KTD4 / KTD3 — it rides
|
|
12
|
+
* the persisted relation's `reason` column).
|
|
13
|
+
*
|
|
14
|
+
* PURE AND DETERMINISTIC (mirrors post-dominators.ts / reaching-defs.ts): no
|
|
15
|
+
* graph, no logger, importable outside the worker; output is deduped per
|
|
16
|
+
* (controller, dependent, label) and sorted, so snapshot tests and
|
|
17
|
+
* content-derived edge ids are stable. The loop header legitimately appears as
|
|
18
|
+
* control-dependent on ITSELF (`controller === dependent`) — the loop predicate
|
|
19
|
+
* gates its own re-execution; this is standard PDG behavior, not a bug.
|
|
20
|
+
*/
|
|
21
|
+
import { computePostDominators, postDominates, NO_IPDOM, } from './post-dominators.js';
|
|
22
|
+
function buildArmSenses(cfg) {
|
|
23
|
+
const n = cfg.blocks.length;
|
|
24
|
+
const senses = Array.from({ length: n }, () => ({
|
|
25
|
+
hasTrueArm: false,
|
|
26
|
+
hasFalseArm: false,
|
|
27
|
+
}));
|
|
28
|
+
for (const e of cfg.edges) {
|
|
29
|
+
if (e.from < 0 || e.from >= n)
|
|
30
|
+
continue;
|
|
31
|
+
if (e.kind === 'cond-true' || e.kind === 'switch-case')
|
|
32
|
+
senses[e.from].hasTrueArm = true;
|
|
33
|
+
else if (e.kind === 'cond-false')
|
|
34
|
+
senses[e.from].hasFalseArm = true;
|
|
35
|
+
}
|
|
36
|
+
return senses;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The CDG label ('T'|'F') for a control-dependence edge, given the controlling
|
|
40
|
+
* block's arm senses. An explicitly-sensed edge is taken at face value; an
|
|
41
|
+
* ambiguous fall-through edge (`seq`/`loop-back`/`fallthrough`/jump) is the
|
|
42
|
+
* COMPLEMENT of the controller's explicit sibling arm. Per-case `switch` value
|
|
43
|
+
* labels are deferred to #2086 — every `switch-case` is 'T' in M5.
|
|
44
|
+
*/
|
|
45
|
+
function labelFor(kind, controller) {
|
|
46
|
+
if (kind === 'cond-true' || kind === 'switch-case')
|
|
47
|
+
return 'T';
|
|
48
|
+
if (kind === 'cond-false')
|
|
49
|
+
return 'F';
|
|
50
|
+
// Ambiguous structural kind: take the complement of the controller's explicit
|
|
51
|
+
// arm. A block with a true arm reaches here via its false fall-through; a
|
|
52
|
+
// do/while bottom-test (false arm = cond-false) reaches here via its true
|
|
53
|
+
// loop-back. With neither explicit arm (a degenerate / exit-unreachable
|
|
54
|
+
// region — see #2188 F2, where the dependence itself is unsound) the sense is
|
|
55
|
+
// indeterminate; default 'F' since fall-through is the common case.
|
|
56
|
+
if (controller.hasTrueArm)
|
|
57
|
+
return 'F';
|
|
58
|
+
if (controller.hasFalseArm)
|
|
59
|
+
return 'T';
|
|
60
|
+
return 'F';
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Compute control-dependence edges for one function's CFG. `postDom` may be
|
|
64
|
+
* supplied to reuse an already-built tree; otherwise it is computed. See the
|
|
65
|
+
* module doc for the purity/determinism contract.
|
|
66
|
+
*/
|
|
67
|
+
export function computeControlDependence(cfg, postDom,
|
|
68
|
+
// Heap-safety ceiling on materialized edges, mirroring computeReachingDefs'
|
|
69
|
+
// `maxFacts` (#2188 review): the pre-dedup walk is O(edges × post-dom depth),
|
|
70
|
+
// so bound it before it can spike. `0` ⇒ unbounded. On overflow `edges` is a
|
|
71
|
+
// deterministic prefix and `truncated` is set — never a silent drop.
|
|
72
|
+
maxEdges = 0) {
|
|
73
|
+
const tree = postDom ?? computePostDominators(cfg);
|
|
74
|
+
const { ipdom } = tree;
|
|
75
|
+
const n = cfg.blocks.length;
|
|
76
|
+
const armSenses = buildArmSenses(cfg);
|
|
77
|
+
const cap = maxEdges > 0 ? maxEdges : Infinity;
|
|
78
|
+
const out = [];
|
|
79
|
+
const seen = new Set();
|
|
80
|
+
let truncated = false;
|
|
81
|
+
scan: for (const e of cfg.edges) {
|
|
82
|
+
const a = e.from;
|
|
83
|
+
const b = e.to;
|
|
84
|
+
if (a < 0 || a >= n || b < 0 || b >= n)
|
|
85
|
+
continue;
|
|
86
|
+
// No control dependence when B post-dominates A — every path leaving A
|
|
87
|
+
// through this edge still reaches B, so A does not decide B's execution.
|
|
88
|
+
// This guard is exactly AC2: a dependence exists IFF post-dominance fails.
|
|
89
|
+
if (postDominates(tree, b, a))
|
|
90
|
+
continue;
|
|
91
|
+
// Sense is read from the CONTROLLER's arms, not this edge's kind alone —
|
|
92
|
+
// seq/loop-back fall-through false arms would otherwise mislabel as 'T'
|
|
93
|
+
// (#2188 F1).
|
|
94
|
+
const label = labelFor(e.kind, armSenses[a]);
|
|
95
|
+
const stop = ipdom[a]; // walk up to ipdom(A), EXCLUSIVE (NO_IPDOM ⇒ to root)
|
|
96
|
+
let cur = b;
|
|
97
|
+
let steps = 0;
|
|
98
|
+
// `steps <= n` is defensive — the ipdom chain is a finite tree.
|
|
99
|
+
while (cur !== NO_IPDOM && cur !== stop && steps <= n) {
|
|
100
|
+
const key = `${a}:${cur}:${label}`;
|
|
101
|
+
if (!seen.has(key)) {
|
|
102
|
+
// Check BEFORE pushing so `truncated` means a genuine overflow (a new
|
|
103
|
+
// unique edge had to be dropped), not merely "reached the ceiling" —
|
|
104
|
+
// exactly `cap` edges is a full, non-truncated result.
|
|
105
|
+
if (out.length >= cap) {
|
|
106
|
+
truncated = true;
|
|
107
|
+
break scan;
|
|
108
|
+
}
|
|
109
|
+
seen.add(key);
|
|
110
|
+
out.push({ controllerBlock: a, dependentBlock: cur, label });
|
|
111
|
+
}
|
|
112
|
+
cur = ipdom[cur];
|
|
113
|
+
steps += 1;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
out.sort((x, y) => x.controllerBlock - y.controllerBlock ||
|
|
117
|
+
x.dependentBlock - y.dependentBlock ||
|
|
118
|
+
(x.label < y.label ? -1 : x.label > y.label ? 1 : 0));
|
|
119
|
+
return { edges: out, truncated };
|
|
120
|
+
}
|
|
@@ -36,6 +36,34 @@ export declare const DEFAULT_MAX_CFG_EDGES_PER_FUNCTION = 5000;
|
|
|
36
36
|
* facts. `0` ⇒ unlimited; `undefined` ⇒ this default.
|
|
37
37
|
*/
|
|
38
38
|
export declare const DEFAULT_PDG_MAX_REACHING_DEF_EDGES_PER_FUNCTION = 4000;
|
|
39
|
+
/**
|
|
40
|
+
* Default per-function CDG edge cap (#2085 M5). CDG edge count is bounded by
|
|
41
|
+
* (blocks × control-nesting-depth) — comparable to the CFG edge count — so it
|
|
42
|
+
* reuses the CFG default of 5000. Counts DEDUPED (controller, dependent, label)
|
|
43
|
+
* edges (the pure {@link computeControlDependence} already dedups). `0` ⇒
|
|
44
|
+
* unlimited; `undefined` ⇒ this default. Folded into the `RepoMeta.pdg` stamp
|
|
45
|
+
* (U5) so introducing CDG forces a full writeback for pre-CDG `--pdg` indexes.
|
|
46
|
+
*/
|
|
47
|
+
export declare const DEFAULT_PDG_MAX_CDG_EDGES_PER_FUNCTION = 5000;
|
|
48
|
+
/**
|
|
49
|
+
* Heap-safety ceiling on {@link computeControlDependence}'s pre-dedup
|
|
50
|
+
* materialization (#2188 review). The walk is O(edges × post-dom depth), and its
|
|
51
|
+
* `out` IS the deduped-edge quantity the per-function cap trims — so, UNLIKE
|
|
52
|
+
* REACHING_DEF's facts ceiling, this is deliberately NOT derived from the
|
|
53
|
+
* runtime edge cap (doing so would pre-truncate the very set the cap reports on,
|
|
54
|
+
* losing the exact dropped count). A fixed, generous multiple of the default
|
|
55
|
+
* edge cap: far above any real function — a catastrophe backstop only. When hit,
|
|
56
|
+
* the per-function cap reporting plus the `truncated` flag keep it observable
|
|
57
|
+
* (never a silent drop).
|
|
58
|
+
*/
|
|
59
|
+
export declare const DEFAULT_PDG_MAX_CDG_MATERIALIZATION_PER_FUNCTION: number;
|
|
60
|
+
/**
|
|
61
|
+
* Env flag that additionally emits diagnostic `POST_DOMINATE` edges
|
|
62
|
+
* (block → its immediate post-dominator) alongside CDG (#2085 M5 KTD8). Off in
|
|
63
|
+
* every normal `--pdg` run — these are for inspecting the post-dom tree, not a
|
|
64
|
+
* queryable product surface. Accepts `1`/`true` (case-insensitive).
|
|
65
|
+
*/
|
|
66
|
+
export declare const POST_DOMINATE_DEBUG_ENV = "GITNEXUS_PDG_EMIT_POST_DOMINATE";
|
|
39
67
|
/**
|
|
40
68
|
* Fact-materialization headroom over the edge cap (#2082 M2 U3/F3): facts are
|
|
41
69
|
* O(defs×uses) BY SPEC in merge-heavy code, and the edge cap alone bounds the
|
|
@@ -142,3 +170,34 @@ export declare const bindingKey: (b: BindingEntry) => string;
|
|
|
142
170
|
* M3 tuning wants.
|
|
143
171
|
*/
|
|
144
172
|
export declare function emitFileReachingDefs(graph: KnowledgeGraph, cfgs: readonly FunctionCfg[], maxEdgesPerFunction?: number, onWarn?: (message: string) => void): ReachingDefEmitResult;
|
|
173
|
+
export interface CdgEmitResult {
|
|
174
|
+
/** Deduped (controller, dependent, label) CDG edges persisted. */
|
|
175
|
+
edges: number;
|
|
176
|
+
/** CDG edges dropped by the per-function edge cap. */
|
|
177
|
+
droppedEdges: number;
|
|
178
|
+
/** Functions that hit the CDG edge cap. */
|
|
179
|
+
cappedFunctions: number;
|
|
180
|
+
/** Diagnostic POST_DOMINATE edges emitted (0 unless the debug env is set). */
|
|
181
|
+
postDominateEdges: number;
|
|
182
|
+
/**
|
|
183
|
+
* Functions skipped because EXIT was not reachable from every entry-reachable
|
|
184
|
+
* block — post-dominance would be unsound (#2188 review). CFG/REACHING_DEF for
|
|
185
|
+
* those functions are kept; only their CDG projection is omitted.
|
|
186
|
+
*/
|
|
187
|
+
skippedUnsoundFunctions: number;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Compute control dependence per function and persist the bounded CDG
|
|
191
|
+
* projection (#2085 M5 U4). Mirrors {@link emitFileReachingDefs}: the pure
|
|
192
|
+
* {@link computeControlDependence} already dedups to (controller, dependent,
|
|
193
|
+
* label), so the per-function cap applies to deduped edges and overflow logs
|
|
194
|
+
* one unconditional `onWarn` naming the dropped count — no silent truncation
|
|
195
|
+
* (R6/R7). The branch label ('T'|'F') rides the `reason` column (KTD3),
|
|
196
|
+
* mirroring how CFG stores its edge kind.
|
|
197
|
+
*
|
|
198
|
+
* When {@link POST_DOMINATE_DEBUG_ENV} is set, also emits diagnostic
|
|
199
|
+
* `POST_DOMINATE` edges (block → its immediate post-dominator). These are NOT
|
|
200
|
+
* capped or counted against the CDG budget — they exist only for inspecting the
|
|
201
|
+
* post-dom tree and never appear in a normal run.
|
|
202
|
+
*/
|
|
203
|
+
export declare function emitFileCdg(graph: KnowledgeGraph, cfgs: readonly FunctionCfg[], maxEdgesPerFunction?: number, onWarn?: (message: string) => void): CdgEmitResult;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { generateId } from '../../../lib/utils.js';
|
|
2
2
|
import { computeReachingDefs } from './reaching-defs.js';
|
|
3
|
+
import { computeControlDependence } from './control-dependence.js';
|
|
4
|
+
import { computePostDominators, isExitReachableFromAllBlocks, NO_IPDOM, } from './post-dominators.js';
|
|
3
5
|
/**
|
|
4
6
|
* Default per-function CFG edge cap. A pathological generated function could
|
|
5
7
|
* otherwise emit an unbounded edge set; the cap bounds graph growth and is
|
|
@@ -16,6 +18,34 @@ export const DEFAULT_MAX_CFG_EDGES_PER_FUNCTION = 5000;
|
|
|
16
18
|
* facts. `0` ⇒ unlimited; `undefined` ⇒ this default.
|
|
17
19
|
*/
|
|
18
20
|
export const DEFAULT_PDG_MAX_REACHING_DEF_EDGES_PER_FUNCTION = 4000;
|
|
21
|
+
/**
|
|
22
|
+
* Default per-function CDG edge cap (#2085 M5). CDG edge count is bounded by
|
|
23
|
+
* (blocks × control-nesting-depth) — comparable to the CFG edge count — so it
|
|
24
|
+
* reuses the CFG default of 5000. Counts DEDUPED (controller, dependent, label)
|
|
25
|
+
* edges (the pure {@link computeControlDependence} already dedups). `0` ⇒
|
|
26
|
+
* unlimited; `undefined` ⇒ this default. Folded into the `RepoMeta.pdg` stamp
|
|
27
|
+
* (U5) so introducing CDG forces a full writeback for pre-CDG `--pdg` indexes.
|
|
28
|
+
*/
|
|
29
|
+
export const DEFAULT_PDG_MAX_CDG_EDGES_PER_FUNCTION = 5000;
|
|
30
|
+
/**
|
|
31
|
+
* Heap-safety ceiling on {@link computeControlDependence}'s pre-dedup
|
|
32
|
+
* materialization (#2188 review). The walk is O(edges × post-dom depth), and its
|
|
33
|
+
* `out` IS the deduped-edge quantity the per-function cap trims — so, UNLIKE
|
|
34
|
+
* REACHING_DEF's facts ceiling, this is deliberately NOT derived from the
|
|
35
|
+
* runtime edge cap (doing so would pre-truncate the very set the cap reports on,
|
|
36
|
+
* losing the exact dropped count). A fixed, generous multiple of the default
|
|
37
|
+
* edge cap: far above any real function — a catastrophe backstop only. When hit,
|
|
38
|
+
* the per-function cap reporting plus the `truncated` flag keep it observable
|
|
39
|
+
* (never a silent drop).
|
|
40
|
+
*/
|
|
41
|
+
export const DEFAULT_PDG_MAX_CDG_MATERIALIZATION_PER_FUNCTION = 8 * DEFAULT_PDG_MAX_CDG_EDGES_PER_FUNCTION;
|
|
42
|
+
/**
|
|
43
|
+
* Env flag that additionally emits diagnostic `POST_DOMINATE` edges
|
|
44
|
+
* (block → its immediate post-dominator) alongside CDG (#2085 M5 KTD8). Off in
|
|
45
|
+
* every normal `--pdg` run — these are for inspecting the post-dom tree, not a
|
|
46
|
+
* queryable product surface. Accepts `1`/`true` (case-insensitive).
|
|
47
|
+
*/
|
|
48
|
+
export const POST_DOMINATE_DEBUG_ENV = 'GITNEXUS_PDG_EMIT_POST_DOMINATE';
|
|
19
49
|
/**
|
|
20
50
|
* Fact-materialization headroom over the edge cap (#2082 M2 U3/F3): facts are
|
|
21
51
|
* O(defs×uses) BY SPEC in merge-heavy code, and the edge cap alone bounds the
|
|
@@ -313,3 +343,100 @@ export function emitFileReachingDefs(graph, cfgs, maxEdgesPerFunction = DEFAULT_
|
|
|
313
343
|
}
|
|
314
344
|
return result;
|
|
315
345
|
}
|
|
346
|
+
/** Whether the POST_DOMINATE debug env flag is enabled (`1`/`true`). */
|
|
347
|
+
const postDominateDebugEnabled = () => {
|
|
348
|
+
const v = process.env[POST_DOMINATE_DEBUG_ENV];
|
|
349
|
+
return v === '1' || v?.toLowerCase() === 'true';
|
|
350
|
+
};
|
|
351
|
+
/**
|
|
352
|
+
* Compute control dependence per function and persist the bounded CDG
|
|
353
|
+
* projection (#2085 M5 U4). Mirrors {@link emitFileReachingDefs}: the pure
|
|
354
|
+
* {@link computeControlDependence} already dedups to (controller, dependent,
|
|
355
|
+
* label), so the per-function cap applies to deduped edges and overflow logs
|
|
356
|
+
* one unconditional `onWarn` naming the dropped count — no silent truncation
|
|
357
|
+
* (R6/R7). The branch label ('T'|'F') rides the `reason` column (KTD3),
|
|
358
|
+
* mirroring how CFG stores its edge kind.
|
|
359
|
+
*
|
|
360
|
+
* When {@link POST_DOMINATE_DEBUG_ENV} is set, also emits diagnostic
|
|
361
|
+
* `POST_DOMINATE` edges (block → its immediate post-dominator). These are NOT
|
|
362
|
+
* capped or counted against the CDG budget — they exist only for inspecting the
|
|
363
|
+
* post-dom tree and never appear in a normal run.
|
|
364
|
+
*/
|
|
365
|
+
export function emitFileCdg(graph, cfgs, maxEdgesPerFunction = DEFAULT_PDG_MAX_CDG_EDGES_PER_FUNCTION, onWarn) {
|
|
366
|
+
const result = {
|
|
367
|
+
edges: 0,
|
|
368
|
+
droppedEdges: 0,
|
|
369
|
+
cappedFunctions: 0,
|
|
370
|
+
postDominateEdges: 0,
|
|
371
|
+
skippedUnsoundFunctions: 0,
|
|
372
|
+
};
|
|
373
|
+
const cap = maxEdgesPerFunction > 0 ? maxEdgesPerFunction : Infinity;
|
|
374
|
+
const emitPostDom = postDominateDebugEnabled();
|
|
375
|
+
for (const cfg of cfgs) {
|
|
376
|
+
const { filePath, functionStartLine, functionStartColumn } = cfg;
|
|
377
|
+
// Sound post-dominance requires EXIT reachable from every entry-reachable
|
|
378
|
+
// block (#2188 review). A CFG that violates it — a future visitor's
|
|
379
|
+
// multi-terminal / non-terminating shape — would yield a CDG that both
|
|
380
|
+
// drops real and invents spurious dependences, so skip CDG for it. CFG and
|
|
381
|
+
// REACHING_DEF (emitted elsewhere, independent of post-dominance) are kept.
|
|
382
|
+
if (!isExitReachableFromAllBlocks(cfg)) {
|
|
383
|
+
result.skippedUnsoundFunctions++;
|
|
384
|
+
onWarn?.(`[cdg] ${filePath}:${functionStartLine}: EXIT not reachable from all ` +
|
|
385
|
+
`blocks — CDG skipped for this function (CFG/REACHING_DEF unaffected)`);
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
// Compute the post-dom tree once and feed it to the control-dependence
|
|
389
|
+
// pass (avoids recomputing it) and to the optional POST_DOMINATE emit.
|
|
390
|
+
const tree = computePostDominators(cfg);
|
|
391
|
+
// Bound the pre-dedup materialization (heap parity with REACHING_DEF). The
|
|
392
|
+
// fixed ceiling is a catastrophe backstop; the per-function edge cap below
|
|
393
|
+
// remains the reporting authority. A ceiling hit is surfaced, not silent.
|
|
394
|
+
const { edges: cdgEdges, truncated } = computeControlDependence(cfg, tree, DEFAULT_PDG_MAX_CDG_MATERIALIZATION_PER_FUNCTION);
|
|
395
|
+
if (truncated) {
|
|
396
|
+
onWarn?.(`[cdg] ${filePath}:${functionStartLine}: control-dependence materialization ` +
|
|
397
|
+
`ceiling (${DEFAULT_PDG_MAX_CDG_MATERIALIZATION_PER_FUNCTION}) reached — ` +
|
|
398
|
+
`edge counts for this function are a floor`);
|
|
399
|
+
}
|
|
400
|
+
let emittedForFn = 0;
|
|
401
|
+
for (const edge of cdgEdges) {
|
|
402
|
+
if (emittedForFn >= cap) {
|
|
403
|
+
const dropped = cdgEdges.length - emittedForFn;
|
|
404
|
+
result.droppedEdges += dropped;
|
|
405
|
+
result.cappedFunctions++;
|
|
406
|
+
onWarn?.(`[cdg] ${filePath}:${functionStartLine}: per-function CDG edge cap ` +
|
|
407
|
+
`(${maxEdgesPerFunction}) reached — dropped ${dropped} of ${cdgEdges.length} edges`);
|
|
408
|
+
break;
|
|
409
|
+
}
|
|
410
|
+
const sourceId = basicBlockId(filePath, functionStartLine, functionStartColumn, edge.controllerBlock);
|
|
411
|
+
const targetId = basicBlockId(filePath, functionStartLine, functionStartColumn, edge.dependentBlock);
|
|
412
|
+
graph.addRelationship({
|
|
413
|
+
id: generateId('CDG', `${filePath}:${functionStartLine}:${functionStartColumn}:` +
|
|
414
|
+
`${edge.controllerBlock}->${edge.dependentBlock}:${edge.label}`),
|
|
415
|
+
type: 'CDG',
|
|
416
|
+
sourceId,
|
|
417
|
+
targetId,
|
|
418
|
+
confidence: 1.0,
|
|
419
|
+
reason: edge.label, // 'T' | 'F' — queryable, mirrors CFG's kind-in-reason
|
|
420
|
+
});
|
|
421
|
+
result.edges++;
|
|
422
|
+
emittedForFn++;
|
|
423
|
+
}
|
|
424
|
+
if (emitPostDom) {
|
|
425
|
+
for (let b = 0; b < tree.ipdom.length; b++) {
|
|
426
|
+
const ip = tree.ipdom[b];
|
|
427
|
+
if (ip === NO_IPDOM)
|
|
428
|
+
continue;
|
|
429
|
+
graph.addRelationship({
|
|
430
|
+
id: generateId('POST_DOMINATE', `${filePath}:${functionStartLine}:${functionStartColumn}:${b}->${ip}`),
|
|
431
|
+
type: 'POST_DOMINATE',
|
|
432
|
+
sourceId: basicBlockId(filePath, functionStartLine, functionStartColumn, b),
|
|
433
|
+
targetId: basicBlockId(filePath, functionStartLine, functionStartColumn, ip),
|
|
434
|
+
confidence: 1.0,
|
|
435
|
+
reason: '',
|
|
436
|
+
});
|
|
437
|
+
result.postDominateEdges++;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return result;
|
|
442
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Post-dominators (#2085 M5 U2) — the immediate-post-dominator tree of one
|
|
3
|
+
* function's CFG, the substrate the Ferrante control-dependence pass walks.
|
|
4
|
+
*
|
|
5
|
+
* A block `p` post-dominates a block `b` iff every path from `b` to the
|
|
6
|
+
* function EXIT passes through `p`. Post-dominators are exactly the DOMINATORS
|
|
7
|
+
* of the REVERSE CFG rooted at EXIT, so this is the Cooper–Harvey–Kennedy
|
|
8
|
+
* "A Simple, Fast Dominance Algorithm" run over reversed edges. KTD2 of the M5
|
|
9
|
+
* plan picks CHK over Lengauer–Tarjan: per-function CFGs are small and
|
|
10
|
+
* line-capped, CHK is near-linear in practice, and its iterative shape matches
|
|
11
|
+
* the reaching-defs fixpoint already in this module.
|
|
12
|
+
*
|
|
13
|
+
* PURE AND DETERMINISTIC (load-bearing, mirrors reaching-defs.ts): no graph, no
|
|
14
|
+
* logger, importable outside the worker; predecessors/successors are sorted and
|
|
15
|
+
* iteration is reverse-postorder so the `ipdom` array is identical across runs
|
|
16
|
+
* (snapshot tests and content-derived edge ids depend on it).
|
|
17
|
+
*
|
|
18
|
+
* The single-EXIT invariant the M1 TS visitor preserves (visitors/typescript.ts)
|
|
19
|
+
* makes EXIT the unique reverse-CFG root. Blocks that cannot reach EXIT in the
|
|
20
|
+
* forward CFG (an exit-less infinite loop) are not reverse-reachable from it and
|
|
21
|
+
* have NO post-dominator: their `ipdom` is {@link NO_IPDOM}. The control-
|
|
22
|
+
* dependence pass treats "no post-dominator" as "does not post-dominate" (KTD5).
|
|
23
|
+
*
|
|
24
|
+
* NOTE (issue #2188 F2): this is NOT a fully sound over-approximation. Inside a
|
|
25
|
+
* region where NO block reaches EXIT, every `ipdom` is `NO_IPDOM`, so the
|
|
26
|
+
* Ferrante walk degenerates to one edge per control point — it can both DROP a
|
|
27
|
+
* real control dependence and INVENT a spurious one. This does not arise for the
|
|
28
|
+
* current TS visitor (every loop is given a structural `header → loopExit`
|
|
29
|
+
* `cond-false` edge, so EXIT stays reverse-reachable), but it is unsound for
|
|
30
|
+
* hand-built CFGs and any future language visitor lacking that exit edge.
|
|
31
|
+
* Nontermination-sensitive post-dominance (a virtual root over the
|
|
32
|
+
* non-terminating SCCs) would be the correct treatment — tracked for follow-up.
|
|
33
|
+
*/
|
|
34
|
+
import type { FunctionCfg } from './types.js';
|
|
35
|
+
/**
|
|
36
|
+
* Sentinel `ipdom` value: the block has no immediate post-dominator. True for
|
|
37
|
+
* the EXIT block itself (the reverse-CFG root) and for any block that cannot
|
|
38
|
+
* reach EXIT. Chosen as -1 so the {@link postDominates} climb terminates
|
|
39
|
+
* naturally instead of self-looping on the root.
|
|
40
|
+
*/
|
|
41
|
+
export declare const NO_IPDOM = -1;
|
|
42
|
+
export interface PostDomTree {
|
|
43
|
+
/**
|
|
44
|
+
* `ipdom[b]` = the index of `b`'s immediate post-dominator, or
|
|
45
|
+
* {@link NO_IPDOM} when `b` has none (EXIT, or a block that cannot reach EXIT).
|
|
46
|
+
*/
|
|
47
|
+
readonly ipdom: readonly number[];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Compute the immediate-post-dominator tree for one function's CFG. See the
|
|
51
|
+
* module doc for the purity/determinism contract and EXIT-root assumptions.
|
|
52
|
+
*/
|
|
53
|
+
export declare function computePostDominators(cfg: FunctionCfg): PostDomTree;
|
|
54
|
+
/**
|
|
55
|
+
* Does block `p` post-dominate block `b`? Climbs the post-dom tree from `b`
|
|
56
|
+
* toward EXIT and tests membership of `p`. Reflexive: a block post-dominates
|
|
57
|
+
* itself. A block with no post-dominator (EXIT, or one that cannot reach EXIT)
|
|
58
|
+
* is post-dominated only by itself. The step guard is purely defensive — the
|
|
59
|
+
* `ipdom` chain is a tree and always terminates at {@link NO_IPDOM}.
|
|
60
|
+
*/
|
|
61
|
+
export declare function postDominates(tree: PostDomTree, p: number, b: number): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Precondition for SOUND post-dominance (#2188 review): EXIT must be reachable
|
|
64
|
+
* (forward) from every block that is itself reachable from ENTRY. When it
|
|
65
|
+
* fails — an entry-reachable region that cannot reach EXIT, e.g. a
|
|
66
|
+
* non-terminating loop or a multi-terminal CFG a future language visitor might
|
|
67
|
+
* emit — the EXIT-rooted reverse walk degenerates (every such block gets
|
|
68
|
+
* {@link NO_IPDOM}), which both DROPS real control dependences and INVENTS
|
|
69
|
+
* spurious ones (the unsoundness documented in the module header). Consumers
|
|
70
|
+
* ({@link emitFileCdg}) check this and skip CDG for the function rather than
|
|
71
|
+
* persist an unsound projection — CFG and REACHING_DEF, which do not depend on
|
|
72
|
+
* post-dominance, are unaffected.
|
|
73
|
+
*
|
|
74
|
+
* The current TS visitor always satisfies this (every loop is given a
|
|
75
|
+
* structural `header → loopExit` edge, keeping EXIT reverse-reachable), so this
|
|
76
|
+
* is a guard for future visitors and hand-built CFGs, not a behavior change
|
|
77
|
+
* today. Pure and O(V+E).
|
|
78
|
+
*/
|
|
79
|
+
export declare function isExitReachableFromAllBlocks(cfg: FunctionCfg): boolean;
|