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