Skip to content

Commit ae7302b

Browse files
authored
Merge branch 'main' into feat/python-315
2 parents b7dae58 + b67adbf commit ae7302b

3 files changed

Lines changed: 86 additions & 26 deletions

File tree

src/bytecode/bytecode.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -164,12 +164,6 @@ def legalize(self) -> None:
164164
for i in reversed(lineno_pos):
165165
del self[i]
166166

167-
def __iter__(self) -> Iterator[U]:
168-
instructions = super().__iter__()
169-
for instr in instructions:
170-
self._check_instr(instr)
171-
yield instr
172-
173167
def _check_instr(self, instr):
174168
raise NotImplementedError()
175169

src/bytecode/concrete.py

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,24 @@ def assemble(self) -> bytes:
181181

182182
return bytes(b)
183183

184+
@classmethod
185+
def _from_opcode(
186+
cls: Type[T],
187+
name: str,
188+
opcode: int,
189+
arg: int,
190+
location: Optional[InstrLocation],
191+
) -> T:
192+
"""Fast path for from_code: arg is a raw byte (0-255), size is always 2."""
193+
new = object.__new__(cls)
194+
new._name = name
195+
new._opcode = opcode
196+
new._arg = arg
197+
new._location = location
198+
new._extended_args = None
199+
new._size = 2
200+
return new
201+
184202
@classmethod
185203
def disassemble(cls: Type[T], lineno: Optional[int], code: bytes, offset: int) -> T:
186204
index = 2 * offset
@@ -336,21 +354,24 @@ def from_code(
336354
code: types.CodeType, *, extended_arg: bool = False
337355
) -> ConcreteBytecode:
338356
instructions: MutableSequence[Union[SetLineno, ConcreteInstr]] = []
339-
for i in dis.get_instructions(code, show_caches=True):
340-
loc = InstrLocation.from_positions(i.positions) if i.positions else None
341-
# dis.get_instructions automatically handle extended arg which
342-
# we do not want, so we fold back arguments to be between 0 and 255
343-
instructions.append(
344-
ConcreteInstr(
345-
i.opname,
346-
i.arg % 256 if i.arg is not None else UNSET,
347-
location=loc,
348-
)
357+
bc = code.co_code
358+
opname = _opcode.opname
359+
# co_positions() yields one (lineno, end_lineno, col_offset,
360+
# end_col_offset) per instruction word (including CACHE entries),
361+
# available from Python 3.11+. CACHE entries are already inline in
362+
# co_code on all supported versions, so iterating co_code directly
363+
# handles all versions without dis overhead.
364+
pos_iter: Iterator[
365+
Tuple[Optional[int], Optional[int], Optional[int], Optional[int]]
366+
] = iter(code.co_positions())
367+
for offset in range(0, len(bc), 2):
368+
op = bc[offset]
369+
arg = bc[offset + 1] if opcode_has_argument(op) else UNSET
370+
pos = next(pos_iter, None)
371+
loc: Optional[InstrLocation] = (
372+
InstrLocation._from_tuple(*pos) if pos is not None else None
349373
)
350-
# cache_info only exist on 3.13+
351-
for _, size, _ in (i.cache_info or ()) if PY313 else (): # type: ignore
352-
for _ in range(size):
353-
instructions.append(ConcreteInstr("CACHE", 0, location=loc))
374+
instructions.append(ConcreteInstr._from_opcode(opname[op], op, arg, loc))
354375

355376
bytecode = ConcreteBytecode()
356377

@@ -575,7 +596,9 @@ def _assemble_locations(
575596

576597
_, size, lineno, old_location = next(iter_in)
577598
# Infer the line if location is None
578-
old_location = old_location or InstrLocation(lineno, None, None, None)
599+
old_location = old_location or InstrLocation._from_tuple(
600+
lineno, None, None, None
601+
)
579602
lineno = first_lineno
580603

581604
# We track the last set lineno to be able to compute deltas
@@ -930,14 +953,18 @@ def to_bytecode(
930953
else:
931954
arg = c_arg
932955

933-
location = c_instr.location or InstrLocation(lineno, None, None, None)
956+
location = c_instr.location or InstrLocation._from_tuple(
957+
lineno, None, None, None
958+
)
934959

935960
if jump_target is not None:
936961
arg = PLACEHOLDER_LABEL
937962
instr_index = len(instructions)
938963
jumps.append((instr_index, jump_target))
939964

940-
instructions.append(Instr(c_instr.name, arg, location=location))
965+
instructions.append(
966+
Instr._from_trusted(c_instr._name, c_instr._opcode, arg, location)
967+
)
941968

942969
# We now insert the TryEnd entries
943970
if current_instr_offset in ex_end:
@@ -1030,7 +1057,9 @@ def add(names: list[str], name: str) -> int:
10301057
return index
10311058

10321059
def concrete_instructions(self) -> None:
1033-
location = InstrLocation(self.bytecode.first_lineno, None, None, None)
1060+
location = InstrLocation._from_tuple(
1061+
self.bytecode.first_lineno, None, None, None
1062+
)
10341063
# Track instruction (index) using cell vars and free vars to be able to update
10351064
# the index used once all the names are known.
10361065
cell_instrs: list[int] = []
@@ -1086,7 +1115,7 @@ def concrete_instructions(self) -> None:
10861115
continue
10871116

10881117
if isinstance(instr, SetLineno):
1089-
location = InstrLocation(instr.lineno, None, None, None)
1118+
location = InstrLocation._from_tuple(instr.lineno, None, None, None)
10901119
continue
10911120

10921121
if isinstance(instr, TryBegin):

src/bytecode/instr.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,22 @@ def from_positions(cls, position: dis.Positions) -> InstrLocation: # type: igno
634634
position.end_col_offset,
635635
)
636636

637+
@classmethod
638+
def _from_tuple(
639+
cls,
640+
lineno: Optional[int],
641+
end_lineno: Optional[int],
642+
col_offset: Optional[int],
643+
end_col_offset: Optional[int],
644+
) -> InstrLocation:
645+
"""Fast path for trusted position data (e.g. from co_positions())."""
646+
new = object.__new__(cls)
647+
object.__setattr__(new, "lineno", lineno)
648+
object.__setattr__(new, "end_lineno", end_lineno)
649+
object.__setattr__(new, "col_offset", col_offset)
650+
object.__setattr__(new, "end_col_offset", end_col_offset)
651+
return new
652+
637653

638654
class SetLineno:
639655
__slots__ = ("_lineno",)
@@ -825,7 +841,28 @@ def pre_and_post_stack_effect(self, jump: Optional[bool] = None) -> tuple[int, i
825841
return (_effect, 0)
826842

827843
def copy(self: T) -> T:
828-
return self.__class__(self._name, self._arg, location=self._location)
844+
new = object.__new__(self.__class__)
845+
new._name = self._name
846+
new._opcode = self._opcode
847+
new._arg = self._arg
848+
new._location = self._location
849+
return new
850+
851+
@classmethod
852+
def _from_trusted(
853+
cls: type[T],
854+
name: str,
855+
opcode: int,
856+
arg: A,
857+
location: Optional[InstrLocation],
858+
) -> T:
859+
"""Fast path for internal construction from already-validated data."""
860+
new = object.__new__(cls)
861+
new._name = name
862+
new._opcode = opcode
863+
new._arg = arg
864+
new._location = location
865+
return new
829866

830867
def has_jump(self) -> bool:
831868
return self._has_jump(self._opcode)

0 commit comments

Comments
 (0)