src/jdk/nashorn/internal/objects/NativeArray.java

Tue, 12 Mar 2013 15:30:53 +0100

author
lagergren
date
Tue, 12 Mar 2013 15:30:53 +0100
changeset 137
e15806b9d716
parent 112
267cc4c85160
child 165
e63b20d4f08a
permissions
-rw-r--r--

8009718: Lazy execution architecture continued - ScriptFunctionData is either final or recompilable. Moved ScriptFunctionData creation logic away from runtime to compile time. Prepared for method generation/specialization. Got rid of ScriptFunctionImplTrampoline whose semantics could be done as part of the relinking anyway. Merge with the lookup package change.
Reviewed-by: attila, jlaskey

jlaskey@3 1 /*
jlaskey@7 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
jlaskey@3 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
jlaskey@3 4 *
jlaskey@3 5 * This code is free software; you can redistribute it and/or modify it
jlaskey@3 6 * under the terms of the GNU General Public License version 2 only, as
jlaskey@3 7 * published by the Free Software Foundation. Oracle designates this
jlaskey@3 8 * particular file as subject to the "Classpath" exception as provided
jlaskey@3 9 * by Oracle in the LICENSE file that accompanied this code.
jlaskey@3 10 *
jlaskey@3 11 * This code is distributed in the hope that it will be useful, but WITHOUT
jlaskey@3 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
jlaskey@3 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
jlaskey@3 14 * version 2 for more details (a copy is included in the LICENSE file that
jlaskey@3 15 * accompanied this code).
jlaskey@3 16 *
jlaskey@3 17 * You should have received a copy of the GNU General Public License version
jlaskey@3 18 * 2 along with this work; if not, write to the Free Software Foundation,
jlaskey@3 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
jlaskey@3 20 *
jlaskey@3 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
jlaskey@3 22 * or visit www.oracle.com if you need additional information or have any
jlaskey@3 23 * questions.
jlaskey@3 24 */
jlaskey@3 25
jlaskey@3 26 package jdk.nashorn.internal.objects;
jlaskey@3 27
jlaskey@3 28 import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError;
jlaskey@3 29 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
jlaskey@3 30 import static jdk.nashorn.internal.runtime.PropertyDescriptor.VALUE;
jlaskey@3 31 import static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE;
jlaskey@3 32 import static jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator.arrayLikeIterator;
jlaskey@3 33 import static jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator.reverseArrayLikeIterator;
jlaskey@3 34
jlaskey@3 35 import java.lang.invoke.MethodHandle;
jlaskey@3 36 import java.util.ArrayList;
jlaskey@3 37 import java.util.Arrays;
jlaskey@3 38 import java.util.Collections;
jlaskey@3 39 import java.util.Comparator;
jlaskey@3 40 import java.util.Iterator;
jlaskey@3 41 import java.util.List;
jlaskey@3 42 import jdk.nashorn.internal.objects.annotations.Attribute;
jlaskey@3 43 import jdk.nashorn.internal.objects.annotations.Constructor;
jlaskey@3 44 import jdk.nashorn.internal.objects.annotations.Function;
jlaskey@3 45 import jdk.nashorn.internal.objects.annotations.Getter;
jlaskey@3 46 import jdk.nashorn.internal.objects.annotations.ScriptClass;
jlaskey@3 47 import jdk.nashorn.internal.objects.annotations.Setter;
jlaskey@3 48 import jdk.nashorn.internal.objects.annotations.SpecializedConstructor;
jlaskey@3 49 import jdk.nashorn.internal.objects.annotations.Where;
jlaskey@3 50 import jdk.nashorn.internal.runtime.JSType;
jlaskey@3 51 import jdk.nashorn.internal.runtime.PropertyDescriptor;
jlaskey@3 52 import jdk.nashorn.internal.runtime.ScriptFunction;
jlaskey@3 53 import jdk.nashorn.internal.runtime.ScriptObject;
jlaskey@3 54 import jdk.nashorn.internal.runtime.ScriptRuntime;
jlaskey@3 55 import jdk.nashorn.internal.runtime.Undefined;
jlaskey@3 56 import jdk.nashorn.internal.runtime.arrays.ArrayData;
jlaskey@3 57 import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
jlaskey@3 58 import jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator;
jlaskey@3 59 import jdk.nashorn.internal.runtime.arrays.IteratorAction;
jlaskey@3 60 import jdk.nashorn.internal.runtime.linker.Bootstrap;
jlaskey@3 61 import jdk.nashorn.internal.runtime.linker.InvokeByName;
jlaskey@3 62
jlaskey@3 63 /**
jlaskey@3 64 * Runtime representation of a JavaScript array. NativeArray only holds numeric
jlaskey@3 65 * keyed values. All other values are stored in spill.
jlaskey@3 66 */
jlaskey@3 67 @ScriptClass("Array")
jlaskey@3 68 public final class NativeArray extends ScriptObject {
jlaskey@3 69 private static final InvokeByName JOIN = new InvokeByName("join", ScriptObject.class);
jlaskey@3 70
jlaskey@3 71 private static final MethodHandle EVERY_CALLBACK_INVOKER = createIteratorCallbackInvoker(boolean.class);
jlaskey@3 72 private static final MethodHandle SOME_CALLBACK_INVOKER = createIteratorCallbackInvoker(boolean.class);
jlaskey@3 73 private static final MethodHandle FOREACH_CALLBACK_INVOKER = createIteratorCallbackInvoker(void.class);
jlaskey@3 74 private static final MethodHandle MAP_CALLBACK_INVOKER = createIteratorCallbackInvoker(Object.class);
jlaskey@3 75 private static final MethodHandle FILTER_CALLBACK_INVOKER = createIteratorCallbackInvoker(boolean.class);
jlaskey@3 76
jlaskey@3 77 private static final MethodHandle REDUCE_CALLBACK_INVOKER = Bootstrap.createDynamicInvoker("dyn:call", Object.class,
jlaskey@3 78 Object.class, Undefined.class, Object.class, Object.class, int.class, Object.class);
jlaskey@3 79 private static final MethodHandle CALL_CMP = Bootstrap.createDynamicInvoker("dyn:call", int.class,
jlaskey@3 80 ScriptFunction.class, Object.class, Object.class, Object.class);
jlaskey@3 81
jlaskey@3 82 private static final InvokeByName TO_LOCALE_STRING = new InvokeByName("toLocaleString", ScriptObject.class, String.class);
jlaskey@3 83
jlaskey@3 84
jlaskey@3 85 /*
jlaskey@3 86 * Constructors.
jlaskey@3 87 */
jlaskey@3 88 NativeArray() {
jlaskey@3 89 this(ArrayData.initialArray());
jlaskey@3 90 }
jlaskey@3 91
jlaskey@3 92 NativeArray(final long length) {
jlaskey@3 93 // TODO assert valid index in long before casting
jlaskey@3 94 this(ArrayData.allocate((int) length));
jlaskey@3 95 }
jlaskey@3 96
jlaskey@3 97 NativeArray(final int[] array) {
jlaskey@3 98 this(ArrayData.allocate(array));
jlaskey@3 99 }
jlaskey@3 100
jlaskey@3 101 NativeArray(final long[] array) {
jlaskey@3 102 this(ArrayData.allocate(array));
jlaskey@3 103 }
jlaskey@3 104
jlaskey@3 105 NativeArray(final double[] array) {
jlaskey@3 106 this(ArrayData.allocate(array));
jlaskey@3 107 }
jlaskey@3 108
jlaskey@3 109 NativeArray(final Object[] array) {
jlaskey@3 110 this(ArrayData.allocate(array.length));
jlaskey@3 111
jlaskey@3 112 ArrayData arrayData = this.getArray();
jlaskey@3 113 arrayData.ensure(array.length - 1);
jlaskey@3 114
jlaskey@3 115 for (int index = 0; index < array.length; index++) {
jlaskey@3 116 final Object value = array[index];
jlaskey@3 117
jlaskey@3 118 if (value == ScriptRuntime.EMPTY) {
jlaskey@3 119 arrayData = arrayData.delete(index);
jlaskey@3 120 } else {
sundar@82 121 arrayData = arrayData.set(index, value, isStrictContext());
jlaskey@3 122 }
jlaskey@3 123 }
jlaskey@3 124
jlaskey@3 125 this.setArray(arrayData);
jlaskey@3 126 }
jlaskey@3 127
jlaskey@3 128 private NativeArray(final ArrayData arrayData) {
jlaskey@3 129 setProto(Global.instance().getArrayPrototype());
jlaskey@3 130 this.setArray(arrayData);
jlaskey@3 131 this.setIsArray();
jlaskey@3 132 }
jlaskey@3 133
jlaskey@3 134 @Override
jlaskey@3 135 public String getClassName() {
jlaskey@3 136 return "Array";
jlaskey@3 137 }
jlaskey@3 138
jlaskey@3 139 @Override
jlaskey@3 140 public Object getLength() {
jlaskey@3 141 return getArray().length() & JSType.MAX_UINT;
jlaskey@3 142 }
jlaskey@3 143
jlaskey@3 144 /**
jlaskey@3 145 * ECMA 15.4.5.1 [[DefineOwnProperty]] ( P, Desc, Throw )
jlaskey@3 146 */
jlaskey@3 147 @Override
jlaskey@3 148 public boolean defineOwnProperty(final String key, final Object propertyDesc, final boolean reject) {
jlaskey@3 149 final PropertyDescriptor desc = toPropertyDescriptor(Global.instance(), propertyDesc);
jlaskey@3 150
jlaskey@3 151 // never be undefined as "length" is always defined and can't be deleted for arrays
jlaskey@3 152 // Step 1
jlaskey@3 153 final PropertyDescriptor oldLenDesc = (PropertyDescriptor) super.getOwnPropertyDescriptor("length");
jlaskey@3 154
jlaskey@3 155 // Step 2
jlaskey@3 156 // get old length and convert to long
jlaskey@3 157 long oldLen = NativeArray.validLength(oldLenDesc.getValue(), true);
jlaskey@3 158
jlaskey@3 159 // Step 3
jlaskey@3 160 if ("length".equals(key)) {
jlaskey@3 161 // Step 3a
jlaskey@3 162 if (!desc.has(VALUE)) {
jlaskey@3 163 return super.defineOwnProperty("length", propertyDesc, reject);
jlaskey@3 164 }
jlaskey@3 165
jlaskey@3 166 // Step 3b
jlaskey@3 167 final PropertyDescriptor newLenDesc = desc;
jlaskey@3 168
jlaskey@3 169 // Step 3c and 3d - get new length and convert to long
jlaskey@3 170 final long newLen = NativeArray.validLength(newLenDesc.getValue(), true);
jlaskey@3 171
jlaskey@3 172 // Step 3e
jlaskey@3 173 newLenDesc.setValue(newLen);
jlaskey@3 174
jlaskey@3 175 // Step 3f
jlaskey@3 176 // increasing array length - just need to set new length value (and attributes if any) and return
jlaskey@3 177 if (newLen >= oldLen) {
jlaskey@3 178 return super.defineOwnProperty("length", newLenDesc, reject);
jlaskey@3 179 }
jlaskey@3 180
jlaskey@3 181 // Step 3g
jlaskey@3 182 if (!oldLenDesc.isWritable()) {
jlaskey@3 183 if (reject) {
lagergren@112 184 throw typeError("property.not.writable", "length", ScriptRuntime.safeToString(this));
jlaskey@3 185 }
jlaskey@3 186 return false;
jlaskey@3 187 }
jlaskey@3 188
jlaskey@3 189 // Step 3h and 3i
jlaskey@3 190 final boolean newWritable = (!newLenDesc.has(WRITABLE) || newLenDesc.isWritable());
jlaskey@3 191 if (!newWritable) {
jlaskey@3 192 newLenDesc.setWritable(true);
jlaskey@3 193 }
jlaskey@3 194
jlaskey@3 195 // Step 3j and 3k
jlaskey@3 196 final boolean succeeded = super.defineOwnProperty("length", newLenDesc, reject);
jlaskey@3 197 if (!succeeded) {
jlaskey@3 198 return false;
jlaskey@3 199 }
jlaskey@3 200
jlaskey@3 201 // Step 3l
jlaskey@3 202 // make sure that length is set till the point we can delete the old elements
jlaskey@3 203 while (newLen < oldLen) {
jlaskey@3 204 oldLen--;
jlaskey@3 205 final boolean deleteSucceeded = delete(oldLen, false);
jlaskey@3 206 if (!deleteSucceeded) {
jlaskey@3 207 newLenDesc.setValue(oldLen + 1);
jlaskey@3 208 if (!newWritable) {
jlaskey@3 209 newLenDesc.setWritable(false);
jlaskey@3 210 }
jlaskey@3 211 super.defineOwnProperty("length", newLenDesc, false);
jlaskey@3 212 if (reject) {
lagergren@112 213 throw typeError("property.not.writable", "length", ScriptRuntime.safeToString(this));
jlaskey@3 214 }
jlaskey@3 215 return false;
jlaskey@3 216 }
jlaskey@3 217 }
jlaskey@3 218
jlaskey@3 219 // Step 3m
jlaskey@3 220 if (!newWritable) {
jlaskey@3 221 // make 'length' property not writable
jlaskey@3 222 final ScriptObject newDesc = Global.newEmptyInstance();
jlaskey@3 223 newDesc.set(WRITABLE, false, false);
jlaskey@3 224 return super.defineOwnProperty("length", newDesc, false);
jlaskey@3 225 }
jlaskey@3 226
jlaskey@3 227 return true;
jlaskey@3 228 }
jlaskey@3 229
jlaskey@3 230 // Step 4a
jlaskey@3 231 final int index = ArrayIndex.getArrayIndexNoThrow(key);
jlaskey@3 232 if (ArrayIndex.isValidArrayIndex(index)) {
jlaskey@3 233 final long longIndex = ArrayIndex.toLongIndex(index);
jlaskey@3 234 // Step 4b
jlaskey@3 235 // setting an element beyond current length, but 'length' is not writable
jlaskey@3 236 if (longIndex >= oldLen && !oldLenDesc.isWritable()) {
jlaskey@3 237 if (reject) {
lagergren@112 238 throw typeError("property.not.writable", Long.toString(longIndex), ScriptRuntime.safeToString(this));
jlaskey@3 239 }
jlaskey@3 240 return false;
jlaskey@3 241 }
jlaskey@3 242
jlaskey@3 243 // Step 4c
jlaskey@3 244 // set the new array element
jlaskey@3 245 final boolean succeeded = super.defineOwnProperty(key, propertyDesc, false);
jlaskey@3 246
jlaskey@3 247 // Step 4d
jlaskey@3 248 if (!succeeded) {
jlaskey@3 249 if (reject) {
lagergren@112 250 throw typeError("cant.redefine.property", key, ScriptRuntime.safeToString(this));
jlaskey@3 251 }
jlaskey@3 252 return false;
jlaskey@3 253 }
jlaskey@3 254
jlaskey@3 255 // Step 4e -- adjust new length based on new element index that is set
jlaskey@3 256 if (longIndex >= oldLen) {
jlaskey@3 257 oldLenDesc.setValue(longIndex + 1);
jlaskey@3 258 super.defineOwnProperty("length", oldLenDesc, false);
jlaskey@3 259 }
jlaskey@3 260
jlaskey@3 261 // Step 4f
jlaskey@3 262 return true;
jlaskey@3 263 }
jlaskey@3 264
jlaskey@3 265 // not an index property
jlaskey@3 266 return super.defineOwnProperty(key, propertyDesc, reject);
jlaskey@3 267 }
jlaskey@3 268
jlaskey@3 269 /**
jlaskey@3 270 * Return the array contents upcasted as an ObjectArray, regardless of
jlaskey@3 271 * representation
jlaskey@3 272 *
jlaskey@3 273 * @return an object array
jlaskey@3 274 */
jlaskey@3 275 public Object[] asObjectArray() {
jlaskey@3 276 return getArray().asObjectArray();
jlaskey@3 277 }
jlaskey@3 278
jlaskey@3 279 /**
jlaskey@3 280 * ECMA 15.4.3.2 Array.isArray ( arg )
jlaskey@3 281 *
jlaskey@3 282 * @param self self reference
jlaskey@3 283 * @param arg argument - object to check
jlaskey@3 284 * @return true if argument is an array
jlaskey@3 285 */
jlaskey@3 286 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
jlaskey@3 287 public static Object isArray(final Object self, final Object arg) {
jlaskey@3 288 return isArray(arg) || (arg == Global.instance().getArrayPrototype())
jlaskey@3 289 || (arg instanceof NativeRegExpExecResult);
jlaskey@3 290 }
jlaskey@3 291
jlaskey@3 292 /**
jlaskey@3 293 * Length getter
jlaskey@3 294 * @param self self reference
jlaskey@3 295 * @return the length of the object
jlaskey@3 296 */
jlaskey@3 297 @Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE)
jlaskey@3 298 public static Object length(final Object self) {
jlaskey@3 299 if (isArray(self)) {
jlaskey@3 300 return ((NativeArray) self).getArray().length() & JSType.MAX_UINT;
jlaskey@3 301 }
jlaskey@3 302
jlaskey@3 303 return 0;
jlaskey@3 304 }
jlaskey@3 305
jlaskey@3 306 /**
jlaskey@3 307 * Length setter
jlaskey@3 308 * @param self self reference
jlaskey@3 309 * @param length new length property
jlaskey@3 310 */
jlaskey@3 311 @Setter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE)
jlaskey@3 312 public static void length(final Object self, final Object length) {
jlaskey@3 313 if (isArray(self)) {
jlaskey@3 314 ((NativeArray) self).setLength(validLength(length, true));
jlaskey@3 315 }
jlaskey@3 316 }
jlaskey@3 317
jlaskey@3 318 static long validLength(final Object length, final boolean reject) {
jlaskey@3 319 final double doubleLength = JSType.toNumber(length);
jlaskey@3 320 if (!Double.isNaN(doubleLength) && JSType.isRepresentableAsLong(doubleLength)) {
jlaskey@3 321 final long len = (long) doubleLength;
jlaskey@3 322 if (len >= 0 && len <= JSType.MAX_UINT) {
jlaskey@3 323 return len;
jlaskey@3 324 }
jlaskey@3 325 }
jlaskey@3 326 if (reject) {
lagergren@112 327 throw rangeError("inappropriate.array.length", ScriptRuntime.safeToString(length));
jlaskey@3 328 }
jlaskey@3 329 return -1;
jlaskey@3 330 }
jlaskey@3 331
jlaskey@3 332 /**
jlaskey@3 333 * ECMA 15.4.4.2 Array.prototype.toString ( )
jlaskey@3 334 *
jlaskey@3 335 * @param self self reference
jlaskey@3 336 * @return string representation of array
jlaskey@3 337 */
jlaskey@3 338 @Function(attributes = Attribute.NOT_ENUMERABLE)
jlaskey@3 339 public static Object toString(final Object self) {
jlaskey@3 340 if (self instanceof ScriptObject) {
jlaskey@3 341 final ScriptObject sobj = (ScriptObject) self;
jlaskey@3 342 try {
jlaskey@3 343 final Object join = JOIN.getGetter().invokeExact(sobj);
jlaskey@3 344 if (join instanceof ScriptFunction) {
jlaskey@3 345 return JOIN.getInvoker().invokeExact(join, sobj);
jlaskey@3 346 }
jlaskey@3 347 } catch (final RuntimeException | Error e) {
jlaskey@3 348 throw e;
jlaskey@3 349 } catch (final Throwable t) {
jlaskey@3 350 throw new RuntimeException(t);
jlaskey@3 351 }
jlaskey@3 352 }
jlaskey@3 353
jlaskey@3 354 // FIXME: should lookup Object.prototype.toString and call that?
jlaskey@3 355 return ScriptRuntime.builtinObjectToString(self);
jlaskey@3 356 }
jlaskey@3 357
jlaskey@3 358 /**
jlaskey@3 359 * ECMA 15.4.4.3 Array.prototype.toLocaleString ( )
jlaskey@3 360 *
jlaskey@3 361 * @param self self reference
jlaskey@3 362 * @return locale specific string representation for array
jlaskey@3 363 */
jlaskey@3 364 @Function(attributes = Attribute.NOT_ENUMERABLE)
jlaskey@3 365 public static Object toLocaleString(final Object self) {
jlaskey@3 366 final StringBuilder sb = new StringBuilder();
jlaskey@3 367 final Iterator<Object> iter = arrayLikeIterator(self, true);
jlaskey@3 368
jlaskey@3 369 while (iter.hasNext()) {
jlaskey@3 370 final Object obj = iter.next();
jlaskey@3 371
jlaskey@3 372 if (obj != null && obj != ScriptRuntime.UNDEFINED) {
sundar@44 373 final Object val = JSType.toScriptObject(obj);
jlaskey@3 374
jlaskey@3 375 try {
jlaskey@3 376 if (val instanceof ScriptObject) {
jlaskey@3 377 final ScriptObject sobj = (ScriptObject)val;
jlaskey@3 378 final Object toLocaleString = TO_LOCALE_STRING.getGetter().invokeExact(sobj);
jlaskey@3 379
jlaskey@3 380 if (toLocaleString instanceof ScriptFunction) {
jlaskey@3 381 sb.append((String)TO_LOCALE_STRING.getInvoker().invokeExact(toLocaleString, sobj));
jlaskey@3 382 } else {
lagergren@112 383 throw typeError("not.a.function", "toLocaleString");
jlaskey@3 384 }
jlaskey@3 385 }
jlaskey@3 386 } catch (final Error|RuntimeException t) {
jlaskey@3 387 throw t;
jlaskey@3 388 } catch (final Throwable t) {
jlaskey@3 389 throw new RuntimeException(t);
jlaskey@3 390 }
jlaskey@3 391 }
jlaskey@3 392
jlaskey@3 393 if (iter.hasNext()) {
jlaskey@3 394 sb.append(",");
jlaskey@3 395 }
jlaskey@3 396 }
jlaskey@3 397
jlaskey@3 398 return sb.toString();
jlaskey@3 399 }
jlaskey@3 400
jlaskey@3 401 /**
jlaskey@3 402 * ECMA 15.4.2.2 new Array (len)
jlaskey@3 403 *
jlaskey@3 404 * @param newObj was the new operator used to instantiate this array
jlaskey@3 405 * @param self self reference
jlaskey@3 406 * @param args arguments (length)
jlaskey@3 407 * @return the new NativeArray
jlaskey@3 408 */
jlaskey@3 409 @Constructor(arity = 1)
jlaskey@3 410 public static Object construct(final boolean newObj, final Object self, final Object... args) {
jlaskey@3 411 switch (args.length) {
jlaskey@3 412 case 0:
jlaskey@3 413 return new NativeArray(0);
jlaskey@3 414 case 1:
jlaskey@3 415 final Object len = args[0];
jlaskey@3 416 if (len instanceof Number) {
jlaskey@3 417 long length;
jlaskey@3 418 if (len instanceof Integer || len instanceof Long) {
jlaskey@3 419 length = ((Number) len).longValue();
jlaskey@3 420 if (length >= 0 && length < 0xffff_ffffL) {
jlaskey@3 421 return new NativeArray(length);
jlaskey@3 422 }
jlaskey@3 423 }
jlaskey@3 424
jlaskey@3 425 length = JSType.toUint32(len);
jlaskey@3 426
jlaskey@3 427 /*
jlaskey@3 428 * If the argument len is a Number and ToUint32(len) is equal to
jlaskey@3 429 * len, then the length property of the newly constructed object
jlaskey@3 430 * is set to ToUint32(len). If the argument len is a Number and
jlaskey@3 431 * ToUint32(len) is not equal to len, a RangeError exception is
jlaskey@3 432 * thrown.
jlaskey@3 433 */
jlaskey@3 434 final double numberLength = ((Number) len).doubleValue();
jlaskey@3 435 if (length != numberLength) {
lagergren@112 436 throw rangeError("inappropriate.array.length", JSType.toString(numberLength));
jlaskey@3 437 }
jlaskey@3 438
jlaskey@3 439 return new NativeArray(length);
jlaskey@3 440 }
jlaskey@3 441 /*
jlaskey@3 442 * If the argument len is not a Number, then the length property of
jlaskey@3 443 * the newly constructed object is set to 1 and the 0 property of
jlaskey@3 444 * the newly constructed object is set to len
jlaskey@3 445 */
jlaskey@3 446 return new NativeArray(new Object[]{args[0]});
jlaskey@3 447 //fallthru
jlaskey@3 448 default:
jlaskey@3 449 return new NativeArray(args);
jlaskey@3 450 }
jlaskey@3 451 }
jlaskey@3 452
jlaskey@3 453 /**
jlaskey@3 454 * ECMA 15.4.2.2 new Array (len)
jlaskey@3 455 *
jlaskey@3 456 * Specialized constructor for zero arguments - empty array
jlaskey@3 457 *
jlaskey@3 458 * @param newObj was the new operator used to instantiate this array
jlaskey@3 459 * @param self self reference
jlaskey@3 460 * @return the new NativeArray
jlaskey@3 461 */
jlaskey@3 462 @SpecializedConstructor
jlaskey@3 463 public static Object construct(final boolean newObj, final Object self) {
jlaskey@3 464 return new NativeArray(0);
jlaskey@3 465 }
jlaskey@3 466
jlaskey@3 467 /**
jlaskey@3 468 * ECMA 15.4.2.2 new Array (len)
jlaskey@3 469 *
jlaskey@3 470 * Specialized constructor for one integer argument (length)
jlaskey@3 471 *
jlaskey@3 472 * @param newObj was the new operator used to instantiate this array
jlaskey@3 473 * @param self self reference
jlaskey@3 474 * @param length array length
jlaskey@3 475 * @return the new NativeArray
jlaskey@3 476 */
jlaskey@3 477 @SpecializedConstructor
jlaskey@3 478 public static Object construct(final boolean newObj, final Object self, final int length) {
jlaskey@3 479 if (length >= 0) {
jlaskey@3 480 return new NativeArray(length);
jlaskey@3 481 }
jlaskey@3 482
jlaskey@3 483 return construct(newObj, self, new Object[]{length});
jlaskey@3 484 }
jlaskey@3 485
jlaskey@3 486 /**
jlaskey@3 487 * ECMA 15.4.2.2 new Array (len)
jlaskey@3 488 *
jlaskey@3 489 * Specialized constructor for one long argument (length)
jlaskey@3 490 *
jlaskey@3 491 * @param newObj was the new operator used to instantiate this array
jlaskey@3 492 * @param self self reference
jlaskey@3 493 * @param length array length
jlaskey@3 494 * @return the new NativeArray
jlaskey@3 495 */
jlaskey@3 496 @SpecializedConstructor
jlaskey@3 497 public static Object construct(final boolean newObj, final Object self, final long length) {
jlaskey@3 498 if (length >= 0L && length <= JSType.MAX_UINT) {
jlaskey@3 499 return new NativeArray(length);
jlaskey@3 500 }
jlaskey@3 501
jlaskey@3 502 return construct(newObj, self, new Object[]{length});
jlaskey@3 503 }
jlaskey@3 504
jlaskey@3 505 /**
jlaskey@3 506 * ECMA 15.4.2.2 new Array (len)
jlaskey@3 507 *
jlaskey@3 508 * Specialized constructor for one double argument (length)
jlaskey@3 509 *
jlaskey@3 510 * @param newObj was the new operator used to instantiate this array
jlaskey@3 511 * @param self self reference
jlaskey@3 512 * @param length array length
jlaskey@3 513 * @return the new NativeArray
jlaskey@3 514 */
jlaskey@3 515 @SpecializedConstructor
jlaskey@3 516 public static Object construct(final boolean newObj, final Object self, final double length) {
jlaskey@3 517 final long uint32length = JSType.toUint32(length);
jlaskey@3 518
jlaskey@3 519 if (uint32length == length) {
jlaskey@3 520 return new NativeArray(uint32length);
jlaskey@3 521 }
jlaskey@3 522
jlaskey@3 523 return construct(newObj, self, new Object[]{length});
jlaskey@3 524 }
jlaskey@3 525
jlaskey@3 526 /**
jlaskey@3 527 * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] )
jlaskey@3 528 *
jlaskey@3 529 * @param self self reference
jlaskey@3 530 * @param args arguments to concat
jlaskey@3 531 * @return resulting NativeArray
jlaskey@3 532 */
jlaskey@3 533 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
jlaskey@3 534 public static Object concat(final Object self, final Object... args) {
jlaskey@3 535 final ArrayList<Object> list = new ArrayList<>();
jlaskey@3 536 final Object selfToObject = Global.toObject(self);
jlaskey@3 537
jlaskey@3 538 if (isArray(selfToObject)) {
jlaskey@3 539 final Iterator<Object> iter = arrayLikeIterator(selfToObject, true);
jlaskey@3 540 while (iter.hasNext()) {
jlaskey@3 541 list.add(iter.next());
jlaskey@3 542 }
jlaskey@3 543 } else {
jlaskey@3 544 // single element, add it
jlaskey@3 545 list.add(selfToObject);
jlaskey@3 546 }
jlaskey@3 547
jlaskey@3 548 for (final Object obj : args) {
jlaskey@3 549 if (isArray(obj) || obj instanceof Iterable || (obj != null && obj.getClass().isArray())) {
jlaskey@3 550 final Iterator<Object> iter = arrayLikeIterator(obj, true);
jlaskey@3 551 if (iter.hasNext()) {
jlaskey@3 552 while (iter.hasNext()) {
jlaskey@3 553 list.add(iter.next());
jlaskey@3 554 }
jlaskey@3 555 } else if (!isArray(obj)) {
jlaskey@3 556 list.add(obj); // add empty object, but not an empty array
jlaskey@3 557 }
jlaskey@3 558 } else {
jlaskey@3 559 // single element, add it
jlaskey@3 560 list.add(obj);
jlaskey@3 561 }
jlaskey@3 562 }
jlaskey@3 563
jlaskey@3 564 return new NativeArray(list.toArray());
jlaskey@3 565 }
jlaskey@3 566
jlaskey@3 567 /**
jlaskey@3 568 * ECMA 15.4.4.5 Array.prototype.join (separator)
jlaskey@3 569 *
jlaskey@3 570 * @param self self reference
jlaskey@3 571 * @param separator element separator
jlaskey@3 572 * @return string representation after join
jlaskey@3 573 */
jlaskey@3 574 @Function(attributes = Attribute.NOT_ENUMERABLE)
jlaskey@3 575 public static Object join(final Object self, final Object separator) {
jlaskey@3 576 final String sep = separator == ScriptRuntime.UNDEFINED ? "," : JSType.toString(separator);
jlaskey@3 577 final StringBuilder sb = new StringBuilder();
jlaskey@3 578 final Iterator<Object> iter = arrayLikeIterator(self, true);
jlaskey@3 579
jlaskey@3 580 while (iter.hasNext()) {
jlaskey@3 581 final Object obj = iter.next();
jlaskey@3 582
jlaskey@3 583 if (obj != null && obj != ScriptRuntime.UNDEFINED) {
jlaskey@3 584 sb.append(JSType.toString(obj));
jlaskey@3 585 }
jlaskey@3 586
jlaskey@3 587 if (iter.hasNext()) {
jlaskey@3 588 sb.append(sep);
jlaskey@3 589 }
jlaskey@3 590 }
jlaskey@3 591
jlaskey@3 592 return sb.toString();
jlaskey@3 593 }
jlaskey@3 594
jlaskey@3 595 /**
jlaskey@3 596 * ECMA 15.4.4.6 Array.prototype.pop ()
jlaskey@3 597 *
jlaskey@3 598 * @param self self reference
jlaskey@3 599 * @return array after pop
jlaskey@3 600 */
jlaskey@3 601 @Function(attributes = Attribute.NOT_ENUMERABLE)
jlaskey@3 602 public static Object pop(final Object self) {
jlaskey@3 603 try {
jlaskey@3 604 final ScriptObject sobj = (ScriptObject)self;
sundar@41 605 final boolean strict = sobj.isStrictContext();
jlaskey@3 606
jlaskey@3 607 if (bulkable(sobj)) {
lagergren@137 608 return sobj.getArray().pop();
jlaskey@3 609 }
jlaskey@3 610
jlaskey@3 611 final long len = JSType.toUint32(sobj.getLength());
jlaskey@3 612
jlaskey@3 613 if (len == 0) {
jlaskey@3 614 sobj.set("length", 0, strict);
jlaskey@3 615 return ScriptRuntime.UNDEFINED;
jlaskey@3 616 }
jlaskey@3 617
jlaskey@3 618 final long index = len - 1;
jlaskey@3 619 final Object element = sobj.get(index);
jlaskey@3 620
jlaskey@3 621 sobj.delete(index, strict);
jlaskey@3 622 sobj.set("length", index, strict);
jlaskey@3 623
jlaskey@3 624 return element;
jlaskey@3 625 } catch (final ClassCastException | NullPointerException e) {
lagergren@112 626 throw typeError("not.an.object", ScriptRuntime.safeToString(self));
jlaskey@3 627 }
jlaskey@3 628 }
jlaskey@3 629
jlaskey@3 630 /**
jlaskey@3 631 * ECMA 15.4.4.7 Array.prototype.push (args...)
jlaskey@3 632 *
jlaskey@3 633 * @param self self reference
jlaskey@3 634 * @param args arguments to push
jlaskey@3 635 * @return array after pushes
jlaskey@3 636 */
jlaskey@3 637 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
jlaskey@3 638 public static Object push(final Object self, final Object... args) {
jlaskey@3 639 try {
jlaskey@3 640 final ScriptObject sobj = (ScriptObject)self;
sundar@41 641 final boolean strict = sobj.isStrictContext();
jlaskey@3 642
jlaskey@3 643 if (bulkable(sobj)) {
jlaskey@3 644 final NativeArray nativeArray = (NativeArray)sobj;
jlaskey@3 645 if (nativeArray.getArray().length() + args.length <= JSType.MAX_UINT) {
sundar@82 646 final ArrayData newData = nativeArray.getArray().push(nativeArray.isStrictContext(), args);
jlaskey@3 647 nativeArray.setArray(newData);
jlaskey@3 648 return newData.length();
jlaskey@3 649 }
jlaskey@3 650 //fallthru
jlaskey@3 651 }
jlaskey@3 652
jlaskey@3 653 long len = JSType.toUint32(sobj.getLength());
jlaskey@3 654 for (final Object element : args) {
jlaskey@3 655 sobj.set(len++, element, strict);
jlaskey@3 656 }
jlaskey@3 657 sobj.set("length", len, strict);
jlaskey@3 658
jlaskey@3 659 return len;
jlaskey@3 660 } catch (final ClassCastException | NullPointerException e) {
lagergren@112 661 throw typeError("not.an.object", ScriptRuntime.safeToString(self));
jlaskey@3 662 }
jlaskey@3 663 }
jlaskey@3 664
jlaskey@3 665 /**
jlaskey@3 666 * ECMA 15.4.4.8 Array.prototype.reverse ()
jlaskey@3 667 *
jlaskey@3 668 * @param self self reference
jlaskey@3 669 * @return reversed array
jlaskey@3 670 */
jlaskey@3 671 @Function(attributes = Attribute.NOT_ENUMERABLE)
jlaskey@3 672 public static Object reverse(final Object self) {
jlaskey@3 673 try {
jlaskey@3 674 final ScriptObject sobj = (ScriptObject)self;
sundar@41 675 final boolean strict = sobj.isStrictContext();
jlaskey@3 676 final long len = JSType.toUint32(sobj.getLength());
jlaskey@3 677 final long middle = len / 2;
jlaskey@3 678
jlaskey@3 679 for (long lower = 0; lower != middle; lower++) {
jlaskey@3 680 final long upper = len - lower - 1;
jlaskey@3 681 final Object lowerValue = sobj.get(lower);
jlaskey@3 682 final Object upperValue = sobj.get(upper);
jlaskey@3 683 final boolean lowerExists = sobj.has(lower);
jlaskey@3 684 final boolean upperExists = sobj.has(upper);
jlaskey@3 685
jlaskey@3 686 if (lowerExists && upperExists) {
jlaskey@3 687 sobj.set(lower, upperValue, strict);
jlaskey@3 688 sobj.set(upper, lowerValue, strict);
jlaskey@3 689 } else if (!lowerExists && upperExists) {
jlaskey@3 690 sobj.set(lower, upperValue, strict);
jlaskey@3 691 sobj.delete(upper, strict);
jlaskey@3 692 } else if (lowerExists && !upperExists) {
jlaskey@3 693 sobj.delete(lower, strict);
jlaskey@3 694 sobj.set(upper, lowerValue, strict);
jlaskey@3 695 }
jlaskey@3 696 }
jlaskey@3 697 return sobj;
jlaskey@3 698 } catch (final ClassCastException | NullPointerException e) {
lagergren@112 699 throw typeError("not.an.object", ScriptRuntime.safeToString(self));
jlaskey@3 700 }
jlaskey@3 701 }
jlaskey@3 702
jlaskey@3 703 /**
jlaskey@3 704 * ECMA 15.4.4.9 Array.prototype.shift ()
jlaskey@3 705 *
jlaskey@3 706 * @param self self reference
jlaskey@3 707 * @return shifted array
jlaskey@3 708 */
jlaskey@3 709 @Function(attributes = Attribute.NOT_ENUMERABLE)
jlaskey@3 710 public static Object shift(final Object self) {
jlaskey@3 711 final Object obj = Global.toObject(self);
jlaskey@3 712
jlaskey@3 713 Object first = ScriptRuntime.UNDEFINED;
jlaskey@3 714
jlaskey@3 715 if (!(obj instanceof ScriptObject)) {
jlaskey@3 716 return first;
jlaskey@3 717 }
jlaskey@3 718
jlaskey@3 719 final ScriptObject sobj = (ScriptObject) obj;
jlaskey@3 720 final boolean strict = Global.isStrict();
jlaskey@3 721
jlaskey@3 722 long len = JSType.toUint32(sobj.getLength());
jlaskey@3 723
jlaskey@3 724 if (len > 0) {
jlaskey@3 725 first = sobj.get(0);
jlaskey@3 726
jlaskey@3 727 if (bulkable(sobj)) {
lagergren@137 728 sobj.getArray().shiftLeft(1);
jlaskey@3 729 } else {
jlaskey@3 730 for (long k = 1; k < len; k++) {
jlaskey@3 731 sobj.set(k - 1, sobj.get(k), strict);
jlaskey@3 732 }
jlaskey@3 733 }
jlaskey@3 734 sobj.delete(--len, strict);
jlaskey@3 735 } else {
jlaskey@3 736 len = 0;
jlaskey@3 737 }
jlaskey@3 738
jlaskey@3 739 sobj.set("length", len, strict);
jlaskey@3 740
jlaskey@3 741 return first;
jlaskey@3 742 }
jlaskey@3 743
jlaskey@3 744 /**
jlaskey@3 745 * ECMA 15.4.4.10 Array.prototype.slice ( start [ , end ] )
jlaskey@3 746 *
jlaskey@3 747 * @param self self reference
jlaskey@3 748 * @param start start of slice (inclusive)
jlaskey@3 749 * @param end end of slice (optional, exclusive)
jlaskey@3 750 * @return sliced array
jlaskey@3 751 */
jlaskey@3 752 @Function(attributes = Attribute.NOT_ENUMERABLE)
jlaskey@3 753 public static Object slice(final Object self, final Object start, final Object end) {
jlaskey@3 754 final Object obj = Global.toObject(self);
jlaskey@3 755 final ScriptObject sobj = (ScriptObject)obj;
jlaskey@3 756 final long len = JSType.toUint32(sobj.getLength());
jlaskey@3 757 final long relativeStartUint32 = JSType.toUint32(start);
jlaskey@3 758 final long relativeStart = JSType.toInteger(start);
jlaskey@3 759
jlaskey@3 760 long k = relativeStart < 0 ?
jlaskey@3 761 Math.max(len + relativeStart, 0) :
jlaskey@3 762 Math.min(
jlaskey@3 763 Math.max(relativeStartUint32, relativeStart),
jlaskey@3 764 len);
jlaskey@3 765
jlaskey@3 766 final long relativeEndUint32 = end == ScriptRuntime.UNDEFINED ? len : JSType.toUint32(end);
jlaskey@3 767 final long relativeEnd = end == ScriptRuntime.UNDEFINED ? len : JSType.toInteger(end);
jlaskey@3 768
jlaskey@3 769 final long finale = relativeEnd < 0 ?
jlaskey@3 770 Math.max(len + relativeEnd, 0) :
jlaskey@3 771 Math.min(
jlaskey@3 772 Math.max(relativeEndUint32, relativeEnd),
jlaskey@3 773 len);
jlaskey@3 774
jlaskey@3 775 if (k >= finale) {
jlaskey@3 776 return new NativeArray(0);
jlaskey@3 777 }
jlaskey@3 778
jlaskey@3 779 if (bulkable(sobj)) {
jlaskey@3 780 final NativeArray narray = (NativeArray) sobj;
jlaskey@3 781 return new NativeArray(narray.getArray().slice(k, finale));
jlaskey@3 782 }
jlaskey@3 783
jlaskey@3 784 final NativeArray copy = new NativeArray(0);
jlaskey@3 785 for (long n = 0; k < finale; n++, k++) {
jlaskey@3 786 copy.defineOwnProperty((int) n, sobj.get(k));
jlaskey@3 787 }
jlaskey@3 788
jlaskey@3 789 return copy;
jlaskey@3 790 }
jlaskey@3 791
jlaskey@3 792 private static ScriptFunction compareFunction(final Object comparefn) {
jlaskey@3 793 try {
jlaskey@3 794 return (ScriptFunction)comparefn;
jlaskey@3 795 } catch (final ClassCastException e) {
jlaskey@3 796 return null; //undefined or null
jlaskey@3 797 }
jlaskey@3 798 }
jlaskey@3 799
jlaskey@3 800 private static Object[] sort(final Object[] array, final Object comparefn) {
jlaskey@3 801 final ScriptFunction cmp = compareFunction(comparefn);
jlaskey@3 802
jlaskey@3 803 final List<Object> list = Arrays.asList(array);
sundar@44 804 final Object cmpThis = cmp == null || cmp.isStrict() ? ScriptRuntime.UNDEFINED : Global.instance();
jlaskey@3 805
jlaskey@3 806 Collections.sort(list, new Comparator<Object>() {
jlaskey@3 807 @Override
jlaskey@3 808 public int compare(final Object x, final Object y) {
jlaskey@3 809 if (x == ScriptRuntime.UNDEFINED && y == ScriptRuntime.UNDEFINED) {
jlaskey@3 810 return 0;
jlaskey@3 811 } else if (x == ScriptRuntime.UNDEFINED) {
jlaskey@3 812 return 1;
jlaskey@3 813 } else if (y == ScriptRuntime.UNDEFINED) {
jlaskey@3 814 return -1;
jlaskey@3 815 }
jlaskey@3 816
jlaskey@3 817 if (cmp != null) {
jlaskey@3 818 try {
jlaskey@3 819 return (int)CALL_CMP.invokeExact(cmp, cmpThis, x, y);
jlaskey@3 820 } catch (final RuntimeException | Error e) {
jlaskey@3 821 throw e;
jlaskey@3 822 } catch (final Throwable t) {
jlaskey@3 823 throw new RuntimeException(t);
jlaskey@3 824 }
jlaskey@3 825 }
jlaskey@3 826
jlaskey@3 827 return JSType.toString(x).compareTo(JSType.toString(y));
jlaskey@3 828 }
jlaskey@3 829 });
jlaskey@3 830
jlaskey@3 831 return list.toArray(new Object[array.length]);
jlaskey@3 832 }
jlaskey@3 833
jlaskey@3 834 /**
jlaskey@3 835 * ECMA 15.4.4.11 Array.prototype.sort ( comparefn )
jlaskey@3 836 *
jlaskey@3 837 * @param self self reference
jlaskey@3 838 * @param comparefn element comparison function
jlaskey@3 839 * @return sorted array
jlaskey@3 840 */
jlaskey@3 841 @Function(attributes = Attribute.NOT_ENUMERABLE)
jlaskey@3 842 public static Object sort(final Object self, final Object comparefn) {
jlaskey@3 843 try {
jlaskey@3 844 final ScriptObject sobj = (ScriptObject) self;
sundar@41 845 final boolean strict = sobj.isStrictContext();
jlaskey@3 846 final long len = JSType.toUint32(sobj.getLength());
jlaskey@3 847
jlaskey@3 848 if (len > 1) {
jlaskey@3 849 final Object[] src = new Object[(int) len];
jlaskey@3 850 for (int i = 0; i < src.length; i++) {
jlaskey@3 851 src[i] = sobj.get(i);
jlaskey@3 852 }
jlaskey@3 853
jlaskey@3 854 final Object[] sorted = sort(src, comparefn);
jlaskey@3 855 assert sorted.length == src.length;
jlaskey@3 856
jlaskey@3 857 for (int i = 0; i < sorted.length; i++) {
jlaskey@3 858 sobj.set(i, sorted[i], strict);
jlaskey@3 859 }
jlaskey@3 860 }
jlaskey@3 861
jlaskey@3 862 return sobj;
jlaskey@3 863 } catch (final ClassCastException | NullPointerException e) {
lagergren@112 864 throw typeError("not.an.object", ScriptRuntime.safeToString(self));
jlaskey@3 865 }
jlaskey@3 866 }
jlaskey@3 867
jlaskey@3 868 /**
jlaskey@3 869 * ECMA 15.4.4.12 Array.prototype.splice ( start, deleteCount [ item1 [ , item2 [ , ... ] ] ] )
jlaskey@3 870 *
jlaskey@3 871 * @param self self reference
jlaskey@3 872 * @param args arguments
jlaskey@3 873 * @return result of splice
jlaskey@3 874 */
jlaskey@3 875 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
jlaskey@3 876 public static Object splice(final Object self, final Object... args) {
jlaskey@3 877 final Object obj = Global.toObject(self);
jlaskey@3 878
jlaskey@3 879 if (!(obj instanceof ScriptObject)) {
jlaskey@3 880 return ScriptRuntime.UNDEFINED;
jlaskey@3 881 }
jlaskey@3 882
jlaskey@3 883 final Object start = (args.length > 0) ? args[0] : ScriptRuntime.UNDEFINED;
jlaskey@3 884 final Object deleteCount = (args.length > 1) ? args[1] : ScriptRuntime.UNDEFINED;
jlaskey@3 885
jlaskey@3 886 Object[] items;
jlaskey@3 887
jlaskey@3 888 if (args.length > 2) {
jlaskey@3 889 items = new Object[args.length - 2];
jlaskey@3 890 System.arraycopy(args, 2, items, 0, items.length);
jlaskey@3 891 } else {
jlaskey@3 892 items = ScriptRuntime.EMPTY_ARRAY;
jlaskey@3 893 }
jlaskey@3 894
jlaskey@3 895 final ScriptObject sobj = (ScriptObject)obj;
jlaskey@3 896 final boolean strict = Global.isStrict();
jlaskey@3 897 final long len = JSType.toUint32(sobj.getLength());
jlaskey@3 898 final long relativeStartUint32 = JSType.toUint32(start);
jlaskey@3 899 final long relativeStart = JSType.toInteger(start);
jlaskey@3 900
jlaskey@3 901 //TODO: workaround overflow of relativeStart for start > Integer.MAX_VALUE
jlaskey@3 902 final long actualStart = relativeStart < 0 ?
jlaskey@3 903 Math.max(len + relativeStart, 0) :
jlaskey@3 904 Math.min(
jlaskey@3 905 Math.max(relativeStartUint32, relativeStart),
jlaskey@3 906 len);
jlaskey@3 907
jlaskey@3 908 final long actualDeleteCount =
jlaskey@3 909 Math.min(
jlaskey@3 910 Math.max(JSType.toInteger(deleteCount), 0),
jlaskey@3 911 len - actualStart);
jlaskey@3 912
jlaskey@3 913 final NativeArray array = new NativeArray(actualDeleteCount);
jlaskey@3 914
jlaskey@3 915 for (long k = 0; k < actualDeleteCount; k++) {
jlaskey@3 916 final long from = actualStart + k;
jlaskey@3 917
jlaskey@3 918 if (sobj.has(from)) {
jlaskey@3 919 array.defineOwnProperty((int) k, sobj.get(from));
jlaskey@3 920 }
jlaskey@3 921 }
jlaskey@3 922
jlaskey@3 923 if (items.length < actualDeleteCount) {
jlaskey@3 924 for (long k = actualStart; k < (len - actualDeleteCount); k++) {
jlaskey@3 925 final long from = k + actualDeleteCount;
jlaskey@3 926 final long to = k + items.length;
jlaskey@3 927
jlaskey@3 928 if (sobj.has(from)) {
jlaskey@3 929 sobj.set(to, sobj.get(from), strict);
jlaskey@3 930 } else {
jlaskey@3 931 sobj.delete(to, strict);
jlaskey@3 932 }
jlaskey@3 933 }
jlaskey@3 934
jlaskey@3 935 for (long k = len; k > (len - actualDeleteCount + items.length); k--) {
jlaskey@3 936 sobj.delete(k - 1, strict);
jlaskey@3 937 }
jlaskey@3 938 } else if (items.length > actualDeleteCount) {
jlaskey@3 939 for (long k = len - actualDeleteCount; k > actualStart; k--) {
jlaskey@3 940 final long from = k + actualDeleteCount - 1;
jlaskey@3 941 final long to = k + items.length - 1;
jlaskey@3 942
jlaskey@3 943 if (sobj.has(from)) {
jlaskey@3 944 final Object fromValue = sobj.get(from);
jlaskey@3 945 sobj.set(to, fromValue, strict);
jlaskey@3 946 } else {
jlaskey@3 947 sobj.delete(to, strict);
jlaskey@3 948 }
jlaskey@3 949 }
jlaskey@3 950 }
jlaskey@3 951
jlaskey@3 952 long k = actualStart;
jlaskey@3 953 for (int i = 0; i < items.length; i++, k++) {
jlaskey@3 954 sobj.set(k, items[i], strict);
jlaskey@3 955 }
jlaskey@3 956
jlaskey@3 957 final long newLength = len - actualDeleteCount + items.length;
jlaskey@3 958 sobj.set("length", newLength, strict);
jlaskey@3 959
jlaskey@3 960 return array;
jlaskey@3 961 }
jlaskey@3 962
jlaskey@3 963 /**
jlaskey@3 964 * ECMA 15.4.4.13 Array.prototype.unshift ( [ item1 [ , item2 [ , ... ] ] ] )
jlaskey@3 965 *
jlaskey@3 966 * @param self self reference
jlaskey@3 967 * @param items items for unshift
jlaskey@3 968 * @return unshifted array
jlaskey@3 969 */
jlaskey@3 970 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
jlaskey@3 971 public static Object unshift(final Object self, final Object... items) {
jlaskey@3 972 final Object obj = Global.toObject(self);
jlaskey@3 973
jlaskey@3 974 if (!(obj instanceof ScriptObject)) {
jlaskey@3 975 return ScriptRuntime.UNDEFINED;
jlaskey@3 976 }
jlaskey@3 977
jlaskey@3 978 final ScriptObject sobj = (ScriptObject)obj;
jlaskey@3 979 final boolean strict = Global.isStrict();
jlaskey@3 980 final long len = JSType.toUint32(sobj.getLength());
jlaskey@3 981
jlaskey@3 982 if (items == null) {
jlaskey@3 983 return ScriptRuntime.UNDEFINED;
jlaskey@3 984 }
jlaskey@3 985
jlaskey@3 986 if (bulkable(sobj)) {
jlaskey@3 987 final NativeArray nativeArray = (NativeArray) sobj;
jlaskey@3 988 nativeArray.getArray().shiftRight(items.length);
jlaskey@3 989
jlaskey@3 990 for (int j = 0; j < items.length; j++) {
sundar@41 991 nativeArray.setArray(nativeArray.getArray().set(j, items[j], sobj.isStrictContext()));
jlaskey@3 992 }
jlaskey@3 993 } else {
jlaskey@3 994 for (long k = len; k > 0; k--) {
jlaskey@3 995 final long from = k - 1;
jlaskey@3 996 final long to = k + items.length - 1;
jlaskey@3 997
jlaskey@3 998 if (sobj.has(from)) {
jlaskey@3 999 final Object fromValue = sobj.get(from);
jlaskey@3 1000 sobj.set(to, fromValue, strict);
jlaskey@3 1001 } else {
jlaskey@3 1002 sobj.delete(to, strict);
jlaskey@3 1003 }
jlaskey@3 1004 }
jlaskey@3 1005
jlaskey@3 1006 for (int j = 0; j < items.length; j++) {
jlaskey@3 1007 sobj.set(j, items[j], strict);
jlaskey@3 1008 }
jlaskey@3 1009 }
jlaskey@3 1010
jlaskey@3 1011 final long newLength = len + items.length;
jlaskey@3 1012 sobj.set("length", newLength, strict);
jlaskey@3 1013
jlaskey@3 1014 return newLength;
jlaskey@3 1015 }
jlaskey@3 1016
jlaskey@3 1017 /**
jlaskey@3 1018 * ECMA 15.4.4.14 Array.prototype.indexOf ( searchElement [ , fromIndex ] )
jlaskey@3 1019 *
jlaskey@3 1020 * @param self self reference
jlaskey@3 1021 * @param searchElement element to search for
jlaskey@3 1022 * @param fromIndex start index of search
jlaskey@3 1023 * @return index of element, or -1 if not found
jlaskey@3 1024 */
jlaskey@3 1025 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
jlaskey@3 1026 public static Object indexOf(final Object self, final Object searchElement, final Object fromIndex) {
jlaskey@3 1027 try {
jlaskey@3 1028 final ScriptObject sobj = (ScriptObject)Global.toObject(self);
jlaskey@3 1029 final long len = JSType.toUint32(sobj.getLength());
jlaskey@3 1030 final long n = JSType.toLong(fromIndex);
jlaskey@3 1031
jlaskey@3 1032 if (len == 0 || n >= len) {
jlaskey@3 1033 return -1;
jlaskey@3 1034 }
jlaskey@3 1035
jlaskey@3 1036 for (long k = Math.max(0, (n < 0) ? (len - Math.abs(n)) : n); k < len; k++) {
jlaskey@3 1037 if (sobj.has(k)) {
jlaskey@3 1038 if (ScriptRuntime.EQ_STRICT(sobj.get(k), searchElement)) {
jlaskey@3 1039 return k;
jlaskey@3 1040 }
jlaskey@3 1041 }
jlaskey@3 1042 }
jlaskey@3 1043 } catch (final ClassCastException | NullPointerException e) {
jlaskey@3 1044 //fallthru
jlaskey@3 1045 }
jlaskey@3 1046
jlaskey@3 1047 return -1;
jlaskey@3 1048 }
jlaskey@3 1049
jlaskey@3 1050 /**
jlaskey@3 1051 * ECMA 15.4.4.15 Array.prototype.lastIndexOf ( searchElement [ , fromIndex ] )
jlaskey@3 1052 *
jlaskey@3 1053 * @param self self reference
jlaskey@3 1054 * @param args arguments: element to search for and optional from index
jlaskey@3 1055 * @return index of element, or -1 if not found
jlaskey@3 1056 */
jlaskey@3 1057 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
jlaskey@3 1058 public static Object lastIndexOf(final Object self, final Object... args) {
jlaskey@3 1059 try {
jlaskey@3 1060 final ScriptObject sobj = (ScriptObject)Global.toObject(self);
jlaskey@3 1061 final long len = JSType.toUint32(sobj.getLength());
jlaskey@3 1062
jlaskey@3 1063 if (len == 0) {
jlaskey@3 1064 return -1;
jlaskey@3 1065 }
jlaskey@3 1066
jlaskey@3 1067 final Object searchElement = (args.length > 0) ? args[0] : ScriptRuntime.UNDEFINED;
jlaskey@3 1068 final long n = (args.length > 1) ? JSType.toLong(args[1]) : (len - 1);
jlaskey@3 1069
jlaskey@3 1070 for (long k = (n < 0) ? (len - Math.abs(n)) : Math.min(n, len - 1); k >= 0; k--) {
jlaskey@3 1071 if (sobj.has(k)) {
jlaskey@3 1072 if (ScriptRuntime.EQ_STRICT(sobj.get(k), searchElement)) {
jlaskey@3 1073 return k;
jlaskey@3 1074 }
jlaskey@3 1075 }
jlaskey@3 1076 }
jlaskey@3 1077 } catch (final ClassCastException | NullPointerException e) {
lagergren@112 1078 throw typeError("not.an.object", ScriptRuntime.safeToString(self));
jlaskey@3 1079 }
jlaskey@3 1080
jlaskey@3 1081 return -1;
jlaskey@3 1082 }
jlaskey@3 1083
jlaskey@3 1084 /**
jlaskey@3 1085 * ECMA 15.4.4.16 Array.prototype.every ( callbackfn [ , thisArg ] )
jlaskey@3 1086 *
jlaskey@3 1087 * @param self self reference
jlaskey@3 1088 * @param callbackfn callback function per element
jlaskey@3 1089 * @param thisArg this argument
jlaskey@3 1090 * @return true if callback function return true for every element in the array, false otherwise
jlaskey@3 1091 */
jlaskey@3 1092 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
jlaskey@3 1093 public static Object every(final Object self, final Object callbackfn, final Object thisArg) {
jlaskey@3 1094 return applyEvery(Global.toObject(self), callbackfn, thisArg);
jlaskey@3 1095 }
jlaskey@3 1096
jlaskey@3 1097 private static boolean applyEvery(final Object self, final Object callbackfn, final Object thisArg) {
jlaskey@3 1098 return new IteratorAction<Boolean>(Global.toObject(self), callbackfn, thisArg, true) {
jlaskey@3 1099 @Override
jlaskey@3 1100 protected boolean forEach(final Object val, final int i) throws Throwable {
jlaskey@3 1101 return (result = (boolean)EVERY_CALLBACK_INVOKER.invokeExact(callbackfn, thisArg, val, i, self));
jlaskey@3 1102 }
jlaskey@3 1103 }.apply();
jlaskey@3 1104 }
jlaskey@3 1105
jlaskey@3 1106 /**
jlaskey@3 1107 * ECMA 15.4.4.17 Array.prototype.some ( callbackfn [ , thisArg ] )
jlaskey@3 1108 *
jlaskey@3 1109 * @param self self reference
jlaskey@3 1110 * @param callbackfn callback function per element
jlaskey@3 1111 * @param thisArg this argument
jlaskey@3 1112 * @return true if callback function returned true for any element in the array, false otherwise
jlaskey@3 1113 */
jlaskey@3 1114 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
jlaskey@3 1115 public static Object some(final Object self, final Object callbackfn, final Object thisArg) {
jlaskey@3 1116 return new IteratorAction<Boolean>(Global.toObject(self), callbackfn, thisArg, false) {
jlaskey@3 1117 @Override
jlaskey@3 1118 protected boolean forEach(final Object val, final int i) throws Throwable {
jlaskey@3 1119 return !(result = (boolean)SOME_CALLBACK_INVOKER.invokeExact(callbackfn, thisArg, val, i, self));
jlaskey@3 1120 }
jlaskey@3 1121 }.apply();
jlaskey@3 1122 }
jlaskey@3 1123
jlaskey@3 1124 /**
jlaskey@3 1125 * ECMA 15.4.4.18 Array.prototype.forEach ( callbackfn [ , thisArg ] )
jlaskey@3 1126 *
jlaskey@3 1127 * @param self self reference
jlaskey@3 1128 * @param callbackfn callback function per element
jlaskey@3 1129 * @param thisArg this argument
jlaskey@3 1130 * @return undefined
jlaskey@3 1131 */
jlaskey@3 1132 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
jlaskey@3 1133 public static Object forEach(final Object self, final Object callbackfn, final Object thisArg) {
jlaskey@3 1134 return new IteratorAction<Object>(Global.toObject(self), callbackfn, thisArg, ScriptRuntime.UNDEFINED) {
jlaskey@3 1135 @Override
jlaskey@3 1136 protected boolean forEach(final Object val, final int i) throws Throwable {
jlaskey@3 1137 FOREACH_CALLBACK_INVOKER.invokeExact(callbackfn, thisArg, val, i, self);
jlaskey@3 1138 return true;
jlaskey@3 1139 }
jlaskey@3 1140 }.apply();
jlaskey@3 1141 }
jlaskey@3 1142
jlaskey@3 1143 /**
jlaskey@3 1144 * ECMA 15.4.4.19 Array.prototype.map ( callbackfn [ , thisArg ] )
jlaskey@3 1145 *
jlaskey@3 1146 * @param self self reference
jlaskey@3 1147 * @param callbackfn callback function per element
jlaskey@3 1148 * @param thisArg this argument
jlaskey@3 1149 * @return array with elements transformed by map function
jlaskey@3 1150 */
jlaskey@3 1151 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
jlaskey@3 1152 public static Object map(final Object self, final Object callbackfn, final Object thisArg) {
jlaskey@3 1153 return new IteratorAction<NativeArray>(Global.toObject(self), callbackfn, thisArg, null) {
jlaskey@3 1154 @Override
jlaskey@3 1155 protected boolean forEach(final Object val, final int i) throws Throwable {
jlaskey@3 1156 final Object r = MAP_CALLBACK_INVOKER.invokeExact(callbackfn, thisArg, val, i, self);
jlaskey@3 1157 result.defineOwnProperty(index, r);
jlaskey@3 1158 return true;
jlaskey@3 1159 }
jlaskey@3 1160
jlaskey@3 1161 @Override
jlaskey@3 1162 public void applyLoopBegin(final ArrayLikeIterator<Object> iter0) {
jlaskey@3 1163 // map return array should be of same length as source array
jlaskey@3 1164 // even if callback reduces source array length
jlaskey@3 1165 result = new NativeArray(iter0.getLength());
jlaskey@3 1166 }
jlaskey@3 1167 }.apply();
jlaskey@3 1168 }
jlaskey@3 1169
jlaskey@3 1170 /**
jlaskey@3 1171 * ECMA 15.4.4.20 Array.prototype.filter ( callbackfn [ , thisArg ] )
jlaskey@3 1172 *
jlaskey@3 1173 * @param self self reference
jlaskey@3 1174 * @param callbackfn callback function per element
jlaskey@3 1175 * @param thisArg this argument
jlaskey@3 1176 * @return filtered array
jlaskey@3 1177 */
jlaskey@3 1178 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
jlaskey@3 1179 public static Object filter(final Object self, final Object callbackfn, final Object thisArg) {
jlaskey@3 1180 return new IteratorAction<NativeArray>(Global.toObject(self), callbackfn, thisArg, new NativeArray()) {
jlaskey@3 1181 private int to = 0;
jlaskey@3 1182
jlaskey@3 1183 @Override
jlaskey@3 1184 protected boolean forEach(final Object val, final int i) throws Throwable {
jlaskey@3 1185 if ((boolean)FILTER_CALLBACK_INVOKER.invokeExact(callbackfn, thisArg, val, i, self)) {
jlaskey@3 1186 result.defineOwnProperty(to++, val);
jlaskey@3 1187 }
jlaskey@3 1188 return true;
jlaskey@3 1189 }
jlaskey@3 1190 }.apply();
jlaskey@3 1191 }
jlaskey@3 1192
jlaskey@3 1193 private static Object reduceInner(final ArrayLikeIterator<Object> iter, final Object self, final Object... args) {
jlaskey@3 1194 final Object callbackfn = args.length > 0 ? args[0] : ScriptRuntime.UNDEFINED;
jlaskey@3 1195 final boolean initialValuePresent = args.length > 1;
jlaskey@3 1196
jlaskey@3 1197 Object initialValue = initialValuePresent ? args[1] : ScriptRuntime.UNDEFINED;
jlaskey@3 1198
jlaskey@3 1199 if (callbackfn == ScriptRuntime.UNDEFINED) {
lagergren@112 1200 throw typeError("not.a.function", "undefined");
jlaskey@3 1201 }
jlaskey@3 1202
jlaskey@3 1203 if (!initialValuePresent) {
jlaskey@3 1204 if (iter.hasNext()) {
jlaskey@3 1205 initialValue = iter.next();
jlaskey@3 1206 } else {
lagergren@112 1207 throw typeError("array.reduce.invalid.init");
jlaskey@3 1208 }
jlaskey@3 1209 }
jlaskey@3 1210
jlaskey@3 1211 //if initial value is ScriptRuntime.UNDEFINED - step forward once.
jlaskey@3 1212 return new IteratorAction<Object>(Global.toObject(self), callbackfn, ScriptRuntime.UNDEFINED, initialValue, iter) {
jlaskey@3 1213 @Override
jlaskey@3 1214 protected boolean forEach(final Object val, final int i) throws Throwable {
jlaskey@3 1215 // TODO: why can't I declare the second arg as Undefined.class?
jlaskey@3 1216 result = REDUCE_CALLBACK_INVOKER.invokeExact(callbackfn, ScriptRuntime.UNDEFINED, result, val, i, self);
jlaskey@3 1217 return true;
jlaskey@3 1218 }
jlaskey@3 1219 }.apply();
jlaskey@3 1220 }
jlaskey@3 1221
jlaskey@3 1222 /**
jlaskey@3 1223 * ECMA 15.4.4.21 Array.prototype.reduce ( callbackfn [ , initialValue ] )
jlaskey@3 1224 *
jlaskey@3 1225 * @param self self reference
jlaskey@3 1226 * @param args arguments to reduce
jlaskey@3 1227 * @return accumulated result
jlaskey@3 1228 */
jlaskey@3 1229 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
jlaskey@3 1230 public static Object reduce(final Object self, final Object... args) {
jlaskey@3 1231 return reduceInner(arrayLikeIterator(self), self, args);
jlaskey@3 1232 }
jlaskey@3 1233
jlaskey@3 1234 /**
jlaskey@3 1235 * ECMA 15.4.4.22 Array.prototype.reduceRight ( callbackfn [ , initialValue ] )
jlaskey@3 1236 *
jlaskey@3 1237 * @param self self reference
jlaskey@3 1238 * @param args arguments to reduce
jlaskey@3 1239 * @return accumulated result
jlaskey@3 1240 */
jlaskey@3 1241 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
jlaskey@3 1242 public static Object reduceRight(final Object self, final Object... args) {
jlaskey@3 1243 return reduceInner(reverseArrayLikeIterator(self), self, args);
jlaskey@3 1244 }
jlaskey@3 1245
jlaskey@3 1246 /**
jlaskey@3 1247 * Determine if Java bulk array operations may be used on the underlying
jlaskey@3 1248 * storage. This is possible only if the object's prototype chain is empty
jlaskey@3 1249 * or each of the prototypes in the chain is empty.
jlaskey@3 1250 *
jlaskey@3 1251 * @param self the object to examine
jlaskey@3 1252 * @return true if optimizable
jlaskey@3 1253 */
jlaskey@3 1254 private static boolean bulkable(final ScriptObject self) {
jlaskey@3 1255 return self.isArray() && !hasInheritedArrayEntries(self);
jlaskey@3 1256 }
jlaskey@3 1257
jlaskey@3 1258 private static boolean hasInheritedArrayEntries(final ScriptObject self) {
jlaskey@3 1259 ScriptObject proto = self.getProto();
jlaskey@3 1260 while (proto != null) {
jlaskey@3 1261 if (proto.hasArrayEntries()) {
jlaskey@3 1262 return true;
jlaskey@3 1263 }
jlaskey@3 1264 proto = proto.getProto();
jlaskey@3 1265 }
jlaskey@3 1266
jlaskey@3 1267 return false;
jlaskey@3 1268 }
jlaskey@3 1269
jlaskey@3 1270 private static MethodHandle createIteratorCallbackInvoker(final Class<?> rtype) {
jlaskey@3 1271 return Bootstrap.createDynamicInvoker("dyn:call", rtype, Object.class, Object.class, Object.class,
jlaskey@3 1272 int.class, Object.class);
jlaskey@3 1273
jlaskey@3 1274 }
jlaskey@3 1275 }

mercurial