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

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

Migrated some code that didn't belong out of Structure.

Patch by Geoffrey Garen <[email protected]> on 2009-10-08
Reviewed by Sam Weinig.

SunSpider says maybe 1.03x faster.

  • runtime/JSCell.h: Nixed Structure::markAggregate, and made marking of

a Structure's prototype the direct responsility of the object using it.
(Giving Structure a mark function was misleading because it implied that
all live structures get marked during GC, when they don't.)

  • runtime/JSGlobalObject.cpp:

(JSC::markIfNeeded):
(JSC::JSGlobalObject::markChildren): Added code to mark prototypes stored
on the global object. Maybe this wasn't necessary, but now we don't have
to wonder.

  • runtime/JSObject.cpp:

(JSC::JSObject::getPropertyNames):
(JSC::JSObject::getOwnPropertyNames):
(JSC::JSObject::getEnumerableNamesFromClassInfoTable):

  • runtime/JSObject.h:

(JSC::JSObject::markChildrenDirect):

  • runtime/PropertyNameArray.h:
  • runtime/Structure.cpp:
  • runtime/Structure.h:

(JSC::Structure::setEnumerationCache):
(JSC::Structure::enumerationCache): Moved property name gathering code
from Structure to JSObject because having a Structure iterate its JSObject
was a layering violation. A JSObject is implemented using a Structure; not
the other way around.

File size: 36.2 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::clearEnumerationCache()
286{
287 if (m_cachedPropertyNameArrayData)
288 m_cachedPropertyNameArrayData->setCachedStructure(0);
289 m_cachedPropertyNameArrayData.clear();
290}
291
292void Structure::growPropertyStorageCapacity()
293{
294 if (m_propertyStorageCapacity == JSObject::inlineStorageCapacity)
295 m_propertyStorageCapacity = JSObject::nonInlineBaseStorageCapacity;
296 else
297 m_propertyStorageCapacity *= 2;
298}
299
300void Structure::despecifyDictionaryFunction(const Identifier& propertyName)
301{
302 const UString::Rep* rep = propertyName._ustring.rep();
303
304 materializePropertyMapIfNecessary();
305
306 ASSERT(isDictionary());
307 ASSERT(m_propertyTable);
308
309 unsigned i = rep->computedHash();
310
311#if DUMP_PROPERTYMAP_STATS
312 ++numProbes;
313#endif
314
315 unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
316 ASSERT(entryIndex != emptyEntryIndex);
317
318 if (rep == m_propertyTable->entries()[entryIndex - 1].key) {
319 m_propertyTable->entries()[entryIndex - 1].specificValue = 0;
320 return;
321 }
322
323#if DUMP_PROPERTYMAP_STATS
324 ++numCollisions;
325#endif
326
327 unsigned k = 1 | doubleHash(rep->computedHash());
328
329 while (1) {
330 i += k;
331
332#if DUMP_PROPERTYMAP_STATS
333 ++numRehashes;
334#endif
335
336 entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
337 ASSERT(entryIndex != emptyEntryIndex);
338
339 if (rep == m_propertyTable->entries()[entryIndex - 1].key) {
340 m_propertyTable->entries()[entryIndex - 1].specificValue = 0;
341 return;
342 }
343 }
344}
345
346PassRefPtr<Structure> Structure::addPropertyTransitionToExistingStructure(Structure* structure, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset)
347{
348 ASSERT(!structure->isDictionary());
349 ASSERT(structure->typeInfo().type() == ObjectType);
350
351 if (Structure* existingTransition = structure->table.get(make_pair(propertyName.ustring().rep(), attributes), specificValue)) {
352 ASSERT(existingTransition->m_offset != noOffset);
353 offset = existingTransition->m_offset;
354 return existingTransition;
355 }
356
357 return 0;
358}
359
360PassRefPtr<Structure> Structure::addPropertyTransition(Structure* structure, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset)
361{
362 ASSERT(!structure->isDictionary());
363 ASSERT(structure->typeInfo().type() == ObjectType);
364 ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure, propertyName, attributes, specificValue, offset));
365
366 if (structure->transitionCount() > s_maxTransitionLength) {
367 RefPtr<Structure> transition = toCacheableDictionaryTransition(structure);
368 ASSERT(structure != transition);
369 offset = transition->put(propertyName, attributes, specificValue);
370 if (transition->propertyStorageSize() > transition->propertyStorageCapacity())
371 transition->growPropertyStorageCapacity();
372 return transition.release();
373 }
374
375 RefPtr<Structure> transition = create(structure->m_prototype, structure->typeInfo());
376
377 transition->m_cachedPrototypeChain = structure->m_cachedPrototypeChain;
378 transition->m_previous = structure;
379 transition->m_nameInPrevious = propertyName.ustring().rep();
380 transition->m_attributesInPrevious = attributes;
381 transition->m_specificValueInPrevious = specificValue;
382 transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
383 transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties;
384
385 if (structure->m_propertyTable) {
386 if (structure->m_isPinnedPropertyTable)
387 transition->m_propertyTable = structure->copyPropertyTable();
388 else {
389 transition->m_propertyTable = structure->m_propertyTable;
390 structure->m_propertyTable = 0;
391 }
392 } else {
393 if (structure->m_previous)
394 transition->materializePropertyMap();
395 else
396 transition->createPropertyMapHashTable();
397 }
398
399 offset = transition->put(propertyName, attributes, specificValue);
400 if (transition->propertyStorageSize() > transition->propertyStorageCapacity())
401 transition->growPropertyStorageCapacity();
402
403 transition->m_offset = offset;
404
405 structure->table.add(make_pair(propertyName.ustring().rep(), attributes), transition.get(), specificValue);
406 return transition.release();
407}
408
409PassRefPtr<Structure> Structure::removePropertyTransition(Structure* structure, const Identifier& propertyName, size_t& offset)
410{
411 ASSERT(!structure->isUncacheableDictionary());
412
413 RefPtr<Structure> transition = toUncacheableDictionaryTransition(structure);
414
415 offset = transition->remove(propertyName);
416
417 return transition.release();
418}
419
420PassRefPtr<Structure> Structure::changePrototypeTransition(Structure* structure, JSValue prototype)
421{
422 RefPtr<Structure> transition = create(prototype, structure->typeInfo());
423
424 transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
425 transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties;
426
427 // Don't set m_offset, as one can not transition to this.
428
429 structure->materializePropertyMapIfNecessary();
430 transition->m_propertyTable = structure->copyPropertyTable();
431 transition->m_isPinnedPropertyTable = true;
432
433 return transition.release();
434}
435
436PassRefPtr<Structure> Structure::despecifyFunctionTransition(Structure* structure, const Identifier& replaceFunction)
437{
438 RefPtr<Structure> transition = create(structure->storedPrototype(), structure->typeInfo());
439
440 transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
441 transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties;
442
443 // Don't set m_offset, as one can not transition to this.
444
445 structure->materializePropertyMapIfNecessary();
446 transition->m_propertyTable = structure->copyPropertyTable();
447 transition->m_isPinnedPropertyTable = true;
448
449 bool removed = transition->despecifyFunction(replaceFunction);
450 ASSERT_UNUSED(removed, removed);
451
452 return transition.release();
453}
454
455PassRefPtr<Structure> Structure::addAnonymousSlotsTransition(Structure* structure, unsigned count)
456{
457 if (Structure* transition = structure->table.getAnonymousSlotTransition(count)) {
458 ASSERT(transition->storedPrototype() == structure->storedPrototype());
459 return transition;
460 }
461 ASSERT(count);
462 ASSERT(count < ((1<<6) - 2));
463 RefPtr<Structure> transition = create(structure->m_prototype, structure->typeInfo());
464
465 transition->m_cachedPrototypeChain = structure->m_cachedPrototypeChain;
466 transition->m_previous = structure;
467 transition->m_nameInPrevious = 0;
468 transition->m_attributesInPrevious = 0;
469 transition->m_anonymousSlotsInPrevious = count;
470 transition->m_specificValueInPrevious = 0;
471 transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
472 transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties;
473
474 if (structure->m_propertyTable) {
475 if (structure->m_isPinnedPropertyTable)
476 transition->m_propertyTable = structure->copyPropertyTable();
477 else {
478 transition->m_propertyTable = structure->m_propertyTable;
479 structure->m_propertyTable = 0;
480 }
481 } else {
482 if (structure->m_previous)
483 transition->materializePropertyMap();
484 else
485 transition->createPropertyMapHashTable();
486 }
487
488 transition->addAnonymousSlots(count);
489 if (transition->propertyStorageSize() > transition->propertyStorageCapacity())
490 transition->growPropertyStorageCapacity();
491
492 structure->table.addAnonymousSlotTransition(count, transition.get());
493 return transition.release();
494}
495
496PassRefPtr<Structure> Structure::getterSetterTransition(Structure* structure)
497{
498 RefPtr<Structure> transition = create(structure->storedPrototype(), structure->typeInfo());
499 transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
500 transition->m_hasGetterSetterProperties = transition->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 return transition.release();
509}
510
511PassRefPtr<Structure> Structure::toDictionaryTransition(Structure* structure, DictionaryKind kind)
512{
513 ASSERT(!structure->isUncacheableDictionary());
514
515 RefPtr<Structure> transition = create(structure->m_prototype, structure->typeInfo());
516 transition->m_dictionaryKind = kind;
517 transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
518 transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties;
519
520 structure->materializePropertyMapIfNecessary();
521 transition->m_propertyTable = structure->copyPropertyTable();
522 transition->m_isPinnedPropertyTable = true;
523
524 return transition.release();
525}
526
527PassRefPtr<Structure> Structure::toCacheableDictionaryTransition(Structure* structure)
528{
529 return toDictionaryTransition(structure, CachedDictionaryKind);
530}
531
532PassRefPtr<Structure> Structure::toUncacheableDictionaryTransition(Structure* structure)
533{
534 return toDictionaryTransition(structure, UncachedDictionaryKind);
535}
536
537PassRefPtr<Structure> Structure::fromDictionaryTransition(Structure* structure)
538{
539 ASSERT(structure->isDictionary());
540
541 // Since dictionary Structures are not shared, and no opcodes specialize
542 // for them, we don't need to allocate a new Structure when transitioning
543 // to non-dictionary status.
544
545 // FIMXE: We can make this more efficient by canonicalizing the Structure (draining the
546 // deleted offsets vector) before transitioning from dictionary.
547 if (!structure->m_propertyTable || !structure->m_propertyTable->deletedOffsets || structure->m_propertyTable->deletedOffsets->isEmpty())
548 structure->m_dictionaryKind = NoneDictionaryKind;
549
550 return structure;
551}
552
553size_t Structure::addPropertyWithoutTransition(const Identifier& propertyName, unsigned attributes, JSCell* specificValue)
554{
555 materializePropertyMapIfNecessary();
556
557 m_isPinnedPropertyTable = true;
558 size_t offset = put(propertyName, attributes, specificValue);
559 if (propertyStorageSize() > propertyStorageCapacity())
560 growPropertyStorageCapacity();
561 clearEnumerationCache();
562 return offset;
563}
564
565size_t Structure::removePropertyWithoutTransition(const Identifier& propertyName)
566{
567 ASSERT(isUncacheableDictionary());
568
569 materializePropertyMapIfNecessary();
570
571 m_isPinnedPropertyTable = true;
572 size_t offset = remove(propertyName);
573 clearEnumerationCache();
574 return offset;
575}
576
577#if DUMP_PROPERTYMAP_STATS
578
579static int numProbes;
580static int numCollisions;
581static int numRehashes;
582static int numRemoves;
583
584struct PropertyMapStatisticsExitLogger {
585 ~PropertyMapStatisticsExitLogger();
586};
587
588static PropertyMapStatisticsExitLogger logger;
589
590PropertyMapStatisticsExitLogger::~PropertyMapStatisticsExitLogger()
591{
592 printf("\nJSC::PropertyMap statistics\n\n");
593 printf("%d probes\n", numProbes);
594 printf("%d collisions (%.1f%%)\n", numCollisions, 100.0 * numCollisions / numProbes);
595 printf("%d rehashes\n", numRehashes);
596 printf("%d removes\n", numRemoves);
597}
598
599#endif
600
601static const unsigned deletedSentinelIndex = 1;
602
603#if !DO_PROPERTYMAP_CONSTENCY_CHECK
604
605inline void Structure::checkConsistency()
606{
607}
608
609#endif
610
611PropertyMapHashTable* Structure::copyPropertyTable()
612{
613 if (!m_propertyTable)
614 return 0;
615
616 size_t tableSize = PropertyMapHashTable::allocationSize(m_propertyTable->size);
617 PropertyMapHashTable* newTable = static_cast<PropertyMapHashTable*>(fastMalloc(tableSize));
618 memcpy(newTable, m_propertyTable, tableSize);
619
620 unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount;
621 for (unsigned i = 1; i <= entryCount; ++i) {
622 if (UString::Rep* key = newTable->entries()[i].key)
623 key->ref();
624 }
625
626 // Copy the deletedOffsets vector.
627 if (m_propertyTable->deletedOffsets)
628 newTable->deletedOffsets = new Vector<unsigned>(*m_propertyTable->deletedOffsets);
629
630 newTable->anonymousSlotCount = m_propertyTable->anonymousSlotCount;
631 return newTable;
632}
633
634size_t Structure::get(const UString::Rep* rep, unsigned& attributes, JSCell*& specificValue)
635{
636 materializePropertyMapIfNecessary();
637 if (!m_propertyTable)
638 return notFound;
639
640 unsigned i = rep->computedHash();
641
642#if DUMP_PROPERTYMAP_STATS
643 ++numProbes;
644#endif
645
646 unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
647 if (entryIndex == emptyEntryIndex)
648 return notFound;
649
650 if (rep == m_propertyTable->entries()[entryIndex - 1].key) {
651 attributes = m_propertyTable->entries()[entryIndex - 1].attributes;
652 specificValue = m_propertyTable->entries()[entryIndex - 1].specificValue;
653 return m_propertyTable->entries()[entryIndex - 1].offset;
654 }
655
656#if DUMP_PROPERTYMAP_STATS
657 ++numCollisions;
658#endif
659
660 unsigned k = 1 | doubleHash(rep->computedHash());
661
662 while (1) {
663 i += k;
664
665#if DUMP_PROPERTYMAP_STATS
666 ++numRehashes;
667#endif
668
669 entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
670 if (entryIndex == emptyEntryIndex)
671 return notFound;
672
673 if (rep == m_propertyTable->entries()[entryIndex - 1].key) {
674 attributes = m_propertyTable->entries()[entryIndex - 1].attributes;
675 specificValue = m_propertyTable->entries()[entryIndex - 1].specificValue;
676 return m_propertyTable->entries()[entryIndex - 1].offset;
677 }
678 }
679}
680
681bool Structure::despecifyFunction(const Identifier& propertyName)
682{
683 ASSERT(!propertyName.isNull());
684
685 materializePropertyMapIfNecessary();
686 if (!m_propertyTable)
687 return false;
688
689 UString::Rep* rep = propertyName._ustring.rep();
690
691 unsigned i = rep->computedHash();
692
693#if DUMP_PROPERTYMAP_STATS
694 ++numProbes;
695#endif
696
697 unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
698 if (entryIndex == emptyEntryIndex)
699 return false;
700
701 if (rep == m_propertyTable->entries()[entryIndex - 1].key) {
702 ASSERT(m_propertyTable->entries()[entryIndex - 1].specificValue);
703 m_propertyTable->entries()[entryIndex - 1].specificValue = 0;
704 return true;
705 }
706
707#if DUMP_PROPERTYMAP_STATS
708 ++numCollisions;
709#endif
710
711 unsigned k = 1 | doubleHash(rep->computedHash());
712
713 while (1) {
714 i += k;
715
716#if DUMP_PROPERTYMAP_STATS
717 ++numRehashes;
718#endif
719
720 entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
721 if (entryIndex == emptyEntryIndex)
722 return false;
723
724 if (rep == m_propertyTable->entries()[entryIndex - 1].key) {
725 ASSERT(m_propertyTable->entries()[entryIndex - 1].specificValue);
726 m_propertyTable->entries()[entryIndex - 1].specificValue = 0;
727 return true;
728 }
729 }
730}
731
732size_t Structure::put(const Identifier& propertyName, unsigned attributes, JSCell* specificValue)
733{
734 ASSERT(!propertyName.isNull());
735 ASSERT(get(propertyName) == notFound);
736
737 checkConsistency();
738
739 UString::Rep* rep = propertyName._ustring.rep();
740
741 if (!m_propertyTable)
742 createPropertyMapHashTable();
743
744 // FIXME: Consider a fast case for tables with no deleted sentinels.
745
746 unsigned i = rep->computedHash();
747 unsigned k = 0;
748 bool foundDeletedElement = false;
749 unsigned deletedElementIndex = 0; // initialize to make the compiler happy
750
751#if DUMP_PROPERTYMAP_STATS
752 ++numProbes;
753#endif
754
755 while (1) {
756 unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
757 if (entryIndex == emptyEntryIndex)
758 break;
759
760 if (entryIndex == deletedSentinelIndex) {
761 // If we find a deleted-element sentinel, remember it for use later.
762 if (!foundDeletedElement) {
763 foundDeletedElement = true;
764 deletedElementIndex = i;
765 }
766 }
767
768 if (k == 0) {
769 k = 1 | doubleHash(rep->computedHash());
770#if DUMP_PROPERTYMAP_STATS
771 ++numCollisions;
772#endif
773 }
774
775 i += k;
776
777#if DUMP_PROPERTYMAP_STATS
778 ++numRehashes;
779#endif
780 }
781
782 // Figure out which entry to use.
783 unsigned entryIndex = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount + 2;
784 if (foundDeletedElement) {
785 i = deletedElementIndex;
786 --m_propertyTable->deletedSentinelCount;
787
788 // Since we're not making the table bigger, we can't use the entry one past
789 // the end that we were planning on using, so search backwards for the empty
790 // slot that we can use. We know it will be there because we did at least one
791 // deletion in the past that left an entry empty.
792 while (m_propertyTable->entries()[--entryIndex - 1].key) { }
793 }
794
795 // Create a new hash table entry.
796 m_propertyTable->entryIndices[i & m_propertyTable->sizeMask] = entryIndex;
797
798 // Create a new hash table entry.
799 rep->ref();
800 m_propertyTable->entries()[entryIndex - 1].key = rep;
801 m_propertyTable->entries()[entryIndex - 1].attributes = attributes;
802 m_propertyTable->entries()[entryIndex - 1].specificValue = specificValue;
803 m_propertyTable->entries()[entryIndex - 1].index = ++m_propertyTable->lastIndexUsed;
804
805 unsigned newOffset;
806 if (m_propertyTable->deletedOffsets && !m_propertyTable->deletedOffsets->isEmpty()) {
807 newOffset = m_propertyTable->deletedOffsets->last();
808 m_propertyTable->deletedOffsets->removeLast();
809 } else
810 newOffset = m_propertyTable->keyCount + m_propertyTable->anonymousSlotCount;
811 m_propertyTable->entries()[entryIndex - 1].offset = newOffset;
812
813 ++m_propertyTable->keyCount;
814
815 if ((m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount) * 2 >= m_propertyTable->size)
816 expandPropertyMapHashTable();
817
818 checkConsistency();
819 return newOffset;
820}
821
822void Structure::addAnonymousSlots(unsigned count)
823{
824 m_propertyTable->anonymousSlotCount += count;
825}
826
827bool Structure::hasTransition(UString::Rep* rep, unsigned attributes)
828{
829 return table.hasTransition(make_pair(rep, attributes));
830}
831
832size_t Structure::remove(const Identifier& propertyName)
833{
834 ASSERT(!propertyName.isNull());
835
836 checkConsistency();
837
838 UString::Rep* rep = propertyName._ustring.rep();
839
840 if (!m_propertyTable)
841 return notFound;
842
843#if DUMP_PROPERTYMAP_STATS
844 ++numProbes;
845 ++numRemoves;
846#endif
847
848 // Find the thing to remove.
849 unsigned i = rep->computedHash();
850 unsigned k = 0;
851 unsigned entryIndex;
852 UString::Rep* key = 0;
853 while (1) {
854 entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
855 if (entryIndex == emptyEntryIndex)
856 return notFound;
857
858 key = m_propertyTable->entries()[entryIndex - 1].key;
859 if (rep == key)
860 break;
861
862 if (k == 0) {
863 k = 1 | doubleHash(rep->computedHash());
864#if DUMP_PROPERTYMAP_STATS
865 ++numCollisions;
866#endif
867 }
868
869 i += k;
870
871#if DUMP_PROPERTYMAP_STATS
872 ++numRehashes;
873#endif
874 }
875
876 // Replace this one element with the deleted sentinel. Also clear out
877 // the entry so we can iterate all the entries as needed.
878 m_propertyTable->entryIndices[i & m_propertyTable->sizeMask] = deletedSentinelIndex;
879
880 size_t offset = m_propertyTable->entries()[entryIndex - 1].offset;
881
882 key->deref();
883 m_propertyTable->entries()[entryIndex - 1].key = 0;
884 m_propertyTable->entries()[entryIndex - 1].attributes = 0;
885 m_propertyTable->entries()[entryIndex - 1].specificValue = 0;
886 m_propertyTable->entries()[entryIndex - 1].offset = 0;
887
888 if (!m_propertyTable->deletedOffsets)
889 m_propertyTable->deletedOffsets = new Vector<unsigned>;
890 m_propertyTable->deletedOffsets->append(offset);
891
892 ASSERT(m_propertyTable->keyCount >= 1);
893 --m_propertyTable->keyCount;
894 ++m_propertyTable->deletedSentinelCount;
895
896 if (m_propertyTable->deletedSentinelCount * 4 >= m_propertyTable->size)
897 rehashPropertyMapHashTable();
898
899 checkConsistency();
900 return offset;
901}
902
903void Structure::insertIntoPropertyMapHashTable(const PropertyMapEntry& entry)
904{
905 ASSERT(m_propertyTable);
906
907 unsigned i = entry.key->computedHash();
908 unsigned k = 0;
909
910#if DUMP_PROPERTYMAP_STATS
911 ++numProbes;
912#endif
913
914 while (1) {
915 unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
916 if (entryIndex == emptyEntryIndex)
917 break;
918
919 if (k == 0) {
920 k = 1 | doubleHash(entry.key->computedHash());
921#if DUMP_PROPERTYMAP_STATS
922 ++numCollisions;
923#endif
924 }
925
926 i += k;
927
928#if DUMP_PROPERTYMAP_STATS
929 ++numRehashes;
930#endif
931 }
932
933 unsigned entryIndex = m_propertyTable->keyCount + 2;
934 m_propertyTable->entryIndices[i & m_propertyTable->sizeMask] = entryIndex;
935 m_propertyTable->entries()[entryIndex - 1] = entry;
936
937 ++m_propertyTable->keyCount;
938}
939
940void Structure::createPropertyMapHashTable()
941{
942 ASSERT(sizeForKeyCount(7) == newTableSize);
943 createPropertyMapHashTable(newTableSize);
944}
945
946void Structure::createPropertyMapHashTable(unsigned newTableSize)
947{
948 ASSERT(!m_propertyTable);
949 ASSERT(isPowerOf2(newTableSize));
950
951 checkConsistency();
952
953 m_propertyTable = static_cast<PropertyMapHashTable*>(fastZeroedMalloc(PropertyMapHashTable::allocationSize(newTableSize)));
954 m_propertyTable->size = newTableSize;
955 m_propertyTable->sizeMask = newTableSize - 1;
956
957 checkConsistency();
958}
959
960void Structure::expandPropertyMapHashTable()
961{
962 ASSERT(m_propertyTable);
963 rehashPropertyMapHashTable(m_propertyTable->size * 2);
964}
965
966void Structure::rehashPropertyMapHashTable()
967{
968 ASSERT(m_propertyTable);
969 ASSERT(m_propertyTable->size);
970 rehashPropertyMapHashTable(m_propertyTable->size);
971}
972
973void Structure::rehashPropertyMapHashTable(unsigned newTableSize)
974{
975 ASSERT(m_propertyTable);
976 ASSERT(isPowerOf2(newTableSize));
977
978 checkConsistency();
979
980 PropertyMapHashTable* oldTable = m_propertyTable;
981
982 m_propertyTable = static_cast<PropertyMapHashTable*>(fastZeroedMalloc(PropertyMapHashTable::allocationSize(newTableSize)));
983 m_propertyTable->size = newTableSize;
984 m_propertyTable->sizeMask = newTableSize - 1;
985 m_propertyTable->anonymousSlotCount = oldTable->anonymousSlotCount;
986
987 unsigned lastIndexUsed = 0;
988 unsigned entryCount = oldTable->keyCount + oldTable->deletedSentinelCount;
989 for (unsigned i = 1; i <= entryCount; ++i) {
990 if (oldTable->entries()[i].key) {
991 lastIndexUsed = max(oldTable->entries()[i].index, lastIndexUsed);
992 insertIntoPropertyMapHashTable(oldTable->entries()[i]);
993 }
994 }
995 m_propertyTable->lastIndexUsed = lastIndexUsed;
996 m_propertyTable->deletedOffsets = oldTable->deletedOffsets;
997
998 fastFree(oldTable);
999
1000 checkConsistency();
1001}
1002
1003static int comparePropertyMapEntryIndices(const void* a, const void* b)
1004{
1005 unsigned ia = static_cast<PropertyMapEntry* const*>(a)[0]->index;
1006 unsigned ib = static_cast<PropertyMapEntry* const*>(b)[0]->index;
1007 if (ia < ib)
1008 return -1;
1009 if (ia > ib)
1010 return +1;
1011 return 0;
1012}
1013
1014void Structure::getEnumerablePropertyNames(PropertyNameArray& propertyNames)
1015{
1016 materializePropertyMapIfNecessary();
1017 if (!m_propertyTable)
1018 return;
1019
1020 if (m_propertyTable->keyCount < tinyMapThreshold) {
1021 PropertyMapEntry* a[tinyMapThreshold];
1022 int i = 0;
1023 unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount;
1024 for (unsigned k = 1; k <= entryCount; k++) {
1025 if (m_propertyTable->entries()[k].key && !(m_propertyTable->entries()[k].attributes & DontEnum)) {
1026 PropertyMapEntry* value = &m_propertyTable->entries()[k];
1027 int j;
1028 for (j = i - 1; j >= 0 && a[j]->index > value->index; --j)
1029 a[j + 1] = a[j];
1030 a[j + 1] = value;
1031 ++i;
1032 }
1033 }
1034 if (!propertyNames.size()) {
1035 for (int k = 0; k < i; ++k)
1036 propertyNames.addKnownUnique(a[k]->key);
1037 } else {
1038 for (int k = 0; k < i; ++k)
1039 propertyNames.add(a[k]->key);
1040 }
1041
1042 return;
1043 }
1044
1045 // Allocate a buffer to use to sort the keys.
1046 Vector<PropertyMapEntry*, smallMapThreshold> sortedEnumerables(m_propertyTable->keyCount);
1047
1048 // Get pointers to the enumerable entries in the buffer.
1049 PropertyMapEntry** p = sortedEnumerables.data();
1050 unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount;
1051 for (unsigned i = 1; i <= entryCount; i++) {
1052 if (m_propertyTable->entries()[i].key && !(m_propertyTable->entries()[i].attributes & DontEnum))
1053 *p++ = &m_propertyTable->entries()[i];
1054 }
1055
1056 size_t enumerableCount = p - sortedEnumerables.data();
1057 // Sort the entries by index.
1058 qsort(sortedEnumerables.data(), enumerableCount, sizeof(PropertyMapEntry*), comparePropertyMapEntryIndices);
1059 sortedEnumerables.resize(enumerableCount);
1060
1061 // Put the keys of the sorted entries into the list.
1062 if (!propertyNames.size()) {
1063 for (size_t i = 0; i < sortedEnumerables.size(); ++i)
1064 propertyNames.addKnownUnique(sortedEnumerables[i]->key);
1065 } else {
1066 for (size_t i = 0; i < sortedEnumerables.size(); ++i)
1067 propertyNames.add(sortedEnumerables[i]->key);
1068 }
1069}
1070
1071#if DO_PROPERTYMAP_CONSTENCY_CHECK
1072
1073void Structure::checkConsistency()
1074{
1075 if (!m_propertyTable)
1076 return;
1077
1078 ASSERT(m_propertyTable->size >= newTableSize);
1079 ASSERT(m_propertyTable->sizeMask);
1080 ASSERT(m_propertyTable->size == m_propertyTable->sizeMask + 1);
1081 ASSERT(!(m_propertyTable->size & m_propertyTable->sizeMask));
1082
1083 ASSERT(m_propertyTable->keyCount <= m_propertyTable->size / 2);
1084 ASSERT(m_propertyTable->deletedSentinelCount <= m_propertyTable->size / 4);
1085
1086 ASSERT(m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount <= m_propertyTable->size / 2);
1087
1088 unsigned indexCount = 0;
1089 unsigned deletedIndexCount = 0;
1090 for (unsigned a = 0; a != m_propertyTable->size; ++a) {
1091 unsigned entryIndex = m_propertyTable->entryIndices[a];
1092 if (entryIndex == emptyEntryIndex)
1093 continue;
1094 if (entryIndex == deletedSentinelIndex) {
1095 ++deletedIndexCount;
1096 continue;
1097 }
1098 ASSERT(entryIndex > deletedSentinelIndex);
1099 ASSERT(entryIndex - 1 <= m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount);
1100 ++indexCount;
1101
1102 for (unsigned b = a + 1; b != m_propertyTable->size; ++b)
1103 ASSERT(m_propertyTable->entryIndices[b] != entryIndex);
1104 }
1105 ASSERT(indexCount == m_propertyTable->keyCount);
1106 ASSERT(deletedIndexCount == m_propertyTable->deletedSentinelCount);
1107
1108 ASSERT(m_propertyTable->entries()[0].key == 0);
1109
1110 unsigned nonEmptyEntryCount = 0;
1111 for (unsigned c = 1; c <= m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; ++c) {
1112 UString::Rep* rep = m_propertyTable->entries()[c].key;
1113 if (!rep)
1114 continue;
1115 ++nonEmptyEntryCount;
1116 unsigned i = rep->computedHash();
1117 unsigned k = 0;
1118 unsigned entryIndex;
1119 while (1) {
1120 entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
1121 ASSERT(entryIndex != emptyEntryIndex);
1122 if (rep == m_propertyTable->entries()[entryIndex - 1].key)
1123 break;
1124 if (k == 0)
1125 k = 1 | doubleHash(rep->computedHash());
1126 i += k;
1127 }
1128 ASSERT(entryIndex == c + 1);
1129 }
1130
1131 ASSERT(nonEmptyEntryCount == m_propertyTable->keyCount);
1132}
1133
1134#endif // DO_PROPERTYMAP_CONSTENCY_CHECK
1135
1136} // namespace JSC
Note: See TracBrowser for help on using the repository browser.