2 * Copyright (C) 2008, 2009 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
= *(new Mutex
);
71 static bool shouldIgnoreLeaks
;
72 static HashSet
<Structure
*>& ignoreSet
= *(new HashSet
<Structure
*>);
75 #if DUMP_STRUCTURE_ID_STATISTICS
76 static HashSet
<Structure
*>& liveStructureSet
= *(new HashSet
<Structure
*>);
79 static int comparePropertyMapEntryIndices(const void* a
, const void* b
);
81 void Structure::dumpStatistics()
83 #if DUMP_STRUCTURE_ID_STATISTICS
84 unsigned numberLeaf
= 0;
85 unsigned numberUsingSingleSlot
= 0;
86 unsigned numberSingletons
= 0;
87 unsigned numberWithPropertyMaps
= 0;
88 unsigned totalPropertyMapsSize
= 0;
90 HashSet
<Structure
*>::const_iterator end
= liveStructureSet
.end();
91 for (HashSet
<Structure
*>::const_iterator it
= liveStructureSet
.begin(); it
!= end
; ++it
) {
92 Structure
* structure
= *it
;
93 if (structure
->m_usingSingleTransitionSlot
) {
94 if (!structure
->m_transitions
.singleTransition
)
97 ++numberUsingSingleSlot
;
99 if (!structure
->m_previous
&& !structure
->m_transitions
.singleTransition
)
103 if (structure
->m_propertyTable
) {
104 ++numberWithPropertyMaps
;
105 totalPropertyMapsSize
+= PropertyMapHashTable::allocationSize(structure
->m_propertyTable
->size
);
106 if (structure
->m_propertyTable
->deletedOffsets
)
107 totalPropertyMapsSize
+= (structure
->m_propertyTable
->deletedOffsets
->capacity() * sizeof(unsigned));
111 printf("Number of live Structures: %d\n", liveStructureSet
.size());
112 printf("Number of Structures using the single item optimization for transition map: %d\n", numberUsingSingleSlot
);
113 printf("Number of Structures that are leaf nodes: %d\n", numberLeaf
);
114 printf("Number of Structures that singletons: %d\n", numberSingletons
);
115 printf("Number of Structures with PropertyMaps: %d\n", numberWithPropertyMaps
);
117 printf("Size of a single Structures: %d\n", static_cast<unsigned>(sizeof(Structure
)));
118 printf("Size of sum of all property maps: %d\n", totalPropertyMapsSize
);
119 printf("Size of average of all property maps: %f\n", static_cast<double>(totalPropertyMapsSize
) / static_cast<double>(liveStructureSet
.size()));
121 printf("Dumping Structure statistics is not enabled.\n");
125 Structure::Structure(JSValue prototype
, const TypeInfo
& typeInfo
)
126 : m_typeInfo(typeInfo
)
127 , m_prototype(prototype
)
128 , m_specificValueInPrevious(0)
130 , m_propertyStorageCapacity(JSObject::inlineStorageCapacity
)
132 , m_dictionaryKind(NoneDictionaryKind
)
133 , m_isPinnedPropertyTable(false)
134 , m_hasGetterSetterProperties(false)
135 , m_usingSingleTransitionSlot(true)
136 , m_attributesInPrevious(0)
139 ASSERT(m_prototype
.isObject() || m_prototype
.isNull());
141 m_transitions
.singleTransition
= 0;
144 #if ENABLE(JSC_MULTIPLE_THREADS)
145 MutexLocker
protect(ignoreSetMutex
);
147 if (shouldIgnoreLeaks
)
150 structureCounter
.increment();
153 #if DUMP_STRUCTURE_ID_STATISTICS
154 liveStructureSet
.add(this);
158 Structure::~Structure()
161 if (m_previous
->m_usingSingleTransitionSlot
) {
162 m_previous
->m_transitions
.singleTransition
= 0;
164 ASSERT(m_previous
->m_transitions
.table
->contains(make_pair(m_nameInPrevious
.get(), m_attributesInPrevious
), m_specificValueInPrevious
));
165 m_previous
->m_transitions
.table
->remove(make_pair(m_nameInPrevious
.get(), m_attributesInPrevious
), m_specificValueInPrevious
);
169 if (m_cachedPropertyNameArrayData
)
170 m_cachedPropertyNameArrayData
->setCachedStructure(0);
172 if (!m_usingSingleTransitionSlot
)
173 delete m_transitions
.table
;
175 if (m_propertyTable
) {
176 unsigned entryCount
= m_propertyTable
->keyCount
+ m_propertyTable
->deletedSentinelCount
;
177 for (unsigned i
= 1; i
<= entryCount
; i
++) {
178 if (UString::Rep
* key
= m_propertyTable
->entries()[i
].key
)
182 delete m_propertyTable
->deletedOffsets
;
183 fastFree(m_propertyTable
);
187 #if ENABLE(JSC_MULTIPLE_THREADS)
188 MutexLocker
protect(ignoreSetMutex
);
190 HashSet
<Structure
*>::iterator it
= ignoreSet
.find(this);
191 if (it
!= ignoreSet
.end())
192 ignoreSet
.remove(it
);
194 structureCounter
.decrement();
197 #if DUMP_STRUCTURE_ID_STATISTICS
198 liveStructureSet
.remove(this);
202 void Structure::startIgnoringLeaks()
205 shouldIgnoreLeaks
= true;
209 void Structure::stopIgnoringLeaks()
212 shouldIgnoreLeaks
= false;
216 static bool isPowerOf2(unsigned v
)
218 // Taken from http://www.cs.utk.edu/~vose/c-stuff/bithacks.html
220 return !(v
& (v
- 1)) && v
;
223 static unsigned nextPowerOf2(unsigned v
)
225 // Taken from http://www.cs.utk.edu/~vose/c-stuff/bithacks.html
226 // Devised by Sean Anderson, Sepember 14, 2001
239 static unsigned sizeForKeyCount(size_t keyCount
)
241 if (keyCount
== notFound
)
247 if (isPowerOf2(keyCount
))
250 return nextPowerOf2(keyCount
) * 2;
253 void Structure::materializePropertyMap()
255 ASSERT(!m_propertyTable
);
257 Vector
<Structure
*, 8> structures
;
258 structures
.append(this);
260 Structure
* structure
= this;
262 // Search for the last Structure with a property table.
263 while ((structure
= structure
->previousID())) {
264 if (structure
->m_isPinnedPropertyTable
) {
265 ASSERT(structure
->m_propertyTable
);
266 ASSERT(!structure
->m_previous
);
268 m_propertyTable
= structure
->copyPropertyTable();
272 structures
.append(structure
);
275 if (!m_propertyTable
)
276 createPropertyMapHashTable(sizeForKeyCount(m_offset
+ 1));
278 if (sizeForKeyCount(m_offset
+ 1) > m_propertyTable
->size
)
279 rehashPropertyMapHashTable(sizeForKeyCount(m_offset
+ 1)); // This could be made more efficient by combining with the copy above.
282 for (ptrdiff_t i
= structures
.size() - 2; i
>= 0; --i
) {
283 structure
= structures
[i
];
284 structure
->m_nameInPrevious
->ref();
285 PropertyMapEntry
entry(structure
->m_nameInPrevious
.get(), structure
->m_offset
, structure
->m_attributesInPrevious
, structure
->m_specificValueInPrevious
, ++m_propertyTable
->lastIndexUsed
);
286 insertIntoPropertyMapHashTable(entry
);
290 void Structure::getEnumerablePropertyNames(ExecState
* exec
, PropertyNameArray
& propertyNames
, JSObject
* baseObject
)
292 bool shouldCache
= propertyNames
.shouldCache() && !(propertyNames
.size() || isDictionary());
294 if (shouldCache
&& m_cachedPropertyNameArrayData
) {
295 if (m_cachedPropertyNameArrayData
->cachedPrototypeChain() == prototypeChain(exec
)) {
296 propertyNames
.setData(m_cachedPropertyNameArrayData
);
299 clearEnumerationCache();
302 getEnumerableNamesFromPropertyTable(propertyNames
);
303 getEnumerableNamesFromClassInfoTable(exec
, baseObject
->classInfo(), propertyNames
);
305 if (m_prototype
.isObject()) {
306 propertyNames
.setShouldCache(false); // No need for our prototypes to waste memory on caching, since they're not being enumerated directly.
307 asObject(m_prototype
)->getPropertyNames(exec
, propertyNames
);
311 StructureChain
* protoChain
= prototypeChain(exec
);
312 m_cachedPropertyNameArrayData
= propertyNames
.data();
313 if (!protoChain
->isCacheable())
315 m_cachedPropertyNameArrayData
->setCachedPrototypeChain(protoChain
);
316 m_cachedPropertyNameArrayData
->setCachedStructure(this);
320 void Structure::clearEnumerationCache()
322 if (m_cachedPropertyNameArrayData
)
323 m_cachedPropertyNameArrayData
->setCachedStructure(0);
324 m_cachedPropertyNameArrayData
.clear();
327 void Structure::growPropertyStorageCapacity()
329 if (m_propertyStorageCapacity
== JSObject::inlineStorageCapacity
)
330 m_propertyStorageCapacity
= JSObject::nonInlineBaseStorageCapacity
;
332 m_propertyStorageCapacity
*= 2;
335 void Structure::despecifyDictionaryFunction(const Identifier
& propertyName
)
337 const UString::Rep
* rep
= propertyName
._ustring
.rep();
339 materializePropertyMapIfNecessary();
341 ASSERT(isDictionary());
342 ASSERT(m_propertyTable
);
344 unsigned i
= rep
->computedHash();
346 #if DUMP_PROPERTYMAP_STATS
350 unsigned entryIndex
= m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
];
351 ASSERT(entryIndex
!= emptyEntryIndex
);
353 if (rep
== m_propertyTable
->entries()[entryIndex
- 1].key
) {
354 m_propertyTable
->entries()[entryIndex
- 1].specificValue
= 0;
358 #if DUMP_PROPERTYMAP_STATS
362 unsigned k
= 1 | doubleHash(rep
->computedHash());
367 #if DUMP_PROPERTYMAP_STATS
371 entryIndex
= m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
];
372 ASSERT(entryIndex
!= emptyEntryIndex
);
374 if (rep
== m_propertyTable
->entries()[entryIndex
- 1].key
) {
375 m_propertyTable
->entries()[entryIndex
- 1].specificValue
= 0;
381 PassRefPtr
<Structure
> Structure::addPropertyTransitionToExistingStructure(Structure
* structure
, const Identifier
& propertyName
, unsigned attributes
, JSCell
* specificValue
, size_t& offset
)
383 ASSERT(!structure
->isDictionary());
384 ASSERT(structure
->typeInfo().type() == ObjectType
);
386 if (structure
->m_usingSingleTransitionSlot
) {
387 Structure
* existingTransition
= structure
->m_transitions
.singleTransition
;
388 if (existingTransition
&& existingTransition
->m_nameInPrevious
.get() == propertyName
.ustring().rep()
389 && existingTransition
->m_attributesInPrevious
== attributes
390 && (existingTransition
->m_specificValueInPrevious
== specificValue
|| existingTransition
->m_specificValueInPrevious
== 0)) {
392 ASSERT(structure
->m_transitions
.singleTransition
->m_offset
!= noOffset
);
393 offset
= structure
->m_transitions
.singleTransition
->m_offset
;
394 return existingTransition
;
397 if (Structure
* existingTransition
= structure
->m_transitions
.table
->get(make_pair(propertyName
.ustring().rep(), attributes
), specificValue
)) {
398 ASSERT(existingTransition
->m_offset
!= noOffset
);
399 offset
= existingTransition
->m_offset
;
400 return existingTransition
;
407 PassRefPtr
<Structure
> Structure::addPropertyTransition(Structure
* structure
, const Identifier
& propertyName
, unsigned attributes
, JSCell
* specificValue
, size_t& offset
)
409 ASSERT(!structure
->isDictionary());
410 ASSERT(structure
->typeInfo().type() == ObjectType
);
411 ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure
, propertyName
, attributes
, specificValue
, offset
));
413 if (structure
->transitionCount() > s_maxTransitionLength
) {
414 RefPtr
<Structure
> transition
= toCacheableDictionaryTransition(structure
);
415 ASSERT(structure
!= transition
);
416 offset
= transition
->put(propertyName
, attributes
, specificValue
);
417 if (transition
->propertyStorageSize() > transition
->propertyStorageCapacity())
418 transition
->growPropertyStorageCapacity();
419 return transition
.release();
422 RefPtr
<Structure
> transition
= create(structure
->m_prototype
, structure
->typeInfo());
424 transition
->m_cachedPrototypeChain
= structure
->m_cachedPrototypeChain
;
425 transition
->m_previous
= structure
;
426 transition
->m_nameInPrevious
= propertyName
.ustring().rep();
427 transition
->m_attributesInPrevious
= attributes
;
428 transition
->m_specificValueInPrevious
= specificValue
;
429 transition
->m_propertyStorageCapacity
= structure
->m_propertyStorageCapacity
;
430 transition
->m_hasGetterSetterProperties
= structure
->m_hasGetterSetterProperties
;
432 if (structure
->m_propertyTable
) {
433 if (structure
->m_isPinnedPropertyTable
)
434 transition
->m_propertyTable
= structure
->copyPropertyTable();
436 transition
->m_propertyTable
= structure
->m_propertyTable
;
437 structure
->m_propertyTable
= 0;
440 if (structure
->m_previous
)
441 transition
->materializePropertyMap();
443 transition
->createPropertyMapHashTable();
446 offset
= transition
->put(propertyName
, attributes
, specificValue
);
447 if (transition
->propertyStorageSize() > transition
->propertyStorageCapacity())
448 transition
->growPropertyStorageCapacity();
450 transition
->m_offset
= offset
;
452 if (structure
->m_usingSingleTransitionSlot
) {
453 if (!structure
->m_transitions
.singleTransition
) {
454 structure
->m_transitions
.singleTransition
= transition
.get();
455 return transition
.release();
458 Structure
* existingTransition
= structure
->m_transitions
.singleTransition
;
459 structure
->m_usingSingleTransitionSlot
= false;
460 StructureTransitionTable
* transitionTable
= new StructureTransitionTable
;
461 structure
->m_transitions
.table
= transitionTable
;
462 transitionTable
->add(make_pair(existingTransition
->m_nameInPrevious
.get(), existingTransition
->m_attributesInPrevious
), existingTransition
, existingTransition
->m_specificValueInPrevious
);
464 structure
->m_transitions
.table
->add(make_pair(propertyName
.ustring().rep(), attributes
), transition
.get(), specificValue
);
465 return transition
.release();
468 PassRefPtr
<Structure
> Structure::removePropertyTransition(Structure
* structure
, const Identifier
& propertyName
, size_t& offset
)
470 ASSERT(!structure
->isUncacheableDictionary());
472 RefPtr
<Structure
> transition
= toUncacheableDictionaryTransition(structure
);
474 offset
= transition
->remove(propertyName
);
476 return transition
.release();
479 PassRefPtr
<Structure
> Structure::changePrototypeTransition(Structure
* structure
, JSValue prototype
)
481 RefPtr
<Structure
> transition
= create(prototype
, structure
->typeInfo());
483 transition
->m_propertyStorageCapacity
= structure
->m_propertyStorageCapacity
;
484 transition
->m_hasGetterSetterProperties
= structure
->m_hasGetterSetterProperties
;
486 // Don't set m_offset, as one can not transition to this.
488 structure
->materializePropertyMapIfNecessary();
489 transition
->m_propertyTable
= structure
->copyPropertyTable();
490 transition
->m_isPinnedPropertyTable
= true;
492 return transition
.release();
495 PassRefPtr
<Structure
> Structure::despecifyFunctionTransition(Structure
* structure
, const Identifier
& replaceFunction
)
497 RefPtr
<Structure
> transition
= create(structure
->storedPrototype(), structure
->typeInfo());
499 transition
->m_propertyStorageCapacity
= structure
->m_propertyStorageCapacity
;
500 transition
->m_hasGetterSetterProperties
= structure
->m_hasGetterSetterProperties
;
502 // Don't set m_offset, as one can not transition to this.
504 structure
->materializePropertyMapIfNecessary();
505 transition
->m_propertyTable
= structure
->copyPropertyTable();
506 transition
->m_isPinnedPropertyTable
= true;
508 bool removed
= transition
->despecifyFunction(replaceFunction
);
509 ASSERT_UNUSED(removed
, removed
);
511 return transition
.release();
514 PassRefPtr
<Structure
> Structure::getterSetterTransition(Structure
* structure
)
516 RefPtr
<Structure
> transition
= create(structure
->storedPrototype(), structure
->typeInfo());
517 transition
->m_propertyStorageCapacity
= structure
->m_propertyStorageCapacity
;
518 transition
->m_hasGetterSetterProperties
= transition
->m_hasGetterSetterProperties
;
520 // Don't set m_offset, as one can not transition to this.
522 structure
->materializePropertyMapIfNecessary();
523 transition
->m_propertyTable
= structure
->copyPropertyTable();
524 transition
->m_isPinnedPropertyTable
= true;
526 return transition
.release();
529 PassRefPtr
<Structure
> Structure::toDictionaryTransition(Structure
* structure
, DictionaryKind kind
)
531 ASSERT(!structure
->isUncacheableDictionary());
533 RefPtr
<Structure
> transition
= create(structure
->m_prototype
, structure
->typeInfo());
534 transition
->m_dictionaryKind
= kind
;
535 transition
->m_propertyStorageCapacity
= structure
->m_propertyStorageCapacity
;
536 transition
->m_hasGetterSetterProperties
= structure
->m_hasGetterSetterProperties
;
538 structure
->materializePropertyMapIfNecessary();
539 transition
->m_propertyTable
= structure
->copyPropertyTable();
540 transition
->m_isPinnedPropertyTable
= true;
542 return transition
.release();
545 PassRefPtr
<Structure
> Structure::toCacheableDictionaryTransition(Structure
* structure
)
547 return toDictionaryTransition(structure
, CachedDictionaryKind
);
550 PassRefPtr
<Structure
> Structure::toUncacheableDictionaryTransition(Structure
* structure
)
552 return toDictionaryTransition(structure
, UncachedDictionaryKind
);
555 PassRefPtr
<Structure
> Structure::flattenDictionaryStructure(JSObject
* object
)
557 ASSERT(isDictionary());
558 if (isUncacheableDictionary()) {
559 ASSERT(m_propertyTable
);
560 Vector
<PropertyMapEntry
*> sortedPropertyEntries(m_propertyTable
->keyCount
);
561 PropertyMapEntry
** p
= sortedPropertyEntries
.data();
562 unsigned entryCount
= m_propertyTable
->keyCount
+ m_propertyTable
->deletedSentinelCount
;
563 for (unsigned i
= 1; i
<= entryCount
; i
++) {
564 if (m_propertyTable
->entries()[i
].key
)
565 *p
++ = &m_propertyTable
->entries()[i
];
567 size_t propertyCount
= p
- sortedPropertyEntries
.data();
568 qsort(sortedPropertyEntries
.data(), propertyCount
, sizeof(PropertyMapEntry
*), comparePropertyMapEntryIndices
);
569 sortedPropertyEntries
.resize(propertyCount
);
571 // We now have the properties currently defined on this object
572 // in the order that they are expected to be in, but we need to
573 // reorder the storage, so we have to copy the current values out
574 Vector
<JSValue
> values(propertyCount
);
575 // FIXME: Remove this workaround with the next merge.
576 unsigned anonymousSlotCount
= 0;
577 for (unsigned i
= 0; i
< propertyCount
; i
++) {
578 PropertyMapEntry
* entry
= sortedPropertyEntries
[i
];
579 values
[i
] = object
->getDirectOffset(entry
->offset
);
580 // Update property table to have the new property offsets
581 entry
->offset
= anonymousSlotCount
+ i
;
585 // Copy the original property values into their final locations
586 for (unsigned i
= 0; i
< propertyCount
; i
++)
587 object
->putDirectOffset(anonymousSlotCount
+ i
, values
[i
]);
589 if (m_propertyTable
->deletedOffsets
) {
590 delete m_propertyTable
->deletedOffsets
;
591 m_propertyTable
->deletedOffsets
= 0;
595 m_dictionaryKind
= NoneDictionaryKind
;
599 size_t Structure::addPropertyWithoutTransition(const Identifier
& propertyName
, unsigned attributes
, JSCell
* specificValue
)
601 ASSERT(!m_transitions
.singleTransition
);
603 materializePropertyMapIfNecessary();
605 m_isPinnedPropertyTable
= true;
606 size_t offset
= put(propertyName
, attributes
, specificValue
);
607 if (propertyStorageSize() > propertyStorageCapacity())
608 growPropertyStorageCapacity();
609 clearEnumerationCache();
613 size_t Structure::removePropertyWithoutTransition(const Identifier
& propertyName
)
615 ASSERT(isUncacheableDictionary());
617 materializePropertyMapIfNecessary();
619 m_isPinnedPropertyTable
= true;
620 size_t offset
= remove(propertyName
);
621 clearEnumerationCache();
625 #if DUMP_PROPERTYMAP_STATS
627 static int numProbes
;
628 static int numCollisions
;
629 static int numRehashes
;
630 static int numRemoves
;
632 struct PropertyMapStatisticsExitLogger
{
633 ~PropertyMapStatisticsExitLogger();
636 static PropertyMapStatisticsExitLogger logger
;
638 PropertyMapStatisticsExitLogger::~PropertyMapStatisticsExitLogger()
640 printf("\nJSC::PropertyMap statistics\n\n");
641 printf("%d probes\n", numProbes
);
642 printf("%d collisions (%.1f%%)\n", numCollisions
, 100.0 * numCollisions
/ numProbes
);
643 printf("%d rehashes\n", numRehashes
);
644 printf("%d removes\n", numRemoves
);
649 static const unsigned deletedSentinelIndex
= 1;
651 #if !DO_PROPERTYMAP_CONSTENCY_CHECK
653 inline void Structure::checkConsistency()
659 PropertyMapHashTable
* Structure::copyPropertyTable()
661 if (!m_propertyTable
)
664 size_t tableSize
= PropertyMapHashTable::allocationSize(m_propertyTable
->size
);
665 PropertyMapHashTable
* newTable
= static_cast<PropertyMapHashTable
*>(fastMalloc(tableSize
));
666 memcpy(newTable
, m_propertyTable
, tableSize
);
668 unsigned entryCount
= m_propertyTable
->keyCount
+ m_propertyTable
->deletedSentinelCount
;
669 for (unsigned i
= 1; i
<= entryCount
; ++i
) {
670 if (UString::Rep
* key
= newTable
->entries()[i
].key
)
674 // Copy the deletedOffsets vector.
675 if (m_propertyTable
->deletedOffsets
)
676 newTable
->deletedOffsets
= new Vector
<unsigned>(*m_propertyTable
->deletedOffsets
);
681 size_t Structure::get(const UString::Rep
* rep
, unsigned& attributes
, JSCell
*& specificValue
)
683 materializePropertyMapIfNecessary();
684 if (!m_propertyTable
)
687 unsigned i
= rep
->computedHash();
689 #if DUMP_PROPERTYMAP_STATS
693 unsigned entryIndex
= m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
];
694 if (entryIndex
== emptyEntryIndex
)
697 if (rep
== m_propertyTable
->entries()[entryIndex
- 1].key
) {
698 attributes
= m_propertyTable
->entries()[entryIndex
- 1].attributes
;
699 specificValue
= m_propertyTable
->entries()[entryIndex
- 1].specificValue
;
700 return m_propertyTable
->entries()[entryIndex
- 1].offset
;
703 #if DUMP_PROPERTYMAP_STATS
707 unsigned k
= 1 | doubleHash(rep
->computedHash());
712 #if DUMP_PROPERTYMAP_STATS
716 entryIndex
= m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
];
717 if (entryIndex
== emptyEntryIndex
)
720 if (rep
== m_propertyTable
->entries()[entryIndex
- 1].key
) {
721 attributes
= m_propertyTable
->entries()[entryIndex
- 1].attributes
;
722 specificValue
= m_propertyTable
->entries()[entryIndex
- 1].specificValue
;
723 return m_propertyTable
->entries()[entryIndex
- 1].offset
;
728 bool Structure::despecifyFunction(const Identifier
& propertyName
)
730 ASSERT(!propertyName
.isNull());
732 materializePropertyMapIfNecessary();
733 if (!m_propertyTable
)
736 UString::Rep
* rep
= propertyName
._ustring
.rep();
738 unsigned i
= rep
->computedHash();
740 #if DUMP_PROPERTYMAP_STATS
744 unsigned entryIndex
= m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
];
745 if (entryIndex
== emptyEntryIndex
)
748 if (rep
== m_propertyTable
->entries()[entryIndex
- 1].key
) {
749 ASSERT(m_propertyTable
->entries()[entryIndex
- 1].specificValue
);
750 m_propertyTable
->entries()[entryIndex
- 1].specificValue
= 0;
754 #if DUMP_PROPERTYMAP_STATS
758 unsigned k
= 1 | doubleHash(rep
->computedHash());
763 #if DUMP_PROPERTYMAP_STATS
767 entryIndex
= m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
];
768 if (entryIndex
== emptyEntryIndex
)
771 if (rep
== m_propertyTable
->entries()[entryIndex
- 1].key
) {
772 ASSERT(m_propertyTable
->entries()[entryIndex
- 1].specificValue
);
773 m_propertyTable
->entries()[entryIndex
- 1].specificValue
= 0;
779 size_t Structure::put(const Identifier
& propertyName
, unsigned attributes
, JSCell
* specificValue
)
781 ASSERT(!propertyName
.isNull());
782 ASSERT(get(propertyName
) == notFound
);
786 UString::Rep
* rep
= propertyName
._ustring
.rep();
788 if (!m_propertyTable
)
789 createPropertyMapHashTable();
791 // FIXME: Consider a fast case for tables with no deleted sentinels.
793 unsigned i
= rep
->computedHash();
795 bool foundDeletedElement
= false;
796 unsigned deletedElementIndex
= 0; // initialize to make the compiler happy
798 #if DUMP_PROPERTYMAP_STATS
803 unsigned entryIndex
= m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
];
804 if (entryIndex
== emptyEntryIndex
)
807 if (entryIndex
== deletedSentinelIndex
) {
808 // If we find a deleted-element sentinel, remember it for use later.
809 if (!foundDeletedElement
) {
810 foundDeletedElement
= true;
811 deletedElementIndex
= i
;
816 k
= 1 | doubleHash(rep
->computedHash());
817 #if DUMP_PROPERTYMAP_STATS
824 #if DUMP_PROPERTYMAP_STATS
829 // Figure out which entry to use.
830 unsigned entryIndex
= m_propertyTable
->keyCount
+ m_propertyTable
->deletedSentinelCount
+ 2;
831 if (foundDeletedElement
) {
832 i
= deletedElementIndex
;
833 --m_propertyTable
->deletedSentinelCount
;
835 // Since we're not making the table bigger, we can't use the entry one past
836 // the end that we were planning on using, so search backwards for the empty
837 // slot that we can use. We know it will be there because we did at least one
838 // deletion in the past that left an entry empty.
839 while (m_propertyTable
->entries()[--entryIndex
- 1].key
) { }
842 // Create a new hash table entry.
843 m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
] = entryIndex
;
845 // Create a new hash table entry.
847 m_propertyTable
->entries()[entryIndex
- 1].key
= rep
;
848 m_propertyTable
->entries()[entryIndex
- 1].attributes
= attributes
;
849 m_propertyTable
->entries()[entryIndex
- 1].specificValue
= specificValue
;
850 m_propertyTable
->entries()[entryIndex
- 1].index
= ++m_propertyTable
->lastIndexUsed
;
853 if (m_propertyTable
->deletedOffsets
&& !m_propertyTable
->deletedOffsets
->isEmpty()) {
854 newOffset
= m_propertyTable
->deletedOffsets
->last();
855 m_propertyTable
->deletedOffsets
->removeLast();
857 newOffset
= m_propertyTable
->keyCount
;
858 m_propertyTable
->entries()[entryIndex
- 1].offset
= newOffset
;
860 ++m_propertyTable
->keyCount
;
862 if ((m_propertyTable
->keyCount
+ m_propertyTable
->deletedSentinelCount
) * 2 >= m_propertyTable
->size
)
863 expandPropertyMapHashTable();
869 bool Structure::hasTransition(UString::Rep
* rep
, unsigned attributes
)
871 if (m_usingSingleTransitionSlot
) {
872 return m_transitions
.singleTransition
873 && m_transitions
.singleTransition
->m_nameInPrevious
== rep
874 && m_transitions
.singleTransition
->m_attributesInPrevious
== attributes
;
876 return m_transitions
.table
->hasTransition(make_pair(rep
, attributes
));
879 size_t Structure::remove(const Identifier
& propertyName
)
881 ASSERT(!propertyName
.isNull());
885 UString::Rep
* rep
= propertyName
._ustring
.rep();
887 if (!m_propertyTable
)
890 #if DUMP_PROPERTYMAP_STATS
895 // Find the thing to remove.
896 unsigned i
= rep
->computedHash();
899 UString::Rep
* key
= 0;
901 entryIndex
= m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
];
902 if (entryIndex
== emptyEntryIndex
)
905 key
= m_propertyTable
->entries()[entryIndex
- 1].key
;
910 k
= 1 | doubleHash(rep
->computedHash());
911 #if DUMP_PROPERTYMAP_STATS
918 #if DUMP_PROPERTYMAP_STATS
923 // Replace this one element with the deleted sentinel. Also clear out
924 // the entry so we can iterate all the entries as needed.
925 m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
] = deletedSentinelIndex
;
927 size_t offset
= m_propertyTable
->entries()[entryIndex
- 1].offset
;
930 m_propertyTable
->entries()[entryIndex
- 1].key
= 0;
931 m_propertyTable
->entries()[entryIndex
- 1].attributes
= 0;
932 m_propertyTable
->entries()[entryIndex
- 1].specificValue
= 0;
933 m_propertyTable
->entries()[entryIndex
- 1].offset
= 0;
935 if (!m_propertyTable
->deletedOffsets
)
936 m_propertyTable
->deletedOffsets
= new Vector
<unsigned>;
937 m_propertyTable
->deletedOffsets
->append(offset
);
939 ASSERT(m_propertyTable
->keyCount
>= 1);
940 --m_propertyTable
->keyCount
;
941 ++m_propertyTable
->deletedSentinelCount
;
943 if (m_propertyTable
->deletedSentinelCount
* 4 >= m_propertyTable
->size
)
944 rehashPropertyMapHashTable();
950 void Structure::insertIntoPropertyMapHashTable(const PropertyMapEntry
& entry
)
952 ASSERT(m_propertyTable
);
954 unsigned i
= entry
.key
->computedHash();
957 #if DUMP_PROPERTYMAP_STATS
962 unsigned entryIndex
= m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
];
963 if (entryIndex
== emptyEntryIndex
)
967 k
= 1 | doubleHash(entry
.key
->computedHash());
968 #if DUMP_PROPERTYMAP_STATS
975 #if DUMP_PROPERTYMAP_STATS
980 unsigned entryIndex
= m_propertyTable
->keyCount
+ 2;
981 m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
] = entryIndex
;
982 m_propertyTable
->entries()[entryIndex
- 1] = entry
;
984 ++m_propertyTable
->keyCount
;
987 void Structure::createPropertyMapHashTable()
989 ASSERT(sizeForKeyCount(7) == newTableSize
);
990 createPropertyMapHashTable(newTableSize
);
993 void Structure::createPropertyMapHashTable(unsigned newTableSize
)
995 ASSERT(!m_propertyTable
);
996 ASSERT(isPowerOf2(newTableSize
));
1000 m_propertyTable
= static_cast<PropertyMapHashTable
*>(fastZeroedMalloc(PropertyMapHashTable::allocationSize(newTableSize
)));
1001 m_propertyTable
->size
= newTableSize
;
1002 m_propertyTable
->sizeMask
= newTableSize
- 1;
1007 void Structure::expandPropertyMapHashTable()
1009 ASSERT(m_propertyTable
);
1010 rehashPropertyMapHashTable(m_propertyTable
->size
* 2);
1013 void Structure::rehashPropertyMapHashTable()
1015 ASSERT(m_propertyTable
);
1016 ASSERT(m_propertyTable
->size
);
1017 rehashPropertyMapHashTable(m_propertyTable
->size
);
1020 void Structure::rehashPropertyMapHashTable(unsigned newTableSize
)
1022 ASSERT(m_propertyTable
);
1023 ASSERT(isPowerOf2(newTableSize
));
1027 PropertyMapHashTable
* oldTable
= m_propertyTable
;
1029 m_propertyTable
= static_cast<PropertyMapHashTable
*>(fastZeroedMalloc(PropertyMapHashTable::allocationSize(newTableSize
)));
1030 m_propertyTable
->size
= newTableSize
;
1031 m_propertyTable
->sizeMask
= newTableSize
- 1;
1033 unsigned lastIndexUsed
= 0;
1034 unsigned entryCount
= oldTable
->keyCount
+ oldTable
->deletedSentinelCount
;
1035 for (unsigned i
= 1; i
<= entryCount
; ++i
) {
1036 if (oldTable
->entries()[i
].key
) {
1037 lastIndexUsed
= max(oldTable
->entries()[i
].index
, lastIndexUsed
);
1038 insertIntoPropertyMapHashTable(oldTable
->entries()[i
]);
1041 m_propertyTable
->lastIndexUsed
= lastIndexUsed
;
1042 m_propertyTable
->deletedOffsets
= oldTable
->deletedOffsets
;
1049 int comparePropertyMapEntryIndices(const void* a
, const void* b
)
1051 unsigned ia
= static_cast<PropertyMapEntry
* const*>(a
)[0]->index
;
1052 unsigned ib
= static_cast<PropertyMapEntry
* const*>(b
)[0]->index
;
1060 void Structure::getEnumerableNamesFromPropertyTable(PropertyNameArray
& propertyNames
)
1062 materializePropertyMapIfNecessary();
1063 if (!m_propertyTable
)
1066 if (m_propertyTable
->keyCount
< tinyMapThreshold
) {
1067 PropertyMapEntry
* a
[tinyMapThreshold
];
1069 unsigned entryCount
= m_propertyTable
->keyCount
+ m_propertyTable
->deletedSentinelCount
;
1070 for (unsigned k
= 1; k
<= entryCount
; k
++) {
1071 if (m_propertyTable
->entries()[k
].key
&& !(m_propertyTable
->entries()[k
].attributes
& DontEnum
)) {
1072 PropertyMapEntry
* value
= &m_propertyTable
->entries()[k
];
1074 for (j
= i
- 1; j
>= 0 && a
[j
]->index
> value
->index
; --j
)
1080 if (!propertyNames
.size()) {
1081 for (int k
= 0; k
< i
; ++k
)
1082 propertyNames
.addKnownUnique(a
[k
]->key
);
1084 for (int k
= 0; k
< i
; ++k
)
1085 propertyNames
.add(a
[k
]->key
);
1091 // Allocate a buffer to use to sort the keys.
1092 Vector
<PropertyMapEntry
*, smallMapThreshold
> sortedEnumerables(m_propertyTable
->keyCount
);
1094 // Get pointers to the enumerable entries in the buffer.
1095 PropertyMapEntry
** p
= sortedEnumerables
.data();
1096 unsigned entryCount
= m_propertyTable
->keyCount
+ m_propertyTable
->deletedSentinelCount
;
1097 for (unsigned i
= 1; i
<= entryCount
; i
++) {
1098 if (m_propertyTable
->entries()[i
].key
&& !(m_propertyTable
->entries()[i
].attributes
& DontEnum
))
1099 *p
++ = &m_propertyTable
->entries()[i
];
1102 size_t enumerableCount
= p
- sortedEnumerables
.data();
1103 // Sort the entries by index.
1104 qsort(sortedEnumerables
.data(), enumerableCount
, sizeof(PropertyMapEntry
*), comparePropertyMapEntryIndices
);
1105 sortedEnumerables
.resize(enumerableCount
);
1107 // Put the keys of the sorted entries into the list.
1108 if (!propertyNames
.size()) {
1109 for (size_t i
= 0; i
< sortedEnumerables
.size(); ++i
)
1110 propertyNames
.addKnownUnique(sortedEnumerables
[i
]->key
);
1112 for (size_t i
= 0; i
< sortedEnumerables
.size(); ++i
)
1113 propertyNames
.add(sortedEnumerables
[i
]->key
);
1117 void Structure::getEnumerableNamesFromClassInfoTable(ExecState
* exec
, const ClassInfo
* classInfo
, PropertyNameArray
& propertyNames
)
1119 // Add properties from the static hashtables of properties
1120 for (; classInfo
; classInfo
= classInfo
->parentClass
) {
1121 const HashTable
* table
= classInfo
->propHashTable(exec
);
1124 table
->initializeIfNeeded(exec
);
1125 ASSERT(table
->table
);
1127 int hashSizeMask
= table
->compactSize
- 1;
1128 const HashEntry
* entry
= table
->table
;
1129 for (int i
= 0; i
<= hashSizeMask
; ++i
, ++entry
) {
1130 if (entry
->key() && !(entry
->attributes() & DontEnum
))
1131 propertyNames
.add(entry
->key());
1136 #if DO_PROPERTYMAP_CONSTENCY_CHECK
1138 void Structure::checkConsistency()
1140 if (!m_propertyTable
)
1143 ASSERT(m_propertyTable
->size
>= newTableSize
);
1144 ASSERT(m_propertyTable
->sizeMask
);
1145 ASSERT(m_propertyTable
->size
== m_propertyTable
->sizeMask
+ 1);
1146 ASSERT(!(m_propertyTable
->size
& m_propertyTable
->sizeMask
));
1148 ASSERT(m_propertyTable
->keyCount
<= m_propertyTable
->size
/ 2);
1149 ASSERT(m_propertyTable
->deletedSentinelCount
<= m_propertyTable
->size
/ 4);
1151 ASSERT(m_propertyTable
->keyCount
+ m_propertyTable
->deletedSentinelCount
<= m_propertyTable
->size
/ 2);
1153 unsigned indexCount
= 0;
1154 unsigned deletedIndexCount
= 0;
1155 for (unsigned a
= 0; a
!= m_propertyTable
->size
; ++a
) {
1156 unsigned entryIndex
= m_propertyTable
->entryIndices
[a
];
1157 if (entryIndex
== emptyEntryIndex
)
1159 if (entryIndex
== deletedSentinelIndex
) {
1160 ++deletedIndexCount
;
1163 ASSERT(entryIndex
> deletedSentinelIndex
);
1164 ASSERT(entryIndex
- 1 <= m_propertyTable
->keyCount
+ m_propertyTable
->deletedSentinelCount
);
1167 for (unsigned b
= a
+ 1; b
!= m_propertyTable
->size
; ++b
)
1168 ASSERT(m_propertyTable
->entryIndices
[b
] != entryIndex
);
1170 ASSERT(indexCount
== m_propertyTable
->keyCount
);
1171 ASSERT(deletedIndexCount
== m_propertyTable
->deletedSentinelCount
);
1173 ASSERT(m_propertyTable
->entries()[0].key
== 0);
1175 unsigned nonEmptyEntryCount
= 0;
1176 for (unsigned c
= 1; c
<= m_propertyTable
->keyCount
+ m_propertyTable
->deletedSentinelCount
; ++c
) {
1177 UString::Rep
* rep
= m_propertyTable
->entries()[c
].key
;
1180 ++nonEmptyEntryCount
;
1181 unsigned i
= rep
->computedHash();
1183 unsigned entryIndex
;
1185 entryIndex
= m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
];
1186 ASSERT(entryIndex
!= emptyEntryIndex
);
1187 if (rep
== m_propertyTable
->entries()[entryIndex
- 1].key
)
1190 k
= 1 | doubleHash(rep
->computedHash());
1193 ASSERT(entryIndex
== c
+ 1);
1196 ASSERT(nonEmptyEntryCount
== m_propertyTable
->keyCount
);
1199 #endif // DO_PROPERTYMAP_CONSTENCY_CHECK