目录
Lua通过实现全局的注册表,来管理全局变量、C API扩展库的加载等信息。
注册表主要通过Table的数据结构进行管理,所以注册表是一个多维数组的结构。本章我们主要讲解Lua的注册表的整体操作方式。
一、Lua注册表 - 实现机制l_registry
前面我们说过,Lua的注册表是通过Table的结构实现了一个多维数组。而这个全局的多维数组,在global_State全局状态机上进行管理。
/*
** 'global state', shared by all threads of this state
** lua 全局状态机
** 作用:管理全局数据,全局字符串表、内存管理函数、 GC 把所有对象串联起来的信息、内存等
*/
typedef struct global_State {
....
TValue l_registry;
....
一般情况下,全局注册表通过lua_setfield和lua_getfield两个函数跟Lua的主线程栈进行通信。
- lua_setfield:将栈顶的值L->top-1,赋值到T[k]上,并将栈顶数据pop弹出(L->top--)。说白了,当我们向一个Table结构进行设置值的时候,将栈顶的L->top-1的值,设置到指定Table的指定索引上,并将栈顶值弹出。
- lua_getfield:从T[k]上取到一个值,并将值放置到L->top栈顶上,并调整栈顶(L->top++)。我们要从全局注册表中获取一个数据值的时候,需要将注册表中的值压入栈顶,该值才能被后续的Lua程序顺利调用。
二、Lua注册表 - 初始化init_registry
注册表G->l_registry,是在主线程栈创建的时候进行初始化的。
- 初始化第一步,创建了一个Table类型的表,并将表赋值到G->l_registry
- 然后默认初始化LUA_RIDX_MAINTHREAD(主线程索引)和LUA_RIDX_GLOBALS(全局环境变量)
/*
** Create registry table and its predefined values
** 创建一个注册表,并且定义默认值
*/
static void init_registry (lua_State *L, global_State *g) {
TValue temp;
/* create registry */
Table *registry = luaH_new(L); //创建一个Table表
sethvalue(L, &g->l_registry, registry);
luaH_resize(L, registry, LUA_RIDX_LAST, 0);
/* registry[LUA_RIDX_MAINTHREAD] = L */
setthvalue(L, &temp, L); /* temp = L */
luaH_setint(L, registry, LUA_RIDX_MAINTHREAD, &temp);
/* registry[LUA_RIDX_GLOBALS] = table of globals */
sethvalue(L, &temp, luaH_new(L)); /* temp = new table (global table) */
luaH_setint(L, registry, LUA_RIDX_GLOBALS, &temp);
}
三、Lua注册表 - 设置值lua_setfield
lua_setfield函数主要用于向Table设置值。
- L:当前执行环境栈
- idx:找Table的索引值。如果是全局注册表,则idx=LUA_REGISTRYINDEX;如果是在栈上的一个Table表,则数字索引,通过index2addr方法寻找栈对象。
- k:Table表的索引键
- 说明:将栈顶的值L->top-1,赋值到T[k]上,并弹出栈顶值(L->top--)。操作注册表,实际上就是操作一个特殊的Table结构。
/**
* 将栈顶的值L->top,赋值到T[k]上,并调整L->top指针L->top--,pop弹出栈顶值
* idx索引(栈地址 or 全局注册表)
* LUA_REGISTRYINDEX[c]=L->top-1
* Table[x]=L->top-1
*
* lua_setfield(L, LUA_REGISTRYINDEX, "c");// 将栈顶的值 赋值 到全局变量[c]中
* lua_setfield(L, idx, "x") //将栈顶的值 赋值 到栈idx[x]上
*/
LUA_API void lua_setfield (lua_State *L, int idx, const char *k) {
lua_lock(L); /* unlock done in 'auxsetstr' */
auxsetstr(L, index2addr(L, idx), k);
}
/*
** t[k] = value at the top of the stack (where 'k' is a string)
*/
static void auxsetstr (lua_State *L, const TValue *t, const char *k) {
const TValue *slot;
TString *str = luaS_new(L, k);
api_checknelems(L, 1);
if (luaV_fastset(L, t, str, slot, luaH_getstr, L->top - 1))
L->top--; /* pop value */
else {
setsvalue2s(L, L->top, str); /* push 'str' (to make it a TValue) */
api_incr_top(L);
luaV_finishset(L, t, L->top - 1, L->top - 2, slot);
L->top -= 2; /* pop value and key */
}
lua_unlock(L); /* lock done by caller */
}
其它还有一系列相关Table表操作,直接上代码,相对比较简单。
/**
* 将栈顶的值,设置到全局变量中去,以LUA_RIDX_GLOBALS[name]=L->top,并调整L->top指针L->top--,pop弹出栈顶值
*/
LUA_API void lua_setglobal (lua_State *L, const char *name) {
Table *reg = hvalue(&G(L)->l_registry);
lua_lock(L); /* unlock done in 'auxsetstr' */
auxsetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name);
}
/**
* 将栈顶的值,设置到Table中去,并调整L->top指针L->top-2,pop弹出栈顶值
* L->top-2 为Key
* L->top-1 为Value
*/
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);
}
四、Lua注册表 - 获取值lua_getfield
lua_getfield函数主要用于向Table获取值。
- L:当前执行环境栈
- idx:找Table的索引值。如果是全局注册表,则idx=LUA_REGISTRYINDEX;如果是在栈上的一个Table表,则数字索引,通过index2addr方法寻找栈对象。
- k:Table表的索引键
- 说明:从T[k]上取到一个值,并将值放置到L->top栈顶上,并调整栈顶(L->top++)。操作注册表,实际上就是操作一个特殊的Table结构。
/**
* 把 t[k] 值压入堆栈顶部, 这里的 t 是指有效索引 index 指向的值
* L->top=LUA_LOADED_TABLE[k]
* L->top=Table[k]
*
* lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE) //从全局注册表上找LUA_LOADED_TABLE,放置到L->top
* lua_getfield(L, 1, 'k') //从栈上找对对应Table,并将T[k]的值取出,压入堆栈L->top
*/
LUA_API int lua_getfield (lua_State *L, int idx, const char *k) {
lua_lock(L);
return auxgetstr(L, index2addr(L, idx), k);
}
static int auxgetstr (lua_State *L, const TValue *t, const char *k) {
const TValue *slot;
TString *str = luaS_new(L, k);
/* 如果能找到值,则压入堆栈 */
if (luaV_fastget(L, t, str, slot, luaH_getstr)) {
setobj2s(L, L->top, slot);
api_incr_top(L);
}
else {
/* 取不到情况处理 */
setsvalue2s(L, L->top, str);
api_incr_top(L);
luaV_finishget(L, t, L->top - 1, L->top - 1, slot);
}
lua_unlock(L);
return ttnov(L->top - 1);
}
其它的操作方法,如下:
/**
* 从全局注册表中,获取一个T[name]的值,放入L->top
* L->top=LUA_RIDX_GLOBALS[name]
*/
LUA_API int lua_getglobal (lua_State *L, const char *name) {
Table *reg = hvalue(&G(L)->l_registry);
lua_lock(L);
return auxgetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name);
}
/**
* 从Table表中,获取一个T[name]的值,放入L->top
* L->top-2 为k
* L->top-1 为v
*/
LUA_API int lua_gettable (lua_State *L, int idx) {
StkId t;
lua_lock(L);
t = index2addr(L, idx);
luaV_gettable(L, t, L->top - 1, L->top - 1);
lua_unlock(L);
return ttnov(L->top - 1);
}
/**
* 通过数组下标获取T[i]的值,并将值放入堆栈顶部L->top
* L->top=Table[n]
*/
LUA_API int lua_geti (lua_State *L, int idx, lua_Integer n) {
StkId t;
const TValue *slot;
lua_lock(L);
t = index2addr(L, idx);
if (luaV_fastget(L, t, n, slot, luaH_getint)) {
setobj2s(L, L->top, slot);
api_incr_top(L);
}
else {
setivalue(L->top, n);
api_incr_top(L);
luaV_finishget(L, t, L->top - 1, L->top - 1, slot);
}
lua_unlock(L);
return ttnov(L->top - 1);
}