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