pyflow-analysis 0.1.0__py3-none-any.whl

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 (757) hide show
  1. pyflow/__init__.py +22 -0
  2. pyflow/_pyflow.py +50 -0
  3. pyflow/analysis/__init__.py +14 -0
  4. pyflow/analysis/alias/__init__.py +3 -0
  5. pyflow/analysis/alias/flow_sensitive/__init__.py +148 -0
  6. pyflow/analysis/alias/flow_sensitive/_transfer_calls.py +3493 -0
  7. pyflow/analysis/alias/flow_sensitive/_transfer_ops.py +1969 -0
  8. pyflow/analysis/alias/flow_sensitive/abstraction.py +1632 -0
  9. pyflow/analysis/alias/flow_sensitive/heap_analysis.py +280 -0
  10. pyflow/analysis/alias/flow_sensitive/heap_effects.py +1209 -0
  11. pyflow/analysis/alias/flow_sensitive/heap_state.py +381 -0
  12. pyflow/analysis/alias/flow_sensitive/heap_summary.py +299 -0
  13. pyflow/analysis/alias/flow_sensitive/intrinsics.py +948 -0
  14. pyflow/analysis/alias/flow_sensitive/model.py +449 -0
  15. pyflow/analysis/alias/flow_sensitive/points_to_graph.py +722 -0
  16. pyflow/analysis/alias/flow_sensitive/transfer.py +1483 -0
  17. pyflow/analysis/alias/kcfa/__init__.py +33 -0
  18. pyflow/analysis/alias/kcfa/_pythonstan/__init__.py +2 -0
  19. pyflow/analysis/alias/kcfa/_pythonstan/analysis/__init__.py +1 -0
  20. pyflow/analysis/alias/kcfa/_pythonstan/analysis/ai/__init__.py +53 -0
  21. pyflow/analysis/alias/kcfa/_pythonstan/analysis/ai/logging.py +490 -0
  22. pyflow/analysis/alias/kcfa/_pythonstan/analysis/ai/operation.py +1601 -0
  23. pyflow/analysis/alias/kcfa/_pythonstan/analysis/ai/pointer_adapter.py +353 -0
  24. pyflow/analysis/alias/kcfa/_pythonstan/analysis/ai/solver.py +559 -0
  25. pyflow/analysis/alias/kcfa/_pythonstan/analysis/ai/state.py +701 -0
  26. pyflow/analysis/alias/kcfa/_pythonstan/analysis/ai/value.py +804 -0
  27. pyflow/analysis/alias/kcfa/_pythonstan/analysis/analysis.py +86 -0
  28. pyflow/analysis/alias/kcfa/_pythonstan/analysis/class_hierarchy.py +83 -0
  29. pyflow/analysis/alias/kcfa/_pythonstan/analysis/closure.py +69 -0
  30. pyflow/analysis/alias/kcfa/_pythonstan/analysis/dataflow/__init__.py +4 -0
  31. pyflow/analysis/alias/kcfa/_pythonstan/analysis/dataflow/analysis.py +58 -0
  32. pyflow/analysis/alias/kcfa/_pythonstan/analysis/dataflow/driver.py +38 -0
  33. pyflow/analysis/alias/kcfa/_pythonstan/analysis/dataflow/liveness.py +33 -0
  34. pyflow/analysis/alias/kcfa/_pythonstan/analysis/dataflow/reaching_definition.py +52 -0
  35. pyflow/analysis/alias/kcfa/_pythonstan/analysis/dataflow/solver.py +178 -0
  36. pyflow/analysis/alias/kcfa/_pythonstan/analysis/defuse.py +46 -0
  37. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/__init__.py +18 -0
  38. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/kcfa/__init__.py +110 -0
  39. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/kcfa/analysis.py +382 -0
  40. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/kcfa/builtin_api_handler.py +1872 -0
  41. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/kcfa/call_graph_analyzer.py +289 -0
  42. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/kcfa/class_hierarchy.py +304 -0
  43. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/kcfa/config.py +139 -0
  44. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/kcfa/constraints.py +576 -0
  45. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/kcfa/context.py +496 -0
  46. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/kcfa/context_selector.py +297 -0
  47. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/kcfa/debug_monitor.py +34 -0
  48. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/kcfa/heap_model.py +221 -0
  49. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/kcfa/ir_translator.py +1181 -0
  50. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/kcfa/libs/__init__.py +2 -0
  51. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/kcfa/libs/math.py +244 -0
  52. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/kcfa/module_analysis.py +269 -0
  53. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/kcfa/module_summary.py +191 -0
  54. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/kcfa/object.py +685 -0
  55. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/kcfa/pointer_flow_graph.py +282 -0
  56. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/kcfa/points_to_set.py +301 -0
  57. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/kcfa/processor/__init__.py +21 -0
  58. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/kcfa/processor/attribute_semantics.py +512 -0
  59. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/kcfa/processor/compose_processor.py +46 -0
  60. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/kcfa/processor/container.py +128 -0
  61. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/kcfa/processor/generator.py +353 -0
  62. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/kcfa/processor/normal_call.py +664 -0
  63. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/kcfa/processor/processor.py +36 -0
  64. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/kcfa/processor/super_resolve.py +131 -0
  65. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/kcfa/pta_query_adapter.py +329 -0
  66. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/kcfa/solver.py +917 -0
  67. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/kcfa/solver_interface.py +76 -0
  68. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/kcfa/state.py +735 -0
  69. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/kcfa/unknown_tracker.py +126 -0
  70. pyflow/analysis/alias/kcfa/_pythonstan/analysis/pointer/kcfa/variable.py +144 -0
  71. pyflow/analysis/alias/kcfa/_pythonstan/analysis/scope/__init__.py +2 -0
  72. pyflow/analysis/alias/kcfa/_pythonstan/analysis/scope/analysis.py +37 -0
  73. pyflow/analysis/alias/kcfa/_pythonstan/analysis/scope/closure.py +81 -0
  74. pyflow/analysis/alias/kcfa/_pythonstan/analysis/transform/__init__.py +8 -0
  75. pyflow/analysis/alias/kcfa/_pythonstan/analysis/transform/block_cfg.py +133 -0
  76. pyflow/analysis/alias/kcfa/_pythonstan/analysis/transform/cfg.py +95 -0
  77. pyflow/analysis/alias/kcfa/_pythonstan/analysis/transform/driver.py +26 -0
  78. pyflow/analysis/alias/kcfa/_pythonstan/analysis/transform/icfg/__init__.py +0 -0
  79. pyflow/analysis/alias/kcfa/_pythonstan/analysis/transform/icfg/base.py +112 -0
  80. pyflow/analysis/alias/kcfa/_pythonstan/analysis/transform/icfg/baseprocess.py +524 -0
  81. pyflow/analysis/alias/kcfa/_pythonstan/analysis/transform/icfg/icfg.py +0 -0
  82. pyflow/analysis/alias/kcfa/_pythonstan/analysis/transform/icfg/icfg_pre_transform.py +69 -0
  83. pyflow/analysis/alias/kcfa/_pythonstan/analysis/transform/icfg/icfg_transform.py +0 -0
  84. pyflow/analysis/alias/kcfa/_pythonstan/analysis/transform/icfg/postprocessor.py +308 -0
  85. pyflow/analysis/alias/kcfa/_pythonstan/analysis/transform/icfg/preprocessor.py +375 -0
  86. pyflow/analysis/alias/kcfa/_pythonstan/analysis/transform/ir.py +361 -0
  87. pyflow/analysis/alias/kcfa/_pythonstan/analysis/transform/three_address.py +1161 -0
  88. pyflow/analysis/alias/kcfa/_pythonstan/analysis/transform/transform.py +27 -0
  89. pyflow/analysis/alias/kcfa/_pythonstan/graph/__init__.py +0 -0
  90. pyflow/analysis/alias/kcfa/_pythonstan/graph/call_graph/__init__.py +2 -0
  91. pyflow/analysis/alias/kcfa/_pythonstan/graph/call_graph/call_edge.py +45 -0
  92. pyflow/analysis/alias/kcfa/_pythonstan/graph/call_graph/call_graph.py +77 -0
  93. pyflow/analysis/alias/kcfa/_pythonstan/graph/cfg/__init__.py +3 -0
  94. pyflow/analysis/alias/kcfa/_pythonstan/graph/cfg/base_block.py +83 -0
  95. pyflow/analysis/alias/kcfa/_pythonstan/graph/cfg/cfg.py +200 -0
  96. pyflow/analysis/alias/kcfa/_pythonstan/graph/cfg/dump.py +20 -0
  97. pyflow/analysis/alias/kcfa/_pythonstan/graph/cfg/edges.py +162 -0
  98. pyflow/analysis/alias/kcfa/_pythonstan/graph/cfg/visualize.py +112 -0
  99. pyflow/analysis/alias/kcfa/_pythonstan/graph/dominator_tree.py +126 -0
  100. pyflow/analysis/alias/kcfa/_pythonstan/graph/graph.py +81 -0
  101. pyflow/analysis/alias/kcfa/_pythonstan/graph/icfg/__init__.py +0 -0
  102. pyflow/analysis/alias/kcfa/_pythonstan/graph/icfg/icfg.py +98 -0
  103. pyflow/analysis/alias/kcfa/_pythonstan/ir/__init__.py +2 -0
  104. pyflow/analysis/alias/kcfa/_pythonstan/ir/ir_statements.py +1579 -0
  105. pyflow/analysis/alias/kcfa/_pythonstan/ir/ir_visitor.py +21 -0
  106. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/LICENSE.pythonstan +28 -0
  107. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/README.md +99 -0
  108. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/abc.cpython-313.pyc +0 -0
  109. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/base64.cpython-313.pyc +0 -0
  110. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/bisect.cpython-313.pyc +0 -0
  111. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/collections.cpython-313.pyc +0 -0
  112. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/contextlib.cpython-313.pyc +0 -0
  113. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/copy.cpython-313.pyc +0 -0
  114. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/csv.cpython-313.pyc +0 -0
  115. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/dataclasses.cpython-313.pyc +0 -0
  116. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/datetime.cpython-313.pyc +0 -0
  117. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/email.cpython-313.pyc +0 -0
  118. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/enum.cpython-313.pyc +0 -0
  119. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/fnmatch.cpython-313.pyc +0 -0
  120. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/functools.cpython-313.pyc +0 -0
  121. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/glob.cpython-313.pyc +0 -0
  122. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/hashlib.cpython-313.pyc +0 -0
  123. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/heapq.cpython-313.pyc +0 -0
  124. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/hmac.cpython-313.pyc +0 -0
  125. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/http.cpython-313.pyc +0 -0
  126. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/importlib.cpython-313.pyc +0 -0
  127. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/inspect.cpython-313.pyc +0 -0
  128. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/io.cpython-313.pyc +0 -0
  129. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/itertools.cpython-313.pyc +0 -0
  130. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/json.cpython-313.pyc +0 -0
  131. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/logging.cpython-313.pyc +0 -0
  132. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/math.cpython-313.pyc +0 -0
  133. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/mimetypes.cpython-313.pyc +0 -0
  134. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/operator.cpython-313.pyc +0 -0
  135. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/os.cpython-313.pyc +0 -0
  136. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/pathlib.cpython-313.pyc +0 -0
  137. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/pickle.cpython-313.pyc +0 -0
  138. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/random.cpython-313.pyc +0 -0
  139. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/re.cpython-313.pyc +0 -0
  140. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/secrets.cpython-313.pyc +0 -0
  141. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/shutil.cpython-313.pyc +0 -0
  142. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/somelib.cpython-313.pyc +0 -0
  143. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/tempfile.cpython-313.pyc +0 -0
  144. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/time.cpython-313.pyc +0 -0
  145. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/traceback.cpython-313.pyc +0 -0
  146. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/types.cpython-313.pyc +0 -0
  147. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/uuid.cpython-313.pyc +0 -0
  148. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/warnings.cpython-313.pyc +0 -0
  149. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/__pycache__/weakref.cpython-313.pyc +0 -0
  150. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/abc.py +80 -0
  151. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/base64.py +62 -0
  152. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/bisect.py +52 -0
  153. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/collections.py +187 -0
  154. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/contextlib.py +113 -0
  155. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/copy.py +15 -0
  156. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/csv.py +163 -0
  157. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/dataclasses.py +150 -0
  158. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/datetime.py +171 -0
  159. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/email.py +270 -0
  160. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/enum.py +144 -0
  161. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/fnmatch.py +14 -0
  162. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/functools.py +84 -0
  163. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/glob.py +10 -0
  164. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/hashlib.py +74 -0
  165. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/heapq.py +91 -0
  166. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/hmac.py +31 -0
  167. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/http.py +317 -0
  168. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/importlib.py +126 -0
  169. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/inspect.py +246 -0
  170. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/io.py +471 -0
  171. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/itertools.py +164 -0
  172. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/json.py +80 -0
  173. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/logging.py +193 -0
  174. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/math.py +244 -0
  175. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/mimetypes.py +119 -0
  176. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/operator.py +310 -0
  177. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/os.py +286 -0
  178. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/pathlib.py +196 -0
  179. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/pickle.py +153 -0
  180. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/random.py +125 -0
  181. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/re.py +125 -0
  182. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/secrets.py +29 -0
  183. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/shutil.py +99 -0
  184. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/somelib.py +2 -0
  185. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/tempfile.py +76 -0
  186. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/time.py +65 -0
  187. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/traceback.py +151 -0
  188. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/types.py +247 -0
  189. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/urllib/__init__.py +1 -0
  190. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/urllib/__pycache__/__init__.cpython-313.pyc +0 -0
  191. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/urllib/__pycache__/parse.cpython-313.pyc +0 -0
  192. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/urllib/parse.py +221 -0
  193. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/uuid.py +34 -0
  194. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/warnings.py +82 -0
  195. pyflow/analysis/alias/kcfa/_pythonstan/stubs/stdlib/weakref.py +272 -0
  196. pyflow/analysis/alias/kcfa/_pythonstan/utils/__init__.py +1 -0
  197. pyflow/analysis/alias/kcfa/_pythonstan/utils/ast_rename.py +35 -0
  198. pyflow/analysis/alias/kcfa/_pythonstan/utils/common.py +70 -0
  199. pyflow/analysis/alias/kcfa/_pythonstan/utils/persistent_rb_tree.py +364 -0
  200. pyflow/analysis/alias/kcfa/_pythonstan/utils/temp_var_generator.py +68 -0
  201. pyflow/analysis/alias/kcfa/_pythonstan/utils/toposort.py +63 -0
  202. pyflow/analysis/alias/kcfa/_pythonstan/utils/var_collector.py +72 -0
  203. pyflow/analysis/alias/kcfa/_pythonstan/world/__init__.py +1 -0
  204. pyflow/analysis/alias/kcfa/_pythonstan/world/analysis_manager.py +141 -0
  205. pyflow/analysis/alias/kcfa/_pythonstan/world/class_hierarchy.py +56 -0
  206. pyflow/analysis/alias/kcfa/_pythonstan/world/config.py +93 -0
  207. pyflow/analysis/alias/kcfa/_pythonstan/world/import_manager.py +16 -0
  208. pyflow/analysis/alias/kcfa/_pythonstan/world/namespace.py +327 -0
  209. pyflow/analysis/alias/kcfa/_pythonstan/world/pipeline.py +153 -0
  210. pyflow/analysis/alias/kcfa/_pythonstan/world/scope_manager.py +133 -0
  211. pyflow/analysis/alias/kcfa/_pythonstan/world/world.py +49 -0
  212. pyflow/analysis/alias/kcfa/bridge.py +136 -0
  213. pyflow/analysis/astcollector.py +171 -0
  214. pyflow/analysis/cache/incremental.py +226 -0
  215. pyflow/analysis/callgraph/__init__.py +58 -0
  216. pyflow/analysis/callgraph/ast_based.py +184 -0
  217. pyflow/analysis/callgraph/callgraph.py +86 -0
  218. pyflow/analysis/callgraph/constraint_based/__init__.py +26 -0
  219. pyflow/analysis/callgraph/constraint_based/_analyzer.py +1976 -0
  220. pyflow/analysis/callgraph/constraint_based/_collector.py +704 -0
  221. pyflow/analysis/callgraph/constraint_based/_evaluator.py +984 -0
  222. pyflow/analysis/callgraph/constraint_based/_loader.py +115 -0
  223. pyflow/analysis/callgraph/constraint_based/_resolver.py +2852 -0
  224. pyflow/analysis/callgraph/constraint_based/api.py +233 -0
  225. pyflow/analysis/callgraph/constraint_based/engine.py +717 -0
  226. pyflow/analysis/callgraph/constraint_based/model.py +303 -0
  227. pyflow/analysis/callgraph/formats.py +99 -0
  228. pyflow/analysis/callgraph/pycg_based.py +273 -0
  229. pyflow/analysis/cdg/__init__.py +52 -0
  230. pyflow/analysis/cdg/construction.py +256 -0
  231. pyflow/analysis/cdg/dump.py +415 -0
  232. pyflow/analysis/cdg/graph.py +456 -0
  233. pyflow/analysis/cfg/__init__.py +38 -0
  234. pyflow/analysis/cfg/dfs.py +55 -0
  235. pyflow/analysis/cfg/dom.py +243 -0
  236. pyflow/analysis/cfg/dump.py +322 -0
  237. pyflow/analysis/cfg/expandphi.py +113 -0
  238. pyflow/analysis/cfg/gc.py +93 -0
  239. pyflow/analysis/cfg/graph.py +878 -0
  240. pyflow/analysis/cfg/inline.py +337 -0
  241. pyflow/analysis/cfg/killflow.py +360 -0
  242. pyflow/analysis/cfg/optimize.py +164 -0
  243. pyflow/analysis/cfg/simplify.py +19 -0
  244. pyflow/analysis/cfg/ssa.py +693 -0
  245. pyflow/analysis/cfg/ssatransform.py +13 -0
  246. pyflow/analysis/cfg/structuralanalysis.py +422 -0
  247. pyflow/analysis/cfg/transform.py +527 -0
  248. pyflow/analysis/cpa/__init__.py +657 -0
  249. pyflow/analysis/cpa/base.py +430 -0
  250. pyflow/analysis/cpa/codecloner.py +262 -0
  251. pyflow/analysis/cpa/constraintextractor.py +797 -0
  252. pyflow/analysis/cpa/constraints.py +1071 -0
  253. pyflow/analysis/cpa/simpleimagebuilder.py +299 -0
  254. pyflow/analysis/cpasignature.py +132 -0
  255. pyflow/analysis/cpg/__init__.py +93 -0
  256. pyflow/analysis/cpg/build.py +221 -0
  257. pyflow/analysis/cpg/dump.py +102 -0
  258. pyflow/analysis/cpg/graph.py +1877 -0
  259. pyflow/analysis/cpg/persist.py +346 -0
  260. pyflow/analysis/cpg/profiles.py +286 -0
  261. pyflow/analysis/cpg/rules.py +365 -0
  262. pyflow/analysis/cpg/taint.py +1934 -0
  263. pyflow/analysis/dataflowIR/__init__.py +6 -0
  264. pyflow/analysis/dataflowIR/annotations.py +174 -0
  265. pyflow/analysis/dataflowIR/convert.py +795 -0
  266. pyflow/analysis/dataflowIR/dump.py +443 -0
  267. pyflow/analysis/dataflowIR/graph.py +1648 -0
  268. pyflow/analysis/dataflowIR/ordering.py +134 -0
  269. pyflow/analysis/dataflowIR/predicate.py +233 -0
  270. pyflow/analysis/dataflowIR/transform/__init__.py +15 -0
  271. pyflow/analysis/dataflowIR/transform/dce.py +215 -0
  272. pyflow/analysis/dataflowIR/transform/loadelimination.py +185 -0
  273. pyflow/analysis/dataflowIR/traverse.py +91 -0
  274. pyflow/analysis/ddg/__init__.py +53 -0
  275. pyflow/analysis/ddg/construction.py +310 -0
  276. pyflow/analysis/ddg/dump.py +218 -0
  277. pyflow/analysis/ddg/graph.py +295 -0
  278. pyflow/analysis/dump/__init__.py +15 -0
  279. pyflow/analysis/dump/dumpgraphs.py +93 -0
  280. pyflow/analysis/dump/dumpreport.py +841 -0
  281. pyflow/analysis/dump/dumputil.py +98 -0
  282. pyflow/analysis/fsdf/__init__.py +1107 -0
  283. pyflow/analysis/fsdf/canonicalset.py +176 -0
  284. pyflow/analysis/fsdf/canonicaltree.py +1071 -0
  285. pyflow/analysis/ifds/__init__.py +284 -0
  286. pyflow/analysis/ifds/annotation_fallback.py +21 -0
  287. pyflow/analysis/ifds/annotation_synthesis.py +803 -0
  288. pyflow/analysis/ifds/api.py +567 -0
  289. pyflow/analysis/ifds/backward_solver.py +328 -0
  290. pyflow/analysis/ifds/cfg_adapter.py +1575 -0
  291. pyflow/analysis/ifds/clients/_call_model.py +166 -0
  292. pyflow/analysis/ifds/clients/_client_common.py +1617 -0
  293. pyflow/analysis/ifds/clients/flow_path_collector.py +159 -0
  294. pyflow/analysis/ifds/clients/library_presets.py +1134 -0
  295. pyflow/analysis/ifds/clients/nullness.py +754 -0
  296. pyflow/analysis/ifds/clients/registry/__init__.py +26 -0
  297. pyflow/analysis/ifds/clients/registry/loader.py +688 -0
  298. pyflow/analysis/ifds/clients/registry/validate.py +37 -0
  299. pyflow/analysis/ifds/clients/taint.py +782 -0
  300. pyflow/analysis/ifds/clients/typestate.py +1550 -0
  301. pyflow/analysis/ifds/clients/typestate_engine.py +534 -0
  302. pyflow/analysis/ifds/diagnostics.py +29 -0
  303. pyflow/analysis/ifds/preparation.py +97 -0
  304. pyflow/analysis/ifds/problem.py +363 -0
  305. pyflow/analysis/ifds/queries.py +142 -0
  306. pyflow/analysis/ifds/reporting.py +289 -0
  307. pyflow/analysis/ifds/shadow_scan.py +349 -0
  308. pyflow/analysis/ifds/solver.py +1495 -0
  309. pyflow/analysis/ifds/supergraph.py +186 -0
  310. pyflow/analysis/ifds/transfers.py +187 -0
  311. pyflow/analysis/ipa/__init__.py +77 -0
  312. pyflow/analysis/ipa/calling/__init__.py +17 -0
  313. pyflow/analysis/ipa/calling/callbinder.py +93 -0
  314. pyflow/analysis/ipa/calling/cpa.py +197 -0
  315. pyflow/analysis/ipa/calling/transfer.py +208 -0
  316. pyflow/analysis/ipa/constraintextractor.py +804 -0
  317. pyflow/analysis/ipa/constraints/__init__.py +18 -0
  318. pyflow/analysis/ipa/constraints/base.py +66 -0
  319. pyflow/analysis/ipa/constraints/calls.py +424 -0
  320. pyflow/analysis/ipa/constraints/flow.py +445 -0
  321. pyflow/analysis/ipa/constraints/node.py +299 -0
  322. pyflow/analysis/ipa/constraints/qualifiers.py +17 -0
  323. pyflow/analysis/ipa/constraints/split.py +178 -0
  324. pyflow/analysis/ipa/dump.py +246 -0
  325. pyflow/analysis/ipa/entrypointbuilder.py +52 -0
  326. pyflow/analysis/ipa/escape/__init__.py +19 -0
  327. pyflow/analysis/ipa/escape/objectescape.py +77 -0
  328. pyflow/analysis/ipa/ipanalysis.py +319 -0
  329. pyflow/analysis/ipa/memory/__init__.py +16 -0
  330. pyflow/analysis/ipa/memory/extractorpolicy.py +38 -0
  331. pyflow/analysis/ipa/memory/storegraphpolicy.py +17 -0
  332. pyflow/analysis/ipa/model/__init__.py +18 -0
  333. pyflow/analysis/ipa/model/context.py +277 -0
  334. pyflow/analysis/ipa/model/invocation.py +159 -0
  335. pyflow/analysis/ipa/model/object.py +102 -0
  336. pyflow/analysis/ipa/model/objectname.py +77 -0
  337. pyflow/analysis/ipa/model/region.py +51 -0
  338. pyflow/analysis/ipa/summary/__init__.py +120 -0
  339. pyflow/analysis/ir_utils.py +215 -0
  340. pyflow/analysis/lifetimeanalysis/__init__.py +935 -0
  341. pyflow/analysis/lifetimeanalysis/database/__init__.py +17 -0
  342. pyflow/analysis/lifetimeanalysis/database/base.py +72 -0
  343. pyflow/analysis/lifetimeanalysis/database/lattice.py +189 -0
  344. pyflow/analysis/lifetimeanalysis/database/mapping.py +174 -0
  345. pyflow/analysis/lifetimeanalysis/database/structure.py +224 -0
  346. pyflow/analysis/lifetimeanalysis/database/tupleset.py +141 -0
  347. pyflow/analysis/numbering/__init__.py +17 -0
  348. pyflow/analysis/numbering/dataflow.py +214 -0
  349. pyflow/analysis/numbering/dominance.py +120 -0
  350. pyflow/analysis/numbering/readmodify.py +251 -0
  351. pyflow/analysis/numbering/ssa.py +604 -0
  352. pyflow/analysis/pdg/__init__.py +55 -0
  353. pyflow/analysis/pdg/construction.py +447 -0
  354. pyflow/analysis/pdg/cypher.py +1150 -0
  355. pyflow/analysis/pdg/dump.py +129 -0
  356. pyflow/analysis/pdg/graph.py +308 -0
  357. pyflow/analysis/programculler.py +224 -0
  358. pyflow/analysis/shape/__init__.py +466 -0
  359. pyflow/analysis/shape/constraintbuilder.py +933 -0
  360. pyflow/analysis/shape/constraints.py +510 -0
  361. pyflow/analysis/shape/dataflow.py +202 -0
  362. pyflow/analysis/shape/model/__init__.py +27 -0
  363. pyflow/analysis/shape/model/canonical.py +137 -0
  364. pyflow/analysis/shape/model/configuration.py +78 -0
  365. pyflow/analysis/shape/model/expressions.py +268 -0
  366. pyflow/analysis/shape/model/pathinformation.py +645 -0
  367. pyflow/analysis/shape/model/referencecount.py +212 -0
  368. pyflow/analysis/shape/model/secondary.py +31 -0
  369. pyflow/analysis/shape/model/slots.py +69 -0
  370. pyflow/analysis/shape/regionanalysis.py +152 -0
  371. pyflow/analysis/shape/transferfunctions.py +353 -0
  372. pyflow/analysis/storegraph/__init__.py +19 -0
  373. pyflow/analysis/storegraph/annotations.py +70 -0
  374. pyflow/analysis/storegraph/canonicalobjects.py +329 -0
  375. pyflow/analysis/storegraph/extendedtypes.py +311 -0
  376. pyflow/analysis/storegraph/setmanager.py +123 -0
  377. pyflow/analysis/storegraph/storegraph.py +577 -0
  378. pyflow/analysis/tools.py +112 -0
  379. pyflow/analysis/typeinfo/__init__.py +93 -0
  380. pyflow/analysis/typeinfo/_config.py +84 -0
  381. pyflow/analysis/typeinfo/annotation_resolver.py +483 -0
  382. pyflow/analysis/typeinfo/collector.py +361 -0
  383. pyflow/analysis/typeinfo/constants.py +378 -0
  384. pyflow/analysis/typeinfo/docstring_parser.py +289 -0
  385. pyflow/analysis/typeinfo/generic_binder.py +145 -0
  386. pyflow/analysis/typeinfo/gradual_typing.py +255 -0
  387. pyflow/analysis/typeinfo/models.py +43 -0
  388. pyflow/analysis/typeinfo/service.py +774 -0
  389. pyflow/analysis/typeinfo/string_subtype_inference.py +113 -0
  390. pyflow/analysis/typeinfo/string_subtypes.py +150 -0
  391. pyflow/analysis/typeinfo/stub_loader.py +667 -0
  392. pyflow/analysis/typeinfo/type_inference.py +747 -0
  393. pyflow/analysis/typeinfo/type_utils.py +361 -0
  394. pyflow/analysis/typeinfo/typesystem.py +2346 -0
  395. pyflow/analysis/typeinfo/typetracing.py +672 -0
  396. pyflow/api/__init__.py +94 -0
  397. pyflow/api/entrypoints/__init__.py +36 -0
  398. pyflow/api/entrypoints/declaration.py +345 -0
  399. pyflow/api/entrypoints/entry_point.py +59 -0
  400. pyflow/api/entrypoints/wrappers.py +83 -0
  401. pyflow/api/queries/__init__.py +65 -0
  402. pyflow/api/queries/_cfg_utils.py +53 -0
  403. pyflow/api/queries/_dataflow_ipa.py +29 -0
  404. pyflow/api/queries/_dataflow_reaching_defs.py +105 -0
  405. pyflow/api/queries/_dataflow_store_graph.py +152 -0
  406. pyflow/api/queries/_dataflow_taint.py +76 -0
  407. pyflow/api/queries/_models.py +177 -0
  408. pyflow/api/queries/call_graph.py +168 -0
  409. pyflow/api/queries/capabilities.py +106 -0
  410. pyflow/api/queries/context.py +152 -0
  411. pyflow/api/queries/control_flow.py +35 -0
  412. pyflow/api/queries/data_flow.py +157 -0
  413. pyflow/api/queries/engine.py +201 -0
  414. pyflow/api/queries/localization.py +8 -0
  415. pyflow/api/queries/service.py +339 -0
  416. pyflow/api/queries/tasks/__init__.py +8 -0
  417. pyflow/api/queries/tasks/code_localization.py +425 -0
  418. pyflow/api/queries/tasks/test_generation.py +338 -0
  419. pyflow/api/queries/test_generation.py +8 -0
  420. pyflow/application/__init__.py +109 -0
  421. pyflow/application/context.py +116 -0
  422. pyflow/application/errors.py +55 -0
  423. pyflow/application/makefile.py +213 -0
  424. pyflow/application/passes.py +515 -0
  425. pyflow/application/passmanager.py +755 -0
  426. pyflow/application/pipeline.py +459 -0
  427. pyflow/application/program.py +147 -0
  428. pyflow/checker/__init__.py +51 -0
  429. pyflow/checker/formatters/__init__.py +14 -0
  430. pyflow/checker/formatters/json.py +153 -0
  431. pyflow/checker/formatters/sarif.py +263 -0
  432. pyflow/checker/formatters/text.py +168 -0
  433. pyflow/checker/formatters/utils.py +17 -0
  434. pyflow/checker/llm/__init__.py +19 -0
  435. pyflow/checker/llm/exploit.py +129 -0
  436. pyflow/checker/llm/judge.py +105 -0
  437. pyflow/checker/llm/llm_utils.py +131 -0
  438. pyflow/checker/microbench.py +338 -0
  439. pyflow/checker/pattern/__init__.py +14 -0
  440. pyflow/checker/pattern/checkers/__init__.py +34 -0
  441. pyflow/checker/pattern/checkers/aws_security.py +234 -0
  442. pyflow/checker/pattern/checkers/blacklist_calls.py +54 -0
  443. pyflow/checker/pattern/checkers/blacklist_imports.py +73 -0
  444. pyflow/checker/pattern/checkers/class_pollution.py +459 -0
  445. pyflow/checker/pattern/checkers/command_injection.py +340 -0
  446. pyflow/checker/pattern/checkers/deserialization.py +373 -0
  447. pyflow/checker/pattern/checkers/django_security.py +285 -0
  448. pyflow/checker/pattern/checkers/exception_handling.py +317 -0
  449. pyflow/checker/pattern/checkers/exec_use.py +16 -0
  450. pyflow/checker/pattern/checkers/fastapi_security.py +291 -0
  451. pyflow/checker/pattern/checkers/flask_security.py +264 -0
  452. pyflow/checker/pattern/checkers/hardcoded_password.py +239 -0
  453. pyflow/checker/pattern/checkers/hardcoded_secrets.py +279 -0
  454. pyflow/checker/pattern/checkers/ldap_injection.py +281 -0
  455. pyflow/checker/pattern/checkers/path_traversal.py +124 -0
  456. pyflow/checker/pattern/checkers/shell_injection.py +46 -0
  457. pyflow/checker/pattern/checkers/sql_injection.py +170 -0
  458. pyflow/checker/pattern/checkers/sql_injection_enhanced.py +574 -0
  459. pyflow/checker/pattern/checkers/ssrf.py +419 -0
  460. pyflow/checker/pattern/checkers/template_security.py +310 -0
  461. pyflow/checker/pattern/checkers/weak_crypto.py +89 -0
  462. pyflow/checker/pattern/checkers/xxe_vulnerabilities.py +155 -0
  463. pyflow/checker/pattern/core/__init__.py +17 -0
  464. pyflow/checker/pattern/core/blacklist.py +569 -0
  465. pyflow/checker/pattern/core/config.py +15 -0
  466. pyflow/checker/pattern/core/constants.py +52 -0
  467. pyflow/checker/pattern/core/context.py +276 -0
  468. pyflow/checker/pattern/core/issue.py +325 -0
  469. pyflow/checker/pattern/core/manager.py +253 -0
  470. pyflow/checker/pattern/core/metrics.py +78 -0
  471. pyflow/checker/pattern/core/node_visitor.py +430 -0
  472. pyflow/checker/pattern/core/test_loader.py +41 -0
  473. pyflow/checker/pattern/core/test_properties.py +54 -0
  474. pyflow/checker/pattern/core/test_set.py +33 -0
  475. pyflow/checker/pattern/core/tester.py +244 -0
  476. pyflow/checker/pattern/core/utils.py +167 -0
  477. pyflow/checker/quality.py +374 -0
  478. pyflow/checker/semantic/__init__.py +16 -0
  479. pyflow/checker/semantic/core/__init__.py +23 -0
  480. pyflow/checker/semantic/core/base.py +26 -0
  481. pyflow/checker/semantic/core/context.py +270 -0
  482. pyflow/checker/semantic/core/issue.py +9 -0
  483. pyflow/checker/semantic/core/manager.py +150 -0
  484. pyflow/checker/semantic/core/runner.py +58 -0
  485. pyflow/checker/semantic/detectors/__init__.py +17 -0
  486. pyflow/checker/semantic/detectors/ast_helpers.py +114 -0
  487. pyflow/checker/semantic/detectors/leak.py +500 -0
  488. pyflow/checker/semantic/detectors/null_dereference.py +132 -0
  489. pyflow/checker/semantic/detectors/semantic_taint.py +2258 -0
  490. pyflow/checker/supply_chain.py +568 -0
  491. pyflow/cli/__init__.py +12 -0
  492. pyflow/cli/alias.py +397 -0
  493. pyflow/cli/callgraph.py +185 -0
  494. pyflow/cli/ir.py +461 -0
  495. pyflow/cli/main.py +125 -0
  496. pyflow/cli/optimize.py +603 -0
  497. pyflow/cli/security.py +1262 -0
  498. pyflow/cli/supply_chain.py +114 -0
  499. pyflow/config/nullness/django.json +21 -0
  500. pyflow/config/nullness/fastapi.json +17 -0
  501. pyflow/config/nullness/flask.json +37 -0
  502. pyflow/config/nullness/sql.json +21 -0
  503. pyflow/config/nullness/sqlalchemy.json +33 -0
  504. pyflow/config/nullness/stdlib.json +161 -0
  505. pyflow/config/rule-pack.schema.json +54 -0
  506. pyflow/config/taint/aiohttp.json +640 -0
  507. pyflow/config/taint/cloud.json +485 -0
  508. pyflow/config/taint/concurrency.json +25 -0
  509. pyflow/config/taint/django.json +710 -0
  510. pyflow/config/taint/falcon.json +375 -0
  511. pyflow/config/taint/fastapi.json +192 -0
  512. pyflow/config/taint/flask.json +504 -0
  513. pyflow/config/taint/injection.json +2294 -0
  514. pyflow/config/taint/network.json +367 -0
  515. pyflow/config/taint/nosql.json +166 -0
  516. pyflow/config/taint/pandas.json +463 -0
  517. pyflow/config/taint/requests.json +952 -0
  518. pyflow/config/taint/serialization.json +109 -0
  519. pyflow/config/taint/sql.json +730 -0
  520. pyflow/config/taint/sqlalchemy.json +235 -0
  521. pyflow/config/taint/stdlib.json +4388 -0
  522. pyflow/config/taint/tornado.json +151 -0
  523. pyflow/config/taint/xml.json +89 -0
  524. pyflow/config/typestate/concurrency.json +145 -0
  525. pyflow/config/typestate/network.json +130 -0
  526. pyflow/config/typestate/requests.json +88 -0
  527. pyflow/config/typestate/sql.json +107 -0
  528. pyflow/config/typestate/sqlalchemy.json +45 -0
  529. pyflow/config/typestate/stdlib.json +91 -0
  530. pyflow/config.py +30 -0
  531. pyflow/frontend/__init__.py +28 -0
  532. pyflow/frontend/ast_converter.py +2593 -0
  533. pyflow/frontend/class_hierarchy.py +665 -0
  534. pyflow/frontend/dependency_resolver.py +1836 -0
  535. pyflow/frontend/function_extractor.py +602 -0
  536. pyflow/frontend/object_manager.py +224 -0
  537. pyflow/frontend/programextractor.py +1033 -0
  538. pyflow/frontend/project_resolution.py +511 -0
  539. pyflow/frontend/source_locator.py +163 -0
  540. pyflow/frontend/stub_manager.py +425 -0
  541. pyflow/language/__init__.py +5 -0
  542. pyflow/language/asttools/__init__.py +34 -0
  543. pyflow/language/asttools/annotation.py +242 -0
  544. pyflow/language/asttools/antlrtranslator.py +344 -0
  545. pyflow/language/asttools/astpprint.py +132 -0
  546. pyflow/language/asttools/complexity.py +224 -0
  547. pyflow/language/asttools/decorators.py +71 -0
  548. pyflow/language/asttools/metaast/__init__.py +370 -0
  549. pyflow/language/asttools/metaast/codegeneration.py +622 -0
  550. pyflow/language/asttools/origin.py +83 -0
  551. pyflow/language/asttools/symbols.py +204 -0
  552. pyflow/language/asttools/visitors.py +179 -0
  553. pyflow/language/modules/__init__.py +18 -0
  554. pyflow/language/modules/_scope_graph.py +484 -0
  555. pyflow/language/modules/ast_helper.py +211 -0
  556. pyflow/language/modules/module_definitions.py +240 -0
  557. pyflow/language/modules/project_handler.py +110 -0
  558. pyflow/language/modules/transform.py +290 -0
  559. pyflow/language/modules/util.py +413 -0
  560. pyflow/language/python/__init__.py +21 -0
  561. pyflow/language/python/annotations.py +378 -0
  562. pyflow/language/python/ast.py +749 -0
  563. pyflow/language/python/collapser.py +604 -0
  564. pyflow/language/python/default_markers.py +8 -0
  565. pyflow/language/python/defuse.py +469 -0
  566. pyflow/language/python/fold.py +220 -0
  567. pyflow/language/python/program.py +371 -0
  568. pyflow/language/python/pythonbase.py +353 -0
  569. pyflow/language/python/pythonoutput.py +82 -0
  570. pyflow/language/python/simplecodegen.py +975 -0
  571. pyflow/optimization/__init__.py +29 -0
  572. pyflow/optimization/argumentnormalization.py +429 -0
  573. pyflow/optimization/callconverter.py +295 -0
  574. pyflow/optimization/clone.py +743 -0
  575. pyflow/optimization/codeinlining.py +587 -0
  576. pyflow/optimization/convertboolelimination.py +111 -0
  577. pyflow/optimization/cullprogram.py +155 -0
  578. pyflow/optimization/dataflow/__init__.py +17 -0
  579. pyflow/optimization/dataflow/base.py +780 -0
  580. pyflow/optimization/dataflow/forward.py +681 -0
  581. pyflow/optimization/dataflow/reverse.py +454 -0
  582. pyflow/optimization/dce.py +354 -0
  583. pyflow/optimization/fold.py +780 -0
  584. pyflow/optimization/loadelimination.py +342 -0
  585. pyflow/optimization/methodcall.py +554 -0
  586. pyflow/optimization/rewrite.py +129 -0
  587. pyflow/optimization/simplify.py +143 -0
  588. pyflow/optimization/storeelimination.py +129 -0
  589. pyflow/optimization/termrewrite.py +287 -0
  590. pyflow/stats/__init__.py +436 -0
  591. pyflow/stubs/__init__.py +34 -0
  592. pyflow/stubs/__pycache__/__init__.cpython-313.pyc +0 -0
  593. pyflow/stubs/__pycache__/lltranslator.cpython-313.pyc +0 -0
  594. pyflow/stubs/__pycache__/stubcollector.cpython-313.pyc +0 -0
  595. pyflow/stubs/lltranslator.py +493 -0
  596. pyflow/stubs/std/__init__.py +83 -0
  597. pyflow/stubs/std/__pycache__/__init__.cpython-313.pyc +0 -0
  598. pyflow/stubs/std/__pycache__/argparse_stubs.cpython-313.pyc +0 -0
  599. pyflow/stubs/std/__pycache__/collections_stubs.cpython-313.pyc +0 -0
  600. pyflow/stubs/std/__pycache__/container.cpython-313.pyc +0 -0
  601. pyflow/stubs/std/__pycache__/copy_stubs.cpython-313.pyc +0 -0
  602. pyflow/stubs/std/__pycache__/csv_stubs.cpython-313.pyc +0 -0
  603. pyflow/stubs/std/__pycache__/datetime_stubs.cpython-313.pyc +0 -0
  604. pyflow/stubs/std/__pycache__/functools_stubs.cpython-313.pyc +0 -0
  605. pyflow/stubs/std/__pycache__/hashlib_stubs.cpython-313.pyc +0 -0
  606. pyflow/stubs/std/__pycache__/interpreter.cpython-313.pyc +0 -0
  607. pyflow/stubs/std/__pycache__/io_stubs.cpython-313.pyc +0 -0
  608. pyflow/stubs/std/__pycache__/itertools_stubs.cpython-313.pyc +0 -0
  609. pyflow/stubs/std/__pycache__/json_stubs.cpython-313.pyc +0 -0
  610. pyflow/stubs/std/__pycache__/llfunc.cpython-313.pyc +0 -0
  611. pyflow/stubs/std/__pycache__/logging_stubs.cpython-313.pyc +0 -0
  612. pyflow/stubs/std/__pycache__/mathstubs.cpython-313.pyc +0 -0
  613. pyflow/stubs/std/__pycache__/operator_stubs.cpython-313.pyc +0 -0
  614. pyflow/stubs/std/__pycache__/os_stubs.cpython-313.pyc +0 -0
  615. pyflow/stubs/std/__pycache__/pathlib_stubs.cpython-313.pyc +0 -0
  616. pyflow/stubs/std/__pycache__/pickle_stubs.cpython-313.pyc +0 -0
  617. pyflow/stubs/std/__pycache__/random.cpython-313.pyc +0 -0
  618. pyflow/stubs/std/__pycache__/re_stubs.cpython-313.pyc +0 -0
  619. pyflow/stubs/std/__pycache__/sampler.cpython-313.pyc +0 -0
  620. pyflow/stubs/std/__pycache__/shutil_stubs.cpython-313.pyc +0 -0
  621. pyflow/stubs/std/__pycache__/subprocess_stubs.cpython-313.pyc +0 -0
  622. pyflow/stubs/std/__pycache__/sys_stubs.cpython-313.pyc +0 -0
  623. pyflow/stubs/std/__pycache__/tempfile_stubs.cpython-313.pyc +0 -0
  624. pyflow/stubs/std/__pycache__/threading_stubs.cpython-313.pyc +0 -0
  625. pyflow/stubs/std/__pycache__/time_stubs.cpython-313.pyc +0 -0
  626. pyflow/stubs/std/argparse_stubs.py +200 -0
  627. pyflow/stubs/std/collections_stubs.py +77 -0
  628. pyflow/stubs/std/container.py +374 -0
  629. pyflow/stubs/std/copy_stubs.py +54 -0
  630. pyflow/stubs/std/csv_stubs.py +187 -0
  631. pyflow/stubs/std/datetime_stubs.py +70 -0
  632. pyflow/stubs/std/functools_stubs.py +61 -0
  633. pyflow/stubs/std/hashlib_stubs.py +198 -0
  634. pyflow/stubs/std/interpreter.py +360 -0
  635. pyflow/stubs/std/io_stubs.py +339 -0
  636. pyflow/stubs/std/itertools_stubs.py +89 -0
  637. pyflow/stubs/std/json_stubs.py +40 -0
  638. pyflow/stubs/std/llfunc.py +240 -0
  639. pyflow/stubs/std/logging_stubs.py +295 -0
  640. pyflow/stubs/std/mathstubs.py +393 -0
  641. pyflow/stubs/std/objects/__init__.py +9 -0
  642. pyflow/stubs/std/objects/__pycache__/__init__.cpython-313.pyc +0 -0
  643. pyflow/stubs/std/objects/__pycache__/float.cpython-313.pyc +0 -0
  644. pyflow/stubs/std/objects/__pycache__/int.cpython-313.pyc +0 -0
  645. pyflow/stubs/std/objects/__pycache__/str.cpython-313.pyc +0 -0
  646. pyflow/stubs/std/objects/float.py +248 -0
  647. pyflow/stubs/std/objects/int.py +221 -0
  648. pyflow/stubs/std/objects/str.py +175 -0
  649. pyflow/stubs/std/operator_stubs.py +148 -0
  650. pyflow/stubs/std/os_stubs.py +727 -0
  651. pyflow/stubs/std/pathlib_stubs.py +364 -0
  652. pyflow/stubs/std/pickle_stubs.py +97 -0
  653. pyflow/stubs/std/random.py +29 -0
  654. pyflow/stubs/std/re_stubs.py +178 -0
  655. pyflow/stubs/std/sampler.py +17 -0
  656. pyflow/stubs/std/shutil_stubs.py +212 -0
  657. pyflow/stubs/std/subprocess_stubs.py +149 -0
  658. pyflow/stubs/std/sys_stubs.py +226 -0
  659. pyflow/stubs/std/tempfile_stubs.py +129 -0
  660. pyflow/stubs/std/threading_stubs.py +328 -0
  661. pyflow/stubs/std/time_stubs.py +283 -0
  662. pyflow/stubs/stubcollector.py +463 -0
  663. pyflow/util/PADS/Automata.py +640 -0
  664. pyflow/util/PADS/BFS.py +28 -0
  665. pyflow/util/PADS/Biconnectivity.py +270 -0
  666. pyflow/util/PADS/Bipartite.py +89 -0
  667. pyflow/util/PADS/BipartiteMatching.py +129 -0
  668. pyflow/util/PADS/CardinalityMatching.py +316 -0
  669. pyflow/util/PADS/Chordal.py +33 -0
  670. pyflow/util/PADS/CubicHam.py +351 -0
  671. pyflow/util/PADS/DFS.py +100 -0
  672. pyflow/util/PADS/Eratosthenes.py +138 -0
  673. pyflow/util/PADS/GraphExamples.py +36 -0
  674. pyflow/util/PADS/Graphs.py +82 -0
  675. pyflow/util/PADS/GrayGraph.py +36 -0
  676. pyflow/util/PADS/IntegerPartitions.py +317 -0
  677. pyflow/util/PADS/LCA.py +349 -0
  678. pyflow/util/PADS/LCS.py +38 -0
  679. pyflow/util/PADS/LexBFS.py +32 -0
  680. pyflow/util/PADS/LongestIncreasingSubsequence.py +65 -0
  681. pyflow/util/PADS/Medium.py +414 -0
  682. pyflow/util/PADS/MinimumSpanningTree.py +26 -0
  683. pyflow/util/PADS/OrderedSequence.py +133 -0
  684. pyflow/util/PADS/PartialCube.py +227 -0
  685. pyflow/util/PADS/PartialOrder.py +129 -0
  686. pyflow/util/PADS/PartitionRefinement.py +85 -0
  687. pyflow/util/PADS/ReadUndirectedGraph.py +431 -0
  688. pyflow/util/PADS/Repetitivity.py +160 -0
  689. pyflow/util/PADS/SMAWK.py +190 -0
  690. pyflow/util/PADS/Sequence.py +118 -0
  691. pyflow/util/PADS/StrongConnectivity.py +106 -0
  692. pyflow/util/PADS/Sudoku.py +2028 -0
  693. pyflow/util/PADS/UnionFind.py +63 -0
  694. pyflow/util/PADS/Util.py +28 -0
  695. pyflow/util/PADS/Wrap.py +79 -0
  696. pyflow/util/PADS/__init__.py +13 -0
  697. pyflow/util/PADS/xyzGraph.py +127 -0
  698. pyflow/util/__init__.py +19 -0
  699. pyflow/util/antlr3/__init__.py +134 -0
  700. pyflow/util/antlr3/compat.py +12 -0
  701. pyflow/util/antlr3/constants.py +24 -0
  702. pyflow/util/antlr3/dfa.py +179 -0
  703. pyflow/util/antlr3/dottreegen.py +170 -0
  704. pyflow/util/antlr3/exceptions.py +346 -0
  705. pyflow/util/antlr3/extras.py +11 -0
  706. pyflow/util/antlr3/main.py +198 -0
  707. pyflow/util/antlr3/recognizers.py +1341 -0
  708. pyflow/util/antlr3/streams.py +1250 -0
  709. pyflow/util/antlr3/tokens.py +370 -0
  710. pyflow/util/antlr3/tree.py +2190 -0
  711. pyflow/util/antlr3/treewizard.py +566 -0
  712. pyflow/util/application/__init__.py +13 -0
  713. pyflow/util/application/async_utils.py +101 -0
  714. pyflow/util/application/compilerexceptions.py +21 -0
  715. pyflow/util/application/console.py +235 -0
  716. pyflow/util/application/errorhandler.py +241 -0
  717. pyflow/util/canonical.py +204 -0
  718. pyflow/util/debug/__init__.py +11 -0
  719. pyflow/util/debug/blame.py +94 -0
  720. pyflow/util/debug/debugger.py +105 -0
  721. pyflow/util/debug/trace.py +56 -0
  722. pyflow/util/graphalgorithim/__init__.py +17 -0
  723. pyflow/util/graphalgorithim/basic.py +77 -0
  724. pyflow/util/graphalgorithim/color.py +104 -0
  725. pyflow/util/graphalgorithim/djtree.py +299 -0
  726. pyflow/util/graphalgorithim/dominator.py +531 -0
  727. pyflow/util/graphalgorithim/exclusiongraph.py +624 -0
  728. pyflow/util/graphalgorithim/merge.py +221 -0
  729. pyflow/util/io/__init__.py +6 -0
  730. pyflow/util/io/dot.py +540 -0
  731. pyflow/util/io/filesystem.py +210 -0
  732. pyflow/util/io/formatting.py +71 -0
  733. pyflow/util/io/report.py +224 -0
  734. pyflow/util/io/xmloutput.py +270 -0
  735. pyflow/util/json_dict.py +92 -0
  736. pyflow/util/monkeypatch/__init__.py +21 -0
  737. pyflow/util/monkeypatch/xcollections.py +206 -0
  738. pyflow/util/monkeypatch/xmath.py +108 -0
  739. pyflow/util/monkeypatch/xnamedtuple.py +180 -0
  740. pyflow/util/monkeypatch/xtypes.py +56 -0
  741. pyflow/util/orderedset.py +420 -0
  742. pyflow/util/pydot.py +1998 -0
  743. pyflow/util/python/__init__.py +136 -0
  744. pyflow/util/python/apply.py +180 -0
  745. pyflow/util/python/calling.py +558 -0
  746. pyflow/util/python/opnames.py +153 -0
  747. pyflow/util/randomness.py +230 -0
  748. pyflow/util/singleton.py +112 -0
  749. pyflow/util/tvl.py +321 -0
  750. pyflow/util/typedispatch.py +342 -0
  751. pyflow/util/unionfind.py +169 -0
  752. pyflow_analysis-0.1.0.dist-info/METADATA +184 -0
  753. pyflow_analysis-0.1.0.dist-info/RECORD +757 -0
  754. pyflow_analysis-0.1.0.dist-info/WHEEL +5 -0
  755. pyflow_analysis-0.1.0.dist-info/entry_points.txt +2 -0
  756. pyflow_analysis-0.1.0.dist-info/licenses/LICENSE.txt +202 -0
  757. pyflow_analysis-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1969 @@
1
+ """Operation-level heap transfer methods (mixin for HeapTransferEngine).
2
+
3
+ This module contains the operation-level methods extracted from
4
+ :class:`HeapTransferEngine`. It is used as a mixin and should not be
5
+ instantiated directly.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from pyflow.analysis.ir_utils import (
11
+ actual_argument_expressions,
12
+ assigned_locals,
13
+ resolve_call_name,
14
+ )
15
+ from pyflow.language.python import ast as py_ast
16
+
17
+ from .heap_effects import (
18
+ CALL_RETURN_COPY,
19
+ CALL_RETURN_FRESH,
20
+ DYNAMIC_SUBSCRIPT_WILDCARD,
21
+ )
22
+ from .intrinsics import CALL_RETURN_NONE
23
+ from .model import (
24
+ HeapLocation,
25
+ HeapObjectIdentity,
26
+ HeapObjectKind,
27
+ UpdatePolicy,
28
+ )
29
+
30
+
31
+ class _TransferOpsMixin:
32
+ """Mixin providing operation-level transfer methods.
33
+
34
+ This class contains only methods; it does not define ``__init__``.
35
+ At runtime ``self`` is the :class:`HeapTransferEngine` instance.
36
+ """
37
+
38
+ def _prepared_target_writes(
39
+ self,
40
+ procedure: object,
41
+ operation: object,
42
+ ) -> tuple[object, ...] | None:
43
+ bases: tuple[HeapLocation, ...]
44
+ locations: tuple[HeapLocation, ...]
45
+ if isinstance(operation, (py_ast.SetAttr, py_ast.Store)):
46
+ bases = self.locations_for_expression(procedure, operation.expr)
47
+ self.locations_for_expression(procedure, operation.name)
48
+ if isinstance(operation, py_ast.Store) and getattr(
49
+ operation,
50
+ "fieldtype",
51
+ None,
52
+ ) in {"Dictionary", "Array"}:
53
+ locations = self.heap.dynamic_subscript_locations(
54
+ bases,
55
+ (f"[{self.effect_builder._path_component(operation.name)}]",),
56
+ )
57
+ self.locations_for_expression(procedure, operation.value)
58
+ else:
59
+ attribute = (
60
+ self.effect_builder._path_component(operation.name)
61
+ if isinstance(operation, py_ast.Store)
62
+ else self.effect_builder._constant_string(operation.name) or "*"
63
+ )
64
+ locations = self.heap.dynamic_attribute_locations(
65
+ bases,
66
+ (attribute,),
67
+ )
68
+ if isinstance(operation, py_ast.Store):
69
+ self.locations_for_expression(procedure, operation.value)
70
+ return self._prepared_writes_for_bases(locations, bases)
71
+ if isinstance(operation, py_ast.SetSubscript):
72
+ bases = self.locations_for_expression(procedure, operation.expr)
73
+ self.locations_for_expression(procedure, operation.subscript)
74
+ subscript = self.effect_builder._constant_subscript(operation.subscript)
75
+ locations = self.heap.dynamic_subscript_locations(
76
+ bases,
77
+ (subscript or DYNAMIC_SUBSCRIPT_WILDCARD,),
78
+ )
79
+ return self._prepared_writes_for_bases(locations, bases)
80
+ if isinstance(operation, py_ast.SetSlice):
81
+ bases = self.locations_for_expression(procedure, operation.expr)
82
+ for component in (operation.start, operation.stop, operation.step):
83
+ self.locations_for_expression(procedure, component)
84
+ locations = self.heap.dynamic_subscript_locations(
85
+ bases,
86
+ (DYNAMIC_SUBSCRIPT_WILDCARD,),
87
+ )
88
+ return self._prepared_writes_for_bases(locations, bases)
89
+ return None
90
+
91
+ def _prepared_writes_for_bases(
92
+ self,
93
+ locations: tuple[HeapLocation, ...],
94
+ bases: tuple[HeapLocation, ...],
95
+ ) -> tuple[object, ...]:
96
+ ambiguous = len({base.root for base in bases}) > 1
97
+ return tuple(
98
+ self.heap.write_for_location(
99
+ location,
100
+ policy=UpdatePolicy.WEAK if ambiguous else None,
101
+ )
102
+ for location in locations
103
+ )
104
+
105
+ def _prepared_delete_locations(
106
+ self,
107
+ procedure: object,
108
+ operation: object,
109
+ ) -> tuple[HeapLocation, ...] | None:
110
+ if isinstance(operation, py_ast.DeleteAttr):
111
+ bases = self.locations_for_expression(procedure, operation.expr)
112
+ self.locations_for_expression(procedure, operation.name)
113
+ attribute = self.effect_builder._constant_string(operation.name) or "*"
114
+ return self.heap.dynamic_attribute_locations(bases, (attribute,))
115
+ if isinstance(operation, py_ast.DeleteSubscript):
116
+ bases = self.locations_for_expression(procedure, operation.expr)
117
+ self.locations_for_expression(procedure, operation.subscript)
118
+ subscript = self.effect_builder._constant_subscript(operation.subscript)
119
+ return self.heap.dynamic_subscript_locations(
120
+ bases,
121
+ (subscript or DYNAMIC_SUBSCRIPT_WILDCARD,),
122
+ )
123
+ if isinstance(operation, py_ast.DeleteSlice):
124
+ bases = self.locations_for_expression(procedure, operation.expr)
125
+ for component in (operation.start, operation.stop, operation.step):
126
+ self.locations_for_expression(procedure, component)
127
+ return self.heap.dynamic_subscript_locations(
128
+ bases,
129
+ (DYNAMIC_SUBSCRIPT_WILDCARD,),
130
+ )
131
+ return None
132
+
133
+ @staticmethod
134
+ def _immediate_escape_locations(
135
+ operation: object,
136
+ effect: object,
137
+ ) -> tuple[HeapLocation, ...]:
138
+ """Return effect escapes that are externally reachable immediately.
139
+
140
+ Stores into fields, subscripts, cells, or collection mutators create
141
+ heap reachability edges. The stored values escape only if the
142
+ destination root is, or later becomes, escaped; the fixed-point escape
143
+ propagation handles that through ``HeapState``.
144
+ """
145
+ escapes = getattr(effect, "escapes", ())
146
+ writes = getattr(effect, "writes", ())
147
+ if not writes or not escapes:
148
+ return escapes
149
+ if isinstance(operation, py_ast.SetGlobal):
150
+ return escapes
151
+ if isinstance(
152
+ operation,
153
+ (
154
+ py_ast.SetAttr,
155
+ py_ast.SetSubscript,
156
+ py_ast.SetSlice,
157
+ py_ast.SetCellDeref,
158
+ py_ast.Store,
159
+ ),
160
+ ):
161
+ return ()
162
+ call = _TransferOpsMixin._call_expression(operation)
163
+ if call is not None:
164
+ return ()
165
+ return escapes
166
+
167
+ def locations_for_expression(
168
+ self,
169
+ procedure: object,
170
+ expression: object,
171
+ ) -> tuple[HeapLocation, ...]:
172
+ cache = (
173
+ self._operation_expression_caches[-1]
174
+ if self._operation_expression_caches
175
+ else None
176
+ )
177
+ cacheable = not isinstance(
178
+ expression,
179
+ (
180
+ HeapLocation,
181
+ py_ast.Local,
182
+ py_ast.GetGlobal,
183
+ py_ast.GetCell,
184
+ py_ast.GetCellDeref,
185
+ py_ast.Cell,
186
+ py_ast.Input,
187
+ ),
188
+ )
189
+ if cache is not None and cacheable:
190
+ cached = cache.get(id(expression))
191
+ if cached is not None:
192
+ return cached
193
+ self._record_exception_prefix()
194
+ result = self._locations_for_expression_impl(procedure, expression)
195
+ self._record_exception_prefix()
196
+ if cache is not None and cacheable:
197
+ cache[id(expression)] = result
198
+ return result
199
+
200
+ def _locations_for_expression_impl(
201
+ self,
202
+ procedure: object,
203
+ expression: object,
204
+ ) -> tuple[HeapLocation, ...]:
205
+ """Best-effort locations read by an expression."""
206
+ if expression is None:
207
+ return ()
208
+ if isinstance(expression, HeapLocation):
209
+ return (expression,)
210
+ if isinstance(expression, py_ast.DoNotCare):
211
+ return ()
212
+ if isinstance(expression, py_ast.Input):
213
+ return self.locations_for_expression(procedure, expression.lcl)
214
+ if isinstance(expression, py_ast.Cell):
215
+ return (self.effect_builder.cell_location(expression, procedure),)
216
+ if isinstance(expression, py_ast.TypeParam):
217
+ bound = self.locations_for_expression(procedure, expression.bound)
218
+ parameter = HeapLocation(
219
+ self.heap.allocation_object(
220
+ procedure,
221
+ expression,
222
+ label=f"type parameter {expression.name}",
223
+ context=self._current_context,
224
+ )
225
+ )
226
+ if bound:
227
+ self.state.write(
228
+ self.heap.dynamic_attribute_location(parameter, "__bound__"),
229
+ bound,
230
+ UpdatePolicy.STRONG,
231
+ )
232
+ return tuple(dict.fromkeys((parameter, *bound)))
233
+ if isinstance(expression, py_ast.TypeParams):
234
+ parameters = self._merge_expression_locations(
235
+ procedure,
236
+ *getattr(expression, "params", ()),
237
+ )
238
+ collection = HeapLocation(
239
+ self.heap.allocation_object(
240
+ procedure,
241
+ expression,
242
+ label="type parameters",
243
+ context=self._current_context,
244
+ )
245
+ )
246
+ if parameters:
247
+ self.state.write(
248
+ self.heap.dynamic_subscript_location(
249
+ collection,
250
+ DYNAMIC_SUBSCRIPT_WILDCARD,
251
+ ),
252
+ parameters,
253
+ UpdatePolicy.WEAK,
254
+ )
255
+ return tuple(dict.fromkeys((collection, *parameters)))
256
+ if isinstance(expression, py_ast.Local):
257
+ declared = self._declared_location(procedure, expression)
258
+ if declared is not None:
259
+ return self._read_heap_locations((declared,))
260
+ locations = self.heap.locations_for_local(procedure, expression)
261
+ if locations:
262
+ return locations
263
+ definition_local = self._definition_locals.get(
264
+ (id(procedure), expression.name)
265
+ )
266
+ if definition_local is not None:
267
+ locations = self.heap.locations_for_local(
268
+ procedure,
269
+ definition_local,
270
+ )
271
+ if locations:
272
+ return locations
273
+ if isinstance(procedure, py_ast.ClassDef):
274
+ outer_locations = self._outer_local_locations(expression)
275
+ if outer_locations:
276
+ return outer_locations
277
+ obj = self.heap.local_object(procedure, expression)
278
+ self.heap.bind_local_to_object(procedure, expression, obj)
279
+ return self.heap.locations_for_local(procedure, expression)
280
+ if isinstance(expression, py_ast.GetGlobal):
281
+ location = self.effect_builder.global_location(procedure, expression.name)
282
+ return self._read_heap_locations((location,))
283
+ if isinstance(expression, (py_ast.GetCell, py_ast.GetCellDeref)):
284
+ location = self.effect_builder.cell_location(expression.cell, procedure)
285
+ return self._read_heap_locations((location,))
286
+ if isinstance(expression, (py_ast.GetAttr, py_ast.Load)):
287
+ bases = self.locations_for_expression(procedure, expression.expr)
288
+ name_locations = self.locations_for_expression(
289
+ procedure,
290
+ expression.name,
291
+ )
292
+ attribute = (
293
+ self.effect_builder._path_component(expression.name)
294
+ if isinstance(expression, py_ast.Load)
295
+ else self.effect_builder._constant_string(expression.name) or "*"
296
+ )
297
+ if isinstance(expression, py_ast.Load) and getattr(
298
+ expression, "fieldtype", None
299
+ ) in {"Dictionary", "Array"}:
300
+ locations = self.heap.dynamic_subscript_locations(
301
+ bases,
302
+ (f"[{attribute}]",),
303
+ )
304
+ else:
305
+ locations = self.heap.dynamic_attribute_locations(
306
+ bases,
307
+ (attribute,),
308
+ )
309
+ values = list(self._read_heap_locations(locations))
310
+ super_members: list[HeapLocation] = []
311
+ super_receivers: list[HeapLocation] = []
312
+ for base in bases:
313
+ current_classes = self.state.read(
314
+ self.heap.dynamic_attribute_location(
315
+ base,
316
+ "__super_class__",
317
+ ),
318
+ fallback=(),
319
+ )
320
+ receivers = self.state.read(
321
+ self.heap.dynamic_attribute_location(base, "__super_self__"),
322
+ fallback=(),
323
+ )
324
+ for current_class in current_classes:
325
+ mro = self._known_class_mro(current_class)
326
+ for next_class in mro[1:]:
327
+ stored = self.state.read(
328
+ self.heap.dynamic_attribute_location(
329
+ next_class,
330
+ attribute,
331
+ ),
332
+ fallback=(),
333
+ )
334
+ if stored:
335
+ super_members.extend(stored)
336
+ super_receivers.extend(receivers)
337
+ break
338
+ values.extend(super_members)
339
+ for member in super_members:
340
+ code = self._function_codes_by_root.get(member.root)
341
+ if code is None:
342
+ continue
343
+ bound = HeapLocation(
344
+ self.heap.allocation_object(
345
+ procedure,
346
+ ("super-bound-method", expression, member),
347
+ label=f"bound super method {attribute}",
348
+ context=self._current_context,
349
+ )
350
+ )
351
+ self._bound_methods_by_root[bound.root] = (
352
+ code,
353
+ tuple(dict.fromkeys(super_receivers)),
354
+ )
355
+ values.append(bound)
356
+ values.extend(
357
+ self._evaluate_known_protocol(
358
+ procedure,
359
+ bases,
360
+ "__getattribute__",
361
+ (name_locations,),
362
+ )
363
+ )
364
+ values.extend(
365
+ self._evaluate_known_protocol(
366
+ procedure,
367
+ bases,
368
+ "__getattr__",
369
+ (name_locations,),
370
+ )
371
+ )
372
+ if attribute != "*":
373
+ classes = tuple(
374
+ dict.fromkeys(
375
+ class_location
376
+ for base in bases
377
+ for class_location in self.state.read(
378
+ self.heap.dynamic_attribute_location(base, "__class__"),
379
+ fallback=(),
380
+ )
381
+ )
382
+ )
383
+ class_values = self._class_attribute_values(classes, attribute)
384
+ bound_values: list[HeapLocation] = []
385
+ ordinary_class_values: list[HeapLocation] = []
386
+ for member in class_values:
387
+ code = self._function_codes_by_root.get(member.root)
388
+ if code is None:
389
+ ordinary_class_values.append(member)
390
+ continue
391
+ kind = self._function_binding_kinds.get(
392
+ member.root,
393
+ "instance",
394
+ )
395
+ if kind == "property":
396
+ values.extend(
397
+ self._evaluate_known_code(
398
+ procedure,
399
+ code,
400
+ (bases,),
401
+ )
402
+ )
403
+ continue
404
+ if kind == "staticmethod":
405
+ ordinary_class_values.append(member)
406
+ continue
407
+ receivers = classes if kind == "classmethod" else bases
408
+ bound = HeapLocation(
409
+ self.heap.allocation_object(
410
+ procedure,
411
+ ("bound-method", expression, member, receivers),
412
+ label=f"bound method {attribute}",
413
+ context=self._current_context,
414
+ )
415
+ )
416
+ self._bound_methods_by_root[bound.root] = (code, receivers)
417
+ self.state.write(
418
+ self.heap.dynamic_attribute_location(bound, "__func__"),
419
+ (member,),
420
+ UpdatePolicy.STRONG,
421
+ )
422
+ self.state.write(
423
+ self.heap.dynamic_attribute_location(bound, "__self__"),
424
+ receivers,
425
+ UpdatePolicy.STRONG,
426
+ )
427
+ bound_values.append(bound)
428
+ values.extend(ordinary_class_values)
429
+ values.extend(bound_values)
430
+ values.extend(
431
+ self._evaluate_known_protocol(
432
+ procedure,
433
+ tuple(ordinary_class_values),
434
+ "__get__",
435
+ (bases, classes),
436
+ )
437
+ )
438
+ return tuple(dict.fromkeys(values))
439
+ if isinstance(expression, py_ast.GetSubscript):
440
+ bases = self.locations_for_expression(procedure, expression.expr)
441
+ subscript_locations = self.locations_for_expression(
442
+ procedure,
443
+ expression.subscript,
444
+ )
445
+ protocol_values = self._evaluate_known_protocol(
446
+ procedure,
447
+ bases,
448
+ "__getitem__",
449
+ (subscript_locations,),
450
+ )
451
+ subscript = self.effect_builder._constant_subscript(expression.subscript)
452
+ if subscript is None:
453
+ values: list[HeapLocation] = []
454
+ for base in bases:
455
+ wildcard = self.heap.dynamic_subscript_location(
456
+ base,
457
+ DYNAMIC_SUBSCRIPT_WILDCARD,
458
+ )
459
+ values.extend(self.state.read_contained(wildcard))
460
+ if values:
461
+ return tuple(dict.fromkeys((*values, *protocol_values)))
462
+ if protocol_values:
463
+ return protocol_values
464
+ return self.heap.dynamic_subscript_locations(
465
+ bases,
466
+ (DYNAMIC_SUBSCRIPT_WILDCARD,),
467
+ )
468
+ locations = self.heap.dynamic_subscript_locations(
469
+ bases,
470
+ (subscript, DYNAMIC_SUBSCRIPT_WILDCARD),
471
+ )
472
+ return tuple(
473
+ dict.fromkeys(
474
+ (*self._read_heap_locations(locations), *protocol_values)
475
+ )
476
+ )
477
+ if isinstance(expression, py_ast.DirectCall) and isinstance(
478
+ expression.code,
479
+ py_ast.Code,
480
+ ):
481
+ return self._evaluate_direct_call_expression(procedure, expression)
482
+ if isinstance(expression, (py_ast.Call, py_ast.DirectCall, py_ast.MethodCall)):
483
+ # Calls are expressions in Python, so their control-flow and heap
484
+ # effects must be applied even when nested inside another
485
+ # expression rather than materialized by an Assign/Discard node.
486
+ self._apply_call_transfer(procedure, expression)
487
+ application_key = self._call_application_key(expression)
488
+ finite_values = self._finite_call_results.get(application_key)
489
+ if finite_values is not None:
490
+ return finite_values
491
+ operand_locations = self._evaluate_call_operands(
492
+ procedure,
493
+ expression,
494
+ )
495
+ known_classes = self._known_class_locations(
496
+ procedure,
497
+ expression,
498
+ operand_locations,
499
+ )
500
+ if known_classes:
501
+ return self._evaluate_known_class_targets(
502
+ procedure,
503
+ expression,
504
+ known_classes,
505
+ operand_locations,
506
+ self.effect_builder._call_result_label(expression),
507
+ )
508
+ kind = self.effect_builder.call_return_kind(expression)
509
+ if kind == CALL_RETURN_NONE:
510
+ return ()
511
+ modeled = self._modeled_call_return_locations(
512
+ procedure,
513
+ expression,
514
+ kind,
515
+ operand_locations,
516
+ )
517
+ if modeled:
518
+ self._attach_known_class(procedure, expression, modeled)
519
+ return tuple(
520
+ dict.fromkeys(
521
+ (
522
+ *modeled,
523
+ *self._protocol_call_results.get(application_key, ()),
524
+ )
525
+ )
526
+ )
527
+ result = (
528
+ HeapLocation(
529
+ self.effect_builder.call_return_object(procedure, expression)
530
+ ),
531
+ )
532
+ call_name = resolve_call_name(expression)
533
+ if (
534
+ kind == CALL_RETURN_FRESH
535
+ and call_name is not None
536
+ and (
537
+ self._module_owner(procedure),
538
+ call_name.rsplit(".", 1)[-1],
539
+ )
540
+ in self._class_definitions
541
+ ):
542
+ result = tuple(
543
+ dict.fromkeys(
544
+ (*result, self._external_value_location(procedure))
545
+ )
546
+ )
547
+ if kind == CALL_RETURN_COPY:
548
+ self._copy_call_result_contents(
549
+ procedure,
550
+ None,
551
+ expression,
552
+ result,
553
+ )
554
+ self._attach_known_class(procedure, expression, result)
555
+ return tuple(
556
+ dict.fromkeys(
557
+ (
558
+ *result,
559
+ *self._protocol_call_results.get(application_key, ()),
560
+ )
561
+ )
562
+ )
563
+ if isinstance(
564
+ expression,
565
+ (
566
+ py_ast.BuildTuple,
567
+ py_ast.BuildList,
568
+ py_ast.BuildSet,
569
+ py_ast.BuildMap,
570
+ py_ast.BuildSlice,
571
+ py_ast.Allocate,
572
+ ),
573
+ ):
574
+ if isinstance(expression, py_ast.BuildSlice):
575
+ for component in (
576
+ expression.start,
577
+ expression.stop,
578
+ expression.step,
579
+ ):
580
+ self.locations_for_expression(procedure, component)
581
+ elif isinstance(expression, py_ast.Allocate):
582
+ self.locations_for_expression(procedure, expression.expr)
583
+ allocation = HeapLocation(
584
+ self.heap.allocation_object(
585
+ procedure,
586
+ expression,
587
+ label=self.effect_builder._allocation_label(expression),
588
+ context=self._current_context,
589
+ )
590
+ )
591
+ if isinstance(
592
+ expression,
593
+ (py_ast.BuildTuple, py_ast.BuildList, py_ast.BuildSet, py_ast.BuildMap),
594
+ ):
595
+ self.state.complete_roots.add(allocation.root)
596
+ for argument in getattr(expression, "args", ()):
597
+ self.locations_for_expression(procedure, argument)
598
+ self._write_collection_literal_elements(
599
+ procedure,
600
+ allocation,
601
+ expression,
602
+ self._collection_literal_values(expression),
603
+ )
604
+ elif isinstance(expression, py_ast.BuildSlice):
605
+ for slice_field, component in (
606
+ ("start", expression.start),
607
+ ("stop", expression.stop),
608
+ ("step", expression.step),
609
+ ):
610
+ component_locations = self.locations_for_expression(
611
+ procedure,
612
+ component,
613
+ )
614
+ if component_locations:
615
+ self.state.write(
616
+ self.heap.dynamic_attribute_location(
617
+ allocation,
618
+ slice_field,
619
+ ),
620
+ component_locations,
621
+ UpdatePolicy.STRONG,
622
+ )
623
+ return (allocation,)
624
+ if isinstance(expression, py_ast.MakeFunction):
625
+ function = HeapLocation(
626
+ self.heap.allocation_object(
627
+ procedure,
628
+ expression,
629
+ label="function",
630
+ context=self._current_context,
631
+ )
632
+ )
633
+ default_locations = self._merge_expression_locations(
634
+ procedure,
635
+ *getattr(expression, "defaults", ()),
636
+ )
637
+ if default_locations:
638
+ self.state.write(
639
+ self.heap.dynamic_attribute_location(function, "__defaults__"),
640
+ default_locations,
641
+ UpdatePolicy.STRONG,
642
+ )
643
+ closure_locations = self._merge_expression_locations(
644
+ procedure,
645
+ *getattr(expression, "cells", ()),
646
+ )
647
+ if closure_locations:
648
+ self.state.write(
649
+ self.heap.dynamic_attribute_location(function, "__closure__"),
650
+ closure_locations,
651
+ UpdatePolicy.STRONG,
652
+ )
653
+ if isinstance(expression.code, py_ast.Code):
654
+ self._function_codes_by_root[function.root] = expression.code
655
+ self._function_binding_kinds[function.root] = "instance"
656
+ self._lexical_parents.setdefault(id(expression.code), procedure)
657
+ self._module_owners.setdefault(
658
+ id(expression.code),
659
+ self._module_owner(procedure),
660
+ )
661
+ return (function,)
662
+ if isinstance(expression, (py_ast.GetIter, py_ast.AsyncGetIter)):
663
+ sources = self.locations_for_expression(procedure, expression.expr)
664
+ protocol_values = self._evaluate_known_protocol(
665
+ procedure,
666
+ sources,
667
+ "__aiter__"
668
+ if isinstance(expression, py_ast.AsyncGetIter)
669
+ else "__iter__",
670
+ )
671
+ if not protocol_values and isinstance(expression, py_ast.GetIter):
672
+ protocol_values = self._evaluate_known_protocol(
673
+ procedure,
674
+ sources,
675
+ "__getitem__",
676
+ ((self._external_value_location(procedure),),),
677
+ )
678
+ iterator = HeapLocation(
679
+ self.heap.allocation_object(
680
+ procedure,
681
+ expression,
682
+ label="async iterator" if isinstance(expression, py_ast.AsyncGetIter) else "iterator",
683
+ context=self._current_context,
684
+ )
685
+ )
686
+ self._copy_locations(sources, (iterator,))
687
+ self.state.write(
688
+ self.heap.dynamic_attribute_location(iterator, "__iterable__"),
689
+ sources,
690
+ UpdatePolicy.STRONG,
691
+ )
692
+ return tuple(
693
+ dict.fromkeys(
694
+ (iterator, *sources, self._external_value_location(procedure))
695
+ if not protocol_values
696
+ else (iterator, *sources, *protocol_values)
697
+ )
698
+ )
699
+ if isinstance(expression, py_ast.GetSlice):
700
+ bases = self.locations_for_expression(procedure, expression.expr)
701
+ components: list[HeapLocation] = []
702
+ for component in (
703
+ expression.start,
704
+ expression.stop,
705
+ expression.step,
706
+ ):
707
+ components.extend(
708
+ self.locations_for_expression(procedure, component)
709
+ )
710
+ protocol_values = self._evaluate_known_protocol(
711
+ procedure,
712
+ bases,
713
+ "__getitem__",
714
+ (tuple(dict.fromkeys(components)),),
715
+ )
716
+ sliced = HeapLocation(
717
+ self.heap.allocation_object(
718
+ procedure,
719
+ expression,
720
+ label="slice result",
721
+ context=self._current_context,
722
+ )
723
+ )
724
+ self._copy_locations(bases, (sliced,))
725
+ return tuple(
726
+ dict.fromkeys(
727
+ (
728
+ *bases,
729
+ sliced,
730
+ *protocol_values,
731
+ self._external_value_location(procedure),
732
+ )
733
+ )
734
+ )
735
+ if isinstance(expression, (py_ast.UnaryPrefixOp,)):
736
+ operand = self.locations_for_expression(procedure, expression.expr)
737
+ protocol = {
738
+ "+": "__pos__",
739
+ "-": "__neg__",
740
+ "~": "__invert__",
741
+ }.get(expression.op)
742
+ protocol_values = (
743
+ self._evaluate_known_protocol(procedure, operand, protocol)
744
+ if protocol is not None
745
+ else ()
746
+ )
747
+ return tuple(
748
+ dict.fromkeys(
749
+ (
750
+ *operand,
751
+ *protocol_values,
752
+ self._external_value_location(procedure),
753
+ )
754
+ )
755
+ )
756
+ if isinstance(expression, py_ast.BinaryOp):
757
+ left = self.locations_for_expression(procedure, expression.left)
758
+ right = self.locations_for_expression(procedure, expression.right)
759
+ if expression.op in {"in", "not in"}:
760
+ protocol_values = self._evaluate_known_protocol(
761
+ procedure,
762
+ right,
763
+ "__contains__",
764
+ (left,),
765
+ )
766
+ else:
767
+ protocol = self._binary_protocol_name(expression.op)
768
+ reflected = self._reflected_binary_protocol_name(expression.op)
769
+ if reflected is None:
770
+ reflected = self._reflected_comparison_protocol_name(
771
+ expression.op
772
+ )
773
+ protocol_values = tuple(
774
+ dict.fromkeys(
775
+ (
776
+ *(
777
+ self._evaluate_known_protocol(
778
+ procedure, left, protocol, (right,)
779
+ )
780
+ if protocol is not None
781
+ else ()
782
+ ),
783
+ *(
784
+ self._evaluate_known_protocol(
785
+ procedure, right, reflected, (left,)
786
+ )
787
+ if reflected is not None
788
+ else ()
789
+ ),
790
+ )
791
+ )
792
+ )
793
+ return tuple(
794
+ dict.fromkeys(
795
+ (
796
+ *left,
797
+ *right,
798
+ *protocol_values,
799
+ self._external_value_location(procedure),
800
+ )
801
+ )
802
+ )
803
+ if isinstance(expression, (py_ast.ConvertToBool, py_ast.Not)):
804
+ operand = self.locations_for_expression(procedure, expression.expr)
805
+ bool_values = self._evaluate_known_protocol(
806
+ procedure,
807
+ operand,
808
+ "__bool__",
809
+ )
810
+ if not bool_values:
811
+ self._evaluate_known_protocol(
812
+ procedure,
813
+ operand,
814
+ "__len__",
815
+ )
816
+ return ()
817
+ if isinstance(expression, (py_ast.Is, py_ast.Check)):
818
+ self.locations_for_expression(procedure, expression.left if isinstance(expression, py_ast.Is) else expression.expr)
819
+ if isinstance(expression, py_ast.Is):
820
+ self.locations_for_expression(procedure, expression.right)
821
+ else:
822
+ self.locations_for_expression(procedure, expression.name)
823
+ return ()
824
+ if isinstance(expression, py_ast.Await):
825
+ awaitable = self.locations_for_expression(procedure, expression.expr)
826
+ protocol_values = self._evaluate_known_protocol(
827
+ procedure,
828
+ awaitable,
829
+ "__await__",
830
+ )
831
+ resumed = self._resume_deferred_activations(
832
+ procedure,
833
+ awaitable,
834
+ use_yields=False,
835
+ )
836
+ self.heap.mark_all_escaped(awaitable)
837
+ self.state.mark_escaped(awaitable)
838
+ return tuple(
839
+ dict.fromkeys(
840
+ (
841
+ *resumed,
842
+ *protocol_values,
843
+ *awaitable,
844
+ self._external_value_location(procedure),
845
+ )
846
+ )
847
+ )
848
+ if isinstance(expression, (py_ast.Yield, py_ast.AsyncYield)):
849
+ yielded = self.locations_for_expression(procedure, expression.expr)
850
+ self.state.set_yields(procedure, yielded)
851
+ self.heap.mark_all_escaped(yielded)
852
+ self.state.mark_escaped(yielded)
853
+ if self._yield_state_stack:
854
+ event_state = self._capture_flow_state()
855
+ for depth in self.state.current_yield_depths(procedure):
856
+ self._yield_state_stack[-1].append(
857
+ (depth, event_state, yielded)
858
+ )
859
+ self.state.advance_yield_depths(procedure)
860
+ if self._resume_input_stack:
861
+ target_depth, sent = self._resume_input_stack[-1]
862
+ if target_depth in self.state.current_yield_depths(procedure):
863
+ return sent
864
+ return (self._external_value_location(procedure),)
865
+ if isinstance(expression, py_ast.YieldFrom):
866
+ yielded = self.locations_for_expression(procedure, expression.expr)
867
+ protocol_values = self._evaluate_known_protocol(
868
+ procedure,
869
+ yielded,
870
+ "__iter__",
871
+ )
872
+ resumed = self._resume_deferred_activations(
873
+ procedure,
874
+ yielded,
875
+ use_yields=True,
876
+ )
877
+ expanded = tuple(
878
+ dict.fromkeys((*resumed, *self._contained_values(yielded)))
879
+ )
880
+ self.state.set_yields(
881
+ procedure,
882
+ expanded or yielded,
883
+ )
884
+ self.heap.mark_all_escaped(yielded)
885
+ self.state.mark_escaped(yielded)
886
+ if self._yield_state_stack:
887
+ event_state = self._capture_flow_state()
888
+ for depth in self.state.current_yield_depths(procedure):
889
+ self._yield_state_stack[-1].append(
890
+ (depth, event_state, expanded or yielded)
891
+ )
892
+ self.state.advance_yield_depths(procedure)
893
+ return tuple(
894
+ dict.fromkeys(
895
+ (
896
+ *yielded,
897
+ *protocol_values,
898
+ self._external_value_location(procedure),
899
+ )
900
+ )
901
+ )
902
+ if isinstance(expression, (py_ast.ShortCircutAnd, py_ast.ShortCircutOr)):
903
+ terms = tuple(getattr(expression, "terms", ()))
904
+ if not terms:
905
+ return ()
906
+ possible_locations: list[HeapLocation] = []
907
+ prefix_states: list[object] = []
908
+ for term in terms:
909
+ possible_locations.extend(
910
+ self.locations_for_expression(procedure, term)
911
+ )
912
+ # Evaluation may stop after every term. Joining all prefixes
913
+ # preserves both skipped and executed side effects from later
914
+ # terms without pretending they execute unconditionally.
915
+ prefix_states.append(self._capture_flow_state())
916
+ self._restore_flow_state(
917
+ self._join_flow_states(tuple(prefix_states))
918
+ )
919
+ return tuple(dict.fromkeys(possible_locations))
920
+ if isinstance(expression, py_ast.ConditionalExpr):
921
+ self.locations_for_expression(procedure, expression.test)
922
+ branch_entry = self._capture_flow_state()
923
+ self._restore_flow_state(branch_entry)
924
+ body_locations = self.locations_for_expression(
925
+ procedure,
926
+ expression.body,
927
+ )
928
+ body_state = self._capture_flow_state()
929
+ self._restore_flow_state(branch_entry)
930
+ else_locations = self.locations_for_expression(
931
+ procedure,
932
+ expression.orelse,
933
+ )
934
+ else_state = self._capture_flow_state()
935
+ self._restore_flow_state(
936
+ self._join_flow_states((body_state, else_state))
937
+ )
938
+ return tuple(dict.fromkeys((*body_locations, *else_locations)))
939
+ if isinstance(expression, py_ast.NamedExpr):
940
+ locations = self.locations_for_expression(procedure, expression.value)
941
+ if locations:
942
+ self._bind_runtime_local(
943
+ procedure,
944
+ expression.target,
945
+ locations,
946
+ )
947
+ else:
948
+ self._clear_runtime_local(procedure, expression.target)
949
+ return locations
950
+ if isinstance(expression, py_ast.Existing):
951
+ value = getattr(expression.object, "pyobj", None)
952
+ if isinstance(
953
+ value,
954
+ (str, bytes, int, float, complex, bool, type(None)),
955
+ ):
956
+ return ()
957
+ return (
958
+ HeapLocation(
959
+ self.heap.external_object(
960
+ ("existing", id(expression.object)),
961
+ label=repr(value),
962
+ stable_identity=True,
963
+ )
964
+ ),
965
+ )
966
+ if isinstance(expression, py_ast.Expression):
967
+ # The IR is extensible. A newly introduced reference-producing
968
+ # expression must never silently become "no reference" merely
969
+ # because this dispatcher has not gained a precision rule yet.
970
+ if hasattr(expression, "visitChildren"):
971
+ children: list[object] = []
972
+ expression.visitChildren(children.append)
973
+ for child in children:
974
+ self.locations_for_expression(procedure, child)
975
+ return (
976
+ HeapLocation(
977
+ self.heap.unknown_object(
978
+ (
979
+ "unsupported-expression",
980
+ type(expression).__name__,
981
+ self._context_token(expression),
982
+ ),
983
+ label=f"unknown {type(expression).__name__} result",
984
+ )
985
+ ),
986
+ )
987
+ return ()
988
+
989
+ def _class_attribute_values(
990
+ self,
991
+ classes: tuple[HeapLocation, ...],
992
+ attribute: str,
993
+ ) -> tuple[HeapLocation, ...]:
994
+ values: list[HeapLocation] = []
995
+ for root_class in classes:
996
+ for class_location in self._known_class_mro(root_class):
997
+ stored = self.state.read(
998
+ self.heap.dynamic_attribute_location(
999
+ class_location,
1000
+ attribute,
1001
+ ),
1002
+ fallback=(),
1003
+ )
1004
+ if stored:
1005
+ values.extend(stored)
1006
+ # Python attribute resolution stops at the first class in
1007
+ # the MRO defining the attribute.
1008
+ break
1009
+ return tuple(dict.fromkeys(values))
1010
+
1011
+ def _external_value_location(self, procedure: object) -> HeapLocation:
1012
+ return HeapLocation(
1013
+ self.heap.unknown_object(
1014
+ ("external-value", id(procedure)),
1015
+ label="external value",
1016
+ )
1017
+ )
1018
+
1019
+ def _read_heap_locations(
1020
+ self,
1021
+ locations: tuple[HeapLocation, ...],
1022
+ ) -> tuple[HeapLocation, ...]:
1023
+ values: list[HeapLocation] = []
1024
+ for location in locations:
1025
+ stored = self.state.read(location, fallback=())
1026
+ if stored:
1027
+ values.extend(stored)
1028
+ elif (
1029
+ not self.state.definitely_absent(location)
1030
+ and location.root not in self.state.complete_roots
1031
+ ):
1032
+ values.append(
1033
+ HeapLocation(
1034
+ self.heap.unknown_object(
1035
+ (
1036
+ "read",
1037
+ location,
1038
+ tuple(sorted(self.state.version_for(location))),
1039
+ ),
1040
+ label=f"unknown value at {location!r}",
1041
+ identity=(
1042
+ HeapObjectIdentity.SYMBOLIC
1043
+ if not location.is_nested()
1044
+ else HeapObjectIdentity.SUMMARY
1045
+ ),
1046
+ )
1047
+ )
1048
+ )
1049
+ return tuple(dict.fromkeys(values))
1050
+
1051
+ def _outer_local_locations(
1052
+ self,
1053
+ local: py_ast.Local,
1054
+ ) -> tuple[HeapLocation, ...]:
1055
+ local_id = id(local)
1056
+ local_name = getattr(local, "name", None)
1057
+ locations: list[HeapLocation] = []
1058
+ keys = set(self.heap.storage_overrides) | set(self.heap.allocation_sites)
1059
+ for key in keys:
1060
+ if (
1061
+ key[1] != local_id
1062
+ and (
1063
+ not isinstance(local_name, str)
1064
+ or self.heap._local_names.get(key) != local_name
1065
+ )
1066
+ ):
1067
+ continue
1068
+ storage = self.heap.storage_overrides.get(key)
1069
+ if storage is None:
1070
+ site = self.heap.allocation_sites.get(key)
1071
+ storage = (
1072
+ self.heap.site_storage.get(site, ())
1073
+ if site is not None
1074
+ else ()
1075
+ )
1076
+ locations.extend(
1077
+ self.heap.location_for_raw(raw) for raw in storage
1078
+ )
1079
+ return tuple(dict.fromkeys(locations))
1080
+
1081
+ def _declared_location(
1082
+ self,
1083
+ procedure: object,
1084
+ local: py_ast.Local,
1085
+ ) -> HeapLocation | None:
1086
+ name = getattr(local, "name", None)
1087
+ if not name:
1088
+ return None
1089
+ if name in self._global_declarations.get(id(procedure), set()):
1090
+ return self.effect_builder.global_location(procedure, name)
1091
+ if name in self._nonlocal_declarations.get(id(procedure), set()):
1092
+ owner = self._nonlocal_owners.get((id(procedure), name))
1093
+ if owner is None:
1094
+ owner = self._register_nonlocal_binding(procedure, name)
1095
+ return self._lexical_cell_location(owner, name)
1096
+ if name in self._captured_names_by_scope.get(id(procedure), set()):
1097
+ return self._lexical_cell_location(procedure, name)
1098
+ return None
1099
+
1100
+ def _register_nonlocal_binding(self, procedure: object, name: str) -> object:
1101
+ owner = self._nearest_lexical_binding(procedure, name)
1102
+ self._nonlocal_owners[(id(procedure), name)] = owner
1103
+ self._captured_names_by_scope.setdefault(id(owner), set()).add(name)
1104
+ cell = self._lexical_cell_location(owner, name)
1105
+ existing = self._scope_name_locations(owner, name)
1106
+ if existing:
1107
+ current = self.state.read(cell, fallback=())
1108
+ self.state.write(
1109
+ cell,
1110
+ tuple(dict.fromkeys((*current, *existing))),
1111
+ UpdatePolicy.STRONG,
1112
+ )
1113
+ return owner
1114
+
1115
+ def _nearest_lexical_binding(self, procedure: object, name: str) -> object:
1116
+ parent = self._lexical_parents.get(id(procedure))
1117
+ fallback = parent if parent is not None else procedure
1118
+ while parent is not None:
1119
+ if self._scope_defines_name(parent, name):
1120
+ return parent
1121
+ parent = self._lexical_parents.get(id(parent))
1122
+ return fallback
1123
+
1124
+ def _scope_defines_name(self, procedure: object, name: str) -> bool:
1125
+ parameters = getattr(procedure, "codeparameters", None)
1126
+ if parameters is not None:
1127
+ for formal in self._callee_formals(procedure):
1128
+ if getattr(formal, "name", None) == name:
1129
+ return True
1130
+ if (id(procedure), name) in self._definition_locals:
1131
+ return True
1132
+ if self._scope_name_locations(procedure, name):
1133
+ return True
1134
+ body = getattr(procedure, "ast", None)
1135
+ for operation in self.iter_operations(body):
1136
+ if isinstance(operation, (py_ast.FunctionDef, py_ast.ClassDef)):
1137
+ if operation.name == name:
1138
+ return True
1139
+ continue
1140
+ if any(
1141
+ getattr(local, "name", None) == name
1142
+ for local in assigned_locals(operation)
1143
+ ):
1144
+ return True
1145
+ return False
1146
+
1147
+ def _scope_name_locations(
1148
+ self,
1149
+ procedure: object,
1150
+ name: str,
1151
+ ) -> tuple[HeapLocation, ...]:
1152
+ locations: list[HeapLocation] = []
1153
+ keys = set(self.heap.storage_overrides) | set(self.heap.allocation_sites)
1154
+ for key in keys:
1155
+ if key[0] != id(procedure) or self.heap._local_names.get(key) != name:
1156
+ continue
1157
+ storage = self.heap.storage_overrides.get(key)
1158
+ if storage is None:
1159
+ site = self.heap.allocation_sites.get(key)
1160
+ storage = self.heap.site_storage.get(site, ()) if site is not None else ()
1161
+ locations.extend(self.heap.location_for_raw(raw) for raw in storage)
1162
+ return tuple(dict.fromkeys(locations))
1163
+
1164
+ def _lexical_cell_location(
1165
+ self,
1166
+ owner: object,
1167
+ name: str,
1168
+ ) -> HeapLocation:
1169
+ key = (id(owner), name)
1170
+ cell = self._lexical_cells.get(key)
1171
+ if cell is None:
1172
+ cell = py_ast.Cell(name)
1173
+ self._lexical_cells[key] = cell
1174
+ return self.effect_builder.cell_location(cell, owner)
1175
+
1176
+ def _bind_runtime_local(
1177
+ self,
1178
+ procedure: object,
1179
+ local: py_ast.Local,
1180
+ locations: tuple[HeapLocation, ...],
1181
+ *,
1182
+ include_raw_fallback: bool = False,
1183
+ ) -> None:
1184
+ declared = self._declared_location(procedure, local)
1185
+ if declared is not None:
1186
+ self.state.write(declared, locations, UpdatePolicy.STRONG)
1187
+ return
1188
+ self.heap.bind_local_to_locations(
1189
+ procedure,
1190
+ local,
1191
+ locations,
1192
+ include_raw_fallback=include_raw_fallback,
1193
+ )
1194
+
1195
+ def _clear_runtime_local(
1196
+ self,
1197
+ procedure: object,
1198
+ local: py_ast.Local,
1199
+ ) -> None:
1200
+ declared = self._declared_location(procedure, local)
1201
+ if declared is not None:
1202
+ self.state.delete(declared)
1203
+ return
1204
+ self.heap.clear_local_binding(procedure, local)
1205
+
1206
+ def _merge_expression_locations(self, procedure, *expressions):
1207
+ """Return the deduplicated union of heap locations from multiple expressions."""
1208
+ locations: list[HeapLocation] = []
1209
+ for expr in expressions:
1210
+ if expr is not None:
1211
+ locations.extend(self.locations_for_expression(procedure, expr))
1212
+ return tuple(dict.fromkeys(locations))
1213
+
1214
+ @classmethod
1215
+ def iter_code_objects(cls, root: object):
1216
+ """Yield code objects reachable from *root* without recursing into bodies."""
1217
+ seen: set[int] = set()
1218
+
1219
+ def visit(value: object):
1220
+ if value is None or isinstance(value, py_ast.leafTypes):
1221
+ return
1222
+ if isinstance(value, py_ast.Code):
1223
+ key = id(value)
1224
+ if key not in seen:
1225
+ seen.add(key)
1226
+ yield value
1227
+ return
1228
+ if isinstance(value, (list, tuple, set, frozenset)):
1229
+ for item in value:
1230
+ yield from visit(item)
1231
+ return
1232
+ for attr in ("liveCode", "entryPoints", "codes", "procedures", "functions"):
1233
+ child = getattr(value, attr, None)
1234
+ if child is not None:
1235
+ yield from visit(child)
1236
+ code = getattr(value, "code", None)
1237
+ if code is not value:
1238
+ yield from visit(code)
1239
+
1240
+ yield from visit(root)
1241
+
1242
+ @classmethod
1243
+ def iter_operations(cls, node: object):
1244
+ """Yield operation nodes inside a code body."""
1245
+ if node is None or isinstance(node, py_ast.leafTypes):
1246
+ return
1247
+ if isinstance(node, py_ast.Code):
1248
+ return
1249
+ if isinstance(node, py_ast.Suite):
1250
+ for block in node.blocks:
1251
+ yield from cls.iter_operations(block)
1252
+ return
1253
+ if isinstance(node, py_ast.PythonASTNode):
1254
+ yield node
1255
+ if hasattr(node, "visitChildren"):
1256
+ children: list[object] = []
1257
+ node.visitChildren(children.append)
1258
+ for child in children:
1259
+ yield from cls.iter_operations(child)
1260
+
1261
+ def _materialize_assignment_result(
1262
+ self, procedure: object, operation: object
1263
+ ) -> None:
1264
+ targets = self._direct_assigned_locals(operation)
1265
+ if not targets:
1266
+ return
1267
+ if isinstance(operation, py_ast.InputBlock):
1268
+ external = self._external_value_location(procedure)
1269
+ for target in targets:
1270
+ self._bind_runtime_local(procedure, target, (external,))
1271
+ return
1272
+ if isinstance(operation, py_ast.Phi):
1273
+ phi_locations = tuple(
1274
+ dict.fromkeys(
1275
+ location
1276
+ for argument in getattr(operation, "arguments", ())
1277
+ if argument is not None
1278
+ for location in self.locations_for_expression(procedure, argument)
1279
+ )
1280
+ )
1281
+ if phi_locations:
1282
+ self._bind_runtime_local(
1283
+ procedure,
1284
+ operation.target,
1285
+ phi_locations,
1286
+ )
1287
+ else:
1288
+ self._bind_runtime_local(
1289
+ procedure,
1290
+ operation.target,
1291
+ (self._external_value_location(procedure),),
1292
+ )
1293
+ return
1294
+ if isinstance(operation, py_ast.UnpackSequence):
1295
+ self._materialize_unpack_targets(procedure, operation)
1296
+ return
1297
+ expr = self._assigned_expression(operation)
1298
+ if isinstance(
1299
+ expr,
1300
+ (
1301
+ py_ast.BuildTuple,
1302
+ py_ast.BuildList,
1303
+ py_ast.BuildSet,
1304
+ py_ast.BuildMap,
1305
+ py_ast.BuildSlice,
1306
+ py_ast.MakeFunction,
1307
+ py_ast.Allocate,
1308
+ ),
1309
+ ):
1310
+ allocations = self.locations_for_expression(procedure, expr)
1311
+ for target in targets:
1312
+ self._bind_runtime_local(procedure, target, allocations)
1313
+ return
1314
+ if isinstance(expr, py_ast.Import):
1315
+ module_name = self._resolved_import_module_name(procedure, expr)
1316
+ module = self.heap.module_object(module_name, label=module_name)
1317
+ imported = [HeapLocation(module)]
1318
+ if not getattr(expr, "fromlist", None) and "." in expr.name:
1319
+ top_level = expr.name.split(".", 1)[0]
1320
+ if all(
1321
+ getattr(target, "name", None) == top_level
1322
+ for target in targets
1323
+ ):
1324
+ imported = [
1325
+ HeapLocation(
1326
+ self.heap.module_object(top_level, label=top_level)
1327
+ )
1328
+ ]
1329
+ else:
1330
+ imported = [HeapLocation(module)]
1331
+ if getattr(expr, "fromlist", None):
1332
+ for imported_name in getattr(expr, "fromlist", ()):
1333
+ name = getattr(imported_name, "name", imported_name)
1334
+ if name == "*":
1335
+ self.precision_degradations.append(
1336
+ (expr, "star-import-namespace")
1337
+ )
1338
+ unknown = self._external_value_location(procedure)
1339
+ self.state.write(
1340
+ self.heap.dynamic_attribute_location(
1341
+ HeapLocation(module),
1342
+ "*",
1343
+ ),
1344
+ (unknown,),
1345
+ UpdatePolicy.WEAK,
1346
+ )
1347
+ break
1348
+ for target in targets:
1349
+ self._bind_runtime_local(
1350
+ procedure,
1351
+ target,
1352
+ tuple(imported),
1353
+ )
1354
+ return
1355
+ if isinstance(expr, (py_ast.Call, py_ast.DirectCall, py_ast.MethodCall)):
1356
+ if isinstance(expr, py_ast.DirectCall) and isinstance(
1357
+ expr.code,
1358
+ py_ast.Code,
1359
+ ):
1360
+ # Resolved calls evaluate/bind their actuals and results in
1361
+ # _apply_call_transfer. Binding a placeholder here would
1362
+ # overwrite a target that may also be used as an argument.
1363
+ return
1364
+ slots = self._bind_call_result_targets(
1365
+ procedure,
1366
+ targets,
1367
+ expr,
1368
+ bind=False,
1369
+ )
1370
+ self._pending_call_results[id(operation)] = (targets, slots)
1371
+ return
1372
+ if expr is not None and not isinstance(expr, py_ast.Local):
1373
+ expr_locations = self.locations_for_expression(procedure, expr)
1374
+ if expr_locations:
1375
+ for target in targets:
1376
+ self._bind_runtime_local(
1377
+ procedure,
1378
+ target,
1379
+ expr_locations,
1380
+ )
1381
+ else:
1382
+ for target in targets:
1383
+ self._clear_runtime_local(procedure, target)
1384
+ return
1385
+ if isinstance(expr, py_ast.Local):
1386
+ source_locations = self.locations_for_expression(procedure, expr)
1387
+ for target in targets:
1388
+ self._bind_runtime_local(procedure, target, source_locations)
1389
+ return
1390
+ self.heap.update_assignment_aliases(procedure, targets, expr)
1391
+
1392
+ def _resolved_import_module_name(
1393
+ self,
1394
+ procedure: object,
1395
+ expression: py_ast.Import,
1396
+ ) -> str:
1397
+ if not getattr(expression, "level", 0):
1398
+ return expression.name
1399
+ owner_name = self._module_name_for_owner(self._module_owner(procedure))
1400
+ if not owner_name:
1401
+ return expression.name or f"relative:{expression.level}"
1402
+ package = owner_name.split(".")[:-1]
1403
+ climb = max(int(expression.level) - 1, 0)
1404
+ if climb:
1405
+ package = package[:-climb] if climb < len(package) else []
1406
+ if expression.name:
1407
+ package.extend(expression.name.split("."))
1408
+ return ".".join(package) or expression.name or owner_name
1409
+
1410
+ @staticmethod
1411
+ def _direct_assigned_locals(operation: object) -> tuple[py_ast.Local, ...]:
1412
+ if isinstance(operation, py_ast.Assign):
1413
+ return tuple(
1414
+ local for local in operation.lcls if isinstance(local, py_ast.Local)
1415
+ )
1416
+ if isinstance(operation, py_ast.UnpackSequence):
1417
+ return tuple(
1418
+ local
1419
+ for local in operation.targets
1420
+ if isinstance(local, py_ast.Local)
1421
+ )
1422
+ if isinstance(operation, py_ast.AnnAssign):
1423
+ if operation.value is not None and isinstance(operation.target, py_ast.Local):
1424
+ return (operation.target,)
1425
+ return ()
1426
+ if isinstance(operation, py_ast.InputBlock):
1427
+ return tuple(
1428
+ input_.lcl
1429
+ for input_ in getattr(operation, "inputs", ())
1430
+ if isinstance(getattr(input_, "lcl", None), py_ast.Local)
1431
+ )
1432
+ if isinstance(operation, py_ast.Phi) and isinstance(
1433
+ getattr(operation, "target", None), py_ast.Local
1434
+ ):
1435
+ return (operation.target,)
1436
+ return ()
1437
+
1438
+ def _materialize_unpack_targets(
1439
+ self,
1440
+ procedure: object,
1441
+ operation: py_ast.UnpackSequence,
1442
+ ) -> None:
1443
+ sources = self.locations_for_expression(procedure, operation.expr)
1444
+ for index, target in enumerate(operation.targets):
1445
+ if not isinstance(target, py_ast.Local):
1446
+ continue
1447
+ values: list[HeapLocation] = []
1448
+ for source in sources:
1449
+ exact = self.heap.dynamic_subscript_location(source, f"[{index}]")
1450
+ wildcard = self.heap.dynamic_subscript_location(
1451
+ source,
1452
+ DYNAMIC_SUBSCRIPT_WILDCARD,
1453
+ )
1454
+ values.extend(self.state.read(exact, fallback=()))
1455
+ values.extend(self.state.read(wildcard, fallback=()))
1456
+ values.extend(self.state.read_contained(wildcard))
1457
+ if not values:
1458
+ values.extend(sources)
1459
+ values.append(self._external_value_location(procedure))
1460
+ self._bind_runtime_local(
1461
+ procedure,
1462
+ target,
1463
+ tuple(dict.fromkeys(values)),
1464
+ )
1465
+
1466
+ def _materialize_return_values(
1467
+ self,
1468
+ procedure: object,
1469
+ operation: py_ast.Return,
1470
+ returns: tuple[HeapLocation, ...],
1471
+ ) -> None:
1472
+ # HeapEffect exposes a flat return set for procedure summaries, but a
1473
+ # direct call with multiple result variables needs the values grouped
1474
+ # by result position. Re-evaluate the expressions here and preserve
1475
+ # that grouping through branch joins.
1476
+ del returns
1477
+ code_parameters = getattr(procedure, "codeparameters", None)
1478
+ if code_parameters is None:
1479
+ return
1480
+ returnparams = tuple(getattr(code_parameters, "returnparams", ()))
1481
+ expression_locations = tuple(
1482
+ self.locations_for_expression(procedure, expression)
1483
+ for expression in getattr(operation, "exprs", ())
1484
+ )
1485
+ return_slots: list[tuple[HeapLocation, ...]] = []
1486
+ for index, target in enumerate(returnparams):
1487
+ if len(returnparams) == 1 and len(expression_locations) == 1:
1488
+ bind_locations = expression_locations[0]
1489
+ elif index < len(expression_locations):
1490
+ bind_locations = expression_locations[index]
1491
+ else:
1492
+ bind_locations = ()
1493
+
1494
+ if not bind_locations:
1495
+ bind_locations = (
1496
+ HeapLocation(
1497
+ self.heap.return_object(
1498
+ procedure,
1499
+ index,
1500
+ label=getattr(target, "name", None),
1501
+ )
1502
+ ),
1503
+ )
1504
+ return_slots.append(tuple(dict.fromkeys(bind_locations)))
1505
+
1506
+ if isinstance(target, py_ast.Local):
1507
+ self.heap.bind_local_to_locations(
1508
+ procedure,
1509
+ target,
1510
+ bind_locations,
1511
+ )
1512
+ # Any returned value is visible from outside the procedure,
1513
+ # including a slot represented by DoNotCare rather than a local.
1514
+ self.heap.mark_all_escaped(bind_locations)
1515
+
1516
+ slots = tuple(return_slots)
1517
+ flat_returns = tuple(
1518
+ dict.fromkeys(
1519
+ location
1520
+ for slot in slots
1521
+ for location in slot
1522
+ )
1523
+ )
1524
+ self.state.set_return_slots(procedure, slots)
1525
+ self.state.set_returns(procedure, flat_returns)
1526
+
1527
+ def _apply_writes(
1528
+ self,
1529
+ procedure: object,
1530
+ operation: object,
1531
+ writes: tuple[object, ...],
1532
+ *,
1533
+ stored_value: object | None = None,
1534
+ ) -> None:
1535
+ value = stored_value
1536
+ if value is not None:
1537
+ value_locations = self.locations_for_expression(procedure, value)
1538
+ if isinstance(operation, py_ast.SetSlice):
1539
+ value_locations = self._expand_contained_locations(
1540
+ value_locations
1541
+ )
1542
+ # Don't return early when value_locations is empty: write()
1543
+ # handles STRONG+empty (pop to clear the binding) and
1544
+ # WEAK+empty (no-op) correctly.
1545
+ else:
1546
+ # No direct stored-value expression. Check whether this operation
1547
+ # wraps a collection mutator call (e.g. ``list.append(x)``) whose
1548
+ # value arguments should be written to the container's wildcard
1549
+ # location generated by HeapEffectBuilder.collection_mutation().
1550
+ coll_value_locs = self._collection_mutator_value_locations(
1551
+ procedure, operation
1552
+ )
1553
+ if not coll_value_locs:
1554
+ return
1555
+ value_locations = coll_value_locs
1556
+ for write in writes:
1557
+ location = getattr(write, "location", None)
1558
+ policy = getattr(write, "policy", None)
1559
+ if not isinstance(location, HeapLocation):
1560
+ continue
1561
+ self.state.write(
1562
+ location,
1563
+ tuple(dict.fromkeys(value_locations)),
1564
+ (
1565
+ UpdatePolicy.STRONG
1566
+ if isinstance(operation, (py_ast.SetGlobal, py_ast.SetCellDeref))
1567
+ else policy
1568
+ if isinstance(policy, UpdatePolicy)
1569
+ else UpdatePolicy.WEAK
1570
+ ),
1571
+ has_non_reference=value is not None and not value_locations,
1572
+ )
1573
+ if isinstance(operation, py_ast.SetGlobal):
1574
+ module = self._module_locations_by_owner.get(
1575
+ self._module_owner(procedure)
1576
+ )
1577
+ if module is not None:
1578
+ self.state.write(
1579
+ self.heap.dynamic_attribute_location(
1580
+ module,
1581
+ self.effect_builder._constant_string(operation.name)
1582
+ or self.effect_builder._path_component(operation.name),
1583
+ ),
1584
+ tuple(dict.fromkeys(value_locations)),
1585
+ UpdatePolicy.STRONG,
1586
+ has_non_reference=(
1587
+ value is not None and not value_locations
1588
+ ),
1589
+ )
1590
+ if isinstance(operation, py_ast.SetSubscript):
1591
+ key_locations = self.locations_for_expression(
1592
+ procedure,
1593
+ operation.subscript,
1594
+ )
1595
+ if key_locations:
1596
+ roots = tuple(
1597
+ dict.fromkeys(
1598
+ HeapLocation(write.location.root)
1599
+ for write in writes
1600
+ if isinstance(getattr(write, "location", None), HeapLocation)
1601
+ )
1602
+ )
1603
+ for root in roots:
1604
+ self.state.write(
1605
+ self.heap.dynamic_attribute_location(root, "__keys__"),
1606
+ key_locations,
1607
+ UpdatePolicy.WEAK,
1608
+ )
1609
+
1610
+ def _collection_mutator_value_locations(
1611
+ self,
1612
+ procedure: object,
1613
+ operation: object,
1614
+ ) -> tuple[HeapLocation, ...]:
1615
+ """Extract value locations for collection mutator calls.
1616
+
1617
+ When a ``Discard(MethodCall(container, "append", [value]))`` is
1618
+ processed, :meth:`HeapEffectBuilder.operation_effect` generates
1619
+ wildcard writes to the container but the write values are buried
1620
+ in the method-call arguments rather than in a ``value`` attribute.
1621
+ This helper extracts those value expressions and resolves them
1622
+ to heap locations.
1623
+ """
1624
+ call = self._call_expression(operation)
1625
+ if call is None:
1626
+ return ()
1627
+ call_name = resolve_call_name(call)
1628
+ if call_name is None or call_name not in self.collection_mutator_names:
1629
+ return ()
1630
+ model = self.intrinsics.collection_mutator(call_name)
1631
+ if model is None or not model.writes_value:
1632
+ return ()
1633
+ actuals = actual_argument_expressions(call)
1634
+ if isinstance(call, py_ast.MethodCall):
1635
+ value_exprs = model.value_args(actuals)
1636
+ else:
1637
+ remaining = actuals[1:] if len(actuals) > 1 else ()
1638
+ value_exprs = model.value_args(remaining)
1639
+ return self._expand_contained_locations(
1640
+ tuple(
1641
+ loc
1642
+ for val_expr in value_exprs
1643
+ for loc in self.locations_for_expression(procedure, val_expr)
1644
+ )
1645
+ )
1646
+
1647
+ def _apply_collection_reorder(
1648
+ self,
1649
+ procedure: object,
1650
+ operation: object,
1651
+ ) -> None:
1652
+ """Move every currently stored element into the wildcard may-set."""
1653
+ call = self._call_expression(operation)
1654
+ container = None
1655
+ if isinstance(operation, (py_ast.SetSlice, py_ast.DeleteSlice, py_ast.DeleteSubscript)):
1656
+ container = operation.expr
1657
+ elif call is not None:
1658
+ model = self.intrinsics.collection_mutator(resolve_call_name(call))
1659
+ if model is None or not model.reorders_values:
1660
+ return
1661
+ actuals = tuple(actual_argument_expressions(call))
1662
+ container = (
1663
+ call.expr
1664
+ if isinstance(call, py_ast.MethodCall)
1665
+ else actuals[0]
1666
+ if actuals
1667
+ else None
1668
+ )
1669
+ else:
1670
+ return
1671
+ if container is None:
1672
+ return
1673
+ evaluated = self._last_call_operands.get(id(call), {}) if call is not None else {}
1674
+ roots = evaluated.get(id(container))
1675
+ if roots is None:
1676
+ roots = self.locations_for_expression(procedure, container)
1677
+ for root in roots:
1678
+ wildcard = self.heap.dynamic_subscript_location(
1679
+ root,
1680
+ DYNAMIC_SUBSCRIPT_WILDCARD,
1681
+ )
1682
+ values = self.state.read_contained(wildcard)
1683
+ if values:
1684
+ self.state.write(wildcard, values, UpdatePolicy.WEAK)
1685
+
1686
+ def _expand_contained_locations(
1687
+ self,
1688
+ roots: tuple[HeapLocation, ...],
1689
+ ) -> tuple[HeapLocation, ...]:
1690
+ """Include values reachable as elements of possible iterable roots.
1691
+
1692
+ This deliberately retains the roots too: append-like mutators store
1693
+ the argument object itself, while extend/update and slice assignment
1694
+ store values obtained by iterating it. Using their union is a sound
1695
+ may-approximation for the shared mutator model.
1696
+ """
1697
+ expanded = list(roots)
1698
+ for root in roots:
1699
+ wildcard = self.heap.dynamic_subscript_location(
1700
+ root,
1701
+ DYNAMIC_SUBSCRIPT_WILDCARD,
1702
+ )
1703
+ expanded.extend(self.state.read_contained(wildcard))
1704
+ return tuple(dict.fromkeys(expanded))
1705
+
1706
+ def _contained_values(
1707
+ self,
1708
+ roots: tuple[HeapLocation, ...],
1709
+ ) -> tuple[HeapLocation, ...]:
1710
+ values: list[HeapLocation] = []
1711
+ for root in roots:
1712
+ values.extend(
1713
+ self.state.read_contained(
1714
+ self.heap.dynamic_subscript_location(
1715
+ root,
1716
+ DYNAMIC_SUBSCRIPT_WILDCARD,
1717
+ )
1718
+ )
1719
+ )
1720
+ return tuple(dict.fromkeys(values))
1721
+
1722
+ def _ordered_contained_values(
1723
+ self,
1724
+ roots: tuple[HeapLocation, ...],
1725
+ ) -> tuple[HeapLocation, ...]:
1726
+ """Return known positional elements before wildcard remainder values."""
1727
+ values: list[HeapLocation] = []
1728
+ for root in roots:
1729
+ for index in range(self.heap.policy.max_index + 1):
1730
+ values.extend(
1731
+ self.state.read(
1732
+ self.heap.dynamic_subscript_location(root, f"[{index}]"),
1733
+ fallback=(),
1734
+ )
1735
+ )
1736
+ values.extend(
1737
+ self.state.read(
1738
+ self.heap.dynamic_subscript_location(
1739
+ root,
1740
+ DYNAMIC_SUBSCRIPT_WILDCARD,
1741
+ ),
1742
+ fallback=(),
1743
+ )
1744
+ )
1745
+ return tuple(values)
1746
+
1747
+ def _apply_deletes(self, deletes: tuple[HeapLocation, ...]) -> None:
1748
+ for deleted in deletes:
1749
+ self.state.delete(deleted)
1750
+
1751
+ def _effective_deletes(
1752
+ self,
1753
+ operation: object,
1754
+ deletes: tuple[HeapLocation, ...],
1755
+ ) -> tuple[HeapLocation, ...]:
1756
+ return self.effect_builder.definite_delete_locations(operation, deletes)
1757
+
1758
+ def _handle_local_delete(
1759
+ self,
1760
+ procedure: object,
1761
+ operation: object,
1762
+ ) -> None:
1763
+ """Handle ``del x`` — break aliasing and remove the local's heap state."""
1764
+ if not isinstance(operation, py_ast.Delete):
1765
+ return
1766
+ self._clear_runtime_local(procedure, operation.lcl)
1767
+
1768
+ def _materialize_collection_literal_values(
1769
+ self,
1770
+ procedure: object,
1771
+ operation: object,
1772
+ ) -> None:
1773
+ expr = self._assigned_expression(operation)
1774
+ if not isinstance(
1775
+ expr,
1776
+ (py_ast.BuildTuple, py_ast.BuildList, py_ast.BuildSet, py_ast.BuildMap),
1777
+ ):
1778
+ return
1779
+ for argument in getattr(expr, "args", ()):
1780
+ self.locations_for_expression(procedure, argument)
1781
+ targets = assigned_locals(operation)
1782
+ target_locations = tuple(
1783
+ location
1784
+ for target in targets
1785
+ for location in self.heap.locations_for_local(procedure, target)
1786
+ )
1787
+ value_exprs = self._collection_literal_values(expr)
1788
+ for location in target_locations:
1789
+ if location.root.kind is HeapObjectKind.ALLOCATION:
1790
+ # Write literal element values into the container's heap state
1791
+ # so subsequent reads and transitive escape propagation can
1792
+ # find them when the container itself escapes.
1793
+ self._write_collection_literal_elements(
1794
+ procedure, location, expr, value_exprs
1795
+ )
1796
+
1797
+ def _materialize_function_default_values(
1798
+ self,
1799
+ procedure: object,
1800
+ operation: object,
1801
+ ) -> None:
1802
+ expr = self._assigned_expression(operation)
1803
+ if not isinstance(expr, py_ast.MakeFunction):
1804
+ return
1805
+ targets = assigned_locals(operation)
1806
+ target_locations = tuple(
1807
+ location
1808
+ for target in targets
1809
+ for location in self.heap.locations_for_local(procedure, target)
1810
+ )
1811
+ default_locations = tuple(
1812
+ location
1813
+ for default in getattr(expr, "defaults", ())
1814
+ for location in self.locations_for_expression(procedure, default)
1815
+ )
1816
+ if not default_locations:
1817
+ return
1818
+ for location in target_locations:
1819
+ defaults_location = self.heap.dynamic_attribute_location(
1820
+ location,
1821
+ "__defaults__",
1822
+ )
1823
+ self.state.write(
1824
+ defaults_location,
1825
+ tuple(dict.fromkeys(default_locations)),
1826
+ UpdatePolicy.STRONG,
1827
+ )
1828
+
1829
+ def _write_collection_literal_elements(
1830
+ self,
1831
+ procedure: object,
1832
+ container: HeapLocation,
1833
+ expr: object,
1834
+ value_exprs: tuple[object, ...],
1835
+ ) -> None:
1836
+ if isinstance(expr, py_ast.BuildMap):
1837
+ keys = tuple(expr.args[0::2])
1838
+ for key_expr, val_expr in zip(keys, value_exprs):
1839
+ key_locations = self.locations_for_expression(
1840
+ procedure,
1841
+ key_expr,
1842
+ )
1843
+ if key_locations:
1844
+ self.state.write(
1845
+ self.heap.dynamic_attribute_location(
1846
+ container,
1847
+ "__keys__",
1848
+ ),
1849
+ key_locations,
1850
+ UpdatePolicy.WEAK,
1851
+ )
1852
+ subscript = self.effect_builder._constant_subscript(key_expr)
1853
+ key_loc = self.heap.dynamic_subscript_location(
1854
+ container,
1855
+ subscript or DYNAMIC_SUBSCRIPT_WILDCARD,
1856
+ )
1857
+ val_locs = self.locations_for_expression(procedure, val_expr)
1858
+ if val_locs:
1859
+ self.state.write(
1860
+ key_loc,
1861
+ val_locs,
1862
+ (
1863
+ UpdatePolicy.STRONG
1864
+ if subscript
1865
+ else UpdatePolicy.WEAK
1866
+ ),
1867
+ )
1868
+ elif isinstance(expr, py_ast.BuildSet):
1869
+ set_loc = self.heap.dynamic_subscript_location(
1870
+ container, DYNAMIC_SUBSCRIPT_WILDCARD
1871
+ )
1872
+ all_val_locs: list[HeapLocation] = []
1873
+ for val_expr in value_exprs:
1874
+ all_val_locs.extend(
1875
+ self.locations_for_expression(procedure, val_expr)
1876
+ )
1877
+ if all_val_locs:
1878
+ self.state.write(
1879
+ set_loc, tuple(dict.fromkeys(all_val_locs)), UpdatePolicy.WEAK
1880
+ )
1881
+ else:
1882
+ for index, val_expr in enumerate(value_exprs):
1883
+ index_loc = self.heap.dynamic_subscript_location(
1884
+ container, f"[{index}]"
1885
+ )
1886
+ val_locs = self.locations_for_expression(procedure, val_expr)
1887
+ if index_loc and val_locs:
1888
+ self.state.write(
1889
+ index_loc, val_locs, UpdatePolicy.STRONG
1890
+ )
1891
+
1892
+ @staticmethod
1893
+ def _collection_literal_values(expr: object) -> tuple[object, ...]:
1894
+ if isinstance(expr, py_ast.BuildMap):
1895
+ return tuple(expr.args[1::2])
1896
+ return tuple(getattr(expr, "args", ()))
1897
+
1898
+ @staticmethod
1899
+ def _binary_protocol_name(operator: str) -> str | None:
1900
+ return {
1901
+ "+": "__add__",
1902
+ "-": "__sub__",
1903
+ "*": "__mul__",
1904
+ "@": "__matmul__",
1905
+ "/": "__truediv__",
1906
+ "//": "__floordiv__",
1907
+ "%": "__mod__",
1908
+ "**": "__pow__",
1909
+ "<<": "__lshift__",
1910
+ ">>": "__rshift__",
1911
+ "&": "__and__",
1912
+ "|": "__or__",
1913
+ "^": "__xor__",
1914
+ "<": "__lt__",
1915
+ "<=": "__le__",
1916
+ ">": "__gt__",
1917
+ ">=": "__ge__",
1918
+ "==": "__eq__",
1919
+ "!=": "__ne__",
1920
+ }.get(operator)
1921
+
1922
+ @staticmethod
1923
+ def _reflected_binary_protocol_name(operator: str) -> str | None:
1924
+ return {
1925
+ "+": "__radd__",
1926
+ "-": "__rsub__",
1927
+ "*": "__rmul__",
1928
+ "@": "__rmatmul__",
1929
+ "/": "__rtruediv__",
1930
+ "//": "__rfloordiv__",
1931
+ "%": "__rmod__",
1932
+ "**": "__rpow__",
1933
+ "<<": "__rlshift__",
1934
+ ">>": "__rrshift__",
1935
+ "&": "__rand__",
1936
+ "|": "__ror__",
1937
+ "^": "__rxor__",
1938
+ }.get(operator)
1939
+
1940
+ @staticmethod
1941
+ def _reflected_comparison_protocol_name(operator: str) -> str | None:
1942
+ return {
1943
+ "<": "__gt__",
1944
+ "<=": "__ge__",
1945
+ ">": "__lt__",
1946
+ ">=": "__le__",
1947
+ "==": "__eq__",
1948
+ "!=": "__ne__",
1949
+ }.get(operator)
1950
+
1951
+ @staticmethod
1952
+ def _assigned_expression(operation: object) -> object | None:
1953
+ if isinstance(operation, (py_ast.Assign, py_ast.UnpackSequence)):
1954
+ return operation.expr
1955
+ if isinstance(operation, py_ast.AnnAssign):
1956
+ return operation.value
1957
+ return None
1958
+
1959
+ @staticmethod
1960
+ def _call_expression(operation: object) -> object | None:
1961
+ expr = _TransferOpsMixin._assigned_expression(operation)
1962
+ if isinstance(expr, (py_ast.Call, py_ast.DirectCall, py_ast.MethodCall)):
1963
+ return expr
1964
+ wrapped = getattr(operation, "expr", None)
1965
+ if isinstance(wrapped, (py_ast.Call, py_ast.DirectCall, py_ast.MethodCall)):
1966
+ return wrapped
1967
+ if isinstance(operation, (py_ast.Call, py_ast.DirectCall, py_ast.MethodCall)):
1968
+ return operation
1969
+ return None