Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Added support for tons of random stuff.

  • Loading branch information...
commit d8b75fe7e0879d426cb97b1fdc8d73fec35ceb4e 1 parent f9d42f1
Alex Gaynor authored June 19, 2011
59  decompile.py
@@ -31,6 +31,14 @@ def finish(self, instructions):
31 31
             else:
32 32
                 assert False
33 33
 
  34
+OPCODE_WITH_CONST = frozenset(opcode.hasconst)
  35
+OPCODE_WITH_NAME = frozenset(opcode.hasname)
  36
+OPCODE_WITH_LOCAL = frozenset(opcode.haslocal)
  37
+OPCODE_WITH_JREL = frozenset(opcode.hasjrel)
  38
+OPCODE_WITH_NUM = frozenset(opcode.hasjabs) | frozenset([
  39
+    opcode.opmap[op] for op in ["BUILD_LIST", "CALL_FUNCTION"]
  40
+])
  41
+
34 42
 def parse_bytecode(code):
35 43
     i = 0
36 44
     bytecodes = map(ord, code.co_code)
@@ -43,16 +51,16 @@ def parse_bytecode(code):
43 51
         if op >= opcode.HAVE_ARGUMENT:
44 52
             oparg = bytecodes[i] + (bytecodes[i + 1] << 8)
45 53
             i += 2
46  
-            if op in opcode.hasconst:
  54
+            if op in OPCODE_WITH_CONST:
47 55
                 arg = code.co_consts[oparg]
48  
-            elif op in opcode.hasname:
  56
+            elif op in OPCODE_WITH_NAME:
49 57
                 arg = code.co_names[oparg]
50  
-            elif op in opcode.hasjabs:
  58
+            elif op in OPCODE_WITH_NUM:
51 59
                 arg = oparg
52  
-            elif op in opcode.hasjrel:
  60
+            elif op in OPCODE_WITH_JREL:
53 61
                 # Make it absolute
54 62
                 arg = i + oparg
55  
-            elif op in opcode.haslocal:
  63
+            elif op in OPCODE_WITH_LOCAL:
56 64
                 arg = code.co_varnames[oparg]
57 65
             else:
58 66
                 raise NotImplementedError
@@ -93,7 +101,10 @@ def get_basic_block(self, idx):
93 101
 
94 102
     def handle_simple_op(self, instr):
95 103
         pass
96  
-    handle_LOAD_CONST = handle_LOAD_FAST = handle_LOAD_GLOBAL = handle_RETURN_VALUE = handle_POP_TOP = handle_simple_op
  104
+    handle_LOAD_CONST = handle_LOAD_FAST = handle_LOAD_ATTR = handle_LOAD_GLOBAL = \
  105
+        handle_STORE_FAST = handle_STORE_SUBSCR = handle_BUILD_LIST = \
  106
+        handle_CALL_FUNCTION = \
  107
+        handle_RETURN_VALUE = handle_POP_TOP = handle_simple_op
97 108
 
98 109
     def handle_POP_JUMP_IF_FALSE(self, instr):
99 110
         instr.true_block = self.get_basic_block(instr.new_idx + 1)
@@ -103,13 +114,6 @@ def handle_JUMP_FORWARD(self, instr):
103 114
         instr.fallthrough = self.get_basic_block(instr.arg)
104 115
 
105 116
 
106  
-class Literal(object):
107  
-    def __init__(self, obj):
108  
-        self.obj = obj
109  
-
110  
-    def build(self):
111  
-        return str(self.obj)
112  
-
113 117
 class AddBasicBlock(Exception):
114 118
     def __init__(self, block):
115 119
         self.block = block
@@ -153,20 +157,41 @@ def handle_basic_block(self, basic_block):
153 157
             handler(instr)
154 158
 
155 159
     def handle_literal(self, instr):
156  
-        self.buf.append(Literal(instr.arg))
  160
+        self.buf.append(str(instr.arg))
157 161
     handle_LOAD_CONST = handle_LOAD_FAST = handle_LOAD_GLOBAL = handle_literal
158 162
 
  163
+    def handle_LOAD_ATTR(self, instr):
  164
+        [obj] = self.get_and_clear_buf(1)
  165
+        self.buf.append("%s.%s" % (obj, instr.arg))
  166
+
  167
+    def handle_BUILD_LIST(self, instr):
  168
+        self.buf.append("[]")
  169
+
  170
+    def handle_STORE_FAST(self, instr):
  171
+        [obj] = self.get_and_clear_buf(1)
  172
+        self.emit("%s = %s" % (instr.arg, obj))
  173
+
  174
+    def handle_STORE_SUBSCR(self, instr):
  175
+        value, obj, idx = self.get_and_clear_buf(3)
  176
+        self.emit("%s[%s] = %s" % (obj, idx, value))
  177
+
  178
+    def handle_CALL_FUNCTION(self, instr):
  179
+        args = self.get_and_clear_buf(instr.arg + 1)
  180
+        func = args[0]
  181
+        args = args[1:]
  182
+        self.buf.append("%s(%s)" % (func, ", ".join(args)))
  183
+
159 184
     def handle_RETURN_VALUE(self, instr):
160 185
         [obj] = self.get_and_clear_buf(1)
161  
-        self.emit("return %s" % obj.build())
  186
+        self.emit("return %s" % obj)
162 187
 
163 188
     def handle_POP_TOP(self, instr):
164 189
         [obj] = self.get_and_clear_buf(1)
165  
-        self.emit(obj.build())
  190
+        self.emit(obj)
166 191
 
167 192
     def handle_POP_JUMP_IF_FALSE(self, instr):
168 193
         [obj] = self.get_and_clear_buf(1)
169  
-        self.emit("if %s:" % obj.build())
  194
+        self.emit("if %s:" % obj)
170 195
         with self.indent():
171 196
             self.handle_basic_block(instr.true_block)
172 197
         self.emit("else:")
14  test_decompile.py
@@ -62,6 +62,20 @@ def f(a):
62 62
                 return a
63 63
         """)
64 64
 
  65
+    def test_list_ops(self):
  66
+        def f():
  67
+            x = []
  68
+            x.append(1)
  69
+            x[0] = 3
  70
+
  71
+        self.assert_decompiles(f, """
  72
+            def f():
  73
+                x = []
  74
+                x.append(1)
  75
+                x[0] = 3
  76
+                return None
  77
+        """)
  78
+
65 79
 class TestBytecodeParser(object):
66 80
     def assert_bytecode(self, func, expected):
67 81
         instructions = parse_bytecode(func.__code__)

0 notes on commit d8b75fe

Please sign in to comment.
Something went wrong with that request. Please try again.