Back to index

lightning-sunbird  0.9+nobinonly
jsd_stak.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 - Call stack support
00040  */
00041 
00042 #include "jsd.h"
00043 
00044 #ifdef DEBUG
00045 void JSD_ASSERT_VALID_THREAD_STATE(JSDThreadState* jsdthreadstate)
00046 {
00047     JS_ASSERT(jsdthreadstate);
00048     JS_ASSERT(jsdthreadstate->stackDepth > 0);
00049 }
00050 
00051 void JSD_ASSERT_VALID_STACK_FRAME(JSDStackFrameInfo* jsdframe)
00052 {
00053     JS_ASSERT(jsdframe);
00054     JS_ASSERT(jsdframe->jsdthreadstate);
00055 }
00056 #endif
00057 
00058 static JSDStackFrameInfo* 
00059 _addNewFrame(JSDContext*        jsdc,
00060              JSDThreadState*    jsdthreadstate,
00061              JSScript*          script,
00062              jsuword            pc,
00063              JSStackFrame*      fp)
00064 {
00065     JSDStackFrameInfo* jsdframe;
00066     JSDScript*         jsdscript = NULL;
00067 
00068     if (!JS_IsNativeFrame(jsdthreadstate->context, fp))
00069     {
00070         JSD_LOCK_SCRIPTS(jsdc);
00071         jsdscript = jsd_FindJSDScript(jsdc, script);
00072         JSD_UNLOCK_SCRIPTS(jsdc);
00073         if (!jsdscript || (jsdc->flags & JSD_HIDE_DISABLED_FRAMES &&
00074                            !JSD_IS_DEBUG_ENABLED(jsdc, jsdscript)))
00075         {
00076             return NULL;
00077         }
00078 
00079         if (!JSD_IS_DEBUG_ENABLED(jsdc, jsdscript))
00080             jsdthreadstate->flags |= TS_HAS_DISABLED_FRAME;
00081     }
00082     
00083     jsdframe = (JSDStackFrameInfo*) calloc(1, sizeof(JSDStackFrameInfo));
00084     if( ! jsdframe )
00085         return NULL;
00086 
00087     jsdframe->jsdthreadstate = jsdthreadstate;
00088     jsdframe->jsdscript      = jsdscript;
00089     jsdframe->pc             = pc;
00090     jsdframe->fp             = fp;
00091 
00092     JS_APPEND_LINK(&jsdframe->links, &jsdthreadstate->stack);
00093     jsdthreadstate->stackDepth++;
00094 
00095     return jsdframe;
00096 }
00097 
00098 static void
00099 _destroyFrame(JSDStackFrameInfo* jsdframe)
00100 {
00101     /* kill any alloc'd objects in frame here... */
00102 
00103     if( jsdframe )
00104         free(jsdframe);
00105 }
00106 
00107 JSDThreadState*
00108 jsd_NewThreadState(JSDContext* jsdc, JSContext *cx )
00109 {
00110     JSDThreadState* jsdthreadstate;
00111     JSStackFrame *  iter = NULL;
00112     JSStackFrame *  fp;
00113 
00114     jsdthreadstate = (JSDThreadState*)calloc(1, sizeof(JSDThreadState));
00115     if( ! jsdthreadstate )
00116         return NULL;
00117 
00118     jsdthreadstate->context = cx;
00119     jsdthreadstate->thread = JSD_CURRENT_THREAD();
00120     JS_INIT_CLIST(&jsdthreadstate->stack);
00121     jsdthreadstate->stackDepth = 0;
00122 
00123     while( NULL != (fp = JS_FrameIterator(cx, &iter)) )
00124     {
00125         JSScript* script = JS_GetFrameScript(cx, fp);
00126         jsuword  pc = (jsuword) JS_GetFramePC(cx, fp);
00127 
00128         /*
00129          * don't construct a JSDStackFrame for dummy frames (those without a
00130          * |this| object, or native frames, if JSD_INCLUDE_NATIVE_FRAMES
00131          * isn't set.
00132          */
00133         if (JS_GetFrameThis(cx, fp) &&
00134             ((jsdc->flags & JSD_INCLUDE_NATIVE_FRAMES) ||
00135              !JS_IsNativeFrame(cx, fp)))
00136         {
00137             JSDStackFrameInfo *frame;
00138 
00139             frame = _addNewFrame( jsdc, jsdthreadstate, script, pc, fp );
00140 
00141             if ((jsdthreadstate->stackDepth == 0 && !frame) ||
00142                 (jsdthreadstate->stackDepth == 1 && frame &&
00143                  frame->jsdscript && !JSD_IS_DEBUG_ENABLED(jsdc, frame->jsdscript)))
00144             {
00145                 /*
00146                  * if we failed to create the first frame, or the top frame
00147                  * is not enabled for debugging, fail the entire thread state.
00148                  */
00149                 JS_INIT_CLIST(&jsdthreadstate->links);
00150                 jsd_DestroyThreadState(jsdc, jsdthreadstate);
00151                 return NULL;
00152             }
00153         }
00154     }
00155 
00156     if (jsdthreadstate->stackDepth == 0)
00157     {
00158         free(jsdthreadstate);
00159         return NULL;
00160     }
00161     
00162     JSD_LOCK_THREADSTATES(jsdc);
00163     JS_APPEND_LINK(&jsdthreadstate->links, &jsdc->threadsStates);
00164     JSD_UNLOCK_THREADSTATES(jsdc);
00165 
00166     return jsdthreadstate;
00167 }
00168 
00169 void
00170 jsd_DestroyThreadState(JSDContext* jsdc, JSDThreadState* jsdthreadstate)
00171 {
00172     JSDStackFrameInfo* jsdframe;
00173     JSCList* list;
00174 
00175     JS_ASSERT(jsdthreadstate);
00176     JS_ASSERT(JSD_CURRENT_THREAD() == jsdthreadstate->thread);
00177 
00178     JSD_LOCK_THREADSTATES(jsdc);
00179     JS_REMOVE_LINK(&jsdthreadstate->links);
00180     JSD_UNLOCK_THREADSTATES(jsdc);
00181 
00182     list = &jsdthreadstate->stack;
00183     while( (JSDStackFrameInfo*)list != (jsdframe = (JSDStackFrameInfo*)list->next) )
00184     {
00185         JS_REMOVE_LINK(&jsdframe->links);
00186         _destroyFrame(jsdframe);
00187     }
00188     free(jsdthreadstate);
00189 }
00190 
00191 uintN
00192 jsd_GetCountOfStackFrames(JSDContext* jsdc, JSDThreadState* jsdthreadstate)
00193 {
00194     uintN count = 0;
00195 
00196     JSD_LOCK_THREADSTATES(jsdc);
00197 
00198     if( jsd_IsValidThreadState(jsdc, jsdthreadstate) )
00199         count = jsdthreadstate->stackDepth;
00200 
00201     JSD_UNLOCK_THREADSTATES(jsdc);
00202 
00203     return count;
00204 }
00205 
00206 JSDStackFrameInfo*
00207 jsd_GetStackFrame(JSDContext* jsdc, JSDThreadState* jsdthreadstate)
00208 {
00209     JSDStackFrameInfo* jsdframe = NULL;
00210 
00211     JSD_LOCK_THREADSTATES(jsdc);
00212 
00213     if( jsd_IsValidThreadState(jsdc, jsdthreadstate) )
00214         jsdframe = (JSDStackFrameInfo*) JS_LIST_HEAD(&jsdthreadstate->stack);
00215     JSD_UNLOCK_THREADSTATES(jsdc);
00216 
00217     return jsdframe;
00218 }
00219 
00220 JSContext *
00221 jsd_GetJSContext (JSDContext* jsdc, JSDThreadState* jsdthreadstate)
00222 {
00223     JSContext* cx = NULL;
00224 
00225     JSD_LOCK_THREADSTATES(jsdc);
00226     if( jsd_IsValidThreadState(jsdc, jsdthreadstate) )
00227         cx = jsdthreadstate->context;
00228     JSD_UNLOCK_THREADSTATES(jsdc);
00229 
00230     return cx;
00231 }
00232     
00233 JSDStackFrameInfo*
00234 jsd_GetCallingStackFrame(JSDContext* jsdc, 
00235                          JSDThreadState* jsdthreadstate,
00236                          JSDStackFrameInfo* jsdframe)
00237 {
00238     JSDStackFrameInfo* nextjsdframe = NULL;
00239 
00240     JSD_LOCK_THREADSTATES(jsdc);
00241 
00242     if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
00243         if( JS_LIST_HEAD(&jsdframe->links) != &jsdframe->jsdthreadstate->stack )
00244             nextjsdframe = (JSDStackFrameInfo*) JS_LIST_HEAD(&jsdframe->links);
00245 
00246     JSD_UNLOCK_THREADSTATES(jsdc);
00247 
00248     return nextjsdframe;
00249 }
00250 
00251 JSDScript*
00252 jsd_GetScriptForStackFrame(JSDContext* jsdc, 
00253                            JSDThreadState* jsdthreadstate,
00254                            JSDStackFrameInfo* jsdframe)
00255 {
00256     JSDScript* jsdscript = NULL;
00257 
00258     JSD_LOCK_THREADSTATES(jsdc);
00259 
00260     if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
00261         jsdscript = jsdframe->jsdscript;
00262 
00263     JSD_UNLOCK_THREADSTATES(jsdc);
00264 
00265     return jsdscript;
00266 }
00267 
00268 jsuword
00269 jsd_GetPCForStackFrame(JSDContext* jsdc, 
00270                        JSDThreadState* jsdthreadstate,
00271                        JSDStackFrameInfo* jsdframe)
00272 {
00273     jsuword pc = 0;
00274 
00275     JSD_LOCK_THREADSTATES(jsdc);
00276 
00277     if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
00278         pc = jsdframe->pc;
00279 
00280     JSD_UNLOCK_THREADSTATES(jsdc);
00281 
00282     return pc;
00283 }
00284 
00285 JSDValue*
00286 jsd_GetCallObjectForStackFrame(JSDContext* jsdc, 
00287                                JSDThreadState* jsdthreadstate,
00288                                JSDStackFrameInfo* jsdframe)
00289 {
00290     JSObject* obj;
00291     JSDValue* jsdval = NULL;
00292 
00293     JSD_LOCK_THREADSTATES(jsdc);
00294 
00295     if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
00296     {
00297         obj = JS_GetFrameCallObject(jsdthreadstate->context, jsdframe->fp); 
00298         if(obj)                                                             
00299             jsdval = JSD_NewValue(jsdc, OBJECT_TO_JSVAL(obj));              
00300     }
00301 
00302     JSD_UNLOCK_THREADSTATES(jsdc);
00303 
00304     return jsdval;
00305 }
00306 
00307 JSDValue*
00308 jsd_GetScopeChainForStackFrame(JSDContext* jsdc, 
00309                                JSDThreadState* jsdthreadstate,
00310                                JSDStackFrameInfo* jsdframe)
00311 {
00312     JSObject* obj;
00313     JSDValue* jsdval = NULL;
00314 
00315     JSD_LOCK_THREADSTATES(jsdc);
00316 
00317     if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
00318     {
00319         obj = JS_GetFrameScopeChain(jsdthreadstate->context, jsdframe->fp); 
00320         if(obj)                                                             
00321             jsdval = JSD_NewValue(jsdc, OBJECT_TO_JSVAL(obj));              
00322     }
00323 
00324     JSD_UNLOCK_THREADSTATES(jsdc);
00325 
00326     return jsdval;
00327 }
00328 
00329 JSDValue*
00330 jsd_GetThisForStackFrame(JSDContext* jsdc, 
00331                          JSDThreadState* jsdthreadstate,
00332                          JSDStackFrameInfo* jsdframe)
00333 {
00334     JSObject* obj;
00335     JSDValue* jsdval = NULL;
00336     JSD_LOCK_THREADSTATES(jsdc);
00337 
00338     if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
00339     {
00340         obj = JS_GetFrameThis(jsdthreadstate->context, jsdframe->fp);
00341         if(obj)
00342             jsdval = JSD_NewValue(jsdc, OBJECT_TO_JSVAL(obj));
00343     }
00344 
00345     JSD_UNLOCK_THREADSTATES(jsdc);
00346     return jsdval;
00347 }
00348 
00349 const char*
00350 jsd_GetNameForStackFrame(JSDContext* jsdc, 
00351                          JSDThreadState* jsdthreadstate,
00352                          JSDStackFrameInfo* jsdframe)
00353 {
00354     const char *rv = NULL;
00355     
00356     JSD_LOCK_THREADSTATES(jsdc);
00357     
00358     if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
00359     {
00360         JSFunction *fun = JS_GetFrameFunction (jsdthreadstate->context,
00361                                                jsdframe->fp);
00362         if (fun)
00363             rv = JS_GetFunctionName (fun);
00364     }
00365     
00366     JSD_UNLOCK_THREADSTATES(jsdc);
00367     return rv;
00368 }
00369 
00370 JSBool
00371 jsd_IsStackFrameNative(JSDContext* jsdc, 
00372                        JSDThreadState* jsdthreadstate,
00373                        JSDStackFrameInfo* jsdframe)
00374 {
00375     JSBool rv;
00376     
00377     JSD_LOCK_THREADSTATES(jsdc);
00378 
00379     if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
00380     {
00381         rv = JS_IsNativeFrame(jsdthreadstate->context, jsdframe->fp);
00382     }
00383     else
00384     {
00385         rv = JS_FALSE;
00386     }
00387 
00388     JSD_UNLOCK_THREADSTATES(jsdc);
00389     return rv;
00390 }
00391 
00392 JSBool
00393 jsd_IsStackFrameDebugger(JSDContext* jsdc, 
00394                          JSDThreadState* jsdthreadstate,
00395                          JSDStackFrameInfo* jsdframe)
00396 {
00397     JSBool rv = JS_TRUE;
00398     JSD_LOCK_THREADSTATES(jsdc);
00399 
00400     if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
00401     {
00402         rv = JS_IsDebuggerFrame(jsdthreadstate->context, jsdframe->fp);
00403     }
00404 
00405     JSD_UNLOCK_THREADSTATES(jsdc);
00406     return rv;
00407 }
00408 
00409 JSBool
00410 jsd_IsStackFrameConstructing(JSDContext* jsdc, 
00411                              JSDThreadState* jsdthreadstate,
00412                              JSDStackFrameInfo* jsdframe)
00413 {
00414     JSBool rv = JS_TRUE;
00415     JSD_LOCK_THREADSTATES(jsdc);
00416 
00417     if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
00418     {
00419         rv = JS_IsConstructorFrame(jsdthreadstate->context, jsdframe->fp);
00420     }
00421 
00422     JSD_UNLOCK_THREADSTATES(jsdc);
00423     return rv;
00424 }
00425 
00426 JSBool
00427 jsd_EvaluateUCScriptInStackFrame(JSDContext* jsdc, 
00428                                  JSDThreadState* jsdthreadstate,
00429                                  JSDStackFrameInfo* jsdframe,
00430                                  const jschar *bytes, uintN length,
00431                                  const char *filename, uintN lineno,
00432                                  JSBool eatExceptions, jsval *rval)
00433 {
00434     JSBool retval;
00435     JSBool valid;
00436     JSExceptionState* exceptionState = NULL;
00437     JSContext* cx;
00438 
00439     JS_ASSERT(JSD_CURRENT_THREAD() == jsdthreadstate->thread);
00440 
00441     JSD_LOCK_THREADSTATES(jsdc);
00442     valid = jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe);
00443     JSD_UNLOCK_THREADSTATES(jsdc);
00444 
00445     if( ! valid )
00446         return JS_FALSE;
00447 
00448     cx = jsdthreadstate->context;
00449     JS_ASSERT(cx);
00450 
00451     if (eatExceptions)
00452         exceptionState = JS_SaveExceptionState(cx);
00453     JS_ClearPendingException(cx);
00454     jsd_StartingEvalUsingFilename(jsdc, filename);
00455     retval = JS_EvaluateUCInStackFrame(cx, jsdframe->fp, bytes, length, 
00456                                        filename, lineno, rval);
00457     jsd_FinishedEvalUsingFilename(jsdc, filename);
00458     if (eatExceptions)
00459         JS_RestoreExceptionState(cx, exceptionState);
00460 
00461     return retval;
00462 }
00463 
00464 JSBool
00465 jsd_EvaluateScriptInStackFrame(JSDContext* jsdc, 
00466                                JSDThreadState* jsdthreadstate,
00467                                JSDStackFrameInfo* jsdframe,
00468                                const char *bytes, uintN length,
00469                                const char *filename, uintN lineno,
00470                                JSBool eatExceptions, jsval *rval)
00471 {
00472     JSBool retval;
00473     JSBool valid;
00474     JSExceptionState* exceptionState = NULL;
00475     JSContext *cx;
00476 
00477     JS_ASSERT(JSD_CURRENT_THREAD() == jsdthreadstate->thread);
00478 
00479     JSD_LOCK_THREADSTATES(jsdc);
00480     valid = jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe);
00481     JSD_UNLOCK_THREADSTATES(jsdc);
00482 
00483     if (!valid)
00484         return JS_FALSE;
00485 
00486     cx = jsdthreadstate->context;
00487     JS_ASSERT(cx);
00488 
00489     if (eatExceptions)
00490         exceptionState = JS_SaveExceptionState(cx);
00491     JS_ClearPendingException(cx);
00492     jsd_StartingEvalUsingFilename(jsdc, filename);
00493     retval = JS_EvaluateInStackFrame(cx, jsdframe->fp, bytes, length,
00494                                      filename, lineno, rval);
00495     jsd_FinishedEvalUsingFilename(jsdc, filename);
00496     if (eatExceptions)
00497         JS_RestoreExceptionState(cx, exceptionState);
00498 
00499     return retval;
00500 }
00501 
00502 JSString*
00503 jsd_ValToStringInStackFrame(JSDContext* jsdc, 
00504                             JSDThreadState* jsdthreadstate,
00505                             JSDStackFrameInfo* jsdframe,
00506                             jsval val)
00507 {
00508     JSBool valid;
00509     JSString* retval;
00510     JSExceptionState* exceptionState;
00511     JSContext* cx;
00512 
00513     JSD_LOCK_THREADSTATES(jsdc);
00514     valid = jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe);
00515     JSD_UNLOCK_THREADSTATES(jsdc);
00516 
00517     if( ! valid )
00518         return NULL;
00519 
00520     cx = jsdthreadstate->context;
00521     JS_ASSERT(cx);
00522 
00523     exceptionState = JS_SaveExceptionState(cx);
00524     retval = JS_ValueToString(cx, val);
00525     JS_RestoreExceptionState(cx, exceptionState);
00526 
00527     return retval;
00528 }
00529 
00530 JSBool
00531 jsd_IsValidThreadState(JSDContext*        jsdc, 
00532                        JSDThreadState*    jsdthreadstate)
00533 {
00534     JSDThreadState *cur;
00535 
00536     JS_ASSERT( JSD_THREADSTATES_LOCKED(jsdc) );
00537 
00538     for( cur = (JSDThreadState*)jsdc->threadsStates.next;
00539          cur != (JSDThreadState*)&jsdc->threadsStates;
00540          cur = (JSDThreadState*)cur->links.next ) 
00541     {
00542         if( cur == jsdthreadstate )
00543             return JS_TRUE;
00544     }
00545     return JS_FALSE;
00546 }    
00547 
00548 JSBool
00549 jsd_IsValidFrameInThreadState(JSDContext*        jsdc, 
00550                               JSDThreadState*    jsdthreadstate,
00551                               JSDStackFrameInfo* jsdframe)
00552 {
00553     JS_ASSERT(JSD_THREADSTATES_LOCKED(jsdc));
00554 
00555     if( ! jsd_IsValidThreadState(jsdc, jsdthreadstate) )
00556         return JS_FALSE;
00557     if( jsdframe->jsdthreadstate != jsdthreadstate )
00558         return JS_FALSE;
00559 
00560     JSD_ASSERT_VALID_THREAD_STATE(jsdthreadstate);
00561     JSD_ASSERT_VALID_STACK_FRAME(jsdframe);
00562     
00563     return JS_TRUE;
00564 }
00565 
00566 static JSContext*
00567 _getContextForThreadState(JSDContext* jsdc, JSDThreadState* jsdthreadstate)
00568 {
00569     JSBool valid;
00570     JSD_LOCK_THREADSTATES(jsdc);
00571     valid = jsd_IsValidThreadState(jsdc, jsdthreadstate);
00572     JSD_UNLOCK_THREADSTATES(jsdc);
00573     if( valid )
00574         return jsdthreadstate->context;
00575     return NULL;
00576 }        
00577 
00578 JSDValue*
00579 jsd_GetException(JSDContext* jsdc, JSDThreadState* jsdthreadstate)
00580 {
00581     JSContext* cx;
00582     jsval val;
00583 
00584     if(!(cx = _getContextForThreadState(jsdc, jsdthreadstate)))
00585         return NULL;
00586 
00587     if(JS_GetPendingException(cx, &val))
00588         return jsd_NewValue(jsdc, val);
00589     return NULL;
00590 }        
00591 
00592 JSBool
00593 jsd_SetException(JSDContext* jsdc, JSDThreadState* jsdthreadstate, 
00594                  JSDValue* jsdval)
00595 {
00596     JSContext* cx;
00597 
00598     if(!(cx = _getContextForThreadState(jsdc, jsdthreadstate)))
00599         return JS_FALSE;
00600 
00601     if(jsdval)
00602         JS_SetPendingException(cx, JSD_GetValueWrappedJSVal(jsdc, jsdval));
00603     else
00604         JS_ClearPendingException(cx);
00605     return JS_TRUE;
00606 }
00607