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  }