2 * Copyright (C) 2008, 2009, 2013 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 "CodeBlock.h"
31 #include "JSPropertyNameIterator.h"
33 #include "PropertyNameArray.h"
34 #include "StructureChain.h"
35 #include "StructureRareDataInlines.h"
36 #include <wtf/RefCountedLeakCounter.h>
37 #include <wtf/RefPtr.h>
38 #include <wtf/Threading.h>
40 #define DUMP_STRUCTURE_ID_STATISTICS 0
43 #define DO_PROPERTYMAP_CONSTENCY_CHECK 0
45 #define DO_PROPERTYMAP_CONSTENCY_CHECK 0
51 #if DUMP_PROPERTYMAP_STATS
62 #if DUMP_STRUCTURE_ID_STATISTICS
63 static HashSet
<Structure
*>& liveStructureSet
= *(new HashSet
<Structure
*>);
66 bool StructureTransitionTable::contains(StringImpl
* rep
, unsigned attributes
) const
68 if (isUsingSingleSlot()) {
69 Structure
* transition
= singleTransition();
70 return transition
&& transition
->m_nameInPrevious
== rep
&& transition
->m_attributesInPrevious
== attributes
;
72 return map()->get(make_pair(rep
, attributes
));
75 inline Structure
* StructureTransitionTable::get(StringImpl
* rep
, unsigned attributes
) const
77 if (isUsingSingleSlot()) {
78 Structure
* transition
= singleTransition();
79 return (transition
&& transition
->m_nameInPrevious
== rep
&& transition
->m_attributesInPrevious
== attributes
) ? transition
: 0;
81 return map()->get(make_pair(rep
, attributes
));
84 inline void StructureTransitionTable::add(VM
& vm
, Structure
* structure
)
86 if (isUsingSingleSlot()) {
87 Structure
* existingTransition
= singleTransition();
89 // This handles the first transition being added.
90 if (!existingTransition
) {
91 setSingleTransition(vm
, structure
);
95 // This handles the second transition being added
96 // (or the first transition being despecified!)
97 setMap(new TransitionMap());
98 add(vm
, existingTransition
);
101 // Add the structure to the map.
103 // Newer versions of the STL have an std::make_pair function that takes rvalue references.
104 // 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.
105 // See https://bugs.webkit.org/show_bug.cgi?id=59261 for more details
106 map()->set(make_pair(structure
->m_nameInPrevious
, +structure
->m_attributesInPrevious
), structure
);
109 void Structure::dumpStatistics()
111 #if DUMP_STRUCTURE_ID_STATISTICS
112 unsigned numberLeaf
= 0;
113 unsigned numberUsingSingleSlot
= 0;
114 unsigned numberSingletons
= 0;
115 unsigned numberWithPropertyMaps
= 0;
116 unsigned totalPropertyMapsSize
= 0;
118 HashSet
<Structure
*>::const_iterator end
= liveStructureSet
.end();
119 for (HashSet
<Structure
*>::const_iterator it
= liveStructureSet
.begin(); it
!= end
; ++it
) {
120 Structure
* structure
= *it
;
122 switch (structure
->m_transitionTable
.size()) {
125 if (!structure
->previousID())
130 ++numberUsingSingleSlot
;
134 if (structure
->propertyTable()) {
135 ++numberWithPropertyMaps
;
136 totalPropertyMapsSize
+= structure
->propertyTable()->sizeInMemory();
140 dataLogF("Number of live Structures: %d\n", liveStructureSet
.size());
141 dataLogF("Number of Structures using the single item optimization for transition map: %d\n", numberUsingSingleSlot
);
142 dataLogF("Number of Structures that are leaf nodes: %d\n", numberLeaf
);
143 dataLogF("Number of Structures that singletons: %d\n", numberSingletons
);
144 dataLogF("Number of Structures with PropertyMaps: %d\n", numberWithPropertyMaps
);
146 dataLogF("Size of a single Structures: %d\n", static_cast<unsigned>(sizeof(Structure
)));
147 dataLogF("Size of sum of all property maps: %d\n", totalPropertyMapsSize
);
148 dataLogF("Size of average of all property maps: %f\n", static_cast<double>(totalPropertyMapsSize
) / static_cast<double>(liveStructureSet
.size()));
150 dataLogF("Dumping Structure statistics is not enabled.\n");
154 Structure::Structure(VM
& vm
, JSGlobalObject
* globalObject
, JSValue prototype
, const TypeInfo
& typeInfo
, const ClassInfo
* classInfo
, IndexingType indexingType
, unsigned inlineCapacity
)
155 : JSCell(vm
, vm
.structureStructure
.get())
156 , m_globalObject(vm
, this, globalObject
, WriteBarrier
<JSGlobalObject
>::MayBeNull
)
157 , m_prototype(vm
, this, prototype
)
158 , m_classInfo(classInfo
)
159 , m_transitionWatchpointSet(InitializedWatching
)
160 , m_offset(invalidOffset
)
161 , m_typeInfo(typeInfo
)
162 , m_indexingType(indexingType
)
163 , m_inlineCapacity(inlineCapacity
)
164 , m_dictionaryKind(NoneDictionaryKind
)
165 , m_isPinnedPropertyTable(false)
166 , m_hasGetterSetterProperties(false)
167 , m_hasReadOnlyOrGetterSetterPropertiesExcludingProto(false)
168 , m_hasNonEnumerableProperties(false)
169 , m_attributesInPrevious(0)
170 , m_specificFunctionThrashCount(0)
171 , m_preventExtensions(false)
172 , m_didTransition(false)
173 , m_staticFunctionReified(false)
175 ASSERT(inlineCapacity
<= JSFinalObject::maxInlineCapacity());
176 ASSERT(static_cast<PropertyOffset
>(inlineCapacity
) < firstOutOfLineOffset
);
177 ASSERT(!typeInfo
.structureHasRareData());
180 const ClassInfo
Structure::s_info
= { "Structure", 0, 0, 0, CREATE_METHOD_TABLE(Structure
) };
182 Structure::Structure(VM
& vm
)
183 : JSCell(CreatingEarlyCell
)
184 , m_prototype(vm
, this, jsNull())
185 , m_classInfo(&s_info
)
186 , m_transitionWatchpointSet(InitializedWatching
)
187 , m_offset(invalidOffset
)
188 , m_typeInfo(CompoundType
, OverridesVisitChildren
)
190 , m_inlineCapacity(0)
191 , m_dictionaryKind(NoneDictionaryKind
)
192 , m_isPinnedPropertyTable(false)
193 , m_hasGetterSetterProperties(false)
194 , m_hasReadOnlyOrGetterSetterPropertiesExcludingProto(false)
195 , m_hasNonEnumerableProperties(false)
196 , m_attributesInPrevious(0)
197 , m_specificFunctionThrashCount(0)
198 , m_preventExtensions(false)
199 , m_didTransition(false)
200 , m_staticFunctionReified(false)
204 Structure::Structure(VM
& vm
, const Structure
* previous
)
205 : JSCell(vm
, vm
.structureStructure
.get())
206 , m_prototype(vm
, this, previous
->storedPrototype())
207 , m_classInfo(previous
->m_classInfo
)
208 , m_transitionWatchpointSet(InitializedWatching
)
209 , m_offset(invalidOffset
)
210 , m_typeInfo(previous
->typeInfo().type(), previous
->typeInfo().flags() & ~StructureHasRareData
)
211 , m_indexingType(previous
->indexingTypeIncludingHistory())
212 , m_inlineCapacity(previous
->m_inlineCapacity
)
213 , m_dictionaryKind(previous
->m_dictionaryKind
)
214 , m_isPinnedPropertyTable(false)
215 , m_hasGetterSetterProperties(previous
->m_hasGetterSetterProperties
)
216 , m_hasReadOnlyOrGetterSetterPropertiesExcludingProto(previous
->m_hasReadOnlyOrGetterSetterPropertiesExcludingProto
)
217 , m_hasNonEnumerableProperties(previous
->m_hasNonEnumerableProperties
)
218 , m_attributesInPrevious(0)
219 , m_specificFunctionThrashCount(previous
->m_specificFunctionThrashCount
)
220 , m_preventExtensions(previous
->m_preventExtensions
)
221 , m_didTransition(true)
222 , m_staticFunctionReified(previous
->m_staticFunctionReified
)
224 if (previous
->typeInfo().structureHasRareData() && previous
->rareData()->needsCloning())
225 cloneRareDataFrom(vm
, previous
);
226 else if (previous
->previousID())
227 m_previousOrRareData
.set(vm
, this, previous
->previousID());
229 previous
->notifyTransitionFromThisStructure();
230 if (previous
->m_globalObject
)
231 m_globalObject
.set(vm
, this, previous
->m_globalObject
.get());
234 void Structure::destroy(JSCell
* cell
)
236 static_cast<Structure
*>(cell
)->Structure::~Structure();
239 void Structure::materializePropertyMap(VM
& vm
)
241 ASSERT(structure()->classInfo() == &s_info
);
242 ASSERT(!propertyTable());
244 Vector
<Structure
*, 8> structures
;
245 structures
.append(this);
247 Structure
* structure
= this;
249 // Search for the last Structure with a property table.
250 while ((structure
= structure
->previousID())) {
251 if (structure
->m_isPinnedPropertyTable
) {
252 ASSERT(structure
->propertyTable());
253 ASSERT(!structure
->previousID());
255 propertyTable().set(vm
, this, structure
->propertyTable()->copy(vm
, 0, numberOfSlotsForLastOffset(m_offset
, m_inlineCapacity
)));
259 structures
.append(structure
);
262 if (!propertyTable())
263 createPropertyMap(vm
, numberOfSlotsForLastOffset(m_offset
, m_inlineCapacity
));
265 for (ptrdiff_t i
= structures
.size() - 1; i
>= 0; --i
) {
266 structure
= structures
[i
];
267 if (!structure
->m_nameInPrevious
)
269 PropertyMapEntry
entry(vm
, this, structure
->m_nameInPrevious
.get(), structure
->m_offset
, structure
->m_attributesInPrevious
, structure
->m_specificValueInPrevious
.get());
270 propertyTable()->add(entry
, m_offset
, PropertyTable::PropertyOffsetMustNotChange
);
273 checkOffsetConsistency();
276 inline size_t nextOutOfLineStorageCapacity(size_t currentCapacity
)
278 if (!currentCapacity
)
279 return initialOutOfLineCapacity
;
280 return currentCapacity
* outOfLineGrowthFactor
;
283 size_t Structure::suggestedNewOutOfLineStorageCapacity()
285 return nextOutOfLineStorageCapacity(outOfLineCapacity());
288 void Structure::despecifyDictionaryFunction(VM
& vm
, PropertyName propertyName
)
290 StringImpl
* rep
= propertyName
.uid();
292 materializePropertyMapIfNecessary(vm
);
294 ASSERT(isDictionary());
295 ASSERT(propertyTable());
297 PropertyMapEntry
* entry
= propertyTable()->find(rep
).first
;
299 entry
->specificValue
.clear();
302 Structure
* Structure::addPropertyTransitionToExistingStructure(Structure
* structure
, PropertyName propertyName
, unsigned attributes
, JSCell
* specificValue
, PropertyOffset
& offset
)
304 ASSERT(!structure
->isDictionary());
305 ASSERT(structure
->isObject());
307 if (Structure
* existingTransition
= structure
->m_transitionTable
.get(propertyName
.uid(), attributes
)) {
308 JSCell
* specificValueInPrevious
= existingTransition
->m_specificValueInPrevious
.get();
309 if (specificValueInPrevious
&& specificValueInPrevious
!= specificValue
)
311 validateOffset(existingTransition
->m_offset
, existingTransition
->inlineCapacity());
312 offset
= existingTransition
->m_offset
;
313 return existingTransition
;
319 bool Structure::anyObjectInChainMayInterceptIndexedAccesses() const
321 for (const Structure
* current
= this; ;) {
322 if (current
->mayInterceptIndexedAccesses())
325 JSValue prototype
= current
->storedPrototype();
326 if (prototype
.isNull())
329 current
= asObject(prototype
)->structure();
333 bool Structure::needsSlowPutIndexing() const
335 return anyObjectInChainMayInterceptIndexedAccesses()
336 || globalObject()->isHavingABadTime();
339 NonPropertyTransition
Structure::suggestedArrayStorageTransition() const
341 if (needsSlowPutIndexing())
342 return AllocateSlowPutArrayStorage
;
344 return AllocateArrayStorage
;
347 Structure
* Structure::addPropertyTransition(VM
& vm
, Structure
* structure
, PropertyName propertyName
, unsigned attributes
, JSCell
* specificValue
, PropertyOffset
& offset
)
349 // If we have a specific function, we may have got to this point if there is
350 // already a transition with the correct property name and attributes, but
351 // specialized to a different function. In this case we just want to give up
352 // and despecialize the transition.
353 // In this case we clear the value of specificFunction which will result
354 // in us adding a non-specific transition, and any subsequent lookup in
355 // Structure::addPropertyTransitionToExistingStructure will just use that.
356 if (specificValue
&& structure
->m_transitionTable
.contains(propertyName
.uid(), attributes
))
359 ASSERT(!structure
->isDictionary());
360 ASSERT(structure
->isObject());
361 ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure
, propertyName
, attributes
, specificValue
, offset
));
363 if (structure
->m_specificFunctionThrashCount
== maxSpecificFunctionThrashCount
)
366 if (structure
->transitionCount() > s_maxTransitionLength
) {
367 Structure
* transition
= toCacheableDictionaryTransition(vm
, structure
);
368 ASSERT(structure
!= transition
);
369 offset
= transition
->putSpecificValue(vm
, propertyName
, attributes
, specificValue
);
373 Structure
* transition
= create(vm
, structure
);
375 transition
->m_cachedPrototypeChain
.setMayBeNull(vm
, transition
, structure
->m_cachedPrototypeChain
.get());
376 transition
->setPreviousID(vm
, transition
, structure
);
377 transition
->m_nameInPrevious
= propertyName
.uid();
378 transition
->m_attributesInPrevious
= attributes
;
379 transition
->m_specificValueInPrevious
.setMayBeNull(vm
, transition
, specificValue
);
380 transition
->propertyTable().set(vm
, transition
, structure
->takePropertyTableOrCloneIfPinned(vm
, transition
));
381 transition
->m_offset
= structure
->m_offset
;
383 offset
= transition
->putSpecificValue(vm
, propertyName
, attributes
, specificValue
);
385 checkOffset(transition
->m_offset
, transition
->inlineCapacity());
386 structure
->m_transitionTable
.add(vm
, transition
);
387 transition
->checkOffsetConsistency();
388 structure
->checkOffsetConsistency();
392 Structure
* Structure::removePropertyTransition(VM
& vm
, Structure
* structure
, PropertyName propertyName
, PropertyOffset
& offset
)
394 ASSERT(!structure
->isUncacheableDictionary());
396 Structure
* transition
= toUncacheableDictionaryTransition(vm
, structure
);
398 offset
= transition
->remove(propertyName
);
400 transition
->checkOffsetConsistency();
404 Structure
* Structure::changePrototypeTransition(VM
& vm
, Structure
* structure
, JSValue prototype
)
406 Structure
* transition
= create(vm
, structure
);
408 transition
->m_prototype
.set(vm
, transition
, prototype
);
410 structure
->materializePropertyMapIfNecessary(vm
);
411 transition
->propertyTable().set(vm
, transition
, structure
->copyPropertyTableForPinning(vm
, transition
));
412 transition
->m_offset
= structure
->m_offset
;
415 transition
->checkOffsetConsistency();
419 Structure
* Structure::despecifyFunctionTransition(VM
& vm
, Structure
* structure
, PropertyName replaceFunction
)
421 ASSERT(structure
->m_specificFunctionThrashCount
< maxSpecificFunctionThrashCount
);
422 Structure
* transition
= create(vm
, structure
);
424 ++transition
->m_specificFunctionThrashCount
;
426 structure
->materializePropertyMapIfNecessary(vm
);
427 transition
->propertyTable().set(vm
, transition
, structure
->copyPropertyTableForPinning(vm
, transition
));
428 transition
->m_offset
= structure
->m_offset
;
431 if (transition
->m_specificFunctionThrashCount
== maxSpecificFunctionThrashCount
)
432 transition
->despecifyAllFunctions(vm
);
434 bool removed
= transition
->despecifyFunction(vm
, replaceFunction
);
435 ASSERT_UNUSED(removed
, removed
);
438 transition
->checkOffsetConsistency();
442 Structure
* Structure::attributeChangeTransition(VM
& vm
, Structure
* structure
, PropertyName propertyName
, unsigned attributes
)
444 if (!structure
->isUncacheableDictionary()) {
445 Structure
* transition
= create(vm
, structure
);
447 structure
->materializePropertyMapIfNecessary(vm
);
448 transition
->propertyTable().set(vm
, transition
, structure
->copyPropertyTableForPinning(vm
, transition
));
449 transition
->m_offset
= structure
->m_offset
;
452 structure
= transition
;
455 ASSERT(structure
->propertyTable());
456 PropertyMapEntry
* entry
= structure
->propertyTable()->find(propertyName
.uid()).first
;
458 entry
->attributes
= attributes
;
460 structure
->checkOffsetConsistency();
464 Structure
* Structure::toDictionaryTransition(VM
& vm
, Structure
* structure
, DictionaryKind kind
)
466 ASSERT(!structure
->isUncacheableDictionary());
468 Structure
* transition
= create(vm
, structure
);
470 structure
->materializePropertyMapIfNecessary(vm
);
471 transition
->propertyTable().set(vm
, transition
, structure
->copyPropertyTableForPinning(vm
, transition
));
472 transition
->m_offset
= structure
->m_offset
;
473 transition
->m_dictionaryKind
= kind
;
476 transition
->checkOffsetConsistency();
480 Structure
* Structure::toCacheableDictionaryTransition(VM
& vm
, Structure
* structure
)
482 return toDictionaryTransition(vm
, structure
, CachedDictionaryKind
);
485 Structure
* Structure::toUncacheableDictionaryTransition(VM
& vm
, Structure
* structure
)
487 return toDictionaryTransition(vm
, structure
, UncachedDictionaryKind
);
490 // In future we may want to cache this transition.
491 Structure
* Structure::sealTransition(VM
& vm
, Structure
* structure
)
493 Structure
* transition
= preventExtensionsTransition(vm
, structure
);
495 if (transition
->propertyTable()) {
496 PropertyTable::iterator end
= transition
->propertyTable()->end();
497 for (PropertyTable::iterator iter
= transition
->propertyTable()->begin(); iter
!= end
; ++iter
)
498 iter
->attributes
|= DontDelete
;
501 transition
->checkOffsetConsistency();
505 // In future we may want to cache this transition.
506 Structure
* Structure::freezeTransition(VM
& vm
, Structure
* structure
)
508 Structure
* transition
= preventExtensionsTransition(vm
, structure
);
510 if (transition
->propertyTable()) {
511 PropertyTable::iterator iter
= transition
->propertyTable()->begin();
512 PropertyTable::iterator end
= transition
->propertyTable()->end();
514 transition
->m_hasReadOnlyOrGetterSetterPropertiesExcludingProto
= true;
515 for (; iter
!= end
; ++iter
)
516 iter
->attributes
|= iter
->attributes
& Accessor
? DontDelete
: (DontDelete
| ReadOnly
);
519 transition
->checkOffsetConsistency();
523 // In future we may want to cache this transition.
524 Structure
* Structure::preventExtensionsTransition(VM
& vm
, Structure
* structure
)
526 Structure
* transition
= create(vm
, structure
);
528 // Don't set m_offset, as one can not transition to this.
530 structure
->materializePropertyMapIfNecessary(vm
);
531 transition
->propertyTable().set(vm
, transition
, structure
->copyPropertyTableForPinning(vm
, transition
));
532 transition
->m_offset
= structure
->m_offset
;
533 transition
->m_preventExtensions
= true;
536 transition
->checkOffsetConsistency();
540 PropertyTable
* Structure::takePropertyTableOrCloneIfPinned(VM
& vm
, Structure
* owner
)
542 materializePropertyMapIfNecessaryForPinning(vm
);
543 if (m_isPinnedPropertyTable
)
544 return propertyTable()->copy(vm
, owner
, propertyTable()->size() + 1);
545 PropertyTable
* takenPropertyTable
= propertyTable().get();
546 propertyTable().clear();
547 return takenPropertyTable
;
550 Structure
* Structure::nonPropertyTransition(VM
& vm
, Structure
* structure
, NonPropertyTransition transitionKind
)
552 unsigned attributes
= toAttributes(transitionKind
);
553 IndexingType indexingType
= newIndexingType(structure
->indexingTypeIncludingHistory(), transitionKind
);
555 if (JSGlobalObject
* globalObject
= structure
->m_globalObject
.get()) {
556 if (globalObject
->isOriginalArrayStructure(structure
)) {
557 Structure
* result
= globalObject
->originalArrayStructureForIndexingType(indexingType
);
558 if (result
->indexingTypeIncludingHistory() == indexingType
) {
559 structure
->notifyTransitionFromThisStructure();
565 if (Structure
* existingTransition
= structure
->m_transitionTable
.get(0, attributes
)) {
566 ASSERT(existingTransition
->m_attributesInPrevious
== attributes
);
567 ASSERT(existingTransition
->indexingTypeIncludingHistory() == indexingType
);
568 return existingTransition
;
571 Structure
* transition
= create(vm
, structure
);
572 transition
->setPreviousID(vm
, transition
, structure
);
573 transition
->m_attributesInPrevious
= attributes
;
574 transition
->m_indexingType
= indexingType
;
575 transition
->propertyTable().set(vm
, transition
, structure
->takePropertyTableOrCloneIfPinned(vm
, transition
));
576 transition
->m_offset
= structure
->m_offset
;
577 checkOffset(transition
->m_offset
, transition
->inlineCapacity());
579 structure
->m_transitionTable
.add(vm
, transition
);
580 transition
->checkOffsetConsistency();
584 // In future we may want to cache this property.
585 bool Structure::isSealed(VM
& vm
)
590 materializePropertyMapIfNecessary(vm
);
591 if (!propertyTable())
594 PropertyTable::iterator end
= propertyTable()->end();
595 for (PropertyTable::iterator iter
= propertyTable()->begin(); iter
!= end
; ++iter
) {
596 if ((iter
->attributes
& DontDelete
) != DontDelete
)
602 // In future we may want to cache this property.
603 bool Structure::isFrozen(VM
& vm
)
608 materializePropertyMapIfNecessary(vm
);
609 if (!propertyTable())
612 PropertyTable::iterator end
= propertyTable()->end();
613 for (PropertyTable::iterator iter
= propertyTable()->begin(); iter
!= end
; ++iter
) {
614 if (!(iter
->attributes
& DontDelete
))
616 if (!(iter
->attributes
& (ReadOnly
| Accessor
)))
622 Structure
* Structure::flattenDictionaryStructure(VM
& vm
, JSObject
* object
)
624 checkOffsetConsistency();
625 ASSERT(isDictionary());
626 if (isUncacheableDictionary()) {
627 ASSERT(propertyTable());
629 size_t propertyCount
= propertyTable()->size();
631 // Holds our values compacted by insertion order.
632 Vector
<JSValue
> values(propertyCount
);
634 // Copies out our values from their hashed locations, compacting property table offsets as we go.
636 PropertyTable::iterator end
= propertyTable()->end();
637 m_offset
= invalidOffset
;
638 for (PropertyTable::iterator iter
= propertyTable()->begin(); iter
!= end
; ++iter
, ++i
) {
639 values
[i
] = object
->getDirect(iter
->offset
);
640 m_offset
= iter
->offset
= offsetForPropertyNumber(i
, m_inlineCapacity
);
643 // Copies in our values to their compacted locations.
644 for (unsigned i
= 0; i
< propertyCount
; i
++)
645 object
->putDirect(vm
, offsetForPropertyNumber(i
, m_inlineCapacity
), values
[i
]);
647 propertyTable()->clearDeletedOffsets();
648 checkOffsetConsistency();
651 m_dictionaryKind
= NoneDictionaryKind
;
655 PropertyOffset
Structure::addPropertyWithoutTransition(VM
& vm
, PropertyName propertyName
, unsigned attributes
, JSCell
* specificValue
)
657 ASSERT(!enumerationCache());
659 if (m_specificFunctionThrashCount
== maxSpecificFunctionThrashCount
)
662 materializePropertyMapIfNecessaryForPinning(vm
);
666 return putSpecificValue(vm
, propertyName
, attributes
, specificValue
);
669 PropertyOffset
Structure::removePropertyWithoutTransition(VM
& vm
, PropertyName propertyName
)
671 ASSERT(isUncacheableDictionary());
672 ASSERT(!enumerationCache());
674 materializePropertyMapIfNecessaryForPinning(vm
);
677 return remove(propertyName
);
680 void Structure::pin()
682 ASSERT(propertyTable());
683 m_isPinnedPropertyTable
= true;
685 m_nameInPrevious
.clear();
688 void Structure::allocateRareData(VM
& vm
)
690 ASSERT(!typeInfo().structureHasRareData());
691 StructureRareData
* rareData
= StructureRareData::create(vm
, previous());
692 m_typeInfo
= TypeInfo(typeInfo().type(), typeInfo().flags() | StructureHasRareData
);
693 m_previousOrRareData
.set(vm
, this, rareData
);
696 void Structure::cloneRareDataFrom(VM
& vm
, const Structure
* other
)
698 ASSERT(other
->typeInfo().structureHasRareData());
699 StructureRareData
* newRareData
= StructureRareData::clone(vm
, other
->rareData());
700 m_typeInfo
= TypeInfo(typeInfo().type(), typeInfo().flags() | StructureHasRareData
);
701 m_previousOrRareData
.set(vm
, this, newRareData
);
704 #if DUMP_PROPERTYMAP_STATS
706 struct PropertyMapStatisticsExitLogger
{
707 ~PropertyMapStatisticsExitLogger();
710 static PropertyMapStatisticsExitLogger logger
;
712 PropertyMapStatisticsExitLogger::~PropertyMapStatisticsExitLogger()
714 dataLogF("\nJSC::PropertyMap statistics\n\n");
715 dataLogF("%d probes\n", numProbes
);
716 dataLogF("%d collisions (%.1f%%)\n", numCollisions
, 100.0 * numCollisions
/ numProbes
);
717 dataLogF("%d rehashes\n", numRehashes
);
718 dataLogF("%d removes\n", numRemoves
);
723 #if !DO_PROPERTYMAP_CONSTENCY_CHECK
725 inline void Structure::checkConsistency()
727 checkOffsetConsistency();
732 PropertyTable
* Structure::copyPropertyTable(VM
& vm
, Structure
* owner
)
734 if (!propertyTable())
736 return PropertyTable::clone(vm
, owner
, *propertyTable().get());
739 PropertyTable
* Structure::copyPropertyTableForPinning(VM
& vm
, Structure
* owner
)
742 return PropertyTable::clone(vm
, owner
, *propertyTable().get());
743 return PropertyTable::create(vm
, numberOfSlotsForLastOffset(m_offset
, m_inlineCapacity
));
746 PropertyOffset
Structure::get(VM
& vm
, PropertyName propertyName
, unsigned& attributes
, JSCell
*& specificValue
)
748 ASSERT(structure()->classInfo() == &s_info
);
750 materializePropertyMapIfNecessary(vm
);
751 if (!propertyTable())
752 return invalidOffset
;
754 PropertyMapEntry
* entry
= propertyTable()->find(propertyName
.uid()).first
;
756 return invalidOffset
;
758 attributes
= entry
->attributes
;
759 specificValue
= entry
->specificValue
.get();
760 return entry
->offset
;
763 bool Structure::despecifyFunction(VM
& vm
, PropertyName propertyName
)
765 materializePropertyMapIfNecessary(vm
);
766 if (!propertyTable())
769 PropertyMapEntry
* entry
= propertyTable()->find(propertyName
.uid()).first
;
773 ASSERT(entry
->specificValue
);
774 entry
->specificValue
.clear();
778 void Structure::despecifyAllFunctions(VM
& vm
)
780 materializePropertyMapIfNecessary(vm
);
781 if (!propertyTable())
784 PropertyTable::iterator end
= propertyTable()->end();
785 for (PropertyTable::iterator iter
= propertyTable()->begin(); iter
!= end
; ++iter
)
786 iter
->specificValue
.clear();
789 PropertyOffset
Structure::putSpecificValue(VM
& vm
, PropertyName propertyName
, unsigned attributes
, JSCell
* specificValue
)
791 ASSERT(!JSC::isValidOffset(get(vm
, propertyName
)));
794 if (attributes
& DontEnum
)
795 m_hasNonEnumerableProperties
= true;
797 StringImpl
* rep
= propertyName
.uid();
799 if (!propertyTable())
800 createPropertyMap(vm
);
802 PropertyOffset newOffset
= propertyTable()->nextOffset(m_inlineCapacity
);
804 propertyTable()->add(PropertyMapEntry(vm
, this, rep
, newOffset
, attributes
, specificValue
), m_offset
, PropertyTable::PropertyOffsetMayChange
);
810 PropertyOffset
Structure::remove(PropertyName propertyName
)
814 StringImpl
* rep
= propertyName
.uid();
816 if (!propertyTable())
817 return invalidOffset
;
819 PropertyTable::find_iterator position
= propertyTable()->find(rep
);
821 return invalidOffset
;
823 PropertyOffset offset
= position
.first
->offset
;
825 propertyTable()->remove(position
);
826 propertyTable()->addDeletedOffset(offset
);
832 void Structure::createPropertyMap(VM
& vm
, unsigned capacity
)
834 ASSERT(!propertyTable());
837 propertyTable().set(vm
, this, PropertyTable::create(vm
, capacity
));
840 void Structure::getPropertyNamesFromStructure(VM
& vm
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
842 materializePropertyMapIfNecessary(vm
);
843 if (!propertyTable())
846 bool knownUnique
= !propertyNames
.size();
848 PropertyTable::iterator end
= propertyTable()->end();
849 for (PropertyTable::iterator iter
= propertyTable()->begin(); iter
!= end
; ++iter
) {
850 ASSERT(m_hasNonEnumerableProperties
|| !(iter
->attributes
& DontEnum
));
851 if (iter
->key
->isIdentifier() && (!(iter
->attributes
& DontEnum
) || mode
== IncludeDontEnumProperties
)) {
853 propertyNames
.addKnownUnique(iter
->key
);
855 propertyNames
.add(iter
->key
);
860 JSValue
Structure::prototypeForLookup(CodeBlock
* codeBlock
) const
862 return prototypeForLookup(codeBlock
->globalObject());
865 void Structure::visitChildren(JSCell
* cell
, SlotVisitor
& visitor
)
867 Structure
* thisObject
= jsCast
<Structure
*>(cell
);
868 ASSERT_GC_OBJECT_INHERITS(thisObject
, &s_info
);
869 ASSERT(thisObject
->structure()->typeInfo().overridesVisitChildren());
871 JSCell::visitChildren(thisObject
, visitor
);
872 visitor
.append(&thisObject
->m_globalObject
);
873 if (!thisObject
->isObject())
874 thisObject
->m_cachedPrototypeChain
.clear();
876 visitor
.append(&thisObject
->m_prototype
);
877 visitor
.append(&thisObject
->m_cachedPrototypeChain
);
879 visitor
.append(&thisObject
->m_previousOrRareData
);
880 visitor
.append(&thisObject
->m_specificValueInPrevious
);
882 if (thisObject
->m_isPinnedPropertyTable
) {
883 ASSERT(thisObject
->m_propertyTableUnsafe
);
884 visitor
.append(&thisObject
->m_propertyTableUnsafe
);
885 } else if (thisObject
->m_propertyTableUnsafe
)
886 thisObject
->m_propertyTableUnsafe
.clear();
889 bool Structure::prototypeChainMayInterceptStoreTo(VM
& vm
, PropertyName propertyName
)
891 unsigned i
= propertyName
.asIndex();
892 if (i
!= PropertyName::NotAnIndex
)
893 return anyObjectInChainMayInterceptIndexedAccesses();
895 for (Structure
* current
= this; ;) {
896 JSValue prototype
= current
->storedPrototype();
897 if (prototype
.isNull())
900 current
= prototype
.asCell()->structure();
903 JSCell
* specificValue
;
904 PropertyOffset offset
= current
->get(vm
, propertyName
, attributes
, specificValue
);
905 if (!JSC::isValidOffset(offset
))
908 if (attributes
& (ReadOnly
| Accessor
))
915 #if DO_PROPERTYMAP_CONSTENCY_CHECK
917 void PropertyTable::checkConsistency()
919 checkOffsetConsistency();
920 ASSERT(m_indexSize
>= PropertyTable::MinimumTableSize
);
922 ASSERT(m_indexSize
== m_indexMask
+ 1);
923 ASSERT(!(m_indexSize
& m_indexMask
));
925 ASSERT(m_keyCount
<= m_indexSize
/ 2);
926 ASSERT(m_keyCount
+ m_deletedCount
<= m_indexSize
/ 2);
927 ASSERT(m_deletedCount
<= m_indexSize
/ 4);
929 unsigned indexCount
= 0;
930 unsigned deletedIndexCount
= 0;
931 for (unsigned a
= 0; a
!= m_indexSize
; ++a
) {
932 unsigned entryIndex
= m_index
[a
];
933 if (entryIndex
== PropertyTable::EmptyEntryIndex
)
935 if (entryIndex
== deletedEntryIndex()) {
939 ASSERT(entryIndex
< deletedEntryIndex());
940 ASSERT(entryIndex
- 1 <= usedCount());
943 for (unsigned b
= a
+ 1; b
!= m_indexSize
; ++b
)
944 ASSERT(m_index
[b
] != entryIndex
);
946 ASSERT(indexCount
== m_keyCount
);
947 ASSERT(deletedIndexCount
== m_deletedCount
);
949 ASSERT(!table()[deletedEntryIndex() - 1].key
);
951 unsigned nonEmptyEntryCount
= 0;
952 for (unsigned c
= 0; c
< usedCount(); ++c
) {
953 StringImpl
* rep
= table()[c
].key
;
954 if (rep
== PROPERTY_MAP_DELETED_ENTRY_KEY
)
956 ++nonEmptyEntryCount
;
957 unsigned i
= rep
->existingHash();
961 entryIndex
= m_index
[i
& m_indexMask
];
962 ASSERT(entryIndex
!= PropertyTable::EmptyEntryIndex
);
963 if (rep
== table()[entryIndex
- 1].key
)
966 k
= 1 | doubleHash(rep
->existingHash());
969 ASSERT(entryIndex
== c
+ 1);
972 ASSERT(nonEmptyEntryCount
== m_keyCount
);
975 void Structure::checkConsistency()
977 if (!propertyTable())
980 if (!m_hasNonEnumerableProperties
) {
981 PropertyTable::iterator end
= propertyTable()->end();
982 for (PropertyTable::iterator iter
= propertyTable()->begin(); iter
!= end
; ++iter
) {
983 ASSERT(!(iter
->attributes
& DontEnum
));
987 propertyTable()->checkConsistency();
990 #endif // DO_PROPERTYMAP_CONSTENCY_CHECK