CodeOrigin.h
1 /* 2 * Copyright (C) 2011-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. ``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 #pragma once 27 28 #include "BytecodeIndex.h" 29 30 #include <limits.h> 31 #include <wtf/HashMap.h> 32 #include <wtf/PrintStream.h> 33 #include <wtf/StdLibExtras.h> 34 #include <wtf/Vector.h> 35 36 namespace JSC { 37 38 class CodeBlock; 39 struct DumpContext; 40 struct InlineCallFrame; 41 42 class CodeOrigin { 43 public: 44 CodeOrigin() 45 #if CPU(ADDRESS64) 46 : m_compositeValue(buildCompositeValue(nullptr, BytecodeIndex())) 47 #else 48 : m_inlineCallFrame(nullptr) 49 #endif 50 { 51 } 52 53 CodeOrigin(WTF::HashTableDeletedValueType) 54 #if CPU(ADDRESS64) 55 : m_compositeValue(buildCompositeValue(deletedMarker(), BytecodeIndex())) 56 #else 57 : m_bytecodeIndex(WTF::HashTableDeletedValue) 58 , m_inlineCallFrame(deletedMarker()) 59 #endif 60 { 61 } 62 63 explicit CodeOrigin(BytecodeIndex bytecodeIndex, InlineCallFrame* inlineCallFrame = nullptr) 64 #if CPU(ADDRESS64) 65 : m_compositeValue(buildCompositeValue(inlineCallFrame, bytecodeIndex)) 66 #else 67 : m_bytecodeIndex(bytecodeIndex) 68 , m_inlineCallFrame(inlineCallFrame) 69 #endif 70 { 71 ASSERT(!!bytecodeIndex); 72 #if CPU(ADDRESS64) 73 ASSERT(!(bitwise_cast<uintptr_t>(inlineCallFrame) & ~s_maskCompositeValueForPointer)); 74 #endif 75 } 76 77 #if CPU(ADDRESS64) 78 CodeOrigin& operator=(const CodeOrigin& other) 79 { 80 if (this != &other) { 81 if (UNLIKELY(isOutOfLine())) 82 delete outOfLineCodeOrigin(); 83 84 if (UNLIKELY(other.isOutOfLine())) 85 m_compositeValue = buildCompositeValue(other.inlineCallFrame(), other.bytecodeIndex()); 86 else 87 m_compositeValue = other.m_compositeValue; 88 } 89 return *this; 90 } 91 CodeOrigin& operator=(CodeOrigin&& other) 92 { 93 if (this != &other) { 94 if (UNLIKELY(isOutOfLine())) 95 delete outOfLineCodeOrigin(); 96 97 m_compositeValue = std::exchange(other.m_compositeValue, 0); 98 } 99 return *this; 100 } 101 102 CodeOrigin(const CodeOrigin& other) 103 { 104 // We don't use the member initializer list because it would not let us optimize the common case where there is no out-of-line storage 105 // (in which case we don't have to extract the components of the composite value just to reassemble it). 106 if (UNLIKELY(other.isOutOfLine())) 107 m_compositeValue = buildCompositeValue(other.inlineCallFrame(), other.bytecodeIndex()); 108 else 109 m_compositeValue = other.m_compositeValue; 110 } 111 CodeOrigin(CodeOrigin&& other) 112 : m_compositeValue(std::exchange(other.m_compositeValue, 0)) 113 { 114 } 115 116 ~CodeOrigin() 117 { 118 if (UNLIKELY(isOutOfLine())) 119 delete outOfLineCodeOrigin(); 120 } 121 #endif 122 123 bool isSet() const 124 { 125 #if CPU(ADDRESS64) 126 return !(m_compositeValue & s_maskIsBytecodeIndexInvalid); 127 #else 128 return !!m_bytecodeIndex; 129 #endif 130 } 131 explicit operator bool() const { return isSet(); } 132 133 bool isHashTableDeletedValue() const 134 { 135 #if CPU(ADDRESS64) 136 return !isSet() && (m_compositeValue & s_maskCompositeValueForPointer); 137 #else 138 return m_bytecodeIndex.isHashTableDeletedValue() && !!m_inlineCallFrame; 139 #endif 140 } 141 142 // The inline depth is the depth of the inline stack, so 1 = not inlined, 143 // 2 = inlined one deep, etc. 144 unsigned inlineDepth() const; 145 146 // If the code origin corresponds to inlined code, gives you the heap object that 147 // would have owned the code if it had not been inlined. Otherwise returns 0. 148 CodeBlock* codeOriginOwner() const; 149 150 int stackOffset() const; 151 152 unsigned hash() const; 153 bool operator==(const CodeOrigin& other) const; 154 bool operator!=(const CodeOrigin& other) const { return !(*this == other); } 155 156 // This checks if the two code origins correspond to the same stack trace snippets, 157 // but ignore whether the InlineCallFrame's are identical. 158 bool isApproximatelyEqualTo(const CodeOrigin& other, InlineCallFrame* terminal = nullptr) const; 159 160 unsigned approximateHash(InlineCallFrame* terminal = nullptr) const; 161 162 template <typename Function> 163 void walkUpInlineStack(const Function&) const; 164 165 inline bool inlineStackContainsActiveCheckpoint() const; 166 167 // Get the inline stack. This is slow, and is intended for debugging only. 168 Vector<CodeOrigin> inlineStack() const; 169 170 JS_EXPORT_PRIVATE void dump(PrintStream&) const; 171 void dumpInContext(PrintStream&, DumpContext*) const; 172 173 BytecodeIndex bytecodeIndex() const 174 { 175 #if CPU(ADDRESS64) 176 if (!isSet()) 177 return BytecodeIndex(); 178 if (UNLIKELY(isOutOfLine())) 179 return outOfLineCodeOrigin()->bytecodeIndex; 180 return BytecodeIndex::fromBits(m_compositeValue >> (64 - s_freeBitsAtTop)); 181 #else 182 return m_bytecodeIndex; 183 #endif 184 } 185 186 InlineCallFrame* inlineCallFrame() const 187 { 188 #if CPU(ADDRESS64) 189 if (UNLIKELY(isOutOfLine())) 190 return outOfLineCodeOrigin()->inlineCallFrame; 191 return bitwise_cast<InlineCallFrame*>(m_compositeValue & s_maskCompositeValueForPointer); 192 #else 193 return m_inlineCallFrame; 194 #endif 195 } 196 197 private: 198 #if CPU(ADDRESS64) 199 static constexpr uintptr_t s_maskIsOutOfLine = 1; 200 static constexpr uintptr_t s_maskIsBytecodeIndexInvalid = 2; 201 202 struct OutOfLineCodeOrigin { 203 WTF_MAKE_FAST_ALLOCATED; 204 public: 205 InlineCallFrame* inlineCallFrame; 206 BytecodeIndex bytecodeIndex; 207 208 OutOfLineCodeOrigin(InlineCallFrame* inlineCallFrame, BytecodeIndex bytecodeIndex) 209 : inlineCallFrame(inlineCallFrame) 210 , bytecodeIndex(bytecodeIndex) 211 { 212 } 213 }; 214 215 bool isOutOfLine() const 216 { 217 return m_compositeValue & s_maskIsOutOfLine; 218 } 219 OutOfLineCodeOrigin* outOfLineCodeOrigin() const 220 { 221 ASSERT(isOutOfLine()); 222 return bitwise_cast<OutOfLineCodeOrigin*>(m_compositeValue & s_maskCompositeValueForPointer); 223 } 224 #endif 225 226 static InlineCallFrame* deletedMarker() 227 { 228 auto value = static_cast<uintptr_t>(1 << 3); 229 #if CPU(ADDRESS64) 230 ASSERT(value & s_maskCompositeValueForPointer); 231 ASSERT(!(value & ~s_maskCompositeValueForPointer)); 232 #endif 233 return bitwise_cast<InlineCallFrame*>(value); 234 } 235 236 #if CPU(ADDRESS64) 237 static constexpr unsigned s_freeBitsAtTop = 64 - OS_CONSTANT(EFFECTIVE_ADDRESS_WIDTH); 238 static constexpr uintptr_t s_maskCompositeValueForPointer = ((1ULL << OS_CONSTANT(EFFECTIVE_ADDRESS_WIDTH)) - 1) & ~(8ULL - 1); 239 static uintptr_t buildCompositeValue(InlineCallFrame* inlineCallFrame, BytecodeIndex bytecodeIndex) 240 { 241 if (!bytecodeIndex) 242 return bitwise_cast<uintptr_t>(inlineCallFrame) | s_maskIsBytecodeIndexInvalid; 243 244 if (UNLIKELY(bytecodeIndex.asBits() >= 1 << s_freeBitsAtTop)) { 245 auto* outOfLine = new OutOfLineCodeOrigin(inlineCallFrame, bytecodeIndex); 246 return bitwise_cast<uintptr_t>(outOfLine) | s_maskIsOutOfLine; 247 } 248 249 uintptr_t encodedBytecodeIndex = static_cast<uintptr_t>(bytecodeIndex.asBits()) << (64 - s_freeBitsAtTop); 250 ASSERT(!(encodedBytecodeIndex & bitwise_cast<uintptr_t>(inlineCallFrame))); 251 return encodedBytecodeIndex | bitwise_cast<uintptr_t>(inlineCallFrame); 252 } 253 254 // The bottom bit indicates whether to look at an out-of-line implementation (because of a bytecode index which is too big for us to store). 255 // The next bit indicates whether this is an invalid bytecode (which depending on the InlineCallFrame* can either indicate an unset CodeOrigin, 256 // or a deletion marker for a hash table). 257 // The next bit is free 258 // The next 64-s_freeBitsAtTop-3 are the InlineCallFrame* or the OutOfLineCodeOrigin* 259 // Finally the last s_freeBitsAtTop are the bytecodeIndex if it is inline 260 uintptr_t m_compositeValue; 261 #else 262 BytecodeIndex m_bytecodeIndex; 263 InlineCallFrame* m_inlineCallFrame; 264 #endif 265 }; 266 267 inline unsigned CodeOrigin::hash() const 268 { 269 return WTF::IntHash<unsigned>::hash(bytecodeIndex().asBits()) + 270 WTF::PtrHash<InlineCallFrame*>::hash(inlineCallFrame()); 271 } 272 273 inline bool CodeOrigin::operator==(const CodeOrigin& other) const 274 { 275 #if CPU(ADDRESS64) 276 if (m_compositeValue == other.m_compositeValue) 277 return true; 278 #endif 279 return bytecodeIndex() == other.bytecodeIndex() 280 && inlineCallFrame() == other.inlineCallFrame(); 281 } 282 283 struct CodeOriginHash { 284 static unsigned hash(const CodeOrigin& key) { return key.hash(); } 285 static bool equal(const CodeOrigin& a, const CodeOrigin& b) { return a == b; } 286 static constexpr bool safeToCompareToEmptyOrDeleted = true; 287 }; 288 289 struct CodeOriginApproximateHash { 290 static unsigned hash(const CodeOrigin& key) { return key.approximateHash(); } 291 static bool equal(const CodeOrigin& a, const CodeOrigin& b) { return a.isApproximatelyEqualTo(b); } 292 static constexpr bool safeToCompareToEmptyOrDeleted = true; 293 }; 294 295 } // namespace JSC 296 297 namespace WTF { 298 299 template<typename T> struct DefaultHash; 300 template<> struct DefaultHash<JSC::CodeOrigin> : JSC::CodeOriginHash { }; 301 302 template<typename T> struct HashTraits; 303 template<> struct HashTraits<JSC::CodeOrigin> : SimpleClassHashTraits<JSC::CodeOrigin> { 304 static constexpr bool emptyValueIsZero = false; 305 }; 306 307 } // namespace WTF