147 lines
3.4 KiB
Go
147 lines
3.4 KiB
Go
package runner
|
|
|
|
import (
|
|
_ "embed"
|
|
"sync"
|
|
"sync/atomic"
|
|
|
|
"Moonshark/core/utils/logger"
|
|
|
|
luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
|
|
)
|
|
|
|
//go:embed sandbox.lua
|
|
var sandboxLuaCode string
|
|
|
|
//go:embed json.lua
|
|
var jsonLuaCode string
|
|
|
|
//go:embed sqlite.lua
|
|
var sqliteLuaCode string
|
|
|
|
//go:embed fs.lua
|
|
var fsLuaCode string
|
|
|
|
//go:embed util.lua
|
|
var utilLuaCode string
|
|
|
|
//go:embed string.lua
|
|
var stringLuaCode string
|
|
|
|
//go:embed table.lua
|
|
var tableLuaCode string
|
|
|
|
//go:embed crypto.lua
|
|
var cryptoLuaCode string
|
|
|
|
// ModuleInfo holds information about an embeddable Lua module
|
|
type ModuleInfo struct {
|
|
Name string // Module name
|
|
Code string // Module source code
|
|
Bytecode atomic.Pointer[[]byte] // Cached bytecode
|
|
Once sync.Once // For one-time compilation
|
|
}
|
|
|
|
var (
|
|
sandbox = ModuleInfo{Name: "sandbox", Code: sandboxLuaCode}
|
|
modules = []ModuleInfo{
|
|
{Name: "json", Code: jsonLuaCode},
|
|
{Name: "sqlite", Code: sqliteLuaCode},
|
|
{Name: "fs", Code: fsLuaCode},
|
|
{Name: "util", Code: utilLuaCode},
|
|
{Name: "string", Code: stringLuaCode},
|
|
{Name: "table", Code: tableLuaCode},
|
|
{Name: "crypto", Code: cryptoLuaCode},
|
|
}
|
|
)
|
|
|
|
// precompileModule compiles a module's code to bytecode once
|
|
func precompileModule(m *ModuleInfo) {
|
|
m.Once.Do(func() {
|
|
tempState := luajit.New()
|
|
if tempState == nil {
|
|
logger.Fatal("Failed to create temp Lua state for %s module compilation", m.Name)
|
|
return
|
|
}
|
|
defer tempState.Close()
|
|
defer tempState.Cleanup()
|
|
|
|
code, err := tempState.CompileBytecode(m.Code, m.Name+".lua")
|
|
if err != nil {
|
|
logger.Error("Failed to compile %s module: %v", m.Name, err)
|
|
return
|
|
}
|
|
|
|
bytecode := make([]byte, len(code))
|
|
copy(bytecode, code)
|
|
m.Bytecode.Store(&bytecode)
|
|
|
|
logger.Debug("Successfully precompiled %s.lua to bytecode (%d bytes)", m.Name, len(code))
|
|
})
|
|
}
|
|
|
|
// loadModule loads a module into a Lua state
|
|
func loadModule(state *luajit.State, m *ModuleInfo, verbose bool) error {
|
|
// Ensure bytecode is compiled
|
|
precompileModule(m)
|
|
|
|
// Attempt to load from bytecode
|
|
bytecode := m.Bytecode.Load()
|
|
if bytecode != nil && len(*bytecode) > 0 {
|
|
if verbose {
|
|
logger.Debug("Loading %s.lua from precompiled bytecode", m.Name)
|
|
}
|
|
|
|
if err := state.LoadBytecode(*bytecode, m.Name+".lua"); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := state.RunBytecodeWithResults(1); err != nil {
|
|
return err
|
|
}
|
|
|
|
state.SetGlobal(m.Name)
|
|
} else {
|
|
// Fallback to interpreting the source
|
|
if verbose {
|
|
logger.Warning("Using non-precompiled %s.lua", m.Name)
|
|
}
|
|
|
|
if err := state.DoString(m.Code); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// loadSandboxIntoState loads all modules and sandbox into a Lua state
|
|
func loadSandboxIntoState(state *luajit.State, verbose bool) error {
|
|
// Load all modules first
|
|
for i := range modules {
|
|
if err := loadModule(state, &modules[i], verbose); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Initialize active connections tracking (specific to SQLite)
|
|
if err := state.DoString(`__active_sqlite_connections = {}`); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Load the sandbox last
|
|
precompileModule(&sandbox)
|
|
bytecode := sandbox.Bytecode.Load()
|
|
if bytecode != nil && len(*bytecode) > 0 {
|
|
if verbose {
|
|
logger.Debug("Loading sandbox.lua from precompiled bytecode")
|
|
}
|
|
return state.LoadAndRunBytecode(*bytecode, "sandbox.lua")
|
|
}
|
|
|
|
if verbose {
|
|
logger.Warning("Using non-precompiled sandbox.lua")
|
|
}
|
|
return state.DoString(sandboxLuaCode)
|
|
}
|