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