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