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