source: webkit/trunk/JavaScriptCore/kjs/StructureID.cpp@ 37508

Last change on this file since 37508 was 37508, checked in by [email protected], 17 years ago

2008-10-11 Cameron Zwarich <[email protected]>

Reviewed by Sam Weinig.

Bug 21525: 55 StructureID leaks on Wikitravel's main page
<https://bugs.webkit.org/show_bug.cgi?id=21525>

Bug 21533: Simple JavaScript code leaks StructureIDs
<https://bugs.webkit.org/show_bug.cgi?id=21533>

StructureID::getEnumerablePropertyNames() ends up calling back to itself
via JSObject::getPropertyNames(), which causes the PropertyNameArray to
be cached twice. This leads to a memory leak in almost every use of
JSObject::getPropertyNames() on an object. The fix here is based on a
suggestion of Sam Weinig.

This patch also fixes every StructureID leaks that occurs while running
the Mozilla MemBuster test.

  • kjs/PropertyNameArray.h: (JSC::PropertyNameArray::PropertyNameArray): (JSC::PropertyNameArray::setCacheable): (JSC::PropertyNameArray::cacheable):
  • kjs/StructureID.cpp: (JSC::StructureID::getEnumerablePropertyNames):
File size: 10.1 KB
Line 
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
36using namespace std;
37
38namespace JSC {
39
40#ifndef NDEBUG
41static WTF::RefCountedLeakCounter structureIDCounter("StructureID");
42#endif
43
44StructureID::StructureID(JSValue* prototype, const TypeInfo& typeInfo)
45 : m_typeInfo(typeInfo)
46 , m_isDictionary(false)
47 , m_hasGetterSetterProperties(false)
48 , m_prototype(prototype)
49 , m_cachedPrototypeChain(0)
50 , m_previous(0)
51 , m_nameInPrevious(0)
52 , m_transitionCount(0)
53 , m_propertyStorageCapacity(JSObject::inlineStorageCapacity)
54 , m_cachedTransistionOffset(WTF::notFound)
55{
56 ASSERT(m_prototype);
57 ASSERT(m_prototype->isObject() || m_prototype->isNull());
58
59#ifndef NDEBUG
60 structureIDCounter.increment();
61#endif
62}
63
64StructureID::~StructureID()
65{
66 if (m_previous) {
67 ASSERT(m_previous->m_transitionTable.contains(make_pair(m_nameInPrevious, m_attributesInPrevious)));
68 m_previous->m_transitionTable.remove(make_pair(m_nameInPrevious, m_attributesInPrevious));
69 }
70
71 if (m_cachedPropertyNameArrayData)
72 m_cachedPropertyNameArrayData->setCachedStructureID(0);
73
74#ifndef NDEBUG
75 structureIDCounter.decrement();
76#endif
77}
78
79void StructureID::getEnumerablePropertyNames(ExecState* exec, PropertyNameArray& propertyNames, JSObject* baseObject)
80{
81 bool shouldCache = propertyNames.cacheable() && !(propertyNames.size() || m_isDictionary);
82
83 if (shouldCache) {
84 if (m_cachedPropertyNameArrayData) {
85 if (structureIDChainsAreEqual(m_cachedPropertyNameArrayData->cachedPrototypeChain(), cachedPrototypeChain())) {
86 propertyNames.setData(m_cachedPropertyNameArrayData);
87 return;
88 }
89 }
90 propertyNames.setCacheable(false);
91 }
92
93 m_propertyMap.getEnumerablePropertyNames(propertyNames);
94
95 // Add properties from the static hashtables of properties
96 for (const ClassInfo* info = baseObject->classInfo(); info; info = info->parentClass) {
97 const HashTable* table = info->propHashTable(exec);
98 if (!table)
99 continue;
100 table->initializeIfNeeded(exec);
101 ASSERT(table->table);
102 int hashSizeMask = table->hashSizeMask;
103 const HashEntry* entry = table->table;
104 for (int i = 0; i <= hashSizeMask; ++i, ++entry) {
105 if (entry->key() && !(entry->attributes() & DontEnum))
106 propertyNames.add(entry->key());
107 }
108 }
109
110 if (m_prototype->isObject())
111 static_cast<JSObject*>(m_prototype)->getPropertyNames(exec, propertyNames);
112
113 if (shouldCache) {
114 if (m_cachedPropertyNameArrayData)
115 m_cachedPropertyNameArrayData->setCachedStructureID(0);
116
117 m_cachedPropertyNameArrayData = propertyNames.data();
118
119 StructureIDChain* chain = cachedPrototypeChain();
120 if (!chain)
121 chain = createCachedPrototypeChain();
122 m_cachedPropertyNameArrayData->setCachedPrototypeChain(chain);
123 m_cachedPropertyNameArrayData->setCachedStructureID(this);
124 }
125}
126
127void StructureID::clearEnumerationCache()
128{
129 if (m_cachedPropertyNameArrayData)
130 m_cachedPropertyNameArrayData->setCachedStructureID(0);
131 m_cachedPropertyNameArrayData.clear();
132}
133
134void StructureID::growPropertyStorageCapacity()
135{
136 if (m_propertyStorageCapacity == JSObject::inlineStorageCapacity)
137 m_propertyStorageCapacity = JSObject::nonInlineBaseStorageCapacity;
138 else
139 m_propertyStorageCapacity *= 2;
140}
141
142PassRefPtr<StructureID> StructureID::addPropertyTransition(StructureID* structureID, const Identifier& propertyName, unsigned attributes, size_t& offset)
143{
144 ASSERT(!structureID->m_isDictionary);
145 ASSERT(structureID->typeInfo().type() == ObjectType);
146
147 if (StructureID* existingTransition = structureID->m_transitionTable.get(make_pair(propertyName.ustring().rep(), attributes))) {
148 offset = existingTransition->cachedTransistionOffset();
149 ASSERT(offset != WTF::notFound);
150 return existingTransition;
151 }
152
153 if (structureID->m_transitionCount > s_maxTransitionLength) {
154 RefPtr<StructureID> transition = toDictionaryTransition(structureID);
155 offset = transition->m_propertyMap.put(propertyName, attributes);
156 if (transition->m_propertyMap.storageSize() > transition->propertyStorageCapacity())
157 transition->growPropertyStorageCapacity();
158 return transition.release();
159 }
160
161 RefPtr<StructureID> transition = create(structureID->m_prototype, structureID->typeInfo());
162 transition->m_cachedPrototypeChain = structureID->m_cachedPrototypeChain;
163 transition->m_previous = structureID;
164 transition->m_nameInPrevious = propertyName.ustring().rep();
165 transition->m_attributesInPrevious = attributes;
166 transition->m_transitionCount = structureID->m_transitionCount + 1;
167 transition->m_propertyMap = structureID->m_propertyMap;
168 transition->m_propertyStorageCapacity = structureID->m_propertyStorageCapacity;
169 transition->m_hasGetterSetterProperties = structureID->m_hasGetterSetterProperties;
170
171 offset = transition->m_propertyMap.put(propertyName, attributes);
172 if (transition->m_propertyMap.storageSize() > transition->propertyStorageCapacity())
173 transition->growPropertyStorageCapacity();
174
175 transition->setCachedTransistionOffset(offset);
176
177 structureID->m_transitionTable.add(make_pair(propertyName.ustring().rep(), attributes), transition.get());
178 return transition.release();
179}
180
181PassRefPtr<StructureID> StructureID::toDictionaryTransition(StructureID* structureID)
182{
183 ASSERT(!structureID->m_isDictionary);
184
185 RefPtr<StructureID> transition = create(structureID->m_prototype, structureID->typeInfo());
186 transition->m_isDictionary = true;
187 transition->m_propertyMap = structureID->m_propertyMap;
188 transition->m_propertyStorageCapacity = structureID->m_propertyStorageCapacity;
189 transition->m_hasGetterSetterProperties = structureID->m_hasGetterSetterProperties;
190 return transition.release();
191}
192
193PassRefPtr<StructureID> StructureID::fromDictionaryTransition(StructureID* structureID)
194{
195 ASSERT(structureID->m_isDictionary);
196
197 // Since dictionary StructureIDs are not shared, and no opcodes specialize
198 // for them, we don't need to allocate a new StructureID when transitioning
199 // to non-dictionary status.
200 structureID->m_isDictionary = false;
201 return structureID;
202}
203
204PassRefPtr<StructureID> StructureID::changePrototypeTransition(StructureID* structureID, JSValue* prototype)
205{
206 RefPtr<StructureID> transition = create(prototype, structureID->typeInfo());
207 transition->m_transitionCount = structureID->m_transitionCount + 1;
208 transition->m_propertyMap = structureID->m_propertyMap;
209 transition->m_propertyStorageCapacity = structureID->m_propertyStorageCapacity;
210 transition->m_hasGetterSetterProperties = structureID->m_hasGetterSetterProperties;
211 return transition.release();
212}
213
214PassRefPtr<StructureID> StructureID::getterSetterTransition(StructureID* structureID)
215{
216 RefPtr<StructureID> transition = create(structureID->storedPrototype(), structureID->typeInfo());
217 transition->m_transitionCount = structureID->m_transitionCount + 1;
218 transition->m_propertyMap = structureID->m_propertyMap;
219 transition->m_propertyStorageCapacity = structureID->m_propertyStorageCapacity;
220 transition->m_hasGetterSetterProperties = transition->m_hasGetterSetterProperties;
221 return transition.release();
222}
223
224StructureIDChain* StructureID::createCachedPrototypeChain()
225{
226 ASSERT(typeInfo().type() == ObjectType);
227 ASSERT(!m_cachedPrototypeChain);
228
229 JSValue* prototype = storedPrototype();
230 if (JSImmediate::isImmediate(prototype))
231 return 0;
232
233 RefPtr<StructureIDChain> chain = StructureIDChain::create(static_cast<JSObject*>(prototype)->structureID());
234 setCachedPrototypeChain(chain.release());
235 return cachedPrototypeChain();
236}
237
238StructureIDChain::StructureIDChain(StructureID* structureID)
239{
240 size_t size = 1;
241
242 StructureID* tmp = structureID;
243 while (!tmp->storedPrototype()->isNull()) {
244 ++size;
245 tmp = static_cast<JSCell*>(tmp->storedPrototype())->structureID();
246 }
247
248 m_vector.set(new RefPtr<StructureID>[size + 1]);
249
250 size_t i;
251 for (i = 0; i < size - 1; ++i) {
252 m_vector[i] = structureID;
253 structureID = static_cast<JSObject*>(structureID->storedPrototype())->structureID();
254 }
255 m_vector[i] = structureID;
256 m_vector[i + 1] = 0;
257}
258
259bool structureIDChainsAreEqual(StructureIDChain* chainA, StructureIDChain* chainB)
260{
261 if (!chainA || !chainB)
262 return false;
263
264 RefPtr<StructureID>* a = chainA->head();
265 RefPtr<StructureID>* b = chainB->head();
266 while (1) {
267 if (*a != *b)
268 return false;
269 if (!*a)
270 return true;
271 a++;
272 b++;
273 }
274}
275
276} // namespace JSC
Note: See TracBrowser for help on using the repository browser.