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