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

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