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 "JSPropertyNameIterator.h"
33 #include "PropertyNameArray.h"
34 #include "StructureChain.h"
35 #include <wtf/RefCountedLeakCounter.h>
36 #include <wtf/RefPtr.h>
38 #if ENABLE(JSC_MULTIPLE_THREADS)
39 #include <wtf/Threading.h>
42 #define DUMP_STRUCTURE_ID_STATISTICS 0
45 #define DO_PROPERTYMAP_CONSTENCY_CHECK 0
47 #define DO_PROPERTYMAP_CONSTENCY_CHECK 0
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;
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;
63 static const unsigned newTableSize
= 16;
66 static WTF::RefCountedLeakCounter
structureCounter("Structure");
68 #if ENABLE(JSC_MULTIPLE_THREADS)
69 static Mutex
& ignoreSetMutex
= *(new Mutex
);
72 static bool shouldIgnoreLeaks
;
73 static HashSet
<Structure
*>& ignoreSet
= *(new HashSet
<Structure
*>);
76 #if DUMP_STRUCTURE_ID_STATISTICS
77 static HashSet
<Structure
*>& liveStructureSet
= *(new HashSet
<Structure
*>);
80 static int comparePropertyMapEntryIndices(const void* a
, const void* b
);
82 void Structure::dumpStatistics()
84 #if DUMP_STRUCTURE_ID_STATISTICS
85 unsigned numberLeaf
= 0;
86 unsigned numberUsingSingleSlot
= 0;
87 unsigned numberSingletons
= 0;
88 unsigned numberWithPropertyMaps
= 0;
89 unsigned totalPropertyMapsSize
= 0;
91 HashSet
<Structure
*>::const_iterator end
= liveStructureSet
.end();
92 for (HashSet
<Structure
*>::const_iterator it
= liveStructureSet
.begin(); it
!= end
; ++it
) {
93 Structure
* structure
= *it
;
94 if (structure
->m_usingSingleTransitionSlot
) {
95 if (!structure
->m_transitions
.singleTransition
)
98 ++numberUsingSingleSlot
;
100 if (!structure
->m_previous
&& !structure
->m_transitions
.singleTransition
)
104 if (structure
->m_propertyTable
) {
105 ++numberWithPropertyMaps
;
106 totalPropertyMapsSize
+= PropertyMapHashTable::allocationSize(structure
->m_propertyTable
->size
);
107 if (structure
->m_propertyTable
->deletedOffsets
)
108 totalPropertyMapsSize
+= (structure
->m_propertyTable
->deletedOffsets
->capacity() * sizeof(unsigned));
112 printf("Number of live Structures: %d\n", liveStructureSet
.size());
113 printf("Number of Structures using the single item optimization for transition map: %d\n", numberUsingSingleSlot
);
114 printf("Number of Structures that are leaf nodes: %d\n", numberLeaf
);
115 printf("Number of Structures that singletons: %d\n", numberSingletons
);
116 printf("Number of Structures with PropertyMaps: %d\n", numberWithPropertyMaps
);
118 printf("Size of a single Structures: %d\n", static_cast<unsigned>(sizeof(Structure
)));
119 printf("Size of sum of all property maps: %d\n", totalPropertyMapsSize
);
120 printf("Size of average of all property maps: %f\n", static_cast<double>(totalPropertyMapsSize
) / static_cast<double>(liveStructureSet
.size()));
122 printf("Dumping Structure statistics is not enabled.\n");
126 Structure::Structure(JSValue prototype
, const TypeInfo
& typeInfo
, unsigned anonymousSlotCount
)
127 : m_typeInfo(typeInfo
)
128 , m_prototype(prototype
)
129 , m_specificValueInPrevious(0)
131 , m_propertyStorageCapacity(JSObject::inlineStorageCapacity
)
133 , m_dictionaryKind(NoneDictionaryKind
)
134 , m_isPinnedPropertyTable(false)
135 , m_hasGetterSetterProperties(false)
136 , m_attributesInPrevious(0)
137 , m_specificFunctionThrashCount(0)
138 , m_anonymousSlotCount(anonymousSlotCount
)
141 ASSERT(m_prototype
.isObject() || m_prototype
.isNull());
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 ASSERT(m_nameInPrevious
);
162 m_previous
->table
.remove(make_pair(m_nameInPrevious
.get(), m_attributesInPrevious
), m_specificValueInPrevious
);
166 if (m_enumerationCache
)
167 m_enumerationCache
->setCachedStructure(0);
169 if (m_propertyTable
) {
170 unsigned entryCount
= m_propertyTable
->keyCount
+ m_propertyTable
->deletedSentinelCount
;
171 for (unsigned i
= 1; i
<= entryCount
; i
++) {
172 if (UString::Rep
* key
= m_propertyTable
->entries()[i
].key
)
176 delete m_propertyTable
->deletedOffsets
;
177 fastFree(m_propertyTable
);
181 #if ENABLE(JSC_MULTIPLE_THREADS)
182 MutexLocker
protect(ignoreSetMutex
);
184 HashSet
<Structure
*>::iterator it
= ignoreSet
.find(this);
185 if (it
!= ignoreSet
.end())
186 ignoreSet
.remove(it
);
188 structureCounter
.decrement();
191 #if DUMP_STRUCTURE_ID_STATISTICS
192 liveStructureSet
.remove(this);
196 void Structure::startIgnoringLeaks()
199 shouldIgnoreLeaks
= true;
203 void Structure::stopIgnoringLeaks()
206 shouldIgnoreLeaks
= false;
210 static bool isPowerOf2(unsigned v
)
212 // Taken from http://www.cs.utk.edu/~vose/c-stuff/bithacks.html
214 return !(v
& (v
- 1)) && v
;
217 static unsigned nextPowerOf2(unsigned v
)
219 // Taken from http://www.cs.utk.edu/~vose/c-stuff/bithacks.html
220 // Devised by Sean Anderson, Sepember 14, 2001
233 static unsigned sizeForKeyCount(size_t keyCount
)
235 if (keyCount
== notFound
)
241 if (isPowerOf2(keyCount
))
244 return nextPowerOf2(keyCount
) * 2;
247 void Structure::materializePropertyMap()
249 ASSERT(!m_propertyTable
);
251 Vector
<Structure
*, 8> structures
;
252 structures
.append(this);
254 Structure
* structure
= this;
256 // Search for the last Structure with a property table.
257 while ((structure
= structure
->previousID())) {
258 if (structure
->m_isPinnedPropertyTable
) {
259 ASSERT(structure
->m_propertyTable
);
260 ASSERT(!structure
->m_previous
);
262 m_propertyTable
= structure
->copyPropertyTable();
266 structures
.append(structure
);
269 if (!m_propertyTable
)
270 createPropertyMapHashTable(sizeForKeyCount(m_offset
+ 1));
272 if (sizeForKeyCount(m_offset
+ 1) > m_propertyTable
->size
)
273 rehashPropertyMapHashTable(sizeForKeyCount(m_offset
+ 1)); // This could be made more efficient by combining with the copy above.
276 for (ptrdiff_t i
= structures
.size() - 2; i
>= 0; --i
) {
277 structure
= structures
[i
];
278 structure
->m_nameInPrevious
->ref();
279 PropertyMapEntry
entry(structure
->m_nameInPrevious
.get(), m_anonymousSlotCount
+ structure
->m_offset
, structure
->m_attributesInPrevious
, structure
->m_specificValueInPrevious
, ++m_propertyTable
->lastIndexUsed
);
280 insertIntoPropertyMapHashTable(entry
);
284 void Structure::growPropertyStorageCapacity()
286 if (m_propertyStorageCapacity
== JSObject::inlineStorageCapacity
)
287 m_propertyStorageCapacity
= JSObject::nonInlineBaseStorageCapacity
;
289 m_propertyStorageCapacity
*= 2;
292 void Structure::despecifyDictionaryFunction(const Identifier
& propertyName
)
294 const UString::Rep
* rep
= propertyName
._ustring
.rep();
296 materializePropertyMapIfNecessary();
298 ASSERT(isDictionary());
299 ASSERT(m_propertyTable
);
301 unsigned i
= rep
->existingHash();
303 #if DUMP_PROPERTYMAP_STATS
307 unsigned entryIndex
= m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
];
308 ASSERT(entryIndex
!= emptyEntryIndex
);
310 if (rep
== m_propertyTable
->entries()[entryIndex
- 1].key
) {
311 m_propertyTable
->entries()[entryIndex
- 1].specificValue
= 0;
315 #if DUMP_PROPERTYMAP_STATS
319 unsigned k
= 1 | doubleHash(rep
->existingHash());
324 #if DUMP_PROPERTYMAP_STATS
328 entryIndex
= m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
];
329 ASSERT(entryIndex
!= emptyEntryIndex
);
331 if (rep
== m_propertyTable
->entries()[entryIndex
- 1].key
) {
332 m_propertyTable
->entries()[entryIndex
- 1].specificValue
= 0;
338 PassRefPtr
<Structure
> Structure::addPropertyTransitionToExistingStructure(Structure
* structure
, const Identifier
& propertyName
, unsigned attributes
, JSCell
* specificValue
, size_t& offset
)
340 ASSERT(!structure
->isDictionary());
341 ASSERT(structure
->typeInfo().type() == ObjectType
);
343 if (Structure
* existingTransition
= structure
->table
.get(make_pair(propertyName
.ustring().rep(), attributes
), specificValue
)) {
344 ASSERT(existingTransition
->m_offset
!= noOffset
);
345 offset
= existingTransition
->m_offset
+ existingTransition
->m_anonymousSlotCount
;
346 ASSERT(offset
>= structure
->m_anonymousSlotCount
);
347 ASSERT(structure
->m_anonymousSlotCount
== existingTransition
->m_anonymousSlotCount
);
348 return existingTransition
;
354 PassRefPtr
<Structure
> Structure::addPropertyTransition(Structure
* structure
, const Identifier
& propertyName
, unsigned attributes
, JSCell
* specificValue
, size_t& offset
)
356 ASSERT(!structure
->isDictionary());
357 ASSERT(structure
->typeInfo().type() == ObjectType
);
358 ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure
, propertyName
, attributes
, specificValue
, offset
));
360 if (structure
->m_specificFunctionThrashCount
== maxSpecificFunctionThrashCount
)
363 if (structure
->transitionCount() > s_maxTransitionLength
) {
364 RefPtr
<Structure
> transition
= toCacheableDictionaryTransition(structure
);
365 ASSERT(structure
!= transition
);
366 offset
= transition
->put(propertyName
, attributes
, specificValue
);
367 ASSERT(offset
>= structure
->m_anonymousSlotCount
);
368 ASSERT(structure
->m_anonymousSlotCount
== transition
->m_anonymousSlotCount
);
369 if (transition
->propertyStorageSize() > transition
->propertyStorageCapacity())
370 transition
->growPropertyStorageCapacity();
371 return transition
.release();
374 RefPtr
<Structure
> transition
= create(structure
->m_prototype
, structure
->typeInfo(), structure
->anonymousSlotCount());
376 transition
->m_cachedPrototypeChain
= structure
->m_cachedPrototypeChain
;
377 transition
->m_previous
= structure
;
378 transition
->m_nameInPrevious
= propertyName
.ustring().rep();
379 transition
->m_attributesInPrevious
= attributes
;
380 transition
->m_specificValueInPrevious
= specificValue
;
381 transition
->m_propertyStorageCapacity
= structure
->m_propertyStorageCapacity
;
382 transition
->m_hasGetterSetterProperties
= structure
->m_hasGetterSetterProperties
;
383 transition
->m_hasNonEnumerableProperties
= structure
->m_hasNonEnumerableProperties
;
384 transition
->m_specificFunctionThrashCount
= structure
->m_specificFunctionThrashCount
;
386 if (structure
->m_propertyTable
) {
387 if (structure
->m_isPinnedPropertyTable
)
388 transition
->m_propertyTable
= structure
->copyPropertyTable();
390 transition
->m_propertyTable
= structure
->m_propertyTable
;
391 structure
->m_propertyTable
= 0;
394 if (structure
->m_previous
)
395 transition
->materializePropertyMap();
397 transition
->createPropertyMapHashTable();
400 offset
= transition
->put(propertyName
, attributes
, specificValue
);
401 ASSERT(offset
>= structure
->m_anonymousSlotCount
);
402 ASSERT(structure
->m_anonymousSlotCount
== transition
->m_anonymousSlotCount
);
403 if (transition
->propertyStorageSize() > transition
->propertyStorageCapacity())
404 transition
->growPropertyStorageCapacity();
406 transition
->m_offset
= offset
- structure
->m_anonymousSlotCount
;
407 ASSERT(structure
->anonymousSlotCount() == transition
->anonymousSlotCount());
408 structure
->table
.add(make_pair(propertyName
.ustring().rep(), attributes
), transition
.get(), specificValue
);
409 return transition
.release();
412 PassRefPtr
<Structure
> Structure::removePropertyTransition(Structure
* structure
, const Identifier
& propertyName
, size_t& offset
)
414 ASSERT(!structure
->isUncacheableDictionary());
416 RefPtr
<Structure
> transition
= toUncacheableDictionaryTransition(structure
);
418 offset
= transition
->remove(propertyName
);
419 ASSERT(offset
>= structure
->m_anonymousSlotCount
);
420 ASSERT(structure
->m_anonymousSlotCount
== transition
->m_anonymousSlotCount
);
422 return transition
.release();
425 PassRefPtr
<Structure
> Structure::changePrototypeTransition(Structure
* structure
, JSValue prototype
)
427 RefPtr
<Structure
> transition
= create(prototype
, structure
->typeInfo(), structure
->anonymousSlotCount());
429 transition
->m_propertyStorageCapacity
= structure
->m_propertyStorageCapacity
;
430 transition
->m_hasGetterSetterProperties
= structure
->m_hasGetterSetterProperties
;
431 transition
->m_hasNonEnumerableProperties
= structure
->m_hasNonEnumerableProperties
;
432 transition
->m_specificFunctionThrashCount
= structure
->m_specificFunctionThrashCount
;
434 // Don't set m_offset, as one can not transition to this.
436 structure
->materializePropertyMapIfNecessary();
437 transition
->m_propertyTable
= structure
->copyPropertyTable();
438 transition
->m_isPinnedPropertyTable
= true;
440 ASSERT(structure
->anonymousSlotCount() == transition
->anonymousSlotCount());
441 return transition
.release();
444 PassRefPtr
<Structure
> Structure::despecifyFunctionTransition(Structure
* structure
, const Identifier
& replaceFunction
)
446 ASSERT(structure
->m_specificFunctionThrashCount
< maxSpecificFunctionThrashCount
);
447 RefPtr
<Structure
> transition
= create(structure
->storedPrototype(), structure
->typeInfo(), structure
->anonymousSlotCount());
449 transition
->m_propertyStorageCapacity
= structure
->m_propertyStorageCapacity
;
450 transition
->m_hasGetterSetterProperties
= structure
->m_hasGetterSetterProperties
;
451 transition
->m_hasNonEnumerableProperties
= structure
->m_hasNonEnumerableProperties
;
452 transition
->m_specificFunctionThrashCount
= structure
->m_specificFunctionThrashCount
+ 1;
454 // Don't set m_offset, as one can not transition to this.
456 structure
->materializePropertyMapIfNecessary();
457 transition
->m_propertyTable
= structure
->copyPropertyTable();
458 transition
->m_isPinnedPropertyTable
= true;
460 if (transition
->m_specificFunctionThrashCount
== maxSpecificFunctionThrashCount
)
461 transition
->despecifyAllFunctions();
463 bool removed
= transition
->despecifyFunction(replaceFunction
);
464 ASSERT_UNUSED(removed
, removed
);
467 ASSERT(structure
->anonymousSlotCount() == transition
->anonymousSlotCount());
468 return transition
.release();
471 PassRefPtr
<Structure
> Structure::getterSetterTransition(Structure
* structure
)
473 RefPtr
<Structure
> transition
= create(structure
->storedPrototype(), structure
->typeInfo(), structure
->anonymousSlotCount());
474 transition
->m_propertyStorageCapacity
= structure
->m_propertyStorageCapacity
;
475 transition
->m_hasGetterSetterProperties
= transition
->m_hasGetterSetterProperties
;
476 transition
->m_hasNonEnumerableProperties
= structure
->m_hasNonEnumerableProperties
;
477 transition
->m_specificFunctionThrashCount
= structure
->m_specificFunctionThrashCount
;
479 // Don't set m_offset, as one can not transition to this.
481 structure
->materializePropertyMapIfNecessary();
482 transition
->m_propertyTable
= structure
->copyPropertyTable();
483 transition
->m_isPinnedPropertyTable
= true;
485 ASSERT(structure
->anonymousSlotCount() == transition
->anonymousSlotCount());
486 return transition
.release();
489 PassRefPtr
<Structure
> Structure::toDictionaryTransition(Structure
* structure
, DictionaryKind kind
)
491 ASSERT(!structure
->isUncacheableDictionary());
493 RefPtr
<Structure
> transition
= create(structure
->m_prototype
, structure
->typeInfo(), structure
->anonymousSlotCount());
494 transition
->m_dictionaryKind
= kind
;
495 transition
->m_propertyStorageCapacity
= structure
->m_propertyStorageCapacity
;
496 transition
->m_hasGetterSetterProperties
= structure
->m_hasGetterSetterProperties
;
497 transition
->m_hasNonEnumerableProperties
= structure
->m_hasNonEnumerableProperties
;
498 transition
->m_specificFunctionThrashCount
= structure
->m_specificFunctionThrashCount
;
500 structure
->materializePropertyMapIfNecessary();
501 transition
->m_propertyTable
= structure
->copyPropertyTable();
502 transition
->m_isPinnedPropertyTable
= true;
504 ASSERT(structure
->anonymousSlotCount() == transition
->anonymousSlotCount());
505 return transition
.release();
508 PassRefPtr
<Structure
> Structure::toCacheableDictionaryTransition(Structure
* structure
)
510 return toDictionaryTransition(structure
, CachedDictionaryKind
);
513 PassRefPtr
<Structure
> Structure::toUncacheableDictionaryTransition(Structure
* structure
)
515 return toDictionaryTransition(structure
, UncachedDictionaryKind
);
518 PassRefPtr
<Structure
> Structure::flattenDictionaryStructure(JSObject
* object
)
520 ASSERT(isDictionary());
521 if (isUncacheableDictionary()) {
522 ASSERT(m_propertyTable
);
523 Vector
<PropertyMapEntry
*> sortedPropertyEntries(m_propertyTable
->keyCount
);
524 PropertyMapEntry
** p
= sortedPropertyEntries
.data();
525 unsigned entryCount
= m_propertyTable
->keyCount
+ m_propertyTable
->deletedSentinelCount
;
526 for (unsigned i
= 1; i
<= entryCount
; i
++) {
527 if (m_propertyTable
->entries()[i
].key
)
528 *p
++ = &m_propertyTable
->entries()[i
];
530 size_t propertyCount
= p
- sortedPropertyEntries
.data();
531 qsort(sortedPropertyEntries
.data(), propertyCount
, sizeof(PropertyMapEntry
*), comparePropertyMapEntryIndices
);
532 sortedPropertyEntries
.resize(propertyCount
);
534 // We now have the properties currently defined on this object
535 // in the order that they are expected to be in, but we need to
536 // reorder the storage, so we have to copy the current values out
537 Vector
<JSValue
> values(propertyCount
);
538 unsigned anonymousSlotCount
= m_anonymousSlotCount
;
539 for (unsigned i
= 0; i
< propertyCount
; i
++) {
540 PropertyMapEntry
* entry
= sortedPropertyEntries
[i
];
541 values
[i
] = object
->getDirectOffset(entry
->offset
);
542 // Update property table to have the new property offsets
543 entry
->offset
= anonymousSlotCount
+ i
;
547 // Copy the original property values into their final locations
548 for (unsigned i
= 0; i
< propertyCount
; i
++)
549 object
->putDirectOffset(anonymousSlotCount
+ i
, values
[i
]);
551 if (m_propertyTable
->deletedOffsets
) {
552 delete m_propertyTable
->deletedOffsets
;
553 m_propertyTable
->deletedOffsets
= 0;
557 m_dictionaryKind
= NoneDictionaryKind
;
561 size_t Structure::addPropertyWithoutTransition(const Identifier
& propertyName
, unsigned attributes
, JSCell
* specificValue
)
563 ASSERT(!m_enumerationCache
);
565 if (m_specificFunctionThrashCount
== maxSpecificFunctionThrashCount
)
568 materializePropertyMapIfNecessary();
570 m_isPinnedPropertyTable
= true;
572 size_t offset
= put(propertyName
, attributes
, specificValue
);
573 ASSERT(offset
>= m_anonymousSlotCount
);
574 if (propertyStorageSize() > propertyStorageCapacity())
575 growPropertyStorageCapacity();
579 size_t Structure::removePropertyWithoutTransition(const Identifier
& propertyName
)
581 ASSERT(isUncacheableDictionary());
582 ASSERT(!m_enumerationCache
);
584 materializePropertyMapIfNecessary();
586 m_isPinnedPropertyTable
= true;
587 size_t offset
= remove(propertyName
);
588 ASSERT(offset
>= m_anonymousSlotCount
);
592 #if DUMP_PROPERTYMAP_STATS
594 static int numProbes
;
595 static int numCollisions
;
596 static int numRehashes
;
597 static int numRemoves
;
599 struct PropertyMapStatisticsExitLogger
{
600 ~PropertyMapStatisticsExitLogger();
603 static PropertyMapStatisticsExitLogger logger
;
605 PropertyMapStatisticsExitLogger::~PropertyMapStatisticsExitLogger()
607 printf("\nJSC::PropertyMap statistics\n\n");
608 printf("%d probes\n", numProbes
);
609 printf("%d collisions (%.1f%%)\n", numCollisions
, 100.0 * numCollisions
/ numProbes
);
610 printf("%d rehashes\n", numRehashes
);
611 printf("%d removes\n", numRemoves
);
616 static const unsigned deletedSentinelIndex
= 1;
618 #if !DO_PROPERTYMAP_CONSTENCY_CHECK
620 inline void Structure::checkConsistency()
626 PropertyMapHashTable
* Structure::copyPropertyTable()
628 if (!m_propertyTable
)
631 size_t tableSize
= PropertyMapHashTable::allocationSize(m_propertyTable
->size
);
632 PropertyMapHashTable
* newTable
= static_cast<PropertyMapHashTable
*>(fastMalloc(tableSize
));
633 memcpy(newTable
, m_propertyTable
, tableSize
);
635 unsigned entryCount
= m_propertyTable
->keyCount
+ m_propertyTable
->deletedSentinelCount
;
636 for (unsigned i
= 1; i
<= entryCount
; ++i
) {
637 if (UString::Rep
* key
= newTable
->entries()[i
].key
)
641 // Copy the deletedOffsets vector.
642 if (m_propertyTable
->deletedOffsets
)
643 newTable
->deletedOffsets
= new Vector
<unsigned>(*m_propertyTable
->deletedOffsets
);
648 size_t Structure::get(const UString::Rep
* rep
, unsigned& attributes
, JSCell
*& specificValue
)
650 materializePropertyMapIfNecessary();
651 if (!m_propertyTable
)
654 unsigned i
= rep
->existingHash();
656 #if DUMP_PROPERTYMAP_STATS
660 unsigned entryIndex
= m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
];
661 if (entryIndex
== emptyEntryIndex
)
664 if (rep
== m_propertyTable
->entries()[entryIndex
- 1].key
) {
665 attributes
= m_propertyTable
->entries()[entryIndex
- 1].attributes
;
666 specificValue
= m_propertyTable
->entries()[entryIndex
- 1].specificValue
;
667 ASSERT(m_propertyTable
->entries()[entryIndex
- 1].offset
>= m_anonymousSlotCount
);
668 return m_propertyTable
->entries()[entryIndex
- 1].offset
;
671 #if DUMP_PROPERTYMAP_STATS
675 unsigned k
= 1 | doubleHash(rep
->existingHash());
680 #if DUMP_PROPERTYMAP_STATS
684 entryIndex
= m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
];
685 if (entryIndex
== emptyEntryIndex
)
688 if (rep
== m_propertyTable
->entries()[entryIndex
- 1].key
) {
689 attributes
= m_propertyTable
->entries()[entryIndex
- 1].attributes
;
690 specificValue
= m_propertyTable
->entries()[entryIndex
- 1].specificValue
;
691 ASSERT(m_propertyTable
->entries()[entryIndex
- 1].offset
>= m_anonymousSlotCount
);
692 return m_propertyTable
->entries()[entryIndex
- 1].offset
;
697 bool Structure::despecifyFunction(const Identifier
& propertyName
)
699 ASSERT(!propertyName
.isNull());
701 materializePropertyMapIfNecessary();
702 if (!m_propertyTable
)
705 UString::Rep
* rep
= propertyName
._ustring
.rep();
707 unsigned i
= rep
->existingHash();
709 #if DUMP_PROPERTYMAP_STATS
713 unsigned entryIndex
= m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
];
714 if (entryIndex
== emptyEntryIndex
)
717 if (rep
== m_propertyTable
->entries()[entryIndex
- 1].key
) {
718 ASSERT(m_propertyTable
->entries()[entryIndex
- 1].specificValue
);
719 m_propertyTable
->entries()[entryIndex
- 1].specificValue
= 0;
723 #if DUMP_PROPERTYMAP_STATS
727 unsigned k
= 1 | doubleHash(rep
->existingHash());
732 #if DUMP_PROPERTYMAP_STATS
736 entryIndex
= m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
];
737 if (entryIndex
== emptyEntryIndex
)
740 if (rep
== m_propertyTable
->entries()[entryIndex
- 1].key
) {
741 ASSERT(m_propertyTable
->entries()[entryIndex
- 1].specificValue
);
742 m_propertyTable
->entries()[entryIndex
- 1].specificValue
= 0;
748 void Structure::despecifyAllFunctions()
750 materializePropertyMapIfNecessary();
751 if (!m_propertyTable
)
754 unsigned entryCount
= m_propertyTable
->keyCount
+ m_propertyTable
->deletedSentinelCount
;
755 for (unsigned i
= 1; i
<= entryCount
; ++i
)
756 m_propertyTable
->entries()[i
].specificValue
= 0;
759 size_t Structure::put(const Identifier
& propertyName
, unsigned attributes
, JSCell
* specificValue
)
761 ASSERT(!propertyName
.isNull());
762 ASSERT(get(propertyName
) == notFound
);
766 if (attributes
& DontEnum
)
767 m_hasNonEnumerableProperties
= true;
769 UString::Rep
* rep
= propertyName
._ustring
.rep();
771 if (!m_propertyTable
)
772 createPropertyMapHashTable();
774 // FIXME: Consider a fast case for tables with no deleted sentinels.
776 unsigned i
= rep
->existingHash();
778 bool foundDeletedElement
= false;
779 unsigned deletedElementIndex
= 0; // initialize to make the compiler happy
781 #if DUMP_PROPERTYMAP_STATS
786 unsigned entryIndex
= m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
];
787 if (entryIndex
== emptyEntryIndex
)
790 if (entryIndex
== deletedSentinelIndex
) {
791 // If we find a deleted-element sentinel, remember it for use later.
792 if (!foundDeletedElement
) {
793 foundDeletedElement
= true;
794 deletedElementIndex
= i
;
799 k
= 1 | doubleHash(rep
->existingHash());
800 #if DUMP_PROPERTYMAP_STATS
807 #if DUMP_PROPERTYMAP_STATS
812 // Figure out which entry to use.
813 unsigned entryIndex
= m_propertyTable
->keyCount
+ m_propertyTable
->deletedSentinelCount
+ 2;
814 if (foundDeletedElement
) {
815 i
= deletedElementIndex
;
816 --m_propertyTable
->deletedSentinelCount
;
818 // Since we're not making the table bigger, we can't use the entry one past
819 // the end that we were planning on using, so search backwards for the empty
820 // slot that we can use. We know it will be there because we did at least one
821 // deletion in the past that left an entry empty.
822 while (m_propertyTable
->entries()[--entryIndex
- 1].key
) { }
825 // Create a new hash table entry.
826 m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
] = entryIndex
;
828 // Create a new hash table entry.
830 m_propertyTable
->entries()[entryIndex
- 1].key
= rep
;
831 m_propertyTable
->entries()[entryIndex
- 1].attributes
= attributes
;
832 m_propertyTable
->entries()[entryIndex
- 1].specificValue
= specificValue
;
833 m_propertyTable
->entries()[entryIndex
- 1].index
= ++m_propertyTable
->lastIndexUsed
;
836 if (m_propertyTable
->deletedOffsets
&& !m_propertyTable
->deletedOffsets
->isEmpty()) {
837 newOffset
= m_propertyTable
->deletedOffsets
->last();
838 m_propertyTable
->deletedOffsets
->removeLast();
840 newOffset
= m_propertyTable
->keyCount
+ m_anonymousSlotCount
;
841 m_propertyTable
->entries()[entryIndex
- 1].offset
= newOffset
;
843 ASSERT(newOffset
>= m_anonymousSlotCount
);
844 ++m_propertyTable
->keyCount
;
846 if ((m_propertyTable
->keyCount
+ m_propertyTable
->deletedSentinelCount
) * 2 >= m_propertyTable
->size
)
847 expandPropertyMapHashTable();
853 bool Structure::hasTransition(UString::Rep
* rep
, unsigned attributes
)
855 return table
.hasTransition(make_pair(rep
, attributes
));
858 size_t Structure::remove(const Identifier
& propertyName
)
860 ASSERT(!propertyName
.isNull());
864 UString::Rep
* rep
= propertyName
._ustring
.rep();
866 if (!m_propertyTable
)
869 #if DUMP_PROPERTYMAP_STATS
874 // Find the thing to remove.
875 unsigned i
= rep
->existingHash();
878 UString::Rep
* key
= 0;
880 entryIndex
= m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
];
881 if (entryIndex
== emptyEntryIndex
)
884 key
= m_propertyTable
->entries()[entryIndex
- 1].key
;
889 k
= 1 | doubleHash(rep
->existingHash());
890 #if DUMP_PROPERTYMAP_STATS
897 #if DUMP_PROPERTYMAP_STATS
902 // Replace this one element with the deleted sentinel. Also clear out
903 // the entry so we can iterate all the entries as needed.
904 m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
] = deletedSentinelIndex
;
906 size_t offset
= m_propertyTable
->entries()[entryIndex
- 1].offset
;
907 ASSERT(offset
>= m_anonymousSlotCount
);
910 m_propertyTable
->entries()[entryIndex
- 1].key
= 0;
911 m_propertyTable
->entries()[entryIndex
- 1].attributes
= 0;
912 m_propertyTable
->entries()[entryIndex
- 1].specificValue
= 0;
913 m_propertyTable
->entries()[entryIndex
- 1].offset
= 0;
915 if (!m_propertyTable
->deletedOffsets
)
916 m_propertyTable
->deletedOffsets
= new Vector
<unsigned>;
917 m_propertyTable
->deletedOffsets
->append(offset
);
919 ASSERT(m_propertyTable
->keyCount
>= 1);
920 --m_propertyTable
->keyCount
;
921 ++m_propertyTable
->deletedSentinelCount
;
923 if (m_propertyTable
->deletedSentinelCount
* 4 >= m_propertyTable
->size
)
924 rehashPropertyMapHashTable();
930 void Structure::insertIntoPropertyMapHashTable(const PropertyMapEntry
& entry
)
932 ASSERT(m_propertyTable
);
933 ASSERT(entry
.offset
>= m_anonymousSlotCount
);
934 unsigned i
= entry
.key
->existingHash();
937 #if DUMP_PROPERTYMAP_STATS
942 unsigned entryIndex
= m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
];
943 if (entryIndex
== emptyEntryIndex
)
947 k
= 1 | doubleHash(entry
.key
->existingHash());
948 #if DUMP_PROPERTYMAP_STATS
955 #if DUMP_PROPERTYMAP_STATS
960 unsigned entryIndex
= m_propertyTable
->keyCount
+ 2;
961 m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
] = entryIndex
;
962 m_propertyTable
->entries()[entryIndex
- 1] = entry
;
964 ++m_propertyTable
->keyCount
;
967 void Structure::createPropertyMapHashTable()
969 ASSERT(sizeForKeyCount(7) == newTableSize
);
970 createPropertyMapHashTable(newTableSize
);
973 void Structure::createPropertyMapHashTable(unsigned newTableSize
)
975 ASSERT(!m_propertyTable
);
976 ASSERT(isPowerOf2(newTableSize
));
980 m_propertyTable
= static_cast<PropertyMapHashTable
*>(fastZeroedMalloc(PropertyMapHashTable::allocationSize(newTableSize
)));
981 m_propertyTable
->size
= newTableSize
;
982 m_propertyTable
->sizeMask
= newTableSize
- 1;
987 void Structure::expandPropertyMapHashTable()
989 ASSERT(m_propertyTable
);
990 rehashPropertyMapHashTable(m_propertyTable
->size
* 2);
993 void Structure::rehashPropertyMapHashTable()
995 ASSERT(m_propertyTable
);
996 ASSERT(m_propertyTable
->size
);
997 rehashPropertyMapHashTable(m_propertyTable
->size
);
1000 void Structure::rehashPropertyMapHashTable(unsigned newTableSize
)
1002 ASSERT(m_propertyTable
);
1003 ASSERT(isPowerOf2(newTableSize
));
1007 PropertyMapHashTable
* oldTable
= m_propertyTable
;
1009 m_propertyTable
= static_cast<PropertyMapHashTable
*>(fastZeroedMalloc(PropertyMapHashTable::allocationSize(newTableSize
)));
1010 m_propertyTable
->size
= newTableSize
;
1011 m_propertyTable
->sizeMask
= newTableSize
- 1;
1013 unsigned lastIndexUsed
= 0;
1014 unsigned entryCount
= oldTable
->keyCount
+ oldTable
->deletedSentinelCount
;
1015 for (unsigned i
= 1; i
<= entryCount
; ++i
) {
1016 if (oldTable
->entries()[i
].key
) {
1017 lastIndexUsed
= max(oldTable
->entries()[i
].index
, lastIndexUsed
);
1018 insertIntoPropertyMapHashTable(oldTable
->entries()[i
]);
1021 m_propertyTable
->lastIndexUsed
= lastIndexUsed
;
1022 m_propertyTable
->deletedOffsets
= oldTable
->deletedOffsets
;
1029 int comparePropertyMapEntryIndices(const void* a
, const void* b
)
1031 unsigned ia
= static_cast<PropertyMapEntry
* const*>(a
)[0]->index
;
1032 unsigned ib
= static_cast<PropertyMapEntry
* const*>(b
)[0]->index
;
1040 void Structure::getPropertyNames(PropertyNameArray
& propertyNames
, EnumerationMode mode
)
1042 materializePropertyMapIfNecessary();
1043 if (!m_propertyTable
)
1046 if (m_propertyTable
->keyCount
< tinyMapThreshold
) {
1047 PropertyMapEntry
* a
[tinyMapThreshold
];
1049 unsigned entryCount
= m_propertyTable
->keyCount
+ m_propertyTable
->deletedSentinelCount
;
1050 for (unsigned k
= 1; k
<= entryCount
; k
++) {
1051 ASSERT(m_hasNonEnumerableProperties
|| !(m_propertyTable
->entries()[k
].attributes
& DontEnum
));
1052 if (m_propertyTable
->entries()[k
].key
&& (!(m_propertyTable
->entries()[k
].attributes
& DontEnum
) || (mode
== IncludeDontEnumProperties
))) {
1053 PropertyMapEntry
* value
= &m_propertyTable
->entries()[k
];
1055 for (j
= i
- 1; j
>= 0 && a
[j
]->index
> value
->index
; --j
)
1061 if (!propertyNames
.size()) {
1062 for (int k
= 0; k
< i
; ++k
)
1063 propertyNames
.addKnownUnique(a
[k
]->key
);
1065 for (int k
= 0; k
< i
; ++k
)
1066 propertyNames
.add(a
[k
]->key
);
1072 // Allocate a buffer to use to sort the keys.
1073 Vector
<PropertyMapEntry
*, smallMapThreshold
> sortedEnumerables(m_propertyTable
->keyCount
);
1075 // Get pointers to the enumerable entries in the buffer.
1076 PropertyMapEntry
** p
= sortedEnumerables
.data();
1077 unsigned entryCount
= m_propertyTable
->keyCount
+ m_propertyTable
->deletedSentinelCount
;
1078 for (unsigned i
= 1; i
<= entryCount
; i
++) {
1079 if (m_propertyTable
->entries()[i
].key
&& (!(m_propertyTable
->entries()[i
].attributes
& DontEnum
) || (mode
== IncludeDontEnumProperties
)))
1080 *p
++ = &m_propertyTable
->entries()[i
];
1083 size_t enumerableCount
= p
- sortedEnumerables
.data();
1084 // Sort the entries by index.
1085 qsort(sortedEnumerables
.data(), enumerableCount
, sizeof(PropertyMapEntry
*), comparePropertyMapEntryIndices
);
1086 sortedEnumerables
.resize(enumerableCount
);
1088 // Put the keys of the sorted entries into the list.
1089 if (!propertyNames
.size()) {
1090 for (size_t i
= 0; i
< sortedEnumerables
.size(); ++i
)
1091 propertyNames
.addKnownUnique(sortedEnumerables
[i
]->key
);
1093 for (size_t i
= 0; i
< sortedEnumerables
.size(); ++i
)
1094 propertyNames
.add(sortedEnumerables
[i
]->key
);
1098 #if DO_PROPERTYMAP_CONSTENCY_CHECK
1100 void Structure::checkConsistency()
1102 if (!m_propertyTable
)
1105 ASSERT(m_propertyTable
->size
>= newTableSize
);
1106 ASSERT(m_propertyTable
->sizeMask
);
1107 ASSERT(m_propertyTable
->size
== m_propertyTable
->sizeMask
+ 1);
1108 ASSERT(!(m_propertyTable
->size
& m_propertyTable
->sizeMask
));
1110 ASSERT(m_propertyTable
->keyCount
<= m_propertyTable
->size
/ 2);
1111 ASSERT(m_propertyTable
->deletedSentinelCount
<= m_propertyTable
->size
/ 4);
1113 ASSERT(m_propertyTable
->keyCount
+ m_propertyTable
->deletedSentinelCount
<= m_propertyTable
->size
/ 2);
1115 unsigned indexCount
= 0;
1116 unsigned deletedIndexCount
= 0;
1117 for (unsigned a
= 0; a
!= m_propertyTable
->size
; ++a
) {
1118 unsigned entryIndex
= m_propertyTable
->entryIndices
[a
];
1119 if (entryIndex
== emptyEntryIndex
)
1121 if (entryIndex
== deletedSentinelIndex
) {
1122 ++deletedIndexCount
;
1125 ASSERT(entryIndex
> deletedSentinelIndex
);
1126 ASSERT(entryIndex
- 1 <= m_propertyTable
->keyCount
+ m_propertyTable
->deletedSentinelCount
);
1129 for (unsigned b
= a
+ 1; b
!= m_propertyTable
->size
; ++b
)
1130 ASSERT(m_propertyTable
->entryIndices
[b
] != entryIndex
);
1132 ASSERT(indexCount
== m_propertyTable
->keyCount
);
1133 ASSERT(deletedIndexCount
== m_propertyTable
->deletedSentinelCount
);
1135 ASSERT(m_propertyTable
->entries()[0].key
== 0);
1137 unsigned nonEmptyEntryCount
= 0;
1138 for (unsigned c
= 1; c
<= m_propertyTable
->keyCount
+ m_propertyTable
->deletedSentinelCount
; ++c
) {
1139 ASSERT(m_hasNonEnumerableProperties
|| !(m_propertyTable
->entries()[c
].attributes
& DontEnum
));
1140 UString::Rep
* rep
= m_propertyTable
->entries()[c
].key
;
1141 ASSERT(m_propertyTable
->entries()[c
].offset
>= m_anonymousSlotCount
);
1144 ++nonEmptyEntryCount
;
1145 unsigned i
= rep
->existingHash();
1147 unsigned entryIndex
;
1149 entryIndex
= m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
];
1150 ASSERT(entryIndex
!= emptyEntryIndex
);
1151 if (rep
== m_propertyTable
->entries()[entryIndex
- 1].key
)
1154 k
= 1 | doubleHash(rep
->existingHash());
1157 ASSERT(entryIndex
== c
+ 1);
1160 ASSERT(nonEmptyEntryCount
== m_propertyTable
->keyCount
);
1163 #endif // DO_PROPERTYMAP_CONSTENCY_CHECK