1// +build js
2
3package reflect
4
5import (
6	"errors"
7	"strconv"
8	"unsafe"
9
10	"github.com/gopherjs/gopherjs/js"
11)
12
13var initialized = false
14
15func init() {
16	// avoid dead code elimination
17	used := func(i interface{}) {}
18	used(rtype{})
19	used(uncommonType{})
20	used(method{})
21	used(arrayType{})
22	used(chanType{})
23	used(funcType{})
24	used(interfaceType{})
25	used(mapType{})
26	used(ptrType{})
27	used(sliceType{})
28	used(structType{})
29	used(imethod{})
30	used(structField{})
31
32	initialized = true
33	uint8Type = TypeOf(uint8(0)).(*rtype) // set for real
34}
35
36func jsType(typ Type) *js.Object {
37	return js.InternalObject(typ).Get("jsType")
38}
39
40func reflectType(typ *js.Object) *rtype {
41	if typ.Get("reflectType") == js.Undefined {
42		rt := &rtype{
43			size: uintptr(typ.Get("size").Int()),
44			kind: uint8(typ.Get("kind").Int()),
45			str:  newNameOff(newName(internalStr(typ.Get("string")), "", typ.Get("exported").Bool())),
46		}
47		js.InternalObject(rt).Set("jsType", typ)
48		typ.Set("reflectType", js.InternalObject(rt))
49
50		methodSet := js.Global.Call("$methodSet", typ)
51		if methodSet.Length() != 0 || typ.Get("named").Bool() {
52			rt.tflag |= tflagUncommon
53			if typ.Get("named").Bool() {
54				rt.tflag |= tflagNamed
55			}
56			var reflectMethods []method
57			for i := 0; i < methodSet.Length(); i++ { // Exported methods first.
58				m := methodSet.Index(i)
59				exported := internalStr(m.Get("pkg")) == ""
60				if !exported {
61					continue
62				}
63				reflectMethods = append(reflectMethods, method{
64					name: newNameOff(newName(internalStr(m.Get("name")), "", exported)),
65					mtyp: newTypeOff(reflectType(m.Get("typ"))),
66				})
67			}
68			xcount := uint16(len(reflectMethods))
69			for i := 0; i < methodSet.Length(); i++ { // Unexported methods second.
70				m := methodSet.Index(i)
71				exported := internalStr(m.Get("pkg")) == ""
72				if exported {
73					continue
74				}
75				reflectMethods = append(reflectMethods, method{
76					name: newNameOff(newName(internalStr(m.Get("name")), "", exported)),
77					mtyp: newTypeOff(reflectType(m.Get("typ"))),
78				})
79			}
80			ut := &uncommonType{
81				pkgPath:  newNameOff(newName(internalStr(typ.Get("pkg")), "", false)),
82				mcount:   uint16(methodSet.Length()),
83				xcount:   xcount,
84				_methods: reflectMethods,
85			}
86			uncommonTypeMap[rt] = ut
87			js.InternalObject(ut).Set("jsType", typ)
88		}
89
90		switch rt.Kind() {
91		case Array:
92			setKindType(rt, &arrayType{
93				elem: reflectType(typ.Get("elem")),
94				len:  uintptr(typ.Get("len").Int()),
95			})
96		case Chan:
97			dir := BothDir
98			if typ.Get("sendOnly").Bool() {
99				dir = SendDir
100			}
101			if typ.Get("recvOnly").Bool() {
102				dir = RecvDir
103			}
104			setKindType(rt, &chanType{
105				elem: reflectType(typ.Get("elem")),
106				dir:  uintptr(dir),
107			})
108		case Func:
109			params := typ.Get("params")
110			in := make([]*rtype, params.Length())
111			for i := range in {
112				in[i] = reflectType(params.Index(i))
113			}
114			results := typ.Get("results")
115			out := make([]*rtype, results.Length())
116			for i := range out {
117				out[i] = reflectType(results.Index(i))
118			}
119			outCount := uint16(results.Length())
120			if typ.Get("variadic").Bool() {
121				outCount |= 1 << 15
122			}
123			setKindType(rt, &funcType{
124				rtype:    *rt,
125				inCount:  uint16(params.Length()),
126				outCount: outCount,
127				_in:      in,
128				_out:     out,
129			})
130		case Interface:
131			methods := typ.Get("methods")
132			imethods := make([]imethod, methods.Length())
133			for i := range imethods {
134				m := methods.Index(i)
135				imethods[i] = imethod{
136					name: newNameOff(newName(internalStr(m.Get("name")), "", internalStr(m.Get("pkg")) == "")),
137					typ:  newTypeOff(reflectType(m.Get("typ"))),
138				}
139			}
140			setKindType(rt, &interfaceType{
141				rtype:   *rt,
142				pkgPath: newName(internalStr(typ.Get("pkg")), "", false),
143				methods: imethods,
144			})
145		case Map:
146			setKindType(rt, &mapType{
147				key:  reflectType(typ.Get("key")),
148				elem: reflectType(typ.Get("elem")),
149			})
150		case Ptr:
151			setKindType(rt, &ptrType{
152				elem: reflectType(typ.Get("elem")),
153			})
154		case Slice:
155			setKindType(rt, &sliceType{
156				elem: reflectType(typ.Get("elem")),
157			})
158		case Struct:
159			fields := typ.Get("fields")
160			reflectFields := make([]structField, fields.Length())
161			for i := range reflectFields {
162				f := fields.Index(i)
163				offsetEmbed := uintptr(i) << 1
164				if f.Get("embedded").Bool() {
165					offsetEmbed |= 1
166				}
167				reflectFields[i] = structField{
168					name:        newName(internalStr(f.Get("name")), internalStr(f.Get("tag")), f.Get("exported").Bool()),
169					typ:         reflectType(f.Get("typ")),
170					offsetEmbed: offsetEmbed,
171				}
172			}
173			setKindType(rt, &structType{
174				rtype:   *rt,
175				pkgPath: newName(internalStr(typ.Get("pkgPath")), "", false),
176				fields:  reflectFields,
177			})
178		}
179	}
180
181	return (*rtype)(unsafe.Pointer(typ.Get("reflectType").Unsafe()))
182}
183
184func setKindType(rt *rtype, kindType interface{}) {
185	js.InternalObject(rt).Set("kindType", js.InternalObject(kindType))
186	js.InternalObject(kindType).Set("rtype", js.InternalObject(rt))
187}
188
189type uncommonType struct {
190	pkgPath nameOff
191	mcount  uint16
192	xcount  uint16
193	moff    uint32
194
195	_methods []method
196}
197
198func (t *uncommonType) methods() []method {
199	return t._methods
200}
201
202func (t *uncommonType) exportedMethods() []method {
203	return t._methods[:t.xcount:t.xcount]
204}
205
206var uncommonTypeMap = make(map[*rtype]*uncommonType)
207
208func (t *rtype) uncommon() *uncommonType {
209	return uncommonTypeMap[t]
210}
211
212type funcType struct {
213	rtype    `reflect:"func"`
214	inCount  uint16
215	outCount uint16
216
217	_in  []*rtype
218	_out []*rtype
219}
220
221func (t *funcType) in() []*rtype {
222	return t._in
223}
224
225func (t *funcType) out() []*rtype {
226	return t._out
227}
228
229type name struct {
230	bytes *byte
231}
232
233type nameData struct {
234	name     string
235	tag      string
236	exported bool
237}
238
239var nameMap = make(map[*byte]*nameData)
240
241func (n name) name() (s string) { return nameMap[n.bytes].name }
242func (n name) tag() (s string)  { return nameMap[n.bytes].tag }
243func (n name) pkgPath() string  { return "" }
244func (n name) isExported() bool { return nameMap[n.bytes].exported }
245
246func newName(n, tag string, exported bool) name {
247	b := new(byte)
248	nameMap[b] = &nameData{
249		name:     n,
250		tag:      tag,
251		exported: exported,
252	}
253	return name{
254		bytes: b,
255	}
256}
257
258var nameOffList []name
259
260func (t *rtype) nameOff(off nameOff) name {
261	return nameOffList[int(off)]
262}
263
264func newNameOff(n name) nameOff {
265	i := len(nameOffList)
266	nameOffList = append(nameOffList, n)
267	return nameOff(i)
268}
269
270var typeOffList []*rtype
271
272func (t *rtype) typeOff(off typeOff) *rtype {
273	return typeOffList[int(off)]
274}
275
276func newTypeOff(t *rtype) typeOff {
277	i := len(typeOffList)
278	typeOffList = append(typeOffList, t)
279	return typeOff(i)
280}
281
282func internalStr(strObj *js.Object) string {
283	var c struct{ str string }
284	js.InternalObject(c).Set("str", strObj) // get string without internalizing
285	return c.str
286}
287
288func isWrapped(typ Type) bool {
289	return jsType(typ).Get("wrapped").Bool()
290}
291
292func copyStruct(dst, src *js.Object, typ Type) {
293	fields := jsType(typ).Get("fields")
294	for i := 0; i < fields.Length(); i++ {
295		prop := fields.Index(i).Get("prop").String()
296		dst.Set(prop, src.Get(prop))
297	}
298}
299
300func makeValue(t Type, v *js.Object, fl flag) Value {
301	rt := t.common()
302	if t.Kind() == Array || t.Kind() == Struct || t.Kind() == Ptr {
303		return Value{rt, unsafe.Pointer(v.Unsafe()), fl | flag(t.Kind())}
304	}
305	return Value{rt, unsafe.Pointer(js.Global.Call("$newDataPointer", v, jsType(rt.ptrTo())).Unsafe()), fl | flag(t.Kind()) | flagIndir}
306}
307
308func MakeSlice(typ Type, len, cap int) Value {
309	if typ.Kind() != Slice {
310		panic("reflect.MakeSlice of non-slice type")
311	}
312	if len < 0 {
313		panic("reflect.MakeSlice: negative len")
314	}
315	if cap < 0 {
316		panic("reflect.MakeSlice: negative cap")
317	}
318	if len > cap {
319		panic("reflect.MakeSlice: len > cap")
320	}
321
322	return makeValue(typ, js.Global.Call("$makeSlice", jsType(typ), len, cap, js.InternalObject(func() *js.Object { return jsType(typ.Elem()).Call("zero") })), 0)
323}
324
325func TypeOf(i interface{}) Type {
326	if !initialized { // avoid error of uint8Type
327		return &rtype{}
328	}
329	if i == nil {
330		return nil
331	}
332	return reflectType(js.InternalObject(i).Get("constructor"))
333}
334
335func ValueOf(i interface{}) Value {
336	if i == nil {
337		return Value{}
338	}
339	return makeValue(reflectType(js.InternalObject(i).Get("constructor")), js.InternalObject(i).Get("$val"), 0)
340}
341
342func ArrayOf(count int, elem Type) Type {
343	return reflectType(js.Global.Call("$arrayType", jsType(elem), count))
344}
345
346func ChanOf(dir ChanDir, t Type) Type {
347	return reflectType(js.Global.Call("$chanType", jsType(t), dir == SendDir, dir == RecvDir))
348}
349
350func FuncOf(in, out []Type, variadic bool) Type {
351	if variadic && (len(in) == 0 || in[len(in)-1].Kind() != Slice) {
352		panic("reflect.FuncOf: last arg of variadic func must be slice")
353	}
354
355	jsIn := make([]*js.Object, len(in))
356	for i, v := range in {
357		jsIn[i] = jsType(v)
358	}
359	jsOut := make([]*js.Object, len(out))
360	for i, v := range out {
361		jsOut[i] = jsType(v)
362	}
363	return reflectType(js.Global.Call("$funcType", jsIn, jsOut, variadic))
364}
365
366func MapOf(key, elem Type) Type {
367	switch key.Kind() {
368	case Func, Map, Slice:
369		panic("reflect.MapOf: invalid key type " + key.String())
370	}
371
372	return reflectType(js.Global.Call("$mapType", jsType(key), jsType(elem)))
373}
374
375func (t *rtype) ptrTo() *rtype {
376	return reflectType(js.Global.Call("$ptrType", jsType(t)))
377}
378
379func SliceOf(t Type) Type {
380	return reflectType(js.Global.Call("$sliceType", jsType(t)))
381}
382
383// func StructOf(fields []StructField) Type {
384// 	jsFields := make([]*js.Object, len(fields))
385// 	fset := map[string]struct{}{}
386// 	for i, f := range fields {
387// 		if f.Type == nil {
388// 			panic("reflect.StructOf: field " + strconv.Itoa(i) + " has no type")
389// 		}
390
391// 		name := f.Name
392// 		if name == "" {
393// 			// Embedded field
394// 			if f.Type.Kind() == Ptr {
395// 				// Embedded ** and *interface{} are illegal
396// 				elem := f.Type.Elem()
397// 				if k := elem.Kind(); k == Ptr || k == Interface {
398// 					panic("reflect.StructOf: illegal anonymous field type " + f.Type.String())
399// 				}
400// 				name = elem.String()
401// 			} else {
402// 				name = f.Type.String()
403// 			}
404// 		}
405
406// 		if _, dup := fset[name]; dup {
407// 			panic("reflect.StructOf: duplicate field " + name)
408// 		}
409// 		fset[name] = struct{}{}
410
411// 		jsf := js.Global.Get("Object").New()
412// 		jsf.Set("prop", name)
413// 		jsf.Set("name", name)
414// 		jsf.Set("exported", true)
415// 		jsf.Set("typ", jsType(f.Type))
416// 		jsf.Set("tag", f.Tag)
417// 		jsFields[i] = jsf
418// 	}
419// 	return reflectType(js.Global.Call("$structType", "", jsFields))
420// }
421
422func Zero(typ Type) Value {
423	return makeValue(typ, jsType(typ).Call("zero"), 0)
424}
425
426func unsafe_New(typ *rtype) unsafe.Pointer {
427	switch typ.Kind() {
428	case Struct:
429		return unsafe.Pointer(jsType(typ).Get("ptr").New().Unsafe())
430	case Array:
431		return unsafe.Pointer(jsType(typ).Call("zero").Unsafe())
432	default:
433		return unsafe.Pointer(js.Global.Call("$newDataPointer", jsType(typ).Call("zero"), jsType(typ.ptrTo())).Unsafe())
434	}
435}
436
437func makeInt(f flag, bits uint64, t Type) Value {
438	typ := t.common()
439	ptr := unsafe_New(typ)
440	switch typ.Kind() {
441	case Int8:
442		*(*int8)(ptr) = int8(bits)
443	case Int16:
444		*(*int16)(ptr) = int16(bits)
445	case Int, Int32:
446		*(*int32)(ptr) = int32(bits)
447	case Int64:
448		*(*int64)(ptr) = int64(bits)
449	case Uint8:
450		*(*uint8)(ptr) = uint8(bits)
451	case Uint16:
452		*(*uint16)(ptr) = uint16(bits)
453	case Uint, Uint32, Uintptr:
454		*(*uint32)(ptr) = uint32(bits)
455	case Uint64:
456		*(*uint64)(ptr) = uint64(bits)
457	}
458	return Value{typ, ptr, f | flagIndir | flag(typ.Kind())}
459}
460
461func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value {
462	if typ.Kind() != Func {
463		panic("reflect: call of MakeFunc with non-Func type")
464	}
465
466	t := typ.common()
467	ftyp := (*funcType)(unsafe.Pointer(t))
468
469	fv := js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} {
470		args := make([]Value, ftyp.NumIn())
471		for i := range args {
472			argType := ftyp.In(i).common()
473			args[i] = makeValue(argType, arguments[i], 0)
474		}
475		resultsSlice := fn(args)
476		switch ftyp.NumOut() {
477		case 0:
478			return nil
479		case 1:
480			return resultsSlice[0].object()
481		default:
482			results := js.Global.Get("Array").New(ftyp.NumOut())
483			for i, r := range resultsSlice {
484				results.SetIndex(i, r.object())
485			}
486			return results
487		}
488	})
489
490	return Value{t, unsafe.Pointer(fv.Unsafe()), flag(Func)}
491}
492
493func typedmemmove(t *rtype, dst, src unsafe.Pointer) {
494	js.InternalObject(dst).Call("$set", js.InternalObject(src).Call("$get"))
495}
496
497func loadScalar(p unsafe.Pointer, n uintptr) uintptr {
498	return js.InternalObject(p).Call("$get").Unsafe()
499}
500
501func makechan(typ *rtype, size int) (ch unsafe.Pointer) {
502	ctyp := (*chanType)(unsafe.Pointer(typ))
503	return unsafe.Pointer(js.Global.Get("$Chan").New(jsType(ctyp.elem), size).Unsafe())
504}
505
506func makemap(t *rtype, cap int) (m unsafe.Pointer) {
507	return unsafe.Pointer(js.Global.Get("Object").New().Unsafe())
508}
509
510func keyFor(t *rtype, key unsafe.Pointer) (*js.Object, string) {
511	kv := js.InternalObject(key)
512	if kv.Get("$get") != js.Undefined {
513		kv = kv.Call("$get")
514	}
515	k := jsType(t.Key()).Call("keyFor", kv).String()
516	return kv, k
517}
518
519func mapaccess(t *rtype, m, key unsafe.Pointer) unsafe.Pointer {
520	_, k := keyFor(t, key)
521	entry := js.InternalObject(m).Get(k)
522	if entry == js.Undefined {
523		return nil
524	}
525	return unsafe.Pointer(js.Global.Call("$newDataPointer", entry.Get("v"), jsType(PtrTo(t.Elem()))).Unsafe())
526}
527
528func mapassign(t *rtype, m, key, val unsafe.Pointer) {
529	kv, k := keyFor(t, key)
530	jsVal := js.InternalObject(val).Call("$get")
531	et := t.Elem()
532	if et.Kind() == Struct {
533		newVal := jsType(et).Call("zero")
534		copyStruct(newVal, jsVal, et)
535		jsVal = newVal
536	}
537	entry := js.Global.Get("Object").New()
538	entry.Set("k", kv)
539	entry.Set("v", jsVal)
540	js.InternalObject(m).Set(k, entry)
541}
542
543func mapdelete(t *rtype, m unsafe.Pointer, key unsafe.Pointer) {
544	_, k := keyFor(t, key)
545	js.InternalObject(m).Delete(k)
546}
547
548type mapIter struct {
549	t    Type
550	m    *js.Object
551	keys *js.Object
552	i    int
553
554	// last is the last object the iterator indicates. If this object exists, the functions that return the
555	// current key or value returns this object, regardless of the current iterator. It is because the current
556	// iterator might be stale due to key deletion in a loop.
557	last *js.Object
558}
559
560func (iter *mapIter) skipUntilValidKey() {
561	for iter.i < iter.keys.Length() {
562		k := iter.keys.Index(iter.i)
563		if iter.m.Get(k.String()) != js.Undefined {
564			break
565		}
566		// The key is already deleted. Move on the next item.
567		iter.i++
568	}
569}
570
571func mapiterinit(t *rtype, m unsafe.Pointer) unsafe.Pointer {
572	return unsafe.Pointer(&mapIter{t, js.InternalObject(m), js.Global.Call("$keys", js.InternalObject(m)), 0, nil})
573}
574
575func mapiterkey(it unsafe.Pointer) unsafe.Pointer {
576	iter := (*mapIter)(it)
577	var kv *js.Object
578	if iter.last != nil {
579		kv = iter.last
580	} else {
581		iter.skipUntilValidKey()
582		if iter.i == iter.keys.Length() {
583			return nil
584		}
585		k := iter.keys.Index(iter.i)
586		kv = iter.m.Get(k.String())
587
588		// Record the key-value pair for later accesses.
589		iter.last = kv
590	}
591	return unsafe.Pointer(js.Global.Call("$newDataPointer", kv.Get("k"), jsType(PtrTo(iter.t.Key()))).Unsafe())
592}
593
594func mapitervalue(it unsafe.Pointer) unsafe.Pointer {
595	iter := (*mapIter)(it)
596	var kv *js.Object
597	if iter.last != nil {
598		kv = iter.last
599	} else {
600		iter.skipUntilValidKey()
601		if iter.i == iter.keys.Length() {
602			return nil
603		}
604		k := iter.keys.Index(iter.i)
605		kv = iter.m.Get(k.String())
606		iter.last = kv
607	}
608	return unsafe.Pointer(js.Global.Call("$newDataPointer", kv.Get("v"), jsType(PtrTo(iter.t.Elem()))).Unsafe())
609}
610
611func mapiternext(it unsafe.Pointer) {
612	iter := (*mapIter)(it)
613	iter.last = nil
614	iter.i++
615}
616
617func maplen(m unsafe.Pointer) int {
618	return js.Global.Call("$keys", js.InternalObject(m)).Length()
619}
620
621func cvtDirect(v Value, typ Type) Value {
622	var srcVal = v.object()
623	if srcVal == jsType(v.typ).Get("nil") {
624		return makeValue(typ, jsType(typ).Get("nil"), v.flag)
625	}
626
627	var val *js.Object
628	switch k := typ.Kind(); k {
629	case Slice:
630		slice := jsType(typ).New(srcVal.Get("$array"))
631		slice.Set("$offset", srcVal.Get("$offset"))
632		slice.Set("$length", srcVal.Get("$length"))
633		slice.Set("$capacity", srcVal.Get("$capacity"))
634		val = js.Global.Call("$newDataPointer", slice, jsType(PtrTo(typ)))
635	case Ptr:
636		if typ.Elem().Kind() == Struct {
637			if typ.Elem() == v.typ.Elem() {
638				val = srcVal
639				break
640			}
641			val = jsType(typ).New()
642			copyStruct(val, srcVal, typ.Elem())
643			break
644		}
645		val = jsType(typ).New(srcVal.Get("$get"), srcVal.Get("$set"))
646	case Struct:
647		val = jsType(typ).Get("ptr").New()
648		copyStruct(val, srcVal, typ)
649	case Array, Bool, Chan, Func, Interface, Map, String:
650		val = js.InternalObject(v.ptr)
651	default:
652		panic(&ValueError{"reflect.Convert", k})
653	}
654	return Value{typ.common(), unsafe.Pointer(val.Unsafe()), v.flag.ro() | v.flag&flagIndir | flag(typ.Kind())}
655}
656
657func Copy(dst, src Value) int {
658	dk := dst.kind()
659	if dk != Array && dk != Slice {
660		panic(&ValueError{"reflect.Copy", dk})
661	}
662	if dk == Array {
663		dst.mustBeAssignable()
664	}
665	dst.mustBeExported()
666
667	sk := src.kind()
668	var stringCopy bool
669	if sk != Array && sk != Slice {
670		stringCopy = sk == String && dst.typ.Elem().Kind() == Uint8
671		if !stringCopy {
672			panic(&ValueError{"reflect.Copy", sk})
673		}
674	}
675	src.mustBeExported()
676
677	if !stringCopy {
678		typesMustMatch("reflect.Copy", dst.typ.Elem(), src.typ.Elem())
679	}
680
681	dstVal := dst.object()
682	if dk == Array {
683		dstVal = jsType(SliceOf(dst.typ.Elem())).New(dstVal)
684	}
685
686	srcVal := src.object()
687	if sk == Array {
688		srcVal = jsType(SliceOf(src.typ.Elem())).New(srcVal)
689	}
690
691	if stringCopy {
692		return js.Global.Call("$copyString", dstVal, srcVal).Int()
693	}
694	return js.Global.Call("$copySlice", dstVal, srcVal).Int()
695}
696
697func valueInterface(v Value, safe bool) interface{} {
698	if v.flag == 0 {
699		panic(&ValueError{"reflect.Value.Interface", 0})
700	}
701	if safe && v.flag&flagRO != 0 {
702		panic("reflect.Value.Interface: cannot return value obtained from unexported field or method")
703	}
704	if v.flag&flagMethod != 0 {
705		v = makeMethodValue("Interface", v)
706	}
707
708	if isWrapped(v.typ) {
709		return interface{}(unsafe.Pointer(jsType(v.typ).New(v.object()).Unsafe()))
710	}
711	return interface{}(unsafe.Pointer(v.object().Unsafe()))
712}
713
714func ifaceE2I(t *rtype, src interface{}, dst unsafe.Pointer) {
715	js.InternalObject(dst).Call("$set", js.InternalObject(src))
716}
717
718func methodName() string {
719	return "?FIXME?"
720}
721
722func makeMethodValue(op string, v Value) Value {
723	if v.flag&flagMethod == 0 {
724		panic("reflect: internal error: invalid use of makePartialFunc")
725	}
726
727	_, _, fn := methodReceiver(op, v, int(v.flag)>>flagMethodShift)
728	rcvr := v.object()
729	if isWrapped(v.typ) {
730		rcvr = jsType(v.typ).New(rcvr)
731	}
732	fv := js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} {
733		return js.InternalObject(fn).Call("apply", rcvr, arguments)
734	})
735	return Value{v.Type().common(), unsafe.Pointer(fv.Unsafe()), v.flag.ro() | flag(Func)}
736}
737
738func (t *rtype) pointers() bool {
739	switch t.Kind() {
740	case Ptr, Map, Chan, Func, Struct, Array:
741		return true
742	default:
743		return false
744	}
745}
746
747func (t *rtype) Comparable() bool {
748	switch t.Kind() {
749	case Func, Slice, Map:
750		return false
751	case Array:
752		return t.Elem().Comparable()
753	case Struct:
754		for i := 0; i < t.NumField(); i++ {
755			if !t.Field(i).Type.Comparable() {
756				return false
757			}
758		}
759	}
760	return true
761}
762
763func (t *rtype) Method(i int) (m Method) {
764	if t.Kind() == Interface {
765		tt := (*interfaceType)(unsafe.Pointer(t))
766		return tt.Method(i)
767	}
768	methods := t.exportedMethods()
769	if i < 0 || i >= len(methods) {
770		panic("reflect: Method index out of range")
771	}
772	p := methods[i]
773	pname := t.nameOff(p.name)
774	m.Name = pname.name()
775	fl := flag(Func)
776	mtyp := t.typeOff(p.mtyp)
777	ft := (*funcType)(unsafe.Pointer(mtyp))
778	in := make([]Type, 0, 1+len(ft.in()))
779	in = append(in, t)
780	for _, arg := range ft.in() {
781		in = append(in, arg)
782	}
783	out := make([]Type, 0, len(ft.out()))
784	for _, ret := range ft.out() {
785		out = append(out, ret)
786	}
787	mt := FuncOf(in, out, ft.IsVariadic())
788	m.Type = mt
789	prop := js.Global.Call("$methodSet", js.InternalObject(t).Get("jsType")).Index(i).Get("prop").String()
790	fn := js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} {
791		rcvr := arguments[0]
792		return rcvr.Get(prop).Call("apply", rcvr, arguments[1:])
793	})
794	m.Func = Value{mt.(*rtype), unsafe.Pointer(fn.Unsafe()), fl}
795
796	m.Index = i
797	return m
798}
799
800func (v Value) object() *js.Object {
801	if v.typ.Kind() == Array || v.typ.Kind() == Struct {
802		return js.InternalObject(v.ptr)
803	}
804	if v.flag&flagIndir != 0 {
805		val := js.InternalObject(v.ptr).Call("$get")
806		if val != js.Global.Get("$ifaceNil") && val.Get("constructor") != jsType(v.typ) {
807			switch v.typ.Kind() {
808			case Uint64, Int64:
809				val = jsType(v.typ).New(val.Get("$high"), val.Get("$low"))
810			case Complex64, Complex128:
811				val = jsType(v.typ).New(val.Get("$real"), val.Get("$imag"))
812			case Slice:
813				if val == val.Get("constructor").Get("nil") {
814					val = jsType(v.typ).Get("nil")
815					break
816				}
817				newVal := jsType(v.typ).New(val.Get("$array"))
818				newVal.Set("$offset", val.Get("$offset"))
819				newVal.Set("$length", val.Get("$length"))
820				newVal.Set("$capacity", val.Get("$capacity"))
821				val = newVal
822			}
823		}
824		return js.InternalObject(val.Unsafe())
825	}
826	return js.InternalObject(v.ptr)
827}
828
829func (v Value) assignTo(context string, dst *rtype, target unsafe.Pointer) Value {
830	if v.flag&flagMethod != 0 {
831		v = makeMethodValue(context, v)
832	}
833
834	switch {
835	case directlyAssignable(dst, v.typ):
836		// Overwrite type so that they match.
837		// Same memory layout, so no harm done.
838		fl := v.flag&(flagAddr|flagIndir) | v.flag.ro()
839		fl |= flag(dst.Kind())
840		return Value{dst, v.ptr, fl}
841
842	case implements(dst, v.typ):
843		if target == nil {
844			target = unsafe_New(dst)
845		}
846		// GopherJS: Skip the v.Kind() == Interface && v.IsNil() if statement
847		//           from upstream. ifaceE2I below does not panic, and it needs
848		//           to run, given its custom implementation.
849		x := valueInterface(v, false)
850		if dst.NumMethod() == 0 {
851			*(*interface{})(target) = x
852		} else {
853			ifaceE2I(dst, x, target)
854		}
855		return Value{dst, target, flagIndir | flag(Interface)}
856	}
857
858	// Failed.
859	panic(context + ": value of type " + v.typ.String() + " is not assignable to type " + dst.String())
860}
861
862var callHelper = js.Global.Get("$call").Interface().(func(...interface{}) *js.Object)
863
864func (v Value) Cap() int {
865	k := v.kind()
866	switch k {
867	case Array:
868		return v.typ.Len()
869	case Chan, Slice:
870		return v.object().Get("$capacity").Int()
871	}
872	panic(&ValueError{"reflect.Value.Cap", k})
873}
874
875var jsObjectPtr = reflectType(js.Global.Get("$jsObjectPtr"))
876
877func wrapJsObject(typ Type, val *js.Object) *js.Object {
878	if typ == jsObjectPtr {
879		return jsType(jsObjectPtr).New(val)
880	}
881	return val
882}
883
884func unwrapJsObject(typ Type, val *js.Object) *js.Object {
885	if typ == jsObjectPtr {
886		return val.Get("object")
887	}
888	return val
889}
890
891func (v Value) Elem() Value {
892	switch k := v.kind(); k {
893	case Interface:
894		val := v.object()
895		if val == js.Global.Get("$ifaceNil") {
896			return Value{}
897		}
898		typ := reflectType(val.Get("constructor"))
899		return makeValue(typ, val.Get("$val"), v.flag.ro())
900
901	case Ptr:
902		if v.IsNil() {
903			return Value{}
904		}
905		val := v.object()
906		tt := (*ptrType)(unsafe.Pointer(v.typ))
907		fl := v.flag&flagRO | flagIndir | flagAddr
908		fl |= flag(tt.elem.Kind())
909		return Value{tt.elem, unsafe.Pointer(wrapJsObject(tt.elem, val).Unsafe()), fl}
910
911	default:
912		panic(&ValueError{"reflect.Value.Elem", k})
913	}
914}
915
916func (v Value) Field(i int) Value {
917	if v.kind() != Struct {
918		panic(&ValueError{"reflect.Value.Field", v.kind()})
919	}
920	tt := (*structType)(unsafe.Pointer(v.typ))
921	if uint(i) >= uint(len(tt.fields)) {
922		panic("reflect: Field index out of range")
923	}
924
925	prop := jsType(v.typ).Get("fields").Index(i).Get("prop").String()
926	field := &tt.fields[i]
927	typ := field.typ
928
929	fl := v.flag&(flagStickyRO|flagIndir|flagAddr) | flag(typ.Kind())
930	if !field.name.isExported() {
931		if field.embedded() {
932			fl |= flagEmbedRO
933		} else {
934			fl |= flagStickyRO
935		}
936	}
937
938	if tag := tt.fields[i].name.tag(); tag != "" && i != 0 {
939		if jsTag := getJsTag(tag); jsTag != "" {
940			for {
941				v = v.Field(0)
942				if v.typ == jsObjectPtr {
943					o := v.object().Get("object")
944					return Value{typ, unsafe.Pointer(jsType(PtrTo(typ)).New(
945						js.InternalObject(func() *js.Object { return js.Global.Call("$internalize", o.Get(jsTag), jsType(typ)) }),
946						js.InternalObject(func(x *js.Object) { o.Set(jsTag, js.Global.Call("$externalize", x, jsType(typ))) }),
947					).Unsafe()), fl}
948				}
949				if v.typ.Kind() == Ptr {
950					v = v.Elem()
951				}
952			}
953		}
954	}
955
956	s := js.InternalObject(v.ptr)
957	if fl&flagIndir != 0 && typ.Kind() != Array && typ.Kind() != Struct {
958		return Value{typ, unsafe.Pointer(jsType(PtrTo(typ)).New(
959			js.InternalObject(func() *js.Object { return wrapJsObject(typ, s.Get(prop)) }),
960			js.InternalObject(func(x *js.Object) { s.Set(prop, unwrapJsObject(typ, x)) }),
961		).Unsafe()), fl}
962	}
963	return makeValue(typ, wrapJsObject(typ, s.Get(prop)), fl)
964}
965
966func getJsTag(tag string) string {
967	for tag != "" {
968		// skip leading space
969		i := 0
970		for i < len(tag) && tag[i] == ' ' {
971			i++
972		}
973		tag = tag[i:]
974		if tag == "" {
975			break
976		}
977
978		// scan to colon.
979		// a space or a quote is a syntax error
980		i = 0
981		for i < len(tag) && tag[i] != ' ' && tag[i] != ':' && tag[i] != '"' {
982			i++
983		}
984		if i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' {
985			break
986		}
987		name := string(tag[:i])
988		tag = tag[i+1:]
989
990		// scan quoted string to find value
991		i = 1
992		for i < len(tag) && tag[i] != '"' {
993			if tag[i] == '\\' {
994				i++
995			}
996			i++
997		}
998		if i >= len(tag) {
999			break
1000		}
1001		qvalue := string(tag[:i+1])
1002		tag = tag[i+1:]
1003
1004		if name == "js" {
1005			value, _ := strconv.Unquote(qvalue)
1006			return value
1007		}
1008	}
1009	return ""
1010}
1011
1012func (v Value) Index(i int) Value {
1013	switch k := v.kind(); k {
1014	case Array:
1015		tt := (*arrayType)(unsafe.Pointer(v.typ))
1016		if i < 0 || i > int(tt.len) {
1017			panic("reflect: array index out of range")
1018		}
1019		typ := tt.elem
1020		fl := v.flag&(flagIndir|flagAddr) | v.flag.ro() | flag(typ.Kind())
1021
1022		a := js.InternalObject(v.ptr)
1023		if fl&flagIndir != 0 && typ.Kind() != Array && typ.Kind() != Struct {
1024			return Value{typ, unsafe.Pointer(jsType(PtrTo(typ)).New(
1025				js.InternalObject(func() *js.Object { return wrapJsObject(typ, a.Index(i)) }),
1026				js.InternalObject(func(x *js.Object) { a.SetIndex(i, unwrapJsObject(typ, x)) }),
1027			).Unsafe()), fl}
1028		}
1029		return makeValue(typ, wrapJsObject(typ, a.Index(i)), fl)
1030
1031	case Slice:
1032		s := v.object()
1033		if i < 0 || i >= s.Get("$length").Int() {
1034			panic("reflect: slice index out of range")
1035		}
1036		tt := (*sliceType)(unsafe.Pointer(v.typ))
1037		typ := tt.elem
1038		fl := flagAddr | flagIndir | v.flag.ro() | flag(typ.Kind())
1039
1040		i += s.Get("$offset").Int()
1041		a := s.Get("$array")
1042		if fl&flagIndir != 0 && typ.Kind() != Array && typ.Kind() != Struct {
1043			return Value{typ, unsafe.Pointer(jsType(PtrTo(typ)).New(
1044				js.InternalObject(func() *js.Object { return wrapJsObject(typ, a.Index(i)) }),
1045				js.InternalObject(func(x *js.Object) { a.SetIndex(i, unwrapJsObject(typ, x)) }),
1046			).Unsafe()), fl}
1047		}
1048		return makeValue(typ, wrapJsObject(typ, a.Index(i)), fl)
1049
1050	case String:
1051		str := *(*string)(v.ptr)
1052		if i < 0 || i >= len(str) {
1053			panic("reflect: string index out of range")
1054		}
1055		fl := v.flag.ro() | flag(Uint8) | flagIndir
1056		c := str[i]
1057		return Value{uint8Type, unsafe.Pointer(&c), fl}
1058
1059	default:
1060		panic(&ValueError{"reflect.Value.Index", k})
1061	}
1062}
1063
1064func (v Value) InterfaceData() [2]uintptr {
1065	panic(errors.New("InterfaceData is not supported by GopherJS"))
1066}
1067
1068func (v Value) IsNil() bool {
1069	switch k := v.kind(); k {
1070	case Ptr, Slice:
1071		return v.object() == jsType(v.typ).Get("nil")
1072	case Chan:
1073		return v.object() == js.Global.Get("$chanNil")
1074	case Func:
1075		return v.object() == js.Global.Get("$throwNilPointerError")
1076	case Map:
1077		return v.object() == js.InternalObject(false)
1078	case Interface:
1079		return v.object() == js.Global.Get("$ifaceNil")
1080	case UnsafePointer:
1081		return v.object().Unsafe() == 0
1082	default:
1083		panic(&ValueError{"reflect.Value.IsNil", k})
1084	}
1085}
1086
1087func (v Value) Len() int {
1088	switch k := v.kind(); k {
1089	case Array, String:
1090		return v.object().Length()
1091	case Slice:
1092		return v.object().Get("$length").Int()
1093	case Chan:
1094		return v.object().Get("$buffer").Get("length").Int()
1095	case Map:
1096		return js.Global.Call("$keys", v.object()).Length()
1097	default:
1098		panic(&ValueError{"reflect.Value.Len", k})
1099	}
1100}
1101
1102func (v Value) Pointer() uintptr {
1103	switch k := v.kind(); k {
1104	case Chan, Map, Ptr, UnsafePointer:
1105		if v.IsNil() {
1106			return 0
1107		}
1108		return v.object().Unsafe()
1109	case Func:
1110		if v.IsNil() {
1111			return 0
1112		}
1113		return 1
1114	case Slice:
1115		if v.IsNil() {
1116			return 0
1117		}
1118		return v.object().Get("$array").Unsafe()
1119	default:
1120		panic(&ValueError{"reflect.Value.Pointer", k})
1121	}
1122}
1123
1124func (v Value) Set(x Value) {
1125	v.mustBeAssignable()
1126	x.mustBeExported()
1127	x = x.assignTo("reflect.Set", v.typ, nil)
1128	if v.flag&flagIndir != 0 {
1129		switch v.typ.Kind() {
1130		case Array:
1131			jsType(v.typ).Call("copy", js.InternalObject(v.ptr), js.InternalObject(x.ptr))
1132		case Interface:
1133			js.InternalObject(v.ptr).Call("$set", js.InternalObject(valueInterface(x, false)))
1134		case Struct:
1135			copyStruct(js.InternalObject(v.ptr), js.InternalObject(x.ptr), v.typ)
1136		default:
1137			js.InternalObject(v.ptr).Call("$set", x.object())
1138		}
1139		return
1140	}
1141	v.ptr = x.ptr
1142}
1143
1144func (v Value) SetBytes(x []byte) {
1145	v.mustBeAssignable()
1146	v.mustBe(Slice)
1147	if v.typ.Elem().Kind() != Uint8 {
1148		panic("reflect.Value.SetBytes of non-byte slice")
1149	}
1150	slice := js.InternalObject(x)
1151	if v.typ.Name() != "" || v.typ.Elem().Name() != "" {
1152		typedSlice := jsType(v.typ).New(slice.Get("$array"))
1153		typedSlice.Set("$offset", slice.Get("$offset"))
1154		typedSlice.Set("$length", slice.Get("$length"))
1155		typedSlice.Set("$capacity", slice.Get("$capacity"))
1156		slice = typedSlice
1157	}
1158	js.InternalObject(v.ptr).Call("$set", slice)
1159}
1160
1161func (v Value) SetCap(n int) {
1162	v.mustBeAssignable()
1163	v.mustBe(Slice)
1164	s := js.InternalObject(v.ptr).Call("$get")
1165	if n < s.Get("$length").Int() || n > s.Get("$capacity").Int() {
1166		panic("reflect: slice capacity out of range in SetCap")
1167	}
1168	newSlice := jsType(v.typ).New(s.Get("$array"))
1169	newSlice.Set("$offset", s.Get("$offset"))
1170	newSlice.Set("$length", s.Get("$length"))
1171	newSlice.Set("$capacity", n)
1172	js.InternalObject(v.ptr).Call("$set", newSlice)
1173}
1174
1175func (v Value) SetLen(n int) {
1176	v.mustBeAssignable()
1177	v.mustBe(Slice)
1178	s := js.InternalObject(v.ptr).Call("$get")
1179	if n < 0 || n > s.Get("$capacity").Int() {
1180		panic("reflect: slice length out of range in SetLen")
1181	}
1182	newSlice := jsType(v.typ).New(s.Get("$array"))
1183	newSlice.Set("$offset", s.Get("$offset"))
1184	newSlice.Set("$length", n)
1185	newSlice.Set("$capacity", s.Get("$capacity"))
1186	js.InternalObject(v.ptr).Call("$set", newSlice)
1187}
1188
1189func (v Value) Slice(i, j int) Value {
1190	var (
1191		cap int
1192		typ Type
1193		s   *js.Object
1194	)
1195	switch kind := v.kind(); kind {
1196	case Array:
1197		if v.flag&flagAddr == 0 {
1198			panic("reflect.Value.Slice: slice of unaddressable array")
1199		}
1200		tt := (*arrayType)(unsafe.Pointer(v.typ))
1201		cap = int(tt.len)
1202		typ = SliceOf(tt.elem)
1203		s = jsType(typ).New(v.object())
1204
1205	case Slice:
1206		typ = v.typ
1207		s = v.object()
1208		cap = s.Get("$capacity").Int()
1209
1210	case String:
1211		str := *(*string)(v.ptr)
1212		if i < 0 || j < i || j > len(str) {
1213			panic("reflect.Value.Slice: string slice index out of bounds")
1214		}
1215		return ValueOf(str[i:j])
1216
1217	default:
1218		panic(&ValueError{"reflect.Value.Slice", kind})
1219	}
1220
1221	if i < 0 || j < i || j > cap {
1222		panic("reflect.Value.Slice: slice index out of bounds")
1223	}
1224
1225	return makeValue(typ, js.Global.Call("$subslice", s, i, j), v.flag.ro())
1226}
1227
1228func (v Value) Slice3(i, j, k int) Value {
1229	var (
1230		cap int
1231		typ Type
1232		s   *js.Object
1233	)
1234	switch kind := v.kind(); kind {
1235	case Array:
1236		if v.flag&flagAddr == 0 {
1237			panic("reflect.Value.Slice: slice of unaddressable array")
1238		}
1239		tt := (*arrayType)(unsafe.Pointer(v.typ))
1240		cap = int(tt.len)
1241		typ = SliceOf(tt.elem)
1242		s = jsType(typ).New(v.object())
1243
1244	case Slice:
1245		typ = v.typ
1246		s = v.object()
1247		cap = s.Get("$capacity").Int()
1248
1249	default:
1250		panic(&ValueError{"reflect.Value.Slice3", kind})
1251	}
1252
1253	if i < 0 || j < i || k < j || k > cap {
1254		panic("reflect.Value.Slice3: slice index out of bounds")
1255	}
1256
1257	return makeValue(typ, js.Global.Call("$subslice", s, i, j, k), v.flag.ro())
1258}
1259
1260func (v Value) Close() {
1261	v.mustBe(Chan)
1262	v.mustBeExported()
1263	js.Global.Call("$close", v.object())
1264}
1265
1266var selectHelper = js.Global.Get("$select").Interface().(func(...interface{}) *js.Object)
1267
1268func chanrecv(ch unsafe.Pointer, nb bool, val unsafe.Pointer) (selected, received bool) {
1269	comms := [][]*js.Object{{js.InternalObject(ch)}}
1270	if nb {
1271		comms = append(comms, []*js.Object{})
1272	}
1273	selectRes := selectHelper(comms)
1274	if nb && selectRes.Index(0).Int() == 1 {
1275		return false, false
1276	}
1277	recvRes := selectRes.Index(1)
1278	js.InternalObject(val).Call("$set", recvRes.Index(0))
1279	return true, recvRes.Index(1).Bool()
1280}
1281
1282func chansend(ch unsafe.Pointer, val unsafe.Pointer, nb bool) bool {
1283	comms := [][]*js.Object{{js.InternalObject(ch), js.InternalObject(val).Call("$get")}}
1284	if nb {
1285		comms = append(comms, []*js.Object{})
1286	}
1287	selectRes := selectHelper(comms)
1288	if nb && selectRes.Index(0).Int() == 1 {
1289		return false
1290	}
1291	return true
1292}
1293
1294func rselect(rselects []runtimeSelect) (chosen int, recvOK bool) {
1295	comms := make([][]*js.Object, len(rselects))
1296	for i, s := range rselects {
1297		switch SelectDir(s.dir) {
1298		case SelectDefault:
1299			comms[i] = []*js.Object{}
1300		case SelectRecv:
1301			ch := js.Global.Get("$chanNil")
1302			if js.InternalObject(s.ch) != js.InternalObject(0) {
1303				ch = js.InternalObject(s.ch)
1304			}
1305			comms[i] = []*js.Object{ch}
1306		case SelectSend:
1307			ch := js.Global.Get("$chanNil")
1308			var val *js.Object
1309			if js.InternalObject(s.ch) != js.InternalObject(0) {
1310				ch = js.InternalObject(s.ch)
1311				val = js.InternalObject(s.val).Call("$get")
1312			}
1313			comms[i] = []*js.Object{ch, val}
1314		}
1315	}
1316	selectRes := selectHelper(comms)
1317	c := selectRes.Index(0).Int()
1318	if SelectDir(rselects[c].dir) == SelectRecv {
1319		recvRes := selectRes.Index(1)
1320		js.InternalObject(rselects[c].val).Call("$set", recvRes.Index(0))
1321		return c, recvRes.Index(1).Bool()
1322	}
1323	return c, false
1324}
1325
1326func DeepEqual(a1, a2 interface{}) bool {
1327	i1 := js.InternalObject(a1)
1328	i2 := js.InternalObject(a2)
1329	if i1 == i2 {
1330		return true
1331	}
1332	if i1 == nil || i2 == nil || i1.Get("constructor") != i2.Get("constructor") {
1333		return false
1334	}
1335	return deepValueEqualJs(ValueOf(a1), ValueOf(a2), nil)
1336}
1337
1338func deepValueEqualJs(v1, v2 Value, visited [][2]unsafe.Pointer) bool {
1339	if !v1.IsValid() || !v2.IsValid() {
1340		return !v1.IsValid() && !v2.IsValid()
1341	}
1342	if v1.Type() != v2.Type() {
1343		return false
1344	}
1345	if v1.Type() == jsObjectPtr {
1346		return unwrapJsObject(jsObjectPtr, v1.object()) == unwrapJsObject(jsObjectPtr, v2.object())
1347	}
1348
1349	switch v1.Kind() {
1350	case Array, Map, Slice, Struct:
1351		for _, entry := range visited {
1352			if v1.ptr == entry[0] && v2.ptr == entry[1] {
1353				return true
1354			}
1355		}
1356		visited = append(visited, [2]unsafe.Pointer{v1.ptr, v2.ptr})
1357	}
1358
1359	switch v1.Kind() {
1360	case Array, Slice:
1361		if v1.Kind() == Slice {
1362			if v1.IsNil() != v2.IsNil() {
1363				return false
1364			}
1365			if v1.object() == v2.object() {
1366				return true
1367			}
1368		}
1369		var n = v1.Len()
1370		if n != v2.Len() {
1371			return false
1372		}
1373		for i := 0; i < n; i++ {
1374			if !deepValueEqualJs(v1.Index(i), v2.Index(i), visited) {
1375				return false
1376			}
1377		}
1378		return true
1379	case Interface:
1380		if v1.IsNil() || v2.IsNil() {
1381			return v1.IsNil() && v2.IsNil()
1382		}
1383		return deepValueEqualJs(v1.Elem(), v2.Elem(), visited)
1384	case Ptr:
1385		return deepValueEqualJs(v1.Elem(), v2.Elem(), visited)
1386	case Struct:
1387		var n = v1.NumField()
1388		for i := 0; i < n; i++ {
1389			if !deepValueEqualJs(v1.Field(i), v2.Field(i), visited) {
1390				return false
1391			}
1392		}
1393		return true
1394	case Map:
1395		if v1.IsNil() != v2.IsNil() {
1396			return false
1397		}
1398		if v1.object() == v2.object() {
1399			return true
1400		}
1401		var keys = v1.MapKeys()
1402		if len(keys) != v2.Len() {
1403			return false
1404		}
1405		for _, k := range keys {
1406			val1 := v1.MapIndex(k)
1407			val2 := v2.MapIndex(k)
1408			if !val1.IsValid() || !val2.IsValid() || !deepValueEqualJs(val1, val2, visited) {
1409				return false
1410			}
1411		}
1412		return true
1413	case Func:
1414		return v1.IsNil() && v2.IsNil()
1415	case UnsafePointer:
1416		return v1.object() == v2.object()
1417	}
1418
1419	return js.Global.Call("$interfaceIsEqual", js.InternalObject(valueInterface(v1, false)), js.InternalObject(valueInterface(v2, false))).Bool()
1420}
1421