# # decompile.py - decompile Python code objects # # Copyright (c) 2001 Jonathan Patrick Giddy # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # Send comments to: # Jonathan Giddy, jongiddy@pobox.co.uk # version 0.9 # Date 21 January 2005 # - no code changes, added MIT licence, updated email address # - there will be no v1.0 - try decompyle instead # version 0.8 # Date 25 January 2001 __version__ = '0.9' import dis, string, sys, types VARARGS = 4 KWARGS = 8 PRECEDENCE_ATOM = 24 PRECEDENCE_POWER = 17 PRECEDENCE_UNARY = 16 PRECEDENCE_MULT = 15 PRECEDENCE_ADD = 14 PRECEDENCE_SHIFT = 13 PRECEDENCE_BAND = 12 PRECEDENCE_BXOR = 11 PRECEDENCE_BOR = 10 PRECEDENCE_CMP = 9 PRECEDENCE_IS = 8 PRECEDENCE_IN = 7 PRECEDENCE_NOT = 6 PRECEDENCE_AND = 5 PRECEDENCE_OR = 4 PRECEDENCE_LAMBDA = 3 PRECEDENCE_ARG = PRECEDENCE_LAMBDA PRECEDENCE_COMMA = 1 PRECEDENCE_NONE = 0 def current_line(code, i): tab = code.co_lnotab line = code.co_firstlineno stopat = i addr = 0 for i in range(0, len(tab), 2): addr = addr + ord(tab[i]) if addr > stopat: break line = line + ord(tab[i+1]) return line class Expression: def __init__(self, value, precedence): self.value = value self.precedence = precedence def __repr__(self): return 'Expression(%s, %s)' % (`self.value`, `self.precedence`) def __str__(self): return str(self.value) def Precedence(self): return self.precedence def Value(self): return self.value def GetString(self, precedence): if self.Precedence() < precedence: return '(%s)' % self else: return str(self) class Atom(Expression): def __init__(self, value): Expression.__init__(self, value, PRECEDENCE_ATOM) class Constant(Atom): def __str__(self): if self.value is Ellipsis: return '...' else: return repr(self.value) class Local(Atom): pass class Global(Atom): pass class Map(Atom): def __init__(self): Atom.__init__(self, []) def __str__(self): return '{%s}' % string.join(self.value, ', ') def SetAttr(self, name, value): self.value.append("%s: %s" % (name, value)) class Tuple(Atom): def __init__(self, values): Atom.__init__(self, values) def __str__(self): values = self.Value() if len(values) == 0: return '()' elif len(values) == 1: # This doesn't need the parens immediately, but # it emphasises the prescence of the comma, and # confirms that it is a 1-tuple return '(%s,)' % values[0] else: return string.join(values, ', ') def Precedence(self): if len(self.value) <= 1: return PRECEDENCE_ATOM else: return PRECEDENCE_COMMA def Value(self): return tuple(self.value) class CodeCursor: def __init__(self, code): self.code = code # code object self.i = 0 # instruction pointer self.extend = 0 # extended opcodes self.lineno = 1 # minimum possible line number self.lastop = 0 # pointer to last operator read self.stopi = [len(code.co_code)] def GetPosition(self): return self.i def AtEnd(self): return self.i == self.stopi[0] def GetLine(self): return max(self.lineno, current_line(self.code, self.lastop)) def SetLine(self, lineno): assert lineno >= self.lineno, `lineno, self.lineno` self.lineno = lineno def PushStop(self, i): self.stopi.append(i) def PopStop(self): return self.stopi.pop() def NextOpcode(self): if self.i < self.stopi[-1]: c = self.code.co_code[self.i] op = ord(c) opcode = dis.opname[op] if opcode == 'EXTENDED_ARG': self.i = self.i + 1 self.extend = self.ReadOperand() return self.NextOpcode() else: opcode = None return opcode def ReadOpcode(self, *args): opcode = self.NextOpcode() assert opcode is not None if args: assert opcode in args, `self.i, opcode` self.lastop = self.i self.i = self.i + 1 return opcode def ReadOperand(self): assert self.i + 1 < self.stopi[-1], `self.i, self.stopi` co_code = self.code.co_code operand = ord(co_code[self.i]) + ord(co_code[self.i+1])*256 + \ (self.extend << 16) self.extend = 0 self.i = self.i + 2 return operand def GetConstant(self, n): return self.code.co_consts[n] def GetLocal(self, n): assert n < len(self.code.co_varnames), `n, self.code.co_varnames` return self.code.co_varnames[n] def GetName(self, n): assert n < len(self.code.co_names), `n, self.code.co_names` return self.code.co_names[n] class Decompiler: def __init__(self, version): self.version = version self.stack = [] self.lines = {} self.global_decl = {} self.loop = None def decompile(self, code, *termop): try: self.code = code opcode = code.NextOpcode() while opcode is not None and opcode not in termop: opcode = string.replace(opcode, '+', '_') method = getattr(self, opcode) apply(method, (code,)) opcode = code.NextOpcode() except: dis.dis(code.code) print raise def getstack(self): return self.stack def getsource(self, indent): assert not self.stack, `self.stack` lines = {} if not self.lines: self.lines[self.code.GetLine()] = 'pass' for key, value in self.lines.items(): lines[key] = ' ' * indent + value return lines def addline(self, lineno, line): assert type(line) == type(''), `line` prev = self.lines.get(lineno) if prev is None: self.lines[lineno] = line else: self.lines[lineno] = '%s; %s' % (prev, line) def addclause(self, lineno, head, body): if body.has_key(lineno): if 0: assert len(body) == 1, `body` self.addline(lineno, "%s %s" % (head, string.strip(body[lineno]))) else: line = body[lineno] self.addline(lineno, "%s %s" % (head, string.strip(line))) del body[lineno] self.lines.update(body) body[lineno] = line else: self.addline(lineno, head) self.lines.update(body) self.code.SetLine(max(body.keys()) + 1) def SET_LINENO(self, code): code.ReadOpcode('SET_LINENO') code.ReadOperand() def BINARY_ADD(self, code): code.ReadOpcode('BINARY_ADD') y = self.stack.pop().GetString(PRECEDENCE_ADD+1) x = self.stack.pop().GetString(PRECEDENCE_ADD) self.stack.append(Expression('%s + %s' % (x, y), PRECEDENCE_ADD)) def BINARY_AND(self, code): code.ReadOpcode('BINARY_AND') y = self.stack.pop().GetString(PRECEDENCE_BAND+1) x = self.stack.pop().GetString(PRECEDENCE_BAND) self.stack.append(Expression('%s & %s' % (x, y), PRECEDENCE_BAND)) def BINARY_DIVIDE(self, code): code.ReadOpcode('BINARY_DIVIDE') y = self.stack.pop().GetString(PRECEDENCE_MULT+1) x = self.stack.pop().GetString(PRECEDENCE_MULT) self.stack.append(Expression('%s / %s' % (x, y), PRECEDENCE_MULT)) def BINARY_LSHIFT(self, code): code.ReadOpcode('BINARY_LSHIFT') y = self.stack.pop().GetString(PRECEDENCE_SHIFT+1) x = self.stack.pop().GetString(PRECEDENCE_SHIFT) self.stack.append(Expression('%s << %s' % (x, y), PRECEDENCE_SHIFT)) def BINARY_MODULO(self, code): code.ReadOpcode('BINARY_MODULO') y = self.stack.pop().GetString(PRECEDENCE_MULT+1) x = self.stack.pop().GetString(PRECEDENCE_MULT) self.stack.append(Expression('%s %% %s' % (x, y), PRECEDENCE_MULT)) def BINARY_MULTIPLY(self, code): code.ReadOpcode('BINARY_MULTIPLY') y = self.stack.pop().GetString(PRECEDENCE_MULT+1) x = self.stack.pop().GetString(PRECEDENCE_MULT) self.stack.append(Expression('%s * %s' % (x, y), PRECEDENCE_MULT)) def BINARY_OR(self, code): code.ReadOpcode('BINARY_OR') y = self.stack.pop().GetString(PRECEDENCE_BOR+1) x = self.stack.pop().GetString(PRECEDENCE_BOR) self.stack.append(Expression('%s | %s' % (x, y), PRECEDENCE_BOR)) def BINARY_POWER(self, code): code.ReadOpcode('BINARY_POWER') y = self.stack.pop() if y.Precedence() == PRECEDENCE_POWER: # include ** in parentheses because the correct order is poorly # understood, and is the opposite of the other binary operators y = y.GetString(PRECEDENCE_ATOM) else: y = y.GetString(PRECEDENCE_UNARY) x = self.stack.pop().GetString(PRECEDENCE_ATOM) self.stack.append(Expression('%s ** %s' % (x, y), PRECEDENCE_POWER)) def BINARY_RSHIFT(self, code): code.ReadOpcode() y = self.stack.pop().GetString(PRECEDENCE_SHIFT+1) x = self.stack.pop().GetString(PRECEDENCE_SHIFT) self.stack.append(Expression('%s >> %s' % (x, y), PRECEDENCE_SHIFT)) def BINARY_SUBSCR(self, code): code.ReadOpcode() y = self.stack.pop().GetString(PRECEDENCE_NONE) x = self.stack.pop().GetString(PRECEDENCE_ATOM) self.stack.append(Expression('%s[%s]' % (x, y), PRECEDENCE_ATOM)) def BINARY_SUBTRACT(self, code): code.ReadOpcode() y = self.stack.pop().GetString(PRECEDENCE_ADD+1) x = self.stack.pop().GetString(PRECEDENCE_ADD) self.stack.append(Expression('%s - %s' % (x, y), PRECEDENCE_ADD)) def BINARY_XOR(self, code): code.ReadOpcode() y = self.stack.pop().GetString(PRECEDENCE_BXOR+1) x = self.stack.pop().GetString(PRECEDENCE_BXOR) self.stack.append(Expression('%s ^ %s' % (x, y), PRECEDENCE_BXOR)) def BREAK_LOOP(self, code): code.ReadOpcode('BREAK_LOOP') self.addline(code.GetLine(), 'break') def BUILD_LIST(self, code): code.ReadOpcode('BUILD_LIST') oparg = code.ReadOperand() values = [] for i in range(oparg): value = self.stack.pop() if value.Precedence() < PRECEDENCE_ARG: value = '(%s)' % value values.append(str(value)) values.reverse() valuelist = string.join(values, ', ') self.stack.append(Expression('[%s]' % valuelist, PRECEDENCE_ATOM)) def BUILD_MAP(self, code): code.ReadOpcode('BUILD_MAP') code.ReadOperand() self.stack.append(Map()) def BUILD_SLICE(self, code): code.ReadOpcode() code.ReadOperand() z = self.stack.pop() if isinstance(z, Constant) and z.Value() is None: z = '' else: z = z.GetString(PRECEDENCE_ARG) y = self.stack.pop() if isinstance(y, Constant) and y.Value() is None: y = '' else: y = y.GetString(PRECEDENCE_ARG) x = self.stack.pop() if isinstance(x, Constant) and x.Value() is None: x = '' else: x = x.GetString(PRECEDENCE_ARG) # always goes into BINARY_SUBSCR, so precedence is irrelevant self.stack.append(Expression('%s:%s:%s' % (x, y, z), PRECEDENCE_NONE)) def BUILD_TUPLE(self, code): code.ReadOpcode() oparg = code.ReadOperand() values = [] for i in range(oparg): value = self.stack.pop().GetString(PRECEDENCE_ARG) values.append(value) values.reverse() self.stack.append(Tuple(values)) def CALL_FUNCTION(self, code): opcode = code.ReadOpcode('CALL_FUNCTION', 'CALL_FUNCTION_VAR', 'CALL_FUNCTION_KW', 'CALL_FUNCTION_VAR_KW') oparg = code.ReadOperand() nkw, nargs = divmod(oparg, 256) args = [] if opcode in ('CALL_FUNCTION_KW', 'CALL_FUNCTION_VAR_KW'): name = self.stack.pop() args.append('**%s' % name) if opcode in ('CALL_FUNCTION_VAR', 'CALL_FUNCTION_VAR_KW'): name = self.stack.pop() args.append('*%s' % name) for i in range(nkw): value = self.stack.pop().GetString(PRECEDENCE_ARG) name = self.stack.pop().Value() args.append('%s=%s' % (name, value)) for i in range(nargs): arg = self.stack.pop().GetString(PRECEDENCE_ARG) args.append(str(arg)) args.reverse() arglist = string.join(args, ', ') func = self.stack.pop().GetString(PRECEDENCE_ATOM) self.stack.append(Expression('%s(%s)' % (func, arglist), PRECEDENCE_ATOM)) CALL_FUNCTION_VAR = CALL_FUNCTION CALL_FUNCTION_KW = CALL_FUNCTION CALL_FUNCTION_VAR_KW = CALL_FUNCTION def COMPARE_OP(self, code): code.ReadOpcode() oparg = code.ReadOperand() if len(self.stack) == 1: y = self.stack.pop() x = None else: y = self.stack.pop() x = self.stack.pop() op = dis.cmp_op[oparg] if op[0] in '!<=>': prec = PRECEDENCE_CMP elif op[-2:] == 'in': prec = PRECEDENCE_IN else: assert op[:2] == 'is', `op` prec = PRECEDENCE_IS if y.Precedence() <= prec: y = '(%s)' % y if x is None: self.stack.append(Chain('%s %s' % (op, y))) else: if x.Precedence() < prec: x = '(%s)' % x self.stack.append(Expression('%s %s %s' % (x, op, y), prec)) def DELETE_ATTR(self, code): code.ReadOpcode() oparg = code.ReadOperand() attr = code.GetName(oparg) x = self.stack.pop().GetString(PRECEDENCE_ATOM) self.addline(code.GetLine(), 'del %s.%s' % (x, attr)) def DELETE_FAST(self, code): names = [] lastlineno = -1 while code.NextOpcode() in ('DELETE_FAST', 'DELETE_GLOBAL', 'DELETE_NAME'): opcode = code.ReadOpcode() if lastlineno == -1: lineno = lastlineno = code.GetLine() else: lineno = code.GetLine() if lineno != lastlineno: self.addline(lastlineno, 'del %s' % string.join(names, ', ')) names = [] lastlineno = lineno oparg = code.ReadOperand() if opcode == 'DELETE_FAST': names.append(code.GetLocal(oparg)) elif opcode == 'DELETE_GLOBAL': names.append(code.GetName(oparg)) else: assert opcode == 'DELETE_NAME', `opcode` names.append(code.GetName(oparg)) self.addline(lastlineno, 'del %s' % string.join(names, ', ')) DELETE_GLOBAL = DELETE_FAST DELETE_NAME = DELETE_FAST def DELETE_SLICE_0(self, code): code.ReadOpcode() x = self.stack.pop().GetString(PRECEDENCE_ATOM) self.addline(code.GetLine(), 'del %s[:]' % x) def DELETE_SLICE_1(self, code): code.ReadOpcode() y = self.stack.pop().GetString(PRECEDENCE_ARG) x = self.stack.pop().GetString(PRECEDENCE_ATOM) self.addline(code.GetLine(), 'del %s[%s:]' % (x, y)) def DELETE_SLICE_2(self, code): code.ReadOpcode() z = self.stack.pop().GetString(PRECEDENCE_ARG) x = self.stack.pop().GetString(PRECEDENCE_ATOM) self.addline(code.GetLine(), 'del %s[:%s]' % (x, z)) def DELETE_SLICE_3(self, code): code.ReadOpcode() z = self.stack.pop().GetString(PRECEDENCE_ARG) y = self.stack.pop().GetString(PRECEDENCE_ARG) x = self.stack.pop().GetString(PRECEDENCE_ATOM) self.addline(code.GetLine(), 'del %s[%s:%s]' % (x, y, z)) def DELETE_SUBSCR(self, code): code.ReadOpcode() y = self.stack.pop().GetString(PRECEDENCE_NONE) x = self.stack.pop().GetString(PRECEDENCE_ATOM) self.addline(code.GetLine(), 'del %s[%s]' % (x, y)) def DUP_TOP(self, code): code.ReadOpcode('DUP_TOP') self.stack.append(self.stack[-1]) def DUP_TOPX(self, code): code.ReadOpcode('DUP_TOPX') n = code.ReadOperand() self.stack = self.stack + self.stack[-n:] def EXEC_STMT(self, code): code.ReadOpcode('EXEC_STMT') lineno = code.GetLine() locals = self.stack.pop() globals = self.stack.pop() stmt = self.stack.pop().GetString(PRECEDENCE_IN) if isinstance(globals, Constant) and globals.Value() is None: self.addline(lineno, 'exec %s' % stmt) else: if locals is globals: globals = globals.GetString(PRECEDENCE_ARG) self.addline(lineno, 'exec %s in %s' % (stmt, globals)) else: globals = globals.GetString(PRECEDENCE_ARG) locals = locals.GetString(PRECEDENCE_ARG) self.addline(lineno, 'exec %s in %s, %s' % (stmt, globals, locals)) def FOR_LOOP(self, code): code.ReadOpcode('FOR_LOOP') leap = code.ReadOperand() loopcleanup = code.GetPosition() + leap self.stack.pop() # sequence index forlist = self.stack.pop() forvar = self.build_target(code).GetString(PRECEDENCE_NONE) head = "for %s in %s:" % (forvar, forlist) lineno = code.GetLine() d = Decompiler(self.version) d.decompile(code, 'JUMP_ABSOLUTE') self.addclause(lineno, head, d.getsource(1)) code.ReadOpcode('JUMP_ABSOLUTE') oparg = code.ReadOperand() # to FOR_LOOP (or SET_LINENO) assert code.GetPosition() == loopcleanup code.ReadOpcode('POP_BLOCK') end = self.loop[1] self.loop = None if code.GetPosition() < end: lineno = code.GetLine() code.PushStop(end) d = Decompiler(self.version) d.decompile(code) code.PopStop() self.addclause(lineno, "else:", d.getsource(1)) assert code.GetPosition() == end def IMPORT_NAME(self, code): names = [] while code.NextOpcode() == 'IMPORT_NAME': code.ReadOpcode('IMPORT_NAME') if self.version >= (2, 0): self.stack.pop() oparg = code.ReadOperand() module = code.GetName(oparg) opname = code.ReadOpcode('IMPORT_FROM', 'IMPORT_STAR', 'STORE_FAST', 'STORE_NAME') if opname in ('IMPORT_FROM', 'IMPORT_STAR'): if names: self.addline('import %s' % string.join(names, ', ')) names = [] if opname == 'IMPORT_STAR': objs = '*' else: while opname == 'IMPORT_FROM': oparg = code.ReadOperand() name1 = code.GetName(oparg) if self.version >= (2, 0): opname = code.ReadOpcode('STORE_FAST', 'STORE_NAME') oparg = code.ReadOperand() if opname == 'STORE_FAST': name2 = code.GetLocal(oparg) else: name2 = code.GetName(oparg) else: name2 = name1 if name1 == name2: names.append(name1) else: names.append('%s as %s' % (name1, name2)) opname = code.ReadOpcode('IMPORT_FROM', 'POP_TOP') objs = string.join(names, ', ') self.addline(code.GetLine(), 'from %s import %s' % (module, objs)) names = [] else: assert opname in ('STORE_FAST', 'STORE_NAME'), `opname` oparg = code.ReadOperand() if opname == 'STORE_FAST': name = code.GetLocal(oparg) else: name = code.GetName(oparg) if module == name: names.append(module) else: names.append("%s as %s" % (module, name)) if names: self.addline(code.GetLine(), 'import %s' % string.join(names, ', ')) def INPLACE_ADD(self, code): opcode = code.ReadOpcode( 'INPLACE_ADD', 'INPLACE_AND', 'INPLACE_DIVIDE', 'INPLACE_LSHIFT', 'INPLACE_MODULO', 'INPLACE_MULTIPLY', 'INPLACE_OR', 'INPLACE_POWER', 'INPLACE_RSHIFT', 'INPLACE_SUBTRACT', 'INPLACE_XOR') if opcode == 'INPLACE_ADD': op = '+=' elif opcode == 'INPLACE_AND': op = '&=' elif opcode == 'INPLACE_DIVIDE': op = '/=' elif opcode == 'INPLACE_LSHIFT': op = '<<=' elif opcode == 'INPLACE_MODULO': op = '%=' elif opcode == 'INPLACE_MULTIPLY': op = '*=' elif opcode == 'INPLACE_OR': op = '|=' elif opcode == 'INPLACE_POWER': op = '**=' elif opcode == 'INPLACE_RSHIFT': op = '>>=' elif opcode == 'INPLACE_SUBTRACT': op = '-=' else: assert opcode == 'INPLACE_XOR', `opcode` op = '^=' y = self.stack.pop().GetString(PRECEDENCE_NONE) x = self.stack.pop().GetString(PRECEDENCE_NONE) opcode = code.ReadOpcode('ROT_THREE', 'ROT_TWO', 'STORE_FAST', 'STORE_GLOBAL') if opcode == 'STORE_FAST': code.ReadOperand() elif opcode == 'STORE_GLOBAL': code.ReadOperand() elif opcode == 'STORE_NAME': code.ReadOperand() elif opcode == 'ROT_THREE': code.ReadOpcode('STORE_SUBSCR') self.stack.pop() self.stack.pop() elif opcode == 'ROT_TWO': code.ReadOpcode('STORE_ATTR') code.ReadOperand() self.stack.pop() self.addline(code.GetLine(), '%s %s %s' % (x, op, y)) INPLACE_AND = INPLACE_ADD INPLACE_DIVIDE = INPLACE_ADD INPLACE_LSHIFT = INPLACE_ADD INPLACE_MODULO = INPLACE_ADD INPLACE_MULTIPLY = INPLACE_ADD INPLACE_OR = INPLACE_ADD INPLACE_POWER = INPLACE_ADD INPLACE_RSHIFT = INPLACE_ADD INPLACE_SUBTRACT = INPLACE_ADD INPLACE_XOR = INPLACE_ADD def JUMP_ABSOLUTE(self, code): code.ReadOpcode('JUMP_ABSOLUTE') code.ReadOperand() self.addline(code.GetLine(), 'continue') def JUMP_IF_FALSE(self, code): code.ReadOpcode('JUMP_IF_FALSE') leap = code.ReadOperand() endcond = code.GetPosition() + leap opcode = code.ReadOpcode('POP_TOP') assert opcode == 'POP_TOP', `opcode` lineno = code.GetLine() code.PushStop(endcond) d = Decompiler(self.version) if self.loop is None: d.decompile(code, 'JUMP_FORWARD') else: d.decompile(code, 'JUMP_ABSOLUTE') code.PopStop() stack = d.getstack() if stack: if len(stack) == 1: assert code.GetPosition() == endcond # and x = self.stack.pop().GetString(PRECEDENCE_AND+1) y = stack.pop().GetString(PRECEDENCE_AND) self.stack.append( Expression('%s and %s' % (x, y), PRECEDENCE_AND)) else: # assert self.stack.pop() test = stack.pop().GetString(PRECEDENCE_ARG) value = stack.pop() if value is None: self.addline(lineno, 'assert %s' % test) else: value = value.GetString(PRECEDENCE_ARG) self.addline(lineno, 'assert %s, %s' % (test, value)) code.ReadOpcode('POP_TOP') else: condition = self.stack.pop() body = d.getsource(1) if self.loop is None: # if self.addclause(lineno, 'if %s:' % condition, body) code.ReadOpcode('JUMP_FORWARD') leap = code.ReadOperand() end = code.GetPosition() + leap code.ReadOpcode('POP_TOP') while code.GetPosition() < end: lineno = code.GetLine() code.PushStop(end) d = Decompiler(self.version) d.decompile(code, 'JUMP_FORWARD') code.PopStop() body = d.getsource(1) if body.has_key(lineno): if body[lineno][-1] == ':': # elif body = d.getsource(0) body[lineno] = 'el' + body[lineno] self.lines.update(body) else: #assert len(body) == 1, `body` line = body[lineno] self.addline(lineno, "else: %s" % string.strip(line)) del body[lineno] self.lines.update(body) body[lineno] = line else: self.addline(lineno, "else:") self.lines.update(body) code.SetLine(max(body.keys()) + 1) else: # while self.addclause(lineno, "while %s:" % condition, body) code.ReadOpcode('JUMP_ABSOLUTE') oparg = code.ReadOperand() assert oparg == self.loop[0], `(oparg, self.loop)` code.ReadOpcode('POP_TOP') code.ReadOpcode('POP_BLOCK') end = self.loop[1] self.loop = None if code.GetPosition() < end: lineno = code.GetLine() code.PushStop(end) d = Decompiler(self.version) d.decompile(code) code.PopStop() self.addclause(lineno, "else:", d.getsource(1)) assert code.GetPosition() == end def JUMP_IF_TRUE(self, code): code.ReadOpcode('JUMP_IF_TRUE') leap = code.ReadOperand() end = code.GetPosition() + leap code.ReadOpcode('POP_TOP') code.PushStop(end) d = Decompiler(self.version) d.decompile(code, 'RAISE_VARARGS') code.PopStop() stack = d.getstack() assert stack if code.GetPosition() == end: # or expression x = self.stack.pop().GetString(PRECEDENCE_OR+1) y = stack.pop().GetString(PRECEDENCE_OR) self.stack.append(Expression('%s or %s' % (x, y), PRECEDENCE_OR)) else: # raise AssertionError, exp test = self.stack.pop() code.ReadOpcode('RAISE_VARARGS') oparg = code.ReadOperand() if oparg == 1: self.stack.append(None) else: value = stack.pop() self.stack.append(value) self.stack.append(test) assert len(self.stack) == 2, `self.stack` assert code.GetPosition() == end def LOAD_ATTR(self, code): code.ReadOpcode('LOAD_ATTR') oparg = code.ReadOperand() attr = code.GetName(oparg) x = self.stack.pop() if x.Precedence() < PRECEDENCE_ATOM: x = '(%s)' % x self.stack.append(Expression('%s.%s' % (x, attr), PRECEDENCE_ATOM)) def LOAD_CONST(self, code): code.ReadOpcode('LOAD_CONST') oparg = code.ReadOperand() self.stack.append(Constant(code.GetConstant(oparg))) def LOAD_FAST(self, code): code.ReadOpcode('LOAD_FAST') oparg = code.ReadOperand() self.stack.append(Local(code.GetLocal(oparg))) def LOAD_GLOBAL(self, code): code.ReadOpcode('LOAD_GLOBAL') oparg = code.ReadOperand() self.stack.append(Global(code.GetName(oparg))) def LOAD_LOCALS(self, code): code.ReadOpcode('LOAD_LOCALS') self.stack.append(Constant(None)) def LOAD_NAME(self, code): code.ReadOpcode('LOAD_NAME') oparg = code.ReadOperand() self.stack.append(Local(code.GetName(oparg))) def MAKE_FUNCTION(self, code): code.ReadOpcode('MAKE_FUNCTION') defaultcount = code.ReadOperand() co = self.stack.pop().Value() if co.co_name == '': # lambda # Get the function def part params = [] argcount = co.co_argcount while argcount: argcount = argcount - 1 name = co.co_varnames[argcount] if defaultcount: defaultcount = defaultcount - 1 default = self.stack.pop().GetString(PRECEDENCE_ARG) params.append('%s=%s' % (name, default)) else: params.append(name) params.reverse() argcount = co.co_argcount if co.co_flags & VARARGS: params.append('*' + co.co_varnames[argcount]) argcount = argcount + 1 if co.co_flags & KWARGS: params.append('**' + co.co_varnames[argcount]) paramlist = string.join(params, ', ') # get the function body d = Decompiler(self.version) d.decompile(CodeCursor(co), 'RETURN_VALUE') stack = d.getstack() assert len(stack) == 1, `stack` y = stack.pop().GetString(PRECEDENCE_LAMBDA) self.stack.append( Expression('lambda %s: %s' % (paramlist, y), PRECEDENCE_LAMBDA)) else: opcode = code.ReadOpcode('CALL_FUNCTION', 'STORE_FAST', 'STORE_NAME') if opcode == 'CALL_FUNCTION': # class oparg = code.ReadOperand() assert oparg == 0, `oparg` code.ReadOpcode('BUILD_CLASS') super = self.stack.pop().Value() name = self.stack.pop().Value() opcode = code.ReadOpcode('STORE_FAST', 'STORE_NAME') oparg = code.ReadOperand() if opcode == 'STORE_FAST': classname = code.GetLocal(oparg) else: classname = code.GetName(oparg) assert name == classname, `name, classname` if super: classname = '%s(%s)' % (classname, string.join(super, ', ')) lineno = code.GetLine() d = Decompiler(self.version) d.decompile(CodeCursor(co)) body = d.getsource(1) if body.has_key(lineno): if len(body) == 1: self.addline(lineno, "class %s: %s" % (classname, string.strip(body[lineno]))) else: assert 0 # __doc__ string appears in 0th row assert not body.has_key(lineno+1), `body` body[lineno+1] = body[lineno] del body[lineno] self.addline(lineno, "class %s:" % classname) self.lines.update(body) else: self.addline(lineno, "class %s:" % classname) self.lines.update(body) code.SetLine(max(body.keys()) + 1) else: assert opcode in ('STORE_FAST', 'STORE_NAME'), `opcode` # def oparg = code.ReadOperand() if opcode == 'STORE_FAST': funcname = code.GetLocal(oparg) else: funcname = code.GetName(oparg) # Get the function def part params = [] argcount = co.co_argcount while argcount: argcount = argcount - 1 name = co.co_varnames[argcount] if defaultcount: defaultcount = defaultcount - 1 default = self.stack.pop() if default.Precedence() < PRECEDENCE_ARG: default = '(%s)' % default params.append('%s=%s' % (name, default)) else: params.append(name) params.reverse() argcount = co.co_argcount if co.co_flags & VARARGS: params.append('*' + co.co_varnames[argcount]) argcount = argcount + 1 if co.co_flags & KWARGS: params.append('**' + co.co_varnames[argcount]) paramlist = string.join(params, ', ') head = "def %s(%s):" % (funcname, paramlist) # get the function body lineno = code.GetLine() d = Decompiler(self.version) d.decompile(CodeCursor(co)) self.addclause(lineno, head, d.getsource(1)) def PRINT_ITEM(self, code): code.ReadOpcode('PRINT_ITEM') x = self.stack.pop().GetString(PRECEDENCE_ARG) if code.NextOpcode() == 'PRINT_NEWLINE': code.ReadOpcode('PRINT_NEWLINE') self.addline(code.GetLine(), 'print %s' % x) else: self.addline(code.GetLine(), 'print %s,' % x) def PRINT_ITEM_TO(self, code): # XXX - if file is an expression, it gets evaluated multiple times. code.ReadOpcode('PRINT_ITEM_TO') file = self.stack.pop() x = self.stack.pop().GetString(PRECEDENCE_ARG) if code.NextOpcode() == 'PRINT_NEWLINE_TO' and self.stack[-1] is file: code.ReadOpcode('PRINT_NEWLINE_TO') self.stack.pop() if file.Precedence() < PRECEDENCE_ARG: file = '(%s)' % file self.addline('print >> %s, %s' % (file, x)) else: if file.Precedence() < PRECEDENCE_ARG: file = '(%s)' % file self.addline('print >> %s, %s,' % (file, x)) def PRINT_NEWLINE(self, code): code.ReadOpcode('PRINT_NEWLINE') self.addline(code.GetLine(), 'print') def PRINT_NEWLINE_TO(self, code): code.ReadOpcode('PRINT_NEWLINE') file = self.stack.pop().GetString(PRECEDENCE_ARG) self.addline(code.GetLine(), 'print >> %s' % file) def POP_TOP(self, code): code.ReadOpcode('POP_TOP') self.addline(code.GetLine(), self.stack.pop().GetString(PRECEDENCE_NONE)) def RAISE_VARARGS(self, code): code.ReadOpcode('RAISE_VARARGS') argcount = code.ReadOperand() args = [] for i in range(argcount): arg = self.stack.pop().GetString(PRECEDENCE_ARG) args.append(arg) args.reverse() self.addline(code.GetLine(), 'raise %s' % string.join(args, ', ')) def RETURN_VALUE(self, code): code.ReadOpcode() y = self.stack.pop() if isinstance(y, Constant) and y.Value() is None: if not code.AtEnd(): self.addline(code.GetLine(), 'return') else: value = y.GetString(PRECEDENCE_NONE) self.addline(code.GetLine(), 'return %s' % value) def ROT_THREE(self, code): code.ReadOpcode('ROT_THREE') assert len(self.stack) >= 3, `code.GetPosition(), self.stack` self.stack.pop() # duplicate of y y = self.stack.pop().GetString(PRECEDENCE_CMP+1) x = self.stack.pop().GetString(PRECEDENCE_CMP+1) code.ReadOpcode('COMPARE_OP') oparg = code.ReadOperand() op = dis.cmp_op[oparg] chain = '%s %s %s' % (x, op, y) opcode = code.ReadOpcode('JUMP_IF_FALSE') leap = code.ReadOperand() stop1 = code.GetPosition() + leap while opcode == 'JUMP_IF_FALSE': assert code.GetPosition() + leap == stop1, \ `code.GetPosition(), leap, stop1` code.ReadOpcode('POP_TOP') code.PushStop(stop1 - 6) d = Decompiler(self.version) d.decompile(code, 'ROT_THREE') code.PopStop() stack = d.getstack() y = stack.pop().GetString(PRECEDENCE_CMP+1) opcode = code.ReadOpcode('COMPARE_OP', 'ROT_THREE') if opcode == 'ROT_THREE': opcode = code.ReadOpcode('COMPARE_OP') oparg = code.ReadOperand() op = dis.cmp_op[oparg] chain = '%s %s %s' % (chain, op, y) opcode = code.ReadOpcode('JUMP_IF_FALSE', 'JUMP_FORWARD') leap = code.ReadOperand() assert leap == 2, `leap` assert code.GetPosition() == stop1, `code.GetPosition(), stop1` code.ReadOpcode('ROT_TWO') code.ReadOpcode('POP_TOP') self.stack.append(Expression(chain, PRECEDENCE_CMP)) def ROT_TWO(self, code): code.ReadOpcode('ROT_TWO') n1 = self.stack.pop() n2 = self.stack.pop() self.stack.append(n1) self.stack.append(n2) def handle_except_clause(self, code): opcode = code.ReadOpcode('DUP_TOP', 'POP_TOP', 'SET_LINENO') if opcode == 'SET_LINENO': code.ReadOperand() opcode = code.ReadOpcode('DUP_TOP', 'POP_TOP') lineno = code.GetLine() if opcode == 'DUP_TOP': d = Decompiler(self.version) d.decompile(code, 'COMPARE_OP') stack = d.getstack() exc_type = stack.pop().GetString(PRECEDENCE_ARG) code.ReadOpcode('COMPARE_OP') oparg = code.ReadOperand() assert oparg == 10, `oparg` # 10 -> exception match code.ReadOpcode('JUMP_IF_FALSE') leap = code.ReadOperand() nextclause = code.GetPosition() + leap code.ReadOpcode('POP_TOP') # result of test code.ReadOpcode('POP_TOP') # exc_type opcode = code.NextOpcode() if opcode == 'POP_TOP': # exc_value code.ReadOpcode('POP_TOP') head = 'except %s:' % exc_type else: exc_value = self.build_target(code).GetString(PRECEDENCE_ARG) head = 'except %s, %s:' % (exc_type, exc_value) else: code.ReadOpcode('POP_TOP') # exc_value head = 'except:' nextclause = None code.ReadOpcode('POP_TOP') # exc_tb d = Decompiler(self.version) d.decompile(code, 'JUMP_FORWARD') self.addclause(lineno, head, d.getsource(1)) code.ReadOpcode('JUMP_FORWARD') leap = code.ReadOperand() end = code.GetPosition() + leap if nextclause is not None: assert code.GetPosition() == nextclause code.ReadOpcode('POP_TOP') return end def SETUP_EXCEPT(self, code): code.ReadOpcode('SETUP_EXCEPT') leap = code.ReadOperand() firstexceptclause = code.GetPosition() + leap lineno = code.GetLine() d = Decompiler(self.version) d.decompile(code, 'POP_BLOCK') self.addclause(lineno, "try:", d.getsource(1)) code.ReadOpcode('POP_BLOCK') code.ReadOpcode('JUMP_FORWARD') leap = code.ReadOperand() elseclause = code.GetPosition() + leap assert code.GetPosition() == firstexceptclause end = self.handle_except_clause(code) while code.NextOpcode() != 'END_FINALLY': end1 = self.handle_except_clause(code) assert end1 == end, `end1, end` code.ReadOpcode('END_FINALLY') assert code.GetPosition() == elseclause, \ `code.GetPosition(), elseclause` if elseclause < end: lineno = code.GetLine() code.PushStop(end) d = Decompiler(self.version) d.decompile(code) code.PopStop() self.addclause(lineno, "else:", d.getsource(1)) assert code.GetPosition() == end def SETUP_FINALLY(self, code): code.ReadOpcode('SETUP_FINALLY') leap = code.ReadOperand() finallyclause = code.GetPosition() + leap lineno = code.GetLine() d = Decompiler(self.version) d.decompile(code, 'POP_BLOCK') body = d.getsource(1) self.addclause(lineno, "try:", body) code.ReadOpcode('POP_BLOCK') code.ReadOpcode('LOAD_CONST') oparg = code.ReadOperand() assert oparg == 0, `oparg` assert code.GetPosition() == finallyclause lineno = code.GetLine() d = Decompiler(self.version) d.decompile(code, 'END_FINALLY') body = d.getsource(1) self.addclause(lineno, "finally:", body) code.ReadOpcode('END_FINALLY') def SETUP_LOOP(self, code): code.ReadOpcode('SETUP_LOOP') leap = code.ReadOperand() assert self.loop is None, `self.loop` i = code.GetPosition() self.loop = i, i + leap def SLICE_0(self, code): code.ReadOpcode() x = self.stack.pop().GetString(PRECEDENCE_ATOM) self.stack.append(Expression('%s[:]' % x, PRECEDENCE_ATOM)) def SLICE_1(self, code): code.ReadOpcode() y = self.stack.pop().GetString(PRECEDENCE_ARG) x = self.stack.pop().GetString(PRECEDENCE_ATOM) self.stack.append(Expression('%s[%s:]' % (x, y), PRECEDENCE_ATOM)) def SLICE_2(self, code): code.ReadOpcode() z = self.stack.pop().GetString(PRECEDENCE_ARG) x = self.stack.pop().GetString(PRECEDENCE_ATOM) self.stack.append(Expression('%s[:%s]' % (x, z), PRECEDENCE_ATOM)) def SLICE_3(self, code): code.ReadOpcode() z = self.stack.pop().GetString(PRECEDENCE_ARG) y = self.stack.pop().GetString(PRECEDENCE_ARG) x = self.stack.pop().GetString(PRECEDENCE_ATOM) self.stack.append(Expression('%s[%s:%s]' % (x, y, z), PRECEDENCE_ATOM)) def STORE_ATTR(self, code): code.ReadOpcode() oparg = code.ReadOperand() attr = code.GetName(oparg) name = self.stack.pop().GetString(PRECEDENCE_ATOM) value = self.stack.pop().GetString(PRECEDENCE_NONE) self.addline(code.GetLine(), '%s.%s = %s' % (name, attr, value)) def STORE_FAST(self, code): code.ReadOpcode() oparg = code.ReadOperand() name = code.GetLocal(oparg) value = self.stack.pop().GetString(PRECEDENCE_NONE) self.addline(code.GetLine(), '%s = %s' % (name, value)) def STORE_GLOBAL(self, code): # XXX - need to put in global statement code.ReadOpcode() oparg = code.ReadOperand() name = code.GetName(oparg) value = self.stack.pop().GetString(PRECEDENCE_NONE) self.addline(code.GetLine(), '%s = %s' % (name, value)) def STORE_NAME(self, code): code.ReadOpcode() oparg = code.ReadOperand() name = code.GetName(oparg) value = self.stack.pop().GetString(PRECEDENCE_NONE) self.addline(code.GetLine(), '%s = %s' % (name, value)) def STORE_SLICE_0(self, code): code.ReadOpcode() x = self.stack.pop().GetString(PRECEDENCE_ATOM) value = self.stack.pop().GetString(PRECEDENCE_NONE) self.addline(code.GetLine(), '%s[:] = %s' % (x, value)) def STORE_SLICE_1(self, code): code.ReadOpcode() y = self.stack.pop().GetString(PRECEDENCE_ARG) x = self.stack.pop().GetString(PRECEDENCE_ATOM) value = self.stack.pop().GetString(PRECEDENCE_NONE) self.addline(code.GetLine(), '%s[%s:] = %s' % (x, y, value)) def STORE_SLICE_2(self, code): code.ReadOpcode() z = self.stack.pop().GetString(PRECEDENCE_ARG) x = self.stack.pop().GetString(PRECEDENCE_ATOM) value = self.stack.pop().GetString(PRECEDENCE_NONE) self.addline(code.GetLine(), '%s[:%s] = %s' % (x, z, value)) def STORE_SLICE_3(self, code): code.ReadOpcode() z = self.stack.pop().GetString(PRECEDENCE_ARG) y = self.stack.pop().GetString(PRECEDENCE_ARG) x = self.stack.pop().GetString(PRECEDENCE_ATOM) value = self.stack.pop().GetString(PRECEDENCE_NONE) self.addline(code.GetLine(), '%s[%s:%s] = %s' % (x, y, z, value)) def STORE_SUBSCR(self, code): code.ReadOpcode() key = self.stack.pop() obj = self.stack.pop() if isinstance(obj, Map): value = self.stack.pop().GetString(PRECEDENCE_ARG) if key.Precedence() < PRECEDENCE_ARG: key = '(%s)' % key obj.SetAttr(key, value) else: obj = obj.GetString(PRECEDENCE_ATOM) value = self.stack.pop().GetString(PRECEDENCE_NONE) self.addline(code.GetLine(), '%s[%s] = %s' % (obj, key, value)) def UNARY_CONVERT(self, code): code.ReadOpcode() value = self.stack.pop().GetString(PRECEDENCE_NONE) self.stack.append(Expression('`%s`' % value, PRECEDENCE_ATOM)) def UNARY_INVERT(self, code): code.ReadOpcode() # only requires PRECEDENCE_UNARY, but both powers and other # unary operators are confusing without parentheses y = self.stack.pop().GetString(PRECEDENCE_ATOM) self.stack.append(Expression('~%s' % y, PRECEDENCE_UNARY)) def UNARY_NEGATIVE(self, code): code.ReadOpcode() # only requires PRECEDENCE_UNARY, but both powers and other # unary operators are confusing without parentheses y = self.stack.pop().GetString(PRECEDENCE_ATOM) self.stack.append(Expression('-%s' % y, PRECEDENCE_UNARY)) def UNARY_NOT(self, code): code.ReadOpcode() y = self.stack.pop().GetString(PRECEDENCE_NOT) self.stack.append(Expression('not %s' % y, PRECEDENCE_NOT)) def UNARY_POSITIVE(self, code): code.ReadOpcode() # only requires PRECEDENCE_UNARY, but both powers and other # unary operators are confusing without parentheses y = self.stack.pop().GetString(PRECEDENCE_ATOM) self.stack.append(Expression('+%s' % y, PRECEDENCE_UNARY)) def build_target(self, code): if code.NextOpcode() not in ('STORE_FAST', 'STORE_GLOBAL', 'STORE_NAME', 'UNPACK_SEQUENCE', 'UNPACK_TUPLE'): d = Decompiler(self.version) d.decompile(code, 'STORE_ATTR', 'STORE_SLICE+0', 'STORE_SLICE+1',\ 'STORE_SLICE+2', 'STORE_SLICE+3', 'STORE_SUBSCR') opcode = code.ReadOpcode() if opcode == 'STORE_ATTR': stack = d.getstack() assert len(stack) == 1, `stack` name = stack.pop().GetString(PRECEDENCE_ATOM) oparg = code.ReadOperand() attr = code.GetName(oparg) target = Expression('%s.%s' % (name, attr), PRECEDENCE_ATOM) elif opcode == 'STORE_FAST': oparg = code.ReadOperand() target = Local(code.GetLocal(oparg)) elif opcode == 'STORE_GLOBAL': oparg = code.ReadOperand() target = Global(code.GetName(oparg)) elif opcode == 'STORE_NAME': oparg = code.ReadOperand() target = Local(code.GetName(oparg)) elif opcode == 'STORE_SLICE+0': stack = d.getstack() assert len(stack) == 1, `stack` x = stack.pop().GetString(PRECEDENCE_ATOM) target = Expression('%s[:]' % x, PRECEDENCE_ATOM) elif opcode == 'STORE_SLICE+1': stack = d.getstack() assert len(stack) == 2, `stack` y = stack.pop().GetString(PRECEDENCE_ARG) x = stack.pop().GetString(PRECEDENCE_ATOM) target = Expression('%s[%s:]' % (x, y), PRECEDENCE_ATOM) elif opcode == 'STORE_SLICE+2': stack = d.getstack() assert len(stack) == 2, `stack` z = stack.pop().GetString(PRECEDENCE_ARG) x = stack.pop().GetString(PRECEDENCE_ATOM) target = Expression('%s[:%s]' % (x, z), PRECEDENCE_ATOM) elif opcode == 'STORE_SLICE+3': stack = d.getstack() assert len(stack) == 3, `stack` z = stack.pop().GetString(PRECEDENCE_ARG) y = stack.pop().GetString(PRECEDENCE_ARG) x = stack.pop().GetString(PRECEDENCE_ATOM) target = Expression('%s[%s:%s]' % (x, y, z), PRECEDENCE_ATOM) elif opcode == 'STORE_SUBSCR': stack = d.getstack() assert len(stack) == 2, `stack` key = stack.pop().GetString(PRECEDENCE_NONE) name = stack.pop().GetString(PRECEDENCE_ATOM) target = Expression('%s[%s]' % (name, key), PRECEDENCE_ATOM) else: assert opcode in ('UNPACK_SEQUENCE', 'UNPACK_TUPLE'), `opcode` count = code.ReadOperand() values = [] while count > 0: value = self.build_target(code).GetString(PRECEDENCE_ARG) values.append(value) count = count - 1 target = Tuple(values) return target def UNPACK_SEQUENCE(self, code): seq = self.build_target(code).GetString(PRECEDENCE_NONE) rhs = self.stack.pop().GetString(PRECEDENCE_NONE) self.addline(code.GetLine(), '%s = %s' % (seq, rhs)) UNPACK_TUPLE = UNPACK_SEQUENCE # These tests need to be more complete, however, the things that are # known to be broken are represented tests = [ '3\n' # constant expression 'a\n' # name expression 'a[3][4]\n' # subscr expression 'a.b.c\n' # attr expression 'a(b, c, d=3, e="d")\n', # function call 'a = b\n', # simple assignment 'a.b.c.d = 2\n', # attr target 'a[b][c][d] = 3\n', # subscr target 'a[:] = 4\n', # slice targets 'a[b:] = 5\n', 'a[:b] = 6\n', 'a[b:c] = 7\n', 'a, b, c.d, e[f], g[:], h[i:], j[:k], l[m:n] = z\n', # tuple target '(a, b), (c, (d, e)) = t\n', # nested tuple target 'import spam', 'import string, spam as ham, sys', 'from spam import ham, eggs', 'import spam.ham', # BROKEN: this type of import 'from package.spam import ham, eggs as milk', 'from string import *', 'def f(a, (b, c)):\n del f\n', # BROKEN: tuple func args 'a = b.d = c[3] = a, b = f()', # BROKEN: chained assignment 'print >> file, a, b', 'a = [2*i for i in x]', # BROKEN: list comprehension 'if 1:\n pass\nelif 2: pass\nelse: 3', # BROKEN, swap conditions JUMP_IF_FALSE ## for i in r: ## print ## print hello, b ## a &= 3; b^=9 ## c|=8 ; d <<=2 ## def g(a, b, c=5, d=10, *args, **kw): ## pass ## class G(foo): ## def __init__(self, b=5): ## "hello" ## x.k = x.k & (7 * (3 + 4)) ## return x ## x, y[5][g+4], z.x = 1, 2, 3 ## f(a, b, x=1, **args) ## try: 1 ## finally: 2 ## if a < b*2 <= c > d: ## print ## x = lambda y: (2 * y, 3) ## class C: pass ## if 3: ## print ## elif 5: ## del e ## else: ## print ## import sys ## if 3: ## print ## else: ## i = j + -2 ** -3 ** -2 ## if stuff[0] == '!': ## stuff = '[^' + stuff[1:] + ']' ## elif stuff == 'g': ## stuff = '\\^' ## else: ## while stuff[0] == '^': ## stuff = stuff[1:] + stuff[0] ## stuff = ('[' + stuff + ']'), ## ## a, b, c.b, a[p] = c ## x = 1 - 2 + 3 - (4 + 5) - 6, 6 ## try: ## print 2 ## except RuntimeError, exc: ## del a ## except IOError: ## del g ## except: ## del b ## else: ## del elses ## if a and b and c: ## del a ## if (a and b) and c: ## del b ## if a and (b and c): ## del c ] def test(): import traceback for osrc in tests: code1 = compile(osrc, '', 'exec') d = Decompiler((2, 0)) try: d.decompile(CodeCursor(code1)) except: print osrc print 'FAILS' traceback.print_exc(file=sys.stdout) print '---' continue source = d.getsource(0) lastline = max(source.keys()) lines = [] for lineno in range(1, lastline+2): lines.append(source.get(lineno, '')) dsrc = string.join(lines, '\n') if dsrc == osrc: continue try: code2 = compile(dsrc, '', 'exec') except SyntaxError: code2 = None if code2 and code2.co_code == code1.co_code: continue print osrc print 'BECOMES' print dsrc print '---' def f(): a(b=3, d=3) a[x:], b[:] = 1 # don't work: ## import spam.ham ## def f(a, (b, c)): ## del f ## a = b.d = c[3] = a, b = f() ## a = [2*i for i in x] ## import string, x as y ## from spam.ham import eggs, foo as bar ## from d import * ## for i in r: ## print print hello, b ## a &= 3; b^=9 ## c|=8 ; d <<=2 ## def g(a, b, c=5, d=10, *args, **kw): ## pass ## class G(foo): ## def __init__(self, b=5): ## "hello" ## x.k = x.k & (7 * (3 + 4)) ## return x ## x, y[5][g+4], z.x = 1, 2, 3 ## f(a, b, x=1, **args) ## try: 1 ## finally: 2 ## if a < b*2 <= c > d: ## print ## x = lambda y: (2 * y, 3) ## class C: pass ## if 3: ## print ## elif 5: ## del e ## else: ## print ## import sys ## if 3: ## print ## else: ## i = j + -2 ** -3 ** -2 ## if stuff[0] == '!': ## stuff = '[^' + stuff[1:] + ']' ## elif stuff == 'g': ## stuff = '\\^' ## else: ## while stuff[0] == '^': ## stuff = stuff[1:] + stuff[0] ## stuff = ('[' + stuff + ']'), ## ## a, b, c.b, a[p] = c ## x = 1 - 2 + 3 - (4 + 5) - 6, 6 ## try: ## print 2 ## except RuntimeError, exc: ## del a ## except IOError: ## del g ## except: ## del b ## else: ## del elses ## if a and b and c: ## del a ## if (a and b) and c: ## del b ## if a and (b and c): ## del c if __name__ == '__main__': if 1: test() else: out = open(r'E:\output.txt', 'wt') try: stdout = sys.stdout sys.stdout = out if 1: import marshal m = open(r'C:\Python20\Lib\code.pyc', 'rb') magic = m.read(4) if magic == '\207\306\015\012': version = (2, 0) elif magic == '\231N\015\012': version = (1, 5, 2) else: raise RuntimeError, 'unrecognised magic: %s' % `magic` timestamp = m.read(4) s = m.read() code = marshal.loads(s) m.close() else: version = (2, 0) print f.func_code.co_names print f.func_code.co_varnames print f.func_code.co_consts code = f.func_code c = CodeCursor(code) d = Decompiler(version) d.decompile(c) lines = d.getsource(0) keys = lines.keys() keys.sort() for lineno in range(keys[0], keys[-1] + 1): print '%4d %s' % (lineno, lines.get(lineno, '')) print sys.stdout = stdout finally: out.close() sys.stderr.write('DONE\n')