CallVariant.h
1 /* 2 * Copyright (C) 2014-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 "ExecutableBaseInlines.h" 29 #include "FunctionExecutable.h" 30 #include "JSCast.h" 31 #include "JSFunction.h" 32 #include "NativeExecutable.h" 33 34 namespace JSC { 35 36 // The CallVariant class is meant to encapsulate a callee in a way that is useful for call linking 37 // and inlining. Because JavaScript has closures, and because JSC implements the notion of internal 38 // non-function objects that nevertheless provide call traps, the call machinery wants to see a 39 // callee in one of the following four forms: 40 // 41 // JSFunction callee: This means that we expect the callsite to always call a particular function 42 // instance, that is associated with a particular lexical environment. This pinpoints not 43 // just the code that will be called (i.e. the executable) but also the scope within which 44 // the code runs. 45 // 46 // Executable callee: This corresponds to a call to a closure. In this case, we know that the 47 // callsite will call a JSFunction, but we do not know which particular JSFunction. We do know 48 // what code will be called - i.e. we know the executable. 49 // 50 // InternalFunction callee: JSC supports a special kind of native functions that support bizarre 51 // semantics. These are always singletons. If we know that the callee is an InternalFunction 52 // then we know both the code that will be called and the scope; in fact the "scope" is really 53 // just the InternalFunction itself. 54 // 55 // Something else: It's possible call all manner of rubbish in JavaScript. This implicitly supports 56 // bizarre object callees, but it can't really tell you anything interesting about them other 57 // than the fact that they don't fall into any of the above categories. 58 // 59 // This class serves as a kind of union over these four things. It does so by just holding a 60 // JSCell*. We determine which of the modes its in by doing type checks on the cell. Note that we 61 // cannot use WriteBarrier<> here because this gets used inside the compiler. 62 63 class CallVariant { 64 WTF_MAKE_FAST_ALLOCATED; 65 public: 66 explicit CallVariant(JSCell* callee = nullptr) 67 : m_callee(callee) 68 { 69 } 70 71 CallVariant(WTF::HashTableDeletedValueType) 72 : m_callee(deletedToken()) 73 { 74 } 75 76 explicit operator bool() const { return !!m_callee; } 77 78 // If this variant refers to a function, change it to refer to its executable. 79 ALWAYS_INLINE CallVariant despecifiedClosure() const 80 { 81 if (m_callee->type() == JSFunctionType) 82 return CallVariant(jsCast<JSFunction*>(m_callee)->executable()); 83 return *this; 84 } 85 86 JSCell* rawCalleeCell() const { return m_callee; } 87 88 InternalFunction* internalFunction() const 89 { 90 return jsDynamicCast<InternalFunction*>(m_callee->vm(), m_callee); 91 } 92 93 JSFunction* function() const 94 { 95 return jsDynamicCast<JSFunction*>(m_callee->vm(), m_callee); 96 } 97 98 bool isClosureCall() const { return !!jsDynamicCast<ExecutableBase*>(m_callee->vm(), m_callee); } 99 100 ExecutableBase* executable() const 101 { 102 if (JSFunction* function = this->function()) 103 return function->executable(); 104 return jsDynamicCast<ExecutableBase*>(m_callee->vm(), m_callee); 105 } 106 107 JSCell* nonExecutableCallee() const 108 { 109 RELEASE_ASSERT(!isClosureCall()); 110 return m_callee; 111 } 112 113 Intrinsic intrinsicFor(CodeSpecializationKind kind) const 114 { 115 if (ExecutableBase* executable = this->executable()) 116 return executable->intrinsicFor(kind); 117 return NoIntrinsic; 118 } 119 120 FunctionExecutable* functionExecutable() const 121 { 122 if (ExecutableBase* executable = this->executable()) 123 return jsDynamicCast<FunctionExecutable*>(m_callee->vm(), executable); 124 return nullptr; 125 } 126 127 NativeExecutable* nativeExecutable() const 128 { 129 if (ExecutableBase* executable = this->executable()) 130 return jsDynamicCast<NativeExecutable*>(m_callee->vm(), executable); 131 return nullptr; 132 } 133 134 const DOMJIT::Signature* signatureFor(CodeSpecializationKind kind) const 135 { 136 if (NativeExecutable* nativeExecutable = this->nativeExecutable()) 137 return nativeExecutable->signatureFor(kind); 138 return nullptr; 139 } 140 141 bool finalize(VM&); 142 143 bool merge(const CallVariant&); 144 145 void filter(VM&, JSValue); 146 147 void dump(PrintStream& out) const; 148 149 bool isHashTableDeletedValue() const 150 { 151 return m_callee == deletedToken(); 152 } 153 154 bool operator==(const CallVariant& other) const 155 { 156 return m_callee == other.m_callee; 157 } 158 159 bool operator!=(const CallVariant& other) const 160 { 161 return !(*this == other); 162 } 163 164 bool operator<(const CallVariant& other) const 165 { 166 return m_callee < other.m_callee; 167 } 168 169 bool operator>(const CallVariant& other) const 170 { 171 return other < *this; 172 } 173 174 bool operator<=(const CallVariant& other) const 175 { 176 return !(*this < other); 177 } 178 179 bool operator>=(const CallVariant& other) const 180 { 181 return other <= *this; 182 } 183 184 unsigned hash() const 185 { 186 return WTF::PtrHash<JSCell*>::hash(m_callee); 187 } 188 189 private: 190 static JSCell* deletedToken() { return bitwise_cast<JSCell*>(static_cast<uintptr_t>(1)); } 191 192 JSCell* m_callee; 193 }; 194 195 struct CallVariantHash { 196 static unsigned hash(const CallVariant& key) { return key.hash(); } 197 static bool equal(const CallVariant& a, const CallVariant& b) { return a == b; } 198 static constexpr bool safeToCompareToEmptyOrDeleted = true; 199 }; 200 201 typedef Vector<CallVariant, 1> CallVariantList; 202 203 // Returns a new variant list by attempting to either append the given variant or merge it with one 204 // of the variants we already have by despecifying closures. 205 CallVariantList variantListWithVariant(const CallVariantList&, CallVariant); 206 207 // Returns a new list where every element is despecified, and the list is deduplicated. 208 CallVariantList despecifiedVariantList(const CallVariantList&); 209 210 } // namespace JSC 211 212 namespace WTF { 213 214 template<typename T> struct DefaultHash; 215 template<> struct DefaultHash<JSC::CallVariant> : JSC::CallVariantHash { }; 216 217 template<typename T> struct HashTraits; 218 template<> struct HashTraits<JSC::CallVariant> : SimpleClassHashTraits<JSC::CallVariant> { }; 219 220 } // namespace WTF