joe 发表于 2022-6-20 15:50:16

Lua源码分析 - 主流程篇 - 注册表的实现(11)

目录

一、Lua注册表 - 实现机制l_registry

二、Lua注册表 - 初始化init_registry

三、Lua注册表 - 设置值lua_setfield

四、Lua注册表 - 获取值lua_getfield

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上,并将栈顶数据pop弹出(L->top--)。说白了,当我们向一个Table结构进行设置值的时候,将栈顶的L->top-1的值,设置到指定Table的指定索引上,并将栈顶值弹出。
lua_getfield:从T上取到一个值,并将值放置到L->top栈顶上,并调整栈顶(L->top++)。我们要从全局注册表中获取一个数据值的时候,需要将注册表中的值压入栈顶,该值才能被后续的Lua程序顺利调用。

https://img-blog.csdnimg.cn/20200217174733490.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2luaXRwaHA=,size_16,color_FFFFFF,t_70


二、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 = L */
setthvalue(L, &temp, L);/* temp = L */
luaH_setint(L, registry, LUA_RIDX_MAINTHREAD, &temp);
/* registry = 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上,并弹出栈顶值(L->top--)。操作注册表,实际上就是操作一个特殊的Table结构。
/**
* 将栈顶的值L->top,赋值到T上,并调整L->top指针L->top--,pop弹出栈顶值
* idx索引(栈地址 or 全局注册表)
* LUA_REGISTRYINDEX=L->top-1
* Table=L->top-1
*
* lua_setfield(L, LUA_REGISTRYINDEX, "c");// 将栈顶的值 赋值 到全局变量中
* lua_setfield(L, idx, "x") //将栈顶的值 赋值 到栈idx上
*/
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 = 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=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上取到一个值,并将值放置到L->top栈顶上,并调整栈顶(L->top++)。操作注册表,实际上就是操作一个特殊的Table结构。
/**
* 把 t 值压入堆栈顶部, 这里的 t 是指有效索引 index 指向的值
* L->top=LUA_LOADED_TABLE
* L->top=Table
*
* lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE) //从全局注册表上找LUA_LOADED_TABLE,放置到L->top
* lua_getfield(L, 1, 'k') //从栈上找对对应Table,并将T的值取出,压入堆栈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的值,放入L->top
* L->top=LUA_RIDX_GLOBALS
*/
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的值,放入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的值,并将值放入堆栈顶部L->top
* L->top=Table
*/
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);
}

————————————————
版权声明:本文为CSDN博主「老码农zhuli」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/initphp/article/details/104318182

页: [1]
查看完整版本: Lua源码分析 - 主流程篇 - 注册表的实现(11)