micropython/tests/bytecode/unpyc.py

113 lines
4.0 KiB
Python

import argparse, dis, marshal, struct, sys, time, types
def show_file(fname):
f = open(fname, "rb")
magic = f.read(4)
moddate = f.read(4)
modtime = time.asctime(time.localtime(struct.unpack('I', moddate)[0]))
f.read(4) # don't know what these 4 bytes are
#print("magic %s" % (bytes_to_hex(magic)))
#print("moddate %s (%s)" % (bytes_to_hex(moddate), modtime))
code = marshal.load(f)
to_show_code = [code]
for c in to_show_code:
show_code(c, to_show_code)
def show_code(code, to_show_code):
print("code {}".format(code.co_name))
indent = ' '
print("%sflags %04x" % (indent, code.co_flags))
print("%sargcount %d" % (indent, code.co_argcount))
print("%snlocals %d" % (indent, code.co_nlocals))
print("%sstacksize %d" % (indent, code.co_stacksize))
#show_hex("code", code.co_code, indent=indent)
disassemble(code)
#print("%sconsts" % indent)
for const in code.co_consts:
if type(const) == types.CodeType:
# print(" {}code at {:#x}".format(indent, id(const)))
to_show_code.append(const)
# else:
# print(" %s%r" % (indent, const))
#print("%snames %r" % (indent, code.co_names))
#print("%svarnames %r" % (indent, code.co_varnames))
#print("%sfreevars %r" % (indent, code.co_freevars))
#print("%scellvars %r" % (indent, code.co_cellvars))
#print("%sfilename %r" % (indent, code.co_filename))
#print("%sname %r" % (indent, code.co_name))
#print("%sfirstlineno %d" % (indent, code.co_firstlineno))
#show_hex("lnotab", code.co_lnotab, indent=indent)
def show_hex(label, h, indent):
h = bytes_to_hex(h)
if len(h) < 60:
print("%s%s %s" % (indent, label, h))
else:
print("%s%s" % (indent, label))
for i in range(0, len(h), 60):
print("%s %s" % (indent, h[i:i+60]))
def bytes_to_hex(bs):
h = []
for b in bs:
h.append("{:02x}".format(b))
return ''.join(h)
# taken from python library
import opcode
def disassemble(co):
"""Disassemble a code object."""
code = co.co_code
num_bytes = len(code)
num_ops = 0
i = 0
extended_arg = 0
free = None
while i < num_bytes:
op = code[i]
print(repr(i).rjust(4), end=' ')
num_ops += 1
i = i+1
if op < opcode.HAVE_ARGUMENT:
print(opcode.opname[op])
else:
print(opcode.opname[op], end=' ')
oparg = code[i] + code[i+1]*256 + extended_arg
extended_arg = 0
i = i+2
if op == opcode.EXTENDED_ARG:
extended_arg = oparg*65536
#print(repr(oparg).rjust(5))
if op in opcode.hasconst:
if type(co.co_consts[oparg]) == types.CodeType:
print('code', co.co_consts[oparg].co_name)
else:
print(repr(co.co_consts[oparg]))
elif op in opcode.hasname:
print(co.co_names[oparg])
elif op in opcode.hasjrel:
print(repr(i + oparg))
elif op in opcode.haslocal:
print(oparg, co.co_varnames[oparg])
elif op in opcode.hascompare:
print(opcode.cmp_op[oparg])
elif op in opcode.hasfree:
if free is None:
free = co.co_cellvars + co.co_freevars
print(oparg, free[oparg])
elif op in opcode.hasnargs:
print('{}, {}'.format(code[i-2], code[i-1]))
else:
print(repr(oparg))
# accounting output for bytes per opcode
#print('{} bytes / {} opcodes = {} bytes per opcode'.format(num_bytes, num_ops, num_bytes / num_ops))
#print('{} {} {} # bpo'.format(num_bytes, num_ops, num_bytes / num_ops))
if __name__ == "__main__":
cmd_parser = argparse.ArgumentParser(description='Uncompile .pyc files')
cmd_parser.add_argument('files', nargs='+', help='input files')
args = cmd_parser.parse_args()
for fname in args.files:
show_file(fname)