]> git.saurik.com Git - apple/javascriptcore.git/blob - runtime/Structure.cpp
JavaScriptCore-7600.1.4.16.1.tar.gz
[apple/javascriptcore.git] / runtime / Structure.cpp
1 /*
2 * Copyright (C) 2008, 2009, 2013 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 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 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 "CodeBlock.h"
30 #include "DumpContext.h"
31 #include "JSCInlines.h"
32 #include "JSObject.h"
33 #include "JSPropertyNameIterator.h"
34 #include "Lookup.h"
35 #include "PropertyMapHashTable.h"
36 #include "PropertyNameArray.h"
37 #include "StructureChain.h"
38 #include "StructureRareDataInlines.h"
39 #include <wtf/CommaPrinter.h>
40 #include <wtf/ProcessID.h>
41 #include <wtf/RefCountedLeakCounter.h>
42 #include <wtf/RefPtr.h>
43 #include <wtf/Threading.h>
44
45 #define DUMP_STRUCTURE_ID_STATISTICS 0
46
47 #ifndef NDEBUG
48 #define DO_PROPERTYMAP_CONSTENCY_CHECK 0
49 #else
50 #define DO_PROPERTYMAP_CONSTENCY_CHECK 0
51 #endif
52
53 using namespace std;
54 using namespace WTF;
55
56 namespace JSC {
57
58 #if DUMP_STRUCTURE_ID_STATISTICS
59 static HashSet<Structure*>& liveStructureSet = *(new HashSet<Structure*>);
60 #endif
61
62 bool StructureTransitionTable::contains(StringImpl* rep, unsigned attributes) const
63 {
64 if (isUsingSingleSlot()) {
65 Structure* transition = singleTransition();
66 return transition && transition->m_nameInPrevious == rep && transition->m_attributesInPrevious == attributes;
67 }
68 return map()->get(std::make_pair(rep, attributes));
69 }
70
71 inline Structure* StructureTransitionTable::get(StringImpl* rep, unsigned attributes) const
72 {
73 if (isUsingSingleSlot()) {
74 Structure* transition = singleTransition();
75 return (transition && transition->m_nameInPrevious == rep && transition->m_attributesInPrevious == attributes) ? transition : 0;
76 }
77 return map()->get(std::make_pair(rep, attributes));
78 }
79
80 inline void StructureTransitionTable::add(VM& vm, Structure* structure)
81 {
82 if (isUsingSingleSlot()) {
83 Structure* existingTransition = singleTransition();
84
85 // This handles the first transition being added.
86 if (!existingTransition) {
87 setSingleTransition(vm, structure);
88 return;
89 }
90
91 // This handles the second transition being added
92 // (or the first transition being despecified!)
93 setMap(new TransitionMap());
94 add(vm, existingTransition);
95 }
96
97 // Add the structure to the map.
98
99 // Newer versions of the STL have an std::make_pair function that takes rvalue references.
100 // 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.
101 // See https://bugs.webkit.org/show_bug.cgi?id=59261 for more details
102 map()->set(std::make_pair(structure->m_nameInPrevious.get(), +structure->m_attributesInPrevious), structure);
103 }
104
105 void Structure::dumpStatistics()
106 {
107 #if DUMP_STRUCTURE_ID_STATISTICS
108 unsigned numberLeaf = 0;
109 unsigned numberUsingSingleSlot = 0;
110 unsigned numberSingletons = 0;
111 unsigned numberWithPropertyMaps = 0;
112 unsigned totalPropertyMapsSize = 0;
113
114 HashSet<Structure*>::const_iterator end = liveStructureSet.end();
115 for (HashSet<Structure*>::const_iterator it = liveStructureSet.begin(); it != end; ++it) {
116 Structure* structure = *it;
117
118 switch (structure->m_transitionTable.size()) {
119 case 0:
120 ++numberLeaf;
121 if (!structure->previousID())
122 ++numberSingletons;
123 break;
124
125 case 1:
126 ++numberUsingSingleSlot;
127 break;
128 }
129
130 if (structure->propertyTable()) {
131 ++numberWithPropertyMaps;
132 totalPropertyMapsSize += structure->propertyTable()->sizeInMemory();
133 }
134 }
135
136 dataLogF("Number of live Structures: %d\n", liveStructureSet.size());
137 dataLogF("Number of Structures using the single item optimization for transition map: %d\n", numberUsingSingleSlot);
138 dataLogF("Number of Structures that are leaf nodes: %d\n", numberLeaf);
139 dataLogF("Number of Structures that singletons: %d\n", numberSingletons);
140 dataLogF("Number of Structures with PropertyMaps: %d\n", numberWithPropertyMaps);
141
142 dataLogF("Size of a single Structures: %d\n", static_cast<unsigned>(sizeof(Structure)));
143 dataLogF("Size of sum of all property maps: %d\n", totalPropertyMapsSize);
144 dataLogF("Size of average of all property maps: %f\n", static_cast<double>(totalPropertyMapsSize) / static_cast<double>(liveStructureSet.size()));
145 #else
146 dataLogF("Dumping Structure statistics is not enabled.\n");
147 #endif
148 }
149
150 Structure::Structure(VM& vm, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo, IndexingType indexingType, unsigned inlineCapacity)
151 : JSCell(vm, vm.structureStructure.get())
152 , m_blob(vm.heap.structureIDTable().allocateID(this), indexingType, typeInfo)
153 , m_outOfLineTypeFlags(typeInfo.outOfLineTypeFlags())
154 , m_globalObject(vm, this, globalObject, WriteBarrier<JSGlobalObject>::MayBeNull)
155 , m_prototype(vm, this, prototype)
156 , m_classInfo(classInfo)
157 , m_transitionWatchpointSet(IsWatched)
158 , m_offset(invalidOffset)
159 , m_inlineCapacity(inlineCapacity)
160 , m_dictionaryKind(NoneDictionaryKind)
161 , m_hasBeenFlattenedBefore(false)
162 , m_isPinnedPropertyTable(false)
163 , m_hasGetterSetterProperties(classInfo->hasStaticSetterOrReadonlyProperties(vm))
164 , m_hasCustomGetterSetterProperties(false)
165 , m_hasReadOnlyOrGetterSetterPropertiesExcludingProto(classInfo->hasStaticSetterOrReadonlyProperties(vm))
166 , m_hasNonEnumerableProperties(false)
167 , m_attributesInPrevious(0)
168 , m_specificFunctionThrashCount(0)
169 , m_preventExtensions(false)
170 , m_didTransition(false)
171 , m_staticFunctionReified(false)
172 , m_hasRareData(false)
173 {
174 ASSERT(inlineCapacity <= JSFinalObject::maxInlineCapacity());
175 ASSERT(static_cast<PropertyOffset>(inlineCapacity) < firstOutOfLineOffset);
176 ASSERT(!m_hasRareData);
177 ASSERT(hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !m_classInfo->hasStaticSetterOrReadonlyProperties(vm));
178 ASSERT(hasGetterSetterProperties() || !m_classInfo->hasStaticSetterOrReadonlyProperties(vm));
179 }
180
181 const ClassInfo Structure::s_info = { "Structure", 0, 0, 0, CREATE_METHOD_TABLE(Structure) };
182
183 Structure::Structure(VM& vm)
184 : JSCell(CreatingEarlyCell)
185 , m_prototype(vm, this, jsNull())
186 , m_classInfo(info())
187 , m_transitionWatchpointSet(IsWatched)
188 , m_offset(invalidOffset)
189 , m_inlineCapacity(0)
190 , m_dictionaryKind(NoneDictionaryKind)
191 , m_hasBeenFlattenedBefore(false)
192 , m_isPinnedPropertyTable(false)
193 , m_hasGetterSetterProperties(m_classInfo->hasStaticSetterOrReadonlyProperties(vm))
194 , m_hasCustomGetterSetterProperties(false)
195 , m_hasReadOnlyOrGetterSetterPropertiesExcludingProto(m_classInfo->hasStaticSetterOrReadonlyProperties(vm))
196 , m_hasNonEnumerableProperties(false)
197 , m_attributesInPrevious(0)
198 , m_specificFunctionThrashCount(0)
199 , m_preventExtensions(false)
200 , m_didTransition(false)
201 , m_staticFunctionReified(false)
202 , m_hasRareData(false)
203 {
204 TypeInfo typeInfo = TypeInfo(CompoundType, OverridesVisitChildren | StructureIsImmortal);
205 m_blob = StructureIDBlob(vm.heap.structureIDTable().allocateID(this), 0, typeInfo);
206 m_outOfLineTypeFlags = typeInfo.outOfLineTypeFlags();
207
208 ASSERT(hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !m_classInfo->hasStaticSetterOrReadonlyProperties(vm));
209 ASSERT(hasGetterSetterProperties() || !m_classInfo->hasStaticSetterOrReadonlyProperties(vm));
210 }
211
212 Structure::Structure(VM& vm, Structure* previous)
213 : JSCell(vm, vm.structureStructure.get())
214 , m_prototype(vm, this, previous->storedPrototype())
215 , m_classInfo(previous->m_classInfo)
216 , m_transitionWatchpointSet(IsWatched)
217 , m_offset(invalidOffset)
218 , m_inlineCapacity(previous->m_inlineCapacity)
219 , m_dictionaryKind(previous->m_dictionaryKind)
220 , m_hasBeenFlattenedBefore(previous->m_hasBeenFlattenedBefore)
221 , m_isPinnedPropertyTable(false)
222 , m_hasGetterSetterProperties(previous->m_hasGetterSetterProperties)
223 , m_hasCustomGetterSetterProperties(previous->m_hasCustomGetterSetterProperties)
224 , m_hasReadOnlyOrGetterSetterPropertiesExcludingProto(previous->m_hasReadOnlyOrGetterSetterPropertiesExcludingProto)
225 , m_hasNonEnumerableProperties(previous->m_hasNonEnumerableProperties)
226 , m_attributesInPrevious(0)
227 , m_specificFunctionThrashCount(previous->m_specificFunctionThrashCount)
228 , m_preventExtensions(previous->m_preventExtensions)
229 , m_didTransition(true)
230 , m_staticFunctionReified(previous->m_staticFunctionReified)
231 , m_hasRareData(false)
232 {
233 TypeInfo typeInfo = previous->typeInfo();
234 m_blob = StructureIDBlob(vm.heap.structureIDTable().allocateID(this), previous->indexingTypeIncludingHistory(), typeInfo);
235 m_outOfLineTypeFlags = typeInfo.outOfLineTypeFlags();
236
237 ASSERT(!previous->typeInfo().structureIsImmortal());
238 if (previous->m_hasRareData && previous->rareData()->needsCloning())
239 cloneRareDataFrom(vm, previous);
240 setPreviousID(vm, previous);
241
242 previous->notifyTransitionFromThisStructure();
243 if (previous->m_globalObject)
244 m_globalObject.set(vm, this, previous->m_globalObject.get());
245 ASSERT(hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !m_classInfo->hasStaticSetterOrReadonlyProperties(vm));
246 ASSERT(hasGetterSetterProperties() || !m_classInfo->hasStaticSetterOrReadonlyProperties(vm));
247 }
248
249 Structure::~Structure()
250 {
251 if (typeInfo().structureIsImmortal())
252 return;
253 Heap::heap(this)->structureIDTable().deallocateID(this, m_blob.structureID());
254 }
255
256 void Structure::destroy(JSCell* cell)
257 {
258 static_cast<Structure*>(cell)->Structure::~Structure();
259 }
260
261 void Structure::findStructuresAndMapForMaterialization(Vector<Structure*, 8>& structures, Structure*& structure, PropertyTable*& table)
262 {
263 ASSERT(structures.isEmpty());
264 table = 0;
265
266 for (structure = this; structure; structure = structure->previousID()) {
267 structure->m_lock.lock();
268
269 table = structure->propertyTable().get();
270 if (table) {
271 // Leave the structure locked, so that the caller can do things to it atomically
272 // before it loses its property table.
273 return;
274 }
275
276 structures.append(structure);
277 structure->m_lock.unlock();
278 }
279
280 ASSERT(!structure);
281 ASSERT(!table);
282 }
283
284 void Structure::materializePropertyMap(VM& vm)
285 {
286 ASSERT(structure()->classInfo() == info());
287 ASSERT(!propertyTable());
288
289 Vector<Structure*, 8> structures;
290 Structure* structure;
291 PropertyTable* table;
292
293 findStructuresAndMapForMaterialization(structures, structure, table);
294
295 if (table) {
296 table = table->copy(vm, numberOfSlotsForLastOffset(m_offset, m_inlineCapacity));
297 structure->m_lock.unlock();
298 }
299
300 // Must hold the lock on this structure, since we will be modifying this structure's
301 // property map. We don't want getConcurrently() to see the property map in a half-baked
302 // state.
303 GCSafeConcurrentJITLocker locker(m_lock, vm.heap);
304 if (!table)
305 createPropertyMap(locker, vm, numberOfSlotsForLastOffset(m_offset, m_inlineCapacity));
306 else
307 propertyTable().set(vm, this, table);
308
309 for (size_t i = structures.size(); i--;) {
310 structure = structures[i];
311 if (!structure->m_nameInPrevious)
312 continue;
313 PropertyMapEntry entry(vm, this, structure->m_nameInPrevious.get(), structure->m_offset, structure->m_attributesInPrevious, structure->m_specificValueInPrevious.get());
314 propertyTable()->add(entry, m_offset, PropertyTable::PropertyOffsetMustNotChange);
315 }
316
317 checkOffsetConsistency();
318 }
319
320 void Structure::despecifyDictionaryFunction(VM& vm, PropertyName propertyName)
321 {
322 StringImpl* rep = propertyName.uid();
323
324 DeferGC deferGC(vm.heap);
325 materializePropertyMapIfNecessary(vm, deferGC);
326
327 ASSERT(isDictionary());
328 ASSERT(propertyTable());
329
330 PropertyMapEntry* entry = propertyTable()->get(rep);
331 ASSERT(entry);
332 entry->specificValue.clear();
333 }
334
335 Structure* Structure::addPropertyTransitionToExistingStructureImpl(Structure* structure, StringImpl* uid, unsigned attributes, JSCell* specificValue, PropertyOffset& offset)
336 {
337 ASSERT(!structure->isDictionary());
338 ASSERT(structure->isObject());
339
340 if (Structure* existingTransition = structure->m_transitionTable.get(uid, attributes)) {
341 JSCell* specificValueInPrevious = existingTransition->m_specificValueInPrevious.get();
342 if (specificValueInPrevious && specificValueInPrevious != specificValue)
343 return 0;
344 validateOffset(existingTransition->m_offset, existingTransition->inlineCapacity());
345 offset = existingTransition->m_offset;
346 return existingTransition;
347 }
348
349 return 0;
350 }
351
352 Structure* Structure::addPropertyTransitionToExistingStructure(Structure* structure, PropertyName propertyName, unsigned attributes, JSCell* specificValue, PropertyOffset& offset)
353 {
354 ASSERT(!isCompilationThread());
355 return addPropertyTransitionToExistingStructureImpl(structure, propertyName.uid(), attributes, specificValue, offset);
356 }
357
358 Structure* Structure::addPropertyTransitionToExistingStructureConcurrently(Structure* structure, StringImpl* uid, unsigned attributes, JSCell* specificValue, PropertyOffset& offset)
359 {
360 ConcurrentJITLocker locker(structure->m_lock);
361 return addPropertyTransitionToExistingStructureImpl(structure, uid, attributes, specificValue, offset);
362 }
363
364 bool Structure::anyObjectInChainMayInterceptIndexedAccesses() const
365 {
366 for (const Structure* current = this; ;) {
367 if (current->mayInterceptIndexedAccesses())
368 return true;
369
370 JSValue prototype = current->storedPrototype();
371 if (prototype.isNull())
372 return false;
373
374 current = asObject(prototype)->structure();
375 }
376 }
377
378 bool Structure::holesMustForwardToPrototype(VM& vm) const
379 {
380 if (this->mayInterceptIndexedAccesses())
381 return true;
382
383 JSValue prototype = this->storedPrototype();
384 if (!prototype.isObject())
385 return false;
386 JSObject* object = asObject(prototype);
387
388 while (true) {
389 Structure& structure = *object->structure(vm);
390 if (hasIndexedProperties(object->indexingType()) || structure.mayInterceptIndexedAccesses())
391 return true;
392 prototype = structure.storedPrototype();
393 if (!prototype.isObject())
394 return false;
395 object = asObject(prototype);
396 }
397
398 RELEASE_ASSERT_NOT_REACHED();
399 return false;
400 }
401
402 bool Structure::needsSlowPutIndexing() const
403 {
404 return anyObjectInChainMayInterceptIndexedAccesses()
405 || globalObject()->isHavingABadTime();
406 }
407
408 NonPropertyTransition Structure::suggestedArrayStorageTransition() const
409 {
410 if (needsSlowPutIndexing())
411 return AllocateSlowPutArrayStorage;
412
413 return AllocateArrayStorage;
414 }
415
416 Structure* Structure::addPropertyTransition(VM& vm, Structure* structure, PropertyName propertyName, unsigned attributes, JSCell* specificValue, PropertyOffset& offset, PutPropertySlot::Context context)
417 {
418 // If we have a specific function, we may have got to this point if there is
419 // already a transition with the correct property name and attributes, but
420 // specialized to a different function. In this case we just want to give up
421 // and despecialize the transition.
422 // In this case we clear the value of specificFunction which will result
423 // in us adding a non-specific transition, and any subsequent lookup in
424 // Structure::addPropertyTransitionToExistingStructure will just use that.
425 if (specificValue && structure->m_transitionTable.contains(propertyName.uid(), attributes))
426 specificValue = 0;
427
428 ASSERT(!structure->isDictionary());
429 ASSERT(structure->isObject());
430 ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure, propertyName, attributes, specificValue, offset));
431
432 if (structure->m_specificFunctionThrashCount == maxSpecificFunctionThrashCount)
433 specificValue = 0;
434
435 int maxTransitionLength;
436 if (context == PutPropertySlot::PutById)
437 maxTransitionLength = s_maxTransitionLengthForNonEvalPutById;
438 else
439 maxTransitionLength = s_maxTransitionLength;
440 if (structure->transitionCount() > maxTransitionLength) {
441 Structure* transition = toCacheableDictionaryTransition(vm, structure);
442 ASSERT(structure != transition);
443 offset = transition->putSpecificValue(vm, propertyName, attributes, specificValue);
444 return transition;
445 }
446
447 Structure* transition = create(vm, structure);
448
449 transition->m_cachedPrototypeChain.setMayBeNull(vm, transition, structure->m_cachedPrototypeChain.get());
450 transition->m_nameInPrevious = propertyName.uid();
451 transition->m_attributesInPrevious = attributes;
452 transition->m_specificValueInPrevious.setMayBeNull(vm, transition, specificValue);
453 transition->propertyTable().set(vm, transition, structure->takePropertyTableOrCloneIfPinned(vm));
454 transition->m_offset = structure->m_offset;
455
456 offset = transition->putSpecificValue(vm, propertyName, attributes, specificValue);
457
458 checkOffset(transition->m_offset, transition->inlineCapacity());
459 {
460 ConcurrentJITLocker locker(structure->m_lock);
461 structure->m_transitionTable.add(vm, transition);
462 }
463 transition->checkOffsetConsistency();
464 structure->checkOffsetConsistency();
465 return transition;
466 }
467
468 Structure* Structure::removePropertyTransition(VM& vm, Structure* structure, PropertyName propertyName, PropertyOffset& offset)
469 {
470 ASSERT(!structure->isUncacheableDictionary());
471
472 Structure* transition = toUncacheableDictionaryTransition(vm, structure);
473
474 offset = transition->remove(propertyName);
475
476 transition->checkOffsetConsistency();
477 return transition;
478 }
479
480 Structure* Structure::changePrototypeTransition(VM& vm, Structure* structure, JSValue prototype)
481 {
482 Structure* transition = create(vm, structure);
483
484 transition->m_prototype.set(vm, transition, prototype);
485
486 DeferGC deferGC(vm.heap);
487 structure->materializePropertyMapIfNecessary(vm, deferGC);
488 transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm));
489 transition->m_offset = structure->m_offset;
490 transition->pin();
491
492 transition->checkOffsetConsistency();
493 return transition;
494 }
495
496 Structure* Structure::despecifyFunctionTransition(VM& vm, Structure* structure, PropertyName replaceFunction)
497 {
498 ASSERT(structure->m_specificFunctionThrashCount < maxSpecificFunctionThrashCount);
499 Structure* transition = create(vm, structure);
500
501 ++transition->m_specificFunctionThrashCount;
502
503 DeferGC deferGC(vm.heap);
504 structure->materializePropertyMapIfNecessary(vm, deferGC);
505 transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm));
506 transition->m_offset = structure->m_offset;
507 transition->pin();
508
509 if (transition->m_specificFunctionThrashCount == maxSpecificFunctionThrashCount)
510 transition->despecifyAllFunctions(vm);
511 else {
512 bool removed = transition->despecifyFunction(vm, replaceFunction);
513 ASSERT_UNUSED(removed, removed);
514 }
515
516 transition->checkOffsetConsistency();
517 return transition;
518 }
519
520 Structure* Structure::attributeChangeTransition(VM& vm, Structure* structure, PropertyName propertyName, unsigned attributes)
521 {
522 DeferGC deferGC(vm.heap);
523 if (!structure->isUncacheableDictionary()) {
524 Structure* transition = create(vm, structure);
525
526 structure->materializePropertyMapIfNecessary(vm, deferGC);
527 transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm));
528 transition->m_offset = structure->m_offset;
529 transition->pin();
530
531 structure = transition;
532 }
533
534 ASSERT(structure->propertyTable());
535 PropertyMapEntry* entry = structure->propertyTable()->get(propertyName.uid());
536 ASSERT(entry);
537 entry->attributes = attributes;
538
539 structure->checkOffsetConsistency();
540 return structure;
541 }
542
543 Structure* Structure::toDictionaryTransition(VM& vm, Structure* structure, DictionaryKind kind)
544 {
545 ASSERT(!structure->isUncacheableDictionary());
546
547 Structure* transition = create(vm, structure);
548
549 DeferGC deferGC(vm.heap);
550 structure->materializePropertyMapIfNecessary(vm, deferGC);
551 transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm));
552 transition->m_offset = structure->m_offset;
553 transition->m_dictionaryKind = kind;
554 transition->pin();
555
556 transition->checkOffsetConsistency();
557 return transition;
558 }
559
560 Structure* Structure::toCacheableDictionaryTransition(VM& vm, Structure* structure)
561 {
562 return toDictionaryTransition(vm, structure, CachedDictionaryKind);
563 }
564
565 Structure* Structure::toUncacheableDictionaryTransition(VM& vm, Structure* structure)
566 {
567 return toDictionaryTransition(vm, structure, UncachedDictionaryKind);
568 }
569
570 // In future we may want to cache this transition.
571 Structure* Structure::sealTransition(VM& vm, Structure* structure)
572 {
573 Structure* transition = preventExtensionsTransition(vm, structure);
574
575 if (transition->propertyTable()) {
576 PropertyTable::iterator end = transition->propertyTable()->end();
577 for (PropertyTable::iterator iter = transition->propertyTable()->begin(); iter != end; ++iter)
578 iter->attributes |= DontDelete;
579 }
580
581 transition->checkOffsetConsistency();
582 return transition;
583 }
584
585 // In future we may want to cache this transition.
586 Structure* Structure::freezeTransition(VM& vm, Structure* structure)
587 {
588 Structure* transition = preventExtensionsTransition(vm, structure);
589
590 if (transition->propertyTable()) {
591 PropertyTable::iterator iter = transition->propertyTable()->begin();
592 PropertyTable::iterator end = transition->propertyTable()->end();
593 if (iter != end)
594 transition->m_hasReadOnlyOrGetterSetterPropertiesExcludingProto = true;
595 for (; iter != end; ++iter)
596 iter->attributes |= iter->attributes & Accessor ? DontDelete : (DontDelete | ReadOnly);
597 }
598
599 ASSERT(transition->hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !transition->classInfo()->hasStaticSetterOrReadonlyProperties(vm));
600 ASSERT(transition->hasGetterSetterProperties() || !transition->classInfo()->hasStaticSetterOrReadonlyProperties(vm));
601 transition->checkOffsetConsistency();
602 return transition;
603 }
604
605 // In future we may want to cache this transition.
606 Structure* Structure::preventExtensionsTransition(VM& vm, Structure* structure)
607 {
608 Structure* transition = create(vm, structure);
609
610 // Don't set m_offset, as one can not transition to this.
611
612 DeferGC deferGC(vm.heap);
613 structure->materializePropertyMapIfNecessary(vm, deferGC);
614 transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm));
615 transition->m_offset = structure->m_offset;
616 transition->m_preventExtensions = true;
617 transition->pin();
618
619 transition->checkOffsetConsistency();
620 return transition;
621 }
622
623 PropertyTable* Structure::takePropertyTableOrCloneIfPinned(VM& vm)
624 {
625 DeferGC deferGC(vm.heap);
626 materializePropertyMapIfNecessaryForPinning(vm, deferGC);
627
628 if (m_isPinnedPropertyTable)
629 return propertyTable()->copy(vm, propertyTable()->size() + 1);
630
631 // Hold the lock while stealing the table - so that getConcurrently() on another thread
632 // will either have to bypass this structure, or will get to use the property table
633 // before it is stolen.
634 ConcurrentJITLocker locker(m_lock);
635 PropertyTable* takenPropertyTable = propertyTable().get();
636 propertyTable().clear();
637 return takenPropertyTable;
638 }
639
640 Structure* Structure::nonPropertyTransition(VM& vm, Structure* structure, NonPropertyTransition transitionKind)
641 {
642 unsigned attributes = toAttributes(transitionKind);
643 IndexingType indexingType = newIndexingType(structure->indexingTypeIncludingHistory(), transitionKind);
644
645 if (JSGlobalObject* globalObject = structure->m_globalObject.get()) {
646 if (globalObject->isOriginalArrayStructure(structure)) {
647 Structure* result = globalObject->originalArrayStructureForIndexingType(indexingType);
648 if (result->indexingTypeIncludingHistory() == indexingType) {
649 structure->notifyTransitionFromThisStructure();
650 return result;
651 }
652 }
653 }
654
655 Structure* existingTransition;
656 if (!structure->isDictionary() && (existingTransition = structure->m_transitionTable.get(0, attributes))) {
657 ASSERT(existingTransition->m_attributesInPrevious == attributes);
658 ASSERT(existingTransition->indexingTypeIncludingHistory() == indexingType);
659 return existingTransition;
660 }
661
662 Structure* transition = create(vm, structure);
663 transition->m_attributesInPrevious = attributes;
664 transition->m_blob.setIndexingType(indexingType);
665 transition->propertyTable().set(vm, transition, structure->takePropertyTableOrCloneIfPinned(vm));
666 transition->m_offset = structure->m_offset;
667 checkOffset(transition->m_offset, transition->inlineCapacity());
668
669 if (structure->isDictionary())
670 transition->pin();
671 else {
672 ConcurrentJITLocker locker(structure->m_lock);
673 structure->m_transitionTable.add(vm, transition);
674 }
675 transition->checkOffsetConsistency();
676 return transition;
677 }
678
679 // In future we may want to cache this property.
680 bool Structure::isSealed(VM& vm)
681 {
682 if (isExtensible())
683 return false;
684
685 DeferGC deferGC(vm.heap);
686 materializePropertyMapIfNecessary(vm, deferGC);
687 if (!propertyTable())
688 return true;
689
690 PropertyTable::iterator end = propertyTable()->end();
691 for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter) {
692 if ((iter->attributes & DontDelete) != DontDelete)
693 return false;
694 }
695 return true;
696 }
697
698 // In future we may want to cache this property.
699 bool Structure::isFrozen(VM& vm)
700 {
701 if (isExtensible())
702 return false;
703
704 DeferGC deferGC(vm.heap);
705 materializePropertyMapIfNecessary(vm, deferGC);
706 if (!propertyTable())
707 return true;
708
709 PropertyTable::iterator end = propertyTable()->end();
710 for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter) {
711 if (!(iter->attributes & DontDelete))
712 return false;
713 if (!(iter->attributes & (ReadOnly | Accessor)))
714 return false;
715 }
716 return true;
717 }
718
719 Structure* Structure::flattenDictionaryStructure(VM& vm, JSObject* object)
720 {
721 checkOffsetConsistency();
722 ASSERT(isDictionary());
723
724 size_t beforeOutOfLineCapacity = this->outOfLineCapacity();
725 if (isUncacheableDictionary()) {
726 ASSERT(propertyTable());
727
728 size_t propertyCount = propertyTable()->size();
729
730 // Holds our values compacted by insertion order.
731 Vector<JSValue> values(propertyCount);
732
733 // Copies out our values from their hashed locations, compacting property table offsets as we go.
734 unsigned i = 0;
735 PropertyTable::iterator end = propertyTable()->end();
736 m_offset = invalidOffset;
737 for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter, ++i) {
738 values[i] = object->getDirect(iter->offset);
739 m_offset = iter->offset = offsetForPropertyNumber(i, m_inlineCapacity);
740 }
741
742 // Copies in our values to their compacted locations.
743 for (unsigned i = 0; i < propertyCount; i++)
744 object->putDirect(vm, offsetForPropertyNumber(i, m_inlineCapacity), values[i]);
745
746 propertyTable()->clearDeletedOffsets();
747 checkOffsetConsistency();
748 }
749
750 m_dictionaryKind = NoneDictionaryKind;
751 m_hasBeenFlattenedBefore = true;
752
753 size_t afterOutOfLineCapacity = this->outOfLineCapacity();
754
755 if (beforeOutOfLineCapacity != afterOutOfLineCapacity) {
756 ASSERT(beforeOutOfLineCapacity > afterOutOfLineCapacity);
757 // If the object had a Butterfly but after flattening/compacting we no longer have need of it,
758 // we need to zero it out because the collector depends on the Structure to know the size for copying.
759 if (object->butterfly() && !afterOutOfLineCapacity && !this->hasIndexingHeader(object))
760 object->setStructureAndButterfly(vm, this, 0);
761 // If the object was down-sized to the point where the base of the Butterfly is no longer within the
762 // first CopiedBlock::blockSize bytes, we'll get the wrong answer if we try to mask the base back to
763 // the CopiedBlock header. To prevent this case we need to memmove the Butterfly down.
764 else if (object->butterfly())
765 object->shiftButterflyAfterFlattening(vm, beforeOutOfLineCapacity, afterOutOfLineCapacity);
766 }
767
768 return this;
769 }
770
771 PropertyOffset Structure::addPropertyWithoutTransition(VM& vm, PropertyName propertyName, unsigned attributes, JSCell* specificValue)
772 {
773 ASSERT(!enumerationCache());
774
775 if (m_specificFunctionThrashCount == maxSpecificFunctionThrashCount)
776 specificValue = 0;
777
778 DeferGC deferGC(vm.heap);
779 materializePropertyMapIfNecessaryForPinning(vm, deferGC);
780
781 pin();
782
783 return putSpecificValue(vm, propertyName, attributes, specificValue);
784 }
785
786 PropertyOffset Structure::removePropertyWithoutTransition(VM& vm, PropertyName propertyName)
787 {
788 ASSERT(isUncacheableDictionary());
789 ASSERT(!enumerationCache());
790
791 DeferGC deferGC(vm.heap);
792 materializePropertyMapIfNecessaryForPinning(vm, deferGC);
793
794 pin();
795 return remove(propertyName);
796 }
797
798 void Structure::pin()
799 {
800 ASSERT(propertyTable());
801 m_isPinnedPropertyTable = true;
802 clearPreviousID();
803 m_nameInPrevious.clear();
804 }
805
806 void Structure::allocateRareData(VM& vm)
807 {
808 ASSERT(!m_hasRareData);
809 StructureRareData* rareData = StructureRareData::create(vm, previous());
810 m_previousOrRareData.set(vm, this, rareData);
811 m_hasRareData = true;
812 ASSERT(m_hasRareData);
813 }
814
815 void Structure::cloneRareDataFrom(VM& vm, const Structure* other)
816 {
817 ASSERT(!m_hasRareData);
818 ASSERT(other->m_hasRareData);
819 StructureRareData* newRareData = StructureRareData::clone(vm, other->rareData());
820 m_previousOrRareData.set(vm, this, newRareData);
821 m_hasRareData = true;
822 ASSERT(m_hasRareData);
823 }
824
825 #if DUMP_PROPERTYMAP_STATS
826
827 PropertyMapHashTableStats* propertyMapHashTableStats = 0;
828
829 struct PropertyMapStatisticsExitLogger {
830 PropertyMapStatisticsExitLogger();
831 ~PropertyMapStatisticsExitLogger();
832 };
833
834 DEFINE_GLOBAL_FOR_LOGGING(PropertyMapStatisticsExitLogger, logger, );
835
836 PropertyMapStatisticsExitLogger::PropertyMapStatisticsExitLogger()
837 {
838 propertyMapHashTableStats = adoptPtr(new PropertyMapHashTableStats()).leakPtr();
839 }
840
841 PropertyMapStatisticsExitLogger::~PropertyMapStatisticsExitLogger()
842 {
843 unsigned finds = propertyMapHashTableStats->numFinds;
844 unsigned collisions = propertyMapHashTableStats->numCollisions;
845 dataLogF("\nJSC::PropertyMap statistics for process %d\n\n", getCurrentProcessID());
846 dataLogF("%d finds\n", finds);
847 dataLogF("%d collisions (%.1f%%)\n", collisions, 100.0 * collisions / finds);
848 dataLogF("%d lookups\n", propertyMapHashTableStats->numLookups.load());
849 dataLogF("%d lookup probings\n", propertyMapHashTableStats->numLookupProbing.load());
850 dataLogF("%d adds\n", propertyMapHashTableStats->numAdds.load());
851 dataLogF("%d removes\n", propertyMapHashTableStats->numRemoves.load());
852 dataLogF("%d rehashes\n", propertyMapHashTableStats->numRehashes.load());
853 dataLogF("%d reinserts\n", propertyMapHashTableStats->numReinserts.load());
854 }
855
856 #endif
857
858 #if !DO_PROPERTYMAP_CONSTENCY_CHECK
859
860 inline void Structure::checkConsistency()
861 {
862 checkOffsetConsistency();
863 }
864
865 #endif
866
867 PropertyTable* Structure::copyPropertyTable(VM& vm)
868 {
869 if (!propertyTable())
870 return 0;
871 return PropertyTable::clone(vm, *propertyTable().get());
872 }
873
874 PropertyTable* Structure::copyPropertyTableForPinning(VM& vm)
875 {
876 if (propertyTable())
877 return PropertyTable::clone(vm, *propertyTable().get());
878 return PropertyTable::create(vm, numberOfSlotsForLastOffset(m_offset, m_inlineCapacity));
879 }
880
881 PropertyOffset Structure::getConcurrently(VM&, StringImpl* uid, unsigned& attributes, JSCell*& specificValue)
882 {
883 Vector<Structure*, 8> structures;
884 Structure* structure;
885 PropertyTable* table;
886
887 findStructuresAndMapForMaterialization(structures, structure, table);
888
889 if (table) {
890 PropertyMapEntry* entry = table->get(uid);
891 if (entry) {
892 attributes = entry->attributes;
893 specificValue = entry->specificValue.get();
894 PropertyOffset result = entry->offset;
895 structure->m_lock.unlock();
896 return result;
897 }
898 structure->m_lock.unlock();
899 }
900
901 for (unsigned i = structures.size(); i--;) {
902 structure = structures[i];
903 if (structure->m_nameInPrevious.get() != uid)
904 continue;
905
906 attributes = structure->m_attributesInPrevious;
907 specificValue = structure->m_specificValueInPrevious.get();
908 return structure->m_offset;
909 }
910
911 return invalidOffset;
912 }
913
914 bool Structure::despecifyFunction(VM& vm, PropertyName propertyName)
915 {
916 DeferGC deferGC(vm.heap);
917 materializePropertyMapIfNecessary(vm, deferGC);
918 if (!propertyTable())
919 return false;
920
921 PropertyMapEntry* entry = propertyTable()->get(propertyName.uid());
922 if (!entry)
923 return false;
924
925 ASSERT(entry->specificValue);
926 entry->specificValue.clear();
927 return true;
928 }
929
930 void Structure::despecifyAllFunctions(VM& vm)
931 {
932 DeferGC deferGC(vm.heap);
933 materializePropertyMapIfNecessary(vm, deferGC);
934 if (!propertyTable())
935 return;
936
937 PropertyTable::iterator end = propertyTable()->end();
938 for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter)
939 iter->specificValue.clear();
940 }
941
942 PropertyOffset Structure::putSpecificValue(VM& vm, PropertyName propertyName, unsigned attributes, JSCell* specificValue)
943 {
944 GCSafeConcurrentJITLocker locker(m_lock, vm.heap);
945
946 ASSERT(!JSC::isValidOffset(get(vm, propertyName)));
947
948 checkConsistency();
949 if (attributes & DontEnum)
950 m_hasNonEnumerableProperties = true;
951
952 StringImpl* rep = propertyName.uid();
953
954 if (!propertyTable())
955 createPropertyMap(locker, vm);
956
957 PropertyOffset newOffset = propertyTable()->nextOffset(m_inlineCapacity);
958
959 propertyTable()->add(PropertyMapEntry(vm, propertyTable().get(), rep, newOffset, attributes, specificValue), m_offset, PropertyTable::PropertyOffsetMayChange);
960
961 checkConsistency();
962 return newOffset;
963 }
964
965 PropertyOffset Structure::remove(PropertyName propertyName)
966 {
967 ConcurrentJITLocker locker(m_lock);
968
969 checkConsistency();
970
971 StringImpl* rep = propertyName.uid();
972
973 if (!propertyTable())
974 return invalidOffset;
975
976 PropertyTable::find_iterator position = propertyTable()->find(rep);
977 if (!position.first)
978 return invalidOffset;
979
980 PropertyOffset offset = position.first->offset;
981
982 propertyTable()->remove(position);
983 propertyTable()->addDeletedOffset(offset);
984
985 checkConsistency();
986 return offset;
987 }
988
989 void Structure::createPropertyMap(const GCSafeConcurrentJITLocker&, VM& vm, unsigned capacity)
990 {
991 ASSERT(!propertyTable());
992
993 checkConsistency();
994 propertyTable().set(vm, this, PropertyTable::create(vm, capacity));
995 }
996
997 void Structure::getPropertyNamesFromStructure(VM& vm, PropertyNameArray& propertyNames, EnumerationMode mode)
998 {
999 DeferGC deferGC(vm.heap);
1000 materializePropertyMapIfNecessary(vm, deferGC);
1001 if (!propertyTable())
1002 return;
1003
1004 bool knownUnique = !propertyNames.size();
1005
1006 PropertyTable::iterator end = propertyTable()->end();
1007 for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter) {
1008 ASSERT(m_hasNonEnumerableProperties || !(iter->attributes & DontEnum));
1009 if (!iter->key->isEmptyUnique() && (!(iter->attributes & DontEnum) || mode == IncludeDontEnumProperties)) {
1010 if (knownUnique)
1011 propertyNames.addKnownUnique(iter->key);
1012 else
1013 propertyNames.add(iter->key);
1014 }
1015 }
1016 }
1017
1018 JSValue Structure::prototypeForLookup(CodeBlock* codeBlock) const
1019 {
1020 return prototypeForLookup(codeBlock->globalObject());
1021 }
1022
1023 void Structure::visitChildren(JSCell* cell, SlotVisitor& visitor)
1024 {
1025 Structure* thisObject = jsCast<Structure*>(cell);
1026 ASSERT_GC_OBJECT_INHERITS(thisObject, info());
1027 ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren());
1028
1029 JSCell::visitChildren(thisObject, visitor);
1030 visitor.append(&thisObject->m_globalObject);
1031 if (!thisObject->isObject())
1032 thisObject->m_cachedPrototypeChain.clear();
1033 else {
1034 visitor.append(&thisObject->m_prototype);
1035 visitor.append(&thisObject->m_cachedPrototypeChain);
1036 }
1037 visitor.append(&thisObject->m_previousOrRareData);
1038 visitor.append(&thisObject->m_specificValueInPrevious);
1039
1040 if (thisObject->m_isPinnedPropertyTable) {
1041 ASSERT(thisObject->m_propertyTableUnsafe);
1042 visitor.append(&thisObject->m_propertyTableUnsafe);
1043 } else if (thisObject->m_propertyTableUnsafe)
1044 thisObject->m_propertyTableUnsafe.clear();
1045 }
1046
1047 bool Structure::prototypeChainMayInterceptStoreTo(VM& vm, PropertyName propertyName)
1048 {
1049 unsigned i = propertyName.asIndex();
1050 if (i != PropertyName::NotAnIndex)
1051 return anyObjectInChainMayInterceptIndexedAccesses();
1052
1053 for (Structure* current = this; ;) {
1054 JSValue prototype = current->storedPrototype();
1055 if (prototype.isNull())
1056 return false;
1057
1058 current = prototype.asCell()->structure(vm);
1059
1060 unsigned attributes;
1061 JSCell* specificValue;
1062 PropertyOffset offset = current->get(vm, propertyName, attributes, specificValue);
1063 if (!JSC::isValidOffset(offset))
1064 continue;
1065
1066 if (attributes & (ReadOnly | Accessor))
1067 return true;
1068
1069 return false;
1070 }
1071 }
1072
1073 void Structure::dump(PrintStream& out) const
1074 {
1075 out.print(RawPointer(this), ":[", classInfo()->className, ", {");
1076
1077 Vector<Structure*, 8> structures;
1078 Structure* structure;
1079 PropertyTable* table;
1080
1081 const_cast<Structure*>(this)->findStructuresAndMapForMaterialization(
1082 structures, structure, table);
1083
1084 CommaPrinter comma;
1085
1086 if (table) {
1087 PropertyTable::iterator iter = table->begin();
1088 PropertyTable::iterator end = table->end();
1089 for (; iter != end; ++iter) {
1090 out.print(comma, iter->key, ":", static_cast<int>(iter->offset));
1091 if (iter->specificValue) {
1092 DumpContext dummyContext;
1093 out.print("=>", RawPointer(iter->specificValue.get()));
1094 }
1095 }
1096
1097 structure->m_lock.unlock();
1098 }
1099
1100 for (unsigned i = structures.size(); i--;) {
1101 Structure* structure = structures[i];
1102 if (!structure->m_nameInPrevious)
1103 continue;
1104 out.print(comma, structure->m_nameInPrevious.get(), ":", static_cast<int>(structure->m_offset));
1105 if (structure->m_specificValueInPrevious) {
1106 DumpContext dummyContext;
1107 out.print("=>", RawPointer(structure->m_specificValueInPrevious.get()));
1108 }
1109 }
1110
1111 out.print("}, ", IndexingTypeDump(indexingType()));
1112
1113 if (m_prototype.get().isCell())
1114 out.print(", Proto:", RawPointer(m_prototype.get().asCell()));
1115
1116 out.print("]");
1117 }
1118
1119 void Structure::dumpInContext(PrintStream& out, DumpContext* context) const
1120 {
1121 if (context)
1122 context->structures.dumpBrief(this, out);
1123 else
1124 dump(out);
1125 }
1126
1127 void Structure::dumpBrief(PrintStream& out, const CString& string) const
1128 {
1129 out.print("%", string, ":", classInfo()->className);
1130 }
1131
1132 void Structure::dumpContextHeader(PrintStream& out)
1133 {
1134 out.print("Structures:");
1135 }
1136
1137 #if DO_PROPERTYMAP_CONSTENCY_CHECK
1138
1139 void PropertyTable::checkConsistency()
1140 {
1141 checkOffsetConsistency();
1142 ASSERT(m_indexSize >= PropertyTable::MinimumTableSize);
1143 ASSERT(m_indexMask);
1144 ASSERT(m_indexSize == m_indexMask + 1);
1145 ASSERT(!(m_indexSize & m_indexMask));
1146
1147 ASSERT(m_keyCount <= m_indexSize / 2);
1148 ASSERT(m_keyCount + m_deletedCount <= m_indexSize / 2);
1149 ASSERT(m_deletedCount <= m_indexSize / 4);
1150
1151 unsigned indexCount = 0;
1152 unsigned deletedIndexCount = 0;
1153 for (unsigned a = 0; a != m_indexSize; ++a) {
1154 unsigned entryIndex = m_index[a];
1155 if (entryIndex == PropertyTable::EmptyEntryIndex)
1156 continue;
1157 if (entryIndex == deletedEntryIndex()) {
1158 ++deletedIndexCount;
1159 continue;
1160 }
1161 ASSERT(entryIndex < deletedEntryIndex());
1162 ASSERT(entryIndex - 1 <= usedCount());
1163 ++indexCount;
1164
1165 for (unsigned b = a + 1; b != m_indexSize; ++b)
1166 ASSERT(m_index[b] != entryIndex);
1167 }
1168 ASSERT(indexCount == m_keyCount);
1169 ASSERT(deletedIndexCount == m_deletedCount);
1170
1171 ASSERT(!table()[deletedEntryIndex() - 1].key);
1172
1173 unsigned nonEmptyEntryCount = 0;
1174 for (unsigned c = 0; c < usedCount(); ++c) {
1175 StringImpl* rep = table()[c].key;
1176 if (rep == PROPERTY_MAP_DELETED_ENTRY_KEY)
1177 continue;
1178 ++nonEmptyEntryCount;
1179 unsigned i = rep->existingHash();
1180 unsigned k = 0;
1181 unsigned entryIndex;
1182 while (1) {
1183 entryIndex = m_index[i & m_indexMask];
1184 ASSERT(entryIndex != PropertyTable::EmptyEntryIndex);
1185 if (rep == table()[entryIndex - 1].key)
1186 break;
1187 if (k == 0)
1188 k = 1 | doubleHash(rep->existingHash());
1189 i += k;
1190 }
1191 ASSERT(entryIndex == c + 1);
1192 }
1193
1194 ASSERT(nonEmptyEntryCount == m_keyCount);
1195 }
1196
1197 void Structure::checkConsistency()
1198 {
1199 if (!propertyTable())
1200 return;
1201
1202 if (!m_hasNonEnumerableProperties) {
1203 PropertyTable::iterator end = propertyTable()->end();
1204 for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter) {
1205 ASSERT(!(iter->attributes & DontEnum));
1206 }
1207 }
1208
1209 propertyTable()->checkConsistency();
1210 }
1211
1212 #endif // DO_PROPERTYMAP_CONSTENCY_CHECK
1213
1214 bool ClassInfo::hasStaticSetterOrReadonlyProperties(VM& vm) const
1215 {
1216 for (const ClassInfo* ci = this; ci; ci = ci->parentClass) {
1217 if (const HashTable* table = ci->propHashTable(vm)) {
1218 if (table->hasSetterOrReadonlyProperties)
1219 return true;
1220 }
1221 }
1222 return false;
1223 }
1224
1225 } // namespace JSC