JSArray.cpp
1 /* 2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) 3 * Copyright (C) 2003-2020 Apple Inc. All rights reserved. 4 * Copyright (C) 2003 Peter Kelly (pmk@post.com) 5 * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 * 21 */ 22 23 #include "config.h" 24 #include "JSArray.h" 25 26 #include "ArrayPrototype.h" 27 #include "JSArrayInlines.h" 28 #include "JSCInlines.h" 29 #include "PropertyNameArray.h" 30 #include "TypeError.h" 31 #include <wtf/Assertions.h> 32 33 namespace JSC { 34 35 const ASCIILiteral LengthExceededTheMaximumArrayLengthError { "Length exceeded the maximum array length"_s }; 36 37 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSArray); 38 39 const ClassInfo JSArray::s_info = {"Array", &JSNonFinalObject::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSArray)}; 40 41 JSArray* JSArray::tryCreateUninitializedRestricted(ObjectInitializationScope& scope, GCDeferralContext* deferralContext, Structure* structure, unsigned initialLength) 42 { 43 VM& vm = scope.vm(); 44 45 if (UNLIKELY(initialLength > MAX_STORAGE_VECTOR_LENGTH)) 46 return nullptr; 47 48 unsigned outOfLineStorage = structure->outOfLineCapacity(); 49 Butterfly* butterfly; 50 IndexingType indexingType = structure->indexingType(); 51 if (LIKELY(!hasAnyArrayStorage(indexingType))) { 52 ASSERT( 53 hasUndecided(indexingType) 54 || hasInt32(indexingType) 55 || hasDouble(indexingType) 56 || hasContiguous(indexingType)); 57 58 unsigned vectorLength = Butterfly::optimalContiguousVectorLength(structure, initialLength); 59 void* temp = vm.jsValueGigacageAuxiliarySpace.allocateNonVirtual( 60 vm, 61 Butterfly::totalSize(0, outOfLineStorage, true, vectorLength * sizeof(EncodedJSValue)), 62 deferralContext, AllocationFailureMode::ReturnNull); 63 if (UNLIKELY(!temp)) 64 return nullptr; 65 butterfly = Butterfly::fromBase(temp, 0, outOfLineStorage); 66 butterfly->setVectorLength(vectorLength); 67 butterfly->setPublicLength(initialLength); 68 if (hasDouble(indexingType)) { 69 for (unsigned i = initialLength; i < vectorLength; ++i) 70 butterfly->contiguousDouble().atUnsafe(i) = PNaN; 71 } else { 72 for (unsigned i = initialLength; i < vectorLength; ++i) 73 butterfly->contiguous().atUnsafe(i).clear(); 74 } 75 } else { 76 ASSERT( 77 indexingType == ArrayWithSlowPutArrayStorage 78 || indexingType == ArrayWithArrayStorage); 79 static constexpr unsigned indexBias = 0; 80 unsigned vectorLength = ArrayStorage::optimalVectorLength(indexBias, structure, initialLength); 81 void* temp = vm.jsValueGigacageAuxiliarySpace.allocateNonVirtual( 82 vm, 83 Butterfly::totalSize(indexBias, outOfLineStorage, true, ArrayStorage::sizeFor(vectorLength)), 84 deferralContext, AllocationFailureMode::ReturnNull); 85 if (UNLIKELY(!temp)) 86 return nullptr; 87 butterfly = Butterfly::fromBase(temp, indexBias, outOfLineStorage); 88 *butterfly->indexingHeader() = indexingHeaderForArrayStorage(initialLength, vectorLength); 89 ArrayStorage* storage = butterfly->arrayStorage(); 90 storage->m_indexBias = indexBias; 91 storage->m_sparseMap.clear(); 92 storage->m_numValuesInVector = initialLength; 93 for (unsigned i = initialLength; i < vectorLength; ++i) 94 storage->m_vector[i].clear(); 95 } 96 97 JSArray* result = createWithButterfly(vm, deferralContext, structure, butterfly); 98 99 scope.notifyAllocated(result); 100 return result; 101 } 102 103 void JSArray::eagerlyInitializeButterfly(ObjectInitializationScope& scope, JSArray* array, unsigned initialLength) 104 { 105 Structure* structure = array->structure(scope.vm()); 106 IndexingType indexingType = structure->indexingType(); 107 Butterfly* butterfly = array->butterfly(); 108 109 // This function only serves as a companion to tryCreateUninitializedRestricted() 110 // in the event that we really can't defer initialization of the butterfly after all. 111 // tryCreateUninitializedRestricted() already initialized the elements between 112 // initialLength and vector length. We just need to do 0 - initialLength. 113 // ObjectInitializationScope::notifyInitialized() will verify that all elements are 114 // initialized. 115 if (LIKELY(!hasAnyArrayStorage(indexingType))) { 116 if (hasDouble(indexingType)) { 117 for (unsigned i = 0; i < initialLength; ++i) 118 butterfly->contiguousDouble().atUnsafe(i) = PNaN; 119 } else { 120 for (unsigned i = 0; i < initialLength; ++i) 121 butterfly->contiguous().atUnsafe(i).clear(); 122 } 123 } else { 124 ArrayStorage* storage = butterfly->arrayStorage(); 125 for (unsigned i = 0; i < initialLength; ++i) 126 storage->m_vector[i].clear(); 127 } 128 scope.notifyInitialized(array); 129 } 130 131 void JSArray::setLengthWritable(JSGlobalObject* globalObject, bool writable) 132 { 133 ASSERT(isLengthWritable() || !writable); 134 if (!isLengthWritable() || writable) 135 return; 136 137 enterDictionaryIndexingMode(globalObject->vm()); 138 139 SparseArrayValueMap* map = arrayStorage()->m_sparseMap.get(); 140 ASSERT(map); 141 map->setLengthIsReadOnly(); 142 } 143 144 // https://tc39.es/ecma262/#sec-array-exotic-objects-defineownproperty-p-desc 145 bool JSArray::defineOwnProperty(JSObject* object, JSGlobalObject* globalObject, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException) 146 { 147 VM& vm = globalObject->vm(); 148 auto scope = DECLARE_THROW_SCOPE(vm); 149 150 JSArray* array = jsCast<JSArray*>(object); 151 152 // 2. If P is "length", then 153 // https://tc39.es/ecma262/#sec-arraysetlength 154 if (propertyName == vm.propertyNames->length) { 155 // FIXME: Nothing prevents this from being called on a RuntimeArray, and the length function will always return 0 in that case. 156 unsigned newLength = array->length(); 157 if (descriptor.value()) { 158 newLength = descriptor.value().toUInt32(globalObject); 159 RETURN_IF_EXCEPTION(scope, false); 160 double valueAsNumber = descriptor.value().toNumber(globalObject); 161 RETURN_IF_EXCEPTION(scope, false); 162 if (valueAsNumber != static_cast<double>(newLength)) { 163 throwRangeError(globalObject, scope, "Invalid array length"_s); 164 return false; 165 } 166 } 167 168 // OrdinaryDefineOwnProperty (https://tc39.es/ecma262/#sec-validateandapplypropertydescriptor) at steps 1.a, 11.a, and 15 is now performed: 169 // 4. If current.[[Configurable]] is false, then 170 // 4.a. If Desc.[[Configurable]] is present and its value is true, return false. 171 if (descriptor.configurablePresent() && descriptor.configurable()) 172 return typeError(globalObject, scope, throwException, UnconfigurablePropertyChangeConfigurabilityError); 173 // 4.b. If Desc.[[Enumerable]] is present and SameValue(Desc.[[Enumerable]], current.[[Enumerable]]) is false, return false. 174 if (descriptor.enumerablePresent() && descriptor.enumerable()) 175 return typeError(globalObject, scope, throwException, UnconfigurablePropertyChangeEnumerabilityError); 176 // 6. Else if SameValue(IsDataDescriptor(current), IsDataDescriptor(Desc)) is false, then 177 // 6.a. If current.[[Configurable]] is false, return false. 178 if (descriptor.isAccessorDescriptor()) 179 return typeError(globalObject, scope, throwException, UnconfigurablePropertyChangeAccessMechanismError); 180 // 7. Else if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true, then 181 // 7.a. If current.[[Configurable]] is false and current.[[Writable]] is false, then 182 if (!array->isLengthWritable()) { 183 // 7.a.i. If Desc.[[Writable]] is present and Desc.[[Writable]] is true, return false. 184 // This check is unaffected by steps 13-14 of ArraySetLength as they change non-writable descriptors only. 185 if (descriptor.writablePresent() && descriptor.writable()) 186 return typeError(globalObject, scope, throwException, UnconfigurablePropertyChangeWritabilityError); 187 // 7.a.ii. If Desc.[[Value]] is present and SameValue(Desc.[[Value]], current.[[Value]]) is false, return false. 188 // This check also covers step 12 of ArraySetLength, which is only reachable if newLen < oldLen. 189 if (newLength != array->length()) 190 return typeError(globalObject, scope, throwException, ReadonlyPropertyChangeError); 191 } 192 193 // setLength() clears indices >= newLength and sets correct "length" value if [[Delete]] fails (step 17.b.i) 194 bool success = array->setLength(globalObject, newLength, throwException); 195 EXCEPTION_ASSERT(!scope.exception() || !success); 196 if (descriptor.writablePresent()) 197 array->setLengthWritable(globalObject, descriptor.writable()); 198 return success; 199 } 200 201 // 4. Else if P is an array index (15.4), then 202 // a. Let index be ToUint32(P). 203 if (Optional<uint32_t> optionalIndex = parseIndex(propertyName)) { 204 // b. Reject if index >= oldLen and oldLenDesc.[[Writable]] is false. 205 uint32_t index = optionalIndex.value(); 206 // FIXME: Nothing prevents this from being called on a RuntimeArray, and the length function will always return 0 in that case. 207 if (index >= array->length() && !array->isLengthWritable()) 208 return typeError(globalObject, scope, throwException, "Attempting to define numeric property on array with non-writable length property."_s); 209 // 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. 210 // d. Reject if succeeded is false. 211 // e. If index >= oldLen 212 // e.i. Set oldLenDesc.[[Value]] to index + 1. 213 // 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. 214 // f. Return true. 215 RELEASE_AND_RETURN(scope, array->defineOwnIndexedProperty(globalObject, index, descriptor, throwException)); 216 } 217 218 RELEASE_AND_RETURN(scope, array->JSObject::defineOwnNonIndexProperty(globalObject, propertyName, descriptor, throwException)); 219 } 220 221 bool JSArray::getOwnPropertySlot(JSObject* object, JSGlobalObject* globalObject, PropertyName propertyName, PropertySlot& slot) 222 { 223 VM& vm = globalObject->vm(); 224 JSArray* thisObject = jsCast<JSArray*>(object); 225 if (propertyName == vm.propertyNames->length) { 226 unsigned attributes = thisObject->isLengthWritable() ? PropertyAttribute::DontDelete | PropertyAttribute::DontEnum : PropertyAttribute::DontDelete | PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly; 227 slot.setValue(thisObject, attributes, jsNumber(thisObject->length())); 228 return true; 229 } 230 231 return JSObject::getOwnPropertySlot(thisObject, globalObject, propertyName, slot); 232 } 233 234 // https://tc39.es/ecma262/#sec-array-exotic-objects-defineownproperty-p-desc 235 bool JSArray::put(JSCell* cell, JSGlobalObject* globalObject, PropertyName propertyName, JSValue value, PutPropertySlot& slot) 236 { 237 VM& vm = globalObject->vm(); 238 auto scope = DECLARE_THROW_SCOPE(vm); 239 240 JSArray* thisObject = jsCast<JSArray*>(cell); 241 242 if (UNLIKELY(isThisValueAltered(slot, thisObject))) 243 RELEASE_AND_RETURN(scope, ordinarySetSlow(globalObject, thisObject, propertyName, value, slot.thisValue(), slot.isStrictMode())); 244 245 thisObject->ensureWritable(vm); 246 247 if (propertyName == vm.propertyNames->length) { 248 if (!thisObject->isLengthWritable()) { 249 if (slot.isStrictMode()) 250 throwTypeError(globalObject, scope, "Array length is not writable"_s); 251 return false; 252 } 253 254 unsigned newLength = value.toUInt32(globalObject); 255 RETURN_IF_EXCEPTION(scope, false); 256 double valueAsNumber = value.toNumber(globalObject); 257 RETURN_IF_EXCEPTION(scope, false); 258 if (valueAsNumber != static_cast<double>(newLength)) { 259 throwException(globalObject, scope, createRangeError(globalObject, "Invalid array length"_s)); 260 return false; 261 } 262 RELEASE_AND_RETURN(scope, thisObject->setLength(globalObject, newLength, slot.isStrictMode())); 263 } 264 265 RELEASE_AND_RETURN(scope, JSObject::put(thisObject, globalObject, propertyName, value, slot)); 266 } 267 268 bool JSArray::deleteProperty(JSCell* cell, JSGlobalObject* globalObject, PropertyName propertyName, DeletePropertySlot& slot) 269 { 270 VM& vm = globalObject->vm(); 271 JSArray* thisObject = jsCast<JSArray*>(cell); 272 273 if (propertyName == vm.propertyNames->length) 274 return false; 275 276 return JSObject::deleteProperty(thisObject, globalObject, propertyName, slot); 277 } 278 279 static int compareKeysForQSort(const void* a, const void* b) 280 { 281 unsigned da = *static_cast<const unsigned*>(a); 282 unsigned db = *static_cast<const unsigned*>(b); 283 return (da > db) - (da < db); 284 } 285 286 void JSArray::getOwnSpecialPropertyNames(JSObject*, JSGlobalObject* globalObject, PropertyNameArray& propertyNames, DontEnumPropertiesMode mode) 287 { 288 VM& vm = globalObject->vm(); 289 if (mode == DontEnumPropertiesMode::Include) 290 propertyNames.add(vm.propertyNames->length); 291 } 292 293 // This method makes room in the vector, but leaves the new space for count slots uncleared. 294 bool JSArray::unshiftCountSlowCase(const AbstractLocker&, VM& vm, DeferGC&, bool addToFront, unsigned count) 295 { 296 ASSERT(cellLock().isLocked()); 297 298 ArrayStorage* storage = ensureArrayStorage(vm); 299 Butterfly* butterfly = storage->butterfly(); 300 Structure* structure = this->structure(vm); 301 unsigned propertyCapacity = structure->outOfLineCapacity(); 302 unsigned propertySize = structure->outOfLineSize(); 303 304 // If not, we should have handled this on the fast path. 305 ASSERT(!addToFront || count > storage->m_indexBias); 306 307 // Step 1: 308 // Gather 4 key metrics: 309 // * usedVectorLength - how many entries are currently in the vector (conservative estimate - fewer may be in use in sparse vectors). 310 // * requiredVectorLength - how many entries are will there be in the vector, after allocating space for 'count' more. 311 // * currentCapacity - what is the current size of the vector, including any pre-capacity. 312 // * desiredCapacity - how large should we like to grow the vector to - based on 2x requiredVectorLength. 313 314 unsigned length = storage->length(); 315 unsigned oldVectorLength = storage->vectorLength(); 316 unsigned usedVectorLength = std::min(oldVectorLength, length); 317 ASSERT(usedVectorLength <= MAX_STORAGE_VECTOR_LENGTH); 318 // Check that required vector length is possible, in an overflow-safe fashion. 319 if (count > MAX_STORAGE_VECTOR_LENGTH - usedVectorLength) 320 return false; 321 unsigned requiredVectorLength = usedVectorLength + count; 322 ASSERT(requiredVectorLength <= MAX_STORAGE_VECTOR_LENGTH); 323 // The sum of m_vectorLength and m_indexBias will never exceed MAX_STORAGE_VECTOR_LENGTH. 324 ASSERT(storage->vectorLength() <= MAX_STORAGE_VECTOR_LENGTH && (MAX_STORAGE_VECTOR_LENGTH - storage->vectorLength()) >= storage->m_indexBias); 325 unsigned currentCapacity = storage->vectorLength() + storage->m_indexBias; 326 // The calculation of desiredCapacity won't overflow, due to the range of MAX_STORAGE_VECTOR_LENGTH. 327 // FIXME: This code should be fixed to avoid internal fragmentation. It's not super high 328 // priority since increaseVectorLength() will "fix" any mistakes we make, but it would be cool 329 // to get this right eventually. 330 unsigned desiredCapacity = std::min(MAX_STORAGE_VECTOR_LENGTH, std::max(BASE_ARRAY_STORAGE_VECTOR_LEN, requiredVectorLength) << 1); 331 332 // Step 2: 333 // We're either going to choose to allocate a new ArrayStorage, or we're going to reuse the existing one. 334 335 void* newAllocBase = nullptr; 336 unsigned newStorageCapacity; 337 bool allocatedNewStorage; 338 // If the current storage array is sufficiently large (but not too large!) then just keep using it. 339 if (currentCapacity > desiredCapacity && isDenseEnoughForVector(currentCapacity, requiredVectorLength)) { 340 newAllocBase = butterfly->base(structure); 341 newStorageCapacity = currentCapacity; 342 allocatedNewStorage = false; 343 } else { 344 const unsigned preCapacity = 0; 345 Butterfly* newButterfly = Butterfly::tryCreateUninitialized(vm, this, preCapacity, propertyCapacity, true, ArrayStorage::sizeFor(desiredCapacity)); 346 if (!newButterfly) 347 return false; 348 newAllocBase = newButterfly->base(preCapacity, propertyCapacity); 349 newStorageCapacity = desiredCapacity; 350 allocatedNewStorage = true; 351 } 352 353 // Step 3: 354 // Work out where we're going to move things to. 355 356 // Determine how much of the vector to use as pre-capacity, and how much as post-capacity. 357 // If we're adding to the end, we'll add all the new space to the end. 358 // If the vector had no free post-capacity (length >= m_vectorLength), don't give it any. 359 // If it did, we calculate the amount that will remain based on an atomic decay - leave the 360 // vector with half the post-capacity it had previously. 361 unsigned postCapacity = 0; 362 if (!addToFront) 363 postCapacity = newStorageCapacity - requiredVectorLength; 364 else if (length < storage->vectorLength()) { 365 // Atomic decay, + the post-capacity cannot be greater than what is available. 366 postCapacity = std::min((storage->vectorLength() - length) >> 1, newStorageCapacity - requiredVectorLength); 367 // If we're moving contents within the same allocation, the post-capacity is being reduced. 368 ASSERT(newAllocBase != butterfly->base(structure) || postCapacity < storage->vectorLength() - length); 369 } 370 371 unsigned newVectorLength = requiredVectorLength + postCapacity; 372 RELEASE_ASSERT(newVectorLength <= MAX_STORAGE_VECTOR_LENGTH); 373 unsigned preCapacity = newStorageCapacity - newVectorLength; 374 375 Butterfly* newButterfly = Butterfly::fromBase(newAllocBase, preCapacity, propertyCapacity); 376 377 { 378 // When moving Butterfly's head to adjust property-storage, we must take a structure lock. 379 // Otherwise, concurrent JIT compiler accesses to a property storage which is half-baked due to move for shift / unshift. 380 // If the butterfly is newly allocated one, we do not need to take a lock since this is not changing the old butterfly. 381 ConcurrentJSLocker structureLock(allocatedNewStorage ? nullptr : &structure->lock()); 382 if (addToFront) { 383 ASSERT(count + usedVectorLength <= newVectorLength); 384 gcSafeMemmove(newButterfly->arrayStorage()->m_vector + count, storage->m_vector, sizeof(JSValue) * usedVectorLength); 385 gcSafeMemmove(newButterfly->propertyStorage() - propertySize, butterfly->propertyStorage() - propertySize, sizeof(JSValue) * propertySize + sizeof(IndexingHeader) + ArrayStorage::sizeFor(0)); 386 387 // We don't need to zero the pre-capacity for the concurrent GC because it is not available to use as property storage. 388 gcSafeZeroMemory(static_cast<JSValue*>(newButterfly->base(0, propertyCapacity)), (propertyCapacity - propertySize) * sizeof(JSValue)); 389 390 if (allocatedNewStorage) { 391 // We will set the vectorLength to newVectorLength. We populated requiredVectorLength 392 // (usedVectorLength + count), which is less. Clear the difference. 393 for (unsigned i = requiredVectorLength; i < newVectorLength; ++i) 394 newButterfly->arrayStorage()->m_vector[i].clear(); 395 } 396 } else if ((newAllocBase != butterfly->base(structure)) || (preCapacity != storage->m_indexBias)) { 397 gcSafeMemmove(newButterfly->propertyStorage() - propertyCapacity, butterfly->propertyStorage() - propertyCapacity, sizeof(JSValue) * propertyCapacity + sizeof(IndexingHeader) + ArrayStorage::sizeFor(0)); 398 gcSafeMemmove(newButterfly->arrayStorage()->m_vector, storage->m_vector, sizeof(JSValue) * usedVectorLength); 399 400 for (unsigned i = requiredVectorLength; i < newVectorLength; i++) 401 newButterfly->arrayStorage()->m_vector[i].clear(); 402 } 403 404 newButterfly->arrayStorage()->setVectorLength(newVectorLength); 405 newButterfly->arrayStorage()->m_indexBias = preCapacity; 406 407 setButterfly(vm, newButterfly); 408 } 409 410 return true; 411 } 412 413 bool JSArray::setLengthWithArrayStorage(JSGlobalObject* globalObject, unsigned newLength, bool throwException, ArrayStorage* storage) 414 { 415 VM& vm = globalObject->vm(); 416 auto scope = DECLARE_THROW_SCOPE(vm); 417 418 unsigned length = storage->length(); 419 if (newLength == length) 420 return true; 421 422 // If the length is read only then we enter sparse mode, so should enter the following 'if'. 423 ASSERT(isLengthWritable() || storage->m_sparseMap); 424 425 if (SparseArrayValueMap* map = storage->m_sparseMap.get()) { 426 // Fail if the length is not writable. 427 if (map->lengthIsReadOnly()) 428 return typeError(globalObject, scope, throwException, ReadonlyPropertyWriteError); 429 430 if (newLength < length) { 431 // Copy any keys we might be interested in into a vector. 432 Vector<unsigned, 0, UnsafeVectorOverflow> keys; 433 keys.reserveInitialCapacity(std::min(map->size(), static_cast<size_t>(length - newLength))); 434 SparseArrayValueMap::const_iterator end = map->end(); 435 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) { 436 unsigned index = static_cast<unsigned>(it->key); 437 if (index < length && index >= newLength) 438 keys.append(index); 439 } 440 441 // Check if the array is in sparse mode. If so there may be non-configurable 442 // properties, so we have to perform deletion with caution, if not we can 443 // delete values in any order. 444 if (map->sparseMode()) { 445 qsort(keys.begin(), keys.size(), sizeof(unsigned), compareKeysForQSort); 446 unsigned i = keys.size(); 447 while (i) { 448 unsigned index = keys[--i]; 449 SparseArrayValueMap::iterator it = map->find(index); 450 ASSERT(it != map->notFound()); 451 if (it->value.attributes() & PropertyAttribute::DontDelete) { 452 storage->setLength(index + 1); 453 return typeError(globalObject, scope, throwException, UnableToDeletePropertyError); 454 } 455 map->remove(it); 456 } 457 } else { 458 for (unsigned i = 0; i < keys.size(); ++i) 459 map->remove(keys[i]); 460 if (map->isEmpty()) 461 deallocateSparseIndexMap(); 462 } 463 } 464 } 465 466 if (newLength < length) { 467 // Delete properties from the vector. 468 unsigned usedVectorLength = std::min(length, storage->vectorLength()); 469 for (unsigned i = newLength; i < usedVectorLength; ++i) { 470 WriteBarrier<Unknown>& valueSlot = storage->m_vector[i]; 471 bool hadValue = !!valueSlot; 472 valueSlot.clear(); 473 storage->m_numValuesInVector -= hadValue; 474 } 475 } 476 477 storage->setLength(newLength); 478 479 return true; 480 } 481 482 bool JSArray::appendMemcpy(JSGlobalObject* globalObject, VM& vm, unsigned startIndex, JSC::JSArray* otherArray) 483 { 484 auto scope = DECLARE_THROW_SCOPE(vm); 485 486 if (!canFastCopy(vm, otherArray)) 487 return false; 488 489 IndexingType type = indexingType(); 490 IndexingType otherType = otherArray->indexingType(); 491 IndexingType copyType = mergeIndexingTypeForCopying(otherType); 492 if (type == ArrayWithUndecided && copyType != NonArray) { 493 if (copyType == ArrayWithInt32) 494 convertUndecidedToInt32(vm); 495 else if (copyType == ArrayWithDouble) 496 convertUndecidedToDouble(vm); 497 else if (copyType == ArrayWithContiguous) 498 convertUndecidedToContiguous(vm); 499 else { 500 ASSERT(copyType == ArrayWithUndecided); 501 return true; 502 } 503 } else if (type != copyType) 504 return false; 505 506 unsigned otherLength = otherArray->length(); 507 Checked<unsigned, RecordOverflow> checkedNewLength = startIndex; 508 checkedNewLength += otherLength; 509 510 unsigned newLength; 511 if (checkedNewLength.safeGet(newLength) == CheckedState::DidOverflow) { 512 throwException(globalObject, scope, createRangeError(globalObject, LengthExceededTheMaximumArrayLengthError)); 513 return false; 514 } 515 516 if (newLength >= MIN_SPARSE_ARRAY_INDEX) 517 return false; 518 519 if (!ensureLength(vm, newLength)) { 520 throwOutOfMemoryError(globalObject, scope); 521 return false; 522 } 523 ASSERT(copyType == indexingType()); 524 525 if (UNLIKELY(otherType == ArrayWithUndecided)) { 526 auto* butterfly = this->butterfly(); 527 if (type == ArrayWithDouble) { 528 for (unsigned i = startIndex; i < newLength; ++i) 529 butterfly->contiguousDouble().at(this, i) = PNaN; 530 } else { 531 for (unsigned i = startIndex; i < newLength; ++i) 532 butterfly->contiguousInt32().at(this, i).setWithoutWriteBarrier(JSValue()); 533 } 534 } else if (type == ArrayWithDouble) 535 gcSafeMemcpy(butterfly()->contiguousDouble().data() + startIndex, otherArray->butterfly()->contiguousDouble().data(), sizeof(JSValue) * otherLength); 536 else { 537 gcSafeMemcpy(butterfly()->contiguous().data() + startIndex, otherArray->butterfly()->contiguous().data(), sizeof(JSValue) * otherLength); 538 vm.heap.writeBarrier(this); 539 } 540 541 return true; 542 } 543 544 bool JSArray::setLength(JSGlobalObject* globalObject, unsigned newLength, bool throwException) 545 { 546 VM& vm = globalObject->vm(); 547 auto scope = DECLARE_THROW_SCOPE(vm); 548 549 Butterfly* butterfly = this->butterfly(); 550 switch (indexingMode()) { 551 case ArrayClass: 552 if (!newLength) 553 return true; 554 if (newLength >= MIN_SPARSE_ARRAY_INDEX) { 555 RELEASE_AND_RETURN(scope, setLengthWithArrayStorage( 556 globalObject, newLength, throwException, 557 ensureArrayStorage(vm))); 558 } 559 createInitialUndecided(vm, newLength); 560 return true; 561 562 case CopyOnWriteArrayWithInt32: 563 case CopyOnWriteArrayWithDouble: 564 case CopyOnWriteArrayWithContiguous: 565 if (newLength == butterfly->publicLength()) 566 return true; 567 convertFromCopyOnWrite(vm); 568 butterfly = this->butterfly(); 569 FALLTHROUGH; 570 571 case ArrayWithUndecided: 572 case ArrayWithInt32: 573 case ArrayWithDouble: 574 case ArrayWithContiguous: { 575 if (newLength == butterfly->publicLength()) 576 return true; 577 if (newLength > MAX_STORAGE_VECTOR_LENGTH // This check ensures that we can do fast push. 578 || (newLength >= MIN_SPARSE_ARRAY_INDEX 579 && !isDenseEnoughForVector(newLength, countElements()))) { 580 RELEASE_AND_RETURN(scope, setLengthWithArrayStorage( 581 globalObject, newLength, throwException, 582 ensureArrayStorage(vm))); 583 } 584 if (newLength > butterfly->publicLength()) { 585 if (!ensureLength(vm, newLength)) { 586 throwOutOfMemoryError(globalObject, scope); 587 return false; 588 } 589 return true; 590 } 591 592 unsigned lengthToClear = butterfly->publicLength() - newLength; 593 unsigned costToAllocateNewButterfly = 64; // a heuristic. 594 if (lengthToClear > newLength && lengthToClear > costToAllocateNewButterfly) { 595 reallocateAndShrinkButterfly(vm, newLength); 596 return true; 597 } 598 599 if (indexingType() == ArrayWithDouble) { 600 for (unsigned i = butterfly->publicLength(); i-- > newLength;) 601 butterfly->contiguousDouble().at(this, i) = PNaN; 602 } else { 603 for (unsigned i = butterfly->publicLength(); i-- > newLength;) 604 butterfly->contiguous().at(this, i).clear(); 605 } 606 butterfly->setPublicLength(newLength); 607 return true; 608 } 609 610 case ArrayWithArrayStorage: 611 case ArrayWithSlowPutArrayStorage: 612 RELEASE_AND_RETURN(scope, setLengthWithArrayStorage(globalObject, newLength, throwException, arrayStorage())); 613 614 default: 615 CRASH(); 616 return false; 617 } 618 } 619 620 JSValue JSArray::pop(JSGlobalObject* globalObject) 621 { 622 VM& vm = globalObject->vm(); 623 auto scope = DECLARE_THROW_SCOPE(vm); 624 625 ensureWritable(vm); 626 627 Butterfly* butterfly = this->butterfly(); 628 629 switch (indexingType()) { 630 case ArrayClass: 631 return jsUndefined(); 632 633 case ArrayWithUndecided: 634 if (!butterfly->publicLength()) 635 return jsUndefined(); 636 // We have nothing but holes. So, drop down to the slow version. 637 break; 638 639 case ArrayWithInt32: 640 case ArrayWithContiguous: { 641 unsigned length = butterfly->publicLength(); 642 643 if (!length--) 644 return jsUndefined(); 645 646 RELEASE_ASSERT(length < butterfly->vectorLength()); 647 JSValue value = butterfly->contiguous().at(this, length).get(); 648 if (value) { 649 butterfly->contiguous().at(this, length).clear(); 650 butterfly->setPublicLength(length); 651 return value; 652 } 653 break; 654 } 655 656 case ArrayWithDouble: { 657 unsigned length = butterfly->publicLength(); 658 659 if (!length--) 660 return jsUndefined(); 661 662 RELEASE_ASSERT(length < butterfly->vectorLength()); 663 double value = butterfly->contiguousDouble().at(this, length); 664 if (value == value) { 665 butterfly->contiguousDouble().at(this, length) = PNaN; 666 butterfly->setPublicLength(length); 667 return JSValue(JSValue::EncodeAsDouble, value); 668 } 669 break; 670 } 671 672 case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: { 673 ArrayStorage* storage = butterfly->arrayStorage(); 674 675 unsigned length = storage->length(); 676 if (!length) { 677 if (!isLengthWritable()) 678 throwTypeError(globalObject, scope, ReadonlyPropertyWriteError); 679 return jsUndefined(); 680 } 681 682 unsigned index = length - 1; 683 if (index < storage->vectorLength()) { 684 WriteBarrier<Unknown>& valueSlot = storage->m_vector[index]; 685 if (valueSlot) { 686 --storage->m_numValuesInVector; 687 JSValue element = valueSlot.get(); 688 valueSlot.clear(); 689 690 RELEASE_ASSERT(isLengthWritable()); 691 storage->setLength(index); 692 return element; 693 } 694 } 695 break; 696 } 697 698 default: 699 CRASH(); 700 return JSValue(); 701 } 702 703 unsigned index = getArrayLength() - 1; 704 // Let element be the result of calling the [[Get]] internal method of O with argument indx. 705 JSValue element = get(globalObject, index); 706 RETURN_IF_EXCEPTION(scope, JSValue()); 707 // Call the [[Delete]] internal method of O with arguments indx and true. 708 bool success = deletePropertyByIndex(this, globalObject, index); 709 RETURN_IF_EXCEPTION(scope, JSValue()); 710 if (!success) { 711 throwTypeError(globalObject, scope, UnableToDeletePropertyError); 712 return jsUndefined(); 713 } 714 // Call the [[Put]] internal method of O with arguments "length", indx, and true. 715 scope.release(); 716 setLength(globalObject, index, true); 717 // Return element. 718 return element; 719 } 720 721 // Push & putIndex are almost identical, with two small differences. 722 // - we always are writing beyond the current array bounds, so it is always necessary to update m_length & m_numValuesInVector. 723 // - pushing to an array of length 2^32-1 stores the property, but throws a range error. 724 NEVER_INLINE void JSArray::push(JSGlobalObject* globalObject, JSValue value) 725 { 726 pushInline(globalObject, value); 727 } 728 729 JSArray* JSArray::fastSlice(JSGlobalObject* globalObject, unsigned startIndex, unsigned count) 730 { 731 VM& vm = globalObject->vm(); 732 733 ensureWritable(vm); 734 735 auto arrayType = indexingMode(); 736 switch (arrayType) { 737 case ArrayWithDouble: 738 case ArrayWithInt32: 739 case ArrayWithContiguous: { 740 if (count >= MIN_SPARSE_ARRAY_INDEX || structure(vm)->holesMustForwardToPrototype(vm, this)) 741 return nullptr; 742 743 Structure* resultStructure = globalObject->arrayStructureForIndexingTypeDuringAllocation(arrayType); 744 if (UNLIKELY(hasAnyArrayStorage(resultStructure->indexingType()))) 745 return nullptr; 746 747 ASSERT(!globalObject->isHavingABadTime()); 748 ObjectInitializationScope scope(vm); 749 JSArray* resultArray = JSArray::tryCreateUninitializedRestricted(scope, resultStructure, count); 750 if (UNLIKELY(!resultArray)) 751 return nullptr; 752 753 auto& resultButterfly = *resultArray->butterfly(); 754 if (arrayType == ArrayWithDouble) 755 gcSafeMemcpy(resultButterfly.contiguousDouble().data(), butterfly()->contiguousDouble().data() + startIndex, sizeof(JSValue) * count); 756 else 757 gcSafeMemcpy(resultButterfly.contiguous().data(), butterfly()->contiguous().data() + startIndex, sizeof(JSValue) * count); 758 759 ASSERT(resultButterfly.publicLength() == count); 760 return resultArray; 761 } 762 default: 763 return nullptr; 764 } 765 } 766 767 bool JSArray::shiftCountWithArrayStorage(VM& vm, unsigned startIndex, unsigned count, ArrayStorage* storage) 768 { 769 unsigned oldLength = storage->length(); 770 RELEASE_ASSERT(count <= oldLength); 771 772 // If the array contains holes or is otherwise in an abnormal state, 773 // use the generic algorithm in ArrayPrototype. 774 if (storage->hasHoles() 775 || hasSparseMap() 776 || shouldUseSlowPut(indexingType())) { 777 return false; 778 } 779 780 if (!oldLength) 781 return true; 782 783 unsigned length = oldLength - count; 784 785 storage->m_numValuesInVector -= count; 786 storage->setLength(length); 787 788 unsigned vectorLength = storage->vectorLength(); 789 if (!vectorLength) 790 return true; 791 792 if (startIndex >= vectorLength) 793 return true; 794 795 DisallowGC disallowGC; 796 auto locker = holdLock(cellLock()); 797 798 if (startIndex + count > vectorLength) 799 count = vectorLength - startIndex; 800 801 unsigned usedVectorLength = std::min(vectorLength, oldLength); 802 803 unsigned numElementsBeforeShiftRegion = startIndex; 804 unsigned firstIndexAfterShiftRegion = startIndex + count; 805 unsigned numElementsAfterShiftRegion = usedVectorLength - firstIndexAfterShiftRegion; 806 ASSERT(numElementsBeforeShiftRegion + count + numElementsAfterShiftRegion == usedVectorLength); 807 808 // The point of this comparison seems to be to minimize the amount of elements that have to 809 // be moved during a shift operation. 810 if (numElementsBeforeShiftRegion < numElementsAfterShiftRegion) { 811 // The number of elements before the shift region is less than the number of elements 812 // after the shift region, so we move the elements before to the right. 813 if (numElementsBeforeShiftRegion) { 814 RELEASE_ASSERT(count + startIndex <= vectorLength); 815 gcSafeMemmove(storage->m_vector + count, 816 storage->m_vector, 817 sizeof(JSValue) * startIndex); 818 } 819 { 820 // When moving Butterfly's head to adjust property-storage, we must take a structure lock. 821 // Otherwise, concurrent JIT compiler accesses to a property storage which is half-baked due to move for shift / unshift. 822 Structure* structure = this->structure(vm); 823 ConcurrentJSLocker structureLock(structure->lock()); 824 // Adjust the Butterfly and the index bias. We only need to do this here because we're changing 825 // the start of the Butterfly, which needs to point at the first indexed property in the used 826 // portion of the vector. 827 Butterfly* butterfly = this->butterfly()->shift(structure, count); 828 storage = butterfly->arrayStorage(); 829 storage->m_indexBias += count; 830 831 // Since we're consuming part of the vector by moving its beginning to the left, 832 // we need to modify the vector length appropriately. 833 storage->setVectorLength(vectorLength - count); 834 setButterfly(vm, butterfly); 835 } 836 } else { 837 // The number of elements before the shift region is greater than or equal to the number 838 // of elements after the shift region, so we move the elements after the shift region to the left. 839 gcSafeMemmove(storage->m_vector + startIndex, 840 storage->m_vector + firstIndexAfterShiftRegion, 841 sizeof(JSValue) * numElementsAfterShiftRegion); 842 843 // Clear the slots of the elements we just moved. 844 unsigned startOfEmptyVectorTail = usedVectorLength - count; 845 for (unsigned i = startOfEmptyVectorTail; i < usedVectorLength; ++i) 846 storage->m_vector[i].clear(); 847 // We don't modify the index bias or the Butterfly pointer in this case because we're not changing 848 // the start of the Butterfly, which needs to point at the first indexed property in the used 849 // portion of the vector. We also don't modify the vector length because we're not actually changing 850 // its length; we're just using less of it. 851 } 852 853 return true; 854 } 855 856 bool JSArray::shiftCountWithAnyIndexingType(JSGlobalObject* globalObject, unsigned& startIndex, unsigned count) 857 { 858 VM& vm = globalObject->vm(); 859 RELEASE_ASSERT(count > 0); 860 861 ensureWritable(vm); 862 863 Butterfly* butterfly = this->butterfly(); 864 865 auto indexingType = this->indexingType(); 866 switch (indexingType) { 867 case ArrayClass: 868 return true; 869 870 case ArrayWithUndecided: 871 // Don't handle this because it's confusing and it shouldn't come up. 872 return false; 873 874 case ArrayWithInt32: 875 case ArrayWithContiguous: { 876 unsigned oldLength = butterfly->publicLength(); 877 RELEASE_ASSERT(count <= oldLength); 878 879 // We may have to walk the entire array to do the shift. We're willing to do 880 // so only if it's not horribly slow. 881 if (oldLength - (startIndex + count) >= MIN_SPARSE_ARRAY_INDEX) 882 return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm)); 883 884 // Storing to a hole is fine since we're still having a good time. But reading from a hole 885 // is totally not fine, since we might have to read from the proto chain. 886 // We have to check for holes before we start moving things around so that we don't get halfway 887 // through shifting and then realize we should have been in ArrayStorage mode. 888 unsigned end = oldLength - count; 889 if (this->structure(vm)->holesMustForwardToPrototype(vm, this)) { 890 for (unsigned i = startIndex; i < end; ++i) { 891 JSValue v = butterfly->contiguous().at(this, i + count).get(); 892 if (UNLIKELY(!v)) { 893 startIndex = i; 894 return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm)); 895 } 896 butterfly->contiguous().at(this, i).setWithoutWriteBarrier(v); 897 } 898 } else { 899 gcSafeMemmove(butterfly->contiguous().data() + startIndex, 900 butterfly->contiguous().data() + startIndex + count, 901 sizeof(JSValue) * (end - startIndex)); 902 } 903 904 for (unsigned i = end; i < oldLength; ++i) 905 butterfly->contiguous().at(this, i).clear(); 906 907 butterfly->setPublicLength(oldLength - count); 908 909 // Our memmoving of values around in the array could have concealed some of them from 910 // the collector. Let's make sure that the collector scans this object again. 911 if (indexingType == ArrayWithContiguous) 912 vm.heap.writeBarrier(this); 913 914 return true; 915 } 916 917 case ArrayWithDouble: { 918 unsigned oldLength = butterfly->publicLength(); 919 RELEASE_ASSERT(count <= oldLength); 920 921 // We may have to walk the entire array to do the shift. We're willing to do 922 // so only if it's not horribly slow. 923 if (oldLength - (startIndex + count) >= MIN_SPARSE_ARRAY_INDEX) 924 return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm)); 925 926 // Storing to a hole is fine since we're still having a good time. But reading from a hole 927 // is totally not fine, since we might have to read from the proto chain. 928 // We have to check for holes before we start moving things around so that we don't get halfway 929 // through shifting and then realize we should have been in ArrayStorage mode. 930 unsigned end = oldLength - count; 931 if (this->structure(vm)->holesMustForwardToPrototype(vm, this)) { 932 for (unsigned i = startIndex; i < end; ++i) { 933 double v = butterfly->contiguousDouble().at(this, i + count); 934 if (UNLIKELY(v != v)) { 935 startIndex = i; 936 return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm)); 937 } 938 butterfly->contiguousDouble().at(this, i) = v; 939 } 940 } else { 941 gcSafeMemmove(butterfly->contiguousDouble().data() + startIndex, 942 butterfly->contiguousDouble().data() + startIndex + count, 943 sizeof(JSValue) * (end - startIndex)); 944 } 945 for (unsigned i = end; i < oldLength; ++i) 946 butterfly->contiguousDouble().at(this, i) = PNaN; 947 948 butterfly->setPublicLength(oldLength - count); 949 return true; 950 } 951 952 case ArrayWithArrayStorage: 953 case ArrayWithSlowPutArrayStorage: 954 return shiftCountWithArrayStorage(vm, startIndex, count, arrayStorage()); 955 956 default: 957 CRASH(); 958 return false; 959 } 960 } 961 962 // Returns true if the unshift can be handled, false to fallback. 963 bool JSArray::unshiftCountWithArrayStorage(JSGlobalObject* globalObject, unsigned startIndex, unsigned count, ArrayStorage* storage) 964 { 965 VM& vm = globalObject->vm(); 966 auto scope = DECLARE_THROW_SCOPE(vm); 967 968 unsigned length = storage->length(); 969 970 RELEASE_ASSERT(startIndex <= length); 971 972 // If the array contains holes or is otherwise in an abnormal state, 973 // use the generic algorithm in ArrayPrototype. 974 if (storage->hasHoles() || storage->inSparseMode() || shouldUseSlowPut(indexingType())) 975 return false; 976 977 bool moveFront = !startIndex || startIndex < length / 2; 978 979 unsigned vectorLength = storage->vectorLength(); 980 981 // Need to have GC deferred around the unshiftCountSlowCase(), since that leaves the butterfly in 982 // a weird state: some parts of it will be left uninitialized, which we will fill in here. 983 DeferGC deferGC(vm.heap); 984 auto locker = holdLock(cellLock()); 985 986 if (moveFront && storage->m_indexBias >= count) { 987 // When moving Butterfly's head to adjust property-storage, we must take a structure lock. 988 // Otherwise, concurrent JIT compiler accesses to a property storage which is half-baked due to move for shift / unshift. 989 Structure* structure = this->structure(vm); 990 ConcurrentJSLocker structureLock(structure->lock()); 991 Butterfly* newButterfly = storage->butterfly()->unshift(structure, count); 992 storage = newButterfly->arrayStorage(); 993 storage->m_indexBias -= count; 994 storage->setVectorLength(vectorLength + count); 995 setButterfly(vm, newButterfly); 996 } else if (!moveFront && vectorLength - length >= count) 997 storage = storage->butterfly()->arrayStorage(); 998 else if (unshiftCountSlowCase(locker, vm, deferGC, moveFront, count)) 999 storage = arrayStorage(); 1000 else { 1001 throwOutOfMemoryError(globalObject, scope); 1002 return true; 1003 } 1004 1005 WriteBarrier<Unknown>* vector = storage->m_vector; 1006 1007 if (startIndex) { 1008 if (moveFront) 1009 gcSafeMemmove(vector, vector + count, startIndex * sizeof(JSValue)); 1010 else if (length - startIndex) 1011 gcSafeMemmove(vector + startIndex + count, vector + startIndex, (length - startIndex) * sizeof(JSValue)); 1012 } 1013 1014 for (unsigned i = 0; i < count; i++) 1015 vector[i + startIndex].clear(); 1016 1017 return true; 1018 } 1019 1020 bool JSArray::unshiftCountWithAnyIndexingType(JSGlobalObject* globalObject, unsigned startIndex, unsigned count) 1021 { 1022 VM& vm = globalObject->vm(); 1023 auto scope = DECLARE_THROW_SCOPE(vm); 1024 1025 ensureWritable(vm); 1026 1027 Butterfly* butterfly = this->butterfly(); 1028 1029 switch (indexingType()) { 1030 case ArrayClass: 1031 case ArrayWithUndecided: 1032 // We could handle this. But it shouldn't ever come up, so we won't. 1033 return false; 1034 1035 case ArrayWithInt32: 1036 case ArrayWithContiguous: { 1037 unsigned oldLength = butterfly->publicLength(); 1038 1039 // We may have to walk the entire array to do the unshift. We're willing to do so 1040 // only if it's not horribly slow. 1041 if (oldLength - startIndex >= MIN_SPARSE_ARRAY_INDEX) 1042 RELEASE_AND_RETURN(scope, unshiftCountWithArrayStorage(globalObject, startIndex, count, ensureArrayStorage(vm))); 1043 1044 Checked<unsigned, RecordOverflow> checkedLength(oldLength); 1045 checkedLength += count; 1046 unsigned newLength; 1047 if (CheckedState::DidOverflow == checkedLength.safeGet(newLength)) { 1048 throwOutOfMemoryError(globalObject, scope); 1049 return true; 1050 } 1051 if (newLength > MAX_STORAGE_VECTOR_LENGTH) 1052 return false; 1053 if (!ensureLength(vm, newLength)) { 1054 throwOutOfMemoryError(globalObject, scope); 1055 return true; 1056 } 1057 butterfly = this->butterfly(); 1058 1059 // We have to check for holes before we start moving things around so that we don't get halfway 1060 // through shifting and then realize we should have been in ArrayStorage mode. 1061 for (unsigned i = oldLength; i-- > startIndex;) { 1062 JSValue v = butterfly->contiguous().at(this, i).get(); 1063 if (UNLIKELY(!v)) 1064 RELEASE_AND_RETURN(scope, unshiftCountWithArrayStorage(globalObject, startIndex, count, ensureArrayStorage(vm))); 1065 } 1066 1067 for (unsigned i = oldLength; i-- > startIndex;) { 1068 JSValue v = butterfly->contiguous().at(this, i).get(); 1069 ASSERT(v); 1070 butterfly->contiguous().at(this, i + count).setWithoutWriteBarrier(v); 1071 } 1072 1073 // Our memmoving of values around in the array could have concealed some of them from 1074 // the collector. Let's make sure that the collector scans this object again. 1075 vm.heap.writeBarrier(this); 1076 1077 // NOTE: we're leaving being garbage in the part of the array that we shifted out 1078 // of. This is fine because the caller is required to store over that area, and 1079 // in contiguous mode storing into a hole is guaranteed to behave exactly the same 1080 // as storing over an existing element. 1081 1082 return true; 1083 } 1084 1085 case ArrayWithDouble: { 1086 unsigned oldLength = butterfly->publicLength(); 1087 1088 // We may have to walk the entire array to do the unshift. We're willing to do so 1089 // only if it's not horribly slow. 1090 if (oldLength - startIndex >= MIN_SPARSE_ARRAY_INDEX) 1091 RELEASE_AND_RETURN(scope, unshiftCountWithArrayStorage(globalObject, startIndex, count, ensureArrayStorage(vm))); 1092 1093 Checked<unsigned, RecordOverflow> checkedLength(oldLength); 1094 checkedLength += count; 1095 unsigned newLength; 1096 if (CheckedState::DidOverflow == checkedLength.safeGet(newLength)) { 1097 throwOutOfMemoryError(globalObject, scope); 1098 return true; 1099 } 1100 if (newLength > MAX_STORAGE_VECTOR_LENGTH) 1101 return false; 1102 if (!ensureLength(vm, newLength)) { 1103 throwOutOfMemoryError(globalObject, scope); 1104 return true; 1105 } 1106 butterfly = this->butterfly(); 1107 1108 // We have to check for holes before we start moving things around so that we don't get halfway 1109 // through shifting and then realize we should have been in ArrayStorage mode. 1110 for (unsigned i = oldLength; i-- > startIndex;) { 1111 double v = butterfly->contiguousDouble().at(this, i); 1112 if (UNLIKELY(v != v)) 1113 RELEASE_AND_RETURN(scope, unshiftCountWithArrayStorage(globalObject, startIndex, count, ensureArrayStorage(vm))); 1114 } 1115 1116 for (unsigned i = oldLength; i-- > startIndex;) { 1117 double v = butterfly->contiguousDouble().at(this, i); 1118 ASSERT(v == v); 1119 butterfly->contiguousDouble().at(this, i + count) = v; 1120 } 1121 1122 // NOTE: we're leaving being garbage in the part of the array that we shifted out 1123 // of. This is fine because the caller is required to store over that area, and 1124 // in contiguous mode storing into a hole is guaranteed to behave exactly the same 1125 // as storing over an existing element. 1126 1127 return true; 1128 } 1129 1130 case ArrayWithArrayStorage: 1131 case ArrayWithSlowPutArrayStorage: 1132 RELEASE_AND_RETURN(scope, unshiftCountWithArrayStorage(globalObject, startIndex, count, arrayStorage())); 1133 1134 default: 1135 CRASH(); 1136 return false; 1137 } 1138 } 1139 1140 void JSArray::fillArgList(JSGlobalObject* globalObject, MarkedArgumentBuffer& args) 1141 { 1142 unsigned i = 0; 1143 unsigned vectorEnd; 1144 WriteBarrier<Unknown>* vector; 1145 1146 Butterfly* butterfly = this->butterfly(); 1147 1148 switch (indexingType()) { 1149 case ArrayClass: 1150 return; 1151 1152 case ArrayWithUndecided: { 1153 vector = nullptr; 1154 vectorEnd = 0; 1155 break; 1156 } 1157 1158 case ArrayWithInt32: 1159 case ArrayWithContiguous: { 1160 vectorEnd = butterfly->publicLength(); 1161 vector = butterfly->contiguous().data(); 1162 break; 1163 } 1164 1165 case ArrayWithDouble: { 1166 vector = nullptr; 1167 vectorEnd = 0; 1168 for (; i < butterfly->publicLength(); ++i) { 1169 double v = butterfly->contiguousDouble().at(this, i); 1170 if (v != v) 1171 break; 1172 args.append(JSValue(JSValue::EncodeAsDouble, v)); 1173 } 1174 break; 1175 } 1176 1177 case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: { 1178 ArrayStorage* storage = butterfly->arrayStorage(); 1179 1180 vector = storage->m_vector; 1181 vectorEnd = std::min(storage->length(), storage->vectorLength()); 1182 break; 1183 } 1184 1185 default: 1186 CRASH(); 1187 #if COMPILER_QUIRK(CONSIDERS_UNREACHABLE_CODE) 1188 vector = 0; 1189 vectorEnd = 0; 1190 break; 1191 #endif 1192 } 1193 1194 for (; i < vectorEnd; ++i) { 1195 WriteBarrier<Unknown>& v = vector[i]; 1196 if (!v) 1197 break; 1198 args.append(v.get()); 1199 } 1200 1201 // FIXME: What prevents this from being called with a RuntimeArray? The length function will always return 0 in that case. 1202 for (; i < length(); ++i) 1203 args.append(get(globalObject, i)); 1204 } 1205 1206 void JSArray::copyToArguments(JSGlobalObject* globalObject, JSValue* firstElementDest, unsigned offset, unsigned length) 1207 { 1208 VM& vm = globalObject->vm(); 1209 auto scope = DECLARE_THROW_SCOPE(vm); 1210 1211 unsigned i = offset; 1212 WriteBarrier<Unknown>* vector; 1213 unsigned vectorEnd; 1214 length += offset; // We like to think of the length as being our length, rather than the output length. 1215 1216 // FIXME: What prevents this from being called with a RuntimeArray? The length function will always return 0 in that case. 1217 ASSERT(length == this->length()); 1218 1219 Butterfly* butterfly = this->butterfly(); 1220 switch (indexingType()) { 1221 case ArrayClass: 1222 return; 1223 1224 case ArrayWithUndecided: { 1225 vector = nullptr; 1226 vectorEnd = 0; 1227 break; 1228 } 1229 1230 case ArrayWithInt32: 1231 case ArrayWithContiguous: { 1232 vector = butterfly->contiguous().data(); 1233 vectorEnd = butterfly->publicLength(); 1234 break; 1235 } 1236 1237 case ArrayWithDouble: { 1238 vector = nullptr; 1239 vectorEnd = 0; 1240 for (; i < butterfly->publicLength(); ++i) { 1241 ASSERT(i < butterfly->vectorLength()); 1242 double v = butterfly->contiguousDouble().at(this, i); 1243 if (v != v) 1244 break; 1245 firstElementDest[i - offset] = JSValue(JSValue::EncodeAsDouble, v); 1246 } 1247 break; 1248 } 1249 1250 case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: { 1251 ArrayStorage* storage = butterfly->arrayStorage(); 1252 vector = storage->m_vector; 1253 vectorEnd = std::min(length, storage->vectorLength()); 1254 break; 1255 } 1256 1257 default: 1258 CRASH(); 1259 #if COMPILER_QUIRK(CONSIDERS_UNREACHABLE_CODE) 1260 vector = 0; 1261 vectorEnd = 0; 1262 break; 1263 #endif 1264 } 1265 1266 for (; i < vectorEnd; ++i) { 1267 WriteBarrier<Unknown>& v = vector[i]; 1268 if (!v) 1269 break; 1270 firstElementDest[i - offset] = v.get(); 1271 } 1272 1273 for (; i < length; ++i) { 1274 firstElementDest[i - offset] = get(globalObject, i); 1275 RETURN_IF_EXCEPTION(scope, void()); 1276 } 1277 } 1278 1279 bool JSArray::isIteratorProtocolFastAndNonObservable() 1280 { 1281 JSGlobalObject* globalObject = this->globalObject(); 1282 if (!globalObject->isArrayPrototypeIteratorProtocolFastAndNonObservable()) 1283 return false; 1284 1285 VM& vm = globalObject->vm(); 1286 Structure* structure = this->structure(vm); 1287 // This is the fast case. Many arrays will be an original array. 1288 if (globalObject->isOriginalArrayStructure(structure)) 1289 return true; 1290 1291 if (structure->mayInterceptIndexedAccesses()) 1292 return false; 1293 1294 if (getPrototypeDirect(vm) != globalObject->arrayPrototype()) 1295 return false; 1296 1297 if (getDirectOffset(vm, vm.propertyNames->iteratorSymbol) != invalidOffset) 1298 return false; 1299 1300 return true; 1301 } 1302 1303 inline JSArray* constructArray(ObjectInitializationScope& scope, Structure* arrayStructure, unsigned length) 1304 { 1305 JSArray* array = JSArray::tryCreateUninitializedRestricted(scope, arrayStructure, length); 1306 1307 // FIXME: we should probably throw an out of memory error here, but 1308 // when making this change we should check that all clients of this 1309 // function will correctly handle an exception being thrown from here. 1310 // https://bugs.webkit.org/show_bug.cgi?id=169786 1311 RELEASE_ASSERT(array); 1312 1313 // FIXME: We only need this for subclasses of Array because we might need to allocate a new structure to change 1314 // indexing types while initializing. If this triggered a GC then we might scan our currently uninitialized 1315 // array and crash. https://bugs.webkit.org/show_bug.cgi?id=186811 1316 if (!arrayStructure->globalObject()->isOriginalArrayStructure(arrayStructure)) 1317 JSArray::eagerlyInitializeButterfly(scope, array, length); 1318 1319 return array; 1320 } 1321 1322 JSArray* constructArray(JSGlobalObject* globalObject, Structure* arrayStructure, const ArgList& values) 1323 { 1324 VM& vm = globalObject->vm(); 1325 unsigned length = values.size(); 1326 ObjectInitializationScope scope(vm); 1327 1328 JSArray* array = constructArray(scope, arrayStructure, length); 1329 for (unsigned i = 0; i < length; ++i) 1330 array->initializeIndex(scope, i, values.at(i)); 1331 return array; 1332 } 1333 1334 JSArray* constructArray(JSGlobalObject* globalObject, Structure* arrayStructure, const JSValue* values, unsigned length) 1335 { 1336 VM& vm = globalObject->vm(); 1337 ObjectInitializationScope scope(vm); 1338 1339 JSArray* array = constructArray(scope, arrayStructure, length); 1340 for (unsigned i = 0; i < length; ++i) 1341 array->initializeIndex(scope, i, values[i]); 1342 return array; 1343 } 1344 1345 JSArray* constructArrayNegativeIndexed(JSGlobalObject* globalObject, Structure* arrayStructure, const JSValue* values, unsigned length) 1346 { 1347 VM& vm = globalObject->vm(); 1348 ObjectInitializationScope scope(vm); 1349 1350 JSArray* array = constructArray(scope, arrayStructure, length); 1351 for (int i = 0; i < static_cast<int>(length); ++i) 1352 array->initializeIndex(scope, i, values[-i]); 1353 return array; 1354 } 1355 1356 } // namespace JSC