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