def get_instructions_to_add( stop_at_line, _pydev_stop_at_break=_pydev_stop_at_break, _pydev_needs_stop_at_break=_pydev_needs_stop_at_break): ''' This is the bytecode for something as: if _pydev_needs_stop_at_break(): _pydev_stop_at_break() but with some special handling for lines. ''' # Good reference to how things work regarding line numbers and jumps: # https://github.com/python/cpython/blob/3.6/Objects/lnotab_notes.txt label = Label() return [ # -- if _pydev_needs_stop_at_break(): Instr("LOAD_CONST", _pydev_needs_stop_at_break, lineno=stop_at_line), Instr("LOAD_CONST", stop_at_line, lineno=stop_at_line), Instr("CALL_FUNCTION", 1, lineno=stop_at_line), Instr("POP_JUMP_IF_FALSE", label, lineno=stop_at_line), # -- _pydev_stop_at_break() # # Note that this has line numbers -1 so that when the NOP just below # is executed we have a spurious line event. Instr("LOAD_CONST", _pydev_stop_at_break, lineno=stop_at_line - 1), Instr("LOAD_CONST", stop_at_line, lineno=stop_at_line - 1), Instr("CALL_FUNCTION", 1, lineno=stop_at_line - 1), Instr("POP_TOP", lineno=stop_at_line - 1), # Reason for the NOP: Python will give us a 'line' trace event whenever we forward jump to # the first instruction of a line, so, in the case where we haven't added a programmatic # breakpoint (either because we didn't hit a breakpoint anymore or because it was already # tracing), we don't want the spurious line event due to the line change, so, we make a jump # to the instruction right after the NOP so that the spurious line event is NOT generated in # this case (otherwise we'd have a line event even if the line didn't change). Instr("NOP", lineno=stop_at_line), label, ]
def to_bytecode(self): """Convert to Bytecode.""" used_blocks = set() for block in self: target_block = block.get_jump() if target_block is not None: used_blocks.add(id(target_block)) labels = {} jumps = [] instructions = [] for block in self: if id(block) in used_blocks: new_label = Label() labels[id(block)] = new_label instructions.append(new_label) for instr in block: # don't copy SetLineno objects if isinstance(instr, Instr): instr = instr.copy() if isinstance(instr.arg, BasicBlock): jumps.append(instr) instructions.append(instr) # Map to new labels for instr in jumps: instr.arg = labels[id(instr.arg)] bytecode = _bytecode.Bytecode() bytecode._copy_attr_from(self) bytecode.argnames = list(self.argnames) bytecode[:] = instructions return bytecode
def to_bytecode(self): # Copy instruction and remove extended args if any (in-place) c_instructions = self[:] self._remove_extended_args(c_instructions) # find jump targets jump_targets = set() offset = 0 for instr in c_instructions: if isinstance(instr, SetLineno): continue target = instr.get_jump_target(offset) if target is not None: jump_targets.add(target) offset += instr.size # create labels jumps = [] instructions = [] labels = {} offset = 0 ncells = len(self.cellvars) for lineno, instr in self._normalize_lineno(c_instructions, self.first_lineno): if offset in jump_targets: label = Label() labels[offset] = label instructions.append(label) jump_target = instr.get_jump_target(offset) size = instr.size arg = instr.arg # FIXME: better error reporting if instr.opcode in _opcode.hasconst: arg = self.consts[arg] elif instr.opcode in _opcode.haslocal: arg = self.varnames[arg] elif instr.opcode in _opcode.hasname: arg = self.names[arg] elif instr.opcode in _opcode.hasfree: if arg < ncells: name = self.cellvars[arg] arg = CellVar(name) else: name = self.freevars[arg - ncells] arg = FreeVar(name) elif instr.opcode in _opcode.hascompare: arg = Compare(arg) if jump_target is None: instr = Instr(instr.name, arg, lineno=lineno, offset=instr.offset) else: instr_index = len(instructions) instructions.append(instr) offset += size if jump_target is not None: jumps.append((instr_index, jump_target)) # replace jump targets with labels for index, jump_target in jumps: instr = instructions[index] # FIXME: better error reporting on missing label label = labels[jump_target] instructions[index] = Instr(instr.name, label, lineno=instr.lineno, offset=instr.offset) bytecode = _bytecode.Bytecode() bytecode._copy_attr_from(self) nargs = bytecode.argcount + bytecode.kwonlyargcount if sys.version_info > (3, 8): nargs += bytecode.posonlyargcount if bytecode.flags & inspect.CO_VARARGS: nargs += 1 if bytecode.flags & inspect.CO_VARKEYWORDS: nargs += 1 bytecode.argnames = self.varnames[:nargs] _set_docstring(bytecode, self.consts) bytecode.extend(instructions) return bytecode