230f59d65133e57b54fe6dd3c38788716976cb69
[WebKit-https.git] / Source / JavaScriptCore / runtime / Structure.h
1 /*
2  * Copyright (C) 2008, 2009 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 "JSCell.h"
31 #include "JSType.h"
32 #include "JSValue.h"
33 #include "PropertyMapHashTable.h"
34 #include "PropertyName.h"
35 #include "PropertyNameArray.h"
36 #include "Protect.h"
37 #include "StructureTransitionTable.h"
38 #include "JSTypeInfo.h"
39 #include "UString.h"
40 #include "Weak.h"
41 #include <wtf/PassOwnPtr.h>
42 #include <wtf/PassRefPtr.h>
43 #include <wtf/RefCounted.h>
44
45
46 namespace JSC {
47
48     class LLIntOffsetsExtractor;
49     class PropertyNameArray;
50     class PropertyNameArrayData;
51     class StructureChain;
52     class SlotVisitor;
53     class JSString;
54
55     class Structure : public JSCell {
56     public:
57         friend class StructureTransitionTable;
58
59         typedef JSCell Base;
60
61         static Structure* create(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo)
62         {
63             ASSERT(globalData.structureStructure);
64             ASSERT(classInfo);
65             Structure* structure = new (NotNull, allocateCell<Structure>(globalData.heap)) Structure(globalData, globalObject, prototype, typeInfo, classInfo);
66             structure->finishCreation(globalData);
67             return structure;
68         }
69
70     protected:
71         void finishCreation(JSGlobalData& globalData)
72         {
73             Base::finishCreation(globalData);
74             ASSERT(m_prototype);
75             ASSERT(m_prototype.isObject() || m_prototype.isNull());
76         }
77
78         void finishCreation(JSGlobalData& globalData, CreatingEarlyCellTag)
79         {
80             Base::finishCreation(globalData, this, CreatingEarlyCell);
81             ASSERT(m_prototype);
82             ASSERT(m_prototype.isNull());
83             ASSERT(!globalData.structureStructure);
84         }
85
86     public:
87         static void dumpStatistics();
88
89         JS_EXPORT_PRIVATE static Structure* addPropertyTransition(JSGlobalData&, Structure*, PropertyName, unsigned attributes, JSCell* specificValue, size_t& offset);
90         JS_EXPORT_PRIVATE static Structure* addPropertyTransitionToExistingStructure(Structure*, PropertyName, unsigned attributes, JSCell* specificValue, size_t& offset);
91         static Structure* removePropertyTransition(JSGlobalData&, Structure*, PropertyName, size_t& offset);
92         JS_EXPORT_PRIVATE static Structure* changePrototypeTransition(JSGlobalData&, Structure*, JSValue prototype);
93         JS_EXPORT_PRIVATE static Structure* despecifyFunctionTransition(JSGlobalData&, Structure*, PropertyName);
94         static Structure* attributeChangeTransition(JSGlobalData&, Structure*, PropertyName, unsigned attributes);
95         static Structure* toCacheableDictionaryTransition(JSGlobalData&, Structure*);
96         static Structure* toUncacheableDictionaryTransition(JSGlobalData&, Structure*);
97         static Structure* sealTransition(JSGlobalData&, Structure*);
98         static Structure* freezeTransition(JSGlobalData&, Structure*);
99         static Structure* preventExtensionsTransition(JSGlobalData&, Structure*);
100
101         bool isSealed(JSGlobalData&);
102         bool isFrozen(JSGlobalData&);
103         bool isExtensible() const { return !m_preventExtensions; }
104         bool didTransition() const { return m_didTransition; }
105         bool shouldGrowPropertyStorage() { return propertyStorageCapacity() == propertyStorageSize(); }
106         JS_EXPORT_PRIVATE size_t suggestedNewPropertyStorageSize(); 
107
108         Structure* flattenDictionaryStructure(JSGlobalData&, JSObject*);
109
110         static void destroy(JSCell*);
111
112         // These should be used with caution.  
113         JS_EXPORT_PRIVATE size_t addPropertyWithoutTransition(JSGlobalData&, PropertyName, unsigned attributes, JSCell* specificValue);
114         size_t removePropertyWithoutTransition(JSGlobalData&, PropertyName);
115         void setPrototypeWithoutTransition(JSGlobalData& globalData, JSValue prototype) { m_prototype.set(globalData, this, prototype); }
116         
117         bool isDictionary() const { return m_dictionaryKind != NoneDictionaryKind; }
118         bool isUncacheableDictionary() const { return m_dictionaryKind == UncachedDictionaryKind; }
119
120         // Type accessors.
121         const TypeInfo& typeInfo() const { ASSERT(structure()->classInfo() == &s_info); return m_typeInfo; }
122         bool isObject() const { return typeInfo().isObject(); }
123
124
125         JSGlobalObject* globalObject() const { return m_globalObject.get(); }
126         void setGlobalObject(JSGlobalData& globalData, JSGlobalObject* globalObject) { m_globalObject.set(globalData, this, globalObject); }
127         
128         JSValue storedPrototype() const { return m_prototype.get(); }
129         JSValue prototypeForLookup(ExecState*) const;
130         StructureChain* prototypeChain(ExecState*) const;
131         static void visitChildren(JSCell*, SlotVisitor&);
132
133         Structure* previousID() const { ASSERT(structure()->classInfo() == &s_info); return m_previous.get(); }
134         bool transitivelyTransitionedFrom(Structure* structureToFind);
135
136         void growPropertyStorageCapacity();
137         unsigned propertyStorageCapacity() const { ASSERT(structure()->classInfo() == &s_info); return m_propertyStorageCapacity; }
138         unsigned propertyStorageSize() const { ASSERT(structure()->classInfo() == &s_info); return (m_propertyTable ? m_propertyTable->propertyStorageSize() : static_cast<unsigned>(m_offset + 1)); }
139         bool isUsingInlineStorage() const;
140
141         size_t get(JSGlobalData&, PropertyName);
142         size_t get(JSGlobalData&, const UString& name);
143         JS_EXPORT_PRIVATE size_t get(JSGlobalData&, PropertyName, unsigned& attributes, JSCell*& specificValue);
144
145         bool hasGetterSetterProperties() const { return m_hasGetterSetterProperties; }
146         bool hasReadOnlyOrGetterSetterPropertiesExcludingProto() const { return m_hasReadOnlyOrGetterSetterPropertiesExcludingProto; }
147         void setHasGetterSetterProperties(bool is__proto__)
148         {
149             m_hasGetterSetterProperties = true;
150             if (!is__proto__)
151                 m_hasReadOnlyOrGetterSetterPropertiesExcludingProto = true;
152         }
153         void setContainsReadOnlyProperties()
154         {
155             m_hasReadOnlyOrGetterSetterPropertiesExcludingProto = true;
156         }
157
158         bool hasNonEnumerableProperties() const { return m_hasNonEnumerableProperties; }
159         
160         bool isEmpty() const { return m_propertyTable ? m_propertyTable->isEmpty() : m_offset == noOffset; }
161
162         JS_EXPORT_PRIVATE void despecifyDictionaryFunction(JSGlobalData&, PropertyName);
163         void disableSpecificFunctionTracking() { m_specificFunctionThrashCount = maxSpecificFunctionThrashCount; }
164
165         void setEnumerationCache(JSGlobalData&, JSPropertyNameIterator* enumerationCache); // Defined in JSPropertyNameIterator.h.
166         JSPropertyNameIterator* enumerationCache(); // Defined in JSPropertyNameIterator.h.
167         void getPropertyNamesFromStructure(JSGlobalData&, PropertyNameArray&, EnumerationMode);
168
169         JSString* objectToStringValue() { return m_objectToStringValue.get(); }
170
171         void setObjectToStringValue(JSGlobalData& globalData, const JSCell* owner, JSString* value)
172         {
173             m_objectToStringValue.set(globalData, owner, value);
174         }
175
176         bool staticFunctionsReified()
177         {
178             return m_staticFunctionReified;
179         }
180
181         void setStaticFunctionsReified()
182         {
183             m_staticFunctionReified = true;
184         }
185
186         const ClassInfo* classInfo() const { return m_classInfo; }
187
188         static ptrdiff_t prototypeOffset()
189         {
190             return OBJECT_OFFSETOF(Structure, m_prototype);
191         }
192
193         static ptrdiff_t typeInfoFlagsOffset()
194         {
195             return OBJECT_OFFSETOF(Structure, m_typeInfo) + TypeInfo::flagsOffset();
196         }
197
198         static ptrdiff_t typeInfoTypeOffset()
199         {
200             return OBJECT_OFFSETOF(Structure, m_typeInfo) + TypeInfo::typeOffset();
201         }
202
203         static Structure* createStructure(JSGlobalData& globalData)
204         {
205             ASSERT(!globalData.structureStructure);
206             Structure* structure = new (NotNull, allocateCell<Structure>(globalData.heap)) Structure(globalData);
207             structure->finishCreation(globalData, CreatingEarlyCell);
208             return structure;
209         }
210         
211         static JS_EXPORTDATA const ClassInfo s_info;
212
213     private:
214         friend class LLIntOffsetsExtractor;
215
216         JS_EXPORT_PRIVATE Structure(JSGlobalData&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*);
217         Structure(JSGlobalData&);
218         Structure(JSGlobalData&, const Structure*);
219
220         static Structure* create(JSGlobalData& globalData, const Structure* structure)
221         {
222             ASSERT(globalData.structureStructure);
223             Structure* newStructure = new (NotNull, allocateCell<Structure>(globalData.heap)) Structure(globalData, structure);
224             newStructure->finishCreation(globalData);
225             return newStructure;
226         }
227         
228         typedef enum { 
229             NoneDictionaryKind = 0,
230             CachedDictionaryKind = 1,
231             UncachedDictionaryKind = 2
232         } DictionaryKind;
233         static Structure* toDictionaryTransition(JSGlobalData&, Structure*, DictionaryKind);
234
235         size_t putSpecificValue(JSGlobalData&, PropertyName, unsigned attributes, JSCell* specificValue);
236         size_t remove(PropertyName);
237
238         void createPropertyMap(unsigned keyCount = 0);
239         void checkConsistency();
240
241         bool despecifyFunction(JSGlobalData&, PropertyName);
242         void despecifyAllFunctions(JSGlobalData&);
243
244         PassOwnPtr<PropertyTable> copyPropertyTable(JSGlobalData&, Structure* owner);
245         PassOwnPtr<PropertyTable> copyPropertyTableForPinning(JSGlobalData&, Structure* owner);
246         JS_EXPORT_PRIVATE void materializePropertyMap(JSGlobalData&);
247         void materializePropertyMapIfNecessary(JSGlobalData& globalData)
248         {
249             ASSERT(structure()->classInfo() == &s_info);
250             if (!m_propertyTable && m_previous)
251                 materializePropertyMap(globalData);
252         }
253         void materializePropertyMapIfNecessaryForPinning(JSGlobalData& globalData)
254         {
255             ASSERT(structure()->classInfo() == &s_info);
256             if (!m_propertyTable)
257                 materializePropertyMap(globalData);
258         }
259
260         int transitionCount() const
261         {
262             // Since the number of transitions is always the same as m_offset, we keep the size of Structure down by not storing both.
263             return m_offset == noOffset ? 0 : m_offset + 1;
264         }
265
266         bool isValid(ExecState*, StructureChain* cachedPrototypeChain) const;
267         
268         void pin();
269
270         static const int s_maxTransitionLength = 64;
271
272         static const int noOffset = -1;
273
274         static const unsigned maxSpecificFunctionThrashCount = 3;
275
276         TypeInfo m_typeInfo;
277         
278         WriteBarrier<JSGlobalObject> m_globalObject;
279         WriteBarrier<Unknown> m_prototype;
280         mutable WriteBarrier<StructureChain> m_cachedPrototypeChain;
281
282         WriteBarrier<Structure> m_previous;
283         RefPtr<StringImpl> m_nameInPrevious;
284         WriteBarrier<JSCell> m_specificValueInPrevious;
285
286         const ClassInfo* m_classInfo;
287
288         StructureTransitionTable m_transitionTable;
289
290         WriteBarrier<JSPropertyNameIterator> m_enumerationCache;
291
292         OwnPtr<PropertyTable> m_propertyTable;
293
294         uint32_t m_propertyStorageCapacity;
295
296         WriteBarrier<JSString> m_objectToStringValue;
297
298         // m_offset does not account for anonymous slots
299         int m_offset;
300
301         unsigned m_dictionaryKind : 2;
302         bool m_isPinnedPropertyTable : 1;
303         bool m_hasGetterSetterProperties : 1;
304         bool m_hasReadOnlyOrGetterSetterPropertiesExcludingProto : 1;
305         bool m_hasNonEnumerableProperties : 1;
306         unsigned m_attributesInPrevious : 7;
307         unsigned m_specificFunctionThrashCount : 2;
308         unsigned m_preventExtensions : 1;
309         unsigned m_didTransition : 1;
310         unsigned m_staticFunctionReified;
311     };
312
313     inline size_t Structure::get(JSGlobalData& globalData, PropertyName propertyName)
314     {
315         ASSERT(structure()->classInfo() == &s_info);
316         materializePropertyMapIfNecessary(globalData);
317         if (!m_propertyTable)
318             return notFound;
319
320         PropertyMapEntry* entry = m_propertyTable->find(propertyName.impl()).first;
321         return entry ? entry->offset : notFound;
322     }
323
324     inline size_t Structure::get(JSGlobalData& globalData, const UString& name)
325     {
326         ASSERT(structure()->classInfo() == &s_info);
327         materializePropertyMapIfNecessary(globalData);
328         if (!m_propertyTable)
329             return notFound;
330
331         PropertyMapEntry* entry = m_propertyTable->findWithString(name.impl()).first;
332         return entry ? entry->offset : notFound;
333     }
334     
335     inline bool JSCell::isObject() const
336     {
337         return m_structure->isObject();
338     }
339
340     inline bool JSCell::isString() const
341     {
342         return m_structure->typeInfo().type() == StringType;
343     }
344
345     inline bool JSCell::isGetterSetter() const
346     {
347         return m_structure->typeInfo().type() == GetterSetterType;
348     }
349
350     inline bool JSCell::isAPIValueWrapper() const
351     {
352         return m_structure->typeInfo().type() == APIValueWrapperType;
353     }
354
355     inline void JSCell::setStructure(JSGlobalData& globalData, Structure* structure)
356     {
357         ASSERT(structure->typeInfo().overridesVisitChildren() == this->structure()->typeInfo().overridesVisitChildren());
358         ASSERT(structure->classInfo() == m_structure->classInfo());
359         m_structure.set(globalData, this, structure);
360     }
361
362     inline const ClassInfo* JSCell::validatedClassInfo() const
363     {
364 #if ENABLE(GC_VALIDATION)
365         ASSERT(m_structure.unvalidatedGet()->classInfo() == m_classInfo);
366 #else
367         ASSERT(m_structure->classInfo() == m_classInfo);
368 #endif
369         return m_classInfo;
370     }
371
372     ALWAYS_INLINE void MarkStack::internalAppend(JSCell* cell)
373     {
374         ASSERT(!m_isCheckingForDefaultMarkViolation);
375 #if ENABLE(GC_VALIDATION)
376         validate(cell);
377 #endif
378         m_visitCount++;
379         if (Heap::testAndSetMarked(cell) || !cell->structure())
380             return;
381         
382         // Should never attempt to mark something that is zapped.
383         ASSERT(!cell->isZapped());
384         
385         m_stack.append(cell);
386     }
387
388     inline StructureTransitionTable::Hash::Key StructureTransitionTable::keyForWeakGCMapFinalizer(void*, Structure* structure)
389     {
390         // Newer versions of the STL have an std::make_pair function that takes rvalue references.
391         // When either of the parameters are bitfields, the C++ compiler will try to bind them as lvalues, which is invalid. To work around this, use unary "+" to make the parameter an rvalue.
392         // See https://bugs.webkit.org/show_bug.cgi?id=59261 for more details.
393         return Hash::Key(structure->m_nameInPrevious.get(), +structure->m_attributesInPrevious);
394     }
395
396     inline bool Structure::transitivelyTransitionedFrom(Structure* structureToFind)
397     {
398         for (Structure* current = this; current; current = current->previousID()) {
399             if (current == structureToFind)
400                 return true;
401         }
402         return false;
403     }
404
405     inline JSCell::JSCell(JSGlobalData& globalData, Structure* structure)
406         : m_classInfo(structure->classInfo())
407         , m_structure(globalData, this, structure)
408     {
409     }
410
411     inline void JSCell::finishCreation(JSGlobalData& globalData, Structure* structure, CreatingEarlyCellTag)
412     {
413 #if ENABLE(GC_VALIDATION)
414         ASSERT(globalData.isInitializingObject());
415         globalData.setInitializingObjectClass(0);
416         if (structure)
417 #endif
418             m_structure.setEarlyValue(globalData, this, structure);
419         m_classInfo = structure->classInfo();
420         // Very first set of allocations won't have a real structure.
421         ASSERT(m_structure || !globalData.structureStructure);
422     }
423
424 } // namespace JSC
425
426 #endif // Structure_h