/ bytecode / CallVariant.h
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