github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/runtime/value.go (about)

     1  //go:build !noscalar
     2  // +build !noscalar
     3  
     4  package runtime
     5  
     6  import (
     7  	"fmt"
     8  	"math"
     9  	"strconv"
    10  	"unsafe"
    11  )
    12  
    13  // A Value is a runtime value.
    14  type Value struct {
    15  	scalar uint64
    16  	iface  interface{}
    17  }
    18  
    19  // In order to minimize memory allocations, the values of scalars Valuess are
    20  // not stored in the iface field, but their type is, using a single interface{}
    21  // value for each scalar type.
    22  var (
    23  	dummyInt64   interface{} = int64(0)   // All IntValue instances share this iface
    24  	dummyFloat64 interface{} = float64(0) // All FloatValue instances share this iface
    25  	dummyBool    interface{} = false      // All BoolVale instances share this iface
    26  )
    27  
    28  // AsValue returns a Value for the passed interface.  Use carefully, as it may
    29  // trigger allocations (e.g. AsValue(1) will allocate as the interface holding 1
    30  // will put 1 on the heap).
    31  func AsValue(i interface{}) Value {
    32  	if i == nil {
    33  		return NilValue
    34  	}
    35  	switch x := i.(type) {
    36  	case int64:
    37  		return IntValue(x)
    38  	case int:
    39  		return IntValue(int64(x))
    40  	case float64:
    41  		return FloatValue(x)
    42  	case float32:
    43  		return FloatValue(float64(x))
    44  	case bool:
    45  		return BoolValue(x)
    46  	case string:
    47  		return StringValue(x)
    48  	case Value:
    49  		return x
    50  	default:
    51  		return Value{iface: i}
    52  	}
    53  }
    54  
    55  // Interface turns the Value into an interface.  As AsValue, this can trigger
    56  // allocations so use with caution.
    57  func (v Value) Interface() interface{} {
    58  	if v.iface == nil {
    59  		return nil
    60  	}
    61  	switch v.iface.(type) {
    62  	case int64:
    63  		return v.AsInt()
    64  	case float64:
    65  		return v.AsFloat()
    66  	case bool:
    67  		return v.AsBool()
    68  	default:
    69  		return v.iface
    70  	}
    71  }
    72  
    73  // Some unsafe function to determine quickly if two interfaces have the same
    74  // type.
    75  func ifaceType(iface interface{}) uintptr {
    76  	return *(*uintptr)(unsafe.Pointer(&iface))
    77  }
    78  
    79  func ifacePtr(iface interface{}) uintptr {
    80  	return (*[2]uintptr)(unsafe.Pointer(&iface))[1]
    81  }
    82  
    83  var float64IfaceType = ifaceType(float64(1))
    84  
    85  // Equals returns true if v is equal to v2.  Provided that v and v2 have been
    86  // built with the official constructor functions, it is equivalent to but
    87  // slightly faster than '=='.
    88  func (v Value) Equals(v2 Value) bool {
    89  	ift := ifaceType(v.iface)
    90  	if ift != ifaceType(v2.iface) {
    91  		return false
    92  	}
    93  	if ift == float64IfaceType {
    94  		// Special case for floats, as some non bitwise equal floats can be equal and vice-versa
    95  		// NaN != NaN
    96  		// -0 == +0
    97  		return v.AsFloat() == v2.AsFloat()
    98  	}
    99  	if v.scalar != v2.scalar {
   100  		return false
   101  	}
   102  	switch x := v.iface.(type) {
   103  	case int64, float64:
   104  		return true
   105  	case string:
   106  		// Short strings are equal if their scalar are
   107  		if v.scalar != 0 {
   108  			return true
   109  		}
   110  	case *Closure:
   111  		return x.Equals(v2.iface.(*Closure))
   112  	}
   113  	return v.iface == v2.iface
   114  }
   115  
   116  //go:linkname goRuntimeInt64Hash runtime.int64Hash
   117  //go:noescape
   118  func goRuntimeInt64Hash(i uint64, seed uintptr) uintptr
   119  
   120  //go:linkname goRuntimeEfaceHash runtime.efaceHash
   121  //go:noescape
   122  func goRuntimeEfaceHash(i interface{}, seed uintptr) uintptr
   123  
   124  // Hash returns a hash for the value.
   125  func (v Value) Hash() uintptr {
   126  	if v.scalar != 0 {
   127  		return goRuntimeInt64Hash(v.scalar, 0)
   128  	}
   129  	return goRuntimeEfaceHash(v.iface, 0)
   130  }
   131  
   132  // IntValue returns a Value holding the given arg.
   133  func IntValue(n int64) Value {
   134  	return Value{uint64(n), dummyInt64}
   135  }
   136  
   137  // FloatValue returns a Value holding the given arg.
   138  func FloatValue(f float64) Value {
   139  	return Value{*(*uint64)(unsafe.Pointer(&f)), dummyFloat64}
   140  }
   141  
   142  // BoolValue returns a Value holding the given arg.
   143  func BoolValue(b bool) Value {
   144  	var s uint64
   145  	if b {
   146  		s = 1
   147  	}
   148  	return Value{s, dummyBool}
   149  }
   150  
   151  // StringValue returns a Value holding the given arg.
   152  func StringValue(s string) (v Value) {
   153  	v.iface = s
   154  	ls := len(s)
   155  	if ls <= 7 {
   156  		// Put a scalar value for short strings.  This speeds up hashing
   157  		// (because it uses the scalar value) and equality tests for unequal
   158  		// strings.
   159  		bs := make([]byte, 8)
   160  		copy(bs, s)
   161  		bs[7] = byte(ls)
   162  		v.scalar = *(*uint64)(unsafe.Pointer(&bs[0]))
   163  	}
   164  	return
   165  }
   166  
   167  // TableValue returns a Value holding the given arg.
   168  func TableValue(t *Table) Value {
   169  	return Value{iface: t}
   170  }
   171  
   172  // FunctionValue returns a Value holding the given arg.
   173  func FunctionValue(c Callable) Value {
   174  	return Value{iface: c}
   175  }
   176  
   177  // ContValue returns a Value holding the given arg.
   178  func ContValue(c Cont) Value {
   179  	return Value{iface: c}
   180  }
   181  
   182  // ArrayValue returns a Value holding the given arg.
   183  func ArrayValue(a []Value) Value {
   184  	return Value{iface: a}
   185  }
   186  
   187  // CodeValue returns a Value holding the given arg.
   188  func CodeValue(c *Code) Value {
   189  	return Value{iface: c}
   190  }
   191  
   192  // ThreadValue returns a Value holding the given arg.
   193  func ThreadValue(t *Thread) Value {
   194  	return Value{iface: t}
   195  }
   196  
   197  // LightUserDataValue returns a Value holding the given arg.
   198  func LightUserDataValue(d LightUserData) Value {
   199  	return Value{iface: d}
   200  }
   201  
   202  // UserDataValue returns a Value holding the given arg.
   203  func UserDataValue(u *UserData) Value {
   204  	return Value{iface: u}
   205  }
   206  
   207  // NilValue is a value holding Nil.
   208  var NilValue = Value{}
   209  
   210  // Type returns the ValueType of v.
   211  func (v Value) Type() ValueType {
   212  	if v.iface == nil {
   213  		return NilType
   214  	}
   215  	switch v.iface.(type) {
   216  	case int64:
   217  		return IntType
   218  	case float64:
   219  		return FloatType
   220  	case bool:
   221  		return BoolType
   222  	case string:
   223  		return StringType
   224  	case *Table:
   225  		return TableType
   226  	case *Code:
   227  		return CodeType
   228  	case *GoFunction, *Closure:
   229  		return FunctionType
   230  	case *Thread:
   231  		return ThreadType
   232  	case *UserData:
   233  		return UserDataType
   234  	default:
   235  		return UnknownType
   236  	}
   237  }
   238  
   239  // TypeName returns a string representing the type of the value (as in the Lua
   240  // function type(v))
   241  func (v Value) TypeName() string {
   242  	if v.iface == nil {
   243  		return "nil"
   244  	}
   245  	switch v.iface.(type) {
   246  	case int64, float64:
   247  		return "number"
   248  	case bool:
   249  		return "boolean"
   250  	case string:
   251  		return "string"
   252  	case *Table:
   253  		return "table"
   254  		// return getName("table", v.iface.(*Table).Metatable())
   255  	case *Code:
   256  		return "code"
   257  	case *GoFunction, *Closure:
   258  		return "function"
   259  	case *Thread:
   260  		return "thread"
   261  	case *UserData:
   262  		return "userdata"
   263  		// return getName("userdata", v.iface.(*UserData).Metatable())
   264  	default:
   265  		return "<unknown type>"
   266  	}
   267  }
   268  
   269  // CustomTypeName is like TypeName but can be changed if the metatable has a
   270  // __name field.
   271  func (v Value) CustomTypeName() string {
   272  	if v.iface == nil {
   273  		return "nil"
   274  	}
   275  	switch v.iface.(type) {
   276  	case int64, float64:
   277  		return "number"
   278  	case bool:
   279  		return "boolean"
   280  	case string:
   281  		return "string"
   282  	case *Table:
   283  		return getName("table", v.iface.(*Table).Metatable())
   284  	case *Code:
   285  		return "code"
   286  	case *GoFunction, *Closure:
   287  		return "function"
   288  	case *Thread:
   289  		return "thread"
   290  	case *UserData:
   291  		return getName("userdata", v.iface.(*UserData).Metatable())
   292  	default:
   293  		return "<unknown type>"
   294  	}
   295  }
   296  
   297  // ToString returns a simple string representation of a Value.  The boolean
   298  // returned specifies whether this is a good string value or not ("good" should
   299  // be defined).
   300  func (v Value) ToString() (string, bool) {
   301  	if v.iface == nil {
   302  		return "nil", false
   303  	}
   304  	switch x := v.iface.(type) {
   305  	case int64:
   306  		return strconv.FormatInt(v.AsInt(), 10), true
   307  	case float64:
   308  		return strconv.FormatFloat(v.AsFloat(), 'g', -1, 64), true
   309  	case bool:
   310  		return strconv.FormatBool(v.AsBool()), false
   311  	case string:
   312  		return v.AsString(), true
   313  	case *Table:
   314  		return fmt.Sprintf("%s: %p", getName("table", x.Metatable()), x), false
   315  	case *Code:
   316  		return fmt.Sprintf("code: %p", x), false
   317  	case *GoFunction:
   318  		return fmt.Sprintf("gofunction: %s", x.name), false
   319  	case *Closure:
   320  		return fmt.Sprintf("function: %p", x), false
   321  	case *Thread:
   322  		return fmt.Sprintf("thread: %p", x), false
   323  	case *UserData:
   324  		return fmt.Sprintf("%s: %p", getName("userdata", x.Metatable()), x), false
   325  	default:
   326  		return "<unknown>", false
   327  	}
   328  }
   329  
   330  func getName(defaultName string, meta *Table) string {
   331  	if v := RawGet(meta, StringValue("__name")); !v.IsNil() {
   332  		s, ok := v.ToString()
   333  		if ok {
   334  			return s
   335  		}
   336  	}
   337  	return defaultName
   338  }
   339  
   340  // NumberType return the ValueType of v if it is a number, otherwise
   341  // UnknownType.
   342  func (v Value) NumberType() ValueType {
   343  	switch v.iface.(type) {
   344  	case int64:
   345  		return IntType
   346  	case float64:
   347  		return FloatType
   348  	}
   349  	return UnknownType
   350  }
   351  
   352  // AsInt returns v as a int64 (or panics).
   353  func (v Value) AsInt() int64 {
   354  	return int64(v.scalar)
   355  }
   356  
   357  // AsFloat returns v as a float64 (or panics).
   358  func (v Value) AsFloat() float64 {
   359  	return *(*float64)(unsafe.Pointer(&v.scalar))
   360  }
   361  
   362  // AsBool returns v as a bool (or panics).
   363  func (v Value) AsBool() bool {
   364  	return v.scalar != 0
   365  }
   366  
   367  // AsString returns v as a string (or panics).
   368  func (v Value) AsString() string {
   369  	return v.iface.(string)
   370  }
   371  
   372  // AsTable returns v as a *Table (or panics).
   373  func (v Value) AsTable() *Table {
   374  	return v.iface.(*Table)
   375  }
   376  
   377  // AsCont returns v as a Cont, by looking at the concrete type (or panics).  It
   378  // is an optimisation as type assertion in Go seems to have a significant cost.
   379  func (v Value) AsCont() Cont {
   380  	switch cont := v.iface.(type) {
   381  	case *GoCont:
   382  		return cont
   383  	case *LuaCont:
   384  		return cont
   385  	case *Termination:
   386  		return cont
   387  	case *messageHandlerCont:
   388  		return cont
   389  	default:
   390  		panic("value is not a continuation")
   391  	}
   392  }
   393  
   394  // AsArray returns v as a [] (or panics).
   395  func (v Value) AsArray() []Value {
   396  	return v.iface.([]Value)
   397  }
   398  
   399  // AsClosure returns v as a *Closure (or panics).
   400  func (v Value) AsClosure() *Closure {
   401  	return v.iface.(*Closure)
   402  }
   403  
   404  // AsCode returns v as a *Code (or panics).
   405  func (v Value) AsCode() *Code {
   406  	return v.iface.(*Code)
   407  }
   408  
   409  // AsUserData returns v as a *UserData (or panics).
   410  func (v Value) AsUserData() *UserData {
   411  	return v.iface.(*UserData)
   412  }
   413  
   414  // AsCallable returns v as a Callable if possible (or panics)).  It is an
   415  // optimisation as type assertion in Go seems to have a significant cost.
   416  func (v Value) AsCallable() Callable {
   417  	switch c := v.iface.(type) {
   418  	case *Closure:
   419  		return c
   420  	case *GoFunction:
   421  		return c
   422  	default:
   423  		panic("value is not a Callable")
   424  	}
   425  }
   426  
   427  // AsThread returns v as a *Thread (or panics).
   428  func (v Value) AsThread() *Thread {
   429  	return v.iface.(*Thread)
   430  }
   431  
   432  // TryInt converts v to type int64 if possible (ok is false otherwise).
   433  func (v Value) TryInt() (n int64, ok bool) {
   434  	_, ok = v.iface.(int64)
   435  	if ok {
   436  		n = v.AsInt()
   437  	}
   438  	return
   439  }
   440  
   441  // TryFloat converts v to type float64 if possible (ok is false otherwise).
   442  func (v Value) TryFloat() (n float64, ok bool) {
   443  	_, ok = v.iface.(float64)
   444  	if ok {
   445  		n = v.AsFloat()
   446  	}
   447  	return
   448  }
   449  
   450  // TryString converts v to type string if possible (ok is false otherwise).
   451  func (v Value) TryString() (s string, ok bool) {
   452  	s, ok = v.iface.(string)
   453  	return
   454  }
   455  
   456  // TryCallable converts v to type Callable if possible by looking at the
   457  // possible concrete types (ok is false otherwise).  It is an optimisation as
   458  // type assertion in Go seems to have a significant cost.
   459  func (v Value) TryCallable() (c Callable, ok bool) {
   460  	switch c := v.iface.(type) {
   461  	case *Closure:
   462  		return c, true
   463  	case *GoFunction:
   464  		return c, true
   465  	default:
   466  		return nil, false
   467  	}
   468  }
   469  
   470  // TryClosure converts v to type *Closure if possible (ok is false otherwise).
   471  func (v Value) TryClosure() (c *Closure, ok bool) {
   472  	c, ok = v.iface.(*Closure)
   473  	return
   474  }
   475  
   476  // TryThread converts v to type *Thread if possible (ok is false otherwise).
   477  func (v Value) TryThread() (t *Thread, ok bool) {
   478  	t, ok = v.iface.(*Thread)
   479  	return
   480  }
   481  
   482  // TryTable converts v to type *Table if possible (ok is false otherwise).
   483  func (v Value) TryTable() (t *Table, ok bool) {
   484  	t, ok = v.iface.(*Table)
   485  	return
   486  }
   487  
   488  // TryUserData converts v to type *UserData if possible (ok is false otherwise).
   489  func (v Value) TryUserData() (u *UserData, ok bool) {
   490  	u, ok = v.iface.(*UserData)
   491  	return
   492  }
   493  
   494  // TryBool converts v to type bool if possible (ok is false otherwise).
   495  func (v Value) TryBool() (b bool, ok bool) {
   496  	_, ok = v.iface.(bool)
   497  	if ok {
   498  		b = v.scalar != 0
   499  	}
   500  	return
   501  }
   502  
   503  // TryCont returns v as a Cont, by looking at the concrete type (ok is false if
   504  // it doesn't implement the Cont interface).  It is an optimisation as type
   505  // assertion in Go seems to have a significant cost.
   506  func (v Value) TryCont() (c Cont, ok bool) {
   507  	switch cont := v.iface.(type) {
   508  	case *LuaCont:
   509  		return cont, true
   510  	case *Termination:
   511  		return cont, true
   512  	// These cases come after because this function is only used in
   513  	// LuaCont.Next() and in that context it is unlikely that the next
   514  	// continuation will be a *GoCont, and probably impossible that it is a
   515  	// *MessageHandlerCont.
   516  	case *GoCont:
   517  		return cont, true
   518  	case *messageHandlerCont:
   519  		return cont, true
   520  	default:
   521  		return nil, false
   522  	}
   523  }
   524  
   525  // TryCode converts v to type *Code if possible (ok is false otherwise).
   526  func (v Value) TryCode() (c *Code, ok bool) {
   527  	c, ok = v.iface.(*Code)
   528  	return
   529  }
   530  
   531  // IsNil returns true if v is nil.
   532  func (v Value) IsNil() bool {
   533  	return v.iface == nil
   534  }
   535  
   536  // IsNaN returns true if v is a FloatValue which has a NaN value.  It is trying
   537  // to do this as efficiently as possible.
   538  func (v Value) IsNaN() bool {
   539  	if ifaceType(v.iface) != float64IfaceType {
   540  		return false
   541  	}
   542  	x := v.AsFloat()
   543  	return math.IsNaN(x)
   544  }