def last_instr(self, start: int, end: int, instr, target=None, exact=True) -> Optional[int]: """ Find the last <instr> in the block from start to end. <instr> is any python bytecode instruction or a list of opcodes If <instr> is an opcode with a target (like a jump), a target destination can be specified which must match precisely if exact is True, or if exact is False, the instruction which has a target closest to <target> will be returned. Return index to it or None if not found. """ code = self.code # Make sure requested positions do not go out of # code bounds if not (start >= 0 and end <= len(code)): return None if not isinstance(instr, list): instr = [instr] result_offset = None current_distance = self.insts[-1].offset - self.insts[0].offset extended_arg = 0 # FIXME: use self.insts rather than code[] for offset in self.op_range(start, end): op = code[offset] if op == self.opc.EXTENDED_ARG: arg = code2num(code, offset + 1) | extended_arg extended_arg = extended_arg_val(self.opc, arg) continue if op in instr: if target is None: result_offset = offset else: dest = self.get_target(offset, extended_arg) if dest == target: current_distance = 0 result_offset = offset elif not exact: new_distance = abs(target - dest) if new_distance <= current_distance: current_distance = new_distance result_offset = offset pass pass pass pass extended_arg = 0 pass return result_offset
def all_instr(self, start, end, instr, target=None, include_beyond_target=False): """ Find all `instr` in the block from start to end. `instr` is any Python opcode or a list of opcodes If `instr` is an opcode with a target (like a jump), a target destination can be specified which must match precisely. Return a list with indexes to them or [] if none found. """ code = self.code assert start >= 0 and end <= len(code) try: None in instr except: instr = [instr] result = [] extended_arg = 0 for offset in self.op_range(start, end): op = code[offset] if op == self.opc.EXTENDED_ARG: arg = code2num(code, offset + 1) | extended_arg extended_arg = extended_arg_val(self.opc, arg) continue if op in instr: if target is None: result.append(offset) else: t = self.get_target(offset, extended_arg) if include_beyond_target and t >= target: result.append(offset) elif t == target: result.append(offset) pass pass pass extended_arg = 0 pass return result
def parse_byte_and_args(self, byte_code, replay=False): """ Parse 1 - 3 bytes of bytecode into an instruction and optionally arguments. Argument replay is used to handle breakpoints. """ f = self.frame f_code = f.f_code co_code = f_code.co_code extended_arg = 0 # Note: There is never more than one argument. # The list size is used to indicate whether an argument # exists or not. # FIMXE: remove and use int_arg as a indicator of whether # the argument exists. arguments = [] int_arg = None while True: if f.fallthrough: if not replay: f.f_lasti = next_offset(byte_code, self.opc, f.f_lasti) else: # Jump instructions must set this False. f.fallthrough = True offset = f.f_lasti line_number = self.frame.linestarts.get(offset, None) if line_number is not None: f.f_lineno = line_number if not replay: byte_code = byteint(co_code[offset]) byte_name = self.opc.opname[byte_code] arg_offset = offset + 1 arg = None if op_has_argument(byte_code, self.opc): if self.version >= 3.6: int_arg = code2num(co_code, arg_offset) | extended_arg # Note: Python 3.6.0a1 is 2, for 3.6.a3 and beyond we have 1 arg_offset += 1 if byte_code == self.opc.EXTENDED_ARG: extended_arg = int_arg << 8 continue else: extended_arg = 0 else: int_arg = (code2num(co_code, arg_offset) + code2num(co_code, arg_offset + 1) * 256 + extended_arg) arg_offset += 2 if byte_code == self.opc.EXTENDED_ARG: extended_arg = int_arg * 65536 continue else: extended_arg = 0 if byte_code in self.opc.CONST_OPS: arg = f_code.co_consts[int_arg] elif byte_code in self.opc.FREE_OPS: if int_arg < len(f_code.co_cellvars): arg = f_code.co_cellvars[int_arg] else: var_idx = int_arg - len(f.f_code.co_cellvars) arg = f_code.co_freevars[var_idx] elif byte_code in self.opc.NAME_OPS: arg = f_code.co_names[int_arg] elif byte_code in self.opc.JREL_OPS: # Many relative jumps are conditional, # so setting f.fallthrough is wrong. arg = arg_offset + int_arg elif byte_code in self.opc.JABS_OPS: # We probably could set fallthough, since many (all?) # of these are unconditional, but we'll make the jump do # the work of setting. arg = int_arg elif byte_code in self.opc.LOCAL_OPS: arg = f_code.co_varnames[int_arg] else: arg = int_arg arguments = [arg] break return byte_name, byte_code, int_arg, arguments, offset, line_number