柚子快報(bào)激活碼778899分享:lua的數(shù)據(jù)類型,lua
柚子快報(bào)激活碼778899分享:lua的數(shù)據(jù)類型,lua
數(shù)據(jù)類型
lua中的數(shù)據(jù)可以這樣分為兩位:值類型和引用類型。引用類型創(chuàng)建時(shí)需要從堆上分配內(nèi)存,復(fù)制時(shí)只需要復(fù)制指針,分配的內(nèi)存由GC負(fù)責(zé)維護(hù)生命期。
所有l(wèi)ua類型都用一個(gè)union來(lái)表示:
/*
** Union of all Lua values
*/
typedef union {
GCObject *gc;
void *p; /* lightuserdata */
lua_Number n;
int b; /* boolean */
} Value;
引用類型用一個(gè)gc指針來(lái)引用,其他值類型都直接保存在Value中。
每個(gè)引用類型的開(kāi)頭都是CommonHeader;,所以都可強(qiáng)轉(zhuǎn)成GCheader*使用:
/*
** Common Header for all collectable objects (in macro form, to be
** included in other objects)
*/
#define CommonHeader GCObject *next; lu_byte tt; lu_byte marked
/*
** Common header in struct form
*/
typedef struct {
CommonHeader;
} GCheader;
/*
** Union of all collectable objects
*/
union GCObject {
GCheader gch;
TString ts;
Udata u;
Closure cl;
Table h;
Proto p;
UpVal uv;
lua_State th; /* thread */
};
為了區(qū)分Value中存放的數(shù)據(jù)類型,再額外綁定一個(gè)類型字段:
/*
** Tagged Values
*/
typedef struct {
Value value;
int tt;
} TValue;
lua_State、數(shù)據(jù)棧、調(diào)用棧
lua_State表示一個(gè)線程/協(xié)程(后面線程與協(xié)程通用)的狀態(tài),lua_newstate用于創(chuàng)建主線程:
/*
** Main thread combines a thread state and the global state
*/
typedef struct {
lua_State l;
global_State g;
} LG;
LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
int i;
lua_State *L;
global_State *g;
void *l = (*f)(ud, NULL, 0, state_size(LG));
if (l == NULL) return NULL;
...
用lua_newstate創(chuàng)建主線程的時(shí)候,同時(shí)也創(chuàng)建了一個(gè)global_State,所有的線程共享這個(gè)global_State,luaE_newthread用于創(chuàng)建協(xié)程:
lua_State *luaE_newthread (lua_State *L) {
lua_State *L1 = tostate(luaM_malloc(L, state_size(lua_State)));
luaC_link(L, obj2gco(L1), LUA_TTHREAD);
preinit_state(L1, G(L));
...
lua的數(shù)據(jù)棧是一個(gè)TValue數(shù)組,代碼中用StkId類型用來(lái)指代對(duì)TValue的引用:
typedef TValue *StkId; /* index to stack elements */
調(diào)用棧放在CallInfo數(shù)組中,CallInfo保存著正在調(diào)用的函數(shù)的運(yùn)行狀態(tài):
/*
** informations about a call
*/
typedef struct {
StkId base; /* base for this function */
StkId func; /* function index in the stack */
StkId top; /* top for this function */
const Instruction *savedpc;
int nresults; /* expected number of results from this function */
int tailcalls; /* number of tail calls lost under this entry */
} CallInfo;
正在調(diào)用的函數(shù)一定存在于數(shù)據(jù)棧上,由func引用正在調(diào)用的函數(shù)對(duì)象。 [base,top)指示了正在調(diào)用的函數(shù)的堆棧在數(shù)據(jù)棧上的范圍。 為什么沒(méi)有堆棧的當(dāng)前位置?lua_State的top就是正在調(diào)用的函數(shù)的堆棧的位置啊。
/*
** `per thread' state
*/
struct lua_State {
CommonHeader;
lu_byte status;
StkId top; /* first free slot in the stack */
StkId base; /* base of current function */
global_State *l_G;
CallInfo *ci; /* call info for current function */
const Instruction *savedpc; /* `savedpc' of current function */
StkId stack_last; /* last free slot in the stack */
StkId stack; /* stack base */
CallInfo *last_ci; /* last free slot in the ci array*/
CallInfo *base_ci; /* array of CallInfo's */
int stacksize;
int size_ci; /* size of array `base_ci' */
...
[stack,stack_last],[base_ci,last_ci]分別是數(shù)據(jù)棧數(shù)組和調(diào)用棧數(shù)組,stacksize,size_ci分別是兩個(gè)數(shù)組的大小,在需要的時(shí)候它們會(huì)進(jìn)行增長(zhǎng)。 ci是當(dāng)前正在調(diào)用的函數(shù)的運(yùn)行狀態(tài),base是該函數(shù)的棧底指針。
lua_newstate和luaE_newthread都調(diào)用了stack_init來(lái)初始化堆棧:
static void stack_init (lua_State *L1, lua_State *L) {
/* initialize CallInfo array */
L1->base_ci = luaM_newvector(L, BASIC_CI_SIZE, CallInfo);
L1->ci = L1->base_ci;
L1->size_ci = BASIC_CI_SIZE;
L1->last_ci = L1->base_ci + L1->size_ci - 1;
/* initialize stack array */
L1->stack = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, TValue);
L1->stacksize = BASIC_STACK_SIZE + EXTRA_STACK;
L1->top = L1->stack;
L1->stack_last = L1->stack + (L1->stacksize - EXTRA_STACK) - 1;
/* initialize first ci */
L1->ci->func = L1->top;
setnilvalue(L1->top++); /* `function' entry for this `ci' */
L1->base = L1->ci->base = L1->top;
L1->ci->top = L1->top + LUA_MINSTACK;
}
可以看到first ci只是占了個(gè)位置,它的func只是一個(gè)空值。
c代碼中的函數(shù)調(diào)用
c代碼中調(diào)用函數(shù)(包括c函數(shù)和Lua函數(shù))使用lua_call和lua_pcall。
void (lua_call) (lua_State *L, int nargs, int nresults);
int (lua_pcall) (lua_State *L, int nargs, int nresults, int errfunc);
在使用lua時(shí)一個(gè)愚蠢的錯(cuò)誤中同時(shí)展示了這兩個(gè)函數(shù)的用法。 二者最終都通過(guò)調(diào)用luaD_call來(lái)實(shí)現(xiàn)主要邏輯,但lua_pcall多了一層錯(cuò)誤處理機(jī)制,如果函數(shù)調(diào)用過(guò)程中出錯(cuò),lua_pcall會(huì)捕獲錯(cuò)誤,并調(diào)用用戶通過(guò)errfunc參數(shù)傳入的錯(cuò)誤處理函數(shù),而lua_call則會(huì)直接退出程序。這套錯(cuò)誤處理機(jī)制是通過(guò)c的longjmp或c++的異常機(jī)制來(lái)實(shí)現(xiàn)的。
/*
** 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)))
luaD_throw(L, LUA_ERRERR); /* error while handing stack error */
}
if (luaD_precall(L, func, nResults) == PCRLUA) /* is a Lua function? */
luaV_execute(L, 1); /* call it */
L->nCcalls--;
luaC_checkGC(L);
}
luaD_precall執(zhí)行的是函數(shù)調(diào)用部分的工作,而luaD_poscall做的是函數(shù)返回的工作。對(duì)于c函數(shù)整個(gè)函數(shù)調(diào)用是連續(xù)的,luaD_precall在調(diào)用完c函數(shù)后可直接調(diào)用luaD_poscall完成工作;而lua函數(shù)執(zhí)行完luaD_precall后,只是切換了lua_State的執(zhí)行狀態(tài),被調(diào)用的函數(shù)的字節(jié)碼尚未運(yùn)行,luaD_precall返回后,調(diào)用luaV_execute去運(yùn)行被調(diào)用函數(shù)的字節(jié)碼,待到虛擬機(jī)執(zhí)行到對(duì)應(yīng)的return指令時(shí),才會(huì)去調(diào)用luaD_poscall完成整次調(diào)用。
c函數(shù)的參數(shù)傳遞
使用lua_call或lua_pcall調(diào)用lua函數(shù)前,應(yīng)先在棧上準(zhǔn)備好函數(shù)對(duì)象和函數(shù)的參數(shù),如果沒(méi)有錯(cuò)誤發(fā)生,函數(shù)返回時(shí),函數(shù)對(duì)象和函數(shù)參數(shù)會(huì)出棧,函數(shù)的返回值會(huì)留在棧上。 而如果調(diào)用的是c函數(shù)時(shí),一般是先通過(guò)lua_pushcclosure來(lái)創(chuàng)建c閉包,然后再調(diào)用lua_call或lua_pcall。
void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) {
Closure *cl;
lua_lock(L);
luaC_checkGC(L);
api_checknelems(L, n);
cl = luaF_newCclosure(L, n, getcurrenv(L));
cl->c.f = fn;
L->top -= n;
while (n--)
setobj2n(L, &cl->c.upvalue[n], L->top+n);
setclvalue(L, L->top, cl);
lua_assert(iswhite(obj2gco(cl)));
api_incr_top(L);
lua_unlock(L);
}
調(diào)用lua_pushcclosure前,fn需要的參數(shù)都在棧上,lua_pushcclosure會(huì)把棧上的參數(shù)轉(zhuǎn)移到c閉包的上值中,然后把c閉包壓入棧。在函數(shù)fn中,需要用lua_getupvalue來(lái)讀取參數(shù)。
lua還提供了另一種調(diào)用c函數(shù)的方式:
int lua_cpcall (lua_State *L, lua_CFunction func, void *ud);
在調(diào)用lua_cpcall前,不需要將參數(shù)壓入棧,而是直接將參數(shù)通過(guò)ud傳遞給lua_cpcall。 lua_cpcall會(huì)創(chuàng)建一個(gè)沒(méi)有上值的c閉包并壓入棧中,然后將ud作為lightuserdata也壓入棧,其余便和lua_pcall類似了。 在函數(shù)func中需要通過(guò)lua_touserdata取出參數(shù)。 lua_cpcall在調(diào)用luaD_call時(shí)nResults傳入的是0,所以func并沒(méi)有返回值。
協(xié)程
協(xié)程的創(chuàng)建就是新建了一個(gè)lua_State,然后將lua函數(shù)對(duì)象移到新建的lua_State上:
int luaB_cocreate (lua_State *L) {
lua_State *NL = lua_newthread(L);
luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1,
"Lua function expected");
lua_pushvalue(L, 1); /* move function to top */
lua_xmove(L, NL, 1); /* move function from L to NL */
return 1;
}
通過(guò)luaB_coresume啟動(dòng)/恢復(fù)協(xié)程,首個(gè)參數(shù)是協(xié)程對(duì)象:
static int luaB_coresume (lua_State *L) {
lua_State *co = lua_tothread(L, 1);
int r;
luaL_argcheck(L, co, 1, "coroutine expected");
r = auxresume(L, co, lua_gettop(L) - 1);
if (r < 0) {
lua_pushboolean(L, 0);
lua_insert(L, -2);
return 2; /* return false + error message */
}
else {
lua_pushboolean(L, 1);
lua_insert(L, -(r + 1));
return r + 1; /* return true + `resume' returns */
}
}
在auxresume中將coroutine.resume的其余參數(shù)從主線程轉(zhuǎn)移至協(xié)程:
int auxresume (lua_State *L, lua_State *co, int narg) {
int status = costatus(L, co);
if (!lua_checkstack(co, narg))
luaL_error(L, "too many arguments to resume");
if (status != CO_SUS) {
lua_pushfstring(L, "cannot resume %s coroutine", statnames[status]);
return -1; /* error flag */
}
lua_xmove(L, co, narg);
lua_setlevel(L, co);
status = lua_resume(co, narg);
...
然后去調(diào)用lua_resume,后續(xù)最終會(huì)調(diào)用resume:
void resume (lua_State *co, void *ud) {
StkId firstArg = cast(StkId, ud);
CallInfo *ci = co->ci;
if (co->status == 0) { /* start coroutine? */
lua_assert(ci == co->base_ci && firstArg - co->base == 1);
lua_assert(isLfunction(co->base));
if (luaD_precall(co, firstArg - 1, LUA_MULTRET) != PCRLUA)
return;
}
else { /* resuming from previous yield */
lua_assert(co->status == LUA_YIELD);
co->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);
if (luaD_poscall(co, firstArg)) /* complete it... */
co->top = co->ci->top; /* and correct top if not multiple results */
}
else /* yielded inside a hook: just continue its execution */
co->base = co->ci->base;
}
luaV_execute(co, cast_int(co->ci - co->base_ci));
}
啟動(dòng)協(xié)程時(shí),和調(diào)用普通lua函數(shù)一樣先調(diào)用luaD_precall,再調(diào)用luaV_execute,luaV_execute中調(diào)用函數(shù)的代碼如下:
void luaV_execute (lua_State *L, int nexeccalls) {
...
case OP_CALL: {
int b = GETARG_B(i);
int nresults = GETARG_C(i) - 1;
if (b != 0) L->top = ra+b; /* else previous instruction set top */
L->savedpc = pc;
switch (luaD_precall(L, ra, nresults)) {
case PCRLUA: {
nexeccalls++;
goto reentry; /* restart luaV_execute over new Lua function */
}
case PCRC: {
/* it was a C function (`precall' called it); adjust results */
if (nresults >= 0) L->top = L->ci->top;
base = L->base;
continue;
}
default: {
return; /* yield */
}
}
}
...
}
int luaD_precall (lua_State *L, StkId func, int nresults) {
...
else { /* if is a C function, call it */
CallInfo *ci;
int n;
luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */
ci = inc_ci(L); /* now `enter' new function */
ci->func = restorestack(L, funcr);
L->base = ci->base = ci->func + 1;
ci->top = L->top + LUA_MINSTACK;
lua_assert(ci->top <= L->stack_last);
ci->nresults = nresults;
if (L->hookmask & LUA_MASKCALL)
luaD_callhook(L, LUA_HOOKCALL, -1);
lua_unlock(L);
n = (*ci_func(ci)->c.f)(L); /* do the actual call */
lua_lock(L);
if (n < 0) /* yielding? */
return PCRYIELD;
else {
// n表示c函數(shù)留在棧上的結(jié)果數(shù)量,luaD_poscall會(huì)將結(jié)果調(diào)整為nresults個(gè),放到func開(kāi)始的位置
// 相當(dāng)于將func和所有參數(shù)出棧,再將前nresults的返回值入棧
luaD_poscall(L, L->top - n);
return PCRC;
}
}
...
}
當(dāng)lua函數(shù)中碰到coroutine.yield時(shí),經(jīng)luaD_precall->luaB_yield,再調(diào)用到lua_yield:
int lua_yield (lua_State *co, int nresults) {
luai_userstateyield(co, nresults);
lua_lock(co);
if (co->nCcalls > co->baseCcalls)
luaG_runerror(co, "attempt to yield across metamethod/C-call boundary");
co->base = co->top - nresults; /* protect stack slots below */
co->status = LUA_YIELD;
lua_unlock(co);
return -1;
}
lua_yield將協(xié)程置為L(zhǎng)UA_YIELD狀態(tài),設(shè)置了co->base,然后一路返回到auxresume:
static int auxresume (lua_State *L, lua_State *co, int narg) {
...
status = lua_resume(co, narg);
if (status == 0 || status == LUA_YIELD) {
int nres = lua_gettop(co);
if (!lua_checkstack(L, nres + 1))
luaL_error(L, "too many results to resume");
lua_xmove(co, L, nres); /* move yielded values */
return nres;
}
else {
lua_xmove(co, L, 1); /* move error message */
return -1; /* error flag */
}
}
因?yàn)閘ua_yield設(shè)置的co->base,lua_gettop(co)返回coroutine.yield的參數(shù)個(gè)數(shù),將這些參數(shù)轉(zhuǎn)移到主線程后,便從coroutine.resume返回了。
注意到luaD_precall中發(fā)生yield時(shí),并沒(méi)有調(diào)用luaD_poscall,當(dāng)協(xié)程恢復(fù)時(shí)調(diào)用luaD_poscall相當(dāng)于把coroutine.resume的參數(shù)做為coroutine.yield的返回值,完成coroutine.yield的調(diào)用。通過(guò)這個(gè)流程就可以看出yield不能跨c函數(shù)。
柚子快報(bào)激活碼778899分享:lua的數(shù)據(jù)類型,lua
推薦閱讀
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。