angr 9.2.158__cp310-abi3-manylinux2014_x86_64.whl → 9.2.159__cp310-abi3-manylinux2014_x86_64.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.

Potentially problematic release.


This version of angr might be problematic. Click here for more details.

Files changed (191) hide show
  1. angr/__init__.py +1 -1
  2. angr/ailment/__init__.py +81 -0
  3. angr/ailment/block.py +81 -0
  4. angr/ailment/block_walker.py +845 -0
  5. angr/ailment/constant.py +3 -0
  6. angr/ailment/converter_common.py +11 -0
  7. angr/ailment/converter_pcode.py +623 -0
  8. angr/ailment/converter_vex.py +798 -0
  9. angr/ailment/expression.py +1639 -0
  10. angr/ailment/manager.py +33 -0
  11. angr/ailment/statement.py +978 -0
  12. angr/ailment/tagged_object.py +61 -0
  13. angr/ailment/utils.py +114 -0
  14. angr/analyses/calling_convention/calling_convention.py +6 -2
  15. angr/analyses/decompiler/ail_simplifier.py +5 -5
  16. angr/analyses/decompiler/block_io_finder.py +4 -4
  17. angr/analyses/decompiler/block_similarity.py +2 -2
  18. angr/analyses/decompiler/block_simplifier.py +4 -4
  19. angr/analyses/decompiler/callsite_maker.py +2 -2
  20. angr/analyses/decompiler/ccall_rewriters/amd64_ccalls.py +1 -1
  21. angr/analyses/decompiler/ccall_rewriters/rewriter_base.py +1 -1
  22. angr/analyses/decompiler/ccall_rewriters/x86_ccalls.py +1 -1
  23. angr/analyses/decompiler/clinic.py +1 -1
  24. angr/analyses/decompiler/condition_processor.py +1 -1
  25. angr/analyses/decompiler/counters/boolean_counter.py +4 -4
  26. angr/analyses/decompiler/counters/call_counter.py +4 -4
  27. angr/analyses/decompiler/counters/expression_counters.py +5 -5
  28. angr/analyses/decompiler/counters/seq_cf_structure_counter.py +1 -1
  29. angr/analyses/decompiler/decompiler.py +5 -3
  30. angr/analyses/decompiler/dephication/dephication_base.py +12 -1
  31. angr/analyses/decompiler/dephication/graph_dephication.py +12 -5
  32. angr/analyses/decompiler/dephication/graph_rewriting.py +6 -10
  33. angr/analyses/decompiler/dephication/graph_vvar_mapping.py +109 -72
  34. angr/analyses/decompiler/dephication/rewriting_engine.py +32 -9
  35. angr/analyses/decompiler/dephication/seqnode_dephication.py +32 -10
  36. angr/analyses/decompiler/empty_node_remover.py +2 -2
  37. angr/analyses/decompiler/expression_narrower.py +6 -6
  38. angr/analyses/decompiler/goto_manager.py +2 -2
  39. angr/analyses/decompiler/jump_target_collector.py +1 -1
  40. angr/analyses/decompiler/label_collector.py +1 -1
  41. angr/analyses/decompiler/optimization_passes/base_ptr_save_simplifier.py +25 -25
  42. angr/analyses/decompiler/optimization_passes/call_stmt_rewriter.py +1 -1
  43. angr/analyses/decompiler/optimization_passes/code_motion.py +2 -2
  44. angr/analyses/decompiler/optimization_passes/condition_constprop.py +3 -3
  45. angr/analyses/decompiler/optimization_passes/const_derefs.py +3 -3
  46. angr/analyses/decompiler/optimization_passes/const_prop_reverter.py +4 -4
  47. angr/analyses/decompiler/optimization_passes/deadblock_remover.py +2 -2
  48. angr/analyses/decompiler/optimization_passes/determine_load_sizes.py +3 -3
  49. angr/analyses/decompiler/optimization_passes/div_simplifier.py +1 -1
  50. angr/analyses/decompiler/optimization_passes/duplication_reverter/ail_merge_graph.py +2 -2
  51. angr/analyses/decompiler/optimization_passes/duplication_reverter/duplication_reverter.py +4 -4
  52. angr/analyses/decompiler/optimization_passes/duplication_reverter/similarity.py +1 -1
  53. angr/analyses/decompiler/optimization_passes/duplication_reverter/utils.py +4 -4
  54. angr/analyses/decompiler/optimization_passes/eager_std_string_concatenation.py +3 -3
  55. angr/analyses/decompiler/optimization_passes/engine_base.py +1 -1
  56. angr/analyses/decompiler/optimization_passes/expr_op_swapper.py +3 -3
  57. angr/analyses/decompiler/optimization_passes/flip_boolean_cmp.py +2 -2
  58. angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +2 -2
  59. angr/analyses/decompiler/optimization_passes/ite_expr_converter.py +3 -3
  60. angr/analyses/decompiler/optimization_passes/ite_region_converter.py +3 -3
  61. angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +4 -4
  62. angr/analyses/decompiler/optimization_passes/mod_simplifier.py +1 -1
  63. angr/analyses/decompiler/optimization_passes/optimization_pass.py +25 -1
  64. angr/analyses/decompiler/optimization_passes/register_save_area_simplifier.py +1 -1
  65. angr/analyses/decompiler/optimization_passes/ret_addr_save_simplifier.py +1 -1
  66. angr/analyses/decompiler/optimization_passes/ret_deduplicator.py +2 -2
  67. angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +4 -4
  68. angr/analyses/decompiler/optimization_passes/return_duplicator_low.py +2 -2
  69. angr/analyses/decompiler/optimization_passes/stack_canary_simplifier.py +1 -1
  70. angr/analyses/decompiler/optimization_passes/switch_default_case_duplicator.py +3 -3
  71. angr/analyses/decompiler/optimization_passes/switch_reused_entry_rewriter.py +3 -3
  72. angr/analyses/decompiler/optimization_passes/tag_slicer.py +1 -1
  73. angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py +1 -1
  74. angr/analyses/decompiler/optimization_passes/x86_gcc_getpc_simplifier.py +1 -1
  75. angr/analyses/decompiler/peephole_optimizations/a_div_const_add_a_mul_n_div_const.py +1 -1
  76. angr/analyses/decompiler/peephole_optimizations/a_mul_const_div_shr_const.py +1 -1
  77. angr/analyses/decompiler/peephole_optimizations/a_mul_const_sub_a.py +1 -1
  78. angr/analyses/decompiler/peephole_optimizations/a_shl_const_sub_a.py +1 -1
  79. angr/analyses/decompiler/peephole_optimizations/a_sub_a_div.py +1 -1
  80. angr/analyses/decompiler/peephole_optimizations/a_sub_a_div_const_mul_const.py +1 -1
  81. angr/analyses/decompiler/peephole_optimizations/a_sub_a_shr_const_shr_const.py +1 -1
  82. angr/analyses/decompiler/peephole_optimizations/a_sub_a_sub_n.py +1 -1
  83. angr/analyses/decompiler/peephole_optimizations/arm_cmpf.py +1 -1
  84. angr/analyses/decompiler/peephole_optimizations/base.py +3 -3
  85. angr/analyses/decompiler/peephole_optimizations/basepointeroffset_add_n.py +1 -1
  86. angr/analyses/decompiler/peephole_optimizations/basepointeroffset_and_mask.py +1 -1
  87. angr/analyses/decompiler/peephole_optimizations/bitwise_or_to_logical_or.py +1 -1
  88. angr/analyses/decompiler/peephole_optimizations/bool_expr_xor_1.py +1 -1
  89. angr/analyses/decompiler/peephole_optimizations/bswap.py +2 -2
  90. angr/analyses/decompiler/peephole_optimizations/cas_intrinsics.py +2 -2
  91. angr/analyses/decompiler/peephole_optimizations/cmpord_rewriter.py +2 -2
  92. angr/analyses/decompiler/peephole_optimizations/coalesce_adjacent_shrs.py +1 -1
  93. angr/analyses/decompiler/peephole_optimizations/coalesce_same_cascading_ifs.py +2 -2
  94. angr/analyses/decompiler/peephole_optimizations/const_mull_a_shift.py +1 -1
  95. angr/analyses/decompiler/peephole_optimizations/constant_derefs.py +1 -1
  96. angr/analyses/decompiler/peephole_optimizations/conv_a_sub0_shr_and.py +1 -1
  97. angr/analyses/decompiler/peephole_optimizations/conv_shl_shr.py +1 -1
  98. angr/analyses/decompiler/peephole_optimizations/eager_eval.py +1 -1
  99. angr/analyses/decompiler/peephole_optimizations/extended_byte_and_mask.py +1 -1
  100. angr/analyses/decompiler/peephole_optimizations/inlined_strcpy.py +2 -2
  101. angr/analyses/decompiler/peephole_optimizations/inlined_strcpy_consolidation.py +2 -2
  102. angr/analyses/decompiler/peephole_optimizations/inlined_wstrcpy.py +2 -2
  103. angr/analyses/decompiler/peephole_optimizations/invert_negated_logical_conjuction_disjunction.py +1 -1
  104. angr/analyses/decompiler/peephole_optimizations/one_sub_bool.py +1 -1
  105. angr/analyses/decompiler/peephole_optimizations/remove_cascading_conversions.py +1 -1
  106. angr/analyses/decompiler/peephole_optimizations/remove_cxx_destructor_calls.py +2 -2
  107. angr/analyses/decompiler/peephole_optimizations/remove_empty_if_body.py +2 -2
  108. angr/analyses/decompiler/peephole_optimizations/remove_noop_conversions.py +1 -1
  109. angr/analyses/decompiler/peephole_optimizations/remove_redundant_bitmasks.py +1 -1
  110. angr/analyses/decompiler/peephole_optimizations/remove_redundant_conversions.py +1 -1
  111. angr/analyses/decompiler/peephole_optimizations/remove_redundant_ite_branch.py +1 -1
  112. angr/analyses/decompiler/peephole_optimizations/remove_redundant_ite_comparisons.py +1 -1
  113. angr/analyses/decompiler/peephole_optimizations/remove_redundant_nots.py +1 -1
  114. angr/analyses/decompiler/peephole_optimizations/remove_redundant_reinterprets.py +1 -1
  115. angr/analyses/decompiler/peephole_optimizations/remove_redundant_shifts.py +1 -1
  116. angr/analyses/decompiler/peephole_optimizations/remove_redundant_shifts_around_comparators.py +1 -1
  117. angr/analyses/decompiler/peephole_optimizations/rewrite_bit_extractions.py +1 -1
  118. angr/analyses/decompiler/peephole_optimizations/rewrite_conv_mul.py +1 -1
  119. angr/analyses/decompiler/peephole_optimizations/rewrite_cxx_operator_calls.py +3 -3
  120. angr/analyses/decompiler/peephole_optimizations/rewrite_mips_gp_loads.py +1 -1
  121. angr/analyses/decompiler/peephole_optimizations/rol_ror.py +2 -2
  122. angr/analyses/decompiler/peephole_optimizations/sar_to_signed_div.py +1 -1
  123. angr/analyses/decompiler/peephole_optimizations/shl_to_mul.py +1 -1
  124. angr/analyses/decompiler/peephole_optimizations/simplify_pc_relative_loads.py +1 -1
  125. angr/analyses/decompiler/peephole_optimizations/single_bit_cond_to_boolexpr.py +1 -1
  126. angr/analyses/decompiler/peephole_optimizations/single_bit_xor.py +1 -1
  127. angr/analyses/decompiler/peephole_optimizations/tidy_stack_addr.py +2 -2
  128. angr/analyses/decompiler/peephole_optimizations/utils.py +1 -1
  129. angr/analyses/decompiler/redundant_label_remover.py +1 -1
  130. angr/analyses/decompiler/region_identifier.py +4 -4
  131. angr/analyses/decompiler/region_simplifiers/cascading_cond_transformer.py +1 -1
  132. angr/analyses/decompiler/region_simplifiers/cascading_ifs.py +1 -1
  133. angr/analyses/decompiler/region_simplifiers/expr_folding.py +37 -8
  134. angr/analyses/decompiler/region_simplifiers/goto.py +1 -1
  135. angr/analyses/decompiler/region_simplifiers/if_.py +1 -1
  136. angr/analyses/decompiler/region_simplifiers/loop.py +1 -1
  137. angr/analyses/decompiler/region_simplifiers/node_address_finder.py +1 -1
  138. angr/analyses/decompiler/region_simplifiers/region_simplifier.py +14 -2
  139. angr/analyses/decompiler/region_simplifiers/switch_cluster_simplifier.py +3 -3
  140. angr/analyses/decompiler/region_simplifiers/switch_expr_simplifier.py +1 -1
  141. angr/analyses/decompiler/return_maker.py +1 -1
  142. angr/analyses/decompiler/seq_to_blocks.py +1 -1
  143. angr/analyses/decompiler/sequence_walker.py +2 -2
  144. angr/analyses/decompiler/ssailification/rewriting.py +4 -4
  145. angr/analyses/decompiler/ssailification/rewriting_engine.py +4 -4
  146. angr/analyses/decompiler/ssailification/rewriting_state.py +3 -3
  147. angr/analyses/decompiler/ssailification/ssailification.py +2 -2
  148. angr/analyses/decompiler/ssailification/traversal.py +1 -1
  149. angr/analyses/decompiler/ssailification/traversal_engine.py +11 -2
  150. angr/analyses/decompiler/structured_codegen/c.py +3 -3
  151. angr/analyses/decompiler/structuring/dream.py +1 -1
  152. angr/analyses/decompiler/structuring/phoenix.py +3 -3
  153. angr/analyses/decompiler/structuring/structurer_base.py +1 -1
  154. angr/analyses/decompiler/structuring/structurer_nodes.py +1 -2
  155. angr/analyses/decompiler/utils.py +1 -1
  156. angr/analyses/deobfuscator/api_obf_peephole_optimizer.py +1 -1
  157. angr/analyses/deobfuscator/string_obf_opt_passes.py +3 -3
  158. angr/analyses/deobfuscator/string_obf_peephole_optimizer.py +2 -2
  159. angr/analyses/propagator/propagator.py +1 -1
  160. angr/analyses/proximity_graph.py +2 -2
  161. angr/analyses/reaching_definitions/engine_ail.py +1 -1
  162. angr/analyses/reaching_definitions/reaching_definitions.py +1 -1
  163. angr/analyses/reaching_definitions/subject.py +1 -1
  164. angr/analyses/s_liveness.py +2 -2
  165. angr/analyses/s_propagator.py +3 -3
  166. angr/analyses/s_reaching_definitions/s_rda_model.py +1 -1
  167. angr/analyses/s_reaching_definitions/s_rda_view.py +3 -3
  168. angr/analyses/s_reaching_definitions/s_reaching_definitions.py +3 -3
  169. angr/analyses/variable_recovery/engine_ail.py +2 -2
  170. angr/analyses/variable_recovery/engine_base.py +1 -1
  171. angr/analyses/variable_recovery/variable_recovery_base.py +1 -1
  172. angr/analyses/variable_recovery/variable_recovery_fast.py +2 -2
  173. angr/engines/light/data.py +1 -1
  174. angr/engines/light/engine.py +1 -1
  175. angr/knowledge_plugins/key_definitions/atoms.py +1 -1
  176. angr/knowledge_plugins/propagations/prop_value.py +1 -1
  177. angr/knowledge_plugins/propagations/propagation_model.py +1 -1
  178. angr/knowledge_plugins/propagations/states.py +1 -1
  179. angr/knowledge_plugins/variables/variable_manager.py +1 -1
  180. angr/rustylib.abi3.so +0 -0
  181. angr/utils/ail.py +4 -4
  182. angr/utils/endness.py +1 -1
  183. angr/utils/ssa/__init__.py +14 -4
  184. angr/utils/ssa/tmp_uses_collector.py +4 -4
  185. angr/utils/ssa/vvar_uses_collector.py +4 -4
  186. {angr-9.2.158.dist-info → angr-9.2.159.dist-info}/METADATA +6 -6
  187. {angr-9.2.158.dist-info → angr-9.2.159.dist-info}/RECORD +191 -179
  188. {angr-9.2.158.dist-info → angr-9.2.159.dist-info}/WHEEL +0 -0
  189. {angr-9.2.158.dist-info → angr-9.2.159.dist-info}/entry_points.txt +0 -0
  190. {angr-9.2.158.dist-info → angr-9.2.159.dist-info}/licenses/LICENSE +0 -0
  191. {angr-9.2.158.dist-info → angr-9.2.159.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,978 @@
1
+ # pylint:disable=isinstance-second-argument-not-valid-type,no-self-use,arguments-renamed,too-many-boolean-expressions
2
+ from __future__ import annotations
3
+ from typing import TYPE_CHECKING
4
+ from collections.abc import Sequence
5
+ from abc import ABC, abstractmethod
6
+ from typing_extensions import Self
7
+
8
+ try:
9
+ import claripy
10
+ except ImportError:
11
+ claripy = None
12
+
13
+ from .utils import stable_hash, is_none_or_likeable, is_none_or_matchable
14
+ from .tagged_object import TaggedObject
15
+ from .expression import Atom, Expression, DirtyExpression
16
+
17
+ if TYPE_CHECKING:
18
+ from angr.calling_conventions import SimCC
19
+
20
+
21
+ class Statement(TaggedObject, ABC):
22
+ """
23
+ The base class of all AIL statements.
24
+ """
25
+
26
+ __slots__ = ()
27
+
28
+ @abstractmethod
29
+ def __repr__(self):
30
+ raise NotImplementedError
31
+
32
+ @abstractmethod
33
+ def __str__(self):
34
+ raise NotImplementedError
35
+
36
+ @abstractmethod
37
+ def replace(self, old_expr: Expression, new_expr: Expression) -> tuple[bool, Self]:
38
+ raise NotImplementedError
39
+
40
+ def eq(self, expr0, expr1): # pylint:disable=no-self-use
41
+ if claripy is not None and (isinstance(expr0, claripy.ast.Base) or isinstance(expr1, claripy.ast.Base)):
42
+ return expr0 is expr1
43
+ return expr0 == expr1
44
+
45
+ @abstractmethod
46
+ def likes(self, other) -> bool: # pylint:disable=unused-argument,no-self-use
47
+ raise NotImplementedError
48
+
49
+ @abstractmethod
50
+ def matches(self, other) -> bool: # pylint:disable=unused-argument,no-self-use
51
+ raise NotImplementedError
52
+
53
+
54
+ class Assignment(Statement):
55
+ """
56
+ Assignment statement: expr_a = expr_b
57
+ """
58
+
59
+ __slots__ = (
60
+ "dst",
61
+ "src",
62
+ )
63
+
64
+ def __init__(self, idx: int | None, dst: Atom, src: Expression, **kwargs):
65
+ super().__init__(idx, **kwargs)
66
+
67
+ self.dst = dst
68
+ self.src = src
69
+
70
+ def __eq__(self, other):
71
+ return type(other) is Assignment and self.idx == other.idx and self.dst == other.dst and self.src == other.src
72
+
73
+ def likes(self, other):
74
+ return type(other) is Assignment and self.dst.likes(other.dst) and self.src.likes(other.src)
75
+
76
+ def matches(self, other):
77
+ return type(other) is Assignment and self.dst.matches(other.dst) and self.src.matches(other.src)
78
+
79
+ __hash__ = TaggedObject.__hash__
80
+
81
+ def _hash_core(self):
82
+ return stable_hash((Assignment, self.idx, self.dst, self.src))
83
+
84
+ def __repr__(self):
85
+ return f"Assignment ({self.dst}, {self.src})"
86
+
87
+ def __str__(self):
88
+ return f"{self.dst!s} = {self.src!s}"
89
+
90
+ def replace(self, old_expr: Expression, new_expr: Expression):
91
+ if self.dst == old_expr:
92
+ r_dst = True
93
+ assert isinstance(new_expr, Atom)
94
+ replaced_dst = new_expr
95
+ else:
96
+ r_dst, replaced_dst = self.dst.replace(old_expr, new_expr)
97
+
98
+ if self.src == old_expr:
99
+ r_src = True
100
+ replaced_src = new_expr
101
+ else:
102
+ r_src, replaced_src = self.src.replace(old_expr, new_expr)
103
+
104
+ if r_dst or r_src:
105
+ return True, Assignment(self.idx, replaced_dst, replaced_src, **self.tags)
106
+ return False, self
107
+
108
+ def copy(self) -> Assignment:
109
+ return Assignment(self.idx, self.dst, self.src, **self.tags)
110
+
111
+
112
+ class WeakAssignment(Statement):
113
+ """
114
+ An assignment statement that does not create a new variable at its destination; It should be seen as
115
+ operator=(&dst, &src) in C++-like syntax.
116
+ """
117
+
118
+ __slots__ = (
119
+ "dst",
120
+ "src",
121
+ )
122
+
123
+ def __init__(self, idx: int | None, dst: Atom, src: Expression, **kwargs):
124
+ super().__init__(idx, **kwargs)
125
+
126
+ self.dst = dst
127
+ self.src = src
128
+
129
+ def __eq__(self, other):
130
+ return (
131
+ type(other) is WeakAssignment and self.idx == other.idx and self.dst == other.dst and self.src == other.src
132
+ )
133
+
134
+ def likes(self, other):
135
+ return type(other) is WeakAssignment and self.dst.likes(other.dst) and self.src.likes(other.src)
136
+
137
+ def matches(self, other):
138
+ return type(other) is WeakAssignment and self.dst.matches(other.dst) and self.src.matches(other.src)
139
+
140
+ __hash__ = TaggedObject.__hash__
141
+
142
+ def _hash_core(self):
143
+ return stable_hash((WeakAssignment, self.idx, self.dst, self.src))
144
+
145
+ def __repr__(self):
146
+ return f"WeakAssignment ({self.dst}, {self.src})"
147
+
148
+ def __str__(self):
149
+ return f"{self.dst!s} =W {self.src!s}"
150
+
151
+ def replace(self, old_expr: Expression, new_expr: Expression):
152
+ if self.dst == old_expr:
153
+ r_dst = True
154
+ assert isinstance(new_expr, Atom)
155
+ replaced_dst = new_expr
156
+ else:
157
+ r_dst, replaced_dst = self.dst.replace(old_expr, new_expr)
158
+
159
+ if self.src == old_expr:
160
+ r_src = True
161
+ replaced_src = new_expr
162
+ else:
163
+ r_src, replaced_src = self.src.replace(old_expr, new_expr)
164
+
165
+ if r_dst or r_src:
166
+ return True, WeakAssignment(self.idx, replaced_dst, replaced_src, **self.tags)
167
+ return False, self
168
+
169
+ def copy(self) -> WeakAssignment:
170
+ return WeakAssignment(self.idx, self.dst, self.src, **self.tags)
171
+
172
+
173
+ class Store(Statement):
174
+ """
175
+ Store statement: *addr = data
176
+ """
177
+
178
+ __slots__ = (
179
+ "addr",
180
+ "data",
181
+ "endness",
182
+ "guard",
183
+ "offset",
184
+ "size",
185
+ "variable",
186
+ )
187
+
188
+ def __init__(
189
+ self,
190
+ idx: int | None,
191
+ addr: Expression,
192
+ data: Expression,
193
+ size: int,
194
+ endness: str,
195
+ guard: Expression | None = None,
196
+ variable=None,
197
+ offset=None,
198
+ **kwargs,
199
+ ):
200
+ super().__init__(idx, **kwargs)
201
+
202
+ self.addr = addr
203
+ self.data = data
204
+ self.size = size
205
+ self.endness = endness
206
+ self.variable = variable
207
+ self.guard = guard
208
+ self.offset = offset # variable_offset
209
+
210
+ def __eq__(self, other):
211
+ return (
212
+ type(other) is Store
213
+ and self.idx == other.idx
214
+ and self.eq(self.addr, other.addr)
215
+ and self.eq(self.data, other.data)
216
+ and self.size == other.size
217
+ and self.guard == other.guard
218
+ and self.endness == other.endness
219
+ )
220
+
221
+ def likes(self, other):
222
+ return (
223
+ type(other) is Store
224
+ and self.addr.likes(other.addr)
225
+ and self.data.likes(other.data)
226
+ and self.size == other.size
227
+ and self.guard == other.guard
228
+ and self.endness == other.endness
229
+ )
230
+
231
+ def matches(self, other):
232
+ return (
233
+ type(other) is Store
234
+ and self.addr.matches(other.addr)
235
+ and self.data.matches(other.data)
236
+ and self.size == other.size
237
+ and self.guard == other.guard
238
+ and self.endness == other.endness
239
+ )
240
+
241
+ __hash__ = TaggedObject.__hash__
242
+
243
+ def _hash_core(self):
244
+ return stable_hash((Store, self.idx, self.addr, self.data, self.size, self.endness, self.guard))
245
+
246
+ def __repr__(self):
247
+ return f"Store ({self.addr}, {self.data}[{self.size}])" + ("" if self.guard is None else f"[{self.guard}]")
248
+
249
+ def __str__(self):
250
+ if self.variable is None:
251
+ return f"STORE(addr={self.addr}, data={self.data!s}, size={self.size}, endness={self.endness}, guard={self.guard})"
252
+ return f"{self.variable.name} ={'L' if self.endness == 'Iend_LE' else 'B'} {self.data}<{self.size}>" + (
253
+ "" if self.guard is None else f"[{self.guard}]"
254
+ )
255
+
256
+ def replace(self, old_expr, new_expr):
257
+ if self.addr.likes(old_expr):
258
+ r_addr = True
259
+ replaced_addr = new_expr
260
+ else:
261
+ r_addr, replaced_addr = self.addr.replace(old_expr, new_expr)
262
+
263
+ if isinstance(self.data, Expression):
264
+ if self.data.likes(old_expr):
265
+ r_data = True
266
+ replaced_data = new_expr
267
+ else:
268
+ r_data, replaced_data = self.data.replace(old_expr, new_expr)
269
+ else:
270
+ r_data, replaced_data = False, self.data
271
+
272
+ if self.guard is not None:
273
+ r_guard, replaced_guard = self.guard.replace(old_expr, new_expr)
274
+ else:
275
+ r_guard, replaced_guard = False, None
276
+
277
+ if r_addr or r_data or r_guard:
278
+ return True, Store(
279
+ self.idx,
280
+ replaced_addr,
281
+ replaced_data,
282
+ self.size,
283
+ self.endness,
284
+ guard=replaced_guard,
285
+ variable=self.variable,
286
+ **self.tags,
287
+ )
288
+ return False, self
289
+
290
+ def copy(self) -> Store:
291
+ return Store(
292
+ self.idx,
293
+ self.addr,
294
+ self.data,
295
+ self.size,
296
+ self.endness,
297
+ guard=self.guard,
298
+ variable=self.variable,
299
+ offset=self.offset,
300
+ **self.tags,
301
+ )
302
+
303
+
304
+ class Jump(Statement):
305
+ """
306
+ Jump statement: goto target
307
+ """
308
+
309
+ __slots__ = (
310
+ "target",
311
+ "target_idx",
312
+ )
313
+
314
+ def __init__(self, idx: int | None, target: Expression, target_idx: int | None = None, **kwargs):
315
+ super().__init__(idx, **kwargs)
316
+
317
+ self.target = target
318
+ self.target_idx = target_idx
319
+
320
+ def __eq__(self, other):
321
+ return type(other) is Jump and self.idx == other.idx and self.target == other.target
322
+
323
+ def likes(self, other):
324
+ return type(other) is Jump and is_none_or_likeable(self.target, other.target)
325
+
326
+ def matches(self, other):
327
+ return type(other) is Jump and is_none_or_matchable(self.target, other.target)
328
+
329
+ __hash__ = TaggedObject.__hash__
330
+
331
+ def _hash_core(self):
332
+ return stable_hash((Jump, self.idx, self.target))
333
+
334
+ def __repr__(self):
335
+ if self.target_idx is not None:
336
+ return f"Jump ({self.target}.{self.target_idx})"
337
+ return f"Jump ({self.target})"
338
+
339
+ def __str__(self):
340
+ if self.target_idx is not None:
341
+ return f"Goto({self.target}.{self.target_idx})"
342
+ return f"Goto({self.target})"
343
+
344
+ @property
345
+ def depth(self):
346
+ return self.target.depth
347
+
348
+ def replace(self, old_expr, new_expr):
349
+ r, replaced_target = self.target.replace(old_expr, new_expr)
350
+
351
+ if r:
352
+ return True, Jump(self.idx, replaced_target, **self.tags)
353
+ return False, self
354
+
355
+ def copy(self):
356
+ return Jump(
357
+ self.idx,
358
+ self.target,
359
+ **self.tags,
360
+ )
361
+
362
+
363
+ class ConditionalJump(Statement):
364
+ """
365
+ if (cond) {true_target} else {false_target}
366
+ """
367
+
368
+ __slots__ = (
369
+ "condition",
370
+ "false_target",
371
+ "false_target_idx",
372
+ "true_target",
373
+ "true_target_idx",
374
+ )
375
+
376
+ def __init__(
377
+ self,
378
+ idx: int | None,
379
+ condition: Expression,
380
+ true_target: Expression | None,
381
+ false_target: Expression | None,
382
+ true_target_idx: int | None = None,
383
+ false_target_idx: int | None = None,
384
+ **kwargs,
385
+ ):
386
+ super().__init__(idx, **kwargs)
387
+
388
+ self.condition = condition
389
+ self.true_target = true_target
390
+ self.false_target = false_target
391
+ self.true_target_idx = true_target_idx
392
+ self.false_target_idx = false_target_idx
393
+
394
+ def __eq__(self, other):
395
+ return (
396
+ type(other) is ConditionalJump
397
+ and self.idx == other.idx
398
+ and self.condition == other.condition
399
+ and self.true_target == other.true_target
400
+ and self.false_target == other.false_target
401
+ and self.true_target_idx == other.true_target_idx
402
+ and self.false_target_idx == other.false_target_idx
403
+ )
404
+
405
+ def likes(self, other):
406
+ return (
407
+ type(other) is ConditionalJump
408
+ and self.condition.likes(other.condition)
409
+ and is_none_or_likeable(self.true_target, other.true_target)
410
+ and is_none_or_likeable(self.false_target, other.false_target)
411
+ )
412
+
413
+ def matches(self, other):
414
+ return (
415
+ type(other) is ConditionalJump
416
+ and self.condition.matches(other.condition)
417
+ and is_none_or_matchable(self.true_target, other.true_target)
418
+ and is_none_or_matchable(self.false_target, other.false_target)
419
+ )
420
+
421
+ __hash__ = TaggedObject.__hash__
422
+
423
+ def _hash_core(self):
424
+ return stable_hash(
425
+ (
426
+ ConditionalJump,
427
+ self.idx,
428
+ self.condition,
429
+ self.true_target,
430
+ self.false_target,
431
+ self.true_target_idx,
432
+ self.false_target_idx,
433
+ )
434
+ )
435
+
436
+ def __repr__(self):
437
+ return "ConditionalJump (condition: {}, true: {}{}, false: {}{})".format(
438
+ self.condition,
439
+ self.true_target,
440
+ "" if self.true_target_idx is None else f".{self.true_target_idx}",
441
+ self.false_target,
442
+ "" if self.false_target_idx is None else f".{self.false_target_idx}",
443
+ )
444
+
445
+ def __str__(self):
446
+ return "if ({}) {{ Goto {}{} }} else {{ Goto {}{} }}".format(
447
+ self.condition,
448
+ self.true_target,
449
+ "" if self.true_target_idx is None else f".{self.true_target_idx}",
450
+ self.false_target,
451
+ "" if self.false_target_idx is None else f".{self.false_target_idx}",
452
+ )
453
+
454
+ def replace(self, old_expr, new_expr):
455
+ if self.condition == old_expr:
456
+ r_cond = True
457
+ replaced_cond = new_expr
458
+ else:
459
+ r_cond, replaced_cond = self.condition.replace(old_expr, new_expr)
460
+
461
+ if self.true_target is not None:
462
+ if self.true_target == old_expr:
463
+ r_true = True
464
+ replaced_true = new_expr
465
+ else:
466
+ r_true, replaced_true = self.true_target.replace(old_expr, new_expr)
467
+ else:
468
+ r_true, replaced_true = False, self.true_target
469
+
470
+ if self.false_target is not None:
471
+ if self.false_target == old_expr:
472
+ r_false = True
473
+ replaced_false = new_expr
474
+ else:
475
+ r_false, replaced_false = self.false_target.replace(old_expr, new_expr)
476
+ else:
477
+ r_false, replaced_false = False, self.false_target
478
+
479
+ r = r_cond or r_true or r_false
480
+
481
+ if r:
482
+ return True, ConditionalJump(
483
+ self.idx,
484
+ replaced_cond,
485
+ replaced_true,
486
+ replaced_false,
487
+ true_target_idx=self.true_target_idx,
488
+ false_target_idx=self.false_target_idx,
489
+ **self.tags,
490
+ )
491
+ return False, self
492
+
493
+ def copy(self) -> ConditionalJump:
494
+ return ConditionalJump(
495
+ self.idx,
496
+ self.condition,
497
+ self.true_target,
498
+ self.false_target,
499
+ true_target_idx=self.true_target_idx,
500
+ false_target_idx=self.false_target_idx,
501
+ **self.tags,
502
+ )
503
+
504
+
505
+ class Call(Expression, Statement):
506
+ """
507
+ Call is both an expression and a statement.
508
+
509
+ When used as a statement, it will set ret_expr, fp_ret_expr, or both if both of them should hold return values.
510
+ When used as an expression, both ret_expr and fp_ret_expr should be None (and should be ignored). The size of the
511
+ call expression is stored in the bits attribute.
512
+ """
513
+
514
+ __slots__ = (
515
+ "args",
516
+ "calling_convention",
517
+ "fp_ret_expr",
518
+ "prototype",
519
+ "ret_expr",
520
+ "target",
521
+ )
522
+
523
+ def __init__(
524
+ self,
525
+ idx: int | None,
526
+ target: Expression | str,
527
+ calling_convention: SimCC | None = None,
528
+ prototype=None,
529
+ args: Sequence[Expression] | None = None,
530
+ ret_expr: Expression | None = None,
531
+ fp_ret_expr: Expression | None = None,
532
+ bits: int | None = None,
533
+ **kwargs,
534
+ ):
535
+ super().__init__(idx, target.depth + 1 if isinstance(target, Expression) else 1, **kwargs)
536
+
537
+ self.target = target
538
+ self.calling_convention = calling_convention
539
+ self.prototype = prototype
540
+ self.args = args
541
+ self.ret_expr = ret_expr
542
+ self.fp_ret_expr = fp_ret_expr
543
+ if bits is not None:
544
+ self.bits = bits
545
+ elif ret_expr is not None:
546
+ self.bits = ret_expr.bits
547
+ elif fp_ret_expr is not None:
548
+ self.bits = fp_ret_expr.bits
549
+ else:
550
+ self.bits = 0 # uhhhhhhhhhhhhhhhhhhh
551
+
552
+ def likes(self, other):
553
+ return (
554
+ type(other) is Call
555
+ and is_none_or_likeable(self.target, other.target)
556
+ and self.calling_convention == other.calling_convention
557
+ and self.prototype == other.prototype
558
+ and is_none_or_likeable(self.args, other.args, is_list=True)
559
+ and is_none_or_likeable(self.ret_expr, other.ret_expr)
560
+ and is_none_or_likeable(self.fp_ret_expr, other.fp_ret_expr)
561
+ )
562
+
563
+ def matches(self, other):
564
+ return (
565
+ type(other) is Call
566
+ and is_none_or_matchable(self.target, other.target)
567
+ and self.calling_convention == other.calling_convention
568
+ and self.prototype == other.prototype
569
+ and is_none_or_matchable(self.args, other.args, is_list=True)
570
+ and is_none_or_matchable(self.ret_expr, other.ret_expr)
571
+ and is_none_or_matchable(self.fp_ret_expr, other.fp_ret_expr)
572
+ )
573
+
574
+ __hash__ = TaggedObject.__hash__ # type: ignore
575
+
576
+ def _hash_core(self):
577
+ return stable_hash((Call, self.idx, self.target))
578
+
579
+ def __repr__(self):
580
+ return f"Call (target: {self.target}, prototype: {self.prototype}, args: {self.args})"
581
+
582
+ def __str__(self):
583
+ cc = "Unknown CC" if self.calling_convention is None else f"{self.calling_convention}"
584
+ if self.args is None:
585
+ if self.calling_convention is not None:
586
+ s = (
587
+ (f"{cc}")
588
+ if self.prototype is None
589
+ else f"{self.calling_convention}: {self.calling_convention.arg_locs(self.prototype)}"
590
+ )
591
+ else:
592
+ s = (f"{cc}") if self.prototype is None else repr(self.prototype)
593
+ else:
594
+ s = (f"{cc}: {self.args}") if self.prototype is None else f"{self.calling_convention}: {self.args}"
595
+
596
+ ret_s = "no-ret-value" if self.ret_expr is None else f"{self.ret_expr}"
597
+ fp_ret_s = "no-fp-ret-value" if self.fp_ret_expr is None else f"{self.fp_ret_expr}"
598
+
599
+ return f"Call({self.target}, {s}, ret: {ret_s}, fp_ret: {fp_ret_s})"
600
+
601
+ @property
602
+ def size(self):
603
+ return self.bits // 8
604
+
605
+ @property
606
+ def verbose_op(self):
607
+ return "call"
608
+
609
+ @property
610
+ def op(self):
611
+ return "call"
612
+
613
+ def replace(self, old_expr: Expression, new_expr: Expression):
614
+ if isinstance(self.target, Expression):
615
+ r0, replaced_target = self.target.replace(old_expr, new_expr)
616
+ else:
617
+ r0 = False
618
+ replaced_target = self.target
619
+
620
+ r = r0
621
+
622
+ new_args = None
623
+ if self.args:
624
+ new_args = []
625
+ for arg in self.args:
626
+ if arg == old_expr:
627
+ r_arg = True
628
+ replaced_arg = new_expr
629
+ else:
630
+ r_arg, replaced_arg = arg.replace(old_expr, new_expr)
631
+ r |= r_arg
632
+ new_args.append(replaced_arg)
633
+
634
+ new_ret_expr = self.ret_expr
635
+ new_bits = self.bits
636
+ if self.ret_expr:
637
+ if self.ret_expr == old_expr:
638
+ r_ret = True
639
+ replaced_ret = new_expr
640
+ else:
641
+ r_ret, replaced_ret = self.ret_expr.replace(old_expr, new_expr)
642
+ r |= r_ret
643
+ new_ret_expr = replaced_ret
644
+ if replaced_ret is not None:
645
+ new_bits = replaced_ret.bits
646
+
647
+ new_fp_ret_expr = self.fp_ret_expr
648
+ if self.fp_ret_expr:
649
+ if self.fp_ret_expr == old_expr:
650
+ r_ret = True
651
+ replaced_fp_ret = new_expr
652
+ else:
653
+ r_ret, replaced_fp_ret = self.fp_ret_expr.replace(old_expr, new_expr)
654
+ r |= r_ret
655
+ new_fp_ret_expr = replaced_fp_ret
656
+
657
+ if r:
658
+ return True, Call(
659
+ self.idx,
660
+ replaced_target,
661
+ calling_convention=self.calling_convention,
662
+ prototype=self.prototype,
663
+ args=new_args,
664
+ ret_expr=new_ret_expr,
665
+ fp_ret_expr=new_fp_ret_expr,
666
+ bits=new_bits,
667
+ **self.tags,
668
+ )
669
+ return False, self
670
+
671
+ def copy(self):
672
+ return Call(
673
+ self.idx,
674
+ self.target,
675
+ calling_convention=self.calling_convention,
676
+ prototype=self.prototype,
677
+ args=self.args[::] if self.args is not None else None,
678
+ ret_expr=self.ret_expr,
679
+ fp_ret_expr=self.fp_ret_expr,
680
+ bits=self.bits,
681
+ **self.tags,
682
+ )
683
+
684
+
685
+ class Return(Statement):
686
+ """
687
+ Return statement: (return expr_a), (return)
688
+ """
689
+
690
+ __slots__ = ("ret_exprs",)
691
+
692
+ def __init__(self, idx: int | None, ret_exprs, **kwargs):
693
+ super().__init__(idx, **kwargs)
694
+ self.ret_exprs = ret_exprs if isinstance(ret_exprs, list) else list(ret_exprs)
695
+
696
+ def __eq__(self, other):
697
+ return type(other) is Return and self.idx == other.idx and self.ret_exprs == other.ret_exprs
698
+
699
+ def likes(self, other):
700
+ return type(other) is Return and is_none_or_likeable(self.ret_exprs, other.ret_exprs, is_list=True)
701
+
702
+ def matches(self, other):
703
+ return type(other) is Return and is_none_or_matchable(self.ret_exprs, other.ret_exprs, is_list=True)
704
+
705
+ __hash__ = TaggedObject.__hash__
706
+
707
+ def _hash_core(self):
708
+ return stable_hash((Return, self.idx, tuple(self.ret_exprs)))
709
+
710
+ def __repr__(self):
711
+ return "Return to ({})".format(",".join(repr(x) for x in self.ret_exprs))
712
+
713
+ def __str__(self):
714
+ exprs = ",".join(str(ret_expr) for ret_expr in self.ret_exprs)
715
+ if not exprs:
716
+ return "return;"
717
+ return f"return {exprs};"
718
+
719
+ def replace(self, old_expr, new_expr):
720
+ new_ret_exprs = []
721
+ replaced = False
722
+
723
+ for expr in self.ret_exprs:
724
+ if expr == old_expr:
725
+ r_expr = True
726
+ replaced_expr = new_expr
727
+ else:
728
+ r_expr, replaced_expr = expr.replace(old_expr, new_expr)
729
+ if r_expr:
730
+ replaced = True
731
+ new_ret_exprs.append(replaced_expr)
732
+ else:
733
+ new_ret_exprs.append(expr)
734
+
735
+ if replaced:
736
+ return True, Return(
737
+ self.idx,
738
+ new_ret_exprs,
739
+ **self.tags,
740
+ )
741
+
742
+ return False, self
743
+
744
+ def copy(self):
745
+ return Return(
746
+ self.idx,
747
+ self.ret_exprs[::],
748
+ **self.tags,
749
+ )
750
+
751
+
752
+ class CAS(Statement):
753
+ """
754
+ Atomic compare-and-swap.
755
+
756
+ *_lo and *_hi are used to represent the low and high parts of a 128-bit CAS operation; *_hi is None if the CAS
757
+ operation works on values that are less than or equal to 64 bits.
758
+
759
+ addr: The address to be compared and swapped.
760
+ data: The value to be written if the comparison is successful.
761
+ expd: The expected value to be compared against.
762
+ old: The value that is currently stored at addr before compare-and-swap; it will be returned after compare-and-swap.
763
+ """
764
+
765
+ __slots__ = ("addr", "data_hi", "data_lo", "endness", "expd_hi", "expd_lo", "old_hi", "old_lo")
766
+
767
+ def __init__(
768
+ self,
769
+ idx: int | None,
770
+ addr: Expression,
771
+ data_lo: Expression,
772
+ data_hi: Expression | None,
773
+ expd_lo: Expression,
774
+ expd_hi: Expression | None,
775
+ old_lo: Atom,
776
+ old_hi: Atom | None,
777
+ endness: str,
778
+ **kwargs,
779
+ ):
780
+ super().__init__(idx, **kwargs)
781
+ self.addr = addr
782
+ self.data_lo = data_lo
783
+ self.data_hi = data_hi
784
+ self.expd_lo = expd_lo
785
+ self.expd_hi = expd_hi
786
+ self.old_lo = old_lo
787
+ self.old_hi = old_hi
788
+ self.endness = endness
789
+
790
+ def _hash_core(self):
791
+ return stable_hash(
792
+ (
793
+ CAS,
794
+ self.idx,
795
+ self.addr,
796
+ self.data_lo,
797
+ self.data_hi,
798
+ self.expd_lo,
799
+ self.expd_hi,
800
+ self.old_lo,
801
+ self.old_hi,
802
+ self.endness,
803
+ )
804
+ )
805
+
806
+ __hash__ = TaggedObject.__hash__
807
+
808
+ def __repr__(self):
809
+ if self.old_hi is None:
810
+ return f"CAS({self.addr}, {self.data_lo}, {self.expd_lo}, {self.old_lo})"
811
+ return (
812
+ f"CAS({self.addr}, {self.data_hi} .. {self.data_lo}, {self.expd_hi} .. {self.expd_lo}, "
813
+ f"{self.old_hi} .. {self.old_lo})"
814
+ )
815
+
816
+ def __str__(self):
817
+ if self.old_hi is None:
818
+ return f"{self.old_lo} = CAS({self.addr}, {self.data_lo}, {self.expd_lo})"
819
+ return (
820
+ f"{self.old_hi} .. {self.old_lo} = CAS({self.addr}, {self.data_hi} .. {self.data_lo}, "
821
+ f"{self.expd_hi} .. {self.expd_lo})"
822
+ )
823
+
824
+ def replace(self, old_expr: Expression, new_expr: Expression) -> tuple[bool, CAS]:
825
+ r_addr, replaced_addr = self.addr.replace(old_expr, new_expr)
826
+ r_data_lo, replaced_data_lo = self.data_lo.replace(old_expr, new_expr)
827
+ r_data_hi, replaced_data_hi = self.data_hi.replace(old_expr, new_expr) if self.data_hi else (False, None)
828
+ r_expd_lo, replaced_expd_lo = self.expd_lo.replace(old_expr, new_expr)
829
+ r_expd_hi, replaced_expd_hi = self.expd_hi.replace(old_expr, new_expr) if self.expd_hi else (False, None)
830
+ r_old_lo, replaced_old_lo = self.old_lo.replace(old_expr, new_expr)
831
+ r_old_hi, replaced_old_hi = self.old_hi.replace(old_expr, new_expr) if self.old_hi else (False, None)
832
+
833
+ if r_addr or r_data_lo or r_data_hi or r_expd_lo or r_expd_hi or r_old_lo or r_old_hi:
834
+ return True, CAS(
835
+ self.idx,
836
+ replaced_addr,
837
+ replaced_data_lo,
838
+ replaced_data_hi,
839
+ replaced_expd_lo,
840
+ replaced_expd_hi,
841
+ replaced_old_lo,
842
+ replaced_old_hi,
843
+ endness=self.endness,
844
+ **self.tags,
845
+ )
846
+ return False, self
847
+
848
+ def copy(self) -> CAS:
849
+ return CAS(
850
+ self.idx,
851
+ self.addr,
852
+ self.data_lo,
853
+ self.data_hi,
854
+ self.expd_lo,
855
+ self.expd_hi,
856
+ self.old_lo,
857
+ self.old_hi,
858
+ endness=self.endness,
859
+ **self.tags,
860
+ )
861
+
862
+ def likes(self, other) -> bool:
863
+ return (
864
+ type(other) is CAS
865
+ and self.addr.likes(other.addr)
866
+ and self.data_lo.likes(other.data_lo)
867
+ and (self.data_hi is None or self.data_hi.likes(other.data_hi))
868
+ and self.expd_lo.likes(other.expd_lo)
869
+ and (self.expd_hi is None or self.expd_hi.likes(other.expd_hi))
870
+ and self.old_lo.likes(other.old_lo)
871
+ and (self.old_hi is None or self.old_hi.likes(other.old_hi))
872
+ )
873
+
874
+ def matches(self, other) -> bool:
875
+ return (
876
+ type(other) is CAS
877
+ and self.addr.matches(other.addr)
878
+ and self.data_lo.matches(other.data_lo)
879
+ and (self.data_hi is None or self.data_hi.matches(other.data_hi))
880
+ and self.expd_lo.matches(other.expd_lo)
881
+ and (self.expd_hi is None or self.expd_hi.matches(other.expd_hi))
882
+ and self.old_lo.matches(other.old_lo)
883
+ and (self.old_hi is None or self.old_hi.matches(other.old_hi))
884
+ )
885
+
886
+ @property
887
+ def bits(self) -> int:
888
+ if self.old_hi is None:
889
+ return self.old_lo.bits
890
+ return self.old_lo.bits + self.old_hi.bits
891
+
892
+ @property
893
+ def size(self) -> int:
894
+ return self.bits // 8
895
+
896
+
897
+ class DirtyStatement(Statement):
898
+ """
899
+ Wrapper around the original statement, which is usually not convertible (temporarily).
900
+ """
901
+
902
+ __slots__ = ("dirty",)
903
+
904
+ def __init__(self, idx: int | None, dirty: DirtyExpression, **kwargs):
905
+ super().__init__(idx, **kwargs)
906
+ self.dirty = dirty
907
+
908
+ def _hash_core(self):
909
+ return stable_hash((DirtyStatement, self.dirty))
910
+
911
+ def __repr__(self):
912
+ return repr(self.dirty)
913
+
914
+ def __str__(self):
915
+ return str(self.dirty)
916
+
917
+ def replace(self, old_expr, new_expr):
918
+ if self.dirty == old_expr:
919
+ assert isinstance(new_expr, DirtyExpression)
920
+ return True, DirtyStatement(self.idx, new_expr, **self.tags)
921
+ r, new_dirty = self.dirty.replace(old_expr, new_expr)
922
+ if r:
923
+ return True, DirtyStatement(self.idx, new_dirty, **self.tags)
924
+ return False, self
925
+
926
+ def copy(self) -> DirtyStatement:
927
+ return DirtyStatement(self.idx, self.dirty, **self.tags)
928
+
929
+ def likes(self, other):
930
+ return type(other) is DirtyStatement and self.dirty.likes(other.dirty)
931
+
932
+ def matches(self, other):
933
+ return type(other) is DirtyStatement and self.dirty.matches(other.dirty)
934
+
935
+
936
+ class Label(Statement):
937
+ """
938
+ A dummy statement that indicates a label with a name.
939
+ """
940
+
941
+ __slots__ = (
942
+ "block_idx",
943
+ "ins_addr",
944
+ "name",
945
+ )
946
+
947
+ def __init__(self, idx: int | None, name: str, ins_addr: int, block_idx: int | None = None, **kwargs):
948
+ super().__init__(idx, **kwargs)
949
+ self.name = name
950
+ self.ins_addr = ins_addr
951
+ self.block_idx = block_idx
952
+
953
+ def likes(self, other: Label):
954
+ return isinstance(other, Label)
955
+
956
+ def replace(self, old_expr, new_expr):
957
+ return False, self
958
+
959
+ matches = likes
960
+
961
+ def _hash_core(self):
962
+ return stable_hash(
963
+ (
964
+ Label,
965
+ self.name,
966
+ self.ins_addr,
967
+ self.block_idx,
968
+ )
969
+ )
970
+
971
+ def __repr__(self):
972
+ return f"Label {self.name}"
973
+
974
+ def __str__(self):
975
+ return f"{self.name}:"
976
+
977
+ def copy(self) -> Label:
978
+ return Label(self.idx, self.name, self.ins_addr, self.block_idx, **self.tags)