File indexing completed on 2024-05-12 15:43:32
0001 /* 0002 * This file is part of the KDE libraries 0003 * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. 0004 * 0005 * This library is free software; you can redistribute it and/or 0006 * modify it under the terms of the GNU Library General Public 0007 * License as published by the Free Software Foundation; either 0008 * version 2 of the License, or (at your option) any later version. 0009 * 0010 * This library is distributed in the hope that it will be useful, 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0013 * Library General Public License for more details. 0014 * 0015 * You should have received a copy of the GNU Library General Public License 0016 * along with this library; see the file COPYING.LIB. If not, write to 0017 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0018 * Boston, MA 02110-1301, USA. 0019 * 0020 */ 0021 0022 #include "property_map.h" 0023 0024 #include "object.h" 0025 #include "protect.h" 0026 #include "PropertyNameArray.h" 0027 #include <algorithm> 0028 #include <wtf/FastMalloc.h> 0029 #include <wtf/Vector.h> 0030 0031 using std::max; 0032 0033 #define DEBUG_PROPERTIES 0 0034 #define DO_CONSISTENCY_CHECK 0 0035 #define DUMP_STATISTICS 0 0036 #define USE_SINGLE_ENTRY 1 0037 0038 // 2/28/2006 ggaren: super accurate JS iBench says that USE_SINGLE_ENTRY is a 0039 // 3.2% performance boost. 0040 0041 #if !DO_CONSISTENCY_CHECK 0042 #define checkConsistency() ((void)0) 0043 #endif 0044 0045 namespace KJS 0046 { 0047 0048 // Choose a number for the following so that most property maps are smaller, 0049 // but it's not going to blow out the stack to allocate this number of pointers. 0050 const int smallMapThreshold = 1024; 0051 0052 #if DUMP_STATISTICS 0053 0054 static int numProbes; 0055 static int numCollisions; 0056 static int numRehashes; 0057 static int numRemoves; 0058 0059 struct PropertyMapStatisticsExitLogger { 0060 ~PropertyMapStatisticsExitLogger(); 0061 }; 0062 0063 static PropertyMapStatisticsExitLogger logger; 0064 0065 PropertyMapStatisticsExitLogger::~PropertyMapStatisticsExitLogger() 0066 { 0067 printf("\nKJS::PropertyMap statistics\n\n"); 0068 printf("%d probes\n", numProbes); 0069 printf("%d collisions (%.1f%%)\n", numCollisions, 100.0 * numCollisions / numProbes); 0070 printf("%d rehashes\n", numRehashes); 0071 printf("%d removes\n", numRemoves); 0072 } 0073 0074 #endif 0075 0076 // lastIndexUsed is an ever-increasing index used to identify the order items 0077 // were inserted into the property map. It's vital that getEnumerablePropertyNames 0078 // return the properties in the order they were added for compatibility with other 0079 // browsers' JavaScript implementations. 0080 struct PropertyMapHashTable { 0081 int sizeMask; 0082 int size; 0083 int keyCount; 0084 int sentinelCount; 0085 int lastIndexUsed; 0086 PropertyMapHashTableEntry entries[1]; 0087 }; 0088 0089 class SavedProperty 0090 { 0091 public: 0092 Identifier key; 0093 ProtectedPtr<JSValue> value; 0094 int attributes; 0095 }; 0096 0097 SavedProperties::SavedProperties() : _count(0) { } 0098 SavedProperties::~SavedProperties() { } 0099 0100 // Algorithm concepts from Algorithms in C++, Sedgewick. 0101 0102 // This is a method rather than a variable to work around <rdar://problem/4462053> 0103 static inline UString::Rep *deletedSentinel() 0104 { 0105 return reinterpret_cast<UString::Rep *>(0x1); 0106 } 0107 0108 // Returns true if the key is not null or the deleted sentinel, false otherwise 0109 static inline bool isValid(UString::Rep *key) 0110 { 0111 return !!(reinterpret_cast<uintptr_t>(key) & ~0x1); 0112 } 0113 0114 PropertyMap::~PropertyMap() 0115 { 0116 if (!m_usingTable) { 0117 #if USE_SINGLE_ENTRY 0118 UString::Rep *key = m_singleEntryKey; 0119 if (key) { 0120 key->deref(); 0121 } 0122 #endif 0123 return; 0124 } 0125 0126 int minimumKeysToProcess = m_u.table->keyCount + m_u.table->sentinelCount; 0127 Entry *entries = m_u.table->entries; 0128 for (int i = 0; i < minimumKeysToProcess; i++) { 0129 UString::Rep *key = entries[i].key; 0130 if (key) { 0131 if (key != deletedSentinel()) { 0132 key->deref(); 0133 } 0134 } else { 0135 ++minimumKeysToProcess; 0136 } 0137 } 0138 fastFree(m_u.table); 0139 } 0140 0141 void PropertyMap::clear() 0142 { 0143 if (!m_usingTable) { 0144 #if USE_SINGLE_ENTRY 0145 UString::Rep *key = m_singleEntryKey; 0146 if (key) { 0147 key->deref(); 0148 m_singleEntryKey = nullptr; 0149 } 0150 #endif 0151 return; 0152 } 0153 0154 int size = m_u.table->size; 0155 Entry *entries = m_u.table->entries; 0156 for (int i = 0; i < size; i++) { 0157 UString::Rep *key = entries[i].key; 0158 if (isValid(key)) { 0159 key->deref(); 0160 entries[i].key = nullptr; 0161 entries[i].value = nullptr; 0162 } 0163 } 0164 m_u.table->keyCount = 0; 0165 m_u.table->sentinelCount = 0; 0166 } 0167 0168 bool PropertyMap::isEmpty() const 0169 { 0170 if (!m_usingTable) { 0171 return !m_singleEntryKey; 0172 } else { 0173 return !m_u.table->keyCount; 0174 } 0175 } 0176 0177 JSValue *PropertyMap::get(const Identifier &name, unsigned &attributes) const 0178 { 0179 assert(!name.isNull()); 0180 0181 UString::Rep *rep = name._ustring.rep(); 0182 0183 if (!m_usingTable) { 0184 #if USE_SINGLE_ENTRY 0185 UString::Rep *key = m_singleEntryKey; 0186 if (rep == key) { 0187 attributes = m_singleEntryAttributes; 0188 return m_u.singleEntryValue; 0189 } 0190 #endif 0191 return nullptr; 0192 } 0193 0194 unsigned h = rep->hash(); 0195 int sizeMask = m_u.table->sizeMask; 0196 Entry *entries = m_u.table->entries; 0197 int i = h & sizeMask; 0198 int k = 0; 0199 #if DUMP_STATISTICS 0200 ++numProbes; 0201 numCollisions += entries[i].key && entries[i].key != rep; 0202 #endif 0203 while (UString::Rep *key = entries[i].key) { 0204 if (rep == key) { 0205 attributes = entries[i].attributes; 0206 return entries[i].value; 0207 } 0208 if (k == 0) { 0209 k = 1 | (h % sizeMask); 0210 } 0211 i = (i + k) & sizeMask; 0212 #if DUMP_STATISTICS 0213 ++numRehashes; 0214 #endif 0215 } 0216 return nullptr; 0217 } 0218 0219 JSValue *PropertyMap::get(const Identifier &name) const 0220 { 0221 assert(!name.isNull()); 0222 0223 UString::Rep *rep = name._ustring.rep(); 0224 0225 if (!m_usingTable) { 0226 #if USE_SINGLE_ENTRY 0227 UString::Rep *key = m_singleEntryKey; 0228 if (rep == key) { 0229 return m_u.singleEntryValue; 0230 } 0231 #endif 0232 return nullptr; 0233 } 0234 0235 unsigned h = rep->hash(); 0236 int sizeMask = m_u.table->sizeMask; 0237 Entry *entries = m_u.table->entries; 0238 int i = h & sizeMask; 0239 int k = 0; 0240 #if DUMP_STATISTICS 0241 ++numProbes; 0242 numCollisions += entries[i].key && entries[i].key != rep; 0243 #endif 0244 while (UString::Rep *key = entries[i].key) { 0245 if (rep == key) { 0246 return entries[i].value; 0247 } 0248 if (k == 0) { 0249 k = 1 | (h % sizeMask); 0250 } 0251 i = (i + k) & sizeMask; 0252 #if DUMP_STATISTICS 0253 ++numRehashes; 0254 #endif 0255 } 0256 return nullptr; 0257 } 0258 0259 JSValue **PropertyMap::getLocation(const Identifier &name) 0260 { 0261 assert(!name.isNull()); 0262 0263 UString::Rep *rep = name._ustring.rep(); 0264 0265 if (!m_usingTable) { 0266 #if USE_SINGLE_ENTRY 0267 UString::Rep *key = m_singleEntryKey; 0268 if (rep == key) { 0269 return &m_u.singleEntryValue; 0270 } 0271 #endif 0272 return nullptr; 0273 } 0274 0275 unsigned h = rep->hash(); 0276 int sizeMask = m_u.table->sizeMask; 0277 Entry *entries = m_u.table->entries; 0278 int i = h & sizeMask; 0279 int k = 0; 0280 #if DUMP_STATISTICS 0281 ++numProbes; 0282 numCollisions += entries[i].key && entries[i].key != rep; 0283 #endif 0284 while (UString::Rep *key = entries[i].key) { 0285 if (rep == key) { 0286 return &entries[i].value; 0287 } 0288 if (k == 0) { 0289 k = 1 | (h % sizeMask); 0290 } 0291 i = (i + k) & sizeMask; 0292 #if DUMP_STATISTICS 0293 ++numRehashes; 0294 #endif 0295 } 0296 return nullptr; 0297 } 0298 0299 JSValue **PropertyMap::getWriteLocation(const Identifier &name) 0300 { 0301 assert(!name.isNull()); 0302 0303 UString::Rep *rep = name._ustring.rep(); 0304 0305 if (!m_usingTable) { 0306 UString::Rep *key = m_singleEntryKey; 0307 if (rep == key && !(m_singleEntryAttributes & (ReadOnly | GetterSetter))) { 0308 return &m_u.singleEntryValue; 0309 } 0310 return nullptr; 0311 } 0312 0313 unsigned h = rep->hash(); 0314 int sizeMask = m_u.table->sizeMask; 0315 Entry *entries = m_u.table->entries; 0316 int i = h & sizeMask; 0317 int k = 0; 0318 #if DUMP_STATISTICS 0319 ++numProbes; 0320 numCollisions += entries[i].key && entries[i].key != rep; 0321 #endif 0322 while (UString::Rep *key = entries[i].key) { 0323 if (rep == key) { 0324 if (entries[i].attributes & (ReadOnly | GetterSetter)) { 0325 return nullptr; 0326 } else { 0327 return &entries[i].value; 0328 } 0329 } 0330 if (k == 0) { 0331 k = 1 | (h % sizeMask); 0332 } 0333 i = (i + k) & sizeMask; 0334 #if DUMP_STATISTICS 0335 ++numRehashes; 0336 #endif 0337 } 0338 return nullptr; 0339 } 0340 0341 #if DEBUG_PROPERTIES 0342 static void printAttributes(int attributes) 0343 { 0344 if (attributes == 0) { 0345 printf("None"); 0346 } else { 0347 if (attributes & ReadOnly) { 0348 printf("ReadOnly "); 0349 } 0350 if (attributes & DontEnum) { 0351 printf("DontEnum "); 0352 } 0353 if (attributes & DontDelete) { 0354 printf("DontDelete "); 0355 } 0356 if (attributes & Internal) { 0357 printf("Internal "); 0358 } 0359 if (attributes & Function) { 0360 printf("Function "); 0361 } 0362 } 0363 } 0364 #endif 0365 0366 void PropertyMap::put(const Identifier &name, JSValue *value, int attributes, bool roCheck) 0367 { 0368 assert(!name.isNull()); 0369 assert(value != nullptr); 0370 0371 checkConsistency(); 0372 0373 UString::Rep *rep = name._ustring.rep(); 0374 0375 #if DEBUG_PROPERTIES 0376 printf("adding property %s, attributes = 0x%08x (", name.ascii(), attributes); 0377 printAttributes(attributes); 0378 printf(")\n"); 0379 #endif 0380 0381 #if USE_SINGLE_ENTRY 0382 if (!m_usingTable) { 0383 UString::Rep *key = m_singleEntryKey; 0384 if (key) { 0385 if (rep == key && !(roCheck && (m_singleEntryAttributes & ReadOnly))) { 0386 m_u.singleEntryValue = value; 0387 return; 0388 } 0389 } else { 0390 rep->ref(); 0391 m_singleEntryKey = rep; 0392 m_u.singleEntryValue = value; 0393 m_singleEntryAttributes = static_cast<short>(attributes); 0394 checkConsistency(); 0395 return; 0396 } 0397 } 0398 #endif 0399 0400 if (!m_usingTable || m_u.table->keyCount * 2 >= m_u.table->size) { 0401 expand(); 0402 } 0403 0404 unsigned h = rep->hash(); 0405 int sizeMask = m_u.table->sizeMask; 0406 Entry *entries = m_u.table->entries; 0407 int i = h & sizeMask; 0408 int k = 0; 0409 bool foundDeletedElement = false; 0410 int deletedElementIndex = 0; /* initialize to make the compiler happy */ 0411 #if DUMP_STATISTICS 0412 ++numProbes; 0413 numCollisions += entries[i].key && entries[i].key != rep; 0414 #endif 0415 while (UString::Rep *key = entries[i].key) { 0416 if (rep == key) { 0417 if (roCheck && (entries[i].attributes & ReadOnly)) { 0418 return; 0419 } 0420 // Put a new value in an existing hash table entry. 0421 entries[i].value = value; 0422 // Attributes are intentionally not updated. 0423 return; 0424 } 0425 // If we find the deleted-element sentinel, remember it for use later. 0426 if (key == deletedSentinel() && !foundDeletedElement) { 0427 foundDeletedElement = true; 0428 deletedElementIndex = i; 0429 } 0430 if (k == 0) { 0431 k = 1 | (h % sizeMask); 0432 } 0433 i = (i + k) & sizeMask; 0434 #if DUMP_STATISTICS 0435 ++numRehashes; 0436 #endif 0437 } 0438 0439 // Use either the deleted element or the 0 at the end of the chain. 0440 if (foundDeletedElement) { 0441 i = deletedElementIndex; 0442 --m_u.table->sentinelCount; 0443 } 0444 0445 // Create a new hash table entry. 0446 rep->ref(); 0447 entries[i].key = rep; 0448 entries[i].value = value; 0449 entries[i].attributes = attributes; 0450 entries[i].index = ++m_u.table->lastIndexUsed; 0451 ++m_u.table->keyCount; 0452 0453 checkConsistency(); 0454 } 0455 0456 void PropertyMap::insert(UString::Rep *key, JSValue *value, int attributes, int index) 0457 { 0458 assert(m_u.table); 0459 0460 unsigned h = key->hash(); 0461 int sizeMask = m_u.table->sizeMask; 0462 Entry *entries = m_u.table->entries; 0463 int i = h & sizeMask; 0464 int k = 0; 0465 #if DUMP_STATISTICS 0466 ++numProbes; 0467 numCollisions += entries[i].key && entries[i].key != key; 0468 #endif 0469 while (entries[i].key) { 0470 assert(entries[i].key != deletedSentinel()); 0471 if (k == 0) { 0472 k = 1 | (h % sizeMask); 0473 } 0474 i = (i + k) & sizeMask; 0475 #if DUMP_STATISTICS 0476 ++numRehashes; 0477 #endif 0478 } 0479 0480 entries[i].key = key; 0481 entries[i].value = value; 0482 entries[i].attributes = attributes; 0483 entries[i].index = index; 0484 } 0485 0486 void PropertyMap::expand() 0487 { 0488 if (!m_usingTable) { 0489 createTable(); 0490 } else { 0491 rehash(m_u.table->size * 2); 0492 } 0493 } 0494 0495 void PropertyMap::rehash() 0496 { 0497 ASSERT(m_usingTable); 0498 ASSERT(m_u.table); 0499 ASSERT(m_u.table->size); 0500 rehash(m_u.table->size); 0501 } 0502 0503 void PropertyMap::createTable() 0504 { 0505 const int newTableSize = 16; 0506 0507 ASSERT(!m_usingTable); 0508 0509 checkConsistency(); 0510 0511 #if USE_SINGLE_ENTRY 0512 JSValue *oldSingleEntryValue = m_u.singleEntryValue; 0513 #endif 0514 0515 m_u.table = (Table *)fastCalloc(1, sizeof(Table) + (newTableSize - 1) * sizeof(Entry)); 0516 m_u.table->size = newTableSize; 0517 m_u.table->sizeMask = newTableSize - 1; 0518 m_usingTable = true; 0519 0520 #if USE_SINGLE_ENTRY 0521 UString::Rep *key = m_singleEntryKey; 0522 if (key) { 0523 insert(key, oldSingleEntryValue, m_singleEntryAttributes, 0); 0524 m_singleEntryKey = nullptr; 0525 m_u.table->keyCount = 1; 0526 } 0527 #endif 0528 0529 checkConsistency(); 0530 } 0531 0532 void PropertyMap::rehash(int newTableSize) 0533 { 0534 ASSERT(!m_singleEntryKey); 0535 ASSERT(m_u.table); 0536 ASSERT(m_usingTable); 0537 0538 checkConsistency(); 0539 0540 Table *oldTable = m_u.table; 0541 int oldTableSize = oldTable->size; 0542 int oldTableKeyCount = oldTable->keyCount; 0543 0544 m_u.table = (Table *)fastCalloc(1, sizeof(Table) + (newTableSize - 1) * sizeof(Entry)); 0545 m_u.table->size = newTableSize; 0546 m_u.table->sizeMask = newTableSize - 1; 0547 m_u.table->keyCount = oldTableKeyCount; 0548 0549 int lastIndexUsed = 0; 0550 for (int i = 0; i != oldTableSize; ++i) { 0551 Entry &entry = oldTable->entries[i]; 0552 UString::Rep *key = entry.key; 0553 if (isValid(key)) { 0554 int index = entry.index; 0555 lastIndexUsed = max(index, lastIndexUsed); 0556 insert(key, entry.value, entry.attributes, index); 0557 } 0558 } 0559 m_u.table->lastIndexUsed = lastIndexUsed; 0560 0561 fastFree(oldTable); 0562 0563 checkConsistency(); 0564 } 0565 0566 void PropertyMap::remove(const Identifier &name) 0567 { 0568 assert(!name.isNull()); 0569 0570 checkConsistency(); 0571 0572 UString::Rep *rep = name._ustring.rep(); 0573 0574 UString::Rep *key; 0575 0576 if (!m_usingTable) { 0577 #if USE_SINGLE_ENTRY 0578 key = m_singleEntryKey; 0579 if (rep == key) { 0580 key->deref(); 0581 m_singleEntryKey = nullptr; 0582 checkConsistency(); 0583 } 0584 #endif 0585 return; 0586 } 0587 0588 // Find the thing to remove. 0589 unsigned h = rep->hash(); 0590 int sizeMask = m_u.table->sizeMask; 0591 Entry *entries = m_u.table->entries; 0592 int i = h & sizeMask; 0593 int k = 0; 0594 #if DUMP_STATISTICS 0595 ++numProbes; 0596 ++numRemoves; 0597 numCollisions += entries[i].key && entries[i].key != rep; 0598 #endif 0599 while ((key = entries[i].key)) { 0600 if (rep == key) { 0601 break; 0602 } 0603 if (k == 0) { 0604 k = 1 | (h % sizeMask); 0605 } 0606 i = (i + k) & sizeMask; 0607 #if DUMP_STATISTICS 0608 ++numRehashes; 0609 #endif 0610 } 0611 if (!key) { 0612 return; 0613 } 0614 0615 // Replace this one element with the deleted sentinel. Also set value to 0 and attributes to DontEnum 0616 // to help callers that iterate all keys not have to check for the sentinel. 0617 key->deref(); 0618 key = deletedSentinel(); 0619 entries[i].key = key; 0620 entries[i].value = nullptr; 0621 entries[i].attributes = DontEnum; 0622 assert(m_u.table->keyCount >= 1); 0623 --m_u.table->keyCount; 0624 ++m_u.table->sentinelCount; 0625 0626 if (m_u.table->sentinelCount * 4 >= m_u.table->size) { 0627 rehash(); 0628 } 0629 0630 checkConsistency(); 0631 } 0632 0633 void PropertyMap::mark() const 0634 { 0635 if (!m_usingTable) { 0636 #if USE_SINGLE_ENTRY 0637 if (m_singleEntryKey) { 0638 JSValue *v = m_u.singleEntryValue; 0639 if (!JSValue::marked(v)) { 0640 JSValue::mark(v); 0641 } 0642 } 0643 #endif 0644 return; 0645 } 0646 0647 int minimumKeysToProcess = m_u.table->keyCount; 0648 Entry *entries = m_u.table->entries; 0649 for (int i = 0; i < minimumKeysToProcess; i++) { 0650 JSValue *v = entries[i].value; 0651 if (v) { 0652 if (!JSValue::marked(v)) { 0653 JSValue::mark(v); 0654 } 0655 } else { 0656 ++minimumKeysToProcess; 0657 } 0658 } 0659 } 0660 0661 static int comparePropertyMapEntryIndices(const void *a, const void *b) 0662 { 0663 int ia = static_cast<PropertyMapHashTableEntry *const *>(a)[0]->index; 0664 int ib = static_cast<PropertyMapHashTableEntry *const *>(b)[0]->index; 0665 if (ia < ib) { 0666 return -1; 0667 } 0668 if (ia > ib) { 0669 return +1; 0670 } 0671 return 0; 0672 } 0673 0674 bool PropertyMap::containsGettersOrSetters() const 0675 { 0676 if (!m_usingTable) { 0677 #if USE_SINGLE_ENTRY 0678 return !!(m_singleEntryAttributes & GetterSetter); 0679 #else 0680 return false; 0681 #endif 0682 } 0683 0684 for (int i = 0; i != m_u.table->size; ++i) { 0685 if (m_u.table->entries[i].attributes & GetterSetter) { 0686 return true; 0687 } 0688 } 0689 0690 return false; 0691 } 0692 0693 void PropertyMap::getPropertyNames(PropertyNameArray &propertyNames, PropertyMode mode) const 0694 { 0695 if (!m_usingTable) { 0696 #if USE_SINGLE_ENTRY 0697 UString::Rep *key = m_singleEntryKey; 0698 if (key && checkEnumerable(m_singleEntryAttributes, mode)) { 0699 propertyNames.add(Identifier(key)); 0700 } 0701 #endif 0702 return; 0703 } 0704 0705 // Allocate a buffer to use to sort the keys. 0706 Vector<Entry *, smallMapThreshold> sortedEnumerables(m_u.table->keyCount); 0707 0708 // Get pointers to the enumerable entries in the buffer. 0709 Entry **p = sortedEnumerables.data(); 0710 int size = m_u.table->size; 0711 Entry *entries = m_u.table->entries; 0712 for (int i = 0; i != size; ++i) { 0713 Entry *e = &entries[i]; 0714 if (e->key && checkEnumerable(e->attributes, mode)) { 0715 *p++ = e; 0716 } 0717 } 0718 0719 // Sort the entries by index. 0720 qsort(sortedEnumerables.data(), p - sortedEnumerables.data(), sizeof(Entry *), comparePropertyMapEntryIndices); 0721 0722 // Put the keys of the sorted entries into the list. 0723 for (Entry **q = sortedEnumerables.data(); q != p; ++q) { 0724 propertyNames.add(Identifier(q[0]->key)); 0725 } 0726 } 0727 0728 void PropertyMap::save(SavedProperties &p) const 0729 { 0730 int count = 0; 0731 0732 if (!m_usingTable) { 0733 #if USE_SINGLE_ENTRY 0734 if (m_singleEntryKey && !(m_singleEntryAttributes & (ReadOnly | Function))) { 0735 ++count; 0736 } 0737 #endif 0738 } else { 0739 int size = m_u.table->size; 0740 Entry *entries = m_u.table->entries; 0741 for (int i = 0; i != size; ++i) 0742 if (isValid(entries[i].key) && !(entries[i].attributes & (ReadOnly | Function))) { 0743 ++count; 0744 } 0745 } 0746 0747 p._properties.clear(); 0748 p._count = count; 0749 0750 if (count == 0) { 0751 return; 0752 } 0753 0754 p._properties.set(new SavedProperty [count]); 0755 0756 SavedProperty *prop = p._properties.get(); 0757 0758 if (!m_usingTable) { 0759 #if USE_SINGLE_ENTRY 0760 if (m_singleEntryKey && !(m_singleEntryAttributes & (ReadOnly | Function))) { 0761 prop->key = Identifier(m_singleEntryKey); 0762 prop->value = m_u.singleEntryValue; 0763 prop->attributes = m_singleEntryAttributes; 0764 ++prop; 0765 } 0766 #endif 0767 } else { 0768 // Save in the right order so we don't lose the order. 0769 // Another possibility would be to save the indices. 0770 0771 // Allocate a buffer to use to sort the keys. 0772 Vector<Entry *, smallMapThreshold> sortedEntries(count); 0773 0774 // Get pointers to the entries in the buffer. 0775 Entry **p = sortedEntries.data(); 0776 int size = m_u.table->size; 0777 Entry *entries = m_u.table->entries; 0778 for (int i = 0; i != size; ++i) { 0779 Entry *e = &entries[i]; 0780 if (isValid(e->key) && !(e->attributes & (ReadOnly | Function))) { 0781 *p++ = e; 0782 } 0783 } 0784 assert(p - sortedEntries.data() == count); 0785 0786 // Sort the entries by index. 0787 qsort(sortedEntries.data(), p - sortedEntries.data(), sizeof(Entry *), comparePropertyMapEntryIndices); 0788 0789 // Put the sorted entries into the saved properties list. 0790 for (Entry **q = sortedEntries.data(); q != p; ++q, ++prop) { 0791 Entry *e = *q; 0792 prop->key = Identifier(e->key); 0793 prop->value = e->value; 0794 prop->attributes = e->attributes; 0795 } 0796 } 0797 } 0798 0799 void PropertyMap::restore(const SavedProperties &p) 0800 { 0801 for (int i = 0; i != p._count; ++i) { 0802 put(p._properties[i].key, p._properties[i].value, p._properties[i].attributes); 0803 } 0804 } 0805 0806 #if DO_CONSISTENCY_CHECK 0807 0808 void PropertyMap::checkConsistency() 0809 { 0810 if (!m_usingTable) { 0811 return; 0812 } 0813 0814 int count = 0; 0815 int sentinelCount = 0; 0816 for (int j = 0; j != m_u.table->size; ++j) { 0817 UString::Rep *rep = m_u.table->entries[j].key; 0818 if (!rep) { 0819 continue; 0820 } 0821 if (rep == deletedSentinel()) { 0822 ++sentinelCount; 0823 continue; 0824 } 0825 unsigned h = rep->hash(); 0826 int i = h & m_u.table->sizeMask; 0827 int k = 0; 0828 while (UString::Rep *key = m_u.table->entries[i].key) { 0829 if (rep == key) { 0830 break; 0831 } 0832 if (k == 0) { 0833 k = 1 | (h % m_u.table->sizeMask); 0834 } 0835 i = (i + k) & m_u.table->sizeMask; 0836 } 0837 assert(i == j); 0838 ++count; 0839 } 0840 assert(count == m_u.table->keyCount); 0841 assert(sentinelCount == m_u.table->sentinelCount); 0842 assert(m_u.table->size >= 16); 0843 assert(m_u.table->sizeMask); 0844 assert(m_u.table->size == m_u.table->sizeMask + 1); 0845 } 0846 0847 #endif // DO_CONSISTENCY_CHECK 0848 0849 } // namespace KJS