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