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