source: webkit/trunk/JavaScriptCore/kjs/PropertyMap.cpp@ 37479

Last change on this file since 37479 was 37479, checked in by [email protected], 17 years ago

2008-10-10 Sam Weinig <[email protected]>

Reviewed by Cameron Zwarich.

Remove now unused m_getterSetterFlag variable from PropertyMap.

  • kjs/PropertyMap.cpp: (JSC::PropertyMap::operator=):
  • kjs/PropertyMap.h: (JSC::PropertyMap::PropertyMap):
  • Property svn:eol-style set to native
File size: 15.0 KB
Line 
1/*
2 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 *
19 */
20
21#include "config.h"
22#include "PropertyMap.h"
23
24#include "JSObject.h"
25#include "PropertyNameArray.h"
26#include "protect.h"
27#include <algorithm>
28#include <wtf/Assertions.h>
29#include <wtf/FastMalloc.h>
30#include <wtf/HashTable.h>
31#include <wtf/Vector.h>
32
33using std::max;
34using WTF::doubleHash;
35
36#ifndef NDEBUG
37#define DO_PROPERTYMAP_CONSTENCY_CHECK 0
38#else
39#define DO_PROPERTYMAP_CONSTENCY_CHECK 0
40#endif
41
42namespace JSC {
43
44// Choose a number for the following so that most property maps are smaller,
45// but it's not going to blow out the stack to allocate this number of pointers.
46static const int smallMapThreshold = 1024;
47
48// The point at which the function call overhead of the qsort implementation
49// becomes small compared to the inefficiency of insertion sort.
50static const unsigned tinyMapThreshold = 20;
51
52#if DUMP_PROPERTYMAP_STATS
53
54static int numProbes;
55static int numCollisions;
56static int numRehashes;
57static int numRemoves;
58
59struct PropertyMapStatisticsExitLogger {
60 ~PropertyMapStatisticsExitLogger();
61};
62
63static PropertyMapStatisticsExitLogger logger;
64
65PropertyMapStatisticsExitLogger::~PropertyMapStatisticsExitLogger()
66{
67 printf("\nJSC::PropertyMap statistics\n\n");
68 printf("%d probes\n", numProbes);
69 printf("%d collisions (%.1f%%)\n", numCollisions, 100.0 * numCollisions / numProbes);
70 printf("%d rehashes\n", numRehashes);
71 printf("%d removes\n", numRemoves);
72}
73
74#endif
75
76static const unsigned deletedSentinelIndex = 1;
77
78#if !DO_PROPERTYMAP_CONSTENCY_CHECK
79
80inline void PropertyMap::checkConsistency()
81{
82}
83
84#endif
85
86PropertyMap& PropertyMap::operator=(const PropertyMap& other)
87{
88 if (other.m_table) {
89 size_t tableSize = Table::allocationSize(other.m_table->size);
90 m_table = static_cast<Table*>(fastMalloc(tableSize));
91 memcpy(m_table, other.m_table, tableSize);
92
93 unsigned entryCount = m_table->keyCount + m_table->deletedSentinelCount;
94 for (unsigned i = 1; i <= entryCount; ++i) {
95 if (UString::Rep* key = m_table->entries()[i].key)
96 key->ref();
97 }
98 }
99
100 m_deletedOffsets = other.m_deletedOffsets;
101 return *this;
102}
103
104PropertyMap::~PropertyMap()
105{
106 if (!m_table)
107 return;
108
109 unsigned entryCount = m_table->keyCount + m_table->deletedSentinelCount;
110 for (unsigned i = 1; i <= entryCount; i++) {
111 if (UString::Rep* key = m_table->entries()[i].key)
112 key->deref();
113 }
114 fastFree(m_table);
115}
116
117size_t PropertyMap::put(const Identifier& propertyName, unsigned attributes)
118{
119 ASSERT(!propertyName.isNull());
120 ASSERT(get(propertyName) == WTF::notFound);
121
122 checkConsistency();
123
124 UString::Rep* rep = propertyName._ustring.rep();
125
126 if (!m_table)
127 createTable();
128
129 // FIXME: Consider a fast case for tables with no deleted sentinels.
130
131 unsigned i = rep->computedHash();
132 unsigned k = 0;
133 bool foundDeletedElement = false;
134 unsigned deletedElementIndex = 0; // initialize to make the compiler happy
135
136#if DUMP_PROPERTYMAP_STATS
137 ++numProbes;
138#endif
139
140 while (1) {
141 unsigned entryIndex = m_table->entryIndices[i & m_table->sizeMask];
142 if (entryIndex == emptyEntryIndex)
143 break;
144
145 if (entryIndex == deletedSentinelIndex) {
146 // If we find a deleted-element sentinel, remember it for use later.
147 if (!foundDeletedElement) {
148 foundDeletedElement = true;
149 deletedElementIndex = i;
150 }
151 }
152
153 if (k == 0) {
154 k = 1 | doubleHash(rep->computedHash());
155#if DUMP_PROPERTYMAP_STATS
156 ++numCollisions;
157#endif
158 }
159
160 i += k;
161
162#if DUMP_PROPERTYMAP_STATS
163 ++numRehashes;
164#endif
165 }
166
167 // Figure out which entry to use.
168 unsigned entryIndex = m_table->keyCount + m_table->deletedSentinelCount + 2;
169 if (foundDeletedElement) {
170 i = deletedElementIndex;
171 --m_table->deletedSentinelCount;
172
173 // Since we're not making the table bigger, we can't use the entry one past
174 // the end that we were planning on using, so search backwards for the empty
175 // slot that we can use. We know it will be there because we did at least one
176 // deletion in the past that left an entry empty.
177 while (m_table->entries()[--entryIndex - 1].key) { }
178 }
179
180 // Create a new hash table entry.
181 m_table->entryIndices[i & m_table->sizeMask] = entryIndex;
182
183 // Create a new hash table entry.
184 rep->ref();
185 m_table->entries()[entryIndex - 1].key = rep;
186 m_table->entries()[entryIndex - 1].attributes = attributes;
187 m_table->entries()[entryIndex - 1].index = ++m_table->lastIndexUsed;
188
189 unsigned newOffset;
190 if (!m_deletedOffsets.isEmpty()) {
191 newOffset = m_deletedOffsets.last();
192 m_deletedOffsets.removeLast();
193 } else
194 newOffset = m_table->keyCount;
195 m_table->entries()[entryIndex - 1].offset = newOffset;
196
197 ++m_table->keyCount;
198
199 if ((m_table->keyCount + m_table->deletedSentinelCount) * 2 >= m_table->size)
200 expand();
201
202 checkConsistency();
203 return newOffset;
204}
205
206size_t PropertyMap::remove(const Identifier& propertyName)
207{
208 ASSERT(!propertyName.isNull());
209
210 checkConsistency();
211
212 UString::Rep* rep = propertyName._ustring.rep();
213
214 if (!m_table)
215 return WTF::notFound;
216
217#if DUMP_PROPERTYMAP_STATS
218 ++numProbes;
219 ++numRemoves;
220#endif
221
222 // Find the thing to remove.
223 unsigned i = rep->computedHash();
224 unsigned k = 0;
225 unsigned entryIndex;
226 UString::Rep* key = 0;
227 while (1) {
228 entryIndex = m_table->entryIndices[i & m_table->sizeMask];
229 if (entryIndex == emptyEntryIndex)
230 return WTF::notFound;
231
232 key = m_table->entries()[entryIndex - 1].key;
233 if (rep == key)
234 break;
235
236 if (k == 0) {
237 k = 1 | doubleHash(rep->computedHash());
238#if DUMP_PROPERTYMAP_STATS
239 ++numCollisions;
240#endif
241 }
242
243 i += k;
244
245#if DUMP_PROPERTYMAP_STATS
246 ++numRehashes;
247#endif
248 }
249
250 // Replace this one element with the deleted sentinel. Also clear out
251 // the entry so we can iterate all the entries as needed.
252 m_table->entryIndices[i & m_table->sizeMask] = deletedSentinelIndex;
253
254 size_t offset = m_table->entries()[entryIndex - 1].offset;
255
256 key->deref();
257 m_table->entries()[entryIndex - 1].key = 0;
258 m_table->entries()[entryIndex - 1].attributes = 0;
259 m_table->entries()[entryIndex - 1].offset = 0;
260 m_deletedOffsets.append(offset);
261
262 ASSERT(m_table->keyCount >= 1);
263 --m_table->keyCount;
264 ++m_table->deletedSentinelCount;
265
266 if (m_table->deletedSentinelCount * 4 >= m_table->size)
267 rehash();
268
269 checkConsistency();
270 return offset;
271}
272
273size_t PropertyMap::get(const Identifier& propertyName, unsigned& attributes)
274{
275 ASSERT(!propertyName.isNull());
276
277 if (!m_table)
278 return WTF::notFound;
279
280 UString::Rep* rep = propertyName._ustring.rep();
281
282 unsigned i = rep->computedHash();
283
284#if DUMP_PROPERTYMAP_STATS
285 ++numProbes;
286#endif
287
288 unsigned entryIndex = m_table->entryIndices[i & m_table->sizeMask];
289 if (entryIndex == emptyEntryIndex)
290 return WTF::notFound;
291
292 if (rep == m_table->entries()[entryIndex - 1].key) {
293 attributes = m_table->entries()[entryIndex - 1].attributes;
294 return m_table->entries()[entryIndex - 1].offset;
295 }
296
297#if DUMP_PROPERTYMAP_STATS
298 ++numCollisions;
299#endif
300
301 unsigned k = 1 | doubleHash(rep->computedHash());
302
303 while (1) {
304 i += k;
305
306#if DUMP_PROPERTYMAP_STATS
307 ++numRehashes;
308#endif
309
310 entryIndex = m_table->entryIndices[i & m_table->sizeMask];
311 if (entryIndex == emptyEntryIndex)
312 return WTF::notFound;
313
314 if (rep == m_table->entries()[entryIndex - 1].key) {
315 attributes = m_table->entries()[entryIndex - 1].attributes;
316 return m_table->entries()[entryIndex - 1].offset;
317 }
318 }
319}
320
321void PropertyMap::insert(const Entry& entry)
322{
323 ASSERT(m_table);
324
325 unsigned i = entry.key->computedHash();
326 unsigned k = 0;
327
328#if DUMP_PROPERTYMAP_STATS
329 ++numProbes;
330#endif
331
332 while (1) {
333 unsigned entryIndex = m_table->entryIndices[i & m_table->sizeMask];
334 if (entryIndex == emptyEntryIndex)
335 break;
336
337 if (k == 0) {
338 k = 1 | doubleHash(entry.key->computedHash());
339#if DUMP_PROPERTYMAP_STATS
340 ++numCollisions;
341#endif
342 }
343
344 i += k;
345
346#if DUMP_PROPERTYMAP_STATS
347 ++numRehashes;
348#endif
349 }
350
351 unsigned entryIndex = m_table->keyCount + 2;
352 m_table->entryIndices[i & m_table->sizeMask] = entryIndex;
353 m_table->entries()[entryIndex - 1] = entry;
354
355 ++m_table->keyCount;
356}
357
358void PropertyMap::expand()
359{
360 ASSERT(m_table);
361 rehash(m_table->size * 2);
362}
363
364void PropertyMap::createTable()
365{
366 const unsigned newTableSize = 16;
367
368 ASSERT(!m_table);
369
370 checkConsistency();
371
372 m_table = static_cast<Table*>(fastZeroedMalloc(Table::allocationSize(newTableSize)));
373 m_table->size = newTableSize;
374 m_table->sizeMask = newTableSize - 1;
375
376 checkConsistency();
377}
378
379void PropertyMap::rehash()
380{
381 ASSERT(m_table);
382 ASSERT(m_table->size);
383 rehash(m_table->size);
384}
385
386void PropertyMap::rehash(unsigned newTableSize)
387{
388 ASSERT(m_table);
389
390 checkConsistency();
391
392 Table* oldTable = m_table;
393
394 m_table = static_cast<Table*>(fastZeroedMalloc(Table::allocationSize(newTableSize)));
395 m_table->size = newTableSize;
396 m_table->sizeMask = newTableSize - 1;
397
398 unsigned lastIndexUsed = 0;
399 unsigned entryCount = oldTable->keyCount + oldTable->deletedSentinelCount;
400 for (unsigned i = 1; i <= entryCount; ++i) {
401 if (oldTable->entries()[i].key) {
402 lastIndexUsed = max(oldTable->entries()[i].index, lastIndexUsed);
403 insert(oldTable->entries()[i]);
404 }
405 }
406 m_table->lastIndexUsed = lastIndexUsed;
407
408 fastFree(oldTable);
409
410 checkConsistency();
411}
412
413static int comparePropertyMapEntryIndices(const void* a, const void* b)
414{
415 unsigned ia = static_cast<PropertyMapEntry* const*>(a)[0]->index;
416 unsigned ib = static_cast<PropertyMapEntry* const*>(b)[0]->index;
417 if (ia < ib)
418 return -1;
419 if (ia > ib)
420 return +1;
421 return 0;
422}
423
424void PropertyMap::getEnumerablePropertyNames(PropertyNameArray& propertyNames) const
425{
426 if (!m_table)
427 return;
428
429 if (m_table->keyCount < tinyMapThreshold) {
430 Entry* a[tinyMapThreshold];
431 int i = 0;
432 unsigned entryCount = m_table->keyCount + m_table->deletedSentinelCount;
433 for (unsigned k = 1; k <= entryCount; k++) {
434 if (m_table->entries()[k].key && !(m_table->entries()[k].attributes & DontEnum)) {
435 Entry* value = &m_table->entries()[k];
436 int j;
437 for (j = i - 1; j >= 0 && a[j]->index > value->index; --j)
438 a[j + 1] = a[j];
439 a[j + 1] = value;
440 ++i;
441 }
442 }
443 if (!propertyNames.size()) {
444 for (int k = 0; k < i; ++k)
445 propertyNames.addKnownUnique(a[k]->key);
446 } else {
447 for (int k = 0; k < i; ++k)
448 propertyNames.add(a[k]->key);
449 }
450
451 return;
452 }
453
454 // Allocate a buffer to use to sort the keys.
455 Vector<Entry*, smallMapThreshold> sortedEnumerables(m_table->keyCount);
456
457 // Get pointers to the enumerable entries in the buffer.
458 Entry** p = sortedEnumerables.data();
459 unsigned entryCount = m_table->keyCount + m_table->deletedSentinelCount;
460 for (unsigned i = 1; i <= entryCount; i++) {
461 if (m_table->entries()[i].key && !(m_table->entries()[i].attributes & DontEnum))
462 *p++ = &m_table->entries()[i];
463 }
464
465 size_t enumerableCount = p - sortedEnumerables.data();
466 // Sort the entries by index.
467 qsort(sortedEnumerables.data(), enumerableCount, sizeof(Entry*), comparePropertyMapEntryIndices);
468 sortedEnumerables.resize(enumerableCount);
469
470 // Put the keys of the sorted entries into the list.
471 if (!propertyNames.size()) {
472 for (size_t i = 0; i < sortedEnumerables.size(); ++i)
473 propertyNames.addKnownUnique(sortedEnumerables[i]->key);
474 } else {
475 for (size_t i = 0; i < sortedEnumerables.size(); ++i)
476 propertyNames.add(sortedEnumerables[i]->key);
477 }
478}
479
480#if DO_PROPERTYMAP_CONSTENCY_CHECK
481
482void PropertyMap::checkConsistency()
483{
484 if (!m_table)
485 return;
486
487 ASSERT(m_table->size >= 16);
488 ASSERT(m_table->sizeMask);
489 ASSERT(m_table->size == m_table->sizeMask + 1);
490 ASSERT(!(m_table->size & m_table->sizeMask));
491
492 ASSERT(m_table->keyCount <= m_table->size / 2);
493 ASSERT(m_table->deletedSentinelCount <= m_table->size / 4);
494
495 ASSERT(m_table->keyCount + m_table->deletedSentinelCount <= m_table->size / 2);
496
497 unsigned indexCount = 0;
498 unsigned deletedIndexCount = 0;
499 for (unsigned a = 0; a != m_table->size; ++a) {
500 unsigned entryIndex = m_table->entryIndices[a];
501 if (entryIndex == emptyEntryIndex)
502 continue;
503 if (entryIndex == deletedSentinelIndex) {
504 ++deletedIndexCount;
505 continue;
506 }
507 ASSERT(entryIndex > deletedSentinelIndex);
508 ASSERT(entryIndex - 1 <= m_table->keyCount + m_table->deletedSentinelCount);
509 ++indexCount;
510
511 for (unsigned b = a + 1; b != m_table->size; ++b)
512 ASSERT(m_table->entryIndices[b] != entryIndex);
513 }
514 ASSERT(indexCount == m_table->keyCount);
515 ASSERT(deletedIndexCount == m_table->deletedSentinelCount);
516
517 ASSERT(m_table->entries()[0].key == 0);
518
519 unsigned nonEmptyEntryCount = 0;
520 for (unsigned c = 1; c <= m_table->keyCount + m_table->deletedSentinelCount; ++c) {
521 UString::Rep* rep = m_table->entries()[c].key;
522 if (!rep)
523 continue;
524 ++nonEmptyEntryCount;
525 unsigned i = rep->computedHash();
526 unsigned k = 0;
527 unsigned entryIndex;
528 while (1) {
529 entryIndex = m_table->entryIndices[i & m_table->sizeMask];
530 ASSERT(entryIndex != emptyEntryIndex);
531 if (rep == m_table->entries()[entryIndex - 1].key)
532 break;
533 if (k == 0)
534 k = 1 | doubleHash(rep->computedHash());
535 i += k;
536 }
537 ASSERT(entryIndex == c + 1);
538 }
539
540 ASSERT(nonEmptyEntryCount == m_table->keyCount);
541}
542
543#endif // DO_PROPERTYMAP_CONSTENCY_CHECK
544
545} // namespace JSC
Note: See TracBrowser for help on using the repository browser.