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