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