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