X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/9dae56ea45a0f5f8136a5c93d6f3a7f99399ca73..fb8617cde5834786bd4e4afd579883e4acf5666e:/runtime/Structure.cpp diff --git a/runtime/Structure.cpp b/runtime/Structure.cpp index 8133cd2..546e2bf 100644 --- a/runtime/Structure.cpp +++ b/runtime/Structure.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,9 +28,10 @@ #include "Identifier.h" #include "JSObject.h" +#include "JSPropertyNameIterator.h" +#include "Lookup.h" #include "PropertyNameArray.h" #include "StructureChain.h" -#include "Lookup.h" #include #include @@ -65,17 +66,19 @@ static const unsigned newTableSize = 16; static WTF::RefCountedLeakCounter structureCounter("Structure"); #if ENABLE(JSC_MULTIPLE_THREADS) -static Mutex ignoreSetMutex; +static Mutex& ignoreSetMutex = *(new Mutex); #endif static bool shouldIgnoreLeaks; -static HashSet ignoreSet; +static HashSet& ignoreSet = *(new HashSet); #endif #if DUMP_STRUCTURE_ID_STATISTICS -static HashSet liveStructureSet; +static HashSet& liveStructureSet = *(new HashSet); #endif +static int comparePropertyMapEntryIndices(const void* a, const void* b); + void Structure::dumpStatistics() { #if DUMP_STRUCTURE_ID_STATISTICS @@ -120,23 +123,23 @@ void Structure::dumpStatistics() #endif } -Structure::Structure(JSValuePtr prototype, const TypeInfo& typeInfo) +Structure::Structure(JSValue prototype, const TypeInfo& typeInfo, unsigned anonymousSlotCount) : m_typeInfo(typeInfo) , m_prototype(prototype) + , m_specificValueInPrevious(0) , m_propertyTable(0) , m_propertyStorageCapacity(JSObject::inlineStorageCapacity) , m_offset(noOffset) - , m_isDictionary(false) + , 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); @@ -155,19 +158,13 @@ Structure::Structure(JSValuePtr prototype, const TypeInfo& typeInfo) 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_previous->m_transitions.table->remove(make_pair(m_nameInPrevious.get(), m_attributesInPrevious)); - } - } - - if (m_cachedPropertyNameArrayData) - m_cachedPropertyNameArrayData->setCachedStructure(0); + ASSERT(m_nameInPrevious); + m_previous->table.remove(make_pair(m_nameInPrevious.get(), m_attributesInPrevious), m_specificValueInPrevious); - 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; @@ -279,97 +276,112 @@ void Structure::materializePropertyMap() 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, ++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) +void Structure::growPropertyStorageCapacity() { - bool shouldCache = propertyNames.shouldCache() && !(propertyNames.size() || m_isDictionary); + if (m_propertyStorageCapacity == JSObject::inlineStorageCapacity) + m_propertyStorageCapacity = JSObject::nonInlineBaseStorageCapacity; + else + m_propertyStorageCapacity *= 2; +} - if (shouldCache && m_cachedPropertyNameArrayData) { - if (m_cachedPropertyNameArrayData->cachedPrototypeChain() == prototypeChain(exec)) { - propertyNames.setData(m_cachedPropertyNameArrayData); - return; - } - clearEnumerationCache(); - } +void Structure::despecifyDictionaryFunction(const Identifier& propertyName) +{ + const UString::Rep* rep = propertyName._ustring.rep(); - getEnumerableNamesFromPropertyTable(propertyNames); - getEnumerableNamesFromClassInfoTable(exec, baseObject->classInfo(), propertyNames); + materializePropertyMapIfNecessary(); - 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); - } + ASSERT(isDictionary()); + ASSERT(m_propertyTable); + + unsigned i = rep->existingHash(); + +#if DUMP_PROPERTYMAP_STATS + ++numProbes; +#endif - if (shouldCache) { - m_cachedPropertyNameArrayData = propertyNames.data(); - m_cachedPropertyNameArrayData->setCachedPrototypeChain(prototypeChain(exec)); - m_cachedPropertyNameArrayData->setCachedStructure(this); + unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; + ASSERT(entryIndex != emptyEntryIndex); + + if (rep == m_propertyTable->entries()[entryIndex - 1].key) { + m_propertyTable->entries()[entryIndex - 1].specificValue = 0; + return; } -} -void Structure::clearEnumerationCache() -{ - if (m_cachedPropertyNameArrayData) - m_cachedPropertyNameArrayData->setCachedStructure(0); - m_cachedPropertyNameArrayData.clear(); -} +#if DUMP_PROPERTYMAP_STATS + ++numCollisions; +#endif -void Structure::growPropertyStorageCapacity() -{ - if (m_propertyStorageCapacity == JSObject::inlineStorageCapacity) - m_propertyStorageCapacity = JSObject::nonInlineBaseStorageCapacity; - else - m_propertyStorageCapacity *= 2; + unsigned k = 1 | doubleHash(rep->existingHash()); + + while (1) { + i += k; + +#if DUMP_PROPERTYMAP_STATS + ++numRehashes; +#endif + + entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; + ASSERT(entryIndex != emptyEntryIndex); + + if (rep == m_propertyTable->entries()[entryIndex - 1].key) { + m_propertyTable->entries()[entryIndex - 1].specificValue = 0; + return; + } + } } -PassRefPtr Structure::addPropertyTransitionToExistingStructure(Structure* structure, const Identifier& propertyName, unsigned attributes, size_t& offset) +PassRefPtr Structure::addPropertyTransitionToExistingStructure(Structure* structure, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset) { - ASSERT(!structure->m_isDictionary); + 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) { - 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))) { - 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; } -PassRefPtr Structure::addPropertyTransition(Structure* structure, const Identifier& propertyName, unsigned attributes, size_t& offset) +PassRefPtr Structure::addPropertyTransition(Structure* structure, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset) { - ASSERT(!structure->m_isDictionary); + ASSERT(!structure->isDictionary()); ASSERT(structure->typeInfo().type() == ObjectType); - ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure, propertyName, attributes, offset)); + ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure, propertyName, attributes, specificValue, offset)); + + if (structure->m_specificFunctionThrashCount == maxSpecificFunctionThrashCount) + specificValue = 0; if (structure->transitionCount() > s_maxTransitionLength) { - RefPtr transition = toDictionaryTransition(structure); - offset = transition->put(propertyName, attributes); + RefPtr 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 transition = create(structure->m_prototype, structure->typeInfo()); + RefPtr transition = create(structure->m_prototype, structure->typeInfo(), structure->anonymousSlotCount()); + transition->m_cachedPrototypeChain = structure->m_cachedPrototypeChain; transition->m_previous = structure; transition->m_nameInPrevious = propertyName.ustring().rep(); transition->m_attributesInPrevious = attributes; + 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) @@ -385,126 +397,195 @@ PassRefPtr Structure::addPropertyTransition(Structure* structure, con transition->createPropertyMapHashTable(); } - offset = transition->put(propertyName, attributes); + 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); - } - structure->m_transitions.table->add(make_pair(propertyName.ustring().rep(), attributes), transition.get()); + 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(); } PassRefPtr Structure::removePropertyTransition(Structure* structure, const Identifier& propertyName, size_t& offset) { - ASSERT(!structure->m_isDictionary); + ASSERT(!structure->isUncacheableDictionary()); - RefPtr transition = toDictionaryTransition(structure); + RefPtr transition = toUncacheableDictionaryTransition(structure); offset = transition->remove(propertyName); + ASSERT(offset >= structure->m_anonymousSlotCount); + ASSERT(structure->m_anonymousSlotCount == transition->m_anonymousSlotCount); return transition.release(); } -PassRefPtr Structure::changePrototypeTransition(Structure* structure, JSValuePtr prototype) +PassRefPtr Structure::changePrototypeTransition(Structure* structure, JSValue prototype) { - RefPtr transition = create(prototype, structure->typeInfo()); + RefPtr 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::despecifyFunctionTransition(Structure* structure, const Identifier& replaceFunction) +{ + ASSERT(structure->m_specificFunctionThrashCount < maxSpecificFunctionThrashCount); + RefPtr 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. + + structure->materializePropertyMapIfNecessary(); + transition->m_propertyTable = structure->copyPropertyTable(); + transition->m_isPinnedPropertyTable = true; + + 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::getterSetterTransition(Structure* structure) { - RefPtr transition = create(structure->storedPrototype(), structure->typeInfo()); + RefPtr 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(); } -PassRefPtr Structure::toDictionaryTransition(Structure* structure) +PassRefPtr Structure::toDictionaryTransition(Structure* structure, DictionaryKind kind) { - ASSERT(!structure->m_isDictionary); - - RefPtr transition = create(structure->m_prototype, structure->typeInfo()); - transition->m_isDictionary = true; + ASSERT(!structure->isUncacheableDictionary()); + + RefPtr 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(); } -PassRefPtr Structure::fromDictionaryTransition(Structure* structure) +PassRefPtr Structure::toCacheableDictionaryTransition(Structure* structure) { - ASSERT(structure->m_isDictionary); + return toDictionaryTransition(structure, CachedDictionaryKind); +} - // Since dictionary Structures are not shared, and no opcodes specialize - // for them, we don't need to allocate a new Structure when transitioning - // to non-dictionary status. +PassRefPtr Structure::toUncacheableDictionaryTransition(Structure* structure) +{ + return toDictionaryTransition(structure, UncachedDictionaryKind); +} - // FIMXE: We can make this more efficient by canonicalizing the Structure (draining the - // deleted offsets vector) before transitioning from dictionary. - if (!structure->m_propertyTable || !structure->m_propertyTable->deletedOffsets || structure->m_propertyTable->deletedOffsets->isEmpty()) - structure->m_isDictionary = false; +PassRefPtr Structure::flattenDictionaryStructure(JSObject* object) +{ + ASSERT(isDictionary()); + if (isUncacheableDictionary()) { + ASSERT(m_propertyTable); + Vector sortedPropertyEntries(m_propertyTable->keyCount); + PropertyMapEntry** p = sortedPropertyEntries.data(); + unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; + for (unsigned i = 1; i <= entryCount; i++) { + if (m_propertyTable->entries()[i].key) + *p++ = &m_propertyTable->entries()[i]; + } + size_t propertyCount = p - sortedPropertyEntries.data(); + qsort(sortedPropertyEntries.data(), propertyCount, sizeof(PropertyMapEntry*), comparePropertyMapEntryIndices); + sortedPropertyEntries.resize(propertyCount); + + // We now have the properties currently defined on this object + // 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 values(propertyCount); + unsigned anonymousSlotCount = m_anonymousSlotCount; + for (unsigned i = 0; i < propertyCount; i++) { + PropertyMapEntry* entry = sortedPropertyEntries[i]; + values[i] = object->getDirectOffset(entry->offset); + // Update property table to have the new property offsets + entry->offset = anonymousSlotCount + i; + entry->index = i; + } + + // Copy the original property values into their final locations + for (unsigned i = 0; i < propertyCount; i++) + object->putDirectOffset(anonymousSlotCount + i, values[i]); + + if (m_propertyTable->deletedOffsets) { + delete m_propertyTable->deletedOffsets; + m_propertyTable->deletedOffsets = 0; + } + } - return structure; + m_dictionaryKind = NoneDictionaryKind; + return this; } -size_t Structure::addPropertyWithoutTransition(const Identifier& propertyName, unsigned attributes) +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); + + 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(!m_transitions.singleTransition); - ASSERT(m_isDictionary); + ASSERT(isUncacheableDictionary()); + ASSERT(!m_enumerationCache); materializePropertyMapIfNecessary(); m_isPinnedPropertyTable = true; size_t offset = remove(propertyName); - clearEnumerationCache(); + ASSERT(offset >= m_anonymousSlotCount); return offset; } @@ -564,17 +645,13 @@ PropertyMapHashTable* Structure::copyPropertyTable() return newTable; } -size_t Structure::get(const Identifier& propertyName, unsigned& attributes) +size_t Structure::get(const UString::Rep* rep, unsigned& attributes, JSCell*& specificValue) { - ASSERT(!propertyName.isNull()); - materializePropertyMapIfNecessary(); if (!m_propertyTable) return notFound; - UString::Rep* rep = propertyName._ustring.rep(); - - unsigned i = rep->computedHash(); + unsigned i = rep->existingHash(); #if DUMP_PROPERTYMAP_STATS ++numProbes; @@ -586,6 +663,8 @@ size_t Structure::get(const Identifier& propertyName, unsigned& attributes) 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; } @@ -593,7 +672,7 @@ size_t Structure::get(const Identifier& propertyName, unsigned& attributes) ++numCollisions; #endif - unsigned k = 1 | doubleHash(rep->computedHash()); + unsigned k = 1 | doubleHash(rep->existingHash()); while (1) { i += k; @@ -608,18 +687,85 @@ size_t Structure::get(const Identifier& propertyName, unsigned& attributes) 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; } } } -size_t Structure::put(const Identifier& propertyName, unsigned attributes) +bool Structure::despecifyFunction(const Identifier& propertyName) +{ + ASSERT(!propertyName.isNull()); + + materializePropertyMapIfNecessary(); + if (!m_propertyTable) + return false; + + UString::Rep* rep = propertyName._ustring.rep(); + + unsigned i = rep->existingHash(); + +#if DUMP_PROPERTYMAP_STATS + ++numProbes; +#endif + + unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; + if (entryIndex == emptyEntryIndex) + return false; + + if (rep == m_propertyTable->entries()[entryIndex - 1].key) { + ASSERT(m_propertyTable->entries()[entryIndex - 1].specificValue); + m_propertyTable->entries()[entryIndex - 1].specificValue = 0; + return true; + } + +#if DUMP_PROPERTYMAP_STATS + ++numCollisions; +#endif + + unsigned k = 1 | doubleHash(rep->existingHash()); + + while (1) { + i += k; + +#if DUMP_PROPERTYMAP_STATS + ++numRehashes; +#endif + + entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; + if (entryIndex == emptyEntryIndex) + return false; + + if (rep == m_propertyTable->entries()[entryIndex - 1].key) { + ASSERT(m_propertyTable->entries()[entryIndex - 1].specificValue); + m_propertyTable->entries()[entryIndex - 1].specificValue = 0; + return true; + } + } +} + +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()); ASSERT(get(propertyName) == notFound); checkConsistency(); + if (attributes & DontEnum) + m_hasNonEnumerableProperties = true; + UString::Rep* rep = propertyName._ustring.rep(); if (!m_propertyTable) @@ -627,7 +773,7 @@ size_t Structure::put(const Identifier& propertyName, unsigned attributes) // 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 @@ -650,7 +796,7 @@ size_t Structure::put(const Identifier& propertyName, unsigned attributes) } if (k == 0) { - k = 1 | doubleHash(rep->computedHash()); + k = 1 | doubleHash(rep->existingHash()); #if DUMP_PROPERTYMAP_STATS ++numCollisions; #endif @@ -683,6 +829,7 @@ size_t Structure::put(const Identifier& propertyName, unsigned attributes) rep->ref(); m_propertyTable->entries()[entryIndex - 1].key = rep; m_propertyTable->entries()[entryIndex - 1].attributes = attributes; + m_propertyTable->entries()[entryIndex - 1].specificValue = specificValue; m_propertyTable->entries()[entryIndex - 1].index = ++m_propertyTable->lastIndexUsed; unsigned newOffset; @@ -690,9 +837,10 @@ size_t Structure::put(const Identifier& propertyName, unsigned attributes) 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) @@ -702,6 +850,11 @@ size_t Structure::put(const Identifier& propertyName, unsigned attributes) return newOffset; } +bool Structure::hasTransition(UString::Rep* rep, unsigned attributes) +{ + return table.hasTransition(make_pair(rep, attributes)); +} + size_t Structure::remove(const Identifier& propertyName) { ASSERT(!propertyName.isNull()); @@ -719,7 +872,7 @@ 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; @@ -733,7 +886,7 @@ size_t Structure::remove(const Identifier& propertyName) break; if (k == 0) { - k = 1 | doubleHash(rep->computedHash()); + k = 1 | doubleHash(rep->existingHash()); #if DUMP_PROPERTYMAP_STATS ++numCollisions; #endif @@ -751,10 +904,12 @@ size_t Structure::remove(const Identifier& propertyName) 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; m_propertyTable->entries()[entryIndex - 1].attributes = 0; + m_propertyTable->entries()[entryIndex - 1].specificValue = 0; m_propertyTable->entries()[entryIndex - 1].offset = 0; if (!m_propertyTable->deletedOffsets) @@ -775,8 +930,8 @@ size_t Structure::remove(const Identifier& propertyName) 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 @@ -789,7 +944,7 @@ void Structure::insertIntoPropertyMapHashTable(const PropertyMapEntry& entry) break; if (k == 0) { - k = 1 | doubleHash(entry.key->computedHash()); + k = 1 | doubleHash(entry.key->existingHash()); #if DUMP_PROPERTYMAP_STATS ++numCollisions; #endif @@ -871,7 +1026,7 @@ void Structure::rehashPropertyMapHashTable(unsigned newTableSize) checkConsistency(); } -static int comparePropertyMapEntryIndices(const void* a, const void* b) +int comparePropertyMapEntryIndices(const void* a, const void* b) { unsigned ia = static_cast(a)[0]->index; unsigned ib = static_cast(b)[0]->index; @@ -882,7 +1037,7 @@ static int comparePropertyMapEntryIndices(const void* a, const void* b) return 0; } -void Structure::getEnumerableNamesFromPropertyTable(PropertyNameArray& propertyNames) +void Structure::getPropertyNames(PropertyNameArray& propertyNames, EnumerationMode mode) { materializePropertyMapIfNecessary(); if (!m_propertyTable) @@ -893,7 +1048,8 @@ void Structure::getEnumerableNamesFromPropertyTable(PropertyNameArray& propertyN 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) @@ -920,7 +1076,7 @@ void Structure::getEnumerableNamesFromPropertyTable(PropertyNameArray& propertyN 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]; } @@ -939,28 +1095,6 @@ void Structure::getEnumerableNamesFromPropertyTable(PropertyNameArray& propertyN } } -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); -#if ENABLE(PERFECT_HASH_SIZE) - int hashSizeMask = table->hashSizeMask; -#else - int hashSizeMask = table->compactSize - 1; -#endif - 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() @@ -1002,11 +1136,13 @@ 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) { @@ -1015,7 +1151,7 @@ void Structure::checkConsistency() 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);