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.internal.dynalink.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] = Double.valueOf(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 void shiftLeft(final int by) { 125 System.arraycopy(array, by, array, 0, array.length - by); 126 } 127 128 @Override 129 public ArrayData shiftRight(final int by) { 130 final ArrayData newData = ensure(by + length() - 1); 131 if (newData != this) { 132 newData.shiftRight(by); 133 return newData; 134 } 135 System.arraycopy(array, 0, array, by, array.length - by); 136 return this; 137 } 138 139 @Override 140 public ArrayData ensure(final long safeIndex) { 141 if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH) { 142 return new SparseArrayData(this, safeIndex + 1); 143 } 144 final int alen = array.length; 145 if (safeIndex >= alen) { 146 final int newLength = ArrayData.nextSize((int)safeIndex); 147 array = Arrays.copyOf(array, newLength); //todo fill with nan or never accessed? 148 } 149 if (safeIndex >= length()) { 150 setLength(safeIndex + 1); 151 } 152 return this; 153 154 } 155 156 @Override 157 public ArrayData shrink(final long newLength) { 158 Arrays.fill(array, (int)newLength, array.length, 0.0); 159 return this; 160 } 161 162 @Override 163 public ArrayData set(final int index, final Object value, final boolean strict) { 164 if (value instanceof Double || (value != null && canWiden(value.getClass()))) { 165 return set(index, ((Number)value).doubleValue(), strict); 166 } else if (value == UNDEFINED) { 167 return new UndefinedArrayFilter(this).set(index, value, strict); 168 } 169 170 final ArrayData newData = convert(value == null ? Object.class : value.getClass()); 171 return newData.set(index, value, strict); 172 } 173 174 @Override 175 public ArrayData set(final int index, final int value, final boolean strict) { 176 array[index] = value; 177 setLength(Math.max(index + 1, length())); 178 return this; 179 } 180 181 @Override 182 public ArrayData set(final int index, final long value, final boolean strict) { 183 array[index] = value; 184 setLength(Math.max(index + 1, length())); 185 return this; 186 } 187 188 @Override 189 public ArrayData set(final int index, final double value, final boolean strict) { 190 array[index] = value; 191 setLength(Math.max(index + 1, length())); 192 return this; 193 } 194 195 private static final MethodHandle HAS_GET_ELEM = specialCall(MethodHandles.lookup(), NumberArrayData.class, "getElem", double.class, int.class).methodHandle(); 196 private static final MethodHandle SET_ELEM = specialCall(MethodHandles.lookup(), NumberArrayData.class, "setElem", void.class, int.class, double.class).methodHandle(); 197 198 @SuppressWarnings("unused") 199 private double getElem(final int index) { 200 if (has(index)) { 201 return array[index]; 202 } 203 throw new ClassCastException(); 204 } 205 206 @SuppressWarnings("unused") 207 private void setElem(final int index, final double elem) { 208 if (hasRoomFor(index)) { 209 array[index] = elem; 210 return; 211 } 212 throw new ClassCastException(); 213 } 214 215 @Override 216 public MethodHandle getElementGetter(final Class<?> returnType, final int programPoint) { 217 if (returnType == int.class || returnType == long.class) { 218 return null; 219 } 220 return getContinuousElementGetter(HAS_GET_ELEM, returnType, programPoint); 221 } 222 223 @Override 224 public MethodHandle getElementSetter(final Class<?> elementType) { 225 return elementType.isPrimitive() ? getContinuousElementSetter(MH.asType(SET_ELEM, SET_ELEM.type().changeParameterType(2, elementType)), elementType) : null; 226 } 227 228 @Override 229 public int getInt(final int index) { 230 return JSType.toInt32(array[index]); 231 } 232 233 @Override 234 public long getLong(final int index) { 235 return (long)array[index]; 236 } 237 238 @Override 239 public double getDouble(final int index) { 240 return array[index]; 241 } 242 243 @Override 244 public double getDoubleOptimistic(final int index, final int programPoint) { 245 return array[index]; 246 } 247 248 @Override 249 public Object getObject(final int index) { 250 return array[index]; 251 } 252 253 @Override 254 public boolean has(final int index) { 255 return 0 <= index && index < length(); 256 } 257 258 @Override 259 public ArrayData delete(final int index) { 260 return new DeletedRangeArrayFilter(this, index, index); 261 } 262 263 @Override 264 public ArrayData delete(final long fromIndex, final long toIndex) { 265 return new DeletedRangeArrayFilter(this, fromIndex, toIndex); 266 } 267 268 @Override 269 public Object pop() { 270 final int len = (int)length(); 271 if (len == 0) { 272 return UNDEFINED; 273 } 274 275 final int newLength = len - 1; 276 final double elem = array[newLength]; 277 array[newLength] = 0; 278 setLength(newLength); 279 return elem; 280 } 281 282 @Override 283 public ArrayData slice(final long from, final long to) { 284 final long start = from < 0 ? from + length() : from; 285 final long newLength = to - start; 286 return new NumberArrayData(Arrays.copyOfRange(array, (int)from, (int)to), (int)newLength); 287 } 288 289 @Override 290 public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException { 291 final long oldLength = length(); 292 final long newLength = oldLength - removed + added; 293 if (newLength > SparseArrayData.MAX_DENSE_LENGTH && newLength > array.length) { 294 throw new UnsupportedOperationException(); 295 } 296 final ArrayData returnValue = removed == 0 ? 297 EMPTY_ARRAY : new NumberArrayData(Arrays.copyOfRange(array, start, start + removed), removed); 298 299 if (newLength != oldLength) { 300 final double[] newArray; 301 302 if (newLength > array.length) { 303 newArray = new double[ArrayData.nextSize((int)newLength)]; 304 System.arraycopy(array, 0, newArray, 0, start); 305 } else { 306 newArray = array; 307 } 308 309 System.arraycopy(array, start + removed, newArray, start + added, (int)(oldLength - start - removed)); 310 array = newArray; 311 setLength(newLength); 312 } 313 314 return returnValue; 315 } 316 317 @Override 318 public long fastPush(final int arg) { 319 return fastPush((double)arg); 320 } 321 322 @Override 323 public long fastPush(final long arg) { 324 return fastPush((double)arg); 325 } 326 327 @Override 328 public long fastPush(final double arg) { 329 final int len = (int)length(); 330 if (len == array.length) { 331 //note that fastpush never creates spares arrays, there is nothing to gain by that - it will just use even more memory 332 array = Arrays.copyOf(array, nextSize(len)); 333 } 334 array[len] = arg; 335 return increaseLength(); 336 } 337 338 @Override 339 public double fastPopDouble() { 340 if (length() == 0) { 341 throw new ClassCastException(); 342 } 343 final int newLength = (int)decreaseLength(); 344 final double elem = array[newLength]; 345 array[newLength] = 0; 346 return elem; 347 } 348 349 @Override 350 public Object fastPopObject() { 351 return fastPopDouble(); 352 } 353 354 @Override 355 public ContinuousArrayData fastConcat(final ContinuousArrayData otherData) { 356 final int otherLength = (int)otherData.length(); 357 final int thisLength = (int)length(); 358 assert otherLength > 0 && thisLength > 0; 359 360 final double[] otherArray = ((NumberArrayData)otherData).array; 361 final int newLength = otherLength + thisLength; 362 final double[] newArray = new double[ArrayData.alignUp(newLength)]; 363 364 System.arraycopy(array, 0, newArray, 0, thisLength); 365 System.arraycopy(otherArray, 0, newArray, thisLength, otherLength); 366 367 return new NumberArrayData(newArray, newLength); 368 } 369 370 @Override 371 public String toString() { 372 assert length() <= array.length : length() + " > " + array.length; 373 return getClass().getSimpleName() + ':' + Arrays.toString(Arrays.copyOf(array, (int)length())); 374 } 375 }