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)
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.
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.
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 USA
26 #include "ArrayPrototype.h"
27 #include "ButterflyInlines.h"
28 #include "CodeBlock.h"
30 #include "GetterSetter.h"
31 #include "IndexingHeaderInlines.h"
32 #include "JSArrayInlines.h"
33 #include "JSCInlines.h"
34 #include "PropertyNameArray.h"
35 #include "TypeError.h"
36 #include <wtf/Assertions.h>
43 const char* const LengthExceededTheMaximumArrayLengthError = "Length exceeded the maximum array length";
45 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSArray);
47 const ClassInfo JSArray::s_info = {"Array", &JSNonFinalObject::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSArray)};
49 Butterfly* createArrayButterflyInDictionaryIndexingMode(
50 VM& vm, JSCell* intendedOwner, unsigned initialLength)
52 Butterfly* butterfly = Butterfly::create(
53 vm, intendedOwner, 0, 0, true, IndexingHeader(), ArrayStorage::sizeFor(0));
54 ArrayStorage* storage = butterfly->arrayStorage();
55 storage->setLength(initialLength);
56 storage->setVectorLength(0);
57 storage->m_indexBias = 0;
58 storage->m_sparseMap.clear();
59 storage->m_numValuesInVector = 0;
63 JSArray* JSArray::tryCreateUninitializedRestricted(ObjectInitializationScope& scope, GCDeferralContext* deferralContext, Structure* structure, unsigned initialLength)
67 if (UNLIKELY(initialLength > MAX_STORAGE_VECTOR_LENGTH))
70 JSGlobalObject* globalObject = structure->globalObject();
71 bool createUninitialized = globalObject->isOriginalArrayStructure(structure);
72 unsigned outOfLineStorage = structure->outOfLineCapacity();
75 IndexingType indexingType = structure->indexingType();
76 if (LIKELY(!hasAnyArrayStorage(indexingType))) {
78 hasUndecided(indexingType)
79 || hasInt32(indexingType)
80 || hasDouble(indexingType)
81 || hasContiguous(indexingType));
83 unsigned vectorLength = Butterfly::optimalContiguousVectorLength(structure, initialLength);
84 void* temp = vm.jsValueGigacageAuxiliarySpace.allocateNonVirtual(Butterfly::totalSize(0, outOfLineStorage, true, vectorLength * sizeof(EncodedJSValue)), deferralContext, AllocationFailureMode::ReturnNull);
87 butterfly = Butterfly::fromBase(temp, 0, outOfLineStorage);
88 butterfly->setVectorLength(vectorLength);
89 butterfly->setPublicLength(initialLength);
90 unsigned i = (createUninitialized ? initialLength : 0);
91 if (hasDouble(indexingType)) {
92 for (; i < vectorLength; ++i)
93 butterfly->contiguousDouble().at(std::numeric_limits<uint32_t>::max(), i) = PNaN;
94 } else if (LIKELY(!hasUndecided(indexingType))) {
95 for (; i < vectorLength; ++i)
96 butterfly->contiguous().at(std::numeric_limits<uint32_t>::max(), i).clear();
99 static const unsigned indexBias = 0;
100 unsigned vectorLength = ArrayStorage::optimalVectorLength(indexBias, structure, initialLength);
101 void* temp = vm.jsValueGigacageAuxiliarySpace.allocateNonVirtual(Butterfly::totalSize(indexBias, outOfLineStorage, true, ArrayStorage::sizeFor(vectorLength)), deferralContext, AllocationFailureMode::ReturnNull);
104 butterfly = Butterfly::fromBase(temp, indexBias, outOfLineStorage);
105 *butterfly->indexingHeader() = indexingHeaderForArrayStorage(initialLength, vectorLength);
106 ArrayStorage* storage = butterfly->arrayStorage();
107 storage->m_indexBias = indexBias;
108 storage->m_sparseMap.clear();
109 storage->m_numValuesInVector = initialLength;
110 unsigned i = (createUninitialized ? initialLength : 0);
111 for (; i < vectorLength; ++i)
112 storage->m_vector[i].clear();
115 JSArray* result = createWithButterfly(vm, deferralContext, structure, butterfly);
116 scope.notifyAllocated(result, createUninitialized);
120 void JSArray::setLengthWritable(ExecState* exec, bool writable)
122 ASSERT(isLengthWritable() || !writable);
123 if (!isLengthWritable() || writable)
126 enterDictionaryIndexingMode(exec->vm());
128 SparseArrayValueMap* map = arrayStorage()->m_sparseMap.get();
130 map->setLengthIsReadOnly();
133 // Defined in ES5.1 15.4.5.1
134 bool JSArray::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException)
137 auto scope = DECLARE_THROW_SCOPE(vm);
139 JSArray* array = jsCast<JSArray*>(object);
141 // 3. If P is "length", then
142 if (propertyName == vm.propertyNames->length) {
143 // All paths through length definition call the default [[DefineOwnProperty]], hence:
144 // from ES5.1 8.12.9 7.a.
145 if (descriptor.configurablePresent() && descriptor.configurable())
146 return typeError(exec, scope, throwException, ASCIILiteral(UnconfigurablePropertyChangeConfigurabilityError));
147 // from ES5.1 8.12.9 7.b.
148 if (descriptor.enumerablePresent() && descriptor.enumerable())
149 return typeError(exec, scope, throwException, ASCIILiteral(UnconfigurablePropertyChangeEnumerabilityError));
151 // a. If the [[Value]] field of Desc is absent, then
152 // a.i. Return the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", Desc, and Throw as arguments.
153 if (descriptor.isAccessorDescriptor())
154 return typeError(exec, scope, throwException, ASCIILiteral(UnconfigurablePropertyChangeAccessMechanismError));
155 // from ES5.1 8.12.9 10.a.
156 if (!array->isLengthWritable() && descriptor.writablePresent() && descriptor.writable())
157 return typeError(exec, scope, throwException, ASCIILiteral(UnconfigurablePropertyChangeWritabilityError));
158 // This descriptor is either just making length read-only, or changing nothing!
159 if (!descriptor.value()) {
160 if (descriptor.writablePresent())
161 array->setLengthWritable(exec, descriptor.writable());
165 // b. Let newLenDesc be a copy of Desc.
166 // c. Let newLen be ToUint32(Desc.[[Value]]).
167 unsigned newLen = descriptor.value().toUInt32(exec);
168 RETURN_IF_EXCEPTION(scope, false);
169 // d. If newLen is not equal to ToNumber( Desc.[[Value]]), throw a RangeError exception.
170 double valueAsNumber = descriptor.value().toNumber(exec);
171 RETURN_IF_EXCEPTION(scope, false);
172 if (newLen != valueAsNumber) {
173 JSC::throwException(exec, scope, createRangeError(exec, ASCIILiteral("Invalid array length")));
177 // Based on SameValue check in 8.12.9, this is always okay.
178 // FIXME: Nothing prevents this from being called on a RuntimeArray, and the length function will always return 0 in that case.
179 if (newLen == array->length()) {
180 if (descriptor.writablePresent())
181 array->setLengthWritable(exec, descriptor.writable());
185 // e. Set newLenDesc.[[Value] to newLen.
186 // f. If newLen >= oldLen, then
187 // f.i. Return the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", newLenDesc, and Throw as arguments.
188 // g. Reject if oldLenDesc.[[Writable]] is false.
189 if (!array->isLengthWritable())
190 return typeError(exec, scope, throwException, ASCIILiteral(ReadonlyPropertyChangeError));
192 // h. If newLenDesc.[[Writable]] is absent or has the value true, let newWritable be true.
194 // i.i. Need to defer setting the [[Writable]] attribute to false in case any elements cannot be deleted.
195 // i.ii. Let newWritable be false.
196 // i.iii. Set newLenDesc.[[Writable] to true.
197 // j. Let succeeded be the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", newLenDesc, and Throw as arguments.
198 // k. If succeeded is false, return false.
199 // l. While newLen < oldLen repeat,
200 // l.i. Set oldLen to oldLen – 1.
201 // l.ii. Let deleteSucceeded be the result of calling the [[Delete]] internal method of A passing ToString(oldLen) and false as arguments.
202 // l.iii. If deleteSucceeded is false, then
203 bool success = array->setLength(exec, newLen, throwException);
204 EXCEPTION_ASSERT(!scope.exception() || !success);
206 // 1. Set newLenDesc.[[Value] to oldLen+1.
207 // 2. If newWritable is false, set newLenDesc.[[Writable] to false.
208 // 3. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", newLenDesc, and false as arguments.
210 if (descriptor.writablePresent())
211 array->setLengthWritable(exec, descriptor.writable());
215 // m. If newWritable is false, then
216 // i. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length",
217 // Property Descriptor{[[Writable]]: false}, and false as arguments. This call will always
219 if (descriptor.writablePresent())
220 array->setLengthWritable(exec, descriptor.writable());
225 // 4. Else if P is an array index (15.4), then
226 // a. Let index be ToUint32(P).
227 if (std::optional<uint32_t> optionalIndex = parseIndex(propertyName)) {
228 // b. Reject if index >= oldLen and oldLenDesc.[[Writable]] is false.
229 uint32_t index = optionalIndex.value();
230 // FIXME: Nothing prevents this from being called on a RuntimeArray, and the length function will always return 0 in that case.
231 if (index >= array->length() && !array->isLengthWritable())
232 return typeError(exec, scope, throwException, ASCIILiteral("Attempting to define numeric property on array with non-writable length property."));
233 // c. Let succeeded be the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing P, Desc, and false as arguments.
234 // d. Reject if succeeded is false.
235 // e. If index >= oldLen
236 // e.i. Set oldLenDesc.[[Value]] to index + 1.
237 // e.ii. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", oldLenDesc, and false as arguments. This call will always return true.
240 return array->defineOwnIndexedProperty(exec, index, descriptor, throwException);
244 return array->JSObject::defineOwnNonIndexProperty(exec, propertyName, descriptor, throwException);
247 bool JSArray::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
250 JSArray* thisObject = jsCast<JSArray*>(object);
251 if (propertyName == vm.propertyNames->length) {
252 unsigned attributes = thisObject->isLengthWritable() ? PropertyAttribute::DontDelete | PropertyAttribute::DontEnum : PropertyAttribute::DontDelete | PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly;
253 slot.setValue(thisObject, attributes, jsNumber(thisObject->length()));
257 return JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot);
261 bool JSArray::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
264 auto scope = DECLARE_THROW_SCOPE(vm);
266 JSArray* thisObject = jsCast<JSArray*>(cell);
268 if (UNLIKELY(isThisValueAltered(slot, thisObject))) {
270 return ordinarySetSlow(exec, thisObject, propertyName, value, slot.thisValue(), slot.isStrictMode());
273 if (propertyName == vm.propertyNames->length) {
274 if (!thisObject->isLengthWritable())
276 unsigned newLength = value.toUInt32(exec);
277 RETURN_IF_EXCEPTION(scope, false);
278 double valueAsNumber = value.toNumber(exec);
279 RETURN_IF_EXCEPTION(scope, false);
280 if (valueAsNumber != static_cast<double>(newLength)) {
281 throwException(exec, scope, createRangeError(exec, ASCIILiteral("Invalid array length")));
285 return thisObject->setLength(exec, newLength, slot.isStrictMode());
289 return JSObject::put(thisObject, exec, propertyName, value, slot);
292 bool JSArray::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
295 JSArray* thisObject = jsCast<JSArray*>(cell);
297 if (propertyName == vm.propertyNames->length)
300 return JSObject::deleteProperty(thisObject, exec, propertyName);
303 static int compareKeysForQSort(const void* a, const void* b)
305 unsigned da = *static_cast<const unsigned*>(a);
306 unsigned db = *static_cast<const unsigned*>(b);
307 return (da > db) - (da < db);
310 void JSArray::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
313 JSArray* thisObject = jsCast<JSArray*>(object);
315 if (mode.includeDontEnumProperties())
316 propertyNames.add(vm.propertyNames->length);
318 JSObject::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode);
321 // This method makes room in the vector, but leaves the new space for count slots uncleared.
322 bool JSArray::unshiftCountSlowCase(const AbstractLocker&, VM& vm, DeferGC&, bool addToFront, unsigned count)
324 ArrayStorage* storage = ensureArrayStorage(vm);
325 Butterfly* butterfly = storage->butterfly();
326 Structure* structure = this->structure(vm);
327 unsigned propertyCapacity = structure->outOfLineCapacity();
328 unsigned propertySize = structure->outOfLineSize();
330 // If not, we should have handled this on the fast path.
331 ASSERT(!addToFront || count > storage->m_indexBias);
334 // Gather 4 key metrics:
335 // * usedVectorLength - how many entries are currently in the vector (conservative estimate - fewer may be in use in sparse vectors).
336 // * requiredVectorLength - how many entries are will there be in the vector, after allocating space for 'count' more.
337 // * currentCapacity - what is the current size of the vector, including any pre-capacity.
338 // * desiredCapacity - how large should we like to grow the vector to - based on 2x requiredVectorLength.
340 unsigned length = storage->length();
341 unsigned oldVectorLength = storage->vectorLength();
342 unsigned usedVectorLength = std::min(oldVectorLength, length);
343 ASSERT(usedVectorLength <= MAX_STORAGE_VECTOR_LENGTH);
344 // Check that required vector length is possible, in an overflow-safe fashion.
345 if (count > MAX_STORAGE_VECTOR_LENGTH - usedVectorLength)
347 unsigned requiredVectorLength = usedVectorLength + count;
348 ASSERT(requiredVectorLength <= MAX_STORAGE_VECTOR_LENGTH);
349 // The sum of m_vectorLength and m_indexBias will never exceed MAX_STORAGE_VECTOR_LENGTH.
350 ASSERT(storage->vectorLength() <= MAX_STORAGE_VECTOR_LENGTH && (MAX_STORAGE_VECTOR_LENGTH - storage->vectorLength()) >= storage->m_indexBias);
351 unsigned currentCapacity = storage->vectorLength() + storage->m_indexBias;
352 // The calculation of desiredCapacity won't overflow, due to the range of MAX_STORAGE_VECTOR_LENGTH.
353 // FIXME: This code should be fixed to avoid internal fragmentation. It's not super high
354 // priority since increaseVectorLength() will "fix" any mistakes we make, but it would be cool
355 // to get this right eventually.
356 unsigned desiredCapacity = std::min(MAX_STORAGE_VECTOR_LENGTH, std::max(BASE_ARRAY_STORAGE_VECTOR_LEN, requiredVectorLength) << 1);
359 // We're either going to choose to allocate a new ArrayStorage, or we're going to reuse the existing one.
361 void* newAllocBase = 0;
362 unsigned newStorageCapacity;
363 bool allocatedNewStorage;
364 // If the current storage array is sufficiently large (but not too large!) then just keep using it.
365 if (currentCapacity > desiredCapacity && isDenseEnoughForVector(currentCapacity, requiredVectorLength)) {
366 newAllocBase = butterfly->base(structure);
367 newStorageCapacity = currentCapacity;
368 allocatedNewStorage = false;
370 size_t newSize = Butterfly::totalSize(0, propertyCapacity, true, ArrayStorage::sizeFor(desiredCapacity));
371 newAllocBase = vm.jsValueGigacageAuxiliarySpace.allocateNonVirtual(newSize, nullptr, AllocationFailureMode::ReturnNull);
374 newStorageCapacity = desiredCapacity;
375 allocatedNewStorage = true;
379 // Work out where we're going to move things to.
381 // Determine how much of the vector to use as pre-capacity, and how much as post-capacity.
382 // If we're adding to the end, we'll add all the new space to the end.
383 // If the vector had no free post-capacity (length >= m_vectorLength), don't give it any.
384 // If it did, we calculate the amount that will remain based on an atomic decay - leave the
385 // vector with half the post-capacity it had previously.
386 unsigned postCapacity = 0;
388 postCapacity = newStorageCapacity - requiredVectorLength;
389 else if (length < storage->vectorLength()) {
390 // Atomic decay, + the post-capacity cannot be greater than what is available.
391 postCapacity = std::min((storage->vectorLength() - length) >> 1, newStorageCapacity - requiredVectorLength);
392 // If we're moving contents within the same allocation, the post-capacity is being reduced.
393 ASSERT(newAllocBase != butterfly->base(structure) || postCapacity < storage->vectorLength() - length);
396 unsigned newVectorLength = requiredVectorLength + postCapacity;
397 RELEASE_ASSERT(newVectorLength <= MAX_STORAGE_VECTOR_LENGTH);
398 unsigned newIndexBias = newStorageCapacity - newVectorLength;
400 Butterfly* newButterfly = Butterfly::fromBase(newAllocBase, newIndexBias, propertyCapacity);
403 ASSERT(count + usedVectorLength <= newVectorLength);
404 memmove(newButterfly->arrayStorage()->m_vector + count, storage->m_vector, sizeof(JSValue) * usedVectorLength);
405 memmove(newButterfly->propertyStorage() - propertySize, butterfly->propertyStorage() - propertySize, sizeof(JSValue) * propertySize + sizeof(IndexingHeader) + ArrayStorage::sizeFor(0));
407 if (allocatedNewStorage) {
408 // We will set the vectorLength to newVectorLength. We populated requiredVectorLength
409 // (usedVectorLength + count), which is less. Clear the difference.
410 for (unsigned i = requiredVectorLength; i < newVectorLength; ++i)
411 newButterfly->arrayStorage()->m_vector[i].clear();
413 } else if ((newAllocBase != butterfly->base(structure)) || (newIndexBias != storage->m_indexBias)) {
414 memmove(newButterfly->propertyStorage() - propertySize, butterfly->propertyStorage() - propertySize, sizeof(JSValue) * propertySize + sizeof(IndexingHeader) + ArrayStorage::sizeFor(0));
415 memmove(newButterfly->arrayStorage()->m_vector, storage->m_vector, sizeof(JSValue) * usedVectorLength);
417 for (unsigned i = requiredVectorLength; i < newVectorLength; i++)
418 newButterfly->arrayStorage()->m_vector[i].clear();
421 newButterfly->arrayStorage()->setVectorLength(newVectorLength);
422 newButterfly->arrayStorage()->m_indexBias = newIndexBias;
424 setButterfly(vm, newButterfly);
429 bool JSArray::setLengthWithArrayStorage(ExecState* exec, unsigned newLength, bool throwException, ArrayStorage* storage)
432 auto scope = DECLARE_THROW_SCOPE(vm);
434 unsigned length = storage->length();
436 // If the length is read only then we enter sparse mode, so should enter the following 'if'.
437 ASSERT(isLengthWritable() || storage->m_sparseMap);
439 if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
440 // Fail if the length is not writable.
441 if (map->lengthIsReadOnly())
442 return typeError(exec, scope, throwException, ASCIILiteral(ReadonlyPropertyWriteError));
444 if (newLength < length) {
445 // Copy any keys we might be interested in into a vector.
446 Vector<unsigned, 0, UnsafeVectorOverflow> keys;
447 keys.reserveInitialCapacity(std::min(map->size(), static_cast<size_t>(length - newLength)));
448 SparseArrayValueMap::const_iterator end = map->end();
449 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) {
450 unsigned index = static_cast<unsigned>(it->key);
451 if (index < length && index >= newLength)
455 // Check if the array is in sparse mode. If so there may be non-configurable
456 // properties, so we have to perform deletion with caution, if not we can
457 // delete values in any order.
458 if (map->sparseMode()) {
459 qsort(keys.begin(), keys.size(), sizeof(unsigned), compareKeysForQSort);
460 unsigned i = keys.size();
462 unsigned index = keys[--i];
463 SparseArrayValueMap::iterator it = map->find(index);
464 ASSERT(it != map->notFound());
465 if (it->value.attributes & PropertyAttribute::DontDelete) {
466 storage->setLength(index + 1);
467 return typeError(exec, scope, throwException, ASCIILiteral(UnableToDeletePropertyError));
472 for (unsigned i = 0; i < keys.size(); ++i)
473 map->remove(keys[i]);
475 deallocateSparseIndexMap();
480 if (newLength < length) {
481 // Delete properties from the vector.
482 unsigned usedVectorLength = std::min(length, storage->vectorLength());
483 for (unsigned i = newLength; i < usedVectorLength; ++i) {
484 WriteBarrier<Unknown>& valueSlot = storage->m_vector[i];
485 bool hadValue = !!valueSlot;
487 storage->m_numValuesInVector -= hadValue;
491 storage->setLength(newLength);
496 bool JSArray::appendMemcpy(ExecState* exec, VM& vm, unsigned startIndex, JSC::JSArray* otherArray)
498 auto scope = DECLARE_THROW_SCOPE(vm);
500 if (!canFastCopy(vm, otherArray))
503 IndexingType type = indexingType();
504 IndexingType otherType = otherArray->indexingType();
505 IndexingType copyType = mergeIndexingTypeForCopying(otherType);
506 if (type == ArrayWithUndecided && copyType != NonArray) {
507 if (copyType == ArrayWithInt32)
508 convertUndecidedToInt32(vm);
509 else if (copyType == ArrayWithDouble)
510 convertUndecidedToDouble(vm);
511 else if (copyType == ArrayWithContiguous)
512 convertUndecidedToContiguous(vm);
514 ASSERT(copyType == ArrayWithUndecided);
517 } else if (type != copyType)
520 unsigned otherLength = otherArray->length();
521 Checked<unsigned, RecordOverflow> checkedNewLength = startIndex;
522 checkedNewLength += otherLength;
525 if (checkedNewLength.safeGet(newLength) == CheckedState::DidOverflow) {
526 throwException(exec, scope, createRangeError(exec, ASCIILiteral(LengthExceededTheMaximumArrayLengthError)));
530 if (newLength >= MIN_SPARSE_ARRAY_INDEX)
533 if (!ensureLength(vm, newLength)) {
534 throwOutOfMemoryError(exec, scope);
537 ASSERT(copyType == indexingType());
539 if (UNLIKELY(otherType == ArrayWithUndecided)) {
540 auto* butterfly = this->butterfly();
541 if (type == ArrayWithDouble) {
542 for (unsigned i = startIndex; i < newLength; ++i)
543 butterfly->contiguousDouble().at(this, i) = PNaN;
545 for (unsigned i = startIndex; i < newLength; ++i)
546 butterfly->contiguousInt32().at(this, i).setWithoutWriteBarrier(JSValue());
548 } else if (type == ArrayWithDouble)
549 memcpy(butterfly()->contiguousDouble().data() + startIndex, otherArray->butterfly()->contiguousDouble().data(), sizeof(JSValue) * otherLength);
551 memcpy(butterfly()->contiguous().data() + startIndex, otherArray->butterfly()->contiguous().data(), sizeof(JSValue) * otherLength);
556 bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException)
559 auto scope = DECLARE_THROW_SCOPE(vm);
561 Butterfly* butterfly = this->butterfly();
562 switch (indexingType()) {
566 if (newLength >= MIN_SPARSE_ARRAY_INDEX) {
568 return setLengthWithArrayStorage(
569 exec, newLength, throwException,
570 ensureArrayStorage(vm));
572 createInitialUndecided(vm, newLength);
575 case ArrayWithUndecided:
577 case ArrayWithDouble:
578 case ArrayWithContiguous: {
579 if (newLength == butterfly->publicLength())
581 if (newLength > MAX_STORAGE_VECTOR_LENGTH // This check ensures that we can do fast push.
582 || (newLength >= MIN_SPARSE_ARRAY_INDEX
583 && !isDenseEnoughForVector(newLength, countElements()))) {
585 return setLengthWithArrayStorage(
586 exec, newLength, throwException,
587 ensureArrayStorage(vm));
589 if (newLength > butterfly->publicLength()) {
590 if (!ensureLength(vm, newLength)) {
591 throwOutOfMemoryError(exec, scope);
597 unsigned lengthToClear = butterfly->publicLength() - newLength;
598 unsigned costToAllocateNewButterfly = 64; // a heuristic.
599 if (lengthToClear > newLength && lengthToClear > costToAllocateNewButterfly) {
600 reallocateAndShrinkButterfly(vm, newLength);
604 if (indexingType() == ArrayWithDouble) {
605 for (unsigned i = butterfly->publicLength(); i-- > newLength;)
606 butterfly->contiguousDouble().at(this, i) = PNaN;
608 for (unsigned i = butterfly->publicLength(); i-- > newLength;)
609 butterfly->contiguous().at(this, i).clear();
611 butterfly->setPublicLength(newLength);
615 case ArrayWithArrayStorage:
616 case ArrayWithSlowPutArrayStorage:
618 return setLengthWithArrayStorage(exec, newLength, throwException, arrayStorage());
626 JSValue JSArray::pop(ExecState* exec)
629 auto scope = DECLARE_THROW_SCOPE(vm);
631 Butterfly* butterfly = this->butterfly();
633 switch (indexingType()) {
635 return jsUndefined();
637 case ArrayWithUndecided:
638 if (!butterfly->publicLength())
639 return jsUndefined();
640 // We have nothing but holes. So, drop down to the slow version.
644 case ArrayWithContiguous: {
645 unsigned length = butterfly->publicLength();
648 return jsUndefined();
650 RELEASE_ASSERT(length < butterfly->vectorLength());
651 JSValue value = butterfly->contiguous().at(this, length).get();
653 butterfly->contiguous().at(this, length).clear();
654 butterfly->setPublicLength(length);
660 case ArrayWithDouble: {
661 unsigned length = butterfly->publicLength();
664 return jsUndefined();
666 RELEASE_ASSERT(length < butterfly->vectorLength());
667 double value = butterfly->contiguousDouble().at(this, length);
668 if (value == value) {
669 butterfly->contiguousDouble().at(this, length) = PNaN;
670 butterfly->setPublicLength(length);
671 return JSValue(JSValue::EncodeAsDouble, value);
676 case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: {
677 ArrayStorage* storage = butterfly->arrayStorage();
679 unsigned length = storage->length();
681 if (!isLengthWritable())
682 throwTypeError(exec, scope, ASCIILiteral(ReadonlyPropertyWriteError));
683 return jsUndefined();
686 unsigned index = length - 1;
687 if (index < storage->vectorLength()) {
688 WriteBarrier<Unknown>& valueSlot = storage->m_vector[index];
690 --storage->m_numValuesInVector;
691 JSValue element = valueSlot.get();
694 RELEASE_ASSERT(isLengthWritable());
695 storage->setLength(index);
707 unsigned index = getArrayLength() - 1;
708 // Let element be the result of calling the [[Get]] internal method of O with argument indx.
709 JSValue element = get(exec, index);
710 RETURN_IF_EXCEPTION(scope, JSValue());
711 // Call the [[Delete]] internal method of O with arguments indx and true.
712 bool success = deletePropertyByIndex(this, exec, index);
713 RETURN_IF_EXCEPTION(scope, JSValue());
715 throwTypeError(exec, scope, ASCIILiteral(UnableToDeletePropertyError));
716 return jsUndefined();
718 // Call the [[Put]] internal method of O with arguments "length", indx, and true.
720 setLength(exec, index, true);
725 // Push & putIndex are almost identical, with two small differences.
726 // - we always are writing beyond the current array bounds, so it is always necessary to update m_length & m_numValuesInVector.
727 // - pushing to an array of length 2^32-1 stores the property, but throws a range error.
728 NEVER_INLINE void JSArray::push(ExecState* exec, JSValue value)
730 pushInline(exec, value);
733 JSArray* JSArray::fastSlice(ExecState& exec, unsigned startIndex, unsigned count)
735 auto arrayType = indexingType();
737 case ArrayWithDouble:
739 case ArrayWithContiguous: {
741 if (count >= MIN_SPARSE_ARRAY_INDEX || structure(vm)->holesMustForwardToPrototype(vm, this))
744 JSGlobalObject* lexicalGlobalObject = exec.lexicalGlobalObject();
745 Structure* resultStructure = lexicalGlobalObject->arrayStructureForIndexingTypeDuringAllocation(arrayType);
746 if (UNLIKELY(hasAnyArrayStorage(resultStructure->indexingType())))
749 ASSERT(!lexicalGlobalObject->isHavingABadTime());
750 ObjectInitializationScope scope(vm);
751 JSArray* resultArray = JSArray::tryCreateUninitializedRestricted(scope, resultStructure, count);
752 if (UNLIKELY(!resultArray))
755 auto& resultButterfly = *resultArray->butterfly();
756 if (arrayType == ArrayWithDouble)
757 memcpy(resultButterfly.contiguousDouble().data(), butterfly()->contiguousDouble().data() + startIndex, sizeof(JSValue) * count);
759 memcpy(resultButterfly.contiguous().data(), butterfly()->contiguous().data() + startIndex, sizeof(JSValue) * count);
760 resultButterfly.setPublicLength(count);
769 bool JSArray::shiftCountWithArrayStorage(VM& vm, unsigned startIndex, unsigned count, ArrayStorage* storage)
771 unsigned oldLength = storage->length();
772 RELEASE_ASSERT(count <= oldLength);
774 // If the array contains holes or is otherwise in an abnormal state,
775 // use the generic algorithm in ArrayPrototype.
776 if ((storage->hasHoles() && this->structure(vm)->holesMustForwardToPrototype(vm, this))
778 || shouldUseSlowPut(indexingType())) {
785 unsigned length = oldLength - count;
787 storage->m_numValuesInVector -= count;
788 storage->setLength(length);
790 unsigned vectorLength = storage->vectorLength();
794 if (startIndex >= vectorLength)
797 DisallowGC disallowGC;
798 auto locker = holdLock(*this);
800 if (startIndex + count > vectorLength)
801 count = vectorLength - startIndex;
803 unsigned usedVectorLength = std::min(vectorLength, oldLength);
805 unsigned numElementsBeforeShiftRegion = startIndex;
806 unsigned firstIndexAfterShiftRegion = startIndex + count;
807 unsigned numElementsAfterShiftRegion = usedVectorLength - firstIndexAfterShiftRegion;
808 ASSERT(numElementsBeforeShiftRegion + count + numElementsAfterShiftRegion == usedVectorLength);
810 // The point of this comparison seems to be to minimize the amount of elements that have to
811 // be moved during a shift operation.
812 if (numElementsBeforeShiftRegion < numElementsAfterShiftRegion) {
813 // The number of elements before the shift region is less than the number of elements
814 // after the shift region, so we move the elements before to the right.
815 if (numElementsBeforeShiftRegion) {
816 RELEASE_ASSERT(count + startIndex <= vectorLength);
817 if (storage->hasHoles()) {
818 for (unsigned i = startIndex; i-- > 0;) {
819 unsigned destinationIndex = count + i;
820 JSValue source = storage->m_vector[i].get();
821 JSValue dest = storage->m_vector[destinationIndex].get();
822 // Any time we overwrite a hole we know we overcounted the number of values we removed
823 // when we subtracted count from m_numValuesInVector above.
824 if (!dest && destinationIndex >= firstIndexAfterShiftRegion)
825 storage->m_numValuesInVector++;
826 storage->m_vector[count + i].setWithoutWriteBarrier(source);
829 memmove(storage->m_vector + count,
831 sizeof(JSValue) * startIndex);
834 // Adjust the Butterfly and the index bias. We only need to do this here because we're changing
835 // the start of the Butterfly, which needs to point at the first indexed property in the used
836 // portion of the vector.
837 Butterfly* butterfly = this->butterfly()->shift(structure(), count);
838 setButterfly(vm, butterfly);
839 storage = butterfly->arrayStorage();
840 storage->m_indexBias += count;
842 // Since we're consuming part of the vector by moving its beginning to the left,
843 // we need to modify the vector length appropriately.
844 storage->setVectorLength(vectorLength - count);
846 // The number of elements before the shift region is greater than or equal to the number
847 // of elements after the shift region, so we move the elements after the shift region to the left.
848 if (storage->hasHoles()) {
849 for (unsigned i = 0; i < numElementsAfterShiftRegion; ++i) {
850 unsigned destinationIndex = startIndex + i;
851 JSValue source = storage->m_vector[firstIndexAfterShiftRegion + i].get();
852 JSValue dest = storage->m_vector[destinationIndex].get();
853 // Any time we overwrite a hole we know we overcounted the number of values we removed
854 // when we subtracted count from m_numValuesInVector above.
855 if (!dest && destinationIndex < firstIndexAfterShiftRegion)
856 storage->m_numValuesInVector++;
857 storage->m_vector[startIndex + i].setWithoutWriteBarrier(source);
860 memmove(storage->m_vector + startIndex,
861 storage->m_vector + firstIndexAfterShiftRegion,
862 sizeof(JSValue) * numElementsAfterShiftRegion);
864 // Clear the slots of the elements we just moved.
865 unsigned startOfEmptyVectorTail = usedVectorLength - count;
866 for (unsigned i = startOfEmptyVectorTail; i < usedVectorLength; ++i)
867 storage->m_vector[i].clear();
868 // We don't modify the index bias or the Butterfly pointer in this case because we're not changing
869 // the start of the Butterfly, which needs to point at the first indexed property in the used
870 // portion of the vector. We also don't modify the vector length because we're not actually changing
871 // its length; we're just using less of it.
877 bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned& startIndex, unsigned count)
880 RELEASE_ASSERT(count > 0);
882 Butterfly* butterfly = this->butterfly();
884 switch (indexingType()) {
888 case ArrayWithUndecided:
889 // Don't handle this because it's confusing and it shouldn't come up.
893 case ArrayWithContiguous: {
894 unsigned oldLength = butterfly->publicLength();
895 RELEASE_ASSERT(count <= oldLength);
897 // We may have to walk the entire array to do the shift. We're willing to do
898 // so only if it's not horribly slow.
899 if (oldLength - (startIndex + count) >= MIN_SPARSE_ARRAY_INDEX)
900 return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
902 // Storing to a hole is fine since we're still having a good time. But reading from a hole
903 // is totally not fine, since we might have to read from the proto chain.
904 // We have to check for holes before we start moving things around so that we don't get halfway
905 // through shifting and then realize we should have been in ArrayStorage mode.
906 unsigned end = oldLength - count;
907 if (this->structure(vm)->holesMustForwardToPrototype(vm, this)) {
908 for (unsigned i = startIndex; i < end; ++i) {
909 JSValue v = butterfly->contiguous().at(this, i + count).get();
912 return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
914 butterfly->contiguous().at(this, i).setWithoutWriteBarrier(v);
917 memmove(butterfly->contiguous().data() + startIndex,
918 butterfly->contiguous().data() + startIndex + count,
919 sizeof(JSValue) * (end - startIndex));
922 for (unsigned i = end; i < oldLength; ++i)
923 butterfly->contiguous().at(this, i).clear();
925 butterfly->setPublicLength(oldLength - count);
927 // Our memmoving of values around in the array could have concealed some of them from
928 // the collector. Let's make sure that the collector scans this object again.
929 vm.heap.writeBarrier(this);
934 case ArrayWithDouble: {
935 unsigned oldLength = butterfly->publicLength();
936 RELEASE_ASSERT(count <= oldLength);
938 // We may have to walk the entire array to do the shift. We're willing to do
939 // so only if it's not horribly slow.
940 if (oldLength - (startIndex + count) >= MIN_SPARSE_ARRAY_INDEX)
941 return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
943 // Storing to a hole is fine since we're still having a good time. But reading from a hole
944 // is totally not fine, since we might have to read from the proto chain.
945 // We have to check for holes before we start moving things around so that we don't get halfway
946 // through shifting and then realize we should have been in ArrayStorage mode.
947 unsigned end = oldLength - count;
948 if (this->structure(vm)->holesMustForwardToPrototype(vm, this)) {
949 for (unsigned i = startIndex; i < end; ++i) {
950 double v = butterfly->contiguousDouble().at(this, i + count);
951 if (UNLIKELY(v != v)) {
953 return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
955 butterfly->contiguousDouble().at(this, i) = v;
958 memmove(butterfly->contiguousDouble().data() + startIndex,
959 butterfly->contiguousDouble().data() + startIndex + count,
960 sizeof(JSValue) * (end - startIndex));
962 for (unsigned i = end; i < oldLength; ++i)
963 butterfly->contiguousDouble().at(this, i) = PNaN;
965 butterfly->setPublicLength(oldLength - count);
969 case ArrayWithArrayStorage:
970 case ArrayWithSlowPutArrayStorage:
971 return shiftCountWithArrayStorage(vm, startIndex, count, arrayStorage());
979 // Returns true if the unshift can be handled, false to fallback.
980 bool JSArray::unshiftCountWithArrayStorage(ExecState* exec, unsigned startIndex, unsigned count, ArrayStorage* storage)
983 auto scope = DECLARE_THROW_SCOPE(vm);
985 unsigned length = storage->length();
987 RELEASE_ASSERT(startIndex <= length);
989 // If the array contains holes or is otherwise in an abnormal state,
990 // use the generic algorithm in ArrayPrototype.
991 if (storage->hasHoles() || storage->inSparseMode() || shouldUseSlowPut(indexingType()))
994 bool moveFront = !startIndex || startIndex < length / 2;
996 unsigned vectorLength = storage->vectorLength();
998 // Need to have GC deferred around the unshiftCountSlowCase(), since that leaves the butterfly in
999 // a weird state: some parts of it will be left uninitialized, which we will fill in here.
1000 DeferGC deferGC(vm.heap);
1001 auto locker = holdLock(*this);
1003 if (moveFront && storage->m_indexBias >= count) {
1004 Butterfly* newButterfly = storage->butterfly()->unshift(structure(), count);
1005 storage = newButterfly->arrayStorage();
1006 storage->m_indexBias -= count;
1007 storage->setVectorLength(vectorLength + count);
1008 setButterfly(vm, newButterfly);
1009 } else if (!moveFront && vectorLength - length >= count)
1010 storage = storage->butterfly()->arrayStorage();
1011 else if (unshiftCountSlowCase(locker, vm, deferGC, moveFront, count))
1012 storage = arrayStorage();
1014 throwOutOfMemoryError(exec, scope);
1018 WriteBarrier<Unknown>* vector = storage->m_vector;
1022 memmove(vector, vector + count, startIndex * sizeof(JSValue));
1023 else if (length - startIndex)
1024 memmove(vector + startIndex + count, vector + startIndex, (length - startIndex) * sizeof(JSValue));
1027 for (unsigned i = 0; i < count; i++)
1028 vector[i + startIndex].clear();
1033 bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex, unsigned count)
1035 VM& vm = exec->vm();
1036 auto scope = DECLARE_THROW_SCOPE(vm);
1038 Butterfly* butterfly = this->butterfly();
1040 switch (indexingType()) {
1042 case ArrayWithUndecided:
1043 // We could handle this. But it shouldn't ever come up, so we won't.
1046 case ArrayWithInt32:
1047 case ArrayWithContiguous: {
1048 unsigned oldLength = butterfly->publicLength();
1050 // We may have to walk the entire array to do the unshift. We're willing to do so
1051 // only if it's not horribly slow.
1052 if (oldLength - startIndex >= MIN_SPARSE_ARRAY_INDEX) {
1054 return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(vm));
1057 if (!ensureLength(vm, oldLength + count)) {
1058 throwOutOfMemoryError(exec, scope);
1061 butterfly = this->butterfly();
1063 // We have to check for holes before we start moving things around so that we don't get halfway
1064 // through shifting and then realize we should have been in ArrayStorage mode.
1065 for (unsigned i = oldLength; i-- > startIndex;) {
1066 JSValue v = butterfly->contiguous().at(this, i).get();
1069 return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(vm));
1073 for (unsigned i = oldLength; i-- > startIndex;) {
1074 JSValue v = butterfly->contiguous().at(this, i).get();
1076 butterfly->contiguous().at(this, i + count).setWithoutWriteBarrier(v);
1079 // Our memmoving of values around in the array could have concealed some of them from
1080 // the collector. Let's make sure that the collector scans this object again.
1081 vm.heap.writeBarrier(this);
1083 // NOTE: we're leaving being garbage in the part of the array that we shifted out
1084 // of. This is fine because the caller is required to store over that area, and
1085 // in contiguous mode storing into a hole is guaranteed to behave exactly the same
1086 // as storing over an existing element.
1091 case ArrayWithDouble: {
1092 unsigned oldLength = butterfly->publicLength();
1094 // We may have to walk the entire array to do the unshift. We're willing to do so
1095 // only if it's not horribly slow.
1096 if (oldLength - startIndex >= MIN_SPARSE_ARRAY_INDEX) {
1098 return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(vm));
1101 if (!ensureLength(vm, oldLength + count)) {
1102 throwOutOfMemoryError(exec, scope);
1105 butterfly = this->butterfly();
1107 // We have to check for holes before we start moving things around so that we don't get halfway
1108 // through shifting and then realize we should have been in ArrayStorage mode.
1109 for (unsigned i = oldLength; i-- > startIndex;) {
1110 double v = butterfly->contiguousDouble().at(this, i);
1111 if (UNLIKELY(v != v)) {
1113 return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(vm));
1117 for (unsigned i = oldLength; i-- > startIndex;) {
1118 double v = butterfly->contiguousDouble().at(this, i);
1120 butterfly->contiguousDouble().at(this, i + count) = v;
1123 // NOTE: we're leaving being garbage in the part of the array that we shifted out
1124 // of. This is fine because the caller is required to store over that area, and
1125 // in contiguous mode storing into a hole is guaranteed to behave exactly the same
1126 // as storing over an existing element.
1131 case ArrayWithArrayStorage:
1132 case ArrayWithSlowPutArrayStorage:
1134 return unshiftCountWithArrayStorage(exec, startIndex, count, arrayStorage());
1142 void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
1146 WriteBarrier<Unknown>* vector;
1148 Butterfly* butterfly = this->butterfly();
1150 switch (indexingType()) {
1154 case ArrayWithUndecided: {
1160 case ArrayWithInt32:
1161 case ArrayWithContiguous: {
1162 vectorEnd = butterfly->publicLength();
1163 vector = butterfly->contiguous().data();
1167 case ArrayWithDouble: {
1170 for (; i < butterfly->publicLength(); ++i) {
1171 double v = butterfly->contiguousDouble().at(this, i);
1174 args.append(JSValue(JSValue::EncodeAsDouble, v));
1179 case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: {
1180 ArrayStorage* storage = butterfly->arrayStorage();
1182 vector = storage->m_vector;
1183 vectorEnd = std::min(storage->length(), storage->vectorLength());
1189 #if COMPILER_QUIRK(CONSIDERS_UNREACHABLE_CODE)
1196 for (; i < vectorEnd; ++i) {
1197 WriteBarrier<Unknown>& v = vector[i];
1200 args.append(v.get());
1203 // FIXME: What prevents this from being called with a RuntimeArray? The length function will always return 0 in that case.
1204 for (; i < length(); ++i)
1205 args.append(get(exec, i));
1208 void JSArray::copyToArguments(ExecState* exec, VirtualRegister firstElementDest, unsigned offset, unsigned length)
1210 VM& vm = exec->vm();
1211 auto scope = DECLARE_THROW_SCOPE(vm);
1213 unsigned i = offset;
1214 WriteBarrier<Unknown>* vector;
1216 length += offset; // We like to think of the length as being our length, rather than the output length.
1218 // FIXME: What prevents this from being called with a RuntimeArray? The length function will always return 0 in that case.
1219 ASSERT(length == this->length());
1221 Butterfly* butterfly = this->butterfly();
1222 switch (indexingType()) {
1226 case ArrayWithUndecided: {
1232 case ArrayWithInt32:
1233 case ArrayWithContiguous: {
1234 vector = butterfly->contiguous().data();
1235 vectorEnd = butterfly->publicLength();
1239 case ArrayWithDouble: {
1242 for (; i < butterfly->publicLength(); ++i) {
1243 ASSERT(i < butterfly->vectorLength());
1244 double v = butterfly->contiguousDouble().at(this, i);
1247 exec->r(firstElementDest + i - offset) = JSValue(JSValue::EncodeAsDouble, v);
1252 case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: {
1253 ArrayStorage* storage = butterfly->arrayStorage();
1254 vector = storage->m_vector;
1255 vectorEnd = std::min(length, storage->vectorLength());
1261 #if COMPILER_QUIRK(CONSIDERS_UNREACHABLE_CODE)
1268 for (; i < vectorEnd; ++i) {
1269 WriteBarrier<Unknown>& v = vector[i];
1272 exec->r(firstElementDest + i - offset) = v.get();
1275 for (; i < length; ++i) {
1276 exec->r(firstElementDest + i - offset) = get(exec, i);
1277 RETURN_IF_EXCEPTION(scope, void());
1281 bool JSArray::isIteratorProtocolFastAndNonObservable()
1283 JSGlobalObject* globalObject = this->globalObject();
1284 if (!globalObject->isArrayPrototypeIteratorProtocolFastAndNonObservable())
1287 Structure* structure = this->structure();
1288 // This is the fast case. Many arrays will be an original array.
1289 if (globalObject->isOriginalArrayStructure(structure))
1292 if (structure->mayInterceptIndexedAccesses())
1295 VM& vm = globalObject->vm();
1296 if (getPrototypeDirect(vm) != globalObject->arrayPrototype())
1299 if (getDirectOffset(vm, vm.propertyNames->iteratorSymbol) != invalidOffset)