Changeset 38016 in webkit for trunk/JavaScriptCore/runtime/StructureID.cpp
- Timestamp:
- Oct 30, 2008, 5:12:50 PM (17 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/JavaScriptCore/runtime/StructureID.cpp
r37989 r38016 38 38 #endif 39 39 40 #ifndef NDEBUG 41 #define DO_PROPERTYMAP_CONSTENCY_CHECK 0 42 #else 43 #define DO_PROPERTYMAP_CONSTENCY_CHECK 0 44 #endif 45 40 46 using namespace std; 47 using WTF::doubleHash; 41 48 42 49 namespace JSC { 50 51 // Choose a number for the following so that most property maps are smaller, 52 // but it's not going to blow out the stack to allocate this number of pointers. 53 static const int smallMapThreshold = 1024; 54 55 // The point at which the function call overhead of the qsort implementation 56 // becomes small compared to the inefficiency of insertion sort. 57 static const unsigned tinyMapThreshold = 20; 43 58 44 59 #ifndef NDEBUG … … 76 91 } 77 92 78 totalPropertyMapsSize += structureID->m_propertyMap.propertyMapSize(); 93 if (structureID->m_propertyTable) 94 totalPropertyMapsSize += PropertyMapHashTable::allocationSize(m_propertyTable->size);; 79 95 } 80 96 … … 97 113 , m_nameInPrevious(0) 98 114 , m_transitionCount(0) 115 , m_propertyTable(0) 99 116 , m_propertyStorageCapacity(JSObject::inlineStorageCapacity) 100 117 , m_cachedTransistionOffset(WTF::notFound) … … 141 158 delete m_transitions.table; 142 159 160 if (m_propertyTable) { 161 unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; 162 for (unsigned i = 1; i <= entryCount; i++) { 163 if (UString::Rep* key = m_propertyTable->entries()[i].key) 164 key->deref(); 165 } 166 fastFree(m_propertyTable); 167 } 168 143 169 #ifndef NDEBUG 144 170 #if ENABLE(JSC_MULTIPLE_THREADS) … … 185 211 } 186 212 187 m_propertyMap.getEnumerablePropertyNames(propertyNames);213 getEnumerablePropertyNamesInternal(propertyNames); 188 214 189 215 // Add properties from the static hashtables of properties … … 256 282 if (structureID->m_transitionCount > s_maxTransitionLength) { 257 283 RefPtr<StructureID> transition = toDictionaryTransition(structureID); 258 offset = transition-> m_propertyMap.put(propertyName, attributes);259 if (transition-> m_propertyMap.storageSize() > transition->propertyStorageCapacity())284 offset = transition->put(propertyName, attributes); 285 if (transition->propertyStorageSize() > transition->propertyStorageCapacity()) 260 286 transition->growPropertyStorageCapacity(); 261 287 return transition.release(); … … 268 294 transition->m_attributesInPrevious = attributes; 269 295 transition->m_transitionCount = structureID->m_transitionCount + 1; 270 transition->m_propertyMap = structureID->m_propertyMap; 296 transition->m_propertyTable = structureID->copyPropertyTable(); 297 transition->m_deletedOffsets = structureID->m_deletedOffsets; 271 298 transition->m_propertyStorageCapacity = structureID->m_propertyStorageCapacity; 272 299 transition->m_hasGetterSetterProperties = structureID->m_hasGetterSetterProperties; 273 300 274 offset = transition-> m_propertyMap.put(propertyName, attributes);275 if (transition-> m_propertyMap.storageSize() > transition->propertyStorageCapacity())301 offset = transition->put(propertyName, attributes); 302 if (transition->propertyStorageSize() > transition->propertyStorageCapacity()) 276 303 transition->growPropertyStorageCapacity(); 277 304 … … 294 321 } 295 322 296 PassRefPtr<StructureID> StructureID:: toDictionaryTransition(StructureID* structureID)323 PassRefPtr<StructureID> StructureID::removePropertyTransition(StructureID* structureID, const Identifier& propertyName, size_t& offset) 297 324 { 298 325 ASSERT(!structureID->m_isDictionary); … … 300 327 RefPtr<StructureID> transition = create(structureID->m_prototype, structureID->typeInfo()); 301 328 transition->m_isDictionary = true; 302 transition->m_propertyMap = structureID->m_propertyMap; 329 transition->m_propertyTable = structureID->copyPropertyTable(); 330 transition->m_deletedOffsets = structureID->m_deletedOffsets; 331 transition->m_propertyStorageCapacity = structureID->m_propertyStorageCapacity; 332 transition->m_hasGetterSetterProperties = structureID->m_hasGetterSetterProperties; 333 334 offset = transition->remove(propertyName); 335 336 return transition.release(); 337 } 338 339 PassRefPtr<StructureID> StructureID::toDictionaryTransition(StructureID* structureID) 340 { 341 ASSERT(!structureID->m_isDictionary); 342 343 RefPtr<StructureID> transition = create(structureID->m_prototype, structureID->typeInfo()); 344 transition->m_isDictionary = true; 345 transition->m_propertyTable = structureID->copyPropertyTable(); 346 transition->m_deletedOffsets = structureID->m_deletedOffsets; 303 347 transition->m_propertyStorageCapacity = structureID->m_propertyStorageCapacity; 304 348 transition->m_hasGetterSetterProperties = structureID->m_hasGetterSetterProperties; … … 321 365 RefPtr<StructureID> transition = create(prototype, structureID->typeInfo()); 322 366 transition->m_transitionCount = structureID->m_transitionCount + 1; 323 transition->m_propertyMap = structureID->m_propertyMap; 367 transition->m_propertyTable = structureID->copyPropertyTable(); 368 transition->m_deletedOffsets = structureID->m_deletedOffsets; 324 369 transition->m_propertyStorageCapacity = structureID->m_propertyStorageCapacity; 325 370 transition->m_hasGetterSetterProperties = structureID->m_hasGetterSetterProperties; … … 331 376 RefPtr<StructureID> transition = create(structureID->storedPrototype(), structureID->typeInfo()); 332 377 transition->m_transitionCount = structureID->m_transitionCount + 1; 333 transition->m_propertyMap = structureID->m_propertyMap; 378 transition->m_propertyTable = structureID->copyPropertyTable(); 379 transition->m_deletedOffsets = structureID->m_deletedOffsets; 334 380 transition->m_propertyStorageCapacity = structureID->m_propertyStorageCapacity; 335 381 transition->m_hasGetterSetterProperties = transition->m_hasGetterSetterProperties; … … 339 385 size_t StructureID::addPropertyWithoutTransition(const Identifier& propertyName, unsigned attributes) 340 386 { 341 size_t offset = m_propertyMap.put(propertyName, attributes);342 if ( m_propertyMap.storageSize() > propertyStorageCapacity())387 size_t offset = put(propertyName, attributes); 388 if (propertyStorageSize() > propertyStorageCapacity()) 343 389 growPropertyStorageCapacity(); 390 clearEnumerationCache(); 391 return offset; 392 } 393 394 size_t StructureID::removePropertyWithoutTransition(const Identifier& propertyName) 395 { 396 size_t offset = remove(propertyName); 397 clearEnumerationCache(); 344 398 return offset; 345 399 } … … 358 412 return cachedPrototypeChain(); 359 413 } 414 415 #if DUMP_PROPERTYMAP_STATS 416 417 static int numProbes; 418 static int numCollisions; 419 static int numRehashes; 420 static int numRemoves; 421 422 struct PropertyMapStatisticsExitLogger { 423 ~PropertyMapStatisticsExitLogger(); 424 }; 425 426 static PropertyMapStatisticsExitLogger logger; 427 428 PropertyMapStatisticsExitLogger::~PropertyMapStatisticsExitLogger() 429 { 430 printf("\nJSC::PropertyMap statistics\n\n"); 431 printf("%d probes\n", numProbes); 432 printf("%d collisions (%.1f%%)\n", numCollisions, 100.0 * numCollisions / numProbes); 433 printf("%d rehashes\n", numRehashes); 434 printf("%d removes\n", numRemoves); 435 } 436 437 #endif 438 439 static const unsigned deletedSentinelIndex = 1; 440 441 #if !DO_PROPERTYMAP_CONSTENCY_CHECK 442 443 inline void StructureID::checkConsistency() 444 { 445 } 446 447 #endif 448 449 PropertyMapHashTable* StructureID::copyPropertyTable() 450 { 451 if (!m_propertyTable) 452 return 0; 453 454 size_t tableSize = PropertyMapHashTable::allocationSize(m_propertyTable->size); 455 PropertyMapHashTable* newTable = static_cast<PropertyMapHashTable*>(fastMalloc(tableSize)); 456 memcpy(newTable, m_propertyTable, tableSize); 457 458 unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; 459 for (unsigned i = 1; i <= entryCount; ++i) { 460 if (UString::Rep* key = newTable->entries()[i].key) 461 key->ref(); 462 } 463 464 return newTable; 465 } 466 467 size_t StructureID::get(const Identifier& propertyName, unsigned& attributes) const 468 { 469 ASSERT(!propertyName.isNull()); 470 471 if (!m_propertyTable) 472 return WTF::notFound; 473 474 UString::Rep* rep = propertyName._ustring.rep(); 475 476 unsigned i = rep->computedHash(); 477 478 #if DUMP_PROPERTYMAP_STATS 479 ++numProbes; 480 #endif 481 482 unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; 483 if (entryIndex == emptyEntryIndex) 484 return WTF::notFound; 485 486 if (rep == m_propertyTable->entries()[entryIndex - 1].key) { 487 attributes = m_propertyTable->entries()[entryIndex - 1].attributes; 488 return m_propertyTable->entries()[entryIndex - 1].offset; 489 } 490 491 #if DUMP_PROPERTYMAP_STATS 492 ++numCollisions; 493 #endif 494 495 unsigned k = 1 | doubleHash(rep->computedHash()); 496 497 while (1) { 498 i += k; 499 500 #if DUMP_PROPERTYMAP_STATS 501 ++numRehashes; 502 #endif 503 504 entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; 505 if (entryIndex == emptyEntryIndex) 506 return WTF::notFound; 507 508 if (rep == m_propertyTable->entries()[entryIndex - 1].key) { 509 attributes = m_propertyTable->entries()[entryIndex - 1].attributes; 510 return m_propertyTable->entries()[entryIndex - 1].offset; 511 } 512 } 513 } 514 515 size_t StructureID::put(const Identifier& propertyName, unsigned attributes) 516 { 517 ASSERT(!propertyName.isNull()); 518 ASSERT(get(propertyName) == WTF::notFound); 519 520 checkConsistency(); 521 522 UString::Rep* rep = propertyName._ustring.rep(); 523 524 if (!m_propertyTable) 525 createPropertyMapHashTable(); 526 527 // FIXME: Consider a fast case for tables with no deleted sentinels. 528 529 unsigned i = rep->computedHash(); 530 unsigned k = 0; 531 bool foundDeletedElement = false; 532 unsigned deletedElementIndex = 0; // initialize to make the compiler happy 533 534 #if DUMP_PROPERTYMAP_STATS 535 ++numProbes; 536 #endif 537 538 while (1) { 539 unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; 540 if (entryIndex == emptyEntryIndex) 541 break; 542 543 if (entryIndex == deletedSentinelIndex) { 544 // If we find a deleted-element sentinel, remember it for use later. 545 if (!foundDeletedElement) { 546 foundDeletedElement = true; 547 deletedElementIndex = i; 548 } 549 } 550 551 if (k == 0) { 552 k = 1 | doubleHash(rep->computedHash()); 553 #if DUMP_PROPERTYMAP_STATS 554 ++numCollisions; 555 #endif 556 } 557 558 i += k; 559 560 #if DUMP_PROPERTYMAP_STATS 561 ++numRehashes; 562 #endif 563 } 564 565 // Figure out which entry to use. 566 unsigned entryIndex = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount + 2; 567 if (foundDeletedElement) { 568 i = deletedElementIndex; 569 --m_propertyTable->deletedSentinelCount; 570 571 // Since we're not making the table bigger, we can't use the entry one past 572 // the end that we were planning on using, so search backwards for the empty 573 // slot that we can use. We know it will be there because we did at least one 574 // deletion in the past that left an entry empty. 575 while (m_propertyTable->entries()[--entryIndex - 1].key) { } 576 } 577 578 // Create a new hash table entry. 579 m_propertyTable->entryIndices[i & m_propertyTable->sizeMask] = entryIndex; 580 581 // Create a new hash table entry. 582 rep->ref(); 583 m_propertyTable->entries()[entryIndex - 1].key = rep; 584 m_propertyTable->entries()[entryIndex - 1].attributes = attributes; 585 m_propertyTable->entries()[entryIndex - 1].index = ++m_propertyTable->lastIndexUsed; 586 587 unsigned newOffset; 588 if (!m_deletedOffsets.isEmpty()) { 589 newOffset = m_deletedOffsets.last(); 590 m_deletedOffsets.removeLast(); 591 } else 592 newOffset = m_propertyTable->keyCount; 593 m_propertyTable->entries()[entryIndex - 1].offset = newOffset; 594 595 ++m_propertyTable->keyCount; 596 597 if ((m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount) * 2 >= m_propertyTable->size) 598 expandPropertyMapHashTable(); 599 600 checkConsistency(); 601 return newOffset; 602 } 603 604 size_t StructureID::remove(const Identifier& propertyName) 605 { 606 ASSERT(!propertyName.isNull()); 607 608 checkConsistency(); 609 610 UString::Rep* rep = propertyName._ustring.rep(); 611 612 if (!m_propertyTable) 613 return WTF::notFound; 614 615 #if DUMP_PROPERTYMAP_STATS 616 ++numProbes; 617 ++numRemoves; 618 #endif 619 620 // Find the thing to remove. 621 unsigned i = rep->computedHash(); 622 unsigned k = 0; 623 unsigned entryIndex; 624 UString::Rep* key = 0; 625 while (1) { 626 entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; 627 if (entryIndex == emptyEntryIndex) 628 return WTF::notFound; 629 630 key = m_propertyTable->entries()[entryIndex - 1].key; 631 if (rep == key) 632 break; 633 634 if (k == 0) { 635 k = 1 | doubleHash(rep->computedHash()); 636 #if DUMP_PROPERTYMAP_STATS 637 ++numCollisions; 638 #endif 639 } 640 641 i += k; 642 643 #if DUMP_PROPERTYMAP_STATS 644 ++numRehashes; 645 #endif 646 } 647 648 // Replace this one element with the deleted sentinel. Also clear out 649 // the entry so we can iterate all the entries as needed. 650 m_propertyTable->entryIndices[i & m_propertyTable->sizeMask] = deletedSentinelIndex; 651 652 size_t offset = m_propertyTable->entries()[entryIndex - 1].offset; 653 654 key->deref(); 655 m_propertyTable->entries()[entryIndex - 1].key = 0; 656 m_propertyTable->entries()[entryIndex - 1].attributes = 0; 657 m_propertyTable->entries()[entryIndex - 1].offset = 0; 658 m_deletedOffsets.append(offset); 659 660 ASSERT(m_propertyTable->keyCount >= 1); 661 --m_propertyTable->keyCount; 662 ++m_propertyTable->deletedSentinelCount; 663 664 if (m_propertyTable->deletedSentinelCount * 4 >= m_propertyTable->size) 665 rehashPropertyMapHashTable(); 666 667 checkConsistency(); 668 return offset; 669 } 670 671 void StructureID::insertIntoPropertyMapHashTable(const PropertyMapEntry& entry) 672 { 673 ASSERT(m_propertyTable); 674 675 unsigned i = entry.key->computedHash(); 676 unsigned k = 0; 677 678 #if DUMP_PROPERTYMAP_STATS 679 ++numProbes; 680 #endif 681 682 while (1) { 683 unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; 684 if (entryIndex == emptyEntryIndex) 685 break; 686 687 if (k == 0) { 688 k = 1 | doubleHash(entry.key->computedHash()); 689 #if DUMP_PROPERTYMAP_STATS 690 ++numCollisions; 691 #endif 692 } 693 694 i += k; 695 696 #if DUMP_PROPERTYMAP_STATS 697 ++numRehashes; 698 #endif 699 } 700 701 unsigned entryIndex = m_propertyTable->keyCount + 2; 702 m_propertyTable->entryIndices[i & m_propertyTable->sizeMask] = entryIndex; 703 m_propertyTable->entries()[entryIndex - 1] = entry; 704 705 ++m_propertyTable->keyCount; 706 } 707 708 void StructureID::expandPropertyMapHashTable() 709 { 710 ASSERT(m_propertyTable); 711 rehashPropertyMapHashTable(m_propertyTable->size * 2); 712 } 713 714 void StructureID::createPropertyMapHashTable() 715 { 716 const unsigned newTableSize = 16; 717 718 ASSERT(!m_propertyTable); 719 720 checkConsistency(); 721 722 m_propertyTable = static_cast<PropertyMapHashTable*>(fastZeroedMalloc(PropertyMapHashTable::allocationSize(newTableSize))); 723 m_propertyTable->size = newTableSize; 724 m_propertyTable->sizeMask = newTableSize - 1; 725 726 checkConsistency(); 727 } 728 729 void StructureID::rehashPropertyMapHashTable() 730 { 731 ASSERT(m_propertyTable); 732 ASSERT(m_propertyTable->size); 733 rehashPropertyMapHashTable(m_propertyTable->size); 734 } 735 736 void StructureID::rehashPropertyMapHashTable(unsigned newTableSize) 737 { 738 ASSERT(m_propertyTable); 739 740 checkConsistency(); 741 742 PropertyMapHashTable* oldTable = m_propertyTable; 743 744 m_propertyTable = static_cast<PropertyMapHashTable*>(fastZeroedMalloc(PropertyMapHashTable::allocationSize(newTableSize))); 745 m_propertyTable->size = newTableSize; 746 m_propertyTable->sizeMask = newTableSize - 1; 747 748 unsigned lastIndexUsed = 0; 749 unsigned entryCount = oldTable->keyCount + oldTable->deletedSentinelCount; 750 for (unsigned i = 1; i <= entryCount; ++i) { 751 if (oldTable->entries()[i].key) { 752 lastIndexUsed = max(oldTable->entries()[i].index, lastIndexUsed); 753 insertIntoPropertyMapHashTable(oldTable->entries()[i]); 754 } 755 } 756 m_propertyTable->lastIndexUsed = lastIndexUsed; 757 758 fastFree(oldTable); 759 760 checkConsistency(); 761 } 762 763 static int comparePropertyMapEntryIndices(const void* a, const void* b) 764 { 765 unsigned ia = static_cast<PropertyMapEntry* const*>(a)[0]->index; 766 unsigned ib = static_cast<PropertyMapEntry* const*>(b)[0]->index; 767 if (ia < ib) 768 return -1; 769 if (ia > ib) 770 return +1; 771 return 0; 772 } 773 774 void StructureID::getEnumerablePropertyNamesInternal(PropertyNameArray& propertyNames) const 775 { 776 if (!m_propertyTable) 777 return; 778 779 if (m_propertyTable->keyCount < tinyMapThreshold) { 780 PropertyMapEntry* a[tinyMapThreshold]; 781 int i = 0; 782 unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; 783 for (unsigned k = 1; k <= entryCount; k++) { 784 if (m_propertyTable->entries()[k].key && !(m_propertyTable->entries()[k].attributes & DontEnum)) { 785 PropertyMapEntry* value = &m_propertyTable->entries()[k]; 786 int j; 787 for (j = i - 1; j >= 0 && a[j]->index > value->index; --j) 788 a[j + 1] = a[j]; 789 a[j + 1] = value; 790 ++i; 791 } 792 } 793 if (!propertyNames.size()) { 794 for (int k = 0; k < i; ++k) 795 propertyNames.addKnownUnique(a[k]->key); 796 } else { 797 for (int k = 0; k < i; ++k) 798 propertyNames.add(a[k]->key); 799 } 800 801 return; 802 } 803 804 // Allocate a buffer to use to sort the keys. 805 Vector<PropertyMapEntry*, smallMapThreshold> sortedEnumerables(m_propertyTable->keyCount); 806 807 // Get pointers to the enumerable entries in the buffer. 808 PropertyMapEntry** p = sortedEnumerables.data(); 809 unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; 810 for (unsigned i = 1; i <= entryCount; i++) { 811 if (m_propertyTable->entries()[i].key && !(m_propertyTable->entries()[i].attributes & DontEnum)) 812 *p++ = &m_propertyTable->entries()[i]; 813 } 814 815 size_t enumerableCount = p - sortedEnumerables.data(); 816 // Sort the entries by index. 817 qsort(sortedEnumerables.data(), enumerableCount, sizeof(PropertyMapEntry*), comparePropertyMapEntryIndices); 818 sortedEnumerables.resize(enumerableCount); 819 820 // Put the keys of the sorted entries into the list. 821 if (!propertyNames.size()) { 822 for (size_t i = 0; i < sortedEnumerables.size(); ++i) 823 propertyNames.addKnownUnique(sortedEnumerables[i]->key); 824 } else { 825 for (size_t i = 0; i < sortedEnumerables.size(); ++i) 826 propertyNames.add(sortedEnumerables[i]->key); 827 } 828 } 829 830 #if DO_PROPERTYMAP_CONSTENCY_CHECK 831 832 void StructureID::checkConsistency() 833 { 834 if (!m_propertyTable) 835 return; 836 837 ASSERT(m_propertyTable->size >= 16); 838 ASSERT(m_propertyTable->sizeMask); 839 ASSERT(m_propertyTable->size == m_propertyTable->sizeMask + 1); 840 ASSERT(!(m_propertyTable->size & m_propertyTable->sizeMask)); 841 842 ASSERT(m_propertyTable->keyCount <= m_propertyTable->size / 2); 843 ASSERT(m_propertyTable->deletedSentinelCount <= m_propertyTable->size / 4); 844 845 ASSERT(m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount <= m_propertyTable->size / 2); 846 847 unsigned indexCount = 0; 848 unsigned deletedIndexCount = 0; 849 for (unsigned a = 0; a != m_propertyTable->size; ++a) { 850 unsigned entryIndex = m_propertyTable->entryIndices[a]; 851 if (entryIndex == emptyEntryIndex) 852 continue; 853 if (entryIndex == deletedSentinelIndex) { 854 ++deletedIndexCount; 855 continue; 856 } 857 ASSERT(entryIndex > deletedSentinelIndex); 858 ASSERT(entryIndex - 1 <= m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount); 859 ++indexCount; 860 861 for (unsigned b = a + 1; b != m_propertyTable->size; ++b) 862 ASSERT(m_propertyTable->entryIndices[b] != entryIndex); 863 } 864 ASSERT(indexCount == m_propertyTable->keyCount); 865 ASSERT(deletedIndexCount == m_propertyTable->deletedSentinelCount); 866 867 ASSERT(m_propertyTable->entries()[0].key == 0); 868 869 unsigned nonEmptyEntryCount = 0; 870 for (unsigned c = 1; c <= m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; ++c) { 871 UString::Rep* rep = m_propertyTable->entries()[c].key; 872 if (!rep) 873 continue; 874 ++nonEmptyEntryCount; 875 unsigned i = rep->computedHash(); 876 unsigned k = 0; 877 unsigned entryIndex; 878 while (1) { 879 entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; 880 ASSERT(entryIndex != emptyEntryIndex); 881 if (rep == m_propertyTable->entries()[entryIndex - 1].key) 882 break; 883 if (k == 0) 884 k = 1 | doubleHash(rep->computedHash()); 885 i += k; 886 } 887 ASSERT(entryIndex == c + 1); 888 } 889 890 ASSERT(nonEmptyEntryCount == m_propertyTable->keyCount); 891 } 892 893 #endif // DO_PROPERTYMAP_CONSTENCY_CHECK 894 895 // StructureIDChain 360 896 361 897 StructureIDChain::StructureIDChain(StructureID* structureID)
Note:
See TracChangeset
for help on using the changeset viewer.