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 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 "JSPropertyNameIterator.h"
35 #include "PropertyMapHashTable.h"
36 #include "PropertyNameArray.h"
37 #include "StructureChain.h"
38 #include "StructureRareDataInlines.h"
39 #include <wtf/CommaPrinter.h>
40 #include <wtf/ProcessID.h>
41 #include <wtf/RefCountedLeakCounter.h>
42 #include <wtf/RefPtr.h>
43 #include <wtf/Threading.h>
45 #define DUMP_STRUCTURE_ID_STATISTICS 0
48 #define DO_PROPERTYMAP_CONSTENCY_CHECK 0
50 #define DO_PROPERTYMAP_CONSTENCY_CHECK 0
58 #if DUMP_STRUCTURE_ID_STATISTICS
59 static HashSet
<Structure
*>& liveStructureSet
= *(new HashSet
<Structure
*>);
62 bool StructureTransitionTable::contains(StringImpl
* rep
, unsigned attributes
) const
64 if (isUsingSingleSlot()) {
65 Structure
* transition
= singleTransition();
66 return transition
&& transition
->m_nameInPrevious
== rep
&& transition
->m_attributesInPrevious
== attributes
;
68 return map()->get(std::make_pair(rep
, attributes
));
71 inline Structure
* StructureTransitionTable::get(StringImpl
* rep
, unsigned attributes
) const
73 if (isUsingSingleSlot()) {
74 Structure
* transition
= singleTransition();
75 return (transition
&& transition
->m_nameInPrevious
== rep
&& transition
->m_attributesInPrevious
== attributes
) ? transition
: 0;
77 return map()->get(std::make_pair(rep
, attributes
));
80 inline void StructureTransitionTable::add(VM
& vm
, Structure
* structure
)
82 if (isUsingSingleSlot()) {
83 Structure
* existingTransition
= singleTransition();
85 // This handles the first transition being added.
86 if (!existingTransition
) {
87 setSingleTransition(vm
, structure
);
91 // This handles the second transition being added
92 // (or the first transition being despecified!)
93 setMap(new TransitionMap());
94 add(vm
, existingTransition
);
97 // Add the structure to the map.
99 // Newer versions of the STL have an std::make_pair function that takes rvalue references.
100 // 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.
101 // See https://bugs.webkit.org/show_bug.cgi?id=59261 for more details
102 map()->set(std::make_pair(structure
->m_nameInPrevious
.get(), +structure
->m_attributesInPrevious
), structure
);
105 void Structure::dumpStatistics()
107 #if DUMP_STRUCTURE_ID_STATISTICS
108 unsigned numberLeaf
= 0;
109 unsigned numberUsingSingleSlot
= 0;
110 unsigned numberSingletons
= 0;
111 unsigned numberWithPropertyMaps
= 0;
112 unsigned totalPropertyMapsSize
= 0;
114 HashSet
<Structure
*>::const_iterator end
= liveStructureSet
.end();
115 for (HashSet
<Structure
*>::const_iterator it
= liveStructureSet
.begin(); it
!= end
; ++it
) {
116 Structure
* structure
= *it
;
118 switch (structure
->m_transitionTable
.size()) {
121 if (!structure
->previousID())
126 ++numberUsingSingleSlot
;
130 if (structure
->propertyTable()) {
131 ++numberWithPropertyMaps
;
132 totalPropertyMapsSize
+= structure
->propertyTable()->sizeInMemory();
136 dataLogF("Number of live Structures: %d\n", liveStructureSet
.size());
137 dataLogF("Number of Structures using the single item optimization for transition map: %d\n", numberUsingSingleSlot
);
138 dataLogF("Number of Structures that are leaf nodes: %d\n", numberLeaf
);
139 dataLogF("Number of Structures that singletons: %d\n", numberSingletons
);
140 dataLogF("Number of Structures with PropertyMaps: %d\n", numberWithPropertyMaps
);
142 dataLogF("Size of a single Structures: %d\n", static_cast<unsigned>(sizeof(Structure
)));
143 dataLogF("Size of sum of all property maps: %d\n", totalPropertyMapsSize
);
144 dataLogF("Size of average of all property maps: %f\n", static_cast<double>(totalPropertyMapsSize
) / static_cast<double>(liveStructureSet
.size()));
146 dataLogF("Dumping Structure statistics is not enabled.\n");
150 Structure::Structure(VM
& vm
, JSGlobalObject
* globalObject
, JSValue prototype
, const TypeInfo
& typeInfo
, const ClassInfo
* classInfo
, IndexingType indexingType
, unsigned inlineCapacity
)
151 : JSCell(vm
, vm
.structureStructure
.get())
152 , m_blob(vm
.heap
.structureIDTable().allocateID(this), indexingType
, typeInfo
)
153 , m_outOfLineTypeFlags(typeInfo
.outOfLineTypeFlags())
154 , m_globalObject(vm
, this, globalObject
, WriteBarrier
<JSGlobalObject
>::MayBeNull
)
155 , m_prototype(vm
, this, prototype
)
156 , m_classInfo(classInfo
)
157 , m_transitionWatchpointSet(IsWatched
)
158 , m_offset(invalidOffset
)
159 , m_inlineCapacity(inlineCapacity
)
160 , m_dictionaryKind(NoneDictionaryKind
)
161 , m_hasBeenFlattenedBefore(false)
162 , m_isPinnedPropertyTable(false)
163 , m_hasGetterSetterProperties(classInfo
->hasStaticSetterOrReadonlyProperties(vm
))
164 , m_hasCustomGetterSetterProperties(false)
165 , m_hasReadOnlyOrGetterSetterPropertiesExcludingProto(classInfo
->hasStaticSetterOrReadonlyProperties(vm
))
166 , m_hasNonEnumerableProperties(false)
167 , m_attributesInPrevious(0)
168 , m_specificFunctionThrashCount(0)
169 , m_preventExtensions(false)
170 , m_didTransition(false)
171 , m_staticFunctionReified(false)
172 , m_hasRareData(false)
174 ASSERT(inlineCapacity
<= JSFinalObject::maxInlineCapacity());
175 ASSERT(static_cast<PropertyOffset
>(inlineCapacity
) < firstOutOfLineOffset
);
176 ASSERT(!m_hasRareData
);
177 ASSERT(hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !m_classInfo
->hasStaticSetterOrReadonlyProperties(vm
));
178 ASSERT(hasGetterSetterProperties() || !m_classInfo
->hasStaticSetterOrReadonlyProperties(vm
));
181 const ClassInfo
Structure::s_info
= { "Structure", 0, 0, 0, CREATE_METHOD_TABLE(Structure
) };
183 Structure::Structure(VM
& vm
)
184 : JSCell(CreatingEarlyCell
)
185 , m_prototype(vm
, this, jsNull())
186 , m_classInfo(info())
187 , m_transitionWatchpointSet(IsWatched
)
188 , m_offset(invalidOffset
)
189 , m_inlineCapacity(0)
190 , m_dictionaryKind(NoneDictionaryKind
)
191 , m_hasBeenFlattenedBefore(false)
192 , m_isPinnedPropertyTable(false)
193 , m_hasGetterSetterProperties(m_classInfo
->hasStaticSetterOrReadonlyProperties(vm
))
194 , m_hasCustomGetterSetterProperties(false)
195 , m_hasReadOnlyOrGetterSetterPropertiesExcludingProto(m_classInfo
->hasStaticSetterOrReadonlyProperties(vm
))
196 , m_hasNonEnumerableProperties(false)
197 , m_attributesInPrevious(0)
198 , m_specificFunctionThrashCount(0)
199 , m_preventExtensions(false)
200 , m_didTransition(false)
201 , m_staticFunctionReified(false)
202 , m_hasRareData(false)
204 TypeInfo typeInfo
= TypeInfo(CompoundType
, OverridesVisitChildren
| StructureIsImmortal
);
205 m_blob
= StructureIDBlob(vm
.heap
.structureIDTable().allocateID(this), 0, typeInfo
);
206 m_outOfLineTypeFlags
= typeInfo
.outOfLineTypeFlags();
208 ASSERT(hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !m_classInfo
->hasStaticSetterOrReadonlyProperties(vm
));
209 ASSERT(hasGetterSetterProperties() || !m_classInfo
->hasStaticSetterOrReadonlyProperties(vm
));
212 Structure::Structure(VM
& vm
, Structure
* previous
)
213 : JSCell(vm
, vm
.structureStructure
.get())
214 , m_prototype(vm
, this, previous
->storedPrototype())
215 , m_classInfo(previous
->m_classInfo
)
216 , m_transitionWatchpointSet(IsWatched
)
217 , m_offset(invalidOffset
)
218 , m_inlineCapacity(previous
->m_inlineCapacity
)
219 , m_dictionaryKind(previous
->m_dictionaryKind
)
220 , m_hasBeenFlattenedBefore(previous
->m_hasBeenFlattenedBefore
)
221 , m_isPinnedPropertyTable(false)
222 , m_hasGetterSetterProperties(previous
->m_hasGetterSetterProperties
)
223 , m_hasCustomGetterSetterProperties(previous
->m_hasCustomGetterSetterProperties
)
224 , m_hasReadOnlyOrGetterSetterPropertiesExcludingProto(previous
->m_hasReadOnlyOrGetterSetterPropertiesExcludingProto
)
225 , m_hasNonEnumerableProperties(previous
->m_hasNonEnumerableProperties
)
226 , m_attributesInPrevious(0)
227 , m_specificFunctionThrashCount(previous
->m_specificFunctionThrashCount
)
228 , m_preventExtensions(previous
->m_preventExtensions
)
229 , m_didTransition(true)
230 , m_staticFunctionReified(previous
->m_staticFunctionReified
)
231 , m_hasRareData(false)
233 TypeInfo typeInfo
= previous
->typeInfo();
234 m_blob
= StructureIDBlob(vm
.heap
.structureIDTable().allocateID(this), previous
->indexingTypeIncludingHistory(), typeInfo
);
235 m_outOfLineTypeFlags
= typeInfo
.outOfLineTypeFlags();
237 ASSERT(!previous
->typeInfo().structureIsImmortal());
238 if (previous
->m_hasRareData
&& previous
->rareData()->needsCloning())
239 cloneRareDataFrom(vm
, previous
);
240 setPreviousID(vm
, previous
);
242 previous
->notifyTransitionFromThisStructure();
243 if (previous
->m_globalObject
)
244 m_globalObject
.set(vm
, this, previous
->m_globalObject
.get());
245 ASSERT(hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !m_classInfo
->hasStaticSetterOrReadonlyProperties(vm
));
246 ASSERT(hasGetterSetterProperties() || !m_classInfo
->hasStaticSetterOrReadonlyProperties(vm
));
249 Structure::~Structure()
251 if (typeInfo().structureIsImmortal())
253 Heap::heap(this)->structureIDTable().deallocateID(this, m_blob
.structureID());
256 void Structure::destroy(JSCell
* cell
)
258 static_cast<Structure
*>(cell
)->Structure::~Structure();
261 void Structure::findStructuresAndMapForMaterialization(Vector
<Structure
*, 8>& structures
, Structure
*& structure
, PropertyTable
*& table
)
263 ASSERT(structures
.isEmpty());
266 for (structure
= this; structure
; structure
= structure
->previousID()) {
267 structure
->m_lock
.lock();
269 table
= structure
->propertyTable().get();
271 // Leave the structure locked, so that the caller can do things to it atomically
272 // before it loses its property table.
276 structures
.append(structure
);
277 structure
->m_lock
.unlock();
284 void Structure::materializePropertyMap(VM
& vm
)
286 ASSERT(structure()->classInfo() == info());
287 ASSERT(!propertyTable());
289 Vector
<Structure
*, 8> structures
;
290 Structure
* structure
;
291 PropertyTable
* table
;
293 findStructuresAndMapForMaterialization(structures
, structure
, table
);
296 table
= table
->copy(vm
, numberOfSlotsForLastOffset(m_offset
, m_inlineCapacity
));
297 structure
->m_lock
.unlock();
300 // Must hold the lock on this structure, since we will be modifying this structure's
301 // property map. We don't want getConcurrently() to see the property map in a half-baked
303 GCSafeConcurrentJITLocker
locker(m_lock
, vm
.heap
);
305 createPropertyMap(locker
, vm
, numberOfSlotsForLastOffset(m_offset
, m_inlineCapacity
));
307 propertyTable().set(vm
, this, table
);
309 for (size_t i
= structures
.size(); i
--;) {
310 structure
= structures
[i
];
311 if (!structure
->m_nameInPrevious
)
313 PropertyMapEntry
entry(vm
, this, structure
->m_nameInPrevious
.get(), structure
->m_offset
, structure
->m_attributesInPrevious
, structure
->m_specificValueInPrevious
.get());
314 propertyTable()->add(entry
, m_offset
, PropertyTable::PropertyOffsetMustNotChange
);
317 checkOffsetConsistency();
320 void Structure::despecifyDictionaryFunction(VM
& vm
, PropertyName propertyName
)
322 StringImpl
* rep
= propertyName
.uid();
324 DeferGC
deferGC(vm
.heap
);
325 materializePropertyMapIfNecessary(vm
, deferGC
);
327 ASSERT(isDictionary());
328 ASSERT(propertyTable());
330 PropertyMapEntry
* entry
= propertyTable()->get(rep
);
332 entry
->specificValue
.clear();
335 Structure
* Structure::addPropertyTransitionToExistingStructureImpl(Structure
* structure
, StringImpl
* uid
, unsigned attributes
, JSCell
* specificValue
, PropertyOffset
& offset
)
337 ASSERT(!structure
->isDictionary());
338 ASSERT(structure
->isObject());
340 if (Structure
* existingTransition
= structure
->m_transitionTable
.get(uid
, attributes
)) {
341 JSCell
* specificValueInPrevious
= existingTransition
->m_specificValueInPrevious
.get();
342 if (specificValueInPrevious
&& specificValueInPrevious
!= specificValue
)
344 validateOffset(existingTransition
->m_offset
, existingTransition
->inlineCapacity());
345 offset
= existingTransition
->m_offset
;
346 return existingTransition
;
352 Structure
* Structure::addPropertyTransitionToExistingStructure(Structure
* structure
, PropertyName propertyName
, unsigned attributes
, JSCell
* specificValue
, PropertyOffset
& offset
)
354 ASSERT(!isCompilationThread());
355 return addPropertyTransitionToExistingStructureImpl(structure
, propertyName
.uid(), attributes
, specificValue
, offset
);
358 Structure
* Structure::addPropertyTransitionToExistingStructureConcurrently(Structure
* structure
, StringImpl
* uid
, unsigned attributes
, JSCell
* specificValue
, PropertyOffset
& offset
)
360 ConcurrentJITLocker
locker(structure
->m_lock
);
361 return addPropertyTransitionToExistingStructureImpl(structure
, uid
, attributes
, specificValue
, offset
);
364 bool Structure::anyObjectInChainMayInterceptIndexedAccesses() const
366 for (const Structure
* current
= this; ;) {
367 if (current
->mayInterceptIndexedAccesses())
370 JSValue prototype
= current
->storedPrototype();
371 if (prototype
.isNull())
374 current
= asObject(prototype
)->structure();
378 bool Structure::holesMustForwardToPrototype(VM
& vm
) const
380 if (this->mayInterceptIndexedAccesses())
383 JSValue prototype
= this->storedPrototype();
384 if (!prototype
.isObject())
386 JSObject
* object
= asObject(prototype
);
389 Structure
& structure
= *object
->structure(vm
);
390 if (hasIndexedProperties(object
->indexingType()) || structure
.mayInterceptIndexedAccesses())
392 prototype
= structure
.storedPrototype();
393 if (!prototype
.isObject())
395 object
= asObject(prototype
);
398 RELEASE_ASSERT_NOT_REACHED();
402 bool Structure::needsSlowPutIndexing() const
404 return anyObjectInChainMayInterceptIndexedAccesses()
405 || globalObject()->isHavingABadTime();
408 NonPropertyTransition
Structure::suggestedArrayStorageTransition() const
410 if (needsSlowPutIndexing())
411 return AllocateSlowPutArrayStorage
;
413 return AllocateArrayStorage
;
416 Structure
* Structure::addPropertyTransition(VM
& vm
, Structure
* structure
, PropertyName propertyName
, unsigned attributes
, JSCell
* specificValue
, PropertyOffset
& offset
, PutPropertySlot::Context context
)
418 // If we have a specific function, we may have got to this point if there is
419 // already a transition with the correct property name and attributes, but
420 // specialized to a different function. In this case we just want to give up
421 // and despecialize the transition.
422 // In this case we clear the value of specificFunction which will result
423 // in us adding a non-specific transition, and any subsequent lookup in
424 // Structure::addPropertyTransitionToExistingStructure will just use that.
425 if (specificValue
&& structure
->m_transitionTable
.contains(propertyName
.uid(), attributes
))
428 ASSERT(!structure
->isDictionary());
429 ASSERT(structure
->isObject());
430 ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure
, propertyName
, attributes
, specificValue
, offset
));
432 if (structure
->m_specificFunctionThrashCount
== maxSpecificFunctionThrashCount
)
435 int maxTransitionLength
;
436 if (context
== PutPropertySlot::PutById
)
437 maxTransitionLength
= s_maxTransitionLengthForNonEvalPutById
;
439 maxTransitionLength
= s_maxTransitionLength
;
440 if (structure
->transitionCount() > maxTransitionLength
) {
441 Structure
* transition
= toCacheableDictionaryTransition(vm
, structure
);
442 ASSERT(structure
!= transition
);
443 offset
= transition
->putSpecificValue(vm
, propertyName
, attributes
, specificValue
);
447 Structure
* transition
= create(vm
, structure
);
449 transition
->m_cachedPrototypeChain
.setMayBeNull(vm
, transition
, structure
->m_cachedPrototypeChain
.get());
450 transition
->m_nameInPrevious
= propertyName
.uid();
451 transition
->m_attributesInPrevious
= attributes
;
452 transition
->m_specificValueInPrevious
.setMayBeNull(vm
, transition
, specificValue
);
453 transition
->propertyTable().set(vm
, transition
, structure
->takePropertyTableOrCloneIfPinned(vm
));
454 transition
->m_offset
= structure
->m_offset
;
456 offset
= transition
->putSpecificValue(vm
, propertyName
, attributes
, specificValue
);
458 checkOffset(transition
->m_offset
, transition
->inlineCapacity());
460 ConcurrentJITLocker
locker(structure
->m_lock
);
461 structure
->m_transitionTable
.add(vm
, transition
);
463 transition
->checkOffsetConsistency();
464 structure
->checkOffsetConsistency();
468 Structure
* Structure::removePropertyTransition(VM
& vm
, Structure
* structure
, PropertyName propertyName
, PropertyOffset
& offset
)
470 ASSERT(!structure
->isUncacheableDictionary());
472 Structure
* transition
= toUncacheableDictionaryTransition(vm
, structure
);
474 offset
= transition
->remove(propertyName
);
476 transition
->checkOffsetConsistency();
480 Structure
* Structure::changePrototypeTransition(VM
& vm
, Structure
* structure
, JSValue prototype
)
482 Structure
* transition
= create(vm
, structure
);
484 transition
->m_prototype
.set(vm
, transition
, prototype
);
486 DeferGC
deferGC(vm
.heap
);
487 structure
->materializePropertyMapIfNecessary(vm
, deferGC
);
488 transition
->propertyTable().set(vm
, transition
, structure
->copyPropertyTableForPinning(vm
));
489 transition
->m_offset
= structure
->m_offset
;
492 transition
->checkOffsetConsistency();
496 Structure
* Structure::despecifyFunctionTransition(VM
& vm
, Structure
* structure
, PropertyName replaceFunction
)
498 ASSERT(structure
->m_specificFunctionThrashCount
< maxSpecificFunctionThrashCount
);
499 Structure
* transition
= create(vm
, structure
);
501 ++transition
->m_specificFunctionThrashCount
;
503 DeferGC
deferGC(vm
.heap
);
504 structure
->materializePropertyMapIfNecessary(vm
, deferGC
);
505 transition
->propertyTable().set(vm
, transition
, structure
->copyPropertyTableForPinning(vm
));
506 transition
->m_offset
= structure
->m_offset
;
509 if (transition
->m_specificFunctionThrashCount
== maxSpecificFunctionThrashCount
)
510 transition
->despecifyAllFunctions(vm
);
512 bool removed
= transition
->despecifyFunction(vm
, replaceFunction
);
513 ASSERT_UNUSED(removed
, removed
);
516 transition
->checkOffsetConsistency();
520 Structure
* Structure::attributeChangeTransition(VM
& vm
, Structure
* structure
, PropertyName propertyName
, unsigned attributes
)
522 DeferGC
deferGC(vm
.heap
);
523 if (!structure
->isUncacheableDictionary()) {
524 Structure
* transition
= create(vm
, structure
);
526 structure
->materializePropertyMapIfNecessary(vm
, deferGC
);
527 transition
->propertyTable().set(vm
, transition
, structure
->copyPropertyTableForPinning(vm
));
528 transition
->m_offset
= structure
->m_offset
;
531 structure
= transition
;
534 ASSERT(structure
->propertyTable());
535 PropertyMapEntry
* entry
= structure
->propertyTable()->get(propertyName
.uid());
537 entry
->attributes
= attributes
;
539 structure
->checkOffsetConsistency();
543 Structure
* Structure::toDictionaryTransition(VM
& vm
, Structure
* structure
, DictionaryKind kind
)
545 ASSERT(!structure
->isUncacheableDictionary());
547 Structure
* transition
= create(vm
, structure
);
549 DeferGC
deferGC(vm
.heap
);
550 structure
->materializePropertyMapIfNecessary(vm
, deferGC
);
551 transition
->propertyTable().set(vm
, transition
, structure
->copyPropertyTableForPinning(vm
));
552 transition
->m_offset
= structure
->m_offset
;
553 transition
->m_dictionaryKind
= kind
;
556 transition
->checkOffsetConsistency();
560 Structure
* Structure::toCacheableDictionaryTransition(VM
& vm
, Structure
* structure
)
562 return toDictionaryTransition(vm
, structure
, CachedDictionaryKind
);
565 Structure
* Structure::toUncacheableDictionaryTransition(VM
& vm
, Structure
* structure
)
567 return toDictionaryTransition(vm
, structure
, UncachedDictionaryKind
);
570 // In future we may want to cache this transition.
571 Structure
* Structure::sealTransition(VM
& vm
, Structure
* structure
)
573 Structure
* transition
= preventExtensionsTransition(vm
, structure
);
575 if (transition
->propertyTable()) {
576 PropertyTable::iterator end
= transition
->propertyTable()->end();
577 for (PropertyTable::iterator iter
= transition
->propertyTable()->begin(); iter
!= end
; ++iter
)
578 iter
->attributes
|= DontDelete
;
581 transition
->checkOffsetConsistency();
585 // In future we may want to cache this transition.
586 Structure
* Structure::freezeTransition(VM
& vm
, Structure
* structure
)
588 Structure
* transition
= preventExtensionsTransition(vm
, structure
);
590 if (transition
->propertyTable()) {
591 PropertyTable::iterator iter
= transition
->propertyTable()->begin();
592 PropertyTable::iterator end
= transition
->propertyTable()->end();
594 transition
->m_hasReadOnlyOrGetterSetterPropertiesExcludingProto
= true;
595 for (; iter
!= end
; ++iter
)
596 iter
->attributes
|= iter
->attributes
& Accessor
? DontDelete
: (DontDelete
| ReadOnly
);
599 ASSERT(transition
->hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !transition
->classInfo()->hasStaticSetterOrReadonlyProperties(vm
));
600 ASSERT(transition
->hasGetterSetterProperties() || !transition
->classInfo()->hasStaticSetterOrReadonlyProperties(vm
));
601 transition
->checkOffsetConsistency();
605 // In future we may want to cache this transition.
606 Structure
* Structure::preventExtensionsTransition(VM
& vm
, Structure
* structure
)
608 Structure
* transition
= create(vm
, structure
);
610 // Don't set m_offset, as one can not transition to this.
612 DeferGC
deferGC(vm
.heap
);
613 structure
->materializePropertyMapIfNecessary(vm
, deferGC
);
614 transition
->propertyTable().set(vm
, transition
, structure
->copyPropertyTableForPinning(vm
));
615 transition
->m_offset
= structure
->m_offset
;
616 transition
->m_preventExtensions
= true;
619 transition
->checkOffsetConsistency();
623 PropertyTable
* Structure::takePropertyTableOrCloneIfPinned(VM
& vm
)
625 DeferGC
deferGC(vm
.heap
);
626 materializePropertyMapIfNecessaryForPinning(vm
, deferGC
);
628 if (m_isPinnedPropertyTable
)
629 return propertyTable()->copy(vm
, propertyTable()->size() + 1);
631 // Hold the lock while stealing the table - so that getConcurrently() on another thread
632 // will either have to bypass this structure, or will get to use the property table
633 // before it is stolen.
634 ConcurrentJITLocker
locker(m_lock
);
635 PropertyTable
* takenPropertyTable
= propertyTable().get();
636 propertyTable().clear();
637 return takenPropertyTable
;
640 Structure
* Structure::nonPropertyTransition(VM
& vm
, Structure
* structure
, NonPropertyTransition transitionKind
)
642 unsigned attributes
= toAttributes(transitionKind
);
643 IndexingType indexingType
= newIndexingType(structure
->indexingTypeIncludingHistory(), transitionKind
);
645 if (JSGlobalObject
* globalObject
= structure
->m_globalObject
.get()) {
646 if (globalObject
->isOriginalArrayStructure(structure
)) {
647 Structure
* result
= globalObject
->originalArrayStructureForIndexingType(indexingType
);
648 if (result
->indexingTypeIncludingHistory() == indexingType
) {
649 structure
->notifyTransitionFromThisStructure();
655 Structure
* existingTransition
;
656 if (!structure
->isDictionary() && (existingTransition
= structure
->m_transitionTable
.get(0, attributes
))) {
657 ASSERT(existingTransition
->m_attributesInPrevious
== attributes
);
658 ASSERT(existingTransition
->indexingTypeIncludingHistory() == indexingType
);
659 return existingTransition
;
662 Structure
* transition
= create(vm
, structure
);
663 transition
->m_attributesInPrevious
= attributes
;
664 transition
->m_blob
.setIndexingType(indexingType
);
665 transition
->propertyTable().set(vm
, transition
, structure
->takePropertyTableOrCloneIfPinned(vm
));
666 transition
->m_offset
= structure
->m_offset
;
667 checkOffset(transition
->m_offset
, transition
->inlineCapacity());
669 if (structure
->isDictionary())
672 ConcurrentJITLocker
locker(structure
->m_lock
);
673 structure
->m_transitionTable
.add(vm
, transition
);
675 transition
->checkOffsetConsistency();
679 // In future we may want to cache this property.
680 bool Structure::isSealed(VM
& vm
)
685 DeferGC
deferGC(vm
.heap
);
686 materializePropertyMapIfNecessary(vm
, deferGC
);
687 if (!propertyTable())
690 PropertyTable::iterator end
= propertyTable()->end();
691 for (PropertyTable::iterator iter
= propertyTable()->begin(); iter
!= end
; ++iter
) {
692 if ((iter
->attributes
& DontDelete
) != DontDelete
)
698 // In future we may want to cache this property.
699 bool Structure::isFrozen(VM
& vm
)
704 DeferGC
deferGC(vm
.heap
);
705 materializePropertyMapIfNecessary(vm
, deferGC
);
706 if (!propertyTable())
709 PropertyTable::iterator end
= propertyTable()->end();
710 for (PropertyTable::iterator iter
= propertyTable()->begin(); iter
!= end
; ++iter
) {
711 if (!(iter
->attributes
& DontDelete
))
713 if (!(iter
->attributes
& (ReadOnly
| Accessor
)))
719 Structure
* Structure::flattenDictionaryStructure(VM
& vm
, JSObject
* object
)
721 checkOffsetConsistency();
722 ASSERT(isDictionary());
724 size_t beforeOutOfLineCapacity
= this->outOfLineCapacity();
725 if (isUncacheableDictionary()) {
726 ASSERT(propertyTable());
728 size_t propertyCount
= propertyTable()->size();
730 // Holds our values compacted by insertion order.
731 Vector
<JSValue
> values(propertyCount
);
733 // Copies out our values from their hashed locations, compacting property table offsets as we go.
735 PropertyTable::iterator end
= propertyTable()->end();
736 m_offset
= invalidOffset
;
737 for (PropertyTable::iterator iter
= propertyTable()->begin(); iter
!= end
; ++iter
, ++i
) {
738 values
[i
] = object
->getDirect(iter
->offset
);
739 m_offset
= iter
->offset
= offsetForPropertyNumber(i
, m_inlineCapacity
);
742 // Copies in our values to their compacted locations.
743 for (unsigned i
= 0; i
< propertyCount
; i
++)
744 object
->putDirect(vm
, offsetForPropertyNumber(i
, m_inlineCapacity
), values
[i
]);
746 propertyTable()->clearDeletedOffsets();
747 checkOffsetConsistency();
750 m_dictionaryKind
= NoneDictionaryKind
;
751 m_hasBeenFlattenedBefore
= true;
753 size_t afterOutOfLineCapacity
= this->outOfLineCapacity();
755 if (beforeOutOfLineCapacity
!= afterOutOfLineCapacity
) {
756 ASSERT(beforeOutOfLineCapacity
> afterOutOfLineCapacity
);
757 // If the object had a Butterfly but after flattening/compacting we no longer have need of it,
758 // we need to zero it out because the collector depends on the Structure to know the size for copying.
759 if (object
->butterfly() && !afterOutOfLineCapacity
&& !this->hasIndexingHeader(object
))
760 object
->setStructureAndButterfly(vm
, this, 0);
761 // If the object was down-sized to the point where the base of the Butterfly is no longer within the
762 // first CopiedBlock::blockSize bytes, we'll get the wrong answer if we try to mask the base back to
763 // the CopiedBlock header. To prevent this case we need to memmove the Butterfly down.
764 else if (object
->butterfly())
765 object
->shiftButterflyAfterFlattening(vm
, beforeOutOfLineCapacity
, afterOutOfLineCapacity
);
771 PropertyOffset
Structure::addPropertyWithoutTransition(VM
& vm
, PropertyName propertyName
, unsigned attributes
, JSCell
* specificValue
)
773 ASSERT(!enumerationCache());
775 if (m_specificFunctionThrashCount
== maxSpecificFunctionThrashCount
)
778 DeferGC
deferGC(vm
.heap
);
779 materializePropertyMapIfNecessaryForPinning(vm
, deferGC
);
783 return putSpecificValue(vm
, propertyName
, attributes
, specificValue
);
786 PropertyOffset
Structure::removePropertyWithoutTransition(VM
& vm
, PropertyName propertyName
)
788 ASSERT(isUncacheableDictionary());
789 ASSERT(!enumerationCache());
791 DeferGC
deferGC(vm
.heap
);
792 materializePropertyMapIfNecessaryForPinning(vm
, deferGC
);
795 return remove(propertyName
);
798 void Structure::pin()
800 ASSERT(propertyTable());
801 m_isPinnedPropertyTable
= true;
803 m_nameInPrevious
.clear();
806 void Structure::allocateRareData(VM
& vm
)
808 ASSERT(!m_hasRareData
);
809 StructureRareData
* rareData
= StructureRareData::create(vm
, previous());
810 m_previousOrRareData
.set(vm
, this, rareData
);
811 m_hasRareData
= true;
812 ASSERT(m_hasRareData
);
815 void Structure::cloneRareDataFrom(VM
& vm
, const Structure
* other
)
817 ASSERT(!m_hasRareData
);
818 ASSERT(other
->m_hasRareData
);
819 StructureRareData
* newRareData
= StructureRareData::clone(vm
, other
->rareData());
820 m_previousOrRareData
.set(vm
, this, newRareData
);
821 m_hasRareData
= true;
822 ASSERT(m_hasRareData
);
825 #if DUMP_PROPERTYMAP_STATS
827 PropertyMapHashTableStats
* propertyMapHashTableStats
= 0;
829 struct PropertyMapStatisticsExitLogger
{
830 PropertyMapStatisticsExitLogger();
831 ~PropertyMapStatisticsExitLogger();
834 DEFINE_GLOBAL_FOR_LOGGING(PropertyMapStatisticsExitLogger
, logger
, );
836 PropertyMapStatisticsExitLogger::PropertyMapStatisticsExitLogger()
838 propertyMapHashTableStats
= adoptPtr(new PropertyMapHashTableStats()).leakPtr();
841 PropertyMapStatisticsExitLogger::~PropertyMapStatisticsExitLogger()
843 unsigned finds
= propertyMapHashTableStats
->numFinds
;
844 unsigned collisions
= propertyMapHashTableStats
->numCollisions
;
845 dataLogF("\nJSC::PropertyMap statistics for process %d\n\n", getCurrentProcessID());
846 dataLogF("%d finds\n", finds
);
847 dataLogF("%d collisions (%.1f%%)\n", collisions
, 100.0 * collisions
/ finds
);
848 dataLogF("%d lookups\n", propertyMapHashTableStats
->numLookups
.load());
849 dataLogF("%d lookup probings\n", propertyMapHashTableStats
->numLookupProbing
.load());
850 dataLogF("%d adds\n", propertyMapHashTableStats
->numAdds
.load());
851 dataLogF("%d removes\n", propertyMapHashTableStats
->numRemoves
.load());
852 dataLogF("%d rehashes\n", propertyMapHashTableStats
->numRehashes
.load());
853 dataLogF("%d reinserts\n", propertyMapHashTableStats
->numReinserts
.load());
858 #if !DO_PROPERTYMAP_CONSTENCY_CHECK
860 inline void Structure::checkConsistency()
862 checkOffsetConsistency();
867 PropertyTable
* Structure::copyPropertyTable(VM
& vm
)
869 if (!propertyTable())
871 return PropertyTable::clone(vm
, *propertyTable().get());
874 PropertyTable
* Structure::copyPropertyTableForPinning(VM
& vm
)
877 return PropertyTable::clone(vm
, *propertyTable().get());
878 return PropertyTable::create(vm
, numberOfSlotsForLastOffset(m_offset
, m_inlineCapacity
));
881 PropertyOffset
Structure::getConcurrently(VM
&, StringImpl
* uid
, unsigned& attributes
, JSCell
*& specificValue
)
883 Vector
<Structure
*, 8> structures
;
884 Structure
* structure
;
885 PropertyTable
* table
;
887 findStructuresAndMapForMaterialization(structures
, structure
, table
);
890 PropertyMapEntry
* entry
= table
->get(uid
);
892 attributes
= entry
->attributes
;
893 specificValue
= entry
->specificValue
.get();
894 PropertyOffset result
= entry
->offset
;
895 structure
->m_lock
.unlock();
898 structure
->m_lock
.unlock();
901 for (unsigned i
= structures
.size(); i
--;) {
902 structure
= structures
[i
];
903 if (structure
->m_nameInPrevious
.get() != uid
)
906 attributes
= structure
->m_attributesInPrevious
;
907 specificValue
= structure
->m_specificValueInPrevious
.get();
908 return structure
->m_offset
;
911 return invalidOffset
;
914 bool Structure::despecifyFunction(VM
& vm
, PropertyName propertyName
)
916 DeferGC
deferGC(vm
.heap
);
917 materializePropertyMapIfNecessary(vm
, deferGC
);
918 if (!propertyTable())
921 PropertyMapEntry
* entry
= propertyTable()->get(propertyName
.uid());
925 ASSERT(entry
->specificValue
);
926 entry
->specificValue
.clear();
930 void Structure::despecifyAllFunctions(VM
& vm
)
932 DeferGC
deferGC(vm
.heap
);
933 materializePropertyMapIfNecessary(vm
, deferGC
);
934 if (!propertyTable())
937 PropertyTable::iterator end
= propertyTable()->end();
938 for (PropertyTable::iterator iter
= propertyTable()->begin(); iter
!= end
; ++iter
)
939 iter
->specificValue
.clear();
942 PropertyOffset
Structure::putSpecificValue(VM
& vm
, PropertyName propertyName
, unsigned attributes
, JSCell
* specificValue
)
944 GCSafeConcurrentJITLocker
locker(m_lock
, vm
.heap
);
946 ASSERT(!JSC::isValidOffset(get(vm
, propertyName
)));
949 if (attributes
& DontEnum
)
950 m_hasNonEnumerableProperties
= true;
952 StringImpl
* rep
= propertyName
.uid();
954 if (!propertyTable())
955 createPropertyMap(locker
, vm
);
957 PropertyOffset newOffset
= propertyTable()->nextOffset(m_inlineCapacity
);
959 propertyTable()->add(PropertyMapEntry(vm
, propertyTable().get(), rep
, newOffset
, attributes
, specificValue
), m_offset
, PropertyTable::PropertyOffsetMayChange
);
965 PropertyOffset
Structure::remove(PropertyName propertyName
)
967 ConcurrentJITLocker
locker(m_lock
);
971 StringImpl
* rep
= propertyName
.uid();
973 if (!propertyTable())
974 return invalidOffset
;
976 PropertyTable::find_iterator position
= propertyTable()->find(rep
);
978 return invalidOffset
;
980 PropertyOffset offset
= position
.first
->offset
;
982 propertyTable()->remove(position
);
983 propertyTable()->addDeletedOffset(offset
);
989 void Structure::createPropertyMap(const GCSafeConcurrentJITLocker
&, VM
& vm
, unsigned capacity
)
991 ASSERT(!propertyTable());
994 propertyTable().set(vm
, this, PropertyTable::create(vm
, capacity
));
997 void Structure::getPropertyNamesFromStructure(VM
& vm
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
999 DeferGC
deferGC(vm
.heap
);
1000 materializePropertyMapIfNecessary(vm
, deferGC
);
1001 if (!propertyTable())
1004 bool knownUnique
= !propertyNames
.size();
1006 PropertyTable::iterator end
= propertyTable()->end();
1007 for (PropertyTable::iterator iter
= propertyTable()->begin(); iter
!= end
; ++iter
) {
1008 ASSERT(m_hasNonEnumerableProperties
|| !(iter
->attributes
& DontEnum
));
1009 if (!iter
->key
->isEmptyUnique() && (!(iter
->attributes
& DontEnum
) || mode
== IncludeDontEnumProperties
)) {
1011 propertyNames
.addKnownUnique(iter
->key
);
1013 propertyNames
.add(iter
->key
);
1018 JSValue
Structure::prototypeForLookup(CodeBlock
* codeBlock
) const
1020 return prototypeForLookup(codeBlock
->globalObject());
1023 void Structure::visitChildren(JSCell
* cell
, SlotVisitor
& visitor
)
1025 Structure
* thisObject
= jsCast
<Structure
*>(cell
);
1026 ASSERT_GC_OBJECT_INHERITS(thisObject
, info());
1027 ASSERT(thisObject
->structure()->typeInfo().overridesVisitChildren());
1029 JSCell::visitChildren(thisObject
, visitor
);
1030 visitor
.append(&thisObject
->m_globalObject
);
1031 if (!thisObject
->isObject())
1032 thisObject
->m_cachedPrototypeChain
.clear();
1034 visitor
.append(&thisObject
->m_prototype
);
1035 visitor
.append(&thisObject
->m_cachedPrototypeChain
);
1037 visitor
.append(&thisObject
->m_previousOrRareData
);
1038 visitor
.append(&thisObject
->m_specificValueInPrevious
);
1040 if (thisObject
->m_isPinnedPropertyTable
) {
1041 ASSERT(thisObject
->m_propertyTableUnsafe
);
1042 visitor
.append(&thisObject
->m_propertyTableUnsafe
);
1043 } else if (thisObject
->m_propertyTableUnsafe
)
1044 thisObject
->m_propertyTableUnsafe
.clear();
1047 bool Structure::prototypeChainMayInterceptStoreTo(VM
& vm
, PropertyName propertyName
)
1049 unsigned i
= propertyName
.asIndex();
1050 if (i
!= PropertyName::NotAnIndex
)
1051 return anyObjectInChainMayInterceptIndexedAccesses();
1053 for (Structure
* current
= this; ;) {
1054 JSValue prototype
= current
->storedPrototype();
1055 if (prototype
.isNull())
1058 current
= prototype
.asCell()->structure(vm
);
1060 unsigned attributes
;
1061 JSCell
* specificValue
;
1062 PropertyOffset offset
= current
->get(vm
, propertyName
, attributes
, specificValue
);
1063 if (!JSC::isValidOffset(offset
))
1066 if (attributes
& (ReadOnly
| Accessor
))
1073 void Structure::dump(PrintStream
& out
) const
1075 out
.print(RawPointer(this), ":[", classInfo()->className
, ", {");
1077 Vector
<Structure
*, 8> structures
;
1078 Structure
* structure
;
1079 PropertyTable
* table
;
1081 const_cast<Structure
*>(this)->findStructuresAndMapForMaterialization(
1082 structures
, structure
, table
);
1087 PropertyTable::iterator iter
= table
->begin();
1088 PropertyTable::iterator end
= table
->end();
1089 for (; iter
!= end
; ++iter
) {
1090 out
.print(comma
, iter
->key
, ":", static_cast<int>(iter
->offset
));
1091 if (iter
->specificValue
) {
1092 DumpContext dummyContext
;
1093 out
.print("=>", RawPointer(iter
->specificValue
.get()));
1097 structure
->m_lock
.unlock();
1100 for (unsigned i
= structures
.size(); i
--;) {
1101 Structure
* structure
= structures
[i
];
1102 if (!structure
->m_nameInPrevious
)
1104 out
.print(comma
, structure
->m_nameInPrevious
.get(), ":", static_cast<int>(structure
->m_offset
));
1105 if (structure
->m_specificValueInPrevious
) {
1106 DumpContext dummyContext
;
1107 out
.print("=>", RawPointer(structure
->m_specificValueInPrevious
.get()));
1111 out
.print("}, ", IndexingTypeDump(indexingType()));
1113 if (m_prototype
.get().isCell())
1114 out
.print(", Proto:", RawPointer(m_prototype
.get().asCell()));
1119 void Structure::dumpInContext(PrintStream
& out
, DumpContext
* context
) const
1122 context
->structures
.dumpBrief(this, out
);
1127 void Structure::dumpBrief(PrintStream
& out
, const CString
& string
) const
1129 out
.print("%", string
, ":", classInfo()->className
);
1132 void Structure::dumpContextHeader(PrintStream
& out
)
1134 out
.print("Structures:");
1137 #if DO_PROPERTYMAP_CONSTENCY_CHECK
1139 void PropertyTable::checkConsistency()
1141 checkOffsetConsistency();
1142 ASSERT(m_indexSize
>= PropertyTable::MinimumTableSize
);
1143 ASSERT(m_indexMask
);
1144 ASSERT(m_indexSize
== m_indexMask
+ 1);
1145 ASSERT(!(m_indexSize
& m_indexMask
));
1147 ASSERT(m_keyCount
<= m_indexSize
/ 2);
1148 ASSERT(m_keyCount
+ m_deletedCount
<= m_indexSize
/ 2);
1149 ASSERT(m_deletedCount
<= m_indexSize
/ 4);
1151 unsigned indexCount
= 0;
1152 unsigned deletedIndexCount
= 0;
1153 for (unsigned a
= 0; a
!= m_indexSize
; ++a
) {
1154 unsigned entryIndex
= m_index
[a
];
1155 if (entryIndex
== PropertyTable::EmptyEntryIndex
)
1157 if (entryIndex
== deletedEntryIndex()) {
1158 ++deletedIndexCount
;
1161 ASSERT(entryIndex
< deletedEntryIndex());
1162 ASSERT(entryIndex
- 1 <= usedCount());
1165 for (unsigned b
= a
+ 1; b
!= m_indexSize
; ++b
)
1166 ASSERT(m_index
[b
] != entryIndex
);
1168 ASSERT(indexCount
== m_keyCount
);
1169 ASSERT(deletedIndexCount
== m_deletedCount
);
1171 ASSERT(!table()[deletedEntryIndex() - 1].key
);
1173 unsigned nonEmptyEntryCount
= 0;
1174 for (unsigned c
= 0; c
< usedCount(); ++c
) {
1175 StringImpl
* rep
= table()[c
].key
;
1176 if (rep
== PROPERTY_MAP_DELETED_ENTRY_KEY
)
1178 ++nonEmptyEntryCount
;
1179 unsigned i
= rep
->existingHash();
1181 unsigned entryIndex
;
1183 entryIndex
= m_index
[i
& m_indexMask
];
1184 ASSERT(entryIndex
!= PropertyTable::EmptyEntryIndex
);
1185 if (rep
== table()[entryIndex
- 1].key
)
1188 k
= 1 | doubleHash(rep
->existingHash());
1191 ASSERT(entryIndex
== c
+ 1);
1194 ASSERT(nonEmptyEntryCount
== m_keyCount
);
1197 void Structure::checkConsistency()
1199 if (!propertyTable())
1202 if (!m_hasNonEnumerableProperties
) {
1203 PropertyTable::iterator end
= propertyTable()->end();
1204 for (PropertyTable::iterator iter
= propertyTable()->begin(); iter
!= end
; ++iter
) {
1205 ASSERT(!(iter
->attributes
& DontEnum
));
1209 propertyTable()->checkConsistency();
1212 #endif // DO_PROPERTYMAP_CONSTENCY_CHECK
1214 bool ClassInfo::hasStaticSetterOrReadonlyProperties(VM
& vm
) const
1216 for (const ClassInfo
* ci
= this; ci
; ci
= ci
->parentClass
) {
1217 if (const HashTable
* table
= ci
->propHashTable(vm
)) {
1218 if (table
->hasSetterOrReadonlyProperties
)