Changeset 128400 in webkit for trunk/Source/JavaScriptCore/runtime/JSObject.cpp
- Timestamp:
- Sep 12, 2012, 9:18:52 PM (13 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/JavaScriptCore/runtime/JSObject.cpp
r127505 r128400 2 2 * Copyright (C) 1999-2001 Harri Porten ([email protected]) 3 3 * Copyright (C) 2001 Peter Kelly ([email protected]) 4 * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Apple Inc. All rights reserved.4 * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009, 2012 Apple Inc. All rights reserved. 5 5 * Copyright (C) 2007 Eric Seidel ([email protected]) 6 6 * … … 25 25 #include "JSObject.h" 26 26 27 #include "ButterflyInlineMethods.h" 27 28 #include "CopiedSpaceInlineMethods.h" 28 29 #include "DatePrototype.h" 29 30 #include "ErrorConstructor.h" 30 31 #include "GetterSetter.h" 32 #include "IndexingHeaderInlineMethods.h" 31 33 #include "JSFunction.h" 32 34 #include "JSGlobalObject.h" … … 39 41 #include "PropertyDescriptor.h" 40 42 #include "PropertyNameArray.h" 43 #include "Reject.h" 41 44 #include "SlotVisitorInlineMethods.h" 45 #include "SparseArrayValueMapInlineMethods.h" 42 46 #include <math.h> 43 47 #include <wtf/Assertions.h> 44 48 45 49 namespace JSC { 50 51 // We keep track of the size of the last array after it was grown. We use this 52 // as a simple heuristic for as the value to grow the next array from size 0. 53 // This value is capped by the constant FIRST_VECTOR_GROW defined above. 54 static unsigned lastArraySize = 0; 46 55 47 56 JSCell* getCallableObjectSlow(JSCell* cell) … … 87 96 } 88 97 89 ALWAYS_INLINE void JSObject::visitOutOfLineStorage(SlotVisitor& visitor, PropertyStorage storage, size_t storageSize) 90 { 91 ASSERT(storage); 92 ASSERT(storageSize); 93 94 size_t capacity = structure()->outOfLineCapacity(); 95 ASSERT(capacity); 96 size_t capacityInBytes = capacity * sizeof(WriteBarrierBase<Unknown>); 97 PropertyStorage baseOfStorage = storage - capacity - 1; 98 if (visitor.checkIfShouldCopyAndPinOtherwise(baseOfStorage, capacityInBytes)) { 99 PropertyStorage newBaseOfStorage = static_cast<PropertyStorage>(visitor.allocateNewSpace(capacityInBytes)); 100 PropertyStorage currentTarget = newBaseOfStorage + capacity; 101 PropertyStorage newStorage = currentTarget + 1; 102 PropertyStorage currentSource = storage - 1; 98 ALWAYS_INLINE void JSObject::visitButterfly(SlotVisitor& visitor, Butterfly* butterfly, size_t storageSize) 99 { 100 ASSERT(butterfly); 101 102 Structure* structure = this->structure(); 103 104 size_t propertyCapacity = structure->outOfLineCapacity(); 105 size_t preCapacity; 106 size_t indexingPayloadSizeInBytes; 107 bool hasIndexingHeader = JSC::hasIndexingHeader(structure->indexingType()); 108 if (UNLIKELY(hasIndexingHeader)) { 109 preCapacity = butterfly->indexingHeader()->preCapacity(structure); 110 indexingPayloadSizeInBytes = butterfly->indexingHeader()->indexingPayloadSizeInBytes(structure); 111 } else { 112 preCapacity = 0; 113 indexingPayloadSizeInBytes = 0; 114 } 115 size_t capacityInBytes = Butterfly::totalSize( 116 preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes); 117 if (visitor.checkIfShouldCopyAndPinOtherwise( 118 butterfly->base(preCapacity, propertyCapacity), capacityInBytes)) { 119 Butterfly* newButterfly = Butterfly::createUninitializedDuringCollection(visitor, preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes); 120 121 // Mark and copy the properties. 122 PropertyStorage currentTarget = newButterfly->propertyStorage(); 123 PropertyStorage currentSource = butterfly->propertyStorage(); 103 124 for (size_t count = storageSize; count--;) { 104 125 JSValue value = (--currentSource)->get(); … … 107 128 (--currentTarget)->setWithoutWriteBarrier(value); 108 129 } 109 m_outOfLineStorage.set(newStorage, StorageBarrier::Unchecked); 110 } else 111 visitor.appendValues(storage - storageSize - 1, storageSize); 130 131 if (UNLIKELY(hasIndexingHeader)) { 132 *newButterfly->indexingHeader() = *butterfly->indexingHeader(); 133 134 // Mark and copy the array if appropriate. 135 switch (structure->indexingType()) { 136 case ArrayWithArrayStorage: 137 case NonArrayWithArrayStorage: { 138 newButterfly->arrayStorage()->copyHeaderFromDuringGC(*butterfly->arrayStorage()); 139 WriteBarrier<Unknown>* currentTarget = newButterfly->arrayStorage()->m_vector; 140 WriteBarrier<Unknown>* currentSource = butterfly->arrayStorage()->m_vector; 141 for (size_t count = newButterfly->arrayStorage()->vectorLength(); count--;) { 142 JSValue value = (currentSource++)->get(); 143 if (value) 144 visitor.appendUnbarrieredValue(&value); 145 (currentTarget++)->setWithoutWriteBarrier(value); 146 } 147 if (newButterfly->arrayStorage()->m_sparseMap) 148 visitor.append(&newButterfly->arrayStorage()->m_sparseMap); 149 break; 150 } 151 default: 152 break; 153 } 154 } 155 156 m_butterfly = newButterfly; 157 } else { 158 // Mark the properties. 159 visitor.appendValues(butterfly->propertyStorage() - storageSize, storageSize); 160 161 // Mark the array if appropriate. 162 switch (structure->indexingType()) { 163 case ArrayWithArrayStorage: 164 case NonArrayWithArrayStorage: 165 visitor.appendValues(butterfly->arrayStorage()->m_vector, butterfly->arrayStorage()->vectorLength()); 166 if (butterfly->arrayStorage()->m_sparseMap) 167 visitor.append(&butterfly->arrayStorage()->m_sparseMap); 168 break; 169 default: 170 break; 171 } 172 } 112 173 } 113 174 … … 123 184 JSCell::visitChildren(thisObject, visitor); 124 185 125 PropertyStorage storage = thisObject->outOfLineStorage();126 if ( storage)127 thisObject->visit OutOfLineStorage(visitor, storage, thisObject->structure()->outOfLineSizeForKnownNonFinalObject());186 Butterfly* butterfly = thisObject->butterfly(); 187 if (butterfly) 188 thisObject->visitButterfly(visitor, butterfly, thisObject->structure()->outOfLineSizeForKnownNonFinalObject()); 128 189 129 190 #if !ASSERT_DISABLED … … 143 204 JSCell::visitChildren(thisObject, visitor); 144 205 145 PropertyStorage storage = thisObject->outOfLineStorage();146 if ( storage)147 thisObject->visit OutOfLineStorage(visitor, storage, thisObject->structure()->outOfLineSizeForKnownFinalObject());206 Butterfly* butterfly = thisObject->butterfly(); 207 if (butterfly) 208 thisObject->visitButterfly(visitor, butterfly, thisObject->structure()->outOfLineSizeForKnownFinalObject()); 148 209 149 210 size_t storageSize = thisObject->structure()->inlineSizeForKnownFinalObject(); … … 162 223 } 163 224 164 bool JSObject::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, PropertySlot& slot) 165 { 225 bool JSObject::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned i, PropertySlot& slot) 226 { 227 // NB. The fact that we're directly consulting our indexed storage implies that it is not 228 // legal for anyone to override getOwnPropertySlot() without also overriding 229 // getOwnPropertySlotByIndex(). 230 166 231 JSObject* thisObject = jsCast<JSObject*>(cell); 167 return thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, Identifier::from(exec, propertyName), slot); 232 233 if (i > MAX_ARRAY_INDEX) 234 return thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot); 235 236 switch (thisObject->structure()->indexingType()) { 237 case NonArray: 238 case Array: 239 break; 240 241 case NonArrayWithArrayStorage: 242 case ArrayWithArrayStorage: { 243 ArrayStorage* storage = thisObject->m_butterfly->arrayStorage(); 244 if (i >= storage->length()) 245 return false; 246 247 if (i < storage->vectorLength()) { 248 JSValue value = storage->m_vector[i].get(); 249 if (value) { 250 slot.setValue(value); 251 return true; 252 } 253 } else if (SparseArrayValueMap* map = storage->m_sparseMap.get()) { 254 SparseArrayValueMap::iterator it = map->find(i); 255 if (it != map->notFound()) { 256 it->second.get(slot); 257 return true; 258 } 259 } 260 break; 261 } 262 263 default: 264 ASSERT_NOT_REACHED(); 265 break; 266 } 267 268 return false; 168 269 } 169 270 … … 175 276 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(thisObject)); 176 277 JSGlobalData& globalData = exec->globalData(); 278 279 // Try indexed put first. This is required for correctness, since loads on property names that appear like 280 // valid indices will never look in the named property storage. 281 unsigned i = propertyName.asIndex(); 282 if (i != PropertyName::NotAnIndex) { 283 putByIndex(thisObject, exec, i, value, slot.isStrictMode()); 284 return; 285 } 177 286 178 287 // Check if there are any setters or getters in the prototype chain … … 236 345 void JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow) 237 346 { 238 PutPropertySlot slot(shouldThrow);239 347 JSObject* thisObject = jsCast<JSObject*>(cell); 240 thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, propertyName), value, slot); 348 thisObject->checkIndexingConsistency(); 349 350 if (UNLIKELY(propertyName > MAX_ARRAY_INDEX)) { 351 PutPropertySlot slot(shouldThrow); 352 thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, propertyName), value, slot); 353 return; 354 } 355 356 switch (thisObject->structure()->indexingType()) { 357 case NonArray: 358 case Array: 359 break; 360 361 case NonArrayWithArrayStorage: 362 case ArrayWithArrayStorage: { 363 ArrayStorage* storage = thisObject->m_butterfly->arrayStorage(); 364 365 if (propertyName >= storage->vectorLength()) 366 break; 367 368 WriteBarrier<Unknown>& valueSlot = storage->m_vector[propertyName]; 369 unsigned length = storage->length(); 370 371 // Update length & m_numValuesInVector as necessary. 372 if (propertyName >= length) { 373 length = propertyName + 1; 374 storage->setLength(length); 375 ++storage->m_numValuesInVector; 376 } else if (!valueSlot) 377 ++storage->m_numValuesInVector; 378 379 valueSlot.set(exec->globalData(), thisObject, value); 380 thisObject->checkIndexingConsistency(); 381 return; 382 } 383 384 default: 385 ASSERT_NOT_REACHED(); 386 } 387 388 thisObject->putByIndexBeyondVectorLength(exec, propertyName, value, shouldThrow); 389 thisObject->checkIndexingConsistency(); 390 } 391 392 ArrayStorage* JSObject::enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(JSGlobalData& globalData, ArrayStorage* storage) 393 { 394 SparseArrayValueMap* map = storage->m_sparseMap.get(); 395 396 if (!map) 397 map = allocateSparseIndexMap(globalData); 398 399 if (map->sparseMode()) 400 return storage; 401 402 map->setSparseMode(); 403 404 unsigned usedVectorLength = std::min(storage->length(), storage->vectorLength()); 405 for (unsigned i = 0; i < usedVectorLength; ++i) { 406 JSValue value = storage->m_vector[i].get(); 407 // This will always be a new entry in the map, so no need to check we can write, 408 // and attributes are default so no need to set them. 409 if (value) 410 map->add(this, i).iterator->second.set(globalData, this, value); 411 } 412 413 Butterfly* newButterfly = storage->butterfly()->resizeArray(globalData, structure(), 0, ArrayStorage::sizeFor(0)); 414 if (!newButterfly) 415 CRASH(); 416 417 m_butterfly = newButterfly; 418 newButterfly->arrayStorage()->m_indexBias = 0; 419 newButterfly->arrayStorage()->setVectorLength(0); 420 newButterfly->arrayStorage()->m_sparseMap.set(globalData, this, map); 421 422 return newButterfly->arrayStorage(); 423 } 424 425 void JSObject::enterDictionaryIndexingMode(JSGlobalData& globalData) 426 { 427 switch (structure()->indexingType()) { 428 case ArrayWithArrayStorage: 429 case NonArrayWithArrayStorage: 430 enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, m_butterfly->arrayStorage()); 431 break; 432 433 default: 434 break; 435 } 436 } 437 438 ArrayStorage* JSObject::createArrayStorage(JSGlobalData& globalData, unsigned length, unsigned vectorLength) 439 { 440 IndexingType oldType = structure()->indexingType(); 441 ASSERT_UNUSED(oldType, oldType == NonArray || oldType == Array); 442 Butterfly* newButterfly = m_butterfly->growArrayRight( 443 globalData, structure(), structure()->outOfLineCapacity(), false, 0, 444 ArrayStorage::sizeFor(vectorLength)); 445 if (!newButterfly) 446 CRASH(); 447 ArrayStorage* result = newButterfly->arrayStorage(); 448 result->setLength(length); 449 result->setVectorLength(vectorLength); 450 result->m_sparseMap.clear(); 451 result->m_numValuesInVector = 0; 452 result->m_indexBias = 0; 453 #if CHECK_ARRAY_CONSISTENCY 454 result->m_initializationIndex = 0; 455 result->m_inCompactInitialization = 0; 456 #endif 457 Structure* newStructure = Structure::nonPropertyTransition(globalData, structure(), AllocateArrayStorage); 458 setButterfly(globalData, newButterfly, newStructure); 459 return result; 460 } 461 462 ArrayStorage* JSObject::createInitialArrayStorage(JSGlobalData& globalData) 463 { 464 return createArrayStorage(globalData, 0, BASE_VECTOR_LEN); 465 } 466 467 ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(JSGlobalData& globalData) 468 { 469 switch (structure()->indexingType()) { 470 case ArrayWithArrayStorage: 471 case NonArrayWithArrayStorage: 472 return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, m_butterfly->arrayStorage()); 473 474 case Array: 475 case NonArray: { 476 createArrayStorage(globalData, 0, 0); 477 SparseArrayValueMap* map = allocateSparseIndexMap(globalData); 478 map->setSparseMode(); 479 return arrayStorage(); 480 } 481 482 default: 483 ASSERT_NOT_REACHED(); 484 return 0; 485 } 241 486 } 242 487 … … 270 515 } 271 516 272 void JSObject::putDirectAccessor( JSGlobalData& globalData, PropertyName propertyName, JSValue value, unsigned attributes)517 void JSObject::putDirectAccessor(ExecState* exec, PropertyName propertyName, JSValue value, unsigned attributes) 273 518 { 274 519 ASSERT(value.isGetterSetter() && (attributes & Accessor)); 520 521 unsigned index = propertyName.asIndex(); 522 if (index != PropertyName::NotAnIndex) { 523 putDirectIndex(exec, index, value, attributes, PutDirectIndexLikePutDirect); 524 return; 525 } 526 527 JSGlobalData& globalData = exec->globalData(); 275 528 276 529 PutPropertySlot slot; … … 305 558 { 306 559 JSObject* thisObject = jsCast<JSObject*>(cell); 560 561 unsigned i = propertyName.asIndex(); 562 if (i != PropertyName::NotAnIndex) 563 return thisObject->methodTable()->deletePropertyByIndex(thisObject, exec, i); 307 564 308 565 if (!thisObject->staticFunctionsReified()) … … 333 590 } 334 591 335 bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned propertyName)592 bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i) 336 593 { 337 594 JSObject* thisObject = jsCast<JSObject*>(cell); 338 return thisObject->methodTable()->deleteProperty(thisObject, exec, Identifier::from(exec, propertyName)); 595 596 if (i > MAX_ARRAY_INDEX) 597 return thisObject->methodTable()->deleteProperty(thisObject, exec, Identifier::from(exec, i)); 598 599 switch (thisObject->structure()->indexingType()) { 600 case Array: 601 case NonArray: 602 return true; 603 604 case ArrayWithArrayStorage: 605 case NonArrayWithArrayStorage: { 606 ArrayStorage* storage = thisObject->m_butterfly->arrayStorage(); 607 608 if (i < storage->vectorLength()) { 609 WriteBarrier<Unknown>& valueSlot = storage->m_vector[i]; 610 if (valueSlot) { 611 valueSlot.clear(); 612 --storage->m_numValuesInVector; 613 } 614 } else if (SparseArrayValueMap* map = storage->m_sparseMap.get()) { 615 SparseArrayValueMap::iterator it = map->find(i); 616 if (it != map->notFound()) { 617 if (it->second.attributes & DontDelete) 618 return false; 619 map->remove(it); 620 } 621 } 622 623 thisObject->checkIndexingConsistency(); 624 return true; 625 } 626 627 default: 628 ASSERT_NOT_REACHED(); 629 return false; 630 } 339 631 } 340 632 … … 466 758 void JSObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) 467 759 { 760 // Add numeric properties first. That appears to be the accepted convention. 761 // FIXME: Filling PropertyNameArray with an identifier for every integer 762 // is incredibly inefficient for large arrays. We need a different approach, 763 // which almost certainly means a different structure for PropertyNameArray. 764 switch (object->structure()->indexingType()) { 765 case NonArray: 766 case Array: 767 break; 768 769 case NonArrayWithArrayStorage: 770 case ArrayWithArrayStorage: { 771 ArrayStorage* storage = object->m_butterfly->arrayStorage(); 772 773 unsigned usedVectorLength = std::min(storage->length(), storage->vectorLength()); 774 for (unsigned i = 0; i < usedVectorLength; ++i) { 775 if (storage->m_vector[i]) 776 propertyNames.add(Identifier::from(exec, i)); 777 } 778 779 if (SparseArrayValueMap* map = storage->m_sparseMap.get()) { 780 Vector<unsigned> keys; 781 keys.reserveCapacity(map->size()); 782 783 SparseArrayValueMap::const_iterator end = map->end(); 784 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) { 785 if (mode == IncludeDontEnumProperties || !(it->second.attributes & DontEnum)) 786 keys.append(static_cast<unsigned>(it->first)); 787 } 788 789 std::sort(keys.begin(), keys.end()); 790 for (unsigned i = 0; i < keys.size(); ++i) 791 propertyNames.add(Identifier::from(exec, keys[i])); 792 } 793 break; 794 } 795 796 default: 797 ASSERT_NOT_REACHED(); 798 } 799 800 object->methodTable()->getOwnNonIndexPropertyNames(object, exec, propertyNames, mode); 801 } 802 803 void JSObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) 804 { 468 805 getClassPropertyNames(exec, object->classInfo(), propertyNames, mode, object->staticFunctionsReified()); 469 806 object->structure()->getPropertyNamesFromStructure(exec->globalData(), propertyNames, mode); … … 516 853 void JSObject::preventExtensions(JSGlobalData& globalData) 517 854 { 518 if (isJSArray(this)) 519 asArray(this)->enterDictionaryMode(globalData); 855 enterDictionaryIndexingMode(globalData); 520 856 if (isExtensible()) 521 857 setStructure(globalData, Structure::preventExtensionsTransition(globalData, structure())); … … 574 910 } 575 911 576 NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, WriteBarrierBase<Unknown>* location)577 { 578 if (JSObject* getterFunction = asGetterSetter( location->get())->getter()) {912 NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, PropertyOffset offset) 913 { 914 if (JSObject* getterFunction = asGetterSetter(getDirectOffset(offset))->getter()) { 579 915 if (!structure()->isDictionary()) 580 slot.setCacheableGetterSlot(this, getterFunction, offset ForLocation(location));916 slot.setCacheableGetterSlot(this, getterFunction, offset); 581 917 else 582 918 slot.setGetterSlot(getterFunction); … … 604 940 } 605 941 606 PropertyStorage JSObject::growOutOfLineStorage(JSGlobalData& globalData, size_t oldSize, size_t newSize) 942 void JSObject::putIndexedDescriptor(ExecState* exec, SparseArrayEntry* entryInMap, PropertyDescriptor& descriptor, PropertyDescriptor& oldDescriptor) 943 { 944 if (descriptor.isDataDescriptor()) { 945 if (descriptor.value()) 946 entryInMap->set(exec->globalData(), this, descriptor.value()); 947 else if (oldDescriptor.isAccessorDescriptor()) 948 entryInMap->set(exec->globalData(), this, jsUndefined()); 949 entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~Accessor; 950 return; 951 } 952 953 if (descriptor.isAccessorDescriptor()) { 954 JSObject* getter = 0; 955 if (descriptor.getterPresent()) 956 getter = descriptor.getterObject(); 957 else if (oldDescriptor.isAccessorDescriptor()) 958 getter = oldDescriptor.getterObject(); 959 JSObject* setter = 0; 960 if (descriptor.setterPresent()) 961 setter = descriptor.setterObject(); 962 else if (oldDescriptor.isAccessorDescriptor()) 963 setter = oldDescriptor.setterObject(); 964 965 GetterSetter* accessor = GetterSetter::create(exec); 966 if (getter) 967 accessor->setGetter(exec->globalData(), getter); 968 if (setter) 969 accessor->setSetter(exec->globalData(), setter); 970 971 entryInMap->set(exec->globalData(), this, accessor); 972 entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~ReadOnly; 973 return; 974 } 975 976 ASSERT(descriptor.isGenericDescriptor()); 977 entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor); 978 } 979 980 // Defined in ES5.1 8.12.9 981 bool JSObject::defineOwnIndexedProperty(ExecState* exec, unsigned index, PropertyDescriptor& descriptor, bool throwException) 982 { 983 ASSERT(index != 0xFFFFFFFF); 984 985 if (!inSparseIndexingMode()) { 986 // Fast case: we're putting a regular property to a regular array 987 // FIXME: this will pessimistically assume that if attributes are missing then they'll default to false 988 // however if the property currently exists missing attributes will override from their current 'true' 989 // state (i.e. defineOwnProperty could be used to set a value without needing to entering 'SparseMode'). 990 if (!descriptor.attributes()) { 991 ASSERT(!descriptor.isAccessorDescriptor()); 992 return putDirectIndex(exec, index, descriptor.value(), 0, throwException ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow); 993 } 994 995 ensureArrayStorageExistsAndEnterDictionaryIndexingMode(exec->globalData()); 996 } 997 998 SparseArrayValueMap* map = m_butterfly->arrayStorage()->m_sparseMap.get(); 999 ASSERT(map); 1000 1001 // 1. Let current be the result of calling the [[GetOwnProperty]] internal method of O with property name P. 1002 SparseArrayValueMap::AddResult result = map->add(this, index); 1003 SparseArrayEntry* entryInMap = &result.iterator->second; 1004 1005 // 2. Let extensible be the value of the [[Extensible]] internal property of O. 1006 // 3. If current is undefined and extensible is false, then Reject. 1007 // 4. If current is undefined and extensible is true, then 1008 if (result.isNewEntry) { 1009 if (!isExtensible()) { 1010 map->remove(result.iterator); 1011 return reject(exec, throwException, "Attempting to define property on object that is not extensible."); 1012 } 1013 1014 // 4.a. If IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then create an own data property 1015 // named P of object O whose [[Value]], [[Writable]], [[Enumerable]] and [[Configurable]] attribute values 1016 // are described by Desc. If the value of an attribute field of Desc is absent, the attribute of the newly 1017 // created property is set to its default value. 1018 // 4.b. Else, Desc must be an accessor Property Descriptor so, create an own accessor property named P of 1019 // object O whose [[Get]], [[Set]], [[Enumerable]] and [[Configurable]] attribute values are described by 1020 // Desc. If the value of an attribute field of Desc is absent, the attribute of the newly created property 1021 // is set to its default value. 1022 // 4.c. Return true. 1023 1024 PropertyDescriptor defaults; 1025 entryInMap->setWithoutWriteBarrier(jsUndefined()); 1026 entryInMap->attributes = DontDelete | DontEnum | ReadOnly; 1027 entryInMap->get(defaults); 1028 1029 putIndexedDescriptor(exec, entryInMap, descriptor, defaults); 1030 if (index >= m_butterfly->arrayStorage()->length()) 1031 m_butterfly->arrayStorage()->setLength(index + 1); 1032 return true; 1033 } 1034 1035 // 5. Return true, if every field in Desc is absent. 1036 // 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). 1037 PropertyDescriptor current; 1038 entryInMap->get(current); 1039 if (descriptor.isEmpty() || descriptor.equalTo(exec, current)) 1040 return true; 1041 1042 // 7. If the [[Configurable]] field of current is false then 1043 if (!current.configurable()) { 1044 // 7.a. Reject, if the [[Configurable]] field of Desc is true. 1045 if (descriptor.configurablePresent() && descriptor.configurable()) 1046 return reject(exec, throwException, "Attempting to change configurable attribute of unconfigurable property."); 1047 // 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. 1048 if (descriptor.enumerablePresent() && current.enumerable() != descriptor.enumerable()) 1049 return reject(exec, throwException, "Attempting to change enumerable attribute of unconfigurable property."); 1050 } 1051 1052 // 8. If IsGenericDescriptor(Desc) is true, then no further validation is required. 1053 if (!descriptor.isGenericDescriptor()) { 1054 // 9. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) have different results, then 1055 if (current.isDataDescriptor() != descriptor.isDataDescriptor()) { 1056 // 9.a. Reject, if the [[Configurable]] field of current is false. 1057 if (!current.configurable()) 1058 return reject(exec, throwException, "Attempting to change access mechanism for an unconfigurable property."); 1059 // 9.b. If IsDataDescriptor(current) is true, then convert the property named P of object O from a 1060 // data property to an accessor property. Preserve the existing values of the converted property's 1061 // [[Configurable]] and [[Enumerable]] attributes and set the rest of the property's attributes to 1062 // their default values. 1063 // 9.c. Else, convert the property named P of object O from an accessor property to a data property. 1064 // Preserve the existing values of the converted property's [[Configurable]] and [[Enumerable]] 1065 // attributes and set the rest of the property's attributes to their default values. 1066 } else if (current.isDataDescriptor() && descriptor.isDataDescriptor()) { 1067 // 10. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true, then 1068 // 10.a. If the [[Configurable]] field of current is false, then 1069 if (!current.configurable() && !current.writable()) { 1070 // 10.a.i. Reject, if the [[Writable]] field of current is false and the [[Writable]] field of Desc is true. 1071 if (descriptor.writable()) 1072 return reject(exec, throwException, "Attempting to change writable attribute of unconfigurable property."); 1073 // 10.a.ii. If the [[Writable]] field of current is false, then 1074 // 10.a.ii.1. Reject, if the [[Value]] field of Desc is present and SameValue(Desc.[[Value]], current.[[Value]]) is false. 1075 if (descriptor.value() && !sameValue(exec, descriptor.value(), current.value())) 1076 return reject(exec, throwException, "Attempting to change value of a readonly property."); 1077 } 1078 // 10.b. else, the [[Configurable]] field of current is true, so any change is acceptable. 1079 } else { 1080 ASSERT(current.isAccessorDescriptor() && current.getterPresent() && current.setterPresent()); 1081 // 11. Else, IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc) are both true so, if the [[Configurable]] field of current is false, then 1082 if (!current.configurable()) { 1083 // 11.i. Reject, if the [[Set]] field of Desc is present and SameValue(Desc.[[Set]], current.[[Set]]) is false. 1084 if (descriptor.setterPresent() && descriptor.setter() != current.setter()) 1085 return reject(exec, throwException, "Attempting to change the setter of an unconfigurable property."); 1086 // 11.ii. Reject, if the [[Get]] field of Desc is present and SameValue(Desc.[[Get]], current.[[Get]]) is false. 1087 if (descriptor.getterPresent() && descriptor.getter() != current.getter()) 1088 return reject(exec, throwException, "Attempting to change the getter of an unconfigurable property."); 1089 } 1090 } 1091 } 1092 1093 // 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. 1094 putIndexedDescriptor(exec, entryInMap, descriptor, current); 1095 // 13. Return true. 1096 return true; 1097 } 1098 1099 SparseArrayValueMap* JSObject::allocateSparseIndexMap(JSGlobalData& globalData) 1100 { 1101 SparseArrayValueMap* result = SparseArrayValueMap::create(globalData); 1102 arrayStorage()->m_sparseMap.set(globalData, this, result); 1103 return result; 1104 } 1105 1106 void JSObject::deallocateSparseIndexMap() 1107 { 1108 if (ArrayStorage* arrayStorage = arrayStorageOrNull()) 1109 arrayStorage->m_sparseMap.clear(); 1110 } 1111 1112 void JSObject::putByIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, bool shouldThrow, ArrayStorage* storage) 1113 { 1114 JSGlobalData& globalData = exec->globalData(); 1115 1116 // i should be a valid array index that is outside of the current vector. 1117 ASSERT(i <= MAX_ARRAY_INDEX); 1118 ASSERT(i >= storage->vectorLength()); 1119 1120 SparseArrayValueMap* map = storage->m_sparseMap.get(); 1121 1122 // First, handle cases where we don't currently have a sparse map. 1123 if (LIKELY(!map)) { 1124 // If the array is not extensible, we should have entered dictionary mode, and created the spare map. 1125 ASSERT(isExtensible()); 1126 1127 // Update m_length if necessary. 1128 if (i >= storage->length()) 1129 storage->setLength(i + 1); 1130 1131 // Check that it is sensible to still be using a vector, and then try to grow the vector. 1132 if (LIKELY((isDenseEnoughForVector(i, storage->m_numValuesInVector)) && increaseVectorLength(globalData, i + 1))) { 1133 // success! - reread m_storage since it has likely been reallocated, and store to the vector. 1134 storage = arrayStorage(); 1135 storage->m_vector[i].set(globalData, this, value); 1136 ++storage->m_numValuesInVector; 1137 return; 1138 } 1139 // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value. 1140 map = allocateSparseIndexMap(exec->globalData()); 1141 map->putEntry(exec, this, i, value, shouldThrow); 1142 return; 1143 } 1144 1145 // Update m_length if necessary. 1146 unsigned length = storage->length(); 1147 if (i >= length) { 1148 // Prohibit growing the array if length is not writable. 1149 if (map->lengthIsReadOnly() || !isExtensible()) { 1150 if (shouldThrow) 1151 throwTypeError(exec, StrictModeReadonlyPropertyWriteError); 1152 return; 1153 } 1154 length = i + 1; 1155 storage->setLength(length); 1156 } 1157 1158 // We are currently using a map - check whether we still want to be doing so. 1159 // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails. 1160 unsigned numValuesInArray = storage->m_numValuesInVector + map->size(); 1161 if (map->sparseMode() || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(exec->globalData(), length)) { 1162 map->putEntry(exec, this, i, value, shouldThrow); 1163 return; 1164 } 1165 1166 // Reread m_storage after increaseVectorLength, update m_numValuesInVector. 1167 storage = arrayStorage(); 1168 storage->m_numValuesInVector = numValuesInArray; 1169 1170 // Copy all values from the map into the vector, and delete the map. 1171 WriteBarrier<Unknown>* vector = storage->m_vector; 1172 SparseArrayValueMap::const_iterator end = map->end(); 1173 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) 1174 vector[it->first].set(globalData, this, it->second.getNonSparseMode()); 1175 deallocateSparseIndexMap(); 1176 1177 // Store the new property into the vector. 1178 WriteBarrier<Unknown>& valueSlot = vector[i]; 1179 if (!valueSlot) 1180 ++storage->m_numValuesInVector; 1181 valueSlot.set(globalData, this, value); 1182 } 1183 1184 void JSObject::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, bool shouldThrow) 1185 { 1186 JSGlobalData& globalData = exec->globalData(); 1187 1188 // i should be a valid array index that is outside of the current vector. 1189 ASSERT(i <= MAX_ARRAY_INDEX); 1190 1191 switch (structure()->indexingType()) { 1192 case NonArray: 1193 case Array: { 1194 if (indexingShouldBeSparse()) { 1195 putByIndexBeyondVectorLengthWithArrayStorage(exec, i, value, shouldThrow, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(globalData)); 1196 break; 1197 } 1198 if (!isDenseEnoughForVector(i, 0) || i >= MAX_STORAGE_VECTOR_LENGTH) { 1199 putByIndexBeyondVectorLengthWithArrayStorage(exec, i, value, shouldThrow, createArrayStorage(globalData, 0, 0)); 1200 break; 1201 } 1202 1203 ArrayStorage* storage = createArrayStorage(globalData, i + 1, getNewVectorLength(0, 0, i + 1)); 1204 storage->m_vector[i].set(globalData, this, value); 1205 storage->m_numValuesInVector = 1; 1206 break; 1207 } 1208 1209 case NonArrayWithArrayStorage: 1210 case ArrayWithArrayStorage: 1211 putByIndexBeyondVectorLengthWithArrayStorage(exec, i, value, shouldThrow, arrayStorage()); 1212 break; 1213 1214 default: 1215 ASSERT_NOT_REACHED(); 1216 } 1217 } 1218 1219 bool JSObject::putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode, ArrayStorage* storage) 1220 { 1221 JSGlobalData& globalData = exec->globalData(); 1222 1223 // i should be a valid array index that is outside of the current vector. 1224 ASSERT(i >= storage->vectorLength() || attributes); 1225 ASSERT(i <= MAX_ARRAY_INDEX); 1226 1227 SparseArrayValueMap* map = storage->m_sparseMap.get(); 1228 1229 // First, handle cases where we don't currently have a sparse map. 1230 if (LIKELY(!map)) { 1231 // If the array is not extensible, we should have entered dictionary mode, and created the spare map. 1232 ASSERT(isExtensible()); 1233 1234 // Update m_length if necessary. 1235 if (i >= storage->length()) 1236 storage->setLength(i + 1); 1237 1238 // Check that it is sensible to still be using a vector, and then try to grow the vector. 1239 if (LIKELY( 1240 !attributes 1241 && (isDenseEnoughForVector(i, storage->m_numValuesInVector)) 1242 && increaseVectorLength(globalData, i + 1))) { 1243 // success! - reread m_storage since it has likely been reallocated, and store to the vector. 1244 storage = arrayStorage(); 1245 storage->m_vector[i].set(globalData, this, value); 1246 ++storage->m_numValuesInVector; 1247 return true; 1248 } 1249 // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value. 1250 map = allocateSparseIndexMap(exec->globalData()); 1251 return map->putDirect(exec, this, i, value, attributes, mode); 1252 } 1253 1254 // Update m_length if necessary. 1255 unsigned length = storage->length(); 1256 if (i >= length) { 1257 if (mode != PutDirectIndexLikePutDirect) { 1258 // Prohibit growing the array if length is not writable. 1259 if (map->lengthIsReadOnly()) 1260 return reject(exec, mode == PutDirectIndexShouldThrow, StrictModeReadonlyPropertyWriteError); 1261 if (!isExtensible()) 1262 return reject(exec, mode == PutDirectIndexShouldThrow, "Attempting to define property on object that is not extensible."); 1263 } 1264 length = i + 1; 1265 storage->setLength(length); 1266 } 1267 1268 // We are currently using a map - check whether we still want to be doing so. 1269 // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails. 1270 unsigned numValuesInArray = storage->m_numValuesInVector + map->size(); 1271 if (map->sparseMode() || attributes || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(exec->globalData(), length)) 1272 return map->putDirect(exec, this, i, value, attributes, mode); 1273 1274 // Reread m_storage after increaseVectorLength, update m_numValuesInVector. 1275 storage = arrayStorage(); 1276 storage->m_numValuesInVector = numValuesInArray; 1277 1278 // Copy all values from the map into the vector, and delete the map. 1279 WriteBarrier<Unknown>* vector = storage->m_vector; 1280 SparseArrayValueMap::const_iterator end = map->end(); 1281 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) 1282 vector[it->first].set(globalData, this, it->second.getNonSparseMode()); 1283 deallocateSparseIndexMap(); 1284 1285 // Store the new property into the vector. 1286 WriteBarrier<Unknown>& valueSlot = vector[i]; 1287 if (!valueSlot) 1288 ++storage->m_numValuesInVector; 1289 valueSlot.set(globalData, this, value); 1290 return true; 1291 } 1292 1293 bool JSObject::putDirectIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode) 1294 { 1295 JSGlobalData& globalData = exec->globalData(); 1296 1297 // i should be a valid array index that is outside of the current vector. 1298 ASSERT(i <= MAX_ARRAY_INDEX); 1299 1300 switch (structure()->indexingType()) { 1301 case NonArray: 1302 case Array: { 1303 if (indexingShouldBeSparse() || attributes) 1304 return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(globalData)); 1305 if (!isDenseEnoughForVector(i, 0) || i >= MAX_STORAGE_VECTOR_LENGTH) 1306 return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, createArrayStorage(globalData, 0, 0)); 1307 1308 ArrayStorage* storage = createArrayStorage(globalData, i + 1, getNewVectorLength(0, 0, i + 1)); 1309 storage->m_vector[i].set(globalData, this, value); 1310 storage->m_numValuesInVector = 1; 1311 return true; 1312 } 1313 1314 case NonArrayWithArrayStorage: 1315 case ArrayWithArrayStorage: 1316 return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, arrayStorage()); 1317 1318 default: 1319 ASSERT_NOT_REACHED(); 1320 return false; 1321 } 1322 } 1323 1324 ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned currentVectorLength, unsigned currentLength, unsigned desiredLength) 1325 { 1326 ASSERT(desiredLength <= MAX_STORAGE_VECTOR_LENGTH); 1327 1328 unsigned increasedLength; 1329 unsigned maxInitLength = std::min(currentLength, 100000U); 1330 1331 if (desiredLength < maxInitLength) 1332 increasedLength = maxInitLength; 1333 else if (!currentVectorLength) 1334 increasedLength = std::max(desiredLength, lastArraySize); 1335 else { 1336 // Mathematically equivalent to: 1337 // increasedLength = (newLength * 3 + 1) / 2; 1338 // or: 1339 // increasedLength = (unsigned)ceil(newLength * 1.5)); 1340 // This form is not prone to internal overflow. 1341 increasedLength = desiredLength + (desiredLength >> 1) + (desiredLength & 1); 1342 } 1343 1344 ASSERT(increasedLength >= desiredLength); 1345 1346 lastArraySize = std::min(increasedLength, FIRST_VECTOR_GROW); 1347 1348 return std::min(increasedLength, MAX_STORAGE_VECTOR_LENGTH); 1349 } 1350 1351 ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned desiredLength) 1352 { 1353 unsigned vectorLength; 1354 unsigned length; 1355 1356 switch (structure()->indexingType()) { 1357 case NonArray: 1358 case Array: 1359 vectorLength = 0; 1360 length = 0; 1361 break; 1362 case NonArrayWithArrayStorage: 1363 case ArrayWithArrayStorage: 1364 vectorLength = m_butterfly->arrayStorage()->vectorLength(); 1365 length = m_butterfly->arrayStorage()->length(); 1366 break; 1367 default: 1368 CRASH(); 1369 return 0; 1370 } 1371 return getNewVectorLength(vectorLength, length, desiredLength); 1372 } 1373 1374 bool JSObject::increaseVectorLength(JSGlobalData& globalData, unsigned newLength) 1375 { 1376 // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map 1377 // to the vector. Callers have to account for that, because they can do it more efficiently. 1378 if (newLength > MAX_STORAGE_VECTOR_LENGTH) 1379 return false; 1380 1381 ArrayStorage* storage = arrayStorage(); 1382 1383 unsigned indexBias = storage->m_indexBias; 1384 unsigned vectorLength = storage->vectorLength(); 1385 ASSERT(newLength > vectorLength); 1386 unsigned newVectorLength = getNewVectorLength(newLength); 1387 1388 // Fast case - there is no precapacity. In these cases a realloc makes sense. 1389 if (LIKELY(!indexBias)) { 1390 Butterfly* newButterfly = storage->butterfly()->growArrayRight(globalData, structure(), structure()->outOfLineCapacity(), true, ArrayStorage::sizeFor(vectorLength), ArrayStorage::sizeFor(newVectorLength)); 1391 if (!newButterfly) 1392 return false; 1393 m_butterfly = newButterfly; 1394 newButterfly->arrayStorage()->setVectorLength(newVectorLength); 1395 return true; 1396 } 1397 1398 // Remove some, but not all of the precapacity. Atomic decay, & capped to not overflow array length. 1399 unsigned newIndexBias = std::min(indexBias >> 1, MAX_STORAGE_VECTOR_LENGTH - newVectorLength); 1400 Butterfly* newButterfly = storage->butterfly()->resizeArray( 1401 globalData, 1402 structure()->outOfLineCapacity(), true, ArrayStorage::sizeFor(vectorLength), 1403 newIndexBias, true, ArrayStorage::sizeFor(newVectorLength)); 1404 if (!newButterfly) 1405 return false; 1406 1407 m_butterfly = newButterfly; 1408 newButterfly->arrayStorage()->setVectorLength(newVectorLength); 1409 newButterfly->arrayStorage()->m_indexBias = newIndexBias; 1410 return true; 1411 } 1412 1413 #if CHECK_ARRAY_CONSISTENCY 1414 void JSObject::checkIndexingConsistency(ConsistencyCheckType type) 1415 { 1416 ArrayStorage* storage = arrayStorageOrNull(); 1417 if (!storage) 1418 return; 1419 1420 ASSERT(!storage->m_inCompactInitialization); 1421 1422 ASSERT(storage); 1423 if (type == SortConsistencyCheck) 1424 ASSERT(!storage->m_sparseMap); 1425 1426 unsigned numValuesInVector = 0; 1427 for (unsigned i = 0; i < storage->vectorLength(); ++i) { 1428 if (JSValue value = storage->m_vector[i].get()) { 1429 ASSERT(i < storage->length()); 1430 if (type != DestructorConsistencyCheck) 1431 value.isUndefined(); // Likely to crash if the object was deallocated. 1432 ++numValuesInVector; 1433 } else { 1434 if (type == SortConsistencyCheck) 1435 ASSERT(i >= storage->m_numValuesInVector); 1436 } 1437 } 1438 ASSERT(numValuesInVector == storage->m_numValuesInVector); 1439 ASSERT(numValuesInVector <= storage->length()); 1440 1441 if (m_sparseValueMap) { 1442 SparseArrayValueMap::const_iterator end = m_sparseValueMap->end(); 1443 for (SparseArrayValueMap::const_iterator it = m_sparseValueMap->begin(); it != end; ++it) { 1444 unsigned index = it->first; 1445 ASSERT(index < storage->length()); 1446 ASSERT(index >= storage->vectorLength()); 1447 ASSERT(index <= MAX_ARRAY_INDEX); 1448 ASSERT(it->second); 1449 if (type != DestructorConsistencyCheck) 1450 it->second.getNonSparseMode().isUndefined(); // Likely to crash if the object was deallocated. 1451 } 1452 } 1453 } 1454 #endif // CHECK_ARRAY_CONSISTENCY 1455 1456 Butterfly* JSObject::growOutOfLineStorage(JSGlobalData& globalData, size_t oldSize, size_t newSize) 607 1457 { 608 1458 ASSERT(newSize > oldSize); 609 1459 610 // It's important that this function not rely on structure(), since 611 // we might be in the middle of a transition. 612 613 PropertyStorage oldPropertyStorage = m_outOfLineStorage.get(); 614 PropertyStorage newPropertyStorage = 0; 615 616 // We have this extra temp here to slake GCC's thirst for the blood of those who dereference type-punned pointers. 617 void* temp = newPropertyStorage; 618 if (!globalData.heap.tryAllocateStorage(sizeof(WriteBarrierBase<Unknown>) * newSize, &temp)) 619 CRASH(); 620 newPropertyStorage = static_cast<PropertyStorage>(temp) + newSize + 1; 621 622 memcpy(newPropertyStorage - oldSize - 1, oldPropertyStorage - oldSize - 1, sizeof(WriteBarrierBase<Unknown>) * oldSize); 623 624 ASSERT(newPropertyStorage); 625 return newPropertyStorage; 1460 // It's important that this function not rely on structure(), for the property 1461 // capacity, since we might have already mutated the structure in-place. 1462 1463 return m_butterfly->growPropertyStorage(globalData, structure(), oldSize, newSize); 626 1464 } 627 1465 … … 631 1469 JSCell* cell = 0; 632 1470 PropertyOffset offset = object->structure()->get(exec->globalData(), propertyName, attributes, cell); 633 if (offset == invalidOffset) 1471 if (isValidOffset(offset)) { 1472 descriptor.setDescriptor(object->getDirectOffset(offset), attributes); 1473 return true; 1474 } 1475 1476 unsigned i = propertyName.asIndex(); 1477 if (i == PropertyName::NotAnIndex) 634 1478 return false; 635 descriptor.setDescriptor(object->getDirectOffset(offset), attributes); 636 return true; 1479 1480 switch (object->structure()->indexingType()) { 1481 case NonArray: 1482 case Array: 1483 return false; 1484 1485 case NonArrayWithArrayStorage: 1486 case ArrayWithArrayStorage: { 1487 ArrayStorage* storage = object->m_butterfly->arrayStorage(); 1488 if (i >= storage->length()) 1489 return false; 1490 if (i < storage->vectorLength()) { 1491 WriteBarrier<Unknown>& value = storage->m_vector[i]; 1492 if (!value) 1493 return false; 1494 descriptor.setDescriptor(value.get(), 0); 1495 return true; 1496 } 1497 if (SparseArrayValueMap* map = storage->m_sparseMap.get()) { 1498 SparseArrayValueMap::iterator it = map->find(i); 1499 if (it == map->notFound()) 1500 return false; 1501 it->second.get(descriptor); 1502 return true; 1503 } 1504 return false; 1505 } 1506 1507 default: 1508 ASSERT_NOT_REACHED(); 1509 return false; 1510 } 637 1511 } 638 1512 … … 659 1533 if (oldDescriptor.setterPresent()) 660 1534 accessor->setSetter(exec->globalData(), oldDescriptor.setterObject()); 661 target->putDirectAccessor(exec ->globalData(), propertyName, accessor, attributes | Accessor);1535 target->putDirectAccessor(exec, propertyName, accessor, attributes | Accessor); 662 1536 return true; 663 1537 } … … 684 1558 accessor->setSetter(exec->globalData(), oldDescriptor.setterObject()); 685 1559 686 target->putDirectAccessor(exec ->globalData(), propertyName, accessor, attributes | Accessor);1560 target->putDirectAccessor(exec, propertyName, accessor, attributes | Accessor); 687 1561 return true; 1562 } 1563 1564 void JSObject::putDirectMayBeIndex(ExecState* exec, PropertyName propertyName, JSValue value) 1565 { 1566 unsigned asIndex = propertyName.asIndex(); 1567 if (asIndex == PropertyName::NotAnIndex) 1568 putDirect(exec->globalData(), propertyName, value); 1569 else 1570 putDirectIndex(exec, asIndex, value); 688 1571 } 689 1572 … … 705 1588 }; 706 1589 707 bool JSObject::defineOwn Property(JSObject* object,ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor, bool throwException)1590 bool JSObject::defineOwnNonIndexProperty(ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor, bool throwException) 708 1591 { 709 1592 // Track on the globaldata that we're in define property. … … 712 1595 // DontDelete) properties to be deleted. For now, we can use this flag to make this work. 713 1596 DefineOwnPropertyScope scope(exec); 714 1597 715 1598 // If we have a new property we can just put it on normally 716 1599 PropertyDescriptor current; 717 if (! object->methodTable()->getOwnPropertyDescriptor(object, exec, propertyName, current)) {1600 if (!methodTable()->getOwnPropertyDescriptor(this, exec, propertyName, current)) { 718 1601 // unless extensions are prevented! 719 if (! object->isExtensible()) {1602 if (!isExtensible()) { 720 1603 if (throwException) 721 1604 throwError(exec, createTypeError(exec, ASCIILiteral("Attempting to define property on object that is not extensible."))); … … 724 1607 PropertyDescriptor oldDescriptor; 725 1608 oldDescriptor.setValue(jsUndefined()); 726 return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributes(), oldDescriptor);1609 return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributes(), oldDescriptor); 727 1610 } 728 1611 … … 750 1633 if (descriptor.isGenericDescriptor()) { 751 1634 if (!current.attributesEqual(descriptor)) { 752 object->methodTable()->deleteProperty(object, exec, propertyName);753 return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);1635 methodTable()->deleteProperty(this, exec, propertyName); 1636 return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current); 754 1637 } 755 1638 return true; … … 763 1646 return false; 764 1647 } 765 object->methodTable()->deleteProperty(object, exec, propertyName);766 return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);1648 methodTable()->deleteProperty(this, exec, propertyName); 1649 return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current); 767 1650 } 768 1651 … … 785 1668 if (current.attributesEqual(descriptor) && !descriptor.value()) 786 1669 return true; 787 object->methodTable()->deleteProperty(object, exec, propertyName);788 return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);1670 methodTable()->deleteProperty(this, exec, propertyName); 1671 return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current); 789 1672 } 790 1673 … … 803 1686 } 804 1687 } 805 JSValue accessor = object->getDirect(exec->globalData(), propertyName);1688 JSValue accessor = getDirect(exec->globalData(), propertyName); 806 1689 if (!accessor) 807 1690 return false; … … 813 1696 if (current.attributesEqual(descriptor)) 814 1697 return true; 815 object->methodTable()->deleteProperty(object, exec, propertyName);1698 methodTable()->deleteProperty(this, exec, propertyName); 816 1699 unsigned attrs = descriptor.attributesOverridingCurrent(current); 817 object->putDirectAccessor(exec->globalData(), propertyName, getterSetter, attrs | Accessor);1700 putDirectAccessor(exec, propertyName, getterSetter, attrs | Accessor); 818 1701 return true; 819 1702 } 820 1703 1704 bool JSObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor, bool throwException) 1705 { 1706 // If it's an array index, then use the indexed property storage. 1707 unsigned index = propertyName.asIndex(); 1708 if (index != PropertyName::NotAnIndex) { 1709 // 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. 1710 // d. Reject if succeeded is false. 1711 // e. If index >= oldLen 1712 // e.i. Set oldLenDesc.[[Value]] to index + 1. 1713 // 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. 1714 // f. Return true. 1715 return object->defineOwnIndexedProperty(exec, index, descriptor, throwException); 1716 } 1717 1718 return object->defineOwnNonIndexProperty(exec, propertyName, descriptor, throwException); 1719 } 1720 1721 bool JSObject::getOwnPropertySlotSlow(ExecState* exec, PropertyName propertyName, PropertySlot& slot) 1722 { 1723 unsigned i = propertyName.asIndex(); 1724 if (i != PropertyName::NotAnIndex) 1725 return getOwnPropertySlotByIndex(this, exec, i, slot); 1726 return false; 1727 } 1728 821 1729 JSObject* throwTypeError(ExecState* exec, const String& message) 822 1730 {
Note:
See TracChangeset
for help on using the changeset viewer.