预编译的代码

简介: 预编译的代码

正如之前所提到的, Lua 语言会在运行源代码之前先对其进行预编译。 Lua 语言也允许我们以预编译的形式分发代码。


生成预编译文件(也被称为二进制文件)的最简单方式是,使用标准发行版中附带的 luac 程序,例如,下面命令会创建文件 prog.lua 的预编译版本 prog.lc :

$ luac -o prog.lc prog.lua


Lua 解析器会像执行普通 Lua 代码一样执行这个新文件,完成与原来代码完全一致的动作:

$ lua prog.lc


几乎在 Lua 语言中所有能够使用源码的地方都可以使用预编译代码。特别地,函数 loadfile 和函数 load 都可以接受预编译代码。


我们可以直接在 Lua 语言中实现一个最简单的 luac :

p = loadfile(arg[1])
f = io.open(arg[2], "wb")
f:write(string.dump(p))
f:close()


这里的关键函数是 string.dump ,该函数的传入参数是一个 Lua 函数,返回值是传入函数对应的字符串形式的预编译代码(已被正确的格式化,可由 Lua 语言直接加载)。


luac 程序提供了一些有意思的选项。特别地,选项 -l 会列出编译器为指定代码生成的操作码opcode )。例如,新建一个 main.lua 文件,写入如下内容:

a = x + y - z


执行 luac -l 命令得到输出:

$  luac -l main.lua 
main <main.lua:0,0> (7 instructions at 0x5560deb9fc60)
0+ params, 2 slots, 1 upvalue, 0 locals, 4 constants, 0 functions
        1       [1]     GETTABUP        0 0 -2  ; _ENV "x"
        2       [1]     GETTABUP        1 0 -3  ; _ENV "y"
        3       [1]     ADD             0 0 1
        4       [1]     GETTABUP        1 0 -4  ; _ENV "z"
        5       [1]     SUB             0 0 1
        6       [1]     SETTABUP        0 -1 0  ; _ENV "a"
        7       [1]     RETURN          0 1


提示

关于Lua语言内部实现的细节可以查看Lua OpCode笔记。


预编译形式的代码不一定比源代码更小,但是却加载的更快。预编译形式的代码的另一个好处是,可以避免由于意外而修改代码。然而,与源代码不同,蓄意损坏或构造的二进制代码可能会让 Lua 解析器崩溃或甚至执行用户提供的机器码。当运行一般的代码时通常无需担心,但应该避免运行以预编译形式给出的非受信代码。这种需求,函数 load 正好有一个选项可以适用。


除了必需的第一个参数外,函数 load 还有 3 个可选参数。第 2 个参数是程序段的名称,只在错误信息中被适用。第 4 个参数是环境,会在环境部分详细讲解。第 3 个参数正是我们这里所关心的,它控制了允许加载的代码段类型。如果该参数存在,则只能是如下的字符串:

  • "t":允许加载文本(普通)类型的代码段。
  • "b":只允许加载二进制(预编译)类型的代码段。
  • "bt":允许同时加载文本和二进制类型的代码段——默认情况
目录
相关文章
|
5月前
预编译处理\条件编译指令
预编译处理\条件编译指令
21 2
|
6月前
|
编译器 C语言 Windows
编译&执行C程序
编译&执行C程序。
52 2
|
11月前
|
存储 程序员 编译器
宏(预编译)详解
宏(预编译)详解
|
JavaScript Go
脚本的预编译
脚本的预编译
58 0
函数调用的预编译
函数调用的预编译
63 0
|
存储 自然语言处理 编译器
【C语言】程序环境和预编译
【C语言】程序环境和预编译
重新编译Telepresence,谈如何写编译脚本
重新编译Telepresence,谈如何写编译脚本
95 0
|
JavaScript 前端开发 Go
细读 JS | 深入了解从预编译到解析执行的过程
细读 JS | 深入了解从预编译到解析执行的过程
186 0