|
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 "JSType.h" |
|
31 #include "JSValue.h" |
|
32 #include "PropertyMapHashTable.h" |
|
33 #include "PropertyNameArray.h" |
|
34 #include "Protect.h" |
|
35 #include "StructureChain.h" |
|
36 #include "StructureTransitionTable.h" |
|
37 #include "JSTypeInfo.h" |
|
38 #include "UString.h" |
|
39 #include "WeakGCPtr.h" |
|
40 #include <wtf/PassRefPtr.h> |
|
41 #include <wtf/RefCounted.h> |
|
42 |
|
43 #ifndef NDEBUG |
|
44 #define DUMP_PROPERTYMAP_STATS 0 |
|
45 #else |
|
46 #define DUMP_PROPERTYMAP_STATS 0 |
|
47 #endif |
|
48 |
|
49 namespace JSC { |
|
50 |
|
51 class MarkStack; |
|
52 class PropertyNameArray; |
|
53 class PropertyNameArrayData; |
|
54 |
|
55 enum EnumerationMode { |
|
56 ExcludeDontEnumProperties, |
|
57 IncludeDontEnumProperties |
|
58 }; |
|
59 |
|
60 class Structure : public RefCounted<Structure> { |
|
61 public: |
|
62 friend class JIT; |
|
63 friend class StructureTransitionTable; |
|
64 static PassRefPtr<Structure> create(JSValue prototype, const TypeInfo& typeInfo, unsigned anonymousSlotCount) |
|
65 { |
|
66 return adoptRef(new Structure(prototype, typeInfo, anonymousSlotCount)); |
|
67 } |
|
68 |
|
69 static void startIgnoringLeaks(); |
|
70 static void stopIgnoringLeaks(); |
|
71 |
|
72 static void dumpStatistics(); |
|
73 |
|
74 static PassRefPtr<Structure> addPropertyTransition(Structure*, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset); |
|
75 static PassRefPtr<Structure> addPropertyTransitionToExistingStructure(Structure*, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset); |
|
76 static PassRefPtr<Structure> removePropertyTransition(Structure*, const Identifier& propertyName, size_t& offset); |
|
77 static PassRefPtr<Structure> changePrototypeTransition(Structure*, JSValue prototype); |
|
78 static PassRefPtr<Structure> despecifyFunctionTransition(Structure*, const Identifier&); |
|
79 static PassRefPtr<Structure> getterSetterTransition(Structure*); |
|
80 static PassRefPtr<Structure> toCacheableDictionaryTransition(Structure*); |
|
81 static PassRefPtr<Structure> toUncacheableDictionaryTransition(Structure*); |
|
82 |
|
83 PassRefPtr<Structure> flattenDictionaryStructure(JSObject*); |
|
84 |
|
85 ~Structure(); |
|
86 |
|
87 // These should be used with caution. |
|
88 size_t addPropertyWithoutTransition(const Identifier& propertyName, unsigned attributes, JSCell* specificValue); |
|
89 size_t removePropertyWithoutTransition(const Identifier& propertyName); |
|
90 void setPrototypeWithoutTransition(JSValue prototype) { m_prototype = prototype; } |
|
91 |
|
92 bool isDictionary() const { return m_dictionaryKind != NoneDictionaryKind; } |
|
93 bool isUncacheableDictionary() const { return m_dictionaryKind == UncachedDictionaryKind; } |
|
94 |
|
95 const TypeInfo& typeInfo() const { return m_typeInfo; } |
|
96 |
|
97 JSValue storedPrototype() const { return m_prototype; } |
|
98 JSValue prototypeForLookup(ExecState*) const; |
|
99 StructureChain* prototypeChain(ExecState*) const; |
|
100 |
|
101 Structure* previousID() const { return m_previous.get(); } |
|
102 |
|
103 void growPropertyStorageCapacity(); |
|
104 unsigned propertyStorageCapacity() const { return m_propertyStorageCapacity; } |
|
105 unsigned propertyStorageSize() const { return m_anonymousSlotCount + (m_propertyTable ? m_propertyTable->keyCount + (m_propertyTable->deletedOffsets ? m_propertyTable->deletedOffsets->size() : 0) : static_cast<unsigned>(m_offset + 1)); } |
|
106 bool isUsingInlineStorage() const; |
|
107 |
|
108 size_t get(const Identifier& propertyName); |
|
109 size_t get(const UString::Rep* rep, unsigned& attributes, JSCell*& specificValue); |
|
110 size_t get(const Identifier& propertyName, unsigned& attributes, JSCell*& specificValue) |
|
111 { |
|
112 ASSERT(!propertyName.isNull()); |
|
113 return get(propertyName.ustring().rep(), attributes, specificValue); |
|
114 } |
|
115 bool transitionedFor(const JSCell* specificValue) |
|
116 { |
|
117 return m_specificValueInPrevious == specificValue; |
|
118 } |
|
119 bool hasTransition(UString::Rep*, unsigned attributes); |
|
120 bool hasTransition(const Identifier& propertyName, unsigned attributes) |
|
121 { |
|
122 return hasTransition(propertyName._ustring.rep(), attributes); |
|
123 } |
|
124 |
|
125 bool hasGetterSetterProperties() const { return m_hasGetterSetterProperties; } |
|
126 void setHasGetterSetterProperties(bool hasGetterSetterProperties) { m_hasGetterSetterProperties = hasGetterSetterProperties; } |
|
127 |
|
128 bool hasNonEnumerableProperties() const { return m_hasNonEnumerableProperties; } |
|
129 |
|
130 bool hasAnonymousSlots() const { return !!m_anonymousSlotCount; } |
|
131 unsigned anonymousSlotCount() const { return m_anonymousSlotCount; } |
|
132 |
|
133 bool isEmpty() const { return m_propertyTable ? !m_propertyTable->keyCount : m_offset == noOffset; } |
|
134 |
|
135 void despecifyDictionaryFunction(const Identifier& propertyName); |
|
136 void disableSpecificFunctionTracking() { m_specificFunctionThrashCount = maxSpecificFunctionThrashCount; } |
|
137 |
|
138 void setEnumerationCache(JSPropertyNameIterator* enumerationCache); // Defined in JSPropertyNameIterator.h. |
|
139 void clearEnumerationCache(JSPropertyNameIterator* enumerationCache); // Defined in JSPropertyNameIterator.h. |
|
140 JSPropertyNameIterator* enumerationCache(); // Defined in JSPropertyNameIterator.h. |
|
141 void getPropertyNames(PropertyNameArray&, EnumerationMode mode); |
|
142 |
|
143 private: |
|
144 |
|
145 Structure(JSValue prototype, const TypeInfo&, unsigned anonymousSlotCount); |
|
146 |
|
147 typedef enum { |
|
148 NoneDictionaryKind = 0, |
|
149 CachedDictionaryKind = 1, |
|
150 UncachedDictionaryKind = 2 |
|
151 } DictionaryKind; |
|
152 static PassRefPtr<Structure> toDictionaryTransition(Structure*, DictionaryKind); |
|
153 |
|
154 size_t put(const Identifier& propertyName, unsigned attributes, JSCell* specificValue); |
|
155 size_t remove(const Identifier& propertyName); |
|
156 |
|
157 void expandPropertyMapHashTable(); |
|
158 void rehashPropertyMapHashTable(); |
|
159 void rehashPropertyMapHashTable(unsigned newTableSize); |
|
160 void createPropertyMapHashTable(); |
|
161 void createPropertyMapHashTable(unsigned newTableSize); |
|
162 void insertIntoPropertyMapHashTable(const PropertyMapEntry&); |
|
163 void checkConsistency(); |
|
164 |
|
165 bool despecifyFunction(const Identifier&); |
|
166 void despecifyAllFunctions(); |
|
167 |
|
168 PropertyMapHashTable* copyPropertyTable(); |
|
169 void materializePropertyMap(); |
|
170 void materializePropertyMapIfNecessary() |
|
171 { |
|
172 if (m_propertyTable || !m_previous) |
|
173 return; |
|
174 materializePropertyMap(); |
|
175 } |
|
176 |
|
177 signed char transitionCount() const |
|
178 { |
|
179 // Since the number of transitions is always the same as m_offset, we keep the size of Structure down by not storing both. |
|
180 return m_offset == noOffset ? 0 : m_offset + 1; |
|
181 } |
|
182 |
|
183 typedef std::pair<Structure*, Structure*> Transition; |
|
184 typedef HashMap<StructureTransitionTableHash::Key, Transition, StructureTransitionTableHash, StructureTransitionTableHashTraits> TransitionTable; |
|
185 |
|
186 inline bool transitionTableContains(const StructureTransitionTableHash::Key& key, JSCell* specificValue); |
|
187 inline void transitionTableRemove(const StructureTransitionTableHash::Key& key, JSCell* specificValue); |
|
188 inline void transitionTableAdd(const StructureTransitionTableHash::Key& key, Structure* structure, JSCell* specificValue); |
|
189 inline bool transitionTableHasTransition(const StructureTransitionTableHash::Key& key) const; |
|
190 inline Structure* transitionTableGet(const StructureTransitionTableHash::Key& key, JSCell* specificValue) const; |
|
191 |
|
192 TransitionTable* transitionTable() const { ASSERT(!m_isUsingSingleSlot); return m_transitions.m_table; } |
|
193 inline void setTransitionTable(TransitionTable* table); |
|
194 Structure* singleTransition() const { ASSERT(m_isUsingSingleSlot); return m_transitions.m_singleTransition; } |
|
195 void setSingleTransition(Structure* structure) { ASSERT(m_isUsingSingleSlot); m_transitions.m_singleTransition = structure; } |
|
196 |
|
197 bool isValid(ExecState*, StructureChain* cachedPrototypeChain) const; |
|
198 |
|
199 static const unsigned emptyEntryIndex = 0; |
|
200 |
|
201 static const signed char s_maxTransitionLength = 64; |
|
202 |
|
203 static const signed char noOffset = -1; |
|
204 |
|
205 static const unsigned maxSpecificFunctionThrashCount = 3; |
|
206 |
|
207 TypeInfo m_typeInfo; |
|
208 |
|
209 JSValue m_prototype; |
|
210 mutable RefPtr<StructureChain> m_cachedPrototypeChain; |
|
211 |
|
212 RefPtr<Structure> m_previous; |
|
213 RefPtr<UString::Rep> m_nameInPrevious; |
|
214 JSCell* m_specificValueInPrevious; |
|
215 |
|
216 // 'm_isUsingSingleSlot' indicates whether we are using the single transition optimisation. |
|
217 union { |
|
218 TransitionTable* m_table; |
|
219 Structure* m_singleTransition; |
|
220 } m_transitions; |
|
221 |
|
222 WeakGCPtr<JSPropertyNameIterator> m_enumerationCache; |
|
223 |
|
224 PropertyMapHashTable* m_propertyTable; |
|
225 |
|
226 uint32_t m_propertyStorageCapacity; |
|
227 |
|
228 // m_offset does not account for anonymous slots |
|
229 signed char m_offset; |
|
230 |
|
231 unsigned m_dictionaryKind : 2; |
|
232 bool m_isPinnedPropertyTable : 1; |
|
233 bool m_hasGetterSetterProperties : 1; |
|
234 bool m_hasNonEnumerableProperties : 1; |
|
235 #if COMPILER(WINSCW) |
|
236 // Workaround for Symbian WINSCW compiler that cannot resolve unsigned type of the declared |
|
237 // bitfield, when used as argument in make_pair() function calls in structure.ccp. |
|
238 // This bitfield optimization is insignificant for the Symbian emulator target. |
|
239 unsigned m_attributesInPrevious; |
|
240 #else |
|
241 unsigned m_attributesInPrevious : 7; |
|
242 #endif |
|
243 unsigned m_specificFunctionThrashCount : 2; |
|
244 unsigned m_anonymousSlotCount : 5; |
|
245 unsigned m_isUsingSingleSlot : 1; |
|
246 // 4 free bits |
|
247 }; |
|
248 |
|
249 inline size_t Structure::get(const Identifier& propertyName) |
|
250 { |
|
251 ASSERT(!propertyName.isNull()); |
|
252 |
|
253 materializePropertyMapIfNecessary(); |
|
254 if (!m_propertyTable) |
|
255 return WTF::notFound; |
|
256 |
|
257 UString::Rep* rep = propertyName._ustring.rep(); |
|
258 |
|
259 unsigned i = rep->existingHash(); |
|
260 |
|
261 #if DUMP_PROPERTYMAP_STATS |
|
262 ++numProbes; |
|
263 #endif |
|
264 |
|
265 unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; |
|
266 if (entryIndex == emptyEntryIndex) |
|
267 return WTF::notFound; |
|
268 |
|
269 if (rep == m_propertyTable->entries()[entryIndex - 1].key) |
|
270 return m_propertyTable->entries()[entryIndex - 1].offset; |
|
271 |
|
272 #if DUMP_PROPERTYMAP_STATS |
|
273 ++numCollisions; |
|
274 #endif |
|
275 |
|
276 unsigned k = 1 | WTF::doubleHash(rep->existingHash()); |
|
277 |
|
278 while (1) { |
|
279 i += k; |
|
280 |
|
281 #if DUMP_PROPERTYMAP_STATS |
|
282 ++numRehashes; |
|
283 #endif |
|
284 |
|
285 entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; |
|
286 if (entryIndex == emptyEntryIndex) |
|
287 return WTF::notFound; |
|
288 |
|
289 if (rep == m_propertyTable->entries()[entryIndex - 1].key) |
|
290 return m_propertyTable->entries()[entryIndex - 1].offset; |
|
291 } |
|
292 } |
|
293 |
|
294 } // namespace JSC |
|
295 |
|
296 #endif // Structure_h |