86d0b6fbdaece3832cc362fb834098dbcee083a4
[WebKit-https.git] / Source / JavaScriptCore / runtime / Structure.cpp
1 /*
2  * Copyright (C) 2008, 2009, 2013, 2014 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
12  *
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. 
24  */
25
26 #include "config.h"
27 #include "Structure.h"
28
29 #include "CodeBlock.h"
30 #include "DumpContext.h"
31 #include "JSCInlines.h"
32 #include "JSObject.h"
33 #include "JSPropertyNameEnumerator.h"
34 #include "Lookup.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>
44
45 #define DUMP_STRUCTURE_ID_STATISTICS 0
46
47 #ifndef NDEBUG
48 #define DO_PROPERTYMAP_CONSTENCY_CHECK 0
49 #else
50 #define DO_PROPERTYMAP_CONSTENCY_CHECK 0
51 #endif
52
53 using namespace std;
54 using namespace WTF;
55
56 namespace JSC {
57
58 #if DUMP_STRUCTURE_ID_STATISTICS
59 static HashSet<Structure*>& liveStructureSet = *(new HashSet<Structure*>);
60 #endif
61
62 bool StructureTransitionTable::contains(AtomicStringImpl* rep, unsigned attributes) const
63 {
64     if (isUsingSingleSlot()) {
65         Structure* transition = singleTransition();
66         return transition && transition->m_nameInPrevious == rep && transition->attributesInPrevious() == attributes;
67     }
68     return map()->get(std::make_pair(rep, attributes));
69 }
70
71 inline Structure* StructureTransitionTable::get(AtomicStringImpl* rep, unsigned attributes) const
72 {
73     if (isUsingSingleSlot()) {
74         Structure* transition = singleTransition();
75         return (transition && transition->m_nameInPrevious == rep && transition->attributesInPrevious() == attributes) ? transition : 0;
76     }
77     return map()->get(std::make_pair(rep, attributes));
78 }
79
80 inline void StructureTransitionTable::add(VM& vm, Structure* structure)
81 {
82     if (isUsingSingleSlot()) {
83         Structure* existingTransition = singleTransition();
84
85         // This handles the first transition being added.
86         if (!existingTransition) {
87             setSingleTransition(vm, structure);
88             return;
89         }
90
91         // This handles the second transition being added
92         // (or the first transition being despecified!)
93         setMap(new TransitionMap());
94         add(vm, existingTransition);
95     }
96
97     // Add the structure to the map.
98
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->attributesInPrevious()), structure);
103 }
104
105 void Structure::dumpStatistics()
106 {
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;
113
114     HashSet<Structure*>::const_iterator end = liveStructureSet.end();
115     for (HashSet<Structure*>::const_iterator it = liveStructureSet.begin(); it != end; ++it) {
116         Structure* structure = *it;
117
118         switch (structure->m_transitionTable.size()) {
119             case 0:
120                 ++numberLeaf;
121                 if (!structure->previousID())
122                     ++numberSingletons;
123                 break;
124
125             case 1:
126                 ++numberUsingSingleSlot;
127                 break;
128         }
129
130         if (structure->propertyTable()) {
131             ++numberWithPropertyMaps;
132             totalPropertyMapsSize += structure->propertyTable()->sizeInMemory();
133         }
134     }
135
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);
141
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()));
145 #else
146     dataLogF("Dumping Structure statistics is not enabled.\n");
147 #endif
148 }
149
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_bitField(0)
161 {
162     setDictionaryKind(NoneDictionaryKind);
163     setIsPinnedPropertyTable(false);
164     setHasGetterSetterProperties(classInfo->hasStaticSetterOrReadonlyProperties());
165     setHasCustomGetterSetterProperties(false);
166     setHasReadOnlyOrGetterSetterPropertiesExcludingProto(classInfo->hasStaticSetterOrReadonlyProperties());
167     setHasNonEnumerableProperties(false);
168     setAttributesInPrevious(0);
169     setPreventExtensions(false);
170     setDidTransition(false);
171     setStaticFunctionsReified(false);
172     setHasRareData(false);
173  
174     ASSERT(inlineCapacity <= JSFinalObject::maxInlineCapacity());
175     ASSERT(static_cast<PropertyOffset>(inlineCapacity) < firstOutOfLineOffset);
176     ASSERT(!hasRareData());
177     ASSERT(hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !m_classInfo->hasStaticSetterOrReadonlyProperties());
178     ASSERT(hasGetterSetterProperties() || !m_classInfo->hasStaticSetterOrReadonlyProperties());
179 }
180
181 const ClassInfo Structure::s_info = { "Structure", 0, 0, CREATE_METHOD_TABLE(Structure) };
182
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_bitField(0)
191 {
192     setDictionaryKind(NoneDictionaryKind);
193     setIsPinnedPropertyTable(false);
194     setHasGetterSetterProperties(m_classInfo->hasStaticSetterOrReadonlyProperties());
195     setHasCustomGetterSetterProperties(false);
196     setHasReadOnlyOrGetterSetterPropertiesExcludingProto(m_classInfo->hasStaticSetterOrReadonlyProperties());
197     setHasNonEnumerableProperties(false);
198     setAttributesInPrevious(0);
199     setPreventExtensions(false);
200     setDidTransition(false);
201     setStaticFunctionsReified(false);
202     setHasRareData(false);
203  
204     TypeInfo typeInfo = TypeInfo(CellType, StructureIsImmortal);
205     m_blob = StructureIDBlob(vm.heap.structureIDTable().allocateID(this), 0, typeInfo);
206     m_outOfLineTypeFlags = typeInfo.outOfLineTypeFlags();
207
208     ASSERT(hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !m_classInfo->hasStaticSetterOrReadonlyProperties());
209     ASSERT(hasGetterSetterProperties() || !m_classInfo->hasStaticSetterOrReadonlyProperties());
210 }
211
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_bitField(0)
220 {
221     setDictionaryKind(previous->dictionaryKind());
222     setIsPinnedPropertyTable(previous->hasBeenFlattenedBefore());
223     setHasGetterSetterProperties(previous->hasGetterSetterProperties());
224     setHasCustomGetterSetterProperties(previous->hasCustomGetterSetterProperties());
225     setHasReadOnlyOrGetterSetterPropertiesExcludingProto(previous->hasReadOnlyOrGetterSetterPropertiesExcludingProto());
226     setHasNonEnumerableProperties(previous->hasNonEnumerableProperties());
227     setAttributesInPrevious(0);
228     setPreventExtensions(previous->preventExtensions());
229     setDidTransition(true);
230     setStaticFunctionsReified(previous->staticFunctionsReified());
231     setHasRareData(false);
232  
233     TypeInfo typeInfo = previous->typeInfo();
234     m_blob = StructureIDBlob(vm.heap.structureIDTable().allocateID(this), previous->indexingTypeIncludingHistory(), typeInfo);
235     m_outOfLineTypeFlags = typeInfo.outOfLineTypeFlags();
236
237     ASSERT(!previous->typeInfo().structureIsImmortal());
238     setPreviousID(vm, previous);
239
240     previous->didTransitionFromThisStructure();
241     if (previous->m_globalObject)
242         m_globalObject.set(vm, this, previous->m_globalObject.get());
243     ASSERT(hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !m_classInfo->hasStaticSetterOrReadonlyProperties());
244     ASSERT(hasGetterSetterProperties() || !m_classInfo->hasStaticSetterOrReadonlyProperties());
245 }
246
247 Structure::~Structure()
248 {
249     if (typeInfo().structureIsImmortal())
250         return;
251     Heap::heap(this)->structureIDTable().deallocateID(this, m_blob.structureID());
252 }
253
254 void Structure::destroy(JSCell* cell)
255 {
256     static_cast<Structure*>(cell)->Structure::~Structure();
257 }
258
259 void Structure::findStructuresAndMapForMaterialization(Vector<Structure*, 8>& structures, Structure*& structure, PropertyTable*& table)
260 {
261     ASSERT(structures.isEmpty());
262     table = 0;
263
264     for (structure = this; structure; structure = structure->previousID()) {
265         structure->m_lock.lock();
266         
267         table = structure->propertyTable().get();
268         if (table) {
269             // Leave the structure locked, so that the caller can do things to it atomically
270             // before it loses its property table.
271             return;
272         }
273         
274         structures.append(structure);
275         structure->m_lock.unlock();
276     }
277     
278     ASSERT(!structure);
279     ASSERT(!table);
280 }
281
282 void Structure::materializePropertyMap(VM& vm)
283 {
284     ASSERT(structure()->classInfo() == info());
285     ASSERT(!propertyTable());
286
287     Vector<Structure*, 8> structures;
288     Structure* structure;
289     PropertyTable* table;
290     
291     findStructuresAndMapForMaterialization(structures, structure, table);
292     
293     if (table) {
294         table = table->copy(vm, numberOfSlotsForLastOffset(m_offset, m_inlineCapacity));
295         structure->m_lock.unlock();
296     }
297     
298     // Must hold the lock on this structure, since we will be modifying this structure's
299     // property map. We don't want getConcurrently() to see the property map in a half-baked
300     // state.
301     GCSafeConcurrentJITLocker locker(m_lock, vm.heap);
302     if (!table)
303         createPropertyMap(locker, vm, numberOfSlotsForLastOffset(m_offset, m_inlineCapacity));
304     else
305         propertyTable().set(vm, this, table);
306
307     for (size_t i = structures.size(); i--;) {
308         structure = structures[i];
309         if (!structure->m_nameInPrevious)
310             continue;
311         PropertyMapEntry entry(structure->m_nameInPrevious.get(), structure->m_offset, structure->attributesInPrevious());
312         propertyTable()->add(entry, m_offset, PropertyTable::PropertyOffsetMustNotChange);
313     }
314     
315     checkOffsetConsistency();
316 }
317
318 Structure* Structure::addPropertyTransitionToExistingStructureImpl(Structure* structure, AtomicStringImpl* uid, unsigned attributes, PropertyOffset& offset)
319 {
320     ASSERT(!structure->isDictionary());
321     ASSERT(structure->isObject());
322
323     if (Structure* existingTransition = structure->m_transitionTable.get(uid, attributes)) {
324         validateOffset(existingTransition->m_offset, existingTransition->inlineCapacity());
325         offset = existingTransition->m_offset;
326         return existingTransition;
327     }
328
329     return 0;
330 }
331
332 Structure* Structure::addPropertyTransitionToExistingStructure(Structure* structure, PropertyName propertyName, unsigned attributes, PropertyOffset& offset)
333 {
334     ASSERT(!isCompilationThread());
335     return addPropertyTransitionToExistingStructureImpl(structure, propertyName.uid(), attributes, offset);
336 }
337
338 Structure* Structure::addPropertyTransitionToExistingStructureConcurrently(Structure* structure, AtomicStringImpl* uid, unsigned attributes, PropertyOffset& offset)
339 {
340     ConcurrentJITLocker locker(structure->m_lock);
341     return addPropertyTransitionToExistingStructureImpl(structure, uid, attributes, offset);
342 }
343
344 bool Structure::anyObjectInChainMayInterceptIndexedAccesses() const
345 {
346     for (const Structure* current = this; ;) {
347         if (current->mayInterceptIndexedAccesses())
348             return true;
349         
350         JSValue prototype = current->storedPrototype();
351         if (prototype.isNull())
352             return false;
353         
354         current = asObject(prototype)->structure();
355     }
356 }
357
358 bool Structure::holesMustForwardToPrototype(VM& vm) const
359 {
360     if (this->mayInterceptIndexedAccesses())
361         return true;
362
363     JSValue prototype = this->storedPrototype();
364     if (!prototype.isObject())
365         return false;
366     JSObject* object = asObject(prototype);
367
368     while (true) {
369         Structure& structure = *object->structure(vm);
370         if (hasIndexedProperties(object->indexingType()) || structure.mayInterceptIndexedAccesses())
371             return true;
372         prototype = structure.storedPrototype();
373         if (!prototype.isObject())
374             return false;
375         object = asObject(prototype);
376     }
377
378     RELEASE_ASSERT_NOT_REACHED();
379     return false;
380 }
381
382 bool Structure::needsSlowPutIndexing() const
383 {
384     return anyObjectInChainMayInterceptIndexedAccesses()
385         || globalObject()->isHavingABadTime();
386 }
387
388 NonPropertyTransition Structure::suggestedArrayStorageTransition() const
389 {
390     if (needsSlowPutIndexing())
391         return AllocateSlowPutArrayStorage;
392     
393     return AllocateArrayStorage;
394 }
395
396 Structure* Structure::addPropertyTransition(VM& vm, Structure* structure, PropertyName propertyName, unsigned attributes, PropertyOffset& offset, PutPropertySlot::Context context)
397 {
398     ASSERT(!structure->isDictionary());
399     ASSERT(structure->isObject());
400     ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure, propertyName, attributes, offset));
401     
402     int maxTransitionLength;
403     if (context == PutPropertySlot::PutById)
404         maxTransitionLength = s_maxTransitionLengthForNonEvalPutById;
405     else
406         maxTransitionLength = s_maxTransitionLength;
407     if (structure->transitionCount() > maxTransitionLength) {
408         Structure* transition = toCacheableDictionaryTransition(vm, structure);
409         ASSERT(structure != transition);
410         offset = transition->add(vm, propertyName, attributes);
411         return transition;
412     }
413     
414     Structure* transition = create(vm, structure);
415
416     transition->m_cachedPrototypeChain.setMayBeNull(vm, transition, structure->m_cachedPrototypeChain.get());
417     transition->m_nameInPrevious = propertyName.uid();
418     transition->setAttributesInPrevious(attributes);
419     transition->propertyTable().set(vm, transition, structure->takePropertyTableOrCloneIfPinned(vm));
420     transition->m_offset = structure->m_offset;
421
422     offset = transition->add(vm, propertyName, attributes);
423
424     checkOffset(transition->m_offset, transition->inlineCapacity());
425     {
426         ConcurrentJITLocker locker(structure->m_lock);
427         structure->m_transitionTable.add(vm, transition);
428     }
429     transition->checkOffsetConsistency();
430     structure->checkOffsetConsistency();
431     return transition;
432 }
433
434 Structure* Structure::removePropertyTransition(VM& vm, Structure* structure, PropertyName propertyName, PropertyOffset& offset)
435 {
436     ASSERT(!structure->isUncacheableDictionary());
437
438     Structure* transition = toUncacheableDictionaryTransition(vm, structure);
439
440     offset = transition->remove(propertyName);
441
442     transition->checkOffsetConsistency();
443     return transition;
444 }
445
446 Structure* Structure::changePrototypeTransition(VM& vm, Structure* structure, JSValue prototype)
447 {
448     Structure* transition = create(vm, structure);
449
450     transition->m_prototype.set(vm, transition, prototype);
451
452     DeferGC deferGC(vm.heap);
453     structure->materializePropertyMapIfNecessary(vm, deferGC);
454     transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm));
455     transition->m_offset = structure->m_offset;
456     transition->pin();
457
458     transition->checkOffsetConsistency();
459     return transition;
460 }
461
462 Structure* Structure::attributeChangeTransition(VM& vm, Structure* structure, PropertyName propertyName, unsigned attributes)
463 {
464     DeferGC deferGC(vm.heap);
465     if (!structure->isUncacheableDictionary()) {
466         Structure* transition = create(vm, structure);
467
468         structure->materializePropertyMapIfNecessary(vm, deferGC);
469         transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm));
470         transition->m_offset = structure->m_offset;
471         transition->pin();
472         
473         structure = transition;
474     }
475
476     ASSERT(structure->propertyTable());
477     PropertyMapEntry* entry = structure->propertyTable()->get(propertyName.uid());
478     ASSERT(entry);
479     entry->attributes = attributes;
480
481     structure->checkOffsetConsistency();
482     return structure;
483 }
484
485 Structure* Structure::toDictionaryTransition(VM& vm, Structure* structure, DictionaryKind kind)
486 {
487     ASSERT(!structure->isUncacheableDictionary());
488     
489     Structure* transition = create(vm, structure);
490
491     DeferGC deferGC(vm.heap);
492     structure->materializePropertyMapIfNecessary(vm, deferGC);
493     transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm));
494     transition->m_offset = structure->m_offset;
495     transition->setDictionaryKind(kind);
496     transition->pin();
497
498     transition->checkOffsetConsistency();
499     return transition;
500 }
501
502 Structure* Structure::toCacheableDictionaryTransition(VM& vm, Structure* structure)
503 {
504     return toDictionaryTransition(vm, structure, CachedDictionaryKind);
505 }
506
507 Structure* Structure::toUncacheableDictionaryTransition(VM& vm, Structure* structure)
508 {
509     return toDictionaryTransition(vm, structure, UncachedDictionaryKind);
510 }
511
512 // In future we may want to cache this transition.
513 Structure* Structure::sealTransition(VM& vm, Structure* structure)
514 {
515     Structure* transition = preventExtensionsTransition(vm, structure);
516
517     if (transition->propertyTable()) {
518         PropertyTable::iterator end = transition->propertyTable()->end();
519         for (PropertyTable::iterator iter = transition->propertyTable()->begin(); iter != end; ++iter)
520             iter->attributes |= DontDelete;
521     }
522
523     transition->checkOffsetConsistency();
524     return transition;
525 }
526
527 // In future we may want to cache this transition.
528 Structure* Structure::freezeTransition(VM& vm, Structure* structure)
529 {
530     Structure* transition = preventExtensionsTransition(vm, structure);
531
532     if (transition->propertyTable()) {
533         PropertyTable::iterator iter = transition->propertyTable()->begin();
534         PropertyTable::iterator end = transition->propertyTable()->end();
535         if (iter != end)
536             transition->setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true);
537         for (; iter != end; ++iter)
538             iter->attributes |= iter->attributes & Accessor ? DontDelete : (DontDelete | ReadOnly);
539     }
540
541     ASSERT(transition->hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !transition->classInfo()->hasStaticSetterOrReadonlyProperties());
542     ASSERT(transition->hasGetterSetterProperties() || !transition->classInfo()->hasStaticSetterOrReadonlyProperties());
543     transition->checkOffsetConsistency();
544     return transition;
545 }
546
547 // In future we may want to cache this transition.
548 Structure* Structure::preventExtensionsTransition(VM& vm, Structure* structure)
549 {
550     Structure* transition = create(vm, structure);
551
552     // Don't set m_offset, as one can not transition to this.
553
554     DeferGC deferGC(vm.heap);
555     structure->materializePropertyMapIfNecessary(vm, deferGC);
556     transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm));
557     transition->m_offset = structure->m_offset;
558     transition->setPreventExtensions(true);
559     transition->pin();
560
561     transition->checkOffsetConsistency();
562     return transition;
563 }
564
565 PropertyTable* Structure::takePropertyTableOrCloneIfPinned(VM& vm)
566 {
567     DeferGC deferGC(vm.heap);
568     materializePropertyMapIfNecessaryForPinning(vm, deferGC);
569     
570     if (isPinnedPropertyTable())
571         return propertyTable()->copy(vm, propertyTable()->size() + 1);
572     
573     // Hold the lock while stealing the table - so that getConcurrently() on another thread
574     // will either have to bypass this structure, or will get to use the property table
575     // before it is stolen.
576     ConcurrentJITLocker locker(m_lock);
577     PropertyTable* takenPropertyTable = propertyTable().get();
578     propertyTable().clear();
579     return takenPropertyTable;
580 }
581
582 Structure* Structure::nonPropertyTransition(VM& vm, Structure* structure, NonPropertyTransition transitionKind)
583 {
584     unsigned attributes = toAttributes(transitionKind);
585     IndexingType indexingType = newIndexingType(structure->indexingTypeIncludingHistory(), transitionKind);
586     
587     if (JSGlobalObject* globalObject = structure->m_globalObject.get()) {
588         if (globalObject->isOriginalArrayStructure(structure)) {
589             Structure* result = globalObject->originalArrayStructureForIndexingType(indexingType);
590             if (result->indexingTypeIncludingHistory() == indexingType) {
591                 structure->didTransitionFromThisStructure();
592                 return result;
593             }
594         }
595     }
596     
597     Structure* existingTransition;
598     if (!structure->isDictionary() && (existingTransition = structure->m_transitionTable.get(0, attributes))) {
599         ASSERT(existingTransition->attributesInPrevious() == attributes);
600         ASSERT(existingTransition->indexingTypeIncludingHistory() == indexingType);
601         return existingTransition;
602     }
603     
604     Structure* transition = create(vm, structure);
605     transition->setAttributesInPrevious(attributes);
606     transition->m_blob.setIndexingType(indexingType);
607     transition->propertyTable().set(vm, transition, structure->takePropertyTableOrCloneIfPinned(vm));
608     transition->m_offset = structure->m_offset;
609     checkOffset(transition->m_offset, transition->inlineCapacity());
610     
611     if (structure->isDictionary())
612         transition->pin();
613     else {
614         ConcurrentJITLocker locker(structure->m_lock);
615         structure->m_transitionTable.add(vm, transition);
616     }
617     transition->checkOffsetConsistency();
618     return transition;
619 }
620
621 // In future we may want to cache this property.
622 bool Structure::isSealed(VM& vm)
623 {
624     if (isExtensible())
625         return false;
626
627     DeferGC deferGC(vm.heap);
628     materializePropertyMapIfNecessary(vm, deferGC);
629     if (!propertyTable())
630         return true;
631
632     PropertyTable::iterator end = propertyTable()->end();
633     for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter) {
634         if ((iter->attributes & DontDelete) != DontDelete)
635             return false;
636     }
637     return true;
638 }
639
640 // In future we may want to cache this property.
641 bool Structure::isFrozen(VM& vm)
642 {
643     if (isExtensible())
644         return false;
645
646     DeferGC deferGC(vm.heap);
647     materializePropertyMapIfNecessary(vm, deferGC);
648     if (!propertyTable())
649         return true;
650
651     PropertyTable::iterator end = propertyTable()->end();
652     for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter) {
653         if (!(iter->attributes & DontDelete))
654             return false;
655         if (!(iter->attributes & (ReadOnly | Accessor)))
656             return false;
657     }
658     return true;
659 }
660
661 Structure* Structure::flattenDictionaryStructure(VM& vm, JSObject* object)
662 {
663     checkOffsetConsistency();
664     ASSERT(isDictionary());
665
666     size_t beforeOutOfLineCapacity = this->outOfLineCapacity();
667     if (isUncacheableDictionary()) {
668         ASSERT(propertyTable());
669
670         size_t propertyCount = propertyTable()->size();
671
672         // Holds our values compacted by insertion order.
673         Vector<JSValue> values(propertyCount);
674
675         // Copies out our values from their hashed locations, compacting property table offsets as we go.
676         unsigned i = 0;
677         PropertyTable::iterator end = propertyTable()->end();
678         m_offset = invalidOffset;
679         for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter, ++i) {
680             values[i] = object->getDirect(iter->offset);
681             m_offset = iter->offset = offsetForPropertyNumber(i, m_inlineCapacity);
682         }
683         
684         // Copies in our values to their compacted locations.
685         for (unsigned i = 0; i < propertyCount; i++)
686             object->putDirect(vm, offsetForPropertyNumber(i, m_inlineCapacity), values[i]);
687
688         propertyTable()->clearDeletedOffsets();
689         checkOffsetConsistency();
690     }
691
692     setDictionaryKind(NoneDictionaryKind);
693     setHasBeenFlattenedBefore(true);
694
695     size_t afterOutOfLineCapacity = this->outOfLineCapacity();
696
697     if (beforeOutOfLineCapacity != afterOutOfLineCapacity) {
698         ASSERT(beforeOutOfLineCapacity > afterOutOfLineCapacity);
699         // If the object had a Butterfly but after flattening/compacting we no longer have need of it,
700         // we need to zero it out because the collector depends on the Structure to know the size for copying.
701         if (object->butterfly() && !afterOutOfLineCapacity && !this->hasIndexingHeader(object))
702             object->setStructureAndButterfly(vm, this, 0);
703         // If the object was down-sized to the point where the base of the Butterfly is no longer within the 
704         // first CopiedBlock::blockSize bytes, we'll get the wrong answer if we try to mask the base back to 
705         // the CopiedBlock header. To prevent this case we need to memmove the Butterfly down.
706         else if (object->butterfly())
707             object->shiftButterflyAfterFlattening(vm, beforeOutOfLineCapacity, afterOutOfLineCapacity);
708     }
709
710     return this;
711 }
712
713 PropertyOffset Structure::addPropertyWithoutTransition(VM& vm, PropertyName propertyName, unsigned attributes)
714 {
715     DeferGC deferGC(vm.heap);
716     materializePropertyMapIfNecessaryForPinning(vm, deferGC);
717     
718     pin();
719
720     return add(vm, propertyName, attributes);
721 }
722
723 PropertyOffset Structure::removePropertyWithoutTransition(VM& vm, PropertyName propertyName)
724 {
725     ASSERT(isUncacheableDictionary());
726
727     DeferGC deferGC(vm.heap);
728     materializePropertyMapIfNecessaryForPinning(vm, deferGC);
729
730     pin();
731     return remove(propertyName);
732 }
733
734 void Structure::pin()
735 {
736     ASSERT(propertyTable());
737     setIsPinnedPropertyTable(true);
738     clearPreviousID();
739     m_nameInPrevious.clear();
740 }
741
742 void Structure::allocateRareData(VM& vm)
743 {
744     ASSERT(!hasRareData());
745     StructureRareData* rareData = StructureRareData::create(vm, previous());
746     WTF::storeStoreFence();
747     m_previousOrRareData.set(vm, this, rareData);
748     WTF::storeStoreFence();
749     setHasRareData(true);
750     ASSERT(hasRareData());
751 }
752
753 WatchpointSet* Structure::ensurePropertyReplacementWatchpointSet(VM& vm, PropertyOffset offset)
754 {
755     ASSERT(!isUncacheableDictionary());
756     
757     if (!hasRareData())
758         allocateRareData(vm);
759     ConcurrentJITLocker locker(m_lock);
760     StructureRareData* rareData = this->rareData();
761     if (!rareData->m_replacementWatchpointSets) {
762         rareData->m_replacementWatchpointSets =
763             std::make_unique<StructureRareData::PropertyWatchpointMap>();
764         WTF::storeStoreFence();
765     }
766     auto result = rareData->m_replacementWatchpointSets->add(offset, nullptr);
767     if (result.isNewEntry)
768         result.iterator->value = adoptRef(new WatchpointSet(IsWatched));
769     return result.iterator->value.get();
770 }
771
772 void Structure::startWatchingPropertyForReplacements(VM& vm, PropertyName propertyName)
773 {
774     ASSERT(!isUncacheableDictionary());
775     
776     PropertyOffset offset = get(vm, propertyName);
777     if (!JSC::isValidOffset(offset))
778         return;
779     
780     startWatchingPropertyForReplacements(vm, offset);
781 }
782
783 void Structure::didCachePropertyReplacement(VM& vm, PropertyOffset offset)
784 {
785     ensurePropertyReplacementWatchpointSet(vm, offset)->fireAll("Did cache property replacement");
786 }
787
788 void Structure::startWatchingInternalProperties(VM& vm)
789 {
790     if (!isUncacheableDictionary()) {
791         startWatchingPropertyForReplacements(vm, vm.propertyNames->toString);
792         startWatchingPropertyForReplacements(vm, vm.propertyNames->valueOf);
793     }
794     setDidWatchInternalProperties(true);
795 }
796
797 #if DUMP_PROPERTYMAP_STATS
798
799 PropertyMapHashTableStats* propertyMapHashTableStats = 0;
800
801 struct PropertyMapStatisticsExitLogger {
802     PropertyMapStatisticsExitLogger();
803     ~PropertyMapStatisticsExitLogger();
804 };
805
806 DEFINE_GLOBAL_FOR_LOGGING(PropertyMapStatisticsExitLogger, logger, );
807
808 PropertyMapStatisticsExitLogger::PropertyMapStatisticsExitLogger()
809 {
810     propertyMapHashTableStats = adoptPtr(new PropertyMapHashTableStats()).leakPtr();
811 }
812
813 PropertyMapStatisticsExitLogger::~PropertyMapStatisticsExitLogger()
814 {
815     unsigned finds = propertyMapHashTableStats->numFinds;
816     unsigned collisions = propertyMapHashTableStats->numCollisions;
817     dataLogF("\nJSC::PropertyMap statistics for process %d\n\n", getCurrentProcessID());
818     dataLogF("%d finds\n", finds);
819     dataLogF("%d collisions (%.1f%%)\n", collisions, 100.0 * collisions / finds);
820     dataLogF("%d lookups\n", propertyMapHashTableStats->numLookups.load());
821     dataLogF("%d lookup probings\n", propertyMapHashTableStats->numLookupProbing.load());
822     dataLogF("%d adds\n", propertyMapHashTableStats->numAdds.load());
823     dataLogF("%d removes\n", propertyMapHashTableStats->numRemoves.load());
824     dataLogF("%d rehashes\n", propertyMapHashTableStats->numRehashes.load());
825     dataLogF("%d reinserts\n", propertyMapHashTableStats->numReinserts.load());
826 }
827
828 #endif
829
830 #if !DO_PROPERTYMAP_CONSTENCY_CHECK
831
832 inline void Structure::checkConsistency()
833 {
834     checkOffsetConsistency();
835 }
836
837 #endif
838
839 PropertyTable* Structure::copyPropertyTable(VM& vm)
840 {
841     if (!propertyTable())
842         return 0;
843     return PropertyTable::clone(vm, *propertyTable().get());
844 }
845
846 PropertyTable* Structure::copyPropertyTableForPinning(VM& vm)
847 {
848     if (propertyTable())
849         return PropertyTable::clone(vm, *propertyTable().get());
850     return PropertyTable::create(vm, numberOfSlotsForLastOffset(m_offset, m_inlineCapacity));
851 }
852
853 PropertyOffset Structure::getConcurrently(AtomicStringImpl* uid, unsigned& attributes)
854 {
855     PropertyOffset result = invalidOffset;
856     
857     forEachPropertyConcurrently(
858         [&] (const PropertyMapEntry& candidate) -> bool {
859             if (candidate.key != uid)
860                 return true;
861             
862             result = candidate.offset;
863             attributes = candidate.attributes;
864             return false;
865         });
866     
867     return result;
868 }
869
870 Vector<PropertyMapEntry> Structure::getPropertiesConcurrently()
871 {
872     Vector<PropertyMapEntry> result;
873
874     forEachPropertyConcurrently(
875         [&] (const PropertyMapEntry& entry) -> bool {
876             result.append(entry);
877             return true;
878         });
879     
880     return result;
881 }
882
883 PropertyOffset Structure::add(VM& vm, PropertyName propertyName, unsigned attributes)
884 {
885     GCSafeConcurrentJITLocker locker(m_lock, vm.heap);
886     
887     ASSERT(!JSC::isValidOffset(get(vm, propertyName)));
888
889     checkConsistency();
890     if (attributes & DontEnum)
891         setHasNonEnumerableProperties(true);
892
893     AtomicStringImpl* rep = propertyName.uid();
894
895     if (!propertyTable())
896         createPropertyMap(locker, vm);
897
898     PropertyOffset newOffset = propertyTable()->nextOffset(m_inlineCapacity);
899
900     propertyTable()->add(PropertyMapEntry(rep, newOffset, attributes), m_offset, PropertyTable::PropertyOffsetMayChange);
901     
902     checkConsistency();
903     return newOffset;
904 }
905
906 PropertyOffset Structure::remove(PropertyName propertyName)
907 {
908     ConcurrentJITLocker locker(m_lock);
909     
910     checkConsistency();
911
912     AtomicStringImpl* rep = propertyName.uid();
913
914     if (!propertyTable())
915         return invalidOffset;
916
917     PropertyTable::find_iterator position = propertyTable()->find(rep);
918     if (!position.first)
919         return invalidOffset;
920
921     PropertyOffset offset = position.first->offset;
922
923     propertyTable()->remove(position);
924     propertyTable()->addDeletedOffset(offset);
925
926     checkConsistency();
927     return offset;
928 }
929
930 void Structure::createPropertyMap(const GCSafeConcurrentJITLocker&, VM& vm, unsigned capacity)
931 {
932     ASSERT(!propertyTable());
933
934     checkConsistency();
935     propertyTable().set(vm, this, PropertyTable::create(vm, capacity));
936 }
937
938 void Structure::getPropertyNamesFromStructure(VM& vm, PropertyNameArray& propertyNames, EnumerationMode mode)
939 {
940     DeferGC deferGC(vm.heap);
941     materializePropertyMapIfNecessary(vm, deferGC);
942     if (!propertyTable())
943         return;
944
945     bool knownUnique = propertyNames.canAddKnownUniqueForStructure();
946
947     PropertyTable::iterator end = propertyTable()->end();
948     for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter) {
949         ASSERT(hasNonEnumerableProperties() || !(iter->attributes & DontEnum));
950         if (!iter->key->isEmptyUnique() && (!(iter->attributes & DontEnum) || shouldIncludeDontEnumProperties(mode))) {
951             if (knownUnique)
952                 propertyNames.addKnownUnique(iter->key);
953             else
954                 propertyNames.add(iter->key);
955         }
956     }
957 }
958
959 namespace {
960
961 class StructureFireDetail : public FireDetail {
962 public:
963     StructureFireDetail(const Structure* structure)
964         : m_structure(structure)
965     {
966     }
967     
968     virtual void dump(PrintStream& out) const override
969     {
970         out.print("Structure transition from ", *m_structure);
971     }
972
973 private:
974     const Structure* m_structure;
975 };
976
977 } // anonymous namespace
978
979 void Structure::didTransitionFromThisStructure() const
980 {
981     m_transitionWatchpointSet.fireAll(StructureFireDetail(this));
982 }
983
984 JSValue Structure::prototypeForLookup(CodeBlock* codeBlock) const
985 {
986     return prototypeForLookup(codeBlock->globalObject());
987 }
988
989 void Structure::visitChildren(JSCell* cell, SlotVisitor& visitor)
990 {
991     Structure* thisObject = jsCast<Structure*>(cell);
992     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
993
994     JSCell::visitChildren(thisObject, visitor);
995     visitor.append(&thisObject->m_globalObject);
996     if (!thisObject->isObject())
997         thisObject->m_cachedPrototypeChain.clear();
998     else {
999         visitor.append(&thisObject->m_prototype);
1000         visitor.append(&thisObject->m_cachedPrototypeChain);
1001     }
1002     visitor.append(&thisObject->m_previousOrRareData);
1003
1004     if (thisObject->isPinnedPropertyTable()) {
1005         ASSERT(thisObject->m_propertyTableUnsafe);
1006         visitor.append(&thisObject->m_propertyTableUnsafe);
1007     } else if (thisObject->m_propertyTableUnsafe)
1008         thisObject->m_propertyTableUnsafe.clear();
1009 }
1010
1011 bool Structure::prototypeChainMayInterceptStoreTo(VM& vm, PropertyName propertyName)
1012 {
1013     unsigned i = propertyName.asIndex();
1014     if (i != PropertyName::NotAnIndex)
1015         return anyObjectInChainMayInterceptIndexedAccesses();
1016     
1017     for (Structure* current = this; ;) {
1018         JSValue prototype = current->storedPrototype();
1019         if (prototype.isNull())
1020             return false;
1021         
1022         current = prototype.asCell()->structure(vm);
1023         
1024         unsigned attributes;
1025         PropertyOffset offset = current->get(vm, propertyName, attributes);
1026         if (!JSC::isValidOffset(offset))
1027             continue;
1028         
1029         if (attributes & (ReadOnly | Accessor))
1030             return true;
1031         
1032         return false;
1033     }
1034 }
1035
1036 PassRefPtr<StructureShape> Structure::toStructureShape(JSValue value)
1037 {
1038     RefPtr<StructureShape> baseShape = StructureShape::create();
1039     RefPtr<StructureShape> curShape = baseShape;
1040     Structure* curStructure = this;
1041     JSValue curValue = value;
1042     while (curStructure) {
1043         Vector<Structure*, 8> structures;
1044         Structure* structure;
1045         PropertyTable* table;
1046
1047         curStructure->findStructuresAndMapForMaterialization(structures, structure, table);
1048         if (table) {
1049             PropertyTable::iterator iter = table->begin();
1050             PropertyTable::iterator end = table->end();
1051             for (; iter != end; ++iter)
1052                 curShape->addProperty(iter->key);
1053             
1054             structure->m_lock.unlock();
1055         }
1056         for (unsigned i = structures.size(); i--;) {
1057             Structure* structure = structures[i];
1058             if (structure->m_nameInPrevious)
1059                 curShape->addProperty(structure->m_nameInPrevious.get());
1060         }
1061
1062         bool foundCtorName = false;
1063         if (JSObject* profilingVal = curValue.getObject()) {
1064             ExecState* exec = profilingVal->globalObject()->globalExec();
1065             PropertySlot slot(storedPrototype());
1066             PropertyName constructor(exec->propertyNames().constructor);
1067             if (profilingVal->getPropertySlot(exec, constructor, slot)) {
1068                 if (slot.isValue()) {
1069                     JSValue constructorValue = slot.getValue(exec, constructor);
1070                     if (constructorValue.isCell()) {
1071                         if (JSCell* constructorCell = constructorValue.asCell()) {
1072                             if (JSObject* ctorObject = constructorCell->getObject()) {
1073                                 if (JSFunction* constructorFunction = jsDynamicCast<JSFunction*>(ctorObject)) {
1074                                     curShape->setConstructorName(constructorFunction->calculatedDisplayName(exec));
1075                                     foundCtorName = true;
1076                                 } else if (InternalFunction* constructorFunction = jsDynamicCast<InternalFunction*>(ctorObject)) {
1077                                     curShape->setConstructorName(constructorFunction->calculatedDisplayName(exec));
1078                                     foundCtorName = true;
1079                                 }
1080                             }
1081                         }
1082                     }
1083                 }
1084             }
1085         }
1086
1087         if (!foundCtorName)
1088             curShape->setConstructorName(curStructure->classInfo()->className);
1089
1090         if (curStructure->isDictionary())
1091             curShape->enterDictionaryMode();
1092
1093         curShape->markAsFinal();
1094
1095         if (curStructure->storedPrototypeStructure()) {
1096             RefPtr<StructureShape> newShape = StructureShape::create();
1097             curShape->setProto(newShape);
1098             curShape = newShape;
1099             curValue = curStructure->storedPrototype();
1100         }
1101
1102         curStructure = curStructure->storedPrototypeStructure();
1103     }
1104     
1105     return baseShape.release();
1106 }
1107
1108 bool Structure::canUseForAllocationsOf(Structure* other)
1109 {
1110     return inlineCapacity() == other->inlineCapacity()
1111         && storedPrototype() == other->storedPrototype()
1112         && objectInitializationBlob() == other->objectInitializationBlob();
1113 }
1114
1115 void Structure::dump(PrintStream& out) const
1116 {
1117     out.print(RawPointer(this), ":[", classInfo()->className, ", {");
1118     
1119     CommaPrinter comma;
1120     
1121     const_cast<Structure*>(this)->forEachPropertyConcurrently(
1122         [&] (const PropertyMapEntry& entry) -> bool {
1123             out.print(comma, entry.key, ":", static_cast<int>(entry.offset));
1124             return true;
1125         });
1126     
1127     out.print("}, ", IndexingTypeDump(indexingType()));
1128     
1129     if (m_prototype.get().isCell())
1130         out.print(", Proto:", RawPointer(m_prototype.get().asCell()));
1131     
1132     out.print("]");
1133 }
1134
1135 void Structure::dumpInContext(PrintStream& out, DumpContext* context) const
1136 {
1137     if (context)
1138         context->structures.dumpBrief(this, out);
1139     else
1140         dump(out);
1141 }
1142
1143 void Structure::dumpBrief(PrintStream& out, const CString& string) const
1144 {
1145     out.print("%", string, ":", classInfo()->className);
1146 }
1147
1148 void Structure::dumpContextHeader(PrintStream& out)
1149 {
1150     out.print("Structures:");
1151 }
1152
1153 #if DO_PROPERTYMAP_CONSTENCY_CHECK
1154
1155 void PropertyTable::checkConsistency()
1156 {
1157     checkOffsetConsistency();
1158     ASSERT(m_indexSize >= PropertyTable::MinimumTableSize);
1159     ASSERT(m_indexMask);
1160     ASSERT(m_indexSize == m_indexMask + 1);
1161     ASSERT(!(m_indexSize & m_indexMask));
1162
1163     ASSERT(m_keyCount <= m_indexSize / 2);
1164     ASSERT(m_keyCount + m_deletedCount <= m_indexSize / 2);
1165     ASSERT(m_deletedCount <= m_indexSize / 4);
1166
1167     unsigned indexCount = 0;
1168     unsigned deletedIndexCount = 0;
1169     for (unsigned a = 0; a != m_indexSize; ++a) {
1170         unsigned entryIndex = m_index[a];
1171         if (entryIndex == PropertyTable::EmptyEntryIndex)
1172             continue;
1173         if (entryIndex == deletedEntryIndex()) {
1174             ++deletedIndexCount;
1175             continue;
1176         }
1177         ASSERT(entryIndex < deletedEntryIndex());
1178         ASSERT(entryIndex - 1 <= usedCount());
1179         ++indexCount;
1180
1181         for (unsigned b = a + 1; b != m_indexSize; ++b)
1182             ASSERT(m_index[b] != entryIndex);
1183     }
1184     ASSERT(indexCount == m_keyCount);
1185     ASSERT(deletedIndexCount == m_deletedCount);
1186
1187     ASSERT(!table()[deletedEntryIndex() - 1].key);
1188
1189     unsigned nonEmptyEntryCount = 0;
1190     for (unsigned c = 0; c < usedCount(); ++c) {
1191         StringImpl* rep = table()[c].key;
1192         if (rep == PROPERTY_MAP_DELETED_ENTRY_KEY)
1193             continue;
1194         ++nonEmptyEntryCount;
1195         unsigned i = rep->existingHash();
1196         unsigned k = 0;
1197         unsigned entryIndex;
1198         while (1) {
1199             entryIndex = m_index[i & m_indexMask];
1200             ASSERT(entryIndex != PropertyTable::EmptyEntryIndex);
1201             if (rep == table()[entryIndex - 1].key)
1202                 break;
1203             if (k == 0)
1204                 k = 1 | doubleHash(rep->existingHash());
1205             i += k;
1206         }
1207         ASSERT(entryIndex == c + 1);
1208     }
1209
1210     ASSERT(nonEmptyEntryCount == m_keyCount);
1211 }
1212
1213 void Structure::checkConsistency()
1214 {
1215     if (!propertyTable())
1216         return;
1217
1218     if (!hasNonEnumerableProperties()) {
1219         PropertyTable::iterator end = propertyTable()->end();
1220         for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter) {
1221             ASSERT(!(iter->attributes & DontEnum));
1222         }
1223     }
1224
1225     propertyTable()->checkConsistency();
1226 }
1227
1228 #endif // DO_PROPERTYMAP_CONSTENCY_CHECK
1229
1230 bool ClassInfo::hasStaticSetterOrReadonlyProperties() const
1231 {
1232     for (const ClassInfo* ci = this; ci; ci = ci->parentClass) {
1233         if (const HashTable* table = ci->staticPropHashTable) {
1234             if (table->hasSetterOrReadonlyProperties)
1235                 return true;
1236         }
1237     }
1238     return false;
1239 }
1240
1241 void Structure::setCachedStructurePropertyNameEnumerator(VM& vm, JSPropertyNameEnumerator* enumerator)
1242 {
1243     ASSERT(!isDictionary());
1244     if (!hasRareData())
1245         allocateRareData(vm);
1246     rareData()->setCachedStructurePropertyNameEnumerator(vm, enumerator);
1247 }
1248
1249 JSPropertyNameEnumerator* Structure::cachedStructurePropertyNameEnumerator() const
1250 {
1251     if (!hasRareData())
1252         return nullptr;
1253     return rareData()->cachedStructurePropertyNameEnumerator();
1254 }
1255
1256 void Structure::setCachedGenericPropertyNameEnumerator(VM& vm, JSPropertyNameEnumerator* enumerator)
1257 {
1258     ASSERT(!isDictionary());
1259     if (!hasRareData())
1260         allocateRareData(vm);
1261     rareData()->setCachedGenericPropertyNameEnumerator(vm, enumerator);
1262 }
1263
1264 JSPropertyNameEnumerator* Structure::cachedGenericPropertyNameEnumerator() const
1265 {
1266     if (!hasRareData())
1267         return nullptr;
1268     return rareData()->cachedGenericPropertyNameEnumerator();
1269 }
1270
1271 bool Structure::canCacheStructurePropertyNameEnumerator() const
1272 {
1273     if (isDictionary())
1274         return false;
1275     return true;
1276 }
1277
1278 bool Structure::canCacheGenericPropertyNameEnumerator() const
1279 {
1280     if (!canCacheStructurePropertyNameEnumerator())
1281         return false;
1282
1283     if (hasIndexedProperties(indexingType()))
1284         return false;
1285
1286     if (typeInfo().overridesGetPropertyNames())
1287         return false;
1288
1289     StructureChain* structureChain = m_cachedPrototypeChain.get();
1290     ASSERT(structureChain);
1291     WriteBarrier<Structure>* structure = structureChain->head();
1292     while (true) {
1293         if (!structure->get())
1294             break;
1295         if (structure->get()->typeInfo().overridesGetPropertyNames())
1296             return false;
1297         structure++;
1298     }
1299
1300     return true;
1301 }
1302
1303 bool Structure::canAccessPropertiesQuickly() const
1304 {
1305     if (hasNonEnumerableProperties())
1306         return false;
1307     if (hasGetterSetterProperties())
1308         return false;
1309     if (isUncacheableDictionary())
1310         return false;
1311     return true;
1312 }
1313
1314 } // namespace JSC