0x1. 数据结构
在lua的脚本中,一个文件对应了一个main函数,这个main函数由解释器自动生成。这一点和C语言很类似,相当于解释器给用户提供的入口和C语言是一样的。调用lua语法解析器解析之后的结构为Proto
,同时这个结构也是用于描述一个函数。
typedef struct Proto {
CommonHeader;
TValue *k; /* constants used by the function */
Instruction *code;
struct Proto **p; /* functions defined inside the function */
int *lineinfo; /* map from opcodes to source lines */
struct LocVar *locvars; /* information about local variables */
TString **upvalues; /* upvalue names */
TString *source;
int sizeupvalues;
int sizek; /* size of `k' */
int sizecode;
int sizelineinfo;
int sizep; /* size of `p' */
int sizelocvars;
int linedefined;
int lastlinedefined;
GCObject *gclist;
lu_byte nups; /* number of upvalues */
lu_byte numparams;
lu_byte is_vararg;
lu_byte maxstacksize;
} Proto;
从上面代码中的注释可以大致看出每个字段的用途。这里再讲几个重点的字段:
Instruction *code
这个保存该函数的所有指令(内部函数除外)。TValue *k
这个保存了该函数内部的常量。比如local s = "12345"
,这个字符串“12345”
就会保存到k
这个字段里面。这个在后面的指令执行里面会用到这个。Proto **p
这个保存了定义在这个函数内部的函数,下图是一个示意图,可以看出一个lua源文件最终的编译结果就是以**Proto(main)**
为根节点的一棵树。
0x2. 编译过程
入口函数
lua编译的入口函数为lua_Yparser
,输入参数为一个IO对象,这个对象可能是文件,也可能是一段内存,也可能是个socket。
LUAI_FUNC Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
const char *name);
解析过程
例子
function sum(p1, p2)
local p = p1 + p2
return p
end
function mul(p1, p2)
return p1 * p2
end
local s = sum(1, 2)
local m = mul(2, 3)
--
编译结果
这里main函数即为lua源文件的默认函数。
main <func.lua:0,0> (13 instructions, 52 bytes at 0x5613840e7870)
0+ params, 4 slots, 0 upvalues, 2 locals, 5 constants, 2 functions
1 [4] CLOSURE 0 0 ; 0x5613840e7a50 --创建函数sum
2 [1] SETGLOBAL 0 -1 ; sum --保存函数sum
3 [8] CLOSURE 0 1 ; 0x5613840e7f10 --创建函数mul
4 [6] SETGLOBAL 0 -2 ; mul --保存函数mul
5 [10] GETGLOBAL 0 -1 ; sum
6 [10] LOADK 1 -3 ; 1
7 [10] LOADK 2 -4 ; 2
8 [10] CALL 0 3 2
9 [11] GETGLOBAL 1 -2 ; mul
10 [11] LOADK 2 -4 ; 2
11 [11] LOADK 3 -5 ; 3
12 [11] CALL 1 3 2
13 [11] RETURN 0 1
constants (5) for 0x5613840e7870:
1 "sum"
2 "mul"
3 1
4 2
5 3
locals (2) for 0x5613840e7870:
0 s 9 13
1 m 13 13
upvalues (0) for 0x5613840e7870:
函数sum的编译结果:
function <func.lua:1,4> (3 instructions, 12 bytes at 0x5613840e7a50)
2 params, 3 slots, 0 upvalues, 3 locals, 0 constants, 0 functions
1 [2] ADD 2 0 1
2 [3] RETURN 2 2
3 [4] RETURN 0 1
constants (0) for 0x5613840e7a50:
locals (3) for 0x5613840e7a50:
0 p1 1 3
1 p2 1 3
2 p 2 3
upvalues (0) for 0x5613840e7a50:
函数mul的编译结果:
function <func.lua:6,8> (3 instructions, 12 bytes at 0x5613840e7f10)
2 params, 3 slots, 0 upvalues, 2 locals, 0 constants, 0 functions
1 [7] MUL 2 0 1
2 [7] RETURN 2 2
3 [8] RETURN 0 1
constants (0) for 0x5613840e7f10:
locals (2) for 0x5613840e7f10:
0 p1 1 3
1 p2 1 3
upvalues (0) for 0x5613840e7f10:
--
从上面可以看出每个函数都多出RETURN 0 1
,这个lua函数默认的返回语句。