1/*
2 * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package jdk.nashorn.internal.objects;
27
28import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError;
29import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
30import static jdk.nashorn.internal.runtime.PropertyDescriptor.VALUE;
31import static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE;
32import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex;
33import static jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator.arrayLikeIterator;
34import static jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator.reverseArrayLikeIterator;
35import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_STRICT;
36
37import java.lang.invoke.MethodHandle;
38import java.util.ArrayList;
39import java.util.Arrays;
40import java.util.Collections;
41import java.util.Comparator;
42import java.util.Iterator;
43import java.util.List;
44import java.util.concurrent.Callable;
45import jdk.dynalink.CallSiteDescriptor;
46import jdk.dynalink.linker.GuardedInvocation;
47import jdk.dynalink.linker.LinkRequest;
48import jdk.nashorn.api.scripting.JSObject;
49import jdk.nashorn.internal.objects.annotations.Attribute;
50import jdk.nashorn.internal.objects.annotations.Constructor;
51import jdk.nashorn.internal.objects.annotations.Function;
52import jdk.nashorn.internal.objects.annotations.Getter;
53import jdk.nashorn.internal.objects.annotations.ScriptClass;
54import jdk.nashorn.internal.objects.annotations.Setter;
55import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
56import jdk.nashorn.internal.objects.annotations.SpecializedFunction.LinkLogic;
57import jdk.nashorn.internal.objects.annotations.Where;
58import jdk.nashorn.internal.runtime.Context;
59import jdk.nashorn.internal.runtime.Debug;
60import jdk.nashorn.internal.runtime.JSType;
61import jdk.nashorn.internal.runtime.OptimisticBuiltins;
62import jdk.nashorn.internal.runtime.PropertyDescriptor;
63import jdk.nashorn.internal.runtime.PropertyMap;
64import jdk.nashorn.internal.runtime.ScriptObject;
65import jdk.nashorn.internal.runtime.ScriptRuntime;
66import jdk.nashorn.internal.runtime.Undefined;
67import jdk.nashorn.internal.runtime.arrays.ArrayData;
68import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
69import jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator;
70import jdk.nashorn.internal.runtime.arrays.ContinuousArrayData;
71import jdk.nashorn.internal.runtime.arrays.IntElements;
72import jdk.nashorn.internal.runtime.arrays.IteratorAction;
73import jdk.nashorn.internal.runtime.arrays.NumericElements;
74import jdk.nashorn.internal.runtime.linker.Bootstrap;
75import jdk.nashorn.internal.runtime.linker.InvokeByName;
76
77/**
78 * Runtime representation of a JavaScript array. NativeArray only holds numeric
79 * keyed values. All other values are stored in spill.
80 */
81@ScriptClass("Array")
82public final class NativeArray extends ScriptObject implements OptimisticBuiltins {
83    private static final Object JOIN                     = new Object();
84    private static final Object EVERY_CALLBACK_INVOKER   = new Object();
85    private static final Object SOME_CALLBACK_INVOKER    = new Object();
86    private static final Object FOREACH_CALLBACK_INVOKER = new Object();
87    private static final Object MAP_CALLBACK_INVOKER     = new Object();
88    private static final Object FILTER_CALLBACK_INVOKER  = new Object();
89    private static final Object REDUCE_CALLBACK_INVOKER  = new Object();
90    private static final Object CALL_CMP                 = new Object();
91    private static final Object TO_LOCALE_STRING         = new Object();
92
93    /*
94     * Constructors.
95     */
96    NativeArray() {
97        this(ArrayData.initialArray());
98    }
99
100    NativeArray(final long length) {
101        this(ArrayData.allocate(length));
102    }
103
104    NativeArray(final int[] array) {
105        this(ArrayData.allocate(array));
106    }
107
108    NativeArray(final double[] array) {
109        this(ArrayData.allocate(array));
110    }
111
112    NativeArray(final long[] array) {
113        this(ArrayData.allocate(array.length));
114
115        ArrayData arrayData = this.getArray();
116        Class<?> widest = int.class;
117
118        for (int index = 0; index < array.length; index++) {
119            final long value = array[index];
120
121            if (widest == int.class && JSType.isRepresentableAsInt(value)) {
122                arrayData = arrayData.set(index, (int) value, false);
123            } else if (widest != Object.class && JSType.isRepresentableAsDouble(value)) {
124                arrayData = arrayData.set(index, (double) value, false);
125                widest = double.class;
126            } else {
127                arrayData = arrayData.set(index, (Object) value, false);
128                widest = Object.class;
129            }
130        }
131
132        this.setArray(arrayData);
133    }
134
135    NativeArray(final Object[] array) {
136        this(ArrayData.allocate(array.length));
137
138        ArrayData arrayData = this.getArray();
139
140        for (int index = 0; index < array.length; index++) {
141            final Object value = array[index];
142
143            if (value == ScriptRuntime.EMPTY) {
144                arrayData = arrayData.delete(index);
145            } else {
146                arrayData = arrayData.set(index, value, false);
147            }
148        }
149
150        this.setArray(arrayData);
151    }
152
153    NativeArray(final ArrayData arrayData) {
154        this(arrayData, Global.instance());
155    }
156
157    NativeArray(final ArrayData arrayData, final Global global) {
158        super(global.getArrayPrototype(), $nasgenmap$);
159        setArray(arrayData);
160        setIsArray();
161    }
162
163    @Override
164    protected GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) {
165        final GuardedInvocation inv = getArray().findFastGetIndexMethod(getArray().getClass(), desc, request);
166        if (inv != null) {
167            return inv;
168        }
169        return super.findGetIndexMethod(desc, request);
170    }
171
172    @Override
173    protected GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) {
174        final GuardedInvocation inv = getArray().findFastSetIndexMethod(getArray().getClass(), desc, request);
175        if (inv != null) {
176            return inv;
177        }
178
179        return super.findSetIndexMethod(desc, request);
180    }
181
182    private static InvokeByName getJOIN() {
183        return Global.instance().getInvokeByName(JOIN,
184                new Callable<InvokeByName>() {
185                    @Override
186                    public InvokeByName call() {
187                        return new InvokeByName("join", ScriptObject.class);
188                    }
189                });
190    }
191
192    private static MethodHandle createIteratorCallbackInvoker(final Object key, final Class<?> rtype) {
193        return Global.instance().getDynamicInvoker(key,
194            new Callable<MethodHandle>() {
195                @Override
196                public MethodHandle call() {
197                    return Bootstrap.createDynamicCallInvoker(rtype, Object.class, Object.class, Object.class,
198                        double.class, Object.class);
199                }
200            });
201    }
202
203    private static MethodHandle getEVERY_CALLBACK_INVOKER() {
204        return createIteratorCallbackInvoker(EVERY_CALLBACK_INVOKER, boolean.class);
205    }
206
207    private static MethodHandle getSOME_CALLBACK_INVOKER() {
208        return createIteratorCallbackInvoker(SOME_CALLBACK_INVOKER, boolean.class);
209    }
210
211    private static MethodHandle getFOREACH_CALLBACK_INVOKER() {
212        return createIteratorCallbackInvoker(FOREACH_CALLBACK_INVOKER, void.class);
213    }
214
215    private static MethodHandle getMAP_CALLBACK_INVOKER() {
216        return createIteratorCallbackInvoker(MAP_CALLBACK_INVOKER, Object.class);
217    }
218
219    private static MethodHandle getFILTER_CALLBACK_INVOKER() {
220        return createIteratorCallbackInvoker(FILTER_CALLBACK_INVOKER, boolean.class);
221    }
222
223    private static MethodHandle getREDUCE_CALLBACK_INVOKER() {
224        return Global.instance().getDynamicInvoker(REDUCE_CALLBACK_INVOKER,
225                new Callable<MethodHandle>() {
226                    @Override
227                    public MethodHandle call() {
228                        return Bootstrap.createDynamicCallInvoker(Object.class, Object.class,
229                             Undefined.class, Object.class, Object.class, double.class, Object.class);
230                    }
231                });
232    }
233
234    private static MethodHandle getCALL_CMP() {
235        return Global.instance().getDynamicInvoker(CALL_CMP,
236                new Callable<MethodHandle>() {
237                    @Override
238                    public MethodHandle call() {
239                        return Bootstrap.createDynamicCallInvoker(double.class,
240                            Object.class, Object.class, Object.class, Object.class);
241                    }
242                });
243    }
244
245    private static InvokeByName getTO_LOCALE_STRING() {
246        return Global.instance().getInvokeByName(TO_LOCALE_STRING,
247                new Callable<InvokeByName>() {
248                    @Override
249                    public InvokeByName call() {
250                        return new InvokeByName("toLocaleString", ScriptObject.class, String.class);
251                    }
252                });
253    }
254
255    // initialized by nasgen
256    private static PropertyMap $nasgenmap$;
257
258    @Override
259    public String getClassName() {
260        return "Array";
261    }
262
263    @Override
264    public Object getLength() {
265        final long length = getArray().length();
266        assert length >= 0L;
267        if (length <= Integer.MAX_VALUE) {
268            return (int)length;
269        }
270        return length;
271    }
272
273    private boolean defineLength(final long oldLen, final PropertyDescriptor oldLenDesc, final PropertyDescriptor desc, final boolean reject) {
274        // Step 3a
275        if (!desc.has(VALUE)) {
276            return super.defineOwnProperty("length", desc, reject);
277        }
278
279        // Step 3b
280        final PropertyDescriptor newLenDesc = desc;
281
282        // Step 3c and 3d - get new length and convert to long
283        final long newLen = NativeArray.validLength(newLenDesc.getValue());
284
285        // Step 3e - note that we need to convert to int or double as long is not considered a JS number type anymore
286        newLenDesc.setValue(JSType.toNarrowestNumber(newLen));
287
288        // Step 3f
289        // increasing array length - just need to set new length value (and attributes if any) and return
290        if (newLen >= oldLen) {
291            return super.defineOwnProperty("length", newLenDesc, reject);
292        }
293
294        // Step 3g
295        if (!oldLenDesc.isWritable()) {
296            if (reject) {
297                throw typeError("property.not.writable", "length", ScriptRuntime.safeToString(this));
298            }
299            return false;
300        }
301
302        // Step 3h and 3i
303        final boolean newWritable = !newLenDesc.has(WRITABLE) || newLenDesc.isWritable();
304        if (!newWritable) {
305            newLenDesc.setWritable(true);
306        }
307
308        // Step 3j and 3k
309        final boolean succeeded = super.defineOwnProperty("length", newLenDesc, reject);
310        if (!succeeded) {
311            return false;
312        }
313
314        // Step 3l
315        // make sure that length is set till the point we can delete the old elements
316        long o = oldLen;
317        while (newLen < o) {
318            o--;
319            final boolean deleteSucceeded = delete(o, false);
320            if (!deleteSucceeded) {
321                newLenDesc.setValue(o + 1);
322                if (!newWritable) {
323                    newLenDesc.setWritable(false);
324                }
325                super.defineOwnProperty("length", newLenDesc, false);
326                if (reject) {
327                    throw typeError("property.not.writable", "length", ScriptRuntime.safeToString(this));
328                }
329                return false;
330            }
331        }
332
333        // Step 3m
334        if (!newWritable) {
335            // make 'length' property not writable
336            final ScriptObject newDesc = Global.newEmptyInstance();
337            newDesc.set(WRITABLE, false, 0);
338            return super.defineOwnProperty("length", newDesc, false);
339        }
340
341        return true;
342    }
343
344    /**
345     * ECMA 15.4.5.1 [[DefineOwnProperty]] ( P, Desc, Throw )
346     */
347    @Override
348    public boolean defineOwnProperty(final Object key, final Object propertyDesc, final boolean reject) {
349        final PropertyDescriptor desc = toPropertyDescriptor(Global.instance(), propertyDesc);
350
351        // never be undefined as "length" is always defined and can't be deleted for arrays
352        // Step 1
353        final PropertyDescriptor oldLenDesc = (PropertyDescriptor) super.getOwnPropertyDescriptor("length");
354
355        // Step 2
356        // get old length and convert to long. Always a Long/Uint32 but we take the safe road.
357        final long oldLen = JSType.toUint32(oldLenDesc.getValue());
358
359        // Step 3
360        if ("length".equals(key)) {
361            // check for length being made non-writable
362            final boolean result = defineLength(oldLen, oldLenDesc, desc, reject);
363            if (desc.has(WRITABLE) && !desc.isWritable()) {
364                setIsLengthNotWritable();
365            }
366            return result;
367        }
368
369        // Step 4a
370        final int index = ArrayIndex.getArrayIndex(key);
371        if (ArrayIndex.isValidArrayIndex(index)) {
372            final long longIndex = ArrayIndex.toLongIndex(index);
373            // Step 4b
374            // setting an element beyond current length, but 'length' is not writable
375            if (longIndex >= oldLen && !oldLenDesc.isWritable()) {
376                if (reject) {
377                    throw typeError("property.not.writable", Long.toString(longIndex), ScriptRuntime.safeToString(this));
378                }
379                return false;
380            }
381
382            // Step 4c
383            // set the new array element
384            final boolean succeeded = super.defineOwnProperty(key, desc, false);
385
386            // Step 4d
387            if (!succeeded) {
388                if (reject) {
389                    throw typeError("cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this));
390                }
391                return false;
392            }
393
394            // Step 4e -- adjust new length based on new element index that is set
395            if (longIndex >= oldLen) {
396                oldLenDesc.setValue(longIndex + 1);
397                super.defineOwnProperty("length", oldLenDesc, false);
398            }
399
400            // Step 4f
401            return true;
402        }
403
404        // not an index property
405        return super.defineOwnProperty(key, desc, reject);
406    }
407
408    /**
409     * Spec. mentions use of [[DefineOwnProperty]] for indexed properties in
410     * certain places (eg. Array.prototype.map, filter). We can not use ScriptObject.set
411     * method in such cases. This is because set method uses inherited setters (if any)
412     * from any object in proto chain such as Array.prototype, Object.prototype.
413     * This method directly sets a particular element value in the current object.
414     *
415     * @param index key for property
416     * @param value value to define
417     */
418    @Override
419    public final void defineOwnProperty(final int index, final Object value) {
420        assert isValidArrayIndex(index) : "invalid array index";
421        final long longIndex = ArrayIndex.toLongIndex(index);
422        if (longIndex >= getArray().length()) {
423            // make array big enough to hold..
424            setArray(getArray().ensure(longIndex));
425        }
426        setArray(getArray().set(index, value, false));
427    }
428
429    /**
430     * Return the array contents upcasted as an ObjectArray, regardless of
431     * representation
432     *
433     * @return an object array
434     */
435    public Object[] asObjectArray() {
436        return getArray().asObjectArray();
437    }
438
439    @Override
440    public void setIsLengthNotWritable() {
441        super.setIsLengthNotWritable();
442        setArray(ArrayData.setIsLengthNotWritable(getArray()));
443    }
444
445    /**
446     * ECMA 15.4.3.2 Array.isArray ( arg )
447     *
448     * @param self self reference
449     * @param arg  argument - object to check
450     * @return true if argument is an array
451     */
452    @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
453    public static boolean isArray(final Object self, final Object arg) {
454        return isArray(arg) || (arg instanceof JSObject && ((JSObject)arg).isArray());
455    }
456
457    /**
458     * Length getter
459     * @param self self reference
460     * @return the length of the object
461     */
462    @Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE)
463    public static Object length(final Object self) {
464        if (isArray(self)) {
465            final long length = ((ScriptObject) self).getArray().length();
466            assert length >= 0L;
467            // Cast to the narrowest supported numeric type to help optimistic type calculator
468            if (length <= Integer.MAX_VALUE) {
469                return (int) length;
470            }
471            return (double) length;
472        }
473
474        return 0;
475    }
476
477    /**
478     * Length setter
479     * @param self   self reference
480     * @param length new length property
481     */
482    @Setter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE)
483    public static void length(final Object self, final Object length) {
484        if (isArray(self)) {
485            ((ScriptObject)self).setLength(validLength(length));
486        }
487    }
488
489    /**
490     * Prototype length getter
491     * @param self self reference
492     * @return the length of the object
493     */
494    @Getter(name = "length", where = Where.PROTOTYPE, attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE)
495    public static Object getProtoLength(final Object self) {
496        return length(self);  // Same as instance getter but we can't make nasgen use the same method for prototype
497    }
498
499    /**
500     * Prototype length setter
501     * @param self   self reference
502     * @param length new length property
503     */
504    @Setter(name = "length", where = Where.PROTOTYPE, attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE)
505    public static void setProtoLength(final Object self, final Object length) {
506        length(self, length);  // Same as instance setter but we can't make nasgen use the same method for prototype
507    }
508
509    static long validLength(final Object length) {
510        // ES5 15.4.5.1, steps 3.c and 3.d require two ToNumber conversions here
511        final double doubleLength = JSType.toNumber(length);
512        if (doubleLength != JSType.toUint32(length)) {
513            throw rangeError("inappropriate.array.length", ScriptRuntime.safeToString(length));
514        }
515        return (long) doubleLength;
516    }
517
518    /**
519     * ECMA 15.4.4.2 Array.prototype.toString ( )
520     *
521     * @param self self reference
522     * @return string representation of array
523     */
524    @Function(attributes = Attribute.NOT_ENUMERABLE)
525    public static Object toString(final Object self) {
526        final Object obj = Global.toObject(self);
527        if (obj instanceof ScriptObject) {
528            final InvokeByName joinInvoker = getJOIN();
529            final ScriptObject sobj = (ScriptObject)obj;
530            try {
531                final Object join = joinInvoker.getGetter().invokeExact(sobj);
532                if (Bootstrap.isCallable(join)) {
533                    return joinInvoker.getInvoker().invokeExact(join, sobj);
534                }
535            } catch (final RuntimeException | Error e) {
536                throw e;
537            } catch (final Throwable t) {
538                throw new RuntimeException(t);
539            }
540        }
541
542        // FIXME: should lookup Object.prototype.toString and call that?
543        return ScriptRuntime.builtinObjectToString(self);
544    }
545
546    /**
547     * Assert that an array is numeric, if not throw type error
548     * @param self self array to check
549     * @return true if numeric
550     */
551    @Function(attributes = Attribute.NOT_ENUMERABLE)
552    public static Object assertNumeric(final Object self) {
553        if(!(self instanceof NativeArray && ((NativeArray)self).getArray().getOptimisticType().isNumeric())) {
554            throw typeError("not.a.numeric.array", ScriptRuntime.safeToString(self));
555        }
556        return Boolean.TRUE;
557    }
558
559    /**
560     * ECMA 15.4.4.3 Array.prototype.toLocaleString ( )
561     *
562     * @param self self reference
563     * @return locale specific string representation for array
564     */
565    @Function(attributes = Attribute.NOT_ENUMERABLE)
566    public static String toLocaleString(final Object self) {
567        final StringBuilder sb = new StringBuilder();
568        final Iterator<Object> iter = arrayLikeIterator(self, true);
569
570        while (iter.hasNext()) {
571            final Object obj = iter.next();
572
573            if (obj != null && obj != ScriptRuntime.UNDEFINED) {
574                final Object val = JSType.toScriptObject(obj);
575
576                try {
577                    if (val instanceof ScriptObject) {
578                        final InvokeByName localeInvoker = getTO_LOCALE_STRING();
579                        final ScriptObject sobj           = (ScriptObject)val;
580                        final Object       toLocaleString = localeInvoker.getGetter().invokeExact(sobj);
581
582                        if (Bootstrap.isCallable(toLocaleString)) {
583                            sb.append((String)localeInvoker.getInvoker().invokeExact(toLocaleString, sobj));
584                        } else {
585                            throw typeError("not.a.function", "toLocaleString");
586                        }
587                    }
588                } catch (final Error|RuntimeException t) {
589                    throw t;
590                } catch (final Throwable t) {
591                    throw new RuntimeException(t);
592                }
593            }
594
595            if (iter.hasNext()) {
596                sb.append(",");
597            }
598        }
599
600        return sb.toString();
601    }
602
603    /**
604     * ECMA 15.4.2.2 new Array (len)
605     *
606     * @param newObj was the new operator used to instantiate this array
607     * @param self   self reference
608     * @param args   arguments (length)
609     * @return the new NativeArray
610     */
611    @Constructor(arity = 1)
612    public static NativeArray construct(final boolean newObj, final Object self, final Object... args) {
613        switch (args.length) {
614        case 0:
615            return new NativeArray(0);
616        case 1:
617            final Object len = args[0];
618            if (len instanceof Number) {
619                long length;
620                if (len instanceof Integer || len instanceof Long) {
621                    length = ((Number) len).longValue();
622                    if (length >= 0 && length < JSType.MAX_UINT) {
623                        return new NativeArray(length);
624                    }
625                }
626
627                length = JSType.toUint32(len);
628
629                /*
630                 * If the argument len is a Number and ToUint32(len) is equal to
631                 * len, then the length property of the newly constructed object
632                 * is set to ToUint32(len). If the argument len is a Number and
633                 * ToUint32(len) is not equal to len, a RangeError exception is
634                 * thrown.
635                 */
636                final double numberLength = ((Number) len).doubleValue();
637                if (length != numberLength) {
638                    throw rangeError("inappropriate.array.length", JSType.toString(numberLength));
639                }
640
641                return new NativeArray(length);
642            }
643            /*
644             * If the argument len is not a Number, then the length property of
645             * the newly constructed object is set to 1 and the 0 property of
646             * the newly constructed object is set to len
647             */
648            return new NativeArray(new Object[]{args[0]});
649            //fallthru
650        default:
651            return new NativeArray(args);
652        }
653    }
654
655    /**
656     * ECMA 15.4.2.2 new Array (len)
657     *
658     * Specialized constructor for zero arguments - empty array
659     *
660     * @param newObj was the new operator used to instantiate this array
661     * @param self   self reference
662     * @return the new NativeArray
663     */
664    @SpecializedFunction(isConstructor=true)
665    public static NativeArray construct(final boolean newObj, final Object self) {
666        return new NativeArray(0);
667    }
668
669    /**
670     * ECMA 15.4.2.2 new Array (len)
671     *
672     * Specialized constructor for zero arguments - empty array
673     *
674     * @param newObj  was the new operator used to instantiate this array
675     * @param self    self reference
676     * @param element first element
677     * @return the new NativeArray
678     */
679    @SpecializedFunction(isConstructor=true)
680    public static Object construct(final boolean newObj, final Object self, final boolean element) {
681        return new NativeArray(new Object[] { element });
682    }
683
684    /**
685     * ECMA 15.4.2.2 new Array (len)
686     *
687     * Specialized constructor for one integer argument (length)
688     *
689     * @param newObj was the new operator used to instantiate this array
690     * @param self   self reference
691     * @param length array length
692     * @return the new NativeArray
693     */
694    @SpecializedFunction(isConstructor=true)
695    public static NativeArray construct(final boolean newObj, final Object self, final int length) {
696        if (length >= 0) {
697            return new NativeArray(length);
698        }
699
700        return construct(newObj, self, new Object[]{length});
701    }
702
703    /**
704     * ECMA 15.4.2.2 new Array (len)
705     *
706     * Specialized constructor for one long argument (length)
707     *
708     * @param newObj was the new operator used to instantiate this array
709     * @param self   self reference
710     * @param length array length
711     * @return the new NativeArray
712     */
713    @SpecializedFunction(isConstructor=true)
714    public static NativeArray construct(final boolean newObj, final Object self, final long length) {
715        if (length >= 0L && length <= JSType.MAX_UINT) {
716            return new NativeArray(length);
717        }
718
719        return construct(newObj, self, new Object[]{length});
720    }
721
722    /**
723     * ECMA 15.4.2.2 new Array (len)
724     *
725     * Specialized constructor for one double argument (length)
726     *
727     * @param newObj was the new operator used to instantiate this array
728     * @param self   self reference
729     * @param length array length
730     * @return the new NativeArray
731     */
732    @SpecializedFunction(isConstructor=true)
733    public static NativeArray construct(final boolean newObj, final Object self, final double length) {
734        final long uint32length = JSType.toUint32(length);
735
736        if (uint32length == length) {
737            return new NativeArray(uint32length);
738        }
739
740        return construct(newObj, self, new Object[]{length});
741    }
742
743    /**
744     * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] )
745     *
746     * @param self self reference
747     * @param arg argument
748     * @return resulting NativeArray
749     */
750    @SpecializedFunction(linkLogic=ConcatLinkLogic.class, convertsNumericArgs = false)
751    public static NativeArray concat(final Object self, final int arg) {
752        final ContinuousArrayData newData = getContinuousArrayDataCCE(self, Integer.class).copy(); //get at least an integer data copy of this data
753        newData.fastPush(arg); //add an integer to its end
754        return new NativeArray(newData);
755    }
756
757    /**
758     * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] )
759     *
760     * @param self self reference
761     * @param arg argument
762     * @return resulting NativeArray
763     */
764    @SpecializedFunction(linkLogic=ConcatLinkLogic.class, convertsNumericArgs = false)
765    public static NativeArray concat(final Object self, final double arg) {
766        final ContinuousArrayData newData = getContinuousArrayDataCCE(self, Double.class).copy(); //get at least a number array data copy of this data
767        newData.fastPush(arg); //add a double at the end
768        return new NativeArray(newData);
769    }
770
771    /**
772     * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] )
773     *
774     * @param self self reference
775     * @param arg argument
776     * @return resulting NativeArray
777     */
778    @SpecializedFunction(linkLogic=ConcatLinkLogic.class)
779    public static NativeArray concat(final Object self, final Object arg) {
780        //arg is [NativeArray] of same type.
781        final ContinuousArrayData selfData = getContinuousArrayDataCCE(self);
782        final ContinuousArrayData newData;
783
784        if (arg instanceof NativeArray) {
785            final ContinuousArrayData argData = (ContinuousArrayData)((NativeArray)arg).getArray();
786            if (argData.isEmpty()) {
787                newData = selfData.copy();
788            } else if (selfData.isEmpty()) {
789                newData = argData.copy();
790            } else {
791                final Class<?> widestElementType = selfData.widest(argData).getBoxedElementType();
792                newData = ((ContinuousArrayData)selfData.convert(widestElementType)).fastConcat((ContinuousArrayData)argData.convert(widestElementType));
793            }
794        } else {
795            newData = getContinuousArrayDataCCE(self, Object.class).copy();
796            newData.fastPush(arg);
797        }
798
799        return new NativeArray(newData);
800    }
801
802    /**
803     * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] )
804     *
805     * @param self self reference
806     * @param args arguments
807     * @return resulting NativeArray
808     */
809    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
810    public static NativeArray concat(final Object self, final Object... args) {
811        final ArrayList<Object> list = new ArrayList<>();
812
813        concatToList(list, Global.toObject(self));
814
815        for (final Object obj : args) {
816            concatToList(list, obj);
817        }
818
819        return new NativeArray(list.toArray());
820    }
821
822    private static void concatToList(final ArrayList<Object> list, final Object obj) {
823        final boolean isScriptArray  = isArray(obj);
824        final boolean isScriptObject = isScriptArray || obj instanceof ScriptObject;
825        if (isScriptArray || obj instanceof Iterable || obj instanceof JSObject || (obj != null && obj.getClass().isArray())) {
826            final Iterator<Object> iter = arrayLikeIterator(obj, true);
827            if (iter.hasNext()) {
828                for (int i = 0; iter.hasNext(); ++i) {
829                    final Object value = iter.next();
830                    if (value == ScriptRuntime.UNDEFINED && isScriptObject && !((ScriptObject)obj).has(i)) {
831                        // TODO: eventually rewrite arrayLikeIterator to use a three-state enum for handling
832                        // UNDEFINED instead of an "includeUndefined" boolean with states SKIP, INCLUDE,
833                        // RETURN_EMPTY. Until then, this is how we'll make sure that empty elements don't make it
834                        // into the concatenated array.
835                        list.add(ScriptRuntime.EMPTY);
836                    } else {
837                        list.add(value);
838                    }
839                }
840            } else if (!isScriptArray) {
841                list.add(obj); // add empty object, but not an empty array
842            }
843        } else {
844            // single element, add it
845            list.add(obj);
846        }
847    }
848
849    /**
850     * ECMA 15.4.4.5 Array.prototype.join (separator)
851     *
852     * @param self      self reference
853     * @param separator element separator
854     * @return string representation after join
855     */
856    @Function(attributes = Attribute.NOT_ENUMERABLE)
857    public static String join(final Object self, final Object separator) {
858        final StringBuilder    sb   = new StringBuilder();
859        final Iterator<Object> iter = arrayLikeIterator(self, true);
860        final String           sep  = separator == ScriptRuntime.UNDEFINED ? "," : JSType.toString(separator);
861
862        while (iter.hasNext()) {
863            final Object obj = iter.next();
864
865            if (obj != null && obj != ScriptRuntime.UNDEFINED) {
866                sb.append(JSType.toString(obj));
867            }
868
869            if (iter.hasNext()) {
870                sb.append(sep);
871            }
872        }
873
874        return sb.toString();
875    }
876
877    /**
878     * Specialization of pop for ContinuousArrayData
879     *   The link guard checks that the array is continuous AND not empty.
880     *   The runtime guard checks that the guard is continuous (CCE otherwise)
881     *
882     * Primitive specialization, {@link LinkLogic}
883     *
884     * @param self self reference
885     * @return element popped
886     * @throws ClassCastException if array is empty, facilitating Undefined return value
887     */
888    @SpecializedFunction(name="pop", linkLogic=PopLinkLogic.class)
889    public static int popInt(final Object self) {
890        //must be non empty IntArrayData
891        return getContinuousNonEmptyArrayDataCCE(self, IntElements.class).fastPopInt();
892    }
893
894    /**
895     * Specialization of pop for ContinuousArrayData
896     *
897     * Primitive specialization, {@link LinkLogic}
898     *
899     * @param self self reference
900     * @return element popped
901     * @throws ClassCastException if array is empty, facilitating Undefined return value
902     */
903    @SpecializedFunction(name="pop", linkLogic=PopLinkLogic.class)
904    public static double popDouble(final Object self) {
905        //must be non empty int long or double array data
906        return getContinuousNonEmptyArrayDataCCE(self, NumericElements.class).fastPopDouble();
907    }
908
909    /**
910     * Specialization of pop for ContinuousArrayData
911     *
912     * Primitive specialization, {@link LinkLogic}
913     *
914     * @param self self reference
915     * @return element popped
916     * @throws ClassCastException if array is empty, facilitating Undefined return value
917     */
918    @SpecializedFunction(name="pop", linkLogic=PopLinkLogic.class)
919    public static Object popObject(final Object self) {
920        //can be any data, because the numeric ones will throw cce and force relink
921        return getContinuousArrayDataCCE(self, null).fastPopObject();
922    }
923
924    /**
925     * ECMA 15.4.4.6 Array.prototype.pop ()
926     *
927     * @param self self reference
928     * @return array after pop
929     */
930    @Function(attributes = Attribute.NOT_ENUMERABLE)
931    public static Object pop(final Object self) {
932        try {
933            final ScriptObject sobj = (ScriptObject)self;
934
935            if (bulkable(sobj)) {
936                return sobj.getArray().pop();
937            }
938
939            final long len = JSType.toUint32(sobj.getLength());
940
941            if (len == 0) {
942                sobj.set("length", 0, CALLSITE_STRICT);
943                return ScriptRuntime.UNDEFINED;
944            }
945
946            final long   index   = len - 1;
947            final Object element = sobj.get(index);
948
949            sobj.delete(index, true);
950            sobj.set("length", index, CALLSITE_STRICT);
951
952            return element;
953        } catch (final ClassCastException | NullPointerException e) {
954            throw typeError("not.an.object", ScriptRuntime.safeToString(self));
955        }
956    }
957
958    /**
959     * ECMA 15.4.4.7 Array.prototype.push (args...)
960     *
961     * Primitive specialization, {@link LinkLogic}
962     *
963     * @param self self reference
964     * @param arg a primitive to push
965     * @return array length after push
966     */
967    @SpecializedFunction(linkLogic=PushLinkLogic.class, convertsNumericArgs = false)
968    public static double push(final Object self, final int arg) {
969        return getContinuousArrayDataCCE(self, Integer.class).fastPush(arg);
970    }
971
972    /**
973     * ECMA 15.4.4.7 Array.prototype.push (args...)
974     *
975     * Primitive specialization, {@link LinkLogic}
976     *
977     * @param self self reference
978     * @param arg a primitive to push
979     * @return array length after push
980     */
981    @SpecializedFunction(linkLogic=PushLinkLogic.class, convertsNumericArgs = false)
982    public static double push(final Object self, final double arg) {
983        return getContinuousArrayDataCCE(self, Double.class).fastPush(arg);
984    }
985
986    /**
987     * ECMA 15.4.4.7 Array.prototype.push (args...)
988     *
989     * Primitive specialization, {@link LinkLogic}
990     *
991     * @param self self reference
992     * @param arg a primitive to push
993     * @return array length after push
994     */
995    @SpecializedFunction(name="push", linkLogic=PushLinkLogic.class)
996    public static double pushObject(final Object self, final Object arg) {
997        return getContinuousArrayDataCCE(self, Object.class).fastPush(arg);
998    }
999
1000    /**
1001     * ECMA 15.4.4.7 Array.prototype.push (args...)
1002     *
1003     * @param self self reference
1004     * @param args arguments to push
1005     * @return array length after pushes
1006     */
1007    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
1008    public static Object push(final Object self, final Object... args) {
1009        try {
1010            final ScriptObject sobj   = (ScriptObject)self;
1011
1012            if (bulkable(sobj) && sobj.getArray().length() + args.length <= JSType.MAX_UINT) {
1013                final ArrayData newData = sobj.getArray().push(true, args);
1014                sobj.setArray(newData);
1015                return JSType.toNarrowestNumber(newData.length());
1016            }
1017
1018            long len = JSType.toUint32(sobj.getLength());
1019            for (final Object element : args) {
1020                sobj.set(len++, element, CALLSITE_STRICT);
1021            }
1022            sobj.set("length", len, CALLSITE_STRICT);
1023
1024            return JSType.toNarrowestNumber(len);
1025        } catch (final ClassCastException | NullPointerException e) {
1026            throw typeError(Context.getGlobal(), e, "not.an.object", ScriptRuntime.safeToString(self));
1027        }
1028    }
1029
1030    /**
1031     * ECMA 15.4.4.7 Array.prototype.push (args...) specialized for single object argument
1032     *
1033     * @param self self reference
1034     * @param arg argument to push
1035     * @return array after pushes
1036     */
1037    @SpecializedFunction
1038    public static double push(final Object self, final Object arg) {
1039        try {
1040            final ScriptObject sobj = (ScriptObject)self;
1041            final ArrayData arrayData = sobj.getArray();
1042            final long length = arrayData.length();
1043            if (bulkable(sobj) && length < JSType.MAX_UINT) {
1044                sobj.setArray(arrayData.push(true, arg));
1045                return length + 1;
1046            }
1047
1048            long len = JSType.toUint32(sobj.getLength());
1049            sobj.set(len++, arg, CALLSITE_STRICT);
1050            sobj.set("length", len, CALLSITE_STRICT);
1051            return len;
1052        } catch (final ClassCastException | NullPointerException e) {
1053            throw typeError("not.an.object", ScriptRuntime.safeToString(self));
1054        }
1055    }
1056
1057    /**
1058     * ECMA 15.4.4.8 Array.prototype.reverse ()
1059     *
1060     * @param self self reference
1061     * @return reversed array
1062     */
1063    @Function(attributes = Attribute.NOT_ENUMERABLE)
1064    public static Object reverse(final Object self) {
1065        try {
1066            final ScriptObject sobj   = (ScriptObject)self;
1067            final long         len    = JSType.toUint32(sobj.getLength());
1068            final long         middle = len / 2;
1069
1070            for (long lower = 0; lower != middle; lower++) {
1071                final long    upper       = len - lower - 1;
1072                final Object  lowerValue  = sobj.get(lower);
1073                final Object  upperValue  = sobj.get(upper);
1074                final boolean lowerExists = sobj.has(lower);
1075                final boolean upperExists = sobj.has(upper);
1076
1077                if (lowerExists && upperExists) {
1078                    sobj.set(lower, upperValue, CALLSITE_STRICT);
1079                    sobj.set(upper, lowerValue, CALLSITE_STRICT);
1080                } else if (!lowerExists && upperExists) {
1081                    sobj.set(lower, upperValue, CALLSITE_STRICT);
1082                    sobj.delete(upper, true);
1083                } else if (lowerExists && !upperExists) {
1084                    sobj.delete(lower, true);
1085                    sobj.set(upper, lowerValue, CALLSITE_STRICT);
1086                }
1087            }
1088            return sobj;
1089        } catch (final ClassCastException | NullPointerException e) {
1090            throw typeError("not.an.object", ScriptRuntime.safeToString(self));
1091        }
1092    }
1093
1094    /**
1095     * ECMA 15.4.4.9 Array.prototype.shift ()
1096     *
1097     * @param self self reference
1098     * @return shifted array
1099     */
1100    @Function(attributes = Attribute.NOT_ENUMERABLE)
1101    public static Object shift(final Object self) {
1102        final Object obj = Global.toObject(self);
1103
1104        Object first = ScriptRuntime.UNDEFINED;
1105
1106        if (!(obj instanceof ScriptObject)) {
1107            return first;
1108        }
1109
1110        final ScriptObject sobj   = (ScriptObject) obj;
1111
1112        long len = JSType.toUint32(sobj.getLength());
1113
1114        if (len > 0) {
1115            first = sobj.get(0);
1116
1117            if (bulkable(sobj)) {
1118                sobj.getArray().shiftLeft(1);
1119            } else {
1120                boolean hasPrevious = true;
1121                for (long k = 1; k < len; k++) {
1122                    final boolean hasCurrent = sobj.has(k);
1123                    if (hasCurrent) {
1124                        sobj.set(k - 1, sobj.get(k), CALLSITE_STRICT);
1125                    } else if (hasPrevious) {
1126                        sobj.delete(k - 1, true);
1127                    }
1128                    hasPrevious = hasCurrent;
1129                }
1130            }
1131            sobj.delete(--len, true);
1132        } else {
1133            len = 0;
1134        }
1135
1136        sobj.set("length", len, CALLSITE_STRICT);
1137
1138        return first;
1139    }
1140
1141    /**
1142     * ECMA 15.4.4.10 Array.prototype.slice ( start [ , end ] )
1143     *
1144     * @param self  self reference
1145     * @param start start of slice (inclusive)
1146     * @param end   end of slice (optional, exclusive)
1147     * @return sliced array
1148     */
1149    @Function(attributes = Attribute.NOT_ENUMERABLE)
1150    public static Object slice(final Object self, final Object start, final Object end) {
1151        final Object       obj                 = Global.toObject(self);
1152        if (!(obj instanceof ScriptObject)) {
1153            return ScriptRuntime.UNDEFINED;
1154        }
1155
1156        final ScriptObject sobj                = (ScriptObject)obj;
1157        final long         len                 = JSType.toUint32(sobj.getLength());
1158        final long         relativeStart       = JSType.toLong(start);
1159        final long         relativeEnd         = end == ScriptRuntime.UNDEFINED ? len : JSType.toLong(end);
1160
1161        long k = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len);
1162        final long finale = relativeEnd < 0 ? Math.max(len + relativeEnd, 0) : Math.min(relativeEnd, len);
1163
1164        if (k >= finale) {
1165            return new NativeArray(0);
1166        }
1167
1168        if (bulkable(sobj)) {
1169            return new NativeArray(sobj.getArray().slice(k, finale));
1170        }
1171
1172        // Construct array with proper length to have a deleted filter on undefined elements
1173        final NativeArray copy = new NativeArray(finale - k);
1174        for (long n = 0; k < finale; n++, k++) {
1175            if (sobj.has(k)) {
1176                copy.defineOwnProperty(ArrayIndex.getArrayIndex(n), sobj.get(k));
1177            }
1178        }
1179
1180        return copy;
1181    }
1182
1183    private static Object compareFunction(final Object comparefn) {
1184        if (comparefn == ScriptRuntime.UNDEFINED) {
1185            return null;
1186        }
1187
1188        if (!Bootstrap.isCallable(comparefn)) {
1189            throw typeError("not.a.function", ScriptRuntime.safeToString(comparefn));
1190        }
1191
1192        return comparefn;
1193    }
1194
1195    private static Object[] sort(final Object[] array, final Object comparefn) {
1196        final Object cmp = compareFunction(comparefn);
1197
1198        final List<Object> list = Arrays.asList(array);
1199        final Object cmpThis = cmp == null || Bootstrap.isStrictCallable(cmp) ? ScriptRuntime.UNDEFINED : Global.instance();
1200
1201        try {
1202            Collections.sort(list, new Comparator<Object>() {
1203                private final MethodHandle call_cmp = getCALL_CMP();
1204                @Override
1205                public int compare(final Object x, final Object y) {
1206                    if (x == ScriptRuntime.UNDEFINED && y == ScriptRuntime.UNDEFINED) {
1207                        return 0;
1208                    } else if (x == ScriptRuntime.UNDEFINED) {
1209                        return 1;
1210                    } else if (y == ScriptRuntime.UNDEFINED) {
1211                        return -1;
1212                    }
1213
1214                    if (cmp != null) {
1215                        try {
1216                            return (int)Math.signum((double)call_cmp.invokeExact(cmp, cmpThis, x, y));
1217                        } catch (final RuntimeException | Error e) {
1218                            throw e;
1219                        } catch (final Throwable t) {
1220                            throw new RuntimeException(t);
1221                        }
1222                    }
1223
1224                    return JSType.toString(x).compareTo(JSType.toString(y));
1225                }
1226            });
1227        } catch (final IllegalArgumentException iae) {
1228            // Collections.sort throws IllegalArgumentException when
1229            // Comparison method violates its general contract
1230
1231            // See ECMA spec 15.4.4.11 Array.prototype.sort (comparefn).
1232            // If "comparefn" is not undefined and is not a consistent
1233            // comparison function for the elements of this array, the
1234            // behaviour of sort is implementation-defined.
1235        }
1236
1237        return list.toArray(new Object[0]);
1238    }
1239
1240    /**
1241     * ECMA 15.4.4.11 Array.prototype.sort ( comparefn )
1242     *
1243     * @param self       self reference
1244     * @param comparefn  element comparison function
1245     * @return sorted array
1246     */
1247    @Function(attributes = Attribute.NOT_ENUMERABLE)
1248    public static ScriptObject sort(final Object self, final Object comparefn) {
1249        try {
1250            final ScriptObject sobj    = (ScriptObject) self;
1251            final long         len     = JSType.toUint32(sobj.getLength());
1252            ArrayData          array   = sobj.getArray();
1253
1254            if (len > 1) {
1255                // Get only non-missing elements. Missing elements go at the end
1256                // of the sorted array. So, just don't copy these to sort input.
1257                final ArrayList<Object> src = new ArrayList<>();
1258
1259                for (final Iterator<Long> iter = array.indexIterator(); iter.hasNext(); ) {
1260                    final long index = iter.next();
1261                    if (index >= len) {
1262                        break;
1263                    }
1264                    src.add(array.getObject((int)index));
1265                }
1266
1267                final Object[] sorted = sort(src.toArray(), comparefn);
1268
1269                for (int i = 0; i < sorted.length; i++) {
1270                    array = array.set(i, sorted[i], true);
1271                }
1272
1273                // delete missing elements - which are at the end of sorted array
1274                if (sorted.length != len) {
1275                    array = array.delete(sorted.length, len - 1);
1276                }
1277
1278                sobj.setArray(array);
1279            }
1280
1281            return sobj;
1282        } catch (final ClassCastException | NullPointerException e) {
1283            throw typeError("not.an.object", ScriptRuntime.safeToString(self));
1284        }
1285    }
1286
1287    /**
1288     * ECMA 15.4.4.12 Array.prototype.splice ( start, deleteCount [ item1 [ , item2 [ , ... ] ] ] )
1289     *
1290     * @param self self reference
1291     * @param args arguments
1292     * @return result of splice
1293     */
1294    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
1295    public static Object splice(final Object self, final Object... args) {
1296        final Object obj = Global.toObject(self);
1297
1298        if (!(obj instanceof ScriptObject)) {
1299            return ScriptRuntime.UNDEFINED;
1300        }
1301
1302        final ScriptObject sobj          = (ScriptObject)obj;
1303        final long         len           = JSType.toUint32(sobj.getLength());
1304        final long         relativeStart = JSType.toLong(args.length > 0 ? args[0] : ScriptRuntime.UNDEFINED);
1305
1306        final long actualStart = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len);
1307        final long actualDeleteCount;
1308        Object[] items = ScriptRuntime.EMPTY_ARRAY;
1309
1310        if (args.length == 0) {
1311            actualDeleteCount = 0;
1312        } else if (args.length == 1) {
1313            actualDeleteCount = len - actualStart;
1314        } else {
1315            actualDeleteCount = Math.min(Math.max(JSType.toLong(args[1]), 0), len - actualStart);
1316            if (args.length > 2) {
1317                items = new Object[args.length - 2];
1318                System.arraycopy(args, 2, items, 0, items.length);
1319            }
1320        }
1321
1322        NativeArray returnValue;
1323
1324        if (actualStart <= Integer.MAX_VALUE && actualDeleteCount <= Integer.MAX_VALUE && bulkable(sobj)) {
1325            try {
1326                returnValue = new NativeArray(sobj.getArray().fastSplice((int)actualStart, (int)actualDeleteCount, items.length));
1327
1328                // Since this is a dense bulkable array we can use faster defineOwnProperty to copy new elements
1329                int k = (int) actualStart;
1330                for (int i = 0; i < items.length; i++, k++) {
1331                    sobj.defineOwnProperty(k, items[i]);
1332                }
1333            } catch (final UnsupportedOperationException uoe) {
1334                returnValue = slowSplice(sobj, actualStart, actualDeleteCount, items, len);
1335            }
1336        } else {
1337            returnValue = slowSplice(sobj, actualStart, actualDeleteCount, items, len);
1338        }
1339
1340        return returnValue;
1341    }
1342
1343    private static NativeArray slowSplice(final ScriptObject sobj, final long start, final long deleteCount, final Object[] items, final long len) {
1344
1345        final NativeArray array = new NativeArray(deleteCount);
1346
1347        for (long k = 0; k < deleteCount; k++) {
1348            final long from = start + k;
1349
1350            if (sobj.has(from)) {
1351                array.defineOwnProperty(ArrayIndex.getArrayIndex(k), sobj.get(from));
1352            }
1353        }
1354
1355        if (items.length < deleteCount) {
1356            for (long k = start; k < len - deleteCount; k++) {
1357                final long from = k + deleteCount;
1358                final long to   = k + items.length;
1359
1360                if (sobj.has(from)) {
1361                    sobj.set(to, sobj.get(from), CALLSITE_STRICT);
1362                } else {
1363                    sobj.delete(to, true);
1364                }
1365            }
1366
1367            for (long k = len; k > len - deleteCount + items.length; k--) {
1368                sobj.delete(k - 1, true);
1369            }
1370        } else if (items.length > deleteCount) {
1371            for (long k = len - deleteCount; k > start; k--) {
1372                final long from = k + deleteCount - 1;
1373                final long to   = k + items.length - 1;
1374
1375                if (sobj.has(from)) {
1376                    final Object fromValue = sobj.get(from);
1377                    sobj.set(to, fromValue, CALLSITE_STRICT);
1378                } else {
1379                    sobj.delete(to, true);
1380                }
1381            }
1382        }
1383
1384        long k = start;
1385        for (int i = 0; i < items.length; i++, k++) {
1386            sobj.set(k, items[i], CALLSITE_STRICT);
1387        }
1388
1389        final long newLength = len - deleteCount + items.length;
1390        sobj.set("length", newLength, CALLSITE_STRICT);
1391
1392        return array;
1393    }
1394
1395    /**
1396     * ECMA 15.4.4.13 Array.prototype.unshift ( [ item1 [ , item2 [ , ... ] ] ] )
1397     *
1398     * @param self  self reference
1399     * @param items items for unshift
1400     * @return unshifted array
1401     */
1402    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
1403    public static Object unshift(final Object self, final Object... items) {
1404        final Object obj = Global.toObject(self);
1405
1406        if (!(obj instanceof ScriptObject)) {
1407            return ScriptRuntime.UNDEFINED;
1408        }
1409
1410        final ScriptObject sobj   = (ScriptObject)obj;
1411        final long         len    = JSType.toUint32(sobj.getLength());
1412
1413        if (items == null) {
1414            return ScriptRuntime.UNDEFINED;
1415        }
1416
1417        if (bulkable(sobj)) {
1418            sobj.getArray().shiftRight(items.length);
1419
1420            for (int j = 0; j < items.length; j++) {
1421                sobj.setArray(sobj.getArray().set(j, items[j], true));
1422            }
1423        } else {
1424            for (long k = len; k > 0; k--) {
1425                final long from = k - 1;
1426                final long to = k + items.length - 1;
1427
1428                if (sobj.has(from)) {
1429                    final Object fromValue = sobj.get(from);
1430                    sobj.set(to, fromValue, CALLSITE_STRICT);
1431                } else {
1432                    sobj.delete(to, true);
1433                }
1434            }
1435
1436            for (int j = 0; j < items.length; j++) {
1437                sobj.set(j, items[j], CALLSITE_STRICT);
1438            }
1439        }
1440
1441        final long newLength = len + items.length;
1442        sobj.set("length", newLength, CALLSITE_STRICT);
1443
1444        return JSType.toNarrowestNumber(newLength);
1445    }
1446
1447    /**
1448     * ECMA 15.4.4.14 Array.prototype.indexOf ( searchElement [ , fromIndex ] )
1449     *
1450     * @param self           self reference
1451     * @param searchElement  element to search for
1452     * @param fromIndex      start index of search
1453     * @return index of element, or -1 if not found
1454     */
1455    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
1456    public static double indexOf(final Object self, final Object searchElement, final Object fromIndex) {
1457        try {
1458            final ScriptObject sobj = (ScriptObject)Global.toObject(self);
1459            final long         len  = JSType.toUint32(sobj.getLength());
1460            if (len == 0) {
1461                return -1;
1462            }
1463
1464            final long         n = JSType.toLong(fromIndex);
1465            if (n >= len) {
1466                return -1;
1467            }
1468
1469
1470            for (long k = Math.max(0, n < 0 ? len - Math.abs(n) : n); k < len; k++) {
1471                if (sobj.has(k)) {
1472                    if (ScriptRuntime.EQ_STRICT(sobj.get(k), searchElement)) {
1473                        return k;
1474                    }
1475                }
1476            }
1477        } catch (final ClassCastException | NullPointerException e) {
1478            //fallthru
1479        }
1480
1481        return -1;
1482    }
1483
1484    /**
1485     * ECMA 15.4.4.15 Array.prototype.lastIndexOf ( searchElement [ , fromIndex ] )
1486     *
1487     * @param self self reference
1488     * @param args arguments: element to search for and optional from index
1489     * @return index of element, or -1 if not found
1490     */
1491    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
1492    public static double lastIndexOf(final Object self, final Object... args) {
1493        try {
1494            final ScriptObject sobj = (ScriptObject)Global.toObject(self);
1495            final long         len  = JSType.toUint32(sobj.getLength());
1496
1497            if (len == 0) {
1498                return -1;
1499            }
1500
1501            final Object searchElement = args.length > 0 ? args[0] : ScriptRuntime.UNDEFINED;
1502            final long   n             = args.length > 1 ? JSType.toLong(args[1]) : len - 1;
1503
1504            for (long k = n < 0 ? len - Math.abs(n) : Math.min(n, len - 1); k >= 0; k--) {
1505                if (sobj.has(k)) {
1506                    if (ScriptRuntime.EQ_STRICT(sobj.get(k), searchElement)) {
1507                        return k;
1508                    }
1509                }
1510            }
1511        } catch (final ClassCastException | NullPointerException e) {
1512            throw typeError("not.an.object", ScriptRuntime.safeToString(self));
1513        }
1514
1515        return -1;
1516    }
1517
1518    /**
1519     * ECMA 15.4.4.16 Array.prototype.every ( callbackfn [ , thisArg ] )
1520     *
1521     * @param self        self reference
1522     * @param callbackfn  callback function per element
1523     * @param thisArg     this argument
1524     * @return true if callback function return true for every element in the array, false otherwise
1525     */
1526    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
1527    public static boolean every(final Object self, final Object callbackfn, final Object thisArg) {
1528        return applyEvery(Global.toObject(self), callbackfn, thisArg);
1529    }
1530
1531    private static boolean applyEvery(final Object self, final Object callbackfn, final Object thisArg) {
1532        return new IteratorAction<Boolean>(Global.toObject(self), callbackfn, thisArg, true) {
1533            private final MethodHandle everyInvoker = getEVERY_CALLBACK_INVOKER();
1534
1535            @Override
1536            protected boolean forEach(final Object val, final double i) throws Throwable {
1537                return result = (boolean)everyInvoker.invokeExact(callbackfn, thisArg, val, i, self);
1538            }
1539        }.apply();
1540    }
1541
1542    /**
1543     * ECMA 15.4.4.17 Array.prototype.some ( callbackfn [ , thisArg ] )
1544     *
1545     * @param self        self reference
1546     * @param callbackfn  callback function per element
1547     * @param thisArg     this argument
1548     * @return true if callback function returned true for any element in the array, false otherwise
1549     */
1550    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
1551    public static boolean some(final Object self, final Object callbackfn, final Object thisArg) {
1552        return new IteratorAction<Boolean>(Global.toObject(self), callbackfn, thisArg, false) {
1553            private final MethodHandle someInvoker = getSOME_CALLBACK_INVOKER();
1554
1555            @Override
1556            protected boolean forEach(final Object val, final double i) throws Throwable {
1557                return !(result = (boolean)someInvoker.invokeExact(callbackfn, thisArg, val, i, self));
1558            }
1559        }.apply();
1560    }
1561
1562    /**
1563     * ECMA 15.4.4.18 Array.prototype.forEach ( callbackfn [ , thisArg ] )
1564     *
1565     * @param self        self reference
1566     * @param callbackfn  callback function per element
1567     * @param thisArg     this argument
1568     * @return undefined
1569     */
1570    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
1571    public static Object forEach(final Object self, final Object callbackfn, final Object thisArg) {
1572        return new IteratorAction<Object>(Global.toObject(self), callbackfn, thisArg, ScriptRuntime.UNDEFINED) {
1573            private final MethodHandle forEachInvoker = getFOREACH_CALLBACK_INVOKER();
1574
1575            @Override
1576            protected boolean forEach(final Object val, final double i) throws Throwable {
1577                forEachInvoker.invokeExact(callbackfn, thisArg, val, i, self);
1578                return true;
1579            }
1580        }.apply();
1581    }
1582
1583    /**
1584     * ECMA 15.4.4.19 Array.prototype.map ( callbackfn [ , thisArg ] )
1585     *
1586     * @param self        self reference
1587     * @param callbackfn  callback function per element
1588     * @param thisArg     this argument
1589     * @return array with elements transformed by map function
1590     */
1591    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
1592    public static NativeArray map(final Object self, final Object callbackfn, final Object thisArg) {
1593        return new IteratorAction<NativeArray>(Global.toObject(self), callbackfn, thisArg, null) {
1594            private final MethodHandle mapInvoker = getMAP_CALLBACK_INVOKER();
1595
1596            @Override
1597            protected boolean forEach(final Object val, final double i) throws Throwable {
1598                final Object r = mapInvoker.invokeExact(callbackfn, thisArg, val, i, self);
1599                result.defineOwnProperty(ArrayIndex.getArrayIndex(index), r);
1600                return true;
1601            }
1602
1603            @Override
1604            public void applyLoopBegin(final ArrayLikeIterator<Object> iter0) {
1605                // map return array should be of same length as source array
1606                // even if callback reduces source array length
1607                result = new NativeArray(iter0.getLength());
1608            }
1609        }.apply();
1610    }
1611
1612    /**
1613     * ECMA 15.4.4.20 Array.prototype.filter ( callbackfn [ , thisArg ] )
1614     *
1615     * @param self        self reference
1616     * @param callbackfn  callback function per element
1617     * @param thisArg     this argument
1618     * @return filtered array
1619     */
1620    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
1621    public static NativeArray filter(final Object self, final Object callbackfn, final Object thisArg) {
1622        return new IteratorAction<NativeArray>(Global.toObject(self), callbackfn, thisArg, new NativeArray()) {
1623            private long to = 0;
1624            private final MethodHandle filterInvoker = getFILTER_CALLBACK_INVOKER();
1625
1626            @Override
1627            protected boolean forEach(final Object val, final double i) throws Throwable {
1628                if ((boolean)filterInvoker.invokeExact(callbackfn, thisArg, val, i, self)) {
1629                    result.defineOwnProperty(ArrayIndex.getArrayIndex(to++), val);
1630                }
1631                return true;
1632            }
1633        }.apply();
1634    }
1635
1636    private static Object reduceInner(final ArrayLikeIterator<Object> iter, final Object self, final Object... args) {
1637        final Object  callbackfn          = args.length > 0 ? args[0] : ScriptRuntime.UNDEFINED;
1638        final boolean initialValuePresent = args.length > 1;
1639
1640        Object initialValue = initialValuePresent ? args[1] : ScriptRuntime.UNDEFINED;
1641
1642        if (callbackfn == ScriptRuntime.UNDEFINED) {
1643            throw typeError("not.a.function", "undefined");
1644        }
1645
1646        if (!initialValuePresent) {
1647            if (iter.hasNext()) {
1648                initialValue = iter.next();
1649            } else {
1650                throw typeError("array.reduce.invalid.init");
1651            }
1652        }
1653
1654        //if initial value is ScriptRuntime.UNDEFINED - step forward once.
1655        return new IteratorAction<Object>(Global.toObject(self), callbackfn, ScriptRuntime.UNDEFINED, initialValue, iter) {
1656            private final MethodHandle reduceInvoker = getREDUCE_CALLBACK_INVOKER();
1657
1658            @Override
1659            protected boolean forEach(final Object val, final double i) throws Throwable {
1660                // TODO: why can't I declare the second arg as Undefined.class?
1661                result = reduceInvoker.invokeExact(callbackfn, ScriptRuntime.UNDEFINED, result, val, i, self);
1662                return true;
1663            }
1664        }.apply();
1665    }
1666
1667    /**
1668     * ECMA 15.4.4.21 Array.prototype.reduce ( callbackfn [ , initialValue ] )
1669     *
1670     * @param self self reference
1671     * @param args arguments to reduce
1672     * @return accumulated result
1673     */
1674    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
1675    public static Object reduce(final Object self, final Object... args) {
1676        return reduceInner(arrayLikeIterator(self), self, args);
1677    }
1678
1679    /**
1680     * ECMA 15.4.4.22 Array.prototype.reduceRight ( callbackfn [ , initialValue ] )
1681     *
1682     * @param self        self reference
1683     * @param args arguments to reduce
1684     * @return accumulated result
1685     */
1686    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
1687    public static Object reduceRight(final Object self, final Object... args) {
1688        return reduceInner(reverseArrayLikeIterator(self), self, args);
1689    }
1690
1691    /**
1692     * ECMA6 22.1.3.4 Array.prototype.entries ( )
1693     *
1694     * @param self the self reference
1695     * @return an iterator over the array's entries
1696     */
1697    @Function(attributes = Attribute.NOT_ENUMERABLE)
1698    public static Object entries(final Object self) {
1699        return ArrayIterator.newArrayKeyValueIterator(self);
1700    }
1701
1702    /**
1703     * ECMA6 22.1.3.13 Array.prototype.keys ( )
1704     *
1705     * @param self the self reference
1706     * @return an iterator over the array's keys
1707     */
1708    @Function(attributes = Attribute.NOT_ENUMERABLE)
1709    public static Object keys(final Object self) {
1710        return ArrayIterator.newArrayKeyIterator(self);
1711    }
1712
1713    /**
1714     * ECMA6 22.1.3.29 Array.prototype.values ( )
1715     *
1716     * @param self the self reference
1717     * @return an iterator over the array's values
1718     */
1719    @Function(attributes = Attribute.NOT_ENUMERABLE)
1720    public static Object values(final Object self) {
1721        return ArrayIterator.newArrayValueIterator(self);
1722    }
1723
1724    /**
1725     * 22.1.3.30 Array.prototype [ @@iterator ] ( )
1726     *
1727     * @param self the self reference
1728     * @return an iterator over the array's values
1729     */
1730    @Function(attributes = Attribute.NOT_ENUMERABLE, name = "@@iterator")
1731    public static Object getIterator(final Object self) {
1732        return ArrayIterator.newArrayValueIterator(self);
1733    }
1734
1735    /**
1736     * Determine if Java bulk array operations may be used on the underlying
1737     * storage. This is possible only if the object's prototype chain is empty
1738     * or each of the prototypes in the chain is empty.
1739     *
1740     * @param self the object to examine
1741     * @return true if optimizable
1742     */
1743    private static boolean bulkable(final ScriptObject self) {
1744        return self.isArray() && !hasInheritedArrayEntries(self) && !self.isLengthNotWritable();
1745    }
1746
1747    private static boolean hasInheritedArrayEntries(final ScriptObject self) {
1748        ScriptObject proto = self.getProto();
1749        while (proto != null) {
1750            if (proto.hasArrayEntries()) {
1751                return true;
1752            }
1753            proto = proto.getProto();
1754        }
1755
1756        return false;
1757    }
1758
1759    @Override
1760    public String toString() {
1761        return "NativeArray@" + Debug.id(this) + " [" + getArray().getClass().getSimpleName() + ']';
1762    }
1763
1764    @Override
1765    public SpecializedFunction.LinkLogic getLinkLogic(final Class<? extends LinkLogic> clazz) {
1766        if (clazz == PushLinkLogic.class) {
1767            return PushLinkLogic.INSTANCE;
1768        } else if (clazz == PopLinkLogic.class) {
1769            return PopLinkLogic.INSTANCE;
1770        } else if (clazz == ConcatLinkLogic.class) {
1771            return ConcatLinkLogic.INSTANCE;
1772        }
1773        return null;
1774    }
1775
1776    @Override
1777    public boolean hasPerInstanceAssumptions() {
1778        return true; //length writable switchpoint
1779    }
1780
1781    /**
1782     * This is an abstract super class that contains common functionality for all
1783     * specialized optimistic builtins in NativeArray. For example, it handles the
1784     * modification switchpoint which is touched when length is written.
1785     */
1786    private static abstract class ArrayLinkLogic extends SpecializedFunction.LinkLogic {
1787        protected ArrayLinkLogic() {
1788        }
1789
1790        protected static ContinuousArrayData getContinuousArrayData(final Object self) {
1791            try {
1792                //cast to NativeArray, to avoid cases like x = {0:0, 1:1}, x.length = 2, where we can't use the array push/pop
1793                return (ContinuousArrayData)((NativeArray)self).getArray();
1794            } catch (final Exception e) {
1795                return null;
1796            }
1797        }
1798
1799        /**
1800         * Push and pop callsites can throw ClassCastException as a mechanism to have them
1801         * relinked - this enabled fast checks of the kind of ((IntArrayData)arrayData).push(x)
1802         * for an IntArrayData only push - if this fails, a CCE will be thrown and we will relink
1803         */
1804        @Override
1805        public Class<? extends Throwable> getRelinkException() {
1806            return ClassCastException.class;
1807        }
1808    }
1809
1810    /**
1811     * This is linker logic for optimistic concatenations
1812     */
1813    private static final class ConcatLinkLogic extends ArrayLinkLogic {
1814        private static final LinkLogic INSTANCE = new ConcatLinkLogic();
1815
1816        @Override
1817        public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) {
1818            final Object[] args = request.getArguments();
1819
1820            if (args.length != 3) { //single argument check
1821                return false;
1822            }
1823
1824            final ContinuousArrayData selfData = getContinuousArrayData(self);
1825            if (selfData == null) {
1826                return false;
1827            }
1828
1829            final Object arg = args[2];
1830            // The generic version uses its own logic and ArrayLikeIterator to decide if an object should
1831            // be iterated over or added as single element. To avoid duplication of code and err on the safe side
1832            // we only use the specialized version if arg is either a continuous array or a JS primitive.
1833            if (arg instanceof NativeArray) {
1834                return (getContinuousArrayData(arg) != null);
1835            }
1836
1837            return JSType.isPrimitive(arg);
1838        }
1839    }
1840
1841    /**
1842     * This is linker logic for optimistic pushes
1843     */
1844    private static final class PushLinkLogic extends ArrayLinkLogic {
1845        private static final LinkLogic INSTANCE = new PushLinkLogic();
1846
1847        @Override
1848        public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) {
1849            return getContinuousArrayData(self) != null;
1850        }
1851    }
1852
1853    /**
1854     * This is linker logic for optimistic pops
1855     */
1856    private static final class PopLinkLogic extends ArrayLinkLogic {
1857        private static final LinkLogic INSTANCE = new PopLinkLogic();
1858
1859        /**
1860         * We need to check if we are dealing with a continuous non empty array data here,
1861         * as pop with a primitive return value returns undefined for arrays with length 0
1862         */
1863        @Override
1864        public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) {
1865            final ContinuousArrayData data = getContinuousNonEmptyArrayData(self);
1866            if (data != null) {
1867                final Class<?> elementType = data.getElementType();
1868                final Class<?> returnType  = desc.getMethodType().returnType();
1869                final boolean  typeFits    = JSType.getAccessorTypeIndex(returnType) >= JSType.getAccessorTypeIndex(elementType);
1870                return typeFits;
1871            }
1872            return false;
1873        }
1874
1875        private static ContinuousArrayData getContinuousNonEmptyArrayData(final Object self) {
1876            final ContinuousArrayData data = getContinuousArrayData(self);
1877            if (data != null) {
1878                return data.length() == 0 ? null : data;
1879            }
1880            return null;
1881        }
1882    }
1883
1884    //runtime calls for push and pops. they could be used as guards, but they also perform the runtime logic,
1885    //so rather than synthesizing them into a guard method handle that would also perform the push on the
1886    //retrieved receiver, we use this as runtime logic
1887
1888    //TODO - fold these into the Link logics, but I'll do that as a later step, as I want to do a checkin
1889    //where everything works first
1890
1891    private static <T> ContinuousArrayData getContinuousNonEmptyArrayDataCCE(final Object self, final Class<T> clazz) {
1892        try {
1893            @SuppressWarnings("unchecked")
1894            final ContinuousArrayData data = (ContinuousArrayData)(T)((NativeArray)self).getArray();
1895            if (data.length() != 0L) {
1896                return data; //if length is 0 we cannot pop and have to relink, because then we'd have to return an undefined, which is a wider type than e.g. int
1897           }
1898        } catch (final NullPointerException e) {
1899            //fallthru
1900        }
1901        throw new ClassCastException();
1902    }
1903
1904    private static ContinuousArrayData getContinuousArrayDataCCE(final Object self) {
1905        try {
1906            return (ContinuousArrayData)((NativeArray)self).getArray();
1907         } catch (final NullPointerException e) {
1908             throw new ClassCastException();
1909         }
1910    }
1911
1912    private static ContinuousArrayData getContinuousArrayDataCCE(final Object self, final Class<?> elementType) {
1913        try {
1914           return (ContinuousArrayData)((NativeArray)self).getArray(elementType); //ensure element type can fit "elementType"
1915        } catch (final NullPointerException e) {
1916            throw new ClassCastException();
1917        }
1918    }
1919}
1920