Skip to content

Commit

Permalink
Started implementing forloops, works for simple cases.
Browse files Browse the repository at this point in the history
  • Loading branch information
alex committed Jun 19, 2011
1 parent d8b75fe commit d5bc231
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 1 deletion.
34 changes: 33 additions & 1 deletion decompile.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@
class Instruction(object):
def __init__(self, op, arg=NO_ARG, real_idx=None, new_idx=None):
self.op = op
self.opname = opcode.opname[op]
self.arg = arg
self.real_idx = real_idx
self.new_idx = new_idx

@property
def opname(self):
return opcode.opname[self.op]

def __repr__(self):
r = self.opname
if self.arg is not NO_ARG:
Expand Down Expand Up @@ -104,6 +107,10 @@ def handle_simple_op(self, instr):
handle_LOAD_CONST = handle_LOAD_FAST = handle_LOAD_ATTR = handle_LOAD_GLOBAL = \
handle_STORE_FAST = handle_STORE_SUBSCR = handle_BUILD_LIST = \
handle_CALL_FUNCTION = \
handle_SETUP_LOOP = \
handle_JUMP_ABSOLUTE = \
handle_POP_BLOCK = \
handle_NOP = \
handle_RETURN_VALUE = handle_POP_TOP = handle_simple_op

def handle_POP_JUMP_IF_FALSE(self, instr):
Expand All @@ -113,6 +120,15 @@ def handle_POP_JUMP_IF_FALSE(self, instr):
def handle_JUMP_FORWARD(self, instr):
instr.fallthrough = self.get_basic_block(instr.arg)

def handle_GET_ITER(self, instr):
# TODO: broken, in oh so many ways
instr.loop_var = self.instructions[instr.new_idx + 2].arg
self.instructions[instr.new_idx + 2].op = opcode.opmap["NOP"]
instr.loop = self.get_basic_block(instr.new_idx + 1)

def handle_FOR_ITER(self, instr):
instr.fallthrough = self.get_basic_block(instr.arg)


class AddBasicBlock(Exception):
def __init__(self, block):
Expand Down Expand Up @@ -156,6 +172,10 @@ def handle_basic_block(self, basic_block):
handler = getattr(self, "handle_%s" % instr.opname)
handler(instr)

def handle_NOP(self, instr):
pass
handle_SETUP_LOOP = handle_POP_BLOCK = handle_NOP

def handle_literal(self, instr):
self.buf.append(str(instr.arg))
handle_LOAD_CONST = handle_LOAD_FAST = handle_LOAD_GLOBAL = handle_literal
Expand All @@ -175,6 +195,15 @@ def handle_STORE_SUBSCR(self, instr):
value, obj, idx = self.get_and_clear_buf(3)
self.emit("%s[%s] = %s" % (obj, idx, value))

def handle_GET_ITER(self, instr):
[obj] = self.get_and_clear_buf(1)
self.emit("for %s in %s:" % (instr.loop_var, obj))
with self.indent():
self.handle_basic_block(instr.loop)

def handle_FOR_ITER(self, instr):
self.basic_blocks.append(instr.fallthrough)

def handle_CALL_FUNCTION(self, instr):
args = self.get_and_clear_buf(instr.arg + 1)
func = args[0]
Expand All @@ -201,6 +230,9 @@ def handle_POP_JUMP_IF_FALSE(self, instr):
def handle_JUMP_FORWARD(self, instr):
self.basic_blocks.append(instr.fallthrough)

def handle_JUMP_ABSOLUTE(self, instr):
self.emit("continue")


def decompile(function):
instructions = parse_bytecode(function.__code__)
Expand Down
13 changes: 13 additions & 0 deletions test_decompile.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,19 @@ def f():
return None
""")

def test_simple_for_loop(self):
def f(x):
for i in x:
pass

# TODO: continue should become pass where possible
self.assert_decompiles(f, """
def f(x):
for i in x:
continue
return None
""")

class TestBytecodeParser(object):
def assert_bytecode(self, func, expected):
instructions = parse_bytecode(func.__code__)
Expand Down

0 comments on commit d5bc231

Please sign in to comment.