golua.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. package lua
  2. /*
  3. #include <stdlib.h>
  4. #include "../clua/lua.h"
  5. #include "../clua/lualib.h"
  6. */
  7. import "C"
  8. import (
  9. "reflect"
  10. "sync"
  11. "unsafe"
  12. )
  13. // Type of allocation functions to use with NewStateAlloc
  14. type Alloc func(ptr unsafe.Pointer, osize uint, nsize uint) unsafe.Pointer
  15. // This is the type of go function that can be registered as lua functions
  16. type LuaGoFunction func(L *State) int
  17. // This is the type of a go function that can be used as a lua_Hook
  18. type HookFunction func(L *State)
  19. // The errorstring used by State.SetExecutionLimit
  20. const ExecutionQuantumExceeded = "Lua execution quantum exceeded"
  21. // Wrapper to keep cgo from complaining about incomplete ptr type
  22. //export State
  23. type State struct {
  24. // Wrapped lua_State object
  25. s *C.lua_State
  26. // index of this object inside the goStates array
  27. Index uintptr
  28. // Registry of go object that have been pushed to Lua VM
  29. registry []interface{}
  30. // Freelist for funcs indices, to allow for freeing
  31. freeIndices []uint
  32. // User self defined memory alloc func for the lua State
  33. allocfn *Alloc
  34. // User defined hook function
  35. hookFn HookFunction
  36. }
  37. var goStates map[uintptr]*State
  38. var goStatesMutex sync.Mutex
  39. func init() {
  40. goStates = make(map[uintptr]*State, 16)
  41. }
  42. func registerGoState(L *State) {
  43. goStatesMutex.Lock()
  44. defer goStatesMutex.Unlock()
  45. L.Index = uintptr(unsafe.Pointer(L))
  46. goStates[L.Index] = L
  47. }
  48. func unregisterGoState(L *State) {
  49. goStatesMutex.Lock()
  50. defer goStatesMutex.Unlock()
  51. delete(goStates, L.Index)
  52. }
  53. func getGoState(gostateindex uintptr) *State {
  54. goStatesMutex.Lock()
  55. defer goStatesMutex.Unlock()
  56. return goStates[gostateindex]
  57. }
  58. //export golua_callgofunction
  59. func golua_callgofunction(gostateindex uintptr, fid uint) int {
  60. L1 := getGoState(gostateindex)
  61. if fid < 0 {
  62. panic(&LuaError{0, "Requested execution of an unknown function", L1.StackTrace()})
  63. }
  64. f := L1.registry[fid].(LuaGoFunction)
  65. return f(L1)
  66. }
  67. //export golua_callgohook
  68. func golua_callgohook(gostateindex uintptr) {
  69. L1 := getGoState(gostateindex)
  70. if L1.hookFn != nil {
  71. L1.hookFn(L1)
  72. }
  73. }
  74. var typeOfBytes = reflect.TypeOf([]byte(nil))
  75. //export golua_interface_newindex_callback
  76. func golua_interface_newindex_callback(gostateindex uintptr, iid uint, field_name_cstr *C.char) int {
  77. L := getGoState(gostateindex)
  78. iface := L.registry[iid]
  79. ifacevalue := reflect.ValueOf(iface).Elem()
  80. field_name := C.GoString(field_name_cstr)
  81. fval := ifacevalue.FieldByName(field_name)
  82. if fval.Kind() == reflect.Ptr {
  83. fval = fval.Elem()
  84. }
  85. luatype := LuaValType(C.lua_type(L.s, 3))
  86. switch fval.Kind() {
  87. case reflect.Bool:
  88. if luatype == LUA_TBOOLEAN {
  89. fval.SetBool(int(C.lua_toboolean(L.s, 3)) != 0)
  90. return 1
  91. } else {
  92. L.PushString("Wrong assignment to field " + field_name)
  93. return -1
  94. }
  95. case reflect.Int:
  96. fallthrough
  97. case reflect.Int8:
  98. fallthrough
  99. case reflect.Int16:
  100. fallthrough
  101. case reflect.Int32:
  102. fallthrough
  103. case reflect.Int64:
  104. if luatype == LUA_TNUMBER {
  105. fval.SetInt(int64(luaToInteger(L.s, 3)))
  106. return 1
  107. } else {
  108. L.PushString("Wrong assignment to field " + field_name)
  109. return -1
  110. }
  111. case reflect.Uint:
  112. fallthrough
  113. case reflect.Uint8:
  114. fallthrough
  115. case reflect.Uint16:
  116. fallthrough
  117. case reflect.Uint32:
  118. fallthrough
  119. case reflect.Uint64:
  120. if luatype == LUA_TNUMBER {
  121. fval.SetUint(uint64(luaToInteger(L.s, 3)))
  122. return 1
  123. } else {
  124. L.PushString("Wrong assignment to field " + field_name)
  125. return -1
  126. }
  127. case reflect.String:
  128. if luatype == LUA_TSTRING {
  129. fval.SetString(C.GoString(C.lua_tolstring(L.s, 3, nil)))
  130. return 1
  131. } else {
  132. L.PushString("Wrong assignment to field " + field_name)
  133. return -1
  134. }
  135. case reflect.Float32:
  136. fallthrough
  137. case reflect.Float64:
  138. if luatype == LUA_TNUMBER {
  139. fval.SetFloat(float64(luaToNumber(L.s, 3)))
  140. return 1
  141. } else {
  142. L.PushString("Wrong assignment to field " + field_name)
  143. return -1
  144. }
  145. case reflect.Slice:
  146. if fval.Type() == typeOfBytes {
  147. if luatype == LUA_TSTRING {
  148. fval.SetBytes(L.ToBytes(3))
  149. return 1
  150. } else {
  151. L.PushString("Wrong assignment to field " + field_name)
  152. return -1
  153. }
  154. }
  155. }
  156. L.PushString("Unsupported type of field " + field_name + ": " + fval.Type().String())
  157. return -1
  158. }
  159. //export golua_interface_index_callback
  160. func golua_interface_index_callback(gostateindex uintptr, iid uint, field_name *C.char) int {
  161. L := getGoState(gostateindex)
  162. iface := L.registry[iid]
  163. ifacevalue := reflect.ValueOf(iface).Elem()
  164. fval := ifacevalue.FieldByName(C.GoString(field_name))
  165. if fval.Kind() == reflect.Ptr {
  166. fval = fval.Elem()
  167. }
  168. switch fval.Kind() {
  169. case reflect.Bool:
  170. L.PushBoolean(fval.Bool())
  171. return 1
  172. case reflect.Int:
  173. fallthrough
  174. case reflect.Int8:
  175. fallthrough
  176. case reflect.Int16:
  177. fallthrough
  178. case reflect.Int32:
  179. fallthrough
  180. case reflect.Int64:
  181. L.PushInteger(fval.Int())
  182. return 1
  183. case reflect.Uint:
  184. fallthrough
  185. case reflect.Uint8:
  186. fallthrough
  187. case reflect.Uint16:
  188. fallthrough
  189. case reflect.Uint32:
  190. fallthrough
  191. case reflect.Uint64:
  192. L.PushInteger(int64(fval.Uint()))
  193. return 1
  194. case reflect.String:
  195. L.PushString(fval.String())
  196. return 1
  197. case reflect.Float32:
  198. fallthrough
  199. case reflect.Float64:
  200. L.PushNumber(fval.Float())
  201. return 1
  202. case reflect.Slice:
  203. if fval.Type() == typeOfBytes {
  204. L.PushBytes(fval.Bytes())
  205. return 1
  206. }
  207. }
  208. L.PushString("Unsupported type of field: " + fval.Type().String())
  209. return -1
  210. }
  211. //export golua_gchook
  212. func golua_gchook(gostateindex uintptr, id uint) int {
  213. L1 := getGoState(gostateindex)
  214. L1.unregister(id)
  215. return 0
  216. }
  217. //export golua_callpanicfunction
  218. func golua_callpanicfunction(gostateindex uintptr, id uint) int {
  219. L1 := getGoState(gostateindex)
  220. f := L1.registry[id].(LuaGoFunction)
  221. return f(L1)
  222. }
  223. //export golua_idtointerface
  224. func golua_idtointerface(id uint) interface{} {
  225. return id
  226. }
  227. //export golua_cfunctiontointerface
  228. func golua_cfunctiontointerface(f *uintptr) interface{} {
  229. return f
  230. }
  231. //export golua_callallocf
  232. func golua_callallocf(fp uintptr, ptr uintptr, osize uint, nsize uint) uintptr {
  233. return uintptr((*((*Alloc)(unsafe.Pointer(fp))))(unsafe.Pointer(ptr), osize, nsize))
  234. }
  235. //export go_panic_msghandler
  236. func go_panic_msghandler(gostateindex uintptr, z *C.char) {
  237. L := getGoState(gostateindex)
  238. s := C.GoString(z)
  239. panic(&LuaError{LUA_ERRERR, s, L.StackTrace()})
  240. }