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