37
37
from pyk .utils import ensure_dir_path , hash_str , run_process_2 , single , unique
38
38
39
39
from . import VERSION
40
+ from .solc import CompilationUnit
40
41
from .solc_to_k import Contract , _contract_name_from_bytecode
41
42
from .state_record import RecreateState , StateDiffEntry , StateDumpEntry
42
43
from .utils import (
78
79
UnrefuteNodeOptions ,
79
80
)
80
81
81
-
82
82
_LOGGER : Final = logging .getLogger (__name__ )
83
83
84
84
@@ -339,18 +339,21 @@ def srcmap_data(self, contract_name: str, pc: int) -> tuple[Path, int, int] | No
339
339
_ , start , end = byte_offset_to_lines (src_contract_text .split ('\n ' ), s , l )
340
340
return (src_contract_path , start , end )
341
341
342
+ def solidity_src_print (self , path : Path , start : int , end : int ) -> Iterable [str ]:
343
+ lines = path .read_text ().split ('\n ' )
344
+ prefix_lines = [f' { l } ' for l in lines [:start ]]
345
+ actual_lines = [f' | { l } ' for l in lines [start :end ]]
346
+ suffix_lines = [f' { l } ' for l in lines [end :]]
347
+ return prefix_lines + actual_lines + suffix_lines
348
+
342
349
def solidity_src (self , contract_name : str , pc : int ) -> Iterable [str ]:
343
350
srcmap_data = self .srcmap_data (contract_name , pc )
344
351
if srcmap_data is None :
345
352
return [f'No sourcemap data for contract at pc { contract_name } : { pc } ' ]
346
353
contract_path , start , end = srcmap_data
347
354
if not (contract_path .exists () and contract_path .is_file ()):
348
355
return [f'No file at path for contract { contract_name } : { contract_path } ' ]
349
- lines = contract_path .read_text ().split ('\n ' )
350
- prefix_lines = [f' { l } ' for l in lines [:start ]]
351
- actual_lines = [f' | { l } ' for l in lines [start :end ]]
352
- suffix_lines = [f' { l } ' for l in lines [end :]]
353
- return prefix_lines + actual_lines + suffix_lines
356
+ return self .solidity_src_print (contract_path , start , end )
354
357
355
358
def short_info_for_contract (self , contract_name : str , cterm : CTerm ) -> list [str ]:
356
359
ret_strs = self .kevm .short_info (cterm )
@@ -362,23 +365,44 @@ def short_info_for_contract(self, contract_name: str, cterm: CTerm) -> list[str]
362
365
ret_strs .append (f'src: { str (path )} :{ start } :{ end } ' )
363
366
return ret_strs
364
367
365
- def custom_view (self , contract_name : str , element : KCFGElem ) -> Iterable [str ]:
368
+ def custom_view (self , contract_name : str , element : KCFGElem , compilation_unit : CompilationUnit ) -> Iterable [str ]:
366
369
if type (element ) is KCFG .Node :
367
370
pc_cell = element .cterm .try_cell ('PC_CELL' )
371
+ program_cell = element .cterm .try_cell ('PROGRAM_CELL' )
368
372
if type (pc_cell ) is KToken and pc_cell .sort == INT :
369
- return self .solidity_src (contract_name , int (pc_cell .token ))
373
+ if type (program_cell ) is KToken :
374
+ try :
375
+ bytecode = ast .literal_eval (program_cell .token )
376
+ instruction = compilation_unit .get_instruction (bytecode , int (pc_cell .token ))
377
+ node = instruction .node ()
378
+ start_line , _ , end_line , _ = node .source_range ()
379
+ return self .solidity_src_print (Path (node .source .name ), start_line - 1 , end_line )
380
+ except Exception :
381
+ return [f'No sourcemap data for contract at pc { contract_name } : { int (pc_cell .token )} ' ]
382
+ return ['NO DATA' ]
370
383
elif type (element ) is KCFG .Edge :
371
384
return list (element .rules )
372
385
elif type (element ) is KCFG .NDBranch :
373
386
return list (element .rules )
374
387
return ['NO DATA' ]
375
388
376
389
def build (self , no_metadata : bool ) -> None :
377
- forge_build_args = ['forge' , 'build' , '--build-info' , '--root' , str (self ._root )] + (
378
- ['--no-metadata' ] if no_metadata else []
379
- )
390
+ forge_build_args = [
391
+ 'forge' ,
392
+ 'build' ,
393
+ '--build-info' ,
394
+ '--extra-output' ,
395
+ 'storageLayout' ,
396
+ 'evm.bytecode.generatedSources' ,
397
+ 'evm.deployedBytecode.generatedSources' ,
398
+ '--root' ,
399
+ str (self ._root ),
400
+ ] + (['--no-metadata' ] if no_metadata else [])
380
401
try :
381
- run_process_2 (forge_build_args , logger = _LOGGER )
402
+ run_process_2 (
403
+ forge_build_args ,
404
+ logger = _LOGGER ,
405
+ )
382
406
except FileNotFoundError as err :
383
407
raise RuntimeError (
384
408
"Error: 'forge' command not found. Please ensure that 'forge' is installed and added to your PATH."
@@ -1333,24 +1357,32 @@ class FoundryNodePrinter(KEVMNodePrinter):
1333
1357
foundry : Foundry
1334
1358
contract_name : str
1335
1359
omit_unstable_output : bool
1360
+ compilation_unit : CompilationUnit
1336
1361
1337
1362
def __init__ (self , foundry : Foundry , contract_name : str , omit_unstable_output : bool = False ):
1338
1363
KEVMNodePrinter .__init__ (self , foundry .kevm )
1339
1364
self .foundry = foundry
1340
1365
self .contract_name = contract_name
1341
1366
self .omit_unstable_output = omit_unstable_output
1367
+ self .compilation_unit = CompilationUnit .load_build_info (foundry ._root )
1342
1368
1343
1369
def print_node (self , kcfg : KCFG , node : KCFG .Node ) -> list [str ]:
1344
1370
ret_strs = super ().print_node (kcfg , node )
1345
1371
_pc = node .cterm .try_cell ('PC_CELL' )
1372
+ program_cell = node .cterm .try_cell ('PROGRAM_CELL' )
1373
+
1346
1374
if type (_pc ) is KToken and _pc .sort == INT :
1347
- srcmap_data = self .foundry .srcmap_data (self .contract_name , int (_pc .token ))
1348
- if not self .omit_unstable_output and srcmap_data is not None :
1349
- path , start , end = srcmap_data
1350
- ret_strs .append (f'src: { str (path )} :{ start } :{ end } ' )
1375
+ if type (program_cell ) is KToken :
1376
+ try :
1377
+ bytecode = ast .literal_eval (program_cell .token )
1378
+ instruction = self .compilation_unit .get_instruction (bytecode , int (_pc .token ))
1379
+ ast_node = instruction .node ()
1380
+ start_line , _ , end_line , _ = ast_node .source_range ()
1381
+ ret_strs .append (f'src: { str (Path (ast_node .source .name ))} :{ start_line } :{ end_line } ' )
1382
+ except Exception :
1383
+ pass
1351
1384
1352
1385
calldata_cell = node .cterm .try_cell ('CALLDATA_CELL' )
1353
- program_cell = node .cterm .try_cell ('PROGRAM_CELL' )
1354
1386
1355
1387
if type (program_cell ) is KToken :
1356
1388
selector_bytes = None
0 commit comments