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
35namespace JSC {
36
37struct 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.
79class AccessCase {
80 WTF_MAKE_FAST_ALLOCATED;
81public:
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
200protected:
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
218private:
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 };
245protected:
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 };
250private:
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