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 26 package jdk.nashorn.internal.runtime.arrays; 27 28 import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall; 29 30 import java.lang.invoke.MethodHandle; 31 import java.lang.invoke.MethodHandles; 32 import java.lang.reflect.Array; 33 import java.nio.ByteBuffer; 34 import java.util.ArrayList; 35 import java.util.Iterator; 36 import java.util.List; 37 import jdk.dynalink.CallSiteDescriptor; 38 import jdk.dynalink.linker.GuardedInvocation; 39 import jdk.dynalink.linker.LinkRequest; 40 import jdk.nashorn.internal.codegen.CompilerConstants; 41 import jdk.nashorn.internal.codegen.types.Type; 42 import jdk.nashorn.internal.objects.Global; 43 import jdk.nashorn.internal.runtime.JSType; 44 import jdk.nashorn.internal.runtime.PropertyDescriptor; 45 import jdk.nashorn.internal.runtime.ScriptRuntime; 46 import jdk.nashorn.internal.runtime.UnwarrantedOptimismException; 47 48 /** 49 * ArrayData - abstraction for wrapping array elements 50 */ 51 public 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 >= 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 }