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