1 /*
   2  * Copyright (c) 2010, 2016, 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.runtime.arrays;
  27 
  28 import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
  29 
  30 import java.lang.invoke.MethodHandle;
  31 import java.lang.invoke.MethodHandles;
  32 import java.lang.reflect.Array;
  33 import java.nio.ByteBuffer;
  34 import java.util.ArrayList;
  35 import java.util.Iterator;
  36 import java.util.List;
  37 import jdk.dynalink.CallSiteDescriptor;
  38 import jdk.dynalink.linker.GuardedInvocation;
  39 import jdk.dynalink.linker.LinkRequest;
  40 import jdk.nashorn.internal.codegen.CompilerConstants;
  41 import jdk.nashorn.internal.codegen.types.Type;
  42 import jdk.nashorn.internal.objects.Global;
  43 import jdk.nashorn.internal.runtime.JSType;
  44 import jdk.nashorn.internal.runtime.PropertyDescriptor;
  45 import jdk.nashorn.internal.runtime.ScriptRuntime;
  46 import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
  47 
  48 /**
  49  * ArrayData - abstraction for wrapping array elements
  50  */
  51 public abstract class ArrayData {
  52     /** Minimum chunk size for underlying arrays */
  53     protected static final int CHUNK_SIZE = 32;
  54 
  55     /** Untouched data - still link callsites as IntArrayData, but expands to
  56      *  a proper ArrayData when we try to write to it */
  57     public static final ArrayData EMPTY_ARRAY = new UntouchedArrayData();
  58 
  59     /**
  60      * Length of the array data. Not necessarily length of the wrapped array.
  61      * This is private to ensure that no one in a subclass is able to touch the length
  62      * without going through {@link #setLength}. This is used to implement
  63      * {@link LengthNotWritableFilter}s, ensuring that there are no ways past
  64      * a {@link #setLength} function replaced by a nop
  65      */
  66     private long length;
  67 
  68     /**
  69      * Method handle to throw an {@link UnwarrantedOptimismException} when getting an element
  70      * of the wrong type
  71      */
  72     protected static final CompilerConstants.Call THROW_UNWARRANTED = staticCall(MethodHandles.lookup(), ArrayData.class, "throwUnwarranted", void.class, ArrayData.class, int.class, int.class);
  73 
  74     /**
  75      * Immutable empty array to get ScriptObjects started.
  76      * Use the same array and convert it to mutable as soon as it is modified
  77      */
  78     private static class UntouchedArrayData extends ContinuousArrayData {
  79         private UntouchedArrayData() {
  80             super(0);
  81         }
  82 
  83         private ArrayData toRealArrayData() {
  84             return new IntArrayData(0);
  85         }
  86 
  87         private ArrayData toRealArrayData(final int index) {
  88             final IntArrayData newData = new IntArrayData(index + 1);
  89             return new DeletedRangeArrayFilter(newData, 0, index);
  90         }
  91 
  92         @Override
  93         public ContinuousArrayData copy() {
  94             assert length() == 0;
  95             return this;
  96         }
  97 
  98         @Override
  99         public Object asArrayOfType(final Class<?> componentType) {
 100             return Array.newInstance(componentType, 0);
 101         }
 102 
 103         @Override
 104         public Object[] asObjectArray() {
 105             return ScriptRuntime.EMPTY_ARRAY;
 106         }
 107 
 108         @Override
 109         public ArrayData ensure(final long safeIndex) {
 110             assert safeIndex >= 0L;
 111             if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH) {
 112                 return new SparseArrayData(this, safeIndex + 1);
 113             }
 114             //known to fit in int
 115             return toRealArrayData((int)safeIndex);
 116 
 117         }
 118 
 119         @Override
 120         public ArrayData convert(final Class<?> type) {
 121             return toRealArrayData().convert(type);
 122         }
 123 
 124         @Override
 125         public ArrayData delete(final int index) {
 126             return new DeletedRangeArrayFilter(this, index, index);
 127         }
 128 
 129         @Override
 130         public ArrayData delete(final long fromIndex, final long toIndex) {
 131             return new DeletedRangeArrayFilter(this, fromIndex, toIndex);
 132         }
 133 
 134         @Override
 135         public ArrayData shiftLeft(final int by) {
 136             return this; //nop, always empty or we wouldn't be of this class
 137         }
 138 
 139         @Override
 140         public ArrayData shiftRight(final int by) {
 141             return this; //always empty or we wouldn't be of this class
 142         }
 143 
 144         @Override
 145         public ArrayData shrink(final long newLength) {
 146             return this;
 147         }
 148 
 149         @Override
 150         public ArrayData set(final int index, final Object value, final boolean strict) {
 151             return toRealArrayData(index).set(index, value, strict);
 152         }
 153 
 154         @Override
 155         public ArrayData set(final int index, final int value, final boolean strict) {
 156             return toRealArrayData(index).set(index, value, strict);
 157         }
 158 
 159         @Override
 160         public ArrayData set(final int index, final double value, final boolean strict) {
 161             return toRealArrayData(index).set(index, value, strict);
 162         }
 163 
 164         @Override
 165         public int getInt(final int index) {
 166             throw new ArrayIndexOutOfBoundsException(index); //empty
 167         }
 168 
 169         @Override
 170         public double getDouble(final int index) {
 171             throw new ArrayIndexOutOfBoundsException(index); //empty
 172         }
 173 
 174         @Override
 175         public Object getObject(final int index) {
 176             throw new ArrayIndexOutOfBoundsException(index); //empty
 177         }
 178 
 179         @Override
 180         public boolean has(final int index) {
 181             return false; //empty
 182         }
 183 
 184         @Override
 185         public Object pop() {
 186             return ScriptRuntime.UNDEFINED;
 187         }
 188 
 189         @Override
 190         public ArrayData push(final boolean strict, final Object item) {
 191             return toRealArrayData().push(strict, item);
 192         }
 193 
 194         @Override
 195         public ArrayData slice(final long from, final long to) {
 196             return this; //empty
 197         }
 198 
 199         @Override
 200         public ContinuousArrayData fastConcat(final ContinuousArrayData otherData) {
 201             return otherData.copy();
 202         }
 203 
 204         //no need to override fastPopInt, as the default behavior is to throw classcast exception so we
 205         //can relink and return an undefined, this is the IntArrayData default behavior
 206         @Override
 207         public String toString() {
 208             return getClass().getSimpleName();
 209         }
 210 
 211         @Override
 212         public MethodHandle getElementGetter(final Class<?> returnType, final int programPoint) {
 213             return null;
 214         }
 215 
 216         @Override
 217         public MethodHandle getElementSetter(final Class<?> elementType) {
 218             return null;
 219         }
 220 
 221         @Override
 222         public Class<?> getElementType() {
 223             return int.class;
 224         }
 225 
 226         @Override
 227         public Class<?> getBoxedElementType() {
 228             return Integer.class;
 229         }
 230     }
 231 
 232     /**
 233      * Constructor
 234      * @param length Virtual length of the array.
 235      */
 236     protected ArrayData(final long length) {
 237         this.length = length;
 238     }
 239 
 240     /**
 241      * Factory method for unspecified array - start as int
 242      * @return ArrayData
 243      */
 244     public static ArrayData initialArray() {
 245         return new IntArrayData();
 246     }
 247 
 248     /**
 249      * Unwarranted thrower
 250      *
 251      * @param data         array data
 252      * @param programPoint program point
 253      * @param index        array index
 254      */
 255     protected static void throwUnwarranted(final ArrayData data, final int programPoint, final int index) {
 256         throw new UnwarrantedOptimismException(data.getObject(index), programPoint);
 257     }
 258 
 259     /**
 260      * Align an array size up to the nearest array chunk size
 261      * @param size size required
 262      * @return size given, always &gt;= size
 263      */
 264     protected static int alignUp(final int size) {
 265         return size + CHUNK_SIZE - 1 & ~(CHUNK_SIZE - 1);
 266     }
 267 
 268     /**
 269      * Factory method for unspecified array with given length - start as int array data
 270      *
 271      * @param length the initial length
 272      * @return ArrayData
 273      */
 274     public static ArrayData allocate(final long length) {
 275         if (length == 0L) {
 276             return new IntArrayData();
 277         } else if (length >= SparseArrayData.MAX_DENSE_LENGTH) {
 278             return new SparseArrayData(EMPTY_ARRAY, length);
 279         } else {
 280             return new DeletedRangeArrayFilter(new IntArrayData((int) length), 0, length - 1);
 281         }
 282     }
 283 
 284     /**
 285      * Factory method for unspecified given an array object
 286      *
 287      * @param  array the array
 288      * @return ArrayData wrapping this array
 289      */
 290     public static ArrayData allocate(final Object array) {
 291         final Class<?> clazz = array.getClass();
 292 
 293         if (clazz == int[].class) {
 294             return new IntArrayData((int[])array, ((int[])array).length);
 295         } else if (clazz == double[].class) {
 296             return new NumberArrayData((double[])array, ((double[])array).length);
 297         } else {
 298             return new ObjectArrayData((Object[])array, ((Object[])array).length);
 299         }
 300     }
 301 
 302     /**
 303      * Allocate an ArrayData wrapping a given array
 304      *
 305      * @param array the array to use for initial elements
 306      * @return the ArrayData
 307      */
 308     public static ArrayData allocate(final int[] array) {
 309          return new IntArrayData(array, array.length);
 310     }
 311 
 312     /**
 313      * Allocate an ArrayData wrapping a given array
 314      *
 315      * @param array the array to use for initial elements
 316      * @return the ArrayData
 317      */
 318     public static ArrayData allocate(final double[] array) {
 319         return new NumberArrayData(array, array.length);
 320     }
 321 
 322     /**
 323      * Allocate an ArrayData wrapping a given array
 324      *
 325      * @param array the array to use for initial elements
 326      * @return the ArrayData
 327      */
 328     public static ArrayData allocate(final Object[] array) {
 329         return new ObjectArrayData(array, array.length);
 330     }
 331 
 332     /**
 333      * Allocate an ArrayData wrapping a given nio ByteBuffer
 334      *
 335      * @param buf the nio ByteBuffer to wrap
 336      * @return the ArrayData
 337      */
 338     public static ArrayData allocate(final ByteBuffer buf) {
 339         return new ByteBufferArrayData(buf);
 340     }
 341 
 342     /**
 343      * Apply a freeze filter to an ArrayData.
 344      *
 345      * @param underlying  the underlying ArrayData to wrap in the freeze filter
 346      * @return the frozen ArrayData
 347      */
 348     public static ArrayData freeze(final ArrayData underlying) {
 349         return new FrozenArrayFilter(underlying);
 350     }
 351 
 352     /**
 353      * Apply a seal filter to an ArrayData.
 354      *
 355      * @param underlying  the underlying ArrayData to wrap in the seal filter
 356      * @return the sealed ArrayData
 357      */
 358     public static ArrayData seal(final ArrayData underlying) {
 359         return new SealedArrayFilter(underlying);
 360     }
 361 
 362     /**
 363      * Prevent this array from being extended
 364      *
 365      * @param  underlying the underlying ArrayData to wrap in the non extensible filter
 366      * @return new array data, filtered
 367      */
 368     public static ArrayData preventExtension(final ArrayData underlying) {
 369         return new NonExtensibleArrayFilter(underlying);
 370     }
 371 
 372     /**
 373      * Prevent this array from having its length reset
 374      *
 375      * @param underlying the underlying ArrayDAta to wrap in the non extensible filter
 376      * @return new array data, filtered
 377      */
 378     public static ArrayData setIsLengthNotWritable(final ArrayData underlying) {
 379         return new LengthNotWritableFilter(underlying);
 380     }
 381 
 382     /**
 383      * Return the length of the array data. This may differ from the actual
 384      * length of the array this wraps as length may be set or gotten as any
 385      * other JavaScript Property
 386      *
 387      * Even though a JavaScript array length may be a long, we only store
 388      * int parts for the optimized array access. For long lengths there
 389      * are special cases anyway.
 390      *
 391      * TODO: represent arrays with "long" lengths as a special ArrayData
 392      * that basically maps to the ScriptObject directly for better abstraction
 393      *
 394      * @return the length of the data
 395      */
 396     public final long length() {
 397         return length;
 398     }
 399 
 400     /**
 401      * Return a copy of the array that can be modified without affecting this instance.
 402      * It is safe to return themselves for immutable subclasses.
 403      *
 404      * @return a new array
 405      */
 406     public abstract ArrayData copy();
 407 
 408     /**
 409      * Return a copy of the array data as an Object array.
 410      *
 411      * @return an Object array
 412      */
 413     public abstract Object[] asObjectArray();
 414 
 415     /**
 416      * Return a copy of the array data as an array of the specified type.
 417      *
 418      * @param componentType  the type of elements in the array
 419      * @return and array of the given type
 420      */
 421     public Object asArrayOfType(final Class<?> componentType) {
 422         return JSType.convertArray(asObjectArray(), componentType);
 423     }
 424 
 425     /**
 426      * Set the length of the data array
 427      *
 428      * @param length the new length for the data array
 429      */
 430     public void setLength(final long length) {
 431         this.length = length;
 432     }
 433 
 434     /**
 435      * Increase length by 1
 436      * @return the new length, not the old one (i.e. pre-increment)
 437      */
 438     protected final long increaseLength() {
 439         return ++this.length;
 440     }
 441 
 442     /**
 443      * Decrease length by 1.
 444      * @return the new length, not the old one (i.e. pre-decrement)
 445      */
 446     protected final long decreaseLength() {
 447         return --this.length;
 448     }
 449 
 450     /**
 451      * Shift the array data left
 452      *
 453      * TODO: This is used for Array.prototype.shift() which only shifts by 1,
 454      * so we might consider dropping the offset parameter.
 455      *
 456      * @param by offset to shift
 457      * @return New arraydata (or same)
 458      */
 459     public abstract ArrayData shiftLeft(final int by);
 460 
 461     /**
 462      * Shift the array right
 463      *
 464      * @param by offset to shift
 465 
 466      * @return New arraydata (or same)
 467      */
 468     public abstract ArrayData shiftRight(final int by);
 469 
 470     /**
 471      * Ensure that the given index exists and won't fail in a subsequent access.
 472      * If {@code safeIndex} is equal or greater than the current length the length is
 473      * updated to {@code safeIndex + 1}.
 474      *
 475      * @param safeIndex the index to ensure wont go out of bounds
 476      * @return new array data (or same)
 477      */
 478     public abstract ArrayData ensure(final long safeIndex);
 479 
 480     /**
 481      * Shrink the array to a new length, may or may not retain the
 482      * inner array
 483      *
 484      * @param newLength new max length
 485      *
 486      * @return new array data (or same)
 487      */
 488     public abstract ArrayData shrink(final long newLength);
 489 
 490     /**
 491      * Set an object value at a given index
 492      *
 493      * @param index the index
 494      * @param value the value
 495      * @param strict are we in strict mode
 496      * @return new array data (or same)
 497      */
 498     public abstract ArrayData set(final int index, final Object value, final boolean strict);
 499 
 500     /**
 501      * Set an int value at a given index
 502      *
 503      * @param index the index
 504      * @param value the value
 505      * @param strict are we in strict mode
 506      * @return new array data (or same)
 507      */
 508     public abstract ArrayData set(final int index, final int value, final boolean strict);
 509 
 510     /**
 511      * Set an double value at a given index
 512      *
 513      * @param index the index
 514      * @param value the value
 515      * @param strict are we in strict mode
 516      * @return new array data (or same)
 517      */
 518     public abstract ArrayData set(final int index, final double value, final boolean strict);
 519 
 520     /**
 521      * Set an empty value at a given index. Should only affect Object array.
 522      *
 523      * @param index the index
 524      * @return new array data (or same)
 525      */
 526     public ArrayData setEmpty(final int index) {
 527         // Do nothing.
 528         return this;
 529     }
 530 
 531     /**
 532      * Set an empty value for a given range. Should only affect Object array.
 533      *
 534      * @param lo range low end
 535      * @param hi range high end
 536      * @return new array data (or same)
 537      */
 538     public ArrayData setEmpty(final long lo, final long hi) {
 539         // Do nothing.
 540         return this;
 541     }
 542 
 543     /**
 544      * Get an int value from a given index
 545      *
 546      * @param index the index
 547      * @return the value
 548      */
 549     public abstract int getInt(final int index);
 550 
 551     /**
 552      * Returns the optimistic type of this array data. Basically, when an array data object needs to throw an
 553      * {@link UnwarrantedOptimismException}, this type is used as the actual type of the return value.
 554      * @return the optimistic type of this array data.
 555      */
 556     public Type getOptimisticType() {
 557         return Type.OBJECT;
 558     }
 559 
 560     /**
 561      * Get optimistic int - default is that it's impossible. Overridden
 562      * by arrays that actually represents ints
 563      *
 564      * @param index        the index
 565      * @param programPoint program point
 566      * @return the value
 567      */
 568     public int getIntOptimistic(final int index, final int programPoint) {
 569         throw new UnwarrantedOptimismException(getObject(index), programPoint, getOptimisticType());
 570     }
 571 
 572     /**
 573      * Get a double value from a given index
 574      *
 575      * @param index the index
 576      * @return the value
 577      */
 578     public abstract double getDouble(final int index);
 579 
 580     /**
 581      * Get optimistic double - default is that it's impossible. Overridden
 582      * by arrays that actually represents doubles or narrower
 583      *
 584      * @param index        the index
 585      * @param programPoint program point
 586      * @return the value
 587      */
 588     public double getDoubleOptimistic(final int index, final int programPoint) {
 589         throw new UnwarrantedOptimismException(getObject(index), programPoint, getOptimisticType());
 590     }
 591 
 592     /**
 593      * Get an Object value from a given index
 594      *
 595      * @param index the index
 596      * @return the value
 597      */
 598     public abstract Object getObject(final int index);
 599 
 600     /**
 601      * Tests to see if an entry exists (avoids boxing.)
 602      * @param index the index
 603      * @return true if entry exists
 604      */
 605     public abstract boolean has(final int index);
 606 
 607     /**
 608      * Returns if element at specific index can be deleted or not.
 609      *
 610      * @param index the index of the element
 611      * @param strict are we in strict mode
 612      *
 613      * @return true if element can be deleted
 614      */
 615     public boolean canDelete(final int index, final boolean strict) {
 616         return true;
 617     }
 618 
 619     /**
 620      * Returns if element at specific index can be deleted or not.
 621      *
 622      * @param longIndex  the index
 623      * @param strict     are we in strict mode
 624      *
 625      * @return true if range can be deleted
 626      */
 627     public boolean canDelete(final long longIndex, final boolean strict) {
 628         return true;
 629     }
 630 
 631     /**
 632      * Delete a range from the array if {@code fromIndex} is less than or equal to {@code toIndex}
 633      * and the array supports deletion.
 634      *
 635      * @param fromIndex  the start index (inclusive)
 636      * @param toIndex    the end index (inclusive)
 637      * @param strict     are we in strict mode
 638      * @return an array with the range deleted, or this array if no deletion took place
 639      */
 640     public final ArrayData safeDelete(final long fromIndex, final long toIndex, final boolean strict) {
 641         if (fromIndex <= toIndex && canDelete(fromIndex, strict)) {
 642             return delete(fromIndex, toIndex);
 643         }
 644         return this;
 645     }
 646 
 647     /**
 648      * Returns property descriptor for element at a given index
 649      *
 650      * @param global the global object
 651      * @param index  the index
 652      *
 653      * @return property descriptor for element
 654      */
 655     public PropertyDescriptor getDescriptor(final Global global, final int index) {
 656         return global.newDataDescriptor(getObject(index), true, true, true);
 657     }
 658 
 659     /**
 660      * Delete an array value at the given index, substituting
 661      * for an undefined
 662      *
 663      * @param index the index
 664      * @return new array data (or same)
 665      */
 666     public abstract ArrayData delete(final int index);
 667 
 668     /**
 669      * Delete a given range from this array;
 670      *
 671      * @param fromIndex  from index (inclusive)
 672      * @param toIndex    to index (inclusive)
 673      *
 674      * @return new ArrayData after deletion
 675      */
 676     public abstract ArrayData delete(final long fromIndex, final long toIndex);
 677 
 678     /**
 679      * Convert the ArrayData to one with a different element type
 680      * Currently Arrays are not collapsed to narrower types, just to
 681      * wider ones. Attempting to narrow an array will assert
 682      *
 683      * @param type new element type
 684      * @return new array data
 685      */
 686     public abstract ArrayData convert(final Class<?> type);
 687 
 688     /**
 689      * Push an array of items to the end of the array
 690      *
 691      * @param strict are we in strict mode
 692      * @param items  the items
 693      * @return new array data (or same)
 694      */
 695     public ArrayData push(final boolean strict, final Object... items) {
 696         if (items.length == 0) {
 697             return this;
 698         }
 699 
 700         final Class<?>  widest  = widestType(items);
 701 
 702         ArrayData newData = convert(widest);
 703         long      pos     = newData.length;
 704         for (final Object item : items) {
 705             newData = newData.ensure(pos); //avoid sparse array
 706             newData.set((int)pos++, item, strict);
 707         }
 708         return newData;
 709     }
 710 
 711     /**
 712      * Push an array of items to the end of the array
 713      *
 714      * @param strict are we in strict mode
 715      * @param item   the item
 716      * @return new array data (or same)
 717      */
 718     public ArrayData push(final boolean strict, final Object item) {
 719         return push(strict, new Object[] { item });
 720     }
 721 
 722     /**
 723      * Pop an element from the end of the array
 724      *
 725      * @return the popped element
 726      */
 727     public abstract Object pop();
 728 
 729     /**
 730      * Slice out a section of the array and return that
 731      * subsection as a new array data: [from, to)
 732      *
 733      * @param from start index
 734      * @param to   end index + 1
 735      * @return new array data
 736      */
 737     public abstract ArrayData slice(final long from, final long to);
 738 
 739     /**
 740      * Fast splice operation. This just modifies the array according to the number of
 741      * elements added and deleted but does not insert the added elements. Throws
 742      * {@code UnsupportedOperationException} if fast splice operation is not supported
 743      * for this class or arguments.
 744      *
 745      * @param start start index of splice operation
 746      * @param removed number of removed elements
 747      * @param added number of added elements
 748      * @throws UnsupportedOperationException if fast splice is not supported for the class or arguments.
 749      * @return new arraydata, but this never happens because we always throw an exception
 750      */
 751     public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException {
 752         throw new UnsupportedOperationException();
 753     }
 754 
 755     static Class<?> widestType(final Object... items) {
 756         assert items.length > 0;
 757 
 758         Class<?> widest = Integer.class;
 759 
 760         for (final Object item : items) {
 761             if (item == null) {
 762                 return Object.class;
 763             }
 764             final Class<?> itemClass = item.getClass();
 765             if (itemClass == Double.class || itemClass == Float.class || itemClass == Long.class) {
 766                 if (widest == Integer.class) {
 767                     widest = Double.class;
 768                 }
 769             } else if (itemClass != Integer.class && itemClass != Short.class && itemClass != Byte.class) {
 770                 return Object.class;
 771             }
 772         }
 773 
 774         return widest;
 775     }
 776 
 777     /**
 778      * Return a list of keys in the array for the iterators
 779      * @return iterator key list
 780      */
 781     protected List<Long> computeIteratorKeys() {
 782         final List<Long> keys = new ArrayList<>();
 783 
 784         final long len = length();
 785         for (long i = 0L; i < len; i = nextIndex(i)) {
 786             if (has((int)i)) {
 787                 keys.add(i);
 788             }
 789         }
 790 
 791         return keys;
 792     }
 793 
 794     /**
 795      * Return an iterator that goes through all indexes of elements
 796      * in this array. This includes those after array.length if
 797      * they exist
 798      *
 799      * @return iterator
 800      */
 801     public Iterator<Long> indexIterator() {
 802         return computeIteratorKeys().iterator();
 803     }
 804 
 805     /**
 806      * Exponential growth function for array size when in
 807      * need of resizing.
 808      *
 809      * @param size current size
 810      * @return next size to allocate for internal array
 811      */
 812     public static int nextSize(final int size) {
 813         return alignUp(size + 1) * 2;
 814     }
 815 
 816     /**
 817      * Return the next valid index from a given one. Subclassed for various
 818      * array representation
 819      *
 820      * @param index the current index
 821      *
 822      * @return the next index
 823      */
 824     long nextIndex(final long index) {
 825         return index + 1;
 826     }
 827 
 828     static Object invoke(final MethodHandle mh, final Object arg) {
 829         try {
 830             return mh.invoke(arg);
 831         } catch (final RuntimeException | Error e) {
 832             throw e;
 833         } catch (final Throwable t) {
 834             throw new RuntimeException(t);
 835         }
 836     }
 837 
 838    /**
 839      * Find a fast call if one exists
 840      *
 841      * @param clazz    array data class
 842      * @param desc     callsite descriptor
 843      * @param request  link request
 844      * @return fast property getter if one is found
 845      */
 846     public GuardedInvocation findFastCallMethod(final Class<? extends ArrayData> clazz, final CallSiteDescriptor desc, final LinkRequest request) {
 847         return null;
 848     }
 849 
 850     /**
 851      * Find a fast element getter if one exists
 852      *
 853      * @param clazz   array data class
 854      * @param desc    callsite descriptor
 855      * @param request link request
 856      * @return fast index getter if one is found
 857      */
 858     public GuardedInvocation findFastGetIndexMethod(final Class<? extends ArrayData> clazz, final CallSiteDescriptor desc, final LinkRequest request) { // array, index, value
 859         return null;
 860     }
 861 
 862     /**
 863      * Find a fast element setter if one exists
 864      *
 865      * @param clazz   array data class
 866      * @param desc    callsite descriptor
 867      * @param request link request
 868      * @return fast index getter if one is found
 869      */
 870     public GuardedInvocation findFastSetIndexMethod(final Class<? extends ArrayData> clazz, final CallSiteDescriptor desc, final LinkRequest request) { // array, index, value
 871         return null;
 872     }
 873 }