diff --git a/src/bytecode/concrete.py b/src/bytecode/concrete.py index 6a21d186..f00a8f10 100644 --- a/src/bytecode/concrete.py +++ b/src/bytecode/concrete.py @@ -575,7 +575,9 @@ def _assemble_locations( _, size, lineno, old_location = next(iter_in) # Infer the line if location is None - old_location = old_location or InstrLocation(lineno, None, None, None) + old_location = old_location or InstrLocation._from_tuple( + lineno, None, None, None + ) lineno = first_lineno # We track the last set lineno to be able to compute deltas @@ -930,14 +932,18 @@ def to_bytecode( else: arg = c_arg - location = c_instr.location or InstrLocation(lineno, None, None, None) + location = c_instr.location or InstrLocation._from_tuple( + lineno, None, None, None + ) if jump_target is not None: arg = PLACEHOLDER_LABEL instr_index = len(instructions) jumps.append((instr_index, jump_target)) - instructions.append(Instr(c_instr.name, arg, location=location)) + instructions.append( + Instr._from_trusted(c_instr._name, c_instr._opcode, arg, location) + ) # We now insert the TryEnd entries if current_instr_offset in ex_end: @@ -1030,7 +1036,9 @@ def add(names: list[str], name: str) -> int: return index def concrete_instructions(self) -> None: - location = InstrLocation(self.bytecode.first_lineno, None, None, None) + location = InstrLocation._from_tuple( + self.bytecode.first_lineno, None, None, None + ) # Track instruction (index) using cell vars and free vars to be able to update # the index used once all the names are known. cell_instrs: list[int] = [] @@ -1086,7 +1094,7 @@ def concrete_instructions(self) -> None: continue if isinstance(instr, SetLineno): - location = InstrLocation(instr.lineno, None, None, None) + location = InstrLocation._from_tuple(instr.lineno, None, None, None) continue if isinstance(instr, TryBegin): diff --git a/src/bytecode/instr.py b/src/bytecode/instr.py index 9e043f3e..56240eac 100644 --- a/src/bytecode/instr.py +++ b/src/bytecode/instr.py @@ -621,6 +621,22 @@ def from_positions(cls, position: dis.Positions) -> InstrLocation: # type: igno position.end_col_offset, ) + @classmethod + def _from_tuple( + cls, + lineno: Optional[int], + end_lineno: Optional[int], + col_offset: Optional[int], + end_col_offset: Optional[int], + ) -> InstrLocation: + """Fast path for trusted position data (e.g. from co_positions()).""" + new = object.__new__(cls) + object.__setattr__(new, "lineno", lineno) + object.__setattr__(new, "end_lineno", end_lineno) + object.__setattr__(new, "col_offset", col_offset) + object.__setattr__(new, "end_col_offset", end_col_offset) + return new + class SetLineno: __slots__ = ("_lineno",) @@ -819,6 +835,22 @@ def copy(self: T) -> T: new._location = self._location return new + @classmethod + def _from_trusted( + cls: type[T], + name: str, + opcode: int, + arg: A, + location: Optional[InstrLocation], + ) -> T: + """Fast path for internal construction from already-validated data.""" + new = object.__new__(cls) + new._name = name + new._opcode = opcode + new._arg = arg + new._location = location + return new + def has_jump(self) -> bool: return self._has_jump(self._opcode)