package luajit /* #include #include */ import "C" import "fmt" // LuaError represents an error from the Lua state type LuaError struct { Code int Message string } func (e *LuaError) Error() string { return fmt.Sprintf("lua error (code=%d): %s", e.Code, e.Message) } // Stack management constants from lua.h const ( LUA_MINSTACK = 20 // Minimum Lua stack size LUA_MAXSTACK = 1000000 // Maximum Lua stack size LUA_REGISTRYINDEX = -10000 // Pseudo-index for the Lua registry LUA_GLOBALSINDEX = -10002 // Pseudo-index for globals table ) // checkStack ensures there is enough space on the Lua stack func (s *State) checkStack(n int) error { if C.lua_checkstack(s.L, C.int(n)) == 0 { return fmt.Errorf("stack overflow (cannot allocate %d slots)", n) } return nil } // safeCall wraps a potentially dangerous C call with stack checking func (s *State) safeCall(f func() C.int) error { // Save current stack size top := s.GetTop() // Ensure we have enough stack space (minimum 20 slots as per Lua standard) if err := s.checkStack(LUA_MINSTACK); err != nil { return err } // Make the call status := f() // Check for errors if status != 0 { err := &LuaError{ Code: int(status), Message: s.ToString(-1), } s.Pop(1) // Remove error message return err } // Verify stack integrity newTop := s.GetTop() if newTop < top { return fmt.Errorf("stack underflow: %d slots lost", top-newTop) } return nil } // stackGuard wraps a function with stack checking and restoration func stackGuard[T any](s *State, f func() (T, error)) (T, error) { // Save current stack size top := s.GetTop() // Run the protected function result, err := f() // Restore stack size newTop := s.GetTop() if newTop > top { s.Pop(newTop - top) } return result, err } // stackGuardValue executes a function that returns a value and error with stack protection func stackGuardValue[T any](s *State, f func() (T, error)) (T, error) { // Save current stack size top := s.GetTop() // Run the protected function result, err := f() // Restore stack size newTop := s.GetTop() if newTop > top { s.Pop(newTop - top) } return result, err } // stackGuardErr executes a function that only returns an error with stack protection func stackGuardErr(s *State, f func() error) error { // Save current stack size top := s.GetTop() // Run the protected function err := f() // Restore stack size newTop := s.GetTop() if newTop > top { s.Pop(newTop - top) } return err } // getStackTrace returns the current Lua stack trace func (s *State) getStackTrace() string { // Push debug.traceback function s.GetGlobal("debug") if !s.IsTable(-1) { s.Pop(1) return "stack trace not available (debug module not loaded)" } s.GetField(-1, "traceback") if !s.IsFunction(-1) { s.Pop(2) return "stack trace not available (debug.traceback not found)" } // Call debug.traceback if err := s.safeCall(func() C.int { return C.lua_pcall(s.L, 0, 1, 0) }); err != nil { return fmt.Sprintf("error getting stack trace: %v", err) } // Get the resulting string trace := s.ToString(-1) s.Pop(1) // Remove the trace string return trace }