You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-03 00:45:53 +08:00
Fix some 3.7+ "if"/"and" logic bugs
This commit is contained in:
@@ -950,6 +950,8 @@ class Python37Parser(Python37BaseParser):
|
|||||||
ret_or ::= expr JUMP_IF_TRUE_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 COME_FROM ret_expr_or_cond
|
ret_cond ::= expr POP_JUMP_IF_FALSE expr RETURN_END_IF COME_FROM ret_expr_or_cond
|
||||||
|
|
||||||
|
jitop_come_from ::= JUMP_IF_TRUE_OR_POP COME_FROM
|
||||||
|
or ::= and jitop_come_from expr COME_FROM
|
||||||
or ::= expr JUMP_IF_TRUE_OR_POP expr COME_FROM
|
or ::= expr JUMP_IF_TRUE_OR_POP expr COME_FROM
|
||||||
or ::= expr JUMP_IF_TRUE expr COME_FROM
|
or ::= expr JUMP_IF_TRUE expr COME_FROM
|
||||||
and ::= expr JUMP_IF_FALSE_OR_POP expr COME_FROM
|
and ::= expr JUMP_IF_FALSE_OR_POP expr COME_FROM
|
||||||
|
@@ -936,6 +936,7 @@ class Python37BaseParser(PythonParser):
|
|||||||
self.check_reduce["iflaststmt"] = "AST"
|
self.check_reduce["iflaststmt"] = "AST"
|
||||||
self.check_reduce["iflaststmtl"] = "AST"
|
self.check_reduce["iflaststmtl"] = "AST"
|
||||||
self.check_reduce["ifstmt"] = "AST"
|
self.check_reduce["ifstmt"] = "AST"
|
||||||
|
self.check_reduce["ifstmtl"] = "AST"
|
||||||
self.check_reduce["annotate_tuple"] = "noAST"
|
self.check_reduce["annotate_tuple"] = "noAST"
|
||||||
|
|
||||||
# FIXME: remove parser errors caused by the below
|
# FIXME: remove parser errors caused by the below
|
||||||
@@ -1028,7 +1029,6 @@ class Python37BaseParser(PythonParser):
|
|||||||
|
|
||||||
if lhs == "and" and ast:
|
if lhs == "and" and ast:
|
||||||
# FIXME: put in a routine somewhere
|
# FIXME: put in a routine somewhere
|
||||||
# Compare with parse30.py of uncompyle6
|
|
||||||
jmp = ast[1]
|
jmp = ast[1]
|
||||||
if jmp.kind.startswith("jmp_"):
|
if jmp.kind.startswith("jmp_"):
|
||||||
if last == n:
|
if last == n:
|
||||||
@@ -1043,7 +1043,11 @@ class Python37BaseParser(PythonParser):
|
|||||||
return jmp_target != jmp2_target
|
return jmp_target != jmp2_target
|
||||||
elif rule == ("and", ("expr", "jmp_false", "expr")):
|
elif rule == ("and", ("expr", "jmp_false", "expr")):
|
||||||
if tokens[last] == "POP_JUMP_IF_FALSE":
|
if tokens[last] == "POP_JUMP_IF_FALSE":
|
||||||
|
# Ok if jump it doesn't jump to last instruction
|
||||||
return jmp_target != tokens[last].attr
|
return jmp_target != tokens[last].attr
|
||||||
|
elif tokens[last] == "JUMP_IF_TRUE_OR_POP":
|
||||||
|
# Ok if jump it does jump right after last instruction
|
||||||
|
return jmp_target + 2 != tokens[last].attr
|
||||||
elif rule == ("and", ("expr", "jmp_false", "expr", "COME_FROM")):
|
elif rule == ("and", ("expr", "jmp_false", "expr", "COME_FROM")):
|
||||||
return ast[-1].attr != jmp_offset
|
return ast[-1].attr != jmp_offset
|
||||||
# elif rule == ("and", ("expr", "jmp_false", "expr", "COME_FROM")):
|
# elif rule == ("and", ("expr", "jmp_false", "expr", "COME_FROM")):
|
||||||
@@ -1153,36 +1157,65 @@ class Python37BaseParser(PythonParser):
|
|||||||
else:
|
else:
|
||||||
return tokens[pop_jump_index].offset > come_froms[-1].attr
|
return tokens[pop_jump_index].offset > come_froms[-1].attr
|
||||||
|
|
||||||
elif lhs == "ifstmt" and ast:
|
elif lhs in ("ifstmt", "ifstmtl"):
|
||||||
# FIXME: put in a routine somewhere
|
# FIXME: put in a routine somewhere
|
||||||
testexpr = ast[0]
|
|
||||||
|
|
||||||
if (last + 1) < n and tokens[last + 1] == "COME_FROM_LOOP":
|
n = len(tokens)
|
||||||
# iflastsmtl jumped outside of loop. No good.
|
if lhs == "ifstmtl":
|
||||||
return True
|
if last == n:
|
||||||
|
last -= 1
|
||||||
|
pass
|
||||||
|
if (tokens[last].attr and isinstance(tokens[last].attr, int)):
|
||||||
|
return tokens[first].offset < tokens[last].attr
|
||||||
|
pass
|
||||||
|
|
||||||
if testexpr[0] in ("testtrue", "testfalse"):
|
# Make sure jumps don't extend beyond the end of the if statement.
|
||||||
test = testexpr[0]
|
l = last
|
||||||
if len(test) > 1 and test[1].kind.startswith("jmp_"):
|
if l == n:
|
||||||
if last == n:
|
l -= 1
|
||||||
last -= 1
|
if isinstance(tokens[l].offset, str):
|
||||||
jmp_target = test[1][0].attr
|
last_offset = int(tokens[l].offset.split("_")[0], 10)
|
||||||
if tokens[first].off2int() <= jmp_target < tokens[last].off2int():
|
else:
|
||||||
return True
|
last_offset = tokens[l].offset
|
||||||
# jmp_target less than tokens[first] is okay - is to a loop
|
for i in range(first, l):
|
||||||
# jmp_target equal tokens[last] is also okay: normal non-optimized non-loop jump
|
t = tokens[i]
|
||||||
if jmp_target > tokens[last].off2int():
|
if t.kind == "POP_JUMP_IF_FALSE":
|
||||||
# One more weird case to look out for
|
if t.attr > last_offset:
|
||||||
# if c1:
|
|
||||||
# if c2: # Jumps around the *outer* "else"
|
|
||||||
# ...
|
|
||||||
# else:
|
|
||||||
if jmp_target == tokens[last - 1].attr:
|
|
||||||
return False
|
|
||||||
if last < n and tokens[last].kind.startswith("JUMP"):
|
|
||||||
return False
|
|
||||||
return True
|
return True
|
||||||
|
pass
|
||||||
|
pass
|
||||||
|
pass
|
||||||
|
|
||||||
|
if ast:
|
||||||
|
testexpr = ast[0]
|
||||||
|
|
||||||
|
if (last + 1) < n and tokens[last + 1] == "COME_FROM_LOOP":
|
||||||
|
# iflastsmtl jumped outside of loop. No good.
|
||||||
|
return True
|
||||||
|
|
||||||
|
if testexpr[0] in ("testtrue", "testfalse"):
|
||||||
|
test = testexpr[0]
|
||||||
|
if len(test) > 1 and test[1].kind.startswith("jmp_"):
|
||||||
|
if last == n:
|
||||||
|
last -= 1
|
||||||
|
jmp_target = test[1][0].attr
|
||||||
|
if tokens[first].off2int() <= jmp_target < tokens[last].off2int():
|
||||||
|
return True
|
||||||
|
# jmp_target less than tokens[first] is okay - is to a loop
|
||||||
|
# jmp_target equal tokens[last] is also okay: normal non-optimized non-loop jump
|
||||||
|
if jmp_target > tokens[last].off2int():
|
||||||
|
# One more weird case to look out for
|
||||||
|
# if c1:
|
||||||
|
# if c2: # Jumps around the *outer* "else"
|
||||||
|
# ...
|
||||||
|
# else:
|
||||||
|
if jmp_target == tokens[last - 1].attr:
|
||||||
|
return False
|
||||||
|
if last < n and tokens[last].kind.startswith("JUMP"):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
pass
|
||||||
pass
|
pass
|
||||||
return False
|
return False
|
||||||
elif lhs in ("iflaststmt", "iflaststmtl") and ast:
|
elif lhs in ("iflaststmt", "iflaststmtl") and ast:
|
||||||
|
@@ -263,10 +263,8 @@ class Python38Parser(Python37Parser):
|
|||||||
def customize_grammar_rules(self, tokens, customize):
|
def customize_grammar_rules(self, tokens, customize):
|
||||||
super(Python37Parser, self).customize_grammar_rules(tokens, customize)
|
super(Python37Parser, self).customize_grammar_rules(tokens, customize)
|
||||||
self.remove_rules_38()
|
self.remove_rules_38()
|
||||||
self.check_reduce["ifstmt"] = "tokens"
|
|
||||||
self.check_reduce["whileTruestmt38"] = "tokens"
|
self.check_reduce["whileTruestmt38"] = "tokens"
|
||||||
self.check_reduce["whilestmt38"] = "tokens"
|
self.check_reduce["whilestmt38"] = "tokens"
|
||||||
self.check_reduce["ifstmtl"] = "tokens"
|
|
||||||
|
|
||||||
def reduce_is_invalid(self, rule, ast, tokens, first, last):
|
def reduce_is_invalid(self, rule, ast, tokens, first, last):
|
||||||
invalid = super(Python38Parser,
|
invalid = super(Python38Parser,
|
||||||
@@ -276,30 +274,7 @@ class Python38Parser(Python37Parser):
|
|||||||
if invalid:
|
if invalid:
|
||||||
return invalid
|
return invalid
|
||||||
lhs = rule[0]
|
lhs = rule[0]
|
||||||
if lhs == "ifstmt":
|
if lhs in ("whileTruestmt38", "whilestmt38"):
|
||||||
# Make sure jumps don't extend beyond the end of the if statement.
|
|
||||||
l = last
|
|
||||||
if l == len(tokens):
|
|
||||||
l -= 1
|
|
||||||
if isinstance(tokens[l].offset, str):
|
|
||||||
last_offset = int(tokens[l].offset.split("_")[0], 10)
|
|
||||||
else:
|
|
||||||
last_offset = tokens[l].offset
|
|
||||||
for i in range(first, l):
|
|
||||||
t = tokens[i]
|
|
||||||
if t.kind == "POP_JUMP_IF_FALSE":
|
|
||||||
if t.attr > last_offset:
|
|
||||||
return True
|
|
||||||
pass
|
|
||||||
pass
|
|
||||||
pass
|
|
||||||
elif lhs == "ifstmtl":
|
|
||||||
if last == len(tokens):
|
|
||||||
last -= 1
|
|
||||||
if (tokens[last].attr and isinstance(tokens[last].attr, int)):
|
|
||||||
return tokens[first].offset < tokens[last].attr
|
|
||||||
pass
|
|
||||||
elif lhs in ("whileTruestmt38", "whilestmt38"):
|
|
||||||
jb_index = last - 1
|
jb_index = last - 1
|
||||||
while jb_index > 0 and tokens[jb_index].kind.startswith("COME_FROM"):
|
while jb_index > 0 and tokens[jb_index].kind.startswith("COME_FROM"):
|
||||||
jb_index -= 1
|
jb_index -= 1
|
||||||
|
@@ -776,25 +776,6 @@ class Scanner37Base(Scanner):
|
|||||||
)
|
)
|
||||||
elif op in self.pop_jump_tf:
|
elif op in self.pop_jump_tf:
|
||||||
target = inst.argval
|
target = inst.argval
|
||||||
prev_op = self.prev_op
|
|
||||||
|
|
||||||
# FIXME: hack upon hack, test_pysource.py fails with this
|
|
||||||
# Until the grammar is corrected we do this fiction...
|
|
||||||
pretarget = self.get_inst(prev_op[target])
|
|
||||||
if (
|
|
||||||
pretarget.opcode in self.pop_jump_if_pop
|
|
||||||
and (target > offset)
|
|
||||||
and pretarget.offset != offset
|
|
||||||
):
|
|
||||||
|
|
||||||
if pretarget.argval != target:
|
|
||||||
# FIXME: this is not accurate The commented out below
|
|
||||||
# is what it should be. However grammar rules right now
|
|
||||||
# assume the incorrect offsets.
|
|
||||||
# self.fixed_jumps[offset] = target
|
|
||||||
self.fixed_jumps[offset] = pretarget.offset
|
|
||||||
return
|
|
||||||
|
|
||||||
self.fixed_jumps[offset] = target
|
self.fixed_jumps[offset] = target
|
||||||
|
|
||||||
elif self.version < 3.8 and op == self.opc.SETUP_EXCEPT:
|
elif self.version < 3.8 and op == self.opc.SETUP_EXCEPT:
|
||||||
|
@@ -111,7 +111,7 @@ def customize_for_version38(self, version):
|
|||||||
'%|try:\n%+%c%-%|finally:\n%+%c%-\n\n',
|
'%|try:\n%+%c%-%|finally:\n%+%c%-\n\n',
|
||||||
(1, "suite_stmts_opt"),
|
(1, "suite_stmts_opt"),
|
||||||
(6, "suite_stmts_opt") ),
|
(6, "suite_stmts_opt") ),
|
||||||
'tryfinally38_return_stmt': (
|
'tryfinally38astmt': (
|
||||||
'%|try:\n%+%c%-%|finally:\n%+%c%-\n\n',
|
'%|try:\n%+%c%-%|finally:\n%+%c%-\n\n',
|
||||||
(2, "suite_stmts_opt"),
|
(2, "suite_stmts_opt"),
|
||||||
(8, "suite_stmts_opt") ),
|
(8, "suite_stmts_opt") ),
|
||||||
|
@@ -96,7 +96,8 @@ class TreeTransform(GenericASTTraversal, object):
|
|||||||
jump_cond = testexpr[0][1]
|
jump_cond = testexpr[0][1]
|
||||||
expr = raise_stmt[0]
|
expr = raise_stmt[0]
|
||||||
RAISE_VARARGS_1 = raise_stmt[1]
|
RAISE_VARARGS_1 = raise_stmt[1]
|
||||||
if expr[0] == "call":
|
call = expr[0]
|
||||||
|
if call == "call":
|
||||||
# ifstmt
|
# ifstmt
|
||||||
# 0. testexpr
|
# 0. testexpr
|
||||||
# testtrue (2)
|
# testtrue (2)
|
||||||
@@ -116,7 +117,8 @@ class TreeTransform(GenericASTTraversal, object):
|
|||||||
assert jump_cond == "jmp_false"
|
assert jump_cond == "jmp_false"
|
||||||
kind = "assert2not"
|
kind = "assert2not"
|
||||||
|
|
||||||
call = expr[0]
|
if call[0] != "LOAD_ASSERT":
|
||||||
|
return node
|
||||||
LOAD_ASSERT = call[0]
|
LOAD_ASSERT = call[0]
|
||||||
if isinstance(call[1], SyntaxTree):
|
if isinstance(call[1], SyntaxTree):
|
||||||
expr = call[1][0]
|
expr = call[1][0]
|
||||||
|
Reference in New Issue
Block a user