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/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(StructureStubClearing, StructureStubClearingWatchpoint)
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
164enum WatchpointState {
165 ClearWatchpoint,
166 IsWatched,
167 IsInvalidated
168};
169
170class InlineWatchpointSet;
171class DeferredWatchpointFire;
172class VM;
173
174class WatchpointSet : public ThreadSafeRefCounted<WatchpointSet> {
175 friend class LLIntOffsetsExtractor;
176 friend class DeferredWatchpointFire;
177public:
178 JS_EXPORT_PRIVATE WatchpointSet(WatchpointState);
179
180 // FIXME: In many cases, it would be amazing if this *did* fire the watchpoints. I suspect that
181 // this might be hard to get right, but still, it might be awesome.
182 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.
183
184 static Ref<WatchpointSet> create(WatchpointState state)
185 {
186 return adoptRef(*new WatchpointSet(state));
187 }
188
189 // Fast way of getting the state, which only works from the main thread.
190 WatchpointState stateOnJSThread() const
191 {
192 return static_cast<WatchpointState>(m_state);
193 }
194
195 // It is safe to call this from another thread. It may return an old
196 // state. Guarantees that if *first* read the state() of the thing being
197 // watched and it returned IsWatched and *second* you actually read its
198 // value then it's safe to assume that if the state being watched changes
199 // then also the watchpoint state() will change to IsInvalidated.
200 WatchpointState state() const
201 {
202 WTF::loadLoadFence();
203 WatchpointState result = static_cast<WatchpointState>(m_state);
204 WTF::loadLoadFence();
205 return result;
206 }
207
208 // It is safe to call this from another thread. It may return true
209 // even if the set actually had been invalidated, but that ought to happen
210 // only in the case of races, and should be rare. Guarantees that if you
211 // call this after observing something that must imply that the set is
212 // invalidated, then you will see this return false. This is ensured by
213 // issuing a load-load fence prior to querying the state.
214 bool isStillValid() const
215 {
216 return state() != IsInvalidated;
217 }
218 // Like isStillValid(), may be called from another thread.
219 bool hasBeenInvalidated() const { return !isStillValid(); }
220
221 // As a convenience, this will ignore 0. That's because code paths in the DFG
222 // that create speculation watchpoints may choose to bail out if speculation
223 // had already been terminated.
224 void add(Watchpoint*);
225
226 // Force the watchpoint set to behave as if it was being watched even if no
227 // watchpoints have been installed. This will result in invalidation if the
228 // watchpoint would have fired. That's a pretty good indication that you
229 // probably don't want to set watchpoints, since we typically don't want to
230 // set watchpoints that we believe will actually be fired.
231 void startWatching()
232 {
233 ASSERT(m_state != IsInvalidated);
234 if (m_state == IsWatched)
235 return;
236 WTF::storeStoreFence();
237 m_state = IsWatched;
238 WTF::storeStoreFence();
239 }
240
241 template <typename T>
242 void fireAll(VM& vm, T& fireDetails)
243 {
244 if (LIKELY(m_state != IsWatched))
245 return;
246 fireAllSlow(vm, fireDetails);
247 }
248
249 void touch(VM& vm, const FireDetail& detail)
250 {
251 if (state() == ClearWatchpoint)
252 startWatching();
253 else
254 fireAll(vm, detail);
255 }
256
257 void touch(VM& vm, const char* reason)
258 {
259 touch(vm, StringFireDetail(reason));
260 }
261
262 void invalidate(VM& vm, const FireDetail& detail)
263 {
264 if (state() == IsWatched)
265 fireAll(vm, detail);
266 m_state = IsInvalidated;
267 }
268
269 void invalidate(VM& vm, const char* reason)
270 {
271 invalidate(vm, StringFireDetail(reason));
272 }
273
274 bool isBeingWatched() const
275 {
276 return m_setIsNotEmpty;
277 }
278
279 int8_t* addressOfState() { return &m_state; }
280 static ptrdiff_t offsetOfState() { return OBJECT_OFFSETOF(WatchpointSet, m_state); }
281 int8_t* addressOfSetIsNotEmpty() { return &m_setIsNotEmpty; }
282
283 JS_EXPORT_PRIVATE void fireAllSlow(VM&, const FireDetail&); // Call only if you've checked isWatched.
284 JS_EXPORT_PRIVATE void fireAllSlow(VM&, DeferredWatchpointFire* deferredWatchpoints); // Ditto.
285 JS_EXPORT_PRIVATE void fireAllSlow(VM&, const char* reason); // Ditto.
286
287private:
288 void fireAllWatchpoints(VM&, const FireDetail&);
289 void take(WatchpointSet* other);
290
291 friend class InlineWatchpointSet;
292
293 int8_t m_state;
294 int8_t m_setIsNotEmpty;
295
296 SentinelLinkedList<Watchpoint, PackedRawSentinelNode<Watchpoint>> m_set;
297};
298
299// InlineWatchpointSet is a low-overhead, non-copyable watchpoint set in which
300// it is not possible to quickly query whether it is being watched in a single
301// branch. There is a fairly simple tradeoff between WatchpointSet and
302// InlineWatchpointSet:
303//
304// Do you have to emit JIT code that rapidly tests whether the watchpoint set
305// is being watched? If so, use WatchpointSet.
306//
307// Do you need multiple parties to have pointers to the same WatchpointSet?
308// If so, use WatchpointSet.
309//
310// Do you have to allocate a lot of watchpoint sets? If so, use
311// InlineWatchpointSet unless you answered "yes" to the previous questions.
312//
313// InlineWatchpointSet will use just one pointer-width word of memory unless
314// you actually add watchpoints to it, in which case it internally inflates
315// to a pointer to a WatchpointSet, and transfers its state to the
316// WatchpointSet.
317
318class InlineWatchpointSet {
319 WTF_MAKE_NONCOPYABLE(InlineWatchpointSet);
320public:
321 InlineWatchpointSet(WatchpointState state)
322 : m_data(encodeState(state))
323 {
324 }
325
326 ~InlineWatchpointSet()
327 {
328 if (isThin())
329 return;
330 freeFat();
331 }
332
333 // Fast way of getting the state, which only works from the main thread.
334 WatchpointState stateOnJSThread() const
335 {
336 uintptr_t data = m_data;
337 if (isFat(data))
338 return fat(data)->stateOnJSThread();
339 return decodeState(data);
340 }
341
342 // It is safe to call this from another thread. It may return a prior state,
343 // but that should be fine since you should only perform actions based on the
344 // state if you also add a watchpoint.
345 WatchpointState state() const
346 {
347 WTF::loadLoadFence();
348 uintptr_t data = m_data;
349 WTF::loadLoadFence();
350 if (isFat(data))
351 return fat(data)->state();
352 return decodeState(data);
353 }
354
355 // It is safe to call this from another thread. It may return false
356 // even if the set actually had been invalidated, but that ought to happen
357 // only in the case of races, and should be rare.
358 bool hasBeenInvalidated() const
359 {
360 return state() == IsInvalidated;
361 }
362
363 // Like hasBeenInvalidated(), may be called from another thread.
364 bool isStillValid() const
365 {
366 return !hasBeenInvalidated();
367 }
368
369 void add(Watchpoint*);
370
371 void startWatching()
372 {
373 if (isFat()) {
374 fat()->startWatching();
375 return;
376 }
377 ASSERT(decodeState(m_data) != IsInvalidated);
378 m_data = encodeState(IsWatched);
379 }
380
381 template <typename T>
382 void fireAll(VM& vm, T fireDetails)
383 {
384 if (isFat()) {
385 fat()->fireAll(vm, fireDetails);
386 return;
387 }
388 if (decodeState(m_data) == ClearWatchpoint)
389 return;
390 m_data = encodeState(IsInvalidated);
391 WTF::storeStoreFence();
392 }
393
394 void invalidate(VM& vm, const FireDetail& detail)
395 {
396 if (isFat())
397 fat()->invalidate(vm, detail);
398 else
399 m_data = encodeState(IsInvalidated);
400 }
401
402 JS_EXPORT_PRIVATE void fireAll(VM&, const char* reason);
403
404 void touch(VM& vm, const FireDetail& detail)
405 {
406 if (isFat()) {
407 fat()->touch(vm, detail);
408 return;
409 }
410 uintptr_t data = m_data;
411 if (decodeState(data) == IsInvalidated)
412 return;
413 WTF::storeStoreFence();
414 if (decodeState(data) == ClearWatchpoint)
415 m_data = encodeState(IsWatched);
416 else
417 m_data = encodeState(IsInvalidated);
418 WTF::storeStoreFence();
419 }
420
421 void touch(VM& vm, const char* reason)
422 {
423 touch(vm, StringFireDetail(reason));
424 }
425
426 // Note that for any watchpoint that is visible from the DFG, it would be incorrect to write code like:
427 //
428 // if (w.isBeingWatched())
429 // w.fireAll()
430 //
431 // Concurrently to this, the DFG could do:
432 //
433 // if (w.isStillValid())
434 // perform optimizations;
435 // if (!w.isStillValid())
436 // retry compilation;
437 //
438 // Note that the DFG algorithm is widespread, and sound, because fireAll() and invalidate() will leave
439 // the watchpoint in a !isStillValid() state. Hence, if fireAll() or invalidate() interleaved between
440 // the first isStillValid() check and the second one, then it would simply cause the DFG to retry
441 // compilation later.
442 //
443 // But, if you change some piece of state that the DFG might optimize for, but invalidate the
444 // watchpoint by doing:
445 //
446 // if (w.isBeingWatched())
447 // w.fireAll()
448 //
449 // then the DFG would never know that you invalidated state between the two checks.
450 //
451 // There are two ways to work around this:
452 //
453 // - Call fireAll() without a isBeingWatched() check. Then, the DFG will know that the watchpoint has
454 // been invalidated when it does its second check.
455 //
456 // - Do not expose the watchpoint set to the DFG directly, and have your own way of validating whether
457 // the assumptions that the DFG thread used are still valid when the DFG code is installed.
458 bool isBeingWatched() const
459 {
460 if (isFat())
461 return fat()->isBeingWatched();
462 return false;
463 }
464
465 // We expose this because sometimes a client knows its about to start
466 // watching this InlineWatchpointSet, hence it'll become inflated regardless.
467 // Such clients may find it useful to have a WatchpointSet* pointer, for example,
468 // if they collect a Vector of WatchpointSet*.
469 WatchpointSet* inflate()
470 {
471 if (LIKELY(isFat()))
472 return fat();
473 return inflateSlow();
474 }
475
476private:
477 static const uintptr_t IsThinFlag = 1;
478 static const uintptr_t StateMask = 6;
479 static const uintptr_t StateShift = 1;
480
481 static bool isThin(uintptr_t data) { return data & IsThinFlag; }
482 static bool isFat(uintptr_t data) { return !isThin(data); }
483
484 static WatchpointState decodeState(uintptr_t data)
485 {
486 ASSERT(isThin(data));
487 return static_cast<WatchpointState>((data & StateMask) >> StateShift);
488 }
489
490 static uintptr_t encodeState(WatchpointState state)
491 {
492 return (static_cast<uintptr_t>(state) << StateShift) | IsThinFlag;
493 }
494
495 bool isThin() const { return isThin(m_data); }
496 bool isFat() const { return isFat(m_data); };
497
498 static WatchpointSet* fat(uintptr_t data)
499 {
500 return bitwise_cast<WatchpointSet*>(data);
501 }
502
503 WatchpointSet* fat()
504 {
505 ASSERT(isFat());
506 return fat(m_data);
507 }
508
509 const WatchpointSet* fat() const
510 {
511 ASSERT(isFat());
512 return fat(m_data);
513 }
514
515 JS_EXPORT_PRIVATE WatchpointSet* inflateSlow();
516 JS_EXPORT_PRIVATE void freeFat();
517
518 uintptr_t m_data;
519};
520
521class DeferredWatchpointFire : public FireDetail {
522 WTF_MAKE_NONCOPYABLE(DeferredWatchpointFire);
523public:
524 JS_EXPORT_PRIVATE DeferredWatchpointFire(VM&);
525 JS_EXPORT_PRIVATE ~DeferredWatchpointFire();
526
527 JS_EXPORT_PRIVATE void takeWatchpointsToFire(WatchpointSet*);
528 JS_EXPORT_PRIVATE void fireAll();
529
530 void dump(PrintStream& out) const override = 0;
531private:
532 VM& m_vm;
533 WatchpointSet m_watchpointsToFire;
534};
535
536} // namespace JSC
537
538namespace WTF {
539
540void printInternal(PrintStream& out, JSC::WatchpointState);
541
542} // namespace WTF
543
544