LuaJIT-to-Go/functions.go

108 lines
2.3 KiB
Go
Raw Normal View History

2025-01-24 19:53:09 -06:00
package luajit
/*
#include <lua.h>
#include <lauxlib.h>
#include <stdlib.h>
extern int goFunctionWrapper(lua_State* L);
2025-02-26 07:00:01 -06:00
// Helper function to access upvalues
2025-01-24 19:53:09 -06:00
static int get_upvalue_index(int i) {
2025-02-26 07:00:01 -06:00
return lua_upvalueindex(i);
2025-01-24 19:53:09 -06:00
}
*/
import "C"
import (
"fmt"
"sync"
"unsafe"
)
2025-02-26 07:00:01 -06:00
// GoFunction defines the signature for Go functions callable from Lua
2025-01-24 19:53:09 -06:00
type GoFunction func(*State) int
var (
2025-02-26 07:00:01 -06:00
// functionRegistry stores all registered Go functions
2025-01-24 19:53:09 -06:00
functionRegistry = struct {
sync.RWMutex
funcs map[unsafe.Pointer]GoFunction
}{
funcs: make(map[unsafe.Pointer]GoFunction),
}
)
//export goFunctionWrapper
func goFunctionWrapper(L *C.lua_State) C.int {
state := &State{L: L}
2025-01-24 19:53:09 -06:00
2025-02-26 07:00:01 -06:00
// Get function pointer from the first upvalue
2025-01-24 19:53:09 -06:00
ptr := C.lua_touserdata(L, C.get_upvalue_index(1))
if ptr == nil {
2025-02-26 07:00:01 -06:00
state.PushString("error: function pointer not found")
2025-01-24 19:53:09 -06:00
return -1
}
functionRegistry.RLock()
fn, ok := functionRegistry.funcs[ptr]
functionRegistry.RUnlock()
if !ok {
state.PushString("error: function not found in registry")
return -1
}
2025-02-26 07:00:01 -06:00
// Call the Go function
return C.int(fn(state))
2025-01-24 19:53:09 -06:00
}
2025-02-26 07:00:01 -06:00
// PushGoFunction wraps a Go function and pushes it onto the Lua stack
2025-01-24 19:53:09 -06:00
func (s *State) PushGoFunction(fn GoFunction) error {
2025-02-26 07:00:01 -06:00
// Allocate a pointer to use as the function key
2025-01-24 19:53:09 -06:00
ptr := C.malloc(1)
if ptr == nil {
return fmt.Errorf("failed to allocate memory for function pointer")
}
2025-02-26 07:00:01 -06:00
// Register the function
2025-01-24 19:53:09 -06:00
functionRegistry.Lock()
functionRegistry.funcs[ptr] = fn
functionRegistry.Unlock()
2025-02-26 07:00:01 -06:00
// Push the pointer as lightuserdata (first upvalue)
2025-01-24 19:53:09 -06:00
C.lua_pushlightuserdata(s.L, ptr)
2025-02-26 07:00:01 -06:00
// Create closure with the C wrapper and the upvalue
2025-01-24 19:53:09 -06:00
C.lua_pushcclosure(s.L, (*[0]byte)(C.goFunctionWrapper), 1)
2025-02-26 07:00:01 -06:00
2025-01-24 19:53:09 -06:00
return nil
}
2025-02-26 07:00:01 -06:00
// RegisterGoFunction registers a Go function as a global Lua function
2025-01-24 19:53:09 -06:00
func (s *State) RegisterGoFunction(name string, fn GoFunction) error {
if err := s.PushGoFunction(fn); err != nil {
return err
}
2025-02-26 07:00:01 -06:00
s.SetGlobal(name)
2025-01-24 19:53:09 -06:00
return nil
}
2025-02-26 07:00:01 -06:00
// UnregisterGoFunction removes a global function
2025-01-24 19:53:09 -06:00
func (s *State) UnregisterGoFunction(name string) {
s.PushNil()
2025-02-26 07:00:01 -06:00
s.SetGlobal(name)
2025-01-24 19:53:09 -06:00
}
2025-02-26 07:00:01 -06:00
// Cleanup frees all function pointers and clears the registry
2025-01-24 19:53:09 -06:00
func (s *State) Cleanup() {
functionRegistry.Lock()
defer functionRegistry.Unlock()
2025-02-26 07:00:01 -06:00
// Free all allocated pointers
2025-01-24 19:53:09 -06:00
for ptr := range functionRegistry.funcs {
C.free(ptr)
2025-02-26 07:00:01 -06:00
delete(functionRegistry.funcs, ptr)
2025-01-24 19:53:09 -06:00
}
}