1、PyFrameObject
Python在真正执行的时候并不是面对的PyCodeObject对象,而是PyFrameObject对象,它包含静态的PyCodeObject和执行环境。
typedef struct _frame {
PyObject_VAR_HEAD
struct _frame *f_back;/* previous frame, or NULL */ 执行环境上一个栈帧
PyCodeObject *f_code;/* code segment */ PyCodeObject对象
PyObject *f_builtins;/* builtin symbol table (PyDictObject) */builtin名字空间
PyObject *f_globals;/* global symbol table (PyDictObject) */global名字空间
PyObject *f_locals;/* local symbol table (any mapping) */local名字空间
PyObject **f_valuestack;/* points after the last local */运行时栈的栈底位置
/* Next free slot in f_valuestack. Frame creation sets to f_valuestack.
Frame evaluation usually NULLs it, but a frame that yields sets it
to the current stack top. */
PyObject **f_stacktop; 栈顶位置
PyObject *f_trace;/* Trace function */
/* If an exception is raised in this frame, the next three are used to
* record the exception info (if any) originally in the thread state. See
* comments before set_exc_info() -- it's not obvious.
* Invariant: if _type is NULL, then so are _value and _traceback.
* Desired invariant: all three are NULL, or all three are non-NULL. That
* one isn't currently true, but "should be".
*/
PyObject *f_exc_type, *f_exc_value, *f_exc_traceback;
PyThreadState *f_tstate;
int f_lasti;/* Last instruction if called */ 上一条字节码指令在f_code中的偏移位置
/* Call PyFrame_GetLineNumber() instead of reading this field
directly. As of 2.3 f_lineno is only valid when tracing is
active (i.e. when f_trace is set). At other times we use
PyCode_Addr2Line to calculate the line from the current
bytecode index. */
int f_lineno;/* Current line number */ 当前字节码对应的源代码行数。
int f_iblock;/* index in f_blockstack */
PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */ try和loop的最大内嵌数目20个。
PyObject *f_localsplus[1];/* locals+stack, dynamically sized */ 动态内存,维护(局部变量+cell对象集合+free对象集合+运行时栈)所需要的空间。
} PyFrameObject;
Python提供了获取当前栈帧的接口:sys._getframe()
2、执行流程。
PyObject *
PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
{
...
co = f->f_code; # PyCodeObject
names = co->co_names;
consts = co->co_consts;
fastlocals = f->f_localsplus;
freevars = f->f_localsplus + co->co_nlocals;
first_instr = (unsigned char*) PyString_AS_STRING(co->co_code); PyStringObject类型的对象,字节码指令序列的开始
next_instr = first_instr + f->f_lasti + 1; 下一条待执行的字节码指令的位置。
stack_pointer = f->f_stacktop;
assert(stack_pointer != NULL);
f->f_stacktop = NULL; /* remains NULL unless yield suspends frame */
...
why = WHY_NOT; 返回标志。
for (;;) {
opcode = NEXTOP();
switch (opcode) {
}
}
}
/* Status code for main loop (reason for stack unwind) */
enum why_code {
WHY_NOT = 0x0001, /* No error */ 正常退出
WHY_EXCEPTION = 0x0002, /* Exception occurred */ 异常
WHY_RERAISE = 0x0004, /* Exception re-raised by 'finally' */ raise
WHY_RETURN = 0x0008, /* 'return' statement */ return返回
WHY_BREAK = 0x0010, /* 'break' statement */ break中断
WHY_CONTINUE = 0x0020, /* 'continue' statement */continue中断
WHY_YIELD = 0x0040 /* 'yield' operator */ yield中断
};