请选择 进入手机版 | 继续访问电脑版

risc-v中文社区

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

Lua源码分析 - 栈结构篇 - 栈操作函数的实现(04)

[复制链接]

347

主题

564

帖子

2237

积分

管理员

Rank: 9Rank: 9Rank: 9

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

一、Lua栈操作 - 引用类型在栈上处理方式

二、Lua栈操作 - 入栈操作

三、Lua栈操作 - 栈类型操作

四、Lua栈操作 - 从栈上获取数据

五、Lua栈操作 - 其它栈操作

上一章节讲解了Lua的栈结构,理解了上一篇的栈接口在看本片应该比较好理解。

Lua常用的栈操作API主要在lapi.c(lapi.c也提供给外部使用)文件中。

一、Lua栈操作 - 引用类型在栈上处理方式
Lua针对需要垃圾回收的元素,在压入栈时,都会在Lua(也就是Lua虚拟机中)生成一个副本。
C里面的值,被压入栈之后,lua不会在依赖这个值,而是通过拷贝副本的方式,自己管理对应的这个值了。
以lua_pushstring为例,我们填入一个字符串值的时候,lua栈函数,会将字符串生成一个内部副本。Lua不会持有指向外部字符串的指针,也不会持有指向任何其他外部对象的指针(除了C函数,因为C函数总是静态的)

/**
* 压入一个字符串到栈L->top上
* 会拷贝一个副本
*/
LUA_API const char *lua_pushstring (lua_State *L, const char *s) {
  lua_lock(L);
  if (s == NULL)
    setnilvalue(L->top);
  else {
    TString *ts;
    ts = luaS_new(L, s); //拷贝一个副本
    setsvalue2s(L, L->top, ts);
    s = getstr(ts);  /* internal copy's address */
  }
  api_incr_top(L);
  luaC_checkGC(L);
  lua_unlock(L);
  return s;
}
二、Lua栈操作 - 入栈操作
/**
* 压入一个nil类型的栈到L->top上
*/
LUA_API void lua_pushnil (lua_State *L)

/**
* 压入一个浮点数字到栈L->top上
*/
LUA_API void lua_pushnumber (lua_State *L, lua_Number n)

/**
* 压入一个int类型数字到栈L->top上
*/
LUA_API void lua_pushinteger (lua_State *L, lua_Integer n)

/**
* 压入一个字符串类型到栈L->top上
*/
LUA_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len)

/**
* 压入一个字符串到栈L->top上
*/
LUA_API const char *lua_pushstring (lua_State *L, const char *s)

/**
* 压入字符串到栈L->top上
*/
LUA_API const char *lua_pushvfstring (lua_State *L, const char *fmt,
                                      va_list argp)

/**
* 压入字符串到栈L->top上
*/
LUA_API const char *lua_pushfstring (lua_State *L, const char *fmt, ...)

/**
* 在L->top栈上设置一个function
* c语言闭包函数
*/
LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n)

/**
* 压入布尔值到L->top栈上
*/
LUA_API void lua_pushboolean (lua_State *L, int b)

/**
* 压入用户数据地址到L->top栈上
*/
LUA_API void lua_pushlightuserdata (lua_State *L, void *p)

/**
* 创建一个lua新线程,并将其压入栈。lua线程不是OS线程
* LUA的线程更多理解上是协程
*/
LUA_API int lua_pushthread (lua_State *L)
三、Lua栈操作 - 栈类型操作
/**
* 栈类型。TValue栈上是方法、数字、nil等类型
*/
LUA_API int lua_type (lua_State *L, int idx)

/**
* 类型编号转成类型名称
* 类型数组: luaT_typenames_[LUA_TOTALTAGS]
* 类型:nil=null boolean=布尔 function=方法 string=字符串
*/
LUA_API const char *lua_typename (lua_State *L, int t)

/**
* 判断是否为function栈
*/
LUA_API int lua_iscfunction (lua_State *L, int idx)

/**
* 判断是否为int类型栈
*/
LUA_API int lua_isinteger (lua_State *L, int idx)

/**
* 判断是否为int类型
*/
LUA_API int lua_isnumber (lua_State *L, int idx)

/**
* 判断是否为数字类型
*/
LUA_API int lua_isstring (lua_State *L, int idx)

/**
* 判断是否为数字类型
*/
LUA_API int lua_isuserdata (lua_State *L, int idx)

/**
* 判断两个栈是否一样,如果一样返回1,否则返回0
*/
LUA_API int lua_rawequal (lua_State *L, int index1, int index2)

四、Lua栈操作 - 从栈上获取数据
该lua_to*批次函数,主要通过index2addr函数,寻找到操作栈CallInfo上的栈指针地址,然后获取数据。

idx > 0,则从操作栈底部开始寻找值
idx < 0,则从操作栈栈顶开始寻找值
例如:

/* lua_toboolean idx=-1 从操作栈栈顶获取最后的值 */
lua_pushcfunction(L, &pmain);  /* 将pmain放入L结构上 L->top值&pmain*/
lua_pushinteger(L, argc);  /* 将argc 放入L结构上  L->top值argc*/
lua_pushlightuserdata(L, argv); /* 将argv 放入L结构上 L->top值argv*/
status = lua_pcall(L, 2, 1, 0);  /* 函数操作,执行pmain 函数 do the call */
result = lua_toboolean(L, -1);  /* 获取pmain函数lua_pushboolean(L, 1) 的信号值get result */

/* idx=1 则获取操作栈从底部开始的第2个栈内容,第一个栈内容未ci->func 函数pmain的栈 */
static int pmain (lua_State *L) {
int argc = (int)lua_tointeger(L, 1); //从L->top结构上,获取argc参数  对应函数:lua_pushinteger
}

/**
* 通过指定索引值idx,寻找调用栈上L->top的栈分片TValue值
* 栈顶=idx=-1
* 栈底=idx=1
*/
static TValue *index2addr (lua_State *L, int idx) {
  CallInfo *ci = L->ci;

  /**
   * idx>0 则通过栈底到栈尾寻地址方法
   */
  if (idx > 0) {
    TValue *o = ci->func + idx;
    api_check(L, idx <= ci->top - (ci->func + 1), "unacceptable index");
    if (o >= L->top) return NONVALIDVALUE;
    else return o;
  }
  else if (!ispseudo(idx)) {  /* negative index */
    api_check(L, idx != 0 && -idx <= L->top - (ci->func + 1), "invalid index");
    return L->top + idx;
  }
  else if (idx == LUA_REGISTRYINDEX)
    return &G(L)->l_registry;
  /**
   * idx<0 则通过顶到栈底寻地址方法
   */
  else {  /* upvalues */
    idx = LUA_REGISTRYINDEX - idx;
    api_check(L, idx <= MAXUPVAL + 1, "upvalue index too large");
    if (ttislcf(ci->func))  /* light C function? */
      return NONVALIDVALUE;  /* it has no upvalues */
    else {
      CClosure *func = clCvalue(ci->func);
      return (idx <= func->nupvalues) ? &func->upvalue[idx-1] : NONVALIDVALUE;
    }
  }
}


/**
* 给定索引处的 Lua 值转换为 lua_Number 这样一个 C 类型
*/
LUA_API lua_Number lua_tonumberx (lua_State *L, int idx, int *pisnum)

/**
* 把给定索引处的 Lua 值转换为 lua_Integer 这样一个有符号整数类型
* 必须:数字/字符串类型数字
*/
LUA_API lua_Integer lua_tointegerx (lua_State *L, int idx, int *pisnum)

/**
* 把指定的索引处的的 Lua 值转换为一个 C 中的 boolean 值( 0 或是 1 )。 和 Lua 中做的所有测试一样,
* lua_toboolean 会把任何 不同于 false 和 nil 的值当作 1 返回; 否则就返回 0 。 如果用一个无效索引去调用也会返回 0 。
*/
LUA_API int lua_toboolean (lua_State *L, int idx)

/**
* 给定索引处的 Lua 值转换为一个 C 字符串
*  如果 len 不为 NULL ,它还把字符串长度设到 *len 中。 这个 Lua 值必须是一个字符串或是一个数字; 否则返回返回 NULL 。
*  如果值是一个数字,lua_tolstring 还会把堆栈中的那个值的实际类型转换为一个字符串。
*/
LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len)

/**
* 给定索引处的 Lua 值转换为一个 C 函数
*/
LUA_API lua_CFunction lua_tocfunction (lua_State *L, int idx)

/**
* 给定索引处的值是一个完整的 userdata
*/
LUA_API void *lua_touserdata (lua_State *L, int idx)

/**
* 把给定索引处的值转换为一个 Lua 线程(由 lua_State* 代表)。 这个值必须是一个线程;否则函数返回 NULL 。
*/
LUA_API lua_State *lua_tothread (lua_State *L, int idx)

/**
* 把给定索引处的值转换为一般的 C 指针 (void*) 。
* 这个值可以是一个 userdata ,table ,thread 或是一个 function
*/
LUA_API const void *lua_topointer (lua_State *L, int idx)
五、Lua栈操作 - 其它栈操作
/**
* 通过指定索引值idx,寻找栈上L->top的栈分片TValue值
* 栈顶=idx=-1
* 栈底=idx=1
*/
static TValue *index2addr (lua_State *L, int idx)

/**
* 针对lua_State进行扩容
*/
static void growstack (lua_State *L, void *ud)

/**
* 检查lua_State的大小,如果栈小了,则扩容(默认栈大小:栈的默认尺寸是35)
* 说明:只会不断扩容,不会缩小
* 32/64位机器栈最大:1000000
* 16位机器栈最大:15000
*/
LUA_API int lua_checkstack (lua_State *L, int n)

/**
* 从*from虚拟机结构上向*to虚拟机结构上拷贝n个栈分片内容
*/
LUA_API void lua_xmove (lua_State *from, lua_State *to, int n)

/**
* 返回LUA 栈的个数
* 同时也是栈顶元素的索引,因为栈底是1
*/
LUA_API int lua_gettop (lua_State *L)

/**
* 设置栈的高度,如果之前的栈顶比新设置的更高,那么高出来的元素会被丢弃,反之压入nil来补足大小
*/
LUA_API void lua_settop (lua_State *L, int idx)


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

回复

使用道具 举报

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

本版积分规则



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

GMT+8, 2024-4-16 20:37 , Processed in 0.020896 second(s), 17 queries .

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

Copyright © 2018-2021, risc-v open source

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