lua_load,lua脚本函数原型Proto,OP_GETTABUP

以下内容是脚本文件:package.lua

#1 -- Note: This file is loaded automatically by the engine.
#2
#3 local mt = {
#4	 __index = function(self, k)
#5		if k ~= "__tostring" then
#6			error("Tried to access an empty package", 2)
#7		end
#8	 end,
#9	  __newindex = function()
#10		error("Tried to access an empty package", 2)
#11	  end,
#12	  __metatable = "empty package",
#13	  __tostring = function() return "{empty package}" end,
#14 }
#15 local empty_pkg = setmetatable({}, mt)
#16
#17 local function global()
#18	  function funcA(a, b)
#19		return a + b + 10
#20	  end
#21	local c = funcA(30, 40)
#22 end
#23
#24 -- TODO: Currently if you require a file by different (relative) paths, each will be a different copy.
#25 function wesnoth.require(pkg_name)
#26	global()
#27 end
#28
#29 return empty_pkg

一、package.lua对应的Proto

从package.lua解析出的函数原型。以下的Proto内容在执行完lua_load(L, , , "@lua/package.lua", "t")后就会生成,存储在L,不必等lua_pcall去执行@lua/package.lua的顶层lua脚本函数。

---source:@lua/package.lua---
linedefined: 0
lastlinedefined: 0
numparams: 0
is_vararg: 1
maxstacksize: 5
sizecode: 21
0     L#1   VARARGPREP(81) (iABC)   [A]0 [k]0 [B]0 [C]0
1     L#3   NEWTABLE(19)   (iABC)   [A]0 [k]0 [B]3 [C]0
2     L#3   EXTRAARG(82)   (iAx)    [Ax]0
3     L#8   CLOSURE(79)    (iABx)   [A]1 [Bx]0
4     L#8   SETFIELD(18)   (iABC)   [A]0 [k]0 [B]0 [C]1
5     L#11  CLOSURE(79)    (iABx)   [A]1 [Bx]1
6     L#11  SETFIELD(18)   (iABC)   [A]0 [k]0 [B]1 [C]1
7     L#12  SETFIELD(18)   (iABC)   [A]0 [k]1 [B]2 [C]3
8     L#13  CLOSURE(79)    (iABx)   [A]1 [Bx]2
9     L#13  SETFIELD(18)   (iABC)   [A]0 [k]0 [B]4 [C]1
10    L#15  GETTABUP(11)   (iABC)   [A]1 [k]0 [B]0 [C]5
11    L#15  NEWTABLE(19)   (iABC)   [A]2 [k]0 [B]0 [C]0
12    L#15  EXTRAARG(82)   (iAx)    [Ax]0
13    L#15  MOVE(0)        (iABC)   [A]3 [k]0 [B]0 [C]0
14    L#15  CALL(68)       (iABC)   [A]1 [k]0 [B]3 [C]2
15    L#22  CLOSURE(79)    (iABx)   [A]2 [Bx]3
16    L#25  GETTABUP(11)   (iABC)   [A]3 [k]0 [B]0 [C]6
17    L#27  CLOSURE(79)    (iABx)   [A]4 [Bx]4
18    L#25  SETFIELD(18)   (iABC)   [A]3 [k]0 [B]7 [C]4
19    L#29  RETURN(70)     (iABC)   [A]1 [k]1 [B]2 [C]1
20    L#29  RETURN(70)     (iABC)   [A]3 [k]1 [B]1 [C]1
sizek: 8
  0    [VSHRSTR]__index
  1    [VSHRSTR]__newindex
  2    [VSHRSTR]__metatable
  3    [VSHRSTR]empty package
  4    [VSHRSTR]__tostring
  5    [VSHRSTR]setmetatable
  6    [VSHRSTR]wesnoth
  7    [VSHRSTR]require
sizeupvalues: 1
  0    _ENV  [instack]true  [idx]0  [kind]0
sizep: 5
	[1/5]---source:@lua/package.lua---
	linedefined: 4
	lastlinedefined: 8
	numparams: 2
	is_vararg: 0
	maxstacksize: 5
	sizecode: 7
	0     L#5   EQK(60)        (iABC)   [A]1 [k]1 [B]0 [C]0
	1     L#5   JMP(56)        (isJ)    [sJ]4
	2     L#6   GETTABUP(11)   (iABC)   [A]2 [k]0 [B]0 [C]1
	3     L#6   LOADK(3)       (iABx)   [A]3 [Bx]2
	4     L#6   LOADI(1)       (iAsBx)  [A]4 [sBx]2
	5     L#6   CALL(68)       (iABC)   [A]2 [k]0 [B]3 [C]1
	6     L#8   RETURN0(71)    (iABC)   [A]2 [k]0 [B]1 [C]0
	sizek: 3
	  0    [VSHRSTR]__tostring
	  1    [VSHRSTR]error
	  2    [VSHRSTR]Tried to access an empty package
	sizeupvalues: 1
	  0    _ENV  [instack]false  [idx]0  [kind]0
	sizep: 0
	sizeabslineinfo: 0
	sizelocvars: 2
	  0    self  [startpc]0  [endpc]7
	  1    k  [startpc]0  [endpc]7
	============
 
	[2/5]---source:@lua/package.lua---
	linedefined: 9
	lastlinedefined: 11
	numparams: 0
	is_vararg: 0
	maxstacksize: 3
	sizecode: 5
	0     L#10  GETTABUP(11)   (iABC)   [A]0 [k]0 [B]0 [C]0
	1     L#10  LOADK(3)       (iABx)   [A]1 [Bx]1
	2     L#10  LOADI(1)       (iAsBx)  [A]2 [sBx]2
	3     L#10  CALL(68)       (iABC)   [A]0 [k]0 [B]3 [C]1
	4     L#11  RETURN0(71)    (iABC)   [A]0 [k]0 [B]1 [C]0
	sizek: 2
	  0    [VSHRSTR]error
	  1    [VSHRSTR]Tried to access an empty package
	sizeupvalues: 1
	  0    _ENV  [instack]false  [idx]0  [kind]0
	sizep: 0
	sizeabslineinfo: 0
	sizelocvars: 0
	============
 
	[3/5]---source:@lua/package.lua---
	linedefined: 13
	lastlinedefined: 13
	numparams: 0
	is_vararg: 0
	maxstacksize: 2
	sizecode: 3
	0     L#13  LOADK(3)       (iABx)   [A]0 [Bx]0
	1     L#13  RETURN1(72)    (iABC)   [A]0 [k]0 [B]2 [C]0
	2     L#13  RETURN0(71)    (iABC)   [A]0 [k]0 [B]1 [C]0
	sizek: 1
	  0    [VSHRSTR]{empty package}
	sizeupvalues: 0
	sizep: 0
	sizeabslineinfo: 0
	sizelocvars: 0
	============
 
	[4/5]---source:@lua/package.lua---
	linedefined: 17
	lastlinedefined: 22
	numparams: 0
	is_vararg: 0
	maxstacksize: 3
	sizecode: 7
	0     L#20  CLOSURE(79)    (iABx)   [A]0 [Bx]0
	1     L#18  SETTABUP(15)   (iABC)   [A]0 [k]0 [B]0 [C]0
	2     L#21  GETTABUP(11)   (iABC)   [A]0 [k]0 [B]0 [C]0
	3     L#21  LOADI(1)       (iAsBx)  [A]1 [sBx]30
	4     L#21  LOADI(1)       (iAsBx)  [A]2 [sBx]40
	5     L#21  CALL(68)       (iABC)   [A]0 [k]0 [B]3 [C]2
	6     L#22  RETURN0(71)    (iABC)   [A]1 [k]0 [B]1 [C]0
	sizek: 1
	  0    [VSHRSTR]funcA
	sizeupvalues: 1
	  0    _ENV  [instack]false  [idx]0  [kind]0
	sizep: 1
		[1/1]---source:@lua/package.lua---
		linedefined: 18
		lastlinedefined: 20
		numparams: 2
		is_vararg: 0
		maxstacksize: 3
		sizecode: 6
		0     L#19  ADD(34)        (iABC)   [A]2 [k]0 [B]0 [C]1
		1     L#19  MMBIN(46)      (iABC)   [A]0 [k]0 [B]1 [C]6
		2     L#19  ADDI(21)       (iABC)   [A]2 [k]0 [B]2 [C]137
		3     L#19  MMBINI(47)     (iABC)   [A]2 [k]0 [B]137 [C]6
		4     L#19  RETURN1(72)    (iABC)   [A]2 [k]0 [B]2 [C]0
		5     L#20  RETURN0(71)    (iABC)   [A]2 [k]0 [B]1 [C]0
		sizek: 0
		sizeupvalues: 0
		sizep: 0
		sizeabslineinfo: 0
		sizelocvars: 2
		  0    a  [startpc]0  [endpc]6
		  1    b  [startpc]0  [endpc]6
		============
 
	sizeabslineinfo: 0
	sizelocvars: 1
	  0    c  [startpc]6  [endpc]7
	============
 
	[5/5]---source:@lua/package.lua---
	linedefined: 25
	lastlinedefined: 27
	numparams: 1
	is_vararg: 0
	maxstacksize: 2
	sizecode: 3
	0     L#26  GETUPVAL(9)    (iABC)   [A]1 [k]0 [B]0 [C]0
	1     L#26  CALL(68)       (iABC)   [A]1 [k]0 [B]1 [C]1
	2     L#27  RETURN0(71)    (iABC)   [A]1 [k]0 [B]1 [C]0
	sizek: 0
	sizeupvalues: 1
	  0    global  [instack]true  [idx]2  [kind]0
	sizep: 0
	sizeabslineinfo: 0
	sizelocvars: 1
	  0    pkg_name  [startpc]0  [endpc]3
	============
 
sizeabslineinfo: 0
sizelocvars: 3
  0    mt  [startpc]10  [endpc]21
  1    empty_pkg  [startpc]15  [endpc]21
  2    global  [startpc]16  [endpc]21
============

二、主Proto的虚拟机指令

 解释package.lua中第一个Proto中的虚拟机指令

  • A、k、B、C、Ax、Bx,这些参数不是在生成时就肯定正确的,有些参数需要后面补上正确值。举个例子,NEWTABLE,生成时A、k、B、C都是0,在语法分析到表结束后,luaK_settablesize(...)会补上正确值

================================================

  • 0     L#1   VARARGPREP(81) (iABC)   [A]0 [k]0 [B]0 [C]0
  • 1     L#3   NEWTABLE(19)   (iABC)   [A]0 [k]0 [B]3 [C]0
    新加入时,B是0,那3应该是后面补。那什么时候补呢?luaK_settablesize。B:构造时“ceillog2(记录式单元数)+1”,否则0。C:构造时“数组式单元数%(MAXARG_C+1)”。k:数组式单元数/MAXARG_C+1。NEWTABLE后总会有一个EXTRAARG。
  • 2     L#3   EXTRAARG(82)   (iAx)    [Ax]0
  • 3     L#8   CLOSURE(79)    (iABx)   [A]1 [Bx]0
    lex遇到function的end,生成一个闭包。R[1] := closure(KPROTO[0])
  • 4     L#8   SETFIELD(18)   (iABC)   [A]0 [k]0 [B]0 [C]1
    设置表的key=value。值从栈中取(k=0)。
    R[0]["__index"] := R[1]。一直到这里,R[1]只是lua闭包,并没有调用lua闭包中函数。当访问不存在的单元时,才会调用元方法__index。
  • 5     L#11  CLOSURE(79)    (iABx)   [A]1 [Bx]1
    lex遇到function的end,生成一个闭包。R[1] := closure(KPROTO[1])
  • 6     L#11  SETFIELD(18)   (iABC)   [A]0 [k]0 [B]1 [C]1
    设置表的key=value。值从栈中取(k=0)。
    R[0]["__newindex"] := R[1]
  • 7     L#12  SETFIELD(18)   (iABC)   [A]0 [k]1 [B]2 [C]3
    设置表的key=value。值从常量中取(k=1)。
  • 8     L#13  CLOSURE(79)    (iABx)   [A]1 [Bx]2
    lex遇到function的end,生成一个闭包。R[1] := closure(KPROTO[2])
  • 9     L#13  SETFIELD(18)   (iABC)   [A]0 [k]0 [B]4 [C]1
    设置表的key=value。值从栈中取(k=0)。注:遇到},是表定义结束符,会个修正和该表相关的指令的,即更新pc=1、pc=2中的指令参数。
  • 10    L#15  GETTABUP(11)   (iABC)   [A]1 [k]0 [B]0 [C]5
    R[A] := UpValue[B][K[C]:string] => R[1] = UpValue[0][K[5]] = UpValue[0]["setmetatable"]
    从上值中取,该值放入R[1],是如何由"setmetatable"得到C=5的,这涉及到如何在全局变量区搜索变量,参考后文“三、GETTABUP(11)   (iABC)   [A]1 [k]0 [B]0 [C]5”)。
  • 11    L#15  NEWTABLE(19)   (iABC)   [A]2 [k]0 [B]0 [C]0
  • 12    L#15  EXTRAARG(82)   (iAx)    [Ax]0
  • 13    L#15  MOVE(0)        (iABC)   [A]3 [k]0 [B]0 [C]0
  • 14    L#15  CALL(68)       (iABC)   [A]1 [k]0 [B]3 [C]2
  • 15    L#22  CLOSURE(79)    (iABx)   [A]2 [Bx]3
  • 16    L#25  GETTABUP(11)   (iABC)   [A]3 [k]0 [B]0 [C]6
  • 17    L#27  CLOSURE(79)    (iABx)   [A]4 [Bx]4
  • 18    L#25  SETFIELD(18)   (iABC)   [A]3 [k]0 [B]7 [C]4
  • 19    L#29  RETURN(70)     (iABC)   [A]1 [k]1 [B]2 [C]1
  • 20    L#29  RETURN(70)     (iABC)   [A]3 [k]1 [B]1 [C]1

 

三、GETTABUP(11)   (iABC)   [A]1 [k]0 [B]0 [C]5

<lua>/lparser.cpp
------
static void localstat (LexState *ls) {
  ......
  // 过程对应的lua脚本:local empty_pkg = setmetatable({}, mt)
  if (testnext(ls, '='))
    nexps = explist(ls, &e);
  else {
    e.k = VVOID;
    nexps = 0;
  }
  ......
}

<lua>/lparser.cpp
------
static int testnext (LexState *ls, int c) {
  if (ls->t.token == c) {
    luaX_next(ls);
    return 1;
  }
  else return 0;
}

满足条件“ls->t.token == '='”,认为是表达式,调用luaX_next,取出表达式右侧内容。

<lua>/llex.cpp
------
void luaX_next (LexState *ls) {
  ls->lastline = ls->linenumber;
  if (ls->lookahead.token != TK_EOS) {  /* is there a look-ahead token? */
    ls->t = ls->lookahead;  /* use this one */
    ls->lookahead.token = TK_EOS;  /* and discharge it */
  } else {
    ls->t.token = llex(ls, &ls->t.seminfo);  /* read next token */
  }
}

luaX_next调用llex。解析出“setmetatable”这个词后,调用llex(..),后者再调用luaX_newstring(...)。

<lua>/llex.cpp
------
TString *luaX_newstring (LexState *ls, const char *str, size_t l) {
  lua_State *L = ls->L;
  // 以“setmetatable”这个字符串生成ts。
  TString *ts = luaS_newlstr(L, str, l);  /* create new string */
  // 以key="setmetatable",搜索ls->h这个扫描表。
  const TValue *o = luaH_getstr(ls->h, ts);
  if (!ttisnil(o))  /* string already present? */
    ts = keystrval(nodefromval(o));  /* get saved copy */
  else {  /* not in use yet */
    // ls->h不存在key="setmetatable",进入这个入口。
    TValue *stv = s2v(L->top++);  /* reserve stack space for string */
    setsvalue(L, stv, ts);  /* temporarily anchor the string */
    // 下面这个luaH_finishset会向ls->h添加记录式单元:ls->h["setmetatable"] = "setmetatable"
    luaH_finishset(L, ls->h, stv, o, stv);  /* t[string] = string */
    /* table is not a metatable, so it does not need to invalidate cache */
    luaC_checkGC(L);
    L->top--;  /* remove string from stack */
  }
  return ts;
}

localstae检测到有表达式,调用explist(...)处理这个表达式。第一步的testnext已解析出“setmetatable”,会调用singlevar(...)。singlevar的功能是要确定这变量在哪里。

<lua>/lparser.cpp
------
static void singlevar (LexState *ls, expdesc *var) {
  // var->k是VNONRELOC
  TString *varname = str_checkname(ls);
  // varname: setmetatable
  const std::string str = TSTRING_2_str(varname);
  FuncState *fs = ls->fs;
  singlevaraux(fs, varname, var, 1);
  // setmetatable不是local变量,singlevaraux会把var->k改为VVOID
  if (var->k == VVOID) {  /* global name? */
    expdesc key;
    // singlevaraux用的TString是ls->envn,而不是varname。
    singlevaraux(fs, ls->envn, var, 1);  /* get environment variable */
    // 经过singlevaraux,var->k改为了KUPVAL。
    lua_assert(var->k != VVOID);  /* this one must exist */
    // key是另一个expdesc,用于表示后绪用于搜索的表索引“key”。
    codestring(&key, varname);  /* key is variable name */
    // key->k = VKSTR,e->u.strval = varname。luaK_indexed执行获取env[varname]
    luaK_indexed(fs, var, &key);  /* env[varname] */
  }
}

<lua>/lcode.cpp
------
void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) {
  if (k->k == VKSTR) {
    // k是字符串类型,会入这里。
    // str2K是主要操作,它会修改k->u.info,指示setmetatable在全局表中的整数值索引。
    str2K(fs, k);
  }
  lua_assert(!hasjumps(t) &&
             (t->k == VLOCAL || t->k == VNONRELOC || t->k == VUPVAL));
  if (t->k == VUPVAL && !isKstr(fs, k))  /* upvalue indexed by non 'Kstr'? */
    luaK_exp2anyreg(fs, t);  /* put it in a register */
  if (t->k == VUPVAL) {
    // setmetatable时会进这入口。
    // 在上值数组的索引。t->u->info: 0
    t->u.ind.t = t->u.info;  /* upvalue index */
    // 在常量数组的索引。k->u.info: 5
    t->u.ind.idx = k->u.info;  /* literal string */
    t->k = VINDEXUP;
  }
  else {
    /* register index of the table */
    t->u.ind.t = (t->k == VLOCAL) ? t->u.var.ridx: t->u.info;
    if (isKstr(fs, k)) {
      t->u.ind.idx = k->u.info;  /* literal string */
      t->k = VINDEXSTR;
    }
    else if (isCint(k)) {
      t->u.ind.idx = cast_int(k->u.ival);  /* int. constant in proper range */
      t->k = VINDEXI;
    }
    else {
      t->u.ind.idx = luaK_exp2anyreg(fs, k);  /* register */
      t->k = VINDEXED;
    }
  }
}

<lua>/lcode.cpp
------
static void str2K (FuncState *fs, expdesc *e) {
  lua_assert(e->k == VKSTR);
  e->u.info = stringK(fs, e->u.strval);
  e->k = VK;
}

<lua>/lcode.cpp
------
static int addk (FuncState *fs, TValue *key, TValue *v) {
  TValue val;
  lua_State *L = fs->ls->L;
  Proto *f = fs->f;
  // 第一步已经让ls->h存在key="setmetatable"的记录。
  const TValue *idx = luaH_get(fs->ls->h, key);  /* query scanner table */
  int k, oldsize;
  if (ttisinteger(idx)) {  /* is there an index there? */
    // 虽然存在key="setmetatable"的记录,但存的值是TSTRING的值"setmetatable",因而不会进入这里。
    k = cast_int(ivalue(idx));
    /* correct value? (warning: must distinguish floats from integers!) */
    if (k < fs->nk && ttypetag(&f->k[k]) == ttypetag(v) &&
                      luaV_rawequalobj(&f->k[k], v))
      return k;  /* reuse index */
  }
  /* constant not found; create a new entry */
  // f->sizek是已经分配的用于存储常量的数目,总是2的次方,值:8。fs->nk是有效的常量数目,值:5。
  oldsize = f->sizek;
  k = fs->nk;
  // k就是要用于之后访问“setmetatable”的常量值了。
  /* numerical value does not need GC barrier;
     table has no metatable, so it does not need to invalidate cache */
  setivalue(&val, k);
  luaH_finishset(L, fs->ls->h, key, idx, &val);
  // 以k=5生成val,并调用luaH_finishset,把新值设置到ls->h,至此key="setmetatable"的记录值会是5。
  luaM_growvector(L, f->k, k, f->sizek, TValue, MAXARG_Ax, "constants");
  while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]);
  setobj(L, &f->k[k], v);
  fs->nk++;
  luaC_barrier(L, f, v);
  return k;
}

addk返回值5,于是e->u.info=5。这个5是在常量区的索引。回到luaK_indexed(...),执行完str2K(fs, k)后,会把e->u.info赋值给t->u.ind.idx。

以上分析可看出,整个过程没有判断_ENV中是否真存在key="setmetatable"的记录。只要解析到“setmetatable”这个词了,就会以它为key加到ls->h。至于5,只是到那时刻,此个lua脚本函数对应的Proto的常量数组中,恰好索引5是每个空闲的位置而已。

得出v后,解析器检测到下一个token是“(”,于是调用luaK_exp2nextreg生成访问这个表达式的指令。由于v.k==VINDEXUP,生成的指令是OP_GETTABUP。

e->k使用的字段说明
VLOCALe->u.info表示值在栈中,R(e->u.info)
VUPVALe->u.info表示值在upvalues中,UpValue[e->u.info]

VINDEXED

e->u.ind.vt = VUPVAL
e->u.ind.t
e->u.ind.idx
表示值在UpValue[e->u.ind.t][RK(e->u.ind.idx)]

更多查找变量细节参考:构建Lua解释器Part7:构建完整的语法分析器(上)

 

四、LexState.h

LexState.h类型是“Table*”,即代码中经常出现的“ls->h”。luaY_parser在把主函数闭包压栈后,便会创建它,并压栈。执行mainfunc后,把反它弹出栈,从而确保经过luaY_Parser后,栈只多了一个主函数闭包。

ls->h作用是提供过程中扫描到的变量。让看下执行mainfunc后、弹出栈之前,它当中内容。

INFO: ======table(e279880)'s sizenode:32======main_lexstate_h(e279880)
INFO: [0/32]empty package --> (49438b0)3
INFO: [1/32]Tried to access an empty package --> (49438c8)1
INFO: [2/32]__tostring --> (49438e0)4
INFO: [3/32]LUA_TNIL --> (49438f8)LUA_TNIL
INFO: [4/32]if --> (4943910)if
INFO: [5/32]LUA_TNIL --> (4943928)LUA_TNIL
INFO: [6/32]require --> (4943940)7
INFO: [7/32]LUA_TNIL --> (4943958)LUA_TNIL
INFO: [8/32]a --> (4943970)a
INFO: [9/32]{empty package} --> (4943988)0
INFO: [10/32]c --> (49439a0)c
INFO: [11/32]error --> (49439b8)0
INFO: [12/32]LUA_TNIL --> (49439d0)LUA_TNIL
INFO: [13/32]self --> (49439e8)self
INFO: [14/32]return --> (4943a00)return
INFO: [15/32]function --> (4943a18)function
INFO: [16/32]LUA_TNIL --> (4943a30)LUA_TNIL
INFO: [17/32]wesnoth --> (4943a48)6
INFO: [18/32]__metatable --> (4943a60)2
INFO: [19/32]mt --> (4943a78)mt
INFO: [20/32]pkg_name --> (4943a90)pkg_name
INFO: [21/32]funcA --> (4943aa8)0
INFO: [22/32]end --> (4943ac0)end
INFO: [23/32]b --> (4943ad8)b
INFO: [24/32]__newindex --> (4943af0)1
INFO: [25/32]local --> (4943b08)local
INFO: [26/32]then --> (4943b20)then
INFO: [27/32]setmetatable --> (4943b38)5
INFO: [28/32]empty_pkg --> (4943b50)empty_pkg
INFO: [29/32]__index --> (4943b68)0
INFO: [30/32]global --> (4943b80)global
INFO: [31/32]k --> (4943b98)k
INFO: =============================

五、删除table,记录类型是lua脚本函数,它们的删除情况

删除table指的是把table置为nil。假设lua/terminal.lua这个文件定义了一个table:aplt_leagor_iaccess__terminal,该表中存在类型是lua脚本函数的记录。

  1. 加载lua/terminal.lua后,terminal.lua主Proto会很快被删掉(主Proto的特征是linedefined、lastlinedefined值都是0)。
  2. 若在下一次lua_gc(L, LUA_GCCOLLECT)前删除table,那table中记录对应Proto都会被删除。
  3. 若没有删除table时,又加载了lua/terminal.lua,那上一次加载时生成的记录对应Proto会被删除。即lua会确保最多保留一份aplt_leagor_iaccess__terminal中的Proto。
  4. 小程序编辑时,如果要重加载小程序的lua,在加载前要删除terminal表格。为提高加载效率,可使用二进制格式的lua文件。

如何调试以上情况?——删除Proto时必会调用luaF_freeproto,可在该函数内设置断点。以下是想查看lua何时删除lua/terminal.lua中的Proto。

<lua>/lfunc.cpp
------
void luaF_freeproto (lua_State *L, Proto *f) {
  const char *str = getstr(f->source);
  if (memcmp(str, "@lua/terminal.lua", strlen(str)) == 0) {
      int ii = 0;
  }
  luaM_freearray(L, f->code, f->sizecode);
  luaM_freearray(L, f->p, f->sizep);
  luaM_freearray(L, f->k, f->sizek);
  luaM_freearray(L, f->lineinfo, f->sizelineinfo);
  luaM_freearray(L, f->abslineinfo, f->sizeabslineinfo);
  luaM_freearray(L, f->locvars, f->sizelocvars);
  luaM_freearray(L, f->upvalues, f->sizeupvalues);
  luaM_free(L, f);
}

全部评论: 0

    写评论: