2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3 * Copyright (C) 2003-2017 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 "CodeBlock.h"
33 #include "GetterSetter.h"
34 #include "Interpreter.h"
36 #include "JSArrayInlines.h"
37 #include "JSCBuiltins.h"
38 #include "JSCInlines.h"
39 #include "JSImmutableButterfly.h"
40 #include "JSStringJoiner.h"
42 #include "ObjectConstructor.h"
43 #include "ObjectPrototype.h"
44 #include "Operations.h"
45 #include "StringRecursionChecker.h"
47 #include <wtf/Assertions.h>
51 EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState*);
52 EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState*);
53 EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState*);
54 EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState*);
55 EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState*);
56 EncodedJSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState*);
57 EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState*);
58 EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState*);
59 EncodedJSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState*);
60 EncodedJSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState*);
61 EncodedJSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState*);
63 // ------------------------------ ArrayPrototype ----------------------------
65 const ClassInfo ArrayPrototype::s_info = {"Array", &JSArray::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(ArrayPrototype)};
67 ArrayPrototype* ArrayPrototype::create(VM& vm, JSGlobalObject* globalObject, Structure* structure)
69 ArrayPrototype* prototype = new (NotNull, allocateCell<ArrayPrototype>(vm.heap)) ArrayPrototype(vm, structure);
70 prototype->finishCreation(vm, globalObject);
71 vm.heap.addFinalizer(prototype, destroy);
76 ArrayPrototype::ArrayPrototype(VM& vm, Structure* structure)
77 : JSArray(vm, structure, 0)
81 void ArrayPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
83 Base::finishCreation(vm);
84 ASSERT(inherits(vm, info()));
87 putDirectWithoutTransition(vm, vm.propertyNames->toString, globalObject->arrayProtoToStringFunction(), static_cast<unsigned>(PropertyAttribute::DontEnum));
88 putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().valuesPublicName(), globalObject->arrayProtoValuesFunction(), static_cast<unsigned>(PropertyAttribute::DontEnum));
89 putDirectWithoutTransition(vm, vm.propertyNames->iteratorSymbol, globalObject->arrayProtoValuesFunction(), static_cast<unsigned>(PropertyAttribute::DontEnum));
91 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->toLocaleString, arrayProtoFuncToLocaleString, static_cast<unsigned>(PropertyAttribute::DontEnum), 0);
92 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("concat", arrayPrototypeConcatCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
93 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("fill", arrayPrototypeFillCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
94 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->join, arrayProtoFuncJoin, static_cast<unsigned>(PropertyAttribute::DontEnum), 1);
95 JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("pop", arrayProtoFuncPop, static_cast<unsigned>(PropertyAttribute::DontEnum), 0, ArrayPopIntrinsic);
96 JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().pushPublicName(), arrayProtoFuncPush, static_cast<unsigned>(PropertyAttribute::DontEnum), 1, ArrayPushIntrinsic);
97 JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().pushPrivateName(), arrayProtoFuncPush, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly, 1, ArrayPushIntrinsic);
98 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("reverse", arrayProtoFuncReverse, static_cast<unsigned>(PropertyAttribute::DontEnum), 0);
99 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().shiftPublicName(), arrayProtoFuncShift, static_cast<unsigned>(PropertyAttribute::DontEnum), 0);
100 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().shiftPrivateName(), arrayProtoFuncShift, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly, 0);
101 JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->slice, arrayProtoFuncSlice, static_cast<unsigned>(PropertyAttribute::DontEnum), 2, ArraySliceIntrinsic);
102 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("sort", arrayPrototypeSortCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
103 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("splice", arrayProtoFuncSplice, static_cast<unsigned>(PropertyAttribute::DontEnum), 2);
104 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("unshift", arrayProtoFuncUnShift, static_cast<unsigned>(PropertyAttribute::DontEnum), 1);
105 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("every", arrayPrototypeEveryCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
106 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("forEach", arrayPrototypeForEachCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
107 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("some", arrayPrototypeSomeCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
108 JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("indexOf", arrayProtoFuncIndexOf, static_cast<unsigned>(PropertyAttribute::DontEnum), 1, ArrayIndexOfIntrinsic);
109 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("lastIndexOf", arrayProtoFuncLastIndexOf, static_cast<unsigned>(PropertyAttribute::DontEnum), 1);
110 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("filter", arrayPrototypeFilterCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
111 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("flat", arrayPrototypeFlatCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
112 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("flatMap", arrayPrototypeFlatMapCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
113 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("reduce", arrayPrototypeReduceCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
114 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("reduceRight", arrayPrototypeReduceRightCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
115 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("map", arrayPrototypeMapCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
116 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().entriesPublicName(), arrayPrototypeEntriesCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
117 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().keysPublicName(), arrayPrototypeKeysCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
118 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("find", arrayPrototypeFindCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
119 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("findIndex", arrayPrototypeFindIndexCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
120 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("includes", arrayPrototypeIncludesCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
121 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("copyWithin", arrayPrototypeCopyWithinCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
123 putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().entriesPrivateName(), getDirect(vm, vm.propertyNames->builtinNames().entriesPublicName()), static_cast<unsigned>(PropertyAttribute::ReadOnly));
124 putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().forEachPrivateName(), getDirect(vm, vm.propertyNames->builtinNames().forEachPublicName()), static_cast<unsigned>(PropertyAttribute::ReadOnly));
125 putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().keysPrivateName(), getDirect(vm, vm.propertyNames->builtinNames().keysPublicName()), static_cast<unsigned>(PropertyAttribute::ReadOnly));
126 putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().valuesPrivateName(), globalObject->arrayProtoValuesFunction(), static_cast<unsigned>(PropertyAttribute::ReadOnly));
128 JSObject* unscopables = constructEmptyObject(globalObject->globalExec(), globalObject->nullPrototypeObjectStructure());
129 const char* unscopableNames[] = {
139 for (const char* unscopableName : unscopableNames)
140 unscopables->putDirect(vm, Identifier::fromString(&vm, unscopableName), jsBoolean(true));
141 putDirectWithoutTransition(vm, vm.propertyNames->unscopablesSymbol, unscopables, PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly);
144 void ArrayPrototype::destroy(JSC::JSCell* cell)
146 ArrayPrototype* thisObject = static_cast<ArrayPrototype*>(cell);
147 thisObject->ArrayPrototype::~ArrayPrototype();
150 // ------------------------------ Array Functions ----------------------------
152 static ALWAYS_INLINE JSValue getProperty(ExecState* exec, JSObject* object, unsigned index)
155 auto scope = DECLARE_THROW_SCOPE(vm);
157 if (JSValue result = object->tryGetIndexQuickly(index))
159 // We want to perform get and has in the same operation.
160 // We can only do so when this behavior is not observable. The
161 // only time it is observable is when we encounter an opaque objects (ProxyObject and JSModuleNamespaceObject)
162 // somewhere in the prototype chain.
163 PropertySlot slot(object, PropertySlot::InternalMethodType::HasProperty);
164 bool hasProperty = object->getPropertySlot(exec, index, slot);
165 EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
168 if (UNLIKELY(slot.isTaintedByOpaqueObject())) {
170 return object->get(exec, index);
173 return slot.getValue(exec, index);
176 static ALWAYS_INLINE bool putLength(ExecState* exec, VM& vm, JSObject* obj, JSValue value)
178 PutPropertySlot slot(obj);
179 return obj->methodTable(vm)->put(obj, exec, vm.propertyNames->length, value, slot);
182 static ALWAYS_INLINE void setLength(ExecState* exec, VM& vm, JSObject* obj, unsigned value)
184 auto scope = DECLARE_THROW_SCOPE(vm);
185 static const bool throwException = true;
186 if (isJSArray(obj)) {
187 jsCast<JSArray*>(obj)->setLength(exec, value, throwException);
188 RETURN_IF_EXCEPTION(scope, void());
190 bool success = putLength(exec, vm, obj, jsNumber(value));
191 RETURN_IF_EXCEPTION(scope, void());
192 if (UNLIKELY(!success))
193 throwTypeError(exec, scope, ReadonlyPropertyWriteError);
196 ALWAYS_INLINE bool speciesWatchpointIsValid(ExecState* exec, JSObject* thisObject)
199 JSGlobalObject* globalObject = thisObject->globalObject(vm);
200 ArrayPrototype* arrayPrototype = globalObject->arrayPrototype();
202 if (globalObject->arraySpeciesWatchpoint().stateOnJSThread() == ClearWatchpoint) {
203 arrayPrototype->tryInitializeSpeciesWatchpoint(exec);
204 ASSERT(globalObject->arraySpeciesWatchpoint().stateOnJSThread() != ClearWatchpoint);
207 return !thisObject->hasCustomProperties(vm)
208 && arrayPrototype == thisObject->getPrototypeDirect(vm)
209 && globalObject->arraySpeciesWatchpoint().stateOnJSThread() == IsWatched;
212 enum class SpeciesConstructResult {
218 static ALWAYS_INLINE std::pair<SpeciesConstructResult, JSObject*> speciesConstructArray(ExecState* exec, JSObject* thisObject, unsigned length)
221 auto scope = DECLARE_THROW_SCOPE(vm);
223 auto exceptionResult = [] () {
224 return std::make_pair(SpeciesConstructResult::Exception, nullptr);
227 // ECMA 9.4.2.3: https://tc39.github.io/ecma262/#sec-arrayspeciescreate
228 JSValue constructor = jsUndefined();
229 bool thisIsArray = isArray(exec, thisObject);
230 RETURN_IF_EXCEPTION(scope, exceptionResult());
231 if (LIKELY(thisIsArray)) {
232 // Fast path in the normal case where the user has not set an own constructor and the Array.prototype.constructor is normal.
233 // We need prototype check for subclasses of Array, which are Array objects but have a different prototype by default.
234 bool isValid = speciesWatchpointIsValid(exec, thisObject);
235 scope.assertNoException();
237 return std::make_pair(SpeciesConstructResult::FastPath, nullptr);
239 constructor = thisObject->get(exec, vm.propertyNames->constructor);
240 RETURN_IF_EXCEPTION(scope, exceptionResult());
241 if (constructor.isConstructor(vm)) {
242 JSObject* constructorObject = jsCast<JSObject*>(constructor);
243 if (exec->lexicalGlobalObject() != constructorObject->globalObject(vm))
244 return std::make_pair(SpeciesConstructResult::FastPath, nullptr);;
246 if (constructor.isObject()) {
247 constructor = constructor.get(exec, vm.propertyNames->speciesSymbol);
248 RETURN_IF_EXCEPTION(scope, exceptionResult());
249 if (constructor.isNull())
250 return std::make_pair(SpeciesConstructResult::FastPath, nullptr);;
253 // If isArray is false, return ? ArrayCreate(length).
254 return std::make_pair(SpeciesConstructResult::FastPath, nullptr);
257 if (constructor.isUndefined())
258 return std::make_pair(SpeciesConstructResult::FastPath, nullptr);
260 MarkedArgumentBuffer args;
261 args.append(jsNumber(length));
262 ASSERT(!args.hasOverflowed());
263 JSObject* newObject = construct(exec, constructor, args, "Species construction did not get a valid constructor");
264 RETURN_IF_EXCEPTION(scope, exceptionResult());
265 return std::make_pair(SpeciesConstructResult::CreatedObject, newObject);
268 static inline unsigned argumentClampedIndexFromStartOrEnd(ExecState* exec, int argument, unsigned length, unsigned undefinedValue = 0)
270 JSValue value = exec->argument(argument);
271 if (value.isUndefined())
272 return undefinedValue;
274 double indexDouble = value.toInteger(exec);
275 if (indexDouble < 0) {
276 indexDouble += length;
277 return indexDouble < 0 ? 0 : static_cast<unsigned>(indexDouble);
279 return indexDouble > length ? length : static_cast<unsigned>(indexDouble);
282 // The shift/unshift function implement the shift/unshift behaviour required
283 // by the corresponding array prototype methods, and by splice. In both cases,
284 // the methods are operating an an array or array like object.
286 // header currentCount (remainder)
287 // [------][------------][-----------]
288 // header resultCount (remainder)
289 // [------][-----------][-----------]
291 // The set of properties in the range 'header' must be unchanged. The set of
292 // properties in the range 'remainder' (where remainder = length - header -
293 // currentCount) will be shifted to the left or right as appropriate; in the
294 // case of shift this must be removing values, in the case of unshift this
295 // must be introducing new values.
297 template<JSArray::ShiftCountMode shiftCountMode>
298 void shift(ExecState* exec, JSObject* thisObj, unsigned header, unsigned currentCount, unsigned resultCount, unsigned length)
301 auto scope = DECLARE_THROW_SCOPE(vm);
303 RELEASE_ASSERT(currentCount > resultCount);
304 unsigned count = currentCount - resultCount;
306 RELEASE_ASSERT(header <= length);
307 RELEASE_ASSERT(currentCount <= (length - header));
309 if (isJSArray(thisObj)) {
310 JSArray* array = asArray(thisObj);
311 if (array->length() == length && array->shiftCount<shiftCountMode>(exec, header, count))
315 for (unsigned k = header; k < length - currentCount; ++k) {
316 unsigned from = k + currentCount;
317 unsigned to = k + resultCount;
318 JSValue value = getProperty(exec, thisObj, from);
319 RETURN_IF_EXCEPTION(scope, void());
321 thisObj->putByIndexInline(exec, to, value, true);
322 RETURN_IF_EXCEPTION(scope, void());
324 bool success = thisObj->methodTable(vm)->deletePropertyByIndex(thisObj, exec, to);
325 RETURN_IF_EXCEPTION(scope, void());
327 throwTypeError(exec, scope, UnableToDeletePropertyError);
332 for (unsigned k = length; k > length - count; --k) {
333 bool success = thisObj->methodTable(vm)->deletePropertyByIndex(thisObj, exec, k - 1);
334 RETURN_IF_EXCEPTION(scope, void());
336 throwTypeError(exec, scope, UnableToDeletePropertyError);
342 template<JSArray::ShiftCountMode shiftCountMode>
343 void unshift(ExecState* exec, JSObject* thisObj, unsigned header, unsigned currentCount, unsigned resultCount, unsigned length)
346 auto scope = DECLARE_THROW_SCOPE(vm);
348 RELEASE_ASSERT(resultCount > currentCount);
349 unsigned count = resultCount - currentCount;
351 RELEASE_ASSERT(header <= length);
352 RELEASE_ASSERT(currentCount <= (length - header));
354 // Guard against overflow.
355 if (count > UINT_MAX - length) {
356 throwOutOfMemoryError(exec, scope);
360 if (isJSArray(thisObj)) {
361 JSArray* array = asArray(thisObj);
362 if (array->length() == length) {
363 bool handled = array->unshiftCount<shiftCountMode>(exec, header, count);
364 EXCEPTION_ASSERT(!scope.exception() || handled);
370 for (unsigned k = length - currentCount; k > header; --k) {
371 unsigned from = k + currentCount - 1;
372 unsigned to = k + resultCount - 1;
373 JSValue value = getProperty(exec, thisObj, from);
374 RETURN_IF_EXCEPTION(scope, void());
376 thisObj->putByIndexInline(exec, to, value, true);
377 RETURN_IF_EXCEPTION(scope, void());
379 bool success = thisObj->methodTable(vm)->deletePropertyByIndex(thisObj, exec, to);
380 RETURN_IF_EXCEPTION(scope, void());
381 if (UNLIKELY(!success)) {
382 throwTypeError(exec, scope, UnableToDeletePropertyError);
389 inline bool canUseFastJoin(const JSObject* thisObject)
391 switch (thisObject->indexingType()) {
392 case ALL_CONTIGUOUS_INDEXING_TYPES:
393 case ALL_INT32_INDEXING_TYPES:
394 case ALL_DOUBLE_INDEXING_TYPES:
402 inline bool holesMustForwardToPrototype(VM& vm, JSObject* object)
404 return object->structure(vm)->holesMustForwardToPrototype(vm, object);
407 inline bool isHole(double value)
409 return std::isnan(value);
412 inline bool isHole(const WriteBarrier<Unknown>& value)
418 inline bool containsHole(T* data, unsigned length)
420 for (unsigned i = 0; i < length; ++i) {
427 inline JSValue fastJoin(ExecState& state, JSObject* thisObject, StringView separator, unsigned length, bool* sawHoles = nullptr)
430 auto scope = DECLARE_THROW_SCOPE(vm);
432 switch (thisObject->indexingType()) {
433 case ALL_INT32_INDEXING_TYPES: {
434 auto& butterfly = *thisObject->butterfly();
435 if (UNLIKELY(length > butterfly.publicLength()))
437 JSStringJoiner joiner(state, separator, length);
438 RETURN_IF_EXCEPTION(scope, { });
439 auto data = butterfly.contiguous().data();
440 bool holesKnownToBeOK = false;
441 for (unsigned i = 0; i < length; ++i) {
442 JSValue value = data[i].get();
444 joiner.appendNumber(vm, value.asInt32());
448 if (!holesKnownToBeOK) {
449 if (holesMustForwardToPrototype(vm, thisObject))
451 holesKnownToBeOK = true;
453 joiner.appendEmptyString();
457 return joiner.join(state);
459 case ALL_CONTIGUOUS_INDEXING_TYPES: {
460 auto& butterfly = *thisObject->butterfly();
461 if (UNLIKELY(length > butterfly.publicLength()))
463 JSStringJoiner joiner(state, separator, length);
464 RETURN_IF_EXCEPTION(scope, { });
465 auto data = butterfly.contiguous().data();
466 bool holesKnownToBeOK = false;
467 for (unsigned i = 0; i < length; ++i) {
468 if (JSValue value = data[i].get()) {
469 if (!joiner.appendWithoutSideEffects(state, value))
474 if (!holesKnownToBeOK) {
475 if (holesMustForwardToPrototype(vm, thisObject))
477 holesKnownToBeOK = true;
479 joiner.appendEmptyString();
483 return joiner.join(state);
485 case ALL_DOUBLE_INDEXING_TYPES: {
486 auto& butterfly = *thisObject->butterfly();
487 if (UNLIKELY(length > butterfly.publicLength()))
489 JSStringJoiner joiner(state, separator, length);
490 RETURN_IF_EXCEPTION(scope, { });
491 auto data = butterfly.contiguousDouble().data();
492 bool holesKnownToBeOK = false;
493 for (unsigned i = 0; i < length; ++i) {
494 double value = data[i];
495 if (LIKELY(!isHole(value)))
496 joiner.appendNumber(vm, value);
500 if (!holesKnownToBeOK) {
501 if (holesMustForwardToPrototype(vm, thisObject))
503 holesKnownToBeOK = true;
505 joiner.appendEmptyString();
509 return joiner.join(state);
514 JSStringJoiner joiner(state, separator, length);
515 RETURN_IF_EXCEPTION(scope, { });
516 for (unsigned i = 0; i < length; ++i) {
517 JSValue element = thisObject->getIndex(&state, i);
518 RETURN_IF_EXCEPTION(scope, { });
519 joiner.append(state, element);
520 RETURN_IF_EXCEPTION(scope, { });
523 return joiner.join(state);
526 EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec)
529 auto scope = DECLARE_THROW_SCOPE(vm);
530 JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
532 // 1. Let array be the result of calling ToObject on the this value.
533 JSObject* thisObject = thisValue.toObject(exec);
534 RETURN_IF_EXCEPTION(scope, encodedJSValue());
536 // 2. Let func be the result of calling the [[Get]] internal method of array with argument "join".
537 JSValue function = JSValue(thisObject).get(exec, vm.propertyNames->join);
538 RETURN_IF_EXCEPTION(scope, encodedJSValue());
540 // 3. If IsCallable(func) is false, then let func be the standard built-in method Object.prototype.toString (15.2.4.2).
541 bool customJoinCase = false;
542 if (!function.isCell())
543 customJoinCase = true;
545 CallType callType = getCallData(vm, function, callData);
546 if (callType == CallType::None)
547 customJoinCase = true;
549 if (UNLIKELY(customJoinCase)) {
551 return JSValue::encode(jsMakeNontrivialString(exec, "[object ", thisObject->methodTable(vm)->className(thisObject), "]"));
554 // 4. Return the result of calling the [[Call]] internal method of func providing array as the this value and an empty arguments list.
555 if (!isJSArray(thisObject) || callType != CallType::Host || callData.native.function != arrayProtoFuncJoin) {
557 return JSValue::encode(call(exec, function, callType, callData, thisObject, *vm.emptyList));
560 ASSERT(isJSArray(thisValue));
561 JSArray* thisArray = asArray(thisValue);
563 unsigned length = thisArray->length();
565 StringRecursionChecker checker(exec, thisArray);
566 EXCEPTION_ASSERT(!scope.exception() || checker.earlyReturnValue());
567 if (JSValue earlyReturnValue = checker.earlyReturnValue())
568 return JSValue::encode(earlyReturnValue);
570 if (LIKELY(canUseFastJoin(thisArray))) {
571 const LChar comma = ',';
574 bool isCoW = isCopyOnWrite(thisArray->indexingMode());
575 JSImmutableButterfly* immutableButterfly = nullptr;
577 immutableButterfly = JSImmutableButterfly::fromButterfly(thisArray->butterfly());
578 auto iter = vm.heap.immutableButterflyToStringCache.find(immutableButterfly);
579 if (iter != vm.heap.immutableButterflyToStringCache.end())
580 return JSValue::encode(iter->value);
583 bool sawHoles = false;
584 JSValue result = fastJoin(*exec, thisArray, { &comma, 1 }, length, &sawHoles);
586 if (!sawHoles && result && isJSString(result) && isCoW) {
587 ASSERT(JSImmutableButterfly::fromButterfly(thisArray->butterfly()) == immutableButterfly);
588 vm.heap.immutableButterflyToStringCache.add(immutableButterfly, jsCast<JSString*>(result));
591 return JSValue::encode(result);
594 JSStringJoiner joiner(*exec, ',', length);
595 RETURN_IF_EXCEPTION(scope, encodedJSValue());
597 for (unsigned i = 0; i < length; ++i) {
598 JSValue element = thisArray->tryGetIndexQuickly(i);
600 element = thisArray->get(exec, i);
601 RETURN_IF_EXCEPTION(scope, encodedJSValue());
603 joiner.append(*exec, element);
604 RETURN_IF_EXCEPTION(scope, encodedJSValue());
608 return JSValue::encode(joiner.join(*exec));
611 EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec)
614 auto scope = DECLARE_THROW_SCOPE(vm);
615 JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
617 JSObject* thisObject = thisValue.toObject(exec);
618 RETURN_IF_EXCEPTION(scope, encodedJSValue());
619 unsigned length = toLength(exec, thisObject);
620 RETURN_IF_EXCEPTION(scope, encodedJSValue());
622 StringRecursionChecker checker(exec, thisObject);
623 EXCEPTION_ASSERT(!scope.exception() || checker.earlyReturnValue());
624 if (JSValue earlyReturnValue = checker.earlyReturnValue())
625 return JSValue::encode(earlyReturnValue);
627 JSStringJoiner stringJoiner(*exec, ',', length);
628 RETURN_IF_EXCEPTION(scope, encodedJSValue());
631 ArgList arguments(exec);
633 for (unsigned i = 0; i < length; ++i) {
634 JSValue element = thisObject->getIndex(exec, i);
635 RETURN_IF_EXCEPTION(scope, encodedJSValue());
636 if (element.isUndefinedOrNull())
637 element = jsEmptyString(exec);
639 JSValue conversionFunction = element.get(exec, vm.propertyNames->toLocaleString);
640 RETURN_IF_EXCEPTION(scope, encodedJSValue());
642 CallType callType = getCallData(vm, conversionFunction, callData);
643 if (callType != CallType::None) {
645 element = call(exec, conversionFunction, callType, callData, element, arguments);
647 element = call(exec, conversionFunction, callType, callData, element, *vm.emptyList);
649 RETURN_IF_EXCEPTION(scope, encodedJSValue());
652 stringJoiner.append(*exec, element);
653 RETURN_IF_EXCEPTION(scope, encodedJSValue());
657 return JSValue::encode(stringJoiner.join(*exec));
660 static JSValue slowJoin(ExecState& exec, JSObject* thisObject, JSString* separator, uint64_t length)
663 auto scope = DECLARE_THROW_SCOPE(vm);
665 // 5. If len is zero, return the empty String.
667 return jsEmptyString(&exec);
669 // 6. Let element0 be Get(O, "0").
670 JSValue element0 = thisObject->getIndex(&exec, 0);
671 RETURN_IF_EXCEPTION(scope, { });
673 // 7. If element0 is undefined or null, let R be the empty String; otherwise, let R be ? ToString(element0).
674 JSString* r = nullptr;
675 if (element0.isUndefinedOrNull())
676 r = jsEmptyString(&exec);
678 r = element0.toString(&exec);
679 RETURN_IF_EXCEPTION(scope, { });
682 // 9. Repeat, while k < len
683 // 9.e Increase k by 1..
684 for (uint64_t k = 1; k < length; ++k) {
685 // b. Let element be ? Get(O, ! ToString(k)).
686 JSValue element = thisObject->get(&exec, Identifier::fromString(&exec, AtomicString::number(k)));
687 RETURN_IF_EXCEPTION(scope, { });
689 // c. If element is undefined or null, let next be the empty String; otherwise, let next be ? ToString(element).
690 JSString* next = nullptr;
691 if (element.isUndefinedOrNull()) {
692 if (!separator->length())
694 next = jsEmptyString(&exec);
696 next = element.toString(&exec);
697 RETURN_IF_EXCEPTION(scope, { });
699 // a. Let S be the String value produced by concatenating R and sep.
700 // d. Let R be a String value produced by concatenating S and next.
701 r = jsString(&exec, r, separator, next);
702 RETURN_IF_EXCEPTION(scope, { });
708 EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec)
711 auto scope = DECLARE_THROW_SCOPE(vm);
713 // 1. Let O be ? ToObject(this value).
714 JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
715 EXCEPTION_ASSERT(!!scope.exception() == !thisObject);
716 if (UNLIKELY(!thisObject))
717 return encodedJSValue();
719 StringRecursionChecker checker(exec, thisObject);
720 EXCEPTION_ASSERT(!scope.exception() || checker.earlyReturnValue());
721 if (JSValue earlyReturnValue = checker.earlyReturnValue())
722 return JSValue::encode(earlyReturnValue);
724 // 2. Let len be ? ToLength(? Get(O, "length")).
725 double length = toLength(exec, thisObject);
726 RETURN_IF_EXCEPTION(scope, encodedJSValue());
728 // 3. If separator is undefined, let separator be the single-element String ",".
729 JSValue separatorValue = exec->argument(0);
730 if (separatorValue.isUndefined()) {
731 const LChar comma = ',';
733 if (UNLIKELY(length > std::numeric_limits<unsigned>::max() || !canUseFastJoin(thisObject))) {
734 uint64_t length64 = static_cast<uint64_t>(length);
735 ASSERT(static_cast<double>(length64) == length);
736 JSString* jsSeparator = jsSingleCharacterString(exec, comma);
737 RETURN_IF_EXCEPTION(scope, encodedJSValue());
740 return JSValue::encode(slowJoin(*exec, thisObject, jsSeparator, length64));
743 unsigned unsignedLength = static_cast<unsigned>(length);
744 ASSERT(static_cast<double>(unsignedLength) == length);
747 return JSValue::encode(fastJoin(*exec, thisObject, { &comma, 1 }, unsignedLength));
750 // 4. Let sep be ? ToString(separator).
751 JSString* jsSeparator = separatorValue.toString(exec);
752 RETURN_IF_EXCEPTION(scope, encodedJSValue());
754 if (UNLIKELY(length > std::numeric_limits<unsigned>::max() || !canUseFastJoin(thisObject))) {
755 uint64_t length64 = static_cast<uint64_t>(length);
756 ASSERT(static_cast<double>(length64) == length);
759 return JSValue::encode(slowJoin(*exec, thisObject, jsSeparator, length64));
762 auto viewWithString = jsSeparator->viewWithUnderlyingString(exec);
763 RETURN_IF_EXCEPTION(scope, encodedJSValue());
766 return JSValue::encode(fastJoin(*exec, thisObject, viewWithString.view, length));
769 EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState* exec)
772 auto scope = DECLARE_THROW_SCOPE(vm);
774 JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
776 if (isJSArray(thisValue)) {
778 return JSValue::encode(asArray(thisValue)->pop(exec));
781 JSObject* thisObj = thisValue.toObject(exec);
782 EXCEPTION_ASSERT(!!scope.exception() == !thisObj);
783 if (UNLIKELY(!thisObj))
784 return encodedJSValue();
785 unsigned length = toLength(exec, thisObj);
786 RETURN_IF_EXCEPTION(scope, encodedJSValue());
790 putLength(exec, vm, thisObj, jsNumber(length));
791 return JSValue::encode(jsUndefined());
794 JSValue result = thisObj->get(exec, length - 1);
795 RETURN_IF_EXCEPTION(scope, encodedJSValue());
796 bool success = thisObj->methodTable(vm)->deletePropertyByIndex(thisObj, exec, length - 1);
797 RETURN_IF_EXCEPTION(scope, encodedJSValue());
798 if (UNLIKELY(!success)) {
799 throwTypeError(exec, scope, UnableToDeletePropertyError);
800 return encodedJSValue();
803 putLength(exec, vm, thisObj, jsNumber(length - 1));
804 return JSValue::encode(result);
807 EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState* exec)
810 auto scope = DECLARE_THROW_SCOPE(vm);
811 JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
813 if (LIKELY(isJSArray(thisValue) && exec->argumentCount() == 1)) {
814 JSArray* array = asArray(thisValue);
816 array->pushInline(exec, exec->uncheckedArgument(0));
817 return JSValue::encode(jsNumber(array->length()));
820 JSObject* thisObj = thisValue.toObject(exec);
821 EXCEPTION_ASSERT(!!scope.exception() == !thisObj);
822 if (UNLIKELY(!thisObj))
823 return encodedJSValue();
824 unsigned length = toLength(exec, thisObj);
825 RETURN_IF_EXCEPTION(scope, encodedJSValue());
827 for (unsigned n = 0; n < exec->argumentCount(); n++) {
828 // Check for integer overflow; where safe we can do a fast put by index.
829 if (length + n >= length)
830 thisObj->methodTable(vm)->putByIndex(thisObj, exec, length + n, exec->uncheckedArgument(n), true);
832 PutPropertySlot slot(thisObj);
833 Identifier propertyName = Identifier::fromString(exec, JSValue(static_cast<int64_t>(length) + static_cast<int64_t>(n)).toWTFString(exec));
834 thisObj->methodTable(vm)->put(thisObj, exec, propertyName, exec->uncheckedArgument(n), slot);
836 RETURN_IF_EXCEPTION(scope, encodedJSValue());
839 JSValue newLength(static_cast<int64_t>(length) + static_cast<int64_t>(exec->argumentCount()));
841 putLength(exec, vm, thisObj, newLength);
842 return JSValue::encode(newLength);
845 EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState* exec)
848 auto scope = DECLARE_THROW_SCOPE(vm);
850 JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
851 EXCEPTION_ASSERT(!!scope.exception() == !thisObject);
852 if (UNLIKELY(!thisObject))
853 return encodedJSValue();
855 unsigned length = toLength(exec, thisObject);
856 RETURN_IF_EXCEPTION(scope, encodedJSValue());
858 switch (thisObject->indexingType()) {
859 case ALL_CONTIGUOUS_INDEXING_TYPES:
860 case ALL_INT32_INDEXING_TYPES: {
861 auto& butterfly = *thisObject->butterfly();
862 if (length > butterfly.publicLength())
864 auto data = butterfly.contiguous().data();
865 if (containsHole(data, length) && holesMustForwardToPrototype(vm, thisObject))
867 std::reverse(data, data + length);
868 if (!hasInt32(thisObject->indexingType()))
869 vm.heap.writeBarrier(thisObject);
870 return JSValue::encode(thisObject);
872 case ALL_DOUBLE_INDEXING_TYPES: {
873 auto& butterfly = *thisObject->butterfly();
874 if (length > butterfly.publicLength())
876 auto data = butterfly.contiguousDouble().data();
877 if (containsHole(data, length) && holesMustForwardToPrototype(vm, thisObject))
879 std::reverse(data, data + length);
880 return JSValue::encode(thisObject);
882 case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
883 auto& storage = *thisObject->butterfly()->arrayStorage();
884 if (length > storage.vectorLength())
886 if (storage.hasHoles() && holesMustForwardToPrototype(vm, thisObject))
888 auto data = storage.vector().data();
889 std::reverse(data, data + length);
890 vm.heap.writeBarrier(thisObject);
891 return JSValue::encode(thisObject);
895 unsigned middle = length / 2;
896 for (unsigned lower = 0; lower < middle; lower++) {
897 unsigned upper = length - lower - 1;
898 bool lowerExists = thisObject->hasProperty(exec, lower);
899 RETURN_IF_EXCEPTION(scope, encodedJSValue());
902 lowerValue = thisObject->get(exec, lower);
903 RETURN_IF_EXCEPTION(scope, encodedJSValue());
906 bool upperExists = thisObject->hasProperty(exec, upper);
907 RETURN_IF_EXCEPTION(scope, encodedJSValue());
910 upperValue = thisObject->get(exec, upper);
911 RETURN_IF_EXCEPTION(scope, encodedJSValue());
915 thisObject->putByIndexInline(exec, lower, upperValue, true);
916 RETURN_IF_EXCEPTION(scope, encodedJSValue());
918 bool success = thisObject->methodTable(vm)->deletePropertyByIndex(thisObject, exec, lower);
919 RETURN_IF_EXCEPTION(scope, encodedJSValue());
920 if (UNLIKELY(!success)) {
921 throwTypeError(exec, scope, UnableToDeletePropertyError);
922 return encodedJSValue();
927 thisObject->putByIndexInline(exec, upper, lowerValue, true);
928 RETURN_IF_EXCEPTION(scope, encodedJSValue());
930 bool success = thisObject->methodTable(vm)->deletePropertyByIndex(thisObject, exec, upper);
931 RETURN_IF_EXCEPTION(scope, encodedJSValue());
932 if (UNLIKELY(!success)) {
933 throwTypeError(exec, scope, UnableToDeletePropertyError);
934 return encodedJSValue();
938 return JSValue::encode(thisObject);
941 EncodedJSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState* exec)
944 auto scope = DECLARE_THROW_SCOPE(vm);
945 JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
946 EXCEPTION_ASSERT(!!scope.exception() == !thisObj);
947 if (UNLIKELY(!thisObj))
948 return encodedJSValue();
949 unsigned length = toLength(exec, thisObj);
950 RETURN_IF_EXCEPTION(scope, encodedJSValue());
954 putLength(exec, vm, thisObj, jsNumber(length));
955 return JSValue::encode(jsUndefined());
958 JSValue result = thisObj->getIndex(exec, 0);
959 RETURN_IF_EXCEPTION(scope, encodedJSValue());
960 shift<JSArray::ShiftCountForShift>(exec, thisObj, 0, 1, 0, length);
961 RETURN_IF_EXCEPTION(scope, encodedJSValue());
963 putLength(exec, vm, thisObj, jsNumber(length - 1));
964 return JSValue::encode(result);
967 EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec)
969 // https://tc39.github.io/ecma262/#sec-array.prototype.slice
971 auto scope = DECLARE_THROW_SCOPE(vm);
972 JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
973 EXCEPTION_ASSERT(!!scope.exception() == !thisObj);
974 if (UNLIKELY(!thisObj))
976 unsigned length = toLength(exec, thisObj);
977 RETURN_IF_EXCEPTION(scope, { });
979 unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length);
980 RETURN_IF_EXCEPTION(scope, { });
981 unsigned end = argumentClampedIndexFromStartOrEnd(exec, 1, length, length);
982 RETURN_IF_EXCEPTION(scope, { });
986 std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, end - begin);
987 // We can only get an exception if we call some user function.
988 EXCEPTION_ASSERT(!!scope.exception() == (speciesResult.first == SpeciesConstructResult::Exception));
989 if (UNLIKELY(speciesResult.first == SpeciesConstructResult::Exception))
992 bool okToDoFastPath = speciesResult.first == SpeciesConstructResult::FastPath && isJSArray(thisObj) && length == toLength(exec, thisObj);
993 RETURN_IF_EXCEPTION(scope, { });
994 if (LIKELY(okToDoFastPath)) {
995 if (JSArray* result = asArray(thisObj)->fastSlice(*exec, begin, end - begin))
996 return JSValue::encode(result);
1000 if (speciesResult.first == SpeciesConstructResult::CreatedObject)
1001 result = speciesResult.second;
1003 result = constructEmptyArray(exec, nullptr, end - begin);
1004 RETURN_IF_EXCEPTION(scope, { });
1008 for (unsigned k = begin; k < end; k++, n++) {
1009 JSValue v = getProperty(exec, thisObj, k);
1010 RETURN_IF_EXCEPTION(scope, { });
1012 result->putDirectIndex(exec, n, v, 0, PutDirectIndexShouldThrow);
1013 RETURN_IF_EXCEPTION(scope, { });
1017 setLength(exec, vm, result, n);
1018 return JSValue::encode(result);
1021 EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec)
1025 VM& vm = exec->vm();
1026 auto scope = DECLARE_THROW_SCOPE(vm);
1028 JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
1029 EXCEPTION_ASSERT(!!scope.exception() == !thisObj);
1030 if (UNLIKELY(!thisObj))
1031 return encodedJSValue();
1032 unsigned length = toLength(exec, thisObj);
1033 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1035 if (!exec->argumentCount()) {
1036 std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, 0);
1037 EXCEPTION_ASSERT(!!scope.exception() == (speciesResult.first == SpeciesConstructResult::Exception));
1038 if (UNLIKELY(speciesResult.first == SpeciesConstructResult::Exception))
1039 return encodedJSValue();
1042 if (speciesResult.first == SpeciesConstructResult::CreatedObject)
1043 result = speciesResult.second;
1045 result = constructEmptyArray(exec, nullptr);
1046 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1049 setLength(exec, vm, result, 0);
1050 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1052 setLength(exec, vm, thisObj, length);
1053 return JSValue::encode(result);
1056 unsigned actualStart = argumentClampedIndexFromStartOrEnd(exec, 0, length);
1057 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1059 unsigned actualDeleteCount = length - actualStart;
1060 if (exec->argumentCount() > 1) {
1061 double deleteCount = exec->uncheckedArgument(1).toInteger(exec);
1062 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1063 if (deleteCount < 0)
1064 actualDeleteCount = 0;
1065 else if (deleteCount > length - actualStart)
1066 actualDeleteCount = length - actualStart;
1068 actualDeleteCount = static_cast<unsigned>(deleteCount);
1071 std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, actualDeleteCount);
1072 EXCEPTION_ASSERT(!!scope.exception() == (speciesResult.first == SpeciesConstructResult::Exception));
1073 if (speciesResult.first == SpeciesConstructResult::Exception)
1074 return JSValue::encode(jsUndefined());
1076 JSObject* result = nullptr;
1077 bool okToDoFastPath = speciesResult.first == SpeciesConstructResult::FastPath && isJSArray(thisObj) && length == toLength(exec, thisObj);
1078 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1079 if (LIKELY(okToDoFastPath))
1080 result = asArray(thisObj)->fastSlice(*exec, actualStart, actualDeleteCount);
1083 if (speciesResult.first == SpeciesConstructResult::CreatedObject)
1084 result = speciesResult.second;
1086 result = JSArray::tryCreate(vm, exec->lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), actualDeleteCount);
1087 if (UNLIKELY(!result)) {
1088 throwOutOfMemoryError(exec, scope);
1089 return encodedJSValue();
1092 for (unsigned k = 0; k < actualDeleteCount; ++k) {
1093 JSValue v = getProperty(exec, thisObj, k + actualStart);
1094 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1097 result->putDirectIndex(exec, k, v, 0, PutDirectIndexShouldThrow);
1098 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1102 unsigned itemCount = std::max<int>(exec->argumentCount() - 2, 0);
1103 if (itemCount < actualDeleteCount) {
1104 shift<JSArray::ShiftCountForSplice>(exec, thisObj, actualStart, actualDeleteCount, itemCount, length);
1105 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1106 } else if (itemCount > actualDeleteCount) {
1107 unshift<JSArray::ShiftCountForSplice>(exec, thisObj, actualStart, actualDeleteCount, itemCount, length);
1108 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1110 for (unsigned k = 0; k < itemCount; ++k) {
1111 thisObj->putByIndexInline(exec, k + actualStart, exec->uncheckedArgument(k + 2), true);
1112 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1116 setLength(exec, vm, thisObj, length - actualDeleteCount + itemCount);
1117 return JSValue::encode(result);
1120 EncodedJSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState* exec)
1122 VM& vm = exec->vm();
1123 auto scope = DECLARE_THROW_SCOPE(vm);
1126 JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
1127 EXCEPTION_ASSERT(!!scope.exception() == !thisObj);
1128 if (UNLIKELY(!thisObj))
1129 return encodedJSValue();
1130 double doubleLength = toLength(exec, thisObj);
1131 unsigned length = doubleLength;
1132 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1134 unsigned nrArgs = exec->argumentCount();
1136 if (UNLIKELY(doubleLength + static_cast<double>(nrArgs) > maxSafeInteger()))
1137 return throwVMTypeError(exec, scope, "Cannot shift to offset greater than (2 ** 53) - 1"_s);
1138 unshift<JSArray::ShiftCountForShift>(exec, thisObj, 0, 0, nrArgs, length);
1139 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1141 for (unsigned k = 0; k < nrArgs; ++k) {
1142 thisObj->putByIndexInline(exec, k, exec->uncheckedArgument(k), true);
1143 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1145 JSValue result = jsNumber(length + nrArgs);
1147 putLength(exec, vm, thisObj, result);
1148 return JSValue::encode(result);
1151 EncodedJSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState* exec)
1153 VM& vm = exec->vm();
1154 auto scope = DECLARE_THROW_SCOPE(vm);
1157 JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
1158 EXCEPTION_ASSERT(!!scope.exception() == !thisObj);
1159 if (UNLIKELY(!thisObj))
1160 return encodedJSValue();
1161 unsigned length = toLength(exec, thisObj);
1162 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1164 unsigned index = argumentClampedIndexFromStartOrEnd(exec, 1, length);
1165 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1166 JSValue searchElement = exec->argument(0);
1167 for (; index < length; ++index) {
1168 JSValue e = getProperty(exec, thisObj, index);
1169 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1172 bool isEqual = JSValue::strictEqual(exec, searchElement, e);
1173 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1175 return JSValue::encode(jsNumber(index));
1178 return JSValue::encode(jsNumber(-1));
1181 EncodedJSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState* exec)
1183 VM& vm = exec->vm();
1184 auto scope = DECLARE_THROW_SCOPE(vm);
1187 JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
1188 EXCEPTION_ASSERT(!!scope.exception() == !thisObj);
1189 if (UNLIKELY(!thisObj))
1190 return encodedJSValue();
1191 unsigned length = toLength(exec, thisObj);
1192 if (UNLIKELY(scope.exception()) || !length)
1193 return JSValue::encode(jsNumber(-1));
1195 unsigned index = length - 1;
1196 if (exec->argumentCount() >= 2) {
1197 JSValue fromValue = exec->uncheckedArgument(1);
1198 double fromDouble = fromValue.toInteger(exec);
1199 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1200 if (fromDouble < 0) {
1201 fromDouble += length;
1203 return JSValue::encode(jsNumber(-1));
1205 if (fromDouble < length)
1206 index = static_cast<unsigned>(fromDouble);
1209 JSValue searchElement = exec->argument(0);
1211 RELEASE_ASSERT(index < length);
1212 JSValue e = getProperty(exec, thisObj, index);
1213 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1216 if (JSValue::strictEqual(exec, searchElement, e))
1217 return JSValue::encode(jsNumber(index));
1218 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1221 return JSValue::encode(jsNumber(-1));
1224 static bool moveElements(ExecState* exec, VM& vm, JSArray* target, unsigned targetOffset, JSArray* source, unsigned sourceLength)
1226 auto scope = DECLARE_THROW_SCOPE(vm);
1228 if (LIKELY(!hasAnyArrayStorage(source->indexingType()) && !holesMustForwardToPrototype(vm, source))) {
1229 for (unsigned i = 0; i < sourceLength; ++i) {
1230 JSValue value = source->tryGetIndexQuickly(i);
1232 target->putDirectIndex(exec, targetOffset + i, value, 0, PutDirectIndexShouldThrow);
1233 RETURN_IF_EXCEPTION(scope, false);
1237 for (unsigned i = 0; i < sourceLength; ++i) {
1238 JSValue value = getProperty(exec, source, i);
1239 RETURN_IF_EXCEPTION(scope, false);
1241 target->putDirectIndex(exec, targetOffset + i, value, 0, PutDirectIndexShouldThrow);
1242 RETURN_IF_EXCEPTION(scope, false);
1249 static EncodedJSValue concatAppendOne(ExecState* exec, VM& vm, JSArray* first, JSValue second)
1251 auto scope = DECLARE_THROW_SCOPE(vm);
1253 ASSERT(!isJSArray(second));
1254 ASSERT(!shouldUseSlowPut(first->indexingType()));
1255 Butterfly* firstButterfly = first->butterfly();
1256 unsigned firstArraySize = firstButterfly->publicLength();
1258 Checked<unsigned, RecordOverflow> checkedResultSize = firstArraySize;
1259 checkedResultSize += 1;
1260 if (UNLIKELY(checkedResultSize.hasOverflowed())) {
1261 throwOutOfMemoryError(exec, scope);
1262 return encodedJSValue();
1265 unsigned resultSize = checkedResultSize.unsafeGet();
1266 IndexingType type = first->mergeIndexingTypeForCopying(indexingTypeForValue(second) | IsArray);
1268 if (type == NonArray)
1269 type = first->indexingType();
1271 Structure* resultStructure = exec->lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(type);
1272 JSArray* result = JSArray::create(vm, resultStructure, resultSize);
1273 if (UNLIKELY(!result)) {
1274 throwOutOfMemoryError(exec, scope);
1275 return encodedJSValue();
1278 bool success = result->appendMemcpy(exec, vm, 0, first);
1279 EXCEPTION_ASSERT(!scope.exception() || !success);
1281 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1283 bool success = moveElements(exec, vm, result, 0, first, firstArraySize);
1284 EXCEPTION_ASSERT(!scope.exception() == success);
1285 if (UNLIKELY(!success))
1286 return encodedJSValue();
1290 result->putDirectIndex(exec, firstArraySize, second);
1291 return JSValue::encode(result);
1296 EncodedJSValue JSC_HOST_CALL arrayProtoPrivateFuncConcatMemcpy(ExecState* exec)
1298 ASSERT(exec->argumentCount() == 2);
1299 VM& vm = exec->vm();
1300 auto scope = DECLARE_THROW_SCOPE(vm);
1302 JSArray* firstArray = jsCast<JSArray*>(exec->uncheckedArgument(0));
1304 // This code assumes that neither array has set Symbol.isConcatSpreadable. If the first array
1305 // has indexed accessors then one of those accessors might change the value of Symbol.isConcatSpreadable
1306 // on the second argument.
1307 if (UNLIKELY(shouldUseSlowPut(firstArray->indexingType())))
1308 return JSValue::encode(jsNull());
1310 // We need to check the species constructor here since checking it in the JS wrapper is too expensive for the non-optimizing tiers.
1311 bool isValid = speciesWatchpointIsValid(exec, firstArray);
1312 scope.assertNoException();
1313 if (UNLIKELY(!isValid))
1314 return JSValue::encode(jsNull());
1316 JSValue second = exec->uncheckedArgument(1);
1317 if (!isJSArray(second)) {
1319 return concatAppendOne(exec, vm, firstArray, second);
1322 JSArray* secondArray = jsCast<JSArray*>(second);
1324 Butterfly* firstButterfly = firstArray->butterfly();
1325 Butterfly* secondButterfly = secondArray->butterfly();
1327 unsigned firstArraySize = firstButterfly->publicLength();
1328 unsigned secondArraySize = secondButterfly->publicLength();
1330 Checked<unsigned, RecordOverflow> checkedResultSize = firstArraySize;
1331 checkedResultSize += secondArraySize;
1333 if (UNLIKELY(checkedResultSize.hasOverflowed())) {
1334 throwOutOfMemoryError(exec, scope);
1335 return encodedJSValue();
1338 unsigned resultSize = checkedResultSize.unsafeGet();
1339 IndexingType firstType = firstArray->indexingType();
1340 IndexingType secondType = secondArray->indexingType();
1341 IndexingType type = firstArray->mergeIndexingTypeForCopying(secondType);
1342 if (type == NonArray || !firstArray->canFastCopy(vm, secondArray) || resultSize >= MIN_SPARSE_ARRAY_INDEX) {
1343 JSArray* result = constructEmptyArray(exec, nullptr, resultSize);
1344 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1346 bool success = moveElements(exec, vm, result, 0, firstArray, firstArraySize);
1347 EXCEPTION_ASSERT(!scope.exception() == success);
1348 if (UNLIKELY(!success))
1349 return encodedJSValue();
1350 success = moveElements(exec, vm, result, firstArraySize, secondArray, secondArraySize);
1351 EXCEPTION_ASSERT(!scope.exception() == success);
1352 if (UNLIKELY(!success))
1353 return encodedJSValue();
1355 return JSValue::encode(result);
1358 JSGlobalObject* lexicalGlobalObject = exec->lexicalGlobalObject();
1359 Structure* resultStructure = lexicalGlobalObject->arrayStructureForIndexingTypeDuringAllocation(type);
1360 if (UNLIKELY(hasAnyArrayStorage(resultStructure->indexingType())))
1361 return JSValue::encode(jsNull());
1363 ASSERT(!lexicalGlobalObject->isHavingABadTime());
1364 ObjectInitializationScope initializationScope(vm);
1365 JSArray* result = JSArray::tryCreateUninitializedRestricted(initializationScope, resultStructure, resultSize);
1366 if (UNLIKELY(!result)) {
1367 throwOutOfMemoryError(exec, scope);
1368 return encodedJSValue();
1371 if (type == ArrayWithDouble) {
1372 double* buffer = result->butterfly()->contiguousDouble().data();
1373 memcpy(buffer, firstButterfly->contiguousDouble().data(), sizeof(JSValue) * firstArraySize);
1374 memcpy(buffer + firstArraySize, secondButterfly->contiguousDouble().data(), sizeof(JSValue) * secondArraySize);
1375 } else if (type != ArrayWithUndecided) {
1376 WriteBarrier<Unknown>* buffer = result->butterfly()->contiguous().data();
1378 auto copy = [&] (unsigned offset, void* source, unsigned size, IndexingType type) {
1379 if (type != ArrayWithUndecided) {
1380 memcpy(buffer + offset, source, sizeof(JSValue) * size);
1384 for (unsigned i = size; i--;)
1385 buffer[i + offset].clear();
1388 copy(0, firstButterfly->contiguous().data(), firstArraySize, firstType);
1389 copy(firstArraySize, secondButterfly->contiguous().data(), secondArraySize, secondType);
1392 result->butterfly()->setPublicLength(resultSize);
1393 return JSValue::encode(result);
1396 EncodedJSValue JSC_HOST_CALL arrayProtoPrivateFuncAppendMemcpy(ExecState* exec)
1398 ASSERT(exec->argumentCount() == 3);
1400 VM& vm = exec->vm();
1401 auto scope = DECLARE_THROW_SCOPE(vm);
1402 JSArray* resultArray = jsCast<JSArray*>(exec->uncheckedArgument(0));
1403 JSArray* otherArray = jsCast<JSArray*>(exec->uncheckedArgument(1));
1404 JSValue startValue = exec->uncheckedArgument(2);
1405 ASSERT(startValue.isAnyInt() && startValue.asAnyInt() >= 0 && startValue.asAnyInt() <= std::numeric_limits<unsigned>::max());
1406 unsigned startIndex = static_cast<unsigned>(startValue.asAnyInt());
1407 bool success = resultArray->appendMemcpy(exec, vm, startIndex, otherArray);
1408 EXCEPTION_ASSERT(!scope.exception() || !success);
1410 return JSValue::encode(jsUndefined());
1411 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1413 moveElements(exec, vm, resultArray, startIndex, otherArray, otherArray->length());
1414 return JSValue::encode(jsUndefined());
1418 // -------------------- ArrayPrototype.constructor Watchpoint ------------------
1420 namespace ArrayPrototypeInternal {
1421 static bool verbose = false;
1424 class ArrayPrototypeAdaptiveInferredPropertyWatchpoint : public AdaptiveInferredPropertyValueWatchpointBase {
1426 typedef AdaptiveInferredPropertyValueWatchpointBase Base;
1427 ArrayPrototypeAdaptiveInferredPropertyWatchpoint(const ObjectPropertyCondition&, ArrayPrototype*);
1430 void handleFire(VM&, const FireDetail&) override;
1432 ArrayPrototype* m_arrayPrototype;
1435 void ArrayPrototype::tryInitializeSpeciesWatchpoint(ExecState* exec)
1437 VM& vm = exec->vm();
1439 RELEASE_ASSERT(!m_constructorWatchpoint);
1440 RELEASE_ASSERT(!m_constructorSpeciesWatchpoint);
1442 auto scope = DECLARE_THROW_SCOPE(vm);
1444 if (ArrayPrototypeInternal::verbose)
1445 dataLog("Initializing Array species watchpoints for Array.prototype: ", pointerDump(this), " with structure: ", pointerDump(this->structure(vm)), "\nand Array: ", pointerDump(this->globalObject(vm)->arrayConstructor()), " with structure: ", pointerDump(this->globalObject(vm)->arrayConstructor()->structure(vm)), "\n");
1446 // First we need to make sure that the Array.prototype.constructor property points to Array
1447 // and that Array[Symbol.species] is the primordial GetterSetter.
1449 // We only initialize once so flattening the structures does not have any real cost.
1450 Structure* prototypeStructure = this->structure(vm);
1451 if (prototypeStructure->isDictionary())
1452 prototypeStructure = prototypeStructure->flattenDictionaryStructure(vm, this);
1453 RELEASE_ASSERT(!prototypeStructure->isDictionary());
1455 JSGlobalObject* globalObject = this->globalObject(vm);
1456 ArrayConstructor* arrayConstructor = globalObject->arrayConstructor();
1458 auto invalidateWatchpoint = [&] {
1459 globalObject->arraySpeciesWatchpoint().invalidate(vm, StringFireDetail("Was not able to set up array species watchpoint."));
1462 PropertySlot constructorSlot(this, PropertySlot::InternalMethodType::VMInquiry);
1463 this->getOwnPropertySlot(this, exec, vm.propertyNames->constructor, constructorSlot);
1464 scope.assertNoException();
1465 if (constructorSlot.slotBase() != this
1466 || !constructorSlot.isCacheableValue()
1467 || constructorSlot.getValue(exec, vm.propertyNames->constructor) != arrayConstructor) {
1468 invalidateWatchpoint();
1472 Structure* constructorStructure = arrayConstructor->structure(vm);
1473 if (constructorStructure->isDictionary())
1474 constructorStructure = constructorStructure->flattenDictionaryStructure(vm, arrayConstructor);
1476 PropertySlot speciesSlot(arrayConstructor, PropertySlot::InternalMethodType::VMInquiry);
1477 arrayConstructor->getOwnPropertySlot(arrayConstructor, exec, vm.propertyNames->speciesSymbol, speciesSlot);
1478 scope.assertNoException();
1479 if (speciesSlot.slotBase() != arrayConstructor
1480 || !speciesSlot.isCacheableGetter()
1481 || speciesSlot.getterSetter() != globalObject->speciesGetterSetter()) {
1482 invalidateWatchpoint();
1486 // Now we need to setup the watchpoints to make sure these conditions remain valid.
1487 prototypeStructure->startWatchingPropertyForReplacements(vm, constructorSlot.cachedOffset());
1488 constructorStructure->startWatchingPropertyForReplacements(vm, speciesSlot.cachedOffset());
1490 ObjectPropertyCondition constructorCondition = ObjectPropertyCondition::equivalence(vm, this, this, vm.propertyNames->constructor.impl(), arrayConstructor);
1491 ObjectPropertyCondition speciesCondition = ObjectPropertyCondition::equivalence(vm, this, arrayConstructor, vm.propertyNames->speciesSymbol.impl(), globalObject->speciesGetterSetter());
1493 if (!constructorCondition.isWatchable() || !speciesCondition.isWatchable()) {
1494 invalidateWatchpoint();
1498 m_constructorWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(constructorCondition, this);
1499 m_constructorWatchpoint->install(vm);
1501 m_constructorSpeciesWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(speciesCondition, this);
1502 m_constructorSpeciesWatchpoint->install(vm);
1504 // We only watch this from the DFG, and the DFG makes sure to only start watching if the watchpoint is in the IsWatched state.
1505 RELEASE_ASSERT(!globalObject->arraySpeciesWatchpoint().isBeingWatched());
1506 globalObject->arraySpeciesWatchpoint().touch(vm, "Set up array species watchpoint.");
1509 ArrayPrototypeAdaptiveInferredPropertyWatchpoint::ArrayPrototypeAdaptiveInferredPropertyWatchpoint(const ObjectPropertyCondition& key, ArrayPrototype* prototype)
1511 , m_arrayPrototype(prototype)
1515 void ArrayPrototypeAdaptiveInferredPropertyWatchpoint::handleFire(VM& vm, const FireDetail& detail)
1517 auto lazyDetail = createLazyFireDetail("ArrayPrototype adaption of ", key(), " failed: ", detail);
1519 if (ArrayPrototypeInternal::verbose)
1520 WTF::dataLog(lazyDetail, "\n");
1522 JSGlobalObject* globalObject = m_arrayPrototype->globalObject(vm);
1523 globalObject->arraySpeciesWatchpoint().fireAll(vm, lazyDetail);