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