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.
Files changed (439) hide show
  1. package/README.md +3 -3
  2. package/dist/_shared/graph/types.d.ts +31 -1
  3. package/dist/_shared/graph/types.d.ts.map +1 -1
  4. package/dist/_shared/language-detection.d.ts.map +1 -1
  5. package/dist/_shared/language-detection.js +11 -1
  6. package/dist/_shared/language-detection.js.map +1 -1
  7. package/dist/_shared/lbug/schema-constants.d.ts +1 -1
  8. package/dist/_shared/lbug/schema-constants.d.ts.map +1 -1
  9. package/dist/_shared/lbug/schema-constants.js +6 -0
  10. package/dist/_shared/lbug/schema-constants.js.map +1 -1
  11. package/dist/_shared/scope-resolution/parsed-file.d.ts +21 -0
  12. package/dist/_shared/scope-resolution/parsed-file.d.ts.map +1 -1
  13. package/dist/_shared/scope-resolution/symbol-definition.d.ts +6 -0
  14. package/dist/_shared/scope-resolution/symbol-definition.d.ts.map +1 -1
  15. package/dist/cli/ai-context.d.ts +31 -1
  16. package/dist/cli/ai-context.js +18 -16
  17. package/dist/cli/analyze-config.js +11 -0
  18. package/dist/cli/analyze.d.ts +14 -0
  19. package/dist/cli/analyze.js +98 -0
  20. package/dist/cli/embedding-dims.d.ts +33 -0
  21. package/dist/cli/embedding-dims.js +39 -0
  22. package/dist/cli/eval-server.d.ts +8 -0
  23. package/dist/cli/eval-server.js +242 -2
  24. package/dist/cli/help-i18n.js +19 -0
  25. package/dist/cli/i18n/en.d.ts +15 -1
  26. package/dist/cli/i18n/en.js +15 -1
  27. package/dist/cli/i18n/resources.d.ts +29 -1
  28. package/dist/cli/i18n/zh-CN.d.ts +14 -0
  29. package/dist/cli/i18n/zh-CN.js +15 -1
  30. package/dist/cli/index.js +89 -1
  31. package/dist/cli/mcp.d.ts +6 -1
  32. package/dist/cli/mcp.js +26 -1
  33. package/dist/cli/optional-grammars.d.ts +6 -8
  34. package/dist/cli/optional-grammars.js +8 -11
  35. package/dist/cli/setup.d.ts +3 -1
  36. package/dist/cli/setup.js +67 -17
  37. package/dist/cli/skill-gen.js +2 -1
  38. package/dist/cli/tool.d.ts +19 -1
  39. package/dist/cli/tool.js +96 -3
  40. package/dist/core/embeddings/hf-env.d.ts +0 -66
  41. package/dist/core/embeddings/http-client.d.ts +7 -0
  42. package/dist/core/embeddings/http-client.js +5 -3
  43. package/dist/core/embeddings/server-mapping.d.ts +1 -1
  44. package/dist/core/embeddings/server-mapping.js +7 -3
  45. package/dist/core/graph/import-cycles.d.ts +10 -0
  46. package/dist/core/graph/import-cycles.js +103 -0
  47. package/dist/core/group/cross-impact.d.ts +2 -1
  48. package/dist/core/group/cross-impact.js +5 -1
  49. package/dist/core/group/extractors/grpc-patterns/proto.js +10 -6
  50. package/dist/core/group/extractors/http-patterns/kotlin.js +7 -7
  51. package/dist/core/group/extractors/http-route-extractor.d.ts +24 -0
  52. package/dist/core/group/extractors/http-route-extractor.js +22 -4
  53. package/dist/core/group/extractors/include-extractor.js +22 -13
  54. package/dist/core/group/sync.d.ts +27 -1
  55. package/dist/core/group/sync.js +0 -0
  56. package/dist/core/incremental/subgraph-extract.js +18 -1
  57. package/dist/core/ingestion/cfg/cfg-builder.d.ts +114 -0
  58. package/dist/core/ingestion/cfg/cfg-builder.js +196 -0
  59. package/dist/core/ingestion/cfg/collect.d.ts +51 -0
  60. package/dist/core/ingestion/cfg/collect.js +77 -0
  61. package/dist/core/ingestion/cfg/control-dependence.d.ts +58 -0
  62. package/dist/core/ingestion/cfg/control-dependence.js +182 -0
  63. package/dist/core/ingestion/cfg/control-flow-context.d.ts +106 -0
  64. package/dist/core/ingestion/cfg/control-flow-context.js +124 -0
  65. package/dist/core/ingestion/cfg/emit.d.ts +293 -0
  66. package/dist/core/ingestion/cfg/emit.js +646 -0
  67. package/dist/core/ingestion/cfg/post-dominators.d.ts +79 -0
  68. package/dist/core/ingestion/cfg/post-dominators.js +176 -0
  69. package/dist/core/ingestion/cfg/reaching-def-reason-codec.d.ts +106 -0
  70. package/dist/core/ingestion/cfg/reaching-def-reason-codec.js +133 -0
  71. package/dist/core/ingestion/cfg/reaching-defs-graph.d.ts +18 -0
  72. package/dist/core/ingestion/cfg/reaching-defs-graph.js +312 -0
  73. package/dist/core/ingestion/cfg/reaching-defs.d.ts +162 -0
  74. package/dist/core/ingestion/cfg/reaching-defs.js +785 -0
  75. package/dist/core/ingestion/cfg/synthetic-escape.d.ts +124 -0
  76. package/dist/core/ingestion/cfg/synthetic-escape.js +300 -0
  77. package/dist/core/ingestion/cfg/traversal-result.d.ts +20 -0
  78. package/dist/core/ingestion/cfg/traversal-result.js +2 -0
  79. package/dist/core/ingestion/cfg/types.d.ts +274 -0
  80. package/dist/core/ingestion/cfg/types.js +13 -0
  81. package/dist/core/ingestion/cfg/visitors/c-cpp-harvest.d.ts +111 -0
  82. package/dist/core/ingestion/cfg/visitors/c-cpp-harvest.js +541 -0
  83. package/dist/core/ingestion/cfg/visitors/c-cpp.d.ts +74 -0
  84. package/dist/core/ingestion/cfg/visitors/c-cpp.js +584 -0
  85. package/dist/core/ingestion/cfg/visitors/call-site-harvest.d.ts +195 -0
  86. package/dist/core/ingestion/cfg/visitors/call-site-harvest.js +350 -0
  87. package/dist/core/ingestion/cfg/visitors/csharp-harvest.d.ts +124 -0
  88. package/dist/core/ingestion/cfg/visitors/csharp-harvest.js +589 -0
  89. package/dist/core/ingestion/cfg/visitors/csharp.d.ts +87 -0
  90. package/dist/core/ingestion/cfg/visitors/csharp.js +867 -0
  91. package/dist/core/ingestion/cfg/visitors/dart-harvest.d.ts +278 -0
  92. package/dist/core/ingestion/cfg/visitors/dart-harvest.js +870 -0
  93. package/dist/core/ingestion/cfg/visitors/dart.d.ts +119 -0
  94. package/dist/core/ingestion/cfg/visitors/dart.js +836 -0
  95. package/dist/core/ingestion/cfg/visitors/go-harvest.d.ts +160 -0
  96. package/dist/core/ingestion/cfg/visitors/go-harvest.js +621 -0
  97. package/dist/core/ingestion/cfg/visitors/go.d.ts +108 -0
  98. package/dist/core/ingestion/cfg/visitors/go.js +638 -0
  99. package/dist/core/ingestion/cfg/visitors/java-harvest.d.ts +108 -0
  100. package/dist/core/ingestion/cfg/visitors/java-harvest.js +513 -0
  101. package/dist/core/ingestion/cfg/visitors/java.d.ts +101 -0
  102. package/dist/core/ingestion/cfg/visitors/java.js +812 -0
  103. package/dist/core/ingestion/cfg/visitors/kotlin-harvest.d.ts +253 -0
  104. package/dist/core/ingestion/cfg/visitors/kotlin-harvest.js +719 -0
  105. package/dist/core/ingestion/cfg/visitors/kotlin.d.ts +121 -0
  106. package/dist/core/ingestion/cfg/visitors/kotlin.js +809 -0
  107. package/dist/core/ingestion/cfg/visitors/php-harvest.d.ts +172 -0
  108. package/dist/core/ingestion/cfg/visitors/php-harvest.js +626 -0
  109. package/dist/core/ingestion/cfg/visitors/php.d.ts +96 -0
  110. package/dist/core/ingestion/cfg/visitors/php.js +721 -0
  111. package/dist/core/ingestion/cfg/visitors/python-harvest.d.ts +212 -0
  112. package/dist/core/ingestion/cfg/visitors/python-harvest.js +739 -0
  113. package/dist/core/ingestion/cfg/visitors/python.d.ts +103 -0
  114. package/dist/core/ingestion/cfg/visitors/python.js +558 -0
  115. package/dist/core/ingestion/cfg/visitors/ruby-harvest.d.ts +223 -0
  116. package/dist/core/ingestion/cfg/visitors/ruby-harvest.js +587 -0
  117. package/dist/core/ingestion/cfg/visitors/ruby.d.ts +112 -0
  118. package/dist/core/ingestion/cfg/visitors/ruby.js +756 -0
  119. package/dist/core/ingestion/cfg/visitors/rust-harvest.d.ts +324 -0
  120. package/dist/core/ingestion/cfg/visitors/rust-harvest.js +873 -0
  121. package/dist/core/ingestion/cfg/visitors/rust.d.ts +95 -0
  122. package/dist/core/ingestion/cfg/visitors/rust.js +558 -0
  123. package/dist/core/ingestion/cfg/visitors/scope-tree-harvest.d.ts +87 -0
  124. package/dist/core/ingestion/cfg/visitors/scope-tree-harvest.js +116 -0
  125. package/dist/core/ingestion/cfg/visitors/swift-harvest.d.ts +255 -0
  126. package/dist/core/ingestion/cfg/visitors/swift-harvest.js +679 -0
  127. package/dist/core/ingestion/cfg/visitors/swift.d.ts +116 -0
  128. package/dist/core/ingestion/cfg/visitors/swift.js +787 -0
  129. package/dist/core/ingestion/cfg/visitors/typescript-harvest.d.ts +149 -0
  130. package/dist/core/ingestion/cfg/visitors/typescript-harvest.js +1056 -0
  131. package/dist/core/ingestion/cfg/visitors/typescript.d.ts +66 -0
  132. package/dist/core/ingestion/cfg/visitors/typescript.js +583 -0
  133. package/dist/core/ingestion/import-resolvers/utils.js +2 -0
  134. package/dist/core/ingestion/language-provider.d.ts +23 -2
  135. package/dist/core/ingestion/languages/c-cpp.js +17 -5
  136. package/dist/core/ingestion/languages/cobol.js +4 -0
  137. package/dist/core/ingestion/languages/cpp/arity-metadata.js +41 -2
  138. package/dist/core/ingestion/languages/cpp/captures.js +50 -1
  139. package/dist/core/ingestion/languages/cpp/conversion-rank.d.ts +2 -0
  140. package/dist/core/ingestion/languages/cpp/conversion-rank.js +89 -0
  141. package/dist/core/ingestion/languages/cpp/header-scan.d.ts +1 -1
  142. package/dist/core/ingestion/languages/cpp/header-scan.js +2 -2
  143. package/dist/core/ingestion/languages/cpp/inline-namespaces.js +7 -2
  144. package/dist/core/ingestion/languages/cpp/member-lookup.js +2 -1
  145. package/dist/core/ingestion/languages/cpp/query.js +23 -0
  146. package/dist/core/ingestion/languages/cpp/scope-resolver.js +2 -1
  147. package/dist/core/ingestion/languages/csharp.js +2 -0
  148. package/dist/core/ingestion/languages/dart.js +2 -0
  149. package/dist/core/ingestion/languages/go.js +2 -0
  150. package/dist/core/ingestion/languages/java.js +3 -0
  151. package/dist/core/ingestion/languages/kotlin/query.js +3 -2
  152. package/dist/core/ingestion/languages/kotlin.js +8 -1
  153. package/dist/core/ingestion/languages/php.js +2 -0
  154. package/dist/core/ingestion/languages/python.js +2 -0
  155. package/dist/core/ingestion/languages/ruby.js +2 -0
  156. package/dist/core/ingestion/languages/rust.js +2 -0
  157. package/dist/core/ingestion/languages/swift.js +2 -0
  158. package/dist/core/ingestion/languages/typescript.js +5 -0
  159. package/dist/core/ingestion/languages/vue.js +6 -0
  160. package/dist/core/ingestion/method-extractors/configs/c-cpp.js +6 -13
  161. package/dist/core/ingestion/method-extractors/generic.js +1 -0
  162. package/dist/core/ingestion/method-types.d.ts +2 -0
  163. package/dist/core/ingestion/model/symbol-table.d.ts +1 -0
  164. package/dist/core/ingestion/model/symbol-table.js +1 -0
  165. package/dist/core/ingestion/mro-processor.js +109 -0
  166. package/dist/core/ingestion/parsing-processor.js +44 -0
  167. package/dist/core/ingestion/pipeline-phases/call-summaries.d.ts +26 -0
  168. package/dist/core/ingestion/pipeline-phases/call-summaries.js +46 -0
  169. package/dist/core/ingestion/pipeline-phases/index.d.ts +2 -0
  170. package/dist/core/ingestion/pipeline-phases/index.js +2 -0
  171. package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +0 -21
  172. package/dist/core/ingestion/pipeline-phases/parse-impl.js +18 -1
  173. package/dist/core/ingestion/pipeline-phases/routes.d.ts +10 -0
  174. package/dist/core/ingestion/pipeline-phases/routes.js +30 -1
  175. package/dist/core/ingestion/pipeline-phases/taint-summaries.d.ts +30 -0
  176. package/dist/core/ingestion/pipeline-phases/taint-summaries.js +81 -0
  177. package/dist/core/ingestion/pipeline.d.ts +92 -10
  178. package/dist/core/ingestion/pipeline.js +11 -2
  179. package/dist/core/ingestion/scope-extractor.js +12 -2
  180. package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +9 -0
  181. package/dist/core/ingestion/scope-resolution/graph-bridge/callee-id-sink.d.ts +75 -0
  182. package/dist/core/ingestion/scope-resolution/graph-bridge/callee-id-sink.js +67 -0
  183. package/dist/core/ingestion/scope-resolution/graph-bridge/edges.d.ts +12 -2
  184. package/dist/core/ingestion/scope-resolution/graph-bridge/edges.js +14 -2
  185. package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.d.ts +6 -1
  186. package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.js +13 -1
  187. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +9 -1
  188. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +28 -2
  189. package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.d.ts +4 -0
  190. package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.js +9 -0
  191. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.d.ts +6 -1
  192. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +174 -40
  193. package/dist/core/ingestion/scope-resolution/pipeline/phase.d.ts +24 -0
  194. package/dist/core/ingestion/scope-resolution/pipeline/phase.js +276 -190
  195. package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.js +43 -3
  196. package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +75 -0
  197. package/dist/core/ingestion/scope-resolution/pipeline/run.js +349 -1
  198. package/dist/core/ingestion/scope-resolution/resolution-outcome.d.ts +1 -1
  199. package/dist/core/ingestion/taint/call-summary-codec.d.ts +75 -0
  200. package/dist/core/ingestion/taint/call-summary-codec.js +128 -0
  201. package/dist/core/ingestion/taint/call-summary-emit.d.ts +43 -0
  202. package/dist/core/ingestion/taint/call-summary-emit.js +70 -0
  203. package/dist/core/ingestion/taint/call-summary-harvest.d.ts +77 -0
  204. package/dist/core/ingestion/taint/call-summary-harvest.js +179 -0
  205. package/dist/core/ingestion/taint/call-summary-model.d.ts +47 -0
  206. package/dist/core/ingestion/taint/call-summary-model.js +25 -0
  207. package/dist/core/ingestion/taint/emit.d.ts +125 -0
  208. package/dist/core/ingestion/taint/emit.js +208 -0
  209. package/dist/core/ingestion/taint/interproc-emit.d.ts +50 -0
  210. package/dist/core/ingestion/taint/interproc-emit.js +88 -0
  211. package/dist/core/ingestion/taint/interproc-solver.d.ts +110 -0
  212. package/dist/core/ingestion/taint/interproc-solver.js +312 -0
  213. package/dist/core/ingestion/taint/match.d.ts +153 -0
  214. package/dist/core/ingestion/taint/match.js +278 -0
  215. package/dist/core/ingestion/taint/path-codec.d.ts +134 -0
  216. package/dist/core/ingestion/taint/path-codec.js +190 -0
  217. package/dist/core/ingestion/taint/propagate.d.ts +216 -0
  218. package/dist/core/ingestion/taint/propagate.js +659 -0
  219. package/dist/core/ingestion/taint/site-safety.d.ts +29 -0
  220. package/dist/core/ingestion/taint/site-safety.js +98 -0
  221. package/dist/core/ingestion/taint/source-sink-config.d.ts +107 -23
  222. package/dist/core/ingestion/taint/source-sink-config.js +35 -12
  223. package/dist/core/ingestion/taint/source-sink-registry.d.ts +6 -4
  224. package/dist/core/ingestion/taint/source-sink-registry.js +6 -4
  225. package/dist/core/ingestion/taint/summary-harvest-driver.d.ts +80 -0
  226. package/dist/core/ingestion/taint/summary-harvest-driver.js +179 -0
  227. package/dist/core/ingestion/taint/summary-harvest.d.ts +89 -0
  228. package/dist/core/ingestion/taint/summary-harvest.js +537 -0
  229. package/dist/core/ingestion/taint/summary-model.d.ts +200 -0
  230. package/dist/core/ingestion/taint/summary-model.js +86 -0
  231. package/dist/core/ingestion/taint/typescript-model.d.ts +38 -0
  232. package/dist/core/ingestion/taint/typescript-model.js +102 -0
  233. package/dist/core/ingestion/type-extractors/c-cpp.js +21 -0
  234. package/dist/core/ingestion/utils/env.d.ts +7 -0
  235. package/dist/core/ingestion/utils/env.js +12 -0
  236. package/dist/core/ingestion/utils/method-props.js +1 -0
  237. package/dist/core/ingestion/workers/clone-safety.d.ts +109 -0
  238. package/dist/core/ingestion/workers/clone-safety.js +465 -0
  239. package/dist/core/ingestion/workers/parse-worker.d.ts +23 -0
  240. package/dist/core/ingestion/workers/parse-worker.js +81 -53
  241. package/dist/core/ingestion/workers/post-result.d.ts +22 -0
  242. package/dist/core/ingestion/workers/post-result.js +87 -0
  243. package/dist/core/ingestion/workers/result-merge.d.ts +20 -0
  244. package/dist/core/ingestion/workers/result-merge.js +43 -0
  245. package/dist/core/ingestion/workers/worker-pool.d.ts +15 -40
  246. package/dist/core/ingestion/workers/worker-pool.js +43 -16
  247. package/dist/core/lbug/csv-generator.d.ts +51 -3
  248. package/dist/core/lbug/csv-generator.js +369 -255
  249. package/dist/core/lbug/lbug-adapter.d.ts +58 -18
  250. package/dist/core/lbug/lbug-adapter.js +346 -84
  251. package/dist/core/lbug/lbug-config.d.ts +19 -0
  252. package/dist/core/lbug/lbug-config.js +29 -0
  253. package/dist/core/lbug/pdg-emit-sink.d.ts +127 -0
  254. package/dist/core/lbug/pdg-emit-sink.js +358 -0
  255. package/dist/core/lbug/pool-adapter.d.ts +35 -0
  256. package/dist/core/lbug/pool-adapter.js +90 -1
  257. package/dist/core/lbug/rel-pair-routing.d.ts +54 -0
  258. package/dist/core/lbug/rel-pair-routing.js +140 -0
  259. package/dist/core/lbug/schema.d.ts +2 -2
  260. package/dist/core/lbug/schema.js +3 -0
  261. package/dist/core/logger.d.ts +0 -5
  262. package/dist/core/run-analyze.d.ts +96 -0
  263. package/dist/core/run-analyze.js +207 -3
  264. package/dist/core/tree-sitter/parser-loader.js +5 -4
  265. package/dist/core/tree-sitter/safe-parse.d.ts +0 -5
  266. package/dist/core/tree-sitter/vendored-grammars.d.ts +39 -0
  267. package/dist/core/tree-sitter/vendored-grammars.js +57 -0
  268. package/dist/core/wiki/generator.js +3 -1
  269. package/dist/core/wiki/graph-queries.d.ts +6 -0
  270. package/dist/core/wiki/graph-queries.js +9 -1
  271. package/dist/mcp/http-transport.d.ts +134 -0
  272. package/dist/mcp/http-transport.js +503 -0
  273. package/dist/mcp/local/local-backend.d.ts +110 -24
  274. package/dist/mcp/local/local-backend.js +1225 -19
  275. package/dist/mcp/local/pdg-impact.d.ts +384 -0
  276. package/dist/mcp/local/pdg-impact.js +1972 -0
  277. package/dist/mcp/resources.js +13 -1
  278. package/dist/mcp/tools.d.ts +12 -0
  279. package/dist/mcp/tools.js +214 -7
  280. package/dist/server/analyze-launch.d.ts +29 -0
  281. package/dist/server/analyze-launch.js +137 -0
  282. package/dist/server/analyze-upload.d.ts +33 -0
  283. package/dist/server/analyze-upload.js +123 -0
  284. package/dist/server/analyze-worker-ipc.d.ts +58 -0
  285. package/dist/server/analyze-worker-ipc.js +16 -0
  286. package/dist/server/analyze-worker.d.ts +17 -1
  287. package/dist/server/analyze-worker.js +7 -1
  288. package/dist/server/api.d.ts +17 -0
  289. package/dist/server/api.js +132 -168
  290. package/dist/server/git-clone.d.ts +43 -2
  291. package/dist/server/git-clone.js +211 -16
  292. package/dist/server/mcp-http.d.ts +5 -6
  293. package/dist/server/mcp-http.js +9 -82
  294. package/dist/server/middleware.d.ts +40 -0
  295. package/dist/server/middleware.js +92 -0
  296. package/dist/server/private-ip.d.ts +1 -0
  297. package/dist/server/private-ip.js +15 -0
  298. package/dist/server/upload-ingest.d.ts +56 -0
  299. package/dist/server/upload-ingest.js +276 -0
  300. package/dist/server/upload-paths.d.ts +37 -0
  301. package/dist/server/upload-paths.js +57 -0
  302. package/dist/server/upload-sweep.d.ts +20 -0
  303. package/dist/server/upload-sweep.js +57 -0
  304. package/dist/storage/git.js +5 -8
  305. package/dist/storage/parse-cache.d.ts +22 -1
  306. package/dist/storage/parse-cache.js +44 -10
  307. package/dist/storage/repo-manager.d.ts +121 -10
  308. package/dist/storage/repo-manager.js +43 -5
  309. package/dist/types/pipeline.d.ts +9 -0
  310. package/hooks/antigravity/gitnexus-antigravity-hook.cjs +110 -10
  311. package/hooks/claude/gitnexus-hook.cjs +110 -13
  312. package/hooks/claude/hook-db-lock-probe.cjs +513 -26
  313. package/package.json +8 -2
  314. package/scripts/assert-publish-grammar-coverage.cjs +31 -0
  315. package/scripts/build-tree-sitter-grammars.cjs +16 -10
  316. package/scripts/cross-platform-tests.ts +1 -0
  317. package/skills/gitnexus-debugging.md +16 -4
  318. package/skills/gitnexus-exploring.md +3 -3
  319. package/skills/gitnexus-guide.md +33 -0
  320. package/skills/gitnexus-pdg-query.md +89 -0
  321. package/skills/gitnexus-refactoring.md +1 -1
  322. package/skills/gitnexus-taint-analysis.md +178 -0
  323. package/vendor/tree-sitter-c/package.json +1 -1
  324. package/vendor/tree-sitter-dart/package.json +1 -1
  325. package/vendor/tree-sitter-kotlin/package.json +1 -1
  326. package/vendor/tree-sitter-proto/package.json +1 -1
  327. package/vendor/tree-sitter-swift/package.json +1 -1
  328. package/web/assets/{agent-CKPMqImC.js → agent-D3Xd7zZE.js} +123 -123
  329. package/web/assets/architecture-7EHR7CIX-6QZW5X65-DxJw65fT.js +1 -0
  330. package/web/assets/architectureDiagram-UL44E2DR-ChH9xW-N.js +36 -0
  331. package/web/assets/{blockDiagram-7IZFK4PR-B4niSLvR.js → blockDiagram-7IZFK4PR-BkWhpzWB.js} +1 -1
  332. package/web/assets/{c4Diagram-Y2BXMSZH-DIzEXalR.js → c4Diagram-Y2BXMSZH-DtBvpzWD.js} +1 -1
  333. package/web/assets/{chunk-3SSMPTDK-BzGeeheI.js → chunk-3SSMPTDK-B4_etUhr.js} +2 -2
  334. package/web/assets/{chunk-5IMINLNL-CnTLsXIV.js → chunk-5IMINLNL-DOdTeQri.js} +1 -1
  335. package/web/assets/{chunk-5VCL7Z4A-w1BCx98Z.js → chunk-5VCL7Z4A-CQ-2dYWj.js} +1 -1
  336. package/web/assets/chunk-6764PJDD-DpIS7hxD.js +1 -0
  337. package/web/assets/{chunk-67TQ5CYL-CChC2cAv.js → chunk-67TQ5CYL-4sNPJU1q.js} +3 -3
  338. package/web/assets/{chunk-7W6UQGC5-CgFyfZ6k.js → chunk-7W6UQGC5-KyEG0HQg.js} +1 -1
  339. package/web/assets/{chunk-AZZRMDJM-5E9ARdDQ.js → chunk-AZZRMDJM-y-xJBuMz.js} +1 -1
  340. package/web/assets/{chunk-INKRHTLW-ELpfuk9h.js → chunk-INKRHTLW-Nu4ri9P-.js} +1 -1
  341. package/web/assets/{chunk-JQRUD6KW-Dcq_Qnyy.js → chunk-JQRUD6KW-Dbva2Z17.js} +1 -1
  342. package/web/assets/{chunk-KGFNY3KK-W-VR57xb.js → chunk-KGFNY3KK-JGMJvKCK.js} +1 -1
  343. package/web/assets/chunk-KGYTTC2M-CWC_c3H9.js +161 -0
  344. package/web/assets/{chunk-KNLZD3CH-BuZBOcso.js → chunk-KNLZD3CH-BGlP6X9R.js} +1 -1
  345. package/web/assets/{chunk-KRXBNO2N-DH2NPM7I.js → chunk-KRXBNO2N-BW5o0KUN.js} +1 -1
  346. package/web/assets/{chunk-LCXTWHL2-Dfmux4m1.js → chunk-LCXTWHL2-zoomRtWX.js} +1 -1
  347. package/web/assets/{chunk-LII3EMHJ-3X33tCpU.js → chunk-LII3EMHJ-D-jm-dLa.js} +1 -1
  348. package/web/assets/{chunk-QA3QBVWF-D5Ha3GgB.js → chunk-QA3QBVWF-BgRW3SXd.js} +1 -1
  349. package/web/assets/{chunk-RG4AUYOV-CAkzcoRj.js → chunk-RG4AUYOV-DD0B2N2B.js} +1 -1
  350. package/web/assets/{chunk-T2UQINTJ-kGYG7ppP.js → chunk-T2UQINTJ-DSBqkzH7.js} +1 -1
  351. package/web/assets/chunk-T5OCTHI4-DdZvN-9i.js +1 -0
  352. package/web/assets/{chunk-UY5QBCOK-Du-c56gs.js → chunk-UY5QBCOK-DyF0vyGd.js} +1 -1
  353. package/web/assets/{chunk-W44A43WB-lrCpuupA.js → chunk-W44A43WB-JqMLIpR0.js} +1 -1
  354. package/web/assets/{chunk-ZXARS5L4-CSZRiyOf.js → chunk-ZXARS5L4-CxN8oiwI.js} +1 -1
  355. package/web/assets/classDiagram-KGZ6W3CR-DJm9BgF-.js +1 -0
  356. package/web/assets/classDiagram-v2-72OJOZXJ-DJm9BgF-.js +1 -0
  357. package/web/assets/{context-builder-_HS0v2ma.js → context-builder-BHiFUA8O.js} +10 -2
  358. package/web/assets/{cose-bilkent-UX7MHV2Q-BHv2bbtl.js → cose-bilkent-UX7MHV2Q-D71wNYRJ.js} +1 -1
  359. package/web/assets/dagre-ND4H6XIP-BIkxmhcu.js +4 -0
  360. package/web/assets/diagram-3NCE3AQN-BTa6iH0T.js +43 -0
  361. package/web/assets/diagram-GF46GFSD-DZDV0vBj.js +24 -0
  362. package/web/assets/{diagram-HNR7UZ2L-fUWreZzx.js → diagram-HNR7UZ2L-DFCfd5LI.js} +2 -2
  363. package/web/assets/diagram-QXG6HAR7-BzM--Jj0.js +24 -0
  364. package/web/assets/diagram-WEQXMOUZ-DFFwz0k4.js +10 -0
  365. package/web/assets/{erDiagram-L5TCEMPS-QN2eEP1e.js → erDiagram-L5TCEMPS-CfHMJJqo.js} +1 -1
  366. package/web/assets/eventmodeling-FCH6USID-MREXMVOE-BQm9QzEa.js +1 -0
  367. package/web/assets/{flowDiagram-H6V6AXG4-CXbXImlN.js → flowDiagram-H6V6AXG4-Be805QiJ.js} +3 -3
  368. package/web/assets/{ganttDiagram-JCBTUEKG-BHQssTz6.js → ganttDiagram-JCBTUEKG-D3iL2Aet.js} +1 -1
  369. package/web/assets/gitGraph-WXDBUCRP-R675I2BI-Cr_Bm2Nb.js +1 -0
  370. package/web/assets/gitGraphDiagram-S2ZK5IYY-CPIDgxGm.js +106 -0
  371. package/web/assets/index-BvYev1UJ.js +635 -0
  372. package/web/assets/index-_lgn7hs5.css +2 -0
  373. package/web/assets/info-J43DQDTF-KCYPFFUO-Cs4p2oyk.js +1 -0
  374. package/web/assets/infoDiagram-3YFTVSEB-Y7BKSoke.js +2 -0
  375. package/web/assets/{ishikawaDiagram-BNXS4ZKH-6LKOvBfp.js → ishikawaDiagram-BNXS4ZKH-CT4SHFuI.js} +1 -1
  376. package/web/assets/{journeyDiagram-M6C3CM3L-DNPYCpvq.js → journeyDiagram-M6C3CM3L-CvArszLo.js} +1 -1
  377. package/web/assets/{kanban-definition-75IXJCU3-DgDi9oJT.js → kanban-definition-75IXJCU3-C6yDxr7N.js} +3 -3
  378. package/web/assets/{katex-K3KEBU37-CrVLz8l7.js → katex-K3KEBU37-CTc5BslQ.js} +1 -1
  379. package/web/assets/{mindmap-definition-2TDM6QVE-CwR5sBB-.js → mindmap-definition-2TDM6QVE-BOc2WEne.js} +1 -1
  380. package/web/assets/packet-YPE3B663-LP52Z2RK-CKCrztD2.js +1 -0
  381. package/web/assets/pie-LRSECV5Y-TCRJHUBD-BB7pHqoD.js +1 -0
  382. package/web/assets/{pieDiagram-CU6KROY3-By8g6f6B.js → pieDiagram-CU6KROY3-Cvc0f_Pd.js} +1 -1
  383. package/web/assets/{quadrantDiagram-VICAPDV7-bAm6Hi_5.js → quadrantDiagram-VICAPDV7-yXSZ2lLz.js} +1 -1
  384. package/web/assets/radar-GUYGQ44K-RDLRG3WG-DhoTOcuK.js +1 -0
  385. package/web/assets/{requirementDiagram-JXO7QTGE-7oDcJ1_J.js → requirementDiagram-JXO7QTGE-BRcZvHs4.js} +1 -1
  386. package/web/assets/{sankeyDiagram-URQDO5SZ-LFJz53u5.js → sankeyDiagram-URQDO5SZ-1qEjlFIB.js} +1 -1
  387. package/web/assets/{sequenceDiagram-VS2MUI6T-FySeKCUy.js → sequenceDiagram-VS2MUI6T-PcSCuvrB.js} +3 -3
  388. package/web/assets/stateDiagram-7D4R322I-D6qYSV1E.js +1 -0
  389. package/web/assets/stateDiagram-v2-36443NZ5-Bg-ImOSX.js +1 -0
  390. package/web/assets/{timeline-definition-O6YCAMPW-Ds2CnVZK.js → timeline-definition-O6YCAMPW-QWkh01xw.js} +1 -1
  391. package/web/assets/treeView-BLDUP644-QA4HXRO3-ODehyGKL.js +1 -0
  392. package/web/assets/treemap-LRROVOQU-LLAWBHMP-BfpgC-7o.js +1 -0
  393. package/web/assets/{vennDiagram-MWXL3ELB-DUIEwXWp.js → vennDiagram-MWXL3ELB-ak4DfwuM.js} +3 -3
  394. package/web/assets/wardley-L42UT6IY-5TKZOOLJ-CNeL6VHE.js +1 -0
  395. package/web/assets/{wardleyDiagram-CUQ6CDDI-DEiFPQih.js → wardleyDiagram-CUQ6CDDI-CTzR4Loq.js} +1 -1
  396. package/web/assets/{xychartDiagram-N2JHSOCM-BUbayhST.js → xychartDiagram-N2JHSOCM-DA1BEaQz.js} +1 -1
  397. package/web/index.html +13 -15
  398. package/scripts/materialize-vendor-grammars.cjs +0 -97
  399. package/web/assets/architecture-7EHR7CIX-6QZW5X65-BETj2x4g.js +0 -1
  400. package/web/assets/architectureDiagram-UL44E2DR-D1EXI0zA.js +0 -36
  401. package/web/assets/chunk-2T2R6R2M-n6s9JZqv.js +0 -4
  402. package/web/assets/chunk-2UTLFMKG-CMBB1TMN.js +0 -1
  403. package/web/assets/chunk-4R4BOZG6-mKhL59ul.js +0 -159
  404. package/web/assets/chunk-6764PJDD-ChwMM2z4.js +0 -1
  405. package/web/assets/chunk-7J6CGLKN-DT-b53FT.js +0 -10
  406. package/web/assets/chunk-C62D2QBJ-CDAWj26E.js +0 -1
  407. package/web/assets/chunk-CEXFNPSA-D68Tk6ls.js +0 -1
  408. package/web/assets/chunk-CilyBKbf.js +0 -1
  409. package/web/assets/chunk-J5EP6P6S-DxWW0yvu.js +0 -1
  410. package/web/assets/chunk-KGYTTC2M-C6PHeuay.js +0 -1
  411. package/web/assets/chunk-RERM46MO-DQNbXtfw.js +0 -1
  412. package/web/assets/chunk-RKZBBQEN-BmTPLSyv.js +0 -1
  413. package/web/assets/chunk-RLI5ZMPA-DExu2DOK.js +0 -1
  414. package/web/assets/chunk-T5OCTHI4--9wWpVws.js +0 -1
  415. package/web/assets/chunk-UP6H54XL-DsKdC6jC.js +0 -1
  416. package/web/assets/chunk-UXSXWOXI-DR81EqLr.js +0 -1
  417. package/web/assets/classDiagram-KGZ6W3CR-Bv93af_b.js +0 -1
  418. package/web/assets/classDiagram-v2-72OJOZXJ-CLfEqHUa.js +0 -1
  419. package/web/assets/dagre-ND4H6XIP-DsIr9MOv.js +0 -4
  420. package/web/assets/diagram-3NCE3AQN-BqAtKUpW.js +0 -43
  421. package/web/assets/diagram-GF46GFSD-CaBG6n6o.js +0 -24
  422. package/web/assets/diagram-QXG6HAR7-CZ-O3rcV.js +0 -24
  423. package/web/assets/diagram-WEQXMOUZ-P4lSL4GH.js +0 -10
  424. package/web/assets/eventmodeling-FCH6USID-MREXMVOE-B3myHUst.js +0 -1
  425. package/web/assets/gitGraph-WXDBUCRP-R675I2BI-DF3rtf4A.js +0 -1
  426. package/web/assets/gitGraphDiagram-S2ZK5IYY-CCwVLT__.js +0 -106
  427. package/web/assets/index-BKWA-m7o.css +0 -2
  428. package/web/assets/index-CG6q8eTs.js +0 -626
  429. package/web/assets/info-J43DQDTF-KCYPFFUO-R4XV_akJ.js +0 -1
  430. package/web/assets/infoDiagram-3YFTVSEB-DmLICZx1.js +0 -2
  431. package/web/assets/packet-YPE3B663-LP52Z2RK-CgQPdNZa.js +0 -1
  432. package/web/assets/pie-LRSECV5Y-TCRJHUBD-BzWOwc_c.js +0 -1
  433. package/web/assets/radar-GUYGQ44K-RDLRG3WG-B7KG0FN7.js +0 -1
  434. package/web/assets/stateDiagram-7D4R322I-CB2nABwH.js +0 -1
  435. package/web/assets/stateDiagram-v2-36443NZ5-COBGd2RL.js +0 -1
  436. package/web/assets/treeView-BLDUP644-QA4HXRO3-CRSxEwLO.js +0 -1
  437. package/web/assets/treemap-LRROVOQU-LLAWBHMP-a0PfKoVn.js +0 -1
  438. package/web/assets/wardley-L42UT6IY-5TKZOOLJ-BKQ6e5mC.js +0 -1
  439. /package/web/assets/{chunk-AQ6EADP3-CCFje6lL.js → chunk-AQ6EADP3-CZhslHi-.js} +0 -0
@@ -0,0 +1,812 @@
1
+ import { CfgBuilder } from '../cfg-builder.js';
2
+ import { ControlFlowContext, drainFinalizerPending, wireJumpThroughFinalizers, } from '../control-flow-context.js';
3
+ import { JavaHarvester } from './java-harvest.js';
4
+ /** Java node types that own a CFG-bearing function body. */
5
+ const JAVA_FUNCTION_TYPES = new Set([
6
+ 'method_declaration',
7
+ 'constructor_declaration',
8
+ 'compact_constructor_declaration',
9
+ 'lambda_expression',
10
+ ]);
11
+ /** Statement node types that break a basic block (everything else coalesces). */
12
+ const CONTROL_FLOW_TYPES = new Set([
13
+ 'if_statement',
14
+ 'while_statement',
15
+ 'do_statement',
16
+ 'for_statement',
17
+ 'enhanced_for_statement',
18
+ 'switch_expression',
19
+ 'try_statement',
20
+ 'try_with_resources_statement',
21
+ 'synchronized_statement',
22
+ 'return_statement',
23
+ 'break_statement',
24
+ 'continue_statement',
25
+ 'throw_statement',
26
+ 'labeled_statement',
27
+ 'yield_statement',
28
+ 'block',
29
+ ]);
30
+ /** Comment node types tree-sitter-java surfaces (NOT `comment`). */
31
+ const COMMENT_TYPES = new Set(['line_comment', 'block_comment']);
32
+ const startLineOf = (n) => n.startPosition.row + 1;
33
+ const endLineOf = (n) => n.endPosition.row + 1;
34
+ const isComment = (n) => COMMENT_TYPES.has(n.type);
35
+ /**
36
+ * Per-function Java walk state. One instance per function so the
37
+ * {@link ControlFlowContext}, exception-handler stack, and labeled-block frame
38
+ * bookkeeping are scoped to that function and never leak across functions.
39
+ */
40
+ class JavaCfgWalk {
41
+ builder;
42
+ harvest;
43
+ cfc = new ControlFlowContext();
44
+ /** Stack of exception-handler entry blocks (catch/finally) a `throw` jumps to. */
45
+ handlers = [];
46
+ /** Label(s) pending attachment to the NEXT pushed loop/switch frame. */
47
+ pendingLabels = [];
48
+ constructor(builder, harvest) {
49
+ this.builder = builder;
50
+ this.harvest = harvest;
51
+ }
52
+ /** Statements of a block node, ignoring comments. */
53
+ statementsOf(block) {
54
+ return block.namedChildren.filter((c) => !isComment(c));
55
+ }
56
+ /** The `body` block of a node (field, or the first `block` child). */
57
+ bodyBlockOf(node) {
58
+ return node.childForFieldName('body') ?? node.namedChildren.find((c) => c.type === 'block');
59
+ }
60
+ /** Strip a `parenthesized_expression` wrapper (Java `if`/`while` conditions). */
61
+ unwrapParen(node) {
62
+ if (node.type === 'parenthesized_expression') {
63
+ const inner = node.namedChildren.find((c) => !isComment(c));
64
+ if (inner)
65
+ return inner;
66
+ }
67
+ return node;
68
+ }
69
+ /** Visit a body that may be a `block` or a single statement. */
70
+ visitBody(node) {
71
+ return this.builder.withNesting(() => {
72
+ if (!node)
73
+ return null;
74
+ if (node.type === 'block')
75
+ return this.visitSeq(this.statementsOf(node));
76
+ return this.visitStmt(node);
77
+ });
78
+ }
79
+ /** Wire a sequence of statements, coalescing straight-line runs into blocks. */
80
+ visitSeq(stmts) {
81
+ return this.builder.withNesting(() => {
82
+ let entry;
83
+ let dangling = [];
84
+ let openSimple;
85
+ for (const stmt of stmts) {
86
+ if (this.breaksBlock(stmt)) {
87
+ openSimple = undefined; // close any open straight-line block
88
+ const res = this.visitStmt(stmt);
89
+ if (res === null)
90
+ continue; // transparent (empty nested block)
91
+ if (entry === undefined)
92
+ entry = res.entry;
93
+ else
94
+ this.builder.connect(dangling, res.entry, 'seq');
95
+ dangling = [...res.exits];
96
+ }
97
+ else {
98
+ if (openSimple === undefined) {
99
+ const idx = this.builder.newBlock(startLineOf(stmt), endLineOf(stmt), stmt.text, 'normal', this.harvest.facts(stmt));
100
+ if (entry === undefined)
101
+ entry = idx;
102
+ else
103
+ this.builder.connect(dangling, idx, 'seq');
104
+ openSimple = idx;
105
+ dangling = [idx];
106
+ }
107
+ else {
108
+ this.builder.extendBlock(openSimple, endLineOf(stmt), stmt.text, this.harvest.facts(stmt));
109
+ }
110
+ }
111
+ }
112
+ if (entry === undefined)
113
+ return null;
114
+ return { entry, exits: dangling };
115
+ });
116
+ }
117
+ /**
118
+ * Whether a statement breaks the current straight-line block. A
119
+ * `switch_expression` breaks only when it is a STATEMENT switch (a value-
120
+ * position switch used directly inside a `block` coalesces). A
121
+ * `local_variable_declaration` whose value is a modelable value-position switch
122
+ * (`var x = switch (…) {…}`, #2207) also breaks — `visitStmt` then models the
123
+ * arms as control flow instead of collapsing the decl to one inline block.
124
+ */
125
+ breaksBlock(stmt) {
126
+ if (stmt.type === 'local_variable_declaration') {
127
+ const v = this.directValue(stmt);
128
+ return v !== undefined && this.isModelableValueBranch(v);
129
+ }
130
+ if (!CONTROL_FLOW_TYPES.has(stmt.type))
131
+ return false;
132
+ if (stmt.type === 'switch_expression')
133
+ return this.isStatementSwitch(stmt);
134
+ return true;
135
+ }
136
+ /** Dispatch one statement to its handler. Non-null except for empty blocks. */
137
+ visitStmt(stmt) {
138
+ switch (stmt.type) {
139
+ case 'local_variable_declaration': {
140
+ // `var x = switch (k) { … }` (#2207): the value is a value-position
141
+ // branch — model it as control flow and bind the result on the rejoin,
142
+ // instead of collapsing the whole decl to one block.
143
+ const value = this.directValue(stmt);
144
+ if (value && this.isModelableValueBranch(value))
145
+ return this.visitBindBranch(stmt, value);
146
+ return this.visitSimple(stmt);
147
+ }
148
+ case 'if_statement':
149
+ return this.visitIf(stmt);
150
+ case 'while_statement':
151
+ return this.visitWhile(stmt);
152
+ case 'do_statement':
153
+ return this.visitDoWhile(stmt);
154
+ case 'for_statement':
155
+ return this.visitFor(stmt);
156
+ case 'enhanced_for_statement':
157
+ return this.visitForEach(stmt);
158
+ case 'switch_expression':
159
+ return this.visitSwitch(stmt);
160
+ case 'try_statement':
161
+ case 'try_with_resources_statement':
162
+ return this.visitTry(stmt);
163
+ case 'synchronized_statement':
164
+ return this.visitSynchronized(stmt);
165
+ case 'return_statement':
166
+ return this.visitReturn(stmt);
167
+ case 'throw_statement':
168
+ return this.visitThrow(stmt);
169
+ case 'break_statement':
170
+ return this.visitBreak(stmt);
171
+ case 'continue_statement':
172
+ return this.visitContinue(stmt);
173
+ case 'labeled_statement':
174
+ return this.visitLabeled(stmt);
175
+ case 'yield_statement':
176
+ return this.visitYield(stmt);
177
+ case 'block':
178
+ return this.visitSeq(this.statementsOf(stmt));
179
+ default:
180
+ return this.visitSimple(stmt);
181
+ }
182
+ }
183
+ visitSimple(stmt) {
184
+ const idx = this.builder.newBlock(startLineOf(stmt), endLineOf(stmt), stmt.text, 'normal', this.harvest.facts(stmt));
185
+ return { entry: idx, exits: [idx] };
186
+ }
187
+ visitReturn(stmt) {
188
+ // `return switch (k) { … };` (#2207): the returned value is a value-position
189
+ // branch — model it as control flow, with each arm returning (its value IS
190
+ // the function result), threading every active finalizer per arm.
191
+ const branch = stmt.namedChildren.find((c) => !isComment(c));
192
+ if (branch && this.isModelableValueBranch(branch)) {
193
+ const res = this.visitBranchExpr(branch);
194
+ const finalizers = this.cfc.finalizersForReturn();
195
+ for (const ex of res.exits) {
196
+ wireJumpThroughFinalizers(this.builder, ex, finalizers, this.builder.exitIndex, 'return');
197
+ }
198
+ return { entry: res.entry, exits: [] };
199
+ }
200
+ const idx = this.builder.newBlock(startLineOf(stmt), endLineOf(stmt), stmt.text, 'normal', this.harvest.facts(stmt));
201
+ // A return crosses EVERY active finalizer (finally / try-with-resources /
202
+ // synchronized) before EXIT.
203
+ wireJumpThroughFinalizers(this.builder, idx, this.cfc.finalizersForReturn(), this.builder.exitIndex, 'return');
204
+ return { entry: idx, exits: [] };
205
+ }
206
+ visitThrow(stmt) {
207
+ const idx = this.builder.newBlock(startLineOf(stmt), endLineOf(stmt), stmt.text, 'normal', this.harvest.facts(stmt));
208
+ this.builder.edge(idx, this.currentHandler(), 'throw');
209
+ return { entry: idx, exits: [] };
210
+ }
211
+ /**
212
+ * `yield e;` (switch-expression arm value) — produces the switch-expression's
213
+ * value and EXITS the enclosing switch (it does NOT fall through to the next
214
+ * colon group). Modeled as a terminator that jumps to the switch exit, threading
215
+ * any finalizer it crosses — exactly like a `break` out of the switch but
216
+ * carrying the yielded value's def/use facts. (Reusing the statement `visitSwitch`
217
+ * for a value-position colon switch would otherwise wire a spurious `fallthrough`
218
+ * edge between yield-terminated arms — #2211 review.)
219
+ */
220
+ visitYield(stmt) {
221
+ const idx = this.builder.newBlock(startLineOf(stmt), endLineOf(stmt), stmt.text, 'normal', this.harvest.facts(stmt));
222
+ const res = this.cfc.resolveYield();
223
+ const { target, finalizers } = res ?? {
224
+ target: this.builder.exitIndex,
225
+ finalizers: this.cfc.finalizersForReturn(),
226
+ };
227
+ wireJumpThroughFinalizers(this.builder, idx, finalizers, target, 'break');
228
+ return { entry: idx, exits: [] };
229
+ }
230
+ visitBreak(stmt) {
231
+ const idx = this.builder.newBlock(startLineOf(stmt), endLineOf(stmt), stmt.text);
232
+ const label = this.jumpLabel(stmt);
233
+ const res = this.cfc.resolveBreak(label);
234
+ const { target, finalizers } = res ?? {
235
+ target: this.builder.exitIndex,
236
+ finalizers: this.cfc.finalizersForReturn(),
237
+ };
238
+ wireJumpThroughFinalizers(this.builder, idx, finalizers, target, 'break');
239
+ return { entry: idx, exits: [] };
240
+ }
241
+ visitContinue(stmt) {
242
+ const idx = this.builder.newBlock(startLineOf(stmt), endLineOf(stmt), stmt.text);
243
+ const label = this.jumpLabel(stmt);
244
+ const res = this.cfc.resolveContinue(label);
245
+ const { target, finalizers } = res ?? {
246
+ target: this.builder.exitIndex,
247
+ finalizers: this.cfc.finalizersForReturn(),
248
+ };
249
+ wireJumpThroughFinalizers(this.builder, idx, finalizers, target, 'continue');
250
+ return { entry: idx, exits: [] };
251
+ }
252
+ /** The trailing label `identifier` of a `break`/`continue`, if any. */
253
+ jumpLabel(stmt) {
254
+ const id = stmt.namedChildren.find((c) => c.type === 'identifier');
255
+ return id?.text;
256
+ }
257
+ /**
258
+ * `label: <statement>` — the label names the construct it directly wraps. For a
259
+ * loop/switch we forward the label so its pushed frame carries it (`break
260
+ * outer;` then resolves to it); for any other labeled statement we push a
261
+ * labeled-block frame around its body so `break label;` reaches the join after.
262
+ */
263
+ visitLabeled(stmt) {
264
+ const labelNode = stmt.namedChildren.find((c) => c.type === 'identifier');
265
+ const label = labelNode?.text;
266
+ const body = stmt.namedChildren.find((c) => c.id !== labelNode?.id && !isComment(c)) ?? null;
267
+ if (!body || label === undefined)
268
+ return this.visitBody(body);
269
+ if (this.isLoopOrSwitchStatement(body)) {
270
+ // Forward the label to the loop/switch frame this statement pushes.
271
+ this.pendingLabels = [...this.pendingLabels, label];
272
+ return this.visitStmt(body);
273
+ }
274
+ // Labeled non-loop (`blk: { … break blk; … }`) — a break-to-label join after
275
+ // the body; an unlabeled break never matches it.
276
+ const joinBlock = this.builder.newBlock(endLineOf(stmt), endLineOf(stmt), '');
277
+ this.cfc.pushLabeledBlock(joinBlock, [label]);
278
+ const res = this.visitBody(body);
279
+ this.cfc.pop();
280
+ if (res)
281
+ this.builder.connect(res.exits, joinBlock, 'seq');
282
+ const entry = res?.entry ?? joinBlock;
283
+ return { entry, exits: [joinBlock] };
284
+ }
285
+ isLoopOrSwitchStatement(node) {
286
+ return (node.type === 'for_statement' ||
287
+ node.type === 'enhanced_for_statement' ||
288
+ node.type === 'while_statement' ||
289
+ node.type === 'do_statement' ||
290
+ (node.type === 'switch_expression' && this.isStatementSwitch(node)));
291
+ }
292
+ /** Take and clear the labels queued by an enclosing `labeled_statement`. */
293
+ takeLabels() {
294
+ const labels = this.pendingLabels;
295
+ this.pendingLabels = [];
296
+ return labels;
297
+ }
298
+ visitIf(stmt) {
299
+ const cond = this.condOf(stmt) ?? stmt;
300
+ const condBlock = this.builder.newBlock(startLineOf(stmt), endLineOf(cond), cond.text, 'normal', this.harvest.facts(cond));
301
+ const exits = [];
302
+ const thenRes = this.visitBody(stmt.childForFieldName('consequence'));
303
+ if (thenRes) {
304
+ this.builder.edge(condBlock, thenRes.entry, 'cond-true');
305
+ exits.push(...thenRes.exits);
306
+ }
307
+ else {
308
+ exits.push(condBlock); // empty then — true path falls through
309
+ }
310
+ // No `else_clause` wrapper in Java: `alternative` is the else body or the
311
+ // nested `if_statement` of an `else if` chain directly.
312
+ const elseNode = stmt.childForFieldName('alternative');
313
+ if (elseNode) {
314
+ const elseRes = this.visitBody(elseNode);
315
+ if (elseRes) {
316
+ this.builder.edge(condBlock, elseRes.entry, 'cond-false');
317
+ exits.push(...elseRes.exits);
318
+ }
319
+ else {
320
+ exits.push(condBlock);
321
+ }
322
+ }
323
+ else {
324
+ exits.push(condBlock); // no else — false path falls through to the join
325
+ }
326
+ return { entry: condBlock, exits: [...new Set(exits)] };
327
+ }
328
+ /** The (paren-unwrapped) condition expression of an if/while/do/synchronized. */
329
+ condOf(stmt) {
330
+ const cond = stmt.childForFieldName('condition');
331
+ return cond ? this.unwrapParen(cond) : undefined;
332
+ }
333
+ visitWhile(stmt) {
334
+ const labels = this.takeLabels();
335
+ const cond = this.condOf(stmt) ?? stmt;
336
+ const header = this.builder.newBlock(startLineOf(stmt), endLineOf(cond), cond.text, 'normal', this.harvest.facts(cond));
337
+ const loopExit = this.builder.newBlock(endLineOf(stmt), endLineOf(stmt), '');
338
+ this.cfc.pushLoop(header, loopExit, labels);
339
+ const body = this.visitBody(this.bodyBlockOf(stmt));
340
+ this.cfc.pop();
341
+ if (body) {
342
+ this.builder.edge(header, body.entry, 'cond-true');
343
+ this.builder.connect(body.exits, header, 'loop-back');
344
+ }
345
+ else {
346
+ this.builder.edge(header, header, 'loop-back'); // empty body re-tests
347
+ }
348
+ // Always emit the structural exit edge — even `while (true)` keeps EXIT
349
+ // reverse-reachable for the post-dominator / CDG pass.
350
+ this.builder.edge(header, loopExit, 'cond-false');
351
+ return { entry: header, exits: [loopExit] };
352
+ }
353
+ visitDoWhile(stmt) {
354
+ const labels = this.takeLabels();
355
+ const cond = this.condOf(stmt) ?? stmt;
356
+ const condBlock = this.builder.newBlock(startLineOf(cond), endLineOf(cond), cond.text, 'normal', this.harvest.facts(cond));
357
+ const loopExit = this.builder.newBlock(endLineOf(stmt), endLineOf(stmt), '');
358
+ this.cfc.pushLoop(condBlock, loopExit, labels);
359
+ const body = this.visitBody(this.bodyBlockOf(stmt));
360
+ this.cfc.pop();
361
+ const backTarget = body ? body.entry : condBlock;
362
+ if (body)
363
+ this.builder.connect(body.exits, condBlock, 'seq');
364
+ this.builder.edge(condBlock, backTarget, 'loop-back'); // cond true → run body again
365
+ this.builder.edge(condBlock, loopExit, 'cond-false');
366
+ return { entry: backTarget, exits: [loopExit] };
367
+ }
368
+ visitFor(stmt) {
369
+ const labels = this.takeLabels();
370
+ const init = stmt.childForFieldName('init');
371
+ const cond = stmt.childForFieldName('condition');
372
+ const incr = stmt.childForFieldName('update');
373
+ const header = this.builder.newBlock(startLineOf(stmt), cond ? endLineOf(cond) : startLineOf(stmt), cond ? cond.text : 'for(;;)', 'normal', cond ? this.harvest.facts(cond) : undefined);
374
+ const loopExit = this.builder.newBlock(endLineOf(stmt), endLineOf(stmt), '');
375
+ let incrBlock = header;
376
+ if (incr) {
377
+ incrBlock = this.builder.newBlock(startLineOf(incr), endLineOf(incr), incr.text, 'normal', this.harvest.facts(incr));
378
+ this.builder.edge(incrBlock, header, 'loop-back');
379
+ }
380
+ this.cfc.pushLoop(incrBlock, loopExit, labels);
381
+ const body = this.visitBody(this.bodyBlockOf(stmt));
382
+ this.cfc.pop();
383
+ if (body) {
384
+ this.builder.edge(header, body.entry, 'cond-true');
385
+ this.builder.connect(body.exits, incrBlock, incr ? 'seq' : 'loop-back');
386
+ }
387
+ else {
388
+ this.builder.edge(header, incrBlock, 'cond-true');
389
+ if (!incr)
390
+ this.builder.edge(header, header, 'loop-back');
391
+ }
392
+ // Structural exit edge — `for (;;) {}` (no condition) still keeps EXIT
393
+ // reverse-reachable so CDG is not silently skipped for the function.
394
+ this.builder.edge(header, loopExit, 'cond-false');
395
+ let entry = header;
396
+ if (init) {
397
+ const initBlock = this.builder.newBlock(startLineOf(init), endLineOf(init), init.text, 'normal', this.harvest.facts(init));
398
+ this.builder.edge(initBlock, header, 'seq');
399
+ entry = initBlock;
400
+ }
401
+ return { entry, exits: [loopExit] };
402
+ }
403
+ visitForEach(stmt) {
404
+ const labels = this.takeLabels();
405
+ // Header text is SYNTHESIZED, so facts come from the name/value nodes
406
+ // directly (the loop variable is a def, the iterated expression a use).
407
+ const header = this.builder.newBlock(startLineOf(stmt), startLineOf(stmt), this.forEachHeaderText(stmt), 'normal', this.harvest.forEachHeadFacts(stmt));
408
+ const loopExit = this.builder.newBlock(endLineOf(stmt), endLineOf(stmt), '');
409
+ this.cfc.pushLoop(header, loopExit, labels);
410
+ const body = this.visitBody(this.bodyBlockOf(stmt));
411
+ this.cfc.pop();
412
+ if (body) {
413
+ this.builder.edge(header, body.entry, 'cond-true');
414
+ this.builder.connect(body.exits, header, 'loop-back');
415
+ }
416
+ else {
417
+ this.builder.edge(header, header, 'loop-back');
418
+ }
419
+ this.builder.edge(header, loopExit, 'cond-false');
420
+ return { entry: header, exits: [loopExit] };
421
+ }
422
+ forEachHeaderText(stmt) {
423
+ const name = stmt.childForFieldName('name')?.text ?? '';
424
+ const value = stmt.childForFieldName('value')?.text ?? '';
425
+ return name || value ? `for(${name} : ${value})` : 'for(… : …)';
426
+ }
427
+ /**
428
+ * `synchronized (obj) body` — the monitor release runs on BOTH normal AND
429
+ * exception exit (deterministic finalizer), exactly like a `finally`. Modeled
430
+ * as a synthesized finalizer block so a `return`/`break`/`continue` crossing it
431
+ * threads through and gets a `finally-*` completion edge.
432
+ */
433
+ visitSynchronized(stmt) {
434
+ const bodyNode = this.bodyBlockOf(stmt) ?? null;
435
+ const lockExpr = this.synchronizedLock(stmt);
436
+ const releaseFacts = lockExpr ? this.harvest.facts(lockExpr) : undefined;
437
+ return this.buildProtectedSynthetic(bodyNode, stmt, 'release', releaseFacts);
438
+ }
439
+ /** The locked expression of a `synchronized (expr) {…}` (paren-unwrapped). */
440
+ synchronizedLock(stmt) {
441
+ const body = stmt.childForFieldName('body');
442
+ const expr = stmt.namedChildren.find((c) => c.id !== body?.id && !isComment(c));
443
+ return expr ? this.unwrapParen(expr) : undefined;
444
+ }
445
+ /** Whether a `switch_expression` is used as a STATEMENT (vs an expression value). */
446
+ isStatementSwitch(node) {
447
+ const p = node.parent;
448
+ if (!p)
449
+ return false;
450
+ // A statement-position switch is a direct child of a `block`, a classic
451
+ // group, or an arrow rule body; an expression-value switch is nested under a
452
+ // declaration / return / assignment / argument and is NOT one of these.
453
+ return (p.type === 'block' ||
454
+ p.type === 'switch_block_statement_group' ||
455
+ p.type === 'switch_rule' ||
456
+ p.type === 'labeled_statement');
457
+ }
458
+ visitSwitch(stmt) {
459
+ const labels = this.takeLabels();
460
+ const value = this.condOf(stmt) ?? stmt;
461
+ const dispatch = this.builder.newBlock(startLineOf(stmt), endLineOf(value), value.text, 'normal', this.harvest.facts(value));
462
+ const switchExit = this.builder.newBlock(endLineOf(stmt), endLineOf(stmt), '');
463
+ this.cfc.pushSwitch(switchExit, labels);
464
+ const body = stmt.childForFieldName('body');
465
+ // A `switch_block` holds `switch_block_statement_group`s (classic, fall
466
+ // through) OR `switch_rule`s (arrow, no fallthrough); never both.
467
+ const groups = body
468
+ ? body.namedChildren.filter((c) => c.type === 'switch_block_statement_group' || c.type === 'switch_rule')
469
+ : [];
470
+ // Each group's case-test expression(s) evaluate before the body runs —
471
+ // harvest their uses (and any pattern binding as a may-def) onto the dispatch
472
+ // block, one record per test, CONDITIONALLY (a later case test only runs when
473
+ // earlier cases didn't match).
474
+ for (const g of groups) {
475
+ for (const test of this.caseTests(g)) {
476
+ this.builder.attachFacts(dispatch, this.harvest.factsConditional(test));
477
+ }
478
+ }
479
+ const groupResults = groups.map((g) => this.visitSeq(this.caseStatements(g)));
480
+ const hasDefault = groups.some((g) => this.caseIsDefault(g));
481
+ const arrowForm = groups.some((g) => g.type === 'switch_rule');
482
+ const entryOf = new Array(groups.length);
483
+ let after = switchExit;
484
+ for (let i = groups.length - 1; i >= 0; i--) {
485
+ entryOf[i] = groupResults[i]?.entry ?? after;
486
+ after = entryOf[i];
487
+ }
488
+ for (let i = 0; i < groups.length; i++) {
489
+ this.builder.edge(dispatch, entryOf[i], 'switch-case');
490
+ }
491
+ if (!hasDefault)
492
+ this.builder.edge(dispatch, switchExit, 'switch-case'); // no-match path
493
+ // Classic colon groups FALL THROUGH to the next group when not break/return-
494
+ // terminated; arrow rules NEVER fall through (each body rejoins after).
495
+ for (let i = 0; i < groups.length; i++) {
496
+ const res = groupResults[i];
497
+ if (!res)
498
+ continue;
499
+ if (groups[i].type === 'switch_rule') {
500
+ this.builder.connect(res.exits, switchExit, 'seq');
501
+ }
502
+ else {
503
+ const fallTarget = i + 1 < groups.length ? entryOf[i + 1] : switchExit;
504
+ this.builder.connect(res.exits, fallTarget, 'fallthrough');
505
+ }
506
+ }
507
+ void arrowForm;
508
+ this.cfc.pop();
509
+ return { entry: dispatch, exits: [switchExit] };
510
+ }
511
+ /** A switch group/rule's body statements (everything but its `switch_label`). */
512
+ caseStatements(group) {
513
+ return group.namedChildren.filter((c) => c.type !== 'switch_label' && !isComment(c));
514
+ }
515
+ /** The case-test value expressions of a group (a `switch_label`'s values). */
516
+ caseTests(group) {
517
+ const label = group.namedChildren.find((c) => c.type === 'switch_label');
518
+ if (!label)
519
+ return [];
520
+ return label.namedChildren.filter((c) => !isComment(c));
521
+ }
522
+ /** A `default:` group has a `switch_label` with no value children. */
523
+ caseIsDefault(group) {
524
+ const label = group.namedChildren.find((c) => c.type === 'switch_label');
525
+ if (!label)
526
+ return false;
527
+ return label.namedChildren.filter((c) => !isComment(c)).length === 0;
528
+ }
529
+ // ── value-position branches (#2207) ─────────────────────────────────────────
530
+ /**
531
+ * The direct value of a `local_variable_declaration` with a SINGLE declarator:
532
+ * its `variable_declarator`'s `value` field (`var x = <value>`). Returns
533
+ * undefined for a multi-declarator decl (`int a = …, b = …;`) — modeling those
534
+ * arm-by-arm is out of scope, so they coalesce inline. The DIRECT value only:
535
+ * `var x = f(switch …)` yields the call, not the nested switch, so an
536
+ * argument-position switch stays inline.
537
+ */
538
+ directValue(stmt) {
539
+ const declarators = stmt.namedChildren.filter((c) => c.type === 'variable_declarator');
540
+ if (declarators.length !== 1)
541
+ return undefined;
542
+ return declarators[0].childForFieldName('value') ?? undefined;
543
+ }
544
+ /**
545
+ * Whether `node` is a value-position branch worth modeling as control flow
546
+ * (#2207): a `switch_expression` with ≥2 case groups (a real dispatch). Java has
547
+ * no value-position `if` (the ternary `?:` is deliberately excluded, like elvis
548
+ * in Kotlin), so `switch` is the only carrier.
549
+ */
550
+ isModelableValueBranch(node) {
551
+ if (node.type !== 'switch_expression')
552
+ return false;
553
+ const body = node.childForFieldName('body');
554
+ if (!body)
555
+ return false;
556
+ const groups = body.namedChildren.filter((c) => c.type === 'switch_block_statement_group' || c.type === 'switch_rule');
557
+ return groups.length >= 2;
558
+ }
559
+ /**
560
+ * Model a value-position `switch` as control flow regardless of position —
561
+ * {@link visitSeq}'s `isStatementSwitch` gate keeps value-position switches
562
+ * inline, so call {@link visitSwitch} directly here.
563
+ */
564
+ visitBranchExpr(node) {
565
+ return this.visitSwitch(node);
566
+ }
567
+ /**
568
+ * `var x = switch (k) { … }` (#2207): visit the switch as control flow, then
569
+ * rejoin its arms at a facts-only continuation carrying ONLY the bound name's
570
+ * def (the subject + arm-value uses are already harvested onto the switch's
571
+ * blocks). The arms are now control-dependent on the dispatch, and `x` is
572
+ * defined at the join — mirrors the Kotlin / Rust value-position binding.
573
+ */
574
+ visitBindBranch(stmt, branch) {
575
+ const res = this.visitBranchExpr(branch);
576
+ const cont = this.builder.newBlock(startLineOf(stmt), startLineOf(stmt), '', 'normal', this.harvest.bindingDefFacts(stmt));
577
+ this.builder.connect(res.exits, cont, 'seq');
578
+ return { entry: res.entry, exits: [cont] };
579
+ }
580
+ /**
581
+ * try / catch / finally / try-with-resources. The `resources` of a
582
+ * try-with-resources auto-close on BOTH normal and exception exit — exactly
583
+ * `finally`-shaped — so we synthesize a close finalizer that wraps an explicit
584
+ * `finally` (if any).
585
+ */
586
+ visitTry(stmt) {
587
+ const bodyNode = stmt.childForFieldName('body');
588
+ const catchClauses = [];
589
+ let finallyClause;
590
+ for (let i = 0; i < stmt.namedChildCount; i++) {
591
+ const c = stmt.namedChild(i);
592
+ if (c?.type === 'catch_clause')
593
+ catchClauses.push(c);
594
+ else if (c?.type === 'finally_clause')
595
+ finallyClause = c;
596
+ }
597
+ const finallyBody = finallyClause?.namedChildren.find((c) => c.type === 'block');
598
+ // try-with-resources: the resource close is a deterministic finalizer that
599
+ // runs after the body and (if present) before the explicit finally. Model it
600
+ // as a synthesized close block prepended to the finalizer chain.
601
+ const resources = this.tryResources(stmt);
602
+ return this.buildProtected(bodyNode ?? null, catchClauses, finallyBody ?? null, resources, stmt);
603
+ }
604
+ /** The `resource` nodes of a try-with-resources, or [] for a plain try. */
605
+ tryResources(stmt) {
606
+ const spec = stmt.childForFieldName('resources');
607
+ if (!spec)
608
+ return [];
609
+ return spec.namedChildren.filter((c) => c.type === 'resource');
610
+ }
611
+ /**
612
+ * Shared try/catch/finally builder. `catchClauses` may be empty; `finallyBody`
613
+ * is the explicit finally's `block` (or null); `resources` are try-with-
614
+ * resources resources (auto-close = an implicit finally that runs first).
615
+ *
616
+ * Models the TS/C# `visitTry` semantics: normal completion of try AND catch
617
+ * flow through the finalizers; a throw in the protected region routes to the
618
+ * handler; early exits crossing a finalizer thread through it (`finally-*`
619
+ * completion edges). The resource close finalizer (synthesized) and the
620
+ * explicit finally are chained so a crossing jump threads BOTH.
621
+ */
622
+ buildProtected(bodyNode, catchClauses, finallyBody, resources, span) {
623
+ // Innermost finalizer first on the active stack = outermost lexically. The
624
+ // resource close runs BEFORE the explicit finally, so push the explicit
625
+ // finally first (deeper) then the close (shallower / nearer the jump).
626
+ const explicitFinRes = finallyBody ? this.visitSeq(this.statementsOf(finallyBody)) : null;
627
+ const explicitFrame = explicitFinRes ? this.cfc.pushFinalizer(explicitFinRes.entry) : null;
628
+ // Synthesized resource-close finalizer (a single block; close()/AutoCloseable
629
+ // has no AST node), carrying the resource bindings' facts as uses.
630
+ let closeRes = null;
631
+ let closeFrame = null;
632
+ if (resources.length > 0) {
633
+ const closeFacts = this.harvest.resourceCloseFacts(resources);
634
+ const closeBlock = this.builder.newBlock(endLineOf(span), endLineOf(span), 'close', 'normal', closeFacts);
635
+ closeRes = { entry: closeBlock, exits: [closeBlock] };
636
+ // The close's normal exit flows into the explicit finally (if any).
637
+ if (explicitFinRes)
638
+ this.builder.edge(closeBlock, explicitFinRes.entry, 'seq');
639
+ closeFrame = this.cfc.pushFinalizer(closeRes.entry);
640
+ }
641
+ // The entry of the finalizer chain (what normal/exception completion runs
642
+ // first): close (if any) else explicit finally.
643
+ const finalizerEntry = closeRes?.entry ?? explicitFinRes?.entry;
644
+ // The set of normal exits AFTER the whole finalizer chain runs.
645
+ const finalizerExits = explicitFinRes?.exits ?? closeRes?.exits ?? null;
646
+ // Build each catch handler.
647
+ const catchEntries = [];
648
+ const catchExits = [];
649
+ let firstCatchEntry;
650
+ for (const clause of catchClauses) {
651
+ const clauseBody = clause.childForFieldName('body');
652
+ if (finalizerEntry !== undefined)
653
+ this.handlers.push(finalizerEntry);
654
+ let res = clauseBody ? this.visitSeq(this.statementsOf(clauseBody)) : null;
655
+ if (finalizerEntry !== undefined)
656
+ this.handlers.pop();
657
+ if (res === null) {
658
+ // Empty `catch {}` still catches — synthesize one block so exception flow
659
+ // lands somewhere and the post-try code stays reachable.
660
+ const idx = this.builder.newBlock(startLineOf(clause), endLineOf(clause), '');
661
+ res = { entry: idx, exits: [idx] };
662
+ }
663
+ const paramFacts = this.harvest.catchParamFacts(clause);
664
+ if (paramFacts) {
665
+ const paramBlock = this.builder.newBlock(startLineOf(clause), startLineOf(clause), '', 'normal', paramFacts);
666
+ this.builder.edge(paramBlock, res.entry, 'seq');
667
+ res = { entry: paramBlock, exits: res.exits };
668
+ }
669
+ catchEntries.push(res.entry);
670
+ catchExits.push(...res.exits);
671
+ if (firstCatchEntry === undefined)
672
+ firstCatchEntry = res.entry;
673
+ }
674
+ // Handler for the try body: first catch if present, else the finalizer
675
+ // chain, else the outer handler.
676
+ const tryHandler = firstCatchEntry ?? finalizerEntry ?? this.currentHandler();
677
+ const protectedStart = this.builder.blockCount;
678
+ this.handlers.push(tryHandler);
679
+ const bodyRes = bodyNode
680
+ ? bodyNode.type === 'block'
681
+ ? this.visitSeq(this.statementsOf(bodyNode))
682
+ : this.visitStmt(bodyNode)
683
+ : null;
684
+ this.handlers.pop();
685
+ if (catchClauses.length > 0 || finalizerEntry !== undefined) {
686
+ for (let b = protectedStart; b < this.builder.blockCount; b++) {
687
+ this.builder.edge(b, tryHandler, 'throw');
688
+ }
689
+ }
690
+ // Pop the finalizer frames (inner→outer) and drain their pending legs.
691
+ if (closeFrame && closeRes) {
692
+ this.cfc.pop();
693
+ drainFinalizerPending(this.builder, closeFrame, closeRes.exits);
694
+ }
695
+ if (explicitFrame && explicitFinRes) {
696
+ this.cfc.pop();
697
+ drainFinalizerPending(this.builder, explicitFrame, explicitFinRes.exits);
698
+ }
699
+ const exits = [];
700
+ if (finalizerEntry !== undefined) {
701
+ if (bodyRes)
702
+ this.builder.connect(bodyRes.exits, finalizerEntry, 'seq');
703
+ for (const e of catchExits)
704
+ this.builder.edge(e, finalizerEntry, 'seq');
705
+ if (finalizerExits)
706
+ exits.push(...finalizerExits);
707
+ // No catch → an exception re-propagates out after the finalizers run.
708
+ if (catchClauses.length === 0 && finalizerExits) {
709
+ this.builder.connect(finalizerExits, this.currentHandler(), 'throw');
710
+ }
711
+ }
712
+ else {
713
+ if (bodyRes)
714
+ exits.push(...bodyRes.exits);
715
+ exits.push(...catchExits);
716
+ }
717
+ const entry = bodyRes?.entry ?? finalizerEntry ?? catchEntries[0];
718
+ if (entry === undefined) {
719
+ void span;
720
+ return null;
721
+ }
722
+ return { entry, exits: [...new Set(exits)] };
723
+ }
724
+ /**
725
+ * `synchronized`: a protected body whose finalizer is a SYNTHESIZED single
726
+ * block (Monitor exit has no AST node). The finalizer runs on both normal and
727
+ * exception exit, and crossing jumps thread through it.
728
+ */
729
+ buildProtectedSynthetic(bodyNode, span, text, finalizerFacts) {
730
+ const finalizerBlock = this.builder.newBlock(endLineOf(span), endLineOf(span), text, 'normal', finalizerFacts ?? undefined);
731
+ const finRes = { entry: finalizerBlock, exits: [finalizerBlock] };
732
+ const finFrame = this.cfc.pushFinalizer(finRes.entry);
733
+ const protectedStart = this.builder.blockCount;
734
+ // The finalizer IS the handler — an exception in the body still runs the
735
+ // release, which then re-propagates to the outer handler.
736
+ this.handlers.push(finRes.entry);
737
+ const bodyRes = bodyNode
738
+ ? bodyNode.type === 'block'
739
+ ? this.visitSeq(this.statementsOf(bodyNode))
740
+ : this.visitStmt(bodyNode)
741
+ : null;
742
+ this.handlers.pop();
743
+ for (let b = protectedStart; b < this.builder.blockCount; b++) {
744
+ this.builder.edge(b, finRes.entry, 'throw');
745
+ }
746
+ this.cfc.pop();
747
+ drainFinalizerPending(this.builder, finFrame, finRes.exits);
748
+ // Normal completion of the body flows through the finalizer; the finalizer's
749
+ // exit re-propagates an exception to the outer handler (it had no catch).
750
+ if (bodyRes)
751
+ this.builder.connect(bodyRes.exits, finRes.entry, 'seq');
752
+ this.builder.connect(finRes.exits, this.currentHandler(), 'throw');
753
+ const entry = bodyRes?.entry ?? finRes.entry;
754
+ return { entry, exits: [...finRes.exits] };
755
+ }
756
+ /** Nearest enclosing exception handler, or the function EXIT. */
757
+ currentHandler() {
758
+ return this.handlers.length ? this.handlers[this.handlers.length - 1] : this.builder.exitIndex;
759
+ }
760
+ }
761
+ /** Build the CFG for one Java function node, or `undefined` if not modelable. */
762
+ function buildFunctionCfg(fnNode, filePath) {
763
+ try {
764
+ if (!JAVA_FUNCTION_TYPES.has(fnNode.type))
765
+ return undefined;
766
+ const startLine = startLineOf(fnNode);
767
+ const endLine = endLineOf(fnNode);
768
+ const startColumn = fnNode.startPosition.column;
769
+ // The body is a `block` (field `body`) OR an expression (single-expression
770
+ // lambda `x -> expr`).
771
+ const body = fnNode.childForFieldName('body') ?? fnNode.namedChildren.find((c) => c.type === 'block');
772
+ if (!body)
773
+ return undefined; // abstract / interface method — no body
774
+ const builder = new CfgBuilder(filePath, startLine, endLine, startColumn);
775
+ const harvest = new JavaHarvester(fnNode);
776
+ const paramFacts = harvest.paramFacts();
777
+ if (paramFacts)
778
+ builder.attachFacts(builder.entryIndex, paramFacts);
779
+ if (body.type !== 'block') {
780
+ // Single-expression lambda (`x -> expr`): one block whose value is returned.
781
+ const blk = builder.newBlock(startLineOf(body), endLineOf(body), body.text, 'normal', harvest.facts(body));
782
+ builder.edge(builder.entryIndex, blk, 'seq');
783
+ builder.edge(blk, builder.exitIndex, 'return');
784
+ return builder.finish(harvest.bindingTable());
785
+ }
786
+ const walk = new JavaCfgWalk(builder, harvest);
787
+ const res = walk.visitSeq(body.namedChildren.filter((c) => !isComment(c)));
788
+ if (!res) {
789
+ builder.edge(builder.entryIndex, builder.exitIndex, 'seq'); // empty body
790
+ return builder.finish(harvest.bindingTable());
791
+ }
792
+ builder.edge(builder.entryIndex, res.entry, 'seq');
793
+ builder.connect(res.exits, builder.exitIndex, 'seq'); // normal fall-off → EXIT
794
+ return builder.finish(harvest.bindingTable());
795
+ }
796
+ catch (err) {
797
+ // Never throw out of buildFunctionCfg — a malformed AST shape must skip only
798
+ // this one function's CFG, never drop the whole file's language group (R4).
799
+ // eslint-disable-next-line no-console
800
+ console.warn(`[cfg] Java buildFunctionCfg skipped a function in ${filePath}: ${String(err)}`);
801
+ return undefined;
802
+ }
803
+ }
804
+ /** Whether a node is a Java function this visitor builds a CFG for. */
805
+ function isFunction(node) {
806
+ return JAVA_FUNCTION_TYPES.has(node.type);
807
+ }
808
+ /** The Java CFG visitor. */
809
+ export function createJavaCfgVisitor() {
810
+ return { buildFunctionCfg, isFunction };
811
+ }
812
+ export { JAVA_FUNCTION_TYPES };