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