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

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

Re-land SNES fix, with correct assertion

RS=Maciej Stachowiak

File size: 38.7 KB
Line 
1/*
2 * Copyright (C) 2008, 2009 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 = *(new Mutex);
69#endif
70
71static bool shouldIgnoreLeaks;
72static HashSet<Structure*>& ignoreSet = *(new HashSet<Structure*>);
73#endif
74
75#if DUMP_STRUCTURE_ID_STATISTICS
76static HashSet<Structure*>& liveStructureSet = *(new HashSet<Structure*>);
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_specificValueInPrevious(0)
127 , m_propertyTable(0)
128 , m_propertyStorageCapacity(JSObject::inlineStorageCapacity)
129 , m_offset(noOffset)
130 , m_dictionaryKind(NoneDictionaryKind)
131 , m_isPinnedPropertyTable(false)
132 , m_hasGetterSetterProperties(false)
133 , m_attributesInPrevious(0)
134{
135 ASSERT(m_prototype);
136 ASSERT(m_prototype.isObject() || m_prototype.isNull());
137
138#ifndef NDEBUG
139#if ENABLE(JSC_MULTIPLE_THREADS)
140 MutexLocker protect(ignoreSetMutex);
141#endif
142 if (shouldIgnoreLeaks)
143 ignoreSet.add(this);
144 else
145 structureCounter.increment();
146#endif
147
148#if DUMP_STRUCTURE_ID_STATISTICS
149 liveStructureSet.add(this);
150#endif
151}
152
153Structure::~Structure()
154{
155 if (m_previous) {
156 if (m_nameInPrevious)
157 m_previous->table.remove(make_pair(m_nameInPrevious.get(), m_attributesInPrevious), m_specificValueInPrevious);
158 else
159 m_previous->table.removeAnonymousSlotTransition(m_anonymousSlotsInPrevious);
160
161 }
162
163 if (m_cachedPropertyNameArrayData)
164 m_cachedPropertyNameArrayData->setCachedStructure(0);
165
166 if (m_propertyTable) {
167 unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount;
168 for (unsigned i = 1; i <= entryCount; i++) {
169 if (UString::Rep* key = m_propertyTable->entries()[i].key)
170 key->deref();
171 }
172
173 delete m_propertyTable->deletedOffsets;
174 fastFree(m_propertyTable);
175 }
176
177#ifndef NDEBUG
178#if ENABLE(JSC_MULTIPLE_THREADS)
179 MutexLocker protect(ignoreSetMutex);
180#endif
181 HashSet<Structure*>::iterator it = ignoreSet.find(this);
182 if (it != ignoreSet.end())
183 ignoreSet.remove(it);
184 else
185 structureCounter.decrement();
186#endif
187
188#if DUMP_STRUCTURE_ID_STATISTICS
189 liveStructureSet.remove(this);
190#endif
191}
192
193void Structure::startIgnoringLeaks()
194{
195#ifndef NDEBUG
196 shouldIgnoreLeaks = true;
197#endif
198}
199
200void Structure::stopIgnoringLeaks()
201{
202#ifndef NDEBUG
203 shouldIgnoreLeaks = false;
204#endif
205}
206
207static bool isPowerOf2(unsigned v)
208{
209 // Taken from http://www.cs.utk.edu/~vose/c-stuff/bithacks.html
210
211 return !(v & (v - 1)) && v;
212}
213
214static unsigned nextPowerOf2(unsigned v)
215{
216 // Taken from http://www.cs.utk.edu/~vose/c-stuff/bithacks.html
217 // Devised by Sean Anderson, Sepember 14, 2001
218
219 v--;
220 v |= v >> 1;
221 v |= v >> 2;
222 v |= v >> 4;
223 v |= v >> 8;
224 v |= v >> 16;
225 v++;
226
227 return v;
228}
229
230static unsigned sizeForKeyCount(size_t keyCount)
231{
232 if (keyCount == notFound)
233 return newTableSize;
234
235 if (keyCount < 8)
236 return newTableSize;
237
238 if (isPowerOf2(keyCount))
239 return keyCount * 4;
240
241 return nextPowerOf2(keyCount) * 2;
242}
243
244void Structure::materializePropertyMap()
245{
246 ASSERT(!m_propertyTable);
247
248 Vector<Structure*, 8> structures;
249 structures.append(this);
250
251 Structure* structure = this;
252
253 // Search for the last Structure with a property table.
254 while ((structure = structure->previousID())) {
255 if (structure->m_isPinnedPropertyTable) {
256 ASSERT(structure->m_propertyTable);
257 ASSERT(!structure->m_previous);
258
259 m_propertyTable = structure->copyPropertyTable();
260 break;
261 }
262
263 structures.append(structure);
264 }
265
266 if (!m_propertyTable)
267 createPropertyMapHashTable(sizeForKeyCount(m_offset + 1));
268 else {
269 if (sizeForKeyCount(m_offset + 1) > m_propertyTable->size)
270 rehashPropertyMapHashTable(sizeForKeyCount(m_offset + 1)); // This could be made more efficient by combining with the copy above.
271 }
272
273 for (ptrdiff_t i = structures.size() - 2; i >= 0; --i) {
274 structure = structures[i];
275 if (!structure->m_nameInPrevious) {
276 m_propertyTable->anonymousSlotCount += structure->m_anonymousSlotsInPrevious;
277 continue;
278 }
279 structure->m_nameInPrevious->ref();
280 PropertyMapEntry entry(structure->m_nameInPrevious.get(), structure->m_offset, structure->m_attributesInPrevious, structure->m_specificValueInPrevious, ++m_propertyTable->lastIndexUsed);
281 insertIntoPropertyMapHashTable(entry);
282 }
283}
284
285void Structure::getOwnEnumerablePropertyNames(ExecState* exec, PropertyNameArray& propertyNames, JSObject* baseObject)
286{
287 getEnumerableNamesFromPropertyTable(propertyNames);
288 getEnumerableNamesFromClassInfoTable(exec, baseObject->classInfo(), propertyNames);
289}
290
291void Structure::getEnumerablePropertyNames(ExecState* exec, PropertyNameArray& propertyNames, JSObject* baseObject)
292{
293 bool shouldCache = propertyNames.shouldCache() && !(propertyNames.size() || isDictionary());
294
295 if (shouldCache && m_cachedPropertyNameArrayData) {
296 if (m_cachedPropertyNameArrayData->cachedPrototypeChain() == prototypeChain(exec)) {
297 propertyNames.setData(m_cachedPropertyNameArrayData);
298 return;
299 }
300 clearEnumerationCache();
301 }
302
303 baseObject->getOwnPropertyNames(exec, 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 JSObject* prototype = asObject(m_prototype);
308 while(1) {
309 if (!prototype->structure()->typeInfo().hasDefaultGetPropertyNames()) {
310 prototype->getPropertyNames(exec, propertyNames);
311 break;
312 }
313 prototype->getOwnPropertyNames(exec, propertyNames);
314 JSValue nextProto = prototype->prototype();
315 if (!nextProto.isObject())
316 break;
317 prototype = asObject(nextProto);
318 }
319 }
320
321 if (shouldCache) {
322 StructureChain* protoChain = prototypeChain(exec);
323 m_cachedPropertyNameArrayData = propertyNames.data();
324 if (!protoChain->isCacheable())
325 return;
326 m_cachedPropertyNameArrayData->setCachedPrototypeChain(protoChain);
327 m_cachedPropertyNameArrayData->setCachedStructure(this);
328 }
329}
330
331void Structure::clearEnumerationCache()
332{
333 if (m_cachedPropertyNameArrayData)
334 m_cachedPropertyNameArrayData->setCachedStructure(0);
335 m_cachedPropertyNameArrayData.clear();
336}
337
338void Structure::growPropertyStorageCapacity()
339{
340 if (m_propertyStorageCapacity == JSObject::inlineStorageCapacity)
341 m_propertyStorageCapacity = JSObject::nonInlineBaseStorageCapacity;
342 else
343 m_propertyStorageCapacity *= 2;
344}
345
346void Structure::despecifyDictionaryFunction(const Identifier& propertyName)
347{
348 const UString::Rep* rep = propertyName._ustring.rep();
349
350 materializePropertyMapIfNecessary();
351
352 ASSERT(isDictionary());
353 ASSERT(m_propertyTable);
354
355 unsigned i = rep->computedHash();
356
357#if DUMP_PROPERTYMAP_STATS
358 ++numProbes;
359#endif
360
361 unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
362 ASSERT(entryIndex != emptyEntryIndex);
363
364 if (rep == m_propertyTable->entries()[entryIndex - 1].key) {
365 m_propertyTable->entries()[entryIndex - 1].specificValue = 0;
366 return;
367 }
368
369#if DUMP_PROPERTYMAP_STATS
370 ++numCollisions;
371#endif
372
373 unsigned k = 1 | doubleHash(rep->computedHash());
374
375 while (1) {
376 i += k;
377
378#if DUMP_PROPERTYMAP_STATS
379 ++numRehashes;
380#endif
381
382 entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
383 ASSERT(entryIndex != emptyEntryIndex);
384
385 if (rep == m_propertyTable->entries()[entryIndex - 1].key) {
386 m_propertyTable->entries()[entryIndex - 1].specificValue = 0;
387 return;
388 }
389 }
390}
391
392PassRefPtr<Structure> Structure::addPropertyTransitionToExistingStructure(Structure* structure, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset)
393{
394 ASSERT(!structure->isDictionary());
395 ASSERT(structure->typeInfo().type() == ObjectType);
396
397 if (Structure* existingTransition = structure->table.get(make_pair(propertyName.ustring().rep(), attributes), specificValue)) {
398 ASSERT(existingTransition->m_offset != noOffset);
399 offset = existingTransition->m_offset;
400 return existingTransition;
401 }
402
403 return 0;
404}
405
406PassRefPtr<Structure> Structure::addPropertyTransition(Structure* structure, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset)
407{
408 ASSERT(!structure->isDictionary());
409 ASSERT(structure->typeInfo().type() == ObjectType);
410 ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure, propertyName, attributes, specificValue, offset));
411
412 if (structure->transitionCount() > s_maxTransitionLength) {
413 RefPtr<Structure> transition = toCacheableDictionaryTransition(structure);
414 ASSERT(structure != transition);
415 offset = transition->put(propertyName, attributes, specificValue);
416 if (transition->propertyStorageSize() > transition->propertyStorageCapacity())
417 transition->growPropertyStorageCapacity();
418 return transition.release();
419 }
420
421 RefPtr<Structure> transition = create(structure->m_prototype, structure->typeInfo());
422
423 transition->m_cachedPrototypeChain = structure->m_cachedPrototypeChain;
424 transition->m_previous = structure;
425 transition->m_nameInPrevious = propertyName.ustring().rep();
426 transition->m_attributesInPrevious = attributes;
427 transition->m_specificValueInPrevious = specificValue;
428 transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
429 transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties;
430
431 if (structure->m_propertyTable) {
432 if (structure->m_isPinnedPropertyTable)
433 transition->m_propertyTable = structure->copyPropertyTable();
434 else {
435 transition->m_propertyTable = structure->m_propertyTable;
436 structure->m_propertyTable = 0;
437 }
438 } else {
439 if (structure->m_previous)
440 transition->materializePropertyMap();
441 else
442 transition->createPropertyMapHashTable();
443 }
444
445 offset = transition->put(propertyName, attributes, specificValue);
446 if (transition->propertyStorageSize() > transition->propertyStorageCapacity())
447 transition->growPropertyStorageCapacity();
448
449 transition->m_offset = offset;
450
451 structure->table.add(make_pair(propertyName.ustring().rep(), attributes), transition.get(), specificValue);
452 return transition.release();
453}
454
455PassRefPtr<Structure> Structure::removePropertyTransition(Structure* structure, const Identifier& propertyName, size_t& offset)
456{
457 ASSERT(!structure->isUncacheableDictionary());
458
459 RefPtr<Structure> transition = toUncacheableDictionaryTransition(structure);
460
461 offset = transition->remove(propertyName);
462
463 return transition.release();
464}
465
466PassRefPtr<Structure> Structure::changePrototypeTransition(Structure* structure, JSValue prototype)
467{
468 RefPtr<Structure> transition = create(prototype, structure->typeInfo());
469
470 transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
471 transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties;
472
473 // Don't set m_offset, as one can not transition to this.
474
475 structure->materializePropertyMapIfNecessary();
476 transition->m_propertyTable = structure->copyPropertyTable();
477 transition->m_isPinnedPropertyTable = true;
478
479 return transition.release();
480}
481
482PassRefPtr<Structure> Structure::despecifyFunctionTransition(Structure* structure, const Identifier& replaceFunction)
483{
484 RefPtr<Structure> transition = create(structure->storedPrototype(), structure->typeInfo());
485
486 transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
487 transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties;
488
489 // Don't set m_offset, as one can not transition to this.
490
491 structure->materializePropertyMapIfNecessary();
492 transition->m_propertyTable = structure->copyPropertyTable();
493 transition->m_isPinnedPropertyTable = true;
494
495 bool removed = transition->despecifyFunction(replaceFunction);
496 ASSERT_UNUSED(removed, removed);
497
498 return transition.release();
499}
500
501PassRefPtr<Structure> Structure::addAnonymousSlotsTransition(Structure* structure, unsigned count)
502{
503 if (Structure* transition = structure->table.getAnonymousSlotTransition(count)) {
504 ASSERT(transition->storedPrototype() == structure->storedPrototype());
505 return transition;
506 }
507 ASSERT(count);
508 ASSERT(count < ((1<<6) - 2));
509 RefPtr<Structure> transition = create(structure->m_prototype, structure->typeInfo());
510
511 transition->m_cachedPrototypeChain = structure->m_cachedPrototypeChain;
512 transition->m_previous = structure;
513 transition->m_nameInPrevious = 0;
514 transition->m_attributesInPrevious = 0;
515 transition->m_anonymousSlotsInPrevious = count;
516 transition->m_specificValueInPrevious = 0;
517 transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
518 transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties;
519
520 if (structure->m_propertyTable) {
521 if (structure->m_isPinnedPropertyTable)
522 transition->m_propertyTable = structure->copyPropertyTable();
523 else {
524 transition->m_propertyTable = structure->m_propertyTable;
525 structure->m_propertyTable = 0;
526 }
527 } else {
528 if (structure->m_previous)
529 transition->materializePropertyMap();
530 else
531 transition->createPropertyMapHashTable();
532 }
533
534 transition->addAnonymousSlots(count);
535 if (transition->propertyStorageSize() > transition->propertyStorageCapacity())
536 transition->growPropertyStorageCapacity();
537
538 structure->table.addAnonymousSlotTransition(count, transition.get());
539 return transition.release();
540}
541
542PassRefPtr<Structure> Structure::getterSetterTransition(Structure* structure)
543{
544 RefPtr<Structure> transition = create(structure->storedPrototype(), structure->typeInfo());
545 transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
546 transition->m_hasGetterSetterProperties = transition->m_hasGetterSetterProperties;
547
548 // Don't set m_offset, as one can not transition to this.
549
550 structure->materializePropertyMapIfNecessary();
551 transition->m_propertyTable = structure->copyPropertyTable();
552 transition->m_isPinnedPropertyTable = true;
553
554 return transition.release();
555}
556
557PassRefPtr<Structure> Structure::toDictionaryTransition(Structure* structure, DictionaryKind kind)
558{
559 ASSERT(!structure->isUncacheableDictionary());
560
561 RefPtr<Structure> transition = create(structure->m_prototype, structure->typeInfo());
562 transition->m_dictionaryKind = kind;
563 transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
564 transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties;
565
566 structure->materializePropertyMapIfNecessary();
567 transition->m_propertyTable = structure->copyPropertyTable();
568 transition->m_isPinnedPropertyTable = true;
569
570 return transition.release();
571}
572
573PassRefPtr<Structure> Structure::toCacheableDictionaryTransition(Structure* structure)
574{
575 return toDictionaryTransition(structure, CachedDictionaryKind);
576}
577
578PassRefPtr<Structure> Structure::toUncacheableDictionaryTransition(Structure* structure)
579{
580 return toDictionaryTransition(structure, UncachedDictionaryKind);
581}
582
583PassRefPtr<Structure> Structure::fromDictionaryTransition(Structure* structure)
584{
585 ASSERT(structure->isDictionary());
586
587 // Since dictionary Structures are not shared, and no opcodes specialize
588 // for them, we don't need to allocate a new Structure when transitioning
589 // to non-dictionary status.
590
591 // FIMXE: We can make this more efficient by canonicalizing the Structure (draining the
592 // deleted offsets vector) before transitioning from dictionary.
593 if (!structure->m_propertyTable || !structure->m_propertyTable->deletedOffsets || structure->m_propertyTable->deletedOffsets->isEmpty())
594 structure->m_dictionaryKind = NoneDictionaryKind;
595
596 return structure;
597}
598
599size_t Structure::addPropertyWithoutTransition(const Identifier& propertyName, unsigned attributes, JSCell* specificValue)
600{
601 materializePropertyMapIfNecessary();
602
603 m_isPinnedPropertyTable = true;
604 size_t offset = put(propertyName, attributes, specificValue);
605 if (propertyStorageSize() > propertyStorageCapacity())
606 growPropertyStorageCapacity();
607 clearEnumerationCache();
608 return offset;
609}
610
611size_t Structure::removePropertyWithoutTransition(const Identifier& propertyName)
612{
613 ASSERT(isUncacheableDictionary());
614
615 materializePropertyMapIfNecessary();
616
617 m_isPinnedPropertyTable = true;
618 size_t offset = remove(propertyName);
619 clearEnumerationCache();
620 return offset;
621}
622
623#if DUMP_PROPERTYMAP_STATS
624
625static int numProbes;
626static int numCollisions;
627static int numRehashes;
628static int numRemoves;
629
630struct PropertyMapStatisticsExitLogger {
631 ~PropertyMapStatisticsExitLogger();
632};
633
634static PropertyMapStatisticsExitLogger logger;
635
636PropertyMapStatisticsExitLogger::~PropertyMapStatisticsExitLogger()
637{
638 printf("\nJSC::PropertyMap statistics\n\n");
639 printf("%d probes\n", numProbes);
640 printf("%d collisions (%.1f%%)\n", numCollisions, 100.0 * numCollisions / numProbes);
641 printf("%d rehashes\n", numRehashes);
642 printf("%d removes\n", numRemoves);
643}
644
645#endif
646
647static const unsigned deletedSentinelIndex = 1;
648
649#if !DO_PROPERTYMAP_CONSTENCY_CHECK
650
651inline void Structure::checkConsistency()
652{
653}
654
655#endif
656
657PropertyMapHashTable* Structure::copyPropertyTable()
658{
659 if (!m_propertyTable)
660 return 0;
661
662 size_t tableSize = PropertyMapHashTable::allocationSize(m_propertyTable->size);
663 PropertyMapHashTable* newTable = static_cast<PropertyMapHashTable*>(fastMalloc(tableSize));
664 memcpy(newTable, m_propertyTable, tableSize);
665
666 unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount;
667 for (unsigned i = 1; i <= entryCount; ++i) {
668 if (UString::Rep* key = newTable->entries()[i].key)
669 key->ref();
670 }
671
672 // Copy the deletedOffsets vector.
673 if (m_propertyTable->deletedOffsets)
674 newTable->deletedOffsets = new Vector<unsigned>(*m_propertyTable->deletedOffsets);
675
676 newTable->anonymousSlotCount = m_propertyTable->anonymousSlotCount;
677 return newTable;
678}
679
680size_t Structure::get(const UString::Rep* rep, unsigned& attributes, JSCell*& specificValue)
681{
682 materializePropertyMapIfNecessary();
683 if (!m_propertyTable)
684 return notFound;
685
686 unsigned i = rep->computedHash();
687
688#if DUMP_PROPERTYMAP_STATS
689 ++numProbes;
690#endif
691
692 unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
693 if (entryIndex == emptyEntryIndex)
694 return notFound;
695
696 if (rep == m_propertyTable->entries()[entryIndex - 1].key) {
697 attributes = m_propertyTable->entries()[entryIndex - 1].attributes;
698 specificValue = m_propertyTable->entries()[entryIndex - 1].specificValue;
699 return m_propertyTable->entries()[entryIndex - 1].offset;
700 }
701
702#if DUMP_PROPERTYMAP_STATS
703 ++numCollisions;
704#endif
705
706 unsigned k = 1 | doubleHash(rep->computedHash());
707
708 while (1) {
709 i += k;
710
711#if DUMP_PROPERTYMAP_STATS
712 ++numRehashes;
713#endif
714
715 entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
716 if (entryIndex == emptyEntryIndex)
717 return notFound;
718
719 if (rep == m_propertyTable->entries()[entryIndex - 1].key) {
720 attributes = m_propertyTable->entries()[entryIndex - 1].attributes;
721 specificValue = m_propertyTable->entries()[entryIndex - 1].specificValue;
722 return m_propertyTable->entries()[entryIndex - 1].offset;
723 }
724 }
725}
726
727bool Structure::despecifyFunction(const Identifier& propertyName)
728{
729 ASSERT(!propertyName.isNull());
730
731 materializePropertyMapIfNecessary();
732 if (!m_propertyTable)
733 return false;
734
735 UString::Rep* rep = propertyName._ustring.rep();
736
737 unsigned i = rep->computedHash();
738
739#if DUMP_PROPERTYMAP_STATS
740 ++numProbes;
741#endif
742
743 unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
744 if (entryIndex == emptyEntryIndex)
745 return false;
746
747 if (rep == m_propertyTable->entries()[entryIndex - 1].key) {
748 ASSERT(m_propertyTable->entries()[entryIndex - 1].specificValue);
749 m_propertyTable->entries()[entryIndex - 1].specificValue = 0;
750 return true;
751 }
752
753#if DUMP_PROPERTYMAP_STATS
754 ++numCollisions;
755#endif
756
757 unsigned k = 1 | doubleHash(rep->computedHash());
758
759 while (1) {
760 i += k;
761
762#if DUMP_PROPERTYMAP_STATS
763 ++numRehashes;
764#endif
765
766 entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
767 if (entryIndex == emptyEntryIndex)
768 return false;
769
770 if (rep == m_propertyTable->entries()[entryIndex - 1].key) {
771 ASSERT(m_propertyTable->entries()[entryIndex - 1].specificValue);
772 m_propertyTable->entries()[entryIndex - 1].specificValue = 0;
773 return true;
774 }
775 }
776}
777
778size_t Structure::put(const Identifier& propertyName, unsigned attributes, JSCell* specificValue)
779{
780 ASSERT(!propertyName.isNull());
781 ASSERT(get(propertyName) == notFound);
782
783 checkConsistency();
784
785 UString::Rep* rep = propertyName._ustring.rep();
786
787 if (!m_propertyTable)
788 createPropertyMapHashTable();
789
790 // FIXME: Consider a fast case for tables with no deleted sentinels.
791
792 unsigned i = rep->computedHash();
793 unsigned k = 0;
794 bool foundDeletedElement = false;
795 unsigned deletedElementIndex = 0; // initialize to make the compiler happy
796
797#if DUMP_PROPERTYMAP_STATS
798 ++numProbes;
799#endif
800
801 while (1) {
802 unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
803 if (entryIndex == emptyEntryIndex)
804 break;
805
806 if (entryIndex == deletedSentinelIndex) {
807 // If we find a deleted-element sentinel, remember it for use later.
808 if (!foundDeletedElement) {
809 foundDeletedElement = true;
810 deletedElementIndex = i;
811 }
812 }
813
814 if (k == 0) {
815 k = 1 | doubleHash(rep->computedHash());
816#if DUMP_PROPERTYMAP_STATS
817 ++numCollisions;
818#endif
819 }
820
821 i += k;
822
823#if DUMP_PROPERTYMAP_STATS
824 ++numRehashes;
825#endif
826 }
827
828 // Figure out which entry to use.
829 unsigned entryIndex = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount + 2;
830 if (foundDeletedElement) {
831 i = deletedElementIndex;
832 --m_propertyTable->deletedSentinelCount;
833
834 // Since we're not making the table bigger, we can't use the entry one past
835 // the end that we were planning on using, so search backwards for the empty
836 // slot that we can use. We know it will be there because we did at least one
837 // deletion in the past that left an entry empty.
838 while (m_propertyTable->entries()[--entryIndex - 1].key) { }
839 }
840
841 // Create a new hash table entry.
842 m_propertyTable->entryIndices[i & m_propertyTable->sizeMask] = entryIndex;
843
844 // Create a new hash table entry.
845 rep->ref();
846 m_propertyTable->entries()[entryIndex - 1].key = rep;
847 m_propertyTable->entries()[entryIndex - 1].attributes = attributes;
848 m_propertyTable->entries()[entryIndex - 1].specificValue = specificValue;
849 m_propertyTable->entries()[entryIndex - 1].index = ++m_propertyTable->lastIndexUsed;
850
851 unsigned newOffset;
852 if (m_propertyTable->deletedOffsets && !m_propertyTable->deletedOffsets->isEmpty()) {
853 newOffset = m_propertyTable->deletedOffsets->last();
854 m_propertyTable->deletedOffsets->removeLast();
855 } else
856 newOffset = m_propertyTable->keyCount + m_propertyTable->anonymousSlotCount;
857 m_propertyTable->entries()[entryIndex - 1].offset = newOffset;
858
859 ++m_propertyTable->keyCount;
860
861 if ((m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount) * 2 >= m_propertyTable->size)
862 expandPropertyMapHashTable();
863
864 checkConsistency();
865 return newOffset;
866}
867
868void Structure::addAnonymousSlots(unsigned count)
869{
870 m_propertyTable->anonymousSlotCount += count;
871}
872
873bool Structure::hasTransition(UString::Rep* rep, unsigned attributes)
874{
875 return table.hasTransition(make_pair(rep, attributes));
876}
877
878size_t Structure::remove(const Identifier& propertyName)
879{
880 ASSERT(!propertyName.isNull());
881
882 checkConsistency();
883
884 UString::Rep* rep = propertyName._ustring.rep();
885
886 if (!m_propertyTable)
887 return notFound;
888
889#if DUMP_PROPERTYMAP_STATS
890 ++numProbes;
891 ++numRemoves;
892#endif
893
894 // Find the thing to remove.
895 unsigned i = rep->computedHash();
896 unsigned k = 0;
897 unsigned entryIndex;
898 UString::Rep* key = 0;
899 while (1) {
900 entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
901 if (entryIndex == emptyEntryIndex)
902 return notFound;
903
904 key = m_propertyTable->entries()[entryIndex - 1].key;
905 if (rep == key)
906 break;
907
908 if (k == 0) {
909 k = 1 | doubleHash(rep->computedHash());
910#if DUMP_PROPERTYMAP_STATS
911 ++numCollisions;
912#endif
913 }
914
915 i += k;
916
917#if DUMP_PROPERTYMAP_STATS
918 ++numRehashes;
919#endif
920 }
921
922 // Replace this one element with the deleted sentinel. Also clear out
923 // the entry so we can iterate all the entries as needed.
924 m_propertyTable->entryIndices[i & m_propertyTable->sizeMask] = deletedSentinelIndex;
925
926 size_t offset = m_propertyTable->entries()[entryIndex - 1].offset;
927
928 key->deref();
929 m_propertyTable->entries()[entryIndex - 1].key = 0;
930 m_propertyTable->entries()[entryIndex - 1].attributes = 0;
931 m_propertyTable->entries()[entryIndex - 1].specificValue = 0;
932 m_propertyTable->entries()[entryIndex - 1].offset = 0;
933
934 if (!m_propertyTable->deletedOffsets)
935 m_propertyTable->deletedOffsets = new Vector<unsigned>;
936 m_propertyTable->deletedOffsets->append(offset);
937
938 ASSERT(m_propertyTable->keyCount >= 1);
939 --m_propertyTable->keyCount;
940 ++m_propertyTable->deletedSentinelCount;
941
942 if (m_propertyTable->deletedSentinelCount * 4 >= m_propertyTable->size)
943 rehashPropertyMapHashTable();
944
945 checkConsistency();
946 return offset;
947}
948
949void Structure::insertIntoPropertyMapHashTable(const PropertyMapEntry& entry)
950{
951 ASSERT(m_propertyTable);
952
953 unsigned i = entry.key->computedHash();
954 unsigned k = 0;
955
956#if DUMP_PROPERTYMAP_STATS
957 ++numProbes;
958#endif
959
960 while (1) {
961 unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
962 if (entryIndex == emptyEntryIndex)
963 break;
964
965 if (k == 0) {
966 k = 1 | doubleHash(entry.key->computedHash());
967#if DUMP_PROPERTYMAP_STATS
968 ++numCollisions;
969#endif
970 }
971
972 i += k;
973
974#if DUMP_PROPERTYMAP_STATS
975 ++numRehashes;
976#endif
977 }
978
979 unsigned entryIndex = m_propertyTable->keyCount + 2;
980 m_propertyTable->entryIndices[i & m_propertyTable->sizeMask] = entryIndex;
981 m_propertyTable->entries()[entryIndex - 1] = entry;
982
983 ++m_propertyTable->keyCount;
984}
985
986void Structure::createPropertyMapHashTable()
987{
988 ASSERT(sizeForKeyCount(7) == newTableSize);
989 createPropertyMapHashTable(newTableSize);
990}
991
992void Structure::createPropertyMapHashTable(unsigned newTableSize)
993{
994 ASSERT(!m_propertyTable);
995 ASSERT(isPowerOf2(newTableSize));
996
997 checkConsistency();
998
999 m_propertyTable = static_cast<PropertyMapHashTable*>(fastZeroedMalloc(PropertyMapHashTable::allocationSize(newTableSize)));
1000 m_propertyTable->size = newTableSize;
1001 m_propertyTable->sizeMask = newTableSize - 1;
1002
1003 checkConsistency();
1004}
1005
1006void Structure::expandPropertyMapHashTable()
1007{
1008 ASSERT(m_propertyTable);
1009 rehashPropertyMapHashTable(m_propertyTable->size * 2);
1010}
1011
1012void Structure::rehashPropertyMapHashTable()
1013{
1014 ASSERT(m_propertyTable);
1015 ASSERT(m_propertyTable->size);
1016 rehashPropertyMapHashTable(m_propertyTable->size);
1017}
1018
1019void Structure::rehashPropertyMapHashTable(unsigned newTableSize)
1020{
1021 ASSERT(m_propertyTable);
1022 ASSERT(isPowerOf2(newTableSize));
1023
1024 checkConsistency();
1025
1026 PropertyMapHashTable* oldTable = m_propertyTable;
1027
1028 m_propertyTable = static_cast<PropertyMapHashTable*>(fastZeroedMalloc(PropertyMapHashTable::allocationSize(newTableSize)));
1029 m_propertyTable->size = newTableSize;
1030 m_propertyTable->sizeMask = newTableSize - 1;
1031 m_propertyTable->anonymousSlotCount = oldTable->anonymousSlotCount;
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
1049static int comparePropertyMapEntryIndices(const void* a, const void* b)
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
1060void 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
1117void 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);
1126
1127 int hashSizeMask = table->compactSize - 1;
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
1138void 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
Note: See TracBrowser for help on using the repository browser.