Skip to content

Commit 40c4255

Browse files
author
Zing
committed
feat(lua_engine): 添加字节码支持并扩展require功能
添加 Lua 字节码编译和执行功能,支持预编译脚本以提高性能 修改 require 函数以支持加载 .gluac 字节码文件 添加相关文档和示例代码说明字节码功能的使用和限制
1 parent c9832f1 commit 40c4255

File tree

4 files changed

+855
-6
lines changed

4 files changed

+855
-6
lines changed

docs/lua_engine/bytecode/README.md

Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
# Lua 字节码功能
2+
3+
## 概述
4+
5+
AutoGo ScriptEngine 现已支持 Lua 字节码功能,允许预编译 Lua 脚本并重复执行,提高执行效率。
6+
7+
## 重要说明
8+
9+
⚠️ **gopher-lua 的字节码格式与标准 Lua 字节码不兼容**
10+
11+
- gopher-lua 使用自己的字节码格式,这是 gopher-lua 特有的实现
12+
- 标准 Lua 编译器(luac)生成的 `.luac` 文件无法在 gopher-lua 中执行
13+
- gopher-lua 的字节码仅能在 gopher-lua 虚拟机中执行
14+
- 字节码格式可能会随 gopher-lua 版本变化,建议保留源码
15+
16+
## 功能特性
17+
18+
### 1. 编译 Lua 源码为字节码
19+
20+
```go
21+
package main
22+
23+
import (
24+
"fmt"
25+
"log"
26+
27+
"github.com/ZingYao/autogo_scriptengine/lua_engine"
28+
)
29+
30+
func main() {
31+
// 初始化 Lua 引擎
32+
engine := lua_engine.NewLuaEngine(nil)
33+
defer engine.Close()
34+
35+
// Lua 源码
36+
source := `
37+
local function add(a, b)
38+
return a + b
39+
end
40+
print("结果: " .. add(3, 5))
41+
`
42+
43+
// 编译源码为字节码
44+
bytecode, err := engine.CompileString(source, "my_script")
45+
if err != nil {
46+
log.Fatalf("编译失败: %v", err)
47+
}
48+
49+
fmt.Printf("字节码编译成功! 名称: %s\n", bytecode.GetName())
50+
}
51+
```
52+
53+
### 2. 执行预编译的字节码
54+
55+
```go
56+
// 执行字节码
57+
err = engine.ExecuteBytecode(bytecode)
58+
if err != nil {
59+
log.Fatalf("执行失败: %v", err)
60+
}
61+
```
62+
63+
### 3. 从文件编译字节码
64+
65+
```go
66+
// 从文件编译
67+
bytecode, err := engine.CompileFile("scripts/main.lua")
68+
if err != nil {
69+
log.Fatalf("编译文件失败: %v", err)
70+
}
71+
72+
// 执行
73+
err = engine.ExecuteBytecode(bytecode)
74+
if err != nil {
75+
log.Fatalf("执行失败: %v", err)
76+
}
77+
```
78+
79+
### 4. 使用全局函数
80+
81+
```go
82+
// 使用全局引擎
83+
engine := lua_engine.GetLuaEngine()
84+
defer engine.Close()
85+
86+
// 全局编译函数
87+
bytecode, err := lua_engine.CompileString("print('Hello, World!')", "hello")
88+
if err != nil {
89+
log.Fatal(err)
90+
}
91+
92+
// 全局执行函数
93+
err = lua_engine.ExecuteBytecode(bytecode)
94+
if err != nil {
95+
log.Fatal(err)
96+
}
97+
```
98+
99+
### 5. 异步执行字节码
100+
101+
```go
102+
// 异步执行字节码
103+
err := engine.ExecuteBytecodeWithMode(bytecode, lua_engine.ExecuteModeAsync)
104+
if err != nil {
105+
log.Fatal(err)
106+
}
107+
```
108+
109+
## require 函数支持字节码模块
110+
111+
`require` 函数现在支持加载 `.gluac` 字节码文件:
112+
113+
### 模块加载优先级
114+
115+
1. `module.gluac` - gopher-lua 字节码文件
116+
2. `module.lua` - Lua 源码文件
117+
3. `module/init.gluac` - 目录形式的字节码模块
118+
4. `module/init.lua` - 目录形式的源码模块
119+
120+
### 使用示例
121+
122+
假设有以下文件结构:
123+
124+
```
125+
scripts/
126+
├── utils.gluac # 预编译的字节码模块
127+
├── utils.lua # 源码模块(备用)
128+
└── main.lua # 主脚本
129+
```
130+
131+
`main.lua` 中:
132+
133+
```lua
134+
-- 加载字节码模块(优先加载 .gluac 文件)
135+
local utils = require("utils")
136+
137+
-- 使用模块功能
138+
utils.hello()
139+
```
140+
141+
## 性能优势
142+
143+
预编译字节码的主要优势:
144+
145+
1. **减少解析开销**:避免每次执行时的词法分析和语法分析
146+
2. **提高启动速度**:直接加载编译后的字节码,减少启动时间
147+
3. **适合重复执行**:在循环或高频调用场景下性能提升明显
148+
149+
### 性能对比示例
150+
151+
```go
152+
package main
153+
154+
import (
155+
"fmt"
156+
"time"
157+
158+
"github.com/ZingYao/autogo_scriptengine/lua_engine"
159+
)
160+
161+
func main() {
162+
engine := lua_engine.NewLuaEngine(nil)
163+
defer engine.Close()
164+
165+
source := "local a = 1 + 1"
166+
167+
// 测试直接执行
168+
start := time.Now()
169+
for i := 0; i < 10000; i++ {
170+
engine.ExecuteString(source)
171+
}
172+
directDuration := time.Since(start)
173+
174+
// 测试字节码执行
175+
bytecode, _ := engine.CompileString(source)
176+
start = time.Now()
177+
for i := 0; i < 10000; i++ {
178+
engine.ExecuteBytecode(bytecode)
179+
}
180+
bytecodeDuration := time.Since(start)
181+
182+
fmt.Printf("直接执行: %v\n", directDuration)
183+
fmt.Printf("字节码执行: %v\n", bytecodeDuration)
184+
fmt.Printf("性能提升: %.2fx\n", float64(directDuration)/float64(bytecodeDuration))
185+
}
186+
```
187+
188+
## API 参考
189+
190+
### Bytecode 类型
191+
192+
```go
193+
type Bytecode struct {
194+
Proto *lua.FunctionProto // 编译后的函数原型
195+
Name string // 字节码名称
196+
}
197+
```
198+
199+
### 方法列表
200+
201+
| 方法 | 说明 |
202+
|------|------|
203+
| `CompileString(source string, name ...string)` | 编译源码字符串为字节码 |
204+
| `CompileFile(path string)` | 编译源码文件为字节码 |
205+
| `ExecuteBytecode(bytecode *Bytecode)` | 执行字节码 |
206+
| `ExecuteBytecodeWithMode(bytecode *Bytecode, mode ExecuteMode)` | 带模式的执行字节码 |
207+
| `bytecode.GetName()` | 获取字节码名称 |
208+
| `bytecode.GetFunctionProto()` | 获取函数原型 |
209+
210+
### 全局函数
211+
212+
| 函数 | 说明 |
213+
|------|------|
214+
| `lua_engine.CompileString(source, name)` | 全局编译函数 |
215+
| `lua_engine.CompileFile(path)` | 全局编译文件函数 |
216+
| `lua_engine.ExecuteBytecode(bytecode)` | 全局执行函数 |
217+
218+
## 最佳实践
219+
220+
### 1. 保留源码
221+
222+
由于 gopher-lua 字节码格式可能变化,建议始终保留原始源码:
223+
224+
```go
225+
// 编译并保存字节码
226+
bytecode, err := engine.CompileString(source, "script")
227+
if err != nil {
228+
log.Fatal(err)
229+
}
230+
231+
// 同时保存源码和字节码
232+
// 源码用于备份和调试
233+
// 字节码用于生产环境
234+
```
235+
236+
### 2. 使用字节码缓存
237+
238+
对于频繁执行的脚本,使用字节码缓存:
239+
240+
```go
241+
// 初始化时编译所有常用脚本
242+
var scriptCache = make(map[string]*lua_engine.Bytecode)
243+
244+
func init() {
245+
engine := lua_engine.GetLuaEngine()
246+
247+
// 预编译常用脚本
248+
scripts := map[string]string{
249+
"utils": "scripts/utils.lua",
250+
"math": "scripts/math.lua",
251+
}
252+
253+
for name, path := range scripts {
254+
bytecode, err := engine.CompileFile(path)
255+
if err != nil {
256+
log.Printf("编译 %s 失败: %v", name, err)
257+
continue
258+
}
259+
scriptCache[name] = bytecode
260+
}
261+
}
262+
```
263+
264+
### 3. 错误处理
265+
266+
```go
267+
bytecode, err := engine.CompileString(source)
268+
if err != nil {
269+
// 编译错误,检查源码语法
270+
log.Printf("编译错误: %v", err)
271+
return
272+
}
273+
274+
err = engine.ExecuteBytecode(bytecode)
275+
if err != nil {
276+
// 运行时错误
277+
log.Printf("执行错误: %v", err)
278+
return
279+
}
280+
```
281+
282+
## 限制和注意事项
283+
284+
1. **字节码格式不兼容**:gopher-lua 字节码与标准 Lua 字节码不兼容
285+
2. **版本依赖**:字节码格式可能随 gopher-lua 版本变化
286+
3. **调试困难**:字节码难以调试,建议开发阶段使用源码
287+
4. **序列化限制**:gopher-lua 的 FunctionProto 不支持直接序列化
288+
289+
## 示例代码
290+
291+
完整的示例代码请参考:
292+
293+
- [examples/lua_engine/bytecode/main.go](../../examples/lua_engine/bytecode/main.go)
294+
295+
## 更新日志
296+
297+
### v1.0.0 (2026-04-06)
298+
299+
- 添加 `CompileString` 方法支持编译源码字符串
300+
- 添加 `CompileFile` 方法支持编译源码文件
301+
- 添加 `ExecuteBytecode` 方法支持执行字节码
302+
- 添加 `ExecuteBytecodeWithMode` 方法支持异步执行
303+
- 修改 `require` 函数支持加载 `.gluac` 字节码文件
304+
- 添加全局编译和执行函数

0 commit comments

Comments
 (0)