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