github.com/gopherjs/gopherjs@v1.19.0-beta1.0.20240506212314-27071a8796e4/compiler/natives/src/reflect/reflect.go (about)

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