You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-03 00:45:53 +08:00
WIP : Add THEN to disambigute from "and"
This commit is contained in:
BIN
test/bytecode_2.6/04_if_and_bug.pyc
Normal file
BIN
test/bytecode_2.6/04_if_and_bug.pyc
Normal file
Binary file not shown.
@@ -113,16 +113,10 @@ class Python26Parser(Python2Parser):
|
|||||||
|
|
||||||
break_stmt ::= BREAK_LOOP JUMP_BACK
|
break_stmt ::= BREAK_LOOP JUMP_BACK
|
||||||
|
|
||||||
# Semantic actions want the else to be at position 3
|
|
||||||
ifelsestmt ::= testexpr c_stmts_opt jf_cf_pop else_suite come_froms
|
|
||||||
ifelsestmt ::= testexpr c_stmts_opt filler else_suitel come_froms POP_TOP
|
|
||||||
|
|
||||||
# Semantic actions want else_suitel to be at index 3
|
# Semantic actions want else_suitel to be at index 3
|
||||||
ifelsestmtl ::= testexpr c_stmts_opt jb_cf_pop else_suitel
|
ifelsestmtl ::= testexpr c_stmts_opt jb_cf_pop else_suitel
|
||||||
ifelsestmtc ::= testexpr c_stmts_opt ja_cf_pop else_suitec
|
ifelsestmtc ::= testexpr c_stmts_opt ja_cf_pop else_suitec
|
||||||
|
|
||||||
iflaststmt ::= testexpr c_stmts_opt JUMP_ABSOLUTE come_froms POP_TOP
|
|
||||||
|
|
||||||
# Semantic actions want suite_stmts_opt to be at index 3
|
# Semantic actions want suite_stmts_opt to be at index 3
|
||||||
withstmt ::= expr setupwith SETUP_FINALLY suite_stmts_opt
|
withstmt ::= expr setupwith SETUP_FINALLY suite_stmts_opt
|
||||||
POP_BLOCK LOAD_CONST COME_FROM WITH_CLEANUP END_FINALLY
|
POP_BLOCK LOAD_CONST COME_FROM WITH_CLEANUP END_FINALLY
|
||||||
@@ -159,6 +153,29 @@ class Python26Parser(Python2Parser):
|
|||||||
|
|
||||||
while1stmt ::= SETUP_LOOP l_stmts_opt JUMP_BACK COME_FROM
|
while1stmt ::= SETUP_LOOP l_stmts_opt JUMP_BACK COME_FROM
|
||||||
|
|
||||||
|
ifstmt ::= testexpr_then _ifstmts_jump
|
||||||
|
|
||||||
|
# Semantic actions want the else to be at position 3
|
||||||
|
ifelsestmt ::= testexpr c_stmts_opt jf_cf_pop else_suite come_froms
|
||||||
|
ifelsestmt ::= testexpr_then c_stmts_opt jf_cf_pop else_suite come_froms
|
||||||
|
ifelsestmt ::= testexpr c_stmts_opt filler else_suitel come_froms POP_TOP
|
||||||
|
ifelsestmt ::= testexpr_then c_stmts_opt filler else_suitel come_froms POP_TOP
|
||||||
|
|
||||||
|
# Semantic actions want else_suitel to be at index 3
|
||||||
|
ifelsestmtl ::= testexpr_then c_stmts_opt jb_cf_pop else_suitel
|
||||||
|
ifelsestmtc ::= testexpr_then c_stmts_opt ja_cf_pop else_suitec
|
||||||
|
|
||||||
|
iflaststmt ::= testexpr_then c_stmts_opt JUMP_ABSOLUTE come_froms POP_TOP
|
||||||
|
iflaststmt ::= testexpr c_stmts_opt JUMP_ABSOLUTE come_froms POP_TOP
|
||||||
|
|
||||||
|
testexpr_then ::= testtrue_then
|
||||||
|
testexpr_then ::= testfalse_then
|
||||||
|
testtrue_then ::= expr jmp_true_then
|
||||||
|
testfalse_then ::= expr jmp_false_then
|
||||||
|
|
||||||
|
jmp_false_then ::= JUMP_IF_FALSE THEN POP_TOP
|
||||||
|
jmp_true_then ::= JUMP_IF_TRUE THEN POP_TOP
|
||||||
|
|
||||||
# Common with 2.7
|
# Common with 2.7
|
||||||
while1stmt ::= SETUP_LOOP return_stmts bp_come_from
|
while1stmt ::= SETUP_LOOP return_stmts bp_come_from
|
||||||
while1stmt ::= SETUP_LOOP return_stmts COME_FROM
|
while1stmt ::= SETUP_LOOP return_stmts COME_FROM
|
||||||
@@ -201,11 +218,11 @@ class Python26Parser(Python2Parser):
|
|||||||
|
|
||||||
def p_ret26(self, args):
|
def p_ret26(self, args):
|
||||||
'''
|
'''
|
||||||
ret_and ::= expr jmp_false ret_expr_or_cond COME_FROM
|
ret_and ::= expr jmp_false ret_expr_or_cond COME_FROM
|
||||||
ret_or ::= expr jmp_true ret_expr_or_cond COME_FROM
|
ret_or ::= expr jmp_true ret_expr_or_cond COME_FROM
|
||||||
ret_cond ::= expr jmp_false expr RETURN_END_IF POP_TOP ret_expr_or_cond
|
ret_cond ::= expr jmp_false_then expr RETURN_END_IF POP_TOP ret_expr_or_cond
|
||||||
ret_cond ::= expr jmp_false expr ret_expr_or_cond
|
ret_cond ::= expr jmp_false_then expr ret_expr_or_cond
|
||||||
ret_cond_not ::= expr jmp_true expr RETURN_END_IF POP_TOP ret_expr_or_cond
|
ret_cond_not ::= expr jmp_true_then expr RETURN_END_IF POP_TOP ret_expr_or_cond
|
||||||
|
|
||||||
return_if_stmt ::= ret_expr RETURN_END_IF POP_TOP
|
return_if_stmt ::= ret_expr RETURN_END_IF POP_TOP
|
||||||
return_stmt ::= ret_expr RETURN_VALUE POP_TOP
|
return_stmt ::= ret_expr RETURN_VALUE POP_TOP
|
||||||
|
@@ -101,10 +101,10 @@ class Scanner2(scan.Scanner):
|
|||||||
|
|
||||||
Token = self.Token # shortcut
|
Token = self.Token # shortcut
|
||||||
|
|
||||||
n = self.setup_code(co)
|
codelen = self.setup_code(co)
|
||||||
|
|
||||||
self.build_lines_data(co, n)
|
self.build_lines_data(co, codelen)
|
||||||
self.build_prev_op(n)
|
self.build_prev_op(codelen)
|
||||||
|
|
||||||
free, names, varnames = self.unmangle_code_names(co, classname)
|
free, names, varnames = self.unmangle_code_names(co, classname)
|
||||||
self.names = names
|
self.names = names
|
||||||
@@ -113,7 +113,7 @@ class Scanner2(scan.Scanner):
|
|||||||
# turn 'LOAD_GLOBAL' to 'LOAD_ASSERT'.
|
# turn 'LOAD_GLOBAL' to 'LOAD_ASSERT'.
|
||||||
# 'LOAD_ASSERT' is used in assert statements.
|
# 'LOAD_ASSERT' is used in assert statements.
|
||||||
self.load_asserts = set()
|
self.load_asserts = set()
|
||||||
for i in self.op_range(0, n):
|
for i in self.op_range(0, codelen):
|
||||||
# We need to detect the difference between:
|
# We need to detect the difference between:
|
||||||
# raise AssertionError
|
# raise AssertionError
|
||||||
# and
|
# and
|
||||||
@@ -138,7 +138,7 @@ class Scanner2(scan.Scanner):
|
|||||||
last_stmt = self.next_stmt[0]
|
last_stmt = self.next_stmt[0]
|
||||||
i = self.next_stmt[last_stmt]
|
i = self.next_stmt[last_stmt]
|
||||||
replace = {}
|
replace = {}
|
||||||
while i < n-1:
|
while i < codelen - 1:
|
||||||
if self.lines[last_stmt].next > i:
|
if self.lines[last_stmt].next > i:
|
||||||
# Distinguish "print ..." from "print ...,"
|
# Distinguish "print ..." from "print ...,"
|
||||||
if self.code[last_stmt] == self.opc.PRINT_ITEM:
|
if self.code[last_stmt] == self.opc.PRINT_ITEM:
|
||||||
@@ -150,7 +150,7 @@ class Scanner2(scan.Scanner):
|
|||||||
i = self.next_stmt[i]
|
i = self.next_stmt[i]
|
||||||
|
|
||||||
extended_arg = 0
|
extended_arg = 0
|
||||||
for offset in self.op_range(0, n):
|
for offset in self.op_range(0, codelen):
|
||||||
if offset in jump_targets:
|
if offset in jump_targets:
|
||||||
jump_idx = 0
|
jump_idx = 0
|
||||||
# We want to process COME_FROMs to the same offset to be in *descending*
|
# We want to process COME_FROMs to the same offset to be in *descending*
|
||||||
@@ -176,6 +176,7 @@ class Scanner2(scan.Scanner):
|
|||||||
offset="%s_%d" % (offset, jump_idx),
|
offset="%s_%d" % (offset, jump_idx),
|
||||||
has_arg = True))
|
has_arg = True))
|
||||||
jump_idx += 1
|
jump_idx += 1
|
||||||
|
pass
|
||||||
|
|
||||||
op = self.code[offset]
|
op = self.code[offset]
|
||||||
op_name = self.opc.opname[op]
|
op_name = self.opc.opname[op]
|
||||||
@@ -675,6 +676,8 @@ class Scanner2(scan.Scanner):
|
|||||||
self.fixed_jumps[offset] = rtarget
|
self.fixed_jumps[offset] = rtarget
|
||||||
return
|
return
|
||||||
|
|
||||||
|
jump_if_offset = offset
|
||||||
|
|
||||||
start = offset+3
|
start = offset+3
|
||||||
pre = self.prev
|
pre = self.prev
|
||||||
|
|
||||||
@@ -827,20 +830,86 @@ class Scanner2(scan.Scanner):
|
|||||||
jump_target = self.get_target(next_offset, next_op)
|
jump_target = self.get_target(next_offset, next_op)
|
||||||
if jump_target in self.setup_loops:
|
if jump_target in self.setup_loops:
|
||||||
self.structs.append({'type': 'while-loop',
|
self.structs.append({'type': 'while-loop',
|
||||||
'start': start - 3,
|
'start': jump_if_offset,
|
||||||
'end': jump_target})
|
'end': jump_target})
|
||||||
self.fixed_jumps[start-3] = jump_target
|
self.fixed_jumps[jump_if_offset] = jump_target
|
||||||
return
|
return
|
||||||
|
|
||||||
end = self.restrict_to_parent(if_end, parent)
|
end = self.restrict_to_parent(if_end, parent)
|
||||||
|
|
||||||
self.structs.append({'type': 'if-then',
|
if_then_maybe = None
|
||||||
'start': start-3,
|
|
||||||
'end': pre_rtarget})
|
if 2.2 <= self.version <= 2.6:
|
||||||
|
# Take the JUMP_IF target. In an "if/then", it will be
|
||||||
|
# a POP_TOP instruction and the instruction before it
|
||||||
|
# will be a JUMP_FORWARD to just after the POP_TOP.
|
||||||
|
# For example:
|
||||||
|
# Good:
|
||||||
|
# 3 JUMP_IF_FALSE 33 'to 39'
|
||||||
|
# ..
|
||||||
|
# 36 JUMP_FORWARD 1 'to 40'
|
||||||
|
# 39 POP_TOP
|
||||||
|
# 40 ...
|
||||||
|
# example:
|
||||||
|
|
||||||
|
# BAD (is an "and"):
|
||||||
|
# 28 JUMP_IF_FALSE 4 'to 35'
|
||||||
|
# ...
|
||||||
|
# 32 JUMP_ABSOLUTE 40 'to 40' # should be 36 or there should
|
||||||
|
# # be a COME_FROM at the pop top
|
||||||
|
# # before 40 to 35
|
||||||
|
# 35 POP_TOP
|
||||||
|
# 36 ...
|
||||||
|
# 39 POP_TOP
|
||||||
|
# 39_0 COME_FROM 3
|
||||||
|
# 40 ...
|
||||||
|
|
||||||
|
if self.opc.opname[code[jump_if_offset]].startswith('JUMP_IF'):
|
||||||
|
jump_if_target = code[jump_if_offset+1]
|
||||||
|
if self.opc.opname[code[jump_if_target + jump_if_offset + 3]] == 'POP_TOP':
|
||||||
|
jump_inst = jump_if_target + jump_if_offset
|
||||||
|
jump_offset = code[jump_inst+1]
|
||||||
|
jump_op = self.opc.opname[code[jump_inst]]
|
||||||
|
if (jump_op == 'JUMP_FORWARD' and jump_offset == 1):
|
||||||
|
self.structs.append({'type': 'if-then',
|
||||||
|
'start': start-3,
|
||||||
|
'end': pre_rtarget})
|
||||||
|
|
||||||
|
self.thens[start] = end
|
||||||
|
elif jump_op == 'JUMP_ABSOLUTE':
|
||||||
|
if_then_maybe = {'type': 'if-then',
|
||||||
|
'start': start-3,
|
||||||
|
'end': pre_rtarget}
|
||||||
|
|
||||||
|
elif self.version == 2.7:
|
||||||
|
self.structs.append({'type': 'if-then',
|
||||||
|
'start': start-3,
|
||||||
|
'end': pre_rtarget})
|
||||||
|
|
||||||
self.not_continue.add(pre_rtarget)
|
self.not_continue.add(pre_rtarget)
|
||||||
|
|
||||||
if rtarget < end:
|
if rtarget < end:
|
||||||
|
# We have an "else" block of some kind.
|
||||||
|
# Is it associated with "if_then_maybe" seen above?
|
||||||
|
# These will be linked in this funny way:
|
||||||
|
|
||||||
|
# 198 JUMP_IF_FALSE 18 'to 219'
|
||||||
|
# 201 POP_TOP
|
||||||
|
# ...
|
||||||
|
# 216 JUMP_ABSOLUTE 256 'to 256'
|
||||||
|
# 219 POP_TOP
|
||||||
|
# ...
|
||||||
|
# 252 JUMP_FORWARD 1 'to 256'
|
||||||
|
# 255 POP_TOP
|
||||||
|
# 256
|
||||||
|
if if_then_maybe and jump_op == 'JUMP_ABSOLUTE':
|
||||||
|
jump_target = self.get_target(jump_inst, code[jump_inst])
|
||||||
|
if self.opc.opname[code[end]] == 'JUMP_FORWARD':
|
||||||
|
end_target = self.get_target(end, code[end])
|
||||||
|
if jump_target == end_target:
|
||||||
|
self.structs.append(if_then_maybe)
|
||||||
|
self.thens[start] = end
|
||||||
|
|
||||||
self.structs.append({'type': 'else',
|
self.structs.append({'type': 'else',
|
||||||
'start': rtarget,
|
'start': rtarget,
|
||||||
'end': end})
|
'end': end})
|
||||||
@@ -849,6 +918,7 @@ class Scanner2(scan.Scanner):
|
|||||||
self.structs.append({'type': 'if-then',
|
self.structs.append({'type': 'if-then',
|
||||||
'start': start,
|
'start': start,
|
||||||
'end': rtarget})
|
'end': rtarget})
|
||||||
|
self.thens[start] = rtarget
|
||||||
if self.version == 2.7 or code[pre_rtarget+1] != self.opc.JUMP_FORWARD:
|
if self.version == 2.7 or code[pre_rtarget+1] != self.opc.JUMP_FORWARD:
|
||||||
self.return_end_ifs.add(pre_rtarget)
|
self.return_end_ifs.add(pre_rtarget)
|
||||||
|
|
||||||
@@ -887,6 +957,7 @@ class Scanner2(scan.Scanner):
|
|||||||
self.return_end_ifs = set()
|
self.return_end_ifs = set()
|
||||||
self.setup_loop_targets = {} # target given setup_loop offset
|
self.setup_loop_targets = {} # target given setup_loop offset
|
||||||
self.setup_loops = {} # setup_loop offset given target
|
self.setup_loops = {} # setup_loop offset given target
|
||||||
|
self.thens = {} # JUMP_IF's that separate the 'then' part of an 'if'
|
||||||
|
|
||||||
targets = {}
|
targets = {}
|
||||||
for offset in self.op_range(0, n):
|
for offset in self.op_range(0, n):
|
||||||
|
@@ -94,35 +94,32 @@ class Scanner26(scan.Scanner2):
|
|||||||
for instr in bytecode.get_instructions(co):
|
for instr in bytecode.get_instructions(co):
|
||||||
print(instr._disassemble())
|
print(instr._disassemble())
|
||||||
|
|
||||||
# from xdis.bytecode import Bytecode
|
|
||||||
# bytecode = Bytecode(co, self.opc)
|
|
||||||
# for instr in bytecode.get_instructions(co):
|
|
||||||
# print(instr._disassemble())
|
|
||||||
|
|
||||||
# Container for tokens
|
# Container for tokens
|
||||||
tokens = []
|
tokens = []
|
||||||
|
|
||||||
customize = {}
|
customize = {}
|
||||||
|
if self.is_pypy:
|
||||||
|
customize['PyPy'] = 1
|
||||||
|
|
||||||
Token = self.Token # shortcut
|
Token = self.Token # shortcut
|
||||||
|
|
||||||
n = self.setup_code(co)
|
codelen = self.setup_code(co)
|
||||||
|
|
||||||
self.build_lines_data(co, n)
|
self.build_lines_data(co, codelen)
|
||||||
self.build_prev_op(n)
|
self.build_prev_op(codelen)
|
||||||
|
|
||||||
free, names, varnames = self.unmangle_code_names(co, classname)
|
free, names, varnames = self.unmangle_code_names(co, classname)
|
||||||
self.names = names
|
self.names = names
|
||||||
|
|
||||||
codelen = len(self.code)
|
|
||||||
|
|
||||||
# Scan for assertions. Later we will
|
# Scan for assertions. Later we will
|
||||||
# turn 'LOAD_GLOBAL' to 'LOAD_ASSERT'.
|
# turn 'LOAD_GLOBAL' to 'LOAD_ASSERT'.
|
||||||
# 'LOAD_ASSERT' is used in assert statements.
|
# 'LOAD_ASSERT' is used in assert statements.
|
||||||
self.load_asserts = set()
|
self.load_asserts = set()
|
||||||
for i in self.op_range(0, n):
|
for i in self.op_range(0, codelen):
|
||||||
# We need to detect the difference between
|
# We need to detect the difference between:
|
||||||
# "raise AssertionError" and
|
# raise AssertionError
|
||||||
# "assert"
|
# and
|
||||||
|
# assert ...
|
||||||
if (self.code[i] == self.opc.JUMP_IF_TRUE and
|
if (self.code[i] == self.opc.JUMP_IF_TRUE and
|
||||||
i + 4 < codelen and
|
i + 4 < codelen and
|
||||||
self.code[i+3] == self.opc.POP_TOP and
|
self.code[i+3] == self.opc.POP_TOP and
|
||||||
@@ -167,6 +164,11 @@ class Scanner26(scan.Scanner2):
|
|||||||
offset="%s_%d" % (offset, jump_idx),
|
offset="%s_%d" % (offset, jump_idx),
|
||||||
has_arg = True))
|
has_arg = True))
|
||||||
jump_idx += 1
|
jump_idx += 1
|
||||||
|
elif offset in self.thens:
|
||||||
|
tokens.append(Token(
|
||||||
|
'THEN', None, self.thens[offset],
|
||||||
|
offset="%s_0" % offset,
|
||||||
|
has_arg = True))
|
||||||
|
|
||||||
has_arg = (op >= self.opc.HAVE_ARGUMENT)
|
has_arg = (op >= self.opc.HAVE_ARGUMENT)
|
||||||
if has_arg:
|
if has_arg:
|
||||||
|
@@ -301,7 +301,7 @@ TABLE_DIRECT = {
|
|||||||
'ifstmt': ( '%|if %c:\n%+%c%-', 0, 1 ),
|
'ifstmt': ( '%|if %c:\n%+%c%-', 0, 1 ),
|
||||||
'iflaststmt': ( '%|if %c:\n%+%c%-', 0, 1 ),
|
'iflaststmt': ( '%|if %c:\n%+%c%-', 0, 1 ),
|
||||||
'iflaststmtl': ( '%|if %c:\n%+%c%-', 0, 1 ),
|
'iflaststmtl': ( '%|if %c:\n%+%c%-', 0, 1 ),
|
||||||
'testtrue': ( 'not %p', (0, 22) ),
|
'testtrue': ( 'not %p', (0, 22) ),
|
||||||
|
|
||||||
'ifelsestmt': ( '%|if %c:\n%+%c%-%|else:\n%+%c%-', 0, 1, 3 ),
|
'ifelsestmt': ( '%|if %c:\n%+%c%-%|else:\n%+%c%-', 0, 1, 3 ),
|
||||||
'ifelsestmtc': ( '%|if %c:\n%+%c%-%|else:\n%+%c%-', 0, 1, 3 ),
|
'ifelsestmtc': ( '%|if %c:\n%+%c%-%|else:\n%+%c%-', 0, 1, 3 ),
|
||||||
@@ -589,7 +589,9 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
TABLE_DIRECT.update({
|
TABLE_DIRECT.update({
|
||||||
'except_cond3': ( '%|except %c, %c:\n', 1, 6 ),
|
'except_cond3': ( '%|except %c, %c:\n', 1, 6 ),
|
||||||
|
'testtrue_then': ( 'not %p', (0, 22) ),
|
||||||
|
|
||||||
})
|
})
|
||||||
if 2.4 <= version <= 2.6:
|
if 2.4 <= version <= 2.6:
|
||||||
TABLE_DIRECT.update({
|
TABLE_DIRECT.update({
|
||||||
|
Reference in New Issue
Block a user