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) {
 124             return this;
 125         }
 126         final int len = (int)length();
 127         if (type == Double.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             return set(index, ((Number)value).longValue(), strict);
 176         } else if (value == ScriptRuntime.UNDEFINED) {
 177             return new UndefinedArrayFilter(this).set(index, value, strict);
 178         }
 179 
 180         final ArrayData newData = convert(value == null ? Object.class : value.getClass());
 181         return newData.set(index, value, strict);
 182     }
 183 
 184     @Override
 185     public ArrayData set(final int index, final int value, final boolean strict) {
 186         array[index] = value;
 187         setLength(Math.max(index + 1, length()));
 188         return this;
 189     }
 190 
 191     @Override
 192     public ArrayData set(final int index, final long value, final boolean strict) {
 193         array[index] = value;
 194         setLength(Math.max(index + 1, length()));
 195         return this;
 196     }
 197 
 198     @Override
 199     public ArrayData set(final int index, final double value, final boolean strict) {
 200         if (JSType.isRepresentableAsLong(value)) {
 201             array[index] = (long)value;
 202             setLength(Math.max(index + 1, length()));
 203             return this;
 204         }
 205         return convert(Double.class).set(index, value, strict);
 206     }
 207 
 208     private static final MethodHandle HAS_GET_ELEM = specialCall(MethodHandles.lookup(), LongArrayData.class, "getElem", long.class, int.class).methodHandle();
 209     private static final MethodHandle SET_ELEM     = specialCall(MethodHandles.lookup(), LongArrayData.class, "setElem", void.class, int.class, long.class).methodHandle();
 210 
 211     @SuppressWarnings("unused")
 212     private long getElem(final int index) {
 213         if (has(index)) {
 214             return array[index];
 215         }
 216         throw new ClassCastException();
 217     }
 218 
 219     @SuppressWarnings("unused")
 220     private void setElem(final int index, final long elem) {
 221         if (hasRoomFor(index)) {
 222             array[index] = elem;
 223             return;
 224         }
 225         throw new ClassCastException();
 226     }
 227 
 228     @Override
 229     public MethodHandle getElementGetter(final Class<?> returnType, final int programPoint) {
 230         if (returnType == int.class) {
 231             return null;
 232         }
 233         return getContinuousElementGetter(HAS_GET_ELEM, returnType, programPoint);
 234     }
 235 
 236     @Override
 237     public MethodHandle getElementSetter(final Class<?> elementType) {
 238         return elementType == int.class || elementType == long.class ? getContinuousElementSetter(MH.asType(SET_ELEM, SET_ELEM.type().changeParameterType(2, elementType)), elementType) : null;
 239     }
 240 
 241     @Override
 242     public int getInt(final int index) {
 243         return (int)array[index];
 244     }
 245 
 246     @Override
 247     public long getLong(final int index) {
 248         return array[index];
 249     }
 250 
 251     @Override
 252     public long getLongOptimistic(final int index, final int programPoint) {
 253         return array[index];
 254     }
 255 
 256     @Override
 257     public double getDouble(final int index) {
 258         return array[index];
 259     }
 260 
 261     @Override
 262     public double getDoubleOptimistic(final int index, final int programPoint) {
 263         return array[index];
 264     }
 265 
 266     @Override
 267     public Object getObject(final int index) {
 268         return array[index];
 269     }
 270 
 271     @Override
 272     public boolean has(final int index) {
 273         return 0 <= index && index < length();
 274     }
 275 
 276     @Override
 277     public ArrayData delete(final int index) {
 278         return new DeletedRangeArrayFilter(this, index, index);
 279     }
 280 
 281     @Override
 282     public ArrayData delete(final long fromIndex, final long toIndex) {
 283         return new DeletedRangeArrayFilter(this, fromIndex, toIndex);
 284     }
 285 
 286     @Override
 287     public Object pop() {
 288         final int len = (int)length();
 289         if (len == 0) {
 290             return ScriptRuntime.UNDEFINED;
 291         }
 292 
 293         final int newLength = len - 1;
 294         final long elem = array[newLength];
 295         array[newLength] = 0;
 296         setLength(newLength);
 297 
 298         return elem;
 299     }
 300 
 301     @Override
 302     public ArrayData slice(final long from, final long to) {
 303         final long start     = from < 0 ? from + length() : from;
 304         final long newLength = to - start;
 305         return new LongArrayData(Arrays.copyOfRange(array, (int)from, (int)to), (int)newLength);
 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 : new LongArrayData(Arrays.copyOfRange(array, start, start + removed), removed);
 317 
 318         if (newLength != oldLength) {
 319             final long[] newArray;
 320 
 321             if (newLength > array.length) {
 322                 newArray = new long[ArrayData.nextSize((int)newLength)];
 323                 System.arraycopy(array, 0, newArray, 0, start);
 324             } else {
 325                 newArray = array;
 326             }
 327 
 328             System.arraycopy(array, start + removed, newArray, start + added, (int)(oldLength - start - removed));
 329             array = newArray;
 330             setLength(newLength);
 331         }
 332 
 333         return returnValue;
 334     }
 335 
 336     @Override
 337     public long fastPush(final int arg) {
 338         return fastPush((long)arg);
 339     }
 340 
 341     @Override
 342     public long fastPush(final long arg) {
 343         final int len = (int)length();
 344         if (len == array.length) {
 345             array = Arrays.copyOf(array, nextSize(len));
 346         }
 347         array[len] = arg;
 348         return increaseLength();
 349     }
 350 
 351     @Override
 352     public long fastPopLong() {
 353         if (length() == 0) {
 354             throw new ClassCastException(); //undefined result
 355         }
 356         final int newLength = (int)decreaseLength();
 357         final long elem = array[newLength];
 358         array[newLength] = 0;
 359         return elem;
 360     }
 361 
 362     @Override
 363     public double fastPopDouble() {
 364         return fastPopLong();
 365    }
 366 
 367     @Override
 368     public Object fastPopObject() {
 369         return fastPopLong();
 370     }
 371 
 372     @Override
 373     public ContinuousArrayData fastConcat(final ContinuousArrayData otherData) {
 374         final int   otherLength = (int)otherData.length();
 375         final int   thisLength  = (int)length();
 376         assert otherLength > 0 && thisLength > 0;
 377 
 378         final long[] otherArray  = ((LongArrayData)otherData).array;
 379         final int    newLength   = otherLength + thisLength;
 380         final long[] newArray   = new long[ArrayData.alignUp(newLength)];
 381 
 382         System.arraycopy(array, 0, newArray, 0, thisLength);
 383         System.arraycopy(otherArray, 0, newArray, thisLength, otherLength);
 384 
 385         return new LongArrayData(newArray, newLength);
 386     }
 387 
 388     @Override
 389     public String toString() {
 390         assert length() <= array.length : length() + " > " + array.length;
 391 
 392         final StringBuilder sb = new StringBuilder(getClass().getSimpleName()).
 393                 append(": [");
 394         final int len = (int)length();
 395         for (int i = 0; i < len; i++) {
 396             sb.append(array[i]).append('L'); //make sure L suffix is on elements, to discriminate this from IntArrayData.toString()
 397             if (i + 1 < len) {
 398                 sb.append(", ");
 399             }
 400         }
 401         sb.append(']');
 402 
 403         return sb.toString();
 404     }
 405 }