1package goja
2
3import (
4	"math"
5	"sort"
6)
7
8func (r *Runtime) newArray(prototype *Object) (a *arrayObject) {
9	v := &Object{runtime: r}
10
11	a = &arrayObject{}
12	a.class = classArray
13	a.val = v
14	a.extensible = true
15	v.self = a
16	a.prototype = prototype
17	a.init()
18	return
19}
20
21func (r *Runtime) newArrayObject() *arrayObject {
22	return r.newArray(r.global.ArrayPrototype)
23}
24
25func setArrayValues(a *arrayObject, values []Value) *arrayObject {
26	a.values = values
27	a.length = uint32(len(values))
28	a.objCount = len(values)
29	return a
30}
31
32func setArrayLength(a *arrayObject, l int64) *arrayObject {
33	a.setOwnStr("length", intToValue(l), true)
34	return a
35}
36
37func arraySpeciesCreate(obj *Object, size int64) *Object {
38	if isArray(obj) {
39		v := obj.self.getStr("constructor", nil)
40		if constructObj, ok := v.(*Object); ok {
41			v = constructObj.self.getSym(SymSpecies, nil)
42			if v == _null {
43				v = nil
44			}
45		}
46
47		if v != nil && v != _undefined {
48			constructObj, _ := v.(*Object)
49			if constructObj != nil {
50				if constructor := constructObj.self.assertConstructor(); constructor != nil {
51					return constructor([]Value{intToValue(size)}, constructObj)
52				}
53			}
54			panic(obj.runtime.NewTypeError("Species is not a constructor"))
55		}
56	}
57	return obj.runtime.newArrayLength(size)
58}
59
60func max(a, b int64) int64 {
61	if a > b {
62		return a
63	}
64	return b
65}
66
67func min(a, b int64) int64 {
68	if a < b {
69		return a
70	}
71	return b
72}
73
74func relToIdx(rel, l int64) int64 {
75	if rel >= 0 {
76		return min(rel, l)
77	}
78	return max(l+rel, 0)
79}
80
81func (r *Runtime) newArrayValues(values []Value) *Object {
82	return setArrayValues(r.newArrayObject(), values).val
83}
84
85func (r *Runtime) newArrayLength(l int64) *Object {
86	return setArrayLength(r.newArrayObject(), l).val
87}
88
89func (r *Runtime) builtin_newArray(args []Value, proto *Object) *Object {
90	l := len(args)
91	if l == 1 {
92		if al, ok := args[0].(valueInt); ok {
93			return setArrayLength(r.newArray(proto), int64(al)).val
94		} else if f, ok := args[0].(valueFloat); ok {
95			al := int64(f)
96			if float64(al) == float64(f) {
97				return r.newArrayLength(al)
98			} else {
99				panic(r.newError(r.global.RangeError, "Invalid array length"))
100			}
101		}
102		return setArrayValues(r.newArray(proto), []Value{args[0]}).val
103	} else {
104		argsCopy := make([]Value, l)
105		copy(argsCopy, args)
106		return setArrayValues(r.newArray(proto), argsCopy).val
107	}
108}
109
110func (r *Runtime) generic_push(obj *Object, call FunctionCall) Value {
111	l := toLength(obj.self.getStr("length", nil))
112	nl := l + int64(len(call.Arguments))
113	if nl >= maxInt {
114		r.typeErrorResult(true, "Invalid array length")
115		panic("unreachable")
116	}
117	for i, arg := range call.Arguments {
118		obj.self.setOwnIdx(valueInt(l+int64(i)), arg, true)
119	}
120	n := valueInt(nl)
121	obj.self.setOwnStr("length", n, true)
122	return n
123}
124
125func (r *Runtime) arrayproto_push(call FunctionCall) Value {
126	obj := call.This.ToObject(r)
127	return r.generic_push(obj, call)
128}
129
130func (r *Runtime) arrayproto_pop_generic(obj *Object) Value {
131	l := toLength(obj.self.getStr("length", nil))
132	if l == 0 {
133		obj.self.setOwnStr("length", intToValue(0), true)
134		return _undefined
135	}
136	idx := valueInt(l - 1)
137	val := obj.self.getIdx(idx, nil)
138	obj.self.deleteIdx(idx, true)
139	obj.self.setOwnStr("length", idx, true)
140	return val
141}
142
143func (r *Runtime) arrayproto_pop(call FunctionCall) Value {
144	obj := call.This.ToObject(r)
145	if a, ok := obj.self.(*arrayObject); ok {
146		l := a.length
147		if l > 0 {
148			var val Value
149			l--
150			if l < uint32(len(a.values)) {
151				val = a.values[l]
152			}
153			if val == nil {
154				// optimisation bail-out
155				return r.arrayproto_pop_generic(obj)
156			}
157			if _, ok := val.(*valueProperty); ok {
158				// optimisation bail-out
159				return r.arrayproto_pop_generic(obj)
160			}
161			//a._setLengthInt(l, false)
162			a.values[l] = nil
163			a.values = a.values[:l]
164			a.length = l
165			return val
166		}
167		return _undefined
168	} else {
169		return r.arrayproto_pop_generic(obj)
170	}
171}
172
173func (r *Runtime) arrayproto_join(call FunctionCall) Value {
174	o := call.This.ToObject(r)
175	l := int(toLength(o.self.getStr("length", nil)))
176	var sep valueString
177	if s := call.Argument(0); s != _undefined {
178		sep = s.toString()
179	} else {
180		sep = asciiString(",")
181	}
182	if l == 0 {
183		return stringEmpty
184	}
185
186	var buf valueStringBuilder
187
188	element0 := o.self.getIdx(valueInt(0), nil)
189	if element0 != nil && element0 != _undefined && element0 != _null {
190		buf.WriteString(element0.toString())
191	}
192
193	for i := 1; i < l; i++ {
194		buf.WriteString(sep)
195		element := o.self.getIdx(valueInt(int64(i)), nil)
196		if element != nil && element != _undefined && element != _null {
197			buf.WriteString(element.toString())
198		}
199	}
200
201	return buf.String()
202}
203
204func (r *Runtime) arrayproto_toString(call FunctionCall) Value {
205	array := call.This.ToObject(r)
206	f := array.self.getStr("join", nil)
207	if fObj, ok := f.(*Object); ok {
208		if fcall, ok := fObj.self.assertCallable(); ok {
209			return fcall(FunctionCall{
210				This: array,
211			})
212		}
213	}
214	return r.objectproto_toString(FunctionCall{
215		This: array,
216	})
217}
218
219func (r *Runtime) writeItemLocaleString(item Value, buf *valueStringBuilder) {
220	if item != nil && item != _undefined && item != _null {
221		if f, ok := r.getVStr(item, "toLocaleString").(*Object); ok {
222			if c, ok := f.self.assertCallable(); ok {
223				strVal := c(FunctionCall{
224					This: item,
225				})
226				buf.WriteString(strVal.toString())
227				return
228			}
229		}
230		r.typeErrorResult(true, "Property 'toLocaleString' of object %s is not a function", item)
231	}
232}
233
234func (r *Runtime) arrayproto_toLocaleString(call FunctionCall) Value {
235	array := call.This.ToObject(r)
236	var buf valueStringBuilder
237	if a := r.checkStdArrayObj(array); a != nil {
238		for i, item := range a.values {
239			if i > 0 {
240				buf.WriteRune(',')
241			}
242			r.writeItemLocaleString(item, &buf)
243		}
244	} else {
245		length := toLength(array.self.getStr("length", nil))
246		for i := int64(0); i < length; i++ {
247			if i > 0 {
248				buf.WriteRune(',')
249			}
250			item := array.self.getIdx(valueInt(i), nil)
251			r.writeItemLocaleString(item, &buf)
252		}
253	}
254
255	return buf.String()
256}
257
258func isConcatSpreadable(obj *Object) bool {
259	spreadable := obj.self.getSym(SymIsConcatSpreadable, nil)
260	if spreadable != nil && spreadable != _undefined {
261		return spreadable.ToBoolean()
262	}
263	return isArray(obj)
264}
265
266func (r *Runtime) arrayproto_concat_append(a *Object, item Value) {
267	aLength := toLength(a.self.getStr("length", nil))
268	if obj, ok := item.(*Object); ok && isConcatSpreadable(obj) {
269		length := toLength(obj.self.getStr("length", nil))
270		if aLength+length >= maxInt {
271			panic(r.NewTypeError("Invalid array length"))
272		}
273		for i := int64(0); i < length; i++ {
274			v := obj.self.getIdx(valueInt(i), nil)
275			if v != nil {
276				createDataPropertyOrThrow(a, intToValue(aLength), v)
277			}
278			aLength++
279		}
280	} else {
281		createDataPropertyOrThrow(a, intToValue(aLength), item)
282		aLength++
283	}
284	a.self.setOwnStr("length", intToValue(aLength), true)
285}
286
287func (r *Runtime) arrayproto_concat(call FunctionCall) Value {
288	obj := call.This.ToObject(r)
289	a := arraySpeciesCreate(obj, 0)
290	r.arrayproto_concat_append(a, call.This.ToObject(r))
291	for _, item := range call.Arguments {
292		r.arrayproto_concat_append(a, item)
293	}
294	return a
295}
296
297func (r *Runtime) arrayproto_slice(call FunctionCall) Value {
298	o := call.This.ToObject(r)
299	length := toLength(o.self.getStr("length", nil))
300	start := relToIdx(call.Argument(0).ToInteger(), length)
301	var end int64
302	if endArg := call.Argument(1); endArg != _undefined {
303		end = endArg.ToInteger()
304	} else {
305		end = length
306	}
307	end = relToIdx(end, length)
308
309	count := end - start
310	if count < 0 {
311		count = 0
312	}
313
314	a := arraySpeciesCreate(o, count)
315	if src := r.checkStdArrayObj(o); src != nil {
316		if dst, ok := a.self.(*arrayObject); ok {
317			values := make([]Value, count)
318			copy(values, src.values[start:])
319			setArrayValues(dst, values)
320			return a
321		}
322	}
323
324	n := int64(0)
325	for start < end {
326		p := o.self.getIdx(valueInt(start), nil)
327		if p != nil {
328			createDataPropertyOrThrow(a, valueInt(n), p)
329		}
330		start++
331		n++
332	}
333	return a
334}
335
336func (r *Runtime) arrayproto_sort(call FunctionCall) Value {
337	o := call.This.ToObject(r)
338
339	var compareFn func(FunctionCall) Value
340	arg := call.Argument(0)
341	if arg != _undefined {
342		if arg, ok := call.Argument(0).(*Object); ok {
343			compareFn, _ = arg.self.assertCallable()
344		}
345		if compareFn == nil {
346			panic(r.NewTypeError("The comparison function must be either a function or undefined"))
347		}
348	}
349
350	if r.checkStdArrayObj(o) != nil {
351		ctx := arraySortCtx{
352			obj:     o.self,
353			compare: compareFn,
354		}
355
356		sort.Stable(&ctx)
357	} else {
358		length := toLength(o.self.getStr("length", nil))
359		a := make([]Value, 0, length)
360		for i := int64(0); i < length; i++ {
361			idx := valueInt(i)
362			if o.self.hasPropertyIdx(idx) {
363				a = append(a, nilSafe(o.self.getIdx(idx, nil)))
364			}
365		}
366		ar := r.newArrayValues(a)
367		ctx := arraySortCtx{
368			obj:     ar.self,
369			compare: compareFn,
370		}
371
372		sort.Stable(&ctx)
373		for i := 0; i < len(a); i++ {
374			o.self.setOwnIdx(valueInt(i), a[i], true)
375		}
376		for i := int64(len(a)); i < length; i++ {
377			o.self.deleteIdx(valueInt(i), true)
378		}
379	}
380	return o
381}
382
383func (r *Runtime) arrayproto_splice(call FunctionCall) Value {
384	o := call.This.ToObject(r)
385	length := toLength(o.self.getStr("length", nil))
386	actualStart := relToIdx(call.Argument(0).ToInteger(), length)
387	var actualDeleteCount int64
388	switch len(call.Arguments) {
389	case 0:
390	case 1:
391		actualDeleteCount = length - actualStart
392	default:
393		actualDeleteCount = min(max(call.Argument(1).ToInteger(), 0), length-actualStart)
394	}
395	a := arraySpeciesCreate(o, actualDeleteCount)
396	itemCount := max(int64(len(call.Arguments)-2), 0)
397	newLength := length - actualDeleteCount + itemCount
398	if src := r.checkStdArrayObj(o); src != nil {
399		if dst, ok := a.self.(*arrayObject); ok {
400			values := make([]Value, actualDeleteCount)
401			copy(values, src.values[actualStart:])
402			setArrayValues(dst, values)
403		} else {
404			for k := int64(0); k < actualDeleteCount; k++ {
405				createDataPropertyOrThrow(a, intToValue(k), src.values[k+actualStart])
406			}
407			a.self.setOwnStr("length", intToValue(actualDeleteCount), true)
408		}
409		var values []Value
410		if itemCount < actualDeleteCount {
411			values = src.values
412			copy(values[actualStart+itemCount:], values[actualStart+actualDeleteCount:])
413			tail := values[newLength:]
414			for k := range tail {
415				tail[k] = nil
416			}
417			values = values[:newLength]
418		} else if itemCount > actualDeleteCount {
419			if int64(cap(src.values)) >= newLength {
420				values = src.values[:newLength]
421				copy(values[actualStart+itemCount:], values[actualStart+actualDeleteCount:length])
422			} else {
423				values = make([]Value, newLength)
424				copy(values, src.values[:actualStart])
425				copy(values[actualStart+itemCount:], src.values[actualStart+actualDeleteCount:])
426			}
427		} else {
428			values = src.values
429		}
430		if itemCount > 0 {
431			copy(values[actualStart:], call.Arguments[2:])
432		}
433		src.values = values
434		src.objCount = len(values)
435	} else {
436		for k := int64(0); k < actualDeleteCount; k++ {
437			from := valueInt(k + actualStart)
438			if o.self.hasPropertyIdx(from) {
439				createDataPropertyOrThrow(a, valueInt(k), nilSafe(o.self.getIdx(from, nil)))
440			}
441		}
442
443		if itemCount < actualDeleteCount {
444			for k := actualStart; k < length-actualDeleteCount; k++ {
445				from := valueInt(k + actualDeleteCount)
446				to := valueInt(k + itemCount)
447				if o.self.hasPropertyIdx(from) {
448					o.self.setOwnIdx(to, nilSafe(o.self.getIdx(from, nil)), true)
449				} else {
450					o.self.deleteIdx(to, true)
451				}
452			}
453
454			for k := length; k > length-actualDeleteCount+itemCount; k-- {
455				o.self.deleteIdx(valueInt(k-1), true)
456			}
457		} else if itemCount > actualDeleteCount {
458			for k := length - actualDeleteCount; k > actualStart; k-- {
459				from := valueInt(k + actualDeleteCount - 1)
460				to := valueInt(k + itemCount - 1)
461				if o.self.hasPropertyIdx(from) {
462					o.self.setOwnIdx(to, nilSafe(o.self.getIdx(from, nil)), true)
463				} else {
464					o.self.deleteIdx(to, true)
465				}
466			}
467		}
468
469		if itemCount > 0 {
470			for i, item := range call.Arguments[2:] {
471				o.self.setOwnIdx(valueInt(actualStart+int64(i)), item, true)
472			}
473		}
474	}
475
476	o.self.setOwnStr("length", intToValue(newLength), true)
477
478	return a
479}
480
481func (r *Runtime) arrayproto_unshift(call FunctionCall) Value {
482	o := call.This.ToObject(r)
483	length := toLength(o.self.getStr("length", nil))
484	argCount := int64(len(call.Arguments))
485	newLen := intToValue(length + argCount)
486	newSize := length + argCount
487	if arr := r.checkStdArrayObj(o); arr != nil && newSize < math.MaxUint32 {
488		if int64(cap(arr.values)) >= newSize {
489			arr.values = arr.values[:newSize]
490			copy(arr.values[argCount:], arr.values[:length])
491		} else {
492			values := make([]Value, newSize)
493			copy(values[argCount:], arr.values)
494			arr.values = values
495		}
496		copy(arr.values, call.Arguments)
497		arr.objCount = int(arr.length)
498	} else {
499		for k := length - 1; k >= 0; k-- {
500			from := valueInt(k)
501			to := valueInt(k + argCount)
502			if o.self.hasPropertyIdx(from) {
503				o.self.setOwnIdx(to, nilSafe(o.self.getIdx(from, nil)), true)
504			} else {
505				o.self.deleteIdx(to, true)
506			}
507		}
508
509		for k, arg := range call.Arguments {
510			o.self.setOwnIdx(valueInt(int64(k)), arg, true)
511		}
512	}
513
514	o.self.setOwnStr("length", newLen, true)
515	return newLen
516}
517
518func (r *Runtime) arrayproto_indexOf(call FunctionCall) Value {
519	o := call.This.ToObject(r)
520	length := toLength(o.self.getStr("length", nil))
521	if length == 0 {
522		return intToValue(-1)
523	}
524
525	n := call.Argument(1).ToInteger()
526	if n >= length {
527		return intToValue(-1)
528	}
529
530	if n < 0 {
531		n = max(length+n, 0)
532	}
533
534	searchElement := call.Argument(0)
535
536	if arr := r.checkStdArrayObj(o); arr != nil {
537		for i, val := range arr.values[n:] {
538			if searchElement.StrictEquals(val) {
539				return intToValue(n + int64(i))
540			}
541		}
542		return intToValue(-1)
543	}
544
545	for ; n < length; n++ {
546		idx := valueInt(n)
547		if o.self.hasPropertyIdx(idx) {
548			if val := o.self.getIdx(idx, nil); val != nil {
549				if searchElement.StrictEquals(val) {
550					return idx
551				}
552			}
553		}
554	}
555
556	return intToValue(-1)
557}
558
559func (r *Runtime) arrayproto_includes(call FunctionCall) Value {
560	o := call.This.ToObject(r)
561	length := toLength(o.self.getStr("length", nil))
562	if length == 0 {
563		return valueFalse
564	}
565
566	n := call.Argument(1).ToInteger()
567	if n >= length {
568		return valueFalse
569	}
570
571	if n < 0 {
572		n = max(length+n, 0)
573	}
574
575	searchElement := call.Argument(0)
576	if searchElement == _negativeZero {
577		searchElement = _positiveZero
578	}
579
580	if arr := r.checkStdArrayObj(o); arr != nil {
581		for _, val := range arr.values[n:] {
582			if searchElement.SameAs(val) {
583				return valueTrue
584			}
585		}
586		return valueFalse
587	}
588
589	for ; n < length; n++ {
590		idx := valueInt(n)
591		val := nilSafe(o.self.getIdx(idx, nil))
592		if searchElement.SameAs(val) {
593			return valueTrue
594		}
595	}
596
597	return valueFalse
598}
599
600func (r *Runtime) arrayproto_lastIndexOf(call FunctionCall) Value {
601	o := call.This.ToObject(r)
602	length := toLength(o.self.getStr("length", nil))
603	if length == 0 {
604		return intToValue(-1)
605	}
606
607	var fromIndex int64
608
609	if len(call.Arguments) < 2 {
610		fromIndex = length - 1
611	} else {
612		fromIndex = call.Argument(1).ToInteger()
613		if fromIndex >= 0 {
614			fromIndex = min(fromIndex, length-1)
615		} else {
616			fromIndex += length
617		}
618	}
619
620	searchElement := call.Argument(0)
621
622	if arr := r.checkStdArrayObj(o); arr != nil {
623		vals := arr.values
624		for k := fromIndex; k >= 0; k-- {
625			if v := vals[k]; v != nil && searchElement.StrictEquals(v) {
626				return intToValue(k)
627			}
628		}
629		return intToValue(-1)
630	}
631
632	for k := fromIndex; k >= 0; k-- {
633		idx := valueInt(k)
634		if o.self.hasPropertyIdx(idx) {
635			if val := o.self.getIdx(idx, nil); val != nil {
636				if searchElement.StrictEquals(val) {
637					return idx
638				}
639			}
640		}
641	}
642
643	return intToValue(-1)
644}
645
646func (r *Runtime) arrayproto_every(call FunctionCall) Value {
647	o := call.This.ToObject(r)
648	length := toLength(o.self.getStr("length", nil))
649	callbackFn := r.toCallable(call.Argument(0))
650	fc := FunctionCall{
651		This:      call.Argument(1),
652		Arguments: []Value{nil, nil, o},
653	}
654	for k := int64(0); k < length; k++ {
655		idx := valueInt(k)
656		if val := o.self.getIdx(idx, nil); val != nil {
657			fc.Arguments[0] = val
658			fc.Arguments[1] = idx
659			if !callbackFn(fc).ToBoolean() {
660				return valueFalse
661			}
662		}
663	}
664	return valueTrue
665}
666
667func (r *Runtime) arrayproto_some(call FunctionCall) Value {
668	o := call.This.ToObject(r)
669	length := toLength(o.self.getStr("length", nil))
670	callbackFn := r.toCallable(call.Argument(0))
671	fc := FunctionCall{
672		This:      call.Argument(1),
673		Arguments: []Value{nil, nil, o},
674	}
675	for k := int64(0); k < length; k++ {
676		idx := valueInt(k)
677		if val := o.self.getIdx(idx, nil); val != nil {
678			fc.Arguments[0] = val
679			fc.Arguments[1] = idx
680			if callbackFn(fc).ToBoolean() {
681				return valueTrue
682			}
683		}
684	}
685	return valueFalse
686}
687
688func (r *Runtime) arrayproto_forEach(call FunctionCall) Value {
689	o := call.This.ToObject(r)
690	length := toLength(o.self.getStr("length", nil))
691	callbackFn := r.toCallable(call.Argument(0))
692	fc := FunctionCall{
693		This:      call.Argument(1),
694		Arguments: []Value{nil, nil, o},
695	}
696	for k := int64(0); k < length; k++ {
697		idx := valueInt(k)
698		if val := o.self.getIdx(idx, nil); val != nil {
699			fc.Arguments[0] = val
700			fc.Arguments[1] = idx
701			callbackFn(fc)
702		}
703	}
704	return _undefined
705}
706
707func (r *Runtime) arrayproto_map(call FunctionCall) Value {
708	o := call.This.ToObject(r)
709	length := toLength(o.self.getStr("length", nil))
710	callbackFn := r.toCallable(call.Argument(0))
711	fc := FunctionCall{
712		This:      call.Argument(1),
713		Arguments: []Value{nil, nil, o},
714	}
715	a := arraySpeciesCreate(o, length)
716	if _, stdSrc := o.self.(*arrayObject); stdSrc {
717		if arr, ok := a.self.(*arrayObject); ok {
718			values := make([]Value, length)
719			for k := int64(0); k < length; k++ {
720				idx := valueInt(k)
721				if val := o.self.getIdx(idx, nil); val != nil {
722					fc.Arguments[0] = val
723					fc.Arguments[1] = idx
724					values[k] = callbackFn(fc)
725				}
726			}
727			setArrayValues(arr, values)
728			return a
729		}
730	}
731	for k := int64(0); k < length; k++ {
732		idx := valueInt(k)
733		if val := o.self.getIdx(idx, nil); val != nil {
734			fc.Arguments[0] = val
735			fc.Arguments[1] = idx
736			createDataPropertyOrThrow(a, idx, callbackFn(fc))
737		}
738	}
739	return a
740}
741
742func (r *Runtime) arrayproto_filter(call FunctionCall) Value {
743	o := call.This.ToObject(r)
744	length := toLength(o.self.getStr("length", nil))
745	callbackFn := call.Argument(0).ToObject(r)
746	if callbackFn, ok := callbackFn.self.assertCallable(); ok {
747		a := arraySpeciesCreate(o, 0)
748		fc := FunctionCall{
749			This:      call.Argument(1),
750			Arguments: []Value{nil, nil, o},
751		}
752		if _, stdSrc := o.self.(*arrayObject); stdSrc {
753			if arr := r.checkStdArrayObj(a); arr != nil {
754				var values []Value
755				for k := int64(0); k < length; k++ {
756					idx := valueInt(k)
757					if val := o.self.getIdx(idx, nil); val != nil {
758						fc.Arguments[0] = val
759						fc.Arguments[1] = idx
760						if callbackFn(fc).ToBoolean() {
761							values = append(values, val)
762						}
763					}
764				}
765				setArrayValues(arr, values)
766				return a
767			}
768		}
769
770		to := int64(0)
771		for k := int64(0); k < length; k++ {
772			idx := valueInt(k)
773			if val := o.self.getIdx(idx, nil); val != nil {
774				fc.Arguments[0] = val
775				fc.Arguments[1] = idx
776				if callbackFn(fc).ToBoolean() {
777					createDataPropertyOrThrow(a, intToValue(to), val)
778					to++
779				}
780			}
781		}
782		return a
783	} else {
784		r.typeErrorResult(true, "%s is not a function", call.Argument(0))
785	}
786	panic("unreachable")
787}
788
789func (r *Runtime) arrayproto_reduce(call FunctionCall) Value {
790	o := call.This.ToObject(r)
791	length := toLength(o.self.getStr("length", nil))
792	callbackFn := call.Argument(0).ToObject(r)
793	if callbackFn, ok := callbackFn.self.assertCallable(); ok {
794		fc := FunctionCall{
795			This:      _undefined,
796			Arguments: []Value{nil, nil, nil, o},
797		}
798
799		var k int64
800
801		if len(call.Arguments) >= 2 {
802			fc.Arguments[0] = call.Argument(1)
803		} else {
804			for ; k < length; k++ {
805				idx := valueInt(k)
806				if val := o.self.getIdx(idx, nil); val != nil {
807					fc.Arguments[0] = val
808					break
809				}
810			}
811			if fc.Arguments[0] == nil {
812				r.typeErrorResult(true, "No initial value")
813				panic("unreachable")
814			}
815			k++
816		}
817
818		for ; k < length; k++ {
819			idx := valueInt(k)
820			if val := o.self.getIdx(idx, nil); val != nil {
821				fc.Arguments[1] = val
822				fc.Arguments[2] = idx
823				fc.Arguments[0] = callbackFn(fc)
824			}
825		}
826		return fc.Arguments[0]
827	} else {
828		r.typeErrorResult(true, "%s is not a function", call.Argument(0))
829	}
830	panic("unreachable")
831}
832
833func (r *Runtime) arrayproto_reduceRight(call FunctionCall) Value {
834	o := call.This.ToObject(r)
835	length := toLength(o.self.getStr("length", nil))
836	callbackFn := call.Argument(0).ToObject(r)
837	if callbackFn, ok := callbackFn.self.assertCallable(); ok {
838		fc := FunctionCall{
839			This:      _undefined,
840			Arguments: []Value{nil, nil, nil, o},
841		}
842
843		k := length - 1
844
845		if len(call.Arguments) >= 2 {
846			fc.Arguments[0] = call.Argument(1)
847		} else {
848			for ; k >= 0; k-- {
849				idx := valueInt(k)
850				if val := o.self.getIdx(idx, nil); val != nil {
851					fc.Arguments[0] = val
852					break
853				}
854			}
855			if fc.Arguments[0] == nil {
856				r.typeErrorResult(true, "No initial value")
857				panic("unreachable")
858			}
859			k--
860		}
861
862		for ; k >= 0; k-- {
863			idx := valueInt(k)
864			if val := o.self.getIdx(idx, nil); val != nil {
865				fc.Arguments[1] = val
866				fc.Arguments[2] = idx
867				fc.Arguments[0] = callbackFn(fc)
868			}
869		}
870		return fc.Arguments[0]
871	} else {
872		r.typeErrorResult(true, "%s is not a function", call.Argument(0))
873	}
874	panic("unreachable")
875}
876
877func arrayproto_reverse_generic_step(o *Object, lower, upper int64) {
878	lowerP := valueInt(lower)
879	upperP := valueInt(upper)
880	lowerValue := o.self.getIdx(lowerP, nil)
881	upperValue := o.self.getIdx(upperP, nil)
882	if lowerValue != nil && upperValue != nil {
883		o.self.setOwnIdx(lowerP, upperValue, true)
884		o.self.setOwnIdx(upperP, lowerValue, true)
885	} else if lowerValue == nil && upperValue != nil {
886		o.self.setOwnIdx(lowerP, upperValue, true)
887		o.self.deleteIdx(upperP, true)
888	} else if lowerValue != nil && upperValue == nil {
889		o.self.deleteIdx(lowerP, true)
890		o.self.setOwnIdx(upperP, lowerValue, true)
891	}
892}
893
894func (r *Runtime) arrayproto_reverse_generic(o *Object, start int64) {
895	l := toLength(o.self.getStr("length", nil))
896	middle := l / 2
897	for lower := start; lower != middle; lower++ {
898		arrayproto_reverse_generic_step(o, lower, l-lower-1)
899	}
900}
901
902func (r *Runtime) arrayproto_reverse(call FunctionCall) Value {
903	o := call.This.ToObject(r)
904	if a := r.checkStdArrayObj(o); a != nil {
905		l := len(a.values)
906		middle := l / 2
907		for lower := 0; lower != middle; lower++ {
908			upper := l - lower - 1
909			a.values[lower], a.values[upper] = a.values[upper], a.values[lower]
910		}
911		//TODO: go arrays
912	} else {
913		r.arrayproto_reverse_generic(o, 0)
914	}
915	return o
916}
917
918func (r *Runtime) arrayproto_shift(call FunctionCall) Value {
919	o := call.This.ToObject(r)
920	if a := r.checkStdArrayObj(o); a != nil {
921		if len(a.values) == 0 {
922			return _undefined
923		}
924		first := a.values[0]
925		copy(a.values, a.values[1:])
926		a.values[len(a.values)-1] = nil
927		a.values = a.values[:len(a.values)-1]
928		a.length--
929		return first
930	}
931	length := toLength(o.self.getStr("length", nil))
932	if length == 0 {
933		o.self.setOwnStr("length", intToValue(0), true)
934		return _undefined
935	}
936	first := o.self.getIdx(valueInt(0), nil)
937	for i := int64(1); i < length; i++ {
938		idxFrom := valueInt(i)
939		idxTo := valueInt(i - 1)
940		if o.self.hasPropertyIdx(idxFrom) {
941			o.self.setOwnIdx(idxTo, nilSafe(o.self.getIdx(idxFrom, nil)), true)
942		} else {
943			o.self.deleteIdx(idxTo, true)
944		}
945	}
946
947	lv := valueInt(length - 1)
948	o.self.deleteIdx(lv, true)
949	o.self.setOwnStr("length", lv, true)
950
951	return first
952}
953
954func (r *Runtime) arrayproto_values(call FunctionCall) Value {
955	return r.createArrayIterator(call.This.ToObject(r), iterationKindValue)
956}
957
958func (r *Runtime) arrayproto_keys(call FunctionCall) Value {
959	return r.createArrayIterator(call.This.ToObject(r), iterationKindKey)
960}
961
962func (r *Runtime) arrayproto_copyWithin(call FunctionCall) Value {
963	o := call.This.ToObject(r)
964	l := toLength(o.self.getStr("length", nil))
965	var relEnd, dir int64
966	to := relToIdx(call.Argument(0).ToInteger(), l)
967	from := relToIdx(call.Argument(1).ToInteger(), l)
968	if end := call.Argument(2); end != _undefined {
969		relEnd = end.ToInteger()
970	} else {
971		relEnd = l
972	}
973	final := relToIdx(relEnd, l)
974	count := min(final-from, l-to)
975	if arr := r.checkStdArrayObj(o); arr != nil {
976		if count > 0 {
977			copy(arr.values[to:to+count], arr.values[from:from+count])
978		}
979		return o
980	}
981	if from < to && to < from+count {
982		dir = -1
983		from = from + count - 1
984		to = to + count - 1
985	} else {
986		dir = 1
987	}
988	for count > 0 {
989		if o.self.hasPropertyIdx(valueInt(from)) {
990			o.self.setOwnIdx(valueInt(to), nilSafe(o.self.getIdx(valueInt(from), nil)), true)
991		} else {
992			o.self.deleteIdx(valueInt(to), true)
993		}
994		from += dir
995		to += dir
996		count--
997	}
998
999	return o
1000}
1001
1002func (r *Runtime) arrayproto_entries(call FunctionCall) Value {
1003	return r.createArrayIterator(call.This.ToObject(r), iterationKindKeyValue)
1004}
1005
1006func (r *Runtime) arrayproto_fill(call FunctionCall) Value {
1007	o := call.This.ToObject(r)
1008	l := toLength(o.self.getStr("length", nil))
1009	k := relToIdx(call.Argument(1).ToInteger(), l)
1010	var relEnd int64
1011	if endArg := call.Argument(2); endArg != _undefined {
1012		relEnd = endArg.ToInteger()
1013	} else {
1014		relEnd = l
1015	}
1016	final := relToIdx(relEnd, l)
1017	value := call.Argument(0)
1018	if arr := r.checkStdArrayObj(o); arr != nil {
1019		for ; k < final; k++ {
1020			arr.values[k] = value
1021		}
1022	} else {
1023		for ; k < final; k++ {
1024			o.self.setOwnIdx(valueInt(k), value, true)
1025		}
1026	}
1027	return o
1028}
1029
1030func (r *Runtime) arrayproto_find(call FunctionCall) Value {
1031	o := call.This.ToObject(r)
1032	l := toLength(o.self.getStr("length", nil))
1033	predicate := r.toCallable(call.Argument(0))
1034	fc := FunctionCall{
1035		This:      call.Argument(1),
1036		Arguments: []Value{nil, nil, o},
1037	}
1038	for k := int64(0); k < l; k++ {
1039		idx := valueInt(k)
1040		kValue := o.self.getIdx(idx, nil)
1041		fc.Arguments[0], fc.Arguments[1] = kValue, idx
1042		if predicate(fc).ToBoolean() {
1043			return kValue
1044		}
1045	}
1046
1047	return _undefined
1048}
1049
1050func (r *Runtime) arrayproto_findIndex(call FunctionCall) Value {
1051	o := call.This.ToObject(r)
1052	l := toLength(o.self.getStr("length", nil))
1053	predicate := r.toCallable(call.Argument(0))
1054	fc := FunctionCall{
1055		This:      call.Argument(1),
1056		Arguments: []Value{nil, nil, o},
1057	}
1058	for k := int64(0); k < l; k++ {
1059		idx := valueInt(k)
1060		kValue := o.self.getIdx(idx, nil)
1061		fc.Arguments[0], fc.Arguments[1] = kValue, idx
1062		if predicate(fc).ToBoolean() {
1063			return idx
1064		}
1065	}
1066
1067	return intToValue(-1)
1068}
1069
1070func (r *Runtime) arrayproto_flat(call FunctionCall) Value {
1071	o := call.This.ToObject(r)
1072	l := toLength(o.self.getStr("length", nil))
1073	depthNum := int64(1)
1074	if len(call.Arguments) > 0 {
1075		depthNum = call.Argument(0).ToInteger()
1076	}
1077	a := arraySpeciesCreate(o, 0)
1078	r.flattenIntoArray(a, o, l, 0, depthNum, nil, nil)
1079	return a
1080}
1081
1082func (r *Runtime) flattenIntoArray(target, source *Object, sourceLen, start, depth int64, mapperFunction func(FunctionCall) Value, thisArg Value) int64 {
1083	targetIndex, sourceIndex := start, int64(0)
1084	for sourceIndex < sourceLen {
1085		p := intToValue(sourceIndex)
1086		if source.hasProperty(p.toString()) {
1087			element := nilSafe(source.get(p, source))
1088			if mapperFunction != nil {
1089				element = mapperFunction(FunctionCall{
1090					This:      thisArg,
1091					Arguments: []Value{element, p, source},
1092				})
1093			}
1094			var elementArray *Object
1095			if depth > 0 {
1096				if elementObj, ok := element.(*Object); ok && isArray(elementObj) {
1097					elementArray = elementObj
1098				}
1099			}
1100			if elementArray != nil {
1101				elementLen := toLength(elementArray.self.getStr("length", nil))
1102				targetIndex = r.flattenIntoArray(target, elementArray, elementLen, targetIndex, depth-1, nil, nil)
1103			} else {
1104				if targetIndex >= maxInt-1 {
1105					panic(r.NewTypeError("Invalid array length"))
1106				}
1107				createDataPropertyOrThrow(target, intToValue(targetIndex), element)
1108				targetIndex++
1109			}
1110		}
1111		sourceIndex++
1112	}
1113	return targetIndex
1114}
1115
1116func (r *Runtime) arrayproto_flatMap(call FunctionCall) Value {
1117	o := call.This.ToObject(r)
1118	l := toLength(o.self.getStr("length", nil))
1119	callbackFn := r.toCallable(call.Argument(0))
1120	thisArg := Undefined()
1121	if len(call.Arguments) > 1 {
1122		thisArg = call.Argument(1)
1123	}
1124	a := arraySpeciesCreate(o, 0)
1125	r.flattenIntoArray(a, o, l, 0, 1, callbackFn, thisArg)
1126	return a
1127}
1128
1129func (r *Runtime) checkStdArrayObj(obj *Object) *arrayObject {
1130	if arr, ok := obj.self.(*arrayObject); ok &&
1131		arr.propValueCount == 0 &&
1132		arr.length == uint32(len(arr.values)) &&
1133		uint32(arr.objCount) == arr.length {
1134
1135		return arr
1136	}
1137
1138	return nil
1139}
1140
1141func (r *Runtime) checkStdArray(v Value) *arrayObject {
1142	if obj, ok := v.(*Object); ok {
1143		return r.checkStdArrayObj(obj)
1144	}
1145
1146	return nil
1147}
1148
1149func (r *Runtime) checkStdArrayIter(v Value) *arrayObject {
1150	if arr := r.checkStdArray(v); arr != nil &&
1151		arr.getSym(SymIterator, nil) == r.global.arrayValues {
1152
1153		return arr
1154	}
1155
1156	return nil
1157}
1158
1159func (r *Runtime) array_from(call FunctionCall) Value {
1160	var mapFn func(FunctionCall) Value
1161	if mapFnArg := call.Argument(1); mapFnArg != _undefined {
1162		if mapFnObj, ok := mapFnArg.(*Object); ok {
1163			if fn, ok := mapFnObj.self.assertCallable(); ok {
1164				mapFn = fn
1165			}
1166		}
1167		if mapFn == nil {
1168			panic(r.NewTypeError("%s is not a function", mapFnArg))
1169		}
1170	}
1171	t := call.Argument(2)
1172	items := call.Argument(0)
1173	if mapFn == nil && call.This == r.global.Array { // mapFn may mutate the array
1174		if arr := r.checkStdArrayIter(items); arr != nil {
1175			items := make([]Value, len(arr.values))
1176			copy(items, arr.values)
1177			return r.newArrayValues(items)
1178		}
1179	}
1180
1181	var ctor func(args []Value, newTarget *Object) *Object
1182	if call.This != r.global.Array {
1183		if o, ok := call.This.(*Object); ok {
1184			if c := o.self.assertConstructor(); c != nil {
1185				ctor = c
1186			}
1187		}
1188	}
1189	var arr *Object
1190	if usingIterator := toMethod(r.getV(items, SymIterator)); usingIterator != nil {
1191		if ctor != nil {
1192			arr = ctor([]Value{}, nil)
1193		} else {
1194			arr = r.newArrayValues(nil)
1195		}
1196		iter := r.getIterator(items, usingIterator)
1197		if mapFn == nil {
1198			if a := r.checkStdArrayObj(arr); a != nil {
1199				var values []Value
1200				r.iterate(iter, func(val Value) {
1201					values = append(values, val)
1202				})
1203				setArrayValues(a, values)
1204				return arr
1205			}
1206		}
1207		k := int64(0)
1208		r.iterate(iter, func(val Value) {
1209			if mapFn != nil {
1210				val = mapFn(FunctionCall{This: t, Arguments: []Value{val, intToValue(k)}})
1211			}
1212			createDataPropertyOrThrow(arr, intToValue(k), val)
1213			k++
1214		})
1215		arr.self.setOwnStr("length", intToValue(k), true)
1216	} else {
1217		arrayLike := items.ToObject(r)
1218		l := toLength(arrayLike.self.getStr("length", nil))
1219		if ctor != nil {
1220			arr = ctor([]Value{intToValue(l)}, nil)
1221		} else {
1222			arr = r.newArrayValues(nil)
1223		}
1224		if mapFn == nil {
1225			if a := r.checkStdArrayObj(arr); a != nil {
1226				values := make([]Value, l)
1227				for k := int64(0); k < l; k++ {
1228					values[k] = nilSafe(arrayLike.self.getIdx(valueInt(k), nil))
1229				}
1230				setArrayValues(a, values)
1231				return arr
1232			}
1233		}
1234		for k := int64(0); k < l; k++ {
1235			idx := valueInt(k)
1236			item := arrayLike.self.getIdx(idx, nil)
1237			if mapFn != nil {
1238				item = mapFn(FunctionCall{This: t, Arguments: []Value{item, idx}})
1239			} else {
1240				item = nilSafe(item)
1241			}
1242			createDataPropertyOrThrow(arr, idx, item)
1243		}
1244		arr.self.setOwnStr("length", intToValue(l), true)
1245	}
1246
1247	return arr
1248}
1249
1250func (r *Runtime) array_isArray(call FunctionCall) Value {
1251	if o, ok := call.Argument(0).(*Object); ok {
1252		if isArray(o) {
1253			return valueTrue
1254		}
1255	}
1256	return valueFalse
1257}
1258
1259func (r *Runtime) array_of(call FunctionCall) Value {
1260	var ctor func(args []Value, newTarget *Object) *Object
1261	if call.This != r.global.Array {
1262		if o, ok := call.This.(*Object); ok {
1263			if c := o.self.assertConstructor(); c != nil {
1264				ctor = c
1265			}
1266		}
1267	}
1268	if ctor == nil {
1269		values := make([]Value, len(call.Arguments))
1270		copy(values, call.Arguments)
1271		return r.newArrayValues(values)
1272	}
1273	l := intToValue(int64(len(call.Arguments)))
1274	arr := ctor([]Value{l}, nil)
1275	for i, val := range call.Arguments {
1276		createDataPropertyOrThrow(arr, intToValue(int64(i)), val)
1277	}
1278	arr.self.setOwnStr("length", l, true)
1279	return arr
1280}
1281
1282func (r *Runtime) arrayIterProto_next(call FunctionCall) Value {
1283	thisObj := r.toObject(call.This)
1284	if iter, ok := thisObj.self.(*arrayIterObject); ok {
1285		return iter.next()
1286	}
1287	panic(r.NewTypeError("Method Array Iterator.prototype.next called on incompatible receiver %s", thisObj.String()))
1288}
1289
1290func (r *Runtime) createArrayProto(val *Object) objectImpl {
1291	o := &arrayObject{
1292		baseObject: baseObject{
1293			class:      classArray,
1294			val:        val,
1295			extensible: true,
1296			prototype:  r.global.ObjectPrototype,
1297		},
1298	}
1299	o.init()
1300
1301	o._putProp("constructor", r.global.Array, true, false, true)
1302	o._putProp("concat", r.newNativeFunc(r.arrayproto_concat, nil, "concat", nil, 1), true, false, true)
1303	o._putProp("copyWithin", r.newNativeFunc(r.arrayproto_copyWithin, nil, "copyWithin", nil, 2), true, false, true)
1304	o._putProp("entries", r.newNativeFunc(r.arrayproto_entries, nil, "entries", nil, 0), true, false, true)
1305	o._putProp("every", r.newNativeFunc(r.arrayproto_every, nil, "every", nil, 1), true, false, true)
1306	o._putProp("fill", r.newNativeFunc(r.arrayproto_fill, nil, "fill", nil, 1), true, false, true)
1307	o._putProp("filter", r.newNativeFunc(r.arrayproto_filter, nil, "filter", nil, 1), true, false, true)
1308	o._putProp("find", r.newNativeFunc(r.arrayproto_find, nil, "find", nil, 1), true, false, true)
1309	o._putProp("findIndex", r.newNativeFunc(r.arrayproto_findIndex, nil, "findIndex", nil, 1), true, false, true)
1310	o._putProp("flat", r.newNativeFunc(r.arrayproto_flat, nil, "flat", nil, 0), true, false, true)
1311	o._putProp("flatMap", r.newNativeFunc(r.arrayproto_flatMap, nil, "flatMap", nil, 1), true, false, true)
1312	o._putProp("forEach", r.newNativeFunc(r.arrayproto_forEach, nil, "forEach", nil, 1), true, false, true)
1313	o._putProp("includes", r.newNativeFunc(r.arrayproto_includes, nil, "includes", nil, 1), true, false, true)
1314	o._putProp("indexOf", r.newNativeFunc(r.arrayproto_indexOf, nil, "indexOf", nil, 1), true, false, true)
1315	o._putProp("join", r.newNativeFunc(r.arrayproto_join, nil, "join", nil, 1), true, false, true)
1316	o._putProp("keys", r.newNativeFunc(r.arrayproto_keys, nil, "keys", nil, 0), true, false, true)
1317	o._putProp("lastIndexOf", r.newNativeFunc(r.arrayproto_lastIndexOf, nil, "lastIndexOf", nil, 1), true, false, true)
1318	o._putProp("map", r.newNativeFunc(r.arrayproto_map, nil, "map", nil, 1), true, false, true)
1319	o._putProp("pop", r.newNativeFunc(r.arrayproto_pop, nil, "pop", nil, 0), true, false, true)
1320	o._putProp("push", r.newNativeFunc(r.arrayproto_push, nil, "push", nil, 1), true, false, true)
1321	o._putProp("reduce", r.newNativeFunc(r.arrayproto_reduce, nil, "reduce", nil, 1), true, false, true)
1322	o._putProp("reduceRight", r.newNativeFunc(r.arrayproto_reduceRight, nil, "reduceRight", nil, 1), true, false, true)
1323	o._putProp("reverse", r.newNativeFunc(r.arrayproto_reverse, nil, "reverse", nil, 0), true, false, true)
1324	o._putProp("shift", r.newNativeFunc(r.arrayproto_shift, nil, "shift", nil, 0), true, false, true)
1325	o._putProp("slice", r.newNativeFunc(r.arrayproto_slice, nil, "slice", nil, 2), true, false, true)
1326	o._putProp("some", r.newNativeFunc(r.arrayproto_some, nil, "some", nil, 1), true, false, true)
1327	o._putProp("sort", r.newNativeFunc(r.arrayproto_sort, nil, "sort", nil, 1), true, false, true)
1328	o._putProp("splice", r.newNativeFunc(r.arrayproto_splice, nil, "splice", nil, 2), true, false, true)
1329	o._putProp("toLocaleString", r.newNativeFunc(r.arrayproto_toLocaleString, nil, "toLocaleString", nil, 0), true, false, true)
1330	o._putProp("toString", r.global.arrayToString, true, false, true)
1331	o._putProp("unshift", r.newNativeFunc(r.arrayproto_unshift, nil, "unshift", nil, 1), true, false, true)
1332	o._putProp("values", r.global.arrayValues, true, false, true)
1333
1334	o._putSym(SymIterator, valueProp(r.global.arrayValues, true, false, true))
1335
1336	bl := r.newBaseObject(nil, classObject)
1337	bl.setOwnStr("copyWithin", valueTrue, true)
1338	bl.setOwnStr("entries", valueTrue, true)
1339	bl.setOwnStr("fill", valueTrue, true)
1340	bl.setOwnStr("find", valueTrue, true)
1341	bl.setOwnStr("findIndex", valueTrue, true)
1342	bl.setOwnStr("flat", valueTrue, true)
1343	bl.setOwnStr("flatMap", valueTrue, true)
1344	bl.setOwnStr("includes", valueTrue, true)
1345	bl.setOwnStr("keys", valueTrue, true)
1346	bl.setOwnStr("values", valueTrue, true)
1347	o._putSym(SymUnscopables, valueProp(bl.val, false, false, true))
1348
1349	return o
1350}
1351
1352func (r *Runtime) createArray(val *Object) objectImpl {
1353	o := r.newNativeFuncConstructObj(val, r.builtin_newArray, "Array", r.global.ArrayPrototype, 1)
1354	o._putProp("from", r.newNativeFunc(r.array_from, nil, "from", nil, 1), true, false, true)
1355	o._putProp("isArray", r.newNativeFunc(r.array_isArray, nil, "isArray", nil, 1), true, false, true)
1356	o._putProp("of", r.newNativeFunc(r.array_of, nil, "of", nil, 0), true, false, true)
1357	o._putSym(SymSpecies, &valueProperty{
1358		getterFunc:   r.newNativeFunc(r.returnThis, nil, "get [Symbol.species]", nil, 0),
1359		accessor:     true,
1360		configurable: true,
1361	})
1362
1363	return o
1364}
1365
1366func (r *Runtime) createArrayIterProto(val *Object) objectImpl {
1367	o := newBaseObjectObj(val, r.global.IteratorPrototype, classObject)
1368
1369	o._putProp("next", r.newNativeFunc(r.arrayIterProto_next, nil, "next", nil, 0), true, false, true)
1370	o._putSym(SymToStringTag, valueProp(asciiString(classArrayIterator), false, false, true))
1371
1372	return o
1373}
1374
1375func (r *Runtime) initArray() {
1376	r.global.arrayValues = r.newNativeFunc(r.arrayproto_values, nil, "values", nil, 0)
1377	r.global.arrayToString = r.newNativeFunc(r.arrayproto_toString, nil, "toString", nil, 0)
1378
1379	r.global.ArrayIteratorPrototype = r.newLazyObject(r.createArrayIterProto)
1380	//r.global.ArrayPrototype = r.newArray(r.global.ObjectPrototype).val
1381	//o := r.global.ArrayPrototype.self
1382	r.global.ArrayPrototype = r.newLazyObject(r.createArrayProto)
1383
1384	//r.global.Array = r.newNativeFuncConstruct(r.builtin_newArray, "Array", r.global.ArrayPrototype, 1)
1385	//o = r.global.Array.self
1386	//o._putProp("isArray", r.newNativeFunc(r.array_isArray, nil, "isArray", nil, 1), true, false, true)
1387	r.global.Array = r.newLazyObject(r.createArray)
1388
1389	r.addToGlobal("Array", r.global.Array)
1390}
1391
1392type sortable interface {
1393	sortLen() int64
1394	sortGet(int64) Value
1395	swap(int64, int64)
1396}
1397
1398type arraySortCtx struct {
1399	obj     sortable
1400	compare func(FunctionCall) Value
1401}
1402
1403func (a *arraySortCtx) sortCompare(x, y Value) int {
1404	if x == nil && y == nil {
1405		return 0
1406	}
1407
1408	if x == nil {
1409		return 1
1410	}
1411
1412	if y == nil {
1413		return -1
1414	}
1415
1416	if x == _undefined && y == _undefined {
1417		return 0
1418	}
1419
1420	if x == _undefined {
1421		return 1
1422	}
1423
1424	if y == _undefined {
1425		return -1
1426	}
1427
1428	if a.compare != nil {
1429		f := a.compare(FunctionCall{
1430			This:      _undefined,
1431			Arguments: []Value{x, y},
1432		}).ToFloat()
1433		if f > 0 {
1434			return 1
1435		}
1436		if f < 0 {
1437			return -1
1438		}
1439		if math.Signbit(f) {
1440			return -1
1441		}
1442		return 0
1443	}
1444	return x.toString().compareTo(y.toString())
1445}
1446
1447// sort.Interface
1448
1449func (a *arraySortCtx) Len() int {
1450	return int(a.obj.sortLen())
1451}
1452
1453func (a *arraySortCtx) Less(j, k int) bool {
1454	return a.sortCompare(a.obj.sortGet(int64(j)), a.obj.sortGet(int64(k))) < 0
1455}
1456
1457func (a *arraySortCtx) Swap(j, k int) {
1458	a.obj.swap(int64(j), int64(k))
1459}
1460