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