2054bf75707f789f09cf40485bd1372339e1aa68
[WebKit-https.git] / Source / JavaScriptCore / bytecode / Watchpoint.h
1 /*
2  * Copyright (C) 2012, 2013 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 Watchpoint_h
27 #define Watchpoint_h
28
29 #include <wtf/RefCounted.h>
30 #include <wtf/SentinelLinkedList.h>
31
32 namespace JSC {
33
34 class Watchpoint : public BasicRawSentinelNode<Watchpoint> {
35 public:
36     Watchpoint()
37     {
38     }
39     
40     virtual ~Watchpoint();
41
42     void fire() { fireInternal(); }
43     
44 protected:
45     virtual void fireInternal() = 0;
46 };
47
48 enum InitialWatchpointSetMode { InitializedWatching, InitializedBlind };
49
50 class InlineWatchpointSet;
51
52 class WatchpointSet : public RefCounted<WatchpointSet> {
53 public:
54     WatchpointSet(InitialWatchpointSetMode);
55     ~WatchpointSet();
56     
57     // It is safe to call this from another thread.  It may return true
58     // even if the set actually had been invalidated, but that ought to happen
59     // only in the case of races, and should be rare.
60     bool isStillValid() const { return !m_isInvalidated; }
61     // Like isStillValid(), may be called from another thread.
62     bool hasBeenInvalidated() const { return m_isInvalidated; }
63     
64     // As a convenience, this will ignore 0. That's because code paths in the DFG
65     // that create speculation watchpoints may choose to bail out if speculation
66     // had already been terminated.
67     void add(Watchpoint*);
68     
69     // Force the watchpoint set to behave as if it was being watched even if no
70     // watchpoints have been installed. This will result in invalidation if the
71     // watchpoint would have fired. That's a pretty good indication that you
72     // probably don't want to set watchpoints, since we typically don't want to
73     // set watchpoints that we believe will actually be fired.
74     void startWatching() { m_isWatched = true; }
75     
76     void notifyWrite()
77     {
78         if (!m_isWatched)
79             return;
80         notifyWriteSlow();
81     }
82     
83     bool* addressOfIsWatched() { return &m_isWatched; }
84     
85     JS_EXPORT_PRIVATE void notifyWriteSlow(); // Call only if you've checked isWatched.
86     
87 private:
88     void fireAllWatchpoints();
89     
90     friend class InlineWatchpointSet;
91     
92     SentinelLinkedList<Watchpoint, BasicRawSentinelNode<Watchpoint> > m_set;
93     bool m_isWatched;
94     bool m_isInvalidated;
95 };
96
97 // InlineWatchpointSet is a low-overhead, non-copyable watchpoint set in which
98 // it is not possible to quickly query whether it is being watched in a single
99 // branch. There is a fairly simple tradeoff between WatchpointSet and
100 // InlineWatchpointSet:
101 //
102 // Do you have to emit JIT code that rapidly tests whether the watchpoint set
103 // is being watched?  If so, use WatchpointSet.
104 //
105 // Do you need multiple parties to have pointers to the same WatchpointSet?
106 // If so, use WatchpointSet.
107 //
108 // Do you have to allocate a lot of watchpoint sets?  If so, use
109 // InlineWatchpointSet unless you answered "yes" to the previous questions.
110 //
111 // InlineWatchpointSet will use just one pointer-width word of memory unless
112 // you actually add watchpoints to it, in which case it internally inflates
113 // to a pointer to a WatchpointSet, and transfers its state to the
114 // WatchpointSet.
115
116 class InlineWatchpointSet {
117     WTF_MAKE_NONCOPYABLE(InlineWatchpointSet);
118 public:
119     InlineWatchpointSet(InitialWatchpointSetMode mode)
120         : m_data((mode == InitializedWatching ? IsWatchedFlag : 0) | IsThinFlag)
121     {
122     }
123     
124     ~InlineWatchpointSet()
125     {
126         if (isThin())
127             return;
128         freeFat();
129     }
130     
131     // It is safe to call this from another thread.  It may return false
132     // even if the set actually had been invalidated, but that ought to happen
133     // only in the case of races, and should be rare.
134     bool hasBeenInvalidated() const
135     {
136         uintptr_t data = m_data;
137         if (isFat(data)) {
138             WTF::loadLoadFence();
139             return fat(data)->hasBeenInvalidated();
140         }
141         return data & IsInvalidatedFlag;
142     }
143     
144     // Like hasBeenInvalidated(), may be called from another thread.
145     bool isStillValid() const
146     {
147         return !hasBeenInvalidated();
148     }
149     
150     void add(Watchpoint*);
151     
152     void startWatching()
153     {
154         if (isFat()) {
155             fat()->startWatching();
156             return;
157         }
158         m_data |= IsWatchedFlag;
159     }
160     
161     void notifyWrite()
162     {
163         if (isFat()) {
164             fat()->notifyWrite();
165             return;
166         }
167         if (!(m_data & IsWatchedFlag))
168             return;
169         m_data |= IsInvalidatedFlag;
170     }
171     
172 private:
173     static const uintptr_t IsThinFlag        = 1;
174     static const uintptr_t IsInvalidatedFlag = 2;
175     static const uintptr_t IsWatchedFlag     = 4;
176     
177     static bool isThin(uintptr_t data) { return data & IsThinFlag; }
178     static bool isFat(uintptr_t data) { return !isThin(data); }
179     
180     bool isThin() const { return isThin(m_data); }
181     bool isFat() const { return isFat(m_data); };
182     
183     static WatchpointSet* fat(uintptr_t data)
184     {
185         return bitwise_cast<WatchpointSet*>(data);
186     }
187     
188     WatchpointSet* fat()
189     {
190         ASSERT(isFat());
191         return fat(m_data);
192     }
193     
194     const WatchpointSet* fat() const
195     {
196         ASSERT(isFat());
197         return fat(m_data);
198     }
199     
200     WatchpointSet* inflate()
201     {
202         if (LIKELY(isFat()))
203             return fat();
204         return inflateSlow();
205     }
206     
207     JS_EXPORT_PRIVATE WatchpointSet* inflateSlow();
208     JS_EXPORT_PRIVATE void freeFat();
209     
210     uintptr_t m_data;
211 };
212
213 } // namespace JSC
214
215 #endif // Watchpoint_h
216