Browse Source

Print opcodes as Python code

master
Ben Kurtovic 10 years ago
parent
commit
0e581bb516
1 changed files with 106 additions and 0 deletions
  1. +106
    -0
      prettify.py

+ 106
- 0
prettify.py View File

@@ -0,0 +1,106 @@
import opcode
import sys

import func_smash

OP_HASLOAD = ("LOAD_FAST", "LOAD_CONST", "LOAD_GLOBAL")
OP_HASBINARY = {"BINARY_ADD": "+", "BINARY_SUBTRACT": "-",
"BINARY_MULTIPLY": "*", "BINARY_DIVIDE": "/",
"BINARY_POWER": "**", "BINARY_MODULO": "%"}
OP_HASBUILD = ("BUILD_TUPLE", "BUILD_LIST", "BUILD_MAP", "BUILD_SET")

def prettify_function(func, indent=0):
args = _get_func_args(func)
_print(indent, "def {0}({1}):".format(func.func_name, args))
prettify_code(func.__code__, indent=indent+4)

def prettify_code(codeobj, indent=0):
codes = []
codestring = codeobj.co_code
length = len(codestring)
i = 0
while i < length:
code = ord(codestring[i])
i += 1
if code >= opcode.HAVE_ARGUMENT:
arg = func_smash._get_argument(codeobj, codestring, i, code)
i += 2
codes.append((code, arg))
else:
codes.append((code, None))
_print_codestring(codes, indent)

def _print_codestring(codes, indent):
stack = []
print_buffer = []
for instruction in codes:
code, arg = instruction
opname = opcode.opname[code]
_print(indent, opname, arg, "; stack ==", stack, debug=True)
if opname in OP_HASLOAD:
stack.append(arg)
elif opname in OP_HASBINARY:
tos, tos1 = str(stack.pop()), str(stack.pop())
stack.append(" ".join((tos1, OP_HASBINARY[opname], tos)))
elif opname in OP_HASBUILD:
args = []
for i in xrange(arg):
args.append(str(stack.pop()))
if opname == "BUILD_TUPLE":
stack.append("(" + ", ".join(args) + ")")
elif opname == "STORE_FAST":
_print(indent, arg, "=", stack.pop())
elif opname == "POP_TOP":
_print(indent, stack.pop())
elif opname == "CALL_FUNCTION":
numargs, numkwargs = arg
args = []
for i in xrange(numkwargs):
value = str(stack.pop())
key = str(stack.pop())
args.append("=".join((key, value)))
for i in xrange(numargs):
args.append(str(stack.pop()))
args.reverse()
funcname = stack.pop()
stack.append("{0}({1})".format(funcname, ", ".join(args)))
elif opname == "PRINT_ITEM":
print_buffer.append(stack.pop())
elif opname == "PRINT_NEWLINE":
_print(indent, "print", ", ".join(print_buffer))
print_buffer = []
elif opname == "RETURN_VALUE":
_print(indent, "return", stack.pop())
else:
raise NotImplementedError(opname, arg, stack)

def _get_func_args(func):
codeobj = func.__code__
count = codeobj.co_argcount
if count == 0:
return ""
return ", ".join([arg for arg in codeobj.co_varnames[:count]])

def _print(indentation, *args, **kwargs):
argstring = " ".join([str(arg) for arg in args if str(arg)])
if kwargs.get("debug"):
# Ignore debug messages without -d flag
if len(sys.argv) > 1 and sys.argv[1] == "-d":
print " " * 40 + "#", argstring
return
print " " * indentation + argstring

if __name__ == "__main__":
def f1(a):
b = a + 10 / 7.0
d = long_func(a, b+9, c=42, d=43)
print b, d
v, z
return d % 10

def f2():
pass

prettify_function(f1)
print
prettify_function(f2)

Loading…
Cancel
Save