2 * Copyright (C) 2008 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "Structure.h"
29 #include "Identifier.h"
31 #include "PropertyNameArray.h"
32 #include "StructureChain.h"
34 #include <wtf/RefCountedLeakCounter.h>
35 #include <wtf/RefPtr.h>
37 #if ENABLE(JSC_MULTIPLE_THREADS)
38 #include <wtf/Threading.h>
41 #define DUMP_STRUCTURE_ID_STATISTICS 0
44 #define DO_PROPERTYMAP_CONSTENCY_CHECK 0
46 #define DO_PROPERTYMAP_CONSTENCY_CHECK 0
54 // Choose a number for the following so that most property maps are smaller,
55 // but it's not going to blow out the stack to allocate this number of pointers.
56 static const int smallMapThreshold
= 1024;
58 // The point at which the function call overhead of the qsort implementation
59 // becomes small compared to the inefficiency of insertion sort.
60 static const unsigned tinyMapThreshold
= 20;
62 static const unsigned newTableSize
= 16;
65 static WTF::RefCountedLeakCounter
structureCounter("Structure");
67 #if ENABLE(JSC_MULTIPLE_THREADS)
68 static Mutex ignoreSetMutex
;
71 static bool shouldIgnoreLeaks
;
72 static HashSet
<Structure
*> ignoreSet
;
75 #if DUMP_STRUCTURE_ID_STATISTICS
76 static HashSet
<Structure
*> liveStructureSet
;
79 void Structure::dumpStatistics()
81 #if DUMP_STRUCTURE_ID_STATISTICS
82 unsigned numberLeaf
= 0;
83 unsigned numberUsingSingleSlot
= 0;
84 unsigned numberSingletons
= 0;
85 unsigned numberWithPropertyMaps
= 0;
86 unsigned totalPropertyMapsSize
= 0;
88 HashSet
<Structure
*>::const_iterator end
= liveStructureSet
.end();
89 for (HashSet
<Structure
*>::const_iterator it
= liveStructureSet
.begin(); it
!= end
; ++it
) {
90 Structure
* structure
= *it
;
91 if (structure
->m_usingSingleTransitionSlot
) {
92 if (!structure
->m_transitions
.singleTransition
)
95 ++numberUsingSingleSlot
;
97 if (!structure
->m_previous
&& !structure
->m_transitions
.singleTransition
)
101 if (structure
->m_propertyTable
) {
102 ++numberWithPropertyMaps
;
103 totalPropertyMapsSize
+= PropertyMapHashTable::allocationSize(structure
->m_propertyTable
->size
);
104 if (structure
->m_propertyTable
->deletedOffsets
)
105 totalPropertyMapsSize
+= (structure
->m_propertyTable
->deletedOffsets
->capacity() * sizeof(unsigned));
109 printf("Number of live Structures: %d\n", liveStructureSet
.size());
110 printf("Number of Structures using the single item optimization for transition map: %d\n", numberUsingSingleSlot
);
111 printf("Number of Structures that are leaf nodes: %d\n", numberLeaf
);
112 printf("Number of Structures that singletons: %d\n", numberSingletons
);
113 printf("Number of Structures with PropertyMaps: %d\n", numberWithPropertyMaps
);
115 printf("Size of a single Structures: %d\n", static_cast<unsigned>(sizeof(Structure
)));
116 printf("Size of sum of all property maps: %d\n", totalPropertyMapsSize
);
117 printf("Size of average of all property maps: %f\n", static_cast<double>(totalPropertyMapsSize
) / static_cast<double>(liveStructureSet
.size()));
119 printf("Dumping Structure statistics is not enabled.\n");
123 Structure::Structure(JSValuePtr prototype
, const TypeInfo
& typeInfo
)
124 : m_typeInfo(typeInfo
)
125 , m_prototype(prototype
)
127 , m_propertyStorageCapacity(JSObject::inlineStorageCapacity
)
129 , m_isDictionary(false)
130 , m_isPinnedPropertyTable(false)
131 , m_hasGetterSetterProperties(false)
132 , m_usingSingleTransitionSlot(true)
133 , m_attributesInPrevious(0)
136 ASSERT(m_prototype
.isObject() || m_prototype
.isNull());
138 m_transitions
.singleTransition
= 0;
141 #if ENABLE(JSC_MULTIPLE_THREADS)
142 MutexLocker
protect(ignoreSetMutex
);
144 if (shouldIgnoreLeaks
)
147 structureCounter
.increment();
150 #if DUMP_STRUCTURE_ID_STATISTICS
151 liveStructureSet
.add(this);
155 Structure::~Structure()
158 if (m_previous
->m_usingSingleTransitionSlot
) {
159 m_previous
->m_transitions
.singleTransition
= 0;
161 ASSERT(m_previous
->m_transitions
.table
->contains(make_pair(m_nameInPrevious
.get(), m_attributesInPrevious
)));
162 m_previous
->m_transitions
.table
->remove(make_pair(m_nameInPrevious
.get(), m_attributesInPrevious
));
166 if (m_cachedPropertyNameArrayData
)
167 m_cachedPropertyNameArrayData
->setCachedStructure(0);
169 if (!m_usingSingleTransitionSlot
)
170 delete m_transitions
.table
;
172 if (m_propertyTable
) {
173 unsigned entryCount
= m_propertyTable
->keyCount
+ m_propertyTable
->deletedSentinelCount
;
174 for (unsigned i
= 1; i
<= entryCount
; i
++) {
175 if (UString::Rep
* key
= m_propertyTable
->entries()[i
].key
)
179 delete m_propertyTable
->deletedOffsets
;
180 fastFree(m_propertyTable
);
184 #if ENABLE(JSC_MULTIPLE_THREADS)
185 MutexLocker
protect(ignoreSetMutex
);
187 HashSet
<Structure
*>::iterator it
= ignoreSet
.find(this);
188 if (it
!= ignoreSet
.end())
189 ignoreSet
.remove(it
);
191 structureCounter
.decrement();
194 #if DUMP_STRUCTURE_ID_STATISTICS
195 liveStructureSet
.remove(this);
199 void Structure::startIgnoringLeaks()
202 shouldIgnoreLeaks
= true;
206 void Structure::stopIgnoringLeaks()
209 shouldIgnoreLeaks
= false;
213 static bool isPowerOf2(unsigned v
)
215 // Taken from http://www.cs.utk.edu/~vose/c-stuff/bithacks.html
217 return !(v
& (v
- 1)) && v
;
220 static unsigned nextPowerOf2(unsigned v
)
222 // Taken from http://www.cs.utk.edu/~vose/c-stuff/bithacks.html
223 // Devised by Sean Anderson, Sepember 14, 2001
236 static unsigned sizeForKeyCount(size_t keyCount
)
238 if (keyCount
== notFound
)
244 if (isPowerOf2(keyCount
))
247 return nextPowerOf2(keyCount
) * 2;
250 void Structure::materializePropertyMap()
252 ASSERT(!m_propertyTable
);
254 Vector
<Structure
*, 8> structures
;
255 structures
.append(this);
257 Structure
* structure
= this;
259 // Search for the last Structure with a property table.
260 while ((structure
= structure
->previousID())) {
261 if (structure
->m_isPinnedPropertyTable
) {
262 ASSERT(structure
->m_propertyTable
);
263 ASSERT(!structure
->m_previous
);
265 m_propertyTable
= structure
->copyPropertyTable();
269 structures
.append(structure
);
272 if (!m_propertyTable
)
273 createPropertyMapHashTable(sizeForKeyCount(m_offset
+ 1));
275 if (sizeForKeyCount(m_offset
+ 1) > m_propertyTable
->size
)
276 rehashPropertyMapHashTable(sizeForKeyCount(m_offset
+ 1)); // This could be made more efficient by combining with the copy above.
279 for (ptrdiff_t i
= structures
.size() - 2; i
>= 0; --i
) {
280 structure
= structures
[i
];
281 structure
->m_nameInPrevious
->ref();
282 PropertyMapEntry
entry(structure
->m_nameInPrevious
.get(), structure
->m_offset
, structure
->m_attributesInPrevious
, ++m_propertyTable
->lastIndexUsed
);
283 insertIntoPropertyMapHashTable(entry
);
287 void Structure::getEnumerablePropertyNames(ExecState
* exec
, PropertyNameArray
& propertyNames
, JSObject
* baseObject
)
289 bool shouldCache
= propertyNames
.shouldCache() && !(propertyNames
.size() || m_isDictionary
);
291 if (shouldCache
&& m_cachedPropertyNameArrayData
) {
292 if (m_cachedPropertyNameArrayData
->cachedPrototypeChain() == prototypeChain(exec
)) {
293 propertyNames
.setData(m_cachedPropertyNameArrayData
);
296 clearEnumerationCache();
299 getEnumerableNamesFromPropertyTable(propertyNames
);
300 getEnumerableNamesFromClassInfoTable(exec
, baseObject
->classInfo(), propertyNames
);
302 if (m_prototype
.isObject()) {
303 propertyNames
.setShouldCache(false); // No need for our prototypes to waste memory on caching, since they're not being enumerated directly.
304 asObject(m_prototype
)->getPropertyNames(exec
, propertyNames
);
308 m_cachedPropertyNameArrayData
= propertyNames
.data();
309 m_cachedPropertyNameArrayData
->setCachedPrototypeChain(prototypeChain(exec
));
310 m_cachedPropertyNameArrayData
->setCachedStructure(this);
314 void Structure::clearEnumerationCache()
316 if (m_cachedPropertyNameArrayData
)
317 m_cachedPropertyNameArrayData
->setCachedStructure(0);
318 m_cachedPropertyNameArrayData
.clear();
321 void Structure::growPropertyStorageCapacity()
323 if (m_propertyStorageCapacity
== JSObject::inlineStorageCapacity
)
324 m_propertyStorageCapacity
= JSObject::nonInlineBaseStorageCapacity
;
326 m_propertyStorageCapacity
*= 2;
329 PassRefPtr
<Structure
> Structure::addPropertyTransitionToExistingStructure(Structure
* structure
, const Identifier
& propertyName
, unsigned attributes
, size_t& offset
)
331 ASSERT(!structure
->m_isDictionary
);
332 ASSERT(structure
->typeInfo().type() == ObjectType
);
334 if (structure
->m_usingSingleTransitionSlot
) {
335 Structure
* existingTransition
= structure
->m_transitions
.singleTransition
;
336 if (existingTransition
&& existingTransition
->m_nameInPrevious
.get() == propertyName
.ustring().rep() && existingTransition
->m_attributesInPrevious
== attributes
) {
337 ASSERT(structure
->m_transitions
.singleTransition
->m_offset
!= noOffset
);
338 offset
= structure
->m_transitions
.singleTransition
->m_offset
;
339 return existingTransition
;
342 if (Structure
* existingTransition
= structure
->m_transitions
.table
->get(make_pair(propertyName
.ustring().rep(), attributes
))) {
343 ASSERT(existingTransition
->m_offset
!= noOffset
);
344 offset
= existingTransition
->m_offset
;
345 return existingTransition
;
352 PassRefPtr
<Structure
> Structure::addPropertyTransition(Structure
* structure
, const Identifier
& propertyName
, unsigned attributes
, size_t& offset
)
354 ASSERT(!structure
->m_isDictionary
);
355 ASSERT(structure
->typeInfo().type() == ObjectType
);
356 ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure
, propertyName
, attributes
, offset
));
358 if (structure
->transitionCount() > s_maxTransitionLength
) {
359 RefPtr
<Structure
> transition
= toDictionaryTransition(structure
);
360 offset
= transition
->put(propertyName
, attributes
);
361 if (transition
->propertyStorageSize() > transition
->propertyStorageCapacity())
362 transition
->growPropertyStorageCapacity();
363 return transition
.release();
366 RefPtr
<Structure
> transition
= create(structure
->m_prototype
, structure
->typeInfo());
367 transition
->m_cachedPrototypeChain
= structure
->m_cachedPrototypeChain
;
368 transition
->m_previous
= structure
;
369 transition
->m_nameInPrevious
= propertyName
.ustring().rep();
370 transition
->m_attributesInPrevious
= attributes
;
371 transition
->m_propertyStorageCapacity
= structure
->m_propertyStorageCapacity
;
372 transition
->m_hasGetterSetterProperties
= structure
->m_hasGetterSetterProperties
;
374 if (structure
->m_propertyTable
) {
375 if (structure
->m_isPinnedPropertyTable
)
376 transition
->m_propertyTable
= structure
->copyPropertyTable();
378 transition
->m_propertyTable
= structure
->m_propertyTable
;
379 structure
->m_propertyTable
= 0;
382 if (structure
->m_previous
)
383 transition
->materializePropertyMap();
385 transition
->createPropertyMapHashTable();
388 offset
= transition
->put(propertyName
, attributes
);
389 if (transition
->propertyStorageSize() > transition
->propertyStorageCapacity())
390 transition
->growPropertyStorageCapacity();
392 transition
->m_offset
= offset
;
394 if (structure
->m_usingSingleTransitionSlot
) {
395 if (!structure
->m_transitions
.singleTransition
) {
396 structure
->m_transitions
.singleTransition
= transition
.get();
397 return transition
.release();
400 Structure
* existingTransition
= structure
->m_transitions
.singleTransition
;
401 structure
->m_usingSingleTransitionSlot
= false;
402 StructureTransitionTable
* transitionTable
= new StructureTransitionTable
;
403 structure
->m_transitions
.table
= transitionTable
;
404 transitionTable
->add(make_pair(existingTransition
->m_nameInPrevious
.get(), existingTransition
->m_attributesInPrevious
), existingTransition
);
406 structure
->m_transitions
.table
->add(make_pair(propertyName
.ustring().rep(), attributes
), transition
.get());
407 return transition
.release();
410 PassRefPtr
<Structure
> Structure::removePropertyTransition(Structure
* structure
, const Identifier
& propertyName
, size_t& offset
)
412 ASSERT(!structure
->m_isDictionary
);
414 RefPtr
<Structure
> transition
= toDictionaryTransition(structure
);
416 offset
= transition
->remove(propertyName
);
418 return transition
.release();
421 PassRefPtr
<Structure
> Structure::changePrototypeTransition(Structure
* structure
, JSValuePtr prototype
)
423 RefPtr
<Structure
> transition
= create(prototype
, structure
->typeInfo());
425 transition
->m_propertyStorageCapacity
= structure
->m_propertyStorageCapacity
;
426 transition
->m_hasGetterSetterProperties
= structure
->m_hasGetterSetterProperties
;
428 // Don't set m_offset, as one can not transition to this.
430 structure
->materializePropertyMapIfNecessary();
431 transition
->m_propertyTable
= structure
->copyPropertyTable();
432 transition
->m_isPinnedPropertyTable
= true;
434 return transition
.release();
437 PassRefPtr
<Structure
> Structure::getterSetterTransition(Structure
* structure
)
439 RefPtr
<Structure
> transition
= create(structure
->storedPrototype(), structure
->typeInfo());
440 transition
->m_propertyStorageCapacity
= structure
->m_propertyStorageCapacity
;
441 transition
->m_hasGetterSetterProperties
= transition
->m_hasGetterSetterProperties
;
443 // Don't set m_offset, as one can not transition to this.
445 structure
->materializePropertyMapIfNecessary();
446 transition
->m_propertyTable
= structure
->copyPropertyTable();
447 transition
->m_isPinnedPropertyTable
= true;
449 return transition
.release();
452 PassRefPtr
<Structure
> Structure::toDictionaryTransition(Structure
* structure
)
454 ASSERT(!structure
->m_isDictionary
);
456 RefPtr
<Structure
> transition
= create(structure
->m_prototype
, structure
->typeInfo());
457 transition
->m_isDictionary
= true;
458 transition
->m_propertyStorageCapacity
= structure
->m_propertyStorageCapacity
;
459 transition
->m_hasGetterSetterProperties
= structure
->m_hasGetterSetterProperties
;
461 structure
->materializePropertyMapIfNecessary();
462 transition
->m_propertyTable
= structure
->copyPropertyTable();
463 transition
->m_isPinnedPropertyTable
= true;
465 return transition
.release();
468 PassRefPtr
<Structure
> Structure::fromDictionaryTransition(Structure
* structure
)
470 ASSERT(structure
->m_isDictionary
);
472 // Since dictionary Structures are not shared, and no opcodes specialize
473 // for them, we don't need to allocate a new Structure when transitioning
474 // to non-dictionary status.
476 // FIMXE: We can make this more efficient by canonicalizing the Structure (draining the
477 // deleted offsets vector) before transitioning from dictionary.
478 if (!structure
->m_propertyTable
|| !structure
->m_propertyTable
->deletedOffsets
|| structure
->m_propertyTable
->deletedOffsets
->isEmpty())
479 structure
->m_isDictionary
= false;
484 size_t Structure::addPropertyWithoutTransition(const Identifier
& propertyName
, unsigned attributes
)
486 ASSERT(!m_transitions
.singleTransition
);
488 materializePropertyMapIfNecessary();
490 m_isPinnedPropertyTable
= true;
491 size_t offset
= put(propertyName
, attributes
);
492 if (propertyStorageSize() > propertyStorageCapacity())
493 growPropertyStorageCapacity();
494 clearEnumerationCache();
498 size_t Structure::removePropertyWithoutTransition(const Identifier
& propertyName
)
500 ASSERT(!m_transitions
.singleTransition
);
501 ASSERT(m_isDictionary
);
503 materializePropertyMapIfNecessary();
505 m_isPinnedPropertyTable
= true;
506 size_t offset
= remove(propertyName
);
507 clearEnumerationCache();
511 #if DUMP_PROPERTYMAP_STATS
513 static int numProbes
;
514 static int numCollisions
;
515 static int numRehashes
;
516 static int numRemoves
;
518 struct PropertyMapStatisticsExitLogger
{
519 ~PropertyMapStatisticsExitLogger();
522 static PropertyMapStatisticsExitLogger logger
;
524 PropertyMapStatisticsExitLogger::~PropertyMapStatisticsExitLogger()
526 printf("\nJSC::PropertyMap statistics\n\n");
527 printf("%d probes\n", numProbes
);
528 printf("%d collisions (%.1f%%)\n", numCollisions
, 100.0 * numCollisions
/ numProbes
);
529 printf("%d rehashes\n", numRehashes
);
530 printf("%d removes\n", numRemoves
);
535 static const unsigned deletedSentinelIndex
= 1;
537 #if !DO_PROPERTYMAP_CONSTENCY_CHECK
539 inline void Structure::checkConsistency()
545 PropertyMapHashTable
* Structure::copyPropertyTable()
547 if (!m_propertyTable
)
550 size_t tableSize
= PropertyMapHashTable::allocationSize(m_propertyTable
->size
);
551 PropertyMapHashTable
* newTable
= static_cast<PropertyMapHashTable
*>(fastMalloc(tableSize
));
552 memcpy(newTable
, m_propertyTable
, tableSize
);
554 unsigned entryCount
= m_propertyTable
->keyCount
+ m_propertyTable
->deletedSentinelCount
;
555 for (unsigned i
= 1; i
<= entryCount
; ++i
) {
556 if (UString::Rep
* key
= newTable
->entries()[i
].key
)
560 // Copy the deletedOffsets vector.
561 if (m_propertyTable
->deletedOffsets
)
562 newTable
->deletedOffsets
= new Vector
<unsigned>(*m_propertyTable
->deletedOffsets
);
567 size_t Structure::get(const Identifier
& propertyName
, unsigned& attributes
)
569 ASSERT(!propertyName
.isNull());
571 materializePropertyMapIfNecessary();
572 if (!m_propertyTable
)
575 UString::Rep
* rep
= propertyName
._ustring
.rep();
577 unsigned i
= rep
->computedHash();
579 #if DUMP_PROPERTYMAP_STATS
583 unsigned entryIndex
= m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
];
584 if (entryIndex
== emptyEntryIndex
)
587 if (rep
== m_propertyTable
->entries()[entryIndex
- 1].key
) {
588 attributes
= m_propertyTable
->entries()[entryIndex
- 1].attributes
;
589 return m_propertyTable
->entries()[entryIndex
- 1].offset
;
592 #if DUMP_PROPERTYMAP_STATS
596 unsigned k
= 1 | doubleHash(rep
->computedHash());
601 #if DUMP_PROPERTYMAP_STATS
605 entryIndex
= m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
];
606 if (entryIndex
== emptyEntryIndex
)
609 if (rep
== m_propertyTable
->entries()[entryIndex
- 1].key
) {
610 attributes
= m_propertyTable
->entries()[entryIndex
- 1].attributes
;
611 return m_propertyTable
->entries()[entryIndex
- 1].offset
;
616 size_t Structure::put(const Identifier
& propertyName
, unsigned attributes
)
618 ASSERT(!propertyName
.isNull());
619 ASSERT(get(propertyName
) == notFound
);
623 UString::Rep
* rep
= propertyName
._ustring
.rep();
625 if (!m_propertyTable
)
626 createPropertyMapHashTable();
628 // FIXME: Consider a fast case for tables with no deleted sentinels.
630 unsigned i
= rep
->computedHash();
632 bool foundDeletedElement
= false;
633 unsigned deletedElementIndex
= 0; // initialize to make the compiler happy
635 #if DUMP_PROPERTYMAP_STATS
640 unsigned entryIndex
= m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
];
641 if (entryIndex
== emptyEntryIndex
)
644 if (entryIndex
== deletedSentinelIndex
) {
645 // If we find a deleted-element sentinel, remember it for use later.
646 if (!foundDeletedElement
) {
647 foundDeletedElement
= true;
648 deletedElementIndex
= i
;
653 k
= 1 | doubleHash(rep
->computedHash());
654 #if DUMP_PROPERTYMAP_STATS
661 #if DUMP_PROPERTYMAP_STATS
666 // Figure out which entry to use.
667 unsigned entryIndex
= m_propertyTable
->keyCount
+ m_propertyTable
->deletedSentinelCount
+ 2;
668 if (foundDeletedElement
) {
669 i
= deletedElementIndex
;
670 --m_propertyTable
->deletedSentinelCount
;
672 // Since we're not making the table bigger, we can't use the entry one past
673 // the end that we were planning on using, so search backwards for the empty
674 // slot that we can use. We know it will be there because we did at least one
675 // deletion in the past that left an entry empty.
676 while (m_propertyTable
->entries()[--entryIndex
- 1].key
) { }
679 // Create a new hash table entry.
680 m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
] = entryIndex
;
682 // Create a new hash table entry.
684 m_propertyTable
->entries()[entryIndex
- 1].key
= rep
;
685 m_propertyTable
->entries()[entryIndex
- 1].attributes
= attributes
;
686 m_propertyTable
->entries()[entryIndex
- 1].index
= ++m_propertyTable
->lastIndexUsed
;
689 if (m_propertyTable
->deletedOffsets
&& !m_propertyTable
->deletedOffsets
->isEmpty()) {
690 newOffset
= m_propertyTable
->deletedOffsets
->last();
691 m_propertyTable
->deletedOffsets
->removeLast();
693 newOffset
= m_propertyTable
->keyCount
;
694 m_propertyTable
->entries()[entryIndex
- 1].offset
= newOffset
;
696 ++m_propertyTable
->keyCount
;
698 if ((m_propertyTable
->keyCount
+ m_propertyTable
->deletedSentinelCount
) * 2 >= m_propertyTable
->size
)
699 expandPropertyMapHashTable();
705 size_t Structure::remove(const Identifier
& propertyName
)
707 ASSERT(!propertyName
.isNull());
711 UString::Rep
* rep
= propertyName
._ustring
.rep();
713 if (!m_propertyTable
)
716 #if DUMP_PROPERTYMAP_STATS
721 // Find the thing to remove.
722 unsigned i
= rep
->computedHash();
725 UString::Rep
* key
= 0;
727 entryIndex
= m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
];
728 if (entryIndex
== emptyEntryIndex
)
731 key
= m_propertyTable
->entries()[entryIndex
- 1].key
;
736 k
= 1 | doubleHash(rep
->computedHash());
737 #if DUMP_PROPERTYMAP_STATS
744 #if DUMP_PROPERTYMAP_STATS
749 // Replace this one element with the deleted sentinel. Also clear out
750 // the entry so we can iterate all the entries as needed.
751 m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
] = deletedSentinelIndex
;
753 size_t offset
= m_propertyTable
->entries()[entryIndex
- 1].offset
;
756 m_propertyTable
->entries()[entryIndex
- 1].key
= 0;
757 m_propertyTable
->entries()[entryIndex
- 1].attributes
= 0;
758 m_propertyTable
->entries()[entryIndex
- 1].offset
= 0;
760 if (!m_propertyTable
->deletedOffsets
)
761 m_propertyTable
->deletedOffsets
= new Vector
<unsigned>;
762 m_propertyTable
->deletedOffsets
->append(offset
);
764 ASSERT(m_propertyTable
->keyCount
>= 1);
765 --m_propertyTable
->keyCount
;
766 ++m_propertyTable
->deletedSentinelCount
;
768 if (m_propertyTable
->deletedSentinelCount
* 4 >= m_propertyTable
->size
)
769 rehashPropertyMapHashTable();
775 void Structure::insertIntoPropertyMapHashTable(const PropertyMapEntry
& entry
)
777 ASSERT(m_propertyTable
);
779 unsigned i
= entry
.key
->computedHash();
782 #if DUMP_PROPERTYMAP_STATS
787 unsigned entryIndex
= m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
];
788 if (entryIndex
== emptyEntryIndex
)
792 k
= 1 | doubleHash(entry
.key
->computedHash());
793 #if DUMP_PROPERTYMAP_STATS
800 #if DUMP_PROPERTYMAP_STATS
805 unsigned entryIndex
= m_propertyTable
->keyCount
+ 2;
806 m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
] = entryIndex
;
807 m_propertyTable
->entries()[entryIndex
- 1] = entry
;
809 ++m_propertyTable
->keyCount
;
812 void Structure::createPropertyMapHashTable()
814 ASSERT(sizeForKeyCount(7) == newTableSize
);
815 createPropertyMapHashTable(newTableSize
);
818 void Structure::createPropertyMapHashTable(unsigned newTableSize
)
820 ASSERT(!m_propertyTable
);
821 ASSERT(isPowerOf2(newTableSize
));
825 m_propertyTable
= static_cast<PropertyMapHashTable
*>(fastZeroedMalloc(PropertyMapHashTable::allocationSize(newTableSize
)));
826 m_propertyTable
->size
= newTableSize
;
827 m_propertyTable
->sizeMask
= newTableSize
- 1;
832 void Structure::expandPropertyMapHashTable()
834 ASSERT(m_propertyTable
);
835 rehashPropertyMapHashTable(m_propertyTable
->size
* 2);
838 void Structure::rehashPropertyMapHashTable()
840 ASSERT(m_propertyTable
);
841 ASSERT(m_propertyTable
->size
);
842 rehashPropertyMapHashTable(m_propertyTable
->size
);
845 void Structure::rehashPropertyMapHashTable(unsigned newTableSize
)
847 ASSERT(m_propertyTable
);
848 ASSERT(isPowerOf2(newTableSize
));
852 PropertyMapHashTable
* oldTable
= m_propertyTable
;
854 m_propertyTable
= static_cast<PropertyMapHashTable
*>(fastZeroedMalloc(PropertyMapHashTable::allocationSize(newTableSize
)));
855 m_propertyTable
->size
= newTableSize
;
856 m_propertyTable
->sizeMask
= newTableSize
- 1;
858 unsigned lastIndexUsed
= 0;
859 unsigned entryCount
= oldTable
->keyCount
+ oldTable
->deletedSentinelCount
;
860 for (unsigned i
= 1; i
<= entryCount
; ++i
) {
861 if (oldTable
->entries()[i
].key
) {
862 lastIndexUsed
= max(oldTable
->entries()[i
].index
, lastIndexUsed
);
863 insertIntoPropertyMapHashTable(oldTable
->entries()[i
]);
866 m_propertyTable
->lastIndexUsed
= lastIndexUsed
;
867 m_propertyTable
->deletedOffsets
= oldTable
->deletedOffsets
;
874 static int comparePropertyMapEntryIndices(const void* a
, const void* b
)
876 unsigned ia
= static_cast<PropertyMapEntry
* const*>(a
)[0]->index
;
877 unsigned ib
= static_cast<PropertyMapEntry
* const*>(b
)[0]->index
;
885 void Structure::getEnumerableNamesFromPropertyTable(PropertyNameArray
& propertyNames
)
887 materializePropertyMapIfNecessary();
888 if (!m_propertyTable
)
891 if (m_propertyTable
->keyCount
< tinyMapThreshold
) {
892 PropertyMapEntry
* a
[tinyMapThreshold
];
894 unsigned entryCount
= m_propertyTable
->keyCount
+ m_propertyTable
->deletedSentinelCount
;
895 for (unsigned k
= 1; k
<= entryCount
; k
++) {
896 if (m_propertyTable
->entries()[k
].key
&& !(m_propertyTable
->entries()[k
].attributes
& DontEnum
)) {
897 PropertyMapEntry
* value
= &m_propertyTable
->entries()[k
];
899 for (j
= i
- 1; j
>= 0 && a
[j
]->index
> value
->index
; --j
)
905 if (!propertyNames
.size()) {
906 for (int k
= 0; k
< i
; ++k
)
907 propertyNames
.addKnownUnique(a
[k
]->key
);
909 for (int k
= 0; k
< i
; ++k
)
910 propertyNames
.add(a
[k
]->key
);
916 // Allocate a buffer to use to sort the keys.
917 Vector
<PropertyMapEntry
*, smallMapThreshold
> sortedEnumerables(m_propertyTable
->keyCount
);
919 // Get pointers to the enumerable entries in the buffer.
920 PropertyMapEntry
** p
= sortedEnumerables
.data();
921 unsigned entryCount
= m_propertyTable
->keyCount
+ m_propertyTable
->deletedSentinelCount
;
922 for (unsigned i
= 1; i
<= entryCount
; i
++) {
923 if (m_propertyTable
->entries()[i
].key
&& !(m_propertyTable
->entries()[i
].attributes
& DontEnum
))
924 *p
++ = &m_propertyTable
->entries()[i
];
927 size_t enumerableCount
= p
- sortedEnumerables
.data();
928 // Sort the entries by index.
929 qsort(sortedEnumerables
.data(), enumerableCount
, sizeof(PropertyMapEntry
*), comparePropertyMapEntryIndices
);
930 sortedEnumerables
.resize(enumerableCount
);
932 // Put the keys of the sorted entries into the list.
933 if (!propertyNames
.size()) {
934 for (size_t i
= 0; i
< sortedEnumerables
.size(); ++i
)
935 propertyNames
.addKnownUnique(sortedEnumerables
[i
]->key
);
937 for (size_t i
= 0; i
< sortedEnumerables
.size(); ++i
)
938 propertyNames
.add(sortedEnumerables
[i
]->key
);
942 void Structure::getEnumerableNamesFromClassInfoTable(ExecState
* exec
, const ClassInfo
* classInfo
, PropertyNameArray
& propertyNames
)
944 // Add properties from the static hashtables of properties
945 for (; classInfo
; classInfo
= classInfo
->parentClass
) {
946 const HashTable
* table
= classInfo
->propHashTable(exec
);
949 table
->initializeIfNeeded(exec
);
950 ASSERT(table
->table
);
951 #if ENABLE(PERFECT_HASH_SIZE)
952 int hashSizeMask
= table
->hashSizeMask
;
954 int hashSizeMask
= table
->compactSize
- 1;
956 const HashEntry
* entry
= table
->table
;
957 for (int i
= 0; i
<= hashSizeMask
; ++i
, ++entry
) {
958 if (entry
->key() && !(entry
->attributes() & DontEnum
))
959 propertyNames
.add(entry
->key());
964 #if DO_PROPERTYMAP_CONSTENCY_CHECK
966 void Structure::checkConsistency()
968 if (!m_propertyTable
)
971 ASSERT(m_propertyTable
->size
>= newTableSize
);
972 ASSERT(m_propertyTable
->sizeMask
);
973 ASSERT(m_propertyTable
->size
== m_propertyTable
->sizeMask
+ 1);
974 ASSERT(!(m_propertyTable
->size
& m_propertyTable
->sizeMask
));
976 ASSERT(m_propertyTable
->keyCount
<= m_propertyTable
->size
/ 2);
977 ASSERT(m_propertyTable
->deletedSentinelCount
<= m_propertyTable
->size
/ 4);
979 ASSERT(m_propertyTable
->keyCount
+ m_propertyTable
->deletedSentinelCount
<= m_propertyTable
->size
/ 2);
981 unsigned indexCount
= 0;
982 unsigned deletedIndexCount
= 0;
983 for (unsigned a
= 0; a
!= m_propertyTable
->size
; ++a
) {
984 unsigned entryIndex
= m_propertyTable
->entryIndices
[a
];
985 if (entryIndex
== emptyEntryIndex
)
987 if (entryIndex
== deletedSentinelIndex
) {
991 ASSERT(entryIndex
> deletedSentinelIndex
);
992 ASSERT(entryIndex
- 1 <= m_propertyTable
->keyCount
+ m_propertyTable
->deletedSentinelCount
);
995 for (unsigned b
= a
+ 1; b
!= m_propertyTable
->size
; ++b
)
996 ASSERT(m_propertyTable
->entryIndices
[b
] != entryIndex
);
998 ASSERT(indexCount
== m_propertyTable
->keyCount
);
999 ASSERT(deletedIndexCount
== m_propertyTable
->deletedSentinelCount
);
1001 ASSERT(m_propertyTable
->entries()[0].key
== 0);
1003 unsigned nonEmptyEntryCount
= 0;
1004 for (unsigned c
= 1; c
<= m_propertyTable
->keyCount
+ m_propertyTable
->deletedSentinelCount
; ++c
) {
1005 UString::Rep
* rep
= m_propertyTable
->entries()[c
].key
;
1008 ++nonEmptyEntryCount
;
1009 unsigned i
= rep
->computedHash();
1011 unsigned entryIndex
;
1013 entryIndex
= m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
];
1014 ASSERT(entryIndex
!= emptyEntryIndex
);
1015 if (rep
== m_propertyTable
->entries()[entryIndex
- 1].key
)
1018 k
= 1 | doubleHash(rep
->computedHash());
1021 ASSERT(entryIndex
== c
+ 1);
1024 ASSERT(nonEmptyEntryCount
== m_propertyTable
->keyCount
);
1027 #endif // DO_PROPERTYMAP_CONSTENCY_CHECK