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