lua/vm.go

206 lines
5.2 KiB
Go

// Package lua ...
//
// Description : lua ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2024-11-14 17:46
package lua
import (
"git.zhangdeman.cn/zhangdeman/exception"
libs "github.com/vadv/gopher-lua-libs"
luaCompile "github.com/yuin/gopher-lua"
"github.com/yuin/gopher-lua/ast"
"github.com/yuin/gopher-lua/parse"
luar "layeh.com/gopher-luar"
"os"
"path/filepath"
"strings"
"sync"
)
var (
VMInstance *VM
)
// InitVM 初始化lua虚拟机
func InitVM(poolSize int, libPath []string, preComplierScript map[string]string) error {
if len(libPath) == 0 {
libPath = []string{"./lib"} // 默认使用内置的lib
}
pathStr := ""
for _, path := range libPath {
// 绝对路径
libDir, _ := filepath.Abs(path)
libDir = filepath.Join(libDir, "?.lua")
pathStr += ";" + libDir
}
pathStr += ";;"
_ = os.Setenv("LUA_PATH", pathStr)
if poolSize <= 0 {
poolSize = 32
}
VMInstance = &VM{
l: &sync.RWMutex{},
scriptLock: &sync.RWMutex{},
pool: make([]*luaCompile.LState, 0),
preComplierScript: make(map[string]*luaCompile.FunctionProto),
}
for i := 0; i < poolSize; i++ {
VMInstance.pool = append(VMInstance.pool, luaCompile.NewState())
}
for scriptID, scriptContent := range preComplierScript {
if err := VMInstance.AddScript(scriptID, scriptContent); nil != err {
return err
}
}
return nil
}
type VM struct {
l *sync.RWMutex
scriptLock *sync.RWMutex
pool []*luaCompile.LState // lua虚拟机实例池
preComplierScript map[string]*luaCompile.FunctionProto // 预编译的脚本内容
}
// AddScript 脚本编译
func (v *VM) AddScript(scriptID string, script string) error {
v.scriptLock.Lock()
defer v.scriptLock.Unlock()
var (
err error
chunk []ast.Stmt
proto *luaCompile.FunctionProto
)
if chunk, err = parse.Parse(strings.NewReader(script), scriptID); nil != err {
return err
}
if proto, err = luaCompile.Compile(chunk, scriptID); nil != err {
return err
}
v.preComplierScript[scriptID] = proto
return nil
}
// RemoveScript 删除一个已编译的脚本
func (v *VM) RemoveScript(scriptID string) {
v.scriptLock.Lock()
defer v.scriptLock.Unlock()
delete(v.preComplierScript, scriptID)
}
// GetVm 获取一个VM实例
func (v *VM) GetVm(scriptParamList ...ScriptParam) (*luaCompile.LState, error) {
v.l.Lock()
defer v.l.Unlock()
if len(v.pool) == 0 {
return nil, exception.New(ErrLuaVmPoolIsEmpty, nil, "lua虚拟机实例池为空,请检查是否初始化了虚拟机以及虚拟机实例使用之后是否及时关闭")
/* // 基于libs预导入lua的常用模块
vm := luaCompile.NewState()
libs.Preload(vm)
// 设置脚本参数
for _, itemParam := range scriptParamList {
vm.SetGlobal(itemParam.Name, luar.New(vm, itemParam.Value))
}
return vm*/
}
l := v.pool[len(v.pool)-1]
// 基于libs预导入lua的常用模块
libs.Preload(l)
// 设置脚本参数
for _, itemParam := range scriptParamList {
l.SetGlobal(itemParam.Name, luar.New(l, itemParam.Value))
}
v.pool = v.pool[:len(v.pool)-1]
return l, nil
}
// Close 关闭指定虚拟机
func (v *VM) Close(l *luaCompile.LState) {
if nil == l {
return
}
l.Close()
v.l.Lock()
defer v.l.Unlock()
// 实例放回实例实例池
v.pool = append(v.pool, l)
}
// Shutdown 关闭全部虚拟机
func (v *VM) Shutdown() {
for _, item := range v.pool {
item.Close()
}
}
// RunPreCompileScript 执行预编译过的脚本
func (v *VM) RunPreCompileScript(scriptID string, scriptParamList ...ScriptParam) (*luaCompile.LState, error) {
var (
err error
l *luaCompile.LState
proto *luaCompile.FunctionProto
exists bool
)
v.scriptLock.Lock()
if proto, exists = v.preComplierScript[scriptID]; !exists {
v.scriptLock.Unlock()
return nil, exception.New(ErrLuaVmPreCompileScriptNotFound, map[string]any{
"script_id": scriptID,
}, "预编译脚本不存在")
}
v.scriptLock.Unlock()
if l, err = v.GetVm(scriptParamList...); nil != err {
return nil, exception.New(ErrLuaVmPreCompileScriptRunFail, map[string]any{
"script_id": scriptID,
"err_msg": err.Error(),
}, "预编译脚本执行失败")
}
lFunc := l.NewFunctionFromProto(proto)
l.Push(lFunc)
if err = l.PCall(0, luaCompile.MultRet, nil); nil != err {
v.Close(l)
return nil, err
}
return l, nil
}
// Run 运行脚本
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 18:08 2024/11/14
func (v *VM) Run(script string, scriptParamList ...ScriptParam) (*luaCompile.LState, error) {
var (
err error
l *luaCompile.LState
)
if l, err = v.GetVm(scriptParamList...); nil != err {
return nil, err
}
if err = l.DoString(script); err != nil {
// 直接归还实例
l.Close()
return nil, exception.New(ErrLuaVmScriptRunFail, map[string]any{
"err_msg": err.Error(),
}, "脚本执行出现异常")
}
return l, nil
}
// GetResultAndRemove 获取最后一个返回值, 并从结果中移除
func (v *VM) GetResultAndRemove(l *luaCompile.LState) luaCompile.LValue {
val := l.Get(-1)
l.Pop(1)
return val
}
// GetResultByIndex 根据索引获取返回值
func (v *VM) GetResultByIndex(l *luaCompile.LState, index int) luaCompile.LValue {
val := l.Get(index)
return val
}