Changeset 128400 in webkit for trunk/Source/JavaScriptCore/runtime/JSArray.cpp
- Timestamp:
- Sep 12, 2012, 9:18:52 PM (13 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/JavaScriptCore/runtime/JSArray.cpp
r127505 r128400 1 1 /* 2 2 * Copyright (C) 1999-2000 Harri Porten ([email protected]) 3 * Copyright (C) 2003, 2007, 2008, 2009 Apple Inc. All rights reserved.3 * Copyright (C) 2003, 2007, 2008, 2009, 2012 Apple Inc. All rights reserved. 4 4 * Copyright (C) 2003 Peter Kelly ([email protected]) 5 5 * Copyright (C) 2006 Alexey Proskuryakov ([email protected]) … … 25 25 26 26 #include "ArrayPrototype.h" 27 #include "ButterflyInlineMethods.h" 27 28 #include "CopiedSpace.h" 28 29 #include "CopiedSpaceInlineMethods.h" … … 31 32 #include "Executable.h" 32 33 #include "GetterSetter.h" 34 #include "IndexingHeaderInlineMethods.h" 33 35 #include "PropertyNameArray.h" 36 #include "Reject.h" 37 #include "SparseArrayValueMapInlineMethods.h" 34 38 #include <wtf/AVLTree.h> 35 39 #include <wtf/Assertions.h> … … 46 50 ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSArray); 47 51 48 // Overview of JSArray49 //50 // Properties of JSArray objects may be stored in one of three locations:51 // * The regular JSObject property map.52 // * A storage vector.53 // * A sparse map of array entries.54 //55 // Properties with non-numeric identifiers, with identifiers that are not representable56 // as an unsigned integer, or where the value is greater than MAX_ARRAY_INDEX57 // (specifically, this is only one property - the value 0xFFFFFFFFU as an unsigned 32-bit58 // integer) are not considered array indices and will be stored in the JSObject property map.59 //60 // All properties with a numeric identifer, representable as an unsigned integer i,61 // where (i <= MAX_ARRAY_INDEX), are an array index and will be stored in either the62 // storage vector or the sparse map. An array index i will be handled in the following63 // fashion:64 //65 // * Where (i < MIN_SPARSE_ARRAY_INDEX) the value will be stored in the storage vector,66 // unless the array is in SparseMode in which case all properties go into the map.67 // * Where (MIN_SPARSE_ARRAY_INDEX <= i <= MAX_STORAGE_VECTOR_INDEX) the value will either68 // be stored in the storage vector or in the sparse array, depending on the density of69 // data that would be stored in the vector (a vector being used where at least70 // (1 / minDensityMultiplier) of the entries would be populated).71 // * Where (MAX_STORAGE_VECTOR_INDEX < i <= MAX_ARRAY_INDEX) the value will always be stored72 // in the sparse array.73 74 // The definition of MAX_STORAGE_VECTOR_LENGTH is dependant on the definition storageSize75 // function below - the MAX_STORAGE_VECTOR_LENGTH limit is defined such that the storage76 // size calculation cannot overflow. (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>)) +77 // (vectorLength * sizeof(WriteBarrier<Unknown>)) must be <= 0xFFFFFFFFU (which is maximum value of size_t).78 #define MAX_STORAGE_VECTOR_LENGTH static_cast<unsigned>((0xFFFFFFFFU - (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>))) / sizeof(WriteBarrier<Unknown>))79 80 // These values have to be macros to be used in max() and min() without introducing81 // a PIC branch in Mach-O binaries, see <rdar://problem/5971391>.82 #define MIN_SPARSE_ARRAY_INDEX 10000U83 #define MAX_STORAGE_VECTOR_INDEX (MAX_STORAGE_VECTOR_LENGTH - 1)84 // 0xFFFFFFFF is a bit weird -- is not an array index even though it's an integer.85 #define MAX_ARRAY_INDEX 0xFFFFFFFEU86 87 // The value BASE_VECTOR_LEN is the maximum number of vector elements we'll allocate88 // for an array that was created with a sepcified length (e.g. a = new Array(123))89 #define BASE_VECTOR_LEN 4U90 91 // The upper bound to the size we'll grow a zero length array when the first element92 // is added.93 #define FIRST_VECTOR_GROW 4U94 95 // Our policy for when to use a vector and when to use a sparse map.96 // For all array indices under MIN_SPARSE_ARRAY_INDEX, we always use a vector.97 // When indices greater than MIN_SPARSE_ARRAY_INDEX are involved, we use a vector98 // as long as it is 1/8 full. If more sparse than that, we use a map.99 static const unsigned minDensityMultiplier = 8;100 101 52 const ClassInfo JSArray::s_info = {"Array", &JSNonFinalObject::s_info, 0, 0, CREATE_METHOD_TABLE(JSArray)}; 102 53 103 // We keep track of the size of the last array after it was grown. We use this 104 // as a simple heuristic for as the value to grow the next array from size 0. 105 // This value is capped by the constant FIRST_VECTOR_GROW defined above. 106 static unsigned lastArraySize = 0; 107 108 static inline bool isDenseEnoughForVector(unsigned length, unsigned numValues) 109 { 110 return length <= MIN_SPARSE_ARRAY_INDEX || length / minDensityMultiplier <= numValues; 111 } 112 113 static bool reject(ExecState* exec, bool throwException, const char* message) 114 { 115 if (throwException) 116 throwTypeError(exec, ASCIILiteral(message)); 117 return false; 118 } 119 120 #if !CHECK_ARRAY_CONSISTENCY 121 122 inline void JSArray::checkConsistency(ConsistencyCheckType) 123 { 124 } 125 54 Butterfly* createArrayButterflyInDictionaryIndexingMode(JSGlobalData& globalData, unsigned initialLength) 55 { 56 Butterfly* butterfly = Butterfly::create( 57 globalData, 0, 0, true, IndexingHeader(), ArrayStorage::sizeFor(0)); 58 ArrayStorage* storage = butterfly->arrayStorage(); 59 storage->setLength(initialLength); 60 storage->setVectorLength(0); 61 storage->m_indexBias = 0; 62 storage->m_sparseMap.clear(); 63 storage->m_numValuesInVector = 0; 64 #if CHECK_ARRAY_CONSISTENCY 65 storage->m_initializationIndex = 0; 66 storage->m_inCompactInitialization = 0; 126 67 #endif 127 128 void JSArray::finishCreation(JSGlobalData& globalData, unsigned initialLength) 129 { 130 Base::finishCreation(globalData); 131 ASSERT(inherits(&s_info)); 132 133 unsigned initialVectorLength = BASE_VECTOR_LEN; 134 unsigned initialStorageSize = storageSize(initialVectorLength); 135 136 void* newStorage = 0; 137 if (!globalData.heap.tryAllocateStorage(initialStorageSize, &newStorage)) 138 CRASH(); 139 140 m_storage = static_cast<ArrayStorage*>(newStorage); 141 m_storage->m_allocBase = m_storage; 142 m_storage->m_length = initialLength; 143 m_vectorLength = initialVectorLength; 144 m_storage->m_numValuesInVector = 0; 145 #if CHECK_ARRAY_CONSISTENCY 146 m_storage->m_inCompactInitialization = false; 147 #endif 148 149 checkConsistency(); 150 } 151 152 JSArray* JSArray::tryFinishCreationUninitialized(JSGlobalData& globalData, unsigned initialLength) 153 { 154 Base::finishCreation(globalData); 155 ASSERT(inherits(&s_info)); 156 157 // Check for lengths larger than we can handle with a vector. 158 if (initialLength > MAX_STORAGE_VECTOR_LENGTH) 159 return 0; 160 161 unsigned initialVectorLength = max(initialLength, BASE_VECTOR_LEN); 162 unsigned initialStorageSize = storageSize(initialVectorLength); 163 164 void* newStorage = 0; 165 if (!globalData.heap.tryAllocateStorage(initialStorageSize, &newStorage)) 166 CRASH(); 167 168 m_storage = static_cast<ArrayStorage*>(newStorage); 169 m_storage->m_allocBase = m_storage; 170 m_storage->m_length = initialLength; 171 m_vectorLength = initialVectorLength; 172 m_storage->m_numValuesInVector = initialLength; 173 174 #if CHECK_ARRAY_CONSISTENCY 175 m_storage->m_initializationIndex = 0; 176 m_storage->m_inCompactInitialization = true; 177 #endif 178 179 return this; 180 } 181 182 // This function can be called multiple times on the same object. 183 void JSArray::finalize(JSCell* cell) 184 { 185 JSArray* thisObject = jsCast<JSArray*>(cell); 186 thisObject->checkConsistency(DestructorConsistencyCheck); 187 thisObject->deallocateSparseMap(); 188 } 189 190 inline SparseArrayValueMap::AddResult SparseArrayValueMap::add(JSArray* array, unsigned i) 191 { 192 SparseArrayEntry entry; 193 entry.setWithoutWriteBarrier(jsUndefined()); 194 195 AddResult result = m_map.add(i, entry); 196 size_t capacity = m_map.capacity(); 197 if (capacity != m_reportedCapacity) { 198 Heap::heap(array)->reportExtraMemoryCost((capacity - m_reportedCapacity) * (sizeof(unsigned) + sizeof(WriteBarrier<Unknown>))); 199 m_reportedCapacity = capacity; 200 } 201 return result; 202 } 203 204 inline void SparseArrayValueMap::put(ExecState* exec, JSArray* array, unsigned i, JSValue value, bool shouldThrow) 205 { 206 AddResult result = add(array, i); 207 SparseArrayEntry& entry = result.iterator->second; 208 209 // To save a separate find & add, we first always add to the sparse map. 210 // In the uncommon case that this is a new property, and the array is not 211 // extensible, this is not the right thing to have done - so remove again. 212 if (result.isNewEntry && !array->isExtensible()) { 213 remove(result.iterator); 214 if (shouldThrow) 215 throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError)); 216 return; 217 } 218 219 if (!(entry.attributes & Accessor)) { 220 if (entry.attributes & ReadOnly) { 221 if (shouldThrow) 222 throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError)); 223 return; 224 } 225 226 entry.set(exec->globalData(), array, value); 227 return; 228 } 229 230 JSValue accessor = entry.Base::get(); 231 ASSERT(accessor.isGetterSetter()); 232 JSObject* setter = asGetterSetter(accessor)->setter(); 233 234 if (!setter) { 235 if (shouldThrow) 236 throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError)); 237 return; 238 } 239 240 CallData callData; 241 CallType callType = setter->methodTable()->getCallData(setter, callData); 242 MarkedArgumentBuffer args; 243 args.append(value); 244 call(exec, setter, callType, callData, array, args); 245 } 246 247 inline bool SparseArrayValueMap::putDirect(ExecState* exec, JSArray* array, unsigned i, JSValue value, PutDirectIndexMode mode) 248 { 249 AddResult result = add(array, i); 250 SparseArrayEntry& entry = result.iterator->second; 251 252 // To save a separate find & add, we first always add to the sparse map. 253 // In the uncommon case that this is a new property, and the array is not 254 // extensible, this is not the right thing to have done - so remove again. 255 if (mode != PutDirectIndexLikePutDirect && result.isNewEntry && !array->isExtensible()) { 256 remove(result.iterator); 257 return reject(exec, mode == PutDirectIndexShouldThrow, "Attempting to define property on object that is not extensible."); 258 } 259 260 entry.attributes = 0; 261 entry.set(exec->globalData(), array, value); 262 return true; 263 } 264 265 inline void SparseArrayEntry::get(PropertySlot& slot) const 266 { 267 JSValue value = Base::get(); 268 ASSERT(value); 269 270 if (LIKELY(!value.isGetterSetter())) { 271 slot.setValue(value); 272 return; 273 } 274 275 JSObject* getter = asGetterSetter(value)->getter(); 276 if (!getter) { 277 slot.setUndefined(); 278 return; 279 } 280 281 slot.setGetterSlot(getter); 282 } 283 284 inline void SparseArrayEntry::get(PropertyDescriptor& descriptor) const 285 { 286 descriptor.setDescriptor(Base::get(), attributes); 287 } 288 289 inline JSValue SparseArrayEntry::get(ExecState* exec, JSArray* array) const 290 { 291 JSValue result = Base::get(); 292 ASSERT(result); 293 294 if (LIKELY(!result.isGetterSetter())) 295 return result; 296 297 JSObject* getter = asGetterSetter(result)->getter(); 298 if (!getter) 299 return jsUndefined(); 300 301 CallData callData; 302 CallType callType = getter->methodTable()->getCallData(getter, callData); 303 return call(exec, getter, callType, callData, array, exec->emptyList()); 304 } 305 306 inline JSValue SparseArrayEntry::getNonSparseMode() const 307 { 308 ASSERT(!attributes); 309 return Base::get(); 310 } 311 312 inline void SparseArrayValueMap::visitChildren(SlotVisitor& visitor) 313 { 314 iterator end = m_map.end(); 315 for (iterator it = m_map.begin(); it != end; ++it) 316 visitor.append(&it->second); 317 } 318 319 void JSArray::allocateSparseMap(JSGlobalData& globalData) 320 { 321 m_sparseValueMap = new SparseArrayValueMap; 322 globalData.heap.addFinalizer(this, finalize); 323 } 324 325 void JSArray::deallocateSparseMap() 326 { 327 delete m_sparseValueMap; 328 m_sparseValueMap = 0; 329 } 330 331 void JSArray::enterDictionaryMode(JSGlobalData& globalData) 332 { 333 ArrayStorage* storage = m_storage; 334 SparseArrayValueMap* map = m_sparseValueMap; 335 336 if (!map) { 337 allocateSparseMap(globalData); 338 map = m_sparseValueMap; 339 } 340 341 if (map->sparseMode()) 342 return; 343 344 map->setSparseMode(); 345 346 unsigned usedVectorLength = min(storage->m_length, m_vectorLength); 347 for (unsigned i = 0; i < usedVectorLength; ++i) { 348 JSValue value = storage->m_vector[i].get(); 349 // This will always be a new entry in the map, so no need to check we can write, 350 // and attributes are default so no need to set them. 351 if (value) 352 map->add(this, i).iterator->second.set(globalData, this, value); 353 } 354 355 void* newRawStorage = 0; 356 if (!globalData.heap.tryAllocateStorage(storageSize(0), &newRawStorage)) 357 CRASH(); 358 359 ArrayStorage* newStorage = static_cast<ArrayStorage*>(newRawStorage); 360 memcpy(newStorage, m_storage, storageSize(0)); 361 newStorage->m_allocBase = newStorage; 362 m_storage = newStorage; 363 m_indexBias = 0; 364 m_vectorLength = 0; 365 } 366 367 void JSArray::putDescriptor(ExecState* exec, SparseArrayEntry* entryInMap, PropertyDescriptor& descriptor, PropertyDescriptor& oldDescriptor) 368 { 369 if (descriptor.isDataDescriptor()) { 370 if (descriptor.value()) 371 entryInMap->set(exec->globalData(), this, descriptor.value()); 372 else if (oldDescriptor.isAccessorDescriptor()) 373 entryInMap->set(exec->globalData(), this, jsUndefined()); 374 entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~Accessor; 375 return; 376 } 377 378 if (descriptor.isAccessorDescriptor()) { 379 JSObject* getter = 0; 380 if (descriptor.getterPresent()) 381 getter = descriptor.getterObject(); 382 else if (oldDescriptor.isAccessorDescriptor()) 383 getter = oldDescriptor.getterObject(); 384 JSObject* setter = 0; 385 if (descriptor.setterPresent()) 386 setter = descriptor.setterObject(); 387 else if (oldDescriptor.isAccessorDescriptor()) 388 setter = oldDescriptor.setterObject(); 389 390 GetterSetter* accessor = GetterSetter::create(exec); 391 if (getter) 392 accessor->setGetter(exec->globalData(), getter); 393 if (setter) 394 accessor->setSetter(exec->globalData(), setter); 395 396 entryInMap->set(exec->globalData(), this, accessor); 397 entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~ReadOnly; 398 return; 399 } 400 401 ASSERT(descriptor.isGenericDescriptor()); 402 entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor); 403 } 404 405 // Defined in ES5.1 8.12.9 406 bool JSArray::defineOwnNumericProperty(ExecState* exec, unsigned index, PropertyDescriptor& descriptor, bool throwException) 407 { 408 ASSERT(index != 0xFFFFFFFF); 409 410 if (!inSparseMode()) { 411 // Fast case: we're putting a regular property to a regular array 412 // FIXME: this will pessimistically assume that if attributes are missing then they'll default to false 413 // – however if the property currently exists missing attributes will override from their current 'true' 414 // state (i.e. defineOwnProperty could be used to set a value without needing to entering 'SparseMode'). 415 if (!descriptor.attributes()) { 416 ASSERT(!descriptor.isAccessorDescriptor()); 417 return putDirectIndex(exec, index, descriptor.value(), throwException ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow); 418 } 419 420 enterDictionaryMode(exec->globalData()); 421 } 422 423 SparseArrayValueMap* map = m_sparseValueMap; 424 ASSERT(map); 425 426 // 1. Let current be the result of calling the [[GetOwnProperty]] internal method of O with property name P. 427 SparseArrayValueMap::AddResult result = map->add(this, index); 428 SparseArrayEntry* entryInMap = &result.iterator->second; 429 430 // 2. Let extensible be the value of the [[Extensible]] internal property of O. 431 // 3. If current is undefined and extensible is false, then Reject. 432 // 4. If current is undefined and extensible is true, then 433 if (result.isNewEntry) { 434 if (!isExtensible()) { 435 map->remove(result.iterator); 436 return reject(exec, throwException, "Attempting to define property on object that is not extensible."); 437 } 438 439 // 4.a. If IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then create an own data property 440 // named P of object O whose [[Value]], [[Writable]], [[Enumerable]] and [[Configurable]] attribute values 441 // are described by Desc. If the value of an attribute field of Desc is absent, the attribute of the newly 442 // created property is set to its default value. 443 // 4.b. Else, Desc must be an accessor Property Descriptor so, create an own accessor property named P of 444 // object O whose [[Get]], [[Set]], [[Enumerable]] and [[Configurable]] attribute values are described by 445 // Desc. If the value of an attribute field of Desc is absent, the attribute of the newly created property 446 // is set to its default value. 447 // 4.c. Return true. 448 449 PropertyDescriptor defaults; 450 entryInMap->setWithoutWriteBarrier(jsUndefined()); 451 entryInMap->attributes = DontDelete | DontEnum | ReadOnly; 452 entryInMap->get(defaults); 453 454 putDescriptor(exec, entryInMap, descriptor, defaults); 455 if (index >= m_storage->m_length) 456 m_storage->m_length = index + 1; 457 return true; 458 } 459 460 // 5. Return true, if every field in Desc is absent. 461 // 6. Return true, if every field in Desc also occurs in current and the value of every field in Desc is the same value as the corresponding field in current when compared using the SameValue algorithm (9.12). 462 PropertyDescriptor current; 463 entryInMap->get(current); 464 if (descriptor.isEmpty() || descriptor.equalTo(exec, current)) 465 return true; 466 467 // 7. If the [[Configurable]] field of current is false then 468 if (!current.configurable()) { 469 // 7.a. Reject, if the [[Configurable]] field of Desc is true. 470 if (descriptor.configurablePresent() && descriptor.configurable()) 471 return reject(exec, throwException, "Attempting to change configurable attribute of unconfigurable property."); 472 // 7.b. Reject, if the [[Enumerable]] field of Desc is present and the [[Enumerable]] fields of current and Desc are the Boolean negation of each other. 473 if (descriptor.enumerablePresent() && current.enumerable() != descriptor.enumerable()) 474 return reject(exec, throwException, "Attempting to change enumerable attribute of unconfigurable property."); 475 } 476 477 // 8. If IsGenericDescriptor(Desc) is true, then no further validation is required. 478 if (!descriptor.isGenericDescriptor()) { 479 // 9. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) have different results, then 480 if (current.isDataDescriptor() != descriptor.isDataDescriptor()) { 481 // 9.a. Reject, if the [[Configurable]] field of current is false. 482 if (!current.configurable()) 483 return reject(exec, throwException, "Attempting to change access mechanism for an unconfigurable property."); 484 // 9.b. If IsDataDescriptor(current) is true, then convert the property named P of object O from a 485 // data property to an accessor property. Preserve the existing values of the converted property‘s 486 // [[Configurable]] and [[Enumerable]] attributes and set the rest of the property‘s attributes to 487 // their default values. 488 // 9.c. Else, convert the property named P of object O from an accessor property to a data property. 489 // Preserve the existing values of the converted property‘s [[Configurable]] and [[Enumerable]] 490 // attributes and set the rest of the property‘s attributes to their default values. 491 } else if (current.isDataDescriptor() && descriptor.isDataDescriptor()) { 492 // 10. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true, then 493 // 10.a. If the [[Configurable]] field of current is false, then 494 if (!current.configurable() && !current.writable()) { 495 // 10.a.i. Reject, if the [[Writable]] field of current is false and the [[Writable]] field of Desc is true. 496 if (descriptor.writable()) 497 return reject(exec, throwException, "Attempting to change writable attribute of unconfigurable property."); 498 // 10.a.ii. If the [[Writable]] field of current is false, then 499 // 10.a.ii.1. Reject, if the [[Value]] field of Desc is present and SameValue(Desc.[[Value]], current.[[Value]]) is false. 500 if (descriptor.value() && !sameValue(exec, descriptor.value(), current.value())) 501 return reject(exec, throwException, "Attempting to change value of a readonly property."); 502 } 503 // 10.b. else, the [[Configurable]] field of current is true, so any change is acceptable. 504 } else { 505 ASSERT(current.isAccessorDescriptor() && current.getterPresent() && current.setterPresent()); 506 // 11. Else, IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc) are both true so, if the [[Configurable]] field of current is false, then 507 if (!current.configurable()) { 508 // 11.i. Reject, if the [[Set]] field of Desc is present and SameValue(Desc.[[Set]], current.[[Set]]) is false. 509 if (descriptor.setterPresent() && descriptor.setter() != current.setter()) 510 return reject(exec, throwException, "Attempting to change the setter of an unconfigurable property."); 511 // 11.ii. Reject, if the [[Get]] field of Desc is present and SameValue(Desc.[[Get]], current.[[Get]]) is false. 512 if (descriptor.getterPresent() && descriptor.getter() != current.getter()) 513 return reject(exec, throwException, "Attempting to change the getter of an unconfigurable property."); 514 } 515 } 516 } 517 518 // 12. For each attribute field of Desc that is present, set the correspondingly named attribute of the property named P of object O to the value of the field. 519 putDescriptor(exec, entryInMap, descriptor, current); 520 // 13. Return true. 521 return true; 68 return butterfly; 522 69 } 523 70 … … 528 75 return; 529 76 530 enterDictionary Mode(exec->globalData());531 532 SparseArrayValueMap* map = m_sparseValueMap;77 enterDictionaryIndexingMode(exec->globalData()); 78 79 SparseArrayValueMap* map = arrayStorage()->m_sparseMap.get(); 533 80 ASSERT(map); 534 81 map->setLengthIsReadOnly(); … … 631 178 // 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. 632 179 // f. Return true. 633 return array->defineOwnNumericProperty(exec, index, descriptor, throwException); 634 } 635 636 return JSObject::defineOwnProperty(object, exec, propertyName, descriptor, throwException); 637 } 638 639 bool JSArray::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned i, PropertySlot& slot) 640 { 641 JSArray* thisObject = jsCast<JSArray*>(cell); 642 ArrayStorage* storage = thisObject->m_storage; 643 644 if (i >= storage->m_length) { 645 if (i > MAX_ARRAY_INDEX) 646 return thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot); 647 return false; 648 } 649 650 if (i < thisObject->m_vectorLength) { 651 JSValue value = storage->m_vector[i].get(); 652 if (value) { 653 slot.setValue(value); 654 return true; 655 } 656 } else if (SparseArrayValueMap* map = thisObject->m_sparseValueMap) { 657 SparseArrayValueMap::iterator it = map->find(i); 658 if (it != map->notFound()) { 659 it->second.get(slot); 660 return true; 661 } 662 } 663 664 return JSObject::getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot); 180 return array->defineOwnIndexedProperty(exec, index, descriptor, throwException); 181 } 182 183 return array->JSObject::defineOwnNonIndexProperty(exec, propertyName, descriptor, throwException); 665 184 } 666 185 … … 673 192 } 674 193 675 unsigned i = propertyName.asIndex();676 if (i != PropertyName::NotAnIndex)677 return JSArray::getOwnPropertySlotByIndex(thisObject, exec, i, slot);678 679 194 return JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot); 680 195 } … … 688 203 } 689 204 690 ArrayStorage* storage = thisObject->m_storage;691 692 unsigned i = propertyName.asIndex();693 if (i != PropertyName::NotAnIndex) {694 if (i >= storage->m_length)695 return false;696 if (i < thisObject->m_vectorLength) {697 WriteBarrier<Unknown>& value = storage->m_vector[i];698 if (value) {699 descriptor.setDescriptor(value.get(), 0);700 return true;701 }702 } else if (SparseArrayValueMap* map = thisObject->m_sparseValueMap) {703 SparseArrayValueMap::iterator it = map->find(i);704 if (it != map->notFound()) {705 it->second.get(descriptor);706 return true;707 }708 }709 }710 205 return JSObject::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor); 711 206 } … … 715 210 { 716 211 JSArray* thisObject = jsCast<JSArray*>(cell); 717 unsigned i = propertyName.asIndex();718 if (i != PropertyName::NotAnIndex) {719 putByIndex(thisObject, exec, i, value, slot.isStrictMode());720 return;721 }722 212 723 213 if (propertyName == exec->propertyNames().length) { … … 734 224 } 735 225 736 void JSArray::putByIndex(JSCell* cell, ExecState* exec, unsigned i, JSValue value, bool shouldThrow)226 bool JSArray::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName) 737 227 { 738 228 JSArray* thisObject = jsCast<JSArray*>(cell); 739 thisObject->checkConsistency();740 741 ArrayStorage* storage = thisObject->m_storage;742 743 // Fast case - store to the vector.744 if (i < thisObject->m_vectorLength) {745 WriteBarrier<Unknown>& valueSlot = storage->m_vector[i];746 unsigned length = storage->m_length;747 748 // Update m_length & m_numValuesInVector as necessary.749 if (i >= length) {750 length = i + 1;751 storage->m_length = length;752 ++storage->m_numValuesInVector;753 } else if (!valueSlot)754 ++storage->m_numValuesInVector;755 756 valueSlot.set(exec->globalData(), thisObject, value);757 thisObject->checkConsistency();758 return;759 }760 761 // Handle 2^32-1 - this is not an array index (see ES5.1 15.4), and is treated as a regular property.762 if (UNLIKELY(i > MAX_ARRAY_INDEX)) {763 PutPropertySlot slot(shouldThrow);764 thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, i), value, slot);765 return;766 }767 768 // For all other cases, call putByIndexBeyondVectorLength.769 thisObject->putByIndexBeyondVectorLength(exec, i, value, shouldThrow);770 thisObject->checkConsistency();771 }772 773 void JSArray::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, bool shouldThrow)774 {775 JSGlobalData& globalData = exec->globalData();776 777 // i should be a valid array index that is outside of the current vector.778 ASSERT(i >= m_vectorLength);779 ASSERT(i <= MAX_ARRAY_INDEX);780 781 ArrayStorage* storage = m_storage;782 SparseArrayValueMap* map = m_sparseValueMap;783 784 // First, handle cases where we don't currently have a sparse map.785 if (LIKELY(!map)) {786 // If the array is not extensible, we should have entered dictionary mode, and created the spare map.787 ASSERT(isExtensible());788 789 // Update m_length if necessary.790 if (i >= storage->m_length)791 storage->m_length = i + 1;792 793 // Check that it is sensible to still be using a vector, and then try to grow the vector.794 if (LIKELY((isDenseEnoughForVector(i, storage->m_numValuesInVector)) && increaseVectorLength(globalData, i + 1))) {795 // success! - reread m_storage since it has likely been reallocated, and store to the vector.796 storage = m_storage;797 storage->m_vector[i].set(globalData, this, value);798 ++storage->m_numValuesInVector;799 return;800 }801 // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.802 allocateSparseMap(exec->globalData());803 map = m_sparseValueMap;804 map->put(exec, this, i, value, shouldThrow);805 return;806 }807 808 // Update m_length if necessary.809 unsigned length = storage->m_length;810 if (i >= length) {811 // Prohibit growing the array if length is not writable.812 if (map->lengthIsReadOnly() || !isExtensible()) {813 if (shouldThrow)814 throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError));815 return;816 }817 length = i + 1;818 storage->m_length = length;819 }820 821 // We are currently using a map - check whether we still want to be doing so.822 // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.823 unsigned numValuesInArray = storage->m_numValuesInVector + map->size();824 if (map->sparseMode() || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(exec->globalData(), length)) {825 map->put(exec, this, i, value, shouldThrow);826 return;827 }828 829 // Reread m_storage afterincreaseVectorLength, update m_numValuesInVector.830 storage = m_storage;831 storage->m_numValuesInVector = numValuesInArray;832 833 // Copy all values from the map into the vector, and delete the map.834 WriteBarrier<Unknown>* vector = storage->m_vector;835 SparseArrayValueMap::const_iterator end = map->end();836 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it)837 vector[it->first].set(globalData, this, it->second.getNonSparseMode());838 deallocateSparseMap();839 840 // Store the new property into the vector.841 WriteBarrier<Unknown>& valueSlot = vector[i];842 if (!valueSlot)843 ++storage->m_numValuesInVector;844 valueSlot.set(globalData, this, value);845 }846 847 bool JSArray::putDirectIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, PutDirectIndexMode mode)848 {849 JSGlobalData& globalData = exec->globalData();850 851 // i should be a valid array index that is outside of the current vector.852 ASSERT(i >= m_vectorLength);853 ASSERT(i <= MAX_ARRAY_INDEX);854 855 ArrayStorage* storage = m_storage;856 SparseArrayValueMap* map = m_sparseValueMap;857 858 // First, handle cases where we don't currently have a sparse map.859 if (LIKELY(!map)) {860 // If the array is not extensible, we should have entered dictionary mode, and created the spare map.861 ASSERT(isExtensible());862 863 // Update m_length if necessary.864 if (i >= storage->m_length)865 storage->m_length = i + 1;866 867 // Check that it is sensible to still be using a vector, and then try to grow the vector.868 if (LIKELY((isDenseEnoughForVector(i, storage->m_numValuesInVector)) && increaseVectorLength(globalData, i + 1))) {869 // success! - reread m_storage since it has likely been reallocated, and store to the vector.870 storage = m_storage;871 storage->m_vector[i].set(globalData, this, value);872 ++storage->m_numValuesInVector;873 return true;874 }875 // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.876 allocateSparseMap(exec->globalData());877 map = m_sparseValueMap;878 return map->putDirect(exec, this, i, value, mode);879 }880 881 // Update m_length if necessary.882 unsigned length = storage->m_length;883 if (i >= length) {884 // Prohibit growing the array if length is not writable.885 if (mode != PutDirectIndexLikePutDirect) {886 if (map->lengthIsReadOnly())887 return reject(exec, mode == PutDirectIndexShouldThrow, StrictModeReadonlyPropertyWriteError);888 if (!isExtensible())889 return reject(exec, mode == PutDirectIndexShouldThrow, "Attempting to define property on object that is not extensible.");890 }891 length = i + 1;892 storage->m_length = length;893 }894 895 // We are currently using a map - check whether we still want to be doing so.896 // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.897 unsigned numValuesInArray = storage->m_numValuesInVector + map->size();898 if (map->sparseMode() || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(exec->globalData(), length))899 return map->putDirect(exec, this, i, value, mode);900 901 // Reread m_storage afterincreaseVectorLength, update m_numValuesInVector.902 storage = m_storage;903 storage->m_numValuesInVector = numValuesInArray;904 905 // Copy all values from the map into the vector, and delete the map.906 WriteBarrier<Unknown>* vector = storage->m_vector;907 SparseArrayValueMap::const_iterator end = map->end();908 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it)909 vector[it->first].set(globalData, this, it->second.getNonSparseMode());910 deallocateSparseMap();911 912 // Store the new property into the vector.913 WriteBarrier<Unknown>& valueSlot = vector[i];914 if (!valueSlot)915 ++storage->m_numValuesInVector;916 valueSlot.set(globalData, this, value);917 return true;918 }919 920 bool JSArray::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)921 {922 JSArray* thisObject = jsCast<JSArray*>(cell);923 unsigned i = propertyName.asIndex();924 if (i != PropertyName::NotAnIndex)925 return thisObject->methodTable()->deletePropertyByIndex(thisObject, exec, i);926 229 927 230 if (propertyName == exec->propertyNames().length) … … 929 232 930 233 return JSObject::deleteProperty(thisObject, exec, propertyName); 931 }932 933 bool JSArray::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i)934 {935 JSArray* thisObject = jsCast<JSArray*>(cell);936 thisObject->checkConsistency();937 938 if (i > MAX_ARRAY_INDEX)939 return thisObject->methodTable()->deleteProperty(thisObject, exec, Identifier::from(exec, i));940 941 ArrayStorage* storage = thisObject->m_storage;942 943 if (i < thisObject->m_vectorLength) {944 WriteBarrier<Unknown>& valueSlot = storage->m_vector[i];945 if (valueSlot) {946 valueSlot.clear();947 --storage->m_numValuesInVector;948 }949 } else if (SparseArrayValueMap* map = thisObject->m_sparseValueMap) {950 SparseArrayValueMap::iterator it = map->find(i);951 if (it != map->notFound()) {952 if (it->second.attributes & DontDelete)953 return false;954 map->remove(it);955 }956 }957 958 thisObject->checkConsistency();959 return true;960 234 } 961 235 … … 967 241 } 968 242 969 void JSArray::getOwn PropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)243 void JSArray::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) 970 244 { 971 245 JSArray* thisObject = jsCast<JSArray*>(object); 972 // FIXME: Filling PropertyNameArray with an identifier for every integer973 // is incredibly inefficient for large arrays. We need a different approach,974 // which almost certainly means a different structure for PropertyNameArray.975 976 ArrayStorage* storage = thisObject->m_storage;977 978 unsigned usedVectorLength = min(storage->m_length, thisObject->m_vectorLength);979 for (unsigned i = 0; i < usedVectorLength; ++i) {980 if (storage->m_vector[i])981 propertyNames.add(Identifier::from(exec, i));982 }983 984 if (SparseArrayValueMap* map = thisObject->m_sparseValueMap) {985 Vector<unsigned> keys;986 keys.reserveCapacity(map->size());987 988 SparseArrayValueMap::const_iterator end = map->end();989 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) {990 if (mode == IncludeDontEnumProperties || !(it->second.attributes & DontEnum))991 keys.append(static_cast<unsigned>(it->first));992 }993 994 qsort(keys.begin(), keys.size(), sizeof(unsigned), compareKeysForQSort);995 for (unsigned i = 0; i < keys.size(); ++i)996 propertyNames.add(Identifier::from(exec, keys[i]));997 }998 246 999 247 if (mode == IncludeDontEnumProperties) 1000 248 propertyNames.add(exec->propertyNames().length); 1001 249 1002 JSObject::getOwnPropertyNames(thisObject, exec, propertyNames, mode); 1003 } 1004 1005 ALWAYS_INLINE unsigned JSArray::getNewVectorLength(unsigned desiredLength) 1006 { 1007 ASSERT(desiredLength <= MAX_STORAGE_VECTOR_LENGTH); 1008 1009 unsigned increasedLength; 1010 unsigned maxInitLength = min(m_storage->m_length, 100000U); 1011 1012 if (desiredLength < maxInitLength) 1013 increasedLength = maxInitLength; 1014 else if (!m_vectorLength) 1015 increasedLength = max(desiredLength, lastArraySize); 1016 else { 1017 // Mathematically equivalent to: 1018 // increasedLength = (newLength * 3 + 1) / 2; 1019 // or: 1020 // increasedLength = (unsigned)ceil(newLength * 1.5)); 1021 // This form is not prone to internal overflow. 1022 increasedLength = desiredLength + (desiredLength >> 1) + (desiredLength & 1); 1023 } 1024 1025 ASSERT(increasedLength >= desiredLength); 1026 1027 lastArraySize = min(increasedLength, FIRST_VECTOR_GROW); 1028 1029 return min(increasedLength, MAX_STORAGE_VECTOR_LENGTH); 1030 } 1031 1032 bool JSArray::increaseVectorLength(JSGlobalData& globalData, unsigned newLength) 1033 { 1034 // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map 1035 // to the vector. Callers have to account for that, because they can do it more efficiently. 1036 if (newLength > MAX_STORAGE_VECTOR_LENGTH) 1037 return false; 1038 1039 ArrayStorage* storage = m_storage; 1040 1041 unsigned vectorLength = m_vectorLength; 1042 ASSERT(newLength > vectorLength); 1043 unsigned newVectorLength = getNewVectorLength(newLength); 1044 1045 // Fast case - there is no precapacity. In these cases a realloc makes sense. 1046 if (LIKELY(!m_indexBias)) { 1047 void* newStorage = storage->m_allocBase; 1048 if (!globalData.heap.tryReallocateStorage(&newStorage, storageSize(vectorLength), storageSize(newVectorLength))) 1049 return false; 1050 1051 storage = m_storage = reinterpret_cast_ptr<ArrayStorage*>(static_cast<char*>(newStorage)); 1052 m_storage->m_allocBase = newStorage; 1053 ASSERT(m_storage->m_allocBase); 1054 1055 m_vectorLength = newVectorLength; 1056 1057 return true; 1058 } 1059 1060 // Remove some, but not all of the precapacity. Atomic decay, & capped to not overflow array length. 1061 unsigned newIndexBias = min(m_indexBias >> 1, MAX_STORAGE_VECTOR_LENGTH - newVectorLength); 1062 // Calculate new stoarge capcity, allowing room for the pre-capacity. 1063 unsigned newStorageCapacity = newVectorLength + newIndexBias; 1064 void* newAllocBase = 0; 1065 if (!globalData.heap.tryAllocateStorage(storageSize(newStorageCapacity), &newAllocBase)) 1066 return false; 1067 // The sum of m_vectorLength and m_indexBias will never exceed MAX_STORAGE_VECTOR_LENGTH. 1068 ASSERT(m_vectorLength <= MAX_STORAGE_VECTOR_LENGTH && (MAX_STORAGE_VECTOR_LENGTH - m_vectorLength) >= m_indexBias); 1069 1070 m_vectorLength = newVectorLength; 1071 m_indexBias = newIndexBias; 1072 m_storage = reinterpret_cast_ptr<ArrayStorage*>(reinterpret_cast<WriteBarrier<Unknown>*>(newAllocBase) + m_indexBias); 1073 1074 // Copy the ArrayStorage header & current contents of the vector. 1075 memmove(m_storage, storage, storageSize(vectorLength)); 1076 1077 // Free the old allocation, update m_allocBase. 1078 m_storage->m_allocBase = newAllocBase; 1079 1080 return true; 250 JSObject::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode); 1081 251 } 1082 252 … … 1084 254 bool JSArray::unshiftCountSlowCase(JSGlobalData& globalData, unsigned count) 1085 255 { 256 ArrayStorage* storage = ensureArrayStorage(globalData); 257 Butterfly* butterfly = storage->butterfly(); 258 unsigned propertyCapacity = structure()->outOfLineCapacity(); 259 unsigned propertySize = structure()->outOfLineSize(); 260 1086 261 // If not, we should have handled this on the fast path. 1087 ASSERT(count > m_indexBias); 1088 1089 ArrayStorage* storage = m_storage; 262 ASSERT(count > storage->m_indexBias); 1090 263 1091 264 // Step 1: … … 1096 269 // * desiredCapacity - how large should we like to grow the vector to - based on 2x requiredVectorLength. 1097 270 1098 unsigned length = storage-> m_length;1099 unsigned usedVectorLength = min( m_vectorLength, length);271 unsigned length = storage->length(); 272 unsigned usedVectorLength = min(storage->vectorLength(), length); 1100 273 ASSERT(usedVectorLength <= MAX_STORAGE_VECTOR_LENGTH); 1101 274 // Check that required vector length is possible, in an overflow-safe fashion. … … 1105 278 ASSERT(requiredVectorLength <= MAX_STORAGE_VECTOR_LENGTH); 1106 279 // The sum of m_vectorLength and m_indexBias will never exceed MAX_STORAGE_VECTOR_LENGTH. 1107 ASSERT( m_vectorLength <= MAX_STORAGE_VECTOR_LENGTH && (MAX_STORAGE_VECTOR_LENGTH - m_vectorLength) >=m_indexBias);1108 unsigned currentCapacity = m_vectorLength +m_indexBias;280 ASSERT(storage->vectorLength() <= MAX_STORAGE_VECTOR_LENGTH && (MAX_STORAGE_VECTOR_LENGTH - storage->vectorLength()) >= storage->m_indexBias); 281 unsigned currentCapacity = storage->vectorLength() + storage->m_indexBias; 1109 282 // The calculation of desiredCapacity won't overflow, due to the range of MAX_STORAGE_VECTOR_LENGTH. 1110 283 unsigned desiredCapacity = min(MAX_STORAGE_VECTOR_LENGTH, max(BASE_VECTOR_LEN, requiredVectorLength) << 1); … … 1117 290 // If the current storage array is sufficiently large (but not too large!) then just keep using it. 1118 291 if (currentCapacity > desiredCapacity && isDenseEnoughForVector(currentCapacity, requiredVectorLength)) { 1119 newAllocBase = storage->m_allocBase;292 newAllocBase = butterfly->base(structure()); 1120 293 newStorageCapacity = currentCapacity; 1121 294 } else { 1122 if (!globalData.heap.tryAllocateStorage(storageSize(desiredCapacity), &newAllocBase)) 295 size_t newSize = Butterfly::totalSize(0, propertyCapacity, true, ArrayStorage::sizeFor(desiredCapacity)); 296 if (!globalData.heap.tryAllocateStorage(newSize, &newAllocBase)) 1123 297 return false; 1124 298 newStorageCapacity = desiredCapacity; … … 1133 307 // vector with half the post-capacity it had previously. 1134 308 unsigned postCapacity = 0; 1135 if (length < m_vectorLength) {309 if (length < storage->vectorLength()) { 1136 310 // Atomic decay, + the post-capacity cannot be greater than what is available. 1137 postCapacity = min(( m_vectorLength- length) >> 1, newStorageCapacity - requiredVectorLength);311 postCapacity = min((storage->vectorLength() - length) >> 1, newStorageCapacity - requiredVectorLength); 1138 312 // If we're moving contents within the same allocation, the post-capacity is being reduced. 1139 ASSERT(newAllocBase != storage->m_allocBase || postCapacity < m_vectorLength - length); 1140 } 1141 1142 m_vectorLength = requiredVectorLength + postCapacity; 1143 m_indexBias = newStorageCapacity - m_vectorLength; 1144 m_storage = reinterpret_cast_ptr<ArrayStorage*>(reinterpret_cast<WriteBarrier<Unknown>*>(newAllocBase) + m_indexBias); 1145 1146 // Step 4: 1147 // Copy array data / header into their new locations, clear post-capacity & free any old allocation. 1148 1149 // If this is being moved within the existing buffer of memory, we are always shifting data 1150 // to the right (since count > m_indexBias). As such this memmove cannot trample the header. 1151 memmove(m_storage->m_vector + count, storage->m_vector, sizeof(WriteBarrier<Unknown>) * usedVectorLength); 1152 memmove(m_storage, storage, storageSize(0)); 1153 1154 // Are we copying into a new allocation? 1155 if (newAllocBase != m_storage->m_allocBase) { 1156 // Free the old allocation, update m_allocBase. 1157 m_storage->m_allocBase = newAllocBase; 1158 } 313 ASSERT(newAllocBase != butterfly->base(structure()) || postCapacity < storage->vectorLength() - length); 314 } 315 316 unsigned newVectorLength = requiredVectorLength + postCapacity; 317 unsigned newIndexBias = newStorageCapacity - newVectorLength; 318 319 Butterfly* newButterfly = Butterfly::fromBase(newAllocBase, newIndexBias, propertyCapacity); 320 321 memmove(newButterfly->arrayStorage()->m_vector + count, storage->m_vector, sizeof(JSValue) * usedVectorLength); 322 memmove(newButterfly->propertyStorage() - propertySize, butterfly->propertyStorage() - propertySize, sizeof(JSValue) * propertySize + sizeof(IndexingHeader) + ArrayStorage::sizeFor(0)); 323 324 newButterfly->arrayStorage()->setVectorLength(newVectorLength); 325 newButterfly->arrayStorage()->m_indexBias = newIndexBias; 326 327 m_butterfly = newButterfly; 1159 328 1160 329 return true; … … 1163 332 bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException) 1164 333 { 1165 check Consistency();1166 1167 ArrayStorage* storage = m_storage;1168 unsigned length = storage-> m_length;334 checkIndexingConsistency(); 335 336 ArrayStorage* storage = ensureArrayStorage(exec->globalData()); 337 unsigned length = storage->length(); 1169 338 1170 339 // If the length is read only then we enter sparse mode, so should enter the following 'if'. 1171 ASSERT(isLengthWritable() || m_sparseValueMap);1172 1173 if (SparseArrayValueMap* map = m_sparseValueMap) {340 ASSERT(isLengthWritable() || storage->m_sparseMap); 341 342 if (SparseArrayValueMap* map = storage->m_sparseMap.get()) { 1174 343 // Fail if the length is not writable. 1175 344 if (map->lengthIsReadOnly()) … … 1198 367 ASSERT(it != map->notFound()); 1199 368 if (it->second.attributes & DontDelete) { 1200 storage-> m_length = index + 1;369 storage->setLength(index + 1); 1201 370 return reject(exec, throwException, "Unable to delete property."); 1202 371 } … … 1207 376 map->remove(keys[i]); 1208 377 if (map->isEmpty()) 1209 deallocateSparse Map();378 deallocateSparseIndexMap(); 1210 379 } 1211 380 } … … 1214 383 if (newLength < length) { 1215 384 // Delete properties from the vector. 1216 unsigned usedVectorLength = min(length, m_vectorLength);385 unsigned usedVectorLength = min(length, storage->vectorLength()); 1217 386 for (unsigned i = newLength; i < usedVectorLength; ++i) { 1218 387 WriteBarrier<Unknown>& valueSlot = storage->m_vector[i]; … … 1223 392 } 1224 393 1225 storage-> m_length = newLength;1226 1227 check Consistency();394 storage->setLength(newLength); 395 396 checkIndexingConsistency(); 1228 397 return true; 1229 398 } … … 1231 400 JSValue JSArray::pop(ExecState* exec) 1232 401 { 1233 checkConsistency(); 1234 ArrayStorage* storage = m_storage; 1235 1236 unsigned length = storage->m_length; 1237 if (!length) { 1238 if (!isLengthWritable()) 1239 throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError)); 402 checkIndexingConsistency(); 403 404 switch (structure()->indexingType()) { 405 case Array: 1240 406 return jsUndefined(); 1241 } 1242 1243 unsigned index = length - 1; 1244 if (index < m_vectorLength) { 1245 WriteBarrier<Unknown>& valueSlot = storage->m_vector[index]; 1246 if (valueSlot) { 1247 --storage->m_numValuesInVector; 1248 JSValue element = valueSlot.get(); 1249 valueSlot.clear(); 407 408 case ArrayWithArrayStorage: { 409 ArrayStorage* storage = m_butterfly->arrayStorage(); 410 411 unsigned length = storage->length(); 412 if (!length) { 413 if (!isLengthWritable()) 414 throwTypeError(exec, StrictModeReadonlyPropertyWriteError); 415 return jsUndefined(); 416 } 417 418 unsigned index = length - 1; 419 if (index < storage->vectorLength()) { 420 WriteBarrier<Unknown>& valueSlot = storage->m_vector[index]; 421 if (valueSlot) { 422 --storage->m_numValuesInVector; 423 JSValue element = valueSlot.get(); 424 valueSlot.clear(); 1250 425 1251 ASSERT(isLengthWritable()); 1252 storage->m_length = index; 1253 checkConsistency(); 1254 return element; 1255 } 1256 } 1257 1258 // Let element be the result of calling the [[Get]] internal method of O with argument indx. 1259 JSValue element = get(exec, index); 1260 if (exec->hadException()) 1261 return jsUndefined(); 1262 // Call the [[Delete]] internal method of O with arguments indx and true. 1263 if (!deletePropertyByIndex(this, exec, index)) { 1264 throwTypeError(exec, ASCIILiteral("Unable to delete property.")); 1265 return jsUndefined(); 1266 } 1267 // Call the [[Put]] internal method of O with arguments "length", indx, and true. 1268 setLength(exec, index, true); 1269 // Return element. 1270 checkConsistency(); 1271 return element; 426 ASSERT(isLengthWritable()); 427 storage->setLength(index); 428 checkIndexingConsistency(); 429 return element; 430 } 431 } 432 433 // Let element be the result of calling the [[Get]] internal method of O with argument indx. 434 JSValue element = get(exec, index); 435 if (exec->hadException()) 436 return jsUndefined(); 437 // Call the [[Delete]] internal method of O with arguments indx and true. 438 if (!deletePropertyByIndex(this, exec, index)) { 439 throwTypeError(exec, "Unable to delete property."); 440 return jsUndefined(); 441 } 442 // Call the [[Put]] internal method of O with arguments "length", indx, and true. 443 setLength(exec, index, true); 444 // Return element. 445 checkIndexingConsistency(); 446 return element; 447 } 448 449 default: 450 ASSERT_NOT_REACHED(); 451 return JSValue(); 452 } 1272 453 } 1273 454 … … 1277 458 void JSArray::push(ExecState* exec, JSValue value) 1278 459 { 1279 checkConsistency(); 1280 ArrayStorage* storage = m_storage; 1281 1282 // Fast case - push within vector, always update m_length & m_numValuesInVector. 1283 unsigned length = storage->m_length; 1284 if (length < m_vectorLength) { 1285 storage->m_vector[length].set(exec->globalData(), this, value); 1286 storage->m_length = length + 1; 1287 ++storage->m_numValuesInVector; 1288 checkConsistency(); 1289 return; 1290 } 1291 1292 // Pushing to an array of length 2^32-1 stores the property, but throws a range error. 1293 if (UNLIKELY(storage->m_length == 0xFFFFFFFFu)) { 1294 methodTable()->putByIndex(this, exec, storage->m_length, value, true); 1295 // Per ES5.1 15.4.4.7 step 6 & 15.4.5.1 step 3.d. 1296 if (!exec->hadException()) 1297 throwError(exec, createRangeError(exec, ASCIILiteral("Invalid array length"))); 1298 return; 1299 } 1300 1301 // Handled the same as putIndex. 1302 putByIndexBeyondVectorLength(exec, storage->m_length, value, true); 1303 checkConsistency(); 1304 } 1305 1306 bool JSArray::shiftCount(ExecState*, unsigned count) 460 checkIndexingConsistency(); 461 462 switch (structure()->indexingType()) { 463 case Array: { 464 putByIndexBeyondVectorLengthWithArrayStorage(exec, 0, value, true, createInitialArrayStorage(exec->globalData())); 465 break; 466 } 467 468 case ArrayWithArrayStorage: { 469 ArrayStorage* storage = m_butterfly->arrayStorage(); 470 471 // Fast case - push within vector, always update m_length & m_numValuesInVector. 472 unsigned length = storage->length(); 473 if (length < storage->vectorLength()) { 474 storage->m_vector[length].set(exec->globalData(), this, value); 475 storage->setLength(length + 1); 476 ++storage->m_numValuesInVector; 477 checkIndexingConsistency(); 478 return; 479 } 480 481 // Pushing to an array of length 2^32-1 stores the property, but throws a range error. 482 if (UNLIKELY(storage->length() == 0xFFFFFFFFu)) { 483 methodTable()->putByIndex(this, exec, storage->length(), value, true); 484 // Per ES5.1 15.4.4.7 step 6 & 15.4.5.1 step 3.d. 485 if (!exec->hadException()) 486 throwError(exec, createRangeError(exec, "Invalid array length")); 487 return; 488 } 489 490 // Handled the same as putIndex. 491 putByIndexBeyondVectorLengthWithArrayStorage(exec, storage->length(), value, true, storage); 492 checkIndexingConsistency(); 493 break; 494 } 495 496 default: 497 ASSERT_NOT_REACHED(); 498 } 499 } 500 501 bool JSArray::shiftCount(ExecState* exec, unsigned count) 1307 502 { 1308 503 ASSERT(count > 0); 1309 504 1310 ArrayStorage* storage = m_storage;1311 1312 unsigned oldLength = storage-> m_length;505 ArrayStorage* storage = ensureArrayStorage(exec->globalData()); 506 507 unsigned oldLength = storage->length(); 1313 508 1314 509 // If the array contains holes or is otherwise in an abnormal state, 1315 510 // use the generic algorithm in ArrayPrototype. 1316 if (oldLength != storage->m_numValuesInVector || inSparse Mode())511 if (oldLength != storage->m_numValuesInVector || inSparseIndexingMode()) 1317 512 return false; 1318 513 … … 1321 516 1322 517 storage->m_numValuesInVector -= count; 1323 storage-> m_length -= count;1324 1325 if (m_vectorLength) {1326 count = min(m_vectorLength, (unsigned)count);1327 1328 m_vectorLength -= count;1329 1330 if (m_vectorLength) {1331 char* newBaseStorage = reinterpret_cast<char*>(storage) + count * sizeof(WriteBarrier<Unknown>);1332 memmove(newBaseStorage, storage, storageSize(0));1333 m_ storage = reinterpret_cast_ptr<ArrayStorage*>(newBaseStorage);1334 1335 m_indexBias += count;518 storage->setLength(oldLength - count); 519 520 unsigned vectorLength = storage->vectorLength(); 521 if (vectorLength) { 522 count = min(vectorLength, (unsigned)count); 523 524 vectorLength -= count; 525 storage->setVectorLength(vectorLength); 526 527 if (vectorLength) { 528 m_butterfly = m_butterfly->shift(structure(), count); 529 storage = m_butterfly->arrayStorage(); 530 storage->m_indexBias += count; 1336 531 } 1337 532 } … … 1342 537 bool JSArray::unshiftCount(ExecState* exec, unsigned count) 1343 538 { 1344 ArrayStorage* storage = m_storage;1345 unsigned length = storage-> m_length;539 ArrayStorage* storage = ensureArrayStorage(exec->globalData()); 540 unsigned length = storage->length(); 1346 541 1347 542 // If the array contains holes or is otherwise in an abnormal state, 1348 543 // use the generic algorithm in ArrayPrototype. 1349 if (length != storage->m_numValuesInVector || inSparseMode())544 if (length != storage->m_numValuesInVector || storage->inSparseMode()) 1350 545 return false; 1351 546 1352 if (m_indexBias >= count) { 1353 m_indexBias -= count; 1354 char* newBaseStorage = reinterpret_cast<char*>(storage) - count * sizeof(WriteBarrier<Unknown>); 1355 memmove(newBaseStorage, storage, storageSize(0)); 1356 m_storage = reinterpret_cast_ptr<ArrayStorage*>(newBaseStorage); 1357 m_vectorLength += count; 547 if (storage->m_indexBias >= count) { 548 m_butterfly = storage->butterfly()->unshift(structure(), count); 549 storage = m_butterfly->arrayStorage(); 550 storage->m_indexBias -= count; 551 storage->setVectorLength(storage->vectorLength() + count); 1358 552 } else if (!unshiftCountSlowCase(exec->globalData(), count)) { 1359 553 throwOutOfMemoryError(exec); … … 1361 555 } 1362 556 1363 WriteBarrier<Unknown>* vector = m_storage->m_vector;557 WriteBarrier<Unknown>* vector = storage->m_vector; 1364 558 for (unsigned i = 0; i < count; i++) 1365 559 vector[i].clear(); … … 1367 561 } 1368 562 1369 void JSArray::visitChildren(JSCell* cell, SlotVisitor& visitor)1370 {1371 JSArray* thisObject = jsCast<JSArray*>(cell);1372 ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info);1373 COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag);1374 ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren());1375 1376 JSNonFinalObject::visitChildren(thisObject, visitor);1377 1378 if (thisObject->m_storage) {1379 MARK_LOG_MESSAGE1("[%u]: ", thisObject->length());1380 1381 ArrayStorage* storage = thisObject->m_storage;1382 void* baseStorage = storage->m_allocBase;1383 1384 visitor.copyAndAppend(reinterpret_cast<void**>(&baseStorage), storageSize(thisObject->m_vectorLength + thisObject->m_indexBias), storage->m_vector->slot(), thisObject->m_vectorLength);1385 1386 if (baseStorage != thisObject->m_storage->m_allocBase) {1387 thisObject->m_storage = reinterpret_cast_ptr<ArrayStorage*>(static_cast<char*>(baseStorage) + sizeof(JSValue) * thisObject->m_indexBias);1388 thisObject->m_storage->m_allocBase = baseStorage;1389 ASSERT(thisObject->m_storage->m_allocBase);1390 }1391 }1392 1393 if (SparseArrayValueMap* map = thisObject->m_sparseValueMap)1394 map->visitChildren(visitor);1395 }1396 1397 563 static int compareNumbersForQSort(const void* a, const void* b) 1398 564 { … … 1411 577 void JSArray::sortNumeric(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData) 1412 578 { 1413 ASSERT(!inSparseMode()); 1414 1415 ArrayStorage* storage = m_storage; 1416 1417 unsigned lengthNotIncludingUndefined = compactForSorting(exec->globalData()); 1418 if (m_sparseValueMap) { 1419 throwOutOfMemoryError(exec); 1420 return; 1421 } 1422 1423 if (!lengthNotIncludingUndefined) 1424 return; 1425 1426 bool allValuesAreNumbers = true; 1427 size_t size = storage->m_numValuesInVector; 1428 for (size_t i = 0; i < size; ++i) { 1429 if (!storage->m_vector[i].isNumber()) { 1430 allValuesAreNumbers = false; 1431 break; 1432 } 1433 } 1434 1435 if (!allValuesAreNumbers) 1436 return sort(exec, compareFunction, callType, callData); 1437 1438 // For numeric comparison, which is fast, qsort is faster than mergesort. We 1439 // also don't require mergesort's stability, since there's no user visible 1440 // side-effect from swapping the order of equal primitive values. 1441 qsort(storage->m_vector, size, sizeof(WriteBarrier<Unknown>), compareNumbersForQSort); 1442 1443 checkConsistency(SortConsistencyCheck); 579 ASSERT(!inSparseIndexingMode()); 580 581 switch (structure()->indexingType()) { 582 case Array: 583 return; 584 585 case ArrayWithArrayStorage: { 586 unsigned lengthNotIncludingUndefined = compactForSorting(exec->globalData()); 587 ArrayStorage* storage = m_butterfly->arrayStorage(); 588 589 if (storage->m_sparseMap.get()) { 590 throwOutOfMemoryError(exec); 591 return; 592 } 593 594 if (!lengthNotIncludingUndefined) 595 return; 596 597 bool allValuesAreNumbers = true; 598 size_t size = storage->m_numValuesInVector; 599 for (size_t i = 0; i < size; ++i) { 600 if (!storage->m_vector[i].isNumber()) { 601 allValuesAreNumbers = false; 602 break; 603 } 604 } 605 606 if (!allValuesAreNumbers) 607 return sort(exec, compareFunction, callType, callData); 608 609 // For numeric comparison, which is fast, qsort is faster than mergesort. We 610 // also don't require mergesort's stability, since there's no user visible 611 // side-effect from swapping the order of equal primitive values. 612 qsort(storage->m_vector, size, sizeof(WriteBarrier<Unknown>), compareNumbersForQSort); 613 614 checkIndexingConsistency(SortConsistencyCheck); 615 return; 616 } 617 618 default: 619 ASSERT_NOT_REACHED(); 620 } 1444 621 } 1445 622 1446 623 void JSArray::sort(ExecState* exec) 1447 624 { 1448 ASSERT(!inSparseMode()); 1449 1450 unsigned lengthNotIncludingUndefined = compactForSorting(exec->globalData()); 1451 if (m_sparseValueMap) { 1452 throwOutOfMemoryError(exec); 1453 return; 1454 } 1455 1456 if (!lengthNotIncludingUndefined) 1457 return; 1458 1459 // Converting JavaScript values to strings can be expensive, so we do it once up front and sort based on that. 1460 // This is a considerable improvement over doing it twice per comparison, though it requires a large temporary 1461 // buffer. Besides, this protects us from crashing if some objects have custom toString methods that return 1462 // random or otherwise changing results, effectively making compare function inconsistent. 1463 1464 Vector<ValueStringPair> values(lengthNotIncludingUndefined); 1465 if (!values.begin()) { 1466 throwOutOfMemoryError(exec); 1467 return; 1468 } 1469 1470 Heap::heap(this)->pushTempSortVector(&values); 1471 1472 bool isSortingPrimitiveValues = true; 1473 for (size_t i = 0; i < lengthNotIncludingUndefined; i++) { 1474 JSValue value = m_storage->m_vector[i].get(); 1475 ASSERT(!value.isUndefined()); 1476 values[i].first = value; 1477 isSortingPrimitiveValues = isSortingPrimitiveValues && value.isPrimitive(); 1478 } 1479 1480 // FIXME: The following loop continues to call toString on subsequent values even after 1481 // a toString call raises an exception. 1482 1483 for (size_t i = 0; i < lengthNotIncludingUndefined; i++) 1484 values[i].second = values[i].first.toWTFStringInline(exec); 1485 1486 if (exec->hadException()) { 625 ASSERT(!inSparseIndexingMode()); 626 627 switch (structure()->indexingType()) { 628 case Array: 629 return; 630 631 case ArrayWithArrayStorage: { 632 unsigned lengthNotIncludingUndefined = compactForSorting(exec->globalData()); 633 ArrayStorage* storage = m_butterfly->arrayStorage(); 634 if (storage->m_sparseMap.get()) { 635 throwOutOfMemoryError(exec); 636 return; 637 } 638 639 if (!lengthNotIncludingUndefined) 640 return; 641 642 // Converting JavaScript values to strings can be expensive, so we do it once up front and sort based on that. 643 // This is a considerable improvement over doing it twice per comparison, though it requires a large temporary 644 // buffer. Besides, this protects us from crashing if some objects have custom toString methods that return 645 // random or otherwise changing results, effectively making compare function inconsistent. 646 647 Vector<ValueStringPair> values(lengthNotIncludingUndefined); 648 if (!values.begin()) { 649 throwOutOfMemoryError(exec); 650 return; 651 } 652 653 Heap::heap(this)->pushTempSortVector(&values); 654 655 bool isSortingPrimitiveValues = true; 656 for (size_t i = 0; i < lengthNotIncludingUndefined; i++) { 657 JSValue value = storage->m_vector[i].get(); 658 ASSERT(!value.isUndefined()); 659 values[i].first = value; 660 isSortingPrimitiveValues = isSortingPrimitiveValues && value.isPrimitive(); 661 } 662 663 // FIXME: The following loop continues to call toString on subsequent values even after 664 // a toString call raises an exception. 665 666 for (size_t i = 0; i < lengthNotIncludingUndefined; i++) 667 values[i].second = values[i].first.toWTFStringInline(exec); 668 669 if (exec->hadException()) { 670 Heap::heap(this)->popTempSortVector(&values); 671 return; 672 } 673 674 // FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather 675 // than O(N log N). 676 677 #if HAVE(MERGESORT) 678 if (isSortingPrimitiveValues) 679 qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort); 680 else 681 mergesort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort); 682 #else 683 // FIXME: The qsort library function is likely to not be a stable sort. 684 // ECMAScript-262 does not specify a stable sort, but in practice, browsers perform a stable sort. 685 qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort); 686 #endif 687 688 // If the toString function changed the length of the array or vector storage, 689 // increase the length to handle the orignal number of actual values. 690 if (storage->vectorLength() < lengthNotIncludingUndefined) { 691 increaseVectorLength(exec->globalData(), lengthNotIncludingUndefined); 692 storage = m_butterfly->arrayStorage(); 693 } 694 if (storage->length() < lengthNotIncludingUndefined) 695 storage->setLength(lengthNotIncludingUndefined); 696 697 JSGlobalData& globalData = exec->globalData(); 698 for (size_t i = 0; i < lengthNotIncludingUndefined; i++) 699 storage->m_vector[i].set(globalData, this, values[i].first); 700 1487 701 Heap::heap(this)->popTempSortVector(&values); 1488 return; 1489 } 1490 1491 // FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather 1492 // than O(N log N). 1493 1494 #if HAVE(MERGESORT) 1495 if (isSortingPrimitiveValues) 1496 qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort); 1497 else 1498 mergesort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort); 1499 #else 1500 // FIXME: The qsort library function is likely to not be a stable sort. 1501 // ECMAScript-262 does not specify a stable sort, but in practice, browsers perform a stable sort. 1502 qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort); 1503 #endif 1504 1505 // If the toString function changed the length of the array or vector storage, 1506 // increase the length to handle the orignal number of actual values. 1507 if (m_vectorLength < lengthNotIncludingUndefined) 1508 increaseVectorLength(exec->globalData(), lengthNotIncludingUndefined); 1509 if (m_storage->m_length < lengthNotIncludingUndefined) 1510 m_storage->m_length = lengthNotIncludingUndefined; 1511 1512 JSGlobalData& globalData = exec->globalData(); 1513 for (size_t i = 0; i < lengthNotIncludingUndefined; i++) 1514 m_storage->m_vector[i].set(globalData, this, values[i].first); 1515 1516 Heap::heap(this)->popTempSortVector(&values); 1517 1518 checkConsistency(SortConsistencyCheck); 702 703 checkIndexingConsistency(SortConsistencyCheck); 704 return; 705 } 706 707 default: 708 ASSERT_NOT_REACHED(); 709 } 1519 710 } 1520 711 … … 1598 789 void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData) 1599 790 { 1600 ASSERT(!inSparseMode()); 1601 1602 checkConsistency(); 1603 1604 // FIXME: This ignores exceptions raised in the compare function or in toNumber. 1605 1606 // The maximum tree depth is compiled in - but the caller is clearly up to no good 1607 // if a larger array is passed. 1608 ASSERT(m_storage->m_length <= static_cast<unsigned>(std::numeric_limits<int>::max())); 1609 if (m_storage->m_length > static_cast<unsigned>(std::numeric_limits<int>::max())) 1610 return; 1611 1612 unsigned usedVectorLength = min(m_storage->m_length, m_vectorLength); 1613 unsigned nodeCount = usedVectorLength + (m_sparseValueMap ? m_sparseValueMap->size() : 0); 1614 1615 if (!nodeCount) 1616 return; 1617 1618 AVLTree<AVLTreeAbstractorForArrayCompare, 44> tree; // Depth 44 is enough for 2^31 items 1619 tree.abstractor().m_exec = exec; 1620 tree.abstractor().m_compareFunction = compareFunction; 1621 tree.abstractor().m_compareCallType = callType; 1622 tree.abstractor().m_compareCallData = &callData; 1623 tree.abstractor().m_nodes.grow(nodeCount); 1624 1625 if (callType == CallTypeJS) 1626 tree.abstractor().m_cachedCall = adoptPtr(new CachedCall(exec, jsCast<JSFunction*>(compareFunction), 2)); 1627 1628 if (!tree.abstractor().m_nodes.begin()) { 1629 throwOutOfMemoryError(exec); 1630 return; 1631 } 1632 1633 // FIXME: If the compare function modifies the array, the vector, map, etc. could be modified 1634 // right out from under us while we're building the tree here. 1635 1636 unsigned numDefined = 0; 1637 unsigned numUndefined = 0; 1638 1639 // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree. 1640 for (; numDefined < usedVectorLength; ++numDefined) { 1641 JSValue v = m_storage->m_vector[numDefined].get(); 1642 if (!v || v.isUndefined()) 1643 break; 1644 tree.abstractor().m_nodes[numDefined].value = v; 1645 tree.insert(numDefined); 1646 } 1647 for (unsigned i = numDefined; i < usedVectorLength; ++i) { 1648 JSValue v = m_storage->m_vector[i].get(); 1649 if (v) { 1650 if (v.isUndefined()) 1651 ++numUndefined; 1652 else { 1653 tree.abstractor().m_nodes[numDefined].value = v; 791 ASSERT(!inSparseIndexingMode()); 792 793 switch (structure()->indexingType()) { 794 case Array: 795 return; 796 797 case ArrayWithArrayStorage: { 798 ArrayStorage* storage = m_butterfly->arrayStorage(); 799 checkIndexingConsistency(); 800 801 // FIXME: This ignores exceptions raised in the compare function or in toNumber. 802 803 // The maximum tree depth is compiled in - but the caller is clearly up to no good 804 // if a larger array is passed. 805 ASSERT(storage->length() <= static_cast<unsigned>(std::numeric_limits<int>::max())); 806 if (storage->length() > static_cast<unsigned>(std::numeric_limits<int>::max())) 807 return; 808 809 unsigned usedVectorLength = min(storage->length(), storage->vectorLength()); 810 unsigned nodeCount = usedVectorLength + (storage->m_sparseMap ? storage->m_sparseMap->size() : 0); 811 812 if (!nodeCount) 813 return; 814 815 AVLTree<AVLTreeAbstractorForArrayCompare, 44> tree; // Depth 44 is enough for 2^31 items 816 tree.abstractor().m_exec = exec; 817 tree.abstractor().m_compareFunction = compareFunction; 818 tree.abstractor().m_compareCallType = callType; 819 tree.abstractor().m_compareCallData = &callData; 820 tree.abstractor().m_nodes.grow(nodeCount); 821 822 if (callType == CallTypeJS) 823 tree.abstractor().m_cachedCall = adoptPtr(new CachedCall(exec, jsCast<JSFunction*>(compareFunction), 2)); 824 825 if (!tree.abstractor().m_nodes.begin()) { 826 throwOutOfMemoryError(exec); 827 return; 828 } 829 830 // FIXME: If the compare function modifies the array, the vector, map, etc. could be modified 831 // right out from under us while we're building the tree here. 832 833 unsigned numDefined = 0; 834 unsigned numUndefined = 0; 835 836 // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree. 837 for (; numDefined < usedVectorLength; ++numDefined) { 838 JSValue v = storage->m_vector[numDefined].get(); 839 if (!v || v.isUndefined()) 840 break; 841 tree.abstractor().m_nodes[numDefined].value = v; 842 tree.insert(numDefined); 843 } 844 for (unsigned i = numDefined; i < usedVectorLength; ++i) { 845 JSValue v = storage->m_vector[i].get(); 846 if (v) { 847 if (v.isUndefined()) 848 ++numUndefined; 849 else { 850 tree.abstractor().m_nodes[numDefined].value = v; 851 tree.insert(numDefined); 852 ++numDefined; 853 } 854 } 855 } 856 857 unsigned newUsedVectorLength = numDefined + numUndefined; 858 859 if (SparseArrayValueMap* map = storage->m_sparseMap.get()) { 860 newUsedVectorLength += map->size(); 861 if (newUsedVectorLength > storage->vectorLength()) { 862 // Check that it is possible to allocate an array large enough to hold all the entries. 863 if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(exec->globalData(), newUsedVectorLength)) { 864 throwOutOfMemoryError(exec); 865 return; 866 } 867 storage = m_butterfly->arrayStorage(); 868 } 869 870 SparseArrayValueMap::const_iterator end = map->end(); 871 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) { 872 tree.abstractor().m_nodes[numDefined].value = it->second.getNonSparseMode(); 1654 873 tree.insert(numDefined); 1655 874 ++numDefined; 1656 875 } 1657 } 1658 } 1659 1660 unsigned newUsedVectorLength = numDefined + numUndefined; 1661 1662 if (SparseArrayValueMap* map = m_sparseValueMap) { 1663 newUsedVectorLength += map->size(); 1664 if (newUsedVectorLength > m_vectorLength) { 1665 // Check that it is possible to allocate an array large enough to hold all the entries. 1666 if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(exec->globalData(), newUsedVectorLength)) { 1667 throwOutOfMemoryError(exec); 1668 return; 876 877 deallocateSparseIndexMap(); 878 } 879 880 ASSERT(tree.abstractor().m_nodes.size() >= numDefined); 881 882 // FIXME: If the compare function changed the length of the array, the following might be 883 // modifying the vector incorrectly. 884 885 // Copy the values back into m_storage. 886 AVLTree<AVLTreeAbstractorForArrayCompare, 44>::Iterator iter; 887 iter.start_iter_least(tree); 888 JSGlobalData& globalData = exec->globalData(); 889 for (unsigned i = 0; i < numDefined; ++i) { 890 storage->m_vector[i].set(globalData, this, tree.abstractor().m_nodes[*iter].value); 891 ++iter; 892 } 893 894 // Put undefined values back in. 895 for (unsigned i = numDefined; i < newUsedVectorLength; ++i) 896 storage->m_vector[i].setUndefined(); 897 898 // Ensure that unused values in the vector are zeroed out. 899 for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i) 900 storage->m_vector[i].clear(); 901 902 storage->m_numValuesInVector = newUsedVectorLength; 903 904 checkIndexingConsistency(SortConsistencyCheck); 905 return; 906 } 907 908 default: 909 ASSERT_NOT_REACHED(); 910 } 911 } 912 913 void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args) 914 { 915 switch (structure()->indexingType()) { 916 case Array: 917 return; 918 919 case ArrayWithArrayStorage: { 920 ArrayStorage* storage = m_butterfly->arrayStorage(); 921 922 WriteBarrier<Unknown>* vector = storage->m_vector; 923 unsigned vectorEnd = min(storage->length(), storage->vectorLength()); 924 unsigned i = 0; 925 for (; i < vectorEnd; ++i) { 926 WriteBarrier<Unknown>& v = vector[i]; 927 if (!v) 928 break; 929 args.append(v.get()); 930 } 931 932 for (; i < storage->length(); ++i) 933 args.append(get(exec, i)); 934 return; 935 } 936 937 default: 938 ASSERT_NOT_REACHED(); 939 } 940 } 941 942 void JSArray::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t length) 943 { 944 ASSERT(length == this->length()); 945 switch (structure()->indexingType()) { 946 case Array: 947 return; 948 949 case ArrayWithArrayStorage: { 950 ArrayStorage* storage = m_butterfly->arrayStorage(); 951 unsigned i = 0; 952 WriteBarrier<Unknown>* vector = storage->m_vector; 953 unsigned vectorEnd = min(length, storage->vectorLength()); 954 for (; i < vectorEnd; ++i) { 955 WriteBarrier<Unknown>& v = vector[i]; 956 if (!v) 957 break; 958 callFrame->setArgument(i, v.get()); 959 } 960 961 for (; i < length; ++i) 962 callFrame->setArgument(i, get(exec, i)); 963 return; 964 } 965 966 default: 967 ASSERT_NOT_REACHED(); 968 } 969 } 970 971 unsigned JSArray::compactForSorting(JSGlobalData& globalData) 972 { 973 ASSERT(!inSparseIndexingMode()); 974 975 checkIndexingConsistency(); 976 977 switch (structure()->indexingType()) { 978 case Array: 979 return 0; 980 981 case ArrayWithArrayStorage: { 982 ArrayStorage* storage = m_butterfly->arrayStorage(); 983 984 unsigned usedVectorLength = min(storage->length(), storage->vectorLength()); 985 986 unsigned numDefined = 0; 987 unsigned numUndefined = 0; 988 989 for (; numDefined < usedVectorLength; ++numDefined) { 990 JSValue v = storage->m_vector[numDefined].get(); 991 if (!v || v.isUndefined()) 992 break; 993 } 994 995 for (unsigned i = numDefined; i < usedVectorLength; ++i) { 996 JSValue v = storage->m_vector[i].get(); 997 if (v) { 998 if (v.isUndefined()) 999 ++numUndefined; 1000 else 1001 storage->m_vector[numDefined++].setWithoutWriteBarrier(v); 1669 1002 } 1670 1003 } 1671 1672 SparseArrayValueMap::const_iterator end = map->end(); 1673 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) { 1674 tree.abstractor().m_nodes[numDefined].value = it->second.getNonSparseMode(); 1675 tree.insert(numDefined); 1676 ++numDefined; 1677 } 1678 1679 deallocateSparseMap(); 1680 } 1681 1682 ASSERT(tree.abstractor().m_nodes.size() >= numDefined); 1683 1684 // FIXME: If the compare function changed the length of the array, the following might be 1685 // modifying the vector incorrectly. 1686 1687 // Copy the values back into m_storage. 1688 AVLTree<AVLTreeAbstractorForArrayCompare, 44>::Iterator iter; 1689 iter.start_iter_least(tree); 1690 JSGlobalData& globalData = exec->globalData(); 1691 for (unsigned i = 0; i < numDefined; ++i) { 1692 m_storage->m_vector[i].set(globalData, this, tree.abstractor().m_nodes[*iter].value); 1693 ++iter; 1694 } 1695 1696 // Put undefined values back in. 1697 for (unsigned i = numDefined; i < newUsedVectorLength; ++i) 1698 m_storage->m_vector[i].setUndefined(); 1699 1700 // Ensure that unused values in the vector are zeroed out. 1701 for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i) 1702 m_storage->m_vector[i].clear(); 1703 1704 m_storage->m_numValuesInVector = newUsedVectorLength; 1705 1706 checkConsistency(SortConsistencyCheck); 1707 } 1708 1709 void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args) 1710 { 1711 ArrayStorage* storage = m_storage; 1712 1713 WriteBarrier<Unknown>* vector = storage->m_vector; 1714 unsigned vectorEnd = min(storage->m_length, m_vectorLength); 1715 unsigned i = 0; 1716 for (; i < vectorEnd; ++i) { 1717 WriteBarrier<Unknown>& v = vector[i]; 1718 if (!v) 1719 break; 1720 args.append(v.get()); 1721 } 1722 1723 for (; i < storage->m_length; ++i) 1724 args.append(get(exec, i)); 1725 } 1726 1727 void JSArray::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t length) 1728 { 1729 ASSERT(length == this->length()); 1730 UNUSED_PARAM(length); 1731 unsigned i = 0; 1732 WriteBarrier<Unknown>* vector = m_storage->m_vector; 1733 unsigned vectorEnd = min(length, m_vectorLength); 1734 for (; i < vectorEnd; ++i) { 1735 WriteBarrier<Unknown>& v = vector[i]; 1736 if (!v) 1737 break; 1738 callFrame->setArgument(i, v.get()); 1739 } 1740 1741 for (; i < length; ++i) 1742 callFrame->setArgument(i, get(exec, i)); 1743 } 1744 1745 unsigned JSArray::compactForSorting(JSGlobalData& globalData) 1746 { 1747 ASSERT(!inSparseMode()); 1748 1749 checkConsistency(); 1750 1751 ArrayStorage* storage = m_storage; 1752 1753 unsigned usedVectorLength = min(storage->m_length, m_vectorLength); 1754 1755 unsigned numDefined = 0; 1756 unsigned numUndefined = 0; 1757 1758 for (; numDefined < usedVectorLength; ++numDefined) { 1759 JSValue v = storage->m_vector[numDefined].get(); 1760 if (!v || v.isUndefined()) 1761 break; 1762 } 1763 1764 for (unsigned i = numDefined; i < usedVectorLength; ++i) { 1765 JSValue v = storage->m_vector[i].get(); 1766 if (v) { 1767 if (v.isUndefined()) 1768 ++numUndefined; 1769 else 1770 storage->m_vector[numDefined++].setWithoutWriteBarrier(v); 1771 } 1772 } 1773 1774 unsigned newUsedVectorLength = numDefined + numUndefined; 1775 1776 if (SparseArrayValueMap* map = m_sparseValueMap) { 1777 newUsedVectorLength += map->size(); 1778 if (newUsedVectorLength > m_vectorLength) { 1779 // Check that it is possible to allocate an array large enough to hold all the entries - if not, 1780 // exception is thrown by caller. 1781 if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(globalData, newUsedVectorLength)) 1782 return 0; 1783 1784 storage = m_storage; 1785 } 1786 1787 SparseArrayValueMap::const_iterator end = map->end(); 1788 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) 1789 storage->m_vector[numDefined++].setWithoutWriteBarrier(it->second.getNonSparseMode()); 1790 1791 deallocateSparseMap(); 1792 } 1793 1794 for (unsigned i = numDefined; i < newUsedVectorLength; ++i) 1795 storage->m_vector[i].setUndefined(); 1796 for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i) 1797 storage->m_vector[i].clear(); 1798 1799 storage->m_numValuesInVector = newUsedVectorLength; 1800 1801 checkConsistency(SortConsistencyCheck); 1802 1803 return numDefined; 1804 } 1805 1806 #if CHECK_ARRAY_CONSISTENCY 1807 1808 void JSArray::checkConsistency(ConsistencyCheckType type) 1809 { 1810 ArrayStorage* storage = m_storage; 1811 1812 ASSERT(!storage->m_inCompactInitialization); 1813 1814 ASSERT(storage); 1815 if (type == SortConsistencyCheck) 1816 ASSERT(!m_sparseValueMap); 1817 1818 unsigned numValuesInVector = 0; 1819 for (unsigned i = 0; i < m_vectorLength; ++i) { 1820 if (JSValue value = storage->m_vector[i].get()) { 1821 ASSERT(i < storage->m_length); 1822 if (type != DestructorConsistencyCheck) 1823 value.isUndefined(); // Likely to crash if the object was deallocated. 1824 ++numValuesInVector; 1825 } else { 1826 if (type == SortConsistencyCheck) 1827 ASSERT(i >= storage->m_numValuesInVector); 1828 } 1829 } 1830 ASSERT(numValuesInVector == storage->m_numValuesInVector); 1831 ASSERT(numValuesInVector <= storage->m_length); 1832 1833 if (m_sparseValueMap) { 1834 SparseArrayValueMap::const_iterator end = m_sparseValueMap->end(); 1835 for (SparseArrayValueMap::const_iterator it = m_sparseValueMap->begin(); it != end; ++it) { 1836 unsigned index = it->first; 1837 ASSERT(index < storage->m_length); 1838 ASSERT(index >= m_vectorLength); 1839 ASSERT(index <= MAX_ARRAY_INDEX); 1840 ASSERT(it->second); 1841 if (type != DestructorConsistencyCheck) 1842 it->second.getNonSparseMode().isUndefined(); // Likely to crash if the object was deallocated. 1843 } 1844 } 1845 } 1846 1847 #endif 1004 1005 unsigned newUsedVectorLength = numDefined + numUndefined; 1006 1007 if (SparseArrayValueMap* map = storage->m_sparseMap.get()) { 1008 newUsedVectorLength += map->size(); 1009 if (newUsedVectorLength > storage->vectorLength()) { 1010 // Check that it is possible to allocate an array large enough to hold all the entries - if not, 1011 // exception is thrown by caller. 1012 if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(globalData, newUsedVectorLength)) 1013 return 0; 1014 1015 storage = m_butterfly->arrayStorage(); 1016 } 1017 1018 SparseArrayValueMap::const_iterator end = map->end(); 1019 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) 1020 storage->m_vector[numDefined++].setWithoutWriteBarrier(it->second.getNonSparseMode()); 1021 1022 deallocateSparseIndexMap(); 1023 } 1024 1025 for (unsigned i = numDefined; i < newUsedVectorLength; ++i) 1026 storage->m_vector[i].setUndefined(); 1027 for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i) 1028 storage->m_vector[i].clear(); 1029 1030 storage->m_numValuesInVector = newUsedVectorLength; 1031 1032 checkIndexingConsistency(SortConsistencyCheck); 1033 1034 return numDefined; 1035 } 1036 1037 default: 1038 ASSERT_NOT_REACHED(); 1039 return 0; 1040 } 1041 } 1042 1848 1043 1849 1044 } // namespace JSC
Note:
See TracChangeset
for help on using the changeset viewer.