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
34namespace 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
63class CallVariant {
64 WTF_MAKE_FAST_ALLOCATED;
65public:
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
189private:
190 static JSCell* deletedToken() { return bitwise_cast<JSCell*>(static_cast<uintptr_t>(1)); }
191
192 JSCell* m_callee;
193};
194
195struct 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
201typedef 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.
205CallVariantList variantListWithVariant(const CallVariantList&, CallVariant);
206
207// Returns a new list where every element is despecified, and the list is deduplicated.
208CallVariantList despecifiedVariantList(const CallVariantList&);
209
210} // namespace JSC
211
212namespace WTF {
213
214template<typename T> struct DefaultHash;
215template<> struct DefaultHash<JSC::CallVariant> {
216 typedef JSC::CallVariantHash Hash;
217};
218
219template<typename T> struct HashTraits;
220template<> struct HashTraits<JSC::CallVariant> : SimpleClassHashTraits<JSC::CallVariant> { };
221
222} // namespace WTF
223