@@ -61,6 +61,7 @@ def __init__(
61
61
description ,
62
62
operand = None ,
63
63
pc = 0 ,
64
+ offset = 0 ,
64
65
):
65
66
"""
66
67
This represents an EVM instruction.
@@ -75,6 +76,7 @@ def __init__(
75
76
:param description: textual description of the instruction
76
77
:param operand: optional immediate operand
77
78
:param pc: optional program counter of this instruction in the program
79
+ :param offset: optional offset of this instruction in the bytecode
78
80
79
81
Example use::
80
82
@@ -83,6 +85,7 @@ def __init__(
83
85
>>> print('\t description:', instruction.description)
84
86
>>> print('\t group:', instruction.group)
85
87
>>> print('\t pc:', instruction.pc)
88
+ >>> print('\t offset:', instruction.offset)
86
89
>>> print('\t size:', instruction.size)
87
90
>>> print('\t has_operand:', instruction.has_operand)
88
91
>>> print('\t operand_size:', instruction.operand_size)
@@ -110,6 +113,7 @@ def __init__(
110
113
self ._description = description
111
114
self ._operand = operand # Immediate operand if any
112
115
self ._pc = pc
116
+ self ._offset = offset
113
117
114
118
def __eq__ (self , other ):
115
119
"""Instructions are equal if all features match"""
@@ -122,11 +126,12 @@ def __eq__(self, other):
122
126
and self ._pushes == other ._pushes
123
127
and self ._fee == other ._fee
124
128
and self ._pc == other ._pc
129
+ and self ._offset == other ._offset
125
130
and self ._description == other ._description
126
131
)
127
132
128
133
def __repr__ (self ):
129
- output = "Instruction(0x{:x}, {}, {:d}, {:d}, {:d}, {:d}, {}, {}, {})" .format (
134
+ output = "Instruction(0x{:x}, {}, {:d}, {:d}, {:d}, {:d}, {}, {}, {}, {} )" .format (
130
135
self ._opcode ,
131
136
self ._name ,
132
137
self ._operand_size ,
@@ -136,6 +141,7 @@ def __repr__(self):
136
141
self ._description ,
137
142
self ._operand ,
138
143
self ._pc ,
144
+ self ._offset
139
145
)
140
146
return output
141
147
@@ -261,6 +267,15 @@ def pc(self, value):
261
267
"""Location in the program (optional)"""
262
268
self ._pc = value
263
269
270
+ @property
271
+ def offset (self ):
272
+ return self ._offset
273
+
274
+ @offset .setter
275
+ def offset (self , value ):
276
+ """Offset in the bytecode (optional)"""
277
+ self ._offset = value
278
+
264
279
@property
265
280
def group (self ):
266
281
"""Instruction classification as per the yellow paper"""
@@ -407,13 +422,15 @@ def is_arithmetic(self):
407
422
}
408
423
409
424
410
- def assemble_one (asmcode , pc = 0 , fork = DEFAULT_FORK ):
425
+ def assemble_one (asmcode , pc = 0 , offset = 0 , fork = DEFAULT_FORK ):
411
426
"""Assemble one EVM instruction from its textual representation.
412
427
413
428
:param asmcode: assembly code for one instruction
414
429
:type asmcode: str
415
430
:param pc: program counter of the instruction(optional)
416
431
:type pc: int
432
+ :param offset: offset of the instruction in the bytecode(optional)
433
+ :type offset: int
417
434
:param fork: fork name (optional)
418
435
:type fork: str
419
436
:return: An Instruction object
@@ -431,6 +448,8 @@ def assemble_one(asmcode, pc=0, fork=DEFAULT_FORK):
431
448
instr = instruction_table [asmcode [0 ].upper ()]
432
449
if pc :
433
450
instr .pc = pc
451
+ if offset :
452
+ instr .offset = offset
434
453
if instr .operand_size > 0 :
435
454
assert len (asmcode ) == 2
436
455
instr .operand = int (asmcode [1 ], 0 )
@@ -439,13 +458,15 @@ def assemble_one(asmcode, pc=0, fork=DEFAULT_FORK):
439
458
raise AssembleError ("Something wrong at pc {:d}" .format (pc ))
440
459
441
460
442
- def assemble_all (asmcode , pc = 0 , fork = DEFAULT_FORK ):
461
+ def assemble_all (asmcode , pc = 0 , offset = 0 , fork = DEFAULT_FORK ):
443
462
""" Assemble a sequence of textual representation of EVM instructions
444
463
445
464
:param asmcode: assembly code for any number of instructions
446
465
:type asmcode: str
447
466
:param pc: program counter of the first instruction(optional)
448
467
:type pc: int
468
+ :param offset: offset of the first instruction in the bytecode(optional)
469
+ :type offset: int
449
470
:param fork: fork name (optional)
450
471
:type fork: str
451
472
:return: An generator of Instruction objects
@@ -471,18 +492,21 @@ def assemble_all(asmcode, pc=0, fork=DEFAULT_FORK):
471
492
for line in asmcode :
472
493
if not line .strip ():
473
494
continue
474
- instr = assemble_one (line , pc = pc , fork = fork )
495
+ instr = assemble_one (line , pc = pc , offset = offset , fork = fork )
475
496
yield instr
476
497
pc += instr .size
498
+ offset += 1
477
499
478
500
479
- def disassemble_one (bytecode , pc = 0 , fork = DEFAULT_FORK ):
501
+ def disassemble_one (bytecode , pc = 0 , offset = 0 , fork = DEFAULT_FORK ):
480
502
"""Disassemble a single instruction from a bytecode
481
503
482
504
:param bytecode: the bytecode stream
483
505
:type bytecode: str | bytes | bytearray | iterator
484
506
:param pc: program counter of the instruction(optional)
485
507
:type pc: int
508
+ :param offset: offset of the instruction in the bytecode(optional)
509
+ :type offset: int
486
510
:param fork: fork name (optional)
487
511
:type fork: str
488
512
:return: an Instruction object
@@ -513,6 +537,7 @@ def disassemble_one(bytecode, pc=0, fork=DEFAULT_FORK):
513
537
opcode , "INVALID" , 0 , 0 , 0 , 0 , "Unspecified invalid instruction."
514
538
)
515
539
instruction .pc = pc
540
+ instruction .offset = offset
516
541
517
542
try :
518
543
if instruction .has_operand :
@@ -523,13 +548,15 @@ def disassemble_one(bytecode, pc=0, fork=DEFAULT_FORK):
523
548
return instruction
524
549
525
550
526
- def disassemble_all (bytecode , pc = 0 , fork = DEFAULT_FORK ):
551
+ def disassemble_all (bytecode , pc = 0 , offset = 0 , fork = DEFAULT_FORK ):
527
552
"""Disassemble all instructions in bytecode
528
553
529
554
:param bytecode: an evm bytecode (binary)
530
555
:type bytecode: str | bytes | bytearray | iterator
531
556
:param pc: program counter of the first instruction(optional)
532
557
:type pc: int
558
+ :param offset: offset of the first instruction in the bytecode(optional)
559
+ :type offset: int
533
560
:param fork: fork name (optional)
534
561
:type fork: str
535
562
:return: An generator of Instruction objects
@@ -561,20 +588,23 @@ def disassemble_all(bytecode, pc=0, fork=DEFAULT_FORK):
561
588
562
589
bytecode = iter (bytecode )
563
590
while True :
564
- instr = disassemble_one (bytecode , pc = pc , fork = fork )
591
+ instr = disassemble_one (bytecode , pc = pc , offset = offset , fork = fork )
565
592
if not instr :
566
593
return
567
594
pc += instr .size
595
+ offset += 1
568
596
yield instr
569
597
570
598
571
- def disassemble (bytecode , pc = 0 , fork = DEFAULT_FORK ):
599
+ def disassemble (bytecode , pc = 0 , offset = 0 , fork = DEFAULT_FORK ):
572
600
"""Disassemble an EVM bytecode
573
601
574
602
:param bytecode: binary representation of an evm bytecode
575
603
:type bytecode: str | bytes | bytearray
576
604
:param pc: program counter of the first instruction(optional)
577
605
:type pc: int
606
+ :param offset: offset of the first instruction in the bytecode(optional)
607
+ :type offset: int
578
608
:param fork: fork name (optional)
579
609
:type fork: str
580
610
:return: the text representation of the assembler code
@@ -590,16 +620,18 @@ def disassemble(bytecode, pc=0, fork=DEFAULT_FORK):
590
620
PUSH2 0x100
591
621
592
622
"""
593
- return "\n " .join (map (str , disassemble_all (bytecode , pc = pc , fork = fork )))
623
+ return "\n " .join (map (str , disassemble_all (bytecode , pc = pc , offset = offset , fork = fork )))
594
624
595
625
596
- def assemble (asmcode , pc = 0 , fork = DEFAULT_FORK ):
626
+ def assemble (asmcode , pc = 0 , offset = 0 , fork = DEFAULT_FORK ):
597
627
""" Assemble an EVM program
598
628
599
629
:param asmcode: an evm assembler program
600
630
:type asmcode: str
601
631
:param pc: program counter of the first instruction(optional)
602
632
:type pc: int
633
+ :param offset: offset of the first instruction in the bytecode(optional)
634
+ :type offset: int
603
635
:param fork: fork name (optional)
604
636
:type fork: str
605
637
:return: the hex representation of the bytecode
@@ -616,16 +648,18 @@ def assemble(asmcode, pc=0, fork=DEFAULT_FORK):
616
648
...
617
649
b"\x60 \x60 \x60 \x40 \x52 \x60 \x02 \x61 \x01 \x00 "
618
650
"""
619
- return b"" .join (x .bytes for x in assemble_all (asmcode , pc = pc , fork = fork ))
651
+ return b"" .join (x .bytes for x in assemble_all (asmcode , pc = pc , offset = offset , fork = fork ))
620
652
621
653
622
- def disassemble_hex (bytecode , pc = 0 , fork = DEFAULT_FORK ):
654
+ def disassemble_hex (bytecode , pc = 0 , offset = 0 , fork = DEFAULT_FORK ):
623
655
"""Disassemble an EVM bytecode
624
656
625
657
:param bytecode: canonical representation of an evm bytecode (hexadecimal)
626
658
:type bytecode: str
627
659
:param pc: program counter of the first instruction(optional)
628
660
:type pc: int
661
+ :param offset: offset of the first instruction in the bytecode(optional)
662
+ :type offset: int
629
663
:param fork: fork name (optional)
630
664
:type fork: str
631
665
:return: the text representation of the assembler code
@@ -645,16 +679,18 @@ def disassemble_hex(bytecode, pc=0, fork=DEFAULT_FORK):
645
679
if bytecode .startswith ("0x" ):
646
680
bytecode = bytecode [2 :]
647
681
bytecode = unhexlify (bytecode )
648
- return disassemble (bytecode , pc = pc , fork = fork )
682
+ return disassemble (bytecode , pc = pc , offset = offset , fork = fork )
649
683
650
684
651
- def assemble_hex (asmcode , pc = 0 , fork = DEFAULT_FORK ):
685
+ def assemble_hex (asmcode , pc = 0 , offset = 0 , fork = DEFAULT_FORK ):
652
686
""" Assemble an EVM program
653
687
654
688
:param asmcode: an evm assembler program
655
689
:type asmcode: str | iterator[Instruction]
656
690
:param pc: program counter of the first instruction(optional)
657
691
:type pc: int
692
+ :param offset: offset of the first instruction in the bytecode(optional)
693
+ :type offset: int
658
694
:param fork: fork name (optional)
659
695
:type fork: str
660
696
:return: the hex representation of the bytecode
@@ -673,7 +709,7 @@ def assemble_hex(asmcode, pc=0, fork=DEFAULT_FORK):
673
709
"""
674
710
if isinstance (asmcode , list ):
675
711
return "0x" + hexlify (b"" .join ([x .bytes for x in asmcode ])).decode ("ascii" )
676
- return "0x" + hexlify (assemble (asmcode , pc = pc , fork = fork )).decode ("ascii" )
712
+ return "0x" + hexlify (assemble (asmcode , pc = pc , offset = offset , fork = fork )).decode ("ascii" )
677
713
678
714
679
715
class InstructionTable :
0 commit comments