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