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