You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-03 16:59:52 +08:00
Detect Python 2.7 weird "for-block" bytecode
It has an unnecessary JUMP_ABSOLUTE to a JUMP_LOOP Fixes #408
This commit is contained in:
@@ -9,6 +9,7 @@ from uncompyle6.parsers.parse2 import Python2Parser
|
|||||||
from uncompyle6.parsers.reducecheck import (
|
from uncompyle6.parsers.reducecheck import (
|
||||||
aug_assign1_check,
|
aug_assign1_check,
|
||||||
ifelsestmt,
|
ifelsestmt,
|
||||||
|
for_block_check,
|
||||||
or_check,
|
or_check,
|
||||||
tryelsestmt,
|
tryelsestmt,
|
||||||
except_handler,
|
except_handler,
|
||||||
@@ -87,6 +88,11 @@ class Python27Parser(Python2Parser):
|
|||||||
|
|
||||||
for_block ::= l_stmts_opt JUMP_BACK
|
for_block ::= l_stmts_opt JUMP_BACK
|
||||||
|
|
||||||
|
# In 2.7 there is occasionally a for_block has an unusual
|
||||||
|
# form: there is a JUMP_ABSOLUTE which jumps to the second JUMP_BACK
|
||||||
|
# listed below. Both JUMP_BACKS go to the same position so the
|
||||||
|
# the JUMP_ABSOLUTE and JUMP_BACK not necessary
|
||||||
|
for_block ::= l_stmts_opt JUMP_ABSOLUTE JUMP_BACK JUMP_BACK
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def p_jump27(self, args):
|
def p_jump27(self, args):
|
||||||
@@ -234,6 +240,7 @@ class Python27Parser(Python2Parser):
|
|||||||
self.reduce_check_table = {
|
self.reduce_check_table = {
|
||||||
"aug_assign1": aug_assign1_check,
|
"aug_assign1": aug_assign1_check,
|
||||||
"except_handler": except_handler,
|
"except_handler": except_handler,
|
||||||
|
"for_block": for_block_check.for_block_invalid,
|
||||||
"ifelsestmt": ifelsestmt,
|
"ifelsestmt": ifelsestmt,
|
||||||
"or": or_check,
|
"or": or_check,
|
||||||
"tryelsestmt": tryelsestmt,
|
"tryelsestmt": tryelsestmt,
|
||||||
@@ -247,6 +254,8 @@ class Python27Parser(Python2Parser):
|
|||||||
self.check_reduce["except_handler"] = "tokens"
|
self.check_reduce["except_handler"] = "tokens"
|
||||||
self.check_reduce["except_handler_else"] = "tokens"
|
self.check_reduce["except_handler_else"] = "tokens"
|
||||||
|
|
||||||
|
self.check_reduce["for_block"] = "tokens"
|
||||||
|
|
||||||
self.check_reduce["or"] = "AST"
|
self.check_reduce["or"] = "AST"
|
||||||
self.check_reduce["raise_stmt1"] = "AST"
|
self.check_reduce["raise_stmt1"] = "AST"
|
||||||
self.check_reduce["iflaststmtl"] = "AST"
|
self.check_reduce["iflaststmtl"] = "AST"
|
||||||
|
@@ -8,6 +8,7 @@ from uncompyle6.parsers.reducecheck.iflaststmt import *
|
|||||||
from uncompyle6.parsers.reducecheck.ifstmt import *
|
from uncompyle6.parsers.reducecheck.ifstmt import *
|
||||||
from uncompyle6.parsers.reducecheck.ifstmt2 import *
|
from uncompyle6.parsers.reducecheck.ifstmt2 import *
|
||||||
from uncompyle6.parsers.reducecheck.ifstmts_jump import *
|
from uncompyle6.parsers.reducecheck.ifstmts_jump import *
|
||||||
|
from uncompyle6.parsers.reducecheck.for_block_check import *
|
||||||
from uncompyle6.parsers.reducecheck.or_check import *
|
from uncompyle6.parsers.reducecheck.or_check import *
|
||||||
from uncompyle6.parsers.reducecheck.testtrue import *
|
from uncompyle6.parsers.reducecheck.testtrue import *
|
||||||
from uncompyle6.parsers.reducecheck.tryelsestmt import *
|
from uncompyle6.parsers.reducecheck.tryelsestmt import *
|
||||||
|
73
uncompyle6/parsers/reducecheck/for_block_check.py
Normal file
73
uncompyle6/parsers/reducecheck/for_block_check.py
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
# Copyright (c) 2022 Rocky Bernstein
|
||||||
|
|
||||||
|
from uncompyle6.scanners.tok import Token
|
||||||
|
|
||||||
|
|
||||||
|
def for_block_invalid(self, lhs, n, rule, tree, tokens, first: int, last: int) -> bool:
|
||||||
|
|
||||||
|
# print("XXX", first, last)
|
||||||
|
# for t in range(first, last):
|
||||||
|
# print(tokens[t])
|
||||||
|
# print("=" * 30)
|
||||||
|
|
||||||
|
if rule == (
|
||||||
|
"for_block",
|
||||||
|
("l_stmts_opt", "JUMP_ABSOLUTE", "JUMP_BACK", "JUMP_BACK"),
|
||||||
|
):
|
||||||
|
# Check that the two JUMP_BACK's go to the same place.
|
||||||
|
jump_back1 = tokens[last - 2]
|
||||||
|
jump_back2 = tokens[last - 1]
|
||||||
|
if jump_back1.attr != jump_back2.attr:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Also check that JUMP_ABSOLUTE jumps to the JUMP_BACK.
|
||||||
|
# In this situation the JUMP_ABSOLUTE and a JUMP_BACK
|
||||||
|
# is not needed, but it seems to be there anyway.
|
||||||
|
|
||||||
|
jump_absolute = tokens[last - 3]
|
||||||
|
if jump_absolute.attr != jump_back2.offset:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Right now all of this is known to happen only in Python 2.7.
|
||||||
|
if self.version[:2] == (2, 7):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if len(rule[1]) <= 1 or not tree:
|
||||||
|
return False
|
||||||
|
|
||||||
|
come_froms = tree[-1]
|
||||||
|
# This is complicated, but note that the JUMP_IF instruction comes immediately
|
||||||
|
# *before* _ifstmts_jump so that's what we have to test
|
||||||
|
# the COME_FROM against. This can be complicated by intervening
|
||||||
|
# POP_TOP, and pseudo COME_FROM, ELSE instructions
|
||||||
|
#
|
||||||
|
pop_jump_index = first - 1
|
||||||
|
while pop_jump_index > 0 and tokens[pop_jump_index] in (
|
||||||
|
"ELSE",
|
||||||
|
"POP_TOP",
|
||||||
|
"JUMP_FORWARD",
|
||||||
|
"COME_FROM",
|
||||||
|
):
|
||||||
|
pop_jump_index -= 1
|
||||||
|
|
||||||
|
# FIXME: something is fishy when and EXTENDED ARG is needed before the
|
||||||
|
# pop_jump_index instruction to get the argment. In this case, the
|
||||||
|
# _ifsmtst_jump can jump to a spot beyond the come_froms.
|
||||||
|
# That is going on in the non-EXTENDED_ARG case is that the POP_JUMP_IF
|
||||||
|
# jumps to a JUMP_(FORWARD) which is changed into an EXTENDED_ARG POP_JUMP_IF
|
||||||
|
# to the jumped forwarded address
|
||||||
|
if tokens[pop_jump_index].attr > 256:
|
||||||
|
return False
|
||||||
|
|
||||||
|
pop_jump_offset = tokens[pop_jump_index].off2int(prefer_last=False)
|
||||||
|
if isinstance(come_froms, Token):
|
||||||
|
if tokens[pop_jump_index].attr < pop_jump_offset and tree[0] != "pass":
|
||||||
|
# This is a jump backwards to a loop. All bets are off here when there the
|
||||||
|
# unless statement is "pass" which has no instructions associated with it.
|
||||||
|
return False
|
||||||
|
return come_froms.attr is not None and pop_jump_offset > come_froms.attr
|
||||||
|
|
||||||
|
elif len(come_froms) == 0:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return pop_jump_offset > come_froms[-1].attr
|
Reference in New Issue
Block a user