Python 原始碼分析之位元組碼之基本操作
阿新 • • 發佈:2018-12-23
本文基於 Python 3.6.4 編譯器生成位元組碼,你可通過如下程式碼片段得到
python 原始碼對應的位元組碼
#!/usr/bin/env python
# encoding: utf-8
import sys
import dis
filename=sys.argv[1]
f = open(filename, 'rb')
content = f.read()
c = compile(content, filename, "exec")
dis.dis(c)
指令執行
所有的位元組碼都位於 Python/ceval.c 檔案
//元祖的第 i 個元素
#define GETITEM(v, i) PyTuple_GetItem((v), (i))
//調整棧頂指標
#define BASIC_STACKADJ(n) (stack_pointer += n)
#define STACKADJ(n) { (void)(BASIC_STACKADJ(n), \
lltrace && prtrace(TOP(), "stackadj")); \
assert(STACK_LEVEL() <= co->co_stacksize); }
//入棧
#define BASIC_PUSH(v) (*stack_pointer++ = (v))
#define PUSH(v) { (void)(BASIC_PUSH(v), \
lltrace && prtrace(TOP(), "push")); \
assert(STACK_LEVEL() <= co->co_stacksize); }
//出棧
#define BASIC_POP() (*--stack_pointer)
#define POP() ((void)(lltrace && prtrace(TOP(), "pop")), \
BASIC_POP())
//指向下一條指令
#define DISPATCH() \
{ \
if (!_Py_atomic_load_relaxed(&eval_breaker)) { \
FAST_DISPATCH(); \
} \
continue; \
}
#ifdef LLTRACE
#define FAST_DISPATCH() \
{ \
if (!lltrace && !_Py_TracingPossible && !PyDTrace_LINE_ENABLED()) { \
f->f_lasti = INSTR_OFFSET(); \
NEXTOPARG(); \
goto *opcode_targets[opcode]; \
} \
goto fast_next_opcode; \
}
#else
#define FAST_DISPATCH() \
{ \
if (!_Py_TracingPossible && !PyDTrace_LINE_ENABLED()) { \
f->f_lasti = INSTR_OFFSET(); \
NEXTOPARG(); \
goto *opcode_targets[opcode]; \
} \
goto fast_next_opcode; \
}
#endif
#define JUMPBY(x) (next_instr += (x) / sizeof(_Py_CODEUNIT))
#define JUMPTO(x) (next_instr = first_instr + (x) / sizeof(_Py_CODEUNIT))
typedef uint16_t _Py_CODEUNIT
#!/usr/bin/env python
# encoding: utf-8
i = 1
s = "Python"
d = {}
l = {}
對應的位元組碼為
4 0 LOAD_CONST 0 (1)
2 STORE_NAME 0 (i)
5 4 LOAD_CONST 1 ('Python')
6 STORE_NAME 1 (s)
6 8 BUILD_MAP 0
10 STORE_NAME 2 (d)
12 LOAD_CONST 2 (None)
14 RETURN_VALUE
注:第一列為原始碼行號,第二列為指令索引, 第三列為指令,第四列為指令的引數,第五列為指令引數對應的值(提示)。
TARGET(LOAD_CONST) {
# 從常量表中獲取索引為 oparg 的元素,其值為 value
PyObject *value = GETITEM(consts, oparg);
Py_INCREF(value);
PUSH(value);
FAST_DISPATCH();
}
TARGET(STORE_NAME) {
//從符號表獲取第 oparg 個元素作為變數名 name
PyObject *name = GETITEM(names, oparg);
//將棧頂元素賦值給 v,並將棧指標減 1
PyObject *v = POP();
PyObject *ns = f->f_locals;
int err;
if (ns == NULL) {
PyErr_Format(PyExc_SystemError,
"no locals found when storing %R", name);
Py_DECREF(v);
goto error;
}
if (PyDict_CheckExact(ns))
//f->f_locals 中設定 name = v
err = PyDict_SetItem(ns, name, v);
else
//ns.name = v
err = PyObject_SetItem(ns, name, v);
//引用減一
Py_DECREF(v);
if (err != 0)
goto error;
DISPATCH();
}
#define PEEK(n) (stack_pointer[-(n)])
TARGET(BUILD_MAP) {
Py_ssize_t i;
//建立一個字典,初始元素個數為 oparg
PyObject *map = _PyDict_NewPresized((Py_ssize_t)oparg);
if (map == NULL)
goto error;
for (i = oparg; i > 0; i--) {
int err;
//找到 key
PyObject *key = PEEK(2*i);
//找到 value
PyObject *value = PEEK(2*i - 1);
//設定 map[key] = value
err = PyDict_SetItem(map, key, value);
if (err != 0) {
Py_DECREF(map);
goto error;
}
}
while (oparg--) {
Py_DECREF(POP());
Py_DECREF(POP());
}
PUSH(map);
DISPATCH();
}
TARGET(BUILD_LIST) {
//建立一個列表,初始元素個數為 oparg
PyObject *list = PyList_New(oparg);
if (list == NULL)
goto error;
//從後往前
while (--oparg >= 0) {
//從棧中彈出一個元素
PyObject *item = POP();
//設定 list[oparg] = item
PyList_SET_ITEM(list, oparg, item);
}
//list 壓棧
PUSH(list);
DISPATCH();
}
TARGET(RETURN_VALUE) {
retval = POP();
why = WHY_RETURN;
goto fast_block_end;
}
例 2
#!/usr/bin/env python
# encoding: utf-8
a = 1
b = "Python"
c = a + b
4 0 LOAD_CONST 0 (1)
3 STORE_NAME 0 (a)
5 6 LOAD_CONST 1 ('Python')
9 STORE_NAME 1 (b)
6 12 LOAD_NAME 0 (a)
15 LOAD_NAME 1 (b)
18 BINARY_ADD
19 STORE_NAME 2 (c)
22 LOAD_CONST 2 (None)
25 RETURN_VALUE
TARGET(LOAD_NAME) {
//從符號表載入中第 oparg 個元素 name,依次從區域性變量表,全域性變量表,
//內建變量表中查詢 name 對應找到值,如果找到將該值壓棧,如果沒有找到拋異常。
PyObject *name = GETITEM(names, oparg);
PyObject *locals = f->f_locals;
PyObject *v;
//確保區域性變量表存在 name
if (locals == NULL) {
PyErr_Format(PyExc_SystemError,
"no locals when loading %R", name);
goto error;
}
//如果是字典物件,從區域性變量表中獲取 name 對應的值
if (PyDict_CheckExact(locals)) {
v = PyDict_GetItem(locals, name);
Py_XINCREF(v);
}
else {
v = PyObject_GetItem(locals, name);
if (v == NULL) {
if (!PyErr_ExceptionMatches(PyExc_KeyError))
goto error;
PyErr_Clear();
}
}
//如果區域性變量表不存在該變數,從全域性變數中查詢
if (v == NULL) {
v = PyDict_GetItem(f->f_globals, name);
Py_XINCREF(v);
if (v == NULL) {
# 如果全域性變量表中也不存在,從內建變量表中查詢
if (PyDict_CheckExact(f->f_builtins)) {
v = PyDict_GetItem(f->f_builtins, name);
if (v == NULL) {
format_exc_check_arg(
PyExc_NameError,
NAME_ERROR_MSG, name);
goto error;
}
Py_INCREF(v);
}
else {
v = PyObject_GetItem(f->f_builtins, name);
if (v == NULL) {
if (PyErr_ExceptionMatches(PyExc_KeyError))
format_exc_check_arg(
PyExc_NameError,
NAME_ERROR_MSG, name);
goto error;
}
}
}
}
PUSH(v);
DISPATCH();
}
TARGET(BINARY_ADD) {
//從棧中彈出右邊的變數
PyObject *right = POP();
//獲取棧頂元素,可見入棧從左往右
PyObject *left = TOP();
PyObject *sum;
if (PyUnicode_CheckExact(left) &&
PyUnicode_CheckExact(right)) {
//字串連線
sum = unicode_concatenate(left, right, f, next_instr);
/* unicode_concatenate consumed the ref to left */
}
else {
//數字相加
sum = PyNumber_Add(left, right);
Py_DECREF(left);
}
Py_DECREF(right);
//直接將棧頂元素設定為疊加之後的值,避免了兩次棧操作
SET_TOP(sum);
if (sum == NULL)
goto error;
DISPATCH();
}
#!/usr/bin/env python
# encoding: utf-8
c = { "1" : "a", "2": "b" }
c = [1, 2, 3]
4 0 BUILD_MAP 2
3 LOAD_CONST 0 ('a')
6 LOAD_CONST 1 ('1')
9 STORE_MAP
10 LOAD_CONST 2 ('b')
13 LOAD_CONST 3 ('2')
16 STORE_MAP
17 STORE_NAME 0 (c)
5 20 LOAD_CONST 4 (1)
23 LOAD_CONST 5 (2)
26 LOAD_CONST 6 (3)
29 BUILD_LIST 3
32 STORE_NAME 0 (c)
35 LOAD_CONST 7 (None)
38 RETURN_VALUE
可見 map 是載入一個元素,寫入 map,再載入一個元素,再寫入,依次類推
而 list 一次將所有元素寫入棧,然後寫入 list
哈哈,這裡 list 會不會有棧溢位攻擊呢? 我試了下 500 元素構造 list
也是壓棧 500 次,之後再加入 list
函式呼叫
#!/usr/bin/env python
# encoding: utf-8
a = 2
print(a)
4 0 LOAD_CONST 0 (2)
2 STORE_NAME 0 (a)
5 4 LOAD_NAME 1 (print)
6 LOAD_NAME 0 (a)
8 CALL_FUNCTION 1
10 POP_TOP
12 LOAD_CONST 1 (None)
14 RETURN_VALUE
PREDICTED(CALL_FUNCTION);
TARGET(CALL_FUNCTION) {
PyObject **sp, *res;
PCALL(PCALL_ALL);
sp = stack_pointer;
res = call_function(&sp, oparg, NULL);
stack_pointer = sp;
PUSH(res);
if (res == NULL) {
goto error;
}
DISPATCH();
}
#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
static PyObject *
call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames)
{
PyObject **pfunc = (*pp_stack) - oparg - 1;
PyObject *func = *pfunc;
PyObject *x, *w;
Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames);
Py_ssize_t nargs = oparg - nkwargs;
PyObject **stack;
/* Always dispatch PyCFunction first, because these are
presumed to be the most frequent callable object.
*/
if (PyCFunction_Check(func)) {
PyThreadState *tstate = PyThreadState_GET();
PCALL(PCALL_CFUNCTION);
stack = (*pp_stack) - nargs - nkwargs;
C_TRACE(x, _PyCFunction_FastCallKeywords(func, stack, nargs, kwnames));
}
else {
if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL) {
/* optimize access to bound methods */
PyObject *self = PyMethod_GET_SELF(func);
PCALL(PCALL_METHOD);
PCALL(PCALL_BOUND_METHOD);
Py_INCREF(self);
func = PyMethod_GET_FUNCTION(func);
Py_INCREF(func);
Py_SETREF(*pfunc, self);
nargs++;
}
else {
Py_INCREF(func);
}
stack = (*pp_stack) - nargs - nkwargs;
if (PyFunction_Check(func)) {
x = fast_function(func, stack, nargs, kwnames);
}
else {
x = _PyObject_FastCallKeywords(func, stack, nargs, kwnames);
}
Py_DECREF(func);
}
assert((x != NULL) ^ (PyErr_Occurred() != NULL));
/* Clear the stack of the function object. Also removes
the arguments in case they weren't consumed already
(fast_function() and err_args() leave them on the stack).
*/
while ((*pp_stack) > pfunc) {
w = EXT_POP(*pp_stack);
Py_DECREF(w);
PCALL(PCALL_POP);
}
return x;
}
if 語句
#!/usr/bin/env python
# encoding: utf-8
a = 2
if a > 1:
a = 0
編譯之後
4 0 LOAD_CONST 0 (2)
2 STORE_NAME 0 (a)
5 4 LOAD_NAME 0 (a)
6 LOAD_CONST 1 (1)
8 COMPARE_OP 4 (>)
10 POP_JUMP_IF_FALSE 16
6 12 LOAD_CONST 2 (0)
14 STORE_NAME 0 (a)
>> 16 LOAD_CONST 3 (None)
18 RETURN_VALUE
static PyObject * cmp_outcome(int op, PyObject *v, PyObject *w)
{
int res = 0;
switch (op) {
//...
default:
return PyObject_RichCompare(v, w, op);
}
v = res ? Py_True : Py_False;
Py_INCREF(v);
return v;
}
PyObject *
PyObject_RichCompare(PyObject *v, PyObject *w, int op)
{
PyObject *res;
assert(Py_LT <= op && op <= Py_GE);
if (v == NULL || w == NULL) {
if (!PyErr_Occurred())
PyErr_BadInternalCall();
return NULL;
}
if (Py_EnterRecursiveCall(" in comparison"))
return NULL;
res = do_richcompare(v, w, op);
Py_LeaveRecursiveCall();
return res;
}
//由下可知實際呼叫的是物件型別的 ob_type->tp_richcompare
static PyObject *
do_richcompare(PyObject *v, PyObject *w, int op)
{
richcmpfunc f;
PyObject *res;
int checked_reverse_op = 0;
if (v->ob_type != w->ob_type &&
PyType_IsSubtype(w->ob_type, v->ob_type) &&
(f = w->ob_type->tp_richcompare) != NULL) {
checked_reverse_op = 1;
res = (*f)(w, v, _Py_SwappedOp[op]);
if (res != Py_NotImplemented)
return res;
Py_DECREF(res);
}
if ((f = v->ob_type->tp_richcompare) != NULL) {
res = (*f)(v, w, op);
if (res != Py_NotImplemented)
return res;
Py_DECREF(res);
}
if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {
res = (*f)(w, v, _Py_SwappedOp[op]);
if (res != Py_NotImplemented)
return res;
Py_DECREF(res);
}
/* If neither object implements it, provide a sensible default
for == and !=, but raise an exception for ordering. */
switch (op) {
case Py_EQ:
res = (v == w) ? Py_True : Py_False;
break;
case Py_NE:
res = (v != w) ? Py_True : Py_False;
break;
default:
PyErr_Format(PyExc_TypeError,
"'%s' not supported between instances of '%.100s' and '%.100s'",
opstrings[op],
v->ob_type->tp_name,
w->ob_type->tp_name);
return NULL;
}
Py_INCREF(res);
return res;
}
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;
//沒有找到 PRED_XXXX 相關程式碼,但從 C 語言的經驗來看應該是分支預測相關,
//當然這與我們掌握位元組碼本身關係也不是特別大,暫且跳過。
PREDICT(POP_JUMP_IF_FALSE);
PREDICT(POP_JUMP_IF_TRUE);
DISPATCH();
}
#if defined(DYNAMIC_EXECUTION_PROFILE) || USE_COMPUTED_GOTOS
#define PREDICT(op) if (0) goto PRED_##op
#else
#define PREDICT(op) \
do{ \
_Py_CODEUNIT word = *next_instr; \
opcode = _Py_OPCODE(word); \
if (opcode == op){ \
oparg = _Py_OPARG(word); \
next_instr++; \
goto PRED_##op; \
} \
} while(0)
#endif
#define PREDICTED(op) PRED_##op:
PREDICTED(POP_JUMP_IF_FALSE);
TARGET(POP_JUMP_IF_FALSE) {
PyObject *cond = POP();
int err;
//如果為 True,繼續執行
if (cond == Py_True) {
Py_DECREF(cond);
FAST_DISPATCH();
}
//如果為 False,跳轉到引數 oparg 的位置
if (cond == Py_False) {
Py_DECREF(cond);
JUMPTO(oparg);
FAST_DISPATCH();
}
//如果不是 bool 型別,大於 0,設定 err = 0
//等於 0,跳轉到引數 oparg 位置,小於 0,報錯
err = PyObject_IsTrue(cond);
Py_DECREF(cond);
if (err > 0)
err = 0;
else if (err == 0)
JUMPTO(oparg);
else
goto error;
DISPATCH();
}
PREDICTED(POP_JUMP_IF_TRUE);
TARGET(POP_JUMP_IF_TRUE) {
PyObject *cond = POP();
int err;
if (cond == Py_False) {
Py_DECREF(cond);
FAST_DISPATCH();
}
if (cond == Py_True) {
Py_DECREF(cond);
JUMPTO(oparg);
FAST_DISPATCH();
}
err = PyObject_IsTrue(cond);
Py_DECREF(cond);
if (err > 0) {
err = 0;
JUMPTO(oparg);
}
else if (err == 0)
;
else
goto error;
DISPATCH();
}
for 語句
#!/usr/bin/env python
# encoding: utf-8
a = [1, 2, 3]
for e in a:
b = e
9 0 LOAD_CONST 0 (1)
2 LOAD_CONST 1 (2)
4 LOAD_CONST 2 (3)
6 BUILD_LIST 3
8 STORE_NAME 0 (a)
10 10 SETUP_LOOP 16 (to 28)
12 LOAD_NAME 0 (a)
14 GET_ITER
# 8/2 = 4,向前跳 4 條指令,本例到了 POP_BLOCK
>> 16 FOR_ITER 8 (to 26)
18 STORE_NAME 1 (e)
11 20 LOAD_NAME 1 (e)
22 STORE_NAME 2 (b)
24 JUMP_ABSOLUTE 16 //調到 FOR_ITER
>> 26 POP_BLOCK
>> 28 LOAD_CONST 3 (None)
30 RETURN_VALUE
const _Py_CODEUNIT *first_instr;
const _Py_CODEUNIT *next_instr;
first_instr = (_Py_CODEUNIT *) PyBytes_AS_STRING(co->co_code);
#define INSTR_OFFSET() (sizeof(_Py_CODEUNIT) * (int)(next_instr - first_instr))
TARGET(SETUP_LOOP) {
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;
//哈哈,還有程式碼塊溢位 #define CO_MAXBLOCKS 20
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;
}
//Include/frameobject.h
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;
typedef struct _frame {
//....
int f_lineno; /* Current line number */
int f_iblock; /* index in f_blockstack */
char f_executing; /* whether the frame is still executing */
PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */
PyObject *f_localsplus[1]; /* locals+stack, dynamically sized */
} PyFrameObject;
TARGET(GET_ITER) {
/* before: [obj]; after [getiter(obj)] */
//獲取棧頂元素
PyObject *iterable = TOP();
//獲取棧頂元素的迭代器,對於 Python 來說,每個物件的類似都是
//PyTypeObject,而 PyTypeObject 都有一個成員 tp_iter 表示其迭代器。
//此處不再詳述,參考 Objects/abstract.c
PyObject *iter = PyObject_GetIter(iterable);
Py_DECREF(iterable);
//用迭代器替代原有棧頂元素
SET_TOP(iter);
if (iter == NULL)
goto error;
PREDICT(FOR_ITER);
PREDICT(CALL_FUNCTION);
DISPATCH();
}
PREDICTED(FOR_ITER);
TARGET(FOR_ITER) {
/* before: [iter]; after: [iter, iter()] *or* [] */
//由 GET_ITER 得知,當前棧頂為 iter
PyObject *iter = TOP();
//獲取下一個元素
PyObject *next = (*iter->ob_type->tp_iternext)(iter);
if (next != NULL) {
//壓棧
PUSH(next);
PREDICT(STORE_FAST);
PREDICT(UNPACK_SEQUENCE);
//continue 的作用是繼續執行下一條指令
DISPATCH();
}
if (PyErr_Occurred()) {
if (!PyErr_ExceptionMatches(PyExc_StopIteration))
goto error;
else if (tstate->c_tracefunc != NULL)
call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, f);
PyErr_Clear();
}
/* iterator ended normally */
STACKADJ(-1);
Py_DECREF(iter);
JUMPBY(oparg);
PREDICT(POP_BLOCK);
DISPATCH();
}
PREDICTED(JUMP_ABSOLUTE);
TARGET(JUMP_ABSOLUTE) {
JUMPTO(oparg);
#if FAST_LOOPS
/* Enabling this path speeds-up all while and for-loops by bypassing
the per-loop checks for signals. By default, this should be turned-off
because it prevents detection of a control-break in tight loops like
"while 1: pass". Compile with this option turned-on when you need
the speed-up and do not need break checking inside tight loops (ones
that contain only instructions ending with FAST_DISPATCH).
*/
FAST_DISPATCH();
#else
DISPATCH();
#endif
}
PREDICTED(POP_BLOCK);
TARGET(POP_BLOCK) {
PyTryBlock *b = PyFrame_BlockPop(f);
UNWIND_BLOCK(b);
DISPATCH();
}
PyTryBlock *
PyFrame_BlockPop(PyFrameObject *f)
{
PyTryBlock *b;
if (f->f_iblock <= 0)
Py_FatalError("XXX block stack underflow");
//將程式碼塊深度減一
b = &f->f_blockstack[--f->f_iblock];
return b;
}
因此,整個 for 迴圈的結構如下
SETUP_LOOP
...
GET_ITER
FOR_ITER
...
JUMP_ABSOLUTE
POP_BLOCK
在建立 for 迴圈的時候 b = &f->f_blockstack[f->f_iblock++];
for 迴圈執行完的時候 b = &f->f_blockstack[–f->f_iblock];
最後,
#!/usr/bin/env python
# encoding: utf-8
for i in range(10):
for i in range(10):
for i in range(10):
for i in range(10):
for i in range(10):
for i in range(10):
for i in range(10):
for i in range(10):
for i in range(10):
for i in range(10):
for i in range(10):
for i in range(10):
for i in range(10):
for i in range(10):
for i in range(10):
for i in range(10):
for i in range(10):
for i in range(10):
a = 1
這估計是一道非常難的 python 面試題了, 猜猜結果是什麼,哈哈,如果換成 21 層 for
迴圈呢?
while 迴圈
#!/usr/bin/env python
# encoding: utf-8
i = 0
while i > 10:
i += 1
if i > 5:
continue
if i == 9:
break
b = 1
位元組碼
9 0 LOAD_CONST 0 (0)
2 STORE_NAME 0 (i)
10 4 SETUP_LOOP 44 (to 50)
>> 6 LOAD_NAME 0 (i)
8 LOAD_CONST 1 (10)
10 COMPARE_OP 0 (<)
12 POP_JUMP_IF_FALSE 48
11 14 LOAD_NAME 0 (i)
16 LOAD_CONST 2 (1)
18 INPLACE_ADD
20 STORE_NAME 0 (i)
12 22 LOAD_NAME 0 (i)
24 LOAD_CONST 3 (5)
26 COMPARE_OP 4 (>)
28 POP_JUMP_IF_FALSE 32
13 30 JUMP_ABSOLUTE 6 //continue
14 >> 32 LOAD_NAME 0 (i)
34 LOAD_CONST 4 (9)
36 COMPARE_OP 2 (==)
38 POP_JUMP_IF_FALSE 42
15 40 BREAK_LOOP
16 >> 42 LOAD_CONST 2 (1)
44 STORE_NAME 1 (b)
46 JUMP_ABSOLUTE 6
>> 48 POP_BLOCK
>> 50 LOAD_CONST 5 (None)
52 RETURN_VALUE
TARGET(BREAK_LOOP) {
why = WHY_BREAK;
goto fast_block_end;
}
fast_block_end:
/* 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];
/* 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 = WHY_NOT;
//調整到 b->b_handler,b->b_handler 實際上就是 SETUP_LOOP 的引數
JUMPTO(b->b_handler);
break;
}
剩下的都是熟悉的指令,熟悉的面孔。
因此,整個 for 迴圈的結構如下
SETUP_LOOP
...
COMPARE_OP
POP_JUMP_IF_FALSE
...
JUMP_ABSOLUTE
POP_BLOCK
在建立 for 迴圈的時候 b = &f->f_blockstack[f->f_iblock++];
for 迴圈執行完的時候 b = &f->f_blockstack[–f->f_iblock];