gitnexus 1.6.8-rc.8 → 1.6.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 +3 -3
- package/dist/_shared/graph/types.d.ts +31 -1
- package/dist/_shared/graph/types.d.ts.map +1 -1
- package/dist/_shared/language-detection.d.ts.map +1 -1
- package/dist/_shared/language-detection.js +11 -1
- package/dist/_shared/language-detection.js.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/_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 +6 -0
- package/dist/_shared/scope-resolution/symbol-definition.d.ts.map +1 -1
- package/dist/cli/ai-context.d.ts +31 -1
- package/dist/cli/ai-context.js +18 -16
- package/dist/cli/analyze-config.js +11 -0
- package/dist/cli/analyze.d.ts +14 -0
- package/dist/cli/analyze.js +98 -0
- package/dist/cli/embedding-dims.d.ts +33 -0
- package/dist/cli/embedding-dims.js +39 -0
- package/dist/cli/eval-server.d.ts +8 -0
- package/dist/cli/eval-server.js +242 -2
- package/dist/cli/help-i18n.js +19 -0
- package/dist/cli/i18n/en.d.ts +15 -1
- package/dist/cli/i18n/en.js +15 -1
- package/dist/cli/i18n/resources.d.ts +29 -1
- package/dist/cli/i18n/zh-CN.d.ts +14 -0
- package/dist/cli/i18n/zh-CN.js +15 -1
- package/dist/cli/index.js +89 -1
- package/dist/cli/mcp.d.ts +6 -1
- package/dist/cli/mcp.js +26 -1
- package/dist/cli/optional-grammars.d.ts +6 -8
- package/dist/cli/optional-grammars.js +8 -11
- package/dist/cli/setup.d.ts +3 -1
- package/dist/cli/setup.js +67 -17
- package/dist/cli/skill-gen.js +2 -1
- package/dist/cli/tool.d.ts +19 -1
- package/dist/cli/tool.js +96 -3
- package/dist/core/embeddings/hf-env.d.ts +0 -66
- package/dist/core/embeddings/http-client.d.ts +7 -0
- package/dist/core/embeddings/http-client.js +5 -3
- package/dist/core/embeddings/server-mapping.d.ts +1 -1
- package/dist/core/embeddings/server-mapping.js +7 -3
- package/dist/core/graph/import-cycles.d.ts +10 -0
- package/dist/core/graph/import-cycles.js +103 -0
- package/dist/core/group/cross-impact.d.ts +2 -1
- package/dist/core/group/cross-impact.js +5 -1
- package/dist/core/group/extractors/grpc-patterns/proto.js +10 -6
- package/dist/core/group/extractors/http-patterns/kotlin.js +7 -7
- package/dist/core/group/extractors/http-route-extractor.d.ts +24 -0
- package/dist/core/group/extractors/http-route-extractor.js +22 -4
- package/dist/core/group/extractors/include-extractor.js +22 -13
- package/dist/core/group/sync.d.ts +27 -1
- package/dist/core/group/sync.js +0 -0
- package/dist/core/incremental/subgraph-extract.js +18 -1
- package/dist/core/ingestion/cfg/cfg-builder.d.ts +114 -0
- package/dist/core/ingestion/cfg/cfg-builder.js +196 -0
- package/dist/core/ingestion/cfg/collect.d.ts +51 -0
- package/dist/core/ingestion/cfg/collect.js +77 -0
- package/dist/core/ingestion/cfg/control-dependence.d.ts +58 -0
- package/dist/core/ingestion/cfg/control-dependence.js +182 -0
- package/dist/core/ingestion/cfg/control-flow-context.d.ts +106 -0
- package/dist/core/ingestion/cfg/control-flow-context.js +124 -0
- package/dist/core/ingestion/cfg/emit.d.ts +293 -0
- package/dist/core/ingestion/cfg/emit.js +646 -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/cfg/reaching-def-reason-codec.d.ts +106 -0
- package/dist/core/ingestion/cfg/reaching-def-reason-codec.js +133 -0
- package/dist/core/ingestion/cfg/reaching-defs-graph.d.ts +18 -0
- package/dist/core/ingestion/cfg/reaching-defs-graph.js +312 -0
- package/dist/core/ingestion/cfg/reaching-defs.d.ts +162 -0
- package/dist/core/ingestion/cfg/reaching-defs.js +785 -0
- package/dist/core/ingestion/cfg/synthetic-escape.d.ts +124 -0
- package/dist/core/ingestion/cfg/synthetic-escape.js +300 -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 +274 -0
- package/dist/core/ingestion/cfg/types.js +13 -0
- package/dist/core/ingestion/cfg/visitors/c-cpp-harvest.d.ts +111 -0
- package/dist/core/ingestion/cfg/visitors/c-cpp-harvest.js +541 -0
- package/dist/core/ingestion/cfg/visitors/c-cpp.d.ts +74 -0
- package/dist/core/ingestion/cfg/visitors/c-cpp.js +584 -0
- package/dist/core/ingestion/cfg/visitors/call-site-harvest.d.ts +195 -0
- package/dist/core/ingestion/cfg/visitors/call-site-harvest.js +350 -0
- package/dist/core/ingestion/cfg/visitors/csharp-harvest.d.ts +124 -0
- package/dist/core/ingestion/cfg/visitors/csharp-harvest.js +589 -0
- package/dist/core/ingestion/cfg/visitors/csharp.d.ts +87 -0
- package/dist/core/ingestion/cfg/visitors/csharp.js +867 -0
- package/dist/core/ingestion/cfg/visitors/dart-harvest.d.ts +278 -0
- package/dist/core/ingestion/cfg/visitors/dart-harvest.js +870 -0
- package/dist/core/ingestion/cfg/visitors/dart.d.ts +119 -0
- package/dist/core/ingestion/cfg/visitors/dart.js +836 -0
- package/dist/core/ingestion/cfg/visitors/go-harvest.d.ts +160 -0
- package/dist/core/ingestion/cfg/visitors/go-harvest.js +621 -0
- package/dist/core/ingestion/cfg/visitors/go.d.ts +108 -0
- package/dist/core/ingestion/cfg/visitors/go.js +638 -0
- package/dist/core/ingestion/cfg/visitors/java-harvest.d.ts +108 -0
- package/dist/core/ingestion/cfg/visitors/java-harvest.js +513 -0
- package/dist/core/ingestion/cfg/visitors/java.d.ts +101 -0
- package/dist/core/ingestion/cfg/visitors/java.js +812 -0
- package/dist/core/ingestion/cfg/visitors/kotlin-harvest.d.ts +253 -0
- package/dist/core/ingestion/cfg/visitors/kotlin-harvest.js +719 -0
- package/dist/core/ingestion/cfg/visitors/kotlin.d.ts +121 -0
- package/dist/core/ingestion/cfg/visitors/kotlin.js +809 -0
- package/dist/core/ingestion/cfg/visitors/php-harvest.d.ts +172 -0
- package/dist/core/ingestion/cfg/visitors/php-harvest.js +626 -0
- package/dist/core/ingestion/cfg/visitors/php.d.ts +96 -0
- package/dist/core/ingestion/cfg/visitors/php.js +721 -0
- package/dist/core/ingestion/cfg/visitors/python-harvest.d.ts +212 -0
- package/dist/core/ingestion/cfg/visitors/python-harvest.js +739 -0
- package/dist/core/ingestion/cfg/visitors/python.d.ts +103 -0
- package/dist/core/ingestion/cfg/visitors/python.js +558 -0
- package/dist/core/ingestion/cfg/visitors/ruby-harvest.d.ts +223 -0
- package/dist/core/ingestion/cfg/visitors/ruby-harvest.js +587 -0
- package/dist/core/ingestion/cfg/visitors/ruby.d.ts +112 -0
- package/dist/core/ingestion/cfg/visitors/ruby.js +756 -0
- package/dist/core/ingestion/cfg/visitors/rust-harvest.d.ts +324 -0
- package/dist/core/ingestion/cfg/visitors/rust-harvest.js +873 -0
- package/dist/core/ingestion/cfg/visitors/rust.d.ts +95 -0
- package/dist/core/ingestion/cfg/visitors/rust.js +558 -0
- package/dist/core/ingestion/cfg/visitors/scope-tree-harvest.d.ts +87 -0
- package/dist/core/ingestion/cfg/visitors/scope-tree-harvest.js +116 -0
- package/dist/core/ingestion/cfg/visitors/swift-harvest.d.ts +255 -0
- package/dist/core/ingestion/cfg/visitors/swift-harvest.js +679 -0
- package/dist/core/ingestion/cfg/visitors/swift.d.ts +116 -0
- package/dist/core/ingestion/cfg/visitors/swift.js +787 -0
- package/dist/core/ingestion/cfg/visitors/typescript-harvest.d.ts +149 -0
- package/dist/core/ingestion/cfg/visitors/typescript-harvest.js +1056 -0
- package/dist/core/ingestion/cfg/visitors/typescript.d.ts +66 -0
- package/dist/core/ingestion/cfg/visitors/typescript.js +583 -0
- package/dist/core/ingestion/import-resolvers/utils.js +2 -0
- package/dist/core/ingestion/language-provider.d.ts +23 -2
- package/dist/core/ingestion/languages/c-cpp.js +17 -5
- package/dist/core/ingestion/languages/cobol.js +4 -0
- package/dist/core/ingestion/languages/cpp/arity-metadata.js +41 -2
- package/dist/core/ingestion/languages/cpp/captures.js +50 -1
- package/dist/core/ingestion/languages/cpp/conversion-rank.d.ts +2 -0
- package/dist/core/ingestion/languages/cpp/conversion-rank.js +89 -0
- package/dist/core/ingestion/languages/cpp/header-scan.d.ts +1 -1
- package/dist/core/ingestion/languages/cpp/header-scan.js +2 -2
- package/dist/core/ingestion/languages/cpp/inline-namespaces.js +7 -2
- package/dist/core/ingestion/languages/cpp/member-lookup.js +2 -1
- package/dist/core/ingestion/languages/cpp/query.js +23 -0
- package/dist/core/ingestion/languages/cpp/scope-resolver.js +2 -1
- package/dist/core/ingestion/languages/csharp.js +2 -0
- package/dist/core/ingestion/languages/dart.js +2 -0
- package/dist/core/ingestion/languages/go.js +2 -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 +8 -1
- package/dist/core/ingestion/languages/php.js +2 -0
- package/dist/core/ingestion/languages/python.js +2 -0
- package/dist/core/ingestion/languages/ruby.js +2 -0
- package/dist/core/ingestion/languages/rust.js +2 -0
- package/dist/core/ingestion/languages/swift.js +2 -0
- package/dist/core/ingestion/languages/typescript.js +5 -0
- package/dist/core/ingestion/languages/vue.js +6 -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/mro-processor.js +109 -0
- package/dist/core/ingestion/parsing-processor.js +44 -0
- package/dist/core/ingestion/pipeline-phases/call-summaries.d.ts +26 -0
- package/dist/core/ingestion/pipeline-phases/call-summaries.js +46 -0
- package/dist/core/ingestion/pipeline-phases/index.d.ts +2 -0
- package/dist/core/ingestion/pipeline-phases/index.js +2 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +0 -21
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +18 -1
- package/dist/core/ingestion/pipeline-phases/routes.d.ts +10 -0
- package/dist/core/ingestion/pipeline-phases/routes.js +30 -1
- package/dist/core/ingestion/pipeline-phases/taint-summaries.d.ts +30 -0
- package/dist/core/ingestion/pipeline-phases/taint-summaries.js +81 -0
- package/dist/core/ingestion/pipeline.d.ts +92 -10
- package/dist/core/ingestion/pipeline.js +11 -2
- package/dist/core/ingestion/scope-extractor.js +12 -2
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +9 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/callee-id-sink.d.ts +75 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/callee-id-sink.js +67 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/edges.d.ts +12 -2
- package/dist/core/ingestion/scope-resolution/graph-bridge/edges.js +14 -2
- package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.d.ts +6 -1
- package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.js +13 -1
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +9 -1
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +28 -2
- package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.d.ts +4 -0
- package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.js +9 -0
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.d.ts +6 -1
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +174 -40
- package/dist/core/ingestion/scope-resolution/pipeline/phase.d.ts +24 -0
- package/dist/core/ingestion/scope-resolution/pipeline/phase.js +276 -190
- package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.js +43 -3
- package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +75 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.js +349 -1
- package/dist/core/ingestion/scope-resolution/resolution-outcome.d.ts +1 -1
- package/dist/core/ingestion/taint/call-summary-codec.d.ts +75 -0
- package/dist/core/ingestion/taint/call-summary-codec.js +128 -0
- package/dist/core/ingestion/taint/call-summary-emit.d.ts +43 -0
- package/dist/core/ingestion/taint/call-summary-emit.js +70 -0
- package/dist/core/ingestion/taint/call-summary-harvest.d.ts +77 -0
- package/dist/core/ingestion/taint/call-summary-harvest.js +179 -0
- package/dist/core/ingestion/taint/call-summary-model.d.ts +47 -0
- package/dist/core/ingestion/taint/call-summary-model.js +25 -0
- package/dist/core/ingestion/taint/emit.d.ts +125 -0
- package/dist/core/ingestion/taint/emit.js +208 -0
- package/dist/core/ingestion/taint/interproc-emit.d.ts +50 -0
- package/dist/core/ingestion/taint/interproc-emit.js +88 -0
- package/dist/core/ingestion/taint/interproc-solver.d.ts +110 -0
- package/dist/core/ingestion/taint/interproc-solver.js +312 -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 +659 -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 +107 -23
- package/dist/core/ingestion/taint/source-sink-config.js +35 -12
- 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/summary-harvest-driver.d.ts +80 -0
- package/dist/core/ingestion/taint/summary-harvest-driver.js +179 -0
- package/dist/core/ingestion/taint/summary-harvest.d.ts +89 -0
- package/dist/core/ingestion/taint/summary-harvest.js +537 -0
- package/dist/core/ingestion/taint/summary-model.d.ts +200 -0
- package/dist/core/ingestion/taint/summary-model.js +86 -0
- 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/type-extractors/c-cpp.js +21 -0
- package/dist/core/ingestion/utils/env.d.ts +7 -0
- package/dist/core/ingestion/utils/env.js +12 -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 +23 -0
- package/dist/core/ingestion/workers/parse-worker.js +81 -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 -40
- package/dist/core/ingestion/workers/worker-pool.js +43 -16
- package/dist/core/lbug/csv-generator.d.ts +51 -3
- package/dist/core/lbug/csv-generator.js +369 -255
- package/dist/core/lbug/lbug-adapter.d.ts +58 -18
- package/dist/core/lbug/lbug-adapter.js +346 -84
- package/dist/core/lbug/lbug-config.d.ts +19 -0
- package/dist/core/lbug/lbug-config.js +29 -0
- package/dist/core/lbug/pdg-emit-sink.d.ts +127 -0
- package/dist/core/lbug/pdg-emit-sink.js +358 -0
- package/dist/core/lbug/pool-adapter.d.ts +35 -0
- package/dist/core/lbug/pool-adapter.js +90 -1
- package/dist/core/lbug/rel-pair-routing.d.ts +54 -0
- package/dist/core/lbug/rel-pair-routing.js +140 -0
- package/dist/core/lbug/schema.d.ts +2 -2
- package/dist/core/lbug/schema.js +3 -0
- package/dist/core/logger.d.ts +0 -5
- package/dist/core/run-analyze.d.ts +96 -0
- package/dist/core/run-analyze.js +207 -3
- package/dist/core/tree-sitter/parser-loader.js +5 -4
- package/dist/core/tree-sitter/safe-parse.d.ts +0 -5
- package/dist/core/tree-sitter/vendored-grammars.d.ts +39 -0
- package/dist/core/tree-sitter/vendored-grammars.js +57 -0
- package/dist/core/wiki/generator.js +3 -1
- package/dist/core/wiki/graph-queries.d.ts +6 -0
- package/dist/core/wiki/graph-queries.js +9 -1
- package/dist/mcp/http-transport.d.ts +134 -0
- package/dist/mcp/http-transport.js +503 -0
- package/dist/mcp/local/local-backend.d.ts +110 -24
- package/dist/mcp/local/local-backend.js +1225 -19
- package/dist/mcp/local/pdg-impact.d.ts +384 -0
- package/dist/mcp/local/pdg-impact.js +1972 -0
- package/dist/mcp/resources.js +13 -1
- package/dist/mcp/tools.d.ts +12 -0
- package/dist/mcp/tools.js +214 -7
- 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.d.ts +17 -0
- package/dist/server/api.js +132 -168
- package/dist/server/git-clone.d.ts +43 -2
- package/dist/server/git-clone.js +211 -16
- package/dist/server/mcp-http.d.ts +5 -6
- package/dist/server/mcp-http.js +9 -82
- package/dist/server/middleware.d.ts +40 -0
- package/dist/server/middleware.js +92 -0
- package/dist/server/private-ip.d.ts +1 -0
- package/dist/server/private-ip.js +15 -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 +37 -0
- package/dist/server/upload-paths.js +57 -0
- package/dist/server/upload-sweep.d.ts +20 -0
- package/dist/server/upload-sweep.js +57 -0
- package/dist/storage/git.js +5 -8
- package/dist/storage/parse-cache.d.ts +22 -1
- package/dist/storage/parse-cache.js +44 -10
- package/dist/storage/repo-manager.d.ts +121 -10
- package/dist/storage/repo-manager.js +43 -5
- package/dist/types/pipeline.d.ts +9 -0
- package/hooks/antigravity/gitnexus-antigravity-hook.cjs +110 -10
- package/hooks/claude/gitnexus-hook.cjs +110 -13
- package/hooks/claude/hook-db-lock-probe.cjs +513 -26
- package/package.json +8 -2
- package/scripts/assert-publish-grammar-coverage.cjs +31 -0
- package/scripts/build-tree-sitter-grammars.cjs +16 -10
- package/scripts/cross-platform-tests.ts +1 -0
- package/skills/gitnexus-debugging.md +16 -4
- package/skills/gitnexus-exploring.md +3 -3
- package/skills/gitnexus-guide.md +33 -0
- package/skills/gitnexus-pdg-query.md +89 -0
- package/skills/gitnexus-refactoring.md +1 -1
- package/skills/gitnexus-taint-analysis.md +178 -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-D3Xd7zZE.js} +123 -123
- package/web/assets/architecture-7EHR7CIX-6QZW5X65-DxJw65fT.js +1 -0
- package/web/assets/architectureDiagram-UL44E2DR-ChH9xW-N.js +36 -0
- package/web/assets/{blockDiagram-7IZFK4PR-B4niSLvR.js → blockDiagram-7IZFK4PR-BkWhpzWB.js} +1 -1
- package/web/assets/{c4Diagram-Y2BXMSZH-DIzEXalR.js → c4Diagram-Y2BXMSZH-DtBvpzWD.js} +1 -1
- package/web/assets/{chunk-3SSMPTDK-BzGeeheI.js → chunk-3SSMPTDK-B4_etUhr.js} +2 -2
- package/web/assets/{chunk-5IMINLNL-CnTLsXIV.js → chunk-5IMINLNL-DOdTeQri.js} +1 -1
- package/web/assets/{chunk-5VCL7Z4A-w1BCx98Z.js → chunk-5VCL7Z4A-CQ-2dYWj.js} +1 -1
- package/web/assets/chunk-6764PJDD-DpIS7hxD.js +1 -0
- package/web/assets/{chunk-67TQ5CYL-CChC2cAv.js → chunk-67TQ5CYL-4sNPJU1q.js} +3 -3
- package/web/assets/{chunk-7W6UQGC5-CgFyfZ6k.js → chunk-7W6UQGC5-KyEG0HQg.js} +1 -1
- package/web/assets/{chunk-AZZRMDJM-5E9ARdDQ.js → chunk-AZZRMDJM-y-xJBuMz.js} +1 -1
- package/web/assets/{chunk-INKRHTLW-ELpfuk9h.js → chunk-INKRHTLW-Nu4ri9P-.js} +1 -1
- package/web/assets/{chunk-JQRUD6KW-Dcq_Qnyy.js → chunk-JQRUD6KW-Dbva2Z17.js} +1 -1
- package/web/assets/{chunk-KGFNY3KK-W-VR57xb.js → chunk-KGFNY3KK-JGMJvKCK.js} +1 -1
- package/web/assets/chunk-KGYTTC2M-CWC_c3H9.js +161 -0
- package/web/assets/{chunk-KNLZD3CH-BuZBOcso.js → chunk-KNLZD3CH-BGlP6X9R.js} +1 -1
- package/web/assets/{chunk-KRXBNO2N-DH2NPM7I.js → chunk-KRXBNO2N-BW5o0KUN.js} +1 -1
- package/web/assets/{chunk-LCXTWHL2-Dfmux4m1.js → chunk-LCXTWHL2-zoomRtWX.js} +1 -1
- package/web/assets/{chunk-LII3EMHJ-3X33tCpU.js → chunk-LII3EMHJ-D-jm-dLa.js} +1 -1
- package/web/assets/{chunk-QA3QBVWF-D5Ha3GgB.js → chunk-QA3QBVWF-BgRW3SXd.js} +1 -1
- package/web/assets/{chunk-RG4AUYOV-CAkzcoRj.js → chunk-RG4AUYOV-DD0B2N2B.js} +1 -1
- package/web/assets/{chunk-T2UQINTJ-kGYG7ppP.js → chunk-T2UQINTJ-DSBqkzH7.js} +1 -1
- package/web/assets/chunk-T5OCTHI4-DdZvN-9i.js +1 -0
- package/web/assets/{chunk-UY5QBCOK-Du-c56gs.js → chunk-UY5QBCOK-DyF0vyGd.js} +1 -1
- package/web/assets/{chunk-W44A43WB-lrCpuupA.js → chunk-W44A43WB-JqMLIpR0.js} +1 -1
- package/web/assets/{chunk-ZXARS5L4-CSZRiyOf.js → chunk-ZXARS5L4-CxN8oiwI.js} +1 -1
- package/web/assets/classDiagram-KGZ6W3CR-DJm9BgF-.js +1 -0
- package/web/assets/classDiagram-v2-72OJOZXJ-DJm9BgF-.js +1 -0
- package/web/assets/{context-builder-_HS0v2ma.js → context-builder-BHiFUA8O.js} +10 -2
- package/web/assets/{cose-bilkent-UX7MHV2Q-BHv2bbtl.js → cose-bilkent-UX7MHV2Q-D71wNYRJ.js} +1 -1
- package/web/assets/dagre-ND4H6XIP-BIkxmhcu.js +4 -0
- package/web/assets/diagram-3NCE3AQN-BTa6iH0T.js +43 -0
- package/web/assets/diagram-GF46GFSD-DZDV0vBj.js +24 -0
- package/web/assets/{diagram-HNR7UZ2L-fUWreZzx.js → diagram-HNR7UZ2L-DFCfd5LI.js} +2 -2
- package/web/assets/diagram-QXG6HAR7-BzM--Jj0.js +24 -0
- package/web/assets/diagram-WEQXMOUZ-DFFwz0k4.js +10 -0
- package/web/assets/{erDiagram-L5TCEMPS-QN2eEP1e.js → erDiagram-L5TCEMPS-CfHMJJqo.js} +1 -1
- package/web/assets/eventmodeling-FCH6USID-MREXMVOE-BQm9QzEa.js +1 -0
- package/web/assets/{flowDiagram-H6V6AXG4-CXbXImlN.js → flowDiagram-H6V6AXG4-Be805QiJ.js} +3 -3
- package/web/assets/{ganttDiagram-JCBTUEKG-BHQssTz6.js → ganttDiagram-JCBTUEKG-D3iL2Aet.js} +1 -1
- package/web/assets/gitGraph-WXDBUCRP-R675I2BI-Cr_Bm2Nb.js +1 -0
- package/web/assets/gitGraphDiagram-S2ZK5IYY-CPIDgxGm.js +106 -0
- package/web/assets/index-BvYev1UJ.js +635 -0
- package/web/assets/index-_lgn7hs5.css +2 -0
- package/web/assets/info-J43DQDTF-KCYPFFUO-Cs4p2oyk.js +1 -0
- package/web/assets/infoDiagram-3YFTVSEB-Y7BKSoke.js +2 -0
- package/web/assets/{ishikawaDiagram-BNXS4ZKH-6LKOvBfp.js → ishikawaDiagram-BNXS4ZKH-CT4SHFuI.js} +1 -1
- package/web/assets/{journeyDiagram-M6C3CM3L-DNPYCpvq.js → journeyDiagram-M6C3CM3L-CvArszLo.js} +1 -1
- package/web/assets/{kanban-definition-75IXJCU3-DgDi9oJT.js → kanban-definition-75IXJCU3-C6yDxr7N.js} +3 -3
- package/web/assets/{katex-K3KEBU37-CrVLz8l7.js → katex-K3KEBU37-CTc5BslQ.js} +1 -1
- package/web/assets/{mindmap-definition-2TDM6QVE-CwR5sBB-.js → mindmap-definition-2TDM6QVE-BOc2WEne.js} +1 -1
- package/web/assets/packet-YPE3B663-LP52Z2RK-CKCrztD2.js +1 -0
- package/web/assets/pie-LRSECV5Y-TCRJHUBD-BB7pHqoD.js +1 -0
- package/web/assets/{pieDiagram-CU6KROY3-By8g6f6B.js → pieDiagram-CU6KROY3-Cvc0f_Pd.js} +1 -1
- package/web/assets/{quadrantDiagram-VICAPDV7-bAm6Hi_5.js → quadrantDiagram-VICAPDV7-yXSZ2lLz.js} +1 -1
- package/web/assets/radar-GUYGQ44K-RDLRG3WG-DhoTOcuK.js +1 -0
- package/web/assets/{requirementDiagram-JXO7QTGE-7oDcJ1_J.js → requirementDiagram-JXO7QTGE-BRcZvHs4.js} +1 -1
- package/web/assets/{sankeyDiagram-URQDO5SZ-LFJz53u5.js → sankeyDiagram-URQDO5SZ-1qEjlFIB.js} +1 -1
- package/web/assets/{sequenceDiagram-VS2MUI6T-FySeKCUy.js → sequenceDiagram-VS2MUI6T-PcSCuvrB.js} +3 -3
- package/web/assets/stateDiagram-7D4R322I-D6qYSV1E.js +1 -0
- package/web/assets/stateDiagram-v2-36443NZ5-Bg-ImOSX.js +1 -0
- package/web/assets/{timeline-definition-O6YCAMPW-Ds2CnVZK.js → timeline-definition-O6YCAMPW-QWkh01xw.js} +1 -1
- package/web/assets/treeView-BLDUP644-QA4HXRO3-ODehyGKL.js +1 -0
- package/web/assets/treemap-LRROVOQU-LLAWBHMP-BfpgC-7o.js +1 -0
- package/web/assets/{vennDiagram-MWXL3ELB-DUIEwXWp.js → vennDiagram-MWXL3ELB-ak4DfwuM.js} +3 -3
- package/web/assets/wardley-L42UT6IY-5TKZOOLJ-CNeL6VHE.js +1 -0
- package/web/assets/{wardleyDiagram-CUQ6CDDI-DEiFPQih.js → wardleyDiagram-CUQ6CDDI-CTzR4Loq.js} +1 -1
- package/web/assets/{xychartDiagram-N2JHSOCM-BUbayhST.js → xychartDiagram-N2JHSOCM-DA1BEaQz.js} +1 -1
- package/web/index.html +13 -15
- package/scripts/materialize-vendor-grammars.cjs +0 -97
- package/web/assets/architecture-7EHR7CIX-6QZW5X65-BETj2x4g.js +0 -1
- package/web/assets/architectureDiagram-UL44E2DR-D1EXI0zA.js +0 -36
- package/web/assets/chunk-2T2R6R2M-n6s9JZqv.js +0 -4
- package/web/assets/chunk-2UTLFMKG-CMBB1TMN.js +0 -1
- package/web/assets/chunk-4R4BOZG6-mKhL59ul.js +0 -159
- package/web/assets/chunk-6764PJDD-ChwMM2z4.js +0 -1
- package/web/assets/chunk-7J6CGLKN-DT-b53FT.js +0 -10
- package/web/assets/chunk-C62D2QBJ-CDAWj26E.js +0 -1
- package/web/assets/chunk-CEXFNPSA-D68Tk6ls.js +0 -1
- package/web/assets/chunk-CilyBKbf.js +0 -1
- package/web/assets/chunk-J5EP6P6S-DxWW0yvu.js +0 -1
- package/web/assets/chunk-KGYTTC2M-C6PHeuay.js +0 -1
- package/web/assets/chunk-RERM46MO-DQNbXtfw.js +0 -1
- package/web/assets/chunk-RKZBBQEN-BmTPLSyv.js +0 -1
- package/web/assets/chunk-RLI5ZMPA-DExu2DOK.js +0 -1
- package/web/assets/chunk-T5OCTHI4--9wWpVws.js +0 -1
- package/web/assets/chunk-UP6H54XL-DsKdC6jC.js +0 -1
- package/web/assets/chunk-UXSXWOXI-DR81EqLr.js +0 -1
- package/web/assets/classDiagram-KGZ6W3CR-Bv93af_b.js +0 -1
- package/web/assets/classDiagram-v2-72OJOZXJ-CLfEqHUa.js +0 -1
- package/web/assets/dagre-ND4H6XIP-DsIr9MOv.js +0 -4
- package/web/assets/diagram-3NCE3AQN-BqAtKUpW.js +0 -43
- package/web/assets/diagram-GF46GFSD-CaBG6n6o.js +0 -24
- package/web/assets/diagram-QXG6HAR7-CZ-O3rcV.js +0 -24
- package/web/assets/diagram-WEQXMOUZ-P4lSL4GH.js +0 -10
- package/web/assets/eventmodeling-FCH6USID-MREXMVOE-B3myHUst.js +0 -1
- package/web/assets/gitGraph-WXDBUCRP-R675I2BI-DF3rtf4A.js +0 -1
- package/web/assets/gitGraphDiagram-S2ZK5IYY-CCwVLT__.js +0 -106
- package/web/assets/index-BKWA-m7o.css +0 -2
- package/web/assets/index-CG6q8eTs.js +0 -626
- package/web/assets/info-J43DQDTF-KCYPFFUO-R4XV_akJ.js +0 -1
- package/web/assets/infoDiagram-3YFTVSEB-DmLICZx1.js +0 -2
- package/web/assets/packet-YPE3B663-LP52Z2RK-CgQPdNZa.js +0 -1
- package/web/assets/pie-LRSECV5Y-TCRJHUBD-BzWOwc_c.js +0 -1
- package/web/assets/radar-GUYGQ44K-RDLRG3WG-B7KG0FN7.js +0 -1
- package/web/assets/stateDiagram-7D4R322I-CB2nABwH.js +0 -1
- package/web/assets/stateDiagram-v2-36443NZ5-COBGd2RL.js +0 -1
- package/web/assets/treeView-BLDUP644-QA4HXRO3-CRSxEwLO.js +0 -1
- package/web/assets/treemap-LRROVOQU-LLAWBHMP-a0PfKoVn.js +0 -1
- package/web/assets/wardley-L42UT6IY-5TKZOOLJ-BKQ6e5mC.js +0 -1
- /package/web/assets/{chunk-AQ6EADP3-CCFje6lL.js → chunk-AQ6EADP3-CZhslHi-.js} +0 -0
|
@@ -0,0 +1,646 @@
|
|
|
1
|
+
import { generateId } from '../../../lib/utils.js';
|
|
2
|
+
import { computeReachingDefs } from './reaching-defs.js';
|
|
3
|
+
import { computeControlDependence } from './control-dependence.js';
|
|
4
|
+
import { computePostDominators, isExitReachableFromAllBlocks, NO_IPDOM, } from './post-dominators.js';
|
|
5
|
+
import { augmentForPostDom } from './synthetic-escape.js';
|
|
6
|
+
import { DEFAULT_PDG_MAX_SITES_PER_STATEMENT } from './visitors/call-site-harvest.js';
|
|
7
|
+
import { calleeIdPosKey } from '../scope-resolution/graph-bridge/callee-id-sink.js';
|
|
8
|
+
import { encodeReachingDefReasonPairs } from './reaching-def-reason-codec.js';
|
|
9
|
+
/**
|
|
10
|
+
* Reserved token placed in `BasicBlock.callees` when a statement's call sites
|
|
11
|
+
* were truncated at {@link DEFAULT_PDG_MAX_SITES_PER_STATEMENT}: the recorded
|
|
12
|
+
* callee list is then INCOMPLETE, so over-cap callees are absent. `*` is not a
|
|
13
|
+
* valid identifier leaf, so it cannot collide with a real callee name. The
|
|
14
|
+
* impact bridge treats a slice containing this sentinel as "callees unknown" and
|
|
15
|
+
* keeps reach callgraph-equal (proven), rather than falsely labeling an
|
|
16
|
+
* absent-but-real callee `unproven-bridge`.
|
|
17
|
+
*/
|
|
18
|
+
export const CALLEES_TRUNCATED_SENTINEL = '*';
|
|
19
|
+
/**
|
|
20
|
+
* Inner separator for the `BasicBlock.calleeIds` cell (resolved callee symbol
|
|
21
|
+
* ids). A TAB is used — NOT a space — because resolved ids embed `filePath` and
|
|
22
|
+
* C++ overload shape tags with multi-word primitive types (e.g. `unsigned char`,
|
|
23
|
+
* `long double`), so an id can legitimately contain a space; a space-joined cell
|
|
24
|
+
* then fragments on read and silently drops inter-procedural reach to that
|
|
25
|
+
* callee (#2227 tri-review). A tab cannot appear in a tree-sitter-derived id
|
|
26
|
+
* token (paths/identifiers/type tokens are tab-free) and round-trips intact
|
|
27
|
+
* through `escapeCSVField` (tab is in its preserved set) and the RFC-4180 COPY
|
|
28
|
+
* reader (every cell is quoted). Producer ({@link calleeIdsOfBlock}) and
|
|
29
|
+
* consumer (`splitCalleeIds`) import this single constant so they cannot drift.
|
|
30
|
+
* The sibling `callees` (leaf-name) cell stays space-joined — leaf names are
|
|
31
|
+
* bare identifiers and never contain a space.
|
|
32
|
+
*/
|
|
33
|
+
export const CALLEE_ID_SEP = '\t';
|
|
34
|
+
/**
|
|
35
|
+
* Default per-function CFG edge cap. A pathological generated function could
|
|
36
|
+
* otherwise emit an unbounded edge set; the cap bounds graph growth and is
|
|
37
|
+
* overridable via `--pdg` options. `0` (in options) means no cap (unlimited
|
|
38
|
+
* — see the `cap` mapping in {@link emitFileCfgs}); `undefined` means this
|
|
39
|
+
* default.
|
|
40
|
+
*/
|
|
41
|
+
export const DEFAULT_MAX_CFG_EDGES_PER_FUNCTION = 5000;
|
|
42
|
+
/**
|
|
43
|
+
* Default per-function REACHING_DEF edge cap (#2082 M2 KTD9). 4000 mirrors
|
|
44
|
+
* Joern's per-method `maxNumberOfDefinitions` — the closest production prior
|
|
45
|
+
* art — but truncates-and-warns instead of silently skipping the function.
|
|
46
|
+
* Counts (defBlock, useBlock, binding) DEDUPED edges, not statement-level
|
|
47
|
+
* facts. `0` ⇒ unlimited; `undefined` ⇒ this default.
|
|
48
|
+
*/
|
|
49
|
+
export const DEFAULT_PDG_MAX_REACHING_DEF_EDGES_PER_FUNCTION = 4000;
|
|
50
|
+
/**
|
|
51
|
+
* Default per-function CDG edge cap (#2085 M5). CDG edge count is bounded by
|
|
52
|
+
* (blocks × control-nesting-depth) — comparable to the CFG edge count — so it
|
|
53
|
+
* reuses the CFG default of 5000. Counts DEDUPED (controller, dependent, label)
|
|
54
|
+
* edges (the pure {@link computeControlDependence} already dedups). `0` ⇒
|
|
55
|
+
* unlimited; `undefined` ⇒ this default. Folded into the `RepoMeta.pdg` stamp
|
|
56
|
+
* (U5) so introducing CDG forces a full writeback for pre-CDG `--pdg` indexes.
|
|
57
|
+
*/
|
|
58
|
+
export const DEFAULT_PDG_MAX_CDG_EDGES_PER_FUNCTION = 5000;
|
|
59
|
+
/**
|
|
60
|
+
* Heap-safety ceiling on {@link computeControlDependence}'s pre-dedup
|
|
61
|
+
* materialization (#2188 review). The walk is O(edges × post-dom depth), and its
|
|
62
|
+
* `out` IS the deduped-edge quantity the per-function cap trims — so, UNLIKE
|
|
63
|
+
* REACHING_DEF's facts ceiling, this is deliberately NOT derived from the
|
|
64
|
+
* runtime edge cap (doing so would pre-truncate the very set the cap reports on,
|
|
65
|
+
* losing the exact dropped count). A fixed, generous multiple of the default
|
|
66
|
+
* edge cap: far above any real function — a catastrophe backstop only. When hit,
|
|
67
|
+
* the per-function cap reporting plus the `truncated` flag keep it observable
|
|
68
|
+
* (never a silent drop).
|
|
69
|
+
*/
|
|
70
|
+
export const DEFAULT_PDG_MAX_CDG_MATERIALIZATION_PER_FUNCTION = 8 * DEFAULT_PDG_MAX_CDG_EDGES_PER_FUNCTION;
|
|
71
|
+
/**
|
|
72
|
+
* Env flag that additionally emits diagnostic `POST_DOMINATE` edges
|
|
73
|
+
* (block → its immediate post-dominator) alongside CDG (#2085 M5 KTD8). Off in
|
|
74
|
+
* every normal `--pdg` run — these are for inspecting the post-dom tree, not a
|
|
75
|
+
* queryable product surface. Accepts `1`/`true` (case-insensitive).
|
|
76
|
+
*/
|
|
77
|
+
export const POST_DOMINATE_DEBUG_ENV = 'GITNEXUS_PDG_EMIT_POST_DOMINATE';
|
|
78
|
+
/**
|
|
79
|
+
* Fact-materialization headroom over the edge cap (#2082 M2 U3/F3): facts are
|
|
80
|
+
* O(defs×uses) BY SPEC in merge-heavy code, and the edge cap alone bounds the
|
|
81
|
+
* GRAPH, not the per-function memory spike of materializing facts before
|
|
82
|
+
* dedup. {@link emitFileReachingDefs} hands `edgeCap × this` to
|
|
83
|
+
* `computeReachingDefs` as `maxFacts` (unlimited when the edge cap is 0) —
|
|
84
|
+
* single source of truth; the DEFAULT constant below is derived, never the
|
|
85
|
+
* mechanism.
|
|
86
|
+
*/
|
|
87
|
+
export const REACHING_DEF_FACTS_PER_EDGE_CAP = 4;
|
|
88
|
+
/** Derived emit-path fact limit at the default edge cap (bench/doc anchor). */
|
|
89
|
+
export const DEFAULT_PDG_MAX_REACHING_DEF_FACTS_PER_FUNCTION = REACHING_DEF_FACTS_PER_EDGE_CAP * DEFAULT_PDG_MAX_REACHING_DEF_EDGES_PER_FUNCTION;
|
|
90
|
+
/**
|
|
91
|
+
* Fixpoint-iteration budget for {@link computeReachingDefs}, as a multiple of
|
|
92
|
+
* the function's block count ({@link emitFileReachingDefs} passes
|
|
93
|
+
* `blocks.length × this` as `maxBlockVisits`). Iterative reaching-defs on a
|
|
94
|
+
* reducible CFG converges in O(loop-nesting-depth) passes, so a worklist
|
|
95
|
+
* re-visits each block a small multiple of times for real code; this budget
|
|
96
|
+
* tolerates a nesting depth far beyond any hand-written function (real code is
|
|
97
|
+
* ≤ ~15 deep) while truncating the pathological deep nest that otherwise drives
|
|
98
|
+
* the solver to O(blocks²) — measured at seconds + GB on a machine-generated
|
|
99
|
+
* 2000-line all-loops function whose fact count stays linear (so `maxFacts`
|
|
100
|
+
* never fires). Truncation degrades to a sound empty REACHING_DEF for that one
|
|
101
|
+
* function (status `truncated`), never wrong facts.
|
|
102
|
+
*
|
|
103
|
+
* As of #2201 this ceiling is an adversarial-only backstop that effectively
|
|
104
|
+
* never fires on real code: the production solver auto-selects the SSA-sparse
|
|
105
|
+
* path for the looping functions that would breach it, and the SSA path has no
|
|
106
|
+
* fixpoint iteration (it answers reaching queries from the def-use graph in one
|
|
107
|
+
* pass) so it computes the full facts where the dense worklist would have
|
|
108
|
+
* truncated. The budget is still consulted on the dense fallback path (small /
|
|
109
|
+
* loop-free functions, and throw-edge / unreachable-block functions the SSA path
|
|
110
|
+
* does not model). WTO / loop-aware iteration ordering was benchmarked and
|
|
111
|
+
* rejected (0% faster — the cost was dense-set propagation, not visitation
|
|
112
|
+
* order); SSA-sparse was the real fix. See reaching-defs.ts.
|
|
113
|
+
*/
|
|
114
|
+
export const DEFAULT_PDG_MAX_REACHING_DEF_BLOCK_REVISITS = 64;
|
|
115
|
+
/**
|
|
116
|
+
* The single BasicBlock id template (module doc). Exported for the M3 taint
|
|
117
|
+
* emit path (taint/emit.ts), whose TAINTED/SANITIZES edges must address the
|
|
118
|
+
* SAME persisted block nodes — a re-derived copy of this template would
|
|
119
|
+
* silently dangle the moment either drifted.
|
|
120
|
+
*/
|
|
121
|
+
export const basicBlockId = (filePath, functionStartLine, functionStartColumn, blockIndex) => `BasicBlock:${filePath}:${functionStartLine}:${functionStartColumn}:${blockIndex}`;
|
|
122
|
+
/**
|
|
123
|
+
* Whether an untrusted `cfgSideChannel` element is safe to feed to
|
|
124
|
+
* {@link emitFileCfgs}. Deliberately NOT full FunctionCfg validation — it
|
|
125
|
+
* checks exactly the fields whose corruption is SILENT given emit's
|
|
126
|
+
* mechanics: {@link basicBlockId} string-templates every id-anchor value
|
|
127
|
+
* (filePath, function start line/column, block index, edge endpoints) and
|
|
128
|
+
* the graph's addNode/addRelationship are no-throw Map inserts. Unchecked,
|
|
129
|
+
* a missing anchor field cross-wires same-`undefined`-id blocks across
|
|
130
|
+
* functions (addNode is first-writer-wins), and an edge endpoint that
|
|
131
|
+
* matches no block index becomes a dangling `BasicBlock:…:<n>` edge that
|
|
132
|
+
* detonates much later at DB bulk-load instead of throwing here — so
|
|
133
|
+
* endpoints are checked for MEMBERSHIP in the block-index set, not just
|
|
134
|
+
* integer-ness. Lives in this module so the guard evolves with the id
|
|
135
|
+
* templating it defends (#2099 F4; M2 fields that join the id path must
|
|
136
|
+
* join this check).
|
|
137
|
+
*/
|
|
138
|
+
export const isEmitSafeCfg = (cfg) => {
|
|
139
|
+
if (typeof cfg?.filePath !== 'string' ||
|
|
140
|
+
!Number.isInteger(cfg.functionStartLine) ||
|
|
141
|
+
!Number.isInteger(cfg.functionStartColumn) ||
|
|
142
|
+
!Array.isArray(cfg.blocks) ||
|
|
143
|
+
!Array.isArray(cfg.edges)) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
// Contiguity (index === position), not just integer-ness: every consumer —
|
|
147
|
+
// this module's id templating AND the reaching-defs solver's
|
|
148
|
+
// position-indexed adjacency arrays — assumes blocks[i].index === i. A
|
|
149
|
+
// membership-only check would admit a compacted channel ({index:0},{index:5})
|
|
150
|
+
// whose edge 0→5 passes membership but indexes past the arrays downstream.
|
|
151
|
+
for (let i = 0; i < cfg.blocks.length; i++) {
|
|
152
|
+
if (cfg.blocks[i]?.index !== i)
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
const n = cfg.blocks.length;
|
|
156
|
+
// entry/exit must land on real blocks — the solver feeds entryIndex straight
|
|
157
|
+
// into its RPO walk, where an out-of-range index throws and (worse than this
|
|
158
|
+
// one element) costs the whole FILE's REACHING_DEF pass (tri-review P3).
|
|
159
|
+
if (!Number.isInteger(cfg.entryIndex) ||
|
|
160
|
+
cfg.entryIndex < 0 ||
|
|
161
|
+
cfg.entryIndex >= n ||
|
|
162
|
+
!Number.isInteger(cfg.exitIndex) ||
|
|
163
|
+
cfg.exitIndex < 0 ||
|
|
164
|
+
cfg.exitIndex >= n) {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
return cfg.edges.every((e) => Number.isInteger(e?.from) &&
|
|
168
|
+
Number.isInteger(e?.to) &&
|
|
169
|
+
e.from >= 0 &&
|
|
170
|
+
e.from < n &&
|
|
171
|
+
e.to >= 0 &&
|
|
172
|
+
e.to < n);
|
|
173
|
+
};
|
|
174
|
+
/**
|
|
175
|
+
* Whether a structurally-valid CFG's M2 statement facts are safe to feed to
|
|
176
|
+
* the reaching-defs solver + REACHING_DEF id templating (#2082 U1/U4): the
|
|
177
|
+
* binding table's name/declLine/declColumn template into edge ids, and
|
|
178
|
+
* statement def/use indices must stay IN RANGE of the table (an escaping
|
|
179
|
+
* index would fabricate `undefined`-keyed ids). Deliberately SEPARATE from
|
|
180
|
+
* {@link isEmitSafeCfg}: malformed facts must cost only the function's
|
|
181
|
+
* REACHING_DEF projection — degrading to M1 behavior (CFG emitted, no facts)
|
|
182
|
+
* — never the BasicBlock/CFG layer itself.
|
|
183
|
+
*/
|
|
184
|
+
export const hasEmitSafeFacts = (cfg) => {
|
|
185
|
+
const bindings = cfg.bindings;
|
|
186
|
+
if (bindings === undefined) {
|
|
187
|
+
// Pre-M2 channel — statements must be absent too.
|
|
188
|
+
return cfg.blocks.every((b) => b.statements === undefined);
|
|
189
|
+
}
|
|
190
|
+
if (!Array.isArray(bindings))
|
|
191
|
+
return false;
|
|
192
|
+
for (const b of bindings) {
|
|
193
|
+
if (typeof b?.name !== 'string' ||
|
|
194
|
+
!Number.isInteger(b.declLine) ||
|
|
195
|
+
!Number.isInteger(b.declColumn)) {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const bindingCount = bindings.length;
|
|
200
|
+
const inRange = (i) => Number.isInteger(i) && i >= 0 && i < bindingCount;
|
|
201
|
+
for (const b of cfg.blocks) {
|
|
202
|
+
const stmts = b.statements;
|
|
203
|
+
if (stmts === undefined)
|
|
204
|
+
continue;
|
|
205
|
+
if (!Array.isArray(stmts))
|
|
206
|
+
return false;
|
|
207
|
+
for (const s of stmts) {
|
|
208
|
+
if (!Number.isInteger(s?.line) || !Array.isArray(s.defs) || !Array.isArray(s.uses)) {
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
if (!s.defs.every(inRange) || !s.uses.every(inRange))
|
|
212
|
+
return false;
|
|
213
|
+
if (s.mayDefs !== undefined) {
|
|
214
|
+
if (!Array.isArray(s.mayDefs) || !s.mayDefs.every(inRange))
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return true;
|
|
220
|
+
};
|
|
221
|
+
/**
|
|
222
|
+
* Emit BasicBlock nodes + CFG edges for every function CFG in `cfgs`.
|
|
223
|
+
*
|
|
224
|
+
* `maxEdgesPerFunction` caps edges per function. On overflow we stop emitting
|
|
225
|
+
* that function's remaining edges and call `onWarn` naming the dropped count —
|
|
226
|
+
* no silent truncation (KTD6/R6). Block nodes are always fully emitted (their
|
|
227
|
+
* count is bounded by the function's statement count); only edges are capped.
|
|
228
|
+
*/
|
|
229
|
+
/**
|
|
230
|
+
* Space-joined, sorted, de-duplicated leaf callee names invoked directly in a
|
|
231
|
+
* block (`call`/`new` sites; the leaf of a dotted path — `child_process.exec` ⇒
|
|
232
|
+
* `exec`). This is the persisted substrate for statement-precise inter-procedural
|
|
233
|
+
* impact: a callee reached from a function is "proven" to be impacted by a
|
|
234
|
+
* changed statement iff its name appears in the callees of a block in that
|
|
235
|
+
* statement's dependence slice. `sites` is harvested only for TS/JS under `--pdg`
|
|
236
|
+
* (and absent on synthetic ENTRY/EXIT), so the field is empty elsewhere and the
|
|
237
|
+
* bridge degrades to the prior (callgraph-equal) behavior. Space-joined because
|
|
238
|
+
* leaf names are identifiers (no spaces) and the field is itself one CSV cell.
|
|
239
|
+
*/
|
|
240
|
+
export function calleesOfBlock(block) {
|
|
241
|
+
const names = new Set();
|
|
242
|
+
for (const stmt of block.statements ?? []) {
|
|
243
|
+
// A statement whose recorded sites reached the per-statement cap may have
|
|
244
|
+
// dropped over-cap callees (the harvester stops at the cap). Flag the block
|
|
245
|
+
// callee-unknown so the impact bridge keeps it callgraph-equal rather than
|
|
246
|
+
// under-proving an absent-but-real callee.
|
|
247
|
+
if ((stmt.sites?.length ?? 0) >= DEFAULT_PDG_MAX_SITES_PER_STATEMENT) {
|
|
248
|
+
names.add(CALLEES_TRUNCATED_SENTINEL);
|
|
249
|
+
}
|
|
250
|
+
for (const site of stmt.sites ?? []) {
|
|
251
|
+
if (site.kind === 'member-read')
|
|
252
|
+
continue;
|
|
253
|
+
const callee = site.callee;
|
|
254
|
+
if (!callee)
|
|
255
|
+
continue;
|
|
256
|
+
const leaf = callee.slice(callee.lastIndexOf('.') + 1);
|
|
257
|
+
if (leaf)
|
|
258
|
+
names.add(leaf);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return [...names].sort().join(' ');
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Tab-joined ({@link CALLEE_ID_SEP}), sorted, de-duplicated RESOLVED callee symbol ids invoked
|
|
265
|
+
* directly in a block — the SOUND parallel to {@link calleesOfBlock}'s leaf
|
|
266
|
+
* names (#2227 follow-up plan U3, KTD1/KTD2/KTD7). Each block site's call-site
|
|
267
|
+
* anchor `at` (U1) is joined by EXACT position to the per-file resolved-id map
|
|
268
|
+
* `fileMap` (U2's `(line,col) → Set<calleeId>`), so a callee reached from a
|
|
269
|
+
* function is proven impacted by a changed statement iff its resolved id — not
|
|
270
|
+
* just its leaf NAME — appears in a slice block's `calleeIds`. This eliminates
|
|
271
|
+
* the same-leaf-name collision (false-proven) and import-alias (false-unproven)
|
|
272
|
+
* the name predicate suffers on overloading languages.
|
|
273
|
+
*
|
|
274
|
+
* The site partitioning is inherited verbatim from {@link calleesOfBlock}: the
|
|
275
|
+
* SAME `member-read`-skip and the SAME per-statement site cap (R7) — a capped
|
|
276
|
+
* statement adds {@link CALLEES_TRUNCATED_SENTINEL} so the bridge keeps the
|
|
277
|
+
* block callee-unknown for ids too (callgraph-equal rather than under-proving).
|
|
278
|
+
* Because `at` is the SAME anchor the CALLS resolution keyed `atRange` on
|
|
279
|
+
* (KTD7), the join lands on exactly the sites the name harvest partitioned,
|
|
280
|
+
* including the nested-function exclusion (so a single-line inline closure's
|
|
281
|
+
* inner call never leaks its id into the outer block).
|
|
282
|
+
*
|
|
283
|
+
* `fileMap` is the resolved-id map for THIS file (`calleeIdAccumulator.get(
|
|
284
|
+
* filePath)` in run.ts). Absent (pdg off, or a file with no captured CALLS) ⇒
|
|
285
|
+
* `''` — the bridge then degrades to the leaf-name fallback (R3). A site whose
|
|
286
|
+
* `at` is absent (pre-U1 channel) or whose position is not in the map
|
|
287
|
+
* contributes no id (graceful, never throws).
|
|
288
|
+
*/
|
|
289
|
+
export function calleeIdsOfBlock(block, fileMap) {
|
|
290
|
+
if (fileMap === undefined)
|
|
291
|
+
return '';
|
|
292
|
+
const ids = new Set();
|
|
293
|
+
for (const stmt of block.statements ?? []) {
|
|
294
|
+
// Mirror calleesOfBlock's cap signal: an over-cap statement dropped sites,
|
|
295
|
+
// so the id list is INCOMPLETE — flag the block callee-unknown (R7).
|
|
296
|
+
if ((stmt.sites?.length ?? 0) >= DEFAULT_PDG_MAX_SITES_PER_STATEMENT) {
|
|
297
|
+
ids.add(CALLEES_TRUNCATED_SENTINEL);
|
|
298
|
+
}
|
|
299
|
+
for (const site of stmt.sites ?? []) {
|
|
300
|
+
if (site.kind === 'member-read')
|
|
301
|
+
continue;
|
|
302
|
+
const at = site.at;
|
|
303
|
+
if (!at)
|
|
304
|
+
continue;
|
|
305
|
+
const resolved = fileMap.get(calleeIdPosKey(at[0], at[1]));
|
|
306
|
+
if (resolved === undefined)
|
|
307
|
+
continue;
|
|
308
|
+
for (const id of resolved)
|
|
309
|
+
ids.add(id);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return [...ids].sort().join(CALLEE_ID_SEP);
|
|
313
|
+
}
|
|
314
|
+
export function emitFileCfgs(graph, cfgs, maxEdgesPerFunction = DEFAULT_MAX_CFG_EDGES_PER_FUNCTION, onWarn, calleeIdMap) {
|
|
315
|
+
const result = { blocks: 0, edges: 0, droppedEdges: 0, cappedFunctions: 0 };
|
|
316
|
+
const cap = maxEdgesPerFunction > 0 ? maxEdgesPerFunction : Infinity;
|
|
317
|
+
for (const cfg of cfgs) {
|
|
318
|
+
const { filePath, functionStartLine, functionStartColumn } = cfg;
|
|
319
|
+
for (const b of cfg.blocks) {
|
|
320
|
+
graph.addNode({
|
|
321
|
+
id: basicBlockId(filePath, functionStartLine, functionStartColumn, b.index),
|
|
322
|
+
label: 'BasicBlock',
|
|
323
|
+
properties: {
|
|
324
|
+
name: '', // BasicBlock has no name column; identified by id + span
|
|
325
|
+
filePath,
|
|
326
|
+
startLine: b.startLine,
|
|
327
|
+
endLine: b.endLine,
|
|
328
|
+
text: b.text,
|
|
329
|
+
// Space-joined leaf callee names invoked in this block — the
|
|
330
|
+
// statement-precise inter-procedural reach substrate. Harvested from
|
|
331
|
+
// the per-statement `sites` (already on the side channel); dropping
|
|
332
|
+
// them here is what made the impact-mode bridge labeling degenerate.
|
|
333
|
+
callees: calleesOfBlock(b),
|
|
334
|
+
// Space-joined RESOLVED callee symbol ids — the SOUND parallel to
|
|
335
|
+
// `callees`, joined from the U2 map by each site's exact `at`
|
|
336
|
+
// position (#2227 follow-up U3). Absent map (pdg off / no captures)
|
|
337
|
+
// ⇒ `''`, and the bridge falls back to the leaf-name match (R3).
|
|
338
|
+
calleeIds: calleeIdsOfBlock(b, calleeIdMap),
|
|
339
|
+
},
|
|
340
|
+
});
|
|
341
|
+
result.blocks++;
|
|
342
|
+
}
|
|
343
|
+
let emittedForFn = 0;
|
|
344
|
+
for (const e of cfg.edges) {
|
|
345
|
+
if (emittedForFn >= cap) {
|
|
346
|
+
const dropped = cfg.edges.length - emittedForFn;
|
|
347
|
+
result.droppedEdges += dropped;
|
|
348
|
+
result.cappedFunctions++;
|
|
349
|
+
onWarn?.(`[cfg] ${filePath}:${functionStartLine}: per-function CFG edge cap ` +
|
|
350
|
+
`(${maxEdgesPerFunction}) reached — dropped ${dropped} of ${cfg.edges.length} edges`);
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
const sourceId = basicBlockId(filePath, functionStartLine, functionStartColumn, e.from);
|
|
354
|
+
const targetId = basicBlockId(filePath, functionStartLine, functionStartColumn, e.to);
|
|
355
|
+
graph.addRelationship({
|
|
356
|
+
id: generateId('CFG', `${sourceId}->${targetId}:${e.kind}`),
|
|
357
|
+
type: 'CFG',
|
|
358
|
+
sourceId,
|
|
359
|
+
targetId,
|
|
360
|
+
confidence: 1.0,
|
|
361
|
+
reason: e.kind, // CfgEdgeKind (seq/cond-true/loop-back/…) — queryable
|
|
362
|
+
});
|
|
363
|
+
result.edges++;
|
|
364
|
+
emittedForFn++;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return result;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Stable identity for a binding inside edge ids (#2082 M2 KTD3/KTD9):
|
|
371
|
+
* `name:declLine:declCol` for declared bindings, `name@module` for synthetic
|
|
372
|
+
* ones. Distinct same-name bindings never share a key; identifier characters
|
|
373
|
+
* cannot contain the id separators. Exported for the M3 taint emit path —
|
|
374
|
+
* TAINTED/SANITIZES ids key bindings with the same discipline.
|
|
375
|
+
*/
|
|
376
|
+
export const bindingKey = (b) => b.synthetic ? `${b.name}@module` : `${b.name}:${b.declLine}:${b.declColumn}`;
|
|
377
|
+
/**
|
|
378
|
+
* Compute reaching definitions per function and persist the bounded
|
|
379
|
+
* REACHING_DEF projection (#2082 M2 U4).
|
|
380
|
+
*
|
|
381
|
+
* Facts are DEDUPED to (defBlock, useBlock, binding) before budgeting — the
|
|
382
|
+
* persisted columns (`from,to,type,confidence,reason,step`; relationship ids
|
|
383
|
+
* are in-memory-only, the CodeRelation table has no id column) cannot
|
|
384
|
+
* distinguish finer rows, so statement-indexed ids would only manufacture
|
|
385
|
+
* byte-identical duplicate rows that burn budget. Statement granularity lives
|
|
386
|
+
* in the in-memory {@link computeReachingDefs} result, which the M3 taint
|
|
387
|
+
* engine recomputes on demand — the budget here governs only this projection
|
|
388
|
+
* and can never drop a taint fact.
|
|
389
|
+
*
|
|
390
|
+
* R7 (no silent truncation) covers BOTH layers: the per-function edge cap AND
|
|
391
|
+
* the solver's fact-materialization limit (which can fire without the edge
|
|
392
|
+
* cap ever being reached, since dedup is many-to-one) each produce one
|
|
393
|
+
* unconditional `onWarn`. The edge-cap warn names the top bindings by fact
|
|
394
|
+
* count — overflow is almost always one variable, which is exactly the datum
|
|
395
|
+
* M3 tuning wants.
|
|
396
|
+
*/
|
|
397
|
+
export function emitFileReachingDefs(graph, cfgs, maxEdgesPerFunction = DEFAULT_PDG_MAX_REACHING_DEF_EDGES_PER_FUNCTION, onWarn,
|
|
398
|
+
// U12: a per-file memoized solver lets the RD-emit / harvest / taint passes
|
|
399
|
+
// share the SAME per-function fixpoint (this caller is its own cache bucket —
|
|
400
|
+
// it passes maxBlockVisits, the harvest/taint callers do not). Defaults to the
|
|
401
|
+
// plain solver so existing callers are unaffected.
|
|
402
|
+
solve = computeReachingDefs) {
|
|
403
|
+
const result = {
|
|
404
|
+
edges: 0,
|
|
405
|
+
droppedEdges: 0,
|
|
406
|
+
cappedFunctions: 0,
|
|
407
|
+
truncatedFunctions: 0,
|
|
408
|
+
malformedFactFunctions: 0,
|
|
409
|
+
facts: 0,
|
|
410
|
+
};
|
|
411
|
+
const cap = maxEdgesPerFunction > 0 ? maxEdgesPerFunction : Infinity;
|
|
412
|
+
const maxFacts = Number.isFinite(cap) ? cap * REACHING_DEF_FACTS_PER_EDGE_CAP : 0; // 0 ⇒ unlimited
|
|
413
|
+
for (const cfg of cfgs) {
|
|
414
|
+
// Graceful degradation: malformed M2 facts cost only this function's
|
|
415
|
+
// REACHING_DEF projection — its BasicBlock/CFG layer was already emitted.
|
|
416
|
+
if (!hasEmitSafeFacts(cfg)) {
|
|
417
|
+
result.malformedFactFunctions++;
|
|
418
|
+
onWarn?.(`[reaching-defs] ${cfg.filePath}:${cfg.functionStartLine}: malformed ` +
|
|
419
|
+
`statement facts (bad binding table or out-of-range fact indices) — ` +
|
|
420
|
+
`REACHING_DEF skipped for this function; its CFG is unaffected`);
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
const r = solve(cfg, {
|
|
424
|
+
maxFacts,
|
|
425
|
+
maxBlockVisits: cfg.blocks.length * DEFAULT_PDG_MAX_REACHING_DEF_BLOCK_REVISITS,
|
|
426
|
+
});
|
|
427
|
+
if (r.status === 'no-facts')
|
|
428
|
+
continue;
|
|
429
|
+
result.facts += r.facts.length;
|
|
430
|
+
const { filePath, functionStartLine, functionStartColumn } = cfg;
|
|
431
|
+
if (r.status === 'truncated') {
|
|
432
|
+
result.truncatedFunctions++;
|
|
433
|
+
onWarn?.(`[reaching-defs] ${filePath}:${functionStartLine}: fact materialization ` +
|
|
434
|
+
`limit (${maxFacts}) reached — facts beyond it were not computed; ` +
|
|
435
|
+
`the persisted REACHING_DEF projection for this function is sparse`);
|
|
436
|
+
}
|
|
437
|
+
else if (r.status === 'overflow') {
|
|
438
|
+
result.truncatedFunctions++;
|
|
439
|
+
onWarn?.(`[reaching-defs] ${filePath}:${functionStartLine}: a basic block exceeds ` +
|
|
440
|
+
`the def-key stride (≥2^21 coalesced statements — minified/generated ` +
|
|
441
|
+
`code) — REACHING_DEF skipped for this function (computing any facts ` +
|
|
442
|
+
`would risk wrong-block aliasing); its CFG is unaffected`);
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
// Dedup to (defBlock, useBlock, binding) — facts arrive sorted, so the
|
|
446
|
+
// deduped order (and therefore cap truncation) is deterministic. ONE edge per
|
|
447
|
+
// group (the edge COUNT is unchanged — substrate/bench safe), but the FU-B-2
|
|
448
|
+
// annotation AGGREGATES the FULL ordered list of (defLine, useLine) pairs for
|
|
449
|
+
// that group into the persisted `reason`. The first fact of a group (facts
|
|
450
|
+
// sort by def block, def stmt, use block, use stmt, binding) keeps the group's
|
|
451
|
+
// emit position; every subsequent fact of the SAME (block-pair, binding)
|
|
452
|
+
// appends its line pair to that group's list. Carrying the full list (not just
|
|
453
|
+
// the first pair) is what makes a SAME-BINDING reassignment chain recoverable:
|
|
454
|
+
// `acc = f(acc); acc = g(acc)` coalesces into one self-block whose
|
|
455
|
+
// `acc@N->acc@N+1` and `acc@N+1->acc@N+2` steps share the one group — a
|
|
456
|
+
// first-pair-only annotation could chain N->N+1 but never reach N+2. Dedup of
|
|
457
|
+
// exact-duplicate pairs within a group keeps the list compact (a `x = x + 1`
|
|
458
|
+
// self-fact never re-adds the same pair).
|
|
459
|
+
const groupIndex = new Map();
|
|
460
|
+
const deduped = [];
|
|
461
|
+
for (const f of r.facts) {
|
|
462
|
+
const key = `${f.def.blockIndex}:${f.use.blockIndex}:${f.bindingIdx}`;
|
|
463
|
+
const at = groupIndex.get(key);
|
|
464
|
+
const pair = { defLine: f.def.line, useLine: f.use.line };
|
|
465
|
+
if (at === undefined) {
|
|
466
|
+
groupIndex.set(key, deduped.length);
|
|
467
|
+
deduped.push({
|
|
468
|
+
defBlock: f.def.blockIndex,
|
|
469
|
+
useBlock: f.use.blockIndex,
|
|
470
|
+
bindingIdx: f.bindingIdx,
|
|
471
|
+
pairs: [pair],
|
|
472
|
+
});
|
|
473
|
+
continue;
|
|
474
|
+
}
|
|
475
|
+
const list = deduped[at].pairs;
|
|
476
|
+
// Skip an exact-duplicate (defLine, useLine) — a self-referential statement
|
|
477
|
+
// (`x = x + 1`) emits the same line pair more than once; the list only needs
|
|
478
|
+
// each distinct step once for the projection walk.
|
|
479
|
+
if (!list.some((p) => p.defLine === pair.defLine && p.useLine === pair.useLine)) {
|
|
480
|
+
list.push(pair);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
let emittedForFn = 0;
|
|
484
|
+
for (const edge of deduped) {
|
|
485
|
+
if (emittedForFn >= cap) {
|
|
486
|
+
const dropped = deduped.length - emittedForFn;
|
|
487
|
+
result.droppedEdges += dropped;
|
|
488
|
+
result.cappedFunctions++;
|
|
489
|
+
// Tallied lazily — cap overflow is the rare path; the common uncapped
|
|
490
|
+
// case must not pay a per-fact counting pass.
|
|
491
|
+
const factsPerBinding = new Map();
|
|
492
|
+
for (const f of r.facts) {
|
|
493
|
+
factsPerBinding.set(f.bindingIdx, (factsPerBinding.get(f.bindingIdx) ?? 0) + 1);
|
|
494
|
+
}
|
|
495
|
+
const top = [...factsPerBinding.entries()]
|
|
496
|
+
.sort((a, b) => b[1] - a[1] || a[0] - b[0])
|
|
497
|
+
.slice(0, 2)
|
|
498
|
+
.map(([idx, count]) => `${r.bindings[idx]?.name ?? `#${idx}`}(${count} facts)`)
|
|
499
|
+
.join(', ');
|
|
500
|
+
onWarn?.(`[reaching-defs] ${filePath}:${functionStartLine}: per-function ` +
|
|
501
|
+
`REACHING_DEF edge cap (${maxEdgesPerFunction}) reached — dropped ` +
|
|
502
|
+
`${dropped} of ${deduped.length} edges; top bindings: ${top}`);
|
|
503
|
+
break;
|
|
504
|
+
}
|
|
505
|
+
const binding = r.bindings[edge.bindingIdx];
|
|
506
|
+
const sourceId = basicBlockId(filePath, functionStartLine, functionStartColumn, edge.defBlock);
|
|
507
|
+
const targetId = basicBlockId(filePath, functionStartLine, functionStartColumn, edge.useBlock);
|
|
508
|
+
graph.addRelationship({
|
|
509
|
+
// Single function anchor — the two block ids share it, so templating
|
|
510
|
+
// it once halves the id size (ids are in-memory-only but ~4000 of
|
|
511
|
+
// them per capped function is real transient heap).
|
|
512
|
+
id: generateId('REACHING_DEF', `${filePath}:${functionStartLine}:${functionStartColumn}:` +
|
|
513
|
+
`${edge.defBlock}->${edge.useBlock}:${bindingKey(binding)}`),
|
|
514
|
+
type: 'REACHING_DEF',
|
|
515
|
+
sourceId,
|
|
516
|
+
targetId,
|
|
517
|
+
confidence: 1.0,
|
|
518
|
+
// FU-B-2: the source-level binding name (M0/S1 verdict — name FIRST so
|
|
519
|
+
// `pdg_query` flows stays queryable) PLUS a compact versioned annotation
|
|
520
|
+
// carrying the FULL ordered list of def/use source LINE pairs for this
|
|
521
|
+
// (block-pair, binding) group. For a self-edge (defBlock === useBlock)
|
|
522
|
+
// this captures the intra-block def@L→use@L' chain — including a
|
|
523
|
+
// SAME-BINDING reassignment chain (`acc@24->acc@25->acc@26`) — that the
|
|
524
|
+
// block-granular projection lost; the statement projection (pdg-impact.ts)
|
|
525
|
+
// walks the list forward to fixpoint to recover the coalesced block's
|
|
526
|
+
// interior statements.
|
|
527
|
+
reason: encodeReachingDefReasonPairs(binding.name, edge.pairs),
|
|
528
|
+
});
|
|
529
|
+
result.edges++;
|
|
530
|
+
emittedForFn++;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
return result;
|
|
534
|
+
}
|
|
535
|
+
/** Whether the POST_DOMINATE debug env flag is enabled (`1`/`true`). */
|
|
536
|
+
const postDominateDebugEnabled = () => {
|
|
537
|
+
const v = process.env[POST_DOMINATE_DEBUG_ENV];
|
|
538
|
+
return v === '1' || v?.toLowerCase() === 'true';
|
|
539
|
+
};
|
|
540
|
+
/**
|
|
541
|
+
* Compute control dependence per function and persist the bounded CDG
|
|
542
|
+
* projection (#2085 M5 U4). Mirrors {@link emitFileReachingDefs}: the pure
|
|
543
|
+
* {@link computeControlDependence} already dedups to (controller, dependent,
|
|
544
|
+
* label), so the per-function cap applies to deduped edges and overflow logs
|
|
545
|
+
* one unconditional `onWarn` naming the dropped count — no silent truncation
|
|
546
|
+
* (R6/R7). The branch label ('T'|'F') rides the `reason` column (KTD3),
|
|
547
|
+
* mirroring how CFG stores its edge kind.
|
|
548
|
+
*
|
|
549
|
+
* When {@link POST_DOMINATE_DEBUG_ENV} is set, also emits diagnostic
|
|
550
|
+
* `POST_DOMINATE` edges (block → its immediate post-dominator). These are NOT
|
|
551
|
+
* capped or counted against the CDG budget — they exist only for inspecting the
|
|
552
|
+
* post-dom tree and never appear in a normal run.
|
|
553
|
+
*/
|
|
554
|
+
export function emitFileCdg(graph, cfgs, maxEdgesPerFunction = DEFAULT_PDG_MAX_CDG_EDGES_PER_FUNCTION, onWarn) {
|
|
555
|
+
const result = {
|
|
556
|
+
edges: 0,
|
|
557
|
+
droppedEdges: 0,
|
|
558
|
+
cappedFunctions: 0,
|
|
559
|
+
postDominateEdges: 0,
|
|
560
|
+
skippedUnsoundFunctions: 0,
|
|
561
|
+
};
|
|
562
|
+
const cap = maxEdgesPerFunction > 0 ? maxEdgesPerFunction : Infinity;
|
|
563
|
+
const emitPostDom = postDominateDebugEnabled();
|
|
564
|
+
for (const cfg of cfgs) {
|
|
565
|
+
const { filePath, functionStartLine, functionStartColumn } = cfg;
|
|
566
|
+
// Synthetic-escape pass (#2197 U1): restore EXIT reverse-reachability for a
|
|
567
|
+
// genuine exit-unreachable CYCLE (an unconditional `goto`-cycle / infinite
|
|
568
|
+
// loop) so the post-dom / CDG pass runs instead of being withheld. A no-op
|
|
569
|
+
// (returns `cfg` unchanged) for terminating functions and properly-escaped
|
|
570
|
+
// loops — those stay byte-identical. The synthetic edges are ANALYSIS-ONLY:
|
|
571
|
+
// they live on the returned shallow clone, never on the persisted `cfg`, so
|
|
572
|
+
// CFG / REACHING_DEF and the byte-identical-off golden are unaffected. Both
|
|
573
|
+
// the gate below AND the post-dom / CDG passes must see the augmented view
|
|
574
|
+
// (KTD7 — the Ferrante walk re-reads `cfg.edges`).
|
|
575
|
+
const view = augmentForPostDom(cfg);
|
|
576
|
+
// Sound post-dominance requires EXIT reachable from every entry-reachable
|
|
577
|
+
// block (#2188 review). The synthetic-escape pass recovers genuine cycles;
|
|
578
|
+
// anything STILL unreachable after it is a residual non-cycle anomaly (a
|
|
579
|
+
// dangling/dead-end block, a branch-less trapping spin, or a construction
|
|
580
|
+
// error) — NOT something we bridge (that would mask the bug). Skip CDG for
|
|
581
|
+
// it and surface the skip. CFG and REACHING_DEF (emitted elsewhere,
|
|
582
|
+
// independent of post-dominance) are kept.
|
|
583
|
+
if (!isExitReachableFromAllBlocks(view)) {
|
|
584
|
+
result.skippedUnsoundFunctions++;
|
|
585
|
+
onWarn?.(`[cdg] ${filePath}:${functionStartLine}: EXIT not reachable from all ` +
|
|
586
|
+
`blocks — CDG skipped for this function (CFG/REACHING_DEF unaffected)`);
|
|
587
|
+
continue;
|
|
588
|
+
}
|
|
589
|
+
// Compute the post-dom tree once and feed it to the control-dependence
|
|
590
|
+
// pass (avoids recomputing it) and to the optional POST_DOMINATE emit. The
|
|
591
|
+
// CDG edges reference BLOCK INDICES, which are identical in `view` and `cfg`
|
|
592
|
+
// (the augmentation only appends edges), so persisting them keyed off the
|
|
593
|
+
// original block ids is correct.
|
|
594
|
+
const tree = computePostDominators(view);
|
|
595
|
+
// Bound the pre-dedup materialization (heap parity with REACHING_DEF). The
|
|
596
|
+
// fixed ceiling is a catastrophe backstop; the per-function edge cap below
|
|
597
|
+
// remains the reporting authority. A ceiling hit is surfaced, not silent.
|
|
598
|
+
const { edges: cdgEdges, truncated } = computeControlDependence(view, tree, DEFAULT_PDG_MAX_CDG_MATERIALIZATION_PER_FUNCTION);
|
|
599
|
+
if (truncated) {
|
|
600
|
+
onWarn?.(`[cdg] ${filePath}:${functionStartLine}: control-dependence materialization ` +
|
|
601
|
+
`ceiling (${DEFAULT_PDG_MAX_CDG_MATERIALIZATION_PER_FUNCTION}) reached — ` +
|
|
602
|
+
`edge counts for this function are a floor`);
|
|
603
|
+
}
|
|
604
|
+
let emittedForFn = 0;
|
|
605
|
+
for (const edge of cdgEdges) {
|
|
606
|
+
if (emittedForFn >= cap) {
|
|
607
|
+
const dropped = cdgEdges.length - emittedForFn;
|
|
608
|
+
result.droppedEdges += dropped;
|
|
609
|
+
result.cappedFunctions++;
|
|
610
|
+
onWarn?.(`[cdg] ${filePath}:${functionStartLine}: per-function CDG edge cap ` +
|
|
611
|
+
`(${maxEdgesPerFunction}) reached — dropped ${dropped} of ${cdgEdges.length} edges`);
|
|
612
|
+
break;
|
|
613
|
+
}
|
|
614
|
+
const sourceId = basicBlockId(filePath, functionStartLine, functionStartColumn, edge.controllerBlock);
|
|
615
|
+
const targetId = basicBlockId(filePath, functionStartLine, functionStartColumn, edge.dependentBlock);
|
|
616
|
+
graph.addRelationship({
|
|
617
|
+
id: generateId('CDG', `${filePath}:${functionStartLine}:${functionStartColumn}:` +
|
|
618
|
+
`${edge.controllerBlock}->${edge.dependentBlock}:${edge.label}`),
|
|
619
|
+
type: 'CDG',
|
|
620
|
+
sourceId,
|
|
621
|
+
targetId,
|
|
622
|
+
confidence: 1.0,
|
|
623
|
+
reason: edge.label, // 'T' | 'F' — queryable, mirrors CFG's kind-in-reason
|
|
624
|
+
});
|
|
625
|
+
result.edges++;
|
|
626
|
+
emittedForFn++;
|
|
627
|
+
}
|
|
628
|
+
if (emitPostDom) {
|
|
629
|
+
for (let b = 0; b < tree.ipdom.length; b++) {
|
|
630
|
+
const ip = tree.ipdom[b];
|
|
631
|
+
if (ip === NO_IPDOM)
|
|
632
|
+
continue;
|
|
633
|
+
graph.addRelationship({
|
|
634
|
+
id: generateId('POST_DOMINATE', `${filePath}:${functionStartLine}:${functionStartColumn}:${b}->${ip}`),
|
|
635
|
+
type: 'POST_DOMINATE',
|
|
636
|
+
sourceId: basicBlockId(filePath, functionStartLine, functionStartColumn, b),
|
|
637
|
+
targetId: basicBlockId(filePath, functionStartLine, functionStartColumn, ip),
|
|
638
|
+
confidence: 1.0,
|
|
639
|
+
reason: '',
|
|
640
|
+
});
|
|
641
|
+
result.postDominateEdges++;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
return result;
|
|
646
|
+
}
|