Fix Object.freeze for non-final objects.
[WebKit-https.git] / Source / JavaScriptCore / runtime / Structure.cpp
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 #include <wtf/Threading.h>
38
39 #define DUMP_STRUCTURE_ID_STATISTICS 0
40
41 #ifndef NDEBUG
42 #define DO_PROPERTYMAP_CONSTENCY_CHECK 0
43 #else
44 #define DO_PROPERTYMAP_CONSTENCY_CHECK 0
45 #endif
46
47 using namespace std;
48 using namespace WTF;
49
50 #if DUMP_PROPERTYMAP_STATS
51
52 int numProbes;
53 int numCollisions;
54 int numRehashes;
55 int numRemoves;
56
57 #endif
58
59 namespace JSC {
60
61 #if DUMP_STRUCTURE_ID_STATISTICS
62 static HashSet<Structure*>& liveStructureSet = *(new HashSet<Structure*>);
63 #endif
64
65 bool StructureTransitionTable::contains(StringImpl* rep, unsigned attributes) const
66 {
67     if (isUsingSingleSlot()) {
68         Structure* transition = singleTransition();
69         return transition && transition->m_nameInPrevious == rep && transition->m_attributesInPrevious == attributes;
70     }
71     return map()->contains(make_pair(rep, attributes));
72 }
73
74 inline Structure* StructureTransitionTable::get(StringImpl* rep, unsigned attributes) const
75 {
76     if (isUsingSingleSlot()) {
77         Structure* transition = singleTransition();
78         return (transition && transition->m_nameInPrevious == rep && transition->m_attributesInPrevious == attributes) ? transition : 0;
79     }
80     return map()->get(make_pair(rep, attributes));
81 }
82
83 inline void StructureTransitionTable::add(JSGlobalData& globalData, Structure* structure)
84 {
85     if (isUsingSingleSlot()) {
86         Structure* existingTransition = singleTransition();
87
88         // This handles the first transition being added.
89         if (!existingTransition) {
90             setSingleTransition(globalData, structure);
91             return;
92         }
93
94         // This handles the second transition being added
95         // (or the first transition being despecified!)
96         setMap(new TransitionMap());
97         add(globalData, existingTransition);
98     }
99
100     // Add the structure to the map.
101
102     // Newer versions of the STL have an std::make_pair function that takes rvalue references.
103     // When either of the parameters are bitfields, the C++ compiler will try to bind them as lvalues, which is invalid. To work around this, use unary "+" to make the parameter an rvalue.
104     // See https://bugs.webkit.org/show_bug.cgi?id=59261 for more details
105     std::pair<TransitionMap::iterator, bool> result = map()->add(globalData, make_pair(structure->m_nameInPrevious, +structure->m_attributesInPrevious), structure);
106     if (!result.second) {
107         // There already is an entry! - we should only hit this when despecifying.
108         ASSERT(result.first.get().second->m_specificValueInPrevious);
109         ASSERT(!structure->m_specificValueInPrevious);
110         map()->set(result.first, structure);
111     }
112 }
113
114 void Structure::dumpStatistics()
115 {
116 #if DUMP_STRUCTURE_ID_STATISTICS
117     unsigned numberLeaf = 0;
118     unsigned numberUsingSingleSlot = 0;
119     unsigned numberSingletons = 0;
120     unsigned numberWithPropertyMaps = 0;
121     unsigned totalPropertyMapsSize = 0;
122
123     HashSet<Structure*>::const_iterator end = liveStructureSet.end();
124     for (HashSet<Structure*>::const_iterator it = liveStructureSet.begin(); it != end; ++it) {
125         Structure* structure = *it;
126
127         switch (structure->m_transitionTable.size()) {
128             case 0:
129                 ++numberLeaf;
130                if (!structure->m_previous)
131                     ++numberSingletons;
132                 break;
133
134             case 1:
135                 ++numberUsingSingleSlot;
136                 break;
137         }
138
139         if (structure->m_propertyTable) {
140             ++numberWithPropertyMaps;
141             totalPropertyMapsSize += structure->m_propertyTable->sizeInMemory();
142         }
143     }
144
145     dataLog("Number of live Structures: %d\n", liveStructureSet.size());
146     dataLog("Number of Structures using the single item optimization for transition map: %d\n", numberUsingSingleSlot);
147     dataLog("Number of Structures that are leaf nodes: %d\n", numberLeaf);
148     dataLog("Number of Structures that singletons: %d\n", numberSingletons);
149     dataLog("Number of Structures with PropertyMaps: %d\n", numberWithPropertyMaps);
150
151     dataLog("Size of a single Structures: %d\n", static_cast<unsigned>(sizeof(Structure)));
152     dataLog("Size of sum of all property maps: %d\n", totalPropertyMapsSize);
153     dataLog("Size of average of all property maps: %f\n", static_cast<double>(totalPropertyMapsSize) / static_cast<double>(liveStructureSet.size()));
154 #else
155     dataLog("Dumping Structure statistics is not enabled.\n");
156 #endif
157 }
158
159 Structure::Structure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo)
160     : JSCell(globalData, globalData.structureStructure.get())
161     , m_typeInfo(typeInfo)
162     , m_globalObject(globalData, this, globalObject, WriteBarrier<JSGlobalObject>::MayBeNull)
163     , m_prototype(globalData, this, prototype)
164     , m_classInfo(classInfo)
165     , m_propertyStorageCapacity(typeInfo.isFinalObject() ? JSFinalObject_inlineStorageCapacity : JSNonFinalObject_inlineStorageCapacity)
166     , m_offset(noOffset)
167     , m_dictionaryKind(NoneDictionaryKind)
168     , m_isPinnedPropertyTable(false)
169     , m_hasGetterSetterProperties(false)
170     , m_hasReadOnlyOrGetterSetterPropertiesExcludingProto(false)
171     , m_hasNonEnumerableProperties(false)
172     , m_attributesInPrevious(0)
173     , m_specificFunctionThrashCount(0)
174     , m_preventExtensions(false)
175     , m_didTransition(false)
176     , m_staticFunctionReified(false)
177 {
178 }
179
180 const ClassInfo Structure::s_info = { "Structure", 0, 0, 0, CREATE_METHOD_TABLE(Structure) };
181
182 Structure::Structure(JSGlobalData& globalData)
183     : JSCell(CreatingEarlyCell)
184     , m_typeInfo(CompoundType, OverridesVisitChildren)
185     , m_prototype(globalData, this, jsNull())
186     , m_classInfo(&s_info)
187     , m_propertyStorageCapacity(0)
188     , m_offset(noOffset)
189     , m_dictionaryKind(NoneDictionaryKind)
190     , m_isPinnedPropertyTable(false)
191     , m_hasGetterSetterProperties(false)
192     , m_hasReadOnlyOrGetterSetterPropertiesExcludingProto(false)
193     , m_hasNonEnumerableProperties(false)
194     , m_attributesInPrevious(0)
195     , m_specificFunctionThrashCount(0)
196     , m_preventExtensions(false)
197     , m_didTransition(false)
198     , m_staticFunctionReified(false)
199 {
200 }
201
202 Structure::Structure(JSGlobalData& globalData, const Structure* previous)
203     : JSCell(globalData, globalData.structureStructure.get())
204     , m_typeInfo(previous->typeInfo())
205     , m_prototype(globalData, this, previous->storedPrototype())
206     , m_classInfo(previous->m_classInfo)
207     , m_propertyStorageCapacity(previous->m_propertyStorageCapacity)
208     , m_offset(noOffset)
209     , m_dictionaryKind(previous->m_dictionaryKind)
210     , m_isPinnedPropertyTable(false)
211     , m_hasGetterSetterProperties(previous->m_hasGetterSetterProperties)
212     , m_hasReadOnlyOrGetterSetterPropertiesExcludingProto(previous->m_hasReadOnlyOrGetterSetterPropertiesExcludingProto)
213     , m_hasNonEnumerableProperties(previous->m_hasNonEnumerableProperties)
214     , m_attributesInPrevious(0)
215     , m_specificFunctionThrashCount(previous->m_specificFunctionThrashCount)
216     , m_preventExtensions(previous->m_preventExtensions)
217     , m_didTransition(true)
218     , m_staticFunctionReified(previous->m_staticFunctionReified)
219 {
220     if (previous->m_globalObject)
221         m_globalObject.set(globalData, this, previous->m_globalObject.get());
222 }
223
224 void Structure::destroy(JSCell* cell)
225 {
226     jsCast<Structure*>(cell)->Structure::~Structure();
227 }
228
229 void Structure::materializePropertyMap(JSGlobalData& globalData)
230 {
231     ASSERT(structure()->classInfo() == &s_info);
232     ASSERT(!m_propertyTable);
233
234     Vector<Structure*, 8> structures;
235     structures.append(this);
236
237     Structure* structure = this;
238
239     // Search for the last Structure with a property table.
240     while ((structure = structure->previousID())) {
241         if (structure->m_isPinnedPropertyTable) {
242             ASSERT(structure->m_propertyTable);
243             ASSERT(!structure->m_previous);
244
245             m_propertyTable = structure->m_propertyTable->copy(globalData, 0, m_offset + 1);
246             break;
247         }
248
249         structures.append(structure);
250     }
251
252     if (!m_propertyTable)
253         createPropertyMap(m_offset + 1);
254
255     for (ptrdiff_t i = structures.size() - 2; i >= 0; --i) {
256         structure = structures[i];
257         PropertyMapEntry entry(globalData, this, structure->m_nameInPrevious.get(), structure->m_offset, structure->m_attributesInPrevious, structure->m_specificValueInPrevious.get());
258         m_propertyTable->add(entry);
259     }
260 }
261
262 void Structure::growPropertyStorageCapacity()
263 {
264     if (isUsingInlineStorage())
265         m_propertyStorageCapacity = JSObject::baseExternalStorageCapacity;
266     else
267         m_propertyStorageCapacity *= 2;
268 }
269
270 void Structure::despecifyDictionaryFunction(JSGlobalData& globalData, const Identifier& propertyName)
271 {
272     StringImpl* rep = propertyName.impl();
273
274     materializePropertyMapIfNecessary(globalData);
275
276     ASSERT(isDictionary());
277     ASSERT(m_propertyTable);
278
279     PropertyMapEntry* entry = m_propertyTable->find(rep).first;
280     ASSERT(entry);
281     entry->specificValue.clear();
282 }
283
284 Structure* Structure::addPropertyTransitionToExistingStructure(Structure* structure, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset)
285 {
286     ASSERT(!structure->isDictionary());
287     ASSERT(structure->isObject());
288
289     if (Structure* existingTransition = structure->m_transitionTable.get(propertyName.impl(), attributes)) {
290         JSCell* specificValueInPrevious = existingTransition->m_specificValueInPrevious.get();
291         if (specificValueInPrevious && specificValueInPrevious != specificValue)
292             return 0;
293         ASSERT(existingTransition->m_offset != noOffset);
294         offset = existingTransition->m_offset;
295         return existingTransition;
296     }
297
298     return 0;
299 }
300
301 Structure* Structure::addPropertyTransition(JSGlobalData& globalData, Structure* structure, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset)
302 {
303     // If we have a specific function, we may have got to this point if there is
304     // already a transition with the correct property name and attributes, but
305     // specialized to a different function.  In this case we just want to give up
306     // and despecialize the transition.
307     // In this case we clear the value of specificFunction which will result
308     // in us adding a non-specific transition, and any subsequent lookup in
309     // Structure::addPropertyTransitionToExistingStructure will just use that.
310     if (specificValue && structure->m_transitionTable.contains(propertyName.impl(), attributes))
311         specificValue = 0;
312
313     ASSERT(!structure->isDictionary());
314     ASSERT(structure->isObject());
315     ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure, propertyName, attributes, specificValue, offset));
316     
317     if (structure->m_specificFunctionThrashCount == maxSpecificFunctionThrashCount)
318         specificValue = 0;
319
320     if (structure->transitionCount() > s_maxTransitionLength) {
321         Structure* transition = toCacheableDictionaryTransition(globalData, structure);
322         ASSERT(structure != transition);
323         offset = transition->putSpecificValue(globalData, propertyName, attributes, specificValue);
324         if (transition->propertyStorageSize() > transition->propertyStorageCapacity())
325             transition->growPropertyStorageCapacity();
326         return transition;
327     }
328     
329     Structure* transition = create(globalData, structure);
330
331     transition->m_cachedPrototypeChain.setMayBeNull(globalData, transition, structure->m_cachedPrototypeChain.get());
332     transition->m_previous.set(globalData, transition, structure);
333     transition->m_nameInPrevious = propertyName.impl();
334     transition->m_attributesInPrevious = attributes;
335     transition->m_specificValueInPrevious.setMayBeNull(globalData, transition, specificValue);
336
337     if (structure->m_propertyTable) {
338         if (structure->m_isPinnedPropertyTable)
339             transition->m_propertyTable = structure->m_propertyTable->copy(globalData, transition, structure->m_propertyTable->size() + 1);
340         else
341             transition->m_propertyTable = structure->m_propertyTable.release();
342     } else {
343         if (structure->m_previous)
344             transition->materializePropertyMap(globalData);
345         else
346             transition->createPropertyMap();
347     }
348
349     offset = transition->putSpecificValue(globalData, propertyName, attributes, specificValue);
350     if (transition->propertyStorageSize() > transition->propertyStorageCapacity())
351         transition->growPropertyStorageCapacity();
352
353     transition->m_offset = offset;
354     structure->m_transitionTable.add(globalData, transition);
355     return transition;
356 }
357
358 Structure* Structure::removePropertyTransition(JSGlobalData& globalData, Structure* structure, const Identifier& propertyName, size_t& offset)
359 {
360     ASSERT(!structure->isUncacheableDictionary());
361
362     Structure* transition = toUncacheableDictionaryTransition(globalData, structure);
363
364     offset = transition->remove(propertyName);
365
366     return transition;
367 }
368
369 Structure* Structure::changePrototypeTransition(JSGlobalData& globalData, Structure* structure, JSValue prototype)
370 {
371     Structure* transition = create(globalData, structure);
372
373     transition->m_prototype.set(globalData, transition, prototype);
374
375     // Don't set m_offset, as one can not transition to this.
376
377     structure->materializePropertyMapIfNecessary(globalData);
378     transition->m_propertyTable = structure->copyPropertyTableForPinning(globalData, transition);
379     transition->pin();
380
381     return transition;
382 }
383
384 Structure* Structure::despecifyFunctionTransition(JSGlobalData& globalData, Structure* structure, const Identifier& replaceFunction)
385 {
386     ASSERT(structure->m_specificFunctionThrashCount < maxSpecificFunctionThrashCount);
387     Structure* transition = create(globalData, structure);
388
389     ++transition->m_specificFunctionThrashCount;
390
391     // Don't set m_offset, as one can not transition to this.
392
393     structure->materializePropertyMapIfNecessary(globalData);
394     transition->m_propertyTable = structure->copyPropertyTableForPinning(globalData, transition);
395     transition->pin();
396
397     if (transition->m_specificFunctionThrashCount == maxSpecificFunctionThrashCount)
398         transition->despecifyAllFunctions(globalData);
399     else {
400         bool removed = transition->despecifyFunction(globalData, replaceFunction);
401         ASSERT_UNUSED(removed, removed);
402     }
403
404     return transition;
405 }
406
407 Structure* Structure::attributeChangeTransition(JSGlobalData& globalData, Structure* structure, const Identifier& propertyName, unsigned attributes)
408 {
409     if (!structure->isUncacheableDictionary()) {
410         Structure* transition = create(globalData, structure);
411
412         // Don't set m_offset, as one can not transition to this.
413
414         structure->materializePropertyMapIfNecessary(globalData);
415         transition->m_propertyTable = structure->copyPropertyTableForPinning(globalData, transition);
416         transition->pin();
417         
418         structure = transition;
419     }
420
421     ASSERT(structure->m_propertyTable);
422     PropertyMapEntry* entry = structure->m_propertyTable->find(propertyName.impl()).first;
423     ASSERT(entry);
424     entry->attributes = attributes;
425
426     return structure;
427 }
428
429 Structure* Structure::toDictionaryTransition(JSGlobalData& globalData, Structure* structure, DictionaryKind kind)
430 {
431     ASSERT(!structure->isUncacheableDictionary());
432     
433     Structure* transition = create(globalData, structure);
434
435     structure->materializePropertyMapIfNecessary(globalData);
436     transition->m_propertyTable = structure->copyPropertyTableForPinning(globalData, transition);
437     transition->m_dictionaryKind = kind;
438     transition->pin();
439
440     return transition;
441 }
442
443 Structure* Structure::toCacheableDictionaryTransition(JSGlobalData& globalData, Structure* structure)
444 {
445     return toDictionaryTransition(globalData, structure, CachedDictionaryKind);
446 }
447
448 Structure* Structure::toUncacheableDictionaryTransition(JSGlobalData& globalData, Structure* structure)
449 {
450     return toDictionaryTransition(globalData, structure, UncachedDictionaryKind);
451 }
452
453 // In future we may want to cache this transition.
454 Structure* Structure::sealTransition(JSGlobalData& globalData, Structure* structure)
455 {
456     Structure* transition = preventExtensionsTransition(globalData, structure);
457
458     if (transition->m_propertyTable) {
459         PropertyTable::iterator end = transition->m_propertyTable->end();
460         for (PropertyTable::iterator iter = transition->m_propertyTable->begin(); iter != end; ++iter)
461             iter->attributes |= DontDelete;
462     }
463
464     return transition;
465 }
466
467 // In future we may want to cache this transition.
468 Structure* Structure::freezeTransition(JSGlobalData& globalData, Structure* structure)
469 {
470     Structure* transition = preventExtensionsTransition(globalData, structure);
471
472     if (transition->m_propertyTable) {
473         PropertyTable::iterator iter = transition->m_propertyTable->begin();
474         PropertyTable::iterator end = transition->m_propertyTable->end();
475         if (iter != end)
476             transition->m_hasReadOnlyOrGetterSetterPropertiesExcludingProto = true;
477         for (; iter != end; ++iter)
478             iter->attributes |= iter->attributes & Accessor ? DontDelete : (DontDelete | ReadOnly);
479     }
480
481     return transition;
482 }
483
484 // In future we may want to cache this transition.
485 Structure* Structure::preventExtensionsTransition(JSGlobalData& globalData, Structure* structure)
486 {
487     Structure* transition = create(globalData, structure);
488
489     // Don't set m_offset, as one can not transition to this.
490
491     structure->materializePropertyMapIfNecessary(globalData);
492     transition->m_propertyTable = structure->copyPropertyTableForPinning(globalData, transition);
493     transition->m_preventExtensions = true;
494     transition->pin();
495
496     return transition;
497 }
498
499 // In future we may want to cache this property.
500 bool Structure::isSealed(JSGlobalData& globalData)
501 {
502     if (isExtensible())
503         return false;
504
505     materializePropertyMapIfNecessary(globalData);
506     if (!m_propertyTable)
507         return true;
508
509     PropertyTable::iterator end = m_propertyTable->end();
510     for (PropertyTable::iterator iter = m_propertyTable->begin(); iter != end; ++iter) {
511         if ((iter->attributes & DontDelete) != DontDelete)
512             return false;
513     }
514     return true;
515 }
516
517 // In future we may want to cache this property.
518 bool Structure::isFrozen(JSGlobalData& globalData)
519 {
520     if (isExtensible())
521         return false;
522
523     materializePropertyMapIfNecessary(globalData);
524     if (!m_propertyTable)
525         return true;
526
527     PropertyTable::iterator end = m_propertyTable->end();
528     for (PropertyTable::iterator iter = m_propertyTable->begin(); iter != end; ++iter) {
529         if (!(iter->attributes & DontDelete))
530             return false;
531         if (!(iter->attributes & (ReadOnly | Accessor)))
532             return false;
533     }
534     return true;
535 }
536
537 Structure* Structure::flattenDictionaryStructure(JSGlobalData& globalData, JSObject* object)
538 {
539     ASSERT(isDictionary());
540     if (isUncacheableDictionary()) {
541         ASSERT(m_propertyTable);
542
543         size_t propertyCount = m_propertyTable->size();
544         Vector<JSValue> values(propertyCount);
545
546         unsigned i = 0;
547         PropertyTable::iterator end = m_propertyTable->end();
548         for (PropertyTable::iterator iter = m_propertyTable->begin(); iter != end; ++iter, ++i) {
549             values[i] = object->getDirectOffset(iter->offset);
550             // Update property table to have the new property offsets
551             iter->offset = i;
552         }
553         
554         // Copy the original property values into their final locations
555         for (unsigned i = 0; i < propertyCount; i++)
556             object->putDirectOffset(globalData, i, values[i]);
557
558         m_propertyTable->clearDeletedOffsets();
559     }
560
561     m_dictionaryKind = NoneDictionaryKind;
562     return this;
563 }
564
565 size_t Structure::addPropertyWithoutTransition(JSGlobalData& globalData, const Identifier& propertyName, unsigned attributes, JSCell* specificValue)
566 {
567     ASSERT(!m_enumerationCache);
568
569     if (m_specificFunctionThrashCount == maxSpecificFunctionThrashCount)
570         specificValue = 0;
571
572     materializePropertyMapIfNecessaryForPinning(globalData);
573     
574     pin();
575
576     size_t offset = putSpecificValue(globalData, propertyName, attributes, specificValue);
577     if (propertyStorageSize() > propertyStorageCapacity())
578         growPropertyStorageCapacity();
579     return offset;
580 }
581
582 size_t Structure::removePropertyWithoutTransition(JSGlobalData& globalData, const Identifier& propertyName)
583 {
584     ASSERT(isUncacheableDictionary());
585     ASSERT(!m_enumerationCache);
586
587     materializePropertyMapIfNecessaryForPinning(globalData);
588
589     pin();
590     size_t offset = remove(propertyName);
591     return offset;
592 }
593
594 void Structure::pin()
595 {
596     ASSERT(m_propertyTable);
597     m_isPinnedPropertyTable = true;
598     m_previous.clear();
599     m_nameInPrevious.clear();
600 }
601
602 #if DUMP_PROPERTYMAP_STATS
603
604 struct PropertyMapStatisticsExitLogger {
605     ~PropertyMapStatisticsExitLogger();
606 };
607
608 static PropertyMapStatisticsExitLogger logger;
609
610 PropertyMapStatisticsExitLogger::~PropertyMapStatisticsExitLogger()
611 {
612     dataLog("\nJSC::PropertyMap statistics\n\n");
613     dataLog("%d probes\n", numProbes);
614     dataLog("%d collisions (%.1f%%)\n", numCollisions, 100.0 * numCollisions / numProbes);
615     dataLog("%d rehashes\n", numRehashes);
616     dataLog("%d removes\n", numRemoves);
617 }
618
619 #endif
620
621 #if !DO_PROPERTYMAP_CONSTENCY_CHECK
622
623 inline void Structure::checkConsistency()
624 {
625 }
626
627 #endif
628
629 PassOwnPtr<PropertyTable> Structure::copyPropertyTable(JSGlobalData& globalData, Structure* owner)
630 {
631     return adoptPtr(m_propertyTable ? new PropertyTable(globalData, owner, *m_propertyTable) : 0);
632 }
633
634 PassOwnPtr<PropertyTable> Structure::copyPropertyTableForPinning(JSGlobalData& globalData, Structure* owner)
635 {
636     return adoptPtr(m_propertyTable ? new PropertyTable(globalData, owner, *m_propertyTable) : new PropertyTable(m_offset == noOffset ? 0 : m_offset));
637 }
638
639 size_t Structure::get(JSGlobalData& globalData, StringImpl* propertyName, unsigned& attributes, JSCell*& specificValue)
640 {
641     materializePropertyMapIfNecessary(globalData);
642     if (!m_propertyTable)
643         return WTF::notFound;
644
645     PropertyMapEntry* entry = m_propertyTable->find(propertyName).first;
646     if (!entry)
647         return WTF::notFound;
648
649     attributes = entry->attributes;
650     specificValue = entry->specificValue.get();
651     return entry->offset;
652 }
653
654 bool Structure::despecifyFunction(JSGlobalData& globalData, const Identifier& propertyName)
655 {
656     materializePropertyMapIfNecessary(globalData);
657     if (!m_propertyTable)
658         return false;
659
660     ASSERT(!propertyName.isNull());
661     PropertyMapEntry* entry = m_propertyTable->find(propertyName.impl()).first;
662     if (!entry)
663         return false;
664
665     ASSERT(entry->specificValue);
666     entry->specificValue.clear();
667     return true;
668 }
669
670 void Structure::despecifyAllFunctions(JSGlobalData& globalData)
671 {
672     materializePropertyMapIfNecessary(globalData);
673     if (!m_propertyTable)
674         return;
675
676     PropertyTable::iterator end = m_propertyTable->end();
677     for (PropertyTable::iterator iter = m_propertyTable->begin(); iter != end; ++iter)
678         iter->specificValue.clear();
679 }
680
681 size_t Structure::putSpecificValue(JSGlobalData& globalData, const Identifier& propertyName, unsigned attributes, JSCell* specificValue)
682 {
683     ASSERT(!propertyName.isNull());
684     ASSERT(get(globalData, propertyName) == notFound);
685
686     checkConsistency();
687     if (attributes & DontEnum)
688         m_hasNonEnumerableProperties = true;
689
690     StringImpl* rep = propertyName.impl();
691
692     if (!m_propertyTable)
693         createPropertyMap();
694
695     unsigned newOffset;
696
697     if (m_propertyTable->hasDeletedOffset())
698         newOffset = m_propertyTable->getDeletedOffset();
699     else
700         newOffset = m_propertyTable->size();
701
702     m_propertyTable->add(PropertyMapEntry(globalData, this, rep, newOffset, attributes, specificValue));
703
704     checkConsistency();
705     return newOffset;
706 }
707
708 size_t Structure::remove(const Identifier& propertyName)
709 {
710     ASSERT(!propertyName.isNull());
711
712     checkConsistency();
713
714     StringImpl* rep = propertyName.impl();
715
716     if (!m_propertyTable)
717         return notFound;
718
719     PropertyTable::find_iterator position = m_propertyTable->find(rep);
720     if (!position.first)
721         return notFound;
722
723     size_t offset = position.first->offset;
724
725     m_propertyTable->remove(position);
726     m_propertyTable->addDeletedOffset(offset);
727
728     checkConsistency();
729     return offset;
730 }
731
732 void Structure::createPropertyMap(unsigned capacity)
733 {
734     ASSERT(!m_propertyTable);
735
736     checkConsistency();
737     m_propertyTable = adoptPtr(new PropertyTable(capacity));
738     checkConsistency();
739 }
740
741 void Structure::getPropertyNamesFromStructure(JSGlobalData& globalData, PropertyNameArray& propertyNames, EnumerationMode mode)
742 {
743     materializePropertyMapIfNecessary(globalData);
744     if (!m_propertyTable)
745         return;
746
747     bool knownUnique = !propertyNames.size();
748
749     PropertyTable::iterator end = m_propertyTable->end();
750     for (PropertyTable::iterator iter = m_propertyTable->begin(); iter != end; ++iter) {
751         ASSERT(m_hasNonEnumerableProperties || !(iter->attributes & DontEnum));
752         if (!(iter->attributes & DontEnum) || (mode == IncludeDontEnumProperties)) {
753             if (knownUnique)
754                 propertyNames.addKnownUnique(iter->key);
755             else
756                 propertyNames.add(iter->key);
757         }
758     }
759 }
760
761 void Structure::visitChildren(JSCell* cell, SlotVisitor& visitor)
762 {
763     Structure* thisObject = jsCast<Structure*>(cell);
764     ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info);
765     ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren());
766     JSCell::visitChildren(thisObject, visitor);
767     if (thisObject->m_globalObject)
768         visitor.append(&thisObject->m_globalObject);
769     if (!thisObject->isObject())
770         thisObject->m_cachedPrototypeChain.clear();
771     else {
772         if (thisObject->m_prototype)
773             visitor.append(&thisObject->m_prototype);
774         if (thisObject->m_cachedPrototypeChain)
775             visitor.append(&thisObject->m_cachedPrototypeChain);
776     }
777     if (thisObject->m_previous)
778         visitor.append(&thisObject->m_previous);
779     if (thisObject->m_specificValueInPrevious)
780         visitor.append(&thisObject->m_specificValueInPrevious);
781     if (thisObject->m_enumerationCache)
782         visitor.append(&thisObject->m_enumerationCache);
783     if (thisObject->m_propertyTable) {
784         PropertyTable::iterator end = thisObject->m_propertyTable->end();
785         for (PropertyTable::iterator ptr = thisObject->m_propertyTable->begin(); ptr != end; ++ptr) {
786             if (ptr->specificValue)
787                 visitor.append(&ptr->specificValue);
788         }
789     }
790 }
791
792 #if DO_PROPERTYMAP_CONSTENCY_CHECK
793
794 void PropertyTable::checkConsistency()
795 {
796     ASSERT(m_indexSize >= PropertyTable::MinimumTableSize);
797     ASSERT(m_indexMask);
798     ASSERT(m_indexSize == m_indexMask + 1);
799     ASSERT(!(m_indexSize & m_indexMask));
800
801     ASSERT(m_keyCount <= m_indexSize / 2);
802     ASSERT(m_keyCount + m_deletedCount <= m_indexSize / 2);
803     ASSERT(m_deletedCount <= m_indexSize / 4);
804
805     unsigned indexCount = 0;
806     unsigned deletedIndexCount = 0;
807     for (unsigned a = 0; a != m_indexSize; ++a) {
808         unsigned entryIndex = m_index[a];
809         if (entryIndex == PropertyTable::EmptyEntryIndex)
810             continue;
811         if (entryIndex == deletedEntryIndex()) {
812             ++deletedIndexCount;
813             continue;
814         }
815         ASSERT(entryIndex < deletedEntryIndex());
816         ASSERT(entryIndex - 1 <= usedCount());
817         ++indexCount;
818
819         for (unsigned b = a + 1; b != m_indexSize; ++b)
820             ASSERT(m_index[b] != entryIndex);
821     }
822     ASSERT(indexCount == m_keyCount);
823     ASSERT(deletedIndexCount == m_deletedCount);
824
825     ASSERT(!table()[deletedEntryIndex() - 1].key);
826
827     unsigned nonEmptyEntryCount = 0;
828     for (unsigned c = 0; c < usedCount(); ++c) {
829         StringImpl* rep = table()[c].key;
830         if (rep == PROPERTY_MAP_DELETED_ENTRY_KEY)
831             continue;
832         ++nonEmptyEntryCount;
833         unsigned i = rep->existingHash();
834         unsigned k = 0;
835         unsigned entryIndex;
836         while (1) {
837             entryIndex = m_index[i & m_indexMask];
838             ASSERT(entryIndex != PropertyTable::EmptyEntryIndex);
839             if (rep == table()[entryIndex - 1].key)
840                 break;
841             if (k == 0)
842                 k = 1 | doubleHash(rep->existingHash());
843             i += k;
844         }
845         ASSERT(entryIndex == c + 1);
846     }
847
848     ASSERT(nonEmptyEntryCount == m_keyCount);
849 }
850
851 void Structure::checkConsistency()
852 {
853     if (!m_propertyTable)
854         return;
855
856     if (!m_hasNonEnumerableProperties) {
857         PropertyTable::iterator end = m_propertyTable->end();
858         for (PropertyTable::iterator iter = m_propertyTable->begin(); iter != end; ++iter) {
859             ASSERT(!(iter->attributes & DontEnum));
860         }
861     }
862
863     m_propertyTable->checkConsistency();
864 }
865
866 #endif // DO_PROPERTYMAP_CONSTENCY_CHECK
867
868 } // namespace JSC