js/jsd/jsd_stak.cpp

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:7a3c5cf89009
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 /*
8 * JavaScript Debugging support - Call stack support
9 */
10
11 #include "jsd.h"
12 #include "jsfriendapi.h"
13 #include "nsCxPusher.h"
14
15 using mozilla::AutoPushJSContext;
16
17 #ifdef DEBUG
18 void JSD_ASSERT_VALID_THREAD_STATE(JSDThreadState* jsdthreadstate)
19 {
20 MOZ_ASSERT(jsdthreadstate);
21 MOZ_ASSERT(jsdthreadstate->stackDepth > 0);
22 }
23
24 void JSD_ASSERT_VALID_STACK_FRAME(JSDStackFrameInfo* jsdframe)
25 {
26 MOZ_ASSERT(jsdframe);
27 MOZ_ASSERT(jsdframe->jsdthreadstate);
28 }
29 #endif
30
31 static JSDStackFrameInfo*
32 _addNewFrame(JSDContext* jsdc,
33 JSDThreadState* jsdthreadstate,
34 JSScript* script,
35 uintptr_t pc,
36 bool isConstructing,
37 JSAbstractFramePtr frame)
38 {
39 JSDStackFrameInfo* jsdframe;
40 JSDScript* jsdscript = nullptr;
41
42 JSD_LOCK_SCRIPTS(jsdc);
43 jsdscript = jsd_FindJSDScript(jsdc, script);
44 JSD_UNLOCK_SCRIPTS(jsdc);
45 if (!jsdscript || (jsdc->flags & JSD_HIDE_DISABLED_FRAMES &&
46 !JSD_IS_DEBUG_ENABLED(jsdc, jsdscript)))
47 {
48 return nullptr;
49 }
50
51 if (!JSD_IS_DEBUG_ENABLED(jsdc, jsdscript))
52 jsdthreadstate->flags |= TS_HAS_DISABLED_FRAME;
53
54 jsdframe = (JSDStackFrameInfo*) calloc(1, sizeof(JSDStackFrameInfo));
55 if( ! jsdframe )
56 return nullptr;
57
58 jsdframe->jsdthreadstate = jsdthreadstate;
59 jsdframe->jsdscript = jsdscript;
60 jsdframe->isConstructing = isConstructing;
61 jsdframe->pc = pc;
62 jsdframe->frame = frame;
63
64 JS_APPEND_LINK(&jsdframe->links, &jsdthreadstate->stack);
65 jsdthreadstate->stackDepth++;
66
67 return jsdframe;
68 }
69
70 static void
71 _destroyFrame(JSDStackFrameInfo* jsdframe)
72 {
73 /* kill any alloc'd objects in frame here... */
74
75 if( jsdframe )
76 free(jsdframe);
77 }
78
79 JSDThreadState*
80 jsd_NewThreadState(JSDContext* jsdc, JSContext *cx )
81 {
82 JSDThreadState* jsdthreadstate;
83
84 jsdthreadstate = (JSDThreadState*)calloc(1, sizeof(JSDThreadState));
85 if( ! jsdthreadstate )
86 return nullptr;
87
88 jsdthreadstate->context = cx;
89 jsdthreadstate->thread = JSD_CURRENT_THREAD();
90 JS_INIT_CLIST(&jsdthreadstate->stack);
91 jsdthreadstate->stackDepth = 0;
92
93 JS_BeginRequest(jsdthreadstate->context);
94
95 JSBrokenFrameIterator iter(cx);
96 while(!iter.done())
97 {
98 JSAbstractFramePtr frame = iter.abstractFramePtr();
99 JS::RootedScript script(cx, frame.script());
100 uintptr_t pc = (uintptr_t)frame.pc();
101 JS::RootedValue dummyThis(cx);
102
103 /*
104 * don't construct a JSDStackFrame for dummy frames (those without a
105 * |this| object, or native frames, if JSD_INCLUDE_NATIVE_FRAMES
106 * isn't set.
107 */
108 if (frame.getThisValue(cx, &dummyThis))
109 {
110 bool isConstructing = iter.isConstructing();
111 JSDStackFrameInfo *frameInfo = _addNewFrame( jsdc, jsdthreadstate, script, pc, isConstructing, frame );
112
113 if ((jsdthreadstate->stackDepth == 0 && !frameInfo) ||
114 (jsdthreadstate->stackDepth == 1 && frameInfo &&
115 frameInfo->jsdscript && !JSD_IS_DEBUG_ENABLED(jsdc, frameInfo->jsdscript)))
116 {
117 /*
118 * if we failed to create the first frame, or the top frame
119 * is not enabled for debugging, fail the entire thread state.
120 */
121 JS_INIT_CLIST(&jsdthreadstate->links);
122 JS_EndRequest(jsdthreadstate->context);
123 jsd_DestroyThreadState(jsdc, jsdthreadstate);
124 return nullptr;
125 }
126 }
127
128 ++iter;
129 }
130 JS_EndRequest(jsdthreadstate->context);
131
132 if (jsdthreadstate->stackDepth == 0)
133 {
134 free(jsdthreadstate);
135 return nullptr;
136 }
137
138 JSD_LOCK_THREADSTATES(jsdc);
139 JS_APPEND_LINK(&jsdthreadstate->links, &jsdc->threadsStates);
140 JSD_UNLOCK_THREADSTATES(jsdc);
141
142 return jsdthreadstate;
143 }
144
145 void
146 jsd_DestroyThreadState(JSDContext* jsdc, JSDThreadState* jsdthreadstate)
147 {
148 JSDStackFrameInfo* jsdframe;
149 JSCList* list;
150
151 MOZ_ASSERT(jsdthreadstate);
152 MOZ_ASSERT(JSD_CURRENT_THREAD() == jsdthreadstate->thread);
153
154 JSD_LOCK_THREADSTATES(jsdc);
155 JS_REMOVE_LINK(&jsdthreadstate->links);
156 JSD_UNLOCK_THREADSTATES(jsdc);
157
158 list = &jsdthreadstate->stack;
159 while( (JSDStackFrameInfo*)list != (jsdframe = (JSDStackFrameInfo*)list->next) )
160 {
161 JS_REMOVE_LINK(&jsdframe->links);
162 _destroyFrame(jsdframe);
163 }
164 free(jsdthreadstate);
165 }
166
167 unsigned
168 jsd_GetCountOfStackFrames(JSDContext* jsdc, JSDThreadState* jsdthreadstate)
169 {
170 unsigned count = 0;
171
172 JSD_LOCK_THREADSTATES(jsdc);
173
174 if( jsd_IsValidThreadState(jsdc, jsdthreadstate) )
175 count = jsdthreadstate->stackDepth;
176
177 JSD_UNLOCK_THREADSTATES(jsdc);
178
179 return count;
180 }
181
182 JSDStackFrameInfo*
183 jsd_GetStackFrame(JSDContext* jsdc, JSDThreadState* jsdthreadstate)
184 {
185 JSDStackFrameInfo* jsdframe = nullptr;
186
187 JSD_LOCK_THREADSTATES(jsdc);
188
189 if( jsd_IsValidThreadState(jsdc, jsdthreadstate) )
190 jsdframe = (JSDStackFrameInfo*) JS_LIST_HEAD(&jsdthreadstate->stack);
191 JSD_UNLOCK_THREADSTATES(jsdc);
192
193 return jsdframe;
194 }
195
196 JSContext *
197 jsd_GetJSContext (JSDContext* jsdc, JSDThreadState* jsdthreadstate)
198 {
199 JSContext* cx = nullptr;
200
201 JSD_LOCK_THREADSTATES(jsdc);
202 if( jsd_IsValidThreadState(jsdc, jsdthreadstate) )
203 cx = jsdthreadstate->context;
204 JSD_UNLOCK_THREADSTATES(jsdc);
205
206 return cx;
207 }
208
209 JSDStackFrameInfo*
210 jsd_GetCallingStackFrame(JSDContext* jsdc,
211 JSDThreadState* jsdthreadstate,
212 JSDStackFrameInfo* jsdframe)
213 {
214 JSDStackFrameInfo* nextjsdframe = nullptr;
215
216 JSD_LOCK_THREADSTATES(jsdc);
217
218 if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
219 if( JS_LIST_HEAD(&jsdframe->links) != &jsdframe->jsdthreadstate->stack )
220 nextjsdframe = (JSDStackFrameInfo*) JS_LIST_HEAD(&jsdframe->links);
221
222 JSD_UNLOCK_THREADSTATES(jsdc);
223
224 return nextjsdframe;
225 }
226
227 JSDScript*
228 jsd_GetScriptForStackFrame(JSDContext* jsdc,
229 JSDThreadState* jsdthreadstate,
230 JSDStackFrameInfo* jsdframe)
231 {
232 JSDScript* jsdscript = nullptr;
233
234 JSD_LOCK_THREADSTATES(jsdc);
235
236 if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
237 jsdscript = jsdframe->jsdscript;
238
239 JSD_UNLOCK_THREADSTATES(jsdc);
240
241 return jsdscript;
242 }
243
244 uintptr_t
245 jsd_GetPCForStackFrame(JSDContext* jsdc,
246 JSDThreadState* jsdthreadstate,
247 JSDStackFrameInfo* jsdframe)
248 {
249 uintptr_t pc = 0;
250
251 JSD_LOCK_THREADSTATES(jsdc);
252
253 if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
254 pc = jsdframe->pc;
255
256 JSD_UNLOCK_THREADSTATES(jsdc);
257
258 return pc;
259 }
260
261 JSDValue*
262 jsd_GetCallObjectForStackFrame(JSDContext* jsdc,
263 JSDThreadState* jsdthreadstate,
264 JSDStackFrameInfo* jsdframe)
265 {
266 JSObject* obj;
267 JSDValue* jsdval = nullptr;
268
269 JSD_LOCK_THREADSTATES(jsdc);
270
271 if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
272 {
273 obj = jsdframe->frame.callObject(jsdthreadstate->context);
274 if(obj)
275 jsdval = JSD_NewValue(jsdc, OBJECT_TO_JSVAL(obj));
276 }
277
278 JSD_UNLOCK_THREADSTATES(jsdc);
279
280 return jsdval;
281 }
282
283 JSDValue*
284 jsd_GetScopeChainForStackFrame(JSDContext* jsdc,
285 JSDThreadState* jsdthreadstate,
286 JSDStackFrameInfo* jsdframe)
287 {
288 JS::RootedObject obj(jsdthreadstate->context);
289 JSDValue* jsdval = nullptr;
290
291 JSD_LOCK_THREADSTATES(jsdc);
292
293 if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
294 {
295 JS_BeginRequest(jsdthreadstate->context);
296 obj = jsdframe->frame.scopeChain(jsdthreadstate->context);
297 JS_EndRequest(jsdthreadstate->context);
298 if(obj)
299 jsdval = JSD_NewValue(jsdc, OBJECT_TO_JSVAL(obj));
300 }
301
302 JSD_UNLOCK_THREADSTATES(jsdc);
303
304 return jsdval;
305 }
306
307 JSDValue*
308 jsd_GetThisForStackFrame(JSDContext* jsdc,
309 JSDThreadState* jsdthreadstate,
310 JSDStackFrameInfo* jsdframe)
311 {
312 JSDValue* jsdval = nullptr;
313 JSD_LOCK_THREADSTATES(jsdc);
314
315 if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
316 {
317 bool ok;
318 JS::RootedValue thisval(jsdthreadstate->context);
319 JS_BeginRequest(jsdthreadstate->context);
320 ok = jsdframe->frame.getThisValue(jsdthreadstate->context, &thisval);
321 JS_EndRequest(jsdthreadstate->context);
322 if(ok)
323 jsdval = JSD_NewValue(jsdc, thisval);
324 }
325
326 JSD_UNLOCK_THREADSTATES(jsdc);
327 return jsdval;
328 }
329
330 JSString*
331 jsd_GetIdForStackFrame(JSDContext* jsdc,
332 JSDThreadState* jsdthreadstate,
333 JSDStackFrameInfo* jsdframe)
334 {
335 JSString *rv = nullptr;
336
337 JSD_LOCK_THREADSTATES(jsdc);
338
339 if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
340 {
341 JSFunction *fun = jsdframe->frame.maybeFun();
342 if( fun )
343 {
344 rv = JS_GetFunctionId (fun);
345
346 /*
347 * For compatibility we return "anonymous", not an empty string
348 * here.
349 */
350 if( !rv )
351 rv = JS_GetAnonymousString(jsdc->jsrt);
352 }
353 }
354
355 JSD_UNLOCK_THREADSTATES(jsdc);
356 return rv;
357 }
358
359 bool
360 jsd_IsStackFrameDebugger(JSDContext* jsdc,
361 JSDThreadState* jsdthreadstate,
362 JSDStackFrameInfo* jsdframe)
363 {
364 bool rv = true;
365 JSD_LOCK_THREADSTATES(jsdc);
366
367 if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
368 {
369 rv = jsdframe->frame.isDebuggerFrame();
370 }
371
372 JSD_UNLOCK_THREADSTATES(jsdc);
373 return rv;
374 }
375
376 bool
377 jsd_IsStackFrameConstructing(JSDContext* jsdc,
378 JSDThreadState* jsdthreadstate,
379 JSDStackFrameInfo* jsdframe)
380 {
381 bool rv = true;
382 JSD_LOCK_THREADSTATES(jsdc);
383
384 if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
385 {
386 rv = jsdframe->isConstructing;
387 }
388
389 JSD_UNLOCK_THREADSTATES(jsdc);
390 return rv;
391 }
392
393 bool
394 jsd_EvaluateUCScriptInStackFrame(JSDContext* jsdc,
395 JSDThreadState* jsdthreadstate,
396 JSDStackFrameInfo* jsdframe,
397 const jschar *bytes, unsigned length,
398 const char *filename, unsigned lineno,
399 bool eatExceptions, JS::MutableHandleValue rval)
400 {
401 bool retval;
402 bool valid;
403 JSExceptionState* exceptionState = nullptr;
404
405 MOZ_ASSERT(JSD_CURRENT_THREAD() == jsdthreadstate->thread);
406
407 JSD_LOCK_THREADSTATES(jsdc);
408 valid = jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe);
409 JSD_UNLOCK_THREADSTATES(jsdc);
410
411 if( ! valid )
412 return false;
413
414 AutoPushJSContext cx(jsdthreadstate->context);
415 MOZ_ASSERT(cx);
416
417 if (eatExceptions)
418 exceptionState = JS_SaveExceptionState(cx);
419 JS_ClearPendingException(cx);
420 jsd_StartingEvalUsingFilename(jsdc, filename);
421 retval = jsdframe->frame.evaluateUCInStackFrame(cx, bytes, length, filename, lineno,
422 rval);
423 jsd_FinishedEvalUsingFilename(jsdc, filename);
424 if (eatExceptions)
425 JS_RestoreExceptionState(cx, exceptionState);
426
427 return retval;
428 }
429
430 bool
431 jsd_EvaluateScriptInStackFrame(JSDContext* jsdc,
432 JSDThreadState* jsdthreadstate,
433 JSDStackFrameInfo* jsdframe,
434 const char *bytes, unsigned length,
435 const char *filename, unsigned lineno,
436 bool eatExceptions, JS::MutableHandleValue rval)
437 {
438 bool retval;
439 bool valid;
440 JSExceptionState* exceptionState = nullptr;
441
442 MOZ_ASSERT(JSD_CURRENT_THREAD() == jsdthreadstate->thread);
443
444 JSD_LOCK_THREADSTATES(jsdc);
445 valid = jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe);
446 JSD_UNLOCK_THREADSTATES(jsdc);
447
448 if (!valid)
449 return false;
450
451 AutoPushJSContext cx(jsdthreadstate->context);
452 MOZ_ASSERT(cx);
453
454 if (eatExceptions)
455 exceptionState = JS_SaveExceptionState(cx);
456 JS_ClearPendingException(cx);
457 jsd_StartingEvalUsingFilename(jsdc, filename);
458 retval = jsdframe->frame.evaluateInStackFrame(cx, bytes, length, filename, lineno,
459 rval);
460 jsd_FinishedEvalUsingFilename(jsdc, filename);
461 if (eatExceptions)
462 JS_RestoreExceptionState(cx, exceptionState);
463
464 return retval;
465 }
466
467 JSString*
468 jsd_ValToStringInStackFrame(JSDContext* jsdc,
469 JSDThreadState* jsdthreadstate,
470 JSDStackFrameInfo* jsdframe,
471 jsval val)
472 {
473 bool valid;
474 JSExceptionState* exceptionState;
475 JSContext *cx = jsdthreadstate->context;
476
477 JSD_LOCK_THREADSTATES(jsdc);
478 valid = jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe);
479 JSD_UNLOCK_THREADSTATES(jsdc);
480
481 if( ! valid )
482 return nullptr;
483
484 JS::RootedString retval(cx);
485 MOZ_ASSERT(cx);
486 JS::RootedValue v(cx, val);
487 {
488 AutoPushJSContext cx(jsdthreadstate->context);
489 exceptionState = JS_SaveExceptionState(cx);
490 retval = JS::ToString(cx, v);
491 JS_RestoreExceptionState(cx, exceptionState);
492 }
493
494 return retval;
495 }
496
497 bool
498 jsd_IsValidThreadState(JSDContext* jsdc,
499 JSDThreadState* jsdthreadstate)
500 {
501 JSDThreadState *cur;
502
503 MOZ_ASSERT( JSD_THREADSTATES_LOCKED(jsdc) );
504
505 for( cur = (JSDThreadState*)jsdc->threadsStates.next;
506 cur != (JSDThreadState*)&jsdc->threadsStates;
507 cur = (JSDThreadState*)cur->links.next )
508 {
509 if( cur == jsdthreadstate )
510 return true;
511 }
512 return false;
513 }
514
515 bool
516 jsd_IsValidFrameInThreadState(JSDContext* jsdc,
517 JSDThreadState* jsdthreadstate,
518 JSDStackFrameInfo* jsdframe)
519 {
520 MOZ_ASSERT(JSD_THREADSTATES_LOCKED(jsdc));
521
522 if( ! jsd_IsValidThreadState(jsdc, jsdthreadstate) )
523 return false;
524 if( jsdframe->jsdthreadstate != jsdthreadstate )
525 return false;
526
527 JSD_ASSERT_VALID_THREAD_STATE(jsdthreadstate);
528 JSD_ASSERT_VALID_STACK_FRAME(jsdframe);
529
530 return true;
531 }
532
533 static JSContext*
534 _getContextForThreadState(JSDContext* jsdc, JSDThreadState* jsdthreadstate)
535 {
536 bool valid;
537 JSD_LOCK_THREADSTATES(jsdc);
538 valid = jsd_IsValidThreadState(jsdc, jsdthreadstate);
539 JSD_UNLOCK_THREADSTATES(jsdc);
540 if( valid )
541 return jsdthreadstate->context;
542 return nullptr;
543 }
544
545 JSDValue*
546 jsd_GetException(JSDContext* jsdc, JSDThreadState* jsdthreadstate)
547 {
548 JSContext* cx;
549 if(!(cx = _getContextForThreadState(jsdc, jsdthreadstate)))
550 return nullptr;
551
552 JS::RootedValue val(cx);
553 if(JS_GetPendingException(cx, &val))
554 return jsd_NewValue(jsdc, val);
555 return nullptr;
556 }
557
558 bool
559 jsd_SetException(JSDContext* jsdc, JSDThreadState* jsdthreadstate,
560 JSDValue* jsdval)
561 {
562 JSContext* cx;
563
564 if(!(cx = _getContextForThreadState(jsdc, jsdthreadstate)))
565 return false;
566
567 if(jsdval) {
568 JS::RootedValue exn(cx, JSD_GetValueWrappedJSVal(jsdc, jsdval));
569 JS_SetPendingException(cx, exn);
570 } else {
571 JS_ClearPendingException(cx);
572 }
573 return true;
574 }
575

mercurial