2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3 * Copyright (C) 2003-2018 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>
40 const ASCIILiteral LengthExceededTheMaximumArrayLengthError { "Length exceeded the maximum array length"_s };
42 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSArray);
44 const ClassInfo JSArray::s_info = {"Array", &JSNonFinalObject::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSArray)};
46 JSArray* JSArray::tryCreateUninitializedRestricted(ObjectInitializationScope& scope, GCDeferralContext* deferralContext, Structure* structure, unsigned initialLength)
50 if (UNLIKELY(initialLength > MAX_STORAGE_VECTOR_LENGTH))
53 unsigned outOfLineStorage = structure->outOfLineCapacity();
55 IndexingType indexingType = structure->indexingType();
56 if (LIKELY(!hasAnyArrayStorage(indexingType))) {
58 hasUndecided(indexingType)
59 || hasInt32(indexingType)
60 || hasDouble(indexingType)
61 || hasContiguous(indexingType));
63 unsigned vectorLength = Butterfly::optimalContiguousVectorLength(structure, initialLength);
64 void* temp = vm.jsValueGigacageAuxiliarySpace.allocateNonVirtual(
66 Butterfly::totalSize(0, outOfLineStorage, true, vectorLength * sizeof(EncodedJSValue)),
67 deferralContext, AllocationFailureMode::ReturnNull);
70 butterfly = Butterfly::fromBase(temp, 0, outOfLineStorage);
71 butterfly->setVectorLength(vectorLength);
72 butterfly->setPublicLength(initialLength);
73 if (hasDouble(indexingType)) {
74 for (unsigned i = initialLength; i < vectorLength; ++i)
75 butterfly->contiguousDouble().atUnsafe(i) = PNaN;
77 for (unsigned i = initialLength; i < vectorLength; ++i)
78 butterfly->contiguous().atUnsafe(i).clear();
81 static const unsigned indexBias = 0;
82 unsigned vectorLength = ArrayStorage::optimalVectorLength(indexBias, structure, initialLength);
83 void* temp = vm.jsValueGigacageAuxiliarySpace.allocateNonVirtual(
85 Butterfly::totalSize(indexBias, outOfLineStorage, true, ArrayStorage::sizeFor(vectorLength)),
86 deferralContext, AllocationFailureMode::ReturnNull);
89 butterfly = Butterfly::fromBase(temp, indexBias, outOfLineStorage);
90 *butterfly->indexingHeader() = indexingHeaderForArrayStorage(initialLength, vectorLength);
91 ArrayStorage* storage = butterfly->arrayStorage();
92 storage->m_indexBias = indexBias;
93 storage->m_sparseMap.clear();
94 storage->m_numValuesInVector = initialLength;
95 for (unsigned i = initialLength; i < vectorLength; ++i)
96 storage->m_vector[i].clear();
99 JSArray* result = createWithButterfly(vm, deferralContext, structure, butterfly);
101 const bool createUninitialized = true;
102 scope.notifyAllocated(result, createUninitialized);
106 void JSArray::setLengthWritable(ExecState* exec, bool writable)
108 ASSERT(isLengthWritable() || !writable);
109 if (!isLengthWritable() || writable)
112 enterDictionaryIndexingMode(exec->vm());
114 SparseArrayValueMap* map = arrayStorage()->m_sparseMap.get();
116 map->setLengthIsReadOnly();
119 // Defined in ES5.1 15.4.5.1
120 bool JSArray::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException)
123 auto scope = DECLARE_THROW_SCOPE(vm);
125 JSArray* array = jsCast<JSArray*>(object);
127 // 3. If P is "length", then
128 if (propertyName == vm.propertyNames->length) {
129 // All paths through length definition call the default [[DefineOwnProperty]], hence:
130 // from ES5.1 8.12.9 7.a.
131 if (descriptor.configurablePresent() && descriptor.configurable())
132 return typeError(exec, scope, throwException, UnconfigurablePropertyChangeConfigurabilityError);
133 // from ES5.1 8.12.9 7.b.
134 if (descriptor.enumerablePresent() && descriptor.enumerable())
135 return typeError(exec, scope, throwException, UnconfigurablePropertyChangeEnumerabilityError);
137 // a. If the [[Value]] field of Desc is absent, then
138 // a.i. Return the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", Desc, and Throw as arguments.
139 if (descriptor.isAccessorDescriptor())
140 return typeError(exec, scope, throwException, UnconfigurablePropertyChangeAccessMechanismError);
141 // from ES5.1 8.12.9 10.a.
142 if (!array->isLengthWritable() && descriptor.writablePresent() && descriptor.writable())
143 return typeError(exec, scope, throwException, UnconfigurablePropertyChangeWritabilityError);
144 // This descriptor is either just making length read-only, or changing nothing!
145 if (!descriptor.value()) {
146 if (descriptor.writablePresent())
147 array->setLengthWritable(exec, descriptor.writable());
151 // b. Let newLenDesc be a copy of Desc.
152 // c. Let newLen be ToUint32(Desc.[[Value]]).
153 unsigned newLen = descriptor.value().toUInt32(exec);
154 RETURN_IF_EXCEPTION(scope, false);
155 // d. If newLen is not equal to ToNumber( Desc.[[Value]]), throw a RangeError exception.
156 double valueAsNumber = descriptor.value().toNumber(exec);
157 RETURN_IF_EXCEPTION(scope, false);
158 if (newLen != valueAsNumber) {
159 JSC::throwException(exec, scope, createRangeError(exec, "Invalid array length"_s));
163 // Based on SameValue check in 8.12.9, this is always okay.
164 // FIXME: Nothing prevents this from being called on a RuntimeArray, and the length function will always return 0 in that case.
165 if (newLen == array->length()) {
166 if (descriptor.writablePresent())
167 array->setLengthWritable(exec, descriptor.writable());
171 // e. Set newLenDesc.[[Value] to newLen.
172 // f. If newLen >= oldLen, then
173 // f.i. Return the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", newLenDesc, and Throw as arguments.
174 // g. Reject if oldLenDesc.[[Writable]] is false.
175 if (!array->isLengthWritable())
176 return typeError(exec, scope, throwException, ReadonlyPropertyChangeError);
178 // h. If newLenDesc.[[Writable]] is absent or has the value true, let newWritable be true.
180 // i.i. Need to defer setting the [[Writable]] attribute to false in case any elements cannot be deleted.
181 // i.ii. Let newWritable be false.
182 // i.iii. Set newLenDesc.[[Writable] to true.
183 // 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.
184 // k. If succeeded is false, return false.
185 // l. While newLen < oldLen repeat,
186 // l.i. Set oldLen to oldLen – 1.
187 // l.ii. Let deleteSucceeded be the result of calling the [[Delete]] internal method of A passing ToString(oldLen) and false as arguments.
188 // l.iii. If deleteSucceeded is false, then
189 bool success = array->setLength(exec, newLen, throwException);
190 EXCEPTION_ASSERT(!scope.exception() || !success);
192 // 1. Set newLenDesc.[[Value] to oldLen+1.
193 // 2. If newWritable is false, set newLenDesc.[[Writable] to false.
194 // 3. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", newLenDesc, and false as arguments.
196 if (descriptor.writablePresent())
197 array->setLengthWritable(exec, descriptor.writable());
201 // m. If newWritable is false, then
202 // i. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length",
203 // Property Descriptor{[[Writable]]: false}, and false as arguments. This call will always
205 if (descriptor.writablePresent())
206 array->setLengthWritable(exec, descriptor.writable());
211 // 4. Else if P is an array index (15.4), then
212 // a. Let index be ToUint32(P).
213 if (std::optional<uint32_t> optionalIndex = parseIndex(propertyName)) {
214 // b. Reject if index >= oldLen and oldLenDesc.[[Writable]] is false.
215 uint32_t index = optionalIndex.value();
216 // FIXME: Nothing prevents this from being called on a RuntimeArray, and the length function will always return 0 in that case.
217 if (index >= array->length() && !array->isLengthWritable())
218 return typeError(exec, scope, throwException, "Attempting to define numeric property on array with non-writable length property."_s);
219 // 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.
220 // d. Reject if succeeded is false.
221 // e. If index >= oldLen
222 // e.i. Set oldLenDesc.[[Value]] to index + 1.
223 // 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.
226 return array->defineOwnIndexedProperty(exec, index, descriptor, throwException);
230 return array->JSObject::defineOwnNonIndexProperty(exec, propertyName, descriptor, throwException);
233 bool JSArray::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
236 JSArray* thisObject = jsCast<JSArray*>(object);
237 if (propertyName == vm.propertyNames->length) {
238 unsigned attributes = thisObject->isLengthWritable() ? PropertyAttribute::DontDelete | PropertyAttribute::DontEnum : PropertyAttribute::DontDelete | PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly;
239 slot.setValue(thisObject, attributes, jsNumber(thisObject->length()));
243 return JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot);
247 bool JSArray::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
250 auto scope = DECLARE_THROW_SCOPE(vm);
252 JSArray* thisObject = jsCast<JSArray*>(cell);
254 if (UNLIKELY(isThisValueAltered(slot, thisObject))) {
256 return ordinarySetSlow(exec, thisObject, propertyName, value, slot.thisValue(), slot.isStrictMode());
259 if (isCopyOnWrite(thisObject->indexingMode()))
260 thisObject->convertFromCopyOnWrite(vm);
262 if (propertyName == vm.propertyNames->length) {
263 if (!thisObject->isLengthWritable())
265 unsigned newLength = value.toUInt32(exec);
266 RETURN_IF_EXCEPTION(scope, false);
267 double valueAsNumber = value.toNumber(exec);
268 RETURN_IF_EXCEPTION(scope, false);
269 if (valueAsNumber != static_cast<double>(newLength)) {
270 throwException(exec, scope, createRangeError(exec, "Invalid array length"_s));
274 return thisObject->setLength(exec, newLength, slot.isStrictMode());
278 return JSObject::put(thisObject, exec, propertyName, value, slot);
281 bool JSArray::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
284 JSArray* thisObject = jsCast<JSArray*>(cell);
286 if (propertyName == vm.propertyNames->length)
289 return JSObject::deleteProperty(thisObject, exec, propertyName);
292 static int compareKeysForQSort(const void* a, const void* b)
294 unsigned da = *static_cast<const unsigned*>(a);
295 unsigned db = *static_cast<const unsigned*>(b);
296 return (da > db) - (da < db);
299 void JSArray::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
302 JSArray* thisObject = jsCast<JSArray*>(object);
304 if (mode.includeDontEnumProperties())
305 propertyNames.add(vm.propertyNames->length);
307 JSObject::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode);
310 // This method makes room in the vector, but leaves the new space for count slots uncleared.
311 bool JSArray::unshiftCountSlowCase(const AbstractLocker&, VM& vm, DeferGC&, bool addToFront, unsigned count)
313 ArrayStorage* storage = ensureArrayStorage(vm);
314 Butterfly* butterfly = storage->butterfly();
315 Structure* structure = this->structure(vm);
316 unsigned propertyCapacity = structure->outOfLineCapacity();
317 unsigned propertySize = structure->outOfLineSize();
319 // If not, we should have handled this on the fast path.
320 ASSERT(!addToFront || count > storage->m_indexBias);
323 // Gather 4 key metrics:
324 // * usedVectorLength - how many entries are currently in the vector (conservative estimate - fewer may be in use in sparse vectors).
325 // * requiredVectorLength - how many entries are will there be in the vector, after allocating space for 'count' more.
326 // * currentCapacity - what is the current size of the vector, including any pre-capacity.
327 // * desiredCapacity - how large should we like to grow the vector to - based on 2x requiredVectorLength.
329 unsigned length = storage->length();
330 unsigned oldVectorLength = storage->vectorLength();
331 unsigned usedVectorLength = std::min(oldVectorLength, length);
332 ASSERT(usedVectorLength <= MAX_STORAGE_VECTOR_LENGTH);
333 // Check that required vector length is possible, in an overflow-safe fashion.
334 if (count > MAX_STORAGE_VECTOR_LENGTH - usedVectorLength)
336 unsigned requiredVectorLength = usedVectorLength + count;
337 ASSERT(requiredVectorLength <= MAX_STORAGE_VECTOR_LENGTH);
338 // The sum of m_vectorLength and m_indexBias will never exceed MAX_STORAGE_VECTOR_LENGTH.
339 ASSERT(storage->vectorLength() <= MAX_STORAGE_VECTOR_LENGTH && (MAX_STORAGE_VECTOR_LENGTH - storage->vectorLength()) >= storage->m_indexBias);
340 unsigned currentCapacity = storage->vectorLength() + storage->m_indexBias;
341 // The calculation of desiredCapacity won't overflow, due to the range of MAX_STORAGE_VECTOR_LENGTH.
342 // FIXME: This code should be fixed to avoid internal fragmentation. It's not super high
343 // priority since increaseVectorLength() will "fix" any mistakes we make, but it would be cool
344 // to get this right eventually.
345 unsigned desiredCapacity = std::min(MAX_STORAGE_VECTOR_LENGTH, std::max(BASE_ARRAY_STORAGE_VECTOR_LEN, requiredVectorLength) << 1);
348 // We're either going to choose to allocate a new ArrayStorage, or we're going to reuse the existing one.
350 void* newAllocBase = nullptr;
351 unsigned newStorageCapacity;
352 bool allocatedNewStorage;
353 // If the current storage array is sufficiently large (but not too large!) then just keep using it.
354 if (currentCapacity > desiredCapacity && isDenseEnoughForVector(currentCapacity, requiredVectorLength)) {
355 newAllocBase = butterfly->base(structure);
356 newStorageCapacity = currentCapacity;
357 allocatedNewStorage = false;
359 const unsigned preCapacity = 0;
360 Butterfly* newButterfly = Butterfly::tryCreateUninitialized(vm, this, preCapacity, propertyCapacity, true, ArrayStorage::sizeFor(desiredCapacity));
363 newAllocBase = newButterfly->base(preCapacity, propertyCapacity);
364 newStorageCapacity = desiredCapacity;
365 allocatedNewStorage = true;
369 // Work out where we're going to move things to.
371 // Determine how much of the vector to use as pre-capacity, and how much as post-capacity.
372 // If we're adding to the end, we'll add all the new space to the end.
373 // If the vector had no free post-capacity (length >= m_vectorLength), don't give it any.
374 // If it did, we calculate the amount that will remain based on an atomic decay - leave the
375 // vector with half the post-capacity it had previously.
376 unsigned postCapacity = 0;
378 postCapacity = newStorageCapacity - requiredVectorLength;
379 else if (length < storage->vectorLength()) {
380 // Atomic decay, + the post-capacity cannot be greater than what is available.
381 postCapacity = std::min((storage->vectorLength() - length) >> 1, newStorageCapacity - requiredVectorLength);
382 // If we're moving contents within the same allocation, the post-capacity is being reduced.
383 ASSERT(newAllocBase != butterfly->base(structure) || postCapacity < storage->vectorLength() - length);
386 unsigned newVectorLength = requiredVectorLength + postCapacity;
387 RELEASE_ASSERT(newVectorLength <= MAX_STORAGE_VECTOR_LENGTH);
388 unsigned preCapacity = newStorageCapacity - newVectorLength;
390 Butterfly* newButterfly = Butterfly::fromBase(newAllocBase, preCapacity, propertyCapacity);
393 ASSERT(count + usedVectorLength <= newVectorLength);
394 memmove(newButterfly->arrayStorage()->m_vector + count, storage->m_vector, sizeof(JSValue) * usedVectorLength);
395 memmove(newButterfly->propertyStorage() - propertySize, butterfly->propertyStorage() - propertySize, sizeof(JSValue) * propertySize + sizeof(IndexingHeader) + ArrayStorage::sizeFor(0));
397 if (allocatedNewStorage) {
398 // We will set the vectorLength to newVectorLength. We populated requiredVectorLength
399 // (usedVectorLength + count), which is less. Clear the difference.
400 for (unsigned i = requiredVectorLength; i < newVectorLength; ++i)
401 newButterfly->arrayStorage()->m_vector[i].clear();
402 // We don't need to zero the pre-capacity because it is not available to use as property storage.
403 memset(newButterfly->base(0, propertyCapacity), 0, (propertyCapacity - propertySize) * sizeof(JSValue));
405 } else if ((newAllocBase != butterfly->base(structure)) || (preCapacity != storage->m_indexBias)) {
406 memmove(newButterfly->propertyStorage() - propertyCapacity, butterfly->propertyStorage() - propertyCapacity, sizeof(JSValue) * propertyCapacity + sizeof(IndexingHeader) + ArrayStorage::sizeFor(0));
407 memmove(newButterfly->arrayStorage()->m_vector, storage->m_vector, sizeof(JSValue) * usedVectorLength);
409 for (unsigned i = requiredVectorLength; i < newVectorLength; i++)
410 newButterfly->arrayStorage()->m_vector[i].clear();
413 newButterfly->arrayStorage()->setVectorLength(newVectorLength);
414 newButterfly->arrayStorage()->m_indexBias = preCapacity;
416 setButterfly(vm, newButterfly);
421 bool JSArray::setLengthWithArrayStorage(ExecState* exec, unsigned newLength, bool throwException, ArrayStorage* storage)
424 auto scope = DECLARE_THROW_SCOPE(vm);
426 unsigned length = storage->length();
428 // If the length is read only then we enter sparse mode, so should enter the following 'if'.
429 ASSERT(isLengthWritable() || storage->m_sparseMap);
431 if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
432 // Fail if the length is not writable.
433 if (map->lengthIsReadOnly())
434 return typeError(exec, scope, throwException, ReadonlyPropertyWriteError);
436 if (newLength < length) {
437 // Copy any keys we might be interested in into a vector.
438 Vector<unsigned, 0, UnsafeVectorOverflow> keys;
439 keys.reserveInitialCapacity(std::min(map->size(), static_cast<size_t>(length - newLength)));
440 SparseArrayValueMap::const_iterator end = map->end();
441 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) {
442 unsigned index = static_cast<unsigned>(it->key);
443 if (index < length && index >= newLength)
447 // Check if the array is in sparse mode. If so there may be non-configurable
448 // properties, so we have to perform deletion with caution, if not we can
449 // delete values in any order.
450 if (map->sparseMode()) {
451 qsort(keys.begin(), keys.size(), sizeof(unsigned), compareKeysForQSort);
452 unsigned i = keys.size();
454 unsigned index = keys[--i];
455 SparseArrayValueMap::iterator it = map->find(index);
456 ASSERT(it != map->notFound());
457 if (it->value.attributes & PropertyAttribute::DontDelete) {
458 storage->setLength(index + 1);
459 return typeError(exec, scope, throwException, UnableToDeletePropertyError);
464 for (unsigned i = 0; i < keys.size(); ++i)
465 map->remove(keys[i]);
467 deallocateSparseIndexMap();
472 if (newLength < length) {
473 // Delete properties from the vector.
474 unsigned usedVectorLength = std::min(length, storage->vectorLength());
475 for (unsigned i = newLength; i < usedVectorLength; ++i) {
476 WriteBarrier<Unknown>& valueSlot = storage->m_vector[i];
477 bool hadValue = !!valueSlot;
479 storage->m_numValuesInVector -= hadValue;
483 storage->setLength(newLength);
488 bool JSArray::appendMemcpy(ExecState* exec, VM& vm, unsigned startIndex, JSC::JSArray* otherArray)
490 auto scope = DECLARE_THROW_SCOPE(vm);
492 if (!canFastCopy(vm, otherArray))
495 IndexingType type = indexingType();
496 IndexingType otherType = otherArray->indexingType();
497 IndexingType copyType = mergeIndexingTypeForCopying(otherType);
498 if (type == ArrayWithUndecided && copyType != NonArray) {
499 if (copyType == ArrayWithInt32)
500 convertUndecidedToInt32(vm);
501 else if (copyType == ArrayWithDouble)
502 convertUndecidedToDouble(vm);
503 else if (copyType == ArrayWithContiguous)
504 convertUndecidedToContiguous(vm);
506 ASSERT(copyType == ArrayWithUndecided);
509 } else if (type != copyType)
512 unsigned otherLength = otherArray->length();
513 Checked<unsigned, RecordOverflow> checkedNewLength = startIndex;
514 checkedNewLength += otherLength;
517 if (checkedNewLength.safeGet(newLength) == CheckedState::DidOverflow) {
518 throwException(exec, scope, createRangeError(exec, LengthExceededTheMaximumArrayLengthError));
522 if (newLength >= MIN_SPARSE_ARRAY_INDEX)
525 if (!ensureLength(vm, newLength)) {
526 throwOutOfMemoryError(exec, scope);
529 ASSERT(copyType == indexingType());
531 if (UNLIKELY(otherType == ArrayWithUndecided)) {
532 auto* butterfly = this->butterfly();
533 if (type == ArrayWithDouble) {
534 for (unsigned i = startIndex; i < newLength; ++i)
535 butterfly->contiguousDouble().at(this, i) = PNaN;
537 for (unsigned i = startIndex; i < newLength; ++i)
538 butterfly->contiguousInt32().at(this, i).setWithoutWriteBarrier(JSValue());
540 } else if (type == ArrayWithDouble)
541 memcpy(butterfly()->contiguousDouble().data() + startIndex, otherArray->butterfly()->contiguousDouble().data(), sizeof(JSValue) * otherLength);
543 memcpy(butterfly()->contiguous().data() + startIndex, otherArray->butterfly()->contiguous().data(), sizeof(JSValue) * otherLength);
544 vm.heap.writeBarrier(this);
550 bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException)
553 auto scope = DECLARE_THROW_SCOPE(vm);
555 Butterfly* butterfly = this->butterfly();
556 switch (indexingMode()) {
560 if (newLength >= MIN_SPARSE_ARRAY_INDEX) {
562 return setLengthWithArrayStorage(
563 exec, newLength, throwException,
564 ensureArrayStorage(vm));
566 createInitialUndecided(vm, newLength);
569 case CopyOnWriteArrayWithInt32:
570 case CopyOnWriteArrayWithDouble:
571 case CopyOnWriteArrayWithContiguous:
572 if (newLength == butterfly->publicLength())
574 convertFromCopyOnWrite(vm);
575 butterfly = this->butterfly();
578 case ArrayWithUndecided:
580 case ArrayWithDouble:
581 case ArrayWithContiguous: {
582 if (newLength == butterfly->publicLength())
584 if (newLength > MAX_STORAGE_VECTOR_LENGTH // This check ensures that we can do fast push.
585 || (newLength >= MIN_SPARSE_ARRAY_INDEX
586 && !isDenseEnoughForVector(newLength, countElements()))) {
588 return setLengthWithArrayStorage(
589 exec, newLength, throwException,
590 ensureArrayStorage(vm));
592 if (newLength > butterfly->publicLength()) {
593 if (!ensureLength(vm, newLength)) {
594 throwOutOfMemoryError(exec, scope);
600 unsigned lengthToClear = butterfly->publicLength() - newLength;
601 unsigned costToAllocateNewButterfly = 64; // a heuristic.
602 if (lengthToClear > newLength && lengthToClear > costToAllocateNewButterfly) {
603 reallocateAndShrinkButterfly(vm, newLength);
607 if (indexingType() == ArrayWithDouble) {
608 for (unsigned i = butterfly->publicLength(); i-- > newLength;)
609 butterfly->contiguousDouble().at(this, i) = PNaN;
611 for (unsigned i = butterfly->publicLength(); i-- > newLength;)
612 butterfly->contiguous().at(this, i).clear();
614 butterfly->setPublicLength(newLength);
618 case ArrayWithArrayStorage:
619 case ArrayWithSlowPutArrayStorage:
621 return setLengthWithArrayStorage(exec, newLength, throwException, arrayStorage());
629 JSValue JSArray::pop(ExecState* exec)
632 auto scope = DECLARE_THROW_SCOPE(vm);
634 if (isCopyOnWrite(indexingMode()))
635 convertFromCopyOnWrite(vm);
637 Butterfly* butterfly = this->butterfly();
639 switch (indexingType()) {
641 return jsUndefined();
643 case ArrayWithUndecided:
644 if (!butterfly->publicLength())
645 return jsUndefined();
646 // We have nothing but holes. So, drop down to the slow version.
650 case ArrayWithContiguous: {
651 unsigned length = butterfly->publicLength();
654 return jsUndefined();
656 RELEASE_ASSERT(length < butterfly->vectorLength());
657 JSValue value = butterfly->contiguous().at(this, length).get();
659 butterfly->contiguous().at(this, length).clear();
660 butterfly->setPublicLength(length);
666 case ArrayWithDouble: {
667 unsigned length = butterfly->publicLength();
670 return jsUndefined();
672 RELEASE_ASSERT(length < butterfly->vectorLength());
673 double value = butterfly->contiguousDouble().at(this, length);
674 if (value == value) {
675 butterfly->contiguousDouble().at(this, length) = PNaN;
676 butterfly->setPublicLength(length);
677 return JSValue(JSValue::EncodeAsDouble, value);
682 case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: {
683 ArrayStorage* storage = butterfly->arrayStorage();
685 unsigned length = storage->length();
687 if (!isLengthWritable())
688 throwTypeError(exec, scope, ReadonlyPropertyWriteError);
689 return jsUndefined();
692 unsigned index = length - 1;
693 if (index < storage->vectorLength()) {
694 WriteBarrier<Unknown>& valueSlot = storage->m_vector[index];
696 --storage->m_numValuesInVector;
697 JSValue element = valueSlot.get();
700 RELEASE_ASSERT(isLengthWritable());
701 storage->setLength(index);
713 unsigned index = getArrayLength() - 1;
714 // Let element be the result of calling the [[Get]] internal method of O with argument indx.
715 JSValue element = get(exec, index);
716 RETURN_IF_EXCEPTION(scope, JSValue());
717 // Call the [[Delete]] internal method of O with arguments indx and true.
718 bool success = deletePropertyByIndex(this, exec, index);
719 RETURN_IF_EXCEPTION(scope, JSValue());
721 throwTypeError(exec, scope, UnableToDeletePropertyError);
722 return jsUndefined();
724 // Call the [[Put]] internal method of O with arguments "length", indx, and true.
726 setLength(exec, index, true);
731 // Push & putIndex are almost identical, with two small differences.
732 // - we always are writing beyond the current array bounds, so it is always necessary to update m_length & m_numValuesInVector.
733 // - pushing to an array of length 2^32-1 stores the property, but throws a range error.
734 NEVER_INLINE void JSArray::push(ExecState* exec, JSValue value)
736 pushInline(exec, value);
739 JSArray* JSArray::fastSlice(ExecState& exec, unsigned startIndex, unsigned count)
742 auto arrayType = indexingMode();
744 case CopyOnWriteArrayWithInt32:
745 case CopyOnWriteArrayWithDouble:
746 case CopyOnWriteArrayWithContiguous:
747 convertFromCopyOnWrite(vm);
748 arrayType = indexingMode();
750 case ArrayWithDouble:
752 case ArrayWithContiguous: {
753 if (count >= MIN_SPARSE_ARRAY_INDEX || structure(vm)->holesMustForwardToPrototype(vm, this))
756 JSGlobalObject* lexicalGlobalObject = exec.lexicalGlobalObject();
757 Structure* resultStructure = lexicalGlobalObject->arrayStructureForIndexingTypeDuringAllocation(arrayType);
758 if (UNLIKELY(hasAnyArrayStorage(resultStructure->indexingType())))
761 ASSERT(!lexicalGlobalObject->isHavingABadTime());
762 ObjectInitializationScope scope(vm);
763 JSArray* resultArray = JSArray::tryCreateUninitializedRestricted(scope, resultStructure, count);
764 if (UNLIKELY(!resultArray))
767 auto& resultButterfly = *resultArray->butterfly();
768 if (arrayType == ArrayWithDouble)
769 memcpy(resultButterfly.contiguousDouble().data(), butterfly()->contiguousDouble().data() + startIndex, sizeof(JSValue) * count);
771 memcpy(resultButterfly.contiguous().data(), butterfly()->contiguous().data() + startIndex, sizeof(JSValue) * count);
772 resultButterfly.setPublicLength(count);
781 bool JSArray::shiftCountWithArrayStorage(VM& vm, unsigned startIndex, unsigned count, ArrayStorage* storage)
783 unsigned oldLength = storage->length();
784 RELEASE_ASSERT(count <= oldLength);
786 // If the array contains holes or is otherwise in an abnormal state,
787 // use the generic algorithm in ArrayPrototype.
788 if ((storage->hasHoles() && this->structure(vm)->holesMustForwardToPrototype(vm, this))
790 || shouldUseSlowPut(indexingType())) {
797 unsigned length = oldLength - count;
799 storage->m_numValuesInVector -= count;
800 storage->setLength(length);
802 unsigned vectorLength = storage->vectorLength();
806 if (startIndex >= vectorLength)
809 DisallowGC disallowGC;
810 auto locker = holdLock(cellLock());
812 if (startIndex + count > vectorLength)
813 count = vectorLength - startIndex;
815 unsigned usedVectorLength = std::min(vectorLength, oldLength);
817 unsigned numElementsBeforeShiftRegion = startIndex;
818 unsigned firstIndexAfterShiftRegion = startIndex + count;
819 unsigned numElementsAfterShiftRegion = usedVectorLength - firstIndexAfterShiftRegion;
820 ASSERT(numElementsBeforeShiftRegion + count + numElementsAfterShiftRegion == usedVectorLength);
822 // The point of this comparison seems to be to minimize the amount of elements that have to
823 // be moved during a shift operation.
824 if (numElementsBeforeShiftRegion < numElementsAfterShiftRegion) {
825 // The number of elements before the shift region is less than the number of elements
826 // after the shift region, so we move the elements before to the right.
827 if (numElementsBeforeShiftRegion) {
828 RELEASE_ASSERT(count + startIndex <= vectorLength);
829 if (storage->hasHoles()) {
830 for (unsigned i = startIndex; i-- > 0;) {
831 unsigned destinationIndex = count + i;
832 JSValue source = storage->m_vector[i].get();
833 JSValue dest = storage->m_vector[destinationIndex].get();
834 // Any time we overwrite a hole we know we overcounted the number of values we removed
835 // when we subtracted count from m_numValuesInVector above.
836 if (!dest && destinationIndex >= firstIndexAfterShiftRegion)
837 storage->m_numValuesInVector++;
838 storage->m_vector[count + i].setWithoutWriteBarrier(source);
841 memmove(storage->m_vector + count,
843 sizeof(JSValue) * startIndex);
846 // Adjust the Butterfly and the index bias. We only need to do this here because we're changing
847 // the start of the Butterfly, which needs to point at the first indexed property in the used
848 // portion of the vector.
849 Butterfly* butterfly = this->butterfly()->shift(structure(vm), count);
850 storage = butterfly->arrayStorage();
851 storage->m_indexBias += count;
853 // Since we're consuming part of the vector by moving its beginning to the left,
854 // we need to modify the vector length appropriately.
855 storage->setVectorLength(vectorLength - count);
856 setButterfly(vm, butterfly);
858 // The number of elements before the shift region is greater than or equal to the number
859 // of elements after the shift region, so we move the elements after the shift region to the left.
860 if (storage->hasHoles()) {
861 for (unsigned i = 0; i < numElementsAfterShiftRegion; ++i) {
862 unsigned destinationIndex = startIndex + i;
863 JSValue source = storage->m_vector[firstIndexAfterShiftRegion + i].get();
864 JSValue dest = storage->m_vector[destinationIndex].get();
865 // Any time we overwrite a hole we know we overcounted the number of values we removed
866 // when we subtracted count from m_numValuesInVector above.
867 if (!dest && destinationIndex < firstIndexAfterShiftRegion)
868 storage->m_numValuesInVector++;
869 storage->m_vector[startIndex + i].setWithoutWriteBarrier(source);
872 memmove(storage->m_vector + startIndex,
873 storage->m_vector + firstIndexAfterShiftRegion,
874 sizeof(JSValue) * numElementsAfterShiftRegion);
876 // Clear the slots of the elements we just moved.
877 unsigned startOfEmptyVectorTail = usedVectorLength - count;
878 for (unsigned i = startOfEmptyVectorTail; i < usedVectorLength; ++i)
879 storage->m_vector[i].clear();
880 // We don't modify the index bias or the Butterfly pointer in this case because we're not changing
881 // the start of the Butterfly, which needs to point at the first indexed property in the used
882 // portion of the vector. We also don't modify the vector length because we're not actually changing
883 // its length; we're just using less of it.
889 bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned& startIndex, unsigned count)
892 RELEASE_ASSERT(count > 0);
894 if (isCopyOnWrite(indexingMode()))
895 convertFromCopyOnWrite(vm);
897 Butterfly* butterfly = this->butterfly();
899 switch (indexingType()) {
903 case ArrayWithUndecided:
904 // Don't handle this because it's confusing and it shouldn't come up.
908 case ArrayWithContiguous: {
909 unsigned oldLength = butterfly->publicLength();
910 RELEASE_ASSERT(count <= oldLength);
912 // We may have to walk the entire array to do the shift. We're willing to do
913 // so only if it's not horribly slow.
914 if (oldLength - (startIndex + count) >= MIN_SPARSE_ARRAY_INDEX)
915 return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
917 // Storing to a hole is fine since we're still having a good time. But reading from a hole
918 // is totally not fine, since we might have to read from the proto chain.
919 // We have to check for holes before we start moving things around so that we don't get halfway
920 // through shifting and then realize we should have been in ArrayStorage mode.
921 unsigned end = oldLength - count;
922 if (this->structure(vm)->holesMustForwardToPrototype(vm, this)) {
923 for (unsigned i = startIndex; i < end; ++i) {
924 JSValue v = butterfly->contiguous().at(this, i + count).get();
927 return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
929 butterfly->contiguous().at(this, i).setWithoutWriteBarrier(v);
932 memmove(butterfly->contiguous().data() + startIndex,
933 butterfly->contiguous().data() + startIndex + count,
934 sizeof(JSValue) * (end - startIndex));
937 for (unsigned i = end; i < oldLength; ++i)
938 butterfly->contiguous().at(this, i).clear();
940 butterfly->setPublicLength(oldLength - count);
942 // Our memmoving of values around in the array could have concealed some of them from
943 // the collector. Let's make sure that the collector scans this object again.
944 vm.heap.writeBarrier(this);
949 case ArrayWithDouble: {
950 unsigned oldLength = butterfly->publicLength();
951 RELEASE_ASSERT(count <= oldLength);
953 // We may have to walk the entire array to do the shift. We're willing to do
954 // so only if it's not horribly slow.
955 if (oldLength - (startIndex + count) >= MIN_SPARSE_ARRAY_INDEX)
956 return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
958 // Storing to a hole is fine since we're still having a good time. But reading from a hole
959 // is totally not fine, since we might have to read from the proto chain.
960 // We have to check for holes before we start moving things around so that we don't get halfway
961 // through shifting and then realize we should have been in ArrayStorage mode.
962 unsigned end = oldLength - count;
963 if (this->structure(vm)->holesMustForwardToPrototype(vm, this)) {
964 for (unsigned i = startIndex; i < end; ++i) {
965 double v = butterfly->contiguousDouble().at(this, i + count);
966 if (UNLIKELY(v != v)) {
968 return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
970 butterfly->contiguousDouble().at(this, i) = v;
973 memmove(butterfly->contiguousDouble().data() + startIndex,
974 butterfly->contiguousDouble().data() + startIndex + count,
975 sizeof(JSValue) * (end - startIndex));
977 for (unsigned i = end; i < oldLength; ++i)
978 butterfly->contiguousDouble().at(this, i) = PNaN;
980 butterfly->setPublicLength(oldLength - count);
984 case ArrayWithArrayStorage:
985 case ArrayWithSlowPutArrayStorage:
986 return shiftCountWithArrayStorage(vm, startIndex, count, arrayStorage());
994 // Returns true if the unshift can be handled, false to fallback.
995 bool JSArray::unshiftCountWithArrayStorage(ExecState* exec, unsigned startIndex, unsigned count, ArrayStorage* storage)
998 auto scope = DECLARE_THROW_SCOPE(vm);
1000 unsigned length = storage->length();
1002 RELEASE_ASSERT(startIndex <= length);
1004 // If the array contains holes or is otherwise in an abnormal state,
1005 // use the generic algorithm in ArrayPrototype.
1006 if (storage->hasHoles() || storage->inSparseMode() || shouldUseSlowPut(indexingType()))
1009 bool moveFront = !startIndex || startIndex < length / 2;
1011 unsigned vectorLength = storage->vectorLength();
1013 // Need to have GC deferred around the unshiftCountSlowCase(), since that leaves the butterfly in
1014 // a weird state: some parts of it will be left uninitialized, which we will fill in here.
1015 DeferGC deferGC(vm.heap);
1016 auto locker = holdLock(cellLock());
1018 if (moveFront && storage->m_indexBias >= count) {
1019 Butterfly* newButterfly = storage->butterfly()->unshift(structure(vm), count);
1020 storage = newButterfly->arrayStorage();
1021 storage->m_indexBias -= count;
1022 storage->setVectorLength(vectorLength + count);
1023 setButterfly(vm, newButterfly);
1024 } else if (!moveFront && vectorLength - length >= count)
1025 storage = storage->butterfly()->arrayStorage();
1026 else if (unshiftCountSlowCase(locker, vm, deferGC, moveFront, count))
1027 storage = arrayStorage();
1029 throwOutOfMemoryError(exec, scope);
1033 WriteBarrier<Unknown>* vector = storage->m_vector;
1037 memmove(vector, vector + count, startIndex * sizeof(JSValue));
1038 else if (length - startIndex)
1039 memmove(vector + startIndex + count, vector + startIndex, (length - startIndex) * sizeof(JSValue));
1042 for (unsigned i = 0; i < count; i++)
1043 vector[i + startIndex].clear();
1048 bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex, unsigned count)
1050 VM& vm = exec->vm();
1051 auto scope = DECLARE_THROW_SCOPE(vm);
1053 if (isCopyOnWrite(indexingMode()))
1054 convertFromCopyOnWrite(vm);
1056 Butterfly* butterfly = this->butterfly();
1058 switch (indexingType()) {
1060 case ArrayWithUndecided:
1061 // We could handle this. But it shouldn't ever come up, so we won't.
1064 case ArrayWithInt32:
1065 case ArrayWithContiguous: {
1066 unsigned oldLength = butterfly->publicLength();
1068 // We may have to walk the entire array to do the unshift. We're willing to do so
1069 // only if it's not horribly slow.
1070 if (oldLength - startIndex >= MIN_SPARSE_ARRAY_INDEX) {
1072 return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(vm));
1075 Checked<unsigned, RecordOverflow> checkedLength(oldLength);
1076 checkedLength += count;
1078 if (CheckedState::DidOverflow == checkedLength.safeGet(newLength)) {
1079 throwOutOfMemoryError(exec, scope);
1082 if (newLength > MAX_STORAGE_VECTOR_LENGTH)
1084 if (!ensureLength(vm, newLength)) {
1085 throwOutOfMemoryError(exec, scope);
1088 butterfly = this->butterfly();
1090 // We have to check for holes before we start moving things around so that we don't get halfway
1091 // through shifting and then realize we should have been in ArrayStorage mode.
1092 for (unsigned i = oldLength; i-- > startIndex;) {
1093 JSValue v = butterfly->contiguous().at(this, i).get();
1096 return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(vm));
1100 for (unsigned i = oldLength; i-- > startIndex;) {
1101 JSValue v = butterfly->contiguous().at(this, i).get();
1103 butterfly->contiguous().at(this, i + count).setWithoutWriteBarrier(v);
1106 // Our memmoving of values around in the array could have concealed some of them from
1107 // the collector. Let's make sure that the collector scans this object again.
1108 vm.heap.writeBarrier(this);
1110 // NOTE: we're leaving being garbage in the part of the array that we shifted out
1111 // of. This is fine because the caller is required to store over that area, and
1112 // in contiguous mode storing into a hole is guaranteed to behave exactly the same
1113 // as storing over an existing element.
1118 case ArrayWithDouble: {
1119 unsigned oldLength = butterfly->publicLength();
1121 // We may have to walk the entire array to do the unshift. We're willing to do so
1122 // only if it's not horribly slow.
1123 if (oldLength - startIndex >= MIN_SPARSE_ARRAY_INDEX) {
1125 return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(vm));
1128 Checked<unsigned, RecordOverflow> checkedLength(oldLength);
1129 checkedLength += count;
1131 if (CheckedState::DidOverflow == checkedLength.safeGet(newLength)) {
1132 throwOutOfMemoryError(exec, scope);
1135 if (newLength > MAX_STORAGE_VECTOR_LENGTH)
1137 if (!ensureLength(vm, newLength)) {
1138 throwOutOfMemoryError(exec, scope);
1141 butterfly = this->butterfly();
1143 // We have to check for holes before we start moving things around so that we don't get halfway
1144 // through shifting and then realize we should have been in ArrayStorage mode.
1145 for (unsigned i = oldLength; i-- > startIndex;) {
1146 double v = butterfly->contiguousDouble().at(this, i);
1147 if (UNLIKELY(v != v)) {
1149 return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(vm));
1153 for (unsigned i = oldLength; i-- > startIndex;) {
1154 double v = butterfly->contiguousDouble().at(this, i);
1156 butterfly->contiguousDouble().at(this, i + count) = v;
1159 // NOTE: we're leaving being garbage in the part of the array that we shifted out
1160 // of. This is fine because the caller is required to store over that area, and
1161 // in contiguous mode storing into a hole is guaranteed to behave exactly the same
1162 // as storing over an existing element.
1167 case ArrayWithArrayStorage:
1168 case ArrayWithSlowPutArrayStorage:
1170 return unshiftCountWithArrayStorage(exec, startIndex, count, arrayStorage());
1178 void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
1182 WriteBarrier<Unknown>* vector;
1184 Butterfly* butterfly = this->butterfly();
1186 switch (indexingType()) {
1190 case ArrayWithUndecided: {
1196 case ArrayWithInt32:
1197 case ArrayWithContiguous: {
1198 vectorEnd = butterfly->publicLength();
1199 vector = butterfly->contiguous().data();
1203 case ArrayWithDouble: {
1206 for (; i < butterfly->publicLength(); ++i) {
1207 double v = butterfly->contiguousDouble().at(this, i);
1210 args.append(JSValue(JSValue::EncodeAsDouble, v));
1215 case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: {
1216 ArrayStorage* storage = butterfly->arrayStorage();
1218 vector = storage->m_vector;
1219 vectorEnd = std::min(storage->length(), storage->vectorLength());
1225 #if COMPILER_QUIRK(CONSIDERS_UNREACHABLE_CODE)
1232 for (; i < vectorEnd; ++i) {
1233 WriteBarrier<Unknown>& v = vector[i];
1236 args.append(v.get());
1239 // FIXME: What prevents this from being called with a RuntimeArray? The length function will always return 0 in that case.
1240 for (; i < length(); ++i)
1241 args.append(get(exec, i));
1244 void JSArray::copyToArguments(ExecState* exec, VirtualRegister firstElementDest, unsigned offset, unsigned length)
1246 VM& vm = exec->vm();
1247 auto scope = DECLARE_THROW_SCOPE(vm);
1249 unsigned i = offset;
1250 WriteBarrier<Unknown>* vector;
1252 length += offset; // We like to think of the length as being our length, rather than the output length.
1254 // FIXME: What prevents this from being called with a RuntimeArray? The length function will always return 0 in that case.
1255 ASSERT(length == this->length());
1257 Butterfly* butterfly = this->butterfly();
1258 switch (indexingType()) {
1262 case ArrayWithUndecided: {
1268 case ArrayWithInt32:
1269 case ArrayWithContiguous: {
1270 vector = butterfly->contiguous().data();
1271 vectorEnd = butterfly->publicLength();
1275 case ArrayWithDouble: {
1278 for (; i < butterfly->publicLength(); ++i) {
1279 ASSERT(i < butterfly->vectorLength());
1280 double v = butterfly->contiguousDouble().at(this, i);
1283 exec->r(firstElementDest + i - offset) = JSValue(JSValue::EncodeAsDouble, v);
1288 case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: {
1289 ArrayStorage* storage = butterfly->arrayStorage();
1290 vector = storage->m_vector;
1291 vectorEnd = std::min(length, storage->vectorLength());
1297 #if COMPILER_QUIRK(CONSIDERS_UNREACHABLE_CODE)
1304 for (; i < vectorEnd; ++i) {
1305 WriteBarrier<Unknown>& v = vector[i];
1308 exec->r(firstElementDest + i - offset) = v.get();
1311 for (; i < length; ++i) {
1312 exec->r(firstElementDest + i - offset) = get(exec, i);
1313 RETURN_IF_EXCEPTION(scope, void());
1317 bool JSArray::isIteratorProtocolFastAndNonObservable()
1319 JSGlobalObject* globalObject = this->globalObject();
1320 if (!globalObject->isArrayPrototypeIteratorProtocolFastAndNonObservable())
1323 VM& vm = globalObject->vm();
1324 Structure* structure = this->structure(vm);
1325 // This is the fast case. Many arrays will be an original array.
1326 if (globalObject->isOriginalArrayStructure(structure))
1329 if (structure->mayInterceptIndexedAccesses())
1332 if (getPrototypeDirect(vm) != globalObject->arrayPrototype())
1335 if (getDirectOffset(vm, vm.propertyNames->iteratorSymbol) != invalidOffset)
1341 inline JSArray* constructArray(ObjectInitializationScope& scope, Structure* arrayStructure, unsigned length)
1343 // FIXME: We only need this for subclasses of Array because we might need to allocate a new structure to change
1344 // indexing types while initializing. If this triggered a GC then we might scan our currently uninitialized
1345 // array and crash. https://bugs.webkit.org/show_bug.cgi?id=186811
1347 if (arrayStructure->globalObject()->isOriginalArrayStructure(arrayStructure))
1348 array = JSArray::tryCreateUninitializedRestricted(scope, arrayStructure, length);
1350 array = JSArray::create(scope.vm(), arrayStructure, length);
1352 // Our client will initialize the storage using initializeIndex() up to
1353 // length values, and expects that we've already set m_numValuesInVector
1354 // to length. This matches the behavior of tryCreateUninitializedRestricted().
1355 IndexingType indexingType = arrayStructure->indexingType();
1356 if (UNLIKELY(hasAnyArrayStorage(indexingType)))
1357 array->butterfly()->arrayStorage()->m_numValuesInVector = length;
1360 // FIXME: we should probably throw an out of memory error here, but
1361 // when making this change we should check that all clients of this
1362 // function will correctly handle an exception being thrown from here.
1363 // https://bugs.webkit.org/show_bug.cgi?id=169786
1364 RELEASE_ASSERT(array);
1368 JSArray* constructArray(ExecState* exec, Structure* arrayStructure, const ArgList& values)
1370 VM& vm = exec->vm();
1371 unsigned length = values.size();
1372 ObjectInitializationScope scope(vm);
1374 JSArray* array = constructArray(scope, arrayStructure, length);
1375 for (unsigned i = 0; i < length; ++i)
1376 array->initializeIndex(scope, i, values.at(i));
1380 JSArray* constructArray(ExecState* exec, Structure* arrayStructure, const JSValue* values, unsigned length)
1382 VM& vm = exec->vm();
1383 ObjectInitializationScope scope(vm);
1385 JSArray* array = constructArray(scope, arrayStructure, length);
1386 for (unsigned i = 0; i < length; ++i)
1387 array->initializeIndex(scope, i, values[i]);
1391 JSArray* constructArrayNegativeIndexed(ExecState* exec, Structure* arrayStructure, const JSValue* values, unsigned length)
1393 VM& vm = exec->vm();
1394 ObjectInitializationScope scope(vm);
1396 JSArray* array = constructArray(scope, arrayStructure, length);
1397 for (int i = 0; i < static_cast<int>(length); ++i)
1398 array->initializeIndex(scope, i, values[-i]);