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 68 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 77 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 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 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 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 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 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 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 240 Structure::~Structure() 241 { 242 } 243 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 276 void Structure::growPropertyStorageCapacity() 277 { 278 if (isUsingInlineStorage()) 279 m_propertyStorageCapacity = JSObject::baseExternalStorageCapacity; 280 else 281 m_propertyStorageCapacity *= 2; 282 } 283 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 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 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 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 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 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 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 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 461 Structure* Structure::toCacheableDictionaryTransition(JSGlobalData& globalData, Structure* structure) 462 { 463 return toDictionaryTransition(globalData, structure, CachedDictionaryKind); 464 } 465 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. 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. 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. 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. 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. 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 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 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 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 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 632 inline void Structure::checkConsistency() 633 { 634 } 635 636 #endif 637 638 PropertyTable* Structure::copyPropertyTable(JSGlobalData& globalData, Structure* owner) 639 { 640 return m_propertyTable ? new PropertyTable(globalData, owner, *m_propertyTable) : 0; 641 } 642 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 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 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 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 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 739 void Structure::createPropertyMap(unsigned capacity) 740 { 741 ASSERT(!m_propertyTable); 742 743 checkConsistency(); 744 m_propertyTable = new PropertyTable(capacity); 745 checkConsistency(); 746 } 747 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 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 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 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