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