Skip to content

Commit 6364ee7

Browse files
committed
Produce cleaner bytecode for 'with' and 'async with' by generating separate code for normal and exceptional paths.
Remove BEGIN_FINALLY, END_FINALLY, CALL_FINALLY and POP_FINALLY bytecodes. Implement finally blocks by code duplication. Reimplement frame.lineno setter using line numbers rather than bytecode offsets.
1 parent 733b9a3 commit 6364ee7

15 files changed

+4779
-4744
lines changed

Doc/library/dis.rst

+9-76
Original file line numberDiff line numberDiff line change
@@ -706,50 +706,21 @@ iterations of the loop.
706706
popped values are used to restore the exception state.
707707

708708

709-
.. opcode:: POP_FINALLY (preserve_tos)
709+
.. opcode:: RERAISE
710710

711-
Cleans up the value stack and the block stack. If *preserve_tos* is not
712-
``0`` TOS first is popped from the stack and pushed on the stack after
713-
performing other stack operations:
711+
Re-raises the exception currently on top of the stack.
714712

715-
* If TOS is ``NULL`` or an integer (pushed by :opcode:`BEGIN_FINALLY`
716-
or :opcode:`CALL_FINALLY`) it is popped from the stack.
717-
* If TOS is an exception type (pushed when an exception has been raised)
718-
6 values are popped from the stack, the last three popped values are
719-
used to restore the exception state. An exception handler block is
720-
removed from the block stack.
713+
.. versionadded:: 3.8
721714

722-
It is similar to :opcode:`END_FINALLY`, but doesn't change the bytecode
723-
counter nor raise an exception. Used for implementing :keyword:`break`,
724-
:keyword:`continue` and :keyword:`return` in the :keyword:`finally` block.
725715

726-
.. versionadded:: 3.8
727-
728-
729-
.. opcode:: BEGIN_FINALLY
730-
731-
Pushes ``NULL`` onto the stack for using it in :opcode:`END_FINALLY`,
732-
:opcode:`POP_FINALLY`, :opcode:`WITH_CLEANUP_START` and
733-
:opcode:`WITH_CLEANUP_FINISH`. Starts the :keyword:`finally` block.
734-
735-
.. versionadded:: 3.8
716+
.. opcode:: WITH_EXCEPT_START
736717

718+
Calls the function in position 7 on the stack with the top three
719+
items on the stack as arguments.
720+
Used to implement the call ``context_manager.__exit__(*exc_info())`` when an exception
721+
has occurred in a :keyword:`with` statement.
737722

738-
.. opcode:: END_FINALLY
739-
740-
Terminates a :keyword:`finally` clause. The interpreter recalls whether the
741-
exception has to be re-raised or execution has to be continued depending on
742-
the value of TOS.
743-
744-
* If TOS is ``NULL`` (pushed by :opcode:`BEGIN_FINALLY`) continue from
745-
the next instruction. TOS is popped.
746-
* If TOS is an integer (pushed by :opcode:`CALL_FINALLY`), sets the
747-
bytecode counter to TOS. TOS is popped.
748-
* If TOS is an exception type (pushed when an exception has been raised)
749-
6 values are popped from the stack, the first three popped values are
750-
used to re-raise the exception and the last three popped values are used
751-
to restore the exception state. An exception handler block is removed
752-
from the block stack.
723+
.. versionadded:: 3.8
753724

754725

755726
.. opcode:: LOAD_ASSERTION_ERROR
@@ -780,35 +751,6 @@ iterations of the loop.
780751
.. versionadded:: 3.2
781752

782753

783-
.. opcode:: WITH_CLEANUP_START
784-
785-
Starts cleaning up the stack when a :keyword:`with` statement block exits.
786-
787-
At the top of the stack are either ``NULL`` (pushed by
788-
:opcode:`BEGIN_FINALLY`) or 6 values pushed if an exception has been
789-
raised in the with block. Below is the context manager's
790-
:meth:`~object.__exit__` or :meth:`~object.__aexit__` bound method.
791-
792-
If TOS is ``NULL``, calls ``SECOND(None, None, None)``,
793-
removes the function from the stack, leaving TOS, and pushes ``None``
794-
to the stack. Otherwise calls ``SEVENTH(TOP, SECOND, THIRD)``,
795-
shifts the bottom 3 values of the stack down, replaces the empty spot
796-
with ``NULL`` and pushes TOS. Finally pushes the result of the call.
797-
798-
799-
.. opcode:: WITH_CLEANUP_FINISH
800-
801-
Finishes cleaning up the stack when a :keyword:`with` statement block exits.
802-
803-
TOS is result of ``__exit__()`` or ``__aexit__()`` function call pushed
804-
by :opcode:`WITH_CLEANUP_START`. SECOND is ``None`` or an exception type
805-
(pushed when an exception has been raised).
806-
807-
Pops two values from the stack. If SECOND is not None and TOS is true
808-
unwinds the EXCEPT_HANDLER block which was created when the exception
809-
was caught and pushes ``NULL`` to the stack.
810-
811-
812754
All of the following opcodes use their arguments.
813755

814756
.. opcode:: STORE_NAME (namei)
@@ -1060,15 +1002,6 @@ All of the following opcodes use their arguments.
10601002
stack. *delta* points to the finally block or the first except block.
10611003

10621004

1063-
.. opcode:: CALL_FINALLY (delta)
1064-
1065-
Pushes the address of the next instruction onto the stack and increments
1066-
bytecode counter by *delta*. Used for calling the finally block as a
1067-
"subroutine".
1068-
1069-
.. versionadded:: 3.8
1070-
1071-
10721005
.. opcode:: LOAD_FAST (var_num)
10731006

10741007
Pushes a reference to the local ``co_varnames[var_num]`` onto the stack.

Include/opcode.h

+2-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/importlib/_bootstrap_external.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,8 @@ def _write_atomic(path, data, mode=0o666):
272272
# only args in ast.arguments #37593)
273273
# Python 3.8b4 3413 (Fix "break" and "continue" in "finally" #37830)
274274
# Python 3.9a0 3420 (add LOAD_ASSERTION_ERROR #34880)
275+
# Python 3.9a0 3421 (simplified bytecode for with blocks #32949)
276+
# Python 3.9a0 3422 (remove BEGIN_FINALLY, END_FINALLY, CALL_FINALLY, POP_FINALLY bytecodes #33387)
275277
#
276278
# MAGIC must change whenever the bytecode emitted by the compiler may no
277279
# longer be understood by older implementations of the eval loop (usually
@@ -280,7 +282,7 @@ def _write_atomic(path, data, mode=0o666):
280282
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
281283
# in PC/launcher.c must also be updated.
282284

283-
MAGIC_NUMBER = (3420).to_bytes(2, 'little') + b'\r\n'
285+
MAGIC_NUMBER = (3422).to_bytes(2, 'little') + b'\r\n'
284286
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
285287

286288
_PYCACHE = '__pycache__'

Lib/opcode.py

+5-6
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,12 @@ def jabs_op(name, op):
8484
def_op('INPLACE_FLOOR_DIVIDE', 28)
8585
def_op('INPLACE_TRUE_DIVIDE', 29)
8686

87+
def_op('RERAISE', 48)
88+
def_op('WITH_EXCEPT_START', 49)
8789
def_op('GET_AITER', 50)
8890
def_op('GET_ANEXT', 51)
8991
def_op('BEFORE_ASYNC_WITH', 52)
90-
def_op('BEGIN_FINALLY', 53)
92+
9193
def_op('END_ASYNC_FOR', 54)
9294
def_op('INPLACE_ADD', 55)
9395
def_op('INPLACE_SUBTRACT', 56)
@@ -115,14 +117,13 @@ def jabs_op(name, op):
115117
def_op('INPLACE_AND', 77)
116118
def_op('INPLACE_XOR', 78)
117119
def_op('INPLACE_OR', 79)
118-
def_op('WITH_CLEANUP_START', 81)
119-
def_op('WITH_CLEANUP_FINISH', 82)
120+
120121
def_op('RETURN_VALUE', 83)
121122
def_op('IMPORT_STAR', 84)
122123
def_op('SETUP_ANNOTATIONS', 85)
123124
def_op('YIELD_VALUE', 86)
124125
def_op('POP_BLOCK', 87)
125-
def_op('END_FINALLY', 88)
126+
126127
def_op('POP_EXCEPT', 89)
127128

128129
HAVE_ARGUMENT = 90 # Opcodes from here have an argument:
@@ -210,7 +211,5 @@ def jabs_op(name, op):
210211

211212
name_op('LOAD_METHOD', 160)
212213
def_op('CALL_METHOD', 161)
213-
jrel_op('CALL_FINALLY', 162)
214-
def_op('POP_FINALLY', 163)
215214

216215
del def_op, name_op, jrel_op, jabs_op

0 commit comments

Comments
 (0)