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