0x1. 数据结构


在lua的脚本中,一个文件对应了一个main函数,这个main函数由解释器自动生成。这一点和C语言很类似,相当于解释器给用户提供的入口和C语言是一样的。调用lua语法解析器解析之后的结构为Proto,同时这个结构也是用于描述一个函数。

  1. typedef struct Proto {
  2. CommonHeader;
  3. TValue *k; /* constants used by the function */
  4. Instruction *code;
  5. struct Proto **p; /* functions defined inside the function */
  6. int *lineinfo; /* map from opcodes to source lines */
  7. struct LocVar *locvars; /* information about local variables */
  8. TString **upvalues; /* upvalue names */
  9. TString *source;
  10. int sizeupvalues;
  11. int sizek; /* size of `k' */
  12. int sizecode;
  13. int sizelineinfo;
  14. int sizep; /* size of `p' */
  15. int sizelocvars;
  16. int linedefined;
  17. int lastlinedefined;
  18. GCObject *gclist;
  19. lu_byte nups; /* number of upvalues */
  20. lu_byte numparams;
  21. lu_byte is_vararg;
  22. lu_byte maxstacksize;
  23. } Proto;

从上面代码中的注释可以大致看出每个字段的用途。这里再讲几个重点的字段:

  • Instruction *code这个保存该函数的所有指令(内部函数除外)。
  • TValue *k这个保存了该函数内部的常量。比如local s = "12345",这个字符串“12345”就会保存到k这个字段里面。这个在后面的指令执行里面会用到这个。
  • Proto **p这个保存了定义在这个函数内部的函数,下图是一个示意图,可以看出一个lua源文件最终的编译结果就是以**Proto(main)**为根节点的一棵树。

image.png

0x2. 编译过程


入口函数

lua编译的入口函数为lua_Yparser,输入参数为一个IO对象,这个对象可能是文件,也可能是一段内存,也可能是个socket。

LUAI_FUNC Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
                                            const char *name);

image.png

解析过程

image.png

例子

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函数默认的返回语句。