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 ArrayData shiftLeft(final int by) {
 102         if (by >= length()) {
 103             shrink(0);
 104         } else {
 105             System.arraycopy(array, by, array, 0, array.length - by);
 106         }
 107         setLength(Math.max(0, length() - by));
 108         return this;
 109     }
 110 
 111     @Override
 112     public ArrayData shiftRight(final int by) {
 113         final ArrayData newData = ensure(by + length() - 1);
 114         if (newData != this) {
 115             newData.shiftRight(by);
 116             return newData;
 117         }
 118         System.arraycopy(array, 0, array, by, array.length - by);
 119         return this;
 120     }
 121 
 122     @Override
 123     public ArrayData ensure(final long safeIndex) {
 124         if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH) {
 125             return new SparseArrayData(this, safeIndex + 1);
 126         }
 127         final int alen = array.length;
 128         if (safeIndex >= alen) {
 129             final int newLength = ArrayData.nextSize((int)safeIndex);
 130             array = Arrays.copyOf(array, newLength); //fill with undefined or OK? TODO
 131         }
 132         if (safeIndex >= length()) {
 133             setLength(safeIndex + 1);
 134         }
 135         return this;
 136     }
 137 
 138     @Override
 139     public ArrayData shrink(final long newLength) {
 140         Arrays.fill(array, (int) newLength, array.length, ScriptRuntime.UNDEFINED);
 141         return this;
 142     }
 143 
 144     @Override
 145     public ArrayData set(final int index, final Object value, final boolean strict) {
 146         array[index] = value;
 147         setLength(Math.max(index + 1, length()));
 148         return this;
 149     }
 150 
 151     @Override
 152     public ArrayData set(final int index, final int value, final boolean strict) {
 153         array[index] = value;
 154         setLength(Math.max(index + 1, length()));
 155         return this;
 156     }
 157 
 158     @Override
 159     public ArrayData set(final int index, final double value, final boolean strict) {
 160         array[index] = value;
 161         setLength(Math.max(index + 1, length()));
 162         return this;
 163     }
 164 
 165     @Override
 166     public ArrayData setEmpty(final int index) {
 167         array[index] = ScriptRuntime.EMPTY;
 168         return this;
 169     }
 170 
 171     @Override
 172     public ArrayData setEmpty(final long lo, final long hi) {
 173         // hi parameter is inclusive, but Arrays.fill toIndex parameter is exclusive
 174         Arrays.fill(array, (int)Math.max(lo, 0L), (int)Math.min(hi + 1, Integer.MAX_VALUE), ScriptRuntime.EMPTY);
 175         return this;
 176     }
 177 
 178     private static final MethodHandle HAS_GET_ELEM = specialCall(MethodHandles.lookup(), ObjectArrayData.class, "getElem", Object.class, int.class).methodHandle();
 179     private static final MethodHandle SET_ELEM     = specialCall(MethodHandles.lookup(), ObjectArrayData.class, "setElem", void.class, int.class, Object.class).methodHandle();
 180 
 181     @SuppressWarnings("unused")
 182     private Object getElem(final int index) {
 183         if (has(index)) {
 184             return array[index];
 185         }
 186         throw new ClassCastException();
 187     }
 188 
 189     @SuppressWarnings("unused")
 190     private void setElem(final int index, final Object elem) {
 191         if (hasRoomFor(index)) {
 192             array[index] = elem;
 193             return;
 194         }
 195         throw new ClassCastException();
 196     }
 197 
 198     @Override
 199     public MethodHandle getElementGetter(final Class<?> returnType, final int programPoint) {
 200         if (returnType.isPrimitive()) {
 201             return null;
 202         }
 203         return getContinuousElementGetter(HAS_GET_ELEM, returnType, programPoint);
 204     }
 205 
 206     @Override
 207     public MethodHandle getElementSetter(final Class<?> elementType) {
 208         return getContinuousElementSetter(SET_ELEM, Object.class);
 209     }
 210 
 211 
 212     @Override
 213     public int getInt(final int index) {
 214         return JSType.toInt32(array[index]);
 215     }
 216 
 217     @Override
 218     public double getDouble(final int index) {
 219         return JSType.toNumber(array[index]);
 220     }
 221 
 222     @Override
 223     public Object getObject(final int index) {
 224         return array[index];
 225     }
 226 
 227     @Override
 228     public boolean has(final int index) {
 229         return 0 <= index && index < length();
 230     }
 231 
 232     @Override
 233     public ArrayData delete(final int index) {
 234         setEmpty(index);
 235         return new DeletedRangeArrayFilter(this, index, index);
 236     }
 237 
 238     @Override
 239     public ArrayData delete(final long fromIndex, final long toIndex) {
 240         setEmpty(fromIndex, toIndex);
 241         return new DeletedRangeArrayFilter(this, fromIndex, toIndex);
 242     }
 243 
 244     @Override
 245     public double fastPush(final int arg) {
 246         return fastPush((Object)arg);
 247     }
 248 
 249     @Override
 250     public double fastPush(final long arg) {
 251         return fastPush((Object)arg);
 252     }
 253 
 254     @Override
 255     public double fastPush(final double arg) {
 256         return fastPush((Object)arg);
 257     }
 258 
 259     @Override
 260     public double fastPush(final Object arg) {
 261         final int len = (int)length();
 262         if (len == array.length) {
 263             array = Arrays.copyOf(array, nextSize(len));
 264         }
 265         array[len] = arg;
 266         return increaseLength();
 267     }
 268 
 269     @Override
 270     public Object fastPopObject() {
 271         if (length() == 0) {
 272             return ScriptRuntime.UNDEFINED;
 273         }
 274         final int newLength = (int)decreaseLength();
 275         final Object elem = array[newLength];
 276         array[newLength] = ScriptRuntime.EMPTY;
 277         return elem;
 278     }
 279 
 280     @Override
 281     public Object pop() {
 282         if (length() == 0) {
 283             return ScriptRuntime.UNDEFINED;
 284         }
 285 
 286         final int newLength = (int)length() - 1;
 287         final Object elem = array[newLength];
 288         setEmpty(newLength);
 289         setLength(newLength);
 290         return elem;
 291     }
 292 
 293     @Override
 294     public ArrayData slice(final long from, final long to) {
 295         final long start     = from < 0 ? from + length() : from;
 296         final long newLength = to - start;
 297         return new ObjectArrayData(Arrays.copyOfRange(array, (int)from, (int)to), (int)newLength);
 298     }
 299 
 300     @Override
 301     public ArrayData push(final boolean strict, final Object item) {
 302         final long      len     = length();
 303         final ArrayData newData = ensure(len);
 304         if (newData == this) {
 305             array[(int)len] = item;
 306             return this;
 307         }
 308         return newData.set((int)len, item, strict);
 309     }
 310 
 311     @Override
 312     public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException {
 313         final long oldLength = length();
 314         final long newLength = oldLength - removed + added;
 315         if (newLength > SparseArrayData.MAX_DENSE_LENGTH && newLength > array.length) {
 316             throw new UnsupportedOperationException();
 317         }
 318         final ArrayData returnValue = removed == 0 ?
 319                 EMPTY_ARRAY : new ObjectArrayData(Arrays.copyOfRange(array, start, start + removed), removed);
 320 
 321         if (newLength != oldLength) {
 322             final Object[] newArray;
 323 
 324             if (newLength > array.length) {
 325                 newArray = new Object[ArrayData.nextSize((int)newLength)];
 326                 System.arraycopy(array, 0, newArray, 0, start);
 327             } else {
 328                 newArray = array;
 329             }
 330 
 331             System.arraycopy(array, start + removed, newArray, start + added, (int)(oldLength - start - removed));
 332             array = newArray;
 333             setLength(newLength);
 334         }
 335 
 336         return returnValue;
 337     }
 338 
 339     @Override
 340     public ContinuousArrayData fastConcat(final ContinuousArrayData otherData) {
 341         final int   otherLength = (int)otherData.length();
 342         final int   thisLength  = (int)length();
 343         assert otherLength > 0 && thisLength > 0;
 344 
 345         final Object[] otherArray = ((ObjectArrayData)otherData).array;
 346         final int      newLength  = otherLength + thisLength;
 347         final Object[] newArray   = new Object[ArrayData.alignUp(newLength)];
 348 
 349         System.arraycopy(array, 0, newArray, 0, thisLength);
 350         System.arraycopy(otherArray, 0, newArray, thisLength, otherLength);
 351 
 352         return new ObjectArrayData(newArray, newLength);
 353     }
 354 
 355     @Override
 356     public String toString() {
 357         assert length() <= array.length : length() + " > " + array.length;
 358         return getClass().getSimpleName() + ':' + Arrays.toString(Arrays.copyOf(array, (int)length()));
 359     }
 360 }