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

Last change on this file since 57879 was 55256, checked in by [email protected], 15 years ago

2010-02-25 Oliver Hunt <[email protected]>

Reviewed by Maciej Stachowiak.

Race condition in JSPropertyNameIterator and Structure destruction
https://bugs.webkit.org/show_bug.cgi?id=35398

JSPropertyNameIterator and Structure have a cyclic dependency that they
manage by clearing the appropriate reference in each other during their
destruction. However if the Structure is destroyed while the
JSPropertyNameIterator is dead but not yet finalized the Structures
WeakGCPtr will return null, and so prevent Structure from clearing
the m_cachedStructure pointer of the iterator. When the iterator is
then finalised the m_cachedStructure is invalid, and the attempt to
clear the structures back reference fails.

To fix this we simply make JSPropertyNameIterator keep the Structure
alive, using the weak pointer to break the ref cycle.

  • runtime/JSPropertyNameIterator.cpp: (JSC::JSPropertyNameIterator::~JSPropertyNameIterator): The iterator now keeps m_cachedStructure alive itself, so no longer needs to check for it being cleared
  • runtime/JSPropertyNameIterator.h: (JSC::JSPropertyNameIterator::setCachedStructure): Add an assertion to ensure correct usage (JSC::JSPropertyNameIterator::cachedStructure): Add .get()
  • runtime/Structure.cpp: (JSC::Structure::~Structure): Add an assertion that our iterator isn't already dead, and remove the now unnecessary attempt to clear the ref in the iterator
  • runtime/WeakGCPtr.h: (JSC::WeakGCPtr::hasDeadObject): An assert-only function to allow us to assert correct behaviour in the Structure destructor

2010-02-25 Oliver Hunt <[email protected]>

Reviewed by Maciej Stachowiak.

Race condition in JSPropertyNameIterator and Structure destruction
https://bugs.webkit.org/show_bug.cgi?id=35398

Add test to ensure that this race condition doesn't occur.

  • fast/js/script-tests/for-in-cached.js: (cacheClearing):
File size: 43.3 KB
Line 
1/*
2 * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "Structure.h"
28
29#include "Identifier.h"
30#include "JSObject.h"
31#include "JSPropertyNameIterator.h"
32#include "Lookup.h"
33#include "PropertyNameArray.h"
34#include "StructureChain.h"
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)
69static Mutex& ignoreSetMutex = *(new Mutex);
70#endif
71
72static bool shouldIgnoreLeaks;
73static HashSet<Structure*>& ignoreSet = *(new HashSet<Structure*>);
74#endif
75
76#if DUMP_STRUCTURE_ID_STATISTICS
77static HashSet<Structure*>& liveStructureSet = *(new HashSet<Structure*>);
78#endif
79
80static int comparePropertyMapEntryIndices(const void* a, const void* b);
81
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, 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(0, structure));
179 }
180}
181
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
226Structure::Structure(JSValue prototype, const TypeInfo& typeInfo, unsigned anonymousSlotCount)
227 : m_typeInfo(typeInfo)
228 , m_prototype(prototype)
229 , m_specificValueInPrevious(0)
230 , m_propertyTable(0)
231 , m_propertyStorageCapacity(JSObject::inlineStorageCapacity)
232 , m_offset(noOffset)
233 , m_dictionaryKind(NoneDictionaryKind)
234 , m_isPinnedPropertyTable(false)
235 , m_hasGetterSetterProperties(false)
236 , m_attributesInPrevious(0)
237 , m_specificFunctionThrashCount(0)
238 , m_anonymousSlotCount(anonymousSlotCount)
239 , m_isUsingSingleSlot(true)
240{
241 m_transitions.m_singleTransition = 0;
242
243 ASSERT(m_prototype);
244 ASSERT(m_prototype.isObject() || m_prototype.isNull());
245
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) {
264 ASSERT(m_nameInPrevious);
265 m_previous->transitionTableRemove(make_pair(m_nameInPrevious.get(), m_attributesInPrevious), m_specificValueInPrevious);
266
267 }
268 ASSERT(!m_enumerationCache.hasDeadObject());
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
281 if (!m_isUsingSingleSlot)
282 delete transitionTable();
283
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();
383 PropertyMapEntry entry(structure->m_nameInPrevious.get(), m_anonymousSlotCount + structure->m_offset, structure->m_attributesInPrevious, structure->m_specificValueInPrevious, ++m_propertyTable->lastIndexUsed);
384 insertIntoPropertyMapHashTable(entry);
385 }
386}
387
388void Structure::growPropertyStorageCapacity()
389{
390 if (m_propertyStorageCapacity == JSObject::inlineStorageCapacity)
391 m_propertyStorageCapacity = JSObject::nonInlineBaseStorageCapacity;
392 else
393 m_propertyStorageCapacity *= 2;
394}
395
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
405 unsigned i = rep->existingHash();
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
423 unsigned k = 1 | doubleHash(rep->existingHash());
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)
443{
444 ASSERT(!structure->isDictionary());
445 ASSERT(structure->typeInfo().type() == ObjectType);
446
447 if (Structure* existingTransition = structure->transitionTableGet(make_pair(propertyName.ustring().rep(), attributes), specificValue)) {
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;
453 }
454
455 return 0;
456}
457
458PassRefPtr<Structure> Structure::addPropertyTransition(Structure* structure, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset)
459{
460 ASSERT(!structure->isDictionary());
461 ASSERT(structure->typeInfo().type() == ObjectType);
462 ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure, propertyName, attributes, specificValue, offset));
463
464 if (structure->m_specificFunctionThrashCount == maxSpecificFunctionThrashCount)
465 specificValue = 0;
466
467 if (structure->transitionCount() > s_maxTransitionLength) {
468 RefPtr<Structure> transition = toCacheableDictionaryTransition(structure);
469 ASSERT(structure != transition);
470 offset = transition->put(propertyName, attributes, specificValue);
471 ASSERT(offset >= structure->m_anonymousSlotCount);
472 ASSERT(structure->m_anonymousSlotCount == transition->m_anonymousSlotCount);
473 if (transition->propertyStorageSize() > transition->propertyStorageCapacity())
474 transition->growPropertyStorageCapacity();
475 return transition.release();
476 }
477
478 RefPtr<Structure> transition = create(structure->m_prototype, structure->typeInfo(), structure->anonymousSlotCount());
479
480 transition->m_cachedPrototypeChain = structure->m_cachedPrototypeChain;
481 transition->m_previous = structure;
482 transition->m_nameInPrevious = propertyName.ustring().rep();
483 transition->m_attributesInPrevious = attributes;
484 transition->m_specificValueInPrevious = specificValue;
485 transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
486 transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties;
487 transition->m_hasNonEnumerableProperties = structure->m_hasNonEnumerableProperties;
488 transition->m_specificFunctionThrashCount = structure->m_specificFunctionThrashCount;
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
504 offset = transition->put(propertyName, attributes, specificValue);
505 ASSERT(offset >= structure->m_anonymousSlotCount);
506 ASSERT(structure->m_anonymousSlotCount == transition->m_anonymousSlotCount);
507 if (transition->propertyStorageSize() > transition->propertyStorageCapacity())
508 transition->growPropertyStorageCapacity();
509
510 transition->m_offset = offset - structure->m_anonymousSlotCount;
511 ASSERT(structure->anonymousSlotCount() == transition->anonymousSlotCount());
512 structure->transitionTableAdd(make_pair(propertyName.ustring().rep(), attributes), transition.get(), specificValue);
513 return transition.release();
514}
515
516PassRefPtr<Structure> Structure::removePropertyTransition(Structure* structure, const Identifier& propertyName, size_t& offset)
517{
518 ASSERT(!structure->isUncacheableDictionary());
519
520 RefPtr<Structure> transition = toUncacheableDictionaryTransition(structure);
521
522 offset = transition->remove(propertyName);
523 ASSERT(offset >= structure->m_anonymousSlotCount);
524 ASSERT(structure->m_anonymousSlotCount == transition->m_anonymousSlotCount);
525
526 return transition.release();
527}
528
529PassRefPtr<Structure> Structure::changePrototypeTransition(Structure* structure, JSValue prototype)
530{
531 RefPtr<Structure> transition = create(prototype, structure->typeInfo(), structure->anonymousSlotCount());
532
533 transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
534 transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties;
535 transition->m_hasNonEnumerableProperties = structure->m_hasNonEnumerableProperties;
536 transition->m_specificFunctionThrashCount = structure->m_specificFunctionThrashCount;
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;
543
544 ASSERT(structure->anonymousSlotCount() == transition->anonymousSlotCount());
545 return transition.release();
546}
547
548PassRefPtr<Structure> Structure::despecifyFunctionTransition(Structure* structure, const Identifier& replaceFunction)
549{
550 ASSERT(structure->m_specificFunctionThrashCount < maxSpecificFunctionThrashCount);
551 RefPtr<Structure> transition = create(structure->storedPrototype(), structure->typeInfo(), structure->anonymousSlotCount());
552
553 transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
554 transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties;
555 transition->m_hasNonEnumerableProperties = structure->m_hasNonEnumerableProperties;
556 transition->m_specificFunctionThrashCount = structure->m_specificFunctionThrashCount + 1;
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
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());
572 return transition.release();
573}
574
575PassRefPtr<Structure> Structure::getterSetterTransition(Structure* structure)
576{
577 RefPtr<Structure> transition = create(structure->storedPrototype(), structure->typeInfo(), structure->anonymousSlotCount());
578 transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
579 transition->m_hasGetterSetterProperties = transition->m_hasGetterSetterProperties;
580 transition->m_hasNonEnumerableProperties = structure->m_hasNonEnumerableProperties;
581 transition->m_specificFunctionThrashCount = structure->m_specificFunctionThrashCount;
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;
588
589 ASSERT(structure->anonymousSlotCount() == transition->anonymousSlotCount());
590 return transition.release();
591}
592
593PassRefPtr<Structure> Structure::toDictionaryTransition(Structure* structure, DictionaryKind kind)
594{
595 ASSERT(!structure->isUncacheableDictionary());
596
597 RefPtr<Structure> transition = create(structure->m_prototype, structure->typeInfo(), structure->anonymousSlotCount());
598 transition->m_dictionaryKind = kind;
599 transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity;
600 transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties;
601 transition->m_hasNonEnumerableProperties = structure->m_hasNonEnumerableProperties;
602 transition->m_specificFunctionThrashCount = structure->m_specificFunctionThrashCount;
603
604 structure->materializePropertyMapIfNecessary();
605 transition->m_propertyTable = structure->copyPropertyTable();
606 transition->m_isPinnedPropertyTable = true;
607
608 ASSERT(structure->anonymousSlotCount() == transition->anonymousSlotCount());
609 return transition.release();
610}
611
612PassRefPtr<Structure> Structure::toCacheableDictionaryTransition(Structure* structure)
613{
614 return toDictionaryTransition(structure, CachedDictionaryKind);
615}
616
617PassRefPtr<Structure> Structure::toUncacheableDictionaryTransition(Structure* structure)
618{
619 return toDictionaryTransition(structure, UncachedDictionaryKind);
620}
621
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);
642 unsigned anonymousSlotCount = m_anonymousSlotCount;
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 }
660
661 m_dictionaryKind = NoneDictionaryKind;
662 return this;
663}
664
665size_t Structure::addPropertyWithoutTransition(const Identifier& propertyName, unsigned attributes, JSCell* specificValue)
666{
667 ASSERT(!m_enumerationCache);
668
669 if (m_specificFunctionThrashCount == maxSpecificFunctionThrashCount)
670 specificValue = 0;
671
672 materializePropertyMapIfNecessary();
673
674 m_isPinnedPropertyTable = true;
675
676 size_t offset = put(propertyName, attributes, specificValue);
677 ASSERT(offset >= m_anonymousSlotCount);
678 if (propertyStorageSize() > propertyStorageCapacity())
679 growPropertyStorageCapacity();
680 return offset;
681}
682
683size_t Structure::removePropertyWithoutTransition(const Identifier& propertyName)
684{
685 ASSERT(isUncacheableDictionary());
686 ASSERT(!m_enumerationCache);
687
688 materializePropertyMapIfNecessary();
689
690 m_isPinnedPropertyTable = true;
691 size_t offset = remove(propertyName);
692 ASSERT(offset >= m_anonymousSlotCount);
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
752size_t Structure::get(const UString::Rep* rep, unsigned& attributes, JSCell*& specificValue)
753{
754 materializePropertyMapIfNecessary();
755 if (!m_propertyTable)
756 return notFound;
757
758 unsigned i = rep->existingHash();
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;
770 specificValue = m_propertyTable->entries()[entryIndex - 1].specificValue;
771 ASSERT(m_propertyTable->entries()[entryIndex - 1].offset >= m_anonymousSlotCount);
772 return m_propertyTable->entries()[entryIndex - 1].offset;
773 }
774
775#if DUMP_PROPERTYMAP_STATS
776 ++numCollisions;
777#endif
778
779 unsigned k = 1 | doubleHash(rep->existingHash());
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;
794 specificValue = m_propertyTable->entries()[entryIndex - 1].specificValue;
795 ASSERT(m_propertyTable->entries()[entryIndex - 1].offset >= m_anonymousSlotCount);
796 return m_propertyTable->entries()[entryIndex - 1].offset;
797 }
798 }
799}
800
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
811 unsigned i = rep->existingHash();
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
831 unsigned k = 1 | doubleHash(rep->existingHash());
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
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
863size_t Structure::put(const Identifier& propertyName, unsigned attributes, JSCell* specificValue)
864{
865 ASSERT(!propertyName.isNull());
866 ASSERT(get(propertyName) == notFound);
867
868 checkConsistency();
869
870 if (attributes & DontEnum)
871 m_hasNonEnumerableProperties = true;
872
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
880 unsigned i = rep->existingHash();
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) {
903 k = 1 | doubleHash(rep->existingHash());
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;
936 m_propertyTable->entries()[entryIndex - 1].specificValue = specificValue;
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
944 newOffset = m_propertyTable->keyCount + m_anonymousSlotCount;
945 m_propertyTable->entries()[entryIndex - 1].offset = newOffset;
946
947 ASSERT(newOffset >= m_anonymousSlotCount);
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
957bool Structure::hasTransition(UString::Rep* rep, unsigned attributes)
958{
959 return transitionTableHasTransition(make_pair(rep, attributes));
960}
961
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.
979 unsigned i = rep->existingHash();
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) {
993 k = 1 | doubleHash(rep->existingHash());
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;
1011 ASSERT(offset >= m_anonymousSlotCount);
1012
1013 key->deref();
1014 m_propertyTable->entries()[entryIndex - 1].key = 0;
1015 m_propertyTable->entries()[entryIndex - 1].attributes = 0;
1016 m_propertyTable->entries()[entryIndex - 1].specificValue = 0;
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);
1037 ASSERT(entry.offset >= m_anonymousSlotCount);
1038 unsigned i = entry.key->existingHash();
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) {
1051 k = 1 | doubleHash(entry.key->existingHash());
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
1133int comparePropertyMapEntryIndices(const void* a, const void* b)
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
1144void Structure::getPropertyNames(PropertyNameArray& propertyNames, EnumerationMode mode)
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++) {
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))) {
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++) {
1183 if (m_propertyTable->entries()[i].key && (!(m_propertyTable->entries()[i].attributes & DontEnum) || (mode == IncludeDontEnumProperties)))
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
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) {
1243 ASSERT(m_hasNonEnumerableProperties || !(m_propertyTable->entries()[c].attributes & DontEnum));
1244 UString::Rep* rep = m_propertyTable->entries()[c].key;
1245 ASSERT(m_propertyTable->entries()[c].offset >= m_anonymousSlotCount);
1246 if (!rep)
1247 continue;
1248 ++nonEmptyEntryCount;
1249 unsigned i = rep->existingHash();
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)
1258 k = 1 | doubleHash(rep->existingHash());
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
Note: See TracBrowser for help on using the repository browser.