[DFG] Support ArrayPush with multiple args
[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(), static_cast<unsigned>(PropertyAttribute::DontEnum));
88     putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().valuesPublicName(), globalObject->arrayProtoValuesFunction(), static_cast<unsigned>(PropertyAttribute::DontEnum));
89     putDirectWithoutTransition(vm, vm.propertyNames->iteratorSymbol, globalObject->arrayProtoValuesFunction(), static_cast<unsigned>(PropertyAttribute::DontEnum));
90
91     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->toLocaleString, arrayProtoFuncToLocaleString, static_cast<unsigned>(PropertyAttribute::DontEnum), 0);
92     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("concat", arrayPrototypeConcatCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
93     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("fill", arrayPrototypeFillCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
94     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->join, arrayProtoFuncJoin, static_cast<unsigned>(PropertyAttribute::DontEnum), 1);
95     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("pop", arrayProtoFuncPop, static_cast<unsigned>(PropertyAttribute::DontEnum), 0, ArrayPopIntrinsic);
96     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().pushPublicName(), arrayProtoFuncPush, static_cast<unsigned>(PropertyAttribute::DontEnum), 1, ArrayPushIntrinsic);
97     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().pushPrivateName(), arrayProtoFuncPush, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly, 1, ArrayPushIntrinsic);
98     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("reverse", arrayProtoFuncReverse, static_cast<unsigned>(PropertyAttribute::DontEnum), 0);
99     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().shiftPublicName(), arrayProtoFuncShift, static_cast<unsigned>(PropertyAttribute::DontEnum), 0);
100     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().shiftPrivateName(), arrayProtoFuncShift, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly, 0);
101     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->slice, arrayProtoFuncSlice, static_cast<unsigned>(PropertyAttribute::DontEnum), 2, ArraySliceIntrinsic);
102     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("sort", arrayPrototypeSortCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
103     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("splice", arrayProtoFuncSplice, static_cast<unsigned>(PropertyAttribute::DontEnum), 2);
104     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("unshift", arrayProtoFuncUnShift, static_cast<unsigned>(PropertyAttribute::DontEnum), 1);
105     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("every", arrayPrototypeEveryCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
106     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("forEach", arrayPrototypeForEachCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
107     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("some", arrayPrototypeSomeCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
108     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("indexOf", arrayProtoFuncIndexOf, static_cast<unsigned>(PropertyAttribute::DontEnum), 1, ArrayIndexOfIntrinsic);
109     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("lastIndexOf", arrayProtoFuncLastIndexOf, static_cast<unsigned>(PropertyAttribute::DontEnum), 1);
110     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("filter", arrayPrototypeFilterCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
111     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("reduce", arrayPrototypeReduceCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
112     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("reduceRight", arrayPrototypeReduceRightCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
113     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("map", arrayPrototypeMapCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
114     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().entriesPublicName(), arrayPrototypeEntriesCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
115     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().keysPublicName(), arrayPrototypeKeysCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
116     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("find", arrayPrototypeFindCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
117     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("findIndex", arrayPrototypeFindIndexCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
118     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("includes", arrayPrototypeIncludesCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
119     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("copyWithin", arrayPrototypeCopyWithinCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
120
121     putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().entriesPrivateName(), getDirect(vm, vm.propertyNames->builtinNames().entriesPublicName()), static_cast<unsigned>(PropertyAttribute::ReadOnly));
122     putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().forEachPrivateName(), getDirect(vm, vm.propertyNames->builtinNames().forEachPublicName()), static_cast<unsigned>(PropertyAttribute::ReadOnly));
123     putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().keysPrivateName(), getDirect(vm, vm.propertyNames->builtinNames().keysPublicName()), static_cast<unsigned>(PropertyAttribute::ReadOnly));
124     putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().valuesPrivateName(), globalObject->arrayProtoValuesFunction(), static_cast<unsigned>(PropertyAttribute::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, PropertyAttribute::DontEnum | PropertyAttribute::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     EXCEPTION_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         scope.assertNoException();
233         if (LIKELY(isValid))
234             return std::make_pair(SpeciesConstructResult::FastPath, nullptr);
235
236         constructor = thisObject->get(exec, vm.propertyNames->constructor);
237         RETURN_IF_EXCEPTION(scope, exceptionResult());
238         if (constructor.isConstructor()) {
239             JSObject* constructorObject = jsCast<JSObject*>(constructor);
240             if (exec->lexicalGlobalObject() != constructorObject->globalObject())
241                 return std::make_pair(SpeciesConstructResult::FastPath, nullptr);;
242         }
243         if (constructor.isObject()) {
244             constructor = constructor.get(exec, vm.propertyNames->speciesSymbol);
245             RETURN_IF_EXCEPTION(scope, exceptionResult());
246             if (constructor.isNull())
247                 return std::make_pair(SpeciesConstructResult::FastPath, nullptr);;
248         }
249     } else
250         RETURN_IF_EXCEPTION(scope, exceptionResult());
251
252     if (constructor.isUndefined())
253         return std::make_pair(SpeciesConstructResult::FastPath, nullptr);
254
255     MarkedArgumentBuffer args;
256     args.append(jsNumber(length));
257     JSObject* newObject = construct(exec, constructor, args, "Species construction did not get a valid constructor");
258     RETURN_IF_EXCEPTION(scope, exceptionResult());
259     return std::make_pair(SpeciesConstructResult::CreatedObject, newObject);
260 }
261
262 static inline unsigned argumentClampedIndexFromStartOrEnd(ExecState* exec, int argument, unsigned length, unsigned undefinedValue = 0)
263 {
264     JSValue value = exec->argument(argument);
265     if (value.isUndefined())
266         return undefinedValue;
267
268     double indexDouble = value.toInteger(exec);
269     if (indexDouble < 0) {
270         indexDouble += length;
271         return indexDouble < 0 ? 0 : static_cast<unsigned>(indexDouble);
272     }
273     return indexDouble > length ? length : static_cast<unsigned>(indexDouble);
274 }
275
276 // The shift/unshift function implement the shift/unshift behaviour required
277 // by the corresponding array prototype methods, and by splice. In both cases,
278 // the methods are operating an an array or array like object.
279 //
280 //  header  currentCount  (remainder)
281 // [------][------------][-----------]
282 //  header  resultCount  (remainder)
283 // [------][-----------][-----------]
284 //
285 // The set of properties in the range 'header' must be unchanged. The set of
286 // properties in the range 'remainder' (where remainder = length - header -
287 // currentCount) will be shifted to the left or right as appropriate; in the
288 // case of shift this must be removing values, in the case of unshift this
289 // must be introducing new values.
290
291 template<JSArray::ShiftCountMode shiftCountMode>
292 void shift(ExecState* exec, JSObject* thisObj, unsigned header, unsigned currentCount, unsigned resultCount, unsigned length)
293 {
294     VM& vm = exec->vm();
295     auto scope = DECLARE_THROW_SCOPE(vm);
296
297     RELEASE_ASSERT(currentCount > resultCount);
298     unsigned count = currentCount - resultCount;
299
300     RELEASE_ASSERT(header <= length);
301     RELEASE_ASSERT(currentCount <= (length - header));
302
303     if (isJSArray(thisObj)) {
304         JSArray* array = asArray(thisObj);
305         if (array->length() == length && array->shiftCount<shiftCountMode>(exec, header, count))
306             return;
307     }
308
309     for (unsigned k = header; k < length - currentCount; ++k) {
310         unsigned from = k + currentCount;
311         unsigned to = k + resultCount;
312         JSValue value = getProperty(exec, thisObj, from);
313         RETURN_IF_EXCEPTION(scope, void());
314         if (value) {
315             thisObj->putByIndexInline(exec, to, value, true);
316             RETURN_IF_EXCEPTION(scope, void());
317         } else {
318             bool success = thisObj->methodTable(vm)->deletePropertyByIndex(thisObj, exec, to);
319             RETURN_IF_EXCEPTION(scope, void());
320             if (!success) {
321                 throwTypeError(exec, scope, ASCIILiteral(UnableToDeletePropertyError));
322                 return;
323             }
324         }
325     }
326     for (unsigned k = length; k > length - count; --k) {
327         bool success = thisObj->methodTable(vm)->deletePropertyByIndex(thisObj, exec, k - 1);
328         RETURN_IF_EXCEPTION(scope, void());
329         if (!success) {
330             throwTypeError(exec, scope, ASCIILiteral(UnableToDeletePropertyError));
331             return;
332         }
333     }
334 }
335
336 template<JSArray::ShiftCountMode shiftCountMode>
337 void unshift(ExecState* exec, JSObject* thisObj, unsigned header, unsigned currentCount, unsigned resultCount, unsigned length)
338 {
339     VM& vm = exec->vm();
340     auto scope = DECLARE_THROW_SCOPE(vm);
341
342     RELEASE_ASSERT(resultCount > currentCount);
343     unsigned count = resultCount - currentCount;
344
345     RELEASE_ASSERT(header <= length);
346     RELEASE_ASSERT(currentCount <= (length - header));
347
348     // Guard against overflow.
349     if (count > (UINT_MAX - length)) {
350         throwOutOfMemoryError(exec, scope);
351         return;
352     }
353
354     if (isJSArray(thisObj)) {
355         JSArray* array = asArray(thisObj);
356         if (array->length() == length) {
357             bool handled = array->unshiftCount<shiftCountMode>(exec, header, count);
358             EXCEPTION_ASSERT(!scope.exception() || handled);
359             if (handled)
360                 return;
361         }
362     }
363
364     for (unsigned k = length - currentCount; k > header; --k) {
365         unsigned from = k + currentCount - 1;
366         unsigned to = k + resultCount - 1;
367         JSValue value = getProperty(exec, thisObj, from);
368         RETURN_IF_EXCEPTION(scope, void());
369         if (value) {
370             thisObj->putByIndexInline(exec, to, value, true);
371             RETURN_IF_EXCEPTION(scope, void());
372         } else {
373             bool success = thisObj->methodTable(vm)->deletePropertyByIndex(thisObj, exec, to);
374             RETURN_IF_EXCEPTION(scope, void());
375             if (UNLIKELY(!success)) {
376                 throwTypeError(exec, scope, ASCIILiteral(UnableToDeletePropertyError));
377                 return;
378             }
379         }
380     }
381 }
382
383 EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec)
384 {
385     VM& vm = exec->vm();
386     auto scope = DECLARE_THROW_SCOPE(vm);
387     JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
388
389     // 1. Let array be the result of calling ToObject on the this value.
390     JSObject* thisObject = thisValue.toObject(exec);
391     RETURN_IF_EXCEPTION(scope, encodedJSValue());
392     
393     // 2. Let func be the result of calling the [[Get]] internal method of array with argument "join".
394     JSValue function = JSValue(thisObject).get(exec, vm.propertyNames->join);
395     RETURN_IF_EXCEPTION(scope, encodedJSValue());
396
397     // 3. If IsCallable(func) is false, then let func be the standard built-in method Object.prototype.toString (15.2.4.2).
398     bool customJoinCase = false;
399     if (!function.isCell())
400         customJoinCase = true;
401     CallData callData;
402     CallType callType = getCallData(function, callData);
403     if (callType == CallType::None)
404         customJoinCase = true;
405
406     if (UNLIKELY(customJoinCase)) {
407         scope.release();
408         return JSValue::encode(jsMakeNontrivialString(exec, "[object ", thisObject->methodTable(vm)->className(thisObject), "]"));
409     }
410
411     // 4. Return the result of calling the [[Call]] internal method of func providing array as the this value and an empty arguments list.
412     if (!isJSArray(thisObject) || callType != CallType::Host || callData.native.function != arrayProtoFuncJoin) {
413         scope.release();
414         return JSValue::encode(call(exec, function, callType, callData, thisObject, *vm.emptyList));
415     }
416
417     ASSERT(isJSArray(thisValue));
418     JSArray* thisArray = asArray(thisValue);
419
420     unsigned length = thisArray->length();
421
422     StringRecursionChecker checker(exec, thisArray);
423     EXCEPTION_ASSERT(!scope.exception() || checker.earlyReturnValue());
424     if (JSValue earlyReturnValue = checker.earlyReturnValue())
425         return JSValue::encode(earlyReturnValue);
426
427     JSStringJoiner joiner(*exec, ',', length);
428     RETURN_IF_EXCEPTION(scope, encodedJSValue());
429
430     for (unsigned i = 0; i < length; ++i) {
431         JSValue element = thisArray->tryGetIndexQuickly(i);
432         if (!element) {
433             element = thisArray->get(exec, i);
434             RETURN_IF_EXCEPTION(scope, encodedJSValue());
435         }
436         joiner.append(*exec, element);
437         RETURN_IF_EXCEPTION(scope, encodedJSValue());
438     }
439
440     scope.release();
441     return JSValue::encode(joiner.join(*exec));
442 }
443
444 EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec)
445 {
446     VM& vm = exec->vm();
447     auto scope = DECLARE_THROW_SCOPE(vm);
448     JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
449
450     JSObject* thisObject = thisValue.toObject(exec);
451     RETURN_IF_EXCEPTION(scope, encodedJSValue());
452     unsigned length = toLength(exec, thisObject);
453     RETURN_IF_EXCEPTION(scope, encodedJSValue());
454
455     StringRecursionChecker checker(exec, thisObject);
456     EXCEPTION_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, *vm.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     EXCEPTION_ASSERT(!!scope.exception() == !thisObject);
670     if (UNLIKELY(!thisObject))
671         return encodedJSValue();
672
673     StringRecursionChecker checker(exec, thisObject);
674     EXCEPTION_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     EXCEPTION_ASSERT(!!scope.exception() == !thisObj);
737     if (UNLIKELY(!thisObj))
738         return encodedJSValue();
739     unsigned length = toLength(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 (LIKELY(isJSArray(thisValue) && exec->argumentCount() == 1)) {
768         JSArray* array = asArray(thisValue);
769         scope.release();
770         array->pushInline(exec, exec->uncheckedArgument(0));
771         return JSValue::encode(jsNumber(array->length()));
772     }
773     
774     JSObject* thisObj = thisValue.toObject(exec);
775     EXCEPTION_ASSERT(!!scope.exception() == !thisObj);
776     if (UNLIKELY(!thisObj))
777         return encodedJSValue();
778     unsigned length = toLength(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     EXCEPTION_ASSERT(!!scope.exception() == !thisObject);
806     if (UNLIKELY(!thisObject))
807         return encodedJSValue();
808
809     unsigned length = toLength(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     EXCEPTION_ASSERT(!!scope.exception() == !thisObj);
898     if (UNLIKELY(!thisObj))
899         return encodedJSValue();
900     unsigned length = toLength(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     EXCEPTION_ASSERT(!!scope.exception() == !thisObj);
925     if (UNLIKELY(!thisObj))
926         return { };
927     unsigned length = toLength(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     EXCEPTION_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 == toLength(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     EXCEPTION_ASSERT(!!scope.exception() == !thisObj);
981     if (UNLIKELY(!thisObj))
982         return encodedJSValue();
983     unsigned length = toLength(exec, thisObj);
984     RETURN_IF_EXCEPTION(scope, encodedJSValue());
985
986     if (!exec->argumentCount()) {
987         std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, 0);
988         EXCEPTION_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     EXCEPTION_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 == toLength(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     EXCEPTION_ASSERT(!!scope.exception() == !thisObj);
1079     if (UNLIKELY(!thisObj))
1080         return encodedJSValue();
1081     double doubleLength = toLength(exec, thisObj);
1082     unsigned length = doubleLength;
1083     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1084
1085     unsigned nrArgs = exec->argumentCount();
1086     if (nrArgs) {
1087         if (UNLIKELY(doubleLength + static_cast<double>(nrArgs) > maxSafeInteger()))
1088             return throwVMTypeError(exec, scope, ASCIILiteral("Cannot shift to offset greater than (2 ** 53) - 1"));
1089         unshift<JSArray::ShiftCountForShift>(exec, thisObj, 0, 0, nrArgs, length);
1090         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1091     }
1092     for (unsigned k = 0; k < nrArgs; ++k) {
1093         thisObj->putByIndexInline(exec, k, exec->uncheckedArgument(k), true);
1094         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1095     }
1096     JSValue result = jsNumber(length + nrArgs);
1097     scope.release();
1098     putLength(exec, vm, thisObj, result);
1099     return JSValue::encode(result);
1100 }
1101
1102 EncodedJSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState* exec)
1103 {
1104     VM& vm = exec->vm();
1105     auto scope = DECLARE_THROW_SCOPE(vm);
1106
1107     // 15.4.4.14
1108     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
1109     EXCEPTION_ASSERT(!!scope.exception() == !thisObj);
1110     if (UNLIKELY(!thisObj))
1111         return encodedJSValue();
1112     unsigned length = toLength(exec, thisObj);
1113     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1114
1115     unsigned index = argumentClampedIndexFromStartOrEnd(exec, 1, length);
1116     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1117     JSValue searchElement = exec->argument(0);
1118     for (; index < length; ++index) {
1119         JSValue e = getProperty(exec, thisObj, index);
1120         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1121         if (!e)
1122             continue;
1123         bool isEqual = JSValue::strictEqual(exec, searchElement, e);
1124         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1125         if (isEqual)
1126             return JSValue::encode(jsNumber(index));
1127     }
1128
1129     return JSValue::encode(jsNumber(-1));
1130 }
1131
1132 EncodedJSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState* exec)
1133 {
1134     VM& vm = exec->vm();
1135     auto scope = DECLARE_THROW_SCOPE(vm);
1136
1137     // 15.4.4.15
1138     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
1139     EXCEPTION_ASSERT(!!scope.exception() == !thisObj);
1140     if (UNLIKELY(!thisObj))
1141         return encodedJSValue();
1142     unsigned length = toLength(exec, thisObj);
1143     if (UNLIKELY(scope.exception()) || !length)
1144         return JSValue::encode(jsNumber(-1));
1145
1146     unsigned index = length - 1;
1147     if (exec->argumentCount() >= 2) {
1148         JSValue fromValue = exec->uncheckedArgument(1);
1149         double fromDouble = fromValue.toInteger(exec);
1150         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1151         if (fromDouble < 0) {
1152             fromDouble += length;
1153             if (fromDouble < 0)
1154                 return JSValue::encode(jsNumber(-1));
1155         }
1156         if (fromDouble < length)
1157             index = static_cast<unsigned>(fromDouble);
1158     }
1159
1160     JSValue searchElement = exec->argument(0);
1161     do {
1162         RELEASE_ASSERT(index < length);
1163         JSValue e = getProperty(exec, thisObj, index);
1164         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1165         if (!e)
1166             continue;
1167         if (JSValue::strictEqual(exec, searchElement, e))
1168             return JSValue::encode(jsNumber(index));
1169         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1170     } while (index--);
1171
1172     return JSValue::encode(jsNumber(-1));
1173 }
1174
1175 static bool moveElements(ExecState* exec, VM& vm, JSArray* target, unsigned targetOffset, JSArray* source, unsigned sourceLength)
1176 {
1177     auto scope = DECLARE_THROW_SCOPE(vm);
1178
1179     if (LIKELY(!hasAnyArrayStorage(source->indexingType()) && !source->structure()->holesMustForwardToPrototype(vm))) {
1180         for (unsigned i = 0; i < sourceLength; ++i) {
1181             JSValue value = source->tryGetIndexQuickly(i);
1182             if (value) {
1183                 target->putDirectIndex(exec, targetOffset + i, value, 0, PutDirectIndexShouldThrow);
1184                 RETURN_IF_EXCEPTION(scope, false);
1185             }
1186         }
1187     } else {
1188         for (unsigned i = 0; i < sourceLength; ++i) {
1189             JSValue value = getProperty(exec, source, i);
1190             RETURN_IF_EXCEPTION(scope, false);
1191             if (value) {
1192                 target->putDirectIndex(exec, targetOffset + i, value, 0, PutDirectIndexShouldThrow);
1193                 RETURN_IF_EXCEPTION(scope, false);
1194             }
1195         }
1196     }
1197     return true;
1198 }
1199
1200 static EncodedJSValue concatAppendOne(ExecState* exec, VM& vm, JSArray* first, JSValue second)
1201 {
1202     auto scope = DECLARE_THROW_SCOPE(vm);
1203
1204     ASSERT(!isJSArray(second));
1205     ASSERT(!shouldUseSlowPut(first->indexingType()));
1206     Butterfly* firstButterfly = first->butterfly();
1207     unsigned firstArraySize = firstButterfly->publicLength();
1208
1209     Checked<unsigned, RecordOverflow> checkedResultSize = firstArraySize;
1210     checkedResultSize += 1;
1211     if (UNLIKELY(checkedResultSize.hasOverflowed())) {
1212         throwOutOfMemoryError(exec, scope);
1213         return encodedJSValue();
1214     }
1215
1216     unsigned resultSize = checkedResultSize.unsafeGet();
1217     IndexingType type = first->mergeIndexingTypeForCopying(indexingTypeForValue(second) | IsArray);
1218     
1219     if (type == NonArray)
1220         type = first->indexingType();
1221
1222     Structure* resultStructure = exec->lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(type);
1223     JSArray* result = JSArray::create(vm, resultStructure, resultSize);
1224     if (UNLIKELY(!result)) {
1225         throwOutOfMemoryError(exec, scope);
1226         return encodedJSValue();
1227     }
1228
1229     bool success = result->appendMemcpy(exec, vm, 0, first);
1230     EXCEPTION_ASSERT(!scope.exception() || !success);
1231     if (!success) {
1232         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1233
1234         bool success = moveElements(exec, vm, result, 0, first, firstArraySize);
1235         EXCEPTION_ASSERT(!scope.exception() == success);
1236         if (UNLIKELY(!success))
1237             return encodedJSValue();
1238     }
1239
1240     scope.release();
1241     result->putDirectIndex(exec, firstArraySize, second);
1242     return JSValue::encode(result);
1243
1244 }
1245
1246
1247 EncodedJSValue JSC_HOST_CALL arrayProtoPrivateFuncConcatMemcpy(ExecState* exec)
1248 {
1249     ASSERT(exec->argumentCount() == 2);
1250     VM& vm = exec->vm();
1251     auto scope = DECLARE_THROW_SCOPE(vm);
1252
1253     JSArray* firstArray = jsCast<JSArray*>(exec->uncheckedArgument(0));
1254     
1255     // This code assumes that neither array has set Symbol.isConcatSpreadable. If the first array
1256     // has indexed accessors then one of those accessors might change the value of Symbol.isConcatSpreadable
1257     // on the second argument.
1258     if (UNLIKELY(shouldUseSlowPut(firstArray->indexingType())))
1259         return JSValue::encode(jsNull());
1260
1261     // We need to check the species constructor here since checking it in the JS wrapper is too expensive for the non-optimizing tiers.
1262     bool isValid = speciesWatchpointIsValid(exec, firstArray);
1263     scope.assertNoException();
1264     if (UNLIKELY(!isValid))
1265         return JSValue::encode(jsNull());
1266
1267     JSValue second = exec->uncheckedArgument(1);
1268     if (!isJSArray(second)) {
1269         scope.release();
1270         return concatAppendOne(exec, vm, firstArray, second);
1271     }
1272
1273     JSArray* secondArray = jsCast<JSArray*>(second);
1274     
1275     Butterfly* firstButterfly = firstArray->butterfly();
1276     Butterfly* secondButterfly = secondArray->butterfly();
1277
1278     unsigned firstArraySize = firstButterfly->publicLength();
1279     unsigned secondArraySize = secondButterfly->publicLength();
1280
1281     Checked<unsigned, RecordOverflow> checkedResultSize = firstArraySize;
1282     checkedResultSize += secondArraySize;
1283
1284     if (UNLIKELY(checkedResultSize.hasOverflowed())) {
1285         throwOutOfMemoryError(exec, scope);
1286         return encodedJSValue();
1287     }
1288
1289     unsigned resultSize = checkedResultSize.unsafeGet();
1290     IndexingType firstType = firstArray->indexingType();
1291     IndexingType secondType = secondArray->indexingType();
1292     IndexingType type = firstArray->mergeIndexingTypeForCopying(secondType);
1293     if (type == NonArray || !firstArray->canFastCopy(vm, secondArray) || resultSize >= MIN_SPARSE_ARRAY_INDEX) {
1294         JSArray* result = constructEmptyArray(exec, nullptr, resultSize);
1295         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1296
1297         bool success = moveElements(exec, vm, result, 0, firstArray, firstArraySize);
1298         EXCEPTION_ASSERT(!scope.exception() == success);
1299         if (UNLIKELY(!success))
1300             return encodedJSValue();
1301         success = moveElements(exec, vm, result, firstArraySize, secondArray, secondArraySize);
1302         EXCEPTION_ASSERT(!scope.exception() == success);
1303         if (UNLIKELY(!success))
1304             return encodedJSValue();
1305
1306         return JSValue::encode(result);
1307     }
1308
1309     JSGlobalObject* lexicalGlobalObject = exec->lexicalGlobalObject();
1310     Structure* resultStructure = lexicalGlobalObject->arrayStructureForIndexingTypeDuringAllocation(type);
1311     if (UNLIKELY(hasAnyArrayStorage(resultStructure->indexingType())))
1312         return JSValue::encode(jsNull());
1313
1314     ASSERT(!lexicalGlobalObject->isHavingABadTime());
1315     ObjectInitializationScope initializationScope(vm);
1316     JSArray* result = JSArray::tryCreateUninitializedRestricted(initializationScope, resultStructure, resultSize);
1317     if (UNLIKELY(!result)) {
1318         throwOutOfMemoryError(exec, scope);
1319         return encodedJSValue();
1320     }
1321     
1322     if (type == ArrayWithDouble) {
1323         double* buffer = result->butterfly()->contiguousDouble().data();
1324         memcpy(buffer, firstButterfly->contiguousDouble().data(), sizeof(JSValue) * firstArraySize);
1325         memcpy(buffer + firstArraySize, secondButterfly->contiguousDouble().data(), sizeof(JSValue) * secondArraySize);
1326     } else if (type != ArrayWithUndecided) {
1327         WriteBarrier<Unknown>* buffer = result->butterfly()->contiguous().data();
1328         
1329         auto copy = [&] (unsigned offset, void* source, unsigned size, IndexingType type) {
1330             if (type != ArrayWithUndecided) {
1331                 memcpy(buffer + offset, source, sizeof(JSValue) * size);
1332                 return;
1333             }
1334             
1335             for (unsigned i = size; i--;)
1336                 buffer[i + offset].clear();
1337         };
1338         
1339         copy(0, firstButterfly->contiguous().data(), firstArraySize, firstType);
1340         copy(firstArraySize, secondButterfly->contiguous().data(), secondArraySize, secondType);
1341     }
1342
1343     result->butterfly()->setPublicLength(resultSize);
1344     return JSValue::encode(result);
1345 }
1346
1347 EncodedJSValue JSC_HOST_CALL arrayProtoPrivateFuncAppendMemcpy(ExecState* exec)
1348 {
1349     ASSERT(exec->argumentCount() == 3);
1350
1351     VM& vm = exec->vm();
1352     auto scope = DECLARE_THROW_SCOPE(vm);
1353     JSArray* resultArray = jsCast<JSArray*>(exec->uncheckedArgument(0));
1354     JSArray* otherArray = jsCast<JSArray*>(exec->uncheckedArgument(1));
1355     JSValue startValue = exec->uncheckedArgument(2);
1356     ASSERT(startValue.isAnyInt() && startValue.asAnyInt() >= 0 && startValue.asAnyInt() <= std::numeric_limits<unsigned>::max());
1357     unsigned startIndex = static_cast<unsigned>(startValue.asAnyInt());
1358     bool success = resultArray->appendMemcpy(exec, vm, startIndex, otherArray);
1359     EXCEPTION_ASSERT(!scope.exception() || !success);
1360     if (success)
1361         return JSValue::encode(jsUndefined());
1362     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1363     scope.release();
1364     moveElements(exec, vm, resultArray, startIndex, otherArray, otherArray->length());
1365     return JSValue::encode(jsUndefined());
1366 }
1367
1368
1369 // -------------------- ArrayPrototype.constructor Watchpoint ------------------
1370
1371 namespace ArrayPrototypeInternal {
1372 static bool verbose = false;
1373 }
1374
1375 class ArrayPrototypeAdaptiveInferredPropertyWatchpoint : public AdaptiveInferredPropertyValueWatchpointBase {
1376 public:
1377     typedef AdaptiveInferredPropertyValueWatchpointBase Base;
1378     ArrayPrototypeAdaptiveInferredPropertyWatchpoint(const ObjectPropertyCondition&, ArrayPrototype*);
1379
1380 private:
1381     void handleFire(const FireDetail&) override;
1382
1383     ArrayPrototype* m_arrayPrototype;
1384 };
1385
1386 void ArrayPrototype::tryInitializeSpeciesWatchpoint(ExecState* exec)
1387 {
1388     VM& vm = exec->vm();
1389
1390     RELEASE_ASSERT(!m_constructorWatchpoint);
1391     RELEASE_ASSERT(!m_constructorSpeciesWatchpoint);
1392
1393     auto scope = DECLARE_THROW_SCOPE(vm);
1394
1395     if (ArrayPrototypeInternal::verbose)
1396         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");
1397     // First we need to make sure that the Array.prototype.constructor property points to Array
1398     // and that Array[Symbol.species] is the primordial GetterSetter.
1399
1400     // We only initialize once so flattening the structures does not have any real cost.
1401     Structure* prototypeStructure = this->structure(vm);
1402     if (prototypeStructure->isDictionary())
1403         prototypeStructure = prototypeStructure->flattenDictionaryStructure(vm, this);
1404     RELEASE_ASSERT(!prototypeStructure->isDictionary());
1405
1406     JSGlobalObject* globalObject = this->globalObject();
1407     ArrayConstructor* arrayConstructor = globalObject->arrayConstructor();
1408
1409     auto invalidateWatchpoint = [&] {
1410         globalObject->arraySpeciesWatchpoint().invalidate(vm, StringFireDetail("Was not able to set up array species watchpoint."));
1411     };
1412
1413     PropertySlot constructorSlot(this, PropertySlot::InternalMethodType::VMInquiry);
1414     this->getOwnPropertySlot(this, exec, vm.propertyNames->constructor, constructorSlot);
1415     scope.assertNoException();
1416     if (constructorSlot.slotBase() != this
1417         || !constructorSlot.isCacheableValue()
1418         || constructorSlot.getValue(exec, vm.propertyNames->constructor) != arrayConstructor) {
1419         invalidateWatchpoint();
1420         return;
1421     }
1422
1423     Structure* constructorStructure = arrayConstructor->structure(vm);
1424     if (constructorStructure->isDictionary())
1425         constructorStructure = constructorStructure->flattenDictionaryStructure(vm, arrayConstructor);
1426
1427     PropertySlot speciesSlot(arrayConstructor, PropertySlot::InternalMethodType::VMInquiry);
1428     arrayConstructor->getOwnPropertySlot(arrayConstructor, exec, vm.propertyNames->speciesSymbol, speciesSlot);
1429     scope.assertNoException();
1430     if (speciesSlot.slotBase() != arrayConstructor
1431         || !speciesSlot.isCacheableGetter()
1432         || speciesSlot.getterSetter() != globalObject->speciesGetterSetter()) {
1433         invalidateWatchpoint();
1434         return;
1435     }
1436
1437     // Now we need to setup the watchpoints to make sure these conditions remain valid.
1438     prototypeStructure->startWatchingPropertyForReplacements(vm, constructorSlot.cachedOffset());
1439     constructorStructure->startWatchingPropertyForReplacements(vm, speciesSlot.cachedOffset());
1440
1441     ObjectPropertyCondition constructorCondition = ObjectPropertyCondition::equivalence(vm, this, this, vm.propertyNames->constructor.impl(), arrayConstructor);
1442     ObjectPropertyCondition speciesCondition = ObjectPropertyCondition::equivalence(vm, this, arrayConstructor, vm.propertyNames->speciesSymbol.impl(), globalObject->speciesGetterSetter());
1443
1444     if (!constructorCondition.isWatchable() || !speciesCondition.isWatchable()) {
1445         invalidateWatchpoint();
1446         return;
1447     }
1448
1449     m_constructorWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(constructorCondition, this);
1450     m_constructorWatchpoint->install();
1451
1452     m_constructorSpeciesWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(speciesCondition, this);
1453     m_constructorSpeciesWatchpoint->install();
1454
1455     // We only watch this from the DFG, and the DFG makes sure to only start watching if the watchpoint is in the IsWatched state.
1456     RELEASE_ASSERT(!globalObject->arraySpeciesWatchpoint().isBeingWatched()); 
1457     globalObject->arraySpeciesWatchpoint().touch(vm, "Set up array species watchpoint.");
1458 }
1459
1460 ArrayPrototypeAdaptiveInferredPropertyWatchpoint::ArrayPrototypeAdaptiveInferredPropertyWatchpoint(const ObjectPropertyCondition& key, ArrayPrototype* prototype)
1461     : Base(key)
1462     , m_arrayPrototype(prototype)
1463 {
1464 }
1465
1466 void ArrayPrototypeAdaptiveInferredPropertyWatchpoint::handleFire(const FireDetail& detail)
1467 {
1468     StringPrintStream out;
1469     out.print("ArrayPrototype adaption of ", key(), " failed: ", detail);
1470
1471     StringFireDetail stringDetail(out.toCString().data());
1472
1473     if (ArrayPrototypeInternal::verbose)
1474         WTF::dataLog(stringDetail, "\n");
1475
1476     JSGlobalObject* globalObject = m_arrayPrototype->globalObject();
1477     globalObject->arraySpeciesWatchpoint().fireAll(globalObject->vm(), stringDetail);
1478 }
1479
1480 } // namespace JSC