#include "Identifier.h"
#include "JSObject.h"
+#include "JSPropertyNameIterator.h"
+#include "Lookup.h"
#include "PropertyNameArray.h"
#include "StructureChain.h"
-#include "Lookup.h"
#include <wtf/RefCountedLeakCounter.h>
#include <wtf/RefPtr.h>
#endif
}
-Structure::Structure(JSValue prototype, const TypeInfo& typeInfo)
+Structure::Structure(JSValue prototype, const TypeInfo& typeInfo, unsigned anonymousSlotCount)
: m_typeInfo(typeInfo)
, m_prototype(prototype)
, m_specificValueInPrevious(0)
, m_dictionaryKind(NoneDictionaryKind)
, m_isPinnedPropertyTable(false)
, m_hasGetterSetterProperties(false)
- , m_usingSingleTransitionSlot(true)
, m_attributesInPrevious(0)
+ , m_specificFunctionThrashCount(0)
+ , m_anonymousSlotCount(anonymousSlotCount)
{
ASSERT(m_prototype);
ASSERT(m_prototype.isObject() || m_prototype.isNull());
- m_transitions.singleTransition = 0;
-
#ifndef NDEBUG
#if ENABLE(JSC_MULTIPLE_THREADS)
MutexLocker protect(ignoreSetMutex);
Structure::~Structure()
{
if (m_previous) {
- if (m_previous->m_usingSingleTransitionSlot) {
- m_previous->m_transitions.singleTransition = 0;
- } else {
- ASSERT(m_previous->m_transitions.table->contains(make_pair(m_nameInPrevious.get(), m_attributesInPrevious), m_specificValueInPrevious));
- m_previous->m_transitions.table->remove(make_pair(m_nameInPrevious.get(), m_attributesInPrevious), m_specificValueInPrevious);
- }
- }
+ ASSERT(m_nameInPrevious);
+ m_previous->table.remove(make_pair(m_nameInPrevious.get(), m_attributesInPrevious), m_specificValueInPrevious);
- if (m_cachedPropertyNameArrayData)
- m_cachedPropertyNameArrayData->setCachedStructure(0);
-
- if (!m_usingSingleTransitionSlot)
- delete m_transitions.table;
+ }
+
+ if (m_enumerationCache)
+ m_enumerationCache->setCachedStructure(0);
if (m_propertyTable) {
unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount;
for (ptrdiff_t i = structures.size() - 2; i >= 0; --i) {
structure = structures[i];
structure->m_nameInPrevious->ref();
- PropertyMapEntry entry(structure->m_nameInPrevious.get(), structure->m_offset, structure->m_attributesInPrevious, structure->m_specificValueInPrevious, ++m_propertyTable->lastIndexUsed);
+ PropertyMapEntry entry(structure->m_nameInPrevious.get(), m_anonymousSlotCount + structure->m_offset, structure->m_attributesInPrevious, structure->m_specificValueInPrevious, ++m_propertyTable->lastIndexUsed);
insertIntoPropertyMapHashTable(entry);
}
}
-void Structure::getEnumerablePropertyNames(ExecState* exec, PropertyNameArray& propertyNames, JSObject* baseObject)
-{
- bool shouldCache = propertyNames.shouldCache() && !(propertyNames.size() || isDictionary());
-
- if (shouldCache && m_cachedPropertyNameArrayData) {
- if (m_cachedPropertyNameArrayData->cachedPrototypeChain() == prototypeChain(exec)) {
- propertyNames.setData(m_cachedPropertyNameArrayData);
- return;
- }
- clearEnumerationCache();
- }
-
- getEnumerableNamesFromPropertyTable(propertyNames);
- getEnumerableNamesFromClassInfoTable(exec, baseObject->classInfo(), propertyNames);
-
- if (m_prototype.isObject()) {
- propertyNames.setShouldCache(false); // No need for our prototypes to waste memory on caching, since they're not being enumerated directly.
- asObject(m_prototype)->getPropertyNames(exec, propertyNames);
- }
-
- if (shouldCache) {
- StructureChain* protoChain = prototypeChain(exec);
- m_cachedPropertyNameArrayData = propertyNames.data();
- if (!protoChain->isCacheable())
- return;
- m_cachedPropertyNameArrayData->setCachedPrototypeChain(protoChain);
- m_cachedPropertyNameArrayData->setCachedStructure(this);
- }
-}
-
-void Structure::clearEnumerationCache()
-{
- if (m_cachedPropertyNameArrayData)
- m_cachedPropertyNameArrayData->setCachedStructure(0);
- m_cachedPropertyNameArrayData.clear();
-}
-
void Structure::growPropertyStorageCapacity()
{
if (m_propertyStorageCapacity == JSObject::inlineStorageCapacity)
ASSERT(isDictionary());
ASSERT(m_propertyTable);
- unsigned i = rep->computedHash();
+ unsigned i = rep->existingHash();
#if DUMP_PROPERTYMAP_STATS
++numProbes;
++numCollisions;
#endif
- unsigned k = 1 | doubleHash(rep->computedHash());
+ unsigned k = 1 | doubleHash(rep->existingHash());
while (1) {
i += k;
ASSERT(!structure->isDictionary());
ASSERT(structure->typeInfo().type() == ObjectType);
- if (structure->m_usingSingleTransitionSlot) {
- Structure* existingTransition = structure->m_transitions.singleTransition;
- if (existingTransition && existingTransition->m_nameInPrevious.get() == propertyName.ustring().rep()
- && existingTransition->m_attributesInPrevious == attributes
- && (existingTransition->m_specificValueInPrevious == specificValue || existingTransition->m_specificValueInPrevious == 0)) {
-
- ASSERT(structure->m_transitions.singleTransition->m_offset != noOffset);
- offset = structure->m_transitions.singleTransition->m_offset;
- return existingTransition;
- }
- } else {
- if (Structure* existingTransition = structure->m_transitions.table->get(make_pair(propertyName.ustring().rep(), attributes), specificValue)) {
- ASSERT(existingTransition->m_offset != noOffset);
- offset = existingTransition->m_offset;
- return existingTransition;
- }
+ if (Structure* existingTransition = structure->table.get(make_pair(propertyName.ustring().rep(), attributes), specificValue)) {
+ ASSERT(existingTransition->m_offset != noOffset);
+ offset = existingTransition->m_offset + existingTransition->m_anonymousSlotCount;
+ ASSERT(offset >= structure->m_anonymousSlotCount);
+ ASSERT(structure->m_anonymousSlotCount == existingTransition->m_anonymousSlotCount);
+ return existingTransition;
}
return 0;
ASSERT(!structure->isDictionary());
ASSERT(structure->typeInfo().type() == ObjectType);
ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure, propertyName, attributes, specificValue, offset));
+
+ if (structure->m_specificFunctionThrashCount == maxSpecificFunctionThrashCount)
+ specificValue = 0;
if (structure->transitionCount() > s_maxTransitionLength) {
RefPtr<Structure> transition = toCacheableDictionaryTransition(structure);
ASSERT(structure != transition);
offset = transition->put(propertyName, attributes, specificValue);
+ ASSERT(offset >= structure->m_anonymousSlotCount);
+ ASSERT(structure->m_anonymousSlotCount == transition->m_anonymousSlotCount);
if (transition->propertyStorageSize() > transition->propertyStorageCapacity())
transition->growPropertyStorageCapacity();
return transition.release();
}
- RefPtr<Structure> transition = create(structure->m_prototype, structure->typeInfo());
+ RefPtr<Structure> transition = create(structure->m_prototype, structure->typeInfo(), structure->anonymousSlotCount());
transition->m_cachedPrototypeChain = structure->m_cachedPrototypeChain;
transition->m_previous = structure;
transition->m_specificValueInPrevious = specificValue;
transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties;
+ transition->m_hasNonEnumerableProperties = structure->m_hasNonEnumerableProperties;
+ transition->m_specificFunctionThrashCount = structure->m_specificFunctionThrashCount;
if (structure->m_propertyTable) {
if (structure->m_isPinnedPropertyTable)
}
offset = transition->put(propertyName, attributes, specificValue);
+ ASSERT(offset >= structure->m_anonymousSlotCount);
+ ASSERT(structure->m_anonymousSlotCount == transition->m_anonymousSlotCount);
if (transition->propertyStorageSize() > transition->propertyStorageCapacity())
transition->growPropertyStorageCapacity();
- transition->m_offset = offset;
-
- if (structure->m_usingSingleTransitionSlot) {
- if (!structure->m_transitions.singleTransition) {
- structure->m_transitions.singleTransition = transition.get();
- return transition.release();
- }
-
- Structure* existingTransition = structure->m_transitions.singleTransition;
- structure->m_usingSingleTransitionSlot = false;
- StructureTransitionTable* transitionTable = new StructureTransitionTable;
- structure->m_transitions.table = transitionTable;
- transitionTable->add(make_pair(existingTransition->m_nameInPrevious.get(), existingTransition->m_attributesInPrevious), existingTransition, existingTransition->m_specificValueInPrevious);
- }
- structure->m_transitions.table->add(make_pair(propertyName.ustring().rep(), attributes), transition.get(), specificValue);
+ transition->m_offset = offset - structure->m_anonymousSlotCount;
+ ASSERT(structure->anonymousSlotCount() == transition->anonymousSlotCount());
+ structure->table.add(make_pair(propertyName.ustring().rep(), attributes), transition.get(), specificValue);
return transition.release();
}
RefPtr<Structure> transition = toUncacheableDictionaryTransition(structure);
offset = transition->remove(propertyName);
+ ASSERT(offset >= structure->m_anonymousSlotCount);
+ ASSERT(structure->m_anonymousSlotCount == transition->m_anonymousSlotCount);
return transition.release();
}
PassRefPtr<Structure> Structure::changePrototypeTransition(Structure* structure, JSValue prototype)
{
- RefPtr<Structure> transition = create(prototype, structure->typeInfo());
+ RefPtr<Structure> transition = create(prototype, structure->typeInfo(), structure->anonymousSlotCount());
transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties;
+ transition->m_hasNonEnumerableProperties = structure->m_hasNonEnumerableProperties;
+ transition->m_specificFunctionThrashCount = structure->m_specificFunctionThrashCount;
// Don't set m_offset, as one can not transition to this.
structure->materializePropertyMapIfNecessary();
transition->m_propertyTable = structure->copyPropertyTable();
transition->m_isPinnedPropertyTable = true;
-
+
+ ASSERT(structure->anonymousSlotCount() == transition->anonymousSlotCount());
return transition.release();
}
PassRefPtr<Structure> Structure::despecifyFunctionTransition(Structure* structure, const Identifier& replaceFunction)
{
- RefPtr<Structure> transition = create(structure->storedPrototype(), structure->typeInfo());
+ ASSERT(structure->m_specificFunctionThrashCount < maxSpecificFunctionThrashCount);
+ RefPtr<Structure> transition = create(structure->storedPrototype(), structure->typeInfo(), structure->anonymousSlotCount());
transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties;
+ transition->m_hasNonEnumerableProperties = structure->m_hasNonEnumerableProperties;
+ transition->m_specificFunctionThrashCount = structure->m_specificFunctionThrashCount + 1;
// Don't set m_offset, as one can not transition to this.
transition->m_propertyTable = structure->copyPropertyTable();
transition->m_isPinnedPropertyTable = true;
- bool removed = transition->despecifyFunction(replaceFunction);
- ASSERT_UNUSED(removed, removed);
-
+ if (transition->m_specificFunctionThrashCount == maxSpecificFunctionThrashCount)
+ transition->despecifyAllFunctions();
+ else {
+ bool removed = transition->despecifyFunction(replaceFunction);
+ ASSERT_UNUSED(removed, removed);
+ }
+
+ ASSERT(structure->anonymousSlotCount() == transition->anonymousSlotCount());
return transition.release();
}
PassRefPtr<Structure> Structure::getterSetterTransition(Structure* structure)
{
- RefPtr<Structure> transition = create(structure->storedPrototype(), structure->typeInfo());
+ RefPtr<Structure> transition = create(structure->storedPrototype(), structure->typeInfo(), structure->anonymousSlotCount());
transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
transition->m_hasGetterSetterProperties = transition->m_hasGetterSetterProperties;
+ transition->m_hasNonEnumerableProperties = structure->m_hasNonEnumerableProperties;
+ transition->m_specificFunctionThrashCount = structure->m_specificFunctionThrashCount;
// Don't set m_offset, as one can not transition to this.
structure->materializePropertyMapIfNecessary();
transition->m_propertyTable = structure->copyPropertyTable();
transition->m_isPinnedPropertyTable = true;
-
+
+ ASSERT(structure->anonymousSlotCount() == transition->anonymousSlotCount());
return transition.release();
}
{
ASSERT(!structure->isUncacheableDictionary());
- RefPtr<Structure> transition = create(structure->m_prototype, structure->typeInfo());
+ RefPtr<Structure> transition = create(structure->m_prototype, structure->typeInfo(), structure->anonymousSlotCount());
transition->m_dictionaryKind = kind;
transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties;
+ transition->m_hasNonEnumerableProperties = structure->m_hasNonEnumerableProperties;
+ transition->m_specificFunctionThrashCount = structure->m_specificFunctionThrashCount;
structure->materializePropertyMapIfNecessary();
transition->m_propertyTable = structure->copyPropertyTable();
transition->m_isPinnedPropertyTable = true;
+ ASSERT(structure->anonymousSlotCount() == transition->anonymousSlotCount());
return transition.release();
}
// in the order that they are expected to be in, but we need to
// reorder the storage, so we have to copy the current values out
Vector<JSValue> values(propertyCount);
- // FIXME: Remove this workaround with the next merge.
- unsigned anonymousSlotCount = 0;
+ unsigned anonymousSlotCount = m_anonymousSlotCount;
for (unsigned i = 0; i < propertyCount; i++) {
PropertyMapEntry* entry = sortedPropertyEntries[i];
values[i] = object->getDirectOffset(entry->offset);
size_t Structure::addPropertyWithoutTransition(const Identifier& propertyName, unsigned attributes, JSCell* specificValue)
{
- ASSERT(!m_transitions.singleTransition);
+ ASSERT(!m_enumerationCache);
+
+ if (m_specificFunctionThrashCount == maxSpecificFunctionThrashCount)
+ specificValue = 0;
materializePropertyMapIfNecessary();
m_isPinnedPropertyTable = true;
+
size_t offset = put(propertyName, attributes, specificValue);
+ ASSERT(offset >= m_anonymousSlotCount);
if (propertyStorageSize() > propertyStorageCapacity())
growPropertyStorageCapacity();
- clearEnumerationCache();
return offset;
}
size_t Structure::removePropertyWithoutTransition(const Identifier& propertyName)
{
ASSERT(isUncacheableDictionary());
+ ASSERT(!m_enumerationCache);
materializePropertyMapIfNecessary();
m_isPinnedPropertyTable = true;
size_t offset = remove(propertyName);
- clearEnumerationCache();
+ ASSERT(offset >= m_anonymousSlotCount);
return offset;
}
if (!m_propertyTable)
return notFound;
- unsigned i = rep->computedHash();
+ unsigned i = rep->existingHash();
#if DUMP_PROPERTYMAP_STATS
++numProbes;
if (rep == m_propertyTable->entries()[entryIndex - 1].key) {
attributes = m_propertyTable->entries()[entryIndex - 1].attributes;
specificValue = m_propertyTable->entries()[entryIndex - 1].specificValue;
+ ASSERT(m_propertyTable->entries()[entryIndex - 1].offset >= m_anonymousSlotCount);
return m_propertyTable->entries()[entryIndex - 1].offset;
}
++numCollisions;
#endif
- unsigned k = 1 | doubleHash(rep->computedHash());
+ unsigned k = 1 | doubleHash(rep->existingHash());
while (1) {
i += k;
if (rep == m_propertyTable->entries()[entryIndex - 1].key) {
attributes = m_propertyTable->entries()[entryIndex - 1].attributes;
specificValue = m_propertyTable->entries()[entryIndex - 1].specificValue;
+ ASSERT(m_propertyTable->entries()[entryIndex - 1].offset >= m_anonymousSlotCount);
return m_propertyTable->entries()[entryIndex - 1].offset;
}
}
UString::Rep* rep = propertyName._ustring.rep();
- unsigned i = rep->computedHash();
+ unsigned i = rep->existingHash();
#if DUMP_PROPERTYMAP_STATS
++numProbes;
++numCollisions;
#endif
- unsigned k = 1 | doubleHash(rep->computedHash());
+ unsigned k = 1 | doubleHash(rep->existingHash());
while (1) {
i += k;
}
}
+void Structure::despecifyAllFunctions()
+{
+ materializePropertyMapIfNecessary();
+ if (!m_propertyTable)
+ return;
+
+ unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount;
+ for (unsigned i = 1; i <= entryCount; ++i)
+ m_propertyTable->entries()[i].specificValue = 0;
+}
+
size_t Structure::put(const Identifier& propertyName, unsigned attributes, JSCell* specificValue)
{
ASSERT(!propertyName.isNull());
checkConsistency();
+ if (attributes & DontEnum)
+ m_hasNonEnumerableProperties = true;
+
UString::Rep* rep = propertyName._ustring.rep();
if (!m_propertyTable)
// FIXME: Consider a fast case for tables with no deleted sentinels.
- unsigned i = rep->computedHash();
+ unsigned i = rep->existingHash();
unsigned k = 0;
bool foundDeletedElement = false;
unsigned deletedElementIndex = 0; // initialize to make the compiler happy
}
if (k == 0) {
- k = 1 | doubleHash(rep->computedHash());
+ k = 1 | doubleHash(rep->existingHash());
#if DUMP_PROPERTYMAP_STATS
++numCollisions;
#endif
newOffset = m_propertyTable->deletedOffsets->last();
m_propertyTable->deletedOffsets->removeLast();
} else
- newOffset = m_propertyTable->keyCount;
+ newOffset = m_propertyTable->keyCount + m_anonymousSlotCount;
m_propertyTable->entries()[entryIndex - 1].offset = newOffset;
-
+
+ ASSERT(newOffset >= m_anonymousSlotCount);
++m_propertyTable->keyCount;
if ((m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount) * 2 >= m_propertyTable->size)
bool Structure::hasTransition(UString::Rep* rep, unsigned attributes)
{
- if (m_usingSingleTransitionSlot) {
- return m_transitions.singleTransition
- && m_transitions.singleTransition->m_nameInPrevious == rep
- && m_transitions.singleTransition->m_attributesInPrevious == attributes;
- }
- return m_transitions.table->hasTransition(make_pair(rep, attributes));
+ return table.hasTransition(make_pair(rep, attributes));
}
size_t Structure::remove(const Identifier& propertyName)
#endif
// Find the thing to remove.
- unsigned i = rep->computedHash();
+ unsigned i = rep->existingHash();
unsigned k = 0;
unsigned entryIndex;
UString::Rep* key = 0;
break;
if (k == 0) {
- k = 1 | doubleHash(rep->computedHash());
+ k = 1 | doubleHash(rep->existingHash());
#if DUMP_PROPERTYMAP_STATS
++numCollisions;
#endif
m_propertyTable->entryIndices[i & m_propertyTable->sizeMask] = deletedSentinelIndex;
size_t offset = m_propertyTable->entries()[entryIndex - 1].offset;
+ ASSERT(offset >= m_anonymousSlotCount);
key->deref();
m_propertyTable->entries()[entryIndex - 1].key = 0;
void Structure::insertIntoPropertyMapHashTable(const PropertyMapEntry& entry)
{
ASSERT(m_propertyTable);
-
- unsigned i = entry.key->computedHash();
+ ASSERT(entry.offset >= m_anonymousSlotCount);
+ unsigned i = entry.key->existingHash();
unsigned k = 0;
#if DUMP_PROPERTYMAP_STATS
break;
if (k == 0) {
- k = 1 | doubleHash(entry.key->computedHash());
+ k = 1 | doubleHash(entry.key->existingHash());
#if DUMP_PROPERTYMAP_STATS
++numCollisions;
#endif
return 0;
}
-void Structure::getEnumerableNamesFromPropertyTable(PropertyNameArray& propertyNames)
+void Structure::getPropertyNames(PropertyNameArray& propertyNames, EnumerationMode mode)
{
materializePropertyMapIfNecessary();
if (!m_propertyTable)
int i = 0;
unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount;
for (unsigned k = 1; k <= entryCount; k++) {
- if (m_propertyTable->entries()[k].key && !(m_propertyTable->entries()[k].attributes & DontEnum)) {
+ ASSERT(m_hasNonEnumerableProperties || !(m_propertyTable->entries()[k].attributes & DontEnum));
+ if (m_propertyTable->entries()[k].key && (!(m_propertyTable->entries()[k].attributes & DontEnum) || (mode == IncludeDontEnumProperties))) {
PropertyMapEntry* value = &m_propertyTable->entries()[k];
int j;
for (j = i - 1; j >= 0 && a[j]->index > value->index; --j)
PropertyMapEntry** p = sortedEnumerables.data();
unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount;
for (unsigned i = 1; i <= entryCount; i++) {
- if (m_propertyTable->entries()[i].key && !(m_propertyTable->entries()[i].attributes & DontEnum))
+ if (m_propertyTable->entries()[i].key && (!(m_propertyTable->entries()[i].attributes & DontEnum) || (mode == IncludeDontEnumProperties)))
*p++ = &m_propertyTable->entries()[i];
}
}
}
-void Structure::getEnumerableNamesFromClassInfoTable(ExecState* exec, const ClassInfo* classInfo, PropertyNameArray& propertyNames)
-{
- // Add properties from the static hashtables of properties
- for (; classInfo; classInfo = classInfo->parentClass) {
- const HashTable* table = classInfo->propHashTable(exec);
- if (!table)
- continue;
- table->initializeIfNeeded(exec);
- ASSERT(table->table);
-
- int hashSizeMask = table->compactSize - 1;
- const HashEntry* entry = table->table;
- for (int i = 0; i <= hashSizeMask; ++i, ++entry) {
- if (entry->key() && !(entry->attributes() & DontEnum))
- propertyNames.add(entry->key());
- }
- }
-}
-
#if DO_PROPERTYMAP_CONSTENCY_CHECK
void Structure::checkConsistency()
unsigned nonEmptyEntryCount = 0;
for (unsigned c = 1; c <= m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; ++c) {
+ ASSERT(m_hasNonEnumerableProperties || !(m_propertyTable->entries()[c].attributes & DontEnum));
UString::Rep* rep = m_propertyTable->entries()[c].key;
+ ASSERT(m_propertyTable->entries()[c].offset >= m_anonymousSlotCount);
if (!rep)
continue;
++nonEmptyEntryCount;
- unsigned i = rep->computedHash();
+ unsigned i = rep->existingHash();
unsigned k = 0;
unsigned entryIndex;
while (1) {
if (rep == m_propertyTable->entries()[entryIndex - 1].key)
break;
if (k == 0)
- k = 1 | doubleHash(rep->computedHash());
+ k = 1 | doubleHash(rep->existingHash());
i += k;
}
ASSERT(entryIndex == c + 1);