Try ripping out inferred types because it might be a performance improvement
[WebKit-https.git] / Source / JavaScriptCore / runtime / Structure.h
1 /*
2  * Copyright (C) 2008-2018 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 #pragma once
27
28 #include "ClassInfo.h"
29 #include "ConcurrentJSLock.h"
30 #include "IndexingType.h"
31 #include "JSCJSValue.h"
32 #include "JSCast.h"
33 #include "JSType.h"
34 #include "PropertyName.h"
35 #include "PropertyNameArray.h"
36 #include "PropertyOffset.h"
37 #include "PutPropertySlot.h"
38 #include "StructureIDBlob.h"
39 #include "StructureRareData.h"
40 #include "StructureTransitionTable.h"
41 #include "JSTypeInfo.h"
42 #include "Watchpoint.h"
43 #include "WriteBarrierInlines.h"
44 #include <wtf/PrintStream.h>
45
46 namespace WTF {
47
48 class UniquedStringImpl;
49
50 } // namespace WTF
51
52 namespace JSC {
53
54 class DeferGC;
55 class LLIntOffsetsExtractor;
56 class PropertyNameArray;
57 class PropertyNameArrayData;
58 class PropertyTable;
59 class StructureChain;
60 class StructureShape;
61 class SlotVisitor;
62 class JSString;
63 struct DumpContext;
64
65 // The out-of-line property storage capacity to use when first allocating out-of-line
66 // storage. Note that all objects start out without having any out-of-line storage;
67 // this comes into play only on the first property store that exhausts inline storage.
68 static const unsigned initialOutOfLineCapacity = 4;
69
70 // The factor by which to grow out-of-line storage when it is exhausted, after the
71 // initial allocation.
72 static const unsigned outOfLineGrowthFactor = 2;
73
74 struct PropertyMapEntry {
75     UniquedStringImpl* key;
76     PropertyOffset offset;
77     uint8_t attributes;
78
79     PropertyMapEntry()
80         : key(nullptr)
81         , offset(invalidOffset)
82         , attributes(0)
83     {
84     }
85     
86     PropertyMapEntry(UniquedStringImpl* key, PropertyOffset offset, unsigned attributes)
87         : key(key)
88         , offset(offset)
89         , attributes(attributes)
90     {
91         ASSERT(this->attributes == attributes);
92     }
93 };
94
95 class StructureFireDetail : public FireDetail {
96 public:
97     StructureFireDetail(const Structure* structure)
98         : m_structure(structure)
99     {
100     }
101     
102     void dump(PrintStream& out) const override;
103
104 private:
105     const Structure* m_structure;
106 };
107
108 class DeferredStructureTransitionWatchpointFire : public DeferredWatchpointFire {
109     WTF_MAKE_NONCOPYABLE(DeferredStructureTransitionWatchpointFire);
110 public:
111     JS_EXPORT_PRIVATE DeferredStructureTransitionWatchpointFire(VM&, Structure*);
112     JS_EXPORT_PRIVATE ~DeferredStructureTransitionWatchpointFire();
113     
114     void dump(PrintStream& out) const override;
115
116     const Structure* structure() const { return m_structure; }
117
118 private:
119     const Structure* m_structure;
120 };
121
122 class Structure final : public JSCell {
123 public:
124     friend class StructureTransitionTable;
125
126     typedef JSCell Base;
127     static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal;
128     
129     enum PolyProtoTag { PolyProto };
130     static Structure* create(VM&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType = NonArray, unsigned inlineCapacity = 0);
131     static Structure* create(PolyProtoTag, VM&, JSGlobalObject*, JSObject* prototype, const TypeInfo&, const ClassInfo*, IndexingType = NonArray, unsigned inlineCapacity = 0);
132
133     ~Structure();
134     
135     template<typename CellType>
136     static IsoSubspace* subspaceFor(VM& vm)
137     {
138         return &vm.structureSpace;
139     }
140
141 protected:
142     void finishCreation(VM& vm)
143     {
144         Base::finishCreation(vm);
145         ASSERT(m_prototype.get().isEmpty() || m_prototype.isObject() || m_prototype.isNull());
146     }
147
148     void finishCreation(VM& vm, const Structure* previous)
149     {
150         this->finishCreation(vm);
151         if (previous->hasRareData()) {
152             const StructureRareData* previousRareData = previous->rareData();
153             if (previousRareData->hasSharedPolyProtoWatchpoint()) {
154                 ensureRareData(vm);
155                 rareData()->setSharedPolyProtoWatchpoint(previousRareData->copySharedPolyProtoWatchpoint());
156             }
157         }
158     }
159
160     void finishCreation(VM& vm, CreatingEarlyCellTag)
161     {
162         Base::finishCreation(vm, this, CreatingEarlyCell);
163         ASSERT(m_prototype);
164         ASSERT(m_prototype.isNull());
165         ASSERT(!vm.structureStructure);
166     }
167
168 public:
169     StructureID id() const { return m_blob.structureID(); }
170     int32_t objectInitializationBlob() const { return m_blob.blobExcludingStructureID(); }
171     int64_t idBlob() const { return m_blob.blob(); }
172
173     bool isProxy() const
174     {
175         JSType type = m_blob.type();
176         return type == ImpureProxyType || type == PureForwardingProxyType || type == ProxyObjectType;
177     }
178
179     static void dumpStatistics();
180
181     JS_EXPORT_PRIVATE static Structure* addPropertyTransition(VM&, Structure*, PropertyName, unsigned attributes, PropertyOffset&);
182     JS_EXPORT_PRIVATE static Structure* addNewPropertyTransition(VM&, Structure*, PropertyName, unsigned attributes, PropertyOffset&, PutPropertySlot::Context = PutPropertySlot::UnknownContext, DeferredStructureTransitionWatchpointFire* = nullptr);
183     static Structure* addPropertyTransitionToExistingStructureConcurrently(Structure*, UniquedStringImpl* uid, unsigned attributes, PropertyOffset&);
184     JS_EXPORT_PRIVATE static Structure* addPropertyTransitionToExistingStructure(Structure*, PropertyName, unsigned attributes, PropertyOffset&);
185     static Structure* removePropertyTransition(VM&, Structure*, PropertyName, PropertyOffset&);
186     static Structure* changePrototypeTransition(VM&, Structure*, JSValue prototype, DeferredStructureTransitionWatchpointFire&);
187     JS_EXPORT_PRIVATE static Structure* attributeChangeTransition(VM&, Structure*, PropertyName, unsigned attributes);
188     JS_EXPORT_PRIVATE static Structure* toCacheableDictionaryTransition(VM&, Structure*, DeferredStructureTransitionWatchpointFire* = nullptr);
189     static Structure* toUncacheableDictionaryTransition(VM&, Structure*);
190     JS_EXPORT_PRIVATE static Structure* sealTransition(VM&, Structure*);
191     JS_EXPORT_PRIVATE static Structure* freezeTransition(VM&, Structure*);
192     static Structure* preventExtensionsTransition(VM&, Structure*);
193     static Structure* nonPropertyTransition(VM&, Structure*, NonPropertyTransition);
194     JS_EXPORT_PRIVATE static Structure* nonPropertyTransitionSlow(VM&, Structure*, NonPropertyTransition);
195
196     JS_EXPORT_PRIVATE bool isSealed(VM&);
197     JS_EXPORT_PRIVATE bool isFrozen(VM&);
198     bool isStructureExtensible() const { return !didPreventExtensions(); }
199
200     JS_EXPORT_PRIVATE Structure* flattenDictionaryStructure(VM&, JSObject*);
201
202     static const bool needsDestruction = true;
203     static void destroy(JSCell*);
204
205     // Versions that take a func will call it after making the change but while still holding
206     // the lock. The callback is not called if there is no change being made, like if you call
207     // removePropertyWithoutTransition() and the property is not found.
208     template<typename Func>
209     PropertyOffset addPropertyWithoutTransition(VM&, PropertyName, unsigned attributes, const Func&);
210     template<typename Func>
211     PropertyOffset removePropertyWithoutTransition(VM&, PropertyName, const Func&);
212     void setPrototypeWithoutTransition(VM&, JSValue prototype);
213         
214     bool isDictionary() const { return dictionaryKind() != NoneDictionaryKind; }
215     bool isUncacheableDictionary() const { return dictionaryKind() == UncachedDictionaryKind; }
216   
217     bool prototypeQueriesAreCacheable()
218     {
219         return !typeInfo().prohibitsPropertyCaching();
220     }
221     
222     bool propertyAccessesAreCacheable()
223     {
224         return dictionaryKind() != UncachedDictionaryKind
225             && prototypeQueriesAreCacheable()
226             && !(typeInfo().getOwnPropertySlotIsImpure() && !typeInfo().newImpurePropertyFiresWatchpoints());
227     }
228
229     bool propertyAccessesAreCacheableForAbsence()
230     {
231         return !typeInfo().getOwnPropertySlotIsImpureForPropertyAbsence();
232     }
233
234     bool needImpurePropertyWatchpoint()
235     {
236         return propertyAccessesAreCacheable()
237             && typeInfo().getOwnPropertySlotIsImpure()
238             && typeInfo().newImpurePropertyFiresWatchpoints();
239     }
240
241     bool isImmutablePrototypeExoticObject()
242     {
243         return typeInfo().isImmutablePrototypeExoticObject();
244     }
245
246     // We use SlowPath in GetByIdStatus for structures that may get new impure properties later to prevent
247     // DFG from inlining property accesses since structures don't transition when a new impure property appears.
248     bool takesSlowPathInDFGForImpureProperty()
249     {
250         return typeInfo().getOwnPropertySlotIsImpure();
251     }
252     
253     // Type accessors.
254     TypeInfo typeInfo() const { return m_blob.typeInfo(m_outOfLineTypeFlags); }
255     bool isObject() const { return typeInfo().isObject(); }
256
257     IndexingType indexingType() const { return m_blob.indexingModeIncludingHistory() & AllWritableArrayTypes; }
258     IndexingType indexingMode() const  { return m_blob.indexingModeIncludingHistory() & AllArrayTypes; }
259     IndexingType indexingModeIncludingHistory() const { return m_blob.indexingModeIncludingHistory(); }
260         
261     inline bool mayInterceptIndexedAccesses() const;
262     
263     bool holesMustForwardToPrototype(VM&, JSObject*) const;
264         
265     JSGlobalObject* globalObject() const { return m_globalObject.get(); }
266
267     // NOTE: This method should only be called during the creation of structures, since the global
268     // object of a structure is presumed to be immutable in a bunch of places.
269     void setGlobalObject(VM&, JSGlobalObject*);
270
271     ALWAYS_INLINE bool hasMonoProto() const
272     {
273         return !m_prototype.get().isEmpty();
274     }
275     ALWAYS_INLINE bool hasPolyProto() const
276     {
277         return !hasMonoProto();
278     }
279     ALWAYS_INLINE JSValue storedPrototype() const
280     {
281         ASSERT(hasMonoProto());
282         return m_prototype.get();
283     }
284     JSValue storedPrototype(const JSObject*) const;
285     JSObject* storedPrototypeObject(const JSObject*) const;
286     Structure* storedPrototypeStructure(const JSObject*) const;
287
288     JSObject* storedPrototypeObject() const;
289     Structure* storedPrototypeStructure() const;
290     JSValue prototypeForLookup(JSGlobalObject*) const;
291     JSValue prototypeForLookup(JSGlobalObject*, JSCell* base) const;
292     StructureChain* prototypeChain(VM&, JSGlobalObject*, JSObject* base) const;
293     StructureChain* prototypeChain(ExecState*, JSObject* base) const;
294     static void visitChildren(JSCell*, SlotVisitor&);
295     
296     // A Structure is cheap to mark during GC if doing so would only add a small and bounded amount
297     // to our heap footprint. For example, if the structure refers to a global object that is not
298     // yet marked, then as far as we know, the decision to mark this Structure would lead to a large
299     // increase in footprint because no other object refers to that global object. This method
300     // returns true if all user-controlled (and hence unbounded in size) objects referenced from the
301     // Structure are already marked.
302     bool isCheapDuringGC();
303     
304     // Returns true if this structure is now marked.
305     bool markIfCheap(SlotVisitor&);
306     
307     bool hasRareData() const
308     {
309         return isRareData(m_previousOrRareData.get());
310     }
311
312     StructureRareData* rareData()
313     {
314         ASSERT(hasRareData());
315         return static_cast<StructureRareData*>(m_previousOrRareData.get());
316     }
317
318     const StructureRareData* rareData() const
319     {
320         ASSERT(hasRareData());
321         return static_cast<const StructureRareData*>(m_previousOrRareData.get());
322     }
323
324     const StructureRareData* rareDataConcurrently() const
325     {
326         JSCell* cell = m_previousOrRareData.get();
327         if (isRareData(cell))
328             return static_cast<StructureRareData*>(cell);
329         return nullptr;
330     }
331
332     StructureRareData* ensureRareData(VM& vm)
333     {
334         if (!hasRareData())
335             allocateRareData(vm);
336         return rareData();
337     }
338     
339     Structure* previousID() const
340     {
341         ASSERT(structure()->classInfo() == info());
342         // This is so written because it's used concurrently. We only load from m_previousOrRareData
343         // once, and this load is guaranteed atomic.
344         JSCell* cell = m_previousOrRareData.get();
345         if (isRareData(cell))
346             return static_cast<StructureRareData*>(cell)->previousID();
347         return static_cast<Structure*>(cell);
348     }
349     bool transitivelyTransitionedFrom(Structure* structureToFind);
350     
351     PropertyOffset lastOffset() const { return m_offset; }
352     
353     void setLastOffset(PropertyOffset offset) { m_offset = offset; }
354
355     static unsigned outOfLineCapacity(PropertyOffset lastOffset)
356     {
357         unsigned outOfLineSize = Structure::outOfLineSize(lastOffset);
358
359         // This algorithm completely determines the out-of-line property storage growth algorithm.
360         // The JSObject code will only trigger a resize if the value returned by this algorithm
361         // changed between the new and old structure. So, it's important to keep this simple because
362         // it's on a fast path.
363         
364         if (!outOfLineSize)
365             return 0;
366
367         if (outOfLineSize <= initialOutOfLineCapacity)
368             return initialOutOfLineCapacity;
369
370         ASSERT(outOfLineSize > initialOutOfLineCapacity);
371         COMPILE_ASSERT(outOfLineGrowthFactor == 2, outOfLineGrowthFactor_is_two);
372         return WTF::roundUpToPowerOfTwo(outOfLineSize);
373     }
374     
375     static unsigned outOfLineSize(PropertyOffset lastOffset)
376     {
377         return numberOfOutOfLineSlotsForLastOffset(lastOffset);
378     }
379
380     unsigned outOfLineCapacity() const
381     {
382         return outOfLineCapacity(m_offset);
383     }
384     unsigned outOfLineSize() const
385     {
386         return outOfLineSize(m_offset);
387     }
388     bool hasInlineStorage() const
389     {
390         return !!m_inlineCapacity;
391     }
392     unsigned inlineCapacity() const
393     {
394         return m_inlineCapacity;
395     }
396     unsigned inlineSize() const
397     {
398         return std::min<unsigned>(m_offset + 1, m_inlineCapacity);
399     }
400     unsigned totalStorageSize() const
401     {
402         return numberOfSlotsForLastOffset(m_offset, m_inlineCapacity);
403     }
404     unsigned totalStorageCapacity() const
405     {
406         ASSERT(structure()->classInfo() == info());
407         return outOfLineCapacity() + inlineCapacity();
408     }
409
410     bool isValidOffset(PropertyOffset offset) const
411     {
412         return JSC::isValidOffset(offset)
413             && offset <= m_offset
414             && (offset < m_inlineCapacity || offset >= firstOutOfLineOffset);
415     }
416
417     bool hijacksIndexingHeader() const
418     {
419         return isTypedView(m_classInfo->typedArrayStorageType);
420     }
421     
422     bool couldHaveIndexingHeader() const
423     {
424         return hasIndexedProperties(indexingType())
425             || hijacksIndexingHeader();
426     }
427     
428     bool hasIndexingHeader(const JSCell*) const;
429     
430     bool masqueradesAsUndefined(JSGlobalObject* lexicalGlobalObject);
431
432     PropertyOffset get(VM&, PropertyName);
433     PropertyOffset get(VM&, PropertyName, unsigned& attributes);
434
435     // This is a somewhat internalish method. It will call your functor while possibly holding the
436     // Structure's lock. There is no guarantee whether the lock is held or not in any particular
437     // call. So, you have to assume the worst. Also, the functor returns true if it wishes for you
438     // to continue or false if it's done.
439     template<typename Functor>
440     void forEachPropertyConcurrently(const Functor&);
441
442     template<typename Functor>
443     void forEachProperty(VM&, const Functor&);
444     
445     PropertyOffset getConcurrently(UniquedStringImpl* uid);
446     PropertyOffset getConcurrently(UniquedStringImpl* uid, unsigned& attributes);
447     
448     Vector<PropertyMapEntry> getPropertiesConcurrently();
449     
450     void setHasGetterSetterPropertiesWithProtoCheck(bool is__proto__)
451     {
452         setHasGetterSetterProperties(true);
453         if (!is__proto__)
454             setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true);
455     }
456     
457     void setContainsReadOnlyProperties() { setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true); }
458     
459     void setHasCustomGetterSetterPropertiesWithProtoCheck(bool is__proto__)
460     {
461         setHasCustomGetterSetterProperties(true);
462         if (!is__proto__)
463             setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true);
464     }
465     
466     bool isEmpty() const
467     {
468         ASSERT(checkOffsetConsistency());
469         return !JSC::isValidOffset(m_offset);
470     }
471
472     void setCachedPropertyNameEnumerator(VM&, JSPropertyNameEnumerator*);
473     JSPropertyNameEnumerator* cachedPropertyNameEnumerator() const;
474     bool canCachePropertyNameEnumerator() const;
475     bool canAccessPropertiesQuicklyForEnumeration() const;
476
477     void setCachedOwnKeys(VM&, JSImmutableButterfly*);
478     JSImmutableButterfly* cachedOwnKeys() const;
479     JSImmutableButterfly* cachedOwnKeysIgnoringSentinel() const;
480     bool canCacheOwnKeys() const;
481
482     void getPropertyNamesFromStructure(VM&, PropertyNameArray&, EnumerationMode);
483
484     JSString* objectToStringValue()
485     {
486         if (!hasRareData())
487             return 0;
488         return rareData()->objectToStringValue();
489     }
490
491     void setObjectToStringValue(ExecState*, VM&, JSString* value, PropertySlot toStringTagSymbolSlot);
492
493     const ClassInfo* classInfo() const { return m_classInfo.unpoisoned(); }
494
495     static ptrdiff_t structureIDOffset()
496     {
497         return OBJECT_OFFSETOF(Structure, m_blob) + StructureIDBlob::structureIDOffset();
498     }
499
500     static ptrdiff_t prototypeOffset()
501     {
502         return OBJECT_OFFSETOF(Structure, m_prototype);
503     }
504
505     static ptrdiff_t globalObjectOffset()
506     {
507         return OBJECT_OFFSETOF(Structure, m_globalObject);
508     }
509
510     static ptrdiff_t classInfoOffset()
511     {
512         return OBJECT_OFFSETOF(Structure, m_classInfo);
513     }
514         
515     static ptrdiff_t indexingModeIncludingHistoryOffset()
516     {
517         return OBJECT_OFFSETOF(Structure, m_blob) + StructureIDBlob::indexingModeIncludingHistoryOffset();
518     }
519     
520     static ptrdiff_t propertyTableUnsafeOffset()
521     {
522         return OBJECT_OFFSETOF(Structure, m_propertyTableUnsafe);
523     }
524
525     static ptrdiff_t inlineCapacityOffset()
526     {
527         return OBJECT_OFFSETOF(Structure, m_inlineCapacity);
528     }
529
530     static ptrdiff_t previousOrRareDataOffset()
531     {
532         return OBJECT_OFFSETOF(Structure, m_previousOrRareData);
533     }
534
535     static Structure* createStructure(VM&);
536         
537     bool transitionWatchpointSetHasBeenInvalidated() const
538     {
539         return m_transitionWatchpointSet.hasBeenInvalidated();
540     }
541         
542     bool transitionWatchpointSetIsStillValid() const
543     {
544         return m_transitionWatchpointSet.isStillValid();
545     }
546     
547     bool dfgShouldWatchIfPossible() const
548     {
549         // FIXME: We would like to not watch things that are unprofitable to watch, like
550         // dictionaries. Unfortunately, we can't do such things: a dictionary could get flattened,
551         // in which case it will start to appear watchable and so the DFG will think that it is
552         // watching it. We should come up with a comprehensive story for not watching things that
553         // aren't profitable to watch.
554         // https://bugs.webkit.org/show_bug.cgi?id=133625
555         
556         // - We don't watch Structures that either decided not to be watched, or whose predecessors
557         //   decided not to be watched. This happens when a transition is fired while being watched.
558         if (transitionWatchpointIsLikelyToBeFired())
559             return false;
560
561         // - Don't watch Structures that had been dictionaries.
562         if (hasBeenDictionary())
563             return false;
564         
565         return true;
566     }
567     
568     bool dfgShouldWatch() const
569     {
570         return dfgShouldWatchIfPossible() && transitionWatchpointSetIsStillValid();
571     }
572         
573     void addTransitionWatchpoint(Watchpoint* watchpoint) const
574     {
575         ASSERT(transitionWatchpointSetIsStillValid());
576         m_transitionWatchpointSet.add(watchpoint);
577     }
578     
579     void didTransitionFromThisStructure(DeferredStructureTransitionWatchpointFire* = nullptr) const;
580     
581     InlineWatchpointSet& transitionWatchpointSet() const
582     {
583         return m_transitionWatchpointSet;
584     }
585     
586     WatchpointSet* ensurePropertyReplacementWatchpointSet(VM&, PropertyOffset);
587     void startWatchingPropertyForReplacements(VM& vm, PropertyOffset offset)
588     {
589         ensurePropertyReplacementWatchpointSet(vm, offset);
590     }
591     void startWatchingPropertyForReplacements(VM&, PropertyName);
592     WatchpointSet* propertyReplacementWatchpointSet(PropertyOffset);
593     void didReplaceProperty(PropertyOffset);
594     void didCachePropertyReplacement(VM&, PropertyOffset);
595     
596     void startWatchingInternalPropertiesIfNecessary(VM& vm)
597     {
598         if (LIKELY(didWatchInternalProperties()))
599             return;
600         startWatchingInternalProperties(vm);
601     }
602     
603     Ref<StructureShape> toStructureShape(JSValue, bool& sawPolyProtoStructure);
604     
605     void dump(PrintStream&) const;
606     void dumpInContext(PrintStream&, DumpContext*) const;
607     void dumpBrief(PrintStream&, const CString&) const;
608     
609     static void dumpContextHeader(PrintStream&);
610     
611     ConcurrentJSLock& lock() { return m_lock; }
612
613     unsigned propertyHash() const { return m_propertyHash; }
614
615     static bool shouldConvertToPolyProto(const Structure* a, const Structure* b);
616     
617     DECLARE_EXPORT_INFO;
618
619 private:
620     typedef enum { 
621         NoneDictionaryKind = 0,
622         CachedDictionaryKind = 1,
623         UncachedDictionaryKind = 2
624     } DictionaryKind;
625
626 public:
627 #define DEFINE_BITFIELD(type, lowerName, upperName, width, offset) \
628     static const uint32_t s_##lowerName##Shift = offset;\
629     static const uint32_t s_##lowerName##Mask = ((1 << (width - 1)) | ((1 << (width - 1)) - 1));\
630     type lowerName() const { return static_cast<type>((m_bitField >> offset) & s_##lowerName##Mask); }\
631     void set##upperName(type newValue) \
632     {\
633         m_bitField &= ~(s_##lowerName##Mask << offset);\
634         m_bitField |= (newValue & s_##lowerName##Mask) << offset;\
635     }
636
637     DEFINE_BITFIELD(DictionaryKind, dictionaryKind, DictionaryKind, 2, 0);
638     DEFINE_BITFIELD(bool, isPinnedPropertyTable, IsPinnedPropertyTable, 1, 2);
639     DEFINE_BITFIELD(bool, hasGetterSetterProperties, HasGetterSetterProperties, 1, 3);
640     DEFINE_BITFIELD(bool, hasReadOnlyOrGetterSetterPropertiesExcludingProto, HasReadOnlyOrGetterSetterPropertiesExcludingProto, 1, 4);
641     DEFINE_BITFIELD(bool, isQuickPropertyAccessAllowedForEnumeration, IsQuickPropertyAccessAllowedForEnumeration, 1, 5);
642     DEFINE_BITFIELD(unsigned, attributesInPrevious, AttributesInPrevious, 14, 6);
643     DEFINE_BITFIELD(bool, didPreventExtensions, DidPreventExtensions, 1, 20);
644     DEFINE_BITFIELD(bool, didTransition, DidTransition, 1, 21);
645     DEFINE_BITFIELD(bool, staticPropertiesReified, StaticPropertiesReified, 1, 22);
646     DEFINE_BITFIELD(bool, hasBeenFlattenedBefore, HasBeenFlattenedBefore, 1, 23);
647     DEFINE_BITFIELD(bool, hasCustomGetterSetterProperties, HasCustomGetterSetterProperties, 1, 24);
648     DEFINE_BITFIELD(bool, didWatchInternalProperties, DidWatchInternalProperties, 1, 25);
649     DEFINE_BITFIELD(bool, transitionWatchpointIsLikelyToBeFired, TransitionWatchpointIsLikelyToBeFired, 1, 26);
650     DEFINE_BITFIELD(bool, hasBeenDictionary, HasBeenDictionary, 1, 27);
651     DEFINE_BITFIELD(bool, isAddingPropertyForTransition, IsAddingPropertyForTransition, 1, 28);
652     DEFINE_BITFIELD(bool, hasUnderscoreProtoPropertyExcludingOriginalProto, HasUnderscoreProtoPropertyExcludingOriginalProto, 1, 29);
653
654 private:
655     friend class LLIntOffsetsExtractor;
656
657     JS_EXPORT_PRIVATE Structure(VM&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType, unsigned inlineCapacity);
658     Structure(VM&);
659     Structure(VM&, Structure*, DeferredStructureTransitionWatchpointFire*);
660
661     static Structure* create(VM&, Structure*, DeferredStructureTransitionWatchpointFire* = nullptr);
662     
663     static Structure* addPropertyTransitionToExistingStructureImpl(Structure*, UniquedStringImpl* uid, unsigned attributes, PropertyOffset&);
664
665     // This will return the structure that has a usable property table, that property table,
666     // and the list of structures that we visited before we got to it. If it returns a
667     // non-null structure, it will also lock the structure that it returns; it is your job
668     // to unlock it.
669     void findStructuresAndMapForMaterialization(Vector<Structure*, 8>& structures, Structure*&, PropertyTable*&);
670     
671     static Structure* toDictionaryTransition(VM&, Structure*, DictionaryKind, DeferredStructureTransitionWatchpointFire* = nullptr);
672
673     enum class ShouldPin { No, Yes };
674     template<ShouldPin, typename Func>
675     PropertyOffset add(VM&, PropertyName, unsigned attributes, const Func&);
676     PropertyOffset add(VM&, PropertyName, unsigned attributes);
677     template<typename Func>
678     PropertyOffset remove(PropertyName, const Func&);
679     PropertyOffset remove(PropertyName);
680
681     void checkConsistency();
682
683     // This may grab the lock, or not. Do not call when holding the Structure's lock.
684     PropertyTable* ensurePropertyTableIfNotEmpty(VM& vm)
685     {
686         if (PropertyTable* result = m_propertyTableUnsafe.get())
687             return result;
688         if (!previousID())
689             return nullptr;
690         return materializePropertyTable(vm);
691     }
692     
693     // This may grab the lock, or not. Do not call when holding the Structure's lock.
694     PropertyTable* ensurePropertyTable(VM& vm)
695     {
696         if (PropertyTable* result = m_propertyTableUnsafe.get())
697             return result;
698         return materializePropertyTable(vm);
699     }
700     
701     PropertyTable* propertyTableOrNull() const
702     {
703         return m_propertyTableUnsafe.get();
704     }
705     
706     // This will grab the lock. Do not call when holding the Structure's lock.
707     JS_EXPORT_PRIVATE PropertyTable* materializePropertyTable(VM&, bool setPropertyTable = true);
708     
709     void setPropertyTable(VM& vm, PropertyTable* table);
710     
711     PropertyTable* takePropertyTableOrCloneIfPinned(VM&);
712     PropertyTable* copyPropertyTableForPinning(VM&);
713
714     void setPreviousID(VM&, Structure*);
715
716     void clearPreviousID()
717     {
718         if (hasRareData())
719             rareData()->clearPreviousID();
720         else
721             m_previousOrRareData.clear();
722     }
723
724     int transitionCount() const
725     {
726         // Since the number of transitions is always the same as m_offset, we keep the size of Structure down by not storing both.
727         return numberOfSlotsForLastOffset(m_offset, m_inlineCapacity);
728     }
729
730     bool isValid(JSGlobalObject*, StructureChain* cachedPrototypeChain, JSObject* base) const;
731
732     // You have to hold the structure lock to do these.
733     JS_EXPORT_PRIVATE void pin(const AbstractLocker&, VM&, PropertyTable*);
734     void pinForCaching(const AbstractLocker&, VM&, PropertyTable*);
735     
736     bool isRareData(JSCell* cell) const
737     {
738         return cell && cell->structureID() != structureID();
739     }
740
741     template<typename DetailsFunc>
742     bool checkOffsetConsistency(PropertyTable*, const DetailsFunc&) const;
743     bool checkOffsetConsistency() const;
744
745     JS_EXPORT_PRIVATE void allocateRareData(VM&);
746     
747     void startWatchingInternalProperties(VM&);
748
749     static const int s_maxTransitionLength = 64;
750     static const int s_maxTransitionLengthForNonEvalPutById = 512;
751
752     // These need to be properly aligned at the beginning of the 'Structure'
753     // part of the object.
754     StructureIDBlob m_blob;
755     TypeInfo::OutOfLineTypeFlags m_outOfLineTypeFlags;
756
757     uint8_t m_inlineCapacity;
758
759     ConcurrentJSLock m_lock;
760
761     uint32_t m_bitField;
762
763     WriteBarrier<JSGlobalObject> m_globalObject;
764     WriteBarrier<Unknown> m_prototype;
765     mutable WriteBarrier<StructureChain> m_cachedPrototypeChain;
766
767     WriteBarrier<JSCell> m_previousOrRareData;
768
769     RefPtr<UniquedStringImpl> m_nameInPrevious;
770
771     PoisonedClassInfoPtr m_classInfo;
772
773     StructureTransitionTable m_transitionTable;
774
775     // Should be accessed through ensurePropertyTable(). During GC, it may be set to 0 by another thread.
776     // During a Heap Snapshot GC we avoid clearing the table so it is safe to use.
777     WriteBarrier<PropertyTable> m_propertyTableUnsafe;
778
779     mutable InlineWatchpointSet m_transitionWatchpointSet;
780
781     COMPILE_ASSERT(firstOutOfLineOffset < 256, firstOutOfLineOffset_fits);
782
783     // m_offset does not account for anonymous slots
784     PropertyOffset m_offset;
785
786     uint32_t m_propertyHash;
787 };
788
789 // We deliberately put Structure::create here in Structure.h instead of StructureInlines.h, because
790 // it is used everywhere. This is so we don't have to hunt down all the places where we would need
791 // to #include StructureInlines.h otherwise.
792 inline Structure* Structure::create(VM& vm, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo, IndexingType indexingType, unsigned inlineCapacity)
793 {
794     ASSERT(vm.structureStructure);
795     ASSERT(classInfo);
796     Structure* structure = new (NotNull, allocateCell<Structure>(vm.heap)) Structure(vm, globalObject, prototype, typeInfo, classInfo, indexingType, inlineCapacity);
797     structure->finishCreation(vm);
798     return structure;
799 }
800
801 } // namespace JSC