diff --git a/test/Makefile b/test/Makefile index 64fd3252..143f8b36 100644 --- a/test/Makefile +++ b/test/Makefile @@ -82,6 +82,10 @@ check-bytecode-3.5: check-native-short: $(PYTHON) test_pythonlib.py --bytecode-$(PYTHON_VERSION) --verify $(COMPILE) +#: Run longer Python 2.6's lib files known to be okay +check-2.6-ok: + $(PYTHON) test_pythonlib.py --ok-2.6 --verify $(COMPILE) + #: Run longer Python 2.7's lib files known to be okay check-2.7-ok: $(PYTHON) test_pythonlib.py --ok-2.7 --verify $(COMPILE) diff --git a/test/ok_lib2.6/abc.pyc b/test/ok_lib2.6/abc.pyc new file mode 100644 index 00000000..20dc074b Binary files /dev/null and b/test/ok_lib2.6/abc.pyc differ diff --git a/test/ok_lib2.6/abc.pyo b/test/ok_lib2.6/abc.pyo new file mode 100644 index 00000000..4313fc68 Binary files /dev/null and b/test/ok_lib2.6/abc.pyo differ diff --git a/test/ok_lib2.6/anydbm.py b/test/ok_lib2.6/anydbm.py deleted file mode 100644 index 8b01ef3e..00000000 --- a/test/ok_lib2.6/anydbm.py +++ /dev/null @@ -1,83 +0,0 @@ -"""Generic interface to all dbm clones. - -Instead of - - import dbm - d = dbm.open(file, 'w', 0666) - -use - - import anydbm - d = anydbm.open(file, 'w') - -The returned object is a dbhash, gdbm, dbm or dumbdbm object, -dependent on the type of database being opened (determined by whichdb -module) in the case of an existing dbm. If the dbm does not exist and -the create or new flag ('c' or 'n') was specified, the dbm type will -be determined by the availability of the modules (tested in the above -order). - -It has the following interface (key and data are strings): - - d[key] = data # store data at key (may override data at - # existing key) - data = d[key] # retrieve data at key (raise KeyError if no - # such key) - del d[key] # delete data stored at key (raises KeyError - # if no such key) - flag = key in d # true if the key exists - list = d.keys() # return a list of all existing keys (slow!) - -Future versions may change the order in which implementations are -tested for existence, add interfaces to other dbm-like -implementations. - -The open function has an optional second argument. This can be 'r', -for read-only access, 'w', for read-write access of an existing -database, 'c' for read-write access to a new or existing database, and -'n' for read-write access to a new database. The default is 'r'. - -Note: 'r' and 'w' fail if the database doesn't exist; 'c' creates it -only if it doesn't exist; and 'n' always creates a new database. - -""" - -class error(Exception): - pass - -_names = ['dbhash', 'gdbm', 'dbm', 'dumbdbm'] -_errors = [error] -_defaultmod = None - -for _name in _names: - try: - _mod = __import__(_name) - except ImportError: - continue - if not _defaultmod: - _defaultmod = _mod - _errors.append(_mod.error) - -if not _defaultmod: - raise ImportError, "no dbm clone found; tried %s" % _names - -error = tuple(_errors) - -def open(file, flag = 'r', mode = 0666): - # guess the type of an existing database - from whichdb import whichdb - result=whichdb(file) - if result is None: - # db doesn't exist - if 'c' in flag or 'n' in flag: - # file doesn't exist and the new - # flag was used so use default type - mod = _defaultmod - else: - raise error, "need 'c' or 'n' flag to open new db" - elif result == "": - # db type cannot be determined - raise error, "db type could not be determined" - else: - mod = __import__(result) - return mod.open(file, flag, mode) diff --git a/test/ok_lib2.6/atexit.pyc b/test/ok_lib2.6/atexit.pyc new file mode 100644 index 00000000..1c882a4e Binary files /dev/null and b/test/ok_lib2.6/atexit.pyc differ diff --git a/test/ok_lib2.6/atexit.pyo b/test/ok_lib2.6/atexit.pyo new file mode 100644 index 00000000..1c882a4e Binary files /dev/null and b/test/ok_lib2.6/atexit.pyo differ diff --git a/test/ok_lib2.6/base64.pyc b/test/ok_lib2.6/base64.pyc new file mode 100644 index 00000000..7f7f57b9 Binary files /dev/null and b/test/ok_lib2.6/base64.pyc differ diff --git a/test/ok_lib2.6/base64.pyo b/test/ok_lib2.6/base64.pyo new file mode 100644 index 00000000..7f7f57b9 Binary files /dev/null and b/test/ok_lib2.6/base64.pyo differ diff --git a/test/ok_lib2.6/calendar.pyc b/test/ok_lib2.6/calendar.pyc new file mode 100644 index 00000000..273eb01c Binary files /dev/null and b/test/ok_lib2.6/calendar.pyc differ diff --git a/test/ok_lib2.6/calendar.pyo b/test/ok_lib2.6/calendar.pyo new file mode 100644 index 00000000..273eb01c Binary files /dev/null and b/test/ok_lib2.6/calendar.pyo differ diff --git a/test/ok_lib2.6/chunk.pyc b/test/ok_lib2.6/chunk.pyc new file mode 100644 index 00000000..04f26e65 Binary files /dev/null and b/test/ok_lib2.6/chunk.pyc differ diff --git a/test/ok_lib2.6/chunk.pyo b/test/ok_lib2.6/chunk.pyo new file mode 100644 index 00000000..04f26e65 Binary files /dev/null and b/test/ok_lib2.6/chunk.pyo differ diff --git a/test/ok_lib2.6/cmd.pyc b/test/ok_lib2.6/cmd.pyc new file mode 100644 index 00000000..574eee6b Binary files /dev/null and b/test/ok_lib2.6/cmd.pyc differ diff --git a/test/ok_lib2.6/cmd.pyo b/test/ok_lib2.6/cmd.pyo new file mode 100644 index 00000000..574eee6b Binary files /dev/null and b/test/ok_lib2.6/cmd.pyo differ diff --git a/test/ok_lib2.6/codeop.pyc b/test/ok_lib2.6/codeop.pyc new file mode 100644 index 00000000..fdcf7efe Binary files /dev/null and b/test/ok_lib2.6/codeop.pyc differ diff --git a/test/ok_lib2.6/codeop.pyo b/test/ok_lib2.6/codeop.pyo new file mode 100644 index 00000000..fdcf7efe Binary files /dev/null and b/test/ok_lib2.6/codeop.pyo differ diff --git a/test/ok_lib2.6/colorsys.pyc b/test/ok_lib2.6/colorsys.pyc new file mode 100644 index 00000000..8123d949 Binary files /dev/null and b/test/ok_lib2.6/colorsys.pyc differ diff --git a/test/ok_lib2.6/colorsys.pyo b/test/ok_lib2.6/colorsys.pyo new file mode 100644 index 00000000..8123d949 Binary files /dev/null and b/test/ok_lib2.6/colorsys.pyo differ diff --git a/test/ok_lib2.6/commands.pyc b/test/ok_lib2.6/commands.pyc new file mode 100644 index 00000000..c779472c Binary files /dev/null and b/test/ok_lib2.6/commands.pyc differ diff --git a/test/ok_lib2.6/commands.pyo b/test/ok_lib2.6/commands.pyo new file mode 100644 index 00000000..c779472c Binary files /dev/null and b/test/ok_lib2.6/commands.pyo differ diff --git a/test/ok_lib2.6/dircache.pyc b/test/ok_lib2.6/dircache.pyc new file mode 100644 index 00000000..cb22e6da Binary files /dev/null and b/test/ok_lib2.6/dircache.pyc differ diff --git a/test/ok_lib2.6/dircache.pyo b/test/ok_lib2.6/dircache.pyo new file mode 100644 index 00000000..cb22e6da Binary files /dev/null and b/test/ok_lib2.6/dircache.pyo differ diff --git a/test/ok_lib2.6/dumbdbm.pyc b/test/ok_lib2.6/dumbdbm.pyc new file mode 100644 index 00000000..c8851940 Binary files /dev/null and b/test/ok_lib2.6/dumbdbm.pyc differ diff --git a/test/ok_lib2.6/dumbdbm.pyo b/test/ok_lib2.6/dumbdbm.pyo new file mode 100644 index 00000000..c8851940 Binary files /dev/null and b/test/ok_lib2.6/dumbdbm.pyo differ diff --git a/test/ok_lib2.6/dummy_threading.pyc b/test/ok_lib2.6/dummy_threading.pyc new file mode 100644 index 00000000..5a777bd2 Binary files /dev/null and b/test/ok_lib2.6/dummy_threading.pyc differ diff --git a/test/ok_lib2.6/dummy_threading.pyo b/test/ok_lib2.6/dummy_threading.pyo new file mode 100644 index 00000000..5a777bd2 Binary files /dev/null and b/test/ok_lib2.6/dummy_threading.pyo differ diff --git a/test/ok_lib2.6/fnmatch.pyc b/test/ok_lib2.6/fnmatch.pyc new file mode 100644 index 00000000..f5857540 Binary files /dev/null and b/test/ok_lib2.6/fnmatch.pyc differ diff --git a/test/ok_lib2.6/fnmatch.pyo b/test/ok_lib2.6/fnmatch.pyo new file mode 100644 index 00000000..f5857540 Binary files /dev/null and b/test/ok_lib2.6/fnmatch.pyo differ diff --git a/test/test_pythonlib.py b/test/test_pythonlib.py index 9915f633..6e759be1 100755 --- a/test/test_pythonlib.py +++ b/test/test_pythonlib.py @@ -59,9 +59,8 @@ test_options = { 'test': ('test', PYC, 'test'), - 'ok-2.6': - (os.path.join(src_dir, 'ok_2.6'), - PYOC, 'ok-2.6', 2.6), + 'ok-2.6': (os.path.join(src_dir, 'ok_lib2.6'), + PYOC, 'ok-2.6', 2.6), 'ok-2.7': (os.path.join(src_dir, 'ok_lib2.7'), PYOC, 'ok-2.7', 2.7), diff --git a/uncompyle6/scanners/scanner2.py b/uncompyle6/scanners/scanner2.py index 0424dc46..f0044c69 100755 --- a/uncompyle6/scanners/scanner2.py +++ b/uncompyle6/scanners/scanner2.py @@ -307,7 +307,7 @@ class Scanner2(scan.Scanner): for i in self.op_range(0, n): op = self.code[i] self.prev.append(i) - if op >= self.opc.HAVE_ARGUMENT: + if self.op_hasArgument(op): self.prev.append(i) self.prev.append(i) pass diff --git a/uncompyle6/scanners/scanner25.py b/uncompyle6/scanners/scanner25.py index 3fc3e8d2..e271b321 100755 --- a/uncompyle6/scanners/scanner25.py +++ b/uncompyle6/scanners/scanner25.py @@ -11,347 +11,62 @@ information for later use in deparsing. """ from uncompyle6.opcodes.opcode_25 import * -from xdis.bytecode import findlinestarts -import uncompyle6.scanners.scanner2 as scan -class Scanner25(scan.Scanner2): +import uncompyle6.scanners.scanner26 as scan +import uncompyle6.scanners.scanner2 as scan2 + +# bytecode verification, verify(), uses JUMP_OPs from here +from xdis.opcodes import opcode_25 +JUMP_OPs = opcode_25.JUMP_OPs + +# We base this off of 2.6 instead of the other way around +# because we cleaned things up this way. +# The history is that 2.7 support is the cleanest, +# then from that we got 2.6 and so on. +class Scanner25(scan.Scanner26): def __init__(self): - super(Scanner25, self).__init__(2.5) + scan2.Scanner2.__init__(self, 2.5) + self.stmt_opcodes = frozenset([ + self.opc.SETUP_LOOP, self.opc.BREAK_LOOP, + self.opc.SETUP_FINALLY, self.opc.END_FINALLY, + self.opc.SETUP_EXCEPT, self.opc.POP_BLOCK, + self.opc.STORE_FAST, self.opc.DELETE_FAST, + self.opc.STORE_DEREF, self.opc.STORE_GLOBAL, + self.opc.DELETE_GLOBAL, self.opc.STORE_NAME, + self.opc.DELETE_NAME, self.opc.STORE_ATTR, + self.opc.DELETE_ATTR, self.opc.STORE_SUBSCR, + self.opc.DELETE_SUBSCR, self.opc.RETURN_VALUE, + self.opc.RAISE_VARARGS, self.opc.POP_TOP, + self.opc.PRINT_EXPR, self.opc.PRINT_ITEM, + self.opc.PRINT_NEWLINE, self.opc.PRINT_ITEM_TO, + self.opc.PRINT_NEWLINE_TO, self.opc.CONTINUE_LOOP, + self.opc.JUMP_ABSOLUTE, self.opc.EXEC_STMT, + ]) - def disassemble(self, co, classname=None, code_objects={}): - ''' - Disassemble a code object, returning a list of 'Token'. + # "setup" opcodes + self.setup_ops = frozenset([ + self.opc.SETUP_EXCEPT, self.opc.SETUP_FINALLY, + ]) - The main part of this procedure is modelled after - dis.disassemble(). - ''' + # opcodes with expect a variable number pushed values whose + # count is in the opcode. For parsing we generally change the + # opcode name to include that number. + self.varargs_ops = frozenset([ + self.opc.BUILD_LIST, self.opc.BUILD_TUPLE, + self.opc.BUILD_SLICE, self.opc.UNPACK_SEQUENCE, + self.opc.MAKE_FUNCTION, self.opc.CALL_FUNCTION, + self.opc.MAKE_CLOSURE, self.opc.CALL_FUNCTION_VAR, + self.opc.CALL_FUNCTION_KW, self.opc.CALL_FUNCTION_VAR_KW, + self.opc.DUP_TOPX, self.opc.RAISE_VARARGS]) - # import dis; dis.disassemble(co) # DEBUG - - # Container for tokens - tokens = [] - customize = {} - Token = self.Token # shortcut - - n = self.setup_code(co) - self.build_lines_data(co, n) - - # linestarts contains block code adresses (addr,block) - self.linestarts = list(findlinestarts(co)) - - # class and names - if classname: - classname = '_' + classname.lstrip('_') + '__' - - def unmangle(name): - if name.startswith(classname) and name[-2:] != '__': - return name[len(classname) - 2:] - return name - - free = [ unmangle(name) for name in (co.co_cellvars + co.co_freevars) ] - names = [ unmangle(name) for name in co.co_names ] - varnames = [ unmangle(name) for name in co.co_varnames ] - else: - free = co.co_cellvars + co.co_freevars - names = co.co_names - varnames = co.co_varnames - self.names = names - - # list of instruction to remove/add or change to match with bytecode 2.7 - self.toChange = [] - self.restructBytecode() - codelen = len(self.code) - - # mapping adresses of previous instruction - self.prev = [0] - for i in self.op_range(0, codelen): - op = self.code[i] - self.prev.append(i) - if self.op_hasArgument(op): - self.prev.append(i) - self.prev.append(i) - - self.load_asserts = set() - for i in self.op_range(0, codelen): - if self.code[i] == self.opc.PJIT and self.code[i + 3] == self.opc.LOAD_GLOBAL: - if names[self.get_argument(i+3)] == 'AssertionError': - self.load_asserts.add(i+3) - - # self.lines contains (block,addrLastInstr) - cf = self.find_jump_targets() - - # contains (code, [addrRefToCode]) - last_stmt = self.next_stmt[0] - i = self.next_stmt[last_stmt] - replace = {} - while i < codelen-1: - if self.lines[last_stmt].next > i: - if self.code[last_stmt] == PRINT_ITEM: - if self.code[i] == PRINT_ITEM: - replace[i] = 'PRINT_ITEM_CONT' - elif self.code[i] == PRINT_NEWLINE: - replace[i] = 'PRINT_NEWLINE_CONT' - last_stmt = i - i = self.next_stmt[i] - - imports = self.all_instr(0, codelen, (IMPORT_NAME, IMPORT_FROM, IMPORT_STAR)) - if len(imports) > 1: - last_import = imports[0] - for i in imports[1:]: - if self.lines[last_import].next > i: - if self.code[last_import] == IMPORT_NAME == self.code[i]: - replace[i] = 'IMPORT_NAME_CONT' - last_import = i - - extended_arg = 0 - for offset in self.op_range(0, codelen): - op = self.code[offset] - op_name = self.opname[op] - oparg = None; pattr = None - - if offset in cf: - k = 0 - for j in cf[offset]: - tokens.append(Token('COME_FROM', None, repr(j), - offset="%s_%d" % (offset, k) )) - k += 1 - if self.op_hasArgument(op): - oparg = self.get_argument(offset) + extended_arg - extended_arg = 0 - if op == EXTENDED_ARG: - raise NotImplementedError - extended_arg = oparg * scan.L65536 - continue - if op in hasconst: - const = co.co_consts[oparg] - # We can't use inspect.iscode() because we may be - # using a different version of Python than the - # one that this was byte-compiled on. So the code - # types may mismatch. - if hasattr(const, 'co_name'): - oparg = const - if const.co_name == '': - assert op_name == 'LOAD_CONST' - op_name = 'LOAD_LAMBDA' - elif const.co_name == '': - op_name = 'LOAD_GENEXPR' - elif const.co_name == '': - op_name = 'LOAD_DICTCOMP' - elif const.co_name == '': - op_name = 'LOAD_SETCOMP' - # verify uses 'pattr' for comparison, since 'attr' - # now holds Code(const) and thus can not be used - # for comparison (todo: think about changing this) - # pattr = 'code_object @ 0x%x %s->%s' % - # (id(const), const.co_filename, const.co_name) - pattr = '' - else: - pattr = const - elif op in hasname: - pattr = names[oparg] - elif op in hasjrel: - pattr = repr(offset + 3 + oparg) - elif op in hasjabs: - pattr = repr(oparg) - elif op in haslocal: - pattr = varnames[oparg] - elif op in hascompare: - pattr = cmp_op[oparg] - elif op in hasfree: - pattr = free[oparg] - if offset in self.toChange: - if self.code[offset] == JA and self.code[oparg] == WITH_CLEANUP: - op_name = 'SETUP_WITH' - cf[oparg] = cf.get(oparg, []) + [offset] - if op in (BUILD_LIST, BUILD_TUPLE, BUILD_SLICE, - UNPACK_SEQUENCE, - MAKE_FUNCTION, CALL_FUNCTION, MAKE_CLOSURE, - CALL_FUNCTION_VAR, CALL_FUNCTION_KW, - CALL_FUNCTION_VAR_KW, DUP_TOPX, RAISE_VARARGS - ): - # CE - Hack for >= 2.5 - # Now all values loaded via LOAD_CLOSURE are packed into - # a tuple before calling MAKE_CLOSURE. - if op == BUILD_TUPLE and \ - self.code[self.prev[offset]] == LOAD_CLOSURE: - continue - else: - op_name = '%s_%d' % (op_name, oparg) - if op != BUILD_SLICE: - customize[op_name] = oparg - elif op == JA: - target = self.get_target(offset) - if target < offset: - if offset in self.stmts and self.code[offset+3] not in (END_FINALLY, POP_BLOCK) \ - and offset not in self.not_continue: - op_name = 'CONTINUE' - else: - op_name = 'JUMP_BACK' - - elif op == LOAD_GLOBAL: - if offset in self.load_asserts: - op_name = 'LOAD_ASSERT' - elif op == RETURN_VALUE: - if offset in self.return_end_ifs: - op_name = 'RETURN_END_IF' - - if offset in self.linestartoffsets: - linestart = self.linestartoffsets[offset] - else: - linestart = None - - if offset not in replace: - tokens.append(Token(op_name, oparg, pattr, offset, linestart)) - else: - tokens.append(Token(replace[offset], oparg, pattr, offset, linestart)) - - return tokens, customize - - def getOpcodeToDel(self, i): - ''' - check validity of the opcode at position I and return a list of opcode to delete - ''' - opcode = self.code[i] - opsize = self.op_size(opcode) - - if i+opsize >= len(self.code): - return None - - if opcode == EXTENDED_ARG: - raise NotImplementedError - # del POP_TOP - if opcode in (PJIF, PJIT, JA, JF): - toDel = [] - # del POP_TOP - if self.code[i+opsize] == POP_TOP: - if self.code[i+opsize] == self.code[i+opsize+1] and self.code[i+opsize] == self.code[i+opsize+2] \ - and opcode in (JF, JA) and self.code[i+opsize] != self.code[i+opsize+3]: - pass - else: - toDel += [i+opsize] - # conditional tuple - if self.code[i] == JA and self.code[i+opsize] == POP_TOP \ - and self.code[i+opsize+1] == JA and self.code[i+opsize+4] == POP_BLOCK: - jmpabs1target = self.get_target(i) - jmpabs2target = self.get_target(i+opsize+1) - if jmpabs1target == jmpabs2target and self.code[jmpabs1target] == FOR_ITER: - destFor = self.get_target(jmpabs1target) - if destFor == i+opsize+4: - setupLoop = self.last_instr(0, jmpabs1target, SETUP_LOOP) - standarFor = self.last_instr(setupLoop, jmpabs1target, GET_ITER) - if standarFor is None: - self.restructJump(jmpabs1target, destFor+self.op_size(POP_BLOCK)) - toDel += [setupLoop, i+opsize+1, i+opsize+4] - if len(toDel) > 0: - return toDel - return None - if opcode == RAISE_VARARGS: - if self.code[i+opsize] == POP_TOP: - return [i+opsize] - if opcode == BUILD_LIST: - if (self.code[i+opsize] == DUP_TOP - and self.code[i+opsize+1] in (STORE_NAME, STORE_FAST)): - # del DUP/STORE_NAME x - toDel = [i+opsize, i+opsize+1] - nameDel = self.get_argument(i+opsize+1) - start = i+opsize+1 - end = start - # del LOAD_NAME x - while end < len(self.code): - end = self.first_instr(end, len(self.code), (LOAD_NAME, LOAD_FAST)) - if nameDel == self.get_argument(end): - toDel += [end] - break - if self.code[end] == LOAD_NAME: - end += self.op_size(LOAD_NAME) - else: - end += self.op_size(LOAD_FAST) - # log JA/POP_TOP to del and update PJIF - while start < end: - start = self.first_instr(start, end, (PJIF, PJIT)) # end = len(self.code) - if start is None: break - target = self.get_target(start) - if self.code[target] == POP_TOP and self.code[target-3] == JA: - toDel += [target, target-3] - # update PJIF - target = self.get_target(target-3) - self.restructJump(start, target) - start += self.op_size(PJIF) - # del DELETE_NAME x - start = end - while end < len(self.code): - end = self.first_instr(end, len(self.code), (DELETE_NAME, DELETE_FAST)) - if nameDel == self.get_argument(end): - toDel += [end] - break - if self.code[end] == DELETE_NAME: - end += self.op_size(DELETE_NAME) - else: - end += self.op_size(DELETE_FAST) - return toDel - # change join(for..) struct - if opcode == SETUP_LOOP: - if self.code[i+3] == LOAD_FAST and self.code[i+6] == FOR_ITER: - end = self.first_instr(i, len(self.code), RETURN_VALUE) - end = self.first_instr(i, end, YIELD_VALUE) - if end and self.code[end+1] == POP_TOP and self.code[end+2] == JA and self.code[end+5] == POP_BLOCK: - return [i, end+5] - # with stmt - if opcode == WITH_CLEANUP: - chckDel = i-self.op_size(DELETE_NAME) - assert self.code[chckDel] in (DELETE_NAME, DELETE_FAST) - toDel = [chckDel] - nameDel = self.get_argument(chckDel) - chckDel -= self.op_size(LOAD_NAME) - assert self.code[chckDel] in (LOAD_NAME, LOAD_FAST) - toDel += [chckDel] - - allStore = self.all_instr(0, i, (STORE_NAME, STORE_FAST)) - chckStore = -1 - for store in allStore: - if nameDel == self.get_argument(store): - if self.code[store+3] == LOAD_ATTR and self.code[store-3] == LOAD_ATTR \ - and self.code[store-4] == DUP_TOP: - chckStore = store - assert chckStore > 0 - toDel += [chckStore-4, chckStore-3, chckStore+3] - - chckStp = -1 - allSetup = self.all_instr(chckStore+3, i, (SETUP_FINALLY)) - for stp in allSetup: - if chckDel == self.get_target(stp): - chckStp = stp - assert chckStp > 0 - toDel += [chckStp] - chckDel = chckStore+3+self.op_size(self.code[chckStore+3]) - while chckDel < chckStp-3: - toDel += [chckDel] - chckDel += self.op_size(self.code[chckDel]) - if (self.code[chckStp-3] in (STORE_NAME, STORE_FAST) - and self.code[chckStp+3] in (LOAD_NAME, LOAD_FAST) - and self.code[chckStp+6] in (DELETE_NAME, DELETE_FAST)): - toDel += [chckStp-3, chckStp+3, chckStp+6] - # SETUP_WITH opcode dosen't exist in 2.5 but is necessary for the grammar - self.code[chckStore] = JUMP_ABSOLUTE # ugly hack - self.restructJump(chckStore, i) - self.toChange.append(chckStore) - return toDel - if opcode == NOP: - return [i] - return None - - def getOpcodeToExp(self): - # we handle listExp, if opcode have to be resized - listExp = [] - i=0 - while i < len(self.code): # we can't use op_range for the moment - op = self.code[i] - if op in self.opc.hasArgumentExtended: - listExp += [i] - elif self.op_hasArgument(op): - i+=2 - i+=1 - return listExp + # opcodes that store values into a variable + self.designator_ops = frozenset([ + self.opc.STORE_FAST, self.opc.STORE_NAME, + self.opc.STORE_GLOBAL, self.opc.STORE_DEREF, self.opc.STORE_ATTR, + self.opc.STORE_SLICE_0, self.opc.STORE_SLICE_1, self.opc.STORE_SLICE_2, + self.opc.STORE_SLICE_3, self.opc.STORE_SUBSCR, self.opc.UNPACK_SEQUENCE, + self.opc.JA + ]) def restructCode(self, listDel, listExp): ''' @@ -491,113 +206,6 @@ class Scanner25(scan.Scanner2): self.code[pos+2] = (target >> 8) & 0xFF self.code[pos+1] = target & 0xFF - def build_stmt_indices(self): - code = self.code - start = 0 - end = len(code) - - stmt_opcodes = set([ - SETUP_LOOP, BREAK_LOOP, CONTINUE_LOOP, - SETUP_FINALLY, END_FINALLY, SETUP_EXCEPT, - POP_BLOCK, STORE_FAST, DELETE_FAST, STORE_DEREF, - STORE_GLOBAL, DELETE_GLOBAL, STORE_NAME, DELETE_NAME, - STORE_ATTR, DELETE_ATTR, STORE_SUBSCR, DELETE_SUBSCR, - RETURN_VALUE, RAISE_VARARGS, POP_TOP, - PRINT_EXPR, PRINT_ITEM, PRINT_NEWLINE, PRINT_ITEM_TO, PRINT_NEWLINE_TO, - JUMP_ABSOLUTE, EXEC_STMT - ]) - - stmt_opcode_seqs = [(PJIF, JF), (PJIF, JA), (PJIT, JF), (PJIT, JA)] - - designator_ops = set([ - STORE_FAST, STORE_NAME, STORE_GLOBAL, STORE_DEREF, STORE_ATTR, - STORE_SLICE_0, STORE_SLICE_1, STORE_SLICE_2, STORE_SLICE_3, - STORE_SUBSCR, UNPACK_SEQUENCE, JA - ]) - - prelim = self.all_instr(start, end, stmt_opcodes) - - stmts = self.stmts = set(prelim) - pass_stmts = set() - for seq in stmt_opcode_seqs: - for i in self.op_range(start, end-(len(seq)+1)): - match = True - for elem in seq: - if elem != code[i]: - match = False - break - i += self.op_size(code[i]) - - if match: - i = self.prev[i] - stmts.add(i) - pass_stmts.add(i) - - if pass_stmts: - stmt_list = list(stmts) - stmt_list.sort() - else: - stmt_list = prelim - last_stmt = -1 - self.next_stmt = [] - slist = self.next_stmt = [] - i = 0 - for s in stmt_list: - if code[s] == JA and s not in pass_stmts: - target = self.get_target(s) - if target > s or self.lines[last_stmt].l_no == self.lines[s].l_no: - stmts.remove(s) - continue - j = self.prev[s] - while code[j] == JA: - j = self.prev[j] - if code[j] == LIST_APPEND: # list comprehension - stmts.remove(s) - continue - elif code[s] == POP_TOP and code[self.prev[s]] == ROT_TWO: - stmts.remove(s) - continue - elif code[s] in designator_ops: - j = self.prev[s] - while code[j] in designator_ops: - j = self.prev[j] - if code[j] == FOR_ITER: - stmts.remove(s) - continue - last_stmt = s - slist += [s] * (s-i) - i = s - slist += [end] * (end-len(slist)) - - def next_except_jump(self, start): - ''' - Return the next jump that was generated by an except SomeException: - construct in a try...except...else clause or None if not found. - ''' - if self.code[start] == DUP_TOP: - except_match = self.first_instr(start, len(self.code), (PJIF)) - if except_match: - jmp = self.prev[self.get_target(except_match)] - self.ignore_if.add(except_match) - self.not_continue.add(jmp) - return jmp - - count_END_FINALLY = 0 - count_SETUP_ = 0 - for i in self.op_range(start, len(self.code)): - op = self.code[i] - if op == END_FINALLY: - if count_END_FINALLY == count_SETUP_: - if self.code[self.prev[i]] == NOP: - i = self.prev[i] - assert self.code[self.prev[i]] in (JA, JF, RETURN_VALUE) - self.not_continue.add(self.prev[i]) - return self.prev[i] - count_END_FINALLY += 1 - elif op in (SETUP_EXCEPT, SETUP_FINALLY): - count_SETUP_ += 1 - # return self.lines[start].next - def detect_structure(self, pos, op=None): ''' Detect type of block structures and their boundaries to fix optimizied jumps diff --git a/uncompyle6/scanners/scanner26.py b/uncompyle6/scanners/scanner26.py index dee8f97b..e3953bf7 100755 --- a/uncompyle6/scanners/scanner26.py +++ b/uncompyle6/scanners/scanner26.py @@ -13,6 +13,10 @@ use in deparsing. from xdis.bytecode import findlinestarts import uncompyle6.scanners.scanner2 as scan +# bytecode verification, verify(), uses JUMP_OPs from here +from xdis.opcodes import opcode_26 +JUMP_OPs = opcode_26.JUMP_OPs + class Scanner26(scan.Scanner2): def __init__(self): super(Scanner26, self).__init__(2.6) @@ -106,14 +110,7 @@ class Scanner26(scan.Scanner2): self.restructBytecode() codelen = len(self.code) - # mapping adresses of prev instruction - self.prev = [0] - for i in self.op_range(0, codelen): - op = self.code[i] - self.prev.append(i) - if self.op_hasArgument(op): - self.prev.append(i) - self.prev.append(i) + self.build_prev_op(codelen) self.load_asserts = set() for i in self.op_range(0, codelen): @@ -187,7 +184,7 @@ class Scanner26(scan.Scanner2): # verify uses 'pattr' for comparison, since 'attr' # now holds Code(const) and thus can not be used # for comparison (todo: think about changing this) - # pattr = 'code_object @ 0x%x %s->%s' %\ + # pattr = 'code_object @ 0x%x %s->%s' % \ # (id(const), const.co_filename, const.co_name) pattr = '' else: @@ -268,7 +265,7 @@ class Scanner26(scan.Scanner2): if opcode == self.opc.EXTENDED_ARG: raise NotImplementedError - # modification of some jump structure + # modification of some jump structures if opcode in (self.opc.PJIF, self.opc.PJIT, self.opc.JA, @@ -300,10 +297,11 @@ class Scanner26(scan.Scanner2): if len(toDel) > 0: return toDel return None - # raise_varags not realy handle for the moment + # raise_varagsis not really handled for the moment if opcode == self.opc.RAISE_VARARGS: if self.code[i+opsize] == self.opc.POP_TOP: return [i+opsize] + # modification of list structure if opcode == self.opc.BUILD_LIST: if (self.code[i+opsize] == self.opc.DUP_TOP and