NativeArray.java revision 1593:8faab9cd4b95
1282785Sgjb/* 2282785Sgjb * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. 3282785Sgjb * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4282785Sgjb * 5282785Sgjb * This code is free software; you can redistribute it and/or modify it 6282785Sgjb * under the terms of the GNU General Public License version 2 only, as 7282785Sgjb * published by the Free Software Foundation. Oracle designates this 8282785Sgjb * particular file as subject to the "Classpath" exception as provided 9282785Sgjb * by Oracle in the LICENSE file that accompanied this code. 10282785Sgjb * 11282785Sgjb * This code is distributed in the hope that it will be useful, but WITHOUT 12282785Sgjb * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13282785Sgjb * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14282787Sgjb * version 2 for more details (a copy is included in the LICENSE file that 15282785Sgjb * accompanied this code). 16282785Sgjb * 17282785Sgjb * You should have received a copy of the GNU General Public License version 18282785Sgjb * 2 along with this work; if not, write to the Free Software Foundation, 19282785Sgjb * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20282785Sgjb * 21282785Sgjb * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22282785Sgjb * or visit www.oracle.com if you need additional information or have any 23282785Sgjb * questions. 24282785Sgjb */ 25282785Sgjb 26282785Sgjbpackage jdk.nashorn.internal.objects; 27282785Sgjb 28282785Sgjbimport static jdk.nashorn.internal.runtime.ECMAErrors.rangeError; 29282785Sgjbimport static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 30282785Sgjbimport static jdk.nashorn.internal.runtime.PropertyDescriptor.VALUE; 31282785Sgjbimport static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE; 32282785Sgjbimport static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex; 33282785Sgjbimport static jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator.arrayLikeIterator; 34282785Sgjbimport static jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator.reverseArrayLikeIterator; 35282785Sgjbimport static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_STRICT; 36282785Sgjb 37282785Sgjbimport java.lang.invoke.MethodHandle; 38282785Sgjbimport java.util.ArrayList; 39282785Sgjbimport java.util.Arrays; 40282785Sgjbimport java.util.Collections; 41282785Sgjbimport java.util.Comparator; 42282785Sgjbimport java.util.Iterator; 43282785Sgjbimport java.util.List; 44282785Sgjbimport java.util.concurrent.Callable; 45282785Sgjbimport jdk.dynalink.CallSiteDescriptor; 46282785Sgjbimport jdk.dynalink.linker.GuardedInvocation; 47282785Sgjbimport jdk.dynalink.linker.LinkRequest; 48282785Sgjbimport jdk.nashorn.api.scripting.JSObject; 49282785Sgjbimport jdk.nashorn.internal.objects.annotations.Attribute; 50282785Sgjbimport jdk.nashorn.internal.objects.annotations.Constructor; 51282785Sgjbimport jdk.nashorn.internal.objects.annotations.Function; 52282785Sgjbimport jdk.nashorn.internal.objects.annotations.Getter; 53282785Sgjbimport jdk.nashorn.internal.objects.annotations.ScriptClass; 54282785Sgjbimport jdk.nashorn.internal.objects.annotations.Setter; 55282785Sgjbimport jdk.nashorn.internal.objects.annotations.SpecializedFunction; 56282785Sgjbimport jdk.nashorn.internal.objects.annotations.SpecializedFunction.LinkLogic; 57282785Sgjbimport jdk.nashorn.internal.objects.annotations.Where; 58282789Sgjbimport jdk.nashorn.internal.runtime.Context; 59282789Sgjbimport jdk.nashorn.internal.runtime.Debug; 60282789Sgjbimport jdk.nashorn.internal.runtime.JSType; 61282785Sgjbimport jdk.nashorn.internal.runtime.OptimisticBuiltins; 62282785Sgjbimport jdk.nashorn.internal.runtime.PropertyDescriptor; 63282785Sgjbimport jdk.nashorn.internal.runtime.PropertyMap; 64282787Sgjbimport jdk.nashorn.internal.runtime.ScriptFunction; 65282787Sgjbimport jdk.nashorn.internal.runtime.ScriptObject; 66282787Sgjbimport jdk.nashorn.internal.runtime.ScriptRuntime; 67282787Sgjbimport jdk.nashorn.internal.runtime.Undefined; 68282787Sgjbimport jdk.nashorn.internal.runtime.arrays.ArrayData; 69282792Sgjbimport jdk.nashorn.internal.runtime.arrays.ArrayIndex; 70282792Sgjbimport jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator; 71282787Sgjbimport jdk.nashorn.internal.runtime.arrays.ContinuousArrayData; 72282791Sgjbimport jdk.nashorn.internal.runtime.arrays.IntElements; 73282793Sgjbimport jdk.nashorn.internal.runtime.arrays.IntOrLongElements; 74282793Sgjbimport jdk.nashorn.internal.runtime.arrays.IteratorAction; 75282793Sgjbimport jdk.nashorn.internal.runtime.arrays.NumericElements; 76282793Sgjbimport jdk.nashorn.internal.runtime.linker.Bootstrap; 77282791Sgjbimport jdk.nashorn.internal.runtime.linker.InvokeByName; 78282787Sgjb 79282787Sgjb/** 80282785Sgjb * Runtime representation of a JavaScript array. NativeArray only holds numeric 81282785Sgjb * keyed values. All other values are stored in spill. 82282785Sgjb */ 83282785Sgjb@ScriptClass("Array") 84282785Sgjbpublic final class NativeArray extends ScriptObject implements OptimisticBuiltins { 85282785Sgjb private static final Object JOIN = new Object(); 86282785Sgjb private static final Object EVERY_CALLBACK_INVOKER = new Object(); 87282785Sgjb private static final Object SOME_CALLBACK_INVOKER = new Object(); 88282785Sgjb private static final Object FOREACH_CALLBACK_INVOKER = new Object(); 89282785Sgjb private static final Object MAP_CALLBACK_INVOKER = new Object(); 90282785Sgjb private static final Object FILTER_CALLBACK_INVOKER = new Object(); 91282785Sgjb private static final Object REDUCE_CALLBACK_INVOKER = new Object(); 92282785Sgjb private static final Object CALL_CMP = new Object(); 93282785Sgjb private static final Object TO_LOCALE_STRING = new Object(); 94282796Sgjb 95282794Sgjb /* 96282795Sgjb * Constructors. 97282785Sgjb */ 98282785Sgjb NativeArray() { 99282785Sgjb this(ArrayData.initialArray()); 100282785Sgjb } 101282785Sgjb 102282785Sgjb NativeArray(final long length) { 103282785Sgjb this(ArrayData.allocate(length)); 104282787Sgjb } 105282785Sgjb 106282787Sgjb NativeArray(final int[] array) { 107282785Sgjb this(ArrayData.allocate(array)); 108282785Sgjb } 109282785Sgjb 110282785Sgjb NativeArray(final double[] array) { 111282785Sgjb this(ArrayData.allocate(array)); 112282785Sgjb } 113282785Sgjb 114282785Sgjb NativeArray(final long[] array) { 115282785Sgjb this(ArrayData.allocate(array.length)); 116282785Sgjb 117282785Sgjb ArrayData arrayData = this.getArray(); 118282785Sgjb Class<?> widest = int.class; 119282785Sgjb 120282785Sgjb for (int index = 0; index < array.length; index++) { 121282785Sgjb final long value = array[index]; 122282785Sgjb 123282785Sgjb if (widest == int.class && JSType.isRepresentableAsInt(value)) { 124282785Sgjb arrayData = arrayData.set(index, (int) value, false); 125282785Sgjb } else if (widest != Object.class && JSType.isRepresentableAsDouble(value)) { 126282785Sgjb arrayData = arrayData.set(index, (double) value, false); 127282785Sgjb widest = double.class; 128282785Sgjb } else { 129282785Sgjb arrayData = arrayData.set(index, (Object) value, false); 130282785Sgjb widest = Object.class; 131282785Sgjb } 132282785Sgjb } 133282785Sgjb 134282785Sgjb this.setArray(arrayData); 135282785Sgjb } 136282785Sgjb 137282785Sgjb NativeArray(final Object[] array) { 138282785Sgjb this(ArrayData.allocate(array.length)); 139282785Sgjb 140282785Sgjb ArrayData arrayData = this.getArray(); 141282785Sgjb 142282785Sgjb for (int index = 0; index < array.length; index++) { 143282785Sgjb final Object value = array[index]; 144282789Sgjb 145282785Sgjb if (value == ScriptRuntime.EMPTY) { 146 arrayData = arrayData.delete(index); 147 } else { 148 arrayData = arrayData.set(index, value, false); 149 } 150 } 151 152 this.setArray(arrayData); 153 } 154 155 NativeArray(final ArrayData arrayData) { 156 this(arrayData, Global.instance()); 157 } 158 159 NativeArray(final ArrayData arrayData, final Global global) { 160 super(global.getArrayPrototype(), $nasgenmap$); 161 setArray(arrayData); 162 setIsArray(); 163 } 164 165 @Override 166 protected GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) { 167 final GuardedInvocation inv = getArray().findFastGetIndexMethod(getArray().getClass(), desc, request); 168 if (inv != null) { 169 return inv; 170 } 171 return super.findGetIndexMethod(desc, request); 172 } 173 174 @Override 175 protected GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) { 176 final GuardedInvocation inv = getArray().findFastSetIndexMethod(getArray().getClass(), desc, request); 177 if (inv != null) { 178 return inv; 179 } 180 181 return super.findSetIndexMethod(desc, request); 182 } 183 184 private static InvokeByName getJOIN() { 185 return Global.instance().getInvokeByName(JOIN, 186 new Callable<InvokeByName>() { 187 @Override 188 public InvokeByName call() { 189 return new InvokeByName("join", ScriptObject.class); 190 } 191 }); 192 } 193 194 private static MethodHandle createIteratorCallbackInvoker(final Object key, final Class<?> rtype) { 195 return Global.instance().getDynamicInvoker(key, 196 new Callable<MethodHandle>() { 197 @Override 198 public MethodHandle call() { 199 return Bootstrap.createDynamicCallInvoker(rtype, Object.class, Object.class, Object.class, 200 double.class, Object.class); 201 } 202 }); 203 } 204 205 private static MethodHandle getEVERY_CALLBACK_INVOKER() { 206 return createIteratorCallbackInvoker(EVERY_CALLBACK_INVOKER, boolean.class); 207 } 208 209 private static MethodHandle getSOME_CALLBACK_INVOKER() { 210 return createIteratorCallbackInvoker(SOME_CALLBACK_INVOKER, boolean.class); 211 } 212 213 private static MethodHandle getFOREACH_CALLBACK_INVOKER() { 214 return createIteratorCallbackInvoker(FOREACH_CALLBACK_INVOKER, void.class); 215 } 216 217 private static MethodHandle getMAP_CALLBACK_INVOKER() { 218 return createIteratorCallbackInvoker(MAP_CALLBACK_INVOKER, Object.class); 219 } 220 221 private static MethodHandle getFILTER_CALLBACK_INVOKER() { 222 return createIteratorCallbackInvoker(FILTER_CALLBACK_INVOKER, boolean.class); 223 } 224 225 private static MethodHandle getREDUCE_CALLBACK_INVOKER() { 226 return Global.instance().getDynamicInvoker(REDUCE_CALLBACK_INVOKER, 227 new Callable<MethodHandle>() { 228 @Override 229 public MethodHandle call() { 230 return Bootstrap.createDynamicCallInvoker(Object.class, Object.class, 231 Undefined.class, Object.class, Object.class, double.class, Object.class); 232 } 233 }); 234 } 235 236 private static MethodHandle getCALL_CMP() { 237 return Global.instance().getDynamicInvoker(CALL_CMP, 238 new Callable<MethodHandle>() { 239 @Override 240 public MethodHandle call() { 241 return Bootstrap.createDynamicCallInvoker(double.class, 242 ScriptFunction.class, Object.class, Object.class, Object.class); 243 } 244 }); 245 } 246 247 private static InvokeByName getTO_LOCALE_STRING() { 248 return Global.instance().getInvokeByName(TO_LOCALE_STRING, 249 new Callable<InvokeByName>() { 250 @Override 251 public InvokeByName call() { 252 return new InvokeByName("toLocaleString", ScriptObject.class, String.class); 253 } 254 }); 255 } 256 257 // initialized by nasgen 258 private static PropertyMap $nasgenmap$; 259 260 @Override 261 public String getClassName() { 262 return "Array"; 263 } 264 265 @Override 266 public Object getLength() { 267 final long length = getArray().length(); 268 assert length >= 0L; 269 if (length <= Integer.MAX_VALUE) { 270 return (int)length; 271 } 272 return length; 273 } 274 275 private boolean defineLength(final long oldLen, final PropertyDescriptor oldLenDesc, final PropertyDescriptor desc, final boolean reject) { 276 // Step 3a 277 if (!desc.has(VALUE)) { 278 return super.defineOwnProperty("length", desc, reject); 279 } 280 281 // Step 3b 282 final PropertyDescriptor newLenDesc = desc; 283 284 // Step 3c and 3d - get new length and convert to long 285 final long newLen = NativeArray.validLength(newLenDesc.getValue()); 286 287 // Step 3e - note that we need to convert to int or double as long is not considered a JS number type anymore 288 newLenDesc.setValue(JSType.isRepresentableAsInt(newLen) ? Integer.valueOf((int) newLen) : Double.valueOf((double) newLen)); 289 290 // Step 3f 291 // increasing array length - just need to set new length value (and attributes if any) and return 292 if (newLen >= oldLen) { 293 return super.defineOwnProperty("length", newLenDesc, reject); 294 } 295 296 // Step 3g 297 if (!oldLenDesc.isWritable()) { 298 if (reject) { 299 throw typeError("property.not.writable", "length", ScriptRuntime.safeToString(this)); 300 } 301 return false; 302 } 303 304 // Step 3h and 3i 305 final boolean newWritable = !newLenDesc.has(WRITABLE) || newLenDesc.isWritable(); 306 if (!newWritable) { 307 newLenDesc.setWritable(true); 308 } 309 310 // Step 3j and 3k 311 final boolean succeeded = super.defineOwnProperty("length", newLenDesc, reject); 312 if (!succeeded) { 313 return false; 314 } 315 316 // Step 3l 317 // make sure that length is set till the point we can delete the old elements 318 long o = oldLen; 319 while (newLen < o) { 320 o--; 321 final boolean deleteSucceeded = delete(o, false); 322 if (!deleteSucceeded) { 323 newLenDesc.setValue(o + 1); 324 if (!newWritable) { 325 newLenDesc.setWritable(false); 326 } 327 super.defineOwnProperty("length", newLenDesc, false); 328 if (reject) { 329 throw typeError("property.not.writable", "length", ScriptRuntime.safeToString(this)); 330 } 331 return false; 332 } 333 } 334 335 // Step 3m 336 if (!newWritable) { 337 // make 'length' property not writable 338 final ScriptObject newDesc = Global.newEmptyInstance(); 339 newDesc.set(WRITABLE, false, 0); 340 return super.defineOwnProperty("length", newDesc, false); 341 } 342 343 return true; 344 } 345 346 /** 347 * ECMA 15.4.5.1 [[DefineOwnProperty]] ( P, Desc, Throw ) 348 */ 349 @Override 350 public boolean defineOwnProperty(final Object key, final Object propertyDesc, final boolean reject) { 351 final PropertyDescriptor desc = toPropertyDescriptor(Global.instance(), propertyDesc); 352 353 // never be undefined as "length" is always defined and can't be deleted for arrays 354 // Step 1 355 final PropertyDescriptor oldLenDesc = (PropertyDescriptor) super.getOwnPropertyDescriptor("length"); 356 357 // Step 2 358 // get old length and convert to long. Always a Long/Uint32 but we take the safe road. 359 final long oldLen = JSType.toUint32(oldLenDesc.getValue()); 360 361 // Step 3 362 if ("length".equals(key)) { 363 // check for length being made non-writable 364 final boolean result = defineLength(oldLen, oldLenDesc, desc, reject); 365 if (desc.has(WRITABLE) && !desc.isWritable()) { 366 setIsLengthNotWritable(); 367 } 368 return result; 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.toString(), 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 setArray(ArrayData.setIsLengthNotWritable(getArray())); 445 } 446 447 /** 448 * ECMA 15.4.3.2 Array.isArray ( arg ) 449 * 450 * @param self self reference 451 * @param arg argument - object to check 452 * @return true if argument is an array 453 */ 454 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 455 public static boolean isArray(final Object self, final Object arg) { 456 return isArray(arg) || (arg instanceof JSObject && ((JSObject)arg).isArray()); 457 } 458 459 /** 460 * Length getter 461 * @param self self reference 462 * @return the length of the object 463 */ 464 @Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE) 465 public static Object length(final Object self) { 466 if (isArray(self)) { 467 final long length = ((ScriptObject) self).getArray().length(); 468 assert length >= 0L; 469 // Cast to the narrowest supported numeric type to help optimistic type calculator 470 if (length <= Integer.MAX_VALUE) { 471 return (int) length; 472 } 473 return (double) length; 474 } 475 476 return 0; 477 } 478 479 /** 480 * Length setter 481 * @param self self reference 482 * @param length new length property 483 */ 484 @Setter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE) 485 public static void length(final Object self, final Object length) { 486 if (isArray(self)) { 487 ((ScriptObject)self).setLength(validLength(length)); 488 } 489 } 490 491 /** 492 * Prototype length getter 493 * @param self self reference 494 * @return the length of the object 495 */ 496 @Getter(name = "length", where = Where.PROTOTYPE, attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE) 497 public static Object getProtoLength(final Object self) { 498 return length(self); // Same as instance getter but we can't make nasgen use the same method for prototype 499 } 500 501 /** 502 * Prototype length setter 503 * @param self self reference 504 * @param length new length property 505 */ 506 @Setter(name = "length", where = Where.PROTOTYPE, attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE) 507 public static void setProtoLength(final Object self, final Object length) { 508 length(self, length); // Same as instance setter but we can't make nasgen use the same method for prototype 509 } 510 511 static long validLength(final Object length) { 512 // ES5 15.4.5.1, steps 3.c and 3.d require two ToNumber conversions here 513 final double doubleLength = JSType.toNumber(length); 514 if (doubleLength != JSType.toUint32(length)) { 515 throw rangeError("inappropriate.array.length", ScriptRuntime.safeToString(length)); 516 } 517 return (long) doubleLength; 518 } 519 520 /** 521 * ECMA 15.4.4.2 Array.prototype.toString ( ) 522 * 523 * @param self self reference 524 * @return string representation of array 525 */ 526 @Function(attributes = Attribute.NOT_ENUMERABLE) 527 public static Object toString(final Object self) { 528 final Object obj = Global.toObject(self); 529 if (obj instanceof ScriptObject) { 530 final InvokeByName joinInvoker = getJOIN(); 531 final ScriptObject sobj = (ScriptObject)obj; 532 try { 533 final Object join = joinInvoker.getGetter().invokeExact(sobj); 534 if (Bootstrap.isCallable(join)) { 535 return joinInvoker.getInvoker().invokeExact(join, sobj); 536 } 537 } catch (final RuntimeException | Error e) { 538 throw e; 539 } catch (final Throwable t) { 540 throw new RuntimeException(t); 541 } 542 } 543 544 // FIXME: should lookup Object.prototype.toString and call that? 545 return ScriptRuntime.builtinObjectToString(self); 546 } 547 548 /** 549 * Assert that an array is numeric, if not throw type error 550 * @param self self array to check 551 * @return true if numeric 552 */ 553 @Function(attributes = Attribute.NOT_ENUMERABLE) 554 public static Object assertNumeric(final Object self) { 555 if(!(self instanceof NativeArray && ((NativeArray)self).getArray().getOptimisticType().isNumeric())) { 556 throw typeError("not.a.numeric.array", ScriptRuntime.safeToString(self)); 557 } 558 return Boolean.TRUE; 559 } 560 561 /** 562 * ECMA 15.4.4.3 Array.prototype.toLocaleString ( ) 563 * 564 * @param self self reference 565 * @return locale specific string representation for array 566 */ 567 @Function(attributes = Attribute.NOT_ENUMERABLE) 568 public static String toLocaleString(final Object self) { 569 final StringBuilder sb = new StringBuilder(); 570 final Iterator<Object> iter = arrayLikeIterator(self, true); 571 572 while (iter.hasNext()) { 573 final Object obj = iter.next(); 574 575 if (obj != null && obj != ScriptRuntime.UNDEFINED) { 576 final Object val = JSType.toScriptObject(obj); 577 578 try { 579 if (val instanceof ScriptObject) { 580 final InvokeByName localeInvoker = getTO_LOCALE_STRING(); 581 final ScriptObject sobj = (ScriptObject)val; 582 final Object toLocaleString = localeInvoker.getGetter().invokeExact(sobj); 583 584 if (Bootstrap.isCallable(toLocaleString)) { 585 sb.append((String)localeInvoker.getInvoker().invokeExact(toLocaleString, sobj)); 586 } else { 587 throw typeError("not.a.function", "toLocaleString"); 588 } 589 } 590 } catch (final Error|RuntimeException t) { 591 throw t; 592 } catch (final Throwable t) { 593 throw new RuntimeException(t); 594 } 595 } 596 597 if (iter.hasNext()) { 598 sb.append(","); 599 } 600 } 601 602 return sb.toString(); 603 } 604 605 /** 606 * ECMA 15.4.2.2 new Array (len) 607 * 608 * @param newObj was the new operator used to instantiate this array 609 * @param self self reference 610 * @param args arguments (length) 611 * @return the new NativeArray 612 */ 613 @Constructor(arity = 1) 614 public static NativeArray construct(final boolean newObj, final Object self, final Object... args) { 615 switch (args.length) { 616 case 0: 617 return new NativeArray(0); 618 case 1: 619 final Object len = args[0]; 620 if (len instanceof Number) { 621 long length; 622 if (len instanceof Integer || len instanceof Long) { 623 length = ((Number) len).longValue(); 624 if (length >= 0 && length < JSType.MAX_UINT) { 625 return new NativeArray(length); 626 } 627 } 628 629 length = JSType.toUint32(len); 630 631 /* 632 * If the argument len is a Number and ToUint32(len) is equal to 633 * len, then the length property of the newly constructed object 634 * is set to ToUint32(len). If the argument len is a Number and 635 * ToUint32(len) is not equal to len, a RangeError exception is 636 * thrown. 637 */ 638 final double numberLength = ((Number) len).doubleValue(); 639 if (length != numberLength) { 640 throw rangeError("inappropriate.array.length", JSType.toString(numberLength)); 641 } 642 643 return new NativeArray(length); 644 } 645 /* 646 * If the argument len is not a Number, then the length property of 647 * the newly constructed object is set to 1 and the 0 property of 648 * the newly constructed object is set to len 649 */ 650 return new NativeArray(new Object[]{args[0]}); 651 //fallthru 652 default: 653 return new NativeArray(args); 654 } 655 } 656 657 /** 658 * ECMA 15.4.2.2 new Array (len) 659 * 660 * Specialized constructor for zero arguments - empty array 661 * 662 * @param newObj was the new operator used to instantiate this array 663 * @param self self reference 664 * @return the new NativeArray 665 */ 666 @SpecializedFunction(isConstructor=true) 667 public static NativeArray construct(final boolean newObj, final Object self) { 668 return new NativeArray(0); 669 } 670 671 /** 672 * ECMA 15.4.2.2 new Array (len) 673 * 674 * Specialized constructor for zero arguments - empty array 675 * 676 * @param newObj was the new operator used to instantiate this array 677 * @param self self reference 678 * @param element first element 679 * @return the new NativeArray 680 */ 681 @SpecializedFunction(isConstructor=true) 682 public static Object construct(final boolean newObj, final Object self, final boolean element) { 683 return new NativeArray(new Object[] { element }); 684 } 685 686 /** 687 * ECMA 15.4.2.2 new Array (len) 688 * 689 * Specialized constructor for one integer argument (length) 690 * 691 * @param newObj was the new operator used to instantiate this array 692 * @param self self reference 693 * @param length array length 694 * @return the new NativeArray 695 */ 696 @SpecializedFunction(isConstructor=true) 697 public static NativeArray construct(final boolean newObj, final Object self, final int length) { 698 if (length >= 0) { 699 return new NativeArray(length); 700 } 701 702 return construct(newObj, self, new Object[]{length}); 703 } 704 705 /** 706 * ECMA 15.4.2.2 new Array (len) 707 * 708 * Specialized constructor for one long argument (length) 709 * 710 * @param newObj was the new operator used to instantiate this array 711 * @param self self reference 712 * @param length array length 713 * @return the new NativeArray 714 */ 715 @SpecializedFunction(isConstructor=true) 716 public static NativeArray construct(final boolean newObj, final Object self, final long length) { 717 if (length >= 0L && length <= JSType.MAX_UINT) { 718 return new NativeArray(length); 719 } 720 721 return construct(newObj, self, new Object[]{length}); 722 } 723 724 /** 725 * ECMA 15.4.2.2 new Array (len) 726 * 727 * Specialized constructor for one double argument (length) 728 * 729 * @param newObj was the new operator used to instantiate this array 730 * @param self self reference 731 * @param length array length 732 * @return the new NativeArray 733 */ 734 @SpecializedFunction(isConstructor=true) 735 public static NativeArray construct(final boolean newObj, final Object self, final double length) { 736 final long uint32length = JSType.toUint32(length); 737 738 if (uint32length == length) { 739 return new NativeArray(uint32length); 740 } 741 742 return construct(newObj, self, new Object[]{length}); 743 } 744 745 /** 746 * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] ) 747 * 748 * @param self self reference 749 * @param arg argument 750 * @return resulting NativeArray 751 */ 752 @SpecializedFunction(linkLogic=ConcatLinkLogic.class) 753 public static NativeArray concat(final Object self, final int arg) { 754 final ContinuousArrayData newData = getContinuousArrayDataCCE(self, Integer.class).copy(); //get at least an integer data copy of this data 755 newData.fastPush(arg); //add an integer to its end 756 return new NativeArray(newData); 757 } 758 759 /** 760 * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] ) 761 * 762 * @param self self reference 763 * @param arg argument 764 * @return resulting NativeArray 765 */ 766 @SpecializedFunction(linkLogic=ConcatLinkLogic.class) 767 public static NativeArray concat(final Object self, final long arg) { 768 final ContinuousArrayData newData = getContinuousArrayDataCCE(self, Long.class).copy(); //get at least a long array data copy of this data 769 newData.fastPush(arg); //add a long at the end 770 return new NativeArray(newData); 771 } 772 773 /** 774 * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] ) 775 * 776 * @param self self reference 777 * @param arg argument 778 * @return resulting NativeArray 779 */ 780 @SpecializedFunction(linkLogic=ConcatLinkLogic.class) 781 public static NativeArray concat(final Object self, final double arg) { 782 final ContinuousArrayData newData = getContinuousArrayDataCCE(self, Double.class).copy(); //get at least a number array data copy of this data 783 newData.fastPush(arg); //add a double at the end 784 return new NativeArray(newData); 785 } 786 787 /** 788 * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] ) 789 * 790 * @param self self reference 791 * @param arg argument 792 * @return resulting NativeArray 793 */ 794 @SpecializedFunction(linkLogic=ConcatLinkLogic.class) 795 public static NativeArray concat(final Object self, final Object arg) { 796 //arg is [NativeArray] of same type. 797 final ContinuousArrayData selfData = getContinuousArrayDataCCE(self); 798 final ContinuousArrayData newData; 799 800 if (arg instanceof NativeArray) { 801 final ContinuousArrayData argData = (ContinuousArrayData)((NativeArray)arg).getArray(); 802 if (argData.isEmpty()) { 803 newData = selfData.copy(); 804 } else if (selfData.isEmpty()) { 805 newData = argData.copy(); 806 } else { 807 final Class<?> widestElementType = selfData.widest(argData).getBoxedElementType(); 808 newData = ((ContinuousArrayData)selfData.convert(widestElementType)).fastConcat((ContinuousArrayData)argData.convert(widestElementType)); 809 } 810 } else { 811 newData = getContinuousArrayDataCCE(self, Object.class).copy(); 812 newData.fastPush(arg); 813 } 814 815 return new NativeArray(newData); 816 } 817 818 /** 819 * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] ) 820 * 821 * @param self self reference 822 * @param args arguments 823 * @return resulting NativeArray 824 */ 825 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 826 public static NativeArray concat(final Object self, final Object... args) { 827 final ArrayList<Object> list = new ArrayList<>(); 828 829 concatToList(list, Global.toObject(self)); 830 831 for (final Object obj : args) { 832 concatToList(list, obj); 833 } 834 835 return new NativeArray(list.toArray()); 836 } 837 838 private static void concatToList(final ArrayList<Object> list, final Object obj) { 839 final boolean isScriptArray = isArray(obj); 840 final boolean isScriptObject = isScriptArray || obj instanceof ScriptObject; 841 if (isScriptArray || obj instanceof Iterable || (obj != null && obj.getClass().isArray())) { 842 final Iterator<Object> iter = arrayLikeIterator(obj, true); 843 if (iter.hasNext()) { 844 for (int i = 0; iter.hasNext(); ++i) { 845 final Object value = iter.next(); 846 final boolean lacksIndex = obj != null && !((ScriptObject)obj).has(i); 847 if (value == ScriptRuntime.UNDEFINED && isScriptObject && lacksIndex) { 848 // TODO: eventually rewrite arrayLikeIterator to use a three-state enum for handling 849 // UNDEFINED instead of an "includeUndefined" boolean with states SKIP, INCLUDE, 850 // RETURN_EMPTY. Until then, this is how we'll make sure that empty elements don't make it 851 // into the concatenated array. 852 list.add(ScriptRuntime.EMPTY); 853 } else { 854 list.add(value); 855 } 856 } 857 } else if (!isScriptArray) { 858 list.add(obj); // add empty object, but not an empty array 859 } 860 } else { 861 // single element, add it 862 list.add(obj); 863 } 864 } 865 866 /** 867 * ECMA 15.4.4.5 Array.prototype.join (separator) 868 * 869 * @param self self reference 870 * @param separator element separator 871 * @return string representation after join 872 */ 873 @Function(attributes = Attribute.NOT_ENUMERABLE) 874 public static String join(final Object self, final Object separator) { 875 final StringBuilder sb = new StringBuilder(); 876 final Iterator<Object> iter = arrayLikeIterator(self, true); 877 final String sep = separator == ScriptRuntime.UNDEFINED ? "," : JSType.toString(separator); 878 879 while (iter.hasNext()) { 880 final Object obj = iter.next(); 881 882 if (obj != null && obj != ScriptRuntime.UNDEFINED) { 883 sb.append(JSType.toString(obj)); 884 } 885 886 if (iter.hasNext()) { 887 sb.append(sep); 888 } 889 } 890 891 return sb.toString(); 892 } 893 894 /** 895 * Specialization of pop for ContinuousArrayData 896 * The link guard checks that the array is continuous AND not empty. 897 * The runtime guard checks that the guard is continuous (CCE otherwise) 898 * 899 * Primitive specialization, {@link LinkLogic} 900 * 901 * @param self self reference 902 * @return element popped 903 * @throws ClassCastException if array is empty, facilitating Undefined return value 904 */ 905 @SpecializedFunction(name="pop", linkLogic=PopLinkLogic.class) 906 public static int popInt(final Object self) { 907 //must be non empty IntArrayData 908 return getContinuousNonEmptyArrayDataCCE(self, IntElements.class).fastPopInt(); 909 } 910 911 /** 912 * Specialization of pop for ContinuousArrayData 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 double popDouble(final Object self) { 922 //must be non empty int long or double array data 923 return getContinuousNonEmptyArrayDataCCE(self, NumericElements.class).fastPopDouble(); 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 Object popObject(final Object self) { 937 //can be any data, because the numeric ones will throw cce and force relink 938 return getContinuousArrayDataCCE(self, null).fastPopObject(); 939 } 940 941 /** 942 * ECMA 15.4.4.6 Array.prototype.pop () 943 * 944 * @param self self reference 945 * @return array after pop 946 */ 947 @Function(attributes = Attribute.NOT_ENUMERABLE) 948 public static Object pop(final Object self) { 949 try { 950 final ScriptObject sobj = (ScriptObject)self; 951 952 if (bulkable(sobj)) { 953 return sobj.getArray().pop(); 954 } 955 956 final long len = JSType.toUint32(sobj.getLength()); 957 958 if (len == 0) { 959 sobj.set("length", 0, CALLSITE_STRICT); 960 return ScriptRuntime.UNDEFINED; 961 } 962 963 final long index = len - 1; 964 final Object element = sobj.get(index); 965 966 sobj.delete(index, true); 967 sobj.set("length", index, CALLSITE_STRICT); 968 969 return element; 970 } catch (final ClassCastException | NullPointerException e) { 971 throw typeError("not.an.object", ScriptRuntime.safeToString(self)); 972 } 973 } 974 975 /** 976 * ECMA 15.4.4.7 Array.prototype.push (args...) 977 * 978 * Primitive specialization, {@link LinkLogic} 979 * 980 * @param self self reference 981 * @param arg a primitive to push 982 * @return array length after push 983 */ 984 @SpecializedFunction(linkLogic=PushLinkLogic.class) 985 public static double push(final Object self, final int arg) { 986 return getContinuousArrayDataCCE(self, Integer.class).fastPush(arg); 987 } 988 989 /** 990 * ECMA 15.4.4.7 Array.prototype.push (args...) 991 * 992 * Primitive specialization, {@link LinkLogic} 993 * 994 * @param self self reference 995 * @param arg a primitive to push 996 * @return array length after push 997 */ 998 @SpecializedFunction(linkLogic=PushLinkLogic.class) 999 public static double push(final Object self, final long arg) { 1000 return getContinuousArrayDataCCE(self, Long.class).fastPush(arg); 1001 } 1002 1003 /** 1004 * ECMA 15.4.4.7 Array.prototype.push (args...) 1005 * 1006 * Primitive specialization, {@link LinkLogic} 1007 * 1008 * @param self self reference 1009 * @param arg a primitive to push 1010 * @return array length after push 1011 */ 1012 @SpecializedFunction(linkLogic=PushLinkLogic.class) 1013 public static double push(final Object self, final double arg) { 1014 return getContinuousArrayDataCCE(self, Double.class).fastPush(arg); 1015 } 1016 1017 /** 1018 * ECMA 15.4.4.7 Array.prototype.push (args...) 1019 * 1020 * Primitive specialization, {@link LinkLogic} 1021 * 1022 * @param self self reference 1023 * @param arg a primitive to push 1024 * @return array length after push 1025 */ 1026 @SpecializedFunction(name="push", linkLogic=PushLinkLogic.class) 1027 public static double pushObject(final Object self, final Object arg) { 1028 return getContinuousArrayDataCCE(self, Object.class).fastPush(arg); 1029 } 1030 1031 /** 1032 * ECMA 15.4.4.7 Array.prototype.push (args...) 1033 * 1034 * @param self self reference 1035 * @param args arguments to push 1036 * @return array length after pushes 1037 */ 1038 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 1039 public static Object push(final Object self, final Object... args) { 1040 try { 1041 final ScriptObject sobj = (ScriptObject)self; 1042 1043 if (bulkable(sobj) && sobj.getArray().length() + args.length <= JSType.MAX_UINT) { 1044 final ArrayData newData = sobj.getArray().push(true, args); 1045 sobj.setArray(newData); 1046 return newData.length(); 1047 } 1048 1049 long len = JSType.toUint32(sobj.getLength()); 1050 for (final Object element : args) { 1051 sobj.set(len++, element, CALLSITE_STRICT); 1052 } 1053 sobj.set("length", len, CALLSITE_STRICT); 1054 1055 return len; 1056 } catch (final ClassCastException | NullPointerException e) { 1057 throw typeError(Context.getGlobal(), e, "not.an.object", ScriptRuntime.safeToString(self)); 1058 } 1059 } 1060 1061 /** 1062 * ECMA 15.4.4.7 Array.prototype.push (args...) specialized for single object argument 1063 * 1064 * @param self self reference 1065 * @param arg argument to push 1066 * @return array after pushes 1067 */ 1068 @SpecializedFunction 1069 public static double push(final Object self, final Object arg) { 1070 try { 1071 final ScriptObject sobj = (ScriptObject)self; 1072 final ArrayData arrayData = sobj.getArray(); 1073 final long length = arrayData.length(); 1074 if (bulkable(sobj) && length < JSType.MAX_UINT) { 1075 sobj.setArray(arrayData.push(true, arg)); 1076 return length + 1; 1077 } 1078 1079 long len = JSType.toUint32(sobj.getLength()); 1080 sobj.set(len++, arg, CALLSITE_STRICT); 1081 sobj.set("length", len, CALLSITE_STRICT); 1082 return len; 1083 } catch (final ClassCastException | NullPointerException e) { 1084 throw typeError("not.an.object", ScriptRuntime.safeToString(self)); 1085 } 1086 } 1087 1088 /** 1089 * ECMA 15.4.4.8 Array.prototype.reverse () 1090 * 1091 * @param self self reference 1092 * @return reversed array 1093 */ 1094 @Function(attributes = Attribute.NOT_ENUMERABLE) 1095 public static Object reverse(final Object self) { 1096 try { 1097 final ScriptObject sobj = (ScriptObject)self; 1098 final long len = JSType.toUint32(sobj.getLength()); 1099 final long middle = len / 2; 1100 1101 for (long lower = 0; lower != middle; lower++) { 1102 final long upper = len - lower - 1; 1103 final Object lowerValue = sobj.get(lower); 1104 final Object upperValue = sobj.get(upper); 1105 final boolean lowerExists = sobj.has(lower); 1106 final boolean upperExists = sobj.has(upper); 1107 1108 if (lowerExists && upperExists) { 1109 sobj.set(lower, upperValue, CALLSITE_STRICT); 1110 sobj.set(upper, lowerValue, CALLSITE_STRICT); 1111 } else if (!lowerExists && upperExists) { 1112 sobj.set(lower, upperValue, CALLSITE_STRICT); 1113 sobj.delete(upper, true); 1114 } else if (lowerExists && !upperExists) { 1115 sobj.delete(lower, true); 1116 sobj.set(upper, lowerValue, CALLSITE_STRICT); 1117 } 1118 } 1119 return sobj; 1120 } catch (final ClassCastException | NullPointerException e) { 1121 throw typeError("not.an.object", ScriptRuntime.safeToString(self)); 1122 } 1123 } 1124 1125 /** 1126 * ECMA 15.4.4.9 Array.prototype.shift () 1127 * 1128 * @param self self reference 1129 * @return shifted array 1130 */ 1131 @Function(attributes = Attribute.NOT_ENUMERABLE) 1132 public static Object shift(final Object self) { 1133 final Object obj = Global.toObject(self); 1134 1135 Object first = ScriptRuntime.UNDEFINED; 1136 1137 if (!(obj instanceof ScriptObject)) { 1138 return first; 1139 } 1140 1141 final ScriptObject sobj = (ScriptObject) obj; 1142 1143 long len = JSType.toUint32(sobj.getLength()); 1144 1145 if (len > 0) { 1146 first = sobj.get(0); 1147 1148 if (bulkable(sobj)) { 1149 sobj.getArray().shiftLeft(1); 1150 } else { 1151 boolean hasPrevious = true; 1152 for (long k = 1; k < len; k++) { 1153 final boolean hasCurrent = sobj.has(k); 1154 if (hasCurrent) { 1155 sobj.set(k - 1, sobj.get(k), CALLSITE_STRICT); 1156 } else if (hasPrevious) { 1157 sobj.delete(k - 1, true); 1158 } 1159 hasPrevious = hasCurrent; 1160 } 1161 } 1162 sobj.delete(--len, true); 1163 } else { 1164 len = 0; 1165 } 1166 1167 sobj.set("length", len, CALLSITE_STRICT); 1168 1169 return first; 1170 } 1171 1172 /** 1173 * ECMA 15.4.4.10 Array.prototype.slice ( start [ , end ] ) 1174 * 1175 * @param self self reference 1176 * @param start start of slice (inclusive) 1177 * @param end end of slice (optional, exclusive) 1178 * @return sliced array 1179 */ 1180 @Function(attributes = Attribute.NOT_ENUMERABLE) 1181 public static Object slice(final Object self, final Object start, final Object end) { 1182 final Object obj = Global.toObject(self); 1183 if (!(obj instanceof ScriptObject)) { 1184 return ScriptRuntime.UNDEFINED; 1185 } 1186 1187 final ScriptObject sobj = (ScriptObject)obj; 1188 final long len = JSType.toUint32(sobj.getLength()); 1189 final long relativeStart = JSType.toLong(start); 1190 final long relativeEnd = end == ScriptRuntime.UNDEFINED ? len : JSType.toLong(end); 1191 1192 long k = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len); 1193 final long finale = relativeEnd < 0 ? Math.max(len + relativeEnd, 0) : Math.min(relativeEnd, len); 1194 1195 if (k >= finale) { 1196 return new NativeArray(0); 1197 } 1198 1199 if (bulkable(sobj)) { 1200 return new NativeArray(sobj.getArray().slice(k, finale)); 1201 } 1202 1203 // Construct array with proper length to have a deleted filter on undefined elements 1204 final NativeArray copy = new NativeArray(finale - k); 1205 for (long n = 0; k < finale; n++, k++) { 1206 if (sobj.has(k)) { 1207 copy.defineOwnProperty(ArrayIndex.getArrayIndex(n), sobj.get(k)); 1208 } 1209 } 1210 1211 return copy; 1212 } 1213 1214 private static ScriptFunction compareFunction(final Object comparefn) { 1215 if (comparefn == ScriptRuntime.UNDEFINED) { 1216 return null; 1217 } 1218 1219 if (! (comparefn instanceof ScriptFunction)) { 1220 throw typeError("not.a.function", ScriptRuntime.safeToString(comparefn)); 1221 } 1222 1223 return (ScriptFunction)comparefn; 1224 } 1225 1226 private static Object[] sort(final Object[] array, final Object comparefn) { 1227 final ScriptFunction cmp = compareFunction(comparefn); 1228 1229 final List<Object> list = Arrays.asList(array); 1230 final Object cmpThis = cmp == null || cmp.isStrict() ? ScriptRuntime.UNDEFINED : Global.instance(); 1231 1232 try { 1233 Collections.sort(list, new Comparator<Object>() { 1234 private final MethodHandle call_cmp = getCALL_CMP(); 1235 @Override 1236 public int compare(final Object x, final Object y) { 1237 if (x == ScriptRuntime.UNDEFINED && y == ScriptRuntime.UNDEFINED) { 1238 return 0; 1239 } else if (x == ScriptRuntime.UNDEFINED) { 1240 return 1; 1241 } else if (y == ScriptRuntime.UNDEFINED) { 1242 return -1; 1243 } 1244 1245 if (cmp != null) { 1246 try { 1247 return (int)Math.signum((double)call_cmp.invokeExact(cmp, cmpThis, x, y)); 1248 } catch (final RuntimeException | Error e) { 1249 throw e; 1250 } catch (final Throwable t) { 1251 throw new RuntimeException(t); 1252 } 1253 } 1254 1255 return JSType.toString(x).compareTo(JSType.toString(y)); 1256 } 1257 }); 1258 } catch (final IllegalArgumentException iae) { 1259 // Collections.sort throws IllegalArgumentException when 1260 // Comparison method violates its general contract 1261 1262 // See ECMA spec 15.4.4.11 Array.prototype.sort (comparefn). 1263 // If "comparefn" is not undefined and is not a consistent 1264 // comparison function for the elements of this array, the 1265 // behaviour of sort is implementation-defined. 1266 } 1267 1268 return list.toArray(new Object[array.length]); 1269 } 1270 1271 /** 1272 * ECMA 15.4.4.11 Array.prototype.sort ( comparefn ) 1273 * 1274 * @param self self reference 1275 * @param comparefn element comparison function 1276 * @return sorted array 1277 */ 1278 @Function(attributes = Attribute.NOT_ENUMERABLE) 1279 public static ScriptObject sort(final Object self, final Object comparefn) { 1280 try { 1281 final ScriptObject sobj = (ScriptObject) self; 1282 final long len = JSType.toUint32(sobj.getLength()); 1283 ArrayData array = sobj.getArray(); 1284 1285 if (len > 1) { 1286 // Get only non-missing elements. Missing elements go at the end 1287 // of the sorted array. So, just don't copy these to sort input. 1288 final ArrayList<Object> src = new ArrayList<>(); 1289 1290 for (final Iterator<Long> iter = array.indexIterator(); iter.hasNext(); ) { 1291 final long index = iter.next(); 1292 if (index >= len) { 1293 break; 1294 } 1295 src.add(array.getObject((int)index)); 1296 } 1297 1298 final Object[] sorted = sort(src.toArray(), comparefn); 1299 1300 for (int i = 0; i < sorted.length; i++) { 1301 array = array.set(i, sorted[i], true); 1302 } 1303 1304 // delete missing elements - which are at the end of sorted array 1305 if (sorted.length != len) { 1306 array = array.delete(sorted.length, len - 1); 1307 } 1308 1309 sobj.setArray(array); 1310 } 1311 1312 return sobj; 1313 } catch (final ClassCastException | NullPointerException e) { 1314 throw typeError("not.an.object", ScriptRuntime.safeToString(self)); 1315 } 1316 } 1317 1318 /** 1319 * ECMA 15.4.4.12 Array.prototype.splice ( start, deleteCount [ item1 [ , item2 [ , ... ] ] ] ) 1320 * 1321 * @param self self reference 1322 * @param args arguments 1323 * @return result of splice 1324 */ 1325 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2) 1326 public static Object splice(final Object self, final Object... args) { 1327 final Object obj = Global.toObject(self); 1328 1329 if (!(obj instanceof ScriptObject)) { 1330 return ScriptRuntime.UNDEFINED; 1331 } 1332 1333 final Object start = args.length > 0 ? args[0] : ScriptRuntime.UNDEFINED; 1334 final Object deleteCount = args.length > 1 ? args[1] : ScriptRuntime.UNDEFINED; 1335 1336 Object[] items; 1337 1338 if (args.length > 2) { 1339 items = new Object[args.length - 2]; 1340 System.arraycopy(args, 2, items, 0, items.length); 1341 } else { 1342 items = ScriptRuntime.EMPTY_ARRAY; 1343 } 1344 1345 final ScriptObject sobj = (ScriptObject)obj; 1346 final long len = JSType.toUint32(sobj.getLength()); 1347 final long relativeStart = JSType.toLong(start); 1348 1349 final long actualStart = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len); 1350 final long actualDeleteCount = Math.min(Math.max(JSType.toLong(deleteCount), 0), len - actualStart); 1351 1352 NativeArray returnValue; 1353 1354 if (actualStart <= Integer.MAX_VALUE && actualDeleteCount <= Integer.MAX_VALUE && bulkable(sobj)) { 1355 try { 1356 returnValue = new NativeArray(sobj.getArray().fastSplice((int)actualStart, (int)actualDeleteCount, items.length)); 1357 1358 // Since this is a dense bulkable array we can use faster defineOwnProperty to copy new elements 1359 int k = (int) actualStart; 1360 for (int i = 0; i < items.length; i++, k++) { 1361 sobj.defineOwnProperty(k, items[i]); 1362 } 1363 } catch (final UnsupportedOperationException uoe) { 1364 returnValue = slowSplice(sobj, actualStart, actualDeleteCount, items, len); 1365 } 1366 } else { 1367 returnValue = slowSplice(sobj, actualStart, actualDeleteCount, items, len); 1368 } 1369 1370 return returnValue; 1371 } 1372 1373 private static NativeArray slowSplice(final ScriptObject sobj, final long start, final long deleteCount, final Object[] items, final long len) { 1374 1375 final NativeArray array = new NativeArray(deleteCount); 1376 1377 for (long k = 0; k < deleteCount; k++) { 1378 final long from = start + k; 1379 1380 if (sobj.has(from)) { 1381 array.defineOwnProperty(ArrayIndex.getArrayIndex(k), sobj.get(from)); 1382 } 1383 } 1384 1385 if (items.length < deleteCount) { 1386 for (long k = start; k < len - deleteCount; k++) { 1387 final long from = k + deleteCount; 1388 final long to = k + items.length; 1389 1390 if (sobj.has(from)) { 1391 sobj.set(to, sobj.get(from), CALLSITE_STRICT); 1392 } else { 1393 sobj.delete(to, true); 1394 } 1395 } 1396 1397 for (long k = len; k > len - deleteCount + items.length; k--) { 1398 sobj.delete(k - 1, true); 1399 } 1400 } else if (items.length > deleteCount) { 1401 for (long k = len - deleteCount; k > start; k--) { 1402 final long from = k + deleteCount - 1; 1403 final long to = k + items.length - 1; 1404 1405 if (sobj.has(from)) { 1406 final Object fromValue = sobj.get(from); 1407 sobj.set(to, fromValue, CALLSITE_STRICT); 1408 } else { 1409 sobj.delete(to, true); 1410 } 1411 } 1412 } 1413 1414 long k = start; 1415 for (int i = 0; i < items.length; i++, k++) { 1416 sobj.set(k, items[i], CALLSITE_STRICT); 1417 } 1418 1419 final long newLength = len - deleteCount + items.length; 1420 sobj.set("length", newLength, CALLSITE_STRICT); 1421 1422 return array; 1423 } 1424 1425 /** 1426 * ECMA 15.4.4.13 Array.prototype.unshift ( [ item1 [ , item2 [ , ... ] ] ] ) 1427 * 1428 * @param self self reference 1429 * @param items items for unshift 1430 * @return unshifted array 1431 */ 1432 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 1433 public static Object unshift(final Object self, final Object... items) { 1434 final Object obj = Global.toObject(self); 1435 1436 if (!(obj instanceof ScriptObject)) { 1437 return ScriptRuntime.UNDEFINED; 1438 } 1439 1440 final ScriptObject sobj = (ScriptObject)obj; 1441 final long len = JSType.toUint32(sobj.getLength()); 1442 1443 if (items == null) { 1444 return ScriptRuntime.UNDEFINED; 1445 } 1446 1447 if (bulkable(sobj)) { 1448 sobj.getArray().shiftRight(items.length); 1449 1450 for (int j = 0; j < items.length; j++) { 1451 sobj.setArray(sobj.getArray().set(j, items[j], true)); 1452 } 1453 } else { 1454 for (long k = len; k > 0; k--) { 1455 final long from = k - 1; 1456 final long to = k + items.length - 1; 1457 1458 if (sobj.has(from)) { 1459 final Object fromValue = sobj.get(from); 1460 sobj.set(to, fromValue, CALLSITE_STRICT); 1461 } else { 1462 sobj.delete(to, true); 1463 } 1464 } 1465 1466 for (int j = 0; j < items.length; j++) { 1467 sobj.set(j, items[j], CALLSITE_STRICT); 1468 } 1469 } 1470 1471 final long newLength = len + items.length; 1472 sobj.set("length", newLength, CALLSITE_STRICT); 1473 1474 return newLength; 1475 } 1476 1477 /** 1478 * ECMA 15.4.4.14 Array.prototype.indexOf ( searchElement [ , fromIndex ] ) 1479 * 1480 * @param self self reference 1481 * @param searchElement element to search for 1482 * @param fromIndex start index of search 1483 * @return index of element, or -1 if not found 1484 */ 1485 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 1486 public static double indexOf(final Object self, final Object searchElement, final Object fromIndex) { 1487 try { 1488 final ScriptObject sobj = (ScriptObject)Global.toObject(self); 1489 final long len = JSType.toUint32(sobj.getLength()); 1490 if (len == 0) { 1491 return -1; 1492 } 1493 1494 final long n = JSType.toLong(fromIndex); 1495 if (n >= len) { 1496 return -1; 1497 } 1498 1499 1500 for (long k = Math.max(0, n < 0 ? len - Math.abs(n) : n); k < len; k++) { 1501 if (sobj.has(k)) { 1502 if (ScriptRuntime.EQ_STRICT(sobj.get(k), searchElement)) { 1503 return k; 1504 } 1505 } 1506 } 1507 } catch (final ClassCastException | NullPointerException e) { 1508 //fallthru 1509 } 1510 1511 return -1; 1512 } 1513 1514 /** 1515 * ECMA 15.4.4.15 Array.prototype.lastIndexOf ( searchElement [ , fromIndex ] ) 1516 * 1517 * @param self self reference 1518 * @param args arguments: element to search for and optional from index 1519 * @return index of element, or -1 if not found 1520 */ 1521 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 1522 public static double lastIndexOf(final Object self, final Object... args) { 1523 try { 1524 final ScriptObject sobj = (ScriptObject)Global.toObject(self); 1525 final long len = JSType.toUint32(sobj.getLength()); 1526 1527 if (len == 0) { 1528 return -1; 1529 } 1530 1531 final Object searchElement = args.length > 0 ? args[0] : ScriptRuntime.UNDEFINED; 1532 final long n = args.length > 1 ? JSType.toLong(args[1]) : len - 1; 1533 1534 for (long k = n < 0 ? len - Math.abs(n) : Math.min(n, len - 1); k >= 0; k--) { 1535 if (sobj.has(k)) { 1536 if (ScriptRuntime.EQ_STRICT(sobj.get(k), searchElement)) { 1537 return k; 1538 } 1539 } 1540 } 1541 } catch (final ClassCastException | NullPointerException e) { 1542 throw typeError("not.an.object", ScriptRuntime.safeToString(self)); 1543 } 1544 1545 return -1; 1546 } 1547 1548 /** 1549 * ECMA 15.4.4.16 Array.prototype.every ( callbackfn [ , thisArg ] ) 1550 * 1551 * @param self self reference 1552 * @param callbackfn callback function per element 1553 * @param thisArg this argument 1554 * @return true if callback function return true for every element in the array, false otherwise 1555 */ 1556 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 1557 public static boolean every(final Object self, final Object callbackfn, final Object thisArg) { 1558 return applyEvery(Global.toObject(self), callbackfn, thisArg); 1559 } 1560 1561 private static boolean applyEvery(final Object self, final Object callbackfn, final Object thisArg) { 1562 return new IteratorAction<Boolean>(Global.toObject(self), callbackfn, thisArg, true) { 1563 private final MethodHandle everyInvoker = getEVERY_CALLBACK_INVOKER(); 1564 1565 @Override 1566 protected boolean forEach(final Object val, final double i) throws Throwable { 1567 return result = (boolean)everyInvoker.invokeExact(callbackfn, thisArg, val, i, self); 1568 } 1569 }.apply(); 1570 } 1571 1572 /** 1573 * ECMA 15.4.4.17 Array.prototype.some ( callbackfn [ , thisArg ] ) 1574 * 1575 * @param self self reference 1576 * @param callbackfn callback function per element 1577 * @param thisArg this argument 1578 * @return true if callback function returned true for any element in the array, false otherwise 1579 */ 1580 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 1581 public static boolean some(final Object self, final Object callbackfn, final Object thisArg) { 1582 return new IteratorAction<Boolean>(Global.toObject(self), callbackfn, thisArg, false) { 1583 private final MethodHandle someInvoker = getSOME_CALLBACK_INVOKER(); 1584 1585 @Override 1586 protected boolean forEach(final Object val, final double i) throws Throwable { 1587 return !(result = (boolean)someInvoker.invokeExact(callbackfn, thisArg, val, i, self)); 1588 } 1589 }.apply(); 1590 } 1591 1592 /** 1593 * ECMA 15.4.4.18 Array.prototype.forEach ( callbackfn [ , thisArg ] ) 1594 * 1595 * @param self self reference 1596 * @param callbackfn callback function per element 1597 * @param thisArg this argument 1598 * @return undefined 1599 */ 1600 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 1601 public static Object forEach(final Object self, final Object callbackfn, final Object thisArg) { 1602 return new IteratorAction<Object>(Global.toObject(self), callbackfn, thisArg, ScriptRuntime.UNDEFINED) { 1603 private final MethodHandle forEachInvoker = getFOREACH_CALLBACK_INVOKER(); 1604 1605 @Override 1606 protected boolean forEach(final Object val, final double i) throws Throwable { 1607 forEachInvoker.invokeExact(callbackfn, thisArg, val, i, self); 1608 return true; 1609 } 1610 }.apply(); 1611 } 1612 1613 /** 1614 * ECMA 15.4.4.19 Array.prototype.map ( callbackfn [ , thisArg ] ) 1615 * 1616 * @param self self reference 1617 * @param callbackfn callback function per element 1618 * @param thisArg this argument 1619 * @return array with elements transformed by map function 1620 */ 1621 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 1622 public static NativeArray map(final Object self, final Object callbackfn, final Object thisArg) { 1623 return new IteratorAction<NativeArray>(Global.toObject(self), callbackfn, thisArg, null) { 1624 private final MethodHandle mapInvoker = getMAP_CALLBACK_INVOKER(); 1625 1626 @Override 1627 protected boolean forEach(final Object val, final double i) throws Throwable { 1628 final Object r = mapInvoker.invokeExact(callbackfn, thisArg, val, i, self); 1629 result.defineOwnProperty(ArrayIndex.getArrayIndex(index), r); 1630 return true; 1631 } 1632 1633 @Override 1634 public void applyLoopBegin(final ArrayLikeIterator<Object> iter0) { 1635 // map return array should be of same length as source array 1636 // even if callback reduces source array length 1637 result = new NativeArray(iter0.getLength()); 1638 } 1639 }.apply(); 1640 } 1641 1642 /** 1643 * ECMA 15.4.4.20 Array.prototype.filter ( callbackfn [ , thisArg ] ) 1644 * 1645 * @param self self reference 1646 * @param callbackfn callback function per element 1647 * @param thisArg this argument 1648 * @return filtered array 1649 */ 1650 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 1651 public static NativeArray filter(final Object self, final Object callbackfn, final Object thisArg) { 1652 return new IteratorAction<NativeArray>(Global.toObject(self), callbackfn, thisArg, new NativeArray()) { 1653 private long to = 0; 1654 private final MethodHandle filterInvoker = getFILTER_CALLBACK_INVOKER(); 1655 1656 @Override 1657 protected boolean forEach(final Object val, final double i) throws Throwable { 1658 if ((boolean)filterInvoker.invokeExact(callbackfn, thisArg, val, i, self)) { 1659 result.defineOwnProperty(ArrayIndex.getArrayIndex(to++), val); 1660 } 1661 return true; 1662 } 1663 }.apply(); 1664 } 1665 1666 private static Object reduceInner(final ArrayLikeIterator<Object> iter, final Object self, final Object... args) { 1667 final Object callbackfn = args.length > 0 ? args[0] : ScriptRuntime.UNDEFINED; 1668 final boolean initialValuePresent = args.length > 1; 1669 1670 Object initialValue = initialValuePresent ? args[1] : ScriptRuntime.UNDEFINED; 1671 1672 if (callbackfn == ScriptRuntime.UNDEFINED) { 1673 throw typeError("not.a.function", "undefined"); 1674 } 1675 1676 if (!initialValuePresent) { 1677 if (iter.hasNext()) { 1678 initialValue = iter.next(); 1679 } else { 1680 throw typeError("array.reduce.invalid.init"); 1681 } 1682 } 1683 1684 //if initial value is ScriptRuntime.UNDEFINED - step forward once. 1685 return new IteratorAction<Object>(Global.toObject(self), callbackfn, ScriptRuntime.UNDEFINED, initialValue, iter) { 1686 private final MethodHandle reduceInvoker = getREDUCE_CALLBACK_INVOKER(); 1687 1688 @Override 1689 protected boolean forEach(final Object val, final double i) throws Throwable { 1690 // TODO: why can't I declare the second arg as Undefined.class? 1691 result = reduceInvoker.invokeExact(callbackfn, ScriptRuntime.UNDEFINED, result, val, i, self); 1692 return true; 1693 } 1694 }.apply(); 1695 } 1696 1697 /** 1698 * ECMA 15.4.4.21 Array.prototype.reduce ( callbackfn [ , initialValue ] ) 1699 * 1700 * @param self self reference 1701 * @param args arguments to reduce 1702 * @return accumulated result 1703 */ 1704 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 1705 public static Object reduce(final Object self, final Object... args) { 1706 return reduceInner(arrayLikeIterator(self), self, args); 1707 } 1708 1709 /** 1710 * ECMA 15.4.4.22 Array.prototype.reduceRight ( callbackfn [ , initialValue ] ) 1711 * 1712 * @param self self reference 1713 * @param args arguments to reduce 1714 * @return accumulated result 1715 */ 1716 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 1717 public static Object reduceRight(final Object self, final Object... args) { 1718 return reduceInner(reverseArrayLikeIterator(self), self, args); 1719 } 1720 1721 /** 1722 * Determine if Java bulk array operations may be used on the underlying 1723 * storage. This is possible only if the object's prototype chain is empty 1724 * or each of the prototypes in the chain is empty. 1725 * 1726 * @param self the object to examine 1727 * @return true if optimizable 1728 */ 1729 private static boolean bulkable(final ScriptObject self) { 1730 return self.isArray() && !hasInheritedArrayEntries(self) && !self.isLengthNotWritable(); 1731 } 1732 1733 private static boolean hasInheritedArrayEntries(final ScriptObject self) { 1734 ScriptObject proto = self.getProto(); 1735 while (proto != null) { 1736 if (proto.hasArrayEntries()) { 1737 return true; 1738 } 1739 proto = proto.getProto(); 1740 } 1741 1742 return false; 1743 } 1744 1745 @Override 1746 public String toString() { 1747 return "NativeArray@" + Debug.id(this) + " [" + getArray().getClass().getSimpleName() + ']'; 1748 } 1749 1750 @Override 1751 public SpecializedFunction.LinkLogic getLinkLogic(final Class<? extends LinkLogic> clazz) { 1752 if (clazz == PushLinkLogic.class) { 1753 return PushLinkLogic.INSTANCE; 1754 } else if (clazz == PopLinkLogic.class) { 1755 return PopLinkLogic.INSTANCE; 1756 } else if (clazz == ConcatLinkLogic.class) { 1757 return ConcatLinkLogic.INSTANCE; 1758 } 1759 return null; 1760 } 1761 1762 @Override 1763 public boolean hasPerInstanceAssumptions() { 1764 return true; //length writable switchpoint 1765 } 1766 1767 /** 1768 * This is an abstract super class that contains common functionality for all 1769 * specialized optimistic builtins in NativeArray. For example, it handles the 1770 * modification switchpoint which is touched when length is written. 1771 */ 1772 private static abstract class ArrayLinkLogic extends SpecializedFunction.LinkLogic { 1773 protected ArrayLinkLogic() { 1774 } 1775 1776 protected static ContinuousArrayData getContinuousArrayData(final Object self) { 1777 try { 1778 //cast to NativeArray, to avoid cases like x = {0:0, 1:1}, x.length = 2, where we can't use the array push/pop 1779 return (ContinuousArrayData)((NativeArray)self).getArray(); 1780 } catch (final Exception e) { 1781 return null; 1782 } 1783 } 1784 1785 /** 1786 * Push and pop callsites can throw ClassCastException as a mechanism to have them 1787 * relinked - this enabled fast checks of the kind of ((IntArrayData)arrayData).push(x) 1788 * for an IntArrayData only push - if this fails, a CCE will be thrown and we will relink 1789 */ 1790 @Override 1791 public Class<? extends Throwable> getRelinkException() { 1792 return ClassCastException.class; 1793 } 1794 } 1795 1796 /** 1797 * This is linker logic for optimistic concatenations 1798 */ 1799 private static final class ConcatLinkLogic extends ArrayLinkLogic { 1800 private static final LinkLogic INSTANCE = new ConcatLinkLogic(); 1801 1802 @Override 1803 public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) { 1804 final Object[] args = request.getArguments(); 1805 1806 if (args.length != 3) { //single argument check 1807 return false; 1808 } 1809 1810 final ContinuousArrayData selfData = getContinuousArrayData(self); 1811 if (selfData == null) { 1812 return false; 1813 } 1814 1815 final Object arg = args[2]; 1816 //args[2] continuousarray or non arraydata, let past non array datas 1817 if (arg instanceof NativeArray) { 1818 final ContinuousArrayData argData = getContinuousArrayData(arg); 1819 if (argData == null) { 1820 return false; 1821 } 1822 } 1823 1824 return true; 1825 } 1826 } 1827 1828 /** 1829 * This is linker logic for optimistic pushes 1830 */ 1831 private static final class PushLinkLogic extends ArrayLinkLogic { 1832 private static final LinkLogic INSTANCE = new PushLinkLogic(); 1833 1834 @Override 1835 public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) { 1836 return getContinuousArrayData(self) != null; 1837 } 1838 } 1839 1840 /** 1841 * This is linker logic for optimistic pops 1842 */ 1843 private static final class PopLinkLogic extends ArrayLinkLogic { 1844 private static final LinkLogic INSTANCE = new PopLinkLogic(); 1845 1846 /** 1847 * We need to check if we are dealing with a continuous non empty array data here, 1848 * as pop with a primitive return value returns undefined for arrays with length 0 1849 */ 1850 @Override 1851 public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) { 1852 final ContinuousArrayData data = getContinuousNonEmptyArrayData(self); 1853 if (data != null) { 1854 final Class<?> elementType = data.getElementType(); 1855 final Class<?> returnType = desc.getMethodType().returnType(); 1856 final boolean typeFits = JSType.getAccessorTypeIndex(returnType) >= JSType.getAccessorTypeIndex(elementType); 1857 return typeFits; 1858 } 1859 return false; 1860 } 1861 1862 private static ContinuousArrayData getContinuousNonEmptyArrayData(final Object self) { 1863 final ContinuousArrayData data = getContinuousArrayData(self); 1864 if (data != null) { 1865 return data.length() == 0 ? null : data; 1866 } 1867 return null; 1868 } 1869 } 1870 1871 //runtime calls for push and pops. they could be used as guards, but they also perform the runtime logic, 1872 //so rather than synthesizing them into a guard method handle that would also perform the push on the 1873 //retrieved receiver, we use this as runtime logic 1874 1875 //TODO - fold these into the Link logics, but I'll do that as a later step, as I want to do a checkin 1876 //where everything works first 1877 1878 private static <T> ContinuousArrayData getContinuousNonEmptyArrayDataCCE(final Object self, final Class<T> clazz) { 1879 try { 1880 @SuppressWarnings("unchecked") 1881 final ContinuousArrayData data = (ContinuousArrayData)(T)((NativeArray)self).getArray(); 1882 if (data.length() != 0L) { 1883 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 1884 } 1885 } catch (final NullPointerException e) { 1886 //fallthru 1887 } 1888 throw new ClassCastException(); 1889 } 1890 1891 private static ContinuousArrayData getContinuousArrayDataCCE(final Object self) { 1892 try { 1893 return (ContinuousArrayData)((NativeArray)self).getArray(); 1894 } catch (final NullPointerException e) { 1895 throw new ClassCastException(); 1896 } 1897 } 1898 1899 private static ContinuousArrayData getContinuousArrayDataCCE(final Object self, final Class<?> elementType) { 1900 try { 1901 return (ContinuousArrayData)((NativeArray)self).getArray(elementType); //ensure element type can fit "elementType" 1902 } catch (final NullPointerException e) { 1903 throw new ClassCastException(); 1904 } 1905 } 1906} 1907