From 00d17461fc3cf53e6a8d588f252cee01401a63ab Mon Sep 17 00:00:00 2001 From: rocky Date: Sat, 12 Dec 2015 06:32:31 -0500 Subject: [PATCH] Python3 compatibility --- uncompyle-code.py | 4 +- uncompyle6/__init__.py | 0 uncompyle6/parser.py | 8 +- uncompyle6/scanner.py | 284 +++++++++++++++++++++++++++++++++++++++++ uncompyle6/walker.py | 13 +- 5 files changed, 300 insertions(+), 9 deletions(-) mode change 100644 => 100755 uncompyle-code.py mode change 100755 => 100644 uncompyle6/__init__.py mode change 100755 => 100644 uncompyle6/parser.py create mode 100755 uncompyle6/scanner.py mode change 100755 => 100644 uncompyle6/walker.py diff --git a/uncompyle-code.py b/uncompyle-code.py old mode 100644 new mode 100755 index c81ce17b..529afc52 --- a/uncompyle-code.py +++ b/uncompyle-code.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python + from __future__ import print_function import uncompyle6 @@ -186,7 +188,7 @@ def uncompyle_test(): frame = inspect.currentframe() try: co = frame.f_code - uncompyle(2.7, co, sys.stdout, 1) + uncompyle(2.7, co, sys.stdout, 1, 1) print() print('------------------------') uncompyle_find(2.7, co, 33) diff --git a/uncompyle6/__init__.py b/uncompyle6/__init__.py old mode 100755 new mode 100644 diff --git a/uncompyle6/parser.py b/uncompyle6/parser.py old mode 100755 new mode 100644 index 4a14599a..97acb7fb --- a/uncompyle6/parser.py +++ b/uncompyle6/parser.py @@ -10,7 +10,11 @@ from __future__ import print_function __all__ = ['parse', 'AST', 'ParserError', 'Parser'] -from spark import GenericASTBuilder +try: + from spark import GenericASTBuilder +except ImportError: + from .spark import GenericASTBuilder + import string, sys if (sys.version_info > (3, 0)): @@ -19,8 +23,6 @@ if (sys.version_info > (3, 0)): else: from UserList import UserList -from scanner import Token - class AST(UserList): def __init__(self, type, kids=[]): self.type = intern(type) diff --git a/uncompyle6/scanner.py b/uncompyle6/scanner.py new file mode 100755 index 00000000..656fb1ea --- /dev/null +++ b/uncompyle6/scanner.py @@ -0,0 +1,284 @@ +from __future__ import print_function + +# Copyright (c) 1999 John Aycock +# Copyright (c) 2000-2002 by hartmut Goebel +# Copyright (c) 2005 by Dan Pascu +# +# See main module for license. +# + +__all__ = ['Token', 'Scanner', 'Code'] + +import types +from collections import namedtuple +from array import array +from operator import itemgetter + +from uncompyle6.opcodes import opcode_25, opcode_26, opcode_27 + +class Token: + ''' + Class representing a byte-code token. + + A byte-code token is equivalent to the contents of one line + as output by dis.dis(). + ''' + def __init__(self, type_, attr=None, pattr=None, offset=-1, linestart=False): + self.type = intern(type_) + self.attr = attr + self.pattr = pattr + self.offset = offset + self.linestart = linestart + + def __cmp__(self, o): + if isinstance(o, Token): + # both are tokens: compare type and pattr + return cmp(self.type, o.type) or cmp(self.pattr, o.pattr) + else: + return cmp(self.type, o) + + def __repr__(self): + return str(self.type) + + def __str__(self): + pattr = self.pattr + if self.linestart: + return '\n%s\t%-17s %r' % (self.offset, self.type, pattr) + else: + return '%s\t%-17s %r' % (self.offset, self.type, pattr) + + def __hash__(self): + return hash(self.type) + + def __getitem__(self, i): + raise IndexError + +class Code: + ''' + Class for representing code-objects. + + This is similar to the original code object, but additionally + the diassembled code is stored in the attribute '_tokens'. + ''' + def __init__(self, co, scanner, classname=None): + for i in dir(co): + if i.startswith('co_'): + setattr(self, i, getattr(co, i)) + self._tokens, self._customize = scanner.disassemble(co, classname) + +class Scanner(object): + opc = None # opcode module + + def __init__(self, version): + if version == 2.7: + self.opc = opcode_27 + elif version == 2.6: + self.opc = opcode_26 + elif version == 2.5: + self.opc = opcode_25 + + return self.resetTokenClass() + + def setShowAsm(self, showasm, out=None): + self.showasm = showasm + self.out = out + + def setTokenClass(self, tokenClass): + assert isinstance(tokenClass, types.ClassType) + self.Token = tokenClass + return self.Token + + def resetTokenClass(self): + return self.setTokenClass(Token) + + def get_target(self, pos, op=None): + if op is None: + op = self.code[pos] + target = self.get_argument(pos) + if op in self.opc.hasjrel: + target += pos + 3 + return target + + def get_argument(self, pos): + arg = self.code[pos+1] + self.code[pos+2] * 256 + return arg + + def print_bytecode(self): + for i in self.op_range(0, len(self.code)): + op = self.code[i] + if op in self.opc.hasjabs+self.opc.hasjrel: + dest = self.get_target(i, op) + print('%i\t%s\t%i' % (i, self.opc.opname[op], dest)) + else: + print('%i\t%s\t' % (i, self.opc.opname[op])) + + def first_instr(self, start, end, instr, target=None, exact=True): + ''' + Find the first in the block from start to end. + is any python bytecode instruction or a list of opcodes + If is an opcode with a target (like a jump), a target + destination can be specified which must match precisely if exact + is True, or if exact is False, the instruction which has a target + closest to will be returned. + + Return index to it or None if not found. + ''' + code = self.code + assert(start>=0 and end<=len(code)) + + try: None in instr + except: instr = [instr] + + pos = None + distance = len(code) + for i in self.op_range(start, end): + op = code[i] + if op in instr: + if target is None: + return i + dest = self.get_target(i, op) + if dest == target: + return i + elif not exact: + _distance = abs(target - dest) + if _distance < distance: + distance = _distance + pos = i + return pos + + def last_instr(self, start, end, instr, target=None, exact=True): + ''' + Find the last in the block from start to end. + is any python bytecode instruction or a list of opcodes + If is an opcode with a target (like a jump), a target + destination can be specified which must match precisely if exact + is True, or if exact is False, the instruction which has a target + closest to will be returned. + + Return index to it or None if not found. + ''' + + code = self.code + if not (start>=0 and end<=len(code)): + return None + + try: None in instr + except: instr = [instr] + + pos = None + distance = len(code) + for i in self.op_range(start, end): + op = code[i] + if op in instr: + if target is None: + pos = i + else: + dest = self.get_target(i, op) + if dest == target: + distance = 0 + pos = i + elif not exact: + _distance = abs(target - dest) + if _distance <= distance: + distance = _distance + pos = i + return pos + + def all_instr(self, start, end, instr, target=None, include_beyond_target=False): + ''' + Find all in the block from start to end. + is any python bytecode instruction or a list of opcodes + If is an opcode with a target (like a jump), a target + destination can be specified which must match precisely. + + Return a list with indexes to them or [] if none found. + ''' + + code = self.code + assert(start>=0 and end<=len(code)) + + try: None in instr + except: instr = [instr] + + result = [] + for i in self.op_range(start, end): + op = code[i] + if op in instr: + if target is None: + result.append(i) + else: + t = self.get_target(i, op) + if include_beyond_target and t >= target: + result.append(i) + elif t == target: + result.append(i) + return result + + def op_size(self, op): + if op < self.opc.HAVE_ARGUMENT and op not in self.opc.hasArgumentExtended: + return 1 + else: + return 3 + + def op_hasArgument(self, op): + return self.op_size(op) > 1 + + def op_range(self, start, end): + while start < end: + yield start + start += self.op_size(self.code[start]) + + def remove_mid_line_ifs(self, ifs): + filtered = [] + for i in ifs: + if self.lines[i].l_no == self.lines[i+3].l_no: + if self.code[self.prev[self.lines[i].next]] in (self.opc.PJIT, self.opc.PJIF): + continue + filtered.append(i) + return filtered + + def rem_or(self, start, end, instr, target=None, include_beyond_target=False): + ''' + Find all in the block from start to end. + is any python bytecode instruction or a list of opcodes + If is an opcode with a target (like a jump), a target + destination can be specified which must match precisely. + + Return a list with indexes to them or [] if none found. + ''' + + code = self.code + assert(start>=0 and end<=len(code)) + + try: None in instr + except: instr = [instr] + + result = [] + for i in self.op_range(start, end): + op = code[i] + if op in instr: + if target is None: + result.append(i) + else: + t = self.get_target(i, op) + if include_beyond_target and t >= target: + result.append(i) + elif t == target: + result.append(i) + + pjits = self.all_instr(start, end, self.opc.PJIT) + filtered = [] + for pjit in pjits: + tgt = self.get_target(pjit)-3 + for i in result: + if i <= pjit or i >= tgt: + filtered.append(i) + result = filtered + filtered = [] + return result + + def restrict_to_parent(self, target, parent): + '''Restrict pos to parent boundaries.''' + if not (parent['start'] < target < parent['end']): + target = parent['end'] + return target diff --git a/uncompyle6/walker.py b/uncompyle6/walker.py old mode 100755 new mode 100644 index 5b4c245e..4f5aad09 --- a/uncompyle6/walker.py +++ b/uncompyle6/walker.py @@ -45,16 +45,19 @@ from __future__ import print_function try: from StringIO import StringIO + from spark import GenericASTTraversal + from parser import AST + from scanner import Token, Code except ImportError: from io import StringIO + from .spark import GenericASTTraversal + from .parser import AST + from .scanner import Token, Code import sys, re -from types import IntType, CodeType +from types import CodeType -from spark import GenericASTTraversal import parser -from parser import AST -from scanner import Token, Code minint = -sys.maxint-1 @@ -704,7 +707,7 @@ class Walker(GenericASTTraversal, object): def n_LOAD_CONST(self, node): data = node.pattr; datatype = type(data) - if datatype is IntType and data == minint: + if isinstance(datatype, int) and data == minint: # convert to hex, since decimal representation # would result in 'LOAD_CONST; UNARY_NEGATIVE' # change:hG/2002-02-07: this was done for all negative integers