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