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