Add ThrowScope::release() calls at all call sites of jsMakeNontrivialString().
[WebKit-https.git] / Source / JavaScriptCore / runtime / ArrayPrototype.cpp
1 /*
2  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2003, 2007-2009, 2011, 2013, 2015-2016 Apple Inc. All rights reserved.
4  *  Copyright (C) 2003 Peter Kelly (pmk@post.com)
5  *  Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Lesser General Public
9  *  License as published by the Free Software Foundation; either
10  *  version 2 of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this library; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
20  *  USA
21  *
22  */
23
24 #include "config.h"
25 #include "ArrayPrototype.h"
26
27 #include "AdaptiveInferredPropertyValueWatchpointBase.h"
28 #include "ArrayConstructor.h"
29 #include "BuiltinNames.h"
30 #include "ButterflyInlines.h"
31 #include "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 "JSStringBuilder.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, 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     vm.heap.addFinalizer(prototype, destroy);
72     return prototype;
73 }
74
75 // ECMA 15.4.4
76 ArrayPrototype::ArrayPrototype(VM& vm, Structure* structure)
77     : JSArray(vm, structure, 0)
78 {
79 }
80
81 void ArrayPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
82 {
83     Base::finishCreation(vm);
84     ASSERT(inherits(info()));
85     vm.prototypeMap.addPrototype(this);
86
87     putDirectWithoutTransition(vm, vm.propertyNames->toString, globalObject->arrayProtoToStringFunction(), DontEnum);
88     putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().valuesPublicName(), globalObject->arrayProtoValuesFunction(), DontEnum);
89     putDirectWithoutTransition(vm, vm.propertyNames->iteratorSymbol, globalObject->arrayProtoValuesFunction(), DontEnum);
90
91     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->toLocaleString, arrayProtoFuncToLocaleString, DontEnum, 0);
92     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("concat", arrayPrototypeConcatCodeGenerator, DontEnum);
93     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("fill", arrayPrototypeFillCodeGenerator, DontEnum);
94     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->join, arrayProtoFuncJoin, DontEnum, 1);
95     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("pop", arrayProtoFuncPop, DontEnum, 0, ArrayPopIntrinsic);
96     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().pushPublicName(), arrayProtoFuncPush, DontEnum, 1, ArrayPushIntrinsic);
97     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().pushPrivateName(), arrayProtoFuncPush, DontEnum | DontDelete | ReadOnly, 1, ArrayPushIntrinsic);
98     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("reverse", arrayProtoFuncReverse, DontEnum, 0);
99     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().shiftPublicName(), arrayProtoFuncShift, DontEnum, 0);
100     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().shiftPrivateName(), arrayProtoFuncShift, DontEnum | DontDelete | ReadOnly, 0);
101     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->slice, arrayProtoFuncSlice, DontEnum, 2);
102     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("sort", arrayPrototypeSortCodeGenerator, DontEnum);
103     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("splice", arrayProtoFuncSplice, DontEnum, 2);
104     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("unshift", arrayProtoFuncUnShift, DontEnum, 1);
105     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("every", arrayPrototypeEveryCodeGenerator, DontEnum);
106     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("forEach", arrayPrototypeForEachCodeGenerator, DontEnum);
107     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("some", arrayPrototypeSomeCodeGenerator, DontEnum);
108     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("indexOf", arrayProtoFuncIndexOf, DontEnum, 1);
109     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("lastIndexOf", arrayProtoFuncLastIndexOf, DontEnum, 1);
110     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("filter", arrayPrototypeFilterCodeGenerator, DontEnum);
111     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("reduce", arrayPrototypeReduceCodeGenerator, DontEnum);
112     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("reduceRight", arrayPrototypeReduceRightCodeGenerator, DontEnum);
113     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("map", arrayPrototypeMapCodeGenerator, DontEnum);
114     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().entriesPublicName(), arrayPrototypeEntriesCodeGenerator, DontEnum);
115     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().keysPublicName(), arrayPrototypeKeysCodeGenerator, DontEnum);
116     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("find", arrayPrototypeFindCodeGenerator, DontEnum);
117     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("findIndex", arrayPrototypeFindIndexCodeGenerator, DontEnum);
118     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("includes", arrayPrototypeIncludesCodeGenerator, DontEnum);
119     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("copyWithin", arrayPrototypeCopyWithinCodeGenerator, DontEnum);
120
121     putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().entriesPrivateName(), getDirect(vm, vm.propertyNames->builtinNames().entriesPublicName()), ReadOnly);
122     putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().forEachPrivateName(), getDirect(vm, vm.propertyNames->builtinNames().forEachPublicName()), ReadOnly);
123     putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().keysPrivateName(), getDirect(vm, vm.propertyNames->builtinNames().keysPublicName()), ReadOnly);
124     putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().valuesPrivateName(), globalObject->arrayProtoValuesFunction(), ReadOnly);
125
126     JSObject* unscopables = constructEmptyObject(globalObject->globalExec(), globalObject->nullPrototypeObjectStructure());
127     const char* unscopableNames[] = {
128         "copyWithin",
129         "entries",
130         "fill",
131         "find",
132         "findIndex",
133         "includes",
134         "keys",
135         "values"
136     };
137     for (const char* unscopableName : unscopableNames)
138         unscopables->putDirect(vm, Identifier::fromString(&vm, unscopableName), jsBoolean(true));
139     putDirectWithoutTransition(vm, vm.propertyNames->unscopablesSymbol, unscopables, DontEnum | ReadOnly);
140 }
141
142 void ArrayPrototype::destroy(JSC::JSCell* cell)
143 {
144     ArrayPrototype* thisObject = static_cast<ArrayPrototype*>(cell);
145     thisObject->ArrayPrototype::~ArrayPrototype();
146 }
147
148 // ------------------------------ Array Functions ----------------------------
149
150 static ALWAYS_INLINE JSValue getProperty(ExecState* exec, JSObject* object, unsigned index)
151 {
152     if (JSValue result = object->tryGetIndexQuickly(index))
153         return result;
154     // 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     if (!object->getPropertySlot(exec, index, slot))
160         return JSValue();
161     if (UNLIKELY(slot.isTaintedByOpaqueObject()))
162         return object->get(exec, index);
163     return slot.getValue(exec, index);
164 }
165
166 static ALWAYS_INLINE bool putLength(ExecState* exec, VM& vm, JSObject* obj, JSValue value)
167 {
168     PutPropertySlot slot(obj);
169     return obj->methodTable()->put(obj, exec, vm.propertyNames->length, value, slot);
170 }
171
172 static ALWAYS_INLINE void setLength(ExecState* exec, VM& vm, JSObject* obj, unsigned value)
173 {
174     auto scope = DECLARE_THROW_SCOPE(vm);
175     static const bool throwException = true;
176     if (isJSArray(obj)) {
177         jsCast<JSArray*>(obj)->setLength(exec, value, throwException);
178         RETURN_IF_EXCEPTION(scope, void());
179     }
180     bool success = putLength(exec, vm, obj, jsNumber(value));
181     RETURN_IF_EXCEPTION(scope, void());
182     if (UNLIKELY(!success))
183         throwTypeError(exec, scope, ASCIILiteral(ReadonlyPropertyWriteError));
184 }
185
186 inline bool speciesWatchpointsValid(ExecState* exec, JSObject* thisObject)
187 {
188     ArrayPrototype* arrayPrototype = thisObject->globalObject()->arrayPrototype();
189     ArrayPrototype::SpeciesWatchpointStatus status = arrayPrototype->speciesWatchpointStatus();
190     if (UNLIKELY(status == ArrayPrototype::SpeciesWatchpointStatus::Uninitialized))
191         status = arrayPrototype->attemptToInitializeSpeciesWatchpoint(exec);
192     ASSERT(status != ArrayPrototype::SpeciesWatchpointStatus::Uninitialized);
193     return !thisObject->hasCustomProperties()
194         && arrayPrototype == thisObject->getPrototypeDirect()
195         && status == ArrayPrototype::SpeciesWatchpointStatus::Initialized;
196 }
197
198 enum class SpeciesConstructResult {
199     FastPath,
200     Exception,
201     CreatedObject
202 };
203
204 static ALWAYS_INLINE std::pair<SpeciesConstructResult, JSObject*> speciesConstructArray(ExecState* exec, JSObject* thisObject, unsigned length)
205 {
206     VM& vm = exec->vm();
207     auto scope = DECLARE_THROW_SCOPE(vm);
208
209     auto exceptionResult = [] () {
210         return std::make_pair(SpeciesConstructResult::Exception, nullptr);
211     };
212
213     // ECMA 9.4.2.3: https://tc39.github.io/ecma262/#sec-arrayspeciescreate
214     JSValue constructor = jsUndefined();
215     if (LIKELY(isArray(exec, thisObject))) {
216         // Fast path in the normal case where the user has not set an own constructor and the Array.prototype.constructor is normal.
217         // We need prototype check for subclasses of Array, which are Array objects but have a different prototype by default.
218         if (LIKELY(speciesWatchpointsValid(exec, thisObject)))
219             return std::make_pair(SpeciesConstructResult::FastPath, nullptr);
220
221         constructor = thisObject->get(exec, exec->propertyNames().constructor);
222         RETURN_IF_EXCEPTION(scope, exceptionResult());
223         if (constructor.isConstructor()) {
224             JSObject* constructorObject = jsCast<JSObject*>(constructor);
225             if (exec->lexicalGlobalObject() != constructorObject->globalObject())
226                 return std::make_pair(SpeciesConstructResult::FastPath, nullptr);;
227         }
228         if (constructor.isObject()) {
229             constructor = constructor.get(exec, exec->propertyNames().speciesSymbol);
230             RETURN_IF_EXCEPTION(scope, exceptionResult());
231             if (constructor.isNull())
232                 return std::make_pair(SpeciesConstructResult::FastPath, nullptr);;
233         }
234     } else
235         RETURN_IF_EXCEPTION(scope, exceptionResult());
236
237     if (constructor.isUndefined())
238         return std::make_pair(SpeciesConstructResult::FastPath, nullptr);
239
240     MarkedArgumentBuffer args;
241     args.append(jsNumber(length));
242     JSObject* newObject = construct(exec, constructor, args, "Species construction did not get a valid constructor");
243     RETURN_IF_EXCEPTION(scope, exceptionResult());
244     return std::make_pair(SpeciesConstructResult::CreatedObject, newObject);
245 }
246
247 static inline unsigned argumentClampedIndexFromStartOrEnd(ExecState* exec, int argument, unsigned length, unsigned undefinedValue = 0)
248 {
249     JSValue value = exec->argument(argument);
250     if (value.isUndefined())
251         return undefinedValue;
252
253     double indexDouble = value.toInteger(exec);
254     if (indexDouble < 0) {
255         indexDouble += length;
256         return indexDouble < 0 ? 0 : static_cast<unsigned>(indexDouble);
257     }
258     return indexDouble > length ? length : static_cast<unsigned>(indexDouble);
259 }
260
261 // The shift/unshift function implement the shift/unshift behaviour required
262 // by the corresponding array prototype methods, and by splice. In both cases,
263 // the methods are operating an an array or array like object.
264 //
265 //  header  currentCount  (remainder)
266 // [------][------------][-----------]
267 //  header  resultCount  (remainder)
268 // [------][-----------][-----------]
269 //
270 // The set of properties in the range 'header' must be unchanged. The set of
271 // properties in the range 'remainder' (where remainder = length - header -
272 // currentCount) will be shifted to the left or right as appropriate; in the
273 // case of shift this must be removing values, in the case of unshift this
274 // must be introducing new values.
275
276 template<JSArray::ShiftCountMode shiftCountMode>
277 void shift(ExecState* exec, JSObject* thisObj, unsigned header, unsigned currentCount, unsigned resultCount, unsigned length)
278 {
279     VM& vm = exec->vm();
280     auto scope = DECLARE_THROW_SCOPE(vm);
281
282     RELEASE_ASSERT(currentCount > resultCount);
283     unsigned count = currentCount - resultCount;
284
285     RELEASE_ASSERT(header <= length);
286     RELEASE_ASSERT(currentCount <= (length - header));
287
288     if (isJSArray(thisObj)) {
289         JSArray* array = asArray(thisObj);
290         if (array->length() == length && array->shiftCount<shiftCountMode>(exec, header, count))
291             return;
292     }
293
294     for (unsigned k = header; k < length - currentCount; ++k) {
295         unsigned from = k + currentCount;
296         unsigned to = k + resultCount;
297         if (JSValue value = getProperty(exec, thisObj, from)) {
298             RETURN_IF_EXCEPTION(scope, void());
299             thisObj->putByIndexInline(exec, to, value, true);
300             RETURN_IF_EXCEPTION(scope, void());
301         } else if (!thisObj->methodTable(vm)->deletePropertyByIndex(thisObj, exec, to)) {
302             throwTypeError(exec, scope, ASCIILiteral(UnableToDeletePropertyError));
303             return;
304         }
305     }
306     for (unsigned k = length; k > length - count; --k) {
307         if (!thisObj->methodTable(vm)->deletePropertyByIndex(thisObj, exec, k - 1)) {
308             throwTypeError(exec, scope, ASCIILiteral(UnableToDeletePropertyError));
309             return;
310         }
311     }
312 }
313
314 template<JSArray::ShiftCountMode shiftCountMode>
315 void unshift(ExecState* exec, JSObject* thisObj, unsigned header, unsigned currentCount, unsigned resultCount, unsigned length)
316 {
317     VM& vm = exec->vm();
318     auto scope = DECLARE_THROW_SCOPE(vm);
319
320     RELEASE_ASSERT(resultCount > currentCount);
321     unsigned count = resultCount - currentCount;
322
323     RELEASE_ASSERT(header <= length);
324     RELEASE_ASSERT(currentCount <= (length - header));
325
326     // Guard against overflow.
327     if (count > (UINT_MAX - length)) {
328         throwOutOfMemoryError(exec, scope);
329         return;
330     }
331
332     if (isJSArray(thisObj)) {
333         JSArray* array = asArray(thisObj);
334         if (array->length() == length && array->unshiftCount<shiftCountMode>(exec, header, count))
335             return;
336     }
337
338     for (unsigned k = length - currentCount; k > header; --k) {
339         unsigned from = k + currentCount - 1;
340         unsigned to = k + resultCount - 1;
341         if (JSValue value = getProperty(exec, thisObj, from)) {
342             RETURN_IF_EXCEPTION(scope, void());
343             thisObj->putByIndexInline(exec, to, value, true);
344         } else if (!thisObj->methodTable(vm)->deletePropertyByIndex(thisObj, exec, to)) {
345             throwTypeError(exec, scope, ASCIILiteral(UnableToDeletePropertyError));
346             return;
347         }
348         RETURN_IF_EXCEPTION(scope, void());
349     }
350 }
351
352 EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec)
353 {
354     VM& vm = exec->vm();
355     auto scope = DECLARE_THROW_SCOPE(vm);
356     JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
357
358     // 1. Let array be the result of calling ToObject on the this value.
359     JSObject* thisObject = thisValue.toObject(exec);
360     RETURN_IF_EXCEPTION(scope, encodedJSValue());
361     
362     // 2. Let func be the result of calling the [[Get]] internal method of array with argument "join".
363     JSValue function = JSValue(thisObject).get(exec, exec->propertyNames().join);
364     RETURN_IF_EXCEPTION(scope, encodedJSValue());
365
366     // 3. If IsCallable(func) is false, then let func be the standard built-in method Object.prototype.toString (15.2.4.2).
367     bool customJoinCase = false;
368     if (!function.isCell())
369         customJoinCase = true;
370     CallData callData;
371     CallType callType = getCallData(function, callData);
372     if (callType == CallType::None)
373         customJoinCase = true;
374
375     if (UNLIKELY(customJoinCase)) {
376         scope.release();
377         return JSValue::encode(jsMakeNontrivialString(exec, "[object ", thisObject->methodTable(vm)->className(thisObject), "]"));
378     }
379
380     // 4. Return the result of calling the [[Call]] internal method of func providing array as the this value and an empty arguments list.
381     if (!isJSArray(thisObject) || callType != CallType::Host || callData.native.function != arrayProtoFuncJoin)
382         return JSValue::encode(call(exec, function, callType, callData, thisObject, exec->emptyList()));
383
384     ASSERT(isJSArray(thisValue));
385     JSArray* thisArray = asArray(thisValue);
386
387     unsigned length = thisArray->length();
388
389     StringRecursionChecker checker(exec, thisArray);
390     if (JSValue earlyReturnValue = checker.earlyReturnValue())
391         return JSValue::encode(earlyReturnValue);
392
393     JSStringJoiner joiner(*exec, ',', length);
394     RETURN_IF_EXCEPTION(scope, encodedJSValue());
395
396     for (unsigned i = 0; i < length; ++i) {
397         JSValue element = thisArray->tryGetIndexQuickly(i);
398         if (!element) {
399             element = thisArray->get(exec, i);
400             RETURN_IF_EXCEPTION(scope, encodedJSValue());
401         }
402         joiner.append(*exec, element);
403         RETURN_IF_EXCEPTION(scope, encodedJSValue());
404     }
405
406     return JSValue::encode(joiner.join(*exec));
407 }
408
409 EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec)
410 {
411     VM& vm = exec->vm();
412     auto scope = DECLARE_THROW_SCOPE(vm);
413     JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
414
415     JSObject* thisObject = thisValue.toObject(exec);
416     RETURN_IF_EXCEPTION(scope, encodedJSValue());
417
418     unsigned length = getLength(exec, thisObject);
419     RETURN_IF_EXCEPTION(scope, encodedJSValue());
420
421     StringRecursionChecker checker(exec, thisObject);
422     if (JSValue earlyReturnValue = checker.earlyReturnValue())
423         return JSValue::encode(earlyReturnValue);
424
425     JSStringJoiner stringJoiner(*exec, ',', length);
426     RETURN_IF_EXCEPTION(scope, encodedJSValue());
427
428 #if ENABLE(INTL)
429     ArgList arguments(exec);
430     for (unsigned i = 0; i < length; ++i) {
431         JSValue element = thisObject->getIndex(exec, i);
432         RETURN_IF_EXCEPTION(scope, encodedJSValue());
433         if (element.isUndefinedOrNull())
434             element = jsEmptyString(exec);
435         else {
436             JSValue conversionFunction = element.get(exec, exec->propertyNames().toLocaleString);
437             RETURN_IF_EXCEPTION(scope, encodedJSValue());
438             CallData callData;
439             CallType callType = getCallData(conversionFunction, callData);
440             if (callType != CallType::None) {
441                 element = call(exec, conversionFunction, callType, callData, element, arguments);
442                 RETURN_IF_EXCEPTION(scope, encodedJSValue());
443             }
444         }
445         stringJoiner.append(*exec, element);
446         RETURN_IF_EXCEPTION(scope, encodedJSValue());
447     }
448 #else // !ENABLE(INTL)
449     for (unsigned i = 0; i < length; ++i) {
450         JSValue element = thisObject->getIndex(exec, i);
451         RETURN_IF_EXCEPTION(scope, encodedJSValue());
452         if (element.isUndefinedOrNull())
453             continue;
454         JSValue conversionFunction = element.get(exec, exec->propertyNames().toLocaleString);
455         RETURN_IF_EXCEPTION(scope, encodedJSValue());
456         CallData callData;
457         CallType callType = getCallData(conversionFunction, callData);
458         if (callType != CallType::None) {
459             element = call(exec, conversionFunction, callType, callData, element, exec->emptyList());
460             RETURN_IF_EXCEPTION(scope, encodedJSValue());
461         }
462         stringJoiner.append(*exec, element);
463         RETURN_IF_EXCEPTION(scope, encodedJSValue());
464     }
465 #endif // !ENABLE(INTL)
466
467     return JSValue::encode(stringJoiner.join(*exec));
468 }
469
470 static inline bool isHole(double value)
471 {
472     return std::isnan(value);
473 }
474
475 static inline bool isHole(const WriteBarrier<Unknown>& value)
476 {
477     return !value;
478 }
479
480 template<typename T> static inline bool containsHole(T* data, unsigned length)
481 {
482     for (unsigned i = 0; i < length; ++i) {
483         if (isHole(data[i]))
484             return true;
485     }
486     return false;
487 }
488
489 static inline bool holesMustForwardToPrototype(ExecState& state, JSObject* object)
490 {
491     auto& vm = state.vm();
492     return object->structure(vm)->holesMustForwardToPrototype(vm);
493 }
494
495 static JSValue slowJoin(ExecState& exec, JSObject* thisObject, JSString* separator, uint64_t length)
496 {
497     VM& vm = exec.vm();
498     auto scope = DECLARE_THROW_SCOPE(vm);
499
500     // 5. If len is zero, return the empty String.
501     if (!length)
502         return jsEmptyString(&exec);
503
504     // 6. Let element0 be Get(O, "0").
505     JSValue element0 = thisObject->getIndex(&exec, 0);
506     RETURN_IF_EXCEPTION(scope, JSValue());
507
508     // 7. If element0 is undefined or null, let R be the empty String; otherwise, let R be ? ToString(element0).
509     JSString* r = nullptr;
510     if (element0.isUndefinedOrNull())
511         r = jsEmptyString(&exec);
512     else
513         r = element0.toString(&exec);
514     RETURN_IF_EXCEPTION(scope, JSValue());
515
516     // 8. Let k be 1.
517     // 9. Repeat, while k < len
518     // 9.e Increase k by 1..
519     for (uint64_t k = 1; k < length; ++k) {
520         // b. Let element be ? Get(O, ! ToString(k)).
521         JSValue element = thisObject->get(&exec, Identifier::fromString(&exec, AtomicString::number(k)));
522         RETURN_IF_EXCEPTION(scope, JSValue());
523
524         // c. If element is undefined or null, let next be the empty String; otherwise, let next be ? ToString(element).
525         JSString* next = nullptr;
526         if (element.isUndefinedOrNull()) {
527             if (!separator->length())
528                 continue;
529             next = jsEmptyString(&exec);
530         } else
531             next = element.toString(&exec);
532         RETURN_IF_EXCEPTION(scope, JSValue());
533
534         // a. Let S be the String value produced by concatenating R and sep.
535         // d. Let R be a String value produced by concatenating S and next.
536         r = jsString(&exec, r, separator, next);
537         RETURN_IF_EXCEPTION(scope, JSValue());
538     }
539     // 10. Return R.
540     return r;
541 }
542
543 static inline bool canUseFastJoin(const JSObject* thisObject)
544 {
545     switch (thisObject->indexingType()) {
546     case ALL_CONTIGUOUS_INDEXING_TYPES:
547     case ALL_INT32_INDEXING_TYPES:
548     case ALL_DOUBLE_INDEXING_TYPES:
549         return true;
550     default:
551         break;
552     }
553     return false;
554 }
555
556 static inline JSValue fastJoin(ExecState& state, JSObject* thisObject, StringView separator, unsigned length)
557 {
558     VM& vm = state.vm();
559     auto scope = DECLARE_THROW_SCOPE(vm);
560
561     switch (thisObject->indexingType()) {
562     case ALL_CONTIGUOUS_INDEXING_TYPES:
563     case ALL_INT32_INDEXING_TYPES: {
564         auto& butterfly = *thisObject->butterfly();
565         if (length > butterfly.publicLength())
566             break;
567         JSStringJoiner joiner(state, separator, length);
568         RETURN_IF_EXCEPTION(scope, JSValue());
569         auto data = butterfly.contiguous().data();
570         bool holesKnownToBeOK = false;
571         for (unsigned i = 0; i < length; ++i) {
572             if (JSValue value = data[i].get()) {
573                 if (!joiner.appendWithoutSideEffects(state, value))
574                     goto generalCase;
575             } else {
576                 if (!holesKnownToBeOK) {
577                     if (holesMustForwardToPrototype(state, thisObject))
578                         goto generalCase;
579                     holesKnownToBeOK = true;
580                 }
581                 joiner.appendEmptyString();
582             }
583         }
584         return joiner.join(state);
585     }
586     case ALL_DOUBLE_INDEXING_TYPES: {
587         auto& butterfly = *thisObject->butterfly();
588         if (length > butterfly.publicLength())
589             break;
590         JSStringJoiner joiner(state, separator, length);
591         RETURN_IF_EXCEPTION(scope, JSValue());
592         auto data = butterfly.contiguousDouble().data();
593         bool holesKnownToBeOK = false;
594         for (unsigned i = 0; i < length; ++i) {
595             double value = data[i];
596             if (!isHole(value))
597                 joiner.append(state, jsDoubleNumber(value));
598             else {
599                 if (!holesKnownToBeOK) {
600                     if (thisObject->structure(vm)->holesMustForwardToPrototype(vm))
601                         goto generalCase;
602                     holesKnownToBeOK = true;
603                 }
604                 joiner.appendEmptyString();
605             }
606         }
607         return joiner.join(state);
608     }
609     }
610
611 generalCase:
612     JSStringJoiner joiner(state, separator, length);
613     RETURN_IF_EXCEPTION(scope, JSValue());
614     for (unsigned i = 0; i < length; ++i) {
615         JSValue element = thisObject->getIndex(&state, i);
616         RETURN_IF_EXCEPTION(scope, JSValue());
617         joiner.append(state, element);
618         RETURN_IF_EXCEPTION(scope, JSValue());
619     }
620     return joiner.join(state);
621 }
622
623 EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec)
624 {
625     VM& vm = exec->vm();
626     auto scope = DECLARE_THROW_SCOPE(vm);
627
628     // 1. Let O be ? ToObject(this value).
629     JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
630     if (!thisObject)
631         return JSValue::encode(JSValue());
632
633     StringRecursionChecker checker(exec, thisObject);
634     if (JSValue earlyReturnValue = checker.earlyReturnValue())
635         return JSValue::encode(earlyReturnValue);
636
637     // 2. Let len be ? ToLength(? Get(O, "length")).
638     double length = toLength(exec, thisObject);
639     RETURN_IF_EXCEPTION(scope, encodedJSValue());
640
641     // 3. If separator is undefined, let separator be the single-element String ",".
642     JSValue separatorValue = exec->argument(0);
643     if (separatorValue.isUndefined()) {
644         const LChar comma = ',';
645
646         if (UNLIKELY(length > std::numeric_limits<unsigned>::max() || !canUseFastJoin(thisObject))) {
647             uint64_t length64 = static_cast<uint64_t>(length);
648             ASSERT(static_cast<double>(length64) == length);
649             JSString* jsSeparator = jsSingleCharacterString(exec, comma);
650             RETURN_IF_EXCEPTION(scope, encodedJSValue());
651
652             return JSValue::encode(slowJoin(*exec, thisObject, jsSeparator, length64));
653         }
654
655         unsigned unsignedLength = static_cast<unsigned>(length);
656         ASSERT(static_cast<double>(unsignedLength) == length);
657         return JSValue::encode(fastJoin(*exec, thisObject, { &comma, 1 }, unsignedLength));
658     }
659
660     // 4. Let sep be ? ToString(separator).
661     JSString* jsSeparator = separatorValue.toString(exec);
662     RETURN_IF_EXCEPTION(scope, encodedJSValue());
663
664     if (UNLIKELY(length > std::numeric_limits<unsigned>::max() || !canUseFastJoin(thisObject))) {
665         uint64_t length64 = static_cast<uint64_t>(length);
666         ASSERT(static_cast<double>(length64) == length);
667         return JSValue::encode(slowJoin(*exec, thisObject, jsSeparator, length64));
668     }
669
670     return JSValue::encode(fastJoin(*exec, thisObject, jsSeparator->view(exec).get(), length));
671 }
672
673 EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState* exec)
674 {
675     VM& vm = exec->vm();
676     auto scope = DECLARE_THROW_SCOPE(vm);
677
678     JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
679
680     if (isJSArray(thisValue))
681         return JSValue::encode(asArray(thisValue)->pop(exec));
682
683     JSObject* thisObj = thisValue.toObject(exec);
684     if (!thisObj)
685         return JSValue::encode(JSValue());
686     unsigned length = getLength(exec, thisObj);
687     RETURN_IF_EXCEPTION(scope, encodedJSValue());
688
689     JSValue result;
690     if (length == 0) {
691         putLength(exec, vm, thisObj, jsNumber(length));
692         result = jsUndefined();
693     } else {
694         result = thisObj->get(exec, length - 1);
695         RETURN_IF_EXCEPTION(scope, encodedJSValue());
696         if (!thisObj->methodTable(vm)->deletePropertyByIndex(thisObj, exec, length - 1)) {
697             throwTypeError(exec, scope, ASCIILiteral(UnableToDeletePropertyError));
698             return JSValue::encode(jsUndefined());
699         }
700         putLength(exec, vm, thisObj, jsNumber(length - 1));
701     }
702     return JSValue::encode(result);
703 }
704
705 EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState* exec)
706 {
707     VM& vm = exec->vm();
708     auto scope = DECLARE_THROW_SCOPE(vm);
709     JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
710
711     if (isJSArray(thisValue) && exec->argumentCount() == 1) {
712         JSArray* array = asArray(thisValue);
713         array->push(exec, exec->uncheckedArgument(0));
714         return JSValue::encode(jsNumber(array->length()));
715     }
716     
717     JSObject* thisObj = thisValue.toObject(exec);
718     if (!thisObj)
719         return JSValue::encode(JSValue());
720     unsigned length = getLength(exec, thisObj);
721     RETURN_IF_EXCEPTION(scope, encodedJSValue());
722
723     for (unsigned n = 0; n < exec->argumentCount(); n++) {
724         // Check for integer overflow; where safe we can do a fast put by index.
725         if (length + n >= length)
726             thisObj->methodTable()->putByIndex(thisObj, exec, length + n, exec->uncheckedArgument(n), true);
727         else {
728             PutPropertySlot slot(thisObj);
729             Identifier propertyName = Identifier::fromString(exec, JSValue(static_cast<int64_t>(length) + static_cast<int64_t>(n)).toWTFString(exec));
730             thisObj->methodTable()->put(thisObj, exec, propertyName, exec->uncheckedArgument(n), slot);
731         }
732         RETURN_IF_EXCEPTION(scope, encodedJSValue());
733     }
734     
735     JSValue newLength(static_cast<int64_t>(length) + static_cast<int64_t>(exec->argumentCount()));
736     putLength(exec, vm, thisObj, newLength);
737     return JSValue::encode(newLength);
738 }
739
740 EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState* exec)
741 {
742     VM& vm = exec->vm();
743     auto scope = DECLARE_THROW_SCOPE(vm);
744
745     JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
746     if (!thisObject)
747         return JSValue::encode(JSValue());
748
749     unsigned length = getLength(exec, thisObject);
750     RETURN_IF_EXCEPTION(scope, encodedJSValue());
751
752     switch (thisObject->indexingType()) {
753     case ALL_CONTIGUOUS_INDEXING_TYPES:
754     case ALL_INT32_INDEXING_TYPES: {
755         auto& butterfly = *thisObject->butterfly();
756         if (length > butterfly.publicLength())
757             break;
758         auto data = butterfly.contiguous().data();
759         if (containsHole(data, length) && holesMustForwardToPrototype(*exec, thisObject))
760             break;
761         std::reverse(data, data + length);
762         return JSValue::encode(thisObject);
763     }
764     case ALL_DOUBLE_INDEXING_TYPES: {
765         auto& butterfly = *thisObject->butterfly();
766         if (length > butterfly.publicLength())
767             break;
768         auto data = butterfly.contiguousDouble().data();
769         if (containsHole(data, length) && holesMustForwardToPrototype(*exec, thisObject))
770             break;
771         std::reverse(data, data + length);
772         return JSValue::encode(thisObject);
773     }
774     case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
775         auto& storage = *thisObject->butterfly()->arrayStorage();
776         if (length > storage.vectorLength())
777             break;
778         if (storage.hasHoles() && holesMustForwardToPrototype(*exec, thisObject))
779             break;
780         auto data = storage.vector().data();
781         std::reverse(data, data + length);
782         return JSValue::encode(thisObject);
783     }
784     }
785
786     unsigned middle = length / 2;
787     for (unsigned lower = 0; lower < middle; lower++) {
788         unsigned upper = length - lower - 1;
789         bool lowerExists = thisObject->hasProperty(exec, lower);
790         RETURN_IF_EXCEPTION(scope, encodedJSValue());
791         JSValue lowerValue;
792         if (lowerExists) {
793             lowerValue = thisObject->get(exec, lower);
794             RETURN_IF_EXCEPTION(scope, encodedJSValue());
795         }
796
797         bool upperExists = thisObject->hasProperty(exec, upper);
798         RETURN_IF_EXCEPTION(scope, encodedJSValue());
799         JSValue upperValue;
800         if (upperExists) {
801             upperValue = thisObject->get(exec, upper);
802             RETURN_IF_EXCEPTION(scope, encodedJSValue());
803         }
804
805         if (upperExists) {
806             thisObject->putByIndexInline(exec, lower, upperValue, true);
807             RETURN_IF_EXCEPTION(scope, encodedJSValue());
808         } else if (!thisObject->methodTable(vm)->deletePropertyByIndex(thisObject, exec, lower)) {
809             if (!scope.exception())
810                 throwTypeError(exec, scope, ASCIILiteral(UnableToDeletePropertyError));
811             return JSValue::encode(JSValue());
812         }
813
814         if (lowerExists) {
815             thisObject->putByIndexInline(exec, upper, lowerValue, true);
816             RETURN_IF_EXCEPTION(scope, encodedJSValue());
817         } else if (!thisObject->methodTable(vm)->deletePropertyByIndex(thisObject, exec, upper)) {
818             if (!scope.exception())
819                 throwTypeError(exec, scope, ASCIILiteral(UnableToDeletePropertyError));
820             return JSValue::encode(JSValue());
821         }
822     }
823     return JSValue::encode(thisObject);
824 }
825
826 EncodedJSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState* exec)
827 {
828     VM& vm = exec->vm();
829     auto scope = DECLARE_THROW_SCOPE(vm);
830     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
831     if (!thisObj)
832         return JSValue::encode(JSValue());
833     unsigned length = getLength(exec, thisObj);
834     RETURN_IF_EXCEPTION(scope, encodedJSValue());
835
836     JSValue result;
837     if (length == 0) {
838         putLength(exec, vm, thisObj, jsNumber(length));
839         result = jsUndefined();
840     } else {
841         result = thisObj->getIndex(exec, 0);
842         shift<JSArray::ShiftCountForShift>(exec, thisObj, 0, 1, 0, length);
843         RETURN_IF_EXCEPTION(scope, encodedJSValue());
844         putLength(exec, vm, thisObj, jsNumber(length - 1));
845     }
846     return JSValue::encode(result);
847 }
848
849 EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec)
850 {
851     // http://developer.netscape.com/docs/manuals/js/client/jsref/array.htm#1193713 or 15.4.4.10
852     VM& vm = exec->vm();
853     auto scope = DECLARE_THROW_SCOPE(vm);
854     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
855     if (!thisObj)
856         return JSValue::encode(JSValue());
857     unsigned length = getLength(exec, thisObj);
858     RETURN_IF_EXCEPTION(scope, encodedJSValue());
859
860     unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length);
861     unsigned end = argumentClampedIndexFromStartOrEnd(exec, 1, length, length);
862
863     std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, end - begin);
864     // We can only get an exception if we call some user function.
865     if (UNLIKELY(speciesResult.first == SpeciesConstructResult::Exception))
866         return JSValue::encode(jsUndefined());
867
868     if (LIKELY(speciesResult.first == SpeciesConstructResult::FastPath && isJSArray(thisObj) && length == getLength(exec, thisObj))) {
869         if (JSArray* result = asArray(thisObj)->fastSlice(*exec, begin, end - begin))
870             return JSValue::encode(result);
871     }
872
873     JSObject* result;
874     if (speciesResult.first == SpeciesConstructResult::CreatedObject)
875         result = speciesResult.second;
876     else {
877         result = constructEmptyArray(exec, nullptr, end - begin);
878         RETURN_IF_EXCEPTION(scope, encodedJSValue());
879     }
880
881     unsigned n = 0;
882     for (unsigned k = begin; k < end; k++, n++) {
883         JSValue v = getProperty(exec, thisObj, k);
884         RETURN_IF_EXCEPTION(scope, encodedJSValue());
885         if (v)
886             result->putDirectIndex(exec, n, v, 0, PutDirectIndexShouldThrow);
887     }
888     scope.release();
889     setLength(exec, vm, result, n);
890     return JSValue::encode(result);
891 }
892
893 EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec)
894 {
895     // 15.4.4.12
896
897     VM& vm = exec->vm();
898     auto scope = DECLARE_THROW_SCOPE(vm);
899
900     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
901     if (!thisObj)
902         return JSValue::encode(JSValue());
903     unsigned length = getLength(exec, thisObj);
904     RETURN_IF_EXCEPTION(scope, encodedJSValue());
905
906     if (!exec->argumentCount()) {
907         std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, 0);
908         if (speciesResult.first == SpeciesConstructResult::Exception)
909             return JSValue::encode(jsUndefined());
910
911         JSObject* result;
912         if (speciesResult.first == SpeciesConstructResult::CreatedObject)
913             result = speciesResult.second;
914         else {
915             result = constructEmptyArray(exec, nullptr);
916             RETURN_IF_EXCEPTION(scope, encodedJSValue());
917         }
918
919         setLength(exec, vm, result, 0);
920         RETURN_IF_EXCEPTION(scope, encodedJSValue());
921         scope.release();
922         setLength(exec, vm, thisObj, length);
923         return JSValue::encode(result);
924     }
925
926     unsigned actualStart = argumentClampedIndexFromStartOrEnd(exec, 0, length);
927
928     unsigned actualDeleteCount = length - actualStart;
929     if (exec->argumentCount() > 1) {
930         double deleteCount = exec->uncheckedArgument(1).toInteger(exec);
931         if (deleteCount < 0)
932             actualDeleteCount = 0;
933         else if (deleteCount > length - actualStart)
934             actualDeleteCount = length - actualStart;
935         else
936             actualDeleteCount = static_cast<unsigned>(deleteCount);
937     }
938
939     std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, actualDeleteCount);
940     if (speciesResult.first == SpeciesConstructResult::Exception)
941         return JSValue::encode(jsUndefined());
942
943     JSObject* result = nullptr;
944     if (speciesResult.first == SpeciesConstructResult::FastPath && isJSArray(thisObj) && length == getLength(exec, thisObj))
945         result = asArray(thisObj)->fastSlice(*exec, actualStart, actualDeleteCount);
946
947     if (!result) {
948         if (speciesResult.first == SpeciesConstructResult::CreatedObject) {
949             result = speciesResult.second;
950             
951             for (unsigned k = 0; k < actualDeleteCount; ++k) {
952                 JSValue v = getProperty(exec, thisObj, k + actualStart);
953                 RETURN_IF_EXCEPTION(scope, encodedJSValue());
954                 if (UNLIKELY(!v))
955                     continue;
956                 result->putByIndexInline(exec, k, v, true);
957                 RETURN_IF_EXCEPTION(scope, encodedJSValue());
958             }
959         } else {
960             result = JSArray::tryCreateUninitialized(vm, exec->lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), actualDeleteCount);
961             if (!result)
962                 return JSValue::encode(throwOutOfMemoryError(exec, scope));
963             
964             for (unsigned k = 0; k < actualDeleteCount; ++k) {
965                 JSValue v = getProperty(exec, thisObj, k + actualStart);
966                 RETURN_IF_EXCEPTION(scope, encodedJSValue());
967                 if (UNLIKELY(!v))
968                     continue;
969                 result->initializeIndex(vm, k, v);
970             }
971         }
972     }
973
974     unsigned itemCount = std::max<int>(exec->argumentCount() - 2, 0);
975     if (itemCount < actualDeleteCount) {
976         shift<JSArray::ShiftCountForSplice>(exec, thisObj, actualStart, actualDeleteCount, itemCount, length);
977         RETURN_IF_EXCEPTION(scope, encodedJSValue());
978     } else if (itemCount > actualDeleteCount) {
979         unshift<JSArray::ShiftCountForSplice>(exec, thisObj, actualStart, actualDeleteCount, itemCount, length);
980         RETURN_IF_EXCEPTION(scope, encodedJSValue());
981     }
982     for (unsigned k = 0; k < itemCount; ++k) {
983         thisObj->putByIndexInline(exec, k + actualStart, exec->uncheckedArgument(k + 2), true);
984         RETURN_IF_EXCEPTION(scope, encodedJSValue());
985     }
986     
987     scope.release();
988     setLength(exec, vm, thisObj, length - actualDeleteCount + itemCount);
989     return JSValue::encode(result);
990 }
991
992 EncodedJSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState* exec)
993 {
994     VM& vm = exec->vm();
995     auto scope = DECLARE_THROW_SCOPE(vm);
996     // 15.4.4.13
997
998     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
999     if (!thisObj)
1000         return JSValue::encode(JSValue());
1001     unsigned length = getLength(exec, thisObj);
1002     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1003
1004     unsigned nrArgs = exec->argumentCount();
1005     if (nrArgs) {
1006         unshift<JSArray::ShiftCountForShift>(exec, thisObj, 0, 0, nrArgs, length);
1007         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1008     }
1009     for (unsigned k = 0; k < nrArgs; ++k) {
1010         thisObj->putByIndexInline(exec, k, exec->uncheckedArgument(k), true);
1011         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1012     }
1013     JSValue result = jsNumber(length + nrArgs);
1014     putLength(exec, vm, thisObj, result);
1015     return JSValue::encode(result);
1016 }
1017
1018 EncodedJSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState* exec)
1019 {
1020     VM& vm = exec->vm();
1021     auto scope = DECLARE_THROW_SCOPE(vm);
1022
1023     // 15.4.4.14
1024     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
1025     if (!thisObj)
1026         return JSValue::encode(JSValue());
1027     unsigned length = getLength(exec, thisObj);
1028     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1029
1030     unsigned index = argumentClampedIndexFromStartOrEnd(exec, 1, length);
1031     JSValue searchElement = exec->argument(0);
1032     for (; index < length; ++index) {
1033         JSValue e = getProperty(exec, thisObj, index);
1034         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1035         if (!e)
1036             continue;
1037         if (JSValue::strictEqual(exec, searchElement, e))
1038             return JSValue::encode(jsNumber(index));
1039         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1040     }
1041
1042     return JSValue::encode(jsNumber(-1));
1043 }
1044
1045 EncodedJSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState* exec)
1046 {
1047     VM& vm = exec->vm();
1048     auto scope = DECLARE_THROW_SCOPE(vm);
1049
1050     // 15.4.4.15
1051     JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
1052     if (!thisObj)
1053         return JSValue::encode(JSValue());
1054     unsigned length = getLength(exec, thisObj);
1055     if (!length)
1056         return JSValue::encode(jsNumber(-1));
1057
1058     unsigned index = length - 1;
1059     if (exec->argumentCount() >= 2) {
1060         JSValue fromValue = exec->uncheckedArgument(1);
1061         double fromDouble = fromValue.toInteger(exec);
1062         if (fromDouble < 0) {
1063             fromDouble += length;
1064             if (fromDouble < 0)
1065                 return JSValue::encode(jsNumber(-1));
1066         }
1067         if (fromDouble < length)
1068             index = static_cast<unsigned>(fromDouble);
1069     }
1070
1071     JSValue searchElement = exec->argument(0);
1072     do {
1073         RELEASE_ASSERT(index < length);
1074         JSValue e = getProperty(exec, thisObj, index);
1075         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1076         if (!e)
1077             continue;
1078         if (JSValue::strictEqual(exec, searchElement, e))
1079             return JSValue::encode(jsNumber(index));
1080         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1081     } while (index--);
1082
1083     return JSValue::encode(jsNumber(-1));
1084 }
1085
1086 static bool moveElements(ExecState* exec, VM& vm, JSArray* target, unsigned targetOffset, JSArray* source, unsigned sourceLength)
1087 {
1088     auto scope = DECLARE_THROW_SCOPE(vm);
1089
1090     if (LIKELY(!hasAnyArrayStorage(source->indexingType()) && !source->structure()->holesMustForwardToPrototype(vm))) {
1091         for (unsigned i = 0; i < sourceLength; ++i) {
1092             JSValue value = source->tryGetIndexQuickly(i);
1093             if (value) {
1094                 target->putDirectIndex(exec, targetOffset + i, value, 0, PutDirectIndexShouldThrow);
1095                 RETURN_IF_EXCEPTION(scope, false);
1096             }
1097         }
1098     } else {
1099         for (unsigned i = 0; i < sourceLength; ++i) {
1100             JSValue value = getProperty(exec, source, i);
1101             RETURN_IF_EXCEPTION(scope, false);
1102             if (value) {
1103                 target->putDirectIndex(exec, targetOffset + i, value, 0, PutDirectIndexShouldThrow);
1104                 RETURN_IF_EXCEPTION(scope, false);
1105             }
1106         }
1107     }
1108     return true;
1109 }
1110
1111 static EncodedJSValue concatAppendOne(ExecState* exec, VM& vm, JSArray* first, JSValue second)
1112 {
1113     auto scope = DECLARE_THROW_SCOPE(vm);
1114
1115     ASSERT(!isJSArray(second));
1116     ASSERT(!shouldUseSlowPut(first->indexingType()));
1117     Butterfly* firstButterfly = first->butterfly();
1118     unsigned firstArraySize = firstButterfly->publicLength();
1119
1120     IndexingType type = first->mergeIndexingTypeForCopying(indexingTypeForValue(second) | IsArray);
1121     
1122     if (type == NonArray)
1123         type = first->indexingType();
1124
1125     Structure* resultStructure = exec->lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(type);
1126     JSArray* result = JSArray::create(vm, resultStructure, firstArraySize + 1);
1127     if (!result)
1128         return JSValue::encode(throwOutOfMemoryError(exec, scope));
1129
1130     if (!result->appendMemcpy(exec, vm, 0, first)) {
1131         if (!moveElements(exec, vm, result, 0, first, firstArraySize)) {
1132             ASSERT(scope.exception());
1133             return JSValue::encode(JSValue());
1134         }
1135     }
1136
1137     result->putDirectIndex(exec, firstArraySize, second);
1138     return JSValue::encode(result);
1139
1140 }
1141
1142
1143 EncodedJSValue JSC_HOST_CALL arrayProtoPrivateFuncConcatMemcpy(ExecState* exec)
1144 {
1145     ASSERT(exec->argumentCount() == 2);
1146     VM& vm = exec->vm();
1147     auto scope = DECLARE_THROW_SCOPE(vm);
1148
1149     JSArray* firstArray = jsCast<JSArray*>(exec->uncheckedArgument(0));
1150     
1151     // This code assumes that neither array has set Symbol.isConcatSpreadable. If the first array
1152     // has indexed accessors then one of those accessors might change the value of Symbol.isConcatSpreadable
1153     // on the second argument.
1154     if (UNLIKELY(shouldUseSlowPut(firstArray->indexingType())))
1155         return JSValue::encode(jsNull());
1156
1157     // We need to check the species constructor here since checking it in the JS wrapper is too expensive for the non-optimizing tiers.
1158     if (UNLIKELY(!speciesWatchpointsValid(exec, firstArray)))
1159         return JSValue::encode(jsNull());
1160
1161     JSValue second = exec->uncheckedArgument(1);
1162     if (!isJSArray(second))
1163         return concatAppendOne(exec, vm, firstArray, second);
1164
1165     JSArray* secondArray = jsCast<JSArray*>(second);
1166     
1167     Butterfly* firstButterfly = firstArray->butterfly();
1168     Butterfly* secondButterfly = secondArray->butterfly();
1169
1170     unsigned firstArraySize = firstButterfly->publicLength();
1171     unsigned secondArraySize = secondButterfly->publicLength();
1172
1173     IndexingType secondType = secondArray->indexingType();
1174     IndexingType type = firstArray->mergeIndexingTypeForCopying(secondType);
1175     if (type == NonArray || !firstArray->canFastCopy(vm, secondArray) || firstArraySize + secondArraySize >= MIN_SPARSE_ARRAY_INDEX) {
1176         JSArray* result = constructEmptyArray(exec, nullptr, firstArraySize + secondArraySize);
1177         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1178
1179         if (!moveElements(exec, vm, result, 0, firstArray, firstArraySize)
1180             || !moveElements(exec, vm, result, firstArraySize, secondArray, secondArraySize)) {
1181             ASSERT(scope.exception());
1182             return JSValue::encode(JSValue());
1183         }
1184
1185         return JSValue::encode(result);
1186     }
1187
1188     Structure* resultStructure = exec->lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(type);
1189     JSArray* result = JSArray::tryCreateUninitialized(vm, resultStructure, firstArraySize + secondArraySize);
1190     if (!result)
1191         return JSValue::encode(throwOutOfMemoryError(exec, scope));
1192     
1193     if (type == ArrayWithDouble) {
1194         double* buffer = result->butterfly()->contiguousDouble().data();
1195         memcpy(buffer, firstButterfly->contiguousDouble().data(), sizeof(JSValue) * firstArraySize);
1196         memcpy(buffer + firstArraySize, secondButterfly->contiguousDouble().data(), sizeof(JSValue) * secondArraySize);
1197     } else if (type != ArrayWithUndecided) {
1198         WriteBarrier<Unknown>* buffer = result->butterfly()->contiguous().data();
1199         memcpy(buffer, firstButterfly->contiguous().data(), sizeof(JSValue) * firstArraySize);
1200         if (secondType != ArrayWithUndecided)
1201             memcpy(buffer + firstArraySize, secondButterfly->contiguous().data(), sizeof(JSValue) * secondArraySize);
1202         else {
1203             for (unsigned i = secondArraySize; i--;)
1204                 buffer[i + firstArraySize].clear();
1205         }
1206     }
1207
1208     result->butterfly()->setPublicLength(firstArraySize + secondArraySize);
1209     return JSValue::encode(result);
1210 }
1211
1212 EncodedJSValue JSC_HOST_CALL arrayProtoPrivateFuncAppendMemcpy(ExecState* exec)
1213 {
1214     ASSERT(exec->argumentCount() == 3);
1215
1216     VM& vm = exec->vm();
1217     JSArray* resultArray = jsCast<JSArray*>(exec->uncheckedArgument(0));
1218     JSArray* otherArray = jsCast<JSArray*>(exec->uncheckedArgument(1));
1219     JSValue startValue = exec->uncheckedArgument(2);
1220     ASSERT(startValue.isAnyInt() && startValue.asAnyInt() >= 0 && startValue.asAnyInt() <= std::numeric_limits<unsigned>::max());
1221     unsigned startIndex = static_cast<unsigned>(startValue.asAnyInt());
1222     if (!resultArray->appendMemcpy(exec, vm, startIndex, otherArray))
1223         moveElements(exec, vm, resultArray, startIndex, otherArray, otherArray->length());
1224
1225     return JSValue::encode(jsUndefined());
1226 }
1227
1228
1229 // -------------------- ArrayPrototype.constructor Watchpoint ------------------
1230
1231 static bool verbose = false;
1232
1233 class ArrayPrototypeAdaptiveInferredPropertyWatchpoint : public AdaptiveInferredPropertyValueWatchpointBase {
1234 public:
1235     typedef AdaptiveInferredPropertyValueWatchpointBase Base;
1236     ArrayPrototypeAdaptiveInferredPropertyWatchpoint(const ObjectPropertyCondition&, ArrayPrototype*);
1237
1238 private:
1239     void handleFire(const FireDetail&) override;
1240
1241     ArrayPrototype* m_arrayPrototype;
1242 };
1243
1244 ArrayPrototype::SpeciesWatchpointStatus ArrayPrototype::attemptToInitializeSpeciesWatchpoint(ExecState* exec)
1245 {
1246     ASSERT(m_speciesWatchpointStatus == SpeciesWatchpointStatus::Uninitialized);
1247
1248     VM& vm = exec->vm();
1249
1250     if (verbose)
1251         dataLog("Attempting to initialize Array species watchpoints for Array.prototype: ", pointerDump(this), " with structure: ", pointerDump(this->structure()), "\nand Array: ", pointerDump(this->globalObject()->arrayConstructor()), " with structure: ", pointerDump(this->globalObject()->arrayConstructor()->structure()), "\n");
1252     // First we need to make sure that the Array.prototype.constructor property points to Array
1253     // and that Array[Symbol.species] is the primordial GetterSetter.
1254
1255     // We only initialize once so flattening the structures does not have any real cost.
1256     Structure* prototypeStructure = this->structure(vm);
1257     if (prototypeStructure->isDictionary())
1258         prototypeStructure = prototypeStructure->flattenDictionaryStructure(vm, this);
1259     RELEASE_ASSERT(!prototypeStructure->isDictionary());
1260
1261     JSGlobalObject* globalObject = this->globalObject();
1262     ArrayConstructor* arrayConstructor = globalObject->arrayConstructor();
1263
1264     PropertySlot constructorSlot(this, PropertySlot::InternalMethodType::VMInquiry);
1265     JSValue(this).get(exec, vm.propertyNames->constructor, constructorSlot);
1266     if (constructorSlot.slotBase() != this
1267         || !constructorSlot.isCacheableValue()
1268         || constructorSlot.getValue(exec, vm.propertyNames->constructor) != arrayConstructor)
1269         return m_speciesWatchpointStatus = SpeciesWatchpointStatus::Fired;
1270
1271     Structure* constructorStructure = arrayConstructor->structure(vm);
1272     if (constructorStructure->isDictionary())
1273         constructorStructure = constructorStructure->flattenDictionaryStructure(vm, arrayConstructor);
1274
1275     PropertySlot speciesSlot(arrayConstructor, PropertySlot::InternalMethodType::VMInquiry);
1276     JSValue(arrayConstructor).get(exec, vm.propertyNames->speciesSymbol, speciesSlot);
1277     if (speciesSlot.slotBase() != arrayConstructor
1278         || !speciesSlot.isCacheableGetter()
1279         || speciesSlot.getterSetter() != globalObject->speciesGetterSetter())
1280         return m_speciesWatchpointStatus = SpeciesWatchpointStatus::Fired;
1281
1282     // Now we need to setup the watchpoints to make sure these conditions remain valid.
1283     prototypeStructure->startWatchingPropertyForReplacements(vm, constructorSlot.cachedOffset());
1284     constructorStructure->startWatchingPropertyForReplacements(vm, speciesSlot.cachedOffset());
1285
1286     ObjectPropertyCondition constructorCondition = ObjectPropertyCondition::equivalence(vm, this, this, vm.propertyNames->constructor.impl(), arrayConstructor);
1287     ObjectPropertyCondition speciesCondition = ObjectPropertyCondition::equivalence(vm, this, arrayConstructor, vm.propertyNames->speciesSymbol.impl(), globalObject->speciesGetterSetter());
1288
1289     if (!constructorCondition.isWatchable() || !speciesCondition.isWatchable())
1290         return m_speciesWatchpointStatus = SpeciesWatchpointStatus::Fired;
1291
1292     m_constructorWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(constructorCondition, this);
1293     m_constructorWatchpoint->install();
1294
1295     m_constructorSpeciesWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(speciesCondition, this);
1296     m_constructorSpeciesWatchpoint->install();
1297
1298     return m_speciesWatchpointStatus = SpeciesWatchpointStatus::Initialized;
1299 }
1300
1301 ArrayPrototypeAdaptiveInferredPropertyWatchpoint::ArrayPrototypeAdaptiveInferredPropertyWatchpoint(const ObjectPropertyCondition& key, ArrayPrototype* prototype)
1302     : Base(key)
1303     , m_arrayPrototype(prototype)
1304 {
1305 }
1306
1307 void ArrayPrototypeAdaptiveInferredPropertyWatchpoint::handleFire(const FireDetail& detail)
1308 {
1309     StringPrintStream out;
1310     out.print("ArrayPrototype adaption of ", key(), " failed: ", detail);
1311
1312     StringFireDetail stringDetail(out.toCString().data());
1313
1314     if (verbose)
1315         WTF::dataLog(stringDetail, "\n");
1316
1317     m_arrayPrototype->m_speciesWatchpointStatus = ArrayPrototype::SpeciesWatchpointStatus::Fired;
1318 }
1319
1320 } // namespace JSC