2008-10-16 Maciej Stachowiak <mjs@apple.com>
[WebKit.git] / JavaScriptCore / kjs / StructureID.cpp
1 /*
2  * Copyright (C) 2008 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 #include "config.h"
27 #include "StructureID.h"
28
29 #include "JSObject.h"
30 #include "PropertyNameArray.h"
31 #include "identifier.h"
32 #include "lookup.h"
33 #include <wtf/RefCountedLeakCounter.h>
34 #include <wtf/RefPtr.h>
35
36 #if ENABLE(JSC_MULTIPLE_THREADS)
37 #include <wtf/Threading.h>
38 #endif
39
40 using namespace std;
41
42 namespace JSC {
43
44 #ifndef NDEBUG
45 static WTF::RefCountedLeakCounter structureIDCounter("StructureID");
46
47 #if ENABLE(JSC_MULTIPLE_THREADS)
48 static Mutex ignoreSetMutex;
49 #endif
50
51 static bool shouldIgnoreLeaks;
52 static HashSet<StructureID*> ignoreSet;
53 #endif
54
55 #if DUMP_STRUCTURE_ID_STATISTICS
56 static HashSet<StructureID*> liveStructureIDSet;
57
58 void StructureID::dumpStatistics()
59 {
60     unsigned numberUsingSingleSlot = 0;
61
62     HashSet<StructureID*>::const_iterator end = liveStructureIDSet.end();
63     for (HashSet<StructureID*>::const_iterator it = liveStructureIDSet.begin(); it != end; ++it) {
64         StructureID* structureID = *it;
65         if (structureID->m_usingSingleTransitionSlot)
66             ++numberUsingSingleSlot;
67     }
68
69     printf("Number of live StructureIDs: %d\n", liveStructureIDSet.size());
70     printf("Number of StructureIDs using the single item optimization for transition map: %d\n", numberUsingSingleSlot);
71 }
72 #endif
73
74 StructureID::StructureID(JSValue* prototype, const TypeInfo& typeInfo)
75     : m_typeInfo(typeInfo)
76     , m_isDictionary(false)
77     , m_hasGetterSetterProperties(false)
78     , m_prototype(prototype)
79     , m_cachedPrototypeChain(0)
80     , m_previous(0)
81     , m_nameInPrevious(0)
82     , m_transitionCount(0)
83     , m_usingSingleTransitionSlot(true)
84     , m_propertyStorageCapacity(JSObject::inlineStorageCapacity)
85     , m_cachedTransistionOffset(WTF::notFound)
86 {
87     ASSERT(m_prototype);
88     ASSERT(m_prototype->isObject() || m_prototype->isNull());
89
90     m_transitions.singleTransition = 0;
91
92 #ifndef NDEBUG
93 #if ENABLE(JSC_MULTIPLE_THREADS)
94     MutexLocker protect(ignoreSetMutex);
95 #endif
96     if (shouldIgnoreLeaks)
97         ignoreSet.add(this);
98     else
99         structureIDCounter.increment();
100 #endif
101
102 #if DUMP_STRUCTURE_ID_STATISTICS
103     liveStructureIDSet.add(this);
104 #endif
105 }
106
107 StructureID::~StructureID()
108 {
109     if (m_previous) {
110         if (m_previous->m_usingSingleTransitionSlot) {
111             m_previous->m_transitions.singleTransition = 0;
112         } else {
113             ASSERT(m_previous->m_transitions.table->contains(make_pair(m_nameInPrevious, m_attributesInPrevious)));
114             m_previous->m_transitions.table->remove(make_pair(m_nameInPrevious, m_attributesInPrevious));
115         }
116     }
117
118     if (m_cachedPropertyNameArrayData)
119         m_cachedPropertyNameArrayData->setCachedStructureID(0);
120
121     if (!m_usingSingleTransitionSlot)
122         delete m_transitions.table;
123
124 #ifndef NDEBUG
125 #if ENABLE(JSC_MULTIPLE_THREADS)
126     MutexLocker protect(ignoreSetMutex);
127 #endif
128     HashSet<StructureID*>::iterator it = ignoreSet.find(this);
129     if (it != ignoreSet.end())
130         ignoreSet.remove(it);
131     else
132         structureIDCounter.decrement();
133 #endif
134
135 #if DUMP_STRUCTURE_ID_STATISTICS
136     liveStructureIDSet.remove(this);
137 #endif
138 }
139
140 void StructureID::startIgnoringLeaks()
141 {
142 #ifndef NDEBUG
143     shouldIgnoreLeaks = true;
144 #endif
145 }
146
147 void StructureID::stopIgnoringLeaks()
148 {
149 #ifndef NDEBUG
150     shouldIgnoreLeaks = false;
151 #endif
152 }
153
154 void StructureID::getEnumerablePropertyNames(ExecState* exec, PropertyNameArray& propertyNames, JSObject* baseObject)
155 {
156     bool shouldCache = propertyNames.cacheable() && !(propertyNames.size() || m_isDictionary);
157
158     if (shouldCache) {
159         if (m_cachedPropertyNameArrayData) {
160             if (structureIDChainsAreEqual(m_cachedPropertyNameArrayData->cachedPrototypeChain(), cachedPrototypeChain())) {
161                 propertyNames.setData(m_cachedPropertyNameArrayData);
162                 return;
163             }
164         }
165         propertyNames.setCacheable(false);
166     }
167
168     m_propertyMap.getEnumerablePropertyNames(propertyNames);
169
170     // Add properties from the static hashtables of properties
171     for (const ClassInfo* info = baseObject->classInfo(); info; info = info->parentClass) {
172         const HashTable* table = info->propHashTable(exec);
173         if (!table)
174             continue;
175         table->initializeIfNeeded(exec);
176         ASSERT(table->table);
177         int hashSizeMask = table->hashSizeMask;
178         const HashEntry* entry = table->table;
179         for (int i = 0; i <= hashSizeMask; ++i, ++entry) {
180             if (entry->key() && !(entry->attributes() & DontEnum))
181                 propertyNames.add(entry->key());
182         }
183     }
184
185     if (m_prototype->isObject())
186         static_cast<JSObject*>(m_prototype)->getPropertyNames(exec, propertyNames);
187
188     if (shouldCache) {
189         if (m_cachedPropertyNameArrayData)
190             m_cachedPropertyNameArrayData->setCachedStructureID(0);
191
192         m_cachedPropertyNameArrayData = propertyNames.data();
193
194         StructureIDChain* chain = cachedPrototypeChain();
195         if (!chain)
196             chain = createCachedPrototypeChain();
197         m_cachedPropertyNameArrayData->setCachedPrototypeChain(chain);
198         m_cachedPropertyNameArrayData->setCachedStructureID(this);
199     }
200 }
201
202 void StructureID::clearEnumerationCache()
203 {
204     if (m_cachedPropertyNameArrayData)
205         m_cachedPropertyNameArrayData->setCachedStructureID(0);
206     m_cachedPropertyNameArrayData.clear();
207 }
208
209 void StructureID::growPropertyStorageCapacity()
210 {
211     if (m_propertyStorageCapacity == JSObject::inlineStorageCapacity)
212         m_propertyStorageCapacity = JSObject::nonInlineBaseStorageCapacity;
213     else
214         m_propertyStorageCapacity *= 2;
215 }
216
217 PassRefPtr<StructureID> StructureID::addPropertyTransition(StructureID* structureID, const Identifier& propertyName, unsigned attributes, size_t& offset)
218 {
219     ASSERT(!structureID->m_isDictionary);
220     ASSERT(structureID->typeInfo().type() == ObjectType);
221
222     if (structureID->m_usingSingleTransitionSlot) {
223         StructureID* existingTransition = structureID->m_transitions.singleTransition;
224         if (existingTransition && existingTransition->m_nameInPrevious == propertyName.ustring().rep() && existingTransition->m_attributesInPrevious == attributes) {
225             offset = structureID->m_transitions.singleTransition->cachedTransistionOffset();
226             ASSERT(offset != WTF::notFound);
227             return existingTransition;
228         }
229     } else {
230         if (StructureID* existingTransition = structureID->m_transitions.table->get(make_pair(propertyName.ustring().rep(), attributes))) {
231             offset = existingTransition->cachedTransistionOffset();
232             ASSERT(offset != WTF::notFound);
233             return existingTransition;
234         }        
235     }
236
237     if (structureID->m_transitionCount > s_maxTransitionLength) {
238         RefPtr<StructureID> transition = toDictionaryTransition(structureID);
239         offset = transition->m_propertyMap.put(propertyName, attributes);
240         if (transition->m_propertyMap.storageSize() > transition->propertyStorageCapacity())
241             transition->growPropertyStorageCapacity();
242         return transition.release();
243     }
244
245     RefPtr<StructureID> transition = create(structureID->m_prototype, structureID->typeInfo());
246     transition->m_cachedPrototypeChain = structureID->m_cachedPrototypeChain;
247     transition->m_previous = structureID;
248     transition->m_nameInPrevious = propertyName.ustring().rep();
249     transition->m_attributesInPrevious = attributes;
250     transition->m_transitionCount = structureID->m_transitionCount + 1;
251     transition->m_propertyMap = structureID->m_propertyMap;
252     transition->m_propertyStorageCapacity = structureID->m_propertyStorageCapacity;
253     transition->m_hasGetterSetterProperties = structureID->m_hasGetterSetterProperties;
254
255     offset = transition->m_propertyMap.put(propertyName, attributes);
256     if (transition->m_propertyMap.storageSize() > transition->propertyStorageCapacity())
257         transition->growPropertyStorageCapacity();
258
259     transition->setCachedTransistionOffset(offset);
260
261     if (structureID->m_usingSingleTransitionSlot) {
262         if (!structureID->m_transitions.singleTransition) {
263             structureID->m_transitions.singleTransition = transition.get();
264             return transition.release();
265         }
266
267         StructureID* existingTransition = structureID->m_transitions.singleTransition;
268         structureID->m_usingSingleTransitionSlot = false;
269         TransitionTable* transitionTable = new TransitionTable;
270         structureID->m_transitions.table = transitionTable;
271         transitionTable->add(make_pair(existingTransition->m_nameInPrevious, existingTransition->m_attributesInPrevious), existingTransition);
272     }
273     structureID->m_transitions.table->add(make_pair(propertyName.ustring().rep(), attributes), transition.get());
274     return transition.release();
275 }
276
277 PassRefPtr<StructureID> StructureID::toDictionaryTransition(StructureID* structureID)
278 {
279     ASSERT(!structureID->m_isDictionary);
280
281     RefPtr<StructureID> transition = create(structureID->m_prototype, structureID->typeInfo());
282     transition->m_isDictionary = true;
283     transition->m_propertyMap = structureID->m_propertyMap;
284     transition->m_propertyStorageCapacity = structureID->m_propertyStorageCapacity;
285     transition->m_hasGetterSetterProperties = structureID->m_hasGetterSetterProperties;
286     return transition.release();
287 }
288
289 PassRefPtr<StructureID> StructureID::fromDictionaryTransition(StructureID* structureID)
290 {
291     ASSERT(structureID->m_isDictionary);
292
293     // Since dictionary StructureIDs are not shared, and no opcodes specialize
294     // for them, we don't need to allocate a new StructureID when transitioning
295     // to non-dictionary status.
296     structureID->m_isDictionary = false;
297     return structureID;
298 }
299
300 PassRefPtr<StructureID> StructureID::changePrototypeTransition(StructureID* structureID, JSValue* prototype)
301 {
302     RefPtr<StructureID> transition = create(prototype, structureID->typeInfo());
303     transition->m_transitionCount = structureID->m_transitionCount + 1;
304     transition->m_propertyMap = structureID->m_propertyMap;
305     transition->m_propertyStorageCapacity = structureID->m_propertyStorageCapacity;
306     transition->m_hasGetterSetterProperties = structureID->m_hasGetterSetterProperties;
307     return transition.release();
308 }
309
310 PassRefPtr<StructureID> StructureID::getterSetterTransition(StructureID* structureID)
311 {
312     RefPtr<StructureID> transition = create(structureID->storedPrototype(), structureID->typeInfo());
313     transition->m_transitionCount = structureID->m_transitionCount + 1;
314     transition->m_propertyMap = structureID->m_propertyMap;
315     transition->m_propertyStorageCapacity = structureID->m_propertyStorageCapacity;
316     transition->m_hasGetterSetterProperties = transition->m_hasGetterSetterProperties;
317     return transition.release();
318 }
319
320 StructureIDChain* StructureID::createCachedPrototypeChain()
321 {
322     ASSERT(typeInfo().type() == ObjectType);
323     ASSERT(!m_cachedPrototypeChain);
324
325     JSValue* prototype = storedPrototype();
326     if (JSImmediate::isImmediate(prototype))
327         return 0;
328
329     RefPtr<StructureIDChain> chain = StructureIDChain::create(static_cast<JSObject*>(prototype)->structureID());
330     setCachedPrototypeChain(chain.release());
331     return cachedPrototypeChain();
332 }
333
334 StructureIDChain::StructureIDChain(StructureID* structureID)
335 {
336     size_t size = 1;
337
338     StructureID* tmp = structureID;
339     while (!tmp->storedPrototype()->isNull()) {
340         ++size;
341         tmp = static_cast<JSCell*>(tmp->storedPrototype())->structureID();
342     }
343     
344     m_vector.set(new RefPtr<StructureID>[size + 1]);
345
346     size_t i;
347     for (i = 0; i < size - 1; ++i) {
348         m_vector[i] = structureID;
349         structureID = static_cast<JSObject*>(structureID->storedPrototype())->structureID();
350     }
351     m_vector[i] = structureID;
352     m_vector[i + 1] = 0;
353 }
354
355 bool structureIDChainsAreEqual(StructureIDChain* chainA, StructureIDChain* chainB)
356 {
357     if (!chainA || !chainB)
358         return false;
359
360     RefPtr<StructureID>* a = chainA->head();
361     RefPtr<StructureID>* b = chainB->head();
362     while (1) {
363         if (*a != *b)
364             return false;
365         if (!*a)
366             return true;
367         a++;
368         b++;
369     }
370 }
371
372 } // namespace JSC