source: webkit/trunk/JavaScriptCore/runtime/Structure.cpp@ 39593

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

2009-01-04 Alice Liu <[email protected]>

<rdar://problem/6341776> Merge m_transitionCount and m_offset in Structure.

Reviewed by Darin Adler.

  • runtime/Structure.cpp: (JSC::Structure::Structure): Remove m_transitionCount (JSC::Structure::addPropertyTransitionToExistingStructure): No need to wait until after the assignment to offset to assert if it's notFound; move it up. (JSC::Structure::addPropertyTransition): Use method for transitionCount instead of m_transitionCount. Remove line that maintains the m_transitionCount. (JSC::Structure::changePrototypeTransition): Remove line that maintains the m_transitionCount. (JSC::Structure::getterSetterTransition): Remove line that maintains the m_transitionCount.
  • runtime/Structure.h: Changed s_maxTransitionLength and m_offset from size_t to signed char. m_offset will never become greater than 64 because the structure transitions to a dictionary at that time. (JSC::Structure::transitionCount): method to replace the data member
File size: 33.6 KB
Line 
1/*
2 * Copyright (C) 2008 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "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
49using namespace std;
50using namespace WTF;
51
52namespace 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.
56static 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.
60static const unsigned tinyMapThreshold = 20;
61
62static const unsigned newTableSize = 16;
63
64#ifndef NDEBUG
65static WTF::RefCountedLeakCounter structureCounter("Structure");
66
67#if ENABLE(JSC_MULTIPLE_THREADS)
68static Mutex ignoreSetMutex;
69#endif
70
71static bool shouldIgnoreLeaks;
72static HashSet<Structure*> ignoreSet;
73#endif
74
75#if DUMP_STRUCTURE_ID_STATISTICS
76static HashSet<Structure*> liveStructureSet;
77#endif
78
79void Structure::dumpStatistics()
80{
81#if DUMP_STRUCTURE_ID_STATISTICS
82 unsigned numberLeaf = 0;
83 unsigned numberUsingSingleSlot = 0;
84 unsigned numberSingletons = 0;
85 unsigned numberWithPropertyMaps = 0;
86 unsigned totalPropertyMapsSize = 0;
87
88 HashSet<Structure*>::const_iterator end = liveStructureSet.end();
89 for (HashSet<Structure*>::const_iterator it = liveStructureSet.begin(); it != end; ++it) {
90 Structure* structure = *it;
91 if (structure->m_usingSingleTransitionSlot) {
92 if (!structure->m_transitions.singleTransition)
93 ++numberLeaf;
94 else
95 ++numberUsingSingleSlot;
96
97 if (!structure->m_previous && !structure->m_transitions.singleTransition)
98 ++numberSingletons;
99 }
100
101 if (structure->m_propertyTable) {
102 ++numberWithPropertyMaps;
103 totalPropertyMapsSize += PropertyMapHashTable::allocationSize(structure->m_propertyTable->size);
104 if (structure->m_propertyTable->deletedOffsets)
105 totalPropertyMapsSize += (structure->m_propertyTable->deletedOffsets->capacity() * sizeof(unsigned));
106 }
107 }
108
109 printf("Number of live Structures: %d\n", liveStructureSet.size());
110 printf("Number of Structures using the single item optimization for transition map: %d\n", numberUsingSingleSlot);
111 printf("Number of Structures that are leaf nodes: %d\n", numberLeaf);
112 printf("Number of Structures that singletons: %d\n", numberSingletons);
113 printf("Number of Structures with PropertyMaps: %d\n", numberWithPropertyMaps);
114
115 printf("Size of a single Structures: %d\n", static_cast<unsigned>(sizeof(Structure)));
116 printf("Size of sum of all property maps: %d\n", totalPropertyMapsSize);
117 printf("Size of average of all property maps: %f\n", static_cast<double>(totalPropertyMapsSize) / static_cast<double>(liveStructureSet.size()));
118#else
119 printf("Dumping Structure statistics is not enabled.\n");
120#endif
121}
122
123Structure::Structure(JSValue* prototype, const TypeInfo& typeInfo)
124 : m_typeInfo(typeInfo)
125 , m_prototype(prototype)
126 , m_cachedPrototypeChain(0)
127 , m_previous(0)
128 , m_nameInPrevious(0)
129 , m_propertyTable(0)
130 , m_propertyStorageCapacity(JSObject::inlineStorageCapacity)
131 , m_offset(noOffset)
132 , m_isDictionary(false)
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
158Structure::~Structure()
159{
160 if (m_previous) {
161 if (m_previous->m_usingSingleTransitionSlot) {
162 m_previous->m_transitions.singleTransition = 0;
163 } else {
164 ASSERT(m_previous->m_transitions.table->contains(make_pair(m_nameInPrevious.get(), m_attributesInPrevious)));
165 m_previous->m_transitions.table->remove(make_pair(m_nameInPrevious.get(), m_attributesInPrevious));
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 fastFree(m_propertyTable);
182 }
183
184#ifndef NDEBUG
185#if ENABLE(JSC_MULTIPLE_THREADS)
186 MutexLocker protect(ignoreSetMutex);
187#endif
188 HashSet<Structure*>::iterator it = ignoreSet.find(this);
189 if (it != ignoreSet.end())
190 ignoreSet.remove(it);
191 else
192 structureCounter.decrement();
193#endif
194
195#if DUMP_STRUCTURE_ID_STATISTICS
196 liveStructureSet.remove(this);
197#endif
198}
199
200void Structure::startIgnoringLeaks()
201{
202#ifndef NDEBUG
203 shouldIgnoreLeaks = true;
204#endif
205}
206
207void Structure::stopIgnoringLeaks()
208{
209#ifndef NDEBUG
210 shouldIgnoreLeaks = false;
211#endif
212}
213
214static bool isPowerOf2(unsigned v)
215{
216 // Taken from http://www.cs.utk.edu/~vose/c-stuff/bithacks.html
217
218 return !(v & (v - 1)) && v;
219}
220
221static unsigned nextPowerOf2(unsigned v)
222{
223 // Taken from http://www.cs.utk.edu/~vose/c-stuff/bithacks.html
224 // Devised by Sean Anderson, Sepember 14, 2001
225
226 v--;
227 v |= v >> 1;
228 v |= v >> 2;
229 v |= v >> 4;
230 v |= v >> 8;
231 v |= v >> 16;
232 v++;
233
234 return v;
235}
236
237static unsigned sizeForKeyCount(size_t keyCount)
238{
239 if (keyCount == notFound)
240 return newTableSize;
241
242 if (keyCount < 8)
243 return newTableSize;
244
245 if (isPowerOf2(keyCount))
246 return keyCount * 4;
247
248 return nextPowerOf2(keyCount) * 2;
249}
250
251void Structure::materializePropertyMap()
252{
253 ASSERT(!m_propertyTable);
254
255 Vector<Structure*, 8> structures;
256 structures.append(this);
257
258 Structure* structure = this;
259
260 // Search for the last Structure with a property table.
261 while ((structure = structure->previousID())) {
262 if (structure->m_isPinnedPropertyTable) {
263 ASSERT(structure->m_propertyTable);
264 ASSERT(!structure->m_previous);
265
266 m_propertyTable = structure->copyPropertyTable();
267 break;
268 }
269
270 structures.append(structure);
271 }
272
273 if (!m_propertyTable)
274 createPropertyMapHashTable(sizeForKeyCount(m_offset + 1));
275 else {
276 if (sizeForKeyCount(m_offset + 1) > m_propertyTable->size)
277 rehashPropertyMapHashTable(sizeForKeyCount(m_offset + 1)); // This could be made more efficient by combining with the copy above.
278 }
279
280 for (ptrdiff_t i = structures.size() - 2; i >= 0; --i) {
281 structure = structures[i];
282 structure->m_nameInPrevious->ref();
283 PropertyMapEntry entry(structure->m_nameInPrevious.get(), structure->m_offset, structure->m_attributesInPrevious, ++m_propertyTable->lastIndexUsed);
284 insertIntoPropertyMapHashTable(entry);
285 }
286}
287
288void Structure::getEnumerablePropertyNames(ExecState* exec, PropertyNameArray& propertyNames, JSObject* baseObject)
289{
290 bool shouldCache = propertyNames.cacheable() && !(propertyNames.size() || m_isDictionary);
291
292 if (shouldCache) {
293 if (m_cachedPropertyNameArrayData) {
294 if (structureChainsAreEqual(m_cachedPropertyNameArrayData->cachedPrototypeChain(), cachedPrototypeChain())) {
295 propertyNames.setData(m_cachedPropertyNameArrayData);
296 return;
297 }
298 }
299 propertyNames.setCacheable(false);
300 }
301
302 getEnumerablePropertyNamesInternal(propertyNames);
303
304 // Add properties from the static hashtables of properties
305 for (const ClassInfo* info = baseObject->classInfo(); info; info = info->parentClass) {
306 const HashTable* table = info->propHashTable(exec);
307 if (!table)
308 continue;
309 table->initializeIfNeeded(exec);
310 ASSERT(table->table);
311#if ENABLE(PERFECT_HASH_SIZE)
312 int hashSizeMask = table->hashSizeMask;
313#else
314 int hashSizeMask = table->compactSize - 1;
315#endif
316 const HashEntry* entry = table->table;
317 for (int i = 0; i <= hashSizeMask; ++i, ++entry) {
318 if (entry->key() && !(entry->attributes() & DontEnum))
319 propertyNames.add(entry->key());
320 }
321 }
322
323 if (m_prototype->isObject())
324 asObject(m_prototype)->getPropertyNames(exec, propertyNames);
325
326 if (shouldCache) {
327 if (m_cachedPropertyNameArrayData)
328 m_cachedPropertyNameArrayData->setCachedStructure(0);
329
330 m_cachedPropertyNameArrayData = propertyNames.data();
331
332 StructureChain* chain = cachedPrototypeChain();
333 if (!chain)
334 chain = createCachedPrototypeChain();
335 m_cachedPropertyNameArrayData->setCachedPrototypeChain(chain);
336 m_cachedPropertyNameArrayData->setCachedStructure(this);
337 }
338}
339
340void Structure::clearEnumerationCache()
341{
342 if (m_cachedPropertyNameArrayData)
343 m_cachedPropertyNameArrayData->setCachedStructure(0);
344 m_cachedPropertyNameArrayData.clear();
345}
346
347void Structure::growPropertyStorageCapacity()
348{
349 if (m_propertyStorageCapacity == JSObject::inlineStorageCapacity)
350 m_propertyStorageCapacity = JSObject::nonInlineBaseStorageCapacity;
351 else
352 m_propertyStorageCapacity *= 2;
353}
354
355PassRefPtr<Structure> Structure::addPropertyTransitionToExistingStructure(Structure* structure, const Identifier& propertyName, unsigned attributes, size_t& offset)
356{
357 ASSERT(!structure->m_isDictionary);
358 ASSERT(structure->typeInfo().type() == ObjectType);
359
360 if (structure->m_usingSingleTransitionSlot) {
361 Structure* existingTransition = structure->m_transitions.singleTransition;
362 if (existingTransition && existingTransition->m_nameInPrevious.get() == propertyName.ustring().rep() && existingTransition->m_attributesInPrevious == attributes) {
363 ASSERT(structure->m_transitions.singleTransition->m_offset != noOffset);
364 offset = structure->m_transitions.singleTransition->m_offset;
365 return existingTransition;
366 }
367 } else {
368 if (Structure* existingTransition = structure->m_transitions.table->get(make_pair(propertyName.ustring().rep(), attributes))) {
369 ASSERT(existingTransition->m_offset != noOffset);
370 offset = existingTransition->m_offset;
371 return existingTransition;
372 }
373 }
374
375 return 0;
376}
377
378PassRefPtr<Structure> Structure::addPropertyTransition(Structure* structure, const Identifier& propertyName, unsigned attributes, size_t& offset)
379{
380 ASSERT(!structure->m_isDictionary);
381 ASSERT(structure->typeInfo().type() == ObjectType);
382 ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure, propertyName, attributes, offset));
383
384 if (structure->transitionCount() > s_maxTransitionLength) {
385 RefPtr<Structure> transition = toDictionaryTransition(structure);
386 offset = transition->put(propertyName, attributes);
387 if (transition->propertyStorageSize() > transition->propertyStorageCapacity())
388 transition->growPropertyStorageCapacity();
389 return transition.release();
390 }
391
392 RefPtr<Structure> transition = create(structure->m_prototype, structure->typeInfo());
393 transition->m_cachedPrototypeChain = structure->m_cachedPrototypeChain;
394 transition->m_previous = structure;
395 transition->m_nameInPrevious = propertyName.ustring().rep();
396 transition->m_attributesInPrevious = attributes;
397 transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
398 transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties;
399
400 if (structure->m_propertyTable) {
401 if (structure->m_isPinnedPropertyTable)
402 transition->m_propertyTable = structure->copyPropertyTable();
403 else {
404 transition->m_propertyTable = structure->m_propertyTable;
405 structure->m_propertyTable = 0;
406 }
407 } else {
408 if (structure->m_previous)
409 transition->materializePropertyMap();
410 else
411 transition->createPropertyMapHashTable();
412 }
413
414 offset = transition->put(propertyName, attributes);
415 if (transition->propertyStorageSize() > transition->propertyStorageCapacity())
416 transition->growPropertyStorageCapacity();
417
418 transition->m_offset = offset;
419
420 if (structure->m_usingSingleTransitionSlot) {
421 if (!structure->m_transitions.singleTransition) {
422 structure->m_transitions.singleTransition = transition.get();
423 return transition.release();
424 }
425
426 Structure* existingTransition = structure->m_transitions.singleTransition;
427 structure->m_usingSingleTransitionSlot = false;
428 StructureTransitionTable* transitionTable = new StructureTransitionTable;
429 structure->m_transitions.table = transitionTable;
430 transitionTable->add(make_pair(existingTransition->m_nameInPrevious.get(), existingTransition->m_attributesInPrevious), existingTransition);
431 }
432 structure->m_transitions.table->add(make_pair(propertyName.ustring().rep(), attributes), transition.get());
433 return transition.release();
434}
435
436PassRefPtr<Structure> Structure::removePropertyTransition(Structure* structure, const Identifier& propertyName, size_t& offset)
437{
438 ASSERT(!structure->m_isDictionary);
439
440 RefPtr<Structure> transition = toDictionaryTransition(structure);
441
442 offset = transition->remove(propertyName);
443
444 return transition.release();
445}
446
447PassRefPtr<Structure> Structure::changePrototypeTransition(Structure* structure, JSValue* prototype)
448{
449 RefPtr<Structure> transition = create(prototype, structure->typeInfo());
450
451 transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
452 transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties;
453
454 // Don't set m_offset, as one can not transition to this.
455
456 structure->materializePropertyMapIfNecessary();
457 transition->m_propertyTable = structure->copyPropertyTable();
458 transition->m_isPinnedPropertyTable = true;
459
460 return transition.release();
461}
462
463PassRefPtr<Structure> Structure::getterSetterTransition(Structure* structure)
464{
465 RefPtr<Structure> transition = create(structure->storedPrototype(), structure->typeInfo());
466 transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
467 transition->m_hasGetterSetterProperties = transition->m_hasGetterSetterProperties;
468
469 // Don't set m_offset, as one can not transition to this.
470
471 structure->materializePropertyMapIfNecessary();
472 transition->m_propertyTable = structure->copyPropertyTable();
473 transition->m_isPinnedPropertyTable = true;
474
475 return transition.release();
476}
477
478PassRefPtr<Structure> Structure::toDictionaryTransition(Structure* structure)
479{
480 ASSERT(!structure->m_isDictionary);
481
482 RefPtr<Structure> transition = create(structure->m_prototype, structure->typeInfo());
483 transition->m_isDictionary = true;
484 transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
485 transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties;
486
487 structure->materializePropertyMapIfNecessary();
488 transition->m_propertyTable = structure->copyPropertyTable();
489 transition->m_isPinnedPropertyTable = true;
490
491 return transition.release();
492}
493
494PassRefPtr<Structure> Structure::fromDictionaryTransition(Structure* structure)
495{
496 ASSERT(structure->m_isDictionary);
497
498 // Since dictionary Structures are not shared, and no opcodes specialize
499 // for them, we don't need to allocate a new Structure when transitioning
500 // to non-dictionary status.
501
502 // FIMXE: We can make this more efficient by canonicalizing the Structure (draining the
503 // deleted offsets vector) before transitioning from dictionary.
504 if (!structure->m_propertyTable || !structure->m_propertyTable->deletedOffsets || structure->m_propertyTable->deletedOffsets->isEmpty())
505 structure->m_isDictionary = false;
506
507 return structure;
508}
509
510size_t Structure::addPropertyWithoutTransition(const Identifier& propertyName, unsigned attributes)
511{
512 ASSERT(!m_transitions.singleTransition);
513
514 materializePropertyMapIfNecessary();
515
516 m_isPinnedPropertyTable = true;
517 size_t offset = put(propertyName, attributes);
518 if (propertyStorageSize() > propertyStorageCapacity())
519 growPropertyStorageCapacity();
520 clearEnumerationCache();
521 return offset;
522}
523
524size_t Structure::removePropertyWithoutTransition(const Identifier& propertyName)
525{
526 ASSERT(!m_transitions.singleTransition);
527 ASSERT(m_isDictionary);
528
529 materializePropertyMapIfNecessary();
530
531 m_isPinnedPropertyTable = true;
532 size_t offset = remove(propertyName);
533 clearEnumerationCache();
534 return offset;
535}
536
537StructureChain* Structure::createCachedPrototypeChain()
538{
539 ASSERT(typeInfo().type() == ObjectType);
540 ASSERT(!m_cachedPrototypeChain);
541
542 JSValue* prototype = storedPrototype();
543 if (JSImmediate::isImmediate(prototype))
544 return 0;
545
546 RefPtr<StructureChain> chain = StructureChain::create(asObject(prototype)->structure());
547 setCachedPrototypeChain(chain.release());
548 return cachedPrototypeChain();
549}
550
551#if DUMP_PROPERTYMAP_STATS
552
553static int numProbes;
554static int numCollisions;
555static int numRehashes;
556static int numRemoves;
557
558struct PropertyMapStatisticsExitLogger {
559 ~PropertyMapStatisticsExitLogger();
560};
561
562static PropertyMapStatisticsExitLogger logger;
563
564PropertyMapStatisticsExitLogger::~PropertyMapStatisticsExitLogger()
565{
566 printf("\nJSC::PropertyMap statistics\n\n");
567 printf("%d probes\n", numProbes);
568 printf("%d collisions (%.1f%%)\n", numCollisions, 100.0 * numCollisions / numProbes);
569 printf("%d rehashes\n", numRehashes);
570 printf("%d removes\n", numRemoves);
571}
572
573#endif
574
575static const unsigned deletedSentinelIndex = 1;
576
577#if !DO_PROPERTYMAP_CONSTENCY_CHECK
578
579inline void Structure::checkConsistency()
580{
581}
582
583#endif
584
585PropertyMapHashTable* Structure::copyPropertyTable()
586{
587 if (!m_propertyTable)
588 return 0;
589
590 size_t tableSize = PropertyMapHashTable::allocationSize(m_propertyTable->size);
591 PropertyMapHashTable* newTable = static_cast<PropertyMapHashTable*>(fastMalloc(tableSize));
592 memcpy(newTable, m_propertyTable, tableSize);
593
594 unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount;
595 for (unsigned i = 1; i <= entryCount; ++i) {
596 if (UString::Rep* key = newTable->entries()[i].key)
597 key->ref();
598 }
599
600 // Copy the deletedOffsets vector.
601 if (m_propertyTable->deletedOffsets)
602 newTable->deletedOffsets = new Vector<unsigned>(*m_propertyTable->deletedOffsets);
603
604 return newTable;
605}
606
607size_t Structure::get(const Identifier& propertyName, unsigned& attributes)
608{
609 ASSERT(!propertyName.isNull());
610
611 materializePropertyMapIfNecessary();
612 if (!m_propertyTable)
613 return notFound;
614
615 UString::Rep* rep = propertyName._ustring.rep();
616
617 unsigned i = rep->computedHash();
618
619#if DUMP_PROPERTYMAP_STATS
620 ++numProbes;
621#endif
622
623 unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
624 if (entryIndex == emptyEntryIndex)
625 return notFound;
626
627 if (rep == m_propertyTable->entries()[entryIndex - 1].key) {
628 attributes = m_propertyTable->entries()[entryIndex - 1].attributes;
629 return m_propertyTable->entries()[entryIndex - 1].offset;
630 }
631
632#if DUMP_PROPERTYMAP_STATS
633 ++numCollisions;
634#endif
635
636 unsigned k = 1 | doubleHash(rep->computedHash());
637
638 while (1) {
639 i += k;
640
641#if DUMP_PROPERTYMAP_STATS
642 ++numRehashes;
643#endif
644
645 entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
646 if (entryIndex == emptyEntryIndex)
647 return notFound;
648
649 if (rep == m_propertyTable->entries()[entryIndex - 1].key) {
650 attributes = m_propertyTable->entries()[entryIndex - 1].attributes;
651 return m_propertyTable->entries()[entryIndex - 1].offset;
652 }
653 }
654}
655
656size_t Structure::put(const Identifier& propertyName, unsigned attributes)
657{
658 ASSERT(!propertyName.isNull());
659 ASSERT(get(propertyName) == notFound);
660
661 checkConsistency();
662
663 UString::Rep* rep = propertyName._ustring.rep();
664
665 if (!m_propertyTable)
666 createPropertyMapHashTable();
667
668 // FIXME: Consider a fast case for tables with no deleted sentinels.
669
670 unsigned i = rep->computedHash();
671 unsigned k = 0;
672 bool foundDeletedElement = false;
673 unsigned deletedElementIndex = 0; // initialize to make the compiler happy
674
675#if DUMP_PROPERTYMAP_STATS
676 ++numProbes;
677#endif
678
679 while (1) {
680 unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
681 if (entryIndex == emptyEntryIndex)
682 break;
683
684 if (entryIndex == deletedSentinelIndex) {
685 // If we find a deleted-element sentinel, remember it for use later.
686 if (!foundDeletedElement) {
687 foundDeletedElement = true;
688 deletedElementIndex = i;
689 }
690 }
691
692 if (k == 0) {
693 k = 1 | doubleHash(rep->computedHash());
694#if DUMP_PROPERTYMAP_STATS
695 ++numCollisions;
696#endif
697 }
698
699 i += k;
700
701#if DUMP_PROPERTYMAP_STATS
702 ++numRehashes;
703#endif
704 }
705
706 // Figure out which entry to use.
707 unsigned entryIndex = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount + 2;
708 if (foundDeletedElement) {
709 i = deletedElementIndex;
710 --m_propertyTable->deletedSentinelCount;
711
712 // Since we're not making the table bigger, we can't use the entry one past
713 // the end that we were planning on using, so search backwards for the empty
714 // slot that we can use. We know it will be there because we did at least one
715 // deletion in the past that left an entry empty.
716 while (m_propertyTable->entries()[--entryIndex - 1].key) { }
717 }
718
719 // Create a new hash table entry.
720 m_propertyTable->entryIndices[i & m_propertyTable->sizeMask] = entryIndex;
721
722 // Create a new hash table entry.
723 rep->ref();
724 m_propertyTable->entries()[entryIndex - 1].key = rep;
725 m_propertyTable->entries()[entryIndex - 1].attributes = attributes;
726 m_propertyTable->entries()[entryIndex - 1].index = ++m_propertyTable->lastIndexUsed;
727
728 unsigned newOffset;
729 if (m_propertyTable->deletedOffsets && !m_propertyTable->deletedOffsets->isEmpty()) {
730 newOffset = m_propertyTable->deletedOffsets->last();
731 m_propertyTable->deletedOffsets->removeLast();
732 } else
733 newOffset = m_propertyTable->keyCount;
734 m_propertyTable->entries()[entryIndex - 1].offset = newOffset;
735
736 ++m_propertyTable->keyCount;
737
738 if ((m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount) * 2 >= m_propertyTable->size)
739 expandPropertyMapHashTable();
740
741 checkConsistency();
742 return newOffset;
743}
744
745size_t Structure::remove(const Identifier& propertyName)
746{
747 ASSERT(!propertyName.isNull());
748
749 checkConsistency();
750
751 UString::Rep* rep = propertyName._ustring.rep();
752
753 if (!m_propertyTable)
754 return notFound;
755
756#if DUMP_PROPERTYMAP_STATS
757 ++numProbes;
758 ++numRemoves;
759#endif
760
761 // Find the thing to remove.
762 unsigned i = rep->computedHash();
763 unsigned k = 0;
764 unsigned entryIndex;
765 UString::Rep* key = 0;
766 while (1) {
767 entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
768 if (entryIndex == emptyEntryIndex)
769 return notFound;
770
771 key = m_propertyTable->entries()[entryIndex - 1].key;
772 if (rep == key)
773 break;
774
775 if (k == 0) {
776 k = 1 | doubleHash(rep->computedHash());
777#if DUMP_PROPERTYMAP_STATS
778 ++numCollisions;
779#endif
780 }
781
782 i += k;
783
784#if DUMP_PROPERTYMAP_STATS
785 ++numRehashes;
786#endif
787 }
788
789 // Replace this one element with the deleted sentinel. Also clear out
790 // the entry so we can iterate all the entries as needed.
791 m_propertyTable->entryIndices[i & m_propertyTable->sizeMask] = deletedSentinelIndex;
792
793 size_t offset = m_propertyTable->entries()[entryIndex - 1].offset;
794
795 key->deref();
796 m_propertyTable->entries()[entryIndex - 1].key = 0;
797 m_propertyTable->entries()[entryIndex - 1].attributes = 0;
798 m_propertyTable->entries()[entryIndex - 1].offset = 0;
799
800 if (!m_propertyTable->deletedOffsets)
801 m_propertyTable->deletedOffsets = new Vector<unsigned>;
802 m_propertyTable->deletedOffsets->append(offset);
803
804 ASSERT(m_propertyTable->keyCount >= 1);
805 --m_propertyTable->keyCount;
806 ++m_propertyTable->deletedSentinelCount;
807
808 if (m_propertyTable->deletedSentinelCount * 4 >= m_propertyTable->size)
809 rehashPropertyMapHashTable();
810
811 checkConsistency();
812 return offset;
813}
814
815void Structure::insertIntoPropertyMapHashTable(const PropertyMapEntry& entry)
816{
817 ASSERT(m_propertyTable);
818
819 unsigned i = entry.key->computedHash();
820 unsigned k = 0;
821
822#if DUMP_PROPERTYMAP_STATS
823 ++numProbes;
824#endif
825
826 while (1) {
827 unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
828 if (entryIndex == emptyEntryIndex)
829 break;
830
831 if (k == 0) {
832 k = 1 | doubleHash(entry.key->computedHash());
833#if DUMP_PROPERTYMAP_STATS
834 ++numCollisions;
835#endif
836 }
837
838 i += k;
839
840#if DUMP_PROPERTYMAP_STATS
841 ++numRehashes;
842#endif
843 }
844
845 unsigned entryIndex = m_propertyTable->keyCount + 2;
846 m_propertyTable->entryIndices[i & m_propertyTable->sizeMask] = entryIndex;
847 m_propertyTable->entries()[entryIndex - 1] = entry;
848
849 ++m_propertyTable->keyCount;
850}
851
852void Structure::createPropertyMapHashTable()
853{
854 ASSERT(sizeForKeyCount(7) == newTableSize);
855 createPropertyMapHashTable(newTableSize);
856}
857
858void Structure::createPropertyMapHashTable(unsigned newTableSize)
859{
860 ASSERT(!m_propertyTable);
861 ASSERT(isPowerOf2(newTableSize));
862
863 checkConsistency();
864
865 m_propertyTable = static_cast<PropertyMapHashTable*>(fastZeroedMalloc(PropertyMapHashTable::allocationSize(newTableSize)));
866 m_propertyTable->size = newTableSize;
867 m_propertyTable->sizeMask = newTableSize - 1;
868
869 checkConsistency();
870}
871
872void Structure::expandPropertyMapHashTable()
873{
874 ASSERT(m_propertyTable);
875 rehashPropertyMapHashTable(m_propertyTable->size * 2);
876}
877
878void Structure::rehashPropertyMapHashTable()
879{
880 ASSERT(m_propertyTable);
881 ASSERT(m_propertyTable->size);
882 rehashPropertyMapHashTable(m_propertyTable->size);
883}
884
885void Structure::rehashPropertyMapHashTable(unsigned newTableSize)
886{
887 ASSERT(m_propertyTable);
888 ASSERT(isPowerOf2(newTableSize));
889
890 checkConsistency();
891
892 PropertyMapHashTable* oldTable = m_propertyTable;
893
894 m_propertyTable = static_cast<PropertyMapHashTable*>(fastZeroedMalloc(PropertyMapHashTable::allocationSize(newTableSize)));
895 m_propertyTable->size = newTableSize;
896 m_propertyTable->sizeMask = newTableSize - 1;
897
898 unsigned lastIndexUsed = 0;
899 unsigned entryCount = oldTable->keyCount + oldTable->deletedSentinelCount;
900 for (unsigned i = 1; i <= entryCount; ++i) {
901 if (oldTable->entries()[i].key) {
902 lastIndexUsed = max(oldTable->entries()[i].index, lastIndexUsed);
903 insertIntoPropertyMapHashTable(oldTable->entries()[i]);
904 }
905 }
906 m_propertyTable->lastIndexUsed = lastIndexUsed;
907 m_propertyTable->deletedOffsets = oldTable->deletedOffsets;
908
909 fastFree(oldTable);
910
911 checkConsistency();
912}
913
914static int comparePropertyMapEntryIndices(const void* a, const void* b)
915{
916 unsigned ia = static_cast<PropertyMapEntry* const*>(a)[0]->index;
917 unsigned ib = static_cast<PropertyMapEntry* const*>(b)[0]->index;
918 if (ia < ib)
919 return -1;
920 if (ia > ib)
921 return +1;
922 return 0;
923}
924
925void Structure::getEnumerablePropertyNamesInternal(PropertyNameArray& propertyNames)
926{
927 materializePropertyMapIfNecessary();
928 if (!m_propertyTable)
929 return;
930
931 if (m_propertyTable->keyCount < tinyMapThreshold) {
932 PropertyMapEntry* a[tinyMapThreshold];
933 int i = 0;
934 unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount;
935 for (unsigned k = 1; k <= entryCount; k++) {
936 if (m_propertyTable->entries()[k].key && !(m_propertyTable->entries()[k].attributes & DontEnum)) {
937 PropertyMapEntry* value = &m_propertyTable->entries()[k];
938 int j;
939 for (j = i - 1; j >= 0 && a[j]->index > value->index; --j)
940 a[j + 1] = a[j];
941 a[j + 1] = value;
942 ++i;
943 }
944 }
945 if (!propertyNames.size()) {
946 for (int k = 0; k < i; ++k)
947 propertyNames.addKnownUnique(a[k]->key);
948 } else {
949 for (int k = 0; k < i; ++k)
950 propertyNames.add(a[k]->key);
951 }
952
953 return;
954 }
955
956 // Allocate a buffer to use to sort the keys.
957 Vector<PropertyMapEntry*, smallMapThreshold> sortedEnumerables(m_propertyTable->keyCount);
958
959 // Get pointers to the enumerable entries in the buffer.
960 PropertyMapEntry** p = sortedEnumerables.data();
961 unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount;
962 for (unsigned i = 1; i <= entryCount; i++) {
963 if (m_propertyTable->entries()[i].key && !(m_propertyTable->entries()[i].attributes & DontEnum))
964 *p++ = &m_propertyTable->entries()[i];
965 }
966
967 size_t enumerableCount = p - sortedEnumerables.data();
968 // Sort the entries by index.
969 qsort(sortedEnumerables.data(), enumerableCount, sizeof(PropertyMapEntry*), comparePropertyMapEntryIndices);
970 sortedEnumerables.resize(enumerableCount);
971
972 // Put the keys of the sorted entries into the list.
973 if (!propertyNames.size()) {
974 for (size_t i = 0; i < sortedEnumerables.size(); ++i)
975 propertyNames.addKnownUnique(sortedEnumerables[i]->key);
976 } else {
977 for (size_t i = 0; i < sortedEnumerables.size(); ++i)
978 propertyNames.add(sortedEnumerables[i]->key);
979 }
980}
981
982#if DO_PROPERTYMAP_CONSTENCY_CHECK
983
984void Structure::checkConsistency()
985{
986 if (!m_propertyTable)
987 return;
988
989 ASSERT(m_propertyTable->size >= newTableSize);
990 ASSERT(m_propertyTable->sizeMask);
991 ASSERT(m_propertyTable->size == m_propertyTable->sizeMask + 1);
992 ASSERT(!(m_propertyTable->size & m_propertyTable->sizeMask));
993
994 ASSERT(m_propertyTable->keyCount <= m_propertyTable->size / 2);
995 ASSERT(m_propertyTable->deletedSentinelCount <= m_propertyTable->size / 4);
996
997 ASSERT(m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount <= m_propertyTable->size / 2);
998
999 unsigned indexCount = 0;
1000 unsigned deletedIndexCount = 0;
1001 for (unsigned a = 0; a != m_propertyTable->size; ++a) {
1002 unsigned entryIndex = m_propertyTable->entryIndices[a];
1003 if (entryIndex == emptyEntryIndex)
1004 continue;
1005 if (entryIndex == deletedSentinelIndex) {
1006 ++deletedIndexCount;
1007 continue;
1008 }
1009 ASSERT(entryIndex > deletedSentinelIndex);
1010 ASSERT(entryIndex - 1 <= m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount);
1011 ++indexCount;
1012
1013 for (unsigned b = a + 1; b != m_propertyTable->size; ++b)
1014 ASSERT(m_propertyTable->entryIndices[b] != entryIndex);
1015 }
1016 ASSERT(indexCount == m_propertyTable->keyCount);
1017 ASSERT(deletedIndexCount == m_propertyTable->deletedSentinelCount);
1018
1019 ASSERT(m_propertyTable->entries()[0].key == 0);
1020
1021 unsigned nonEmptyEntryCount = 0;
1022 for (unsigned c = 1; c <= m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; ++c) {
1023 UString::Rep* rep = m_propertyTable->entries()[c].key;
1024 if (!rep)
1025 continue;
1026 ++nonEmptyEntryCount;
1027 unsigned i = rep->computedHash();
1028 unsigned k = 0;
1029 unsigned entryIndex;
1030 while (1) {
1031 entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
1032 ASSERT(entryIndex != emptyEntryIndex);
1033 if (rep == m_propertyTable->entries()[entryIndex - 1].key)
1034 break;
1035 if (k == 0)
1036 k = 1 | doubleHash(rep->computedHash());
1037 i += k;
1038 }
1039 ASSERT(entryIndex == c + 1);
1040 }
1041
1042 ASSERT(nonEmptyEntryCount == m_propertyTable->keyCount);
1043}
1044
1045#endif // DO_PROPERTYMAP_CONSTENCY_CHECK
1046
1047} // namespace JSC
Note: See TracBrowser for help on using the repository browser.