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