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