DFG should inline prototype chain accesses, and do the right things if the
[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         JSValue prototypeForLookup(JSGlobalObject*) const;
131         JSValue prototypeForLookup(CodeBlock*) const;
132         StructureChain* prototypeChain(ExecState*) const;
133         static void visitChildren(JSCell*, SlotVisitor&);
134
135         Structure* previousID() const { ASSERT(structure()->classInfo() == &s_info); return m_previous.get(); }
136         bool transitivelyTransitionedFrom(Structure* structureToFind);
137
138         void growPropertyStorageCapacity();
139         unsigned propertyStorageCapacity() const { ASSERT(structure()->classInfo() == &s_info); return m_propertyStorageCapacity; }
140         unsigned propertyStorageSize() const { ASSERT(structure()->classInfo() == &s_info); return (m_propertyTable ? m_propertyTable->propertyStorageSize() : static_cast<unsigned>(m_offset + 1)); }
141         bool isUsingInlineStorage() const;
142
143         size_t get(JSGlobalData&, PropertyName);
144         size_t get(JSGlobalData&, const UString& name);
145         JS_EXPORT_PRIVATE size_t get(JSGlobalData&, PropertyName, unsigned& attributes, JSCell*& specificValue);
146
147         bool hasGetterSetterProperties() const { return m_hasGetterSetterProperties; }
148         bool hasReadOnlyOrGetterSetterPropertiesExcludingProto() const { return m_hasReadOnlyOrGetterSetterPropertiesExcludingProto; }
149         void setHasGetterSetterProperties(bool is__proto__)
150         {
151             m_hasGetterSetterProperties = true;
152             if (!is__proto__)
153                 m_hasReadOnlyOrGetterSetterPropertiesExcludingProto = true;
154         }
155         void setContainsReadOnlyProperties()
156         {
157             m_hasReadOnlyOrGetterSetterPropertiesExcludingProto = true;
158         }
159
160         bool hasNonEnumerableProperties() const { return m_hasNonEnumerableProperties; }
161         
162         bool isEmpty() const { return m_propertyTable ? m_propertyTable->isEmpty() : m_offset == noOffset; }
163
164         JS_EXPORT_PRIVATE void despecifyDictionaryFunction(JSGlobalData&, PropertyName);
165         void disableSpecificFunctionTracking() { m_specificFunctionThrashCount = maxSpecificFunctionThrashCount; }
166
167         void setEnumerationCache(JSGlobalData&, JSPropertyNameIterator* enumerationCache); // Defined in JSPropertyNameIterator.h.
168         JSPropertyNameIterator* enumerationCache(); // Defined in JSPropertyNameIterator.h.
169         void getPropertyNamesFromStructure(JSGlobalData&, PropertyNameArray&, EnumerationMode);
170
171         JSString* objectToStringValue() { return m_objectToStringValue.get(); }
172
173         void setObjectToStringValue(JSGlobalData& globalData, const JSCell* owner, JSString* value)
174         {
175             m_objectToStringValue.set(globalData, owner, value);
176         }
177
178         bool staticFunctionsReified()
179         {
180             return m_staticFunctionReified;
181         }
182
183         void setStaticFunctionsReified()
184         {
185             m_staticFunctionReified = true;
186         }
187
188         const ClassInfo* classInfo() const { return m_classInfo; }
189
190         static ptrdiff_t prototypeOffset()
191         {
192             return OBJECT_OFFSETOF(Structure, m_prototype);
193         }
194
195         static ptrdiff_t typeInfoFlagsOffset()
196         {
197             return OBJECT_OFFSETOF(Structure, m_typeInfo) + TypeInfo::flagsOffset();
198         }
199
200         static ptrdiff_t typeInfoTypeOffset()
201         {
202             return OBJECT_OFFSETOF(Structure, m_typeInfo) + TypeInfo::typeOffset();
203         }
204
205         static Structure* createStructure(JSGlobalData& globalData)
206         {
207             ASSERT(!globalData.structureStructure);
208             Structure* structure = new (NotNull, allocateCell<Structure>(globalData.heap)) Structure(globalData);
209             structure->finishCreation(globalData, CreatingEarlyCell);
210             return structure;
211         }
212         
213         static JS_EXPORTDATA const ClassInfo s_info;
214
215     private:
216         friend class LLIntOffsetsExtractor;
217
218         JS_EXPORT_PRIVATE Structure(JSGlobalData&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*);
219         Structure(JSGlobalData&);
220         Structure(JSGlobalData&, const Structure*);
221
222         static Structure* create(JSGlobalData& globalData, const Structure* structure)
223         {
224             ASSERT(globalData.structureStructure);
225             Structure* newStructure = new (NotNull, allocateCell<Structure>(globalData.heap)) Structure(globalData, structure);
226             newStructure->finishCreation(globalData);
227             return newStructure;
228         }
229         
230         typedef enum { 
231             NoneDictionaryKind = 0,
232             CachedDictionaryKind = 1,
233             UncachedDictionaryKind = 2
234         } DictionaryKind;
235         static Structure* toDictionaryTransition(JSGlobalData&, Structure*, DictionaryKind);
236
237         size_t putSpecificValue(JSGlobalData&, PropertyName, unsigned attributes, JSCell* specificValue);
238         size_t remove(PropertyName);
239
240         void createPropertyMap(unsigned keyCount = 0);
241         void checkConsistency();
242
243         bool despecifyFunction(JSGlobalData&, PropertyName);
244         void despecifyAllFunctions(JSGlobalData&);
245
246         PassOwnPtr<PropertyTable> copyPropertyTable(JSGlobalData&, Structure* owner);
247         PassOwnPtr<PropertyTable> copyPropertyTableForPinning(JSGlobalData&, Structure* owner);
248         JS_EXPORT_PRIVATE void materializePropertyMap(JSGlobalData&);
249         void materializePropertyMapIfNecessary(JSGlobalData& globalData)
250         {
251             ASSERT(structure()->classInfo() == &s_info);
252             if (!m_propertyTable && m_previous)
253                 materializePropertyMap(globalData);
254         }
255         void materializePropertyMapIfNecessaryForPinning(JSGlobalData& globalData)
256         {
257             ASSERT(structure()->classInfo() == &s_info);
258             if (!m_propertyTable)
259                 materializePropertyMap(globalData);
260         }
261
262         int transitionCount() const
263         {
264             // Since the number of transitions is always the same as m_offset, we keep the size of Structure down by not storing both.
265             return m_offset == noOffset ? 0 : m_offset + 1;
266         }
267
268         bool isValid(ExecState*, StructureChain* cachedPrototypeChain) const;
269         
270         void pin();
271
272         static const int s_maxTransitionLength = 64;
273
274         static const int noOffset = -1;
275
276         static const unsigned maxSpecificFunctionThrashCount = 3;
277
278         TypeInfo m_typeInfo;
279         
280         WriteBarrier<JSGlobalObject> m_globalObject;
281         WriteBarrier<Unknown> m_prototype;
282         mutable WriteBarrier<StructureChain> m_cachedPrototypeChain;
283
284         WriteBarrier<Structure> m_previous;
285         RefPtr<StringImpl> m_nameInPrevious;
286         WriteBarrier<JSCell> m_specificValueInPrevious;
287
288         const ClassInfo* m_classInfo;
289
290         StructureTransitionTable m_transitionTable;
291
292         WriteBarrier<JSPropertyNameIterator> m_enumerationCache;
293
294         OwnPtr<PropertyTable> m_propertyTable;
295
296         uint32_t m_propertyStorageCapacity;
297
298         WriteBarrier<JSString> m_objectToStringValue;
299
300         // m_offset does not account for anonymous slots
301         int m_offset;
302
303         unsigned m_dictionaryKind : 2;
304         bool m_isPinnedPropertyTable : 1;
305         bool m_hasGetterSetterProperties : 1;
306         bool m_hasReadOnlyOrGetterSetterPropertiesExcludingProto : 1;
307         bool m_hasNonEnumerableProperties : 1;
308         unsigned m_attributesInPrevious : 7;
309         unsigned m_specificFunctionThrashCount : 2;
310         unsigned m_preventExtensions : 1;
311         unsigned m_didTransition : 1;
312         unsigned m_staticFunctionReified;
313     };
314
315     inline size_t Structure::get(JSGlobalData& globalData, PropertyName propertyName)
316     {
317         ASSERT(structure()->classInfo() == &s_info);
318         materializePropertyMapIfNecessary(globalData);
319         if (!m_propertyTable)
320             return notFound;
321
322         PropertyMapEntry* entry = m_propertyTable->find(propertyName.uid()).first;
323         return entry ? entry->offset : notFound;
324     }
325
326     inline size_t Structure::get(JSGlobalData& globalData, const UString& name)
327     {
328         ASSERT(structure()->classInfo() == &s_info);
329         materializePropertyMapIfNecessary(globalData);
330         if (!m_propertyTable)
331             return notFound;
332
333         PropertyMapEntry* entry = m_propertyTable->findWithString(name.impl()).first;
334         return entry ? entry->offset : notFound;
335     }
336     
337     inline JSValue JSValue::structureOrUndefined() const
338     {
339         if (isCell())
340             return JSValue(asCell()->structure());
341         return jsUndefined();
342     }
343
344     inline bool JSCell::isObject() const
345     {
346         return m_structure->isObject();
347     }
348
349     inline bool JSCell::isString() const
350     {
351         return m_structure->typeInfo().type() == StringType;
352     }
353
354     inline bool JSCell::isGetterSetter() const
355     {
356         return m_structure->typeInfo().type() == GetterSetterType;
357     }
358
359     inline bool JSCell::isAPIValueWrapper() const
360     {
361         return m_structure->typeInfo().type() == APIValueWrapperType;
362     }
363
364     inline void JSCell::setStructure(JSGlobalData& globalData, Structure* structure)
365     {
366         ASSERT(structure->typeInfo().overridesVisitChildren() == this->structure()->typeInfo().overridesVisitChildren());
367         ASSERT(structure->classInfo() == m_structure->classInfo());
368         m_structure.set(globalData, this, structure);
369     }
370
371     inline const ClassInfo* JSCell::validatedClassInfo() const
372     {
373 #if ENABLE(GC_VALIDATION)
374         ASSERT(m_structure.unvalidatedGet()->classInfo() == m_classInfo);
375 #else
376         ASSERT(m_structure->classInfo() == m_classInfo);
377 #endif
378         return m_classInfo;
379     }
380
381     ALWAYS_INLINE void MarkStack::internalAppend(JSCell* cell)
382     {
383         ASSERT(!m_isCheckingForDefaultMarkViolation);
384 #if ENABLE(GC_VALIDATION)
385         validate(cell);
386 #endif
387         if (Heap::testAndSetMarked(cell) || !cell->structure())
388             return;
389
390         m_visitCount++;
391         
392         MARK_LOG_CHILD(*this, cell);
393
394         // Should never attempt to mark something that is zapped.
395         ASSERT(!cell->isZapped());
396         
397         m_stack.append(cell);
398     }
399
400     inline StructureTransitionTable::Hash::Key StructureTransitionTable::keyForWeakGCMapFinalizer(void*, Structure* structure)
401     {
402         // Newer versions of the STL have an std::make_pair function that takes rvalue references.
403         // 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.
404         // See https://bugs.webkit.org/show_bug.cgi?id=59261 for more details.
405         return Hash::Key(structure->m_nameInPrevious.get(), +structure->m_attributesInPrevious);
406     }
407
408     inline bool Structure::transitivelyTransitionedFrom(Structure* structureToFind)
409     {
410         for (Structure* current = this; current; current = current->previousID()) {
411             if (current == structureToFind)
412                 return true;
413         }
414         return false;
415     }
416
417     inline JSCell::JSCell(JSGlobalData& globalData, Structure* structure)
418         : m_classInfo(structure->classInfo())
419         , m_structure(globalData, this, structure)
420     {
421     }
422
423     inline void JSCell::finishCreation(JSGlobalData& globalData, Structure* structure, CreatingEarlyCellTag)
424     {
425 #if ENABLE(GC_VALIDATION)
426         ASSERT(globalData.isInitializingObject());
427         globalData.setInitializingObjectClass(0);
428         if (structure)
429 #endif
430             m_structure.setEarlyValue(globalData, this, structure);
431         m_classInfo = structure->classInfo();
432         // Very first set of allocations won't have a real structure.
433         ASSERT(m_structure || !globalData.structureStructure);
434     }
435
436 } // namespace JSC
437
438 #endif // Structure_h