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