DFG should have adaptive structure watchpoints
[WebKit-https.git] / Source / JavaScriptCore / bytecode / PropertyCondition.h
1 /*
2  * Copyright (C) 2015 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 #ifndef PropertyCondition_h
27 #define PropertyCondition_h
28
29 #include "JSObject.h"
30 #include <wtf/HashMap.h>
31
32 namespace JSC {
33
34 class TrackedReferences;
35
36 class PropertyCondition {
37 public:
38     enum Kind {
39         Presence,
40         Absence,
41         AbsenceOfSetter,
42         Equivalence // An adaptive watchpoint on this will be a pair of watchpoints, and when the structure transitions, we will set the replacement watchpoint on the new structure.
43     };
44     
45     PropertyCondition()
46         : m_uid(nullptr)
47         , m_kind(Presence)
48     {
49         memset(&u, 0, sizeof(u));
50     }
51     
52     PropertyCondition(WTF::HashTableDeletedValueType)
53         : m_uid(nullptr)
54         , m_kind(Absence)
55     {
56         memset(&u, 0, sizeof(u));
57     }
58     
59     static PropertyCondition presenceWithoutBarrier(UniquedStringImpl* uid, PropertyOffset offset, unsigned attributes)
60     {
61         PropertyCondition result;
62         result.m_uid = uid;
63         result.m_kind = Presence;
64         result.u.presence.offset = offset;
65         result.u.presence.attributes = attributes;
66         return result;
67     }
68     
69     static PropertyCondition presence(
70         VM&, JSCell*, UniquedStringImpl* uid, PropertyOffset offset, unsigned attributes)
71     {
72         return presenceWithoutBarrier(uid, offset, attributes);
73     }
74
75     // NOTE: The prototype is the storedPrototype not the prototypeForLookup.
76     static PropertyCondition absenceWithoutBarrier(UniquedStringImpl* uid, JSObject* prototype)
77     {
78         PropertyCondition result;
79         result.m_uid = uid;
80         result.m_kind = Absence;
81         result.u.absence.prototype = prototype;
82         return result;
83     }
84     
85     static PropertyCondition absence(
86         VM& vm, JSCell* owner, UniquedStringImpl* uid, JSObject* prototype)
87     {
88         if (owner)
89             vm.heap.writeBarrier(owner);
90         return absenceWithoutBarrier(uid, prototype);
91     }
92     
93     static PropertyCondition absenceOfSetterWithoutBarrier(
94         UniquedStringImpl* uid, JSObject* prototype)
95     {
96         PropertyCondition result;
97         result.m_uid = uid;
98         result.m_kind = AbsenceOfSetter;
99         result.u.absence.prototype = prototype;
100         return result;
101     }
102     
103     static PropertyCondition absenceOfSetter(
104         VM& vm, JSCell* owner, UniquedStringImpl* uid, JSObject* prototype)
105     {
106         if (owner)
107             vm.heap.writeBarrier(owner);
108         return absenceOfSetterWithoutBarrier(uid, prototype);
109     }
110     
111     static PropertyCondition equivalenceWithoutBarrier(
112         UniquedStringImpl* uid, JSValue value)
113     {
114         PropertyCondition result;
115         result.m_uid = uid;
116         result.m_kind = Equivalence;
117         result.u.equivalence.value = JSValue::encode(value);
118         return result;
119     }
120         
121     static PropertyCondition equivalence(
122         VM& vm, JSCell* owner, UniquedStringImpl* uid, JSValue value)
123     {
124         if (value.isCell() && owner)
125             vm.heap.writeBarrier(owner);
126         return equivalenceWithoutBarrier(uid, value);
127     }
128         
129     bool operator!() const { return !m_uid && m_kind == Presence; };
130     
131     Kind kind() const { return m_kind; }
132     UniquedStringImpl* uid() const { return m_uid; }
133     
134     bool hasOffset() const { return !!*this && m_kind == Presence; };
135     PropertyOffset offset() const
136     {
137         ASSERT(hasOffset());
138         return u.presence.offset;
139     }
140     bool hasAttributes() const { return !!*this && m_kind == Presence; };
141     unsigned attributes() const
142     {
143         ASSERT(hasAttributes());
144         return u.presence.attributes;
145     }
146     
147     bool hasPrototype() const { return !!*this && (m_kind == Absence || m_kind == AbsenceOfSetter); }
148     JSObject* prototype() const
149     {
150         ASSERT(hasPrototype());
151         return u.absence.prototype;
152     }
153     
154     bool hasRequiredValue() const { return !!*this && m_kind == Equivalence; }
155     JSValue requiredValue() const
156     {
157         ASSERT(hasRequiredValue());
158         return JSValue::decode(u.equivalence.value);
159     }
160     
161     void dumpInContext(PrintStream&, DumpContext*) const;
162     void dump(PrintStream&) const;
163     
164     unsigned hash() const
165     {
166         unsigned result = WTF::PtrHash<UniquedStringImpl*>::hash(m_uid) + static_cast<unsigned>(m_kind);
167         switch (m_kind) {
168         case Presence:
169             result ^= u.presence.offset;
170             result ^= u.presence.attributes;
171             break;
172         case Absence:
173         case AbsenceOfSetter:
174             result ^= WTF::PtrHash<JSObject*>::hash(u.absence.prototype);
175             break;
176         case Equivalence:
177             result ^= EncodedJSValueHash::hash(u.equivalence.value);
178             break;
179         }
180         return result;
181     }
182     
183     bool operator==(const PropertyCondition& other) const
184     {
185         if (m_uid != other.m_uid)
186             return false;
187         if (m_kind != other.m_kind)
188             return false;
189         switch (m_kind) {
190         case Presence:
191             return u.presence.offset == other.u.presence.offset
192                 && u.presence.attributes == other.u.presence.attributes;
193         case Absence:
194         case AbsenceOfSetter:
195             return u.absence.prototype == other.u.absence.prototype;
196         case Equivalence:
197             return u.equivalence.value == other.u.equivalence.value;
198         }
199         RELEASE_ASSERT_NOT_REACHED();
200         return false;
201     }
202     
203     bool isHashTableDeletedValue() const
204     {
205         return !m_uid && m_kind == Absence;
206     }
207     
208     // Two conditions are compatible if they are identical or if they speak of different uids. If
209     // false is returned, you have to decide how to resolve the conflict - for example if there is
210     // a Presence and an Equivalence then in some cases you'll want the more general of the two
211     // while in other cases you'll want the more specific of the two. This will also return false
212     // for contradictions, like Presence and Absence on the same uid. By convention, invalid
213     // conditions aren't compatible with anything.
214     bool isCompatibleWith(const PropertyCondition& other) const
215     {
216         if (!*this || !other)
217             return false;
218         return *this == other || uid() != other.uid();
219     }
220     
221     // Checks if the object's structure claims that the property won't be intercepted.
222     bool isStillValidAssumingImpurePropertyWatchpoint(Structure*, JSObject* base = nullptr) const;
223     
224     // Returns true if we need an impure property watchpoint to ensure validity even if
225     // isStillValidAccordingToStructure() returned true.
226     bool validityRequiresImpurePropertyWatchpoint(Structure*) const;
227     
228     // Checks if the condition is still valid right now for the given object and structure.
229     // May conservatively return false, if the object and structure alone don't guarantee the
230     // condition. This happens for an Absence condition on an object that may have impure
231     // properties. If the object is not supplied, then a "true" return indicates that checking if
232     // an object has the given structure guarantees the condition still holds. If an object is
233     // supplied, then you may need to use some other watchpoints on the object to guarantee the
234     // condition in addition to the structure check.
235     bool isStillValid(Structure*, JSObject* base = nullptr) const;
236     
237     // In some cases, the condition is not watchable, but could be made watchable by enabling the
238     // appropriate watchpoint. For example, replacement watchpoints are enabled only when some
239     // access is cached on the property in some structure. This is mainly to save space for
240     // dictionary properties or properties that never get very hot. But, it's always safe to
241     // enable watching, provided that this is called from the main thread.
242     enum WatchabilityEffort {
243         // This is the default. It means that we don't change the state of any Structure or
244         // object, and implies that if the property happens not to be watchable then we don't make
245         // it watchable. This is mandatory if calling from a JIT thread. This is also somewhat
246         // preferable when first deciding whether to watch a condition for the first time (i.e.
247         // not from a watchpoint fire that causes us to see if we should adapt), since a
248         // watchpoint not being initialized for watching implies that maybe we don't know enough
249         // yet to make it profitable to watch -- as in, the thing being watched may not have
250         // stabilized yet. We prefer to only assume that a condition will hold if it has been
251         // known to hold for a while already.
252         MakeNoChanges,
253         
254         // Do what it takes to ensure that the property can be watched, if doing so has no
255         // user-observable effect. For now this just means that we will ensure that a property
256         // replacement watchpoint is enabled if it hadn't been enabled already. Do not use this
257         // from JIT threads, since the act of enabling watchpoints is not thread-safe.
258         EnsureWatchability
259     };
260     
261     // This means that it's still valid and we could enforce validity by setting a transition
262     // watchpoint on the structure and possibly an impure property watchpoint.
263     bool isWatchableAssumingImpurePropertyWatchpoint(
264         Structure*, JSObject* base = nullptr, WatchabilityEffort = MakeNoChanges) const;
265     
266     // This means that it's still valid and we could enforce validity by setting a transition
267     // watchpoint on the structure.
268     bool isWatchable(
269         Structure*, JSObject* base = nullptr, WatchabilityEffort = MakeNoChanges) const;
270     
271     bool watchingRequiresStructureTransitionWatchpoint() const
272     {
273         // Currently, this is required for all of our conditions.
274         return !!*this;
275     }
276     bool watchingRequiresReplacementWatchpoint() const
277     {
278         return !!*this && m_kind == Equivalence;
279     }
280     
281     // This means that the objects involved in this are still live.
282     bool isStillLive() const;
283     
284     void validateReferences(const TrackedReferences&) const;
285
286     static bool isValidValueForAttributes(JSValue value, unsigned attributes);
287
288     bool isValidValueForPresence(JSValue) const;
289
290     PropertyCondition attemptToMakeEquivalenceWithoutBarrier(JSObject* base) const;
291
292 private:
293     bool isWatchableWhenValid(Structure*, WatchabilityEffort) const;
294
295     UniquedStringImpl* m_uid;
296     Kind m_kind;
297     union {
298         struct {
299             PropertyOffset offset;
300             unsigned attributes;
301         } presence;
302         struct {
303             JSObject* prototype;
304         } absence;
305         struct {
306             EncodedJSValue value;
307         } equivalence;
308     } u;
309 };
310
311 struct PropertyConditionHash {
312     static unsigned hash(const PropertyCondition& key) { return key.hash(); }
313     static bool equal(
314         const PropertyCondition& a, const PropertyCondition& b)
315     {
316         return a == b;
317     }
318     static const bool safeToCompareToEmptyOrDeleted = true;
319 };
320
321 } // namespace JSC
322
323 namespace WTF {
324
325 void printInternal(PrintStream&, JSC::PropertyCondition::Kind);
326
327 template<typename T> struct DefaultHash;
328 template<> struct DefaultHash<JSC::PropertyCondition> {
329     typedef JSC::PropertyConditionHash Hash;
330 };
331
332 template<typename T> struct HashTraits;
333 template<> struct HashTraits<JSC::PropertyCondition> : SimpleClassHashTraits<JSC::PropertyCondition> { };
334
335 } // namespace WTF
336
337 #endif // PropertyCondition_h
338