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  }