You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-02 16:44:46 +08:00
Simplify EXTENDED_ARG on 3.x
We largely remove them and fold them itno the next op. MAKE_FUNCTION though before 3.6 is an exception as that indicates an annotated function
This commit is contained in:
@@ -627,12 +627,6 @@ class Python3Parser(PythonParser):
|
||||
self.custom_build_class_rule(opname, i, token, tokens, customize)
|
||||
elif opname_base in ('BUILD_LIST', 'BUILD_TUPLE', 'BUILD_SET'):
|
||||
v = token.attr
|
||||
if self.version >= 3.6:
|
||||
extended_arg = 1 << 8
|
||||
else:
|
||||
extended_arg = 1 << 16
|
||||
if v >= extended_arg:
|
||||
opname = "EXTENDED_ARG %s" % opname
|
||||
rule = ('build_list ::= ' + 'expr1024 ' * int(v//1024) +
|
||||
'expr32 ' * int((v//32) % 32) +
|
||||
'expr ' * (v % 32) + opname)
|
||||
@@ -764,7 +758,18 @@ class Python3Parser(PythonParser):
|
||||
('pos_arg ' * args_pos, opname))
|
||||
self.add_unique_rule(rule, opname, token.attr, customize)
|
||||
if opname.startswith('MAKE_FUNCTION_A'):
|
||||
if self.version >= 3.6:
|
||||
rule = ('mkfunc_annotate ::= %s%sannotate_tuple LOAD_CONST LOAD_CONST %s' %
|
||||
(('pos_arg ' * (args_pos)),
|
||||
('call_function ' * (annotate_args-1)), opname))
|
||||
self.add_unique_rule(rule, opname, token.attr, customize)
|
||||
rule = ('mkfunc_annotate ::= %s%sannotate_tuple LOAD_CONST LOAD_CONST %s' %
|
||||
(('pos_arg ' * (args_pos)),
|
||||
('annotate_arg ' * (annotate_args-1)), opname))
|
||||
if self.version >= 3.3:
|
||||
# Normally we remove EXTENDED_ARG from the opcodes, but in the case of
|
||||
# annotated functions can use the EXTENDED_ARG tuple to signal we have an annotated function.
|
||||
# Yes this is a little hacky
|
||||
rule = ('mkfunc_annotate ::= %s%sannotate_tuple LOAD_CONST LOAD_CONST EXTENDED_ARG %s' %
|
||||
(('pos_arg ' * (args_pos)),
|
||||
('call_function ' * (annotate_args-1)), opname))
|
||||
@@ -773,6 +778,7 @@ class Python3Parser(PythonParser):
|
||||
(('pos_arg ' * (args_pos)),
|
||||
('annotate_arg ' * (annotate_args-1)), opname))
|
||||
else:
|
||||
# See above comment about use of EXTENDED_ARG
|
||||
rule = ('mkfunc_annotate ::= %s%sannotate_tuple LOAD_CONST EXTENDED_ARG %s' %
|
||||
(('pos_arg ' * (args_pos)),
|
||||
('annotate_arg ' * (annotate_args-1)), opname))
|
||||
|
@@ -14,63 +14,12 @@ class Python36Parser(Python35Parser):
|
||||
super(Python36Parser, self).__init__(debug_parser)
|
||||
self.customized = {}
|
||||
|
||||
def p_loop_stmt3(self, args):
|
||||
"""
|
||||
# The range of values in arguments in 3.6 has been reduced from 2**16 to 2**8.
|
||||
# As a result, EXTENDED_ARG needs to be used where it didn't before.
|
||||
# Is this relevant to < 3.6 as well?
|
||||
|
||||
|
||||
setup_loop ::= SETUP_LOOP
|
||||
setup_loop ::= EXTENDED_ARG SETUP_LOOP
|
||||
|
||||
forstmt ::= setup_loop expr _for designator for_block POP_BLOCK
|
||||
opt_come_from_loop
|
||||
|
||||
forelsestmt ::= setup_loop expr _for designator for_block POP_BLOCK else_suite
|
||||
COME_FROM_LOOP
|
||||
|
||||
forelselaststmt ::= setup_loop expr _for designator for_block POP_BLOCK else_suitec
|
||||
COME_FROM_LOOP
|
||||
|
||||
forelselaststmtl ::= setup_loop expr _for designator for_block POP_BLOCK else_suitel
|
||||
COME_FROM_LOOP
|
||||
|
||||
whilestmt ::= setup_loop testexpr l_stmts_opt COME_FROM jump_back POP_BLOCK
|
||||
COME_FROM_LOOP
|
||||
|
||||
whilestmt ::= setup_loop testexpr l_stmts_opt jump_back POP_BLOCK
|
||||
COME_FROM_LOOP
|
||||
|
||||
whileTruestmt ::= setup_loop l_stmts_opt JUMP_BACK POP_BLOCK COME_FROM_LOOP
|
||||
"""
|
||||
|
||||
|
||||
def p_36misc(self, args):
|
||||
"""
|
||||
# The range of values in arguments in 3.6 has been reduced from 2**16 to 2**8.
|
||||
# As a result, EXTENDED_ARG needs to be used where it didn't before.
|
||||
# Is this relevant to < 3.6 as well?
|
||||
|
||||
jmp_false ::= EXTENDED_ARG POP_JUMP_IF_FALSE
|
||||
jmp_true ::= EXTENDED_ARG POP_JUMP_IF_TRUE
|
||||
_jump ::= EXTENDED_ARG JUMP_BACK
|
||||
jump_back ::= EXTENDED_ARG JUMP_BACK
|
||||
|
||||
continue_stmt ::= EXTENDED_ARG CONTINUE
|
||||
continue_stmt ::= EXTENDED_ARG CONTINUE_LOOP
|
||||
|
||||
for_block ::= l_stmts_opt opt_come_from_loop jump_back
|
||||
cmp_list1 ::= expr DUP_TOP ROT_THREE COMPARE_OP
|
||||
EXTENDED_ARG JUMP_IF_FALSE_OR_POP cmp_list2 COME_FROM
|
||||
|
||||
|
||||
# 3.6 redoes how return_closure works
|
||||
return_closure ::= LOAD_CLOSURE DUP_TOP STORE_NAME RETURN_VALUE RETURN_LAST
|
||||
|
||||
expr ::= LOAD_NAME EXTENDED_ARG
|
||||
expr ::= LOAD_CONST EXTENDED_ARG
|
||||
|
||||
fstring_multi ::= fstring_expr_or_strs BUILD_STRING
|
||||
fstring_expr_or_strs ::= fstring_expr_or_str+
|
||||
|
||||
@@ -86,29 +35,6 @@ class Python36Parser(Python35Parser):
|
||||
|
||||
def add_custom_rules(self, tokens, customize):
|
||||
super(Python36Parser, self).add_custom_rules(tokens, customize)
|
||||
self.remove_rule("""
|
||||
forstmt ::= SETUP_LOOP expr _for designator for_block POP_BLOCK
|
||||
opt_come_from_loop
|
||||
|
||||
forelsestmt ::= SETUP_LOOP expr _for designator for_block POP_BLOCK else_suite
|
||||
COME_FROM_LOOP
|
||||
|
||||
forelselaststmt ::= SETUP_LOOP expr _for designator for_block POP_BLOCK else_suitec
|
||||
COME_FROM_LOOP
|
||||
|
||||
forelselaststmtl ::= SETUP_LOOP expr _for designator for_block POP_BLOCK else_suitel
|
||||
COME_FROM_LOOP
|
||||
|
||||
whilestmt ::= SETUP_LOOP testexpr l_stmts_opt COME_FROM jump_back POP_BLOCK
|
||||
COME_FROM_LOOP
|
||||
|
||||
whilestmt ::= SETUP_LOOP testexpr l_stmts_opt jump_back POP_BLOCK
|
||||
COME_FROM_LOOP
|
||||
|
||||
whileTruestmt ::= SETUP_LOOP l_stmts_opt JUMP_BACK POP_BLOCK COME_FROM_LOOP
|
||||
COME_FROM_LOOP
|
||||
""")
|
||||
|
||||
for i, token in enumerate(tokens):
|
||||
opname = token.type
|
||||
|
||||
|
@@ -208,9 +208,12 @@ class Scanner3(Scanner):
|
||||
jump_targets = self.find_jump_targets(show_asm)
|
||||
last_op_was_break = False
|
||||
|
||||
for inst in bytecode:
|
||||
extended_arg = 0
|
||||
for i, inst in enumerate(bytecode):
|
||||
|
||||
argval = inst.argval
|
||||
if isinstance(argval, int):
|
||||
argval += extended_arg
|
||||
if inst.offset in jump_targets:
|
||||
jump_idx = 0
|
||||
# We want to process COME_FROMs to the same offset to be in *descending*
|
||||
@@ -247,12 +250,28 @@ class Scanner3(Scanner):
|
||||
|
||||
pass
|
||||
|
||||
pattr = inst.argrepr
|
||||
pattr = inst.argrepr
|
||||
opname = inst.opname
|
||||
op = inst.opcode
|
||||
op = inst.opcode
|
||||
|
||||
has_arg = op_has_argument(op, self.opc)
|
||||
if has_arg:
|
||||
extended_arg = 0
|
||||
if op == self.opc.EXTENDED_ARG:
|
||||
if self.version < 3.6:
|
||||
extended_arg = argval * (1<<16)
|
||||
else:
|
||||
extended_arg = argval * (1<<8)
|
||||
|
||||
# Normally we remove EXTENDED_ARG from the
|
||||
# opcodes, but in the case of annotated functions
|
||||
# can use the EXTENDED_ARG tuple to signal we have
|
||||
# an annotated function.
|
||||
if not bs[i+1].opname.startswith("MAKE_FUNCTION"):
|
||||
continue
|
||||
|
||||
if opname in ['LOAD_CONST']:
|
||||
const = inst.argval
|
||||
const = argval
|
||||
if iscode(const):
|
||||
if const.co_name == '<lambda>':
|
||||
opname = 'LOAD_LAMBDA'
|
||||
@@ -276,7 +295,7 @@ class Scanner3(Scanner):
|
||||
elif opname in ('MAKE_FUNCTION', 'MAKE_CLOSURE'):
|
||||
if self.version >= 3.6:
|
||||
# 3.6+ doesn't have MAKE_CLOSURE, so opname == 'MAKE_FUNCTION'
|
||||
flags = inst.argval
|
||||
flags = argval
|
||||
opname = 'MAKE_FUNCTION_%d' % (flags)
|
||||
attr = []
|
||||
for flag in self.MAKE_FUNCTION_FLAGS:
|
||||
@@ -315,7 +334,7 @@ class Scanner3(Scanner):
|
||||
)
|
||||
continue
|
||||
elif op in self.varargs_ops:
|
||||
pos_args = inst.argval
|
||||
pos_args = argval
|
||||
if self.is_pypy and not pos_args and opname == 'BUILD_MAP':
|
||||
opname = 'BUILD_MAP_n'
|
||||
else:
|
||||
@@ -327,9 +346,9 @@ class Scanner3(Scanner):
|
||||
customize[opname] = 0
|
||||
elif opname == 'UNPACK_EX':
|
||||
# FIXME: try with scanner and parser by
|
||||
# changing inst.argval
|
||||
before_args = inst.argval & 0xFF
|
||||
after_args = (inst.argval >> 8) & 0xff
|
||||
# changing argval
|
||||
before_args = argval & 0xFF
|
||||
after_args = (argval >> 8) & 0xff
|
||||
pattr = "%d before vararg, %d after" % (before_args, after_args)
|
||||
argval = (before_args, after_args)
|
||||
opname = '%s_%d+%d' % (opname, before_args, after_args)
|
||||
@@ -346,7 +365,7 @@ class Scanner3(Scanner):
|
||||
# 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 = argval
|
||||
target = self.get_target(inst.offset)
|
||||
if target <= inst.offset:
|
||||
next_opname = self.opname[self.code[inst.offset+3]]
|
||||
@@ -606,27 +625,16 @@ class Scanner3(Scanner):
|
||||
rel_offset = 0
|
||||
if self.version >= 3.6:
|
||||
target = self.code[offset+1]
|
||||
arg_offset = 1
|
||||
extended_arg_mult = 1 << 8
|
||||
if op in self.opc.hasjrel:
|
||||
rel_offset = offset + 2
|
||||
else:
|
||||
target = self.code[offset+1] + self.code[offset+2] * 256
|
||||
arg_offset = 2
|
||||
extended_arg_mult = 1 << 16
|
||||
if op in self.opc.hasjrel:
|
||||
rel_offset = offset + 3
|
||||
pass
|
||||
pass
|
||||
target += rel_offset
|
||||
|
||||
if offset > 0:
|
||||
prev_offset = self.prev_op[offset]
|
||||
prev_op = self.code[prev_offset]
|
||||
if prev_op == self.opc.EXTENDED_ARG:
|
||||
target += (self.code[prev_offset + arg_offset]
|
||||
* extended_arg_mult)
|
||||
pass
|
||||
return target
|
||||
|
||||
def detect_control_flow(self, offset, targets):
|
||||
|
Reference in New Issue
Block a user