]>
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" | |
31 | #include "PropertyNameArray.h" | |
32 | #include "StructureChain.h" | |
33 | #include "Lookup.h" | |
34 | #include <wtf/RefCountedLeakCounter.h> | |
35 | #include <wtf/RefPtr.h> | |
36 | ||
37 | #if ENABLE(JSC_MULTIPLE_THREADS) | |
38 | #include <wtf/Threading.h> | |
39 | #endif | |
40 | ||
41 | #define DUMP_STRUCTURE_ID_STATISTICS 0 | |
42 | ||
43 | #ifndef NDEBUG | |
44 | #define DO_PROPERTYMAP_CONSTENCY_CHECK 0 | |
45 | #else | |
46 | #define DO_PROPERTYMAP_CONSTENCY_CHECK 0 | |
47 | #endif | |
48 | ||
49 | using namespace std; | |
50 | using namespace WTF; | |
51 | ||
52 | namespace JSC { | |
53 | ||
54 | // Choose a number for the following so that most property maps are smaller, | |
55 | // but it's not going to blow out the stack to allocate this number of pointers. | |
56 | static const int smallMapThreshold = 1024; | |
57 | ||
58 | // The point at which the function call overhead of the qsort implementation | |
59 | // becomes small compared to the inefficiency of insertion sort. | |
60 | static const unsigned tinyMapThreshold = 20; | |
61 | ||
62 | static const unsigned newTableSize = 16; | |
63 | ||
64 | #ifndef NDEBUG | |
65 | static WTF::RefCountedLeakCounter structureCounter("Structure"); | |
66 | ||
67 | #if ENABLE(JSC_MULTIPLE_THREADS) | |
ba379fdc | 68 | static Mutex& ignoreSetMutex = *(new Mutex); |
9dae56ea A |
69 | #endif |
70 | ||
71 | static bool shouldIgnoreLeaks; | |
ba379fdc | 72 | static HashSet<Structure*>& ignoreSet = *(new HashSet<Structure*>); |
9dae56ea A |
73 | #endif |
74 | ||
75 | #if DUMP_STRUCTURE_ID_STATISTICS | |
ba379fdc | 76 | static HashSet<Structure*>& liveStructureSet = *(new HashSet<Structure*>); |
9dae56ea A |
77 | #endif |
78 | ||
ba379fdc A |
79 | static int comparePropertyMapEntryIndices(const void* a, const void* b); |
80 | ||
9dae56ea A |
81 | void Structure::dumpStatistics() |
82 | { | |
83 | #if DUMP_STRUCTURE_ID_STATISTICS | |
84 | unsigned numberLeaf = 0; | |
85 | unsigned numberUsingSingleSlot = 0; | |
86 | unsigned numberSingletons = 0; | |
87 | unsigned numberWithPropertyMaps = 0; | |
88 | unsigned totalPropertyMapsSize = 0; | |
89 | ||
90 | HashSet<Structure*>::const_iterator end = liveStructureSet.end(); | |
91 | for (HashSet<Structure*>::const_iterator it = liveStructureSet.begin(); it != end; ++it) { | |
92 | Structure* structure = *it; | |
93 | if (structure->m_usingSingleTransitionSlot) { | |
94 | if (!structure->m_transitions.singleTransition) | |
95 | ++numberLeaf; | |
96 | else | |
97 | ++numberUsingSingleSlot; | |
98 | ||
99 | if (!structure->m_previous && !structure->m_transitions.singleTransition) | |
100 | ++numberSingletons; | |
101 | } | |
102 | ||
103 | if (structure->m_propertyTable) { | |
104 | ++numberWithPropertyMaps; | |
105 | totalPropertyMapsSize += PropertyMapHashTable::allocationSize(structure->m_propertyTable->size); | |
106 | if (structure->m_propertyTable->deletedOffsets) | |
107 | totalPropertyMapsSize += (structure->m_propertyTable->deletedOffsets->capacity() * sizeof(unsigned)); | |
108 | } | |
109 | } | |
110 | ||
111 | printf("Number of live Structures: %d\n", liveStructureSet.size()); | |
112 | printf("Number of Structures using the single item optimization for transition map: %d\n", numberUsingSingleSlot); | |
113 | printf("Number of Structures that are leaf nodes: %d\n", numberLeaf); | |
114 | printf("Number of Structures that singletons: %d\n", numberSingletons); | |
115 | printf("Number of Structures with PropertyMaps: %d\n", numberWithPropertyMaps); | |
116 | ||
117 | printf("Size of a single Structures: %d\n", static_cast<unsigned>(sizeof(Structure))); | |
118 | printf("Size of sum of all property maps: %d\n", totalPropertyMapsSize); | |
119 | printf("Size of average of all property maps: %f\n", static_cast<double>(totalPropertyMapsSize) / static_cast<double>(liveStructureSet.size())); | |
120 | #else | |
121 | printf("Dumping Structure statistics is not enabled.\n"); | |
122 | #endif | |
123 | } | |
124 | ||
ba379fdc | 125 | Structure::Structure(JSValue prototype, const TypeInfo& typeInfo) |
9dae56ea A |
126 | : m_typeInfo(typeInfo) |
127 | , m_prototype(prototype) | |
ba379fdc | 128 | , m_specificValueInPrevious(0) |
9dae56ea A |
129 | , m_propertyTable(0) |
130 | , m_propertyStorageCapacity(JSObject::inlineStorageCapacity) | |
131 | , m_offset(noOffset) | |
ba379fdc | 132 | , m_dictionaryKind(NoneDictionaryKind) |
9dae56ea A |
133 | , m_isPinnedPropertyTable(false) |
134 | , m_hasGetterSetterProperties(false) | |
135 | , m_usingSingleTransitionSlot(true) | |
136 | , m_attributesInPrevious(0) | |
137 | { | |
138 | ASSERT(m_prototype); | |
139 | ASSERT(m_prototype.isObject() || m_prototype.isNull()); | |
140 | ||
141 | m_transitions.singleTransition = 0; | |
142 | ||
143 | #ifndef NDEBUG | |
144 | #if ENABLE(JSC_MULTIPLE_THREADS) | |
145 | MutexLocker protect(ignoreSetMutex); | |
146 | #endif | |
147 | if (shouldIgnoreLeaks) | |
148 | ignoreSet.add(this); | |
149 | else | |
150 | structureCounter.increment(); | |
151 | #endif | |
152 | ||
153 | #if DUMP_STRUCTURE_ID_STATISTICS | |
154 | liveStructureSet.add(this); | |
155 | #endif | |
156 | } | |
157 | ||
158 | Structure::~Structure() | |
159 | { | |
160 | if (m_previous) { | |
161 | if (m_previous->m_usingSingleTransitionSlot) { | |
162 | m_previous->m_transitions.singleTransition = 0; | |
163 | } else { | |
ba379fdc A |
164 | ASSERT(m_previous->m_transitions.table->contains(make_pair(m_nameInPrevious.get(), m_attributesInPrevious), m_specificValueInPrevious)); |
165 | m_previous->m_transitions.table->remove(make_pair(m_nameInPrevious.get(), m_attributesInPrevious), m_specificValueInPrevious); | |
9dae56ea A |
166 | } |
167 | } | |
168 | ||
169 | if (m_cachedPropertyNameArrayData) | |
170 | m_cachedPropertyNameArrayData->setCachedStructure(0); | |
171 | ||
172 | if (!m_usingSingleTransitionSlot) | |
173 | delete m_transitions.table; | |
174 | ||
175 | if (m_propertyTable) { | |
176 | unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; | |
177 | for (unsigned i = 1; i <= entryCount; i++) { | |
178 | if (UString::Rep* key = m_propertyTable->entries()[i].key) | |
179 | key->deref(); | |
180 | } | |
181 | ||
182 | delete m_propertyTable->deletedOffsets; | |
183 | fastFree(m_propertyTable); | |
184 | } | |
185 | ||
186 | #ifndef NDEBUG | |
187 | #if ENABLE(JSC_MULTIPLE_THREADS) | |
188 | MutexLocker protect(ignoreSetMutex); | |
189 | #endif | |
190 | HashSet<Structure*>::iterator it = ignoreSet.find(this); | |
191 | if (it != ignoreSet.end()) | |
192 | ignoreSet.remove(it); | |
193 | else | |
194 | structureCounter.decrement(); | |
195 | #endif | |
196 | ||
197 | #if DUMP_STRUCTURE_ID_STATISTICS | |
198 | liveStructureSet.remove(this); | |
199 | #endif | |
200 | } | |
201 | ||
202 | void Structure::startIgnoringLeaks() | |
203 | { | |
204 | #ifndef NDEBUG | |
205 | shouldIgnoreLeaks = true; | |
206 | #endif | |
207 | } | |
208 | ||
209 | void Structure::stopIgnoringLeaks() | |
210 | { | |
211 | #ifndef NDEBUG | |
212 | shouldIgnoreLeaks = false; | |
213 | #endif | |
214 | } | |
215 | ||
216 | static bool isPowerOf2(unsigned v) | |
217 | { | |
218 | // Taken from http://www.cs.utk.edu/~vose/c-stuff/bithacks.html | |
219 | ||
220 | return !(v & (v - 1)) && v; | |
221 | } | |
222 | ||
223 | static unsigned nextPowerOf2(unsigned v) | |
224 | { | |
225 | // Taken from http://www.cs.utk.edu/~vose/c-stuff/bithacks.html | |
226 | // Devised by Sean Anderson, Sepember 14, 2001 | |
227 | ||
228 | v--; | |
229 | v |= v >> 1; | |
230 | v |= v >> 2; | |
231 | v |= v >> 4; | |
232 | v |= v >> 8; | |
233 | v |= v >> 16; | |
234 | v++; | |
235 | ||
236 | return v; | |
237 | } | |
238 | ||
239 | static unsigned sizeForKeyCount(size_t keyCount) | |
240 | { | |
241 | if (keyCount == notFound) | |
242 | return newTableSize; | |
243 | ||
244 | if (keyCount < 8) | |
245 | return newTableSize; | |
246 | ||
247 | if (isPowerOf2(keyCount)) | |
248 | return keyCount * 4; | |
249 | ||
250 | return nextPowerOf2(keyCount) * 2; | |
251 | } | |
252 | ||
253 | void Structure::materializePropertyMap() | |
254 | { | |
255 | ASSERT(!m_propertyTable); | |
256 | ||
257 | Vector<Structure*, 8> structures; | |
258 | structures.append(this); | |
259 | ||
260 | Structure* structure = this; | |
261 | ||
262 | // Search for the last Structure with a property table. | |
263 | while ((structure = structure->previousID())) { | |
264 | if (structure->m_isPinnedPropertyTable) { | |
265 | ASSERT(structure->m_propertyTable); | |
266 | ASSERT(!structure->m_previous); | |
267 | ||
268 | m_propertyTable = structure->copyPropertyTable(); | |
269 | break; | |
270 | } | |
271 | ||
272 | structures.append(structure); | |
273 | } | |
274 | ||
275 | if (!m_propertyTable) | |
276 | createPropertyMapHashTable(sizeForKeyCount(m_offset + 1)); | |
277 | else { | |
278 | if (sizeForKeyCount(m_offset + 1) > m_propertyTable->size) | |
279 | rehashPropertyMapHashTable(sizeForKeyCount(m_offset + 1)); // This could be made more efficient by combining with the copy above. | |
280 | } | |
281 | ||
282 | for (ptrdiff_t i = structures.size() - 2; i >= 0; --i) { | |
283 | structure = structures[i]; | |
284 | structure->m_nameInPrevious->ref(); | |
ba379fdc | 285 | PropertyMapEntry entry(structure->m_nameInPrevious.get(), structure->m_offset, structure->m_attributesInPrevious, structure->m_specificValueInPrevious, ++m_propertyTable->lastIndexUsed); |
9dae56ea A |
286 | insertIntoPropertyMapHashTable(entry); |
287 | } | |
288 | } | |
289 | ||
290 | void Structure::getEnumerablePropertyNames(ExecState* exec, PropertyNameArray& propertyNames, JSObject* baseObject) | |
291 | { | |
ba379fdc | 292 | bool shouldCache = propertyNames.shouldCache() && !(propertyNames.size() || isDictionary()); |
9dae56ea A |
293 | |
294 | if (shouldCache && m_cachedPropertyNameArrayData) { | |
295 | if (m_cachedPropertyNameArrayData->cachedPrototypeChain() == prototypeChain(exec)) { | |
296 | propertyNames.setData(m_cachedPropertyNameArrayData); | |
297 | return; | |
298 | } | |
299 | clearEnumerationCache(); | |
300 | } | |
301 | ||
302 | getEnumerableNamesFromPropertyTable(propertyNames); | |
303 | getEnumerableNamesFromClassInfoTable(exec, baseObject->classInfo(), propertyNames); | |
304 | ||
305 | if (m_prototype.isObject()) { | |
306 | propertyNames.setShouldCache(false); // No need for our prototypes to waste memory on caching, since they're not being enumerated directly. | |
307 | asObject(m_prototype)->getPropertyNames(exec, propertyNames); | |
308 | } | |
309 | ||
310 | if (shouldCache) { | |
ba379fdc | 311 | StructureChain* protoChain = prototypeChain(exec); |
9dae56ea | 312 | m_cachedPropertyNameArrayData = propertyNames.data(); |
ba379fdc A |
313 | if (!protoChain->isCacheable()) |
314 | return; | |
315 | m_cachedPropertyNameArrayData->setCachedPrototypeChain(protoChain); | |
9dae56ea A |
316 | m_cachedPropertyNameArrayData->setCachedStructure(this); |
317 | } | |
318 | } | |
319 | ||
320 | void Structure::clearEnumerationCache() | |
321 | { | |
322 | if (m_cachedPropertyNameArrayData) | |
323 | m_cachedPropertyNameArrayData->setCachedStructure(0); | |
324 | m_cachedPropertyNameArrayData.clear(); | |
325 | } | |
326 | ||
327 | void Structure::growPropertyStorageCapacity() | |
328 | { | |
329 | if (m_propertyStorageCapacity == JSObject::inlineStorageCapacity) | |
330 | m_propertyStorageCapacity = JSObject::nonInlineBaseStorageCapacity; | |
331 | else | |
332 | m_propertyStorageCapacity *= 2; | |
333 | } | |
334 | ||
ba379fdc A |
335 | void Structure::despecifyDictionaryFunction(const Identifier& propertyName) |
336 | { | |
337 | const UString::Rep* rep = propertyName._ustring.rep(); | |
338 | ||
339 | materializePropertyMapIfNecessary(); | |
340 | ||
341 | ASSERT(isDictionary()); | |
342 | ASSERT(m_propertyTable); | |
343 | ||
344 | unsigned i = rep->computedHash(); | |
345 | ||
346 | #if DUMP_PROPERTYMAP_STATS | |
347 | ++numProbes; | |
348 | #endif | |
349 | ||
350 | unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; | |
351 | ASSERT(entryIndex != emptyEntryIndex); | |
352 | ||
353 | if (rep == m_propertyTable->entries()[entryIndex - 1].key) { | |
354 | m_propertyTable->entries()[entryIndex - 1].specificValue = 0; | |
355 | return; | |
356 | } | |
357 | ||
358 | #if DUMP_PROPERTYMAP_STATS | |
359 | ++numCollisions; | |
360 | #endif | |
361 | ||
362 | unsigned k = 1 | doubleHash(rep->computedHash()); | |
363 | ||
364 | while (1) { | |
365 | i += k; | |
366 | ||
367 | #if DUMP_PROPERTYMAP_STATS | |
368 | ++numRehashes; | |
369 | #endif | |
370 | ||
371 | entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; | |
372 | ASSERT(entryIndex != emptyEntryIndex); | |
373 | ||
374 | if (rep == m_propertyTable->entries()[entryIndex - 1].key) { | |
375 | m_propertyTable->entries()[entryIndex - 1].specificValue = 0; | |
376 | return; | |
377 | } | |
378 | } | |
379 | } | |
380 | ||
381 | PassRefPtr<Structure> Structure::addPropertyTransitionToExistingStructure(Structure* structure, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset) | |
9dae56ea | 382 | { |
ba379fdc | 383 | ASSERT(!structure->isDictionary()); |
9dae56ea A |
384 | ASSERT(structure->typeInfo().type() == ObjectType); |
385 | ||
386 | if (structure->m_usingSingleTransitionSlot) { | |
387 | Structure* existingTransition = structure->m_transitions.singleTransition; | |
ba379fdc A |
388 | if (existingTransition && existingTransition->m_nameInPrevious.get() == propertyName.ustring().rep() |
389 | && existingTransition->m_attributesInPrevious == attributes | |
390 | && (existingTransition->m_specificValueInPrevious == specificValue || existingTransition->m_specificValueInPrevious == 0)) { | |
391 | ||
9dae56ea A |
392 | ASSERT(structure->m_transitions.singleTransition->m_offset != noOffset); |
393 | offset = structure->m_transitions.singleTransition->m_offset; | |
394 | return existingTransition; | |
395 | } | |
396 | } else { | |
ba379fdc | 397 | if (Structure* existingTransition = structure->m_transitions.table->get(make_pair(propertyName.ustring().rep(), attributes), specificValue)) { |
9dae56ea A |
398 | ASSERT(existingTransition->m_offset != noOffset); |
399 | offset = existingTransition->m_offset; | |
400 | return existingTransition; | |
401 | } | |
402 | } | |
403 | ||
404 | return 0; | |
405 | } | |
406 | ||
ba379fdc | 407 | PassRefPtr<Structure> Structure::addPropertyTransition(Structure* structure, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset) |
9dae56ea | 408 | { |
ba379fdc | 409 | ASSERT(!structure->isDictionary()); |
9dae56ea | 410 | ASSERT(structure->typeInfo().type() == ObjectType); |
ba379fdc | 411 | ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure, propertyName, attributes, specificValue, offset)); |
9dae56ea A |
412 | |
413 | if (structure->transitionCount() > s_maxTransitionLength) { | |
ba379fdc A |
414 | RefPtr<Structure> transition = toCacheableDictionaryTransition(structure); |
415 | ASSERT(structure != transition); | |
416 | offset = transition->put(propertyName, attributes, specificValue); | |
9dae56ea A |
417 | if (transition->propertyStorageSize() > transition->propertyStorageCapacity()) |
418 | transition->growPropertyStorageCapacity(); | |
419 | return transition.release(); | |
420 | } | |
421 | ||
422 | RefPtr<Structure> transition = create(structure->m_prototype, structure->typeInfo()); | |
ba379fdc | 423 | |
9dae56ea A |
424 | transition->m_cachedPrototypeChain = structure->m_cachedPrototypeChain; |
425 | transition->m_previous = structure; | |
426 | transition->m_nameInPrevious = propertyName.ustring().rep(); | |
427 | transition->m_attributesInPrevious = attributes; | |
ba379fdc | 428 | transition->m_specificValueInPrevious = specificValue; |
9dae56ea A |
429 | transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity; |
430 | transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties; | |
431 | ||
432 | if (structure->m_propertyTable) { | |
433 | if (structure->m_isPinnedPropertyTable) | |
434 | transition->m_propertyTable = structure->copyPropertyTable(); | |
435 | else { | |
436 | transition->m_propertyTable = structure->m_propertyTable; | |
437 | structure->m_propertyTable = 0; | |
438 | } | |
439 | } else { | |
440 | if (structure->m_previous) | |
441 | transition->materializePropertyMap(); | |
442 | else | |
443 | transition->createPropertyMapHashTable(); | |
444 | } | |
445 | ||
ba379fdc | 446 | offset = transition->put(propertyName, attributes, specificValue); |
9dae56ea A |
447 | if (transition->propertyStorageSize() > transition->propertyStorageCapacity()) |
448 | transition->growPropertyStorageCapacity(); | |
449 | ||
450 | transition->m_offset = offset; | |
451 | ||
452 | if (structure->m_usingSingleTransitionSlot) { | |
453 | if (!structure->m_transitions.singleTransition) { | |
454 | structure->m_transitions.singleTransition = transition.get(); | |
455 | return transition.release(); | |
456 | } | |
457 | ||
458 | Structure* existingTransition = structure->m_transitions.singleTransition; | |
459 | structure->m_usingSingleTransitionSlot = false; | |
460 | StructureTransitionTable* transitionTable = new StructureTransitionTable; | |
461 | structure->m_transitions.table = transitionTable; | |
ba379fdc | 462 | transitionTable->add(make_pair(existingTransition->m_nameInPrevious.get(), existingTransition->m_attributesInPrevious), existingTransition, existingTransition->m_specificValueInPrevious); |
9dae56ea | 463 | } |
ba379fdc | 464 | structure->m_transitions.table->add(make_pair(propertyName.ustring().rep(), attributes), transition.get(), specificValue); |
9dae56ea A |
465 | return transition.release(); |
466 | } | |
467 | ||
468 | PassRefPtr<Structure> Structure::removePropertyTransition(Structure* structure, const Identifier& propertyName, size_t& offset) | |
469 | { | |
ba379fdc | 470 | ASSERT(!structure->isUncacheableDictionary()); |
9dae56ea | 471 | |
ba379fdc | 472 | RefPtr<Structure> transition = toUncacheableDictionaryTransition(structure); |
9dae56ea A |
473 | |
474 | offset = transition->remove(propertyName); | |
475 | ||
476 | return transition.release(); | |
477 | } | |
478 | ||
ba379fdc | 479 | PassRefPtr<Structure> Structure::changePrototypeTransition(Structure* structure, JSValue prototype) |
9dae56ea A |
480 | { |
481 | RefPtr<Structure> transition = create(prototype, structure->typeInfo()); | |
482 | ||
483 | transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity; | |
484 | transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties; | |
485 | ||
486 | // Don't set m_offset, as one can not transition to this. | |
487 | ||
488 | structure->materializePropertyMapIfNecessary(); | |
489 | transition->m_propertyTable = structure->copyPropertyTable(); | |
490 | transition->m_isPinnedPropertyTable = true; | |
491 | ||
492 | return transition.release(); | |
493 | } | |
494 | ||
ba379fdc A |
495 | PassRefPtr<Structure> Structure::despecifyFunctionTransition(Structure* structure, const Identifier& replaceFunction) |
496 | { | |
497 | RefPtr<Structure> transition = create(structure->storedPrototype(), structure->typeInfo()); | |
498 | ||
499 | transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity; | |
500 | transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties; | |
501 | ||
502 | // Don't set m_offset, as one can not transition to this. | |
503 | ||
504 | structure->materializePropertyMapIfNecessary(); | |
505 | transition->m_propertyTable = structure->copyPropertyTable(); | |
506 | transition->m_isPinnedPropertyTable = true; | |
507 | ||
508 | bool removed = transition->despecifyFunction(replaceFunction); | |
509 | ASSERT_UNUSED(removed, removed); | |
510 | ||
511 | return transition.release(); | |
512 | } | |
513 | ||
9dae56ea A |
514 | PassRefPtr<Structure> Structure::getterSetterTransition(Structure* structure) |
515 | { | |
516 | RefPtr<Structure> transition = create(structure->storedPrototype(), structure->typeInfo()); | |
517 | transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity; | |
518 | transition->m_hasGetterSetterProperties = transition->m_hasGetterSetterProperties; | |
519 | ||
520 | // Don't set m_offset, as one can not transition to this. | |
521 | ||
522 | structure->materializePropertyMapIfNecessary(); | |
523 | transition->m_propertyTable = structure->copyPropertyTable(); | |
524 | transition->m_isPinnedPropertyTable = true; | |
525 | ||
526 | return transition.release(); | |
527 | } | |
528 | ||
ba379fdc | 529 | PassRefPtr<Structure> Structure::toDictionaryTransition(Structure* structure, DictionaryKind kind) |
9dae56ea | 530 | { |
ba379fdc A |
531 | ASSERT(!structure->isUncacheableDictionary()); |
532 | ||
9dae56ea | 533 | RefPtr<Structure> transition = create(structure->m_prototype, structure->typeInfo()); |
ba379fdc | 534 | transition->m_dictionaryKind = kind; |
9dae56ea A |
535 | transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity; |
536 | transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties; | |
ba379fdc | 537 | |
9dae56ea A |
538 | structure->materializePropertyMapIfNecessary(); |
539 | transition->m_propertyTable = structure->copyPropertyTable(); | |
540 | transition->m_isPinnedPropertyTable = true; | |
ba379fdc | 541 | |
9dae56ea A |
542 | return transition.release(); |
543 | } | |
544 | ||
ba379fdc | 545 | PassRefPtr<Structure> Structure::toCacheableDictionaryTransition(Structure* structure) |
9dae56ea | 546 | { |
ba379fdc A |
547 | return toDictionaryTransition(structure, CachedDictionaryKind); |
548 | } | |
9dae56ea | 549 | |
ba379fdc A |
550 | PassRefPtr<Structure> Structure::toUncacheableDictionaryTransition(Structure* structure) |
551 | { | |
552 | return toDictionaryTransition(structure, UncachedDictionaryKind); | |
553 | } | |
9dae56ea | 554 | |
ba379fdc A |
555 | PassRefPtr<Structure> Structure::flattenDictionaryStructure(JSObject* object) |
556 | { | |
557 | ASSERT(isDictionary()); | |
558 | if (isUncacheableDictionary()) { | |
559 | ASSERT(m_propertyTable); | |
560 | Vector<PropertyMapEntry*> sortedPropertyEntries(m_propertyTable->keyCount); | |
561 | PropertyMapEntry** p = sortedPropertyEntries.data(); | |
562 | unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; | |
563 | for (unsigned i = 1; i <= entryCount; i++) { | |
564 | if (m_propertyTable->entries()[i].key) | |
565 | *p++ = &m_propertyTable->entries()[i]; | |
566 | } | |
567 | size_t propertyCount = p - sortedPropertyEntries.data(); | |
568 | qsort(sortedPropertyEntries.data(), propertyCount, sizeof(PropertyMapEntry*), comparePropertyMapEntryIndices); | |
569 | sortedPropertyEntries.resize(propertyCount); | |
570 | ||
571 | // We now have the properties currently defined on this object | |
572 | // in the order that they are expected to be in, but we need to | |
573 | // reorder the storage, so we have to copy the current values out | |
574 | Vector<JSValue> values(propertyCount); | |
575 | // FIXME: Remove this workaround with the next merge. | |
576 | unsigned anonymousSlotCount = 0; | |
577 | for (unsigned i = 0; i < propertyCount; i++) { | |
578 | PropertyMapEntry* entry = sortedPropertyEntries[i]; | |
579 | values[i] = object->getDirectOffset(entry->offset); | |
580 | // Update property table to have the new property offsets | |
581 | entry->offset = anonymousSlotCount + i; | |
582 | entry->index = i; | |
583 | } | |
584 | ||
585 | // Copy the original property values into their final locations | |
586 | for (unsigned i = 0; i < propertyCount; i++) | |
587 | object->putDirectOffset(anonymousSlotCount + i, values[i]); | |
588 | ||
589 | if (m_propertyTable->deletedOffsets) { | |
590 | delete m_propertyTable->deletedOffsets; | |
591 | m_propertyTable->deletedOffsets = 0; | |
592 | } | |
593 | } | |
9dae56ea | 594 | |
ba379fdc A |
595 | m_dictionaryKind = NoneDictionaryKind; |
596 | return this; | |
9dae56ea A |
597 | } |
598 | ||
ba379fdc | 599 | size_t Structure::addPropertyWithoutTransition(const Identifier& propertyName, unsigned attributes, JSCell* specificValue) |
9dae56ea A |
600 | { |
601 | ASSERT(!m_transitions.singleTransition); | |
602 | ||
603 | materializePropertyMapIfNecessary(); | |
604 | ||
605 | m_isPinnedPropertyTable = true; | |
ba379fdc | 606 | size_t offset = put(propertyName, attributes, specificValue); |
9dae56ea A |
607 | if (propertyStorageSize() > propertyStorageCapacity()) |
608 | growPropertyStorageCapacity(); | |
609 | clearEnumerationCache(); | |
610 | return offset; | |
611 | } | |
612 | ||
613 | size_t Structure::removePropertyWithoutTransition(const Identifier& propertyName) | |
614 | { | |
ba379fdc | 615 | ASSERT(isUncacheableDictionary()); |
9dae56ea A |
616 | |
617 | materializePropertyMapIfNecessary(); | |
618 | ||
619 | m_isPinnedPropertyTable = true; | |
620 | size_t offset = remove(propertyName); | |
621 | clearEnumerationCache(); | |
622 | return offset; | |
623 | } | |
624 | ||
625 | #if DUMP_PROPERTYMAP_STATS | |
626 | ||
627 | static int numProbes; | |
628 | static int numCollisions; | |
629 | static int numRehashes; | |
630 | static int numRemoves; | |
631 | ||
632 | struct PropertyMapStatisticsExitLogger { | |
633 | ~PropertyMapStatisticsExitLogger(); | |
634 | }; | |
635 | ||
636 | static PropertyMapStatisticsExitLogger logger; | |
637 | ||
638 | PropertyMapStatisticsExitLogger::~PropertyMapStatisticsExitLogger() | |
639 | { | |
640 | printf("\nJSC::PropertyMap statistics\n\n"); | |
641 | printf("%d probes\n", numProbes); | |
642 | printf("%d collisions (%.1f%%)\n", numCollisions, 100.0 * numCollisions / numProbes); | |
643 | printf("%d rehashes\n", numRehashes); | |
644 | printf("%d removes\n", numRemoves); | |
645 | } | |
646 | ||
647 | #endif | |
648 | ||
649 | static const unsigned deletedSentinelIndex = 1; | |
650 | ||
651 | #if !DO_PROPERTYMAP_CONSTENCY_CHECK | |
652 | ||
653 | inline void Structure::checkConsistency() | |
654 | { | |
655 | } | |
656 | ||
657 | #endif | |
658 | ||
659 | PropertyMapHashTable* Structure::copyPropertyTable() | |
660 | { | |
661 | if (!m_propertyTable) | |
662 | return 0; | |
663 | ||
664 | size_t tableSize = PropertyMapHashTable::allocationSize(m_propertyTable->size); | |
665 | PropertyMapHashTable* newTable = static_cast<PropertyMapHashTable*>(fastMalloc(tableSize)); | |
666 | memcpy(newTable, m_propertyTable, tableSize); | |
667 | ||
668 | unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; | |
669 | for (unsigned i = 1; i <= entryCount; ++i) { | |
670 | if (UString::Rep* key = newTable->entries()[i].key) | |
671 | key->ref(); | |
672 | } | |
673 | ||
674 | // Copy the deletedOffsets vector. | |
675 | if (m_propertyTable->deletedOffsets) | |
676 | newTable->deletedOffsets = new Vector<unsigned>(*m_propertyTable->deletedOffsets); | |
677 | ||
678 | return newTable; | |
679 | } | |
680 | ||
ba379fdc | 681 | size_t Structure::get(const UString::Rep* rep, unsigned& attributes, JSCell*& specificValue) |
9dae56ea | 682 | { |
9dae56ea A |
683 | materializePropertyMapIfNecessary(); |
684 | if (!m_propertyTable) | |
685 | return notFound; | |
686 | ||
9dae56ea A |
687 | unsigned i = rep->computedHash(); |
688 | ||
689 | #if DUMP_PROPERTYMAP_STATS | |
690 | ++numProbes; | |
691 | #endif | |
692 | ||
693 | unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; | |
694 | if (entryIndex == emptyEntryIndex) | |
695 | return notFound; | |
696 | ||
697 | if (rep == m_propertyTable->entries()[entryIndex - 1].key) { | |
698 | attributes = m_propertyTable->entries()[entryIndex - 1].attributes; | |
ba379fdc | 699 | specificValue = m_propertyTable->entries()[entryIndex - 1].specificValue; |
9dae56ea A |
700 | return m_propertyTable->entries()[entryIndex - 1].offset; |
701 | } | |
702 | ||
703 | #if DUMP_PROPERTYMAP_STATS | |
704 | ++numCollisions; | |
705 | #endif | |
706 | ||
707 | unsigned k = 1 | doubleHash(rep->computedHash()); | |
708 | ||
709 | while (1) { | |
710 | i += k; | |
711 | ||
712 | #if DUMP_PROPERTYMAP_STATS | |
713 | ++numRehashes; | |
714 | #endif | |
715 | ||
716 | entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; | |
717 | if (entryIndex == emptyEntryIndex) | |
718 | return notFound; | |
719 | ||
720 | if (rep == m_propertyTable->entries()[entryIndex - 1].key) { | |
721 | attributes = m_propertyTable->entries()[entryIndex - 1].attributes; | |
ba379fdc | 722 | specificValue = m_propertyTable->entries()[entryIndex - 1].specificValue; |
9dae56ea A |
723 | return m_propertyTable->entries()[entryIndex - 1].offset; |
724 | } | |
725 | } | |
726 | } | |
727 | ||
ba379fdc A |
728 | bool Structure::despecifyFunction(const Identifier& propertyName) |
729 | { | |
730 | ASSERT(!propertyName.isNull()); | |
731 | ||
732 | materializePropertyMapIfNecessary(); | |
733 | if (!m_propertyTable) | |
734 | return false; | |
735 | ||
736 | UString::Rep* rep = propertyName._ustring.rep(); | |
737 | ||
738 | unsigned i = rep->computedHash(); | |
739 | ||
740 | #if DUMP_PROPERTYMAP_STATS | |
741 | ++numProbes; | |
742 | #endif | |
743 | ||
744 | unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; | |
745 | if (entryIndex == emptyEntryIndex) | |
746 | return false; | |
747 | ||
748 | if (rep == m_propertyTable->entries()[entryIndex - 1].key) { | |
749 | ASSERT(m_propertyTable->entries()[entryIndex - 1].specificValue); | |
750 | m_propertyTable->entries()[entryIndex - 1].specificValue = 0; | |
751 | return true; | |
752 | } | |
753 | ||
754 | #if DUMP_PROPERTYMAP_STATS | |
755 | ++numCollisions; | |
756 | #endif | |
757 | ||
758 | unsigned k = 1 | doubleHash(rep->computedHash()); | |
759 | ||
760 | while (1) { | |
761 | i += k; | |
762 | ||
763 | #if DUMP_PROPERTYMAP_STATS | |
764 | ++numRehashes; | |
765 | #endif | |
766 | ||
767 | entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; | |
768 | if (entryIndex == emptyEntryIndex) | |
769 | return false; | |
770 | ||
771 | if (rep == m_propertyTable->entries()[entryIndex - 1].key) { | |
772 | ASSERT(m_propertyTable->entries()[entryIndex - 1].specificValue); | |
773 | m_propertyTable->entries()[entryIndex - 1].specificValue = 0; | |
774 | return true; | |
775 | } | |
776 | } | |
777 | } | |
778 | ||
779 | size_t Structure::put(const Identifier& propertyName, unsigned attributes, JSCell* specificValue) | |
9dae56ea A |
780 | { |
781 | ASSERT(!propertyName.isNull()); | |
782 | ASSERT(get(propertyName) == notFound); | |
783 | ||
784 | checkConsistency(); | |
785 | ||
786 | UString::Rep* rep = propertyName._ustring.rep(); | |
787 | ||
788 | if (!m_propertyTable) | |
789 | createPropertyMapHashTable(); | |
790 | ||
791 | // FIXME: Consider a fast case for tables with no deleted sentinels. | |
792 | ||
793 | unsigned i = rep->computedHash(); | |
794 | unsigned k = 0; | |
795 | bool foundDeletedElement = false; | |
796 | unsigned deletedElementIndex = 0; // initialize to make the compiler happy | |
797 | ||
798 | #if DUMP_PROPERTYMAP_STATS | |
799 | ++numProbes; | |
800 | #endif | |
801 | ||
802 | while (1) { | |
803 | unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; | |
804 | if (entryIndex == emptyEntryIndex) | |
805 | break; | |
806 | ||
807 | if (entryIndex == deletedSentinelIndex) { | |
808 | // If we find a deleted-element sentinel, remember it for use later. | |
809 | if (!foundDeletedElement) { | |
810 | foundDeletedElement = true; | |
811 | deletedElementIndex = i; | |
812 | } | |
813 | } | |
814 | ||
815 | if (k == 0) { | |
816 | k = 1 | doubleHash(rep->computedHash()); | |
817 | #if DUMP_PROPERTYMAP_STATS | |
818 | ++numCollisions; | |
819 | #endif | |
820 | } | |
821 | ||
822 | i += k; | |
823 | ||
824 | #if DUMP_PROPERTYMAP_STATS | |
825 | ++numRehashes; | |
826 | #endif | |
827 | } | |
828 | ||
829 | // Figure out which entry to use. | |
830 | unsigned entryIndex = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount + 2; | |
831 | if (foundDeletedElement) { | |
832 | i = deletedElementIndex; | |
833 | --m_propertyTable->deletedSentinelCount; | |
834 | ||
835 | // Since we're not making the table bigger, we can't use the entry one past | |
836 | // the end that we were planning on using, so search backwards for the empty | |
837 | // slot that we can use. We know it will be there because we did at least one | |
838 | // deletion in the past that left an entry empty. | |
839 | while (m_propertyTable->entries()[--entryIndex - 1].key) { } | |
840 | } | |
841 | ||
842 | // Create a new hash table entry. | |
843 | m_propertyTable->entryIndices[i & m_propertyTable->sizeMask] = entryIndex; | |
844 | ||
845 | // Create a new hash table entry. | |
846 | rep->ref(); | |
847 | m_propertyTable->entries()[entryIndex - 1].key = rep; | |
848 | m_propertyTable->entries()[entryIndex - 1].attributes = attributes; | |
ba379fdc | 849 | m_propertyTable->entries()[entryIndex - 1].specificValue = specificValue; |
9dae56ea A |
850 | m_propertyTable->entries()[entryIndex - 1].index = ++m_propertyTable->lastIndexUsed; |
851 | ||
852 | unsigned newOffset; | |
853 | if (m_propertyTable->deletedOffsets && !m_propertyTable->deletedOffsets->isEmpty()) { | |
854 | newOffset = m_propertyTable->deletedOffsets->last(); | |
855 | m_propertyTable->deletedOffsets->removeLast(); | |
856 | } else | |
857 | newOffset = m_propertyTable->keyCount; | |
858 | m_propertyTable->entries()[entryIndex - 1].offset = newOffset; | |
859 | ||
860 | ++m_propertyTable->keyCount; | |
861 | ||
862 | if ((m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount) * 2 >= m_propertyTable->size) | |
863 | expandPropertyMapHashTable(); | |
864 | ||
865 | checkConsistency(); | |
866 | return newOffset; | |
867 | } | |
868 | ||
ba379fdc A |
869 | bool Structure::hasTransition(UString::Rep* rep, unsigned attributes) |
870 | { | |
871 | if (m_usingSingleTransitionSlot) { | |
872 | return m_transitions.singleTransition | |
873 | && m_transitions.singleTransition->m_nameInPrevious == rep | |
874 | && m_transitions.singleTransition->m_attributesInPrevious == attributes; | |
875 | } | |
876 | return m_transitions.table->hasTransition(make_pair(rep, attributes)); | |
877 | } | |
878 | ||
9dae56ea A |
879 | size_t Structure::remove(const Identifier& propertyName) |
880 | { | |
881 | ASSERT(!propertyName.isNull()); | |
882 | ||
883 | checkConsistency(); | |
884 | ||
885 | UString::Rep* rep = propertyName._ustring.rep(); | |
886 | ||
887 | if (!m_propertyTable) | |
888 | return notFound; | |
889 | ||
890 | #if DUMP_PROPERTYMAP_STATS | |
891 | ++numProbes; | |
892 | ++numRemoves; | |
893 | #endif | |
894 | ||
895 | // Find the thing to remove. | |
896 | unsigned i = rep->computedHash(); | |
897 | unsigned k = 0; | |
898 | unsigned entryIndex; | |
899 | UString::Rep* key = 0; | |
900 | while (1) { | |
901 | entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; | |
902 | if (entryIndex == emptyEntryIndex) | |
903 | return notFound; | |
904 | ||
905 | key = m_propertyTable->entries()[entryIndex - 1].key; | |
906 | if (rep == key) | |
907 | break; | |
908 | ||
909 | if (k == 0) { | |
910 | k = 1 | doubleHash(rep->computedHash()); | |
911 | #if DUMP_PROPERTYMAP_STATS | |
912 | ++numCollisions; | |
913 | #endif | |
914 | } | |
915 | ||
916 | i += k; | |
917 | ||
918 | #if DUMP_PROPERTYMAP_STATS | |
919 | ++numRehashes; | |
920 | #endif | |
921 | } | |
922 | ||
923 | // Replace this one element with the deleted sentinel. Also clear out | |
924 | // the entry so we can iterate all the entries as needed. | |
925 | m_propertyTable->entryIndices[i & m_propertyTable->sizeMask] = deletedSentinelIndex; | |
926 | ||
927 | size_t offset = m_propertyTable->entries()[entryIndex - 1].offset; | |
928 | ||
929 | key->deref(); | |
930 | m_propertyTable->entries()[entryIndex - 1].key = 0; | |
931 | m_propertyTable->entries()[entryIndex - 1].attributes = 0; | |
ba379fdc | 932 | m_propertyTable->entries()[entryIndex - 1].specificValue = 0; |
9dae56ea A |
933 | m_propertyTable->entries()[entryIndex - 1].offset = 0; |
934 | ||
935 | if (!m_propertyTable->deletedOffsets) | |
936 | m_propertyTable->deletedOffsets = new Vector<unsigned>; | |
937 | m_propertyTable->deletedOffsets->append(offset); | |
938 | ||
939 | ASSERT(m_propertyTable->keyCount >= 1); | |
940 | --m_propertyTable->keyCount; | |
941 | ++m_propertyTable->deletedSentinelCount; | |
942 | ||
943 | if (m_propertyTable->deletedSentinelCount * 4 >= m_propertyTable->size) | |
944 | rehashPropertyMapHashTable(); | |
945 | ||
946 | checkConsistency(); | |
947 | return offset; | |
948 | } | |
949 | ||
950 | void Structure::insertIntoPropertyMapHashTable(const PropertyMapEntry& entry) | |
951 | { | |
952 | ASSERT(m_propertyTable); | |
953 | ||
954 | unsigned i = entry.key->computedHash(); | |
955 | unsigned k = 0; | |
956 | ||
957 | #if DUMP_PROPERTYMAP_STATS | |
958 | ++numProbes; | |
959 | #endif | |
960 | ||
961 | while (1) { | |
962 | unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; | |
963 | if (entryIndex == emptyEntryIndex) | |
964 | break; | |
965 | ||
966 | if (k == 0) { | |
967 | k = 1 | doubleHash(entry.key->computedHash()); | |
968 | #if DUMP_PROPERTYMAP_STATS | |
969 | ++numCollisions; | |
970 | #endif | |
971 | } | |
972 | ||
973 | i += k; | |
974 | ||
975 | #if DUMP_PROPERTYMAP_STATS | |
976 | ++numRehashes; | |
977 | #endif | |
978 | } | |
979 | ||
980 | unsigned entryIndex = m_propertyTable->keyCount + 2; | |
981 | m_propertyTable->entryIndices[i & m_propertyTable->sizeMask] = entryIndex; | |
982 | m_propertyTable->entries()[entryIndex - 1] = entry; | |
983 | ||
984 | ++m_propertyTable->keyCount; | |
985 | } | |
986 | ||
987 | void Structure::createPropertyMapHashTable() | |
988 | { | |
989 | ASSERT(sizeForKeyCount(7) == newTableSize); | |
990 | createPropertyMapHashTable(newTableSize); | |
991 | } | |
992 | ||
993 | void Structure::createPropertyMapHashTable(unsigned newTableSize) | |
994 | { | |
995 | ASSERT(!m_propertyTable); | |
996 | ASSERT(isPowerOf2(newTableSize)); | |
997 | ||
998 | checkConsistency(); | |
999 | ||
1000 | m_propertyTable = static_cast<PropertyMapHashTable*>(fastZeroedMalloc(PropertyMapHashTable::allocationSize(newTableSize))); | |
1001 | m_propertyTable->size = newTableSize; | |
1002 | m_propertyTable->sizeMask = newTableSize - 1; | |
1003 | ||
1004 | checkConsistency(); | |
1005 | } | |
1006 | ||
1007 | void Structure::expandPropertyMapHashTable() | |
1008 | { | |
1009 | ASSERT(m_propertyTable); | |
1010 | rehashPropertyMapHashTable(m_propertyTable->size * 2); | |
1011 | } | |
1012 | ||
1013 | void Structure::rehashPropertyMapHashTable() | |
1014 | { | |
1015 | ASSERT(m_propertyTable); | |
1016 | ASSERT(m_propertyTable->size); | |
1017 | rehashPropertyMapHashTable(m_propertyTable->size); | |
1018 | } | |
1019 | ||
1020 | void Structure::rehashPropertyMapHashTable(unsigned newTableSize) | |
1021 | { | |
1022 | ASSERT(m_propertyTable); | |
1023 | ASSERT(isPowerOf2(newTableSize)); | |
1024 | ||
1025 | checkConsistency(); | |
1026 | ||
1027 | PropertyMapHashTable* oldTable = m_propertyTable; | |
1028 | ||
1029 | m_propertyTable = static_cast<PropertyMapHashTable*>(fastZeroedMalloc(PropertyMapHashTable::allocationSize(newTableSize))); | |
1030 | m_propertyTable->size = newTableSize; | |
1031 | m_propertyTable->sizeMask = newTableSize - 1; | |
1032 | ||
1033 | unsigned lastIndexUsed = 0; | |
1034 | unsigned entryCount = oldTable->keyCount + oldTable->deletedSentinelCount; | |
1035 | for (unsigned i = 1; i <= entryCount; ++i) { | |
1036 | if (oldTable->entries()[i].key) { | |
1037 | lastIndexUsed = max(oldTable->entries()[i].index, lastIndexUsed); | |
1038 | insertIntoPropertyMapHashTable(oldTable->entries()[i]); | |
1039 | } | |
1040 | } | |
1041 | m_propertyTable->lastIndexUsed = lastIndexUsed; | |
1042 | m_propertyTable->deletedOffsets = oldTable->deletedOffsets; | |
1043 | ||
1044 | fastFree(oldTable); | |
1045 | ||
1046 | checkConsistency(); | |
1047 | } | |
1048 | ||
ba379fdc | 1049 | int comparePropertyMapEntryIndices(const void* a, const void* b) |
9dae56ea A |
1050 | { |
1051 | unsigned ia = static_cast<PropertyMapEntry* const*>(a)[0]->index; | |
1052 | unsigned ib = static_cast<PropertyMapEntry* const*>(b)[0]->index; | |
1053 | if (ia < ib) | |
1054 | return -1; | |
1055 | if (ia > ib) | |
1056 | return +1; | |
1057 | return 0; | |
1058 | } | |
1059 | ||
1060 | void Structure::getEnumerableNamesFromPropertyTable(PropertyNameArray& propertyNames) | |
1061 | { | |
1062 | materializePropertyMapIfNecessary(); | |
1063 | if (!m_propertyTable) | |
1064 | return; | |
1065 | ||
1066 | if (m_propertyTable->keyCount < tinyMapThreshold) { | |
1067 | PropertyMapEntry* a[tinyMapThreshold]; | |
1068 | int i = 0; | |
1069 | unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; | |
1070 | for (unsigned k = 1; k <= entryCount; k++) { | |
1071 | if (m_propertyTable->entries()[k].key && !(m_propertyTable->entries()[k].attributes & DontEnum)) { | |
1072 | PropertyMapEntry* value = &m_propertyTable->entries()[k]; | |
1073 | int j; | |
1074 | for (j = i - 1; j >= 0 && a[j]->index > value->index; --j) | |
1075 | a[j + 1] = a[j]; | |
1076 | a[j + 1] = value; | |
1077 | ++i; | |
1078 | } | |
1079 | } | |
1080 | if (!propertyNames.size()) { | |
1081 | for (int k = 0; k < i; ++k) | |
1082 | propertyNames.addKnownUnique(a[k]->key); | |
1083 | } else { | |
1084 | for (int k = 0; k < i; ++k) | |
1085 | propertyNames.add(a[k]->key); | |
1086 | } | |
1087 | ||
1088 | return; | |
1089 | } | |
1090 | ||
1091 | // Allocate a buffer to use to sort the keys. | |
1092 | Vector<PropertyMapEntry*, smallMapThreshold> sortedEnumerables(m_propertyTable->keyCount); | |
1093 | ||
1094 | // Get pointers to the enumerable entries in the buffer. | |
1095 | PropertyMapEntry** p = sortedEnumerables.data(); | |
1096 | unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; | |
1097 | for (unsigned i = 1; i <= entryCount; i++) { | |
1098 | if (m_propertyTable->entries()[i].key && !(m_propertyTable->entries()[i].attributes & DontEnum)) | |
1099 | *p++ = &m_propertyTable->entries()[i]; | |
1100 | } | |
1101 | ||
1102 | size_t enumerableCount = p - sortedEnumerables.data(); | |
1103 | // Sort the entries by index. | |
1104 | qsort(sortedEnumerables.data(), enumerableCount, sizeof(PropertyMapEntry*), comparePropertyMapEntryIndices); | |
1105 | sortedEnumerables.resize(enumerableCount); | |
1106 | ||
1107 | // Put the keys of the sorted entries into the list. | |
1108 | if (!propertyNames.size()) { | |
1109 | for (size_t i = 0; i < sortedEnumerables.size(); ++i) | |
1110 | propertyNames.addKnownUnique(sortedEnumerables[i]->key); | |
1111 | } else { | |
1112 | for (size_t i = 0; i < sortedEnumerables.size(); ++i) | |
1113 | propertyNames.add(sortedEnumerables[i]->key); | |
1114 | } | |
1115 | } | |
1116 | ||
1117 | void Structure::getEnumerableNamesFromClassInfoTable(ExecState* exec, const ClassInfo* classInfo, PropertyNameArray& propertyNames) | |
1118 | { | |
1119 | // Add properties from the static hashtables of properties | |
1120 | for (; classInfo; classInfo = classInfo->parentClass) { | |
1121 | const HashTable* table = classInfo->propHashTable(exec); | |
1122 | if (!table) | |
1123 | continue; | |
1124 | table->initializeIfNeeded(exec); | |
1125 | ASSERT(table->table); | |
ba379fdc | 1126 | |
9dae56ea | 1127 | int hashSizeMask = table->compactSize - 1; |
9dae56ea A |
1128 | const HashEntry* entry = table->table; |
1129 | for (int i = 0; i <= hashSizeMask; ++i, ++entry) { | |
1130 | if (entry->key() && !(entry->attributes() & DontEnum)) | |
1131 | propertyNames.add(entry->key()); | |
1132 | } | |
1133 | } | |
1134 | } | |
1135 | ||
1136 | #if DO_PROPERTYMAP_CONSTENCY_CHECK | |
1137 | ||
1138 | void Structure::checkConsistency() | |
1139 | { | |
1140 | if (!m_propertyTable) | |
1141 | return; | |
1142 | ||
1143 | ASSERT(m_propertyTable->size >= newTableSize); | |
1144 | ASSERT(m_propertyTable->sizeMask); | |
1145 | ASSERT(m_propertyTable->size == m_propertyTable->sizeMask + 1); | |
1146 | ASSERT(!(m_propertyTable->size & m_propertyTable->sizeMask)); | |
1147 | ||
1148 | ASSERT(m_propertyTable->keyCount <= m_propertyTable->size / 2); | |
1149 | ASSERT(m_propertyTable->deletedSentinelCount <= m_propertyTable->size / 4); | |
1150 | ||
1151 | ASSERT(m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount <= m_propertyTable->size / 2); | |
1152 | ||
1153 | unsigned indexCount = 0; | |
1154 | unsigned deletedIndexCount = 0; | |
1155 | for (unsigned a = 0; a != m_propertyTable->size; ++a) { | |
1156 | unsigned entryIndex = m_propertyTable->entryIndices[a]; | |
1157 | if (entryIndex == emptyEntryIndex) | |
1158 | continue; | |
1159 | if (entryIndex == deletedSentinelIndex) { | |
1160 | ++deletedIndexCount; | |
1161 | continue; | |
1162 | } | |
1163 | ASSERT(entryIndex > deletedSentinelIndex); | |
1164 | ASSERT(entryIndex - 1 <= m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount); | |
1165 | ++indexCount; | |
1166 | ||
1167 | for (unsigned b = a + 1; b != m_propertyTable->size; ++b) | |
1168 | ASSERT(m_propertyTable->entryIndices[b] != entryIndex); | |
1169 | } | |
1170 | ASSERT(indexCount == m_propertyTable->keyCount); | |
1171 | ASSERT(deletedIndexCount == m_propertyTable->deletedSentinelCount); | |
1172 | ||
1173 | ASSERT(m_propertyTable->entries()[0].key == 0); | |
1174 | ||
1175 | unsigned nonEmptyEntryCount = 0; | |
1176 | for (unsigned c = 1; c <= m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; ++c) { | |
1177 | UString::Rep* rep = m_propertyTable->entries()[c].key; | |
1178 | if (!rep) | |
1179 | continue; | |
1180 | ++nonEmptyEntryCount; | |
1181 | unsigned i = rep->computedHash(); | |
1182 | unsigned k = 0; | |
1183 | unsigned entryIndex; | |
1184 | while (1) { | |
1185 | entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; | |
1186 | ASSERT(entryIndex != emptyEntryIndex); | |
1187 | if (rep == m_propertyTable->entries()[entryIndex - 1].key) | |
1188 | break; | |
1189 | if (k == 0) | |
1190 | k = 1 | doubleHash(rep->computedHash()); | |
1191 | i += k; | |
1192 | } | |
1193 | ASSERT(entryIndex == c + 1); | |
1194 | } | |
1195 | ||
1196 | ASSERT(nonEmptyEntryCount == m_propertyTable->keyCount); | |
1197 | } | |
1198 | ||
1199 | #endif // DO_PROPERTYMAP_CONSTENCY_CHECK | |
1200 | ||
1201 | } // namespace JSC |