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) } }
// 调用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 }
// 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 }
//export Invoke func Invoke(funcId int64) int { function, ok := functionRegister.Get(funcId) if !ok { //NOCOVER panic(fmt.Sprintf("invalid function id %d\n", funcId)) } // check argument count argc := C.lua_gettop(function.lua.State) if int(argc) != function.argc { function.lua.Panic("arguments not match: %v", function.fun) } // arguments var args []reflect.Value for i := C.int(1); i <= argc; i++ { args = append(args, function.lua.toGoValue(i, function.funcType.In(int(i-1)))) } // call and returns returnValues := function.funcValue.Call(args) if len(returnValues) != function.funcType.NumOut() { //NOCOVER function.lua.Panic("return values not match: %v", function.fun) } for _, v := range returnValues { function.lua.PushGoValue(v) } return len(returnValues) }
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 }
// Pcall calls a lua function. no panic func (l *Lua) Pcall(fullname string, args ...interface{}) (returns []interface{}, err error) { C.push_errfunc(l.State) curTop := C.lua_gettop(l.State) // get function path := strings.Split(fullname, ".") for i, name := range path { if i == 0 { C.lua_getfield(l.State, C.LUA_GLOBALSINDEX, cstr(name)) } else { if C.lua_type(l.State, -1) != C.LUA_TTABLE { return nil, fmt.Errorf("%s is not a function", fullname) } C.lua_pushstring(l.State, cstr(name)) C.lua_gettable(l.State, -2) C.lua_remove(l.State, -2) // remove table } } if C.lua_type(l.State, -1) != C.LUA_TFUNCTION { return nil, fmt.Errorf("%s is not a function", fullname) } // args for _, arg := range args { l.pushGoValue(arg, "") } // call l.err = nil if ret := C.lua_pcall(l.State, C.int(len(args)), C.LUA_MULTRET, C.int(-(len(args))-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 } returns[int(nReturn-1-i)] = value.Interface() } } 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 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 (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 luaPackToString(state State) int { // arg 1 is func udata itself var out bytes.Buffer L := state.L from := 2 to := int(C.lua_gettop(state.L)) ok, err := PackLuaObjects(&out, state, from, to) if !ok { C.lua_pushnil(L) pushStringToLua(L, fmt.Sprintf("%v", err)) return 2 } pushBytesToLua(L, out.Bytes()) return 1 }
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 }
func luaPushMultiLevelTable(L *C.lua_State, path []string) (bool, error) { ok, _ := luaGetSubTable(L, C.LUA_GLOBALSINDEX, path[0]) if !ok { return false, fmt.Errorf("field `%v` exist, and it is not a table", path[0]) } for i := 1; i < len(path); i++ { table := C.lua_gettop(L) ok, _ := luaGetSubTable(L, table, path[i]) if !ok { return false, fmt.Errorf("field `%v` exist, and it is not a table", strings.Join(path[:i+1], ".")) } } return true, nil }
//export invokeGoFunc func invokeGoFunc(state *C.lua_State) int { id := C.lua_tointeger(state, C.LUA_GLOBALSINDEX-1) funcsLock.RLock() function := funcs[id] funcsLock.RUnlock() // fast paths switch f := function.fun.(type) { case func(): f() return 0 } // check args argc := C.lua_gettop(state) if int(argc) != function.argc { // Lua.Eval will check err function.lua.err = fmt.Errorf("CALL ERROR: number of arguments not match: %s\n%s", function.name, function.lua.getStackTraceback()) return 0 } // prepare args var args []reflect.Value for i := C.int(1); i <= argc; i++ { goValue, err := function.lua.toGoValue(i, function.funcType.In(int(i-1))) if err != nil { function.lua.err = fmt.Errorf("CALL ERROR: toGoValue error: %v\n%s", err, function.lua.getStackTraceback()) return 0 } if goValue != nil { args = append(args, *goValue) } else { args = append(args, reflect.Zero(function.funcType.In(int(i-1)))) } } // call and returns returnValues := function.funcValue.Call(args) for _, v := range returnValues { function.lua.pushGoValue(v.Interface(), "") } return len(returnValues) }
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) }
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) CallFunction(name string, args ...interface{}) { 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) } }() cName := cstr(name) C.setup_message_handler(self.State) C.lua_getglobal(self.State, cName) for _, arg := range args { self.PushGoValue(reflect.ValueOf(arg)) } ret := C.lua_pcallk(self.State, C.int(len(args)), 0, C.lua_gettop(self.State)-C.int(len(args)+2), 0, nil) if ret != C.int(0) { self.Panic("%s", C.GoString(C.lua_tolstring(self.State, -1, 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) }
func (L *State) getFuncIn(ft rf.Type) []rf.Value { var in []rf.Value var i int for i = 0; i < ft.NumIn()-1; i++ { in = append(in, *L.getValueByType(ft.In(i).Kind(), i)) } switch { case ft.IsVariadic(): ek := ft.In(i).Elem().Kind() for ; i < int(C.lua_gettop(L.s)); i++ { switch ek { case rf.Interface: in = append(in, *L.getValueByLuaType(i)) default: in = append(in, *L.getValueByType(ek, i)) } } case i < ft.NumIn(): in = append(in, *L.getValueByType(ft.In(i).Kind(), i)) } return in }
// lua_gettop func (L *State) GetTop() int { return int(C.lua_gettop(L.s)) }
// Returns the index of the top element in the stack. Because indices start // at 1, this result is equal to the number of elements in the stack (and // so 0 means an empty stack). func (this *State) Gettop() int { return int(C.lua_gettop(this.luastate)) }
// Returns the index of the top element in the stack. Because indices start // at 1, this result is equal to the number of elements in the stack (and // so 0 means an empty stack). func (s *State) Gettop() int { return int(C.lua_gettop(s.l)) }
//export GO_callObject func GO_callObject(_L unsafe.Pointer, ref unsafe.Pointer) int { L := (*C.lua_State)(_L) node := (*refGo)(ref) obj := node.obj vm := node.vm state := State{vm, L} v := reflect.ValueOf(obj) k := v.Kind() if k != reflect.Func { pushStringToLua(L, fmt.Sprintf("try to call a non-function go object, type `%v'", k)) return -1 } t := v.Type() ningo := t.NumIn() if ningo == 1 { if t.In(0) == reflect.TypeOf(state) { return state.safeRawCall(v) } } ltop := int(C.lua_gettop(L)) in := make([]reflect.Value, ningo) ilua := 2 if t.IsVariadic() { for i := 0; i < ningo-1; i++ { tin := t.In(i) value, err := state.luaToGoValue(ilua, &tin) if err != nil { pushCallArgError(L, ilua, err) return -1 } in[i] = value ilua++ } nvarg := ltop - (ilua - 1) varg := reflect.MakeSlice(t.In(ningo-1), nvarg, nvarg) vargType := t.In(ningo - 1).Elem() for i := 0; i < nvarg; i++ { value, err := state.luaToGoValue(ilua, &vargType) if err != nil { pushCallArgError(L, ilua, err) return -1 } if value.IsValid() { varg.Index(i).Set(value) } ilua++ } in[ningo-1] = varg } else { for i := 0; i < ningo; i++ { tin := t.In(i) value, err := state.luaToGoValue(ilua, &tin) if err != nil { pushCallArgError(L, ilua, err) return -1 } in[i] = value ilua++ } } ok, out, err := safeCall(v, in) if !ok { pushStringToLua(L, "call go func error: "+err.Error()) return -1 } for _, value := range out { state.goToLuaValue(value) } return len(out) }