147 lines
3.1 KiB
Go
147 lines
3.1 KiB
Go
package luajit
|
|
|
|
/*
|
|
#include <lua.h>
|
|
#include <lauxlib.h>
|
|
*/
|
|
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
|
|
}
|