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