]> git.saurik.com Git - apple/javascriptcore.git/blame - runtime/Structure.cpp
JavaScriptCore-554.1.tar.gz
[apple/javascriptcore.git] / runtime / Structure.cpp
CommitLineData
9dae56ea 1/*
ba379fdc 2 * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
9dae56ea
A
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "Structure.h"
28
29#include "Identifier.h"
30#include "JSObject.h"
31#include "PropertyNameArray.h"
32#include "StructureChain.h"
33#include "Lookup.h"
34#include <wtf/RefCountedLeakCounter.h>
35#include <wtf/RefPtr.h>
36
37#if ENABLE(JSC_MULTIPLE_THREADS)
38#include <wtf/Threading.h>
39#endif
40
41#define DUMP_STRUCTURE_ID_STATISTICS 0
42
43#ifndef NDEBUG
44#define DO_PROPERTYMAP_CONSTENCY_CHECK 0
45#else
46#define DO_PROPERTYMAP_CONSTENCY_CHECK 0
47#endif
48
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)
ba379fdc 68static Mutex& ignoreSetMutex = *(new Mutex);
9dae56ea
A
69#endif
70
71static bool shouldIgnoreLeaks;
ba379fdc 72static HashSet<Structure*>& ignoreSet = *(new HashSet<Structure*>);
9dae56ea
A
73#endif
74
75#if DUMP_STRUCTURE_ID_STATISTICS
ba379fdc 76static HashSet<Structure*>& liveStructureSet = *(new HashSet<Structure*>);
9dae56ea
A
77#endif
78
ba379fdc
A
79static int comparePropertyMapEntryIndices(const void* a, const void* b);
80
9dae56ea
A
81void Structure::dumpStatistics()
82{
83#if DUMP_STRUCTURE_ID_STATISTICS
84 unsigned numberLeaf = 0;
85 unsigned numberUsingSingleSlot = 0;
86 unsigned numberSingletons = 0;
87 unsigned numberWithPropertyMaps = 0;
88 unsigned totalPropertyMapsSize = 0;
89
90 HashSet<Structure*>::const_iterator end = liveStructureSet.end();
91 for (HashSet<Structure*>::const_iterator it = liveStructureSet.begin(); it != end; ++it) {
92 Structure* structure = *it;
93 if (structure->m_usingSingleTransitionSlot) {
94 if (!structure->m_transitions.singleTransition)
95 ++numberLeaf;
96 else
97 ++numberUsingSingleSlot;
98
99 if (!structure->m_previous && !structure->m_transitions.singleTransition)
100 ++numberSingletons;
101 }
102
103 if (structure->m_propertyTable) {
104 ++numberWithPropertyMaps;
105 totalPropertyMapsSize += PropertyMapHashTable::allocationSize(structure->m_propertyTable->size);
106 if (structure->m_propertyTable->deletedOffsets)
107 totalPropertyMapsSize += (structure->m_propertyTable->deletedOffsets->capacity() * sizeof(unsigned));
108 }
109 }
110
111 printf("Number of live Structures: %d\n", liveStructureSet.size());
112 printf("Number of Structures using the single item optimization for transition map: %d\n", numberUsingSingleSlot);
113 printf("Number of Structures that are leaf nodes: %d\n", numberLeaf);
114 printf("Number of Structures that singletons: %d\n", numberSingletons);
115 printf("Number of Structures with PropertyMaps: %d\n", numberWithPropertyMaps);
116
117 printf("Size of a single Structures: %d\n", static_cast<unsigned>(sizeof(Structure)));
118 printf("Size of sum of all property maps: %d\n", totalPropertyMapsSize);
119 printf("Size of average of all property maps: %f\n", static_cast<double>(totalPropertyMapsSize) / static_cast<double>(liveStructureSet.size()));
120#else
121 printf("Dumping Structure statistics is not enabled.\n");
122#endif
123}
124
ba379fdc 125Structure::Structure(JSValue prototype, const TypeInfo& typeInfo)
9dae56ea
A
126 : m_typeInfo(typeInfo)
127 , m_prototype(prototype)
ba379fdc 128 , m_specificValueInPrevious(0)
9dae56ea
A
129 , m_propertyTable(0)
130 , m_propertyStorageCapacity(JSObject::inlineStorageCapacity)
131 , m_offset(noOffset)
ba379fdc 132 , m_dictionaryKind(NoneDictionaryKind)
9dae56ea
A
133 , m_isPinnedPropertyTable(false)
134 , m_hasGetterSetterProperties(false)
135 , m_usingSingleTransitionSlot(true)
136 , m_attributesInPrevious(0)
137{
138 ASSERT(m_prototype);
139 ASSERT(m_prototype.isObject() || m_prototype.isNull());
140
141 m_transitions.singleTransition = 0;
142
143#ifndef NDEBUG
144#if ENABLE(JSC_MULTIPLE_THREADS)
145 MutexLocker protect(ignoreSetMutex);
146#endif
147 if (shouldIgnoreLeaks)
148 ignoreSet.add(this);
149 else
150 structureCounter.increment();
151#endif
152
153#if DUMP_STRUCTURE_ID_STATISTICS
154 liveStructureSet.add(this);
155#endif
156}
157
158Structure::~Structure()
159{
160 if (m_previous) {
161 if (m_previous->m_usingSingleTransitionSlot) {
162 m_previous->m_transitions.singleTransition = 0;
163 } else {
ba379fdc
A
164 ASSERT(m_previous->m_transitions.table->contains(make_pair(m_nameInPrevious.get(), m_attributesInPrevious), m_specificValueInPrevious));
165 m_previous->m_transitions.table->remove(make_pair(m_nameInPrevious.get(), m_attributesInPrevious), m_specificValueInPrevious);
9dae56ea
A
166 }
167 }
168
169 if (m_cachedPropertyNameArrayData)
170 m_cachedPropertyNameArrayData->setCachedStructure(0);
171
172 if (!m_usingSingleTransitionSlot)
173 delete m_transitions.table;
174
175 if (m_propertyTable) {
176 unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount;
177 for (unsigned i = 1; i <= entryCount; i++) {
178 if (UString::Rep* key = m_propertyTable->entries()[i].key)
179 key->deref();
180 }
181
182 delete m_propertyTable->deletedOffsets;
183 fastFree(m_propertyTable);
184 }
185
186#ifndef NDEBUG
187#if ENABLE(JSC_MULTIPLE_THREADS)
188 MutexLocker protect(ignoreSetMutex);
189#endif
190 HashSet<Structure*>::iterator it = ignoreSet.find(this);
191 if (it != ignoreSet.end())
192 ignoreSet.remove(it);
193 else
194 structureCounter.decrement();
195#endif
196
197#if DUMP_STRUCTURE_ID_STATISTICS
198 liveStructureSet.remove(this);
199#endif
200}
201
202void Structure::startIgnoringLeaks()
203{
204#ifndef NDEBUG
205 shouldIgnoreLeaks = true;
206#endif
207}
208
209void Structure::stopIgnoringLeaks()
210{
211#ifndef NDEBUG
212 shouldIgnoreLeaks = false;
213#endif
214}
215
216static 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
223static 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
239static 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
253void Structure::materializePropertyMap()
254{
255 ASSERT(!m_propertyTable);
256
257 Vector<Structure*, 8> structures;
258 structures.append(this);
259
260 Structure* structure = this;
261
262 // Search for the last Structure with a property table.
263 while ((structure = structure->previousID())) {
264 if (structure->m_isPinnedPropertyTable) {
265 ASSERT(structure->m_propertyTable);
266 ASSERT(!structure->m_previous);
267
268 m_propertyTable = structure->copyPropertyTable();
269 break;
270 }
271
272 structures.append(structure);
273 }
274
275 if (!m_propertyTable)
276 createPropertyMapHashTable(sizeForKeyCount(m_offset + 1));
277 else {
278 if (sizeForKeyCount(m_offset + 1) > m_propertyTable->size)
279 rehashPropertyMapHashTable(sizeForKeyCount(m_offset + 1)); // This could be made more efficient by combining with the copy above.
280 }
281
282 for (ptrdiff_t i = structures.size() - 2; i >= 0; --i) {
283 structure = structures[i];
284 structure->m_nameInPrevious->ref();
ba379fdc 285 PropertyMapEntry entry(structure->m_nameInPrevious.get(), structure->m_offset, structure->m_attributesInPrevious, structure->m_specificValueInPrevious, ++m_propertyTable->lastIndexUsed);
9dae56ea
A
286 insertIntoPropertyMapHashTable(entry);
287 }
288}
289
290void Structure::getEnumerablePropertyNames(ExecState* exec, PropertyNameArray& propertyNames, JSObject* baseObject)
291{
ba379fdc 292 bool shouldCache = propertyNames.shouldCache() && !(propertyNames.size() || isDictionary());
9dae56ea
A
293
294 if (shouldCache && m_cachedPropertyNameArrayData) {
295 if (m_cachedPropertyNameArrayData->cachedPrototypeChain() == prototypeChain(exec)) {
296 propertyNames.setData(m_cachedPropertyNameArrayData);
297 return;
298 }
299 clearEnumerationCache();
300 }
301
302 getEnumerableNamesFromPropertyTable(propertyNames);
303 getEnumerableNamesFromClassInfoTable(exec, baseObject->classInfo(), propertyNames);
304
305 if (m_prototype.isObject()) {
306 propertyNames.setShouldCache(false); // No need for our prototypes to waste memory on caching, since they're not being enumerated directly.
307 asObject(m_prototype)->getPropertyNames(exec, propertyNames);
308 }
309
310 if (shouldCache) {
ba379fdc 311 StructureChain* protoChain = prototypeChain(exec);
9dae56ea 312 m_cachedPropertyNameArrayData = propertyNames.data();
ba379fdc
A
313 if (!protoChain->isCacheable())
314 return;
315 m_cachedPropertyNameArrayData->setCachedPrototypeChain(protoChain);
9dae56ea
A
316 m_cachedPropertyNameArrayData->setCachedStructure(this);
317 }
318}
319
320void Structure::clearEnumerationCache()
321{
322 if (m_cachedPropertyNameArrayData)
323 m_cachedPropertyNameArrayData->setCachedStructure(0);
324 m_cachedPropertyNameArrayData.clear();
325}
326
327void Structure::growPropertyStorageCapacity()
328{
329 if (m_propertyStorageCapacity == JSObject::inlineStorageCapacity)
330 m_propertyStorageCapacity = JSObject::nonInlineBaseStorageCapacity;
331 else
332 m_propertyStorageCapacity *= 2;
333}
334
ba379fdc
A
335void 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
381PassRefPtr<Structure> Structure::addPropertyTransitionToExistingStructure(Structure* structure, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset)
9dae56ea 382{
ba379fdc 383 ASSERT(!structure->isDictionary());
9dae56ea
A
384 ASSERT(structure->typeInfo().type() == ObjectType);
385
386 if (structure->m_usingSingleTransitionSlot) {
387 Structure* existingTransition = structure->m_transitions.singleTransition;
ba379fdc
A
388 if (existingTransition && existingTransition->m_nameInPrevious.get() == propertyName.ustring().rep()
389 && existingTransition->m_attributesInPrevious == attributes
390 && (existingTransition->m_specificValueInPrevious == specificValue || existingTransition->m_specificValueInPrevious == 0)) {
391
9dae56ea
A
392 ASSERT(structure->m_transitions.singleTransition->m_offset != noOffset);
393 offset = structure->m_transitions.singleTransition->m_offset;
394 return existingTransition;
395 }
396 } else {
ba379fdc 397 if (Structure* existingTransition = structure->m_transitions.table->get(make_pair(propertyName.ustring().rep(), attributes), specificValue)) {
9dae56ea
A
398 ASSERT(existingTransition->m_offset != noOffset);
399 offset = existingTransition->m_offset;
400 return existingTransition;
401 }
402 }
403
404 return 0;
405}
406
ba379fdc 407PassRefPtr<Structure> Structure::addPropertyTransition(Structure* structure, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset)
9dae56ea 408{
ba379fdc 409 ASSERT(!structure->isDictionary());
9dae56ea 410 ASSERT(structure->typeInfo().type() == ObjectType);
ba379fdc 411 ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure, propertyName, attributes, specificValue, offset));
9dae56ea
A
412
413 if (structure->transitionCount() > s_maxTransitionLength) {
ba379fdc
A
414 RefPtr<Structure> transition = toCacheableDictionaryTransition(structure);
415 ASSERT(structure != transition);
416 offset = transition->put(propertyName, attributes, specificValue);
9dae56ea
A
417 if (transition->propertyStorageSize() > transition->propertyStorageCapacity())
418 transition->growPropertyStorageCapacity();
419 return transition.release();
420 }
421
422 RefPtr<Structure> transition = create(structure->m_prototype, structure->typeInfo());
ba379fdc 423
9dae56ea
A
424 transition->m_cachedPrototypeChain = structure->m_cachedPrototypeChain;
425 transition->m_previous = structure;
426 transition->m_nameInPrevious = propertyName.ustring().rep();
427 transition->m_attributesInPrevious = attributes;
ba379fdc 428 transition->m_specificValueInPrevious = specificValue;
9dae56ea
A
429 transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
430 transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties;
431
432 if (structure->m_propertyTable) {
433 if (structure->m_isPinnedPropertyTable)
434 transition->m_propertyTable = structure->copyPropertyTable();
435 else {
436 transition->m_propertyTable = structure->m_propertyTable;
437 structure->m_propertyTable = 0;
438 }
439 } else {
440 if (structure->m_previous)
441 transition->materializePropertyMap();
442 else
443 transition->createPropertyMapHashTable();
444 }
445
ba379fdc 446 offset = transition->put(propertyName, attributes, specificValue);
9dae56ea
A
447 if (transition->propertyStorageSize() > transition->propertyStorageCapacity())
448 transition->growPropertyStorageCapacity();
449
450 transition->m_offset = offset;
451
452 if (structure->m_usingSingleTransitionSlot) {
453 if (!structure->m_transitions.singleTransition) {
454 structure->m_transitions.singleTransition = transition.get();
455 return transition.release();
456 }
457
458 Structure* existingTransition = structure->m_transitions.singleTransition;
459 structure->m_usingSingleTransitionSlot = false;
460 StructureTransitionTable* transitionTable = new StructureTransitionTable;
461 structure->m_transitions.table = transitionTable;
ba379fdc 462 transitionTable->add(make_pair(existingTransition->m_nameInPrevious.get(), existingTransition->m_attributesInPrevious), existingTransition, existingTransition->m_specificValueInPrevious);
9dae56ea 463 }
ba379fdc 464 structure->m_transitions.table->add(make_pair(propertyName.ustring().rep(), attributes), transition.get(), specificValue);
9dae56ea
A
465 return transition.release();
466}
467
468PassRefPtr<Structure> Structure::removePropertyTransition(Structure* structure, const Identifier& propertyName, size_t& offset)
469{
ba379fdc 470 ASSERT(!structure->isUncacheableDictionary());
9dae56ea 471
ba379fdc 472 RefPtr<Structure> transition = toUncacheableDictionaryTransition(structure);
9dae56ea
A
473
474 offset = transition->remove(propertyName);
475
476 return transition.release();
477}
478
ba379fdc 479PassRefPtr<Structure> Structure::changePrototypeTransition(Structure* structure, JSValue prototype)
9dae56ea
A
480{
481 RefPtr<Structure> transition = create(prototype, structure->typeInfo());
482
483 transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
484 transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties;
485
486 // Don't set m_offset, as one can not transition to this.
487
488 structure->materializePropertyMapIfNecessary();
489 transition->m_propertyTable = structure->copyPropertyTable();
490 transition->m_isPinnedPropertyTable = true;
491
492 return transition.release();
493}
494
ba379fdc
A
495PassRefPtr<Structure> Structure::despecifyFunctionTransition(Structure* structure, const Identifier& replaceFunction)
496{
497 RefPtr<Structure> transition = create(structure->storedPrototype(), structure->typeInfo());
498
499 transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
500 transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties;
501
502 // Don't set m_offset, as one can not transition to this.
503
504 structure->materializePropertyMapIfNecessary();
505 transition->m_propertyTable = structure->copyPropertyTable();
506 transition->m_isPinnedPropertyTable = true;
507
508 bool removed = transition->despecifyFunction(replaceFunction);
509 ASSERT_UNUSED(removed, removed);
510
511 return transition.release();
512}
513
9dae56ea
A
514PassRefPtr<Structure> Structure::getterSetterTransition(Structure* structure)
515{
516 RefPtr<Structure> transition = create(structure->storedPrototype(), structure->typeInfo());
517 transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
518 transition->m_hasGetterSetterProperties = transition->m_hasGetterSetterProperties;
519
520 // Don't set m_offset, as one can not transition to this.
521
522 structure->materializePropertyMapIfNecessary();
523 transition->m_propertyTable = structure->copyPropertyTable();
524 transition->m_isPinnedPropertyTable = true;
525
526 return transition.release();
527}
528
ba379fdc 529PassRefPtr<Structure> Structure::toDictionaryTransition(Structure* structure, DictionaryKind kind)
9dae56ea 530{
ba379fdc
A
531 ASSERT(!structure->isUncacheableDictionary());
532
9dae56ea 533 RefPtr<Structure> transition = create(structure->m_prototype, structure->typeInfo());
ba379fdc 534 transition->m_dictionaryKind = kind;
9dae56ea
A
535 transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
536 transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties;
ba379fdc 537
9dae56ea
A
538 structure->materializePropertyMapIfNecessary();
539 transition->m_propertyTable = structure->copyPropertyTable();
540 transition->m_isPinnedPropertyTable = true;
ba379fdc 541
9dae56ea
A
542 return transition.release();
543}
544
ba379fdc 545PassRefPtr<Structure> Structure::toCacheableDictionaryTransition(Structure* structure)
9dae56ea 546{
ba379fdc
A
547 return toDictionaryTransition(structure, CachedDictionaryKind);
548}
9dae56ea 549
ba379fdc
A
550PassRefPtr<Structure> Structure::toUncacheableDictionaryTransition(Structure* structure)
551{
552 return toDictionaryTransition(structure, UncachedDictionaryKind);
553}
9dae56ea 554
ba379fdc
A
555PassRefPtr<Structure> Structure::flattenDictionaryStructure(JSObject* object)
556{
557 ASSERT(isDictionary());
558 if (isUncacheableDictionary()) {
559 ASSERT(m_propertyTable);
560 Vector<PropertyMapEntry*> sortedPropertyEntries(m_propertyTable->keyCount);
561 PropertyMapEntry** p = sortedPropertyEntries.data();
562 unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount;
563 for (unsigned i = 1; i <= entryCount; i++) {
564 if (m_propertyTable->entries()[i].key)
565 *p++ = &m_propertyTable->entries()[i];
566 }
567 size_t propertyCount = p - sortedPropertyEntries.data();
568 qsort(sortedPropertyEntries.data(), propertyCount, sizeof(PropertyMapEntry*), comparePropertyMapEntryIndices);
569 sortedPropertyEntries.resize(propertyCount);
570
571 // We now have the properties currently defined on this object
572 // in the order that they are expected to be in, but we need to
573 // reorder the storage, so we have to copy the current values out
574 Vector<JSValue> values(propertyCount);
575 // FIXME: Remove this workaround with the next merge.
576 unsigned anonymousSlotCount = 0;
577 for (unsigned i = 0; i < propertyCount; i++) {
578 PropertyMapEntry* entry = sortedPropertyEntries[i];
579 values[i] = object->getDirectOffset(entry->offset);
580 // Update property table to have the new property offsets
581 entry->offset = anonymousSlotCount + i;
582 entry->index = i;
583 }
584
585 // Copy the original property values into their final locations
586 for (unsigned i = 0; i < propertyCount; i++)
587 object->putDirectOffset(anonymousSlotCount + i, values[i]);
588
589 if (m_propertyTable->deletedOffsets) {
590 delete m_propertyTable->deletedOffsets;
591 m_propertyTable->deletedOffsets = 0;
592 }
593 }
9dae56ea 594
ba379fdc
A
595 m_dictionaryKind = NoneDictionaryKind;
596 return this;
9dae56ea
A
597}
598
ba379fdc 599size_t Structure::addPropertyWithoutTransition(const Identifier& propertyName, unsigned attributes, JSCell* specificValue)
9dae56ea
A
600{
601 ASSERT(!m_transitions.singleTransition);
602
603 materializePropertyMapIfNecessary();
604
605 m_isPinnedPropertyTable = true;
ba379fdc 606 size_t offset = put(propertyName, attributes, specificValue);
9dae56ea
A
607 if (propertyStorageSize() > propertyStorageCapacity())
608 growPropertyStorageCapacity();
609 clearEnumerationCache();
610 return offset;
611}
612
613size_t Structure::removePropertyWithoutTransition(const Identifier& propertyName)
614{
ba379fdc 615 ASSERT(isUncacheableDictionary());
9dae56ea
A
616
617 materializePropertyMapIfNecessary();
618
619 m_isPinnedPropertyTable = true;
620 size_t offset = remove(propertyName);
621 clearEnumerationCache();
622 return offset;
623}
624
625#if DUMP_PROPERTYMAP_STATS
626
627static int numProbes;
628static int numCollisions;
629static int numRehashes;
630static int numRemoves;
631
632struct PropertyMapStatisticsExitLogger {
633 ~PropertyMapStatisticsExitLogger();
634};
635
636static PropertyMapStatisticsExitLogger logger;
637
638PropertyMapStatisticsExitLogger::~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
649static const unsigned deletedSentinelIndex = 1;
650
651#if !DO_PROPERTYMAP_CONSTENCY_CHECK
652
653inline void Structure::checkConsistency()
654{
655}
656
657#endif
658
659PropertyMapHashTable* Structure::copyPropertyTable()
660{
661 if (!m_propertyTable)
662 return 0;
663
664 size_t tableSize = PropertyMapHashTable::allocationSize(m_propertyTable->size);
665 PropertyMapHashTable* newTable = static_cast<PropertyMapHashTable*>(fastMalloc(tableSize));
666 memcpy(newTable, m_propertyTable, tableSize);
667
668 unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount;
669 for (unsigned i = 1; i <= entryCount; ++i) {
670 if (UString::Rep* key = newTable->entries()[i].key)
671 key->ref();
672 }
673
674 // Copy the deletedOffsets vector.
675 if (m_propertyTable->deletedOffsets)
676 newTable->deletedOffsets = new Vector<unsigned>(*m_propertyTable->deletedOffsets);
677
678 return newTable;
679}
680
ba379fdc 681size_t Structure::get(const UString::Rep* rep, unsigned& attributes, JSCell*& specificValue)
9dae56ea 682{
9dae56ea
A
683 materializePropertyMapIfNecessary();
684 if (!m_propertyTable)
685 return notFound;
686
9dae56ea
A
687 unsigned i = rep->computedHash();
688
689#if DUMP_PROPERTYMAP_STATS
690 ++numProbes;
691#endif
692
693 unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
694 if (entryIndex == emptyEntryIndex)
695 return notFound;
696
697 if (rep == m_propertyTable->entries()[entryIndex - 1].key) {
698 attributes = m_propertyTable->entries()[entryIndex - 1].attributes;
ba379fdc 699 specificValue = m_propertyTable->entries()[entryIndex - 1].specificValue;
9dae56ea
A
700 return m_propertyTable->entries()[entryIndex - 1].offset;
701 }
702
703#if DUMP_PROPERTYMAP_STATS
704 ++numCollisions;
705#endif
706
707 unsigned k = 1 | doubleHash(rep->computedHash());
708
709 while (1) {
710 i += k;
711
712#if DUMP_PROPERTYMAP_STATS
713 ++numRehashes;
714#endif
715
716 entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
717 if (entryIndex == emptyEntryIndex)
718 return notFound;
719
720 if (rep == m_propertyTable->entries()[entryIndex - 1].key) {
721 attributes = m_propertyTable->entries()[entryIndex - 1].attributes;
ba379fdc 722 specificValue = m_propertyTable->entries()[entryIndex - 1].specificValue;
9dae56ea
A
723 return m_propertyTable->entries()[entryIndex - 1].offset;
724 }
725 }
726}
727
ba379fdc
A
728bool 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
779size_t Structure::put(const Identifier& propertyName, unsigned attributes, JSCell* specificValue)
9dae56ea
A
780{
781 ASSERT(!propertyName.isNull());
782 ASSERT(get(propertyName) == notFound);
783
784 checkConsistency();
785
786 UString::Rep* rep = propertyName._ustring.rep();
787
788 if (!m_propertyTable)
789 createPropertyMapHashTable();
790
791 // FIXME: Consider a fast case for tables with no deleted sentinels.
792
793 unsigned i = rep->computedHash();
794 unsigned k = 0;
795 bool foundDeletedElement = false;
796 unsigned deletedElementIndex = 0; // initialize to make the compiler happy
797
798#if DUMP_PROPERTYMAP_STATS
799 ++numProbes;
800#endif
801
802 while (1) {
803 unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
804 if (entryIndex == emptyEntryIndex)
805 break;
806
807 if (entryIndex == deletedSentinelIndex) {
808 // If we find a deleted-element sentinel, remember it for use later.
809 if (!foundDeletedElement) {
810 foundDeletedElement = true;
811 deletedElementIndex = i;
812 }
813 }
814
815 if (k == 0) {
816 k = 1 | doubleHash(rep->computedHash());
817#if DUMP_PROPERTYMAP_STATS
818 ++numCollisions;
819#endif
820 }
821
822 i += k;
823
824#if DUMP_PROPERTYMAP_STATS
825 ++numRehashes;
826#endif
827 }
828
829 // Figure out which entry to use.
830 unsigned entryIndex = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount + 2;
831 if (foundDeletedElement) {
832 i = deletedElementIndex;
833 --m_propertyTable->deletedSentinelCount;
834
835 // Since we're not making the table bigger, we can't use the entry one past
836 // the end that we were planning on using, so search backwards for the empty
837 // slot that we can use. We know it will be there because we did at least one
838 // deletion in the past that left an entry empty.
839 while (m_propertyTable->entries()[--entryIndex - 1].key) { }
840 }
841
842 // Create a new hash table entry.
843 m_propertyTable->entryIndices[i & m_propertyTable->sizeMask] = entryIndex;
844
845 // Create a new hash table entry.
846 rep->ref();
847 m_propertyTable->entries()[entryIndex - 1].key = rep;
848 m_propertyTable->entries()[entryIndex - 1].attributes = attributes;
ba379fdc 849 m_propertyTable->entries()[entryIndex - 1].specificValue = specificValue;
9dae56ea
A
850 m_propertyTable->entries()[entryIndex - 1].index = ++m_propertyTable->lastIndexUsed;
851
852 unsigned newOffset;
853 if (m_propertyTable->deletedOffsets && !m_propertyTable->deletedOffsets->isEmpty()) {
854 newOffset = m_propertyTable->deletedOffsets->last();
855 m_propertyTable->deletedOffsets->removeLast();
856 } else
857 newOffset = m_propertyTable->keyCount;
858 m_propertyTable->entries()[entryIndex - 1].offset = newOffset;
859
860 ++m_propertyTable->keyCount;
861
862 if ((m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount) * 2 >= m_propertyTable->size)
863 expandPropertyMapHashTable();
864
865 checkConsistency();
866 return newOffset;
867}
868
ba379fdc
A
869bool Structure::hasTransition(UString::Rep* rep, unsigned attributes)
870{
871 if (m_usingSingleTransitionSlot) {
872 return m_transitions.singleTransition
873 && m_transitions.singleTransition->m_nameInPrevious == rep
874 && m_transitions.singleTransition->m_attributesInPrevious == attributes;
875 }
876 return m_transitions.table->hasTransition(make_pair(rep, attributes));
877}
878
9dae56ea
A
879size_t Structure::remove(const Identifier& propertyName)
880{
881 ASSERT(!propertyName.isNull());
882
883 checkConsistency();
884
885 UString::Rep* rep = propertyName._ustring.rep();
886
887 if (!m_propertyTable)
888 return notFound;
889
890#if DUMP_PROPERTYMAP_STATS
891 ++numProbes;
892 ++numRemoves;
893#endif
894
895 // Find the thing to remove.
896 unsigned i = rep->computedHash();
897 unsigned k = 0;
898 unsigned entryIndex;
899 UString::Rep* key = 0;
900 while (1) {
901 entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
902 if (entryIndex == emptyEntryIndex)
903 return notFound;
904
905 key = m_propertyTable->entries()[entryIndex - 1].key;
906 if (rep == key)
907 break;
908
909 if (k == 0) {
910 k = 1 | doubleHash(rep->computedHash());
911#if DUMP_PROPERTYMAP_STATS
912 ++numCollisions;
913#endif
914 }
915
916 i += k;
917
918#if DUMP_PROPERTYMAP_STATS
919 ++numRehashes;
920#endif
921 }
922
923 // Replace this one element with the deleted sentinel. Also clear out
924 // the entry so we can iterate all the entries as needed.
925 m_propertyTable->entryIndices[i & m_propertyTable->sizeMask] = deletedSentinelIndex;
926
927 size_t offset = m_propertyTable->entries()[entryIndex - 1].offset;
928
929 key->deref();
930 m_propertyTable->entries()[entryIndex - 1].key = 0;
931 m_propertyTable->entries()[entryIndex - 1].attributes = 0;
ba379fdc 932 m_propertyTable->entries()[entryIndex - 1].specificValue = 0;
9dae56ea
A
933 m_propertyTable->entries()[entryIndex - 1].offset = 0;
934
935 if (!m_propertyTable->deletedOffsets)
936 m_propertyTable->deletedOffsets = new Vector<unsigned>;
937 m_propertyTable->deletedOffsets->append(offset);
938
939 ASSERT(m_propertyTable->keyCount >= 1);
940 --m_propertyTable->keyCount;
941 ++m_propertyTable->deletedSentinelCount;
942
943 if (m_propertyTable->deletedSentinelCount * 4 >= m_propertyTable->size)
944 rehashPropertyMapHashTable();
945
946 checkConsistency();
947 return offset;
948}
949
950void 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
987void Structure::createPropertyMapHashTable()
988{
989 ASSERT(sizeForKeyCount(7) == newTableSize);
990 createPropertyMapHashTable(newTableSize);
991}
992
993void 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
1007void Structure::expandPropertyMapHashTable()
1008{
1009 ASSERT(m_propertyTable);
1010 rehashPropertyMapHashTable(m_propertyTable->size * 2);
1011}
1012
1013void Structure::rehashPropertyMapHashTable()
1014{
1015 ASSERT(m_propertyTable);
1016 ASSERT(m_propertyTable->size);
1017 rehashPropertyMapHashTable(m_propertyTable->size);
1018}
1019
1020void Structure::rehashPropertyMapHashTable(unsigned newTableSize)
1021{
1022 ASSERT(m_propertyTable);
1023 ASSERT(isPowerOf2(newTableSize));
1024
1025 checkConsistency();
1026
1027 PropertyMapHashTable* oldTable = m_propertyTable;
1028
1029 m_propertyTable = static_cast<PropertyMapHashTable*>(fastZeroedMalloc(PropertyMapHashTable::allocationSize(newTableSize)));
1030 m_propertyTable->size = newTableSize;
1031 m_propertyTable->sizeMask = newTableSize - 1;
1032
1033 unsigned lastIndexUsed = 0;
1034 unsigned entryCount = oldTable->keyCount + oldTable->deletedSentinelCount;
1035 for (unsigned i = 1; i <= entryCount; ++i) {
1036 if (oldTable->entries()[i].key) {
1037 lastIndexUsed = max(oldTable->entries()[i].index, lastIndexUsed);
1038 insertIntoPropertyMapHashTable(oldTable->entries()[i]);
1039 }
1040 }
1041 m_propertyTable->lastIndexUsed = lastIndexUsed;
1042 m_propertyTable->deletedOffsets = oldTable->deletedOffsets;
1043
1044 fastFree(oldTable);
1045
1046 checkConsistency();
1047}
1048
ba379fdc 1049int comparePropertyMapEntryIndices(const void* a, const void* b)
9dae56ea
A
1050{
1051 unsigned ia = static_cast<PropertyMapEntry* const*>(a)[0]->index;
1052 unsigned ib = static_cast<PropertyMapEntry* const*>(b)[0]->index;
1053 if (ia < ib)
1054 return -1;
1055 if (ia > ib)
1056 return +1;
1057 return 0;
1058}
1059
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);
ba379fdc 1126
9dae56ea 1127 int hashSizeMask = table->compactSize - 1;
9dae56ea
A
1128 const HashEntry* entry = table->table;
1129 for (int i = 0; i <= hashSizeMask; ++i, ++entry) {
1130 if (entry->key() && !(entry->attributes() & DontEnum))
1131 propertyNames.add(entry->key());
1132 }
1133 }
1134}
1135
1136#if DO_PROPERTYMAP_CONSTENCY_CHECK
1137
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