|
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 - Script support |
|
9 */ |
|
10 |
|
11 #include "jsd.h" |
|
12 #include "jsfriendapi.h" |
|
13 #include "nsCxPusher.h" |
|
14 |
|
15 using mozilla::AutoSafeJSContext; |
|
16 |
|
17 /* Comment this out to disable (NT specific) dumping as we go */ |
|
18 /* |
|
19 ** #ifdef DEBUG |
|
20 ** #define JSD_DUMP 1 |
|
21 ** #endif |
|
22 */ |
|
23 |
|
24 #define NOT_SET_YET -1 |
|
25 |
|
26 /***************************************************************************/ |
|
27 |
|
28 #ifdef DEBUG |
|
29 void JSD_ASSERT_VALID_SCRIPT(JSDScript* jsdscript) |
|
30 { |
|
31 MOZ_ASSERT(jsdscript); |
|
32 MOZ_ASSERT(jsdscript->script); |
|
33 } |
|
34 void JSD_ASSERT_VALID_EXEC_HOOK(JSDExecHook* jsdhook) |
|
35 { |
|
36 MOZ_ASSERT(jsdhook); |
|
37 MOZ_ASSERT(jsdhook->hook); |
|
38 } |
|
39 #endif |
|
40 |
|
41 static JSDScript* |
|
42 _newJSDScript(JSDContext* jsdc, |
|
43 JSContext *cx, |
|
44 JSScript *script_) |
|
45 { |
|
46 JS::RootedScript script(cx, script_); |
|
47 if ( JS_GetScriptIsSelfHosted(script) ) |
|
48 return nullptr; |
|
49 |
|
50 JSDScript* jsdscript; |
|
51 unsigned lineno; |
|
52 const char* raw_filename; |
|
53 |
|
54 MOZ_ASSERT(JSD_SCRIPTS_LOCKED(jsdc)); |
|
55 |
|
56 /* these are inlined javascript: urls and we can't handle them now */ |
|
57 lineno = (unsigned) JS_GetScriptBaseLineNumber(cx, script); |
|
58 if( lineno == 0 ) |
|
59 return nullptr; |
|
60 |
|
61 jsdscript = (JSDScript*) calloc(1, sizeof(JSDScript)); |
|
62 if( ! jsdscript ) |
|
63 return nullptr; |
|
64 |
|
65 raw_filename = JS_GetScriptFilename(script); |
|
66 |
|
67 JS_HashTableAdd(jsdc->scriptsTable, (void *)script, (void *)jsdscript); |
|
68 JS_APPEND_LINK(&jsdscript->links, &jsdc->scripts); |
|
69 jsdscript->jsdc = jsdc; |
|
70 jsdscript->script = script; |
|
71 jsdscript->lineBase = lineno; |
|
72 jsdscript->lineExtent = (unsigned)NOT_SET_YET; |
|
73 jsdscript->data = nullptr; |
|
74 jsdscript->url = (char*) jsd_BuildNormalizedURL(raw_filename); |
|
75 |
|
76 JS_INIT_CLIST(&jsdscript->hooks); |
|
77 |
|
78 return jsdscript; |
|
79 } |
|
80 |
|
81 static void |
|
82 _destroyJSDScript(JSDContext* jsdc, |
|
83 JSDScript* jsdscript) |
|
84 { |
|
85 MOZ_ASSERT(JSD_SCRIPTS_LOCKED(jsdc)); |
|
86 |
|
87 /* destroy all hooks */ |
|
88 jsd_ClearAllExecutionHooksForScript(jsdc, jsdscript); |
|
89 |
|
90 JS_REMOVE_LINK(&jsdscript->links); |
|
91 if(jsdscript->url) |
|
92 free(jsdscript->url); |
|
93 |
|
94 if (jsdscript->profileData) |
|
95 free(jsdscript->profileData); |
|
96 |
|
97 free(jsdscript); |
|
98 } |
|
99 |
|
100 /***************************************************************************/ |
|
101 |
|
102 #ifdef JSD_DUMP |
|
103 #ifndef XP_WIN |
|
104 void |
|
105 OutputDebugString (char *buf) |
|
106 { |
|
107 fprintf (stderr, "%s", buf); |
|
108 } |
|
109 #endif |
|
110 |
|
111 static void |
|
112 _dumpJSDScript(JSDContext* jsdc, JSDScript* jsdscript, const char* leadingtext) |
|
113 { |
|
114 const char* name; |
|
115 JSString* fun; |
|
116 unsigned base; |
|
117 unsigned extent; |
|
118 char Buf[256]; |
|
119 size_t n; |
|
120 |
|
121 name = jsd_GetScriptFilename(jsdc, jsdscript); |
|
122 fun = jsd_GetScriptFunctionId(jsdc, jsdscript); |
|
123 base = jsd_GetScriptBaseLineNumber(jsdc, jsdscript); |
|
124 extent = jsd_GetScriptLineExtent(jsdc, jsdscript); |
|
125 n = size_t(snprintf(Buf, sizeof(Buf), "%sscript=%08X, %s, ", |
|
126 leadingtext, (unsigned) jsdscript->script, |
|
127 name ? name : "no URL")); |
|
128 if (n + 1 < sizeof(Buf)) { |
|
129 if (fun) { |
|
130 n += size_t(snprintf(Buf + n, sizeof(Buf) - n, "%s", "no fun")); |
|
131 } else { |
|
132 n += JS_PutEscapedFlatString(Buf + n, sizeof(Buf) - n, |
|
133 MOZ_ASSERT_STRING_IS_FLAT(fun), 0); |
|
134 Buf[sizeof(Buf) - 1] = '\0'; |
|
135 } |
|
136 if (n + 1 < sizeof(Buf)) |
|
137 snprintf(Buf + n, sizeof(Buf) - n, ", %d-%d\n", base, base + extent - 1); |
|
138 } |
|
139 OutputDebugString( Buf ); |
|
140 } |
|
141 |
|
142 static void |
|
143 _dumpJSDScriptList( JSDContext* jsdc ) |
|
144 { |
|
145 JSDScript* iterp = nullptr; |
|
146 JSDScript* jsdscript = nullptr; |
|
147 |
|
148 OutputDebugString( "*** JSDScriptDump\n" ); |
|
149 while( nullptr != (jsdscript = jsd_IterateScripts(jsdc, &iterp)) ) |
|
150 _dumpJSDScript( jsdc, jsdscript, " script: " ); |
|
151 } |
|
152 #endif /* JSD_DUMP */ |
|
153 |
|
154 /***************************************************************************/ |
|
155 static JSHashNumber |
|
156 jsd_hash_script(const void *key) |
|
157 { |
|
158 return ((JSHashNumber)(ptrdiff_t) key) >> 2; /* help lame MSVC1.5 on Win16 */ |
|
159 } |
|
160 |
|
161 static void * |
|
162 jsd_alloc_script_table(void *priv, size_t size) |
|
163 { |
|
164 return malloc(size); |
|
165 } |
|
166 |
|
167 static void |
|
168 jsd_free_script_table(void *priv, void *item, size_t size) |
|
169 { |
|
170 free(item); |
|
171 } |
|
172 |
|
173 static JSHashEntry * |
|
174 jsd_alloc_script_entry(void *priv, const void *item) |
|
175 { |
|
176 return (JSHashEntry*) malloc(sizeof(JSHashEntry)); |
|
177 } |
|
178 |
|
179 static void |
|
180 jsd_free_script_entry(void *priv, JSHashEntry *he, unsigned flag) |
|
181 { |
|
182 if (flag == HT_FREE_ENTRY) |
|
183 { |
|
184 _destroyJSDScript((JSDContext*) priv, (JSDScript*) he->value); |
|
185 free(he); |
|
186 } |
|
187 } |
|
188 |
|
189 static const JSHashAllocOps script_alloc_ops = { |
|
190 jsd_alloc_script_table, jsd_free_script_table, |
|
191 jsd_alloc_script_entry, jsd_free_script_entry |
|
192 }; |
|
193 |
|
194 #ifndef JSD_SCRIPT_HASH_SIZE |
|
195 #define JSD_SCRIPT_HASH_SIZE 1024 |
|
196 #endif |
|
197 |
|
198 bool |
|
199 jsd_InitScriptManager(JSDContext* jsdc) |
|
200 { |
|
201 JS_INIT_CLIST(&jsdc->scripts); |
|
202 jsdc->scriptsTable = JS_NewHashTable(JSD_SCRIPT_HASH_SIZE, jsd_hash_script, |
|
203 JS_CompareValues, JS_CompareValues, |
|
204 &script_alloc_ops, (void*) jsdc); |
|
205 return !!jsdc->scriptsTable; |
|
206 } |
|
207 |
|
208 void |
|
209 jsd_DestroyScriptManager(JSDContext* jsdc) |
|
210 { |
|
211 JSD_LOCK_SCRIPTS(jsdc); |
|
212 if (jsdc->scriptsTable) |
|
213 JS_HashTableDestroy(jsdc->scriptsTable); |
|
214 JSD_UNLOCK_SCRIPTS(jsdc); |
|
215 } |
|
216 |
|
217 JSDScript* |
|
218 jsd_FindJSDScript( JSDContext* jsdc, |
|
219 JSScript *script ) |
|
220 { |
|
221 MOZ_ASSERT(JSD_SCRIPTS_LOCKED(jsdc)); |
|
222 return (JSDScript*) JS_HashTableLookup(jsdc->scriptsTable, (void *)script); |
|
223 } |
|
224 |
|
225 JSDScript * |
|
226 jsd_FindOrCreateJSDScript(JSDContext *jsdc, |
|
227 JSContext *cx, |
|
228 JSScript *script_, |
|
229 JSAbstractFramePtr frame) |
|
230 { |
|
231 JS::RootedScript script(cx, script_); |
|
232 JSDScript *jsdscript; |
|
233 MOZ_ASSERT(JSD_SCRIPTS_LOCKED(jsdc)); |
|
234 |
|
235 jsdscript = jsd_FindJSDScript(jsdc, script); |
|
236 if (jsdscript) |
|
237 return jsdscript; |
|
238 |
|
239 /* Fallback for unknown scripts: create a new script. */ |
|
240 if (!frame) { |
|
241 JSBrokenFrameIterator iter(cx); |
|
242 if (!iter.done()) |
|
243 frame = iter.abstractFramePtr(); |
|
244 } |
|
245 if (frame) |
|
246 jsdscript = _newJSDScript(jsdc, cx, script); |
|
247 |
|
248 return jsdscript; |
|
249 } |
|
250 |
|
251 JSDProfileData* |
|
252 jsd_GetScriptProfileData(JSDContext* jsdc, JSDScript *script) |
|
253 { |
|
254 if (!script->profileData) |
|
255 script->profileData = (JSDProfileData*)calloc(1, sizeof(JSDProfileData)); |
|
256 |
|
257 return script->profileData; |
|
258 } |
|
259 |
|
260 uint32_t |
|
261 jsd_GetScriptFlags(JSDContext *jsdc, JSDScript *script) |
|
262 { |
|
263 return script->flags; |
|
264 } |
|
265 |
|
266 void |
|
267 jsd_SetScriptFlags(JSDContext *jsdc, JSDScript *script, uint32_t flags) |
|
268 { |
|
269 script->flags = flags; |
|
270 } |
|
271 |
|
272 unsigned |
|
273 jsd_GetScriptCallCount(JSDContext* jsdc, JSDScript *script) |
|
274 { |
|
275 if (script->profileData) |
|
276 return script->profileData->callCount; |
|
277 |
|
278 return 0; |
|
279 } |
|
280 |
|
281 unsigned |
|
282 jsd_GetScriptMaxRecurseDepth(JSDContext* jsdc, JSDScript *script) |
|
283 { |
|
284 if (script->profileData) |
|
285 return script->profileData->maxRecurseDepth; |
|
286 |
|
287 return 0; |
|
288 } |
|
289 |
|
290 double |
|
291 jsd_GetScriptMinExecutionTime(JSDContext* jsdc, JSDScript *script) |
|
292 { |
|
293 if (script->profileData) |
|
294 return script->profileData->minExecutionTime; |
|
295 |
|
296 return 0.0; |
|
297 } |
|
298 |
|
299 double |
|
300 jsd_GetScriptMaxExecutionTime(JSDContext* jsdc, JSDScript *script) |
|
301 { |
|
302 if (script->profileData) |
|
303 return script->profileData->maxExecutionTime; |
|
304 |
|
305 return 0.0; |
|
306 } |
|
307 |
|
308 double |
|
309 jsd_GetScriptTotalExecutionTime(JSDContext* jsdc, JSDScript *script) |
|
310 { |
|
311 if (script->profileData) |
|
312 return script->profileData->totalExecutionTime; |
|
313 |
|
314 return 0.0; |
|
315 } |
|
316 |
|
317 double |
|
318 jsd_GetScriptMinOwnExecutionTime(JSDContext* jsdc, JSDScript *script) |
|
319 { |
|
320 if (script->profileData) |
|
321 return script->profileData->minOwnExecutionTime; |
|
322 |
|
323 return 0.0; |
|
324 } |
|
325 |
|
326 double |
|
327 jsd_GetScriptMaxOwnExecutionTime(JSDContext* jsdc, JSDScript *script) |
|
328 { |
|
329 if (script->profileData) |
|
330 return script->profileData->maxOwnExecutionTime; |
|
331 |
|
332 return 0.0; |
|
333 } |
|
334 |
|
335 double |
|
336 jsd_GetScriptTotalOwnExecutionTime(JSDContext* jsdc, JSDScript *script) |
|
337 { |
|
338 if (script->profileData) |
|
339 return script->profileData->totalOwnExecutionTime; |
|
340 |
|
341 return 0.0; |
|
342 } |
|
343 |
|
344 void |
|
345 jsd_ClearScriptProfileData(JSDContext* jsdc, JSDScript *script) |
|
346 { |
|
347 if (script->profileData) |
|
348 { |
|
349 free(script->profileData); |
|
350 script->profileData = nullptr; |
|
351 } |
|
352 } |
|
353 |
|
354 JSScript * |
|
355 jsd_GetJSScript (JSDContext *jsdc, JSDScript *script) |
|
356 { |
|
357 return script->script; |
|
358 } |
|
359 |
|
360 JSFunction * |
|
361 jsd_GetJSFunction (JSDContext *jsdc, JSDScript *script) |
|
362 { |
|
363 AutoSafeJSContext cx; |
|
364 return JS_GetScriptFunction(cx, script->script); |
|
365 } |
|
366 |
|
367 JSDScript* |
|
368 jsd_IterateScripts(JSDContext* jsdc, JSDScript **iterp) |
|
369 { |
|
370 JSDScript *jsdscript = *iterp; |
|
371 |
|
372 MOZ_ASSERT(JSD_SCRIPTS_LOCKED(jsdc)); |
|
373 |
|
374 if( !jsdscript ) |
|
375 jsdscript = (JSDScript *)jsdc->scripts.next; |
|
376 if( jsdscript == (JSDScript *)&jsdc->scripts ) |
|
377 return nullptr; |
|
378 *iterp = (JSDScript*) jsdscript->links.next; |
|
379 return jsdscript; |
|
380 } |
|
381 |
|
382 void * |
|
383 jsd_SetScriptPrivate(JSDScript *jsdscript, void *data) |
|
384 { |
|
385 void *rval = jsdscript->data; |
|
386 jsdscript->data = data; |
|
387 return rval; |
|
388 } |
|
389 |
|
390 void * |
|
391 jsd_GetScriptPrivate(JSDScript *jsdscript) |
|
392 { |
|
393 return jsdscript->data; |
|
394 } |
|
395 |
|
396 bool |
|
397 jsd_IsActiveScript(JSDContext* jsdc, JSDScript *jsdscript) |
|
398 { |
|
399 JSDScript *current; |
|
400 |
|
401 MOZ_ASSERT(JSD_SCRIPTS_LOCKED(jsdc)); |
|
402 |
|
403 for( current = (JSDScript *)jsdc->scripts.next; |
|
404 current != (JSDScript *)&jsdc->scripts; |
|
405 current = (JSDScript *)current->links.next ) |
|
406 { |
|
407 if(jsdscript == current) |
|
408 return true; |
|
409 } |
|
410 return false; |
|
411 } |
|
412 |
|
413 const char* |
|
414 jsd_GetScriptFilename(JSDContext* jsdc, JSDScript *jsdscript) |
|
415 { |
|
416 return jsdscript->url; |
|
417 } |
|
418 |
|
419 JSString* |
|
420 jsd_GetScriptFunctionId(JSDContext* jsdc, JSDScript *jsdscript) |
|
421 { |
|
422 JSString* str; |
|
423 JSFunction *fun = jsd_GetJSFunction(jsdc, jsdscript); |
|
424 |
|
425 if( ! fun ) |
|
426 return nullptr; |
|
427 str = JS_GetFunctionId(fun); |
|
428 |
|
429 /* For compatibility we return "anonymous", not an empty string here. */ |
|
430 return str ? str : JS_GetAnonymousString(jsdc->jsrt); |
|
431 } |
|
432 |
|
433 unsigned |
|
434 jsd_GetScriptBaseLineNumber(JSDContext* jsdc, JSDScript *jsdscript) |
|
435 { |
|
436 return jsdscript->lineBase; |
|
437 } |
|
438 |
|
439 unsigned |
|
440 jsd_GetScriptLineExtent(JSDContext* jsdc, JSDScript *jsdscript) |
|
441 { |
|
442 AutoSafeJSContext cx; |
|
443 JSAutoCompartment ac(cx, jsdc->glob); // Just in case. |
|
444 if( NOT_SET_YET == (int)jsdscript->lineExtent ) |
|
445 jsdscript->lineExtent = JS_GetScriptLineExtent(cx, jsdscript->script); |
|
446 return jsdscript->lineExtent; |
|
447 } |
|
448 |
|
449 uintptr_t |
|
450 jsd_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, unsigned line) |
|
451 { |
|
452 uintptr_t pc; |
|
453 |
|
454 if( !jsdscript ) |
|
455 return 0; |
|
456 |
|
457 AutoSafeJSContext cx; |
|
458 JSAutoCompartment ac(cx, jsdscript->script); |
|
459 pc = (uintptr_t) JS_LineNumberToPC(cx, jsdscript->script, line ); |
|
460 return pc; |
|
461 } |
|
462 |
|
463 unsigned |
|
464 jsd_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, uintptr_t pc) |
|
465 { |
|
466 unsigned first = jsdscript->lineBase; |
|
467 unsigned last = first + jsd_GetScriptLineExtent(jsdc, jsdscript) - 1; |
|
468 unsigned line = 0; |
|
469 |
|
470 if (pc) { |
|
471 AutoSafeJSContext cx; |
|
472 JSAutoCompartment ac(cx, jsdscript->script); |
|
473 line = JS_PCToLineNumber(cx, jsdscript->script, (jsbytecode*)pc); |
|
474 } |
|
475 |
|
476 if( line < first ) |
|
477 return first; |
|
478 if( line > last ) |
|
479 return last; |
|
480 |
|
481 return line; |
|
482 } |
|
483 |
|
484 bool |
|
485 jsd_GetLinePCs(JSDContext* jsdc, JSDScript* jsdscript, |
|
486 unsigned startLine, unsigned maxLines, |
|
487 unsigned* count, unsigned** retLines, uintptr_t** retPCs) |
|
488 { |
|
489 unsigned first = jsdscript->lineBase; |
|
490 unsigned last = first + jsd_GetScriptLineExtent(jsdc, jsdscript) - 1; |
|
491 bool ok; |
|
492 jsbytecode **pcs; |
|
493 unsigned i; |
|
494 |
|
495 if (last < startLine) |
|
496 return true; |
|
497 |
|
498 AutoSafeJSContext cx; |
|
499 JSAutoCompartment ac(cx, jsdscript->script); |
|
500 |
|
501 ok = JS_GetLinePCs(cx, jsdscript->script, |
|
502 startLine, maxLines, |
|
503 count, retLines, &pcs); |
|
504 |
|
505 if (ok) { |
|
506 if (retPCs) { |
|
507 for (i = 0; i < *count; ++i) { |
|
508 (*retPCs)[i] = (*pcs)[i]; |
|
509 } |
|
510 } |
|
511 |
|
512 JS_free(cx, pcs); |
|
513 } |
|
514 |
|
515 return ok; |
|
516 } |
|
517 |
|
518 bool |
|
519 jsd_SetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc hook, void* callerdata) |
|
520 { |
|
521 JSD_LOCK(); |
|
522 jsdc->scriptHook = hook; |
|
523 jsdc->scriptHookData = callerdata; |
|
524 JSD_UNLOCK(); |
|
525 return true; |
|
526 } |
|
527 |
|
528 bool |
|
529 jsd_GetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc* hook, void** callerdata) |
|
530 { |
|
531 JSD_LOCK(); |
|
532 if( hook ) |
|
533 *hook = jsdc->scriptHook; |
|
534 if( callerdata ) |
|
535 *callerdata = jsdc->scriptHookData; |
|
536 JSD_UNLOCK(); |
|
537 return true; |
|
538 } |
|
539 |
|
540 bool |
|
541 jsd_EnableSingleStepInterrupts(JSDContext* jsdc, JSDScript* jsdscript, bool enable) |
|
542 { |
|
543 bool rv; |
|
544 AutoSafeJSContext cx; |
|
545 JS::RootedScript script(cx, jsdscript->script); |
|
546 JSAutoCompartment ac(cx, script); |
|
547 JSD_LOCK(); |
|
548 rv = JS_SetSingleStepMode(cx, script, enable); |
|
549 JSD_UNLOCK(); |
|
550 return rv; |
|
551 } |
|
552 |
|
553 |
|
554 /***************************************************************************/ |
|
555 |
|
556 void |
|
557 jsd_NewScriptHookProc( |
|
558 JSContext *cx, |
|
559 const char *filename, /* URL this script loads from */ |
|
560 unsigned lineno, /* line where this script starts */ |
|
561 JSScript *script, |
|
562 JSFunction *fun, |
|
563 void* callerdata ) |
|
564 { |
|
565 JSDScript* jsdscript = nullptr; |
|
566 JSDContext* jsdc = (JSDContext*) callerdata; |
|
567 JSD_ScriptHookProc hook; |
|
568 void* hookData; |
|
569 |
|
570 JSD_ASSERT_VALID_CONTEXT(jsdc); |
|
571 |
|
572 if( JSD_IS_DANGEROUS_THREAD(jsdc) ) |
|
573 return; |
|
574 |
|
575 JSD_LOCK_SCRIPTS(jsdc); |
|
576 jsdscript = _newJSDScript(jsdc, cx, script); |
|
577 JSD_UNLOCK_SCRIPTS(jsdc); |
|
578 if( ! jsdscript ) |
|
579 return; |
|
580 |
|
581 #ifdef JSD_DUMP |
|
582 JSD_LOCK_SCRIPTS(jsdc); |
|
583 _dumpJSDScript(jsdc, jsdscript, "***NEW Script: "); |
|
584 _dumpJSDScriptList( jsdc ); |
|
585 JSD_UNLOCK_SCRIPTS(jsdc); |
|
586 #endif /* JSD_DUMP */ |
|
587 |
|
588 /* local in case jsdc->scriptHook gets cleared on another thread */ |
|
589 JSD_LOCK(); |
|
590 hook = jsdc->scriptHook; |
|
591 if( hook ) |
|
592 jsdscript->flags = jsdscript->flags | JSD_SCRIPT_CALL_DESTROY_HOOK_BIT; |
|
593 hookData = jsdc->scriptHookData; |
|
594 JSD_UNLOCK(); |
|
595 |
|
596 if( hook ) |
|
597 hook(jsdc, jsdscript, true, hookData); |
|
598 } |
|
599 |
|
600 void |
|
601 jsd_DestroyScriptHookProc( |
|
602 JSFreeOp *fop, |
|
603 JSScript *script_, |
|
604 void* callerdata ) |
|
605 { |
|
606 JSDScript* jsdscript = nullptr; |
|
607 JSDContext* jsdc = (JSDContext*) callerdata; |
|
608 // NB: We're called during GC, so we can't push a cx. Root directly with |
|
609 // the runtime. |
|
610 JS::RootedScript script(jsdc->jsrt, script_); |
|
611 JSD_ScriptHookProc hook; |
|
612 void* hookData; |
|
613 |
|
614 JSD_ASSERT_VALID_CONTEXT(jsdc); |
|
615 |
|
616 if( JSD_IS_DANGEROUS_THREAD(jsdc) ) |
|
617 return; |
|
618 |
|
619 JSD_LOCK_SCRIPTS(jsdc); |
|
620 jsdscript = jsd_FindJSDScript(jsdc, script); |
|
621 JSD_UNLOCK_SCRIPTS(jsdc); |
|
622 |
|
623 if( ! jsdscript ) |
|
624 return; |
|
625 |
|
626 #ifdef JSD_DUMP |
|
627 JSD_LOCK_SCRIPTS(jsdc); |
|
628 _dumpJSDScript(jsdc, jsdscript, "***DESTROY Script: "); |
|
629 JSD_UNLOCK_SCRIPTS(jsdc); |
|
630 #endif /* JSD_DUMP */ |
|
631 |
|
632 /* local in case hook gets cleared on another thread */ |
|
633 JSD_LOCK(); |
|
634 hook = (jsdscript->flags & JSD_SCRIPT_CALL_DESTROY_HOOK_BIT) ? jsdc->scriptHook |
|
635 : nullptr; |
|
636 hookData = jsdc->scriptHookData; |
|
637 JSD_UNLOCK(); |
|
638 |
|
639 if( hook ) |
|
640 hook(jsdc, jsdscript, false, hookData); |
|
641 |
|
642 JSD_LOCK_SCRIPTS(jsdc); |
|
643 JS_HashTableRemove(jsdc->scriptsTable, (void *)script); |
|
644 JSD_UNLOCK_SCRIPTS(jsdc); |
|
645 |
|
646 #ifdef JSD_DUMP |
|
647 JSD_LOCK_SCRIPTS(jsdc); |
|
648 _dumpJSDScriptList(jsdc); |
|
649 JSD_UNLOCK_SCRIPTS(jsdc); |
|
650 #endif /* JSD_DUMP */ |
|
651 } |
|
652 |
|
653 |
|
654 /***************************************************************************/ |
|
655 |
|
656 static JSDExecHook* |
|
657 _findHook(JSDContext* jsdc, JSDScript* jsdscript, uintptr_t pc) |
|
658 { |
|
659 JSDExecHook* jsdhook; |
|
660 JSCList* list = &jsdscript->hooks; |
|
661 |
|
662 for( jsdhook = (JSDExecHook*)list->next; |
|
663 jsdhook != (JSDExecHook*)list; |
|
664 jsdhook = (JSDExecHook*)jsdhook->links.next ) |
|
665 { |
|
666 if (jsdhook->pc == pc) |
|
667 return jsdhook; |
|
668 } |
|
669 return nullptr; |
|
670 } |
|
671 |
|
672 static bool |
|
673 _isActiveHook(JSDContext* jsdc, JSScript *script, JSDExecHook* jsdhook) |
|
674 { |
|
675 JSDExecHook* current; |
|
676 JSCList* list; |
|
677 JSDScript* jsdscript; |
|
678 |
|
679 JSD_LOCK_SCRIPTS(jsdc); |
|
680 jsdscript = jsd_FindJSDScript(jsdc, script); |
|
681 if( ! jsdscript) |
|
682 { |
|
683 JSD_UNLOCK_SCRIPTS(jsdc); |
|
684 return false; |
|
685 } |
|
686 |
|
687 list = &jsdscript->hooks; |
|
688 |
|
689 for( current = (JSDExecHook*)list->next; |
|
690 current != (JSDExecHook*)list; |
|
691 current = (JSDExecHook*)current->links.next ) |
|
692 { |
|
693 if(current == jsdhook) |
|
694 { |
|
695 JSD_UNLOCK_SCRIPTS(jsdc); |
|
696 return true; |
|
697 } |
|
698 } |
|
699 JSD_UNLOCK_SCRIPTS(jsdc); |
|
700 return false; |
|
701 } |
|
702 |
|
703 |
|
704 JSTrapStatus |
|
705 jsd_TrapHandler(JSContext *cx, JSScript *script_, jsbytecode *pc, jsval *rval, |
|
706 jsval closure) |
|
707 { |
|
708 JS::RootedScript script(cx, script_); |
|
709 JSDExecHook* jsdhook = (JSDExecHook*) JSVAL_TO_PRIVATE(closure); |
|
710 JSD_ExecutionHookProc hook; |
|
711 void* hookData; |
|
712 JSDContext* jsdc; |
|
713 |
|
714 JSD_LOCK(); |
|
715 |
|
716 if( nullptr == (jsdc = jsd_JSDContextForJSContext(cx)) || |
|
717 ! _isActiveHook(jsdc, script, jsdhook) ) |
|
718 { |
|
719 JSD_UNLOCK(); |
|
720 return JSTRAP_CONTINUE; |
|
721 } |
|
722 |
|
723 JSD_ASSERT_VALID_EXEC_HOOK(jsdhook); |
|
724 MOZ_ASSERT(!jsdhook->pc || jsdhook->pc == (uintptr_t)pc); |
|
725 MOZ_ASSERT(jsdhook->jsdscript->script == script); |
|
726 MOZ_ASSERT(jsdhook->jsdscript->jsdc == jsdc); |
|
727 |
|
728 hook = jsdhook->hook; |
|
729 hookData = jsdhook->callerdata; |
|
730 |
|
731 /* do not use jsdhook-> after this point */ |
|
732 JSD_UNLOCK(); |
|
733 |
|
734 if( ! jsdc || ! jsdc->inited ) |
|
735 return JSTRAP_CONTINUE; |
|
736 |
|
737 if( JSD_IS_DANGEROUS_THREAD(jsdc) ) |
|
738 return JSTRAP_CONTINUE; |
|
739 |
|
740 return jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_BREAKPOINT, |
|
741 hook, hookData, rval); |
|
742 } |
|
743 |
|
744 |
|
745 |
|
746 bool |
|
747 jsd_SetExecutionHook(JSDContext* jsdc, |
|
748 JSDScript* jsdscript, |
|
749 uintptr_t pc, |
|
750 JSD_ExecutionHookProc hook, |
|
751 void* callerdata) |
|
752 { |
|
753 JSDExecHook* jsdhook; |
|
754 bool rv; |
|
755 |
|
756 JSD_LOCK(); |
|
757 if( ! hook ) |
|
758 { |
|
759 jsd_ClearExecutionHook(jsdc, jsdscript, pc); |
|
760 JSD_UNLOCK(); |
|
761 return true; |
|
762 } |
|
763 |
|
764 jsdhook = _findHook(jsdc, jsdscript, pc); |
|
765 if( jsdhook ) |
|
766 { |
|
767 jsdhook->hook = hook; |
|
768 jsdhook->callerdata = callerdata; |
|
769 JSD_UNLOCK(); |
|
770 return true; |
|
771 } |
|
772 /* else... */ |
|
773 |
|
774 jsdhook = (JSDExecHook*)calloc(1, sizeof(JSDExecHook)); |
|
775 if( ! jsdhook ) { |
|
776 JSD_UNLOCK(); |
|
777 return false; |
|
778 } |
|
779 jsdhook->jsdscript = jsdscript; |
|
780 jsdhook->pc = pc; |
|
781 jsdhook->hook = hook; |
|
782 jsdhook->callerdata = callerdata; |
|
783 |
|
784 { |
|
785 AutoSafeJSContext cx; |
|
786 JSAutoCompartment ac(cx, jsdscript->script); |
|
787 JS::RootedScript script(cx, jsdscript->script); |
|
788 JS::RootedValue hookValue(cx, PRIVATE_TO_JSVAL(jsdhook)); |
|
789 rv = JS_SetTrap(cx, script, (jsbytecode*)pc, jsd_TrapHandler, hookValue); |
|
790 } |
|
791 |
|
792 if ( ! rv ) { |
|
793 free(jsdhook); |
|
794 JSD_UNLOCK(); |
|
795 return false; |
|
796 } |
|
797 |
|
798 JS_APPEND_LINK(&jsdhook->links, &jsdscript->hooks); |
|
799 JSD_UNLOCK(); |
|
800 |
|
801 return true; |
|
802 } |
|
803 |
|
804 bool |
|
805 jsd_ClearExecutionHook(JSDContext* jsdc, |
|
806 JSDScript* jsdscript, |
|
807 uintptr_t pc) |
|
808 { |
|
809 JSDExecHook* jsdhook; |
|
810 |
|
811 JSD_LOCK(); |
|
812 |
|
813 jsdhook = _findHook(jsdc, jsdscript, pc); |
|
814 if( ! jsdhook ) |
|
815 { |
|
816 JSD_UNLOCK(); |
|
817 return false; |
|
818 } |
|
819 |
|
820 { |
|
821 AutoSafeJSContext cx; |
|
822 JSAutoCompartment ac(cx, jsdscript->script); |
|
823 JS_ClearTrap(cx, jsdscript->script, |
|
824 (jsbytecode*)pc, nullptr, nullptr); |
|
825 } |
|
826 |
|
827 JS_REMOVE_LINK(&jsdhook->links); |
|
828 free(jsdhook); |
|
829 |
|
830 JSD_UNLOCK(); |
|
831 return true; |
|
832 } |
|
833 |
|
834 bool |
|
835 jsd_ClearAllExecutionHooksForScript(JSDContext* jsdc, JSDScript* jsdscript) |
|
836 { |
|
837 JSDExecHook* jsdhook; |
|
838 JSCList* list = &jsdscript->hooks; |
|
839 JSD_LOCK(); |
|
840 |
|
841 while( (JSDExecHook*)list != (jsdhook = (JSDExecHook*)list->next) ) |
|
842 { |
|
843 JS_REMOVE_LINK(&jsdhook->links); |
|
844 free(jsdhook); |
|
845 } |
|
846 |
|
847 JS_ClearScriptTraps(jsdc->jsrt, jsdscript->script); |
|
848 JSD_UNLOCK(); |
|
849 |
|
850 return true; |
|
851 } |
|
852 |
|
853 bool |
|
854 jsd_ClearAllExecutionHooks(JSDContext* jsdc) |
|
855 { |
|
856 JSDScript* jsdscript; |
|
857 JSDScript* iterp = nullptr; |
|
858 |
|
859 JSD_LOCK(); |
|
860 while( nullptr != (jsdscript = jsd_IterateScripts(jsdc, &iterp)) ) |
|
861 jsd_ClearAllExecutionHooksForScript(jsdc, jsdscript); |
|
862 JSD_UNLOCK(); |
|
863 return true; |
|
864 } |
|
865 |
|
866 void |
|
867 jsd_ScriptCreated(JSDContext* jsdc, |
|
868 JSContext *cx, |
|
869 const char *filename, /* URL this script loads from */ |
|
870 unsigned lineno, /* line where this script starts */ |
|
871 JSScript *script, |
|
872 JSFunction *fun) |
|
873 { |
|
874 jsd_NewScriptHookProc(cx, filename, lineno, script, fun, jsdc); |
|
875 } |
|
876 |
|
877 void |
|
878 jsd_ScriptDestroyed(JSDContext* jsdc, |
|
879 JSFreeOp *fop, |
|
880 JSScript *script) |
|
881 { |
|
882 jsd_DestroyScriptHookProc(fop, script, jsdc); |
|
883 } |