1 Star 1 Fork 0

蒙海康/Lua-5.1.1

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
ldo.c 26.41 KB
一键复制 编辑 原始数据 按行查看 历史
蒙海康 提交于 2024-06-26 19:55 . add notes
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
/*
** $Id: ldo.c,v 2.37 2005/12/22 16:19:56 roberto Exp roberto $
** Stack and Call structure of Lua
** See Copyright Notice in lua.h
*/
#include <setjmp.h>
#include <stdlib.h>
#include <string.h>
// #include <stdio.h>
#define ldo_c
#define LUA_CORE
#include "lua.h"
#include "ldebug.h"
#include "ldo.h"
#include "lfunc.h"
#include "lgc.h"
#include "lmem.h"
#include "lobject.h"
#include "lopcodes.h"
#include "lparser.h"
#include "lstate.h"
#include "lstring.h"
#include "ltable.h"
#include "ltm.h"
#include "lundump.h"
#include "lvm.h"
#include "lzio.h"
/*
** {======================================================
** Error-recovery functions
** =======================================================
*/
/* chain list of long jump buffers */
struct lua_longjmp {
struct lua_longjmp *previous;
luai_jmpbuf b;
volatile int status; /* error code */
};
void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) {
switch (errcode) {
case LUA_ERRMEM: {
setsvalue2s(L, oldtop, luaS_newliteral(L, MEMERRMSG));
break;
}
case LUA_ERRERR: {
setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling"));
break;
}
case LUA_ERRSYNTAX:
case LUA_ERRRUN: {
setobjs2s(L, oldtop, L->top - 1); /* error message on current top */
break;
}
}
L->top = oldtop + 1;
}
static void restore_stack_limit (lua_State *L) {
lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1);
if (L->size_ci > LUAI_MAXCALLS) { /* there was an overflow? */
int inuse = cast_int(L->ci - L->base_ci);
if (inuse + 1 < LUAI_MAXCALLS) /* can `undo' overflow? */
luaD_reallocCI(L, LUAI_MAXCALLS);
}
}
static void resetstack (lua_State *L, int status) {
L->ci = L->base_ci;
L->base = L->ci->base;
luaF_close(L, L->base); /* close eventual pending closures */
luaD_seterrorobj(L, status, L->base);
L->nCcalls = 0;
L->allowhook = 1;
restore_stack_limit(L);
L->errfunc = 0;
L->errorJmp = NULL;
}
// 虽然有些情形下,会不生成errMsg到栈顶然后调用luaD_throw,但这是极其罕见的情况,通常是在处理报错时报错得更离谱才会这样(全局搜了下源码)
void luaD_throw (lua_State *L, int errcode) {
if (L->errorJmp) {
// 设置错误码,并回跳到上一次的LUAI_TRY(setjump)调用点
L->errorJmp->status = errcode;
LUAI_THROW(L, L->errorJmp);
}
else {
// 无errorJmp可以回跳,则这个异常无人承接处理,lua vm直接退出
L->status = cast_byte(errcode);
// 调用global_State的panic函数,通常用于给出lua vm终止的原因,默认为luaL_newstate()时注入的panic()函数
if (G(L)->panic) {
resetstack(L, errcode);
lua_unlock(L);
G(L)->panic(L);
}
// 终止进程,进程的相关资源被操作系统自动回收
exit(EXIT_FAILURE);
}
}
// 以保护模式运行函数f,其实现基于C语言的setjump longjump机制,最终返回此次函数调用的status(结果码),外部调用由返回值不为0就可以知道发生了异常,并承接处理
int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
// errorJmp切换为新构建的setjump点,旧的作为前缀
struct lua_longjmp lj;
lj.status = 0;
lj.previous = L->errorJmp; /* chain new error handler */
L->errorJmp = &lj;
// setjump()并执行函数f,如果函数执行过程中调用了LUAI_THROW(),则直接、立刻回到setjump()处,后续不执行(*f)(L, ud);
LUAI_TRY(L, &lj,
(*f)(L, ud);
);
// errorJmp切回旧的(前缀)
L->errorJmp = lj.previous; /* restore old error handler */
return lj.status;
}
/* }====================================================== */
static void correctstack (lua_State *L, TValue *oldstack) {
CallInfo *ci;
GCObject *up;
L->top = (L->top - oldstack) + L->stack;
for (up = L->openupval; up != NULL; up = up->gch.next)
gco2uv(up)->v = (gco2uv(up)->v - oldstack) + L->stack;
for (ci = L->base_ci; ci <= L->ci; ci++) {
ci->top = (ci->top - oldstack) + L->stack;
ci->base = (ci->base - oldstack) + L->stack;
ci->func = (ci->func - oldstack) + L->stack;
}
L->base = (L->base - oldstack) + L->stack;
}
void luaD_reallocstack (lua_State *L, int newsize) {
TValue *oldstack = L->stack;
int realsize = newsize + 1 + EXTRA_STACK;
// 写成 (L->stack_last - L->stack + 1) == (L->stacksize - EXTRA_STACK) 可能更好理解一些
lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1);
// 以当前L->stack的地址为基础,调用realloc(),将内存块扩张或缩小至realsize * sizeof(TValue)的大小
luaM_reallocvector(L, L->stack, L->stacksize, realsize, TValue);
L->stacksize = realsize;
L->stack_last = L->stack + newsize;
// 根据realloc()的机制,可能L->stack的地址后面没有足够的空间,那么就会另申请一块空间,将原内存块中的数据copy过去,然后释放原内存块
// 这样一来当前struct lua_State实例的top、base指针,CallInfo中的各种指向虚拟栈的指针,其指向就是错误的了,需要调整
correctstack(L, oldstack);
}
void luaD_reallocCI (lua_State *L, int newsize) {
CallInfo *oldci = L->base_ci;
luaM_reallocvector(L, L->base_ci, L->size_ci, newsize, CallInfo);
L->size_ci = newsize;
L->ci = (L->ci - oldci) + L->base_ci;
L->end_ci = L->base_ci + L->size_ci - 1;
}
void luaD_growstack (lua_State *L, int n) {
if (n <= L->stacksize) /* double size is enough? */
luaD_reallocstack(L, 2*L->stacksize);
else
luaD_reallocstack(L, L->stacksize + n);
}
static CallInfo *growCI (lua_State *L) {
if (L->size_ci > LUAI_MAXCALLS) /* overflow while handling overflow? */
luaD_throw(L, LUA_ERRERR);
else {
luaD_reallocCI(L, 2*L->size_ci);
if (L->size_ci > LUAI_MAXCALLS)
luaG_runerror(L, "stack overflow");
}
return ++L->ci;
}
void luaD_callhook (lua_State *L, int event, int line) {
lua_Hook hook = L->hook;
if (hook && L->allowhook) {
ptrdiff_t top = savestack(L, L->top);
ptrdiff_t ci_top = savestack(L, L->ci->top);
lua_Debug ar;
ar.event = event;
ar.currentline = line;
if (event == LUA_HOOKTAILRET)
ar.i_ci = 0; /* tail call; no debug information about it */
else
ar.i_ci = cast_int(L->ci - L->base_ci);
luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */
L->ci->top = L->top + LUA_MINSTACK;
lua_assert(L->ci->top <= L->stack_last);
L->allowhook = 0; /* cannot call hooks inside a hook */
lua_unlock(L);
(*hook)(L, &ar);
lua_lock(L);
lua_assert(!L->allowhook);
L->allowhook = 1;
L->ci->top = restorestack(L, ci_top);
L->top = restorestack(L, top);
}
}
static StkId adjust_varargs (lua_State *L, Proto *p, int actual) {
int i;
int nfixargs = p->numparams; // 函数定义中,形参表的具名形参个数(除开 ...)
Table *htab = NULL;
StkId base, fixed;
// 调用实际函数时,实参个数 < 形参表的具名形参个数,用nil补充;注意actual所代表的实参总数也在增加
for (; actual < nfixargs; ++actual)
setnilvalue(L->top++);
// LUA_COMPAT_VARARG,兼容Lua 5.0的编译选项,不预定义这个选项会比较好,加了的话,本次函数调用会多出一个叫arg的局部变量
// 阅读源码时你可以把这块代码当作不存在
#if defined(LUA_COMPAT_VARARG)
if (p->is_vararg & VARARG_NEEDSARG) { /* compat. with old-style vararg? */
int nvar = actual - nfixargs; /* number of extra arguments */
lua_assert(p->is_vararg & VARARG_HASARG);
luaC_checkGC(L);
htab = luaH_new(L, nvar, 1); /* create `arg' table */
for (i=0; i<nvar; i++) /* put extra arguments into `arg' table */
setobj2n(L, luaH_setnum(L, htab, i+1), L->top - nvar + i);
/* store counter in field `n' */
setnvalue(luaH_setstr(L, htab, luaS_newliteral(L, "n")), cast_num(nvar));
}
#endif
// 此时L->top的下方依次为:本次函数调用的实参、函数本身
/* move fixed parameters to final position */
// 此时虚拟栈中的实参list已经经过一轮fix(补充实参),fixed必定指向func上方第1个槽位上,即第1个实参
// base指向最后一个实参的后一个槽位
fixed = L->top - actual; /* first fixed argument */
base = L->top; /* final position of first argument */
// nfixargs = 形参表的具名形参个数;也就是说,只移动具名形参的那部分,可变参数那部分不复制
// 最后,可变参数那部分的实参,位置处于base指针的下方
// 注意,这里的L->top也一直在随之上移
for (i=0; i<nfixargs; i++) {
setobjs2s(L, L->top++, fixed+i); // set obj1 = obj2
setnilvalue(fixed+i);
}
// 我画个图来表示pcall开始执行前的虚拟栈布局
/*
| 函数 | 实参(形参表具名部分) |实参(可变参数部分)|
| | | |
| func | 444 | 555 | 453 | nil | nil | 22 | nil | 33 |
*/
// 目前的虚拟栈内存布局,左边靠近虚拟栈栈底,右边靠近栈顶
/*
| 函数 | 实参(形参表具名部分) |实参(可变参数部分)| 被拷贝过来的实参(形参表具名部分)|
| | | |
| func | nil | nil | nil | nil | nil | 22 | nil | 33 | 444 | 555 | 453 | nil | nil |
^ ^ ^
| | |
fixed L->base = base L->top
*/
// 把实参重新复制到更上层的虚拟栈位置上是要干撒子?把原先位置上的元素置nil又是要干撒子?为什么要这样子做呢?
// 我知道你脑子里充满了疑惑,但我肯定地告诉你,你的理解是没有错的,在lvm.c的case OP_VARARG处有写后续的取值逻辑
/* add `arg' parameter */
if (htab) {
sethvalue(L, L->top++, htab);
lua_assert(iswhite(obj2gco(htab)));
}
return base;
}
static StkId tryfuncTM (lua_State *L, StkId func) {
const TValue *tm = luaT_gettmbyobj(L, func, TM_CALL);
StkId p;
ptrdiff_t funcr = savestack(L, func);
if (!ttisfunction(tm))
luaG_typeerror(L, func, "call");
/* Open a hole inside the stack at `func' */
for (p = L->top; p > func; p--) setobjs2s(L, p, p-1);
incr_top(L);
func = restorestack(L, funcr); /* previous call may change stack */
setobj2s(L, func, tm); /* tag method is the new function to be called */
return func;
}
#define inc_ci(L) \
((L->ci == L->end_ci) ? growCI(L) : \
(condhardstacktests(luaD_reallocCI(L, L->size_ci)), ++L->ci))
int luaD_precall (lua_State *L, StkId func, int nresults) {
LClosure *cl;
ptrdiff_t funcr;
// 如果不是一个函数数值,func可能是个设置了__call元方法的table,尝试找它的__call元方法执行
if (!ttisfunction(func)) /* `func' is not a function? */
func = tryfuncTM(L, func); /* check the `function' tag method */
funcr = savestack(L, func); // StkId func到虚拟栈栈底的偏移量
cl = &clvalue(func)->l; // StkId -> TValue -> LClosure l, func不一定是LClosure,用指针引用一下而已
L->ci->savedpc = L->savedpc; // 初始,lua_State的savedpc为nil
if (!cl->isC) { /* Lua function? prepare its call */
CallInfo *ci;
StkId st, base;
Proto *p = cl->p; // 取函数定义中的原型数据
// 检查并调整虚拟栈的大小,函数调用时会有一些临时变量需要存储到虚拟栈上,p->maxstacksize表示该函数原型在执行时,最多需要xx个虚拟栈槽位
// 后续看编译部分的时候注意一下这个maxstacksize的数值是如何被确定的,它是否会考虑本chunk内调用其他函数的情况?
// 我觉得是不会的,实际验证也是如此,如果在本层chunk中调用了其他函数,那么它不会考虑被调函数的内部情况,也不会考虑被调函数的返回值有多少个
// lua的策略是,在真正要执行函数的时候再调整lua_State虚拟栈的大小,见OP_CALL指令执行时会调用luaD_precall();
// 然后在gc流程中会检查lua_State虚拟栈是否太大,太大的话就释放缩小
luaD_checkstack(L, p->maxstacksize);
// printf("luaD_precall show %s-%d %d\n", getstr(p->source), p->linedefined, p->maxstacksize);
// 用偏移量取回StkId func,上面的luaD_checkstack()可能会将虚拟栈迁移到另一块内存中,所以这里得用偏移量取出(恍然大悟)
func = restorestack(L, funcr);
if (!p->is_vararg) { /* no varargs? 函数定义的参数表没有可变参数 ... */
// 调整L->top的指向,不能超过 (StkId func + 1 + 原型Proto形参表的参数个数),也就是说,多余的实参会在这一步被舍弃掉
base = func + 1;
if (L->top > base + p->numparams)
L->top = base + p->numparams;
}
else { /* vararg function */
// Proto原型的形参表有可变参数 ... ,那么本次执行的函数调用栈,需要接受外部调用传入的所有实参;
// nargs为本次函数调用中,实参的总数
int nargs = cast_int(L->top - func) - 1;
// base 的指向看 adjust_varargs()的内部注释
base = adjust_varargs(L, p, nargs);
// adjust_varargs() -> luaC_checkGC(),虚拟栈可能会迁移内存,这里用偏移量重新取回回来
func = restorestack(L, funcr); /* previous call may change the stack */
}
// return ++L->ci,递进到next CallInfo,供此次函数调用使用,如果CallInfo实例不够用,会自动扩容
ci = inc_ci(L); /* now `enter' new function */
// 对新CallInfo实例各种赋值,顺便切一下L->base、L->savedpc的数值
ci->func = func;
L->base = ci->base = base;
ci->top = L->base + p->maxstacksize;
lua_assert(ci->top <= L->stack_last);
L->savedpc = p->code; /* starting point */
ci->tailcalls = 0;
ci->nresults = nresults;
// 已经属于新CallInfo的这段虚拟栈重置为nil
for (st = L->top; st < ci->top; st++)
setnilvalue(st);
// 维护L->top
L->top = ci->top;
// lua hook机制的相关逻辑,可以先不看
if (L->hookmask & LUA_MASKCALL) {
L->savedpc++; /* hooks assume 'pc' is already incremented */
luaD_callhook(L, LUA_HOOKCALL, -1);
L->savedpc--; /* correct 'pc' */
}
return PCRLUA;
}
else { /* if is a C function, call it */
CallInfo *ci;
int n;
// 执行CClosure前,确保剩余可用的栈位至少有LUA_MINSTACK个
luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */
ci = inc_ci(L); /* now `enter' new function */
// 调整新CallInfo实例的信息
ci->func = restorestack(L, funcr); // 根据偏移量重新找回要执行的func所在的栈位
L->base = ci->base = ci->func + 1;
ci->top = L->top + LUA_MINSTACK; // 设置C函数调用栈的栈顶,实际执行时不够的话,C函数的定义中可以用luaL_checkstack()接口增长函数调用栈
lua_assert(ci->top <= L->stack_last);
ci->nresults = nresults;
// 注意,这里未更改L->top = 的指向,因为C函数目前还无法确定最终要使用的虚拟栈栈位有多少个
// 所以采取的策略是在C函数执行时,再将L->top逐渐上移,api_incr_top()
// lua hook机制相关
if (L->hookmask & LUA_MASKCALL)
luaD_callhook(L, LUA_HOOKCALL, -1);
lua_unlock(L);
// 以C函数指针获取到函数地址,然后调用执行它
// 从后面的源码中不难看出,C接口最后return的数值,其含义如下:
// n < 0 表示将lua协程挂起了;n >= 0 表示把n个返回值留在了虚拟栈中,交由lua层处理;这也是我们自定C接口时所需要遵循的规定
n = (*curr_func(L)->c.f)(L); /* do the actual call */
lua_lock(L);
if (n < 0) /* yielding? */
return PCRYIELD;
else {
// C接口正常执行完毕,走正常的收尾流程,往虚拟栈里放了一些返回值,第一个返回值所在的槽位 = L->top - n
luaD_poscall(L, L->top - n);
return PCRC;
}
}
}
static StkId callrethooks (lua_State *L, StkId firstResult) {
ptrdiff_t fr = savestack(L, firstResult); /* next call may change stack */
luaD_callhook(L, LUA_HOOKRET, -1);
if (f_isLua(L->ci)) { /* Lua function? */
while (L->ci->tailcalls--) /* call hook for eventual tail calls */
luaD_callhook(L, LUA_HOOKTAILRET, -1);
}
return restorestack(L, fr);
}
// 将本次函数调用的返回值入栈,并将L->ci恢复到上一层函数调用的状态
// firstResult指向第一个返回值所在的虚拟栈槽位
int luaD_poscall (lua_State *L, StkId firstResult) {
StkId res;
int wanted, i;
CallInfo *ci;
// 如果设置了lua hook,在此处调用
if (L->hookmask & LUA_MASKRET)
firstResult = callrethooks(L, firstResult);
ci = L->ci--; // lua_State的ci切回上一个CallInfo,局部的ci仍为当前函数调用的CallInfo
// 函数调用结束return时,返回值的赋值从被调func所在的槽位开始
res = ci->func; /* res == final position of 1st result */
wanted = ci->nresults; // 本次函数调用,期望有多少个返回值,其实是 调用方 决定是多少的
// 如果期待有返回值,那么返回值必定会拿去干某些事情,说明函数调用的前面有东西在承接着返回值
// lua_State的两个成员变量恢复到上一个CallInfo的记录状态
L->base = (ci - 1)->base; /* restore base */
L->savedpc = (ci - 1)->savedpc; /* restore savedpc */
/* move results to correct place */
// 此时函数调用栈中,自底向上依次为 被调func、若干个实参或过程值(可能没有)、返回值1、返回值2、...
// 现在将返回值拷贝到自StkId func开始的槽位上,其实就是整体向下挪了X个槽位
for (i = wanted; i != 0 && firstResult < L->top; i--)
setobjs2s(L, res++, firstResult++);
// 若实际return的返回值数量不满足nresults的要求,则用nil补全
while (i-- > 0)
setnilvalue(res++);
L->top = res; // 移动L->top指向这堆返回值的上方槽位
return (wanted - LUA_MULTRET); /* return 0 if nresults == LUA_MULTRET; else return (nresults + 1), return > 0 */
}
/*
** Call a function (C or Lua). The function to be called is at *func.
** The arguments are on the stack, right after the function.
** When returns, all the results are on the stack, starting at the original
** function position.
*/
void luaD_call (lua_State *L, StkId func, int nResults) {
// 函数调用的层级计数自增,并检查有没超过上限
if (++L->nCcalls >= LUAI_MAXCCALLS) {
if (L->nCcalls == LUAI_MAXCCALLS)
// 层级溢出时,直接抛出异常
luaG_runerror(L, "C stack overflow");
else if (L->nCcalls >= (LUAI_MAXCCALLS + (LUAI_MAXCCALLS>>3)))
// errfunc处理上面的调用层级溢出异常时,溢出得更离谱了,就调用该逻辑分支
luaD_throw(L, LUA_ERRERR); /* error while handing stack error */
}
// precall是执行一些准备工作,将接下来函数执行的环境布置好
// 但如果func是C函数,那么在luaD_precall()内部就会被直接执行
if (luaD_precall(L, func, nResults) == PCRLUA) /* is a Lua function? */
// 取出函数原型Proto中的指令一个个执行,初看的话,把lua函数的函数体内容想象为`return 1, 2, 3, 4`的简单形式,直接看OP_RETURN指令的操作即可
luaV_execute(L, 1); /* call it */
// 函数调用已经进入结束阶段,调用层级回归
L->nCcalls--;
luaC_checkGC(L);
}
static void resume (lua_State *L, void *ud) {
// 第一个实参所在的虚拟栈栈位
StkId firstArg = cast(StkId, ud);
CallInfo *ci = L->ci;
// 初始执行协程
if (L->status == 0) { /* start coroutine? */
lua_assert(ci == L->base_ci && firstArg > L->base);
// 执行的返回值全部保留,非LClosure则直接退出
if (luaD_precall(L, firstArg - 1, LUA_MULTRET) != PCRLUA)
return;
}
// 从上一次yield开始继续执行
else { /* resuming from previous yield */
lua_assert(L->status == LUA_YIELD);
L->status = 0;
if (!f_isLua(ci)) { /* `common' yield? */
/* finish interrupted execution of `OP_CALL' */
lua_assert(GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_CALL ||
GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_TAILCALL);
// 将firstArg以及后续附带的多个参数,作为yield()的返回值,它们已经在虚拟栈上,yield()函数真正结束执行
// 如果并不期待所有的返回值,那么则需要调整一下栈顶
if (luaD_poscall(L, firstArg)) /* complete it... */
L->top = L->ci->top; /* and correct top if not multiple results */
}
else /* yielded inside a hook: just continue its execution */
L->base = L->ci->base;
}
// 继续执行LClosure中的后续指令集
luaV_execute(L, cast_int(L->ci - L->base_ci));
}
static int resume_error (lua_State *L, const char *msg) {
L->top = L->ci->base;
setsvalue2s(L, L->top, luaS_new(L, msg));
incr_top(L);
lua_unlock(L);
return LUA_ERRRUN;
}
LUA_API int lua_resume (lua_State *L, int nargs) {
int status;
lua_lock(L);
if (L->status != LUA_YIELD) {
if (L->status != 0)
return resume_error(L, "cannot resume dead coroutine");
else if (L->ci != L->base_ci)
return resume_error(L, "cannot resume non-suspended coroutine");
}
// 预留的接口
luai_userstateresume(L, nargs);
// 为什么可以断言co->nCcalls == 0呢?因为在某些时机,例如抛出异常前的错误消息处理函数(C 或 Lua)的执行过程中,协程是不能被挂起的,注意分析co的状态。
lua_assert(L->errfunc == 0 && L->nCcalls == 0);
// 以保护模式执行resume函数
status = luaD_rawrunprotected(L, resume, L->top - nargs);
if (status != 0) { /* error? */
L->status = cast_byte(status); /* mark thread as `dead' */
// 根据error code,将errMsg生成或复制到栈顶
luaD_seterrorobj(L, status, L->top);
L->ci->top = L->top;
}
else
// 函数被正常执行,那么沿用co->status的状态即可
status = L->status;
lua_unlock(L);
return status;
}
LUA_API int lua_yield (lua_State *L, int nresults) {
// 预留的编程接口
luai_userstateyield(L, nresults);
lua_lock(L);
// 在某些时机,例如抛出异常前的错误消息处理函数(C 或 Lua)的执行过程中,协程是不能被挂起的,注意分析co的状态。
if (L->nCcalls > 0)
luaG_runerror(L, "attempt to yield across metamethod/C-call boundary");
// 在调用yield()前,要返回给(初次)resume()调用处的实参,已经在虚拟栈中,返回到上一个函数层级后,按逻辑继续执行即可
// co当中函数调用链的执行,暂时停止在这一个点上,本次lua vm的resume()执行完毕,这部分返回值会从co被复制到源lua_State的虚拟栈中,见auxresume()
// 保护下方的虚拟栈槽位
L->base = L->top - nresults; /* protect stack slots below */
L->status = LUA_YIELD;
lua_unlock(L);
return -1;
}
// old_top是本次函数调用的目标函数(TValue),到虚拟栈栈底的偏移量,即,原先的虚拟栈栈顶
// ef则是errMsg处理函数到虚拟栈栈底的偏移量,有可能为0
int luaD_pcall (lua_State *L, Pfunc func, void *u, ptrdiff_t old_top, ptrdiff_t ef) {
int status;
unsigned short oldnCcalls = L->nCcalls; // C函数调用链的层数计数
ptrdiff_t old_ci = saveci(L, L->ci); // L->ci 到 L->base_ci 的偏移量; 初始的lua_State, L->ci和L->base_ci其实指向同一个(第一个)CallInfo
lu_byte old_allowhooks = L->allowhook; // 暂时不知道这个有啥用,应该是hook机制相关的
// 将lua_State原先的错误消息处理函数缓存起来,本次函数执行用新指定的
ptrdiff_t old_errfunc = L->errfunc;
// 接下来在luaD_rawrunprotected()的运行过程中,如果有异常发生,那么原生的errMsg会被推到虚拟栈栈顶,然后检查有无L->errfunc,
// 有的话则将原生的errMsg会作为errfunc的实参,调用errfunc,errfunc通常会将经过处理的errStr留在虚拟栈栈顶
// 具体见luaG_errormsg(),从语言机制的角度上来说,luaD_throw()之后,函数调用链的堆栈就被回滚删除了,所以一般是在errfunc中,去收集报错时的堆栈信息
L->errfunc = ef;
// 在保护模式下运行func指代的f_call()函数,struct CallS指针u,作为f_call()的实参
status = luaD_rawrunprotected(L, func, u);
if (status != 0) { /* an error occurred? */
StkId oldtop = restorestack(L, old_top);
// luaF_close()看起来是对upvalues相关的处理
luaF_close(L, oldtop); /* close eventual pending closures */
// 将errStr设置到old_top槽位,并调整L->top = old_top + 1,errStr可能是原生的errMsg,也可能是经过errfunc处理后生成的newErrMsg
luaD_seterrorobj(L, status, oldtop);
// 异常报错后,L的成员变量都恢复(回滚)到lua vm被驱动前的状态
// 如果是初始lua_State,可以看下preinit_state()的内容
L->nCcalls = oldnCcalls;
L->ci = restoreci(L, old_ci);
L->base = L->ci->base;
L->savedpc = L->ci->savedpc;
L->allowhook = old_allowhooks;
// L的CallInfo数组,其大小如果大于LUAI_MAXCALLS,尝试将它的大小调整回去
restore_stack_limit(L);
}
// func指代的函数调用结束,errfunc重置为旧的
L->errfunc = old_errfunc;
return status;
}
/*
** Execute a protected parser.
*/
struct SParser { /* data to `f_parser' */
ZIO *z;
Mbuffer buff; /* buffer to be used by the scanner */
const char *name;
};
static void f_parser (lua_State *L, void *ud) {
int i;
Proto *tf;
Closure *cl;
struct SParser *p = cast(struct SParser *, ud);
int c = luaZ_lookahead(p->z);
luaC_checkGC(L);
tf = ((c == LUA_SIGNATURE[0]) ? luaU_undump : luaY_parser)(L, p->z,
&p->buff, p->name);
cl = luaF_newLclosure(L, tf->nups, hvalue(gt(L)));
cl->l.p = tf;
for (i = 0; i < tf->nups; i++) /* initialize eventual upvalues */
cl->l.upvals[i] = luaF_newupval(L);
setclvalue(L, L->top, cl);
incr_top(L);
}
int luaD_protectedparser (lua_State *L, ZIO *z, const char *name) {
struct SParser p;
int status;
p.z = z; p.name = name;
luaZ_initbuffer(L, &p.buff);
status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc);
luaZ_freebuffer(L, &p.buff);
return status;
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
C
1
https://gitee.com/menghaikang/Lua-5.1.1.git
git@gitee.com:menghaikang/Lua-5.1.1.git
menghaikang
Lua-5.1.1
Lua-5.1.1
main

搜索帮助

0d507c66 1850385 C8b1a773 1850385