Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
add option to include offset and labels on same line as instruction
  • Loading branch information
iritkatriel committed Nov 21, 2023
commit 390c7a238cc995e767c9ee7262d3e5862072bbb2
73 changes: 46 additions & 27 deletions Lib/dis.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ def _try_compile(source, name):
pass
return compile(source, name, 'exec')

def dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False):
def dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False,
show_offsets=False):
"""Disassemble classes, methods, functions, and other compiled objects.

With no argument, disassemble the last traceback.
Expand All @@ -82,7 +83,8 @@ def dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False):
in a special attribute.
"""
if x is None:
distb(file=file, show_caches=show_caches, adaptive=adaptive)
distb(file=file, show_caches=show_caches, adaptive=adaptive,
show_offsets=show_offsets)
return
# Extract functions from methods.
if hasattr(x, '__func__'):
Expand All @@ -103,21 +105,21 @@ def dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False):
if isinstance(x1, _have_code):
print("Disassembly of %s:" % name, file=file)
try:
dis(x1, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive)
dis(x1, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets)
except TypeError as msg:
print("Sorry:", msg, file=file)
print(file=file)
elif hasattr(x, 'co_code'): # Code object
_disassemble_recursive(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive)
_disassemble_recursive(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets)
elif isinstance(x, (bytes, bytearray)): # Raw bytecode
_disassemble_bytes(x, file=file, show_caches=show_caches)
_disassemble_bytes(x, file=file, show_caches=show_caches, show_offsets=show_offsets)
elif isinstance(x, str): # Source code
_disassemble_str(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive)
_disassemble_str(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets)
else:
raise TypeError("don't know how to disassemble %s objects" %
type(x).__name__)

def distb(tb=None, *, file=None, show_caches=False, adaptive=False):
def distb(tb=None, *, file=None, show_caches=False, adaptive=False, show_offsets=False):
"""Disassemble a traceback (default: last traceback)."""
if tb is None:
try:
Expand All @@ -128,7 +130,7 @@ def distb(tb=None, *, file=None, show_caches=False, adaptive=False):
except AttributeError:
raise RuntimeError("no last traceback to disassemble") from None
while tb.tb_next: tb = tb.tb_next
disassemble(tb.tb_frame.f_code, tb.tb_lasti, file=file, show_caches=show_caches, adaptive=adaptive)
disassemble(tb.tb_frame.f_code, tb.tb_lasti, file=file, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets)

# The inspect module interrogates this dictionary to build its
# list of CO_* constants. It is also used by pretty_flags to
Expand Down Expand Up @@ -464,16 +466,22 @@ def is_jump_target(self):
"""True if other code jumps to here, otherwise False"""
return self.label is not None

def _disassemble(self, lineno_width=3, mark_as_current=False, offset_width=4):
def _disassemble(self, lineno_width=3, mark_as_current=False, offset_width=0,
label_width=4):
"""Format instruction details for inclusion in disassembly output.

*lineno_width* sets the width of the line number field (0 omits it)
*mark_as_current* inserts a '-->' marker arrow as part of the line
*offset_width* sets the width of the instruction offset field
"""
fields = []
# Column: Labels (on separate lines)
fields.append(" ")
#
# Column: Label
if self.label is not None:
lbl = f"L{self.label}:"
fields.append(f"{lbl:<{label_width}}")
else:
fields.append(' ' * label_width)
# Column: Source code line number
if lineno_width:
if self.starts_line:
Expand All @@ -487,6 +495,9 @@ def _disassemble(self, lineno_width=3, mark_as_current=False, offset_width=4):
fields.append('-->')
else:
fields.append(' ')
# Column: Instruction offset from start of code sequence
if offset_width > 0:
fields.append(f"{repr(self.offset):>{offset_width}} ")
# Column: Opcode name
fields.append(self.opname.ljust(_OPNAME_WIDTH))
# Column: Opcode argument
Expand Down Expand Up @@ -680,11 +691,12 @@ def make_labels_map(original_code, exception_entries):
else:
argrepr = ""
yield Instruction(
"CACHE", CACHE, 0, None, argrepr, offset, offset, False, None, False,
"CACHE", CACHE, 0, None, argrepr, offset, offset, False, None, None,
Positions(*next(co_positions, ()))
)

def disassemble(co, lasti=-1, *, file=None, show_caches=False, adaptive=False):
def disassemble(co, lasti=-1, *, file=None, show_caches=False, adaptive=False,
show_offsets=False):
"""Disassemble a code object."""
linestarts = dict(findlinestarts(co))
exception_entries = _parse_exception_table(co)
Expand All @@ -693,10 +705,10 @@ def disassemble(co, lasti=-1, *, file=None, show_caches=False, adaptive=False):
co.co_names, co.co_consts, linestarts, file=file,
exception_entries=exception_entries,
co_positions=co.co_positions(), show_caches=show_caches,
original_code=co.co_code)
original_code=co.co_code, show_offsets=show_offsets)

def _disassemble_recursive(co, *, file=None, depth=None, show_caches=False, adaptive=False):
disassemble(co, file=file, show_caches=show_caches, adaptive=adaptive)
def _disassemble_recursive(co, *, file=None, depth=None, show_caches=False, adaptive=False, show_offsets=False):
disassemble(co, file=file, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets)
if depth is None or depth > 0:
if depth is not None:
depth = depth - 1
Expand All @@ -705,13 +717,15 @@ def _disassemble_recursive(co, *, file=None, depth=None, show_caches=False, adap
print(file=file)
print("Disassembly of %r:" % (x,), file=file)
_disassemble_recursive(
x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive
x, file=file, depth=depth, show_caches=show_caches,
adaptive=adaptive, show_offsets=show_offsets
)

def _disassemble_bytes(code, lasti=-1, varname_from_oparg=None,
names=None, co_consts=None, linestarts=None,
*, file=None, line_offset=0, exception_entries=(),
co_positions=None, show_caches=False, original_code=None):
co_positions=None, show_caches=False, original_code=None,
show_offsets=False):
# Omit the line number column entirely if we have no line number info
if bool(linestarts):
linestarts_ints = [line for line in linestarts.values() if line is not None]
Expand All @@ -730,11 +744,14 @@ def _disassemble_bytes(code, lasti=-1, varname_from_oparg=None,
lineno_width = len(str(None))
else:
lineno_width = 0
maxoffset = len(code) - 2
if maxoffset >= 10000:
offset_width = len(str(maxoffset))
if show_offsets:
maxoffset = len(code) - 2
if maxoffset >= 10000:
offset_width = len(str(maxoffset))
else:
offset_width = 4
else:
offset_width = 4
offset_width = 0

for instr in _get_instructions_bytes(code, varname_from_oparg, names,
co_consts, linestarts,
Expand All @@ -754,8 +771,6 @@ def _disassemble_bytes(code, lasti=-1, varname_from_oparg=None,
# Each CACHE takes 2 bytes
is_current_instr = instr.offset <= lasti \
<= instr.offset + 2 * _get_cache_size(_all_opname[_deoptop(instr.opcode)])
if instr.label is not None:
print(f"L{instr.label}:", file=file)
print(instr._disassemble(lineno_width, is_current_instr, offset_width),
file=file)
if exception_entries:
Expand Down Expand Up @@ -886,7 +901,7 @@ class Bytecode:

Iterating over this yields the bytecode operations as Instruction instances.
"""
def __init__(self, x, *, first_line=None, current_offset=None, show_caches=False, adaptive=False):
def __init__(self, x, *, first_line=None, current_offset=None, show_caches=False, adaptive=False, show_offsets=False):
self.codeobj = co = _get_code_object(x)
if first_line is None:
self.first_line = co.co_firstlineno
Expand All @@ -900,6 +915,7 @@ def __init__(self, x, *, first_line=None, current_offset=None, show_caches=False
self.exception_entries = _parse_exception_table(co)
self.show_caches = show_caches
self.adaptive = adaptive
self.show_offsets = show_offsets

def __iter__(self):
co = self.codeobj
Expand Down Expand Up @@ -948,7 +964,8 @@ def dis(self):
exception_entries=self.exception_entries,
co_positions=co.co_positions(),
show_caches=self.show_caches,
original_code=co.co_code)
original_code=co.co_code,
show_offsets=self.show_offsets)
return output.getvalue()


Expand All @@ -958,12 +975,14 @@ def main():
parser = argparse.ArgumentParser()
parser.add_argument('-C', '--show-caches', action='store_true',
help='show inline caches')
parser.add_argument('-O', '--show-offsets', action='store_true',
help='show instruction offsets')
parser.add_argument('infile', type=argparse.FileType('rb'), nargs='?', default='-')
args = parser.parse_args()
with args.infile as infile:
source = infile.read()
code = compile(source, args.infile.name, "exec")
dis(code, show_caches=args.show_caches)
dis(code, show_caches=args.show_caches, show_offsets=args.show_offsets)

if __name__ == "__main__":
main()
Loading