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