2 * Copyright (C) 2008, 2009, 2013, 2014 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 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 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"
30 #include "DumpContext.h"
31 #include "JSCInlines.h"
33 #include "JSPropertyNameEnumerator.h"
35 #include "PropertyMapHashTable.h"
36 #include "PropertyNameArray.h"
37 #include "StructureChain.h"
38 #include "StructureRareDataInlines.h"
39 #include "WeakGCMapInlines.h"
40 #include <wtf/CommaPrinter.h>
41 #include <wtf/ProcessID.h>
42 #include <wtf/RefCountedLeakCounter.h>
43 #include <wtf/RefPtr.h>
44 #include <wtf/Threading.h>
46 #define DUMP_STRUCTURE_ID_STATISTICS 0
49 #define DO_PROPERTYMAP_CONSTENCY_CHECK 0
51 #define DO_PROPERTYMAP_CONSTENCY_CHECK 0
59 #if DUMP_STRUCTURE_ID_STATISTICS
60 static HashSet
<Structure
*>& liveStructureSet
= *(new HashSet
<Structure
*>);
63 bool StructureTransitionTable::contains(UniquedStringImpl
* rep
, unsigned attributes
) const
65 if (isUsingSingleSlot()) {
66 Structure
* transition
= singleTransition();
67 return transition
&& transition
->m_nameInPrevious
== rep
&& transition
->attributesInPrevious() == attributes
;
69 return map()->get(std::make_pair(rep
, attributes
));
72 Structure
* StructureTransitionTable::get(UniquedStringImpl
* rep
, unsigned attributes
) const
74 if (isUsingSingleSlot()) {
75 Structure
* transition
= singleTransition();
76 return (transition
&& transition
->m_nameInPrevious
== rep
&& transition
->attributesInPrevious() == attributes
) ? transition
: 0;
78 return map()->get(std::make_pair(rep
, attributes
));
81 void StructureTransitionTable::add(VM
& vm
, Structure
* structure
)
83 if (isUsingSingleSlot()) {
84 Structure
* existingTransition
= singleTransition();
86 // This handles the first transition being added.
87 if (!existingTransition
) {
88 setSingleTransition(vm
, structure
);
92 // This handles the second transition being added
93 // (or the first transition being despecified!)
94 setMap(new TransitionMap(vm
));
95 add(vm
, existingTransition
);
98 // Add the structure to the map.
100 // Newer versions of the STL have an std::make_pair function that takes rvalue references.
101 // 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.
102 // See https://bugs.webkit.org/show_bug.cgi?id=59261 for more details
103 map()->set(std::make_pair(structure
->m_nameInPrevious
.get(), +structure
->attributesInPrevious()), structure
);
106 void Structure::dumpStatistics()
108 #if DUMP_STRUCTURE_ID_STATISTICS
109 unsigned numberLeaf
= 0;
110 unsigned numberUsingSingleSlot
= 0;
111 unsigned numberSingletons
= 0;
112 unsigned numberWithPropertyMaps
= 0;
113 unsigned totalPropertyMapsSize
= 0;
115 HashSet
<Structure
*>::const_iterator end
= liveStructureSet
.end();
116 for (HashSet
<Structure
*>::const_iterator it
= liveStructureSet
.begin(); it
!= end
; ++it
) {
117 Structure
* structure
= *it
;
119 switch (structure
->m_transitionTable
.size()) {
122 if (!structure
->previousID())
127 ++numberUsingSingleSlot
;
131 if (structure
->propertyTable()) {
132 ++numberWithPropertyMaps
;
133 totalPropertyMapsSize
+= structure
->propertyTable()->sizeInMemory();
137 dataLogF("Number of live Structures: %d\n", liveStructureSet
.size());
138 dataLogF("Number of Structures using the single item optimization for transition map: %d\n", numberUsingSingleSlot
);
139 dataLogF("Number of Structures that are leaf nodes: %d\n", numberLeaf
);
140 dataLogF("Number of Structures that singletons: %d\n", numberSingletons
);
141 dataLogF("Number of Structures with PropertyMaps: %d\n", numberWithPropertyMaps
);
143 dataLogF("Size of a single Structures: %d\n", static_cast<unsigned>(sizeof(Structure
)));
144 dataLogF("Size of sum of all property maps: %d\n", totalPropertyMapsSize
);
145 dataLogF("Size of average of all property maps: %f\n", static_cast<double>(totalPropertyMapsSize
) / static_cast<double>(liveStructureSet
.size()));
147 dataLogF("Dumping Structure statistics is not enabled.\n");
151 Structure::Structure(VM
& vm
, JSGlobalObject
* globalObject
, JSValue prototype
, const TypeInfo
& typeInfo
, const ClassInfo
* classInfo
, IndexingType indexingType
, unsigned inlineCapacity
)
152 : JSCell(vm
, vm
.structureStructure
.get())
153 , m_blob(vm
.heap
.structureIDTable().allocateID(this), indexingType
, typeInfo
)
154 , m_outOfLineTypeFlags(typeInfo
.outOfLineTypeFlags())
155 , m_globalObject(vm
, this, globalObject
, WriteBarrier
<JSGlobalObject
>::MayBeNull
)
156 , m_prototype(vm
, this, prototype
)
157 , m_classInfo(classInfo
)
158 , m_transitionWatchpointSet(IsWatched
)
159 , m_offset(invalidOffset
)
160 , m_inlineCapacity(inlineCapacity
)
163 setDictionaryKind(NoneDictionaryKind
);
164 setIsPinnedPropertyTable(false);
165 setHasGetterSetterProperties(classInfo
->hasStaticSetterOrReadonlyProperties());
166 setHasCustomGetterSetterProperties(false);
167 setHasReadOnlyOrGetterSetterPropertiesExcludingProto(classInfo
->hasStaticSetterOrReadonlyProperties());
168 setHasNonEnumerableProperties(false);
169 setAttributesInPrevious(0);
170 setPreventExtensions(false);
171 setDidTransition(false);
172 setStaticFunctionsReified(false);
173 setHasRareData(false);
175 ASSERT(inlineCapacity
<= JSFinalObject::maxInlineCapacity());
176 ASSERT(static_cast<PropertyOffset
>(inlineCapacity
) < firstOutOfLineOffset
);
177 ASSERT(!hasRareData());
178 ASSERT(hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !m_classInfo
->hasStaticSetterOrReadonlyProperties());
179 ASSERT(hasGetterSetterProperties() || !m_classInfo
->hasStaticSetterOrReadonlyProperties());
182 const ClassInfo
Structure::s_info
= { "Structure", 0, 0, CREATE_METHOD_TABLE(Structure
) };
184 Structure::Structure(VM
& vm
)
185 : JSCell(CreatingEarlyCell
)
186 , m_prototype(vm
, this, jsNull())
187 , m_classInfo(info())
188 , m_transitionWatchpointSet(IsWatched
)
189 , m_offset(invalidOffset
)
190 , m_inlineCapacity(0)
193 setDictionaryKind(NoneDictionaryKind
);
194 setIsPinnedPropertyTable(false);
195 setHasGetterSetterProperties(m_classInfo
->hasStaticSetterOrReadonlyProperties());
196 setHasCustomGetterSetterProperties(false);
197 setHasReadOnlyOrGetterSetterPropertiesExcludingProto(m_classInfo
->hasStaticSetterOrReadonlyProperties());
198 setHasNonEnumerableProperties(false);
199 setAttributesInPrevious(0);
200 setPreventExtensions(false);
201 setDidTransition(false);
202 setStaticFunctionsReified(false);
203 setHasRareData(false);
205 TypeInfo typeInfo
= TypeInfo(CellType
, StructureFlags
);
206 m_blob
= StructureIDBlob(vm
.heap
.structureIDTable().allocateID(this), 0, typeInfo
);
207 m_outOfLineTypeFlags
= typeInfo
.outOfLineTypeFlags();
209 ASSERT(hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !m_classInfo
->hasStaticSetterOrReadonlyProperties());
210 ASSERT(hasGetterSetterProperties() || !m_classInfo
->hasStaticSetterOrReadonlyProperties());
213 Structure::Structure(VM
& vm
, Structure
* previous
)
214 : JSCell(vm
, vm
.structureStructure
.get())
215 , m_prototype(vm
, this, previous
->storedPrototype())
216 , m_classInfo(previous
->m_classInfo
)
217 , m_transitionWatchpointSet(IsWatched
)
218 , m_offset(invalidOffset
)
219 , m_inlineCapacity(previous
->m_inlineCapacity
)
222 setDictionaryKind(previous
->dictionaryKind());
223 setIsPinnedPropertyTable(previous
->hasBeenFlattenedBefore());
224 setHasGetterSetterProperties(previous
->hasGetterSetterProperties());
225 setHasCustomGetterSetterProperties(previous
->hasCustomGetterSetterProperties());
226 setHasReadOnlyOrGetterSetterPropertiesExcludingProto(previous
->hasReadOnlyOrGetterSetterPropertiesExcludingProto());
227 setHasNonEnumerableProperties(previous
->hasNonEnumerableProperties());
228 setAttributesInPrevious(0);
229 setPreventExtensions(previous
->preventExtensions());
230 setDidTransition(true);
231 setStaticFunctionsReified(previous
->staticFunctionsReified());
232 setHasRareData(false);
234 TypeInfo typeInfo
= previous
->typeInfo();
235 m_blob
= StructureIDBlob(vm
.heap
.structureIDTable().allocateID(this), previous
->indexingTypeIncludingHistory(), typeInfo
);
236 m_outOfLineTypeFlags
= typeInfo
.outOfLineTypeFlags();
238 ASSERT(!previous
->typeInfo().structureIsImmortal());
239 setPreviousID(vm
, previous
);
241 previous
->didTransitionFromThisStructure();
242 if (previous
->m_globalObject
)
243 m_globalObject
.set(vm
, this, previous
->m_globalObject
.get());
244 ASSERT(hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !m_classInfo
->hasStaticSetterOrReadonlyProperties());
245 ASSERT(hasGetterSetterProperties() || !m_classInfo
->hasStaticSetterOrReadonlyProperties());
248 Structure::~Structure()
250 if (typeInfo().structureIsImmortal())
252 Heap::heap(this)->structureIDTable().deallocateID(this, m_blob
.structureID());
255 void Structure::destroy(JSCell
* cell
)
257 static_cast<Structure
*>(cell
)->Structure::~Structure();
260 void Structure::findStructuresAndMapForMaterialization(Vector
<Structure
*, 8>& structures
, Structure
*& structure
, PropertyTable
*& table
)
262 ASSERT(structures
.isEmpty());
265 for (structure
= this; structure
; structure
= structure
->previousID()) {
266 structure
->m_lock
.lock();
268 table
= structure
->propertyTable().get();
270 // Leave the structure locked, so that the caller can do things to it atomically
271 // before it loses its property table.
275 structures
.append(structure
);
276 structure
->m_lock
.unlock();
283 void Structure::materializePropertyMap(VM
& vm
)
285 ASSERT(structure()->classInfo() == info());
286 ASSERT(!propertyTable());
288 Vector
<Structure
*, 8> structures
;
289 Structure
* structure
;
290 PropertyTable
* table
;
292 findStructuresAndMapForMaterialization(structures
, structure
, table
);
295 table
= table
->copy(vm
, numberOfSlotsForLastOffset(m_offset
, m_inlineCapacity
));
296 structure
->m_lock
.unlock();
299 // Must hold the lock on this structure, since we will be modifying this structure's
300 // property map. We don't want getConcurrently() to see the property map in a half-baked
302 GCSafeConcurrentJITLocker
locker(m_lock
, vm
.heap
);
304 createPropertyMap(locker
, vm
, numberOfSlotsForLastOffset(m_offset
, m_inlineCapacity
));
306 propertyTable().set(vm
, this, table
);
308 for (size_t i
= structures
.size(); i
--;) {
309 structure
= structures
[i
];
310 if (!structure
->m_nameInPrevious
)
312 PropertyMapEntry
entry(structure
->m_nameInPrevious
.get(), structure
->m_offset
, structure
->attributesInPrevious());
313 propertyTable()->add(entry
, m_offset
, PropertyTable::PropertyOffsetMustNotChange
);
316 checkOffsetConsistency();
319 Structure
* Structure::addPropertyTransitionToExistingStructureImpl(Structure
* structure
, UniquedStringImpl
* uid
, unsigned attributes
, PropertyOffset
& offset
)
321 ASSERT(!structure
->isDictionary());
322 ASSERT(structure
->isObject());
324 if (Structure
* existingTransition
= structure
->m_transitionTable
.get(uid
, attributes
)) {
325 validateOffset(existingTransition
->m_offset
, existingTransition
->inlineCapacity());
326 offset
= existingTransition
->m_offset
;
327 return existingTransition
;
333 Structure
* Structure::addPropertyTransitionToExistingStructure(Structure
* structure
, PropertyName propertyName
, unsigned attributes
, PropertyOffset
& offset
)
335 ASSERT(!isCompilationThread());
336 return addPropertyTransitionToExistingStructureImpl(structure
, propertyName
.uid(), attributes
, offset
);
339 Structure
* Structure::addPropertyTransitionToExistingStructureConcurrently(Structure
* structure
, UniquedStringImpl
* uid
, unsigned attributes
, PropertyOffset
& offset
)
341 ConcurrentJITLocker
locker(structure
->m_lock
);
342 return addPropertyTransitionToExistingStructureImpl(structure
, uid
, attributes
, offset
);
345 bool Structure::anyObjectInChainMayInterceptIndexedAccesses() const
347 for (const Structure
* current
= this; ;) {
348 if (current
->mayInterceptIndexedAccesses())
351 JSValue prototype
= current
->storedPrototype();
352 if (prototype
.isNull())
355 current
= asObject(prototype
)->structure();
359 bool Structure::holesMustForwardToPrototype(VM
& vm
) const
361 if (this->mayInterceptIndexedAccesses())
364 JSValue prototype
= this->storedPrototype();
365 if (!prototype
.isObject())
367 JSObject
* object
= asObject(prototype
);
370 Structure
& structure
= *object
->structure(vm
);
371 if (hasIndexedProperties(object
->indexingType()) || structure
.mayInterceptIndexedAccesses())
373 prototype
= structure
.storedPrototype();
374 if (!prototype
.isObject())
376 object
= asObject(prototype
);
379 RELEASE_ASSERT_NOT_REACHED();
383 bool Structure::needsSlowPutIndexing() const
385 return anyObjectInChainMayInterceptIndexedAccesses()
386 || globalObject()->isHavingABadTime();
389 NonPropertyTransition
Structure::suggestedArrayStorageTransition() const
391 if (needsSlowPutIndexing())
392 return AllocateSlowPutArrayStorage
;
394 return AllocateArrayStorage
;
397 Structure
* Structure::addPropertyTransition(VM
& vm
, Structure
* structure
, PropertyName propertyName
, unsigned attributes
, PropertyOffset
& offset
, PutPropertySlot::Context context
)
399 ASSERT(!structure
->isDictionary());
400 ASSERT(structure
->isObject());
401 ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure
, propertyName
, attributes
, offset
));
403 int maxTransitionLength
;
404 if (context
== PutPropertySlot::PutById
)
405 maxTransitionLength
= s_maxTransitionLengthForNonEvalPutById
;
407 maxTransitionLength
= s_maxTransitionLength
;
408 if (structure
->transitionCount() > maxTransitionLength
) {
409 Structure
* transition
= toCacheableDictionaryTransition(vm
, structure
);
410 ASSERT(structure
!= transition
);
411 offset
= transition
->add(vm
, propertyName
, attributes
);
415 Structure
* transition
= create(vm
, structure
);
417 transition
->m_cachedPrototypeChain
.setMayBeNull(vm
, transition
, structure
->m_cachedPrototypeChain
.get());
418 transition
->m_nameInPrevious
= propertyName
.uid();
419 transition
->setAttributesInPrevious(attributes
);
420 transition
->propertyTable().set(vm
, transition
, structure
->takePropertyTableOrCloneIfPinned(vm
));
421 transition
->m_offset
= structure
->m_offset
;
423 offset
= transition
->add(vm
, propertyName
, attributes
);
425 checkOffset(transition
->m_offset
, transition
->inlineCapacity());
427 ConcurrentJITLocker
locker(structure
->m_lock
);
428 structure
->m_transitionTable
.add(vm
, transition
);
430 transition
->checkOffsetConsistency();
431 structure
->checkOffsetConsistency();
435 Structure
* Structure::removePropertyTransition(VM
& vm
, Structure
* structure
, PropertyName propertyName
, PropertyOffset
& offset
)
437 ASSERT(!structure
->isUncacheableDictionary());
439 Structure
* transition
= toUncacheableDictionaryTransition(vm
, structure
);
441 offset
= transition
->remove(propertyName
);
443 transition
->checkOffsetConsistency();
447 Structure
* Structure::changePrototypeTransition(VM
& vm
, Structure
* structure
, JSValue prototype
)
449 Structure
* transition
= create(vm
, structure
);
451 transition
->m_prototype
.set(vm
, transition
, prototype
);
453 DeferGC
deferGC(vm
.heap
);
454 structure
->materializePropertyMapIfNecessary(vm
, deferGC
);
455 transition
->propertyTable().set(vm
, transition
, structure
->copyPropertyTableForPinning(vm
));
456 transition
->m_offset
= structure
->m_offset
;
459 transition
->checkOffsetConsistency();
463 Structure
* Structure::attributeChangeTransition(VM
& vm
, Structure
* structure
, PropertyName propertyName
, unsigned attributes
)
465 DeferGC
deferGC(vm
.heap
);
466 if (!structure
->isUncacheableDictionary()) {
467 Structure
* transition
= create(vm
, structure
);
469 structure
->materializePropertyMapIfNecessary(vm
, deferGC
);
470 transition
->propertyTable().set(vm
, transition
, structure
->copyPropertyTableForPinning(vm
));
471 transition
->m_offset
= structure
->m_offset
;
474 structure
= transition
;
477 ASSERT(structure
->propertyTable());
478 PropertyMapEntry
* entry
= structure
->propertyTable()->get(propertyName
.uid());
480 entry
->attributes
= attributes
;
482 structure
->checkOffsetConsistency();
486 Structure
* Structure::toDictionaryTransition(VM
& vm
, Structure
* structure
, DictionaryKind kind
)
488 ASSERT(!structure
->isUncacheableDictionary());
490 Structure
* transition
= create(vm
, structure
);
492 DeferGC
deferGC(vm
.heap
);
493 structure
->materializePropertyMapIfNecessary(vm
, deferGC
);
494 transition
->propertyTable().set(vm
, transition
, structure
->copyPropertyTableForPinning(vm
));
495 transition
->m_offset
= structure
->m_offset
;
496 transition
->setDictionaryKind(kind
);
499 transition
->checkOffsetConsistency();
503 Structure
* Structure::toCacheableDictionaryTransition(VM
& vm
, Structure
* structure
)
505 return toDictionaryTransition(vm
, structure
, CachedDictionaryKind
);
508 Structure
* Structure::toUncacheableDictionaryTransition(VM
& vm
, Structure
* structure
)
510 return toDictionaryTransition(vm
, structure
, UncachedDictionaryKind
);
513 // In future we may want to cache this transition.
514 Structure
* Structure::sealTransition(VM
& vm
, Structure
* structure
)
516 Structure
* transition
= preventExtensionsTransition(vm
, structure
);
518 if (transition
->propertyTable()) {
519 PropertyTable::iterator end
= transition
->propertyTable()->end();
520 for (PropertyTable::iterator iter
= transition
->propertyTable()->begin(); iter
!= end
; ++iter
)
521 iter
->attributes
|= DontDelete
;
524 transition
->checkOffsetConsistency();
528 // In future we may want to cache this transition.
529 Structure
* Structure::freezeTransition(VM
& vm
, Structure
* structure
)
531 Structure
* transition
= preventExtensionsTransition(vm
, structure
);
533 if (transition
->propertyTable()) {
534 PropertyTable::iterator iter
= transition
->propertyTable()->begin();
535 PropertyTable::iterator end
= transition
->propertyTable()->end();
537 transition
->setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true);
538 for (; iter
!= end
; ++iter
)
539 iter
->attributes
|= iter
->attributes
& Accessor
? DontDelete
: (DontDelete
| ReadOnly
);
542 ASSERT(transition
->hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !transition
->classInfo()->hasStaticSetterOrReadonlyProperties());
543 ASSERT(transition
->hasGetterSetterProperties() || !transition
->classInfo()->hasStaticSetterOrReadonlyProperties());
544 transition
->checkOffsetConsistency();
548 // In future we may want to cache this transition.
549 Structure
* Structure::preventExtensionsTransition(VM
& vm
, Structure
* structure
)
551 Structure
* transition
= create(vm
, structure
);
553 // Don't set m_offset, as one can not transition to this.
555 DeferGC
deferGC(vm
.heap
);
556 structure
->materializePropertyMapIfNecessary(vm
, deferGC
);
557 transition
->propertyTable().set(vm
, transition
, structure
->copyPropertyTableForPinning(vm
));
558 transition
->m_offset
= structure
->m_offset
;
559 transition
->setPreventExtensions(true);
562 transition
->checkOffsetConsistency();
566 PropertyTable
* Structure::takePropertyTableOrCloneIfPinned(VM
& vm
)
568 DeferGC
deferGC(vm
.heap
);
569 materializePropertyMapIfNecessaryForPinning(vm
, deferGC
);
571 if (isPinnedPropertyTable())
572 return propertyTable()->copy(vm
, propertyTable()->size() + 1);
574 // Hold the lock while stealing the table - so that getConcurrently() on another thread
575 // will either have to bypass this structure, or will get to use the property table
576 // before it is stolen.
577 ConcurrentJITLocker
locker(m_lock
);
578 PropertyTable
* takenPropertyTable
= propertyTable().get();
579 propertyTable().clear();
580 return takenPropertyTable
;
583 Structure
* Structure::nonPropertyTransition(VM
& vm
, Structure
* structure
, NonPropertyTransition transitionKind
)
585 unsigned attributes
= toAttributes(transitionKind
);
586 IndexingType indexingType
= newIndexingType(structure
->indexingTypeIncludingHistory(), transitionKind
);
588 if (JSGlobalObject
* globalObject
= structure
->m_globalObject
.get()) {
589 if (globalObject
->isOriginalArrayStructure(structure
)) {
590 Structure
* result
= globalObject
->originalArrayStructureForIndexingType(indexingType
);
591 if (result
->indexingTypeIncludingHistory() == indexingType
) {
592 structure
->didTransitionFromThisStructure();
598 Structure
* existingTransition
;
599 if (!structure
->isDictionary() && (existingTransition
= structure
->m_transitionTable
.get(0, attributes
))) {
600 ASSERT(existingTransition
->attributesInPrevious() == attributes
);
601 ASSERT(existingTransition
->indexingTypeIncludingHistory() == indexingType
);
602 return existingTransition
;
605 Structure
* transition
= create(vm
, structure
);
606 transition
->setAttributesInPrevious(attributes
);
607 transition
->m_blob
.setIndexingType(indexingType
);
608 transition
->propertyTable().set(vm
, transition
, structure
->takePropertyTableOrCloneIfPinned(vm
));
609 transition
->m_offset
= structure
->m_offset
;
610 checkOffset(transition
->m_offset
, transition
->inlineCapacity());
612 if (structure
->isDictionary())
615 ConcurrentJITLocker
locker(structure
->m_lock
);
616 structure
->m_transitionTable
.add(vm
, transition
);
618 transition
->checkOffsetConsistency();
622 // In future we may want to cache this property.
623 bool Structure::isSealed(VM
& vm
)
628 DeferGC
deferGC(vm
.heap
);
629 materializePropertyMapIfNecessary(vm
, deferGC
);
630 if (!propertyTable())
633 PropertyTable::iterator end
= propertyTable()->end();
634 for (PropertyTable::iterator iter
= propertyTable()->begin(); iter
!= end
; ++iter
) {
635 if ((iter
->attributes
& DontDelete
) != DontDelete
)
641 // In future we may want to cache this property.
642 bool Structure::isFrozen(VM
& vm
)
647 DeferGC
deferGC(vm
.heap
);
648 materializePropertyMapIfNecessary(vm
, deferGC
);
649 if (!propertyTable())
652 PropertyTable::iterator end
= propertyTable()->end();
653 for (PropertyTable::iterator iter
= propertyTable()->begin(); iter
!= end
; ++iter
) {
654 if (!(iter
->attributes
& DontDelete
))
656 if (!(iter
->attributes
& (ReadOnly
| Accessor
)))
662 Structure
* Structure::flattenDictionaryStructure(VM
& vm
, JSObject
* object
)
664 checkOffsetConsistency();
665 ASSERT(isDictionary());
667 size_t beforeOutOfLineCapacity
= this->outOfLineCapacity();
668 if (isUncacheableDictionary()) {
669 ASSERT(propertyTable());
671 size_t propertyCount
= propertyTable()->size();
673 // Holds our values compacted by insertion order.
674 Vector
<JSValue
> values(propertyCount
);
676 // Copies out our values from their hashed locations, compacting property table offsets as we go.
678 PropertyTable::iterator end
= propertyTable()->end();
679 m_offset
= invalidOffset
;
680 for (PropertyTable::iterator iter
= propertyTable()->begin(); iter
!= end
; ++iter
, ++i
) {
681 values
[i
] = object
->getDirect(iter
->offset
);
682 m_offset
= iter
->offset
= offsetForPropertyNumber(i
, m_inlineCapacity
);
685 // Copies in our values to their compacted locations.
686 for (unsigned i
= 0; i
< propertyCount
; i
++)
687 object
->putDirect(vm
, offsetForPropertyNumber(i
, m_inlineCapacity
), values
[i
]);
689 propertyTable()->clearDeletedOffsets();
690 checkOffsetConsistency();
693 setDictionaryKind(NoneDictionaryKind
);
694 setHasBeenFlattenedBefore(true);
696 size_t afterOutOfLineCapacity
= this->outOfLineCapacity();
698 if (beforeOutOfLineCapacity
!= afterOutOfLineCapacity
) {
699 ASSERT(beforeOutOfLineCapacity
> afterOutOfLineCapacity
);
700 // If the object had a Butterfly but after flattening/compacting we no longer have need of it,
701 // we need to zero it out because the collector depends on the Structure to know the size for copying.
702 if (object
->butterfly() && !afterOutOfLineCapacity
&& !this->hasIndexingHeader(object
))
703 object
->setStructureAndButterfly(vm
, this, 0);
704 // If the object was down-sized to the point where the base of the Butterfly is no longer within the
705 // first CopiedBlock::blockSize bytes, we'll get the wrong answer if we try to mask the base back to
706 // the CopiedBlock header. To prevent this case we need to memmove the Butterfly down.
707 else if (object
->butterfly())
708 object
->shiftButterflyAfterFlattening(vm
, beforeOutOfLineCapacity
, afterOutOfLineCapacity
);
714 PropertyOffset
Structure::addPropertyWithoutTransition(VM
& vm
, PropertyName propertyName
, unsigned attributes
)
716 DeferGC
deferGC(vm
.heap
);
717 materializePropertyMapIfNecessaryForPinning(vm
, deferGC
);
721 return add(vm
, propertyName
, attributes
);
724 PropertyOffset
Structure::removePropertyWithoutTransition(VM
& vm
, PropertyName propertyName
)
726 ASSERT(isUncacheableDictionary());
728 DeferGC
deferGC(vm
.heap
);
729 materializePropertyMapIfNecessaryForPinning(vm
, deferGC
);
732 return remove(propertyName
);
735 void Structure::pin()
737 ASSERT(propertyTable());
738 setIsPinnedPropertyTable(true);
740 m_nameInPrevious
= nullptr;
743 void Structure::allocateRareData(VM
& vm
)
745 ASSERT(!hasRareData());
746 StructureRareData
* rareData
= StructureRareData::create(vm
, previous());
747 WTF::storeStoreFence();
748 m_previousOrRareData
.set(vm
, this, rareData
);
749 WTF::storeStoreFence();
750 setHasRareData(true);
751 ASSERT(hasRareData());
754 WatchpointSet
* Structure::ensurePropertyReplacementWatchpointSet(VM
& vm
, PropertyOffset offset
)
756 ASSERT(!isUncacheableDictionary());
759 allocateRareData(vm
);
760 ConcurrentJITLocker
locker(m_lock
);
761 StructureRareData
* rareData
= this->rareData();
762 if (!rareData
->m_replacementWatchpointSets
) {
763 rareData
->m_replacementWatchpointSets
=
764 std::make_unique
<StructureRareData::PropertyWatchpointMap
>();
765 WTF::storeStoreFence();
767 auto result
= rareData
->m_replacementWatchpointSets
->add(offset
, nullptr);
768 if (result
.isNewEntry
)
769 result
.iterator
->value
= adoptRef(new WatchpointSet(IsWatched
));
770 return result
.iterator
->value
.get();
773 void Structure::startWatchingPropertyForReplacements(VM
& vm
, PropertyName propertyName
)
775 ASSERT(!isUncacheableDictionary());
777 PropertyOffset offset
= get(vm
, propertyName
);
778 if (!JSC::isValidOffset(offset
))
781 startWatchingPropertyForReplacements(vm
, offset
);
784 void Structure::didCachePropertyReplacement(VM
& vm
, PropertyOffset offset
)
786 ensurePropertyReplacementWatchpointSet(vm
, offset
)->fireAll("Did cache property replacement");
789 void Structure::startWatchingInternalProperties(VM
& vm
)
791 if (!isUncacheableDictionary()) {
792 startWatchingPropertyForReplacements(vm
, vm
.propertyNames
->toString
);
793 startWatchingPropertyForReplacements(vm
, vm
.propertyNames
->valueOf
);
795 setDidWatchInternalProperties(true);
798 #if DUMP_PROPERTYMAP_STATS
800 PropertyMapHashTableStats
* propertyMapHashTableStats
= 0;
802 struct PropertyMapStatisticsExitLogger
{
803 PropertyMapStatisticsExitLogger();
804 ~PropertyMapStatisticsExitLogger();
807 DEFINE_GLOBAL_FOR_LOGGING(PropertyMapStatisticsExitLogger
, logger
, );
809 PropertyMapStatisticsExitLogger::PropertyMapStatisticsExitLogger()
811 propertyMapHashTableStats
= adoptPtr(new PropertyMapHashTableStats()).leakPtr();
814 PropertyMapStatisticsExitLogger::~PropertyMapStatisticsExitLogger()
816 unsigned finds
= propertyMapHashTableStats
->numFinds
;
817 unsigned collisions
= propertyMapHashTableStats
->numCollisions
;
818 dataLogF("\nJSC::PropertyMap statistics for process %d\n\n", getCurrentProcessID());
819 dataLogF("%d finds\n", finds
);
820 dataLogF("%d collisions (%.1f%%)\n", collisions
, 100.0 * collisions
/ finds
);
821 dataLogF("%d lookups\n", propertyMapHashTableStats
->numLookups
.load());
822 dataLogF("%d lookup probings\n", propertyMapHashTableStats
->numLookupProbing
.load());
823 dataLogF("%d adds\n", propertyMapHashTableStats
->numAdds
.load());
824 dataLogF("%d removes\n", propertyMapHashTableStats
->numRemoves
.load());
825 dataLogF("%d rehashes\n", propertyMapHashTableStats
->numRehashes
.load());
826 dataLogF("%d reinserts\n", propertyMapHashTableStats
->numReinserts
.load());
831 PropertyTable
* Structure::copyPropertyTable(VM
& vm
)
833 if (!propertyTable())
835 return PropertyTable::clone(vm
, *propertyTable().get());
838 PropertyTable
* Structure::copyPropertyTableForPinning(VM
& vm
)
841 return PropertyTable::clone(vm
, *propertyTable().get());
842 return PropertyTable::create(vm
, numberOfSlotsForLastOffset(m_offset
, m_inlineCapacity
));
845 PropertyOffset
Structure::getConcurrently(UniquedStringImpl
* uid
, unsigned& attributes
)
847 PropertyOffset result
= invalidOffset
;
849 forEachPropertyConcurrently(
850 [&] (const PropertyMapEntry
& candidate
) -> bool {
851 if (candidate
.key
!= uid
)
854 result
= candidate
.offset
;
855 attributes
= candidate
.attributes
;
862 Vector
<PropertyMapEntry
> Structure::getPropertiesConcurrently()
864 Vector
<PropertyMapEntry
> result
;
866 forEachPropertyConcurrently(
867 [&] (const PropertyMapEntry
& entry
) -> bool {
868 result
.append(entry
);
875 PropertyOffset
Structure::add(VM
& vm
, PropertyName propertyName
, unsigned attributes
)
877 GCSafeConcurrentJITLocker
locker(m_lock
, vm
.heap
);
879 ASSERT(!JSC::isValidOffset(get(vm
, propertyName
)));
882 if (attributes
& DontEnum
)
883 setHasNonEnumerableProperties(true);
885 auto rep
= propertyName
.uid();
887 if (!propertyTable())
888 createPropertyMap(locker
, vm
);
890 PropertyOffset newOffset
= propertyTable()->nextOffset(m_inlineCapacity
);
892 propertyTable()->add(PropertyMapEntry(rep
, newOffset
, attributes
), m_offset
, PropertyTable::PropertyOffsetMayChange
);
898 PropertyOffset
Structure::remove(PropertyName propertyName
)
900 ConcurrentJITLocker
locker(m_lock
);
904 auto rep
= propertyName
.uid();
906 if (!propertyTable())
907 return invalidOffset
;
909 PropertyTable::find_iterator position
= propertyTable()->find(rep
);
911 return invalidOffset
;
913 PropertyOffset offset
= position
.first
->offset
;
915 propertyTable()->remove(position
);
916 propertyTable()->addDeletedOffset(offset
);
922 void Structure::createPropertyMap(const GCSafeConcurrentJITLocker
&, VM
& vm
, unsigned capacity
)
924 ASSERT(!propertyTable());
927 propertyTable().set(vm
, this, PropertyTable::create(vm
, capacity
));
930 void Structure::getPropertyNamesFromStructure(VM
& vm
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
932 DeferGC
deferGC(vm
.heap
);
933 materializePropertyMapIfNecessary(vm
, deferGC
);
934 if (!propertyTable())
937 bool knownUnique
= propertyNames
.canAddKnownUniqueForStructure();
939 PropertyTable::iterator end
= propertyTable()->end();
940 for (PropertyTable::iterator iter
= propertyTable()->begin(); iter
!= end
; ++iter
) {
941 ASSERT(hasNonEnumerableProperties() || !(iter
->attributes
& DontEnum
));
942 if (!(iter
->attributes
& DontEnum
) || mode
.includeDontEnumProperties()) {
943 if (iter
->key
->isSymbol() && !mode
.includeSymbolProperties())
946 propertyNames
.addKnownUnique(iter
->key
);
948 propertyNames
.add(iter
->key
);
955 class StructureFireDetail
: public FireDetail
{
957 StructureFireDetail(const Structure
* structure
)
958 : m_structure(structure
)
962 virtual void dump(PrintStream
& out
) const override
964 out
.print("Structure transition from ", *m_structure
);
968 const Structure
* m_structure
;
971 } // anonymous namespace
973 void Structure::didTransitionFromThisStructure() const
975 m_transitionWatchpointSet
.fireAll(StructureFireDetail(this));
978 JSValue
Structure::prototypeForLookup(CodeBlock
* codeBlock
) const
980 return prototypeForLookup(codeBlock
->globalObject());
983 void Structure::visitChildren(JSCell
* cell
, SlotVisitor
& visitor
)
985 Structure
* thisObject
= jsCast
<Structure
*>(cell
);
986 ASSERT_GC_OBJECT_INHERITS(thisObject
, info());
988 JSCell::visitChildren(thisObject
, visitor
);
989 visitor
.append(&thisObject
->m_globalObject
);
990 if (!thisObject
->isObject())
991 thisObject
->m_cachedPrototypeChain
.clear();
993 visitor
.append(&thisObject
->m_prototype
);
994 visitor
.append(&thisObject
->m_cachedPrototypeChain
);
996 visitor
.append(&thisObject
->m_previousOrRareData
);
998 if (thisObject
->isPinnedPropertyTable()) {
999 ASSERT(thisObject
->m_propertyTableUnsafe
);
1000 visitor
.append(&thisObject
->m_propertyTableUnsafe
);
1001 } else if (thisObject
->m_propertyTableUnsafe
)
1002 thisObject
->m_propertyTableUnsafe
.clear();
1005 bool Structure::prototypeChainMayInterceptStoreTo(VM
& vm
, PropertyName propertyName
)
1007 if (parseIndex(propertyName
))
1008 return anyObjectInChainMayInterceptIndexedAccesses();
1010 for (Structure
* current
= this; ;) {
1011 JSValue prototype
= current
->storedPrototype();
1012 if (prototype
.isNull())
1015 current
= prototype
.asCell()->structure(vm
);
1017 unsigned attributes
;
1018 PropertyOffset offset
= current
->get(vm
, propertyName
, attributes
);
1019 if (!JSC::isValidOffset(offset
))
1022 if (attributes
& (ReadOnly
| Accessor
))
1029 PassRefPtr
<StructureShape
> Structure::toStructureShape(JSValue value
)
1031 RefPtr
<StructureShape
> baseShape
= StructureShape::create();
1032 RefPtr
<StructureShape
> curShape
= baseShape
;
1033 Structure
* curStructure
= this;
1034 JSValue curValue
= value
;
1035 while (curStructure
) {
1036 Vector
<Structure
*, 8> structures
;
1037 Structure
* structure
;
1038 PropertyTable
* table
;
1040 curStructure
->findStructuresAndMapForMaterialization(structures
, structure
, table
);
1042 PropertyTable::iterator iter
= table
->begin();
1043 PropertyTable::iterator end
= table
->end();
1044 for (; iter
!= end
; ++iter
)
1045 curShape
->addProperty(*iter
->key
);
1047 structure
->m_lock
.unlock();
1049 for (unsigned i
= structures
.size(); i
--;) {
1050 Structure
* structure
= structures
[i
];
1051 if (structure
->m_nameInPrevious
)
1052 curShape
->addProperty(*structure
->m_nameInPrevious
);
1055 if (JSObject
* curObject
= curValue
.getObject())
1056 curShape
->setConstructorName(JSObject::calculatedClassName(curObject
));
1058 curShape
->setConstructorName(curStructure
->classInfo()->className
);
1060 if (curStructure
->isDictionary())
1061 curShape
->enterDictionaryMode();
1063 curShape
->markAsFinal();
1065 if (curStructure
->storedPrototypeStructure()) {
1066 RefPtr
<StructureShape
> newShape
= StructureShape::create();
1067 curShape
->setProto(newShape
);
1068 curShape
= newShape
.release();
1069 curValue
= curStructure
->storedPrototype();
1072 curStructure
= curStructure
->storedPrototypeStructure();
1075 return baseShape
.release();
1078 bool Structure::canUseForAllocationsOf(Structure
* other
)
1080 return inlineCapacity() == other
->inlineCapacity()
1081 && storedPrototype() == other
->storedPrototype()
1082 && objectInitializationBlob() == other
->objectInitializationBlob();
1085 void Structure::dump(PrintStream
& out
) const
1087 out
.print(RawPointer(this), ":[", classInfo()->className
, ", {");
1091 const_cast<Structure
*>(this)->forEachPropertyConcurrently(
1092 [&] (const PropertyMapEntry
& entry
) -> bool {
1093 out
.print(comma
, entry
.key
, ":", static_cast<int>(entry
.offset
));
1097 out
.print("}, ", IndexingTypeDump(indexingType()));
1099 if (m_prototype
.get().isCell())
1100 out
.print(", Proto:", RawPointer(m_prototype
.get().asCell()));
1105 void Structure::dumpInContext(PrintStream
& out
, DumpContext
* context
) const
1108 context
->structures
.dumpBrief(this, out
);
1113 void Structure::dumpBrief(PrintStream
& out
, const CString
& string
) const
1115 out
.print("%", string
, ":", classInfo()->className
);
1118 void Structure::dumpContextHeader(PrintStream
& out
)
1120 out
.print("Structures:");
1123 #if DO_PROPERTYMAP_CONSTENCY_CHECK
1125 void PropertyTable::checkConsistency()
1127 ASSERT(m_indexSize
>= PropertyTable::MinimumTableSize
);
1128 ASSERT(m_indexMask
);
1129 ASSERT(m_indexSize
== m_indexMask
+ 1);
1130 ASSERT(!(m_indexSize
& m_indexMask
));
1132 ASSERT(m_keyCount
<= m_indexSize
/ 2);
1133 ASSERT(m_keyCount
+ m_deletedCount
<= m_indexSize
/ 2);
1134 ASSERT(m_deletedCount
<= m_indexSize
/ 4);
1136 unsigned indexCount
= 0;
1137 unsigned deletedIndexCount
= 0;
1138 for (unsigned a
= 0; a
!= m_indexSize
; ++a
) {
1139 unsigned entryIndex
= m_index
[a
];
1140 if (entryIndex
== PropertyTable::EmptyEntryIndex
)
1142 if (entryIndex
== deletedEntryIndex()) {
1143 ++deletedIndexCount
;
1146 ASSERT(entryIndex
< deletedEntryIndex());
1147 ASSERT(entryIndex
- 1 <= usedCount());
1150 for (unsigned b
= a
+ 1; b
!= m_indexSize
; ++b
)
1151 ASSERT(m_index
[b
] != entryIndex
);
1153 ASSERT(indexCount
== m_keyCount
);
1154 ASSERT(deletedIndexCount
== m_deletedCount
);
1156 ASSERT(!table()[deletedEntryIndex() - 1].key
);
1158 unsigned nonEmptyEntryCount
= 0;
1159 for (unsigned c
= 0; c
< usedCount(); ++c
) {
1160 StringImpl
* rep
= table()[c
].key
;
1161 if (rep
== PROPERTY_MAP_DELETED_ENTRY_KEY
)
1163 ++nonEmptyEntryCount
;
1164 unsigned i
= IdentifierRepHash::hash(rep
);
1166 unsigned entryIndex
;
1168 entryIndex
= m_index
[i
& m_indexMask
];
1169 ASSERT(entryIndex
!= PropertyTable::EmptyEntryIndex
);
1170 if (rep
== table()[entryIndex
- 1].key
)
1173 k
= 1 | doubleHash(IdentifierRepHash::hash(rep
));
1176 ASSERT(entryIndex
== c
+ 1);
1179 ASSERT(nonEmptyEntryCount
== m_keyCount
);
1182 void Structure::checkConsistency()
1184 checkOffsetConsistency();
1186 if (!propertyTable())
1189 if (!hasNonEnumerableProperties()) {
1190 PropertyTable::iterator end
= propertyTable()->end();
1191 for (PropertyTable::iterator iter
= propertyTable()->begin(); iter
!= end
; ++iter
) {
1192 ASSERT(!(iter
->attributes
& DontEnum
));
1196 propertyTable()->checkConsistency();
1201 inline void Structure::checkConsistency()
1203 checkOffsetConsistency();
1206 #endif // DO_PROPERTYMAP_CONSTENCY_CHECK
1208 bool ClassInfo::hasStaticSetterOrReadonlyProperties() const
1210 for (const ClassInfo
* ci
= this; ci
; ci
= ci
->parentClass
) {
1211 if (const HashTable
* table
= ci
->staticPropHashTable
) {
1212 if (table
->hasSetterOrReadonlyProperties
)
1219 void Structure::setCachedPropertyNameEnumerator(VM
& vm
, JSPropertyNameEnumerator
* enumerator
)
1221 ASSERT(!isDictionary());
1223 allocateRareData(vm
);
1224 rareData()->setCachedPropertyNameEnumerator(vm
, enumerator
);
1227 JSPropertyNameEnumerator
* Structure::cachedPropertyNameEnumerator() const
1231 return rareData()->cachedPropertyNameEnumerator();
1234 bool Structure::canCachePropertyNameEnumerator() const
1239 if (hasIndexedProperties(indexingType()))
1242 if (typeInfo().overridesGetPropertyNames())
1245 StructureChain
* structureChain
= m_cachedPrototypeChain
.get();
1246 ASSERT(structureChain
);
1247 WriteBarrier
<Structure
>* structure
= structureChain
->head();
1249 if (!structure
->get())
1251 if (structure
->get()->typeInfo().overridesGetPropertyNames())
1259 bool Structure::canAccessPropertiesQuickly() const
1261 if (hasNonEnumerableProperties())
1263 if (hasGetterSetterProperties())
1265 if (isUncacheableDictionary())