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 }