WIP: Handle EXTENDED_ARGS better

This commit is contained in:
rocky
2018-02-25 08:22:07 -05:00
parent 8463221527
commit aaf83ea35d

View File

@@ -42,6 +42,38 @@ if PYTHON3:
globals().update(op3.opmap) globals().update(op3.opmap)
def remove_extended_args(instructions, prev_op):
"""Go through instructions removing extended ARG.
get_instruction_bytes previously adjusted the operand values
to account for these"""
new_instructions = []
last_was_extarg = False
n = len(instructions)
for i, inst in enumerate(instructions):
if (inst.opname == 'EXTENDED_ARG' and
i+1 < n and instructions[i+1].opname != 'MAKE_FUNCTION'):
last_was_extarg = True
starts_line = inst.starts_line
is_jump_target = inst.is_jump_target
offset = inst.offset
continue
if last_was_extarg:
new_inst= inst._replace(starts_line=starts_line,
is_jump_target=is_jump_target,
offset=offset)
inst = new_inst
if i < n:
j = instructions[i+1].offset
old_prev = prev_op[j]
while prev_op[j] == old_prev and j < n:
prev_op[j] = prev_op[i]
j += 1
last_was_extarg = False
new_instructions.append(inst)
return new_instructions
class Scanner3(Scanner): class Scanner3(Scanner):
def __init__(self, version, show_asm=None, is_pypy=False): def __init__(self, version, show_asm=None, is_pypy=False):
@@ -189,7 +221,8 @@ class Scanner3(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()
self.insts = list(bytecode) self.insts = remove_extended_args(list(bytecode), self.prev_op)
self.offset2inst_index = {} self.offset2inst_index = {}
n = len(self.insts) n = len(self.insts)
for i, inst in enumerate(self.insts): for i, inst in enumerate(self.insts):
@@ -219,15 +252,16 @@ class Scanner3(Scanner):
last_op_was_break = False last_op_was_break = False
for i, inst in enumerate(bytecode): for i, inst in enumerate(self.insts):
argval = inst.argval argval = inst.argval
op = inst.opcode op = inst.opcode
if op == self.opc.EXTENDED_ARG: if inst.opname == 'EXTENDED_ARG':
# FIXME: The EXTENDED_ARG is used to signal annotation # FIXME: The EXTENDED_ARG is used to signal annotation
# parameters # parameters
if self.insts[i+1].opcode != self.opc.MAKE_FUNCTION: if (i+1 < n and
self.insts[i+1].opcode != self.opc.MAKE_FUNCTION):
continue continue
if inst.offset in jump_targets: if inst.offset in jump_targets:
@@ -622,23 +656,9 @@ class Scanner3(Scanner):
def get_target(self, offset, extended_arg=0): def get_target(self, offset, extended_arg=0):
""" """
Get target offset for op located at given <offset>. Get target offset for op located at given <offset>.
NOTE: extended_arg is no longer used
""" """
op = self.code[offset] return self.insts[self.offset2inst_index[offset]].argval
rel_offset = 0
if self.version >= 3.6:
target = self.code[offset+1]
if op in self.opc.JREL_OPS:
rel_offset = offset + 2
else:
target = self.code[offset+1] + self.code[offset+2] * 256
if op in self.opc.JREL_OPS:
rel_offset = offset + 3
pass
pass
target += rel_offset
target += extended_arg
return target
def detect_control_flow(self, offset, targets, inst_index): def detect_control_flow(self, offset, targets, inst_index):
""" """
@@ -649,7 +669,7 @@ class Scanner3(Scanner):
# TODO: check the struct boundaries more precisely -Dan # TODO: check the struct boundaries more precisely -Dan
code = self.code code = self.code
op = code[offset] op = self.insts[inst_index].opcode
# Detect parent structure # Detect parent structure
parent = self.structs[0] parent = self.structs[0]
@@ -673,8 +693,7 @@ class Scanner3(Scanner):
# It could be a return instruction. # It could be a return instruction.
start += instruction_size(op, self.opc) start += instruction_size(op, self.opc)
# FIXME: 0 = extended arg which is not right. Use self.insts instead target = self.get_target(offset)
target = self.get_target(offset, 0)
end = self.restrict_to_parent(target, parent) end = self.restrict_to_parent(target, parent)
self.setup_loops[target] = offset self.setup_loops[target] = offset
@@ -715,7 +734,7 @@ class Scanner3(Scanner):
target = next_line_byte target = next_line_byte
end = xdis.next_offset(code[jump_back], self.opc, jump_back) end = xdis.next_offset(code[jump_back], self.opc, jump_back)
else: else:
if self.get_target(jump_back, 0) >= next_line_byte: if self.get_target(jump_back) >= next_line_byte:
jump_back = self.last_instr(start, end, self.opc.JUMP_ABSOLUTE, start, False) jump_back = self.last_instr(start, end, self.opc.JUMP_ABSOLUTE, start, False)
# This is wrong for 3.6+ # This is wrong for 3.6+
@@ -728,21 +747,20 @@ class Scanner3(Scanner):
self.fixed_jumps[offset] = jump_back+4 self.fixed_jumps[offset] = jump_back+4
end = jump_back+4 end = jump_back+4
# I think 0 right because jump_back has been adjusted for any EXTENDED_ARG
# it encounters
target = self.get_target(jump_back) target = self.get_target(jump_back)
if code[target] in (self.opc.FOR_ITER, self.opc.GET_ITER): if code[target] in (self.opc.FOR_ITER, self.opc.GET_ITER):
loop_type = 'for' loop_type = 'for'
else: else:
loop_type = 'while' loop_type = 'while'
test = self.prev_op[next_line_byte] test_inst = self.insts[self.offset2inst_index[next_line_byte]-1]
# test = self.prev_op[next_line_byte]
if test == offset: if test_inst.offset == offset:
loop_type = 'while 1' loop_type = 'while 1'
elif self.code[test] in self.opc.JUMP_OPs: elif test_inst.opcode in self.opc.JUMP_OPs:
self.ignore_if.add(test) self.ignore_if.add(test_inst.offset)
test_target = self.get_target(test) test_target = self.get_target(test_inst.offset)
if test_target > (jump_back+3): if test_target > (jump_back+3):
jump_back = test_target jump_back = test_target
self.not_continue.add(jump_back) self.not_continue.add(jump_back)
@@ -888,7 +906,7 @@ class Scanner3(Scanner):
# like whether the target is "END_FINALLY" # like whether the target is "END_FINALLY"
# or if the condition jump is to a forward location # or if the condition jump is to a forward location
if self.is_jump_forward(pre_rtarget): if self.is_jump_forward(pre_rtarget):
if_end = self.get_target(pre_rtarget, 0) if_end = self.get_target(pre_rtarget)
# If the jump target is back, we are looping # If the jump target is back, we are looping
if (if_end < pre_rtarget and if (if_end < pre_rtarget and