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 NumberArrayData convertToDouble() {
 160         return new NumberArrayData(toDoubleArray(), (int)length());
 161     }
 162 
 163     private ObjectArrayData convertToObject() {
 164         return new ObjectArrayData(toObjectArray(false), (int)length());
 165     }
 166 
 167     @Override
 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
 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
 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
 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
 219     public ArrayData shrink(final long newLength) {
 220         Arrays.fill(array, (int)newLength, array.length, 0);
 221         return this;
 222     }
 223 
 224     @Override
 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
 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
 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
 256     public int getInt(final int index) {
 257         return array[index];
 258     }
 259 
 260     @Override
 261     public int getIntOptimistic(final int index, final int programPoint) {
 262         return array[index];
 263     }
 264 
 265     @Override
 266     public double getDouble(final int index) {
 267         return array[index];
 268     }
 269 
 270     @Override
 271     public double getDoubleOptimistic(final int index, final int programPoint) {
 272         return array[index];
 273     }
 274 
 275     @Override
 276     public Object getObject(final int index) {
 277         return array[index];
 278     }
 279 
 280     @Override
 281     public boolean has(final int index) {
 282         return 0 <= index && index < length();
 283     }
 284 
 285     @Override
 286     public ArrayData delete(final int index) {
 287         return new DeletedRangeArrayFilter(this, index, index);
 288     }
 289 
 290     @Override
 291     public ArrayData delete(final long fromIndex, final long toIndex) {
 292         return new DeletedRangeArrayFilter(this, fromIndex, toIndex);
 293     }
 294 
 295     @Override
 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
 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
 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
 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
 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
 372     public double fastPopDouble() {
 373         return fastPopInt();
 374     }
 375 
 376     @Override
 377     public Object fastPopObject() {
 378         return fastPopInt();
 379     }
 380 
 381     @Override
 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
 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 }