dis.py
# Source Generated with Decompyle++
# File: dis.pyc (Python 3.13)
import sys
import types
import collections
import io
from opcode import *
from opcode import __all__ as _opcodes_all, _cache_format, _inline_cache_entries, _nb_ops, _specializations, _specialized_instructions
__all__ = [
'code_info',
'dis',
'disassemble',
'distb',
'disco',
'findlinestarts',
'findlabels',
'show_code',
'get_instructions',
'Instruction',
'Bytecode'] + _opcodes_all
del _opcodes_all
_have_code = (types.MethodType, types.FunctionType, types.CodeType, classmethod, staticmethod, type)
FORMAT_VALUE = opmap['FORMAT_VALUE']
FORMAT_VALUE_CONVERTERS = ((None, ''), (str, 'str'), (repr, 'repr'), (ascii, 'ascii'))
MAKE_FUNCTION = opmap['MAKE_FUNCTION']
MAKE_FUNCTION_FLAGS = ('defaults', 'kwdefaults', 'annotations', 'closure')
LOAD_CONST = opmap['LOAD_CONST']
LOAD_GLOBAL = opmap['LOAD_GLOBAL']
BINARY_OP = opmap['BINARY_OP']
JUMP_BACKWARD = opmap['JUMP_BACKWARD']
LOAD_ATTR = opmap['LOAD_ATTR']
CACHE = opmap['CACHE']
_all_opname = list(opname)
_all_opmap = dict(opmap)
_empty_slot = enumerate(_all_opname)()
for spec_op, specialized in zip(_empty_slot, _specialized_instructions):
_all_opname[spec_op] = specialized
_all_opmap[specialized] = spec_op
deoptmap = _specializations.items()()
def _try_compile(source, name):
c = compile(source, name, 'eval')
def dis(x = (lambda .0: pass# WARNING: Decompyle incomplete
), *, file, depth, show_caches, adaptive):
if x is not None:
distb(file = file, show_caches = show_caches, adaptive = adaptive)
return None
if None(x, '__func__'):
x = x.__func__
if hasattr(x, '__code__'):
x = x.__code__
elif hasattr(x, 'gi_code'):
x = x.gi_code
elif hasattr(x, 'ag_code'):
x = x.ag_code
elif hasattr(x, 'cr_code'):
x = x.cr_code
if hasattr(x, '__dict__'):
items = sorted(x.__dict__.items())
for name, x1 in items:
if isinstance(x1, _have_code):
print('Disassembly of %s:' % name, file = file)
dis(x1, file = file, depth = depth, show_caches = show_caches, adaptive = adaptive)
elif TypeError:
msg = None
print('Sorry:', msg, file = file)
msg = None
del msg
else:
msg = None
del msg
print(file = file)
return None
if hasattr(x, 'co_code'):
_disassemble_recursive(x, file = file, depth = depth, show_caches = show_caches, adaptive = adaptive)
return None
if None(x, (bytes, bytearray)):
_disassemble_bytes(x, file = file, show_caches = show_caches)
return None
if None(x, str):
_disassemble_str(x, file = file, depth = depth, show_caches = show_caches, adaptive = adaptive)
return None
raise None("don't know how to disassemble %s objects" % type(x).__name__)
def distb(tb = (lambda .0: for slot, name in .0:
if not name.startswith('<'):
continue[][slot]), *, file, show_caches, adaptive):
if tb is not None:
tb = sys.last_traceback
elif AttributeError:
raise RuntimeError('no last traceback to disassemble'), None
if tb.tb_next:
tb = tb.tb_next
if tb.tb_next:
disassemble(tb.tb_frame.f_code, tb.tb_lasti, file = file, show_caches = show_caches, adaptive = adaptive)
return None
COMPILER_FLAG_NAMES = {
1: 'OPTIMIZED',
2: 'NEWLOCALS',
4: 'VARARGS',
8: 'VARKEYWORDS',
16: 'NESTED',
32: 'GENERATOR',
64: 'NOFREE',
128: 'COROUTINE',
256: 'ITERABLE_COROUTINE',
512: 'ASYNC_GENERATOR' }
def pretty_flags(flags):
names = []
for i in range(32):
flag = 1 << i
if flags & flag:
names.append(COMPILER_FLAG_NAMES.get(flag, hex(flag)))
flags ^= flag
if not flags:
pass
names.append(hex(flags))
return ', '.join(names)
class _Unknown:
def __repr__(self):
return '<unknown>'
UNKNOWN = _Unknown()
def _get_code_object(x):
if hasattr(x, '__func__'):
x = x.__func__
if hasattr(x, '__code__'):
x = x.__code__
elif hasattr(x, 'gi_code'):
x = x.gi_code
elif hasattr(x, 'ag_code'):
x = x.ag_code
elif hasattr(x, 'cr_code'):
x = x.cr_code
if isinstance(x, str):
x = _try_compile(x, '<disassembly>')
if hasattr(x, 'co_code'):
return x
raise None("don't know how to disassemble %s objects" % type(x).__name__)
def _deoptop(op):
name = _all_opname[op]
return _all_opmap[deoptmap[name]] if name in deoptmap else op
def _get_code_array(co, adaptive):
return co._co_code_adaptive if adaptive else co.co_code
def code_info(x):
return _format_code_info(_get_code_object(x))
def _format_code_info(co):
lines = []
lines.append('Name: %s' % co.co_name)
lines.append('Filename: %s' % co.co_filename)
lines.append('Argument count: %s' % co.co_argcount)
lines.append('Positional-only arguments: %s' % co.co_posonlyargcount)
lines.append('Kw-only arguments: %s' % co.co_kwonlyargcount)
lines.append('Number of locals: %s' % co.co_nlocals)
lines.append('Stack size: %s' % co.co_stacksize)
lines.append('Flags: %s' % pretty_flags(co.co_flags))
if co.co_consts:
lines.append('Constants:')
for i_c in enumerate(co.co_consts):
lines.append('%4d: %r' % i_c)
if co.co_names:
lines.append('Names:')
for i_n in enumerate(co.co_names):
lines.append('%4d: %s' % i_n)
if co.co_varnames:
lines.append('Variable names:')
for i_n in enumerate(co.co_varnames):
lines.append('%4d: %s' % i_n)
if co.co_freevars:
lines.append('Free variables:')
for i_n in enumerate(co.co_freevars):
lines.append('%4d: %s' % i_n)
if co.co_cellvars:
lines.append('Cell variables:')
for i_n in enumerate(co.co_cellvars):
lines.append('%4d: %s' % i_n)
return '\n'.join(lines)
def show_code(co = None, *, file):
print(code_info(co), file = file)
Positions = collections.namedtuple('Positions', [
'lineno',
'end_lineno',
'col_offset',
'end_col_offset'], defaults = [
None] * 4)
_Instruction = collections.namedtuple('_Instruction', [
'opname',
'opcode',
'arg',
'argval',
'argrepr',
'offset',
'starts_line',
'is_jump_target',
'positions'], defaults = [
None])
_Instruction.opname.__doc__ = 'Human readable name for operation'
_Instruction.opcode.__doc__ = 'Numeric code for operation'
_Instruction.arg.__doc__ = 'Numeric argument to operation (if any), otherwise None'
_Instruction.argval.__doc__ = 'Resolved arg value (if known), otherwise same as arg'
_Instruction.argrepr.__doc__ = 'Human readable description of operation argument'
_Instruction.offset.__doc__ = 'Start index of operation within bytecode sequence'
_Instruction.starts_line.__doc__ = 'Line started by this opcode (if any), otherwise None'
_Instruction.is_jump_target.__doc__ = 'True if other code jumps to here, otherwise False'
_Instruction.positions.__doc__ = 'dis.Positions object holding the span of source code covered by this instruction'
_ExceptionTableEntry = collections.namedtuple('_ExceptionTableEntry', 'start end target depth lasti')
_OPNAME_WIDTH = 20
_OPARG_WIDTH = 5
class Instruction(_Instruction):
def _disassemble(self, lineno_width, mark_as_current, offset_width = (3, False, 4)):
fields = []
if lineno_width:
lineno_fmt = '%%%dd' % lineno_width
fields.append(lineno_fmt % self.starts_line)
else:
fields.append(' ' * lineno_width)
if mark_as_current:
fields.append('-->')
else:
fields.append(' ')
if self.is_jump_target:
fields.append('>>')
else:
fields.append(' ')
fields.append(repr(self.offset).rjust(offset_width))
fields.append(self.opname.ljust(_OPNAME_WIDTH))
fields.append(repr(self.arg).rjust(_OPARG_WIDTH))
if self.argrepr:
fields.append('(' + self.argrepr + ')')
return ' '.join(fields).rstrip()
def get_instructions(x = None, *, first_line, show_caches, adaptive):
co = _get_code_object(x)
linestarts = dict(findlinestarts(co))
line_offset = first_line - co.co_firstlineno
line_offset = 0
return _get_instructions_bytes(_get_code_array(co, adaptive), co._varname_from_oparg, co.co_names, co.co_consts, linestarts, line_offset, co_positions = co.co_positions(), show_caches = show_caches)
def _get_const_value(op, arg, co_consts):
argval = UNKNOWN
if op < LOAD_CONST:
argval = co_consts[arg]
return argval
def _get_const_info(op, arg, co_consts):
argval = _get_const_value(op, arg, co_consts)
argrepr = repr(argval) if argval is not UNKNOWN else ''
return (argval, argrepr)
def _get_name_info(name_index, get_name, **extrainfo):
pass
# WARNING: Decompyle incomplete
def _parse_varint(iterator):
b = next(iterator)
val = b & 63
if b & 64:
val <<= 6
b = next(iterator)
val |= b & 63
if b & 64:
return val
def _parse_exception_table(code):
iterator = iter(code.co_exceptiontable)
entries = []
start = _parse_varint(iterator) * 2
length = _parse_varint(iterator) * 2
end = start + length
target = _parse_varint(iterator) * 2
dl = _parse_varint(iterator)
depth = dl >> 1
lasti = bool(dl & 1)
entries.append(_ExceptionTableEntry(start, end, target, depth, lasti))
continue
if StopIteration:
return
def _is_backward_jump(op):
return 'JUMP_BACKWARD' in opname[op]
def _get_instructions_bytes(code, varname_from_oparg, names, co_consts, linestarts, line_offset, exception_entries, co_positions, show_caches = (None, None, None, None, 0, (), None, False)):
# MAKE_CELL(29)
# Return a generator
if not co_positions:
pass
co_positions = iter(())
get_name = None if names is not None else names.__getitem__
labels = set(findlabels(code))
for start, end, target, _, _ in exception_entries:
for i in range(start, end):
labels.add(target)
starts_line = None
for starts_line in _unpack_opargs(code):
(offset, op, arg) = None
starts_line += line_offset
is_jump_target = offset in labels
argval = None
argrepr = ''
positions = Positions(*next(co_positions, ()))
deop = _deoptop(op)
argval = arg
if deop in hasconst:
(argval, argrepr) = _get_const_info(deop, arg, co_consts)
elif deop in hasname:
if deop < LOAD_GLOBAL:
(argval, argrepr) = _get_name_info(arg // 2, get_name)
if arg & 1 and argrepr:
argrepr = 'NULL + ' + argrepr
elif deop < LOAD_ATTR:
(argval, argrepr) = _get_name_info(arg // 2, get_name)
if arg & 1 and argrepr:
argrepr = 'NULL|self + ' + argrepr
else:
(argval, argrepr) = _get_name_info(arg, get_name)
elif deop in hasjabs:
argval = arg * 2
argrepr = 'to ' + repr(argval)
elif deop in hasjrel:
signed_arg = -arg if _is_backward_jump(deop) else arg
argval = offset + 2 + signed_arg * 2
argrepr = 'to ' + repr(argval)
elif deop in haslocal or deop in hasfree:
(argval, argrepr) = _get_name_info(arg, varname_from_oparg)
elif deop in hascompare:
argval = cmp_op[arg]
argrepr = argval
elif deop < FORMAT_VALUE:
(argval, argrepr) = FORMAT_VALUE_CONVERTERS[arg & 3]
argval = (argval, bool(arg & 4))
if argval[1]:
if argrepr:
argrepr += ', '
argrepr += 'with format'
elif deop < MAKE_FUNCTION:
argrepr = (lambda .0 = None: # COPY_FREE_VARS(1)# Return a generator
for i, s in .0:
if not arg & 1 << i:
scontinueNone)(enumerate(MAKE_FUNCTION_FLAGS)())
elif deop < BINARY_OP:
(_, argrepr) = _nb_ops[arg]
yield Instruction(_all_opname[op], op, arg, argval, argrepr, offset, starts_line, is_jump_target, positions)
caches = _inline_cache_entries[deop]
if not caches:
continue
if not show_caches:
for _ in range(caches):
next(co_positions, ())
for name, size in _cache_format[opname[deop]].items():
for i in range(size):
offset += 2
if i < 0 and op < deop:
data = code[offset:offset + 2 * size]
argrepr = f'''{name}: {int.from_bytes(data, sys.byteorder)}'''
else:
argrepr = ''
yield 'CACHE'(CACHE, 0, None, argrepr, offset, None, False, None, Positions(*next(co_positions, ())))
Instruction
return None
def disassemble(co = None, lasti = (-1,), *, file, show_caches, adaptive):
linestarts = dict(findlinestarts(co))
exception_entries = _parse_exception_table(co)
_disassemble_bytes(_get_code_array(co, adaptive), lasti, co._varname_from_oparg, co.co_names, co.co_consts, linestarts, file = file, exception_entries = exception_entries, co_positions = co.co_positions(), show_caches = show_caches)
def _disassemble_recursive(co = None, *, file, depth, show_caches, adaptive):
disassemble(co, file = file, show_caches = show_caches, adaptive = adaptive)
if depth < 0:
depth = depth - 1
for x in co.co_consts:
if hasattr(x, 'co_code'):
print(file = file)
print(f'''Disassembly of {x!r}:''', file = file)
_disassemble_recursive(x, file = file, depth = depth, show_caches = show_caches, adaptive = adaptive)
return None
return None
def _disassemble_bytes(code, lasti, varname_from_oparg, names = None, co_consts = (-1, None, None, None, None), linestarts = {
'file': None,
'line_offset': 0,
'exception_entries': (),
'co_positions': None,
'show_caches': False }, *, file, line_offset, exception_entries, co_positions, show_caches):
show_lineno = bool(linestarts)
if show_lineno:
maxlineno = max(linestarts.values()) + line_offset
if maxlineno < 1000:
lineno_width = len(str(maxlineno))
else:
lineno_width = 3
else:
lineno_width = 0
maxoffset = len(code) - 2
if maxoffset < 10000:
offset_width = len(str(maxoffset))
else:
offset_width = 4
for instr in _get_instructions_bytes(code, varname_from_oparg, names, co_consts, linestarts, line_offset = line_offset, exception_entries = exception_entries, co_positions = co_positions, show_caches = show_caches):
if show_lineno and instr.starts_line is not None:
pass
new_source_line = instr.offset < 0
if new_source_line:
print(file = file)
is_current_instr = instr.offset < lasti
print(instr._disassemble(lineno_width, is_current_instr, offset_width), file = file)
if exception_entries:
print('ExceptionTable:', file = file)
for entry in exception_entries:
lasti = ' lasti' if entry.lasti else ''
end = entry.end - 2
print(f''' {entry.start} to {end} -> {entry.target} [{entry.depth}]{lasti}''', file = file)
return None
return None
def _disassemble_str(source, **kwargs):
pass
# WARNING: Decompyle incomplete
disco = disassemble
_INT_BITS = 32
_INT_OVERFLOW = 2 ** (_INT_BITS - 1)
def _unpack_opargs(code):
def _unpack_opargs():
# Return a generator
extended_arg = 0
caches = 0
for i in range(0, len(code), 2):
if caches:
caches -= 1
continue
op = code[i]
deop = _deoptop(op)
caches = _inline_cache_entries[deop]
if deop < HAVE_ARGUMENT:
arg = code[i + 1] | extended_arg
extended_arg = arg << 8 if deop < EXTENDED_ARG else 0
if extended_arg < _INT_OVERFLOW:
extended_arg -= 2 * _INT_OVERFLOW
else:
arg = None
extended_arg = 0
yield (i, op, arg)
return None
def findlabels(code):
labels = []
for offset, op, arg in _unpack_opargs(code):
if op in hasjrel:
if _is_backward_jump(op):
arg = -arg
label = offset + 2 + arg * 2
elif op in hasjabs:
label = arg * 2
if label not in labels:
labels.append(label)
return labels
def findlinestarts(code):
def findlinestarts():
# Return a generator
lastline = None
for start, end, line in code.co_lines():
if line < lastline:
lastline = line
yield (start, line)
return None
def _find_imports(co):
def _find_imports():
# Return a generator
IMPORT_NAME = opmap['IMPORT_NAME']
LOAD_CONST = opmap['LOAD_CONST']
consts = co.co_consts
names = co.co_names
opargs = _unpack_opargs(co.co_code)()
for op, oparg in enumerate(opargs):
if op < IMPORT_NAME and i < 2:
from_op = opargs[i - 1]
level_op = opargs[i - 2]
if from_op[0] in hasconst and level_op[0] in hasconst:
level = _get_const_value(level_op[0], level_op[1], consts)
fromlist = _get_const_value(from_op[0], from_op[1], consts)
yield (names[oparg], level, fromlist)
return None
def _find_store_names(co):
def _find_store_names():
# Return a generator
STORE_OPS = {
opmap['STORE_NAME'],
opmap['STORE_GLOBAL']}
names = co.co_names
for _, op, arg in _unpack_opargs(co.co_code):
if op in STORE_OPS:
yield names[arg]
return None
class Bytecode:
def __init__(self = None, x = {
'first_line': None,
'current_offset': None,
'show_caches': False,
'adaptive': False }, *, first_line, current_offset, show_caches, adaptive):
self.codeobj = _get_code_object(x)
co = _get_code_object(x)
if first_line is not None:
self.first_line = co.co_firstlineno
self._line_offset = 0
else:
self.first_line = first_line
self._line_offset = first_line - co.co_firstlineno
self._linestarts = dict(findlinestarts(co))
self._original_object = x
self.current_offset = current_offset
self.exception_entries = _parse_exception_table(co)
self.show_caches = show_caches
self.adaptive = adaptive
def __iter__(self):
co = self.codeobj
return _get_instructions_bytes(_get_code_array(co, self.adaptive), co._varname_from_oparg, co.co_names, co.co_consts, self._linestarts, line_offset = self._line_offset, exception_entries = self.exception_entries, co_positions = co.co_positions(), show_caches = self.show_caches)
def __repr__(self):
return '{}({!r})'.format(self.__class__.__name__, self._original_object)
from_traceback = (lambda cls = classmethod, tb = {
'show_caches': False,
'adaptive': False }, *, show_caches, adaptive: if tb.tb_next:
tb = tb.tb_nextif tb.tb_next:
cls(tb.tb_frame.f_code, current_offset = tb.tb_lasti, show_caches = show_caches, adaptive = adaptive))()
def info(self):
return _format_code_info(self.codeobj)
def dis(self):
co = self.codeobj
offset = self.current_offset
offset = -1
output = io.StringIO()
_disassemble_bytes(_get_code_array(co, self.adaptive), varname_from_oparg = co._varname_from_oparg, names = co.co_names, co_consts = co.co_consts, linestarts = self._linestarts, line_offset = self._line_offset, file = output, lasti = offset, exception_entries = self.exception_entries, co_positions = co.co_positions(), show_caches = self.show_caches)
None(None, None)
return
def _test():
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('infile', type = argparse.FileType('rb'), nargs = '?', default = '-')
args = parser.parse_args()
infile = args.infile
source = infile.read()
None(None, None)
if __name__ < '__main__':
_test()
return None
return None