Foo::s_info should be Foo::info(), so that you can change how the s_info is actually...
[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 COMPUTER, 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 COMPUTER, 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 "StructureRareData.h"
40 #include "StructureTransitionTable.h"
41 #include "JSTypeInfo.h"
42 #include "Watchpoint.h"
43 #include "Weak.h"
44 #include <wtf/CompilationThread.h>
45 #include <wtf/PassRefPtr.h>
46 #include <wtf/PrintStream.h>
47 #include <wtf/RefCounted.h>
48 #include <wtf/text/StringImpl.h>
49
50
51 namespace JSC {
52
53 class LLIntOffsetsExtractor;
54 class PropertyNameArray;
55 class PropertyNameArrayData;
56 class PropertyTable;
57 class StructureChain;
58 class SlotVisitor;
59 class JSString;
60 struct DumpContext;
61
62 // The out-of-line property storage capacity to use when first allocating out-of-line
63 // storage. Note that all objects start out without having any out-of-line storage;
64 // this comes into play only on the first property store that exhausts inline storage.
65 static const unsigned initialOutOfLineCapacity = 4;
66
67 // The factor by which to grow out-of-line storage when it is exhausted, after the
68 // initial allocation.
69 static const unsigned outOfLineGrowthFactor = 2;
70
71 class Structure : public JSCell {
72 public:
73     friend class StructureTransitionTable;
74
75     typedef JSCell Base;
76     
77     static Structure* create(VM&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType = NonArray, unsigned inlineCapacity = 0);
78
79 protected:
80     void finishCreation(VM& vm)
81     {
82         Base::finishCreation(vm);
83         ASSERT(m_prototype);
84         ASSERT(m_prototype.isObject() || m_prototype.isNull());
85     }
86
87     void finishCreation(VM& vm, CreatingEarlyCellTag)
88     {
89         Base::finishCreation(vm, this, CreatingEarlyCell);
90         ASSERT(m_prototype);
91         ASSERT(m_prototype.isNull());
92         ASSERT(!vm.structureStructure);
93     }
94
95 public:
96     static void dumpStatistics();
97
98     JS_EXPORT_PRIVATE static Structure* addPropertyTransition(VM&, Structure*, PropertyName, unsigned attributes, JSCell* specificValue, PropertyOffset&);
99     static Structure* addPropertyTransitionToExistingStructureConcurrently(Structure*, StringImpl* uid, unsigned attributes, JSCell* specificValue, PropertyOffset&);
100     JS_EXPORT_PRIVATE static Structure* addPropertyTransitionToExistingStructure(Structure*, PropertyName, unsigned attributes, JSCell* specificValue, PropertyOffset&);
101     static Structure* removePropertyTransition(VM&, Structure*, PropertyName, PropertyOffset&);
102     JS_EXPORT_PRIVATE static Structure* changePrototypeTransition(VM&, Structure*, JSValue prototype);
103     JS_EXPORT_PRIVATE static Structure* despecifyFunctionTransition(VM&, Structure*, PropertyName);
104     static Structure* attributeChangeTransition(VM&, Structure*, PropertyName, unsigned attributes);
105     static Structure* toCacheableDictionaryTransition(VM&, Structure*);
106     static Structure* toUncacheableDictionaryTransition(VM&, Structure*);
107     static Structure* sealTransition(VM&, Structure*);
108     static Structure* freezeTransition(VM&, Structure*);
109     static Structure* preventExtensionsTransition(VM&, Structure*);
110     static Structure* nonPropertyTransition(VM&, Structure*, NonPropertyTransition);
111
112     bool isSealed(VM&);
113     bool isFrozen(VM&);
114     bool isExtensible() const { return !m_preventExtensions; }
115     bool didTransition() const { return m_didTransition; }
116     bool putWillGrowOutOfLineStorage();
117     JS_EXPORT_PRIVATE size_t suggestedNewOutOfLineStorageCapacity(); 
118
119     Structure* flattenDictionaryStructure(VM&, JSObject*);
120
121     static const bool needsDestruction = true;
122     static const bool hasImmortalStructure = true;
123     static void destroy(JSCell*);
124
125     // These should be used with caution.  
126     JS_EXPORT_PRIVATE PropertyOffset addPropertyWithoutTransition(VM&, PropertyName, unsigned attributes, JSCell* specificValue);
127     PropertyOffset removePropertyWithoutTransition(VM&, PropertyName);
128     void setPrototypeWithoutTransition(VM& vm, JSValue prototype) { m_prototype.set(vm, this, prototype); }
129         
130     bool isDictionary() const { return m_dictionaryKind != NoneDictionaryKind; }
131     bool isUncacheableDictionary() const { return m_dictionaryKind == UncachedDictionaryKind; }
132
133     bool propertyAccessesAreCacheable() { return m_dictionaryKind != UncachedDictionaryKind && !typeInfo().prohibitsPropertyCaching(); }
134
135     // Type accessors.
136     const TypeInfo& typeInfo() const { ASSERT(structure()->classInfo() == info()); return m_typeInfo; }
137     bool isObject() const { return typeInfo().isObject(); }
138
139     IndexingType indexingType() const { return m_indexingType & AllArrayTypes; }
140     IndexingType indexingTypeIncludingHistory() const { return m_indexingType; }
141         
142     bool mayInterceptIndexedAccesses() const
143     {
144         return !!(indexingTypeIncludingHistory() & MayHaveIndexedAccessors);
145     }
146         
147     bool anyObjectInChainMayInterceptIndexedAccesses() const;
148         
149     bool needsSlowPutIndexing() const;
150     NonPropertyTransition suggestedArrayStorageTransition() const;
151         
152     JSGlobalObject* globalObject() const { return m_globalObject.get(); }
153     void setGlobalObject(VM& vm, JSGlobalObject* globalObject) { m_globalObject.set(vm, this, globalObject); }
154         
155     JSValue storedPrototype() const { return m_prototype.get(); }
156     JSObject* storedPrototypeObject() const;
157     Structure* storedPrototypeStructure() const;
158     JSValue prototypeForLookup(ExecState*) const;
159     JSValue prototypeForLookup(JSGlobalObject*) const;
160     JSValue prototypeForLookup(CodeBlock*) const;
161     StructureChain* prototypeChain(VM&, JSGlobalObject*) const;
162     StructureChain* prototypeChain(ExecState*) const;
163     static void visitChildren(JSCell*, SlotVisitor&);
164         
165     // Will just the prototype chain intercept this property access?
166     bool prototypeChainMayInterceptStoreTo(VM&, PropertyName);
167         
168     bool transitionDidInvolveSpecificValue() const { return !!m_specificValueInPrevious; }
169         
170     Structure* previousID() const
171     {
172         ASSERT(structure()->classInfo() == info());
173         if (typeInfo().structureHasRareData())
174             return rareData()->previousID();
175         return previous();
176     }
177     bool transitivelyTransitionedFrom(Structure* structureToFind);
178
179     unsigned outOfLineCapacity() const
180     {
181         ASSERT(checkOffsetConsistency());
182             
183         unsigned outOfLineSize = this->outOfLineSize();
184
185         if (!outOfLineSize)
186             return 0;
187
188         if (outOfLineSize <= initialOutOfLineCapacity)
189             return initialOutOfLineCapacity;
190
191         ASSERT(outOfLineSize > initialOutOfLineCapacity);
192         COMPILE_ASSERT(outOfLineGrowthFactor == 2, outOfLineGrowthFactor_is_two);
193         return WTF::roundUpToPowerOfTwo(outOfLineSize);
194     }
195     unsigned outOfLineSize() const
196     {
197         ASSERT(checkOffsetConsistency());
198         ASSERT(structure()->classInfo() == info());
199             
200         return numberOfOutOfLineSlotsForLastOffset(m_offset);
201     }
202     bool hasInlineStorage() const
203     {
204         return !!m_inlineCapacity;
205     }
206     unsigned inlineCapacity() const
207     {
208         return m_inlineCapacity;
209     }
210     unsigned inlineSize() const
211     {
212         return std::min<unsigned>(m_offset + 1, m_inlineCapacity);
213     }
214     unsigned totalStorageSize() const
215     {
216         return numberOfSlotsForLastOffset(m_offset, m_inlineCapacity);
217     }
218     unsigned totalStorageCapacity() const
219     {
220         ASSERT(structure()->classInfo() == info());
221         return outOfLineCapacity() + inlineCapacity();
222     }
223
224     bool isValidOffset(PropertyOffset offset) const
225     {
226         return JSC::isValidOffset(offset)
227             && offset <= m_offset
228             && (offset < m_inlineCapacity || offset >= firstOutOfLineOffset);
229     }
230     
231     bool couldHaveIndexingHeader() const
232     {
233         return hasIndexedProperties(indexingType());
234     }
235     
236     bool hasIndexingHeader(const JSCell*) const
237     {
238         return hasIndexedProperties(indexingType());
239     }
240     
241     bool masqueradesAsUndefined(JSGlobalObject* lexicalGlobalObject);
242
243     PropertyOffset get(VM&, PropertyName);
244     PropertyOffset get(VM&, const WTF::String& name);
245     JS_EXPORT_PRIVATE PropertyOffset get(VM&, PropertyName, unsigned& attributes, JSCell*& specificValue);
246
247     PropertyOffset getConcurrently(VM&, StringImpl* uid);
248     PropertyOffset getConcurrently(VM&, StringImpl* uid, unsigned& attributes, JSCell*& specificValue);
249
250     bool hasGetterSetterProperties() const { return m_hasGetterSetterProperties; }
251     bool hasReadOnlyOrGetterSetterPropertiesExcludingProto() const { return m_hasReadOnlyOrGetterSetterPropertiesExcludingProto; }
252     void setHasGetterSetterProperties(bool is__proto__)
253     {
254         m_hasGetterSetterProperties = true;
255         if (!is__proto__)
256             m_hasReadOnlyOrGetterSetterPropertiesExcludingProto = true;
257     }
258     void setContainsReadOnlyProperties()
259     {
260         m_hasReadOnlyOrGetterSetterPropertiesExcludingProto = true;
261     }
262
263     bool hasNonEnumerableProperties() const { return m_hasNonEnumerableProperties; }
264         
265     bool isEmpty() const
266     {
267         ASSERT(checkOffsetConsistency());
268         return !JSC::isValidOffset(m_offset);
269     }
270
271     JS_EXPORT_PRIVATE void despecifyDictionaryFunction(VM&, PropertyName);
272     void disableSpecificFunctionTracking() { m_specificFunctionThrashCount = maxSpecificFunctionThrashCount; }
273
274     void setEnumerationCache(VM&, JSPropertyNameIterator* enumerationCache); // Defined in JSPropertyNameIterator.h.
275     JSPropertyNameIterator* enumerationCache(); // Defined in JSPropertyNameIterator.h.
276     void getPropertyNamesFromStructure(VM&, PropertyNameArray&, EnumerationMode);
277
278     JSString* objectToStringValue()
279     {
280         if (!typeInfo().structureHasRareData())
281             return 0;
282         return rareData()->objectToStringValue();
283     }
284
285     void setObjectToStringValue(VM& vm, const JSCell* owner, JSString* value)
286     {
287         if (!typeInfo().structureHasRareData())
288             allocateRareData(vm);
289         rareData()->setObjectToStringValue(vm, owner, value);
290     }
291
292     bool staticFunctionsReified()
293     {
294         return m_staticFunctionReified;
295     }
296
297     void setStaticFunctionsReified()
298     {
299         m_staticFunctionReified = true;
300     }
301
302     const ClassInfo* classInfo() const { return m_classInfo; }
303
304     static ptrdiff_t prototypeOffset()
305     {
306         return OBJECT_OFFSETOF(Structure, m_prototype);
307     }
308
309     static ptrdiff_t globalObjectOffset()
310     {
311         return OBJECT_OFFSETOF(Structure, m_globalObject);
312     }
313
314     static ptrdiff_t typeInfoFlagsOffset()
315     {
316         return OBJECT_OFFSETOF(Structure, m_typeInfo) + TypeInfo::flagsOffset();
317     }
318
319     static ptrdiff_t typeInfoTypeOffset()
320     {
321         return OBJECT_OFFSETOF(Structure, m_typeInfo) + TypeInfo::typeOffset();
322     }
323         
324     static ptrdiff_t classInfoOffset()
325     {
326         return OBJECT_OFFSETOF(Structure, m_classInfo);
327     }
328         
329     static ptrdiff_t indexingTypeOffset()
330     {
331         return OBJECT_OFFSETOF(Structure, m_indexingType);
332     }
333
334     static Structure* createStructure(VM&);
335         
336     bool transitionWatchpointSetHasBeenInvalidated() const
337     {
338         return m_transitionWatchpointSet.hasBeenInvalidated();
339     }
340         
341     bool transitionWatchpointSetIsStillValid() const
342     {
343         return m_transitionWatchpointSet.isStillValid();
344     }
345         
346     void addTransitionWatchpoint(Watchpoint* watchpoint) const
347     {
348         ASSERT(transitionWatchpointSetIsStillValid());
349         m_transitionWatchpointSet.add(watchpoint);
350     }
351         
352     void notifyTransitionFromThisStructure() const
353     {
354         m_transitionWatchpointSet.notifyWrite();
355     }
356     
357     InlineWatchpointSet& transitionWatchpointSet() const
358     {
359         return m_transitionWatchpointSet;
360     }
361     
362     void dump(PrintStream&) const;
363     void dumpInContext(PrintStream&, DumpContext*) const;
364     void dumpBrief(PrintStream&, const CString&) const;
365     
366     static void dumpContextHeader(PrintStream&);
367     
368     DECLARE_EXPORT_INFO;
369
370 private:
371     friend class LLIntOffsetsExtractor;
372
373     JS_EXPORT_PRIVATE Structure(VM&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType, unsigned inlineCapacity);
374     Structure(VM&);
375     Structure(VM&, const Structure*);
376
377     static Structure* create(VM&, const Structure*);
378     
379     static Structure* addPropertyTransitionToExistingStructureImpl(Structure*, StringImpl* uid, unsigned attributes, JSCell* specificValue, PropertyOffset&);
380
381     // This will return the structure that has a usable property table, that property table,
382     // and the list of structures that we visited before we got to it. If it returns a
383     // non-null structure, it will also lock the structure that it returns; it is your job
384     // to unlock it.
385     void findStructuresAndMapForMaterialization(Vector<Structure*, 8>& structures, Structure*&, PropertyTable*&);
386     
387     typedef enum { 
388         NoneDictionaryKind = 0,
389         CachedDictionaryKind = 1,
390         UncachedDictionaryKind = 2
391     } DictionaryKind;
392     static Structure* toDictionaryTransition(VM&, Structure*, DictionaryKind);
393
394     PropertyOffset putSpecificValue(VM&, PropertyName, unsigned attributes, JSCell* specificValue);
395     PropertyOffset remove(PropertyName);
396
397     void createPropertyMap(const ConcurrentJITLocker&, VM&, unsigned keyCount = 0);
398     void checkConsistency();
399
400     bool despecifyFunction(VM&, PropertyName);
401     void despecifyAllFunctions(VM&);
402
403     WriteBarrier<PropertyTable>& propertyTable();
404     PropertyTable* takePropertyTableOrCloneIfPinned(VM&, Structure* owner);
405     PropertyTable* copyPropertyTable(VM&, Structure* owner);
406     PropertyTable* copyPropertyTableForPinning(VM&, Structure* owner);
407     JS_EXPORT_PRIVATE void materializePropertyMap(VM&);
408     void materializePropertyMapIfNecessary(VM& vm)
409     {
410         ASSERT(!isCompilationThread());
411         ASSERT(structure()->classInfo() == info());
412         ASSERT(checkOffsetConsistency());
413         if (!propertyTable() && previousID())
414             materializePropertyMap(vm);
415     }
416     void materializePropertyMapIfNecessaryForPinning(VM& vm)
417     {
418         ASSERT(structure()->classInfo() == info());
419         checkOffsetConsistency();
420         if (!propertyTable())
421             materializePropertyMap(vm);
422     }
423
424     void setPreviousID(VM& vm, Structure* transition, Structure* structure)
425     {
426         if (typeInfo().structureHasRareData())
427             rareData()->setPreviousID(vm, transition, structure);
428         else
429             m_previousOrRareData.set(vm, transition, structure);
430     }
431
432     void clearPreviousID()
433     {
434         if (typeInfo().structureHasRareData())
435             rareData()->clearPreviousID();
436         else
437             m_previousOrRareData.clear();
438     }
439
440     int transitionCount() const
441     {
442         // Since the number of transitions is always the same as m_offset, we keep the size of Structure down by not storing both.
443         return numberOfSlotsForLastOffset(m_offset, m_inlineCapacity);
444     }
445
446     bool isValid(JSGlobalObject*, StructureChain* cachedPrototypeChain) const;
447     bool isValid(ExecState*, StructureChain* cachedPrototypeChain) const;
448         
449     void pin();
450
451     Structure* previous() const
452     {
453         ASSERT(!typeInfo().structureHasRareData());
454         return static_cast<Structure*>(m_previousOrRareData.get());
455     }
456
457     StructureRareData* rareData() const
458     {
459         ASSERT(typeInfo().structureHasRareData());
460         return static_cast<StructureRareData*>(m_previousOrRareData.get());
461     }
462         
463     bool checkOffsetConsistency() const;
464
465     void allocateRareData(VM&);
466     void cloneRareDataFrom(VM&, const Structure*);
467
468     static const int s_maxTransitionLength = 64;
469
470     static const unsigned maxSpecificFunctionThrashCount = 3;
471         
472     WriteBarrier<JSGlobalObject> m_globalObject;
473     WriteBarrier<Unknown> m_prototype;
474     mutable WriteBarrier<StructureChain> m_cachedPrototypeChain;
475
476     WriteBarrier<JSCell> m_previousOrRareData;
477
478     RefPtr<StringImpl> m_nameInPrevious;
479     WriteBarrier<JSCell> m_specificValueInPrevious;
480
481     const ClassInfo* m_classInfo;
482
483     StructureTransitionTable m_transitionTable;
484
485     // Should be accessed through propertyTable(). During GC, it may be set to 0 by another thread.
486     WriteBarrier<PropertyTable> m_propertyTableUnsafe;
487
488     mutable InlineWatchpointSet m_transitionWatchpointSet;
489
490     COMPILE_ASSERT(firstOutOfLineOffset < 256, firstOutOfLineOffset_fits);
491
492     // m_offset does not account for anonymous slots
493     PropertyOffset m_offset;
494
495     TypeInfo m_typeInfo;
496     IndexingType m_indexingType;
497     uint8_t m_inlineCapacity;
498     
499     ConcurrentJITLock m_lock;
500     
501     unsigned m_dictionaryKind : 2;
502     bool m_isPinnedPropertyTable : 1;
503     bool m_hasGetterSetterProperties : 1;
504     bool m_hasReadOnlyOrGetterSetterPropertiesExcludingProto : 1;
505     bool m_hasNonEnumerableProperties : 1;
506     unsigned m_attributesInPrevious : 14;
507     unsigned m_specificFunctionThrashCount : 2;
508     unsigned m_preventExtensions : 1;
509     unsigned m_didTransition : 1;
510     unsigned m_staticFunctionReified;
511 };
512
513 } // namespace JSC
514
515 #endif // Structure_h