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