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>
37 #include <wtf/Threading.h>
39 #define DUMP_STRUCTURE_ID_STATISTICS 0
42 #define DO_PROPERTYMAP_CONSTENCY_CHECK 0
44 #define DO_PROPERTYMAP_CONSTENCY_CHECK 0
50 #if DUMP_PROPERTYMAP_STATS
61 #if DUMP_STRUCTURE_ID_STATISTICS
62 static HashSet
<Structure
*>& liveStructureSet
= *(new HashSet
<Structure
*>);
65 bool StructureTransitionTable::contains(StringImpl
* rep
, unsigned attributes
) const
67 if (isUsingSingleSlot()) {
68 Structure
* transition
= singleTransition();
69 return transition
&& transition
->m_nameInPrevious
== rep
&& transition
->m_attributesInPrevious
== attributes
;
71 return map()->contains(make_pair(rep
, attributes
));
74 inline Structure
* StructureTransitionTable::get(StringImpl
* rep
, unsigned attributes
) const
76 if (isUsingSingleSlot()) {
77 Structure
* transition
= singleTransition();
78 return (transition
&& transition
->m_nameInPrevious
== rep
&& transition
->m_attributesInPrevious
== attributes
) ? transition
: 0;
80 return map()->get(make_pair(rep
, attributes
));
83 inline void StructureTransitionTable::add(JSGlobalData
& globalData
, Structure
* structure
)
85 if (isUsingSingleSlot()) {
86 Structure
* existingTransition
= singleTransition();
88 // This handles the first transition being added.
89 if (!existingTransition
) {
90 setSingleTransition(globalData
, structure
);
94 // This handles the second transition being added
95 // (or the first transition being despecified!)
96 setMap(new TransitionMap());
97 add(globalData
, existingTransition
);
100 // Add the structure to the map.
102 // Newer versions of the STL have an std::make_pair function that takes rvalue references.
103 // When either of the parameters are bitfields, the C++ compiler will try to bind them as lvalues, which is invalid. To work around this, use unary "+" to make the parameter an rvalue.
104 // See https://bugs.webkit.org/show_bug.cgi?id=59261 for more details
105 TransitionMap::AddResult result
= map()->add(globalData
, make_pair(structure
->m_nameInPrevious
, +structure
->m_attributesInPrevious
), structure
);
106 if (!result
.isNewEntry
) {
107 // There already is an entry! - we should only hit this when despecifying.
108 ASSERT(result
.iterator
.get().second
->m_specificValueInPrevious
);
109 ASSERT(!structure
->m_specificValueInPrevious
);
110 map()->set(globalData
, result
.iterator
.get().first
, structure
);
114 void Structure::dumpStatistics()
116 #if DUMP_STRUCTURE_ID_STATISTICS
117 unsigned numberLeaf
= 0;
118 unsigned numberUsingSingleSlot
= 0;
119 unsigned numberSingletons
= 0;
120 unsigned numberWithPropertyMaps
= 0;
121 unsigned totalPropertyMapsSize
= 0;
123 HashSet
<Structure
*>::const_iterator end
= liveStructureSet
.end();
124 for (HashSet
<Structure
*>::const_iterator it
= liveStructureSet
.begin(); it
!= end
; ++it
) {
125 Structure
* structure
= *it
;
127 switch (structure
->m_transitionTable
.size()) {
130 if (!structure
->m_previous
)
135 ++numberUsingSingleSlot
;
139 if (structure
->m_propertyTable
) {
140 ++numberWithPropertyMaps
;
141 totalPropertyMapsSize
+= structure
->m_propertyTable
->sizeInMemory();
145 dataLog("Number of live Structures: %d\n", liveStructureSet
.size());
146 dataLog("Number of Structures using the single item optimization for transition map: %d\n", numberUsingSingleSlot
);
147 dataLog("Number of Structures that are leaf nodes: %d\n", numberLeaf
);
148 dataLog("Number of Structures that singletons: %d\n", numberSingletons
);
149 dataLog("Number of Structures with PropertyMaps: %d\n", numberWithPropertyMaps
);
151 dataLog("Size of a single Structures: %d\n", static_cast<unsigned>(sizeof(Structure
)));
152 dataLog("Size of sum of all property maps: %d\n", totalPropertyMapsSize
);
153 dataLog("Size of average of all property maps: %f\n", static_cast<double>(totalPropertyMapsSize
) / static_cast<double>(liveStructureSet
.size()));
155 dataLog("Dumping Structure statistics is not enabled.\n");
159 Structure::Structure(JSGlobalData
& globalData
, JSGlobalObject
* globalObject
, JSValue prototype
, const TypeInfo
& typeInfo
, const ClassInfo
* classInfo
)
160 : JSCell(globalData
, globalData
.structureStructure
.get())
161 , m_typeInfo(typeInfo
)
162 , m_globalObject(globalData
, this, globalObject
, WriteBarrier
<JSGlobalObject
>::MayBeNull
)
163 , m_prototype(globalData
, this, prototype
)
164 , m_classInfo(classInfo
)
165 , m_propertyStorageCapacity(typeInfo
.isFinalObject() ? JSFinalObject_inlineStorageCapacity
: JSNonFinalObject_inlineStorageCapacity
)
167 , m_dictionaryKind(NoneDictionaryKind
)
168 , m_isPinnedPropertyTable(false)
169 , m_hasGetterSetterProperties(false)
170 , m_hasReadOnlyOrGetterSetterPropertiesExcludingProto(false)
171 , m_hasNonEnumerableProperties(false)
172 , m_attributesInPrevious(0)
173 , m_specificFunctionThrashCount(0)
174 , m_preventExtensions(false)
175 , m_didTransition(false)
176 , m_staticFunctionReified(false)
180 const ClassInfo
Structure::s_info
= { "Structure", 0, 0, 0, CREATE_METHOD_TABLE(Structure
) };
182 Structure::Structure(JSGlobalData
& globalData
)
183 : JSCell(CreatingEarlyCell
)
184 , m_typeInfo(CompoundType
, OverridesVisitChildren
)
185 , m_prototype(globalData
, this, jsNull())
186 , m_classInfo(&s_info
)
187 , m_propertyStorageCapacity(0)
189 , m_dictionaryKind(NoneDictionaryKind
)
190 , m_isPinnedPropertyTable(false)
191 , m_hasGetterSetterProperties(false)
192 , m_hasReadOnlyOrGetterSetterPropertiesExcludingProto(false)
193 , m_hasNonEnumerableProperties(false)
194 , m_attributesInPrevious(0)
195 , m_specificFunctionThrashCount(0)
196 , m_preventExtensions(false)
197 , m_didTransition(false)
198 , m_staticFunctionReified(false)
202 Structure::Structure(JSGlobalData
& globalData
, const Structure
* previous
)
203 : JSCell(globalData
, globalData
.structureStructure
.get())
204 , m_typeInfo(previous
->typeInfo())
205 , m_prototype(globalData
, this, previous
->storedPrototype())
206 , m_classInfo(previous
->m_classInfo
)
207 , m_propertyStorageCapacity(previous
->m_propertyStorageCapacity
)
209 , m_dictionaryKind(previous
->m_dictionaryKind
)
210 , m_isPinnedPropertyTable(false)
211 , m_hasGetterSetterProperties(previous
->m_hasGetterSetterProperties
)
212 , m_hasReadOnlyOrGetterSetterPropertiesExcludingProto(previous
->m_hasReadOnlyOrGetterSetterPropertiesExcludingProto
)
213 , m_hasNonEnumerableProperties(previous
->m_hasNonEnumerableProperties
)
214 , m_attributesInPrevious(0)
215 , m_specificFunctionThrashCount(previous
->m_specificFunctionThrashCount
)
216 , m_preventExtensions(previous
->m_preventExtensions
)
217 , m_didTransition(true)
218 , m_staticFunctionReified(previous
->m_staticFunctionReified
)
220 if (previous
->m_globalObject
)
221 m_globalObject
.set(globalData
, this, previous
->m_globalObject
.get());
224 void Structure::destroy(JSCell
* cell
)
226 jsCast
<Structure
*>(cell
)->Structure::~Structure();
229 void Structure::materializePropertyMap(JSGlobalData
& globalData
)
231 ASSERT(structure()->classInfo() == &s_info
);
232 ASSERT(!m_propertyTable
);
234 Vector
<Structure
*, 8> structures
;
235 structures
.append(this);
237 Structure
* structure
= this;
239 // Search for the last Structure with a property table.
240 while ((structure
= structure
->previousID())) {
241 if (structure
->m_isPinnedPropertyTable
) {
242 ASSERT(structure
->m_propertyTable
);
243 ASSERT(!structure
->m_previous
);
245 m_propertyTable
= structure
->m_propertyTable
->copy(globalData
, 0, m_offset
+ 1);
249 structures
.append(structure
);
252 if (!m_propertyTable
)
253 createPropertyMap(m_offset
+ 1);
255 for (ptrdiff_t i
= structures
.size() - 2; i
>= 0; --i
) {
256 structure
= structures
[i
];
257 PropertyMapEntry
entry(globalData
, this, structure
->m_nameInPrevious
.get(), structure
->m_offset
, structure
->m_attributesInPrevious
, structure
->m_specificValueInPrevious
.get());
258 m_propertyTable
->add(entry
);
262 void Structure::growPropertyStorageCapacity()
264 if (isUsingInlineStorage())
265 m_propertyStorageCapacity
= JSObject::baseExternalStorageCapacity
;
267 m_propertyStorageCapacity
*= 2;
270 size_t Structure::suggestedNewPropertyStorageSize()
272 if (isUsingInlineStorage())
273 return JSObject::baseExternalStorageCapacity
;
274 return m_propertyStorageCapacity
* 2;
277 void Structure::despecifyDictionaryFunction(JSGlobalData
& globalData
, const Identifier
& propertyName
)
279 StringImpl
* rep
= propertyName
.impl();
281 materializePropertyMapIfNecessary(globalData
);
283 ASSERT(isDictionary());
284 ASSERT(m_propertyTable
);
286 PropertyMapEntry
* entry
= m_propertyTable
->find(rep
).first
;
288 entry
->specificValue
.clear();
291 Structure
* Structure::addPropertyTransitionToExistingStructure(Structure
* structure
, const Identifier
& propertyName
, unsigned attributes
, JSCell
* specificValue
, size_t& offset
)
293 ASSERT(!structure
->isDictionary());
294 ASSERT(structure
->isObject());
296 if (Structure
* existingTransition
= structure
->m_transitionTable
.get(propertyName
.impl(), attributes
)) {
297 JSCell
* specificValueInPrevious
= existingTransition
->m_specificValueInPrevious
.get();
298 if (specificValueInPrevious
&& specificValueInPrevious
!= specificValue
)
300 ASSERT(existingTransition
->m_offset
!= noOffset
);
301 offset
= existingTransition
->m_offset
;
302 return existingTransition
;
308 Structure
* Structure::addPropertyTransition(JSGlobalData
& globalData
, Structure
* structure
, const Identifier
& propertyName
, unsigned attributes
, JSCell
* specificValue
, size_t& offset
)
310 // If we have a specific function, we may have got to this point if there is
311 // already a transition with the correct property name and attributes, but
312 // specialized to a different function. In this case we just want to give up
313 // and despecialize the transition.
314 // In this case we clear the value of specificFunction which will result
315 // in us adding a non-specific transition, and any subsequent lookup in
316 // Structure::addPropertyTransitionToExistingStructure will just use that.
317 if (specificValue
&& structure
->m_transitionTable
.contains(propertyName
.impl(), attributes
))
320 ASSERT(!structure
->isDictionary());
321 ASSERT(structure
->isObject());
322 ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure
, propertyName
, attributes
, specificValue
, offset
));
324 if (structure
->m_specificFunctionThrashCount
== maxSpecificFunctionThrashCount
)
327 if (structure
->transitionCount() > s_maxTransitionLength
) {
328 Structure
* transition
= toCacheableDictionaryTransition(globalData
, structure
);
329 ASSERT(structure
!= transition
);
330 offset
= transition
->putSpecificValue(globalData
, propertyName
, attributes
, specificValue
);
331 if (transition
->propertyStorageSize() > transition
->propertyStorageCapacity())
332 transition
->growPropertyStorageCapacity();
336 Structure
* transition
= create(globalData
, structure
);
338 transition
->m_cachedPrototypeChain
.setMayBeNull(globalData
, transition
, structure
->m_cachedPrototypeChain
.get());
339 transition
->m_previous
.set(globalData
, transition
, structure
);
340 transition
->m_nameInPrevious
= propertyName
.impl();
341 transition
->m_attributesInPrevious
= attributes
;
342 transition
->m_specificValueInPrevious
.setMayBeNull(globalData
, transition
, specificValue
);
344 if (structure
->m_propertyTable
) {
345 if (structure
->m_isPinnedPropertyTable
)
346 transition
->m_propertyTable
= structure
->m_propertyTable
->copy(globalData
, transition
, structure
->m_propertyTable
->size() + 1);
348 transition
->m_propertyTable
= structure
->m_propertyTable
.release();
350 if (structure
->m_previous
)
351 transition
->materializePropertyMap(globalData
);
353 transition
->createPropertyMap();
356 offset
= transition
->putSpecificValue(globalData
, propertyName
, attributes
, specificValue
);
357 if (transition
->propertyStorageSize() > transition
->propertyStorageCapacity())
358 transition
->growPropertyStorageCapacity();
360 transition
->m_offset
= offset
;
361 structure
->m_transitionTable
.add(globalData
, transition
);
365 Structure
* Structure::removePropertyTransition(JSGlobalData
& globalData
, Structure
* structure
, const Identifier
& propertyName
, size_t& offset
)
367 ASSERT(!structure
->isUncacheableDictionary());
369 Structure
* transition
= toUncacheableDictionaryTransition(globalData
, structure
);
371 offset
= transition
->remove(propertyName
);
376 Structure
* Structure::changePrototypeTransition(JSGlobalData
& globalData
, Structure
* structure
, JSValue prototype
)
378 Structure
* transition
= create(globalData
, structure
);
380 transition
->m_prototype
.set(globalData
, transition
, prototype
);
382 // Don't set m_offset, as one can not transition to this.
384 structure
->materializePropertyMapIfNecessary(globalData
);
385 transition
->m_propertyTable
= structure
->copyPropertyTableForPinning(globalData
, transition
);
391 Structure
* Structure::despecifyFunctionTransition(JSGlobalData
& globalData
, Structure
* structure
, const Identifier
& replaceFunction
)
393 ASSERT(structure
->m_specificFunctionThrashCount
< maxSpecificFunctionThrashCount
);
394 Structure
* transition
= create(globalData
, structure
);
396 ++transition
->m_specificFunctionThrashCount
;
398 // Don't set m_offset, as one can not transition to this.
400 structure
->materializePropertyMapIfNecessary(globalData
);
401 transition
->m_propertyTable
= structure
->copyPropertyTableForPinning(globalData
, transition
);
404 if (transition
->m_specificFunctionThrashCount
== maxSpecificFunctionThrashCount
)
405 transition
->despecifyAllFunctions(globalData
);
407 bool removed
= transition
->despecifyFunction(globalData
, replaceFunction
);
408 ASSERT_UNUSED(removed
, removed
);
414 Structure
* Structure::attributeChangeTransition(JSGlobalData
& globalData
, Structure
* structure
, const Identifier
& propertyName
, unsigned attributes
)
416 if (!structure
->isUncacheableDictionary()) {
417 Structure
* transition
= create(globalData
, structure
);
419 // Don't set m_offset, as one can not transition to this.
421 structure
->materializePropertyMapIfNecessary(globalData
);
422 transition
->m_propertyTable
= structure
->copyPropertyTableForPinning(globalData
, transition
);
425 structure
= transition
;
428 ASSERT(structure
->m_propertyTable
);
429 PropertyMapEntry
* entry
= structure
->m_propertyTable
->find(propertyName
.impl()).first
;
431 entry
->attributes
= attributes
;
436 Structure
* Structure::toDictionaryTransition(JSGlobalData
& globalData
, Structure
* structure
, DictionaryKind kind
)
438 ASSERT(!structure
->isUncacheableDictionary());
440 Structure
* transition
= create(globalData
, structure
);
442 structure
->materializePropertyMapIfNecessary(globalData
);
443 transition
->m_propertyTable
= structure
->copyPropertyTableForPinning(globalData
, transition
);
444 transition
->m_dictionaryKind
= kind
;
450 Structure
* Structure::toCacheableDictionaryTransition(JSGlobalData
& globalData
, Structure
* structure
)
452 return toDictionaryTransition(globalData
, structure
, CachedDictionaryKind
);
455 Structure
* Structure::toUncacheableDictionaryTransition(JSGlobalData
& globalData
, Structure
* structure
)
457 return toDictionaryTransition(globalData
, structure
, UncachedDictionaryKind
);
460 // In future we may want to cache this transition.
461 Structure
* Structure::sealTransition(JSGlobalData
& globalData
, Structure
* structure
)
463 Structure
* transition
= preventExtensionsTransition(globalData
, structure
);
465 if (transition
->m_propertyTable
) {
466 PropertyTable::iterator end
= transition
->m_propertyTable
->end();
467 for (PropertyTable::iterator iter
= transition
->m_propertyTable
->begin(); iter
!= end
; ++iter
)
468 iter
->attributes
|= DontDelete
;
474 // In future we may want to cache this transition.
475 Structure
* Structure::freezeTransition(JSGlobalData
& globalData
, Structure
* structure
)
477 Structure
* transition
= preventExtensionsTransition(globalData
, structure
);
479 if (transition
->m_propertyTable
) {
480 PropertyTable::iterator iter
= transition
->m_propertyTable
->begin();
481 PropertyTable::iterator end
= transition
->m_propertyTable
->end();
483 transition
->m_hasReadOnlyOrGetterSetterPropertiesExcludingProto
= true;
484 for (; iter
!= end
; ++iter
)
485 iter
->attributes
|= iter
->attributes
& Accessor
? DontDelete
: (DontDelete
| ReadOnly
);
491 // In future we may want to cache this transition.
492 Structure
* Structure::preventExtensionsTransition(JSGlobalData
& globalData
, Structure
* structure
)
494 Structure
* transition
= create(globalData
, structure
);
496 // Don't set m_offset, as one can not transition to this.
498 structure
->materializePropertyMapIfNecessary(globalData
);
499 transition
->m_propertyTable
= structure
->copyPropertyTableForPinning(globalData
, transition
);
500 transition
->m_preventExtensions
= true;
506 // In future we may want to cache this property.
507 bool Structure::isSealed(JSGlobalData
& globalData
)
512 materializePropertyMapIfNecessary(globalData
);
513 if (!m_propertyTable
)
516 PropertyTable::iterator end
= m_propertyTable
->end();
517 for (PropertyTable::iterator iter
= m_propertyTable
->begin(); iter
!= end
; ++iter
) {
518 if ((iter
->attributes
& DontDelete
) != DontDelete
)
524 // In future we may want to cache this property.
525 bool Structure::isFrozen(JSGlobalData
& globalData
)
530 materializePropertyMapIfNecessary(globalData
);
531 if (!m_propertyTable
)
534 PropertyTable::iterator end
= m_propertyTable
->end();
535 for (PropertyTable::iterator iter
= m_propertyTable
->begin(); iter
!= end
; ++iter
) {
536 if (!(iter
->attributes
& DontDelete
))
538 if (!(iter
->attributes
& (ReadOnly
| Accessor
)))
544 Structure
* Structure::flattenDictionaryStructure(JSGlobalData
& globalData
, JSObject
* object
)
546 ASSERT(isDictionary());
547 if (isUncacheableDictionary()) {
548 ASSERT(m_propertyTable
);
550 size_t propertyCount
= m_propertyTable
->size();
551 Vector
<JSValue
> values(propertyCount
);
554 PropertyTable::iterator end
= m_propertyTable
->end();
555 for (PropertyTable::iterator iter
= m_propertyTable
->begin(); iter
!= end
; ++iter
, ++i
) {
556 values
[i
] = object
->getDirectOffset(iter
->offset
);
557 // Update property table to have the new property offsets
561 // Copy the original property values into their final locations
562 for (unsigned i
= 0; i
< propertyCount
; i
++)
563 object
->putDirectOffset(globalData
, i
, values
[i
]);
565 m_propertyTable
->clearDeletedOffsets();
568 m_dictionaryKind
= NoneDictionaryKind
;
572 size_t Structure::addPropertyWithoutTransition(JSGlobalData
& globalData
, const Identifier
& propertyName
, unsigned attributes
, JSCell
* specificValue
)
574 ASSERT(!m_enumerationCache
);
576 if (m_specificFunctionThrashCount
== maxSpecificFunctionThrashCount
)
579 materializePropertyMapIfNecessaryForPinning(globalData
);
583 size_t offset
= putSpecificValue(globalData
, propertyName
, attributes
, specificValue
);
584 if (propertyStorageSize() > propertyStorageCapacity())
585 growPropertyStorageCapacity();
589 size_t Structure::removePropertyWithoutTransition(JSGlobalData
& globalData
, const Identifier
& propertyName
)
591 ASSERT(isUncacheableDictionary());
592 ASSERT(!m_enumerationCache
);
594 materializePropertyMapIfNecessaryForPinning(globalData
);
597 size_t offset
= remove(propertyName
);
601 void Structure::pin()
603 ASSERT(m_propertyTable
);
604 m_isPinnedPropertyTable
= true;
606 m_nameInPrevious
.clear();
609 #if DUMP_PROPERTYMAP_STATS
611 struct PropertyMapStatisticsExitLogger
{
612 ~PropertyMapStatisticsExitLogger();
615 static PropertyMapStatisticsExitLogger logger
;
617 PropertyMapStatisticsExitLogger::~PropertyMapStatisticsExitLogger()
619 dataLog("\nJSC::PropertyMap statistics\n\n");
620 dataLog("%d probes\n", numProbes
);
621 dataLog("%d collisions (%.1f%%)\n", numCollisions
, 100.0 * numCollisions
/ numProbes
);
622 dataLog("%d rehashes\n", numRehashes
);
623 dataLog("%d removes\n", numRemoves
);
628 #if !DO_PROPERTYMAP_CONSTENCY_CHECK
630 inline void Structure::checkConsistency()
636 PassOwnPtr
<PropertyTable
> Structure::copyPropertyTable(JSGlobalData
& globalData
, Structure
* owner
)
638 return adoptPtr(m_propertyTable
? new PropertyTable(globalData
, owner
, *m_propertyTable
) : 0);
641 PassOwnPtr
<PropertyTable
> Structure::copyPropertyTableForPinning(JSGlobalData
& globalData
, Structure
* owner
)
643 return adoptPtr(m_propertyTable
? new PropertyTable(globalData
, owner
, *m_propertyTable
) : new PropertyTable(m_offset
== noOffset
? 0 : m_offset
));
646 size_t Structure::get(JSGlobalData
& globalData
, StringImpl
* propertyName
, unsigned& attributes
, JSCell
*& specificValue
)
648 materializePropertyMapIfNecessary(globalData
);
649 if (!m_propertyTable
)
650 return WTF::notFound
;
652 PropertyMapEntry
* entry
= m_propertyTable
->find(propertyName
).first
;
654 return WTF::notFound
;
656 attributes
= entry
->attributes
;
657 specificValue
= entry
->specificValue
.get();
658 return entry
->offset
;
661 bool Structure::despecifyFunction(JSGlobalData
& globalData
, const Identifier
& propertyName
)
663 materializePropertyMapIfNecessary(globalData
);
664 if (!m_propertyTable
)
667 ASSERT(!propertyName
.isNull());
668 PropertyMapEntry
* entry
= m_propertyTable
->find(propertyName
.impl()).first
;
672 ASSERT(entry
->specificValue
);
673 entry
->specificValue
.clear();
677 void Structure::despecifyAllFunctions(JSGlobalData
& globalData
)
679 materializePropertyMapIfNecessary(globalData
);
680 if (!m_propertyTable
)
683 PropertyTable::iterator end
= m_propertyTable
->end();
684 for (PropertyTable::iterator iter
= m_propertyTable
->begin(); iter
!= end
; ++iter
)
685 iter
->specificValue
.clear();
688 size_t Structure::putSpecificValue(JSGlobalData
& globalData
, const Identifier
& propertyName
, unsigned attributes
, JSCell
* specificValue
)
690 ASSERT(!propertyName
.isNull());
691 ASSERT(get(globalData
, propertyName
) == notFound
);
694 if (attributes
& DontEnum
)
695 m_hasNonEnumerableProperties
= true;
697 StringImpl
* rep
= propertyName
.impl();
699 if (!m_propertyTable
)
704 if (m_propertyTable
->hasDeletedOffset())
705 newOffset
= m_propertyTable
->getDeletedOffset();
707 newOffset
= m_propertyTable
->size();
709 m_propertyTable
->add(PropertyMapEntry(globalData
, this, rep
, newOffset
, attributes
, specificValue
));
715 size_t Structure::remove(const Identifier
& propertyName
)
717 ASSERT(!propertyName
.isNull());
721 StringImpl
* rep
= propertyName
.impl();
723 if (!m_propertyTable
)
726 PropertyTable::find_iterator position
= m_propertyTable
->find(rep
);
730 size_t offset
= position
.first
->offset
;
732 m_propertyTable
->remove(position
);
733 m_propertyTable
->addDeletedOffset(offset
);
739 void Structure::createPropertyMap(unsigned capacity
)
741 ASSERT(!m_propertyTable
);
744 m_propertyTable
= adoptPtr(new PropertyTable(capacity
));
748 void Structure::getPropertyNamesFromStructure(JSGlobalData
& globalData
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
750 materializePropertyMapIfNecessary(globalData
);
751 if (!m_propertyTable
)
754 bool knownUnique
= !propertyNames
.size();
756 PropertyTable::iterator end
= m_propertyTable
->end();
757 for (PropertyTable::iterator iter
= m_propertyTable
->begin(); iter
!= end
; ++iter
) {
758 ASSERT(m_hasNonEnumerableProperties
|| !(iter
->attributes
& DontEnum
));
759 if (!(iter
->attributes
& DontEnum
) || (mode
== IncludeDontEnumProperties
)) {
761 propertyNames
.addKnownUnique(iter
->key
);
763 propertyNames
.add(iter
->key
);
768 void Structure::visitChildren(JSCell
* cell
, SlotVisitor
& visitor
)
770 Structure
* thisObject
= jsCast
<Structure
*>(cell
);
771 ASSERT_GC_OBJECT_INHERITS(thisObject
, &s_info
);
772 ASSERT(thisObject
->structure()->typeInfo().overridesVisitChildren());
773 JSCell::visitChildren(thisObject
, visitor
);
774 if (thisObject
->m_globalObject
)
775 visitor
.append(&thisObject
->m_globalObject
);
776 if (!thisObject
->isObject())
777 thisObject
->m_cachedPrototypeChain
.clear();
779 if (thisObject
->m_prototype
)
780 visitor
.append(&thisObject
->m_prototype
);
781 if (thisObject
->m_cachedPrototypeChain
)
782 visitor
.append(&thisObject
->m_cachedPrototypeChain
);
784 if (thisObject
->m_previous
)
785 visitor
.append(&thisObject
->m_previous
);
786 if (thisObject
->m_specificValueInPrevious
)
787 visitor
.append(&thisObject
->m_specificValueInPrevious
);
788 if (thisObject
->m_enumerationCache
)
789 visitor
.append(&thisObject
->m_enumerationCache
);
790 if (thisObject
->m_propertyTable
) {
791 PropertyTable::iterator end
= thisObject
->m_propertyTable
->end();
792 for (PropertyTable::iterator ptr
= thisObject
->m_propertyTable
->begin(); ptr
!= end
; ++ptr
) {
793 if (ptr
->specificValue
)
794 visitor
.append(&ptr
->specificValue
);
797 if (thisObject
->m_objectToStringValue
)
798 visitor
.append(&thisObject
->m_objectToStringValue
);
801 #if DO_PROPERTYMAP_CONSTENCY_CHECK
803 void PropertyTable::checkConsistency()
805 ASSERT(m_indexSize
>= PropertyTable::MinimumTableSize
);
807 ASSERT(m_indexSize
== m_indexMask
+ 1);
808 ASSERT(!(m_indexSize
& m_indexMask
));
810 ASSERT(m_keyCount
<= m_indexSize
/ 2);
811 ASSERT(m_keyCount
+ m_deletedCount
<= m_indexSize
/ 2);
812 ASSERT(m_deletedCount
<= m_indexSize
/ 4);
814 unsigned indexCount
= 0;
815 unsigned deletedIndexCount
= 0;
816 for (unsigned a
= 0; a
!= m_indexSize
; ++a
) {
817 unsigned entryIndex
= m_index
[a
];
818 if (entryIndex
== PropertyTable::EmptyEntryIndex
)
820 if (entryIndex
== deletedEntryIndex()) {
824 ASSERT(entryIndex
< deletedEntryIndex());
825 ASSERT(entryIndex
- 1 <= usedCount());
828 for (unsigned b
= a
+ 1; b
!= m_indexSize
; ++b
)
829 ASSERT(m_index
[b
] != entryIndex
);
831 ASSERT(indexCount
== m_keyCount
);
832 ASSERT(deletedIndexCount
== m_deletedCount
);
834 ASSERT(!table()[deletedEntryIndex() - 1].key
);
836 unsigned nonEmptyEntryCount
= 0;
837 for (unsigned c
= 0; c
< usedCount(); ++c
) {
838 StringImpl
* rep
= table()[c
].key
;
839 if (rep
== PROPERTY_MAP_DELETED_ENTRY_KEY
)
841 ++nonEmptyEntryCount
;
842 unsigned i
= rep
->existingHash();
846 entryIndex
= m_index
[i
& m_indexMask
];
847 ASSERT(entryIndex
!= PropertyTable::EmptyEntryIndex
);
848 if (rep
== table()[entryIndex
- 1].key
)
851 k
= 1 | doubleHash(rep
->existingHash());
854 ASSERT(entryIndex
== c
+ 1);
857 ASSERT(nonEmptyEntryCount
== m_keyCount
);
860 void Structure::checkConsistency()
862 if (!m_propertyTable
)
865 if (!m_hasNonEnumerableProperties
) {
866 PropertyTable::iterator end
= m_propertyTable
->end();
867 for (PropertyTable::iterator iter
= m_propertyTable
->begin(); iter
!= end
; ++iter
) {
868 ASSERT(!(iter
->attributes
& DontEnum
));
872 m_propertyTable
->checkConsistency();
875 #endif // DO_PROPERTYMAP_CONSTENCY_CHECK