57a77ae6dc3d4b4cdda237a71ee34421006f4e67
[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 "JSArrayInlines.h"
38 #include "JSArrayIterator.h"
39 #include "JSCBuiltins.h"
40 #include "JSCInlines.h"
41 #include "JSStringBuilder.h"
42 #include "JSStringJoiner.h"
43 #include "Lookup.h"
44 #include "ObjectConstructor.h"
45 #include "ObjectPrototype.h"
46 #include "StringRecursionChecker.h"
47 #include <algorithm>
48 #include <wtf/Assertions.h>
49 #include <wtf/HashSet.h>
50
51 namespace JSC {
52
53 EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(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_BUILTIN_FUNCTION_WITHOUT_TRANSITION("concat", arrayPrototypeConcatCodeGenerator, DontEnum);
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 arrayProtoFuncPop(ExecState* exec)
592 {
593     JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
594
595     if (isJSArray(thisValue))
596         return JSValue::encode(asArray(thisValue)->pop(exec));
597
598     JSObject* thisObj = thisValue.toObject(exec);
599     if (!thisObj)
600         return JSValue::encode(JSValue());
601     unsigned length = getLength(exec, thisObj);
602     if (exec->hadException())
603         return JSValue::encode(jsUndefined());
604
605     JSValue result;
606     if (length == 0) {
607         putLength(exec, thisObj, jsNumber(length));
608         result = jsUndefined();
609     } else {
610         result = thisObj->get(exec, length - 1);
611         if (exec->hadException())
612             return JSValue::encode(jsUndefined());
613         if (!thisObj->methodTable(exec->vm())->deletePropertyByIndex(thisObj, exec, length - 1)) {
614             throwTypeError(exec, ASCIILiteral("Unable to delete property."));
615             return JSValue::encode(jsUndefined());
616         }
617         putLength(exec, thisObj, jsNumber(length - 1));
618     }
619     return JSValue::encode(result);
620 }
621
622 EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState* exec)
623 {
624     JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
625
626     if (isJSArray(thisValue) && exec->argumentCount() == 1) {
627         JSArray* array = asArray(thisValue);
628         array->push(exec, exec->uncheckedArgument(0));
629         return JSValue::encode(jsNumber(array->length()));
630     }
631     
632     JSObject* thisObj = thisValue.toObject(exec);
633     if (!thisObj)
634         return JSValue::encode(JSValue());
635     unsigned length = getLength(exec, thisObj);
636     if (exec->hadException())
637         return JSValue::encode(jsUndefined());
638
639     for (unsigned n = 0; n < exec->argumentCount(); n++) {
640         // Check for integer overflow; where safe we can do a fast put by index.
641         if (length + n >= length)
642             thisObj->methodTable()->putByIndex(thisObj, exec, length + n, exec->uncheckedArgument(n), true);
643         else {
644             PutPropertySlot slot(thisObj);
645             Identifier propertyName = Identifier::fromString(exec, JSValue(static_cast<int64_t>(length) + static_cast<int64_t>(n)).toWTFString(exec));
646             thisObj->methodTable()->put(thisObj, exec, propertyName, exec->uncheckedArgument(n), slot);
647         }
648         if (exec->hadException())
649             return JSValue::encode(jsUndefined());
650     }
651     
652     JSValue newLength(static_cast<int64_t>(length) + static_cast<int64_t>(exec->argumentCount()));
653     putLength(exec, thisObj, newLength);
654     return JSValue::encode(newLength);
655 }
656
657 EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState* exec)
658 {
659     JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
660     if (!thisObject)
661         return JSValue::encode(JSValue());
662
663     VM& vm = exec->vm();
664     unsigned length = getLength(exec, thisObject);
665     if (vm.exception())
666         return JSValue::encode(jsUndefined());
667
668     switch (thisObject->indexingType()) {
669     case ALL_CONTIGUOUS_INDEXING_TYPES:
670     case ALL_INT32_INDEXING_TYPES: {
671         auto& butterfly = *thisObject->butterfly();
672         if (length > butterfly.publicLength())
673             break;
674         auto data = butterfly.contiguous().data();
675         if (containsHole(data, length) && holesMustForwardToPrototype(*exec, thisObject))
676             break;
677         std::reverse(data, data + length);
678         return JSValue::encode(thisObject);
679     }
680     case ALL_DOUBLE_INDEXING_TYPES: {
681         auto& butterfly = *thisObject->butterfly();
682         if (length > butterfly.publicLength())
683             break;
684         auto data = butterfly.contiguousDouble().data();
685         if (containsHole(data, length) && holesMustForwardToPrototype(*exec, thisObject))
686             break;
687         std::reverse(data, data + length);
688         return JSValue::encode(thisObject);
689     }
690     case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
691         auto& storage = *thisObject->butterfly()->arrayStorage();
692         if (length > storage.vectorLength())
693             break;
694         if (storage.hasHoles() && holesMustForwardToPrototype(*exec, thisObject))
695             break;
696         auto data = storage.vector().data();
697         std::reverse(data, data + length);
698         return JSValue::encode(thisObject);
699     }
700     }
701
702     unsigned middle = length / 2;
703     for (unsigned lower = 0; lower < middle; lower++) {
704         unsigned upper = length - lower - 1;
705         bool lowerExists = thisObject->hasProperty(exec, lower);
706         if (vm.exception())
707             return JSValue::encode(jsUndefined());
708         JSValue lowerValue;
709         if (lowerExists)
710             lowerValue = thisObject->get(exec, lower);
711
712         bool upperExists = thisObject->hasProperty(exec, upper);
713         if (vm.exception())
714             return JSValue::encode(jsUndefined());
715         JSValue upperValue;
716         if (upperExists)
717             upperValue = thisObject->get(exec, upper);
718
719         if (upperExists) {
720             thisObject->putByIndexInline(exec, lower, upperValue, true);
721             if (vm.exception())
722                 return JSValue::encode(JSValue());
723         } else if (!thisObject->methodTable(vm)->deletePropertyByIndex(thisObject, exec, lower)) {
724             if (!vm.exception())
725                 throwTypeError(exec, ASCIILiteral("Unable to delete property."));
726             return JSValue::encode(JSValue());
727         }
728
729         if (lowerExists) {
730             thisObject->putByIndexInline(exec, upper, lowerValue, true);
731             if (vm.exception())
732                 return JSValue::encode(JSValue());
733         } else if (!thisObject->methodTable(vm)->deletePropertyByIndex(thisObject, exec, upper)) {
734             if (!vm.exception())
735                 throwTypeError(exec, ASCIILiteral("Unable to delete property."));
736             return JSValue::encode(JSValue());
737         }
738     }
739     return JSValue::encode(thisObject);
740 }
741
742 EncodedJSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState* exec)
743 {
744     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
745     if (!thisObj)
746         return JSValue::encode(JSValue());
747     unsigned length = getLength(exec, thisObj);
748     if (exec->hadException())
749         return JSValue::encode(jsUndefined());
750
751     JSValue result;
752     if (length == 0) {
753         putLength(exec, thisObj, jsNumber(length));
754         result = jsUndefined();
755     } else {
756         result = thisObj->getIndex(exec, 0);
757         shift<JSArray::ShiftCountForShift>(exec, thisObj, 0, 1, 0, length);
758         if (exec->hadException())
759             return JSValue::encode(jsUndefined());
760         putLength(exec, thisObj, jsNumber(length - 1));
761     }
762     return JSValue::encode(result);
763 }
764
765 EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec)
766 {
767     // http://developer.netscape.com/docs/manuals/js/client/jsref/array.htm#1193713 or 15.4.4.10
768     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
769     if (!thisObj)
770         return JSValue::encode(JSValue());
771     unsigned length = getLength(exec, thisObj);
772     if (exec->hadException())
773         return JSValue::encode(jsUndefined());
774
775     unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length);
776     unsigned end = argumentClampedIndexFromStartOrEnd(exec, 1, length, length);
777
778     std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, end - begin);
779     // We can only get an exception if we call some user function.
780     if (UNLIKELY(speciesResult.first == SpeciesConstructResult::Exception))
781         return JSValue::encode(jsUndefined());
782
783     if (LIKELY(speciesResult.first == SpeciesConstructResult::FastPath && isJSArray(thisObj) && length == getLength(exec, thisObj))) {
784         if (JSArray* result = asArray(thisObj)->fastSlice(*exec, begin, end - begin))
785             return JSValue::encode(result);
786     }
787
788     JSObject* result;
789     if (speciesResult.first == SpeciesConstructResult::CreatedObject)
790         result = speciesResult.second;
791     else
792         result = constructEmptyArray(exec, nullptr, end - begin);
793
794     unsigned n = 0;
795     for (unsigned k = begin; k < end; k++, n++) {
796         JSValue v = getProperty(exec, thisObj, k);
797         if (exec->hadException())
798             return JSValue::encode(jsUndefined());
799         if (v)
800             result->putDirectIndex(exec, n, v);
801     }
802     setLength(exec, result, n);
803     return JSValue::encode(result);
804 }
805
806 EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec)
807 {
808     // 15.4.4.12
809
810     VM& vm = exec->vm();
811
812     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
813     if (!thisObj)
814         return JSValue::encode(JSValue());
815     unsigned length = getLength(exec, thisObj);
816     if (exec->hadException())
817         return JSValue::encode(jsUndefined());
818
819     if (!exec->argumentCount()) {
820         std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, 0);
821         if (speciesResult.first == SpeciesConstructResult::Exception)
822             return JSValue::encode(jsUndefined());
823
824         JSObject* result;
825         if (speciesResult.first == SpeciesConstructResult::CreatedObject)
826             result = speciesResult.second;
827         else
828             result = constructEmptyArray(exec, nullptr);
829
830         setLength(exec, result, 0);
831         return JSValue::encode(result);
832     }
833
834     unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length);
835
836     unsigned deleteCount = length - begin;
837     if (exec->argumentCount() > 1) {
838         double deleteDouble = exec->uncheckedArgument(1).toInteger(exec);
839         if (deleteDouble < 0)
840             deleteCount = 0;
841         else if (deleteDouble > length - begin)
842             deleteCount = length - begin;
843         else
844             deleteCount = static_cast<unsigned>(deleteDouble);
845     }
846
847     std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, deleteCount);
848     if (speciesResult.first == SpeciesConstructResult::Exception)
849         return JSValue::encode(jsUndefined());
850
851     JSObject* result = nullptr;
852     if (speciesResult.first == SpeciesConstructResult::FastPath && isJSArray(thisObj) && length == getLength(exec, thisObj))
853         result = asArray(thisObj)->fastSlice(*exec, begin, deleteCount);
854
855     if (!result) {
856         if (speciesResult.first == SpeciesConstructResult::CreatedObject) {
857             result = speciesResult.second;
858             
859             for (unsigned k = 0; k < deleteCount; ++k) {
860                 JSValue v = getProperty(exec, thisObj, k + begin);
861                 if (exec->hadException())
862                     return JSValue::encode(jsUndefined());
863                 result->putByIndexInline(exec, k, v, true);
864                 if (exec->hadException())
865                     return JSValue::encode(jsUndefined());
866             }
867         } else {
868             result = JSArray::tryCreateUninitialized(vm, exec->lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), deleteCount);
869             if (!result)
870                 return JSValue::encode(throwOutOfMemoryError(exec));
871             
872             for (unsigned k = 0; k < deleteCount; ++k) {
873                 JSValue v = getProperty(exec, thisObj, k + begin);
874                 if (exec->hadException())
875                     return JSValue::encode(jsUndefined());
876                 result->initializeIndex(vm, k, v);
877             }
878         }
879     }
880
881     unsigned additionalArgs = std::max<int>(exec->argumentCount() - 2, 0);
882     if (additionalArgs < deleteCount) {
883         shift<JSArray::ShiftCountForSplice>(exec, thisObj, begin, deleteCount, additionalArgs, length);
884         if (exec->hadException())
885             return JSValue::encode(jsUndefined());
886     } else if (additionalArgs > deleteCount) {
887         unshift<JSArray::ShiftCountForSplice>(exec, thisObj, begin, deleteCount, additionalArgs, length);
888         if (exec->hadException())
889             return JSValue::encode(jsUndefined());
890     }
891     for (unsigned k = 0; k < additionalArgs; ++k) {
892         thisObj->putByIndexInline(exec, k + begin, exec->uncheckedArgument(k + 2), true);
893         if (exec->hadException())
894             return JSValue::encode(jsUndefined());
895     }
896
897     setLength(exec, thisObj, length - deleteCount + additionalArgs);
898     return JSValue::encode(result);
899 }
900
901 EncodedJSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState* exec)
902 {
903     // 15.4.4.13
904
905     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
906     if (!thisObj)
907         return JSValue::encode(JSValue());
908     unsigned length = getLength(exec, thisObj);
909     if (exec->hadException())
910         return JSValue::encode(jsUndefined());
911
912     unsigned nrArgs = exec->argumentCount();
913     if (nrArgs) {
914         unshift<JSArray::ShiftCountForShift>(exec, thisObj, 0, 0, nrArgs, length);
915         if (exec->hadException())
916             return JSValue::encode(jsUndefined());
917     }
918     for (unsigned k = 0; k < nrArgs; ++k) {
919         thisObj->putByIndexInline(exec, k, exec->uncheckedArgument(k), true);
920         if (exec->hadException())
921             return JSValue::encode(jsUndefined());
922     }
923     JSValue result = jsNumber(length + nrArgs);
924     putLength(exec, thisObj, result);
925     return JSValue::encode(result);
926 }
927
928 EncodedJSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState* exec)
929 {
930     // 15.4.4.14
931     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
932     if (!thisObj)
933         return JSValue::encode(JSValue());
934     unsigned length = getLength(exec, thisObj);
935     if (exec->hadException())
936         return JSValue::encode(jsUndefined());
937
938     unsigned index = argumentClampedIndexFromStartOrEnd(exec, 1, length);
939     JSValue searchElement = exec->argument(0);
940     for (; index < length; ++index) {
941         JSValue e = getProperty(exec, thisObj, index);
942         if (exec->hadException())
943             return JSValue::encode(jsUndefined());
944         if (!e)
945             continue;
946         if (JSValue::strictEqual(exec, searchElement, e))
947             return JSValue::encode(jsNumber(index));
948     }
949
950     return JSValue::encode(jsNumber(-1));
951 }
952
953 EncodedJSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState* exec)
954 {
955     // 15.4.4.15
956     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
957     if (!thisObj)
958         return JSValue::encode(JSValue());
959     unsigned length = getLength(exec, thisObj);
960     if (!length)
961         return JSValue::encode(jsNumber(-1));
962
963     unsigned index = length - 1;
964     if (exec->argumentCount() >= 2) {
965         JSValue fromValue = exec->uncheckedArgument(1);
966         double fromDouble = fromValue.toInteger(exec);
967         if (fromDouble < 0) {
968             fromDouble += length;
969             if (fromDouble < 0)
970                 return JSValue::encode(jsNumber(-1));
971         }
972         if (fromDouble < length)
973             index = static_cast<unsigned>(fromDouble);
974     }
975
976     JSValue searchElement = exec->argument(0);
977     do {
978         RELEASE_ASSERT(index < length);
979         JSValue e = getProperty(exec, thisObj, index);
980         if (exec->hadException())
981             return JSValue::encode(jsUndefined());
982         if (!e)
983             continue;
984         if (JSValue::strictEqual(exec, searchElement, e))
985             return JSValue::encode(jsNumber(index));
986     } while (index--);
987
988     return JSValue::encode(jsNumber(-1));
989 }
990
991 EncodedJSValue JSC_HOST_CALL arrayProtoPrivateFuncIsJSArray(ExecState* exec)
992 {
993     JSValue value = exec->uncheckedArgument(0);
994     if (value.isObject())
995         return JSValue::encode(jsBoolean(value.getObject()->type() == ArrayType));
996     return JSValue::encode(jsBoolean(false));
997 }
998
999 inline bool moveElements(ExecState* exec, VM& vm, JSArray* target, unsigned targetOffset, JSArray* source, unsigned sourceLength)
1000 {
1001     ASSERT(!hasAnyArrayStorage(source->indexingType()));
1002     for (unsigned i = 0; i < sourceLength; ++i) {
1003         JSValue value = getProperty(exec, source, i);
1004         if (vm.exception())
1005             return false;
1006         if (value) {
1007             target->putDirectIndex(exec, targetOffset + i, value);
1008             if (vm.exception())
1009                 return false;
1010         }
1011     }
1012     return true;
1013 }
1014
1015 EncodedJSValue JSC_HOST_CALL arrayProtoPrivateFuncConcatMemcpy(ExecState* exec)
1016 {
1017     ASSERT(exec->argumentCount() == 2);
1018     VM& vm = exec->vm();
1019
1020     JSArray* firstArray = jsCast<JSArray*>(exec->uncheckedArgument(0));
1021     JSArray* secondArray = jsCast<JSArray*>(exec->uncheckedArgument(1));
1022
1023     if (!firstArray->canFastCopy(vm, secondArray))
1024         return JSValue::encode(jsNull());
1025
1026     Butterfly* firstButterfly = firstArray->butterfly();
1027     Butterfly* secondButterfly = secondArray->butterfly();
1028
1029     unsigned firstArraySize = firstButterfly->publicLength();
1030     unsigned secondArraySize = secondButterfly->publicLength();
1031
1032     IndexingType type = firstArray->memCopyWithIndexingType(secondArray->indexingType());
1033     if (type == NonArray || firstArraySize + secondArraySize >= MIN_SPARSE_ARRAY_INDEX) {
1034         JSArray* result = constructEmptyArray(exec, nullptr, firstArraySize + secondArraySize);
1035         if (vm.exception())
1036             return JSValue::encode(JSValue());
1037
1038         if (!moveElements(exec, vm, result, 0, firstArray, firstArraySize)
1039             || !moveElements(exec, vm, result, firstArraySize, secondArray, secondArraySize)) {
1040             ASSERT(vm.exception());
1041             return JSValue::encode(JSValue());
1042         }
1043
1044         return JSValue::encode(result);
1045     }
1046
1047     Structure* resultStructure = exec->lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(type);
1048     JSArray* result = JSArray::tryCreateUninitialized(vm, resultStructure, firstArraySize + secondArraySize);
1049     if (!result)
1050         return JSValue::encode(throwOutOfMemoryError(exec));
1051
1052     if (type == ArrayWithDouble) {
1053         double* buffer = result->butterfly()->contiguousDouble().data();
1054         memcpy(buffer, firstButterfly->contiguousDouble().data(), sizeof(JSValue) * firstArraySize);
1055         memcpy(buffer + firstArraySize, secondButterfly->contiguousDouble().data(), sizeof(JSValue) * secondArraySize);
1056     } else if (type != ArrayWithUndecided) {
1057         WriteBarrier<Unknown>* buffer = result->butterfly()->contiguous().data();
1058         memcpy(buffer, firstButterfly->contiguous().data(), sizeof(JSValue) * firstArraySize);
1059         memcpy(buffer + firstArraySize, secondButterfly->contiguous().data(), sizeof(JSValue) * secondArraySize);
1060     }
1061
1062     result->butterfly()->setPublicLength(firstArraySize + secondArraySize);
1063     return JSValue::encode(result);
1064 }
1065
1066 EncodedJSValue JSC_HOST_CALL arrayProtoPrivateFuncAppendMemcpy(ExecState* exec)
1067 {
1068     ASSERT(exec->argumentCount() == 2);
1069
1070     JSArray* resultArray = jsCast<JSArray*>(exec->uncheckedArgument(0));
1071     JSArray* otherArray = jsCast<JSArray*>(exec->uncheckedArgument(1));
1072
1073     return JSValue::encode(jsBoolean(resultArray->appendMemcpy(exec, exec->vm(), otherArray)));
1074 }
1075
1076
1077 // -------------------- ArrayPrototype.constructor Watchpoint ------------------
1078
1079 class ArrayPrototypeAdaptiveInferredPropertyWatchpoint : public AdaptiveInferredPropertyValueWatchpointBase {
1080 public:
1081     typedef AdaptiveInferredPropertyValueWatchpointBase Base;
1082     ArrayPrototypeAdaptiveInferredPropertyWatchpoint(const ObjectPropertyCondition&, ArrayPrototype*);
1083
1084 private:
1085     void handleFire(const FireDetail&) override;
1086
1087     ArrayPrototype* m_arrayPrototype;
1088 };
1089
1090 void ArrayPrototype::setConstructor(VM& vm, JSObject* constructorProperty, unsigned attributes)
1091 {
1092     putDirectWithoutTransition(vm, vm.propertyNames->constructor, constructorProperty, attributes);
1093
1094     // Do the watchpoint on our constructor property
1095     PropertyOffset offset = this->structure()->get(vm, vm.propertyNames->constructor);
1096     ASSERT(isValidOffset(offset));
1097     this->structure()->startWatchingPropertyForReplacements(vm, offset);
1098
1099     ObjectPropertyCondition condition = ObjectPropertyCondition::equivalence(vm, this, this, vm.propertyNames->constructor.impl(), constructorProperty);
1100     ASSERT(condition.isWatchable());
1101
1102     m_constructorWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(condition, this);
1103     m_constructorWatchpoint->install();
1104     
1105     // Do the watchpoint on the constructor's Symbol.species property
1106     offset = constructorProperty->structure()->get(vm, vm.propertyNames->speciesSymbol);
1107     ASSERT(isValidOffset(offset));
1108     constructorProperty->structure()->startWatchingPropertyForReplacements(vm, offset);
1109
1110     ASSERT(constructorProperty->getDirect(offset).isGetterSetter());
1111     condition = ObjectPropertyCondition::equivalence(vm, this, constructorProperty, vm.propertyNames->speciesSymbol.impl(), constructorProperty->getDirect(offset));
1112     ASSERT(condition.isWatchable());
1113
1114     m_constructorSpeciesWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(condition, this);
1115     m_constructorSpeciesWatchpoint->install();
1116 }
1117
1118 ArrayPrototypeAdaptiveInferredPropertyWatchpoint::ArrayPrototypeAdaptiveInferredPropertyWatchpoint(const ObjectPropertyCondition& key, ArrayPrototype* prototype)
1119     : Base(key)
1120     , m_arrayPrototype(prototype)
1121 {
1122 }
1123
1124 void ArrayPrototypeAdaptiveInferredPropertyWatchpoint::handleFire(const FireDetail& detail)
1125 {
1126     StringPrintStream out;
1127     out.print("ArrayPrototype adaption of ", key(), " failed: ", detail);
1128
1129     StringFireDetail stringDetail(out.toCString().data());
1130
1131     m_arrayPrototype->m_didChangeConstructorOrSpeciesProperties = true;
1132 }
1133
1134 } // namespace JSC