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