2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3 * Copyright (C) 2003, 2007-2009, 2011, 2013, 2015-2016 Apple Inc. All rights reserved.
4 * Copyright (C) 2003 Peter Kelly (pmk@post.com)
5 * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
25 #include "ArrayPrototype.h"
27 #include "AdaptiveInferredPropertyValueWatchpointBase.h"
28 #include "ArrayConstructor.h"
29 #include "BuiltinNames.h"
30 #include "ButterflyInlines.h"
31 #include "CachedCall.h"
32 #include "CodeBlock.h"
33 #include "CopiedSpaceInlines.h"
35 #include "Interpreter.h"
37 #include "JSArrayIterator.h"
38 #include "JSCBuiltins.h"
39 #include "JSCInlines.h"
40 #include "JSStringBuilder.h"
41 #include "JSStringJoiner.h"
43 #include "ObjectConstructor.h"
44 #include "ObjectPrototype.h"
45 #include "StringRecursionChecker.h"
47 #include <wtf/Assertions.h>
48 #include <wtf/HashSet.h>
52 EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState*);
53 EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState*);
54 EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState*);
55 EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState*);
56 EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState*);
57 EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState*);
58 EncodedJSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState*);
59 EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState*);
60 EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState*);
61 EncodedJSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState*);
62 EncodedJSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState*);
63 EncodedJSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState*);
65 // ------------------------------ ArrayPrototype ----------------------------
67 const ClassInfo ArrayPrototype::s_info = {"Array", &JSArray::s_info, nullptr, CREATE_METHOD_TABLE(ArrayPrototype)};
69 ArrayPrototype* ArrayPrototype::create(VM& vm, JSGlobalObject* globalObject, Structure* structure)
71 ArrayPrototype* prototype = new (NotNull, allocateCell<ArrayPrototype>(vm.heap)) ArrayPrototype(vm, structure);
72 prototype->finishCreation(vm, globalObject);
73 vm.heap.addFinalizer(prototype, destroy);
78 ArrayPrototype::ArrayPrototype(VM& vm, Structure* structure)
79 : JSArray(vm, structure, 0)
83 void ArrayPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
85 Base::finishCreation(vm);
86 ASSERT(inherits(info()));
87 vm.prototypeMap.addPrototype(this);
89 putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().valuesPublicName(), globalObject->arrayProtoValuesFunction(), DontEnum);
90 putDirectWithoutTransition(vm, vm.propertyNames->iteratorSymbol, globalObject->arrayProtoValuesFunction(), DontEnum);
92 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->toString, arrayProtoFuncToString, DontEnum, 0);
93 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->toLocaleString, arrayProtoFuncToLocaleString, DontEnum, 0);
94 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("concat", arrayProtoFuncConcat, DontEnum, 1);
95 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("fill", arrayPrototypeFillCodeGenerator, DontEnum);
96 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->join, arrayProtoFuncJoin, DontEnum, 1);
97 JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("pop", arrayProtoFuncPop, DontEnum, 0, ArrayPopIntrinsic);
98 JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().pushPublicName(), arrayProtoFuncPush, DontEnum, 1, ArrayPushIntrinsic);
99 JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().pushPrivateName(), arrayProtoFuncPush, DontEnum | DontDelete | ReadOnly, 1, ArrayPushIntrinsic);
100 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("reverse", arrayProtoFuncReverse, DontEnum, 0);
101 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().shiftPublicName(), arrayProtoFuncShift, DontEnum, 0);
102 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().shiftPrivateName(), arrayProtoFuncShift, DontEnum | DontDelete | ReadOnly, 0);
103 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->slice, arrayProtoFuncSlice, DontEnum, 2);
104 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("sort", arrayPrototypeSortCodeGenerator, DontEnum);
105 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("splice", arrayProtoFuncSplice, DontEnum, 2);
106 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("unshift", arrayProtoFuncUnShift, DontEnum, 1);
107 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("every", arrayPrototypeEveryCodeGenerator, DontEnum);
108 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("forEach", arrayPrototypeForEachCodeGenerator, DontEnum);
109 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("some", arrayPrototypeSomeCodeGenerator, DontEnum);
110 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("indexOf", arrayProtoFuncIndexOf, DontEnum, 1);
111 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("lastIndexOf", arrayProtoFuncLastIndexOf, DontEnum, 1);
112 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("filter", arrayPrototypeFilterCodeGenerator, DontEnum);
113 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("reduce", arrayPrototypeReduceCodeGenerator, DontEnum);
114 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("reduceRight", arrayPrototypeReduceRightCodeGenerator, DontEnum);
115 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("map", arrayPrototypeMapCodeGenerator, DontEnum);
116 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().entriesPublicName(), arrayPrototypeEntriesCodeGenerator, DontEnum);
117 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().keysPublicName(), arrayPrototypeKeysCodeGenerator, DontEnum);
118 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("find", arrayPrototypeFindCodeGenerator, DontEnum);
119 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("findIndex", arrayPrototypeFindIndexCodeGenerator, DontEnum);
120 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("includes", arrayPrototypeIncludesCodeGenerator, DontEnum);
121 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("copyWithin", arrayPrototypeCopyWithinCodeGenerator, DontEnum);
123 JSObject* unscopables = constructEmptyObject(globalObject->globalExec(), globalObject->nullPrototypeObjectStructure());
124 const char* unscopableNames[] = {
133 for (const char* unscopableName : unscopableNames)
134 unscopables->putDirect(vm, Identifier::fromString(&vm, unscopableName), jsBoolean(true));
135 putDirectWithoutTransition(vm, vm.propertyNames->unscopablesSymbol, unscopables, DontEnum | ReadOnly);
138 void ArrayPrototype::destroy(JSC::JSCell* cell)
140 ArrayPrototype* thisObject = static_cast<ArrayPrototype*>(cell);
141 thisObject->ArrayPrototype::~ArrayPrototype();
144 // ------------------------------ Array Functions ----------------------------
146 static ALWAYS_INLINE JSValue getProperty(ExecState* exec, JSObject* object, unsigned index)
148 if (JSValue result = object->tryGetIndexQuickly(index))
150 // We want to perform get and has in the same operation.
151 // We can only do so when this behavior is not observable. The
152 // only time it is observable is when we encounter a ProxyObject
153 // somewhere in the prototype chain.
154 PropertySlot slot(object, PropertySlot::InternalMethodType::HasProperty);
155 if (!object->getPropertySlot(exec, index, slot))
157 if (UNLIKELY(slot.isTaintedByProxy()))
158 return object->get(exec, index);
159 return slot.getValue(exec, index);
162 static ALWAYS_INLINE void putLength(ExecState* exec, JSObject* obj, JSValue value)
164 PutPropertySlot slot(obj);
165 obj->methodTable()->put(obj, exec, exec->propertyNames().length, value, slot);
168 static ALWAYS_INLINE void setLength(ExecState* exec, JSObject* obj, unsigned value)
171 jsCast<JSArray*>(obj)->setLength(exec, value);
172 putLength(exec, obj, jsNumber(value));
175 enum class SpeciesConstructResult {
181 static ALWAYS_INLINE std::pair<SpeciesConstructResult, JSObject*> speciesConstructArray(ExecState* exec, JSObject* thisObject, unsigned length)
183 // ECMA 9.4.2.3: https://tc39.github.io/ecma262/#sec-arrayspeciescreate
184 JSValue constructor = jsUndefined();
185 if (LIKELY(isArray(exec, thisObject))) {
186 // Fast path in the normal case where the user has not set an own constructor and the Array.prototype.constructor is normal.
187 // We need prototype check for subclasses of Array, which are Array objects but have a different prototype by default.
188 if (LIKELY(!thisObject->hasCustomProperties()
189 && thisObject->globalObject()->arrayPrototype() == thisObject->getPrototypeDirect()
190 && !thisObject->globalObject()->arrayPrototype()->didChangeConstructorOrSpeciesProperties()))
191 return std::make_pair(SpeciesConstructResult::FastPath, nullptr);
193 constructor = thisObject->get(exec, exec->propertyNames().constructor);
194 if (exec->hadException())
195 return std::make_pair(SpeciesConstructResult::Exception, nullptr);
196 if (constructor.isConstructor()) {
197 JSObject* constructorObject = jsCast<JSObject*>(constructor);
198 if (exec->lexicalGlobalObject() != constructorObject->globalObject())
199 return std::make_pair(SpeciesConstructResult::FastPath, nullptr);;
201 if (constructor.isObject()) {
202 constructor = constructor.get(exec, exec->propertyNames().speciesSymbol);
203 if (exec->hadException())
204 return std::make_pair(SpeciesConstructResult::Exception, nullptr);
205 if (constructor.isNull())
206 return std::make_pair(SpeciesConstructResult::FastPath, nullptr);;
208 } else if (exec->hadException())
209 return std::make_pair(SpeciesConstructResult::Exception, nullptr);
211 if (constructor.isUndefined())
212 return std::make_pair(SpeciesConstructResult::FastPath, nullptr);
214 MarkedArgumentBuffer args;
215 args.append(jsNumber(length));
216 JSObject* newObject = construct(exec, constructor, args, "Species construction did not get a valid constructor");
217 if (exec->hadException())
218 return std::make_pair(SpeciesConstructResult::Exception, nullptr);
219 return std::make_pair(SpeciesConstructResult::CreatedObject, newObject);
222 static inline unsigned argumentClampedIndexFromStartOrEnd(ExecState* exec, int argument, unsigned length, unsigned undefinedValue = 0)
224 JSValue value = exec->argument(argument);
225 if (value.isUndefined())
226 return undefinedValue;
228 double indexDouble = value.toInteger(exec);
229 if (indexDouble < 0) {
230 indexDouble += length;
231 return indexDouble < 0 ? 0 : static_cast<unsigned>(indexDouble);
233 return indexDouble > length ? length : static_cast<unsigned>(indexDouble);
236 // The shift/unshift function implement the shift/unshift behaviour required
237 // by the corresponding array prototype methods, and by splice. In both cases,
238 // the methods are operating an an array or array like object.
240 // header currentCount (remainder)
241 // [------][------------][-----------]
242 // header resultCount (remainder)
243 // [------][-----------][-----------]
245 // The set of properties in the range 'header' must be unchanged. The set of
246 // properties in the range 'remainder' (where remainder = length - header -
247 // currentCount) will be shifted to the left or right as appropriate; in the
248 // case of shift this must be removing values, in the case of unshift this
249 // must be introducing new values.
251 template<JSArray::ShiftCountMode shiftCountMode>
252 void shift(ExecState* exec, JSObject* thisObj, unsigned header, unsigned currentCount, unsigned resultCount, unsigned length)
254 RELEASE_ASSERT(currentCount > resultCount);
255 unsigned count = currentCount - resultCount;
257 RELEASE_ASSERT(header <= length);
258 RELEASE_ASSERT(currentCount <= (length - header));
260 if (isJSArray(thisObj)) {
261 JSArray* array = asArray(thisObj);
262 if (array->length() == length && array->shiftCount<shiftCountMode>(exec, header, count))
266 for (unsigned k = header; k < length - currentCount; ++k) {
267 unsigned from = k + currentCount;
268 unsigned to = k + resultCount;
269 if (JSValue value = getProperty(exec, thisObj, from)) {
270 if (exec->hadException())
272 thisObj->putByIndexInline(exec, to, value, true);
273 if (exec->hadException())
275 } else if (!thisObj->methodTable(exec->vm())->deletePropertyByIndex(thisObj, exec, to)) {
276 throwTypeError(exec, ASCIILiteral("Unable to delete property."));
280 for (unsigned k = length; k > length - count; --k) {
281 if (!thisObj->methodTable(exec->vm())->deletePropertyByIndex(thisObj, exec, k - 1)) {
282 throwTypeError(exec, ASCIILiteral("Unable to delete property."));
288 template<JSArray::ShiftCountMode shiftCountMode>
289 void unshift(ExecState* exec, JSObject* thisObj, unsigned header, unsigned currentCount, unsigned resultCount, unsigned length)
291 RELEASE_ASSERT(resultCount > currentCount);
292 unsigned count = resultCount - currentCount;
294 RELEASE_ASSERT(header <= length);
295 RELEASE_ASSERT(currentCount <= (length - header));
297 // Guard against overflow.
298 if (count > (UINT_MAX - length)) {
299 throwOutOfMemoryError(exec);
303 if (isJSArray(thisObj)) {
304 JSArray* array = asArray(thisObj);
305 if (array->length() == length && array->unshiftCount<shiftCountMode>(exec, header, count))
309 for (unsigned k = length - currentCount; k > header; --k) {
310 unsigned from = k + currentCount - 1;
311 unsigned to = k + resultCount - 1;
312 if (JSValue value = getProperty(exec, thisObj, from)) {
313 if (exec->hadException())
315 thisObj->putByIndexInline(exec, to, value, true);
316 } else if (!thisObj->methodTable(exec->vm())->deletePropertyByIndex(thisObj, exec, to)) {
317 throwTypeError(exec, ASCIILiteral("Unable to delete property."));
320 if (exec->hadException())
325 EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec)
327 JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
329 // 1. Let array be the result of calling ToObject on the this value.
330 JSObject* thisObject = thisValue.toObject(exec);
332 if (UNLIKELY(vm.exception()))
333 return JSValue::encode(jsUndefined());
335 // 2. Let func be the result of calling the [[Get]] internal method of array with argument "join".
336 JSValue function = JSValue(thisObject).get(exec, exec->propertyNames().join);
338 // 3. If IsCallable(func) is false, then let func be the standard built-in method Object.prototype.toString (15.2.4.2).
339 bool customJoinCase = false;
340 if (!function.isCell())
341 customJoinCase = true;
343 CallType callType = getCallData(function, callData);
344 if (callType == CallType::None)
345 customJoinCase = true;
347 if (UNLIKELY(customJoinCase))
348 return JSValue::encode(jsMakeNontrivialString(exec, "[object ", thisObject->methodTable(exec->vm())->className(thisObject), "]"));
350 // 4. Return the result of calling the [[Call]] internal method of func providing array as the this value and an empty arguments list.
351 if (!isJSArray(thisObject) || callType != CallType::Host || callData.native.function != arrayProtoFuncJoin)
352 return JSValue::encode(call(exec, function, callType, callData, thisObject, exec->emptyList()));
354 ASSERT(isJSArray(thisValue));
355 JSArray* thisArray = asArray(thisValue);
357 unsigned length = thisArray->length();
359 StringRecursionChecker checker(exec, thisArray);
360 if (JSValue earlyReturnValue = checker.earlyReturnValue())
361 return JSValue::encode(earlyReturnValue);
363 JSStringJoiner joiner(*exec, ',', length);
364 if (UNLIKELY(vm.exception()))
365 return JSValue::encode(jsUndefined());
367 for (unsigned i = 0; i < length; ++i) {
368 JSValue element = thisArray->tryGetIndexQuickly(i);
370 element = thisArray->get(exec, i);
371 if (UNLIKELY(vm.exception()))
372 return JSValue::encode(jsUndefined());
374 joiner.append(*exec, element);
375 if (UNLIKELY(vm.exception()))
376 return JSValue::encode(jsUndefined());
379 return JSValue::encode(joiner.join(*exec));
382 EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec)
384 JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
386 JSObject* thisObject = thisValue.toObject(exec);
387 if (exec->hadException())
388 return JSValue::encode(jsUndefined());
390 unsigned length = getLength(exec, thisObject);
391 if (exec->hadException())
392 return JSValue::encode(jsUndefined());
394 StringRecursionChecker checker(exec, thisObject);
395 if (JSValue earlyReturnValue = checker.earlyReturnValue())
396 return JSValue::encode(earlyReturnValue);
398 JSStringJoiner stringJoiner(*exec, ',', length);
399 if (exec->hadException())
400 return JSValue::encode(jsUndefined());
403 ArgList arguments(exec);
404 for (unsigned i = 0; i < length; ++i) {
405 JSValue element = thisObject->getIndex(exec, i);
406 if (exec->hadException())
407 return JSValue::encode(jsUndefined());
408 if (element.isUndefinedOrNull())
409 element = jsEmptyString(exec);
411 JSValue conversionFunction = element.get(exec, exec->propertyNames().toLocaleString);
412 if (exec->hadException())
413 return JSValue::encode(jsUndefined());
415 CallType callType = getCallData(conversionFunction, callData);
416 if (callType != CallType::None) {
417 element = call(exec, conversionFunction, callType, callData, element, arguments);
418 if (exec->hadException())
419 return JSValue::encode(jsUndefined());
422 stringJoiner.append(*exec, element);
423 if (exec->hadException())
424 return JSValue::encode(jsUndefined());
426 #else // !ENABLE(INTL)
427 for (unsigned i = 0; i < length; ++i) {
428 JSValue element = thisObject->getIndex(exec, i);
429 if (exec->hadException())
430 return JSValue::encode(jsUndefined());
431 if (element.isUndefinedOrNull())
433 JSValue conversionFunction = element.get(exec, exec->propertyNames().toLocaleString);
434 if (exec->hadException())
435 return JSValue::encode(jsUndefined());
437 CallType callType = getCallData(conversionFunction, callData);
438 if (callType != CallType::None) {
439 element = call(exec, conversionFunction, callType, callData, element, exec->emptyList());
440 if (exec->hadException())
441 return JSValue::encode(jsUndefined());
443 stringJoiner.append(*exec, element);
444 if (exec->hadException())
445 return JSValue::encode(jsUndefined());
447 #endif // !ENABLE(INTL)
449 return JSValue::encode(stringJoiner.join(*exec));
452 static inline bool isHole(double value)
454 return std::isnan(value);
457 static inline bool isHole(const WriteBarrier<Unknown>& value)
462 template<typename T> static inline bool containsHole(T* data, unsigned length)
464 for (unsigned i = 0; i < length; ++i) {
471 static inline bool holesMustForwardToPrototype(ExecState& state, JSObject* object)
473 auto& vm = state.vm();
474 return object->structure(vm)->holesMustForwardToPrototype(vm);
477 static inline JSValue join(ExecState& state, JSObject* thisObject, StringView separator)
479 unsigned length = getLength(&state, thisObject);
480 if (state.hadException())
481 return jsUndefined();
483 switch (thisObject->indexingType()) {
484 case ALL_CONTIGUOUS_INDEXING_TYPES:
485 case ALL_INT32_INDEXING_TYPES: {
486 auto& butterfly = *thisObject->butterfly();
487 if (length > butterfly.publicLength())
489 JSStringJoiner joiner(state, separator, length);
490 if (state.hadException())
491 return jsUndefined();
492 auto data = butterfly.contiguous().data();
493 bool holesKnownToBeOK = false;
494 for (unsigned i = 0; i < length; ++i) {
495 if (JSValue value = data[i].get()) {
496 if (!joiner.appendWithoutSideEffects(state, value))
499 if (!holesKnownToBeOK) {
500 if (holesMustForwardToPrototype(state, thisObject))
502 holesKnownToBeOK = true;
504 joiner.appendEmptyString();
507 return joiner.join(state);
509 case ALL_DOUBLE_INDEXING_TYPES: {
510 auto& butterfly = *thisObject->butterfly();
511 if (length > butterfly.publicLength())
513 JSStringJoiner joiner(state, separator, length);
514 if (state.hadException())
515 return jsUndefined();
516 auto data = butterfly.contiguousDouble().data();
517 bool holesKnownToBeOK = false;
518 for (unsigned i = 0; i < length; ++i) {
519 double value = data[i];
521 joiner.append(state, jsDoubleNumber(value));
523 if (!holesKnownToBeOK) {
524 if (thisObject->structure(state.vm())->holesMustForwardToPrototype(state.vm()))
526 holesKnownToBeOK = true;
528 joiner.appendEmptyString();
531 return joiner.join(state);
533 case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
534 auto& storage = *thisObject->butterfly()->arrayStorage();
535 if (length > storage.vectorLength())
537 if (storage.hasHoles() && thisObject->structure(state.vm())->holesMustForwardToPrototype(state.vm()))
539 JSStringJoiner joiner(state, separator, length);
540 if (state.hadException())
541 return jsUndefined();
542 auto data = storage.vector().data();
543 for (unsigned i = 0; i < length; ++i) {
544 if (JSValue value = data[i].get()) {
545 if (!joiner.appendWithoutSideEffects(state, value))
548 joiner.appendEmptyString();
550 return joiner.join(state);
555 JSStringJoiner joiner(state, separator, length);
556 if (state.hadException())
557 return jsUndefined();
558 for (unsigned i = 0; i < length; ++i) {
559 JSValue element = thisObject->getIndex(&state, i);
560 if (state.hadException())
561 return jsUndefined();
562 joiner.append(state, element);
563 if (state.hadException())
564 return jsUndefined();
566 return joiner.join(state);
569 EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec)
571 JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
573 return JSValue::encode(JSValue());
575 StringRecursionChecker checker(exec, thisObject);
576 if (JSValue earlyReturnValue = checker.earlyReturnValue())
577 return JSValue::encode(earlyReturnValue);
579 JSValue separatorValue = exec->argument(0);
580 if (separatorValue.isUndefined()) {
581 const LChar comma = ',';
582 return JSValue::encode(join(*exec, thisObject, { &comma, 1 }));
585 JSString* separator = separatorValue.toString(exec);
586 if (exec->hadException())
587 return JSValue::encode(jsUndefined());
588 return JSValue::encode(join(*exec, thisObject, separator->view(exec).get()));
591 EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState* exec)
593 JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
594 unsigned argCount = exec->argumentCount();
595 JSValue curArg = thisValue.toObject(exec);
597 return JSValue::encode(JSValue());
598 Checked<unsigned, RecordOverflow> finalArraySize = 0;
600 // We need to do species construction before geting the rest of the elements.
601 std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, curArg.getObject(), 0);
602 if (speciesResult.first == SpeciesConstructResult::Exception)
603 return JSValue::encode(jsUndefined());
605 JSArray* currentArray = nullptr;
606 JSArray* previousArray = nullptr;
607 for (unsigned i = 0; ; ++i) {
608 previousArray = currentArray;
609 currentArray = jsDynamicCast<JSArray*>(curArg);
611 // Can't use JSArray::length here because this might be a RuntimeArray!
612 finalArraySize += getLength(exec, currentArray);
613 if (exec->hadException())
614 return JSValue::encode(jsUndefined());
619 curArg = exec->uncheckedArgument(i);
622 if (finalArraySize.hasOverflowed())
623 return JSValue::encode(throwOutOfMemoryError(exec));
625 if (speciesResult.first == SpeciesConstructResult::FastPath && argCount == 1 && previousArray && currentArray && finalArraySize.unsafeGet() < MIN_SPARSE_ARRAY_INDEX) {
626 IndexingType type = JSArray::fastConcatType(exec->vm(), *previousArray, *currentArray);
627 if (type != NonArray)
628 return previousArray->fastConcatWith(*exec, *currentArray);
631 ASSERT(speciesResult.first != SpeciesConstructResult::Exception);
634 if (speciesResult.first == SpeciesConstructResult::CreatedObject)
635 result = speciesResult.second;
637 // We add the newTarget because the compiler gets confused between 0 being a number and a pointer.
638 result = constructEmptyArray(exec, nullptr, 0, JSValue());
639 if (exec->hadException())
640 return JSValue::encode(jsUndefined());
643 curArg = thisValue.toObject(exec);
644 ASSERT(!exec->hadException());
646 for (unsigned i = 0; ; ++i) {
647 if (JSArray* currentArray = jsDynamicCast<JSArray*>(curArg)) {
648 // Can't use JSArray::length here because this might be a RuntimeArray!
649 unsigned length = getLength(exec, currentArray);
650 if (exec->hadException())
651 return JSValue::encode(jsUndefined());
652 for (unsigned k = 0; k < length; ++k) {
653 JSValue v = getProperty(exec, currentArray, k);
654 if (exec->hadException())
655 return JSValue::encode(jsUndefined());
657 result->putDirectIndex(exec, n, v);
661 result->putDirectIndex(exec, n, curArg);
666 curArg = exec->uncheckedArgument(i);
668 setLength(exec, result, n);
669 return JSValue::encode(result);
672 EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState* exec)
674 JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
676 if (isJSArray(thisValue))
677 return JSValue::encode(asArray(thisValue)->pop(exec));
679 JSObject* thisObj = thisValue.toObject(exec);
681 return JSValue::encode(JSValue());
682 unsigned length = getLength(exec, thisObj);
683 if (exec->hadException())
684 return JSValue::encode(jsUndefined());
688 putLength(exec, thisObj, jsNumber(length));
689 result = jsUndefined();
691 result = thisObj->get(exec, length - 1);
692 if (exec->hadException())
693 return JSValue::encode(jsUndefined());
694 if (!thisObj->methodTable(exec->vm())->deletePropertyByIndex(thisObj, exec, length - 1)) {
695 throwTypeError(exec, ASCIILiteral("Unable to delete property."));
696 return JSValue::encode(jsUndefined());
698 putLength(exec, thisObj, jsNumber(length - 1));
700 return JSValue::encode(result);
703 EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState* exec)
705 JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
707 if (isJSArray(thisValue) && exec->argumentCount() == 1) {
708 JSArray* array = asArray(thisValue);
709 array->push(exec, exec->uncheckedArgument(0));
710 return JSValue::encode(jsNumber(array->length()));
713 JSObject* thisObj = thisValue.toObject(exec);
715 return JSValue::encode(JSValue());
716 unsigned length = getLength(exec, thisObj);
717 if (exec->hadException())
718 return JSValue::encode(jsUndefined());
720 for (unsigned n = 0; n < exec->argumentCount(); n++) {
721 // Check for integer overflow; where safe we can do a fast put by index.
722 if (length + n >= length)
723 thisObj->methodTable()->putByIndex(thisObj, exec, length + n, exec->uncheckedArgument(n), true);
725 PutPropertySlot slot(thisObj);
726 Identifier propertyName = Identifier::fromString(exec, JSValue(static_cast<int64_t>(length) + static_cast<int64_t>(n)).toWTFString(exec));
727 thisObj->methodTable()->put(thisObj, exec, propertyName, exec->uncheckedArgument(n), slot);
729 if (exec->hadException())
730 return JSValue::encode(jsUndefined());
733 JSValue newLength(static_cast<int64_t>(length) + static_cast<int64_t>(exec->argumentCount()));
734 putLength(exec, thisObj, newLength);
735 return JSValue::encode(newLength);
738 EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState* exec)
740 JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
742 return JSValue::encode(JSValue());
745 unsigned length = getLength(exec, thisObject);
747 return JSValue::encode(jsUndefined());
749 switch (thisObject->indexingType()) {
750 case ALL_CONTIGUOUS_INDEXING_TYPES:
751 case ALL_INT32_INDEXING_TYPES: {
752 auto& butterfly = *thisObject->butterfly();
753 if (length > butterfly.publicLength())
755 auto data = butterfly.contiguous().data();
756 if (containsHole(data, length) && holesMustForwardToPrototype(*exec, thisObject))
758 std::reverse(data, data + length);
759 return JSValue::encode(thisObject);
761 case ALL_DOUBLE_INDEXING_TYPES: {
762 auto& butterfly = *thisObject->butterfly();
763 if (length > butterfly.publicLength())
765 auto data = butterfly.contiguousDouble().data();
766 if (containsHole(data, length) && holesMustForwardToPrototype(*exec, thisObject))
768 std::reverse(data, data + length);
769 return JSValue::encode(thisObject);
771 case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
772 auto& storage = *thisObject->butterfly()->arrayStorage();
773 if (length > storage.vectorLength())
775 if (storage.hasHoles() && holesMustForwardToPrototype(*exec, thisObject))
777 auto data = storage.vector().data();
778 std::reverse(data, data + length);
779 return JSValue::encode(thisObject);
783 unsigned middle = length / 2;
784 for (unsigned lower = 0; lower < middle; lower++) {
785 unsigned upper = length - lower - 1;
786 bool lowerExists = thisObject->hasProperty(exec, lower);
788 return JSValue::encode(jsUndefined());
791 lowerValue = thisObject->get(exec, lower);
793 bool upperExists = thisObject->hasProperty(exec, upper);
795 return JSValue::encode(jsUndefined());
798 upperValue = thisObject->get(exec, upper);
801 thisObject->putByIndexInline(exec, lower, upperValue, true);
803 return JSValue::encode(JSValue());
804 } else if (!thisObject->methodTable(vm)->deletePropertyByIndex(thisObject, exec, lower)) {
806 throwTypeError(exec, ASCIILiteral("Unable to delete property."));
807 return JSValue::encode(JSValue());
811 thisObject->putByIndexInline(exec, upper, lowerValue, true);
813 return JSValue::encode(JSValue());
814 } else if (!thisObject->methodTable(vm)->deletePropertyByIndex(thisObject, exec, upper)) {
816 throwTypeError(exec, ASCIILiteral("Unable to delete property."));
817 return JSValue::encode(JSValue());
820 return JSValue::encode(thisObject);
823 EncodedJSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState* exec)
825 JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
827 return JSValue::encode(JSValue());
828 unsigned length = getLength(exec, thisObj);
829 if (exec->hadException())
830 return JSValue::encode(jsUndefined());
834 putLength(exec, thisObj, jsNumber(length));
835 result = jsUndefined();
837 result = thisObj->getIndex(exec, 0);
838 shift<JSArray::ShiftCountForShift>(exec, thisObj, 0, 1, 0, length);
839 if (exec->hadException())
840 return JSValue::encode(jsUndefined());
841 putLength(exec, thisObj, jsNumber(length - 1));
843 return JSValue::encode(result);
846 EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec)
848 // http://developer.netscape.com/docs/manuals/js/client/jsref/array.htm#1193713 or 15.4.4.10
849 JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
851 return JSValue::encode(JSValue());
852 unsigned length = getLength(exec, thisObj);
853 if (exec->hadException())
854 return JSValue::encode(jsUndefined());
856 unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length);
857 unsigned end = argumentClampedIndexFromStartOrEnd(exec, 1, length, length);
859 std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, end - begin);
860 // We can only get an exception if we call some user function.
861 if (UNLIKELY(speciesResult.first == SpeciesConstructResult::Exception))
862 return JSValue::encode(jsUndefined());
864 if (LIKELY(speciesResult.first == SpeciesConstructResult::FastPath && isJSArray(thisObj) && length == getLength(exec, thisObj))) {
865 if (JSArray* result = asArray(thisObj)->fastSlice(*exec, begin, end - begin))
866 return JSValue::encode(result);
870 if (speciesResult.first == SpeciesConstructResult::CreatedObject)
871 result = speciesResult.second;
873 result = constructEmptyArray(exec, nullptr, end - begin);
876 for (unsigned k = begin; k < end; k++, n++) {
877 JSValue v = getProperty(exec, thisObj, k);
878 if (exec->hadException())
879 return JSValue::encode(jsUndefined());
881 result->putDirectIndex(exec, n, v);
883 setLength(exec, result, n);
884 return JSValue::encode(result);
887 EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec)
893 JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
895 return JSValue::encode(JSValue());
896 unsigned length = getLength(exec, thisObj);
897 if (exec->hadException())
898 return JSValue::encode(jsUndefined());
900 if (!exec->argumentCount()) {
901 std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, 0);
902 if (speciesResult.first == SpeciesConstructResult::Exception)
903 return JSValue::encode(jsUndefined());
906 if (speciesResult.first == SpeciesConstructResult::CreatedObject)
907 result = speciesResult.second;
909 result = constructEmptyArray(exec, nullptr);
911 setLength(exec, result, 0);
912 return JSValue::encode(result);
915 unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length);
917 unsigned deleteCount = length - begin;
918 if (exec->argumentCount() > 1) {
919 double deleteDouble = exec->uncheckedArgument(1).toInteger(exec);
920 if (deleteDouble < 0)
922 else if (deleteDouble > length - begin)
923 deleteCount = length - begin;
925 deleteCount = static_cast<unsigned>(deleteDouble);
928 std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, deleteCount);
929 if (speciesResult.first == SpeciesConstructResult::Exception)
930 return JSValue::encode(jsUndefined());
932 JSObject* result = nullptr;
933 if (speciesResult.first == SpeciesConstructResult::FastPath && isJSArray(thisObj) && length == getLength(exec, thisObj))
934 result = asArray(thisObj)->fastSlice(*exec, begin, deleteCount);
937 if (speciesResult.first == SpeciesConstructResult::CreatedObject) {
938 result = speciesResult.second;
940 for (unsigned k = 0; k < deleteCount; ++k) {
941 JSValue v = getProperty(exec, thisObj, k + begin);
942 if (exec->hadException())
943 return JSValue::encode(jsUndefined());
944 result->putByIndexInline(exec, k, v, true);
945 if (exec->hadException())
946 return JSValue::encode(jsUndefined());
949 result = JSArray::tryCreateUninitialized(vm, exec->lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), deleteCount);
951 return JSValue::encode(throwOutOfMemoryError(exec));
953 for (unsigned k = 0; k < deleteCount; ++k) {
954 JSValue v = getProperty(exec, thisObj, k + begin);
955 if (exec->hadException())
956 return JSValue::encode(jsUndefined());
957 result->initializeIndex(vm, k, v);
962 unsigned additionalArgs = std::max<int>(exec->argumentCount() - 2, 0);
963 if (additionalArgs < deleteCount) {
964 shift<JSArray::ShiftCountForSplice>(exec, thisObj, begin, deleteCount, additionalArgs, length);
965 if (exec->hadException())
966 return JSValue::encode(jsUndefined());
967 } else if (additionalArgs > deleteCount) {
968 unshift<JSArray::ShiftCountForSplice>(exec, thisObj, begin, deleteCount, additionalArgs, length);
969 if (exec->hadException())
970 return JSValue::encode(jsUndefined());
972 for (unsigned k = 0; k < additionalArgs; ++k) {
973 thisObj->putByIndexInline(exec, k + begin, exec->uncheckedArgument(k + 2), true);
974 if (exec->hadException())
975 return JSValue::encode(jsUndefined());
978 setLength(exec, thisObj, length - deleteCount + additionalArgs);
979 return JSValue::encode(result);
982 EncodedJSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState* exec)
986 JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
988 return JSValue::encode(JSValue());
989 unsigned length = getLength(exec, thisObj);
990 if (exec->hadException())
991 return JSValue::encode(jsUndefined());
993 unsigned nrArgs = exec->argumentCount();
995 unshift<JSArray::ShiftCountForShift>(exec, thisObj, 0, 0, nrArgs, length);
996 if (exec->hadException())
997 return JSValue::encode(jsUndefined());
999 for (unsigned k = 0; k < nrArgs; ++k) {
1000 thisObj->putByIndexInline(exec, k, exec->uncheckedArgument(k), true);
1001 if (exec->hadException())
1002 return JSValue::encode(jsUndefined());
1004 JSValue result = jsNumber(length + nrArgs);
1005 putLength(exec, thisObj, result);
1006 return JSValue::encode(result);
1009 EncodedJSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState* exec)
1012 JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
1014 return JSValue::encode(JSValue());
1015 unsigned length = getLength(exec, thisObj);
1016 if (exec->hadException())
1017 return JSValue::encode(jsUndefined());
1019 unsigned index = argumentClampedIndexFromStartOrEnd(exec, 1, length);
1020 JSValue searchElement = exec->argument(0);
1021 for (; index < length; ++index) {
1022 JSValue e = getProperty(exec, thisObj, index);
1023 if (exec->hadException())
1024 return JSValue::encode(jsUndefined());
1027 if (JSValue::strictEqual(exec, searchElement, e))
1028 return JSValue::encode(jsNumber(index));
1031 return JSValue::encode(jsNumber(-1));
1034 EncodedJSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState* exec)
1037 JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
1039 return JSValue::encode(JSValue());
1040 unsigned length = getLength(exec, thisObj);
1042 return JSValue::encode(jsNumber(-1));
1044 unsigned index = length - 1;
1045 if (exec->argumentCount() >= 2) {
1046 JSValue fromValue = exec->uncheckedArgument(1);
1047 double fromDouble = fromValue.toInteger(exec);
1048 if (fromDouble < 0) {
1049 fromDouble += length;
1051 return JSValue::encode(jsNumber(-1));
1053 if (fromDouble < length)
1054 index = static_cast<unsigned>(fromDouble);
1057 JSValue searchElement = exec->argument(0);
1059 RELEASE_ASSERT(index < length);
1060 JSValue e = getProperty(exec, thisObj, index);
1061 if (exec->hadException())
1062 return JSValue::encode(jsUndefined());
1065 if (JSValue::strictEqual(exec, searchElement, e))
1066 return JSValue::encode(jsNumber(index));
1069 return JSValue::encode(jsNumber(-1));
1072 // -------------------- ArrayPrototype.constructor Watchpoint ------------------
1074 class ArrayPrototypeAdaptiveInferredPropertyWatchpoint : public AdaptiveInferredPropertyValueWatchpointBase {
1076 typedef AdaptiveInferredPropertyValueWatchpointBase Base;
1077 ArrayPrototypeAdaptiveInferredPropertyWatchpoint(const ObjectPropertyCondition&, ArrayPrototype*);
1080 void handleFire(const FireDetail&) override;
1082 ArrayPrototype* m_arrayPrototype;
1085 void ArrayPrototype::setConstructor(VM& vm, JSObject* constructorProperty, unsigned attributes)
1087 putDirectWithoutTransition(vm, vm.propertyNames->constructor, constructorProperty, attributes);
1089 // Do the watchpoint on our constructor property
1090 PropertyOffset offset = this->structure()->get(vm, vm.propertyNames->constructor);
1091 ASSERT(isValidOffset(offset));
1092 this->structure()->startWatchingPropertyForReplacements(vm, offset);
1094 ObjectPropertyCondition condition = ObjectPropertyCondition::equivalence(vm, this, this, vm.propertyNames->constructor.impl(), constructorProperty);
1095 ASSERT(condition.isWatchable());
1097 m_constructorWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(condition, this);
1098 m_constructorWatchpoint->install();
1100 // Do the watchpoint on the constructor's Symbol.species property
1101 offset = constructorProperty->structure()->get(vm, vm.propertyNames->speciesSymbol);
1102 ASSERT(isValidOffset(offset));
1103 constructorProperty->structure()->startWatchingPropertyForReplacements(vm, offset);
1105 ASSERT(constructorProperty->getDirect(offset).isGetterSetter());
1106 condition = ObjectPropertyCondition::equivalence(vm, this, constructorProperty, vm.propertyNames->speciesSymbol.impl(), constructorProperty->getDirect(offset));
1107 ASSERT(condition.isWatchable());
1109 m_constructorSpeciesWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(condition, this);
1110 m_constructorSpeciesWatchpoint->install();
1113 ArrayPrototypeAdaptiveInferredPropertyWatchpoint::ArrayPrototypeAdaptiveInferredPropertyWatchpoint(const ObjectPropertyCondition& key, ArrayPrototype* prototype)
1115 , m_arrayPrototype(prototype)
1119 void ArrayPrototypeAdaptiveInferredPropertyWatchpoint::handleFire(const FireDetail& detail)
1121 StringPrintStream out;
1122 out.print("ArrayPrototype adaption of ", key(), " failed: ", detail);
1124 StringFireDetail stringDetail(out.toCString().data());
1126 m_arrayPrototype->m_didChangeConstructorOrSpeciesProperties = true;