diff --git a/uncompyle6/parsers/parse3.py b/uncompyle6/parsers/parse3.py index 70ca2c0d..11bb9ffb 100644 --- a/uncompyle6/parsers/parse3.py +++ b/uncompyle6/parsers/parse3.py @@ -40,14 +40,17 @@ class Python3Parser(PythonParser): # listcomp is a custom Python3 rule expr ::= listcomp - list_for ::= expr FOR_ITER designator list_iter JUMP_BACK - # Our "continue" heuristic - in two successive JUMP_BACKS, the first # one may be a continue - sometimes classifies a JUMP_BACK # as a CONTINUE. The two are kind of the same in a comprehension. comp_for ::= expr _for designator comp_iter CONTINUE + list_for ::= expr FOR_ITER designator list_iter jb_or_c + + jb_or_c ::= JUMP_BACK + jb_or_c ::= CONTINUE + # See also common Python p_list_comprehension """ @@ -580,18 +583,6 @@ class Python33Parser(Python3Parser): yield_from ::= expr expr YIELD_FROM """ -class Python34Parser(Python3Parser): - def p_34(self, args): - """ - _ifstmts_jump ::= c_stmts_opt JUMP_FORWARD _come_from - - # Python 3.3 added yield from. Do it the same way as in - # 3.3 - - expr ::= yield_from - yield_from ::= expr expr YIELD_FROM - """ - class Python35onParser(Python3Parser): def p_35on(self, args): """ diff --git a/uncompyle6/parsers/parse34.py b/uncompyle6/parsers/parse34.py index f97613d6..6be0a665 100644 --- a/uncompyle6/parsers/parse34.py +++ b/uncompyle6/parsers/parse34.py @@ -21,12 +21,22 @@ class Python34Parser(Python3Parser): iflaststmtl ::= testexpr c_stmts_opt + _ifstmts_jump ::= c_stmts_opt JUMP_FORWARD _come_from # We do the grammar hackery below for semantics # actions that want c_stmts_opt at index 1 iflaststmt ::= testexpr c_stmts_opt34 c_stmts_opt34 ::= JUMP_BACK JUMP_ABSOLUTE c_stmts_opt + # Python 3.3 added "yield from." Do it the same way as in + # 3.3 + + expr ::= yield_from + yield_from ::= expr expr YIELD_FROM + + # Is this 3.4 only? + yield_from ::= expr GET_ITER LOAD_CONST YIELD_FROM + """ class Python34ParserSingle(Python34Parser, PythonParserSingle): pass diff --git a/uncompyle6/scanners/scanner2.py b/uncompyle6/scanners/scanner2.py index 9835e6f1..ab87fe0d 100755 --- a/uncompyle6/scanners/scanner2.py +++ b/uncompyle6/scanners/scanner2.py @@ -194,7 +194,7 @@ class Scanner2(scan.Scanner): # into a "pass" statement because JUMPs are sometimes # ignored in rules as just boundary overhead. target = self.get_target(offset) - if target < offset: + if target <= offset: if (offset in self.stmts and self.code[offset+3] not in (self.opc.END_FINALLY, self.opc.POP_BLOCK) @@ -374,7 +374,7 @@ class Scanner2(scan.Scanner): 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: + if self.version <= 2.6 and self.is_jump_forward(jmp): self.not_continue.add(jmp) jmp = self.get_target(jmp) if jmp not in self.pop_jump_if | self.jump_forward: @@ -421,6 +421,14 @@ class Scanner2(scan.Scanner): _start = s['start'] _end = s['end'] if (_start <= pos < _end) and (_start >= start and _end <= end): + + # # Rocky: The SETUP_LOOP gives its boundary. So respect that as well. + # # Without this we can associate a COME_FROM incorrectly + # if op == self.opc.SETUP_LOOP: + # target = self.get_target(pos, op) + # if _end < target: + # continue + start = _start end = _end parent = s @@ -461,7 +469,7 @@ class Scanner2(scan.Scanner): return_val_offset1 = self.prev[self.prev[end]] if (jump_back and jump_back != self.prev[end] - and code[jump_forward_offset] in self.jump_forward): + and self.is_jump_forward(jump_forward_offset)): if (code[self.prev[end]] == self.opc.RETURN_VALUE or (code[self.prev[end]] == self.opc.POP_BLOCK and code[return_val_offset1] == self.opc.RETURN_VALUE)): @@ -500,8 +508,8 @@ class Scanner2(scan.Scanner): else: if self.get_target(jump_back) >= next_line_byte: jump_back = self.last_instr(start, end, self.opc.JUMP_ABSOLUTE, start, False) - if end > jump_back+4 and code[end] in self.jump_forward: - if code[jump_back+4] in self.jump_forward: + if end > jump_back+4 and self.is_jump_forward(end): + if self.is_jump_forward(jump_back+4): if self.get_target(jump_back+4) == self.get_target(end): self.fixed_jumps[pos] = jump_back+4 end = jump_back+4 @@ -621,7 +629,7 @@ class Scanner2(scan.Scanner): # Is it an "and" inside an "if" block if op == self.opc.PJIF: - # Search for other POP_JUMP_IF_FALSE targetting the same op, + # Search for other PJIF targetting the same op, # in current statement, starting from current offset, and filter # everything inside inner 'or' jumps and midline ifs match = self.rem_or(start, self.next_stmt[pos], self.opc.PJIF, target) @@ -631,14 +639,14 @@ class Scanner2(scan.Scanner): # If we still have any offsets in set, start working on it if match: - if code[pre[rtarget]] in self.jump_forward \ - 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.JUMP_ABSOLUTE \ - 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))): + is_jump_forward = self.is_jump_forward(pre[rtarget]) + if (is_jump_forward 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.JUMP_ABSOLUTE + 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]) \ @@ -662,7 +670,8 @@ class Scanner2(scan.Scanner): self.fixed_jumps[pos] = fix or match[-1] return else: - self.fixed_jumps[pos] = match[-1] + if is_jump_forward: + self.fixed_jumps[pos] = match[-1] return else: # op == self.opc.PJIT if self.version < 2.7 and code[pos+3] == self.opc.POP_TOP: @@ -677,17 +686,17 @@ class Scanner2(scan.Scanner): next = self.next_stmt[pos] if pre[next] == pos: pass - elif code[next] in self.jump_forward and target == self.get_target(next): + elif self.is_jump_forward(next) and target == self.get_target(next): if code[pre[next]] == self.opc.PJIF: if code[next] == self.opc.JUMP_FORWARD or target != rtarget or code[pre[pre[rtarget]]] not in (self.opc.JUMP_ABSOLUTE, self.opc.RETURN_VALUE): self.fixed_jumps[pos] = pre[next] return - elif code[next] == self.opc.JUMP_ABSOLUTE and code[target] in self.jump_forward: - next_target = self.get_target(next) + elif code[next] == self.opc.JUMP_ABSOLUTE and self.is_jump_forward(target): + next_target = self.get_target(next) if self.get_target(target) == next_target: self.fixed_jumps[pos] = pre[next] return - elif code[next_target] in self.jump_forward and self.get_target(next_target) == self.get_target(target): + elif self.is_jump_forward(next_target) and self.get_target(next_target) == self.get_target(target): self.fixed_jumps[pos] = pre[next] return @@ -710,7 +719,7 @@ class Scanner2(scan.Scanner): # Does the "if" jump just beyond a jump op, then this is probably an if statement pre_rtarget = pre[rtarget] code_pre_rtarget = code[pre_rtarget] - if code_pre_rtarget in self.jump_forward: + if self.is_jump_forward(pre_rtarget): if_end = self.get_target(pre_rtarget) # Is this a loop and not an "if" statment? @@ -786,6 +795,7 @@ class Scanner2(scan.Scanner): if label is not None and label != -1: targets[label] = targets.get(label, []) + [i] + elif op == self.opc.END_FINALLY and i in self.fixed_jumps: label = self.fixed_jumps[i] targets[label] = targets.get(label, []) + [i] diff --git a/uncompyle6/scanners/scanner26.py b/uncompyle6/scanners/scanner26.py index 4806c338..8ad6854c 100755 --- a/uncompyle6/scanners/scanner26.py +++ b/uncompyle6/scanners/scanner26.py @@ -247,7 +247,7 @@ class Scanner26(scan.Scanner2): # into a "pass" statement because JUMPs are sometimes # ignored in rules as just boundary overhead. target = self.get_target(offset) - if target < offset: + if target <= offset: if (offset in self.stmts and self.code[offset+3] not in (self.opc.END_FINALLY, self.opc.POP_BLOCK) diff --git a/uncompyle6/scanners/scanner3.py b/uncompyle6/scanners/scanner3.py index 7fa8a5c3..20faae41 100644 --- a/uncompyle6/scanners/scanner3.py +++ b/uncompyle6/scanners/scanner3.py @@ -235,7 +235,7 @@ class Scanner3(scan.Scanner): # rule for that. pattr = inst.argval target = self.get_target(inst.offset) - if target < inst.offset: + if target <= inst.offset: next_opname = self.opname[self.code[inst.offset+3]] if (inst.offset in self.stmts and next_opname not in ('END_FINALLY', 'POP_BLOCK') @@ -487,11 +487,12 @@ class Scanner3(scan.Scanner): # Pick inner-most parent for our offset for struct in self.structs: - curent_start = struct['start'] - curent_end = struct['end'] - if (curent_start <= offset < curent_end) and (curent_start >= start and curent_end <= end): - start = curent_start - end = curent_end + current_start = struct['start'] + current_end = struct['end'] + if ((current_start <= offset < current_end) + and (current_start >= start and current_end <= end)): + start = current_start + end = current_end parent = struct if op == self.opc.SETUP_LOOP: @@ -566,10 +567,11 @@ class Scanner3(scan.Scanner): self.fixed_jumps[offset] = rtarget return - # Does this jump to right after another cond jump that is + # Does this jump to right after another conditional jump that is # not myself? If so, it's part of a larger conditional. # rocky: if we have a conditional jump to the next instruction, then # possibly I am "skipping over" a "pass" or null statement. + if ((code[prev_op[target]] in self.pop_jump_if_pop) and (target > offset) and prev_op[target] != offset): self.fixed_jumps[offset] = prev_op[target] @@ -585,7 +587,9 @@ class Scanner3(scan.Scanner): # everything inside inner 'or' jumps and midline ifs match = self.rem_or(start, self.next_stmt[offset], self.opc.POP_JUMP_IF_FALSE, target) - match = self.remove_mid_line_ifs(match) + ## We can't remove mid-line ifs because line structures have changed + ## from restructBytecode(). + ## match = self.remove_mid_line_ifs(match) # If we still have any offsets in set, start working on it if match: