]>
Commit | Line | Data |
---|---|---|
9dae56ea | 1 | /* |
f9bf01c6 | 2 | * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. |
9dae56ea A |
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" | |
f9bf01c6 A |
33 | #include "PropertyNameArray.h" |
34 | #include "Protect.h" | |
9dae56ea A |
35 | #include "StructureChain.h" |
36 | #include "StructureTransitionTable.h" | |
f9bf01c6 | 37 | #include "JSTypeInfo.h" |
9dae56ea | 38 | #include "UString.h" |
f9bf01c6 | 39 | #include "WeakGCPtr.h" |
9dae56ea A |
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 | ||
f9bf01c6 | 51 | class MarkStack; |
9dae56ea A |
52 | class PropertyNameArray; |
53 | class PropertyNameArrayData; | |
54 | ||
f9bf01c6 A |
55 | enum EnumerationMode { |
56 | ExcludeDontEnumProperties, | |
57 | IncludeDontEnumProperties | |
58 | }; | |
59 | ||
9dae56ea A |
60 | class Structure : public RefCounted<Structure> { |
61 | public: | |
62 | friend class JIT; | |
f9bf01c6 A |
63 | friend class StructureTransitionTable; |
64 | static PassRefPtr<Structure> create(JSValue prototype, const TypeInfo& typeInfo, unsigned anonymousSlotCount) | |
9dae56ea | 65 | { |
f9bf01c6 | 66 | return adoptRef(new Structure(prototype, typeInfo, anonymousSlotCount)); |
9dae56ea A |
67 | } |
68 | ||
69 | static void startIgnoringLeaks(); | |
70 | static void stopIgnoringLeaks(); | |
71 | ||
72 | static void dumpStatistics(); | |
73 | ||
ba379fdc A |
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); | |
9dae56ea | 76 | static PassRefPtr<Structure> removePropertyTransition(Structure*, const Identifier& propertyName, size_t& offset); |
ba379fdc | 77 | static PassRefPtr<Structure> changePrototypeTransition(Structure*, JSValue prototype); |
f9bf01c6 | 78 | static PassRefPtr<Structure> despecifyFunctionTransition(Structure*, const Identifier&); |
9dae56ea | 79 | static PassRefPtr<Structure> getterSetterTransition(Structure*); |
ba379fdc A |
80 | static PassRefPtr<Structure> toCacheableDictionaryTransition(Structure*); |
81 | static PassRefPtr<Structure> toUncacheableDictionaryTransition(Structure*); | |
82 | ||
83 | PassRefPtr<Structure> flattenDictionaryStructure(JSObject*); | |
9dae56ea A |
84 | |
85 | ~Structure(); | |
86 | ||
9dae56ea | 87 | // These should be used with caution. |
ba379fdc | 88 | size_t addPropertyWithoutTransition(const Identifier& propertyName, unsigned attributes, JSCell* specificValue); |
9dae56ea | 89 | size_t removePropertyWithoutTransition(const Identifier& propertyName); |
ba379fdc A |
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; } | |
9dae56ea A |
94 | |
95 | const TypeInfo& typeInfo() const { return m_typeInfo; } | |
96 | ||
ba379fdc A |
97 | JSValue storedPrototype() const { return m_prototype; } |
98 | JSValue prototypeForLookup(ExecState*) const; | |
9dae56ea A |
99 | StructureChain* prototypeChain(ExecState*) const; |
100 | ||
101 | Structure* previousID() const { return m_previous.get(); } | |
102 | ||
103 | void growPropertyStorageCapacity(); | |
f9bf01c6 A |
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) : m_offset + 1); } | |
ba379fdc | 106 | bool isUsingInlineStorage() const; |
9dae56ea A |
107 | |
108 | size_t get(const Identifier& propertyName); | |
ba379fdc A |
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 | ||
9dae56ea A |
125 | bool hasGetterSetterProperties() const { return m_hasGetterSetterProperties; } |
126 | void setHasGetterSetterProperties(bool hasGetterSetterProperties) { m_hasGetterSetterProperties = hasGetterSetterProperties; } | |
127 | ||
f9bf01c6 A |
128 | bool hasNonEnumerableProperties() const { return m_hasNonEnumerableProperties; } |
129 | ||
130 | bool hasAnonymousSlots() const { return !!m_anonymousSlotCount; } | |
131 | unsigned anonymousSlotCount() const { return m_anonymousSlotCount; } | |
132 | ||
9dae56ea A |
133 | bool isEmpty() const { return m_propertyTable ? !m_propertyTable->keyCount : m_offset == noOffset; } |
134 | ||
ba379fdc | 135 | void despecifyDictionaryFunction(const Identifier& propertyName); |
f9bf01c6 | 136 | void disableSpecificFunctionTracking() { m_specificFunctionThrashCount = maxSpecificFunctionThrashCount; } |
9dae56ea | 137 | |
f9bf01c6 A |
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 | ||
ba379fdc | 143 | private: |
f9bf01c6 A |
144 | |
145 | Structure(JSValue prototype, const TypeInfo&, unsigned anonymousSlotCount); | |
ba379fdc A |
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); | |
9dae56ea | 155 | size_t remove(const Identifier& propertyName); |
9dae56ea A |
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 | ||
ba379fdc | 165 | bool despecifyFunction(const Identifier&); |
f9bf01c6 | 166 | void despecifyAllFunctions(); |
ba379fdc | 167 | |
9dae56ea A |
168 | PropertyMapHashTable* copyPropertyTable(); |
169 | void materializePropertyMap(); | |
170 | void materializePropertyMapIfNecessary() | |
171 | { | |
172 | if (m_propertyTable || !m_previous) | |
173 | return; | |
174 | materializePropertyMap(); | |
175 | } | |
176 | ||
9dae56ea A |
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 | bool isValid(ExecState*, StructureChain* cachedPrototypeChain) const; | |
184 | ||
185 | static const unsigned emptyEntryIndex = 0; | |
186 | ||
187 | static const signed char s_maxTransitionLength = 64; | |
188 | ||
189 | static const signed char noOffset = -1; | |
190 | ||
f9bf01c6 A |
191 | static const unsigned maxSpecificFunctionThrashCount = 3; |
192 | ||
9dae56ea A |
193 | TypeInfo m_typeInfo; |
194 | ||
ba379fdc | 195 | JSValue m_prototype; |
9dae56ea A |
196 | mutable RefPtr<StructureChain> m_cachedPrototypeChain; |
197 | ||
198 | RefPtr<Structure> m_previous; | |
199 | RefPtr<UString::Rep> m_nameInPrevious; | |
ba379fdc | 200 | JSCell* m_specificValueInPrevious; |
9dae56ea | 201 | |
f9bf01c6 | 202 | StructureTransitionTable table; |
9dae56ea | 203 | |
f9bf01c6 | 204 | WeakGCPtr<JSPropertyNameIterator> m_enumerationCache; |
9dae56ea A |
205 | |
206 | PropertyMapHashTable* m_propertyTable; | |
207 | ||
f9bf01c6 A |
208 | uint32_t m_propertyStorageCapacity; |
209 | ||
210 | // m_offset does not account for anonymous slots | |
9dae56ea A |
211 | signed char m_offset; |
212 | ||
ba379fdc | 213 | unsigned m_dictionaryKind : 2; |
9dae56ea A |
214 | bool m_isPinnedPropertyTable : 1; |
215 | bool m_hasGetterSetterProperties : 1; | |
f9bf01c6 A |
216 | bool m_hasNonEnumerableProperties : 1; |
217 | #if COMPILER(WINSCW) | |
218 | // Workaround for Symbian WINSCW compiler that cannot resolve unsigned type of the declared | |
219 | // bitfield, when used as argument in make_pair() function calls in structure.ccp. | |
220 | // This bitfield optimization is insignificant for the Symbian emulator target. | |
221 | unsigned m_attributesInPrevious; | |
222 | #else | |
ba379fdc | 223 | unsigned m_attributesInPrevious : 7; |
f9bf01c6 A |
224 | #endif |
225 | unsigned m_specificFunctionThrashCount : 2; | |
226 | unsigned m_anonymousSlotCount : 5; | |
227 | // 5 free bits | |
9dae56ea A |
228 | }; |
229 | ||
230 | inline size_t Structure::get(const Identifier& propertyName) | |
231 | { | |
232 | ASSERT(!propertyName.isNull()); | |
233 | ||
234 | materializePropertyMapIfNecessary(); | |
235 | if (!m_propertyTable) | |
236 | return WTF::notFound; | |
237 | ||
238 | UString::Rep* rep = propertyName._ustring.rep(); | |
239 | ||
f9bf01c6 | 240 | unsigned i = rep->existingHash(); |
9dae56ea A |
241 | |
242 | #if DUMP_PROPERTYMAP_STATS | |
243 | ++numProbes; | |
244 | #endif | |
245 | ||
246 | unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; | |
247 | if (entryIndex == emptyEntryIndex) | |
248 | return WTF::notFound; | |
249 | ||
250 | if (rep == m_propertyTable->entries()[entryIndex - 1].key) | |
251 | return m_propertyTable->entries()[entryIndex - 1].offset; | |
252 | ||
253 | #if DUMP_PROPERTYMAP_STATS | |
254 | ++numCollisions; | |
255 | #endif | |
256 | ||
f9bf01c6 | 257 | unsigned k = 1 | WTF::doubleHash(rep->existingHash()); |
9dae56ea A |
258 | |
259 | while (1) { | |
260 | i += k; | |
261 | ||
262 | #if DUMP_PROPERTYMAP_STATS | |
263 | ++numRehashes; | |
264 | #endif | |
265 | ||
266 | entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; | |
267 | if (entryIndex == emptyEntryIndex) | |
268 | return WTF::notFound; | |
269 | ||
270 | if (rep == m_propertyTable->entries()[entryIndex - 1].key) | |
271 | return m_propertyTable->entries()[entryIndex - 1].offset; | |
272 | } | |
273 | } | |
ba379fdc A |
274 | |
275 | bool StructureTransitionTable::contains(const StructureTransitionTableHash::Key& key, JSCell* specificValue) | |
276 | { | |
f9bf01c6 A |
277 | if (usingSingleTransitionSlot()) { |
278 | Structure* existingTransition = singleTransition(); | |
279 | return existingTransition && existingTransition->m_nameInPrevious.get() == key.first | |
280 | && existingTransition->m_attributesInPrevious == key.second | |
281 | && (existingTransition->m_specificValueInPrevious == specificValue || existingTransition->m_specificValueInPrevious == 0); | |
282 | } | |
283 | TransitionTable::iterator find = table()->find(key); | |
284 | if (find == table()->end()) | |
ba379fdc A |
285 | return false; |
286 | ||
287 | return find->second.first || find->second.second->transitionedFor(specificValue); | |
288 | } | |
9dae56ea | 289 | |
ba379fdc A |
290 | Structure* StructureTransitionTable::get(const StructureTransitionTableHash::Key& key, JSCell* specificValue) const |
291 | { | |
f9bf01c6 A |
292 | if (usingSingleTransitionSlot()) { |
293 | Structure* existingTransition = singleTransition(); | |
294 | if (existingTransition && existingTransition->m_nameInPrevious.get() == key.first | |
295 | && existingTransition->m_attributesInPrevious == key.second | |
296 | && (existingTransition->m_specificValueInPrevious == specificValue || existingTransition->m_specificValueInPrevious == 0)) | |
297 | return existingTransition; | |
298 | return 0; | |
299 | } | |
300 | ||
301 | Transition transition = table()->get(key); | |
ba379fdc A |
302 | if (transition.second && transition.second->transitionedFor(specificValue)) |
303 | return transition.second; | |
304 | return transition.first; | |
305 | } | |
f9bf01c6 A |
306 | |
307 | bool StructureTransitionTable::hasTransition(const StructureTransitionTableHash::Key& key) const | |
308 | { | |
309 | if (usingSingleTransitionSlot()) { | |
310 | Structure* transition = singleTransition(); | |
311 | return transition && transition->m_nameInPrevious == key.first | |
312 | && transition->m_attributesInPrevious == key.second; | |
313 | } | |
314 | return table()->contains(key); | |
315 | } | |
316 | ||
317 | void StructureTransitionTable::reifySingleTransition() | |
318 | { | |
319 | ASSERT(usingSingleTransitionSlot()); | |
320 | Structure* existingTransition = singleTransition(); | |
321 | TransitionTable* transitionTable = new TransitionTable; | |
322 | setTransitionTable(transitionTable); | |
323 | if (existingTransition) | |
324 | add(std::make_pair(existingTransition->m_nameInPrevious.get(), existingTransition->m_attributesInPrevious), existingTransition, existingTransition->m_specificValueInPrevious); | |
325 | } | |
9dae56ea A |
326 | } // namespace JSC |
327 | ||
328 | #endif // Structure_h |