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