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.specialCall;
29 
30 import java.lang.invoke.MethodHandle;
31 import java.lang.invoke.MethodHandles;
32 import java.util.Arrays;
33 import jdk.nashorn.internal.runtime.JSType;
34 import jdk.nashorn.internal.runtime.ScriptRuntime;
35 
36 /**
37  * Implementation of {@link ArrayData} as soon as an int has been
38  * written to the array. This is the default data for new arrays
39  */
40 final class IntArrayData extends ContinuousArrayData implements IntElements {
41     /**
42      * The wrapped array
43      */
44     private int[] array;
45 
IntArrayData()46     IntArrayData() {
47         this(new int[ArrayData.CHUNK_SIZE], 0);
48     }
49 
IntArrayData(final int length)50     IntArrayData(final int length) {
51         super(length);
52         this.array  = new int[ArrayData.nextSize(length)];
53     }
54 
55     /**
56      * Constructor
57      * @param array an int array
58      * @param length a length, not necessarily array.length
59      */
IntArrayData(final int[] array, final int length)60     IntArrayData(final int[] array, final int length) {
61         super(length);
62         assert array == null || array.length >= length;
63         this.array = array;
64     }
65 
66     @Override
getElementType()67     public final Class<?> getElementType() {
68         return int.class;
69     }
70 
71     @Override
getBoxedElementType()72     public final Class<?> getBoxedElementType() {
73         return Integer.class;
74     }
75 
76     @Override
getElementWeight()77     public final int getElementWeight() {
78         return 1;
79     }
80 
81     @Override
widest(final ContinuousArrayData otherData)82     public final ContinuousArrayData widest(final ContinuousArrayData otherData) {
83         return otherData;
84     }
85 
86     private static final MethodHandle HAS_GET_ELEM = specialCall(MethodHandles.lookup(), IntArrayData.class, "getElem", int.class, int.class).methodHandle();
87     private static final MethodHandle SET_ELEM     = specialCall(MethodHandles.lookup(), IntArrayData.class, "setElem", void.class, int.class, int.class).methodHandle();
88 
89     @Override
asObjectArray()90     public Object[] asObjectArray() {
91         return toObjectArray(true);
92     }
93 
94     @SuppressWarnings("unused")
getElem(final int index)95     private int getElem(final int index) {
96         if (has(index)) {
97             return array[index];
98         }
99         throw new ClassCastException();
100     }
101 
102     @SuppressWarnings("unused")
setElem(final int index, final int elem)103     private void setElem(final int index, final int elem) {
104         if (hasRoomFor(index)) {
105             array[index] = elem;
106             return;
107         }
108         throw new ClassCastException();
109     }
110 
111     @Override
getElementGetter(final Class<?> returnType, final int programPoint)112     public MethodHandle getElementGetter(final Class<?> returnType, final int programPoint) {
113         return getContinuousElementGetter(HAS_GET_ELEM, returnType, programPoint);
114     }
115 
116     @Override
getElementSetter(final Class<?> elementType)117     public MethodHandle getElementSetter(final Class<?> elementType) {
118         return elementType == int.class ? getContinuousElementSetter(SET_ELEM, elementType) : null;
119     }
120 
121     @Override
copy()122     public IntArrayData copy() {
123         return new IntArrayData(array.clone(), (int)length());
124     }
125 
126     @Override
asArrayOfType(final Class<?> componentType)127     public Object asArrayOfType(final Class<?> componentType) {
128         if (componentType == int.class) {
129             final int len = (int)length();
130             return array.length == len ? array.clone() : Arrays.copyOf(array, len);
131         }
132         return super.asArrayOfType(componentType);
133     }
134 
toObjectArray(final boolean trim)135     private Object[] toObjectArray(final boolean trim) {
136         assert length() <= array.length : "length exceeds internal array size";
137         final int len = (int)length();
138         final Object[] oarray = new Object[trim ? len : array.length];
139 
140         for (int index = 0; index < len; index++) {
141             oarray[index] = array[index];
142         }
143 
144         return oarray;
145     }
146 
toDoubleArray()147     private double[] toDoubleArray() {
148         assert length() <= array.length : "length exceeds internal array size";
149         final int len = (int)length();
150         final double[] darray = new double[array.length];
151 
152         for (int index = 0; index < len; index++) {
153             darray[index] = array[index];
154         }
155 
156         return darray;
157     }
158 
convertToDouble()159     private NumberArrayData convertToDouble() {
160         return new NumberArrayData(toDoubleArray(), (int)length());
161     }
162 
convertToObject()163     private ObjectArrayData convertToObject() {
164         return new ObjectArrayData(toObjectArray(false), (int)length());
165     }
166 
167     @Override
convert(final Class<?> type)168     public ArrayData convert(final Class<?> type) {
169         if (type == Integer.class || type == Byte.class || type == Short.class) {
170             return this;
171         } else if (type == Double.class || type == Float.class) {
172             return convertToDouble();
173         } else {
174             return convertToObject();
175         }
176     }
177 
178     @Override
shiftLeft(final int by)179     public ArrayData shiftLeft(final int by) {
180         if (by >= length()) {
181             shrink(0);
182         } else {
183             System.arraycopy(array, by, array, 0, array.length - by);
184         }
185         setLength(Math.max(0, length() - by));
186 
187         return this;
188     }
189 
190     @Override
shiftRight(final int by)191     public ArrayData shiftRight(final int by) {
192         final ArrayData newData = ensure(by + length() - 1);
193         if (newData != this) {
194             newData.shiftRight(by);
195             return newData;
196         }
197         System.arraycopy(array, 0, array, by, array.length - by);
198 
199         return this;
200     }
201 
202     @Override
ensure(final long safeIndex)203     public ArrayData ensure(final long safeIndex) {
204         if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH) {
205             return new SparseArrayData(this, safeIndex + 1);
206         }
207         final int alen = array.length;
208         if (safeIndex >= alen) {
209             final int newLength = ArrayData.nextSize((int)safeIndex);
210             array = Arrays.copyOf(array, newLength);
211         }
212         if (safeIndex >= length()) {
213             setLength(safeIndex + 1);
214         }
215         return this;
216     }
217 
218     @Override
shrink(final long newLength)219     public ArrayData shrink(final long newLength) {
220         Arrays.fill(array, (int)newLength, array.length, 0);
221         return this;
222     }
223 
224     @Override
set(final int index, final Object value, final boolean strict)225     public ArrayData set(final int index, final Object value, final boolean strict) {
226         if (JSType.isRepresentableAsInt(value)) {
227             return set(index, JSType.toInt32(value), strict);
228         } else if (value == ScriptRuntime.UNDEFINED) {
229             return new UndefinedArrayFilter(this).set(index, value, strict);
230         }
231 
232         final ArrayData newData = convert(value == null ? Object.class : value.getClass());
233         return newData.set(index, value, strict);
234     }
235 
236     @Override
set(final int index, final int value, final boolean strict)237     public ArrayData set(final int index, final int value, final boolean strict) {
238         array[index] = value;
239         setLength(Math.max(index + 1, length()));
240 
241         return this;
242     }
243 
244     @Override
set(final int index, final double value, final boolean strict)245     public ArrayData set(final int index, final double value, final boolean strict) {
246         if (JSType.isRepresentableAsInt(value)) {
247             array[index] = (int)(long)value;
248             setLength(Math.max(index + 1, length()));
249             return this;
250         }
251 
252         return convert(Double.class).set(index, value, strict);
253     }
254 
255     @Override
getInt(final int index)256     public int getInt(final int index) {
257         return array[index];
258     }
259 
260     @Override
getIntOptimistic(final int index, final int programPoint)261     public int getIntOptimistic(final int index, final int programPoint) {
262         return array[index];
263     }
264 
265     @Override
getDouble(final int index)266     public double getDouble(final int index) {
267         return array[index];
268     }
269 
270     @Override
getDoubleOptimistic(final int index, final int programPoint)271     public double getDoubleOptimistic(final int index, final int programPoint) {
272         return array[index];
273     }
274 
275     @Override
getObject(final int index)276     public Object getObject(final int index) {
277         return array[index];
278     }
279 
280     @Override
has(final int index)281     public boolean has(final int index) {
282         return 0 <= index && index < length();
283     }
284 
285     @Override
delete(final int index)286     public ArrayData delete(final int index) {
287         return new DeletedRangeArrayFilter(this, index, index);
288     }
289 
290     @Override
delete(final long fromIndex, final long toIndex)291     public ArrayData delete(final long fromIndex, final long toIndex) {
292         return new DeletedRangeArrayFilter(this, fromIndex, toIndex);
293     }
294 
295     @Override
pop()296     public Object pop() {
297         final int len = (int)length();
298         if (len == 0) {
299             return ScriptRuntime.UNDEFINED;
300         }
301 
302         final int newLength = len - 1;
303         final int elem = array[newLength];
304         array[newLength] = 0;
305         setLength(newLength);
306 
307         return elem;
308     }
309 
310     @Override
slice(final long from, final long to)311     public ArrayData slice(final long from, final long to) {
312         return new IntArrayData(Arrays.copyOfRange(array, (int)from, (int)to), (int)(to - (from < 0 ? from + length() : from)));
313     }
314 
315     @Override
fastSplice(final int start, final int removed, final int added)316     public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException {
317         final long oldLength = length();
318         final long newLength = oldLength - removed + added;
319         if (newLength > SparseArrayData.MAX_DENSE_LENGTH && newLength > array.length) {
320             throw new UnsupportedOperationException();
321         }
322         final ArrayData returnValue = removed == 0 ?
323                 EMPTY_ARRAY :
324                 new IntArrayData(
325                         Arrays.copyOfRange(
326                                 array,
327                                 start,
328                                 start + removed),
329                         removed);
330 
331         if (newLength != oldLength) {
332             final int[] newArray;
333 
334             if (newLength > array.length) {
335                 newArray = new int[ArrayData.nextSize((int)newLength)];
336                 System.arraycopy(array, 0, newArray, 0, start);
337             } else {
338                 newArray = array;
339             }
340 
341             System.arraycopy(array, start + removed, newArray, start + added, (int)(oldLength - start - removed));
342             array = newArray;
343             setLength(newLength);
344         }
345 
346         return returnValue;
347     }
348 
349     @Override
fastPush(final int arg)350     public double fastPush(final int arg) {
351         final int len = (int)length();
352         if (len == array.length) {
353             array = Arrays.copyOf(array, nextSize(len));
354         }
355         array[len] = arg;
356         return increaseLength();
357     }
358 
359     //length must not be zero
360     @Override
fastPopInt()361     public int fastPopInt() {
362         if (length() == 0) {
363             throw new ClassCastException(); //relink
364         }
365         final int newLength = (int)decreaseLength();
366         final int elem = array[newLength];
367         array[newLength] = 0;
368         return elem;
369     }
370 
371     @Override
fastPopDouble()372     public double fastPopDouble() {
373         return fastPopInt();
374     }
375 
376     @Override
fastPopObject()377     public Object fastPopObject() {
378         return fastPopInt();
379     }
380 
381     @Override
fastConcat(final ContinuousArrayData otherData)382     public ContinuousArrayData fastConcat(final ContinuousArrayData otherData) {
383         final int   otherLength = (int)otherData.length();
384         final int   thisLength  = (int)length();
385         assert otherLength > 0 && thisLength > 0;
386 
387         final int[] otherArray  = ((IntArrayData)otherData).array;
388         final int   newLength   = otherLength + thisLength;
389         final int[] newArray    = new int[ArrayData.alignUp(newLength)];
390 
391         System.arraycopy(array, 0, newArray, 0, thisLength);
392         System.arraycopy(otherArray, 0, newArray, thisLength, otherLength);
393 
394         return new IntArrayData(newArray, newLength);
395     }
396 
397     @Override
toString()398     public String toString() {
399         assert length() <= array.length : length() + " > " + array.length;
400         return getClass().getSimpleName() + ':' + Arrays.toString(Arrays.copyOf(array, (int)length()));
401     }
402 }
403