You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-03 00:45:53 +08:00
Structure detection bugs +
Had borked 3.4 grammar rules in previous refactor
This commit is contained in:
@@ -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):
|
||||
"""
|
||||
|
@@ -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
|
||||
|
@@ -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]
|
||||
|
@@ -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)
|
||||
|
@@ -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:
|
||||
|
Reference in New Issue
Block a user