Imported Upstream version 1.0.0
[platform/upstream/js.git] / js / jsd / jsd_high.c
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 void JSD_ASSERT_VALID_CONTEXT(JSDContext* jsdc)
64 {
65     JS_ASSERT(jsdc->inited);
66     JS_ASSERT(jsdc->jsrt);
67     JS_ASSERT(jsdc->dumbContext);
68     JS_ASSERT(jsdc->glob);
69 }
70 #endif
71
72 static JSClass global_class = {
73     "JSDGlobal", JSCLASS_GLOBAL_FLAGS,
74     JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
75     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub,
76     JSCLASS_NO_OPTIONAL_MEMBERS
77 };
78
79 static JSBool
80 _validateUserCallbacks(JSD_UserCallbacks* callbacks)
81 {
82     return !callbacks ||
83            (callbacks->size && callbacks->size <= sizeof(JSD_UserCallbacks));
84 }    
85
86 static JSDContext*
87 _newJSDContext(JSRuntime*         jsrt, 
88                JSD_UserCallbacks* callbacks, 
89                void*              user,
90                JSObject*          scopeobj)
91 {
92     JSDContext* jsdc = NULL;
93     JSCrossCompartmentCall *call = NULL;
94
95     if( ! jsrt )
96         return NULL;
97
98     if( ! _validateUserCallbacks(callbacks) )
99         return NULL;
100
101     jsdc = (JSDContext*) calloc(1, sizeof(JSDContext));
102     if( ! jsdc )
103         goto label_newJSDContext_failure;
104
105     if( ! JSD_INIT_LOCKS(jsdc) )
106         goto label_newJSDContext_failure;
107
108     JS_INIT_CLIST(&jsdc->links);
109
110     jsdc->jsrt = jsrt;
111
112     if( callbacks )
113         memcpy(&jsdc->userCallbacks, callbacks, callbacks->size);
114     
115     jsdc->user = user;
116
117 #ifdef JSD_HAS_DANGEROUS_THREAD
118     jsdc->dangerousThread = _dangerousThread;
119 #endif
120
121     JS_INIT_CLIST(&jsdc->threadsStates);
122     JS_INIT_CLIST(&jsdc->sources);
123     JS_INIT_CLIST(&jsdc->removedSources);
124
125     jsdc->sourceAlterCount = 1;
126
127     if( ! jsd_CreateAtomTable(jsdc) )
128         goto label_newJSDContext_failure;
129
130     if( ! jsd_InitObjectManager(jsdc) )
131         goto label_newJSDContext_failure;
132
133     if( ! jsd_InitScriptManager(jsdc) )
134         goto label_newJSDContext_failure;
135
136     jsdc->dumbContext = JS_NewContext(jsdc->jsrt, 256);
137     if( ! jsdc->dumbContext )
138         goto label_newJSDContext_failure;
139
140     JS_BeginRequest(jsdc->dumbContext);
141
142     jsdc->glob = JS_NewCompartmentAndGlobalObject(jsdc->dumbContext, &global_class, NULL);
143
144     if( ! jsdc->glob )
145         goto label_newJSDContext_failure;
146
147     call = JS_EnterCrossCompartmentCall(jsdc->dumbContext, jsdc->glob);
148     if( ! call )
149         goto label_newJSDContext_failure;
150
151     if( ! JS_InitStandardClasses(jsdc->dumbContext, jsdc->glob) )
152         goto label_newJSDContext_failure;
153
154     if( call )
155         JS_LeaveCrossCompartmentCall(call);
156
157     JS_EndRequest(jsdc->dumbContext);
158
159     jsdc->data = NULL;
160     jsdc->inited = JS_TRUE;
161
162     JSD_LOCK();
163     JS_INSERT_LINK(&jsdc->links, &_jsd_context_list);
164     JSD_UNLOCK();
165
166     return jsdc;
167
168 label_newJSDContext_failure:
169     if( jsdc ) {
170         jsd_DestroyObjectManager(jsdc);
171         jsd_DestroyAtomTable(jsdc);
172         JS_EndRequest(jsdc->dumbContext);
173         free(jsdc);
174     }
175     return NULL;
176 }
177
178 static void
179 _destroyJSDContext(JSDContext* jsdc)
180 {
181     JSD_ASSERT_VALID_CONTEXT(jsdc);
182
183     JSD_LOCK();
184     JS_REMOVE_LINK(&jsdc->links);
185     JSD_UNLOCK();
186
187     jsd_DestroyObjectManager(jsdc);
188     jsd_DestroyAtomTable(jsdc);
189
190     jsdc->inited = JS_FALSE;
191
192     /*
193     * We should free jsdc here, but we let it leak in case there are any 
194     * asynchronous hooks calling into the system using it as a handle
195     *
196     * XXX we also leak the locks
197     */
198     JS_DestroyContext(jsdc->dumbContext);
199     jsdc->dumbContext = NULL;
200 }
201
202 /***************************************************************************/
203
204 JSDContext*
205 jsd_DebuggerOnForUser(JSRuntime*         jsrt, 
206                       JSD_UserCallbacks* callbacks, 
207                       void*              user,
208                       JSObject*          scopeobj)
209 {
210     JSDContext* jsdc;
211     JSContext* iter = NULL;
212
213     jsdc = _newJSDContext(jsrt, callbacks, user, scopeobj);
214     if( ! jsdc )
215         return NULL;
216
217     /*
218      * Set hooks here.  The new/destroy script hooks are on even when
219      * the debugger is paused.  The destroy hook so we'll clean up
220      * internal data structures when scripts are destroyed, and the
221      * newscript hook for backwards compatibility for now.  We'd like
222      * to stop doing that.
223      */
224     JS_SetNewScriptHookProc(jsdc->jsrt, jsd_NewScriptHookProc, jsdc);
225     JS_SetDestroyScriptHookProc(jsdc->jsrt, jsd_DestroyScriptHookProc, jsdc);
226     jsd_DebuggerUnpause(jsdc);
227 #ifdef LIVEWIRE
228     LWDBG_SetNewScriptHookProc(jsd_NewScriptHookProc, jsdc);
229 #endif
230     if( jsdc->userCallbacks.setContext )
231         jsdc->userCallbacks.setContext(jsdc, jsdc->user);
232     return jsdc;
233 }
234
235 JSDContext*
236 jsd_DebuggerOn(void)
237 {
238     JS_ASSERT(_jsrt);
239     JS_ASSERT(_validateUserCallbacks(&_callbacks));
240     return jsd_DebuggerOnForUser(_jsrt, &_callbacks, _user, NULL);
241 }
242
243 void
244 jsd_DebuggerOff(JSDContext* jsdc)
245 {
246     jsd_DebuggerPause(jsdc, JS_TRUE);
247     /* clear hooks here */
248     JS_SetNewScriptHookProc(jsdc->jsrt, NULL, NULL);
249     JS_SetDestroyScriptHookProc(jsdc->jsrt, NULL, NULL);
250 #ifdef LIVEWIRE
251     LWDBG_SetNewScriptHookProc(NULL,NULL);
252 #endif
253
254     /* clean up */
255     JSD_LockScriptSubsystem(jsdc);
256     jsd_DestroyScriptManager(jsdc);
257     JSD_UnlockScriptSubsystem(jsdc);
258     jsd_DestroyAllSources(jsdc);
259     
260     _destroyJSDContext(jsdc);
261
262     if( jsdc->userCallbacks.setContext )
263         jsdc->userCallbacks.setContext(NULL, jsdc->user);
264 }
265
266 void
267 jsd_DebuggerPause(JSDContext* jsdc, JSBool forceAllHooksOff)
268 {
269     JS_SetDebuggerHandler(jsdc->jsrt, NULL, NULL);
270     if (forceAllHooksOff || !(jsdc->flags & JSD_COLLECT_PROFILE_DATA)) {
271         JS_SetExecuteHook(jsdc->jsrt, NULL, NULL);
272         JS_SetCallHook(jsdc->jsrt, NULL, NULL);
273     }
274     JS_SetThrowHook(jsdc->jsrt, NULL, NULL);
275     JS_SetDebugErrorHook(jsdc->jsrt, NULL, NULL);
276 }
277
278 void
279 jsd_DebuggerUnpause(JSDContext* jsdc)
280 {
281     JS_SetDebuggerHandler(jsdc->jsrt, jsd_DebuggerHandler, jsdc);
282     JS_SetExecuteHook(jsdc->jsrt, jsd_TopLevelCallHook, jsdc);
283     JS_SetCallHook(jsdc->jsrt, jsd_FunctionCallHook, jsdc);
284     JS_SetThrowHook(jsdc->jsrt, jsd_ThrowHandler, jsdc);
285     JS_SetDebugErrorHook(jsdc->jsrt, jsd_DebugErrorHook, jsdc);
286 }
287
288 void
289 jsd_SetUserCallbacks(JSRuntime* jsrt, JSD_UserCallbacks* callbacks, void* user)
290 {
291     _jsrt = jsrt;
292     _user = user;
293
294 #ifdef JSD_HAS_DANGEROUS_THREAD
295     _dangerousThread = JSD_CURRENT_THREAD();
296 #endif
297
298     if( callbacks )
299         memcpy(&_callbacks, callbacks, sizeof(JSD_UserCallbacks));
300     else
301         memset(&_callbacks, 0 , sizeof(JSD_UserCallbacks));
302 }
303
304 void*
305 jsd_SetContextPrivate(JSDContext* jsdc, void *data)
306 {
307     void *rval = jsdc->data;
308     jsdc->data = data;
309     return data;
310 }
311
312 void*
313 jsd_GetContextPrivate(JSDContext* jsdc)
314 {
315     return jsdc->data;
316 }
317
318 void
319 jsd_ClearAllProfileData(JSDContext* jsdc)
320 {
321     JSDScript *current;
322     
323     JSD_LOCK_SCRIPTS(jsdc);
324     current = (JSDScript *)jsdc->scripts.next;
325     while (current != (JSDScript *)&jsdc->scripts)
326     {
327         jsd_ClearScriptProfileData(jsdc, current);
328         current = (JSDScript *)current->links.next;
329     }
330
331     JSD_UNLOCK_SCRIPTS(jsdc);
332 }
333
334 JSDContext*
335 jsd_JSDContextForJSContext(JSContext* context)
336 {
337     JSDContext* iter;
338     JSDContext* jsdc = NULL;
339     JSRuntime*  runtime = JS_GetRuntime(context);
340
341     JSD_LOCK();
342     for( iter = (JSDContext*)_jsd_context_list.next;
343          iter != (JSDContext*)&_jsd_context_list;
344          iter = (JSDContext*)iter->links.next )
345     {
346         if( runtime == iter->jsrt )
347         {
348             jsdc = iter;
349             break;
350         }
351     }
352     JSD_UNLOCK();
353     return jsdc;
354 }    
355
356 static JSBool
357 jsd_DebugErrorHook(JSContext *cx, const char *message,
358                    JSErrorReport *report, void *closure)
359 {
360     JSDContext* jsdc = (JSDContext*) closure;
361     JSD_ErrorReporter errorReporter;
362     void*             errorReporterData;
363     
364     if( ! jsdc )
365     {
366         JS_ASSERT(0);
367         return JS_TRUE;
368     }
369     if( JSD_IS_DANGEROUS_THREAD(jsdc) )
370         return JS_TRUE;
371
372     /* local in case hook gets cleared on another thread */
373     JSD_LOCK();
374     errorReporter     = jsdc->errorReporter;
375     errorReporterData = jsdc->errorReporterData;
376     JSD_UNLOCK();
377
378     if(!errorReporter)
379         return JS_TRUE;
380
381     switch(errorReporter(jsdc, cx, message, report, errorReporterData))
382     {
383         case JSD_ERROR_REPORTER_PASS_ALONG:
384             return JS_TRUE;
385         case JSD_ERROR_REPORTER_RETURN:
386             return JS_FALSE;
387         case JSD_ERROR_REPORTER_DEBUG:
388         {
389             jsval rval;
390             JSD_ExecutionHookProc   hook;
391             void*                   hookData;
392
393             /* local in case hook gets cleared on another thread */
394             JSD_LOCK();
395             hook = jsdc->debugBreakHook;
396             hookData = jsdc->debugBreakHookData;
397             JSD_UNLOCK();
398
399             jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_DEBUG_REQUESTED,
400                                   hook, hookData, &rval);
401             /* XXX Should make this dependent on ExecutionHook retval */
402             return JS_TRUE;
403         }
404         case JSD_ERROR_REPORTER_CLEAR_RETURN:
405             if(report && JSREPORT_IS_EXCEPTION(report->flags))
406                 JS_ClearPendingException(cx);
407             return JS_FALSE;
408         default:
409             JS_ASSERT(0);
410             break;
411     }
412     return JS_TRUE;
413 }
414
415 JSBool
416 jsd_SetErrorReporter(JSDContext*       jsdc, 
417                      JSD_ErrorReporter reporter, 
418                      void*             callerdata)
419 {
420     JSD_LOCK();
421     jsdc->errorReporter = reporter;
422     jsdc->errorReporterData = callerdata;
423     JSD_UNLOCK();
424     return JS_TRUE;
425 }
426
427 JSBool
428 jsd_GetErrorReporter(JSDContext*        jsdc, 
429                      JSD_ErrorReporter* reporter, 
430                      void**             callerdata)
431 {
432     JSD_LOCK();
433     if( reporter )
434         *reporter = jsdc->errorReporter;
435     if( callerdata )
436         *callerdata = jsdc->errorReporterData;
437     JSD_UNLOCK();
438     return JS_TRUE;
439 }