diff --git a/test/bytecode_2.5/10_if_else_ternary.pyc b/test/bytecode_2.5/10_if_else_ternary.pyc deleted file mode 100644 index aad37cf5..00000000 Binary files a/test/bytecode_2.5/10_if_else_ternary.pyc and /dev/null differ diff --git a/uncompyle6/parser.py b/uncompyle6/parser.py index b880dc9e..bae4ec5e 100644 --- a/uncompyle6/parser.py +++ b/uncompyle6/parser.py @@ -200,10 +200,10 @@ class PythonParser(GenericASTBuilder): _jump ::= JUMP_BACK # Note: Python < 2.7 doesn't have POP_JUMP_IF ... + # FIXME: segregate 2.7+ + jmp_false ::= POP_JUMP_IF_FALSE - jmp_false ::= JUMP_IF_FALSE jmp_true ::= POP_JUMP_IF_TRUE - jmp_true ::= JUMP_IF_TRUE # Zero or more COME_FROM # loops can have this @@ -465,10 +465,14 @@ class PythonParser(GenericASTBuilder): _mklambda ::= load_closure mklambda _mklambda ::= mklambda + # Note: Python < 2.7 doesn't have *POP* or this. Remove from here? + # FIXME: segregate 2.7+ + or ::= expr JUMP_IF_TRUE_OR_POP expr COME_FROM + and ::= expr JUMP_IF_FALSE_OR_POP expr COME_FROM + or ::= expr jmp_true expr come_from_opt and ::= expr jmp_false expr come_from_opt - and ::= expr JUMP_IF_FALSE_OR_POP expr COME_FROM and2 ::= _jump jmp_false COME_FROM expr COME_FROM expr ::= conditional @@ -485,7 +489,9 @@ class PythonParser(GenericASTBuilder): ret_expr_or_cond ::= ret_cond ret_expr_or_cond ::= ret_cond_not - # Note: Python < 2.7 doesn't use this. Remove from here? + # Note: Python < 2.7 doesn't have *POP* or this. Remove from here? + # FIXME: segregate 2.7+ + ret_and ::= expr JUMP_IF_FALSE_OR_POP ret_expr_or_cond COME_FROM ret_or ::= expr JUMP_IF_TRUE_OR_POP ret_expr_or_cond COME_FROM ret_cond ::= expr POP_JUMP_IF_FALSE expr RETURN_END_IF ret_expr_or_cond @@ -589,6 +595,13 @@ def get_python_parser(version, debug_parser, compile_mode='exec'): p = parse23.Python23Parser(debug_parser) else: p = parse23.Python23ParserSingle(debug_parser) + elif version == 2.5: + # For now, we'll consider 2.5 exactly like 2.6 + import uncompyle6.parsers.parse26 as parse25 + if compile_mode == 'exec': + p = parse25.Python26Parser(debug_parser) + else: + p = parse25.Python26ParserSingle(debug_parser) elif version == 2.6: import uncompyle6.parsers.parse26 as parse26 if compile_mode == 'exec': diff --git a/uncompyle6/parsers/parse26.py b/uncompyle6/parsers/parse26.py index dc28617a..aef40cb2 100644 --- a/uncompyle6/parsers/parse26.py +++ b/uncompyle6/parsers/parse26.py @@ -107,6 +107,10 @@ class Python26Parser(Python2Parser): ret_cond ::= expr jmp_false expr RETURN_END_IF come_from_pop ret_expr_or_cond ret_cond ::= expr jmp_false expr ret_expr_or_cond ret_cond_not ::= expr jmp_true expr RETURN_END_IF come_from_pop ret_expr_or_cond + + # FIXME: split into Python 2.5 + ret_cond ::= expr jmp_false expr JUMP_RETURN come_from_pop ret_expr_or_cond + ret_or ::= expr jmp_true ret_expr_or_cond come_froms ''' def p_except26(self, args): @@ -114,6 +118,12 @@ class Python26Parser(Python2Parser): except_suite ::= c_stmts_opt jmp_abs new_block ''' + def p_jump26(self, args): + """ + jmp_false ::= JUMP_IF_FALSE + jmp_true ::= JUMP_IF_TRUE + """ + class Python26ParserSingle(Python2Parser, PythonParserSingle): pass diff --git a/uncompyle6/parsers/parse27.py b/uncompyle6/parsers/parse27.py index 2a3ccdc0..7bedde85 100644 --- a/uncompyle6/parsers/parse27.py +++ b/uncompyle6/parsers/parse27.py @@ -28,7 +28,7 @@ class Python27Parser(Python2Parser): _ifstmts_jump ::= c_stmts_opt JUMP_FORWARD COME_FROM """ -class Python26ParserSingle(Python2Parser, PythonParserSingle): +class Python27ParserSingle(Python27Parser, PythonParserSingle): pass if __name__ == '__main__': diff --git a/uncompyle6/scanners/scanner2.py b/uncompyle6/scanners/scanner2.py index 46ef57f9..a7287c0b 100755 --- a/uncompyle6/scanners/scanner2.py +++ b/uncompyle6/scanners/scanner2.py @@ -188,7 +188,6 @@ class Scanner2(scan.Scanner): and self.code[offset+3] not in (self.opc.END_FINALLY, self.opc.POP_BLOCK) and offset not in self.not_continue): - print("WOOT", target, offset, self.stmts) opname = 'CONTINUE' else: opname = 'JUMP_BACK' @@ -481,7 +480,6 @@ class Scanner2(scan.Scanner): target = self.get_target(pos, op) end = self.restrict_to_parent(target, parent) if target != end: - print("XXXX", pos, end) self.fixed_jumps[pos] = end # print target, end, parent # Add the try block @@ -517,7 +515,7 @@ class Scanner2(scan.Scanner): if end_else != start_else: r_end_else = self.restrict_to_parent(end_else, parent) # May be able to drop the 2.7 test. - if self.version == 2.7 and i > r_end_else: + if self.version == 2.7: self.structs.append({'type': 'try-else', 'start': i+1, 'end': r_end_else}) diff --git a/uncompyle6/scanners/scanner25.py b/uncompyle6/scanners/scanner25.py index 86a0ffc4..2de5882b 100755 --- a/uncompyle6/scanners/scanner25.py +++ b/uncompyle6/scanners/scanner25.py @@ -70,350 +70,3 @@ class Scanner25(scan.Scanner26): # Add an empty set make processing more uniform. self.pop_jump_if_or_pop = frozenset([]) return - - def restructCode(self, listDel, listExp): - ''' - restruct linestarts and jump destination - ''' - # restruct linestarts with deleted / modificated opcode - result = list() - for block in self.linestarts: - startBlock = 0 - for toDel in listDel: - if toDel < block[0]: - startBlock -= self.op_size(self.code[toDel]) - for toExp in listExp: - if toExp < block[0]: - startBlock += 2 - result.append((block[0]+startBlock, block[1])) - self.linestarts = result - # handle opcodeToChange deplacement - for index in range(len(self.toChange)): - change = self.toChange[index] - delta = 0 - for toDel in listDel: - if change > toDel: - delta -= self.op_size(self.code[toDel]) - for toExp in listExp: - if change > toExp: - delta += 2 - self.toChange[index] += delta - # restruct jmp opcode - if listDel: - for jmp in self.op_range(0, len(self.code)): - op = self.code[jmp] - if op in self.opc.hasjrel + self.opc.hasjabs: - offset = 0 - jmpTarget = self.get_target(jmp) - for toDel in listDel: - if toDel < jmpTarget: - if op in self.opc.hasjabs or jmp < toDel: - offset-=self.op_size(self.code[toDel]) - self.restructJump(jmp, jmpTarget+offset) - if listExp: - jmp = 0 - while jmp < len(self.code): # we can't use op_range for the moment - op = self.code[jmp] - if op in self.opc.hasjrel + self.opc.hasjabs: - offset = 0 - jmpTarget = self.get_target(jmp) - for toExp in listExp: - if toExp < jmpTarget: - if op in self.opc.hasjabs or jmp < toExp: - offset+=2 - self.restructJump(jmp, jmpTarget+offset) - if self.op_hasArgument(op) and op not in self.opc.hasArgumentExtended: - jmp += 3 - else: jmp += 1 - - def restructBytecode(self): - ''' - add/change/delete bytecode for suiting bytecode 2.7 - ''' - # we can't use op_range for the moment - # convert jump opcode to 2.7 - self.restructRelativeJump() - - listExp = self.getOpcodeToExp() - # change code structure - if listExp: - listExp = sorted(list(set(listExp))) - self.restructCode([], listExp) - # we add arg to expended opcode - offset=0 - for toExp in listExp: - self.code.insert(toExp+offset+1, 0) - self.code.insert(toExp+offset+1, 0) - offset+=2 - # op_range is now ok :) - # add instruction to change in "toChange" list + MAJ toDel - listDel = [] - for i in self.op_range(0, len(self.code)): - ret = self.getOpcodeToDel(i) - if ret is not None: - listDel += ret - - # change code structure after deleting byte - if listDel: - listDel = sorted(list(set(listDel))) - self.restructCode(listDel, []) - # finaly we delete useless opcode - delta = 0 - for x in listDel: - if self.op_hasArgument(self.code[x-delta]): - self.code.pop(x-delta) - self.code.pop(x-delta) - self.code.pop(x-delta) - delta += 3 - else: - self.code.pop(x-delta) - delta += 1 - - def detect_structure(self, pos, op=None): - ''' - Detect type of block structures and their boundaries to fix optimizied jumps - in python2.3+ - ''' - - code = self.code - # Ev remove this test and make op a mandatory argument -Dan - if op is None: - op = code[pos] - - # Detect parent structure - parent = self.structs[0] - start = parent['start'] - end = parent['end'] - for s in self.structs: - _start = s['start'] - _end = s['end'] - if (_start <= pos < _end) and (_start >= start and _end <= end): - start = _start - end = _end - parent = s - # We need to know how many new structures were added in this run - - - if op == self.opc.SETUP_LOOP: - start = pos+3 - target = self.get_target(pos, op) - end = self.restrict_to_parent(target, parent) - - if target != end: - self.fixed_jumps[pos] = end - - (line_no, next_line_byte) = self.lines[pos] - jump_back = self.last_instr(start, end, self.opc.JA, next_line_byte, False) - if jump_back and jump_back != self.prev[end] and code[jump_back+3] in (JA, JF): - if code[self.prev[end]] == RETURN_VALUE or \ - (code[self.prev[end]] == POP_BLOCK and code[self.prev[self.prev[end]]] == RETURN_VALUE): - jump_back = None - if not jump_back: # loop suite ends in return. wtf right? - jump_back = self.last_instr(start, end, RETURN_VALUE) - if not jump_back: - return - jump_back += 1 - if code[self.prev[next_line_byte]] not in self.pop_jump_if: - loop_type = 'for' - else: - loop_type = 'while' - self.ignore_if.add(self.prev[next_line_byte]) - target = next_line_byte - end = jump_back + 3 - else: - if self.get_target(jump_back) >= next_line_byte: - jump_back = self.last_instr(start, end, JA, - start, False) - if end > jump_back+4 and code[end] in (JF, JA): - if code[jump_back+4] in (JA, JF): - if self.get_target(jump_back+4) == self.get_target(end): - self.fixed_jumps[pos] = jump_back+4 - end = jump_back+4 - elif target < pos: - self.fixed_jumps[pos] = jump_back+4 - end = jump_back+4 - - target = self.get_target(jump_back, self.opc.JA) - - if code[target] in (self.opc.FOR_ITER, self.opc.GET_ITER): - loop_type = 'for' - else: - loop_type = 'while' - test = self.prev[next_line_byte] - if test == pos: - loop_type = 'while 1' - elif self.code[test] in self.opc.hasjabs + self.opc.hasjrel: - self.ignore_if.add(test) - test_target = self.get_target(test) - if test_target > (jump_back+3): - jump_back = test_target - self.not_continue.add(jump_back) - self.loops.append(target) - self.structs.append({'type': loop_type + '-loop', - 'start': target, - 'end': jump_back}) - if jump_back+3 != end: - self.structs.append({'type': loop_type + '-else', - 'start': jump_back+3, - 'end': end}) - elif op == self.opc.SETUP_EXCEPT: - start = pos+3 - target = self.get_target(pos, op) - end = self.restrict_to_parent(target, parent) - if target != end: - self.fixed_jumps[pos] = end - # Add the try block - self.structs.append({'type': 'try', - 'start': start, - 'end': end-4}) - # Now isolate the except and else blocks - end_else = start_else = self.get_target(self.prev[end]) - - # Add the except blocks - i = end - while i < len(self.code) and self.code[i] != self.opc.END_FINALLY: - jmp = self.next_except_jump(i) - if jmp is None: # check - i = self.next_stmt[i] - continue - if self.code[jmp] == self.opc.RETURN_VALUE: - self.structs.append({'type': 'except', - 'start': i, - 'end': jmp+1}) - i = jmp + 1 - else: - if self.get_target(jmp) != start_else: - end_else = self.get_target(jmp) - if self.code[jmp] == self.opc.JF: - self.fixed_jumps[jmp] = -1 - self.structs.append({'type': 'except', - 'start': i, - 'end': jmp}) - i = jmp + 3 - - # Add the try-else block - if end_else != start_else: - r_end_else = self.restrict_to_parent(end_else, parent) - self.structs.append({'type': 'try-else', - 'start': i+2, # check - 'end': r_end_else}) - self.fixed_jumps[i] = r_end_else - else: - self.fixed_jumps[i] = i+1 - - elif op in self.pop_jump_if: - start = pos+3 - target = self.get_target(pos, op) - rtarget = self.restrict_to_parent(target, parent) - pre = self.prev - - if target != rtarget and parent['type'] == 'and/or': - self.fixed_jumps[pos] = rtarget - return - # does this jump to right after another cond jump? - # if so, it's part of a larger conditional - if (code[pre[target]] in (PJIF, PJIT)) and (target > pos): - self.fixed_jumps[pos] = pre[target] - self.structs.append({'type': 'and/or', - 'start': start, - 'end': pre[target]}) - return - - # is this an if and - if op == PJIF: - match = self.rem_or(start, self.next_stmt[pos], PJIF, target) - match = self.remove_mid_line_ifs(match) - if match: - if code[pre[rtarget]] in (JF, JA) \ - and pre[rtarget] not in self.stmts \ - and self.restrict_to_parent(self.get_target(pre[rtarget]), parent) == rtarget: - if code[pre[pre[rtarget]]] == JA \ - and self.remove_mid_line_ifs([pos]) \ - and target == self.get_target(pre[pre[rtarget]]) \ - and (pre[pre[rtarget]] not in self.stmts or self.get_target(pre[pre[rtarget]]) > pre[pre[rtarget]])\ - and 1 == len(self.remove_mid_line_ifs(self.rem_or(start, pre[pre[rtarget]], (PJIF, PJIT), target))): - pass - elif code[pre[pre[rtarget]]] == RETURN_VALUE \ - and self.remove_mid_line_ifs([pos]) \ - and 1 == (len(set(self.remove_mid_line_ifs(self.rem_or(start, pre[pre[rtarget]], - (PJIF, PJIT), target))) - | set(self.remove_mid_line_ifs(self.rem_or(start, pre[pre[rtarget]], - (PJIF, PJIT, JA), pre[rtarget], True))))): - pass - else: - fix = None - jump_ifs = self.all_instr(start, self.next_stmt[pos], PJIF) - last_jump_good = True - for j in jump_ifs: - if target == self.get_target(j): - if self.lines[j].next == j+3 and last_jump_good: - fix = j - break - else: - last_jump_good = False - self.fixed_jumps[pos] = fix or match[-1] - return - elif pos < rtarget and code[target] == self.opc.ROT_TWO: - self.fixed_jumps[pos] = target - return - else: - self.fixed_jumps[pos] = match[-1] - return - else: # op == PJIT - if (pos+3) in self.load_asserts: - if code[pre[rtarget]] == self.opc.RAISE_VARARGS: - return - self.load_asserts.remove(pos+3) - - next = self.next_stmt[pos] - if pre[next] == pos: - pass - elif code[next] in (JF, JA) and target == self.get_target(next): - if code[pre[next]] == PJIF: - if code[next] == JF or target != rtarget or code[pre[pre[rtarget]]] not in (JA, RETURN_VALUE): - self.fixed_jumps[pos] = pre[next] - return - elif code[next] == JA and code[target] in (JA, JF) \ - and self.get_target(target) == self.get_target(next): - self.fixed_jumps[pos] = pre[next] - return - # don't add a struct for a while test, it's already taken care of - if pos in self.ignore_if: - return - - if code[pre[rtarget]] == JA and pre[rtarget] in self.stmts \ - and pre[rtarget] != pos and pre[pre[rtarget]] != pos \ - and not (code[rtarget] == JA and code[rtarget+3] == POP_BLOCK and code[pre[pre[rtarget]]] != JA): - rtarget = pre[rtarget] - # does the if jump just beyond a jump op, then this is probably an if statement - if code[pre[rtarget]] in (JA, JF): - if_end = self.get_target(pre[rtarget]) - - # is this a loop not an if? - if (if_end < pre[rtarget]) and (code[pre[if_end]] == self.opc.SETUP_LOOP): - if(if_end > start): - return - - end = self.restrict_to_parent(if_end, parent) - - self.structs.append({'type': 'if-then', - 'start': start, - 'end': pre[rtarget]}) - self.not_continue.add(pre[rtarget]) - - if rtarget < end: - self.structs.append({'type': 'if-else', - 'start': rtarget, - 'end': end}) - elif code[pre[rtarget]] == self.opc.RETURN_VALUE: - # if it's an old JUMP_IF_FALSE_OR_POP, JUMP_IF_TRUE_OR_POP (return 1<2<3 case) - if pos < rtarget and code[rtarget] == self.opc.ROT_TWO: - return - self.structs.append({'type': 'if-then', - 'start': start, - 'end': rtarget}) - self.return_end_ifs.add(pre[rtarget]) - pass - pass - return - pass diff --git a/uncompyle6/scanners/scanner26.py b/uncompyle6/scanners/scanner26.py index 553d9fcf..616237f1 100755 --- a/uncompyle6/scanners/scanner26.py +++ b/uncompyle6/scanners/scanner26.py @@ -212,6 +212,12 @@ class Scanner26(scan.Scanner2): pattr = names[oparg] elif op in self.opc.hasjrel: pattr = repr(offset + 3 + oparg) + if op == self.opc.JUMP_FORWARD: + target = self.get_target(offset) + if target > offset and self.code[target] == self.opc.RETURN_VALUE: + # Python 2.5 sometimes has this + op_name = 'JUMP_RETURN' + elif op in self.opc.hasjabs: pattr = repr(oparg) elif op in self.opc.haslocal: