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