]>
Commit | Line | Data |
---|---|---|
9dae56ea | 1 | /* |
ba379fdc | 2 | * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. |
9dae56ea A |
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 "Structure.h" | |
28 | ||
29 | #include "Identifier.h" | |
30 | #include "JSObject.h" | |
f9bf01c6 A |
31 | #include "JSPropertyNameIterator.h" |
32 | #include "Lookup.h" | |
9dae56ea A |
33 | #include "PropertyNameArray.h" |
34 | #include "StructureChain.h" | |
9dae56ea A |
35 | #include <wtf/RefCountedLeakCounter.h> |
36 | #include <wtf/RefPtr.h> | |
9dae56ea | 37 | #include <wtf/Threading.h> |
9dae56ea A |
38 | |
39 | #define DUMP_STRUCTURE_ID_STATISTICS 0 | |
40 | ||
41 | #ifndef NDEBUG | |
42 | #define DO_PROPERTYMAP_CONSTENCY_CHECK 0 | |
43 | #else | |
44 | #define DO_PROPERTYMAP_CONSTENCY_CHECK 0 | |
45 | #endif | |
46 | ||
47 | using namespace std; | |
48 | using namespace WTF; | |
49 | ||
14957cd0 | 50 | #if DUMP_PROPERTYMAP_STATS |
9dae56ea | 51 | |
14957cd0 A |
52 | int numProbes; |
53 | int numCollisions; | |
54 | int numRehashes; | |
55 | int numRemoves; | |
9dae56ea | 56 | |
9dae56ea A |
57 | #endif |
58 | ||
14957cd0 | 59 | namespace JSC { |
9dae56ea A |
60 | |
61 | #if DUMP_STRUCTURE_ID_STATISTICS | |
ba379fdc | 62 | static HashSet<Structure*>& liveStructureSet = *(new HashSet<Structure*>); |
9dae56ea A |
63 | #endif |
64 | ||
14957cd0 | 65 | bool StructureTransitionTable::contains(StringImpl* rep, unsigned attributes) const |
4e4e5a6f | 66 | { |
14957cd0 A |
67 | if (isUsingSingleSlot()) { |
68 | Structure* transition = singleTransition(); | |
69 | return transition && transition->m_nameInPrevious == rep && transition->m_attributesInPrevious == attributes; | |
4e4e5a6f | 70 | } |
14957cd0 | 71 | return map()->contains(make_pair(rep, attributes)); |
4e4e5a6f A |
72 | } |
73 | ||
14957cd0 | 74 | inline Structure* StructureTransitionTable::get(StringImpl* rep, unsigned attributes) const |
4e4e5a6f | 75 | { |
14957cd0 | 76 | if (isUsingSingleSlot()) { |
4e4e5a6f | 77 | Structure* transition = singleTransition(); |
14957cd0 | 78 | return (transition && transition->m_nameInPrevious == rep && transition->m_attributesInPrevious == attributes) ? transition : 0; |
4e4e5a6f | 79 | } |
14957cd0 | 80 | return map()->get(make_pair(rep, attributes)); |
4e4e5a6f A |
81 | } |
82 | ||
14957cd0 | 83 | inline void StructureTransitionTable::add(JSGlobalData& globalData, Structure* structure) |
4e4e5a6f | 84 | { |
14957cd0 A |
85 | if (isUsingSingleSlot()) { |
86 | Structure* existingTransition = singleTransition(); | |
87 | ||
88 | // This handles the first transition being added. | |
89 | if (!existingTransition) { | |
90 | setSingleTransition(globalData, structure); | |
4e4e5a6f A |
91 | return; |
92 | } | |
14957cd0 A |
93 | |
94 | // This handles the second transition being added | |
95 | // (or the first transition being despecified!) | |
96 | setMap(new TransitionMap()); | |
97 | add(globalData, existingTransition); | |
4e4e5a6f | 98 | } |
14957cd0 A |
99 | |
100 | // Add the structure to the map. | |
101 | ||
102 | // Newer versions of the STL have an std::make_pair function that takes rvalue references. | |
103 | // When either of the parameters are bitfields, the C++ compiler will try to bind them as lvalues, which is invalid. To work around this, use unary "+" to make the parameter an rvalue. | |
104 | // See https://bugs.webkit.org/show_bug.cgi?id=59261 for more details | |
6fe7ccc8 A |
105 | TransitionMap::AddResult result = map()->add(globalData, make_pair(structure->m_nameInPrevious, +structure->m_attributesInPrevious), structure); |
106 | if (!result.isNewEntry) { | |
14957cd0 | 107 | // There already is an entry! - we should only hit this when despecifying. |
6fe7ccc8 | 108 | ASSERT(result.iterator.get().second->m_specificValueInPrevious); |
14957cd0 | 109 | ASSERT(!structure->m_specificValueInPrevious); |
6fe7ccc8 | 110 | map()->set(globalData, result.iterator.get().first, structure); |
4e4e5a6f A |
111 | } |
112 | } | |
113 | ||
9dae56ea A |
114 | void Structure::dumpStatistics() |
115 | { | |
116 | #if DUMP_STRUCTURE_ID_STATISTICS | |
117 | unsigned numberLeaf = 0; | |
118 | unsigned numberUsingSingleSlot = 0; | |
119 | unsigned numberSingletons = 0; | |
120 | unsigned numberWithPropertyMaps = 0; | |
121 | unsigned totalPropertyMapsSize = 0; | |
122 | ||
123 | HashSet<Structure*>::const_iterator end = liveStructureSet.end(); | |
124 | for (HashSet<Structure*>::const_iterator it = liveStructureSet.begin(); it != end; ++it) { | |
125 | Structure* structure = *it; | |
14957cd0 A |
126 | |
127 | switch (structure->m_transitionTable.size()) { | |
128 | case 0: | |
9dae56ea | 129 | ++numberLeaf; |
14957cd0 A |
130 | if (!structure->m_previous) |
131 | ++numberSingletons; | |
132 | break; | |
9dae56ea | 133 | |
14957cd0 A |
134 | case 1: |
135 | ++numberUsingSingleSlot; | |
136 | break; | |
9dae56ea A |
137 | } |
138 | ||
139 | if (structure->m_propertyTable) { | |
140 | ++numberWithPropertyMaps; | |
14957cd0 | 141 | totalPropertyMapsSize += structure->m_propertyTable->sizeInMemory(); |
9dae56ea A |
142 | } |
143 | } | |
144 | ||
6fe7ccc8 A |
145 | dataLog("Number of live Structures: %d\n", liveStructureSet.size()); |
146 | dataLog("Number of Structures using the single item optimization for transition map: %d\n", numberUsingSingleSlot); | |
147 | dataLog("Number of Structures that are leaf nodes: %d\n", numberLeaf); | |
148 | dataLog("Number of Structures that singletons: %d\n", numberSingletons); | |
149 | dataLog("Number of Structures with PropertyMaps: %d\n", numberWithPropertyMaps); | |
9dae56ea | 150 | |
6fe7ccc8 A |
151 | dataLog("Size of a single Structures: %d\n", static_cast<unsigned>(sizeof(Structure))); |
152 | dataLog("Size of sum of all property maps: %d\n", totalPropertyMapsSize); | |
153 | dataLog("Size of average of all property maps: %f\n", static_cast<double>(totalPropertyMapsSize) / static_cast<double>(liveStructureSet.size())); | |
9dae56ea | 154 | #else |
6fe7ccc8 | 155 | dataLog("Dumping Structure statistics is not enabled.\n"); |
9dae56ea A |
156 | #endif |
157 | } | |
158 | ||
6fe7ccc8 | 159 | Structure::Structure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo) |
14957cd0 A |
160 | : JSCell(globalData, globalData.structureStructure.get()) |
161 | , m_typeInfo(typeInfo) | |
6fe7ccc8 | 162 | , m_globalObject(globalData, this, globalObject, WriteBarrier<JSGlobalObject>::MayBeNull) |
14957cd0 A |
163 | , m_prototype(globalData, this, prototype) |
164 | , m_classInfo(classInfo) | |
6fe7ccc8 | 165 | , m_propertyStorageCapacity(typeInfo.isFinalObject() ? JSFinalObject_inlineStorageCapacity : JSNonFinalObject_inlineStorageCapacity) |
9dae56ea | 166 | , m_offset(noOffset) |
ba379fdc | 167 | , m_dictionaryKind(NoneDictionaryKind) |
9dae56ea A |
168 | , m_isPinnedPropertyTable(false) |
169 | , m_hasGetterSetterProperties(false) | |
6fe7ccc8 | 170 | , m_hasReadOnlyOrGetterSetterPropertiesExcludingProto(false) |
14957cd0 | 171 | , m_hasNonEnumerableProperties(false) |
9dae56ea | 172 | , m_attributesInPrevious(0) |
f9bf01c6 | 173 | , m_specificFunctionThrashCount(0) |
14957cd0 A |
174 | , m_preventExtensions(false) |
175 | , m_didTransition(false) | |
6fe7ccc8 | 176 | , m_staticFunctionReified(false) |
9dae56ea | 177 | { |
9dae56ea A |
178 | } |
179 | ||
6fe7ccc8 | 180 | const ClassInfo Structure::s_info = { "Structure", 0, 0, 0, CREATE_METHOD_TABLE(Structure) }; |
9dae56ea | 181 | |
14957cd0 | 182 | Structure::Structure(JSGlobalData& globalData) |
6fe7ccc8 | 183 | : JSCell(CreatingEarlyCell) |
14957cd0 A |
184 | , m_typeInfo(CompoundType, OverridesVisitChildren) |
185 | , m_prototype(globalData, this, jsNull()) | |
186 | , m_classInfo(&s_info) | |
187 | , m_propertyStorageCapacity(0) | |
188 | , m_offset(noOffset) | |
189 | , m_dictionaryKind(NoneDictionaryKind) | |
190 | , m_isPinnedPropertyTable(false) | |
191 | , m_hasGetterSetterProperties(false) | |
6fe7ccc8 | 192 | , m_hasReadOnlyOrGetterSetterPropertiesExcludingProto(false) |
14957cd0 A |
193 | , m_hasNonEnumerableProperties(false) |
194 | , m_attributesInPrevious(0) | |
195 | , m_specificFunctionThrashCount(0) | |
14957cd0 A |
196 | , m_preventExtensions(false) |
197 | , m_didTransition(false) | |
6fe7ccc8 | 198 | , m_staticFunctionReified(false) |
9dae56ea | 199 | { |
9dae56ea A |
200 | } |
201 | ||
14957cd0 A |
202 | Structure::Structure(JSGlobalData& globalData, const Structure* previous) |
203 | : JSCell(globalData, globalData.structureStructure.get()) | |
204 | , m_typeInfo(previous->typeInfo()) | |
205 | , m_prototype(globalData, this, previous->storedPrototype()) | |
206 | , m_classInfo(previous->m_classInfo) | |
207 | , m_propertyStorageCapacity(previous->m_propertyStorageCapacity) | |
208 | , m_offset(noOffset) | |
6fe7ccc8 | 209 | , m_dictionaryKind(previous->m_dictionaryKind) |
14957cd0 A |
210 | , m_isPinnedPropertyTable(false) |
211 | , m_hasGetterSetterProperties(previous->m_hasGetterSetterProperties) | |
6fe7ccc8 | 212 | , m_hasReadOnlyOrGetterSetterPropertiesExcludingProto(previous->m_hasReadOnlyOrGetterSetterPropertiesExcludingProto) |
14957cd0 A |
213 | , m_hasNonEnumerableProperties(previous->m_hasNonEnumerableProperties) |
214 | , m_attributesInPrevious(0) | |
215 | , m_specificFunctionThrashCount(previous->m_specificFunctionThrashCount) | |
14957cd0 A |
216 | , m_preventExtensions(previous->m_preventExtensions) |
217 | , m_didTransition(true) | |
6fe7ccc8 | 218 | , m_staticFunctionReified(previous->m_staticFunctionReified) |
9dae56ea | 219 | { |
6fe7ccc8 A |
220 | if (previous->m_globalObject) |
221 | m_globalObject.set(globalData, this, previous->m_globalObject.get()); | |
9dae56ea A |
222 | } |
223 | ||
6fe7ccc8 | 224 | void Structure::destroy(JSCell* cell) |
9dae56ea | 225 | { |
6fe7ccc8 | 226 | jsCast<Structure*>(cell)->Structure::~Structure(); |
9dae56ea A |
227 | } |
228 | ||
14957cd0 | 229 | void Structure::materializePropertyMap(JSGlobalData& globalData) |
9dae56ea | 230 | { |
14957cd0 | 231 | ASSERT(structure()->classInfo() == &s_info); |
9dae56ea A |
232 | ASSERT(!m_propertyTable); |
233 | ||
234 | Vector<Structure*, 8> structures; | |
235 | structures.append(this); | |
236 | ||
237 | Structure* structure = this; | |
238 | ||
14957cd0 | 239 | // Search for the last Structure with a property table. |
9dae56ea A |
240 | while ((structure = structure->previousID())) { |
241 | if (structure->m_isPinnedPropertyTable) { | |
242 | ASSERT(structure->m_propertyTable); | |
243 | ASSERT(!structure->m_previous); | |
244 | ||
14957cd0 | 245 | m_propertyTable = structure->m_propertyTable->copy(globalData, 0, m_offset + 1); |
9dae56ea A |
246 | break; |
247 | } | |
248 | ||
249 | structures.append(structure); | |
250 | } | |
251 | ||
252 | if (!m_propertyTable) | |
14957cd0 | 253 | createPropertyMap(m_offset + 1); |
9dae56ea A |
254 | |
255 | for (ptrdiff_t i = structures.size() - 2; i >= 0; --i) { | |
256 | structure = structures[i]; | |
6fe7ccc8 | 257 | PropertyMapEntry entry(globalData, this, structure->m_nameInPrevious.get(), structure->m_offset, structure->m_attributesInPrevious, structure->m_specificValueInPrevious.get()); |
14957cd0 | 258 | m_propertyTable->add(entry); |
9dae56ea A |
259 | } |
260 | } | |
261 | ||
9dae56ea A |
262 | void Structure::growPropertyStorageCapacity() |
263 | { | |
14957cd0 A |
264 | if (isUsingInlineStorage()) |
265 | m_propertyStorageCapacity = JSObject::baseExternalStorageCapacity; | |
9dae56ea A |
266 | else |
267 | m_propertyStorageCapacity *= 2; | |
268 | } | |
269 | ||
6fe7ccc8 A |
270 | size_t Structure::suggestedNewPropertyStorageSize() |
271 | { | |
272 | if (isUsingInlineStorage()) | |
273 | return JSObject::baseExternalStorageCapacity; | |
274 | return m_propertyStorageCapacity * 2; | |
275 | } | |
276 | ||
14957cd0 | 277 | void Structure::despecifyDictionaryFunction(JSGlobalData& globalData, const Identifier& propertyName) |
ba379fdc | 278 | { |
14957cd0 | 279 | StringImpl* rep = propertyName.impl(); |
ba379fdc | 280 | |
14957cd0 | 281 | materializePropertyMapIfNecessary(globalData); |
ba379fdc A |
282 | |
283 | ASSERT(isDictionary()); | |
284 | ASSERT(m_propertyTable); | |
285 | ||
14957cd0 A |
286 | PropertyMapEntry* entry = m_propertyTable->find(rep).first; |
287 | ASSERT(entry); | |
288 | entry->specificValue.clear(); | |
ba379fdc A |
289 | } |
290 | ||
14957cd0 | 291 | Structure* Structure::addPropertyTransitionToExistingStructure(Structure* structure, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset) |
9dae56ea | 292 | { |
ba379fdc | 293 | ASSERT(!structure->isDictionary()); |
6fe7ccc8 | 294 | ASSERT(structure->isObject()); |
9dae56ea | 295 | |
14957cd0 A |
296 | if (Structure* existingTransition = structure->m_transitionTable.get(propertyName.impl(), attributes)) { |
297 | JSCell* specificValueInPrevious = existingTransition->m_specificValueInPrevious.get(); | |
298 | if (specificValueInPrevious && specificValueInPrevious != specificValue) | |
299 | return 0; | |
f9bf01c6 | 300 | ASSERT(existingTransition->m_offset != noOffset); |
6fe7ccc8 | 301 | offset = existingTransition->m_offset; |
f9bf01c6 | 302 | return existingTransition; |
9dae56ea A |
303 | } |
304 | ||
305 | return 0; | |
306 | } | |
307 | ||
14957cd0 | 308 | Structure* Structure::addPropertyTransition(JSGlobalData& globalData, Structure* structure, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset) |
9dae56ea | 309 | { |
14957cd0 A |
310 | // If we have a specific function, we may have got to this point if there is |
311 | // already a transition with the correct property name and attributes, but | |
312 | // specialized to a different function. In this case we just want to give up | |
313 | // and despecialize the transition. | |
314 | // In this case we clear the value of specificFunction which will result | |
315 | // in us adding a non-specific transition, and any subsequent lookup in | |
316 | // Structure::addPropertyTransitionToExistingStructure will just use that. | |
317 | if (specificValue && structure->m_transitionTable.contains(propertyName.impl(), attributes)) | |
318 | specificValue = 0; | |
319 | ||
ba379fdc | 320 | ASSERT(!structure->isDictionary()); |
6fe7ccc8 | 321 | ASSERT(structure->isObject()); |
ba379fdc | 322 | ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure, propertyName, attributes, specificValue, offset)); |
f9bf01c6 A |
323 | |
324 | if (structure->m_specificFunctionThrashCount == maxSpecificFunctionThrashCount) | |
325 | specificValue = 0; | |
9dae56ea A |
326 | |
327 | if (structure->transitionCount() > s_maxTransitionLength) { | |
14957cd0 | 328 | Structure* transition = toCacheableDictionaryTransition(globalData, structure); |
ba379fdc | 329 | ASSERT(structure != transition); |
14957cd0 | 330 | offset = transition->putSpecificValue(globalData, propertyName, attributes, specificValue); |
9dae56ea A |
331 | if (transition->propertyStorageSize() > transition->propertyStorageCapacity()) |
332 | transition->growPropertyStorageCapacity(); | |
14957cd0 | 333 | return transition; |
9dae56ea | 334 | } |
6fe7ccc8 | 335 | |
14957cd0 | 336 | Structure* transition = create(globalData, structure); |
ba379fdc | 337 | |
14957cd0 A |
338 | transition->m_cachedPrototypeChain.setMayBeNull(globalData, transition, structure->m_cachedPrototypeChain.get()); |
339 | transition->m_previous.set(globalData, transition, structure); | |
340 | transition->m_nameInPrevious = propertyName.impl(); | |
9dae56ea | 341 | transition->m_attributesInPrevious = attributes; |
14957cd0 | 342 | transition->m_specificValueInPrevious.setMayBeNull(globalData, transition, specificValue); |
9dae56ea A |
343 | |
344 | if (structure->m_propertyTable) { | |
345 | if (structure->m_isPinnedPropertyTable) | |
6fe7ccc8 | 346 | transition->m_propertyTable = structure->m_propertyTable->copy(globalData, transition, structure->m_propertyTable->size() + 1); |
14957cd0 A |
347 | else |
348 | transition->m_propertyTable = structure->m_propertyTable.release(); | |
9dae56ea A |
349 | } else { |
350 | if (structure->m_previous) | |
14957cd0 | 351 | transition->materializePropertyMap(globalData); |
9dae56ea | 352 | else |
14957cd0 | 353 | transition->createPropertyMap(); |
9dae56ea A |
354 | } |
355 | ||
14957cd0 | 356 | offset = transition->putSpecificValue(globalData, propertyName, attributes, specificValue); |
9dae56ea A |
357 | if (transition->propertyStorageSize() > transition->propertyStorageCapacity()) |
358 | transition->growPropertyStorageCapacity(); | |
359 | ||
6fe7ccc8 | 360 | transition->m_offset = offset; |
14957cd0 A |
361 | structure->m_transitionTable.add(globalData, transition); |
362 | return transition; | |
9dae56ea A |
363 | } |
364 | ||
14957cd0 | 365 | Structure* Structure::removePropertyTransition(JSGlobalData& globalData, Structure* structure, const Identifier& propertyName, size_t& offset) |
9dae56ea | 366 | { |
ba379fdc | 367 | ASSERT(!structure->isUncacheableDictionary()); |
9dae56ea | 368 | |
14957cd0 | 369 | Structure* transition = toUncacheableDictionaryTransition(globalData, structure); |
9dae56ea A |
370 | |
371 | offset = transition->remove(propertyName); | |
372 | ||
14957cd0 | 373 | return transition; |
9dae56ea A |
374 | } |
375 | ||
14957cd0 | 376 | Structure* Structure::changePrototypeTransition(JSGlobalData& globalData, Structure* structure, JSValue prototype) |
9dae56ea | 377 | { |
14957cd0 | 378 | Structure* transition = create(globalData, structure); |
9dae56ea | 379 | |
14957cd0 | 380 | transition->m_prototype.set(globalData, transition, prototype); |
9dae56ea A |
381 | |
382 | // Don't set m_offset, as one can not transition to this. | |
383 | ||
14957cd0 | 384 | structure->materializePropertyMapIfNecessary(globalData); |
6fe7ccc8 A |
385 | transition->m_propertyTable = structure->copyPropertyTableForPinning(globalData, transition); |
386 | transition->pin(); | |
387 | ||
14957cd0 | 388 | return transition; |
9dae56ea A |
389 | } |
390 | ||
14957cd0 | 391 | Structure* Structure::despecifyFunctionTransition(JSGlobalData& globalData, Structure* structure, const Identifier& replaceFunction) |
ba379fdc | 392 | { |
f9bf01c6 | 393 | ASSERT(structure->m_specificFunctionThrashCount < maxSpecificFunctionThrashCount); |
14957cd0 | 394 | Structure* transition = create(globalData, structure); |
ba379fdc | 395 | |
14957cd0 | 396 | ++transition->m_specificFunctionThrashCount; |
ba379fdc A |
397 | |
398 | // Don't set m_offset, as one can not transition to this. | |
399 | ||
14957cd0 | 400 | structure->materializePropertyMapIfNecessary(globalData); |
6fe7ccc8 A |
401 | transition->m_propertyTable = structure->copyPropertyTableForPinning(globalData, transition); |
402 | transition->pin(); | |
ba379fdc | 403 | |
f9bf01c6 | 404 | if (transition->m_specificFunctionThrashCount == maxSpecificFunctionThrashCount) |
14957cd0 | 405 | transition->despecifyAllFunctions(globalData); |
f9bf01c6 | 406 | else { |
14957cd0 | 407 | bool removed = transition->despecifyFunction(globalData, replaceFunction); |
f9bf01c6 A |
408 | ASSERT_UNUSED(removed, removed); |
409 | } | |
6fe7ccc8 | 410 | |
14957cd0 | 411 | return transition; |
ba379fdc A |
412 | } |
413 | ||
6fe7ccc8 | 414 | Structure* Structure::attributeChangeTransition(JSGlobalData& globalData, Structure* structure, const Identifier& propertyName, unsigned attributes) |
9dae56ea | 415 | { |
6fe7ccc8 A |
416 | if (!structure->isUncacheableDictionary()) { |
417 | Structure* transition = create(globalData, structure); | |
9dae56ea | 418 | |
6fe7ccc8 | 419 | // Don't set m_offset, as one can not transition to this. |
9dae56ea | 420 | |
6fe7ccc8 A |
421 | structure->materializePropertyMapIfNecessary(globalData); |
422 | transition->m_propertyTable = structure->copyPropertyTableForPinning(globalData, transition); | |
423 | transition->pin(); | |
424 | ||
425 | structure = transition; | |
426 | } | |
427 | ||
428 | ASSERT(structure->m_propertyTable); | |
429 | PropertyMapEntry* entry = structure->m_propertyTable->find(propertyName.impl()).first; | |
430 | ASSERT(entry); | |
431 | entry->attributes = attributes; | |
432 | ||
433 | return structure; | |
9dae56ea A |
434 | } |
435 | ||
14957cd0 | 436 | Structure* Structure::toDictionaryTransition(JSGlobalData& globalData, Structure* structure, DictionaryKind kind) |
9dae56ea | 437 | { |
ba379fdc A |
438 | ASSERT(!structure->isUncacheableDictionary()); |
439 | ||
14957cd0 A |
440 | Structure* transition = create(globalData, structure); |
441 | ||
442 | structure->materializePropertyMapIfNecessary(globalData); | |
6fe7ccc8 | 443 | transition->m_propertyTable = structure->copyPropertyTableForPinning(globalData, transition); |
ba379fdc | 444 | transition->m_dictionaryKind = kind; |
6fe7ccc8 A |
445 | transition->pin(); |
446 | ||
14957cd0 A |
447 | return transition; |
448 | } | |
449 | ||
450 | Structure* Structure::toCacheableDictionaryTransition(JSGlobalData& globalData, Structure* structure) | |
451 | { | |
452 | return toDictionaryTransition(globalData, structure, CachedDictionaryKind); | |
453 | } | |
454 | ||
455 | Structure* Structure::toUncacheableDictionaryTransition(JSGlobalData& globalData, Structure* structure) | |
456 | { | |
457 | return toDictionaryTransition(globalData, structure, UncachedDictionaryKind); | |
458 | } | |
459 | ||
460 | // In future we may want to cache this transition. | |
461 | Structure* Structure::sealTransition(JSGlobalData& globalData, Structure* structure) | |
462 | { | |
463 | Structure* transition = preventExtensionsTransition(globalData, structure); | |
464 | ||
465 | if (transition->m_propertyTable) { | |
466 | PropertyTable::iterator end = transition->m_propertyTable->end(); | |
467 | for (PropertyTable::iterator iter = transition->m_propertyTable->begin(); iter != end; ++iter) | |
468 | iter->attributes |= DontDelete; | |
469 | } | |
470 | ||
471 | return transition; | |
472 | } | |
473 | ||
474 | // In future we may want to cache this transition. | |
475 | Structure* Structure::freezeTransition(JSGlobalData& globalData, Structure* structure) | |
476 | { | |
477 | Structure* transition = preventExtensionsTransition(globalData, structure); | |
478 | ||
479 | if (transition->m_propertyTable) { | |
6fe7ccc8 | 480 | PropertyTable::iterator iter = transition->m_propertyTable->begin(); |
14957cd0 | 481 | PropertyTable::iterator end = transition->m_propertyTable->end(); |
6fe7ccc8 A |
482 | if (iter != end) |
483 | transition->m_hasReadOnlyOrGetterSetterPropertiesExcludingProto = true; | |
484 | for (; iter != end; ++iter) | |
485 | iter->attributes |= iter->attributes & Accessor ? DontDelete : (DontDelete | ReadOnly); | |
14957cd0 A |
486 | } |
487 | ||
488 | return transition; | |
489 | } | |
490 | ||
491 | // In future we may want to cache this transition. | |
492 | Structure* Structure::preventExtensionsTransition(JSGlobalData& globalData, Structure* structure) | |
493 | { | |
494 | Structure* transition = create(globalData, structure); | |
495 | ||
496 | // Don't set m_offset, as one can not transition to this. | |
497 | ||
498 | structure->materializePropertyMapIfNecessary(globalData); | |
6fe7ccc8 | 499 | transition->m_propertyTable = structure->copyPropertyTableForPinning(globalData, transition); |
14957cd0 | 500 | transition->m_preventExtensions = true; |
6fe7ccc8 | 501 | transition->pin(); |
14957cd0 | 502 | |
14957cd0 | 503 | return transition; |
9dae56ea A |
504 | } |
505 | ||
14957cd0 A |
506 | // In future we may want to cache this property. |
507 | bool Structure::isSealed(JSGlobalData& globalData) | |
9dae56ea | 508 | { |
14957cd0 A |
509 | if (isExtensible()) |
510 | return false; | |
511 | ||
512 | materializePropertyMapIfNecessary(globalData); | |
513 | if (!m_propertyTable) | |
514 | return true; | |
515 | ||
516 | PropertyTable::iterator end = m_propertyTable->end(); | |
517 | for (PropertyTable::iterator iter = m_propertyTable->begin(); iter != end; ++iter) { | |
518 | if ((iter->attributes & DontDelete) != DontDelete) | |
519 | return false; | |
520 | } | |
521 | return true; | |
ba379fdc | 522 | } |
9dae56ea | 523 | |
14957cd0 A |
524 | // In future we may want to cache this property. |
525 | bool Structure::isFrozen(JSGlobalData& globalData) | |
ba379fdc | 526 | { |
14957cd0 A |
527 | if (isExtensible()) |
528 | return false; | |
529 | ||
530 | materializePropertyMapIfNecessary(globalData); | |
531 | if (!m_propertyTable) | |
532 | return true; | |
533 | ||
534 | PropertyTable::iterator end = m_propertyTable->end(); | |
535 | for (PropertyTable::iterator iter = m_propertyTable->begin(); iter != end; ++iter) { | |
6fe7ccc8 A |
536 | if (!(iter->attributes & DontDelete)) |
537 | return false; | |
538 | if (!(iter->attributes & (ReadOnly | Accessor))) | |
14957cd0 A |
539 | return false; |
540 | } | |
541 | return true; | |
ba379fdc | 542 | } |
9dae56ea | 543 | |
14957cd0 | 544 | Structure* Structure::flattenDictionaryStructure(JSGlobalData& globalData, JSObject* object) |
ba379fdc A |
545 | { |
546 | ASSERT(isDictionary()); | |
547 | if (isUncacheableDictionary()) { | |
548 | ASSERT(m_propertyTable); | |
ba379fdc | 549 | |
14957cd0 A |
550 | size_t propertyCount = m_propertyTable->size(); |
551 | Vector<JSValue> values(propertyCount); | |
552 | ||
553 | unsigned i = 0; | |
554 | PropertyTable::iterator end = m_propertyTable->end(); | |
555 | for (PropertyTable::iterator iter = m_propertyTable->begin(); iter != end; ++iter, ++i) { | |
556 | values[i] = object->getDirectOffset(iter->offset); | |
ba379fdc | 557 | // Update property table to have the new property offsets |
6fe7ccc8 | 558 | iter->offset = i; |
ba379fdc A |
559 | } |
560 | ||
561 | // Copy the original property values into their final locations | |
562 | for (unsigned i = 0; i < propertyCount; i++) | |
6fe7ccc8 | 563 | object->putDirectOffset(globalData, i, values[i]); |
ba379fdc | 564 | |
14957cd0 | 565 | m_propertyTable->clearDeletedOffsets(); |
ba379fdc | 566 | } |
9dae56ea | 567 | |
ba379fdc A |
568 | m_dictionaryKind = NoneDictionaryKind; |
569 | return this; | |
9dae56ea A |
570 | } |
571 | ||
14957cd0 | 572 | size_t Structure::addPropertyWithoutTransition(JSGlobalData& globalData, const Identifier& propertyName, unsigned attributes, JSCell* specificValue) |
9dae56ea | 573 | { |
f9bf01c6 A |
574 | ASSERT(!m_enumerationCache); |
575 | ||
576 | if (m_specificFunctionThrashCount == maxSpecificFunctionThrashCount) | |
577 | specificValue = 0; | |
9dae56ea | 578 | |
6fe7ccc8 A |
579 | materializePropertyMapIfNecessaryForPinning(globalData); |
580 | ||
581 | pin(); | |
f9bf01c6 | 582 | |
14957cd0 | 583 | size_t offset = putSpecificValue(globalData, propertyName, attributes, specificValue); |
9dae56ea A |
584 | if (propertyStorageSize() > propertyStorageCapacity()) |
585 | growPropertyStorageCapacity(); | |
9dae56ea A |
586 | return offset; |
587 | } | |
588 | ||
14957cd0 | 589 | size_t Structure::removePropertyWithoutTransition(JSGlobalData& globalData, const Identifier& propertyName) |
9dae56ea | 590 | { |
ba379fdc | 591 | ASSERT(isUncacheableDictionary()); |
f9bf01c6 | 592 | ASSERT(!m_enumerationCache); |
9dae56ea | 593 | |
6fe7ccc8 | 594 | materializePropertyMapIfNecessaryForPinning(globalData); |
9dae56ea | 595 | |
6fe7ccc8 | 596 | pin(); |
9dae56ea | 597 | size_t offset = remove(propertyName); |
9dae56ea A |
598 | return offset; |
599 | } | |
600 | ||
6fe7ccc8 A |
601 | void Structure::pin() |
602 | { | |
603 | ASSERT(m_propertyTable); | |
604 | m_isPinnedPropertyTable = true; | |
605 | m_previous.clear(); | |
606 | m_nameInPrevious.clear(); | |
607 | } | |
608 | ||
9dae56ea A |
609 | #if DUMP_PROPERTYMAP_STATS |
610 | ||
9dae56ea A |
611 | struct PropertyMapStatisticsExitLogger { |
612 | ~PropertyMapStatisticsExitLogger(); | |
613 | }; | |
614 | ||
615 | static PropertyMapStatisticsExitLogger logger; | |
616 | ||
617 | PropertyMapStatisticsExitLogger::~PropertyMapStatisticsExitLogger() | |
618 | { | |
6fe7ccc8 A |
619 | dataLog("\nJSC::PropertyMap statistics\n\n"); |
620 | dataLog("%d probes\n", numProbes); | |
621 | dataLog("%d collisions (%.1f%%)\n", numCollisions, 100.0 * numCollisions / numProbes); | |
622 | dataLog("%d rehashes\n", numRehashes); | |
623 | dataLog("%d removes\n", numRemoves); | |
9dae56ea A |
624 | } |
625 | ||
626 | #endif | |
627 | ||
9dae56ea A |
628 | #if !DO_PROPERTYMAP_CONSTENCY_CHECK |
629 | ||
630 | inline void Structure::checkConsistency() | |
631 | { | |
632 | } | |
633 | ||
634 | #endif | |
635 | ||
14957cd0 | 636 | PassOwnPtr<PropertyTable> Structure::copyPropertyTable(JSGlobalData& globalData, Structure* owner) |
9dae56ea | 637 | { |
14957cd0 | 638 | return adoptPtr(m_propertyTable ? new PropertyTable(globalData, owner, *m_propertyTable) : 0); |
9dae56ea A |
639 | } |
640 | ||
6fe7ccc8 A |
641 | PassOwnPtr<PropertyTable> Structure::copyPropertyTableForPinning(JSGlobalData& globalData, Structure* owner) |
642 | { | |
643 | return adoptPtr(m_propertyTable ? new PropertyTable(globalData, owner, *m_propertyTable) : new PropertyTable(m_offset == noOffset ? 0 : m_offset)); | |
644 | } | |
645 | ||
14957cd0 | 646 | size_t Structure::get(JSGlobalData& globalData, StringImpl* propertyName, unsigned& attributes, JSCell*& specificValue) |
9dae56ea | 647 | { |
14957cd0 | 648 | materializePropertyMapIfNecessary(globalData); |
9dae56ea | 649 | if (!m_propertyTable) |
14957cd0 | 650 | return WTF::notFound; |
9dae56ea | 651 | |
14957cd0 A |
652 | PropertyMapEntry* entry = m_propertyTable->find(propertyName).first; |
653 | if (!entry) | |
654 | return WTF::notFound; | |
9dae56ea | 655 | |
14957cd0 A |
656 | attributes = entry->attributes; |
657 | specificValue = entry->specificValue.get(); | |
14957cd0 | 658 | return entry->offset; |
9dae56ea A |
659 | } |
660 | ||
14957cd0 | 661 | bool Structure::despecifyFunction(JSGlobalData& globalData, const Identifier& propertyName) |
ba379fdc | 662 | { |
14957cd0 | 663 | materializePropertyMapIfNecessary(globalData); |
ba379fdc A |
664 | if (!m_propertyTable) |
665 | return false; | |
666 | ||
14957cd0 A |
667 | ASSERT(!propertyName.isNull()); |
668 | PropertyMapEntry* entry = m_propertyTable->find(propertyName.impl()).first; | |
669 | if (!entry) | |
ba379fdc A |
670 | return false; |
671 | ||
14957cd0 A |
672 | ASSERT(entry->specificValue); |
673 | entry->specificValue.clear(); | |
674 | return true; | |
ba379fdc A |
675 | } |
676 | ||
14957cd0 | 677 | void Structure::despecifyAllFunctions(JSGlobalData& globalData) |
f9bf01c6 | 678 | { |
14957cd0 | 679 | materializePropertyMapIfNecessary(globalData); |
f9bf01c6 A |
680 | if (!m_propertyTable) |
681 | return; | |
14957cd0 A |
682 | |
683 | PropertyTable::iterator end = m_propertyTable->end(); | |
684 | for (PropertyTable::iterator iter = m_propertyTable->begin(); iter != end; ++iter) | |
685 | iter->specificValue.clear(); | |
f9bf01c6 A |
686 | } |
687 | ||
14957cd0 | 688 | size_t Structure::putSpecificValue(JSGlobalData& globalData, const Identifier& propertyName, unsigned attributes, JSCell* specificValue) |
9dae56ea A |
689 | { |
690 | ASSERT(!propertyName.isNull()); | |
14957cd0 | 691 | ASSERT(get(globalData, propertyName) == notFound); |
9dae56ea A |
692 | |
693 | checkConsistency(); | |
f9bf01c6 A |
694 | if (attributes & DontEnum) |
695 | m_hasNonEnumerableProperties = true; | |
696 | ||
14957cd0 | 697 | StringImpl* rep = propertyName.impl(); |
9dae56ea A |
698 | |
699 | if (!m_propertyTable) | |
14957cd0 | 700 | createPropertyMap(); |
9dae56ea A |
701 | |
702 | unsigned newOffset; | |
14957cd0 A |
703 | |
704 | if (m_propertyTable->hasDeletedOffset()) | |
705 | newOffset = m_propertyTable->getDeletedOffset(); | |
706 | else | |
6fe7ccc8 | 707 | newOffset = m_propertyTable->size(); |
9dae56ea | 708 | |
14957cd0 | 709 | m_propertyTable->add(PropertyMapEntry(globalData, this, rep, newOffset, attributes, specificValue)); |
9dae56ea A |
710 | |
711 | checkConsistency(); | |
712 | return newOffset; | |
713 | } | |
714 | ||
715 | size_t Structure::remove(const Identifier& propertyName) | |
716 | { | |
717 | ASSERT(!propertyName.isNull()); | |
718 | ||
719 | checkConsistency(); | |
720 | ||
14957cd0 | 721 | StringImpl* rep = propertyName.impl(); |
9dae56ea A |
722 | |
723 | if (!m_propertyTable) | |
724 | return notFound; | |
725 | ||
14957cd0 A |
726 | PropertyTable::find_iterator position = m_propertyTable->find(rep); |
727 | if (!position.first) | |
728 | return notFound; | |
9dae56ea | 729 | |
14957cd0 | 730 | size_t offset = position.first->offset; |
9dae56ea | 731 | |
14957cd0 A |
732 | m_propertyTable->remove(position); |
733 | m_propertyTable->addDeletedOffset(offset); | |
9dae56ea A |
734 | |
735 | checkConsistency(); | |
736 | return offset; | |
737 | } | |
738 | ||
14957cd0 | 739 | void Structure::createPropertyMap(unsigned capacity) |
9dae56ea A |
740 | { |
741 | ASSERT(!m_propertyTable); | |
9dae56ea A |
742 | |
743 | checkConsistency(); | |
14957cd0 | 744 | m_propertyTable = adoptPtr(new PropertyTable(capacity)); |
9dae56ea A |
745 | checkConsistency(); |
746 | } | |
747 | ||
6fe7ccc8 | 748 | void Structure::getPropertyNamesFromStructure(JSGlobalData& globalData, PropertyNameArray& propertyNames, EnumerationMode mode) |
9dae56ea | 749 | { |
14957cd0 A |
750 | materializePropertyMapIfNecessary(globalData); |
751 | if (!m_propertyTable) | |
752 | return; | |
9dae56ea | 753 | |
14957cd0 | 754 | bool knownUnique = !propertyNames.size(); |
9dae56ea | 755 | |
14957cd0 A |
756 | PropertyTable::iterator end = m_propertyTable->end(); |
757 | for (PropertyTable::iterator iter = m_propertyTable->begin(); iter != end; ++iter) { | |
758 | ASSERT(m_hasNonEnumerableProperties || !(iter->attributes & DontEnum)); | |
759 | if (!(iter->attributes & DontEnum) || (mode == IncludeDontEnumProperties)) { | |
760 | if (knownUnique) | |
761 | propertyNames.addKnownUnique(iter->key); | |
762 | else | |
763 | propertyNames.add(iter->key); | |
9dae56ea A |
764 | } |
765 | } | |
9dae56ea A |
766 | } |
767 | ||
6fe7ccc8 A |
768 | void Structure::visitChildren(JSCell* cell, SlotVisitor& visitor) |
769 | { | |
770 | Structure* thisObject = jsCast<Structure*>(cell); | |
771 | ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); | |
772 | ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); | |
773 | JSCell::visitChildren(thisObject, visitor); | |
774 | if (thisObject->m_globalObject) | |
775 | visitor.append(&thisObject->m_globalObject); | |
776 | if (!thisObject->isObject()) | |
777 | thisObject->m_cachedPrototypeChain.clear(); | |
778 | else { | |
779 | if (thisObject->m_prototype) | |
780 | visitor.append(&thisObject->m_prototype); | |
781 | if (thisObject->m_cachedPrototypeChain) | |
782 | visitor.append(&thisObject->m_cachedPrototypeChain); | |
783 | } | |
784 | if (thisObject->m_previous) | |
785 | visitor.append(&thisObject->m_previous); | |
786 | if (thisObject->m_specificValueInPrevious) | |
787 | visitor.append(&thisObject->m_specificValueInPrevious); | |
788 | if (thisObject->m_enumerationCache) | |
789 | visitor.append(&thisObject->m_enumerationCache); | |
790 | if (thisObject->m_propertyTable) { | |
791 | PropertyTable::iterator end = thisObject->m_propertyTable->end(); | |
792 | for (PropertyTable::iterator ptr = thisObject->m_propertyTable->begin(); ptr != end; ++ptr) { | |
14957cd0 A |
793 | if (ptr->specificValue) |
794 | visitor.append(&ptr->specificValue); | |
9dae56ea | 795 | } |
9dae56ea | 796 | } |
6fe7ccc8 A |
797 | if (thisObject->m_objectToStringValue) |
798 | visitor.append(&thisObject->m_objectToStringValue); | |
9dae56ea A |
799 | } |
800 | ||
9dae56ea A |
801 | #if DO_PROPERTYMAP_CONSTENCY_CHECK |
802 | ||
14957cd0 | 803 | void PropertyTable::checkConsistency() |
9dae56ea | 804 | { |
14957cd0 A |
805 | ASSERT(m_indexSize >= PropertyTable::MinimumTableSize); |
806 | ASSERT(m_indexMask); | |
807 | ASSERT(m_indexSize == m_indexMask + 1); | |
808 | ASSERT(!(m_indexSize & m_indexMask)); | |
9dae56ea | 809 | |
14957cd0 A |
810 | ASSERT(m_keyCount <= m_indexSize / 2); |
811 | ASSERT(m_keyCount + m_deletedCount <= m_indexSize / 2); | |
812 | ASSERT(m_deletedCount <= m_indexSize / 4); | |
9dae56ea A |
813 | |
814 | unsigned indexCount = 0; | |
815 | unsigned deletedIndexCount = 0; | |
14957cd0 A |
816 | for (unsigned a = 0; a != m_indexSize; ++a) { |
817 | unsigned entryIndex = m_index[a]; | |
818 | if (entryIndex == PropertyTable::EmptyEntryIndex) | |
9dae56ea | 819 | continue; |
14957cd0 | 820 | if (entryIndex == deletedEntryIndex()) { |
9dae56ea A |
821 | ++deletedIndexCount; |
822 | continue; | |
823 | } | |
14957cd0 A |
824 | ASSERT(entryIndex < deletedEntryIndex()); |
825 | ASSERT(entryIndex - 1 <= usedCount()); | |
9dae56ea A |
826 | ++indexCount; |
827 | ||
14957cd0 A |
828 | for (unsigned b = a + 1; b != m_indexSize; ++b) |
829 | ASSERT(m_index[b] != entryIndex); | |
9dae56ea | 830 | } |
14957cd0 A |
831 | ASSERT(indexCount == m_keyCount); |
832 | ASSERT(deletedIndexCount == m_deletedCount); | |
9dae56ea | 833 | |
14957cd0 | 834 | ASSERT(!table()[deletedEntryIndex() - 1].key); |
9dae56ea A |
835 | |
836 | unsigned nonEmptyEntryCount = 0; | |
14957cd0 A |
837 | for (unsigned c = 0; c < usedCount(); ++c) { |
838 | StringImpl* rep = table()[c].key; | |
839 | if (rep == PROPERTY_MAP_DELETED_ENTRY_KEY) | |
9dae56ea A |
840 | continue; |
841 | ++nonEmptyEntryCount; | |
f9bf01c6 | 842 | unsigned i = rep->existingHash(); |
9dae56ea A |
843 | unsigned k = 0; |
844 | unsigned entryIndex; | |
845 | while (1) { | |
14957cd0 A |
846 | entryIndex = m_index[i & m_indexMask]; |
847 | ASSERT(entryIndex != PropertyTable::EmptyEntryIndex); | |
848 | if (rep == table()[entryIndex - 1].key) | |
9dae56ea A |
849 | break; |
850 | if (k == 0) | |
f9bf01c6 | 851 | k = 1 | doubleHash(rep->existingHash()); |
9dae56ea A |
852 | i += k; |
853 | } | |
854 | ASSERT(entryIndex == c + 1); | |
855 | } | |
856 | ||
14957cd0 A |
857 | ASSERT(nonEmptyEntryCount == m_keyCount); |
858 | } | |
859 | ||
860 | void Structure::checkConsistency() | |
861 | { | |
862 | if (!m_propertyTable) | |
863 | return; | |
864 | ||
865 | if (!m_hasNonEnumerableProperties) { | |
866 | PropertyTable::iterator end = m_propertyTable->end(); | |
867 | for (PropertyTable::iterator iter = m_propertyTable->begin(); iter != end; ++iter) { | |
868 | ASSERT(!(iter->attributes & DontEnum)); | |
14957cd0 A |
869 | } |
870 | } | |
871 | ||
872 | m_propertyTable->checkConsistency(); | |
9dae56ea A |
873 | } |
874 | ||
875 | #endif // DO_PROPERTYMAP_CONSTENCY_CHECK | |
876 | ||
877 | } // namespace JSC |