diff --git a/test/bytecode_2.6/25_try_except.pyc b/test/bytecode_2.6/25_try_except.pyc index ebb3a8a9..0f2cee80 100644 Binary files a/test/bytecode_2.6/25_try_except.pyc and b/test/bytecode_2.6/25_try_except.pyc differ diff --git a/test/bytecode_2.7/25_try_except.pyc b/test/bytecode_2.7/25_try_except.pyc index f5cde2e0..0a67b044 100644 Binary files a/test/bytecode_2.7/25_try_except.pyc and b/test/bytecode_2.7/25_try_except.pyc differ diff --git a/uncompyle6/parser.py b/uncompyle6/parser.py index 10cbfb71..46be2dcb 100644 --- a/uncompyle6/parser.py +++ b/uncompyle6/parser.py @@ -594,6 +594,12 @@ def get_python_parser(version, debug_parser, compile_mode='exec'): p = parse26.Python26Parser(debug_parser) else: p = parse26.Python26ParserSingle(debug_parser) + elif version == 2.7: + import uncompyle6.parsers.parse27 as parse27 + if compile_mode == 'exec': + p = parse27.Python27Parser(debug_parser) + else: + p = parse27.Python27ParserSingle(debug_parser) else: import uncompyle6.parsers.parse2 as parse2 if compile_mode == 'exec': diff --git a/uncompyle6/parsers/astnode.py b/uncompyle6/parsers/astnode.py index 19cacdfc..7c66502d 100644 --- a/uncompyle6/parsers/astnode.py +++ b/uncompyle6/parsers/astnode.py @@ -6,7 +6,6 @@ from spark_parser.ast import AST as spark_AST if PYTHON3: intern = sys.intern - class AST(spark_AST): def isNone(self): """An AST None token. We can't use regular list comparisons diff --git a/uncompyle6/parsers/parse2.py b/uncompyle6/parsers/parse2.py index 4f269398..81dca720 100644 --- a/uncompyle6/parsers/parse2.py +++ b/uncompyle6/parsers/parse2.py @@ -134,7 +134,6 @@ class Python2Parser(PythonParser): classdefdeco1 ::= expr classdefdeco2 CALL_FUNCTION_1 classdefdeco2 ::= LOAD_CONST expr mkfunc CALL_FUNCTION_0 BUILD_CLASS - assert ::= assert_expr jmp_true LOAD_ASSERT RAISE_VARARGS_1 assert2 ::= assert_expr jmp_true LOAD_ASSERT expr CALL_FUNCTION_1 RAISE_VARARGS_1 assert2 ::= assert_expr jmp_true LOAD_ASSERT expr RAISE_VARARGS_2 @@ -152,7 +151,6 @@ class Python2Parser(PythonParser): testtrue ::= expr jmp_true _ifstmts_jump ::= return_if_stmts - _ifstmts_jump ::= c_stmts_opt JUMP_FORWARD COME_FROM iflaststmt ::= testexpr c_stmts_opt JUMP_ABSOLUTE @@ -167,9 +165,6 @@ class Python2Parser(PythonParser): ifelsestmtl ::= testexpr c_stmts_opt JUMP_BACK else_suitel - trystmt ::= SETUP_EXCEPT suite_stmts_opt POP_BLOCK - try_middle COME_FROM - # this is nested inside a trystmt tryfinallystmt ::= SETUP_FINALLY suite_stmts_opt POP_BLOCK LOAD_CONST @@ -184,13 +179,6 @@ class Python2Parser(PythonParser): tryelsestmtl ::= SETUP_EXCEPT suite_stmts_opt POP_BLOCK try_middle else_suitel COME_FROM - try_middle ::= jmp_abs COME_FROM except_stmts - END_FINALLY - - ## FIXME: this might be a 2.7+ thing only - try_middle ::= JUMP_FORWARD COME_FROM except_stmts - END_FINALLY COME_FROM - except_stmts ::= except_stmts except_stmt except_stmts ::= except_stmt @@ -202,14 +190,6 @@ class Python2Parser(PythonParser): except_suite ::= c_stmts_opt jmp_abs except_suite ::= return_stmts - ## FIXME: this might be a 2.7+ thing only - except_cond1 ::= DUP_TOP expr COMPARE_OP - jmp_false POP_TOP POP_TOP POP_TOP - - ## FIXME: this might be a 2.7+ thing only - except_cond2 ::= DUP_TOP expr COMPARE_OP - jmp_false POP_TOP designator POP_TOP - except ::= POP_TOP POP_TOP POP_TOP c_stmts_opt _jump except ::= POP_TOP POP_TOP POP_TOP return_stmts diff --git a/uncompyle6/parsers/parse26.py b/uncompyle6/parsers/parse26.py index fb3d9524..199c7293 100644 --- a/uncompyle6/parsers/parse26.py +++ b/uncompyle6/parsers/parse26.py @@ -16,16 +16,42 @@ class Python26Parser(Python2Parser): def p_try_except26(self, args): """ - # FIXME: move parse2's corresponding rules to 2.7 - except_cond1 ::= DUP_TOP expr COMPARE_OP JUMP_IF_FALSE POP_TOP POP_TOP POP_TOP POP_TOP except_cond2 ::= DUP_TOP expr COMPARE_OP JUMP_IF_FALSE POP_TOP POP_TOP designator POP_TOP try_middle ::= JUMP_FORWARD COME_FROM except_stmts - POP_TOP END_FINALLY COME_FROM + POP_TOP END_FINALLY come_froms + try_middle ::= JUMP_FORWARD COME_FROM except_stmts + END_FINALLY come_froms + try_middle ::= jmp_abs COME_FROM except_stmts + POP_TOP END_FINALLY + + trystmt ::= SETUP_EXCEPT suite_stmts_opt POP_BLOCK + try_middle + + except_suite ::= c_stmts_opt JUMP_FORWARD POP_TOP + + # Python 3 also has this. + come_froms ::= come_froms COME_FROM + come_froms ::= COME_FROM + """ + def p_misc26(self, args): + """ + jmp_true ::= JUMP_IF_TRUE POP_TOP + jmp_false ::= JUMP_IF_FALSE POP_TOP + jf_pop ::= JUMP_FORWARD POP_TOP + + _ifstmts_jump ::= c_stmts_opt jf_pop COME_FROM + """ + + def p_stmt26(self, args): + """ + assert ::= assert_expr jmp_true LOAD_ASSERT RAISE_VARARGS_1 POP_TOP + ifelsestmt ::= testexpr c_stmts_opt jf_pop else_suite COME_FROM + """ def p_comp26(self, args): ''' @@ -33,8 +59,6 @@ class Python26Parser(Python2Parser): list_compr ::= BUILD_LIST_0 DUP_TOP designator list_iter del_stmt lc_body ::= LOAD_NAME expr LIST_APPEND - - ''' class Python26ParserSingle(Python2Parser, PythonParserSingle): diff --git a/uncompyle6/parsers/parse27.py b/uncompyle6/parsers/parse27.py new file mode 100644 index 00000000..2a0e132c --- /dev/null +++ b/uncompyle6/parsers/parse27.py @@ -0,0 +1,40 @@ +from uncompyle6.parser import PythonParserSingle +from spark_parser import DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG +from uncompyle6.parsers.parse2 import Python2Parser + +class Python27Parser(Python2Parser): + + def __init__(self, debug_parser=PARSER_DEFAULT_DEBUG): + super(Python27Parser, self).__init__(debug_parser) + self.customized = {} + + def p_try27(self, args): + """ + trystmt ::= SETUP_EXCEPT suite_stmts_opt POP_BLOCK + try_middle COME_FROM + + try_middle ::= JUMP_FORWARD COME_FROM except_stmts + END_FINALLY COME_FROM + try_middle ::= jmp_abs COME_FROM except_stmts + END_FINALLY + + except_cond1 ::= DUP_TOP expr COMPARE_OP + jmp_false POP_TOP POP_TOP POP_TOP + + except_cond2 ::= DUP_TOP expr COMPARE_OP + jmp_false POP_TOP designator POP_TOP + """ + + def p_misc27(self, args): + """ + assert ::= assert_expr jmp_true LOAD_ASSERT RAISE_VARARGS_1 + _ifstmts_jump ::= c_stmts_opt JUMP_FORWARD COME_FROM + """ + +class Python26ParserSingle(Python2Parser, PythonParserSingle): + pass + +if __name__ == '__main__': + # Check grammar + p = Python27Parser() + p.checkGrammar() diff --git a/uncompyle6/scanners/scanner2.py b/uncompyle6/scanners/scanner2.py index 898669c2..1c00120c 100755 --- a/uncompyle6/scanners/scanner2.py +++ b/uncompyle6/scanners/scanner2.py @@ -359,6 +359,12 @@ class Scanner2(scan.Scanner): except_match = self.first_instr(start, len(self.code), self.opc.PJIF) if except_match: jmp = self.prev[self.get_target(except_match)] + + # In Python <= 2.6 we may have jumps to jumps + if self.version <= 2.6 and self.code[jmp] in self.jump_forward: + self.not_continue.add(jmp) + jmp = self.get_target(jmp) + self.ignore_if.add(except_match) self.not_continue.add(jmp) return jmp @@ -370,7 +376,8 @@ class Scanner2(scan.Scanner): if op == self.opc.END_FINALLY: if count_END_FINALLY == count_SETUP_: if self.version == 2.7: - assert self.code[self.prev[i]] in (self.opc.JA, self.opc.JF, self.opc.RETURN_VALUE) + assert self.code[self.prev[i]] in \ + self.jump_forward | frozenset([self.opc.RETURN_VALUE]) self.not_continue.add(self.prev[i]) return self.prev[i] count_END_FINALLY += 1 @@ -474,6 +481,7 @@ 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 @@ -508,10 +516,12 @@ class Scanner2(scan.Scanner): # 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+1, - 'end': r_end_else}) - self.fixed_jumps[i] = r_end_else + # May be able to drop the 2.7 test. + if self.version == 2.7 and i > r_end_else: + self.structs.append({'type': 'try-else', + 'start': i+1, + 'end': r_end_else}) + self.fixed_jumps[i] = r_end_else else: self.fixed_jumps[i] = i+1 diff --git a/uncompyle6/scanners/scanner26.py b/uncompyle6/scanners/scanner26.py index d0b18173..553d9fcf 100755 --- a/uncompyle6/scanners/scanner26.py +++ b/uncompyle6/scanners/scanner26.py @@ -81,7 +81,6 @@ class Scanner26(scan.Scanner2): bytecode = Bytecode(co, self.opc) for instr in bytecode.get_instructions(co): print(instr._disassemble()) - show_asm = 'both' # from xdis.bytecode import Bytecode # bytecode = Bytecode(co, self.opc) @@ -130,9 +129,12 @@ class Scanner26(scan.Scanner2): 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) + if (self.code[i] == self.opc.JUMP_IF_TRUE and + i + 4 < codelen and + self.code[i+3] == self.opc.POP_TOP and + self.code[i+4] == self.opc.LOAD_GLOBAL): + if names[self.get_argument(i+4)] == 'AssertionError': + self.load_asserts.add(i+4) cf = self.find_jump_targets() # contains (code, [addrRefToCode]) @@ -270,553 +272,6 @@ class Scanner26(scan.Scanner2): print() 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 == self.opc.EXTENDED_ARG: - raise NotImplementedError - - # modification of some jump structures - if opcode in (self.opc.PJIF, - self.opc.PJIT, - self.opc.JA, - self.opc.JF, - self.opc.RETURN_VALUE): - toDel = [] - # del POP_TOP - if self.code[i+opsize] == self.opc.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 self.jump_forward and self.code[i+opsize] != self.code[i+opsize+3]: - pass - else: - toDel += [i+opsize] - # conditional tuple (not optimal at all, no good solution...) - if self.code[i] == self.opc.JA and self.code[i+opsize] == self.opc.POP_TOP \ - and self.code[i+opsize+1] == self.opc.JA and self.code[i+opsize+4] == self.opc.POP_BLOCK: - jmpabs1target = self.get_target(i) - jmpabs2target = self.get_target(i+opsize+1) - if jmpabs1target == jmpabs2target and self.code[jmpabs1target] == self.opc.FOR_ITER \ - and self.code[jmpabs1target-1] != self.opc.GET_ITER: - destFor = self.get_target(jmpabs1target) - if destFor == i+opsize+4: - setupLoop = self.last_instr(0, jmpabs1target, self.opc.SETUP_LOOP) - standarFor = self.last_instr(setupLoop, jmpabs1target, self.opc.GET_ITER) - if standarFor is None: - self.restructJump(jmpabs1target, destFor+self.op_size(self.opc.POP_BLOCK)) - toDel += [setupLoop, i+opsize+1, i+opsize+4] - - if len(toDel) > 0: - return toDel - return None - # 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 - self.code[i+opsize+1] in (self.opc.STORE_NAME, self.opc.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), (self.opc.LOAD_NAME, self.opc.LOAD_FAST)) - if nameDel == self.get_argument(end): - toDel += [end] - break - if self.code[end] == self.opc.LOAD_NAME: - end += self.op_size(self.opc.LOAD_NAME) - else: - end += self.op_size(self.opc.LOAD_FAST) - # log JA/POP_TOP to del and update PJIF - while start < end: - start = self.first_instr(start, end, self.pop_jump_if) - if start is None: break - target = self.get_target(start) - if self.code[target] == self.opc.POP_TOP and self.code[target-3] == self.opc.JA: - toDel += [target, target-3] - # update PJIF - target = self.get_target(target-3) - self.restructJump(start, target) - start += self.op_size(self.opc.PJIF) - # del DELETE_NAME x - start = end - while end < len(self.code): - end = self.first_instr(end, len(self.code), - (self.opc.DELETE_NAME, self.opc.DELETE_FAST)) - if end: - if nameDel == self.get_argument(end): - toDel += [end] - break - if self.code[end] == self.opc.DELETE_NAME: - end += self.op_size(self.opc.DELETE_NAME) - else: - end += self.op_size(self.opc.DELETE_FAST) - return toDel - # for / while struct - if opcode == self.opc.SETUP_LOOP: - # change join(for..) struct - if self.code[i+3] == self.opc.LOAD_FAST and self.code[i+6] == self.opc.FOR_ITER: - end = self.first_instr(i, len(self.code), self.opc.RETURN_VALUE) - end = self.first_instr(i, end, self.opc.YIELD_VALUE) - if end and self.code[end+1] == self.opc.POP_TOP and self.code[end+2] == self.opc.JA and self.code[end+5] == self.opc.POP_BLOCK: - return [i, end+5] - # with stmt - if opcode == self.opc.WITH_CLEANUP: - allRot = self.all_instr(0, i, (self.opc.ROT_TWO)) - chckRot = -1 - for rot in allRot: - if self.code[rot+1] == self.opc.LOAD_ATTR and self.code[rot-3] == self.opc.LOAD_ATTR \ - and self.code[rot-4] == self.opc.DUP_TOP: - chckRot = rot - assert chckRot > 0 - toDel = [chckRot-4, chckRot-3, chckRot] - chckStp = -1 - allSetup = self.all_instr(chckRot+1, i, (self.opc.SETUP_FINALLY)) - for stp in allSetup: - if i == self.get_target(stp): - chckStp = stp - assert chckStp > 0 - toDel += [chckStp] - chckDel = chckRot+1+self.op_size(self.code[chckRot+1]) - while chckDel < chckStp-3: - toDel += [chckDel] - chckDel += self.op_size(self.code[chckDel]) - if (self.code[chckStp-3] in (self.opc.STORE_NAME, self.opc.STORE_FAST) and - self.code[chckStp+3] in (self.opc.LOAD_NAME, self.opc.LOAD_FAST) - and self.code[chckStp+6] in (self.opc.DELETE_NAME, self.opc.DELETE_FAST)): - toDel += [chckStp-3, chckStp+3, chckStp+6] - # SETUP_WITH opcode dosen't exist in 2.6 but is necessary for the grammar - self.code[chckRot+1] = self.opc.JUMP_ABSOLUTE # ugly hack - self.restructJump(chckRot+1, i) - self.toChange.append(chckRot+1) - return toDel - if opcode == self.opc.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 - - def restructCode(self, listDel, listExp): - ''' - restruct linestarts and jump destination after converting bytecode - ''' - # 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 restructRelativeJump(self): - # ''' - # change relative JUMP_IF_FALSE/TRUE to absolut jump - # and remap the target of PJIF/PJIT - # ''' - # i=0 - # while i < len(self.code): # we can't use op_range for the moment - # op = self.code[i] - # if op in self.pop_jump_if: - # target = self.get_argument(i) - # target += i + 3 - # self.restructJump(i, target) - # i += self.op_size(op) - - # i=0 - # while i < len(self.code): # we can't use op_range for the moment - # op = self.code[i] - # if op in self.pop_jump_if: - # target = self.get_target(i) - # if self.code[target] == self.opc.JA: - # target = self.get_target(target) - # self.restructJump(i, target) - # i += self.op_size(op) - # i=0 - # # while i < len(self.code): # we can't use op_range for the moment - # # op = self.code[i] - # # name = self.opc.opname[op] - # # if self.op_hasArgument(op): - # # oparg = self.get_argument(i) - # # print("%d %s %d" % (i, name, oparg)) - # # else: - # # print("%d %s" % (i, name)) - # # i += self.op_size(op) - - # def restructJump(self, pos, newTarget): - # if self.code[pos] not in self.opc.hasjabs + self.opc.hasjrel: - # raise 'Can t change this argument. Opcode is not a jump' - # if newTarget > 0xFFFF: - # raise NotImplementedError - # offset = newTarget-self.get_target(pos) - # target = self.get_argument(pos)+offset - # if target < 0 or target > 0xFFFF: - # raise NotImplementedError - # self.code[pos+2] = (target >> 8) & 0xFF - # self.code[pos+1] = target & 0xFF - - def detect_structure(self, pos, op=None): - ''' - Detect type of block structures and their boundaries to fix optimized jumps - in python2.3+ - ''' - - # TODO: check the struct boundaries more precisely -Dan - - 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 - - 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 self.jump_forward): - if (code[self.prev[end]] == self.opc.RETURN_VALUE - or (code[self.prev[end]] == self.opc.POP_BLOCK - and code[self.prev[self.prev[end]]] == self.opc.RETURN_VALUE)): - jump_back = None - if not jump_back: # loop suite ends in return. wtf right? - jump_back = self.last_instr(start, end, self.opc.JA, start, False) - 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, self.opc.JA, start, False) - if end > jump_back + 4 and code[end] in self.jump_forward: - if code[jump_back + 4] in (self.opc.JA, self.opc.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 - # print target, end, parent - # 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 self.pop_jump_if 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 == self.opc.PJIF: - match = self.rem_or(start, self.next_stmt[pos], self.opc.PJIF, target) - ## We can't remove mid-line ifs because line structures have changed - ## from restructBytecode(). - ## match = self.remove_mid_line_ifs(match) - if match: - if (code[pre[rtarget]] in (self.opc.JF, self.opc.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]]] == self.opc.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]], self.pop_jump_if, target)))): - pass - elif code[pre[pre[rtarget]]] == self.opc.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]], - self.pop_jump_if, target))) - | set(self.remove_mid_line_ifs(self.rem_or(start, pre[pre[rtarget]], - (self.opc.PJIF, self.opc.PJIT, self.opc.JA), pre[rtarget], True))))): - pass - else: - fix = None - jump_ifs = self.all_instr(start, self.next_stmt[pos], self.opc.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 self.jump_forward and target == self.get_target(next): - if code[pre[next]] == self.opc.PJIF: - if code[next] == self.opc.JF or target != rtarget or code[pre[pre[rtarget]]] not in (self.opc.JA, self.opc.RETURN_VALUE): - self.fixed_jumps[pos] = pre[next] - return - elif code[next] == self.opc.JA and code[target] in self.opc.jump_foward \ - 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]] == self.opc.JA and pre[rtarget] in self.stmts \ - and pre[rtarget] != pos and pre[pre[rtarget]] != pos \ - and not (code[rtarget] == self.opc.JA and code[rtarget+3] == self.opc.POP_BLOCK and code[pre[pre[rtarget]]] != self.opc.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 (self.opc.JA, self.opc.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 - if __name__ == "__main__": from uncompyle6 import PYTHON_VERSION if PYTHON_VERSION == 2.6: diff --git a/uncompyle6/semantics/fragments.py b/uncompyle6/semantics/fragments.py index ec018da3..d14f573b 100644 --- a/uncompyle6/semantics/fragments.py +++ b/uncompyle6/semantics/fragments.py @@ -27,10 +27,10 @@ node 2 range information, it in %c, is copied to nodes 0 and 1. 2. %r ----- - %n associates recursively location information for the string that follows + %r associates recursively location information for the string that follows For example in: - 'break_stmt': ( '%|%nbreak\n', ), + 'break_stmt': ( '%|%rbreak\n', ), The node will be associated with the text break, excluding the trailing newline. diff --git a/uncompyle6/show.py b/uncompyle6/show.py index c2856982..ff16d12e 100644 --- a/uncompyle6/show.py +++ b/uncompyle6/show.py @@ -52,7 +52,7 @@ def maybe_show_ast_param_default(showast, name, default): stream.write('\n') stream.write('--' + name) stream.write('\n') - stream.write(default) + stream.write(str(default)) stream.write('\n') stream.write('--') stream.write('\n')