github.com/gopherjs/gopherjs@v1.19.0-beta1.0.20240506212314-27071a8796e4/compiler/natives/src/internal/reflectlite/value.go (about) 1 //go:build js 2 // +build js 3 4 package reflectlite 5 6 import ( 7 "unsafe" 8 9 "github.com/gopherjs/gopherjs/js" 10 ) 11 12 func (v Value) object() *js.Object { 13 if v.typ.Kind() == Array || v.typ.Kind() == Struct { 14 return js.InternalObject(v.ptr) 15 } 16 if v.flag&flagIndir != 0 { 17 val := js.InternalObject(v.ptr).Call("$get") 18 if val != js.Global.Get("$ifaceNil") && val.Get("constructor") != jsType(v.typ) { 19 switch v.typ.Kind() { 20 case Uint64, Int64: 21 val = jsType(v.typ).New(val.Get("$high"), val.Get("$low")) 22 case Complex64, Complex128: 23 val = jsType(v.typ).New(val.Get("$real"), val.Get("$imag")) 24 case Slice: 25 if val == val.Get("constructor").Get("nil") { 26 val = jsType(v.typ).Get("nil") 27 break 28 } 29 newVal := jsType(v.typ).New(val.Get("$array")) 30 newVal.Set("$offset", val.Get("$offset")) 31 newVal.Set("$length", val.Get("$length")) 32 newVal.Set("$capacity", val.Get("$capacity")) 33 val = newVal 34 } 35 } 36 return js.InternalObject(val.Unsafe()) 37 } 38 return js.InternalObject(v.ptr) 39 } 40 41 func (v Value) assignTo(context string, dst *rtype, target unsafe.Pointer) Value { 42 if v.flag&flagMethod != 0 { 43 v = makeMethodValue(context, v) 44 } 45 switch { 46 case directlyAssignable(dst, v.typ): 47 // Overwrite type so that they match. 48 // Same memory layout, so no harm done. 49 fl := v.flag&(flagAddr|flagIndir) | v.flag.ro() 50 fl |= flag(dst.Kind()) 51 return Value{dst, v.ptr, fl} 52 53 case implements(dst, v.typ): 54 if target == nil { 55 target = unsafe_New(dst) 56 } 57 // GopherJS: Skip the v.Kind() == Interface && v.IsNil() if statement 58 // from upstream. ifaceE2I below does not panic, and it needs 59 // to run, given its custom implementation. 60 x := valueInterface(v) 61 if dst.NumMethod() == 0 { 62 *(*interface{})(target) = x 63 } else { 64 ifaceE2I(dst, x, target) 65 } 66 return Value{dst, target, flagIndir | flag(Interface)} 67 } 68 69 // Failed. 70 panic(context + ": value of type " + v.typ.String() + " is not assignable to type " + dst.String()) 71 } 72 73 var callHelper = js.Global.Get("$call").Interface().(func(...interface{}) *js.Object) 74 75 func (v Value) call(op string, in []Value) []Value { 76 var ( 77 t *funcType 78 fn unsafe.Pointer 79 rcvr *js.Object 80 ) 81 if v.flag&flagMethod != 0 { 82 _, t, fn = methodReceiver(op, v, int(v.flag)>>flagMethodShift) 83 rcvr = v.object() 84 if isWrapped(v.typ) { 85 rcvr = jsType(v.typ).New(rcvr) 86 } 87 } else { 88 t = (*funcType)(unsafe.Pointer(v.typ)) 89 fn = unsafe.Pointer(v.object().Unsafe()) 90 rcvr = js.Undefined 91 } 92 93 if fn == nil { 94 panic("reflect.Value.Call: call of nil function") 95 } 96 97 isSlice := op == "CallSlice" 98 n := t.NumIn() 99 if isSlice { 100 if !t.IsVariadic() { 101 panic("reflect: CallSlice of non-variadic function") 102 } 103 if len(in) < n { 104 panic("reflect: CallSlice with too few input arguments") 105 } 106 if len(in) > n { 107 panic("reflect: CallSlice with too many input arguments") 108 } 109 } else { 110 if t.IsVariadic() { 111 n-- 112 } 113 if len(in) < n { 114 panic("reflect: Call with too few input arguments") 115 } 116 if !t.IsVariadic() && len(in) > n { 117 panic("reflect: Call with too many input arguments") 118 } 119 } 120 for _, x := range in { 121 if x.Kind() == Invalid { 122 panic("reflect: " + op + " using zero Value argument") 123 } 124 } 125 for i := 0; i < n; i++ { 126 if xt, targ := in[i].Type(), t.In(i); !xt.AssignableTo(targ) { 127 panic("reflect: " + op + " using " + xt.String() + " as type " + targ.String()) 128 } 129 } 130 if !isSlice && t.IsVariadic() { 131 // prepare slice for remaining values 132 m := len(in) - n 133 slice := MakeSlice(t.In(n), m, m) 134 elem := t.In(n).Elem() 135 for i := 0; i < m; i++ { 136 x := in[n+i] 137 if xt := x.Type(); !xt.AssignableTo(elem) { 138 panic("reflect: cannot use " + xt.String() + " as type " + elem.String() + " in " + op) 139 } 140 slice.Index(i).Set(x) 141 } 142 origIn := in 143 in = make([]Value, n+1) 144 copy(in[:n], origIn) 145 in[n] = slice 146 } 147 148 nin := len(in) 149 if nin != t.NumIn() { 150 panic("reflect.Value.Call: wrong argument count") 151 } 152 nout := t.NumOut() 153 154 argsArray := js.Global.Get("Array").New(t.NumIn()) 155 for i, arg := range in { 156 argsArray.SetIndex(i, unwrapJsObject(t.In(i), arg.assignTo("reflect.Value.Call", t.In(i).common(), nil).object())) 157 } 158 results := callHelper(js.InternalObject(fn), rcvr, argsArray) 159 160 switch nout { 161 case 0: 162 return nil 163 case 1: 164 return []Value{makeValue(t.Out(0), wrapJsObject(t.Out(0), results), 0)} 165 default: 166 ret := make([]Value, nout) 167 for i := range ret { 168 ret[i] = makeValue(t.Out(i), wrapJsObject(t.Out(i), results.Index(i)), 0) 169 } 170 return ret 171 } 172 } 173 174 func (v Value) Cap() int { 175 k := v.kind() 176 switch k { 177 case Array: 178 return v.typ.Len() 179 case Chan, Slice: 180 return v.object().Get("$capacity").Int() 181 } 182 panic(&ValueError{"reflect.Value.Cap", k}) 183 } 184 185 func (v Value) Index(i int) Value { 186 switch k := v.kind(); k { 187 case Array: 188 tt := (*arrayType)(unsafe.Pointer(v.typ)) 189 if i < 0 || i > int(tt.len) { 190 panic("reflect: array index out of range") 191 } 192 typ := tt.elem 193 fl := v.flag&(flagIndir|flagAddr) | v.flag.ro() | flag(typ.Kind()) 194 195 a := js.InternalObject(v.ptr) 196 if fl&flagIndir != 0 && typ.Kind() != Array && typ.Kind() != Struct { 197 return Value{typ, unsafe.Pointer(jsType(PtrTo(typ)).New( 198 js.InternalObject(func() *js.Object { return wrapJsObject(typ, a.Index(i)) }), 199 js.InternalObject(func(x *js.Object) { a.SetIndex(i, unwrapJsObject(typ, x)) }), 200 ).Unsafe()), fl} 201 } 202 return makeValue(typ, wrapJsObject(typ, a.Index(i)), fl) 203 204 case Slice: 205 s := v.object() 206 if i < 0 || i >= s.Get("$length").Int() { 207 panic("reflect: slice index out of range") 208 } 209 tt := (*sliceType)(unsafe.Pointer(v.typ)) 210 typ := tt.elem 211 fl := flagAddr | flagIndir | v.flag.ro() | flag(typ.Kind()) 212 213 i += s.Get("$offset").Int() 214 a := s.Get("$array") 215 if fl&flagIndir != 0 && typ.Kind() != Array && typ.Kind() != Struct { 216 return Value{typ, unsafe.Pointer(jsType(PtrTo(typ)).New( 217 js.InternalObject(func() *js.Object { return wrapJsObject(typ, a.Index(i)) }), 218 js.InternalObject(func(x *js.Object) { a.SetIndex(i, unwrapJsObject(typ, x)) }), 219 ).Unsafe()), fl} 220 } 221 return makeValue(typ, wrapJsObject(typ, a.Index(i)), fl) 222 223 case String: 224 str := *(*string)(v.ptr) 225 if i < 0 || i >= len(str) { 226 panic("reflect: string index out of range") 227 } 228 fl := v.flag.ro() | flag(Uint8) | flagIndir 229 c := str[i] 230 return Value{uint8Type, unsafe.Pointer(&c), fl} 231 232 default: 233 panic(&ValueError{"reflect.Value.Index", k}) 234 } 235 } 236 237 func (v Value) InterfaceData() [2]uintptr { 238 panic("InterfaceData is not supported by GopherJS") 239 } 240 241 func (v Value) IsNil() bool { 242 switch k := v.kind(); k { 243 case Ptr, Slice: 244 return v.object() == jsType(v.typ).Get("nil") 245 case Chan: 246 return v.object() == js.Global.Get("$chanNil") 247 case Func: 248 return v.object() == js.Global.Get("$throwNilPointerError") 249 case Map: 250 return v.object() == js.InternalObject(false) 251 case Interface: 252 return v.object() == js.Global.Get("$ifaceNil") 253 case UnsafePointer: 254 return v.object().Unsafe() == 0 255 default: 256 panic(&ValueError{"reflect.Value.IsNil", k}) 257 } 258 } 259 260 func (v Value) Len() int { 261 switch k := v.kind(); k { 262 case Array, String: 263 return v.object().Length() 264 case Slice: 265 return v.object().Get("$length").Int() 266 case Chan: 267 return v.object().Get("$buffer").Get("length").Int() 268 case Map: 269 return v.object().Get("size").Int() 270 default: 271 panic(&ValueError{"reflect.Value.Len", k}) 272 } 273 } 274 275 func (v Value) Pointer() uintptr { 276 switch k := v.kind(); k { 277 case Chan, Map, Ptr, UnsafePointer: 278 if v.IsNil() { 279 return 0 280 } 281 return v.object().Unsafe() 282 case Func: 283 if v.IsNil() { 284 return 0 285 } 286 return 1 287 case Slice: 288 if v.IsNil() { 289 return 0 290 } 291 return v.object().Get("$array").Unsafe() 292 default: 293 panic(&ValueError{"reflect.Value.Pointer", k}) 294 } 295 } 296 297 func (v Value) Set(x Value) { 298 v.mustBeAssignable() 299 x.mustBeExported() 300 x = x.assignTo("reflect.Set", v.typ, nil) 301 if v.flag&flagIndir != 0 { 302 switch v.typ.Kind() { 303 case Array: 304 jsType(v.typ).Call("copy", js.InternalObject(v.ptr), js.InternalObject(x.ptr)) 305 case Interface: 306 js.InternalObject(v.ptr).Call("$set", js.InternalObject(valueInterface(x))) 307 case Struct: 308 copyStruct(js.InternalObject(v.ptr), js.InternalObject(x.ptr), v.typ) 309 default: 310 js.InternalObject(v.ptr).Call("$set", x.object()) 311 } 312 return 313 } 314 v.ptr = x.ptr 315 } 316 317 func (v Value) SetBytes(x []byte) { 318 v.mustBeAssignable() 319 v.mustBe(Slice) 320 if v.typ.Elem().Kind() != Uint8 { 321 panic("reflect.Value.SetBytes of non-byte slice") 322 } 323 slice := js.InternalObject(x) 324 if v.typ.Name() != "" || v.typ.Elem().Name() != "" { 325 typedSlice := jsType(v.typ).New(slice.Get("$array")) 326 typedSlice.Set("$offset", slice.Get("$offset")) 327 typedSlice.Set("$length", slice.Get("$length")) 328 typedSlice.Set("$capacity", slice.Get("$capacity")) 329 slice = typedSlice 330 } 331 js.InternalObject(v.ptr).Call("$set", slice) 332 } 333 334 func (v Value) SetCap(n int) { 335 v.mustBeAssignable() 336 v.mustBe(Slice) 337 s := js.InternalObject(v.ptr).Call("$get") 338 if n < s.Get("$length").Int() || n > s.Get("$capacity").Int() { 339 panic("reflect: slice capacity out of range in SetCap") 340 } 341 newSlice := jsType(v.typ).New(s.Get("$array")) 342 newSlice.Set("$offset", s.Get("$offset")) 343 newSlice.Set("$length", s.Get("$length")) 344 newSlice.Set("$capacity", n) 345 js.InternalObject(v.ptr).Call("$set", newSlice) 346 } 347 348 func (v Value) SetLen(n int) { 349 v.mustBeAssignable() 350 v.mustBe(Slice) 351 s := js.InternalObject(v.ptr).Call("$get") 352 if n < 0 || n > s.Get("$capacity").Int() { 353 panic("reflect: slice length out of range in SetLen") 354 } 355 newSlice := jsType(v.typ).New(s.Get("$array")) 356 newSlice.Set("$offset", s.Get("$offset")) 357 newSlice.Set("$length", n) 358 newSlice.Set("$capacity", s.Get("$capacity")) 359 js.InternalObject(v.ptr).Call("$set", newSlice) 360 } 361 362 func (v Value) Slice(i, j int) Value { 363 var ( 364 cap int 365 typ Type 366 s *js.Object 367 ) 368 switch kind := v.kind(); kind { 369 case Array: 370 if v.flag&flagAddr == 0 { 371 panic("reflect.Value.Slice: slice of unaddressable array") 372 } 373 tt := (*arrayType)(unsafe.Pointer(v.typ)) 374 cap = int(tt.len) 375 typ = SliceOf(tt.elem) 376 s = jsType(typ).New(v.object()) 377 378 case Slice: 379 typ = v.typ 380 s = v.object() 381 cap = s.Get("$capacity").Int() 382 383 case String: 384 str := *(*string)(v.ptr) 385 if i < 0 || j < i || j > len(str) { 386 panic("reflect.Value.Slice: string slice index out of bounds") 387 } 388 return ValueOf(str[i:j]) 389 390 default: 391 panic(&ValueError{"reflect.Value.Slice", kind}) 392 } 393 394 if i < 0 || j < i || j > cap { 395 panic("reflect.Value.Slice: slice index out of bounds") 396 } 397 398 return makeValue(typ, js.Global.Call("$subslice", s, i, j), v.flag.ro()) 399 } 400 401 func (v Value) Slice3(i, j, k int) Value { 402 var ( 403 cap int 404 typ Type 405 s *js.Object 406 ) 407 switch kind := v.kind(); kind { 408 case Array: 409 if v.flag&flagAddr == 0 { 410 panic("reflect.Value.Slice: slice of unaddressable array") 411 } 412 tt := (*arrayType)(unsafe.Pointer(v.typ)) 413 cap = int(tt.len) 414 typ = SliceOf(tt.elem) 415 s = jsType(typ).New(v.object()) 416 417 case Slice: 418 typ = v.typ 419 s = v.object() 420 cap = s.Get("$capacity").Int() 421 422 default: 423 panic(&ValueError{"reflect.Value.Slice3", kind}) 424 } 425 426 if i < 0 || j < i || k < j || k > cap { 427 panic("reflect.Value.Slice3: slice index out of bounds") 428 } 429 430 return makeValue(typ, js.Global.Call("$subslice", s, i, j, k), v.flag.ro()) 431 } 432 433 func (v Value) Close() { 434 v.mustBe(Chan) 435 v.mustBeExported() 436 js.Global.Call("$close", v.object()) 437 } 438 439 func (v Value) Elem() Value { 440 switch k := v.kind(); k { 441 case Interface: 442 val := v.object() 443 if val == js.Global.Get("$ifaceNil") { 444 return Value{} 445 } 446 typ := reflectType(val.Get("constructor")) 447 return makeValue(typ, val.Get("$val"), v.flag.ro()) 448 449 case Ptr: 450 if v.IsNil() { 451 return Value{} 452 } 453 val := v.object() 454 tt := (*ptrType)(unsafe.Pointer(v.typ)) 455 fl := v.flag&flagRO | flagIndir | flagAddr 456 fl |= flag(tt.elem.Kind()) 457 return Value{tt.elem, unsafe.Pointer(wrapJsObject(tt.elem, val).Unsafe()), fl} 458 459 default: 460 panic(&ValueError{"reflect.Value.Elem", k}) 461 } 462 } 463 464 // NumField returns the number of fields in the struct v. 465 // It panics if v's Kind is not Struct. 466 func (v Value) NumField() int { 467 v.mustBe(Struct) 468 tt := (*structType)(unsafe.Pointer(v.typ)) 469 return len(tt.fields) 470 } 471 472 // MapKeys returns a slice containing all the keys present in the map, 473 // in unspecified order. 474 // It panics if v's Kind is not Map. 475 // It returns an empty slice if v represents a nil map. 476 func (v Value) MapKeys() []Value { 477 v.mustBe(Map) 478 tt := (*mapType)(unsafe.Pointer(v.typ)) 479 keyType := tt.key 480 481 fl := v.flag.ro() | flag(keyType.Kind()) 482 483 m := v.pointer() 484 mlen := int(0) 485 if m != nil { 486 mlen = maplen(m) 487 } 488 it := mapiterinit(v.typ, m) 489 a := make([]Value, mlen) 490 var i int 491 for i = 0; i < len(a); i++ { 492 key := mapiterkey(it) 493 if key == nil { 494 // Someone deleted an entry from the map since we 495 // called maplen above. It's a data race, but nothing 496 // we can do about it. 497 break 498 } 499 a[i] = copyVal(keyType, fl, key) 500 mapiternext(it) 501 } 502 return a[:i] 503 } 504 505 // MapIndex returns the value associated with key in the map v. 506 // It panics if v's Kind is not Map. 507 // It returns the zero Value if key is not found in the map or if v represents a nil map. 508 // As in Go, the key's value must be assignable to the map's key type. 509 func (v Value) MapIndex(key Value) Value { 510 v.mustBe(Map) 511 tt := (*mapType)(unsafe.Pointer(v.typ)) 512 513 // Do not require key to be exported, so that DeepEqual 514 // and other programs can use all the keys returned by 515 // MapKeys as arguments to MapIndex. If either the map 516 // or the key is unexported, though, the result will be 517 // considered unexported. This is consistent with the 518 // behavior for structs, which allow read but not write 519 // of unexported fields. 520 key = key.assignTo("reflect.Value.MapIndex", tt.key, nil) 521 522 var k unsafe.Pointer 523 if key.flag&flagIndir != 0 { 524 k = key.ptr 525 } else { 526 k = unsafe.Pointer(&key.ptr) 527 } 528 e := mapaccess(v.typ, v.pointer(), k) 529 if e == nil { 530 return Value{} 531 } 532 typ := tt.elem 533 fl := (v.flag | key.flag).ro() 534 fl |= flag(typ.Kind()) 535 return copyVal(typ, fl, e) 536 } 537 538 func (v Value) Field(i int) Value { 539 if v.kind() != Struct { 540 panic(&ValueError{"reflect.Value.Field", v.kind()}) 541 } 542 tt := (*structType)(unsafe.Pointer(v.typ)) 543 if uint(i) >= uint(len(tt.fields)) { 544 panic("reflect: Field index out of range") 545 } 546 547 prop := jsType(v.typ).Get("fields").Index(i).Get("prop").String() 548 field := &tt.fields[i] 549 typ := field.typ 550 551 fl := v.flag&(flagStickyRO|flagIndir|flagAddr) | flag(typ.Kind()) 552 if !field.name.isExported() { 553 if field.embedded() { 554 fl |= flagEmbedRO 555 } else { 556 fl |= flagStickyRO 557 } 558 } 559 560 if tag := tt.fields[i].name.tag(); tag != "" && i != 0 { 561 if jsTag := getJsTag(tag); jsTag != "" { 562 for { 563 v = v.Field(0) 564 if v.typ == jsObjectPtr { 565 o := v.object().Get("object") 566 return Value{typ, unsafe.Pointer(jsType(PtrTo(typ)).New( 567 js.InternalObject(func() *js.Object { return js.Global.Call("$internalize", o.Get(jsTag), jsType(typ)) }), 568 js.InternalObject(func(x *js.Object) { o.Set(jsTag, js.Global.Call("$externalize", x, jsType(typ))) }), 569 ).Unsafe()), fl} 570 } 571 if v.typ.Kind() == Ptr { 572 v = v.Elem() 573 } 574 } 575 } 576 } 577 578 s := js.InternalObject(v.ptr) 579 if fl&flagIndir != 0 && typ.Kind() != Array && typ.Kind() != Struct { 580 return Value{typ, unsafe.Pointer(jsType(PtrTo(typ)).New( 581 js.InternalObject(func() *js.Object { return wrapJsObject(typ, s.Get(prop)) }), 582 js.InternalObject(func(x *js.Object) { s.Set(prop, unwrapJsObject(typ, x)) }), 583 ).Unsafe()), fl} 584 } 585 return makeValue(typ, wrapJsObject(typ, s.Get(prop)), fl) 586 }