diff --git a/doc/changelog.rst b/doc/changelog.rst index 369142bf..dfc43287 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -6,6 +6,7 @@ ChangeLog Bugfixes: +- Disallow creating an instruction targeting a pseudo/instrumented opcode PR #133 - Fixes encoding of 0 as a varint PR #132 - Correct spelling of "INTRINSIC" in several places; this affected some ops in Python 3.12. PR #131 diff --git a/src/bytecode/instr.py b/src/bytecode/instr.py index 95e1c43d..e927cdf4 100644 --- a/src/bytecode/instr.py +++ b/src/bytecode/instr.py @@ -14,7 +14,9 @@ import bytecode as _bytecode -# --- Instruction argument tools and abstractions +# --- Instruction argument tools and + +MIN_INSTRUMENTED_OPCODE = getattr(_opcode, "MIN_INSTRUMENTED_OPCODE", 256) # Instructions relying on a bit to modify its behavior. # The lowest bit is used to encode custom behavior. @@ -734,6 +736,12 @@ def _set(self, name: str, arg: A) -> None: except KeyError: raise ValueError(f"invalid operation name: {name}") + if opcode >= MIN_INSTRUMENTED_OPCODE: + raise ValueError( + f"operation {name} is an instrumented or pseudo opcode. " + "Only base opcodes are supported" + ) + self._check_arg(name, opcode, arg) self._name = name diff --git a/tests/test_instr.py b/tests/test_instr.py index 4952ab2a..1437b87a 100644 --- a/tests/test_instr.py +++ b/tests/test_instr.py @@ -138,6 +138,12 @@ def test_repr(self): self.assertIn("arg", r) self.assertIn("_x_", r) + def test_reject_pseudo_opcode(self): + if sys.version_info >= (3, 12): + with self.assertRaises(ValueError) as e: + Instr("LOAD_METHOD", "x") + self.assertIn("is an instrumented or pseudo opcode", str(e.exception)) + def test_invalid_arg(self): label = Label() block = BasicBlock()