github.com/gopherjs/gopherjs@v1.19.0-beta1.0.20240506212314-27071a8796e4/compiler/natives/src/reflect/reflect.go (about) 1 //go:build js 2 // +build js 3 4 package reflect 5 6 import ( 7 "errors" 8 "runtime" 9 "strconv" 10 "unsafe" 11 12 "internal/itoa" 13 14 "github.com/gopherjs/gopherjs/js" 15 ) 16 17 var initialized = false 18 19 func init() { 20 // avoid dead code elimination 21 used := func(i interface{}) {} 22 used(rtype{}) 23 used(uncommonType{}) 24 used(method{}) 25 used(arrayType{}) 26 used(chanType{}) 27 used(funcType{}) 28 used(interfaceType{}) 29 used(mapType{}) 30 used(ptrType{}) 31 used(sliceType{}) 32 used(structType{}) 33 used(imethod{}) 34 used(structField{}) 35 36 initialized = true 37 uint8Type = TypeOf(uint8(0)).(*rtype) // set for real 38 } 39 40 // New returns a Value representing a pointer to a new zero value 41 // for the specified type. That is, the returned Value's Type is PtrTo(typ). 42 // 43 // The upstream version includes an extra check to avoid creating types that 44 // are tagged as go:notinheap. This shouldn't matter in GopherJS, and tracking 45 // that state is over-complex, so we just skip that check. 46 func New(typ Type) Value { 47 if typ == nil { 48 panic("reflect: New(nil)") 49 } 50 t := typ.(*rtype) 51 pt := t.ptrTo() 52 ptr := unsafe_New(t) 53 fl := flag(Ptr) 54 return Value{pt, ptr, fl} 55 } 56 57 func jsType(typ Type) *js.Object { 58 return js.InternalObject(typ).Get("jsType") 59 } 60 61 func reflectType(typ *js.Object) *rtype { 62 if typ.Get("reflectType") == js.Undefined { 63 rt := &rtype{ 64 size: uintptr(typ.Get("size").Int()), 65 kind: uint8(typ.Get("kind").Int()), 66 str: resolveReflectName(newName(internalStr(typ.Get("string")), "", typ.Get("exported").Bool(), false)), 67 } 68 js.InternalObject(rt).Set("jsType", typ) 69 typ.Set("reflectType", js.InternalObject(rt)) 70 71 methodSet := js.Global.Call("$methodSet", typ) 72 if methodSet.Length() != 0 || typ.Get("named").Bool() { 73 rt.tflag |= tflagUncommon 74 if typ.Get("named").Bool() { 75 rt.tflag |= tflagNamed 76 } 77 var reflectMethods []method 78 for i := 0; i < methodSet.Length(); i++ { // Exported methods first. 79 m := methodSet.Index(i) 80 exported := internalStr(m.Get("pkg")) == "" 81 if !exported { 82 continue 83 } 84 reflectMethods = append(reflectMethods, method{ 85 name: resolveReflectName(newMethodName(m)), 86 mtyp: newTypeOff(reflectType(m.Get("typ"))), 87 }) 88 } 89 xcount := uint16(len(reflectMethods)) 90 for i := 0; i < methodSet.Length(); i++ { // Unexported methods second. 91 m := methodSet.Index(i) 92 exported := internalStr(m.Get("pkg")) == "" 93 if exported { 94 continue 95 } 96 reflectMethods = append(reflectMethods, method{ 97 name: resolveReflectName(newMethodName(m)), 98 mtyp: newTypeOff(reflectType(m.Get("typ"))), 99 }) 100 } 101 ut := &uncommonType{ 102 pkgPath: resolveReflectName(newName(internalStr(typ.Get("pkg")), "", false, false)), 103 mcount: uint16(methodSet.Length()), 104 xcount: xcount, 105 _methods: reflectMethods, 106 } 107 js.InternalObject(ut).Set("jsType", typ) 108 js.InternalObject(rt).Set("uncommonType", js.InternalObject(ut)) 109 } 110 111 switch rt.Kind() { 112 case Array: 113 setKindType(rt, &arrayType{ 114 elem: reflectType(typ.Get("elem")), 115 len: uintptr(typ.Get("len").Int()), 116 }) 117 case Chan: 118 dir := BothDir 119 if typ.Get("sendOnly").Bool() { 120 dir = SendDir 121 } 122 if typ.Get("recvOnly").Bool() { 123 dir = RecvDir 124 } 125 setKindType(rt, &chanType{ 126 elem: reflectType(typ.Get("elem")), 127 dir: uintptr(dir), 128 }) 129 case Func: 130 params := typ.Get("params") 131 in := make([]*rtype, params.Length()) 132 for i := range in { 133 in[i] = reflectType(params.Index(i)) 134 } 135 results := typ.Get("results") 136 out := make([]*rtype, results.Length()) 137 for i := range out { 138 out[i] = reflectType(results.Index(i)) 139 } 140 outCount := uint16(results.Length()) 141 if typ.Get("variadic").Bool() { 142 outCount |= 1 << 15 143 } 144 setKindType(rt, &funcType{ 145 rtype: *rt, 146 inCount: uint16(params.Length()), 147 outCount: outCount, 148 _in: in, 149 _out: out, 150 }) 151 case Interface: 152 methods := typ.Get("methods") 153 imethods := make([]imethod, methods.Length()) 154 for i := range imethods { 155 m := methods.Index(i) 156 imethods[i] = imethod{ 157 name: resolveReflectName(newMethodName(m)), 158 typ: newTypeOff(reflectType(m.Get("typ"))), 159 } 160 } 161 setKindType(rt, &interfaceType{ 162 rtype: *rt, 163 pkgPath: newName(internalStr(typ.Get("pkg")), "", false, false), 164 methods: imethods, 165 }) 166 case Map: 167 setKindType(rt, &mapType{ 168 key: reflectType(typ.Get("key")), 169 elem: reflectType(typ.Get("elem")), 170 }) 171 case Ptr: 172 setKindType(rt, &ptrType{ 173 elem: reflectType(typ.Get("elem")), 174 }) 175 case Slice: 176 setKindType(rt, &sliceType{ 177 elem: reflectType(typ.Get("elem")), 178 }) 179 case Struct: 180 fields := typ.Get("fields") 181 reflectFields := make([]structField, fields.Length()) 182 for i := range reflectFields { 183 f := fields.Index(i) 184 reflectFields[i] = structField{ 185 name: newName(internalStr(f.Get("name")), internalStr(f.Get("tag")), f.Get("exported").Bool(), f.Get("embedded").Bool()), 186 typ: reflectType(f.Get("typ")), 187 offset: uintptr(i), 188 } 189 } 190 setKindType(rt, &structType{ 191 rtype: *rt, 192 pkgPath: newName(internalStr(typ.Get("pkgPath")), "", false, false), 193 fields: reflectFields, 194 }) 195 } 196 } 197 198 return (*rtype)(unsafe.Pointer(typ.Get("reflectType").Unsafe())) 199 } 200 201 func setKindType(rt *rtype, kindType interface{}) { 202 js.InternalObject(rt).Set("kindType", js.InternalObject(kindType)) 203 js.InternalObject(kindType).Set("rtype", js.InternalObject(rt)) 204 } 205 206 type uncommonType struct { 207 pkgPath nameOff 208 mcount uint16 209 xcount uint16 210 moff uint32 211 212 _methods []method 213 } 214 215 func (t *uncommonType) methods() []method { 216 return t._methods 217 } 218 219 func (t *uncommonType) exportedMethods() []method { 220 return t._methods[:t.xcount:t.xcount] 221 } 222 223 func (t *rtype) uncommon() *uncommonType { 224 obj := js.InternalObject(t).Get("uncommonType") 225 if obj == js.Undefined { 226 return nil 227 } 228 return (*uncommonType)(unsafe.Pointer(obj.Unsafe())) 229 } 230 231 type funcType struct { 232 rtype `reflect:"func"` 233 inCount uint16 234 outCount uint16 235 236 _in []*rtype 237 _out []*rtype 238 } 239 240 func (t *funcType) in() []*rtype { 241 return t._in 242 } 243 244 func (t *funcType) out() []*rtype { 245 return t._out 246 } 247 248 type name struct { 249 bytes *byte 250 } 251 252 type nameData struct { 253 name string 254 tag string 255 exported bool 256 embedded bool 257 pkgPath string 258 } 259 260 var nameMap = make(map[*byte]*nameData) 261 262 func (n name) name() (s string) { return nameMap[n.bytes].name } 263 func (n name) tag() (s string) { return nameMap[n.bytes].tag } 264 func (n name) pkgPath() string { return nameMap[n.bytes].pkgPath } 265 func (n name) isExported() bool { return nameMap[n.bytes].exported } 266 func (n name) embedded() bool { return nameMap[n.bytes].embedded } 267 func (n name) setPkgPath(pkgpath string) { 268 nameMap[n.bytes].pkgPath = pkgpath 269 } 270 271 func newName(n, tag string, exported, embedded bool) name { 272 b := new(byte) 273 nameMap[b] = &nameData{ 274 name: n, 275 tag: tag, 276 exported: exported, 277 embedded: embedded, 278 } 279 return name{ 280 bytes: b, 281 } 282 } 283 284 // newMethodName creates name instance for a method. 285 // 286 // Input object is expected to be an entry of the "methods" list of the 287 // corresponding JS type. 288 func newMethodName(m *js.Object) name { 289 b := new(byte) 290 nameMap[b] = &nameData{ 291 name: internalStr(m.Get("name")), 292 tag: "", 293 pkgPath: internalStr(m.Get("pkg")), 294 exported: internalStr(m.Get("pkg")) == "", 295 } 296 return name{bytes: b} 297 } 298 299 var nameOffList []name 300 301 func (t *rtype) nameOff(off nameOff) name { 302 return nameOffList[int(off)] 303 } 304 305 func resolveReflectName(n name) nameOff { 306 i := len(nameOffList) 307 nameOffList = append(nameOffList, n) 308 return nameOff(i) 309 } 310 311 var typeOffList []*rtype 312 313 func (t *rtype) typeOff(off typeOff) *rtype { 314 return typeOffList[int(off)] 315 } 316 317 func newTypeOff(t *rtype) typeOff { 318 i := len(typeOffList) 319 typeOffList = append(typeOffList, t) 320 return typeOff(i) 321 } 322 323 // addReflectOff adds a pointer to the reflection lookup map in the runtime. 324 // It returns a new ID that can be used as a typeOff or textOff, and will 325 // be resolved correctly. Implemented in the runtime package. 326 func addReflectOff(ptr unsafe.Pointer) int32 { 327 i := len(typeOffList) 328 typeOffList = append(typeOffList, (*rtype)(ptr)) 329 return int32(i) 330 } 331 332 func internalStr(strObj *js.Object) string { 333 var c struct{ str string } 334 js.InternalObject(c).Set("str", strObj) // get string without internalizing 335 return c.str 336 } 337 338 func isWrapped(typ Type) bool { 339 return jsType(typ).Get("wrapped").Bool() 340 } 341 342 func copyStruct(dst, src *js.Object, typ Type) { 343 fields := jsType(typ).Get("fields") 344 for i := 0; i < fields.Length(); i++ { 345 prop := fields.Index(i).Get("prop").String() 346 dst.Set(prop, src.Get(prop)) 347 } 348 } 349 350 func makeValue(t Type, v *js.Object, fl flag) Value { 351 rt := t.common() 352 if t.Kind() == Array || t.Kind() == Struct || t.Kind() == Ptr { 353 return Value{rt, unsafe.Pointer(v.Unsafe()), fl | flag(t.Kind())} 354 } 355 return Value{rt, unsafe.Pointer(js.Global.Call("$newDataPointer", v, jsType(rt.ptrTo())).Unsafe()), fl | flag(t.Kind()) | flagIndir} 356 } 357 358 func MakeSlice(typ Type, len, cap int) Value { 359 if typ.Kind() != Slice { 360 panic("reflect.MakeSlice of non-slice type") 361 } 362 if len < 0 { 363 panic("reflect.MakeSlice: negative len") 364 } 365 if cap < 0 { 366 panic("reflect.MakeSlice: negative cap") 367 } 368 if len > cap { 369 panic("reflect.MakeSlice: len > cap") 370 } 371 372 return makeValue(typ, js.Global.Call("$makeSlice", jsType(typ), len, cap, js.InternalObject(func() *js.Object { return jsType(typ.Elem()).Call("zero") })), 0) 373 } 374 375 func TypeOf(i interface{}) Type { 376 if !initialized { // avoid error of uint8Type 377 return &rtype{} 378 } 379 if i == nil { 380 return nil 381 } 382 return reflectType(js.InternalObject(i).Get("constructor")) 383 } 384 385 func ValueOf(i interface{}) Value { 386 if i == nil { 387 return Value{} 388 } 389 return makeValue(reflectType(js.InternalObject(i).Get("constructor")), js.InternalObject(i).Get("$val"), 0) 390 } 391 392 func ArrayOf(count int, elem Type) Type { 393 if count < 0 { 394 panic("reflect: negative length passed to ArrayOf") 395 } 396 397 return reflectType(js.Global.Call("$arrayType", jsType(elem), count)) 398 } 399 400 func ChanOf(dir ChanDir, t Type) Type { 401 return reflectType(js.Global.Call("$chanType", jsType(t), dir == SendDir, dir == RecvDir)) 402 } 403 404 func FuncOf(in, out []Type, variadic bool) Type { 405 if variadic && (len(in) == 0 || in[len(in)-1].Kind() != Slice) { 406 panic("reflect.FuncOf: last arg of variadic func must be slice") 407 } 408 409 jsIn := make([]*js.Object, len(in)) 410 for i, v := range in { 411 jsIn[i] = jsType(v) 412 } 413 jsOut := make([]*js.Object, len(out)) 414 for i, v := range out { 415 jsOut[i] = jsType(v) 416 } 417 return reflectType(js.Global.Call("$funcType", jsIn, jsOut, variadic)) 418 } 419 420 func MapOf(key, elem Type) Type { 421 switch key.Kind() { 422 case Func, Map, Slice: 423 panic("reflect.MapOf: invalid key type " + key.String()) 424 } 425 426 return reflectType(js.Global.Call("$mapType", jsType(key), jsType(elem))) 427 } 428 429 func (t *rtype) ptrTo() *rtype { 430 return reflectType(js.Global.Call("$ptrType", jsType(t))) 431 } 432 433 func SliceOf(t Type) Type { 434 return reflectType(js.Global.Call("$sliceType", jsType(t))) 435 } 436 437 func StructOf(fields []StructField) Type { 438 var ( 439 jsFields = make([]*js.Object, len(fields)) 440 fset = map[string]struct{}{} 441 pkgpath string 442 hasGCProg bool 443 ) 444 for i, field := range fields { 445 if field.Name == "" { 446 panic("reflect.StructOf: field " + strconv.Itoa(i) + " has no name") 447 } 448 if !isValidFieldName(field.Name) { 449 panic("reflect.StructOf: field " + strconv.Itoa(i) + " has invalid name") 450 } 451 if field.Type == nil { 452 panic("reflect.StructOf: field " + strconv.Itoa(i) + " has no type") 453 } 454 f, fpkgpath := runtimeStructField(field) 455 ft := f.typ 456 if ft.kind&kindGCProg != 0 { 457 hasGCProg = true 458 } 459 if fpkgpath != "" { 460 if pkgpath == "" { 461 pkgpath = fpkgpath 462 } else if pkgpath != fpkgpath { 463 panic("reflect.Struct: fields with different PkgPath " + pkgpath + " and " + fpkgpath) 464 } 465 } 466 name := field.Name 467 if f.embedded() { 468 // Embedded field 469 if field.Type.Kind() == Ptr { 470 // Embedded ** and *interface{} are illegal 471 elem := field.Type.Elem() 472 if k := elem.Kind(); k == Ptr || k == Interface { 473 panic("reflect.StructOf: illegal anonymous field type " + field.Type.String()) 474 } 475 } 476 switch field.Type.Kind() { 477 case Interface: 478 case Ptr: 479 ptr := (*ptrType)(unsafe.Pointer(ft)) 480 if unt := ptr.uncommon(); unt != nil { 481 if i > 0 && unt.mcount > 0 { 482 // Issue 15924. 483 panic("reflect: embedded type with methods not implemented if type is not first field") 484 } 485 if len(fields) > 1 { 486 panic("reflect: embedded type with methods not implemented if there is more than one field") 487 } 488 } 489 default: 490 if unt := ft.uncommon(); unt != nil { 491 if i > 0 && unt.mcount > 0 { 492 // Issue 15924. 493 panic("reflect: embedded type with methods not implemented if type is not first field") 494 } 495 if len(fields) > 1 && ft.kind&kindDirectIface != 0 { 496 panic("reflect: embedded type with methods not implemented for non-pointer type") 497 } 498 } 499 } 500 } 501 502 if _, dup := fset[name]; dup && name != "_" { 503 panic("reflect.StructOf: duplicate field " + name) 504 } 505 fset[name] = struct{}{} 506 // To be consistent with Compiler's behavior we need to avoid externalizing 507 // the "name" property. The line below is effectively an inverse of the 508 // internalStr() function. 509 jsf := js.InternalObject(struct{ name string }{name}) 510 // The rest is set through the js.Object() interface, which the compiler will 511 // externalize for us. 512 jsf.Set("prop", name) 513 jsf.Set("exported", f.name.isExported()) 514 jsf.Set("typ", jsType(field.Type)) 515 jsf.Set("tag", field.Tag) 516 jsf.Set("embedded", field.Anonymous) 517 jsFields[i] = jsf 518 } 519 _ = hasGCProg 520 typ := js.Global.Call("$structType", "", jsFields) 521 if pkgpath != "" { 522 typ.Set("pkgPath", pkgpath) 523 } 524 return reflectType(typ) 525 } 526 527 func Zero(typ Type) Value { 528 return makeValue(typ, jsType(typ).Call("zero"), 0) 529 } 530 531 func unsafe_New(typ *rtype) unsafe.Pointer { 532 switch typ.Kind() { 533 case Struct: 534 return unsafe.Pointer(jsType(typ).Get("ptr").New().Unsafe()) 535 case Array: 536 return unsafe.Pointer(jsType(typ).Call("zero").Unsafe()) 537 default: 538 return unsafe.Pointer(js.Global.Call("$newDataPointer", jsType(typ).Call("zero"), jsType(typ.ptrTo())).Unsafe()) 539 } 540 } 541 542 func makeInt(f flag, bits uint64, t Type) Value { 543 typ := t.common() 544 ptr := unsafe_New(typ) 545 switch typ.Kind() { 546 case Int8: 547 *(*int8)(ptr) = int8(bits) 548 case Int16: 549 *(*int16)(ptr) = int16(bits) 550 case Int, Int32: 551 *(*int32)(ptr) = int32(bits) 552 case Int64: 553 *(*int64)(ptr) = int64(bits) 554 case Uint8: 555 *(*uint8)(ptr) = uint8(bits) 556 case Uint16: 557 *(*uint16)(ptr) = uint16(bits) 558 case Uint, Uint32, Uintptr: 559 *(*uint32)(ptr) = uint32(bits) 560 case Uint64: 561 *(*uint64)(ptr) = uint64(bits) 562 } 563 return Value{typ, ptr, f | flagIndir | flag(typ.Kind())} 564 } 565 566 func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value { 567 if typ.Kind() != Func { 568 panic("reflect: call of MakeFunc with non-Func type") 569 } 570 571 t := typ.common() 572 ftyp := (*funcType)(unsafe.Pointer(t)) 573 574 fv := js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} { 575 // Convert raw JS arguments into []Value the user-supplied function expects. 576 args := make([]Value, ftyp.NumIn()) 577 for i := range args { 578 argType := ftyp.In(i).common() 579 args[i] = makeValue(argType, arguments[i], 0) 580 } 581 582 // Call the user-supplied function. 583 resultsSlice := fn(args) 584 585 // Verify that returned value types are compatible with the function type specified by the caller. 586 if want, got := ftyp.NumOut(), len(resultsSlice); want != got { 587 panic("reflect: expected " + strconv.Itoa(want) + " return values, got " + strconv.Itoa(got)) 588 } 589 for i, rtyp := range ftyp.out() { 590 if !resultsSlice[i].Type().AssignableTo(rtyp) { 591 panic("reflect: " + strconv.Itoa(i) + " return value type is not compatible with the function declaration") 592 } 593 } 594 595 // Rearrange return values according to the expected function signature. 596 switch ftyp.NumOut() { 597 case 0: 598 return nil 599 case 1: 600 return resultsSlice[0].object() 601 default: 602 results := js.Global.Get("Array").New(ftyp.NumOut()) 603 for i, r := range resultsSlice { 604 results.SetIndex(i, r.object()) 605 } 606 return results 607 } 608 }) 609 610 return Value{t, unsafe.Pointer(fv.Unsafe()), flag(Func)} 611 } 612 613 func typedmemmove(t *rtype, dst, src unsafe.Pointer) { 614 js.InternalObject(dst).Call("$set", js.InternalObject(src).Call("$get")) 615 } 616 617 func loadScalar(p unsafe.Pointer, n uintptr) uintptr { 618 return js.InternalObject(p).Call("$get").Unsafe() 619 } 620 621 func makechan(typ *rtype, size int) (ch unsafe.Pointer) { 622 ctyp := (*chanType)(unsafe.Pointer(typ)) 623 return unsafe.Pointer(js.Global.Get("$Chan").New(jsType(ctyp.elem), size).Unsafe()) 624 } 625 626 func makemap(t *rtype, cap int) (m unsafe.Pointer) { 627 return unsafe.Pointer(js.Global.Get("Map").New().Unsafe()) 628 } 629 630 func keyFor(t *rtype, key unsafe.Pointer) (*js.Object, *js.Object) { 631 kv := js.InternalObject(key) 632 if kv.Get("$get") != js.Undefined { 633 kv = kv.Call("$get") 634 } 635 k := jsType(t.Key()).Call("keyFor", kv) 636 return kv, k 637 } 638 639 func mapaccess(t *rtype, m, key unsafe.Pointer) unsafe.Pointer { 640 if !js.InternalObject(m).Bool() { 641 return nil // nil map 642 } 643 _, k := keyFor(t, key) 644 entry := js.InternalObject(m).Call("get", k) 645 if entry == js.Undefined { 646 return nil 647 } 648 return unsafe.Pointer(js.Global.Call("$newDataPointer", entry.Get("v"), jsType(PtrTo(t.Elem()))).Unsafe()) 649 } 650 651 func mapassign(t *rtype, m, key, val unsafe.Pointer) { 652 kv, k := keyFor(t, key) 653 jsVal := js.InternalObject(val).Call("$get") 654 et := t.Elem() 655 if et.Kind() == Struct { 656 newVal := jsType(et).Call("zero") 657 copyStruct(newVal, jsVal, et) 658 jsVal = newVal 659 } 660 entry := js.Global.Get("Object").New() 661 entry.Set("k", kv) 662 entry.Set("v", jsVal) 663 js.InternalObject(m).Call("set", k, entry) 664 } 665 666 func mapdelete(t *rtype, m unsafe.Pointer, key unsafe.Pointer) { 667 _, k := keyFor(t, key) 668 if !js.InternalObject(m).Bool() { 669 return // nil map 670 } 671 js.InternalObject(m).Call("delete", k) 672 } 673 674 // TODO(nevkonatkte): The following three "faststr" implementations are meant to 675 // perform better for the common case of string-keyed maps (see upstream: 676 // https://github.com/golang/go/commit/23832ba2e2fb396cda1dacf3e8afcb38ec36dcba) 677 // However, the stubs below will perform the same or worse because of the extra 678 // string-to-pointer conversion. Not sure how to fix this without significant 679 // code duplication, however. 680 681 func mapaccess_faststr(t *rtype, m unsafe.Pointer, key string) (val unsafe.Pointer) { 682 return mapaccess(t, m, unsafe.Pointer(&key)) 683 } 684 685 func mapassign_faststr(t *rtype, m unsafe.Pointer, key string, val unsafe.Pointer) { 686 mapassign(t, m, unsafe.Pointer(&key), val) 687 } 688 689 func mapdelete_faststr(t *rtype, m unsafe.Pointer, key string) { 690 mapdelete(t, m, unsafe.Pointer(&key)) 691 } 692 693 type hiter struct { 694 t Type 695 m *js.Object // Underlying map object. 696 keys *js.Object 697 i int 698 699 // last is the last object the iterator indicates. If this object exists, the 700 // functions that return the current key or value returns this object, 701 // regardless of the current iterator. It is because the current iterator 702 // might be stale due to key deletion in a loop. 703 last *js.Object 704 } 705 706 func (iter *hiter) skipUntilValidKey() { 707 for iter.i < iter.keys.Length() { 708 k := iter.keys.Index(iter.i) 709 entry := iter.m.Call("get", k) 710 if entry != js.Undefined { 711 break 712 } 713 // The key is already deleted. Move on the next item. 714 iter.i++ 715 } 716 } 717 718 func mapiterinit(t *rtype, m unsafe.Pointer, it *hiter) { 719 mapObj := js.InternalObject(m) 720 keys := js.Global.Get("Array").New() 721 if mapObj.Get("keys") != js.Undefined { 722 keysIter := mapObj.Call("keys") 723 if mapObj.Get("keys") != js.Undefined { 724 keys = js.Global.Get("Array").Call("from", keysIter) 725 } 726 } 727 728 *it = hiter{ 729 t: t, 730 m: mapObj, 731 keys: keys, 732 i: 0, 733 last: nil, 734 } 735 } 736 737 func mapiterkey(it *hiter) unsafe.Pointer { 738 var kv *js.Object 739 if it.last != nil { 740 kv = it.last 741 } else { 742 it.skipUntilValidKey() 743 if it.i == it.keys.Length() { 744 return nil 745 } 746 k := it.keys.Index(it.i) 747 kv = it.m.Call("get", k) 748 749 // Record the key-value pair for later accesses. 750 it.last = kv 751 } 752 return unsafe.Pointer(js.Global.Call("$newDataPointer", kv.Get("k"), jsType(PtrTo(it.t.Key()))).Unsafe()) 753 } 754 755 func mapiterelem(it *hiter) unsafe.Pointer { 756 var kv *js.Object 757 if it.last != nil { 758 kv = it.last 759 } else { 760 it.skipUntilValidKey() 761 if it.i == it.keys.Length() { 762 return nil 763 } 764 k := it.keys.Index(it.i) 765 kv = it.m.Call("get", k) 766 it.last = kv 767 } 768 return unsafe.Pointer(js.Global.Call("$newDataPointer", kv.Get("v"), jsType(PtrTo(it.t.Elem()))).Unsafe()) 769 } 770 771 func mapiternext(it *hiter) { 772 it.last = nil 773 it.i++ 774 } 775 776 func maplen(m unsafe.Pointer) int { 777 return js.InternalObject(m).Get("size").Int() 778 } 779 780 func cvtDirect(v Value, typ Type) Value { 781 srcVal := v.object() 782 if srcVal == jsType(v.typ).Get("nil") { 783 return makeValue(typ, jsType(typ).Get("nil"), v.flag) 784 } 785 786 var val *js.Object 787 switch k := typ.Kind(); k { 788 case Slice: 789 slice := jsType(typ).New(srcVal.Get("$array")) 790 slice.Set("$offset", srcVal.Get("$offset")) 791 slice.Set("$length", srcVal.Get("$length")) 792 slice.Set("$capacity", srcVal.Get("$capacity")) 793 val = js.Global.Call("$newDataPointer", slice, jsType(PtrTo(typ))) 794 case Ptr: 795 switch typ.Elem().Kind() { 796 case Struct: 797 if typ.Elem() == v.typ.Elem() { 798 val = srcVal 799 break 800 } 801 val = jsType(typ).New() 802 copyStruct(val, srcVal, typ.Elem()) 803 case Array: 804 // Unlike other pointers, array pointers are "wrapped" types (see 805 // isWrapped() in the compiler package), and are represented by a native 806 // javascript array object here. 807 val = srcVal 808 default: 809 val = jsType(typ).New(srcVal.Get("$get"), srcVal.Get("$set")) 810 } 811 case Struct: 812 val = jsType(typ).Get("ptr").New() 813 copyStruct(val, srcVal, typ) 814 case Array, Bool, Chan, Func, Interface, Map, String, UnsafePointer: 815 val = js.InternalObject(v.ptr) 816 default: 817 panic(&ValueError{"reflect.Convert", k}) 818 } 819 return Value{typ.common(), unsafe.Pointer(val.Unsafe()), v.flag.ro() | v.flag&flagIndir | flag(typ.Kind())} 820 } 821 822 // convertOp: []T -> *[N]T 823 func cvtSliceArrayPtr(v Value, t Type) Value { 824 slice := v.object() 825 826 slen := slice.Get("$length").Int() 827 alen := t.Elem().Len() 828 if alen > slen { 829 panic("reflect: cannot convert slice with length " + itoa.Itoa(slen) + " to pointer to array with length " + itoa.Itoa(alen)) 830 } 831 array := js.Global.Call("$sliceToGoArray", slice, jsType(t)) 832 return Value{t.common(), unsafe.Pointer(array.Unsafe()), v.flag&^(flagIndir|flagAddr|flagKindMask) | flag(Ptr)} 833 } 834 835 func Copy(dst, src Value) int { 836 dk := dst.kind() 837 if dk != Array && dk != Slice { 838 panic(&ValueError{"reflect.Copy", dk}) 839 } 840 if dk == Array { 841 dst.mustBeAssignable() 842 } 843 dst.mustBeExported() 844 845 sk := src.kind() 846 var stringCopy bool 847 if sk != Array && sk != Slice { 848 stringCopy = sk == String && dst.typ.Elem().Kind() == Uint8 849 if !stringCopy { 850 panic(&ValueError{"reflect.Copy", sk}) 851 } 852 } 853 src.mustBeExported() 854 855 if !stringCopy { 856 typesMustMatch("reflect.Copy", dst.typ.Elem(), src.typ.Elem()) 857 } 858 859 dstVal := dst.object() 860 if dk == Array { 861 dstVal = jsType(SliceOf(dst.typ.Elem())).New(dstVal) 862 } 863 864 srcVal := src.object() 865 if sk == Array { 866 srcVal = jsType(SliceOf(src.typ.Elem())).New(srcVal) 867 } 868 869 if stringCopy { 870 return js.Global.Call("$copyString", dstVal, srcVal).Int() 871 } 872 return js.Global.Call("$copySlice", dstVal, srcVal).Int() 873 } 874 875 func methodReceiver(op string, v Value, i int) (_ *rtype, t *funcType, fn unsafe.Pointer) { 876 var prop string 877 if v.typ.Kind() == Interface { 878 tt := (*interfaceType)(unsafe.Pointer(v.typ)) 879 if i < 0 || i >= len(tt.methods) { 880 panic("reflect: internal error: invalid method index") 881 } 882 m := &tt.methods[i] 883 if !tt.nameOff(m.name).isExported() { 884 panic("reflect: " + op + " of unexported method") 885 } 886 t = (*funcType)(unsafe.Pointer(tt.typeOff(m.typ))) 887 prop = tt.nameOff(m.name).name() 888 } else { 889 ms := v.typ.exportedMethods() 890 if uint(i) >= uint(len(ms)) { 891 panic("reflect: internal error: invalid method index") 892 } 893 m := ms[i] 894 if !v.typ.nameOff(m.name).isExported() { 895 panic("reflect: " + op + " of unexported method") 896 } 897 t = (*funcType)(unsafe.Pointer(v.typ.typeOff(m.mtyp))) 898 prop = js.Global.Call("$methodSet", jsType(v.typ)).Index(i).Get("prop").String() 899 } 900 rcvr := v.object() 901 if isWrapped(v.typ) { 902 rcvr = jsType(v.typ).New(rcvr) 903 } 904 fn = unsafe.Pointer(rcvr.Get(prop).Unsafe()) 905 return 906 } 907 908 func valueInterface(v Value, safe bool) interface{} { 909 if v.flag == 0 { 910 panic(&ValueError{"reflect.Value.Interface", 0}) 911 } 912 if safe && v.flag&flagRO != 0 { 913 panic("reflect.Value.Interface: cannot return value obtained from unexported field or method") 914 } 915 if v.flag&flagMethod != 0 { 916 v = makeMethodValue("Interface", v) 917 } 918 919 if isWrapped(v.typ) { 920 if v.flag&flagIndir != 0 && v.Kind() == Struct { 921 cv := jsType(v.typ).Call("zero") 922 copyStruct(cv, v.object(), v.typ) 923 return interface{}(unsafe.Pointer(jsType(v.typ).New(cv).Unsafe())) 924 } 925 return interface{}(unsafe.Pointer(jsType(v.typ).New(v.object()).Unsafe())) 926 } 927 return interface{}(unsafe.Pointer(v.object().Unsafe())) 928 } 929 930 func ifaceE2I(t *rtype, src interface{}, dst unsafe.Pointer) { 931 js.InternalObject(dst).Call("$set", js.InternalObject(src)) 932 } 933 934 func makeMethodValue(op string, v Value) Value { 935 if v.flag&flagMethod == 0 { 936 panic("reflect: internal error: invalid use of makePartialFunc") 937 } 938 939 _, _, fn := methodReceiver(op, v, int(v.flag)>>flagMethodShift) 940 rcvr := v.object() 941 if isWrapped(v.typ) { 942 rcvr = jsType(v.typ).New(rcvr) 943 } 944 fv := js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} { 945 return js.InternalObject(fn).Call("apply", rcvr, arguments) 946 }) 947 return Value{v.Type().common(), unsafe.Pointer(fv.Unsafe()), v.flag.ro() | flag(Func)} 948 } 949 950 func (t *rtype) pointers() bool { 951 switch t.Kind() { 952 case Ptr, Map, Chan, Func, Struct, Array: 953 return true 954 default: 955 return false 956 } 957 } 958 959 func (t *rtype) Comparable() bool { 960 switch t.Kind() { 961 case Func, Slice, Map: 962 return false 963 case Array: 964 return t.Elem().Comparable() 965 case Struct: 966 for i := 0; i < t.NumField(); i++ { 967 if !t.Field(i).Type.Comparable() { 968 return false 969 } 970 } 971 } 972 return true 973 } 974 975 func (t *rtype) Method(i int) (m Method) { 976 if t.Kind() == Interface { 977 tt := (*interfaceType)(unsafe.Pointer(t)) 978 return tt.Method(i) 979 } 980 methods := t.exportedMethods() 981 if i < 0 || i >= len(methods) { 982 panic("reflect: Method index out of range") 983 } 984 p := methods[i] 985 pname := t.nameOff(p.name) 986 m.Name = pname.name() 987 fl := flag(Func) 988 mtyp := t.typeOff(p.mtyp) 989 ft := (*funcType)(unsafe.Pointer(mtyp)) 990 in := make([]Type, 0, 1+len(ft.in())) 991 in = append(in, t) 992 for _, arg := range ft.in() { 993 in = append(in, arg) 994 } 995 out := make([]Type, 0, len(ft.out())) 996 for _, ret := range ft.out() { 997 out = append(out, ret) 998 } 999 mt := FuncOf(in, out, ft.IsVariadic()) 1000 m.Type = mt 1001 prop := js.Global.Call("$methodSet", js.InternalObject(t).Get("jsType")).Index(i).Get("prop").String() 1002 fn := js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} { 1003 rcvr := arguments[0] 1004 return rcvr.Get(prop).Call("apply", rcvr, arguments[1:]) 1005 }) 1006 m.Func = Value{mt.(*rtype), unsafe.Pointer(fn.Unsafe()), fl} 1007 1008 m.Index = i 1009 return m 1010 } 1011 1012 func (v Value) object() *js.Object { 1013 if v.typ.Kind() == Array || v.typ.Kind() == Struct { 1014 return js.InternalObject(v.ptr) 1015 } 1016 if v.flag&flagIndir != 0 { 1017 val := js.InternalObject(v.ptr).Call("$get") 1018 if val != js.Global.Get("$ifaceNil") && val.Get("constructor") != jsType(v.typ) { 1019 switch v.typ.Kind() { 1020 case Uint64, Int64: 1021 val = jsType(v.typ).New(val.Get("$high"), val.Get("$low")) 1022 case Complex64, Complex128: 1023 val = jsType(v.typ).New(val.Get("$real"), val.Get("$imag")) 1024 case Slice: 1025 if val == val.Get("constructor").Get("nil") { 1026 val = jsType(v.typ).Get("nil") 1027 break 1028 } 1029 newVal := jsType(v.typ).New(val.Get("$array")) 1030 newVal.Set("$offset", val.Get("$offset")) 1031 newVal.Set("$length", val.Get("$length")) 1032 newVal.Set("$capacity", val.Get("$capacity")) 1033 val = newVal 1034 } 1035 } 1036 return js.InternalObject(val.Unsafe()) 1037 } 1038 return js.InternalObject(v.ptr) 1039 } 1040 1041 func (v Value) assignTo(context string, dst *rtype, target unsafe.Pointer) Value { 1042 if v.flag&flagMethod != 0 { 1043 v = makeMethodValue(context, v) 1044 } 1045 1046 switch { 1047 case directlyAssignable(dst, v.typ): 1048 // Overwrite type so that they match. 1049 // Same memory layout, so no harm done. 1050 fl := v.flag&(flagAddr|flagIndir) | v.flag.ro() 1051 fl |= flag(dst.Kind()) 1052 return Value{dst, v.ptr, fl} 1053 1054 case implements(dst, v.typ): 1055 if target == nil { 1056 target = unsafe_New(dst) 1057 } 1058 // GopherJS: Skip the v.Kind() == Interface && v.IsNil() if statement 1059 // from upstream. ifaceE2I below does not panic, and it needs 1060 // to run, given its custom implementation. 1061 x := valueInterface(v, false) 1062 if dst.NumMethod() == 0 { 1063 *(*interface{})(target) = x 1064 } else { 1065 ifaceE2I(dst, x, target) 1066 } 1067 return Value{dst, target, flagIndir | flag(Interface)} 1068 } 1069 1070 // Failed. 1071 panic(context + ": value of type " + v.typ.String() + " is not assignable to type " + dst.String()) 1072 } 1073 1074 var callHelper = js.Global.Get("$call").Interface().(func(...interface{}) *js.Object) 1075 1076 func (v Value) call(op string, in []Value) []Value { 1077 var ( 1078 t *funcType 1079 fn unsafe.Pointer 1080 rcvr *js.Object 1081 ) 1082 if v.flag&flagMethod != 0 { 1083 _, t, fn = methodReceiver(op, v, int(v.flag)>>flagMethodShift) 1084 rcvr = v.object() 1085 if isWrapped(v.typ) { 1086 rcvr = jsType(v.typ).New(rcvr) 1087 } 1088 } else { 1089 t = (*funcType)(unsafe.Pointer(v.typ)) 1090 fn = unsafe.Pointer(v.object().Unsafe()) 1091 rcvr = js.Undefined 1092 } 1093 1094 if fn == nil { 1095 panic("reflect.Value.Call: call of nil function") 1096 } 1097 1098 isSlice := op == "CallSlice" 1099 n := t.NumIn() 1100 if isSlice { 1101 if !t.IsVariadic() { 1102 panic("reflect: CallSlice of non-variadic function") 1103 } 1104 if len(in) < n { 1105 panic("reflect: CallSlice with too few input arguments") 1106 } 1107 if len(in) > n { 1108 panic("reflect: CallSlice with too many input arguments") 1109 } 1110 } else { 1111 if t.IsVariadic() { 1112 n-- 1113 } 1114 if len(in) < n { 1115 panic("reflect: Call with too few input arguments") 1116 } 1117 if !t.IsVariadic() && len(in) > n { 1118 panic("reflect: Call with too many input arguments") 1119 } 1120 } 1121 for _, x := range in { 1122 if x.Kind() == Invalid { 1123 panic("reflect: " + op + " using zero Value argument") 1124 } 1125 } 1126 for i := 0; i < n; i++ { 1127 if xt, targ := in[i].Type(), t.In(i); !xt.AssignableTo(targ) { 1128 panic("reflect: " + op + " using " + xt.String() + " as type " + targ.String()) 1129 } 1130 } 1131 if !isSlice && t.IsVariadic() { 1132 // prepare slice for remaining values 1133 m := len(in) - n 1134 slice := MakeSlice(t.In(n), m, m) 1135 elem := t.In(n).Elem() 1136 for i := 0; i < m; i++ { 1137 x := in[n+i] 1138 if xt := x.Type(); !xt.AssignableTo(elem) { 1139 panic("reflect: cannot use " + xt.String() + " as type " + elem.String() + " in " + op) 1140 } 1141 slice.Index(i).Set(x) 1142 } 1143 origIn := in 1144 in = make([]Value, n+1) 1145 copy(in[:n], origIn) 1146 in[n] = slice 1147 } 1148 1149 nin := len(in) 1150 if nin != t.NumIn() { 1151 panic("reflect.Value.Call: wrong argument count") 1152 } 1153 nout := t.NumOut() 1154 1155 argsArray := js.Global.Get("Array").New(t.NumIn()) 1156 for i, arg := range in { 1157 argsArray.SetIndex(i, unwrapJsObject(t.In(i), arg.assignTo("reflect.Value.Call", t.In(i).common(), nil).object())) 1158 } 1159 results := callHelper(js.InternalObject(fn), rcvr, argsArray) 1160 1161 switch nout { 1162 case 0: 1163 return nil 1164 case 1: 1165 return []Value{makeValue(t.Out(0), wrapJsObject(t.Out(0), results), 0)} 1166 default: 1167 ret := make([]Value, nout) 1168 for i := range ret { 1169 ret[i] = makeValue(t.Out(i), wrapJsObject(t.Out(i), results.Index(i)), 0) 1170 } 1171 return ret 1172 } 1173 } 1174 1175 func (v Value) Cap() int { 1176 k := v.kind() 1177 switch k { 1178 case Array: 1179 return v.typ.Len() 1180 case Chan, Slice: 1181 return v.object().Get("$capacity").Int() 1182 case Ptr: 1183 if v.typ.Elem().Kind() == Array { 1184 return v.typ.Elem().Len() 1185 } 1186 panic("reflect: call of reflect.Value.Cap on ptr to non-array Value") 1187 } 1188 panic(&ValueError{"reflect.Value.Cap", k}) 1189 } 1190 1191 var jsObjectPtr = reflectType(js.Global.Get("$jsObjectPtr")) 1192 1193 func wrapJsObject(typ Type, val *js.Object) *js.Object { 1194 if typ == jsObjectPtr { 1195 return jsType(jsObjectPtr).New(val) 1196 } 1197 return val 1198 } 1199 1200 func unwrapJsObject(typ Type, val *js.Object) *js.Object { 1201 if typ == jsObjectPtr { 1202 return val.Get("object") 1203 } 1204 return val 1205 } 1206 1207 func (v Value) Elem() Value { 1208 switch k := v.kind(); k { 1209 case Interface: 1210 val := v.object() 1211 if val == js.Global.Get("$ifaceNil") { 1212 return Value{} 1213 } 1214 typ := reflectType(val.Get("constructor")) 1215 return makeValue(typ, val.Get("$val"), v.flag.ro()) 1216 1217 case Ptr: 1218 if v.IsNil() { 1219 return Value{} 1220 } 1221 val := v.object() 1222 tt := (*ptrType)(unsafe.Pointer(v.typ)) 1223 fl := v.flag&flagRO | flagIndir | flagAddr 1224 fl |= flag(tt.elem.Kind()) 1225 return Value{tt.elem, unsafe.Pointer(wrapJsObject(tt.elem, val).Unsafe()), fl} 1226 1227 default: 1228 panic(&ValueError{"reflect.Value.Elem", k}) 1229 } 1230 } 1231 1232 func (v Value) Field(i int) Value { 1233 if v.kind() != Struct { 1234 panic(&ValueError{"reflect.Value.Field", v.kind()}) 1235 } 1236 tt := (*structType)(unsafe.Pointer(v.typ)) 1237 if uint(i) >= uint(len(tt.fields)) { 1238 panic("reflect: Field index out of range") 1239 } 1240 1241 prop := jsType(v.typ).Get("fields").Index(i).Get("prop").String() 1242 field := &tt.fields[i] 1243 typ := field.typ 1244 1245 fl := v.flag&(flagStickyRO|flagIndir|flagAddr) | flag(typ.Kind()) 1246 if !field.name.isExported() { 1247 if field.embedded() { 1248 fl |= flagEmbedRO 1249 } else { 1250 fl |= flagStickyRO 1251 } 1252 } 1253 1254 if tag := tt.fields[i].name.tag(); tag != "" && i != 0 { 1255 if jsTag := getJsTag(tag); jsTag != "" { 1256 for { 1257 v = v.Field(0) 1258 if v.typ == jsObjectPtr { 1259 o := v.object().Get("object") 1260 return Value{typ, unsafe.Pointer(jsType(PtrTo(typ)).New( 1261 js.InternalObject(func() *js.Object { return js.Global.Call("$internalize", o.Get(jsTag), jsType(typ)) }), 1262 js.InternalObject(func(x *js.Object) { o.Set(jsTag, js.Global.Call("$externalize", x, jsType(typ))) }), 1263 ).Unsafe()), fl} 1264 } 1265 if v.typ.Kind() == Ptr { 1266 v = v.Elem() 1267 } 1268 } 1269 } 1270 } 1271 1272 s := js.InternalObject(v.ptr) 1273 if fl&flagIndir != 0 && typ.Kind() != Array && typ.Kind() != Struct { 1274 return Value{typ, unsafe.Pointer(jsType(PtrTo(typ)).New( 1275 js.InternalObject(func() *js.Object { return wrapJsObject(typ, s.Get(prop)) }), 1276 js.InternalObject(func(x *js.Object) { s.Set(prop, unwrapJsObject(typ, x)) }), 1277 ).Unsafe()), fl} 1278 } 1279 return makeValue(typ, wrapJsObject(typ, s.Get(prop)), fl) 1280 } 1281 1282 func getJsTag(tag string) string { 1283 for tag != "" { 1284 // skip leading space 1285 i := 0 1286 for i < len(tag) && tag[i] == ' ' { 1287 i++ 1288 } 1289 tag = tag[i:] 1290 if tag == "" { 1291 break 1292 } 1293 1294 // scan to colon. 1295 // a space or a quote is a syntax error 1296 i = 0 1297 for i < len(tag) && tag[i] != ' ' && tag[i] != ':' && tag[i] != '"' { 1298 i++ 1299 } 1300 if i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' { 1301 break 1302 } 1303 name := string(tag[:i]) 1304 tag = tag[i+1:] 1305 1306 // scan quoted string to find value 1307 i = 1 1308 for i < len(tag) && tag[i] != '"' { 1309 if tag[i] == '\\' { 1310 i++ 1311 } 1312 i++ 1313 } 1314 if i >= len(tag) { 1315 break 1316 } 1317 qvalue := string(tag[:i+1]) 1318 tag = tag[i+1:] 1319 1320 if name == "js" { 1321 value, _ := strconv.Unquote(qvalue) 1322 return value 1323 } 1324 } 1325 return "" 1326 } 1327 1328 func (v Value) Index(i int) Value { 1329 switch k := v.kind(); k { 1330 case Array: 1331 tt := (*arrayType)(unsafe.Pointer(v.typ)) 1332 if i < 0 || i > int(tt.len) { 1333 panic("reflect: array index out of range") 1334 } 1335 typ := tt.elem 1336 fl := v.flag&(flagIndir|flagAddr) | v.flag.ro() | flag(typ.Kind()) 1337 1338 a := js.InternalObject(v.ptr) 1339 if fl&flagIndir != 0 && typ.Kind() != Array && typ.Kind() != Struct { 1340 return Value{typ, unsafe.Pointer(jsType(PtrTo(typ)).New( 1341 js.InternalObject(func() *js.Object { return wrapJsObject(typ, a.Index(i)) }), 1342 js.InternalObject(func(x *js.Object) { a.SetIndex(i, unwrapJsObject(typ, x)) }), 1343 ).Unsafe()), fl} 1344 } 1345 return makeValue(typ, wrapJsObject(typ, a.Index(i)), fl) 1346 1347 case Slice: 1348 s := v.object() 1349 if i < 0 || i >= s.Get("$length").Int() { 1350 panic("reflect: slice index out of range") 1351 } 1352 tt := (*sliceType)(unsafe.Pointer(v.typ)) 1353 typ := tt.elem 1354 fl := flagAddr | flagIndir | v.flag.ro() | flag(typ.Kind()) 1355 1356 i += s.Get("$offset").Int() 1357 a := s.Get("$array") 1358 if fl&flagIndir != 0 && typ.Kind() != Array && typ.Kind() != Struct { 1359 return Value{typ, unsafe.Pointer(jsType(PtrTo(typ)).New( 1360 js.InternalObject(func() *js.Object { return wrapJsObject(typ, a.Index(i)) }), 1361 js.InternalObject(func(x *js.Object) { a.SetIndex(i, unwrapJsObject(typ, x)) }), 1362 ).Unsafe()), fl} 1363 } 1364 return makeValue(typ, wrapJsObject(typ, a.Index(i)), fl) 1365 1366 case String: 1367 str := *(*string)(v.ptr) 1368 if i < 0 || i >= len(str) { 1369 panic("reflect: string index out of range") 1370 } 1371 fl := v.flag.ro() | flag(Uint8) | flagIndir 1372 c := str[i] 1373 return Value{uint8Type, unsafe.Pointer(&c), fl} 1374 1375 default: 1376 panic(&ValueError{"reflect.Value.Index", k}) 1377 } 1378 } 1379 1380 func (v Value) InterfaceData() [2]uintptr { 1381 panic(errors.New("InterfaceData is not supported by GopherJS")) 1382 } 1383 1384 func (v Value) IsNil() bool { 1385 switch k := v.kind(); k { 1386 case Ptr, Slice: 1387 return v.object() == jsType(v.typ).Get("nil") 1388 case Chan: 1389 return v.object() == js.Global.Get("$chanNil") 1390 case Func: 1391 return v.object() == js.Global.Get("$throwNilPointerError") 1392 case Map: 1393 return v.object() == js.InternalObject(false) 1394 case Interface: 1395 return v.object() == js.Global.Get("$ifaceNil") 1396 case UnsafePointer: 1397 return v.object().Unsafe() == 0 1398 default: 1399 panic(&ValueError{"reflect.Value.IsNil", k}) 1400 } 1401 } 1402 1403 func (v Value) Len() int { 1404 switch k := v.kind(); k { 1405 case Array, String: 1406 return v.object().Length() 1407 case Slice: 1408 return v.object().Get("$length").Int() 1409 case Chan: 1410 return v.object().Get("$buffer").Get("length").Int() 1411 case Map: 1412 return v.object().Get("size").Int() 1413 case Ptr: 1414 if v.typ.Elem().Kind() == Array { 1415 return v.typ.Elem().Len() 1416 } 1417 panic("reflect: call of reflect.Value.Len on ptr to non-array Value") 1418 default: 1419 panic(&ValueError{"reflect.Value.Len", k}) 1420 } 1421 } 1422 1423 func (v Value) Pointer() uintptr { 1424 switch k := v.kind(); k { 1425 case Chan, Map, Ptr, UnsafePointer: 1426 if v.IsNil() { 1427 return 0 1428 } 1429 return v.object().Unsafe() 1430 case Func: 1431 if v.IsNil() { 1432 return 0 1433 } 1434 return 1 1435 case Slice: 1436 if v.IsNil() { 1437 return 0 1438 } 1439 return v.object().Get("$array").Unsafe() 1440 default: 1441 panic(&ValueError{"reflect.Value.Pointer", k}) 1442 } 1443 } 1444 1445 func (v Value) Set(x Value) { 1446 v.mustBeAssignable() 1447 x.mustBeExported() 1448 x = x.assignTo("reflect.Set", v.typ, nil) 1449 if v.flag&flagIndir != 0 { 1450 switch v.typ.Kind() { 1451 case Array, Struct: 1452 jsType(v.typ).Call("copy", js.InternalObject(v.ptr), js.InternalObject(x.ptr)) 1453 case Interface: 1454 js.InternalObject(v.ptr).Call("$set", js.InternalObject(valueInterface(x, false))) 1455 default: 1456 js.InternalObject(v.ptr).Call("$set", x.object()) 1457 } 1458 return 1459 } 1460 v.ptr = x.ptr 1461 } 1462 1463 func (v Value) bytesSlow() []byte { 1464 switch v.kind() { 1465 case Slice: 1466 if v.typ.Elem().Kind() != Uint8 { 1467 panic("reflect.Value.Bytes of non-byte slice") 1468 } 1469 return *(*[]byte)(v.ptr) 1470 case Array: 1471 if v.typ.Elem().Kind() != Uint8 { 1472 panic("reflect.Value.Bytes of non-byte array") 1473 } 1474 if !v.CanAddr() { 1475 panic("reflect.Value.Bytes of unaddressable byte array") 1476 } 1477 // Replace the following with JS to avoid using unsafe pointers. 1478 // p := (*byte)(v.ptr) 1479 // n := int((*arrayType)(unsafe.Pointer(v.typ)).len) 1480 // return unsafe.Slice(p, n) 1481 return js.InternalObject(v.ptr).Interface().([]byte) 1482 } 1483 panic(&ValueError{"reflect.Value.Bytes", v.kind()}) 1484 } 1485 1486 func (v Value) SetBytes(x []byte) { 1487 v.mustBeAssignable() 1488 v.mustBe(Slice) 1489 if v.typ.Elem().Kind() != Uint8 { 1490 panic("reflect.Value.SetBytes of non-byte slice") 1491 } 1492 slice := js.InternalObject(x) 1493 if v.typ.Name() != "" || v.typ.Elem().Name() != "" { 1494 typedSlice := jsType(v.typ).New(slice.Get("$array")) 1495 typedSlice.Set("$offset", slice.Get("$offset")) 1496 typedSlice.Set("$length", slice.Get("$length")) 1497 typedSlice.Set("$capacity", slice.Get("$capacity")) 1498 slice = typedSlice 1499 } 1500 js.InternalObject(v.ptr).Call("$set", slice) 1501 } 1502 1503 func (v Value) SetCap(n int) { 1504 v.mustBeAssignable() 1505 v.mustBe(Slice) 1506 s := js.InternalObject(v.ptr).Call("$get") 1507 if n < s.Get("$length").Int() || n > s.Get("$capacity").Int() { 1508 panic("reflect: slice capacity out of range in SetCap") 1509 } 1510 newSlice := jsType(v.typ).New(s.Get("$array")) 1511 newSlice.Set("$offset", s.Get("$offset")) 1512 newSlice.Set("$length", s.Get("$length")) 1513 newSlice.Set("$capacity", n) 1514 js.InternalObject(v.ptr).Call("$set", newSlice) 1515 } 1516 1517 func (v Value) SetLen(n int) { 1518 v.mustBeAssignable() 1519 v.mustBe(Slice) 1520 s := js.InternalObject(v.ptr).Call("$get") 1521 if n < 0 || n > s.Get("$capacity").Int() { 1522 panic("reflect: slice length out of range in SetLen") 1523 } 1524 newSlice := jsType(v.typ).New(s.Get("$array")) 1525 newSlice.Set("$offset", s.Get("$offset")) 1526 newSlice.Set("$length", n) 1527 newSlice.Set("$capacity", s.Get("$capacity")) 1528 js.InternalObject(v.ptr).Call("$set", newSlice) 1529 } 1530 1531 func (v Value) Slice(i, j int) Value { 1532 var ( 1533 cap int 1534 typ Type 1535 s *js.Object 1536 ) 1537 switch kind := v.kind(); kind { 1538 case Array: 1539 if v.flag&flagAddr == 0 { 1540 panic("reflect.Value.Slice: slice of unaddressable array") 1541 } 1542 tt := (*arrayType)(unsafe.Pointer(v.typ)) 1543 cap = int(tt.len) 1544 typ = SliceOf(tt.elem) 1545 s = jsType(typ).New(v.object()) 1546 1547 case Slice: 1548 typ = v.typ 1549 s = v.object() 1550 cap = s.Get("$capacity").Int() 1551 1552 case String: 1553 str := *(*string)(v.ptr) 1554 if i < 0 || j < i || j > len(str) { 1555 panic("reflect.Value.Slice: string slice index out of bounds") 1556 } 1557 return ValueOf(str[i:j]) 1558 1559 default: 1560 panic(&ValueError{"reflect.Value.Slice", kind}) 1561 } 1562 1563 if i < 0 || j < i || j > cap { 1564 panic("reflect.Value.Slice: slice index out of bounds") 1565 } 1566 1567 return makeValue(typ, js.Global.Call("$subslice", s, i, j), v.flag.ro()) 1568 } 1569 1570 func (v Value) Slice3(i, j, k int) Value { 1571 var ( 1572 cap int 1573 typ Type 1574 s *js.Object 1575 ) 1576 switch kind := v.kind(); kind { 1577 case Array: 1578 if v.flag&flagAddr == 0 { 1579 panic("reflect.Value.Slice: slice of unaddressable array") 1580 } 1581 tt := (*arrayType)(unsafe.Pointer(v.typ)) 1582 cap = int(tt.len) 1583 typ = SliceOf(tt.elem) 1584 s = jsType(typ).New(v.object()) 1585 1586 case Slice: 1587 typ = v.typ 1588 s = v.object() 1589 cap = s.Get("$capacity").Int() 1590 1591 default: 1592 panic(&ValueError{"reflect.Value.Slice3", kind}) 1593 } 1594 1595 if i < 0 || j < i || k < j || k > cap { 1596 panic("reflect.Value.Slice3: slice index out of bounds") 1597 } 1598 1599 return makeValue(typ, js.Global.Call("$subslice", s, i, j, k), v.flag.ro()) 1600 } 1601 1602 func (v Value) Close() { 1603 v.mustBe(Chan) 1604 v.mustBeExported() 1605 js.Global.Call("$close", v.object()) 1606 } 1607 1608 var selectHelper = js.Global.Get("$select").Interface().(func(...interface{}) *js.Object) 1609 1610 func chanrecv(ch unsafe.Pointer, nb bool, val unsafe.Pointer) (selected, received bool) { 1611 comms := [][]*js.Object{{js.InternalObject(ch)}} 1612 if nb { 1613 comms = append(comms, []*js.Object{}) 1614 } 1615 selectRes := selectHelper(comms) 1616 if nb && selectRes.Index(0).Int() == 1 { 1617 return false, false 1618 } 1619 recvRes := selectRes.Index(1) 1620 js.InternalObject(val).Call("$set", recvRes.Index(0)) 1621 return true, recvRes.Index(1).Bool() 1622 } 1623 1624 func chansend(ch unsafe.Pointer, val unsafe.Pointer, nb bool) bool { 1625 comms := [][]*js.Object{{js.InternalObject(ch), js.InternalObject(val).Call("$get")}} 1626 if nb { 1627 comms = append(comms, []*js.Object{}) 1628 } 1629 selectRes := selectHelper(comms) 1630 if nb && selectRes.Index(0).Int() == 1 { 1631 return false 1632 } 1633 return true 1634 } 1635 1636 func rselect(rselects []runtimeSelect) (chosen int, recvOK bool) { 1637 comms := make([][]*js.Object, len(rselects)) 1638 for i, s := range rselects { 1639 switch SelectDir(s.dir) { 1640 case SelectDefault: 1641 comms[i] = []*js.Object{} 1642 case SelectRecv: 1643 ch := js.Global.Get("$chanNil") 1644 if js.InternalObject(s.ch) != js.InternalObject(0) { 1645 ch = js.InternalObject(s.ch) 1646 } 1647 comms[i] = []*js.Object{ch} 1648 case SelectSend: 1649 ch := js.Global.Get("$chanNil") 1650 var val *js.Object 1651 if js.InternalObject(s.ch) != js.InternalObject(0) { 1652 ch = js.InternalObject(s.ch) 1653 val = js.InternalObject(s.val).Call("$get") 1654 } 1655 comms[i] = []*js.Object{ch, val} 1656 } 1657 } 1658 selectRes := selectHelper(comms) 1659 c := selectRes.Index(0).Int() 1660 if SelectDir(rselects[c].dir) == SelectRecv { 1661 recvRes := selectRes.Index(1) 1662 js.InternalObject(rselects[c].val).Call("$set", recvRes.Index(0)) 1663 return c, recvRes.Index(1).Bool() 1664 } 1665 return c, false 1666 } 1667 1668 func DeepEqual(a1, a2 interface{}) bool { 1669 i1 := js.InternalObject(a1) 1670 i2 := js.InternalObject(a2) 1671 if i1 == i2 { 1672 return true 1673 } 1674 if i1 == nil || i2 == nil || i1.Get("constructor") != i2.Get("constructor") { 1675 return false 1676 } 1677 return deepValueEqualJs(ValueOf(a1), ValueOf(a2), nil) 1678 } 1679 1680 func deepValueEqualJs(v1, v2 Value, visited [][2]unsafe.Pointer) bool { 1681 if !v1.IsValid() || !v2.IsValid() { 1682 return !v1.IsValid() && !v2.IsValid() 1683 } 1684 if v1.Type() != v2.Type() { 1685 return false 1686 } 1687 if v1.Type() == jsObjectPtr { 1688 return unwrapJsObject(jsObjectPtr, v1.object()) == unwrapJsObject(jsObjectPtr, v2.object()) 1689 } 1690 1691 switch v1.Kind() { 1692 case Array, Map, Slice, Struct: 1693 for _, entry := range visited { 1694 if v1.ptr == entry[0] && v2.ptr == entry[1] { 1695 return true 1696 } 1697 } 1698 visited = append(visited, [2]unsafe.Pointer{v1.ptr, v2.ptr}) 1699 } 1700 1701 switch v1.Kind() { 1702 case Array, Slice: 1703 if v1.Kind() == Slice { 1704 if v1.IsNil() != v2.IsNil() { 1705 return false 1706 } 1707 if v1.object() == v2.object() { 1708 return true 1709 } 1710 } 1711 n := v1.Len() 1712 if n != v2.Len() { 1713 return false 1714 } 1715 for i := 0; i < n; i++ { 1716 if !deepValueEqualJs(v1.Index(i), v2.Index(i), visited) { 1717 return false 1718 } 1719 } 1720 return true 1721 case Interface: 1722 if v1.IsNil() || v2.IsNil() { 1723 return v1.IsNil() && v2.IsNil() 1724 } 1725 return deepValueEqualJs(v1.Elem(), v2.Elem(), visited) 1726 case Ptr: 1727 return deepValueEqualJs(v1.Elem(), v2.Elem(), visited) 1728 case Struct: 1729 n := v1.NumField() 1730 for i := 0; i < n; i++ { 1731 if !deepValueEqualJs(v1.Field(i), v2.Field(i), visited) { 1732 return false 1733 } 1734 } 1735 return true 1736 case Map: 1737 if v1.IsNil() != v2.IsNil() { 1738 return false 1739 } 1740 if v1.object() == v2.object() { 1741 return true 1742 } 1743 keys := v1.MapKeys() 1744 if len(keys) != v2.Len() { 1745 return false 1746 } 1747 for _, k := range keys { 1748 val1 := v1.MapIndex(k) 1749 val2 := v2.MapIndex(k) 1750 if !val1.IsValid() || !val2.IsValid() || !deepValueEqualJs(val1, val2, visited) { 1751 return false 1752 } 1753 } 1754 return true 1755 case Func: 1756 return v1.IsNil() && v2.IsNil() 1757 case UnsafePointer: 1758 return v1.object() == v2.object() 1759 } 1760 1761 return js.Global.Call("$interfaceIsEqual", js.InternalObject(valueInterface(v1, false)), js.InternalObject(valueInterface(v2, false))).Bool() 1762 } 1763 1764 func stringsLastIndex(s string, c byte) int { 1765 for i := len(s) - 1; i >= 0; i-- { 1766 if s[i] == c { 1767 return i 1768 } 1769 } 1770 return -1 1771 } 1772 1773 func stringsHasPrefix(s, prefix string) bool { 1774 return len(s) >= len(prefix) && s[:len(prefix)] == prefix 1775 } 1776 1777 func valueMethodName() string { 1778 var pc [5]uintptr 1779 n := runtime.Callers(1, pc[:]) 1780 frames := runtime.CallersFrames(pc[:n]) 1781 var frame runtime.Frame 1782 for more := true; more; { 1783 frame, more = frames.Next() 1784 name := frame.Function 1785 1786 // Function name extracted from the call stack can be different from 1787 // vanilla Go, so is not prefixed by "reflect.Value." as needed by the original. 1788 // See https://cs.opensource.google/go/go/+/refs/tags/go1.19.13:src/reflect/value.go;l=173-191 1789 // Here we try to fix stuff like "Object.$packages.reflect.Q.ptr.SetIterKey" 1790 // into "reflect.Value.SetIterKey". 1791 // This workaround may become obsolete after 1792 // https://github.com/gopherjs/gopherjs/issues/1085 is resolved. 1793 1794 const prefix = `Object.$packages.reflect.` 1795 if stringsHasPrefix(name, prefix) { 1796 if idx := stringsLastIndex(name, '.'); idx >= 0 { 1797 methodName := name[idx+1:] 1798 if len(methodName) > 0 && 'A' <= methodName[0] && methodName[0] <= 'Z' { 1799 return `reflect.Value.` + methodName 1800 } 1801 } 1802 } 1803 } 1804 return "unknown method" 1805 } 1806 1807 func verifyNotInHeapPtr(p uintptr) bool { 1808 // Go runtime uses this method to make sure that a uintptr won't crash GC if 1809 // interpreted as a heap pointer. This is not relevant for GopherJS, so we can 1810 // always return true. 1811 return true 1812 }