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