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 java.lang.invoke.MethodHandle; 29 import jdk.nashorn.internal.runtime.GlobalObject; 30 import jdk.nashorn.internal.runtime.JSType; 31 import jdk.nashorn.internal.runtime.PropertyDescriptor; 32 33 /** 34 * ArrayData - abstraction for wrapping array elements 35 */ 36 public abstract class ArrayData { 37 38 /** Minimum chunk size for underlying arrays */ 39 protected static final int CHUNK_SIZE = 16; 40 41 /** Mask for getting a chunk */ 42 protected static final int CHUNK_MASK = CHUNK_SIZE - 1; 43 44 /** 45 * Immutable empty array to get ScriptObjects started. 46 */ 47 public static final ArrayData EMPTY_ARRAY = new NoTypeArrayData(); 48 49 /** 50 * Length of the array data. Not necessarily length of the wrapped array. 51 */ 52 private long length; 53 54 /** 55 * Constructor 56 * @param length Virtual length of the array. 57 */ 58 protected ArrayData(final long length) { 59 this.length = length; 60 } 61 62 /** 63 * Factory method for unspecified array - start as int 64 * @return ArrayData 65 */ 66 public static ArrayData initialArray() { 67 return new IntArrayData(); 68 } 69 70 /** 71 * Factory method for unspecified array with given length - start as int array data 72 * 73 * @param length the initial length 74 * @return ArrayData 75 */ 76 public static ArrayData allocate(final int length) { 77 if (length == 0) { 78 return new IntArrayData(); 79 } else if (length >= SparseArrayData.MAX_DENSE_LENGTH) { 80 return new SparseArrayData(EMPTY_ARRAY, length); 81 } else { 82 return new DeletedRangeArrayFilter(new IntArrayData(length), 0, length - 1); 83 } 84 } 85 86 /** 87 * Factory method for unspecified given an array object 88 * 89 * @param array the array 90 * @return ArrayData wrapping this array 91 */ 92 public static ArrayData allocate(final Object array) { 93 final Class<?> clazz = array.getClass(); 94 95 if (clazz == int[].class) { 96 return new IntArrayData((int[])array, ((int[])array).length); 97 } else if (clazz == long[].class) { 98 return new LongArrayData((long[])array, ((long[])array).length); 99 } else if (clazz == double[].class) { 100 return new NumberArrayData((double[])array, ((double[])array).length); 101 } else { 102 return new ObjectArrayData((Object[])array, ((Object[])array).length); 103 } 104 } 105 106 /** 107 * Allocate an ArrayData wrapping a given array 108 * 109 * @param array the array to use for initial elements 110 * @return the ArrayData 111 */ 112 public static ArrayData allocate(final int[] array) { 113 return new IntArrayData(array, array.length); 114 } 115 116 /** 117 * Allocate an ArrayData wrapping a given array 118 * 119 * @param array the array to use for initial elements 120 * @return the ArrayData 121 */ 122 public static ArrayData allocate(final long[] array) { 123 return new LongArrayData(array, array.length); 124 } 125 126 /** 127 * Allocate an ArrayData wrapping a given array 128 * 129 * @param array the array to use for initial elements 130 * @return the ArrayData 131 */ 132 public static ArrayData allocate(final double[] array) { 133 return new NumberArrayData(array, array.length); 134 } 135 136 /** 137 * Allocate an ArrayData wrapping a given array 138 * 139 * @param array the array to use for initial elements 140 * @return the ArrayData 141 */ 142 public static ArrayData allocate(final Object[] array) { 143 return new ObjectArrayData(array, array.length); 144 } 145 146 /** 147 * Apply a freeze filter to an ArrayData. 148 * 149 * @param underlying the underlying ArrayData to wrap in the freeze filter 150 * @return the frozen ArrayData 151 */ 152 public static ArrayData freeze(final ArrayData underlying) { 153 return new FrozenArrayFilter(underlying); 154 } 155 156 /** 157 * Apply a seal filter to an ArrayData. 158 * 159 * @param underlying the underlying ArrayData to wrap in the seal filter 160 * @return the sealed ArrayData 161 */ 162 public static ArrayData seal(final ArrayData underlying) { 163 return new SealedArrayFilter(underlying); 164 } 165 166 /** 167 * Return the length of the array data. This may differ from the actual 168 * length of the array this wraps as length may be set or gotten as any 169 * other JavaScript Property 170 * 171 * Even though a JavaScript array length may be a long, we only store 172 * int parts for the optimized array access. For long lengths there 173 * are special cases anyway. 174 * 175 * TODO: represent arrays with "long" lengths as a special ArrayData 176 * that basically maps to the ScriptObject directly for better abstraction 177 * 178 * @return the length of the data 179 */ 180 public final long length() { 181 return length; 182 } 183 184 /** 185 * Return a copy of the array that can be modified without affecting this instance. 186 * It is safe to return themselves for immutable subclasses. 187 * 188 * @return a new array 189 */ 190 public abstract ArrayData copy(); 191 192 /** 193 * Return a copy of the array data as an Object array. 194 * 195 * @return an Object array 196 */ 197 public abstract Object[] asObjectArray(); 198 199 /** 200 * Return a copy of the array data as an array of the specified type. 201 * 202 * @param componentType the type of elements in the array 203 * @return and array of the given type 204 */ 205 public Object asArrayOfType(final Class<?> componentType) { 206 return JSType.convertArray(asObjectArray(), componentType); 207 } 208 209 /** 210 * Set the length of the data array 211 * 212 * @param length the new length for the data array 213 */ 214 public void setLength(final long length) { 215 this.length = length; 216 } 217 218 /** 219 * Shift the array data left 220 * 221 * TODO: explore start at an index and not at zero, to make these operations 222 * even faster. Offset everything from the index. Costs memory but is probably 223 * worth it 224 * 225 * @param by offset to shift 226 */ 227 public abstract void shiftLeft(int by); 228 229 /** 230 * Shift the array right 231 * 232 * @param by offset to shift 233 234 * @return New arraydata (or same) 235 */ 236 public abstract ArrayData shiftRight(int by); 237 238 /** 239 * Ensure that the given index exists and won't fail subsequent 240 * 241 * @param safeIndex the index to ensure wont go out of bounds 242 * @return new array data (or same) 243 */ 244 public abstract ArrayData ensure(long safeIndex); 245 246 /** 247 * Shrink the array to a new length, may or may not retain the 248 * inner array 249 * 250 * @param newLength new max length 251 * 252 * @return new array data (or same) 253 */ 254 public abstract ArrayData shrink(long newLength); 255 256 /** 257 * Set an object value at a given index 258 * 259 * @param index the index 260 * @param value the value 261 * @param strict are we in strict mode 262 * @return new array data (or same) 263 */ 264 public abstract ArrayData set(int index, Object value, boolean strict); 265 266 /** 267 * Set an int value at a given index 268 * 269 * @param index the index 270 * @param value the value 271 * @param strict are we in strict mode 272 * @return new array data (or same) 273 */ 274 public abstract ArrayData set(int index, int value, boolean strict); 275 276 /** 277 * Set a long value at a given index 278 * 279 * @param index the index 280 * @param value the value 281 * @param strict are we in strict mode 282 * @return new array data (or same) 283 */ 284 public abstract ArrayData set(int index, long value, boolean strict); 285 286 /** 287 * Set an double value at a given index 288 * 289 * @param index the index 290 * @param value the value 291 * @param strict are we in strict mode 292 * @return new array data (or same) 293 */ 294 public abstract ArrayData set(int index, double value, boolean strict); 295 296 /** 297 * Set an empty value at a given index. Should only affect Object array. 298 * 299 * @param index the index 300 * @return new array data (or same) 301 */ 302 public ArrayData setEmpty(final int index) { 303 // Do nothing. 304 return this; 305 } 306 307 /** 308 * Set an empty value for a given range. Should only affect Object array. 309 * 310 * @param lo range low end 311 * @param hi range high end 312 * @return new array data (or same) 313 */ 314 public ArrayData setEmpty(final long lo, final long hi) { 315 // Do nothing. 316 return this; 317 } 318 319 /** 320 * Get an int value from a given index 321 * 322 * @param index the index 323 * @return the value 324 */ 325 public abstract int getInt(int index); 326 327 /** 328 * Get a long value from a given index 329 * 330 * @param index the index 331 * @return the value 332 */ 333 public abstract long getLong(int index); 334 335 /** 336 * Get a double value from a given index 337 * 338 * @param index the index 339 * @return the value 340 */ 341 public abstract double getDouble(int index); 342 343 /** 344 * Get an Object value from a given index 345 * 346 * @param index the index 347 * @return the value 348 */ 349 public abstract Object getObject(int index); 350 351 /** 352 * Tests to see if an entry exists (avoids boxing.) 353 * @param index the index 354 * @return true if entry exists 355 */ 356 public abstract boolean has(int index); 357 358 /** 359 * Returns if element at specific index can be deleted or not. 360 * 361 * @param index the index of the element 362 * @param strict are we in strict mode 363 * 364 * @return true if element can be deleted 365 */ 366 public boolean canDelete(final int index, final boolean strict) { 367 return true; 368 } 369 370 /** 371 * Returns if element at specific index range can be deleted or not. 372 * 373 * @param fromIndex the start index 374 * @param toIndex the end index 375 * @param strict are we in strict mode 376 * 377 * @return true if range can be deleted 378 */ 379 public boolean canDelete(final long fromIndex, final long toIndex, final boolean strict) { 380 return true; 381 } 382 383 /** 384 * Returns property descriptor for element at a given index 385 * 386 * @param global the global object 387 * @param index the index 388 * 389 * @return property descriptor for element 390 */ 391 public PropertyDescriptor getDescriptor(final GlobalObject global, final int index) { 392 return global.newDataDescriptor(getObject(index), true, true, true); 393 } 394 395 /** 396 * Delete an array value at the given index, substituting 397 * for an undefined 398 * 399 * @param index the index 400 * @return new array data (or same) 401 */ 402 public abstract ArrayData delete(int index); 403 404 /** 405 * Delete a given range from this array; 406 * 407 * @param fromIndex from index (inclusive) 408 * @param toIndex to index (inclusive) 409 * 410 * @return new ArrayData after deletion 411 */ 412 public abstract ArrayData delete(long fromIndex, long toIndex); 413 414 /** 415 * Convert the ArrayData to one with a different element type 416 * Currently Arrays are not collapsed to narrower types, just to 417 * wider ones. Attempting to narrow an array will assert 418 * 419 * @param type new element type 420 * @return new array data 421 */ 422 protected abstract ArrayData convert(Class<?> type); 423 424 /** 425 * Push an array of items to the end of the array 426 * 427 * @param strict are we in strict mode 428 * @param items the items 429 * @return new array data (or same) 430 */ 431 public ArrayData push(final boolean strict, final Object... items) { 432 if (items.length == 0) { 433 return this; 434 } 435 436 final Class<?> widest = widestType(items); 437 438 ArrayData newData = convert(widest); 439 long pos = newData.length(); 440 for (final Object item : items) { 441 newData = newData.ensure(pos); //avoid sparse array 442 newData.set((int)pos++, item, strict); 443 } 444 return newData; 445 } 446 447 /** 448 * Pop an element from the end of the array 449 * 450 * @return the popped element 451 */ 452 public abstract Object pop(); 453 454 /** 455 * Slice out a section of the array and return that 456 * subsection as a new array data: [from, to) 457 * 458 * @param from start index 459 * @param to end index + 1 460 * @return new array data 461 */ 462 public abstract ArrayData slice(long from, long to); 463 464 /** 465 * Fast splice operation. This just modifies the array according to the number of 466 * elements added and deleted but does not insert the added elements. Throws 467 * {@code UnsupportedOperationException} if fast splice operation is not supported 468 * for this class or arguments. 469 * 470 * @param start start index of splice operation 471 * @param removed number of removed elements 472 * @param added number of added elements 473 * @throws UnsupportedOperationException if fast splice is not supported for the class or arguments. 474 */ 475 public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException { 476 throw new UnsupportedOperationException(); 477 } 478 479 480 static Class<?> widestType(final Object... items) { 481 assert items.length > 0; 482 483 Class<?> widest = Integer.class; 484 485 for (final Object item : items) { 486 final Class<?> itemClass = item == null ? null : item.getClass(); 487 if (itemClass == Long.class) { 488 if (widest == Integer.class) { 489 widest = Long.class; 490 } 491 } else if (itemClass == Double.class) { 492 if (widest == Integer.class || widest == Long.class) { 493 widest = Double.class; 494 } 495 } else if (!(item instanceof Number)) { 496 widest = Object.class; 497 break; 498 } 499 } 500 501 return widest; 502 } 503 504 /** 505 * Exponential growth function for array size when in 506 * need of resizing. 507 * 508 * @param size current size 509 * @return next size to allocate for internal array 510 */ 511 protected static int nextSize(final int size) { 512 if (size == 0) { 513 return CHUNK_SIZE; 514 } 515 516 int i = size; 517 while ((i & CHUNK_MASK) != 0) { 518 i++; 519 } 520 521 return i << 1; 522 } 523 524 /** 525 * Return the next valid index from a given one. Subclassed for various 526 * array representation 527 * 528 * @param index the current index 529 * 530 * @return the next index 531 */ 532 public long nextIndex(final long index) { 533 return index + 1; 534 } 535 536 static Object invoke(final MethodHandle mh, final Object arg) { 537 try { 538 return mh.invoke(arg); 539 } catch (final RuntimeException | Error e) { 540 throw e; 541 } catch (final Throwable t) { 542 throw new RuntimeException(t); 543 } 544 } 545 }