31a03a76a7dfd77442dfc722cb3968e2731058c1
[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(vm)) {
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, vm), "]"));
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     thisObject->ensureWritable(vm);
859
860     switch (thisObject->indexingType()) {
861     case ALL_CONTIGUOUS_INDEXING_TYPES:
862     case ALL_INT32_INDEXING_TYPES: {
863         auto& butterfly = *thisObject->butterfly();
864         if (length > butterfly.publicLength())
865             break;
866         auto data = butterfly.contiguous().data();
867         if (containsHole(data, length) && holesMustForwardToPrototype(vm, thisObject))
868             break;
869         std::reverse(data, data + length);
870         if (!hasInt32(thisObject->indexingType()))
871             vm.heap.writeBarrier(thisObject);
872         return JSValue::encode(thisObject);
873     }
874     case ALL_DOUBLE_INDEXING_TYPES: {
875         auto& butterfly = *thisObject->butterfly();
876         if (length > butterfly.publicLength())
877             break;
878         auto data = butterfly.contiguousDouble().data();
879         if (containsHole(data, length) && holesMustForwardToPrototype(vm, thisObject))
880             break;
881         std::reverse(data, data + length);
882         return JSValue::encode(thisObject);
883     }
884     case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
885         auto& storage = *thisObject->butterfly()->arrayStorage();
886         if (length > storage.vectorLength())
887             break;
888         if (storage.hasHoles() && holesMustForwardToPrototype(vm, thisObject))
889             break;
890         auto data = storage.vector().data();
891         std::reverse(data, data + length);
892         vm.heap.writeBarrier(thisObject);
893         return JSValue::encode(thisObject);
894     }
895     }
896
897     unsigned middle = length / 2;
898     for (unsigned lower = 0; lower < middle; lower++) {
899         unsigned upper = length - lower - 1;
900         bool lowerExists = thisObject->hasProperty(exec, lower);
901         RETURN_IF_EXCEPTION(scope, encodedJSValue());
902         JSValue lowerValue;
903         if (lowerExists) {
904             lowerValue = thisObject->get(exec, lower);
905             RETURN_IF_EXCEPTION(scope, encodedJSValue());
906         }
907
908         bool upperExists = thisObject->hasProperty(exec, upper);
909         RETURN_IF_EXCEPTION(scope, encodedJSValue());
910         JSValue upperValue;
911         if (upperExists) {
912             upperValue = thisObject->get(exec, upper);
913             RETURN_IF_EXCEPTION(scope, encodedJSValue());
914         }
915
916         if (upperExists) {
917             thisObject->putByIndexInline(exec, lower, upperValue, true);
918             RETURN_IF_EXCEPTION(scope, encodedJSValue());
919         } else {
920             bool success = thisObject->methodTable(vm)->deletePropertyByIndex(thisObject, exec, lower);
921             RETURN_IF_EXCEPTION(scope, encodedJSValue());
922             if (UNLIKELY(!success)) {
923                 throwTypeError(exec, scope, UnableToDeletePropertyError);
924                 return encodedJSValue();
925             }
926         }
927
928         if (lowerExists) {
929             thisObject->putByIndexInline(exec, upper, lowerValue, true);
930             RETURN_IF_EXCEPTION(scope, encodedJSValue());
931         } else {
932             bool success = thisObject->methodTable(vm)->deletePropertyByIndex(thisObject, exec, upper);
933             RETURN_IF_EXCEPTION(scope, encodedJSValue());
934             if (UNLIKELY(!success)) {
935                 throwTypeError(exec, scope, UnableToDeletePropertyError);
936                 return encodedJSValue();
937             }
938         }
939     }
940     return JSValue::encode(thisObject);
941 }
942
943 EncodedJSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState* exec)
944 {
945     VM& vm = exec->vm();
946     auto scope = DECLARE_THROW_SCOPE(vm);
947     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
948     EXCEPTION_ASSERT(!!scope.exception() == !thisObj);
949     if (UNLIKELY(!thisObj))
950         return encodedJSValue();
951     unsigned length = toLength(exec, thisObj);
952     RETURN_IF_EXCEPTION(scope, encodedJSValue());
953
954     if (length == 0) {
955         scope.release();
956         putLength(exec, vm, thisObj, jsNumber(length));
957         return JSValue::encode(jsUndefined());
958     }
959
960     JSValue result = thisObj->getIndex(exec, 0);
961     RETURN_IF_EXCEPTION(scope, encodedJSValue());
962     shift<JSArray::ShiftCountForShift>(exec, thisObj, 0, 1, 0, length);
963     RETURN_IF_EXCEPTION(scope, encodedJSValue());
964     scope.release();
965     putLength(exec, vm, thisObj, jsNumber(length - 1));
966     return JSValue::encode(result);
967 }
968
969 EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec)
970 {
971     // https://tc39.github.io/ecma262/#sec-array.prototype.slice
972     VM& vm = exec->vm();
973     auto scope = DECLARE_THROW_SCOPE(vm);
974     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
975     EXCEPTION_ASSERT(!!scope.exception() == !thisObj);
976     if (UNLIKELY(!thisObj))
977         return { };
978     unsigned length = toLength(exec, thisObj);
979     RETURN_IF_EXCEPTION(scope, { });
980
981     unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length);
982     RETURN_IF_EXCEPTION(scope, { });
983     unsigned end = argumentClampedIndexFromStartOrEnd(exec, 1, length, length);
984     RETURN_IF_EXCEPTION(scope, { });
985     if (end < begin)
986         end = begin;
987
988     std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, end - begin);
989     // We can only get an exception if we call some user function.
990     EXCEPTION_ASSERT(!!scope.exception() == (speciesResult.first == SpeciesConstructResult::Exception));
991     if (UNLIKELY(speciesResult.first == SpeciesConstructResult::Exception))
992         return { };
993
994     bool okToDoFastPath = speciesResult.first == SpeciesConstructResult::FastPath && isJSArray(thisObj) && length == toLength(exec, thisObj);
995     RETURN_IF_EXCEPTION(scope, { });
996     if (LIKELY(okToDoFastPath)) {
997         if (JSArray* result = asArray(thisObj)->fastSlice(*exec, begin, end - begin))
998             return JSValue::encode(result);
999     }
1000
1001     JSObject* result;
1002     if (speciesResult.first == SpeciesConstructResult::CreatedObject)
1003         result = speciesResult.second;
1004     else {
1005         result = constructEmptyArray(exec, nullptr, end - begin);
1006         RETURN_IF_EXCEPTION(scope, { });
1007     }
1008
1009     unsigned n = 0;
1010     for (unsigned k = begin; k < end; k++, n++) {
1011         JSValue v = getProperty(exec, thisObj, k);
1012         RETURN_IF_EXCEPTION(scope, { });
1013         if (v) {
1014             result->putDirectIndex(exec, n, v, 0, PutDirectIndexShouldThrow);
1015             RETURN_IF_EXCEPTION(scope, { });
1016         }
1017     }
1018     scope.release();
1019     setLength(exec, vm, result, n);
1020     return JSValue::encode(result);
1021 }
1022
1023 EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec)
1024 {
1025     // 15.4.4.12
1026
1027     VM& vm = exec->vm();
1028     auto scope = DECLARE_THROW_SCOPE(vm);
1029
1030     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
1031     EXCEPTION_ASSERT(!!scope.exception() == !thisObj);
1032     if (UNLIKELY(!thisObj))
1033         return encodedJSValue();
1034     unsigned length = toLength(exec, thisObj);
1035     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1036
1037     if (!exec->argumentCount()) {
1038         std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, 0);
1039         EXCEPTION_ASSERT(!!scope.exception() == (speciesResult.first == SpeciesConstructResult::Exception));
1040         if (UNLIKELY(speciesResult.first == SpeciesConstructResult::Exception))
1041             return encodedJSValue();
1042
1043         JSObject* result;
1044         if (speciesResult.first == SpeciesConstructResult::CreatedObject)
1045             result = speciesResult.second;
1046         else {
1047             result = constructEmptyArray(exec, nullptr);
1048             RETURN_IF_EXCEPTION(scope, encodedJSValue());
1049         }
1050
1051         setLength(exec, vm, result, 0);
1052         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1053         scope.release();
1054         setLength(exec, vm, thisObj, length);
1055         return JSValue::encode(result);
1056     }
1057
1058     unsigned actualStart = argumentClampedIndexFromStartOrEnd(exec, 0, length);
1059     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1060
1061     unsigned actualDeleteCount = length - actualStart;
1062     if (exec->argumentCount() > 1) {
1063         double deleteCount = exec->uncheckedArgument(1).toInteger(exec);
1064         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1065         if (deleteCount < 0)
1066             actualDeleteCount = 0;
1067         else if (deleteCount > length - actualStart)
1068             actualDeleteCount = length - actualStart;
1069         else
1070             actualDeleteCount = static_cast<unsigned>(deleteCount);
1071     }
1072
1073     std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, actualDeleteCount);
1074     EXCEPTION_ASSERT(!!scope.exception() == (speciesResult.first == SpeciesConstructResult::Exception));
1075     if (speciesResult.first == SpeciesConstructResult::Exception)
1076         return JSValue::encode(jsUndefined());
1077
1078     JSObject* result = nullptr;
1079     bool okToDoFastPath = speciesResult.first == SpeciesConstructResult::FastPath && isJSArray(thisObj) && length == toLength(exec, thisObj);
1080     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1081     if (LIKELY(okToDoFastPath))
1082         result = asArray(thisObj)->fastSlice(*exec, actualStart, actualDeleteCount);
1083
1084     if (!result) {
1085         if (speciesResult.first == SpeciesConstructResult::CreatedObject)
1086             result = speciesResult.second;
1087         else {
1088             result = JSArray::tryCreate(vm, exec->lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), actualDeleteCount);
1089             if (UNLIKELY(!result)) {
1090                 throwOutOfMemoryError(exec, scope);
1091                 return encodedJSValue();
1092             }
1093         }
1094         for (unsigned k = 0; k < actualDeleteCount; ++k) {
1095             JSValue v = getProperty(exec, thisObj, k + actualStart);
1096             RETURN_IF_EXCEPTION(scope, encodedJSValue());
1097             if (UNLIKELY(!v))
1098                 continue;
1099             result->putDirectIndex(exec, k, v, 0, PutDirectIndexShouldThrow);
1100             RETURN_IF_EXCEPTION(scope, encodedJSValue());
1101         }
1102     }
1103
1104     unsigned itemCount = std::max<int>(exec->argumentCount() - 2, 0);
1105     if (itemCount < actualDeleteCount) {
1106         shift<JSArray::ShiftCountForSplice>(exec, thisObj, actualStart, actualDeleteCount, itemCount, length);
1107         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1108     } else if (itemCount > actualDeleteCount) {
1109         unshift<JSArray::ShiftCountForSplice>(exec, thisObj, actualStart, actualDeleteCount, itemCount, length);
1110         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1111     }
1112     for (unsigned k = 0; k < itemCount; ++k) {
1113         thisObj->putByIndexInline(exec, k + actualStart, exec->uncheckedArgument(k + 2), true);
1114         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1115     }
1116     
1117     scope.release();
1118     setLength(exec, vm, thisObj, length - actualDeleteCount + itemCount);
1119     return JSValue::encode(result);
1120 }
1121
1122 EncodedJSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState* exec)
1123 {
1124     VM& vm = exec->vm();
1125     auto scope = DECLARE_THROW_SCOPE(vm);
1126     // 15.4.4.13
1127
1128     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
1129     EXCEPTION_ASSERT(!!scope.exception() == !thisObj);
1130     if (UNLIKELY(!thisObj))
1131         return encodedJSValue();
1132     double doubleLength = toLength(exec, thisObj);
1133     unsigned length = doubleLength;
1134     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1135
1136     unsigned nrArgs = exec->argumentCount();
1137     if (nrArgs) {
1138         if (UNLIKELY(doubleLength + static_cast<double>(nrArgs) > maxSafeInteger()))
1139             return throwVMTypeError(exec, scope, "Cannot shift to offset greater than (2 ** 53) - 1"_s);
1140         unshift<JSArray::ShiftCountForShift>(exec, thisObj, 0, 0, nrArgs, length);
1141         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1142     }
1143     for (unsigned k = 0; k < nrArgs; ++k) {
1144         thisObj->putByIndexInline(exec, k, exec->uncheckedArgument(k), true);
1145         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1146     }
1147     JSValue result = jsNumber(length + nrArgs);
1148     scope.release();
1149     putLength(exec, vm, thisObj, result);
1150     return JSValue::encode(result);
1151 }
1152
1153 EncodedJSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState* exec)
1154 {
1155     VM& vm = exec->vm();
1156     auto scope = DECLARE_THROW_SCOPE(vm);
1157
1158     // 15.4.4.14
1159     JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
1160     EXCEPTION_ASSERT(!!scope.exception() == !thisObject);
1161     if (UNLIKELY(!thisObject))
1162         return { };
1163     unsigned length = toLength(exec, thisObject);
1164     RETURN_IF_EXCEPTION(scope, { });
1165
1166     unsigned index = argumentClampedIndexFromStartOrEnd(exec, 1, length);
1167     RETURN_IF_EXCEPTION(scope, { });
1168     JSValue searchElement = exec->argument(0);
1169
1170     if (isJSArray(thisObject)) {
1171         JSArray* array = asArray(thisObject);
1172         bool canDoFastPath = array->canDoFastIndexedAccess(vm)
1173             && array->getArrayLength() == length; // The effects in getting `index` could have changed the length of this array.
1174         if (canDoFastPath) {
1175             switch (array->indexingType()) {
1176             case ALL_INT32_INDEXING_TYPES: {
1177                 if (!searchElement.isNumber())
1178                     return JSValue::encode(jsNumber(-1));
1179                 JSValue searchInt32;
1180                 if (searchElement.isInt32())
1181                     searchInt32 = searchElement;
1182                 else {
1183                     double searchNumber = searchElement.asNumber();
1184                     if (!canBeInt32(searchNumber))
1185                         return JSValue::encode(jsNumber(-1));
1186                     searchInt32 = jsNumber(static_cast<int32_t>(searchNumber));
1187                 }
1188                 auto& butterfly = *array->butterfly();
1189                 auto data = butterfly.contiguous().data();
1190                 for (; index < length; ++index) {
1191                     // Array#indexOf uses `===` semantics (not HashMap isEqual semantics).
1192                     // And the hole never matches against Int32 value.
1193                     if (searchInt32 == data[index].get())
1194                         return JSValue::encode(jsNumber(index));
1195                 }
1196                 return JSValue::encode(jsNumber(-1));
1197             }
1198             case ALL_CONTIGUOUS_INDEXING_TYPES: {
1199                 auto& butterfly = *array->butterfly();
1200                 auto data = butterfly.contiguous().data();
1201                 for (; index < length; ++index) {
1202                     JSValue value = data[index].get();
1203                     if (!value)
1204                         continue;
1205                     bool isEqual = JSValue::strictEqual(exec, searchElement, value);
1206                     RETURN_IF_EXCEPTION(scope, { });
1207                     if (isEqual)
1208                         return JSValue::encode(jsNumber(index));
1209                 }
1210                 return JSValue::encode(jsNumber(-1));
1211             }
1212             case ALL_DOUBLE_INDEXING_TYPES: {
1213                 if (!searchElement.isNumber())
1214                     return JSValue::encode(jsNumber(-1));
1215                 double searchNumber = searchElement.asNumber();
1216                 auto& butterfly = *array->butterfly();
1217                 auto data = butterfly.contiguousDouble().data();
1218                 for (; index < length; ++index) {
1219                     // Array#indexOf uses `===` semantics (not HashMap isEqual semantics).
1220                     // And the hole never matches since it is NaN.
1221                     if (data[index] == searchNumber)
1222                         return JSValue::encode(jsNumber(index));
1223                 }
1224                 return JSValue::encode(jsNumber(-1));
1225             }
1226             default:
1227                 break;
1228             }
1229         }
1230     }
1231
1232     for (; index < length; ++index) {
1233         JSValue e = getProperty(exec, thisObject, index);
1234         RETURN_IF_EXCEPTION(scope, { });
1235         if (!e)
1236             continue;
1237         bool isEqual = JSValue::strictEqual(exec, searchElement, e);
1238         RETURN_IF_EXCEPTION(scope, { });
1239         if (isEqual)
1240             return JSValue::encode(jsNumber(index));
1241     }
1242
1243     return JSValue::encode(jsNumber(-1));
1244 }
1245
1246 EncodedJSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState* exec)
1247 {
1248     VM& vm = exec->vm();
1249     auto scope = DECLARE_THROW_SCOPE(vm);
1250
1251     // 15.4.4.15
1252     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
1253     EXCEPTION_ASSERT(!!scope.exception() == !thisObj);
1254     if (UNLIKELY(!thisObj))
1255         return encodedJSValue();
1256     unsigned length = toLength(exec, thisObj);
1257     if (UNLIKELY(scope.exception()) || !length)
1258         return JSValue::encode(jsNumber(-1));
1259
1260     unsigned index = length - 1;
1261     if (exec->argumentCount() >= 2) {
1262         JSValue fromValue = exec->uncheckedArgument(1);
1263         double fromDouble = fromValue.toInteger(exec);
1264         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1265         if (fromDouble < 0) {
1266             fromDouble += length;
1267             if (fromDouble < 0)
1268                 return JSValue::encode(jsNumber(-1));
1269         }
1270         if (fromDouble < length)
1271             index = static_cast<unsigned>(fromDouble);
1272     }
1273
1274     JSValue searchElement = exec->argument(0);
1275     do {
1276         RELEASE_ASSERT(index < length);
1277         JSValue e = getProperty(exec, thisObj, index);
1278         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1279         if (!e)
1280             continue;
1281         bool isEqual = JSValue::strictEqual(exec, searchElement, e);
1282         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1283         if (isEqual)
1284             return JSValue::encode(jsNumber(index));
1285     } while (index--);
1286
1287     return JSValue::encode(jsNumber(-1));
1288 }
1289
1290 static bool moveElements(ExecState* exec, VM& vm, JSArray* target, unsigned targetOffset, JSArray* source, unsigned sourceLength)
1291 {
1292     auto scope = DECLARE_THROW_SCOPE(vm);
1293
1294     if (LIKELY(!hasAnyArrayStorage(source->indexingType()) && !holesMustForwardToPrototype(vm, source))) {
1295         for (unsigned i = 0; i < sourceLength; ++i) {
1296             JSValue value = source->tryGetIndexQuickly(i);
1297             if (value) {
1298                 target->putDirectIndex(exec, targetOffset + i, value, 0, PutDirectIndexShouldThrow);
1299                 RETURN_IF_EXCEPTION(scope, false);
1300             }
1301         }
1302     } else {
1303         for (unsigned i = 0; i < sourceLength; ++i) {
1304             JSValue value = getProperty(exec, source, i);
1305             RETURN_IF_EXCEPTION(scope, false);
1306             if (value) {
1307                 target->putDirectIndex(exec, targetOffset + i, value, 0, PutDirectIndexShouldThrow);
1308                 RETURN_IF_EXCEPTION(scope, false);
1309             }
1310         }
1311     }
1312     return true;
1313 }
1314
1315 static EncodedJSValue concatAppendOne(ExecState* exec, VM& vm, JSArray* first, JSValue second)
1316 {
1317     auto scope = DECLARE_THROW_SCOPE(vm);
1318
1319     ASSERT(!isJSArray(second));
1320     ASSERT(!shouldUseSlowPut(first->indexingType()));
1321     Butterfly* firstButterfly = first->butterfly();
1322     unsigned firstArraySize = firstButterfly->publicLength();
1323
1324     Checked<unsigned, RecordOverflow> checkedResultSize = firstArraySize;
1325     checkedResultSize += 1;
1326     if (UNLIKELY(checkedResultSize.hasOverflowed())) {
1327         throwOutOfMemoryError(exec, scope);
1328         return encodedJSValue();
1329     }
1330
1331     unsigned resultSize = checkedResultSize.unsafeGet();
1332     IndexingType type = first->mergeIndexingTypeForCopying(indexingTypeForValue(second) | IsArray);
1333     
1334     if (type == NonArray)
1335         type = first->indexingType();
1336
1337     Structure* resultStructure = exec->lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(type);
1338     JSArray* result = JSArray::tryCreate(vm, resultStructure, resultSize);
1339     if (UNLIKELY(!result)) {
1340         throwOutOfMemoryError(exec, scope);
1341         return encodedJSValue();
1342     }
1343
1344     bool success = result->appendMemcpy(exec, vm, 0, first);
1345     EXCEPTION_ASSERT(!scope.exception() || !success);
1346     if (!success) {
1347         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1348
1349         bool success = moveElements(exec, vm, result, 0, first, firstArraySize);
1350         EXCEPTION_ASSERT(!scope.exception() == success);
1351         if (UNLIKELY(!success))
1352             return encodedJSValue();
1353     }
1354
1355     scope.release();
1356     result->putDirectIndex(exec, firstArraySize, second);
1357     return JSValue::encode(result);
1358
1359 }
1360
1361 template<typename T>
1362 void clearElement(T& element)
1363 {
1364     element.clear();
1365 }
1366
1367 template<>
1368 void clearElement(double& element)
1369 {
1370     element = PNaN;
1371 }
1372
1373 template<typename T>
1374 ALWAYS_INLINE void copyElements(T* buffer, unsigned offset, void* source, unsigned sourceSize, IndexingType sourceType)
1375 {
1376     if (sourceType != ArrayWithUndecided) {
1377         memcpy(buffer + offset, source, sizeof(JSValue) * sourceSize);
1378         return;
1379     }
1380
1381     for (unsigned i = sourceSize; i--;)
1382         clearElement<T>(buffer[i + offset]);
1383 };
1384
1385 EncodedJSValue JSC_HOST_CALL arrayProtoPrivateFuncConcatMemcpy(ExecState* exec)
1386 {
1387     ASSERT(exec->argumentCount() == 2);
1388     VM& vm = exec->vm();
1389     auto scope = DECLARE_THROW_SCOPE(vm);
1390
1391     JSArray* firstArray = jsCast<JSArray*>(exec->uncheckedArgument(0));
1392     
1393     // This code assumes that neither array has set Symbol.isConcatSpreadable. If the first array
1394     // has indexed accessors then one of those accessors might change the value of Symbol.isConcatSpreadable
1395     // on the second argument.
1396     if (UNLIKELY(shouldUseSlowPut(firstArray->indexingType())))
1397         return JSValue::encode(jsNull());
1398
1399     // We need to check the species constructor here since checking it in the JS wrapper is too expensive for the non-optimizing tiers.
1400     bool isValid = speciesWatchpointIsValid(exec, firstArray);
1401     scope.assertNoException();
1402     if (UNLIKELY(!isValid))
1403         return JSValue::encode(jsNull());
1404
1405     JSValue second = exec->uncheckedArgument(1);
1406     if (!isJSArray(second)) {
1407         scope.release();
1408         return concatAppendOne(exec, vm, firstArray, second);
1409     }
1410
1411     JSArray* secondArray = jsCast<JSArray*>(second);
1412     
1413     Butterfly* firstButterfly = firstArray->butterfly();
1414     Butterfly* secondButterfly = secondArray->butterfly();
1415
1416     unsigned firstArraySize = firstButterfly->publicLength();
1417     unsigned secondArraySize = secondButterfly->publicLength();
1418
1419     Checked<unsigned, RecordOverflow> checkedResultSize = firstArraySize;
1420     checkedResultSize += secondArraySize;
1421
1422     if (UNLIKELY(checkedResultSize.hasOverflowed())) {
1423         throwOutOfMemoryError(exec, scope);
1424         return encodedJSValue();
1425     }
1426
1427     unsigned resultSize = checkedResultSize.unsafeGet();
1428     IndexingType firstType = firstArray->indexingType();
1429     IndexingType secondType = secondArray->indexingType();
1430     IndexingType type = firstArray->mergeIndexingTypeForCopying(secondType);
1431     if (type == NonArray || !firstArray->canFastCopy(vm, secondArray) || resultSize >= MIN_SPARSE_ARRAY_INDEX) {
1432         JSArray* result = constructEmptyArray(exec, nullptr, resultSize);
1433         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1434
1435         bool success = moveElements(exec, vm, result, 0, firstArray, firstArraySize);
1436         EXCEPTION_ASSERT(!scope.exception() == success);
1437         if (UNLIKELY(!success))
1438             return encodedJSValue();
1439         success = moveElements(exec, vm, result, firstArraySize, secondArray, secondArraySize);
1440         EXCEPTION_ASSERT(!scope.exception() == success);
1441         if (UNLIKELY(!success))
1442             return encodedJSValue();
1443
1444         return JSValue::encode(result);
1445     }
1446
1447     JSGlobalObject* lexicalGlobalObject = exec->lexicalGlobalObject();
1448     Structure* resultStructure = lexicalGlobalObject->arrayStructureForIndexingTypeDuringAllocation(type);
1449     if (UNLIKELY(hasAnyArrayStorage(resultStructure->indexingType())))
1450         return JSValue::encode(jsNull());
1451
1452     ASSERT(!lexicalGlobalObject->isHavingABadTime());
1453     ObjectInitializationScope initializationScope(vm);
1454     JSArray* result = JSArray::tryCreateUninitializedRestricted(initializationScope, resultStructure, resultSize);
1455     if (UNLIKELY(!result)) {
1456         throwOutOfMemoryError(exec, scope);
1457         return encodedJSValue();
1458     }
1459
1460     if (type == ArrayWithDouble) {
1461         double* buffer = result->butterfly()->contiguousDouble().data();
1462         copyElements(buffer, 0, firstButterfly->contiguousDouble().data(), firstArraySize, firstType);
1463         copyElements(buffer, firstArraySize, secondButterfly->contiguousDouble().data(), secondArraySize, secondType);
1464
1465     } else if (type != ArrayWithUndecided) {
1466         WriteBarrier<Unknown>* buffer = result->butterfly()->contiguous().data();
1467         copyElements(buffer, 0, firstButterfly->contiguous().data(), firstArraySize, firstType);
1468         copyElements(buffer, firstArraySize, secondButterfly->contiguous().data(), secondArraySize, secondType);
1469     }
1470
1471     result->butterfly()->setPublicLength(resultSize);
1472     return JSValue::encode(result);
1473 }
1474
1475 EncodedJSValue JSC_HOST_CALL arrayProtoPrivateFuncAppendMemcpy(ExecState* exec)
1476 {
1477     ASSERT(exec->argumentCount() == 3);
1478
1479     VM& vm = exec->vm();
1480     auto scope = DECLARE_THROW_SCOPE(vm);
1481     JSArray* resultArray = jsCast<JSArray*>(exec->uncheckedArgument(0));
1482     JSArray* otherArray = jsCast<JSArray*>(exec->uncheckedArgument(1));
1483     JSValue startValue = exec->uncheckedArgument(2);
1484     ASSERT(startValue.isAnyInt() && startValue.asAnyInt() >= 0 && startValue.asAnyInt() <= std::numeric_limits<unsigned>::max());
1485     unsigned startIndex = static_cast<unsigned>(startValue.asAnyInt());
1486     bool success = resultArray->appendMemcpy(exec, vm, startIndex, otherArray);
1487     EXCEPTION_ASSERT(!scope.exception() || !success);
1488     if (success)
1489         return JSValue::encode(jsUndefined());
1490     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1491     scope.release();
1492     moveElements(exec, vm, resultArray, startIndex, otherArray, otherArray->length());
1493     return JSValue::encode(jsUndefined());
1494 }
1495
1496
1497 // -------------------- ArrayPrototype.constructor Watchpoint ------------------
1498
1499 namespace ArrayPrototypeInternal {
1500 static bool verbose = false;
1501 }
1502
1503 class ArrayPrototypeAdaptiveInferredPropertyWatchpoint : public AdaptiveInferredPropertyValueWatchpointBase {
1504 public:
1505     typedef AdaptiveInferredPropertyValueWatchpointBase Base;
1506     ArrayPrototypeAdaptiveInferredPropertyWatchpoint(const ObjectPropertyCondition&, ArrayPrototype*);
1507
1508 private:
1509     void handleFire(VM&, const FireDetail&) override;
1510
1511     ArrayPrototype* m_arrayPrototype;
1512 };
1513
1514 void ArrayPrototype::tryInitializeSpeciesWatchpoint(ExecState* exec)
1515 {
1516     VM& vm = exec->vm();
1517
1518     RELEASE_ASSERT(!m_constructorWatchpoint);
1519     RELEASE_ASSERT(!m_constructorSpeciesWatchpoint);
1520
1521     auto scope = DECLARE_THROW_SCOPE(vm);
1522
1523     if (ArrayPrototypeInternal::verbose)
1524         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");
1525     // First we need to make sure that the Array.prototype.constructor property points to Array
1526     // and that Array[Symbol.species] is the primordial GetterSetter.
1527
1528     // We only initialize once so flattening the structures does not have any real cost.
1529     Structure* prototypeStructure = this->structure(vm);
1530     if (prototypeStructure->isDictionary())
1531         prototypeStructure = prototypeStructure->flattenDictionaryStructure(vm, this);
1532     RELEASE_ASSERT(!prototypeStructure->isDictionary());
1533
1534     JSGlobalObject* globalObject = this->globalObject(vm);
1535     ArrayConstructor* arrayConstructor = globalObject->arrayConstructor();
1536
1537     auto invalidateWatchpoint = [&] {
1538         globalObject->arraySpeciesWatchpoint().invalidate(vm, StringFireDetail("Was not able to set up array species watchpoint."));
1539     };
1540
1541     PropertySlot constructorSlot(this, PropertySlot::InternalMethodType::VMInquiry);
1542     this->getOwnPropertySlot(this, exec, vm.propertyNames->constructor, constructorSlot);
1543     scope.assertNoException();
1544     if (constructorSlot.slotBase() != this
1545         || !constructorSlot.isCacheableValue()
1546         || constructorSlot.getValue(exec, vm.propertyNames->constructor) != arrayConstructor) {
1547         invalidateWatchpoint();
1548         return;
1549     }
1550
1551     Structure* constructorStructure = arrayConstructor->structure(vm);
1552     if (constructorStructure->isDictionary())
1553         constructorStructure = constructorStructure->flattenDictionaryStructure(vm, arrayConstructor);
1554
1555     PropertySlot speciesSlot(arrayConstructor, PropertySlot::InternalMethodType::VMInquiry);
1556     arrayConstructor->getOwnPropertySlot(arrayConstructor, exec, vm.propertyNames->speciesSymbol, speciesSlot);
1557     scope.assertNoException();
1558     if (speciesSlot.slotBase() != arrayConstructor
1559         || !speciesSlot.isCacheableGetter()
1560         || speciesSlot.getterSetter() != globalObject->speciesGetterSetter()) {
1561         invalidateWatchpoint();
1562         return;
1563     }
1564
1565     // Now we need to setup the watchpoints to make sure these conditions remain valid.
1566     prototypeStructure->startWatchingPropertyForReplacements(vm, constructorSlot.cachedOffset());
1567     constructorStructure->startWatchingPropertyForReplacements(vm, speciesSlot.cachedOffset());
1568
1569     ObjectPropertyCondition constructorCondition = ObjectPropertyCondition::equivalence(vm, this, this, vm.propertyNames->constructor.impl(), arrayConstructor);
1570     ObjectPropertyCondition speciesCondition = ObjectPropertyCondition::equivalence(vm, this, arrayConstructor, vm.propertyNames->speciesSymbol.impl(), globalObject->speciesGetterSetter());
1571
1572     if (!constructorCondition.isWatchable() || !speciesCondition.isWatchable()) {
1573         invalidateWatchpoint();
1574         return;
1575     }
1576
1577     m_constructorWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(constructorCondition, this);
1578     m_constructorWatchpoint->install(vm);
1579
1580     m_constructorSpeciesWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(speciesCondition, this);
1581     m_constructorSpeciesWatchpoint->install(vm);
1582
1583     // We only watch this from the DFG, and the DFG makes sure to only start watching if the watchpoint is in the IsWatched state.
1584     RELEASE_ASSERT(!globalObject->arraySpeciesWatchpoint().isBeingWatched()); 
1585     globalObject->arraySpeciesWatchpoint().touch(vm, "Set up array species watchpoint.");
1586 }
1587
1588 ArrayPrototypeAdaptiveInferredPropertyWatchpoint::ArrayPrototypeAdaptiveInferredPropertyWatchpoint(const ObjectPropertyCondition& key, ArrayPrototype* prototype)
1589     : Base(key)
1590     , m_arrayPrototype(prototype)
1591 {
1592 }
1593
1594 void ArrayPrototypeAdaptiveInferredPropertyWatchpoint::handleFire(VM& vm, const FireDetail& detail)
1595 {
1596     auto lazyDetail = createLazyFireDetail("ArrayPrototype adaption of ", key(), " failed: ", detail);
1597
1598     if (ArrayPrototypeInternal::verbose)
1599         WTF::dataLog(lazyDetail, "\n");
1600
1601     JSGlobalObject* globalObject = m_arrayPrototype->globalObject(vm);
1602     globalObject->arraySpeciesWatchpoint().fireAll(vm, lazyDetail);
1603 }
1604
1605 } // namespace JSC