| 1 | /* |
| 2 | * Copyright (C) 2017 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 | #if ENABLE(JIT) |
| 29 | |
| 30 | #include "JSFunctionInlines.h" |
| 31 | #include "ObjectPropertyConditionSet.h" |
| 32 | #include "PolyProtoAccessChain.h" |
| 33 | #include <wtf/CommaPrinter.h> |
| 34 | |
| 35 | namespace JSC { |
| 36 | |
| 37 | struct AccessGenerationState; |
| 38 | |
| 39 | // An AccessCase describes one of the cases of a PolymorphicAccess. A PolymorphicAccess represents a |
| 40 | // planned (to generate in future) or generated stub for some inline cache. That stub contains fast |
| 41 | // path code for some finite number of fast cases, each described by an AccessCase object. |
| 42 | // |
| 43 | // An AccessCase object has a lifecycle that proceeds through several states. Note that the states |
| 44 | // of AccessCase have a lot to do with the global effect epoch (we'll say epoch for short). This is |
| 45 | // a simple way of reasoning about the state of the system outside this AccessCase. Any observable |
| 46 | // effect - like storing to a property, changing an object's structure, etc. - increments the epoch. |
| 47 | // The states are: |
| 48 | // |
| 49 | // Primordial: This is an AccessCase that was just allocated. It does not correspond to any actual |
| 50 | // code and it is not owned by any PolymorphicAccess. In this state, the AccessCase |
| 51 | // assumes that it is in the same epoch as when it was created. This is important |
| 52 | // because it may make claims about itself ("I represent a valid case so long as you |
| 53 | // register a watchpoint on this set") that could be contradicted by some outside |
| 54 | // effects (like firing and deleting the watchpoint set in question). This is also the |
| 55 | // state that an AccessCase is in when it is cloned (AccessCase::clone()). |
| 56 | // |
| 57 | // Committed: This happens as soon as some PolymorphicAccess takes ownership of this AccessCase. |
| 58 | // In this state, the AccessCase no longer assumes anything about the epoch. To |
| 59 | // accomplish this, PolymorphicAccess calls AccessCase::commit(). This must be done |
| 60 | // during the same epoch when the AccessCase was created, either by the client or by |
| 61 | // clone(). When created by the client, committing during the same epoch works because |
| 62 | // we can be sure that whatever watchpoint sets they spoke of are still valid. When |
| 63 | // created by clone(), we can be sure that the set is still valid because the original |
| 64 | // of the clone still has watchpoints on it. |
| 65 | // |
| 66 | // Generated: This is the state when the PolymorphicAccess generates code for this case by |
| 67 | // calling AccessCase::generate() or AccessCase::generateWithGuard(). At this point |
| 68 | // the case object will have some extra stuff in it, like possibly the CallLinkInfo |
| 69 | // object associated with the inline cache. |
| 70 | // FIXME: Moving into the Generated state should not mutate the AccessCase object or |
| 71 | // put more stuff into it. If we fix this, then we can get rid of AccessCase::clone(). |
| 72 | // https://bugs.webkit.org/show_bug.cgi?id=156456 |
| 73 | // |
| 74 | // An AccessCase may be destroyed while in any of these states. |
| 75 | // |
| 76 | // We will sometimes buffer committed AccessCases in the PolymorphicAccess object before generating |
| 77 | // code. This allows us to only regenerate once we've accumulated (hopefully) more than one new |
| 78 | // AccessCase. |
| 79 | class AccessCase { |
| 80 | WTF_MAKE_FAST_ALLOCATED; |
| 81 | public: |
| 82 | enum AccessType : uint8_t { |
| 83 | Load, |
| 84 | Transition, |
| 85 | Replace, |
| 86 | Miss, |
| 87 | GetGetter, |
| 88 | Getter, |
| 89 | Setter, |
| 90 | CustomValueGetter, |
| 91 | CustomAccessorGetter, |
| 92 | CustomValueSetter, |
| 93 | CustomAccessorSetter, |
| 94 | IntrinsicGetter, |
| 95 | InHit, |
| 96 | InMiss, |
| 97 | ArrayLength, |
| 98 | StringLength, |
| 99 | DirectArgumentsLength, |
| 100 | ScopedArgumentsLength, |
| 101 | ModuleNamespaceLoad, |
| 102 | InstanceOfHit, |
| 103 | InstanceOfMiss, |
| 104 | InstanceOfGeneric |
| 105 | }; |
| 106 | |
| 107 | enum State : uint8_t { |
| 108 | Primordial, |
| 109 | Committed, |
| 110 | Generated |
| 111 | }; |
| 112 | |
| 113 | template<typename T> |
| 114 | T& as() { return *static_cast<T*>(this); } |
| 115 | |
| 116 | template<typename T> |
| 117 | const T& as() const { return *static_cast<const T*>(this); } |
| 118 | |
| 119 | |
| 120 | template<typename AccessCaseType, typename... Arguments> |
| 121 | static std::unique_ptr<AccessCaseType> create(Arguments... arguments) |
| 122 | { |
| 123 | return std::unique_ptr<AccessCaseType>(new AccessCaseType(arguments...)); |
| 124 | } |
| 125 | |
| 126 | static std::unique_ptr<AccessCase> create(VM&, JSCell* owner, AccessType, PropertyOffset = invalidOffset, |
| 127 | Structure* = nullptr, const ObjectPropertyConditionSet& = ObjectPropertyConditionSet(), std::unique_ptr<PolyProtoAccessChain> = nullptr); |
| 128 | |
| 129 | // This create method should be used for transitions. |
| 130 | static std::unique_ptr<AccessCase> create(VM&, JSCell* owner, PropertyOffset, Structure* oldStructure, |
| 131 | Structure* newStructure, const ObjectPropertyConditionSet&, std::unique_ptr<PolyProtoAccessChain>); |
| 132 | |
| 133 | static std::unique_ptr<AccessCase> fromStructureStubInfo(VM&, JSCell* owner, StructureStubInfo&); |
| 134 | |
| 135 | AccessType type() const { return m_type; } |
| 136 | State state() const { return m_state; } |
| 137 | PropertyOffset offset() const { return m_offset; } |
| 138 | |
| 139 | Structure* structure() const |
| 140 | { |
| 141 | if (m_type == Transition) |
| 142 | return m_structure->previousID(); |
| 143 | return m_structure.get(); |
| 144 | } |
| 145 | bool guardedByStructureCheck() const; |
| 146 | |
| 147 | Structure* newStructure() const |
| 148 | { |
| 149 | ASSERT(m_type == Transition); |
| 150 | return m_structure.get(); |
| 151 | } |
| 152 | |
| 153 | ObjectPropertyConditionSet conditionSet() const { return m_conditionSet; } |
| 154 | |
| 155 | virtual bool hasAlternateBase() const; |
| 156 | virtual JSObject* alternateBase() const; |
| 157 | |
| 158 | virtual WatchpointSet* additionalSet() const { return nullptr; } |
| 159 | bool viaProxy() const { return m_viaProxy; } |
| 160 | |
| 161 | // If you supply the optional vector, this will append the set of cells that this will need to keep alive |
| 162 | // past the call. |
| 163 | bool doesCalls(Vector<JSCell*>* cellsToMark = nullptr) const; |
| 164 | |
| 165 | bool isGetter() const |
| 166 | { |
| 167 | switch (type()) { |
| 168 | case Getter: |
| 169 | case CustomValueGetter: |
| 170 | case CustomAccessorGetter: |
| 171 | return true; |
| 172 | default: |
| 173 | return false; |
| 174 | } |
| 175 | } |
| 176 | |
| 177 | bool isAccessor() const { return isGetter() || type() == Setter; } |
| 178 | |
| 179 | // Is it still possible for this case to ever be taken? Must call this as a prerequisite for |
| 180 | // calling generate() and friends. If this returns true, then you can call generate(). If |
| 181 | // this returns false, then generate() will crash. You must call generate() in the same epoch |
| 182 | // as when you called couldStillSucceed(). |
| 183 | bool couldStillSucceed() const; |
| 184 | |
| 185 | // If this method returns true, then it's a good idea to remove 'other' from the access once 'this' |
| 186 | // is added. This method assumes that in case of contradictions, 'this' represents a newer, and so |
| 187 | // more useful, truth. This method can be conservative; it will return false when it doubt. |
| 188 | bool canReplace(const AccessCase& other) const; |
| 189 | |
| 190 | void dump(PrintStream& out) const; |
| 191 | virtual void dumpImpl(PrintStream&, CommaPrinter&) const { } |
| 192 | |
| 193 | virtual ~AccessCase(); |
| 194 | |
| 195 | bool usesPolyProto() const |
| 196 | { |
| 197 | return !!m_polyProtoAccessChain; |
| 198 | } |
| 199 | |
| 200 | protected: |
| 201 | AccessCase(VM&, JSCell* owner, AccessType, PropertyOffset, Structure*, const ObjectPropertyConditionSet&, std::unique_ptr<PolyProtoAccessChain>); |
| 202 | AccessCase(AccessCase&&) = default; |
| 203 | AccessCase(const AccessCase& other) |
| 204 | : m_type(other.m_type) |
| 205 | , m_state(other.m_state) |
| 206 | , m_viaProxy(other.m_viaProxy) |
| 207 | , m_offset(other.m_offset) |
| 208 | , m_structure(other.m_structure) |
| 209 | , m_conditionSet(other.m_conditionSet) |
| 210 | { |
| 211 | if (other.m_polyProtoAccessChain) |
| 212 | m_polyProtoAccessChain = other.m_polyProtoAccessChain->clone(); |
| 213 | } |
| 214 | |
| 215 | AccessCase& operator=(const AccessCase&) = delete; |
| 216 | void resetState() { m_state = Primordial; } |
| 217 | |
| 218 | private: |
| 219 | friend class CodeBlock; |
| 220 | friend class PolymorphicAccess; |
| 221 | |
| 222 | bool visitWeak(VM&) const; |
| 223 | bool propagateTransitions(SlotVisitor&) const; |
| 224 | |
| 225 | // FIXME: This only exists because of how AccessCase puts post-generation things into itself. |
| 226 | // https://bugs.webkit.org/show_bug.cgi?id=156456 |
| 227 | virtual std::unique_ptr<AccessCase> clone() const; |
| 228 | |
| 229 | // Perform any action that must be performed before the end of the epoch in which the case |
| 230 | // was created. Returns a set of watchpoint sets that will need to be watched. |
| 231 | Vector<WatchpointSet*, 2> commit(VM&, const Identifier&); |
| 232 | |
| 233 | // Fall through on success. Two kinds of failures are supported: fall-through, which means that we |
| 234 | // should try a different case; and failure, which means that this was the right case but it needs |
| 235 | // help from the slow path. |
| 236 | void generateWithGuard(AccessGenerationState&, MacroAssembler::JumpList& fallThrough); |
| 237 | |
| 238 | // Fall through on success, add a jump to the failure list on failure. |
| 239 | void generate(AccessGenerationState&); |
| 240 | |
| 241 | void generateImpl(AccessGenerationState&); |
| 242 | |
| 243 | AccessType m_type; |
| 244 | State m_state { Primordial }; |
| 245 | protected: |
| 246 | // m_viaProxy is true only if the instance inherits (or it is) ProxyableAccessCase. |
| 247 | // We put this value here instead of ProxyableAccessCase to reduce the size of ProxyableAccessCase and its |
| 248 | // derived classes, which are super frequently allocated. |
| 249 | bool m_viaProxy { false }; |
| 250 | private: |
| 251 | PropertyOffset m_offset; |
| 252 | |
| 253 | // Usually this is the structure that we expect the base object to have. But, this is the *new* |
| 254 | // structure for a transition and we rely on the fact that it has a strong reference to the old |
| 255 | // structure. For proxies, this is the structure of the object behind the proxy. |
| 256 | WriteBarrier<Structure> m_structure; |
| 257 | |
| 258 | ObjectPropertyConditionSet m_conditionSet; |
| 259 | |
| 260 | std::unique_ptr<PolyProtoAccessChain> m_polyProtoAccessChain; |
| 261 | }; |
| 262 | |
| 263 | } // namespace JSC |
| 264 | |
| 265 | #endif |
| 266 | |