2014-09-13 10:11:05 +04:00
|
|
|
package lua
|
|
|
|
|
|
|
|
/*
|
2016-05-01 18:33:02 +03:00
|
|
|
#cgo CFLAGS: -Ilua
|
|
|
|
|
2014-09-13 10:11:05 +04:00
|
|
|
#include <lua.h>
|
|
|
|
#include <lualib.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
*/
|
|
|
|
import "C"
|
|
|
|
|
|
|
|
import (
|
|
|
|
"reflect"
|
2016-05-01 18:33:02 +03:00
|
|
|
"sync"
|
2014-09-13 10:11:05 +04:00
|
|
|
"unsafe"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Type of allocation functions to use with NewStateAlloc
|
|
|
|
type Alloc func(ptr unsafe.Pointer, osize uint, nsize uint) unsafe.Pointer
|
|
|
|
|
|
|
|
// This is the type of go function that can be registered as lua functions
|
|
|
|
type LuaGoFunction func(L *State) int
|
|
|
|
|
|
|
|
// Wrapper to keep cgo from complaining about incomplete ptr type
|
|
|
|
//export State
|
|
|
|
type State struct {
|
|
|
|
// Wrapped lua_State object
|
|
|
|
s *C.lua_State
|
|
|
|
|
2016-05-01 18:33:02 +03:00
|
|
|
// index of this object inside the goStates array
|
|
|
|
Index uintptr
|
|
|
|
|
2014-09-13 10:11:05 +04:00
|
|
|
// Registry of go object that have been pushed to Lua VM
|
|
|
|
registry []interface{}
|
|
|
|
|
|
|
|
// Freelist for funcs indices, to allow for freeing
|
|
|
|
freeIndices []uint
|
|
|
|
}
|
|
|
|
|
2016-05-01 18:33:02 +03:00
|
|
|
var goStates map[uintptr]*State
|
|
|
|
var goStatesMutex sync.Mutex
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
goStates = make(map[uintptr]*State, 16)
|
|
|
|
}
|
|
|
|
|
|
|
|
func registerGoState(L *State) {
|
|
|
|
goStatesMutex.Lock()
|
|
|
|
defer goStatesMutex.Unlock()
|
|
|
|
L.Index = uintptr(unsafe.Pointer(L))
|
|
|
|
goStates[L.Index] = L
|
|
|
|
}
|
|
|
|
|
|
|
|
func unregisterGoState(L *State) {
|
|
|
|
goStatesMutex.Lock()
|
|
|
|
defer goStatesMutex.Unlock()
|
|
|
|
delete(goStates, L.Index)
|
|
|
|
}
|
|
|
|
|
|
|
|
func getGoState(gostateindex uintptr) *State {
|
|
|
|
goStatesMutex.Lock()
|
|
|
|
defer goStatesMutex.Unlock()
|
|
|
|
return goStates[gostateindex]
|
|
|
|
}
|
|
|
|
|
2014-09-13 10:11:05 +04:00
|
|
|
//export golua_callgofunction
|
2016-05-01 18:33:02 +03:00
|
|
|
func golua_callgofunction(gostateindex uintptr, fid uint) int {
|
|
|
|
L1 := getGoState(gostateindex)
|
2014-09-13 10:11:05 +04:00
|
|
|
if fid < 0 {
|
|
|
|
panic(&LuaError{0, "Requested execution of an unknown function", L1.StackTrace()})
|
|
|
|
}
|
|
|
|
f := L1.registry[fid].(LuaGoFunction)
|
|
|
|
return f(L1)
|
|
|
|
}
|
|
|
|
|
|
|
|
//export golua_interface_newindex_callback
|
2016-05-01 18:33:02 +03:00
|
|
|
func golua_interface_newindex_callback(gostateindex uintptr, iid uint, field_name_cstr *C.char) int {
|
|
|
|
L := getGoState(gostateindex)
|
2014-09-13 10:11:05 +04:00
|
|
|
iface := L.registry[iid]
|
|
|
|
ifacevalue := reflect.ValueOf(iface).Elem()
|
|
|
|
|
|
|
|
field_name := C.GoString(field_name_cstr)
|
|
|
|
|
|
|
|
fval := ifacevalue.FieldByName(field_name)
|
|
|
|
|
|
|
|
if fval.Kind() == reflect.Ptr {
|
|
|
|
fval = fval.Elem()
|
|
|
|
}
|
|
|
|
|
|
|
|
luatype := LuaValType(C.lua_type(L.s, 3))
|
|
|
|
|
|
|
|
switch fval.Kind() {
|
|
|
|
case reflect.Bool:
|
|
|
|
if luatype == LUA_TBOOLEAN {
|
|
|
|
fval.SetBool(int(C.lua_toboolean(L.s, 3)) != 0)
|
|
|
|
return 1
|
|
|
|
} else {
|
|
|
|
L.PushString("Wrong assignment to field " + field_name)
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
|
|
|
case reflect.Int:
|
|
|
|
fallthrough
|
|
|
|
case reflect.Int8:
|
|
|
|
fallthrough
|
|
|
|
case reflect.Int16:
|
|
|
|
fallthrough
|
|
|
|
case reflect.Int32:
|
|
|
|
fallthrough
|
|
|
|
case reflect.Int64:
|
|
|
|
if luatype == LUA_TNUMBER {
|
|
|
|
fval.SetInt(int64(C.lua_tointeger(L.s, 3)))
|
|
|
|
return 1
|
|
|
|
} else {
|
|
|
|
L.PushString("Wrong assignment to field " + field_name)
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
|
|
|
case reflect.Uint:
|
|
|
|
fallthrough
|
|
|
|
case reflect.Uint8:
|
|
|
|
fallthrough
|
|
|
|
case reflect.Uint16:
|
|
|
|
fallthrough
|
|
|
|
case reflect.Uint32:
|
|
|
|
fallthrough
|
|
|
|
case reflect.Uint64:
|
|
|
|
if luatype == LUA_TNUMBER {
|
|
|
|
fval.SetUint(uint64(C.lua_tointeger(L.s, 3)))
|
|
|
|
return 1
|
|
|
|
} else {
|
|
|
|
L.PushString("Wrong assignment to field " + field_name)
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
|
|
|
case reflect.String:
|
|
|
|
if luatype == LUA_TSTRING {
|
|
|
|
fval.SetString(C.GoString(C.lua_tolstring(L.s, 3, nil)))
|
|
|
|
return 1
|
|
|
|
} else {
|
|
|
|
L.PushString("Wrong assignment to field " + field_name)
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
|
|
|
case reflect.Float32:
|
|
|
|
fallthrough
|
|
|
|
case reflect.Float64:
|
|
|
|
if luatype == LUA_TNUMBER {
|
|
|
|
fval.SetFloat(float64(C.lua_tonumber(L.s, 3)))
|
|
|
|
return 1
|
|
|
|
} else {
|
|
|
|
L.PushString("Wrong assignment to field " + field_name)
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
L.PushString("Unsupported type of field " + field_name + ": " + fval.Type().String())
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
|
|
|
//export golua_interface_index_callback
|
2016-05-01 18:33:02 +03:00
|
|
|
func golua_interface_index_callback(gostateindex uintptr, iid uint, field_name *C.char) int {
|
|
|
|
L := getGoState(gostateindex)
|
2014-09-13 10:11:05 +04:00
|
|
|
iface := L.registry[iid]
|
|
|
|
ifacevalue := reflect.ValueOf(iface).Elem()
|
|
|
|
|
|
|
|
fval := ifacevalue.FieldByName(C.GoString(field_name))
|
|
|
|
|
|
|
|
if fval.Kind() == reflect.Ptr {
|
|
|
|
fval = fval.Elem()
|
|
|
|
}
|
|
|
|
|
|
|
|
switch fval.Kind() {
|
|
|
|
case reflect.Bool:
|
|
|
|
L.PushBoolean(fval.Bool())
|
|
|
|
return 1
|
|
|
|
|
|
|
|
case reflect.Int:
|
|
|
|
fallthrough
|
|
|
|
case reflect.Int8:
|
|
|
|
fallthrough
|
|
|
|
case reflect.Int16:
|
|
|
|
fallthrough
|
|
|
|
case reflect.Int32:
|
|
|
|
fallthrough
|
|
|
|
case reflect.Int64:
|
|
|
|
L.PushInteger(fval.Int())
|
|
|
|
return 1
|
|
|
|
|
|
|
|
case reflect.Uint:
|
|
|
|
fallthrough
|
|
|
|
case reflect.Uint8:
|
|
|
|
fallthrough
|
|
|
|
case reflect.Uint16:
|
|
|
|
fallthrough
|
|
|
|
case reflect.Uint32:
|
|
|
|
fallthrough
|
|
|
|
case reflect.Uint64:
|
|
|
|
L.PushInteger(int64(fval.Uint()))
|
|
|
|
return 1
|
|
|
|
|
|
|
|
case reflect.String:
|
|
|
|
L.PushString(fval.String())
|
|
|
|
return 1
|
|
|
|
|
|
|
|
case reflect.Float32:
|
|
|
|
fallthrough
|
|
|
|
case reflect.Float64:
|
|
|
|
L.PushNumber(fval.Float())
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
L.PushString("Unsupported type of field: " + fval.Type().String())
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
|
|
|
//export golua_gchook
|
2016-05-01 18:33:02 +03:00
|
|
|
func golua_gchook(gostateindex uintptr, id uint) int {
|
|
|
|
L1 := getGoState(gostateindex)
|
2014-09-13 10:11:05 +04:00
|
|
|
L1.unregister(id)
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
//export golua_callpanicfunction
|
2016-05-01 18:33:02 +03:00
|
|
|
func golua_callpanicfunction(gostateindex uintptr, id uint) int {
|
|
|
|
L1 := getGoState(gostateindex)
|
2014-09-13 10:11:05 +04:00
|
|
|
f := L1.registry[id].(LuaGoFunction)
|
|
|
|
return f(L1)
|
|
|
|
}
|
|
|
|
|
|
|
|
//export golua_idtointerface
|
|
|
|
func golua_idtointerface(id uint) interface{} {
|
|
|
|
return id
|
|
|
|
}
|
|
|
|
|
|
|
|
//export golua_cfunctiontointerface
|
|
|
|
func golua_cfunctiontointerface(f *uintptr) interface{} {
|
|
|
|
return f
|
|
|
|
}
|
|
|
|
|
|
|
|
//export golua_callallocf
|
|
|
|
func golua_callallocf(fp uintptr, ptr uintptr, osize uint, nsize uint) uintptr {
|
|
|
|
return uintptr((*((*Alloc)(unsafe.Pointer(fp))))(unsafe.Pointer(ptr), osize, nsize))
|
|
|
|
}
|
|
|
|
|
|
|
|
//export go_panic_msghandler
|
2016-05-01 18:33:02 +03:00
|
|
|
func go_panic_msghandler(gostateindex uintptr, z *C.char) {
|
|
|
|
L := getGoState(gostateindex)
|
2014-09-13 10:11:05 +04:00
|
|
|
s := C.GoString(z)
|
|
|
|
|
|
|
|
panic(&LuaError{LUA_ERRERR, s, L.StackTrace()})
|
|
|
|
}
|