2025-01-24 19:53:09 -06:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2025-02-03 10:41:05 -06:00
|
|
|
// For lua_pcall, the function and arguments are popped before results are pushed
|
|
|
|
// So we don't consider it an underflow if the new top is less than the original
|
|
|
|
if status == 0 && s.GetType(-1) == TypeFunction {
|
|
|
|
// If we still have a function on the stack, restore original size
|
|
|
|
s.SetTop(top)
|
2025-01-24 19:53:09 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2025-02-03 10:41:05 -06:00
|
|
|
// stackGuard wraps a function with stack checking
|
2025-01-24 19:53:09 -06:00
|
|
|
func stackGuard[T any](s *State, f func() (T, error)) (T, error) {
|
|
|
|
// Save current stack size
|
|
|
|
top := s.GetTop()
|
2025-02-03 10:41:05 -06:00
|
|
|
defer func() {
|
|
|
|
// Only restore if stack is larger than original
|
|
|
|
if s.GetTop() > top {
|
|
|
|
s.SetTop(top)
|
|
|
|
}
|
|
|
|
}()
|
2025-01-24 19:53:09 -06:00
|
|
|
|
|
|
|
// Run the protected function
|
2025-02-03 10:41:05 -06:00
|
|
|
return f()
|
2025-01-24 19:53:09 -06:00
|
|
|
}
|
|
|
|
|
2025-02-03 10:41:05 -06:00
|
|
|
// stackGuardValue executes a function with stack protection
|
2025-01-24 19:53:09 -06:00
|
|
|
func stackGuardValue[T any](s *State, f func() (T, error)) (T, error) {
|
2025-02-03 10:41:05 -06:00
|
|
|
return stackGuard(s, f)
|
2025-01-24 19:53:09 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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()
|
2025-02-03 10:41:05 -06:00
|
|
|
defer func() {
|
|
|
|
// Only restore if stack is larger than original
|
|
|
|
if s.GetTop() > top {
|
|
|
|
s.SetTop(top)
|
|
|
|
}
|
|
|
|
}()
|
2025-01-24 19:53:09 -06:00
|
|
|
|
|
|
|
// Run the protected function
|
2025-02-03 10:41:05 -06:00
|
|
|
return f()
|
2025-01-24 19:53:09 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// getStackTrace returns the current Lua stack trace
|
|
|
|
func (s *State) getStackTrace() string {
|
2025-02-03 10:41:05 -06:00
|
|
|
// Same implementation...
|
|
|
|
return ""
|
2025-01-24 19:53:09 -06:00
|
|
|
}
|