Done is better than perfect

0%

Lua虚拟机源码阅读

文件结构

Lua文件结构

提供外部使用的头文件

  • lua.h 定义了Lua的核心API。
  • lauxlib.h 定义了Lua的辅助API,此文件中定义的函数,是对lua.h中函数的简单封装,方便使用。
  • lualib.h 定义了Lua的标准库(coroutine, table, io, os, string, utf8, math, debug, package)。
  • luaconf.h 定义编译相关的宏,主要用于区分别不同平台的特性以及一些其他编译特性。

核心代码

Lua的核心代码主要分为以下几个模块:

核心API

  • lapi.h 定义了几个栈的操作宏(api_incr_top:栈顶加一, adjustresults:调节栈顶以容纳更多的返回值, api_checknelems:检查栈中是否有对应的栈空间)。
  • lapi.c lua.h头文件中定义核心API的实现。
  • lctype.h C语言的标准字符检查函数头文件,主要检查单个字符的类型,增加了EOZ,允许传入-1(EOZ)进行检测。
  • lctype.c 定义了符号表。
  • ldo.c 处理Lua的栈和调用结构。
  • lfunc.c 定义了用于操纵函数原型和闭包的辅助函数。
  • llimits.c 定义了一些基础的类型和安装依赖信息。
  • lmen.c 内存管理接口。
  • lobject.h Lua中的对象类型定义。
  • lobject.c 对象上的一些操作。
  • lstate.h 定义了lua_State相关的结构,这是Lua的核心结构。
  • lstate.c lstate.h中定义结构的相关操作。
  • lstring.c Lua字符串表,处理和缓存Lua中使用的字符串。
  • ltable.c 定义了Lua中Tale相关的操作。
  • ltm.c 元表相关的定义和操作。
  • ldump.c 将Lua函数转储为预编译块。
  • lundump.c 加载预编译的Lua块。
  • lzio.c 定义了一个buffer流,用于读取数据。

GC管理

  • lgc.c 垃圾回收器

词法,语法和语义分析器

  • llex.c 词法分析器。
  • lparser.h 语法和语义分析器。

Lua虚拟机(解释器)

  • lopcodes.h 定义了Lua虚拟机的操作码(OP_MOVE, OP_LOADI等)跟汇编的MOV指令类型,虚拟机就是用来执行这些指令的。
  • lopnames.h 定义了操作码对应的名字。
  • ljumptab.h 定义了Lua解释器跳转表相关的宏(vmdispatch, vmcase, vmbreak)。
  • lvm.c Lua的虚拟机用于执行opcode。

调试

  • ldebug.c 获取调试信息(函数调用, 行号信息等)。

标准库代码

  • lauxlib.c 实现了Lua的辅助API,此文件中定义的函数,是对lua.h中函数的简单封装,方便使用。
  • lbaselib.c 实现了Lua中使用的一些全局函数(assert, dofile, error, ipairs, pairs, print, getmetatable, setmetatable, xpcall, tostring, type等)。
  • lcorolib.c 协程(coroutine)。
  • ldblib.c 调试库(debug)。
  • liolib 标准IO操作(io)。
  • lmathlib.c 数学库(math)。
  • loadlib.c 加载库(package)。
  • loslib.c 系统库(os)。
  • lstrlib.c 字符串库(string)。
  • ltablib.c 表(table)。
  • lutf8lib.c utf8库(utf8)。
  • linit.c 注册所有的标注库到Lua, 核心函数luaL_openlibs,会遍历注册所有的标准库。

解释器和编译器

  • lua.c 通过上面的API构建的一个Lua解释器。
  • luac.c Lua的编译器,主要用于将Lua源文件编译成二进制格式。

核心代码

核心结构

global_State

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// 全局的State, 被所用State共享的,用于记录一些全局数据
struct global_State
{
lua_Alloc frealloc; // 内存分配函数,现在是使用的realloc(有连续空间,扩展内存是不会进行拷贝数的)
void *ud; // frealloc 的辅助数据,现在没有用,如果使用其他的内存分配函数可以使用这个预留的字段,在lauxlib.c中的l_alloc函数中是未使用的
l_mem totalbytes; // 当前分配的总内存(单位:Byte)
l_mem GCdebt; // 垃圾收集器还未归还的内存(单位:Byte)
lu_mem GCestimate; // 正在使用的非垃圾内存的预估值(单位:Byte)
lu_mem lastatomic; // 代垃圾收集的一个标志
stringtable strt; // 用于存储Lua中使用的短字符(长度小于等于40,通过宏LUAI_MAXSHORTLEN定义的)串表
TValue l_registry; // 注册表(Table类型),默认存储了全局主线程的State和全局Table,主要用来存储用户数据的元表
TValue nilvalue; // 定义的全局nil值
unsigned int seed; // 随机数种子
// 垃圾收集相关
lu_byte currentwhite;
lu_byte gcstate; // 垃圾收集器状态(GCSpropagate, GCSenteratomic, GCSatomic, GCSswpallgc, GCSswpfinobj, GCSswptobefnz, GCSswpend, GCScallfin, GCSpause)
lu_byte gckind; // 当前运行的GC类型(KGC_INC(增量GC), KGC_GEN(分代GC))
lu_byte gcstopem; // 是否停止紧急收集
lu_byte genminormul; // 次代收集倍数
lu_byte genmajormul; // 主代收集倍数
lu_byte gcrunning; // GC是否在运行
lu_byte gcemergency; // 是紧急收集
lu_byte gcpause; // 两个连续GC的暂停阈值
lu_byte gcstepmul; // GC的步长,用于控制GC的速度
lu_byte gcstepsize; // 单步长收集的的大小
GCObject *allgc; // 所有可收集对象的列表
GCObject **sweepgc; // 当前扫到的位置
GCObject *finobj; // 进入了析构的对象列表(还未真正的释放)
GCObject *gray; // 灰色对象列表
GCObject *grayagain; /* list of objects to be traversed atomically */
GCObject *weak; // 弱值弱引用表
GCObject *ephemeron; // 弱key引用表
GCObject *allweak; // 所有的若引用
GCObject *tobefnz; // 要GC的userdata列表
GCObject *fixedgc; // 不被GC回收的对象列表
// 分代收集器相关的字段
GCObject *survival; // 存活了一个GC周期的对象头
GCObject *old1; /* start of old1 objects */
GCObject *reallyold; /* objects more than one cycle old ("really old") */
GCObject *firstold1; /* first OLD1 object in the list (if any) */
GCObject *finobjsur; /* list of survival objects with finalizers */
GCObject *finobjold1; /* list of old1 objects with finalizers */
GCObject *finobjrold; /* list of really old objects with finalizers */
struct lua_State *twups; // 开放了上值的现场列表
lua_CFunction panic; // 为保护模式下发生异常时调用的函数
struct lua_State *mainthread; // 主线程的State对象
TString *memerrmsg; // 缓存的一个内存分配的错误消息
TString *tmname[TM_N];// 元方法名字(__index, __len等)
struct Table *mt[LUA_NUMTAGS]; 基础类型的元表()
TString *strcache[STRCACHE_N][STRCACHE_M]; // 字符串缓存,避免在使用一些字符串时频繁创建
lua_WarnFunction warnf; // 警告函数
void *ud_warn; // 警告函数的辅助数据
};

lua_State

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 每个线程的State
struct lua_State
{
CommonHeader; // 公共头(所有可回收对象都有)宏struct GCObject *next; lu_byte tt; lu_byte marked
lu_byte status; // 线程状态LUA_OK,LUA_YIELD,LUA_ERRRUN,LUA_ERRSYNTAX,LUA_ERRMEM,UA_ERRERR
lu_byte allowhook; // 是否允许hook
unsigned short nci; // 调用信息(CallInfo)的数量
StkId top; // 当前可以使用的栈值(也就是栈顶的上一个值)StkId( typedef StackValue *StkId)
global_State *l_G; // 关联的全局State
CallInfo *ci; // 当前函数的调用信息
StkId stack_last; // 栈底的下一个栈值
StkId stack; // 栈
UpVal *openupval; // 在栈中的开放上值(open upvalues)列表
StkId tbclist; // to-be-closed变量列表
GCObject *gclist; // GC对象列表
struct lua_State *twups; // 使用了开放上值(open upvalues)的线程列表
struct lua_longjmp *errorJmp; //保存setjump和longjump上下问环境,用于错误恢复
CallInfo base_ci; // 第一级调用信息(c 调用 Lua)
volatile lua_Hook hook; // 钩子(hook)函数指针
ptrdiff_t errfunc; // 当前的错误处理函数的栈索引
l_uint32 nCcalls; // 内嵌的调用数量(non-yieldable | C)
int oldpc; // 记录最后一次PC(指令)指针的位置
int basehookcount; // hook的基础计数
int hookcount; // 当前的hook计数
volatile l_signalT hookmask; // hook的掩码
};

CallInfo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
struct CallInfo {
StkId func; // 函数在栈中的位置(一个StackValue的地址)
StkId top; // 这个函数的栈顶位置(栈顶的地址)
struct CallInfo *previous, *next; // 调用链的前后指针
union {
struct {
const Instruction *savedpc; // 虚拟机的指令列表
volatile l_signalT trap; //
int nextraargs; // 变长函数的额外参数个数(...)
} l; // Lua函数
struct {
lua_KFunction k; /* continuation in case of yields */
ptrdiff_t old_errfunc;
lua_KContext ctx; /* context info. in case of yields */
} c; // c函数
} u;
union {
int funcidx; 当前调用函数在栈中的索引(func的地址-L.stack的地址求出函数在栈中的索引),仅用于在保护模式下调用的c函数
int nyield; // yielded的值的数量
int nres; // 返回的值的数量, 字段“nres”仅在从C函数返回时关闭tbc变量时使用
struct {
unsigned short ftransfer; 首个传递值的偏移
unsigned short ntransfer; 传递值的数量
} transferinfo;
} u2; // 从hook调用或返回时传递的值的信息
short nresults; 从这个函数返回的期望的结果数量
unsigned short callstatus; // 调用的状态(CIST_OAH:运行hook, CIST_C:函数调用,等)
} CallInfo;

TString

1
2
3
4
5
6
7
8
9
10
11
12
// 字符串
struct TString {
CommonHeader; // 所有的GC对象都必须包含此宏 #define CommonHeader struct GCObject *next; lu_byte tt; lu_byte marked
lu_byte extra; // 保留关键字(and, break, do 等)的枚举值, 此字段有非0值,表示是关键字。
lu_byte shrlen; // 短字符串(小于40)长度,短字符串会被缓存到global_State.strt中
unsigned int hash; // 长字符串对应的hash值
union {
size_t lnglen; // 长字符串的长度
struct TString *hnext; // 链接的字符串列表
} u;
char contents[1]; // 字符串本身的内容
};

Closure

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 闭包的公共头
#define ClosureHeader \
CommonHeader; lu_byte nupvalues; GCObject *gclist

// C闭包
typedef struct CClosure {
ClosureHeader;
lua_CFunction f; // C函数指针
TValue upvalue[1];// 上值列表
} CClosure;

// Lua闭包
typedef struct LClosure {
ClosureHeader;
struct Proto *p; // Lua函数原型
UpVal *upvals[1];// 上值列表
} LClosure;

// 包含C和Lua的闭包
typedef union Closure {
CClosure c;
LClosure l;
} Closure;

Proto

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 函数原型
typedef struct Proto {
CommonHeader; // 所有GC独享都有的
lu_byte numparams; // 函数的固定参数数量
lu_byte is_vararg; // 有变成参数吗
lu_byte maxstacksize; // 这个函数需要的寄存器数量(也就是有多少参数要压栈)
int sizeupvalues; // 函数的上值数量
int sizek; // 函数使用的常量数量
int sizecode; // 指令的数量
int sizelineinfo; // 行号的数量
int sizep; // 函数的子函数(内部函数)的数量
int sizelocvars; // 本地变量的数量
int sizeabslineinfo; // 每条指令对应的行号信息
int linedefined; // 函数的开始行, 用于调试
int lastlinedefined; // 函数的结束行, 用于调试
TValue *k; // 函数使用的常量值
Instruction *code; // 函数的指令列表
struct Proto **p; // 函数的内部函数原型列表
Upvaldesc *upvalues; // 函数的上值信息
ls_byte *lineinfo; // 函数源码的行列表, 用于调试
AbsLineInfo *abslineinfo; // 函数指令的行列表
LocVar *locvars; // 本地变量, 用于调试
TString *source; // 函数源码, 用于调试
GCObject *gclist; //
} Proto;

Upvaldesc

1
2
3
4
5
6
7
// 函数原型的上值描述
typedef struct Upvaldesc {
TString *name; // 上值得名字,用于调试
lu_byte instack; // 是否在栈上
lu_byte idx; // 在栈上或外部函数列表的索引
lu_byte kind; // 类型(0:常规, 1:常量, 2:to-be-closed(超出作用域会调用__close原方法,local t<clost> = xx 5.4新增的), 3:编译时常量)
} Upvaldesc;

UpVal

1
2
3
4
5
6
7
8
9
10
11
12
13
// 闭包的上值
typedef struct UpVal {
CommonHeader;
lu_byte tbc; // 是否是to-be-closed变量
TValue *v; // 指向堆栈或其自身的值
union {
struct {
struct UpVal *next; 列表的Next指针
struct UpVal **previous; 列表的Previous指针
} open; // open 的上值
TValue value; closed的上值
} u;
} UpVal;

Udata

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// userdata的头,分配的内存区域放在此结构的后面
typedef struct Udata {
CommonHeader;
unsigned short nuvalue; // 用户值的数量
size_t len; // bytes大小
struct Table *metatable; // userdata的元表
GCObject *gclist;
UValue uv[1]; // 用户值
} Udata;


// 其实就是TValue,只是为了保证最大化的字节对齐在里面放了所有类型
#define LUAI_MAXALIGN lua_Number n; double u; void *s; lua_Integer i; long l
typedef union UValue {
TValue uv;
LUAI_MAXALIGN; /* ensures maximum alignment for udata bytes */
} UValue;

Table

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Lua的核心数据结构,我们常用的表的结构
typedef struct Table {
CommonHeader;
lu_byte flags; // 表的标志位
lu_byte lsizenode; // 表的Hash部分的大小(2的lsizenode次幂),lsizenode存的是2的指数部分的值
unsigned int alimit; // 表的数组部分的大小
TValue *array; // 数组部分的开始地址
Node *node; // Hash部分的开始地址
Node *lastfree; // 用于记录最后一个空闲节点
struct Table *metatable; // 元表
GCObject *gclist;
} Table;

// hash表使用的节点,为什么Key的类型和值不能直接使用TValue? 因为字节对齐的原因分开定义能减少NodeKey的内存占用大小
typedef union Node {
struct NodeKey {
TValuefields; // 值(带类型的Value)#define TValuefields Value value_; lu_byte tt_
lu_byte key_tt; // Key的类型
int next; // Hash冲突时,用于表示下一个Key
Value key_val; // Key的值
} u;
TValue i_val; // 直接访问节点的TValue(只是为了方便访问值)
} Node;

flags字段说明:

7 位 表示alimit字段是否代表了数组的真实大小(0:是,1:不是),否则数组的真实大小是不小于alimit的2的整数次幂。 0-6 位 表示是否有TM_INDEX, TM_NEWINDEX, TM_GC, TM_MODE, TM_LEN, TM_EQ(0:有,1:没有)主要用于快速检测这几个常用的元方法是否存在。

Table同时使用数组和Hash表的结构,当使用的key是整形且不大于alimit时,Lua会使用array字段,当大于alimit时,则会转换成hash表的方式来。当数组或Hash表的大小需要扩容,应尽量避免这些没有避免的性能消耗。解决Hash冲突的方式是结合了开放寻址和链表的形式。

TValue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Lua中的值(所有类型都是用的此结构)
typedef union Value {
struct GCObject *gc; // 可收集(GC)对象
void *p; // 轻量级userdata
lua_CFunction f; // 轻量级c函数
lua_Integer i; // 整型
lua_Number n; // 浮点型数据
} Value;


// 带Tag(标记)的值(定义了TValue的字段),value_:为值 tt_ 为(Tag)标记字段,
#define TValuefields Value value_; lu_byte tt_

typedef struct TValue {
TValuefields;
} TValue;

Tag(标记)字段说明:

0-3 位 表示LUA_T*定义的类型(最多16种类型) 4-5 位 表示变体标志(额外附加的一些标志位,比如要表示一个Number类型是整形还是浮点型,则可以加上一个变体标志来表示,如果LUA_TNUMBER(3)宏默认定义为整形,为了表示是浮点型,则可以 1 & (3<<4) 这就在变体位加上了一个1,假设想区分单/双精度浮点则我们可以用1表单精度,2表示双精度,则可以使用2&(3<<4)来表示) 6 位 表示是否是可收集(GC)对象

StackValue

1
2
3
4
5
6
7
8
9
10
11
12
// 用于表示Lua栈上的值
typedef union StackValue {
TValue val;
struct {
TValuefields;
unsigned short delta;
} tbclist; // to-be-closed值,表示5.4新增的一种带作用域的变量,比如在lua中这样定义: local a<close> = xxx 当a超出作用范围时,则会调用a元表中的__close元方法
} StackValue;


// 栈元素的索引(通过将两个栈值的地址先减就能计算出两个栈值的相对位置,比如当前栈值地址-栈基地址,就能求出在栈中的索引)
typedef StackValue *StkId;

核心函数

State相关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// 线程状态+额外空间
typedef struct LX {
lu_byte extra_[LUA_EXTRASPACE];
lua_State l;
} LX;

// 主线程和全局状态对象
typedef struct LG {
LX l;
global_State g;
} LG;


// 创建一个State
LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
int i;
lua_State *L;
global_State *g;
LG *l = cast(LG *, (*f)(ud, NULL, LUA_TTHREAD, sizeof(LG))); // 分配一个LG对象
if (l == NULL) return NULL;
L = &l->l.l; // 线程状态对象
g = &l->g; // 全局状态对象
L->tt = LUA_VTHREAD; // 初始化类型为线程
g->currentwhite = bitmask(WHITE0BIT); // 垃圾收集器相关的,后续单独分析
L->marked = luaC_white(g);
preinit_thread(L, g); // 初始化一个线程状态,并关联线程状态对应的全局状态
g->allgc = obj2gco(L); // 将线程状态放入GC列表
L->next = NULL; // 下一个GC为空(当前它是最有一个GC对象,因为当前只分配了这一个线程状态对象)
incnny(L); ///* main thread is always non yieldable */
g->frealloc = f; // 保持内存分配函数到全局状态对象上
g->ud = ud; // NULL
g->warnf = NULL;
g->ud_warn = NULL;
g->mainthread = L; // 将当前线程状态设置为主线程
g->seed = luai_makeseed(L); // 产生随机数种子
g->gcrunning = 0;
g->strt.size = g->strt.nuse = 0; // 初始化短字符串缓存表
g->strt.hash = NULL;
setnilvalue(&g->l_registry); // 注册表设为空
g->panic = NULL;
g->gcstate = GCSpause; // 初始化时GC处于暂停状态
g->gckind = KGC_INC; // 默认的GC类型是增量GC
g->gcstopem = 0;
g->gcemergency = 0;
g->finobj = g->tobefnz = g->fixedgc = NULL;
g->firstold1 = g->survival = g->old1 = g->reallyold = NULL;
g->finobjsur = g->finobjold1 = g->finobjrold = NULL;
g->sweepgc = NULL;
g->gray = g->grayagain = NULL;
g->weak = g->ephemeron = g->allweak = NULL;
g->twups = NULL;
g->totalbytes = sizeof(LG);
g->GCdebt = 0;
g->lastatomic = 0;
setivalue(&g->nilvalue, 0);
setgcparam(g->gcpause, LUAI_GCPAUSE);
setgcparam(g->gcstepmul, LUAI_GCMUL);
g->gcstepsize = LUAI_GCSTEPSIZE;
setgcparam(g->genmajormul, LUAI_GENMAJORMUL);
g->genminormul = LUAI_GENMINORMUL;
for (i=0; i < LUA_NUMTAGS; i++) g->mt[i] = NULL; // 基础类型的元表设置为空
if (luaD_rawrunprotected(L, f_luaopen, NULL) != LUA_OK) { // 调用线程状态打开函数
// 不成功,则关闭线程状态并释放已经分配的内存
close_state(L);
L = NULL;
}
return L;
}

// 线程状态初始化函数
static void f_luaopen (lua_State *L, void *ud) {
global_State *g = G(L);
UNUSED(ud);
stack_init(L, L); // 初始化线程状态的栈
init_registry(L, g); // 初始化注册表
luaS_init(L); // 初始化短字符串缓存表
luaT_init(L); // 初始化元方法字符串,并缓存到全局状态的tmname中
luaX_init(L); // 初始化保留关键字字符串
g->gcrunning = 1; // 开启GC
setnilvalue(&g->nilvalue);
luai_userstateopen(L);
}

// 初始化一个栈
static void stack_init (lua_State *L1, lua_State *L) {
int i; CallInfo *ci;
// 分配一个大小为(40+5)的栈
L1->stack = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue);
L1->tbclist = L1->stack;
// 将栈中的值设置为nil
for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++)
setnilvalue(s2v(L1->stack + i));
L1->top = L1->stack; // 将栈顶设置到栈的初始位置
L1->stack_last = L1->stack + BASIC_STACK_SIZE; // 标记栈的最后一个栈值(不包含额外空间)
// 初始化首个调用信息
ci = &L1->base_ci;
ci->next = ci->previous = NULL;
ci->callstatus = CIST_C; // C调用
ci->func = L1->top;
ci->u.c.k = NULL;
ci->nresults = 0;
setnilvalue(s2v(L1->top));
L1->top++;
ci->top = L1->top + LUA_MINSTACK; // 设置当前调用信息的栈顶为当前线程状态的栈顶+20
L1->ci = ci; // 设置base_ci调用信息为当前的ci
}

// 创建注册表并初始化
static void init_registry (lua_State *L, global_State *g) {
// 创建注册表
Table *registry = luaH_new(L);
// 保持注册表到全局状态中去
sethvalue(L, &g->l_registry, registry);
// 扩展注册表大小为2
luaH_resize(L, registry, LUA_RIDX_LAST, 0);
// 将主线程保存到注册表中
setthvalue(L, &registry->array[LUA_RIDX_MAINTHREAD - 1], L);
// 创建一个全局表到注册表中
sethvalue(L, &registry->array[LUA_RIDX_GLOBALS - 1], luaH_new(L));
}

// 初始化stringtable
void luaS_init (lua_State *L) {
global_State *g = G(L);
int i, j;
stringtable *tb = &G(L)->strt;
tb->hash = luaM_newvector(L, MINSTRTABSIZE, TString*); // 初始化一个大小为128的TString指针数组
tablerehash(tb->hash, 0, MINSTRTABSIZE); // 清空tb->hash
tb->size = MINSTRTABSIZE;
g->memerrmsg = luaS_newliteral(L, MEMERRMSG); // 创建一个"not enough memory"消息
luaC_fix(L, obj2gco(g->memerrmsg)); // 不让其被GC
for (i = 0; i < STRCACHE_N; i++) // 给strcache填充一个有效值
for (j = 0; j < STRCACHE_M; j++)
g->strcache[i][j] = g->memerrmsg;
}

// 初始化元方法字符串
void luaT_init (lua_State *L) {
static const char *const luaT_eventname[] = {
"__index", "__newindex",
"__gc", "__mode", "__len", "__eq",
"__add", "__sub", "__mul", "__mod", "__pow",
"__div", "__idiv",
"__band", "__bor", "__bxor", "__shl", "__shr",
"__unm", "__bnot", "__lt", "__le",
"__concat", "__call", "__close"
};
int i;
for (i=0; i<TM_N; i++) {
G(L)->tmname[i] = luaS_new(L, luaT_eventname[i]);
luaC_fix(L, obj2gco(G(L)->tmname[i])); // 不被GC回收
}
}

// 初始化Lua中的关键字字符串
void luaX_init (lua_State *L) {
int i;
TString *e = luaS_newliteral(L, LUA_ENV); // 创建"_ENV"字符串
luaC_fix(L, obj2gco(e));
for (i=0; i<NUM_RESERVED; i++) {
TString *ts = luaS_new(L, luaX_tokens[i]); // 创建关键字字符串
luaC_fix(L, obj2gco(ts));
ts->extra = cast_byte(i+1); // 记录关键字枚举值
}
}

Proto和Closure相关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 分配一个Proto对象
LUAI_FUNC Proto *luaF_newproto (lua_State *L);
// 分配C闭包
LUAI_FUNC CClosure *luaF_newCclosure (lua_State *L, int nupvals);
// 分配一个Lua闭包
LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nupvals);


// 初始化上值
void luaF_initupvals (lua_State *L, LClosure *cl) {
int i;
for (i = 0; i < cl->nupvalues; i++) {
GCObject *o = luaC_newobj(L, LUA_VUPVAL, sizeof(UpVal));
UpVal *uv = gco2upv(o);
uv->v = &uv->u.value; /* make it closed */
setnilvalue(uv->v);
cl->upvals[i] = uv;
luaC_objbarrier(L, cl, uv);
}
}

// 查找或创建一个上值
UpVal *luaF_findupval (lua_State *L, StkId level) {
UpVal **pp = &L->openupval;
UpVal *p;
lua_assert(isintwups(L) || L->openupval == NULL);
while ((p = *pp) != NULL && uplevel(p) >= level) { /* search for it */
lua_assert(!isdead(G(L), p));
if (uplevel(p) == level) /* corresponding upvalue? */
return p; /* return it */
pp = &p->u.open.next;
}
/* not found: create a new upvalue after 'pp' */
return newupval(L, 0, level, pp);
}

CallInfo相关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
// 调用lua解析器生成Lua闭包和函数原型
int luaD_protectedparser (lua_State *L, ZIO *z, const char *name,
const char *mode) {
struct SParser p;
int status;
incnny(L); /* cannot yield during parsing */
p.z = z; p.name = name; p.mode = mode;
p.dyd.actvar.arr = NULL; p.dyd.actvar.size = 0;
p.dyd.gt.arr = NULL; p.dyd.gt.size = 0;
p.dyd.label.arr = NULL; p.dyd.label.size = 0;
luaZ_initbuffer(L, &p.buff);
status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc);
luaZ_freebuffer(L, &p.buff);
luaM_freearray(L, p.dyd.actvar.arr, p.dyd.actvar.size);
luaM_freearray(L, p.dyd.gt.arr, p.dyd.gt.size);
luaM_freearray(L, p.dyd.label.arr, p.dyd.label.size);
decnny(L);
return status;
}

// 准备函数的调用信息
CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) {
lua_CFunction f;
retry:
switch (ttypetag(s2v(func))) {
case LUA_VCCL: /* C closure */
f = clCvalue(s2v(func))->f;
goto Cfunc;
case LUA_VLCF: /* light C function */
f = fvalue(s2v(func));
Cfunc: {
int n; /* number of returns */
CallInfo *ci;
checkstackGCp(L, LUA_MINSTACK, func); /* ensure minimum stack size */
L->ci = ci = next_ci(L);
ci->nresults = nresults;
ci->callstatus = CIST_C;
ci->top = L->top + LUA_MINSTACK;
ci->func = func;
lua_assert(ci->top <= L->stack_last);
if (l_unlikely(L->hookmask & LUA_MASKCALL)) {
int narg = cast_int(L->top - func) - 1;
luaD_hook(L, LUA_HOOKCALL, -1, 1, narg);
}
lua_unlock(L);
n = (*f)(L); /* do the actual call */
lua_lock(L);
api_checknelems(L, n);
luaD_poscall(L, ci, n);
return NULL;
}
case LUA_VLCL: { /* Lua function */
CallInfo *ci;
Proto *p = clLvalue(s2v(func))->p;
int narg = cast_int(L->top - func) - 1; /* number of real arguments */
int nfixparams = p->numparams;
int fsize = p->maxstacksize; /* frame size */
checkstackGCp(L, fsize, func);
L->ci = ci = next_ci(L);
ci->nresults = nresults;
ci->u.l.savedpc = p->code; /* starting point */
ci->top = func + 1 + fsize;
ci->func = func;
L->ci = ci;
for (; narg < nfixparams; narg++)
setnilvalue(s2v(L->top++)); /* complete missing arguments */
lua_assert(ci->top <= L->stack_last);
return ci;
}
default: { /* not a function */
checkstackGCp(L, 1, func); /* space for metamethod */
luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */
goto retry; /* try again with metamethod */
}
}
}

// 调用函数
static void ccall (lua_State *L, StkId func, int nResults, int inc) {
CallInfo *ci;
L->nCcalls += inc;
if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS))
luaE_checkcstack(L);
if ((ci = luaD_precall(L, func, nResults)) != NULL) { /* Lua function? */
ci->callstatus = CIST_FRESH; /* mark that it is a "fresh" execute */
luaV_execute(L, ci); /* call it */
}
L->nCcalls -= inc;
}

void luaD_call (lua_State *L, StkId func, int nResults) {
ccall(L, func, nResults, 1);
}

Table相关

创建和释放:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 创建一个Tablew
Table *luaH_new (lua_State *L) {
GCObject *o = luaC_newobj(L, LUA_VTABLE, sizeof(Table)); // 分配一个Table对象
Table *t = gco2t(o);
t->metatable = NULL;
t->flags = cast_byte(maskflags); /* table has no metamethod fields */
t->array = NULL;
t->alimit = 0;
setnodevector(L, t, 0); // 初始化一个带虚拟节点的空table
return t;
}

// 释放
void luaH_free (lua_State *L, Table *t) {
freehash(L, t);
luaM_freearray(L, t->array, luaH_realasize(t));
luaM_free(L, t);
}

获取:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// 获取函数
const TValue *luaH_get (Table *t, const TValue *key) {
switch (ttypetag(key)) {
case LUA_VSHRSTR: return luaH_getshortstr(t, tsvalue(key)); // 短字符串key
case LUA_VNUMINT: return luaH_getint(t, ivalue(key)); // 整型key
case LUA_VNIL: return &absentkey; // Nil返回未找到static const TValue absentkey = { value_: {NULL}, tt_: makevariant(LUA_TNIL, 2)};
case LUA_VNUMFLT: { // 浮点型key
lua_Integer k;
if (luaV_flttointeger(fltvalue(key), &k, F2Ieq)) // 能转换成整形吗
return luaH_getint(t, k); // 能则调用获取整形key的函数,否则直接到default
}
default:
return getgeneric(t, key, 0); // 通用的get方法
}
}

// 整型key时,如果key的小于等于alimit则直接访问数组部分,当等于alimit + 1时则增加数组部分的大小并访问,否则就访问hash部分
const TValue *luaH_getint (Table *t, lua_Integer key) {
if (l_castS2U(key) - 1u < t->alimit)
return &t->array[key - 1];
else if (!limitequalsasize(t) &&
(l_castS2U(key) == t->alimit + 1 ||
l_castS2U(key) - 1u < luaH_realasize(t))) {
t->alimit = cast_uint(key);
return &t->array[key - 1];
}
else {
Node *n = hashint(t, key); 获取hash的slot(主节点)
for (;;) {
if (keyisinteger(n) && keyival(n) == key)
return gval(n);
else {
int nx = gnext(n);
if (nx == 0) break;
n += nx;
}
}
return &absentkey;
}
}

// “通用"的get(不是通用的:对于可能在数组部分的整数无效,对于具有整数值的浮点数也无效。)
static const TValue *getgeneric (Table *t, const TValue *key, int deadok) {
Node *n = mainpositionTV(t, key);
for (;;) { // 检查key是否在链表中的某处
if (equalkey(key, n, deadok))
return gval(n); 匹配到key就返回,否则需要在hash的冲突链中查找
else {
int nx = gnext(n); // 获取冲突链中的下一个
if (nx == 0) // 已经到最后一个了,则返回没有找到
return &absentkey;
n += nx;
}
}
}

// 获取key值对应的hash槽(slot)也就是hash值对应的主节点
static Node *mainpositionTV (const Table *t, const TValue *key) {
return mainposition(t, rawtt(key), valraw(key));
}

static Node *mainposition (const Table *t, int ktt, const Value *kvl) {
switch (withvariant(ktt)) {
case LUA_VNUMINT: { // 整形的hash函数
lua_Integer key = ivalueraw(*kvl);
return hashint(t, key);
}
case LUA_VNUMFLT: { // 浮点数的hash 函数
lua_Number n = fltvalueraw(*kvl);
return hashmod(t, l_hashfloat(n));
}
case LUA_VSHRSTR: { // 字符串的哈希函数,
TString *ts = tsvalueraw(*kvl);
return hashstr(t, ts);
}
case LUA_VLNGSTR: { // 字符串的函数函数
TString *ts = tsvalueraw(*kvl);
return hashpow2(t, luaS_hashlongstr(ts));
}
case LUA_VFALSE: // 布尔值的hash函数
return hashboolean(t, 0);
case LUA_VTRUE:
return hashboolean(t, 1);
case LUA_VLIGHTUSERDATA: { // 轻量级用户数据的hash函数
void *p = pvalueraw(*kvl);
return hashpointer(t, p);
}
case LUA_VLCF: { // C函数的hash函数
lua_CFunction f = fvalueraw(*kvl);
return hashpointer(t, f);
}
default: { // 其他类型的hash函数
GCObject *o = gcvalueraw(*kvl);
return hashpointer(t, o);
}
}
}

// Hash函数说明:
// 整形:直接用整形值对表的长度求模。
// 浮点数:
#define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1))))
static int l_hashfloat (lua_Number n) {
int i;
lua_Integer ni;
n = l_mathop(frexp)(n, &i) * -cast_num(INT_MIN);
if (!lua_numbertointeger(n, &ni)) { /* is 'n' inf/-inf/NaN? */
lua_assert(luai_numisnan(n) || l_mathop(fabs)(n) == cast_num(HUGE_VAL));
return 0;
}
else { /* normal case */
unsigned int u = cast_uint(i) + cast_uint(ni);
return cast_int(u <= cast_uint(INT_MAX) ? u : ~u);
}
}

// 字符串:
#define hashstr(t,str) hashpow2(t, (str)->hash)
unsigned int luaS_hash (const char *str, size_t l, unsigned int seed) {
unsigned int h = seed ^ cast_uint(l);
for (; l > 0; l--)
h ^= ((h<<5) + (h>>2) + cast_byte(str[l - 1]));
return h;
}

// 布尔值:
#define hashboolean(t,p) hashpow2(t, p)

// 轻量级用户数据, C函数和其他类型:
// 都是将地址转换为整型
#define hashpointer(t,p) hashmod(t, point2uint(p))

设置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// 设置值
void luaH_set (lua_State *L, Table *t, const TValue *key, TValue *value) {
const TValue *slot = luaH_get(t, key);
luaH_finishset(L, t, key, slot, value);
}

// 完成设置
void luaH_finishset (lua_State *L, Table *t, const TValue *key,
const TValue *slot, TValue *value) {
// 没有发现key,则新建一个,否则直接设置值
if (isabstkey(slot))
luaH_newkey(L, t, key, value);
else
setobj2t(L, cast(TValue *, slot), value);
}

// 在Hash表中添加一个新的key
void luaH_newkey (lua_State *L, Table *t, const TValue *key, TValue *value) {
Node *mp;
TValue aux;
// 检查key
if (l_unlikely(ttisnil(key))) // key为nil
luaG_runerror(L, "table index is nil");
else if (ttisfloat(key)) { // key为浮点数
lua_Number f = fltvalue(key);
lua_Integer k;
if (luaV_flttointeger(f, &k, F2Ieq)) { // 将浮点数转为整型
setivalue(&aux, k);
key = &aux; // 将其作为一个整数key插入
}
else if (l_unlikely(luai_numisnan(f)))
luaG_runerror(L, "table index is NaN");
}

// 检查值
if (ttisnil(value))
return; // 值为nil就不插入了
mp = mainpositionTV(t, key); // 获取key的在hash槽的主位置
if (!isempty(gval(mp)) || isdummy(t)) { // 获取到主位置
Node *othern;
Node *f = getfreepos(t); // 获取一个空的Node(倒着查找)
if (f == NULL) { // 没有空余位置时,则需要进行扩容
rehash(L, t, key); // 重新计算hash并扩容
luaH_set(L, t, key, value); 插入值到成长后的表
return;
}
lua_assert(!isdummy(t));
othern = mainposition(t, keytt(mp), &keyval(mp)); // 获取主位置上key值对应的主位置
if (othern != mp) { // 冲突节点在主位置外部(当前key对应的槽已经被其他的key占用了)
// 移动冲突节点的空位置,并将其重新连接上
while (othern + gnext(othern) != mp)
othern += gnext(othern);
gnext(othern) = cast_int(f - othern);
*f = *mp; // 拷贝冲突节点到空的位置上(next也一并拷贝了)
if (gnext(mp) != 0) {
gnext(f) += cast_int(mp - f); // 重新修正next的位置
gnext(mp) = 0;
}
setempty(gval(mp)); // 现在主位置空出来了,将其设置为空
}
else { // 冲突节点在主位置上,这说明了是相同的key
// 将空节点连接到冲突链上
if (gnext(mp) != 0)
gnext(f) = cast_int((mp + gnext(mp)) - f); /* chain new position */
else lua_assert(gnext(f) == 0);
gnext(mp) = cast_int(f - mp);
mp = f;
}
}
// 设置节点的key
setnodekey(L, mp, key);
luaC_barrierback(L, obj2gco(t), key);
lua_assert(isempty(gval(mp)));
// 设置节点的值
setobj2t(L, gval(mp), value);
}

HelloWorld

通过HelloWorld函数程序来说明Lua程序是如何运行起来的。

创建LuaState并注册标注库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
int main()
{
// 先构建一个lua state
lua_State* L = luaL_newstate();
// 注册lua的标注库
luaL_openlibs(L);

// 构造打印HelloWorld的Lua代码
std::string str = "print('hello world')";
// 加载并执行
luaL_dostring(L, str.c_str());
}

// 创建一个Lua State
LUALIB_API lua_State *luaL_newstate (void) {
lua_State *L = lua_newstate(l_alloc, NULL); // 注册全局内存分配函数l_alloc,辅助库里的内存分配函数使用的是realloc
if (l_likely(L)) {
lua_atpanic(L, &panic); // 注册全局错误回调函数
lua_setwarnf(L, warnfoff, L); // 设置全局的默认警告函数,默认警告关闭
}
return L;
}

// 需要注册的库
static const luaL_Reg loadedlibs[] = {
{LUA_GNAME, luaopen_base},
{LUA_LOADLIBNAME, luaopen_package},
{LUA_COLIBNAME, luaopen_coroutine},
{LUA_TABLIBNAME, luaopen_table},
{LUA_IOLIBNAME, luaopen_io},
{LUA_OSLIBNAME, luaopen_os},
{LUA_STRLIBNAME, luaopen_string},
{LUA_MATHLIBNAME, luaopen_math},
{LUA_UTF8LIBNAME, luaopen_utf8},
{LUA_DBLIBNAME, luaopen_debug},
{"mylib", luaopen_mylib},
{NULL, NULL}
};

// 注册所有的库
LUALIB_API void luaL_openlibs (lua_State *L) {
const luaL_Reg *lib;
for (lib = loadedlibs; lib->func; lib++) {
luaL_requiref(L, lib->name, lib->func, 1);
lua_pop(L, 1);
}
}

// 调用注册函数
LUALIB_API void luaL_requiref (lua_State *L, const char *modname,
lua_CFunction openf, int glb) {
luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); // 从注册表中获取_LOADED对应的table
lua_getfield(L, -1, modname); // 获取table中对应的模块(modname)
if (!lua_toboolean(L, -1)) { // 检查获取结果,如果没有就加载
lua_pop(L, 1); // 弹出刚lua_getfield获取的结果
lua_pushcfunction(L, openf); // 将要调用的c函数压入栈中
lua_pushstring(L, modname); // 传递到open函数的参数
lua_call(L, 1, 1); // 调用‘openf’函数去打开库(1:一个传入参数, 1:一个返回值)
lua_pushvalue(L, -1); // 将调用的结果(一个table)再次压入栈中
lua_setfield(L, -3, modname); // 将返回的table保存到key(modname)中
}
lua_remove(L, -2); // 移除栈中的LOADED table
if (glb) { // 需要将其保存到全局表中?
lua_pushvalue(L, -1); // 先将lua_call返回的table压入栈顶
lua_setglobal(L, modname); // 将栈顶的table设置到全局表中的modname字段
}
}

// 看一个简单的table库的打开函数
// 辅助库定义的一个创建库的函数,就是创建了一个table, 然后将tab_funcs中定义的字段名和函数绑定到table中
#define luaL_newlib(L,l) \
(luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))

// 需要注册的函数
static const luaL_Reg tab_funcs[] = {
{"concat", tconcat},
{"insert", tinsert},
{"pack", tpack},
{"unpack", tunpack},
{"remove", tremove},
{"move", tmove},
{"sort", sort},
{NULL, NULL}
};

// table库的打开函数
LUAMOD_API int luaopen_table (lua_State *L) {
luaL_newlib(L, tab_funcs);
return 1;
}

解析Lua代码生产指令 Lua解释器将Lua源码转换成Lua指令的过程。核心代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// 加载解析并执行Lua代码
#define luaL_dostring(L, s) \
(luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0))

// 加载并解析字符串
LUALIB_API int luaL_loadstring (lua_State *L, const char *s) {
return luaL_loadbuffer(L, s, strlen(s), s);
}
#define luaL_loadbuffer(L,s,sz,n) luaL_loadbufferx(L,s,sz,n,NULL)

LUALIB_API int luaL_loadbufferx (lua_State *L, const char *buff, size_t size,
const char *name, const char *mode) {
LoadS ls; // 将Lua代码放在ls中
ls.s = buff;
ls.size = size;
return lua_load(L, getS, &ls, name, mode); // 加载
}

LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data,
const char *chunkname, const char *mode) {
ZIO z; // 数据流缓存,用于解析Lua代码
int status;
lua_lock(L);
if (!chunkname) chunkname = "?";
luaZ_init(L, &z, reader, data); // 初始化数据流缓存
status = luaD_protectedparser(L, &z, chunkname, mode);
if (status == LUA_OK) { /* no errors? */
LClosure *f = clLvalue(s2v(L->top - 1)); /* get newly created function */
if (f->nupvalues >= 1) { /* does it have an upvalue? */
/* get global table from registry */
const TValue *gt = getGtable(L);
/* set global table as 1st upvalue of 'f' (may be LUA_ENV) */
setobj(L, f->upvals[0]->v, gt);
luaC_barrier(L, f->upvals[0], gt);
}
}
lua_unlock(L);
return status;
}

// 解析(词法,语法,语义分析)Lua代码
int luaD_protectedparser (lua_State *L, ZIO *z, const char *name,
const char *mode) {
struct SParser p; // 创建一个解析器
int status;
incnny(L);
p.z = z; p.name = name; p.mode = mode;
p.dyd.actvar.arr = NULL; p.dyd.actvar.size = 0;
p.dyd.gt.arr = NULL; p.dyd.gt.size = 0;
p.dyd.label.arr = NULL; p.dyd.label.size = 0;
luaZ_initbuffer(L, &p.buff);
status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc);// 调用解析函数
luaZ_freebuffer(L, &p.buff);
luaM_freearray(L, p.dyd.actvar.arr, p.dyd.actvar.size);
luaM_freearray(L, p.dyd.gt.arr, p.dyd.gt.size);
luaM_freearray(L, p.dyd.label.arr, p.dyd.label.size);
decnny(L);
return status;
}

// 解析函数
static void f_parser (lua_State *L, void *ud) {
LClosure *cl;
struct SParser *p = cast(struct SParser *, ud);
int c = zgetc(p->z); // 读取第一个字符
if (c == LUA_SIGNATURE[0]) {
checkmode(L, p->mode, "binary");
cl = luaU_undump(L, p->z, p->name); //解析编译好的二进制格式
}
else {
checkmode(L, p->mode, "text");
cl = luaY_parser(L, p->z, &p->buff, &p->dyd, p->name, c); // 解析源码
}
lua_assert(cl->nupvalues == cl->p->sizeupvalues);
luaF_initupvals(L, cl); // 初始化闭包的上值
}

解析完后会生成一个原型(Proto)对象,对象的code字段存放的就是对应的指令码,指令码有如下5种格式: 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 iABC C(8) | B(8) |k| A(8) | Op(7) | iABx Bx(17) | A(8) | Op(7) | iAsBx sBx (signed)(17) | A(8) | Op(7) | iAx Ax(25) | Op(7) | isJ sJ(25) | Op(7) |

开头的7位表示操作码,后面的表示操作码对应的参数。

执行指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void luaV_execute (lua_State *L, CallInfo *ci) {
LClosure *cl;
TValue *k;
StkId base;
const Instruction *pc;
......
base = ci->func + 1; // 函数在栈上的地址
// 主解释循环
for (;;) {
Instruction i; // 当前被执行的指令
StkId ra; // 寄存器A(就是一个栈值的地址)
vmfetch(); // pc指针加1并给ra寄存器赋值
lua_assert(base == ci->func + 1);
lua_assert(base <= L->top && L->top < L->stack_last);
lua_assert(isIT(i) || (cast_void(L->top = base), 1));
// 获取操作码
vmdispatch (GET_OPCODE(i)) {
vmcase(OP_MOVE) { // 移动指令OP_MOVE将寄存器B的值赋值个寄存A, /* A B R[A] := R[B] */
setobjs2s(L, ra, RB(i));
vmbreak;
}
vmcase(OP_LOADI) { // 装载一个整形常量值到ra
lua_Integer b = GETARG_sBx(i);
setivalue(s2v(ra), b);
vmbreak;
}
......
}
}
}

总结

通过对源码的阅读,对Lua虚拟机的内部结构以及运行机制有更深入的理解。但是并不是所有细节和模块都进行了阅读,比如像Lua词法,语法和语义分析以及GC相关的代码就是直接跳过的。Lua本身也不是一篇文章就能理解完的,对于Lua虚拟机的学习先暂时告一段落。后续闲了会再对GC相关的代码进行阅读,至于词法,语法和语义分析这块的代码暂时不打算去学习,主要是在实际项目中暂时不需要这方面的内容。