You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-04 09:22:40 +08:00
Python 3 better CONTINUE op classification
Also document what's up with JUMP_ABSOLUTE classification
This commit is contained in:
@@ -42,6 +42,12 @@ class Python3Parser(PythonParser):
|
|||||||
|
|
||||||
list_for ::= expr FOR_ITER designator list_iter JUMP_BACK
|
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
|
||||||
|
|
||||||
# See also common Python p_list_comprehension
|
# See also common Python p_list_comprehension
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@@ -184,7 +184,15 @@ class Scanner2(scan.Scanner):
|
|||||||
opname = '%s_%d' % (opname, oparg)
|
opname = '%s_%d' % (opname, oparg)
|
||||||
if op != self.opc.BUILD_SLICE:
|
if op != self.opc.BUILD_SLICE:
|
||||||
customize[opname] = oparg
|
customize[opname] = oparg
|
||||||
elif op == self.opc.JA:
|
elif op == self.opc.JUMP_ABSOLUTE:
|
||||||
|
# Further classifhy JUMP_ABSOLUTE into backward jumps
|
||||||
|
# which are used in loops, and "CONTINUE" jumps which
|
||||||
|
# may appear in a "continue" statement. The loop-type
|
||||||
|
# and continue-type jumps will help us classify loop
|
||||||
|
# boundaries The continue-type jumps help us get
|
||||||
|
# "continue" statements with would otherwise be turned
|
||||||
|
# into a "pass" statement because JUMPs are sometimes
|
||||||
|
# ignored in rules as just boundary overhead.
|
||||||
target = self.get_target(offset)
|
target = self.get_target(offset)
|
||||||
if target < offset:
|
if target < offset:
|
||||||
if (offset in self.stmts
|
if (offset in self.stmts
|
||||||
|
@@ -237,7 +237,15 @@ class Scanner26(scan.Scanner2):
|
|||||||
op_name = '%s_%d' % (op_name, oparg)
|
op_name = '%s_%d' % (op_name, oparg)
|
||||||
if op != self.opc.BUILD_SLICE:
|
if op != self.opc.BUILD_SLICE:
|
||||||
customize[op_name] = oparg
|
customize[op_name] = oparg
|
||||||
elif op == self.opc.JA:
|
elif op == self.opc.JUMP_ABSOLUTE:
|
||||||
|
# Further classifhy JUMP_ABSOLUTE into backward jumps
|
||||||
|
# which are used in loops, and "CONTINUE" jumps which
|
||||||
|
# may appear in a "continue" statement. The loop-type
|
||||||
|
# and continue-type jumps will help us classify loop
|
||||||
|
# boundaries The continue-type jumps help us get
|
||||||
|
# "continue" statements with would otherwise be turned
|
||||||
|
# into a "pass" statement because JUMPs are sometimes
|
||||||
|
# ignored in rules as just boundary overhead.
|
||||||
target = self.get_target(offset)
|
target = self.get_target(offset)
|
||||||
if target < offset:
|
if target < offset:
|
||||||
if (offset in self.stmts
|
if (offset in self.stmts
|
||||||
|
@@ -86,6 +86,14 @@ class Scanner3(scan.Scanner):
|
|||||||
self.opc.POP_JUMP_IF_TRUE,
|
self.opc.POP_JUMP_IF_TRUE,
|
||||||
self.opc.POP_JUMP_IF_FALSE])
|
self.opc.POP_JUMP_IF_FALSE])
|
||||||
|
|
||||||
|
self.varargs = frozenset([self.opc.BUILD_LIST,
|
||||||
|
self.opc.BUILD_TUPLE,
|
||||||
|
self.opc.BUILD_SET,
|
||||||
|
self.opc.BUILD_SLICE,
|
||||||
|
self.opc.BUILD_MAP,
|
||||||
|
self.opc.UNPACK_SEQUENCE,
|
||||||
|
self.opc.RAISE_VARARGS])
|
||||||
|
|
||||||
def disassemble(self, co, classname=None, code_objects={}, show_asm=None):
|
def disassemble(self, co, classname=None, code_objects={}, show_asm=None):
|
||||||
"""
|
"""
|
||||||
Disassemble a Python 3 code object, returning a list of 'Token'.
|
Disassemble a Python 3 code object, returning a list of 'Token'.
|
||||||
@@ -147,6 +155,7 @@ class Scanner3(scan.Scanner):
|
|||||||
|
|
||||||
pattr = inst.argrepr
|
pattr = inst.argrepr
|
||||||
opname = inst.opname
|
opname = inst.opname
|
||||||
|
op = inst.opcode
|
||||||
|
|
||||||
if opname in ['LOAD_CONST']:
|
if opname in ['LOAD_CONST']:
|
||||||
const = inst.argval
|
const = inst.argval
|
||||||
@@ -190,10 +199,7 @@ class Scanner3(scan.Scanner):
|
|||||||
linestart = inst.starts_line)
|
linestart = inst.starts_line)
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
elif opname in ('BUILD_LIST', 'BUILD_TUPLE', 'BUILD_SET', 'BUILD_SLICE',
|
elif op in self.varargs:
|
||||||
'BUILD_MAP', 'UNPACK_SEQUENCE',
|
|
||||||
'RAISE_VARARGS'
|
|
||||||
):
|
|
||||||
pos_args = inst.argval
|
pos_args = inst.argval
|
||||||
opname = '%s_%d' % (opname, pos_args)
|
opname = '%s_%d' % (opname, pos_args)
|
||||||
elif opname == 'UNPACK_EX':
|
elif opname == 'UNPACK_EX':
|
||||||
@@ -204,7 +210,18 @@ class Scanner3(scan.Scanner):
|
|||||||
pattr = "%d before vararg, %d after" % (before_args, after_args)
|
pattr = "%d before vararg, %d after" % (before_args, after_args)
|
||||||
argval = (before_args, after_args)
|
argval = (before_args, after_args)
|
||||||
opname = '%s_%d+%d' % (opname, before_args, after_args)
|
opname = '%s_%d+%d' % (opname, before_args, after_args)
|
||||||
elif opname == 'JUMP_ABSOLUTE':
|
elif op == self.opc.JUMP_ABSOLUTE:
|
||||||
|
# Further classifhy JUMP_ABSOLUTE into backward jumps
|
||||||
|
# which are used in loops, and "CONTINUE" jumps which
|
||||||
|
# may appear in a "continue" statement. The loop-type
|
||||||
|
# and continue-type jumps will help us classify loop
|
||||||
|
# boundaries The continue-type jumps help us get
|
||||||
|
# "continue" statements with would otherwise be turned
|
||||||
|
# into a "pass" statement because JUMPs are sometimes
|
||||||
|
# ignored in rules as just boundary overhead. In
|
||||||
|
# comprehensions we might sometimes classify JUMP_BACK
|
||||||
|
# as CONTINUE, but that's okay since we add a grammar
|
||||||
|
# rule for that.
|
||||||
pattr = inst.argval
|
pattr = inst.argval
|
||||||
target = self.get_target(inst.offset)
|
target = self.get_target(inst.offset)
|
||||||
if target < inst.offset:
|
if target < inst.offset:
|
||||||
@@ -215,8 +232,15 @@ class Scanner3(scan.Scanner):
|
|||||||
opname = 'CONTINUE'
|
opname = 'CONTINUE'
|
||||||
else:
|
else:
|
||||||
opname = 'JUMP_BACK'
|
opname = 'JUMP_BACK'
|
||||||
|
# FIXME: this is a hack to catch stuff like:
|
||||||
|
# if x: continue
|
||||||
|
# the "continue" is not on a new line.
|
||||||
|
# There are other situations were we don't catch
|
||||||
|
# CONTINUE as well.
|
||||||
|
if tokens[-1].type == 'JUMP_BACK':
|
||||||
|
tokens[-1].type = intern('CONTINUE')
|
||||||
|
|
||||||
elif opname == 'RETURN_VALUE':
|
elif op == self.opc.RETURN_VALUE:
|
||||||
if inst.offset in self.return_end_ifs:
|
if inst.offset in self.return_end_ifs:
|
||||||
opname = 'RETURN_END_IF'
|
opname = 'RETURN_END_IF'
|
||||||
elif inst.offset in self.load_asserts:
|
elif inst.offset in self.load_asserts:
|
||||||
@@ -556,21 +580,27 @@ class Scanner3(scan.Scanner):
|
|||||||
if match:
|
if match:
|
||||||
if (code[prev_op[rtarget]] in self.jump_forward and prev_op[rtarget] not in self.stmts and
|
if (code[prev_op[rtarget]] in self.jump_forward and prev_op[rtarget] not in self.stmts and
|
||||||
self.restrict_to_parent(self.get_target(prev_op[rtarget]), parent) == rtarget):
|
self.restrict_to_parent(self.get_target(prev_op[rtarget]), parent) == rtarget):
|
||||||
if (code[prev_op[prev_op[rtarget]]] == JUMP_ABSOLUTE and self.remove_mid_line_ifs([offset]) and
|
if (code[prev_op[prev_op[rtarget]]] == self.opc.JUMP_ABSOLUTE
|
||||||
|
and self.remove_mid_line_ifs([offset]) and
|
||||||
target == self.get_target(prev_op[prev_op[rtarget]]) and
|
target == self.get_target(prev_op[prev_op[rtarget]]) and
|
||||||
(prev_op[prev_op[rtarget]] not in self.stmts or self.get_target(prev_op[prev_op[rtarget]]) > prev_op[prev_op[rtarget]]) and
|
(prev_op[prev_op[rtarget]] not in self.stmts or
|
||||||
|
self.get_target(prev_op[prev_op[rtarget]]) > prev_op[prev_op[rtarget]]) and
|
||||||
1 == len(self.remove_mid_line_ifs(self.rem_or(start, prev_op[prev_op[rtarget]], POP_JUMP_TF, target)))):
|
1 == len(self.remove_mid_line_ifs(self.rem_or(start, prev_op[prev_op[rtarget]], POP_JUMP_TF, target)))):
|
||||||
pass
|
pass
|
||||||
elif (code[prev_op[prev_op[rtarget]]] == RETURN_VALUE and self.remove_mid_line_ifs([offset]) and
|
elif (code[prev_op[prev_op[rtarget]]] == self.opc.RETURN_VALUE
|
||||||
|
and self.remove_mid_line_ifs([offset]) and
|
||||||
1 == (len(set(self.remove_mid_line_ifs(self.rem_or(start, prev_op[prev_op[rtarget]],
|
1 == (len(set(self.remove_mid_line_ifs(self.rem_or(start, prev_op[prev_op[rtarget]],
|
||||||
POP_JUMP_TF, target))) |
|
POP_JUMP_TF, target))) |
|
||||||
set(self.remove_mid_line_ifs(self.rem_or(start, prev_op[prev_op[rtarget]],
|
set(self.remove_mid_line_ifs(self.rem_or(start, prev_op[prev_op[rtarget]],
|
||||||
(POP_JUMP_IF_FALSE, POP_JUMP_IF_TRUE, JUMP_ABSOLUTE),
|
(self.opc.POP_JUMP_IF_FALSE,
|
||||||
|
self.opc.POP_JUMP_IF_TRUE,
|
||||||
|
self.opc.JUMP_ABSOLUTE),
|
||||||
prev_op[rtarget], True)))))):
|
prev_op[rtarget], True)))))):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
fix = None
|
fix = None
|
||||||
jump_ifs = self.all_instr(start, self.next_stmt[offset], POP_JUMP_IF_FALSE)
|
jump_ifs = self.all_instr(start, self.next_stmt[offset],
|
||||||
|
self.opc.POP_JUMP_IF_FALSE)
|
||||||
last_jump_good = True
|
last_jump_good = True
|
||||||
for j in jump_ifs:
|
for j in jump_ifs:
|
||||||
if target == self.get_target(j):
|
if target == self.get_target(j):
|
||||||
@@ -606,9 +636,13 @@ class Scanner3(scan.Scanner):
|
|||||||
if offset in self.ignore_if:
|
if offset in self.ignore_if:
|
||||||
return
|
return
|
||||||
|
|
||||||
if (code[prev_op[rtarget]] == JUMP_ABSOLUTE and prev_op[rtarget] in self.stmts and
|
if (code[prev_op[rtarget]] == self.opc.JUMP_ABSOLUTE and
|
||||||
prev_op[rtarget] != offset and prev_op[prev_op[rtarget]] != offset and
|
prev_op[rtarget] in self.stmts and
|
||||||
not (code[rtarget] == JUMP_ABSOLUTE and code[rtarget+3] == POP_BLOCK and code[prev_op[prev_op[rtarget]]] != JUMP_ABSOLUTE)):
|
prev_op[rtarget] != offset and
|
||||||
|
prev_op[prev_op[rtarget]] != offset and
|
||||||
|
not (code[rtarget] == self.opc.JUMP_ABSOLUTE and
|
||||||
|
code[rtarget+3] == self.opc.POP_BLOCK and
|
||||||
|
code[prev_op[prev_op[rtarget]]] != self.opc.JUMP_ABSOLUTE)):
|
||||||
rtarget = prev_op[rtarget]
|
rtarget = prev_op[rtarget]
|
||||||
|
|
||||||
# Does the "if" jump just beyond a jump op, then this is probably an if statement
|
# Does the "if" jump just beyond a jump op, then this is probably an if statement
|
||||||
|
Reference in New Issue
Block a user