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