@@ -258,6 +258,7 @@ def show_code(co, *, file=None):
258258 'argval' ,
259259 'argrepr' ,
260260 'offset' ,
261+ 'start_offset' ,
261262 'starts_line' ,
262263 'is_jump_target' ,
263264 'positions'
@@ -271,6 +272,8 @@ def show_code(co, *, file=None):
271272_Instruction .argval .__doc__ = "Resolved arg value (if known), otherwise same as arg"
272273_Instruction .argrepr .__doc__ = "Human readable description of operation argument"
273274_Instruction .offset .__doc__ = "Start index of operation within bytecode sequence"
275+ _Instruction .start_offset .__doc__ = "Start index of operation within bytecode sequence including extended args if present. " \
276+ "Otherwise equal to Instruction.offset"
274277_Instruction .starts_line .__doc__ = "Line started by this opcode (if any), otherwise None"
275278_Instruction .is_jump_target .__doc__ = "True if other code jumps to here, otherwise False"
276279_Instruction .positions .__doc__ = "dis.Positions object holding the span of source code covered by this instruction"
@@ -281,6 +284,23 @@ def show_code(co, *, file=None):
281284_OPNAME_WIDTH = 20
282285_OPARG_WIDTH = 5
283286
287+ def _get_jump_target (op , arg , offset ):
288+ """Gets the bytecode offset of the jump target if this is a jump instruction,
289+ otherwise returns None
290+ """
291+ deop = _deoptop (op )
292+ caches = _inline_cache_entries [deop ]
293+ if deop in hasjrel :
294+ if _is_backward_jump (deop ):
295+ arg = - arg
296+ target = offset + 2 + arg * 2
297+ target += 2 * caches
298+ elif deop in hasjabs :
299+ target = arg * 2
300+ else :
301+ target = None
302+ return target
303+
284304class Instruction (_Instruction ):
285305 """Details for a bytecode operation
286306
@@ -291,12 +311,48 @@ class Instruction(_Instruction):
291311 argval - resolved arg value (if known), otherwise same as arg
292312 argrepr - human readable description of operation argument
293313 offset - start index of operation within bytecode sequence
314+ start_offset - start index of operation within bytecode sequence including extended args if present.
315+ Otherwise equal to Instruction.offset
294316 starts_line - line started by this opcode (if any), otherwise None
295317 is_jump_target - True if other code jumps to here, otherwise False
296318 positions - Optional dis.Positions object holding the span of source code
297319 covered by this instruction
298320 """
299321
322+ @property
323+ def oparg (self ):
324+ """Alias for Instruction.arg"""
325+ return self .arg
326+
327+ @property
328+ def baseopcode (self ):
329+ """numeric code for the base operation if operation is specialized.
330+ Otherwise equal to Instruction.opcode
331+ """
332+ return _deoptop (self .opcode )
333+
334+ @property
335+ def baseopname (self ):
336+ """human readable name for the base operation if operation is specialized.
337+ Otherwise equal to Instruction.opname
338+ """
339+ return opname [self .baseopcode ]
340+
341+ @property
342+ def cache_offset (self ):
343+ """start index of the cache entries following the operation"""
344+ return self .offset + 2
345+
346+ @property
347+ def end_offset (self ):
348+ """end index of the cache entries following the operation"""
349+ return self .cache_offset + _inline_cache_entries [self .opcode ]* 2
350+
351+ @property
352+ def jump_target (self ):
353+ """bytecode index of the jump target if this is a jump operation, otherwise None"""
354+ return _get_jump_target (self .opcode , self .arg , self .offset )
355+
300356 def _disassemble (self , lineno_width = 3 , mark_as_current = False , offset_width = 4 ):
301357 """Format instruction details for inclusion in disassembly output
302358
@@ -328,12 +384,23 @@ def _disassemble(self, lineno_width=3, mark_as_current=False, offset_width=4):
328384 fields .append (self .opname .ljust (_OPNAME_WIDTH ))
329385 # Column: Opcode argument
330386 if self .arg is not None :
331- fields .append (repr (self .arg ).rjust (_OPARG_WIDTH ))
387+ arg = repr (self .arg )
388+ # If opname is longer than _OPNAME_WIDTH, but the total length together with
389+ # oparg is less than _OPNAME_WIDTH + _OPARG_WIDTH (with at least one space in between),
390+ # we allow opname to overflow into the space reserved for oparg.
391+ # This results in fewer misaligned opargs in the disassembly output
392+ opname_excess = max (0 , len (self .opname ) - _OPNAME_WIDTH )
393+ if opname_excess + len (arg ) < _OPARG_WIDTH :
394+ fields .append (arg .rjust (_OPARG_WIDTH - opname_excess ))
395+ else :
396+ fields .append (arg .rjust (_OPARG_WIDTH ))
332397 # Column: Opcode argument details
333398 if self .argrepr :
334399 fields .append ('(' + self .argrepr + ')' )
335400 return ' ' .join (fields ).rstrip ()
336401
402+ def __str__ (self ):
403+ return self ._disassemble ()
337404
338405def get_instructions (x , * , first_line = None , show_caches = False , adaptive = False ):
339406 """Iterator for the opcodes in methods, functions or code
@@ -448,7 +515,7 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
448515 for i in range (start , end ):
449516 labels .add (target )
450517 starts_line = None
451- for offset , op , arg in _unpack_opargs (code ):
518+ for offset , start_offset , op , arg in _unpack_opargs (code ):
452519 if linestarts is not None :
453520 starts_line = linestarts .get (offset , None )
454521 if starts_line is not None :
@@ -509,7 +576,7 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
509576 _ , argrepr = _nb_ops [arg ]
510577 yield Instruction (_all_opname [op ], op ,
511578 arg , argval , argrepr ,
512- offset , starts_line , is_jump_target , positions )
579+ offset , start_offset , starts_line , is_jump_target , positions )
513580 caches = _inline_cache_entries [deop ]
514581 if not caches :
515582 continue
@@ -529,7 +596,7 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
529596 else :
530597 argrepr = ""
531598 yield Instruction (
532- "CACHE" , CACHE , 0 , None , argrepr , offset , None , False ,
599+ "CACHE" , CACHE , 0 , None , argrepr , offset , offset , None , False ,
533600 Positions (* next (co_positions , ()))
534601 )
535602
@@ -615,6 +682,7 @@ def _disassemble_str(source, **kwargs):
615682
616683def _unpack_opargs (code ):
617684 extended_arg = 0
685+ extended_args_offset = 0 # Number of EXTENDED_ARG instructions preceding the current instruction
618686 caches = 0
619687 for i in range (0 , len (code ), 2 ):
620688 # Skip inline CACHE entries:
@@ -635,7 +703,13 @@ def _unpack_opargs(code):
635703 else :
636704 arg = None
637705 extended_arg = 0
638- yield (i , op , arg )
706+ if deop == EXTENDED_ARG :
707+ extended_args_offset += 1
708+ yield (i , i , op , arg )
709+ else :
710+ start_offset = i - extended_args_offset * 2
711+ yield (i , start_offset , op , arg )
712+ extended_args_offset = 0
639713
640714def findlabels (code ):
641715 """Detect all offsets in a byte code which are jump targets.
@@ -644,18 +718,10 @@ def findlabels(code):
644718
645719 """
646720 labels = []
647- for offset , op , arg in _unpack_opargs (code ):
721+ for offset , _ , op , arg in _unpack_opargs (code ):
648722 if arg is not None :
649- deop = _deoptop (op )
650- caches = _inline_cache_entries [deop ]
651- if deop in hasjrel :
652- if _is_backward_jump (deop ):
653- arg = - arg
654- label = offset + 2 + arg * 2
655- label += 2 * caches
656- elif deop in hasjabs :
657- label = arg * 2
658- else :
723+ label = _get_jump_target (op , arg , offset )
724+ if label is None :
659725 continue
660726 if label not in labels :
661727 labels .append (label )
@@ -684,7 +750,7 @@ def _find_imports(co):
684750
685751 consts = co .co_consts
686752 names = co .co_names
687- opargs = [(op , arg ) for _ , op , arg in _unpack_opargs (co .co_code )
753+ opargs = [(op , arg ) for _ , _ , op , arg in _unpack_opargs (co .co_code )
688754 if op != EXTENDED_ARG ]
689755 for i , (op , oparg ) in enumerate (opargs ):
690756 if op == IMPORT_NAME and i >= 2 :
@@ -706,7 +772,7 @@ def _find_store_names(co):
706772 }
707773
708774 names = co .co_names
709- for _ , op , arg in _unpack_opargs (co .co_code ):
775+ for _ , _ , op , arg in _unpack_opargs (co .co_code ):
710776 if op in STORE_OPS :
711777 yield names [arg ]
712778
0 commit comments