Rollout r200426 since it causes PLT regressions.
[WebKit-https.git] / Source / JavaScriptCore / runtime / ArrayPrototype.cpp
1 /*
2  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2003, 2007-2009, 2011, 2013, 2015-2016 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 "CachedCall.h"
32 #include "CodeBlock.h"
33 #include "CopiedSpaceInlines.h"
34 #include "Error.h"
35 #include "Interpreter.h"
36 #include "JIT.h"
37 #include "JSArrayIterator.h"
38 #include "JSCBuiltins.h"
39 #include "JSCInlines.h"
40 #include "JSStringBuilder.h"
41 #include "JSStringJoiner.h"
42 #include "Lookup.h"
43 #include "ObjectConstructor.h"
44 #include "ObjectPrototype.h"
45 #include "StringRecursionChecker.h"
46 #include <algorithm>
47 #include <wtf/Assertions.h>
48 #include <wtf/HashSet.h>
49
50 namespace JSC {
51
52 EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState*);
53 EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState*);
54 EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState*);
55 EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState*);
56 EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState*);
57 EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState*);
58 EncodedJSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState*);
59 EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState*);
60 EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState*);
61 EncodedJSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState*);
62 EncodedJSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState*);
63 EncodedJSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState*);
64
65 // ------------------------------ ArrayPrototype ----------------------------
66
67 const ClassInfo ArrayPrototype::s_info = {"Array", &JSArray::s_info, nullptr, CREATE_METHOD_TABLE(ArrayPrototype)};
68
69 ArrayPrototype* ArrayPrototype::create(VM& vm, JSGlobalObject* globalObject, Structure* structure)
70 {
71     ArrayPrototype* prototype = new (NotNull, allocateCell<ArrayPrototype>(vm.heap)) ArrayPrototype(vm, structure);
72     prototype->finishCreation(vm, globalObject);
73     vm.heap.addFinalizer(prototype, destroy);
74     return prototype;
75 }
76
77 // ECMA 15.4.4
78 ArrayPrototype::ArrayPrototype(VM& vm, Structure* structure)
79     : JSArray(vm, structure, 0)
80 {
81 }
82
83 void ArrayPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
84 {
85     Base::finishCreation(vm);
86     ASSERT(inherits(info()));
87     vm.prototypeMap.addPrototype(this);
88
89     putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().valuesPublicName(), globalObject->arrayProtoValuesFunction(), DontEnum);
90     putDirectWithoutTransition(vm, vm.propertyNames->iteratorSymbol, globalObject->arrayProtoValuesFunction(), DontEnum);
91     
92     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->toString, arrayProtoFuncToString, DontEnum, 0);
93     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->toLocaleString, arrayProtoFuncToLocaleString, DontEnum, 0);
94     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("concat", arrayProtoFuncConcat, DontEnum, 1);
95     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("fill", arrayPrototypeFillCodeGenerator, DontEnum);
96     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->join, arrayProtoFuncJoin, DontEnum, 1);
97     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("pop", arrayProtoFuncPop, DontEnum, 0, ArrayPopIntrinsic);
98     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().pushPublicName(), arrayProtoFuncPush, DontEnum, 1, ArrayPushIntrinsic);
99     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().pushPrivateName(), arrayProtoFuncPush, DontEnum | DontDelete | ReadOnly, 1, ArrayPushIntrinsic);
100     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("reverse", arrayProtoFuncReverse, DontEnum, 0);
101     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().shiftPublicName(), arrayProtoFuncShift, DontEnum, 0);
102     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().shiftPrivateName(), arrayProtoFuncShift, DontEnum | DontDelete | ReadOnly, 0);
103     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->slice, arrayProtoFuncSlice, DontEnum, 2);
104     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("sort", arrayPrototypeSortCodeGenerator, DontEnum);
105     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("splice", arrayProtoFuncSplice, DontEnum, 2);
106     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("unshift", arrayProtoFuncUnShift, DontEnum, 1);
107     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("every", arrayPrototypeEveryCodeGenerator, DontEnum);
108     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("forEach", arrayPrototypeForEachCodeGenerator, DontEnum);
109     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("some", arrayPrototypeSomeCodeGenerator, DontEnum);
110     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("indexOf", arrayProtoFuncIndexOf, DontEnum, 1);
111     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("lastIndexOf", arrayProtoFuncLastIndexOf, DontEnum, 1);
112     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("filter", arrayPrototypeFilterCodeGenerator, DontEnum);
113     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("reduce", arrayPrototypeReduceCodeGenerator, DontEnum);
114     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("reduceRight", arrayPrototypeReduceRightCodeGenerator, DontEnum);
115     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("map", arrayPrototypeMapCodeGenerator, DontEnum);
116     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().entriesPublicName(), arrayPrototypeEntriesCodeGenerator, DontEnum);
117     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().keysPublicName(), arrayPrototypeKeysCodeGenerator, DontEnum);
118     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("find", arrayPrototypeFindCodeGenerator, DontEnum);
119     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("findIndex", arrayPrototypeFindIndexCodeGenerator, DontEnum);
120     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("includes", arrayPrototypeIncludesCodeGenerator, DontEnum);
121     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("copyWithin", arrayPrototypeCopyWithinCodeGenerator, DontEnum);
122     
123     JSObject* unscopables = constructEmptyObject(globalObject->globalExec(), globalObject->nullPrototypeObjectStructure());
124     const char* unscopableNames[] = {
125         "copyWithin",
126         "entries",
127         "fill",
128         "find",
129         "findIndex",
130         "keys",
131         "values"
132     };
133     for (const char* unscopableName : unscopableNames)
134         unscopables->putDirect(vm, Identifier::fromString(&vm, unscopableName), jsBoolean(true));
135     putDirectWithoutTransition(vm, vm.propertyNames->unscopablesSymbol, unscopables, DontEnum | ReadOnly);
136 }
137
138 void ArrayPrototype::destroy(JSC::JSCell* cell)
139 {
140     ArrayPrototype* thisObject = static_cast<ArrayPrototype*>(cell);
141     thisObject->ArrayPrototype::~ArrayPrototype();
142 }
143
144 // ------------------------------ Array Functions ----------------------------
145
146 static ALWAYS_INLINE JSValue getProperty(ExecState* exec, JSObject* object, unsigned index)
147 {
148     if (JSValue result = object->tryGetIndexQuickly(index))
149         return result;
150     // We want to perform get and has in the same operation.
151     // We can only do so when this behavior is not observable. The
152     // only time it is observable is when we encounter a ProxyObject
153     // somewhere in the prototype chain.
154     PropertySlot slot(object, PropertySlot::InternalMethodType::HasProperty);
155     if (!object->getPropertySlot(exec, index, slot))
156         return JSValue();
157     if (UNLIKELY(slot.isTaintedByProxy()))
158         return object->get(exec, index);
159     return slot.getValue(exec, index);
160 }
161
162 static ALWAYS_INLINE void putLength(ExecState* exec, JSObject* obj, JSValue value)
163 {
164     PutPropertySlot slot(obj);
165     obj->methodTable()->put(obj, exec, exec->propertyNames().length, value, slot);
166 }
167
168 static ALWAYS_INLINE void setLength(ExecState* exec, JSObject* obj, unsigned value)
169 {
170     if (isJSArray(obj))
171         jsCast<JSArray*>(obj)->setLength(exec, value);
172     putLength(exec, obj, jsNumber(value));
173 }
174
175 enum class SpeciesConstructResult {
176     FastPath,
177     Exception,
178     CreatedObject
179 };
180
181 static ALWAYS_INLINE std::pair<SpeciesConstructResult, JSObject*> speciesConstructArray(ExecState* exec, JSObject* thisObject, unsigned length)
182 {
183     // ECMA 9.4.2.3: https://tc39.github.io/ecma262/#sec-arrayspeciescreate
184     JSValue constructor = jsUndefined();
185     if (LIKELY(isArray(exec, thisObject))) {
186         // Fast path in the normal case where the user has not set an own constructor and the Array.prototype.constructor is normal.
187         // We need prototype check for subclasses of Array, which are Array objects but have a different prototype by default.
188         if (LIKELY(!thisObject->hasCustomProperties()
189             && thisObject->globalObject()->arrayPrototype() == thisObject->getPrototypeDirect()
190             && !thisObject->globalObject()->arrayPrototype()->didChangeConstructorOrSpeciesProperties()))
191             return std::make_pair(SpeciesConstructResult::FastPath, nullptr);
192
193         constructor = thisObject->get(exec, exec->propertyNames().constructor);
194         if (exec->hadException())
195             return std::make_pair(SpeciesConstructResult::Exception, nullptr);
196         if (constructor.isConstructor()) {
197             JSObject* constructorObject = jsCast<JSObject*>(constructor);
198             if (exec->lexicalGlobalObject() != constructorObject->globalObject())
199                 return std::make_pair(SpeciesConstructResult::FastPath, nullptr);;
200         }
201         if (constructor.isObject()) {
202             constructor = constructor.get(exec, exec->propertyNames().speciesSymbol);
203             if (exec->hadException())
204                 return std::make_pair(SpeciesConstructResult::Exception, nullptr);
205             if (constructor.isNull())
206                 return std::make_pair(SpeciesConstructResult::FastPath, nullptr);;
207         }
208     } else if (exec->hadException())
209         return std::make_pair(SpeciesConstructResult::Exception, nullptr);
210
211     if (constructor.isUndefined())
212         return std::make_pair(SpeciesConstructResult::FastPath, nullptr);
213
214     MarkedArgumentBuffer args;
215     args.append(jsNumber(length));
216     JSObject* newObject = construct(exec, constructor, args, "Species construction did not get a valid constructor");
217     if (exec->hadException())
218         return std::make_pair(SpeciesConstructResult::Exception, nullptr);
219     return std::make_pair(SpeciesConstructResult::CreatedObject, newObject);
220 }
221
222 static inline unsigned argumentClampedIndexFromStartOrEnd(ExecState* exec, int argument, unsigned length, unsigned undefinedValue = 0)
223 {
224     JSValue value = exec->argument(argument);
225     if (value.isUndefined())
226         return undefinedValue;
227
228     double indexDouble = value.toInteger(exec);
229     if (indexDouble < 0) {
230         indexDouble += length;
231         return indexDouble < 0 ? 0 : static_cast<unsigned>(indexDouble);
232     }
233     return indexDouble > length ? length : static_cast<unsigned>(indexDouble);
234 }
235
236 // The shift/unshift function implement the shift/unshift behaviour required
237 // by the corresponding array prototype methods, and by splice. In both cases,
238 // the methods are operating an an array or array like object.
239 //
240 //  header  currentCount  (remainder)
241 // [------][------------][-----------]
242 //  header  resultCount  (remainder)
243 // [------][-----------][-----------]
244 //
245 // The set of properties in the range 'header' must be unchanged. The set of
246 // properties in the range 'remainder' (where remainder = length - header -
247 // currentCount) will be shifted to the left or right as appropriate; in the
248 // case of shift this must be removing values, in the case of unshift this
249 // must be introducing new values.
250
251 template<JSArray::ShiftCountMode shiftCountMode>
252 void shift(ExecState* exec, JSObject* thisObj, unsigned header, unsigned currentCount, unsigned resultCount, unsigned length)
253 {
254     RELEASE_ASSERT(currentCount > resultCount);
255     unsigned count = currentCount - resultCount;
256
257     RELEASE_ASSERT(header <= length);
258     RELEASE_ASSERT(currentCount <= (length - header));
259
260     if (isJSArray(thisObj)) {
261         JSArray* array = asArray(thisObj);
262         if (array->length() == length && array->shiftCount<shiftCountMode>(exec, header, count))
263             return;
264     }
265
266     for (unsigned k = header; k < length - currentCount; ++k) {
267         unsigned from = k + currentCount;
268         unsigned to = k + resultCount;
269         if (JSValue value = getProperty(exec, thisObj, from)) {
270             if (exec->hadException())
271                 return;
272             thisObj->putByIndexInline(exec, to, value, true);
273             if (exec->hadException())
274                 return;
275         } else if (!thisObj->methodTable(exec->vm())->deletePropertyByIndex(thisObj, exec, to)) {
276             throwTypeError(exec, ASCIILiteral("Unable to delete property."));
277             return;
278         }
279     }
280     for (unsigned k = length; k > length - count; --k) {
281         if (!thisObj->methodTable(exec->vm())->deletePropertyByIndex(thisObj, exec, k - 1)) {
282             throwTypeError(exec, ASCIILiteral("Unable to delete property."));
283             return;
284         }
285     }
286 }
287
288 template<JSArray::ShiftCountMode shiftCountMode>
289 void unshift(ExecState* exec, JSObject* thisObj, unsigned header, unsigned currentCount, unsigned resultCount, unsigned length)
290 {
291     RELEASE_ASSERT(resultCount > currentCount);
292     unsigned count = resultCount - currentCount;
293
294     RELEASE_ASSERT(header <= length);
295     RELEASE_ASSERT(currentCount <= (length - header));
296
297     // Guard against overflow.
298     if (count > (UINT_MAX - length)) {
299         throwOutOfMemoryError(exec);
300         return;
301     }
302
303     if (isJSArray(thisObj)) {
304         JSArray* array = asArray(thisObj);
305         if (array->length() == length && array->unshiftCount<shiftCountMode>(exec, header, count))
306             return;
307     }
308
309     for (unsigned k = length - currentCount; k > header; --k) {
310         unsigned from = k + currentCount - 1;
311         unsigned to = k + resultCount - 1;
312         if (JSValue value = getProperty(exec, thisObj, from)) {
313             if (exec->hadException())
314                 return;
315             thisObj->putByIndexInline(exec, to, value, true);
316         } else if (!thisObj->methodTable(exec->vm())->deletePropertyByIndex(thisObj, exec, to)) {
317             throwTypeError(exec, ASCIILiteral("Unable to delete property."));
318             return;
319         }
320         if (exec->hadException())
321             return;
322     }
323 }
324
325 EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec)
326 {
327     JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
328
329     // 1. Let array be the result of calling ToObject on the this value.
330     JSObject* thisObject = thisValue.toObject(exec);
331     VM& vm = exec->vm();
332     if (UNLIKELY(vm.exception()))
333         return JSValue::encode(jsUndefined());
334     
335     // 2. Let func be the result of calling the [[Get]] internal method of array with argument "join".
336     JSValue function = JSValue(thisObject).get(exec, exec->propertyNames().join);
337
338     // 3. If IsCallable(func) is false, then let func be the standard built-in method Object.prototype.toString (15.2.4.2).
339     bool customJoinCase = false;
340     if (!function.isCell())
341         customJoinCase = true;
342     CallData callData;
343     CallType callType = getCallData(function, callData);
344     if (callType == CallType::None)
345         customJoinCase = true;
346
347     if (UNLIKELY(customJoinCase))
348         return JSValue::encode(jsMakeNontrivialString(exec, "[object ", thisObject->methodTable(exec->vm())->className(thisObject), "]"));
349
350     // 4. Return the result of calling the [[Call]] internal method of func providing array as the this value and an empty arguments list.
351     if (!isJSArray(thisObject) || callType != CallType::Host || callData.native.function != arrayProtoFuncJoin)
352         return JSValue::encode(call(exec, function, callType, callData, thisObject, exec->emptyList()));
353
354     ASSERT(isJSArray(thisValue));
355     JSArray* thisArray = asArray(thisValue);
356
357     unsigned length = thisArray->length();
358
359     StringRecursionChecker checker(exec, thisArray);
360     if (JSValue earlyReturnValue = checker.earlyReturnValue())
361         return JSValue::encode(earlyReturnValue);
362
363     JSStringJoiner joiner(*exec, ',', length);
364     if (UNLIKELY(vm.exception()))
365         return JSValue::encode(jsUndefined());
366
367     for (unsigned i = 0; i < length; ++i) {
368         JSValue element = thisArray->tryGetIndexQuickly(i);
369         if (!element) {
370             element = thisArray->get(exec, i);
371             if (UNLIKELY(vm.exception()))
372                 return JSValue::encode(jsUndefined());
373         }
374         joiner.append(*exec, element);
375         if (UNLIKELY(vm.exception()))
376             return JSValue::encode(jsUndefined());
377     }
378
379     return JSValue::encode(joiner.join(*exec));
380 }
381
382 EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec)
383 {
384     JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
385
386     JSObject* thisObject = thisValue.toObject(exec);
387     if (exec->hadException())
388         return JSValue::encode(jsUndefined());
389
390     unsigned length = getLength(exec, thisObject);
391     if (exec->hadException())
392         return JSValue::encode(jsUndefined());
393
394     StringRecursionChecker checker(exec, thisObject);
395     if (JSValue earlyReturnValue = checker.earlyReturnValue())
396         return JSValue::encode(earlyReturnValue);
397
398     JSStringJoiner stringJoiner(*exec, ',', length);
399     if (exec->hadException())
400         return JSValue::encode(jsUndefined());
401
402 #if ENABLE(INTL)
403     ArgList arguments(exec);
404     for (unsigned i = 0; i < length; ++i) {
405         JSValue element = thisObject->getIndex(exec, i);
406         if (exec->hadException())
407             return JSValue::encode(jsUndefined());
408         if (element.isUndefinedOrNull())
409             element = jsEmptyString(exec);
410         else {
411             JSValue conversionFunction = element.get(exec, exec->propertyNames().toLocaleString);
412             if (exec->hadException())
413                 return JSValue::encode(jsUndefined());
414             CallData callData;
415             CallType callType = getCallData(conversionFunction, callData);
416             if (callType != CallType::None) {
417                 element = call(exec, conversionFunction, callType, callData, element, arguments);
418                 if (exec->hadException())
419                 return JSValue::encode(jsUndefined());
420             }
421         }
422         stringJoiner.append(*exec, element);
423         if (exec->hadException())
424             return JSValue::encode(jsUndefined());
425     }
426 #else // !ENABLE(INTL)
427     for (unsigned i = 0; i < length; ++i) {
428         JSValue element = thisObject->getIndex(exec, i);
429         if (exec->hadException())
430             return JSValue::encode(jsUndefined());
431         if (element.isUndefinedOrNull())
432             continue;
433         JSValue conversionFunction = element.get(exec, exec->propertyNames().toLocaleString);
434         if (exec->hadException())
435             return JSValue::encode(jsUndefined());
436         CallData callData;
437         CallType callType = getCallData(conversionFunction, callData);
438         if (callType != CallType::None) {
439             element = call(exec, conversionFunction, callType, callData, element, exec->emptyList());
440             if (exec->hadException())
441                 return JSValue::encode(jsUndefined());
442         }
443         stringJoiner.append(*exec, element);
444         if (exec->hadException())
445             return JSValue::encode(jsUndefined());
446     }
447 #endif // !ENABLE(INTL)
448
449     return JSValue::encode(stringJoiner.join(*exec));
450 }
451
452 static inline bool isHole(double value)
453 {
454     return std::isnan(value);
455 }
456
457 static inline bool isHole(const WriteBarrier<Unknown>& value)
458 {
459     return !value;
460 }
461
462 template<typename T> static inline bool containsHole(T* data, unsigned length)
463 {
464     for (unsigned i = 0; i < length; ++i) {
465         if (isHole(data[i]))
466             return true;
467     }
468     return false;
469 }
470
471 static inline bool holesMustForwardToPrototype(ExecState& state, JSObject* object)
472 {
473     auto& vm = state.vm();
474     return object->structure(vm)->holesMustForwardToPrototype(vm);
475 }
476
477 static inline JSValue join(ExecState& state, JSObject* thisObject, StringView separator)
478 {
479     unsigned length = getLength(&state, thisObject);
480     if (state.hadException())
481         return jsUndefined();
482
483     switch (thisObject->indexingType()) {
484     case ALL_CONTIGUOUS_INDEXING_TYPES:
485     case ALL_INT32_INDEXING_TYPES: {
486         auto& butterfly = *thisObject->butterfly();
487         if (length > butterfly.publicLength())
488             break;
489         JSStringJoiner joiner(state, separator, length);
490         if (state.hadException())
491             return jsUndefined();
492         auto data = butterfly.contiguous().data();
493         bool holesKnownToBeOK = false;
494         for (unsigned i = 0; i < length; ++i) {
495             if (JSValue value = data[i].get()) {
496                 if (!joiner.appendWithoutSideEffects(state, value))
497                     goto generalCase;
498             } else {
499                 if (!holesKnownToBeOK) {
500                     if (holesMustForwardToPrototype(state, thisObject))
501                         goto generalCase;
502                     holesKnownToBeOK = true;
503                 }
504                 joiner.appendEmptyString();
505             }
506         }
507         return joiner.join(state);
508     }
509     case ALL_DOUBLE_INDEXING_TYPES: {
510         auto& butterfly = *thisObject->butterfly();
511         if (length > butterfly.publicLength())
512             break;
513         JSStringJoiner joiner(state, separator, length);
514         if (state.hadException())
515             return jsUndefined();
516         auto data = butterfly.contiguousDouble().data();
517         bool holesKnownToBeOK = false;
518         for (unsigned i = 0; i < length; ++i) {
519             double value = data[i];
520             if (!isHole(value))
521                 joiner.append(state, jsDoubleNumber(value));
522             else {
523                 if (!holesKnownToBeOK) {
524                     if (thisObject->structure(state.vm())->holesMustForwardToPrototype(state.vm()))
525                         goto generalCase;
526                     holesKnownToBeOK = true;
527                 }
528                 joiner.appendEmptyString();
529             }
530         }
531         return joiner.join(state);
532     }
533     case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
534         auto& storage = *thisObject->butterfly()->arrayStorage();
535         if (length > storage.vectorLength())
536             break;
537         if (storage.hasHoles() && thisObject->structure(state.vm())->holesMustForwardToPrototype(state.vm()))
538             break;
539         JSStringJoiner joiner(state, separator, length);
540         if (state.hadException())
541             return jsUndefined();
542         auto data = storage.vector().data();
543         for (unsigned i = 0; i < length; ++i) {
544             if (JSValue value = data[i].get()) {
545                 if (!joiner.appendWithoutSideEffects(state, value))
546                     goto generalCase;
547             } else
548                 joiner.appendEmptyString();
549         }
550         return joiner.join(state);
551     }
552     }
553
554 generalCase:
555     JSStringJoiner joiner(state, separator, length);
556     if (state.hadException())
557         return jsUndefined();
558     for (unsigned i = 0; i < length; ++i) {
559         JSValue element = thisObject->getIndex(&state, i);
560         if (state.hadException())
561             return jsUndefined();
562         joiner.append(state, element);
563         if (state.hadException())
564             return jsUndefined();
565     }
566     return joiner.join(state);
567 }
568
569 EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec)
570 {
571     JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
572     if (!thisObject)
573         return JSValue::encode(JSValue());
574
575     StringRecursionChecker checker(exec, thisObject);
576     if (JSValue earlyReturnValue = checker.earlyReturnValue())
577         return JSValue::encode(earlyReturnValue);
578
579     JSValue separatorValue = exec->argument(0);
580     if (separatorValue.isUndefined()) {
581         const LChar comma = ',';
582         return JSValue::encode(join(*exec, thisObject, { &comma, 1 }));
583     }
584
585     JSString* separator = separatorValue.toString(exec);
586     if (exec->hadException())
587         return JSValue::encode(jsUndefined());
588     return JSValue::encode(join(*exec, thisObject, separator->view(exec).get()));
589 }
590
591 EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState* exec)
592 {
593     JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
594     unsigned argCount = exec->argumentCount();
595     JSValue curArg = thisValue.toObject(exec);
596     if (!curArg)
597         return JSValue::encode(JSValue());
598     Checked<unsigned, RecordOverflow> finalArraySize = 0;
599
600     // We need to do species construction before geting the rest of the elements.
601     std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, curArg.getObject(), 0);
602     if (speciesResult.first == SpeciesConstructResult::Exception)
603         return JSValue::encode(jsUndefined());
604
605     JSArray* currentArray = nullptr;
606     JSArray* previousArray = nullptr;
607     for (unsigned i = 0; ; ++i) {
608         previousArray = currentArray;
609         currentArray = jsDynamicCast<JSArray*>(curArg);
610         if (currentArray) {
611             // Can't use JSArray::length here because this might be a RuntimeArray!
612             finalArraySize += getLength(exec, currentArray);
613             if (exec->hadException())
614                 return JSValue::encode(jsUndefined());
615         } else
616             ++finalArraySize;
617         if (i == argCount)
618             break;
619         curArg = exec->uncheckedArgument(i);
620     }
621
622     if (finalArraySize.hasOverflowed())
623         return JSValue::encode(throwOutOfMemoryError(exec));
624
625     if (speciesResult.first == SpeciesConstructResult::FastPath && argCount == 1 && previousArray && currentArray && finalArraySize.unsafeGet() < MIN_SPARSE_ARRAY_INDEX) {
626         IndexingType type = JSArray::fastConcatType(exec->vm(), *previousArray, *currentArray);
627         if (type != NonArray)
628             return previousArray->fastConcatWith(*exec, *currentArray);
629     }
630
631     ASSERT(speciesResult.first != SpeciesConstructResult::Exception);
632
633     JSObject* result;
634     if (speciesResult.first == SpeciesConstructResult::CreatedObject)
635         result = speciesResult.second;
636     else {
637         // We add the newTarget because the compiler gets confused between 0 being a number and a pointer.
638         result = constructEmptyArray(exec, nullptr, 0, JSValue());
639         if (exec->hadException())
640             return JSValue::encode(jsUndefined());
641     }
642
643     curArg = thisValue.toObject(exec);
644     ASSERT(!exec->hadException());
645     unsigned n = 0;
646     for (unsigned i = 0; ; ++i) {
647         if (JSArray* currentArray = jsDynamicCast<JSArray*>(curArg)) {
648             // Can't use JSArray::length here because this might be a RuntimeArray!
649             unsigned length = getLength(exec, currentArray);
650             if (exec->hadException())
651                 return JSValue::encode(jsUndefined());
652             for (unsigned k = 0; k < length; ++k) {
653                 JSValue v = getProperty(exec, currentArray, k);
654                 if (exec->hadException())
655                     return JSValue::encode(jsUndefined());
656                 if (v)
657                     result->putDirectIndex(exec, n, v);
658                 n++;
659             }
660         } else {
661             result->putDirectIndex(exec, n, curArg);
662             n++;
663         }
664         if (i == argCount)
665             break;
666         curArg = exec->uncheckedArgument(i);
667     }
668     setLength(exec, result, n);
669     return JSValue::encode(result);
670 }
671
672 EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState* exec)
673 {
674     JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
675
676     if (isJSArray(thisValue))
677         return JSValue::encode(asArray(thisValue)->pop(exec));
678
679     JSObject* thisObj = thisValue.toObject(exec);
680     if (!thisObj)
681         return JSValue::encode(JSValue());
682     unsigned length = getLength(exec, thisObj);
683     if (exec->hadException())
684         return JSValue::encode(jsUndefined());
685
686     JSValue result;
687     if (length == 0) {
688         putLength(exec, thisObj, jsNumber(length));
689         result = jsUndefined();
690     } else {
691         result = thisObj->get(exec, length - 1);
692         if (exec->hadException())
693             return JSValue::encode(jsUndefined());
694         if (!thisObj->methodTable(exec->vm())->deletePropertyByIndex(thisObj, exec, length - 1)) {
695             throwTypeError(exec, ASCIILiteral("Unable to delete property."));
696             return JSValue::encode(jsUndefined());
697         }
698         putLength(exec, thisObj, jsNumber(length - 1));
699     }
700     return JSValue::encode(result);
701 }
702
703 EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState* exec)
704 {
705     JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
706
707     if (isJSArray(thisValue) && exec->argumentCount() == 1) {
708         JSArray* array = asArray(thisValue);
709         array->push(exec, exec->uncheckedArgument(0));
710         return JSValue::encode(jsNumber(array->length()));
711     }
712     
713     JSObject* thisObj = thisValue.toObject(exec);
714     if (!thisObj)
715         return JSValue::encode(JSValue());
716     unsigned length = getLength(exec, thisObj);
717     if (exec->hadException())
718         return JSValue::encode(jsUndefined());
719
720     for (unsigned n = 0; n < exec->argumentCount(); n++) {
721         // Check for integer overflow; where safe we can do a fast put by index.
722         if (length + n >= length)
723             thisObj->methodTable()->putByIndex(thisObj, exec, length + n, exec->uncheckedArgument(n), true);
724         else {
725             PutPropertySlot slot(thisObj);
726             Identifier propertyName = Identifier::fromString(exec, JSValue(static_cast<int64_t>(length) + static_cast<int64_t>(n)).toWTFString(exec));
727             thisObj->methodTable()->put(thisObj, exec, propertyName, exec->uncheckedArgument(n), slot);
728         }
729         if (exec->hadException())
730             return JSValue::encode(jsUndefined());
731     }
732     
733     JSValue newLength(static_cast<int64_t>(length) + static_cast<int64_t>(exec->argumentCount()));
734     putLength(exec, thisObj, newLength);
735     return JSValue::encode(newLength);
736 }
737
738 EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState* exec)
739 {
740     JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
741     if (!thisObject)
742         return JSValue::encode(JSValue());
743
744     VM& vm = exec->vm();
745     unsigned length = getLength(exec, thisObject);
746     if (vm.exception())
747         return JSValue::encode(jsUndefined());
748
749     switch (thisObject->indexingType()) {
750     case ALL_CONTIGUOUS_INDEXING_TYPES:
751     case ALL_INT32_INDEXING_TYPES: {
752         auto& butterfly = *thisObject->butterfly();
753         if (length > butterfly.publicLength())
754             break;
755         auto data = butterfly.contiguous().data();
756         if (containsHole(data, length) && holesMustForwardToPrototype(*exec, thisObject))
757             break;
758         std::reverse(data, data + length);
759         return JSValue::encode(thisObject);
760     }
761     case ALL_DOUBLE_INDEXING_TYPES: {
762         auto& butterfly = *thisObject->butterfly();
763         if (length > butterfly.publicLength())
764             break;
765         auto data = butterfly.contiguousDouble().data();
766         if (containsHole(data, length) && holesMustForwardToPrototype(*exec, thisObject))
767             break;
768         std::reverse(data, data + length);
769         return JSValue::encode(thisObject);
770     }
771     case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
772         auto& storage = *thisObject->butterfly()->arrayStorage();
773         if (length > storage.vectorLength())
774             break;
775         if (storage.hasHoles() && holesMustForwardToPrototype(*exec, thisObject))
776             break;
777         auto data = storage.vector().data();
778         std::reverse(data, data + length);
779         return JSValue::encode(thisObject);
780     }
781     }
782
783     unsigned middle = length / 2;
784     for (unsigned lower = 0; lower < middle; lower++) {
785         unsigned upper = length - lower - 1;
786         bool lowerExists = thisObject->hasProperty(exec, lower);
787         if (vm.exception())
788             return JSValue::encode(jsUndefined());
789         JSValue lowerValue;
790         if (lowerExists)
791             lowerValue = thisObject->get(exec, lower);
792
793         bool upperExists = thisObject->hasProperty(exec, upper);
794         if (vm.exception())
795             return JSValue::encode(jsUndefined());
796         JSValue upperValue;
797         if (upperExists)
798             upperValue = thisObject->get(exec, upper);
799
800         if (upperExists) {
801             thisObject->putByIndexInline(exec, lower, upperValue, true);
802             if (vm.exception())
803                 return JSValue::encode(JSValue());
804         } else if (!thisObject->methodTable(vm)->deletePropertyByIndex(thisObject, exec, lower)) {
805             if (!vm.exception())
806                 throwTypeError(exec, ASCIILiteral("Unable to delete property."));
807             return JSValue::encode(JSValue());
808         }
809
810         if (lowerExists) {
811             thisObject->putByIndexInline(exec, upper, lowerValue, true);
812             if (vm.exception())
813                 return JSValue::encode(JSValue());
814         } else if (!thisObject->methodTable(vm)->deletePropertyByIndex(thisObject, exec, upper)) {
815             if (!vm.exception())
816                 throwTypeError(exec, ASCIILiteral("Unable to delete property."));
817             return JSValue::encode(JSValue());
818         }
819     }
820     return JSValue::encode(thisObject);
821 }
822
823 EncodedJSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState* exec)
824 {
825     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
826     if (!thisObj)
827         return JSValue::encode(JSValue());
828     unsigned length = getLength(exec, thisObj);
829     if (exec->hadException())
830         return JSValue::encode(jsUndefined());
831
832     JSValue result;
833     if (length == 0) {
834         putLength(exec, thisObj, jsNumber(length));
835         result = jsUndefined();
836     } else {
837         result = thisObj->getIndex(exec, 0);
838         shift<JSArray::ShiftCountForShift>(exec, thisObj, 0, 1, 0, length);
839         if (exec->hadException())
840             return JSValue::encode(jsUndefined());
841         putLength(exec, thisObj, jsNumber(length - 1));
842     }
843     return JSValue::encode(result);
844 }
845
846 EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec)
847 {
848     // http://developer.netscape.com/docs/manuals/js/client/jsref/array.htm#1193713 or 15.4.4.10
849     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
850     if (!thisObj)
851         return JSValue::encode(JSValue());
852     unsigned length = getLength(exec, thisObj);
853     if (exec->hadException())
854         return JSValue::encode(jsUndefined());
855
856     unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length);
857     unsigned end = argumentClampedIndexFromStartOrEnd(exec, 1, length, length);
858
859     std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, end - begin);
860     // We can only get an exception if we call some user function.
861     if (UNLIKELY(speciesResult.first == SpeciesConstructResult::Exception))
862         return JSValue::encode(jsUndefined());
863
864     if (LIKELY(speciesResult.first == SpeciesConstructResult::FastPath && isJSArray(thisObj) && length == getLength(exec, thisObj))) {
865         if (JSArray* result = asArray(thisObj)->fastSlice(*exec, begin, end - begin))
866             return JSValue::encode(result);
867     }
868
869     JSObject* result;
870     if (speciesResult.first == SpeciesConstructResult::CreatedObject)
871         result = speciesResult.second;
872     else
873         result = constructEmptyArray(exec, nullptr, end - begin);
874
875     unsigned n = 0;
876     for (unsigned k = begin; k < end; k++, n++) {
877         JSValue v = getProperty(exec, thisObj, k);
878         if (exec->hadException())
879             return JSValue::encode(jsUndefined());
880         if (v)
881             result->putDirectIndex(exec, n, v);
882     }
883     setLength(exec, result, n);
884     return JSValue::encode(result);
885 }
886
887 EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec)
888 {
889     // 15.4.4.12
890
891     VM& vm = exec->vm();
892
893     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
894     if (!thisObj)
895         return JSValue::encode(JSValue());
896     unsigned length = getLength(exec, thisObj);
897     if (exec->hadException())
898         return JSValue::encode(jsUndefined());
899
900     if (!exec->argumentCount()) {
901         std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, 0);
902         if (speciesResult.first == SpeciesConstructResult::Exception)
903             return JSValue::encode(jsUndefined());
904
905         JSObject* result;
906         if (speciesResult.first == SpeciesConstructResult::CreatedObject)
907             result = speciesResult.second;
908         else
909             result = constructEmptyArray(exec, nullptr);
910
911         setLength(exec, result, 0);
912         return JSValue::encode(result);
913     }
914
915     unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length);
916
917     unsigned deleteCount = length - begin;
918     if (exec->argumentCount() > 1) {
919         double deleteDouble = exec->uncheckedArgument(1).toInteger(exec);
920         if (deleteDouble < 0)
921             deleteCount = 0;
922         else if (deleteDouble > length - begin)
923             deleteCount = length - begin;
924         else
925             deleteCount = static_cast<unsigned>(deleteDouble);
926     }
927
928     std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, deleteCount);
929     if (speciesResult.first == SpeciesConstructResult::Exception)
930         return JSValue::encode(jsUndefined());
931
932     JSObject* result = nullptr;
933     if (speciesResult.first == SpeciesConstructResult::FastPath && isJSArray(thisObj) && length == getLength(exec, thisObj))
934         result = asArray(thisObj)->fastSlice(*exec, begin, deleteCount);
935
936     if (!result) {
937         if (speciesResult.first == SpeciesConstructResult::CreatedObject) {
938             result = speciesResult.second;
939             
940             for (unsigned k = 0; k < deleteCount; ++k) {
941                 JSValue v = getProperty(exec, thisObj, k + begin);
942                 if (exec->hadException())
943                     return JSValue::encode(jsUndefined());
944                 result->putByIndexInline(exec, k, v, true);
945                 if (exec->hadException())
946                     return JSValue::encode(jsUndefined());
947             }
948         } else {
949             result = JSArray::tryCreateUninitialized(vm, exec->lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), deleteCount);
950             if (!result)
951                 return JSValue::encode(throwOutOfMemoryError(exec));
952             
953             for (unsigned k = 0; k < deleteCount; ++k) {
954                 JSValue v = getProperty(exec, thisObj, k + begin);
955                 if (exec->hadException())
956                     return JSValue::encode(jsUndefined());
957                 result->initializeIndex(vm, k, v);
958             }
959         }
960     }
961
962     unsigned additionalArgs = std::max<int>(exec->argumentCount() - 2, 0);
963     if (additionalArgs < deleteCount) {
964         shift<JSArray::ShiftCountForSplice>(exec, thisObj, begin, deleteCount, additionalArgs, length);
965         if (exec->hadException())
966             return JSValue::encode(jsUndefined());
967     } else if (additionalArgs > deleteCount) {
968         unshift<JSArray::ShiftCountForSplice>(exec, thisObj, begin, deleteCount, additionalArgs, length);
969         if (exec->hadException())
970             return JSValue::encode(jsUndefined());
971     }
972     for (unsigned k = 0; k < additionalArgs; ++k) {
973         thisObj->putByIndexInline(exec, k + begin, exec->uncheckedArgument(k + 2), true);
974         if (exec->hadException())
975             return JSValue::encode(jsUndefined());
976     }
977
978     setLength(exec, thisObj, length - deleteCount + additionalArgs);
979     return JSValue::encode(result);
980 }
981
982 EncodedJSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState* exec)
983 {
984     // 15.4.4.13
985
986     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
987     if (!thisObj)
988         return JSValue::encode(JSValue());
989     unsigned length = getLength(exec, thisObj);
990     if (exec->hadException())
991         return JSValue::encode(jsUndefined());
992
993     unsigned nrArgs = exec->argumentCount();
994     if (nrArgs) {
995         unshift<JSArray::ShiftCountForShift>(exec, thisObj, 0, 0, nrArgs, length);
996         if (exec->hadException())
997             return JSValue::encode(jsUndefined());
998     }
999     for (unsigned k = 0; k < nrArgs; ++k) {
1000         thisObj->putByIndexInline(exec, k, exec->uncheckedArgument(k), true);
1001         if (exec->hadException())
1002             return JSValue::encode(jsUndefined());
1003     }
1004     JSValue result = jsNumber(length + nrArgs);
1005     putLength(exec, thisObj, result);
1006     return JSValue::encode(result);
1007 }
1008
1009 EncodedJSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState* exec)
1010 {
1011     // 15.4.4.14
1012     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
1013     if (!thisObj)
1014         return JSValue::encode(JSValue());
1015     unsigned length = getLength(exec, thisObj);
1016     if (exec->hadException())
1017         return JSValue::encode(jsUndefined());
1018
1019     unsigned index = argumentClampedIndexFromStartOrEnd(exec, 1, length);
1020     JSValue searchElement = exec->argument(0);
1021     for (; index < length; ++index) {
1022         JSValue e = getProperty(exec, thisObj, index);
1023         if (exec->hadException())
1024             return JSValue::encode(jsUndefined());
1025         if (!e)
1026             continue;
1027         if (JSValue::strictEqual(exec, searchElement, e))
1028             return JSValue::encode(jsNumber(index));
1029     }
1030
1031     return JSValue::encode(jsNumber(-1));
1032 }
1033
1034 EncodedJSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState* exec)
1035 {
1036     // 15.4.4.15
1037     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
1038     if (!thisObj)
1039         return JSValue::encode(JSValue());
1040     unsigned length = getLength(exec, thisObj);
1041     if (!length)
1042         return JSValue::encode(jsNumber(-1));
1043
1044     unsigned index = length - 1;
1045     if (exec->argumentCount() >= 2) {
1046         JSValue fromValue = exec->uncheckedArgument(1);
1047         double fromDouble = fromValue.toInteger(exec);
1048         if (fromDouble < 0) {
1049             fromDouble += length;
1050             if (fromDouble < 0)
1051                 return JSValue::encode(jsNumber(-1));
1052         }
1053         if (fromDouble < length)
1054             index = static_cast<unsigned>(fromDouble);
1055     }
1056
1057     JSValue searchElement = exec->argument(0);
1058     do {
1059         RELEASE_ASSERT(index < length);
1060         JSValue e = getProperty(exec, thisObj, index);
1061         if (exec->hadException())
1062             return JSValue::encode(jsUndefined());
1063         if (!e)
1064             continue;
1065         if (JSValue::strictEqual(exec, searchElement, e))
1066             return JSValue::encode(jsNumber(index));
1067     } while (index--);
1068
1069     return JSValue::encode(jsNumber(-1));
1070 }
1071
1072 // -------------------- ArrayPrototype.constructor Watchpoint ------------------
1073
1074 class ArrayPrototypeAdaptiveInferredPropertyWatchpoint : public AdaptiveInferredPropertyValueWatchpointBase {
1075 public:
1076     typedef AdaptiveInferredPropertyValueWatchpointBase Base;
1077     ArrayPrototypeAdaptiveInferredPropertyWatchpoint(const ObjectPropertyCondition&, ArrayPrototype*);
1078
1079 private:
1080     void handleFire(const FireDetail&) override;
1081
1082     ArrayPrototype* m_arrayPrototype;
1083 };
1084
1085 void ArrayPrototype::setConstructor(VM& vm, JSObject* constructorProperty, unsigned attributes)
1086 {
1087     putDirectWithoutTransition(vm, vm.propertyNames->constructor, constructorProperty, attributes);
1088
1089     // Do the watchpoint on our constructor property
1090     PropertyOffset offset = this->structure()->get(vm, vm.propertyNames->constructor);
1091     ASSERT(isValidOffset(offset));
1092     this->structure()->startWatchingPropertyForReplacements(vm, offset);
1093
1094     ObjectPropertyCondition condition = ObjectPropertyCondition::equivalence(vm, this, this, vm.propertyNames->constructor.impl(), constructorProperty);
1095     ASSERT(condition.isWatchable());
1096
1097     m_constructorWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(condition, this);
1098     m_constructorWatchpoint->install();
1099     
1100     // Do the watchpoint on the constructor's Symbol.species property
1101     offset = constructorProperty->structure()->get(vm, vm.propertyNames->speciesSymbol);
1102     ASSERT(isValidOffset(offset));
1103     constructorProperty->structure()->startWatchingPropertyForReplacements(vm, offset);
1104
1105     ASSERT(constructorProperty->getDirect(offset).isGetterSetter());
1106     condition = ObjectPropertyCondition::equivalence(vm, this, constructorProperty, vm.propertyNames->speciesSymbol.impl(), constructorProperty->getDirect(offset));
1107     ASSERT(condition.isWatchable());
1108
1109     m_constructorSpeciesWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(condition, this);
1110     m_constructorSpeciesWatchpoint->install();
1111 }
1112
1113 ArrayPrototypeAdaptiveInferredPropertyWatchpoint::ArrayPrototypeAdaptiveInferredPropertyWatchpoint(const ObjectPropertyCondition& key, ArrayPrototype* prototype)
1114     : Base(key)
1115     , m_arrayPrototype(prototype)
1116 {
1117 }
1118
1119 void ArrayPrototypeAdaptiveInferredPropertyWatchpoint::handleFire(const FireDetail& detail)
1120 {
1121     StringPrintStream out;
1122     out.print("ArrayPrototype adaption of ", key(), " failed: ", detail);
1123
1124     StringFireDetail stringDetail(out.toCString().data());
1125
1126     m_arrayPrototype->m_didChangeConstructorOrSpeciesProperties = true;
1127 }
1128
1129 } // namespace JSC