2 * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
29 #include "Identifier.h"
32 #include "PropertyMapHashTable.h"
33 #include "PropertyNameArray.h"
35 #include "StructureChain.h"
36 #include "StructureTransitionTable.h"
37 #include "JSTypeInfo.h"
39 #include "WeakGCPtr.h"
40 #include <wtf/PassRefPtr.h>
41 #include <wtf/RefCounted.h>
44 #define DUMP_PROPERTYMAP_STATS 0
46 #define DUMP_PROPERTYMAP_STATS 0
52 class PropertyNameArray
;
53 class PropertyNameArrayData
;
55 enum EnumerationMode
{
56 ExcludeDontEnumProperties
,
57 IncludeDontEnumProperties
60 class Structure
: public RefCounted
<Structure
> {
63 friend class StructureTransitionTable
;
64 static PassRefPtr
<Structure
> create(JSValue prototype
, const TypeInfo
& typeInfo
, unsigned anonymousSlotCount
)
66 return adoptRef(new Structure(prototype
, typeInfo
, anonymousSlotCount
));
69 static void startIgnoringLeaks();
70 static void stopIgnoringLeaks();
72 static void dumpStatistics();
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
*);
83 PassRefPtr
<Structure
> flattenDictionaryStructure(JSObject
*);
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
; }
92 bool isDictionary() const { return m_dictionaryKind
!= NoneDictionaryKind
; }
93 bool isUncacheableDictionary() const { return m_dictionaryKind
== UncachedDictionaryKind
; }
95 const TypeInfo
& typeInfo() const { return m_typeInfo
; }
97 JSValue
storedPrototype() const { return m_prototype
; }
98 JSValue
prototypeForLookup(ExecState
*) const;
99 StructureChain
* prototypeChain(ExecState
*) const;
101 Structure
* previousID() const { return m_previous
.get(); }
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) : m_offset
+ 1); }
106 bool isUsingInlineStorage() const;
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
)
112 ASSERT(!propertyName
.isNull());
113 return get(propertyName
.ustring().rep(), attributes
, specificValue
);
115 bool transitionedFor(const JSCell
* specificValue
)
117 return m_specificValueInPrevious
== specificValue
;
119 bool hasTransition(UString::Rep
*, unsigned attributes
);
120 bool hasTransition(const Identifier
& propertyName
, unsigned attributes
)
122 return hasTransition(propertyName
._ustring
.rep(), attributes
);
125 bool hasGetterSetterProperties() const { return m_hasGetterSetterProperties
; }
126 void setHasGetterSetterProperties(bool hasGetterSetterProperties
) { m_hasGetterSetterProperties
= hasGetterSetterProperties
; }
128 bool hasNonEnumerableProperties() const { return m_hasNonEnumerableProperties
; }
130 bool hasAnonymousSlots() const { return !!m_anonymousSlotCount
; }
131 unsigned anonymousSlotCount() const { return m_anonymousSlotCount
; }
133 bool isEmpty() const { return m_propertyTable
? !m_propertyTable
->keyCount
: m_offset
== noOffset
; }
135 void despecifyDictionaryFunction(const Identifier
& propertyName
);
136 void disableSpecificFunctionTracking() { m_specificFunctionThrashCount
= maxSpecificFunctionThrashCount
; }
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
);
145 Structure(JSValue prototype
, const TypeInfo
&, unsigned anonymousSlotCount
);
148 NoneDictionaryKind
= 0,
149 CachedDictionaryKind
= 1,
150 UncachedDictionaryKind
= 2
152 static PassRefPtr
<Structure
> toDictionaryTransition(Structure
*, DictionaryKind
);
154 size_t put(const Identifier
& propertyName
, unsigned attributes
, JSCell
* specificValue
);
155 size_t remove(const Identifier
& propertyName
);
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();
165 bool despecifyFunction(const Identifier
&);
166 void despecifyAllFunctions();
168 PropertyMapHashTable
* copyPropertyTable();
169 void materializePropertyMap();
170 void materializePropertyMapIfNecessary()
172 if (m_propertyTable
|| !m_previous
)
174 materializePropertyMap();
177 signed char transitionCount() const
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;
183 bool isValid(ExecState
*, StructureChain
* cachedPrototypeChain
) const;
185 static const unsigned emptyEntryIndex
= 0;
187 static const signed char s_maxTransitionLength
= 64;
189 static const signed char noOffset
= -1;
191 static const unsigned maxSpecificFunctionThrashCount
= 3;
196 mutable RefPtr
<StructureChain
> m_cachedPrototypeChain
;
198 RefPtr
<Structure
> m_previous
;
199 RefPtr
<UString::Rep
> m_nameInPrevious
;
200 JSCell
* m_specificValueInPrevious
;
202 StructureTransitionTable table
;
204 WeakGCPtr
<JSPropertyNameIterator
> m_enumerationCache
;
206 PropertyMapHashTable
* m_propertyTable
;
208 uint32_t m_propertyStorageCapacity
;
210 // m_offset does not account for anonymous slots
211 signed char m_offset
;
213 unsigned m_dictionaryKind
: 2;
214 bool m_isPinnedPropertyTable
: 1;
215 bool m_hasGetterSetterProperties
: 1;
216 bool m_hasNonEnumerableProperties
: 1;
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
;
223 unsigned m_attributesInPrevious
: 7;
225 unsigned m_specificFunctionThrashCount
: 2;
226 unsigned m_anonymousSlotCount
: 5;
230 inline size_t Structure::get(const Identifier
& propertyName
)
232 ASSERT(!propertyName
.isNull());
234 materializePropertyMapIfNecessary();
235 if (!m_propertyTable
)
236 return WTF::notFound
;
238 UString::Rep
* rep
= propertyName
._ustring
.rep();
240 unsigned i
= rep
->existingHash();
242 #if DUMP_PROPERTYMAP_STATS
246 unsigned entryIndex
= m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
];
247 if (entryIndex
== emptyEntryIndex
)
248 return WTF::notFound
;
250 if (rep
== m_propertyTable
->entries()[entryIndex
- 1].key
)
251 return m_propertyTable
->entries()[entryIndex
- 1].offset
;
253 #if DUMP_PROPERTYMAP_STATS
257 unsigned k
= 1 | WTF::doubleHash(rep
->existingHash());
262 #if DUMP_PROPERTYMAP_STATS
266 entryIndex
= m_propertyTable
->entryIndices
[i
& m_propertyTable
->sizeMask
];
267 if (entryIndex
== emptyEntryIndex
)
268 return WTF::notFound
;
270 if (rep
== m_propertyTable
->entries()[entryIndex
- 1].key
)
271 return m_propertyTable
->entries()[entryIndex
- 1].offset
;
275 bool StructureTransitionTable::contains(const StructureTransitionTableHash::Key
& key
, JSCell
* specificValue
)
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);
283 TransitionTable::iterator find
= table()->find(key
);
284 if (find
== table()->end())
287 return find
->second
.first
|| find
->second
.second
->transitionedFor(specificValue
);
290 Structure
* StructureTransitionTable::get(const StructureTransitionTableHash::Key
& key
, JSCell
* specificValue
) const
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
;
301 Transition transition
= table()->get(key
);
302 if (transition
.second
&& transition
.second
->transitionedFor(specificValue
))
303 return transition
.second
;
304 return transition
.first
;
307 bool StructureTransitionTable::hasTransition(const StructureTransitionTableHash::Key
& key
) const
309 if (usingSingleTransitionSlot()) {
310 Structure
* transition
= singleTransition();
311 return transition
&& transition
->m_nameInPrevious
== key
.first
312 && transition
->m_attributesInPrevious
== key
.second
;
314 return table()->contains(key
);
317 void StructureTransitionTable::reifySingleTransition()
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
);
328 #endif // Structure_h