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