Tue, 12 Mar 2013 15:30:53 +0100
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 | } |