JavaScriptCore/runtime/Structure.cpp
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     1 /*
       
     2  * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
       
     3  *
       
     4  * Redistribution and use in source and binary forms, with or without
       
     5  * modification, are permitted provided that the following conditions
       
     6  * are met:
       
     7  * 1. Redistributions of source code must retain the above copyright
       
     8  *    notice, this list of conditions and the following disclaimer.
       
     9  * 2. Redistributions in binary form must reproduce the above copyright
       
    10  *    notice, this list of conditions and the following disclaimer in the
       
    11  *    documentation and/or other materials provided with the distribution.
       
    12  *
       
    13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
       
    14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
       
    15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
       
    16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
       
    17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
       
    18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
       
    19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
       
    20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
       
    21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
       
    22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
       
    23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
       
    24  */
       
    25 
       
    26 #include "config.h"
       
    27 #include "Structure.h"
       
    28 
       
    29 #include "Identifier.h"
       
    30 #include "JSObject.h"
       
    31 #include "JSPropertyNameIterator.h"
       
    32 #include "Lookup.h"
       
    33 #include "PropertyNameArray.h"
       
    34 #include "StructureChain.h"
       
    35 #include <wtf/RefCountedLeakCounter.h>
       
    36 #include <wtf/RefPtr.h>
       
    37 
       
    38 #if ENABLE(JSC_MULTIPLE_THREADS)
       
    39 #include <wtf/Threading.h>
       
    40 #endif
       
    41 
       
    42 #define DUMP_STRUCTURE_ID_STATISTICS 0
       
    43 
       
    44 #ifndef NDEBUG
       
    45 #define DO_PROPERTYMAP_CONSTENCY_CHECK 0
       
    46 #else
       
    47 #define DO_PROPERTYMAP_CONSTENCY_CHECK 0
       
    48 #endif
       
    49 
       
    50 using namespace std;
       
    51 using namespace WTF;
       
    52 
       
    53 namespace JSC {
       
    54 
       
    55 // Choose a number for the following so that most property maps are smaller,
       
    56 // but it's not going to blow out the stack to allocate this number of pointers.
       
    57 static const int smallMapThreshold = 1024;
       
    58 
       
    59 // The point at which the function call overhead of the qsort implementation
       
    60 // becomes small compared to the inefficiency of insertion sort.
       
    61 static const unsigned tinyMapThreshold = 20;
       
    62 
       
    63 static const unsigned newTableSize = 16;
       
    64 
       
    65 #ifndef NDEBUG
       
    66 static WTF::RefCountedLeakCounter structureCounter("Structure");
       
    67 
       
    68 #if ENABLE(JSC_MULTIPLE_THREADS)
       
    69 static Mutex& ignoreSetMutex = *(new Mutex);
       
    70 #endif
       
    71 
       
    72 static bool shouldIgnoreLeaks;
       
    73 static HashSet<Structure*>& ignoreSet = *(new HashSet<Structure*>);
       
    74 #endif
       
    75 
       
    76 #if DUMP_STRUCTURE_ID_STATISTICS
       
    77 static HashSet<Structure*>& liveStructureSet = *(new HashSet<Structure*>);
       
    78 #endif
       
    79 
       
    80 static int comparePropertyMapEntryIndices(const void* a, const void* b);
       
    81 
       
    82 inline void Structure::setTransitionTable(TransitionTable* table)
       
    83 {
       
    84     ASSERT(m_isUsingSingleSlot);
       
    85 #ifndef NDEBUG
       
    86     setSingleTransition(0);
       
    87 #endif
       
    88     m_isUsingSingleSlot = false;
       
    89     m_transitions.m_table = table;
       
    90     // This implicitly clears the flag that indicates we're using a single transition
       
    91     ASSERT(!m_isUsingSingleSlot);
       
    92 }
       
    93 
       
    94 // The contains and get methods accept imprecise matches, so if an unspecialised transition exists
       
    95 // for the given key they will consider that transition to be a match.  If a specialised transition
       
    96 // exists and it matches the provided specificValue, get will return the specific transition.
       
    97 inline bool Structure::transitionTableContains(const StructureTransitionTableHash::Key& key, JSCell* specificValue)
       
    98 {
       
    99     if (m_isUsingSingleSlot) {
       
   100         Structure* existingTransition = singleTransition();
       
   101         return existingTransition && existingTransition->m_nameInPrevious.get() == key.first
       
   102                && existingTransition->m_attributesInPrevious == key.second
       
   103                && (existingTransition->m_specificValueInPrevious == specificValue || existingTransition->m_specificValueInPrevious == 0);
       
   104     }
       
   105     TransitionTable::iterator find = transitionTable()->find(key);
       
   106     if (find == transitionTable()->end())
       
   107         return false;
       
   108 
       
   109     return find->second.first || find->second.second->transitionedFor(specificValue);
       
   110 }
       
   111 
       
   112 inline Structure* Structure::transitionTableGet(const StructureTransitionTableHash::Key& key, JSCell* specificValue) const
       
   113 {
       
   114     if (m_isUsingSingleSlot) {
       
   115         Structure* existingTransition = singleTransition();
       
   116         if (existingTransition && existingTransition->m_nameInPrevious.get() == key.first
       
   117             && existingTransition->m_attributesInPrevious == key.second
       
   118             && (existingTransition->m_specificValueInPrevious == specificValue || existingTransition->m_specificValueInPrevious == 0))
       
   119             return existingTransition;
       
   120         return 0;
       
   121     }
       
   122 
       
   123     Transition transition = transitionTable()->get(key);
       
   124     if (transition.second && transition.second->transitionedFor(specificValue))
       
   125         return transition.second;
       
   126     return transition.first;
       
   127 }
       
   128 
       
   129 inline bool Structure::transitionTableHasTransition(const StructureTransitionTableHash::Key& key) const
       
   130 {
       
   131     if (m_isUsingSingleSlot) {
       
   132         Structure* transition = singleTransition();
       
   133         return transition && transition->m_nameInPrevious == key.first
       
   134         && transition->m_attributesInPrevious == key.second;
       
   135     }
       
   136     return transitionTable()->contains(key);
       
   137 }
       
   138 
       
   139 inline void Structure::transitionTableRemove(const StructureTransitionTableHash::Key& key, JSCell* specificValue)
       
   140 {
       
   141     if (m_isUsingSingleSlot) {
       
   142         ASSERT(transitionTableContains(key, specificValue));
       
   143         setSingleTransition(0);
       
   144         return;
       
   145     }
       
   146     TransitionTable::iterator find = transitionTable()->find(key);
       
   147     if (!specificValue)
       
   148         find->second.first = 0;
       
   149     else
       
   150         find->second.second = 0;
       
   151     if (!find->second.first && !find->second.second)
       
   152         transitionTable()->remove(find);
       
   153 }
       
   154 
       
   155 inline void Structure::transitionTableAdd(const StructureTransitionTableHash::Key& key, Structure* structure, JSCell* specificValue)
       
   156 {
       
   157     if (m_isUsingSingleSlot) {
       
   158         if (!singleTransition()) {
       
   159             setSingleTransition(structure);
       
   160             return;
       
   161         }
       
   162         Structure* existingTransition = singleTransition();
       
   163         TransitionTable* transitionTable = new TransitionTable;
       
   164         setTransitionTable(transitionTable);
       
   165         if (existingTransition)
       
   166             transitionTableAdd(std::make_pair(existingTransition->m_nameInPrevious.get(), existingTransition->m_attributesInPrevious), existingTransition, existingTransition->m_specificValueInPrevious);
       
   167     }
       
   168     if (!specificValue) {
       
   169         TransitionTable::iterator find = transitionTable()->find(key);
       
   170         if (find == transitionTable()->end())
       
   171             transitionTable()->add(key, Transition(structure, static_cast<Structure*>(0)));
       
   172         else
       
   173             find->second.first = structure;
       
   174     } else {
       
   175         // If we're adding a transition to a specific value, then there cannot be
       
   176         // an existing transition
       
   177         ASSERT(!transitionTable()->contains(key));
       
   178         transitionTable()->add(key, Transition(static_cast<Structure*>(0), structure));
       
   179     }
       
   180 }
       
   181 
       
   182 void Structure::dumpStatistics()
       
   183 {
       
   184 #if DUMP_STRUCTURE_ID_STATISTICS
       
   185     unsigned numberLeaf = 0;
       
   186     unsigned numberUsingSingleSlot = 0;
       
   187     unsigned numberSingletons = 0;
       
   188     unsigned numberWithPropertyMaps = 0;
       
   189     unsigned totalPropertyMapsSize = 0;
       
   190 
       
   191     HashSet<Structure*>::const_iterator end = liveStructureSet.end();
       
   192     for (HashSet<Structure*>::const_iterator it = liveStructureSet.begin(); it != end; ++it) {
       
   193         Structure* structure = *it;
       
   194         if (structure->m_usingSingleTransitionSlot) {
       
   195             if (!structure->m_transitions.singleTransition)
       
   196                 ++numberLeaf;
       
   197             else
       
   198                 ++numberUsingSingleSlot;
       
   199 
       
   200            if (!structure->m_previous && !structure->m_transitions.singleTransition)
       
   201                 ++numberSingletons;
       
   202         }
       
   203 
       
   204         if (structure->m_propertyTable) {
       
   205             ++numberWithPropertyMaps;
       
   206             totalPropertyMapsSize += PropertyMapHashTable::allocationSize(structure->m_propertyTable->size);
       
   207             if (structure->m_propertyTable->deletedOffsets)
       
   208                 totalPropertyMapsSize += (structure->m_propertyTable->deletedOffsets->capacity() * sizeof(unsigned)); 
       
   209         }
       
   210     }
       
   211 
       
   212     printf("Number of live Structures: %d\n", liveStructureSet.size());
       
   213     printf("Number of Structures using the single item optimization for transition map: %d\n", numberUsingSingleSlot);
       
   214     printf("Number of Structures that are leaf nodes: %d\n", numberLeaf);
       
   215     printf("Number of Structures that singletons: %d\n", numberSingletons);
       
   216     printf("Number of Structures with PropertyMaps: %d\n", numberWithPropertyMaps);
       
   217 
       
   218     printf("Size of a single Structures: %d\n", static_cast<unsigned>(sizeof(Structure)));
       
   219     printf("Size of sum of all property maps: %d\n", totalPropertyMapsSize);
       
   220     printf("Size of average of all property maps: %f\n", static_cast<double>(totalPropertyMapsSize) / static_cast<double>(liveStructureSet.size()));
       
   221 #else
       
   222     printf("Dumping Structure statistics is not enabled.\n");
       
   223 #endif
       
   224 }
       
   225 
       
   226 Structure::Structure(JSValue prototype, const TypeInfo& typeInfo, unsigned anonymousSlotCount)
       
   227     : m_typeInfo(typeInfo)
       
   228     , m_prototype(prototype)
       
   229     , m_specificValueInPrevious(0)
       
   230     , m_propertyTable(0)
       
   231     , m_propertyStorageCapacity(JSObject::inlineStorageCapacity)
       
   232     , m_offset(noOffset)
       
   233     , m_dictionaryKind(NoneDictionaryKind)
       
   234     , m_isPinnedPropertyTable(false)
       
   235     , m_hasGetterSetterProperties(false)
       
   236     , m_attributesInPrevious(0)
       
   237     , m_specificFunctionThrashCount(0)
       
   238     , m_anonymousSlotCount(anonymousSlotCount)
       
   239     , m_isUsingSingleSlot(true)
       
   240 {
       
   241     m_transitions.m_singleTransition = 0;
       
   242 
       
   243     ASSERT(m_prototype);
       
   244     ASSERT(m_prototype.isObject() || m_prototype.isNull());
       
   245 
       
   246 #ifndef NDEBUG
       
   247 #if ENABLE(JSC_MULTIPLE_THREADS)
       
   248     MutexLocker protect(ignoreSetMutex);
       
   249 #endif
       
   250     if (shouldIgnoreLeaks)
       
   251         ignoreSet.add(this);
       
   252     else
       
   253         structureCounter.increment();
       
   254 #endif
       
   255 
       
   256 #if DUMP_STRUCTURE_ID_STATISTICS
       
   257     liveStructureSet.add(this);
       
   258 #endif
       
   259 }
       
   260 
       
   261 Structure::~Structure()
       
   262 {
       
   263     if (m_previous) {
       
   264         ASSERT(m_nameInPrevious);
       
   265         m_previous->transitionTableRemove(make_pair(m_nameInPrevious.get(), m_attributesInPrevious), m_specificValueInPrevious);
       
   266 
       
   267     }
       
   268     ASSERT(!m_enumerationCache.hasDeadObject());
       
   269 
       
   270     if (m_propertyTable) {
       
   271         unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount;
       
   272         for (unsigned i = 1; i <= entryCount; i++) {
       
   273             if (UString::Rep* key = m_propertyTable->entries()[i].key)
       
   274                 key->deref();
       
   275         }
       
   276 
       
   277         delete m_propertyTable->deletedOffsets;
       
   278         fastFree(m_propertyTable);
       
   279     }
       
   280 
       
   281     if (!m_isUsingSingleSlot)
       
   282         delete transitionTable();
       
   283 
       
   284 #ifndef NDEBUG
       
   285 #if ENABLE(JSC_MULTIPLE_THREADS)
       
   286     MutexLocker protect(ignoreSetMutex);
       
   287 #endif
       
   288     HashSet<Structure*>::iterator it = ignoreSet.find(this);
       
   289     if (it != ignoreSet.end())
       
   290         ignoreSet.remove(it);
       
   291     else
       
   292         structureCounter.decrement();
       
   293 #endif
       
   294 
       
   295 #if DUMP_STRUCTURE_ID_STATISTICS
       
   296     liveStructureSet.remove(this);
       
   297 #endif
       
   298 }
       
   299 
       
   300 void Structure::startIgnoringLeaks()
       
   301 {
       
   302 #ifndef NDEBUG
       
   303     shouldIgnoreLeaks = true;
       
   304 #endif
       
   305 }
       
   306 
       
   307 void Structure::stopIgnoringLeaks()
       
   308 {
       
   309 #ifndef NDEBUG
       
   310     shouldIgnoreLeaks = false;
       
   311 #endif
       
   312 }
       
   313 
       
   314 static bool isPowerOf2(unsigned v)
       
   315 {
       
   316     // Taken from http://www.cs.utk.edu/~vose/c-stuff/bithacks.html
       
   317     
       
   318     return !(v & (v - 1)) && v;
       
   319 }
       
   320 
       
   321 static unsigned nextPowerOf2(unsigned v)
       
   322 {
       
   323     // Taken from http://www.cs.utk.edu/~vose/c-stuff/bithacks.html
       
   324     // Devised by Sean Anderson, Sepember 14, 2001
       
   325 
       
   326     v--;
       
   327     v |= v >> 1;
       
   328     v |= v >> 2;
       
   329     v |= v >> 4;
       
   330     v |= v >> 8;
       
   331     v |= v >> 16;
       
   332     v++;
       
   333 
       
   334     return v;
       
   335 }
       
   336 
       
   337 static unsigned sizeForKeyCount(size_t keyCount)
       
   338 {
       
   339     if (keyCount == notFound)
       
   340         return newTableSize;
       
   341 
       
   342     if (keyCount < 8)
       
   343         return newTableSize;
       
   344 
       
   345     if (isPowerOf2(keyCount))
       
   346         return keyCount * 4;
       
   347 
       
   348     return nextPowerOf2(keyCount) * 2;
       
   349 }
       
   350 
       
   351 void Structure::materializePropertyMap()
       
   352 {
       
   353     ASSERT(!m_propertyTable);
       
   354 
       
   355     Vector<Structure*, 8> structures;
       
   356     structures.append(this);
       
   357 
       
   358     Structure* structure = this;
       
   359 
       
   360     // Search for the last Structure with a property table. 
       
   361     while ((structure = structure->previousID())) {
       
   362         if (structure->m_isPinnedPropertyTable) {
       
   363             ASSERT(structure->m_propertyTable);
       
   364             ASSERT(!structure->m_previous);
       
   365 
       
   366             m_propertyTable = structure->copyPropertyTable();
       
   367             break;
       
   368         }
       
   369 
       
   370         structures.append(structure);
       
   371     }
       
   372 
       
   373     if (!m_propertyTable)
       
   374         createPropertyMapHashTable(sizeForKeyCount(m_offset + 1));
       
   375     else {
       
   376         if (sizeForKeyCount(m_offset + 1) > m_propertyTable->size)
       
   377             rehashPropertyMapHashTable(sizeForKeyCount(m_offset + 1)); // This could be made more efficient by combining with the copy above. 
       
   378     }
       
   379 
       
   380     for (ptrdiff_t i = structures.size() - 2; i >= 0; --i) {
       
   381         structure = structures[i];
       
   382         structure->m_nameInPrevious->ref();
       
   383         PropertyMapEntry entry(structure->m_nameInPrevious.get(), m_anonymousSlotCount + structure->m_offset, structure->m_attributesInPrevious, structure->m_specificValueInPrevious, ++m_propertyTable->lastIndexUsed);
       
   384         insertIntoPropertyMapHashTable(entry);
       
   385     }
       
   386 }
       
   387 
       
   388 void Structure::growPropertyStorageCapacity()
       
   389 {
       
   390     if (m_propertyStorageCapacity == JSObject::inlineStorageCapacity)
       
   391         m_propertyStorageCapacity = JSObject::nonInlineBaseStorageCapacity;
       
   392     else
       
   393         m_propertyStorageCapacity *= 2;
       
   394 }
       
   395 
       
   396 void Structure::despecifyDictionaryFunction(const Identifier& propertyName)
       
   397 {
       
   398     const UString::Rep* rep = propertyName._ustring.rep();
       
   399 
       
   400     materializePropertyMapIfNecessary();
       
   401 
       
   402     ASSERT(isDictionary());
       
   403     ASSERT(m_propertyTable);
       
   404 
       
   405     unsigned i = rep->existingHash();
       
   406 
       
   407 #if DUMP_PROPERTYMAP_STATS
       
   408     ++numProbes;
       
   409 #endif
       
   410 
       
   411     unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
       
   412     ASSERT(entryIndex != emptyEntryIndex);
       
   413 
       
   414     if (rep == m_propertyTable->entries()[entryIndex - 1].key) {
       
   415         m_propertyTable->entries()[entryIndex - 1].specificValue = 0;
       
   416         return;
       
   417     }
       
   418 
       
   419 #if DUMP_PROPERTYMAP_STATS
       
   420     ++numCollisions;
       
   421 #endif
       
   422 
       
   423     unsigned k = 1 | doubleHash(rep->existingHash());
       
   424 
       
   425     while (1) {
       
   426         i += k;
       
   427 
       
   428 #if DUMP_PROPERTYMAP_STATS
       
   429         ++numRehashes;
       
   430 #endif
       
   431 
       
   432         entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
       
   433         ASSERT(entryIndex != emptyEntryIndex);
       
   434 
       
   435         if (rep == m_propertyTable->entries()[entryIndex - 1].key) {
       
   436             m_propertyTable->entries()[entryIndex - 1].specificValue = 0;
       
   437             return;
       
   438         }
       
   439     }
       
   440 }
       
   441 
       
   442 PassRefPtr<Structure> Structure::addPropertyTransitionToExistingStructure(Structure* structure, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset)
       
   443 {
       
   444     ASSERT(!structure->isDictionary());
       
   445     ASSERT(structure->typeInfo().type() == ObjectType);
       
   446 
       
   447     if (Structure* existingTransition = structure->transitionTableGet(make_pair(propertyName.ustring().rep(), attributes), specificValue)) {
       
   448         ASSERT(existingTransition->m_offset != noOffset);
       
   449         offset = existingTransition->m_offset + existingTransition->m_anonymousSlotCount;
       
   450         ASSERT(offset >= structure->m_anonymousSlotCount);
       
   451         ASSERT(structure->m_anonymousSlotCount == existingTransition->m_anonymousSlotCount);
       
   452         return existingTransition;
       
   453     }
       
   454 
       
   455     return 0;
       
   456 }
       
   457 
       
   458 PassRefPtr<Structure> Structure::addPropertyTransition(Structure* structure, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset)
       
   459 {
       
   460     ASSERT(!structure->isDictionary());
       
   461     ASSERT(structure->typeInfo().type() == ObjectType);
       
   462     ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure, propertyName, attributes, specificValue, offset));
       
   463     
       
   464     if (structure->m_specificFunctionThrashCount == maxSpecificFunctionThrashCount)
       
   465         specificValue = 0;
       
   466 
       
   467     if (structure->transitionCount() > s_maxTransitionLength) {
       
   468         RefPtr<Structure> transition = toCacheableDictionaryTransition(structure);
       
   469         ASSERT(structure != transition);
       
   470         offset = transition->put(propertyName, attributes, specificValue);
       
   471         ASSERT(offset >= structure->m_anonymousSlotCount);
       
   472         ASSERT(structure->m_anonymousSlotCount == transition->m_anonymousSlotCount);
       
   473         if (transition->propertyStorageSize() > transition->propertyStorageCapacity())
       
   474             transition->growPropertyStorageCapacity();
       
   475         return transition.release();
       
   476     }
       
   477 
       
   478     RefPtr<Structure> transition = create(structure->m_prototype, structure->typeInfo(), structure->anonymousSlotCount());
       
   479 
       
   480     transition->m_cachedPrototypeChain = structure->m_cachedPrototypeChain;
       
   481     transition->m_previous = structure;
       
   482     transition->m_nameInPrevious = propertyName.ustring().rep();
       
   483     transition->m_attributesInPrevious = attributes;
       
   484     transition->m_specificValueInPrevious = specificValue;
       
   485     transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
       
   486     transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties;
       
   487     transition->m_hasNonEnumerableProperties = structure->m_hasNonEnumerableProperties;
       
   488     transition->m_specificFunctionThrashCount = structure->m_specificFunctionThrashCount;
       
   489 
       
   490     if (structure->m_propertyTable) {
       
   491         if (structure->m_isPinnedPropertyTable)
       
   492             transition->m_propertyTable = structure->copyPropertyTable();
       
   493         else {
       
   494             transition->m_propertyTable = structure->m_propertyTable;
       
   495             structure->m_propertyTable = 0;
       
   496         }
       
   497     } else {
       
   498         if (structure->m_previous)
       
   499             transition->materializePropertyMap();
       
   500         else
       
   501             transition->createPropertyMapHashTable();
       
   502     }
       
   503 
       
   504     offset = transition->put(propertyName, attributes, specificValue);
       
   505     ASSERT(offset >= structure->m_anonymousSlotCount);
       
   506     ASSERT(structure->m_anonymousSlotCount == transition->m_anonymousSlotCount);
       
   507     if (transition->propertyStorageSize() > transition->propertyStorageCapacity())
       
   508         transition->growPropertyStorageCapacity();
       
   509 
       
   510     transition->m_offset = offset - structure->m_anonymousSlotCount;
       
   511     ASSERT(structure->anonymousSlotCount() == transition->anonymousSlotCount());
       
   512     structure->transitionTableAdd(make_pair(propertyName.ustring().rep(), attributes), transition.get(), specificValue);
       
   513     return transition.release();
       
   514 }
       
   515 
       
   516 PassRefPtr<Structure> Structure::removePropertyTransition(Structure* structure, const Identifier& propertyName, size_t& offset)
       
   517 {
       
   518     ASSERT(!structure->isUncacheableDictionary());
       
   519 
       
   520     RefPtr<Structure> transition = toUncacheableDictionaryTransition(structure);
       
   521 
       
   522     offset = transition->remove(propertyName);
       
   523     ASSERT(offset >= structure->m_anonymousSlotCount);
       
   524     ASSERT(structure->m_anonymousSlotCount == transition->m_anonymousSlotCount);
       
   525 
       
   526     return transition.release();
       
   527 }
       
   528 
       
   529 PassRefPtr<Structure> Structure::changePrototypeTransition(Structure* structure, JSValue prototype)
       
   530 {
       
   531     RefPtr<Structure> transition = create(prototype, structure->typeInfo(), structure->anonymousSlotCount());
       
   532 
       
   533     transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
       
   534     transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties;
       
   535     transition->m_hasNonEnumerableProperties = structure->m_hasNonEnumerableProperties;
       
   536     transition->m_specificFunctionThrashCount = structure->m_specificFunctionThrashCount;
       
   537 
       
   538     // Don't set m_offset, as one can not transition to this.
       
   539 
       
   540     structure->materializePropertyMapIfNecessary();
       
   541     transition->m_propertyTable = structure->copyPropertyTable();
       
   542     transition->m_isPinnedPropertyTable = true;
       
   543     
       
   544     ASSERT(structure->anonymousSlotCount() == transition->anonymousSlotCount());
       
   545     return transition.release();
       
   546 }
       
   547 
       
   548 PassRefPtr<Structure> Structure::despecifyFunctionTransition(Structure* structure, const Identifier& replaceFunction)
       
   549 {
       
   550     ASSERT(structure->m_specificFunctionThrashCount < maxSpecificFunctionThrashCount);
       
   551     RefPtr<Structure> transition = create(structure->storedPrototype(), structure->typeInfo(), structure->anonymousSlotCount());
       
   552 
       
   553     transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
       
   554     transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties;
       
   555     transition->m_hasNonEnumerableProperties = structure->m_hasNonEnumerableProperties;
       
   556     transition->m_specificFunctionThrashCount = structure->m_specificFunctionThrashCount + 1;
       
   557 
       
   558     // Don't set m_offset, as one can not transition to this.
       
   559 
       
   560     structure->materializePropertyMapIfNecessary();
       
   561     transition->m_propertyTable = structure->copyPropertyTable();
       
   562     transition->m_isPinnedPropertyTable = true;
       
   563 
       
   564     if (transition->m_specificFunctionThrashCount == maxSpecificFunctionThrashCount)
       
   565         transition->despecifyAllFunctions();
       
   566     else {
       
   567         bool removed = transition->despecifyFunction(replaceFunction);
       
   568         ASSERT_UNUSED(removed, removed);
       
   569     }
       
   570     
       
   571     ASSERT(structure->anonymousSlotCount() == transition->anonymousSlotCount());
       
   572     return transition.release();
       
   573 }
       
   574 
       
   575 PassRefPtr<Structure> Structure::getterSetterTransition(Structure* structure)
       
   576 {
       
   577     RefPtr<Structure> transition = create(structure->storedPrototype(), structure->typeInfo(), structure->anonymousSlotCount());
       
   578     transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
       
   579     transition->m_hasGetterSetterProperties = transition->m_hasGetterSetterProperties;
       
   580     transition->m_hasNonEnumerableProperties = structure->m_hasNonEnumerableProperties;
       
   581     transition->m_specificFunctionThrashCount = structure->m_specificFunctionThrashCount;
       
   582 
       
   583     // Don't set m_offset, as one can not transition to this.
       
   584 
       
   585     structure->materializePropertyMapIfNecessary();
       
   586     transition->m_propertyTable = structure->copyPropertyTable();
       
   587     transition->m_isPinnedPropertyTable = true;
       
   588     
       
   589     ASSERT(structure->anonymousSlotCount() == transition->anonymousSlotCount());
       
   590     return transition.release();
       
   591 }
       
   592 
       
   593 PassRefPtr<Structure> Structure::toDictionaryTransition(Structure* structure, DictionaryKind kind)
       
   594 {
       
   595     ASSERT(!structure->isUncacheableDictionary());
       
   596     
       
   597     RefPtr<Structure> transition = create(structure->m_prototype, structure->typeInfo(), structure->anonymousSlotCount());
       
   598     transition->m_dictionaryKind = kind;
       
   599     transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
       
   600     transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties;
       
   601     transition->m_hasNonEnumerableProperties = structure->m_hasNonEnumerableProperties;
       
   602     transition->m_specificFunctionThrashCount = structure->m_specificFunctionThrashCount;
       
   603     
       
   604     structure->materializePropertyMapIfNecessary();
       
   605     transition->m_propertyTable = structure->copyPropertyTable();
       
   606     transition->m_isPinnedPropertyTable = true;
       
   607     
       
   608     ASSERT(structure->anonymousSlotCount() == transition->anonymousSlotCount());
       
   609     return transition.release();
       
   610 }
       
   611 
       
   612 PassRefPtr<Structure> Structure::toCacheableDictionaryTransition(Structure* structure)
       
   613 {
       
   614     return toDictionaryTransition(structure, CachedDictionaryKind);
       
   615 }
       
   616 
       
   617 PassRefPtr<Structure> Structure::toUncacheableDictionaryTransition(Structure* structure)
       
   618 {
       
   619     return toDictionaryTransition(structure, UncachedDictionaryKind);
       
   620 }
       
   621 
       
   622 PassRefPtr<Structure> Structure::flattenDictionaryStructure(JSObject* object)
       
   623 {
       
   624     ASSERT(isDictionary());
       
   625     if (isUncacheableDictionary()) {
       
   626         ASSERT(m_propertyTable);
       
   627         Vector<PropertyMapEntry*> sortedPropertyEntries(m_propertyTable->keyCount);
       
   628         PropertyMapEntry** p = sortedPropertyEntries.data();
       
   629         unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount;
       
   630         for (unsigned i = 1; i <= entryCount; i++) {
       
   631             if (m_propertyTable->entries()[i].key)
       
   632                 *p++ = &m_propertyTable->entries()[i];
       
   633         }
       
   634         size_t propertyCount = p - sortedPropertyEntries.data();
       
   635         qsort(sortedPropertyEntries.data(), propertyCount, sizeof(PropertyMapEntry*), comparePropertyMapEntryIndices);
       
   636         sortedPropertyEntries.resize(propertyCount);
       
   637 
       
   638         // We now have the properties currently defined on this object
       
   639         // in the order that they are expected to be in, but we need to
       
   640         // reorder the storage, so we have to copy the current values out
       
   641         Vector<JSValue> values(propertyCount);
       
   642         unsigned anonymousSlotCount = m_anonymousSlotCount;
       
   643         for (unsigned i = 0; i < propertyCount; i++) {
       
   644             PropertyMapEntry* entry = sortedPropertyEntries[i];
       
   645             values[i] = object->getDirectOffset(entry->offset);
       
   646             // Update property table to have the new property offsets
       
   647             entry->offset = anonymousSlotCount + i;
       
   648             entry->index = i;
       
   649         }
       
   650         
       
   651         // Copy the original property values into their final locations
       
   652         for (unsigned i = 0; i < propertyCount; i++)
       
   653             object->putDirectOffset(anonymousSlotCount + i, values[i]);
       
   654 
       
   655         if (m_propertyTable->deletedOffsets) {
       
   656             delete m_propertyTable->deletedOffsets;
       
   657             m_propertyTable->deletedOffsets = 0;
       
   658         }
       
   659     }
       
   660 
       
   661     m_dictionaryKind = NoneDictionaryKind;
       
   662     return this;
       
   663 }
       
   664 
       
   665 size_t Structure::addPropertyWithoutTransition(const Identifier& propertyName, unsigned attributes, JSCell* specificValue)
       
   666 {
       
   667     ASSERT(!m_enumerationCache);
       
   668 
       
   669     if (m_specificFunctionThrashCount == maxSpecificFunctionThrashCount)
       
   670         specificValue = 0;
       
   671 
       
   672     materializePropertyMapIfNecessary();
       
   673 
       
   674     m_isPinnedPropertyTable = true;
       
   675 
       
   676     size_t offset = put(propertyName, attributes, specificValue);
       
   677     ASSERT(offset >= m_anonymousSlotCount);
       
   678     if (propertyStorageSize() > propertyStorageCapacity())
       
   679         growPropertyStorageCapacity();
       
   680     return offset;
       
   681 }
       
   682 
       
   683 size_t Structure::removePropertyWithoutTransition(const Identifier& propertyName)
       
   684 {
       
   685     ASSERT(isUncacheableDictionary());
       
   686     ASSERT(!m_enumerationCache);
       
   687 
       
   688     materializePropertyMapIfNecessary();
       
   689 
       
   690     m_isPinnedPropertyTable = true;
       
   691     size_t offset = remove(propertyName);
       
   692     ASSERT(offset >= m_anonymousSlotCount);
       
   693     return offset;
       
   694 }
       
   695 
       
   696 #if DUMP_PROPERTYMAP_STATS
       
   697 
       
   698 static int numProbes;
       
   699 static int numCollisions;
       
   700 static int numRehashes;
       
   701 static int numRemoves;
       
   702 
       
   703 struct PropertyMapStatisticsExitLogger {
       
   704     ~PropertyMapStatisticsExitLogger();
       
   705 };
       
   706 
       
   707 static PropertyMapStatisticsExitLogger logger;
       
   708 
       
   709 PropertyMapStatisticsExitLogger::~PropertyMapStatisticsExitLogger()
       
   710 {
       
   711     printf("\nJSC::PropertyMap statistics\n\n");
       
   712     printf("%d probes\n", numProbes);
       
   713     printf("%d collisions (%.1f%%)\n", numCollisions, 100.0 * numCollisions / numProbes);
       
   714     printf("%d rehashes\n", numRehashes);
       
   715     printf("%d removes\n", numRemoves);
       
   716 }
       
   717 
       
   718 #endif
       
   719 
       
   720 static const unsigned deletedSentinelIndex = 1;
       
   721 
       
   722 #if !DO_PROPERTYMAP_CONSTENCY_CHECK
       
   723 
       
   724 inline void Structure::checkConsistency()
       
   725 {
       
   726 }
       
   727 
       
   728 #endif
       
   729 
       
   730 PropertyMapHashTable* Structure::copyPropertyTable()
       
   731 {
       
   732     if (!m_propertyTable)
       
   733         return 0;
       
   734 
       
   735     size_t tableSize = PropertyMapHashTable::allocationSize(m_propertyTable->size);
       
   736     PropertyMapHashTable* newTable = static_cast<PropertyMapHashTable*>(fastMalloc(tableSize));
       
   737     memcpy(newTable, m_propertyTable, tableSize);
       
   738 
       
   739     unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount;
       
   740     for (unsigned i = 1; i <= entryCount; ++i) {
       
   741         if (UString::Rep* key = newTable->entries()[i].key)
       
   742             key->ref();
       
   743     }
       
   744 
       
   745     // Copy the deletedOffsets vector.
       
   746     if (m_propertyTable->deletedOffsets)
       
   747         newTable->deletedOffsets = new Vector<unsigned>(*m_propertyTable->deletedOffsets);
       
   748 
       
   749     return newTable;
       
   750 }
       
   751 
       
   752 size_t Structure::get(const UString::Rep* rep, unsigned& attributes, JSCell*& specificValue)
       
   753 {
       
   754     materializePropertyMapIfNecessary();
       
   755     if (!m_propertyTable)
       
   756         return notFound;
       
   757 
       
   758     unsigned i = rep->existingHash();
       
   759 
       
   760 #if DUMP_PROPERTYMAP_STATS
       
   761     ++numProbes;
       
   762 #endif
       
   763 
       
   764     unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
       
   765     if (entryIndex == emptyEntryIndex)
       
   766         return notFound;
       
   767 
       
   768     if (rep == m_propertyTable->entries()[entryIndex - 1].key) {
       
   769         attributes = m_propertyTable->entries()[entryIndex - 1].attributes;
       
   770         specificValue = m_propertyTable->entries()[entryIndex - 1].specificValue;
       
   771         ASSERT(m_propertyTable->entries()[entryIndex - 1].offset >= m_anonymousSlotCount);
       
   772         return m_propertyTable->entries()[entryIndex - 1].offset;
       
   773     }
       
   774 
       
   775 #if DUMP_PROPERTYMAP_STATS
       
   776     ++numCollisions;
       
   777 #endif
       
   778 
       
   779     unsigned k = 1 | doubleHash(rep->existingHash());
       
   780 
       
   781     while (1) {
       
   782         i += k;
       
   783 
       
   784 #if DUMP_PROPERTYMAP_STATS
       
   785         ++numRehashes;
       
   786 #endif
       
   787 
       
   788         entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
       
   789         if (entryIndex == emptyEntryIndex)
       
   790             return notFound;
       
   791 
       
   792         if (rep == m_propertyTable->entries()[entryIndex - 1].key) {
       
   793             attributes = m_propertyTable->entries()[entryIndex - 1].attributes;
       
   794             specificValue = m_propertyTable->entries()[entryIndex - 1].specificValue;
       
   795             ASSERT(m_propertyTable->entries()[entryIndex - 1].offset >= m_anonymousSlotCount);
       
   796             return m_propertyTable->entries()[entryIndex - 1].offset;
       
   797         }
       
   798     }
       
   799 }
       
   800 
       
   801 bool Structure::despecifyFunction(const Identifier& propertyName)
       
   802 {
       
   803     ASSERT(!propertyName.isNull());
       
   804 
       
   805     materializePropertyMapIfNecessary();
       
   806     if (!m_propertyTable)
       
   807         return false;
       
   808 
       
   809     UString::Rep* rep = propertyName._ustring.rep();
       
   810 
       
   811     unsigned i = rep->existingHash();
       
   812 
       
   813 #if DUMP_PROPERTYMAP_STATS
       
   814     ++numProbes;
       
   815 #endif
       
   816 
       
   817     unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
       
   818     if (entryIndex == emptyEntryIndex)
       
   819         return false;
       
   820 
       
   821     if (rep == m_propertyTable->entries()[entryIndex - 1].key) {
       
   822         ASSERT(m_propertyTable->entries()[entryIndex - 1].specificValue);
       
   823         m_propertyTable->entries()[entryIndex - 1].specificValue = 0;
       
   824         return true;
       
   825     }
       
   826 
       
   827 #if DUMP_PROPERTYMAP_STATS
       
   828     ++numCollisions;
       
   829 #endif
       
   830 
       
   831     unsigned k = 1 | doubleHash(rep->existingHash());
       
   832 
       
   833     while (1) {
       
   834         i += k;
       
   835 
       
   836 #if DUMP_PROPERTYMAP_STATS
       
   837         ++numRehashes;
       
   838 #endif
       
   839 
       
   840         entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
       
   841         if (entryIndex == emptyEntryIndex)
       
   842             return false;
       
   843 
       
   844         if (rep == m_propertyTable->entries()[entryIndex - 1].key) {
       
   845             ASSERT(m_propertyTable->entries()[entryIndex - 1].specificValue);
       
   846             m_propertyTable->entries()[entryIndex - 1].specificValue = 0;
       
   847             return true;
       
   848         }
       
   849     }
       
   850 }
       
   851 
       
   852 void Structure::despecifyAllFunctions()
       
   853 {
       
   854     materializePropertyMapIfNecessary();
       
   855     if (!m_propertyTable)
       
   856         return;
       
   857     
       
   858     unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount;
       
   859     for (unsigned i = 1; i <= entryCount; ++i)
       
   860         m_propertyTable->entries()[i].specificValue = 0;
       
   861 }
       
   862 
       
   863 size_t Structure::put(const Identifier& propertyName, unsigned attributes, JSCell* specificValue)
       
   864 {
       
   865     ASSERT(!propertyName.isNull());
       
   866     ASSERT(get(propertyName) == notFound);
       
   867 
       
   868     checkConsistency();
       
   869 
       
   870     if (attributes & DontEnum)
       
   871         m_hasNonEnumerableProperties = true;
       
   872 
       
   873     UString::Rep* rep = propertyName._ustring.rep();
       
   874 
       
   875     if (!m_propertyTable)
       
   876         createPropertyMapHashTable();
       
   877 
       
   878     // FIXME: Consider a fast case for tables with no deleted sentinels.
       
   879 
       
   880     unsigned i = rep->existingHash();
       
   881     unsigned k = 0;
       
   882     bool foundDeletedElement = false;
       
   883     unsigned deletedElementIndex = 0; // initialize to make the compiler happy
       
   884 
       
   885 #if DUMP_PROPERTYMAP_STATS
       
   886     ++numProbes;
       
   887 #endif
       
   888 
       
   889     while (1) {
       
   890         unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
       
   891         if (entryIndex == emptyEntryIndex)
       
   892             break;
       
   893 
       
   894         if (entryIndex == deletedSentinelIndex) {
       
   895             // If we find a deleted-element sentinel, remember it for use later.
       
   896             if (!foundDeletedElement) {
       
   897                 foundDeletedElement = true;
       
   898                 deletedElementIndex = i;
       
   899             }
       
   900         }
       
   901 
       
   902         if (k == 0) {
       
   903             k = 1 | doubleHash(rep->existingHash());
       
   904 #if DUMP_PROPERTYMAP_STATS
       
   905             ++numCollisions;
       
   906 #endif
       
   907         }
       
   908 
       
   909         i += k;
       
   910 
       
   911 #if DUMP_PROPERTYMAP_STATS
       
   912         ++numRehashes;
       
   913 #endif
       
   914     }
       
   915 
       
   916     // Figure out which entry to use.
       
   917     unsigned entryIndex = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount + 2;
       
   918     if (foundDeletedElement) {
       
   919         i = deletedElementIndex;
       
   920         --m_propertyTable->deletedSentinelCount;
       
   921 
       
   922         // Since we're not making the table bigger, we can't use the entry one past
       
   923         // the end that we were planning on using, so search backwards for the empty
       
   924         // slot that we can use. We know it will be there because we did at least one
       
   925         // deletion in the past that left an entry empty.
       
   926         while (m_propertyTable->entries()[--entryIndex - 1].key) { }
       
   927     }
       
   928 
       
   929     // Create a new hash table entry.
       
   930     m_propertyTable->entryIndices[i & m_propertyTable->sizeMask] = entryIndex;
       
   931 
       
   932     // Create a new hash table entry.
       
   933     rep->ref();
       
   934     m_propertyTable->entries()[entryIndex - 1].key = rep;
       
   935     m_propertyTable->entries()[entryIndex - 1].attributes = attributes;
       
   936     m_propertyTable->entries()[entryIndex - 1].specificValue = specificValue;
       
   937     m_propertyTable->entries()[entryIndex - 1].index = ++m_propertyTable->lastIndexUsed;
       
   938 
       
   939     unsigned newOffset;
       
   940     if (m_propertyTable->deletedOffsets && !m_propertyTable->deletedOffsets->isEmpty()) {
       
   941         newOffset = m_propertyTable->deletedOffsets->last();
       
   942         m_propertyTable->deletedOffsets->removeLast();
       
   943     } else
       
   944         newOffset = m_propertyTable->keyCount + m_anonymousSlotCount;
       
   945     m_propertyTable->entries()[entryIndex - 1].offset = newOffset;
       
   946     
       
   947     ASSERT(newOffset >= m_anonymousSlotCount);
       
   948     ++m_propertyTable->keyCount;
       
   949 
       
   950     if ((m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount) * 2 >= m_propertyTable->size)
       
   951         expandPropertyMapHashTable();
       
   952 
       
   953     checkConsistency();
       
   954     return newOffset;
       
   955 }
       
   956 
       
   957 bool Structure::hasTransition(UString::Rep* rep, unsigned attributes)
       
   958 {
       
   959     return transitionTableHasTransition(make_pair(rep, attributes));
       
   960 }
       
   961 
       
   962 size_t Structure::remove(const Identifier& propertyName)
       
   963 {
       
   964     ASSERT(!propertyName.isNull());
       
   965 
       
   966     checkConsistency();
       
   967 
       
   968     UString::Rep* rep = propertyName._ustring.rep();
       
   969 
       
   970     if (!m_propertyTable)
       
   971         return notFound;
       
   972 
       
   973 #if DUMP_PROPERTYMAP_STATS
       
   974     ++numProbes;
       
   975     ++numRemoves;
       
   976 #endif
       
   977 
       
   978     // Find the thing to remove.
       
   979     unsigned i = rep->existingHash();
       
   980     unsigned k = 0;
       
   981     unsigned entryIndex;
       
   982     UString::Rep* key = 0;
       
   983     while (1) {
       
   984         entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
       
   985         if (entryIndex == emptyEntryIndex)
       
   986             return notFound;
       
   987 
       
   988         key = m_propertyTable->entries()[entryIndex - 1].key;
       
   989         if (rep == key)
       
   990             break;
       
   991 
       
   992         if (k == 0) {
       
   993             k = 1 | doubleHash(rep->existingHash());
       
   994 #if DUMP_PROPERTYMAP_STATS
       
   995             ++numCollisions;
       
   996 #endif
       
   997         }
       
   998 
       
   999         i += k;
       
  1000 
       
  1001 #if DUMP_PROPERTYMAP_STATS
       
  1002         ++numRehashes;
       
  1003 #endif
       
  1004     }
       
  1005 
       
  1006     // Replace this one element with the deleted sentinel. Also clear out
       
  1007     // the entry so we can iterate all the entries as needed.
       
  1008     m_propertyTable->entryIndices[i & m_propertyTable->sizeMask] = deletedSentinelIndex;
       
  1009 
       
  1010     size_t offset = m_propertyTable->entries()[entryIndex - 1].offset;
       
  1011     ASSERT(offset >= m_anonymousSlotCount);
       
  1012 
       
  1013     key->deref();
       
  1014     m_propertyTable->entries()[entryIndex - 1].key = 0;
       
  1015     m_propertyTable->entries()[entryIndex - 1].attributes = 0;
       
  1016     m_propertyTable->entries()[entryIndex - 1].specificValue = 0;
       
  1017     m_propertyTable->entries()[entryIndex - 1].offset = 0;
       
  1018 
       
  1019     if (!m_propertyTable->deletedOffsets)
       
  1020         m_propertyTable->deletedOffsets = new Vector<unsigned>;
       
  1021     m_propertyTable->deletedOffsets->append(offset);
       
  1022 
       
  1023     ASSERT(m_propertyTable->keyCount >= 1);
       
  1024     --m_propertyTable->keyCount;
       
  1025     ++m_propertyTable->deletedSentinelCount;
       
  1026 
       
  1027     if (m_propertyTable->deletedSentinelCount * 4 >= m_propertyTable->size)
       
  1028         rehashPropertyMapHashTable();
       
  1029 
       
  1030     checkConsistency();
       
  1031     return offset;
       
  1032 }
       
  1033 
       
  1034 void Structure::insertIntoPropertyMapHashTable(const PropertyMapEntry& entry)
       
  1035 {
       
  1036     ASSERT(m_propertyTable);
       
  1037     ASSERT(entry.offset >= m_anonymousSlotCount);
       
  1038     unsigned i = entry.key->existingHash();
       
  1039     unsigned k = 0;
       
  1040 
       
  1041 #if DUMP_PROPERTYMAP_STATS
       
  1042     ++numProbes;
       
  1043 #endif
       
  1044 
       
  1045     while (1) {
       
  1046         unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
       
  1047         if (entryIndex == emptyEntryIndex)
       
  1048             break;
       
  1049 
       
  1050         if (k == 0) {
       
  1051             k = 1 | doubleHash(entry.key->existingHash());
       
  1052 #if DUMP_PROPERTYMAP_STATS
       
  1053             ++numCollisions;
       
  1054 #endif
       
  1055         }
       
  1056 
       
  1057         i += k;
       
  1058 
       
  1059 #if DUMP_PROPERTYMAP_STATS
       
  1060         ++numRehashes;
       
  1061 #endif
       
  1062     }
       
  1063 
       
  1064     unsigned entryIndex = m_propertyTable->keyCount + 2;
       
  1065     m_propertyTable->entryIndices[i & m_propertyTable->sizeMask] = entryIndex;
       
  1066     m_propertyTable->entries()[entryIndex - 1] = entry;
       
  1067 
       
  1068     ++m_propertyTable->keyCount;
       
  1069 }
       
  1070 
       
  1071 void Structure::createPropertyMapHashTable()
       
  1072 {
       
  1073     ASSERT(sizeForKeyCount(7) == newTableSize);
       
  1074     createPropertyMapHashTable(newTableSize);
       
  1075 }
       
  1076 
       
  1077 void Structure::createPropertyMapHashTable(unsigned newTableSize)
       
  1078 {
       
  1079     ASSERT(!m_propertyTable);
       
  1080     ASSERT(isPowerOf2(newTableSize));
       
  1081 
       
  1082     checkConsistency();
       
  1083 
       
  1084     m_propertyTable = static_cast<PropertyMapHashTable*>(fastZeroedMalloc(PropertyMapHashTable::allocationSize(newTableSize)));
       
  1085     m_propertyTable->size = newTableSize;
       
  1086     m_propertyTable->sizeMask = newTableSize - 1;
       
  1087 
       
  1088     checkConsistency();
       
  1089 }
       
  1090 
       
  1091 void Structure::expandPropertyMapHashTable()
       
  1092 {
       
  1093     ASSERT(m_propertyTable);
       
  1094     rehashPropertyMapHashTable(m_propertyTable->size * 2);
       
  1095 }
       
  1096 
       
  1097 void Structure::rehashPropertyMapHashTable()
       
  1098 {
       
  1099     ASSERT(m_propertyTable);
       
  1100     ASSERT(m_propertyTable->size);
       
  1101     rehashPropertyMapHashTable(m_propertyTable->size);
       
  1102 }
       
  1103 
       
  1104 void Structure::rehashPropertyMapHashTable(unsigned newTableSize)
       
  1105 {
       
  1106     ASSERT(m_propertyTable);
       
  1107     ASSERT(isPowerOf2(newTableSize));
       
  1108 
       
  1109     checkConsistency();
       
  1110 
       
  1111     PropertyMapHashTable* oldTable = m_propertyTable;
       
  1112 
       
  1113     m_propertyTable = static_cast<PropertyMapHashTable*>(fastZeroedMalloc(PropertyMapHashTable::allocationSize(newTableSize)));
       
  1114     m_propertyTable->size = newTableSize;
       
  1115     m_propertyTable->sizeMask = newTableSize - 1;
       
  1116 
       
  1117     unsigned lastIndexUsed = 0;
       
  1118     unsigned entryCount = oldTable->keyCount + oldTable->deletedSentinelCount;
       
  1119     for (unsigned i = 1; i <= entryCount; ++i) {
       
  1120         if (oldTable->entries()[i].key) {
       
  1121             lastIndexUsed = max(oldTable->entries()[i].index, lastIndexUsed);
       
  1122             insertIntoPropertyMapHashTable(oldTable->entries()[i]);
       
  1123         }
       
  1124     }
       
  1125     m_propertyTable->lastIndexUsed = lastIndexUsed;
       
  1126     m_propertyTable->deletedOffsets = oldTable->deletedOffsets;
       
  1127 
       
  1128     fastFree(oldTable);
       
  1129 
       
  1130     checkConsistency();
       
  1131 }
       
  1132 
       
  1133 int comparePropertyMapEntryIndices(const void* a, const void* b)
       
  1134 {
       
  1135     unsigned ia = static_cast<PropertyMapEntry* const*>(a)[0]->index;
       
  1136     unsigned ib = static_cast<PropertyMapEntry* const*>(b)[0]->index;
       
  1137     if (ia < ib)
       
  1138         return -1;
       
  1139     if (ia > ib)
       
  1140         return +1;
       
  1141     return 0;
       
  1142 }
       
  1143 
       
  1144 void Structure::getPropertyNames(PropertyNameArray& propertyNames, EnumerationMode mode)
       
  1145 {
       
  1146     materializePropertyMapIfNecessary();
       
  1147     if (!m_propertyTable)
       
  1148         return;
       
  1149 
       
  1150     if (m_propertyTable->keyCount < tinyMapThreshold) {
       
  1151         PropertyMapEntry* a[tinyMapThreshold];
       
  1152         int i = 0;
       
  1153         unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount;
       
  1154         for (unsigned k = 1; k <= entryCount; k++) {
       
  1155             ASSERT(m_hasNonEnumerableProperties || !(m_propertyTable->entries()[k].attributes & DontEnum));
       
  1156             if (m_propertyTable->entries()[k].key && (!(m_propertyTable->entries()[k].attributes & DontEnum) || (mode == IncludeDontEnumProperties))) {
       
  1157                 PropertyMapEntry* value = &m_propertyTable->entries()[k];
       
  1158                 int j;
       
  1159                 for (j = i - 1; j >= 0 && a[j]->index > value->index; --j)
       
  1160                     a[j + 1] = a[j];
       
  1161                 a[j + 1] = value;
       
  1162                 ++i;
       
  1163             }
       
  1164         }
       
  1165         if (!propertyNames.size()) {
       
  1166             for (int k = 0; k < i; ++k)
       
  1167                 propertyNames.addKnownUnique(a[k]->key);
       
  1168         } else {
       
  1169             for (int k = 0; k < i; ++k)
       
  1170                 propertyNames.add(a[k]->key);
       
  1171         }
       
  1172 
       
  1173         return;
       
  1174     }
       
  1175 
       
  1176     // Allocate a buffer to use to sort the keys.
       
  1177     Vector<PropertyMapEntry*, smallMapThreshold> sortedEnumerables(m_propertyTable->keyCount);
       
  1178 
       
  1179     // Get pointers to the enumerable entries in the buffer.
       
  1180     PropertyMapEntry** p = sortedEnumerables.data();
       
  1181     unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount;
       
  1182     for (unsigned i = 1; i <= entryCount; i++) {
       
  1183         if (m_propertyTable->entries()[i].key && (!(m_propertyTable->entries()[i].attributes & DontEnum) || (mode == IncludeDontEnumProperties)))
       
  1184             *p++ = &m_propertyTable->entries()[i];
       
  1185     }
       
  1186 
       
  1187     size_t enumerableCount = p - sortedEnumerables.data();
       
  1188     // Sort the entries by index.
       
  1189     qsort(sortedEnumerables.data(), enumerableCount, sizeof(PropertyMapEntry*), comparePropertyMapEntryIndices);
       
  1190     sortedEnumerables.resize(enumerableCount);
       
  1191 
       
  1192     // Put the keys of the sorted entries into the list.
       
  1193     if (!propertyNames.size()) {
       
  1194         for (size_t i = 0; i < sortedEnumerables.size(); ++i)
       
  1195             propertyNames.addKnownUnique(sortedEnumerables[i]->key);
       
  1196     } else {
       
  1197         for (size_t i = 0; i < sortedEnumerables.size(); ++i)
       
  1198             propertyNames.add(sortedEnumerables[i]->key);
       
  1199     }
       
  1200 }
       
  1201 
       
  1202 #if DO_PROPERTYMAP_CONSTENCY_CHECK
       
  1203 
       
  1204 void Structure::checkConsistency()
       
  1205 {
       
  1206     if (!m_propertyTable)
       
  1207         return;
       
  1208 
       
  1209     ASSERT(m_propertyTable->size >= newTableSize);
       
  1210     ASSERT(m_propertyTable->sizeMask);
       
  1211     ASSERT(m_propertyTable->size == m_propertyTable->sizeMask + 1);
       
  1212     ASSERT(!(m_propertyTable->size & m_propertyTable->sizeMask));
       
  1213 
       
  1214     ASSERT(m_propertyTable->keyCount <= m_propertyTable->size / 2);
       
  1215     ASSERT(m_propertyTable->deletedSentinelCount <= m_propertyTable->size / 4);
       
  1216 
       
  1217     ASSERT(m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount <= m_propertyTable->size / 2);
       
  1218 
       
  1219     unsigned indexCount = 0;
       
  1220     unsigned deletedIndexCount = 0;
       
  1221     for (unsigned a = 0; a != m_propertyTable->size; ++a) {
       
  1222         unsigned entryIndex = m_propertyTable->entryIndices[a];
       
  1223         if (entryIndex == emptyEntryIndex)
       
  1224             continue;
       
  1225         if (entryIndex == deletedSentinelIndex) {
       
  1226             ++deletedIndexCount;
       
  1227             continue;
       
  1228         }
       
  1229         ASSERT(entryIndex > deletedSentinelIndex);
       
  1230         ASSERT(entryIndex - 1 <= m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount);
       
  1231         ++indexCount;
       
  1232 
       
  1233         for (unsigned b = a + 1; b != m_propertyTable->size; ++b)
       
  1234             ASSERT(m_propertyTable->entryIndices[b] != entryIndex);
       
  1235     }
       
  1236     ASSERT(indexCount == m_propertyTable->keyCount);
       
  1237     ASSERT(deletedIndexCount == m_propertyTable->deletedSentinelCount);
       
  1238 
       
  1239     ASSERT(m_propertyTable->entries()[0].key == 0);
       
  1240 
       
  1241     unsigned nonEmptyEntryCount = 0;
       
  1242     for (unsigned c = 1; c <= m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; ++c) {
       
  1243         ASSERT(m_hasNonEnumerableProperties || !(m_propertyTable->entries()[c].attributes & DontEnum));
       
  1244         UString::Rep* rep = m_propertyTable->entries()[c].key;
       
  1245         ASSERT(m_propertyTable->entries()[c].offset >= m_anonymousSlotCount);
       
  1246         if (!rep)
       
  1247             continue;
       
  1248         ++nonEmptyEntryCount;
       
  1249         unsigned i = rep->existingHash();
       
  1250         unsigned k = 0;
       
  1251         unsigned entryIndex;
       
  1252         while (1) {
       
  1253             entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
       
  1254             ASSERT(entryIndex != emptyEntryIndex);
       
  1255             if (rep == m_propertyTable->entries()[entryIndex - 1].key)
       
  1256                 break;
       
  1257             if (k == 0)
       
  1258                 k = 1 | doubleHash(rep->existingHash());
       
  1259             i += k;
       
  1260         }
       
  1261         ASSERT(entryIndex == c + 1);
       
  1262     }
       
  1263 
       
  1264     ASSERT(nonEmptyEntryCount == m_propertyTable->keyCount);
       
  1265 }
       
  1266 
       
  1267 #endif // DO_PROPERTYMAP_CONSTENCY_CHECK
       
  1268 
       
  1269 } // namespace JSC