def _PRINT_ITEM(self, cmd, prevcmd, nextcmd, stack, curIndent): r = '' if nextcmd is not None and nextcmd.mnemonics == 'PRINT_NEWLINE': r += ind(curIndent) + 'print ' + str(stack[-1]) + '\n' else: r += ind(curIndent) + 'print ' + str(stack[-1]) + ',\n' stack.pop().dec() return r
def _EXEC_STMT(self, cmd, prevcmd, nextcmd, stack, curIndent): r = '' if stack[-2].value is None: r += ind(curIndent) + 'exec ' + str(stack[-3]) + '\n' # TODO: validate stack[-2] == stack[-1] elif stack[-2] == stack[-1]: r += ind(curIndent) + 'exec ' + str(stack[-3]) + \ ' in ' + str(stack[-2]) + '\n' else: r += ind(curIndent) + 'exec ' + str(stack[-3]) + \ ' in ' + str(stack[-2]) + ', ' + str(stack[-1]) + '\n' stack.pop().dec() stack.pop().dec() stack.pop().dec() return r
def _DELETE_SLICE_3(self, cmd, prevcmd, nextcmd, stack, curIndent): r = ind(curIndent) + 'del ' + \ str(Slice3Op(stack[-3], stack[-2], stack[-1])) + '\n' stack.pop().dec() stack.pop().dec() stack.pop().dec() return r
def _POP_TOP(self, cmd, prevcmd, nextcmd, stack, curIndent): # TODO: tests/all_opcodes/prints.py fails becaues of POP_TOP # TODO: POP_TOP should process postponedStores... r = '' o = stack.pop() o.dec() if o.isLastCopy(): if isinstance(o, Import): level = '.' * o.level.value a = [] for f in o.froms: if f[0] != f[1]: a.append('%s as %s' % (f[0], f[1])) else: a.append(f[0]) r += ind(curIndent) + \ 'from %s%s import %s\n' % (level, o.module, ', '.join(a)) elif isinstance(o, DummyEx): pass elif not isinstance(o, CompareOp) and \ not isinstance(o, YieldedValue): r += ind(curIndent) + str(o) + '\n' return r
def _RETURN_VALUE(self, cmd, prevcmd, nextcmd, stack, curIndent): r = '' o = stack.pop() o.dec() noneInNames = False for x in self.co.names.value: if x.value == 'None': noneInNames = True break if self.co.name.value != '<module>' and \ not isinstance(o, DummyLocals) and \ (not isinstance(o, Constant) or o.value is not None or noneInNames): r += ind(curIndent) + 'return ' + str(o) + '\n' return r
def _INPLACE_TRUE_DIVIDE(self, cmd, prevcmd, nextcmd, stack, curIndent): r = ind(curIndent) + '# from __future__ import division\n' + \ ind(curIndent) + '# CAUTION! future division detected!\n' stack[-2] = InplaceDivisionOp(stack[-2], stack[-1]) stack.pop().dec() return r
def STORE(self, lvalue, rvalue, curIndent, emptystack): ''' This method handles all STORE_* commands. @param lvalue: destination, where data is stored. @param rvalue: source of data. @param curIndent: current indent in the generated source code. @param emptystack: should be True if no postponed stores are expected. ''' r = '' if isinstance(rvalue, NewFunction): r += ind(curIndent) + 'def ' + lvalue + '(' r += rvalue.getParams() r += '):\n' da = disasm.Disassembler(rvalue.value, optimizeJumps=True) x = Decompiler(da).decompile(startIndent=curIndent+1) #FixMe if x != None: if x in ('', '\n'): x = ind(curIndent + 1) + 'pass\n' r += x + '\n' elif isinstance(rvalue, NewClass): r += 'class ' + lvalue + '(' r += ', '.join(x.value for x in rvalue.baseclasses.value) r += '):\n' # offset=6 to avoid __module__ = __name__ duplication co = self.findCoByName(rvalue.classname.value) da = disasm.Disassembler(co, optimizeJumps=True) dc = Decompiler(da) x = dc.decompile(offset=6, startIndent=curIndent+1) #FixMe if x != None: x += '\n' if x in ('', '\n'): x = ind(curIndent + 1) + 'pass\n' r += x + '\n' elif isinstance(rvalue, Import): if rvalue.module.split('.')[0] != lvalue: # TODO: recheck if rvalue can be != self.co.names... here... r += ind(curIndent) + \ 'import %s as %s\n' % (rvalue.module, lvalue) else: r += ind(curIndent) + 'import %s\n' % rvalue.module elif isinstance(rvalue, ImportFrom): rvalue.importobj.addFrom(rvalue.name, lvalue) elif isinstance(rvalue, InplaceOp): storedIn = str(lvalue) + ' ' if storedIn == str(rvalue)[:len(storedIn)]: r += ind(curIndent) + str(rvalue) + '\n' else: r += ind(curIndent) + '# INPLACE_* op used not as INPLACE!!!\n' # TODO: possible error r += ind(curIndent) + str(lvalue) + ' = ' + \ rvalue.children[0] + ' ' + rvalue.value + \ ' ' + rvalue.children[1] + '\n' elif isinstance(rvalue, Iterator): r += ind(curIndent) + 'for ' + str(lvalue) + ' in ' + \ str(rvalue) + ':\n' elif isinstance(rvalue, DummyEx): # quite a hack here... take a look at mergeCompoundNodes # for more info r += '# as ' + str(lvalue) + '\n' else: if not emptystack: self.postponedStores.append((lvalue, rvalue)) else: if len(self.postponedStores) == 0: r += ind(curIndent) + str(lvalue) + ' = ' + \ str(rvalue) + '\n' else: self.postponedStores.append((lvalue, rvalue)) r += ind(curIndent) + '(' + \ ', '.join(str(x[0]) for x in self.postponedStores) + \ ') = (' + \ ', '.join(str(x[1]) for x in self.postponedStores) + \ ')\n' self.postponedStores = [] return r
class Decompiler: '''Decompiler itself.''' def __init__(self, disassembler, debugDraw=False): ''' @param disassembler: L{Disassembler} to get command list, code blocks and such. @param debugDraw: if true, intermediate CFGs are saved. See L{structure} for more details. ''' self.disassembler = disassembler self.debugDraw = debugDraw self.co = disassembler.co self.postponedStores = [] #RICH TEMP self.richy_count = 0 def findCoByName(self, name): ''' @return: code object with a given name. ''' for x in self.co.consts.value: if isinstance(x, parse.pyCode) and x.name.value == name: return x return None @staticmethod def checkStack(stack, depth): ''' @return: True if stack is of given depth. Instances of DummyEx are ignored. ''' counter = 0 for x in stack: if not isinstance(x, DummyEx): counter += 1 return counter == depth def STORE(self, lvalue, rvalue, curIndent, emptystack): ''' This method handles all STORE_* commands. @param lvalue: destination, where data is stored. @param rvalue: source of data. @param curIndent: current indent in the generated source code. @param emptystack: should be True if no postponed stores are expected. ''' r = '' if isinstance(rvalue, NewFunction): r += ind(curIndent) + 'def ' + lvalue + '(' r += rvalue.getParams() r += '):\n' da = disasm.Disassembler(rvalue.value, optimizeJumps=True) x = Decompiler(da).decompile(startIndent=curIndent + 1) #FixMe if x != None: if x in ('', '\n'): x = ind(curIndent + 1) + 'pass\n' r += x + '\n' elif isinstance(rvalue, NewClass): r += 'class ' + lvalue + '(' r += ', '.join(x.value for x in rvalue.baseclasses.value) r += '):\n' # offset=6 to avoid __module__ = __name__ duplication co = self.findCoByName(rvalue.classname.value) da = disasm.Disassembler(co, optimizeJumps=True) dc = Decompiler(da) x = dc.decompile(offset=6, startIndent=curIndent + 1) #FixMe if x != None: x += '\n' if x in ('', '\n'): x = ind(curIndent + 1) + 'pass\n' r += x + '\n' elif isinstance(rvalue, Import): if rvalue.module.split('.')[0] != lvalue: # TODO: recheck if rvalue can be != self.co.names... here... r += ind(curIndent) + \ 'import %s as %s\n' % (rvalue.module, lvalue) else: r += ind(curIndent) + 'import %s\n' % rvalue.module elif isinstance(rvalue, ImportFrom): rvalue.importobj.addFrom(rvalue.name, lvalue) elif isinstance(rvalue, InplaceOp): storedIn = str(lvalue) + ' ' if storedIn == str(rvalue)[:len(storedIn)]: r += ind(curIndent) + str(rvalue) + '\n' else: r += ind(curIndent) + '# INPLACE_* op used not as INPLACE!!!\n' # TODO: possible error r += ind(curIndent) + str(lvalue) + ' = ' + \ rvalue.children[0] + ' ' + rvalue.value + \ ' ' + rvalue.children[1] + '\n' elif isinstance(rvalue, Iterator): r += ind(curIndent) + 'for ' + str(lvalue) + ' in ' + \ str(rvalue) + ':\n' elif isinstance(rvalue, DummyEx): # quite a hack here... take a look at mergeCompoundNodes # for more info r += '# as ' + str(lvalue) + '\n' else: if not emptystack: self.postponedStores.append((lvalue, rvalue)) else: if len(self.postponedStores) == 0: r += ind(curIndent) + str(lvalue) + ' = ' + \ str(rvalue) + '\n' else: self.postponedStores.append((lvalue, rvalue)) r += ind(curIndent) + '(' + \ ', '.join(str(x[0]) for x in self.postponedStores) + \ ') = (' + \ ', '.join(str(x[1]) for x in self.postponedStores) + \ ')\n' self.postponedStores = [] return r #################################### # actions for commands in bytecode # #################################### def _STOP_CODE(self, cmd, prevcmd, nextcmd, stack, curIndent): pass def _POP_TOP(self, cmd, prevcmd, nextcmd, stack, curIndent): # TODO: tests/all_opcodes/prints.py fails becaues of POP_TOP # TODO: POP_TOP should process postponedStores... r = '' ## XXX Rich try: o = stack.pop() except IndexError, err: print "XXXX: pop from empty stack caught _POP_TOP, returning: ", r return r o.dec() if o.isLastCopy(): if isinstance(o, Import): level = '.' * o.level.value a = [] for f in o.froms: if f[0] != f[1]: a.append('%s as %s' % (f[0], f[1])) else: a.append(f[0]) r += ind(curIndent) + \ 'from %s%s import %s\n' % (level, o.module, ', '.join(a)) elif isinstance(o, DummyEx): pass elif not isinstance(o, CompareOp) and \ not isinstance(o, YieldedValue): r += ind(curIndent) + str(o) + '\n' return r
def _PRINT_NEWLINE_TO(self, cmd, prevcmd, nextcmd, stack, curIndent): r = '' if prevcmd is None or prevcmd.mnemonics != 'PRINT_ITEM_TO': r += ind(curIndent) + 'print >> ' + str(stack[-1]) + '\n' stack.pop().dec() return r
def _BREAK_LOOP(self, cmd, prevcmd, nextcmd, stack, curIndent): # TODO: recheck BREAK_LOOP return ind(curIndent) + 'break\n'
def _IMPORT_STAR(self, cmd, prevcmd, nextcmd, stack, curIndent): r = ind(curIndent) + 'from ' + stack[-1].module + ' import *\n' stack.pop().dec() return r
def _DELETE_ATTR(self, cmd, prevcmd, nextcmd, stack, curIndent): v = Variable(self.co.names.value[cmd.argument].value) stack[-1] = AttributeOp(stack[-1], v) r = ind(curIndent) + 'del ' + str(stack[-1]) + '\n' stack.pop().dec() return r
def _PRINT_NEWLINE(self, cmd, prevcmd, nextcmd, stack, curIndent): r = '' if prevcmd is None or prevcmd.mnemonics != 'PRINT_ITEM': r += ind(curIndent) + 'print\n' return r
def _YIELD_VALUE(self, cmd, prevcmd, nextcmd, stack, curIndent): # TODO: check weather POP_TOP always follows YIELD_VALUE r = ind(curIndent) + 'yield ' + str(stack[-1]) + '\n' stack[-1] = YieldedValue(stack[-1]) return r
def _DELETE_NAME(self, cmd, prevcmd, nextcmd, stack, curIndent): r = ind(curIndent) + 'del ' + \ self.co.names.value[cmd.argument].value.__str__() + '\n' return r
def _DELETE_SUBSCR(self, cmd, prevcmd, nextcmd, stack, curIndent): r = ind(curIndent) + 'del ' + \ str(SubscriptionOp(stack[-2], stack[-1])) + '\n' stack.pop().dec() stack.pop().dec() return r
def _JUMP_IF_TRUE(self, cmd, prevcmd, nextcmd, stack, curIndent): return ind(curIndent) + str(stack[-1]) + '\n'
try: o = stack.pop() except IndexError, err: print "XXXX: pop from empty stack caught, returning: ",r return r o.dec() noneInNames = False for x in self.co.names.value: if x.value == 'None': noneInNames = True break if self.co.name.value != '<module>' and \ not isinstance(o, DummyLocals) and \ (not isinstance(o, Constant) or o.value is not None or noneInNames): r += ind(curIndent) + 'return ' + str(o) + '\n' return r def _IMPORT_STAR(self, cmd, prevcmd, nextcmd, stack, curIndent): r = ind(curIndent) + 'from ' + stack[-1].module + ' import *\n' stack.pop().dec() return r def _EXEC_STMT(self, cmd, prevcmd, nextcmd, stack, curIndent): r = '' if stack[-2].value is None: r += ind(curIndent) + 'exec ' + str(stack[-3]) + '\n' # TODO: validate stack[-2] == stack[-1] elif stack[-2] == stack[-1]: r += ind(curIndent) + 'exec ' + str(stack[-3]) + \ ' in ' + str(stack[-2]) + '\n'
def _DELETE_FAST(self, cmd, prevcmd, nextcmd, stack, curIndent): # TODO: recheck __str__ or __repr__?? value = self.co.varnames.value[cmd.argument].value r = ind(curIndent) + 'del ' + str(value) + '\n' return r
def _JUMP_IF_FALSE(self, cmd, prevcmd, nextcmd, stack, curIndent): # TODO: JUMP_IF_FALSE return ind(curIndent) + str(stack[-1]) + '\n'
def _CONTINUE_LOOP(self, cmd, prevcmd, nextcmd, stack, curIndent): return ind(curIndent) + 'continue\n'
def STORE(self, lvalue, rvalue, curIndent, emptystack): ''' This method handles all STORE_* commands. @param lvalue: destination, where data is stored. @param rvalue: source of data. @param curIndent: current indent in the generated source code. @param emptystack: should be True if no postponed stores are expected. ''' r = '' if isinstance(rvalue, NewFunction): r += ind(curIndent) + 'def ' + lvalue + '(' r += rvalue.getParams() r += '):\n' da = disasm.Disassembler(rvalue.value, optimizeJumps=True) x = Decompiler(da).decompile(startIndent=curIndent + 1) if x in ('', '\n'): x = ind(curIndent + 1) + 'pass\n' r += x + '\n' elif isinstance(rvalue, NewClass): r += 'class ' + lvalue + '(' r += ', '.join(x.value for x in rvalue.baseclasses.value) r += '):\n' # offset=6 to avoid __module__ = __name__ duplication co = self.findCoByName(rvalue.classname.value) da = disasm.Disassembler(co, optimizeJumps=True) dc = Decompiler(da) x = dc.decompile(offset=6, startIndent=curIndent + 1) + '\n' if x in ('', '\n'): x = ind(curIndent + 1) + 'pass\n' r += x + '\n' elif isinstance(rvalue, Import): if rvalue.module.split('.')[0] != lvalue: # TODO: recheck if rvalue can be != self.co.names... here... r += ind(curIndent) + \ 'import %s as %s\n' % (rvalue.module, lvalue) else: r += ind(curIndent) + 'import %s\n' % rvalue.module elif isinstance(rvalue, ImportFrom): rvalue.importobj.addFrom(rvalue.name, lvalue) elif isinstance(rvalue, InplaceOp): storedIn = str(lvalue) + ' ' if storedIn == str(rvalue)[:len(storedIn)]: r += ind(curIndent) + str(rvalue) + '\n' else: r += ind(curIndent) + '# INPLACE_* op used not as INPLACE!!!\n' # TODO: possible error r += ind(curIndent) + str(lvalue) + ' = ' + \ rvalue.children[0] + ' ' + rvalue.value + \ ' ' + rvalue.children[1] + '\n' elif isinstance(rvalue, Iterator): r += ind(curIndent) + 'for ' + str(lvalue) + ' in ' + \ str(rvalue) + ':\n' elif isinstance(rvalue, DummyEx): # quite a hack here... take a look at mergeCompoundNodes # for more info r += '# as ' + str(lvalue) + '\n' else: if not emptystack: self.postponedStores.append((lvalue, rvalue)) else: if len(self.postponedStores) == 0: r += ind(curIndent) + str(lvalue) + ' = ' + \ str(rvalue) + '\n' else: self.postponedStores.append((lvalue, rvalue)) r += ind(curIndent) + '(' + \ ', '.join(str(x[0]) for x in self.postponedStores) + \ ') = (' + \ ', '.join(str(x[1]) for x in self.postponedStores) + \ ')\n' self.postponedStores = [] return r
try: o = stack.pop() except IndexError, err: print "XXXX: pop from empty stack caught, returning: ", r return r o.dec() noneInNames = False for x in self.co.names.value: if x.value == 'None': noneInNames = True break if self.co.name.value != '<module>' and \ not isinstance(o, DummyLocals) and \ (not isinstance(o, Constant) or o.value is not None or noneInNames): r += ind(curIndent) + 'return ' + str(o) + '\n' return r def _IMPORT_STAR(self, cmd, prevcmd, nextcmd, stack, curIndent): r = ind(curIndent) + 'from ' + stack[-1].module + ' import *\n' stack.pop().dec() return r def _EXEC_STMT(self, cmd, prevcmd, nextcmd, stack, curIndent): r = '' if stack[-2].value is None: r += ind(curIndent) + 'exec ' + str(stack[-3]) + '\n' # TODO: validate stack[-2] == stack[-1] elif stack[-2] == stack[-1]: r += ind(curIndent) + 'exec ' + str(stack[-3]) + \ ' in ' + str(stack[-2]) + '\n'