github.com/dfcfw/lua@v0.0.0-20230325031207-0cc7ffb7b8b9/luar/luar.go (about) 1 package luar 2 3 import ( 4 "fmt" 5 "reflect" 6 7 "github.com/dfcfw/lua" 8 ) 9 10 // New creates and returns a new lua.LValue for the given value. Values are 11 // converted in the following manner: 12 // 13 // A nil value (untyped, or a nil channel, function, map, pointer, or slice) is 14 // converted to lua.LNil. 15 // 16 // A lua.LValue value is returned without conversion. 17 // 18 // Boolean values are converted to lua.LBool. 19 // 20 // String values are converted to lua.LString. 21 // 22 // Real numeric values (ints, uints, and floats) are converted to lua.LNumber. 23 // 24 // Functions are converted to *lua.LFunction. When called from Lua, Lua values 25 // are converted to Go using the rules described in the package documentation, 26 // and Go return values converted to Lua values using the rules described by 27 // New. 28 // 29 // If a function has the signature: 30 // 31 // func(*LState) int // *LState defined in this package, not in lua 32 // 33 // The argument and return value conversions described above are skipped, and 34 // the function is called with the arguments passed on the Lua stack. Return 35 // values are pushed to the stack and the number of return values is returned 36 // from the function. 37 // 38 // Arrays, channels, maps, pointers, slices, and structs are all converted to 39 // *lua.LUserData with its Value field set to value. The userdata's metatable 40 // is set to a table generated for value's type. The type's method set is 41 // callable from the Lua type. If the type implements the fmt.Stringer 42 // interface, that method will be used when the value is passed to the Lua 43 // tostring function. 44 // 45 // With arrays, the # operator returns the array's length. Array elements can 46 // be accessed with the index operator (array[index]). Calling an array 47 // (array()) returns an iterator over the array that can be used in a for loop. 48 // Two arrays of the same type can be compared for equality. Additionally, a 49 // pointer to an array allows the array elements to be modified 50 // (array[index] = value). 51 // 52 // With channels, the # operator returns the number of elements buffered in the 53 // channel. Two channels of the same type can be compared for equality (i.e. if 54 // they were created with the same make call). Calling a channel value with 55 // no arguments reads one element from the channel, returning the value and a 56 // boolean indicating if the channel is closed. Calling a channel value with 57 // one argument sends the argument to the channel. The channel's unary minus 58 // operator closes the channel (_ = -channel). 59 // 60 // With maps, the # operator returns the number of elements in the map. Map 61 // elements can be accessed using the index operator (map[key]) and also set 62 // (map[key] = value). Calling a map value returns an iterator over the map that 63 // can be used in a for loop. If a map's key type is string, map values take 64 // priority over methods. 65 // 66 // With slices, the # operator returns the length of the slice. Slice elements 67 // can be accessed using the index operator (slice[index]) and also set 68 // (slice[index] = value). Calling a slice returns an iterator over its elements 69 // that can be used in a for loop. Elements can be appended to a slice using the 70 // add operator (new_slice = slice + element). 71 // 72 // With structs, fields can be accessed using the index operator 73 // (struct[field]). As a special case, accessing field that is an array or 74 // struct field will return a pointer to that value. Structs of the same type 75 // can be tested for equality. Additionally, a pointer to a struct can have its 76 // fields set (struct[field] = value). 77 // 78 // Struct field accessibility can be changed by setting the field's luar tag. 79 // If the tag is empty (default), the field is accessed by its name and its 80 // name with a lowercase first letter (e.g. "Field1" would be accessible using 81 // "Field1" or "field1"). If the tag is "-", the field will not be accessible. 82 // Any other tag value makes the field accessible through that name. 83 // 84 // Pointer values can be compared for equality. The pointed to value can be 85 // changed using the pow operator (pointer = pointer ^ value). A pointer can be 86 // dereferenced using the unary minus operator (value = -pointer). 87 // 88 // All other values (complex numbers, unsafepointer, uintptr) are converted to 89 // *lua.LUserData with its Value field set to value and no custom metatable. 90 func New(L *lua.LState, value interface{}) lua.LValue { 91 if value == nil { 92 return lua.LNil 93 } 94 if lval, ok := value.(lua.LValue); ok { 95 return lval 96 } 97 98 switch val := reflect.ValueOf(value); val.Kind() { 99 case reflect.Bool: 100 return lua.LBool(val.Bool()) 101 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 102 return lua.LNumber(float64(val.Int())) 103 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 104 return lua.LNumber(float64(val.Uint())) 105 case reflect.Float32, reflect.Float64: 106 return lua.LNumber(val.Float()) 107 case reflect.Chan, reflect.Map, reflect.Ptr, reflect.Slice: 108 if val.IsNil() { 109 return lua.LNil 110 } 111 fallthrough 112 case reflect.Array, reflect.Struct: 113 ud := L.NewUserData() 114 ud.Value = val.Interface() 115 ud.Metatable = getMetatable(L, val.Type()) 116 return ud 117 case reflect.Func: 118 if val.IsNil() { 119 return lua.LNil 120 } 121 return funcWrapper(L, val, false) 122 case reflect.String: 123 return lua.LString(val.String()) 124 default: 125 ud := L.NewUserData() 126 ud.Value = val.Interface() 127 return ud 128 } 129 } 130 131 // NewType returns a new type generator for the given value's type. 132 // 133 // When the returned lua.LValue is called, a new value will be created that is 134 // dependent on value's type: 135 // 136 // If value is a channel, the first argument optionally specifies the channel's 137 // buffer size (defaults to 1). The new channel is returned. 138 // 139 // If value is a map, a new map is returned. 140 // 141 // If value is a slice, the first argument optionally specifies the slices's 142 // length (defaults to 0), and the second argument optionally specifies the 143 // slice's capacity (defaults to the first argument). The new slice is returned. 144 // 145 // All other types return a new pointer to the zero value of value's type. 146 func NewType(L *lua.LState, value interface{}) lua.LValue { 147 val := reflect.TypeOf(value) 148 ud := L.NewUserData() 149 ud.Value = val 150 ud.Metatable = getTypeMetatable(L, val) 151 152 return ud 153 } 154 155 type conversionError struct { 156 Lua lua.LValue 157 Hint reflect.Type 158 } 159 160 func (c conversionError) Error() string { 161 if _, isNil := c.Lua.(*lua.LNilType); isNil { 162 return fmt.Sprintf("cannot use nil as type %s", c.Hint) 163 } 164 165 var val interface{} 166 167 if userData, ok := c.Lua.(*lua.LUserData); ok { 168 val = userData.Value 169 } else { 170 val = c.Lua 171 } 172 173 return fmt.Sprintf("cannot use %v (type %T) as type %s", val, val, c.Hint) 174 } 175 176 type structFieldError struct { 177 Field string 178 Type reflect.Type 179 } 180 181 func (s structFieldError) Error() string { 182 return `type ` + s.Type.String() + ` has no field ` + s.Field 183 } 184 185 func lValueToReflect(L *lua.LState, v lua.LValue, hint reflect.Type, tryConvertPtr *bool) (reflect.Value, error) { 186 visited := make(map[*lua.LTable]reflect.Value) 187 return lValueToReflectInner(L, v, hint, visited, tryConvertPtr) 188 } 189 190 func lValueToReflectInner(L *lua.LState, v lua.LValue, hint reflect.Type, visited map[*lua.LTable]reflect.Value, tryConvertPtr *bool) (reflect.Value, error) { 191 if hint.Implements(refTypeLuaLValue) { 192 return reflect.ValueOf(v), nil 193 } 194 195 isPtr := false 196 197 switch converted := v.(type) { 198 case lua.LBool: 199 val := reflect.ValueOf(bool(converted)) 200 if !val.Type().ConvertibleTo(hint) { 201 return reflect.Value{}, conversionError{ 202 Lua: v, 203 Hint: hint, 204 } 205 } 206 return val.Convert(hint), nil 207 case lua.LChannel: 208 val := reflect.ValueOf(converted) 209 if !val.Type().ConvertibleTo(hint) { 210 return reflect.Value{}, conversionError{ 211 Lua: v, 212 Hint: hint, 213 } 214 } 215 return val.Convert(hint), nil 216 case lua.LNumber: 217 val := reflect.ValueOf(float64(converted)) 218 if !val.Type().ConvertibleTo(hint) { 219 return reflect.Value{}, conversionError{ 220 Lua: v, 221 Hint: hint, 222 } 223 } 224 return val.Convert(hint), nil 225 case *lua.LFunction: 226 emptyIfaceHint := false 227 switch { 228 case hint == refTypeEmptyIface: 229 emptyIfaceHint = true 230 inOut := []reflect.Type{ 231 reflect.SliceOf(refTypeEmptyIface), 232 } 233 hint = reflect.FuncOf(inOut, inOut, true) 234 case hint.Kind() != reflect.Func: 235 return reflect.Value{}, conversionError{ 236 Lua: v, 237 Hint: hint, 238 } 239 } 240 241 fn := func(args []reflect.Value) []reflect.Value { 242 thread, cancelFunc := L.NewThread() 243 defer thread.Close() 244 if cancelFunc != nil { 245 defer cancelFunc() 246 } 247 thread.Push(converted) 248 defer thread.SetTop(0) 249 250 argCount := 0 251 for i, arg := range args { 252 if i+1 == len(args) && hint.IsVariadic() { 253 // arg is a varadic slice 254 for j := 0; j < arg.Len(); j++ { 255 arg := arg.Index(j) 256 thread.Push(New(thread, arg.Interface())) 257 argCount++ 258 } 259 break 260 } 261 262 thread.Push(New(thread, arg.Interface())) 263 argCount++ 264 } 265 266 thread.Call(argCount, lua.MultRet) 267 top := thread.GetTop() 268 269 switch { 270 case emptyIfaceHint: 271 ret := reflect.MakeSlice(reflect.SliceOf(refTypeEmptyIface), top, top) 272 273 for i := 1; i <= top; i++ { 274 item, err := lValueToReflect(thread, thread.Get(i), refTypeEmptyIface, nil) 275 if err != nil { 276 panic(err) 277 } 278 ret.Index(i - 1).Set(item) 279 } 280 281 return []reflect.Value{ret} 282 283 case top == hint.NumOut(): 284 ret := make([]reflect.Value, top) 285 286 var err error 287 for i := 1; i <= top; i++ { 288 outHint := hint.Out(i - 1) 289 item := thread.Get(i) 290 ret[i-1], err = lValueToReflect(thread, item, outHint, nil) 291 if err != nil { 292 panic(err) 293 } 294 } 295 296 return ret 297 } 298 299 panic(fmt.Errorf("expecting %d return values, got %d", hint.NumOut(), top)) 300 } 301 return reflect.MakeFunc(hint, fn), nil 302 case *lua.LNilType: 303 switch hint.Kind() { 304 case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer, reflect.Uintptr: 305 return reflect.Zero(hint), nil 306 } 307 308 return reflect.Value{}, conversionError{ 309 Lua: v, 310 Hint: hint, 311 } 312 313 case *lua.LState: 314 val := reflect.ValueOf(converted) 315 if !val.Type().ConvertibleTo(hint) { 316 return reflect.Value{}, conversionError{ 317 Lua: v, 318 Hint: hint, 319 } 320 } 321 return val.Convert(hint), nil 322 323 case lua.LString: 324 val := reflect.ValueOf(string(converted)) 325 if !val.Type().ConvertibleTo(hint) { 326 return reflect.Value{}, conversionError{ 327 Lua: v, 328 Hint: hint, 329 } 330 } 331 return val.Convert(hint), nil 332 333 case *lua.LTable: 334 if existing := visited[converted]; existing.IsValid() { 335 return existing, nil 336 } 337 338 if hint == refTypeEmptyIface { 339 hint = reflect.MapOf(refTypeEmptyIface, refTypeEmptyIface) 340 } 341 342 switch { 343 case hint.Kind() == reflect.Array: 344 elemType := hint.Elem() 345 length := converted.Len() 346 if length != hint.Len() { 347 return reflect.Value{}, conversionError{ 348 Lua: v, 349 Hint: hint, 350 } 351 } 352 s := reflect.New(hint).Elem() 353 visited[converted] = s 354 355 for i := 0; i < length; i++ { 356 value := converted.RawGetInt(i + 1) 357 elemValue, err := lValueToReflectInner(L, value, elemType, visited, nil) 358 if err != nil { 359 return reflect.Value{}, err 360 } 361 s.Index(i).Set(elemValue) 362 } 363 364 return s, nil 365 366 case hint.Kind() == reflect.Slice: 367 elemType := hint.Elem() 368 length := converted.Len() 369 s := reflect.MakeSlice(hint, length, length) 370 visited[converted] = s 371 372 for i := 0; i < length; i++ { 373 value := converted.RawGetInt(i + 1) 374 elemValue, err := lValueToReflectInner(L, value, elemType, visited, nil) 375 if err != nil { 376 return reflect.Value{}, err 377 } 378 s.Index(i).Set(elemValue) 379 } 380 381 return s, nil 382 383 case hint.Kind() == reflect.Map: 384 keyType := hint.Key() 385 elemType := hint.Elem() 386 s := reflect.MakeMap(hint) 387 visited[converted] = s 388 389 for key := lua.LNil; ; { 390 var value lua.LValue 391 key, value = converted.Next(key) 392 if key == lua.LNil { 393 break 394 } 395 396 lKey, err := lValueToReflectInner(L, key, keyType, visited, nil) 397 if err != nil { 398 return reflect.Value{}, err 399 } 400 lValue, err := lValueToReflectInner(L, value, elemType, visited, nil) 401 if err != nil { 402 return reflect.Value{}, err 403 } 404 s.SetMapIndex(lKey, lValue) 405 } 406 407 return s, nil 408 409 case hint.Kind() == reflect.Ptr && hint.Elem().Kind() == reflect.Struct: 410 hint = hint.Elem() 411 isPtr = true 412 fallthrough 413 case hint.Kind() == reflect.Struct: 414 s := reflect.New(hint) 415 visited[converted] = s 416 417 t := s.Elem() 418 419 mt := &Metatable{ 420 LTable: getMetatable(L, hint), 421 } 422 423 for key := lua.LNil; ; { 424 var value lua.LValue 425 key, value = converted.Next(key) 426 if key == lua.LNil { 427 break 428 } 429 if _, ok := key.(lua.LString); !ok { 430 continue 431 } 432 433 fieldName := key.String() 434 index := mt.fieldIndex(fieldName) 435 if index == nil { 436 return reflect.Value{}, structFieldError{ 437 Type: hint, 438 Field: fieldName, 439 } 440 } 441 field := hint.FieldByIndex(index) 442 443 lValue, err := lValueToReflectInner(L, value, field.Type, visited, nil) 444 if err != nil { 445 return reflect.Value{}, nil 446 } 447 t.FieldByIndex(field.Index).Set(lValue) 448 } 449 450 if isPtr { 451 return s, nil 452 } 453 454 return t, nil 455 } 456 457 return reflect.Value{}, conversionError{ 458 Lua: v, 459 Hint: hint, 460 } 461 462 case *lua.LUserData: 463 val := reflect.ValueOf(converted.Value) 464 if tryConvertPtr != nil && val.Kind() != reflect.Ptr && hint.Kind() == reflect.Ptr && val.Type() == hint.Elem() { 465 newVal := reflect.New(hint.Elem()) 466 newVal.Elem().Set(val) 467 val = newVal 468 *tryConvertPtr = true 469 } else { 470 if !val.Type().ConvertibleTo(hint) { 471 return reflect.Value{}, conversionError{ 472 Lua: converted, 473 Hint: hint, 474 } 475 } 476 val = val.Convert(hint) 477 if tryConvertPtr != nil { 478 *tryConvertPtr = false 479 } 480 } 481 return val, nil 482 } 483 484 panic("never reaches") 485 }