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