NativeArray.java revision 1104:d248a0bddf79
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(), true);
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
352        final long oldLen = NativeArray.validLength(oldLenDesc.getValue(), true);
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, true));
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, final boolean reject) {
499        final double doubleLength = JSType.toNumber(length);
500        if (!Double.isNaN(doubleLength) && JSType.isRepresentableAsLong(doubleLength)) {
501            final long len = (long) doubleLength;
502            if (len >= 0 && len <= JSType.MAX_UINT) {
503                return len;
504            }
505        }
506        if (reject) {
507            throw rangeError("inappropriate.array.length", ScriptRuntime.safeToString(length));
508        }
509        return -1;
510    }
511
512    /**
513     * ECMA 15.4.4.2 Array.prototype.toString ( )
514     *
515     * @param self self reference
516     * @return string representation of array
517     */
518    @Function(attributes = Attribute.NOT_ENUMERABLE)
519    public static Object toString(final Object self) {
520        final Object obj = Global.toObject(self);
521        if (obj instanceof ScriptObject) {
522            final InvokeByName joinInvoker = getJOIN();
523            final ScriptObject sobj = (ScriptObject)obj;
524            try {
525                final Object join = joinInvoker.getGetter().invokeExact(sobj);
526                if (Bootstrap.isCallable(join)) {
527                    return joinInvoker.getInvoker().invokeExact(join, sobj);
528                }
529            } catch (final RuntimeException | Error e) {
530                throw e;
531            } catch (final Throwable t) {
532                throw new RuntimeException(t);
533            }
534        }
535
536        // FIXME: should lookup Object.prototype.toString and call that?
537        return ScriptRuntime.builtinObjectToString(self);
538    }
539
540    /**
541     * Assert that an array is numeric, if not throw type error
542     * @param self self array to check
543     * @return true if numeric
544     */
545    @Function(attributes = Attribute.NOT_ENUMERABLE)
546    public static Object assertNumeric(final Object self) {
547        if(!(self instanceof NativeArray && ((NativeArray)self).getArray().getOptimisticType().isNumeric())) {
548            throw typeError("not.a.numeric.array", ScriptRuntime.safeToString(self));
549        }
550        return Boolean.TRUE;
551    }
552
553    /**
554     * ECMA 15.4.4.3 Array.prototype.toLocaleString ( )
555     *
556     * @param self self reference
557     * @return locale specific string representation for array
558     */
559    @Function(attributes = Attribute.NOT_ENUMERABLE)
560    public static String toLocaleString(final Object self) {
561        final StringBuilder sb = new StringBuilder();
562        final Iterator<Object> iter = arrayLikeIterator(self, true);
563
564        while (iter.hasNext()) {
565            final Object obj = iter.next();
566
567            if (obj != null && obj != ScriptRuntime.UNDEFINED) {
568                final Object val = JSType.toScriptObject(obj);
569
570                try {
571                    if (val instanceof ScriptObject) {
572                        final InvokeByName localeInvoker = getTO_LOCALE_STRING();
573                        final ScriptObject sobj           = (ScriptObject)val;
574                        final Object       toLocaleString = localeInvoker.getGetter().invokeExact(sobj);
575
576                        if (Bootstrap.isCallable(toLocaleString)) {
577                            sb.append((String)localeInvoker.getInvoker().invokeExact(toLocaleString, sobj));
578                        } else {
579                            throw typeError("not.a.function", "toLocaleString");
580                        }
581                    }
582                } catch (final Error|RuntimeException t) {
583                    throw t;
584                } catch (final Throwable t) {
585                    throw new RuntimeException(t);
586                }
587            }
588
589            if (iter.hasNext()) {
590                sb.append(",");
591            }
592        }
593
594        return sb.toString();
595    }
596
597    /**
598     * ECMA 15.4.2.2 new Array (len)
599     *
600     * @param newObj was the new operator used to instantiate this array
601     * @param self   self reference
602     * @param args   arguments (length)
603     * @return the new NativeArray
604     */
605    @Constructor(arity = 1)
606    public static NativeArray construct(final boolean newObj, final Object self, final Object... args) {
607        switch (args.length) {
608        case 0:
609            return new NativeArray(0);
610        case 1:
611            final Object len = args[0];
612            if (len instanceof Number) {
613                long length;
614                if (len instanceof Integer || len instanceof Long) {
615                    length = ((Number) len).longValue();
616                    if (length >= 0 && length < JSType.MAX_UINT) {
617                        return new NativeArray(length);
618                    }
619                }
620
621                length = JSType.toUint32(len);
622
623                /*
624                 * If the argument len is a Number and ToUint32(len) is equal to
625                 * len, then the length property of the newly constructed object
626                 * is set to ToUint32(len). If the argument len is a Number and
627                 * ToUint32(len) is not equal to len, a RangeError exception is
628                 * thrown.
629                 */
630                final double numberLength = ((Number) len).doubleValue();
631                if (length != numberLength) {
632                    throw rangeError("inappropriate.array.length", JSType.toString(numberLength));
633                }
634
635                return new NativeArray(length);
636            }
637            /*
638             * If the argument len is not a Number, then the length property of
639             * the newly constructed object is set to 1 and the 0 property of
640             * the newly constructed object is set to len
641             */
642            return new NativeArray(new Object[]{args[0]});
643            //fallthru
644        default:
645            return new NativeArray(args);
646        }
647    }
648
649    /**
650     * ECMA 15.4.2.2 new Array (len)
651     *
652     * Specialized constructor for zero arguments - empty array
653     *
654     * @param newObj was the new operator used to instantiate this array
655     * @param self   self reference
656     * @return the new NativeArray
657     */
658    @SpecializedFunction(isConstructor=true)
659    public static NativeArray construct(final boolean newObj, final Object self) {
660        return new NativeArray(0);
661    }
662
663    /**
664     * ECMA 15.4.2.2 new Array (len)
665     *
666     * Specialized constructor for zero arguments - empty array
667     *
668     * @param newObj  was the new operator used to instantiate this array
669     * @param self    self reference
670     * @param element first element
671     * @return the new NativeArray
672     */
673    @SpecializedFunction(isConstructor=true)
674    public static Object construct(final boolean newObj, final Object self, final boolean element) {
675        return new NativeArray(new Object[] { element });
676    }
677
678    /**
679     * ECMA 15.4.2.2 new Array (len)
680     *
681     * Specialized constructor for one integer argument (length)
682     *
683     * @param newObj was the new operator used to instantiate this array
684     * @param self   self reference
685     * @param length array length
686     * @return the new NativeArray
687     */
688    @SpecializedFunction(isConstructor=true)
689    public static NativeArray construct(final boolean newObj, final Object self, final int length) {
690        if (length >= 0) {
691            return new NativeArray(length);
692        }
693
694        return construct(newObj, self, new Object[]{length});
695    }
696
697    /**
698     * ECMA 15.4.2.2 new Array (len)
699     *
700     * Specialized constructor for one long argument (length)
701     *
702     * @param newObj was the new operator used to instantiate this array
703     * @param self   self reference
704     * @param length array length
705     * @return the new NativeArray
706     */
707    @SpecializedFunction(isConstructor=true)
708    public static NativeArray construct(final boolean newObj, final Object self, final long length) {
709        if (length >= 0L && length <= JSType.MAX_UINT) {
710            return new NativeArray(length);
711        }
712
713        return construct(newObj, self, new Object[]{length});
714    }
715
716    /**
717     * ECMA 15.4.2.2 new Array (len)
718     *
719     * Specialized constructor for one double argument (length)
720     *
721     * @param newObj was the new operator used to instantiate this array
722     * @param self   self reference
723     * @param length array length
724     * @return the new NativeArray
725     */
726    @SpecializedFunction(isConstructor=true)
727    public static NativeArray construct(final boolean newObj, final Object self, final double length) {
728        final long uint32length = JSType.toUint32(length);
729
730        if (uint32length == length) {
731            return new NativeArray(uint32length);
732        }
733
734        return construct(newObj, self, new Object[]{length});
735    }
736
737    /**
738     * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] )
739     *
740     * @param self self reference
741     * @param arg argument
742     * @return resulting NativeArray
743     */
744    @SpecializedFunction(linkLogic=ConcatLinkLogic.class)
745    public static NativeArray concat(final Object self, final int arg) {
746        final ContinuousArrayData newData = getContinuousArrayDataCCE(self, Integer.class).copy(); //get at least an integer data copy of this data
747        newData.fastPush(arg); //add an integer to its end
748        return new NativeArray(newData);
749    }
750
751    /**
752     * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] )
753     *
754     * @param self self reference
755     * @param arg argument
756     * @return resulting NativeArray
757     */
758    @SpecializedFunction(linkLogic=ConcatLinkLogic.class)
759    public static NativeArray concat(final Object self, final long arg) {
760        final ContinuousArrayData newData = getContinuousArrayDataCCE(self, Long.class).copy(); //get at least a long array data copy of this data
761        newData.fastPush(arg); //add a long at the end
762        return new NativeArray(newData);
763    }
764
765    /**
766     * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] )
767     *
768     * @param self self reference
769     * @param arg argument
770     * @return resulting NativeArray
771     */
772    @SpecializedFunction(linkLogic=ConcatLinkLogic.class)
773    public static NativeArray concat(final Object self, final double arg) {
774        final ContinuousArrayData newData = getContinuousArrayDataCCE(self, Double.class).copy(); //get at least a number array data copy of this data
775        newData.fastPush(arg); //add a double at the end
776        return new NativeArray(newData);
777    }
778
779    /**
780     * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] )
781     *
782     * @param self self reference
783     * @param arg argument
784     * @return resulting NativeArray
785     */
786    @SpecializedFunction(linkLogic=ConcatLinkLogic.class)
787    public static NativeArray concat(final Object self, final Object arg) {
788        //arg is [NativeArray] of same type.
789        final ContinuousArrayData selfData = getContinuousArrayDataCCE(self);
790        final ContinuousArrayData newData;
791
792        if (arg instanceof NativeArray) {
793            final ContinuousArrayData argData = (ContinuousArrayData)((NativeArray)arg).getArray();
794            if (argData.isEmpty()) {
795                newData = selfData.copy();
796            } else if (selfData.isEmpty()) {
797                newData = argData.copy();
798            } else {
799                final Class<?> widestElementType = selfData.widest(argData).getBoxedElementType();
800                newData = ((ContinuousArrayData)selfData.convert(widestElementType)).fastConcat((ContinuousArrayData)argData.convert(widestElementType));
801            }
802        } else {
803            newData = getContinuousArrayDataCCE(self, Object.class).copy();
804            newData.fastPush(arg);
805        }
806
807        return new NativeArray(newData);
808    }
809
810    /**
811     * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] )
812     *
813     * @param self self reference
814     * @param args arguments
815     * @return resulting NativeArray
816     */
817    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
818    public static NativeArray concat(final Object self, final Object... args) {
819        final ArrayList<Object> list = new ArrayList<>();
820
821        concatToList(list, Global.toObject(self));
822
823        for (final Object obj : args) {
824            concatToList(list, obj);
825        }
826
827        return new NativeArray(list.toArray());
828    }
829
830    private static void concatToList(final ArrayList<Object> list, final Object obj) {
831        final boolean isScriptArray  = isArray(obj);
832        final boolean isScriptObject = isScriptArray || obj instanceof ScriptObject;
833        if (isScriptArray || obj instanceof Iterable || (obj != null && obj.getClass().isArray())) {
834            final Iterator<Object> iter = arrayLikeIterator(obj, true);
835            if (iter.hasNext()) {
836                for (int i = 0; iter.hasNext(); ++i) {
837                    final Object value = iter.next();
838                    final boolean lacksIndex = obj != null && !((ScriptObject)obj).has(i);
839                    if (value == ScriptRuntime.UNDEFINED && isScriptObject && lacksIndex) {
840                        // TODO: eventually rewrite arrayLikeIterator to use a three-state enum for handling
841                        // UNDEFINED instead of an "includeUndefined" boolean with states SKIP, INCLUDE,
842                        // RETURN_EMPTY. Until then, this is how we'll make sure that empty elements don't make it
843                        // into the concatenated array.
844                        list.add(ScriptRuntime.EMPTY);
845                    } else {
846                        list.add(value);
847                    }
848                }
849            } else if (!isScriptArray) {
850                list.add(obj); // add empty object, but not an empty array
851            }
852        } else {
853            // single element, add it
854            list.add(obj);
855        }
856    }
857
858    /**
859     * ECMA 15.4.4.5 Array.prototype.join (separator)
860     *
861     * @param self      self reference
862     * @param separator element separator
863     * @return string representation after join
864     */
865    @Function(attributes = Attribute.NOT_ENUMERABLE)
866    public static String join(final Object self, final Object separator) {
867        final StringBuilder    sb   = new StringBuilder();
868        final Iterator<Object> iter = arrayLikeIterator(self, true);
869        final String           sep  = separator == ScriptRuntime.UNDEFINED ? "," : JSType.toString(separator);
870
871        while (iter.hasNext()) {
872            final Object obj = iter.next();
873
874            if (obj != null && obj != ScriptRuntime.UNDEFINED) {
875                sb.append(JSType.toString(obj));
876            }
877
878            if (iter.hasNext()) {
879                sb.append(sep);
880            }
881        }
882
883        return sb.toString();
884    }
885
886    /**
887     * Specialization of pop for ContinuousArrayData
888     *   The link guard checks that the array is continuous AND not empty.
889     *   The runtime guard checks that the guard is continuous (CCE otherwise)
890     *
891     * Primitive specialization, {@link LinkLogic}
892     *
893     * @param self self reference
894     * @return element popped
895     * @throws ClassCastException if array is empty, facilitating Undefined return value
896     */
897    @SpecializedFunction(name="pop", linkLogic=PopLinkLogic.class)
898    public static int popInt(final Object self) {
899        //must be non empty IntArrayData
900        return getContinuousNonEmptyArrayDataCCE(self, IntElements.class).fastPopInt();
901    }
902
903    /**
904     * Specialization of pop for ContinuousArrayData
905     *
906     * Primitive specialization, {@link LinkLogic}
907     *
908     * @param self self reference
909     * @return element popped
910     * @throws ClassCastException if array is empty, facilitating Undefined return value
911     */
912    @SpecializedFunction(name="pop", linkLogic=PopLinkLogic.class)
913    public static long popLong(final Object self) {
914        //must be non empty Int or LongArrayData
915        return getContinuousNonEmptyArrayDataCCE(self, IntOrLongElements.class).fastPopLong();
916    }
917
918    /**
919     * Specialization of pop for ContinuousArrayData
920     *
921     * Primitive specialization, {@link LinkLogic}
922     *
923     * @param self self reference
924     * @return element popped
925     * @throws ClassCastException if array is empty, facilitating Undefined return value
926     */
927    @SpecializedFunction(name="pop", linkLogic=PopLinkLogic.class)
928    public static double popDouble(final Object self) {
929        //must be non empty int long or double array data
930        return getContinuousNonEmptyArrayDataCCE(self, NumericElements.class).fastPopDouble();
931    }
932
933    /**
934     * Specialization of pop for ContinuousArrayData
935     *
936     * Primitive specialization, {@link LinkLogic}
937     *
938     * @param self self reference
939     * @return element popped
940     * @throws ClassCastException if array is empty, facilitating Undefined return value
941     */
942    @SpecializedFunction(name="pop", linkLogic=PopLinkLogic.class)
943    public static Object popObject(final Object self) {
944        //can be any data, because the numeric ones will throw cce and force relink
945        return getContinuousArrayDataCCE(self, null).fastPopObject();
946    }
947
948    /**
949     * ECMA 15.4.4.6 Array.prototype.pop ()
950     *
951     * @param self self reference
952     * @return array after pop
953     */
954    @Function(attributes = Attribute.NOT_ENUMERABLE)
955    public static Object pop(final Object self) {
956        try {
957            final ScriptObject sobj = (ScriptObject)self;
958
959            if (bulkable(sobj)) {
960                return sobj.getArray().pop();
961            }
962
963            final long len = JSType.toUint32(sobj.getLength());
964
965            if (len == 0) {
966                sobj.set("length", 0, CALLSITE_STRICT);
967                return ScriptRuntime.UNDEFINED;
968            }
969
970            final long   index   = len - 1;
971            final Object element = sobj.get(index);
972
973            sobj.delete(index, true);
974            sobj.set("length", index, CALLSITE_STRICT);
975
976            return element;
977        } catch (final ClassCastException | NullPointerException e) {
978            throw typeError("not.an.object", ScriptRuntime.safeToString(self));
979        }
980    }
981
982    /**
983     * ECMA 15.4.4.7 Array.prototype.push (args...)
984     *
985     * Primitive specialization, {@link LinkLogic}
986     *
987     * @param self self reference
988     * @param arg a primitive to push
989     * @return array length after push
990     */
991    @SpecializedFunction(linkLogic=PushLinkLogic.class)
992    public static long push(final Object self, final int arg) {
993        return getContinuousArrayDataCCE(self, Integer.class).fastPush(arg);
994    }
995
996    /**
997     * ECMA 15.4.4.7 Array.prototype.push (args...)
998     *
999     * Primitive specialization, {@link LinkLogic}
1000     *
1001     * @param self self reference
1002     * @param arg a primitive to push
1003     * @return array length after push
1004     */
1005    @SpecializedFunction(linkLogic=PushLinkLogic.class)
1006    public static long push(final Object self, final long arg) {
1007        return getContinuousArrayDataCCE(self, Long.class).fastPush(arg);
1008    }
1009
1010    /**
1011     * ECMA 15.4.4.7 Array.prototype.push (args...)
1012     *
1013     * Primitive specialization, {@link LinkLogic}
1014     *
1015     * @param self self reference
1016     * @param arg a primitive to push
1017     * @return array length after push
1018     */
1019    @SpecializedFunction(linkLogic=PushLinkLogic.class)
1020    public static long push(final Object self, final double arg) {
1021        return getContinuousArrayDataCCE(self, Double.class).fastPush(arg);
1022    }
1023
1024    /**
1025     * ECMA 15.4.4.7 Array.prototype.push (args...)
1026     *
1027     * Primitive specialization, {@link LinkLogic}
1028     *
1029     * @param self self reference
1030     * @param arg a primitive to push
1031     * @return array length after push
1032     */
1033    @SpecializedFunction(name="push", linkLogic=PushLinkLogic.class)
1034    public static long pushObject(final Object self, final Object arg) {
1035        return getContinuousArrayDataCCE(self, Object.class).fastPush(arg);
1036    }
1037
1038    /**
1039     * ECMA 15.4.4.7 Array.prototype.push (args...)
1040     *
1041     * @param self self reference
1042     * @param args arguments to push
1043     * @return array length after pushes
1044     */
1045    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
1046    public static Object push(final Object self, final Object... args) {
1047        try {
1048            final ScriptObject sobj   = (ScriptObject)self;
1049
1050            if (bulkable(sobj) && sobj.getArray().length() + args.length <= JSType.MAX_UINT) {
1051                final ArrayData newData = sobj.getArray().push(true, args);
1052                sobj.setArray(newData);
1053                return newData.length();
1054            }
1055
1056            long len = JSType.toUint32(sobj.getLength());
1057            for (final Object element : args) {
1058                sobj.set(len++, element, CALLSITE_STRICT);
1059            }
1060            sobj.set("length", len, CALLSITE_STRICT);
1061
1062            return len;
1063        } catch (final ClassCastException | NullPointerException e) {
1064            throw typeError(Context.getGlobal(), e, "not.an.object", ScriptRuntime.safeToString(self));
1065        }
1066    }
1067
1068    /**
1069     * ECMA 15.4.4.7 Array.prototype.push (args...) specialized for single object argument
1070     *
1071     * @param self self reference
1072     * @param arg argument to push
1073     * @return array after pushes
1074     */
1075    @SpecializedFunction
1076    public static long push(final Object self, final Object arg) {
1077        try {
1078            final ScriptObject sobj = (ScriptObject)self;
1079            final ArrayData arrayData = sobj.getArray();
1080            final long length = arrayData.length();
1081            if (bulkable(sobj) && length < JSType.MAX_UINT) {
1082                sobj.setArray(arrayData.push(true, arg));
1083                return length + 1;
1084            }
1085
1086            long len = JSType.toUint32(sobj.getLength());
1087            sobj.set(len++, arg, CALLSITE_STRICT);
1088            sobj.set("length", len, CALLSITE_STRICT);
1089            return len;
1090        } catch (final ClassCastException | NullPointerException e) {
1091            throw typeError("not.an.object", ScriptRuntime.safeToString(self));
1092        }
1093    }
1094
1095    /**
1096     * ECMA 15.4.4.8 Array.prototype.reverse ()
1097     *
1098     * @param self self reference
1099     * @return reversed array
1100     */
1101    @Function(attributes = Attribute.NOT_ENUMERABLE)
1102    public static Object reverse(final Object self) {
1103        try {
1104            final ScriptObject sobj   = (ScriptObject)self;
1105            final long         len    = JSType.toUint32(sobj.getLength());
1106            final long         middle = len / 2;
1107
1108            for (long lower = 0; lower != middle; lower++) {
1109                final long    upper       = len - lower - 1;
1110                final Object  lowerValue  = sobj.get(lower);
1111                final Object  upperValue  = sobj.get(upper);
1112                final boolean lowerExists = sobj.has(lower);
1113                final boolean upperExists = sobj.has(upper);
1114
1115                if (lowerExists && upperExists) {
1116                    sobj.set(lower, upperValue, CALLSITE_STRICT);
1117                    sobj.set(upper, lowerValue, CALLSITE_STRICT);
1118                } else if (!lowerExists && upperExists) {
1119                    sobj.set(lower, upperValue, CALLSITE_STRICT);
1120                    sobj.delete(upper, true);
1121                } else if (lowerExists && !upperExists) {
1122                    sobj.delete(lower, true);
1123                    sobj.set(upper, lowerValue, CALLSITE_STRICT);
1124                }
1125            }
1126            return sobj;
1127        } catch (final ClassCastException | NullPointerException e) {
1128            throw typeError("not.an.object", ScriptRuntime.safeToString(self));
1129        }
1130    }
1131
1132    /**
1133     * ECMA 15.4.4.9 Array.prototype.shift ()
1134     *
1135     * @param self self reference
1136     * @return shifted array
1137     */
1138    @Function(attributes = Attribute.NOT_ENUMERABLE)
1139    public static Object shift(final Object self) {
1140        final Object obj = Global.toObject(self);
1141
1142        Object first = ScriptRuntime.UNDEFINED;
1143
1144        if (!(obj instanceof ScriptObject)) {
1145            return first;
1146        }
1147
1148        final ScriptObject sobj   = (ScriptObject) obj;
1149
1150        long len = JSType.toUint32(sobj.getLength());
1151
1152        if (len > 0) {
1153            first = sobj.get(0);
1154
1155            if (bulkable(sobj)) {
1156                sobj.getArray().shiftLeft(1);
1157            } else {
1158                boolean hasPrevious = true;
1159                for (long k = 1; k < len; k++) {
1160                    final boolean hasCurrent = sobj.has(k);
1161                    if (hasCurrent) {
1162                        sobj.set(k - 1, sobj.get(k), CALLSITE_STRICT);
1163                    } else if (hasPrevious) {
1164                        sobj.delete(k - 1, true);
1165                    }
1166                    hasPrevious = hasCurrent;
1167                }
1168            }
1169            sobj.delete(--len, true);
1170        } else {
1171            len = 0;
1172        }
1173
1174        sobj.set("length", len, CALLSITE_STRICT);
1175
1176        return first;
1177    }
1178
1179    /**
1180     * ECMA 15.4.4.10 Array.prototype.slice ( start [ , end ] )
1181     *
1182     * @param self  self reference
1183     * @param start start of slice (inclusive)
1184     * @param end   end of slice (optional, exclusive)
1185     * @return sliced array
1186     */
1187    @Function(attributes = Attribute.NOT_ENUMERABLE)
1188    public static Object slice(final Object self, final Object start, final Object end) {
1189        final Object       obj                 = Global.toObject(self);
1190        if (!(obj instanceof ScriptObject)) {
1191            return ScriptRuntime.UNDEFINED;
1192        }
1193
1194        final ScriptObject sobj                = (ScriptObject)obj;
1195        final long         len                 = JSType.toUint32(sobj.getLength());
1196        final long         relativeStart       = JSType.toLong(start);
1197        final long         relativeEnd         = end == ScriptRuntime.UNDEFINED ? len : JSType.toLong(end);
1198
1199        long k = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len);
1200        final long finale = relativeEnd < 0 ? Math.max(len + relativeEnd, 0) : Math.min(relativeEnd, len);
1201
1202        if (k >= finale) {
1203            return new NativeArray(0);
1204        }
1205
1206        if (bulkable(sobj)) {
1207            return new NativeArray(sobj.getArray().slice(k, finale));
1208        }
1209
1210        // Construct array with proper length to have a deleted filter on undefined elements
1211        final NativeArray copy = new NativeArray(finale - k);
1212        for (long n = 0; k < finale; n++, k++) {
1213            if (sobj.has(k)) {
1214                copy.defineOwnProperty(ArrayIndex.getArrayIndex(n), sobj.get(k));
1215            }
1216        }
1217
1218        return copy;
1219    }
1220
1221    private static ScriptFunction compareFunction(final Object comparefn) {
1222        if (comparefn == ScriptRuntime.UNDEFINED) {
1223            return null;
1224        }
1225
1226        if (! (comparefn instanceof ScriptFunction)) {
1227            throw typeError("not.a.function", ScriptRuntime.safeToString(comparefn));
1228        }
1229
1230        return (ScriptFunction)comparefn;
1231    }
1232
1233    private static Object[] sort(final Object[] array, final Object comparefn) {
1234        final ScriptFunction cmp = compareFunction(comparefn);
1235
1236        final List<Object> list = Arrays.asList(array);
1237        final Object cmpThis = cmp == null || cmp.isStrict() ? ScriptRuntime.UNDEFINED : Global.instance();
1238
1239        Collections.sort(list, new Comparator<Object>() {
1240            private final MethodHandle call_cmp = getCALL_CMP();
1241            @Override
1242            public int compare(final Object x, final Object y) {
1243                if (x == ScriptRuntime.UNDEFINED && y == ScriptRuntime.UNDEFINED) {
1244                    return 0;
1245                } else if (x == ScriptRuntime.UNDEFINED) {
1246                    return 1;
1247                } else if (y == ScriptRuntime.UNDEFINED) {
1248                    return -1;
1249                }
1250
1251                if (cmp != null) {
1252                    try {
1253                        return (int)Math.signum((double)call_cmp.invokeExact(cmp, cmpThis, x, y));
1254                    } catch (final RuntimeException | Error e) {
1255                        throw e;
1256                    } catch (final Throwable t) {
1257                        throw new RuntimeException(t);
1258                    }
1259                }
1260
1261                return JSType.toString(x).compareTo(JSType.toString(y));
1262            }
1263        });
1264
1265        return list.toArray(new Object[array.length]);
1266    }
1267
1268    /**
1269     * ECMA 15.4.4.11 Array.prototype.sort ( comparefn )
1270     *
1271     * @param self       self reference
1272     * @param comparefn  element comparison function
1273     * @return sorted array
1274     */
1275    @Function(attributes = Attribute.NOT_ENUMERABLE)
1276    public static ScriptObject sort(final Object self, final Object comparefn) {
1277        try {
1278            final ScriptObject sobj    = (ScriptObject) self;
1279            final long         len     = JSType.toUint32(sobj.getLength());
1280            ArrayData          array   = sobj.getArray();
1281
1282            if (len > 1) {
1283                // Get only non-missing elements. Missing elements go at the end
1284                // of the sorted array. So, just don't copy these to sort input.
1285                final ArrayList<Object> src = new ArrayList<>();
1286
1287                for (final Iterator<Long> iter = array.indexIterator(); iter.hasNext(); ) {
1288                    final long index = iter.next();
1289                    if (index >= len) {
1290                        break;
1291                    }
1292                    src.add(array.getObject((int)index));
1293                }
1294
1295                final Object[] sorted = sort(src.toArray(), comparefn);
1296
1297                for (int i = 0; i < sorted.length; i++) {
1298                    array = array.set(i, sorted[i], true);
1299                }
1300
1301                // delete missing elements - which are at the end of sorted array
1302                if (sorted.length != len) {
1303                    array = array.delete(sorted.length, len - 1);
1304                }
1305
1306                sobj.setArray(array);
1307            }
1308
1309            return sobj;
1310        } catch (final ClassCastException | NullPointerException e) {
1311            throw typeError("not.an.object", ScriptRuntime.safeToString(self));
1312        }
1313    }
1314
1315    /**
1316     * ECMA 15.4.4.12 Array.prototype.splice ( start, deleteCount [ item1 [ , item2 [ , ... ] ] ] )
1317     *
1318     * @param self self reference
1319     * @param args arguments
1320     * @return result of splice
1321     */
1322    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
1323    public static Object splice(final Object self, final Object... args) {
1324        final Object obj = Global.toObject(self);
1325
1326        if (!(obj instanceof ScriptObject)) {
1327            return ScriptRuntime.UNDEFINED;
1328        }
1329
1330        final Object start = args.length > 0 ? args[0] : ScriptRuntime.UNDEFINED;
1331        final Object deleteCount = args.length > 1 ? args[1] : ScriptRuntime.UNDEFINED;
1332
1333        Object[] items;
1334
1335        if (args.length > 2) {
1336            items = new Object[args.length - 2];
1337            System.arraycopy(args, 2, items, 0, items.length);
1338        } else {
1339            items = ScriptRuntime.EMPTY_ARRAY;
1340        }
1341
1342        final ScriptObject sobj                = (ScriptObject)obj;
1343        final long         len                 = JSType.toUint32(sobj.getLength());
1344        final long         relativeStart       = JSType.toLong(start);
1345
1346        final long actualStart = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len);
1347        final long actualDeleteCount = Math.min(Math.max(JSType.toLong(deleteCount), 0), len - actualStart);
1348
1349        NativeArray returnValue;
1350
1351        if (actualStart <= Integer.MAX_VALUE && actualDeleteCount <= Integer.MAX_VALUE && bulkable(sobj)) {
1352            try {
1353                returnValue =  new NativeArray(sobj.getArray().fastSplice((int)actualStart, (int)actualDeleteCount, items.length));
1354
1355                // Since this is a dense bulkable array we can use faster defineOwnProperty to copy new elements
1356                int k = (int) actualStart;
1357                for (int i = 0; i < items.length; i++, k++) {
1358                    sobj.defineOwnProperty(k, items[i]);
1359                }
1360            } catch (final UnsupportedOperationException uoe) {
1361                returnValue = slowSplice(sobj, actualStart, actualDeleteCount, items, len);
1362            }
1363        } else {
1364            returnValue = slowSplice(sobj, actualStart, actualDeleteCount, items, len);
1365        }
1366
1367        return returnValue;
1368    }
1369
1370    private static NativeArray slowSplice(final ScriptObject sobj, final long start, final long deleteCount, final Object[] items, final long len) {
1371
1372        final NativeArray array = new NativeArray(deleteCount);
1373
1374        for (long k = 0; k < deleteCount; k++) {
1375            final long from = start + k;
1376
1377            if (sobj.has(from)) {
1378                array.defineOwnProperty(ArrayIndex.getArrayIndex(k), sobj.get(from));
1379            }
1380        }
1381
1382        if (items.length < deleteCount) {
1383            for (long k = start; k < len - deleteCount; k++) {
1384                final long from = k + deleteCount;
1385                final long to   = k + items.length;
1386
1387                if (sobj.has(from)) {
1388                    sobj.set(to, sobj.get(from), CALLSITE_STRICT);
1389                } else {
1390                    sobj.delete(to, true);
1391                }
1392            }
1393
1394            for (long k = len; k > len - deleteCount + items.length; k--) {
1395                sobj.delete(k - 1, true);
1396            }
1397        } else if (items.length > deleteCount) {
1398            for (long k = len - deleteCount; k > start; k--) {
1399                final long from = k + deleteCount - 1;
1400                final long to   = k + items.length - 1;
1401
1402                if (sobj.has(from)) {
1403                    final Object fromValue = sobj.get(from);
1404                    sobj.set(to, fromValue, CALLSITE_STRICT);
1405                } else {
1406                    sobj.delete(to, true);
1407                }
1408            }
1409        }
1410
1411        long k = start;
1412        for (int i = 0; i < items.length; i++, k++) {
1413            sobj.set(k, items[i], CALLSITE_STRICT);
1414        }
1415
1416        final long newLength = len - deleteCount + items.length;
1417        sobj.set("length", newLength, CALLSITE_STRICT);
1418
1419        return array;
1420    }
1421
1422    /**
1423     * ECMA 15.4.4.13 Array.prototype.unshift ( [ item1 [ , item2 [ , ... ] ] ] )
1424     *
1425     * @param self  self reference
1426     * @param items items for unshift
1427     * @return unshifted array
1428     */
1429    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
1430    public static Object unshift(final Object self, final Object... items) {
1431        final Object obj = Global.toObject(self);
1432
1433        if (!(obj instanceof ScriptObject)) {
1434            return ScriptRuntime.UNDEFINED;
1435        }
1436
1437        final ScriptObject sobj   = (ScriptObject)obj;
1438        final long         len    = JSType.toUint32(sobj.getLength());
1439
1440        if (items == null) {
1441            return ScriptRuntime.UNDEFINED;
1442        }
1443
1444        if (bulkable(sobj)) {
1445            sobj.getArray().shiftRight(items.length);
1446
1447            for (int j = 0; j < items.length; j++) {
1448                sobj.setArray(sobj.getArray().set(j, items[j], true));
1449            }
1450        } else {
1451            for (long k = len; k > 0; k--) {
1452                final long from = k - 1;
1453                final long to = k + items.length - 1;
1454
1455                if (sobj.has(from)) {
1456                    final Object fromValue = sobj.get(from);
1457                    sobj.set(to, fromValue, CALLSITE_STRICT);
1458                } else {
1459                    sobj.delete(to, true);
1460                }
1461            }
1462
1463            for (int j = 0; j < items.length; j++) {
1464                sobj.set(j, items[j], CALLSITE_STRICT);
1465            }
1466        }
1467
1468        final long newLength = len + items.length;
1469        sobj.set("length", newLength, CALLSITE_STRICT);
1470
1471        return newLength;
1472    }
1473
1474    /**
1475     * ECMA 15.4.4.14 Array.prototype.indexOf ( searchElement [ , fromIndex ] )
1476     *
1477     * @param self           self reference
1478     * @param searchElement  element to search for
1479     * @param fromIndex      start index of search
1480     * @return index of element, or -1 if not found
1481     */
1482    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
1483    public static long indexOf(final Object self, final Object searchElement, final Object fromIndex) {
1484        try {
1485            final ScriptObject sobj = (ScriptObject)Global.toObject(self);
1486            final long         len  = JSType.toUint32(sobj.getLength());
1487            if (len == 0) {
1488                return -1;
1489            }
1490
1491            final long         n = JSType.toLong(fromIndex);
1492            if (n >= len) {
1493                return -1;
1494            }
1495
1496
1497            for (long k = Math.max(0, n < 0 ? len - Math.abs(n) : n); k < len; k++) {
1498                if (sobj.has(k)) {
1499                    if (ScriptRuntime.EQ_STRICT(sobj.get(k), searchElement)) {
1500                        return k;
1501                    }
1502                }
1503            }
1504        } catch (final ClassCastException | NullPointerException e) {
1505            //fallthru
1506        }
1507
1508        return -1;
1509    }
1510
1511    /**
1512     * ECMA 15.4.4.15 Array.prototype.lastIndexOf ( searchElement [ , fromIndex ] )
1513     *
1514     * @param self self reference
1515     * @param args arguments: element to search for and optional from index
1516     * @return index of element, or -1 if not found
1517     */
1518    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
1519    public static long lastIndexOf(final Object self, final Object... args) {
1520        try {
1521            final ScriptObject sobj = (ScriptObject)Global.toObject(self);
1522            final long         len  = JSType.toUint32(sobj.getLength());
1523
1524            if (len == 0) {
1525                return -1;
1526            }
1527
1528            final Object searchElement = args.length > 0 ? args[0] : ScriptRuntime.UNDEFINED;
1529            final long   n             = args.length > 1 ? JSType.toLong(args[1]) : len - 1;
1530
1531            for (long k = n < 0 ? len - Math.abs(n) : Math.min(n, len - 1); k >= 0; k--) {
1532                if (sobj.has(k)) {
1533                    if (ScriptRuntime.EQ_STRICT(sobj.get(k), searchElement)) {
1534                        return k;
1535                    }
1536                }
1537            }
1538        } catch (final ClassCastException | NullPointerException e) {
1539            throw typeError("not.an.object", ScriptRuntime.safeToString(self));
1540        }
1541
1542        return -1;
1543    }
1544
1545    /**
1546     * ECMA 15.4.4.16 Array.prototype.every ( callbackfn [ , thisArg ] )
1547     *
1548     * @param self        self reference
1549     * @param callbackfn  callback function per element
1550     * @param thisArg     this argument
1551     * @return true if callback function return true for every element in the array, false otherwise
1552     */
1553    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
1554    public static boolean every(final Object self, final Object callbackfn, final Object thisArg) {
1555        return applyEvery(Global.toObject(self), callbackfn, thisArg);
1556    }
1557
1558    private static boolean applyEvery(final Object self, final Object callbackfn, final Object thisArg) {
1559        return new IteratorAction<Boolean>(Global.toObject(self), callbackfn, thisArg, true) {
1560            private final MethodHandle everyInvoker = getEVERY_CALLBACK_INVOKER();
1561
1562            @Override
1563            protected boolean forEach(final Object val, final long i) throws Throwable {
1564                return result = (boolean)everyInvoker.invokeExact(callbackfn, thisArg, val, i, self);
1565            }
1566        }.apply();
1567    }
1568
1569    /**
1570     * ECMA 15.4.4.17 Array.prototype.some ( callbackfn [ , thisArg ] )
1571     *
1572     * @param self        self reference
1573     * @param callbackfn  callback function per element
1574     * @param thisArg     this argument
1575     * @return true if callback function returned true for any element in the array, false otherwise
1576     */
1577    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
1578    public static boolean some(final Object self, final Object callbackfn, final Object thisArg) {
1579        return new IteratorAction<Boolean>(Global.toObject(self), callbackfn, thisArg, false) {
1580            private final MethodHandle someInvoker = getSOME_CALLBACK_INVOKER();
1581
1582            @Override
1583            protected boolean forEach(final Object val, final long i) throws Throwable {
1584                return !(result = (boolean)someInvoker.invokeExact(callbackfn, thisArg, val, i, self));
1585            }
1586        }.apply();
1587    }
1588
1589    /**
1590     * ECMA 15.4.4.18 Array.prototype.forEach ( callbackfn [ , thisArg ] )
1591     *
1592     * @param self        self reference
1593     * @param callbackfn  callback function per element
1594     * @param thisArg     this argument
1595     * @return undefined
1596     */
1597    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
1598    public static Object forEach(final Object self, final Object callbackfn, final Object thisArg) {
1599        return new IteratorAction<Object>(Global.toObject(self), callbackfn, thisArg, ScriptRuntime.UNDEFINED) {
1600            private final MethodHandle forEachInvoker = getFOREACH_CALLBACK_INVOKER();
1601
1602            @Override
1603            protected boolean forEach(final Object val, final long i) throws Throwable {
1604                forEachInvoker.invokeExact(callbackfn, thisArg, val, i, self);
1605                return true;
1606            }
1607        }.apply();
1608    }
1609
1610    /**
1611     * ECMA 15.4.4.19 Array.prototype.map ( callbackfn [ , thisArg ] )
1612     *
1613     * @param self        self reference
1614     * @param callbackfn  callback function per element
1615     * @param thisArg     this argument
1616     * @return array with elements transformed by map function
1617     */
1618    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
1619    public static NativeArray map(final Object self, final Object callbackfn, final Object thisArg) {
1620        return new IteratorAction<NativeArray>(Global.toObject(self), callbackfn, thisArg, null) {
1621            private final MethodHandle mapInvoker = getMAP_CALLBACK_INVOKER();
1622
1623            @Override
1624            protected boolean forEach(final Object val, final long i) throws Throwable {
1625                final Object r = mapInvoker.invokeExact(callbackfn, thisArg, val, i, self);
1626                result.defineOwnProperty(ArrayIndex.getArrayIndex(index), r);
1627                return true;
1628            }
1629
1630            @Override
1631            public void applyLoopBegin(final ArrayLikeIterator<Object> iter0) {
1632                // map return array should be of same length as source array
1633                // even if callback reduces source array length
1634                result = new NativeArray(iter0.getLength());
1635            }
1636        }.apply();
1637    }
1638
1639    /**
1640     * ECMA 15.4.4.20 Array.prototype.filter ( callbackfn [ , thisArg ] )
1641     *
1642     * @param self        self reference
1643     * @param callbackfn  callback function per element
1644     * @param thisArg     this argument
1645     * @return filtered array
1646     */
1647    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
1648    public static NativeArray filter(final Object self, final Object callbackfn, final Object thisArg) {
1649        return new IteratorAction<NativeArray>(Global.toObject(self), callbackfn, thisArg, new NativeArray()) {
1650            private long to = 0;
1651            private final MethodHandle filterInvoker = getFILTER_CALLBACK_INVOKER();
1652
1653            @Override
1654            protected boolean forEach(final Object val, final long i) throws Throwable {
1655                if ((boolean)filterInvoker.invokeExact(callbackfn, thisArg, val, i, self)) {
1656                    result.defineOwnProperty(ArrayIndex.getArrayIndex(to++), val);
1657                }
1658                return true;
1659            }
1660        }.apply();
1661    }
1662
1663    private static Object reduceInner(final ArrayLikeIterator<Object> iter, final Object self, final Object... args) {
1664        final Object  callbackfn          = args.length > 0 ? args[0] : ScriptRuntime.UNDEFINED;
1665        final boolean initialValuePresent = args.length > 1;
1666
1667        Object initialValue = initialValuePresent ? args[1] : ScriptRuntime.UNDEFINED;
1668
1669        if (callbackfn == ScriptRuntime.UNDEFINED) {
1670            throw typeError("not.a.function", "undefined");
1671        }
1672
1673        if (!initialValuePresent) {
1674            if (iter.hasNext()) {
1675                initialValue = iter.next();
1676            } else {
1677                throw typeError("array.reduce.invalid.init");
1678            }
1679        }
1680
1681        //if initial value is ScriptRuntime.UNDEFINED - step forward once.
1682        return new IteratorAction<Object>(Global.toObject(self), callbackfn, ScriptRuntime.UNDEFINED, initialValue, iter) {
1683            private final MethodHandle reduceInvoker = getREDUCE_CALLBACK_INVOKER();
1684
1685            @Override
1686            protected boolean forEach(final Object val, final long i) throws Throwable {
1687                // TODO: why can't I declare the second arg as Undefined.class?
1688                result = reduceInvoker.invokeExact(callbackfn, ScriptRuntime.UNDEFINED, result, val, i, self);
1689                return true;
1690            }
1691        }.apply();
1692    }
1693
1694    /**
1695     * ECMA 15.4.4.21 Array.prototype.reduce ( callbackfn [ , initialValue ] )
1696     *
1697     * @param self self reference
1698     * @param args arguments to reduce
1699     * @return accumulated result
1700     */
1701    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
1702    public static Object reduce(final Object self, final Object... args) {
1703        return reduceInner(arrayLikeIterator(self), self, args);
1704    }
1705
1706    /**
1707     * ECMA 15.4.4.22 Array.prototype.reduceRight ( callbackfn [ , initialValue ] )
1708     *
1709     * @param self        self reference
1710     * @param args arguments to reduce
1711     * @return accumulated result
1712     */
1713    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
1714    public static Object reduceRight(final Object self, final Object... args) {
1715        return reduceInner(reverseArrayLikeIterator(self), self, args);
1716    }
1717
1718    /**
1719     * Determine if Java bulk array operations may be used on the underlying
1720     * storage. This is possible only if the object's prototype chain is empty
1721     * or each of the prototypes in the chain is empty.
1722     *
1723     * @param self the object to examine
1724     * @return true if optimizable
1725     */
1726    private static boolean bulkable(final ScriptObject self) {
1727        return self.isArray() && !hasInheritedArrayEntries(self) && !self.isLengthNotWritable();
1728    }
1729
1730    private static boolean hasInheritedArrayEntries(final ScriptObject self) {
1731        ScriptObject proto = self.getProto();
1732        while (proto != null) {
1733            if (proto.hasArrayEntries()) {
1734                return true;
1735            }
1736            proto = proto.getProto();
1737        }
1738
1739        return false;
1740    }
1741
1742    @Override
1743    public String toString() {
1744        return "NativeArray@" + Debug.id(this) + " [" + getArray().getClass().getSimpleName() + ']';
1745    }
1746
1747    @Override
1748    public SpecializedFunction.LinkLogic getLinkLogic(final Class<? extends LinkLogic> clazz) {
1749        if (clazz == PushLinkLogic.class) {
1750            return PushLinkLogic.INSTANCE;
1751        } else if (clazz == PopLinkLogic.class) {
1752            return PopLinkLogic.INSTANCE;
1753        } else if (clazz == ConcatLinkLogic.class) {
1754            return ConcatLinkLogic.INSTANCE;
1755        }
1756        return null;
1757    }
1758
1759    @Override
1760    public boolean hasPerInstanceAssumptions() {
1761        return true; //length writable switchpoint
1762    }
1763
1764    /**
1765     * This is an abstract super class that contains common functionality for all
1766     * specialized optimistic builtins in NativeArray. For example, it handles the
1767     * modification switchpoint which is touched when length is written.
1768     */
1769    private static abstract class ArrayLinkLogic extends SpecializedFunction.LinkLogic {
1770        protected ArrayLinkLogic() {
1771        }
1772
1773        protected static ContinuousArrayData getContinuousArrayData(final Object self) {
1774            try {
1775                //cast to NativeArray, to avoid cases like x = {0:0, 1:1}, x.length = 2, where we can't use the array push/pop
1776                return (ContinuousArrayData)((NativeArray)self).getArray();
1777            } catch (final Exception e) {
1778                return null;
1779            }
1780        }
1781
1782        /**
1783         * Push and pop callsites can throw ClassCastException as a mechanism to have them
1784         * relinked - this enabled fast checks of the kind of ((IntArrayData)arrayData).push(x)
1785         * for an IntArrayData only push - if this fails, a CCE will be thrown and we will relink
1786         */
1787        @Override
1788        public Class<? extends Throwable> getRelinkException() {
1789            return ClassCastException.class;
1790        }
1791    }
1792
1793    /**
1794     * This is linker logic for optimistic concatenations
1795     */
1796    private static final class ConcatLinkLogic extends ArrayLinkLogic {
1797        private static final LinkLogic INSTANCE = new ConcatLinkLogic();
1798
1799        @Override
1800        public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) {
1801            final Object[] args = request.getArguments();
1802
1803            if (args.length != 3) { //single argument check
1804                return false;
1805            }
1806
1807            final ContinuousArrayData selfData = getContinuousArrayData(self);
1808            if (selfData == null) {
1809                return false;
1810            }
1811
1812            final Object arg = args[2];
1813            //args[2] continuousarray or non arraydata, let past non array datas
1814            if (arg instanceof NativeArray) {
1815                final ContinuousArrayData argData = getContinuousArrayData(arg);
1816                if (argData == null) {
1817                    return false;
1818                }
1819            }
1820
1821            return true;
1822        }
1823    }
1824
1825    /**
1826     * This is linker logic for optimistic pushes
1827     */
1828    private static final class PushLinkLogic extends ArrayLinkLogic {
1829        private static final LinkLogic INSTANCE = new PushLinkLogic();
1830
1831        @Override
1832        public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) {
1833            return getContinuousArrayData(self) != null;
1834        }
1835    }
1836
1837    /**
1838     * This is linker logic for optimistic pops
1839     */
1840    private static final class PopLinkLogic extends ArrayLinkLogic {
1841        private static final LinkLogic INSTANCE = new PopLinkLogic();
1842
1843        /**
1844         * We need to check if we are dealing with a continuous non empty array data here,
1845         * as pop with a primitive return value returns undefined for arrays with length 0
1846         */
1847        @Override
1848        public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) {
1849            final ContinuousArrayData data = getContinuousNonEmptyArrayData(self);
1850            if (data != null) {
1851                final Class<?> elementType = data.getElementType();
1852                final Class<?> returnType  = desc.getMethodType().returnType();
1853                final boolean  typeFits    = JSType.getAccessorTypeIndex(returnType) >= JSType.getAccessorTypeIndex(elementType);
1854                return typeFits;
1855            }
1856            return false;
1857        }
1858
1859        private static ContinuousArrayData getContinuousNonEmptyArrayData(final Object self) {
1860            final ContinuousArrayData data = getContinuousArrayData(self);
1861            if (data != null) {
1862                return data.length() == 0 ? null : data;
1863            }
1864            return null;
1865        }
1866    }
1867
1868    //runtime calls for push and pops. they could be used as guards, but they also perform the runtime logic,
1869    //so rather than synthesizing them into a guard method handle that would also perform the push on the
1870    //retrieved receiver, we use this as runtime logic
1871
1872    //TODO - fold these into the Link logics, but I'll do that as a later step, as I want to do a checkin
1873    //where everything works first
1874
1875    private static final <T> ContinuousArrayData getContinuousNonEmptyArrayDataCCE(final Object self, final Class<T> clazz) {
1876        try {
1877            @SuppressWarnings("unchecked")
1878            final ContinuousArrayData data = (ContinuousArrayData)(T)((NativeArray)self).getArray();
1879            if (data.length() != 0L) {
1880                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
1881           }
1882        } catch (final NullPointerException e) {
1883            //fallthru
1884        }
1885        throw new ClassCastException();
1886    }
1887
1888    private static final ContinuousArrayData getContinuousArrayDataCCE(final Object self) {
1889        try {
1890            return (ContinuousArrayData)((NativeArray)self).getArray();
1891         } catch (final NullPointerException e) {
1892             throw new ClassCastException();
1893         }
1894    }
1895
1896    private static final ContinuousArrayData getContinuousArrayDataCCE(final Object self, final Class<?> elementType) {
1897        try {
1898           return (ContinuousArrayData)((NativeArray)self).getArray(elementType); //ensure element type can fit "elementType"
1899        } catch (final NullPointerException e) {
1900            throw new ClassCastException();
1901        }
1902    }
1903}
1904