diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index a24589fd0a5af3..618ff144b8080e 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -813,18 +813,23 @@ not have to be) the original ``STACK[-2]``. .. versionadded:: 3.5 -.. opcode:: END_ASYNC_FOR +.. opcode:: END_ASYNC_FOR (delta) Terminates an :keyword:`async for` loop. Handles an exception raised when awaiting a next item. The stack contains the async iterable in ``STACK[-2]`` and the raised exception in ``STACK[-1]``. Both are popped. If the exception is not :exc:`StopAsyncIteration`, it is re-raised. + Decrements the bytecode counter by *delta* to jump back to the loop start + if continuing iteration. + .. versionadded:: 3.8 .. versionchanged:: 3.11 Exception representation on the stack now consist of one, not three, items. + .. versionchanged:: 3.14 + Added the *delta* parameter. This opcode is now a backward jump instruction. .. opcode:: CLEANUP_THROW @@ -2081,3 +2086,16 @@ instructions: .. deprecated:: 3.13 All jumps are now relative. This list is empty. + +.. data:: hasjforward + + Sequence of bytecodes that perform forward jumps. + + .. versionadded:: 3.14 + +.. data:: hasjback + + Sequence of bytecodes that perform backward jumps, such as + :opcode:`JUMP_BACKWARD`, :opcode:`FOR_ITER`, and :opcode:`END_ASYNC_FOR`. + + .. versionadded:: 3.14 diff --git a/Lib/opcode.py b/Lib/opcode.py index 0e9520b6832499..eee04f712ce83b 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -7,7 +7,7 @@ __all__ = ["cmp_op", "stack_effect", "hascompare", "opname", "opmap", "HAVE_ARGUMENT", "EXTENDED_ARG", "hasarg", "hasconst", "hasname", - "hasjump", "hasjrel", "hasjabs", "hasfree", "haslocal", "hasexc"] + "hasjump", "hasjrel", "hasjabs", "hasjforward", "hasjback", "hasfree", "haslocal", "hasexc"] import builtins import _opcode @@ -17,6 +17,18 @@ HAVE_ARGUMENT, MIN_INSTRUMENTED_OPCODE) # noqa: F401 EXTENDED_ARG = opmap['EXTENDED_ARG'] +def _is_backward_jump_op(op): + """Helper function to identify backward jump opcodes.""" + # Get the opcode name from the op number + op_name = opname[op] if op < len(opname) else None + return op_name in ( + 'JUMP_BACKWARD', + 'JUMP_BACKWARD_NO_INTERRUPT', + 'FOR_ITER', + 'END_ASYNC_FOR', + ) + + opname = ['<%r>' % (op,) for op in range(max(opmap.values()) + 1)] for m in (opmap, _specialized_opmap): for op, i in m.items(): @@ -35,6 +47,11 @@ haslocal = [op for op in opmap.values() if _opcode.has_local(op)] hasexc = [op for op in opmap.values() if _opcode.has_exc(op)] +hasjforward = [op for op in hasjump if not _is_backward_jump_op(op)] +hasjback = [op for op in hasjump if _is_backward_jump_op(op)] + + + _intrinsic_1_descs = _opcode.get_intrinsic1_descs() _intrinsic_2_descs = _opcode.get_intrinsic2_descs() diff --git a/Misc/NEWS.d/next/Library/2024-12-05-12-00-00.gh-issue-142250.xyz123.rst b/Misc/NEWS.d/next/Library/2024-12-05-12-00-00.gh-issue-142250.xyz123.rst new file mode 100644 index 00000000000000..e524ede6772b89 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-05-12-00-00.gh-issue-142250.xyz123.rst @@ -0,0 +1,4 @@ +Document the ``delta`` parameter for the :opcode:`!END_ASYNC_FOR` opcode in +Python 3.14. Add :data:`~dis.hasjforward` and :data:`~dis.hasjback` opcode +collections to distinguish between forward and backward jump instructions. +Patch by Khanak.