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