StructureIDTable.cpp
1 /* 2 * Copyright (C) 2013-2019 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. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "config.h" 27 #include "StructureIDTable.h" 28 29 #include <wtf/Atomics.h> 30 #include <wtf/DataLog.h> 31 #include <wtf/RawPointer.h> 32 33 namespace JSC { 34 35 #if USE(JSVALUE64) 36 37 namespace StructureIDTableInternal { 38 static constexpr bool verbose = false; 39 } 40 41 StructureIDTable::StructureIDTable() 42 : m_table(makeUniqueArray<StructureOrOffset>(s_initialSize)) 43 , m_size(1) 44 , m_capacity(s_initialSize) 45 { 46 // We pre-allocate the first offset so that the null Structure 47 // can still be represented as the StructureID '0'. 48 table()[0].encodedStructureBits = 0; 49 50 makeFreeListFromRange(1, m_capacity - 1); 51 } 52 53 void StructureIDTable::makeFreeListFromRange(uint32_t first, uint32_t last) 54 { 55 ASSERT(!m_firstFreeOffset); 56 ASSERT(!m_lastFreeOffset); 57 58 // Put all the new IDs on the free list sequentially. 59 uint32_t head = first; 60 uint32_t tail = last; 61 for (uint32_t i = first; i < last; ++i) 62 table()[i].offset = i + 1; 63 table()[last].offset = 0; 64 65 // Randomize the free list. 66 uint32_t size = last - first + 1; 67 uint32_t maxIterations = (size * 2) / 3; 68 for (uint32_t count = 0; count < maxIterations; ++count) { 69 // Move a random pick either to the head or the tail of the free list. 70 uint32_t random = m_weakRandom.getUint32(); 71 uint32_t nodeBefore = first + (random % size); 72 uint32_t pick = table()[nodeBefore].offset; 73 if (pick) { 74 uint32_t nodeAfter = table()[pick].offset; 75 table()[nodeBefore].offset = nodeAfter; 76 if ((random & 1) || !nodeAfter) { 77 // Move to the head. 78 table()[pick].offset = head; 79 head = pick; 80 if (!nodeAfter) 81 tail = nodeBefore; 82 } else { 83 // Move to the tail. 84 table()[pick].offset = 0; 85 table()[tail].offset = pick; 86 tail = pick; 87 } 88 } 89 } 90 91 // Cut list in half and swap halves. 92 uint32_t cut = first + (m_weakRandom.getUint32() % size); 93 uint32_t afterCut = table()[cut].offset; 94 if (afterCut) { 95 table()[tail].offset = head; 96 tail = cut; 97 head = afterCut; 98 table()[cut].offset = 0; 99 } 100 101 m_firstFreeOffset = head; 102 m_lastFreeOffset = tail; 103 } 104 105 void StructureIDTable::resize(size_t newCapacity) 106 { 107 if (newCapacity > s_maximumNumberOfStructures) 108 newCapacity = s_maximumNumberOfStructures; 109 110 // If m_size is already s_maximumNumberOfStructures, newCapacity becomes s_maximumNumberOfStructures in the above code. 111 // In that case, we should crash because of exhaust of StructureIDs. 112 RELEASE_ASSERT_WITH_MESSAGE(m_size < newCapacity, "Crash intentionally because of exhaust of StructureIDs."); 113 114 // Create the new table. 115 auto newTable = makeUniqueArray<StructureOrOffset>(newCapacity); 116 117 // Copy the contents of the old table to the new table. 118 memcpy(newTable.get(), table(), m_capacity * sizeof(StructureOrOffset)); 119 120 // Store fence to make sure we've copied everything before doing the swap. 121 WTF::storeStoreFence(); 122 123 // Swap the old and new tables. 124 swap(m_table, newTable); 125 126 // Put the old table (now labeled as new) into the list of old tables. 127 m_oldTables.append(WTFMove(newTable)); 128 129 // Update the capacity. 130 m_capacity = newCapacity; 131 132 makeFreeListFromRange(m_size, m_capacity - 1); 133 } 134 135 void StructureIDTable::flushOldTables() 136 { 137 m_oldTables.clear(); 138 } 139 140 StructureID StructureIDTable::allocateID(Structure* structure) 141 { 142 if (UNLIKELY(!m_firstFreeOffset)) { 143 RELEASE_ASSERT(m_capacity <= s_maximumNumberOfStructures); 144 ASSERT(m_size == m_capacity); 145 resize(m_capacity * 2); 146 ASSERT(m_size < m_capacity); 147 RELEASE_ASSERT(m_firstFreeOffset); 148 } 149 150 // entropyBits must not be zero. This ensures that if a corrupted 151 // structureID is encountered (with incorrect entropyBits), the decoded 152 // structure pointer for that ID will be always be a bad pointer with 153 // high bits set. 154 constexpr uint32_t entropyBitsMask = (1 << s_numberOfEntropyBits) - 1; 155 uint32_t entropyBits = m_weakRandom.getUint32() & entropyBitsMask; 156 if (UNLIKELY(!entropyBits)) { 157 constexpr uint32_t numberOfValuesToPickFrom = entropyBitsMask; 158 entropyBits = (m_weakRandom.getUint32() % numberOfValuesToPickFrom) + 1; 159 } 160 161 uint32_t structureIndex = m_firstFreeOffset; 162 m_firstFreeOffset = table()[m_firstFreeOffset].offset; 163 if (!m_firstFreeOffset) 164 m_lastFreeOffset = 0; 165 166 StructureID result = (structureIndex << s_numberOfEntropyBits) | entropyBits; 167 table()[structureIndex].encodedStructureBits = encode(structure, result); 168 m_size++; 169 ASSERT(!isNuked(result)); 170 171 dataLogLnIf(StructureIDTableInternal::verbose, "Allocated StructureID ", result, " for Structure ", RawPointer(structure)); 172 return result; 173 } 174 175 void StructureIDTable::deallocateID(Structure* structure, StructureID structureID) 176 { 177 dataLogLnIf(StructureIDTableInternal::verbose, "Deallocated StructureID ", structureID); 178 ASSERT(structureID != s_unusedID); 179 uint32_t structureIndex = structureID >> s_numberOfEntropyBits; 180 ASSERT(structureIndex && structureIndex < s_maximumNumberOfStructures); 181 RELEASE_ASSERT(table()[structureIndex].encodedStructureBits == encode(structure, structureID)); 182 m_size--; 183 if (!m_firstFreeOffset) { 184 table()[structureIndex].offset = 0; 185 m_firstFreeOffset = structureIndex; 186 m_lastFreeOffset = structureIndex; 187 return; 188 } 189 190 bool insertAtHead = m_weakRandom.getUint32() & 1; 191 if (insertAtHead) { 192 table()[structureIndex].offset = m_firstFreeOffset; 193 m_firstFreeOffset = structureIndex; 194 } else { 195 table()[structureIndex].offset = 0; 196 table()[m_lastFreeOffset].offset = structureIndex; 197 m_lastFreeOffset = structureIndex; 198 } 199 } 200 201 #endif // USE(JSVALUE64) 202 203 } // namespace JSC