7
7
8
8
from opcode import *
9
9
from opcode import __all__ as _opcodes_all
10
- from opcode import _nb_ops
10
+ from opcode import _nb_ops , _inline_cache_entries , _specializations , _specialized_instructions
11
11
12
12
__all__ = ["code_info" , "dis" , "disassemble" , "distb" , "disco" ,
13
13
"findlinestarts" , "findlabels" , "show_code" ,
34
34
35
35
CACHE = opmap ["CACHE" ]
36
36
37
+ _all_opname = list (opname )
38
+ _all_opmap = dict (opmap )
39
+ _empty_slot = [slot for slot , name in enumerate (_all_opname ) if name .startswith ("<" )]
40
+ for spec_op , specialized in zip (_empty_slot , _specialized_instructions ):
41
+ # fill opname and opmap
42
+ _all_opname [spec_op ] = specialized
43
+ _all_opmap [specialized ] = spec_op
44
+
45
+ deoptmap = {
46
+ specialized : base for base , family in _specializations .items () for specialized in family
47
+ }
48
+
37
49
def _try_compile (source , name ):
38
50
"""Attempts to compile the given source, first as an expression and
39
51
then as a statement if the first approach fails.
@@ -47,7 +59,7 @@ def _try_compile(source, name):
47
59
c = compile (source , name , 'exec' )
48
60
return c
49
61
50
- def dis (x = None , * , file = None , depth = None , show_caches = False ):
62
+ def dis (x = None , * , file = None , depth = None , show_caches = False , adaptive = False ):
51
63
"""Disassemble classes, methods, functions, and other compiled objects.
52
64
53
65
With no argument, disassemble the last traceback.
@@ -57,7 +69,7 @@ def dis(x=None, *, file=None, depth=None, show_caches=False):
57
69
in a special attribute.
58
70
"""
59
71
if x is None :
60
- distb (file = file , show_caches = show_caches )
72
+ distb (file = file , show_caches = show_caches , adaptive = adaptive )
61
73
return
62
74
# Extract functions from methods.
63
75
if hasattr (x , '__func__' ):
@@ -78,29 +90,29 @@ def dis(x=None, *, file=None, depth=None, show_caches=False):
78
90
if isinstance (x1 , _have_code ):
79
91
print ("Disassembly of %s:" % name , file = file )
80
92
try :
81
- dis (x1 , file = file , depth = depth , show_caches = show_caches )
93
+ dis (x1 , file = file , depth = depth , show_caches = show_caches , adaptive = adaptive )
82
94
except TypeError as msg :
83
95
print ("Sorry:" , msg , file = file )
84
96
print (file = file )
85
97
elif hasattr (x , 'co_code' ): # Code object
86
- _disassemble_recursive (x , file = file , depth = depth , show_caches = show_caches )
98
+ _disassemble_recursive (x , file = file , depth = depth , show_caches = show_caches , adaptive = adaptive )
87
99
elif isinstance (x , (bytes , bytearray )): # Raw bytecode
88
100
_disassemble_bytes (x , file = file , show_caches = show_caches )
89
101
elif isinstance (x , str ): # Source code
90
- _disassemble_str (x , file = file , depth = depth , show_caches = show_caches )
102
+ _disassemble_str (x , file = file , depth = depth , show_caches = show_caches , adaptive = adaptive )
91
103
else :
92
104
raise TypeError ("don't know how to disassemble %s objects" %
93
105
type (x ).__name__ )
94
106
95
- def distb (tb = None , * , file = None , show_caches = False ):
107
+ def distb (tb = None , * , file = None , show_caches = False , adaptive = False ):
96
108
"""Disassemble a traceback (default: last traceback)."""
97
109
if tb is None :
98
110
try :
99
111
tb = sys .last_traceback
100
112
except AttributeError :
101
113
raise RuntimeError ("no last traceback to disassemble" ) from None
102
114
while tb .tb_next : tb = tb .tb_next
103
- disassemble (tb .tb_frame .f_code , tb .tb_lasti , file = file , show_caches = show_caches )
115
+ disassemble (tb .tb_frame .f_code , tb .tb_lasti , file = file , show_caches = show_caches , adaptive = adaptive )
104
116
105
117
# The inspect module interrogates this dictionary to build its
106
118
# list of CO_* constants. It is also used by pretty_flags to
@@ -162,6 +174,13 @@ def _get_code_object(x):
162
174
raise TypeError ("don't know how to disassemble %s objects" %
163
175
type (x ).__name__ )
164
176
177
+ def _deoptop (op ):
178
+ name = _all_opname [op ]
179
+ return _all_opmap [deoptmap [name ]] if name in deoptmap else op
180
+
181
+ def _get_code_array (co , adaptive ):
182
+ return co ._co_code_adaptive if adaptive else co .co_code
183
+
165
184
def code_info (x ):
166
185
"""Formatted details of methods, functions, or code."""
167
186
return _format_code_info (_get_code_object (x ))
@@ -302,7 +321,7 @@ def _disassemble(self, lineno_width=3, mark_as_current=False, offset_width=4):
302
321
return ' ' .join (fields ).rstrip ()
303
322
304
323
305
- def get_instructions (x , * , first_line = None , show_caches = False ):
324
+ def get_instructions (x , * , first_line = None , show_caches = False , adaptive = False ):
306
325
"""Iterator for the opcodes in methods, functions or code
307
326
308
327
Generates a series of Instruction named tuples giving the details of
@@ -319,7 +338,7 @@ def get_instructions(x, *, first_line=None, show_caches=False):
319
338
line_offset = first_line - co .co_firstlineno
320
339
else :
321
340
line_offset = 0
322
- return _get_instructions_bytes (co . co_code ,
341
+ return _get_instructions_bytes (_get_code_array ( co , adaptive ) ,
323
342
co ._varname_from_oparg ,
324
343
co .co_names , co .co_consts ,
325
344
linestarts , line_offset ,
@@ -415,8 +434,13 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
415
434
for i in range (start , end ):
416
435
labels .add (target )
417
436
starts_line = None
437
+ cache_counter = 0
418
438
for offset , op , arg in _unpack_opargs (code ):
419
- if not show_caches and op == CACHE :
439
+ if cache_counter > 0 :
440
+ if show_caches :
441
+ yield Instruction ("CACHE" , 0 , None , None , '' ,
442
+ offset , None , False , None )
443
+ cache_counter -= 1
420
444
continue
421
445
if linestarts is not None :
422
446
starts_line = linestarts .get (offset , None )
@@ -426,61 +450,63 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
426
450
argval = None
427
451
argrepr = ''
428
452
positions = Positions (* next (co_positions , ()))
453
+ deop = _deoptop (op )
454
+ cache_counter = _inline_cache_entries [deop ]
429
455
if arg is not None :
430
456
# Set argval to the dereferenced value of the argument when
431
457
# available, and argrepr to the string representation of argval.
432
458
# _disassemble_bytes needs the string repr of the
433
459
# raw name index for LOAD_GLOBAL, LOAD_CONST, etc.
434
460
argval = arg
435
- if op in hasconst :
436
- argval , argrepr = _get_const_info (op , arg , co_consts )
437
- elif op in hasname :
438
- if op == LOAD_GLOBAL :
461
+ if deop in hasconst :
462
+ argval , argrepr = _get_const_info (deop , arg , co_consts )
463
+ elif deop in hasname :
464
+ if deop == LOAD_GLOBAL :
439
465
argval , argrepr = _get_name_info (arg // 2 , get_name )
440
466
if (arg & 1 ) and argrepr :
441
467
argrepr = "NULL + " + argrepr
442
468
else :
443
469
argval , argrepr = _get_name_info (arg , get_name )
444
- elif op in hasjabs :
470
+ elif deop in hasjabs :
445
471
argval = arg * 2
446
472
argrepr = "to " + repr (argval )
447
- elif op in hasjrel :
448
- signed_arg = - arg if _is_backward_jump (op ) else arg
473
+ elif deop in hasjrel :
474
+ signed_arg = - arg if _is_backward_jump (deop ) else arg
449
475
argval = offset + 2 + signed_arg * 2
450
476
argrepr = "to " + repr (argval )
451
- elif op in haslocal or op in hasfree :
477
+ elif deop in haslocal or deop in hasfree :
452
478
argval , argrepr = _get_name_info (arg , varname_from_oparg )
453
- elif op in hascompare :
479
+ elif deop in hascompare :
454
480
argval = cmp_op [arg ]
455
481
argrepr = argval
456
- elif op == FORMAT_VALUE :
482
+ elif deop == FORMAT_VALUE :
457
483
argval , argrepr = FORMAT_VALUE_CONVERTERS [arg & 0x3 ]
458
484
argval = (argval , bool (arg & 0x4 ))
459
485
if argval [1 ]:
460
486
if argrepr :
461
487
argrepr += ', '
462
488
argrepr += 'with format'
463
- elif op == MAKE_FUNCTION :
489
+ elif deop == MAKE_FUNCTION :
464
490
argrepr = ', ' .join (s for i , s in enumerate (MAKE_FUNCTION_FLAGS )
465
491
if arg & (1 << i ))
466
- elif op == BINARY_OP :
492
+ elif deop == BINARY_OP :
467
493
_ , argrepr = _nb_ops [arg ]
468
- yield Instruction (opname [op ], op ,
494
+ yield Instruction (_all_opname [op ], op ,
469
495
arg , argval , argrepr ,
470
496
offset , starts_line , is_jump_target , positions )
471
497
472
- def disassemble (co , lasti = - 1 , * , file = None , show_caches = False ):
498
+ def disassemble (co , lasti = - 1 , * , file = None , show_caches = False , adaptive = False ):
473
499
"""Disassemble a code object."""
474
500
linestarts = dict (findlinestarts (co ))
475
501
exception_entries = parse_exception_table (co )
476
- _disassemble_bytes (co . co_code , lasti ,
477
- co ._varname_from_oparg ,
502
+ _disassemble_bytes (_get_code_array ( co , adaptive ) ,
503
+ lasti , co ._varname_from_oparg ,
478
504
co .co_names , co .co_consts , linestarts , file = file ,
479
505
exception_entries = exception_entries ,
480
506
co_positions = co .co_positions (), show_caches = show_caches )
481
507
482
- def _disassemble_recursive (co , * , file = None , depth = None , show_caches = False ):
483
- disassemble (co , file = file , show_caches = show_caches )
508
+ def _disassemble_recursive (co , * , file = None , depth = None , show_caches = False , adaptive = False ):
509
+ disassemble (co , file = file , show_caches = show_caches , adaptive = adaptive )
484
510
if depth is None or depth > 0 :
485
511
if depth is not None :
486
512
depth = depth - 1
@@ -489,7 +515,7 @@ def _disassemble_recursive(co, *, file=None, depth=None, show_caches=False):
489
515
print (file = file )
490
516
print ("Disassembly of %r:" % (x ,), file = file )
491
517
_disassemble_recursive (
492
- x , file = file , depth = depth , show_caches = show_caches
518
+ x , file = file , depth = depth , show_caches = show_caches , adaptive = adaptive
493
519
)
494
520
495
521
def _disassemble_bytes (code , lasti = - 1 , varname_from_oparg = None ,
@@ -548,7 +574,7 @@ def _unpack_opargs(code):
548
574
extended_arg = 0
549
575
for i in range (0 , len (code ), 2 ):
550
576
op = code [i ]
551
- if op >= HAVE_ARGUMENT :
577
+ if _deoptop ( op ) >= HAVE_ARGUMENT :
552
578
arg = code [i + 1 ] | extended_arg
553
579
extended_arg = (arg << 8 ) if op == EXTENDED_ARG else 0
554
580
# The oparg is stored as a signed integer
@@ -641,7 +667,7 @@ class Bytecode:
641
667
642
668
Iterating over this yields the bytecode operations as Instruction instances.
643
669
"""
644
- def __init__ (self , x , * , first_line = None , current_offset = None , show_caches = False ):
670
+ def __init__ (self , x , * , first_line = None , current_offset = None , show_caches = False , adaptive = False ):
645
671
self .codeobj = co = _get_code_object (x )
646
672
if first_line is None :
647
673
self .first_line = co .co_firstlineno
@@ -654,10 +680,11 @@ def __init__(self, x, *, first_line=None, current_offset=None, show_caches=False
654
680
self .current_offset = current_offset
655
681
self .exception_entries = parse_exception_table (co )
656
682
self .show_caches = show_caches
683
+ self .adaptive = adaptive
657
684
658
685
def __iter__ (self ):
659
686
co = self .codeobj
660
- return _get_instructions_bytes (co . co_code ,
687
+ return _get_instructions_bytes (_get_code_array ( co , self . adaptive ) ,
661
688
co ._varname_from_oparg ,
662
689
co .co_names , co .co_consts ,
663
690
self ._linestarts ,
@@ -671,12 +698,12 @@ def __repr__(self):
671
698
self ._original_object )
672
699
673
700
@classmethod
674
- def from_traceback (cls , tb , * , show_caches = False ):
701
+ def from_traceback (cls , tb , * , show_caches = False , adaptive = False ):
675
702
""" Construct a Bytecode from the given traceback """
676
703
while tb .tb_next :
677
704
tb = tb .tb_next
678
705
return cls (
679
- tb .tb_frame .f_code , current_offset = tb .tb_lasti , show_caches = show_caches
706
+ tb .tb_frame .f_code , current_offset = tb .tb_lasti , show_caches = show_caches , adaptive = adaptive
680
707
)
681
708
682
709
def info (self ):
@@ -691,7 +718,7 @@ def dis(self):
691
718
else :
692
719
offset = - 1
693
720
with io .StringIO () as output :
694
- _disassemble_bytes (co . co_code ,
721
+ _disassemble_bytes (_get_code_array ( co , self . adaptive ) ,
695
722
varname_from_oparg = co ._varname_from_oparg ,
696
723
names = co .co_names , co_consts = co .co_consts ,
697
724
linestarts = self ._linestarts ,
0 commit comments