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) } }
// ComplexImag defines 'luar.imag' when 'Init' is called. // It is the equivalent of Go's 'imag' function. // // WARNING: Deprecated, use the 'imag' index instead. func ComplexImag(L *lua.State) int { v := mustUnwrapProxy(L, 1) val := reflect.ValueOf(v) if unsizedKind(val) != reflect.Complex128 { RaiseError(L, "not a complex") } L.PushNumber(imag(val.Complex())) return 1 }
// GoLuaFunc converts an arbitrary Go function into a Lua-compatible GoFunction. // There are special optimized cases for functions that go from strings to strings, // and doubles to doubles, but otherwise Go // reflection is used to provide a generic wrapper function func GoLuaFunc(L *lua.State, fun interface{}) lua.LuaGoFunction { switch f := fun.(type) { case func(*lua.State) int: return f case func(string) string: return func(L *lua.State) int { L.PushString(f(L.ToString(1))) return 1 } case func(float64) float64: return func(L *lua.State) int { L.PushNumber(f(L.ToNumber(1))) return 1 } default: } var funv reflect.Value switch ff := fun.(type) { case reflect.Value: funv = ff default: funv = valueOf(fun) } funt := funv.Type() targs, tout := functionArgRetTypes(funt) return func(L *lua.State) int { var lastT reflect.Type orig_targs := targs isVariadic := funt.IsVariadic() if isVariadic { n := len(targs) lastT = targs[n-1].Elem() targs = targs[0 : n-1] } args := make([]reflect.Value, len(targs)) for i, t := range targs { val := LuaToGo(L, t, i+1) args[i] = valueOfNil(val) //println(i,args[i].String()) } if isVariadic { n := L.GetTop() for i := len(targs) + 1; i <= n; i++ { ival := LuaToGo(L, lastT, i) args = append(args, valueOfNil(ival)) } targs = orig_targs } resv := callGo(L, funv, args) for i, val := range resv { GoToLua(L, tout[i], val, false) } return len(resv) } }
// 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) } } } }
// 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 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 }
func (p *Plugin) apiAudioVolume(l *lua.State) int { l.PushNumber(float64(p.instance.Audio.Volume)) return 1 }
// 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. func GoToLua(L *lua.State, t reflect.Type, val reflect.Value) { proxify := true if t == nil { t = val.Type() if t.Kind() == reflect.Interface { // unbox interfaces! val = valueOf(val.Interface()) t = val.Type() } proxify = false } if t.Kind() == reflect.Ptr { t = t.Elem() } switch t.Kind() { case reflect.Float64: case reflect.Float32: { L.PushNumber(val.Float()) } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32: { L.PushNumber(float64(val.Int())) } case reflect.Uint, reflect.Uint8: { L.PushNumber(float64(val.Uint())) } case reflect.String: { L.PushString(val.String()) } case reflect.Bool: { L.PushBoolean(val.Bool()) } case reflect.Slice: { if proxify { makeValueProxy(L, val, SLICE_META) } else { CopySliceToTable(L, val) } } case reflect.Map: { if proxify { makeValueProxy(L, val, MAP_META) } else { CopyMapToTable(L, val) } } case reflect.Struct: { makeValueProxy(L, val, STRUCT_META) } default: { if val.IsNil() { L.PushNil() } else { makeValueProxy(L, val, INTERFACE_META) } } } }