Lua源码分析 - 栈结构篇 - 栈操作函数的实现(04)
上一章节讲解了Lua的栈结构,理解了上一篇的栈接口在看本片应该比较好理解。Lua常用的栈操作API主要在lapi.c(lapi.c也提供给外部使用)文件中。Lua栈操作 - 垃圾回收值处理Lua针对需要垃圾回收的元素,在压入栈时,都会在Lua(也就是Lua虚拟机中)生成一个副本。总之,C里面的值,被压入栈之后,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)
更多推荐
已为社区贡献13条内容
所有评论(0)