LuaJIT-to-Go/stack.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
}