forked from rocky/python-uncompyle6
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Detect Python 2.7 weird "for-block" bytecode
It has an unnecessary JUMP_ABSOLUTE to a JUMP_LOOP Fixes #408
- Loading branch information
Showing
3 changed files
with
83 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |