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