Imported Upstream version 1.0.0
[platform/upstream/js.git] / js / jsd / jsd_stak.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 - Call stack support
40  */
41
42 #include "jsd.h"
43 #include "jsfriendapi.h"
44
45 #ifdef DEBUG
46 void JSD_ASSERT_VALID_THREAD_STATE(JSDThreadState* jsdthreadstate)
47 {
48     JS_ASSERT(jsdthreadstate);
49     JS_ASSERT(jsdthreadstate->stackDepth > 0);
50 }
51
52 void JSD_ASSERT_VALID_STACK_FRAME(JSDStackFrameInfo* jsdframe)
53 {
54     JS_ASSERT(jsdframe);
55     JS_ASSERT(jsdframe->jsdthreadstate);
56 }
57 #endif
58
59 static JSDStackFrameInfo* 
60 _addNewFrame(JSDContext*        jsdc,
61              JSDThreadState*    jsdthreadstate,
62              JSScript*          script,
63              jsuword            pc,
64              JSStackFrame*      fp)
65 {
66     JSDStackFrameInfo* jsdframe;
67     JSDScript*         jsdscript = NULL;
68
69     if (JS_IsScriptFrame(jsdthreadstate->context, fp))
70     {
71         JSD_LOCK_SCRIPTS(jsdc);
72         jsdscript = jsd_FindJSDScript(jsdc, script);
73         JSD_UNLOCK_SCRIPTS(jsdc);
74         if (!jsdscript || (jsdc->flags & JSD_HIDE_DISABLED_FRAMES &&
75                            !JSD_IS_DEBUG_ENABLED(jsdc, jsdscript)))
76         {
77             return NULL;
78         }
79
80         if (!JSD_IS_DEBUG_ENABLED(jsdc, jsdscript))
81             jsdthreadstate->flags |= TS_HAS_DISABLED_FRAME;
82     }
83     
84     jsdframe = (JSDStackFrameInfo*) calloc(1, sizeof(JSDStackFrameInfo));
85     if( ! jsdframe )
86         return NULL;
87
88     jsdframe->jsdthreadstate = jsdthreadstate;
89     jsdframe->jsdscript      = jsdscript;
90     jsdframe->pc             = pc;
91     jsdframe->fp             = fp;
92
93     JS_APPEND_LINK(&jsdframe->links, &jsdthreadstate->stack);
94     jsdthreadstate->stackDepth++;
95
96     return jsdframe;
97 }
98
99 static void
100 _destroyFrame(JSDStackFrameInfo* jsdframe)
101 {
102     /* kill any alloc'd objects in frame here... */
103
104     if( jsdframe )
105         free(jsdframe);
106 }
107
108 JSDThreadState*
109 jsd_NewThreadState(JSDContext* jsdc, JSContext *cx )
110 {
111     JSDThreadState* jsdthreadstate;
112     JSStackFrame *  iter = NULL;
113     JSStackFrame *  fp;
114
115     jsdthreadstate = (JSDThreadState*)calloc(1, sizeof(JSDThreadState));
116     if( ! jsdthreadstate )
117         return NULL;
118
119     jsdthreadstate->context = cx;
120     jsdthreadstate->thread = JSD_CURRENT_THREAD();
121     JS_INIT_CLIST(&jsdthreadstate->stack);
122     jsdthreadstate->stackDepth = 0;
123
124     JS_BeginRequest(jsdthreadstate->context);
125     while( NULL != (fp = JS_FrameIterator(cx, &iter)) )
126     {
127         JSScript* script = JS_GetFrameScript(cx, fp);
128         jsuword  pc = (jsuword) JS_GetFramePC(cx, fp);
129         jsval dummyThis;
130
131         /*
132          * don't construct a JSDStackFrame for dummy frames (those without a
133          * |this| object, or native frames, if JSD_INCLUDE_NATIVE_FRAMES
134          * isn't set.
135          */
136         if (JS_GetFrameThis(cx, fp, &dummyThis) &&
137             ((jsdc->flags & JSD_INCLUDE_NATIVE_FRAMES) ||
138              JS_IsScriptFrame(cx, fp)))
139         {
140             JSDStackFrameInfo *frame;
141
142             frame = _addNewFrame( jsdc, jsdthreadstate, script, pc, fp );
143
144             if ((jsdthreadstate->stackDepth == 0 && !frame) ||
145                 (jsdthreadstate->stackDepth == 1 && frame &&
146                  frame->jsdscript && !JSD_IS_DEBUG_ENABLED(jsdc, frame->jsdscript)))
147             {
148                 /*
149                  * if we failed to create the first frame, or the top frame
150                  * is not enabled for debugging, fail the entire thread state.
151                  */
152                 JS_INIT_CLIST(&jsdthreadstate->links);
153                 JS_EndRequest(jsdthreadstate->context);
154                 jsd_DestroyThreadState(jsdc, jsdthreadstate);
155                 return NULL;
156             }
157         }
158     }
159     JS_EndRequest(jsdthreadstate->context);
160
161     if (jsdthreadstate->stackDepth == 0)
162     {
163         free(jsdthreadstate);
164         return NULL;
165     }
166     
167     JSD_LOCK_THREADSTATES(jsdc);
168     JS_APPEND_LINK(&jsdthreadstate->links, &jsdc->threadsStates);
169     JSD_UNLOCK_THREADSTATES(jsdc);
170
171     return jsdthreadstate;
172 }
173
174 void
175 jsd_DestroyThreadState(JSDContext* jsdc, JSDThreadState* jsdthreadstate)
176 {
177     JSDStackFrameInfo* jsdframe;
178     JSCList* list;
179
180     JS_ASSERT(jsdthreadstate);
181     JS_ASSERT(JSD_CURRENT_THREAD() == jsdthreadstate->thread);
182
183     JSD_LOCK_THREADSTATES(jsdc);
184     JS_REMOVE_LINK(&jsdthreadstate->links);
185     JSD_UNLOCK_THREADSTATES(jsdc);
186
187     list = &jsdthreadstate->stack;
188     while( (JSDStackFrameInfo*)list != (jsdframe = (JSDStackFrameInfo*)list->next) )
189     {
190         JS_REMOVE_LINK(&jsdframe->links);
191         _destroyFrame(jsdframe);
192     }
193     free(jsdthreadstate);
194 }
195
196 uintN
197 jsd_GetCountOfStackFrames(JSDContext* jsdc, JSDThreadState* jsdthreadstate)
198 {
199     uintN count = 0;
200
201     JSD_LOCK_THREADSTATES(jsdc);
202
203     if( jsd_IsValidThreadState(jsdc, jsdthreadstate) )
204         count = jsdthreadstate->stackDepth;
205
206     JSD_UNLOCK_THREADSTATES(jsdc);
207
208     return count;
209 }
210
211 JSDStackFrameInfo*
212 jsd_GetStackFrame(JSDContext* jsdc, JSDThreadState* jsdthreadstate)
213 {
214     JSDStackFrameInfo* jsdframe = NULL;
215
216     JSD_LOCK_THREADSTATES(jsdc);
217
218     if( jsd_IsValidThreadState(jsdc, jsdthreadstate) )
219         jsdframe = (JSDStackFrameInfo*) JS_LIST_HEAD(&jsdthreadstate->stack);
220     JSD_UNLOCK_THREADSTATES(jsdc);
221
222     return jsdframe;
223 }
224
225 JSContext *
226 jsd_GetJSContext (JSDContext* jsdc, JSDThreadState* jsdthreadstate)
227 {
228     JSContext* cx = NULL;
229
230     JSD_LOCK_THREADSTATES(jsdc);
231     if( jsd_IsValidThreadState(jsdc, jsdthreadstate) )
232         cx = jsdthreadstate->context;
233     JSD_UNLOCK_THREADSTATES(jsdc);
234
235     return cx;
236 }
237     
238 JSDStackFrameInfo*
239 jsd_GetCallingStackFrame(JSDContext* jsdc, 
240                          JSDThreadState* jsdthreadstate,
241                          JSDStackFrameInfo* jsdframe)
242 {
243     JSDStackFrameInfo* nextjsdframe = NULL;
244
245     JSD_LOCK_THREADSTATES(jsdc);
246
247     if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
248         if( JS_LIST_HEAD(&jsdframe->links) != &jsdframe->jsdthreadstate->stack )
249             nextjsdframe = (JSDStackFrameInfo*) JS_LIST_HEAD(&jsdframe->links);
250
251     JSD_UNLOCK_THREADSTATES(jsdc);
252
253     return nextjsdframe;
254 }
255
256 JSDScript*
257 jsd_GetScriptForStackFrame(JSDContext* jsdc, 
258                            JSDThreadState* jsdthreadstate,
259                            JSDStackFrameInfo* jsdframe)
260 {
261     JSDScript* jsdscript = NULL;
262
263     JSD_LOCK_THREADSTATES(jsdc);
264
265     if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
266         jsdscript = jsdframe->jsdscript;
267
268     JSD_UNLOCK_THREADSTATES(jsdc);
269
270     return jsdscript;
271 }
272
273 jsuword
274 jsd_GetPCForStackFrame(JSDContext* jsdc, 
275                        JSDThreadState* jsdthreadstate,
276                        JSDStackFrameInfo* jsdframe)
277 {
278     jsuword pc = 0;
279
280     JSD_LOCK_THREADSTATES(jsdc);
281
282     if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
283         pc = jsdframe->pc;
284
285     JSD_UNLOCK_THREADSTATES(jsdc);
286
287     return pc;
288 }
289
290 JSDValue*
291 jsd_GetCallObjectForStackFrame(JSDContext* jsdc, 
292                                JSDThreadState* jsdthreadstate,
293                                JSDStackFrameInfo* jsdframe)
294 {
295     JSObject* obj;
296     JSDValue* jsdval = NULL;
297
298     JSD_LOCK_THREADSTATES(jsdc);
299
300     if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
301     {
302         obj = JS_GetFrameCallObject(jsdthreadstate->context, jsdframe->fp); 
303         if(obj)                                                             
304             jsdval = JSD_NewValue(jsdc, OBJECT_TO_JSVAL(obj));              
305     }
306
307     JSD_UNLOCK_THREADSTATES(jsdc);
308
309     return jsdval;
310 }
311
312 JSDValue*
313 jsd_GetScopeChainForStackFrame(JSDContext* jsdc, 
314                                JSDThreadState* jsdthreadstate,
315                                JSDStackFrameInfo* jsdframe)
316 {
317     JSObject* obj;
318     JSDValue* jsdval = NULL;
319
320     JSD_LOCK_THREADSTATES(jsdc);
321
322     if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
323     {
324         JS_BeginRequest(jsdthreadstate->context);
325         obj = JS_GetFrameScopeChain(jsdthreadstate->context, jsdframe->fp); 
326         JS_EndRequest(jsdthreadstate->context);
327         if(obj)                                                             
328             jsdval = JSD_NewValue(jsdc, OBJECT_TO_JSVAL(obj));              
329     }
330
331     JSD_UNLOCK_THREADSTATES(jsdc);
332
333     return jsdval;
334 }
335
336 JSDValue*
337 jsd_GetThisForStackFrame(JSDContext* jsdc, 
338                          JSDThreadState* jsdthreadstate,
339                          JSDStackFrameInfo* jsdframe)
340 {
341     JSObject* obj;
342     JSDValue* jsdval = NULL;
343     JSD_LOCK_THREADSTATES(jsdc);
344
345     if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
346     {
347         JSBool ok;
348         jsval thisval;
349         JS_BeginRequest(jsdthreadstate->context);
350         ok = JS_GetFrameThis(jsdthreadstate->context, jsdframe->fp, &thisval);
351         JS_EndRequest(jsdthreadstate->context);
352         if(ok)
353             jsdval = JSD_NewValue(jsdc, thisval);
354     }
355
356     JSD_UNLOCK_THREADSTATES(jsdc);
357     return jsdval;
358 }
359
360 JSString*
361 jsd_GetIdForStackFrame(JSDContext* jsdc, 
362                        JSDThreadState* jsdthreadstate,
363                        JSDStackFrameInfo* jsdframe)
364 {
365     JSString *rv = NULL;
366     
367     JSD_LOCK_THREADSTATES(jsdc);
368     
369     if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
370     {
371         JSFunction *fun = JS_GetFrameFunction (jsdthreadstate->context,
372                                                jsdframe->fp);
373         if( fun )
374         {
375             rv = JS_GetFunctionId (fun);
376
377             /*
378              * For compatibility we return "anonymous", not an empty string
379              * here.
380              */
381             if( !rv )
382                 rv = JS_GetAnonymousString(jsdc->jsrt);
383         }
384     }
385     
386     JSD_UNLOCK_THREADSTATES(jsdc);
387     return rv;
388 }
389
390 JSBool
391 jsd_IsStackFrameDebugger(JSDContext* jsdc, 
392                          JSDThreadState* jsdthreadstate,
393                          JSDStackFrameInfo* jsdframe)
394 {
395     JSBool rv = JS_TRUE;
396     JSD_LOCK_THREADSTATES(jsdc);
397
398     if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
399     {
400         rv = JS_IsDebuggerFrame(jsdthreadstate->context, jsdframe->fp);
401     }
402
403     JSD_UNLOCK_THREADSTATES(jsdc);
404     return rv;
405 }
406
407 JSBool
408 jsd_IsStackFrameConstructing(JSDContext* jsdc, 
409                              JSDThreadState* jsdthreadstate,
410                              JSDStackFrameInfo* jsdframe)
411 {
412     JSBool rv = JS_TRUE;
413     JSD_LOCK_THREADSTATES(jsdc);
414
415     if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
416     {
417         rv = JS_IsConstructorFrame(jsdthreadstate->context, jsdframe->fp);
418     }
419
420     JSD_UNLOCK_THREADSTATES(jsdc);
421     return rv;
422 }
423
424 JSBool
425 jsd_EvaluateUCScriptInStackFrame(JSDContext* jsdc, 
426                                  JSDThreadState* jsdthreadstate,
427                                  JSDStackFrameInfo* jsdframe,
428                                  const jschar *bytes, uintN length,
429                                  const char *filename, uintN lineno,
430                                  JSBool eatExceptions, jsval *rval)
431 {
432     JSBool retval;
433     JSBool valid;
434     JSExceptionState* exceptionState = NULL;
435     JSContext* cx;
436
437     JS_ASSERT(JSD_CURRENT_THREAD() == jsdthreadstate->thread);
438
439     JSD_LOCK_THREADSTATES(jsdc);
440     valid = jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe);
441     JSD_UNLOCK_THREADSTATES(jsdc);
442
443     if( ! valid )
444         return JS_FALSE;
445
446     cx = jsdthreadstate->context;
447     JS_ASSERT(cx);
448
449     if (eatExceptions)
450         exceptionState = JS_SaveExceptionState(cx);
451     JS_ClearPendingException(cx);
452     jsd_StartingEvalUsingFilename(jsdc, filename);
453     retval = JS_EvaluateUCInStackFrame(cx, jsdframe->fp, bytes, length, 
454                                        filename, lineno, rval);
455     jsd_FinishedEvalUsingFilename(jsdc, filename);
456     if (eatExceptions)
457         JS_RestoreExceptionState(cx, exceptionState);
458
459     return retval;
460 }
461
462 JSBool
463 jsd_EvaluateScriptInStackFrame(JSDContext* jsdc, 
464                                JSDThreadState* jsdthreadstate,
465                                JSDStackFrameInfo* jsdframe,
466                                const char *bytes, uintN length,
467                                const char *filename, uintN lineno,
468                                JSBool eatExceptions, jsval *rval)
469 {
470     JSBool retval;
471     JSBool valid;
472     JSExceptionState* exceptionState = NULL;
473     JSContext *cx;
474
475     JS_ASSERT(JSD_CURRENT_THREAD() == jsdthreadstate->thread);
476
477     JSD_LOCK_THREADSTATES(jsdc);
478     valid = jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe);
479     JSD_UNLOCK_THREADSTATES(jsdc);
480
481     if (!valid)
482         return JS_FALSE;
483
484     cx = jsdthreadstate->context;
485     JS_ASSERT(cx);
486
487     if (eatExceptions)
488         exceptionState = JS_SaveExceptionState(cx);
489     JS_ClearPendingException(cx);
490     jsd_StartingEvalUsingFilename(jsdc, filename);
491     retval = JS_EvaluateInStackFrame(cx, jsdframe->fp, bytes, length,
492                                      filename, lineno, rval);
493     jsd_FinishedEvalUsingFilename(jsdc, filename);
494     if (eatExceptions)
495         JS_RestoreExceptionState(cx, exceptionState);
496
497     return retval;
498 }
499
500 JSString*
501 jsd_ValToStringInStackFrame(JSDContext* jsdc, 
502                             JSDThreadState* jsdthreadstate,
503                             JSDStackFrameInfo* jsdframe,
504                             jsval val)
505 {
506     JSBool valid;
507     JSString* retval;
508     JSExceptionState* exceptionState;
509     JSContext* cx;
510
511     JSD_LOCK_THREADSTATES(jsdc);
512     valid = jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe);
513     JSD_UNLOCK_THREADSTATES(jsdc);
514
515     if( ! valid )
516         return NULL;
517
518     cx = jsdthreadstate->context;
519     JS_ASSERT(cx);
520
521     exceptionState = JS_SaveExceptionState(cx);
522     retval = JS_ValueToString(cx, val);
523     JS_RestoreExceptionState(cx, exceptionState);
524
525     return retval;
526 }
527
528 JSBool
529 jsd_IsValidThreadState(JSDContext*        jsdc, 
530                        JSDThreadState*    jsdthreadstate)
531 {
532     JSDThreadState *cur;
533
534     JS_ASSERT( JSD_THREADSTATES_LOCKED(jsdc) );
535
536     for( cur = (JSDThreadState*)jsdc->threadsStates.next;
537          cur != (JSDThreadState*)&jsdc->threadsStates;
538          cur = (JSDThreadState*)cur->links.next ) 
539     {
540         if( cur == jsdthreadstate )
541             return JS_TRUE;
542     }
543     return JS_FALSE;
544 }    
545
546 JSBool
547 jsd_IsValidFrameInThreadState(JSDContext*        jsdc, 
548                               JSDThreadState*    jsdthreadstate,
549                               JSDStackFrameInfo* jsdframe)
550 {
551     JS_ASSERT(JSD_THREADSTATES_LOCKED(jsdc));
552
553     if( ! jsd_IsValidThreadState(jsdc, jsdthreadstate) )
554         return JS_FALSE;
555     if( jsdframe->jsdthreadstate != jsdthreadstate )
556         return JS_FALSE;
557
558     JSD_ASSERT_VALID_THREAD_STATE(jsdthreadstate);
559     JSD_ASSERT_VALID_STACK_FRAME(jsdframe);
560     
561     return JS_TRUE;
562 }
563
564 static JSContext*
565 _getContextForThreadState(JSDContext* jsdc, JSDThreadState* jsdthreadstate)
566 {
567     JSBool valid;
568     JSD_LOCK_THREADSTATES(jsdc);
569     valid = jsd_IsValidThreadState(jsdc, jsdthreadstate);
570     JSD_UNLOCK_THREADSTATES(jsdc);
571     if( valid )
572         return jsdthreadstate->context;
573     return NULL;
574 }        
575
576 JSDValue*
577 jsd_GetException(JSDContext* jsdc, JSDThreadState* jsdthreadstate)
578 {
579     JSContext* cx;
580     jsval val;
581
582     if(!(cx = _getContextForThreadState(jsdc, jsdthreadstate)))
583         return NULL;
584
585     if(JS_GetPendingException(cx, &val))
586         return jsd_NewValue(jsdc, val);
587     return NULL;
588 }        
589
590 JSBool
591 jsd_SetException(JSDContext* jsdc, JSDThreadState* jsdthreadstate, 
592                  JSDValue* jsdval)
593 {
594     JSContext* cx;
595
596     if(!(cx = _getContextForThreadState(jsdc, jsdthreadstate)))
597         return JS_FALSE;
598
599     if(jsdval)
600         JS_SetPendingException(cx, JSD_GetValueWrappedJSVal(jsdc, jsdval));
601     else
602         JS_ClearPendingException(cx);
603     return JS_TRUE;
604 }
605