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 void shiftLeft(final int by) {
 180         System.arraycopy(array, by, array, 0, array.length - by);
 181     }
 182 
 183     @Override
 184     public ArrayData shiftRight(final int by) {
 185         final ArrayData newData = ensure(by + length() - 1);
 186         if (newData != this) {
 187             newData.shiftRight(by);
 188             return newData;
 189         }
 190         System.arraycopy(array, 0, array, by, array.length - by);
 191 
 192         return this;
 193     }
 194 
 195     @Override
 196     public ArrayData ensure(final long safeIndex) {
 197         if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH) {
 198             return new SparseArrayData(this, safeIndex + 1);
 199         }
 200         final int alen = array.length;
 201         if (safeIndex >= alen) {
 202             final int newLength = ArrayData.nextSize((int)safeIndex);
 203             array = Arrays.copyOf(array, newLength);
 204         }
 205         if (safeIndex >= length()) {
 206             setLength(safeIndex + 1);
 207         }
 208         return this;
 209     }
 210 
 211     @Override
 212     public ArrayData shrink(final long newLength) {
 213         Arrays.fill(array, (int)newLength, array.length, 0);
 214         return this;
 215     }
 216 
 217     @Override
 218     public ArrayData set(final int index, final Object value, final boolean strict) {
 219         if (JSType.isRepresentableAsInt(value)) {
 220             return set(index, JSType.toInt32(value), strict);
 221         } else if (value == ScriptRuntime.UNDEFINED) {
 222             return new UndefinedArrayFilter(this).set(index, value, strict);
 223         }
 224 
 225         final ArrayData newData = convert(value == null ? Object.class : value.getClass());
 226         return newData.set(index, value, strict);
 227     }
 228 
 229     @Override
 230     public ArrayData set(final int index, final int value, final boolean strict) {
 231         array[index] = value;
 232         setLength(Math.max(index + 1, length()));
 233 
 234         return this;
 235     }
 236 
 237     @Override
 238     public ArrayData set(final int index, final double value, final boolean strict) {
 239         if (JSType.isRepresentableAsInt(value)) {
 240             array[index] = (int)(long)value;
 241             setLength(Math.max(index + 1, length()));
 242             return this;
 243         }
 244 
 245         return convert(Double.class).set(index, value, strict);
 246     }
 247 
 248     @Override
 249     public int getInt(final int index) {
 250         return array[index];
 251     }
 252 
 253     @Override
 254     public int getIntOptimistic(final int index, final int programPoint) {
 255         return array[index];
 256     }
 257 
 258     @Override
 259     public double getDouble(final int index) {
 260         return array[index];
 261     }
 262 
 263     @Override
 264     public double getDoubleOptimistic(final int index, final int programPoint) {
 265         return array[index];
 266     }
 267 
 268     @Override
 269     public Object getObject(final int index) {
 270         return array[index];
 271     }
 272 
 273     @Override
 274     public boolean has(final int index) {
 275         return 0 <= index && index < length();
 276     }
 277 
 278     @Override
 279     public ArrayData delete(final int index) {
 280         return new DeletedRangeArrayFilter(this, index, index);
 281     }
 282 
 283     @Override
 284     public ArrayData delete(final long fromIndex, final long toIndex) {
 285         return new DeletedRangeArrayFilter(this, fromIndex, toIndex);
 286     }
 287 
 288     @Override
 289     public Object pop() {
 290         final int len = (int)length();
 291         if (len == 0) {
 292             return ScriptRuntime.UNDEFINED;
 293         }
 294 
 295         final int newLength = len - 1;
 296         final int elem = array[newLength];
 297         array[newLength] = 0;
 298         setLength(newLength);
 299 
 300         return elem;
 301     }
 302 
 303     @Override
 304     public ArrayData slice(final long from, final long to) {
 305         return new IntArrayData(Arrays.copyOfRange(array, (int)from, (int)to), (int)(to - (from < 0 ? from + length() : from)));
 306     }
 307 
 308     @Override
 309     public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException {
 310         final long oldLength = length();
 311         final long newLength = oldLength - removed + added;
 312         if (newLength > SparseArrayData.MAX_DENSE_LENGTH && newLength > array.length) {
 313             throw new UnsupportedOperationException();
 314         }
 315         final ArrayData returnValue = removed == 0 ?
 316                 EMPTY_ARRAY :
 317                 new IntArrayData(
 318                         Arrays.copyOfRange(
 319                                 array,
 320                                 start,
 321                                 start + removed),
 322                         removed);
 323 
 324         if (newLength != oldLength) {
 325             final int[] newArray;
 326 
 327             if (newLength > array.length) {
 328                 newArray = new int[ArrayData.nextSize((int)newLength)];
 329                 System.arraycopy(array, 0, newArray, 0, start);
 330             } else {
 331                 newArray = array;
 332             }
 333 
 334             System.arraycopy(array, start + removed, newArray, start + added, (int)(oldLength - start - removed));
 335             array = newArray;
 336             setLength(newLength);
 337         }
 338 
 339         return returnValue;
 340     }
 341 
 342     @Override
 343     public double fastPush(final int arg) {
 344         final int len = (int)length();
 345         if (len == array.length) {
 346             array = Arrays.copyOf(array, nextSize(len));
 347         }
 348         array[len] = arg;
 349         return increaseLength();
 350     }
 351 
 352     //length must not be zero
 353     @Override
 354     public int fastPopInt() {
 355         if (length() == 0) {
 356             throw new ClassCastException(); //relink
 357         }
 358         final int newLength = (int)decreaseLength();
 359         final int elem = array[newLength];
 360         array[newLength] = 0;
 361         return elem;
 362     }
 363 
 364     @Override
 365     public double fastPopDouble() {
 366         return fastPopInt();
 367     }
 368 
 369     @Override
 370     public Object fastPopObject() {
 371         return fastPopInt();
 372     }
 373 
 374     @Override
 375     public ContinuousArrayData fastConcat(final ContinuousArrayData otherData) {
 376         final int   otherLength = (int)otherData.length();
 377         final int   thisLength  = (int)length();
 378         assert otherLength > 0 && thisLength > 0;
 379 
 380         final int[] otherArray  = ((IntArrayData)otherData).array;
 381         final int   newLength   = otherLength + thisLength;
 382         final int[] newArray    = new int[ArrayData.alignUp(newLength)];
 383 
 384         System.arraycopy(array, 0, newArray, 0, thisLength);
 385         System.arraycopy(otherArray, 0, newArray, thisLength, otherLength);
 386 
 387         return new IntArrayData(newArray, newLength);
 388     }
 389 
 390     @Override
 391     public String toString() {
 392         assert length() <= array.length : length() + " > " + array.length;
 393         return getClass().getSimpleName() + ':' + Arrays.toString(Arrays.copyOf(array, (int)length()));
 394     }
 395 }