diff --git a/runner/embed.go b/runner/embed.go index f51909a..655d44f 100644 --- a/runner/embed.go +++ b/runner/embed.go @@ -1,11 +1,8 @@ package runner import ( - _ "embed" - "sync" - "sync/atomic" - "Moonshark/utils/logger" + _ "embed" luajit "git.sharkk.net/Sky/LuaJIT-to-Go" ) @@ -43,130 +40,67 @@ var mathLuaCode string //go:embed lua/env.lua var envLuaCode 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 - DefinesGlobal bool // Whether module defines globals directly +// Module represents a Lua module to load +type Module struct { + name string + code string + global bool // true if module defines globals, false if it returns a table } -var ( - sandbox = ModuleInfo{Name: "sandbox", Code: sandboxLuaCode, DefinesGlobal: true} - modules = []ModuleInfo{ - {Name: "json", Code: jsonLuaCode, DefinesGlobal: true}, - {Name: "sqlite", Code: sqliteLuaCode}, - {Name: "fs", Code: fsLuaCode, DefinesGlobal: true}, - {Name: "util", Code: utilLuaCode, DefinesGlobal: true}, - {Name: "string", Code: stringLuaCode}, - {Name: "table", Code: tableLuaCode}, - {Name: "crypto", Code: cryptoLuaCode, DefinesGlobal: true}, - {Name: "time", Code: timeLuaCode}, - {Name: "math", Code: mathLuaCode}, - {Name: "env", Code: envLuaCode, DefinesGlobal: true}, - } -) - -// precompileModule compiles a module's code to bytecode once -func precompileModule(m *ModuleInfo) { - m.Once.Do(func() { - tempState := luajit.New(true) - if tempState == nil { - logger.Fatalf("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.Errorf("Failed to compile %s module: %v", m.Name, err) - return - } - - bytecode := make([]byte, len(code)) - copy(bytecode, code) - m.Bytecode.Store(&bytecode) - - logger.Debugf("Successfully precompiled %s.lua to bytecode (%d bytes)", m.Name, len(code)) - }) +var modules = []Module{ + {"json", jsonLuaCode, true}, + {"sqlite", sqliteLuaCode, false}, + {"fs", fsLuaCode, true}, + {"util", utilLuaCode, true}, + {"string", stringLuaCode, false}, + {"table", tableLuaCode, false}, + {"crypto", cryptoLuaCode, true}, + {"time", timeLuaCode, false}, + {"math", mathLuaCode, false}, + {"env", envLuaCode, true}, } -// 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.Debugf("Loading %s.lua from precompiled bytecode", m.Name) - } - - if m.DefinesGlobal { - // Module defines its own globals, just run it - if err := state.LoadAndRunBytecode(*bytecode, m.Name+".lua"); err != nil { - return err - } - } else { - // Module returns a table, capture and set as global - if err := state.LoadAndRunBytecodeWithResults(*bytecode, m.Name+".lua", 1); err != nil { - return err - } - state.SetGlobal(m.Name) - } - } else { - // Fallback to interpreting the source - if verbose { - logger.Warnf("Using non-precompiled %s.lua", m.Name) - } - - if m.DefinesGlobal { - if err := state.DoString(m.Code); err != nil { - return err - } - } else { - if err := state.LoadString(m.Code); err != nil { - return err - } - if err := state.Call(0, 1); err != nil { - return err - } - state.SetGlobal(m.Name) - } +// loadModule loads a single module into the Lua state +func loadModule(state *luajit.State, m Module) error { + if m.global { + // Module defines globals directly, just execute it + return state.DoString(m.code) } + // Module returns a table, capture it and set as global + if err := state.LoadString(m.code); err != nil { + return err + } + if err := state.Call(0, 1); err != nil { + return err + } + state.SetGlobal(m.name) return nil } // loadSandboxIntoState loads all modules and sandbox into a Lua state func loadSandboxIntoState(state *luajit.State, verbose bool) error { - // Load all utility modules first - for i := range modules { - if err := loadModule(state, &modules[i], verbose); err != nil { + // Load all utility modules + for _, module := range modules { + if err := loadModule(state, module); err != nil { + if verbose { + logger.Errorf("Failed to load %s module: %v", module.name, err) + } return err } + if verbose { + logger.Debugf("Loaded %s.lua", module.name) + } } - // Initialize any module-specific globals (like SQLite tracking) + // Initialize module-specific globals if err := state.DoString(`__active_sqlite_connections = {}`); err != nil { return err } - // Load the sandbox last - it defines __execute and other core functions - precompileModule(&sandbox) - bytecode := sandbox.Bytecode.Load() - if bytecode != nil && len(*bytecode) > 0 { - if verbose { - logger.Debugf("Loading sandbox.lua from precompiled bytecode") - } - return state.LoadAndRunBytecode(*bytecode, "sandbox.lua") - } - + // Load sandbox last - defines __execute and core functions if verbose { - logger.Warnf("Using non-precompiled sandbox.lua") + logger.Debugf("Loading sandbox.lua") } return state.DoString(sandboxLuaCode) }