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