lua的栈

lua的栈

lua的栈

lua中有两种栈:数据栈和调用栈.

栈的定义和结构

lua中的数据可以分为两类:值类型和引用类型,值类型可以被任意复制,而引用类型共享一份数据,复制时只是复制其引用,并由GC负责维护其生命期.lua使用一个unine Value来保存数据.

union Value {  
    GCObject *gc;    /* collectable objects */  
    void *p;         /* light userdata */  
    int b;           /* booleans */  
    lua_CFunction f; /* light C functions */  
    numfield         /* numbers */  
};  

引用类型用一个指针GCObject *gc来间接引用,而其它值类型都直接保存在联合中.

在lua_State中栈的保存如下:

struct lua_State {  
    StkId top;          /* first free slot in the stack */
    StkId stack_last;   /* last free slot in the stack */  
    StkId stack;        /* stack base */  
    int stacksize;
    CallInfo base_ci;  /* CallInfo for first level (C calling Lua) */
    ...
}  

StkId的定义:

typedef TValue *StkId;  
typedef struct lua_TValue TValue;  
struct lua_TValue {  
    TValuefields;  
};  

#define TValuefields    Value value_; int tt_  

在Windows VC下:

#define TValuefields  \  
union { struct { Value v__; int tt__; } i; double d__; } u  

这里使用了繁杂的宏定义,TValuefields和numfield是为了应用一个被称为NaN Trick的技巧.

lua初始化堆栈:

static void stack_init (lua_State *L1, lua_State *L) {
    int i; CallInfo *ci;
    /* initialize stack array */
    L1->stack = luaM_newvector(L, BASIC_STACK_SIZE, TValue);        //BASIC_STACK_SIZE为40
    L1->stacksize = BASIC_STACK_SIZE;
    for (i = 0; i < BASIC_STACK_SIZE; i++)
        setnilvalue(L1->stack + i);  /* erase new stack */          //将tt_字段设置为0
    L1->top = L1->stack;
    L1->stack_last = L1->stack + L1->stacksize - EXTRA_STACK;       //EXTRA_STACK为5
    /* initialize first ci */
    ci = &L1->base_ci;
    ci->next = ci->previous = NULL;
    ci->callstatus = 0;
    ci->func = L1->top;
    setnilvalue(L1->top++);  /* 'function' entry for this 'ci' */
    ci->top = L1->top + LUA_MINSTACK;
    L1->ci = ci;
}

初始化之后,lua_State栈的情况:

lua供C使用的栈相关API是不检查数据栈越界的,因为通常编写C扩展都能把数据栈空间的使用控制在BASIC_STACK_SIZE以内,或是显式扩展.对每次数据栈访问都强制做越界检查是非常低效的.
数据栈不够用时,可以使用luaD_reallocstack\luaD_growstack函数扩展,每次至少分配比原来大一倍的空间.

/* some space for error handling */  
#define ERRORSTACKSIZE  (LUAI_MAXSTACK + 200)  

void luaD_reallocstack (lua_State *L, int newsize) {        
    TValue *oldstack = L->stack;        
    int lim = L->stacksize;     
    lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE);  
    lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK);  
    luaM_reallocvector(L, L->stack, L->stacksize, newsize, TValue);
    for (; lim < newsize; lim++)   
        setnilvalue(L->stack + lim); /* erase new segment */  
    L->stacksize = newsize;  
    L->stack_last = L->stack + newsize - EXTRA_STACK;  
    correctstack(L, oldstack);  
}  

void luaD_growstack (lua_State *L, int n) {  
    int size = L->stacksize;  
    if (size > LUAI_MAXSTACK)  /* error after extra size? */  
        luaD_throw(L, LUA_ERRERR);  
    else {  
        int needed = cast_int(L->top - L->stack) + n + EXTRA_STACK;  
        int newsize = 2 * size;  
        if (newsize > LUAI_MAXSTACK) newsize = LUAI_MAXSTACK;  
        if (newsize < needed) newsize = needed;  
        if (newsize > LUAI_MAXSTACK) {  /* stack overflow? */  
            luaD_reallocstack(L, ERRORSTACKSIZE);  
            luaG_runerror(L, "stack overflow");  
        }  
        else   
            luaD_reallocstack(L, newsize);  
    }  
}  

数据栈扩展的过程,伴随着数据拷贝,这些数据都是可能直接值复制的,所有不需要在扩展之后修正其中的指针.但有此外部对数据栈的引用需要修正为正确的新地址.这些需要修正的位置包括upvalue以及执行对数据栈的引用.此过程由correctstack函数实现.

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->ci; ci != NULL; ci = ci->previous) {  
        ci->top = (ci->top - oldstack) + L->stack;  
    ci->func = (ci->func - oldstack) + L->stack;  
    if (isLua(ci))  
        ci->u.l.base = (ci->u.l.base - oldstack) + L->stack;  
    }  
}  

栈的使用

入栈的函数或宏:

lua_pushnil\lua_pushnumber\lua_pushinteger\lua_pushunsigned\lua_pushlstring\lua_pushstring\lua_pushvfstring\lua_pushfstring\lua_pushcclosure\lua_pushboolean\lua_pushlightuserdata\lua_pushthread\lua_newtable\lua_register\lua_pushcfunction;  
lua_getglobal\lua_gettable\lua_getfield\lua_rawget\lua_rawgeti\lua_rawgetp\lua_createtable\lua_newuserdata\lua_getmetatable\lua_getuservalue  

lua_pushnumber定义如下:

LUA_API void lua_pushinteger (lua_State *L, lua_Integer n) {  
    lua_lock(L);  
    //VS下展开之后:TValue *io_=(L->top); ((io_)->u.d__)=(n); ((void)0);  
    setnvalue(L->top, cast_num(n));  
    //VS下展开之后:L->top++; ((void)0);  
    api_incr_top(L);  
    lua_unlock(L);  
}  

lua_newtable的定义如下:

#define lua_newtable(L)     lua_createtable(L, 0, 0)  
LUA_API void lua_createtable (lua_State *L, int narray, int nrec) {     
    Table *t;   
    lua_lock(L);  
    luaC_checkGC(L);  
    t = luaH_new(L);  
    //VC下展开之后:TValue *io=(L->top); ((io)->u.i.v__).gc=((GCObject *)((t)));  
    //(((io)->u.i.tt__) = (0x7FF7A500 | (((5) | (1 << 6))))); ((void)0);  
    sethvalue(L, L->top, t); 
    //VC下展开之后:L->top++; ((void)0);  
    api_incr_top(L);   
    if (narray > 0 || nrec > 0)  
    luaH_resize(L, t, narray, nrec);  
    lua_unlock(L);  
}  

示例代码如下:

lua_State* L = luaL_newstate();
lua_pushnumber(L, 1);
lua_newtable(L);

lua_pushinteger(L, 1)之后,栈的情况:

lua_newtable(L)之后,栈的情况:

出栈的函数或宏:
lua_setglobal\lua_settable\lua_setfield\lua_rawset\lua_rawseti\lua_rawsetp\lua_setmetatable\lua_setuservalue
从栈中获取数据的函数或宏:
luaL_checklstring\luaL_checknumber\luaL_checkinteger\luaL_checkunsigned
lua_settable的定义如下:

LUA_API void lua_settable (lua_State *L, int idx) {  
    StkId t;  
    lua_lock(L);  
    api_checknelems(L, 2);  
    t = index2addr(L, idx);  
    luaV_settable(L, t, L->top - 2, L->top - 1);  
    L->top -= 2;  /* pop index and value */  
    lua_unlock(L);  
}  

luaL_checknumber的定义如下:

LUALIB_API lua_Number luaL_checknumber (lua_State *L, int narg) {  
    int isnum;  
    lua_Number d = lua_tonumberx(L, narg, &isnum);  
    if (!isnum)  
        tag_error(L, narg, LUA_TNUMBER);  
    return d;  
}  

示例代码如下(lua库的luaopen_base函数,用于注册):

#define lua_pushglobaltable(L)  \
    lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS)

LUALIB_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) {
    luaL_checkversion(L);
    luaL_checkstack(L, nup, "too many upvalues");
    for (; l->name != NULL; l++) {  /* fill the table with given functions */
        int i;
        for (i = 0; i < nup; i++)  /* copy upvalues to the top */
            lua_pushvalue(L, -nup);
        lua_pushcclosure(L, l->func, nup);  /* closure with those upvalues */
        lua_setfield(L, -(nup + 2), l->name);
    }
    lua_pop(L, nup);  /* remove upvalues */
}

LUAMOD_API int luaopen_base (lua_State *L) {  
    /* set global _G */  
    lua_pushglobaltable(L);  
    lua_pushglobaltable(L);  
    lua_setfield(L, -2, "_G");       
    /* open lib into global table */  
    luaL_setfuncs(L, base_funcs, 0);  
    lua_pushliteral(L, LUA_VERSION);  
    lua_setfield(L, -2, "_VERSION");  /* set global _VERSION */  
    return 1;  
}  

luaopen_base函数设置全局注册表的"_G"字段,base_funcs指定的函数列表,"_VERSION"字段.
第一个lua_pushglobaltable(L)之后:

第二个lua_pushglobaltable(L)之后:

lua_setfield(L, -2, "_G")之后:

luaL_setfuncs(L, base_funcs, 0)之后:

lua_pushliteral(L, LUA_VERSION)之后:

lua_setfield(L, -2, "_VERSION")

此时栈顶下的第一个元素为luaopen_base函数生成的table,返回值为1,标记函数的返回参数个数为1.

参考资源

 PS:个人新干博客地址 http://www.lontoken.com/

posted @ 2013-12-24 12:53  lontoken  阅读(2470)  评论(0编辑  收藏  举报