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