1/*
2 * Copyright (C) 2012-2016 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 <wtf/Atomics.h>
29#include <wtf/FastMalloc.h>
30#include <wtf/Noncopyable.h>
31#include <wtf/PrintStream.h>
32#include <wtf/ScopedLambda.h>
33#include <wtf/SentinelLinkedList.h>
34#include <wtf/ThreadSafeRefCounted.h>
35
36namespace JSC {
37
38class VM;
39
40class FireDetail {
41 void* operator new(size_t) = delete;
42
43public:
44 FireDetail()
45 {
46 }
47
48 virtual ~FireDetail()
49 {
50 }
51
52 virtual void dump(PrintStream&) const = 0;
53};
54
55class StringFireDetail : public FireDetail {
56public:
57 StringFireDetail(const char* string)
58 : m_string(string)
59 {
60 }
61
62 void dump(PrintStream& out) const override;
63
64private:
65 const char* m_string;
66};
67
68template<typename... Types>
69class LazyFireDetail : public FireDetail {
70public:
71 LazyFireDetail(const Types&... args)
72 {
73 m_lambda = scopedLambda<void(PrintStream&)>([&] (PrintStream& out) {
74 out.print(args...);
75 });
76 }
77
78 void dump(PrintStream& out) const override { m_lambda(out); }
79
80private:
81 ScopedLambda<void(PrintStream&)> m_lambda;
82};
83
84template<typename... Types>
85LazyFireDetail<Types...> createLazyFireDetail(const Types&... types)
86{
87 return LazyFireDetail<Types...>(types...);
88}
89
90class WatchpointSet;
91
92class Watchpoint : public BasicRawSentinelNode<Watchpoint> {
93 WTF_MAKE_NONCOPYABLE(Watchpoint);
94 WTF_MAKE_FAST_ALLOCATED;
95public:
96 Watchpoint() = default;
97
98 virtual ~Watchpoint();
99
100protected:
101 virtual void fireInternal(VM&, const FireDetail&) = 0;
102
103private:
104 friend class WatchpointSet;
105 void fire(VM&, const FireDetail&);
106};
107
108enum WatchpointState {
109 ClearWatchpoint,
110 IsWatched,
111 IsInvalidated
112};
113
114class InlineWatchpointSet;
115class DeferredWatchpointFire;
116class VM;
117
118class WatchpointSet : public ThreadSafeRefCounted<WatchpointSet> {
119 friend class LLIntOffsetsExtractor;
120 friend class DeferredWatchpointFire;
121public:
122 JS_EXPORT_PRIVATE WatchpointSet(WatchpointState);
123
124 // FIXME: In many cases, it would be amazing if this *did* fire the watchpoints. I suspect that
125 // this might be hard to get right, but still, it might be awesome.
126 JS_EXPORT_PRIVATE ~WatchpointSet(); // Note that this will not fire any of the watchpoints; if you need to know when a WatchpointSet dies then you need a separate mechanism for this.
127
128 static Ref<WatchpointSet> create(WatchpointState state)
129 {
130 return adoptRef(*new WatchpointSet(state));
131 }
132
133 // Fast way of getting the state, which only works from the main thread.
134 WatchpointState stateOnJSThread() const
135 {
136 return static_cast<WatchpointState>(m_state);
137 }
138
139 // It is safe to call this from another thread. It may return an old
140 // state. Guarantees that if *first* read the state() of the thing being
141 // watched and it returned IsWatched and *second* you actually read its
142 // value then it's safe to assume that if the state being watched changes
143 // then also the watchpoint state() will change to IsInvalidated.
144 WatchpointState state() const
145 {
146 WTF::loadLoadFence();
147 WatchpointState result = static_cast<WatchpointState>(m_state);
148 WTF::loadLoadFence();
149 return result;
150 }
151
152 // It is safe to call this from another thread. It may return true
153 // even if the set actually had been invalidated, but that ought to happen
154 // only in the case of races, and should be rare. Guarantees that if you
155 // call this after observing something that must imply that the set is
156 // invalidated, then you will see this return false. This is ensured by
157 // issuing a load-load fence prior to querying the state.
158 bool isStillValid() const
159 {
160 return state() != IsInvalidated;
161 }
162 // Like isStillValid(), may be called from another thread.
163 bool hasBeenInvalidated() const { return !isStillValid(); }
164
165 // As a convenience, this will ignore 0. That's because code paths in the DFG
166 // that create speculation watchpoints may choose to bail out if speculation
167 // had already been terminated.
168 void add(Watchpoint*);
169
170 // Force the watchpoint set to behave as if it was being watched even if no
171 // watchpoints have been installed. This will result in invalidation if the
172 // watchpoint would have fired. That's a pretty good indication that you
173 // probably don't want to set watchpoints, since we typically don't want to
174 // set watchpoints that we believe will actually be fired.
175 void startWatching()
176 {
177 ASSERT(m_state != IsInvalidated);
178 if (m_state == IsWatched)
179 return;
180 WTF::storeStoreFence();
181 m_state = IsWatched;
182 WTF::storeStoreFence();
183 }
184
185 template <typename T>
186 void fireAll(VM& vm, T& fireDetails)
187 {
188 if (LIKELY(m_state != IsWatched))
189 return;
190 fireAllSlow(vm, fireDetails);
191 }
192
193 void touch(VM& vm, const FireDetail& detail)
194 {
195 if (state() == ClearWatchpoint)
196 startWatching();
197 else
198 fireAll(vm, detail);
199 }
200
201 void touch(VM& vm, const char* reason)
202 {
203 touch(vm, StringFireDetail(reason));
204 }
205
206 void invalidate(VM& vm, const FireDetail& detail)
207 {
208 if (state() == IsWatched)
209 fireAll(vm, detail);
210 m_state = IsInvalidated;
211 }
212
213 void invalidate(VM& vm, const char* reason)
214 {
215 invalidate(vm, StringFireDetail(reason));
216 }
217
218 bool isBeingWatched() const
219 {
220 return m_setIsNotEmpty;
221 }
222
223 int8_t* addressOfState() { return &m_state; }
224 static ptrdiff_t offsetOfState() { return OBJECT_OFFSETOF(WatchpointSet, m_state); }
225 int8_t* addressOfSetIsNotEmpty() { return &m_setIsNotEmpty; }
226
227 JS_EXPORT_PRIVATE void fireAllSlow(VM&, const FireDetail&); // Call only if you've checked isWatched.
228 JS_EXPORT_PRIVATE void fireAllSlow(VM&, DeferredWatchpointFire* deferredWatchpoints); // Ditto.
229 JS_EXPORT_PRIVATE void fireAllSlow(VM&, const char* reason); // Ditto.
230
231private:
232 void fireAllWatchpoints(VM&, const FireDetail&);
233 void take(WatchpointSet* other);
234
235 friend class InlineWatchpointSet;
236
237 int8_t m_state;
238 int8_t m_setIsNotEmpty;
239
240 SentinelLinkedList<Watchpoint, BasicRawSentinelNode<Watchpoint>> m_set;
241};
242
243// InlineWatchpointSet is a low-overhead, non-copyable watchpoint set in which
244// it is not possible to quickly query whether it is being watched in a single
245// branch. There is a fairly simple tradeoff between WatchpointSet and
246// InlineWatchpointSet:
247//
248// Do you have to emit JIT code that rapidly tests whether the watchpoint set
249// is being watched? If so, use WatchpointSet.
250//
251// Do you need multiple parties to have pointers to the same WatchpointSet?
252// If so, use WatchpointSet.
253//
254// Do you have to allocate a lot of watchpoint sets? If so, use
255// InlineWatchpointSet unless you answered "yes" to the previous questions.
256//
257// InlineWatchpointSet will use just one pointer-width word of memory unless
258// you actually add watchpoints to it, in which case it internally inflates
259// to a pointer to a WatchpointSet, and transfers its state to the
260// WatchpointSet.
261
262class InlineWatchpointSet {
263 WTF_MAKE_NONCOPYABLE(InlineWatchpointSet);
264public:
265 InlineWatchpointSet(WatchpointState state)
266 : m_data(encodeState(state))
267 {
268 }
269
270 ~InlineWatchpointSet()
271 {
272 if (isThin())
273 return;
274 freeFat();
275 }
276
277 // Fast way of getting the state, which only works from the main thread.
278 WatchpointState stateOnJSThread() const
279 {
280 uintptr_t data = m_data;
281 if (isFat(data))
282 return fat(data)->stateOnJSThread();
283 return decodeState(data);
284 }
285
286 // It is safe to call this from another thread. It may return a prior state,
287 // but that should be fine since you should only perform actions based on the
288 // state if you also add a watchpoint.
289 WatchpointState state() const
290 {
291 WTF::loadLoadFence();
292 uintptr_t data = m_data;
293 WTF::loadLoadFence();
294 if (isFat(data))
295 return fat(data)->state();
296 return decodeState(data);
297 }
298
299 // It is safe to call this from another thread. It may return false
300 // even if the set actually had been invalidated, but that ought to happen
301 // only in the case of races, and should be rare.
302 bool hasBeenInvalidated() const
303 {
304 return state() == IsInvalidated;
305 }
306
307 // Like hasBeenInvalidated(), may be called from another thread.
308 bool isStillValid() const
309 {
310 return !hasBeenInvalidated();
311 }
312
313 void add(Watchpoint*);
314
315 void startWatching()
316 {
317 if (isFat()) {
318 fat()->startWatching();
319 return;
320 }
321 ASSERT(decodeState(m_data) != IsInvalidated);
322 m_data = encodeState(IsWatched);
323 }
324
325 template <typename T>
326 void fireAll(VM& vm, T fireDetails)
327 {
328 if (isFat()) {
329 fat()->fireAll(vm, fireDetails);
330 return;
331 }
332 if (decodeState(m_data) == ClearWatchpoint)
333 return;
334 m_data = encodeState(IsInvalidated);
335 WTF::storeStoreFence();
336 }
337
338 void invalidate(VM& vm, const FireDetail& detail)
339 {
340 if (isFat())
341 fat()->invalidate(vm, detail);
342 else
343 m_data = encodeState(IsInvalidated);
344 }
345
346 JS_EXPORT_PRIVATE void fireAll(VM&, const char* reason);
347
348 void touch(VM& vm, const FireDetail& detail)
349 {
350 if (isFat()) {
351 fat()->touch(vm, detail);
352 return;
353 }
354 uintptr_t data = m_data;
355 if (decodeState(data) == IsInvalidated)
356 return;
357 WTF::storeStoreFence();
358 if (decodeState(data) == ClearWatchpoint)
359 m_data = encodeState(IsWatched);
360 else
361 m_data = encodeState(IsInvalidated);
362 WTF::storeStoreFence();
363 }
364
365 void touch(VM& vm, const char* reason)
366 {
367 touch(vm, StringFireDetail(reason));
368 }
369
370 // Note that for any watchpoint that is visible from the DFG, it would be incorrect to write code like:
371 //
372 // if (w.isBeingWatched())
373 // w.fireAll()
374 //
375 // Concurrently to this, the DFG could do:
376 //
377 // if (w.isStillValid())
378 // perform optimizations;
379 // if (!w.isStillValid())
380 // retry compilation;
381 //
382 // Note that the DFG algorithm is widespread, and sound, because fireAll() and invalidate() will leave
383 // the watchpoint in a !isStillValid() state. Hence, if fireAll() or invalidate() interleaved between
384 // the first isStillValid() check and the second one, then it would simply cause the DFG to retry
385 // compilation later.
386 //
387 // But, if you change some piece of state that the DFG might optimize for, but invalidate the
388 // watchpoint by doing:
389 //
390 // if (w.isBeingWatched())
391 // w.fireAll()
392 //
393 // then the DFG would never know that you invalidated state between the two checks.
394 //
395 // There are two ways to work around this:
396 //
397 // - Call fireAll() without a isBeingWatched() check. Then, the DFG will know that the watchpoint has
398 // been invalidated when it does its second check.
399 //
400 // - Do not expose the watchpoint set to the DFG directly, and have your own way of validating whether
401 // the assumptions that the DFG thread used are still valid when the DFG code is installed.
402 bool isBeingWatched() const
403 {
404 if (isFat())
405 return fat()->isBeingWatched();
406 return false;
407 }
408
409 // We expose this because sometimes a client knows its about to start
410 // watching this InlineWatchpointSet, hence it'll become inflated regardless.
411 // Such clients may find it useful to have a WatchpointSet* pointer, for example,
412 // if they collect a Vector of WatchpointSet*.
413 WatchpointSet* inflate()
414 {
415 if (LIKELY(isFat()))
416 return fat();
417 return inflateSlow();
418 }
419
420private:
421 static const uintptr_t IsThinFlag = 1;
422 static const uintptr_t StateMask = 6;
423 static const uintptr_t StateShift = 1;
424
425 static bool isThin(uintptr_t data) { return data & IsThinFlag; }
426 static bool isFat(uintptr_t data) { return !isThin(data); }
427
428 static WatchpointState decodeState(uintptr_t data)
429 {
430 ASSERT(isThin(data));
431 return static_cast<WatchpointState>((data & StateMask) >> StateShift);
432 }
433
434 static uintptr_t encodeState(WatchpointState state)
435 {
436 return (static_cast<uintptr_t>(state) << StateShift) | IsThinFlag;
437 }
438
439 bool isThin() const { return isThin(m_data); }
440 bool isFat() const { return isFat(m_data); };
441
442 static WatchpointSet* fat(uintptr_t data)
443 {
444 return bitwise_cast<WatchpointSet*>(data);
445 }
446
447 WatchpointSet* fat()
448 {
449 ASSERT(isFat());
450 return fat(m_data);
451 }
452
453 const WatchpointSet* fat() const
454 {
455 ASSERT(isFat());
456 return fat(m_data);
457 }
458
459 JS_EXPORT_PRIVATE WatchpointSet* inflateSlow();
460 JS_EXPORT_PRIVATE void freeFat();
461
462 uintptr_t m_data;
463};
464
465class DeferredWatchpointFire : public FireDetail {
466 WTF_MAKE_NONCOPYABLE(DeferredWatchpointFire);
467public:
468 JS_EXPORT_PRIVATE DeferredWatchpointFire(VM&);
469 JS_EXPORT_PRIVATE ~DeferredWatchpointFire();
470
471 JS_EXPORT_PRIVATE void takeWatchpointsToFire(WatchpointSet*);
472 JS_EXPORT_PRIVATE void fireAll();
473
474 void dump(PrintStream& out) const override = 0;
475private:
476 VM& m_vm;
477 WatchpointSet m_watchpointsToFire;
478};
479
480} // namespace JSC
481
482namespace WTF {
483
484void printInternal(PrintStream& out, JSC::WatchpointState);
485
486} // namespace WTF
487
488