【lua学习】7.环境

1 最重要的两个数据结构

1.1 lua_State(Lua虚拟机/Lua协程)

每个lua虚拟机(协程)对应一个lua_State结构体

(lstate.h) lua_State

struct lua_State
{
	CommonHeader;//#define CommonHeader	GCObject *next; lu_byte tt; lu_byte marked
	lu_byte status;//协程的状态码
	StkId top;//栈顶位置,“寄存器”第一个可用位置
	StkId base;//当前函数调用的栈基址
	global_State* l_G;//全局状态机
	CallInfo* ci;//当前函数调用信息
	const Instruction* savedpc;//指令指针
	StkId stack_last;//“寄存器”最后一个可用位置
	StkId stack;//栈数组的起始位置
	CallInfo* end_ci;//函数调用信息数组的最后一个位置的下一个位置
	CallInfo* base_ci;//函数调用信息数组首地址
	int stacksize;//栈的大小
	int size_ci;//函数调用信息数组大小
	unsigned short nCcalls;//内嵌C调用的层数
	unsigned short baseCcalls;//唤醒协程时的内嵌C调用层数
	lu_byte hookmask;
	lu_byte allowhook;
	int basehookcount;
	int hookcount;
	lua_Hook hook;
	TValue l_gt;//Global表
	TValue env;//环境表的临时位置
	GCObject* openupval;//栈上open状态的uvalues
	GCObject* gclist;
	struct lua_longjmp* errorJmp;//当前跳转信息,实现try catch的关键结构
	ptrdiff_t errfunc;//当前错误处理函数相对于“寄存器数组首地址”的偏移地址
};

1.2 global_State(Lua全局状态)

(lstate.h) global_State

typedef struct global_State
{
	stringtable strt;//全局字符串表
	lua_Alloc freealloc;//内存重分配函数
	void* ud;//freealloc的辅助数据
	lu_byte currentwhite;//当前白色,见GC章节
	lu_byte gcstate;//GC状态,见GC章节
	int sweepstrgc;//strt中GC扫描到的位置
	GCObject* rootgc;//所有可回收对象的链表
	GCObject** sweepgc;//rootgc中扫描到的位置
	GCObject* gray;//灰色对象链表
	GCObject* grayagain;//需要被原子性遍历地对象链表
	GCObject* week;//弱表的链表
	GCObject* tmudata;//需要被GC的userdata的链表的最后一个元素
	Mbuffer* buff;//字符串连接操作用的临时缓冲对象
	lu_mem GCthreshold;//触发GC的边界值,见GC章节
	lu_mem totalbytes;//当前分配的总字节数
	lu_mem estimate;//实际上使用的总字节数的估计值
	lu_mem gcdept;//在预定的回收字节数中,还欠多少字节没有回收
	int gcpause;//连续的GC中的停顿步长相关值,见GC章节
	int gcstepmul;//GC的粒度
	lua_CFunction panic;//对于未捕获异常,会调用这个函数
	TValue l_registry;//注册表
	struct lua_State* mainthread;//主协程
	UpVal uvhead;//open状态的upvalue双链表的头部
	struct Table* mt[NUM_TAGS];//存放 基本类型的元表 的数组
	TString* tmname[TM_N];//存放 元方法名字符串对象地址 的数组
}

2 环境相关的变量

2.1 Global表

Global表存放在lua_State结构体中,每个lua_State实例都有一个对应的Global表,对于一个lua_State这个表就是用来存放全局变量的。

2.1.1 Global表在lua_State结构中

struct lua_State
{
	//..//
	TValue l_gt;//Global表
	//..//
}

2.1.2 Global表在 f_luaopen 时被初始化

(lstate.c) f_luaopen (f_luaopen 又被 lua_newstate 调用)

static void f_luaopen(lua_State* L, void* ud)
{
	//...//
	sethvalue(L, gt(L), luaH_new(L, 0, 2));//创建了一个arraysize=0, hashsize=2的表作为全局表
	//...//
}

2.2 env表

env表存放在Closure结构体中,也就是每个函数都有自己独立的环境

2.2.1 env表在Closure结构中

(lobject.h) Closure

#define ClosureHeader \
	CommonHeader; \
	lu_byte isC; \
	lu_byte nupvalues; \
	GCObject *gclist; \
	struct Table *env //函数的环境表地址

typedef struct CClosure
{
	ClosureHeader;
	lua_CFunction f;
	TValue upvalue[1];
} CClosure;

typedef struct LClosure
{
	ClosureHeader;
	struct Proto* p;
	UpVal* upvals[1];
} LClosure;

typedef union Closure
{
	CClosure c;
	LClosure l;
} Closure;

2.2.2 查找一个全局变量<=>在当前函数的env表中寻找

(lvm.c) luaV_execute 模拟CPU,后续虚拟机章节细说

//我们暂且只关心 全局变量 的 查找或设置
void luaV_execute (lua_State *L, int nexeccalls)
{
	LClosure* cl;
	StkId base;
	TValue* k;
	const Instruction* pc;
reentry:
	pc = L->savedpc;
	cl = &clvalue(L->ci->func)->l;
	base = L->base;
	k = cl->p->k;
	//循环取指令,执行指令
	for (;;) {
		const Instruction i = *pc++;//取出当前指令i,pc指向下一个指令,完全符合CPU工作原理
		if ((L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) && (--L->hookcount == 0 || L->hookmask & LUA_MASKLINE))
		{
			traceexec(L, pc);//traceexec后面说
			if (L->status == LUA_YIELD)
			{
				L->savedpc = pc - 1;
				return;
			}
			base = L->base;
		}
		StkId ra = RA(i);//#define RA(i) (base+GETARG_A(i))
		//#define GET_OPCODE(i)	(cast(OpCode, ((i)>>POS_OP) & MASK1(SIZE_OP,0)))
		switch (GET_OPCODE(i)) 
		{
			//..//
			case OP_GETGLOBAL: //OP_GETGLOBAL 指令格式A Bx 指令意义 R(A) := Gbl[Kst(Bx)]
			{
				TValue g;
				TValue* rb = KBx(i);//#define KBx(i) k+GETARG_Bx(i)
				sethvalue(L, &g, cl->env);//g设为去当前函数的env表
				Protect(luaV_gettable(L, &g, rb, ra));
				continue;
			}
			//..//
			case OP_SETGLOBAL:
			{
				TValue g;
				sethvalue(L, &g, cl->env);
				Protect(luaV_settable(L, &g, KBx(i), ra));
				continue;
			}
			//..//
		}
	}
}

(lvm.c) Protect宏

  • 执行代码x前 先将L->savedpc设为 下一步药执行的指令地址pc (因为代码x有异常的可能,所以记录pc到L上以便从下一条指令继续执行)
  • 执行代码x
  • 执行代码x后,再将 base设为 L->base(一位代码x可能会触发栈的重新分配内存)
#define Protect(x)	{ L->savedpc = pc; {x;}; base = L->base; }

2.2.3 lua函数的env表何时被设置

  • f_parser函数里,env被设置为&L->l_gt(lua_State的Global表)
  • luaV_execute的OP_CLOSURE指令分支,env被设置为&clvalue(L->ci->func)->l->env (当前调用信息对应的函数的env表)

(ldo.c) f_parser

static void f_parser(lua_State* L, void* ud)
{
	struct SParser* p = cast(struct SParser*, ud);
	int c = luaZ_lookahead(p->z);
	luaC_checkGC(L);
	Proto* tf = ((c == LUA_SIGNATURE[0]) ? luaU_undump : luaY_parser)(L, p->z, &p->buff, p->name);
	//很显然,在解析二进制或原文件时得到的lua函数,默认的env表时L的Global表
	Closure* cl = luaF_newLclosure(L, tf->nups, hvalue(gt(L)));
	cl->l.p = tf;
	for (i = 0; i < tf.nups; i++)
	{
		cl->l.upvals[i] = luaF_newupval(L);
	}
	setclvalue(L, L->top, cl);
	incr_top(L);
}

(lvm.c) luaV_execute

//我们暂且只关心 OP_CLOSURE 指令
void luaV_execute (lua_State *L, int nexeccalls)
{
	LClosure* cl;
	StkId base;
	TValue* k;
	const Instruction* pc;
reentry:
	pc = L->savedpc;
	cl = &clvalue(L->ci->func)->l;
	base = L->base;
	k = cl->p->k;
	//循环取指令,执行指令
	for (;;) {
		const Instruction i = *pc++;//取出当前指令i,pc指向下一个指令,完全符合CPU工作原理
		if ((L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) && (--L->hookcount == 0 || L->hookmask & LUA_MASKLINE))
		{
			traceexec(L, pc);//traceexec后面说
			if (L->status == LUA_YIELD)
			{
				L->savedpc = pc - 1;
				return;
			}
			base = L->base;
		}
		StkId ra = RA(i);//#define RA(i) (base+GETARG_A(i))
		//#define GET_OPCODE(i)	(cast(OpCode, ((i)>>POS_OP) & MASK1(SIZE_OP,0)))
		switch (GET_OPCODE(i)) 
		{
			//..//
			case OP_CLOSURE:
			{
				//根据索引获取当前函数的内嵌函数原型
				Proto* p = cl->p->p[GETARG_Bx(i)];
				//获取内嵌函数的upvalue数量
				int nup = p->nups;
				//根据upvalue数量和env表创建内嵌函数
				CLosure* ncl = luaF_newLclosure(L, nup, cl->env);
				ncl->l.p = p;
				for (int j = 0; j < nup; j++, pc++)
				{
					if (GET_OPCODE(*pc) == OP_GETUPVAL)
					{
						ncl->l.upvals[j] = cl->upvals[GETARG_B(*pc)];
					}
					else
					{
						ncl->l.upvals[j] = luaF_findupval(L, base + GETARG_B(*pc));
					}
				}
				setclvalue(L, ra, ncl);
				Protect(luaC_checkGC(L));
				continue;
			}
			//..//
		}
	}
}

(lfunc.c) luaF_newLclosure 创建一个新的lua函数(闭包)

Closure* luaF_newLclosure(lua_State* L, int nupvalues, Table* env)
{
	//#define sizeLclosure(n) (cast(int, sizeof(LClosure)) + cast(int, sizeof(TValue *)*((n)-1))) 
	Closure* cl = cast(Closure*, luaM_malloc(L, sizeLClosure(nupvalues)));
	luaC_link(L, obj2gco(cl), LUA_TFUNCTION);
	cl->l.isC = 0;
	cl->l.env = e;
	cl->l.nupvalues = cast_byte(nupvalues);
	while (nupvalues--)
	{
		cl->l.upvals[nupvalues] = NULL;
	}
	return c;
}

2.2.4 C函数的env表何时被设置

  • lua_pushcclosure函数里,env被设置为getcurrenv(L)
  • f_Ccall函数里,env被设置为getcurrenv(L)

(lapi.c) lua_pushcclosure 创建一个C闭包入栈

LUA_API void lua_pushcclosure(lua_State* L, lua_CFunction fn, int n)
{
	luaC_checkGC(L);
	Closure* cl = luaF_newCclosure(L, n, getcurrentv(L));
	cl->c.f = fn;
	L->top -= n;
	while(n--)
	{
		setobj2n(L, &cl->c.upvalue[n], L->top + n);
	}
	setclvalue(L, L->top, cl);
	api_incr_top(L);//#define api_incr_top(L)   {api_check(L, L->top < L->ci->top); L->top++;}
}

(lfunc.c) luaF_newCclosure 创建C闭包

Closure* luaF_newCclosure(lua_State* L, int nupvals, Table* env)
{
	//#define sizeCclosure(n)	(cast(int, sizeof(CClosure)) + cast(int, sizeof(TValue)*((n)-1)))
	Closure* c = cast(Closure*, luaM_malloc(L, sizeCclosure(nupvals)));
	luaC_link(L, obj2gco(c), LUA_TFUNCTION);
	c->c.isC = 1;
	c->c.env = env;
	c->c.nupvals = cast_byte(nupvals);
	return c;
}

(lapi.c) getcurrentv 获取当前的函数env表(获取L->ci的env表,当然L->base_ci的env表就是Global表)

  • 若不是内嵌函数,则返回Global表
  • 若是内嵌函数,则返回母函数的env表
static Table* getcurrenv(lua_State* L)
{
	if (L->ci == L->base_ci)
	{
		return hvalue(gt(L));
	}
	Closure* func = curr_func(L);//#define curr_func(L)	(clvalue(L->ci->func))
	return func->c.env;
}

(lapi.c) f_Ccall 创建并调用一个C函数

static void f_Ccall(lua_State* L, void* ud)
{
	struct CCallS* c = cast(struct CCallS*, ud);
	Closure* cl = luaF_newCclosure(L, 0, getcurrenv(L));
	cl->c.f = c->func;
	setclvalue(L, L->top, cl);
	api_incr_top(L);
	luaD_call(L, L->top - 2, 0);
}

2.2.5 getfenv(读取函数的环境)

(lbaselib.c) luaB_getfenv (若为C函数,则返回Global表;否则lua_getfenv)

static int luaB_getfenv(lua_State* L)
{
	getfunc(L, 1);
	if (lua_iscfuntion(L, -1))
	{
		lua_pushvalue(L, LUA_GLOBALSINDEX);
	}
	else
	{
		lua_getfenv(L, -1);
	}
	return 1;
}

2.2.6 setfenv(强制设置函数的环境)

(lbaselib.c) luaB_setfenv

static int luaB_setfenv(lua_State* L)
{
	//检查L->base+2-1处是不是table类型
	luaL_chechtype(L, 2, LUA_TTABLE);
	getfunc(L, 0);
	//把 L->base+2-1 处的值复制到 L->top 处,并L->top++
	lua_pushvalue(L, 2);
	//若level为0,则改变当前lua线程的环境
	if (lua_isnumber(L, 1) && lua_tonumber(L, 1) == 0)
	{
		lua_pushthread(L);
		lua_insert(L, -2);
		lua_settenv(L, -2);
		return 0;
	}
	//若L->top-2 是C函数,或者给 L->top-2 设置env失败,则报错
	if (lua_iscfunction(L, -2) || lua_setfenv(L, -2) == 0)
	{
		//#define LUA_QL(x)	"'" x "'"
		luaL_error(L, LUA_QL("setfenv") " cannot change environment of given object");
	}
	return 1;
}

(lauxlib.c) luaL_checktype 检查类型是否满足,否则报错

LUALIB_API void luaL_checktype(lua_State* L, int narg, int t)
{
	if (lua_type(L, narg) != t)
	{
		tag_error(L, narg, t);
	}
}

(lauxlib.c) tag_error 根据类型标识报错

static void tag_error(lua_State* L, int narg, int tag)
{
	luaL_typeerror(L, narg, lua_typename(L, tag));
}

(lauxlib.c) luaL_typeerror 根据类型名报错

LUALIB_API int luaL_typeerror(lua_State* L, int narg, const char* tname)
{
	const char* msg = lua_pushfstring(L, "%s expected, got %s", tname, luaL_typename(L, narg));
	return luaL_argerror(L, narg, msg);//luaL_argerror见异常章节
}

(lbaselib.c) getfunc (获取L->base 处的函数,并压入栈顶)

static void getfunc(lua_State* L, int opt)
{
	//若L->base处是函数,则把L->base处的值复制到L->top处,L->top++
	if (lua_isfunction(L, 1))
	{
		lua_pushvalue(L, 1);
		return;
	}
	
	//#define luaL_optint(L,n,d)	((int)luaL_optinteger(L, (n), (d)))
	//#define luaL_checkint(L,n)	((int)luaL_checkinteger(L, (n)))
	//opt为非0时,对L->base处的值进行判断,若为nil或none,则取默认值,否则执行整数转换操作;若opt为0,则仅仅执行整数转换操作
	int level = opt ? luaL_optint(L, 1, 1) : luaL_checkint(L, 1);
	
	//level必须为>=0
	luaL_argcheck(L, level >= 0, 1, "level must be non-negative");
	
	lua_Debug ar;
	if (lua_getstack(L, level, &ar) == 0)
	{
		luaL_argerror(L, 1, "invalid level");
	}
	lua_getinfo(L, "f", &ar);
	if (lua_isnil(L, -1))
	{
		luaL_error(L, "no function environmnent for tail call at level %d", level);//luaL_error见异常章节
	}
}

(lauxlib.c) luaL_optinteger

//不是数字类型且转为整数后不是0,则报错
LUALIB_API lua_chechinteger(lua_State* L, int narg)
{
	lua_Integer d = lua_tointeger(L, narg);
	if (d == 0 && !lua_isnumber(L, narg))
	{
		tag_error(L, narg, LUA_TNUMBER);
	}
	return d;
}

LUALIB_API lua_Integer luaL_optinteger(lua_State* L, int narg, lua_Integer def)
{
	//#define luaL_opt(L,f,n,d)	(lua_isnoneornil(L,(n)) ? (d) : f(L,(n)))
	return luaL_opt(L, luaL_checkinteger, narg, def);
}

(ldebug.c) lua_getstack 获取指定层级的调试信息

LUA_API int lua_getstack(lua_State* L, int level, lua_Debug* ar)
{
	for (CallInfo* ci = L->ci; level > 0 && ci > L->base_ci; ci--)
	{
		level--;
		//若为lua函数,则跳过尾调用,//见函数章节
		if (f_isLua(ci))
		{
			level -= ci->tailcalls;//原文 /* skip lost tail calls */ 见函数章节
		}
	}
	int status;
	if (level == 0 && ci > L->base_ci)
	{
		//找到了合适的level
		status = 1;
		ar->i_ci = cast_int(ci - L->base_ci);
	}
	else if (level < 0)
	{
		//原文 /* level is of a lost tail call? */ 见函数章节
		status = 1;
		ar->i_ci = 0;
	}
	else
	{
		status = 0;
	}
	return status;
}

(ldebug.c) lua_getinfo 获取debug信息

LUA_API int lua_getinfo(lua_State* L, const char* what, lua_Debug* ar)
{
	Closure* f = NULL;
	if (*what == '>')
	{
		StdId func = L->top - 1;
		what++;
		f = clvalue(func);
		L->top--;//函数出栈
	}
	else if (ar->i_ci != 0)//没有尾调用  //见函数章节
	{
		ci = L->base_ci + ar->i_ci;
		f = clvalue(ci->func);
	}
	int status = auxgetinfo(L, what, ar, f, ci);//auxgetinfo,见函数章节
	if (strchr(what, 'f'))
	{
		if (f == NULL)
		{
			setnilvalue(L->top);
		}
		else
		{
			setclvalue(L, L->top, f);
		}
		incr_top(L);
	}
	if (strchr(what, 'L'))
	{
		collectvalidlines(L, f);//收集行号信息
	}
	return status;
}

(ldebug.c) collectvalidlines 收集行号信息(获得一个table<行号,true> 的结构,压入栈顶)

static void collectvalidlines(lua_State* L, Closure* f)
{
	if (f == NULL || f->c.isC)
	{
		setnilvalue(L->top);
	}
	else
	{
		Table* t = luaH_new(L, 0, 0);
		int* lineinfo = f->l.p->lineinfo;
		for (int i = 0; i < f->l.p->sizelineinfo; i++)
		{
			setbvalue(luaH_setnum(L, t, lineinfo[i]), 1);
		}
		sethvalue(L, L->top, t);
	}
	incr_top(L);
}

2.3 registry表(注册表)

  • 注册表在global_State中,全局唯一,这个表可以被多个lua_State访问
  • 注册表只能被C代码访问,Lua代码不能访问

(lauxlib.h) lua_ref,lua_unref,lua_getref

#define LUA_NOREF       (-2)
#define LUA_REFNIL      (-1)

//往注册表新增一个唯一key
#define lua_ref(L,lock) ((lock) ? luaL_ref(L, LUA_REGISTRYINDEX) : \
      (lua_pushstring(L, "unlocked references are obsolete"), lua_error(L), 0))
//取消注册表一个唯一key
#define lua_unref(L,ref)        luaL_unref(L, LUA_REGISTRYINDEX, (ref))
//获取注册表唯一key对应的值
#define lua_getref(L,ref)       lua_rawgeti(L, LUA_REGISTRYINDEX, (ref))

(lauxlib.c) luaL_ref,luaL_unref

//对于栈顶的value,从表t中获取一个可用key,来存value
/*
表中的key=FREELIST_REF对应的value是 空闲的key的索引(而所谓的空闲的key是由于unref造成的结果)

举个例子来理解这个设计:
开始t={[FREELIST_REF]=nil}
存v1:  ref=t[FREELIST_REF]=nil => ref=#t + 1 = 1, t[ref]=t[1]=v1 => t={[FREELIST_REF]=nil, [1]=v1}
存v2:  ref=t[FREELIST_REF]=nil => ref=#t + 1 = 2, t[ref]=t[2]=v2 => t={[FREELIST_REF]=nil, [1]=v1, [2]=v2}
unref(1):  ref=1, t[ref]=t[FREELIST_REF]=nil, t[FREELIST_REF]=ref=1 => t={[FREELIST_REF]=1, [1]=nil, [2]=v2}
unref(2):  ref=2, t[ref=t[FREELIST_REF]=1, t[FREELIST_REF]=ref=2 => t={[FREELIST_REF]=2, [1]=nil, [2]=1}
存v3:  t[FREELIST_REF]=2 => ref=2, t[FREELIST_REF]=t[ref]=1, t[ref]=v3 => t={[FREELIST_REF]=1, [1]=nil, [2]=v3}
存v4:  t[FREELIST_REF]=1 => ref=1, t[FREELIST_REF]=t[ref]=nil, t[ref]=v4 => t={[FREELIST_REF]=nil, [1]=v4, [2]=v3}

也就说t中的key分为2种状态:空闲 和 非空闲。若为空闲,则其value是下一个空闲key的索引;若为非空闲,则其value是有意义的值。

拓展讨论:
开始t={[FREELIST_REF]=nil}
存v1:  t[FREELIST_REF]=nil => ref=#t + 1 = 1, t[ref]=t[1]=v1 => t={[FREELIST_REF]=nil, [1]=v1}
存v2:  t[FREELIST_REF]=nil => ref=#t + 1 = 2, t[ref]=t[2]=v2 => t={[FREELIST_REF]=nil, [1]=v1, [2]=v2}
unref(1):  ref=1, t[ref]=t[FREELIST_REF]=nil, t[FREELIST_REF]=ref=1 => t={[FREELIST_REF]=1, [1]=nil, [2]=v2}
再次unref(1):  ref=1, t[ref]=t[FREELIST_REF]=1, t[FREELIST_REF]=ref=1 => t={[FREELIST_REF]=1, [1]=1, [2]=v2}
unref(2):  ref=2, t[ref]=t[FREELIST_REF]=1, t[FREELIST_REF]=ref=2 => t={[FREELIST_REF]=2, [1]=1, [2]=1}
再次unref(2):  ref=2, t[ref]=t[FREELIST_REF]=2, t[FREELIST_REF]=ref=2 => t={[FREELIST_REF]=2, [1]=1, [2]=2}
存v3:  t[FREELIST_REF]=2 => ref=2, t[FREELIST_REF]=t[ref]=2, t[ref]=v3 => t={[FREELIST_REF]=2, [1]=1, [2]=v3}
存v4:  t[FREELIST_REF]=2 => ref=2, t[FREELIST_REF]=t[ref]=v3, t[ref]=v4 => t={[FREELIST_REF]=2, [1]=1, [2]=v4}//v3不见了,出问题了!!!

*/
LUALIB_API int luaL_ref(lua_State* L, int t)
{
	//假设栈顶的值为v
	//若v==nil,则返回-1
	if (lua_isnil(L, -1))
	{
		lua_pop(L, 1);
		return LUA_REFNIL;//#define LUA_REFNIL      (-1)
	}
	t = abs_index(L, t);//#define abs_index(L, i) ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : lua_gettop(L) + (i) + 1)
	lua_rawgeti(L, t, FREELIST_REF);//#define FREELIST_REF	0	/* free list of references */
	//ref=t[FREELIST_REF], 获取一个空闲的key
	int ref = (int)lua_tointeger(L, -1);
	lua_pop(L, -1);
	if (ref != 0)//ref!=0,说明有空闲key
	{
		lua_rawgeti(L, t, ref);
		lua_rawseti(L, t, FREELIST_REF);//t[FREELIST_REF] = t[ref]
	}
	else//ref==0,说明没有空闲位置,需要新建key
	{
		ref = (int)lua_objlen(L, t);
		ref++;//创建一个新的引用
	}
	lua_rawseti(L, t, ref);//t[ref]=v
	return ref;
}

LUALIB_API void luaL_unref(lua_State* L, int t, int ref)
{
	if (ref < 0)
	{
		return;
	}
	t = abs_index(L, t);
	lua_rawgeti(L, t, FREELIST_REF);
	lua_rawseti(L, t, ref);//t[ref]=t[FREELIST_REF]
	lua_pushinteger(L, ref);
	lua_rawseti(L, t, FREELIST_REF);//t[FREELIST_REF]=ref
}

(lapi.c) lua_objlen 获取对象的长度

LUA_API size_t lua_objlen(lua_State* L, int idx)
{
	StkId o = index2adr(L, idx);
	switch(ttype(o))
	{
		case LUA_TSTRING:
		{
			return tsvalue(o)->len;
		}
		case LUA_TUSERDATA:
		{
			return uvalue(o)->len;
		}
		case LUA_TTABLE:
		{
			return luaH_getn(hvalue(o));
		}
		case LUA_TNUMBER:
		{
			size_t l = luaV_tostring(L, o) ? tsvalue(o)->len : 0;
			return l;
		}
		default:
		{
			return 0;
		}
	}
}

2.4 UpValue

registry表是全局变量的存储,env表是函数内全局变量的存储,UpValue则是函数内静态变量的存储
通过 index2adr(L, int idx) (其中idx<LUA_GLOBALSINDEX)获取C闭包的upvalue地址
至于UpValue的一切,见函数章节。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值