mirror of
https://github.com/python/cpython.git
synced 2025-08-31 05:58:33 +00:00
bpo-17611. Move unwinding of stack for "pseudo exceptions" from interpreter to compiler. (GH-5006)
Co-authored-by: Mark Shannon <mark@hotpy.org> Co-authored-by: Antoine Pitrou <antoine@python.org>
This commit is contained in:
parent
4af8fd5614
commit
520b7ae27e
19 changed files with 4506 additions and 4392 deletions
369
Python/ceval.c
369
Python/ceval.c
|
@ -500,17 +500,6 @@ _Py_CheckRecursiveCall(const char *where)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Status code for main loop (reason for stack unwind) */
|
||||
enum why_code {
|
||||
WHY_NOT = 0x0001, /* No error */
|
||||
WHY_EXCEPTION = 0x0002, /* Exception occurred */
|
||||
WHY_RETURN = 0x0008, /* 'return' statement */
|
||||
WHY_BREAK = 0x0010, /* 'break' statement */
|
||||
WHY_CONTINUE = 0x0020, /* 'continue' statement */
|
||||
WHY_YIELD = 0x0040, /* 'yield' operator */
|
||||
WHY_SILENCED = 0x0080 /* Exception silenced by 'with' */
|
||||
};
|
||||
|
||||
static int do_raise(PyObject *, PyObject *);
|
||||
static int unpack_iterable(PyObject *, int, int, PyObject **);
|
||||
|
||||
|
@ -556,7 +545,6 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
|
|||
const _Py_CODEUNIT *next_instr;
|
||||
int opcode; /* Current opcode */
|
||||
int oparg; /* Current opcode argument, if any */
|
||||
enum why_code why; /* Reason for block stack unwind */
|
||||
PyObject **fastlocals, **freevars;
|
||||
PyObject *retval = NULL; /* Return value */
|
||||
PyThreadState *tstate = PyThreadState_GET();
|
||||
|
@ -914,8 +902,6 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
|
|||
lltrace = _PyDict_GetItemId(f->f_globals, &PyId___ltrace__) != NULL;
|
||||
#endif
|
||||
|
||||
why = WHY_NOT;
|
||||
|
||||
if (throwflag) /* support for generator.throw() */
|
||||
goto error;
|
||||
|
||||
|
@ -926,6 +912,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
|
|||
assert(!PyErr_Occurred());
|
||||
#endif
|
||||
|
||||
main_loop:
|
||||
for (;;) {
|
||||
assert(stack_pointer >= f->f_valuestack); /* else underflow */
|
||||
assert(STACK_LEVEL() <= co->co_stacksize); /* else overflow */
|
||||
|
@ -1056,9 +1043,8 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
|
|||
switch (opcode) {
|
||||
|
||||
/* BEWARE!
|
||||
It is essential that any operation that fails sets either
|
||||
x to NULL, err to nonzero, or why to anything but WHY_NOT,
|
||||
and that no operation that succeeds does this! */
|
||||
It is essential that any operation that fails must goto error
|
||||
and that all operation that succeed call [FAST_]DISPATCH() ! */
|
||||
|
||||
TARGET(NOP)
|
||||
FAST_DISPATCH();
|
||||
|
@ -1115,6 +1101,18 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
|
|||
FAST_DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(ROT_FOUR) {
|
||||
PyObject *top = TOP();
|
||||
PyObject *second = SECOND();
|
||||
PyObject *third = THIRD();
|
||||
PyObject *fourth = FOURTH();
|
||||
SET_TOP(second);
|
||||
SET_SECOND(third);
|
||||
SET_THIRD(fourth);
|
||||
SET_FOURTH(top);
|
||||
FAST_DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(DUP_TOP) {
|
||||
PyObject *top = TOP();
|
||||
Py_INCREF(top);
|
||||
|
@ -1618,8 +1616,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
|
|||
/* fall through */
|
||||
case 0:
|
||||
if (do_raise(exc, cause)) {
|
||||
why = WHY_EXCEPTION;
|
||||
goto fast_block_end;
|
||||
goto exception_unwind;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -1632,8 +1629,8 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
|
|||
|
||||
TARGET(RETURN_VALUE) {
|
||||
retval = POP();
|
||||
why = WHY_RETURN;
|
||||
goto fast_block_end;
|
||||
assert(f->f_iblock == 0);
|
||||
goto return_or_yield;
|
||||
}
|
||||
|
||||
TARGET(GET_AITER) {
|
||||
|
@ -1794,11 +1791,10 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
|
|||
}
|
||||
/* receiver remains on stack, retval is value to be yielded */
|
||||
f->f_stacktop = stack_pointer;
|
||||
why = WHY_YIELD;
|
||||
/* and repeat... */
|
||||
assert(f->f_lasti >= (int)sizeof(_Py_CODEUNIT));
|
||||
f->f_lasti -= sizeof(_Py_CODEUNIT);
|
||||
goto fast_yield;
|
||||
goto return_or_yield;
|
||||
}
|
||||
|
||||
TARGET(YIELD_VALUE) {
|
||||
|
@ -1815,67 +1811,137 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
|
|||
}
|
||||
|
||||
f->f_stacktop = stack_pointer;
|
||||
why = WHY_YIELD;
|
||||
goto fast_yield;
|
||||
goto return_or_yield;
|
||||
}
|
||||
|
||||
TARGET(POP_EXCEPT) {
|
||||
PyObject *type, *value, *traceback;
|
||||
_PyErr_StackItem *exc_info;
|
||||
PyTryBlock *b = PyFrame_BlockPop(f);
|
||||
if (b->b_type != EXCEPT_HANDLER) {
|
||||
PyErr_SetString(PyExc_SystemError,
|
||||
"popped block is not an except handler");
|
||||
goto error;
|
||||
}
|
||||
UNWIND_EXCEPT_HANDLER(b);
|
||||
assert(STACK_LEVEL() >= (b)->b_level + 3 &&
|
||||
STACK_LEVEL() <= (b)->b_level + 4);
|
||||
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);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
PREDICTED(POP_BLOCK);
|
||||
TARGET(POP_BLOCK) {
|
||||
PyTryBlock *b = PyFrame_BlockPop(f);
|
||||
UNWIND_BLOCK(b);
|
||||
PyFrame_BlockPop(f);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
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(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();
|
||||
}
|
||||
|
||||
TARGET(CALL_FINALLY) {
|
||||
PyObject *ret = PyLong_FromLong(INSTR_OFFSET());
|
||||
if (ret == NULL) {
|
||||
goto error;
|
||||
}
|
||||
PUSH(ret);
|
||||
JUMPBY(oparg);
|
||||
FAST_DISPATCH();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
PREDICTED(END_FINALLY);
|
||||
TARGET(END_FINALLY) {
|
||||
PyObject *status = POP();
|
||||
if (PyLong_Check(status)) {
|
||||
why = (enum why_code) PyLong_AS_LONG(status);
|
||||
assert(why != WHY_YIELD && why != WHY_EXCEPTION);
|
||||
if (why == WHY_RETURN ||
|
||||
why == WHY_CONTINUE)
|
||||
retval = POP();
|
||||
if (why == WHY_SILENCED) {
|
||||
/* An exception was silenced by 'with', 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);
|
||||
why = WHY_NOT;
|
||||
Py_DECREF(status);
|
||||
DISPATCH();
|
||||
/* 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()) {
|
||||
goto error;
|
||||
}
|
||||
Py_DECREF(status);
|
||||
goto fast_block_end;
|
||||
JUMPTO(ret);
|
||||
FAST_DISPATCH();
|
||||
}
|
||||
else if (PyExceptionClass_Check(status)) {
|
||||
PyObject *exc = POP();
|
||||
else {
|
||||
assert(PyExceptionClass_Check(exc));
|
||||
PyObject *val = POP();
|
||||
PyObject *tb = POP();
|
||||
PyErr_Restore(status, exc, tb);
|
||||
why = WHY_EXCEPTION;
|
||||
goto fast_block_end;
|
||||
PyErr_Restore(exc, val, tb);
|
||||
goto exception_unwind;
|
||||
}
|
||||
else if (status != Py_None) {
|
||||
PyErr_SetString(PyExc_SystemError,
|
||||
"'finally' pops bad exception");
|
||||
Py_DECREF(status);
|
||||
goto error;
|
||||
}
|
||||
Py_DECREF(status);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(LOAD_BUILD_CLASS) {
|
||||
|
@ -2815,28 +2881,13 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
|
|||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(BREAK_LOOP) {
|
||||
why = WHY_BREAK;
|
||||
goto fast_block_end;
|
||||
}
|
||||
|
||||
TARGET(CONTINUE_LOOP) {
|
||||
retval = PyLong_FromLong(oparg);
|
||||
if (retval == NULL)
|
||||
goto error;
|
||||
why = WHY_CONTINUE;
|
||||
goto fast_block_end;
|
||||
}
|
||||
|
||||
TARGET(SETUP_LOOP)
|
||||
TARGET(SETUP_EXCEPT)
|
||||
TARGET(SETUP_FINALLY) {
|
||||
/* NOTE: If you add any new block-setup opcodes that
|
||||
are not try/except/finally handlers, you may need
|
||||
to update the PyGen_NeedsFinalizing() function.
|
||||
*/
|
||||
|
||||
PyFrame_BlockSetup(f, opcode, INSTR_OFFSET() + oparg,
|
||||
PyFrame_BlockSetup(f, SETUP_FINALLY, INSTR_OFFSET() + oparg,
|
||||
STACK_LEVEL());
|
||||
DISPATCH();
|
||||
}
|
||||
|
@ -2904,60 +2955,40 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
|
|||
}
|
||||
|
||||
TARGET(WITH_CLEANUP_START) {
|
||||
/* At the top of the stack are 1-6 values indicating
|
||||
/* At the top of the stack are 1 or 6 values indicating
|
||||
how/why we entered the finally clause:
|
||||
- TOP = None
|
||||
- (TOP, SECOND) = (WHY_{RETURN,CONTINUE}), retval
|
||||
- TOP = WHY_*; no retval below it
|
||||
- TOP = NULL
|
||||
- (TOP, SECOND, THIRD) = exc_info()
|
||||
(FOURTH, FITH, SIXTH) = previous exception for EXCEPT_HANDLER
|
||||
Below them is EXIT, the context.__exit__ bound method.
|
||||
In the last case, we must call
|
||||
EXIT(TOP, SECOND, THIRD)
|
||||
otherwise we must call
|
||||
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 three cases, we remove EXIT from the
|
||||
stack, leaving the rest in the same order. In the
|
||||
fourth case, we shift the bottom 3 values of the
|
||||
stack down, and replace the empty spot with NULL.
|
||||
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.
|
||||
|
||||
In addition, if the stack represents an exception,
|
||||
*and* the function call returns a 'true' value, we
|
||||
push WHY_SILENCED onto the stack. END_FINALLY will
|
||||
then not re-raise the exception. (But non-local
|
||||
gotos should still be resumed.)
|
||||
Finally we push the result of the call.
|
||||
*/
|
||||
|
||||
PyObject* stack[3];
|
||||
PyObject *stack[3];
|
||||
PyObject *exit_func;
|
||||
PyObject *exc, *val, *tb, *res;
|
||||
|
||||
val = tb = Py_None;
|
||||
exc = TOP();
|
||||
if (exc == Py_None) {
|
||||
(void)POP();
|
||||
if (exc == NULL) {
|
||||
STACKADJ(-1);
|
||||
exit_func = TOP();
|
||||
SET_TOP(exc);
|
||||
}
|
||||
else if (PyLong_Check(exc)) {
|
||||
STACKADJ(-1);
|
||||
switch (PyLong_AsLong(exc)) {
|
||||
case WHY_RETURN:
|
||||
case WHY_CONTINUE:
|
||||
/* Retval in TOP. */
|
||||
exit_func = SECOND();
|
||||
SET_SECOND(TOP());
|
||||
SET_TOP(exc);
|
||||
break;
|
||||
default:
|
||||
exit_func = TOP();
|
||||
SET_TOP(exc);
|
||||
break;
|
||||
}
|
||||
exc = Py_None;
|
||||
}
|
||||
else {
|
||||
assert(PyExceptionClass_Check(exc));
|
||||
PyObject *tp2, *exc2, *tb2;
|
||||
PyTryBlock *block;
|
||||
val = SECOND();
|
||||
|
@ -2974,8 +3005,10 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
|
|||
/* 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--;
|
||||
}
|
||||
|
||||
|
@ -2996,6 +3029,12 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
|
|||
|
||||
PREDICTED(WITH_CLEANUP_FINISH);
|
||||
TARGET(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;
|
||||
|
@ -3011,8 +3050,15 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
|
|||
if (err < 0)
|
||||
goto error;
|
||||
else if (err > 0) {
|
||||
/* There was an exception and a True return */
|
||||
PUSH(PyLong_FromLong((long) WHY_SILENCED));
|
||||
/* 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 inconsisten state.
|
||||
*/
|
||||
PyTryBlock *b = PyFrame_BlockPop(f);
|
||||
assert(b->b_type == EXCEPT_HANDLER);
|
||||
UNWIND_EXCEPT_HANDLER(b);
|
||||
PUSH(NULL);
|
||||
}
|
||||
PREDICT(END_FINALLY);
|
||||
DISPATCH();
|
||||
|
@ -3322,10 +3368,6 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
|
|||
Py_UNREACHABLE();
|
||||
|
||||
error:
|
||||
|
||||
assert(why == WHY_NOT);
|
||||
why = WHY_EXCEPTION;
|
||||
|
||||
/* Double-check exception status. */
|
||||
#ifdef NDEBUG
|
||||
if (!PyErr_Occurred())
|
||||
|
@ -3342,36 +3384,18 @@ error:
|
|||
call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj,
|
||||
tstate, f);
|
||||
|
||||
fast_block_end:
|
||||
assert(why != WHY_NOT);
|
||||
|
||||
/* Unwind stacks if a (pseudo) exception occurred */
|
||||
while (why != WHY_NOT && f->f_iblock > 0) {
|
||||
/* Peek at the current block. */
|
||||
PyTryBlock *b = &f->f_blockstack[f->f_iblock - 1];
|
||||
|
||||
assert(why != WHY_YIELD);
|
||||
if (b->b_type == SETUP_LOOP && why == WHY_CONTINUE) {
|
||||
why = WHY_NOT;
|
||||
JUMPTO(PyLong_AS_LONG(retval));
|
||||
Py_DECREF(retval);
|
||||
break;
|
||||
}
|
||||
/* Now we have to pop the block. */
|
||||
f->f_iblock--;
|
||||
exception_unwind:
|
||||
/* Unwind stacks if an exception occurred */
|
||||
while (f->f_iblock > 0) {
|
||||
/* Pop the current block. */
|
||||
PyTryBlock *b = &f->f_blockstack[--f->f_iblock];
|
||||
|
||||
if (b->b_type == EXCEPT_HANDLER) {
|
||||
UNWIND_EXCEPT_HANDLER(b);
|
||||
continue;
|
||||
}
|
||||
UNWIND_BLOCK(b);
|
||||
if (b->b_type == SETUP_LOOP && why == WHY_BREAK) {
|
||||
why = WHY_NOT;
|
||||
JUMPTO(b->b_handler);
|
||||
break;
|
||||
}
|
||||
if (why == WHY_EXCEPTION && (b->b_type == SETUP_EXCEPT
|
||||
|| b->b_type == SETUP_FINALLY)) {
|
||||
if (b->b_type == SETUP_FINALLY) {
|
||||
PyObject *exc, *val, *tb;
|
||||
int handler = b->b_handler;
|
||||
_PyErr_StackItem *exc_info = tstate->exc_info;
|
||||
|
@ -3408,70 +3432,37 @@ fast_block_end:
|
|||
PUSH(tb);
|
||||
PUSH(val);
|
||||
PUSH(exc);
|
||||
why = WHY_NOT;
|
||||
JUMPTO(handler);
|
||||
break;
|
||||
}
|
||||
if (b->b_type == SETUP_FINALLY) {
|
||||
if (why & (WHY_RETURN | WHY_CONTINUE))
|
||||
PUSH(retval);
|
||||
PUSH(PyLong_FromLong((long)why));
|
||||
why = WHY_NOT;
|
||||
JUMPTO(b->b_handler);
|
||||
break;
|
||||
/* Resume normal execution */
|
||||
goto main_loop;
|
||||
}
|
||||
} /* unwind stack */
|
||||
|
||||
/* End the loop if we still have an error (or return) */
|
||||
|
||||
if (why != WHY_NOT)
|
||||
break;
|
||||
|
||||
assert(!PyErr_Occurred());
|
||||
|
||||
/* End the loop as we still have an error */
|
||||
break;
|
||||
} /* main loop */
|
||||
|
||||
assert(why != WHY_YIELD);
|
||||
/* Pop remaining stack entries. */
|
||||
while (!EMPTY()) {
|
||||
PyObject *o = POP();
|
||||
Py_XDECREF(o);
|
||||
}
|
||||
|
||||
if (why != WHY_RETURN)
|
||||
retval = NULL;
|
||||
|
||||
assert((retval != NULL) ^ (PyErr_Occurred() != NULL));
|
||||
|
||||
fast_yield:
|
||||
assert(retval == NULL);
|
||||
assert(PyErr_Occurred());
|
||||
|
||||
return_or_yield:
|
||||
if (tstate->use_tracing) {
|
||||
if (tstate->c_tracefunc) {
|
||||
if (why == WHY_RETURN || why == WHY_YIELD) {
|
||||
if (call_trace(tstate->c_tracefunc, tstate->c_traceobj,
|
||||
tstate, f,
|
||||
PyTrace_RETURN, retval)) {
|
||||
Py_CLEAR(retval);
|
||||
why = WHY_EXCEPTION;
|
||||
}
|
||||
}
|
||||
else if (why == WHY_EXCEPTION) {
|
||||
call_trace_protected(tstate->c_tracefunc, tstate->c_traceobj,
|
||||
tstate, f,
|
||||
PyTrace_RETURN, NULL);
|
||||
if (call_trace_protected(tstate->c_tracefunc, tstate->c_traceobj,
|
||||
tstate, f, PyTrace_RETURN, retval)) {
|
||||
Py_CLEAR(retval);
|
||||
}
|
||||
}
|
||||
if (tstate->c_profilefunc) {
|
||||
if (why == WHY_EXCEPTION)
|
||||
call_trace_protected(tstate->c_profilefunc,
|
||||
tstate->c_profileobj,
|
||||
tstate, f,
|
||||
PyTrace_RETURN, NULL);
|
||||
else if (call_trace(tstate->c_profilefunc, tstate->c_profileobj,
|
||||
tstate, f,
|
||||
PyTrace_RETURN, retval)) {
|
||||
if (call_trace_protected(tstate->c_profilefunc, tstate->c_profileobj,
|
||||
tstate, f, PyTrace_RETURN, retval)) {
|
||||
Py_CLEAR(retval);
|
||||
/* why = WHY_EXCEPTION; useless yet but cause compiler warnings */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
411
Python/compile.c
411
Python/compile.c
|
@ -81,11 +81,14 @@ It's called a frame block to distinguish it from a basic block in the
|
|||
compiler IR.
|
||||
*/
|
||||
|
||||
enum fblocktype { LOOP, EXCEPT, FINALLY_TRY, FINALLY_END };
|
||||
enum fblocktype { WHILE_LOOP, FOR_LOOP, EXCEPT, FINALLY_TRY, FINALLY_END,
|
||||
WITH, ASYNC_WITH, HANDLER_CLEANUP };
|
||||
|
||||
struct fblockinfo {
|
||||
enum fblocktype fb_type;
|
||||
basicblock *fb_block;
|
||||
/* (optional) type-specific exit or cleanup block */
|
||||
basicblock *fb_exit;
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -183,13 +186,6 @@ static int compiler_annassign(struct compiler *, stmt_ty);
|
|||
static int compiler_visit_slice(struct compiler *, slice_ty,
|
||||
expr_context_ty);
|
||||
|
||||
static int compiler_push_fblock(struct compiler *, enum fblocktype,
|
||||
basicblock *);
|
||||
static void compiler_pop_fblock(struct compiler *, enum fblocktype,
|
||||
basicblock *);
|
||||
/* Returns true if there is a loop on the fblock stack. */
|
||||
static int compiler_in_loop(struct compiler *);
|
||||
|
||||
static int inplace_binop(struct compiler *, operator_ty);
|
||||
static int expr_constant(expr_ty);
|
||||
|
||||
|
@ -846,7 +842,7 @@ compiler_next_instr(struct compiler *c, basicblock *b)
|
|||
- when entering a new scope
|
||||
- on each statement
|
||||
- on each expression that start a new line
|
||||
- before the "except" clause
|
||||
- before the "except" and "finally" clauses
|
||||
- before the "for" and "while" expressions
|
||||
*/
|
||||
|
||||
|
@ -881,6 +877,7 @@ stack_effect(int opcode, int oparg, int jump)
|
|||
return -1;
|
||||
case ROT_TWO:
|
||||
case ROT_THREE:
|
||||
case ROT_FOUR:
|
||||
return 0;
|
||||
case DUP_TOP:
|
||||
return 1;
|
||||
|
@ -947,8 +944,7 @@ stack_effect(int opcode, int oparg, int jump)
|
|||
case INPLACE_XOR:
|
||||
case INPLACE_OR:
|
||||
return -1;
|
||||
case BREAK_LOOP:
|
||||
return 0;
|
||||
|
||||
case SETUP_WITH:
|
||||
/* 1 in the normal flow.
|
||||
* Restore the stack position and push 6 values before jumping to
|
||||
|
@ -975,6 +971,7 @@ stack_effect(int opcode, int oparg, int jump)
|
|||
case POP_EXCEPT:
|
||||
return -3;
|
||||
case END_FINALLY:
|
||||
case POP_FINALLY:
|
||||
/* Pop 6 values when an exception was raised. */
|
||||
return -6;
|
||||
|
||||
|
@ -1043,16 +1040,20 @@ stack_effect(int opcode, int oparg, int jump)
|
|||
case LOAD_GLOBAL:
|
||||
return 1;
|
||||
|
||||
case CONTINUE_LOOP:
|
||||
return 0;
|
||||
case SETUP_LOOP:
|
||||
return 0;
|
||||
case SETUP_EXCEPT:
|
||||
/* Exception handling */
|
||||
case SETUP_FINALLY:
|
||||
/* 0 in the normal flow.
|
||||
* 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 LOAD_FAST:
|
||||
return 1;
|
||||
|
@ -1458,6 +1459,103 @@ find_ann(asdl_seq *stmts)
|
|||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Frame block handling functions
|
||||
*/
|
||||
|
||||
static int
|
||||
compiler_push_fblock(struct compiler *c, enum fblocktype t, basicblock *b,
|
||||
basicblock *exit)
|
||||
{
|
||||
struct fblockinfo *f;
|
||||
if (c->u->u_nfblocks >= CO_MAXBLOCKS) {
|
||||
PyErr_SetString(PyExc_SyntaxError,
|
||||
"too many statically nested blocks");
|
||||
return 0;
|
||||
}
|
||||
f = &c->u->u_fblock[c->u->u_nfblocks++];
|
||||
f->fb_type = t;
|
||||
f->fb_block = b;
|
||||
f->fb_exit = exit;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
compiler_pop_fblock(struct compiler *c, enum fblocktype t, basicblock *b)
|
||||
{
|
||||
struct compiler_unit *u = c->u;
|
||||
assert(u->u_nfblocks > 0);
|
||||
u->u_nfblocks--;
|
||||
assert(u->u_fblock[u->u_nfblocks].fb_type == t);
|
||||
assert(u->u_fblock[u->u_nfblocks].fb_block == b);
|
||||
}
|
||||
|
||||
/* Unwind a frame block. If preserve_tos is true, the TOS before
|
||||
* popping the blocks will be restored afterwards.
|
||||
*/
|
||||
static int
|
||||
compiler_unwind_fblock(struct compiler *c, struct fblockinfo *info,
|
||||
int preserve_tos)
|
||||
{
|
||||
switch (info->fb_type) {
|
||||
case WHILE_LOOP:
|
||||
return 1;
|
||||
|
||||
case FINALLY_END:
|
||||
ADDOP_I(c, POP_FINALLY, preserve_tos);
|
||||
return 1;
|
||||
|
||||
case FOR_LOOP:
|
||||
/* Pop the iterator */
|
||||
if (preserve_tos) {
|
||||
ADDOP(c, ROT_TWO);
|
||||
}
|
||||
ADDOP(c, POP_TOP);
|
||||
return 1;
|
||||
|
||||
case EXCEPT:
|
||||
ADDOP(c, POP_BLOCK);
|
||||
return 1;
|
||||
|
||||
case FINALLY_TRY:
|
||||
ADDOP(c, POP_BLOCK);
|
||||
ADDOP_JREL(c, CALL_FINALLY, info->fb_exit);
|
||||
return 1;
|
||||
|
||||
case WITH:
|
||||
case ASYNC_WITH:
|
||||
ADDOP(c, POP_BLOCK);
|
||||
if (preserve_tos) {
|
||||
ADDOP(c, ROT_TWO);
|
||||
}
|
||||
ADDOP(c, BEGIN_FINALLY);
|
||||
ADDOP(c, WITH_CLEANUP_START);
|
||||
if (info->fb_type == ASYNC_WITH) {
|
||||
ADDOP(c, GET_AWAITABLE);
|
||||
ADDOP_O(c, LOAD_CONST, Py_None, consts);
|
||||
ADDOP(c, YIELD_FROM);
|
||||
}
|
||||
ADDOP(c, WITH_CLEANUP_FINISH);
|
||||
ADDOP_I(c, POP_FINALLY, 0);
|
||||
return 1;
|
||||
|
||||
case HANDLER_CLEANUP:
|
||||
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);
|
||||
}
|
||||
else {
|
||||
ADDOP(c, POP_EXCEPT);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
Py_UNREACHABLE();
|
||||
}
|
||||
|
||||
/* Compile a sequence of statements, checking for a docstring
|
||||
and for annotations. */
|
||||
|
||||
|
@ -2312,9 +2410,10 @@ compiler_for(struct compiler *c, stmt_ty s)
|
|||
end = compiler_new_block(c);
|
||||
if (start == NULL || end == NULL || cleanup == NULL)
|
||||
return 0;
|
||||
ADDOP_JREL(c, SETUP_LOOP, end);
|
||||
if (!compiler_push_fblock(c, LOOP, start))
|
||||
|
||||
if (!compiler_push_fblock(c, FOR_LOOP, start, end))
|
||||
return 0;
|
||||
|
||||
VISIT(c, expr, s->v.For.iter);
|
||||
ADDOP(c, GET_ITER);
|
||||
compiler_use_next_block(c, start);
|
||||
|
@ -2323,8 +2422,9 @@ compiler_for(struct compiler *c, stmt_ty s)
|
|||
VISIT_SEQ(c, stmt, s->v.For.body);
|
||||
ADDOP_JABS(c, JUMP_ABSOLUTE, start);
|
||||
compiler_use_next_block(c, cleanup);
|
||||
ADDOP(c, POP_BLOCK);
|
||||
compiler_pop_fblock(c, LOOP, start);
|
||||
|
||||
compiler_pop_fblock(c, FOR_LOOP, start);
|
||||
|
||||
VISIT_SEQ(c, stmt, s->v.For.orelse);
|
||||
compiler_use_next_block(c, end);
|
||||
return 1;
|
||||
|
@ -2356,8 +2456,7 @@ compiler_async_for(struct compiler *c, stmt_ty s)
|
|||
|| after_try == NULL || try_cleanup == NULL)
|
||||
return 0;
|
||||
|
||||
ADDOP_JREL(c, SETUP_LOOP, after_loop);
|
||||
if (!compiler_push_fblock(c, LOOP, try))
|
||||
if (!compiler_push_fblock(c, FOR_LOOP, try, after_loop))
|
||||
return 0;
|
||||
|
||||
VISIT(c, expr, s->v.AsyncFor.iter);
|
||||
|
@ -2366,19 +2465,21 @@ compiler_async_for(struct compiler *c, stmt_ty s)
|
|||
compiler_use_next_block(c, try);
|
||||
|
||||
|
||||
ADDOP_JREL(c, SETUP_EXCEPT, except);
|
||||
if (!compiler_push_fblock(c, EXCEPT, try))
|
||||
/* SETUP_FINALLY to guard the __anext__ call */
|
||||
ADDOP_JREL(c, SETUP_FINALLY, except);
|
||||
if (!compiler_push_fblock(c, EXCEPT, try, NULL))
|
||||
return 0;
|
||||
|
||||
ADDOP(c, GET_ANEXT);
|
||||
ADDOP_O(c, LOAD_CONST, Py_None, consts);
|
||||
ADDOP(c, YIELD_FROM);
|
||||
VISIT(c, expr, s->v.AsyncFor.target);
|
||||
ADDOP(c, POP_BLOCK);
|
||||
ADDOP(c, POP_BLOCK); /* for SETUP_FINALLY */
|
||||
compiler_pop_fblock(c, EXCEPT, try);
|
||||
ADDOP_JREL(c, JUMP_FORWARD, after_try);
|
||||
|
||||
|
||||
/* Except block for __anext__ */
|
||||
compiler_use_next_block(c, except);
|
||||
ADDOP(c, DUP_TOP);
|
||||
ADDOP_O(c, LOAD_GLOBAL, stop_aiter_error, names);
|
||||
|
@ -2388,25 +2489,26 @@ compiler_async_for(struct compiler *c, stmt_ty s)
|
|||
ADDOP(c, POP_TOP);
|
||||
ADDOP(c, POP_TOP);
|
||||
ADDOP(c, POP_TOP);
|
||||
ADDOP(c, POP_EXCEPT); /* for SETUP_EXCEPT */
|
||||
ADDOP(c, POP_TOP); /* for correct calculation of stack effect */
|
||||
ADDOP(c, POP_BLOCK); /* for SETUP_LOOP */
|
||||
ADDOP(c, POP_EXCEPT); /* for SETUP_FINALLY */
|
||||
ADDOP(c, POP_TOP); /* pop iterator from stack */
|
||||
ADDOP_JABS(c, JUMP_ABSOLUTE, after_loop_else);
|
||||
|
||||
|
||||
compiler_use_next_block(c, try_cleanup);
|
||||
ADDOP(c, END_FINALLY);
|
||||
|
||||
/* Success block for __anext__ */
|
||||
compiler_use_next_block(c, after_try);
|
||||
VISIT_SEQ(c, stmt, s->v.AsyncFor.body);
|
||||
ADDOP_JABS(c, JUMP_ABSOLUTE, try);
|
||||
|
||||
ADDOP(c, POP_BLOCK); /* for SETUP_LOOP */
|
||||
compiler_pop_fblock(c, LOOP, try);
|
||||
compiler_pop_fblock(c, FOR_LOOP, try);
|
||||
|
||||
/* Block reached after `break`ing from loop */
|
||||
compiler_use_next_block(c, after_loop);
|
||||
ADDOP_JABS(c, JUMP_ABSOLUTE, end);
|
||||
|
||||
/* `else` block */
|
||||
compiler_use_next_block(c, after_loop_else);
|
||||
VISIT_SEQ(c, stmt, s->v.For.orelse);
|
||||
|
||||
|
@ -2443,9 +2545,8 @@ compiler_while(struct compiler *c, stmt_ty s)
|
|||
else
|
||||
orelse = NULL;
|
||||
|
||||
ADDOP_JREL(c, SETUP_LOOP, end);
|
||||
compiler_use_next_block(c, loop);
|
||||
if (!compiler_push_fblock(c, LOOP, loop))
|
||||
if (!compiler_push_fblock(c, WHILE_LOOP, loop, end))
|
||||
return 0;
|
||||
if (constant == -1) {
|
||||
if (!compiler_jump_if(c, s->v.While.test, anchor, 0))
|
||||
|
@ -2460,8 +2561,8 @@ compiler_while(struct compiler *c, stmt_ty s)
|
|||
|
||||
if (constant == -1)
|
||||
compiler_use_next_block(c, anchor);
|
||||
ADDOP(c, POP_BLOCK);
|
||||
compiler_pop_fblock(c, LOOP, loop);
|
||||
compiler_pop_fblock(c, WHILE_LOOP, loop);
|
||||
|
||||
if (orelse != NULL) /* what if orelse is just pass? */
|
||||
VISIT_SEQ(c, stmt, s->v.While.orelse);
|
||||
compiler_use_next_block(c, end);
|
||||
|
@ -2470,46 +2571,83 @@ compiler_while(struct compiler *c, stmt_ty s)
|
|||
}
|
||||
|
||||
static int
|
||||
compiler_continue(struct compiler *c)
|
||||
compiler_return(struct compiler *c, stmt_ty s)
|
||||
{
|
||||
static const char LOOP_ERROR_MSG[] = "'continue' not properly in loop";
|
||||
static const char IN_FINALLY_ERROR_MSG[] =
|
||||
"'continue' not supported inside 'finally' clause";
|
||||
int i;
|
||||
|
||||
if (!c->u->u_nfblocks)
|
||||
return compiler_error(c, LOOP_ERROR_MSG);
|
||||
i = c->u->u_nfblocks - 1;
|
||||
switch (c->u->u_fblock[i].fb_type) {
|
||||
case LOOP:
|
||||
ADDOP_JABS(c, JUMP_ABSOLUTE, c->u->u_fblock[i].fb_block);
|
||||
break;
|
||||
case EXCEPT:
|
||||
case FINALLY_TRY:
|
||||
while (--i >= 0 && c->u->u_fblock[i].fb_type != LOOP) {
|
||||
/* Prevent continue anywhere under a finally
|
||||
even if hidden in a sub-try or except. */
|
||||
if (c->u->u_fblock[i].fb_type == FINALLY_END)
|
||||
return compiler_error(c, IN_FINALLY_ERROR_MSG);
|
||||
}
|
||||
if (i == -1)
|
||||
return compiler_error(c, LOOP_ERROR_MSG);
|
||||
ADDOP_JABS(c, CONTINUE_LOOP, c->u->u_fblock[i].fb_block);
|
||||
break;
|
||||
case FINALLY_END:
|
||||
return compiler_error(c, IN_FINALLY_ERROR_MSG);
|
||||
int preserve_tos = ((s->v.Return.value != NULL) &&
|
||||
!is_const(s->v.Return.value));
|
||||
if (c->u->u_ste->ste_type != FunctionBlock)
|
||||
return compiler_error(c, "'return' outside function");
|
||||
if (s->v.Return.value != NULL &&
|
||||
c->u->u_ste->ste_coroutine && c->u->u_ste->ste_generator)
|
||||
{
|
||||
return compiler_error(
|
||||
c, "'return' with value in async generator");
|
||||
}
|
||||
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))
|
||||
return 0;
|
||||
}
|
||||
if (s->v.Return.value == NULL) {
|
||||
ADDOP_O(c, LOAD_CONST, Py_None, consts);
|
||||
}
|
||||
else if (!preserve_tos) {
|
||||
VISIT(c, expr, s->v.Return.value);
|
||||
}
|
||||
ADDOP(c, RETURN_VALUE);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
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))
|
||||
return 0;
|
||||
if (info->fb_type == WHILE_LOOP || info->fb_type == FOR_LOOP) {
|
||||
ADDOP_JABS(c, JUMP_ABSOLUTE, info->fb_exit);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return compiler_error(c, "'break' outside loop");
|
||||
}
|
||||
|
||||
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 (info->fb_type == FINALLY_END) {
|
||||
return compiler_error(c,
|
||||
"'continue' not supported inside 'finally' clause");
|
||||
}
|
||||
if (!compiler_unwind_fblock(c, info, 0))
|
||||
return 0;
|
||||
}
|
||||
return compiler_error(c, "'continue' not properly in loop");
|
||||
}
|
||||
|
||||
|
||||
/* Code generated for "try: <body> finally: <finalbody>" is as follows:
|
||||
|
||||
SETUP_FINALLY L
|
||||
<code for body>
|
||||
POP_BLOCK
|
||||
LOAD_CONST <None>
|
||||
L: <code for finalbody>
|
||||
BEGIN_FINALLY
|
||||
L:
|
||||
<code for finalbody>
|
||||
END_FINALLY
|
||||
|
||||
The special instructions use the block stack. Each block
|
||||
|
@ -2521,33 +2659,34 @@ compiler_continue(struct compiler *c)
|
|||
Pushes the current value stack level and the label
|
||||
onto the block stack.
|
||||
POP_BLOCK:
|
||||
Pops en entry from the block stack, and pops the value
|
||||
stack until its level is the same as indicated on the
|
||||
block stack. (The label is ignored.)
|
||||
Pops en entry from the block stack.
|
||||
BEGIN_FINALLY
|
||||
Pushes NULL onto the value stack.
|
||||
END_FINALLY:
|
||||
Pops a variable number of entries from the *value* stack
|
||||
and re-raises the exception they specify. The number of
|
||||
entries popped depends on the (pseudo) exception type.
|
||||
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 exception is pushed
|
||||
onto the value stack (and the exception condition is cleared),
|
||||
and the interpreter jumps to the label gotten from the block
|
||||
stack.
|
||||
when a SETUP_FINALLY entry is found, the raised and the caught
|
||||
exceptions are pushed onto the value stack (and the exception
|
||||
condition is cleared), and the interpreter jumps to the label
|
||||
gotten from the block stack.
|
||||
*/
|
||||
|
||||
static int
|
||||
compiler_try_finally(struct compiler *c, stmt_ty s)
|
||||
{
|
||||
basicblock *body, *end;
|
||||
|
||||
body = compiler_new_block(c);
|
||||
end = compiler_new_block(c);
|
||||
if (body == NULL || end == NULL)
|
||||
return 0;
|
||||
|
||||
/* `try` block */
|
||||
ADDOP_JREL(c, SETUP_FINALLY, end);
|
||||
compiler_use_next_block(c, body);
|
||||
if (!compiler_push_fblock(c, FINALLY_TRY, body))
|
||||
if (!compiler_push_fblock(c, FINALLY_TRY, body, end))
|
||||
return 0;
|
||||
if (s->v.Try.handlers && asdl_seq_LEN(s->v.Try.handlers)) {
|
||||
if (!compiler_try_except(c, s))
|
||||
|
@ -2557,16 +2696,16 @@ 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, FINALLY_TRY, body);
|
||||
|
||||
ADDOP_O(c, LOAD_CONST, Py_None, consts);
|
||||
/* `finally` block */
|
||||
compiler_use_next_block(c, end);
|
||||
if (!compiler_push_fblock(c, FINALLY_END, end))
|
||||
if (!compiler_push_fblock(c, FINALLY_END, end, NULL))
|
||||
return 0;
|
||||
VISIT_SEQ(c, stmt, s->v.Try.finalbody);
|
||||
ADDOP(c, END_FINALLY);
|
||||
compiler_pop_fblock(c, FINALLY_END, end);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -2577,7 +2716,7 @@ compiler_try_finally(struct compiler *c, stmt_ty s)
|
|||
associated value, and 'exc' the exception.)
|
||||
|
||||
Value stack Label Instruction Argument
|
||||
[] SETUP_EXCEPT L1
|
||||
[] SETUP_FINALLY L1
|
||||
[] <code for S>
|
||||
[] POP_BLOCK
|
||||
[] JUMP_FORWARD L0
|
||||
|
@ -2613,9 +2752,9 @@ compiler_try_except(struct compiler *c, stmt_ty s)
|
|||
end = compiler_new_block(c);
|
||||
if (body == NULL || except == NULL || orelse == NULL || end == NULL)
|
||||
return 0;
|
||||
ADDOP_JREL(c, SETUP_EXCEPT, except);
|
||||
ADDOP_JREL(c, SETUP_FINALLY, except);
|
||||
compiler_use_next_block(c, body);
|
||||
if (!compiler_push_fblock(c, EXCEPT, body))
|
||||
if (!compiler_push_fblock(c, EXCEPT, body, NULL))
|
||||
return 0;
|
||||
VISIT_SEQ(c, stmt, s->v.Try.body);
|
||||
ADDOP(c, POP_BLOCK);
|
||||
|
@ -2666,25 +2805,23 @@ 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, FINALLY_TRY, cleanup_body))
|
||||
if (!compiler_push_fblock(c, HANDLER_CLEANUP, cleanup_body, cleanup_end))
|
||||
return 0;
|
||||
|
||||
/* second # body */
|
||||
VISIT_SEQ(c, stmt, handler->v.ExceptHandler.body);
|
||||
ADDOP(c, POP_BLOCK);
|
||||
compiler_pop_fblock(c, FINALLY_TRY, cleanup_body);
|
||||
ADDOP(c, BEGIN_FINALLY);
|
||||
compiler_pop_fblock(c, HANDLER_CLEANUP, cleanup_body);
|
||||
|
||||
/* finally: */
|
||||
ADDOP_O(c, LOAD_CONST, Py_None, consts);
|
||||
compiler_use_next_block(c, cleanup_end);
|
||||
if (!compiler_push_fblock(c, FINALLY_END, cleanup_end))
|
||||
if (!compiler_push_fblock(c, FINALLY_END, cleanup_end, NULL))
|
||||
return 0;
|
||||
|
||||
/* name = None */
|
||||
/* name = None; del name */
|
||||
ADDOP_O(c, LOAD_CONST, Py_None, consts);
|
||||
compiler_nameop(c, handler->v.ExceptHandler.name, Store);
|
||||
|
||||
/* del name */
|
||||
compiler_nameop(c, handler->v.ExceptHandler.name, Del);
|
||||
|
||||
ADDOP(c, END_FINALLY);
|
||||
|
@ -2701,11 +2838,11 @@ 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, FINALLY_TRY, cleanup_body))
|
||||
if (!compiler_push_fblock(c, HANDLER_CLEANUP, cleanup_body, NULL))
|
||||
return 0;
|
||||
VISIT_SEQ(c, stmt, handler->v.ExceptHandler.body);
|
||||
ADDOP(c, POP_EXCEPT);
|
||||
compiler_pop_fblock(c, FINALLY_TRY, cleanup_body);
|
||||
compiler_pop_fblock(c, HANDLER_CLEANUP, cleanup_body);
|
||||
}
|
||||
ADDOP_JREL(c, JUMP_FORWARD, end);
|
||||
compiler_use_next_block(c, except);
|
||||
|
@ -2964,18 +3101,7 @@ compiler_visit_stmt(struct compiler *c, stmt_ty s)
|
|||
case ClassDef_kind:
|
||||
return compiler_class(c, s);
|
||||
case Return_kind:
|
||||
if (c->u->u_ste->ste_type != FunctionBlock)
|
||||
return compiler_error(c, "'return' outside function");
|
||||
if (s->v.Return.value) {
|
||||
if (c->u->u_ste->ste_coroutine && c->u->u_ste->ste_generator)
|
||||
return compiler_error(
|
||||
c, "'return' with value in async generator");
|
||||
VISIT(c, expr, s->v.Return.value);
|
||||
}
|
||||
else
|
||||
ADDOP_O(c, LOAD_CONST, Py_None, consts);
|
||||
ADDOP(c, RETURN_VALUE);
|
||||
break;
|
||||
return compiler_return(c, s);
|
||||
case Delete_kind:
|
||||
VISIT_SEQ(c, expr, s->v.Delete.targets)
|
||||
break;
|
||||
|
@ -3027,10 +3153,7 @@ compiler_visit_stmt(struct compiler *c, stmt_ty s)
|
|||
case Pass_kind:
|
||||
break;
|
||||
case Break_kind:
|
||||
if (!compiler_in_loop(c))
|
||||
return compiler_error(c, "'break' outside loop");
|
||||
ADDOP(c, BREAK_LOOP);
|
||||
break;
|
||||
return compiler_break(c);
|
||||
case Continue_kind:
|
||||
return compiler_continue(c);
|
||||
case With_kind:
|
||||
|
@ -3771,8 +3894,6 @@ compiler_call_helper(struct compiler *c,
|
|||
The LC/SC version returns the populated container, while the GE version is
|
||||
flagged in symtable.c as a generator, so it returns the generator object
|
||||
when the function is called.
|
||||
This code *knows* that the loop cannot contain break, continue, or return,
|
||||
so it cheats and skips the SETUP_LOOP/POP_BLOCK steps used in normal loops.
|
||||
|
||||
Possible cleanups:
|
||||
- iterate over the generator sequence instead of using recursion
|
||||
|
@ -3932,8 +4053,8 @@ compiler_async_comprehension_generator(struct compiler *c,
|
|||
compiler_use_next_block(c, try);
|
||||
|
||||
|
||||
ADDOP_JREL(c, SETUP_EXCEPT, except);
|
||||
if (!compiler_push_fblock(c, EXCEPT, try))
|
||||
ADDOP_JREL(c, SETUP_FINALLY, except);
|
||||
if (!compiler_push_fblock(c, EXCEPT, try, NULL))
|
||||
return 0;
|
||||
|
||||
ADDOP(c, GET_ANEXT);
|
||||
|
@ -3954,7 +4075,7 @@ compiler_async_comprehension_generator(struct compiler *c,
|
|||
ADDOP(c, POP_TOP);
|
||||
ADDOP(c, POP_TOP);
|
||||
ADDOP(c, POP_TOP);
|
||||
ADDOP(c, POP_EXCEPT); /* for SETUP_EXCEPT */
|
||||
ADDOP(c, POP_EXCEPT); /* for SETUP_FINALLY */
|
||||
ADDOP_JABS(c, JUMP_ABSOLUTE, anchor);
|
||||
|
||||
|
||||
|
@ -4246,7 +4367,7 @@ compiler_async_with(struct compiler *c, stmt_ty s, int pos)
|
|||
|
||||
/* SETUP_ASYNC_WITH pushes a finally block. */
|
||||
compiler_use_next_block(c, block);
|
||||
if (!compiler_push_fblock(c, FINALLY_TRY, block)) {
|
||||
if (!compiler_push_fblock(c, ASYNC_WITH, block, finally)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -4267,11 +4388,11 @@ compiler_async_with(struct compiler *c, stmt_ty s, int pos)
|
|||
|
||||
/* End of try block; start the finally block */
|
||||
ADDOP(c, POP_BLOCK);
|
||||
compiler_pop_fblock(c, FINALLY_TRY, block);
|
||||
ADDOP(c, BEGIN_FINALLY);
|
||||
compiler_pop_fblock(c, ASYNC_WITH, block);
|
||||
|
||||
ADDOP_O(c, LOAD_CONST, Py_None, consts);
|
||||
compiler_use_next_block(c, finally);
|
||||
if (!compiler_push_fblock(c, FINALLY_END, finally))
|
||||
if (!compiler_push_fblock(c, FINALLY_END, finally, NULL))
|
||||
return 0;
|
||||
|
||||
/* Finally block starts; context.__exit__ is on the stack under
|
||||
|
@ -4334,7 +4455,7 @@ compiler_with(struct compiler *c, stmt_ty s, int pos)
|
|||
|
||||
/* SETUP_WITH pushes a finally block. */
|
||||
compiler_use_next_block(c, block);
|
||||
if (!compiler_push_fblock(c, FINALLY_TRY, block)) {
|
||||
if (!compiler_push_fblock(c, WITH, block, finally)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -4355,11 +4476,11 @@ compiler_with(struct compiler *c, stmt_ty s, int pos)
|
|||
|
||||
/* End of try block; start the finally block */
|
||||
ADDOP(c, POP_BLOCK);
|
||||
compiler_pop_fblock(c, FINALLY_TRY, block);
|
||||
ADDOP(c, BEGIN_FINALLY);
|
||||
compiler_pop_fblock(c, WITH, block);
|
||||
|
||||
ADDOP_O(c, LOAD_CONST, Py_None, consts);
|
||||
compiler_use_next_block(c, finally);
|
||||
if (!compiler_push_fblock(c, FINALLY_END, finally))
|
||||
if (!compiler_push_fblock(c, FINALLY_END, finally, NULL))
|
||||
return 0;
|
||||
|
||||
/* Finally block starts; context.__exit__ is on the stack under
|
||||
|
@ -4745,41 +4866,6 @@ compiler_annassign(struct compiler *c, stmt_ty s)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
compiler_push_fblock(struct compiler *c, enum fblocktype t, basicblock *b)
|
||||
{
|
||||
struct fblockinfo *f;
|
||||
if (c->u->u_nfblocks >= CO_MAXBLOCKS) {
|
||||
PyErr_SetString(PyExc_SyntaxError,
|
||||
"too many statically nested blocks");
|
||||
return 0;
|
||||
}
|
||||
f = &c->u->u_fblock[c->u->u_nfblocks++];
|
||||
f->fb_type = t;
|
||||
f->fb_block = b;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
compiler_pop_fblock(struct compiler *c, enum fblocktype t, basicblock *b)
|
||||
{
|
||||
struct compiler_unit *u = c->u;
|
||||
assert(u->u_nfblocks > 0);
|
||||
u->u_nfblocks--;
|
||||
assert(u->u_fblock[u->u_nfblocks].fb_type == t);
|
||||
assert(u->u_fblock[u->u_nfblocks].fb_block == b);
|
||||
}
|
||||
|
||||
static int
|
||||
compiler_in_loop(struct compiler *c) {
|
||||
int i;
|
||||
struct compiler_unit *u = c->u;
|
||||
for (i = 0; i < u->u_nfblocks; ++i) {
|
||||
if (u->u_fblock[i].fb_type == LOOP)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/* Raises a SyntaxError and returns 0.
|
||||
If something goes wrong, a different exception may be raised.
|
||||
*/
|
||||
|
@ -4974,9 +5060,7 @@ dfs(struct compiler *c, basicblock *b, struct assembler *a, int end)
|
|||
Py_LOCAL_INLINE(void)
|
||||
stackdepth_push(basicblock ***sp, basicblock *b, int depth)
|
||||
{
|
||||
/* XXX b->b_startdepth > depth only for the target of SETUP_FINALLY,
|
||||
* SETUP_WITH and SETUP_ASYNC_WITH. */
|
||||
assert(b->b_startdepth < 0 || b->b_startdepth >= depth);
|
||||
assert(b->b_startdepth < 0 || b->b_startdepth == depth);
|
||||
if (b->b_startdepth < depth) {
|
||||
assert(b->b_startdepth < 0);
|
||||
b->b_startdepth = depth;
|
||||
|
@ -5033,15 +5117,11 @@ stackdepth(struct compiler *c)
|
|||
maxdepth = target_depth;
|
||||
}
|
||||
assert(target_depth >= 0); /* invalid code or bug in stackdepth() */
|
||||
if (instr->i_opcode == CONTINUE_LOOP) {
|
||||
/* Pops a variable number of values from the stack,
|
||||
* but the target should be already proceeding.
|
||||
*/
|
||||
if (instr->i_opcode == CALL_FINALLY) {
|
||||
assert(instr->i_target->b_startdepth >= 0);
|
||||
assert(instr->i_target->b_startdepth <= depth);
|
||||
/* remaining code is dead */
|
||||
next = NULL;
|
||||
break;
|
||||
assert(instr->i_target->b_startdepth >= target_depth);
|
||||
depth = new_depth;
|
||||
continue;
|
||||
}
|
||||
stackdepth_push(&sp, instr->i_target, target_depth);
|
||||
}
|
||||
|
@ -5049,8 +5129,7 @@ stackdepth(struct compiler *c)
|
|||
if (instr->i_opcode == JUMP_ABSOLUTE ||
|
||||
instr->i_opcode == JUMP_FORWARD ||
|
||||
instr->i_opcode == RETURN_VALUE ||
|
||||
instr->i_opcode == RAISE_VARARGS ||
|
||||
instr->i_opcode == BREAK_LOOP)
|
||||
instr->i_opcode == RAISE_VARARGS)
|
||||
{
|
||||
/* remaining code is dead */
|
||||
next = NULL;
|
||||
|
|
3303
Python/importlib.h
3303
Python/importlib.h
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -5,7 +5,7 @@ static void *opcode_targets[256] = {
|
|||
&&TARGET_ROT_THREE,
|
||||
&&TARGET_DUP_TOP,
|
||||
&&TARGET_DUP_TOP_TWO,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_ROT_FOUR,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_NOP,
|
||||
|
@ -52,7 +52,7 @@ static void *opcode_targets[256] = {
|
|||
&&TARGET_GET_AITER,
|
||||
&&TARGET_GET_ANEXT,
|
||||
&&TARGET_BEFORE_ASYNC_WITH,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_BEGIN_FINALLY,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_INPLACE_ADD,
|
||||
&&TARGET_INPLACE_SUBTRACT,
|
||||
|
@ -79,7 +79,7 @@ static void *opcode_targets[256] = {
|
|||
&&TARGET_INPLACE_AND,
|
||||
&&TARGET_INPLACE_XOR,
|
||||
&&TARGET_INPLACE_OR,
|
||||
&&TARGET_BREAK_LOOP,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_WITH_CLEANUP_START,
|
||||
&&TARGET_WITH_CLEANUP_FINISH,
|
||||
&&TARGET_RETURN_VALUE,
|
||||
|
@ -118,9 +118,9 @@ static void *opcode_targets[256] = {
|
|||
&&TARGET_LOAD_GLOBAL,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_CONTINUE_LOOP,
|
||||
&&TARGET_SETUP_LOOP,
|
||||
&&TARGET_SETUP_EXCEPT,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_SETUP_FINALLY,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_LOAD_FAST,
|
||||
|
@ -161,8 +161,8 @@ static void *opcode_targets[256] = {
|
|||
&&_unknown_opcode,
|
||||
&&TARGET_LOAD_METHOD,
|
||||
&&TARGET_CALL_METHOD,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_CALL_FINALLY,
|
||||
&&TARGET_POP_FINALLY,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
#define UNCONDITIONAL_JUMP(op) (op==JUMP_ABSOLUTE || op==JUMP_FORWARD)
|
||||
#define CONDITIONAL_JUMP(op) (op==POP_JUMP_IF_FALSE || op==POP_JUMP_IF_TRUE \
|
||||
|| op==JUMP_IF_FALSE_OR_POP || op==JUMP_IF_TRUE_OR_POP)
|
||||
#define ABSOLUTE_JUMP(op) (op==JUMP_ABSOLUTE || op==CONTINUE_LOOP \
|
||||
#define ABSOLUTE_JUMP(op) (op==JUMP_ABSOLUTE \
|
||||
|| op==POP_JUMP_IF_FALSE || op==POP_JUMP_IF_TRUE \
|
||||
|| op==JUMP_IF_FALSE_OR_POP || op==JUMP_IF_TRUE_OR_POP)
|
||||
#define JUMPS_ON_TRUE(op) (op==POP_JUMP_IF_TRUE || op==JUMP_IF_TRUE_OR_POP)
|
||||
|
@ -185,12 +185,10 @@ markblocks(_Py_CODEUNIT *code, Py_ssize_t len)
|
|||
case POP_JUMP_IF_FALSE:
|
||||
case POP_JUMP_IF_TRUE:
|
||||
case JUMP_ABSOLUTE:
|
||||
case CONTINUE_LOOP:
|
||||
case SETUP_LOOP:
|
||||
case SETUP_EXCEPT:
|
||||
case SETUP_FINALLY:
|
||||
case SETUP_WITH:
|
||||
case SETUP_ASYNC_WITH:
|
||||
case CALL_FINALLY:
|
||||
j = GETJUMPTGT(code, i);
|
||||
assert(j < len);
|
||||
blocks[j] = 1;
|
||||
|
@ -373,15 +371,8 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
|
|||
/* Replace jumps to unconditional jumps */
|
||||
case POP_JUMP_IF_FALSE:
|
||||
case POP_JUMP_IF_TRUE:
|
||||
case FOR_ITER:
|
||||
case JUMP_FORWARD:
|
||||
case JUMP_ABSOLUTE:
|
||||
case CONTINUE_LOOP:
|
||||
case SETUP_LOOP:
|
||||
case SETUP_EXCEPT:
|
||||
case SETUP_FINALLY:
|
||||
case SETUP_WITH:
|
||||
case SETUP_ASYNC_WITH:
|
||||
h = GETJUMPTGT(codestr, i);
|
||||
tgt = find_op(codestr, h);
|
||||
/* Replace JUMP_* to a RETURN into just a RETURN */
|
||||
|
@ -407,7 +398,21 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
|
|||
/* Remove unreachable ops after RETURN */
|
||||
case RETURN_VALUE:
|
||||
h = i + 1;
|
||||
while (h < codelen && ISBASICBLOCK(blocks, i, h)) {
|
||||
/* 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)
|
||||
{
|
||||
if (_Py_OPCODE(codestr[h]) == SETUP_FINALLY) {
|
||||
while (h > i + 1 &&
|
||||
_Py_OPCODE(codestr[h - 1]) == EXTENDED_ARG)
|
||||
{
|
||||
h--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
h++;
|
||||
}
|
||||
if (h > i + 1) {
|
||||
|
@ -452,7 +457,6 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
|
|||
case NOP:continue;
|
||||
|
||||
case JUMP_ABSOLUTE:
|
||||
case CONTINUE_LOOP:
|
||||
case POP_JUMP_IF_FALSE:
|
||||
case POP_JUMP_IF_TRUE:
|
||||
case JUMP_IF_FALSE_OR_POP:
|
||||
|
@ -462,11 +466,10 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
|
|||
|
||||
case FOR_ITER:
|
||||
case JUMP_FORWARD:
|
||||
case SETUP_LOOP:
|
||||
case SETUP_EXCEPT:
|
||||
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…
Add table
Add a link
Reference in a new issue