lua函數定義
FuncState
proto結構數組保存函數原型信息;prev保存父函數體指針;actvar保存定義的局部變量;upvalues保存upvalue
Lua源碼中,專門有一個結構體FuncState用來保存函數相關的信息.其實,即使沒有創建任何函數,對於Lua而言也有一個最外層的FuncState數據.這個結構體的定義:
typedef struct FuncState { Proto *f; /* current function header */ Table *h; /* table to find (and reuse) elements in */ struct FuncState *prev; /* enclosing function */ struct LexState *ls; /* lexical state */ struct lua_State *L; /* copy of the Lua state */ struct BlockCnt *bl; /* chain of current blocks */ int pc; /* next position to code (equivalent to `ncode‘) */ int lasttarget; /* `pc‘ of last `jump target‘ */ int jpc; /* list of pending jumps to `pc‘ */ int freereg; /* first free register */ int nk; /* number of elements in `k‘ */ int np; /* number of elements in `p‘ */ short nlocvars; /* number of elements in `locvars‘ */ lu_byte nactvar; /* number of active local variables */ upvaldesc upvalues[LUAI_MAXUPVALUES]; /* upvalues */ unsigned short actvar[LUAI_MAXVARS]; /* declared-variable stack */ } FuncState;
其中的Proto結構體數組用於保存函數原型信息,包括函數體代碼(opcode),之所以使用數組,是因為在某個函數內,可能存在多個局部函數.而prev指針就是指向這個函數的”父函數體”的指針.
比如以下代碼:
function fun()
function test()
end
end
那麽,在保存test函數原型的Proto數據就存放在保存fun函數的FuncState結構體的p數組中,反之,保存test函數的FuncState.prev指針就指向保存func函數的FuncState指針.
接著看Funcstate結構體的成員,actvar數組用於保存局部變量,比如函數的參數就是保存在這裏.另外還有一個存放upval值的upvalues數組.這裏有兩種不同的處理.如果這個upval是父函數內的局部變量,則生成的是MOVE指令用於賦值;如果對於父函數而言也是它的upval,則生成GET_UPVAL指令用於賦值.
當開始處理一個函數的定義時,首先調用open_func函數,創建一個新的Proto結構體用於保存函數原型信息,接著將該函數的FuncState的prev指針指向父函數.
最後當函數處理完畢時,調用pushclosure函數將這個新的函數的信息push到父函數的Proto數組中.
函數也是第一類值 可以存在變量裏
最後,由於函數在Lua中是所謂的”first class type”,所以其實以下兩段Lua代碼是等價的:
local function test() -- 可以test end --以上相當於 local test; test = function() ... end local test = function () --不可以調用test 以為第一類之定義完成之後才可以使用 end
也就是說,其實是生成一段代碼,用於保存函數test的相關信息,之後再將這些信息賦值給變量test,這裏的test可以是local,也可以是global的,這一點跟一般的變量無異.
函數定義詞法分析
所以在與函數定義相關的詞法分析代碼中:
static void funcstat (LexState *ls, int line) {
/* funcstat -> FUNCTION funcname body */
int needself;
expdesc v, b;
luaX_next(ls); /* skip FUNCTION */
needself = funcname(ls, &v);
body(ls, &b, needself, line);
luaK_storevar(ls->fs, &v, &b);
luaK_fixline(ls->fs, line); /* definition `happens‘ in the first line */
}
上面的變量v首先在funcname函數中獲得該函數的函數名,變量b在進入函數body之後可以得到函數體相關的內容.在這之後的luaK_storevar調用,就是把b的值賦值給v,也就是前面提到的函數體賦值給函數名.
lua函數定義