Produce cleaner bytecode for 'with' and 'async with' by generating separate code for normal and exceptional paths. (#6641)
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.
This commit is contained in:
parent
5dcc06f6e0
commit
fee552669f
|
@ -706,52 +706,23 @@ iterations of the loop.
|
|||
popped values are used to restore the exception state.
|
||||
|
||||
|
||||
.. opcode:: POP_FINALLY (preserve_tos)
|
||||
.. opcode:: RERAISE
|
||||
|
||||
Cleans up the value stack and the block stack. If *preserve_tos* is not
|
||||
``0`` TOS first is popped from the stack and pushed on the stack after
|
||||
performing other stack operations:
|
||||
|
||||
* If TOS is ``NULL`` or an integer (pushed by :opcode:`BEGIN_FINALLY`
|
||||
or :opcode:`CALL_FINALLY`) it is popped from the stack.
|
||||
* If TOS is an exception type (pushed when an exception has been raised)
|
||||
6 values are popped from the stack, the last three popped values are
|
||||
used to restore the exception state. An exception handler block is
|
||||
removed from the block stack.
|
||||
|
||||
It is similar to :opcode:`END_FINALLY`, but doesn't change the bytecode
|
||||
counter nor raise an exception. Used for implementing :keyword:`break`,
|
||||
:keyword:`continue` and :keyword:`return` in the :keyword:`finally` block.
|
||||
Re-raises the exception currently on top of the stack.
|
||||
|
||||
.. versionadded:: 3.8
|
||||
|
||||
|
||||
.. opcode:: BEGIN_FINALLY
|
||||
.. opcode:: WITH_EXCEPT_START
|
||||
|
||||
Pushes ``NULL`` onto the stack for using it in :opcode:`END_FINALLY`,
|
||||
:opcode:`POP_FINALLY`, :opcode:`WITH_CLEANUP_START` and
|
||||
:opcode:`WITH_CLEANUP_FINISH`. Starts the :keyword:`finally` block.
|
||||
Calls the function in position 7 on the stack with the top three
|
||||
items on the stack as arguments.
|
||||
Used to implement the call ``context_manager.__exit__(*exc_info())`` when an exception
|
||||
has occurred in a :keyword:`with` statement.
|
||||
|
||||
.. versionadded:: 3.8
|
||||
|
||||
|
||||
.. opcode:: END_FINALLY
|
||||
|
||||
Terminates a :keyword:`finally` clause. The interpreter recalls whether the
|
||||
exception has to be re-raised or execution has to be continued depending on
|
||||
the value of TOS.
|
||||
|
||||
* If TOS is ``NULL`` (pushed by :opcode:`BEGIN_FINALLY`) continue from
|
||||
the next instruction. TOS is popped.
|
||||
* If TOS is an integer (pushed by :opcode:`CALL_FINALLY`), sets the
|
||||
bytecode counter to TOS. TOS is popped.
|
||||
* If TOS is an exception type (pushed when an exception has been raised)
|
||||
6 values are popped from the stack, the first three popped values are
|
||||
used to re-raise the exception and the last three popped values are used
|
||||
to restore the exception state. An exception handler block is removed
|
||||
from the block stack.
|
||||
|
||||
|
||||
.. opcode:: LOAD_ASSERTION_ERROR
|
||||
|
||||
Pushes :exc:`AssertionError` onto the stack. Used by the :keyword:`assert`
|
||||
|
@ -780,35 +751,6 @@ iterations of the loop.
|
|||
.. versionadded:: 3.2
|
||||
|
||||
|
||||
.. opcode:: WITH_CLEANUP_START
|
||||
|
||||
Starts cleaning up the stack when a :keyword:`with` statement block exits.
|
||||
|
||||
At the top of the stack are either ``NULL`` (pushed by
|
||||
:opcode:`BEGIN_FINALLY`) or 6 values pushed if an exception has been
|
||||
raised in the with block. Below is the context manager's
|
||||
:meth:`~object.__exit__` or :meth:`~object.__aexit__` bound method.
|
||||
|
||||
If TOS is ``NULL``, calls ``SECOND(None, None, None)``,
|
||||
removes the function from the stack, leaving TOS, and pushes ``None``
|
||||
to the stack. Otherwise calls ``SEVENTH(TOP, SECOND, THIRD)``,
|
||||
shifts the bottom 3 values of the stack down, replaces the empty spot
|
||||
with ``NULL`` and pushes TOS. Finally pushes the result of the call.
|
||||
|
||||
|
||||
.. opcode:: WITH_CLEANUP_FINISH
|
||||
|
||||
Finishes cleaning up the stack when a :keyword:`with` statement block exits.
|
||||
|
||||
TOS is result of ``__exit__()`` or ``__aexit__()`` function call pushed
|
||||
by :opcode:`WITH_CLEANUP_START`. SECOND is ``None`` or an exception type
|
||||
(pushed when an exception has been raised).
|
||||
|
||||
Pops two values from the stack. If SECOND is not None and TOS is true
|
||||
unwinds the EXCEPT_HANDLER block which was created when the exception
|
||||
was caught and pushes ``NULL`` to the stack.
|
||||
|
||||
|
||||
All of the following opcodes use their arguments.
|
||||
|
||||
.. opcode:: STORE_NAME (namei)
|
||||
|
@ -1060,15 +1002,6 @@ All of the following opcodes use their arguments.
|
|||
stack. *delta* points to the finally block or the first except block.
|
||||
|
||||
|
||||
.. opcode:: CALL_FINALLY (delta)
|
||||
|
||||
Pushes the address of the next instruction onto the stack and increments
|
||||
bytecode counter by *delta*. Used for calling the finally block as a
|
||||
"subroutine".
|
||||
|
||||
.. versionadded:: 3.8
|
||||
|
||||
|
||||
.. opcode:: LOAD_FAST (var_num)
|
||||
|
||||
Pushes a reference to the local ``co_varnames[var_num]`` onto the stack.
|
||||
|
|
|
@ -30,10 +30,11 @@ extern "C" {
|
|||
#define BINARY_TRUE_DIVIDE 27
|
||||
#define INPLACE_FLOOR_DIVIDE 28
|
||||
#define INPLACE_TRUE_DIVIDE 29
|
||||
#define RERAISE 48
|
||||
#define WITH_EXCEPT_START 49
|
||||
#define GET_AITER 50
|
||||
#define GET_ANEXT 51
|
||||
#define BEFORE_ASYNC_WITH 52
|
||||
#define BEGIN_FINALLY 53
|
||||
#define END_ASYNC_FOR 54
|
||||
#define INPLACE_ADD 55
|
||||
#define INPLACE_SUBTRACT 56
|
||||
|
@ -59,14 +60,11 @@ extern "C" {
|
|||
#define INPLACE_AND 77
|
||||
#define INPLACE_XOR 78
|
||||
#define INPLACE_OR 79
|
||||
#define WITH_CLEANUP_START 81
|
||||
#define WITH_CLEANUP_FINISH 82
|
||||
#define RETURN_VALUE 83
|
||||
#define IMPORT_STAR 84
|
||||
#define SETUP_ANNOTATIONS 85
|
||||
#define YIELD_VALUE 86
|
||||
#define POP_BLOCK 87
|
||||
#define END_FINALLY 88
|
||||
#define POP_EXCEPT 89
|
||||
#define HAVE_ARGUMENT 90
|
||||
#define STORE_NAME 90
|
||||
|
@ -127,8 +125,6 @@ extern "C" {
|
|||
#define BUILD_TUPLE_UNPACK_WITH_CALL 158
|
||||
#define LOAD_METHOD 160
|
||||
#define CALL_METHOD 161
|
||||
#define CALL_FINALLY 162
|
||||
#define POP_FINALLY 163
|
||||
|
||||
/* EXCEPT_HANDLER is a special, implicit block type which is created when
|
||||
entering an except handler. It is not an opcode but we define it here
|
||||
|
|
|
@ -272,6 +272,8 @@ _code_type = type(_write_atomic.__code__)
|
|||
# only args in ast.arguments #37593)
|
||||
# Python 3.8b4 3413 (Fix "break" and "continue" in "finally" #37830)
|
||||
# Python 3.9a0 3420 (add LOAD_ASSERTION_ERROR #34880)
|
||||
# Python 3.9a0 3421 (simplified bytecode for with blocks #32949)
|
||||
# Python 3.9a0 3422 (remove BEGIN_FINALLY, END_FINALLY, CALL_FINALLY, POP_FINALLY bytecodes #33387)
|
||||
#
|
||||
# MAGIC must change whenever the bytecode emitted by the compiler may no
|
||||
# longer be understood by older implementations of the eval loop (usually
|
||||
|
@ -280,7 +282,7 @@ _code_type = type(_write_atomic.__code__)
|
|||
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
|
||||
# in PC/launcher.c must also be updated.
|
||||
|
||||
MAGIC_NUMBER = (3420).to_bytes(2, 'little') + b'\r\n'
|
||||
MAGIC_NUMBER = (3422).to_bytes(2, 'little') + b'\r\n'
|
||||
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
|
||||
|
||||
_PYCACHE = '__pycache__'
|
||||
|
|
|
@ -84,10 +84,12 @@ def_op('BINARY_TRUE_DIVIDE', 27)
|
|||
def_op('INPLACE_FLOOR_DIVIDE', 28)
|
||||
def_op('INPLACE_TRUE_DIVIDE', 29)
|
||||
|
||||
def_op('RERAISE', 48)
|
||||
def_op('WITH_EXCEPT_START', 49)
|
||||
def_op('GET_AITER', 50)
|
||||
def_op('GET_ANEXT', 51)
|
||||
def_op('BEFORE_ASYNC_WITH', 52)
|
||||
def_op('BEGIN_FINALLY', 53)
|
||||
|
||||
def_op('END_ASYNC_FOR', 54)
|
||||
def_op('INPLACE_ADD', 55)
|
||||
def_op('INPLACE_SUBTRACT', 56)
|
||||
|
@ -115,14 +117,13 @@ def_op('INPLACE_RSHIFT', 76)
|
|||
def_op('INPLACE_AND', 77)
|
||||
def_op('INPLACE_XOR', 78)
|
||||
def_op('INPLACE_OR', 79)
|
||||
def_op('WITH_CLEANUP_START', 81)
|
||||
def_op('WITH_CLEANUP_FINISH', 82)
|
||||
|
||||
def_op('RETURN_VALUE', 83)
|
||||
def_op('IMPORT_STAR', 84)
|
||||
def_op('SETUP_ANNOTATIONS', 85)
|
||||
def_op('YIELD_VALUE', 86)
|
||||
def_op('POP_BLOCK', 87)
|
||||
def_op('END_FINALLY', 88)
|
||||
|
||||
def_op('POP_EXCEPT', 89)
|
||||
|
||||
HAVE_ARGUMENT = 90 # Opcodes from here have an argument:
|
||||
|
@ -210,7 +211,5 @@ def_op('BUILD_TUPLE_UNPACK_WITH_CALL', 158)
|
|||
|
||||
name_op('LOAD_METHOD', 160)
|
||||
def_op('CALL_METHOD', 161)
|
||||
jrel_op('CALL_FINALLY', 162)
|
||||
def_op('POP_FINALLY', 163)
|
||||
|
||||
del def_op, name_op, jrel_op, jabs_op
|
||||
|
|
|
@ -278,32 +278,34 @@ dis_traceback = """\
|
|||
--> 6 BINARY_TRUE_DIVIDE
|
||||
8 POP_TOP
|
||||
10 POP_BLOCK
|
||||
12 JUMP_FORWARD 40 (to 54)
|
||||
12 JUMP_FORWARD 44 (to 58)
|
||||
|
||||
%3d >> 14 DUP_TOP
|
||||
16 LOAD_GLOBAL 0 (Exception)
|
||||
18 COMPARE_OP 10 (exception match)
|
||||
20 POP_JUMP_IF_FALSE 52
|
||||
20 POP_JUMP_IF_FALSE 56
|
||||
22 POP_TOP
|
||||
24 STORE_FAST 0 (e)
|
||||
26 POP_TOP
|
||||
28 SETUP_FINALLY 10 (to 40)
|
||||
28 SETUP_FINALLY 18 (to 48)
|
||||
|
||||
%3d 30 LOAD_FAST 0 (e)
|
||||
32 LOAD_ATTR 1 (__traceback__)
|
||||
34 STORE_FAST 1 (tb)
|
||||
36 POP_BLOCK
|
||||
38 BEGIN_FINALLY
|
||||
>> 40 LOAD_CONST 0 (None)
|
||||
38 POP_EXCEPT
|
||||
40 LOAD_CONST 0 (None)
|
||||
42 STORE_FAST 0 (e)
|
||||
44 DELETE_FAST 0 (e)
|
||||
46 END_FINALLY
|
||||
48 POP_EXCEPT
|
||||
50 JUMP_FORWARD 2 (to 54)
|
||||
>> 52 END_FINALLY
|
||||
46 JUMP_FORWARD 10 (to 58)
|
||||
>> 48 LOAD_CONST 0 (None)
|
||||
50 STORE_FAST 0 (e)
|
||||
52 DELETE_FAST 0 (e)
|
||||
54 RERAISE
|
||||
>> 56 RERAISE
|
||||
|
||||
%3d >> 54 LOAD_FAST 1 (tb)
|
||||
56 RETURN_VALUE
|
||||
%3d >> 58 LOAD_FAST 1 (tb)
|
||||
60 RETURN_VALUE
|
||||
""" % (TRACEBACK_CODE.co_firstlineno + 1,
|
||||
TRACEBACK_CODE.co_firstlineno + 2,
|
||||
TRACEBACK_CODE.co_firstlineno + 3,
|
||||
|
@ -752,7 +754,7 @@ Argument count: 0
|
|||
Positional-only arguments: 0
|
||||
Kw-only arguments: 0
|
||||
Number of locals: 2
|
||||
Stack size: 10
|
||||
Stack size: 9
|
||||
Flags: OPTIMIZED, NEWLOCALS, NOFREE, COROUTINE
|
||||
Constants:
|
||||
0: None
|
||||
|
@ -977,10 +979,10 @@ expected_opinfo_jumpy = [
|
|||
Instruction(opname='LOAD_CONST', opcode=100, arg=6, argval='Who let lolcatz into this test suite?', argrepr="'Who let lolcatz into this test suite?'", offset=96, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=98, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=100, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='SETUP_FINALLY', opcode=122, arg=70, argval=174, argrepr='to 174', offset=102, starts_line=20, is_jump_target=True),
|
||||
Instruction(opname='SETUP_FINALLY', opcode=122, arg=98, argval=202, argrepr='to 202', offset=102, starts_line=20, is_jump_target=True),
|
||||
Instruction(opname='SETUP_FINALLY', opcode=122, arg=12, argval=118, argrepr='to 118', offset=104, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval=1, argrepr='1', offset=106, starts_line=21, is_jump_target=False),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=8, argval=0, argrepr='0', offset=108, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=7, argval=0, argrepr='0', offset=108, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='BINARY_TRUE_DIVIDE', opcode=27, arg=None, argval=None, argrepr='', offset=110, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=112, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=114, starts_line=None, is_jump_target=False),
|
||||
|
@ -993,33 +995,47 @@ expected_opinfo_jumpy = [
|
|||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=128, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=130, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=132, starts_line=23, is_jump_target=False),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=9, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=134, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=8, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=134, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=136, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=138, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=140, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='JUMP_FORWARD', opcode=110, arg=26, argval=170, argrepr='to 170', offset=142, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='END_FINALLY', opcode=88, arg=None, argval=None, argrepr='', offset=144, starts_line=None, is_jump_target=True),
|
||||
Instruction(opname='JUMP_FORWARD', opcode=110, arg=46, argval=190, argrepr='to 190', offset=142, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='RERAISE', opcode=48, arg=None, argval=None, argrepr='', offset=144, starts_line=None, is_jump_target=True),
|
||||
Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=146, starts_line=25, is_jump_target=True),
|
||||
Instruction(opname='SETUP_WITH', opcode=143, arg=14, argval=164, argrepr='to 164', offset=148, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='SETUP_WITH', opcode=143, arg=24, argval=174, argrepr='to 174', offset=148, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='STORE_FAST', opcode=125, arg=1, argval='dodgy', argrepr='dodgy', offset=150, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=152, starts_line=26, is_jump_target=False),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval='Never reach this', argrepr="'Never reach this'", offset=154, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=9, argval='Never reach this', argrepr="'Never reach this'", offset=154, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=156, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=158, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=160, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='BEGIN_FINALLY', opcode=53, arg=None, argval=None, argrepr='', offset=162, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='WITH_CLEANUP_START', opcode=81, arg=None, argval=None, argrepr='', offset=164, starts_line=None, is_jump_target=True),
|
||||
Instruction(opname='WITH_CLEANUP_FINISH', opcode=82, arg=None, argval=None, argrepr='', offset=166, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='END_FINALLY', opcode=88, arg=None, argval=None, argrepr='', offset=168, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=170, starts_line=None, is_jump_target=True),
|
||||
Instruction(opname='BEGIN_FINALLY', opcode=53, arg=None, argval=None, argrepr='', offset=172, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=174, starts_line=28, is_jump_target=True),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=7, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=176, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=178, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=180, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='END_FINALLY', opcode=88, arg=None, argval=None, argrepr='', offset=182, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=184, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=186, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=162, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='DUP_TOP', opcode=4, arg=None, argval=None, argrepr='', offset=164, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='DUP_TOP', opcode=4, arg=None, argval=None, argrepr='', offset=166, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='CALL_FUNCTION', opcode=131, arg=3, argval=3, argrepr='', offset=168, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=170, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='JUMP_FORWARD', opcode=110, arg=16, argval=190, argrepr='to 190', offset=172, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='WITH_EXCEPT_START', opcode=49, arg=None, argval=None, argrepr='', offset=174, starts_line=None, is_jump_target=True),
|
||||
Instruction(opname='POP_JUMP_IF_TRUE', opcode=115, arg=180, argval=180, argrepr='', offset=176, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='RERAISE', opcode=48, arg=None, argval=None, argrepr='', offset=178, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=180, starts_line=None, is_jump_target=True),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=182, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=184, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=186, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=188, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=190, starts_line=None, is_jump_target=True),
|
||||
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=192, starts_line=28, is_jump_target=False),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=194, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=196, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=198, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='JUMP_FORWARD', opcode=110, arg=10, argval=212, argrepr='to 212', offset=200, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=202, starts_line=None, is_jump_target=True),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=204, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=206, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=208, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='RERAISE', opcode=48, arg=None, argval=None, argrepr='', offset=210, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=212, starts_line=None, is_jump_target=True),
|
||||
Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=214, starts_line=None, is_jump_target=False)
|
||||
]
|
||||
|
||||
# One last piece of inspect fodder to check the default line number handling
|
||||
|
@ -1032,6 +1048,10 @@ expected_opinfo_simple = [
|
|||
|
||||
class InstructionTests(BytecodeTestCase):
|
||||
|
||||
def __init__(self, *args):
|
||||
super().__init__(*args)
|
||||
self.maxDiff = None
|
||||
|
||||
def test_default_first_line(self):
|
||||
actual = dis.get_instructions(simple)
|
||||
self.assertEqual(list(actual), expected_opinfo_simple)
|
||||
|
@ -1063,6 +1083,7 @@ class InstructionTests(BytecodeTestCase):
|
|||
# get_instructions has its own tests above, so can rely on it to validate
|
||||
# the object oriented API
|
||||
class BytecodeTests(unittest.TestCase):
|
||||
|
||||
def test_instantiation(self):
|
||||
# Test with function, method, code string and code object
|
||||
for obj in [_f, _C(1).__init__, "a=1", _f.__code__]:
|
||||
|
|
|
@ -1431,7 +1431,7 @@ output.append(4)
|
|||
1 / 0
|
||||
|
||||
@jump_test(3, 2, [2], event='return', error=(ValueError,
|
||||
"can't jump from a yield statement"))
|
||||
"can't jump from a 'yield' statement"))
|
||||
def test_no_jump_from_yield(output):
|
||||
def gen():
|
||||
output.append(2)
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
Removed WITH_CLEANUP_START, WITH_CLEANUP_FINISH, BEGIN_FINALLY, END_FINALLY, CALL_FINALLY and POP_FINALLY bytecodes.
|
||||
Replaced with RERAISE and WITH_EXCEPT_FINISH bytecodes.
|
||||
The compiler now generates different code for exceptional and non-exceptional branches for 'with' and 'try-except'
|
||||
statements. For 'try-finally' statements the 'finally' block is replicated for each exit from the 'try' body.
|
||||
|
|
@ -66,6 +66,285 @@ get_arg(const _Py_CODEUNIT *codestr, Py_ssize_t i)
|
|||
return oparg;
|
||||
}
|
||||
|
||||
typedef struct _codetracker {
|
||||
unsigned char *code;
|
||||
Py_ssize_t code_len;
|
||||
unsigned char *lnotab;
|
||||
Py_ssize_t lnotab_len;
|
||||
int start_line;
|
||||
int offset;
|
||||
int line;
|
||||
int addr;
|
||||
int line_addr;
|
||||
} codetracker;
|
||||
|
||||
/* Reset the mutable parts of the tracker */
|
||||
static void
|
||||
reset(codetracker *tracker)
|
||||
{
|
||||
tracker->offset = 0;
|
||||
tracker->addr = 0;
|
||||
tracker->line_addr = 0;
|
||||
tracker->line = tracker->start_line;
|
||||
}
|
||||
|
||||
/* Initialise the tracker */
|
||||
static void
|
||||
init_codetracker(codetracker *tracker, PyCodeObject *code_obj)
|
||||
{
|
||||
PyBytes_AsStringAndSize(code_obj->co_code,
|
||||
(char **)&tracker->code, &tracker->code_len);
|
||||
PyBytes_AsStringAndSize(code_obj->co_lnotab,
|
||||
(char **)&tracker->lnotab, &tracker->lnotab_len);
|
||||
tracker->start_line = code_obj->co_firstlineno;
|
||||
reset(tracker);
|
||||
}
|
||||
|
||||
static void
|
||||
advance_tracker(codetracker *tracker)
|
||||
{
|
||||
tracker->addr += sizeof(_Py_CODEUNIT);
|
||||
if (tracker->offset >= tracker->lnotab_len) {
|
||||
return;
|
||||
}
|
||||
while (tracker->offset < tracker->lnotab_len &&
|
||||
tracker->addr >= tracker->line_addr + tracker->lnotab[tracker->offset]) {
|
||||
tracker->line_addr += tracker->lnotab[tracker->offset];
|
||||
tracker->line += (signed char)tracker->lnotab[tracker->offset+1];
|
||||
tracker->offset += 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
retreat_tracker(codetracker *tracker)
|
||||
{
|
||||
tracker->addr -= sizeof(_Py_CODEUNIT);
|
||||
while (tracker->addr < tracker->line_addr) {
|
||||
tracker->offset -= 2;
|
||||
tracker->line_addr -= tracker->lnotab[tracker->offset];
|
||||
tracker->line -= (signed char)tracker->lnotab[tracker->offset+1];
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
move_to_addr(codetracker *tracker, int addr)
|
||||
{
|
||||
while (addr > tracker->addr) {
|
||||
advance_tracker(tracker);
|
||||
if (tracker->addr >= tracker->code_len) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
while (addr < tracker->addr) {
|
||||
retreat_tracker(tracker);
|
||||
if (tracker->addr < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
first_line_not_before(codetracker *tracker, int line)
|
||||
{
|
||||
int result = INT_MAX;
|
||||
reset(tracker);
|
||||
while (tracker->addr < tracker->code_len) {
|
||||
if (tracker->line == line) {
|
||||
return line;
|
||||
}
|
||||
if (tracker->line > line && tracker->line < result) {
|
||||
result = tracker->line;
|
||||
}
|
||||
advance_tracker(tracker);
|
||||
}
|
||||
if (result == INT_MAX) {
|
||||
return -1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int
|
||||
move_to_nearest_start_of_line(codetracker *tracker, int line)
|
||||
{
|
||||
if (line > tracker->line) {
|
||||
while (line != tracker->line) {
|
||||
advance_tracker(tracker);
|
||||
if (tracker->addr >= tracker->code_len) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
while (line != tracker->line) {
|
||||
retreat_tracker(tracker);
|
||||
if (tracker->addr < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
while (tracker->addr > tracker->line_addr) {
|
||||
retreat_tracker(tracker);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct _blockitem
|
||||
{
|
||||
unsigned char kind;
|
||||
int end_addr;
|
||||
int start_line;
|
||||
} blockitem;
|
||||
|
||||
typedef struct _blockstack
|
||||
{
|
||||
blockitem stack[CO_MAXBLOCKS];
|
||||
int depth;
|
||||
} blockstack;
|
||||
|
||||
|
||||
static void
|
||||
init_blockstack(blockstack *blocks)
|
||||
{
|
||||
blocks->depth = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
push_block(blockstack *blocks, unsigned char kind,
|
||||
int end_addr, int start_line)
|
||||
{
|
||||
assert(blocks->depth < CO_MAXBLOCKS);
|
||||
blocks->stack[blocks->depth].kind = kind;
|
||||
blocks->stack[blocks->depth].end_addr = end_addr;
|
||||
blocks->stack[blocks->depth].start_line = start_line;
|
||||
blocks->depth++;
|
||||
}
|
||||
|
||||
static unsigned char
|
||||
pop_block(blockstack *blocks)
|
||||
{
|
||||
assert(blocks->depth > 0);
|
||||
blocks->depth--;
|
||||
return blocks->stack[blocks->depth].kind;
|
||||
}
|
||||
|
||||
static blockitem *
|
||||
top_block(blockstack *blocks)
|
||||
{
|
||||
assert(blocks->depth > 0);
|
||||
return &blocks->stack[blocks->depth-1];
|
||||
}
|
||||
|
||||
static inline int
|
||||
is_try_except(unsigned char op, int target_op)
|
||||
{
|
||||
return op == SETUP_FINALLY && (target_op == DUP_TOP || target_op == POP_TOP);
|
||||
}
|
||||
|
||||
static inline int
|
||||
is_async_for(unsigned char op, int target_op)
|
||||
{
|
||||
return op == SETUP_FINALLY && target_op == END_ASYNC_FOR;
|
||||
}
|
||||
|
||||
static inline int
|
||||
is_try_finally(unsigned char op, int target_op)
|
||||
{
|
||||
return op == SETUP_FINALLY && !is_try_except(op, target_op) && !is_async_for(op, target_op);
|
||||
}
|
||||
|
||||
/* Kind for finding except blocks in the jump to line code */
|
||||
#define TRY_EXCEPT 250
|
||||
|
||||
static int
|
||||
block_stack_for_line(codetracker *tracker, int line, blockstack *blocks)
|
||||
{
|
||||
if (line < tracker->start_line) {
|
||||
return -1;
|
||||
}
|
||||
init_blockstack(blocks);
|
||||
reset(tracker);
|
||||
while (tracker->addr < tracker->code_len) {
|
||||
if (tracker->line == line) {
|
||||
return 0;
|
||||
}
|
||||
if (blocks->depth > 0 && tracker->addr == top_block(blocks)->end_addr) {
|
||||
unsigned char kind = pop_block(blocks);
|
||||
assert(kind != SETUP_FINALLY);
|
||||
if (kind == TRY_EXCEPT) {
|
||||
push_block(blocks, POP_EXCEPT, -1, tracker->line);
|
||||
}
|
||||
if (kind == SETUP_WITH || kind == SETUP_ASYNC_WITH) {
|
||||
push_block(blocks, WITH_EXCEPT_START, -1, tracker->line);
|
||||
}
|
||||
}
|
||||
unsigned char op = tracker->code[tracker->addr];
|
||||
if (op == SETUP_FINALLY || op == SETUP_ASYNC_WITH || op == SETUP_WITH || op == FOR_ITER) {
|
||||
unsigned int oparg = get_arg((const _Py_CODEUNIT *)tracker->code,
|
||||
tracker->addr / sizeof(_Py_CODEUNIT));
|
||||
int target_addr = tracker->addr + oparg + sizeof(_Py_CODEUNIT);
|
||||
int target_op = tracker->code[target_addr];
|
||||
if (is_async_for(op, target_op)) {
|
||||
push_block(blocks, FOR_ITER, target_addr, tracker->line);
|
||||
}
|
||||
else if (op == FOR_ITER) {
|
||||
push_block(blocks, FOR_ITER, target_addr-sizeof(_Py_CODEUNIT), tracker->line);
|
||||
}
|
||||
else if (is_try_except(op, target_op)) {
|
||||
push_block(blocks, TRY_EXCEPT, target_addr-sizeof(_Py_CODEUNIT), tracker->line);
|
||||
}
|
||||
else if (is_try_finally(op, target_op)) {
|
||||
int addr = tracker->addr;
|
||||
// Skip over duplicate 'finally' blocks if line is after body.
|
||||
move_to_addr(tracker, target_addr);
|
||||
if (tracker->line > line) {
|
||||
// Target is in body, rewind to start.
|
||||
move_to_addr(tracker, addr);
|
||||
push_block(blocks, op, target_addr, tracker->line);
|
||||
}
|
||||
else {
|
||||
// Now in finally block.
|
||||
push_block(blocks, RERAISE, -1, tracker->line);
|
||||
}
|
||||
}
|
||||
else {
|
||||
push_block(blocks, op, target_addr, tracker->line);
|
||||
}
|
||||
}
|
||||
else if (op == RERAISE) {
|
||||
assert(blocks->depth > 0);
|
||||
unsigned char kind = top_block(blocks)->kind;
|
||||
if (kind == RERAISE || kind == WITH_EXCEPT_START || kind == POP_EXCEPT) {
|
||||
pop_block(blocks);
|
||||
}
|
||||
|
||||
}
|
||||
advance_tracker(tracker);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
frame_stack_pop(PyFrameObject *f)
|
||||
{
|
||||
PyObject *v = (*--f->f_stacktop);
|
||||
Py_DECREF(v);
|
||||
}
|
||||
|
||||
static void
|
||||
frame_block_unwind(PyFrameObject *f)
|
||||
{
|
||||
assert(f->f_iblock > 0);
|
||||
f->f_iblock--;
|
||||
PyTryBlock *b = &f->f_blockstack[f->f_iblock];
|
||||
int delta = (f->f_stacktop - f->f_valuestack) - b->b_level;
|
||||
while (delta > 0) {
|
||||
frame_stack_pop(f);
|
||||
delta--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Setter for f_lineno - you can set f_lineno from within a trace function in
|
||||
* order to jump to a given line of code, subject to some restrictions. Most
|
||||
|
@ -77,7 +356,8 @@ get_arg(const _Py_CODEUNIT *codestr, Py_ssize_t i)
|
|||
* o Lines with an 'except' statement on them can't be jumped to, because
|
||||
* they expect an exception to be on the top of the stack.
|
||||
* o Lines that live in a 'finally' block can't be jumped from or to, since
|
||||
* the END_FINALLY expects to clean up the stack after the 'try' block.
|
||||
* we cannot be sure which state the interpreter was in or would be in
|
||||
* during execution of the finally block.
|
||||
* o 'try', 'with' and 'async with' blocks can't be jumped into because
|
||||
* the blockstack needs to be set up before their code runs.
|
||||
* o 'for' and 'async for' loops can't be jumped into because the
|
||||
|
@ -89,22 +369,6 @@ get_arg(const _Py_CODEUNIT *codestr, Py_ssize_t i)
|
|||
static int
|
||||
frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignored))
|
||||
{
|
||||
int new_lineno = 0; /* The new value of f_lineno */
|
||||
long l_new_lineno;
|
||||
int overflow;
|
||||
int new_lasti = 0; /* The new value of f_lasti */
|
||||
unsigned char *code = NULL; /* The bytecode for the frame... */
|
||||
Py_ssize_t code_len = 0; /* ...and its length */
|
||||
unsigned char *lnotab = NULL; /* Iterating over co_lnotab */
|
||||
Py_ssize_t lnotab_len = 0; /* (ditto) */
|
||||
int offset = 0; /* (ditto) */
|
||||
int line = 0; /* (ditto) */
|
||||
int addr = 0; /* (ditto) */
|
||||
int delta_iblock = 0; /* Scanning the SETUPs and POPs */
|
||||
int delta = 0;
|
||||
int blockstack[CO_MAXBLOCKS]; /* Walking the 'finally' blocks */
|
||||
int blockstack_top = 0; /* (ditto) */
|
||||
|
||||
if (p_new_lineno == NULL) {
|
||||
PyErr_SetString(PyExc_AttributeError, "cannot delete attribute");
|
||||
return -1;
|
||||
|
@ -145,8 +409,19 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* Fail if the line comes before the start of the code block. */
|
||||
l_new_lineno = PyLong_AsLongAndOverflow(p_new_lineno, &overflow);
|
||||
|
||||
codetracker tracker;
|
||||
init_codetracker(&tracker, f->f_code);
|
||||
move_to_addr(&tracker, f->f_lasti);
|
||||
int current_line = tracker.line;
|
||||
assert(current_line >= 0);
|
||||
int new_lineno;
|
||||
|
||||
{
|
||||
/* Fail if the line falls outside the code block and
|
||||
select first line with actual code. */
|
||||
int overflow;
|
||||
long l_new_lineno = PyLong_AsLongAndOverflow(p_new_lineno, &overflow);
|
||||
if (overflow
|
||||
#if SIZEOF_LONG > SIZEOF_INT
|
||||
|| l_new_lineno > INT_MAX
|
||||
|
@ -165,169 +440,100 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore
|
|||
new_lineno);
|
||||
return -1;
|
||||
}
|
||||
else if (new_lineno == f->f_code->co_firstlineno) {
|
||||
new_lasti = 0;
|
||||
new_lineno = f->f_code->co_firstlineno;
|
||||
}
|
||||
else {
|
||||
/* Find the bytecode offset for the start of the given
|
||||
* line, or the first code-owning line after it. */
|
||||
char *tmp;
|
||||
PyBytes_AsStringAndSize(f->f_code->co_lnotab,
|
||||
&tmp, &lnotab_len);
|
||||
lnotab = (unsigned char *) tmp;
|
||||
addr = 0;
|
||||
line = f->f_code->co_firstlineno;
|
||||
new_lasti = -1;
|
||||
for (offset = 0; offset < lnotab_len; offset += 2) {
|
||||
addr += lnotab[offset];
|
||||
line += (signed char)lnotab[offset+1];
|
||||
if (line >= new_lineno) {
|
||||
new_lasti = addr;
|
||||
new_lineno = line;
|
||||
break;
|
||||
}
|
||||
|
||||
new_lineno = first_line_not_before(&tracker, new_lineno);
|
||||
if (new_lineno < 0) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"line %d comes after the current code block",
|
||||
(int)l_new_lineno);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we didn't reach the requested line, return an error. */
|
||||
if (new_lasti == -1) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"line %d comes after the current code block",
|
||||
new_lineno);
|
||||
if (tracker.code[f->f_lasti] == YIELD_VALUE || tracker.code[f->f_lasti] == YIELD_FROM) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"can't jump from a 'yield' statement");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* We're now ready to look at the bytecode. */
|
||||
PyBytes_AsStringAndSize(f->f_code->co_code, (char **)&code, &code_len);
|
||||
/* Find block stack for current line and target line. */
|
||||
blockstack current_stack, new_stack;
|
||||
block_stack_for_line(&tracker, new_lineno, &new_stack);
|
||||
block_stack_for_line(&tracker, current_line, ¤t_stack);
|
||||
|
||||
/* The trace function is called with a 'return' trace event after the
|
||||
* execution of a yield statement. */
|
||||
assert(f->f_lasti != -1);
|
||||
if (code[f->f_lasti] == YIELD_VALUE || code[f->f_lasti] == YIELD_FROM) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"can't jump from a yield statement");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* You can't jump onto a line with an 'except' statement on it -
|
||||
* they expect to have an exception on the top of the stack, which
|
||||
* won't be true if you jump to them. They always start with code
|
||||
* that either pops the exception using POP_TOP (plain 'except:'
|
||||
* lines do this) or duplicates the exception on the stack using
|
||||
* DUP_TOP (if there's an exception type specified). See compile.c,
|
||||
* 'com_try_except' for the full details. There aren't any other
|
||||
* cases (AFAIK) where a line's code can start with DUP_TOP or
|
||||
* POP_TOP, but if any ever appear, they'll be subject to the same
|
||||
* restriction (but with a different error message). */
|
||||
if (code[new_lasti] == DUP_TOP || code[new_lasti] == POP_TOP) {
|
||||
if (tracker.code[tracker.addr] == DUP_TOP || tracker.code[tracker.addr] == POP_TOP) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"can't jump to 'except' line as there's no exception");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* You can't jump into or out of a 'finally' block because the 'try'
|
||||
* block leaves something on the stack for the END_FINALLY to clean up.
|
||||
* So we walk the bytecode, maintaining a simulated blockstack.
|
||||
* 'blockstack' is a stack of the bytecode addresses of the starts of
|
||||
* the 'finally' blocks. */
|
||||
memset(blockstack, '\0', sizeof(blockstack));
|
||||
blockstack_top = 0;
|
||||
unsigned char prevop = NOP;
|
||||
for (addr = 0; addr < code_len; addr += sizeof(_Py_CODEUNIT)) {
|
||||
unsigned char op = code[addr];
|
||||
switch (op) {
|
||||
/* Validate change of block stack. */
|
||||
if (new_stack.depth > 0) {
|
||||
blockitem *current_block_at_new_depth = &(current_stack.stack[new_stack.depth-1]);
|
||||
if (new_stack.depth > current_stack.depth ||
|
||||
top_block(&new_stack)->start_line != current_block_at_new_depth->start_line) {
|
||||
unsigned char target_kind = top_block(&new_stack)->kind;
|
||||
char *msg;
|
||||
if (target_kind == POP_EXCEPT) {
|
||||
msg = "can't jump into an 'except' block as there's no exception";
|
||||
}
|
||||
else if (target_kind == RERAISE) {
|
||||
msg = "can't jump into a 'finally' block";
|
||||
}
|
||||
else {
|
||||
msg = "can't jump into the middle of a block";
|
||||
}
|
||||
PyErr_SetString(PyExc_ValueError, msg);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for illegal jumps out of finally or except blocks. */
|
||||
for (int depth = new_stack.depth; depth < current_stack.depth; depth++) {
|
||||
switch(current_stack.stack[depth].kind) {
|
||||
case RERAISE:
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"can't jump out of a 'finally' block");
|
||||
return -1;
|
||||
case POP_EXCEPT:
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"can't jump out of an 'except' block");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Unwind block stack. */
|
||||
while (current_stack.depth > new_stack.depth) {
|
||||
unsigned char kind = pop_block(¤t_stack);
|
||||
switch(kind) {
|
||||
case FOR_ITER:
|
||||
frame_stack_pop(f);
|
||||
break;
|
||||
case SETUP_FINALLY:
|
||||
case TRY_EXCEPT:
|
||||
frame_block_unwind(f);
|
||||
break;
|
||||
case SETUP_WITH:
|
||||
case SETUP_ASYNC_WITH:
|
||||
case FOR_ITER: {
|
||||
unsigned int oparg = get_arg((const _Py_CODEUNIT *)code,
|
||||
addr / sizeof(_Py_CODEUNIT));
|
||||
int target_addr = addr + oparg + sizeof(_Py_CODEUNIT);
|
||||
assert(target_addr < code_len);
|
||||
/* Police block-jumping (you can't jump into the middle of a block)
|
||||
* and ensure that the blockstack finishes up in a sensible state (by
|
||||
* popping any blocks we're jumping out of). We look at all the
|
||||
* blockstack operations between the current position and the new
|
||||
* one, and keep track of how many blocks we drop out of on the way.
|
||||
* By also keeping track of the lowest blockstack position we see, we
|
||||
* can tell whether the jump goes into any blocks without coming out
|
||||
* again - in that case we raise an exception below. */
|
||||
int first_in = addr < f->f_lasti && f->f_lasti < target_addr;
|
||||
int second_in = addr < new_lasti && new_lasti < target_addr;
|
||||
if (!first_in && second_in) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"can't jump into the middle of a block");
|
||||
frame_block_unwind(f);
|
||||
// Pop the exit function
|
||||
frame_stack_pop(f);
|
||||
break;
|
||||
default:
|
||||
PyErr_SetString(PyExc_SystemError,
|
||||
"unexpected block kind");
|
||||
return -1;
|
||||
}
|
||||
int in_for_loop = op == FOR_ITER || code[target_addr] == END_ASYNC_FOR;
|
||||
if (first_in && !second_in) {
|
||||
if (!delta_iblock) {
|
||||
if (in_for_loop) {
|
||||
/* Pop the iterators of any 'for' and 'async for' loop
|
||||
* we're jumping out of. */
|
||||
delta++;
|
||||
}
|
||||
else if (prevop == LOAD_CONST) {
|
||||
/* Pops None pushed before SETUP_FINALLY. */
|
||||
delta++;
|
||||
}
|
||||
}
|
||||
if (!in_for_loop) {
|
||||
delta_iblock++;
|
||||
}
|
||||
}
|
||||
if (!in_for_loop) {
|
||||
blockstack[blockstack_top++] = target_addr;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case END_FINALLY: {
|
||||
assert(blockstack_top > 0);
|
||||
int target_addr = blockstack[--blockstack_top];
|
||||
assert(target_addr <= addr);
|
||||
int first_in = target_addr <= f->f_lasti && f->f_lasti <= addr;
|
||||
int second_in = target_addr <= new_lasti && new_lasti <= addr;
|
||||
if (first_in != second_in) {
|
||||
op = code[target_addr];
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"can't jump %s %s block",
|
||||
second_in ? "into" : "out of",
|
||||
(op == DUP_TOP || op == POP_TOP) ?
|
||||
"an 'except'" : "a 'finally'");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
prevop = op;
|
||||
}
|
||||
|
||||
/* Verify that the blockstack tracking code didn't get lost. */
|
||||
assert(blockstack_top == 0);
|
||||
|
||||
/* Pop any blocks that we're jumping out of. */
|
||||
if (delta_iblock > 0) {
|
||||
f->f_iblock -= delta_iblock;
|
||||
PyTryBlock *b = &f->f_blockstack[f->f_iblock];
|
||||
delta += (int)(f->f_stacktop - f->f_valuestack) - b->b_level;
|
||||
if (b->b_type == SETUP_FINALLY &&
|
||||
code[b->b_handler] == WITH_CLEANUP_START)
|
||||
{
|
||||
/* Pop the exit function. */
|
||||
delta++;
|
||||
}
|
||||
}
|
||||
while (delta > 0) {
|
||||
PyObject *v = (*--f->f_stacktop);
|
||||
Py_DECREF(v);
|
||||
delta--;
|
||||
}
|
||||
move_to_addr(&tracker, f->f_lasti);
|
||||
move_to_nearest_start_of_line(&tracker, new_lineno);
|
||||
|
||||
/* Finally set the new f_lineno and f_lasti and return OK. */
|
||||
f->f_lineno = new_lineno;
|
||||
f->f_lasti = new_lasti;
|
||||
f->f_lasti = tracker.addr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
210
Python/ceval.c
210
Python/ceval.c
|
@ -79,7 +79,7 @@ static PyObject * unicode_concatenate(PyThreadState *, PyObject *, PyObject *,
|
|||
static PyObject * special_lookup(PyThreadState *, PyObject *, _Py_Identifier *);
|
||||
static int check_args_iterable(PyThreadState *, PyObject *func, PyObject *vararg);
|
||||
static void format_kwargs_error(PyThreadState *, PyObject *func, PyObject *kwargs);
|
||||
static void format_awaitable_error(PyThreadState *, PyTypeObject *, int);
|
||||
static void format_awaitable_error(PyThreadState *, PyTypeObject *, int, int);
|
||||
|
||||
#define NAME_ERROR_MSG \
|
||||
"name '%.200s' is not defined"
|
||||
|
@ -2017,7 +2017,12 @@ main_loop:
|
|||
PyObject *iter = _PyCoro_GetAwaitableIter(iterable);
|
||||
|
||||
if (iter == NULL) {
|
||||
int opcode_at_minus_3 = 0;
|
||||
if ((next_instr - first_instr) > 2) {
|
||||
opcode_at_minus_3 = _Py_OPCODE(next_instr[-3]);
|
||||
}
|
||||
format_awaitable_error(tstate, Py_TYPE(iterable),
|
||||
opcode_at_minus_3,
|
||||
_Py_OPCODE(next_instr[-2]));
|
||||
}
|
||||
|
||||
|
@ -2128,105 +2133,14 @@ main_loop:
|
|||
DISPATCH();
|
||||
}
|
||||
|
||||
case TARGET(POP_FINALLY): {
|
||||
/* If oparg is 0 at the top of the stack are 1 or 6 values:
|
||||
Either:
|
||||
- TOP = NULL or an integer
|
||||
or:
|
||||
- (TOP, SECOND, THIRD) = exc_info()
|
||||
- (FOURTH, FITH, SIXTH) = previous exception for EXCEPT_HANDLER
|
||||
|
||||
If oparg is 1 the value for 'return' was additionally pushed
|
||||
at the top of the stack.
|
||||
*/
|
||||
PyObject *res = NULL;
|
||||
if (oparg) {
|
||||
res = POP();
|
||||
}
|
||||
case TARGET(RERAISE): {
|
||||
PyObject *exc = POP();
|
||||
if (exc == NULL || PyLong_CheckExact(exc)) {
|
||||
Py_XDECREF(exc);
|
||||
}
|
||||
else {
|
||||
Py_DECREF(exc);
|
||||
Py_DECREF(POP());
|
||||
Py_DECREF(POP());
|
||||
|
||||
PyObject *type, *value, *traceback;
|
||||
_PyErr_StackItem *exc_info;
|
||||
PyTryBlock *b = PyFrame_BlockPop(f);
|
||||
if (b->b_type != EXCEPT_HANDLER) {
|
||||
_PyErr_SetString(tstate, PyExc_SystemError,
|
||||
"popped block is not an except handler");
|
||||
Py_XDECREF(res);
|
||||
goto error;
|
||||
}
|
||||
assert(STACK_LEVEL() == (b)->b_level + 3);
|
||||
exc_info = tstate->exc_info;
|
||||
type = exc_info->exc_type;
|
||||
value = exc_info->exc_value;
|
||||
traceback = exc_info->exc_traceback;
|
||||
exc_info->exc_type = POP();
|
||||
exc_info->exc_value = POP();
|
||||
exc_info->exc_traceback = POP();
|
||||
Py_XDECREF(type);
|
||||
Py_XDECREF(value);
|
||||
Py_XDECREF(traceback);
|
||||
}
|
||||
if (oparg) {
|
||||
PUSH(res);
|
||||
}
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
case TARGET(CALL_FINALLY): {
|
||||
PyObject *ret = PyLong_FromLong(INSTR_OFFSET());
|
||||
if (ret == NULL) {
|
||||
goto error;
|
||||
}
|
||||
PUSH(ret);
|
||||
JUMPBY(oparg);
|
||||
FAST_DISPATCH();
|
||||
}
|
||||
|
||||
case TARGET(BEGIN_FINALLY): {
|
||||
/* Push NULL onto the stack for using it in END_FINALLY,
|
||||
POP_FINALLY, WITH_CLEANUP_START and WITH_CLEANUP_FINISH.
|
||||
*/
|
||||
PUSH(NULL);
|
||||
FAST_DISPATCH();
|
||||
}
|
||||
|
||||
case TARGET(END_FINALLY): {
|
||||
PREDICTED(END_FINALLY);
|
||||
/* At the top of the stack are 1 or 6 values:
|
||||
Either:
|
||||
- TOP = NULL or an integer
|
||||
or:
|
||||
- (TOP, SECOND, THIRD) = exc_info()
|
||||
- (FOURTH, FITH, SIXTH) = previous exception for EXCEPT_HANDLER
|
||||
*/
|
||||
PyObject *exc = POP();
|
||||
if (exc == NULL) {
|
||||
FAST_DISPATCH();
|
||||
}
|
||||
else if (PyLong_CheckExact(exc)) {
|
||||
int ret = _PyLong_AsInt(exc);
|
||||
Py_DECREF(exc);
|
||||
if (ret == -1 && _PyErr_Occurred(tstate)) {
|
||||
goto error;
|
||||
}
|
||||
JUMPTO(ret);
|
||||
FAST_DISPATCH();
|
||||
}
|
||||
else {
|
||||
assert(PyExceptionClass_Check(exc));
|
||||
PyObject *val = POP();
|
||||
PyObject *tb = POP();
|
||||
_PyErr_Restore(tstate, exc, val, tb);
|
||||
assert(PyExceptionClass_Check(exc));
|
||||
PyErr_Restore(exc, val, tb);
|
||||
goto exception_unwind;
|
||||
}
|
||||
}
|
||||
|
||||
case TARGET(END_ASYNC_FOR): {
|
||||
PyObject *exc = POP();
|
||||
|
@ -3302,111 +3216,31 @@ main_loop:
|
|||
DISPATCH();
|
||||
}
|
||||
|
||||
case TARGET(WITH_CLEANUP_START): {
|
||||
/* At the top of the stack are 1 or 6 values indicating
|
||||
how/why we entered the finally clause:
|
||||
- TOP = NULL
|
||||
case TARGET(WITH_EXCEPT_START): {
|
||||
/* At the top of the stack are 7 values:
|
||||
- (TOP, SECOND, THIRD) = exc_info()
|
||||
(FOURTH, FITH, SIXTH) = previous exception for EXCEPT_HANDLER
|
||||
Below them is EXIT, the context.__exit__ or context.__aexit__
|
||||
bound method.
|
||||
In the first case, we must call
|
||||
EXIT(None, None, None)
|
||||
otherwise we must call
|
||||
EXIT(TOP, SECOND, THIRD)
|
||||
|
||||
In the first case, we remove EXIT from the
|
||||
stack, leaving TOP, and push TOP on the stack.
|
||||
Otherwise we shift the bottom 3 values of the
|
||||
stack down, replace the empty spot with NULL, and push
|
||||
None on the stack.
|
||||
|
||||
Finally we push the result of the call.
|
||||
- (FOURTH, FIFTH, SIXTH) = previous exception for EXCEPT_HANDLER
|
||||
- SEVENTH: the context.__exit__ bound method
|
||||
We call SEVENTH(TOP, SECOND, THIRD).
|
||||
Then we push again the TOP exception and the __exit__
|
||||
return value.
|
||||
*/
|
||||
PyObject *exit_func;
|
||||
PyObject *exc, *val, *tb, *res;
|
||||
|
||||
val = tb = Py_None;
|
||||
exc = TOP();
|
||||
if (exc == NULL) {
|
||||
STACK_SHRINK(1);
|
||||
exit_func = TOP();
|
||||
SET_TOP(exc);
|
||||
exc = Py_None;
|
||||
}
|
||||
else {
|
||||
assert(PyExceptionClass_Check(exc));
|
||||
PyObject *tp2, *exc2, *tb2;
|
||||
PyTryBlock *block;
|
||||
val = SECOND();
|
||||
tb = THIRD();
|
||||
tp2 = FOURTH();
|
||||
exc2 = PEEK(5);
|
||||
tb2 = PEEK(6);
|
||||
assert(exc != Py_None);
|
||||
assert(!PyLong_Check(exc));
|
||||
exit_func = PEEK(7);
|
||||
SET_VALUE(7, tb2);
|
||||
SET_VALUE(6, exc2);
|
||||
SET_VALUE(5, tp2);
|
||||
/* UNWIND_EXCEPT_HANDLER will pop this off. */
|
||||
SET_FOURTH(NULL);
|
||||
/* We just shifted the stack down, so we have
|
||||
to tell the except handler block that the
|
||||
values are lower than it expects. */
|
||||
assert(f->f_iblock > 0);
|
||||
block = &f->f_blockstack[f->f_iblock - 1];
|
||||
assert(block->b_type == EXCEPT_HANDLER);
|
||||
assert(block->b_level > 0);
|
||||
block->b_level--;
|
||||
}
|
||||
|
||||
PyObject *stack[4] = {NULL, exc, val, tb};
|
||||
res = _PyObject_Vectorcall(exit_func, stack + 1,
|
||||
3 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL);
|
||||
Py_DECREF(exit_func);
|
||||
if (res == NULL)
|
||||
goto error;
|
||||
|
||||
Py_INCREF(exc); /* Duplicating the exception on the stack */
|
||||
PUSH(exc);
|
||||
PUSH(res);
|
||||
PREDICT(WITH_CLEANUP_FINISH);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
case TARGET(WITH_CLEANUP_FINISH): {
|
||||
PREDICTED(WITH_CLEANUP_FINISH);
|
||||
/* TOP = the result of calling the context.__exit__ bound method
|
||||
SECOND = either None or exception type
|
||||
|
||||
If SECOND is None below is NULL or the return address,
|
||||
otherwise below are 7 values representing an exception.
|
||||
*/
|
||||
PyObject *res = POP();
|
||||
PyObject *exc = POP();
|
||||
int err;
|
||||
|
||||
if (exc != Py_None)
|
||||
err = PyObject_IsTrue(res);
|
||||
else
|
||||
err = 0;
|
||||
|
||||
Py_DECREF(res);
|
||||
Py_DECREF(exc);
|
||||
|
||||
if (err < 0)
|
||||
goto error;
|
||||
else if (err > 0) {
|
||||
/* There was an exception and a True return.
|
||||
* We must manually unwind the EXCEPT_HANDLER block
|
||||
* which was created when the exception was caught,
|
||||
* otherwise the stack will be in an inconsistent state.
|
||||
*/
|
||||
PyTryBlock *b = PyFrame_BlockPop(f);
|
||||
assert(b->b_type == EXCEPT_HANDLER);
|
||||
UNWIND_EXCEPT_HANDLER(b);
|
||||
PUSH(NULL);
|
||||
}
|
||||
PREDICT(END_FINALLY);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
|
@ -3776,6 +3610,10 @@ exception_unwind:
|
|||
PUSH(val);
|
||||
PUSH(exc);
|
||||
JUMPTO(handler);
|
||||
if (_Py_TracingPossible(ceval)) {
|
||||
/* Make sure that we trace line after exception */
|
||||
instr_prev = INT_MAX;
|
||||
}
|
||||
/* Resume normal execution */
|
||||
goto main_loop;
|
||||
}
|
||||
|
@ -5477,7 +5315,7 @@ format_exc_unbound(PyThreadState *tstate, PyCodeObject *co, int oparg)
|
|||
}
|
||||
|
||||
static void
|
||||
format_awaitable_error(PyThreadState *tstate, PyTypeObject *type, int prevopcode)
|
||||
format_awaitable_error(PyThreadState *tstate, PyTypeObject *type, int prevprevopcode, int prevopcode)
|
||||
{
|
||||
if (type->tp_as_async == NULL || type->tp_as_async->am_await == NULL) {
|
||||
if (prevopcode == BEFORE_ASYNC_WITH) {
|
||||
|
@ -5486,7 +5324,7 @@ format_awaitable_error(PyThreadState *tstate, PyTypeObject *type, int prevopcode
|
|||
"that does not implement __await__: %.100s",
|
||||
type->tp_name);
|
||||
}
|
||||
else if (prevopcode == WITH_CLEANUP_START) {
|
||||
else if (prevopcode == WITH_EXCEPT_START || (prevopcode == CALL_FUNCTION && prevprevopcode == DUP_TOP)) {
|
||||
_PyErr_Format(tstate, PyExc_TypeError,
|
||||
"'async with' received an object from __aexit__ "
|
||||
"that does not implement __await__: %.100s",
|
||||
|
|
428
Python/compile.c
428
Python/compile.c
|
@ -81,14 +81,16 @@ It's called a frame block to distinguish it from a basic block in the
|
|||
compiler IR.
|
||||
*/
|
||||
|
||||
enum fblocktype { WHILE_LOOP, FOR_LOOP, EXCEPT, FINALLY_TRY, FINALLY_TRY2, FINALLY_END,
|
||||
WITH, ASYNC_WITH, HANDLER_CLEANUP };
|
||||
enum fblocktype { WHILE_LOOP, FOR_LOOP, EXCEPT, FINALLY_TRY, FINALLY_END,
|
||||
WITH, ASYNC_WITH, HANDLER_CLEANUP, POP_VALUE };
|
||||
|
||||
struct fblockinfo {
|
||||
enum fblocktype fb_type;
|
||||
basicblock *fb_block;
|
||||
/* (optional) type-specific exit or cleanup block */
|
||||
basicblock *fb_exit;
|
||||
/* (optional) additional information required for unwinding */
|
||||
void *fb_datum;
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -960,12 +962,6 @@ stack_effect(int opcode, int oparg, int jump)
|
|||
* Restore the stack position and push 6 values before jumping to
|
||||
* the handler if an exception be raised. */
|
||||
return jump ? 6 : 1;
|
||||
case WITH_CLEANUP_START:
|
||||
return 2; /* or 1, depending on TOS */
|
||||
case WITH_CLEANUP_FINISH:
|
||||
/* Pop a variable number of values pushed by WITH_CLEANUP_START
|
||||
* + __exit__ or __aexit__. */
|
||||
return -3;
|
||||
case RETURN_VALUE:
|
||||
return -1;
|
||||
case IMPORT_STAR:
|
||||
|
@ -980,10 +976,6 @@ stack_effect(int opcode, int oparg, int jump)
|
|||
return 0;
|
||||
case POP_EXCEPT:
|
||||
return -3;
|
||||
case END_FINALLY:
|
||||
case POP_FINALLY:
|
||||
/* Pop 6 values when an exception was raised. */
|
||||
return -6;
|
||||
|
||||
case STORE_NAME:
|
||||
return -1;
|
||||
|
@ -1056,14 +1048,11 @@ stack_effect(int opcode, int oparg, int jump)
|
|||
* Restore the stack position and push 6 values before jumping to
|
||||
* the handler if an exception be raised. */
|
||||
return jump ? 6 : 0;
|
||||
case BEGIN_FINALLY:
|
||||
/* Actually pushes 1 value, but count 6 for balancing with
|
||||
* END_FINALLY and POP_FINALLY.
|
||||
* This is the main reason of using this opcode instead of
|
||||
* "LOAD_CONST None". */
|
||||
return 6;
|
||||
case CALL_FINALLY:
|
||||
return jump ? 1 : 0;
|
||||
case RERAISE:
|
||||
return -3;
|
||||
|
||||
case WITH_EXCEPT_START:
|
||||
return 1;
|
||||
|
||||
case LOAD_FAST:
|
||||
return 1;
|
||||
|
@ -1629,7 +1618,7 @@ find_ann(asdl_seq *stmts)
|
|||
|
||||
static int
|
||||
compiler_push_fblock(struct compiler *c, enum fblocktype t, basicblock *b,
|
||||
basicblock *exit)
|
||||
basicblock *exit, void *datum)
|
||||
{
|
||||
struct fblockinfo *f;
|
||||
if (c->u->u_nfblocks >= CO_MAXBLOCKS) {
|
||||
|
@ -1641,6 +1630,7 @@ compiler_push_fblock(struct compiler *c, enum fblocktype t, basicblock *b,
|
|||
f->fb_type = t;
|
||||
f->fb_block = b;
|
||||
f->fb_exit = exit;
|
||||
f->fb_datum = datum;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -1654,8 +1644,19 @@ compiler_pop_fblock(struct compiler *c, enum fblocktype t, basicblock *b)
|
|||
assert(u->u_fblock[u->u_nfblocks].fb_block == b);
|
||||
}
|
||||
|
||||
static int
|
||||
compiler_call_exit_with_nones(struct compiler *c) {
|
||||
ADDOP_O(c, LOAD_CONST, Py_None, consts);
|
||||
ADDOP(c, DUP_TOP);
|
||||
ADDOP(c, DUP_TOP);
|
||||
ADDOP_I(c, CALL_FUNCTION, 3);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Unwind a frame block. If preserve_tos is true, the TOS before
|
||||
* popping the blocks will be restored afterwards.
|
||||
* popping the blocks will be restored afterwards, unless another
|
||||
* return, break or continue is found. In which case, the TOS will
|
||||
* be popped.
|
||||
*/
|
||||
static int
|
||||
compiler_unwind_fblock(struct compiler *c, struct fblockinfo *info,
|
||||
|
@ -1665,15 +1666,6 @@ compiler_unwind_fblock(struct compiler *c, struct fblockinfo *info,
|
|||
case WHILE_LOOP:
|
||||
return 1;
|
||||
|
||||
case FINALLY_END:
|
||||
info->fb_exit = NULL;
|
||||
ADDOP_I(c, POP_FINALLY, preserve_tos);
|
||||
if (preserve_tos) {
|
||||
ADDOP(c, ROT_TWO);
|
||||
}
|
||||
ADDOP(c, POP_TOP);
|
||||
return 1;
|
||||
|
||||
case FOR_LOOP:
|
||||
/* Pop the iterator */
|
||||
if (preserve_tos) {
|
||||
|
@ -1688,20 +1680,28 @@ compiler_unwind_fblock(struct compiler *c, struct fblockinfo *info,
|
|||
|
||||
case FINALLY_TRY:
|
||||
ADDOP(c, POP_BLOCK);
|
||||
ADDOP_JREL(c, CALL_FINALLY, info->fb_exit);
|
||||
if (preserve_tos) {
|
||||
if (!compiler_push_fblock(c, POP_VALUE, NULL, NULL, NULL)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
VISIT_SEQ(c, stmt, info->fb_datum);
|
||||
if (preserve_tos) {
|
||||
compiler_pop_fblock(c, POP_VALUE, NULL);
|
||||
}
|
||||
return 1;
|
||||
|
||||
case FINALLY_TRY2:
|
||||
ADDOP(c, POP_BLOCK);
|
||||
case FINALLY_END:
|
||||
if (preserve_tos) {
|
||||
ADDOP(c, ROT_TWO);
|
||||
ADDOP(c, POP_TOP);
|
||||
ADDOP_JREL(c, CALL_FINALLY, info->fb_exit);
|
||||
ADDOP(c, ROT_FOUR);
|
||||
}
|
||||
else {
|
||||
ADDOP_JREL(c, CALL_FINALLY, info->fb_exit);
|
||||
ADDOP(c, POP_TOP);
|
||||
ADDOP(c, POP_TOP);
|
||||
ADDOP(c, POP_TOP);
|
||||
if (preserve_tos) {
|
||||
ADDOP(c, ROT_FOUR);
|
||||
}
|
||||
ADDOP(c, POP_EXCEPT);
|
||||
return 1;
|
||||
|
||||
case WITH:
|
||||
|
@ -1710,34 +1710,66 @@ compiler_unwind_fblock(struct compiler *c, struct fblockinfo *info,
|
|||
if (preserve_tos) {
|
||||
ADDOP(c, ROT_TWO);
|
||||
}
|
||||
ADDOP(c, BEGIN_FINALLY);
|
||||
ADDOP(c, WITH_CLEANUP_START);
|
||||
if(!compiler_call_exit_with_nones(c)) {
|
||||
return 0;
|
||||
}
|
||||
if (info->fb_type == ASYNC_WITH) {
|
||||
ADDOP(c, GET_AWAITABLE);
|
||||
ADDOP_LOAD_CONST(c, Py_None);
|
||||
ADDOP(c, YIELD_FROM);
|
||||
}
|
||||
ADDOP(c, WITH_CLEANUP_FINISH);
|
||||
ADDOP_I(c, POP_FINALLY, 0);
|
||||
ADDOP(c, POP_TOP);
|
||||
return 1;
|
||||
|
||||
case HANDLER_CLEANUP:
|
||||
if (info->fb_datum) {
|
||||
ADDOP(c, POP_BLOCK);
|
||||
}
|
||||
if (preserve_tos) {
|
||||
ADDOP(c, ROT_FOUR);
|
||||
}
|
||||
if (info->fb_exit) {
|
||||
ADDOP(c, POP_BLOCK);
|
||||
ADDOP(c, POP_EXCEPT);
|
||||
ADDOP_JREL(c, CALL_FINALLY, info->fb_exit);
|
||||
if (info->fb_datum) {
|
||||
ADDOP_LOAD_CONST(c, Py_None);
|
||||
compiler_nameop(c, info->fb_datum, Store);
|
||||
compiler_nameop(c, info->fb_datum, Del);
|
||||
}
|
||||
else {
|
||||
ADDOP(c, POP_EXCEPT);
|
||||
return 1;
|
||||
|
||||
case POP_VALUE:
|
||||
if (preserve_tos) {
|
||||
ADDOP(c, ROT_TWO);
|
||||
}
|
||||
ADDOP(c, POP_TOP);
|
||||
return 1;
|
||||
}
|
||||
Py_UNREACHABLE();
|
||||
}
|
||||
|
||||
/** Unwind block stack. If loop is not NULL, then stop when the first loop is encountered. */
|
||||
static int
|
||||
compiler_unwind_fblock_stack(struct compiler *c, int preserve_tos, struct fblockinfo **loop) {
|
||||
if (c->u->u_nfblocks == 0) {
|
||||
return 1;
|
||||
}
|
||||
struct fblockinfo *top = &c->u->u_fblock[c->u->u_nfblocks-1];
|
||||
if (loop != NULL && (top->fb_type == WHILE_LOOP || top->fb_type == FOR_LOOP)) {
|
||||
*loop = top;
|
||||
return 1;
|
||||
}
|
||||
struct fblockinfo copy = *top;
|
||||
c->u->u_nfblocks--;
|
||||
if (!compiler_unwind_fblock(c, ©, preserve_tos)) {
|
||||
return 0;
|
||||
}
|
||||
if (!compiler_unwind_fblock_stack(c, preserve_tos, loop)) {
|
||||
return 0;
|
||||
}
|
||||
c->u->u_fblock[c->u->u_nfblocks] = copy;
|
||||
c->u->u_nfblocks++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Compile a sequence of statements, checking for a docstring
|
||||
and for annotations. */
|
||||
|
||||
|
@ -2634,10 +2666,12 @@ compiler_if(struct compiler *c, stmt_ty s)
|
|||
if (next == NULL)
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
else {
|
||||
next = end;
|
||||
if (!compiler_jump_if(c, s->v.If.test, next, 0))
|
||||
}
|
||||
if (!compiler_jump_if(c, s->v.If.test, next, 0)) {
|
||||
return 0;
|
||||
}
|
||||
VISIT_SEQ(c, stmt, s->v.If.body);
|
||||
if (asdl_seq_LEN(s->v.If.orelse)) {
|
||||
ADDOP_JREL(c, JUMP_FORWARD, end);
|
||||
|
@ -2657,12 +2691,12 @@ compiler_for(struct compiler *c, stmt_ty s)
|
|||
start = compiler_new_block(c);
|
||||
cleanup = compiler_new_block(c);
|
||||
end = compiler_new_block(c);
|
||||
if (start == NULL || end == NULL || cleanup == NULL)
|
||||
if (start == NULL || end == NULL || cleanup == NULL) {
|
||||
return 0;
|
||||
|
||||
if (!compiler_push_fblock(c, FOR_LOOP, start, end))
|
||||
}
|
||||
if (!compiler_push_fblock(c, FOR_LOOP, start, end, NULL)) {
|
||||
return 0;
|
||||
|
||||
}
|
||||
VISIT(c, expr, s->v.For.iter);
|
||||
ADDOP(c, GET_ITER);
|
||||
compiler_use_next_block(c, start);
|
||||
|
@ -2694,16 +2728,16 @@ compiler_async_for(struct compiler *c, stmt_ty s)
|
|||
except = compiler_new_block(c);
|
||||
end = compiler_new_block(c);
|
||||
|
||||
if (start == NULL || except == NULL || end == NULL)
|
||||
if (start == NULL || except == NULL || end == NULL) {
|
||||
return 0;
|
||||
|
||||
}
|
||||
VISIT(c, expr, s->v.AsyncFor.iter);
|
||||
ADDOP(c, GET_AITER);
|
||||
|
||||
compiler_use_next_block(c, start);
|
||||
if (!compiler_push_fblock(c, FOR_LOOP, start, end))
|
||||
if (!compiler_push_fblock(c, FOR_LOOP, start, end, NULL)) {
|
||||
return 0;
|
||||
|
||||
}
|
||||
/* SETUP_FINALLY to guard the __anext__ call */
|
||||
ADDOP_JREL(c, SETUP_FINALLY, except);
|
||||
ADDOP(c, GET_ANEXT);
|
||||
|
@ -2741,7 +2775,7 @@ compiler_while(struct compiler *c, stmt_ty s)
|
|||
// Push a dummy block so the VISIT_SEQ knows that we are
|
||||
// inside a while loop so it can correctly evaluate syntax
|
||||
// errors.
|
||||
if (!compiler_push_fblock(c, WHILE_LOOP, NULL, NULL)) {
|
||||
if (!compiler_push_fblock(c, WHILE_LOOP, NULL, NULL, NULL)) {
|
||||
return 0;
|
||||
}
|
||||
VISIT_SEQ(c, stmt, s->v.While.body);
|
||||
|
@ -2771,7 +2805,7 @@ compiler_while(struct compiler *c, stmt_ty s)
|
|||
orelse = NULL;
|
||||
|
||||
compiler_use_next_block(c, loop);
|
||||
if (!compiler_push_fblock(c, WHILE_LOOP, loop, end))
|
||||
if (!compiler_push_fblock(c, WHILE_LOOP, loop, end, NULL))
|
||||
return 0;
|
||||
if (constant == -1) {
|
||||
if (!compiler_jump_if(c, s->v.While.test, anchor, 0))
|
||||
|
@ -2811,12 +2845,8 @@ compiler_return(struct compiler *c, stmt_ty s)
|
|||
if (preserve_tos) {
|
||||
VISIT(c, expr, s->v.Return.value);
|
||||
}
|
||||
for (int depth = c->u->u_nfblocks; depth--;) {
|
||||
struct fblockinfo *info = &c->u->u_fblock[depth];
|
||||
|
||||
if (!compiler_unwind_fblock(c, info, preserve_tos))
|
||||
if (!compiler_unwind_fblock_stack(c, preserve_tos, NULL))
|
||||
return 0;
|
||||
}
|
||||
if (s->v.Return.value == NULL) {
|
||||
ADDOP_LOAD_CONST(c, Py_None);
|
||||
}
|
||||
|
@ -2831,33 +2861,32 @@ compiler_return(struct compiler *c, stmt_ty s)
|
|||
static int
|
||||
compiler_break(struct compiler *c)
|
||||
{
|
||||
for (int depth = c->u->u_nfblocks; depth--;) {
|
||||
struct fblockinfo *info = &c->u->u_fblock[depth];
|
||||
|
||||
if (!compiler_unwind_fblock(c, info, 0))
|
||||
struct fblockinfo *loop = NULL;
|
||||
if (!compiler_unwind_fblock_stack(c, 0, &loop)) {
|
||||
return 0;
|
||||
if (info->fb_type == WHILE_LOOP || info->fb_type == FOR_LOOP) {
|
||||
ADDOP_JABS(c, JUMP_ABSOLUTE, info->fb_exit);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (loop == NULL) {
|
||||
return compiler_error(c, "'break' outside loop");
|
||||
}
|
||||
if (!compiler_unwind_fblock(c, loop, 0)) {
|
||||
return 0;
|
||||
}
|
||||
ADDOP_JABS(c, JUMP_ABSOLUTE, loop->fb_exit);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
compiler_continue(struct compiler *c)
|
||||
{
|
||||
for (int depth = c->u->u_nfblocks; depth--;) {
|
||||
struct fblockinfo *info = &c->u->u_fblock[depth];
|
||||
|
||||
if (info->fb_type == WHILE_LOOP || info->fb_type == FOR_LOOP) {
|
||||
ADDOP_JABS(c, JUMP_ABSOLUTE, info->fb_block);
|
||||
return 1;
|
||||
}
|
||||
if (!compiler_unwind_fblock(c, info, 0))
|
||||
struct fblockinfo *loop = NULL;
|
||||
if (!compiler_unwind_fblock_stack(c, 0, &loop)) {
|
||||
return 0;
|
||||
}
|
||||
if (loop == NULL) {
|
||||
return compiler_error(c, "'continue' not properly in loop");
|
||||
}
|
||||
ADDOP_JABS(c, JUMP_ABSOLUTE, loop->fb_block);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
@ -2866,10 +2895,11 @@ compiler_continue(struct compiler *c)
|
|||
SETUP_FINALLY L
|
||||
<code for body>
|
||||
POP_BLOCK
|
||||
BEGIN_FINALLY
|
||||
<code for finalbody>
|
||||
JUMP E
|
||||
L:
|
||||
<code for finalbody>
|
||||
END_FINALLY
|
||||
E:
|
||||
|
||||
The special instructions use the block stack. Each block
|
||||
stack entry contains the instruction that created it (here
|
||||
|
@ -2881,11 +2911,6 @@ compiler_continue(struct compiler *c)
|
|||
onto the block stack.
|
||||
POP_BLOCK:
|
||||
Pops en entry from the block stack.
|
||||
BEGIN_FINALLY
|
||||
Pushes NULL onto the value stack.
|
||||
END_FINALLY:
|
||||
Pops 1 (NULL or int) or 6 entries from the *value* stack and restore
|
||||
the raised and the caught exceptions they specify.
|
||||
|
||||
The block stack is unwound when an exception is raised:
|
||||
when a SETUP_FINALLY entry is found, the raised and the caught
|
||||
|
@ -2897,47 +2922,18 @@ compiler_continue(struct compiler *c)
|
|||
static int
|
||||
compiler_try_finally(struct compiler *c, stmt_ty s)
|
||||
{
|
||||
basicblock *start, *newcurblock, *body, *end;
|
||||
int break_finally = 1;
|
||||
basicblock *body, *end, *exit;
|
||||
|
||||
body = compiler_new_block(c);
|
||||
end = compiler_new_block(c);
|
||||
if (body == NULL || end == NULL)
|
||||
exit = compiler_new_block(c);
|
||||
if (body == NULL || end == NULL || exit == NULL)
|
||||
return 0;
|
||||
|
||||
start = c->u->u_curblock;
|
||||
|
||||
/* `finally` block. Compile it first to determine if any of "break",
|
||||
"continue" or "return" are used in it. */
|
||||
compiler_use_next_block(c, end);
|
||||
if (!compiler_push_fblock(c, FINALLY_END, end, end))
|
||||
return 0;
|
||||
VISIT_SEQ(c, stmt, s->v.Try.finalbody);
|
||||
ADDOP(c, END_FINALLY);
|
||||
break_finally = (c->u->u_fblock[c->u->u_nfblocks - 1].fb_exit == NULL);
|
||||
if (break_finally) {
|
||||
/* Pops a placeholder. See below */
|
||||
ADDOP(c, POP_TOP);
|
||||
}
|
||||
compiler_pop_fblock(c, FINALLY_END, end);
|
||||
|
||||
newcurblock = c->u->u_curblock;
|
||||
c->u->u_curblock = start;
|
||||
start->b_next = NULL;
|
||||
|
||||
/* `try` block */
|
||||
c->u->u_lineno_set = 0;
|
||||
c->u->u_lineno = s->lineno;
|
||||
c->u->u_col_offset = s->col_offset;
|
||||
if (break_finally) {
|
||||
/* Pushes a placeholder for the value of "return" in the "try" block
|
||||
to balance the stack for "break", "continue" and "return" in
|
||||
the "finally" block. */
|
||||
ADDOP_LOAD_CONST(c, Py_None);
|
||||
}
|
||||
ADDOP_JREL(c, SETUP_FINALLY, end);
|
||||
compiler_use_next_block(c, body);
|
||||
if (!compiler_push_fblock(c, break_finally ? FINALLY_TRY2 : FINALLY_TRY, body, end))
|
||||
if (!compiler_push_fblock(c, FINALLY_TRY, body, end, s->v.Try.finalbody))
|
||||
return 0;
|
||||
if (s->v.Try.handlers && asdl_seq_LEN(s->v.Try.handlers)) {
|
||||
if (!compiler_try_except(c, s))
|
||||
|
@ -2947,12 +2943,17 @@ compiler_try_finally(struct compiler *c, stmt_ty s)
|
|||
VISIT_SEQ(c, stmt, s->v.Try.body);
|
||||
}
|
||||
ADDOP(c, POP_BLOCK);
|
||||
ADDOP(c, BEGIN_FINALLY);
|
||||
compiler_pop_fblock(c, break_finally ? FINALLY_TRY2 : FINALLY_TRY, body);
|
||||
|
||||
c->u->u_curblock->b_next = end;
|
||||
c->u->u_curblock = newcurblock;
|
||||
|
||||
compiler_pop_fblock(c, FINALLY_TRY, body);
|
||||
VISIT_SEQ(c, stmt, s->v.Try.finalbody);
|
||||
ADDOP_JREL(c, JUMP_FORWARD, exit);
|
||||
/* `finally` block */
|
||||
compiler_use_next_block(c, end);
|
||||
if (!compiler_push_fblock(c, FINALLY_END, end, NULL, NULL))
|
||||
return 0;
|
||||
VISIT_SEQ(c, stmt, s->v.Try.finalbody);
|
||||
compiler_pop_fblock(c, FINALLY_END, end);
|
||||
ADDOP(c, RERAISE);
|
||||
compiler_use_next_block(c, exit);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -2981,7 +2982,7 @@ compiler_try_finally(struct compiler *c, stmt_ty s)
|
|||
[tb, val, exc] L2: DUP
|
||||
.............................etc.......................
|
||||
|
||||
[tb, val, exc] Ln+1: END_FINALLY # re-raise exception
|
||||
[tb, val, exc] Ln+1: RERAISE # re-raise exception
|
||||
|
||||
[] L0: <next statement>
|
||||
|
||||
|
@ -3001,7 +3002,7 @@ compiler_try_except(struct compiler *c, stmt_ty s)
|
|||
return 0;
|
||||
ADDOP_JREL(c, SETUP_FINALLY, except);
|
||||
compiler_use_next_block(c, body);
|
||||
if (!compiler_push_fblock(c, EXCEPT, body, NULL))
|
||||
if (!compiler_push_fblock(c, EXCEPT, body, NULL, NULL))
|
||||
return 0;
|
||||
VISIT_SEQ(c, stmt, s->v.Try.body);
|
||||
ADDOP(c, POP_BLOCK);
|
||||
|
@ -3053,28 +3054,29 @@ compiler_try_except(struct compiler *c, stmt_ty s)
|
|||
/* second try: */
|
||||
ADDOP_JREL(c, SETUP_FINALLY, cleanup_end);
|
||||
compiler_use_next_block(c, cleanup_body);
|
||||
if (!compiler_push_fblock(c, HANDLER_CLEANUP, cleanup_body, cleanup_end))
|
||||
if (!compiler_push_fblock(c, HANDLER_CLEANUP, cleanup_body, NULL, handler->v.ExceptHandler.name))
|
||||
return 0;
|
||||
|
||||
/* second # body */
|
||||
VISIT_SEQ(c, stmt, handler->v.ExceptHandler.body);
|
||||
ADDOP(c, POP_BLOCK);
|
||||
ADDOP(c, BEGIN_FINALLY);
|
||||
compiler_pop_fblock(c, HANDLER_CLEANUP, cleanup_body);
|
||||
ADDOP(c, POP_BLOCK);
|
||||
ADDOP(c, POP_EXCEPT);
|
||||
/* name = None; del name */
|
||||
ADDOP_LOAD_CONST(c, Py_None);
|
||||
compiler_nameop(c, handler->v.ExceptHandler.name, Store);
|
||||
compiler_nameop(c, handler->v.ExceptHandler.name, Del);
|
||||
ADDOP_JREL(c, JUMP_FORWARD, end);
|
||||
|
||||
/* finally: */
|
||||
/* except: */
|
||||
compiler_use_next_block(c, cleanup_end);
|
||||
if (!compiler_push_fblock(c, FINALLY_END, cleanup_end, NULL))
|
||||
return 0;
|
||||
|
||||
/* name = None; del name */
|
||||
ADDOP_LOAD_CONST(c, Py_None);
|
||||
compiler_nameop(c, handler->v.ExceptHandler.name, Store);
|
||||
compiler_nameop(c, handler->v.ExceptHandler.name, Del);
|
||||
|
||||
ADDOP(c, END_FINALLY);
|
||||
ADDOP(c, POP_EXCEPT);
|
||||
compiler_pop_fblock(c, FINALLY_END, cleanup_end);
|
||||
ADDOP(c, RERAISE);
|
||||
}
|
||||
else {
|
||||
basicblock *cleanup_body;
|
||||
|
@ -3086,16 +3088,16 @@ compiler_try_except(struct compiler *c, stmt_ty s)
|
|||
ADDOP(c, POP_TOP);
|
||||
ADDOP(c, POP_TOP);
|
||||
compiler_use_next_block(c, cleanup_body);
|
||||
if (!compiler_push_fblock(c, HANDLER_CLEANUP, cleanup_body, NULL))
|
||||
if (!compiler_push_fblock(c, HANDLER_CLEANUP, cleanup_body, NULL, NULL))
|
||||
return 0;
|
||||
VISIT_SEQ(c, stmt, handler->v.ExceptHandler.body);
|
||||
ADDOP(c, POP_EXCEPT);
|
||||
compiler_pop_fblock(c, HANDLER_CLEANUP, cleanup_body);
|
||||
}
|
||||
ADDOP(c, POP_EXCEPT);
|
||||
ADDOP_JREL(c, JUMP_FORWARD, end);
|
||||
}
|
||||
compiler_use_next_block(c, except);
|
||||
}
|
||||
ADDOP(c, END_FINALLY);
|
||||
ADDOP(c, RERAISE);
|
||||
compiler_use_next_block(c, orelse);
|
||||
VISIT_SEQ(c, stmt, s->v.Try.orelse);
|
||||
compiler_use_next_block(c, end);
|
||||
|
@ -4630,6 +4632,22 @@ expr_constant(expr_ty e)
|
|||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
compiler_with_except_finish(struct compiler *c) {
|
||||
basicblock *exit;
|
||||
exit = compiler_new_block(c);
|
||||
if (exit == NULL)
|
||||
return 0;
|
||||
ADDOP_JABS(c, POP_JUMP_IF_TRUE, exit);
|
||||
ADDOP(c, RERAISE);
|
||||
compiler_use_next_block(c, exit);
|
||||
ADDOP(c, POP_TOP);
|
||||
ADDOP(c, POP_TOP);
|
||||
ADDOP(c, POP_TOP);
|
||||
ADDOP(c, POP_EXCEPT);
|
||||
ADDOP(c, POP_TOP);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
Implements the async with statement.
|
||||
|
@ -4658,7 +4676,7 @@ expr_constant(expr_ty e)
|
|||
static int
|
||||
compiler_async_with(struct compiler *c, stmt_ty s, int pos)
|
||||
{
|
||||
basicblock *block, *finally;
|
||||
basicblock *block, *final, *exit;
|
||||
withitem_ty item = asdl_seq_GET(s->v.AsyncWith.items, pos);
|
||||
|
||||
assert(s->kind == AsyncWith_kind);
|
||||
|
@ -4669,8 +4687,9 @@ compiler_async_with(struct compiler *c, stmt_ty s, int pos)
|
|||
}
|
||||
|
||||
block = compiler_new_block(c);
|
||||
finally = compiler_new_block(c);
|
||||
if (!block || !finally)
|
||||
final = compiler_new_block(c);
|
||||
exit = compiler_new_block(c);
|
||||
if (!block || !final || !exit)
|
||||
return 0;
|
||||
|
||||
/* Evaluate EXPR */
|
||||
|
@ -4681,11 +4700,11 @@ compiler_async_with(struct compiler *c, stmt_ty s, int pos)
|
|||
ADDOP_LOAD_CONST(c, Py_None);
|
||||
ADDOP(c, YIELD_FROM);
|
||||
|
||||
ADDOP_JREL(c, SETUP_ASYNC_WITH, finally);
|
||||
ADDOP_JREL(c, SETUP_ASYNC_WITH, final);
|
||||
|
||||
/* SETUP_ASYNC_WITH pushes a finally block. */
|
||||
compiler_use_next_block(c, block);
|
||||
if (!compiler_push_fblock(c, ASYNC_WITH, block, finally)) {
|
||||
if (!compiler_push_fblock(c, ASYNC_WITH, block, final, NULL)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -4704,76 +4723,80 @@ compiler_async_with(struct compiler *c, stmt_ty s, int pos)
|
|||
else if (!compiler_async_with(c, s, pos))
|
||||
return 0;
|
||||
|
||||
/* End of try block; start the finally block */
|
||||
ADDOP(c, POP_BLOCK);
|
||||
ADDOP(c, BEGIN_FINALLY);
|
||||
compiler_pop_fblock(c, ASYNC_WITH, block);
|
||||
ADDOP(c, POP_BLOCK);
|
||||
/* End of body; start the cleanup */
|
||||
|
||||
compiler_use_next_block(c, finally);
|
||||
if (!compiler_push_fblock(c, FINALLY_END, finally, NULL))
|
||||
/* For successful outcome:
|
||||
* call __exit__(None, None, None)
|
||||
*/
|
||||
if(!compiler_call_exit_with_nones(c))
|
||||
return 0;
|
||||
ADDOP(c, GET_AWAITABLE);
|
||||
ADDOP_O(c, LOAD_CONST, Py_None, consts);
|
||||
ADDOP(c, YIELD_FROM);
|
||||
|
||||
/* Finally block starts; context.__exit__ is on the stack under
|
||||
the exception or return information. Just issue our magic
|
||||
opcode. */
|
||||
ADDOP(c, WITH_CLEANUP_START);
|
||||
ADDOP(c, POP_TOP);
|
||||
|
||||
ADDOP_JABS(c, JUMP_ABSOLUTE, exit);
|
||||
|
||||
/* For exceptional outcome: */
|
||||
compiler_use_next_block(c, final);
|
||||
|
||||
ADDOP(c, WITH_EXCEPT_START);
|
||||
ADDOP(c, GET_AWAITABLE);
|
||||
ADDOP_LOAD_CONST(c, Py_None);
|
||||
ADDOP(c, YIELD_FROM);
|
||||
compiler_with_except_finish(c);
|
||||
|
||||
ADDOP(c, WITH_CLEANUP_FINISH);
|
||||
|
||||
/* Finally block ends. */
|
||||
ADDOP(c, END_FINALLY);
|
||||
compiler_pop_fblock(c, FINALLY_END, finally);
|
||||
compiler_use_next_block(c, exit);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Implements the with statement from PEP 343.
|
||||
|
||||
The semantics outlined in that PEP are as follows:
|
||||
|
||||
with EXPR as VAR:
|
||||
BLOCK
|
||||
|
||||
It is implemented roughly as:
|
||||
|
||||
context = EXPR
|
||||
exit = context.__exit__ # not calling it
|
||||
value = context.__enter__()
|
||||
try:
|
||||
VAR = value # if VAR present in the syntax
|
||||
BLOCK
|
||||
finally:
|
||||
if an exception was raised:
|
||||
exc = copy of (exception, instance, traceback)
|
||||
else:
|
||||
exc = (None, None, None)
|
||||
exit(*exc)
|
||||
is implemented as:
|
||||
<code for EXPR>
|
||||
SETUP_WITH E
|
||||
<code to store to VAR> or POP_TOP
|
||||
<code for BLOCK>
|
||||
LOAD_CONST (None, None, None)
|
||||
CALL_FUNCTION_EX 0
|
||||
JUMP_FORWARD EXIT
|
||||
E: WITH_EXCEPT_START (calls EXPR.__exit__)
|
||||
POP_JUMP_IF_TRUE T:
|
||||
RERAISE
|
||||
T: POP_TOP * 3 (remove exception from stack)
|
||||
POP_EXCEPT
|
||||
POP_TOP
|
||||
EXIT:
|
||||
*/
|
||||
|
||||
static int
|
||||
compiler_with(struct compiler *c, stmt_ty s, int pos)
|
||||
{
|
||||
basicblock *block, *finally;
|
||||
basicblock *block, *final, *exit;
|
||||
withitem_ty item = asdl_seq_GET(s->v.With.items, pos);
|
||||
|
||||
assert(s->kind == With_kind);
|
||||
|
||||
block = compiler_new_block(c);
|
||||
finally = compiler_new_block(c);
|
||||
if (!block || !finally)
|
||||
final = compiler_new_block(c);
|
||||
exit = compiler_new_block(c);
|
||||
if (!block || !final || !exit)
|
||||
return 0;
|
||||
|
||||
/* Evaluate EXPR */
|
||||
VISIT(c, expr, item->context_expr);
|
||||
ADDOP_JREL(c, SETUP_WITH, finally);
|
||||
/* Will push bound __exit__ */
|
||||
ADDOP_JREL(c, SETUP_WITH, final);
|
||||
|
||||
/* SETUP_WITH pushes a finally block. */
|
||||
compiler_use_next_block(c, block);
|
||||
if (!compiler_push_fblock(c, WITH, block, finally)) {
|
||||
if (!compiler_push_fblock(c, WITH, block, final, NULL)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -4792,24 +4815,26 @@ compiler_with(struct compiler *c, stmt_ty s, int pos)
|
|||
else if (!compiler_with(c, s, pos))
|
||||
return 0;
|
||||
|
||||
/* End of try block; start the finally block */
|
||||
ADDOP(c, POP_BLOCK);
|
||||
ADDOP(c, BEGIN_FINALLY);
|
||||
compiler_pop_fblock(c, WITH, block);
|
||||
|
||||
compiler_use_next_block(c, finally);
|
||||
if (!compiler_push_fblock(c, FINALLY_END, finally, NULL))
|
||||
/* End of body; start the cleanup. */
|
||||
|
||||
/* For successful outcome:
|
||||
* call __exit__(None, None, None)
|
||||
*/
|
||||
if (!compiler_call_exit_with_nones(c))
|
||||
return 0;
|
||||
ADDOP(c, POP_TOP);
|
||||
ADDOP_JREL(c, JUMP_FORWARD, exit);
|
||||
|
||||
/* Finally block starts; context.__exit__ is on the stack under
|
||||
the exception or return information. Just issue our magic
|
||||
opcode. */
|
||||
ADDOP(c, WITH_CLEANUP_START);
|
||||
ADDOP(c, WITH_CLEANUP_FINISH);
|
||||
/* For exceptional outcome: */
|
||||
compiler_use_next_block(c, final);
|
||||
|
||||
/* Finally block ends. */
|
||||
ADDOP(c, END_FINALLY);
|
||||
compiler_pop_fblock(c, FINALLY_END, finally);
|
||||
ADDOP(c, WITH_EXCEPT_START);
|
||||
compiler_with_except_finish(c);
|
||||
|
||||
compiler_use_next_block(c, exit);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -5427,7 +5452,7 @@ Py_LOCAL_INLINE(void)
|
|||
stackdepth_push(basicblock ***sp, basicblock *b, int depth)
|
||||
{
|
||||
assert(b->b_startdepth < 0 || b->b_startdepth == depth);
|
||||
if (b->b_startdepth < depth) {
|
||||
if (b->b_startdepth < depth && b->b_startdepth < 100) {
|
||||
assert(b->b_startdepth < 0);
|
||||
b->b_startdepth = depth;
|
||||
*(*sp)++ = b;
|
||||
|
@ -5483,19 +5508,14 @@ stackdepth(struct compiler *c)
|
|||
maxdepth = target_depth;
|
||||
}
|
||||
assert(target_depth >= 0); /* invalid code or bug in stackdepth() */
|
||||
if (instr->i_opcode == CALL_FINALLY) {
|
||||
assert(instr->i_target->b_startdepth >= 0);
|
||||
assert(instr->i_target->b_startdepth >= target_depth);
|
||||
depth = new_depth;
|
||||
continue;
|
||||
}
|
||||
stackdepth_push(&sp, instr->i_target, target_depth);
|
||||
}
|
||||
depth = new_depth;
|
||||
if (instr->i_opcode == JUMP_ABSOLUTE ||
|
||||
instr->i_opcode == JUMP_FORWARD ||
|
||||
instr->i_opcode == RETURN_VALUE ||
|
||||
instr->i_opcode == RAISE_VARARGS)
|
||||
instr->i_opcode == RAISE_VARARGS ||
|
||||
instr->i_opcode == RERAISE)
|
||||
{
|
||||
/* remaining code is dead */
|
||||
next = NULL;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -47,12 +47,12 @@ static void *opcode_targets[256] = {
|
|||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_RERAISE,
|
||||
&&TARGET_WITH_EXCEPT_START,
|
||||
&&TARGET_GET_AITER,
|
||||
&&TARGET_GET_ANEXT,
|
||||
&&TARGET_BEFORE_ASYNC_WITH,
|
||||
&&TARGET_BEGIN_FINALLY,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_END_ASYNC_FOR,
|
||||
&&TARGET_INPLACE_ADD,
|
||||
&&TARGET_INPLACE_SUBTRACT,
|
||||
|
@ -80,14 +80,14 @@ static void *opcode_targets[256] = {
|
|||
&&TARGET_INPLACE_XOR,
|
||||
&&TARGET_INPLACE_OR,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_WITH_CLEANUP_START,
|
||||
&&TARGET_WITH_CLEANUP_FINISH,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_RETURN_VALUE,
|
||||
&&TARGET_IMPORT_STAR,
|
||||
&&TARGET_SETUP_ANNOTATIONS,
|
||||
&&TARGET_YIELD_VALUE,
|
||||
&&TARGET_POP_BLOCK,
|
||||
&&TARGET_END_FINALLY,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_POP_EXCEPT,
|
||||
&&TARGET_STORE_NAME,
|
||||
&&TARGET_DELETE_NAME,
|
||||
|
@ -161,8 +161,8 @@ static void *opcode_targets[256] = {
|
|||
&&_unknown_opcode,
|
||||
&&TARGET_LOAD_METHOD,
|
||||
&&TARGET_CALL_METHOD,
|
||||
&&TARGET_CALL_FINALLY,
|
||||
&&TARGET_POP_FINALLY,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
|
|
|
@ -198,7 +198,6 @@ markblocks(_Py_CODEUNIT *code, Py_ssize_t len)
|
|||
case SETUP_FINALLY:
|
||||
case SETUP_WITH:
|
||||
case SETUP_ASYNC_WITH:
|
||||
case CALL_FINALLY:
|
||||
j = GETJUMPTGT(code, i);
|
||||
assert(j < len);
|
||||
blocks[j] = 1;
|
||||
|
@ -432,14 +431,10 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
|
|||
/* Remove unreachable ops after RETURN */
|
||||
case RETURN_VALUE:
|
||||
h = i + 1;
|
||||
/* END_FINALLY should be kept since it denotes the end of
|
||||
the 'finally' block in frame_setlineno() in frameobject.c.
|
||||
SETUP_FINALLY should be kept for balancing.
|
||||
*/
|
||||
while (h < codelen && ISBASICBLOCK(blocks, i, h) &&
|
||||
_Py_OPCODE(codestr[h]) != END_FINALLY)
|
||||
while (h < codelen && ISBASICBLOCK(blocks, i, h))
|
||||
{
|
||||
if (_Py_OPCODE(codestr[h]) == SETUP_FINALLY) {
|
||||
/* Leave SETUP_FINALLY and RERAISE in place to help find block limits. */
|
||||
if (_Py_OPCODE(codestr[h]) == SETUP_FINALLY || _Py_OPCODE(codestr[h]) == RERAISE) {
|
||||
while (h > i + 1 &&
|
||||
_Py_OPCODE(codestr[h - 1]) == EXTENDED_ARG)
|
||||
{
|
||||
|
@ -506,7 +501,6 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
|
|||
case SETUP_FINALLY:
|
||||
case SETUP_WITH:
|
||||
case SETUP_ASYNC_WITH:
|
||||
case CALL_FINALLY:
|
||||
j = blocks[j / sizeof(_Py_CODEUNIT) + i + 1] - blocks[i] - 1;
|
||||
j *= sizeof(_Py_CODEUNIT);
|
||||
break;
|
||||
|
|
Loading…
Reference in New Issue