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 >= 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