src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/ArrayData.java
changeset 47216 71c04702a3d5
parent 42790 edb30aa1a99b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/ArrayData.java	Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,873 @@
+/*
+ * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.runtime.arrays;
+
+import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Array;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import jdk.dynalink.CallSiteDescriptor;
+import jdk.dynalink.linker.GuardedInvocation;
+import jdk.dynalink.linker.LinkRequest;
+import jdk.nashorn.internal.codegen.CompilerConstants;
+import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.objects.Global;
+import jdk.nashorn.internal.runtime.JSType;
+import jdk.nashorn.internal.runtime.PropertyDescriptor;
+import jdk.nashorn.internal.runtime.ScriptRuntime;
+import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
+
+/**
+ * ArrayData - abstraction for wrapping array elements
+ */
+public abstract class ArrayData {
+    /** Minimum chunk size for underlying arrays */
+    protected static final int CHUNK_SIZE = 32;
+
+    /** Untouched data - still link callsites as IntArrayData, but expands to
+     *  a proper ArrayData when we try to write to it */
+    public static final ArrayData EMPTY_ARRAY = new UntouchedArrayData();
+
+    /**
+     * Length of the array data. Not necessarily length of the wrapped array.
+     * This is private to ensure that no one in a subclass is able to touch the length
+     * without going through {@link #setLength}. This is used to implement
+     * {@link LengthNotWritableFilter}s, ensuring that there are no ways past
+     * a {@link #setLength} function replaced by a nop
+     */
+    private long length;
+
+    /**
+     * Method handle to throw an {@link UnwarrantedOptimismException} when getting an element
+     * of the wrong type
+     */
+    protected static final CompilerConstants.Call THROW_UNWARRANTED = staticCall(MethodHandles.lookup(), ArrayData.class, "throwUnwarranted", void.class, ArrayData.class, int.class, int.class);
+
+    /**
+     * Immutable empty array to get ScriptObjects started.
+     * Use the same array and convert it to mutable as soon as it is modified
+     */
+    private static class UntouchedArrayData extends ContinuousArrayData {
+        private UntouchedArrayData() {
+            super(0);
+        }
+
+        private ArrayData toRealArrayData() {
+            return new IntArrayData(0);
+        }
+
+        private ArrayData toRealArrayData(final int index) {
+            final IntArrayData newData = new IntArrayData(index + 1);
+            return new DeletedRangeArrayFilter(newData, 0, index);
+        }
+
+        @Override
+        public ContinuousArrayData copy() {
+            assert length() == 0;
+            return this;
+        }
+
+        @Override
+        public Object asArrayOfType(final Class<?> componentType) {
+            return Array.newInstance(componentType, 0);
+        }
+
+        @Override
+        public Object[] asObjectArray() {
+            return ScriptRuntime.EMPTY_ARRAY;
+        }
+
+        @Override
+        public ArrayData ensure(final long safeIndex) {
+            assert safeIndex >= 0L;
+            if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH) {
+                return new SparseArrayData(this, safeIndex + 1);
+            }
+            //known to fit in int
+            return toRealArrayData((int)safeIndex);
+
+        }
+
+        @Override
+        public ArrayData convert(final Class<?> type) {
+            return toRealArrayData().convert(type);
+        }
+
+        @Override
+        public ArrayData delete(final int index) {
+            return new DeletedRangeArrayFilter(this, index, index);
+        }
+
+        @Override
+        public ArrayData delete(final long fromIndex, final long toIndex) {
+            return new DeletedRangeArrayFilter(this, fromIndex, toIndex);
+        }
+
+        @Override
+        public ArrayData shiftLeft(final int by) {
+            return this; //nop, always empty or we wouldn't be of this class
+        }
+
+        @Override
+        public ArrayData shiftRight(final int by) {
+            return this; //always empty or we wouldn't be of this class
+        }
+
+        @Override
+        public ArrayData shrink(final long newLength) {
+            return this;
+        }
+
+        @Override
+        public ArrayData set(final int index, final Object value, final boolean strict) {
+            return toRealArrayData(index).set(index, value, strict);
+        }
+
+        @Override
+        public ArrayData set(final int index, final int value, final boolean strict) {
+            return toRealArrayData(index).set(index, value, strict);
+        }
+
+        @Override
+        public ArrayData set(final int index, final double value, final boolean strict) {
+            return toRealArrayData(index).set(index, value, strict);
+        }
+
+        @Override
+        public int getInt(final int index) {
+            throw new ArrayIndexOutOfBoundsException(index); //empty
+        }
+
+        @Override
+        public double getDouble(final int index) {
+            throw new ArrayIndexOutOfBoundsException(index); //empty
+        }
+
+        @Override
+        public Object getObject(final int index) {
+            throw new ArrayIndexOutOfBoundsException(index); //empty
+        }
+
+        @Override
+        public boolean has(final int index) {
+            return false; //empty
+        }
+
+        @Override
+        public Object pop() {
+            return ScriptRuntime.UNDEFINED;
+        }
+
+        @Override
+        public ArrayData push(final boolean strict, final Object item) {
+            return toRealArrayData().push(strict, item);
+        }
+
+        @Override
+        public ArrayData slice(final long from, final long to) {
+            return this; //empty
+        }
+
+        @Override
+        public ContinuousArrayData fastConcat(final ContinuousArrayData otherData) {
+            return otherData.copy();
+        }
+
+        //no need to override fastPopInt, as the default behavior is to throw classcast exception so we
+        //can relink and return an undefined, this is the IntArrayData default behavior
+        @Override
+        public String toString() {
+            return getClass().getSimpleName();
+        }
+
+        @Override
+        public MethodHandle getElementGetter(final Class<?> returnType, final int programPoint) {
+            return null;
+        }
+
+        @Override
+        public MethodHandle getElementSetter(final Class<?> elementType) {
+            return null;
+        }
+
+        @Override
+        public Class<?> getElementType() {
+            return int.class;
+        }
+
+        @Override
+        public Class<?> getBoxedElementType() {
+            return Integer.class;
+        }
+    }
+
+    /**
+     * Constructor
+     * @param length Virtual length of the array.
+     */
+    protected ArrayData(final long length) {
+        this.length = length;
+    }
+
+    /**
+     * Factory method for unspecified array - start as int
+     * @return ArrayData
+     */
+    public static ArrayData initialArray() {
+        return new IntArrayData();
+    }
+
+    /**
+     * Unwarranted thrower
+     *
+     * @param data         array data
+     * @param programPoint program point
+     * @param index        array index
+     */
+    protected static void throwUnwarranted(final ArrayData data, final int programPoint, final int index) {
+        throw new UnwarrantedOptimismException(data.getObject(index), programPoint);
+    }
+
+    /**
+     * Align an array size up to the nearest array chunk size
+     * @param size size required
+     * @return size given, always &gt;= size
+     */
+    protected static int alignUp(final int size) {
+        return size + CHUNK_SIZE - 1 & ~(CHUNK_SIZE - 1);
+    }
+
+    /**
+     * Factory method for unspecified array with given length - start as int array data
+     *
+     * @param length the initial length
+     * @return ArrayData
+     */
+    public static ArrayData allocate(final long length) {
+        if (length == 0L) {
+            return new IntArrayData();
+        } else if (length >= SparseArrayData.MAX_DENSE_LENGTH) {
+            return new SparseArrayData(EMPTY_ARRAY, length);
+        } else {
+            return new DeletedRangeArrayFilter(new IntArrayData((int) length), 0, length - 1);
+        }
+    }
+
+    /**
+     * Factory method for unspecified given an array object
+     *
+     * @param  array the array
+     * @return ArrayData wrapping this array
+     */
+    public static ArrayData allocate(final Object array) {
+        final Class<?> clazz = array.getClass();
+
+        if (clazz == int[].class) {
+            return new IntArrayData((int[])array, ((int[])array).length);
+        } else if (clazz == double[].class) {
+            return new NumberArrayData((double[])array, ((double[])array).length);
+        } else {
+            return new ObjectArrayData((Object[])array, ((Object[])array).length);
+        }
+    }
+
+    /**
+     * Allocate an ArrayData wrapping a given array
+     *
+     * @param array the array to use for initial elements
+     * @return the ArrayData
+     */
+    public static ArrayData allocate(final int[] array) {
+         return new IntArrayData(array, array.length);
+    }
+
+    /**
+     * Allocate an ArrayData wrapping a given array
+     *
+     * @param array the array to use for initial elements
+     * @return the ArrayData
+     */
+    public static ArrayData allocate(final double[] array) {
+        return new NumberArrayData(array, array.length);
+    }
+
+    /**
+     * Allocate an ArrayData wrapping a given array
+     *
+     * @param array the array to use for initial elements
+     * @return the ArrayData
+     */
+    public static ArrayData allocate(final Object[] array) {
+        return new ObjectArrayData(array, array.length);
+    }
+
+    /**
+     * Allocate an ArrayData wrapping a given nio ByteBuffer
+     *
+     * @param buf the nio ByteBuffer to wrap
+     * @return the ArrayData
+     */
+    public static ArrayData allocate(final ByteBuffer buf) {
+        return new ByteBufferArrayData(buf);
+    }
+
+    /**
+     * Apply a freeze filter to an ArrayData.
+     *
+     * @param underlying  the underlying ArrayData to wrap in the freeze filter
+     * @return the frozen ArrayData
+     */
+    public static ArrayData freeze(final ArrayData underlying) {
+        return new FrozenArrayFilter(underlying);
+    }
+
+    /**
+     * Apply a seal filter to an ArrayData.
+     *
+     * @param underlying  the underlying ArrayData to wrap in the seal filter
+     * @return the sealed ArrayData
+     */
+    public static ArrayData seal(final ArrayData underlying) {
+        return new SealedArrayFilter(underlying);
+    }
+
+    /**
+     * Prevent this array from being extended
+     *
+     * @param  underlying the underlying ArrayData to wrap in the non extensible filter
+     * @return new array data, filtered
+     */
+    public static ArrayData preventExtension(final ArrayData underlying) {
+        return new NonExtensibleArrayFilter(underlying);
+    }
+
+    /**
+     * Prevent this array from having its length reset
+     *
+     * @param underlying the underlying ArrayDAta to wrap in the non extensible filter
+     * @return new array data, filtered
+     */
+    public static ArrayData setIsLengthNotWritable(final ArrayData underlying) {
+        return new LengthNotWritableFilter(underlying);
+    }
+
+    /**
+     * Return the length of the array data. This may differ from the actual
+     * length of the array this wraps as length may be set or gotten as any
+     * other JavaScript Property
+     *
+     * Even though a JavaScript array length may be a long, we only store
+     * int parts for the optimized array access. For long lengths there
+     * are special cases anyway.
+     *
+     * TODO: represent arrays with "long" lengths as a special ArrayData
+     * that basically maps to the ScriptObject directly for better abstraction
+     *
+     * @return the length of the data
+     */
+    public final long length() {
+        return length;
+    }
+
+    /**
+     * Return a copy of the array that can be modified without affecting this instance.
+     * It is safe to return themselves for immutable subclasses.
+     *
+     * @return a new array
+     */
+    public abstract ArrayData copy();
+
+    /**
+     * Return a copy of the array data as an Object array.
+     *
+     * @return an Object array
+     */
+    public abstract Object[] asObjectArray();
+
+    /**
+     * Return a copy of the array data as an array of the specified type.
+     *
+     * @param componentType  the type of elements in the array
+     * @return and array of the given type
+     */
+    public Object asArrayOfType(final Class<?> componentType) {
+        return JSType.convertArray(asObjectArray(), componentType);
+    }
+
+    /**
+     * Set the length of the data array
+     *
+     * @param length the new length for the data array
+     */
+    public void setLength(final long length) {
+        this.length = length;
+    }
+
+    /**
+     * Increase length by 1
+     * @return the new length, not the old one (i.e. pre-increment)
+     */
+    protected final long increaseLength() {
+        return ++this.length;
+    }
+
+    /**
+     * Decrease length by 1.
+     * @return the new length, not the old one (i.e. pre-decrement)
+     */
+    protected final long decreaseLength() {
+        return --this.length;
+    }
+
+    /**
+     * Shift the array data left
+     *
+     * TODO: This is used for Array.prototype.shift() which only shifts by 1,
+     * so we might consider dropping the offset parameter.
+     *
+     * @param by offset to shift
+     * @return New arraydata (or same)
+     */
+    public abstract ArrayData shiftLeft(final int by);
+
+    /**
+     * Shift the array right
+     *
+     * @param by offset to shift
+
+     * @return New arraydata (or same)
+     */
+    public abstract ArrayData shiftRight(final int by);
+
+    /**
+     * Ensure that the given index exists and won't fail in a subsequent access.
+     * If {@code safeIndex} is equal or greater than the current length the length is
+     * updated to {@code safeIndex + 1}.
+     *
+     * @param safeIndex the index to ensure wont go out of bounds
+     * @return new array data (or same)
+     */
+    public abstract ArrayData ensure(final long safeIndex);
+
+    /**
+     * Shrink the array to a new length, may or may not retain the
+     * inner array
+     *
+     * @param newLength new max length
+     *
+     * @return new array data (or same)
+     */
+    public abstract ArrayData shrink(final long newLength);
+
+    /**
+     * Set an object value at a given index
+     *
+     * @param index the index
+     * @param value the value
+     * @param strict are we in strict mode
+     * @return new array data (or same)
+     */
+    public abstract ArrayData set(final int index, final Object value, final boolean strict);
+
+    /**
+     * Set an int value at a given index
+     *
+     * @param index the index
+     * @param value the value
+     * @param strict are we in strict mode
+     * @return new array data (or same)
+     */
+    public abstract ArrayData set(final int index, final int value, final boolean strict);
+
+    /**
+     * Set an double value at a given index
+     *
+     * @param index the index
+     * @param value the value
+     * @param strict are we in strict mode
+     * @return new array data (or same)
+     */
+    public abstract ArrayData set(final int index, final double value, final boolean strict);
+
+    /**
+     * Set an empty value at a given index. Should only affect Object array.
+     *
+     * @param index the index
+     * @return new array data (or same)
+     */
+    public ArrayData setEmpty(final int index) {
+        // Do nothing.
+        return this;
+    }
+
+    /**
+     * Set an empty value for a given range. Should only affect Object array.
+     *
+     * @param lo range low end
+     * @param hi range high end
+     * @return new array data (or same)
+     */
+    public ArrayData setEmpty(final long lo, final long hi) {
+        // Do nothing.
+        return this;
+    }
+
+    /**
+     * Get an int value from a given index
+     *
+     * @param index the index
+     * @return the value
+     */
+    public abstract int getInt(final int index);
+
+    /**
+     * Returns the optimistic type of this array data. Basically, when an array data object needs to throw an
+     * {@link UnwarrantedOptimismException}, this type is used as the actual type of the return value.
+     * @return the optimistic type of this array data.
+     */
+    public Type getOptimisticType() {
+        return Type.OBJECT;
+    }
+
+    /**
+     * Get optimistic int - default is that it's impossible. Overridden
+     * by arrays that actually represents ints
+     *
+     * @param index        the index
+     * @param programPoint program point
+     * @return the value
+     */
+    public int getIntOptimistic(final int index, final int programPoint) {
+        throw new UnwarrantedOptimismException(getObject(index), programPoint, getOptimisticType());
+    }
+
+    /**
+     * Get a double value from a given index
+     *
+     * @param index the index
+     * @return the value
+     */
+    public abstract double getDouble(final int index);
+
+    /**
+     * Get optimistic double - default is that it's impossible. Overridden
+     * by arrays that actually represents doubles or narrower
+     *
+     * @param index        the index
+     * @param programPoint program point
+     * @return the value
+     */
+    public double getDoubleOptimistic(final int index, final int programPoint) {
+        throw new UnwarrantedOptimismException(getObject(index), programPoint, getOptimisticType());
+    }
+
+    /**
+     * Get an Object value from a given index
+     *
+     * @param index the index
+     * @return the value
+     */
+    public abstract Object getObject(final int index);
+
+    /**
+     * Tests to see if an entry exists (avoids boxing.)
+     * @param index the index
+     * @return true if entry exists
+     */
+    public abstract boolean has(final int index);
+
+    /**
+     * Returns if element at specific index can be deleted or not.
+     *
+     * @param index the index of the element
+     * @param strict are we in strict mode
+     *
+     * @return true if element can be deleted
+     */
+    public boolean canDelete(final int index, final boolean strict) {
+        return true;
+    }
+
+    /**
+     * Returns if element at specific index can be deleted or not.
+     *
+     * @param longIndex  the index
+     * @param strict     are we in strict mode
+     *
+     * @return true if range can be deleted
+     */
+    public boolean canDelete(final long longIndex, final boolean strict) {
+        return true;
+    }
+
+    /**
+     * Delete a range from the array if {@code fromIndex} is less than or equal to {@code toIndex}
+     * and the array supports deletion.
+     *
+     * @param fromIndex  the start index (inclusive)
+     * @param toIndex    the end index (inclusive)
+     * @param strict     are we in strict mode
+     * @return an array with the range deleted, or this array if no deletion took place
+     */
+    public final ArrayData safeDelete(final long fromIndex, final long toIndex, final boolean strict) {
+        if (fromIndex <= toIndex && canDelete(fromIndex, strict)) {
+            return delete(fromIndex, toIndex);
+        }
+        return this;
+    }
+
+    /**
+     * Returns property descriptor for element at a given index
+     *
+     * @param global the global object
+     * @param index  the index
+     *
+     * @return property descriptor for element
+     */
+    public PropertyDescriptor getDescriptor(final Global global, final int index) {
+        return global.newDataDescriptor(getObject(index), true, true, true);
+    }
+
+    /**
+     * Delete an array value at the given index, substituting
+     * for an undefined
+     *
+     * @param index the index
+     * @return new array data (or same)
+     */
+    public abstract ArrayData delete(final int index);
+
+    /**
+     * Delete a given range from this array;
+     *
+     * @param fromIndex  from index (inclusive)
+     * @param toIndex    to index (inclusive)
+     *
+     * @return new ArrayData after deletion
+     */
+    public abstract ArrayData delete(final long fromIndex, final long toIndex);
+
+    /**
+     * Convert the ArrayData to one with a different element type
+     * Currently Arrays are not collapsed to narrower types, just to
+     * wider ones. Attempting to narrow an array will assert
+     *
+     * @param type new element type
+     * @return new array data
+     */
+    public abstract ArrayData convert(final Class<?> type);
+
+    /**
+     * Push an array of items to the end of the array
+     *
+     * @param strict are we in strict mode
+     * @param items  the items
+     * @return new array data (or same)
+     */
+    public ArrayData push(final boolean strict, final Object... items) {
+        if (items.length == 0) {
+            return this;
+        }
+
+        final Class<?>  widest  = widestType(items);
+
+        ArrayData newData = convert(widest);
+        long      pos     = newData.length;
+        for (final Object item : items) {
+            newData = newData.ensure(pos); //avoid sparse array
+            newData.set((int)pos++, item, strict);
+        }
+        return newData;
+    }
+
+    /**
+     * Push an array of items to the end of the array
+     *
+     * @param strict are we in strict mode
+     * @param item   the item
+     * @return new array data (or same)
+     */
+    public ArrayData push(final boolean strict, final Object item) {
+        return push(strict, new Object[] { item });
+    }
+
+    /**
+     * Pop an element from the end of the array
+     *
+     * @return the popped element
+     */
+    public abstract Object pop();
+
+    /**
+     * Slice out a section of the array and return that
+     * subsection as a new array data: [from, to)
+     *
+     * @param from start index
+     * @param to   end index + 1
+     * @return new array data
+     */
+    public abstract ArrayData slice(final long from, final long to);
+
+    /**
+     * Fast splice operation. This just modifies the array according to the number of
+     * elements added and deleted but does not insert the added elements. Throws
+     * {@code UnsupportedOperationException} if fast splice operation is not supported
+     * for this class or arguments.
+     *
+     * @param start start index of splice operation
+     * @param removed number of removed elements
+     * @param added number of added elements
+     * @throws UnsupportedOperationException if fast splice is not supported for the class or arguments.
+     * @return new arraydata, but this never happens because we always throw an exception
+     */
+    public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException {
+        throw new UnsupportedOperationException();
+    }
+
+    static Class<?> widestType(final Object... items) {
+        assert items.length > 0;
+
+        Class<?> widest = Integer.class;
+
+        for (final Object item : items) {
+            if (item == null) {
+                return Object.class;
+            }
+            final Class<?> itemClass = item.getClass();
+            if (itemClass == Double.class || itemClass == Float.class || itemClass == Long.class) {
+                if (widest == Integer.class) {
+                    widest = Double.class;
+                }
+            } else if (itemClass != Integer.class && itemClass != Short.class && itemClass != Byte.class) {
+                return Object.class;
+            }
+        }
+
+        return widest;
+    }
+
+    /**
+     * Return a list of keys in the array for the iterators
+     * @return iterator key list
+     */
+    protected List<Long> computeIteratorKeys() {
+        final List<Long> keys = new ArrayList<>();
+
+        final long len = length();
+        for (long i = 0L; i < len; i = nextIndex(i)) {
+            if (has((int)i)) {
+                keys.add(i);
+            }
+        }
+
+        return keys;
+    }
+
+    /**
+     * Return an iterator that goes through all indexes of elements
+     * in this array. This includes those after array.length if
+     * they exist
+     *
+     * @return iterator
+     */
+    public Iterator<Long> indexIterator() {
+        return computeIteratorKeys().iterator();
+    }
+
+    /**
+     * Exponential growth function for array size when in
+     * need of resizing.
+     *
+     * @param size current size
+     * @return next size to allocate for internal array
+     */
+    public static int nextSize(final int size) {
+        return alignUp(size + 1) * 2;
+    }
+
+    /**
+     * Return the next valid index from a given one. Subclassed for various
+     * array representation
+     *
+     * @param index the current index
+     *
+     * @return the next index
+     */
+    long nextIndex(final long index) {
+        return index + 1;
+    }
+
+    static Object invoke(final MethodHandle mh, final Object arg) {
+        try {
+            return mh.invoke(arg);
+        } catch (final RuntimeException | Error e) {
+            throw e;
+        } catch (final Throwable t) {
+            throw new RuntimeException(t);
+        }
+    }
+
+   /**
+     * Find a fast call if one exists
+     *
+     * @param clazz    array data class
+     * @param desc     callsite descriptor
+     * @param request  link request
+     * @return fast property getter if one is found
+     */
+    public GuardedInvocation findFastCallMethod(final Class<? extends ArrayData> clazz, final CallSiteDescriptor desc, final LinkRequest request) {
+        return null;
+    }
+
+    /**
+     * Find a fast element getter if one exists
+     *
+     * @param clazz   array data class
+     * @param desc    callsite descriptor
+     * @param request link request
+     * @return fast index getter if one is found
+     */
+    public GuardedInvocation findFastGetIndexMethod(final Class<? extends ArrayData> clazz, final CallSiteDescriptor desc, final LinkRequest request) { // array, index, value
+        return null;
+    }
+
+    /**
+     * Find a fast element setter if one exists
+     *
+     * @param clazz   array data class
+     * @param desc    callsite descriptor
+     * @param request link request
+     * @return fast index getter if one is found
+     */
+    public GuardedInvocation findFastSetIndexMethod(final Class<? extends ArrayData> clazz, final CallSiteDescriptor desc, final LinkRequest request) { // array, index, value
+        return null;
+    }
+}