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