|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: set ts=8 sts=4 et sw=4 tw=99: |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 /* |
|
8 * JavaScript Debugging support - Hook support |
|
9 */ |
|
10 |
|
11 #include "jsd.h" |
|
12 |
|
13 JSTrapStatus |
|
14 jsd_InterruptHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, |
|
15 void *closure) |
|
16 { |
|
17 JSDScript* jsdscript; |
|
18 JSDContext* jsdc = (JSDContext*) closure; |
|
19 JSD_ExecutionHookProc hook; |
|
20 void* hookData; |
|
21 |
|
22 if( ! jsdc || ! jsdc->inited ) |
|
23 return JSTRAP_CONTINUE; |
|
24 |
|
25 if( JSD_IS_DANGEROUS_THREAD(jsdc) ) |
|
26 return JSTRAP_CONTINUE; |
|
27 |
|
28 /* local in case jsdc->interruptHook gets cleared on another thread */ |
|
29 JSD_LOCK(); |
|
30 hook = jsdc->interruptHook; |
|
31 hookData = jsdc->interruptHookData; |
|
32 JSD_UNLOCK(); |
|
33 |
|
34 if (!hook) |
|
35 return JSTRAP_CONTINUE; |
|
36 |
|
37 JSD_LOCK_SCRIPTS(jsdc); |
|
38 jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, script, JSNullFramePtr()); |
|
39 JSD_UNLOCK_SCRIPTS(jsdc); |
|
40 if( ! jsdscript ) |
|
41 return JSTRAP_CONTINUE; |
|
42 |
|
43 return jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_INTERRUPTED, |
|
44 hook, hookData, rval); |
|
45 } |
|
46 |
|
47 JSTrapStatus |
|
48 jsd_DebuggerHandler(JSContext *cx, JSScript *script, jsbytecode *pc, |
|
49 jsval *rval, void *closure) |
|
50 { |
|
51 JSDScript* jsdscript; |
|
52 JSDContext* jsdc = (JSDContext*) closure; |
|
53 JSD_ExecutionHookProc hook; |
|
54 void* hookData; |
|
55 |
|
56 if( ! jsdc || ! jsdc->inited ) |
|
57 return JSTRAP_CONTINUE; |
|
58 |
|
59 if( JSD_IS_DANGEROUS_THREAD(jsdc) ) |
|
60 return JSTRAP_CONTINUE; |
|
61 |
|
62 /* local in case jsdc->debuggerHook gets cleared on another thread */ |
|
63 JSD_LOCK(); |
|
64 hook = jsdc->debuggerHook; |
|
65 hookData = jsdc->debuggerHookData; |
|
66 JSD_UNLOCK(); |
|
67 if(!hook) |
|
68 return JSTRAP_CONTINUE; |
|
69 |
|
70 JSD_LOCK_SCRIPTS(jsdc); |
|
71 jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, script, JSNullFramePtr()); |
|
72 JSD_UNLOCK_SCRIPTS(jsdc); |
|
73 if( ! jsdscript ) |
|
74 return JSTRAP_CONTINUE; |
|
75 |
|
76 return jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_DEBUGGER_KEYWORD, |
|
77 hook, hookData, rval); |
|
78 } |
|
79 |
|
80 |
|
81 JSTrapStatus |
|
82 jsd_ThrowHandler(JSContext *cx, JSScript *script, jsbytecode *pc, |
|
83 jsval *rvalArg, void *closure) |
|
84 { |
|
85 JSDScript* jsdscript; |
|
86 JSDContext* jsdc = (JSDContext*) closure; |
|
87 JSD_ExecutionHookProc hook; |
|
88 void* hookData; |
|
89 |
|
90 if( ! jsdc || ! jsdc->inited ) |
|
91 return JSTRAP_CONTINUE; |
|
92 |
|
93 if( JSD_IS_DANGEROUS_THREAD(jsdc) ) |
|
94 return JSTRAP_CONTINUE; |
|
95 |
|
96 /* local in case jsdc->throwHook gets cleared on another thread */ |
|
97 JSD_LOCK(); |
|
98 hook = jsdc->throwHook; |
|
99 hookData = jsdc->throwHookData; |
|
100 JSD_UNLOCK(); |
|
101 if (!hook) |
|
102 return JSTRAP_CONTINUE; |
|
103 |
|
104 JSD_LOCK_SCRIPTS(jsdc); |
|
105 jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, script, JSNullFramePtr()); |
|
106 JSD_UNLOCK_SCRIPTS(jsdc); |
|
107 if( ! jsdscript ) |
|
108 return JSTRAP_CONTINUE; |
|
109 |
|
110 JS::RootedValue rval(cx); |
|
111 JS_GetPendingException(cx, &rval); |
|
112 |
|
113 JSTrapStatus result = jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_THROW, |
|
114 hook, hookData, rval.address()); |
|
115 *rvalArg = rval; |
|
116 return result; |
|
117 } |
|
118 |
|
119 JSTrapStatus |
|
120 jsd_CallExecutionHook(JSDContext* jsdc, |
|
121 JSContext *cx, |
|
122 unsigned type, |
|
123 JSD_ExecutionHookProc hook, |
|
124 void* hookData, |
|
125 jsval* rval) |
|
126 { |
|
127 unsigned hookanswer = JSD_HOOK_THROW == type ? |
|
128 JSD_HOOK_RETURN_CONTINUE_THROW : |
|
129 JSD_HOOK_RETURN_CONTINUE; |
|
130 JSDThreadState* jsdthreadstate; |
|
131 |
|
132 if(hook && nullptr != (jsdthreadstate = jsd_NewThreadState(jsdc, cx))) |
|
133 { |
|
134 if ((type != JSD_HOOK_THROW && type != JSD_HOOK_INTERRUPTED) || |
|
135 jsdc->flags & JSD_MASK_TOP_FRAME_ONLY || |
|
136 !(jsdthreadstate->flags & TS_HAS_DISABLED_FRAME)) |
|
137 { |
|
138 /* |
|
139 * if it's not a throw and it's not an interrupt, |
|
140 * or we're only masking the top frame, |
|
141 * or there are no disabled frames in this stack, |
|
142 * then call out. |
|
143 */ |
|
144 hookanswer = hook(jsdc, jsdthreadstate, type, hookData, rval); |
|
145 jsd_DestroyThreadState(jsdc, jsdthreadstate); |
|
146 } |
|
147 } |
|
148 |
|
149 switch(hookanswer) |
|
150 { |
|
151 case JSD_HOOK_RETURN_ABORT: |
|
152 case JSD_HOOK_RETURN_HOOK_ERROR: |
|
153 return JSTRAP_ERROR; |
|
154 case JSD_HOOK_RETURN_RET_WITH_VAL: |
|
155 return JSTRAP_RETURN; |
|
156 case JSD_HOOK_RETURN_THROW_WITH_VAL: |
|
157 return JSTRAP_THROW; |
|
158 case JSD_HOOK_RETURN_CONTINUE: |
|
159 break; |
|
160 case JSD_HOOK_RETURN_CONTINUE_THROW: |
|
161 /* only makes sense for jsd_ThrowHandler (which init'd rval) */ |
|
162 MOZ_ASSERT(JSD_HOOK_THROW == type); |
|
163 return JSTRAP_THROW; |
|
164 default: |
|
165 MOZ_ASSERT(0); |
|
166 break; |
|
167 } |
|
168 return JSTRAP_CONTINUE; |
|
169 } |
|
170 |
|
171 bool |
|
172 jsd_CallCallHook (JSDContext* jsdc, |
|
173 JSContext *cx, |
|
174 unsigned type, |
|
175 JSD_CallHookProc hook, |
|
176 void* hookData) |
|
177 { |
|
178 bool hookanswer; |
|
179 JSDThreadState* jsdthreadstate; |
|
180 |
|
181 hookanswer = false; |
|
182 if(hook && nullptr != (jsdthreadstate = jsd_NewThreadState(jsdc, cx))) |
|
183 { |
|
184 hookanswer = hook(jsdc, jsdthreadstate, type, hookData); |
|
185 jsd_DestroyThreadState(jsdc, jsdthreadstate); |
|
186 } |
|
187 |
|
188 return hookanswer; |
|
189 } |
|
190 |
|
191 bool |
|
192 jsd_SetInterruptHook(JSDContext* jsdc, |
|
193 JSD_ExecutionHookProc hook, |
|
194 void* callerdata) |
|
195 { |
|
196 JSD_LOCK(); |
|
197 jsdc->interruptHookData = callerdata; |
|
198 jsdc->interruptHook = hook; |
|
199 JS_SetInterrupt(jsdc->jsrt, jsd_InterruptHandler, (void*) jsdc); |
|
200 JSD_UNLOCK(); |
|
201 |
|
202 return true; |
|
203 } |
|
204 |
|
205 bool |
|
206 jsd_ClearInterruptHook(JSDContext* jsdc) |
|
207 { |
|
208 JSD_LOCK(); |
|
209 JS_ClearInterrupt(jsdc->jsrt, nullptr, nullptr); |
|
210 jsdc->interruptHook = nullptr; |
|
211 JSD_UNLOCK(); |
|
212 |
|
213 return true; |
|
214 } |
|
215 |
|
216 bool |
|
217 jsd_SetDebugBreakHook(JSDContext* jsdc, |
|
218 JSD_ExecutionHookProc hook, |
|
219 void* callerdata) |
|
220 { |
|
221 JSD_LOCK(); |
|
222 jsdc->debugBreakHookData = callerdata; |
|
223 jsdc->debugBreakHook = hook; |
|
224 JSD_UNLOCK(); |
|
225 |
|
226 return true; |
|
227 } |
|
228 |
|
229 bool |
|
230 jsd_ClearDebugBreakHook(JSDContext* jsdc) |
|
231 { |
|
232 JSD_LOCK(); |
|
233 jsdc->debugBreakHook = nullptr; |
|
234 JSD_UNLOCK(); |
|
235 |
|
236 return true; |
|
237 } |
|
238 |
|
239 bool |
|
240 jsd_SetDebuggerHook(JSDContext* jsdc, |
|
241 JSD_ExecutionHookProc hook, |
|
242 void* callerdata) |
|
243 { |
|
244 JSD_LOCK(); |
|
245 jsdc->debuggerHookData = callerdata; |
|
246 jsdc->debuggerHook = hook; |
|
247 JSD_UNLOCK(); |
|
248 |
|
249 return true; |
|
250 } |
|
251 |
|
252 bool |
|
253 jsd_ClearDebuggerHook(JSDContext* jsdc) |
|
254 { |
|
255 JSD_LOCK(); |
|
256 jsdc->debuggerHook = nullptr; |
|
257 JSD_UNLOCK(); |
|
258 |
|
259 return true; |
|
260 } |
|
261 |
|
262 bool |
|
263 jsd_SetThrowHook(JSDContext* jsdc, |
|
264 JSD_ExecutionHookProc hook, |
|
265 void* callerdata) |
|
266 { |
|
267 JSD_LOCK(); |
|
268 jsdc->throwHookData = callerdata; |
|
269 jsdc->throwHook = hook; |
|
270 JSD_UNLOCK(); |
|
271 |
|
272 return true; |
|
273 } |
|
274 |
|
275 bool |
|
276 jsd_ClearThrowHook(JSDContext* jsdc) |
|
277 { |
|
278 JSD_LOCK(); |
|
279 jsdc->throwHook = nullptr; |
|
280 JSD_UNLOCK(); |
|
281 |
|
282 return true; |
|
283 } |
|
284 |
|
285 bool |
|
286 jsd_SetFunctionHook(JSDContext* jsdc, |
|
287 JSD_CallHookProc hook, |
|
288 void* callerdata) |
|
289 { |
|
290 JSD_LOCK(); |
|
291 jsdc->functionHookData = callerdata; |
|
292 jsdc->functionHook = hook; |
|
293 JSD_UNLOCK(); |
|
294 |
|
295 return true; |
|
296 } |
|
297 |
|
298 bool |
|
299 jsd_ClearFunctionHook(JSDContext* jsdc) |
|
300 { |
|
301 JSD_LOCK(); |
|
302 jsdc->functionHook = nullptr; |
|
303 JSD_UNLOCK(); |
|
304 |
|
305 return true; |
|
306 } |
|
307 |
|
308 bool |
|
309 jsd_SetTopLevelHook(JSDContext* jsdc, |
|
310 JSD_CallHookProc hook, |
|
311 void* callerdata) |
|
312 { |
|
313 JSD_LOCK(); |
|
314 jsdc->toplevelHookData = callerdata; |
|
315 jsdc->toplevelHook = hook; |
|
316 JSD_UNLOCK(); |
|
317 |
|
318 return true; |
|
319 } |
|
320 |
|
321 bool |
|
322 jsd_ClearTopLevelHook(JSDContext* jsdc) |
|
323 { |
|
324 JSD_LOCK(); |
|
325 jsdc->toplevelHook = nullptr; |
|
326 JSD_UNLOCK(); |
|
327 |
|
328 return true; |
|
329 } |
|
330 |