diff --git a/.github/workflows/cis.yml b/.github/workflows/cis.yml index 09e2da8bba2904a7c576594c28d746a59a4725ef..f1e192215907bdf4911947b0b960011fd5ba3527 100644 --- a/.github/workflows/cis.yml +++ b/.github/workflows/cis.yml @@ -21,7 +21,7 @@ jobs: name: Lint code runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v4 with: @@ -54,7 +54,7 @@ jobs: - python-version: "3.12-dev" toxenv: py312 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Get history and tags for SCM versioning to work run: | git fetch --prune --unshallow diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index cff1aa9514f98de8a31094f43717834d164b15b2..a8ab0f389fd199b6d563b311ac27fcc74f0d81f2 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -20,7 +20,7 @@ jobs: name: Docs building runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Get history and tags for SCM versioning to work run: | git fetch --prune --unshallow diff --git a/.github/workflows/frameworks.yml b/.github/workflows/frameworks.yml index b6e4a39986647044d88d8dfbe1056f0f731f8d02..79b4be83ad926d85b076a639a389bd160463625d 100644 --- a/.github/workflows/frameworks.yml +++ b/.github/workflows/frameworks.yml @@ -25,7 +25,7 @@ jobs: python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f25f83a27e9b09a99ff9b893e90874524ec7c831..aa4419870791ac4516595a8eb4b98b2505867b07 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Get history and tags for SCM versioning to work run: | git fetch --prune --unshallow @@ -43,7 +43,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Get history and tags for SCM versioning to work run: | git fetch --prune --unshallow diff --git a/doc/api.rst b/doc/api.rst index cf91ba821630f9c66dc00460c76824262521483c..db54cae840db61e9080e6d90967b6573bc223c4a 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -351,11 +351,11 @@ Intrinsic operations .. class:: Intrinsic1Op - Enum for the argument of the ``CALL_INSTRINSIC_1`` instruction (3.12+). + Enum for the argument of the ``CALL_INTRINSIC_1`` instruction (3.12+). - ``INSTRINSIC_1_INVALID`` - ``INSTRINSIC_PRINT`` - ``INSTRINSIC_IMPORT_STAR`` + ``INTRINSIC_1_INVALID`` + ``INTRINSIC_PRINT`` + ``INTRINSIC_IMPORT_STAR`` ``INTRINSIC_STOPITERATION_ERROR`` ``INTRINSIC_ASYNC_GEN_WRAP`` ``INTRINSIC_UNARY_POSITIVE`` @@ -368,9 +368,9 @@ Intrinsic operations .. class:: Intrinsic2Op - Enum for the argument of the ``CALL_INSTRINSIC_2`` instruction (3.12+). + Enum for the argument of the ``CALL_INTRINSIC_2`` instruction (3.12+). - ``INSTRINSIC_2_INVALID`` + ``INTRINSIC_2_INVALID`` ``INTRINSIC_PREP_RERAISE_STAR`` ``INTRINSIC_TYPEVAR_WITH_BOUND`` ``INTRINSIC_TYPEVAR_WITH_CONSTRAINTS`` diff --git a/doc/changelog.rst b/doc/changelog.rst index 01a1950ba0d96716e1c5886fd8a8d6c324d6e13c..dfc432873d2c69448fb81e0ea94f61eaf4cc5cfd 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -1,6 +1,16 @@ ChangeLog ========= +2023-10-13: Version 0.15.1 +-------------------------- + +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 + 2023-09-01: Version 0.15.0 -------------------------- @@ -17,7 +27,7 @@ New features: a ``tuple[bool, bool, str]`` as argument - ``POP_JUMP_IF_*`` instructions are undirected in Python 3.12 - ``YIELD_VALUE`` now takes an argument - - Support for ``CALL_INSTRINSIC_1/2`` led to the addition of 2 new enums to + - Support for ``CALL_INTRINSIC_1/2`` led to the addition of 2 new enums to represent the argument 2023-05-24: Version 0.14.2 diff --git a/src/bytecode/concrete.py b/src/bytecode/concrete.py index 4a65a5e5012dc90296e6b4f5adf329b94e4948ae..4908e1c57591203e24dc2e76f0fd7bc2fe56ed2d 100644 --- a/src/bytecode/concrete.py +++ b/src/bytecode/concrete.py @@ -818,9 +818,10 @@ class ConcreteBytecode(_bytecode._BaseBytecodeList[Union[ConcreteInstr, SetLinen while value: temp.append(value & 63 | (64 if temp else 0)) value >>= 6 + temp = temp or [0] if set_begin_marker: temp[-1] |= 128 - return reversed(temp or [0]) + return reversed(temp) def _assemble_exception_table(self) -> bytes: table = bytearray() diff --git a/src/bytecode/instr.py b/src/bytecode/instr.py index f65e5aff8917d25dc1504113cceca03c345c84fe..e927cdf4cce035d823f846661fb529a0485ae2dd 100644 --- a/src/bytecode/instr.py +++ b/src/bytecode/instr.py @@ -14,7 +14,9 @@ except ImportError: 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. @@ -104,9 +106,9 @@ class BinaryOp(enum.IntEnum): @enum.unique class Intrinsic1Op(enum.IntEnum): - INSTRINSIC_1_INVALID = 0 - INSTRINSIC_PRINT = 1 - INSTRINSIC_IMPORT_STAR = 2 + INTRINSIC_1_INVALID = 0 + INTRINSIC_PRINT = 1 + INTRINSIC_IMPORT_STAR = 2 INTRINSIC_STOPITERATION_ERROR = 3 INTRINSIC_ASYNC_GEN_WRAP = 4 INTRINSIC_UNARY_POSITIVE = 5 @@ -120,7 +122,7 @@ class Intrinsic1Op(enum.IntEnum): @enum.unique class Intrinsic2Op(enum.IntEnum): - INSTRINSIC_2_INVALID = 0 + INTRINSIC_2_INVALID = 0 INTRINSIC_PREP_RERAISE_STAR = 1 INTRINSIC_TYPEVAR_WITH_BOUND = 2 INTRINSIC_TYPEVAR_WITH_CONSTRAINTS = 3 @@ -734,6 +736,12 @@ class BaseInstr(Generic[A]): 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_concrete.py b/tests/test_concrete.py index 32c179a5cb837ce6c5b2a06902a57eb90abceb28..3b75ba9ad43dba635b30298867ed7c5741402811 100644 --- a/tests/test_concrete.py +++ b/tests/test_concrete.py @@ -529,7 +529,6 @@ class ConcreteBytecodeTests(TestCase): ], ) - # XXX adjust test for 3.12 in which load_classderef does not exist anymore def test_load_classderef(self): i_name = ( "LOAD_FROM_DICT_OR_DEREF" @@ -682,6 +681,11 @@ class ConcreteBytecodeTests(TestCase): ) self.assertInstructionListEqual(concrete, concrete.copy()) + def test_encode_varint(self): + self.assertListEqual(list(ConcreteBytecode._encode_varint(0)), [0]) + self.assertListEqual(list(ConcreteBytecode._encode_varint(0, True)), [128]) + self.assertListEqual(list(ConcreteBytecode._encode_varint(64, False)), [65, 0]) + class ConcreteFromCodeTests(TestCase): def test_extended_arg(self): diff --git a/tests/test_instr.py b/tests/test_instr.py index d36c31532d304b91c21fdd57fc235f94e8f8baf5..1437b87af6594b7efd75b8e3efa0601458aa296d 100644 --- a/tests/test_instr.py +++ b/tests/test_instr.py @@ -138,6 +138,12 @@ class InstrTests(TestCase): 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() @@ -208,7 +214,7 @@ class InstrTests(TestCase): for name in [opcode.opname[i] for i in INTRINSIC_1OP]: self.assertRaises(TypeError, Instr, name, 1) - Instr(name, Intrinsic1Op.INSTRINSIC_PRINT) + Instr(name, Intrinsic1Op.INTRINSIC_PRINT) for name in [opcode.opname[i] for i in INTRINSIC_2OP]: self.assertRaises(TypeError, Instr, name, 1)