1 : /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : /*
39 : * JavaScript Debugging support - 'High Level' functions
40 : */
41 :
42 : #include "jsd.h"
43 :
44 : /***************************************************************************/
45 :
46 : /* XXX not 'static' because of old Mac CodeWarrior bug */
47 : JSCList _jsd_context_list = JS_INIT_STATIC_CLIST(&_jsd_context_list);
48 :
49 : /* these are used to connect JSD_SetUserCallbacks() with JSD_DebuggerOn() */
50 : static JSD_UserCallbacks _callbacks;
51 : static void* _user = NULL;
52 : static JSRuntime* _jsrt = NULL;
53 :
54 : #ifdef JSD_HAS_DANGEROUS_THREAD
55 : static void* _dangerousThread = NULL;
56 : #endif
57 :
58 : #ifdef JSD_THREADSAFE
59 : void* _jsd_global_lock = NULL;
60 : #endif
61 :
62 : #ifdef DEBUG
63 439078 : void JSD_ASSERT_VALID_CONTEXT(JSDContext* jsdc)
64 : {
65 439078 : JS_ASSERT(jsdc->inited);
66 439078 : JS_ASSERT(jsdc->jsrt);
67 439078 : JS_ASSERT(jsdc->dumbContext);
68 439078 : JS_ASSERT(jsdc->glob);
69 439078 : }
70 : #endif
71 :
72 : static JSClass global_class = {
73 : "JSDGlobal", JSCLASS_GLOBAL_FLAGS,
74 : JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
75 : JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
76 : JSCLASS_NO_OPTIONAL_MEMBERS
77 : };
78 :
79 : static JSBool
80 280 : _validateUserCallbacks(JSD_UserCallbacks* callbacks)
81 : {
82 560 : return !callbacks ||
83 0 : (callbacks->size && callbacks->size <= sizeof(JSD_UserCallbacks));
84 : }
85 :
86 : static JSDContext*
87 280 : _newJSDContext(JSRuntime* jsrt,
88 : JSD_UserCallbacks* callbacks,
89 : void* user,
90 : JSObject* scopeobj)
91 : {
92 280 : JSDContext* jsdc = NULL;
93 280 : JSCrossCompartmentCall *call = NULL;
94 : JSBool ok;
95 :
96 280 : if( ! jsrt )
97 0 : return NULL;
98 :
99 280 : if( ! _validateUserCallbacks(callbacks) )
100 0 : return NULL;
101 :
102 280 : jsdc = (JSDContext*) calloc(1, sizeof(JSDContext));
103 280 : if( ! jsdc )
104 0 : goto label_newJSDContext_failure;
105 :
106 280 : if( ! JSD_INIT_LOCKS(jsdc) )
107 : goto label_newJSDContext_failure;
108 :
109 280 : JS_INIT_CLIST(&jsdc->links);
110 :
111 280 : jsdc->jsrt = jsrt;
112 :
113 280 : if( callbacks )
114 0 : memcpy(&jsdc->userCallbacks, callbacks, callbacks->size);
115 :
116 280 : jsdc->user = user;
117 :
118 : #ifdef JSD_HAS_DANGEROUS_THREAD
119 : jsdc->dangerousThread = _dangerousThread;
120 : #endif
121 :
122 280 : JS_INIT_CLIST(&jsdc->threadsStates);
123 280 : JS_INIT_CLIST(&jsdc->sources);
124 280 : JS_INIT_CLIST(&jsdc->removedSources);
125 :
126 280 : jsdc->sourceAlterCount = 1;
127 :
128 280 : if( ! jsd_CreateAtomTable(jsdc) )
129 0 : goto label_newJSDContext_failure;
130 :
131 280 : if( ! jsd_InitObjectManager(jsdc) )
132 0 : goto label_newJSDContext_failure;
133 :
134 280 : if( ! jsd_InitScriptManager(jsdc) )
135 0 : goto label_newJSDContext_failure;
136 :
137 280 : jsdc->dumbContext = JS_NewContext(jsdc->jsrt, 256);
138 280 : if( ! jsdc->dumbContext )
139 0 : goto label_newJSDContext_failure;
140 :
141 280 : JS_BeginRequest(jsdc->dumbContext);
142 :
143 280 : jsdc->glob = JS_NewCompartmentAndGlobalObject(jsdc->dumbContext, &global_class, NULL);
144 :
145 280 : if( ! jsdc->glob )
146 0 : goto label_newJSDContext_failure;
147 :
148 280 : call = JS_EnterCrossCompartmentCall(jsdc->dumbContext, jsdc->glob);
149 280 : if( ! call )
150 0 : goto label_newJSDContext_failure;
151 :
152 280 : ok = JS_InitStandardClasses(jsdc->dumbContext, jsdc->glob);
153 :
154 280 : JS_LeaveCrossCompartmentCall(call);
155 280 : if( ! ok )
156 0 : goto label_newJSDContext_failure;
157 :
158 280 : JS_EndRequest(jsdc->dumbContext);
159 :
160 280 : jsdc->data = NULL;
161 280 : jsdc->inited = JS_TRUE;
162 :
163 280 : JSD_LOCK();
164 280 : JS_INSERT_LINK(&jsdc->links, &_jsd_context_list);
165 280 : JSD_UNLOCK();
166 :
167 280 : return jsdc;
168 :
169 : label_newJSDContext_failure:
170 0 : if( jsdc ) {
171 0 : jsd_DestroyObjectManager(jsdc);
172 0 : jsd_DestroyAtomTable(jsdc);
173 0 : if( jsdc->dumbContext )
174 0 : JS_EndRequest(jsdc->dumbContext);
175 0 : free(jsdc);
176 : }
177 0 : return NULL;
178 : }
179 :
180 : static void
181 280 : _destroyJSDContext(JSDContext* jsdc)
182 : {
183 280 : JSD_ASSERT_VALID_CONTEXT(jsdc);
184 :
185 280 : JSD_LOCK();
186 280 : JS_REMOVE_LINK(&jsdc->links);
187 280 : JSD_UNLOCK();
188 :
189 280 : jsd_DestroyObjectManager(jsdc);
190 280 : jsd_DestroyAtomTable(jsdc);
191 :
192 280 : jsdc->inited = JS_FALSE;
193 :
194 : /*
195 : * We should free jsdc here, but we let it leak in case there are any
196 : * asynchronous hooks calling into the system using it as a handle
197 : *
198 : * XXX we also leak the locks
199 : */
200 280 : JS_DestroyContext(jsdc->dumbContext);
201 280 : jsdc->dumbContext = NULL;
202 280 : }
203 :
204 : /***************************************************************************/
205 :
206 : JSDContext*
207 280 : jsd_DebuggerOnForUser(JSRuntime* jsrt,
208 : JSD_UserCallbacks* callbacks,
209 : void* user,
210 : JSObject* scopeobj)
211 : {
212 : JSDContext* jsdc;
213 280 : JSContext* iter = NULL;
214 :
215 280 : jsdc = _newJSDContext(jsrt, callbacks, user, scopeobj);
216 280 : if( ! jsdc )
217 0 : return NULL;
218 :
219 : /*
220 : * Set hooks here. The new/destroy script hooks are on even when
221 : * the debugger is paused. The destroy hook so we'll clean up
222 : * internal data structures when scripts are destroyed, and the
223 : * newscript hook for backwards compatibility for now. We'd like
224 : * to stop doing that.
225 : */
226 280 : JS_SetNewScriptHookProc(jsdc->jsrt, jsd_NewScriptHookProc, jsdc);
227 280 : JS_SetDestroyScriptHookProc(jsdc->jsrt, jsd_DestroyScriptHookProc, jsdc);
228 280 : jsd_DebuggerUnpause(jsdc);
229 : #ifdef LIVEWIRE
230 : LWDBG_SetNewScriptHookProc(jsd_NewScriptHookProc, jsdc);
231 : #endif
232 280 : if( jsdc->userCallbacks.setContext )
233 0 : jsdc->userCallbacks.setContext(jsdc, jsdc->user);
234 280 : return jsdc;
235 : }
236 :
237 : JSDContext*
238 0 : jsd_DebuggerOn(void)
239 : {
240 0 : JS_ASSERT(_jsrt);
241 0 : JS_ASSERT(_validateUserCallbacks(&_callbacks));
242 0 : return jsd_DebuggerOnForUser(_jsrt, &_callbacks, _user, NULL);
243 : }
244 :
245 : void
246 280 : jsd_DebuggerOff(JSDContext* jsdc)
247 : {
248 280 : jsd_DebuggerPause(jsdc, JS_TRUE);
249 : /* clear hooks here */
250 280 : JS_SetNewScriptHookProc(jsdc->jsrt, NULL, NULL);
251 280 : JS_SetDestroyScriptHookProc(jsdc->jsrt, NULL, NULL);
252 : #ifdef LIVEWIRE
253 : LWDBG_SetNewScriptHookProc(NULL,NULL);
254 : #endif
255 :
256 : /* clean up */
257 280 : JSD_LockScriptSubsystem(jsdc);
258 280 : jsd_DestroyScriptManager(jsdc);
259 280 : JSD_UnlockScriptSubsystem(jsdc);
260 280 : jsd_DestroyAllSources(jsdc);
261 :
262 280 : _destroyJSDContext(jsdc);
263 :
264 280 : if( jsdc->userCallbacks.setContext )
265 0 : jsdc->userCallbacks.setContext(NULL, jsdc->user);
266 280 : }
267 :
268 : void
269 282 : jsd_DebuggerPause(JSDContext* jsdc, JSBool forceAllHooksOff)
270 : {
271 282 : JS_SetDebuggerHandler(jsdc->jsrt, NULL, NULL);
272 282 : if (forceAllHooksOff || !(jsdc->flags & JSD_COLLECT_PROFILE_DATA)) {
273 282 : JS_SetExecuteHook(jsdc->jsrt, NULL, NULL);
274 282 : JS_SetCallHook(jsdc->jsrt, NULL, NULL);
275 : }
276 282 : JS_SetThrowHook(jsdc->jsrt, NULL, NULL);
277 282 : JS_SetDebugErrorHook(jsdc->jsrt, NULL, NULL);
278 282 : }
279 :
280 : void
281 282 : jsd_DebuggerUnpause(JSDContext* jsdc)
282 : {
283 282 : JS_SetDebuggerHandler(jsdc->jsrt, jsd_DebuggerHandler, jsdc);
284 282 : JS_SetExecuteHook(jsdc->jsrt, jsd_TopLevelCallHook, jsdc);
285 282 : JS_SetCallHook(jsdc->jsrt, jsd_FunctionCallHook, jsdc);
286 282 : JS_SetThrowHook(jsdc->jsrt, jsd_ThrowHandler, jsdc);
287 282 : JS_SetDebugErrorHook(jsdc->jsrt, jsd_DebugErrorHook, jsdc);
288 282 : }
289 :
290 : void
291 0 : jsd_SetUserCallbacks(JSRuntime* jsrt, JSD_UserCallbacks* callbacks, void* user)
292 : {
293 0 : _jsrt = jsrt;
294 0 : _user = user;
295 :
296 : #ifdef JSD_HAS_DANGEROUS_THREAD
297 : _dangerousThread = JSD_CURRENT_THREAD();
298 : #endif
299 :
300 0 : if( callbacks )
301 0 : memcpy(&_callbacks, callbacks, sizeof(JSD_UserCallbacks));
302 : else
303 0 : memset(&_callbacks, 0 , sizeof(JSD_UserCallbacks));
304 0 : }
305 :
306 : void*
307 0 : jsd_SetContextPrivate(JSDContext* jsdc, void *data)
308 : {
309 0 : void *rval = jsdc->data;
310 0 : jsdc->data = data;
311 0 : return data;
312 : }
313 :
314 : void*
315 0 : jsd_GetContextPrivate(JSDContext* jsdc)
316 : {
317 0 : return jsdc->data;
318 : }
319 :
320 : void
321 0 : jsd_ClearAllProfileData(JSDContext* jsdc)
322 : {
323 : JSDScript *current;
324 :
325 0 : JSD_LOCK_SCRIPTS(jsdc);
326 0 : current = (JSDScript *)jsdc->scripts.next;
327 0 : while (current != (JSDScript *)&jsdc->scripts)
328 : {
329 0 : jsd_ClearScriptProfileData(jsdc, current);
330 0 : current = (JSDScript *)current->links.next;
331 : }
332 :
333 0 : JSD_UNLOCK_SCRIPTS(jsdc);
334 0 : }
335 :
336 : JSDContext*
337 5 : jsd_JSDContextForJSContext(JSContext* context)
338 : {
339 : JSDContext* iter;
340 5 : JSDContext* jsdc = NULL;
341 5 : JSRuntime* runtime = JS_GetRuntime(context);
342 :
343 5 : JSD_LOCK();
344 10 : for( iter = (JSDContext*)_jsd_context_list.next;
345 5 : iter != (JSDContext*)&_jsd_context_list;
346 0 : iter = (JSDContext*)iter->links.next )
347 : {
348 5 : if( runtime == iter->jsrt )
349 : {
350 5 : jsdc = iter;
351 5 : break;
352 : }
353 : }
354 5 : JSD_UNLOCK();
355 5 : return jsdc;
356 : }
357 :
358 : static JSBool
359 2007 : jsd_DebugErrorHook(JSContext *cx, const char *message,
360 : JSErrorReport *report, void *closure)
361 : {
362 2007 : JSDContext* jsdc = (JSDContext*) closure;
363 : JSD_ErrorReporter errorReporter;
364 : void* errorReporterData;
365 :
366 2007 : if( ! jsdc )
367 : {
368 0 : JS_ASSERT(0);
369 0 : return JS_TRUE;
370 : }
371 : if( JSD_IS_DANGEROUS_THREAD(jsdc) )
372 : return JS_TRUE;
373 :
374 : /* local in case hook gets cleared on another thread */
375 2007 : JSD_LOCK();
376 2007 : errorReporter = jsdc->errorReporter;
377 2007 : errorReporterData = jsdc->errorReporterData;
378 2007 : JSD_UNLOCK();
379 :
380 2007 : if(!errorReporter)
381 2007 : return JS_TRUE;
382 :
383 0 : switch(errorReporter(jsdc, cx, message, report, errorReporterData))
384 : {
385 : case JSD_ERROR_REPORTER_PASS_ALONG:
386 0 : return JS_TRUE;
387 : case JSD_ERROR_REPORTER_RETURN:
388 0 : return JS_FALSE;
389 : case JSD_ERROR_REPORTER_DEBUG:
390 : {
391 : jsval rval;
392 : JSD_ExecutionHookProc hook;
393 : void* hookData;
394 :
395 : /* local in case hook gets cleared on another thread */
396 0 : JSD_LOCK();
397 0 : hook = jsdc->debugBreakHook;
398 0 : hookData = jsdc->debugBreakHookData;
399 0 : JSD_UNLOCK();
400 :
401 0 : jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_DEBUG_REQUESTED,
402 : hook, hookData, &rval);
403 : /* XXX Should make this dependent on ExecutionHook retval */
404 0 : return JS_TRUE;
405 : }
406 : case JSD_ERROR_REPORTER_CLEAR_RETURN:
407 0 : if(report && JSREPORT_IS_EXCEPTION(report->flags))
408 0 : JS_ClearPendingException(cx);
409 0 : return JS_FALSE;
410 : default:
411 0 : JS_ASSERT(0);
412 : break;
413 : }
414 0 : return JS_TRUE;
415 : }
416 :
417 : JSBool
418 282 : jsd_SetErrorReporter(JSDContext* jsdc,
419 : JSD_ErrorReporter reporter,
420 : void* callerdata)
421 : {
422 282 : JSD_LOCK();
423 282 : jsdc->errorReporter = reporter;
424 282 : jsdc->errorReporterData = callerdata;
425 282 : JSD_UNLOCK();
426 282 : return JS_TRUE;
427 : }
428 :
429 : JSBool
430 0 : jsd_GetErrorReporter(JSDContext* jsdc,
431 : JSD_ErrorReporter* reporter,
432 : void** callerdata)
433 : {
434 0 : JSD_LOCK();
435 0 : if( reporter )
436 0 : *reporter = jsdc->errorReporter;
437 0 : if( callerdata )
438 0 : *callerdata = jsdc->errorReporterData;
439 0 : JSD_UNLOCK();
440 0 : return JS_TRUE;
441 : }
|