1
1
from __future__ import annotations
2
2
3
3
import logging
4
- from typing import TYPE_CHECKING
4
+ from collections .abc import Callable
5
+ from typing import TYPE_CHECKING , NamedTuple
5
6
6
- from pyk .cterm import CTerm
7
+ from pyk .cterm import CTerm , CTermSymbolic
7
8
from pyk .kast import KInner
8
9
from pyk .kast .inner import (
9
10
KApply ,
12
13
KSort ,
13
14
KToken ,
14
15
KVariable ,
16
+ Subst ,
15
17
bottom_up ,
16
18
build_assoc ,
17
19
build_cons ,
18
20
top_down ,
19
21
)
20
22
from pyk .kast .manip import abstract_term_safely , flatten_label , set_cell
21
23
from pyk .kast .pretty import paren
22
- from pyk .kcfg .kcfg import Step
24
+ from pyk .kcfg .kcfg import KCFGExtendResult , Step
23
25
from pyk .kcfg .semantics import DefaultSemantics
24
26
from pyk .kcfg .show import NodePrinter
25
27
from pyk .ktool .kprove import KProve
37
39
from pathlib import Path
38
40
from typing import Final
39
41
40
- from pyk .cterm import CTermSymbolic
41
- from pyk .kast .inner import KAst , Subst
42
+ from pyk .kast .inner import KAst
42
43
from pyk .kast .outer import KFlatModule
43
44
from pyk .kcfg import KCFG
44
- from pyk .kcfg .semantics import KCFGExtendResult
45
45
from pyk .ktool .kprint import SymbolTable
46
46
from pyk .utils import BugReport
47
47
48
48
_LOGGER : Final = logging .getLogger (__name__ )
49
49
50
+ CustomStepImpl = Callable [[Subst , CTerm , CTermSymbolic ], KCFGExtendResult | None ]
51
+
52
+
53
+ class CustomStep (NamedTuple ):
54
+ """Encapsulates a custom step definition consisting of an abstract pattern and its execution function."""
55
+
56
+ pattern : KSequence
57
+ exec_fn : CustomStepImpl
58
+
59
+ def check_pattern_match (self , cterm : CTerm ) -> bool :
60
+ return self .pattern .match (cterm .cell ('K_CELL' )) is not None
61
+
62
+ def try_execute (self , cterm : CTerm , cterm_symbolic : CTermSymbolic ) -> KCFGExtendResult | None :
63
+ subst = self .pattern .match (cterm .cell ('K_CELL' ))
64
+ if subst is not None :
65
+ return self .exec_fn (subst , cterm , cterm_symbolic )
66
+ return None
67
+
68
+
50
69
# KEVM class
51
70
52
71
53
72
class KEVMSemantics (DefaultSemantics ):
54
73
auto_abstract_gas : bool
55
74
allow_symbolic_program : bool
56
- _cached_subst : Subst | None
75
+ _custom_steps : tuple [ CustomStep , ...]
57
76
58
- def __init__ (self , auto_abstract_gas : bool = False , allow_symbolic_program : bool = False ) -> None :
77
+ def __init__ (
78
+ self ,
79
+ auto_abstract_gas : bool = False ,
80
+ allow_symbolic_program : bool = False ,
81
+ custom_step_definitions : tuple [CustomStep , ...] = (),
82
+ ) -> None :
59
83
self .auto_abstract_gas = auto_abstract_gas
60
84
self .allow_symbolic_program = allow_symbolic_program
61
- self ._cached_subst = None
85
+ if custom_step_definitions :
86
+ self ._custom_steps = (
87
+ CustomStep (self ._load_pattern , self ._exec_load_custom_step ),
88
+ ) + custom_step_definitions
89
+ else :
90
+ self ._custom_steps = (CustomStep (self ._load_pattern , self ._exec_load_custom_step ),)
62
91
63
92
@staticmethod
64
93
def is_functional (term : KInner ) -> bool :
@@ -145,11 +174,12 @@ def _replace(term: KInner) -> KInner:
145
174
146
175
return CTerm (config = bottom_up (_replace , cterm .config ), constraints = cterm .constraints )
147
176
148
- def custom_step (self , cterm : CTerm , _cterm_symbolic : CTermSymbolic ) -> KCFGExtendResult | None :
149
- if self ._check_load_pattern (cterm ):
150
- return self ._exec_load_custom_step (cterm )
151
- else :
152
- return None
177
+ def custom_step (self , cterm : CTerm , cterm_symbolic : CTermSymbolic ) -> KCFGExtendResult | None :
178
+ for c_step in self ._custom_steps :
179
+ result = c_step .try_execute (cterm , cterm_symbolic )
180
+ if result is not None :
181
+ return result
182
+ return None
153
183
154
184
@staticmethod
155
185
def cut_point_rules (
@@ -197,26 +227,16 @@ def terminal_rules(break_every_step: bool) -> list[str]:
197
227
terminal_rules .append ('EVM.step' )
198
228
return terminal_rules
199
229
200
- def _check_load_pattern (self , cterm : CTerm ) -> bool :
201
- """Given a CTerm, check if the rule 'EVM.program.load' is at the top of the K_CELL.
202
-
203
- This method checks if the `EVM.program.load` rule is at the top of the `K_CELL` in the given `cterm`.
204
- If the rule matches, the resulting substitution is cached in `_cached_subst` for later use in `custom_step`
205
- :param cterm: The CTerm representing the current state of the proof node.
206
- :return: `True` if the pattern matches and a custom step can be made; `False` otherwise.
207
- """
208
- load_pattern = KSequence ([KApply ('loadProgram' , KVariable ('###BYTECODE' )), KVariable ('###CONTINUATION' )])
209
- self ._cached_subst = load_pattern .match (cterm .cell ('K_CELL' ))
210
- return self ._cached_subst is not None
230
+ @property
231
+ def _load_pattern (self ) -> KSequence :
232
+ return KSequence ([KApply ('loadProgram' , KVariable ('###BYTECODE' )), KVariable ('###CONTINUATION' )])
211
233
212
- def _exec_load_custom_step (self , cterm : CTerm ) -> KCFGExtendResult :
234
+ def _exec_load_custom_step (self , subst : Subst , cterm : CTerm , _c : CTermSymbolic ) -> KCFGExtendResult :
213
235
"""Given a CTerm, update the JUMPDESTS_CELL and PROGRAM_CELL if the rule 'EVM.program.load' is at the top of the K_CELL.
214
236
215
237
:param cterm: CTerm of a proof node.
216
238
:return: If the K_CELL matches the load_pattern, a Step with depth 1 is returned together with the new configuration, also registering that the `EVM.program.load` rule has been applied.
217
239
"""
218
- subst = self ._cached_subst
219
- assert subst is not None
220
240
bytecode_sections = flatten_label ('_+Bytes__BYTES-HOOKED_Bytes_Bytes_Bytes' , subst ['###BYTECODE' ])
221
241
jumpdests_set = compute_jumpdests (bytecode_sections )
222
242
new_cterm = CTerm .from_kast (set_cell (cterm .kast , 'JUMPDESTS_CELL' , jumpdests_set ))
@@ -225,7 +245,7 @@ def _exec_load_custom_step(self, cterm: CTerm) -> KCFGExtendResult:
225
245
return Step (new_cterm , 1 , (), ['EVM.program.load' ], cut = True )
226
246
227
247
def can_make_custom_step (self , cterm : CTerm ) -> bool :
228
- return self . _check_load_pattern (cterm )
248
+ return any ( c_step . check_pattern_match (cterm ) for c_step in self . _custom_steps )
229
249
230
250
def is_mergeable (self , ct1 : CTerm , ct2 : CTerm ) -> bool :
231
251
"""Given two CTerms of Edges' targets, check if they are mergeable.
0 commit comments