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 53using namespace std; 54using namespace WTF; 55 56namespace JSC { 57 58#if DUMP_STRUCTURE_ID_STATISTICS 59static HashSet<Structure*>& liveStructureSet = *(new HashSet<Structure*>); 60#endif 61 62bool 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 71inline 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 80inline 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 105void 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 150Structure::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 181const ClassInfo Structure::s_info = { "Structure", 0, 0, 0, CREATE_METHOD_TABLE(Structure) }; 182 183Structure::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 212Structure::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 249Structure::~Structure() 250{ 251 if (typeInfo().structureIsImmortal()) 252 return; 253 Heap::heap(this)->structureIDTable().deallocateID(this, m_blob.structureID()); 254} 255 256void Structure::destroy(JSCell* cell) 257{ 258 static_cast<Structure*>(cell)->Structure::~Structure(); 259} 260 261void 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 284void 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 320void 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 335Structure* 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 352Structure* 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 358Structure* 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 364bool 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 378bool 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 402bool Structure::needsSlowPutIndexing() const 403{ 404 return anyObjectInChainMayInterceptIndexedAccesses() 405 || globalObject()->isHavingABadTime(); 406} 407 408NonPropertyTransition Structure::suggestedArrayStorageTransition() const 409{ 410 if (needsSlowPutIndexing()) 411 return AllocateSlowPutArrayStorage; 412 413 return AllocateArrayStorage; 414} 415 416Structure* 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 468Structure* 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 480Structure* 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 496Structure* 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 520Structure* 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 543Structure* 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 560Structure* Structure::toCacheableDictionaryTransition(VM& vm, Structure* structure) 561{ 562 return toDictionaryTransition(vm, structure, CachedDictionaryKind); 563} 564 565Structure* 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. 571Structure* 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. 586Structure* 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. 606Structure* 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 623PropertyTable* 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 640Structure* 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. 680bool 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. 699bool 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 719Structure* 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 771PropertyOffset 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 786PropertyOffset 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 798void Structure::pin() 799{ 800 ASSERT(propertyTable()); 801 m_isPinnedPropertyTable = true; 802 clearPreviousID(); 803 m_nameInPrevious.clear(); 804} 805 806void 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 815void 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 827PropertyMapHashTableStats* propertyMapHashTableStats = 0; 828 829struct PropertyMapStatisticsExitLogger { 830 PropertyMapStatisticsExitLogger(); 831 ~PropertyMapStatisticsExitLogger(); 832}; 833 834DEFINE_GLOBAL_FOR_LOGGING(PropertyMapStatisticsExitLogger, logger, ); 835 836PropertyMapStatisticsExitLogger::PropertyMapStatisticsExitLogger() 837{ 838 propertyMapHashTableStats = adoptPtr(new PropertyMapHashTableStats()).leakPtr(); 839} 840 841PropertyMapStatisticsExitLogger::~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 860inline void Structure::checkConsistency() 861{ 862 checkOffsetConsistency(); 863} 864 865#endif 866 867PropertyTable* Structure::copyPropertyTable(VM& vm) 868{ 869 if (!propertyTable()) 870 return 0; 871 return PropertyTable::clone(vm, *propertyTable().get()); 872} 873 874PropertyTable* 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 881PropertyOffset 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 914bool 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 930void 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 942PropertyOffset 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 965PropertyOffset 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 989void 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 997void 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 1018JSValue Structure::prototypeForLookup(CodeBlock* codeBlock) const 1019{ 1020 return prototypeForLookup(codeBlock->globalObject()); 1021} 1022 1023void 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 1047bool 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 1073void 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 1119void Structure::dumpInContext(PrintStream& out, DumpContext* context) const 1120{ 1121 if (context) 1122 context->structures.dumpBrief(this, out); 1123 else 1124 dump(out); 1125} 1126 1127void Structure::dumpBrief(PrintStream& out, const CString& string) const 1128{ 1129 out.print("%", string, ":", classInfo()->className); 1130} 1131 1132void Structure::dumpContextHeader(PrintStream& out) 1133{ 1134 out.print("Structures:"); 1135} 1136 1137#if DO_PROPERTYMAP_CONSTENCY_CHECK 1138 1139void 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 1197void 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 1214bool 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 1226