optimize moduleLoader, re-add to runner
This commit is contained in:
parent
e3ee503c31
commit
bf8ce59b73
@ -148,7 +148,7 @@ func (s *Moonshark) initRunner(poolSize int) error {
|
|||||||
sessions.GlobalSessionManager.SetCookieOptions("MoonsharkSID", "/", "", false, true, 86400)
|
sessions.GlobalSessionManager.SetCookieOptions("MoonsharkSID", "/", "", false, true, 86400)
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
s.LuaRunner, err = runner.NewRunner(poolSize, s.Config.Dirs.Data, s.Config.Dirs.FS)
|
s.LuaRunner, err = runner.NewRunner(poolSize, s.Config.Dirs.Data, s.Config.Dirs.FS, s.Config.Dirs.Libs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("lua runner init failed: %v", err)
|
return fmt.Errorf("lua runner init failed: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -12,403 +12,267 @@ import (
|
|||||||
luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
|
luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ModuleConfig holds configuration for Lua's module loading system
|
|
||||||
type ModuleConfig struct {
|
type ModuleConfig struct {
|
||||||
ScriptDir string // Base directory for script being executed
|
ScriptDir string
|
||||||
LibDirs []string // Additional library directories
|
LibDirs []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ModuleLoader manages module loading and caching
|
|
||||||
type ModuleLoader struct {
|
type ModuleLoader struct {
|
||||||
config *ModuleConfig
|
config *ModuleConfig
|
||||||
pathCache map[string]string // Cache module paths for fast lookups
|
pathCache map[string]string // For reverse lookups (path -> module name)
|
||||||
bytecodeCache map[string][]byte // Cache of compiled bytecode
|
|
||||||
debug bool
|
debug bool
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewModuleLoader creates a new module loader
|
|
||||||
func NewModuleLoader(config *ModuleConfig) *ModuleLoader {
|
func NewModuleLoader(config *ModuleConfig) *ModuleLoader {
|
||||||
if config == nil {
|
if config == nil {
|
||||||
config = &ModuleConfig{
|
config = &ModuleConfig{}
|
||||||
ScriptDir: "",
|
|
||||||
LibDirs: []string{},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ModuleLoader{
|
return &ModuleLoader{
|
||||||
config: config,
|
config: config,
|
||||||
pathCache: make(map[string]string),
|
pathCache: make(map[string]string),
|
||||||
bytecodeCache: make(map[string][]byte),
|
|
||||||
debug: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnableDebug turns on debug logging
|
|
||||||
func (l *ModuleLoader) EnableDebug() {
|
func (l *ModuleLoader) EnableDebug() {
|
||||||
l.debug = true
|
l.debug = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetScriptDir sets the script directory
|
|
||||||
func (l *ModuleLoader) SetScriptDir(dir string) {
|
func (l *ModuleLoader) SetScriptDir(dir string) {
|
||||||
l.mu.Lock()
|
l.mu.Lock()
|
||||||
defer l.mu.Unlock()
|
defer l.mu.Unlock()
|
||||||
l.config.ScriptDir = dir
|
l.config.ScriptDir = dir
|
||||||
}
|
}
|
||||||
|
|
||||||
// debugLog logs a message if debug mode is enabled
|
func (l *ModuleLoader) debugLog(format string, args ...any) {
|
||||||
func (l *ModuleLoader) debugLog(format string, args ...interface{}) {
|
|
||||||
if l.debug {
|
if l.debug {
|
||||||
logger.Debugf("ModuleLoader "+format, args...)
|
logger.Debugf("ModuleLoader "+format, args...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetupRequire configures the require system in a Lua state
|
|
||||||
func (l *ModuleLoader) SetupRequire(state *luajit.State) error {
|
func (l *ModuleLoader) SetupRequire(state *luajit.State) error {
|
||||||
l.mu.RLock()
|
// Set package.path
|
||||||
defer l.mu.RUnlock()
|
|
||||||
|
|
||||||
// Initialize our module registry in Lua
|
|
||||||
err := state.DoString(`
|
|
||||||
-- Initialize global module registry
|
|
||||||
__module_paths = {}
|
|
||||||
__module_bytecode = {}
|
|
||||||
__ready_modules = {}
|
|
||||||
|
|
||||||
-- Create module preload table
|
|
||||||
package.preload = package.preload or {}
|
|
||||||
`)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up package.path based on search paths
|
|
||||||
paths := l.getSearchPaths()
|
paths := l.getSearchPaths()
|
||||||
pathStr := strings.Join(paths, ";")
|
pathStr := strings.Join(paths, ";")
|
||||||
escapedPathStr := escapeLuaString(pathStr)
|
|
||||||
|
|
||||||
return state.DoString(`package.path = "` + escapedPathStr + `"`)
|
return state.DoString(`package.path = "` + escapeLuaString(pathStr) + `"`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getSearchPaths returns a list of Lua search paths
|
|
||||||
func (l *ModuleLoader) getSearchPaths() []string {
|
func (l *ModuleLoader) getSearchPaths() []string {
|
||||||
absPaths := []string{}
|
var paths []string
|
||||||
seen := map[string]bool{}
|
seen := make(map[string]bool)
|
||||||
|
|
||||||
// Add script directory (highest priority)
|
// Script directory first
|
||||||
if l.config.ScriptDir != "" {
|
if l.config.ScriptDir != "" {
|
||||||
absPath, err := filepath.Abs(l.config.ScriptDir)
|
if absPath, err := filepath.Abs(l.config.ScriptDir); err == nil && !seen[absPath] {
|
||||||
if err == nil && !seen[absPath] {
|
paths = append(paths, filepath.Join(absPath, "?.lua"))
|
||||||
absPaths = append(absPaths, filepath.Join(absPath, "?.lua"))
|
|
||||||
seen[absPath] = true
|
seen[absPath] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add lib directories
|
// Library directories
|
||||||
for _, dir := range l.config.LibDirs {
|
for _, dir := range l.config.LibDirs {
|
||||||
if dir == "" {
|
if dir == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if absPath, err := filepath.Abs(dir); err == nil && !seen[absPath] {
|
||||||
absPath, err := filepath.Abs(dir)
|
paths = append(paths, filepath.Join(absPath, "?.lua"))
|
||||||
if err == nil && !seen[absPath] {
|
|
||||||
absPaths = append(absPaths, filepath.Join(absPath, "?.lua"))
|
|
||||||
seen[absPath] = true
|
seen[absPath] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return absPaths
|
return paths
|
||||||
}
|
}
|
||||||
|
|
||||||
// PreloadModules preloads modules from library directories
|
|
||||||
func (l *ModuleLoader) PreloadModules(state *luajit.State) error {
|
func (l *ModuleLoader) PreloadModules(state *luajit.State) error {
|
||||||
l.mu.Lock()
|
l.mu.Lock()
|
||||||
defer l.mu.Unlock()
|
defer l.mu.Unlock()
|
||||||
|
|
||||||
// Reset caches
|
// Reset caches
|
||||||
l.pathCache = make(map[string]string)
|
l.pathCache = make(map[string]string)
|
||||||
l.bytecodeCache = make(map[string][]byte)
|
|
||||||
|
|
||||||
// Reset module registry in Lua
|
|
||||||
if err := state.DoString(`
|
|
||||||
-- Reset module registry
|
|
||||||
__module_paths = {}
|
|
||||||
__module_bytecode = {}
|
|
||||||
__ready_modules = {}
|
|
||||||
|
|
||||||
-- Clear non-core modules from package.loaded
|
|
||||||
local core_modules = {
|
|
||||||
string = true, table = true, math = true, os = true,
|
|
||||||
package = true, io = true, coroutine = true, debug = true, _G = true
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Clear non-core modules
|
||||||
|
err := state.DoString(`
|
||||||
|
local core = {string=1, table=1, math=1, os=1, package=1, io=1, coroutine=1, debug=1, _G=1}
|
||||||
for name in pairs(package.loaded) do
|
for name in pairs(package.loaded) do
|
||||||
if not core_modules[name] then
|
if not core[name] then package.loaded[name] = nil end
|
||||||
package.loaded[name] = nil
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
-- Reset preload table
|
|
||||||
package.preload = {}
|
package.preload = {}
|
||||||
`); err != nil {
|
`)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scan and preload modules from all library directories
|
// Scan and preload modules
|
||||||
for _, dir := range l.config.LibDirs {
|
for _, dir := range l.config.LibDirs {
|
||||||
if dir == "" {
|
if err := l.scanDirectory(state, dir); err != nil {
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
absDir, err := filepath.Abs(dir)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
l.debugLog("Scanning directory: %s", absDir)
|
|
||||||
|
|
||||||
// Find all Lua files
|
|
||||||
err = filepath.Walk(absDir, func(path string, info os.FileInfo, err error) error {
|
|
||||||
if err != nil || info.IsDir() || !strings.HasSuffix(path, ".lua") {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get module name from path
|
|
||||||
relPath, err := filepath.Rel(absDir, path)
|
|
||||||
if err != nil || strings.HasPrefix(relPath, "..") {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert path to module name
|
|
||||||
modName := strings.TrimSuffix(relPath, ".lua")
|
|
||||||
modName = strings.ReplaceAll(modName, string(filepath.Separator), ".")
|
|
||||||
|
|
||||||
l.debugLog("Found module: %s at %s", modName, path)
|
|
||||||
|
|
||||||
// Register in our caches
|
|
||||||
l.pathCache[modName] = path
|
|
||||||
|
|
||||||
// Load file content
|
|
||||||
content, err := os.ReadFile(path)
|
|
||||||
if err != nil {
|
|
||||||
l.debugLog("Failed to read module file: %v", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compile to bytecode
|
|
||||||
bytecode, err := state.CompileBytecode(string(content), path)
|
|
||||||
if err != nil {
|
|
||||||
l.debugLog("Failed to compile module: %v", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cache bytecode
|
|
||||||
l.bytecodeCache[modName] = bytecode
|
|
||||||
|
|
||||||
// Register in Lua - store path info
|
|
||||||
escapedPath := escapeLuaString(path)
|
|
||||||
escapedName := escapeLuaString(modName)
|
|
||||||
|
|
||||||
if err := state.DoString(`__module_paths["` + escapedName + `"] = "` + escapedPath + `"`); err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load bytecode and register in package.preload properly
|
|
||||||
if err := state.LoadBytecode(bytecode, path); err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store the function in package.preload - the function is on the stack
|
|
||||||
state.GetGlobal("package")
|
|
||||||
state.GetField(-1, "preload")
|
|
||||||
state.PushString(modName)
|
|
||||||
state.PushCopy(-4) // Copy the compiled function
|
|
||||||
state.SetTable(-3) // preload[modName] = function
|
|
||||||
state.Pop(2) // Pop package and preload tables
|
|
||||||
|
|
||||||
// Mark as ready
|
|
||||||
if err := state.DoString(`__ready_modules["` + escapedName + `"] = true`); err != nil {
|
|
||||||
state.Pop(1) // Remove the function from stack
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
state.Pop(1) // Remove the function from stack
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Install optimized require implementation
|
// Install simplified require
|
||||||
return state.DoString(`
|
return state.DoString(`
|
||||||
-- Setup environment-aware require function
|
|
||||||
function __setup_require(env)
|
function __setup_require(env)
|
||||||
-- Create require function specific to this environment
|
|
||||||
env.require = function(modname)
|
env.require = function(modname)
|
||||||
-- Check if already loaded
|
|
||||||
if package.loaded[modname] then
|
if package.loaded[modname] then
|
||||||
return package.loaded[modname]
|
return package.loaded[modname]
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check preloaded modules
|
|
||||||
if __ready_modules[modname] then
|
|
||||||
local loader = package.preload[modname]
|
local loader = package.preload[modname]
|
||||||
if loader then
|
if loader then
|
||||||
-- Set environment for loader
|
|
||||||
setfenv(loader, env)
|
setfenv(loader, env)
|
||||||
|
local result = loader() or true
|
||||||
-- Execute and store result
|
|
||||||
local result = loader()
|
|
||||||
if result == nil then
|
|
||||||
result = true
|
|
||||||
end
|
|
||||||
|
|
||||||
package.loaded[modname] = result
|
package.loaded[modname] = result
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
-- Direct file load as fallback
|
-- Standard path search
|
||||||
if __module_paths[modname] then
|
|
||||||
local path = __module_paths[modname]
|
|
||||||
local chunk, err = loadfile(path)
|
|
||||||
if chunk then
|
|
||||||
setfenv(chunk, env)
|
|
||||||
local result = chunk()
|
|
||||||
if result == nil then
|
|
||||||
result = true
|
|
||||||
end
|
|
||||||
package.loaded[modname] = result
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Full path search as last resort
|
|
||||||
local errors = {}
|
|
||||||
for path in package.path:gmatch("[^;]+") do
|
for path in package.path:gmatch("[^;]+") do
|
||||||
local file_path = path:gsub("?", modname:gsub("%.", "/"))
|
local file = path:gsub("?", modname:gsub("%.", "/"))
|
||||||
local chunk, err = loadfile(file_path)
|
local chunk = loadfile(file)
|
||||||
if chunk then
|
if chunk then
|
||||||
setfenv(chunk, env)
|
setfenv(chunk, env)
|
||||||
local result = chunk()
|
local result = chunk() or true
|
||||||
if result == nil then
|
|
||||||
result = true
|
|
||||||
end
|
|
||||||
package.loaded[modname] = result
|
package.loaded[modname] = result
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
table.insert(errors, "\tno file '" .. file_path .. "'")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
error("module '" .. modname .. "' not found:\n" .. table.concat(errors, "\n"), 2)
|
error("module '" .. modname .. "' not found", 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
return env
|
return env
|
||||||
end
|
end
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetModuleByPath finds the module name for a file path
|
func (l *ModuleLoader) scanDirectory(state *luajit.State, dir string) error {
|
||||||
|
if dir == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
absDir, err := filepath.Abs(dir)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
l.debugLog("Scanning directory: %s", absDir)
|
||||||
|
|
||||||
|
return filepath.Walk(absDir, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil || info.IsDir() || !strings.HasSuffix(path, ".lua") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
relPath, err := filepath.Rel(absDir, path)
|
||||||
|
if err != nil || strings.HasPrefix(relPath, "..") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to module name
|
||||||
|
modName := strings.TrimSuffix(relPath, ".lua")
|
||||||
|
modName = strings.ReplaceAll(modName, string(filepath.Separator), ".")
|
||||||
|
|
||||||
|
l.debugLog("Found module: %s at %s", modName, path)
|
||||||
|
l.pathCache[modName] = path
|
||||||
|
|
||||||
|
// Load and compile module
|
||||||
|
content, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
l.debugLog("Failed to read %s: %v", path, err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := state.LoadString(string(content)); err != nil {
|
||||||
|
l.debugLog("Failed to compile %s: %v", path, err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store in package.preload
|
||||||
|
state.GetGlobal("package")
|
||||||
|
state.GetField(-1, "preload")
|
||||||
|
state.PushString(modName)
|
||||||
|
state.PushCopy(-4) // Copy compiled function
|
||||||
|
state.SetTable(-3)
|
||||||
|
state.Pop(2) // Pop package and preload
|
||||||
|
state.Pop(1) // Pop function
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (l *ModuleLoader) GetModuleByPath(path string) (string, bool) {
|
func (l *ModuleLoader) GetModuleByPath(path string) (string, bool) {
|
||||||
l.mu.RLock()
|
l.mu.RLock()
|
||||||
defer l.mu.RUnlock()
|
defer l.mu.RUnlock()
|
||||||
|
|
||||||
// Convert to absolute path for consistent comparison
|
|
||||||
absPath, err := filepath.Abs(path)
|
absPath, err := filepath.Abs(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
absPath = filepath.Clean(path)
|
absPath = filepath.Clean(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try direct lookup from cache with absolute path
|
// Direct lookup
|
||||||
for modName, modPath := range l.pathCache {
|
for modName, modPath := range l.pathCache {
|
||||||
if modPath == absPath {
|
if modPath == absPath {
|
||||||
return modName, true
|
return modName, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to construct module name from lib dirs
|
// Construct from lib dirs
|
||||||
for _, dir := range l.config.LibDirs {
|
for _, dir := range l.config.LibDirs {
|
||||||
absDir, err := filepath.Abs(dir)
|
absDir, err := filepath.Abs(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the file is under this lib directory
|
|
||||||
relPath, err := filepath.Rel(absDir, absPath)
|
relPath, err := filepath.Rel(absDir, absPath)
|
||||||
if err != nil || strings.HasPrefix(relPath, "..") {
|
if err != nil || strings.HasPrefix(relPath, "..") || !strings.HasSuffix(relPath, ".lua") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasSuffix(relPath, ".lua") {
|
|
||||||
modName := strings.TrimSuffix(relPath, ".lua")
|
modName := strings.TrimSuffix(relPath, ".lua")
|
||||||
modName = strings.ReplaceAll(modName, string(filepath.Separator), ".")
|
modName = strings.ReplaceAll(modName, string(filepath.Separator), ".")
|
||||||
|
|
||||||
l.debugLog("Found module %s for path %s", modName, path)
|
|
||||||
return modName, true
|
return modName, true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
l.debugLog("No module found for path %s", path)
|
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
// RefreshModule recompiles and updates a specific module
|
|
||||||
func (l *ModuleLoader) RefreshModule(state *luajit.State, moduleName string) error {
|
func (l *ModuleLoader) RefreshModule(state *luajit.State, moduleName string) error {
|
||||||
l.mu.Lock()
|
l.mu.Lock()
|
||||||
defer l.mu.Unlock()
|
defer l.mu.Unlock()
|
||||||
|
|
||||||
// Get module path
|
|
||||||
path, exists := l.pathCache[moduleName]
|
path, exists := l.pathCache[moduleName]
|
||||||
if !exists {
|
if !exists {
|
||||||
l.debugLog("Module not found in cache: %s", moduleName)
|
|
||||||
return fmt.Errorf("module %s not found", moduleName)
|
return fmt.Errorf("module %s not found", moduleName)
|
||||||
}
|
}
|
||||||
|
|
||||||
l.debugLog("Refreshing module: %s at %s", moduleName, path)
|
l.debugLog("Refreshing module: %s", moduleName)
|
||||||
|
|
||||||
// Read updated file content
|
|
||||||
content, err := os.ReadFile(path)
|
content, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to read module file: %w", err)
|
return fmt.Errorf("failed to read module: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recompile to bytecode
|
// Compile new version
|
||||||
bytecode, err := state.CompileBytecode(string(content), path)
|
if err := state.LoadString(string(content)); err != nil {
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to compile module: %w", err)
|
return fmt.Errorf("failed to compile module: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update bytecode cache
|
// Update package.preload
|
||||||
l.bytecodeCache[moduleName] = bytecode
|
|
||||||
|
|
||||||
// Load new bytecode
|
|
||||||
if err := state.LoadBytecode(bytecode, path); err != nil {
|
|
||||||
return fmt.Errorf("failed to load bytecode: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update package.preload with new function (function is on stack)
|
|
||||||
state.GetGlobal("package")
|
state.GetGlobal("package")
|
||||||
state.GetField(-1, "preload")
|
state.GetField(-1, "preload")
|
||||||
state.PushString(moduleName)
|
state.PushString(moduleName)
|
||||||
state.PushCopy(-4) // Copy the new compiled function
|
state.PushCopy(-4) // Copy function
|
||||||
state.SetTable(-3) // preload[moduleName] = new_function
|
state.SetTable(-3)
|
||||||
state.Pop(2) // Pop package and preload tables
|
state.Pop(2) // Pop package and preload
|
||||||
state.Pop(1) // Pop the function
|
state.Pop(1) // Pop function
|
||||||
|
|
||||||
// Clear from package.loaded so it gets reloaded
|
// Clear from loaded
|
||||||
escapedName := escapeLuaString(moduleName)
|
state.DoString(`package.loaded["` + escapeLuaString(moduleName) + `"] = nil`)
|
||||||
if err := state.DoString(`package.loaded["` + escapedName + `"] = nil`); err != nil {
|
|
||||||
return fmt.Errorf("failed to clear loaded module: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.debugLog("Successfully refreshed module: %s", moduleName)
|
l.debugLog("Successfully refreshed: %s", moduleName)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RefreshModuleByPath refreshes a module by its file path
|
|
||||||
func (l *ModuleLoader) RefreshModuleByPath(state *luajit.State, filePath string) error {
|
func (l *ModuleLoader) RefreshModuleByPath(state *luajit.State, filePath string) error {
|
||||||
moduleName, exists := l.GetModuleByPath(filePath)
|
moduleName, exists := l.GetModuleByPath(filePath)
|
||||||
if !exists {
|
if !exists {
|
||||||
@ -417,14 +281,12 @@ func (l *ModuleLoader) RefreshModuleByPath(state *luajit.State, filePath string)
|
|||||||
return l.RefreshModule(state, moduleName)
|
return l.RefreshModule(state, moduleName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// escapeLuaString escapes special characters in a string for Lua
|
|
||||||
func escapeLuaString(s string) string {
|
func escapeLuaString(s string) string {
|
||||||
replacer := strings.NewReplacer(
|
return strings.NewReplacer(
|
||||||
"\\", "\\\\",
|
`\`, `\\`,
|
||||||
"\"", "\\\"",
|
`"`, `\"`,
|
||||||
"\n", "\\n",
|
"\n", `\n`,
|
||||||
"\r", "\\r",
|
"\r", `\r`,
|
||||||
"\t", "\\t",
|
"\t", `\t`,
|
||||||
)
|
).Replace(s)
|
||||||
return replacer.Replace(s)
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
// runner.go - Simplified interface
|
|
||||||
package runner
|
package runner
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -52,14 +51,19 @@ type Runner struct {
|
|||||||
paramsPool sync.Pool
|
paramsPool sync.Pool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRunner(poolSize int, dataDir, fsDir string) (*Runner, error) {
|
func NewRunner(poolSize int, dataDir, fsDir string, libDirs []string) (*Runner, error) {
|
||||||
if poolSize <= 0 {
|
if poolSize <= 0 {
|
||||||
poolSize = runtime.GOMAXPROCS(0)
|
poolSize = runtime.GOMAXPROCS(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Configure module loader with lib directories
|
||||||
|
moduleConfig := &ModuleConfig{
|
||||||
|
LibDirs: libDirs,
|
||||||
|
}
|
||||||
|
|
||||||
r := &Runner{
|
r := &Runner{
|
||||||
poolSize: poolSize,
|
poolSize: poolSize,
|
||||||
moduleLoader: NewModuleLoader(&ModuleConfig{}),
|
moduleLoader: NewModuleLoader(moduleConfig),
|
||||||
ctxPool: sync.Pool{
|
ctxPool: sync.Pool{
|
||||||
New: func() any { return make(map[string]any, 8) },
|
New: func() any { return make(map[string]any, 8) },
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user