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