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