Back to index

lightning-sunbird  0.9+nobinonly
jsd_high.c
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 /*
00039  * JavaScript Debugging support - 'High Level' functions
00040  */
00041 
00042 #include "jsd.h"
00043 
00044 /***************************************************************************/
00045 
00046 /* XXX not 'static' because of old Mac CodeWarrior bug */ 
00047 JSCList _jsd_context_list = JS_INIT_STATIC_CLIST(&_jsd_context_list);
00048 
00049 /* these are used to connect JSD_SetUserCallbacks() with JSD_DebuggerOn() */
00050 static JSD_UserCallbacks _callbacks;
00051 static void*             _user = NULL; 
00052 static JSRuntime*        _jsrt = NULL;
00053 
00054 #ifdef JSD_HAS_DANGEROUS_THREAD
00055 static void* _dangerousThread = NULL;
00056 #endif
00057 
00058 #ifdef JSD_THREADSAFE
00059 void* _jsd_global_lock = NULL;
00060 #endif
00061 
00062 #ifdef DEBUG
00063 void JSD_ASSERT_VALID_CONTEXT(JSDContext* jsdc)
00064 {
00065     JS_ASSERT(jsdc->inited);
00066     JS_ASSERT(jsdc->jsrt);
00067     JS_ASSERT(jsdc->dumbContext);
00068     JS_ASSERT(jsdc->glob);
00069 }
00070 #endif
00071 
00072 static JSClass global_class = {
00073     "JSDGlobal", 0,
00074     JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
00075     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub,
00076     JSCLASS_NO_OPTIONAL_MEMBERS
00077 };
00078 
00079 static JSBool
00080 _validateUserCallbacks(JSD_UserCallbacks* callbacks)
00081 {
00082     return !callbacks ||
00083            (callbacks->size && callbacks->size <= sizeof(JSD_UserCallbacks));
00084 }    
00085 
00086 static JSDContext*
00087 _newJSDContext(JSRuntime*         jsrt, 
00088                JSD_UserCallbacks* callbacks, 
00089                void*              user)
00090 {
00091     JSDContext* jsdc = NULL;
00092 
00093     if( ! jsrt )
00094         return NULL;
00095 
00096     if( ! _validateUserCallbacks(callbacks) )
00097         return NULL;
00098 
00099     jsdc = (JSDContext*) calloc(1, sizeof(JSDContext));
00100     if( ! jsdc )
00101         goto label_newJSDContext_failure;
00102 
00103     if( ! JSD_INIT_LOCKS(jsdc) )
00104         goto label_newJSDContext_failure;
00105 
00106     JS_INIT_CLIST(&jsdc->links);
00107 
00108     jsdc->jsrt = jsrt;
00109 
00110     if( callbacks )
00111         memcpy(&jsdc->userCallbacks, callbacks, callbacks->size);
00112     
00113     jsdc->user = user;
00114 
00115 #ifdef JSD_HAS_DANGEROUS_THREAD
00116     jsdc->dangerousThread = _dangerousThread;
00117 #endif
00118 
00119     JS_INIT_CLIST(&jsdc->threadsStates);
00120     JS_INIT_CLIST(&jsdc->sources);
00121     JS_INIT_CLIST(&jsdc->removedSources);
00122 
00123     jsdc->sourceAlterCount = 1;
00124 
00125     if( ! jsd_CreateAtomTable(jsdc) )
00126         goto label_newJSDContext_failure;
00127 
00128     if( ! jsd_InitObjectManager(jsdc) )
00129         goto label_newJSDContext_failure;
00130 
00131     if( ! jsd_InitScriptManager(jsdc) )
00132         goto label_newJSDContext_failure;
00133 
00134     jsdc->dumbContext = JS_NewContext(jsdc->jsrt, 256);
00135     if( ! jsdc->dumbContext )
00136         goto label_newJSDContext_failure;
00137 
00138     jsdc->glob = JS_NewObject(jsdc->dumbContext, &global_class, NULL, NULL);
00139     if( ! jsdc->glob )
00140         goto label_newJSDContext_failure;
00141 
00142     if( ! JS_InitStandardClasses(jsdc->dumbContext, jsdc->glob) )
00143         goto label_newJSDContext_failure;
00144 
00145     jsdc->data = NULL;
00146     jsdc->inited = JS_TRUE;
00147 
00148     JSD_LOCK();
00149     JS_INSERT_LINK(&jsdc->links, &_jsd_context_list);
00150     JSD_UNLOCK();
00151 
00152     return jsdc;
00153 
00154 label_newJSDContext_failure:
00155     jsd_DestroyObjectManager(jsdc);
00156     jsd_DestroyAtomTable(jsdc);
00157     if( jsdc )
00158         free(jsdc);
00159     return NULL;
00160 }
00161 
00162 static void
00163 _destroyJSDContext(JSDContext* jsdc)
00164 {
00165     JSD_ASSERT_VALID_CONTEXT(jsdc);
00166 
00167     JSD_LOCK();
00168     JS_REMOVE_LINK(&jsdc->links);
00169     JSD_UNLOCK();
00170 
00171     jsd_DestroyObjectManager(jsdc);
00172     jsd_DestroyAtomTable(jsdc);
00173 
00174     jsdc->inited = JS_FALSE;
00175 
00176     /*
00177     * We should free jsdc here, but we let it leak in case there are any 
00178     * asynchronous hooks calling into the system using it as a handle
00179     *
00180     * XXX we also leak the locks
00181     */
00182     JS_DestroyContext(jsdc->dumbContext);
00183     jsdc->dumbContext = NULL;
00184 }
00185 
00186 /***************************************************************************/
00187 
00188 JSDContext*
00189 jsd_DebuggerOnForUser(JSRuntime*         jsrt, 
00190                       JSD_UserCallbacks* callbacks, 
00191                       void*              user)
00192 {
00193     JSDContext* jsdc;
00194     JSContext* iter = NULL;
00195 
00196     jsdc = _newJSDContext(jsrt, callbacks, user);
00197     if( ! jsdc )
00198         return NULL;
00199 
00200     /* set hooks here */
00201     JS_SetNewScriptHookProc(jsdc->jsrt, jsd_NewScriptHookProc, jsdc);
00202     JS_SetDestroyScriptHookProc(jsdc->jsrt, jsd_DestroyScriptHookProc, jsdc);
00203     JS_SetDebuggerHandler(jsdc->jsrt, jsd_DebuggerHandler, jsdc);
00204     JS_SetExecuteHook(jsdc->jsrt, jsd_TopLevelCallHook, jsdc);
00205     JS_SetCallHook(jsdc->jsrt, jsd_FunctionCallHook, jsdc);
00206     JS_SetObjectHook(jsdc->jsrt, jsd_ObjectHook, jsdc);
00207     JS_SetThrowHook(jsdc->jsrt, jsd_ThrowHandler, jsdc);
00208     JS_SetDebugErrorHook(jsdc->jsrt, jsd_DebugErrorHook, jsdc);
00209 #ifdef LIVEWIRE
00210     LWDBG_SetNewScriptHookProc(jsd_NewScriptHookProc, jsdc);
00211 #endif
00212     if( jsdc->userCallbacks.setContext )
00213         jsdc->userCallbacks.setContext(jsdc, jsdc->user);
00214     return jsdc;
00215 }
00216 
00217 JSDContext*
00218 jsd_DebuggerOn(void)
00219 {
00220     JS_ASSERT(_jsrt);
00221     JS_ASSERT(_validateUserCallbacks(&_callbacks));
00222     return jsd_DebuggerOnForUser(_jsrt, &_callbacks, _user);
00223 }
00224 
00225 void
00226 jsd_DebuggerOff(JSDContext* jsdc)
00227 {
00228     /* clear hooks here */
00229     JS_SetNewScriptHookProc(jsdc->jsrt, NULL, NULL);
00230     JS_SetDestroyScriptHookProc(jsdc->jsrt, NULL, NULL);
00231     JS_SetDebuggerHandler(jsdc->jsrt, NULL, NULL);
00232     JS_SetExecuteHook(jsdc->jsrt, NULL, NULL);
00233     JS_SetCallHook(jsdc->jsrt, NULL, NULL);
00234     JS_SetObjectHook(jsdc->jsrt, NULL, NULL);
00235     JS_SetThrowHook(jsdc->jsrt, NULL, NULL);
00236     JS_SetDebugErrorHook(jsdc->jsrt, NULL, NULL);
00237 #ifdef LIVEWIRE
00238     LWDBG_SetNewScriptHookProc(NULL,NULL);
00239 #endif
00240 
00241     /* clean up */
00242     JSD_LockScriptSubsystem(jsdc);
00243     jsd_DestroyScriptManager(jsdc);
00244     JSD_UnlockScriptSubsystem(jsdc);
00245     jsd_DestroyAllSources(jsdc);
00246     
00247     _destroyJSDContext(jsdc);
00248 
00249     if( jsdc->userCallbacks.setContext )
00250         jsdc->userCallbacks.setContext(NULL, jsdc->user);
00251 }
00252 
00253 void
00254 jsd_SetUserCallbacks(JSRuntime* jsrt, JSD_UserCallbacks* callbacks, void* user)
00255 {
00256     _jsrt = jsrt;
00257     _user = user;
00258 
00259 #ifdef JSD_HAS_DANGEROUS_THREAD
00260     _dangerousThread = JSD_CURRENT_THREAD();
00261 #endif
00262 
00263     if( callbacks )
00264         memcpy(&_callbacks, callbacks, sizeof(JSD_UserCallbacks));
00265     else
00266         memset(&_callbacks, 0 , sizeof(JSD_UserCallbacks));
00267 }
00268 
00269 void*
00270 jsd_SetContextPrivate(JSDContext* jsdc, void *data)
00271 {
00272     void *rval = jsdc->data;
00273     jsdc->data = data;
00274     return data;
00275 }
00276 
00277 void*
00278 jsd_GetContextPrivate(JSDContext* jsdc)
00279 {
00280     return jsdc->data;
00281 }
00282 
00283 void
00284 jsd_ClearAllProfileData(JSDContext* jsdc)
00285 {
00286     JSDScript *current;
00287     
00288     JSD_LOCK_SCRIPTS(jsdc);
00289     current = (JSDScript *)jsdc->scripts.next;
00290     while (current != (JSDScript *)&jsdc->scripts)
00291     {
00292         jsd_ClearScriptProfileData(jsdc, current);
00293         current = (JSDScript *)current->links.next;
00294     }
00295 
00296     JSD_UNLOCK_SCRIPTS(jsdc);
00297 }
00298 
00299 JSDContext*
00300 jsd_JSDContextForJSContext(JSContext* context)
00301 {
00302     JSDContext* iter;
00303     JSDContext* jsdc = NULL;
00304     JSRuntime*  runtime = JS_GetRuntime(context);
00305 
00306     JSD_LOCK();
00307     for( iter = (JSDContext*)_jsd_context_list.next;
00308          iter != (JSDContext*)&_jsd_context_list;
00309          iter = (JSDContext*)iter->links.next )
00310     {
00311         if( runtime == iter->jsrt )
00312         {
00313             jsdc = iter;
00314             break;
00315         }
00316     }
00317     JSD_UNLOCK();
00318     return jsdc;
00319 }    
00320 
00321 JS_STATIC_DLL_CALLBACK(JSBool)
00322 jsd_DebugErrorHook(JSContext *cx, const char *message,
00323                    JSErrorReport *report, void *closure)
00324 {
00325     JSDContext* jsdc = (JSDContext*) closure;
00326     JSD_ErrorReporter errorReporter;
00327     void*             errorReporterData;
00328     
00329     if( ! jsdc )
00330     {
00331         JS_ASSERT(0);
00332         return JS_TRUE;
00333     }
00334     if( JSD_IS_DANGEROUS_THREAD(jsdc) )
00335         return JS_TRUE;
00336 
00337     /* local in case hook gets cleared on another thread */
00338     JSD_LOCK();
00339     errorReporter     = jsdc->errorReporter;
00340     errorReporterData = jsdc->errorReporterData;
00341     JSD_UNLOCK();
00342 
00343     if(!errorReporter)
00344         return JS_TRUE;
00345 
00346     switch(errorReporter(jsdc, cx, message, report, errorReporterData))
00347     {
00348         case JSD_ERROR_REPORTER_PASS_ALONG:
00349             return JS_TRUE;
00350         case JSD_ERROR_REPORTER_RETURN:
00351             return JS_FALSE;
00352         case JSD_ERROR_REPORTER_DEBUG:
00353         {
00354             jsval rval;
00355             JSD_ExecutionHookProc   hook;
00356             void*                   hookData;
00357 
00358             /* local in case hook gets cleared on another thread */
00359             JSD_LOCK();
00360             hook = jsdc->debugBreakHook;
00361             hookData = jsdc->debugBreakHookData;
00362             JSD_UNLOCK();
00363 
00364             jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_DEBUG_REQUESTED,
00365                                   hook, hookData, &rval);
00366             /* XXX Should make this dependent on ExecutionHook retval */
00367             return JS_TRUE;
00368         }
00369         case JSD_ERROR_REPORTER_CLEAR_RETURN:
00370             if(report && JSREPORT_IS_EXCEPTION(report->flags))
00371                 JS_ClearPendingException(cx);
00372             return JS_FALSE;
00373         default:
00374             JS_ASSERT(0);
00375             break;
00376     }
00377     return JS_TRUE;
00378 }
00379 
00380 JSBool
00381 jsd_SetErrorReporter(JSDContext*       jsdc, 
00382                      JSD_ErrorReporter reporter, 
00383                      void*             callerdata)
00384 {
00385     JSD_LOCK();
00386     jsdc->errorReporter = reporter;
00387     jsdc->errorReporterData = callerdata;
00388     JSD_UNLOCK();
00389     return JS_TRUE;
00390 }
00391 
00392 JSBool
00393 jsd_GetErrorReporter(JSDContext*        jsdc, 
00394                      JSD_ErrorReporter* reporter, 
00395                      void**             callerdata)
00396 {
00397     JSD_LOCK();
00398     if( reporter )
00399         *reporter = jsdc->errorReporter;
00400     if( callerdata )
00401         *callerdata = jsdc->errorReporterData;
00402     JSD_UNLOCK();
00403     return JS_TRUE;
00404 }