1/*
2 * Copyright (C) 2014-2018 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 {
64public:
65 explicit CallVariant(JSCell* callee = nullptr)
66 : m_callee(callee)
67 {
68 }
69
70 CallVariant(WTF::HashTableDeletedValueType)
71 : m_callee(deletedToken())
72 {
73 }
74
75 explicit operator bool() const { return !!m_callee; }
76
77 // If this variant refers to a function, change it to refer to its executable.
78 ALWAYS_INLINE CallVariant despecifiedClosure() const
79 {
80 if (m_callee->type() == JSFunctionType)
81 return CallVariant(jsCast<JSFunction*>(m_callee)->executable());
82 return *this;
83 }
84
85 JSCell* rawCalleeCell() const { return m_callee; }
86
87 InternalFunction* internalFunction() const
88 {
89 return jsDynamicCast<InternalFunction*>(*m_callee->vm(), m_callee);
90 }
91
92 JSFunction* function() const
93 {
94 return jsDynamicCast<JSFunction*>(*m_callee->vm(), m_callee);
95 }
96
97 bool isClosureCall() const { return !!jsDynamicCast<ExecutableBase*>(*m_callee->vm(), m_callee); }
98
99 ExecutableBase* executable() const
100 {
101 if (JSFunction* function = this->function())
102 return function->executable();
103 return jsDynamicCast<ExecutableBase*>(*m_callee->vm(), m_callee);
104 }
105
106 JSCell* nonExecutableCallee() const
107 {
108 RELEASE_ASSERT(!isClosureCall());
109 return m_callee;
110 }
111
112 Intrinsic intrinsicFor(CodeSpecializationKind kind) const
113 {
114 if (ExecutableBase* executable = this->executable())
115 return executable->intrinsicFor(kind);
116 return NoIntrinsic;
117 }
118
119 FunctionExecutable* functionExecutable() const
120 {
121 if (ExecutableBase* executable = this->executable())
122 return jsDynamicCast<FunctionExecutable*>(*m_callee->vm(), executable);
123 return nullptr;
124 }
125
126 NativeExecutable* nativeExecutable() const
127 {
128 if (ExecutableBase* executable = this->executable())
129 return jsDynamicCast<NativeExecutable*>(*m_callee->vm(), executable);
130 return nullptr;
131 }
132
133 const DOMJIT::Signature* signatureFor(CodeSpecializationKind kind) const
134 {
135 if (NativeExecutable* nativeExecutable = this->nativeExecutable())
136 return nativeExecutable->signatureFor(kind);
137 return nullptr;
138 }
139
140 bool finalize(VM&);
141
142 bool merge(const CallVariant&);
143
144 void filter(VM&, JSValue);
145
146 void dump(PrintStream& out) const;
147
148 bool isHashTableDeletedValue() const
149 {
150 return m_callee == deletedToken();
151 }
152
153 bool operator==(const CallVariant& other) const
154 {
155 return m_callee == other.m_callee;
156 }
157
158 bool operator!=(const CallVariant& other) const
159 {
160 return !(*this == other);
161 }
162
163 bool operator<(const CallVariant& other) const
164 {
165 return m_callee < other.m_callee;
166 }
167
168 bool operator>(const CallVariant& other) const
169 {
170 return other < *this;
171 }
172
173 bool operator<=(const CallVariant& other) const
174 {
175 return !(*this < other);
176 }
177
178 bool operator>=(const CallVariant& other) const
179 {
180 return other <= *this;
181 }
182
183 unsigned hash() const
184 {
185 return WTF::PtrHash<JSCell*>::hash(m_callee);
186 }
187
188private:
189 static JSCell* deletedToken() { return bitwise_cast<JSCell*>(static_cast<uintptr_t>(1)); }
190
191 JSCell* m_callee;
192};
193
194struct CallVariantHash {
195 static unsigned hash(const CallVariant& key) { return key.hash(); }
196 static bool equal(const CallVariant& a, const CallVariant& b) { return a == b; }
197 static const bool safeToCompareToEmptyOrDeleted = true;
198};
199
200typedef Vector<CallVariant, 1> CallVariantList;
201
202// Returns a new variant list by attempting to either append the given variant or merge it with one
203// of the variants we already have by despecifying closures.
204CallVariantList variantListWithVariant(const CallVariantList&, CallVariant);
205
206// Returns a new list where every element is despecified, and the list is deduplicated.
207CallVariantList despecifiedVariantList(const CallVariantList&);
208
209} // namespace JSC
210
211namespace WTF {
212
213template<typename T> struct DefaultHash;
214template<> struct DefaultHash<JSC::CallVariant> {
215 typedef JSC::CallVariantHash Hash;
216};
217
218template<typename T> struct HashTraits;
219template<> struct HashTraits<JSC::CallVariant> : SimpleClassHashTraits<JSC::CallVariant> { };
220
221} // namespace WTF
222