github.com/gopherjs/gopherjs@v1.19.0-beta1.0.20240506212314-27071a8796e4/compiler/natives/src/internal/reflectlite/reflectlite.go (about) 1 //go:build js 2 // +build js 3 4 package reflectlite 5 6 import ( 7 "unsafe" 8 9 "github.com/gopherjs/gopherjs/js" 10 ) 11 12 var initialized = false 13 14 func init() { 15 // avoid dead code elimination 16 used := func(i interface{}) {} 17 used(rtype{}) 18 used(uncommonType{}) 19 used(method{}) 20 used(arrayType{}) 21 used(chanType{}) 22 used(funcType{}) 23 used(interfaceType{}) 24 used(mapType{}) 25 used(ptrType{}) 26 used(sliceType{}) 27 used(structType{}) 28 used(imethod{}) 29 used(structField{}) 30 31 initialized = true 32 uint8Type = TypeOf(uint8(0)).(*rtype) // set for real 33 } 34 35 var uint8Type *rtype 36 37 var ( 38 idJsType = "_jsType" 39 idReflectType = "_reflectType" 40 idKindType = "kindType" 41 idRtype = "_rtype" 42 ) 43 44 func jsType(typ Type) *js.Object { 45 return js.InternalObject(typ).Get(idJsType) 46 } 47 48 func reflectType(typ *js.Object) *rtype { 49 if typ.Get(idReflectType) == js.Undefined { 50 rt := &rtype{ 51 size: uintptr(typ.Get("size").Int()), 52 kind: uint8(typ.Get("kind").Int()), 53 str: newNameOff(newName(internalStr(typ.Get("string")), "", typ.Get("exported").Bool(), false)), 54 } 55 js.InternalObject(rt).Set(idJsType, typ) 56 typ.Set(idReflectType, js.InternalObject(rt)) 57 58 methodSet := js.Global.Call("$methodSet", typ) 59 if methodSet.Length() != 0 || typ.Get("named").Bool() { 60 rt.tflag |= tflagUncommon 61 if typ.Get("named").Bool() { 62 rt.tflag |= tflagNamed 63 } 64 var reflectMethods []method 65 for i := 0; i < methodSet.Length(); i++ { // Exported methods first. 66 m := methodSet.Index(i) 67 exported := internalStr(m.Get("pkg")) == "" 68 if !exported { 69 continue 70 } 71 reflectMethods = append(reflectMethods, method{ 72 name: newNameOff(newName(internalStr(m.Get("name")), "", exported, false)), 73 mtyp: newTypeOff(reflectType(m.Get("typ"))), 74 }) 75 } 76 xcount := uint16(len(reflectMethods)) 77 for i := 0; i < methodSet.Length(); i++ { // Unexported methods second. 78 m := methodSet.Index(i) 79 exported := internalStr(m.Get("pkg")) == "" 80 if exported { 81 continue 82 } 83 reflectMethods = append(reflectMethods, method{ 84 name: newNameOff(newName(internalStr(m.Get("name")), "", exported, false)), 85 mtyp: newTypeOff(reflectType(m.Get("typ"))), 86 }) 87 } 88 ut := &uncommonType{ 89 pkgPath: newNameOff(newName(internalStr(typ.Get("pkg")), "", false, false)), 90 mcount: uint16(methodSet.Length()), 91 xcount: xcount, 92 _methods: reflectMethods, 93 } 94 uncommonTypeMap[rt] = ut 95 js.InternalObject(ut).Set(idJsType, typ) 96 } 97 98 switch rt.Kind() { 99 case Array: 100 setKindType(rt, &arrayType{ 101 elem: reflectType(typ.Get("elem")), 102 len: uintptr(typ.Get("len").Int()), 103 }) 104 case Chan: 105 dir := BothDir 106 if typ.Get("sendOnly").Bool() { 107 dir = SendDir 108 } 109 if typ.Get("recvOnly").Bool() { 110 dir = RecvDir 111 } 112 setKindType(rt, &chanType{ 113 elem: reflectType(typ.Get("elem")), 114 dir: uintptr(dir), 115 }) 116 case Func: 117 params := typ.Get("params") 118 in := make([]*rtype, params.Length()) 119 for i := range in { 120 in[i] = reflectType(params.Index(i)) 121 } 122 results := typ.Get("results") 123 out := make([]*rtype, results.Length()) 124 for i := range out { 125 out[i] = reflectType(results.Index(i)) 126 } 127 outCount := uint16(results.Length()) 128 if typ.Get("variadic").Bool() { 129 outCount |= 1 << 15 130 } 131 setKindType(rt, &funcType{ 132 rtype: *rt, 133 inCount: uint16(params.Length()), 134 outCount: outCount, 135 _in: in, 136 _out: out, 137 }) 138 case Interface: 139 methods := typ.Get("methods") 140 imethods := make([]imethod, methods.Length()) 141 for i := range imethods { 142 m := methods.Index(i) 143 imethods[i] = imethod{ 144 name: newNameOff(newName(internalStr(m.Get("name")), "", internalStr(m.Get("pkg")) == "", false)), 145 typ: newTypeOff(reflectType(m.Get("typ"))), 146 } 147 } 148 setKindType(rt, &interfaceType{ 149 rtype: *rt, 150 pkgPath: newName(internalStr(typ.Get("pkg")), "", false, false), 151 methods: imethods, 152 }) 153 case Map: 154 setKindType(rt, &mapType{ 155 key: reflectType(typ.Get("key")), 156 elem: reflectType(typ.Get("elem")), 157 }) 158 case Ptr: 159 setKindType(rt, &ptrType{ 160 elem: reflectType(typ.Get("elem")), 161 }) 162 case Slice: 163 setKindType(rt, &sliceType{ 164 elem: reflectType(typ.Get("elem")), 165 }) 166 case Struct: 167 fields := typ.Get("fields") 168 reflectFields := make([]structField, fields.Length()) 169 for i := range reflectFields { 170 f := fields.Index(i) 171 reflectFields[i] = structField{ 172 name: newName(internalStr(f.Get("name")), internalStr(f.Get("tag")), f.Get("exported").Bool(), f.Get("embedded").Bool()), 173 typ: reflectType(f.Get("typ")), 174 offset: uintptr(i), 175 } 176 } 177 setKindType(rt, &structType{ 178 rtype: *rt, 179 pkgPath: newName(internalStr(typ.Get("pkgPath")), "", false, false), 180 fields: reflectFields, 181 }) 182 } 183 } 184 185 return (*rtype)(unsafe.Pointer(typ.Get(idReflectType).Unsafe())) 186 } 187 188 func setKindType(rt *rtype, kindType interface{}) { 189 js.InternalObject(rt).Set(idKindType, js.InternalObject(kindType)) 190 js.InternalObject(kindType).Set(idRtype, 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 var uncommonTypeMap = make(map[*rtype]*uncommonType) 211 212 func (t *rtype) uncommon() *uncommonType { 213 return uncommonTypeMap[t] 214 } 215 216 type funcType struct { 217 rtype `reflect:"func"` 218 inCount uint16 219 outCount uint16 220 221 _in []*rtype 222 _out []*rtype 223 } 224 225 func (t *funcType) in() []*rtype { 226 return t._in 227 } 228 229 func (t *funcType) out() []*rtype { 230 return t._out 231 } 232 233 type name struct { 234 bytes *byte 235 } 236 237 type nameData struct { 238 name string 239 tag string 240 exported bool 241 embedded bool 242 } 243 244 var nameMap = make(map[*byte]*nameData) 245 246 func (n name) name() (s string) { return nameMap[n.bytes].name } 247 func (n name) tag() (s string) { return nameMap[n.bytes].tag } 248 func (n name) pkgPath() string { return "" } 249 func (n name) isExported() bool { return nameMap[n.bytes].exported } 250 func (n name) embedded() bool { return nameMap[n.bytes].embedded } 251 252 func newName(n, tag string, exported, embedded bool) name { 253 b := new(byte) 254 nameMap[b] = &nameData{ 255 name: n, 256 tag: tag, 257 exported: exported, 258 embedded: embedded, 259 } 260 return name{ 261 bytes: b, 262 } 263 } 264 265 var nameOffList []name 266 267 func (t *rtype) nameOff(off nameOff) name { 268 return nameOffList[int(off)] 269 } 270 271 func newNameOff(n name) nameOff { 272 i := len(nameOffList) 273 nameOffList = append(nameOffList, n) 274 return nameOff(i) 275 } 276 277 var typeOffList []*rtype 278 279 func (t *rtype) typeOff(off typeOff) *rtype { 280 return typeOffList[int(off)] 281 } 282 283 func newTypeOff(t *rtype) typeOff { 284 i := len(typeOffList) 285 typeOffList = append(typeOffList, t) 286 return typeOff(i) 287 } 288 289 func internalStr(strObj *js.Object) string { 290 var c struct{ str string } 291 js.InternalObject(c).Set("str", strObj) // get string without internalizing 292 return c.str 293 } 294 295 func isWrapped(typ Type) bool { 296 return jsType(typ).Get("wrapped").Bool() 297 } 298 299 func copyStruct(dst, src *js.Object, typ Type) { 300 fields := jsType(typ).Get("fields") 301 for i := 0; i < fields.Length(); i++ { 302 prop := fields.Index(i).Get("prop").String() 303 dst.Set(prop, src.Get(prop)) 304 } 305 } 306 307 func makeValue(t Type, v *js.Object, fl flag) Value { 308 rt := t.common() 309 if t.Kind() == Array || t.Kind() == Struct || t.Kind() == Ptr { 310 return Value{rt, unsafe.Pointer(v.Unsafe()), fl | flag(t.Kind())} 311 } 312 return Value{rt, unsafe.Pointer(js.Global.Call("$newDataPointer", v, jsType(rt.ptrTo())).Unsafe()), fl | flag(t.Kind()) | flagIndir} 313 } 314 315 func MakeSlice(typ Type, len, cap int) Value { 316 if typ.Kind() != Slice { 317 panic("reflect.MakeSlice of non-slice type") 318 } 319 if len < 0 { 320 panic("reflect.MakeSlice: negative len") 321 } 322 if cap < 0 { 323 panic("reflect.MakeSlice: negative cap") 324 } 325 if len > cap { 326 panic("reflect.MakeSlice: len > cap") 327 } 328 329 return makeValue(typ, js.Global.Call("$makeSlice", jsType(typ), len, cap, js.InternalObject(func() *js.Object { return jsType(typ.Elem()).Call("zero") })), 0) 330 } 331 332 func TypeOf(i interface{}) Type { 333 if !initialized { // avoid error of uint8Type 334 return &rtype{} 335 } 336 if i == nil { 337 return nil 338 } 339 return reflectType(js.InternalObject(i).Get("constructor")) 340 } 341 342 func ValueOf(i interface{}) Value { 343 if i == nil { 344 return Value{} 345 } 346 return makeValue(reflectType(js.InternalObject(i).Get("constructor")), js.InternalObject(i).Get("$val"), 0) 347 } 348 349 func ArrayOf(count int, elem Type) Type { 350 return reflectType(js.Global.Call("$arrayType", jsType(elem), count)) 351 } 352 353 func ChanOf(dir ChanDir, t Type) Type { 354 return reflectType(js.Global.Call("$chanType", jsType(t), dir == SendDir, dir == RecvDir)) 355 } 356 357 func FuncOf(in, out []Type, variadic bool) Type { 358 if variadic && (len(in) == 0 || in[len(in)-1].Kind() != Slice) { 359 panic("reflect.FuncOf: last arg of variadic func must be slice") 360 } 361 362 jsIn := make([]*js.Object, len(in)) 363 for i, v := range in { 364 jsIn[i] = jsType(v) 365 } 366 jsOut := make([]*js.Object, len(out)) 367 for i, v := range out { 368 jsOut[i] = jsType(v) 369 } 370 return reflectType(js.Global.Call("$funcType", jsIn, jsOut, variadic)) 371 } 372 373 func MapOf(key, elem Type) Type { 374 switch key.Kind() { 375 case Func, Map, Slice: 376 panic("reflect.MapOf: invalid key type " + key.String()) 377 } 378 379 return reflectType(js.Global.Call("$mapType", jsType(key), jsType(elem))) 380 } 381 382 func (t *rtype) ptrTo() *rtype { 383 return reflectType(js.Global.Call("$ptrType", jsType(t))) 384 } 385 386 func SliceOf(t Type) Type { 387 return reflectType(js.Global.Call("$sliceType", jsType(t))) 388 } 389 390 func Zero(typ Type) Value { 391 return makeValue(typ, jsType(typ).Call("zero"), 0) 392 } 393 394 func unsafe_New(typ *rtype) unsafe.Pointer { 395 switch typ.Kind() { 396 case Struct: 397 return unsafe.Pointer(jsType(typ).Get("ptr").New().Unsafe()) 398 case Array: 399 return unsafe.Pointer(jsType(typ).Call("zero").Unsafe()) 400 default: 401 return unsafe.Pointer(js.Global.Call("$newDataPointer", jsType(typ).Call("zero"), jsType(typ.ptrTo())).Unsafe()) 402 } 403 } 404 405 func makeInt(f flag, bits uint64, t Type) Value { 406 typ := t.common() 407 ptr := unsafe_New(typ) 408 switch typ.Kind() { 409 case Int8: 410 *(*int8)(ptr) = int8(bits) 411 case Int16: 412 *(*int16)(ptr) = int16(bits) 413 case Int, Int32: 414 *(*int32)(ptr) = int32(bits) 415 case Int64: 416 *(*int64)(ptr) = int64(bits) 417 case Uint8: 418 *(*uint8)(ptr) = uint8(bits) 419 case Uint16: 420 *(*uint16)(ptr) = uint16(bits) 421 case Uint, Uint32, Uintptr: 422 *(*uint32)(ptr) = uint32(bits) 423 case Uint64: 424 *(*uint64)(ptr) = uint64(bits) 425 } 426 return Value{typ, ptr, f | flagIndir | flag(typ.Kind())} 427 } 428 429 func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value { 430 if typ.Kind() != Func { 431 panic("reflect: call of MakeFunc with non-Func type") 432 } 433 434 t := typ.common() 435 ftyp := (*funcType)(unsafe.Pointer(t)) 436 437 fv := js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} { 438 args := make([]Value, ftyp.NumIn()) 439 for i := range args { 440 argType := ftyp.In(i).common() 441 args[i] = makeValue(argType, arguments[i], 0) 442 } 443 resultsSlice := fn(args) 444 switch ftyp.NumOut() { 445 case 0: 446 return nil 447 case 1: 448 return resultsSlice[0].object() 449 default: 450 results := js.Global.Get("Array").New(ftyp.NumOut()) 451 for i, r := range resultsSlice { 452 results.SetIndex(i, r.object()) 453 } 454 return results 455 } 456 }) 457 458 return Value{t, unsafe.Pointer(fv.Unsafe()), flag(Func)} 459 } 460 461 func typedmemmove(t *rtype, dst, src unsafe.Pointer) { 462 js.InternalObject(dst).Call("$set", js.InternalObject(src).Call("$get")) 463 } 464 465 func loadScalar(p unsafe.Pointer, n uintptr) uintptr { 466 return js.InternalObject(p).Call("$get").Unsafe() 467 } 468 469 func makechan(typ *rtype, size int) (ch unsafe.Pointer) { 470 ctyp := (*chanType)(unsafe.Pointer(typ)) 471 return unsafe.Pointer(js.Global.Get("$Chan").New(jsType(ctyp.elem), size).Unsafe()) 472 } 473 474 func makemap(t *rtype, cap int) (m unsafe.Pointer) { 475 return unsafe.Pointer(js.Global.Get("Map").New().Unsafe()) 476 } 477 478 func keyFor(t *rtype, key unsafe.Pointer) (*js.Object, string) { 479 kv := js.InternalObject(key) 480 if kv.Get("$get") != js.Undefined { 481 kv = kv.Call("$get") 482 } 483 k := jsType(t.Key()).Call("keyFor", kv).String() 484 return kv, k 485 } 486 487 func mapaccess(t *rtype, m, key unsafe.Pointer) unsafe.Pointer { 488 _, k := keyFor(t, key) 489 entry := js.InternalObject(m).Call("get", k) 490 if entry == js.Undefined { 491 return nil 492 } 493 return unsafe.Pointer(js.Global.Call("$newDataPointer", entry.Get("v"), jsType(PtrTo(t.Elem()))).Unsafe()) 494 } 495 496 func mapassign(t *rtype, m, key, val unsafe.Pointer) { 497 kv, k := keyFor(t, key) 498 jsVal := js.InternalObject(val).Call("$get") 499 et := t.Elem() 500 if et.Kind() == Struct { 501 newVal := jsType(et).Call("zero") 502 copyStruct(newVal, jsVal, et) 503 jsVal = newVal 504 } 505 entry := js.Global.Get("Object").New() 506 entry.Set("k", kv) 507 entry.Set("v", jsVal) 508 js.InternalObject(m).Call("set", k, entry) 509 } 510 511 func mapdelete(t *rtype, m unsafe.Pointer, key unsafe.Pointer) { 512 _, k := keyFor(t, key) 513 js.InternalObject(m).Call("delete", k) 514 } 515 516 type mapIter struct { 517 t Type 518 m *js.Object 519 keys *js.Object 520 i int 521 522 // last is the last object the iterator indicates. If this object exists, the functions that return the 523 // current key or value returns this object, regardless of the current iterator. It is because the current 524 // iterator might be stale due to key deletion in a loop. 525 last *js.Object 526 } 527 528 func (iter *mapIter) skipUntilValidKey() { 529 for iter.i < iter.keys.Length() { 530 k := iter.keys.Index(iter.i) 531 if iter.m.Call("get", k) != js.Undefined { 532 break 533 } 534 // The key is already deleted. Move on the next item. 535 iter.i++ 536 } 537 } 538 539 func mapiterinit(t *rtype, m unsafe.Pointer) unsafe.Pointer { 540 return unsafe.Pointer(&mapIter{t, js.InternalObject(m), js.Global.Get("Array").Call("from", js.InternalObject(m).Call("keys")), 0, nil}) 541 } 542 543 type TypeEx interface { 544 Type 545 Key() Type 546 } 547 548 func mapiterkey(it unsafe.Pointer) unsafe.Pointer { 549 iter := (*mapIter)(it) 550 var kv *js.Object 551 if iter.last != nil { 552 kv = iter.last 553 } else { 554 iter.skipUntilValidKey() 555 if iter.i == iter.keys.Length() { 556 return nil 557 } 558 k := iter.keys.Index(iter.i) 559 kv = iter.m.Call("get", k) 560 561 // Record the key-value pair for later accesses. 562 iter.last = kv 563 } 564 return unsafe.Pointer(js.Global.Call("$newDataPointer", kv.Get("k"), jsType(PtrTo(iter.t.(TypeEx).Key()))).Unsafe()) 565 } 566 567 func mapiternext(it unsafe.Pointer) { 568 iter := (*mapIter)(it) 569 iter.last = nil 570 iter.i++ 571 } 572 573 func maplen(m unsafe.Pointer) int { 574 return js.InternalObject(m).Get("size").Int() 575 } 576 577 func cvtDirect(v Value, typ Type) Value { 578 srcVal := v.object() 579 if srcVal == jsType(v.typ).Get("nil") { 580 return makeValue(typ, jsType(typ).Get("nil"), v.flag) 581 } 582 583 var val *js.Object 584 switch k := typ.Kind(); k { 585 case Slice: 586 slice := jsType(typ).New(srcVal.Get("$array")) 587 slice.Set("$offset", srcVal.Get("$offset")) 588 slice.Set("$length", srcVal.Get("$length")) 589 slice.Set("$capacity", srcVal.Get("$capacity")) 590 val = js.Global.Call("$newDataPointer", slice, jsType(PtrTo(typ))) 591 case Ptr: 592 if typ.Elem().Kind() == Struct { 593 if typ.Elem() == v.typ.Elem() { 594 val = srcVal 595 break 596 } 597 val = jsType(typ).New() 598 copyStruct(val, srcVal, typ.Elem()) 599 break 600 } 601 val = jsType(typ).New(srcVal.Get("$get"), srcVal.Get("$set")) 602 case Struct: 603 val = jsType(typ).Get("ptr").New() 604 copyStruct(val, srcVal, typ) 605 case Array, Bool, Chan, Func, Interface, Map, String: 606 val = js.InternalObject(v.ptr) 607 default: 608 panic(&ValueError{"reflect.Convert", k}) 609 } 610 return Value{typ.common(), unsafe.Pointer(val.Unsafe()), v.flag.ro() | v.flag&flagIndir | flag(typ.Kind())} 611 } 612 613 func Copy(dst, src Value) int { 614 dk := dst.kind() 615 if dk != Array && dk != Slice { 616 panic(&ValueError{"reflect.Copy", dk}) 617 } 618 if dk == Array { 619 dst.mustBeAssignable() 620 } 621 dst.mustBeExported() 622 623 sk := src.kind() 624 var stringCopy bool 625 if sk != Array && sk != Slice { 626 stringCopy = sk == String && dst.typ.Elem().Kind() == Uint8 627 if !stringCopy { 628 panic(&ValueError{"reflect.Copy", sk}) 629 } 630 } 631 src.mustBeExported() 632 633 if !stringCopy { 634 typesMustMatch("reflect.Copy", dst.typ.Elem(), src.typ.Elem()) 635 } 636 637 dstVal := dst.object() 638 if dk == Array { 639 dstVal = jsType(SliceOf(dst.typ.Elem())).New(dstVal) 640 } 641 642 srcVal := src.object() 643 if sk == Array { 644 srcVal = jsType(SliceOf(src.typ.Elem())).New(srcVal) 645 } 646 647 if stringCopy { 648 return js.Global.Call("$copyString", dstVal, srcVal).Int() 649 } 650 return js.Global.Call("$copySlice", dstVal, srcVal).Int() 651 } 652 653 func methodReceiver(op string, v Value, i int) (_ *rtype, t *funcType, fn unsafe.Pointer) { 654 var prop string 655 if v.typ.Kind() == Interface { 656 tt := (*interfaceType)(unsafe.Pointer(v.typ)) 657 if i < 0 || i >= len(tt.methods) { 658 panic("reflect: internal error: invalid method index") 659 } 660 m := &tt.methods[i] 661 if !tt.nameOff(m.name).isExported() { 662 panic("reflect: " + op + " of unexported method") 663 } 664 t = (*funcType)(unsafe.Pointer(tt.typeOff(m.typ))) 665 prop = tt.nameOff(m.name).name() 666 } else { 667 ms := v.typ.exportedMethods() 668 if uint(i) >= uint(len(ms)) { 669 panic("reflect: internal error: invalid method index") 670 } 671 m := ms[i] 672 if !v.typ.nameOff(m.name).isExported() { 673 panic("reflect: " + op + " of unexported method") 674 } 675 t = (*funcType)(unsafe.Pointer(v.typ.typeOff(m.mtyp))) 676 prop = js.Global.Call("$methodSet", jsType(v.typ)).Index(i).Get("prop").String() 677 } 678 rcvr := v.object() 679 if isWrapped(v.typ) { 680 rcvr = jsType(v.typ).New(rcvr) 681 } 682 fn = unsafe.Pointer(rcvr.Get(prop).Unsafe()) 683 return 684 } 685 686 func valueInterface(v Value) interface{} { 687 if v.flag == 0 { 688 panic(&ValueError{"reflect.Value.Interface", 0}) 689 } 690 691 if v.flag&flagMethod != 0 { 692 v = makeMethodValue("Interface", v) 693 } 694 695 if isWrapped(v.typ) { 696 if v.flag&flagIndir != 0 && v.Kind() == Struct { 697 cv := jsType(v.typ).Call("zero") 698 copyStruct(cv, v.object(), v.typ) 699 return interface{}(unsafe.Pointer(jsType(v.typ).New(cv).Unsafe())) 700 } 701 return interface{}(unsafe.Pointer(jsType(v.typ).New(v.object()).Unsafe())) 702 } 703 return interface{}(unsafe.Pointer(v.object().Unsafe())) 704 } 705 706 func ifaceE2I(t *rtype, src interface{}, dst unsafe.Pointer) { 707 js.InternalObject(dst).Call("$set", js.InternalObject(src)) 708 } 709 710 func methodName() string { 711 return "?FIXME?" 712 } 713 714 func makeMethodValue(op string, v Value) Value { 715 if v.flag&flagMethod == 0 { 716 panic("reflect: internal error: invalid use of makePartialFunc") 717 } 718 719 _, _, fn := methodReceiver(op, v, int(v.flag)>>flagMethodShift) 720 rcvr := v.object() 721 if isWrapped(v.typ) { 722 rcvr = jsType(v.typ).New(rcvr) 723 } 724 fv := js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} { 725 return js.InternalObject(fn).Call("apply", rcvr, arguments) 726 }) 727 return Value{v.Type().common(), unsafe.Pointer(fv.Unsafe()), v.flag.ro() | flag(Func)} 728 } 729 730 var jsObjectPtr = reflectType(js.Global.Get("$jsObjectPtr")) 731 732 func wrapJsObject(typ Type, val *js.Object) *js.Object { 733 if typ == jsObjectPtr { 734 return jsType(jsObjectPtr).New(val) 735 } 736 return val 737 } 738 739 func unwrapJsObject(typ Type, val *js.Object) *js.Object { 740 if typ == jsObjectPtr { 741 return val.Get("object") 742 } 743 return val 744 } 745 746 func getJsTag(tag string) string { 747 for tag != "" { 748 // skip leading space 749 i := 0 750 for i < len(tag) && tag[i] == ' ' { 751 i++ 752 } 753 tag = tag[i:] 754 if tag == "" { 755 break 756 } 757 758 // scan to colon. 759 // a space or a quote is a syntax error 760 i = 0 761 for i < len(tag) && tag[i] != ' ' && tag[i] != ':' && tag[i] != '"' { 762 i++ 763 } 764 if i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' { 765 break 766 } 767 name := string(tag[:i]) 768 tag = tag[i+1:] 769 770 // scan quoted string to find value 771 i = 1 772 for i < len(tag) && tag[i] != '"' { 773 if tag[i] == '\\' { 774 i++ 775 } 776 i++ 777 } 778 if i >= len(tag) { 779 break 780 } 781 qvalue := string(tag[:i+1]) 782 tag = tag[i+1:] 783 784 if name == "js" { 785 value, _ := unquote(qvalue) 786 return value 787 } 788 } 789 return "" 790 } 791 792 // PtrTo returns the pointer type with element t. 793 // For example, if t represents type Foo, PtrTo(t) represents *Foo. 794 func PtrTo(t Type) Type { 795 return t.(*rtype).ptrTo() 796 } 797 798 // copyVal returns a Value containing the map key or value at ptr, 799 // allocating a new variable as needed. 800 func copyVal(typ *rtype, fl flag, ptr unsafe.Pointer) Value { 801 if ifaceIndir(typ) { 802 // Copy result so future changes to the map 803 // won't change the underlying value. 804 c := unsafe_New(typ) 805 typedmemmove(typ, c, ptr) 806 return Value{typ, c, fl | flagIndir} 807 } 808 return Value{typ, *(*unsafe.Pointer)(ptr), fl} 809 } 810 811 var selectHelper = js.Global.Get("$select").Interface().(func(...interface{}) *js.Object) 812 813 func chanrecv(ch unsafe.Pointer, nb bool, val unsafe.Pointer) (selected, received bool) { 814 comms := [][]*js.Object{{js.InternalObject(ch)}} 815 if nb { 816 comms = append(comms, []*js.Object{}) 817 } 818 selectRes := selectHelper(comms) 819 if nb && selectRes.Index(0).Int() == 1 { 820 return false, false 821 } 822 recvRes := selectRes.Index(1) 823 js.InternalObject(val).Call("$set", recvRes.Index(0)) 824 return true, recvRes.Index(1).Bool() 825 } 826 827 func chansend(ch unsafe.Pointer, val unsafe.Pointer, nb bool) bool { 828 comms := [][]*js.Object{{js.InternalObject(ch), js.InternalObject(val).Call("$get")}} 829 if nb { 830 comms = append(comms, []*js.Object{}) 831 } 832 selectRes := selectHelper(comms) 833 if nb && selectRes.Index(0).Int() == 1 { 834 return false 835 } 836 return true 837 } 838 839 func rselect(rselects []runtimeSelect) (chosen int, recvOK bool) { 840 comms := make([][]*js.Object, len(rselects)) 841 for i, s := range rselects { 842 switch SelectDir(s.dir) { 843 case SelectDefault: 844 comms[i] = []*js.Object{} 845 case SelectRecv: 846 ch := js.Global.Get("$chanNil") 847 if js.InternalObject(s.ch) != js.InternalObject(0) { 848 ch = js.InternalObject(s.ch) 849 } 850 comms[i] = []*js.Object{ch} 851 case SelectSend: 852 ch := js.Global.Get("$chanNil") 853 var val *js.Object 854 if js.InternalObject(s.ch) != js.InternalObject(0) { 855 ch = js.InternalObject(s.ch) 856 val = js.InternalObject(s.val).Call("$get") 857 } 858 comms[i] = []*js.Object{ch, val} 859 } 860 } 861 selectRes := selectHelper(comms) 862 c := selectRes.Index(0).Int() 863 if SelectDir(rselects[c].dir) == SelectRecv { 864 recvRes := selectRes.Index(1) 865 js.InternalObject(rselects[c].val).Call("$set", recvRes.Index(0)) 866 return c, recvRes.Index(1).Bool() 867 } 868 return c, false 869 } 870 871 func DeepEqual(a1, a2 interface{}) bool { 872 i1 := js.InternalObject(a1) 873 i2 := js.InternalObject(a2) 874 if i1 == i2 { 875 return true 876 } 877 if i1 == nil || i2 == nil || i1.Get("constructor") != i2.Get("constructor") { 878 return false 879 } 880 return deepValueEqualJs(ValueOf(a1), ValueOf(a2), nil) 881 } 882 883 func deepValueEqualJs(v1, v2 Value, visited [][2]unsafe.Pointer) bool { 884 if !v1.IsValid() || !v2.IsValid() { 885 return !v1.IsValid() && !v2.IsValid() 886 } 887 if v1.Type() != v2.Type() { 888 return false 889 } 890 if v1.Type() == jsObjectPtr { 891 return unwrapJsObject(jsObjectPtr, v1.object()) == unwrapJsObject(jsObjectPtr, v2.object()) 892 } 893 894 switch v1.Kind() { 895 case Array, Map, Slice, Struct: 896 for _, entry := range visited { 897 if v1.ptr == entry[0] && v2.ptr == entry[1] { 898 return true 899 } 900 } 901 visited = append(visited, [2]unsafe.Pointer{v1.ptr, v2.ptr}) 902 } 903 904 switch v1.Kind() { 905 case Array, Slice: 906 if v1.Kind() == Slice { 907 if v1.IsNil() != v2.IsNil() { 908 return false 909 } 910 if v1.object() == v2.object() { 911 return true 912 } 913 } 914 n := v1.Len() 915 if n != v2.Len() { 916 return false 917 } 918 for i := 0; i < n; i++ { 919 if !deepValueEqualJs(v1.Index(i), v2.Index(i), visited) { 920 return false 921 } 922 } 923 return true 924 case Interface: 925 if v1.IsNil() || v2.IsNil() { 926 return v1.IsNil() && v2.IsNil() 927 } 928 return deepValueEqualJs(v1.Elem(), v2.Elem(), visited) 929 case Ptr: 930 return deepValueEqualJs(v1.Elem(), v2.Elem(), visited) 931 case Struct: 932 n := v1.NumField() 933 for i := 0; i < n; i++ { 934 if !deepValueEqualJs(v1.Field(i), v2.Field(i), visited) { 935 return false 936 } 937 } 938 return true 939 case Map: 940 if v1.IsNil() != v2.IsNil() { 941 return false 942 } 943 if v1.object() == v2.object() { 944 return true 945 } 946 keys := v1.MapKeys() 947 if len(keys) != v2.Len() { 948 return false 949 } 950 for _, k := range keys { 951 val1 := v1.MapIndex(k) 952 val2 := v2.MapIndex(k) 953 if !val1.IsValid() || !val2.IsValid() || !deepValueEqualJs(val1, val2, visited) { 954 return false 955 } 956 } 957 return true 958 case Func: 959 return v1.IsNil() && v2.IsNil() 960 case UnsafePointer: 961 return v1.object() == v2.object() 962 } 963 964 return js.Global.Call("$interfaceIsEqual", js.InternalObject(valueInterface(v1)), js.InternalObject(valueInterface(v2))).Bool() 965 }