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