// Copy matching Lua table entries to a struct, given the struct type // and the index on the Lua stack. func CopyTableToStruct(L *lua.State, t reflect.Type, idx int) interface{} { was_ptr := t.Kind() == reflect.Ptr if was_ptr { t = t.Elem() } s := reflect.New(t) // T -> *T ref := s.Elem() L.PushNil() if idx < 0 { idx-- } for L.Next(idx) != 0 { key := L.ToString(-2) f := ref.FieldByName(strings.Title(key)) if f.IsValid() { val := luaToGoValue(L, f.Type(), -1) f.Set(val) } L.Pop(1) } if was_ptr { return s.Interface() } return s.Elem().Interface() }
// Also for arrays. func copySliceToTable(L *lua.State, vslice reflect.Value, visited visitor) int { ref := vslice for vslice.Kind() == reflect.Ptr { // For arrays. vslice = vslice.Elem() } if vslice.IsValid() && (vslice.Kind() == reflect.Slice || vslice.Kind() == reflect.Array) { n := vslice.Len() L.CreateTable(n, 0) if vslice.Kind() == reflect.Slice { visited.mark(vslice) } else if ref.Kind() == reflect.Ptr { visited.mark(ref) } for i := 0; i < n; i++ { L.PushInteger(int64(i + 1)) v := vslice.Index(i) if isNil(v) { v = nullv } goToLua(L, nil, v, true, visited) L.SetTable(-3) } return 1 } L.PushNil() L.PushString("not a slice/array") return 2 }
func popMap(L *lua.State) map[string]interface{} { m := make(map[string]interface{}) L.PushNil() for L.Next(-2) != 0 { if L.IsString(-2) { var v interface{} if L.IsBoolean(-1) { v = L.ToBoolean(-1) } else if L.IsNumber(-1) { var t float64 t = L.ToNumber(-1) if t == float64(int64(t)) { v = int(t) } else { v = t } } else if L.IsString(-1) { v = L.ToString(-1) } else if L.IsNil(-1) { v = nil } else if L.IsTable(-1) { v = popMap(L) } m[L.ToString(-2)] = v } L.Pop(1) } return m }
func pushMap(L *lua.State, m map[string]interface{}, lower bool) { L.CreateTable(0, len(m)) for k, v := range m { if lower { L.PushString(strings.ToLower(k)) } else { L.PushString(k) } switch t := v.(type) { case string: L.PushString(t) case int64: L.PushInteger(t) case int: L.PushInteger(int64(t)) case float64: L.PushNumber(t) case bool: L.PushBoolean(t) case map[string]interface{}: pushMap(L, t, false) default: L.PushNil() } L.SetTable(-3) } }
func copyStructToTable(L *lua.State, vstruct reflect.Value, visited visitor) int { // If 'vstruct' is a pointer to struct, use the pointer to mark as visited. ref := vstruct for vstruct.Kind() == reflect.Ptr { vstruct = vstruct.Elem() } if vstruct.IsValid() && vstruct.Type().Kind() == reflect.Struct { n := vstruct.NumField() L.CreateTable(n, 0) if ref.Kind() == reflect.Ptr { visited.mark(ref) } for i := 0; i < n; i++ { st := vstruct.Type() field := st.Field(i) key := field.Name tag := field.Tag.Get("lua") if tag != "" { key = tag } goToLua(L, nil, reflect.ValueOf(key), true, visited) v := vstruct.Field(i) goToLua(L, nil, v, true, visited) L.SetTable(-3) } return 1 } L.PushNil() L.PushString("not a struct") return 2 }
func copyTableToMap(L *lua.State, t reflect.Type, idx int, visited map[uintptr]interface{}) interface{} { if t == nil { t = tmap } te, tk := t.Elem(), t.Key() m := reflect.MakeMap(t) // See copyTableToSlice. ptr := L.ToPointer(idx) if !luaIsEmpty(L, idx) { visited[ptr] = m.Interface() } L.PushNil() if idx < 0 { idx-- } for L.Next(idx) != 0 { // key at -2, value at -1 key := reflect.ValueOf(luaToGo(L, tk, -2, visited)) val := reflect.ValueOf(luaToGo(L, te, -1, visited)) if val.Interface() == nullv.Interface() { val = reflect.Zero(te) } m.SetMapIndex(key, val) L.Pop(1) } return m.Interface() }
func outputNumbersToStrings(L *lua.State) { L.GetGlobal("output") if !L.IsTable(-1) { L.NewTable() L.SetGlobal("output") } L.GetField(-1, "tags") if L.IsTable(-1) { // First key. L.PushNil() for L.Next(-2) != 0 { // Use 'key' at index -2 and 'value' at index -1. if L.IsString(-2) && L.IsString(-1) { // Convert numbers to strings. L.ToString(-1) L.SetField(-3, L.ToString(-2)) } else { // Remove 'value' and keep 'key' for next iteration. L.Pop(1) } } } L.Pop(1) L.Pop(1) }
func proxyType(L *lua.State) int { v := unwrapProxy(L, 1) if v != nil { GoToLua(L, nil, valueOf(reflect.TypeOf(v)), false) } else { L.PushNil() } return 1 }
func pairsAux(L *lua.State) int { L.CheckType(1, lua.LUA_TTABLE) L.SetTop(2) // Create a 2nd argument if there isn't one. if L.Next(1) != 0 { return 2 } L.PushNil() return 1 }
// Unproxify converts a proxy to an unproxified Lua value. // // Argument: proxy // // Returns: value (Lua value) func Unproxify(L *lua.State) int { if !isValueProxy(L, 1) { L.PushNil() return 1 } v, _ := valueOfProxy(L, 1) GoToLua(L, nil, v, true) return 1 }
// ProxyMethod pushes the proxy method on the stack. // // Argument: proxy // // Returns: method (function) func ProxyMethod(L *lua.State) int { if !isValueProxy(L, 1) { L.PushNil() return 1 } v, _ := valueOfProxy(L, 1) name := L.ToString(2) pushGoMethod(L, name, v) return 1 }
func luaIsEmpty(L *lua.State, idx int) bool { L.PushNil() if idx < 0 { idx-- } if L.Next(idx) != 0 { L.Pop(2) return false } return true }
// ProxyRaw unproxifies a value. // // WARNING: Deprecated, use luar.unproxify instead. func ProxyRaw(L *lua.State) int { v := mustUnwrapProxy(L, 1) val := reflect.ValueOf(v) tp := predeclaredScalarType(val.Type()) if tp != nil { val = val.Convert(tp) GoToLua(L, nil, val, false) } else { L.PushNil() } return 1 }
func copyTableToStruct(L *lua.State, t reflect.Type, idx int, visited map[uintptr]interface{}) interface{} { if t == nil { RaiseError(L, "type argument must be non-nill") } wasPtr := t.Kind() == reflect.Ptr if wasPtr { t = t.Elem() } s := reflect.New(t) // T -> *T ref := s.Elem() // See copyTableToSlice. ptr := L.ToPointer(idx) if !luaIsEmpty(L, idx) { if wasPtr { visited[ptr] = s.Interface() } else { visited[ptr] = s.Elem().Interface() } } // Associate Lua keys with Go fields: tags have priority over matching field // name. fields := map[string]string{} st := ref.Type() for i := 0; i < ref.NumField(); i++ { field := st.Field(i) tag := field.Tag.Get("lua") if tag != "" { fields[tag] = field.Name continue } fields[field.Name] = field.Name } L.PushNil() if idx < 0 { idx-- } for L.Next(idx) != 0 { key := L.ToString(-2) f := ref.FieldByName(fields[key]) if f.CanSet() && f.IsValid() { val := reflect.ValueOf(luaToGo(L, f.Type(), -1, visited)) f.Set(val) } L.Pop(1) } if wasPtr { return s.Interface() } return s.Elem().Interface() }
// ProxyType pushes the proxy type on the stack. // // Argument: proxy // // Returns: type (string) func ProxyType(L *lua.State) int { if !isValueProxy(L, 1) { L.PushNil() return 1 } v, _ := valueOfProxy(L, 1) if v.Interface() == nil { L.PushNil() return 1 } GoToLua(L, nil, reflect.ValueOf(v.Type()), false) return 1 }
func proxyRaw(L *lua.State) int { v := unwrapProxyOrComplain(L, 1) t := reflect.TypeOf(v) tp := isPrimitiveDerived(t, t.Kind()) if tp != nil { val := valueOf(v) val = val.Convert(tp) GoToLua(L, nil, val, false) } else { L.PushNil() } return 1 }
// ProxyPairs implements Lua 5.2 'pairs' functions. // It respects the __pairs metamethod. // // It is only useful for compatibility with Lua 5.1. func ProxyPairs(L *lua.State) int { // See Lua >=5.2 source code. if L.GetMetaField(1, "__pairs") { L.PushValue(1) L.Call(1, 3) return 3 } L.CheckType(1, lua.LUA_TTABLE) L.PushGoFunction(pairsAux) L.PushValue(1) L.PushNil() return 3 }
// copy a Go slice to a Lua table func CopySliceToTable(L *lua.State, vslice reflect.Value) int { if vslice.IsValid() && vslice.Type().Kind() == reflect.Slice { n := vslice.Len() L.CreateTable(n, 0) for i := 0; i < n; i++ { L.PushInteger(int64(i + 1)) GoToLua(L, nil, vslice.Index(i)) L.SetTable(-3) } return 1 } else { L.PushNil() L.PushString("not a slice!") } return 2 }
// copy a Go map to a Lua table func CopyMapToTable(L *lua.State, vmap reflect.Value) int { if vmap.IsValid() && vmap.Type().Kind() == reflect.Map { n := vmap.Len() L.CreateTable(0, n) for _, key := range vmap.MapKeys() { val := vmap.MapIndex(key) GoToLua(L, nil, key) GoToLua(L, nil, val) L.SetTable(-3) } return 1 } else { L.PushNil() L.PushString("not a map!") } return 2 }
func slice__ipairs(L *lua.State) int { s, _ := valueOfProxy(L, 1) n := s.Len() idx := -1 iter := func(L *lua.State) int { idx++ if idx == n { L.PushNil() return 1 } GoToLua(L, nil, valueOf(idx+1), false) // report as 1-based index val := s.Index(idx) GoToLua(L, nil, val, false) return 2 } L.PushGoFunction(iter) return 1 }
func map__pairs(L *lua.State) int { m, _ := valueOfProxy(L, 1) keys := m.MapKeys() idx := -1 n := m.Len() iter := func(L *lua.State) int { idx++ if idx == n { L.PushNil() return 1 } GoToLua(L, nil, keys[idx], false) val := m.MapIndex(keys[idx]) GoToLua(L, nil, val, false) return 2 } L.PushGoFunction(iter) return 1 }
// return the Lua table at 'idx' as a copied Go map. If 't' is nil then the map // type is map[string]interface{} func CopyTableToMap(L *lua.State, t reflect.Type, idx int) interface{} { if t == nil { t = reflect.TypeOf(tmap) } te, tk := t.Elem(), t.Key() m := reflect.MakeMap(t) L.PushNil() if idx < 0 { idx-- } for L.Next(idx) != 0 { // key at -2, value at -1 key := valueOf(LuaToGo(L, tk, -2)) val := valueOf(LuaToGo(L, te, -1)) m.SetMapIndex(key, val) L.Pop(1) } return m.Interface() }
func copyMapToTable(L *lua.State, vmap reflect.Value, visited visitor) int { if vmap.IsValid() && vmap.Type().Kind() == reflect.Map { n := vmap.Len() L.CreateTable(0, n) visited.mark(vmap) for _, key := range vmap.MapKeys() { v := vmap.MapIndex(key) goToLua(L, nil, key, false, visited) if isNil(v) { v = nullv } goToLua(L, nil, v, true, visited) L.SetTable(-3) } return 1 } L.PushNil() L.PushString("not a map!") return 2 }
// Copy matching Lua table entries to a struct, given the struct type // and the index on the Lua stack. func CopyTableToStruct(L *lua.State, t reflect.Type, idx int) interface{} { was_ptr := t.Kind() == reflect.Ptr if was_ptr { t = t.Elem() } s := reflect.New(t) // T -> *T ref := s.Elem() // Associate Lua keys with Go fields: tags have priority over matching field // name. fields := map[string]string{} st := ref.Type() for i := 0; i < ref.NumField(); i++ { field := st.Field(i) tag := field.Tag.Get("lua") if tag != "" { fields[tag] = field.Name continue } fields[field.Name] = field.Name } L.PushNil() if idx < 0 { idx-- } for L.Next(idx) != 0 { key := L.ToString(-2) f := ref.FieldByName(fields[key]) if f.CanSet() && f.IsValid() { val := luaToGoValue(L, f.Type(), -1) f.Set(val) } L.Pop(1) } if was_ptr { return s.Interface() } return s.Elem().Interface() }
// Return the Lua table at 'idx' as a copied Go map. If 't' is nil then the map // type is map[string]interface{} func CopyTableToMap(L *lua.State, t reflect.Type, idx int) interface{} { if t == nil { t = tmap } te, tk := t.Elem(), t.Key() m := reflect.MakeMap(t) L.PushNil() if idx < 0 { idx-- } for L.Next(idx) != 0 { // key at -2, value at -1 key := luaToGoValue(L, tk, -2) val := luaToGoValue(L, te, -1) if val == nullv { val = reflect.Zero(te) } m.SetMapIndex(key, val) L.Pop(1) } return m.Interface() }
// Copy a Go struct to a Lua table. nils in both slices and structs // are represented as luar.null. Defines luar.struct2table // Use tags to set field names. func CopyStructToTable(L *lua.State, vstruct reflect.Value) int { if vstruct.IsValid() && vstruct.Type().Kind() == reflect.Struct { n := vstruct.NumField() L.CreateTable(n, 0) for i := 0; i < n; i++ { st := vstruct.Type() field := st.Field(i) key := field.Name tag := field.Tag.Get("lua") if tag != "" { key = tag } GoToLua(L, nil, reflect.ValueOf(key), true) v := vstruct.Field(i) GoToLua(L, nil, v, true) L.SetTable(-3) } return 1 } else { L.PushNil() L.PushString("not a struct!") } return 2 }
// Push a Go value 'val' of type 't' on the Lua stack. // If we haven't been given a concrete type, use the type of the value // and unbox any interfaces. You can force slices and maps to be copied // over as tables by setting 'dontproxify' to true. func GoToLua(L *lua.State, t reflect.Type, val reflect.Value, dontproxify bool) { if !val.IsValid() { L.PushNil() return } if t == nil { t = val.Type() } if t.Kind() == reflect.Interface && !val.IsNil() { // unbox interfaces! val = valueOf(val.Interface()) t = val.Type() } if t.Kind() == reflect.Ptr { t = t.Elem() } kind := t.Kind() // underlying type is 'primitive' ? wrap it as a proxy! if isPrimitiveDerived(t, kind) != nil { makeValueProxy(L, val, cINTERFACE_META) return } switch kind { case reflect.Float64, reflect.Float32: { L.PushNumber(val.Float()) } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: { L.PushNumber(float64(val.Int())) } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: { L.PushNumber(float64(val.Uint())) } case reflect.String: { L.PushString(val.String()) } case reflect.Bool: { L.PushBoolean(val.Bool()) } case reflect.Slice: { if !dontproxify { makeValueProxy(L, val, cSLICE_META) } else { CopySliceToTable(L, val) } } case reflect.Map: { if !dontproxify { makeValueProxy(L, val, cMAP_META) } else { CopyMapToTable(L, val) } } case reflect.Struct: { if v, ok := val.Interface().(error); ok { L.PushString(v.Error()) } else if v, ok := val.Interface().(*LuaObject); ok { v.Push() } else { if (val.Kind() == reflect.Ptr || val.Kind() == reflect.Interface) && !val.Elem().IsValid() { L.PushNil() return } makeValueProxy(L, val, cSTRUCT_META) } } default: { if v, ok := val.Interface().(error); ok { L.PushString(v.Error()) } else if val.IsNil() { L.PushNil() } else { makeValueProxy(L, val, cINTERFACE_META) } } } }
func pushValue(state *lua.State, value interface{}) error { switch v := value.(type) { default: return fmt.Errorf("An item of unknown type was included in the environment or arguments of a Lua call (skipping it): %v", value) case nil: log.Println("Pushing nil onto lua stack") state.PushNil() case string: log.Println("Pushing string onto lua stack") state.PushString(v) case int: log.Println("Pushing int onto lua stack") state.PushInteger(int64(v)) case int64: log.Println("Pushing int64 onto lua stack") state.PushInteger(v) case float64: log.Println("Pushing float64 onto lua stack") state.PushNumber(v) case bool: log.Println("Pushing bool onto lua stack") state.PushBoolean(v) case map[string]interface{}: log.Println("Pushing map[string]interface{} onto lua stack") state.CreateTable(0, len(v)) for name, value := range v { err := pushValue(state, value) if err != nil { // error means nothing was added to stack. So pop our new table so *we* leave nothing added to the stack. state.Pop(1) return err } state.SetField(-2, name) } // then leave the table on the stack case ThingType: // These are singleton sentinel values, so load them from Lua-land. state.GetGlobal("world") state.GetField(-1, strings.Title(v.String())) state.Remove(-2) case *Thing: log.Println("Pushing *Thing onto lua stack") return pushValue(state, v.Id) case ThingId: log.Println("Pushing ThingId onto lua stack") // We're pushing a ThingId, so make a new userdata for it, with the Thing metatable. userdata := state.NewUserdata(uintptr(unsafe.Sizeof(int64(0)))) thingPtr := (*int64)(userdata) *thingPtr = int64(v) if !state.IsUserdata(-1) { log.Println("!!! HOGAD JUST PUSHED NEW USERDATA BUT IT ISN'T OMG !!!") } log.Println("Pushed ThingId", *thingPtr, "onto lua stack") // Now make it act like a Thing. state.LGetMetaTable(ThingMetaTableName) // ( udata -- udata mtbl ) state.SetMetaTable(-2) // ( udata mtbl -- udata ) // Let's just check that it's that, for sures. if !state.IsUserdata(-1) { log.Println("!!! WOOP WOOP DID NOT SET METATABLE RIGHT :( !!!") } } return nil }
// TODO: Check if we really need multiple pointer levels since pointer methods // can be called on non-pointers. func goToLua(L *lua.State, t reflect.Type, val reflect.Value, dontproxify bool, visited visitor) { if !val.IsValid() { L.PushNil() return } // Unbox interface. if val.Kind() == reflect.Interface && !val.IsNil() { val = reflect.ValueOf(val.Interface()) } // Follow pointers if not proxifying. We save the original pointer Value in case we proxify. ptrVal := val for val.Kind() == reflect.Ptr { val = val.Elem() } if !val.IsValid() { L.PushNil() return } // As a special case, we always proxify nullv, the empty element for slices and maps. if val.CanInterface() && val.Interface() == nullv.Interface() { makeValueProxy(L, val, cInterfaceMeta) return } switch val.Kind() { case reflect.Float64, reflect.Float32: if !dontproxify && predeclaredScalarType(val.Type()) != nil { makeValueProxy(L, ptrVal, cNumberMeta) } else { L.PushNumber(val.Float()) } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: if !dontproxify && predeclaredScalarType(val.Type()) != nil { makeValueProxy(L, ptrVal, cNumberMeta) } else { L.PushNumber(float64(val.Int())) } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: if !dontproxify && predeclaredScalarType(val.Type()) != nil { makeValueProxy(L, ptrVal, cNumberMeta) } else { L.PushNumber(float64(val.Uint())) } case reflect.String: if !dontproxify && predeclaredScalarType(val.Type()) != nil { makeValueProxy(L, ptrVal, cStringMeta) } else { L.PushString(val.String()) } case reflect.Bool: if !dontproxify && predeclaredScalarType(val.Type()) != nil { makeValueProxy(L, ptrVal, cInterfaceMeta) } else { L.PushBoolean(val.Bool()) } case reflect.Complex128, reflect.Complex64: makeValueProxy(L, ptrVal, cComplexMeta) case reflect.Array: // It needs be a pointer to be a proxy, otherwise values won't be settable. if !dontproxify && ptrVal.Kind() == reflect.Ptr { makeValueProxy(L, ptrVal, cSliceMeta) } else { // See the case of struct. if ptrVal.Kind() == reflect.Ptr && visited.push(ptrVal) { return } copySliceToTable(L, ptrVal, visited) } case reflect.Slice: if !dontproxify { makeValueProxy(L, ptrVal, cSliceMeta) } else { if visited.push(val) { return } copySliceToTable(L, val, visited) } case reflect.Map: if !dontproxify { makeValueProxy(L, ptrVal, cMapMeta) } else { if visited.push(val) { return } copyMapToTable(L, val, visited) } case reflect.Struct: if !dontproxify && ptrVal.Kind() == reflect.Ptr { if ptrVal.CanInterface() { switch v := ptrVal.Interface().(type) { case error: L.PushString(v.Error()) case *LuaObject: v.Push() default: makeValueProxy(L, ptrVal, cStructMeta) } } else { makeValueProxy(L, ptrVal, cStructMeta) } } else { // Use ptrVal instead of val to detect cycles from the very first element, if a pointer. if ptrVal.Kind() == reflect.Ptr && visited.push(ptrVal) { return } copyStructToTable(L, ptrVal, visited) } case reflect.Chan: makeValueProxy(L, ptrVal, cChannelMeta) case reflect.Func: L.PushGoFunction(goLuaFunc(L, val)) default: if v, ok := val.Interface().(error); ok { L.PushString(v.Error()) } else if val.IsNil() { L.PushNil() } else { makeValueProxy(L, ptrVal, cInterfaceMeta) } } }
func register(L *lua.State, table string, values Map, convertFun bool) { pop := true if table == "*" { pop = false } else if len(table) > 0 { L.GetGlobal(table) if L.IsNil(-1) { L.NewTable() L.SetGlobal(table) L.GetGlobal(table) } } else { L.GetGlobal("_G") } for name, val := range values { t := reflect.TypeOf(val) if t.Kind() == reflect.Func { if convertFun { L.PushGoFunction(GoLuaFunc(L, val)) } else { lf := val.(func(*lua.State) int) L.PushGoFunction(lf) } } else { GoToLua(L, t, valueOf(val), false) } L.SetField(-2, name) if t.Kind() == reflect.Func { var lf func(*lua.State) int if convertFun { lf = GoLuaFunc(L, val) } else { lf = val.(func(*lua.State) int) } L.PushGoFunction(func(L *lua.State) (ret int) { defer func() { if err2 := recover(); err2 != nil { GoToLua(L, typeof(err2), valueOf(err2), false) ret = 1 return } }() ret = lf(L) pos := L.GetTop() - ret + 1 L.PushNil() L.Insert(pos) for i := 0; i < L.GetTop(); i++ { fmt.Println(L.Typename(int(L.Type(i + 1)))) } return ret + 1 }) L.SetField(-2, "safe_"+name) } } if pop { L.Pop(1) } }