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 
  46     IntArrayData() {
  47         this(new int[ArrayData.CHUNK_SIZE], 0);
  48     }
  49 
  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      */
  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
  67     public final Class<?> getElementType() {
  68         return int.class;
  69     }
  70 
  71     @Override
  72     public final Class<?> getBoxedElementType() {
  73         return Integer.class;
  74     }
  75 
  76     @Override
  77     public final int getElementWeight() {
  78         return 1;
  79     }
  80 
  81     @Override
  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
  90     public Object[] asObjectArray() {
  91         return toObjectArray(true);
  92     }
  93 
  94     @SuppressWarnings("unused")
  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")
 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
 112     public MethodHandle getElementGetter(final Class<?> returnType, final int programPoint) {
 113         return getContinuousElementGetter(HAS_GET_ELEM, returnType, programPoint);
 114     }
 115 
 116     @Override
 117     public MethodHandle getElementSetter(final Class<?> elementType) {
 118         return elementType == int.class ? getContinuousElementSetter(SET_ELEM, elementType) : null;
 119     }
 120 
 121     @Override
 122     public IntArrayData copy() {
 123         return new IntArrayData(array.clone(), (int)length());
 124     }
 125 
 126     @Override
 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 
 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 
 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 
 159     private long[] toLongArray() {
 160         assert length() <= array.length : "length exceeds internal array size";
 161         final int len = (int)length();
 162         final long[] larray = new long[array.length];
 163 
 164         for (int index = 0; index < len; index++) {
 165             larray[index] = array[index];
 166         }
 167 
 168         return larray;
 169     }
 170 
 171     private LongArrayData convertToLong() {
 172         return new LongArrayData(toLongArray(), (int)length());
 173     }
 174 
 175     private NumberArrayData convertToDouble() {
 176         return new NumberArrayData(toDoubleArray(), (int)length());
 177     }
 178 
 179     private ObjectArrayData convertToObject() {
 180         return new ObjectArrayData(toObjectArray(false), (int)length());
 181     }
 182 
 183     @Override
 184     public ArrayData convert(final Class<?> type) {
 185         if (type == Integer.class || type == Byte.class || type == Short.class) {
 186             return this;
 187         } else if (type == Long.class) {
 188             return convertToLong();
 189         } else if (type == Double.class || type == Float.class) {
 190             return convertToDouble();
 191         } else {
 192             return convertToObject();
 193         }
 194     }
 195 
 196     @Override
 197     public void shiftLeft(final int by) {
 198         System.arraycopy(array, by, array, 0, array.length - by);
 199     }
 200 
 201     @Override
 202     public ArrayData shiftRight(final int by) {
 203         final ArrayData newData = ensure(by + length() - 1);
 204         if (newData != this) {
 205             newData.shiftRight(by);
 206             return newData;
 207         }
 208         System.arraycopy(array, 0, array, by, array.length - by);
 209 
 210         return this;
 211     }
 212 
 213     @Override
 214     public ArrayData ensure(final long safeIndex) {
 215         if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH) {
 216             return new SparseArrayData(this, safeIndex + 1);
 217         }
 218         final int alen = array.length;
 219         if (safeIndex >= alen) {
 220             final int newLength = ArrayData.nextSize((int)safeIndex);
 221             array = Arrays.copyOf(array, newLength);
 222         }
 223         if (safeIndex >= length()) {
 224             setLength(safeIndex + 1);
 225         }
 226         return this;
 227     }
 228 
 229     @Override
 230     public ArrayData shrink(final long newLength) {
 231         Arrays.fill(array, (int)newLength, array.length, 0);
 232         return this;
 233     }
 234 
 235     @Override
 236     public ArrayData set(final int index, final Object value, final boolean strict) {
 237         if (JSType.isRepresentableAsInt(value)) {
 238             return set(index, JSType.toInt32(value), strict);
 239         } else if (value == ScriptRuntime.UNDEFINED) {
 240             return new UndefinedArrayFilter(this).set(index, value, strict);
 241         }
 242 
 243         final ArrayData newData = convert(value == null ? Object.class : value.getClass());
 244         return newData.set(index, value, strict);
 245     }
 246 
 247     @Override
 248     public ArrayData set(final int index, final int value, final boolean strict) {
 249         array[index] = value;
 250         setLength(Math.max(index + 1, length()));
 251 
 252         return this;
 253     }
 254 
 255     @Override
 256     public ArrayData set(final int index, final long value, final boolean strict) {
 257         if (JSType.isRepresentableAsInt(value)) {
 258             array[index] = JSType.toInt32(value);
 259             setLength(Math.max(index + 1, length()));
 260             return this;
 261         }
 262 
 263         return convert(Long.class).set(index, value, strict);
 264     }
 265 
 266     @Override
 267     public ArrayData set(final int index, final double value, final boolean strict) {
 268         if (JSType.isRepresentableAsInt(value)) {
 269             array[index] = (int)(long)value;
 270             setLength(Math.max(index + 1, length()));
 271             return this;
 272         }
 273 
 274         return convert(Double.class).set(index, value, strict);
 275     }
 276 
 277     @Override
 278     public int getInt(final int index) {
 279         return array[index];
 280     }
 281 
 282     @Override
 283     public int getIntOptimistic(final int index, final int programPoint) {
 284         return array[index];
 285     }
 286 
 287     @Override
 288     public long getLong(final int index) {
 289         return array[index];
 290     }
 291 
 292     @Override
 293     public long getLongOptimistic(final int index, final int programPoint) {
 294         return array[index];
 295     }
 296 
 297     @Override
 298     public double getDouble(final int index) {
 299         return array[index];
 300     }
 301 
 302     @Override
 303     public double getDoubleOptimistic(final int index, final int programPoint) {
 304         return array[index];
 305     }
 306 
 307     @Override
 308     public Object getObject(final int index) {
 309         return array[index];
 310     }
 311 
 312     @Override
 313     public boolean has(final int index) {
 314         return 0 <= index && index < length();
 315     }
 316 
 317     @Override
 318     public ArrayData delete(final int index) {
 319         return new DeletedRangeArrayFilter(this, index, index);
 320     }
 321 
 322     @Override
 323     public ArrayData delete(final long fromIndex, final long toIndex) {
 324         return new DeletedRangeArrayFilter(this, fromIndex, toIndex);
 325     }
 326 
 327     @Override
 328     public Object pop() {
 329         final int len = (int)length();
 330         if (len == 0) {
 331             return ScriptRuntime.UNDEFINED;
 332         }
 333 
 334         final int newLength = len - 1;
 335         final int elem = array[newLength];
 336         array[newLength] = 0;
 337         setLength(newLength);
 338 
 339         return elem;
 340     }
 341 
 342     @Override
 343     public ArrayData slice(final long from, final long to) {
 344         return new IntArrayData(Arrays.copyOfRange(array, (int)from, (int)to), (int)(to - (from < 0 ? from + length() : from)));
 345     }
 346 
 347     @Override
 348     public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException {
 349         final long oldLength = length();
 350         final long newLength = oldLength - removed + added;
 351         if (newLength > SparseArrayData.MAX_DENSE_LENGTH && newLength > array.length) {
 352             throw new UnsupportedOperationException();
 353         }
 354         final ArrayData returnValue = removed == 0 ?
 355                 EMPTY_ARRAY :
 356                 new IntArrayData(
 357                         Arrays.copyOfRange(
 358                                 array,
 359                                 start,
 360                                 start + removed),
 361                         removed);
 362 
 363         if (newLength != oldLength) {
 364             final int[] newArray;
 365 
 366             if (newLength > array.length) {
 367                 newArray = new int[ArrayData.nextSize((int)newLength)];
 368                 System.arraycopy(array, 0, newArray, 0, start);
 369             } else {
 370                 newArray = array;
 371             }
 372 
 373             System.arraycopy(array, start + removed, newArray, start + added, (int)(oldLength - start - removed));
 374             array = newArray;
 375             setLength(newLength);
 376         }
 377 
 378         return returnValue;
 379     }
 380 
 381     @Override
 382     public long fastPush(final int arg) {
 383         final int len = (int)length();
 384         if (len == array.length) {
 385             array = Arrays.copyOf(array, nextSize(len));
 386         }
 387         array[len] = arg;
 388         return increaseLength();
 389     }
 390 
 391     //length must not be zero
 392     @Override
 393     public int fastPopInt() {
 394         if (length() == 0) {
 395             throw new ClassCastException(); //relink
 396         }
 397         final int newLength = (int)decreaseLength();
 398         final int elem = array[newLength];
 399         array[newLength] = 0;
 400         return elem;
 401     }
 402 
 403     @Override
 404     public long fastPopLong() {
 405         return fastPopInt();
 406     }
 407 
 408     @Override
 409     public double fastPopDouble() {
 410         return fastPopInt();
 411     }
 412 
 413     @Override
 414     public Object fastPopObject() {
 415         return fastPopInt();
 416     }
 417 
 418     @Override
 419     public ContinuousArrayData fastConcat(final ContinuousArrayData otherData) {
 420         final int   otherLength = (int)otherData.length();
 421         final int   thisLength  = (int)length();
 422         assert otherLength > 0 && thisLength > 0;
 423 
 424         final int[] otherArray  = ((IntArrayData)otherData).array;
 425         final int   newLength   = otherLength + thisLength;
 426         final int[] newArray    = new int[ArrayData.alignUp(newLength)];
 427 
 428         System.arraycopy(array, 0, newArray, 0, thisLength);
 429         System.arraycopy(otherArray, 0, newArray, thisLength, otherLength);
 430 
 431         return new IntArrayData(newArray, newLength);
 432     }
 433 
 434     @Override
 435     public String toString() {
 436         assert length() <= array.length : length() + " > " + array.length;
 437         return getClass().getSimpleName() + ':' + Arrays.toString(Arrays.copyOf(array, (int)length()));
 438     }
 439 }