2 * Copyright (C) 2008 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.
27 #include "StructureID.h"
30 #include "PropertyNameArray.h"
31 #include "identifier.h"
33 #include <wtf/RefCountedLeakCounter.h>
34 #include <wtf/RefPtr.h>
36 #if ENABLE(JSC_MULTIPLE_THREADS)
37 #include <wtf/Threading.h>
45 static WTF::RefCountedLeakCounter structureIDCounter("StructureID");
47 #if ENABLE(JSC_MULTIPLE_THREADS)
48 static Mutex ignoreSetMutex;
51 static bool shouldIgnoreLeaks;
52 static HashSet<StructureID*> ignoreSet;
55 #if DUMP_STRUCTURE_ID_STATISTICS
56 static HashSet<StructureID*> liveStructureIDSet;
58 void StructureID::dumpStatistics()
60 unsigned numberUsingSingleSlot = 0;
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;
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);
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)
82 , m_transitionCount(0)
83 , m_usingSingleTransitionSlot(true)
84 , m_propertyStorageCapacity(JSObject::inlineStorageCapacity)
85 , m_cachedTransistionOffset(WTF::notFound)
88 ASSERT(m_prototype->isObject() || m_prototype->isNull());
90 m_transitions.singleTransition = 0;
93 #if ENABLE(JSC_MULTIPLE_THREADS)
94 MutexLocker protect(ignoreSetMutex);
96 if (shouldIgnoreLeaks)
99 structureIDCounter.increment();
102 #if DUMP_STRUCTURE_ID_STATISTICS
103 liveStructureIDSet.add(this);
107 StructureID::~StructureID()
110 if (m_previous->m_usingSingleTransitionSlot) {
111 m_previous->m_transitions.singleTransition->deref();
112 m_previous->m_transitions.singleTransition = 0;
114 ASSERT(m_previous->m_transitions.table->contains(make_pair(m_nameInPrevious, m_attributesInPrevious)));
115 m_previous->m_transitions.table->remove(make_pair(m_nameInPrevious, m_attributesInPrevious));
119 if (m_cachedPropertyNameArrayData)
120 m_cachedPropertyNameArrayData->setCachedStructureID(0);
122 if (m_usingSingleTransitionSlot) {
123 if (m_transitions.singleTransition)
124 m_transitions.singleTransition->deref();
126 delete m_transitions.table;
129 #if ENABLE(JSC_MULTIPLE_THREADS)
130 MutexLocker protect(ignoreSetMutex);
132 HashSet<StructureID*>::iterator it = ignoreSet.find(this);
133 if (it != ignoreSet.end())
134 ignoreSet.remove(it);
136 structureIDCounter.decrement();
139 #if DUMP_STRUCTURE_ID_STATISTICS
140 liveStructureIDSet.remove(this);
144 void StructureID::startIgnoringLeaks()
147 shouldIgnoreLeaks = true;
151 void StructureID::stopIgnoringLeaks()
154 shouldIgnoreLeaks = false;
158 void StructureID::getEnumerablePropertyNames(ExecState* exec, PropertyNameArray& propertyNames, JSObject* baseObject)
160 bool shouldCache = propertyNames.cacheable() && !(propertyNames.size() || m_isDictionary);
163 if (m_cachedPropertyNameArrayData) {
164 if (structureIDChainsAreEqual(m_cachedPropertyNameArrayData->cachedPrototypeChain(), cachedPrototypeChain())) {
165 propertyNames.setData(m_cachedPropertyNameArrayData);
169 propertyNames.setCacheable(false);
172 m_propertyMap.getEnumerablePropertyNames(propertyNames);
174 // Add properties from the static hashtables of properties
175 for (const ClassInfo* info = baseObject->classInfo(); info; info = info->parentClass) {
176 const HashTable* table = info->propHashTable(exec);
179 table->initializeIfNeeded(exec);
180 ASSERT(table->table);
181 int hashSizeMask = table->hashSizeMask;
182 const HashEntry* entry = table->table;
183 for (int i = 0; i <= hashSizeMask; ++i, ++entry) {
184 if (entry->key() && !(entry->attributes() & DontEnum))
185 propertyNames.add(entry->key());
189 if (m_prototype->isObject())
190 static_cast<JSObject*>(m_prototype)->getPropertyNames(exec, propertyNames);
193 if (m_cachedPropertyNameArrayData)
194 m_cachedPropertyNameArrayData->setCachedStructureID(0);
196 m_cachedPropertyNameArrayData = propertyNames.data();
198 StructureIDChain* chain = cachedPrototypeChain();
200 chain = createCachedPrototypeChain();
201 m_cachedPropertyNameArrayData->setCachedPrototypeChain(chain);
202 m_cachedPropertyNameArrayData->setCachedStructureID(this);
206 void StructureID::clearEnumerationCache()
208 if (m_cachedPropertyNameArrayData)
209 m_cachedPropertyNameArrayData->setCachedStructureID(0);
210 m_cachedPropertyNameArrayData.clear();
213 void StructureID::growPropertyStorageCapacity()
215 if (m_propertyStorageCapacity == JSObject::inlineStorageCapacity)
216 m_propertyStorageCapacity = JSObject::nonInlineBaseStorageCapacity;
218 m_propertyStorageCapacity *= 2;
221 PassRefPtr<StructureID> StructureID::addPropertyTransition(StructureID* structureID, const Identifier& propertyName, unsigned attributes, size_t& offset)
223 ASSERT(!structureID->m_isDictionary);
224 ASSERT(structureID->typeInfo().type() == ObjectType);
226 if (structureID->m_usingSingleTransitionSlot) {
227 StructureID* existingTransition = structureID->m_transitions.singleTransition;
228 if (existingTransition && existingTransition->m_nameInPrevious == propertyName.ustring().rep() && existingTransition->m_attributesInPrevious == attributes) {
229 offset = structureID->m_transitions.singleTransition->cachedTransistionOffset();
230 ASSERT(offset != WTF::notFound);
231 return existingTransition;
234 if (StructureID* existingTransition = structureID->m_transitions.table->get(make_pair(propertyName.ustring().rep(), attributes))) {
235 offset = existingTransition->cachedTransistionOffset();
236 ASSERT(offset != WTF::notFound);
237 return existingTransition;
241 if (structureID->m_transitionCount > s_maxTransitionLength) {
242 RefPtr<StructureID> transition = toDictionaryTransition(structureID);
243 offset = transition->m_propertyMap.put(propertyName, attributes);
244 if (transition->m_propertyMap.storageSize() > transition->propertyStorageCapacity())
245 transition->growPropertyStorageCapacity();
246 return transition.release();
249 RefPtr<StructureID> transition = create(structureID->m_prototype, structureID->typeInfo());
250 transition->m_cachedPrototypeChain = structureID->m_cachedPrototypeChain;
251 transition->m_previous = structureID;
252 transition->m_nameInPrevious = propertyName.ustring().rep();
253 transition->m_attributesInPrevious = attributes;
254 transition->m_transitionCount = structureID->m_transitionCount + 1;
255 transition->m_propertyMap = structureID->m_propertyMap;
256 transition->m_propertyStorageCapacity = structureID->m_propertyStorageCapacity;
257 transition->m_hasGetterSetterProperties = structureID->m_hasGetterSetterProperties;
259 offset = transition->m_propertyMap.put(propertyName, attributes);
260 if (transition->m_propertyMap.storageSize() > transition->propertyStorageCapacity())
261 transition->growPropertyStorageCapacity();
263 transition->setCachedTransistionOffset(offset);
265 if (structureID->m_usingSingleTransitionSlot) {
266 if (!structureID->m_transitions.singleTransition) {
267 structureID->m_transitions.singleTransition = transition.get();
269 return transition.release();
272 StructureID* existingTransition = structureID->m_transitions.singleTransition;
273 structureID->m_usingSingleTransitionSlot = false;
274 TransitionTable* transitionTable = new TransitionTable;
275 structureID->m_transitions.table = transitionTable;
276 transitionTable->add(make_pair(existingTransition->m_nameInPrevious, existingTransition->m_attributesInPrevious), existingTransition);
277 existingTransition->deref();
279 structureID->m_transitions.table->add(make_pair(propertyName.ustring().rep(), attributes), transition.get());
280 return transition.release();
283 PassRefPtr<StructureID> StructureID::toDictionaryTransition(StructureID* structureID)
285 ASSERT(!structureID->m_isDictionary);
287 RefPtr<StructureID> transition = create(structureID->m_prototype, structureID->typeInfo());
288 transition->m_isDictionary = true;
289 transition->m_propertyMap = structureID->m_propertyMap;
290 transition->m_propertyStorageCapacity = structureID->m_propertyStorageCapacity;
291 transition->m_hasGetterSetterProperties = structureID->m_hasGetterSetterProperties;
292 return transition.release();
295 PassRefPtr<StructureID> StructureID::fromDictionaryTransition(StructureID* structureID)
297 ASSERT(structureID->m_isDictionary);
299 // Since dictionary StructureIDs are not shared, and no opcodes specialize
300 // for them, we don't need to allocate a new StructureID when transitioning
301 // to non-dictionary status.
302 structureID->m_isDictionary = false;
306 PassRefPtr<StructureID> StructureID::changePrototypeTransition(StructureID* structureID, JSValue* prototype)
308 RefPtr<StructureID> transition = create(prototype, structureID->typeInfo());
309 transition->m_transitionCount = structureID->m_transitionCount + 1;
310 transition->m_propertyMap = structureID->m_propertyMap;
311 transition->m_propertyStorageCapacity = structureID->m_propertyStorageCapacity;
312 transition->m_hasGetterSetterProperties = structureID->m_hasGetterSetterProperties;
313 return transition.release();
316 PassRefPtr<StructureID> StructureID::getterSetterTransition(StructureID* structureID)
318 RefPtr<StructureID> transition = create(structureID->storedPrototype(), structureID->typeInfo());
319 transition->m_transitionCount = structureID->m_transitionCount + 1;
320 transition->m_propertyMap = structureID->m_propertyMap;
321 transition->m_propertyStorageCapacity = structureID->m_propertyStorageCapacity;
322 transition->m_hasGetterSetterProperties = transition->m_hasGetterSetterProperties;
323 return transition.release();
326 StructureIDChain* StructureID::createCachedPrototypeChain()
328 ASSERT(typeInfo().type() == ObjectType);
329 ASSERT(!m_cachedPrototypeChain);
331 JSValue* prototype = storedPrototype();
332 if (JSImmediate::isImmediate(prototype))
335 RefPtr<StructureIDChain> chain = StructureIDChain::create(static_cast<JSObject*>(prototype)->structureID());
336 setCachedPrototypeChain(chain.release());
337 return cachedPrototypeChain();
340 StructureIDChain::StructureIDChain(StructureID* structureID)
344 StructureID* tmp = structureID;
345 while (!tmp->storedPrototype()->isNull()) {
347 tmp = static_cast<JSCell*>(tmp->storedPrototype())->structureID();
350 m_vector.set(new RefPtr<StructureID>[size + 1]);
353 for (i = 0; i < size - 1; ++i) {
354 m_vector[i] = structureID;
355 structureID = static_cast<JSObject*>(structureID->storedPrototype())->structureID();
357 m_vector[i] = structureID;
361 bool structureIDChainsAreEqual(StructureIDChain* chainA, StructureIDChain* chainB)
363 if (!chainA || !chainB)
366 RefPtr<StructureID>* a = chainA->head();
367 RefPtr<StructureID>* b = chainB->head();