NativeArray.java revision 1076:871cd9451896
1/* 2 * Copyright (c) 2010, 2014, 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 26package jdk.nashorn.internal.objects; 27 28import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError; 29import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 30import static jdk.nashorn.internal.runtime.PropertyDescriptor.VALUE; 31import static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE; 32import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex; 33import static jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator.arrayLikeIterator; 34import static jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator.reverseArrayLikeIterator; 35import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_STRICT; 36 37import java.lang.invoke.MethodHandle; 38import java.lang.invoke.SwitchPoint; 39import java.util.ArrayList; 40import java.util.Arrays; 41import java.util.Collections; 42import java.util.Comparator; 43import java.util.Iterator; 44import java.util.List; 45import java.util.concurrent.Callable; 46import jdk.internal.dynalink.CallSiteDescriptor; 47import jdk.internal.dynalink.linker.GuardedInvocation; 48import jdk.internal.dynalink.linker.LinkRequest; 49import jdk.nashorn.api.scripting.JSObject; 50import jdk.nashorn.internal.objects.annotations.Attribute; 51import jdk.nashorn.internal.objects.annotations.Constructor; 52import jdk.nashorn.internal.objects.annotations.Function; 53import jdk.nashorn.internal.objects.annotations.Getter; 54import jdk.nashorn.internal.objects.annotations.ScriptClass; 55import jdk.nashorn.internal.objects.annotations.Setter; 56import jdk.nashorn.internal.objects.annotations.SpecializedFunction; 57import jdk.nashorn.internal.objects.annotations.SpecializedFunction.LinkLogic; 58import jdk.nashorn.internal.objects.annotations.Where; 59import jdk.nashorn.internal.runtime.Context; 60import jdk.nashorn.internal.runtime.Debug; 61import jdk.nashorn.internal.runtime.JSType; 62import jdk.nashorn.internal.runtime.OptimisticBuiltins; 63import jdk.nashorn.internal.runtime.PropertyDescriptor; 64import jdk.nashorn.internal.runtime.PropertyMap; 65import jdk.nashorn.internal.runtime.ScriptFunction; 66import jdk.nashorn.internal.runtime.ScriptObject; 67import jdk.nashorn.internal.runtime.ScriptRuntime; 68import jdk.nashorn.internal.runtime.Undefined; 69import jdk.nashorn.internal.runtime.arrays.ArrayData; 70import jdk.nashorn.internal.runtime.arrays.ArrayIndex; 71import jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator; 72import jdk.nashorn.internal.runtime.arrays.ContinuousArrayData; 73import jdk.nashorn.internal.runtime.arrays.IntElements; 74import jdk.nashorn.internal.runtime.arrays.IntOrLongElements; 75import jdk.nashorn.internal.runtime.arrays.IteratorAction; 76import jdk.nashorn.internal.runtime.arrays.NumericElements; 77import jdk.nashorn.internal.runtime.linker.Bootstrap; 78import jdk.nashorn.internal.runtime.linker.InvokeByName; 79 80/** 81 * Runtime representation of a JavaScript array. NativeArray only holds numeric 82 * keyed values. All other values are stored in spill. 83 */ 84@ScriptClass("Array") 85public final class NativeArray extends ScriptObject implements OptimisticBuiltins { 86 private static final Object JOIN = new Object(); 87 private static final Object EVERY_CALLBACK_INVOKER = new Object(); 88 private static final Object SOME_CALLBACK_INVOKER = new Object(); 89 private static final Object FOREACH_CALLBACK_INVOKER = new Object(); 90 private static final Object MAP_CALLBACK_INVOKER = new Object(); 91 private static final Object FILTER_CALLBACK_INVOKER = new Object(); 92 private static final Object REDUCE_CALLBACK_INVOKER = new Object(); 93 private static final Object CALL_CMP = new Object(); 94 private static final Object TO_LOCALE_STRING = new Object(); 95 96 private SwitchPoint lengthMadeNotWritableSwitchPoint; 97 private PushLinkLogic pushLinkLogic; 98 private PopLinkLogic popLinkLogic; 99 private ConcatLinkLogic concatLinkLogic; 100 101 /** 102 * Index for the modification SwitchPoint that triggers when length 103 * becomes not writable 104 */ 105 private static final int LENGTH_NOT_WRITABLE_SWITCHPOINT = 0; 106 107 /* 108 * Constructors. 109 */ 110 NativeArray() { 111 this(ArrayData.initialArray()); 112 } 113 114 NativeArray(final long length) { 115 // TODO assert valid index in long before casting 116 this(ArrayData.allocate((int)length)); 117 } 118 119 NativeArray(final int[] array) { 120 this(ArrayData.allocate(array)); 121 } 122 123 NativeArray(final long[] array) { 124 this(ArrayData.allocate(array)); 125 } 126 127 NativeArray(final double[] array) { 128 this(ArrayData.allocate(array)); 129 } 130 131 NativeArray(final Object[] array) { 132 this(ArrayData.allocate(array.length)); 133 134 ArrayData arrayData = this.getArray(); 135 if (array.length > 0) { 136 arrayData.ensure(array.length - 1); 137 } 138 139 for (int index = 0; index < array.length; index++) { 140 final Object value = array[index]; 141 142 if (value == ScriptRuntime.EMPTY) { 143 arrayData = arrayData.delete(index); 144 } else { 145 arrayData = arrayData.set(index, value, false); 146 } 147 } 148 149 this.setArray(arrayData); 150 } 151 152 NativeArray(final ArrayData arrayData) { 153 this(arrayData, Global.instance()); 154 } 155 156 NativeArray(final ArrayData arrayData, final Global global) { 157 super(global.getArrayPrototype(), $nasgenmap$); 158 setArray(arrayData); 159 setIsArray(); 160 } 161 162 @Override 163 protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) { 164 final GuardedInvocation inv = getArray().findFastGetMethod(getArray().getClass(), desc, request, operator); 165 if (inv != null) { 166 return inv; 167 } 168 return super.findGetMethod(desc, request, operator); 169 } 170 171 @Override 172 protected GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) { 173 final GuardedInvocation inv = getArray().findFastGetIndexMethod(getArray().getClass(), desc, request); 174 if (inv != null) { 175 return inv; 176 } 177 return super.findGetIndexMethod(desc, request); 178 } 179 180 @Override 181 protected GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) { 182 final GuardedInvocation inv = getArray().findFastSetIndexMethod(getArray().getClass(), desc, request); 183 if (inv != null) { 184 return inv; 185 } 186 187 return super.findSetIndexMethod(desc, request); 188 } 189 190 private static InvokeByName getJOIN() { 191 return Global.instance().getInvokeByName(JOIN, 192 new Callable<InvokeByName>() { 193 @Override 194 public InvokeByName call() { 195 return new InvokeByName("join", ScriptObject.class); 196 } 197 }); 198 } 199 200 private static MethodHandle createIteratorCallbackInvoker(final Object key, final Class<?> rtype) { 201 return Global.instance().getDynamicInvoker(key, 202 new Callable<MethodHandle>() { 203 @Override 204 public MethodHandle call() { 205 return Bootstrap.createDynamicInvoker("dyn:call", rtype, Object.class, Object.class, Object.class, 206 long.class, Object.class); 207 } 208 }); 209 } 210 211 private static MethodHandle getEVERY_CALLBACK_INVOKER() { 212 return createIteratorCallbackInvoker(EVERY_CALLBACK_INVOKER, boolean.class); 213 } 214 215 private static MethodHandle getSOME_CALLBACK_INVOKER() { 216 return createIteratorCallbackInvoker(SOME_CALLBACK_INVOKER, boolean.class); 217 } 218 219 private static MethodHandle getFOREACH_CALLBACK_INVOKER() { 220 return createIteratorCallbackInvoker(FOREACH_CALLBACK_INVOKER, void.class); 221 } 222 223 private static MethodHandle getMAP_CALLBACK_INVOKER() { 224 return createIteratorCallbackInvoker(MAP_CALLBACK_INVOKER, Object.class); 225 } 226 227 private static MethodHandle getFILTER_CALLBACK_INVOKER() { 228 return createIteratorCallbackInvoker(FILTER_CALLBACK_INVOKER, boolean.class); 229 } 230 231 private static MethodHandle getREDUCE_CALLBACK_INVOKER() { 232 return Global.instance().getDynamicInvoker(REDUCE_CALLBACK_INVOKER, 233 new Callable<MethodHandle>() { 234 @Override 235 public MethodHandle call() { 236 return Bootstrap.createDynamicInvoker("dyn:call", Object.class, Object.class, 237 Undefined.class, Object.class, Object.class, long.class, Object.class); 238 } 239 }); 240 } 241 242 private static MethodHandle getCALL_CMP() { 243 return Global.instance().getDynamicInvoker(CALL_CMP, 244 new Callable<MethodHandle>() { 245 @Override 246 public MethodHandle call() { 247 return Bootstrap.createDynamicInvoker("dyn:call", double.class, 248 ScriptFunction.class, Object.class, Object.class, Object.class); 249 } 250 }); 251 } 252 253 private static InvokeByName getTO_LOCALE_STRING() { 254 return Global.instance().getInvokeByName(TO_LOCALE_STRING, 255 new Callable<InvokeByName>() { 256 @Override 257 public InvokeByName call() { 258 return new InvokeByName("toLocaleString", ScriptObject.class, String.class); 259 } 260 }); 261 } 262 263 // initialized by nasgen 264 private static PropertyMap $nasgenmap$; 265 266 @Override 267 public String getClassName() { 268 return "Array"; 269 } 270 271 @Override 272 public Object getLength() { 273 final long length = JSType.toUint32(getArray().length()); 274 if(length < Integer.MAX_VALUE) { 275 return (int)length; 276 } 277 return length; 278 } 279 280 /** 281 * ECMA 15.4.5.1 [[DefineOwnProperty]] ( P, Desc, Throw ) 282 */ 283 @Override 284 public boolean defineOwnProperty(final String key, final Object propertyDesc, final boolean reject) { 285 final PropertyDescriptor desc = toPropertyDescriptor(Global.instance(), propertyDesc); 286 287 // never be undefined as "length" is always defined and can't be deleted for arrays 288 // Step 1 289 final PropertyDescriptor oldLenDesc = (PropertyDescriptor) super.getOwnPropertyDescriptor("length"); 290 291 // Step 2 292 // get old length and convert to long 293 long oldLen = NativeArray.validLength(oldLenDesc.getValue(), true); 294 295 // Step 3 296 if ("length".equals(key)) { 297 // check for length being made non-writable 298 if (desc.has(WRITABLE) && !desc.isWritable()) { 299 setIsLengthNotWritable(); 300 } 301 302 // Step 3a 303 if (!desc.has(VALUE)) { 304 return super.defineOwnProperty("length", desc, reject); 305 } 306 307 // Step 3b 308 final PropertyDescriptor newLenDesc = desc; 309 310 // Step 3c and 3d - get new length and convert to long 311 final long newLen = NativeArray.validLength(newLenDesc.getValue(), true); 312 313 // Step 3e 314 newLenDesc.setValue(newLen); 315 316 // Step 3f 317 // increasing array length - just need to set new length value (and attributes if any) and return 318 if (newLen >= oldLen) { 319 return super.defineOwnProperty("length", newLenDesc, reject); 320 } 321 322 // Step 3g 323 if (!oldLenDesc.isWritable()) { 324 if (reject) { 325 throw typeError("property.not.writable", "length", ScriptRuntime.safeToString(this)); 326 } 327 return false; 328 } 329 330 // Step 3h and 3i 331 final boolean newWritable = !newLenDesc.has(WRITABLE) || newLenDesc.isWritable(); 332 if (!newWritable) { 333 newLenDesc.setWritable(true); 334 } 335 336 // Step 3j and 3k 337 final boolean succeeded = super.defineOwnProperty("length", newLenDesc, reject); 338 if (!succeeded) { 339 return false; 340 } 341 342 // Step 3l 343 // make sure that length is set till the point we can delete the old elements 344 while (newLen < oldLen) { 345 oldLen--; 346 final boolean deleteSucceeded = delete(oldLen, false); 347 if (!deleteSucceeded) { 348 newLenDesc.setValue(oldLen + 1); 349 if (!newWritable) { 350 newLenDesc.setWritable(false); 351 } 352 super.defineOwnProperty("length", newLenDesc, false); 353 if (reject) { 354 throw typeError("property.not.writable", "length", ScriptRuntime.safeToString(this)); 355 } 356 return false; 357 } 358 } 359 360 // Step 3m 361 if (!newWritable) { 362 // make 'length' property not writable 363 final ScriptObject newDesc = Global.newEmptyInstance(); 364 newDesc.set(WRITABLE, false, 0); 365 return super.defineOwnProperty("length", newDesc, false); 366 } 367 368 return true; 369 } 370 371 // Step 4a 372 final int index = ArrayIndex.getArrayIndex(key); 373 if (ArrayIndex.isValidArrayIndex(index)) { 374 final long longIndex = ArrayIndex.toLongIndex(index); 375 // Step 4b 376 // setting an element beyond current length, but 'length' is not writable 377 if (longIndex >= oldLen && !oldLenDesc.isWritable()) { 378 if (reject) { 379 throw typeError("property.not.writable", Long.toString(longIndex), ScriptRuntime.safeToString(this)); 380 } 381 return false; 382 } 383 384 // Step 4c 385 // set the new array element 386 final boolean succeeded = super.defineOwnProperty(key, desc, false); 387 388 // Step 4d 389 if (!succeeded) { 390 if (reject) { 391 throw typeError("cant.redefine.property", key, ScriptRuntime.safeToString(this)); 392 } 393 return false; 394 } 395 396 // Step 4e -- adjust new length based on new element index that is set 397 if (longIndex >= oldLen) { 398 oldLenDesc.setValue(longIndex + 1); 399 super.defineOwnProperty("length", oldLenDesc, false); 400 } 401 402 // Step 4f 403 return true; 404 } 405 406 // not an index property 407 return super.defineOwnProperty(key, desc, reject); 408 } 409 410 /** 411 * Spec. mentions use of [[DefineOwnProperty]] for indexed properties in 412 * certain places (eg. Array.prototype.map, filter). We can not use ScriptObject.set 413 * method in such cases. This is because set method uses inherited setters (if any) 414 * from any object in proto chain such as Array.prototype, Object.prototype. 415 * This method directly sets a particular element value in the current object. 416 * 417 * @param index key for property 418 * @param value value to define 419 */ 420 @Override 421 public final void defineOwnProperty(final int index, final Object value) { 422 assert isValidArrayIndex(index) : "invalid array index"; 423 final long longIndex = ArrayIndex.toLongIndex(index); 424 if (longIndex >= getArray().length()) { 425 // make array big enough to hold.. 426 setArray(getArray().ensure(longIndex)); 427 } 428 setArray(getArray().set(index, value, false)); 429 } 430 431 /** 432 * Return the array contents upcasted as an ObjectArray, regardless of 433 * representation 434 * 435 * @return an object array 436 */ 437 public Object[] asObjectArray() { 438 return getArray().asObjectArray(); 439 } 440 441 @Override 442 public void setIsLengthNotWritable() { 443 super.setIsLengthNotWritable(); 444 /* 445 * Switchpoints are created lazily. If we link any push or pop site, 446 * we need to create the "length made not writable" switchpoint, if it 447 * doesn't exist. 448 * 449 * If the switchpoint already exists, we will find it here, and invalidate 450 * it, invalidating all previous callsites that use it. 451 * 452 * If the switchpoint doesn't exist, no push/pop has been linked so far, 453 * because that would create it too. We invalidate it immediately and the 454 * check link logic for all future callsites will fail immediately at link 455 * time 456 */ 457 if (lengthMadeNotWritableSwitchPoint == null) { 458 lengthMadeNotWritableSwitchPoint = new SwitchPoint(); 459 } 460 SwitchPoint.invalidateAll(new SwitchPoint[] { lengthMadeNotWritableSwitchPoint }); 461 } 462 463 /** 464 * ECMA 15.4.3.2 Array.isArray ( arg ) 465 * 466 * @param self self reference 467 * @param arg argument - object to check 468 * @return true if argument is an array 469 */ 470 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 471 public static boolean isArray(final Object self, final Object arg) { 472 return isArray(arg) || (arg instanceof JSObject && ((JSObject)arg).isArray()); 473 } 474 475 /** 476 * Length getter 477 * @param self self reference 478 * @return the length of the object 479 */ 480 @Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE) 481 public static Object length(final Object self) { 482 if (isArray(self)) { 483 return JSType.toUint32(((ScriptObject) self).getArray().length()); 484 } 485 486 return 0; 487 } 488 489 /** 490 * Length setter 491 * @param self self reference 492 * @param length new length property 493 */ 494 @Setter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE) 495 public static void length(final Object self, final Object length) { 496 if (isArray(self)) { 497 ((ScriptObject) self).setLength(validLength(length, true)); 498 } 499 } 500 501 /** 502 * Prototype length getter 503 * @param self self reference 504 * @return the length of the object 505 */ 506 @Getter(name = "length", where = Where.PROTOTYPE, attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE) 507 public static Object getProtoLength(final Object self) { 508 return length(self); // Same as instance getter but we can't make nasgen use the same method for prototype 509 } 510 511 /** 512 * Prototype length setter 513 * @param self self reference 514 * @param length new length property 515 */ 516 @Setter(name = "length", where = Where.PROTOTYPE, attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE) 517 public static void setProtoLength(final Object self, final Object length) { 518 length(self, length); // Same as instance setter but we can't make nasgen use the same method for prototype 519 } 520 521 static long validLength(final Object length, final boolean reject) { 522 final double doubleLength = JSType.toNumber(length); 523 if (!Double.isNaN(doubleLength) && JSType.isRepresentableAsLong(doubleLength)) { 524 final long len = (long) doubleLength; 525 if (len >= 0 && len <= JSType.MAX_UINT) { 526 return len; 527 } 528 } 529 if (reject) { 530 throw rangeError("inappropriate.array.length", ScriptRuntime.safeToString(length)); 531 } 532 return -1; 533 } 534 535 /** 536 * ECMA 15.4.4.2 Array.prototype.toString ( ) 537 * 538 * @param self self reference 539 * @return string representation of array 540 */ 541 @Function(attributes = Attribute.NOT_ENUMERABLE) 542 public static Object toString(final Object self) { 543 final Object obj = Global.toObject(self); 544 if (obj instanceof ScriptObject) { 545 final InvokeByName joinInvoker = getJOIN(); 546 final ScriptObject sobj = (ScriptObject)obj; 547 try { 548 final Object join = joinInvoker.getGetter().invokeExact(sobj); 549 if (Bootstrap.isCallable(join)) { 550 return joinInvoker.getInvoker().invokeExact(join, sobj); 551 } 552 } catch (final RuntimeException | Error e) { 553 throw e; 554 } catch (final Throwable t) { 555 throw new RuntimeException(t); 556 } 557 } 558 559 // FIXME: should lookup Object.prototype.toString and call that? 560 return ScriptRuntime.builtinObjectToString(self); 561 } 562 563 /** 564 * Assert that an array is numeric, if not throw type error 565 * @param self self array to check 566 * @return true if numeric 567 */ 568 @Function(attributes = Attribute.NOT_ENUMERABLE) 569 public static Object assertNumeric(final Object self) { 570 if(!(self instanceof NativeArray && ((NativeArray)self).getArray().getOptimisticType().isNumeric())) { 571 throw typeError("not.a.numeric.array", ScriptRuntime.safeToString(self)); 572 } 573 return Boolean.TRUE; 574 } 575 576 /** 577 * ECMA 15.4.4.3 Array.prototype.toLocaleString ( ) 578 * 579 * @param self self reference 580 * @return locale specific string representation for array 581 */ 582 @Function(attributes = Attribute.NOT_ENUMERABLE) 583 public static String toLocaleString(final Object self) { 584 final StringBuilder sb = new StringBuilder(); 585 final Iterator<Object> iter = arrayLikeIterator(self, true); 586 587 while (iter.hasNext()) { 588 final Object obj = iter.next(); 589 590 if (obj != null && obj != ScriptRuntime.UNDEFINED) { 591 final Object val = JSType.toScriptObject(obj); 592 593 try { 594 if (val instanceof ScriptObject) { 595 final InvokeByName localeInvoker = getTO_LOCALE_STRING(); 596 final ScriptObject sobj = (ScriptObject)val; 597 final Object toLocaleString = localeInvoker.getGetter().invokeExact(sobj); 598 599 if (Bootstrap.isCallable(toLocaleString)) { 600 sb.append((String)localeInvoker.getInvoker().invokeExact(toLocaleString, sobj)); 601 } else { 602 throw typeError("not.a.function", "toLocaleString"); 603 } 604 } 605 } catch (final Error|RuntimeException t) { 606 throw t; 607 } catch (final Throwable t) { 608 throw new RuntimeException(t); 609 } 610 } 611 612 if (iter.hasNext()) { 613 sb.append(","); 614 } 615 } 616 617 return sb.toString(); 618 } 619 620 /** 621 * ECMA 15.4.2.2 new Array (len) 622 * 623 * @param newObj was the new operator used to instantiate this array 624 * @param self self reference 625 * @param args arguments (length) 626 * @return the new NativeArray 627 */ 628 @Constructor(arity = 1) 629 public static NativeArray construct(final boolean newObj, final Object self, final Object... args) { 630 switch (args.length) { 631 case 0: 632 return new NativeArray(0); 633 case 1: 634 final Object len = args[0]; 635 if (len instanceof Number) { 636 long length; 637 if (len instanceof Integer || len instanceof Long) { 638 length = ((Number) len).longValue(); 639 if (length >= 0 && length < JSType.MAX_UINT) { 640 return new NativeArray(length); 641 } 642 } 643 644 length = JSType.toUint32(len); 645 646 /* 647 * If the argument len is a Number and ToUint32(len) is equal to 648 * len, then the length property of the newly constructed object 649 * is set to ToUint32(len). If the argument len is a Number and 650 * ToUint32(len) is not equal to len, a RangeError exception is 651 * thrown. 652 */ 653 final double numberLength = ((Number) len).doubleValue(); 654 if (length != numberLength) { 655 throw rangeError("inappropriate.array.length", JSType.toString(numberLength)); 656 } 657 658 return new NativeArray(length); 659 } 660 /* 661 * If the argument len is not a Number, then the length property of 662 * the newly constructed object is set to 1 and the 0 property of 663 * the newly constructed object is set to len 664 */ 665 return new NativeArray(new Object[]{args[0]}); 666 //fallthru 667 default: 668 return new NativeArray(args); 669 } 670 } 671 672 /** 673 * ECMA 15.4.2.2 new Array (len) 674 * 675 * Specialized constructor for zero arguments - empty array 676 * 677 * @param newObj was the new operator used to instantiate this array 678 * @param self self reference 679 * @return the new NativeArray 680 */ 681 @SpecializedFunction(isConstructor=true) 682 public static NativeArray construct(final boolean newObj, final Object self) { 683 return new NativeArray(0); 684 } 685 686 /** 687 * ECMA 15.4.2.2 new Array (len) 688 * 689 * Specialized constructor for zero arguments - empty array 690 * 691 * @param newObj was the new operator used to instantiate this array 692 * @param self self reference 693 * @param element first element 694 * @return the new NativeArray 695 */ 696 @SpecializedFunction(isConstructor=true) 697 public static Object construct(final boolean newObj, final Object self, final boolean element) { 698 return new NativeArray(new Object[] { element }); 699 } 700 701 /** 702 * ECMA 15.4.2.2 new Array (len) 703 * 704 * Specialized constructor for one integer argument (length) 705 * 706 * @param newObj was the new operator used to instantiate this array 707 * @param self self reference 708 * @param length array length 709 * @return the new NativeArray 710 */ 711 @SpecializedFunction(isConstructor=true) 712 public static NativeArray construct(final boolean newObj, final Object self, final int length) { 713 if (length >= 0) { 714 return new NativeArray(length); 715 } 716 717 return construct(newObj, self, new Object[]{length}); 718 } 719 720 /** 721 * ECMA 15.4.2.2 new Array (len) 722 * 723 * Specialized constructor for one long argument (length) 724 * 725 * @param newObj was the new operator used to instantiate this array 726 * @param self self reference 727 * @param length array length 728 * @return the new NativeArray 729 */ 730 @SpecializedFunction(isConstructor=true) 731 public static NativeArray construct(final boolean newObj, final Object self, final long length) { 732 if (length >= 0L && length <= JSType.MAX_UINT) { 733 return new NativeArray(length); 734 } 735 736 return construct(newObj, self, new Object[]{length}); 737 } 738 739 /** 740 * ECMA 15.4.2.2 new Array (len) 741 * 742 * Specialized constructor for one double argument (length) 743 * 744 * @param newObj was the new operator used to instantiate this array 745 * @param self self reference 746 * @param length array length 747 * @return the new NativeArray 748 */ 749 @SpecializedFunction(isConstructor=true) 750 public static NativeArray construct(final boolean newObj, final Object self, final double length) { 751 final long uint32length = JSType.toUint32(length); 752 753 if (uint32length == length) { 754 return new NativeArray(uint32length); 755 } 756 757 return construct(newObj, self, new Object[]{length}); 758 } 759 760 /** 761 * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] ) 762 * 763 * @param self self reference 764 * @param arg argument 765 * @return resulting NativeArray 766 */ 767 @SpecializedFunction(linkLogic=ConcatLinkLogic.class) 768 public static NativeArray concat(final Object self, final int arg) { 769 final ContinuousArrayData newData = getContinuousArrayDataCCE(self, Integer.class).copy(); //get at least an integer data copy of this data 770 newData.fastPush(arg); //add an integer to its end 771 return new NativeArray(newData); 772 } 773 774 /** 775 * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] ) 776 * 777 * @param self self reference 778 * @param arg argument 779 * @return resulting NativeArray 780 */ 781 @SpecializedFunction(linkLogic=ConcatLinkLogic.class) 782 public static NativeArray concat(final Object self, final long arg) { 783 final ContinuousArrayData newData = getContinuousArrayDataCCE(self, Long.class).copy(); //get at least a long array data copy of this data 784 newData.fastPush(arg); //add a long at the end 785 return new NativeArray(newData); 786 } 787 788 /** 789 * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] ) 790 * 791 * @param self self reference 792 * @param arg argument 793 * @return resulting NativeArray 794 */ 795 @SpecializedFunction(linkLogic=ConcatLinkLogic.class) 796 public static NativeArray concat(final Object self, final double arg) { 797 final ContinuousArrayData newData = getContinuousArrayDataCCE(self, Double.class).copy(); //get at least a number array data copy of this data 798 newData.fastPush(arg); //add a double at the end 799 return new NativeArray(newData); 800 } 801 802 /** 803 * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] ) 804 * 805 * @param self self reference 806 * @param arg argument 807 * @return resulting NativeArray 808 */ 809 @SpecializedFunction(linkLogic=ConcatLinkLogic.class) 810 public static NativeArray concat(final Object self, final Object arg) { 811 //arg is [NativeArray] of same type. 812 final ContinuousArrayData selfData = getContinuousArrayDataCCE(self); 813 final ContinuousArrayData newData; 814 815 if (arg instanceof NativeArray) { 816 final ContinuousArrayData argData = (ContinuousArrayData)((NativeArray)arg).getArray(); 817 if (argData.isEmpty()) { 818 newData = selfData.copy(); 819 } else if (selfData.isEmpty()) { 820 newData = argData.copy(); 821 } else { 822 final Class<?> widestElementType = selfData.widest(argData).getBoxedElementType(); 823 newData = ((ContinuousArrayData)selfData.convert(widestElementType)).fastConcat((ContinuousArrayData)argData.convert(widestElementType)); 824 } 825 } else { 826 newData = getContinuousArrayDataCCE(self, Object.class).copy(); 827 newData.fastPush(arg); 828 } 829 830 return new NativeArray(newData); 831 } 832 833 /** 834 * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] ) 835 * 836 * @param self self reference 837 * @param args arguments 838 * @return resulting NativeArray 839 */ 840 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 841 public static NativeArray concat(final Object self, final Object... args) { 842 final ArrayList<Object> list = new ArrayList<>(); 843 844 concatToList(list, Global.toObject(self)); 845 846 for (final Object obj : args) { 847 concatToList(list, obj); 848 } 849 850 return new NativeArray(list.toArray()); 851 } 852 853 private static void concatToList(final ArrayList<Object> list, final Object obj) { 854 final boolean isScriptArray = isArray(obj); 855 final boolean isScriptObject = isScriptArray || obj instanceof ScriptObject; 856 if (isScriptArray || obj instanceof Iterable || (obj != null && obj.getClass().isArray())) { 857 final Iterator<Object> iter = arrayLikeIterator(obj, true); 858 if (iter.hasNext()) { 859 for (int i = 0; iter.hasNext(); ++i) { 860 final Object value = iter.next(); 861 final boolean lacksIndex = obj != null && !((ScriptObject)obj).has(i); 862 if (value == ScriptRuntime.UNDEFINED && isScriptObject && lacksIndex) { 863 // TODO: eventually rewrite arrayLikeIterator to use a three-state enum for handling 864 // UNDEFINED instead of an "includeUndefined" boolean with states SKIP, INCLUDE, 865 // RETURN_EMPTY. Until then, this is how we'll make sure that empty elements don't make it 866 // into the concatenated array. 867 list.add(ScriptRuntime.EMPTY); 868 } else { 869 list.add(value); 870 } 871 } 872 } else if (!isScriptArray) { 873 list.add(obj); // add empty object, but not an empty array 874 } 875 } else { 876 // single element, add it 877 list.add(obj); 878 } 879 } 880 881 /** 882 * ECMA 15.4.4.5 Array.prototype.join (separator) 883 * 884 * @param self self reference 885 * @param separator element separator 886 * @return string representation after join 887 */ 888 @Function(attributes = Attribute.NOT_ENUMERABLE) 889 public static String join(final Object self, final Object separator) { 890 final StringBuilder sb = new StringBuilder(); 891 final Iterator<Object> iter = arrayLikeIterator(self, true); 892 final String sep = separator == ScriptRuntime.UNDEFINED ? "," : JSType.toString(separator); 893 894 while (iter.hasNext()) { 895 final Object obj = iter.next(); 896 897 if (obj != null && obj != ScriptRuntime.UNDEFINED) { 898 sb.append(JSType.toString(obj)); 899 } 900 901 if (iter.hasNext()) { 902 sb.append(sep); 903 } 904 } 905 906 return sb.toString(); 907 } 908 909 /** 910 * Specialization of pop for ContinuousArrayData 911 * The link guard checks that the array is continuous AND not empty. 912 * The runtime guard checks that the guard is continuous (CCE otherwise) 913 * 914 * Primitive specialization, {@link LinkLogic} 915 * 916 * @param self self reference 917 * @return element popped 918 * @throws ClassCastException if array is empty, facilitating Undefined return value 919 */ 920 @SpecializedFunction(name="pop", linkLogic=PopLinkLogic.class) 921 public static int popInt(final Object self) { 922 //must be non empty IntArrayData 923 return getContinuousNonEmptyArrayDataCCE(self, IntElements.class).fastPopInt(); 924 } 925 926 /** 927 * Specialization of pop for ContinuousArrayData 928 * 929 * Primitive specialization, {@link LinkLogic} 930 * 931 * @param self self reference 932 * @return element popped 933 * @throws ClassCastException if array is empty, facilitating Undefined return value 934 */ 935 @SpecializedFunction(name="pop", linkLogic=PopLinkLogic.class) 936 public static long popLong(final Object self) { 937 //must be non empty Int or LongArrayData 938 return getContinuousNonEmptyArrayDataCCE(self, IntOrLongElements.class).fastPopLong(); 939 } 940 941 /** 942 * Specialization of pop for ContinuousArrayData 943 * 944 * Primitive specialization, {@link LinkLogic} 945 * 946 * @param self self reference 947 * @return element popped 948 * @throws ClassCastException if array is empty, facilitating Undefined return value 949 */ 950 @SpecializedFunction(name="pop", linkLogic=PopLinkLogic.class) 951 public static double popDouble(final Object self) { 952 //must be non empty int long or double array data 953 return getContinuousNonEmptyArrayDataCCE(self, NumericElements.class).fastPopDouble(); 954 } 955 956 /** 957 * Specialization of pop for ContinuousArrayData 958 * 959 * Primitive specialization, {@link LinkLogic} 960 * 961 * @param self self reference 962 * @return element popped 963 * @throws ClassCastException if array is empty, facilitating Undefined return value 964 */ 965 @SpecializedFunction(name="pop", linkLogic=PopLinkLogic.class) 966 public static Object popObject(final Object self) { 967 //can be any data, because the numeric ones will throw cce and force relink 968 return getContinuousArrayDataCCE(self, null).fastPopObject(); 969 } 970 971 /** 972 * ECMA 15.4.4.6 Array.prototype.pop () 973 * 974 * @param self self reference 975 * @return array after pop 976 */ 977 @Function(attributes = Attribute.NOT_ENUMERABLE) 978 public static Object pop(final Object self) { 979 try { 980 final ScriptObject sobj = (ScriptObject)self; 981 982 if (bulkable(sobj)) { 983 return sobj.getArray().pop(); 984 } 985 986 final long len = JSType.toUint32(sobj.getLength()); 987 988 if (len == 0) { 989 sobj.set("length", 0, CALLSITE_STRICT); 990 return ScriptRuntime.UNDEFINED; 991 } 992 993 final long index = len - 1; 994 final Object element = sobj.get(index); 995 996 sobj.delete(index, true); 997 sobj.set("length", index, CALLSITE_STRICT); 998 999 return element; 1000 } catch (final ClassCastException | NullPointerException e) { 1001 throw typeError("not.an.object", ScriptRuntime.safeToString(self)); 1002 } 1003 } 1004 1005 /** 1006 * ECMA 15.4.4.7 Array.prototype.push (args...) 1007 * 1008 * Primitive specialization, {@link LinkLogic} 1009 * 1010 * @param self self reference 1011 * @param arg a primitive to push 1012 * @return array length after push 1013 */ 1014 @SpecializedFunction(linkLogic=PushLinkLogic.class) 1015 public static long push(final Object self, final int arg) { 1016 return getContinuousArrayDataCCE(self, Integer.class).fastPush(arg); 1017 } 1018 1019 /** 1020 * ECMA 15.4.4.7 Array.prototype.push (args...) 1021 * 1022 * Primitive specialization, {@link LinkLogic} 1023 * 1024 * @param self self reference 1025 * @param arg a primitive to push 1026 * @return array length after push 1027 */ 1028 @SpecializedFunction(linkLogic=PushLinkLogic.class) 1029 public static long push(final Object self, final long arg) { 1030 return getContinuousArrayDataCCE(self, Long.class).fastPush(arg); 1031 } 1032 1033 /** 1034 * ECMA 15.4.4.7 Array.prototype.push (args...) 1035 * 1036 * Primitive specialization, {@link LinkLogic} 1037 * 1038 * @param self self reference 1039 * @param arg a primitive to push 1040 * @return array length after push 1041 */ 1042 @SpecializedFunction(linkLogic=PushLinkLogic.class) 1043 public static long push(final Object self, final double arg) { 1044 return getContinuousArrayDataCCE(self, Double.class).fastPush(arg); 1045 } 1046 1047 /** 1048 * ECMA 15.4.4.7 Array.prototype.push (args...) 1049 * 1050 * Primitive specialization, {@link LinkLogic} 1051 * 1052 * @param self self reference 1053 * @param arg a primitive to push 1054 * @return array length after push 1055 */ 1056 @SpecializedFunction(name="push", linkLogic=PushLinkLogic.class) 1057 public static long pushObject(final Object self, final Object arg) { 1058 return getContinuousArrayDataCCE(self, Object.class).fastPush(arg); 1059 } 1060 1061 /** 1062 * ECMA 15.4.4.7 Array.prototype.push (args...) 1063 * 1064 * @param self self reference 1065 * @param args arguments to push 1066 * @return array length after pushes 1067 */ 1068 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 1069 public static Object push(final Object self, final Object... args) { 1070 try { 1071 final ScriptObject sobj = (ScriptObject)self; 1072 1073 if (bulkable(sobj) && sobj.getArray().length() + args.length <= JSType.MAX_UINT) { 1074 final ArrayData newData = sobj.getArray().push(true, args); 1075 sobj.setArray(newData); 1076 return newData.length(); 1077 } 1078 1079 long len = JSType.toUint32(sobj.getLength()); 1080 for (final Object element : args) { 1081 sobj.set(len++, element, CALLSITE_STRICT); 1082 } 1083 sobj.set("length", len, CALLSITE_STRICT); 1084 1085 return len; 1086 } catch (final ClassCastException | NullPointerException e) { 1087 throw typeError(Context.getGlobal(), e, "not.an.object", ScriptRuntime.safeToString(self)); 1088 } 1089 } 1090 1091 /** 1092 * ECMA 15.4.4.7 Array.prototype.push (args...) specialized for single object argument 1093 * 1094 * @param self self reference 1095 * @param arg argument to push 1096 * @return array after pushes 1097 */ 1098 @SpecializedFunction 1099 public static long push(final Object self, final Object arg) { 1100 try { 1101 final ScriptObject sobj = (ScriptObject)self; 1102 final ArrayData arrayData = sobj.getArray(); 1103 final long length = arrayData.length(); 1104 if (bulkable(sobj) && length < JSType.MAX_UINT) { 1105 sobj.setArray(arrayData.push(true, arg)); 1106 return length + 1; 1107 } 1108 1109 long len = JSType.toUint32(sobj.getLength()); 1110 sobj.set(len++, arg, CALLSITE_STRICT); 1111 sobj.set("length", len, CALLSITE_STRICT); 1112 return len; 1113 } catch (final ClassCastException | NullPointerException e) { 1114 throw typeError("not.an.object", ScriptRuntime.safeToString(self)); 1115 } 1116 } 1117 1118 /** 1119 * ECMA 15.4.4.8 Array.prototype.reverse () 1120 * 1121 * @param self self reference 1122 * @return reversed array 1123 */ 1124 @Function(attributes = Attribute.NOT_ENUMERABLE) 1125 public static Object reverse(final Object self) { 1126 try { 1127 final ScriptObject sobj = (ScriptObject)self; 1128 final long len = JSType.toUint32(sobj.getLength()); 1129 final long middle = len / 2; 1130 1131 for (long lower = 0; lower != middle; lower++) { 1132 final long upper = len - lower - 1; 1133 final Object lowerValue = sobj.get(lower); 1134 final Object upperValue = sobj.get(upper); 1135 final boolean lowerExists = sobj.has(lower); 1136 final boolean upperExists = sobj.has(upper); 1137 1138 if (lowerExists && upperExists) { 1139 sobj.set(lower, upperValue, CALLSITE_STRICT); 1140 sobj.set(upper, lowerValue, CALLSITE_STRICT); 1141 } else if (!lowerExists && upperExists) { 1142 sobj.set(lower, upperValue, CALLSITE_STRICT); 1143 sobj.delete(upper, true); 1144 } else if (lowerExists && !upperExists) { 1145 sobj.delete(lower, true); 1146 sobj.set(upper, lowerValue, CALLSITE_STRICT); 1147 } 1148 } 1149 return sobj; 1150 } catch (final ClassCastException | NullPointerException e) { 1151 throw typeError("not.an.object", ScriptRuntime.safeToString(self)); 1152 } 1153 } 1154 1155 /** 1156 * ECMA 15.4.4.9 Array.prototype.shift () 1157 * 1158 * @param self self reference 1159 * @return shifted array 1160 */ 1161 @Function(attributes = Attribute.NOT_ENUMERABLE) 1162 public static Object shift(final Object self) { 1163 final Object obj = Global.toObject(self); 1164 1165 Object first = ScriptRuntime.UNDEFINED; 1166 1167 if (!(obj instanceof ScriptObject)) { 1168 return first; 1169 } 1170 1171 final ScriptObject sobj = (ScriptObject) obj; 1172 1173 long len = JSType.toUint32(sobj.getLength()); 1174 1175 if (len > 0) { 1176 first = sobj.get(0); 1177 1178 if (bulkable(sobj)) { 1179 sobj.getArray().shiftLeft(1); 1180 } else { 1181 boolean hasPrevious = true; 1182 for (long k = 1; k < len; k++) { 1183 final boolean hasCurrent = sobj.has(k); 1184 if (hasCurrent) { 1185 sobj.set(k - 1, sobj.get(k), CALLSITE_STRICT); 1186 } else if (hasPrevious) { 1187 sobj.delete(k - 1, true); 1188 } 1189 hasPrevious = hasCurrent; 1190 } 1191 } 1192 sobj.delete(--len, true); 1193 } else { 1194 len = 0; 1195 } 1196 1197 sobj.set("length", len, CALLSITE_STRICT); 1198 1199 return first; 1200 } 1201 1202 /** 1203 * ECMA 15.4.4.10 Array.prototype.slice ( start [ , end ] ) 1204 * 1205 * @param self self reference 1206 * @param start start of slice (inclusive) 1207 * @param end end of slice (optional, exclusive) 1208 * @return sliced array 1209 */ 1210 @Function(attributes = Attribute.NOT_ENUMERABLE) 1211 public static Object slice(final Object self, final Object start, final Object end) { 1212 final Object obj = Global.toObject(self); 1213 if (!(obj instanceof ScriptObject)) { 1214 return ScriptRuntime.UNDEFINED; 1215 } 1216 1217 final ScriptObject sobj = (ScriptObject)obj; 1218 final long len = JSType.toUint32(sobj.getLength()); 1219 final long relativeStart = JSType.toLong(start); 1220 final long relativeEnd = end == ScriptRuntime.UNDEFINED ? len : JSType.toLong(end); 1221 1222 long k = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len); 1223 final long finale = relativeEnd < 0 ? Math.max(len + relativeEnd, 0) : Math.min(relativeEnd, len); 1224 1225 if (k >= finale) { 1226 return new NativeArray(0); 1227 } 1228 1229 if (bulkable(sobj)) { 1230 return new NativeArray(sobj.getArray().slice(k, finale)); 1231 } 1232 1233 // Construct array with proper length to have a deleted filter on undefined elements 1234 final NativeArray copy = new NativeArray(finale - k); 1235 for (long n = 0; k < finale; n++, k++) { 1236 if (sobj.has(k)) { 1237 copy.defineOwnProperty(ArrayIndex.getArrayIndex(n), sobj.get(k)); 1238 } 1239 } 1240 1241 return copy; 1242 } 1243 1244 private static ScriptFunction compareFunction(final Object comparefn) { 1245 if (comparefn == ScriptRuntime.UNDEFINED) { 1246 return null; 1247 } 1248 1249 if (! (comparefn instanceof ScriptFunction)) { 1250 throw typeError("not.a.function", ScriptRuntime.safeToString(comparefn)); 1251 } 1252 1253 return (ScriptFunction)comparefn; 1254 } 1255 1256 private static Object[] sort(final Object[] array, final Object comparefn) { 1257 final ScriptFunction cmp = compareFunction(comparefn); 1258 1259 final List<Object> list = Arrays.asList(array); 1260 final Object cmpThis = cmp == null || cmp.isStrict() ? ScriptRuntime.UNDEFINED : Global.instance(); 1261 1262 Collections.sort(list, new Comparator<Object>() { 1263 private final MethodHandle call_cmp = getCALL_CMP(); 1264 @Override 1265 public int compare(final Object x, final Object y) { 1266 if (x == ScriptRuntime.UNDEFINED && y == ScriptRuntime.UNDEFINED) { 1267 return 0; 1268 } else if (x == ScriptRuntime.UNDEFINED) { 1269 return 1; 1270 } else if (y == ScriptRuntime.UNDEFINED) { 1271 return -1; 1272 } 1273 1274 if (cmp != null) { 1275 try { 1276 return (int)Math.signum((double)call_cmp.invokeExact(cmp, cmpThis, x, y)); 1277 } catch (final RuntimeException | Error e) { 1278 throw e; 1279 } catch (final Throwable t) { 1280 throw new RuntimeException(t); 1281 } 1282 } 1283 1284 return JSType.toString(x).compareTo(JSType.toString(y)); 1285 } 1286 }); 1287 1288 return list.toArray(new Object[array.length]); 1289 } 1290 1291 /** 1292 * ECMA 15.4.4.11 Array.prototype.sort ( comparefn ) 1293 * 1294 * @param self self reference 1295 * @param comparefn element comparison function 1296 * @return sorted array 1297 */ 1298 @Function(attributes = Attribute.NOT_ENUMERABLE) 1299 public static ScriptObject sort(final Object self, final Object comparefn) { 1300 try { 1301 final ScriptObject sobj = (ScriptObject) self; 1302 final long len = JSType.toUint32(sobj.getLength()); 1303 ArrayData array = sobj.getArray(); 1304 1305 if (len > 1) { 1306 // Get only non-missing elements. Missing elements go at the end 1307 // of the sorted array. So, just don't copy these to sort input. 1308 final ArrayList<Object> src = new ArrayList<>(); 1309 for (long i = 0; i < len; i = array.nextIndex(i)) { 1310 if (array.has((int) i)) { 1311 src.add(array.getObject((int) i)); 1312 } 1313 } 1314 1315 final Object[] sorted = sort(src.toArray(), comparefn); 1316 1317 for (int i = 0; i < sorted.length; i++) { 1318 array = array.set(i, sorted[i], true); 1319 } 1320 1321 // delete missing elements - which are at the end of sorted array 1322 if (sorted.length != len) { 1323 array = array.delete(sorted.length, len - 1); 1324 } 1325 1326 sobj.setArray(array); 1327 } 1328 1329 return sobj; 1330 } catch (final ClassCastException | NullPointerException e) { 1331 throw typeError("not.an.object", ScriptRuntime.safeToString(self)); 1332 } 1333 } 1334 1335 /** 1336 * ECMA 15.4.4.12 Array.prototype.splice ( start, deleteCount [ item1 [ , item2 [ , ... ] ] ] ) 1337 * 1338 * @param self self reference 1339 * @param args arguments 1340 * @return result of splice 1341 */ 1342 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2) 1343 public static Object splice(final Object self, final Object... args) { 1344 final Object obj = Global.toObject(self); 1345 1346 if (!(obj instanceof ScriptObject)) { 1347 return ScriptRuntime.UNDEFINED; 1348 } 1349 1350 final Object start = args.length > 0 ? args[0] : ScriptRuntime.UNDEFINED; 1351 final Object deleteCount = args.length > 1 ? args[1] : ScriptRuntime.UNDEFINED; 1352 1353 Object[] items; 1354 1355 if (args.length > 2) { 1356 items = new Object[args.length - 2]; 1357 System.arraycopy(args, 2, items, 0, items.length); 1358 } else { 1359 items = ScriptRuntime.EMPTY_ARRAY; 1360 } 1361 1362 final ScriptObject sobj = (ScriptObject)obj; 1363 final long len = JSType.toUint32(sobj.getLength()); 1364 final long relativeStart = JSType.toLong(start); 1365 1366 final long actualStart = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len); 1367 final long actualDeleteCount = Math.min(Math.max(JSType.toLong(deleteCount), 0), len - actualStart); 1368 1369 NativeArray returnValue; 1370 1371 if (actualStart <= Integer.MAX_VALUE && actualDeleteCount <= Integer.MAX_VALUE && bulkable(sobj)) { 1372 try { 1373 returnValue = new NativeArray(sobj.getArray().fastSplice((int)actualStart, (int)actualDeleteCount, items.length)); 1374 1375 // Since this is a dense bulkable array we can use faster defineOwnProperty to copy new elements 1376 int k = (int) actualStart; 1377 for (int i = 0; i < items.length; i++, k++) { 1378 sobj.defineOwnProperty(k, items[i]); 1379 } 1380 } catch (final UnsupportedOperationException uoe) { 1381 returnValue = slowSplice(sobj, actualStart, actualDeleteCount, items, len); 1382 } 1383 } else { 1384 returnValue = slowSplice(sobj, actualStart, actualDeleteCount, items, len); 1385 } 1386 1387 return returnValue; 1388 } 1389 1390 private static NativeArray slowSplice(final ScriptObject sobj, final long start, final long deleteCount, final Object[] items, final long len) { 1391 1392 final NativeArray array = new NativeArray(deleteCount); 1393 1394 for (long k = 0; k < deleteCount; k++) { 1395 final long from = start + k; 1396 1397 if (sobj.has(from)) { 1398 array.defineOwnProperty(ArrayIndex.getArrayIndex(k), sobj.get(from)); 1399 } 1400 } 1401 1402 if (items.length < deleteCount) { 1403 for (long k = start; k < len - deleteCount; k++) { 1404 final long from = k + deleteCount; 1405 final long to = k + items.length; 1406 1407 if (sobj.has(from)) { 1408 sobj.set(to, sobj.get(from), CALLSITE_STRICT); 1409 } else { 1410 sobj.delete(to, true); 1411 } 1412 } 1413 1414 for (long k = len; k > len - deleteCount + items.length; k--) { 1415 sobj.delete(k - 1, true); 1416 } 1417 } else if (items.length > deleteCount) { 1418 for (long k = len - deleteCount; k > start; k--) { 1419 final long from = k + deleteCount - 1; 1420 final long to = k + items.length - 1; 1421 1422 if (sobj.has(from)) { 1423 final Object fromValue = sobj.get(from); 1424 sobj.set(to, fromValue, CALLSITE_STRICT); 1425 } else { 1426 sobj.delete(to, true); 1427 } 1428 } 1429 } 1430 1431 long k = start; 1432 for (int i = 0; i < items.length; i++, k++) { 1433 sobj.set(k, items[i], CALLSITE_STRICT); 1434 } 1435 1436 final long newLength = len - deleteCount + items.length; 1437 sobj.set("length", newLength, CALLSITE_STRICT); 1438 1439 return array; 1440 } 1441 1442 /** 1443 * ECMA 15.4.4.13 Array.prototype.unshift ( [ item1 [ , item2 [ , ... ] ] ] ) 1444 * 1445 * @param self self reference 1446 * @param items items for unshift 1447 * @return unshifted array 1448 */ 1449 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 1450 public static Object unshift(final Object self, final Object... items) { 1451 final Object obj = Global.toObject(self); 1452 1453 if (!(obj instanceof ScriptObject)) { 1454 return ScriptRuntime.UNDEFINED; 1455 } 1456 1457 final ScriptObject sobj = (ScriptObject)obj; 1458 final long len = JSType.toUint32(sobj.getLength()); 1459 1460 if (items == null) { 1461 return ScriptRuntime.UNDEFINED; 1462 } 1463 1464 if (bulkable(sobj)) { 1465 sobj.getArray().shiftRight(items.length); 1466 1467 for (int j = 0; j < items.length; j++) { 1468 sobj.setArray(sobj.getArray().set(j, items[j], true)); 1469 } 1470 } else { 1471 for (long k = len; k > 0; k--) { 1472 final long from = k - 1; 1473 final long to = k + items.length - 1; 1474 1475 if (sobj.has(from)) { 1476 final Object fromValue = sobj.get(from); 1477 sobj.set(to, fromValue, CALLSITE_STRICT); 1478 } else { 1479 sobj.delete(to, true); 1480 } 1481 } 1482 1483 for (int j = 0; j < items.length; j++) { 1484 sobj.set(j, items[j], CALLSITE_STRICT); 1485 } 1486 } 1487 1488 final long newLength = len + items.length; 1489 sobj.set("length", newLength, CALLSITE_STRICT); 1490 1491 return newLength; 1492 } 1493 1494 /** 1495 * ECMA 15.4.4.14 Array.prototype.indexOf ( searchElement [ , fromIndex ] ) 1496 * 1497 * @param self self reference 1498 * @param searchElement element to search for 1499 * @param fromIndex start index of search 1500 * @return index of element, or -1 if not found 1501 */ 1502 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 1503 public static long indexOf(final Object self, final Object searchElement, final Object fromIndex) { 1504 try { 1505 final ScriptObject sobj = (ScriptObject)Global.toObject(self); 1506 final long len = JSType.toUint32(sobj.getLength()); 1507 if (len == 0) { 1508 return -1; 1509 } 1510 1511 final long n = JSType.toLong(fromIndex); 1512 if (n >= len) { 1513 return -1; 1514 } 1515 1516 1517 for (long k = Math.max(0, n < 0 ? len - Math.abs(n) : n); k < len; k++) { 1518 if (sobj.has(k)) { 1519 if (ScriptRuntime.EQ_STRICT(sobj.get(k), searchElement)) { 1520 return k; 1521 } 1522 } 1523 } 1524 } catch (final ClassCastException | NullPointerException e) { 1525 //fallthru 1526 } 1527 1528 return -1; 1529 } 1530 1531 /** 1532 * ECMA 15.4.4.15 Array.prototype.lastIndexOf ( searchElement [ , fromIndex ] ) 1533 * 1534 * @param self self reference 1535 * @param args arguments: element to search for and optional from index 1536 * @return index of element, or -1 if not found 1537 */ 1538 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 1539 public static long lastIndexOf(final Object self, final Object... args) { 1540 try { 1541 final ScriptObject sobj = (ScriptObject)Global.toObject(self); 1542 final long len = JSType.toUint32(sobj.getLength()); 1543 1544 if (len == 0) { 1545 return -1; 1546 } 1547 1548 final Object searchElement = args.length > 0 ? args[0] : ScriptRuntime.UNDEFINED; 1549 final long n = args.length > 1 ? JSType.toLong(args[1]) : len - 1; 1550 1551 for (long k = n < 0 ? len - Math.abs(n) : Math.min(n, len - 1); k >= 0; k--) { 1552 if (sobj.has(k)) { 1553 if (ScriptRuntime.EQ_STRICT(sobj.get(k), searchElement)) { 1554 return k; 1555 } 1556 } 1557 } 1558 } catch (final ClassCastException | NullPointerException e) { 1559 throw typeError("not.an.object", ScriptRuntime.safeToString(self)); 1560 } 1561 1562 return -1; 1563 } 1564 1565 /** 1566 * ECMA 15.4.4.16 Array.prototype.every ( callbackfn [ , thisArg ] ) 1567 * 1568 * @param self self reference 1569 * @param callbackfn callback function per element 1570 * @param thisArg this argument 1571 * @return true if callback function return true for every element in the array, false otherwise 1572 */ 1573 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 1574 public static boolean every(final Object self, final Object callbackfn, final Object thisArg) { 1575 return applyEvery(Global.toObject(self), callbackfn, thisArg); 1576 } 1577 1578 private static boolean applyEvery(final Object self, final Object callbackfn, final Object thisArg) { 1579 return new IteratorAction<Boolean>(Global.toObject(self), callbackfn, thisArg, true) { 1580 private final MethodHandle everyInvoker = getEVERY_CALLBACK_INVOKER(); 1581 1582 @Override 1583 protected boolean forEach(final Object val, final long i) throws Throwable { 1584 return result = (boolean)everyInvoker.invokeExact(callbackfn, thisArg, val, i, self); 1585 } 1586 }.apply(); 1587 } 1588 1589 /** 1590 * ECMA 15.4.4.17 Array.prototype.some ( callbackfn [ , thisArg ] ) 1591 * 1592 * @param self self reference 1593 * @param callbackfn callback function per element 1594 * @param thisArg this argument 1595 * @return true if callback function returned true for any element in the array, false otherwise 1596 */ 1597 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 1598 public static boolean some(final Object self, final Object callbackfn, final Object thisArg) { 1599 return new IteratorAction<Boolean>(Global.toObject(self), callbackfn, thisArg, false) { 1600 private final MethodHandle someInvoker = getSOME_CALLBACK_INVOKER(); 1601 1602 @Override 1603 protected boolean forEach(final Object val, final long i) throws Throwable { 1604 return !(result = (boolean)someInvoker.invokeExact(callbackfn, thisArg, val, i, self)); 1605 } 1606 }.apply(); 1607 } 1608 1609 /** 1610 * ECMA 15.4.4.18 Array.prototype.forEach ( callbackfn [ , thisArg ] ) 1611 * 1612 * @param self self reference 1613 * @param callbackfn callback function per element 1614 * @param thisArg this argument 1615 * @return undefined 1616 */ 1617 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 1618 public static Object forEach(final Object self, final Object callbackfn, final Object thisArg) { 1619 return new IteratorAction<Object>(Global.toObject(self), callbackfn, thisArg, ScriptRuntime.UNDEFINED) { 1620 private final MethodHandle forEachInvoker = getFOREACH_CALLBACK_INVOKER(); 1621 1622 @Override 1623 protected boolean forEach(final Object val, final long i) throws Throwable { 1624 forEachInvoker.invokeExact(callbackfn, thisArg, val, i, self); 1625 return true; 1626 } 1627 }.apply(); 1628 } 1629 1630 /** 1631 * ECMA 15.4.4.19 Array.prototype.map ( callbackfn [ , thisArg ] ) 1632 * 1633 * @param self self reference 1634 * @param callbackfn callback function per element 1635 * @param thisArg this argument 1636 * @return array with elements transformed by map function 1637 */ 1638 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 1639 public static NativeArray map(final Object self, final Object callbackfn, final Object thisArg) { 1640 return new IteratorAction<NativeArray>(Global.toObject(self), callbackfn, thisArg, null) { 1641 private final MethodHandle mapInvoker = getMAP_CALLBACK_INVOKER(); 1642 1643 @Override 1644 protected boolean forEach(final Object val, final long i) throws Throwable { 1645 final Object r = mapInvoker.invokeExact(callbackfn, thisArg, val, i, self); 1646 result.defineOwnProperty(ArrayIndex.getArrayIndex(index), r); 1647 return true; 1648 } 1649 1650 @Override 1651 public void applyLoopBegin(final ArrayLikeIterator<Object> iter0) { 1652 // map return array should be of same length as source array 1653 // even if callback reduces source array length 1654 result = new NativeArray(iter0.getLength()); 1655 } 1656 }.apply(); 1657 } 1658 1659 /** 1660 * ECMA 15.4.4.20 Array.prototype.filter ( callbackfn [ , thisArg ] ) 1661 * 1662 * @param self self reference 1663 * @param callbackfn callback function per element 1664 * @param thisArg this argument 1665 * @return filtered array 1666 */ 1667 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 1668 public static NativeArray filter(final Object self, final Object callbackfn, final Object thisArg) { 1669 return new IteratorAction<NativeArray>(Global.toObject(self), callbackfn, thisArg, new NativeArray()) { 1670 private long to = 0; 1671 private final MethodHandle filterInvoker = getFILTER_CALLBACK_INVOKER(); 1672 1673 @Override 1674 protected boolean forEach(final Object val, final long i) throws Throwable { 1675 if ((boolean)filterInvoker.invokeExact(callbackfn, thisArg, val, i, self)) { 1676 result.defineOwnProperty(ArrayIndex.getArrayIndex(to++), val); 1677 } 1678 return true; 1679 } 1680 }.apply(); 1681 } 1682 1683 private static Object reduceInner(final ArrayLikeIterator<Object> iter, final Object self, final Object... args) { 1684 final Object callbackfn = args.length > 0 ? args[0] : ScriptRuntime.UNDEFINED; 1685 final boolean initialValuePresent = args.length > 1; 1686 1687 Object initialValue = initialValuePresent ? args[1] : ScriptRuntime.UNDEFINED; 1688 1689 if (callbackfn == ScriptRuntime.UNDEFINED) { 1690 throw typeError("not.a.function", "undefined"); 1691 } 1692 1693 if (!initialValuePresent) { 1694 if (iter.hasNext()) { 1695 initialValue = iter.next(); 1696 } else { 1697 throw typeError("array.reduce.invalid.init"); 1698 } 1699 } 1700 1701 //if initial value is ScriptRuntime.UNDEFINED - step forward once. 1702 return new IteratorAction<Object>(Global.toObject(self), callbackfn, ScriptRuntime.UNDEFINED, initialValue, iter) { 1703 private final MethodHandle reduceInvoker = getREDUCE_CALLBACK_INVOKER(); 1704 1705 @Override 1706 protected boolean forEach(final Object val, final long i) throws Throwable { 1707 // TODO: why can't I declare the second arg as Undefined.class? 1708 result = reduceInvoker.invokeExact(callbackfn, ScriptRuntime.UNDEFINED, result, val, i, self); 1709 return true; 1710 } 1711 }.apply(); 1712 } 1713 1714 /** 1715 * ECMA 15.4.4.21 Array.prototype.reduce ( callbackfn [ , initialValue ] ) 1716 * 1717 * @param self self reference 1718 * @param args arguments to reduce 1719 * @return accumulated result 1720 */ 1721 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 1722 public static Object reduce(final Object self, final Object... args) { 1723 return reduceInner(arrayLikeIterator(self), self, args); 1724 } 1725 1726 /** 1727 * ECMA 15.4.4.22 Array.prototype.reduceRight ( callbackfn [ , initialValue ] ) 1728 * 1729 * @param self self reference 1730 * @param args arguments to reduce 1731 * @return accumulated result 1732 */ 1733 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 1734 public static Object reduceRight(final Object self, final Object... args) { 1735 return reduceInner(reverseArrayLikeIterator(self), self, args); 1736 } 1737 1738 /** 1739 * Determine if Java bulk array operations may be used on the underlying 1740 * storage. This is possible only if the object's prototype chain is empty 1741 * or each of the prototypes in the chain is empty. 1742 * 1743 * @param self the object to examine 1744 * @return true if optimizable 1745 */ 1746 private static boolean bulkable(final ScriptObject self) { 1747 return self.isArray() && !hasInheritedArrayEntries(self) && !self.isLengthNotWritable(); 1748 } 1749 1750 private static boolean hasInheritedArrayEntries(final ScriptObject self) { 1751 ScriptObject proto = self.getProto(); 1752 while (proto != null) { 1753 if (proto.hasArrayEntries()) { 1754 return true; 1755 } 1756 proto = proto.getProto(); 1757 } 1758 1759 return false; 1760 } 1761 1762 @Override 1763 public String toString() { 1764 return "NativeArray@" + Debug.id(this) + " [" + getArray().getClass().getSimpleName() + ']'; 1765 } 1766 1767 @Override 1768 public SpecializedFunction.LinkLogic getLinkLogic(final Class<? extends LinkLogic> clazz) { 1769 if (clazz == PushLinkLogic.class) { 1770 return pushLinkLogic == null ? new PushLinkLogic(this) : pushLinkLogic; 1771 } else if (clazz == PopLinkLogic.class) { 1772 return popLinkLogic == null ? new PopLinkLogic(this) : pushLinkLogic; 1773 } else if (clazz == ConcatLinkLogic.class) { 1774 return concatLinkLogic == null ? new ConcatLinkLogic(this) : concatLinkLogic; 1775 } 1776 return null; 1777 } 1778 1779 @Override 1780 public boolean hasPerInstanceAssumptions() { 1781 return true; //length writable switchpoint 1782 } 1783 1784 /** 1785 * This is an abstract super class that contains common functionality for all 1786 * specialized optimistic builtins in NativeArray. For example, it handles the 1787 * modification switchpoint which is touched when length is written. 1788 */ 1789 private static abstract class ArrayLinkLogic extends SpecializedFunction.LinkLogic { 1790 private final NativeArray array; 1791 1792 protected ArrayLinkLogic(final NativeArray array) { 1793 this.array = array; 1794 } 1795 1796 private SwitchPoint getSwitchPoint() { 1797 return array.lengthMadeNotWritableSwitchPoint; 1798 } 1799 1800 private SwitchPoint newSwitchPoint() { 1801 assert array.lengthMadeNotWritableSwitchPoint == null; 1802 final SwitchPoint sp = new SwitchPoint(); 1803 array.lengthMadeNotWritableSwitchPoint = sp; 1804 return sp; 1805 } 1806 1807 protected static ContinuousArrayData getContinuousArrayData(final Object self) { 1808 try { 1809 //cast to NativeArray, to avoid cases like x = {0:0, 1:1}, x.length = 2, where we can't use the array push/pop 1810 return (ContinuousArrayData)((NativeArray)self).getArray(); 1811 } catch (final Exception e) { 1812 return null; 1813 } 1814 } 1815 1816 /** 1817 * Push and pop callsites can throw ClassCastException as a mechanism to have them 1818 * relinked - this enabled fast checks of the kind of ((IntArrayData)arrayData).push(x) 1819 * for an IntArrayData only push - if this fails, a CCE will be thrown and we will relink 1820 */ 1821 @Override 1822 public Class<? extends Throwable> getRelinkException() { 1823 return ClassCastException.class; 1824 } 1825 1826 @Override 1827 public boolean hasModificationSwitchPoints() { 1828 return getSwitchPoint() != null; 1829 } 1830 1831 @Override 1832 public boolean hasModificationSwitchPoint(final int index) { 1833 assert index == LENGTH_NOT_WRITABLE_SWITCHPOINT; 1834 return hasModificationSwitchPoints(); 1835 } 1836 1837 @Override 1838 public SwitchPoint getOrCreateModificationSwitchPoint(final int index) { 1839 assert index == LENGTH_NOT_WRITABLE_SWITCHPOINT; 1840 SwitchPoint sp = getSwitchPoint(); 1841 if (sp == null) { 1842 sp = newSwitchPoint(); 1843 } 1844 return sp; 1845 } 1846 1847 @Override 1848 public SwitchPoint[] getOrCreateModificationSwitchPoints() { 1849 return new SwitchPoint[] { getOrCreateModificationSwitchPoint(LENGTH_NOT_WRITABLE_SWITCHPOINT) }; 1850 } 1851 1852 @Override 1853 public void invalidateModificationSwitchPoint(final int index) { 1854 assert index == LENGTH_NOT_WRITABLE_SWITCHPOINT; 1855 invalidateModificationSwitchPoints(); 1856 } 1857 1858 @Override 1859 public void invalidateModificationSwitchPoints() { 1860 final SwitchPoint sp = getSwitchPoint(); 1861 assert sp != null : "trying to invalidate non-existant modified SwitchPoint"; 1862 if (!sp.hasBeenInvalidated()) { 1863 SwitchPoint.invalidateAll(new SwitchPoint[] { sp }); 1864 } 1865 } 1866 1867 @Override 1868 public boolean hasInvalidatedModificationSwitchPoint(final int index) { 1869 assert index == LENGTH_NOT_WRITABLE_SWITCHPOINT; 1870 return hasInvalidatedModificationSwitchPoints(); 1871 } 1872 1873 @Override 1874 public boolean hasInvalidatedModificationSwitchPoints() { 1875 final SwitchPoint sp = getSwitchPoint(); 1876 return sp != null && !sp.hasBeenInvalidated(); 1877 } 1878 } 1879 1880 /** 1881 * This is linker logic for optimistic concatenations 1882 */ 1883 private static final class ConcatLinkLogic extends ArrayLinkLogic { 1884 private ConcatLinkLogic(final NativeArray array) { 1885 super(array); 1886 } 1887 1888 @Override 1889 public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) { 1890 final Object[] args = request.getArguments(); 1891 1892 if (args.length != 3) { //single argument check 1893 return false; 1894 } 1895 1896 final ContinuousArrayData selfData = getContinuousArrayData(self); 1897 if (selfData == null) { 1898 return false; 1899 } 1900 1901 final Object arg = args[2]; 1902 //args[2] continuousarray or non arraydata, let past non array datas 1903 if (arg instanceof NativeArray) { 1904 final ContinuousArrayData argData = getContinuousArrayData(arg); 1905 if (argData == null) { 1906 return false; 1907 } 1908 } 1909 1910 return true; 1911 } 1912 } 1913 1914 /** 1915 * This is linker logic for optimistic pushes 1916 */ 1917 private static final class PushLinkLogic extends ArrayLinkLogic { 1918 private PushLinkLogic(final NativeArray array) { 1919 super(array); 1920 } 1921 1922 @Override 1923 public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) { 1924 return getContinuousArrayData(self) != null; 1925 } 1926 } 1927 1928 /** 1929 * This is linker logic for optimistic pops 1930 */ 1931 private static final class PopLinkLogic extends ArrayLinkLogic { 1932 private PopLinkLogic(final NativeArray array) { 1933 super(array); 1934 } 1935 1936 /** 1937 * We need to check if we are dealing with a continuous non empty array data here, 1938 * as pop with a primitive return value returns undefined for arrays with length 0 1939 */ 1940 @Override 1941 public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) { 1942 final ContinuousArrayData data = getContinuousNonEmptyArrayData(self); 1943 if (data != null) { 1944 final Class<?> elementType = data.getElementType(); 1945 final Class<?> returnType = desc.getMethodType().returnType(); 1946 final boolean typeFits = JSType.getAccessorTypeIndex(returnType) >= JSType.getAccessorTypeIndex(elementType); 1947 return typeFits; 1948 } 1949 return false; 1950 } 1951 1952 private static ContinuousArrayData getContinuousNonEmptyArrayData(final Object self) { 1953 final ContinuousArrayData data = getContinuousArrayData(self); 1954 if (data != null) { 1955 return data.length() == 0 ? null : data; 1956 } 1957 return null; 1958 } 1959 } 1960 1961 //runtime calls for push and pops. they could be used as guards, but they also perform the runtime logic, 1962 //so rather than synthesizing them into a guard method handle that would also perform the push on the 1963 //retrieved receiver, we use this as runtime logic 1964 1965 //TODO - fold these into the Link logics, but I'll do that as a later step, as I want to do a checkin 1966 //where everything works first 1967 1968 private static final <T> ContinuousArrayData getContinuousNonEmptyArrayDataCCE(final Object self, final Class<T> clazz) { 1969 try { 1970 @SuppressWarnings("unchecked") 1971 final ContinuousArrayData data = (ContinuousArrayData)(T)((NativeArray)self).getArray(); 1972 if (data.length() != 0L) { 1973 return data; //if length is 0 we cannot pop and have to relink, because then we'd have to return an undefined, which is a wider type than e.g. int 1974 } 1975 } catch (final NullPointerException e) { 1976 //fallthru 1977 } 1978 throw new ClassCastException(); 1979 } 1980 1981 private static final ContinuousArrayData getContinuousArrayDataCCE(final Object self) { 1982 try { 1983 return (ContinuousArrayData)((NativeArray)self).getArray(); 1984 } catch (final NullPointerException e) { 1985 throw new ClassCastException(); 1986 } 1987 } 1988 1989 private static final ContinuousArrayData getContinuousArrayDataCCE(final Object self, final Class<?> elementType) { 1990 try { 1991 return (ContinuousArrayData)((NativeArray)self).getArray(elementType); //ensure element type can fit "elementType" 1992 } catch (final NullPointerException e) { 1993 throw new ClassCastException(); 1994 } 1995 } 1996} 1997