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(AtomicStringImpl* 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 inline Structure* StructureTransitionTable::get(AtomicStringImpl* 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 inline 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, AtomicStringImpl* 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, AtomicStringImpl* 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.clear();
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(AtomicStringImpl* 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 AtomicStringImpl* 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 AtomicStringImpl* 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.get());
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())