8f16943a51a7f6dbb9e29c29b7861396cb426950
[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
452     unsigned length = getLength(exec, thisObject);
453     RETURN_IF_EXCEPTION(scope, encodedJSValue());
454
455     StringRecursionChecker checker(exec, thisObject);
456     ASSERT(!scope.exception() || checker.earlyReturnValue());
457     if (JSValue earlyReturnValue = checker.earlyReturnValue())
458         return JSValue::encode(earlyReturnValue);
459
460     JSStringJoiner stringJoiner(*exec, ',', length);
461     RETURN_IF_EXCEPTION(scope, encodedJSValue());
462
463 #if ENABLE(INTL)
464     ArgList arguments(exec);
465     for (unsigned i = 0; i < length; ++i) {
466         JSValue element = thisObject->getIndex(exec, i);
467         RETURN_IF_EXCEPTION(scope, encodedJSValue());
468         if (element.isUndefinedOrNull())
469             element = jsEmptyString(exec);
470         else {
471             JSValue conversionFunction = element.get(exec, vm.propertyNames->toLocaleString);
472             RETURN_IF_EXCEPTION(scope, encodedJSValue());
473             CallData callData;
474             CallType callType = getCallData(conversionFunction, callData);
475             if (callType != CallType::None) {
476                 element = call(exec, conversionFunction, callType, callData, element, arguments);
477                 RETURN_IF_EXCEPTION(scope, encodedJSValue());
478             }
479         }
480         stringJoiner.append(*exec, element);
481         RETURN_IF_EXCEPTION(scope, encodedJSValue());
482     }
483 #else // !ENABLE(INTL)
484     for (unsigned i = 0; i < length; ++i) {
485         JSValue element = thisObject->getIndex(exec, i);
486         RETURN_IF_EXCEPTION(scope, encodedJSValue());
487         if (element.isUndefinedOrNull())
488             continue;
489         JSValue conversionFunction = element.get(exec, vm.propertyNames->toLocaleString);
490         RETURN_IF_EXCEPTION(scope, encodedJSValue());
491         CallData callData;
492         CallType callType = getCallData(conversionFunction, callData);
493         if (callType != CallType::None) {
494             element = call(exec, conversionFunction, callType, callData, element, exec->emptyList());
495             RETURN_IF_EXCEPTION(scope, encodedJSValue());
496         }
497         stringJoiner.append(*exec, element);
498         RETURN_IF_EXCEPTION(scope, encodedJSValue());
499     }
500 #endif // !ENABLE(INTL)
501
502     scope.release();
503     return JSValue::encode(stringJoiner.join(*exec));
504 }
505
506 static inline bool isHole(double value)
507 {
508     return std::isnan(value);
509 }
510
511 static inline bool isHole(const WriteBarrier<Unknown>& value)
512 {
513     return !value;
514 }
515
516 template<typename T> static inline bool containsHole(T* data, unsigned length)
517 {
518     for (unsigned i = 0; i < length; ++i) {
519         if (isHole(data[i]))
520             return true;
521     }
522     return false;
523 }
524
525 static inline bool holesMustForwardToPrototype(ExecState& state, JSObject* object)
526 {
527     auto& vm = state.vm();
528     return object->structure(vm)->holesMustForwardToPrototype(vm);
529 }
530
531 static JSValue slowJoin(ExecState& exec, JSObject* thisObject, JSString* separator, uint64_t length)
532 {
533     VM& vm = exec.vm();
534     auto scope = DECLARE_THROW_SCOPE(vm);
535
536     // 5. If len is zero, return the empty String.
537     if (!length)
538         return jsEmptyString(&exec);
539
540     // 6. Let element0 be Get(O, "0").
541     JSValue element0 = thisObject->getIndex(&exec, 0);
542     RETURN_IF_EXCEPTION(scope, { });
543
544     // 7. If element0 is undefined or null, let R be the empty String; otherwise, let R be ? ToString(element0).
545     JSString* r = nullptr;
546     if (element0.isUndefinedOrNull())
547         r = jsEmptyString(&exec);
548     else
549         r = element0.toString(&exec);
550     RETURN_IF_EXCEPTION(scope, { });
551
552     // 8. Let k be 1.
553     // 9. Repeat, while k < len
554     // 9.e Increase k by 1..
555     for (uint64_t k = 1; k < length; ++k) {
556         // b. Let element be ? Get(O, ! ToString(k)).
557         JSValue element = thisObject->get(&exec, Identifier::fromString(&exec, AtomicString::number(k)));
558         RETURN_IF_EXCEPTION(scope, { });
559
560         // c. If element is undefined or null, let next be the empty String; otherwise, let next be ? ToString(element).
561         JSString* next = nullptr;
562         if (element.isUndefinedOrNull()) {
563             if (!separator->length())
564                 continue;
565             next = jsEmptyString(&exec);
566         } else
567             next = element.toString(&exec);
568         RETURN_IF_EXCEPTION(scope, { });
569
570         // a. Let S be the String value produced by concatenating R and sep.
571         // d. Let R be a String value produced by concatenating S and next.
572         r = jsString(&exec, r, separator, next);
573         RETURN_IF_EXCEPTION(scope, { });
574     }
575     // 10. Return R.
576     return r;
577 }
578
579 static inline bool canUseFastJoin(const JSObject* thisObject)
580 {
581     switch (thisObject->indexingType()) {
582     case ALL_CONTIGUOUS_INDEXING_TYPES:
583     case ALL_INT32_INDEXING_TYPES:
584     case ALL_DOUBLE_INDEXING_TYPES:
585         return true;
586     default:
587         break;
588     }
589     return false;
590 }
591
592 static inline JSValue fastJoin(ExecState& state, JSObject* thisObject, StringView separator, unsigned length)
593 {
594     VM& vm = state.vm();
595     auto scope = DECLARE_THROW_SCOPE(vm);
596
597     switch (thisObject->indexingType()) {
598     case ALL_CONTIGUOUS_INDEXING_TYPES:
599     case ALL_INT32_INDEXING_TYPES: {
600         auto& butterfly = *thisObject->butterfly();
601         if (length > butterfly.publicLength())
602             break;
603         JSStringJoiner joiner(state, separator, length);
604         RETURN_IF_EXCEPTION(scope, { });
605         auto data = butterfly.contiguous().data();
606         bool holesKnownToBeOK = false;
607         for (unsigned i = 0; i < length; ++i) {
608             if (JSValue value = data[i].get()) {
609                 if (!joiner.appendWithoutSideEffects(state, value))
610                     goto generalCase;
611             } else {
612                 if (!holesKnownToBeOK) {
613                     if (holesMustForwardToPrototype(state, thisObject))
614                         goto generalCase;
615                     holesKnownToBeOK = true;
616                 }
617                 joiner.appendEmptyString();
618             }
619         }
620         scope.release();
621         return joiner.join(state);
622     }
623     case ALL_DOUBLE_INDEXING_TYPES: {
624         auto& butterfly = *thisObject->butterfly();
625         if (length > butterfly.publicLength())
626             break;
627         JSStringJoiner joiner(state, separator, length);
628         RETURN_IF_EXCEPTION(scope, { });
629         auto data = butterfly.contiguousDouble().data();
630         bool holesKnownToBeOK = false;
631         for (unsigned i = 0; i < length; ++i) {
632             double value = data[i];
633             if (!isHole(value))
634                 joiner.append(state, jsDoubleNumber(value));
635             else {
636                 if (!holesKnownToBeOK) {
637                     if (thisObject->structure(vm)->holesMustForwardToPrototype(vm))
638                         goto generalCase;
639                     holesKnownToBeOK = true;
640                 }
641                 joiner.appendEmptyString();
642             }
643         }
644         scope.release();
645         return joiner.join(state);
646     }
647     }
648
649 generalCase:
650     JSStringJoiner joiner(state, separator, length);
651     RETURN_IF_EXCEPTION(scope, { });
652     for (unsigned i = 0; i < length; ++i) {
653         JSValue element = thisObject->getIndex(&state, i);
654         RETURN_IF_EXCEPTION(scope, { });
655         joiner.append(state, element);
656         RETURN_IF_EXCEPTION(scope, { });
657     }
658     scope.release();
659     return joiner.join(state);
660 }
661
662 EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec)
663 {
664     VM& vm = exec->vm();
665     auto scope = DECLARE_THROW_SCOPE(vm);
666
667     // 1. Let O be ? ToObject(this value).
668     JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
669     ASSERT(!!scope.exception() == !thisObject);
670     if (UNLIKELY(!thisObject))
671         return encodedJSValue();
672
673     StringRecursionChecker checker(exec, thisObject);
674     ASSERT(!scope.exception() || checker.earlyReturnValue());
675     if (JSValue earlyReturnValue = checker.earlyReturnValue())
676         return JSValue::encode(earlyReturnValue);
677
678     // 2. Let len be ? ToLength(? Get(O, "length")).
679     double length = toLength(exec, thisObject);
680     RETURN_IF_EXCEPTION(scope, encodedJSValue());
681
682     // 3. If separator is undefined, let separator be the single-element String ",".
683     JSValue separatorValue = exec->argument(0);
684     if (separatorValue.isUndefined()) {
685         const LChar comma = ',';
686
687         if (UNLIKELY(length > std::numeric_limits<unsigned>::max() || !canUseFastJoin(thisObject))) {
688             uint64_t length64 = static_cast<uint64_t>(length);
689             ASSERT(static_cast<double>(length64) == length);
690             JSString* jsSeparator = jsSingleCharacterString(exec, comma);
691             RETURN_IF_EXCEPTION(scope, encodedJSValue());
692
693             scope.release();
694             return JSValue::encode(slowJoin(*exec, thisObject, jsSeparator, length64));
695         }
696
697         unsigned unsignedLength = static_cast<unsigned>(length);
698         ASSERT(static_cast<double>(unsignedLength) == length);
699
700         scope.release();
701         return JSValue::encode(fastJoin(*exec, thisObject, { &comma, 1 }, unsignedLength));
702     }
703
704     // 4. Let sep be ? ToString(separator).
705     JSString* jsSeparator = separatorValue.toString(exec);
706     RETURN_IF_EXCEPTION(scope, encodedJSValue());
707
708     if (UNLIKELY(length > std::numeric_limits<unsigned>::max() || !canUseFastJoin(thisObject))) {
709         uint64_t length64 = static_cast<uint64_t>(length);
710         ASSERT(static_cast<double>(length64) == length);
711
712         scope.release();
713         return JSValue::encode(slowJoin(*exec, thisObject, jsSeparator, length64));
714     }
715
716     auto viewWithString = jsSeparator->viewWithUnderlyingString(exec);
717     RETURN_IF_EXCEPTION(scope, encodedJSValue());
718
719     scope.release();
720     return JSValue::encode(fastJoin(*exec, thisObject, viewWithString.view, length));
721 }
722
723 EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState* exec)
724 {
725     VM& vm = exec->vm();
726     auto scope = DECLARE_THROW_SCOPE(vm);
727
728     JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
729
730     if (isJSArray(thisValue)) {
731         scope.release();
732         return JSValue::encode(asArray(thisValue)->pop(exec));
733     }
734
735     JSObject* thisObj = thisValue.toObject(exec);
736     ASSERT(!!scope.exception() == !thisObj);
737     if (UNLIKELY(!thisObj))
738         return encodedJSValue();
739     unsigned length = getLength(exec, thisObj);
740     RETURN_IF_EXCEPTION(scope, encodedJSValue());
741
742     if (length == 0) {
743         scope.release();
744         putLength(exec, vm, thisObj, jsNumber(length));
745         return JSValue::encode(jsUndefined());
746     }
747
748     JSValue result = thisObj->get(exec, length - 1);
749     RETURN_IF_EXCEPTION(scope, encodedJSValue());
750     bool success = thisObj->methodTable(vm)->deletePropertyByIndex(thisObj, exec, length - 1);
751     RETURN_IF_EXCEPTION(scope, encodedJSValue());
752     if (UNLIKELY(!success)) {
753         throwTypeError(exec, scope, ASCIILiteral(UnableToDeletePropertyError));
754         return encodedJSValue();
755     }
756     scope.release();
757     putLength(exec, vm, thisObj, jsNumber(length - 1));
758     return JSValue::encode(result);
759 }
760
761 EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState* exec)
762 {
763     VM& vm = exec->vm();
764     auto scope = DECLARE_THROW_SCOPE(vm);
765     JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
766
767     if (isJSArray(thisValue) && exec->argumentCount() == 1) {
768         JSArray* array = asArray(thisValue);
769         scope.release();
770         array->push(exec, exec->uncheckedArgument(0));
771         return JSValue::encode(jsNumber(array->length()));
772     }
773     
774     JSObject* thisObj = thisValue.toObject(exec);
775     ASSERT(!!scope.exception() == !thisObj);
776     if (UNLIKELY(!thisObj))
777         return encodedJSValue();
778     unsigned length = getLength(exec, thisObj);
779     RETURN_IF_EXCEPTION(scope, encodedJSValue());
780
781     for (unsigned n = 0; n < exec->argumentCount(); n++) {
782         // Check for integer overflow; where safe we can do a fast put by index.
783         if (length + n >= length)
784             thisObj->methodTable(vm)->putByIndex(thisObj, exec, length + n, exec->uncheckedArgument(n), true);
785         else {
786             PutPropertySlot slot(thisObj);
787             Identifier propertyName = Identifier::fromString(exec, JSValue(static_cast<int64_t>(length) + static_cast<int64_t>(n)).toWTFString(exec));
788             thisObj->methodTable(vm)->put(thisObj, exec, propertyName, exec->uncheckedArgument(n), slot);
789         }
790         RETURN_IF_EXCEPTION(scope, encodedJSValue());
791     }
792     
793     JSValue newLength(static_cast<int64_t>(length) + static_cast<int64_t>(exec->argumentCount()));
794     scope.release();
795     putLength(exec, vm, thisObj, newLength);
796     return JSValue::encode(newLength);
797 }
798
799 EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState* exec)
800 {
801     VM& vm = exec->vm();
802     auto scope = DECLARE_THROW_SCOPE(vm);
803
804     JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
805     ASSERT(!!scope.exception() == !thisObject);
806     if (UNLIKELY(!thisObject))
807         return encodedJSValue();
808
809     unsigned length = getLength(exec, thisObject);
810     RETURN_IF_EXCEPTION(scope, encodedJSValue());
811
812     switch (thisObject->indexingType()) {
813     case ALL_CONTIGUOUS_INDEXING_TYPES:
814     case ALL_INT32_INDEXING_TYPES: {
815         auto& butterfly = *thisObject->butterfly();
816         if (length > butterfly.publicLength())
817             break;
818         auto data = butterfly.contiguous().data();
819         if (containsHole(data, length) && holesMustForwardToPrototype(*exec, thisObject))
820             break;
821         std::reverse(data, data + length);
822         return JSValue::encode(thisObject);
823     }
824     case ALL_DOUBLE_INDEXING_TYPES: {
825         auto& butterfly = *thisObject->butterfly();
826         if (length > butterfly.publicLength())
827             break;
828         auto data = butterfly.contiguousDouble().data();
829         if (containsHole(data, length) && holesMustForwardToPrototype(*exec, thisObject))
830             break;
831         std::reverse(data, data + length);
832         return JSValue::encode(thisObject);
833     }
834     case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
835         auto& storage = *thisObject->butterfly()->arrayStorage();
836         if (length > storage.vectorLength())
837             break;
838         if (storage.hasHoles() && holesMustForwardToPrototype(*exec, thisObject))
839             break;
840         auto data = storage.vector().data();
841         std::reverse(data, data + length);
842         return JSValue::encode(thisObject);
843     }
844     }
845
846     unsigned middle = length / 2;
847     for (unsigned lower = 0; lower < middle; lower++) {
848         unsigned upper = length - lower - 1;
849         bool lowerExists = thisObject->hasProperty(exec, lower);
850         RETURN_IF_EXCEPTION(scope, encodedJSValue());
851         JSValue lowerValue;
852         if (lowerExists) {
853             lowerValue = thisObject->get(exec, lower);
854             RETURN_IF_EXCEPTION(scope, encodedJSValue());
855         }
856
857         bool upperExists = thisObject->hasProperty(exec, upper);
858         RETURN_IF_EXCEPTION(scope, encodedJSValue());
859         JSValue upperValue;
860         if (upperExists) {
861             upperValue = thisObject->get(exec, upper);
862             RETURN_IF_EXCEPTION(scope, encodedJSValue());
863         }
864
865         if (upperExists) {
866             thisObject->putByIndexInline(exec, lower, upperValue, true);
867             RETURN_IF_EXCEPTION(scope, encodedJSValue());
868         } else {
869             bool success = thisObject->methodTable(vm)->deletePropertyByIndex(thisObject, exec, lower);
870             RETURN_IF_EXCEPTION(scope, encodedJSValue());
871             if (UNLIKELY(!success)) {
872                 throwTypeError(exec, scope, ASCIILiteral(UnableToDeletePropertyError));
873                 return encodedJSValue();
874             }
875         }
876
877         if (lowerExists) {
878             thisObject->putByIndexInline(exec, upper, lowerValue, true);
879             RETURN_IF_EXCEPTION(scope, encodedJSValue());
880         } else {
881             bool success = thisObject->methodTable(vm)->deletePropertyByIndex(thisObject, exec, upper);
882             RETURN_IF_EXCEPTION(scope, encodedJSValue());
883             if (UNLIKELY(!success)) {
884                 throwTypeError(exec, scope, ASCIILiteral(UnableToDeletePropertyError));
885                 return encodedJSValue();
886             }
887         }
888     }
889     return JSValue::encode(thisObject);
890 }
891
892 EncodedJSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState* exec)
893 {
894     VM& vm = exec->vm();
895     auto scope = DECLARE_THROW_SCOPE(vm);
896     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
897     ASSERT(!!scope.exception() == !thisObj);
898     if (UNLIKELY(!thisObj))
899         return encodedJSValue();
900     unsigned length = getLength(exec, thisObj);
901     RETURN_IF_EXCEPTION(scope, encodedJSValue());
902
903     if (length == 0) {
904         scope.release();
905         putLength(exec, vm, thisObj, jsNumber(length));
906         return JSValue::encode(jsUndefined());
907     }
908
909     JSValue result = thisObj->getIndex(exec, 0);
910     RETURN_IF_EXCEPTION(scope, encodedJSValue());
911     shift<JSArray::ShiftCountForShift>(exec, thisObj, 0, 1, 0, length);
912     RETURN_IF_EXCEPTION(scope, encodedJSValue());
913     scope.release();
914     putLength(exec, vm, thisObj, jsNumber(length - 1));
915     return JSValue::encode(result);
916 }
917
918 EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec)
919 {
920     // https://tc39.github.io/ecma262/#sec-array.prototype.slice
921     VM& vm = exec->vm();
922     auto scope = DECLARE_THROW_SCOPE(vm);
923     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
924     ASSERT(!!scope.exception() == !thisObj);
925     if (UNLIKELY(!thisObj))
926         return { };
927     unsigned length = getLength(exec, thisObj);
928     RETURN_IF_EXCEPTION(scope, { });
929
930     unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length);
931     RETURN_IF_EXCEPTION(scope, { });
932     unsigned end = argumentClampedIndexFromStartOrEnd(exec, 1, length, length);
933     RETURN_IF_EXCEPTION(scope, { });
934     if (end < begin)
935         end = begin;
936
937     std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, end - begin);
938     // We can only get an exception if we call some user function.
939     ASSERT(!!scope.exception() == (speciesResult.first == SpeciesConstructResult::Exception));
940     if (UNLIKELY(speciesResult.first == SpeciesConstructResult::Exception))
941         return { };
942
943     bool okToDoFastPath = speciesResult.first == SpeciesConstructResult::FastPath && isJSArray(thisObj) && length == getLength(exec, thisObj);
944     RETURN_IF_EXCEPTION(scope, { });
945     if (LIKELY(okToDoFastPath)) {
946         if (JSArray* result = asArray(thisObj)->fastSlice(*exec, begin, end - begin))
947             return JSValue::encode(result);
948     }
949
950     JSObject* result;
951     if (speciesResult.first == SpeciesConstructResult::CreatedObject)
952         result = speciesResult.second;
953     else {
954         result = constructEmptyArray(exec, nullptr, end - begin);
955         RETURN_IF_EXCEPTION(scope, { });
956     }
957
958     unsigned n = 0;
959     for (unsigned k = begin; k < end; k++, n++) {
960         JSValue v = getProperty(exec, thisObj, k);
961         RETURN_IF_EXCEPTION(scope, { });
962         if (v) {
963             result->putDirectIndex(exec, n, v, 0, PutDirectIndexShouldThrow);
964             RETURN_IF_EXCEPTION(scope, { });
965         }
966     }
967     scope.release();
968     setLength(exec, vm, result, n);
969     return JSValue::encode(result);
970 }
971
972 EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec)
973 {
974     // 15.4.4.12
975
976     VM& vm = exec->vm();
977     auto scope = DECLARE_THROW_SCOPE(vm);
978
979     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
980     ASSERT(!!scope.exception() == !thisObj);
981     if (UNLIKELY(!thisObj))
982         return encodedJSValue();
983     unsigned length = getLength(exec, thisObj);
984     RETURN_IF_EXCEPTION(scope, encodedJSValue());
985
986     if (!exec->argumentCount()) {
987         std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, 0);
988         ASSERT(!!scope.exception() == (speciesResult.first == SpeciesConstructResult::Exception));
989         if (UNLIKELY(speciesResult.first == SpeciesConstructResult::Exception))
990             return encodedJSValue();
991
992         JSObject* result;
993         if (speciesResult.first == SpeciesConstructResult::CreatedObject)
994             result = speciesResult.second;
995         else {
996             result = constructEmptyArray(exec, nullptr);
997             RETURN_IF_EXCEPTION(scope, encodedJSValue());
998         }
999
1000         setLength(exec, vm, result, 0);
1001         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1002         scope.release();
1003         setLength(exec, vm, thisObj, length);
1004         return JSValue::encode(result);
1005     }
1006
1007     unsigned actualStart = argumentClampedIndexFromStartOrEnd(exec, 0, length);
1008     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1009
1010     unsigned actualDeleteCount = length - actualStart;
1011     if (exec->argumentCount() > 1) {
1012         double deleteCount = exec->uncheckedArgument(1).toInteger(exec);
1013         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1014         if (deleteCount < 0)
1015             actualDeleteCount = 0;
1016         else if (deleteCount > length - actualStart)
1017             actualDeleteCount = length - actualStart;
1018         else
1019             actualDeleteCount = static_cast<unsigned>(deleteCount);
1020     }
1021
1022     std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, actualDeleteCount);
1023     ASSERT(!!scope.exception() == (speciesResult.first == SpeciesConstructResult::Exception));
1024     if (speciesResult.first == SpeciesConstructResult::Exception)
1025         return JSValue::encode(jsUndefined());
1026
1027     JSObject* result = nullptr;
1028     bool okToDoFastPath = speciesResult.first == SpeciesConstructResult::FastPath && isJSArray(thisObj) && length == getLength(exec, thisObj);
1029     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1030     if (LIKELY(okToDoFastPath))
1031         result = asArray(thisObj)->fastSlice(*exec, actualStart, actualDeleteCount);
1032
1033     if (!result) {
1034         if (speciesResult.first == SpeciesConstructResult::CreatedObject)
1035             result = speciesResult.second;
1036         else {
1037             result = JSArray::tryCreate(vm, exec->lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), actualDeleteCount);
1038             if (UNLIKELY(!result)) {
1039                 throwOutOfMemoryError(exec, scope);
1040                 return encodedJSValue();
1041             }
1042         }
1043         for (unsigned k = 0; k < actualDeleteCount; ++k) {
1044             JSValue v = getProperty(exec, thisObj, k + actualStart);
1045             RETURN_IF_EXCEPTION(scope, encodedJSValue());
1046             if (UNLIKELY(!v))
1047                 continue;
1048             result->putDirectIndex(exec, k, v, 0, PutDirectIndexShouldThrow);
1049             RETURN_IF_EXCEPTION(scope, encodedJSValue());
1050         }
1051     }
1052
1053     unsigned itemCount = std::max<int>(exec->argumentCount() - 2, 0);
1054     if (itemCount < actualDeleteCount) {
1055         shift<JSArray::ShiftCountForSplice>(exec, thisObj, actualStart, actualDeleteCount, itemCount, length);
1056         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1057     } else if (itemCount > actualDeleteCount) {
1058         unshift<JSArray::ShiftCountForSplice>(exec, thisObj, actualStart, actualDeleteCount, itemCount, length);
1059         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1060     }
1061     for (unsigned k = 0; k < itemCount; ++k) {
1062         thisObj->putByIndexInline(exec, k + actualStart, exec->uncheckedArgument(k + 2), true);
1063         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1064     }
1065     
1066     scope.release();
1067     setLength(exec, vm, thisObj, length - actualDeleteCount + itemCount);
1068     return JSValue::encode(result);
1069 }
1070
1071 EncodedJSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState* exec)
1072 {
1073     VM& vm = exec->vm();
1074     auto scope = DECLARE_THROW_SCOPE(vm);
1075     // 15.4.4.13
1076
1077     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
1078     ASSERT(!!scope.exception() == !thisObj);
1079     if (UNLIKELY(!thisObj))
1080         return encodedJSValue();
1081     unsigned length = getLength(exec, thisObj);
1082     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1083
1084     unsigned nrArgs = exec->argumentCount();
1085     if (nrArgs) {
1086         unshift<JSArray::ShiftCountForShift>(exec, thisObj, 0, 0, nrArgs, length);
1087         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1088     }
1089     for (unsigned k = 0; k < nrArgs; ++k) {
1090         thisObj->putByIndexInline(exec, k, exec->uncheckedArgument(k), true);
1091         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1092     }
1093     JSValue result = jsNumber(length + nrArgs);
1094     scope.release();
1095     putLength(exec, vm, thisObj, result);
1096     return JSValue::encode(result);
1097 }
1098
1099 EncodedJSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState* exec)
1100 {
1101     VM& vm = exec->vm();
1102     auto scope = DECLARE_THROW_SCOPE(vm);
1103
1104     // 15.4.4.14
1105     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
1106     ASSERT(!!scope.exception() == !thisObj);
1107     if (UNLIKELY(!thisObj))
1108         return encodedJSValue();
1109     unsigned length = getLength(exec, thisObj);
1110     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1111
1112     unsigned index = argumentClampedIndexFromStartOrEnd(exec, 1, length);
1113     JSValue searchElement = exec->argument(0);
1114     for (; index < length; ++index) {
1115         JSValue e = getProperty(exec, thisObj, index);
1116         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1117         if (!e)
1118             continue;
1119         if (JSValue::strictEqual(exec, searchElement, e))
1120             return JSValue::encode(jsNumber(index));
1121         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1122     }
1123
1124     return JSValue::encode(jsNumber(-1));
1125 }
1126
1127 EncodedJSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState* exec)
1128 {
1129     VM& vm = exec->vm();
1130     auto scope = DECLARE_THROW_SCOPE(vm);
1131
1132     // 15.4.4.15
1133     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
1134     ASSERT(!!scope.exception() == !thisObj);
1135     if (UNLIKELY(!thisObj))
1136         return encodedJSValue();
1137     unsigned length = getLength(exec, thisObj);
1138     if (UNLIKELY(scope.exception()) || !length)
1139         return JSValue::encode(jsNumber(-1));
1140
1141     unsigned index = length - 1;
1142     if (exec->argumentCount() >= 2) {
1143         JSValue fromValue = exec->uncheckedArgument(1);
1144         double fromDouble = fromValue.toInteger(exec);
1145         if (fromDouble < 0) {
1146             fromDouble += length;
1147             if (fromDouble < 0)
1148                 return JSValue::encode(jsNumber(-1));
1149         }
1150         if (fromDouble < length)
1151             index = static_cast<unsigned>(fromDouble);
1152     }
1153
1154     JSValue searchElement = exec->argument(0);
1155     do {
1156         RELEASE_ASSERT(index < length);
1157         JSValue e = getProperty(exec, thisObj, index);
1158         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1159         if (!e)
1160             continue;
1161         if (JSValue::strictEqual(exec, searchElement, e))
1162             return JSValue::encode(jsNumber(index));
1163         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1164     } while (index--);
1165
1166     return JSValue::encode(jsNumber(-1));
1167 }
1168
1169 static bool moveElements(ExecState* exec, VM& vm, JSArray* target, unsigned targetOffset, JSArray* source, unsigned sourceLength)
1170 {
1171     auto scope = DECLARE_THROW_SCOPE(vm);
1172
1173     if (LIKELY(!hasAnyArrayStorage(source->indexingType()) && !source->structure()->holesMustForwardToPrototype(vm))) {
1174         for (unsigned i = 0; i < sourceLength; ++i) {
1175             JSValue value = source->tryGetIndexQuickly(i);
1176             if (value) {
1177                 target->putDirectIndex(exec, targetOffset + i, value, 0, PutDirectIndexShouldThrow);
1178                 RETURN_IF_EXCEPTION(scope, false);
1179             }
1180         }
1181     } else {
1182         for (unsigned i = 0; i < sourceLength; ++i) {
1183             JSValue value = getProperty(exec, source, i);
1184             RETURN_IF_EXCEPTION(scope, false);
1185             if (value) {
1186                 target->putDirectIndex(exec, targetOffset + i, value, 0, PutDirectIndexShouldThrow);
1187                 RETURN_IF_EXCEPTION(scope, false);
1188             }
1189         }
1190     }
1191     return true;
1192 }
1193
1194 static EncodedJSValue concatAppendOne(ExecState* exec, VM& vm, JSArray* first, JSValue second)
1195 {
1196     auto scope = DECLARE_THROW_SCOPE(vm);
1197
1198     ASSERT(!isJSArray(second));
1199     ASSERT(!shouldUseSlowPut(first->indexingType()));
1200     Butterfly* firstButterfly = first->butterfly();
1201     unsigned firstArraySize = firstButterfly->publicLength();
1202
1203     Checked<unsigned, RecordOverflow> checkedResultSize = firstArraySize;
1204     checkedResultSize += 1;
1205     if (UNLIKELY(checkedResultSize.hasOverflowed())) {
1206         throwOutOfMemoryError(exec, scope);
1207         return encodedJSValue();
1208     }
1209
1210     unsigned resultSize = checkedResultSize.unsafeGet();
1211     IndexingType type = first->mergeIndexingTypeForCopying(indexingTypeForValue(second) | IsArray);
1212     
1213     if (type == NonArray)
1214         type = first->indexingType();
1215
1216     Structure* resultStructure = exec->lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(type);
1217     JSArray* result = JSArray::create(vm, resultStructure, resultSize);
1218     if (UNLIKELY(!result)) {
1219         throwOutOfMemoryError(exec, scope);
1220         return encodedJSValue();
1221     }
1222
1223     bool success = result->appendMemcpy(exec, vm, 0, first);
1224     ASSERT(!success || !scope.exception());
1225     if (!success) {
1226         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1227
1228         bool success = moveElements(exec, vm, result, 0, first, firstArraySize);
1229         ASSERT(!scope.exception() == success);
1230         if (UNLIKELY(!success))
1231             return encodedJSValue();
1232     }
1233
1234     scope.release();
1235     result->putDirectIndex(exec, firstArraySize, second);
1236     return JSValue::encode(result);
1237
1238 }
1239
1240
1241 EncodedJSValue JSC_HOST_CALL arrayProtoPrivateFuncConcatMemcpy(ExecState* exec)
1242 {
1243     ASSERT(exec->argumentCount() == 2);
1244     VM& vm = exec->vm();
1245     auto scope = DECLARE_THROW_SCOPE(vm);
1246
1247     JSArray* firstArray = jsCast<JSArray*>(exec->uncheckedArgument(0));
1248     
1249     // This code assumes that neither array has set Symbol.isConcatSpreadable. If the first array
1250     // has indexed accessors then one of those accessors might change the value of Symbol.isConcatSpreadable
1251     // on the second argument.
1252     if (UNLIKELY(shouldUseSlowPut(firstArray->indexingType())))
1253         return JSValue::encode(jsNull());
1254
1255     // We need to check the species constructor here since checking it in the JS wrapper is too expensive for the non-optimizing tiers.
1256     bool isValid = speciesWatchpointIsValid(exec, firstArray);
1257     if (UNLIKELY(!isValid))
1258         return JSValue::encode(jsNull());
1259
1260     JSValue second = exec->uncheckedArgument(1);
1261     if (!isJSArray(second)) {
1262         scope.release();
1263         return concatAppendOne(exec, vm, firstArray, second);
1264     }
1265
1266     JSArray* secondArray = jsCast<JSArray*>(second);
1267     
1268     Butterfly* firstButterfly = firstArray->butterfly();
1269     Butterfly* secondButterfly = secondArray->butterfly();
1270
1271     unsigned firstArraySize = firstButterfly->publicLength();
1272     unsigned secondArraySize = secondButterfly->publicLength();
1273
1274     Checked<unsigned, RecordOverflow> checkedResultSize = firstArraySize;
1275     checkedResultSize += secondArraySize;
1276
1277     if (UNLIKELY(checkedResultSize.hasOverflowed())) {
1278         throwOutOfMemoryError(exec, scope);
1279         return encodedJSValue();
1280     }
1281
1282     unsigned resultSize = checkedResultSize.unsafeGet();
1283     IndexingType firstType = firstArray->indexingType();
1284     IndexingType secondType = secondArray->indexingType();
1285     IndexingType type = firstArray->mergeIndexingTypeForCopying(secondType);
1286     if (type == NonArray || !firstArray->canFastCopy(vm, secondArray) || resultSize >= MIN_SPARSE_ARRAY_INDEX) {
1287         JSArray* result = constructEmptyArray(exec, nullptr, resultSize);
1288         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1289
1290         bool success = moveElements(exec, vm, result, 0, firstArray, firstArraySize);
1291         ASSERT(!scope.exception() == success);
1292         if (UNLIKELY(!success))
1293             return encodedJSValue();
1294         success = moveElements(exec, vm, result, firstArraySize, secondArray, secondArraySize);
1295         ASSERT(!scope.exception() == success);
1296         if (UNLIKELY(!success))
1297             return encodedJSValue();
1298
1299         return JSValue::encode(result);
1300     }
1301
1302     JSGlobalObject* lexicalGlobalObject = exec->lexicalGlobalObject();
1303     Structure* resultStructure = lexicalGlobalObject->arrayStructureForIndexingTypeDuringAllocation(type);
1304     if (UNLIKELY(hasAnyArrayStorage(resultStructure->indexingType())))
1305         return JSValue::encode(jsNull());
1306
1307     ASSERT(!lexicalGlobalObject->isHavingABadTime());
1308     ObjectInitializationScope initializationScope(vm);
1309     JSArray* result = JSArray::tryCreateUninitializedRestricted(initializationScope, resultStructure, resultSize);
1310     if (UNLIKELY(!result)) {
1311         throwOutOfMemoryError(exec, scope);
1312         return encodedJSValue();
1313     }
1314     
1315     if (type == ArrayWithDouble) {
1316         double* buffer = result->butterfly()->contiguousDouble().data();
1317         memcpy(buffer, firstButterfly->contiguousDouble().data(), sizeof(JSValue) * firstArraySize);
1318         memcpy(buffer + firstArraySize, secondButterfly->contiguousDouble().data(), sizeof(JSValue) * secondArraySize);
1319     } else if (type != ArrayWithUndecided) {
1320         WriteBarrier<Unknown>* buffer = result->butterfly()->contiguous().data();
1321         
1322         auto copy = [&] (unsigned offset, void* source, unsigned size, IndexingType type) {
1323             if (type != ArrayWithUndecided) {
1324                 memcpy(buffer + offset, source, sizeof(JSValue) * size);
1325                 return;
1326             }
1327             
1328             for (unsigned i = size; i--;)
1329                 buffer[i + offset].clear();
1330         };
1331         
1332         copy(0, firstButterfly->contiguous().data(), firstArraySize, firstType);
1333         copy(firstArraySize, secondButterfly->contiguous().data(), secondArraySize, secondType);
1334     }
1335
1336     result->butterfly()->setPublicLength(resultSize);
1337     return JSValue::encode(result);
1338 }
1339
1340 EncodedJSValue JSC_HOST_CALL arrayProtoPrivateFuncAppendMemcpy(ExecState* exec)
1341 {
1342     ASSERT(exec->argumentCount() == 3);
1343
1344     VM& vm = exec->vm();
1345     auto scope = DECLARE_THROW_SCOPE(vm);
1346     JSArray* resultArray = jsCast<JSArray*>(exec->uncheckedArgument(0));
1347     JSArray* otherArray = jsCast<JSArray*>(exec->uncheckedArgument(1));
1348     JSValue startValue = exec->uncheckedArgument(2);
1349     ASSERT(startValue.isAnyInt() && startValue.asAnyInt() >= 0 && startValue.asAnyInt() <= std::numeric_limits<unsigned>::max());
1350     unsigned startIndex = static_cast<unsigned>(startValue.asAnyInt());
1351     bool success = resultArray->appendMemcpy(exec, vm, startIndex, otherArray);
1352     ASSERT(!success || !scope.exception());
1353     if (!success) {
1354         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1355         moveElements(exec, vm, resultArray, startIndex, otherArray, otherArray->length());
1356     }
1357     return JSValue::encode(jsUndefined());
1358 }
1359
1360
1361 // -------------------- ArrayPrototype.constructor Watchpoint ------------------
1362
1363 static bool verbose = false;
1364
1365 class ArrayPrototypeAdaptiveInferredPropertyWatchpoint : public AdaptiveInferredPropertyValueWatchpointBase {
1366 public:
1367     typedef AdaptiveInferredPropertyValueWatchpointBase Base;
1368     ArrayPrototypeAdaptiveInferredPropertyWatchpoint(const ObjectPropertyCondition&, ArrayPrototype*);
1369
1370 private:
1371     void handleFire(const FireDetail&) override;
1372
1373     ArrayPrototype* m_arrayPrototype;
1374 };
1375
1376 void ArrayPrototype::tryInitializeSpeciesWatchpoint(ExecState* exec)
1377 {
1378     VM& vm = exec->vm();
1379
1380     RELEASE_ASSERT(!m_constructorWatchpoint);
1381     RELEASE_ASSERT(!m_constructorSpeciesWatchpoint);
1382
1383     auto scope = DECLARE_THROW_SCOPE(vm);
1384
1385     if (verbose)
1386         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");
1387     // First we need to make sure that the Array.prototype.constructor property points to Array
1388     // and that Array[Symbol.species] is the primordial GetterSetter.
1389
1390     // We only initialize once so flattening the structures does not have any real cost.
1391     Structure* prototypeStructure = this->structure(vm);
1392     if (prototypeStructure->isDictionary())
1393         prototypeStructure = prototypeStructure->flattenDictionaryStructure(vm, this);
1394     RELEASE_ASSERT(!prototypeStructure->isDictionary());
1395
1396     JSGlobalObject* globalObject = this->globalObject();
1397     ArrayConstructor* arrayConstructor = globalObject->arrayConstructor();
1398
1399     auto invalidateWatchpoint = [&] {
1400         globalObject->arraySpeciesWatchpoint().invalidate(vm, StringFireDetail("Was not able to set up array species watchpoint."));
1401     };
1402
1403     PropertySlot constructorSlot(this, PropertySlot::InternalMethodType::VMInquiry);
1404     this->getOwnPropertySlot(this, exec, vm.propertyNames->constructor, constructorSlot);
1405     scope.assertNoException();
1406     if (constructorSlot.slotBase() != this
1407         || !constructorSlot.isCacheableValue()
1408         || constructorSlot.getValue(exec, vm.propertyNames->constructor) != arrayConstructor) {
1409         invalidateWatchpoint();
1410         return;
1411     }
1412
1413     Structure* constructorStructure = arrayConstructor->structure(vm);
1414     if (constructorStructure->isDictionary())
1415         constructorStructure = constructorStructure->flattenDictionaryStructure(vm, arrayConstructor);
1416
1417     PropertySlot speciesSlot(arrayConstructor, PropertySlot::InternalMethodType::VMInquiry);
1418     arrayConstructor->getOwnPropertySlot(arrayConstructor, exec, vm.propertyNames->speciesSymbol, speciesSlot);
1419     scope.assertNoException();
1420     if (speciesSlot.slotBase() != arrayConstructor
1421         || !speciesSlot.isCacheableGetter()
1422         || speciesSlot.getterSetter() != globalObject->speciesGetterSetter()) {
1423         invalidateWatchpoint();
1424         return;
1425     }
1426
1427     // Now we need to setup the watchpoints to make sure these conditions remain valid.
1428     prototypeStructure->startWatchingPropertyForReplacements(vm, constructorSlot.cachedOffset());
1429     constructorStructure->startWatchingPropertyForReplacements(vm, speciesSlot.cachedOffset());
1430
1431     ObjectPropertyCondition constructorCondition = ObjectPropertyCondition::equivalence(vm, this, this, vm.propertyNames->constructor.impl(), arrayConstructor);
1432     ObjectPropertyCondition speciesCondition = ObjectPropertyCondition::equivalence(vm, this, arrayConstructor, vm.propertyNames->speciesSymbol.impl(), globalObject->speciesGetterSetter());
1433
1434     if (!constructorCondition.isWatchable() || !speciesCondition.isWatchable()) {
1435         invalidateWatchpoint();
1436         return;
1437     }
1438
1439     m_constructorWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(constructorCondition, this);
1440     m_constructorWatchpoint->install();
1441
1442     m_constructorSpeciesWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(speciesCondition, this);
1443     m_constructorSpeciesWatchpoint->install();
1444
1445     // We only watch this from the DFG, and the DFG makes sure to only start watching if the watchpoint is in the IsWatched state.
1446     RELEASE_ASSERT(!globalObject->arraySpeciesWatchpoint().isBeingWatched()); 
1447     globalObject->arraySpeciesWatchpoint().touch(vm, "Set up array species watchpoint.");
1448 }
1449
1450 ArrayPrototypeAdaptiveInferredPropertyWatchpoint::ArrayPrototypeAdaptiveInferredPropertyWatchpoint(const ObjectPropertyCondition& key, ArrayPrototype* prototype)
1451     : Base(key)
1452     , m_arrayPrototype(prototype)
1453 {
1454 }
1455
1456 void ArrayPrototypeAdaptiveInferredPropertyWatchpoint::handleFire(const FireDetail& detail)
1457 {
1458     StringPrintStream out;
1459     out.print("ArrayPrototype adaption of ", key(), " failed: ", detail);
1460
1461     StringFireDetail stringDetail(out.toCString().data());
1462
1463     if (verbose)
1464         WTF::dataLog(stringDetail, "\n");
1465
1466     JSGlobalObject* globalObject = m_arrayPrototype->globalObject();
1467     globalObject->arraySpeciesWatchpoint().fireAll(globalObject->vm(), stringDetail);
1468 }
1469
1470 } // namespace JSC