ArrayData.java revision 1073:06c06c8443fd
1234353Sdim/*
2218885Sdim * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3218885Sdim * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4218885Sdim *
5218885Sdim * This code is free software; you can redistribute it and/or modify it
6218885Sdim * under the terms of the GNU General Public License version 2 only, as
7218885Sdim * published by the Free Software Foundation.  Oracle designates this
8218885Sdim * particular file as subject to the "Classpath" exception as provided
9218885Sdim * by Oracle in the LICENSE file that accompanied this code.
10218885Sdim *
11218885Sdim * This code is distributed in the hope that it will be useful, but WITHOUT
12218885Sdim * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13218885Sdim * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14218885Sdim * version 2 for more details (a copy is included in the LICENSE file that
15218885Sdim * accompanied this code).
16218885Sdim *
17218885Sdim * You should have received a copy of the GNU General Public License version
18218885Sdim * 2 along with this work; if not, write to the Free Software Foundation,
19218885Sdim * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20218885Sdim *
21218885Sdim * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22249423Sdim * or visit www.oracle.com if you need additional information or have any
23249423Sdim * questions.
24249423Sdim */
25218885Sdim
26218885Sdimpackage jdk.nashorn.internal.runtime.arrays;
27218885Sdim
28218885Sdimimport static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
29218885Sdimimport java.lang.invoke.MethodHandle;
30218885Sdimimport java.lang.invoke.MethodHandles;
31218885Sdimimport java.lang.reflect.Array;
32234353Sdimimport java.nio.ByteBuffer;
33218885Sdimimport jdk.internal.dynalink.CallSiteDescriptor;
34218885Sdimimport jdk.internal.dynalink.linker.GuardedInvocation;
35218885Sdimimport jdk.internal.dynalink.linker.LinkRequest;
36218885Sdimimport jdk.nashorn.internal.codegen.CompilerConstants;
37218885Sdimimport jdk.nashorn.internal.codegen.types.Type;
38218885Sdimimport jdk.nashorn.internal.objects.Global;
39218885Sdimimport jdk.nashorn.internal.runtime.JSType;
40218885Sdimimport jdk.nashorn.internal.runtime.PropertyDescriptor;
41218885Sdimimport jdk.nashorn.internal.runtime.ScriptRuntime;
42218885Sdimimport jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
43218885Sdim
44218885Sdim/**
45218885Sdim * ArrayData - abstraction for wrapping array elements
46218885Sdim */
47218885Sdimpublic abstract class ArrayData {
48218885Sdim    /** Minimum chunk size for underlying arrays */
49218885Sdim    protected static final int CHUNK_SIZE = 32;
50218885Sdim
51218885Sdim    /** Mask for getting a chunk */
52218885Sdim    protected static final int CHUNK_MASK = CHUNK_SIZE - 1;
53218885Sdim
54218885Sdim    /** Untouched data - still link callsites as IntArrayData, but expands to
55218885Sdim     *  a proper ArrayData when we try to write to it */
56218885Sdim    public static final ArrayData EMPTY_ARRAY = new UntouchedArrayData();
57218885Sdim
58218885Sdim    /**
59218885Sdim     * Immutable empty array to get ScriptObjects started.
60218885Sdim     * Use the same array and convert it to mutable as soon as it is modified
61218885Sdim     */
62218885Sdim    private static class UntouchedArrayData extends ContinuousArrayData {
63218885Sdim        private UntouchedArrayData() {
64218885Sdim            this(0);
65218885Sdim        }
66218885Sdim
67218885Sdim        private UntouchedArrayData(final int length) {
68218885Sdim            super(length);
69218885Sdim        }
70218885Sdim
71218885Sdim        private ArrayData toRealArrayData() {
72218885Sdim            return toRealArrayData(0);
73218885Sdim        }
74218885Sdim
75218885Sdim        private ArrayData toRealArrayData(final int index) {
76218885Sdim            final IntArrayData newData = new IntArrayData(index + 1);
77218885Sdim            if (index == 0) {
78218885Sdim                return newData;
79218885Sdim            }
80218885Sdim            return new DeletedRangeArrayFilter(newData, 0, index);
81218885Sdim        }
82218885Sdim
83218885Sdim        @Override
84218885Sdim        public ContinuousArrayData copy() {
85218885Sdim            return new UntouchedArrayData((int)length);
86218885Sdim        }
87218885Sdim
88218885Sdim        @Override
89218885Sdim        public Object asArrayOfType(final Class<?> componentType) {
90218885Sdim            return Array.newInstance(componentType, 0);
91218885Sdim        }
92218885Sdim
93218885Sdim        @Override
94218885Sdim        public Object[] asObjectArray() {
95218885Sdim            return ScriptRuntime.EMPTY_ARRAY;
96218885Sdim        }
97218885Sdim
98218885Sdim        @Override
99218885Sdim        public ArrayData ensure(final long safeIndex) {
100218885Sdim            if (safeIndex > 0L) {
101218885Sdim                return toRealArrayData((int)safeIndex).ensure(safeIndex);
102218885Sdim           }
103218885Sdim           return this;
104218885Sdim        }
105218885Sdim
106218885Sdim        @Override
107218885Sdim        public ArrayData convert(final Class<?> type) {
108218885Sdim            return toRealArrayData(0).convert(type);
109218885Sdim        }
110218885Sdim
111218885Sdim        @Override
112218885Sdim        public void shiftLeft(final int by) {
113218885Sdim            //nop, always empty or we wouldn't be of this class
114218885Sdim        }
115218885Sdim
116218885Sdim        @Override
117218885Sdim        public ArrayData shiftRight(final int by) {
118218885Sdim            return this; //always empty or we wouldn't be of this class
119218885Sdim        }
120218885Sdim
121218885Sdim        @Override
122218885Sdim        public ArrayData shrink(final long newLength) {
123218885Sdim            return this;
124218885Sdim        }
125218885Sdim
126218885Sdim        @Override
127218885Sdim        public ArrayData set(final int index, final Object value, final boolean strict) {
128218885Sdim            return toRealArrayData(index).set(index, value, strict);
129218885Sdim        }
130218885Sdim
131218885Sdim        @Override
132218885Sdim        public ArrayData set(final int index, final int value, final boolean strict) {
133218885Sdim            return toRealArrayData(index).set(index, value, strict);
134218885Sdim        }
135218885Sdim
136218885Sdim        @Override
137218885Sdim        public ArrayData set(final int index, final long value, final boolean strict) {
138218885Sdim            return toRealArrayData(index).set(index, value, strict);
139218885Sdim        }
140218885Sdim
141218885Sdim        @Override
142218885Sdim        public ArrayData set(final int index, final double value, final boolean strict) {
143234353Sdim            return toRealArrayData(index).set(index, value, strict);
144218885Sdim        }
145218885Sdim
146218885Sdim        @Override
147218885Sdim        public int getInt(final int index) {
148218885Sdim            throw new ArrayIndexOutOfBoundsException(index); //empty
149218885Sdim        }
150218885Sdim
151218885Sdim        @Override
152218885Sdim        public long getLong(final int index) {
153218885Sdim            throw new ArrayIndexOutOfBoundsException(index); //empty
154218885Sdim        }
155218885Sdim
156218885Sdim        @Override
157218885Sdim        public double getDouble(final int index) {
158218885Sdim            throw new ArrayIndexOutOfBoundsException(index); //empty
159218885Sdim        }
160218885Sdim
161218885Sdim        @Override
162218885Sdim        public Object getObject(final int index) {
163218885Sdim            throw new ArrayIndexOutOfBoundsException(index); //empty
164218885Sdim        }
165218885Sdim
166218885Sdim        @Override
167218885Sdim        public boolean has(final int index) {
168218885Sdim            return false; //empty
169218885Sdim        }
170218885Sdim
171218885Sdim        @Override
172218885Sdim        public ArrayData delete(final int index) {
173218885Sdim            return new DeletedRangeArrayFilter(this, index, index);
174218885Sdim        }
175218885Sdim
176218885Sdim        @Override
177218885Sdim        public ArrayData delete(final long fromIndex, final long toIndex) {
178218885Sdim            return new DeletedRangeArrayFilter(this, fromIndex, toIndex);
179218885Sdim        }
180218885Sdim
181218885Sdim        @Override
182218885Sdim        public Object pop() {
183218885Sdim            return ScriptRuntime.UNDEFINED;
184218885Sdim        }
185218885Sdim
186218885Sdim        @Override
187218885Sdim        public ArrayData push(final boolean strict, final Object item) {
188218885Sdim            return toRealArrayData().push(strict, item);
189218885Sdim        }
190218885Sdim
191218885Sdim        @Override
192218885Sdim        public ArrayData slice(final long from, final long to) {
193218885Sdim            return this; //empty
194218885Sdim        }
195218885Sdim
196218885Sdim        @Override
197218885Sdim        public ContinuousArrayData fastConcat(final ContinuousArrayData otherData) {
198218885Sdim            return otherData.copy();
199218885Sdim        }
200218885Sdim
201218885Sdim        //no need to override fastPopInt, as the default behavior is to throw classcast exception so we
202218885Sdim        //can relink and return an undefined, this is the IntArrayData default behavior
203218885Sdim        @Override
204218885Sdim        public String toString() {
205218885Sdim            return getClass().getSimpleName();
206218885Sdim        }
207218885Sdim
208218885Sdim        @Override
209218885Sdim        public MethodHandle getElementGetter(final Class<?> returnType, final int programPoint) {
210218885Sdim            return null;
211218885Sdim        }
212218885Sdim
213218885Sdim        @Override
214218885Sdim        public MethodHandle getElementSetter(final Class<?> elementType) {
215218885Sdim            return null;
216218885Sdim        }
217218885Sdim
218218885Sdim        @Override
219218885Sdim        public Class<?> getElementType() {
220218885Sdim            return int.class;
221218885Sdim        }
222218885Sdim
223218885Sdim        @Override
224243830Sdim        public Class<?> getBoxedElementType() {
225249423Sdim            return Integer.class;
226249423Sdim        }
227249423Sdim    };
228249423Sdim
229249423Sdim    /**
230249423Sdim     * Length of the array data. Not necessarily length of the wrapped array.
231249423Sdim     */
232249423Sdim    protected long length;
233249423Sdim
234249423Sdim    /**
235249423Sdim     * Method handle to throw an {@link UnwarrantedOptimismException} when getting an element
236249423Sdim     * of the wrong type
237249423Sdim     */
238249423Sdim    protected static final CompilerConstants.Call THROW_UNWARRANTED = staticCall(MethodHandles.lookup(), ArrayData.class, "throwUnwarranted", void.class, ArrayData.class, int.class, int.class);
239249423Sdim
240249423Sdim    /**
241249423Sdim     * Constructor
242249423Sdim     * @param length Virtual length of the array.
243249423Sdim     */
244249423Sdim    protected ArrayData(final long length) {
245249423Sdim        this.length = length;
246249423Sdim    }
247249423Sdim
248249423Sdim    /**
249249423Sdim     * Factory method for unspecified array - start as int
250249423Sdim     * @return ArrayData
251249423Sdim     */
252249423Sdim    public final static ArrayData initialArray() {
253249423Sdim        return new IntArrayData();
254249423Sdim    }
255249423Sdim
256249423Sdim    /**
257249423Sdim     * Unwarranted thrower
258249423Sdim     *
259249423Sdim     * @param data         array data
260249423Sdim     * @param programPoint program point
261249423Sdim     * @param index        array index
262249423Sdim     */
263249423Sdim    protected static void throwUnwarranted(final ArrayData data, final int programPoint, final int index) {
264249423Sdim        throw new UnwarrantedOptimismException(data.getObject(index), programPoint);
265249423Sdim    }
266249423Sdim
267249423Sdim    /**
268249423Sdim     * Align an array size up to the nearest array chunk size
269249423Sdim     * @param size size required
270249423Sdim     * @return size given, always >= size
271249423Sdim     */
272249423Sdim    protected final static int alignUp(final int size) {
273249423Sdim        return size + CHUNK_SIZE - 1 & ~(CHUNK_SIZE - 1);
274249423Sdim    }
275249423Sdim
276249423Sdim    /**
277249423Sdim     * Factory method for unspecified array with given length - start as int array data
278249423Sdim     *
279249423Sdim     * @param length the initial length
280249423Sdim     * @return ArrayData
281249423Sdim     */
282249423Sdim    public static final ArrayData allocate(final int length) {
283249423Sdim        if (length == 0) {
284249423Sdim            return new IntArrayData();
285249423Sdim        } else if (length >= SparseArrayData.MAX_DENSE_LENGTH) {
286249423Sdim            return new SparseArrayData(EMPTY_ARRAY, length);
287243830Sdim        } else {
288249423Sdim            return new DeletedRangeArrayFilter(new IntArrayData(length), 0, length - 1);
289249423Sdim        }
290243830Sdim    }
291249423Sdim
292243830Sdim    /**
293243830Sdim     * Factory method for unspecified given an array object
294243830Sdim     *
295243830Sdim     * @param  array the array
296243830Sdim     * @return ArrayData wrapping this array
297243830Sdim     */
298    public static final ArrayData allocate(final Object array) {
299        final Class<?> clazz = array.getClass();
300
301        if (clazz == int[].class) {
302            return new IntArrayData((int[])array, ((int[])array).length);
303        } else if (clazz == long[].class) {
304            return new LongArrayData((long[])array, ((long[])array).length);
305        } else if (clazz == double[].class) {
306            return new NumberArrayData((double[])array, ((double[])array).length);
307        } else {
308            return new ObjectArrayData((Object[])array, ((Object[])array).length);
309        }
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 final ArrayData allocate(final int[] array) {
319         return new IntArrayData(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 final ArrayData allocate(final long[] array) {
329        return new LongArrayData(array, array.length);
330    }
331
332    /**
333     * Allocate an ArrayData wrapping a given array
334     *
335     * @param array the array to use for initial elements
336     * @return the ArrayData
337     */
338    public static final ArrayData allocate(final double[] array) {
339        return new NumberArrayData(array, array.length);
340    }
341
342    /**
343     * Allocate an ArrayData wrapping a given array
344     *
345     * @param array the array to use for initial elements
346     * @return the ArrayData
347     */
348    public static final ArrayData allocate(final Object[] array) {
349        return new ObjectArrayData(array, array.length);
350    }
351
352    /**
353     * Allocate an ArrayData wrapping a given nio ByteBuffer
354     *
355     * @param buf the nio ByteBuffer to wrap
356     * @return the ArrayData
357     */
358    public static final ArrayData allocate(final ByteBuffer buf) {
359        return new ByteBufferArrayData(buf);
360    }
361
362    /**
363     * Apply a freeze filter to an ArrayData.
364     *
365     * @param underlying  the underlying ArrayData to wrap in the freeze filter
366     * @return the frozen ArrayData
367     */
368    public static final ArrayData freeze(final ArrayData underlying) {
369        return new FrozenArrayFilter(underlying);
370    }
371
372    /**
373     * Apply a seal filter to an ArrayData.
374     *
375     * @param underlying  the underlying ArrayData to wrap in the seal filter
376     * @return the sealed ArrayData
377     */
378    public static final ArrayData seal(final ArrayData underlying) {
379        return new SealedArrayFilter(underlying);
380    }
381
382    /**
383     * Prevent this array from being extended
384     *
385     * @param  underlying the underlying ArrayData to wrap in the non extensible filter
386     * @return new array data, filtered
387     */
388    public static final ArrayData preventExtension(final ArrayData underlying) {
389        return new NonExtensibleArrayFilter(underlying);
390    }
391
392    /**
393     * Return the length of the array data. This may differ from the actual
394     * length of the array this wraps as length may be set or gotten as any
395     * other JavaScript Property
396     *
397     * Even though a JavaScript array length may be a long, we only store
398     * int parts for the optimized array access. For long lengths there
399     * are special cases anyway.
400     *
401     * TODO: represent arrays with "long" lengths as a special ArrayData
402     * that basically maps to the ScriptObject directly for better abstraction
403     *
404     * @return the length of the data
405     */
406    public final long length() {
407        return length;
408    }
409
410    /**
411     * Return a copy of the array that can be modified without affecting this instance.
412     * It is safe to return themselves for immutable subclasses.
413     *
414     * @return a new array
415     */
416    public abstract ArrayData copy();
417
418    /**
419     * Return a copy of the array data as an Object array.
420     *
421     * @return an Object array
422     */
423    public abstract Object[] asObjectArray();
424
425    /**
426     * Return a copy of the array data as an array of the specified type.
427     *
428     * @param componentType  the type of elements in the array
429     * @return and array of the given type
430     */
431    public Object asArrayOfType(final Class<?> componentType) {
432        return JSType.convertArray(asObjectArray(), componentType);
433    }
434
435    /**
436     * Set the length of the data array
437     *
438     * @param length the new length for the data array
439     */
440    public void setLength(final long length) {
441        this.length = length;
442    }
443
444    /**
445     * Shift the array data left
446     *
447     * TODO: explore start at an index and not at zero, to make these operations
448     * even faster. Offset everything from the index. Costs memory but is probably
449     * worth it
450     *
451     * @param by offset to shift
452     */
453    public abstract void shiftLeft(int by);
454
455    /**
456     * Shift the array right
457     *
458     * @param by offset to shift
459
460     * @return New arraydata (or same)
461     */
462    public abstract ArrayData shiftRight(int by);
463
464    /**
465     * Ensure that the given index exists and won't fail subsequent
466     *
467     * @param safeIndex the index to ensure wont go out of bounds
468     * @return new array data (or same)
469     */
470    public abstract ArrayData ensure(long safeIndex);
471
472    /**
473     * Shrink the array to a new length, may or may not retain the
474     * inner array
475     *
476     * @param newLength new max length
477     *
478     * @return new array data (or same)
479     */
480    public abstract ArrayData shrink(long newLength);
481
482    /**
483     * Set an object value at a given index
484     *
485     * @param index the index
486     * @param value the value
487     * @param strict are we in strict mode
488     * @return new array data (or same)
489     */
490    public abstract ArrayData set(int index, Object value, boolean strict);
491
492    /**
493     * Set an int value at a given index
494     *
495     * @param index the index
496     * @param value the value
497     * @param strict are we in strict mode
498     * @return new array data (or same)
499     */
500    public abstract ArrayData set(int index, int value, boolean strict);
501
502    /**
503     * Set a long value at a given index
504     *
505     * @param index the index
506     * @param value the value
507     * @param strict are we in strict mode
508     * @return new array data (or same)
509     */
510    public abstract ArrayData set(int index, long value, boolean strict);
511
512    /**
513     * Set an double value at a given index
514     *
515     * @param index the index
516     * @param value the value
517     * @param strict are we in strict mode
518     * @return new array data (or same)
519     */
520    public abstract ArrayData set(int index, double value, boolean strict);
521
522    /**
523     * Set an empty value at a given index. Should only affect Object array.
524     *
525     * @param index the index
526     * @return new array data (or same)
527     */
528    public ArrayData setEmpty(final int index) {
529        // Do nothing.
530        return this;
531    }
532
533    /**
534     * Set an empty value for a given range. Should only affect Object array.
535     *
536     * @param lo range low end
537     * @param hi range high end
538     * @return new array data (or same)
539     */
540    public ArrayData setEmpty(final long lo, final long hi) {
541        // Do nothing.
542        return this;
543    }
544
545    /**
546     * Get an int value from a given index
547     *
548     * @param index the index
549     * @return the value
550     */
551    public abstract int getInt(int index);
552
553    /**
554     * Returns the optimistic type of this array data. Basically, when an array data object needs to throw an
555     * {@link UnwarrantedOptimismException}, this type is used as the actual type of the return value.
556     * @return the optimistic type of this array data.
557     */
558    public Type getOptimisticType() {
559        return Type.OBJECT;
560    }
561
562    /**
563     * Get optimistic int - default is that it's impossible. Overridden
564     * by arrays that actually represents ints
565     *
566     * @param index        the index
567     * @param programPoint program point
568     * @return the value
569     */
570    public int getIntOptimistic(final int index, final int programPoint) {
571        throw new UnwarrantedOptimismException(getObject(index), programPoint, getOptimisticType());
572    }
573
574    /**
575     * Get a long value from a given index
576     *
577     * @param index the index
578     * @return the value
579     */
580    public abstract long getLong(int index);
581
582    /**
583     * Get optimistic long - default is that it's impossible. Overridden
584     * by arrays that actually represents longs or narrower
585     *
586     * @param index        the index
587     * @param programPoint program point
588     * @return the value
589     */
590    public long getLongOptimistic(final int index, final int programPoint) {
591        throw new UnwarrantedOptimismException(getObject(index), programPoint, getOptimisticType());
592    }
593
594    /**
595     * Get a double value from a given index
596     *
597     * @param index the index
598     * @return the value
599     */
600    public abstract double getDouble(int index);
601
602    /**
603     * Get optimistic double - default is that it's impossible. Overridden
604     * by arrays that actually represents doubles or narrower
605     *
606     * @param index        the index
607     * @param programPoint program point
608     * @return the value
609     */
610    public double getDoubleOptimistic(final int index, final int programPoint) {
611        throw new UnwarrantedOptimismException(getObject(index), programPoint, getOptimisticType());
612    }
613
614    /**
615     * Get an Object value from a given index
616     *
617     * @param index the index
618     * @return the value
619     */
620    public abstract Object getObject(int index);
621
622    /**
623     * Tests to see if an entry exists (avoids boxing.)
624     * @param index the index
625     * @return true if entry exists
626     */
627    public abstract boolean has(int index);
628
629    /**
630     * Returns if element at specific index can be deleted or not.
631     *
632     * @param index the index of the element
633     * @param strict are we in strict mode
634     *
635     * @return true if element can be deleted
636     */
637    public boolean canDelete(final int index, final boolean strict) {
638        return true;
639    }
640
641    /**
642     * Returns if element at specific index range can be deleted or not.
643     *
644     * @param fromIndex  the start index
645     * @param toIndex    the end index
646     * @param strict     are we in strict mode
647     *
648     * @return true if range can be deleted
649     */
650    public boolean canDelete(final long fromIndex, final long toIndex, final boolean strict) {
651        return true;
652    }
653
654    /**
655     * Returns property descriptor for element at a given index
656     *
657     * @param global the global object
658     * @param index  the index
659     *
660     * @return property descriptor for element
661     */
662    public PropertyDescriptor getDescriptor(final Global global, final int index) {
663        return global.newDataDescriptor(getObject(index), true, true, true);
664    }
665
666    /**
667     * Delete an array value at the given index, substituting
668     * for an undefined
669     *
670     * @param index the index
671     * @return new array data (or same)
672     */
673    public abstract ArrayData delete(int index);
674
675    /**
676     * Delete a given range from this array;
677     *
678     * @param fromIndex  from index (inclusive)
679     * @param toIndex    to index (inclusive)
680     *
681     * @return new ArrayData after deletion
682     */
683    public abstract ArrayData delete(long fromIndex, long toIndex);
684
685    /**
686     * Convert the ArrayData to one with a different element type
687     * Currently Arrays are not collapsed to narrower types, just to
688     * wider ones. Attempting to narrow an array will assert
689     *
690     * @param type new element type
691     * @return new array data
692     */
693    public abstract ArrayData convert(Class<?> type);
694
695    /**
696     * Push an array of items to the end of the array
697     *
698     * @param strict are we in strict mode
699     * @param items  the items
700     * @return new array data (or same)
701     */
702    public ArrayData push(final boolean strict, final Object... items) {
703        if (items.length == 0) {
704            return this;
705        }
706
707        final Class<?>  widest  = widestType(items);
708
709        ArrayData newData = convert(widest);
710        long      pos     = newData.length;
711        for (final Object item : items) {
712            newData = newData.ensure(pos); //avoid sparse array
713            newData.set((int)pos++, item, strict);
714        }
715        return newData;
716    }
717
718    /**
719     * Push an array of items to the end of the array
720     *
721     * @param strict are we in strict mode
722     * @param item   the item
723     * @return new array data (or same)
724     */
725    public ArrayData push(final boolean strict, final Object item) {
726        return push(strict, new Object[] { item });
727    }
728
729    /**
730     * Push an array of items to the end of the array
731     *
732     * @param strict are we in strict mode
733     * @param item   the item
734     * @return new array data (or same)
735     */
736    public ArrayData push(final boolean strict, final double item) {
737        return push(strict, item);
738    }
739
740    /**
741     * Push an array of items to the end of the array
742     *
743     * @param strict are we in strict mode
744     * @param item   the item
745     * @return new array data (or same)
746     */
747    public ArrayData push(final boolean strict, final long item) {
748        return push(strict, item);
749    }
750
751    /**
752     * Push an array of items to the end of the array
753     *
754     * @param strict are we in strict mode
755     * @param item   the item
756     * @return new array data (or same)
757     */
758    public ArrayData push(final boolean strict, final int item) {
759        return push(strict, item);
760    }
761
762    /**
763     * Pop an element from the end of the array
764     *
765     * @return the popped element
766     */
767    public abstract Object pop();
768
769    /**
770     * Slice out a section of the array and return that
771     * subsection as a new array data: [from, to)
772     *
773     * @param from start index
774     * @param to   end index + 1
775     * @return new array data
776     */
777    public abstract ArrayData slice(long from, long to);
778
779    /**
780     * Fast splice operation. This just modifies the array according to the number of
781     * elements added and deleted but does not insert the added elements. Throws
782     * {@code UnsupportedOperationException} if fast splice operation is not supported
783     * for this class or arguments.
784     *
785     * @param start start index of splice operation
786     * @param removed number of removed elements
787     * @param added number of added elements
788     * @throws UnsupportedOperationException if fast splice is not supported for the class or arguments.
789     * @return new arraydata, but this never happens because we always throw an exception
790     */
791    public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException {
792        throw new UnsupportedOperationException();
793    }
794
795    static Class<?> widestType(final Object... items) {
796        assert items.length > 0;
797
798        Class<?> widest = Integer.class;
799
800        for (final Object item : items) {
801            if (item == null) {
802                return Object.class;
803            }
804            final Class<?> itemClass = item.getClass();
805            if (itemClass == Long.class) {
806                if (widest == Integer.class) {
807                    widest = Long.class;
808                }
809            } else if (itemClass == Double.class || itemClass == Float.class) {
810                if (widest == Integer.class || widest == Long.class) {
811                    widest = Double.class;
812                }
813            } else if (itemClass != Integer.class && itemClass != Short.class && itemClass != Byte.class) {
814                return Object.class;
815            }
816        }
817
818        return widest;
819    }
820
821    /**
822     * Exponential growth function for array size when in
823     * need of resizing.
824     *
825     * @param size current size
826     * @return next size to allocate for internal array
827     */
828    public static int nextSize(final int size) {
829        return alignUp(size + 1) * 2;
830    }
831
832    /**
833     * Return the next valid index from a given one. Subclassed for various
834     * array representation
835     *
836     * @param index the current index
837     *
838     * @return the next index
839     */
840    public long nextIndex(final long index) {
841        return index + 1;
842    }
843
844    static Object invoke(final MethodHandle mh, final Object arg) {
845        try {
846            return mh.invoke(arg);
847        } catch (final RuntimeException | Error e) {
848            throw e;
849        } catch (final Throwable t) {
850            throw new RuntimeException(t);
851        }
852    }
853
854   /**
855     * Find a fast call if one exists
856     *
857     * @param clazz    array data class
858     * @param desc     callsite descriptor
859     * @param request  link request
860     * @return fast property getter if one is found
861     */
862    public GuardedInvocation findFastCallMethod(final Class<? extends ArrayData> clazz, final CallSiteDescriptor desc, final LinkRequest request) {
863        return null;
864    }
865
866    /**
867     * Find a fast property getter if one exists
868     *
869     * @param clazz    array data class
870     * @param desc     callsite descriptor
871     * @param request  link request
872     * @param operator operator
873     * @return fast property getter if one is found
874     */
875    public GuardedInvocation findFastGetMethod(final Class<? extends ArrayData> clazz, final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
876        return null;
877    }
878
879    /**
880     * Find a fast element getter if one exists
881     *
882     * @param clazz   array data class
883     * @param desc    callsite descriptor
884     * @param request link request
885     * @return fast index getter if one is found
886     */
887    public GuardedInvocation findFastGetIndexMethod(final Class<? extends ArrayData> clazz, final CallSiteDescriptor desc, final LinkRequest request) { // array, index, value
888        return null;
889    }
890
891    /**
892     * Find a fast element setter if one exists
893     *
894     * @param clazz   array data class
895     * @param desc    callsite descriptor
896     * @param request link request
897     * @return fast index getter if one is found
898     */
899    public GuardedInvocation findFastSetIndexMethod(final Class<? extends ArrayData> clazz, final CallSiteDescriptor desc, final LinkRequest request) { // array, index, value
900        return null;
901    }
902}
903