func (self *Lua) RegisterFunction(name string, fun interface{}) { path := strings.Split(name, ".") name = path[len(path)-1] path = path[0 : len(path)-1] if len(path) == 0 { path = append(path, "_G") } // ensure namespaces for i, namespace := range path { cNamespace := cstr(namespace) if i == 0 { // top namespace what := C.lua_getglobal(self.State, cNamespace) if what == C.LUA_TNIL { // not exists C.lua_settop(self.State, -2) C.lua_createtable(self.State, 0, 0) C.lua_setglobal(self.State, cNamespace) C.lua_getglobal(self.State, cNamespace) } if C.lua_type(self.State, -1) != C.LUA_TTABLE { self.Panic("global %s is not a table", namespace) } } else { // sub namespace C.lua_pushstring(self.State, cNamespace) C.lua_rawget(self.State, -2) if C.lua_type(self.State, -1) == C.LUA_TNIL { C.lua_settop(self.State, -2) C.lua_pushstring(self.State, cNamespace) C.lua_createtable(self.State, 0, 0) C.lua_rawset(self.State, -3) C.lua_pushstring(self.State, cNamespace) C.lua_rawget(self.State, -2) } if C.lua_type(self.State, -1) != C.LUA_TTABLE { self.Panic("namespace %s is not a table", namespace) } } } // register function funcType := reflect.TypeOf(fun) if funcType.IsVariadic() { self.Panic("cannot register variadic function: %v", fun) } argc := funcType.NumIn() cName := cstr(name) function := &Function{ fun: fun, lua: self, name: name, funcType: funcType, funcValue: reflect.ValueOf(fun), argc: argc, } funcId := rand.Int63() functionRegister.Set(funcId, function) C.register_function(self.State, cName, (C.int64_t)(funcId)) self.Functions[name] = function C.lua_settop(self.State, -2) }
func (tbl *Table) Foreach(fn func(key interface{}, value interface{}) bool) { if tbl.Ref == 0 { return } L := tbl.VM.globalL state := State{tbl.VM, L} bottom := C.lua_gettop(L) defer C.lua_settop(L, bottom) tbl.PushValue(state) ltable := C.lua_gettop(L) C.lua_pushnil(L) for { if 0 == C.lua_next(L, ltable) { return } vkey, err := state.luaToGoValue(-2, nil) if err != nil { return } vvalue, err := state.luaToGoValue(-1, nil) if err != nil { return } cont := fn(vkey.Interface(), vvalue.Interface()) if !cont { return } C.lua_settop(L, -2) } }
func packLuaTable(out io.Writer, state State, object int, depth int) (n int, err error) { depth++ L := state.L if depth > MAX_PACK_DEPTH { return 0, fmt.Errorf("pack too depth, depth=%v", depth) } n = 0 err = nil var mapSize int = 0 C.lua_pushnil(L) for { if 0 == C.lua_next(L, C.int(object)) { break } mapSize++ C.lua_settop(L, -2) // pop 1 } var ni int ni, err = P.PackMapHead(out, uint32(mapSize)) n += ni if err != nil { return } C.lua_pushnil(L) for { if 0 == C.lua_next(L, C.int(object)) { break } top := int(C.lua_gettop(L)) // key ni, err = packLuaObject(out, state, top-1, depth) n += ni if err != nil { C.lua_settop(L, -3) // pop 2 return } // value ni, err = packLuaObject(out, state, top, depth) n += ni if err != nil { C.lua_settop(L, -3) // pop 2 return } C.lua_settop(L, -2) // removes value, keeps key for next iteration } return }
func (state *State) luaTableToKeyValues(ltable int) (value reflect.Value, err error) { var vvalue reflect.Value L := state.L size := sizeOfLuaTable(L, ltable) result := make([]base.KeyValue, 0, size) C.lua_pushnil(L) for { if 0 == C.lua_next(L, C.int(ltable)) { break } lvalue := int(C.lua_gettop(L)) lkey := lvalue - 1 vkey, err := state.luaToGoValue(lkey, nil) if err != nil { C.lua_settop(L, -3) // pop 2 break } if C.LUA_TTABLE == C.lua_type(L, C.int(lvalue)) { vvalue, err = state.luaTableToKeyValues(lvalue) } else { vvalue, err = state.luaToGoValue(lvalue, nil) } if err != nil { C.lua_settop(L, -3) // pop 2 break } key := vkey.Interface() var skey string if s, ok := key.(string); ok { skey = s } else { skey = fmt.Sprint(key) } value := vvalue.Interface() result = append(result, base.KeyValue{skey, value}) C.lua_settop(L, -2) // pop 1 } return reflect.ValueOf(result), err }
// 注册Go库到lua func (L *State) Register(lib *Libfuncs) (bool, error) { libn := C.CString(lib.Libname) defer C.free(unsafe.Pointer(libn)) // 获取GlibTable fsize := len(lib.Funcs) if C.GetGlibTable(L.s, libn, C.int(fsize)) != 0 { return false, errors.New(fmt.Sprintf("Lib name(%s) is wrong.\n", lib.Libname)) } // 设置函数 for k, v := range lib.Funcs { // 检查函数列表 if ok, err := checkFuncInOutArgs(v); !ok { log.Println(err) continue } // 保存到State L.lf = append(L.lf, v) idx := len(L.lf) kn := C.CString(k) // 设置index到GlibTtable C.SetGfunc(L.s, kn, C.int(idx-1)) C.free(unsafe.Pointer(kn)) } C.lua_settop(L.s, 0) return true, nil }
// 调用lua中的函数,但是只能返回bool, float, string,以及其他go特殊类型,int型被转换为float返回。 func (L *State) Call(fname string, args ...interface{}) (out []interface{}, ok bool) { fn := C.CString(fname) defer C.free(unsafe.Pointer(fn)) top := int(C.lua_gettop(L.s)) if C.int(1) != C.FindFuncs(L.s, fn) { ok = false out = append(out, errors.New(fmt.Sprintf("not find the function(%s).\n", fname))) return } num := len(args) for _, arg := range args { argt := rf.TypeOf(arg) argv := rf.ValueOf(arg) L.pushValueByType(argt.Kind(), &argv) } C.lua_call(L.s, C.int(num), C.LUA_MULTRET) for i := top; i < int(C.lua_gettop(L.s)); i++ { ret := L.getValueByLuaType(i) if ret.IsValid() { out = append(out, ret.Interface()) } else { out = append(out, nil) } } C.lua_settop(L.s, C.int(top)) ok = true return }
func (tbl *Table) GetWithError(key interface{}) (interface{}, error) { if tbl.Ref == 0 { return nil, fmt.Errorf("cannot get a released lua table") } L := tbl.VM.globalL state := State{tbl.VM, L} bottom := C.lua_gettop(L) defer C.lua_settop(L, bottom) tbl.PushValue(state) vkey := reflect.ValueOf(key) ok := state.goToLuaValue(vkey) if !ok { return nil, fmt.Errorf("invalid key type for lua type: %v", vkey.Kind()) } C.lua_gettable(L, C.int(-2)) vvalue, err := state.luaToGoValue(-1, nil) if err != nil { return nil, err } if vvalue.IsValid() { return vvalue.Interface(), nil } return nil, nil }
// Peval evaluates a piece of lua code. no panic when error occur. func (l *Lua) Peval(code string, envs ...interface{}) (returns []interface{}, err error) { defer C.lua_settop(l.State, 0) C.push_errfunc(l.State) curTop := C.lua_gettop(l.State) // parse cCode := C.CString(code) defer C.free(unsafe.Pointer(cCode)) if ret := C.luaL_loadstring(l.State, cCode); ret != 0 { // load error return nil, fmt.Errorf("LOAD ERROR: %s", C.GoString(C.lua_tolstring(l.State, -1, nil))) } // env if len(envs) > 0 { if len(envs)%2 != 0 { return nil, fmt.Errorf("number of arguments not match") } C.lua_createtable(l.State, 0, 0) for i := 0; i < len(envs); i += 2 { name, ok := envs[i].(string) if !ok { return nil, fmt.Errorf("name must be string, not %v", envs[i]) } C.lua_pushstring(l.State, cstr(name)) err := l.pushGoValue(envs[i+1], name) if err != nil { return nil, err } C.lua_rawset(l.State, -3) } // set env C.set_eval_env(l.State) } // call l.err = nil if ret := C.lua_pcall(l.State, 0, C.LUA_MULTRET, -2); ret != 0 { // error occured return nil, fmt.Errorf("CALL ERROR: %s", C.GoString(C.lua_tolstring(l.State, -1, nil))) } else if l.err != nil { // error raise by invokeGoFunc return nil, l.err } else { // return values nReturn := C.lua_gettop(l.State) - curTop returns = make([]interface{}, int(nReturn)) for i := C.int(0); i < nReturn; i++ { value, err := l.toGoValue(-1-i, interfaceType) if err != nil { return nil, err } if value != nil { returns[int(nReturn-1-i)] = value.Interface() } else { returns[int(nReturn-1-i)] = nil } } } return }
func callLuaFuncUtil(state State, inv []reflect.Value, nout int) ([]interface{}, error) { L := state.L bottom := int(C.lua_gettop(L)) var result []interface{} var nluaout C.int var nin C.int if nout >= 0 { nluaout = C.int(nout) result = make([]interface{}, 0, nout) } else { nluaout = C.LUA_MULTRET result = make([]interface{}, 0, 1) } if inv != nil { for _, iarg := range inv { state.goToLuaValue(iarg) } nin = C.int(len(inv)) } else { nin = 0 } ret := int(C.lua_pcall(L, nin, nluaout, 0)) if ret != 0 { err := stringFromLua(L, -1) C.lua_settop(L, -2) return result, errors.New(err) } top := int(C.lua_gettop(L)) for i := bottom; i <= top; i++ { value, _ := state.luaToGoValue(i, nil) if value.IsValid() { result = append(result, value.Interface()) } else { result = append(result, nil) } } rnout := C.int(top + 1 - bottom) C.lua_settop(L, -rnout-1) return result, nil }
func sizeOfLuaTable(L *C.lua_State, ltable int) int { var size int = 0 C.lua_pushnil(L) for { if 0 == C.lua_next(L, C.int(ltable)) { break } size++ C.lua_settop(L, -2) // pop 1 } return size }
func (tbl *Table) GetnWithError() (int, error) { if tbl.Ref == 0 { return 0, fmt.Errorf("cannot get lenght a released lua table") } L := tbl.VM.globalL state := State{tbl.VM, L} bottom := int(C.lua_gettop(L)) defer C.lua_settop(L, C.int(bottom)) tbl.PushValue(state) n := int(C.lua_objlen(L, C.int(-1))) return n, nil }
func PackLuaObjects(out io.Writer, state State, from int, to int) (ok bool, err error) { L := state.L top := C.lua_gettop(L) ok = true err = nil for object := from; object <= to; object++ { _, err = PackLuaObject(out, state, object) if err != nil { ok = false break } } C.lua_settop(L, top) return ok, err }
func luaGetSubTable(L *C.lua_State, table C.int, key string) (bool, error) { pushStringToLua(L, key) C.lua_gettable(L, table) ltype := C.lua_type(L, -1) if ltype == C.LUA_TNIL { C.lua_createtable(L, 0, 0) // table[key] = {} pushStringToLua(L, key) C.lua_pushvalue(L, -2) C.lua_settable(L, table) } ltype = C.lua_type(L, -1) if ltype != C.LUA_TTABLE { C.lua_settop(L, -2) return false, fmt.Errorf("field `%v` exist, and it is not a table", key) } return true, nil }
func (l *Lua) set(fullname string, v interface{}) error { // ensure name errMsg := C.ensure_name(l.State, C.CString(fullname)) if errMsg != nil { return fmt.Errorf("%s: %s", errMsg, fullname) } // push value err := l.pushGoValue(v, fullname) if err != nil { return err } // set C.lua_rawset(l.State, -3) // clear stack C.lua_settop(l.State, 0) return nil }
func (vm *VM) EvalStringWithError(str string, arg ...interface{}) ([]interface{}, error) { L := vm.globalL state := State{vm, L} s, n := stringToC(str) bottom := C.lua_gettop(L) defer C.lua_settop(L, bottom) ret := int(C.luaL_loadbuffer(L, s, n, nil)) if ret != 0 { err := stringFromLua(L, -1) return make([]interface{}, 0), errors.New(err) } nout := -1 if len(arg) > 0 { if x, ok := arg[0].(int); ok { nout = x } } return callLuaFuncUtil(state, nil, nout) }
// Decodes the result from a function into a Go object. func (e *ExecutionEngine) decodeResult() (interface{}, error) { // Encode Lua object into msgpack. rc := C.mp_pack(e.state) if rc != 1 { return nil, errors.New("skyd.ExecutionEngine: Unable to msgpack decode Lua result") } sz := C.size_t(0) ptr := C.lua_tolstring(e.state, -1, (*C.size_t)(&sz)) str := C.GoStringN(ptr, (C.int)(sz)) C.lua_settop(e.state, -(1)-1) // lua_pop() // Decode msgpack into a Go object. var ret interface{} decoder := msgpack.NewDecoder(bytes.NewBufferString(str), nil) err := decoder.Decode(&ret) if err != nil { return nil, err } return ret, nil }
func (tbl *Table) Set(key interface{}, value interface{}) (bool, error) { if tbl.Ref == 0 { return false, fmt.Errorf("cannot set a released lua table") } L := tbl.VM.globalL state := State{tbl.VM, L} bottom := C.lua_gettop(L) defer C.lua_settop(L, bottom) tbl.PushValue(state) vkey := reflect.ValueOf(key) ok := state.goToLuaValue(vkey) if !ok { return false, fmt.Errorf("invalid key type for lua type: %v", vkey.Kind()) } state.goToLuaValue(reflect.ValueOf(value)) C.lua_settable(L, C.int(-3)) return true, nil }
func (self *Lua) RunString(code string) { defer func() { if r := recover(); r != nil { if self.PrintTraceback { //NOCOVER print("============ start lua traceback ============\n") self.RunString(`print(debug.traceback())`) print("============ end lua traceback ==============\n") } panic(r) } }() cCode := cstr(code) C.setup_message_handler(self.State) if ret := C.luaL_loadstring(self.State, cCode); ret != C.int(0) { self.Panic("%s", C.GoString(C.lua_tolstring(self.State, -1, nil))) } ret := C.lua_pcallk(self.State, 0, 0, C.lua_gettop(self.State)-C.int(1), 0, nil) if ret != C.int(0) { self.Panic("%s", C.GoString(C.lua_tolstring(self.State, -1, nil))) } C.lua_settop(self.State, 0) }
func (vm *VM) EvalBufferWithError(reader io.Reader, arg ...interface{}) ([]interface{}, error) { L := vm.globalL state := State{vm, L} context := loadBufferContext{ reader: reader, buf: make([]byte, READ_BUFFER_SIZE), } bottom := C.lua_gettop(L) defer C.lua_settop(L, bottom) ret := int(C.clua_loadProxy(L, unsafe.Pointer(&context))) if ret != 0 { err := stringFromLua(L, -1) return make([]interface{}, 0), errors.New(err) } nout := -1 if len(arg) > 0 { if x, ok := arg[0].(int); ok { nout = x } } return callLuaFuncUtil(state, nil, nout) }
// Accepts any valid index, or 0, and sets the stack top to this // index. If the new top is larger than the old one, then the new elements // are filled with nil. If index is 0, then all stack elements are removed. func (this *State) Settop(index int) { C.lua_settop(this.luastate, C.int(index)) }
func (L *State) Pop(n int) { //C.lua_pop(L.s, C.int(n)); C.lua_settop(L.s, C.int(-n-1)) }
// Accepts any valid index, or 0, and sets the stack top to this // index. If the new top is larger than the old one, then the new elements // are filled with nil. If index is 0, then all stack elements are removed. func (s *State) Settop(index int) { C.lua_settop(s.l, C.int(index)) }
func (L *State) setFuncOut(ft rf.Type, out []rf.Value) { C.lua_settop(L.s, 0) for i, v := range out { L.pushValueByType(ft.Out(i).Kind(), &v) } }
func (lua *Lua) toGoValue(i C.int, paramType reflect.Type) (ret reflect.Value) { luaType := C.lua_type(lua.State, i) paramKind := paramType.Kind() switch paramKind { case reflect.Bool: if luaType != C.LUA_TBOOLEAN { lua.Panic("not a boolean") } ret = reflect.ValueOf(C.lua_toboolean(lua.State, i) == C.int(1)) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: if luaType != C.LUA_TNUMBER { lua.Panic("not an integer") } ret = reflect.New(paramType).Elem() ret.SetInt(int64(C.lua_tointegerx(lua.State, i, nil))) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: if luaType != C.LUA_TNUMBER { lua.Panic("not an unsigned") } ret = reflect.New(paramType).Elem() ret.SetUint(uint64(C.lua_tointegerx(lua.State, i, nil))) case reflect.Float32, reflect.Float64: if luaType != C.LUA_TNUMBER { lua.Panic("not a float") } ret = reflect.New(paramType).Elem() ret.SetFloat(float64(C.lua_tonumberx(lua.State, i, nil))) case reflect.Interface: switch luaType { case C.LUA_TNUMBER: ret = reflect.New(floatType).Elem() ret.SetFloat(float64(C.lua_tonumberx(lua.State, i, nil))) case C.LUA_TSTRING: ret = reflect.New(stringType).Elem() ret.SetString(C.GoString(C.lua_tolstring(lua.State, i, nil))) case C.LUA_TLIGHTUSERDATA: ret = reflect.ValueOf(C.lua_topointer(lua.State, i)) case C.LUA_TBOOLEAN: ret = reflect.New(boolType).Elem() ret.SetBool(C.lua_toboolean(lua.State, i) == C.int(1)) //TODO nil //TODO table default: lua.Panic("wrong interface argument: %v", paramKind) } case reflect.String: if luaType != C.LUA_TSTRING { lua.Panic("not a string") } ret = reflect.New(paramType).Elem() ret.SetString(C.GoString(C.lua_tolstring(lua.State, i, nil))) case reflect.Slice: switch luaType { case C.LUA_TSTRING: ret = reflect.New(paramType).Elem() cstr := C.lua_tolstring(lua.State, i, nil) ret.SetBytes(C.GoBytes(unsafe.Pointer(cstr), C.int(C.strlen(cstr)))) case C.LUA_TTABLE: ret = reflect.MakeSlice(paramType, 0, 0) C.lua_pushnil(lua.State) elemType := paramType.Elem() for C.lua_next(lua.State, i) != 0 { ret = reflect.Append(ret, lua.toGoValue(-1, elemType)) C.lua_settop(lua.State, -2) } default: lua.Panic("wrong slice argument") } case reflect.Ptr: if luaType != C.LUA_TLIGHTUSERDATA { lua.Panic("not a pointer") } pointer := C.lua_topointer(lua.State, i) ret = reflect.NewAt(paramType, unsafe.Pointer(&pointer)).Elem() case reflect.Map: if luaType != C.LUA_TTABLE { lua.Panic("not a map") } ret = reflect.MakeMap(paramType) C.lua_pushnil(lua.State) keyType := paramType.Key() elemType := paramType.Elem() for C.lua_next(lua.State, i) != 0 { ret.SetMapIndex( lua.toGoValue(-2, keyType), lua.toGoValue(-1, elemType)) C.lua_settop(lua.State, -2) } case reflect.UnsafePointer: ret = reflect.ValueOf(C.lua_topointer(lua.State, i)) //TODO complex64/128 //TODO array //TODO chan //TODO func //TODO struct default: lua.Panic("unknown argument type %v", paramType) } return }
func (l *Lua) toGoValue(i C.int, paramType reflect.Type) (ret *reflect.Value, err error) { luaType := C.lua_type(l.State, i) paramKind := paramType.Kind() switch paramKind { case reflect.Bool: if luaType != C.LUA_TBOOLEAN { err = fmt.Errorf("not a boolean") return } v := reflect.ValueOf(C.lua_toboolean(l.State, i) == C.int(1)) ret = &v case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: if luaType != C.LUA_TNUMBER { err = fmt.Errorf("not an integer") return } v := reflect.New(paramType).Elem() v.SetInt(int64(C.lua_tointeger(l.State, i))) ret = &v case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: if luaType != C.LUA_TNUMBER { err = fmt.Errorf("not a unsigned") return } v := reflect.New(paramType).Elem() v.SetUint(uint64(C.lua_tointeger(l.State, i))) ret = &v case reflect.Float32, reflect.Float64: if luaType != C.LUA_TNUMBER { err = fmt.Errorf("not a float") return } v := reflect.New(paramType).Elem() v.SetFloat(float64(C.lua_tonumber(l.State, i))) ret = &v case reflect.Interface: switch paramType { case interfaceType: switch luaType { case C.LUA_TNUMBER: v := reflect.New(floatType).Elem() // always return float64 for interface{} v.SetFloat(float64(C.lua_tonumber(l.State, i))) ret = &v case C.LUA_TSTRING: v := reflect.New(stringType).Elem() v.SetString(C.GoString(C.lua_tolstring(l.State, i, nil))) ret = &v case C.LUA_TLIGHTUSERDATA, C.LUA_TUSERDATA: v := reflect.ValueOf(C.lua_touserdata(l.State, i)) ret = &v case C.LUA_TBOOLEAN: v := reflect.New(boolType).Elem() v.SetBool(C.lua_toboolean(l.State, i) == C.int(1)) ret = &v case C.LUA_TNIL: ret = nil default: err = fmt.Errorf("unsupported type %s for interface{}", luaTypeName(luaType)) return } default: err = fmt.Errorf("only interface{} is supported, no %v", paramType) return } case reflect.String: if luaType != C.LUA_TSTRING { err = fmt.Errorf("not a string") return } v := reflect.New(paramType).Elem() v.SetString(C.GoString(C.lua_tolstring(l.State, i, nil))) ret = &v case reflect.Slice: switch luaType { case C.LUA_TSTRING: v := reflect.New(paramType).Elem() cstr := C.lua_tolstring(l.State, i, nil) v.SetBytes(C.GoBytes(unsafe.Pointer(cstr), C.int(C.strlen(cstr)))) ret = &v case C.LUA_TTABLE: v := reflect.MakeSlice(paramType, 0, 0) C.lua_pushnil(l.State) elemType := paramType.Elem() for C.lua_next(l.State, i) != 0 { elemValue, e := l.toGoValue(-1, elemType) if e != nil { err = e return } // there is no nil value in lua table so elemValue will never be nil v = reflect.Append(v, *elemValue) C.lua_settop(l.State, -2) ret = &v } default: err = fmt.Errorf("wrong slice argument") return } case reflect.Ptr: if luaType != C.LUA_TLIGHTUSERDATA { err = fmt.Errorf("not a pointer") return } p := C.lua_topointer(l.State, i) v := reflect.NewAt(paramType, unsafe.Pointer(&p)).Elem() ret = &v case reflect.Map: if luaType != C.LUA_TTABLE { err = fmt.Errorf("not a map") return } v := reflect.MakeMap(paramType) C.lua_pushnil(l.State) keyType := paramType.Key() elemType := paramType.Elem() for C.lua_next(l.State, i) != 0 { keyValue, e := l.toGoValue(-2, keyType) if e != nil { err = e return } // table has no nil key so keyValue will not be nil elemValue, e := l.toGoValue(-1, elemType) if e != nil { err = e return } // table has no nil value so elemValue will not be nil v.SetMapIndex(*keyValue, *elemValue) C.lua_settop(l.State, -2) } ret = &v case reflect.UnsafePointer: v := reflect.ValueOf(C.lua_topointer(l.State, i)) ret = &v default: err = fmt.Errorf("unsupported toGoValue type %v", paramType) return } return }
// lua_pop func (L *State) Pop(n int) { //Why is this implemented this way? I don't get it... //C.lua_pop(L.s, C.int(n)); C.lua_settop(L.s, C.int(-n-1)) }
// lua_settop func (L *State) SetTop(index int) { C.lua_settop(L.s, C.int(index)) }
func Pop(s *State, n int) { // Is defined as a macro, so we define it: C.lua_settop((*C.lua_State)(s), C.int(-n-1)) }