summaryrefslogtreecommitdiffstatshomepage
path: root/tests/bytecode/unpyc.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/bytecode/unpyc.py')
-rw-r--r--tests/bytecode/unpyc.py112
1 files changed, 112 insertions, 0 deletions
diff --git a/tests/bytecode/unpyc.py b/tests/bytecode/unpyc.py
new file mode 100644
index 0000000000..e31220cf3e
--- /dev/null
+++ b/tests/bytecode/unpyc.py
@@ -0,0 +1,112 @@
+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)