ArrayPrototype methods should use JSValue::toLength for non-Arrays.
[WebKit-https.git] / Source / JavaScriptCore / runtime / ArrayPrototype.cpp
1 /*
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)
6  *
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.
11  *
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.
16  *
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
20  *  USA
21  *
22  */
23
24 #include "config.h"
25 #include "ArrayPrototype.h"
26
27 #include "AdaptiveInferredPropertyValueWatchpointBase.h"
28 #include "ArrayConstructor.h"
29 #include "BuiltinNames.h"
30 #include "ButterflyInlines.h"
31 #include "CodeBlock.h"
32 #include "Error.h"
33 #include "GetterSetter.h"
34 #include "Interpreter.h"
35 #include "JIT.h"
36 #include "JSArrayInlines.h"
37 #include "JSCBuiltins.h"
38 #include "JSCInlines.h"
39 #include "JSStringBuilder.h"
40 #include "JSStringJoiner.h"
41 #include "Lookup.h"
42 #include "ObjectConstructor.h"
43 #include "ObjectPrototype.h"
44 #include "Operations.h"
45 #include "StringRecursionChecker.h"
46 #include <algorithm>
47 #include <wtf/Assertions.h>
48
49 namespace JSC {
50
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*);
62
63 // ------------------------------ ArrayPrototype ----------------------------
64
65 const ClassInfo ArrayPrototype::s_info = {"Array", &JSArray::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(ArrayPrototype)};
66
67 ArrayPrototype* ArrayPrototype::create(VM& vm, JSGlobalObject* globalObject, Structure* structure)
68 {
69     ArrayPrototype* prototype = new (NotNull, allocateCell<ArrayPrototype>(vm.heap)) ArrayPrototype(vm, structure);
70     prototype->finishCreation(vm, globalObject);
71     vm.heap.addFinalizer(prototype, destroy);
72     return prototype;
73 }
74
75 // ECMA 15.4.4
76 ArrayPrototype::ArrayPrototype(VM& vm, Structure* structure)
77     : JSArray(vm, structure, 0)
78 {
79 }
80
81 void ArrayPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
82 {
83     Base::finishCreation(vm);
84     ASSERT(inherits(vm, info()));
85     vm.prototypeMap.addPrototype(this);
86
87     putDirectWithoutTransition(vm, vm.propertyNames->toString, globalObject->arrayProtoToStringFunction(), DontEnum);
88     putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().valuesPublicName(), globalObject->arrayProtoValuesFunction(), DontEnum);
89     putDirectWithoutTransition(vm, vm.propertyNames->iteratorSymbol, globalObject->arrayProtoValuesFunction(), DontEnum);
90
91     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->toLocaleString, arrayProtoFuncToLocaleString, DontEnum, 0);
92     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("concat", arrayPrototypeConcatCodeGenerator, DontEnum);
93     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("fill", arrayPrototypeFillCodeGenerator, DontEnum);
94     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->join, arrayProtoFuncJoin, DontEnum, 1);
95     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("pop", arrayProtoFuncPop, DontEnum, 0, ArrayPopIntrinsic);
96     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().pushPublicName(), arrayProtoFuncPush, DontEnum, 1, ArrayPushIntrinsic);
97     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().pushPrivateName(), arrayProtoFuncPush, DontEnum | DontDelete | ReadOnly, 1, ArrayPushIntrinsic);
98     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("reverse", arrayProtoFuncReverse, DontEnum, 0);
99     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().shiftPublicName(), arrayProtoFuncShift, DontEnum, 0);
100     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().shiftPrivateName(), arrayProtoFuncShift, DontEnum | DontDelete | ReadOnly, 0);
101     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->slice, arrayProtoFuncSlice, DontEnum, 2, ArraySliceIntrinsic);
102     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("sort", arrayPrototypeSortCodeGenerator, DontEnum);
103     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("splice", arrayProtoFuncSplice, DontEnum, 2);
104     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("unshift", arrayProtoFuncUnShift, DontEnum, 1);
105     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("every", arrayPrototypeEveryCodeGenerator, DontEnum);
106     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("forEach", arrayPrototypeForEachCodeGenerator, DontEnum);
107     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("some", arrayPrototypeSomeCodeGenerator, DontEnum);
108     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("indexOf", arrayProtoFuncIndexOf, DontEnum, 1, ArrayIndexOfIntrinsic);
109     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("lastIndexOf", arrayProtoFuncLastIndexOf, DontEnum, 1);
110     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("filter", arrayPrototypeFilterCodeGenerator, DontEnum);
111     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("reduce", arrayPrototypeReduceCodeGenerator, DontEnum);
112     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("reduceRight", arrayPrototypeReduceRightCodeGenerator, DontEnum);
113     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("map", arrayPrototypeMapCodeGenerator, DontEnum);
114     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().entriesPublicName(), arrayPrototypeEntriesCodeGenerator, DontEnum);
115     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().keysPublicName(), arrayPrototypeKeysCodeGenerator, DontEnum);
116     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("find", arrayPrototypeFindCodeGenerator, DontEnum);
117     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("findIndex", arrayPrototypeFindIndexCodeGenerator, DontEnum);
118     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("includes", arrayPrototypeIncludesCodeGenerator, DontEnum);
119     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("copyWithin", arrayPrototypeCopyWithinCodeGenerator, DontEnum);
120
121     putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().entriesPrivateName(), getDirect(vm, vm.propertyNames->builtinNames().entriesPublicName()), ReadOnly);
122     putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().forEachPrivateName(), getDirect(vm, vm.propertyNames->builtinNames().forEachPublicName()), ReadOnly);
123     putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().keysPrivateName(), getDirect(vm, vm.propertyNames->builtinNames().keysPublicName()), ReadOnly);
124     putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().valuesPrivateName(), globalObject->arrayProtoValuesFunction(), ReadOnly);
125
126     JSObject* unscopables = constructEmptyObject(globalObject->globalExec(), globalObject->nullPrototypeObjectStructure());
127     const char* unscopableNames[] = {
128         "copyWithin",
129         "entries",
130         "fill",
131         "find",
132         "findIndex",
133         "includes",
134         "keys",
135         "values"
136     };
137     for (const char* unscopableName : unscopableNames)
138         unscopables->putDirect(vm, Identifier::fromString(&vm, unscopableName), jsBoolean(true));
139     putDirectWithoutTransition(vm, vm.propertyNames->unscopablesSymbol, unscopables, DontEnum | ReadOnly);
140 }
141
142 void ArrayPrototype::destroy(JSC::JSCell* cell)
143 {
144     ArrayPrototype* thisObject = static_cast<ArrayPrototype*>(cell);
145     thisObject->ArrayPrototype::~ArrayPrototype();
146 }
147
148 // ------------------------------ Array Functions ----------------------------
149
150 static ALWAYS_INLINE JSValue getProperty(ExecState* exec, JSObject* object, unsigned index)
151 {
152     VM& vm = exec->vm();
153     auto scope = DECLARE_THROW_SCOPE(vm);
154     
155     if (JSValue result = object->tryGetIndexQuickly(index))
156         return result;
157     // We want to perform get and has in the same operation.
158     // We can only do so when this behavior is not observable. The
159     // only time it is observable is when we encounter an opaque objects (ProxyObject and JSModuleNamespaceObject)
160     // somewhere in the prototype chain.
161     PropertySlot slot(object, PropertySlot::InternalMethodType::HasProperty);
162     bool hasProperty = object->getPropertySlot(exec, index, slot);
163     ASSERT(!scope.exception() || !hasProperty);
164     if (!hasProperty)
165         return { };
166     if (UNLIKELY(slot.isTaintedByOpaqueObject())) {
167         scope.release();
168         return object->get(exec, index);
169     }
170     scope.release();
171     return slot.getValue(exec, index);
172 }
173
174 static ALWAYS_INLINE bool putLength(ExecState* exec, VM& vm, JSObject* obj, JSValue value)
175 {
176     PutPropertySlot slot(obj);
177     return obj->methodTable(vm)->put(obj, exec, vm.propertyNames->length, value, slot);
178 }
179
180 static ALWAYS_INLINE void setLength(ExecState* exec, VM& vm, JSObject* obj, unsigned value)
181 {
182     auto scope = DECLARE_THROW_SCOPE(vm);
183     static const bool throwException = true;
184     if (isJSArray(obj)) {
185         jsCast<JSArray*>(obj)->setLength(exec, value, throwException);
186         RETURN_IF_EXCEPTION(scope, void());
187     }
188     bool success = putLength(exec, vm, obj, jsNumber(value));
189     RETURN_IF_EXCEPTION(scope, void());
190     if (UNLIKELY(!success))
191         throwTypeError(exec, scope, ASCIILiteral(ReadonlyPropertyWriteError));
192 }
193
194 ALWAYS_INLINE bool speciesWatchpointIsValid(ExecState* exec, JSObject* thisObject)
195 {
196     JSGlobalObject* globalObject = thisObject->globalObject();
197     ArrayPrototype* arrayPrototype = globalObject->arrayPrototype();
198
199     if (globalObject->arraySpeciesWatchpoint().stateOnJSThread() == ClearWatchpoint) {
200         arrayPrototype->tryInitializeSpeciesWatchpoint(exec);
201         ASSERT(globalObject->arraySpeciesWatchpoint().stateOnJSThread() != ClearWatchpoint);
202     }
203
204     return !thisObject->hasCustomProperties()
205         && arrayPrototype == thisObject->getPrototypeDirect()
206         && globalObject->arraySpeciesWatchpoint().stateOnJSThread() == IsWatched;
207 }
208
209 enum class SpeciesConstructResult {
210     FastPath,
211     Exception,
212     CreatedObject
213 };
214
215 static ALWAYS_INLINE std::pair<SpeciesConstructResult, JSObject*> speciesConstructArray(ExecState* exec, JSObject* thisObject, unsigned length)
216 {
217     VM& vm = exec->vm();
218     auto scope = DECLARE_THROW_SCOPE(vm);
219
220     auto exceptionResult = [] () {
221         return std::make_pair(SpeciesConstructResult::Exception, nullptr);
222     };
223
224     // ECMA 9.4.2.3: https://tc39.github.io/ecma262/#sec-arrayspeciescreate
225     JSValue constructor = jsUndefined();
226     bool thisIsArray = isArray(exec, thisObject);
227     RETURN_IF_EXCEPTION(scope, exceptionResult());
228     if (LIKELY(thisIsArray)) {
229         // Fast path in the normal case where the user has not set an own constructor and the Array.prototype.constructor is normal.
230         // We need prototype check for subclasses of Array, which are Array objects but have a different prototype by default.
231         bool isValid = speciesWatchpointIsValid(exec, thisObject);
232         if (LIKELY(isValid))
233             return std::make_pair(SpeciesConstructResult::FastPath, nullptr);
234
235         constructor = thisObject->get(exec, vm.propertyNames->constructor);
236         RETURN_IF_EXCEPTION(scope, exceptionResult());
237         if (constructor.isConstructor()) {
238             JSObject* constructorObject = jsCast<JSObject*>(constructor);
239             if (exec->lexicalGlobalObject() != constructorObject->globalObject())
240                 return std::make_pair(SpeciesConstructResult::FastPath, nullptr);;
241         }
242         if (constructor.isObject()) {
243             constructor = constructor.get(exec, vm.propertyNames->speciesSymbol);
244             RETURN_IF_EXCEPTION(scope, exceptionResult());
245             if (constructor.isNull())
246                 return std::make_pair(SpeciesConstructResult::FastPath, nullptr);;
247         }
248     } else
249         RETURN_IF_EXCEPTION(scope, exceptionResult());
250
251     if (constructor.isUndefined())
252         return std::make_pair(SpeciesConstructResult::FastPath, nullptr);
253
254     MarkedArgumentBuffer args;
255     args.append(jsNumber(length));
256     JSObject* newObject = construct(exec, constructor, args, "Species construction did not get a valid constructor");
257     RETURN_IF_EXCEPTION(scope, exceptionResult());
258     return std::make_pair(SpeciesConstructResult::CreatedObject, newObject);
259 }
260
261 static inline unsigned argumentClampedIndexFromStartOrEnd(ExecState* exec, int argument, unsigned length, unsigned undefinedValue = 0)
262 {
263     JSValue value = exec->argument(argument);
264     if (value.isUndefined())
265         return undefinedValue;
266
267     double indexDouble = value.toInteger(exec);
268     if (indexDouble < 0) {
269         indexDouble += length;
270         return indexDouble < 0 ? 0 : static_cast<unsigned>(indexDouble);
271     }
272     return indexDouble > length ? length : static_cast<unsigned>(indexDouble);
273 }
274
275 // The shift/unshift function implement the shift/unshift behaviour required
276 // by the corresponding array prototype methods, and by splice. In both cases,
277 // the methods are operating an an array or array like object.
278 //
279 //  header  currentCount  (remainder)
280 // [------][------------][-----------]
281 //  header  resultCount  (remainder)
282 // [------][-----------][-----------]
283 //
284 // The set of properties in the range 'header' must be unchanged. The set of
285 // properties in the range 'remainder' (where remainder = length - header -
286 // currentCount) will be shifted to the left or right as appropriate; in the
287 // case of shift this must be removing values, in the case of unshift this
288 // must be introducing new values.
289
290 template<JSArray::ShiftCountMode shiftCountMode>
291 void shift(ExecState* exec, JSObject* thisObj, unsigned header, unsigned currentCount, unsigned resultCount, unsigned length)
292 {
293     VM& vm = exec->vm();
294     auto scope = DECLARE_THROW_SCOPE(vm);
295
296     RELEASE_ASSERT(currentCount > resultCount);
297     unsigned count = currentCount - resultCount;
298
299     RELEASE_ASSERT(header <= length);
300     RELEASE_ASSERT(currentCount <= (length - header));
301
302     if (isJSArray(thisObj)) {
303         JSArray* array = asArray(thisObj);
304         if (array->length() == length && array->shiftCount<shiftCountMode>(exec, header, count))
305             return;
306     }
307
308     for (unsigned k = header; k < length - currentCount; ++k) {
309         unsigned from = k + currentCount;
310         unsigned to = k + resultCount;
311         JSValue value = getProperty(exec, thisObj, from);
312         RETURN_IF_EXCEPTION(scope, void());
313         if (value) {
314             thisObj->putByIndexInline(exec, to, value, true);
315             RETURN_IF_EXCEPTION(scope, void());
316         } else {
317             bool success = thisObj->methodTable(vm)->deletePropertyByIndex(thisObj, exec, to);
318             RETURN_IF_EXCEPTION(scope, void());
319             if (!success) {
320                 throwTypeError(exec, scope, ASCIILiteral(UnableToDeletePropertyError));
321                 return;
322             }
323         }
324     }
325     for (unsigned k = length; k > length - count; --k) {
326         bool success = thisObj->methodTable(vm)->deletePropertyByIndex(thisObj, exec, k - 1);
327         RETURN_IF_EXCEPTION(scope, void());
328         if (!success) {
329             throwTypeError(exec, scope, ASCIILiteral(UnableToDeletePropertyError));
330             return;
331         }
332     }
333 }
334
335 template<JSArray::ShiftCountMode shiftCountMode>
336 void unshift(ExecState* exec, JSObject* thisObj, unsigned header, unsigned currentCount, unsigned resultCount, unsigned length)
337 {
338     VM& vm = exec->vm();
339     auto scope = DECLARE_THROW_SCOPE(vm);
340
341     RELEASE_ASSERT(resultCount > currentCount);
342     unsigned count = resultCount - currentCount;
343
344     RELEASE_ASSERT(header <= length);
345     RELEASE_ASSERT(currentCount <= (length - header));
346
347     // Guard against overflow.
348     if (count > (UINT_MAX - length)) {
349         throwOutOfMemoryError(exec, scope);
350         return;
351     }
352
353     if (isJSArray(thisObj)) {
354         JSArray* array = asArray(thisObj);
355         if (array->length() == length) {
356             bool handled = array->unshiftCount<shiftCountMode>(exec, header, count);
357             ASSERT(!scope.exception() || handled);
358             if (handled)
359                 return;
360         }
361     }
362
363     for (unsigned k = length - currentCount; k > header; --k) {
364         unsigned from = k + currentCount - 1;
365         unsigned to = k + resultCount - 1;
366         JSValue value = getProperty(exec, thisObj, from);
367         RETURN_IF_EXCEPTION(scope, void());
368         if (value) {
369             thisObj->putByIndexInline(exec, to, value, true);
370             RETURN_IF_EXCEPTION(scope, void());
371         } else {
372             bool success = thisObj->methodTable(vm)->deletePropertyByIndex(thisObj, exec, to);
373             RETURN_IF_EXCEPTION(scope, void());
374             if (UNLIKELY(!success)) {
375                 throwTypeError(exec, scope, ASCIILiteral(UnableToDeletePropertyError));
376                 return;
377             }
378         }
379     }
380 }
381
382 EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec)
383 {
384     VM& vm = exec->vm();
385     auto scope = DECLARE_THROW_SCOPE(vm);
386     JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
387
388     // 1. Let array be the result of calling ToObject on the this value.
389     JSObject* thisObject = thisValue.toObject(exec);
390     RETURN_IF_EXCEPTION(scope, encodedJSValue());
391     
392     // 2. Let func be the result of calling the [[Get]] internal method of array with argument "join".
393     JSValue function = JSValue(thisObject).get(exec, vm.propertyNames->join);
394     RETURN_IF_EXCEPTION(scope, encodedJSValue());
395
396     // 3. If IsCallable(func) is false, then let func be the standard built-in method Object.prototype.toString (15.2.4.2).
397     bool customJoinCase = false;
398     if (!function.isCell())
399         customJoinCase = true;
400     CallData callData;
401     CallType callType = getCallData(function, callData);
402     if (callType == CallType::None)
403         customJoinCase = true;
404
405     if (UNLIKELY(customJoinCase)) {
406         scope.release();
407         return JSValue::encode(jsMakeNontrivialString(exec, "[object ", thisObject->methodTable(vm)->className(thisObject), "]"));
408     }
409
410     // 4. Return the result of calling the [[Call]] internal method of func providing array as the this value and an empty arguments list.
411     if (!isJSArray(thisObject) || callType != CallType::Host || callData.native.function != arrayProtoFuncJoin) {
412         scope.release();
413         return JSValue::encode(call(exec, function, callType, callData, thisObject, exec->emptyList()));
414     }
415
416     ASSERT(isJSArray(thisValue));
417     JSArray* thisArray = asArray(thisValue);
418
419     unsigned length = thisArray->length();
420
421     StringRecursionChecker checker(exec, thisArray);
422     ASSERT(!scope.exception() || checker.earlyReturnValue());
423     if (JSValue earlyReturnValue = checker.earlyReturnValue())
424         return JSValue::encode(earlyReturnValue);
425
426     JSStringJoiner joiner(*exec, ',', length);
427     RETURN_IF_EXCEPTION(scope, encodedJSValue());
428
429     for (unsigned i = 0; i < length; ++i) {
430         JSValue element = thisArray->tryGetIndexQuickly(i);
431         if (!element) {
432             element = thisArray->get(exec, i);
433             RETURN_IF_EXCEPTION(scope, encodedJSValue());
434         }
435         joiner.append(*exec, element);
436         RETURN_IF_EXCEPTION(scope, encodedJSValue());
437     }
438
439     scope.release();
440     return JSValue::encode(joiner.join(*exec));
441 }
442
443 EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec)
444 {
445     VM& vm = exec->vm();
446     auto scope = DECLARE_THROW_SCOPE(vm);
447     JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
448
449     JSObject* thisObject = thisValue.toObject(exec);
450     RETURN_IF_EXCEPTION(scope, encodedJSValue());
451     unsigned length = toLength(exec, thisObject);
452     RETURN_IF_EXCEPTION(scope, encodedJSValue());
453
454     StringRecursionChecker checker(exec, thisObject);
455     ASSERT(!scope.exception() || checker.earlyReturnValue());
456     if (JSValue earlyReturnValue = checker.earlyReturnValue())
457         return JSValue::encode(earlyReturnValue);
458
459     JSStringJoiner stringJoiner(*exec, ',', length);
460     RETURN_IF_EXCEPTION(scope, encodedJSValue());
461
462 #if ENABLE(INTL)
463     ArgList arguments(exec);
464     for (unsigned i = 0; i < length; ++i) {
465         JSValue element = thisObject->getIndex(exec, i);
466         RETURN_IF_EXCEPTION(scope, encodedJSValue());
467         if (element.isUndefinedOrNull())
468             element = jsEmptyString(exec);
469         else {
470             JSValue conversionFunction = element.get(exec, vm.propertyNames->toLocaleString);
471             RETURN_IF_EXCEPTION(scope, encodedJSValue());
472             CallData callData;
473             CallType callType = getCallData(conversionFunction, callData);
474             if (callType != CallType::None) {
475                 element = call(exec, conversionFunction, callType, callData, element, arguments);
476                 RETURN_IF_EXCEPTION(scope, encodedJSValue());
477             }
478         }
479         stringJoiner.append(*exec, element);
480         RETURN_IF_EXCEPTION(scope, encodedJSValue());
481     }
482 #else // !ENABLE(INTL)
483     for (unsigned i = 0; i < length; ++i) {
484         JSValue element = thisObject->getIndex(exec, i);
485         RETURN_IF_EXCEPTION(scope, encodedJSValue());
486         if (element.isUndefinedOrNull())
487             continue;
488         JSValue conversionFunction = element.get(exec, vm.propertyNames->toLocaleString);
489         RETURN_IF_EXCEPTION(scope, encodedJSValue());
490         CallData callData;
491         CallType callType = getCallData(conversionFunction, callData);
492         if (callType != CallType::None) {
493             element = call(exec, conversionFunction, callType, callData, element, exec->emptyList());
494             RETURN_IF_EXCEPTION(scope, encodedJSValue());
495         }
496         stringJoiner.append(*exec, element);
497         RETURN_IF_EXCEPTION(scope, encodedJSValue());
498     }
499 #endif // !ENABLE(INTL)
500
501     scope.release();
502     return JSValue::encode(stringJoiner.join(*exec));
503 }
504
505 static inline bool isHole(double value)
506 {
507     return std::isnan(value);
508 }
509
510 static inline bool isHole(const WriteBarrier<Unknown>& value)
511 {
512     return !value;
513 }
514
515 template<typename T> static inline bool containsHole(T* data, unsigned length)
516 {
517     for (unsigned i = 0; i < length; ++i) {
518         if (isHole(data[i]))
519             return true;
520     }
521     return false;
522 }
523
524 static inline bool holesMustForwardToPrototype(ExecState& state, JSObject* object)
525 {
526     auto& vm = state.vm();
527     return object->structure(vm)->holesMustForwardToPrototype(vm);
528 }
529
530 static JSValue slowJoin(ExecState& exec, JSObject* thisObject, JSString* separator, uint64_t length)
531 {
532     VM& vm = exec.vm();
533     auto scope = DECLARE_THROW_SCOPE(vm);
534
535     // 5. If len is zero, return the empty String.
536     if (!length)
537         return jsEmptyString(&exec);
538
539     // 6. Let element0 be Get(O, "0").
540     JSValue element0 = thisObject->getIndex(&exec, 0);
541     RETURN_IF_EXCEPTION(scope, { });
542
543     // 7. If element0 is undefined or null, let R be the empty String; otherwise, let R be ? ToString(element0).
544     JSString* r = nullptr;
545     if (element0.isUndefinedOrNull())
546         r = jsEmptyString(&exec);
547     else
548         r = element0.toString(&exec);
549     RETURN_IF_EXCEPTION(scope, { });
550
551     // 8. Let k be 1.
552     // 9. Repeat, while k < len
553     // 9.e Increase k by 1..
554     for (uint64_t k = 1; k < length; ++k) {
555         // b. Let element be ? Get(O, ! ToString(k)).
556         JSValue element = thisObject->get(&exec, Identifier::fromString(&exec, AtomicString::number(k)));
557         RETURN_IF_EXCEPTION(scope, { });
558
559         // c. If element is undefined or null, let next be the empty String; otherwise, let next be ? ToString(element).
560         JSString* next = nullptr;
561         if (element.isUndefinedOrNull()) {
562             if (!separator->length())
563                 continue;
564             next = jsEmptyString(&exec);
565         } else
566             next = element.toString(&exec);
567         RETURN_IF_EXCEPTION(scope, { });
568
569         // a. Let S be the String value produced by concatenating R and sep.
570         // d. Let R be a String value produced by concatenating S and next.
571         r = jsString(&exec, r, separator, next);
572         RETURN_IF_EXCEPTION(scope, { });
573     }
574     // 10. Return R.
575     return r;
576 }
577
578 static inline bool canUseFastJoin(const JSObject* thisObject)
579 {
580     switch (thisObject->indexingType()) {
581     case ALL_CONTIGUOUS_INDEXING_TYPES:
582     case ALL_INT32_INDEXING_TYPES:
583     case ALL_DOUBLE_INDEXING_TYPES:
584         return true;
585     default:
586         break;
587     }
588     return false;
589 }
590
591 static inline JSValue fastJoin(ExecState& state, JSObject* thisObject, StringView separator, unsigned length)
592 {
593     VM& vm = state.vm();
594     auto scope = DECLARE_THROW_SCOPE(vm);
595
596     switch (thisObject->indexingType()) {
597     case ALL_CONTIGUOUS_INDEXING_TYPES:
598     case ALL_INT32_INDEXING_TYPES: {
599         auto& butterfly = *thisObject->butterfly();
600         if (length > butterfly.publicLength())
601             break;
602         JSStringJoiner joiner(state, separator, length);
603         RETURN_IF_EXCEPTION(scope, { });
604         auto data = butterfly.contiguous().data();
605         bool holesKnownToBeOK = false;
606         for (unsigned i = 0; i < length; ++i) {
607             if (JSValue value = data[i].get()) {
608                 if (!joiner.appendWithoutSideEffects(state, value))
609                     goto generalCase;
610             } else {
611                 if (!holesKnownToBeOK) {
612                     if (holesMustForwardToPrototype(state, thisObject))
613                         goto generalCase;
614                     holesKnownToBeOK = true;
615                 }
616                 joiner.appendEmptyString();
617             }
618         }
619         scope.release();
620         return joiner.join(state);
621     }
622     case ALL_DOUBLE_INDEXING_TYPES: {
623         auto& butterfly = *thisObject->butterfly();
624         if (length > butterfly.publicLength())
625             break;
626         JSStringJoiner joiner(state, separator, length);
627         RETURN_IF_EXCEPTION(scope, { });
628         auto data = butterfly.contiguousDouble().data();
629         bool holesKnownToBeOK = false;
630         for (unsigned i = 0; i < length; ++i) {
631             double value = data[i];
632             if (!isHole(value))
633                 joiner.append(state, jsDoubleNumber(value));
634             else {
635                 if (!holesKnownToBeOK) {
636                     if (thisObject->structure(vm)->holesMustForwardToPrototype(vm))
637                         goto generalCase;
638                     holesKnownToBeOK = true;
639                 }
640                 joiner.appendEmptyString();
641             }
642         }
643         scope.release();
644         return joiner.join(state);
645     }
646     }
647
648 generalCase:
649     JSStringJoiner joiner(state, separator, length);
650     RETURN_IF_EXCEPTION(scope, { });
651     for (unsigned i = 0; i < length; ++i) {
652         JSValue element = thisObject->getIndex(&state, i);
653         RETURN_IF_EXCEPTION(scope, { });
654         joiner.append(state, element);
655         RETURN_IF_EXCEPTION(scope, { });
656     }
657     scope.release();
658     return joiner.join(state);
659 }
660
661 EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec)
662 {
663     VM& vm = exec->vm();
664     auto scope = DECLARE_THROW_SCOPE(vm);
665
666     // 1. Let O be ? ToObject(this value).
667     JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
668     ASSERT(!!scope.exception() == !thisObject);
669     if (UNLIKELY(!thisObject))
670         return encodedJSValue();
671
672     StringRecursionChecker checker(exec, thisObject);
673     ASSERT(!scope.exception() || checker.earlyReturnValue());
674     if (JSValue earlyReturnValue = checker.earlyReturnValue())
675         return JSValue::encode(earlyReturnValue);
676
677     // 2. Let len be ? ToLength(? Get(O, "length")).
678     double length = toLength(exec, thisObject);
679     RETURN_IF_EXCEPTION(scope, encodedJSValue());
680
681     // 3. If separator is undefined, let separator be the single-element String ",".
682     JSValue separatorValue = exec->argument(0);
683     if (separatorValue.isUndefined()) {
684         const LChar comma = ',';
685
686         if (UNLIKELY(length > std::numeric_limits<unsigned>::max() || !canUseFastJoin(thisObject))) {
687             uint64_t length64 = static_cast<uint64_t>(length);
688             ASSERT(static_cast<double>(length64) == length);
689             JSString* jsSeparator = jsSingleCharacterString(exec, comma);
690             RETURN_IF_EXCEPTION(scope, encodedJSValue());
691
692             scope.release();
693             return JSValue::encode(slowJoin(*exec, thisObject, jsSeparator, length64));
694         }
695
696         unsigned unsignedLength = static_cast<unsigned>(length);
697         ASSERT(static_cast<double>(unsignedLength) == length);
698
699         scope.release();
700         return JSValue::encode(fastJoin(*exec, thisObject, { &comma, 1 }, unsignedLength));
701     }
702
703     // 4. Let sep be ? ToString(separator).
704     JSString* jsSeparator = separatorValue.toString(exec);
705     RETURN_IF_EXCEPTION(scope, encodedJSValue());
706
707     if (UNLIKELY(length > std::numeric_limits<unsigned>::max() || !canUseFastJoin(thisObject))) {
708         uint64_t length64 = static_cast<uint64_t>(length);
709         ASSERT(static_cast<double>(length64) == length);
710
711         scope.release();
712         return JSValue::encode(slowJoin(*exec, thisObject, jsSeparator, length64));
713     }
714
715     auto viewWithString = jsSeparator->viewWithUnderlyingString(exec);
716     RETURN_IF_EXCEPTION(scope, encodedJSValue());
717
718     scope.release();
719     return JSValue::encode(fastJoin(*exec, thisObject, viewWithString.view, length));
720 }
721
722 EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState* exec)
723 {
724     VM& vm = exec->vm();
725     auto scope = DECLARE_THROW_SCOPE(vm);
726
727     JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
728
729     if (isJSArray(thisValue)) {
730         scope.release();
731         return JSValue::encode(asArray(thisValue)->pop(exec));
732     }
733
734     JSObject* thisObj = thisValue.toObject(exec);
735     ASSERT(!!scope.exception() == !thisObj);
736     if (UNLIKELY(!thisObj))
737         return encodedJSValue();
738     unsigned length = toLength(exec, thisObj);
739     RETURN_IF_EXCEPTION(scope, encodedJSValue());
740
741     if (length == 0) {
742         scope.release();
743         putLength(exec, vm, thisObj, jsNumber(length));
744         return JSValue::encode(jsUndefined());
745     }
746
747     JSValue result = thisObj->get(exec, length - 1);
748     RETURN_IF_EXCEPTION(scope, encodedJSValue());
749     bool success = thisObj->methodTable(vm)->deletePropertyByIndex(thisObj, exec, length - 1);
750     RETURN_IF_EXCEPTION(scope, encodedJSValue());
751     if (UNLIKELY(!success)) {
752         throwTypeError(exec, scope, ASCIILiteral(UnableToDeletePropertyError));
753         return encodedJSValue();
754     }
755     scope.release();
756     putLength(exec, vm, thisObj, jsNumber(length - 1));
757     return JSValue::encode(result);
758 }
759
760 EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState* exec)
761 {
762     VM& vm = exec->vm();
763     auto scope = DECLARE_THROW_SCOPE(vm);
764     JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
765
766     if (isJSArray(thisValue) && exec->argumentCount() == 1) {
767         JSArray* array = asArray(thisValue);
768         scope.release();
769         array->push(exec, exec->uncheckedArgument(0));
770         return JSValue::encode(jsNumber(array->length()));
771     }
772     
773     JSObject* thisObj = thisValue.toObject(exec);
774     ASSERT(!!scope.exception() == !thisObj);
775     if (UNLIKELY(!thisObj))
776         return encodedJSValue();
777     unsigned length = toLength(exec, thisObj);
778     RETURN_IF_EXCEPTION(scope, encodedJSValue());
779
780     for (unsigned n = 0; n < exec->argumentCount(); n++) {
781         // Check for integer overflow; where safe we can do a fast put by index.
782         if (length + n >= length)
783             thisObj->methodTable(vm)->putByIndex(thisObj, exec, length + n, exec->uncheckedArgument(n), true);
784         else {
785             PutPropertySlot slot(thisObj);
786             Identifier propertyName = Identifier::fromString(exec, JSValue(static_cast<int64_t>(length) + static_cast<int64_t>(n)).toWTFString(exec));
787             thisObj->methodTable(vm)->put(thisObj, exec, propertyName, exec->uncheckedArgument(n), slot);
788         }
789         RETURN_IF_EXCEPTION(scope, encodedJSValue());
790     }
791     
792     JSValue newLength(static_cast<int64_t>(length) + static_cast<int64_t>(exec->argumentCount()));
793     scope.release();
794     putLength(exec, vm, thisObj, newLength);
795     return JSValue::encode(newLength);
796 }
797
798 EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState* exec)
799 {
800     VM& vm = exec->vm();
801     auto scope = DECLARE_THROW_SCOPE(vm);
802
803     JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
804     ASSERT(!!scope.exception() == !thisObject);
805     if (UNLIKELY(!thisObject))
806         return encodedJSValue();
807
808     unsigned length = toLength(exec, thisObject);
809     RETURN_IF_EXCEPTION(scope, encodedJSValue());
810
811     switch (thisObject->indexingType()) {
812     case ALL_CONTIGUOUS_INDEXING_TYPES:
813     case ALL_INT32_INDEXING_TYPES: {
814         auto& butterfly = *thisObject->butterfly();
815         if (length > butterfly.publicLength())
816             break;
817         auto data = butterfly.contiguous().data();
818         if (containsHole(data, length) && holesMustForwardToPrototype(*exec, thisObject))
819             break;
820         std::reverse(data, data + length);
821         return JSValue::encode(thisObject);
822     }
823     case ALL_DOUBLE_INDEXING_TYPES: {
824         auto& butterfly = *thisObject->butterfly();
825         if (length > butterfly.publicLength())
826             break;
827         auto data = butterfly.contiguousDouble().data();
828         if (containsHole(data, length) && holesMustForwardToPrototype(*exec, thisObject))
829             break;
830         std::reverse(data, data + length);
831         return JSValue::encode(thisObject);
832     }
833     case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
834         auto& storage = *thisObject->butterfly()->arrayStorage();
835         if (length > storage.vectorLength())
836             break;
837         if (storage.hasHoles() && holesMustForwardToPrototype(*exec, thisObject))
838             break;
839         auto data = storage.vector().data();
840         std::reverse(data, data + length);
841         return JSValue::encode(thisObject);
842     }
843     }
844
845     unsigned middle = length / 2;
846     for (unsigned lower = 0; lower < middle; lower++) {
847         unsigned upper = length - lower - 1;
848         bool lowerExists = thisObject->hasProperty(exec, lower);
849         RETURN_IF_EXCEPTION(scope, encodedJSValue());
850         JSValue lowerValue;
851         if (lowerExists) {
852             lowerValue = thisObject->get(exec, lower);
853             RETURN_IF_EXCEPTION(scope, encodedJSValue());
854         }
855
856         bool upperExists = thisObject->hasProperty(exec, upper);
857         RETURN_IF_EXCEPTION(scope, encodedJSValue());
858         JSValue upperValue;
859         if (upperExists) {
860             upperValue = thisObject->get(exec, upper);
861             RETURN_IF_EXCEPTION(scope, encodedJSValue());
862         }
863
864         if (upperExists) {
865             thisObject->putByIndexInline(exec, lower, upperValue, true);
866             RETURN_IF_EXCEPTION(scope, encodedJSValue());
867         } else {
868             bool success = thisObject->methodTable(vm)->deletePropertyByIndex(thisObject, exec, lower);
869             RETURN_IF_EXCEPTION(scope, encodedJSValue());
870             if (UNLIKELY(!success)) {
871                 throwTypeError(exec, scope, ASCIILiteral(UnableToDeletePropertyError));
872                 return encodedJSValue();
873             }
874         }
875
876         if (lowerExists) {
877             thisObject->putByIndexInline(exec, upper, lowerValue, true);
878             RETURN_IF_EXCEPTION(scope, encodedJSValue());
879         } else {
880             bool success = thisObject->methodTable(vm)->deletePropertyByIndex(thisObject, exec, upper);
881             RETURN_IF_EXCEPTION(scope, encodedJSValue());
882             if (UNLIKELY(!success)) {
883                 throwTypeError(exec, scope, ASCIILiteral(UnableToDeletePropertyError));
884                 return encodedJSValue();
885             }
886         }
887     }
888     return JSValue::encode(thisObject);
889 }
890
891 EncodedJSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState* exec)
892 {
893     VM& vm = exec->vm();
894     auto scope = DECLARE_THROW_SCOPE(vm);
895     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
896     ASSERT(!!scope.exception() == !thisObj);
897     if (UNLIKELY(!thisObj))
898         return encodedJSValue();
899     unsigned length = toLength(exec, thisObj);
900     RETURN_IF_EXCEPTION(scope, encodedJSValue());
901
902     if (length == 0) {
903         scope.release();
904         putLength(exec, vm, thisObj, jsNumber(length));
905         return JSValue::encode(jsUndefined());
906     }
907
908     JSValue result = thisObj->getIndex(exec, 0);
909     RETURN_IF_EXCEPTION(scope, encodedJSValue());
910     shift<JSArray::ShiftCountForShift>(exec, thisObj, 0, 1, 0, length);
911     RETURN_IF_EXCEPTION(scope, encodedJSValue());
912     scope.release();
913     putLength(exec, vm, thisObj, jsNumber(length - 1));
914     return JSValue::encode(result);
915 }
916
917 EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec)
918 {
919     // https://tc39.github.io/ecma262/#sec-array.prototype.slice
920     VM& vm = exec->vm();
921     auto scope = DECLARE_THROW_SCOPE(vm);
922     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
923     ASSERT(!!scope.exception() == !thisObj);
924     if (UNLIKELY(!thisObj))
925         return { };
926     unsigned length = toLength(exec, thisObj);
927     RETURN_IF_EXCEPTION(scope, { });
928
929     unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length);
930     RETURN_IF_EXCEPTION(scope, { });
931     unsigned end = argumentClampedIndexFromStartOrEnd(exec, 1, length, length);
932     RETURN_IF_EXCEPTION(scope, { });
933     if (end < begin)
934         end = begin;
935
936     std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, end - begin);
937     // We can only get an exception if we call some user function.
938     ASSERT(!!scope.exception() == (speciesResult.first == SpeciesConstructResult::Exception));
939     if (UNLIKELY(speciesResult.first == SpeciesConstructResult::Exception))
940         return { };
941
942     bool okToDoFastPath = speciesResult.first == SpeciesConstructResult::FastPath && isJSArray(thisObj) && length == toLength(exec, thisObj);
943     RETURN_IF_EXCEPTION(scope, { });
944     if (LIKELY(okToDoFastPath)) {
945         if (JSArray* result = asArray(thisObj)->fastSlice(*exec, begin, end - begin))
946             return JSValue::encode(result);
947     }
948
949     JSObject* result;
950     if (speciesResult.first == SpeciesConstructResult::CreatedObject)
951         result = speciesResult.second;
952     else {
953         result = constructEmptyArray(exec, nullptr, end - begin);
954         RETURN_IF_EXCEPTION(scope, { });
955     }
956
957     unsigned n = 0;
958     for (unsigned k = begin; k < end; k++, n++) {
959         JSValue v = getProperty(exec, thisObj, k);
960         RETURN_IF_EXCEPTION(scope, { });
961         if (v) {
962             result->putDirectIndex(exec, n, v, 0, PutDirectIndexShouldThrow);
963             RETURN_IF_EXCEPTION(scope, { });
964         }
965     }
966     scope.release();
967     setLength(exec, vm, result, n);
968     return JSValue::encode(result);
969 }
970
971 EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec)
972 {
973     // 15.4.4.12
974
975     VM& vm = exec->vm();
976     auto scope = DECLARE_THROW_SCOPE(vm);
977
978     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
979     ASSERT(!!scope.exception() == !thisObj);
980     if (UNLIKELY(!thisObj))
981         return encodedJSValue();
982     unsigned length = toLength(exec, thisObj);
983     RETURN_IF_EXCEPTION(scope, encodedJSValue());
984
985     if (!exec->argumentCount()) {
986         std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, 0);
987         ASSERT(!!scope.exception() == (speciesResult.first == SpeciesConstructResult::Exception));
988         if (UNLIKELY(speciesResult.first == SpeciesConstructResult::Exception))
989             return encodedJSValue();
990
991         JSObject* result;
992         if (speciesResult.first == SpeciesConstructResult::CreatedObject)
993             result = speciesResult.second;
994         else {
995             result = constructEmptyArray(exec, nullptr);
996             RETURN_IF_EXCEPTION(scope, encodedJSValue());
997         }
998
999         setLength(exec, vm, result, 0);
1000         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1001         scope.release();
1002         setLength(exec, vm, thisObj, length);
1003         return JSValue::encode(result);
1004     }
1005
1006     unsigned actualStart = argumentClampedIndexFromStartOrEnd(exec, 0, length);
1007     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1008
1009     unsigned actualDeleteCount = length - actualStart;
1010     if (exec->argumentCount() > 1) {
1011         double deleteCount = exec->uncheckedArgument(1).toInteger(exec);
1012         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1013         if (deleteCount < 0)
1014             actualDeleteCount = 0;
1015         else if (deleteCount > length - actualStart)
1016             actualDeleteCount = length - actualStart;
1017         else
1018             actualDeleteCount = static_cast<unsigned>(deleteCount);
1019     }
1020
1021     std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, actualDeleteCount);
1022     ASSERT(!!scope.exception() == (speciesResult.first == SpeciesConstructResult::Exception));
1023     if (speciesResult.first == SpeciesConstructResult::Exception)
1024         return JSValue::encode(jsUndefined());
1025
1026     JSObject* result = nullptr;
1027     bool okToDoFastPath = speciesResult.first == SpeciesConstructResult::FastPath && isJSArray(thisObj) && length == toLength(exec, thisObj);
1028     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1029     if (LIKELY(okToDoFastPath))
1030         result = asArray(thisObj)->fastSlice(*exec, actualStart, actualDeleteCount);
1031
1032     if (!result) {
1033         if (speciesResult.first == SpeciesConstructResult::CreatedObject)
1034             result = speciesResult.second;
1035         else {
1036             result = JSArray::tryCreate(vm, exec->lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), actualDeleteCount);
1037             if (UNLIKELY(!result)) {
1038                 throwOutOfMemoryError(exec, scope);
1039                 return encodedJSValue();
1040             }
1041         }
1042         for (unsigned k = 0; k < actualDeleteCount; ++k) {
1043             JSValue v = getProperty(exec, thisObj, k + actualStart);
1044             RETURN_IF_EXCEPTION(scope, encodedJSValue());
1045             if (UNLIKELY(!v))
1046                 continue;
1047             result->putDirectIndex(exec, k, v, 0, PutDirectIndexShouldThrow);
1048             RETURN_IF_EXCEPTION(scope, encodedJSValue());
1049         }
1050     }
1051
1052     unsigned itemCount = std::max<int>(exec->argumentCount() - 2, 0);
1053     if (itemCount < actualDeleteCount) {
1054         shift<JSArray::ShiftCountForSplice>(exec, thisObj, actualStart, actualDeleteCount, itemCount, length);
1055         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1056     } else if (itemCount > actualDeleteCount) {
1057         unshift<JSArray::ShiftCountForSplice>(exec, thisObj, actualStart, actualDeleteCount, itemCount, length);
1058         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1059     }
1060     for (unsigned k = 0; k < itemCount; ++k) {
1061         thisObj->putByIndexInline(exec, k + actualStart, exec->uncheckedArgument(k + 2), true);
1062         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1063     }
1064     
1065     scope.release();
1066     setLength(exec, vm, thisObj, length - actualDeleteCount + itemCount);
1067     return JSValue::encode(result);
1068 }
1069
1070 EncodedJSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState* exec)
1071 {
1072     VM& vm = exec->vm();
1073     auto scope = DECLARE_THROW_SCOPE(vm);
1074     // 15.4.4.13
1075
1076     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
1077     ASSERT(!!scope.exception() == !thisObj);
1078     if (UNLIKELY(!thisObj))
1079         return encodedJSValue();
1080     double doubleLength = toLength(exec, thisObj);
1081     unsigned length = doubleLength;
1082     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1083
1084     unsigned nrArgs = exec->argumentCount();
1085     if (nrArgs) {
1086         if (UNLIKELY(doubleLength + static_cast<double>(nrArgs) > maxSafeInteger()))
1087             return throwVMTypeError(exec, scope, ASCIILiteral("Cannot shift to offset greater than (2 ** 53) - 1"));
1088         unshift<JSArray::ShiftCountForShift>(exec, thisObj, 0, 0, nrArgs, length);
1089         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1090     }
1091     for (unsigned k = 0; k < nrArgs; ++k) {
1092         thisObj->putByIndexInline(exec, k, exec->uncheckedArgument(k), true);
1093         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1094     }
1095     JSValue result = jsNumber(length + nrArgs);
1096     scope.release();
1097     putLength(exec, vm, thisObj, result);
1098     return JSValue::encode(result);
1099 }
1100
1101 EncodedJSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState* exec)
1102 {
1103     VM& vm = exec->vm();
1104     auto scope = DECLARE_THROW_SCOPE(vm);
1105
1106     // 15.4.4.14
1107     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
1108     ASSERT(!!scope.exception() == !thisObj);
1109     if (UNLIKELY(!thisObj))
1110         return encodedJSValue();
1111     unsigned length = toLength(exec, thisObj);
1112     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1113
1114     unsigned index = argumentClampedIndexFromStartOrEnd(exec, 1, length);
1115     JSValue searchElement = exec->argument(0);
1116     for (; index < length; ++index) {
1117         JSValue e = getProperty(exec, thisObj, index);
1118         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1119         if (!e)
1120             continue;
1121         if (JSValue::strictEqual(exec, searchElement, e))
1122             return JSValue::encode(jsNumber(index));
1123         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1124     }
1125
1126     return JSValue::encode(jsNumber(-1));
1127 }
1128
1129 EncodedJSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState* exec)
1130 {
1131     VM& vm = exec->vm();
1132     auto scope = DECLARE_THROW_SCOPE(vm);
1133
1134     // 15.4.4.15
1135     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
1136     ASSERT(!!scope.exception() == !thisObj);
1137     if (UNLIKELY(!thisObj))
1138         return encodedJSValue();
1139     unsigned length = toLength(exec, thisObj);
1140     if (UNLIKELY(scope.exception()) || !length)
1141         return JSValue::encode(jsNumber(-1));
1142
1143     unsigned index = length - 1;
1144     if (exec->argumentCount() >= 2) {
1145         JSValue fromValue = exec->uncheckedArgument(1);
1146         double fromDouble = fromValue.toInteger(exec);
1147         if (fromDouble < 0) {
1148             fromDouble += length;
1149             if (fromDouble < 0)
1150                 return JSValue::encode(jsNumber(-1));
1151         }
1152         if (fromDouble < length)
1153             index = static_cast<unsigned>(fromDouble);
1154     }
1155
1156     JSValue searchElement = exec->argument(0);
1157     do {
1158         RELEASE_ASSERT(index < length);
1159         JSValue e = getProperty(exec, thisObj, index);
1160         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1161         if (!e)
1162             continue;
1163         if (JSValue::strictEqual(exec, searchElement, e))
1164             return JSValue::encode(jsNumber(index));
1165         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1166     } while (index--);
1167
1168     return JSValue::encode(jsNumber(-1));
1169 }
1170
1171 static bool moveElements(ExecState* exec, VM& vm, JSArray* target, unsigned targetOffset, JSArray* source, unsigned sourceLength)
1172 {
1173     auto scope = DECLARE_THROW_SCOPE(vm);
1174
1175     if (LIKELY(!hasAnyArrayStorage(source->indexingType()) && !source->structure()->holesMustForwardToPrototype(vm))) {
1176         for (unsigned i = 0; i < sourceLength; ++i) {
1177             JSValue value = source->tryGetIndexQuickly(i);
1178             if (value) {
1179                 target->putDirectIndex(exec, targetOffset + i, value, 0, PutDirectIndexShouldThrow);
1180                 RETURN_IF_EXCEPTION(scope, false);
1181             }
1182         }
1183     } else {
1184         for (unsigned i = 0; i < sourceLength; ++i) {
1185             JSValue value = getProperty(exec, source, i);
1186             RETURN_IF_EXCEPTION(scope, false);
1187             if (value) {
1188                 target->putDirectIndex(exec, targetOffset + i, value, 0, PutDirectIndexShouldThrow);
1189                 RETURN_IF_EXCEPTION(scope, false);
1190             }
1191         }
1192     }
1193     return true;
1194 }
1195
1196 static EncodedJSValue concatAppendOne(ExecState* exec, VM& vm, JSArray* first, JSValue second)
1197 {
1198     auto scope = DECLARE_THROW_SCOPE(vm);
1199
1200     ASSERT(!isJSArray(second));
1201     ASSERT(!shouldUseSlowPut(first->indexingType()));
1202     Butterfly* firstButterfly = first->butterfly();
1203     unsigned firstArraySize = firstButterfly->publicLength();
1204
1205     Checked<unsigned, RecordOverflow> checkedResultSize = firstArraySize;
1206     checkedResultSize += 1;
1207     if (UNLIKELY(checkedResultSize.hasOverflowed())) {
1208         throwOutOfMemoryError(exec, scope);
1209         return encodedJSValue();
1210     }
1211
1212     unsigned resultSize = checkedResultSize.unsafeGet();
1213     IndexingType type = first->mergeIndexingTypeForCopying(indexingTypeForValue(second) | IsArray);
1214     
1215     if (type == NonArray)
1216         type = first->indexingType();
1217
1218     Structure* resultStructure = exec->lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(type);
1219     JSArray* result = JSArray::create(vm, resultStructure, resultSize);
1220     if (UNLIKELY(!result)) {
1221         throwOutOfMemoryError(exec, scope);
1222         return encodedJSValue();
1223     }
1224
1225     bool success = result->appendMemcpy(exec, vm, 0, first);
1226     ASSERT(!success || !scope.exception());
1227     if (!success) {
1228         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1229
1230         bool success = moveElements(exec, vm, result, 0, first, firstArraySize);
1231         ASSERT(!scope.exception() == success);
1232         if (UNLIKELY(!success))
1233             return encodedJSValue();
1234     }
1235
1236     scope.release();
1237     result->putDirectIndex(exec, firstArraySize, second);
1238     return JSValue::encode(result);
1239
1240 }
1241
1242
1243 EncodedJSValue JSC_HOST_CALL arrayProtoPrivateFuncConcatMemcpy(ExecState* exec)
1244 {
1245     ASSERT(exec->argumentCount() == 2);
1246     VM& vm = exec->vm();
1247     auto scope = DECLARE_THROW_SCOPE(vm);
1248
1249     JSArray* firstArray = jsCast<JSArray*>(exec->uncheckedArgument(0));
1250     
1251     // This code assumes that neither array has set Symbol.isConcatSpreadable. If the first array
1252     // has indexed accessors then one of those accessors might change the value of Symbol.isConcatSpreadable
1253     // on the second argument.
1254     if (UNLIKELY(shouldUseSlowPut(firstArray->indexingType())))
1255         return JSValue::encode(jsNull());
1256
1257     // We need to check the species constructor here since checking it in the JS wrapper is too expensive for the non-optimizing tiers.
1258     bool isValid = speciesWatchpointIsValid(exec, firstArray);
1259     if (UNLIKELY(!isValid))
1260         return JSValue::encode(jsNull());
1261
1262     JSValue second = exec->uncheckedArgument(1);
1263     if (!isJSArray(second)) {
1264         scope.release();
1265         return concatAppendOne(exec, vm, firstArray, second);
1266     }
1267
1268     JSArray* secondArray = jsCast<JSArray*>(second);
1269     
1270     Butterfly* firstButterfly = firstArray->butterfly();
1271     Butterfly* secondButterfly = secondArray->butterfly();
1272
1273     unsigned firstArraySize = firstButterfly->publicLength();
1274     unsigned secondArraySize = secondButterfly->publicLength();
1275
1276     Checked<unsigned, RecordOverflow> checkedResultSize = firstArraySize;
1277     checkedResultSize += secondArraySize;
1278
1279     if (UNLIKELY(checkedResultSize.hasOverflowed())) {
1280         throwOutOfMemoryError(exec, scope);
1281         return encodedJSValue();
1282     }
1283
1284     unsigned resultSize = checkedResultSize.unsafeGet();
1285     IndexingType firstType = firstArray->indexingType();
1286     IndexingType secondType = secondArray->indexingType();
1287     IndexingType type = firstArray->mergeIndexingTypeForCopying(secondType);
1288     if (type == NonArray || !firstArray->canFastCopy(vm, secondArray) || resultSize >= MIN_SPARSE_ARRAY_INDEX) {
1289         JSArray* result = constructEmptyArray(exec, nullptr, resultSize);
1290         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1291
1292         bool success = moveElements(exec, vm, result, 0, firstArray, firstArraySize);
1293         ASSERT(!scope.exception() == success);
1294         if (UNLIKELY(!success))
1295             return encodedJSValue();
1296         success = moveElements(exec, vm, result, firstArraySize, secondArray, secondArraySize);
1297         ASSERT(!scope.exception() == success);
1298         if (UNLIKELY(!success))
1299             return encodedJSValue();
1300
1301         return JSValue::encode(result);
1302     }
1303
1304     JSGlobalObject* lexicalGlobalObject = exec->lexicalGlobalObject();
1305     Structure* resultStructure = lexicalGlobalObject->arrayStructureForIndexingTypeDuringAllocation(type);
1306     if (UNLIKELY(hasAnyArrayStorage(resultStructure->indexingType())))
1307         return JSValue::encode(jsNull());
1308
1309     ASSERT(!lexicalGlobalObject->isHavingABadTime());
1310     ObjectInitializationScope initializationScope(vm);
1311     JSArray* result = JSArray::tryCreateUninitializedRestricted(initializationScope, resultStructure, resultSize);
1312     if (UNLIKELY(!result)) {
1313         throwOutOfMemoryError(exec, scope);
1314         return encodedJSValue();
1315     }
1316     
1317     if (type == ArrayWithDouble) {
1318         double* buffer = result->butterfly()->contiguousDouble().data();
1319         memcpy(buffer, firstButterfly->contiguousDouble().data(), sizeof(JSValue) * firstArraySize);
1320         memcpy(buffer + firstArraySize, secondButterfly->contiguousDouble().data(), sizeof(JSValue) * secondArraySize);
1321     } else if (type != ArrayWithUndecided) {
1322         WriteBarrier<Unknown>* buffer = result->butterfly()->contiguous().data();
1323         
1324         auto copy = [&] (unsigned offset, void* source, unsigned size, IndexingType type) {
1325             if (type != ArrayWithUndecided) {
1326                 memcpy(buffer + offset, source, sizeof(JSValue) * size);
1327                 return;
1328             }
1329             
1330             for (unsigned i = size; i--;)
1331                 buffer[i + offset].clear();
1332         };
1333         
1334         copy(0, firstButterfly->contiguous().data(), firstArraySize, firstType);
1335         copy(firstArraySize, secondButterfly->contiguous().data(), secondArraySize, secondType);
1336     }
1337
1338     result->butterfly()->setPublicLength(resultSize);
1339     return JSValue::encode(result);
1340 }
1341
1342 EncodedJSValue JSC_HOST_CALL arrayProtoPrivateFuncAppendMemcpy(ExecState* exec)
1343 {
1344     ASSERT(exec->argumentCount() == 3);
1345
1346     VM& vm = exec->vm();
1347     auto scope = DECLARE_THROW_SCOPE(vm);
1348     JSArray* resultArray = jsCast<JSArray*>(exec->uncheckedArgument(0));
1349     JSArray* otherArray = jsCast<JSArray*>(exec->uncheckedArgument(1));
1350     JSValue startValue = exec->uncheckedArgument(2);
1351     ASSERT(startValue.isAnyInt() && startValue.asAnyInt() >= 0 && startValue.asAnyInt() <= std::numeric_limits<unsigned>::max());
1352     unsigned startIndex = static_cast<unsigned>(startValue.asAnyInt());
1353     bool success = resultArray->appendMemcpy(exec, vm, startIndex, otherArray);
1354     ASSERT(!success || !scope.exception());
1355     if (!success) {
1356         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1357         moveElements(exec, vm, resultArray, startIndex, otherArray, otherArray->length());
1358     }
1359     return JSValue::encode(jsUndefined());
1360 }
1361
1362
1363 // -------------------- ArrayPrototype.constructor Watchpoint ------------------
1364
1365 static bool verbose = false;
1366
1367 class ArrayPrototypeAdaptiveInferredPropertyWatchpoint : public AdaptiveInferredPropertyValueWatchpointBase {
1368 public:
1369     typedef AdaptiveInferredPropertyValueWatchpointBase Base;
1370     ArrayPrototypeAdaptiveInferredPropertyWatchpoint(const ObjectPropertyCondition&, ArrayPrototype*);
1371
1372 private:
1373     void handleFire(const FireDetail&) override;
1374
1375     ArrayPrototype* m_arrayPrototype;
1376 };
1377
1378 void ArrayPrototype::tryInitializeSpeciesWatchpoint(ExecState* exec)
1379 {
1380     VM& vm = exec->vm();
1381
1382     RELEASE_ASSERT(!m_constructorWatchpoint);
1383     RELEASE_ASSERT(!m_constructorSpeciesWatchpoint);
1384
1385     auto scope = DECLARE_THROW_SCOPE(vm);
1386
1387     if (verbose)
1388         dataLog("Initializing Array species watchpoints for Array.prototype: ", pointerDump(this), " with structure: ", pointerDump(this->structure()), "\nand Array: ", pointerDump(this->globalObject()->arrayConstructor()), " with structure: ", pointerDump(this->globalObject()->arrayConstructor()->structure()), "\n");
1389     // First we need to make sure that the Array.prototype.constructor property points to Array
1390     // and that Array[Symbol.species] is the primordial GetterSetter.
1391
1392     // We only initialize once so flattening the structures does not have any real cost.
1393     Structure* prototypeStructure = this->structure(vm);
1394     if (prototypeStructure->isDictionary())
1395         prototypeStructure = prototypeStructure->flattenDictionaryStructure(vm, this);
1396     RELEASE_ASSERT(!prototypeStructure->isDictionary());
1397
1398     JSGlobalObject* globalObject = this->globalObject();
1399     ArrayConstructor* arrayConstructor = globalObject->arrayConstructor();
1400
1401     auto invalidateWatchpoint = [&] {
1402         globalObject->arraySpeciesWatchpoint().invalidate(vm, StringFireDetail("Was not able to set up array species watchpoint."));
1403     };
1404
1405     PropertySlot constructorSlot(this, PropertySlot::InternalMethodType::VMInquiry);
1406     this->getOwnPropertySlot(this, exec, vm.propertyNames->constructor, constructorSlot);
1407     scope.assertNoException();
1408     if (constructorSlot.slotBase() != this
1409         || !constructorSlot.isCacheableValue()
1410         || constructorSlot.getValue(exec, vm.propertyNames->constructor) != arrayConstructor) {
1411         invalidateWatchpoint();
1412         return;
1413     }
1414
1415     Structure* constructorStructure = arrayConstructor->structure(vm);
1416     if (constructorStructure->isDictionary())
1417         constructorStructure = constructorStructure->flattenDictionaryStructure(vm, arrayConstructor);
1418
1419     PropertySlot speciesSlot(arrayConstructor, PropertySlot::InternalMethodType::VMInquiry);
1420     arrayConstructor->getOwnPropertySlot(arrayConstructor, exec, vm.propertyNames->speciesSymbol, speciesSlot);
1421     scope.assertNoException();
1422     if (speciesSlot.slotBase() != arrayConstructor
1423         || !speciesSlot.isCacheableGetter()
1424         || speciesSlot.getterSetter() != globalObject->speciesGetterSetter()) {
1425         invalidateWatchpoint();
1426         return;
1427     }
1428
1429     // Now we need to setup the watchpoints to make sure these conditions remain valid.
1430     prototypeStructure->startWatchingPropertyForReplacements(vm, constructorSlot.cachedOffset());
1431     constructorStructure->startWatchingPropertyForReplacements(vm, speciesSlot.cachedOffset());
1432
1433     ObjectPropertyCondition constructorCondition = ObjectPropertyCondition::equivalence(vm, this, this, vm.propertyNames->constructor.impl(), arrayConstructor);
1434     ObjectPropertyCondition speciesCondition = ObjectPropertyCondition::equivalence(vm, this, arrayConstructor, vm.propertyNames->speciesSymbol.impl(), globalObject->speciesGetterSetter());
1435
1436     if (!constructorCondition.isWatchable() || !speciesCondition.isWatchable()) {
1437         invalidateWatchpoint();
1438         return;
1439     }
1440
1441     m_constructorWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(constructorCondition, this);
1442     m_constructorWatchpoint->install();
1443
1444     m_constructorSpeciesWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(speciesCondition, this);
1445     m_constructorSpeciesWatchpoint->install();
1446
1447     // We only watch this from the DFG, and the DFG makes sure to only start watching if the watchpoint is in the IsWatched state.
1448     RELEASE_ASSERT(!globalObject->arraySpeciesWatchpoint().isBeingWatched()); 
1449     globalObject->arraySpeciesWatchpoint().touch(vm, "Set up array species watchpoint.");
1450 }
1451
1452 ArrayPrototypeAdaptiveInferredPropertyWatchpoint::ArrayPrototypeAdaptiveInferredPropertyWatchpoint(const ObjectPropertyCondition& key, ArrayPrototype* prototype)
1453     : Base(key)
1454     , m_arrayPrototype(prototype)
1455 {
1456 }
1457
1458 void ArrayPrototypeAdaptiveInferredPropertyWatchpoint::handleFire(const FireDetail& detail)
1459 {
1460     StringPrintStream out;
1461     out.print("ArrayPrototype adaption of ", key(), " failed: ", detail);
1462
1463     StringFireDetail stringDetail(out.toCString().data());
1464
1465     if (verbose)
1466         WTF::dataLog(stringDetail, "\n");
1467
1468     JSGlobalObject* globalObject = m_arrayPrototype->globalObject();
1469     globalObject->arraySpeciesWatchpoint().fireAll(globalObject->vm(), stringDetail);
1470 }
1471
1472 } // namespace JSC