大師兄的Python源碼學(xué)習(xí)筆記(十七): 虛擬機(jī)中的控制流(四)
大師兄的Python源碼學(xué)習(xí)筆記(十九): 虛擬機(jī)中的函數(shù)機(jī)制(一)
四、虛擬機(jī)中的異??刂屏?/h2>
2 異??刂普Z義結(jié)構(gòu)
- 首先創(chuàng)建一段異常捕獲代碼:
demo.py
try:
raise Exception("an exception")
except Exception as e:
...
finally:
...
- 對(duì)應(yīng)的指令字節(jié)碼如下:
1 0 SETUP_FINALLY 52 (to 54)
2 SETUP_EXCEPT 12 (to 16)
2 4 LOAD_NAME 0 (Exception)
6 LOAD_CONST 0 ('an exception')
8 CALL_FUNCTION 1
10 RAISE_VARARGS 1
12 POP_BLOCK
14 JUMP_FORWARD 34 (to 50)
3 >> 16 DUP_TOP
18 LOAD_NAME 0 (Exception)
20 COMPARE_OP 10 (exception match)
22 POP_JUMP_IF_FALSE 48
24 POP_TOP
26 STORE_NAME 1 (e)
28 POP_TOP
30 SETUP_FINALLY 4 (to 36)
4 32 POP_BLOCK
34 LOAD_CONST 1 (None)
>> 36 LOAD_CONST 1 (None)
38 STORE_NAME 1 (e)
40 DELETE_NAME 1 (e)
42 END_FINALLY
44 POP_EXCEPT
46 JUMP_FORWARD 2 (to 50)
>> 48 END_FINALLY
>> 50 POP_BLOCK
52 LOAD_CONST 1 (None)
6 >> 54 END_FINALLY
56 LOAD_CONST 1 (None)
58 RETURN_VALUE
- 開始的兩個(gè)字節(jié)碼SETUP_FINALLY和SETUP_EXCEPT都是調(diào)用了PyFrame_BlockSetup:
ceval.c
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,
STACK_LEVEL());
DISPATCH();
}
Objects\frameobject.c
void
PyFrame_BlockSetup(PyFrameObject *f, int type, int handler, int level)
{
PyTryBlock *b;
if (f->f_iblock >= CO_MAXBLOCKS)
Py_FatalError("XXX block stack overflow");
b = &f->f_blockstack[f->f_iblock++];
b->b_type = type;
b->b_level = level;
b->b_handler = handler;
}
- 從上面的代碼可以看出PyFrame_BlockSetup對(duì)當(dāng)前PyFrameObject進(jìn)行了配置:
Objects\frameobject.c
typedef struct _frame {
... ...
PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */
... ...
} PyFrameObject;
Objects\frameobject.c
typedef struct {
int b_type; /* what kind of block this is */
int b_handler; /* where to jump to find handler */
int b_level; /* value stack level to pop to */
} PyTryBlock;
- 接著,通過LOAD_NAME、LOAD_CONST和CALL_FUNCTION構(gòu)造了一個(gè)異常對(duì)象壓入棧中,并通過RAISE_VARARGS將異常對(duì)象從運(yùn)行時(shí)棧中取出:
ceval.c
TARGET(RAISE_VARARGS) {
PyObject *cause = NULL, *exc = NULL;
switch (oparg) {
case 2:
cause = POP(); /* cause */
/* fall through */
case 1:
exc = POP(); /* exc */
/* fall through */
case 0:
if (do_raise(exc, cause)) {
why = WHY_EXCEPTION;
goto fast_block_end;
}
break;
default:
PyErr_SetString(PyExc_SystemError,
"bad RAISE_VARARGS oparg");
break;
}
goto error;
}
- 這里oparg參數(shù)為1,所以將異常對(duì)象取出賦值給w,接著運(yùn)行do_raise函數(shù):
ceval.c
static int
do_raise(PyObject *exc, PyObject *cause)
{
PyObject *type = NULL, *value = NULL;
if (exc == NULL) {
/* Reraise */
PyThreadState *tstate = PyThreadState_GET();
_PyErr_StackItem *exc_info = _PyErr_GetTopmostException(tstate);
PyObject *tb;
type = exc_info->exc_type;
value = exc_info->exc_value;
tb = exc_info->exc_traceback;
if (type == Py_None || type == NULL) {
PyErr_SetString(PyExc_RuntimeError,
"No active exception to reraise");
return 0;
}
Py_XINCREF(type);
Py_XINCREF(value);
Py_XINCREF(tb);
PyErr_Restore(type, value, tb);
return 1;
}
/* We support the following forms of raise:
raise
raise <instance>
raise <type> */
if (PyExceptionClass_Check(exc)) {
type = exc;
value = _PyObject_CallNoArg(exc);
if (value == NULL)
goto raise_error;
if (!PyExceptionInstance_Check(value)) {
PyErr_Format(PyExc_TypeError,
"calling %R should have returned an instance of "
"BaseException, not %R",
type, Py_TYPE(value));
goto raise_error;
}
}
else if (PyExceptionInstance_Check(exc)) {
value = exc;
type = PyExceptionInstance_Class(exc);
Py_INCREF(type);
}
else {
/* Not something you can raise. You get an exception
anyway, just not what you specified :-) */
Py_DECREF(exc);
PyErr_SetString(PyExc_TypeError,
"exceptions must derive from BaseException");
goto raise_error;
}
assert(type != NULL);
assert(value != NULL);
if (cause) {
PyObject *fixed_cause;
if (PyExceptionClass_Check(cause)) {
fixed_cause = _PyObject_CallNoArg(cause);
if (fixed_cause == NULL)
goto raise_error;
Py_DECREF(cause);
}
else if (PyExceptionInstance_Check(cause)) {
fixed_cause = cause;
}
else if (cause == Py_None) {
Py_DECREF(cause);
fixed_cause = NULL;
}
else {
PyErr_SetString(PyExc_TypeError,
"exception causes must derive from "
"BaseException");
goto raise_error;
}
PyException_SetCause(value, fixed_cause);
}
PyErr_SetObject(type, value);
/* PyErr_SetObject incref's its arguments */
Py_DECREF(value);
Py_DECREF(type);
return 0;
raise_error:
Py_XDECREF(value);
Py_XDECREF(type);
Py_XDECREF(cause);
return 0;
}
-
do_raise將異常對(duì)象存儲(chǔ)到當(dāng)前線程狀態(tài)中,并在結(jié)束后跳到fast_block_end。
ceval.c
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--;
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)) {
PyObject *exc, *val, *tb;
int handler = b->b_handler;
_PyErr_StackItem *exc_info = tstate->exc_info;
/* Beware, this invalidates all b->b_* fields */
PyFrame_BlockSetup(f, EXCEPT_HANDLER, -1, STACK_LEVEL());
PUSH(exc_info->exc_traceback);
PUSH(exc_info->exc_value);
if (exc_info->exc_type != NULL) {
PUSH(exc_info->exc_type);
}
else {
Py_INCREF(Py_None);
PUSH(Py_None);
}
PyErr_Fetch(&exc, &val, &tb);
/* Make the raw exception data
available to the handler,
so a program can emulate the
Python main loop. */
PyErr_NormalizeException(
&exc, &val, &tb);
if (tb != NULL)
PyException_SetTraceback(val, tb);
else
PyException_SetTraceback(val, Py_None);
Py_INCREF(exc);
exc_info->exc_type = exc;
Py_INCREF(val);
exc_info->exc_value = val;
exc_info->exc_traceback = tb;
if (tb == NULL)
tb = Py_None;
Py_INCREF(tb);
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;
}
} /* unwind stack */
/* End the loop if we still have an error (or return) */
if (why != WHY_NOT)
break;
assert(!PyErr_Occurred());
} /* 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));
- 在這里,虛擬機(jī)首先從當(dāng)前PyFrameObject對(duì)象中的f_blockstack彈出一個(gè)PyTryBlock來。
- 接著虛擬機(jī)通過PyErr_Fetch獲取當(dāng)前線程狀態(tài)對(duì)象中存儲(chǔ)的最新異常對(duì)象和traceback對(duì)象。
ceval.c
void
PyErr_Fetch(PyObject **p_type, PyObject **p_value, PyObject **p_traceback)
{
PyThreadState *tstate = PyThreadState_GET();
*p_type = tstate->curexc_type;
*p_value = tstate->curexc_value;
*p_traceback = tstate->curexc_traceback;
tstate->curexc_type = NULL;
tstate->curexc_value = NULL;
tstate->curexc_traceback = NULL;
}
- 隨后,虛擬機(jī)將tb、val和exc壓入到運(yùn)行時(shí)棧中,并將why設(shè)置為WHY_NOT。
- 最后通過JUMPTO(b->b_handler)將要執(zhí)行的嚇一跳指令設(shè)置為異常處理代碼編譯后所得到的第一條字節(jié)碼指令。
- 經(jīng)過偏移后,字節(jié)碼指令跳轉(zhuǎn)字節(jié)碼DUP_TOP的位置,它是異常處理代碼隊(duì)形的第一條字節(jié)碼指令:
ceval.c
TARGET(DUP_TOP) {
PyObject *top = TOP();
Py_INCREF(top);
PUSH(top);
FAST_DISPATCH();
}
- 在異常處理中,首先通過COMPARE_OP進(jìn)行比對(duì),用來判斷是否需要使用POP_JUMP_IF_FALSE進(jìn)行指令跳躍:
ceval.c
TARGET(COMPARE_OP) {
PyObject *right = POP();
PyObject *left = TOP();
PyObject *res = cmp_outcome(oparg, left, right);
Py_DECREF(left);
Py_DECREF(right);
SET_TOP(res);
if (res == NULL)
goto error;
PREDICT(POP_JUMP_IF_FALSE);
PREDICT(POP_JUMP_IF_TRUE);
DISPATCH();
}
- 由于在進(jìn)入except之前,虛擬機(jī)已經(jīng)從當(dāng)前線程將異常信息tb、val和exc提取了出來。
- 當(dāng)COMPARE_OP匹配時(shí),異常信息會(huì)從棧中取出來處理掉。
- 而當(dāng)COMPARE_OP不匹配時(shí),則需要將異常信息重新放回到線程狀態(tài)對(duì)象中,然后重新設(shè)置why狀態(tài),尋找真正能處理異常的代碼,通過字節(jié)指令碼POP TOP和SETUP_FINALLY完成:
ceval.c
if (opcode == SETUP_FINALLY ||
opcode == SETUP_WITH ||
opcode == BEFORE_ASYNC_WITH ||
opcode == YIELD_FROM) {
/* Few cases where we skip running signal handlers and other
pending calls:
- If we're about to enter the 'with:'. It will prevent
emitting a resource warning in the common idiom
'with open(path) as file:'.
- If we're about to enter the 'async with:'.
- If we're about to enter the 'try:' of a try/finally (not
*very* useful, but might help in some cases and it's
traditional)
- If we're resuming a chain of nested 'yield from' or
'await' calls, then each frame is parked with YIELD_FROM
as its next opcode. If the user hit control-C we want to
wait until we've reached the innermost frame before
running the signal handler and raising KeyboardInterrupt
(see bpo-30039).
*/
goto fast_next_opcode;
}
if (_Py_atomic_load_relaxed(
&_PyRuntime.ceval.pending.calls_to_do))
{
if (Py_MakePendingCalls() < 0)
goto error;
}
if (_Py_atomic_load_relaxed(
&_PyRuntime.ceval.gil_drop_request))
{
/* Give another thread a chance */
if (PyThreadState_Swap(NULL) != tstate)
Py_FatalError("ceval: tstate mix-up");
drop_gil(tstate);
/* Other threads may run now */
take_gil(tstate);
/* Check if we should make a quick exit. */
if (_Py_IsFinalizing() &&
!_Py_CURRENTLY_FINALIZING(tstate))
{
drop_gil(tstate);
PyThread_exit_thread();
}
if (PyThreadState_Swap(tstate) != NULL)
Py_FatalError("ceval: orphan tstate");
}
/* Check for asynchronous exceptions. */
if (tstate->async_exc != NULL) {
PyObject *exc = tstate->async_exc;
tstate->async_exc = NULL;
UNSIGNAL_ASYNC_EXC();
PyErr_SetNone(exc);
Py_DECREF(exc);
goto error;
}
}
- 從上面的代碼的結(jié)尾處,重新跳轉(zhuǎn)到error設(shè)置了why。
- 最終不管是否匹配,兩條岔路在POP_BLOCK會(huì)和:
ceval.c
PREDICTED(POP_BLOCK);
TARGET(POP_BLOCK) {
PyTryBlock *b = PyFrame_BlockPop(f);
UNWIND_BLOCK(b);
DISPATCH();
}
- 這里將PyTryBlock彈出,并進(jìn)入finally表達(dá)式對(duì)應(yīng)的字節(jié)碼指令。
-
最終Python實(shí)現(xiàn)異常機(jī)制的詳細(xì)流程如下:
demo.py
try:
raise Exception("an exception")
except Exception as e:
...
finally:
...
1 0 SETUP_FINALLY 52 (to 54)
2 SETUP_EXCEPT 12 (to 16)
2 4 LOAD_NAME 0 (Exception)
6 LOAD_CONST 0 ('an exception')
8 CALL_FUNCTION 1
10 RAISE_VARARGS 1
12 POP_BLOCK
14 JUMP_FORWARD 34 (to 50)
3 >> 16 DUP_TOP
18 LOAD_NAME 0 (Exception)
20 COMPARE_OP 10 (exception match)
22 POP_JUMP_IF_FALSE 48
24 POP_TOP
26 STORE_NAME 1 (e)
28 POP_TOP
30 SETUP_FINALLY 4 (to 36)
4 32 POP_BLOCK
34 LOAD_CONST 1 (None)
>> 36 LOAD_CONST 1 (None)
38 STORE_NAME 1 (e)
40 DELETE_NAME 1 (e)
42 END_FINALLY
44 POP_EXCEPT
46 JUMP_FORWARD 2 (to 50)
>> 48 END_FINALLY
>> 50 POP_BLOCK
52 LOAD_CONST 1 (None)
6 >> 54 END_FINALLY
56 LOAD_CONST 1 (None)
58 RETURN_VALUE
ceval.c
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,
STACK_LEVEL());
DISPATCH();
}
Objects\frameobject.c
void
PyFrame_BlockSetup(PyFrameObject *f, int type, int handler, int level)
{
PyTryBlock *b;
if (f->f_iblock >= CO_MAXBLOCKS)
Py_FatalError("XXX block stack overflow");
b = &f->f_blockstack[f->f_iblock++];
b->b_type = type;
b->b_level = level;
b->b_handler = handler;
}
- 從上面的代碼可以看出PyFrame_BlockSetup對(duì)當(dāng)前PyFrameObject進(jìn)行了配置:
Objects\frameobject.c
typedef struct _frame {
... ...
PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */
... ...
} PyFrameObject;
Objects\frameobject.c
typedef struct {
int b_type; /* what kind of block this is */
int b_handler; /* where to jump to find handler */
int b_level; /* value stack level to pop to */
} PyTryBlock;
ceval.c
TARGET(RAISE_VARARGS) {
PyObject *cause = NULL, *exc = NULL;
switch (oparg) {
case 2:
cause = POP(); /* cause */
/* fall through */
case 1:
exc = POP(); /* exc */
/* fall through */
case 0:
if (do_raise(exc, cause)) {
why = WHY_EXCEPTION;
goto fast_block_end;
}
break;
default:
PyErr_SetString(PyExc_SystemError,
"bad RAISE_VARARGS oparg");
break;
}
goto error;
}
- 這里oparg參數(shù)為1,所以將異常對(duì)象取出賦值給w,接著運(yùn)行do_raise函數(shù):
ceval.c
static int
do_raise(PyObject *exc, PyObject *cause)
{
PyObject *type = NULL, *value = NULL;
if (exc == NULL) {
/* Reraise */
PyThreadState *tstate = PyThreadState_GET();
_PyErr_StackItem *exc_info = _PyErr_GetTopmostException(tstate);
PyObject *tb;
type = exc_info->exc_type;
value = exc_info->exc_value;
tb = exc_info->exc_traceback;
if (type == Py_None || type == NULL) {
PyErr_SetString(PyExc_RuntimeError,
"No active exception to reraise");
return 0;
}
Py_XINCREF(type);
Py_XINCREF(value);
Py_XINCREF(tb);
PyErr_Restore(type, value, tb);
return 1;
}
/* We support the following forms of raise:
raise
raise <instance>
raise <type> */
if (PyExceptionClass_Check(exc)) {
type = exc;
value = _PyObject_CallNoArg(exc);
if (value == NULL)
goto raise_error;
if (!PyExceptionInstance_Check(value)) {
PyErr_Format(PyExc_TypeError,
"calling %R should have returned an instance of "
"BaseException, not %R",
type, Py_TYPE(value));
goto raise_error;
}
}
else if (PyExceptionInstance_Check(exc)) {
value = exc;
type = PyExceptionInstance_Class(exc);
Py_INCREF(type);
}
else {
/* Not something you can raise. You get an exception
anyway, just not what you specified :-) */
Py_DECREF(exc);
PyErr_SetString(PyExc_TypeError,
"exceptions must derive from BaseException");
goto raise_error;
}
assert(type != NULL);
assert(value != NULL);
if (cause) {
PyObject *fixed_cause;
if (PyExceptionClass_Check(cause)) {
fixed_cause = _PyObject_CallNoArg(cause);
if (fixed_cause == NULL)
goto raise_error;
Py_DECREF(cause);
}
else if (PyExceptionInstance_Check(cause)) {
fixed_cause = cause;
}
else if (cause == Py_None) {
Py_DECREF(cause);
fixed_cause = NULL;
}
else {
PyErr_SetString(PyExc_TypeError,
"exception causes must derive from "
"BaseException");
goto raise_error;
}
PyException_SetCause(value, fixed_cause);
}
PyErr_SetObject(type, value);
/* PyErr_SetObject incref's its arguments */
Py_DECREF(value);
Py_DECREF(type);
return 0;
raise_error:
Py_XDECREF(value);
Py_XDECREF(type);
Py_XDECREF(cause);
return 0;
}
- do_raise將異常對(duì)象存儲(chǔ)到當(dāng)前線程狀態(tài)中,并在結(jié)束后跳到fast_block_end。
ceval.c
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--;
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)) {
PyObject *exc, *val, *tb;
int handler = b->b_handler;
_PyErr_StackItem *exc_info = tstate->exc_info;
/* Beware, this invalidates all b->b_* fields */
PyFrame_BlockSetup(f, EXCEPT_HANDLER, -1, STACK_LEVEL());
PUSH(exc_info->exc_traceback);
PUSH(exc_info->exc_value);
if (exc_info->exc_type != NULL) {
PUSH(exc_info->exc_type);
}
else {
Py_INCREF(Py_None);
PUSH(Py_None);
}
PyErr_Fetch(&exc, &val, &tb);
/* Make the raw exception data
available to the handler,
so a program can emulate the
Python main loop. */
PyErr_NormalizeException(
&exc, &val, &tb);
if (tb != NULL)
PyException_SetTraceback(val, tb);
else
PyException_SetTraceback(val, Py_None);
Py_INCREF(exc);
exc_info->exc_type = exc;
Py_INCREF(val);
exc_info->exc_value = val;
exc_info->exc_traceback = tb;
if (tb == NULL)
tb = Py_None;
Py_INCREF(tb);
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;
}
} /* unwind stack */
/* End the loop if we still have an error (or return) */
if (why != WHY_NOT)
break;
assert(!PyErr_Occurred());
} /* 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));
- 在這里,虛擬機(jī)首先從當(dāng)前PyFrameObject對(duì)象中的f_blockstack彈出一個(gè)PyTryBlock來。
- 接著虛擬機(jī)通過PyErr_Fetch獲取當(dāng)前線程狀態(tài)對(duì)象中存儲(chǔ)的最新異常對(duì)象和traceback對(duì)象。
ceval.c
void
PyErr_Fetch(PyObject **p_type, PyObject **p_value, PyObject **p_traceback)
{
PyThreadState *tstate = PyThreadState_GET();
*p_type = tstate->curexc_type;
*p_value = tstate->curexc_value;
*p_traceback = tstate->curexc_traceback;
tstate->curexc_type = NULL;
tstate->curexc_value = NULL;
tstate->curexc_traceback = NULL;
}
- 隨后,虛擬機(jī)將tb、val和exc壓入到運(yùn)行時(shí)棧中,并將why設(shè)置為WHY_NOT。
- 最后通過JUMPTO(b->b_handler)將要執(zhí)行的嚇一跳指令設(shè)置為異常處理代碼編譯后所得到的第一條字節(jié)碼指令。
ceval.c
TARGET(DUP_TOP) {
PyObject *top = TOP();
Py_INCREF(top);
PUSH(top);
FAST_DISPATCH();
}
ceval.c
TARGET(COMPARE_OP) {
PyObject *right = POP();
PyObject *left = TOP();
PyObject *res = cmp_outcome(oparg, left, right);
Py_DECREF(left);
Py_DECREF(right);
SET_TOP(res);
if (res == NULL)
goto error;
PREDICT(POP_JUMP_IF_FALSE);
PREDICT(POP_JUMP_IF_TRUE);
DISPATCH();
}
ceval.c
if (opcode == SETUP_FINALLY ||
opcode == SETUP_WITH ||
opcode == BEFORE_ASYNC_WITH ||
opcode == YIELD_FROM) {
/* Few cases where we skip running signal handlers and other
pending calls:
- If we're about to enter the 'with:'. It will prevent
emitting a resource warning in the common idiom
'with open(path) as file:'.
- If we're about to enter the 'async with:'.
- If we're about to enter the 'try:' of a try/finally (not
*very* useful, but might help in some cases and it's
traditional)
- If we're resuming a chain of nested 'yield from' or
'await' calls, then each frame is parked with YIELD_FROM
as its next opcode. If the user hit control-C we want to
wait until we've reached the innermost frame before
running the signal handler and raising KeyboardInterrupt
(see bpo-30039).
*/
goto fast_next_opcode;
}
if (_Py_atomic_load_relaxed(
&_PyRuntime.ceval.pending.calls_to_do))
{
if (Py_MakePendingCalls() < 0)
goto error;
}
if (_Py_atomic_load_relaxed(
&_PyRuntime.ceval.gil_drop_request))
{
/* Give another thread a chance */
if (PyThreadState_Swap(NULL) != tstate)
Py_FatalError("ceval: tstate mix-up");
drop_gil(tstate);
/* Other threads may run now */
take_gil(tstate);
/* Check if we should make a quick exit. */
if (_Py_IsFinalizing() &&
!_Py_CURRENTLY_FINALIZING(tstate))
{
drop_gil(tstate);
PyThread_exit_thread();
}
if (PyThreadState_Swap(tstate) != NULL)
Py_FatalError("ceval: orphan tstate");
}
/* Check for asynchronous exceptions. */
if (tstate->async_exc != NULL) {
PyObject *exc = tstate->async_exc;
tstate->async_exc = NULL;
UNSIGNAL_ASYNC_EXC();
PyErr_SetNone(exc);
Py_DECREF(exc);
goto error;
}
}
- 從上面的代碼的結(jié)尾處,重新跳轉(zhuǎn)到error設(shè)置了why。
ceval.c
PREDICTED(POP_BLOCK);
TARGET(POP_BLOCK) {
PyTryBlock *b = PyFrame_BlockPop(f);
UNWIND_BLOCK(b);
DISPATCH();
}
最終Python實(shí)現(xiàn)異常機(jī)制的詳細(xì)流程如下:
