risc-v中文社区

 找回密码
 立即注册
查看: 1318|回复: 0

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

[复制链接]

347

主题

564

帖子

2237

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
2237
发表于 2022-6-20 15:50:16 | 显示全部楼层 |阅读模式
目录

一、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[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的值,并将值放入堆栈顶部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);
}

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

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



Archiver|手机版|小黑屋|risc-v中文社区

GMT+8, 2024-4-19 11:25 , Processed in 0.018195 second(s), 17 queries .

risc-v中文社区论坛 官方网站

Copyright © 2018-2021, risc-v open source

快速回复 返回顶部 返回列表