为了达到较高的执行效率,lua代码并不是直接被Lua解释器解释执行,而是会先编译为字节码,然后再交给lua虚拟机去执行
lua代码称为chunk,编译成的字节码则称为二进制chunk(Binary chunk)
lua.exe、wlua.exe解释器可直接执行lua代码(解释器内部会先将其编译成字节码),也可执行使用luac.exe将lua代码预编译(Precompiled)为字节码
使用预编译的字节码并不会加快脚本执行的速度,但可以加快脚本加载的速度,并在一定程度上保护源代码
luac.exe可作为编译器,把lua代码编译成字节码,同时可作为反编译器,分析字节码的内容
luac.exe -v // 显示luac的版本号
luac.exe -s -o d:\Hello.out Hello.lua // 编译得到Hello.lua的二进制chunk文件d:\Hello.out(去掉调试符号)
luac.exe -p Hello1.lua Hello2.lua // 对Hello1.lua和Hello2.lua只进行语法检测(注:只会检查语法规则,不会检查变量、函数等是否定义和实现,函数参数返回值是否合法)
lua编译器以函数为单位对源代码进行编译,每个函数会被编译成一个称之为原型(Prototype)的结构
原型主要包含6部分内容:函数基本信息(basic info:含参数数量、局部变量数量等信息)、字节码(bytecodes)、常量(constants)表、upvalue(闭包捕获的非局部变量)表、调试信息(debug info)、子函数原型列表(sub functions)
原型结构使用这种嵌套递归结构,来描述函数中定义的子函数
注:lua允许开发者可将语句写到文件的全局范围中,这是因为lua在编译时会将整个文件放到一个称之为main函数中,并以它为起点进行编译
Hello.lua源代码如下:
1 print ("hello")2
3 functionadd(a, b)4 return a+b5 end
编译得到的Hello.out的二进制为:
二进制chunk(Binary chunk)的格式并没有标准化,也没有任何官方文档对其进行说明,一切以lua官方实现的源代码为准。
其设计并没有考虑跨平台,对于需要超过一个字节表示的数据,必须要考虑大小端(Endianness)问题。
lua官方实现的做法比较简单:编译lua脚本时,直接按照本机的大小端方式生成二进制chunk文件,当加载二进制chunk文件时,会探测被加载文件的大小端方式,如果和本机不匹配,就拒绝加载.
二进制chunk格式设计也没有考虑不同lua版本之间的兼容问题,当加载二进制chunk文件时,会检测其版本号,如果和当前lua版本不匹配,就拒绝加载
另外,二进制chunk格式设计也没有被刻意设计得很紧凑。在某些情况下,一段lua代码编译成二进制chunk后,甚至会被文本形式的源代码还要大。
预编译成二进制chunk主要是为了提升加载速度,因此这也不是很大的问题.
头部字段:
字段
字节数
值
解释
签名(signature)
byte[4]
0x1B4C7561
二进制chunk文件的魔数,分别是ESC、L、u、a的ASCII码
版本号(version)
byte
0x53
lua语言的版本号由3部分组成:大版本号(Major Version)、小版本号(Minor Version)和发布号(Release Version)
比如:当前lua的版本号为5.3.5,那么大版本号为5,小版本号为3、发布号为5
由于发布号的增加只是为了修复bug,不会对二进制chunk文件格式进行调整,因此版本号(version)只存储了大版本号和小版本号
格式号(format)
byte
0x00
二进制chunk文件的格式号
lua虚拟机在加载二进制chunk时,会检查其格式号,如果和虚拟机本身的格式号不匹配,就会拒绝加载该文件
luacData
byte[6]
0x19930D0A1A0A
其中前两个字节0x1993,是lua1.0发布的年份;后四个字节依次是回车符(0x0D)、换行符(0x0A)、替换符(0x1A)和另一个换行符
cintSize
byte
0x04
cint数据类型size
sizeSize
byte
0x04
size_t数据类型size
instructionSize
byte
0x04
lua虚拟机指令size
luaIntegerSize
byte
0x08
lua整型size
luaNumberSize
byte
0x08
lua浮点数size
lua整数值(luacInt)
int64
0x7856000000000000
加载二进制chunk文件时,会用该字段检测其大小端和本机是否匹配
格式号(luacNum)
float64
0x0000000000287740
浮点数值为370.5
加载二进制chunk文件时,会用该字段检测其使用的浮点数格式(目前主流平台一般都采用IEEE754浮点数格式)
嵌套的函数原型:
0B4048656C6C6F2E6C7561
源文件名Hello.lua;-s去掉调试信息后,该项数值为:00
main
00000000 00000000
main函数起始行列号
00
函数参数个数
01
函数为不定参数
02
寄存器数量
06000000
函数指令数目
06004000
第1条指令
41400000
第2条指令
24400001
第3条指令
2C000000
第4条指令
08000081
第5条指令
26008000
第6条指令
03000000
常量数目
04
第1个常量tag,04表示字符串
067072696E74
第1个常量内容
04
第2个常量tag,04表示字符串
0668656C6C6F
第2个常量内容
04
第3个常量tag,04表示字符串
04616464
第3个常量内容
01000000
Upvalue数目
0100
第1个Upvalue
01000000
子函数原型数目
add
03000000 05000000
add函数起始行列号
02
函数参数个数
00
函数为不定参数
03
寄存器数量
03000000
函数指令数目
8D400000
第1条指令
A6000001
第2条指令
26008000
第3条指令
00000000
常量数目
00000000
Upvalue数目
00000000
子函数原型数目
03000000
行号数目;-s去掉调试信息后,该项数值为:00000000;为0时下面的信息也都没有
04000000
第1条指令行号
04000000
第2条指令行号
05000000
第3条指令行号
02000000
局部变量数目;-s去掉调试信息后,该项数值为:00000000;为0时下面的信息也都没有
0261
第1个局部变量名称
00000000
第1个局部变量起始指令索引
03000000
第1个局部变量终止指令索引
0262
第2个局部变量名称
00000000
第2个局部变量起始指令索引
03000000
第2个局部变量终止指令索引
00000000
Upvalue名称数目;-s去掉调试信息后,该项数值为:00000000;为0时下面的信息也都没有
06000000
行号数目