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());
 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. Always a Long/Uint32 but we take the safe road.
 352         final long oldLen = JSType.toUint32(oldLenDesc.getValue());
 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));
 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) {
 499         // ES5 15.4.5.1, steps 3.c and 3.d require two ToNumber conversions here
 500         final double doubleLength = JSType.toNumber(length);
 501         if (doubleLength != JSType.toUint32(length)) {
 502             throw rangeError("inappropriate.array.length", ScriptRuntime.safeToString(length));
 503         }
 504         return (long) doubleLength;
 505     }
 506 
 507     /**
 508      * ECMA 15.4.4.2 Array.prototype.toString ( )
 509      *
 510      * @param self self reference
 511      * @return string representation of array
 512      */
 513     @Function(attributes = Attribute.NOT_ENUMERABLE)
 514     public static Object toString(final Object self) {
 515         final Object obj = Global.toObject(self);
 516         if (obj instanceof ScriptObject) {
 517             final InvokeByName joinInvoker = getJOIN();
 518             final ScriptObject sobj = (ScriptObject)obj;
 519             try {
 520                 final Object join = joinInvoker.getGetter().invokeExact(sobj);
 521                 if (Bootstrap.isCallable(join)) {
 522                     return joinInvoker.getInvoker().invokeExact(join, sobj);
 523                 }
 524             } catch (final RuntimeException | Error e) {
 525                 throw e;
 526             } catch (final Throwable t) {
 527                 throw new RuntimeException(t);
 528             }
 529         }
 530 
 531         // FIXME: should lookup Object.prototype.toString and call that?
 532         return ScriptRuntime.builtinObjectToString(self);
 533     }
 534 
 535     /**
 536      * Assert that an array is numeric, if not throw type error
 537      * @param self self array to check
 538      * @return true if numeric
 539      */
 540     @Function(attributes = Attribute.NOT_ENUMERABLE)
 541     public static Object assertNumeric(final Object self) {
 542         if(!(self instanceof NativeArray && ((NativeArray)self).getArray().getOptimisticType().isNumeric())) {
 543             throw typeError("not.a.numeric.array", ScriptRuntime.safeToString(self));
 544         }
 545         return Boolean.TRUE;
 546     }
 547 
 548     /**
 549      * ECMA 15.4.4.3 Array.prototype.toLocaleString ( )
 550      *
 551      * @param self self reference
 552      * @return locale specific string representation for array
 553      */
 554     @Function(attributes = Attribute.NOT_ENUMERABLE)
 555     public static String toLocaleString(final Object self) {
 556         final StringBuilder sb = new StringBuilder();
 557         final Iterator<Object> iter = arrayLikeIterator(self, true);
 558 
 559         while (iter.hasNext()) {
 560             final Object obj = iter.next();
 561 
 562             if (obj != null && obj != ScriptRuntime.UNDEFINED) {
 563                 final Object val = JSType.toScriptObject(obj);
 564 
 565                 try {
 566                     if (val instanceof ScriptObject) {
 567                         final InvokeByName localeInvoker = getTO_LOCALE_STRING();
 568                         final ScriptObject sobj           = (ScriptObject)val;
 569                         final Object       toLocaleString = localeInvoker.getGetter().invokeExact(sobj);
 570 
 571                         if (Bootstrap.isCallable(toLocaleString)) {
 572                             sb.append((String)localeInvoker.getInvoker().invokeExact(toLocaleString, sobj));
 573                         } else {
 574                             throw typeError("not.a.function", "toLocaleString");
 575                         }
 576                     }
 577                 } catch (final Error|RuntimeException t) {
 578                     throw t;
 579                 } catch (final Throwable t) {
 580                     throw new RuntimeException(t);
 581                 }
 582             }
 583 
 584             if (iter.hasNext()) {
 585                 sb.append(",");
 586             }
 587         }
 588 
 589         return sb.toString();
 590     }
 591 
 592     /**
 593      * ECMA 15.4.2.2 new Array (len)
 594      *
 595      * @param newObj was the new operator used to instantiate this array
 596      * @param self   self reference
 597      * @param args   arguments (length)
 598      * @return the new NativeArray
 599      */
 600     @Constructor(arity = 1)
 601     public static NativeArray construct(final boolean newObj, final Object self, final Object... args) {
 602         switch (args.length) {
 603         case 0:
 604             return new NativeArray(0);
 605         case 1:
 606             final Object len = args[0];
 607             if (len instanceof Number) {
 608                 long length;
 609                 if (len instanceof Integer || len instanceof Long) {
 610                     length = ((Number) len).longValue();
 611                     if (length >= 0 && length < JSType.MAX_UINT) {
 612                         return new NativeArray(length);
 613                     }
 614                 }
 615 
 616                 length = JSType.toUint32(len);
 617 
 618                 /*
 619                  * If the argument len is a Number and ToUint32(len) is equal to
 620                  * len, then the length property of the newly constructed object
 621                  * is set to ToUint32(len). If the argument len is a Number and
 622                  * ToUint32(len) is not equal to len, a RangeError exception is
 623                  * thrown.
 624                  */
 625                 final double numberLength = ((Number) len).doubleValue();
 626                 if (length != numberLength) {
 627                     throw rangeError("inappropriate.array.length", JSType.toString(numberLength));
 628                 }
 629 
 630                 return new NativeArray(length);
 631             }
 632             /*
 633              * If the argument len is not a Number, then the length property of
 634              * the newly constructed object is set to 1 and the 0 property of
 635              * the newly constructed object is set to len
 636              */
 637             return new NativeArray(new Object[]{args[0]});
 638             //fallthru
 639         default:
 640             return new NativeArray(args);
 641         }
 642     }
 643 
 644     /**
 645      * ECMA 15.4.2.2 new Array (len)
 646      *
 647      * Specialized constructor for zero arguments - empty array
 648      *
 649      * @param newObj was the new operator used to instantiate this array
 650      * @param self   self reference
 651      * @return the new NativeArray
 652      */
 653     @SpecializedFunction(isConstructor=true)
 654     public static NativeArray construct(final boolean newObj, final Object self) {
 655         return new NativeArray(0);
 656     }
 657 
 658     /**
 659      * ECMA 15.4.2.2 new Array (len)
 660      *
 661      * Specialized constructor for zero arguments - empty array
 662      *
 663      * @param newObj  was the new operator used to instantiate this array
 664      * @param self    self reference
 665      * @param element first element
 666      * @return the new NativeArray
 667      */
 668     @SpecializedFunction(isConstructor=true)
 669     public static Object construct(final boolean newObj, final Object self, final boolean element) {
 670         return new NativeArray(new Object[] { element });
 671     }
 672 
 673     /**
 674      * ECMA 15.4.2.2 new Array (len)
 675      *
 676      * Specialized constructor for one integer argument (length)
 677      *
 678      * @param newObj was the new operator used to instantiate this array
 679      * @param self   self reference
 680      * @param length array length
 681      * @return the new NativeArray
 682      */
 683     @SpecializedFunction(isConstructor=true)
 684     public static NativeArray construct(final boolean newObj, final Object self, final int length) {
 685         if (length >= 0) {
 686             return new NativeArray(length);
 687         }
 688 
 689         return construct(newObj, self, new Object[]{length});
 690     }
 691 
 692     /**
 693      * ECMA 15.4.2.2 new Array (len)
 694      *
 695      * Specialized constructor for one long argument (length)
 696      *
 697      * @param newObj was the new operator used to instantiate this array
 698      * @param self   self reference
 699      * @param length array length
 700      * @return the new NativeArray
 701      */
 702     @SpecializedFunction(isConstructor=true)
 703     public static NativeArray construct(final boolean newObj, final Object self, final long length) {
 704         if (length >= 0L && length <= JSType.MAX_UINT) {
 705             return new NativeArray(length);
 706         }
 707 
 708         return construct(newObj, self, new Object[]{length});
 709     }
 710 
 711     /**
 712      * ECMA 15.4.2.2 new Array (len)
 713      *
 714      * Specialized constructor for one double argument (length)
 715      *
 716      * @param newObj was the new operator used to instantiate this array
 717      * @param self   self reference
 718      * @param length array length
 719      * @return the new NativeArray
 720      */
 721     @SpecializedFunction(isConstructor=true)
 722     public static NativeArray construct(final boolean newObj, final Object self, final double length) {
 723         final long uint32length = JSType.toUint32(length);
 724 
 725         if (uint32length == length) {
 726             return new NativeArray(uint32length);
 727         }
 728 
 729         return construct(newObj, self, new Object[]{length});
 730     }
 731 
 732     /**
 733      * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] )
 734      *
 735      * @param self self reference
 736      * @param arg argument
 737      * @return resulting NativeArray
 738      */
 739     @SpecializedFunction(linkLogic=ConcatLinkLogic.class)
 740     public static NativeArray concat(final Object self, final int arg) {
 741         final ContinuousArrayData newData = getContinuousArrayDataCCE(self, Integer.class).copy(); //get at least an integer data copy of this data
 742         newData.fastPush(arg); //add an integer to its end
 743         return new NativeArray(newData);
 744     }
 745 
 746     /**
 747      * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] )
 748      *
 749      * @param self self reference
 750      * @param arg argument
 751      * @return resulting NativeArray
 752      */
 753     @SpecializedFunction(linkLogic=ConcatLinkLogic.class)
 754     public static NativeArray concat(final Object self, final long arg) {
 755         final ContinuousArrayData newData = getContinuousArrayDataCCE(self, Long.class).copy(); //get at least a long array data copy of this data
 756         newData.fastPush(arg); //add a long at the end
 757         return new NativeArray(newData);
 758     }
 759 
 760     /**
 761      * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] )
 762      *
 763      * @param self self reference
 764      * @param arg argument
 765      * @return resulting NativeArray
 766      */
 767     @SpecializedFunction(linkLogic=ConcatLinkLogic.class)
 768     public static NativeArray concat(final Object self, final double arg) {
 769         final ContinuousArrayData newData = getContinuousArrayDataCCE(self, Double.class).copy(); //get at least a number array data copy of this data
 770         newData.fastPush(arg); //add a double at the end
 771         return new NativeArray(newData);
 772     }
 773 
 774     /**
 775      * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] )
 776      *
 777      * @param self self reference
 778      * @param arg argument
 779      * @return resulting NativeArray
 780      */
 781     @SpecializedFunction(linkLogic=ConcatLinkLogic.class)
 782     public static NativeArray concat(final Object self, final Object arg) {
 783         //arg is [NativeArray] of same type.
 784         final ContinuousArrayData selfData = getContinuousArrayDataCCE(self);
 785         final ContinuousArrayData newData;
 786 
 787         if (arg instanceof NativeArray) {
 788             final ContinuousArrayData argData = (ContinuousArrayData)((NativeArray)arg).getArray();
 789             if (argData.isEmpty()) {
 790                 newData = selfData.copy();
 791             } else if (selfData.isEmpty()) {
 792                 newData = argData.copy();
 793             } else {
 794                 final Class<?> widestElementType = selfData.widest(argData).getBoxedElementType();
 795                 newData = ((ContinuousArrayData)selfData.convert(widestElementType)).fastConcat((ContinuousArrayData)argData.convert(widestElementType));
 796             }
 797         } else {
 798             newData = getContinuousArrayDataCCE(self, Object.class).copy();
 799             newData.fastPush(arg);
 800         }
 801 
 802         return new NativeArray(newData);
 803     }
 804 
 805     /**
 806      * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] )
 807      *
 808      * @param self self reference
 809      * @param args arguments
 810      * @return resulting NativeArray
 811      */
 812     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
 813     public static NativeArray concat(final Object self, final Object... args) {
 814         final ArrayList<Object> list = new ArrayList<>();
 815 
 816         concatToList(list, Global.toObject(self));
 817 
 818         for (final Object obj : args) {
 819             concatToList(list, obj);
 820         }
 821 
 822         return new NativeArray(list.toArray());
 823     }
 824 
 825     private static void concatToList(final ArrayList<Object> list, final Object obj) {
 826         final boolean isScriptArray  = isArray(obj);
 827         final boolean isScriptObject = isScriptArray || obj instanceof ScriptObject;
 828         if (isScriptArray || obj instanceof Iterable || (obj != null && obj.getClass().isArray())) {
 829             final Iterator<Object> iter = arrayLikeIterator(obj, true);
 830             if (iter.hasNext()) {
 831                 for (int i = 0; iter.hasNext(); ++i) {
 832                     final Object value = iter.next();
 833                     final boolean lacksIndex = obj != null && !((ScriptObject)obj).has(i);
 834                     if (value == ScriptRuntime.UNDEFINED && isScriptObject && lacksIndex) {
 835                         // TODO: eventually rewrite arrayLikeIterator to use a three-state enum for handling
 836                         // UNDEFINED instead of an "includeUndefined" boolean with states SKIP, INCLUDE,
 837                         // RETURN_EMPTY. Until then, this is how we'll make sure that empty elements don't make it
 838                         // into the concatenated array.
 839                         list.add(ScriptRuntime.EMPTY);
 840                     } else {
 841                         list.add(value);
 842                     }
 843                 }
 844             } else if (!isScriptArray) {
 845                 list.add(obj); // add empty object, but not an empty array
 846             }
 847         } else {
 848             // single element, add it
 849             list.add(obj);
 850         }
 851     }
 852 
 853     /**
 854      * ECMA 15.4.4.5 Array.prototype.join (separator)
 855      *
 856      * @param self      self reference
 857      * @param separator element separator
 858      * @return string representation after join
 859      */
 860     @Function(attributes = Attribute.NOT_ENUMERABLE)
 861     public static String join(final Object self, final Object separator) {
 862         final StringBuilder    sb   = new StringBuilder();
 863         final Iterator<Object> iter = arrayLikeIterator(self, true);
 864         final String           sep  = separator == ScriptRuntime.UNDEFINED ? "," : JSType.toString(separator);
 865 
 866         while (iter.hasNext()) {
 867             final Object obj = iter.next();
 868 
 869             if (obj != null && obj != ScriptRuntime.UNDEFINED) {
 870                 sb.append(JSType.toString(obj));
 871             }
 872 
 873             if (iter.hasNext()) {
 874                 sb.append(sep);
 875             }
 876         }
 877 
 878         return sb.toString();
 879     }
 880 
 881     /**
 882      * Specialization of pop for ContinuousArrayData
 883      *   The link guard checks that the array is continuous AND not empty.
 884      *   The runtime guard checks that the guard is continuous (CCE otherwise)
 885      *
 886      * Primitive specialization, {@link LinkLogic}
 887      *
 888      * @param self self reference
 889      * @return element popped
 890      * @throws ClassCastException if array is empty, facilitating Undefined return value
 891      */
 892     @SpecializedFunction(name="pop", linkLogic=PopLinkLogic.class)
 893     public static int popInt(final Object self) {
 894         //must be non empty IntArrayData
 895         return getContinuousNonEmptyArrayDataCCE(self, IntElements.class).fastPopInt();
 896     }
 897 
 898     /**
 899      * Specialization of pop for ContinuousArrayData
 900      *
 901      * Primitive specialization, {@link LinkLogic}
 902      *
 903      * @param self self reference
 904      * @return element popped
 905      * @throws ClassCastException if array is empty, facilitating Undefined return value
 906      */
 907     @SpecializedFunction(name="pop", linkLogic=PopLinkLogic.class)
 908     public static long popLong(final Object self) {
 909         //must be non empty Int or LongArrayData
 910         return getContinuousNonEmptyArrayDataCCE(self, IntOrLongElements.class).fastPopLong();
 911     }
 912 
 913     /**
 914      * Specialization of pop for ContinuousArrayData
 915      *
 916      * Primitive specialization, {@link LinkLogic}
 917      *
 918      * @param self self reference
 919      * @return element popped
 920      * @throws ClassCastException if array is empty, facilitating Undefined return value
 921      */
 922     @SpecializedFunction(name="pop", linkLogic=PopLinkLogic.class)
 923     public static double popDouble(final Object self) {
 924         //must be non empty int long or double array data
 925         return getContinuousNonEmptyArrayDataCCE(self, NumericElements.class).fastPopDouble();
 926     }
 927 
 928     /**
 929      * Specialization of pop for ContinuousArrayData
 930      *
 931      * Primitive specialization, {@link LinkLogic}
 932      *
 933      * @param self self reference
 934      * @return element popped
 935      * @throws ClassCastException if array is empty, facilitating Undefined return value
 936      */
 937     @SpecializedFunction(name="pop", linkLogic=PopLinkLogic.class)
 938     public static Object popObject(final Object self) {
 939         //can be any data, because the numeric ones will throw cce and force relink
 940         return getContinuousArrayDataCCE(self, null).fastPopObject();
 941     }
 942 
 943     /**
 944      * ECMA 15.4.4.6 Array.prototype.pop ()
 945      *
 946      * @param self self reference
 947      * @return array after pop
 948      */
 949     @Function(attributes = Attribute.NOT_ENUMERABLE)
 950     public static Object pop(final Object self) {
 951         try {
 952             final ScriptObject sobj = (ScriptObject)self;
 953 
 954             if (bulkable(sobj)) {
 955                 return sobj.getArray().pop();
 956             }
 957 
 958             final long len = JSType.toUint32(sobj.getLength());
 959 
 960             if (len == 0) {
 961                 sobj.set("length", 0, CALLSITE_STRICT);
 962                 return ScriptRuntime.UNDEFINED;
 963             }
 964 
 965             final long   index   = len - 1;
 966             final Object element = sobj.get(index);
 967 
 968             sobj.delete(index, true);
 969             sobj.set("length", index, CALLSITE_STRICT);
 970 
 971             return element;
 972         } catch (final ClassCastException | NullPointerException e) {
 973             throw typeError("not.an.object", ScriptRuntime.safeToString(self));
 974         }
 975     }
 976 
 977     /**
 978      * ECMA 15.4.4.7 Array.prototype.push (args...)
 979      *
 980      * Primitive specialization, {@link LinkLogic}
 981      *
 982      * @param self self reference
 983      * @param arg a primitive to push
 984      * @return array length after push
 985      */
 986     @SpecializedFunction(linkLogic=PushLinkLogic.class)
 987     public static long push(final Object self, final int arg) {
 988         return getContinuousArrayDataCCE(self, Integer.class).fastPush(arg);
 989     }
 990 
 991     /**
 992      * ECMA 15.4.4.7 Array.prototype.push (args...)
 993      *
 994      * Primitive specialization, {@link LinkLogic}
 995      *
 996      * @param self self reference
 997      * @param arg a primitive to push
 998      * @return array length after push
 999      */
1000     @SpecializedFunction(linkLogic=PushLinkLogic.class)
1001     public static long push(final Object self, final long arg) {
1002         return getContinuousArrayDataCCE(self, Long.class).fastPush(arg);
1003     }
1004 
1005     /**
1006      * ECMA 15.4.4.7 Array.prototype.push (args...)
1007      *
1008      * Primitive specialization, {@link LinkLogic}
1009      *
1010      * @param self self reference
1011      * @param arg a primitive to push
1012      * @return array length after push
1013      */
1014     @SpecializedFunction(linkLogic=PushLinkLogic.class)
1015     public static long push(final Object self, final double arg) {
1016         return getContinuousArrayDataCCE(self, Double.class).fastPush(arg);
1017     }
1018 
1019     /**
1020      * ECMA 15.4.4.7 Array.prototype.push (args...)
1021      *
1022      * Primitive specialization, {@link LinkLogic}
1023      *
1024      * @param self self reference
1025      * @param arg a primitive to push
1026      * @return array length after push
1027      */
1028     @SpecializedFunction(name="push", linkLogic=PushLinkLogic.class)
1029     public static long pushObject(final Object self, final Object arg) {
1030         return getContinuousArrayDataCCE(self, Object.class).fastPush(arg);
1031     }
1032 
1033     /**
1034      * ECMA 15.4.4.7 Array.prototype.push (args...)
1035      *
1036      * @param self self reference
1037      * @param args arguments to push
1038      * @return array length after pushes
1039      */
1040     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
1041     public static Object push(final Object self, final Object... args) {
1042         try {
1043             final ScriptObject sobj   = (ScriptObject)self;
1044 
1045             if (bulkable(sobj) && sobj.getArray().length() + args.length <= JSType.MAX_UINT) {
1046                 final ArrayData newData = sobj.getArray().push(true, args);
1047                 sobj.setArray(newData);
1048                 return newData.length();
1049             }
1050 
1051             long len = JSType.toUint32(sobj.getLength());
1052             for (final Object element : args) {
1053                 sobj.set(len++, element, CALLSITE_STRICT);
1054             }
1055             sobj.set("length", len, CALLSITE_STRICT);
1056 
1057             return len;
1058         } catch (final ClassCastException | NullPointerException e) {
1059             throw typeError(Context.getGlobal(), e, "not.an.object", ScriptRuntime.safeToString(self));
1060         }
1061     }
1062 
1063     /**
1064      * ECMA 15.4.4.7 Array.prototype.push (args...) specialized for single object argument
1065      *
1066      * @param self self reference
1067      * @param arg argument to push
1068      * @return array after pushes
1069      */
1070     @SpecializedFunction
1071     public static long push(final Object self, final Object arg) {
1072         try {
1073             final ScriptObject sobj = (ScriptObject)self;
1074             final ArrayData arrayData = sobj.getArray();
1075             final long length = arrayData.length();
1076             if (bulkable(sobj) && length < JSType.MAX_UINT) {
1077                 sobj.setArray(arrayData.push(true, arg));
1078                 return length + 1;
1079             }
1080 
1081             long len = JSType.toUint32(sobj.getLength());
1082             sobj.set(len++, arg, CALLSITE_STRICT);
1083             sobj.set("length", len, CALLSITE_STRICT);
1084             return len;
1085         } catch (final ClassCastException | NullPointerException e) {
1086             throw typeError("not.an.object", ScriptRuntime.safeToString(self));
1087         }
1088     }
1089 
1090     /**
1091      * ECMA 15.4.4.8 Array.prototype.reverse ()
1092      *
1093      * @param self self reference
1094      * @return reversed array
1095      */
1096     @Function(attributes = Attribute.NOT_ENUMERABLE)
1097     public static Object reverse(final Object self) {
1098         try {
1099             final ScriptObject sobj   = (ScriptObject)self;
1100             final long         len    = JSType.toUint32(sobj.getLength());
1101             final long         middle = len / 2;
1102 
1103             for (long lower = 0; lower != middle; lower++) {
1104                 final long    upper       = len - lower - 1;
1105                 final Object  lowerValue  = sobj.get(lower);
1106                 final Object  upperValue  = sobj.get(upper);
1107                 final boolean lowerExists = sobj.has(lower);
1108                 final boolean upperExists = sobj.has(upper);
1109 
1110                 if (lowerExists && upperExists) {
1111                     sobj.set(lower, upperValue, CALLSITE_STRICT);
1112                     sobj.set(upper, lowerValue, CALLSITE_STRICT);
1113                 } else if (!lowerExists && upperExists) {
1114                     sobj.set(lower, upperValue, CALLSITE_STRICT);
1115                     sobj.delete(upper, true);
1116                 } else if (lowerExists && !upperExists) {
1117                     sobj.delete(lower, true);
1118                     sobj.set(upper, lowerValue, CALLSITE_STRICT);
1119                 }
1120             }
1121             return sobj;
1122         } catch (final ClassCastException | NullPointerException e) {
1123             throw typeError("not.an.object", ScriptRuntime.safeToString(self));
1124         }
1125     }
1126 
1127     /**
1128      * ECMA 15.4.4.9 Array.prototype.shift ()
1129      *
1130      * @param self self reference
1131      * @return shifted array
1132      */
1133     @Function(attributes = Attribute.NOT_ENUMERABLE)
1134     public static Object shift(final Object self) {
1135         final Object obj = Global.toObject(self);
1136 
1137         Object first = ScriptRuntime.UNDEFINED;
1138 
1139         if (!(obj instanceof ScriptObject)) {
1140             return first;
1141         }
1142 
1143         final ScriptObject sobj   = (ScriptObject) obj;
1144 
1145         long len = JSType.toUint32(sobj.getLength());
1146 
1147         if (len > 0) {
1148             first = sobj.get(0);
1149 
1150             if (bulkable(sobj)) {
1151                 sobj.getArray().shiftLeft(1);
1152             } else {
1153                 boolean hasPrevious = true;
1154                 for (long k = 1; k < len; k++) {
1155                     final boolean hasCurrent = sobj.has(k);
1156                     if (hasCurrent) {
1157                         sobj.set(k - 1, sobj.get(k), CALLSITE_STRICT);
1158                     } else if (hasPrevious) {
1159                         sobj.delete(k - 1, true);
1160                     }
1161                     hasPrevious = hasCurrent;
1162                 }
1163             }
1164             sobj.delete(--len, true);
1165         } else {
1166             len = 0;
1167         }
1168 
1169         sobj.set("length", len, CALLSITE_STRICT);
1170 
1171         return first;
1172     }
1173 
1174     /**
1175      * ECMA 15.4.4.10 Array.prototype.slice ( start [ , end ] )
1176      *
1177      * @param self  self reference
1178      * @param start start of slice (inclusive)
1179      * @param end   end of slice (optional, exclusive)
1180      * @return sliced array
1181      */
1182     @Function(attributes = Attribute.NOT_ENUMERABLE)
1183     public static Object slice(final Object self, final Object start, final Object end) {
1184         final Object       obj                 = Global.toObject(self);
1185         if (!(obj instanceof ScriptObject)) {
1186             return ScriptRuntime.UNDEFINED;
1187         }
1188 
1189         final ScriptObject sobj                = (ScriptObject)obj;
1190         final long         len                 = JSType.toUint32(sobj.getLength());
1191         final long         relativeStart       = JSType.toLong(start);
1192         final long         relativeEnd         = end == ScriptRuntime.UNDEFINED ? len : JSType.toLong(end);
1193 
1194         long k = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len);
1195         final long finale = relativeEnd < 0 ? Math.max(len + relativeEnd, 0) : Math.min(relativeEnd, len);
1196 
1197         if (k >= finale) {
1198             return new NativeArray(0);
1199         }
1200 
1201         if (bulkable(sobj)) {
1202             return new NativeArray(sobj.getArray().slice(k, finale));
1203         }
1204 
1205         // Construct array with proper length to have a deleted filter on undefined elements
1206         final NativeArray copy = new NativeArray(finale - k);
1207         for (long n = 0; k < finale; n++, k++) {
1208             if (sobj.has(k)) {
1209                 copy.defineOwnProperty(ArrayIndex.getArrayIndex(n), sobj.get(k));
1210             }
1211         }
1212 
1213         return copy;
1214     }
1215 
1216     private static ScriptFunction compareFunction(final Object comparefn) {
1217         if (comparefn == ScriptRuntime.UNDEFINED) {
1218             return null;
1219         }
1220 
1221         if (! (comparefn instanceof ScriptFunction)) {
1222             throw typeError("not.a.function", ScriptRuntime.safeToString(comparefn));
1223         }
1224 
1225         return (ScriptFunction)comparefn;
1226     }
1227 
1228     private static Object[] sort(final Object[] array, final Object comparefn) {
1229         final ScriptFunction cmp = compareFunction(comparefn);
1230 
1231         final List<Object> list = Arrays.asList(array);
1232         final Object cmpThis = cmp == null || cmp.isStrict() ? ScriptRuntime.UNDEFINED : Global.instance();
1233 
1234         Collections.sort(list, new Comparator<Object>() {
1235             private final MethodHandle call_cmp = getCALL_CMP();
1236             @Override
1237             public int compare(final Object x, final Object y) {
1238                 if (x == ScriptRuntime.UNDEFINED && y == ScriptRuntime.UNDEFINED) {
1239                     return 0;
1240                 } else if (x == ScriptRuntime.UNDEFINED) {
1241                     return 1;
1242                 } else if (y == ScriptRuntime.UNDEFINED) {
1243                     return -1;
1244                 }
1245 
1246                 if (cmp != null) {
1247                     try {
1248                         return (int)Math.signum((double)call_cmp.invokeExact(cmp, cmpThis, x, y));
1249                     } catch (final RuntimeException | Error e) {
1250                         throw e;
1251                     } catch (final Throwable t) {
1252                         throw new RuntimeException(t);
1253                     }
1254                 }
1255 
1256                 return JSType.toString(x).compareTo(JSType.toString(y));
1257             }
1258         });
1259 
1260         return list.toArray(new Object[array.length]);
1261     }
1262 
1263     /**
1264      * ECMA 15.4.4.11 Array.prototype.sort ( comparefn )
1265      *
1266      * @param self       self reference
1267      * @param comparefn  element comparison function
1268      * @return sorted array
1269      */
1270     @Function(attributes = Attribute.NOT_ENUMERABLE)
1271     public static ScriptObject sort(final Object self, final Object comparefn) {
1272         try {
1273             final ScriptObject sobj    = (ScriptObject) self;
1274             final long         len     = JSType.toUint32(sobj.getLength());
1275             ArrayData          array   = sobj.getArray();
1276 
1277             if (len > 1) {
1278                 // Get only non-missing elements. Missing elements go at the end
1279                 // of the sorted array. So, just don't copy these to sort input.
1280                 final ArrayList<Object> src = new ArrayList<>();
1281 
1282                 for (final Iterator<Long> iter = array.indexIterator(); iter.hasNext(); ) {
1283                     final long index = iter.next();
1284                     if (index >= len) {
1285                         break;
1286                     }
1287                     src.add(array.getObject((int)index));
1288                 }
1289 
1290                 final Object[] sorted = sort(src.toArray(), comparefn);
1291 
1292                 for (int i = 0; i < sorted.length; i++) {
1293                     array = array.set(i, sorted[i], true);
1294                 }
1295 
1296                 // delete missing elements - which are at the end of sorted array
1297                 if (sorted.length != len) {
1298                     array = array.delete(sorted.length, len - 1);
1299                 }
1300 
1301                 sobj.setArray(array);
1302             }
1303 
1304             return sobj;
1305         } catch (final ClassCastException | NullPointerException e) {
1306             throw typeError("not.an.object", ScriptRuntime.safeToString(self));
1307         }
1308     }
1309 
1310     /**
1311      * ECMA 15.4.4.12 Array.prototype.splice ( start, deleteCount [ item1 [ , item2 [ , ... ] ] ] )
1312      *
1313      * @param self self reference
1314      * @param args arguments
1315      * @return result of splice
1316      */
1317     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
1318     public static Object splice(final Object self, final Object... args) {
1319         final Object obj = Global.toObject(self);
1320 
1321         if (!(obj instanceof ScriptObject)) {
1322             return ScriptRuntime.UNDEFINED;
1323         }
1324 
1325         final Object start = args.length > 0 ? args[0] : ScriptRuntime.UNDEFINED;
1326         final Object deleteCount = args.length > 1 ? args[1] : ScriptRuntime.UNDEFINED;
1327 
1328         Object[] items;
1329 
1330         if (args.length > 2) {
1331             items = new Object[args.length - 2];
1332             System.arraycopy(args, 2, items, 0, items.length);
1333         } else {
1334             items = ScriptRuntime.EMPTY_ARRAY;
1335         }
1336 
1337         final ScriptObject sobj                = (ScriptObject)obj;
1338         final long         len                 = JSType.toUint32(sobj.getLength());
1339         final long         relativeStart       = JSType.toLong(start);
1340 
1341         final long actualStart = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len);
1342         final long actualDeleteCount = Math.min(Math.max(JSType.toLong(deleteCount), 0), len - actualStart);
1343 
1344         NativeArray returnValue;
1345 
1346         if (actualStart <= Integer.MAX_VALUE && actualDeleteCount <= Integer.MAX_VALUE && bulkable(sobj)) {
1347             try {
1348                 returnValue =  new NativeArray(sobj.getArray().fastSplice((int)actualStart, (int)actualDeleteCount, items.length));
1349 
1350                 // Since this is a dense bulkable array we can use faster defineOwnProperty to copy new elements
1351                 int k = (int) actualStart;
1352                 for (int i = 0; i < items.length; i++, k++) {
1353                     sobj.defineOwnProperty(k, items[i]);
1354                 }
1355             } catch (final UnsupportedOperationException uoe) {
1356                 returnValue = slowSplice(sobj, actualStart, actualDeleteCount, items, len);
1357             }
1358         } else {
1359             returnValue = slowSplice(sobj, actualStart, actualDeleteCount, items, len);
1360         }
1361 
1362         return returnValue;
1363     }
1364 
1365     private static NativeArray slowSplice(final ScriptObject sobj, final long start, final long deleteCount, final Object[] items, final long len) {
1366 
1367         final NativeArray array = new NativeArray(deleteCount);
1368 
1369         for (long k = 0; k < deleteCount; k++) {
1370             final long from = start + k;
1371 
1372             if (sobj.has(from)) {
1373                 array.defineOwnProperty(ArrayIndex.getArrayIndex(k), sobj.get(from));
1374             }
1375         }
1376 
1377         if (items.length < deleteCount) {
1378             for (long k = start; k < len - deleteCount; k++) {
1379                 final long from = k + deleteCount;
1380                 final long to   = k + items.length;
1381 
1382                 if (sobj.has(from)) {
1383                     sobj.set(to, sobj.get(from), CALLSITE_STRICT);
1384                 } else {
1385                     sobj.delete(to, true);
1386                 }
1387             }
1388 
1389             for (long k = len; k > len - deleteCount + items.length; k--) {
1390                 sobj.delete(k - 1, true);
1391             }
1392         } else if (items.length > deleteCount) {
1393             for (long k = len - deleteCount; k > start; k--) {
1394                 final long from = k + deleteCount - 1;
1395                 final long to   = k + items.length - 1;
1396 
1397                 if (sobj.has(from)) {
1398                     final Object fromValue = sobj.get(from);
1399                     sobj.set(to, fromValue, CALLSITE_STRICT);
1400                 } else {
1401                     sobj.delete(to, true);
1402                 }
1403             }
1404         }
1405 
1406         long k = start;
1407         for (int i = 0; i < items.length; i++, k++) {
1408             sobj.set(k, items[i], CALLSITE_STRICT);
1409         }
1410 
1411         final long newLength = len - deleteCount + items.length;
1412         sobj.set("length", newLength, CALLSITE_STRICT);
1413 
1414         return array;
1415     }
1416 
1417     /**
1418      * ECMA 15.4.4.13 Array.prototype.unshift ( [ item1 [ , item2 [ , ... ] ] ] )
1419      *
1420      * @param self  self reference
1421      * @param items items for unshift
1422      * @return unshifted array
1423      */
1424     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
1425     public static Object unshift(final Object self, final Object... items) {
1426         final Object obj = Global.toObject(self);
1427 
1428         if (!(obj instanceof ScriptObject)) {
1429             return ScriptRuntime.UNDEFINED;
1430         }
1431 
1432         final ScriptObject sobj   = (ScriptObject)obj;
1433         final long         len    = JSType.toUint32(sobj.getLength());
1434 
1435         if (items == null) {
1436             return ScriptRuntime.UNDEFINED;
1437         }
1438 
1439         if (bulkable(sobj)) {
1440             sobj.getArray().shiftRight(items.length);
1441 
1442             for (int j = 0; j < items.length; j++) {
1443                 sobj.setArray(sobj.getArray().set(j, items[j], true));
1444             }
1445         } else {
1446             for (long k = len; k > 0; k--) {
1447                 final long from = k - 1;
1448                 final long to = k + items.length - 1;
1449 
1450                 if (sobj.has(from)) {
1451                     final Object fromValue = sobj.get(from);
1452                     sobj.set(to, fromValue, CALLSITE_STRICT);
1453                 } else {
1454                     sobj.delete(to, true);
1455                 }
1456             }
1457 
1458             for (int j = 0; j < items.length; j++) {
1459                 sobj.set(j, items[j], CALLSITE_STRICT);
1460             }
1461         }
1462 
1463         final long newLength = len + items.length;
1464         sobj.set("length", newLength, CALLSITE_STRICT);
1465 
1466         return newLength;
1467     }
1468 
1469     /**
1470      * ECMA 15.4.4.14 Array.prototype.indexOf ( searchElement [ , fromIndex ] )
1471      *
1472      * @param self           self reference
1473      * @param searchElement  element to search for
1474      * @param fromIndex      start index of search
1475      * @return index of element, or -1 if not found
1476      */
1477     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
1478     public static long indexOf(final Object self, final Object searchElement, final Object fromIndex) {
1479         try {
1480             final ScriptObject sobj = (ScriptObject)Global.toObject(self);
1481             final long         len  = JSType.toUint32(sobj.getLength());
1482             if (len == 0) {
1483                 return -1;
1484             }
1485 
1486             final long         n = JSType.toLong(fromIndex);
1487             if (n >= len) {
1488                 return -1;
1489             }
1490 
1491 
1492             for (long k = Math.max(0, n < 0 ? len - Math.abs(n) : n); k < len; k++) {
1493                 if (sobj.has(k)) {
1494                     if (ScriptRuntime.EQ_STRICT(sobj.get(k), searchElement)) {
1495                         return k;
1496                     }
1497                 }
1498             }
1499         } catch (final ClassCastException | NullPointerException e) {
1500             //fallthru
1501         }
1502 
1503         return -1;
1504     }
1505 
1506     /**
1507      * ECMA 15.4.4.15 Array.prototype.lastIndexOf ( searchElement [ , fromIndex ] )
1508      *
1509      * @param self self reference
1510      * @param args arguments: element to search for and optional from index
1511      * @return index of element, or -1 if not found
1512      */
1513     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
1514     public static long lastIndexOf(final Object self, final Object... args) {
1515         try {
1516             final ScriptObject sobj = (ScriptObject)Global.toObject(self);
1517             final long         len  = JSType.toUint32(sobj.getLength());
1518 
1519             if (len == 0) {
1520                 return -1;
1521             }
1522 
1523             final Object searchElement = args.length > 0 ? args[0] : ScriptRuntime.UNDEFINED;
1524             final long   n             = args.length > 1 ? JSType.toLong(args[1]) : len - 1;
1525 
1526             for (long k = n < 0 ? len - Math.abs(n) : Math.min(n, len - 1); k >= 0; k--) {
1527                 if (sobj.has(k)) {
1528                     if (ScriptRuntime.EQ_STRICT(sobj.get(k), searchElement)) {
1529                         return k;
1530                     }
1531                 }
1532             }
1533         } catch (final ClassCastException | NullPointerException e) {
1534             throw typeError("not.an.object", ScriptRuntime.safeToString(self));
1535         }
1536 
1537         return -1;
1538     }
1539 
1540     /**
1541      * ECMA 15.4.4.16 Array.prototype.every ( callbackfn [ , thisArg ] )
1542      *
1543      * @param self        self reference
1544      * @param callbackfn  callback function per element
1545      * @param thisArg     this argument
1546      * @return true if callback function return true for every element in the array, false otherwise
1547      */
1548     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
1549     public static boolean every(final Object self, final Object callbackfn, final Object thisArg) {
1550         return applyEvery(Global.toObject(self), callbackfn, thisArg);
1551     }
1552 
1553     private static boolean applyEvery(final Object self, final Object callbackfn, final Object thisArg) {
1554         return new IteratorAction<Boolean>(Global.toObject(self), callbackfn, thisArg, true) {
1555             private final MethodHandle everyInvoker = getEVERY_CALLBACK_INVOKER();
1556 
1557             @Override
1558             protected boolean forEach(final Object val, final long i) throws Throwable {
1559                 return result = (boolean)everyInvoker.invokeExact(callbackfn, thisArg, val, i, self);
1560             }
1561         }.apply();
1562     }
1563 
1564     /**
1565      * ECMA 15.4.4.17 Array.prototype.some ( callbackfn [ , thisArg ] )
1566      *
1567      * @param self        self reference
1568      * @param callbackfn  callback function per element
1569      * @param thisArg     this argument
1570      * @return true if callback function returned true for any element in the array, false otherwise
1571      */
1572     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
1573     public static boolean some(final Object self, final Object callbackfn, final Object thisArg) {
1574         return new IteratorAction<Boolean>(Global.toObject(self), callbackfn, thisArg, false) {
1575             private final MethodHandle someInvoker = getSOME_CALLBACK_INVOKER();
1576 
1577             @Override
1578             protected boolean forEach(final Object val, final long i) throws Throwable {
1579                 return !(result = (boolean)someInvoker.invokeExact(callbackfn, thisArg, val, i, self));
1580             }
1581         }.apply();
1582     }
1583 
1584     /**
1585      * ECMA 15.4.4.18 Array.prototype.forEach ( callbackfn [ , thisArg ] )
1586      *
1587      * @param self        self reference
1588      * @param callbackfn  callback function per element
1589      * @param thisArg     this argument
1590      * @return undefined
1591      */
1592     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
1593     public static Object forEach(final Object self, final Object callbackfn, final Object thisArg) {
1594         return new IteratorAction<Object>(Global.toObject(self), callbackfn, thisArg, ScriptRuntime.UNDEFINED) {
1595             private final MethodHandle forEachInvoker = getFOREACH_CALLBACK_INVOKER();
1596 
1597             @Override
1598             protected boolean forEach(final Object val, final long i) throws Throwable {
1599                 forEachInvoker.invokeExact(callbackfn, thisArg, val, i, self);
1600                 return true;
1601             }
1602         }.apply();
1603     }
1604 
1605     /**
1606      * ECMA 15.4.4.19 Array.prototype.map ( callbackfn [ , thisArg ] )
1607      *
1608      * @param self        self reference
1609      * @param callbackfn  callback function per element
1610      * @param thisArg     this argument
1611      * @return array with elements transformed by map function
1612      */
1613     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
1614     public static NativeArray map(final Object self, final Object callbackfn, final Object thisArg) {
1615         return new IteratorAction<NativeArray>(Global.toObject(self), callbackfn, thisArg, null) {
1616             private final MethodHandle mapInvoker = getMAP_CALLBACK_INVOKER();
1617 
1618             @Override
1619             protected boolean forEach(final Object val, final long i) throws Throwable {
1620                 final Object r = mapInvoker.invokeExact(callbackfn, thisArg, val, i, self);
1621                 result.defineOwnProperty(ArrayIndex.getArrayIndex(index), r);
1622                 return true;
1623             }
1624 
1625             @Override
1626             public void applyLoopBegin(final ArrayLikeIterator<Object> iter0) {
1627                 // map return array should be of same length as source array
1628                 // even if callback reduces source array length
1629                 result = new NativeArray(iter0.getLength());
1630             }
1631         }.apply();
1632     }
1633 
1634     /**
1635      * ECMA 15.4.4.20 Array.prototype.filter ( callbackfn [ , thisArg ] )
1636      *
1637      * @param self        self reference
1638      * @param callbackfn  callback function per element
1639      * @param thisArg     this argument
1640      * @return filtered array
1641      */
1642     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
1643     public static NativeArray filter(final Object self, final Object callbackfn, final Object thisArg) {
1644         return new IteratorAction<NativeArray>(Global.toObject(self), callbackfn, thisArg, new NativeArray()) {
1645             private long to = 0;
1646             private final MethodHandle filterInvoker = getFILTER_CALLBACK_INVOKER();
1647 
1648             @Override
1649             protected boolean forEach(final Object val, final long i) throws Throwable {
1650                 if ((boolean)filterInvoker.invokeExact(callbackfn, thisArg, val, i, self)) {
1651                     result.defineOwnProperty(ArrayIndex.getArrayIndex(to++), val);
1652                 }
1653                 return true;
1654             }
1655         }.apply();
1656     }
1657 
1658     private static Object reduceInner(final ArrayLikeIterator<Object> iter, final Object self, final Object... args) {
1659         final Object  callbackfn          = args.length > 0 ? args[0] : ScriptRuntime.UNDEFINED;
1660         final boolean initialValuePresent = args.length > 1;
1661 
1662         Object initialValue = initialValuePresent ? args[1] : ScriptRuntime.UNDEFINED;
1663 
1664         if (callbackfn == ScriptRuntime.UNDEFINED) {
1665             throw typeError("not.a.function", "undefined");
1666         }
1667 
1668         if (!initialValuePresent) {
1669             if (iter.hasNext()) {
1670                 initialValue = iter.next();
1671             } else {
1672                 throw typeError("array.reduce.invalid.init");
1673             }
1674         }
1675 
1676         //if initial value is ScriptRuntime.UNDEFINED - step forward once.
1677         return new IteratorAction<Object>(Global.toObject(self), callbackfn, ScriptRuntime.UNDEFINED, initialValue, iter) {
1678             private final MethodHandle reduceInvoker = getREDUCE_CALLBACK_INVOKER();
1679 
1680             @Override
1681             protected boolean forEach(final Object val, final long i) throws Throwable {
1682                 // TODO: why can't I declare the second arg as Undefined.class?
1683                 result = reduceInvoker.invokeExact(callbackfn, ScriptRuntime.UNDEFINED, result, val, i, self);
1684                 return true;
1685             }
1686         }.apply();
1687     }
1688 
1689     /**
1690      * ECMA 15.4.4.21 Array.prototype.reduce ( callbackfn [ , initialValue ] )
1691      *
1692      * @param self self reference
1693      * @param args arguments to reduce
1694      * @return accumulated result
1695      */
1696     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
1697     public static Object reduce(final Object self, final Object... args) {
1698         return reduceInner(arrayLikeIterator(self), self, args);
1699     }
1700 
1701     /**
1702      * ECMA 15.4.4.22 Array.prototype.reduceRight ( 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 reduceRight(final Object self, final Object... args) {
1710         return reduceInner(reverseArrayLikeIterator(self), self, args);
1711     }
1712 
1713     /**
1714      * Determine if Java bulk array operations may be used on the underlying
1715      * storage. This is possible only if the object's prototype chain is empty
1716      * or each of the prototypes in the chain is empty.
1717      *
1718      * @param self the object to examine
1719      * @return true if optimizable
1720      */
1721     private static boolean bulkable(final ScriptObject self) {
1722         return self.isArray() && !hasInheritedArrayEntries(self) && !self.isLengthNotWritable();
1723     }
1724 
1725     private static boolean hasInheritedArrayEntries(final ScriptObject self) {
1726         ScriptObject proto = self.getProto();
1727         while (proto != null) {
1728             if (proto.hasArrayEntries()) {
1729                 return true;
1730             }
1731             proto = proto.getProto();
1732         }
1733 
1734         return false;
1735     }
1736 
1737     @Override
1738     public String toString() {
1739         return "NativeArray@" + Debug.id(this) + " [" + getArray().getClass().getSimpleName() + ']';
1740     }
1741 
1742     @Override
1743     public SpecializedFunction.LinkLogic getLinkLogic(final Class<? extends LinkLogic> clazz) {
1744         if (clazz == PushLinkLogic.class) {
1745             return PushLinkLogic.INSTANCE;
1746         } else if (clazz == PopLinkLogic.class) {
1747             return PopLinkLogic.INSTANCE;
1748         } else if (clazz == ConcatLinkLogic.class) {
1749             return ConcatLinkLogic.INSTANCE;
1750         }
1751         return null;
1752     }
1753 
1754     @Override
1755     public boolean hasPerInstanceAssumptions() {
1756         return true; //length writable switchpoint
1757     }
1758 
1759     /**
1760      * This is an abstract super class that contains common functionality for all
1761      * specialized optimistic builtins in NativeArray. For example, it handles the
1762      * modification switchpoint which is touched when length is written.
1763      */
1764     private static abstract class ArrayLinkLogic extends SpecializedFunction.LinkLogic {
1765         protected ArrayLinkLogic() {
1766         }
1767 
1768         protected static ContinuousArrayData getContinuousArrayData(final Object self) {
1769             try {
1770                 //cast to NativeArray, to avoid cases like x = {0:0, 1:1}, x.length = 2, where we can't use the array push/pop
1771                 return (ContinuousArrayData)((NativeArray)self).getArray();
1772             } catch (final Exception e) {
1773                 return null;
1774             }
1775         }
1776 
1777         /**
1778          * Push and pop callsites can throw ClassCastException as a mechanism to have them
1779          * relinked - this enabled fast checks of the kind of ((IntArrayData)arrayData).push(x)
1780          * for an IntArrayData only push - if this fails, a CCE will be thrown and we will relink
1781          */
1782         @Override
1783         public Class<? extends Throwable> getRelinkException() {
1784             return ClassCastException.class;
1785         }
1786     }
1787 
1788     /**
1789      * This is linker logic for optimistic concatenations
1790      */
1791     private static final class ConcatLinkLogic extends ArrayLinkLogic {
1792         private static final LinkLogic INSTANCE = new ConcatLinkLogic();
1793 
1794         @Override
1795         public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) {
1796             final Object[] args = request.getArguments();
1797 
1798             if (args.length != 3) { //single argument check
1799                 return false;
1800             }
1801 
1802             final ContinuousArrayData selfData = getContinuousArrayData(self);
1803             if (selfData == null) {
1804                 return false;
1805             }
1806 
1807             final Object arg = args[2];
1808             //args[2] continuousarray or non arraydata, let past non array datas
1809             if (arg instanceof NativeArray) {
1810                 final ContinuousArrayData argData = getContinuousArrayData(arg);
1811                 if (argData == null) {
1812                     return false;
1813                 }
1814             }
1815 
1816             return true;
1817         }
1818     }
1819 
1820     /**
1821      * This is linker logic for optimistic pushes
1822      */
1823     private static final class PushLinkLogic extends ArrayLinkLogic {
1824         private static final LinkLogic INSTANCE = new PushLinkLogic();
1825 
1826         @Override
1827         public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) {
1828             return getContinuousArrayData(self) != null;
1829         }
1830     }
1831 
1832     /**
1833      * This is linker logic for optimistic pops
1834      */
1835     private static final class PopLinkLogic extends ArrayLinkLogic {
1836         private static final LinkLogic INSTANCE = new PopLinkLogic();
1837 
1838         /**
1839          * We need to check if we are dealing with a continuous non empty array data here,
1840          * as pop with a primitive return value returns undefined for arrays with length 0
1841          */
1842         @Override
1843         public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) {
1844             final ContinuousArrayData data = getContinuousNonEmptyArrayData(self);
1845             if (data != null) {
1846                 final Class<?> elementType = data.getElementType();
1847                 final Class<?> returnType  = desc.getMethodType().returnType();
1848                 final boolean  typeFits    = JSType.getAccessorTypeIndex(returnType) >= JSType.getAccessorTypeIndex(elementType);
1849                 return typeFits;
1850             }
1851             return false;
1852         }
1853 
1854         private static ContinuousArrayData getContinuousNonEmptyArrayData(final Object self) {
1855             final ContinuousArrayData data = getContinuousArrayData(self);
1856             if (data != null) {
1857                 return data.length() == 0 ? null : data;
1858             }
1859             return null;
1860         }
1861     }
1862 
1863     //runtime calls for push and pops. they could be used as guards, but they also perform the runtime logic,
1864     //so rather than synthesizing them into a guard method handle that would also perform the push on the
1865     //retrieved receiver, we use this as runtime logic
1866 
1867     //TODO - fold these into the Link logics, but I'll do that as a later step, as I want to do a checkin
1868     //where everything works first
1869 
1870     private static final <T> ContinuousArrayData getContinuousNonEmptyArrayDataCCE(final Object self, final Class<T> clazz) {
1871         try {
1872             @SuppressWarnings("unchecked")
1873             final ContinuousArrayData data = (ContinuousArrayData)(T)((NativeArray)self).getArray();
1874             if (data.length() != 0L) {
1875                 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
1876            }
1877         } catch (final NullPointerException e) {
1878             //fallthru
1879         }
1880         throw new ClassCastException();
1881     }
1882 
1883     private static final ContinuousArrayData getContinuousArrayDataCCE(final Object self) {
1884         try {
1885             return (ContinuousArrayData)((NativeArray)self).getArray();
1886          } catch (final NullPointerException e) {
1887              throw new ClassCastException();
1888          }
1889     }
1890 
1891     private static final ContinuousArrayData getContinuousArrayDataCCE(final Object self, final Class<?> elementType) {
1892         try {
1893            return (ContinuousArrayData)((NativeArray)self).getArray(elementType); //ensure element type can fit "elementType"
1894         } catch (final NullPointerException e) {
1895             throw new ClassCastException();
1896         }
1897     }
1898 }