1/*
2 * Copyright (C) 2012-2015 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#include "config.h"
27#include "Watchpoint.h"
28
29#include "HeapInlines.h"
30#include "VM.h"
31#include <wtf/CompilationThread.h>
32
33namespace JSC {
34
35void StringFireDetail::dump(PrintStream& out) const
36{
37 out.print(m_string);
38}
39
40Watchpoint::~Watchpoint()
41{
42 if (isOnList()) {
43 // This will happen if we get destroyed before the set fires. That's totally a valid
44 // possibility. For example:
45 //
46 // CodeBlock has a Watchpoint on transition from structure S1. The transition never
47 // happens, but the CodeBlock gets destroyed because of GC.
48 remove();
49 }
50}
51
52void Watchpoint::fire(VM& vm, const FireDetail& detail)
53{
54 RELEASE_ASSERT(!isOnList());
55 fireInternal(vm, detail);
56}
57
58WatchpointSet::WatchpointSet(WatchpointState state)
59 : m_state(state)
60 , m_setIsNotEmpty(false)
61{
62}
63
64WatchpointSet::~WatchpointSet()
65{
66 // Remove all watchpoints, so that they don't try to remove themselves. Note that we
67 // don't fire watchpoints on deletion. We assume that any code that is interested in
68 // watchpoints already also separately has a mechanism to make sure that the code is
69 // either keeping the watchpoint set's owner alive, or does some weak reference thing.
70 while (!m_set.isEmpty())
71 m_set.begin()->remove();
72}
73
74void WatchpointSet::add(Watchpoint* watchpoint)
75{
76 ASSERT(!isCompilationThread());
77 ASSERT(state() != IsInvalidated);
78 if (!watchpoint)
79 return;
80 m_set.push(watchpoint);
81 m_setIsNotEmpty = true;
82 m_state = IsWatched;
83}
84
85void WatchpointSet::fireAllSlow(VM& vm, const FireDetail& detail)
86{
87 ASSERT(state() == IsWatched);
88
89 WTF::storeStoreFence();
90 m_state = IsInvalidated; // Do this first. Needed for adaptive watchpoints.
91 fireAllWatchpoints(vm, detail);
92 WTF::storeStoreFence();
93}
94
95void WatchpointSet::fireAllSlow(VM&, DeferredWatchpointFire* deferredWatchpoints)
96{
97 ASSERT(state() == IsWatched);
98
99 WTF::storeStoreFence();
100 deferredWatchpoints->takeWatchpointsToFire(this);
101 m_state = IsInvalidated; // Do after moving watchpoints to deferredWatchpoints so deferredWatchpoints gets our current state.
102 WTF::storeStoreFence();
103}
104
105void WatchpointSet::fireAllSlow(VM& vm, const char* reason)
106{
107 fireAllSlow(vm, StringFireDetail(reason));
108}
109
110void WatchpointSet::fireAllWatchpoints(VM& vm, const FireDetail& detail)
111{
112 // In case there are any adaptive watchpoints, we need to make sure that they see that this
113 // watchpoint has been already invalidated.
114 RELEASE_ASSERT(hasBeenInvalidated());
115
116 // Firing a watchpoint may cause a GC to happen. This GC could destroy various
117 // Watchpoints themselves while they're in the process of firing. It's not safe
118 // for most Watchpoints to be destructed while they're in the middle of firing.
119 // This GC could also destroy us, and we're not in a safe state to be destroyed.
120 // The safest thing to do is to DeferGCForAWhile to prevent this GC from happening.
121 DeferGCForAWhile deferGC(vm.heap);
122
123 while (!m_set.isEmpty()) {
124 Watchpoint* watchpoint = m_set.begin();
125 ASSERT(watchpoint->isOnList());
126
127 // Removing the Watchpoint before firing it makes it possible to implement watchpoints
128 // that add themselves to a different set when they fire. This kind of "adaptive"
129 // watchpoint can be used to track some semantic property that is more fine-graiend than
130 // what the set can convey. For example, we might care if a singleton object ever has a
131 // property called "foo". We can watch for this by checking if its Structure has "foo" and
132 // then watching its transitions. But then the watchpoint fires if any property is added.
133 // So, before the watchpoint decides to invalidate any code, it can check if it is
134 // possible to add itself to the transition watchpoint set of the singleton object's new
135 // Structure.
136 watchpoint->remove();
137 ASSERT(m_set.begin() != watchpoint);
138 ASSERT(!watchpoint->isOnList());
139
140 watchpoint->fire(vm, detail);
141 // After we fire the watchpoint, the watchpoint pointer may be a dangling pointer. That's
142 // fine, because we have no use for the pointer anymore.
143 }
144}
145
146void WatchpointSet::take(WatchpointSet* other)
147{
148 ASSERT(state() == ClearWatchpoint);
149 m_set.takeFrom(other->m_set);
150 m_setIsNotEmpty = other->m_setIsNotEmpty;
151 m_state = other->m_state;
152 other->m_setIsNotEmpty = false;
153}
154
155void InlineWatchpointSet::add(Watchpoint* watchpoint)
156{
157 inflate()->add(watchpoint);
158}
159
160void InlineWatchpointSet::fireAll(VM& vm, const char* reason)
161{
162 fireAll(vm, StringFireDetail(reason));
163}
164
165WatchpointSet* InlineWatchpointSet::inflateSlow()
166{
167 ASSERT(isThin());
168 ASSERT(!isCompilationThread());
169 WatchpointSet* fat = adoptRef(new WatchpointSet(decodeState(m_data))).leakRef();
170 WTF::storeStoreFence();
171 m_data = bitwise_cast<uintptr_t>(fat);
172 return fat;
173}
174
175void InlineWatchpointSet::freeFat()
176{
177 ASSERT(isFat());
178 fat()->deref();
179}
180
181DeferredWatchpointFire::DeferredWatchpointFire(VM& vm)
182 : m_vm(vm)
183 , m_watchpointsToFire(ClearWatchpoint)
184{
185}
186
187DeferredWatchpointFire::~DeferredWatchpointFire()
188{
189}
190
191void DeferredWatchpointFire::fireAll()
192{
193 if (m_watchpointsToFire.state() == IsWatched)
194 m_watchpointsToFire.fireAll(m_vm, *this);
195}
196
197void DeferredWatchpointFire::takeWatchpointsToFire(WatchpointSet* watchpointsToFire)
198{
199 ASSERT(m_watchpointsToFire.state() == ClearWatchpoint);
200 ASSERT(watchpointsToFire->state() == IsWatched);
201 m_watchpointsToFire.take(watchpointsToFire);
202}
203
204} // namespace JSC
205
206namespace WTF {
207
208void printInternal(PrintStream& out, JSC::WatchpointState state)
209{
210 switch (state) {
211 case JSC::ClearWatchpoint:
212 out.print("ClearWatchpoint");
213 return;
214 case JSC::IsWatched:
215 out.print("IsWatched");
216 return;
217 case JSC::IsInvalidated:
218 out.print("IsInvalidated");
219 return;
220 }
221 RELEASE_ASSERT_NOT_REACHED();
222}
223
224} // namespace WTF
225
226