diff options
author | Simon Hausmann <simon.hausmann@digia.com> | 2012-10-17 16:21:14 +0200 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@digia.com> | 2012-10-17 16:21:14 +0200 |
commit | 8995b83bcbfbb68245f779b64e5517627c6cc6ea (patch) | |
tree | 17985605dab9263cc2444bd4d45f189e142cca7c /Source/JavaScriptCore/runtime/JSObject.cpp | |
parent | b9c9652036d5e9f1e29c574f40bc73a35c81ace6 (diff) | |
download | qtwebkit-8995b83bcbfbb68245f779b64e5517627c6cc6ea.tar.gz |
Imported WebKit commit cf4f8fc6f19b0629f51860cb2d4b25e139d07e00 (http://svn.webkit.org/repository/webkit/trunk@131592)
New snapshot that includes the build fixes for Mac OS X 10.6 and earlier as well
as the previously cherry-picked changes
Diffstat (limited to 'Source/JavaScriptCore/runtime/JSObject.cpp')
-rw-r--r-- | Source/JavaScriptCore/runtime/JSObject.cpp | 473 |
1 files changed, 389 insertions, 84 deletions
diff --git a/Source/JavaScriptCore/runtime/JSObject.cpp b/Source/JavaScriptCore/runtime/JSObject.cpp index bf38f6876..6a3fb84e4 100644 --- a/Source/JavaScriptCore/runtime/JSObject.cpp +++ b/Source/JavaScriptCore/runtime/JSObject.cpp @@ -26,13 +26,14 @@ #include "ButterflyInlineMethods.h" #include "CopiedSpaceInlineMethods.h" +#include "CopyVisitor.h" +#include "CopyVisitorInlineMethods.h" #include "DatePrototype.h" #include "ErrorConstructor.h" #include "GetterSetter.h" #include "IndexingHeaderInlineMethods.h" #include "JSFunction.h" #include "JSGlobalObject.h" -#include "JSGlobalThis.h" #include "Lookup.h" #include "NativeErrorConstructor.h" #include "Nodes.h" @@ -63,10 +64,6 @@ JSCell* getCallableObjectSlow(JSCell* cell) return 0; } -ASSERT_CLASS_FITS_IN_CELL(JSObject); -ASSERT_CLASS_FITS_IN_CELL(JSNonFinalObject); -ASSERT_CLASS_FITS_IN_CELL(JSFinalObject); - ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSObject); ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSFinalObject); @@ -95,7 +92,7 @@ static inline void getClassPropertyNames(ExecState* exec, const ClassInfo* class } } -ALWAYS_INLINE void JSObject::visitButterfly(SlotVisitor& visitor, Butterfly* butterfly, size_t storageSize) +ALWAYS_INLINE void JSObject::copyButterfly(CopyVisitor& visitor, Butterfly* butterfly, size_t storageSize) { ASSERT(butterfly); @@ -112,61 +109,93 @@ ALWAYS_INLINE void JSObject::visitButterfly(SlotVisitor& visitor, Butterfly* but preCapacity = 0; indexingPayloadSizeInBytes = 0; } - size_t capacityInBytes = Butterfly::totalSize( - preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes); - if (visitor.checkIfShouldCopyAndPinOtherwise( - butterfly->base(preCapacity, propertyCapacity), capacityInBytes)) { + size_t capacityInBytes = Butterfly::totalSize(preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes); + if (visitor.checkIfShouldCopy(butterfly->base(preCapacity, propertyCapacity), capacityInBytes)) { Butterfly* newButterfly = Butterfly::createUninitializedDuringCollection(visitor, preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes); - // Mark and copy the properties. + // Copy the properties. PropertyStorage currentTarget = newButterfly->propertyStorage(); PropertyStorage currentSource = butterfly->propertyStorage(); - for (size_t count = storageSize; count--;) { - JSValue value = (--currentSource)->get(); - ASSERT(value); - visitor.appendUnbarrieredValue(&value); - (--currentTarget)->setWithoutWriteBarrier(value); - } + for (size_t count = storageSize; count--;) + (--currentTarget)->setWithoutWriteBarrier((--currentSource)->get()); if (UNLIKELY(hasIndexingHeader)) { *newButterfly->indexingHeader() = *butterfly->indexingHeader(); - // Mark and copy the array if appropriate. + // Copy the array if appropriate. + + WriteBarrier<Unknown>* currentTarget; + WriteBarrier<Unknown>* currentSource; + size_t count; + switch (structure->indexingType()) { + case ALL_CONTIGUOUS_INDEXING_TYPES: { + currentTarget = newButterfly->contiguous(); + currentSource = butterfly->contiguous(); + ASSERT(newButterfly->publicLength() <= newButterfly->vectorLength()); + count = newButterfly->vectorLength(); + break; + } + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { newButterfly->arrayStorage()->copyHeaderFromDuringGC(*butterfly->arrayStorage()); - WriteBarrier<Unknown>* currentTarget = newButterfly->arrayStorage()->m_vector; - WriteBarrier<Unknown>* currentSource = butterfly->arrayStorage()->m_vector; - for (size_t count = newButterfly->arrayStorage()->vectorLength(); count--;) { - JSValue value = (currentSource++)->get(); - if (value) - visitor.appendUnbarrieredValue(&value); - (currentTarget++)->setWithoutWriteBarrier(value); - } - if (newButterfly->arrayStorage()->m_sparseMap) - visitor.append(&newButterfly->arrayStorage()->m_sparseMap); + currentTarget = newButterfly->arrayStorage()->m_vector; + currentSource = butterfly->arrayStorage()->m_vector; + count = newButterfly->arrayStorage()->vectorLength(); break; } default: + CRASH(); + currentTarget = 0; + currentSource = 0; + count = 0; break; } + + while (count--) + (currentTarget++)->setWithoutWriteBarrier((currentSource++)->get()); } m_butterfly = newButterfly; + visitor.didCopy(butterfly->base(preCapacity, propertyCapacity), capacityInBytes); + } +} + +ALWAYS_INLINE void JSObject::visitButterfly(SlotVisitor& visitor, Butterfly* butterfly, size_t storageSize) +{ + ASSERT(butterfly); + + Structure* structure = this->structure(); + + size_t propertyCapacity = structure->outOfLineCapacity(); + size_t preCapacity; + size_t indexingPayloadSizeInBytes; + bool hasIndexingHeader = JSC::hasIndexingHeader(structure->indexingType()); + if (UNLIKELY(hasIndexingHeader)) { + preCapacity = butterfly->indexingHeader()->preCapacity(structure); + indexingPayloadSizeInBytes = butterfly->indexingHeader()->indexingPayloadSizeInBytes(structure); } else { - // Mark the properties. - visitor.appendValues(butterfly->propertyStorage() - storageSize, storageSize); - - // Mark the array if appropriate. - switch (structure->indexingType()) { - case ALL_ARRAY_STORAGE_INDEXING_TYPES: - visitor.appendValues(butterfly->arrayStorage()->m_vector, butterfly->arrayStorage()->vectorLength()); - if (butterfly->arrayStorage()->m_sparseMap) - visitor.append(&butterfly->arrayStorage()->m_sparseMap); - break; - default: - break; - } + preCapacity = 0; + indexingPayloadSizeInBytes = 0; + } + size_t capacityInBytes = Butterfly::totalSize(preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes); + + // Mark the properties. + visitor.appendValues(butterfly->propertyStorage() - storageSize, storageSize); + visitor.copyLater(butterfly->base(preCapacity, propertyCapacity), capacityInBytes); + + // Mark the array if appropriate. + switch (structure->indexingType()) { + case ALL_CONTIGUOUS_INDEXING_TYPES: + visitor.appendValues(butterfly->contiguous(), butterfly->publicLength()); + break; + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + visitor.appendValues(butterfly->arrayStorage()->m_vector, butterfly->arrayStorage()->vectorLength()); + if (butterfly->arrayStorage()->m_sparseMap) + visitor.append(&butterfly->arrayStorage()->m_sparseMap); + break; + default: + break; } } @@ -183,13 +212,23 @@ void JSObject::visitChildren(JSCell* cell, SlotVisitor& visitor) Butterfly* butterfly = thisObject->butterfly(); if (butterfly) - thisObject->visitButterfly(visitor, butterfly, thisObject->structure()->outOfLineSizeForKnownNonFinalObject()); + thisObject->visitButterfly(visitor, butterfly, thisObject->structure()->outOfLineSize()); #if !ASSERT_DISABLED visitor.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation; #endif } +void JSObject::copyBackingStore(JSCell* cell, CopyVisitor& visitor) +{ + JSObject* thisObject = jsCast<JSObject*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); + + Butterfly* butterfly = thisObject->butterfly(); + if (butterfly) + thisObject->copyButterfly(visitor, butterfly, thisObject->structure()->outOfLineSize()); +} + void JSFinalObject::visitChildren(JSCell* cell, SlotVisitor& visitor) { JSFinalObject* thisObject = jsCast<JSFinalObject*>(cell); @@ -203,9 +242,9 @@ void JSFinalObject::visitChildren(JSCell* cell, SlotVisitor& visitor) Butterfly* butterfly = thisObject->butterfly(); if (butterfly) - thisObject->visitButterfly(visitor, butterfly, thisObject->structure()->outOfLineSizeForKnownFinalObject()); + thisObject->visitButterfly(visitor, butterfly, thisObject->structure()->outOfLineSize()); - size_t storageSize = thisObject->structure()->inlineSizeForKnownFinalObject(); + size_t storageSize = thisObject->structure()->inlineSize(); visitor.appendValues(thisObject->inlineStorage(), storageSize); #if !ASSERT_DISABLED @@ -235,6 +274,20 @@ bool JSObject::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned case ALL_BLANK_INDEXING_TYPES: break; + case ALL_CONTIGUOUS_INDEXING_TYPES: { + Butterfly* butterfly = thisObject->m_butterfly; + if (i >= butterfly->vectorLength()) + return false; + + JSValue value = butterfly->contiguous()[i].get(); + if (value) { + slot.setValue(value); + return true; + } + + return false; + } + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = thisObject->m_butterfly->arrayStorage(); if (i >= storage->length()) @@ -249,7 +302,7 @@ bool JSObject::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned } else if (SparseArrayValueMap* map = storage->m_sparseMap.get()) { SparseArrayValueMap::iterator it = map->find(i); if (it != map->notFound()) { - it->second.get(slot); + it->value.get(slot); return true; } } @@ -352,6 +405,16 @@ void JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, case ALL_BLANK_INDEXING_TYPES: break; + case ALL_CONTIGUOUS_INDEXING_TYPES: { + Butterfly* butterfly = thisObject->m_butterfly; + if (propertyName >= butterfly->vectorLength()) + break; + butterfly->contiguous()[propertyName].set(exec->globalData(), thisObject, value); + if (propertyName >= butterfly->publicLength()) + butterfly->setPublicLength(propertyName + 1); + return; + } + case NonArrayWithArrayStorage: case ArrayWithArrayStorage: { ArrayStorage* storage = thisObject->m_butterfly->arrayStorage(); @@ -426,7 +489,7 @@ ArrayStorage* JSObject::enterDictionaryIndexingModeWhenArrayStorageAlreadyExists // This will always be a new entry in the map, so no need to check we can write, // and attributes are default so no need to set them. if (value) - map->add(this, i).iterator->second.set(globalData, this, value); + map->add(this, i).iterator->value.set(globalData, this, value); } Butterfly* newButterfly = storage->butterfly()->resizeArray(globalData, structure(), 0, ArrayStorage::sizeFor(0)); @@ -444,6 +507,11 @@ ArrayStorage* JSObject::enterDictionaryIndexingModeWhenArrayStorageAlreadyExists void JSObject::enterDictionaryIndexingMode(JSGlobalData& globalData) { switch (structure()->indexingType()) { + case ALL_CONTIGUOUS_INDEXING_TYPES: + // NOTE: this is horribly inefficient, as it will perform two conversions. We could optimize + // this case if we ever cared. + enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, convertContiguousToArrayStorage(globalData)); + break; case ALL_ARRAY_STORAGE_INDEXING_TYPES: enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, m_butterfly->arrayStorage()); break; @@ -466,6 +534,24 @@ void JSObject::notifyPresenceOfIndexedAccessors(JSGlobalData& globalData) globalObject()->haveABadTime(globalData); } +WriteBarrier<Unknown>* JSObject::createInitialContiguous(JSGlobalData& globalData, unsigned length) +{ + ASSERT(length < MAX_ARRAY_INDEX); + IndexingType oldType = structure()->indexingType(); + ASSERT_UNUSED(oldType, !hasIndexedProperties(oldType)); + ASSERT(!structure()->needsSlowPutIndexing()); + ASSERT(!indexingShouldBeSparse()); + unsigned vectorLength = std::max(length, BASE_VECTOR_LEN); + Butterfly* newButterfly = m_butterfly->growArrayRight( + globalData, structure(), structure()->outOfLineCapacity(), false, 0, + sizeof(EncodedJSValue) * vectorLength); + newButterfly->setPublicLength(length); + newButterfly->setVectorLength(vectorLength); + Structure* newStructure = Structure::nonPropertyTransition(globalData, structure(), AllocateContiguous); + setButterfly(globalData, newButterfly, newStructure); + return newButterfly->contiguous(); +} + ArrayStorage* JSObject::createArrayStorage(JSGlobalData& globalData, unsigned length, unsigned vectorLength) { IndexingType oldType = structure()->indexingType(); @@ -481,7 +567,7 @@ ArrayStorage* JSObject::createArrayStorage(JSGlobalData& globalData, unsigned le result->m_sparseMap.clear(); result->m_numValuesInVector = 0; result->m_indexBias = 0; - Structure* newStructure = Structure::nonPropertyTransition(globalData, structure(), structure()->suggestedIndexingTransition()); + Structure* newStructure = Structure::nonPropertyTransition(globalData, structure(), structure()->suggestedArrayStorageTransition()); setButterfly(globalData, newButterfly, newStructure); return result; } @@ -491,9 +577,107 @@ ArrayStorage* JSObject::createInitialArrayStorage(JSGlobalData& globalData) return createArrayStorage(globalData, 0, BASE_VECTOR_LEN); } +ArrayStorage* JSObject::convertContiguousToArrayStorage(JSGlobalData& globalData, NonPropertyTransition transition, unsigned neededLength) +{ + ASSERT(hasContiguous(structure()->indexingType())); + + unsigned publicLength = m_butterfly->publicLength(); + unsigned propertyCapacity = structure()->outOfLineCapacity(); + unsigned propertySize = structure()->outOfLineSize(); + + Butterfly* newButterfly = Butterfly::createUninitialized( + globalData, 0, propertyCapacity, true, ArrayStorage::sizeFor(neededLength)); + + memcpy( + newButterfly->propertyStorage() - propertySize, + m_butterfly->propertyStorage() - propertySize, + propertySize * sizeof(EncodedJSValue)); + + ArrayStorage* newStorage = newButterfly->arrayStorage(); + newStorage->setVectorLength(neededLength); + newStorage->setLength(publicLength); + newStorage->m_sparseMap.clear(); + newStorage->m_indexBias = 0; + newStorage->m_numValuesInVector = 0; + for (unsigned i = publicLength; i--;) { + JSValue v = m_butterfly->contiguous()[i].get(); + if (!v) + continue; + newStorage->m_vector[i].setWithoutWriteBarrier(v); + newStorage->m_numValuesInVector++; + } + + Structure* newStructure = Structure::nonPropertyTransition(globalData, structure(), transition); + setButterfly(globalData, newButterfly, newStructure); + return newStorage; +} + +ArrayStorage* JSObject::convertContiguousToArrayStorage(JSGlobalData& globalData, NonPropertyTransition transition) +{ + return convertContiguousToArrayStorage(globalData, transition, m_butterfly->vectorLength()); +} + +ArrayStorage* JSObject::convertContiguousToArrayStorage(JSGlobalData& globalData) +{ + return convertContiguousToArrayStorage(globalData, structure()->suggestedArrayStorageTransition()); +} + +WriteBarrier<Unknown>* JSObject::ensureContiguousSlow(JSGlobalData& globalData) +{ + switch (structure()->indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + if (UNLIKELY(indexingShouldBeSparse() || structure()->needsSlowPutIndexing())) + return 0; + return createInitialContiguous(globalData, 0); + + default: + ASSERT_NOT_REACHED(); + return 0; + } +} + +ArrayStorage* JSObject::ensureArrayStorageSlow(JSGlobalData& globalData) +{ + switch (structure()->indexingType()) { + case ALL_CONTIGUOUS_INDEXING_TYPES: + ASSERT(!indexingShouldBeSparse()); + ASSERT(!structure()->needsSlowPutIndexing()); + return convertContiguousToArrayStorage(globalData); + + case ALL_BLANK_INDEXING_TYPES: + if (UNLIKELY(indexingShouldBeSparse())) + return ensureArrayStorageExistsAndEnterDictionaryIndexingMode(globalData); + return createInitialArrayStorage(globalData); + + default: + ASSERT_NOT_REACHED(); + return 0; + } +} + +Butterfly* JSObject::ensureIndexedStorageSlow(JSGlobalData& globalData) +{ + switch (structure()->indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + if (UNLIKELY(structure()->needsSlowPutIndexing())) + return createInitialArrayStorage(globalData)->butterfly(); + if (UNLIKELY(indexingShouldBeSparse())) + return ensureArrayStorageExistsAndEnterDictionaryIndexingMode(globalData)->butterfly(); + return Butterfly::fromContiguous(createInitialContiguous(globalData, 0)); + + default: + ASSERT_NOT_REACHED(); + return 0; + } +} + ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(JSGlobalData& globalData) { switch (structure()->indexingType()) { + case ALL_CONTIGUOUS_INDEXING_TYPES: + // FIXME: This could be made way more efficient, if we cared. + return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, convertContiguousToArrayStorage(globalData)); + case ALL_ARRAY_STORAGE_INDEXING_TYPES: return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, m_butterfly->arrayStorage()); @@ -513,6 +697,11 @@ ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(J void JSObject::switchToSlowPutArrayStorage(JSGlobalData& globalData) { switch (structure()->indexingType()) { + case ALL_CONTIGUOUS_INDEXING_TYPES: { + convertContiguousToArrayStorage(globalData, AllocateSlowPutArrayStorage); + break; + } + case NonArrayWithArrayStorage: case ArrayWithArrayStorage: { Structure* newStructure = Structure::nonPropertyTransition(globalData, structure(), SwitchToSlowPutArrayStorage); @@ -556,8 +745,7 @@ void JSObject::setPrototype(JSGlobalData& globalData, JSValue prototype) if (shouldUseSlowPut(structure()->indexingType())) return; - newStructure = Structure::nonPropertyTransition(globalData, newStructure, SwitchToSlowPutArrayStorage); - setStructure(globalData, newStructure); + switchToSlowPutArrayStorage(globalData); } bool JSObject::setPrototypeWithCycleCheck(JSGlobalData& globalData, JSValue prototype) @@ -691,6 +879,14 @@ bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i) case ALL_BLANK_INDEXING_TYPES: return true; + case ALL_CONTIGUOUS_INDEXING_TYPES: { + Butterfly* butterfly = thisObject->m_butterfly; + if (i >= butterfly->vectorLength()) + return true; + butterfly->contiguous()[i].clear(); + return true; + } + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = thisObject->m_butterfly->arrayStorage(); @@ -703,7 +899,7 @@ bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i) } else if (SparseArrayValueMap* map = storage->m_sparseMap.get()) { SparseArrayValueMap::iterator it = map->find(i); if (it != map->notFound()) { - if (it->second.attributes & DontDelete) + if (it->value.attributes & DontDelete) return false; map->remove(it); } @@ -864,6 +1060,17 @@ void JSObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNa case ALL_BLANK_INDEXING_TYPES: break; + case ALL_CONTIGUOUS_INDEXING_TYPES: { + Butterfly* butterfly = object->m_butterfly; + unsigned usedLength = butterfly->publicLength(); + for (unsigned i = 0; i < usedLength; ++i) { + if (!butterfly->contiguous()[i]) + continue; + propertyNames.add(Identifier::from(exec, i)); + } + break; + } + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = object->m_butterfly->arrayStorage(); @@ -879,8 +1086,8 @@ void JSObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNa SparseArrayValueMap::const_iterator end = map->end(); for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) { - if (mode == IncludeDontEnumProperties || !(it->second.attributes & DontEnum)) - keys.append(static_cast<unsigned>(it->first)); + if (mode == IncludeDontEnumProperties || !(it->value.attributes & DontEnum)) + keys.append(static_cast<unsigned>(it->key)); } std::sort(keys.begin(), keys.end()); @@ -1089,9 +1296,6 @@ bool JSObject::defineOwnIndexedProperty(ExecState* exec, unsigned index, Propert { ASSERT(index <= MAX_ARRAY_INDEX); - if (descriptor.attributes() & (ReadOnly | Accessor)) - notifyPresenceOfIndexedAccessors(exec->globalData()); - if (!inSparseIndexingMode()) { // Fast case: we're putting a regular property to a regular array // FIXME: this will pessimistically assume that if attributes are missing then they'll default to false @@ -1101,16 +1305,19 @@ bool JSObject::defineOwnIndexedProperty(ExecState* exec, unsigned index, Propert ASSERT(!descriptor.isAccessorDescriptor()); return putDirectIndex(exec, index, descriptor.value(), 0, throwException ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow); } - + ensureArrayStorageExistsAndEnterDictionaryIndexingMode(exec->globalData()); } + if (descriptor.attributes() & (ReadOnly | Accessor)) + notifyPresenceOfIndexedAccessors(exec->globalData()); + SparseArrayValueMap* map = m_butterfly->arrayStorage()->m_sparseMap.get(); ASSERT(map); // 1. Let current be the result of calling the [[GetOwnProperty]] internal method of O with property name P. SparseArrayValueMap::AddResult result = map->add(this, index); - SparseArrayEntry* entryInMap = &result.iterator->second; + SparseArrayEntry* entryInMap = &result.iterator->value; // 2. Let extensible be the value of the [[Extensible]] internal property of O. // 3. If current is undefined and extensible is false, then Reject. @@ -1230,8 +1437,8 @@ bool JSObject::attemptToInterceptPutByIndexOnHoleForPrototype(ExecState* exec, J ArrayStorage* storage = current->arrayStorageOrNull(); if (storage && storage->m_sparseMap) { SparseArrayValueMap::iterator iter = storage->m_sparseMap->find(i); - if (iter != storage->m_sparseMap->notFound() && (iter->second.attributes & (Accessor | ReadOnly))) { - iter->second.put(exec, thisValue, storage->m_sparseMap.get(), value, shouldThrow); + if (iter != storage->m_sparseMap->notFound() && (iter->value.attributes & (Accessor | ReadOnly))) { + iter->value.put(exec, thisValue, storage->m_sparseMap.get(), value, shouldThrow); return true; } } @@ -1253,6 +1460,35 @@ bool JSObject::attemptToInterceptPutByIndexOnHole(ExecState* exec, unsigned i, J return asObject(prototypeValue)->attemptToInterceptPutByIndexOnHoleForPrototype(exec, this, i, value, shouldThrow); } +void JSObject::putByIndexBeyondVectorLengthContiguousWithoutAttributes(ExecState* exec, unsigned i, JSValue value) +{ + ASSERT(hasContiguous(structure()->indexingType())); + ASSERT(!indexingShouldBeSparse()); + + // For us to get here, the index is either greater than the public length, or greater than + // or equal to the vector length. + ASSERT(i >= m_butterfly->vectorLength()); + + JSGlobalData& globalData = exec->globalData(); + + if (i >= MAX_ARRAY_INDEX - 1 + || (i >= MIN_SPARSE_ARRAY_INDEX + && !isDenseEnoughForVector(i, countElementsInContiguous(m_butterfly)))) { + ASSERT(i <= MAX_ARRAY_INDEX); + convertContiguousToArrayStorage(globalData, AllocateArrayStorage); + SparseArrayValueMap* map = allocateSparseIndexMap(globalData); + map->putEntry(exec, this, i, value, false); + ASSERT(i >= arrayStorage()->length()); + arrayStorage()->setLength(i + 1); + return; + } + + ensureContiguousLength(globalData, i + 1); + + ASSERT(i < m_butterfly->vectorLength()); + m_butterfly->contiguous()[i].set(globalData, this, value); +} + void JSObject::putByIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, bool shouldThrow, ArrayStorage* storage) { JSGlobalData& globalData = exec->globalData(); @@ -1315,7 +1551,7 @@ void JSObject::putByIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, uns WriteBarrier<Unknown>* vector = storage->m_vector; SparseArrayValueMap::const_iterator end = map->end(); for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) - vector[it->first].set(globalData, this, it->second.getNonSparseMode()); + vector[it->key].set(globalData, this, it->value.getNonSparseMode()); deallocateSparseIndexMap(); // Store the new property into the vector. @@ -1335,17 +1571,29 @@ void JSObject::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: { if (indexingShouldBeSparse()) { - putByIndexBeyondVectorLengthWithArrayStorage(exec, i, value, shouldThrow, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(globalData)); + putByIndexBeyondVectorLengthWithArrayStorage( + exec, i, value, shouldThrow, + ensureArrayStorageExistsAndEnterDictionaryIndexingMode(globalData)); + break; + } + if (i >= MIN_SPARSE_ARRAY_INDEX) { + putByIndexBeyondVectorLengthWithArrayStorage( + exec, i, value, shouldThrow, createArrayStorage(globalData, 0, 0)); break; } - if (!isDenseEnoughForVector(i, 0) || i >= MAX_STORAGE_VECTOR_LENGTH) { - putByIndexBeyondVectorLengthWithArrayStorage(exec, i, value, shouldThrow, createArrayStorage(globalData, 0, 0)); + if (structure()->needsSlowPutIndexing()) { + ArrayStorage* storage = createArrayStorage(globalData, i + 1, getNewVectorLength(0, 0, i + 1)); + storage->m_vector[i].set(globalData, this, value); + storage->m_numValuesInVector++; break; } + + createInitialContiguous(globalData, i + 1)[i].set(globalData, this, value); + break; + } - ArrayStorage* storage = createArrayStorage(globalData, i + 1, getNewVectorLength(0, 0, i + 1)); - storage->m_vector[i].set(globalData, this, value); - storage->m_numValuesInVector = 1; + case ALL_CONTIGUOUS_INDEXING_TYPES: { + putByIndexBeyondVectorLengthContiguousWithoutAttributes(exec, i, value); break; } @@ -1371,8 +1619,10 @@ void JSObject::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue bool JSObject::putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode, ArrayStorage* storage) { JSGlobalData& globalData = exec->globalData(); - + // i should be a valid array index that is outside of the current vector. + ASSERT(hasArrayStorage(structure()->indexingType())); + ASSERT(arrayStorage() == storage); ASSERT(i >= storage->vectorLength() || attributes); ASSERT(i <= MAX_ARRAY_INDEX); @@ -1431,7 +1681,7 @@ bool JSObject::putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, WriteBarrier<Unknown>* vector = storage->m_vector; SparseArrayValueMap::const_iterator end = map->end(); for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) - vector[it->first].set(globalData, this, it->second.getNonSparseMode()); + vector[it->key].set(globalData, this, it->value.getNonSparseMode()); deallocateSparseIndexMap(); // Store the new property into the vector. @@ -1454,14 +1704,32 @@ bool JSObject::putDirectIndexBeyondVectorLength(ExecState* exec, unsigned i, JSV switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: { - if (indexingShouldBeSparse() || attributes) - return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(globalData)); - if (!isDenseEnoughForVector(i, 0) || i >= MAX_STORAGE_VECTOR_LENGTH) - return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, createArrayStorage(globalData, 0, 0)); + if (indexingShouldBeSparse() || attributes) { + return putDirectIndexBeyondVectorLengthWithArrayStorage( + exec, i, value, attributes, mode, + ensureArrayStorageExistsAndEnterDictionaryIndexingMode(globalData)); + } + if (i >= MIN_SPARSE_ARRAY_INDEX) { + return putDirectIndexBeyondVectorLengthWithArrayStorage( + exec, i, value, attributes, mode, createArrayStorage(globalData, 0, 0)); + } + if (structure()->needsSlowPutIndexing()) { + ArrayStorage* storage = createArrayStorage(globalData, i + 1, getNewVectorLength(0, 0, i + 1)); + storage->m_vector[i].set(globalData, this, value); + storage->m_numValuesInVector++; + return true; + } - ArrayStorage* storage = createArrayStorage(globalData, i + 1, getNewVectorLength(0, 0, i + 1)); - storage->m_vector[i].set(globalData, this, value); - storage->m_numValuesInVector = 1; + createInitialContiguous(globalData, i + 1)[i].set(globalData, this, value); + return true; + } + + case ALL_CONTIGUOUS_INDEXING_TYPES: { + if (attributes & (ReadOnly | Accessor)) { + return putDirectIndexBeyondVectorLengthWithArrayStorage( + exec, i, value, attributes, mode, convertContiguousToArrayStorage(globalData)); + } + putByIndexBeyondVectorLengthContiguousWithoutAttributes(exec, i, value); return true; } @@ -1486,12 +1754,7 @@ ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned currentVectorLength else if (!currentVectorLength) increasedLength = std::max(desiredLength, lastArraySize); else { - // Mathematically equivalent to: - // increasedLength = (newLength * 3 + 1) / 2; - // or: - // increasedLength = (unsigned)ceil(newLength * 1.5)); - // This form is not prone to internal overflow. - increasedLength = desiredLength + (desiredLength >> 1) + (desiredLength & 1); + increasedLength = timesThreePlusOneDividedByTwo(desiredLength); } ASSERT(increasedLength >= desiredLength); @@ -1511,9 +1774,10 @@ ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned desiredLength) vectorLength = 0; length = 0; break; + case ALL_CONTIGUOUS_INDEXING_TYPES: case ALL_ARRAY_STORAGE_INDEXING_TYPES: - vectorLength = m_butterfly->arrayStorage()->vectorLength(); - length = m_butterfly->arrayStorage()->length(); + vectorLength = m_butterfly->vectorLength(); + length = m_butterfly->publicLength(); break; default: CRASH(); @@ -1522,6 +1786,16 @@ ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned desiredLength) return getNewVectorLength(vectorLength, length, desiredLength); } +unsigned JSObject::countElementsInContiguous(Butterfly* butterfly) +{ + unsigned numValues = 0; + for (unsigned i = butterfly->publicLength(); i--;) { + if (butterfly->contiguous()[i]) + numValues++; + } + return numValues; +} + bool JSObject::increaseVectorLength(JSGlobalData& globalData, unsigned newLength) { // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map @@ -1530,6 +1804,10 @@ bool JSObject::increaseVectorLength(JSGlobalData& globalData, unsigned newLength return false; ArrayStorage* storage = arrayStorage(); + + if (newLength >= MIN_SPARSE_ARRAY_INDEX + && !isDenseEnoughForVector(newLength, storage->m_numValuesInVector)) + return false; unsigned indexBias = storage->m_indexBias; unsigned vectorLength = storage->vectorLength(); @@ -1561,6 +1839,22 @@ bool JSObject::increaseVectorLength(JSGlobalData& globalData, unsigned newLength return true; } +void JSObject::ensureContiguousLengthSlow(JSGlobalData& globalData, unsigned length) +{ + ASSERT(length < MAX_ARRAY_INDEX); + ASSERT(hasContiguous(structure()->indexingType())); + ASSERT(length > m_butterfly->vectorLength()); + + unsigned newVectorLength = std::min( + length << 1, + MAX_STORAGE_VECTOR_LENGTH); + m_butterfly = m_butterfly->growArrayRight( + globalData, structure(), structure()->outOfLineCapacity(), true, + m_butterfly->vectorLength() * sizeof(EncodedJSValue), + newVectorLength * sizeof(EncodedJSValue)); + m_butterfly->setVectorLength(newVectorLength); +} + Butterfly* JSObject::growOutOfLineStorage(JSGlobalData& globalData, size_t oldSize, size_t newSize) { ASSERT(newSize > oldSize); @@ -1589,6 +1883,17 @@ bool JSObject::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, Prope case ALL_BLANK_INDEXING_TYPES: return false; + case ALL_CONTIGUOUS_INDEXING_TYPES: { + Butterfly* butterfly = object->m_butterfly; + if (i >= butterfly->vectorLength()) + return false; + JSValue value = butterfly->contiguous()[i].get(); + if (!value) + return false; + descriptor.setDescriptor(value, 0); + return true; + } + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = object->m_butterfly->arrayStorage(); if (i >= storage->length()) @@ -1604,7 +1909,7 @@ bool JSObject::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, Prope SparseArrayValueMap::iterator it = map->find(i); if (it == map->notFound()) return false; - it->second.get(descriptor); + it->value.get(descriptor); return true; } return false; |