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