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 "Identifier.h"
30#include "JSCell.h"
31#include "JSType.h"
32#include "JSValue.h"
33#include "PropertyMapHashTable.h"
34#include "PropertyNameArray.h"
35#include "Protect.h"
36#include "StructureTransitionTable.h"
37#include "JSTypeInfo.h"
38#include "UString.h"
39#include "Weak.h"
40#include <wtf/PassRefPtr.h>
41#include <wtf/RefCounted.h>
42
43
44namespace JSC {
45
46    class MarkStack;
47    class PropertyNameArray;
48    class PropertyNameArrayData;
49    class StructureChain;
50
51    struct ClassInfo;
52
53    enum EnumerationMode {
54        ExcludeDontEnumProperties,
55        IncludeDontEnumProperties
56    };
57
58    class Structure : public JSCell {
59    public:
60        friend class StructureTransitionTable;
61        static Structure* create(JSGlobalData& globalData, JSValue prototype, const TypeInfo& typeInfo, unsigned anonymousSlotCount, const ClassInfo* classInfo)
62        {
63            ASSERT(globalData.structureStructure);
64            return new (&globalData) Structure(globalData, prototype, typeInfo, anonymousSlotCount, classInfo);
65        }
66
67        static void dumpStatistics();
68
69        static Structure* addPropertyTransition(JSGlobalData&, Structure*, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset);
70        static Structure* addPropertyTransitionToExistingStructure(Structure*, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset);
71        static Structure* removePropertyTransition(JSGlobalData&, Structure*, const Identifier& propertyName, size_t& offset);
72        static Structure* changePrototypeTransition(JSGlobalData&, Structure*, JSValue prototype);
73        static Structure* despecifyFunctionTransition(JSGlobalData&, Structure*, const Identifier&);
74        static Structure* getterSetterTransition(JSGlobalData&, Structure*);
75        static Structure* toCacheableDictionaryTransition(JSGlobalData&, Structure*);
76        static Structure* toUncacheableDictionaryTransition(JSGlobalData&, Structure*);
77        static Structure* sealTransition(JSGlobalData&, Structure*);
78        static Structure* freezeTransition(JSGlobalData&, Structure*);
79        static Structure* preventExtensionsTransition(JSGlobalData&, Structure*);
80
81        bool isSealed(JSGlobalData&);
82        bool isFrozen(JSGlobalData&);
83        bool isExtensible() const { return !m_preventExtensions; }
84
85        Structure* flattenDictionaryStructure(JSGlobalData&, JSObject*);
86
87        ~Structure();
88
89        // These should be used with caution.
90        size_t addPropertyWithoutTransition(JSGlobalData&, const Identifier& propertyName, unsigned attributes, JSCell* specificValue);
91        size_t removePropertyWithoutTransition(JSGlobalData&, const Identifier& propertyName);
92        void setPrototypeWithoutTransition(JSGlobalData& globalData, JSValue prototype) { m_prototype.set(globalData, this, prototype); }
93
94        bool isDictionary() const { return m_dictionaryKind != NoneDictionaryKind; }
95        bool isUncacheableDictionary() const { return m_dictionaryKind == UncachedDictionaryKind; }
96
97        const TypeInfo& typeInfo() const { return m_typeInfo; }
98
99        JSValue storedPrototype() const { return m_prototype.get(); }
100        JSValue prototypeForLookup(ExecState*) const;
101        StructureChain* prototypeChain(ExecState*) const;
102        void markChildren(MarkStack&);
103
104        Structure* previousID() const { return m_previous.get(); }
105
106        void growPropertyStorageCapacity();
107        unsigned propertyStorageCapacity() const { return m_propertyStorageCapacity; }
108        unsigned propertyStorageSize() const { return m_anonymousSlotCount + (m_propertyTable ? m_propertyTable->propertyStorageSize() : static_cast<unsigned>(m_offset + 1)); }
109        bool isUsingInlineStorage() const;
110
111        size_t get(JSGlobalData&, const Identifier& propertyName);
112        size_t get(JSGlobalData&, StringImpl* propertyName, unsigned& attributes, JSCell*& specificValue);
113        size_t get(JSGlobalData& globalData, const Identifier& propertyName, unsigned& attributes, JSCell*& specificValue)
114        {
115            ASSERT(!propertyName.isNull());
116            return get(globalData, propertyName.impl(), attributes, specificValue);
117        }
118
119        bool hasGetterSetterProperties() const { return m_hasGetterSetterProperties; }
120        void setHasGetterSetterProperties(bool hasGetterSetterProperties) { m_hasGetterSetterProperties = hasGetterSetterProperties; }
121
122        bool hasNonEnumerableProperties() const { return m_hasNonEnumerableProperties; }
123
124        bool hasAnonymousSlots() const { return !!m_anonymousSlotCount; }
125        unsigned anonymousSlotCount() const { return m_anonymousSlotCount; }
126
127        bool isEmpty() const { return m_propertyTable ? m_propertyTable->isEmpty() : m_offset == noOffset; }
128
129        void despecifyDictionaryFunction(JSGlobalData&, const Identifier& propertyName);
130        void disableSpecificFunctionTracking() { m_specificFunctionThrashCount = maxSpecificFunctionThrashCount; }
131
132        void setEnumerationCache(JSGlobalData&, JSPropertyNameIterator* enumerationCache); // Defined in JSPropertyNameIterator.h.
133        JSPropertyNameIterator* enumerationCache(); // Defined in JSPropertyNameIterator.h.
134        void getPropertyNames(JSGlobalData&, PropertyNameArray&, EnumerationMode mode);
135
136        const ClassInfo* classInfo() const { return m_classInfo; }
137
138        static ptrdiff_t prototypeOffset()
139        {
140            return OBJECT_OFFSETOF(Structure, m_prototype);
141        }
142
143        static ptrdiff_t typeInfoFlagsOffset()
144        {
145            return OBJECT_OFFSETOF(Structure, m_typeInfo) + TypeInfo::flagsOffset();
146        }
147
148        static ptrdiff_t typeInfoTypeOffset()
149        {
150            return OBJECT_OFFSETOF(Structure, m_typeInfo) + TypeInfo::typeOffset();
151        }
152
153        static Structure* createStructure(JSGlobalData& globalData)
154        {
155            ASSERT(!globalData.structureStructure);
156            return new (&globalData) Structure(globalData);
157        }
158
159    private:
160        Structure(JSGlobalData&, JSValue prototype, const TypeInfo&, unsigned anonymousSlotCount, const ClassInfo*);
161        Structure(JSGlobalData&);
162        Structure(JSGlobalData&, const Structure*);
163
164        static Structure* create(JSGlobalData& globalData, const Structure* structure)
165        {
166            ASSERT(globalData.structureStructure);
167            return new (&globalData) Structure(globalData, structure);
168        }
169
170        static const ClassInfo s_info;
171
172        typedef enum {
173            NoneDictionaryKind = 0,
174            CachedDictionaryKind = 1,
175            UncachedDictionaryKind = 2
176        } DictionaryKind;
177        static Structure* toDictionaryTransition(JSGlobalData&, Structure*, DictionaryKind);
178
179        size_t putSpecificValue(JSGlobalData&, const Identifier& propertyName, unsigned attributes, JSCell* specificValue);
180        size_t remove(const Identifier& propertyName);
181
182        void createPropertyMap(unsigned keyCount = 0);
183        void checkConsistency();
184
185        bool despecifyFunction(JSGlobalData&, const Identifier&);
186        void despecifyAllFunctions(JSGlobalData&);
187
188        PropertyTable* copyPropertyTable(JSGlobalData&, Structure* owner);
189        void materializePropertyMap(JSGlobalData&);
190        void materializePropertyMapIfNecessary(JSGlobalData& globalData)
191        {
192            if (!m_propertyTable && m_previous)
193                materializePropertyMap(globalData);
194        }
195
196        signed char transitionCount() const
197        {
198            // Since the number of transitions is always the same as m_offset, we keep the size of Structure down by not storing both.
199            return m_offset == noOffset ? 0 : m_offset + 1;
200        }
201
202        bool isValid(ExecState*, StructureChain* cachedPrototypeChain) const;
203
204        static const signed char s_maxTransitionLength = 64;
205
206        static const signed char noOffset = -1;
207
208        static const unsigned maxSpecificFunctionThrashCount = 3;
209
210        TypeInfo m_typeInfo;
211
212        WriteBarrier<Unknown> m_prototype;
213        mutable WriteBarrier<StructureChain> m_cachedPrototypeChain;
214
215        WriteBarrier<Structure> m_previous;
216        RefPtr<StringImpl> m_nameInPrevious;
217        WriteBarrier<JSCell> m_specificValueInPrevious;
218
219        const ClassInfo* m_classInfo;
220
221        StructureTransitionTable m_transitionTable;
222
223        WriteBarrier<JSPropertyNameIterator> m_enumerationCache;
224
225        OwnPtr<PropertyTable> m_propertyTable;
226
227        uint32_t m_propertyStorageCapacity;
228
229        // m_offset does not account for anonymous slots
230        signed char m_offset;
231
232        unsigned m_dictionaryKind : 2;
233        bool m_isPinnedPropertyTable : 1;
234        bool m_hasGetterSetterProperties : 1;
235        bool m_hasNonEnumerableProperties : 1;
236#if COMPILER(WINSCW)
237        // Workaround for Symbian WINSCW compiler that cannot resolve unsigned type of the declared
238        // bitfield, when used as argument in make_pair() function calls in structure.ccp.
239        // This bitfield optimization is insignificant for the Symbian emulator target.
240        unsigned m_attributesInPrevious;
241#else
242        unsigned m_attributesInPrevious : 7;
243#endif
244        unsigned m_specificFunctionThrashCount : 2;
245        unsigned m_anonymousSlotCount : 5;
246        unsigned m_preventExtensions : 1;
247        // 4 free bits
248    };
249
250    inline size_t Structure::get(JSGlobalData& globalData, const Identifier& propertyName)
251    {
252        materializePropertyMapIfNecessary(globalData);
253        if (!m_propertyTable)
254            return notFound;
255
256        PropertyMapEntry* entry = m_propertyTable->find(propertyName.impl()).first;
257        ASSERT(!entry || entry->offset >= m_anonymousSlotCount);
258        return entry ? entry->offset : notFound;
259    }
260
261    inline bool JSCell::isObject() const
262    {
263        return m_structure->typeInfo().type() == ObjectType;
264    }
265
266    inline bool JSCell::isString() const
267    {
268        return m_structure->typeInfo().type() == StringType;
269    }
270
271    inline const ClassInfo* JSCell::classInfo() const
272    {
273        return m_structure->classInfo();
274    }
275
276    inline Structure* JSCell::createDummyStructure(JSGlobalData& globalData)
277    {
278        return Structure::create(globalData, jsNull(), TypeInfo(UnspecifiedType), AnonymousSlotCount, 0);
279    }
280
281    inline bool JSValue::needsThisConversion() const
282    {
283        if (UNLIKELY(!isCell()))
284            return true;
285        return asCell()->structure()->typeInfo().needsThisConversion();
286    }
287
288    ALWAYS_INLINE void MarkStack::internalAppend(JSCell* cell)
289    {
290        ASSERT(!m_isCheckingForDefaultMarkViolation);
291        ASSERT(cell);
292        if (Heap::testAndSetMarked(cell))
293            return;
294        if (cell->structure()->typeInfo().type() >= CompoundType)
295            m_values.append(cell);
296    }
297
298    inline StructureTransitionTable::Hash::Key StructureTransitionTable::keyForWeakGCMapFinalizer(void*, Structure* structure)
299    {
300        return Hash::Key(structure->m_nameInPrevious.get(), structure->m_attributesInPrevious);
301    }
302
303} // namespace JSC
304
305#endif // Structure_h
306