Structure::get should instantiate DeferGC only when materializing property map
[WebKit-https.git] / Source / JavaScriptCore / runtime / Structure.h
1 /*
2  * Copyright (C) 2008, 2009, 2012, 2013 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #ifndef Structure_h
27 #define Structure_h
28
29 #include "ClassInfo.h"
30 #include "ConcurrentJITLock.h"
31 #include "IndexingType.h"
32 #include "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 "StructureTransitionTable.h"
43 #include "JSTypeInfo.h"
44 #include "Watchpoint.h"
45 #include "Weak.h"
46 #include "WriteBarrierInlines.h"
47 #include <wtf/CompilationThread.h>
48 #include <wtf/PassRefPtr.h>
49 #include <wtf/PrintStream.h>
50 #include <wtf/RefCounted.h>
51 #include <wtf/text/StringImpl.h>
52
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 SlotVisitor;
63 class JSString;
64 struct DumpContext;
65
66 // The out-of-line property storage capacity to use when first allocating out-of-line
67 // storage. Note that all objects start out without having any out-of-line storage;
68 // this comes into play only on the first property store that exhausts inline storage.
69 static const unsigned initialOutOfLineCapacity = 4;
70
71 // The factor by which to grow out-of-line storage when it is exhausted, after the
72 // initial allocation.
73 static const unsigned outOfLineGrowthFactor = 2;
74
75 class Structure : public JSCell {
76 public:
77     friend class StructureTransitionTable;
78
79     typedef JSCell Base;
80     
81     static Structure* create(VM&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType = NonArray, unsigned inlineCapacity = 0);
82
83     ~Structure();
84
85 protected:
86     void finishCreation(VM& vm)
87     {
88         Base::finishCreation(vm);
89         ASSERT(m_prototype);
90         ASSERT(m_prototype.isObject() || m_prototype.isNull());
91     }
92
93     void finishCreation(VM& vm, CreatingEarlyCellTag)
94     {
95         Base::finishCreation(vm, this, CreatingEarlyCell);
96         ASSERT(m_prototype);
97         ASSERT(m_prototype.isNull());
98         ASSERT(!vm.structureStructure);
99     }
100
101 public:
102     StructureID id() const { return m_blob.structureID(); }
103     int32_t objectInitializationBlob() const { return m_blob.blobExcludingStructureID(); }
104     int64_t idBlob() const { return m_blob.blob(); }
105
106     bool isProxy() const
107     {
108         JSType type = m_blob.type();
109         return type == ImpureProxyType || type == PureForwardingProxyType;
110     }
111
112     static void dumpStatistics();
113
114     JS_EXPORT_PRIVATE static Structure* addPropertyTransition(VM&, Structure*, PropertyName, unsigned attributes, JSCell* specificValue, PropertyOffset&, PutPropertySlot::Context = PutPropertySlot::UnknownContext);
115     static Structure* addPropertyTransitionToExistingStructureConcurrently(Structure*, StringImpl* uid, unsigned attributes, JSCell* specificValue, PropertyOffset&);
116     JS_EXPORT_PRIVATE static Structure* addPropertyTransitionToExistingStructure(Structure*, PropertyName, unsigned attributes, JSCell* specificValue, PropertyOffset&);
117     static Structure* removePropertyTransition(VM&, Structure*, PropertyName, PropertyOffset&);
118     JS_EXPORT_PRIVATE static Structure* changePrototypeTransition(VM&, Structure*, JSValue prototype);
119     JS_EXPORT_PRIVATE static Structure* despecifyFunctionTransition(VM&, Structure*, PropertyName);
120     static Structure* attributeChangeTransition(VM&, Structure*, PropertyName, unsigned attributes);
121     JS_EXPORT_PRIVATE static Structure* toCacheableDictionaryTransition(VM&, Structure*);
122     static Structure* toUncacheableDictionaryTransition(VM&, Structure*);
123     static Structure* sealTransition(VM&, Structure*);
124     static Structure* freezeTransition(VM&, Structure*);
125     static Structure* preventExtensionsTransition(VM&, Structure*);
126     static Structure* nonPropertyTransition(VM&, Structure*, NonPropertyTransition);
127
128     bool isSealed(VM&);
129     bool isFrozen(VM&);
130     bool isExtensible() const { return !m_preventExtensions; }
131     bool didTransition() const { return m_didTransition; }
132     bool putWillGrowOutOfLineStorage();
133     size_t suggestedNewOutOfLineStorageCapacity(); 
134
135     JS_EXPORT_PRIVATE Structure* flattenDictionaryStructure(VM&, JSObject*);
136
137     static const bool needsDestruction = true;
138     static const bool hasImmortalStructure = true;
139     static void destroy(JSCell*);
140
141     // These should be used with caution.  
142     JS_EXPORT_PRIVATE PropertyOffset addPropertyWithoutTransition(VM&, PropertyName, unsigned attributes, JSCell* specificValue);
143     PropertyOffset removePropertyWithoutTransition(VM&, PropertyName);
144     void setPrototypeWithoutTransition(VM& vm, JSValue prototype) { m_prototype.set(vm, this, prototype); }
145         
146     bool isDictionary() const { return m_dictionaryKind != NoneDictionaryKind; }
147     bool isUncacheableDictionary() const { return m_dictionaryKind == UncachedDictionaryKind; }
148
149     bool propertyAccessesAreCacheable() { return m_dictionaryKind != UncachedDictionaryKind && !typeInfo().prohibitsPropertyCaching(); }
150
151     // We use SlowPath in GetByIdStatus for structures that may get new impure properties later to prevent
152     // DFG from inlining property accesses since structures don't transition when a new impure property appears.
153     bool takesSlowPathInDFGForImpureProperty()
154     {
155         return typeInfo().hasImpureGetOwnPropertySlot();
156     }
157
158     // Type accessors.
159     TypeInfo typeInfo() const { ASSERT(structure()->classInfo() == info()); return m_blob.typeInfo(m_outOfLineTypeFlags); }
160     bool isObject() const { return typeInfo().isObject(); }
161
162     IndexingType indexingType() const { return m_blob.indexingType() & AllArrayTypes; }
163     IndexingType indexingTypeIncludingHistory() const { return m_blob.indexingType(); }
164         
165     bool mayInterceptIndexedAccesses() const
166     {
167         return !!(indexingTypeIncludingHistory() & MayHaveIndexedAccessors);
168     }
169         
170     bool anyObjectInChainMayInterceptIndexedAccesses() const;
171     bool holesMustForwardToPrototype(VM&) const;
172         
173     bool needsSlowPutIndexing() const;
174     NonPropertyTransition suggestedArrayStorageTransition() const;
175         
176     JSGlobalObject* globalObject() const { return m_globalObject.get(); }
177     void setGlobalObject(VM& vm, JSGlobalObject* globalObject) { m_globalObject.set(vm, this, globalObject); }
178         
179     JSValue storedPrototype() const { return m_prototype.get(); }
180     JSObject* storedPrototypeObject() const;
181     Structure* storedPrototypeStructure() const;
182     JSValue prototypeForLookup(ExecState*) const;
183     JSValue prototypeForLookup(JSGlobalObject*) const;
184     JSValue prototypeForLookup(CodeBlock*) const;
185     StructureChain* prototypeChain(VM&, JSGlobalObject*) const;
186     StructureChain* prototypeChain(ExecState*) const;
187     static void visitChildren(JSCell*, SlotVisitor&);
188         
189     // Will just the prototype chain intercept this property access?
190     bool prototypeChainMayInterceptStoreTo(VM&, PropertyName);
191         
192     bool transitionDidInvolveSpecificValue() const { return !!m_specificValueInPrevious; }
193         
194     Structure* previousID() const
195     {
196         ASSERT(structure()->classInfo() == info());
197         if (typeInfo().structureHasRareData())
198             return rareData()->previousID();
199         return previous();
200     }
201     bool transitivelyTransitionedFrom(Structure* structureToFind);
202
203     unsigned outOfLineCapacity() const
204     {
205         ASSERT(checkOffsetConsistency());
206             
207         unsigned outOfLineSize = this->outOfLineSize();
208
209         if (!outOfLineSize)
210             return 0;
211
212         if (outOfLineSize <= initialOutOfLineCapacity)
213             return initialOutOfLineCapacity;
214
215         ASSERT(outOfLineSize > initialOutOfLineCapacity);
216         COMPILE_ASSERT(outOfLineGrowthFactor == 2, outOfLineGrowthFactor_is_two);
217         return WTF::roundUpToPowerOfTwo(outOfLineSize);
218     }
219     unsigned outOfLineSize() const
220     {
221         ASSERT(checkOffsetConsistency());
222         ASSERT(structure()->classInfo() == info());
223             
224         return numberOfOutOfLineSlotsForLastOffset(m_offset);
225     }
226     bool hasInlineStorage() const
227     {
228         return !!m_inlineCapacity;
229     }
230     unsigned inlineCapacity() const
231     {
232         return m_inlineCapacity;
233     }
234     unsigned inlineSize() const
235     {
236         return std::min<unsigned>(m_offset + 1, m_inlineCapacity);
237     }
238     unsigned totalStorageSize() const
239     {
240         return numberOfSlotsForLastOffset(m_offset, m_inlineCapacity);
241     }
242     unsigned totalStorageCapacity() const
243     {
244         ASSERT(structure()->classInfo() == info());
245         return outOfLineCapacity() + inlineCapacity();
246     }
247
248     bool isValidOffset(PropertyOffset offset) const
249     {
250         return JSC::isValidOffset(offset)
251             && offset <= m_offset
252             && (offset < m_inlineCapacity || offset >= firstOutOfLineOffset);
253     }
254     
255     bool couldHaveIndexingHeader() const
256     {
257         return hasIndexedProperties(indexingType())
258             || isTypedView(m_classInfo->typedArrayStorageType);
259     }
260     
261     bool hasIndexingHeader(const JSCell*) const;
262     
263     bool masqueradesAsUndefined(JSGlobalObject* lexicalGlobalObject);
264
265     PropertyOffset get(VM&, PropertyName);
266     PropertyOffset get(VM&, const WTF::String& name);
267     PropertyOffset get(VM&, PropertyName, unsigned& attributes, JSCell*& specificValue);
268
269     PropertyOffset getConcurrently(VM&, StringImpl* uid);
270     PropertyOffset getConcurrently(VM&, StringImpl* uid, unsigned& attributes, JSCell*& specificValue);
271
272     bool hasGetterSetterProperties() const { return m_hasGetterSetterProperties; }
273     bool hasReadOnlyOrGetterSetterPropertiesExcludingProto() const { return m_hasReadOnlyOrGetterSetterPropertiesExcludingProto; }
274     void setHasGetterSetterProperties(bool is__proto__)
275     {
276         m_hasGetterSetterProperties = true;
277         if (!is__proto__)
278             m_hasReadOnlyOrGetterSetterPropertiesExcludingProto = true;
279     }
280
281     bool hasCustomGetterSetterProperties() const { return m_hasCustomGetterSetterProperties; }
282     void setHasCustomGetterSetterProperties() { m_hasCustomGetterSetterProperties = true; }
283
284     void setContainsReadOnlyProperties()
285     {
286         m_hasReadOnlyOrGetterSetterPropertiesExcludingProto = true;
287     }
288
289     bool hasNonEnumerableProperties() const { return m_hasNonEnumerableProperties; }
290         
291     bool isEmpty() const
292     {
293         ASSERT(checkOffsetConsistency());
294         return !JSC::isValidOffset(m_offset);
295     }
296
297     JS_EXPORT_PRIVATE void despecifyDictionaryFunction(VM&, PropertyName);
298     void disableSpecificFunctionTracking() { m_specificFunctionThrashCount = maxSpecificFunctionThrashCount; }
299
300     void setEnumerationCache(VM&, JSPropertyNameIterator* enumerationCache); // Defined in JSPropertyNameIterator.h.
301     JSPropertyNameIterator* enumerationCache(); // Defined in JSPropertyNameIterator.h.
302     void getPropertyNamesFromStructure(VM&, PropertyNameArray&, EnumerationMode);
303
304     JSString* objectToStringValue()
305     {
306         if (!typeInfo().structureHasRareData())
307             return 0;
308         return rareData()->objectToStringValue();
309     }
310
311     void setObjectToStringValue(VM& vm, const JSCell* owner, JSString* value)
312     {
313         if (!typeInfo().structureHasRareData())
314             allocateRareData(vm);
315         rareData()->setObjectToStringValue(vm, owner, value);
316     }
317
318     bool staticFunctionsReified()
319     {
320         return m_staticFunctionReified;
321     }
322
323     void setStaticFunctionsReified()
324     {
325         m_staticFunctionReified = true;
326     }
327
328     const ClassInfo* classInfo() const { return m_classInfo; }
329
330     static ptrdiff_t structureIDOffset()
331     {
332         return OBJECT_OFFSETOF(Structure, m_blob) + StructureIDBlob::structureIDOffset();
333     }
334
335     static ptrdiff_t prototypeOffset()
336     {
337         return OBJECT_OFFSETOF(Structure, m_prototype);
338     }
339
340     static ptrdiff_t globalObjectOffset()
341     {
342         return OBJECT_OFFSETOF(Structure, m_globalObject);
343     }
344
345     static ptrdiff_t classInfoOffset()
346     {
347         return OBJECT_OFFSETOF(Structure, m_classInfo);
348     }
349         
350     static ptrdiff_t indexingTypeOffset()
351     {
352         return OBJECT_OFFSETOF(Structure, m_blob) + StructureIDBlob::indexingTypeOffset();
353     }
354
355     static Structure* createStructure(VM&);
356         
357     bool transitionWatchpointSetHasBeenInvalidated() const
358     {
359         return m_transitionWatchpointSet.hasBeenInvalidated();
360     }
361         
362     bool transitionWatchpointSetIsStillValid() const
363     {
364         return m_transitionWatchpointSet.isStillValid();
365     }
366         
367     void addTransitionWatchpoint(Watchpoint* watchpoint) const
368     {
369         ASSERT(transitionWatchpointSetIsStillValid());
370         m_transitionWatchpointSet.add(watchpoint);
371     }
372         
373     void notifyTransitionFromThisStructure() const
374     {
375         m_transitionWatchpointSet.fireAll();
376     }
377     
378     InlineWatchpointSet& transitionWatchpointSet() const
379     {
380         return m_transitionWatchpointSet;
381     }
382     
383     void dump(PrintStream&) const;
384     void dumpInContext(PrintStream&, DumpContext*) const;
385     void dumpBrief(PrintStream&, const CString&) const;
386     
387     static void dumpContextHeader(PrintStream&);
388     
389     DECLARE_EXPORT_INFO;
390
391 private:
392     friend class LLIntOffsetsExtractor;
393
394     JS_EXPORT_PRIVATE Structure(VM&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType, unsigned inlineCapacity);
395     Structure(VM&);
396     Structure(VM&, Structure*);
397
398     static Structure* create(VM&, Structure*);
399     
400     static Structure* addPropertyTransitionToExistingStructureImpl(Structure*, StringImpl* uid, unsigned attributes, JSCell* specificValue, PropertyOffset&);
401
402     // This will return the structure that has a usable property table, that property table,
403     // and the list of structures that we visited before we got to it. If it returns a
404     // non-null structure, it will also lock the structure that it returns; it is your job
405     // to unlock it.
406     void findStructuresAndMapForMaterialization(Vector<Structure*, 8>& structures, Structure*&, PropertyTable*&);
407     
408     typedef enum { 
409         NoneDictionaryKind = 0,
410         CachedDictionaryKind = 1,
411         UncachedDictionaryKind = 2
412     } DictionaryKind;
413     static Structure* toDictionaryTransition(VM&, Structure*, DictionaryKind);
414
415     PropertyOffset putSpecificValue(VM&, PropertyName, unsigned attributes, JSCell* specificValue);
416     PropertyOffset remove(PropertyName);
417
418     void createPropertyMap(const GCSafeConcurrentJITLocker&, VM&, unsigned keyCount = 0);
419     void checkConsistency();
420
421     bool despecifyFunction(VM&, PropertyName);
422     void despecifyAllFunctions(VM&);
423
424     WriteBarrier<PropertyTable>& propertyTable();
425     PropertyTable* takePropertyTableOrCloneIfPinned(VM&, Structure* owner);
426     PropertyTable* copyPropertyTable(VM&, Structure* owner);
427     PropertyTable* copyPropertyTableForPinning(VM&, Structure* owner);
428     JS_EXPORT_PRIVATE void materializePropertyMap(VM&);
429     void materializePropertyMapIfNecessary(VM& vm, DeferGC&)
430     {
431         ASSERT(!isCompilationThread());
432         ASSERT(structure()->classInfo() == info());
433         ASSERT(checkOffsetConsistency());
434         if (!propertyTable() && previousID())
435             materializePropertyMap(vm);
436     }
437     void materializePropertyMapIfNecessary(VM& vm, PropertyTable*& table)
438     {
439         ASSERT(!isCompilationThread());
440         ASSERT(structure()->classInfo() == info());
441         ASSERT(checkOffsetConsistency());
442         table = propertyTable().get();
443         if (!table && previousID()) {
444             DeferGC deferGC(vm.heap);
445             materializePropertyMap(vm);
446             table = propertyTable().get();
447         }
448     }
449     void materializePropertyMapIfNecessaryForPinning(VM& vm, DeferGC&)
450     {
451         ASSERT(structure()->classInfo() == info());
452         checkOffsetConsistency();
453         if (!propertyTable())
454             materializePropertyMap(vm);
455     }
456
457     void setPreviousID(VM& vm, Structure* transition, Structure* structure)
458     {
459         if (typeInfo().structureHasRareData())
460             rareData()->setPreviousID(vm, transition, structure);
461         else
462             m_previousOrRareData.set(vm, transition, structure);
463     }
464
465     void clearPreviousID()
466     {
467         if (typeInfo().structureHasRareData())
468             rareData()->clearPreviousID();
469         else
470             m_previousOrRareData.clear();
471     }
472
473     int transitionCount() const
474     {
475         // Since the number of transitions is always the same as m_offset, we keep the size of Structure down by not storing both.
476         return numberOfSlotsForLastOffset(m_offset, m_inlineCapacity);
477     }
478
479     bool isValid(JSGlobalObject*, StructureChain* cachedPrototypeChain) const;
480     bool isValid(ExecState*, StructureChain* cachedPrototypeChain) const;
481         
482     void pin();
483
484     Structure* previous() const
485     {
486         ASSERT(!typeInfo().structureHasRareData());
487         return static_cast<Structure*>(m_previousOrRareData.get());
488     }
489
490     StructureRareData* rareData() const
491     {
492         ASSERT(typeInfo().structureHasRareData());
493         return static_cast<StructureRareData*>(m_previousOrRareData.get());
494     }
495         
496     bool checkOffsetConsistency() const;
497
498     void allocateRareData(VM&);
499     void cloneRareDataFrom(VM&, const Structure*);
500
501     static const int s_maxTransitionLength = 64;
502     static const int s_maxTransitionLengthForNonEvalPutById = 512;
503
504     static const unsigned maxSpecificFunctionThrashCount = 3;
505     
506     // These need to be properly aligned at the beginning of the 'Structure'
507     // part of the object.
508     StructureIDBlob m_blob;
509     TypeInfo::OutOfLineTypeFlags m_outOfLineTypeFlags;
510
511     WriteBarrier<JSGlobalObject> m_globalObject;
512     WriteBarrier<Unknown> m_prototype;
513     mutable WriteBarrier<StructureChain> m_cachedPrototypeChain;
514
515     WriteBarrier<JSCell> m_previousOrRareData;
516
517     RefPtr<StringImpl> m_nameInPrevious;
518     WriteBarrier<JSCell> m_specificValueInPrevious;
519
520     const ClassInfo* m_classInfo;
521
522     StructureTransitionTable m_transitionTable;
523
524     // Should be accessed through propertyTable(). During GC, it may be set to 0 by another thread.
525     WriteBarrier<PropertyTable> m_propertyTableUnsafe;
526
527     mutable InlineWatchpointSet m_transitionWatchpointSet;
528
529     COMPILE_ASSERT(firstOutOfLineOffset < 256, firstOutOfLineOffset_fits);
530
531     // m_offset does not account for anonymous slots
532     PropertyOffset m_offset;
533
534     uint8_t m_inlineCapacity;
535     
536     ConcurrentJITLock m_lock;
537     
538     unsigned m_dictionaryKind : 2;
539     bool m_isPinnedPropertyTable : 1;
540     bool m_hasGetterSetterProperties : 1;
541     bool m_hasCustomGetterSetterProperties : 1;
542     bool m_hasReadOnlyOrGetterSetterPropertiesExcludingProto : 1;
543     bool m_hasNonEnumerableProperties : 1;
544     unsigned m_attributesInPrevious : 14;
545     unsigned m_specificFunctionThrashCount : 2;
546     unsigned m_preventExtensions : 1;
547     unsigned m_didTransition : 1;
548     unsigned m_staticFunctionReified : 1;
549 };
550
551 } // namespace JSC
552
553 #endif // Structure_h