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) 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 (state State) goToLuaValue(value reflect.Value) bool { L := state.L gkind := value.Kind() switch gkind { case reflect.Bool: v := value.Bool() if v { C.lua_pushboolean(L, 1) } else { C.lua_pushboolean(L, 0) } return true case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: v := value.Int() C.lua_pushinteger(L, C.lua_Integer(v)) return true case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: v := value.Uint() C.lua_pushinteger(L, C.lua_Integer(v)) return true // case reflect.Uintptr: case reflect.Float32, reflect.Float64: v := value.Float() C.lua_pushnumber(L, C.lua_Number(v)) return true // case reflect.Array: // case reflect.Complex64, reflect.Complex128: // case reflect.Chan // case reflect.Interface case reflect.Ptr: iv := value.Interface() if v, ok := iv.(ILuaRef); ok { v.PushValue(state) return true } state.pushObjToLua(value.Interface()) return true case reflect.Func, reflect.Map, reflect.Slice: state.pushObjToLua(value.Interface()) return true case reflect.String: v := value.String() pushStringToLua(L, v) return true case reflect.Struct: objPtr := reflect.New(value.Type()) objPtr.Elem().Set(value) state.pushObjToLua(objPtr.Interface()) return true //case reflect.UnsafePointer case reflect.Interface: return state.goToLuaValue(value.Elem()) } C.lua_pushnil(L) return false }
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 }
//export GO_indexObject func GO_indexObject(_L unsafe.Pointer, ref unsafe.Pointer, lkey C.int) (ret int) { L := (*C.lua_State)(_L) node := (*refGo)(ref) vm := node.vm state := State{vm, L} v := reflect.ValueOf(node.obj) t := v.Type() k := v.Kind() defer func() { if r := recover(); r != nil { pushStringToLua(L, fmt.Sprintf("%v", r)) ret = -1 } }() ltype := C.lua_type(L, lkey) switch k { case reflect.Slice: if ltype == C.LUA_TNUMBER { idx := int(C.lua_tointeger(L, lkey)) value := v.Index(idx) state.goToLuaValue(value) return 1 } panic(fmt.Sprintf("index of slice must be a number type, here got `%v'", luaTypeName(ltype))) case reflect.Map: keyType := t.Key() key, err := state.luaToGoValue(int(lkey), &keyType) if err != nil { panic(fmt.Sprintf("index type of map must be type `%v', %s", keyType.Kind(), err.Error())) } value := v.MapIndex(key) if !value.IsValid() { C.lua_pushnil(L) return 1 } state.goToLuaValue(value) return 1 case reflect.Ptr: if t.Elem().Kind() == reflect.Struct { ret, err := state.getStructField(v, lkey) if err != nil { panic(fmt.Sprintf("error when get field of struct, %s", err.Error())) } return ret } } panic(fmt.Sprintf("try to index a non-indexable go object, type `%v'", k)) return -1 }
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 luaKeys(state State) int { L := state.L vmap := mustBeMap(state, 2) if vmap == nil { C.lua_pushnil(L) pushStringToLua(L, "Keys() only apply to `map'") return 2 } vkeys := vmap.MapKeys() C.lua_createtable(L, C.int(len(vkeys)), 0) for i := 0; i < len(vkeys); i++ { if !state.goToLuaValue(vkeys[i]) { continue } C.lua_rawseti(L, C.int(-2), C.int(i+1)) } return 1 }
// lua_pushnil func (L *State) PushNil() { C.lua_pushnil(L.s) }
// Pushes a nil value onto the stack. func (s *State) Pushnil() { C.lua_pushnil(s.l) }
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 }
func (l *Lua) pushGoValue(v interface{}, name string) error { if v == nil { C.lua_pushnil(l.State) return nil } switch value := v.(type) { case bool: if value { C.lua_pushboolean(l.State, C.int(1)) } else { C.lua_pushboolean(l.State, C.int(0)) } case string: C.lua_pushstring(l.State, C.CString(value)) case int: C.lua_pushnumber(l.State, C.lua_Number(C.longlong(value))) case int8: C.lua_pushnumber(l.State, C.lua_Number(C.longlong(value))) case int16: C.lua_pushnumber(l.State, C.lua_Number(C.longlong(value))) case int32: C.lua_pushnumber(l.State, C.lua_Number(C.longlong(value))) case int64: C.lua_pushnumber(l.State, C.lua_Number(C.longlong(value))) case uint: C.lua_pushnumber(l.State, C.lua_Number(C.ulonglong(value))) case uint8: C.lua_pushnumber(l.State, C.lua_Number(C.ulonglong(value))) case uint16: C.lua_pushnumber(l.State, C.lua_Number(C.ulonglong(value))) case uint32: C.lua_pushnumber(l.State, C.lua_Number(C.ulonglong(value))) case uint64: C.lua_pushnumber(l.State, C.lua_Number(C.ulonglong(value))) case float32: C.lua_pushnumber(l.State, C.lua_Number(C.double(value))) case float64: C.lua_pushnumber(l.State, C.lua_Number(C.double(value))) case unsafe.Pointer: C.lua_pushlightuserdata(l.State, value) default: // not basic types, use reflect switch valueType := reflect.TypeOf(v); valueType.Kind() { case reflect.Func: // function if valueType.IsVariadic() { return fmt.Errorf("variadic function is not supported, %s", name) } function := &_Function{ name: name, lua: l, fun: v, funcType: valueType, funcValue: reflect.ValueOf(v), argc: valueType.NumIn(), } funcsLock.Lock() funcs = append(funcs, function) id := len(funcs) - 1 funcsLock.Unlock() C.push_go_func(l.State, C.int64_t(id)) case reflect.Slice: value := reflect.ValueOf(v) length := value.Len() C.lua_createtable(l.State, C.int(length), 0) for i := 0; i < length; i++ { C.lua_pushnumber(l.State, C.lua_Number(i+1)) err := l.pushGoValue(value.Index(i).Interface(), "") if err != nil { return err } C.lua_settable(l.State, -3) } case reflect.Ptr: C.lua_pushlightuserdata(l.State, unsafe.Pointer(reflect.ValueOf(v).Pointer())) default: // unknown type return fmt.Errorf("unsupported type %v", v) } } return nil }