Bug 465497, Wait for AUS to notice new snippets before testing them, r=bhearsum
[mozilla-1.9.git] / js / jsd / jsd_xpc.cpp
blobbc33a848810a56e670738a734757a7a81df5225b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * Robert Ginda, <rginda@netscape.com>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #include "jsd_xpc.h"
41 #include "jsdbgapi.h"
42 #include "jscntxt.h"
43 #include "jsfun.h"
45 #include "nsIXPConnect.h"
46 #include "nsIGenericFactory.h"
47 #include "nsIServiceManager.h"
48 #include "nsIScriptGlobalObject.h"
49 #include "nsIObserver.h"
50 #include "nsIObserverService.h"
51 #include "nsICategoryManager.h"
52 #include "nsIJSRuntimeService.h"
53 #include "nsIThreadInternal.h"
54 #include "nsThreadUtils.h"
55 #include "nsMemory.h"
56 #include "jsdebug.h"
57 #include "nsReadableUtils.h"
58 #include "nsCRT.h"
60 /* XXX DOM dependency */
61 #include "nsIScriptContext.h"
62 #include "nsIJSContextStack.h"
65 * defining CAUTIOUS_SCRIPTHOOK makes jsds disable GC while calling out to the
66 * script hook. This was a hack to avoid some js engine problems that should
67 * be fixed now (see Mozilla bug 77636).
69 #undef CAUTIOUS_SCRIPTHOOK
71 #ifdef DEBUG_verbose
72 # define DEBUG_COUNT(name, count) \
73 { if ((count % 10) == 0) printf (name ": %i\n", count); }
74 # define DEBUG_CREATE(name, count) {count++; DEBUG_COUNT ("+++++ "name,count)}
75 # define DEBUG_DESTROY(name, count) {count--; DEBUG_COUNT ("----- "name,count)}
76 #else
77 # define DEBUG_CREATE(name, count)
78 # define DEBUG_DESTROY(name, count)
79 #endif
81 #define ASSERT_VALID_CONTEXT { if (!mCx) return NS_ERROR_NOT_AVAILABLE; }
82 #define ASSERT_VALID_EPHEMERAL { if (!mValid) return NS_ERROR_NOT_AVAILABLE; }
84 #define JSDSERVICE_CID \
85 { /* f1299dc2-1dd1-11b2-a347-ee6b7660e048 */ \
86 0xf1299dc2, \
87 0x1dd1, \
88 0x11b2, \
89 {0xa3, 0x47, 0xee, 0x6b, 0x76, 0x60, 0xe0, 0x48} \
92 #define JSDASO_CID \
93 { /* 2fd6b7f6-eb8c-4f32-ad26-113f2c02d0fe */ \
94 0x2fd6b7f6, \
95 0xeb8c, \
96 0x4f32, \
97 {0xad, 0x26, 0x11, 0x3f, 0x2c, 0x02, 0xd0, 0xfe} \
100 #define JSDS_MAJOR_VERSION 1
101 #define JSDS_MINOR_VERSION 2
103 #define NS_CATMAN_CTRID "@mozilla.org/categorymanager;1"
104 #define NS_JSRT_CTRID "@mozilla.org/js/xpc/RuntimeService;1"
106 #define AUTOREG_CATEGORY "xpcom-autoregistration"
107 #define APPSTART_CATEGORY "app-startup"
108 #define JSD_AUTOREG_ENTRY "JSDebugger Startup Observer"
109 #define JSD_STARTUP_ENTRY "JSDebugger Startup Observer"
111 JS_STATIC_DLL_CALLBACK (JSBool)
112 jsds_GCCallbackProc (JSContext *cx, JSGCStatus status);
114 /*******************************************************************************
115 * global vars
116 ******************************************************************************/
118 const char implementationString[] = "Mozilla JavaScript Debugger Service";
120 const char jsdServiceCtrID[] = "@mozilla.org/js/jsd/debugger-service;1";
121 const char jsdARObserverCtrID[] = "@mozilla.org/js/jsd/app-start-observer;2";
122 const char jsdASObserverCtrID[] = "service,@mozilla.org/js/jsd/app-start-observer;2";
124 #ifdef DEBUG_verbose
125 PRUint32 gScriptCount = 0;
126 PRUint32 gValueCount = 0;
127 PRUint32 gPropertyCount = 0;
128 PRUint32 gContextCount = 0;
129 PRUint32 gFrameCount = 0;
130 #endif
132 static jsdService *gJsds = 0;
133 static JSGCCallback gLastGCProc = jsds_GCCallbackProc;
134 static JSGCStatus gGCStatus = JSGC_END;
136 static struct DeadScript {
137 PRCList links;
138 JSDContext *jsdc;
139 jsdIScript *script;
140 } *gDeadScripts = nsnull;
142 enum PatternType {
143 ptIgnore = 0U,
144 ptStartsWith = 1U,
145 ptEndsWith = 2U,
146 ptContains = 3U,
147 ptEquals = 4U
150 static struct FilterRecord {
151 PRCList links;
152 jsdIFilter *filterObject;
153 void *glob;
154 char *urlPattern;
155 PRUint32 patternLength;
156 PatternType patternType;
157 PRUint32 startLine;
158 PRUint32 endLine;
159 } *gFilters = nsnull;
161 static struct LiveEphemeral *gLiveValues = nsnull;
162 static struct LiveEphemeral *gLiveProperties = nsnull;
163 static struct LiveEphemeral *gLiveContexts = nsnull;
164 static struct LiveEphemeral *gLiveStackFrames = nsnull;
166 /*******************************************************************************
167 * utility functions for ephemeral lists
168 *******************************************************************************/
169 already_AddRefed<jsdIEphemeral>
170 jsds_FindEphemeral (LiveEphemeral **listHead, void *key)
172 if (!*listHead)
173 return nsnull;
175 LiveEphemeral *lv_record =
176 reinterpret_cast<LiveEphemeral *>
177 (PR_NEXT_LINK(&(*listHead)->links));
180 if (lv_record->key == key)
182 NS_IF_ADDREF(lv_record->value);
183 return lv_record->value;
185 lv_record = reinterpret_cast<LiveEphemeral *>
186 (PR_NEXT_LINK(&lv_record->links));
188 while (lv_record != *listHead);
190 return nsnull;
193 void
194 jsds_InvalidateAllEphemerals (LiveEphemeral **listHead)
196 LiveEphemeral *lv_record =
197 reinterpret_cast<LiveEphemeral *>
198 (PR_NEXT_LINK(&(*listHead)->links));
199 while (*listHead)
201 LiveEphemeral *next =
202 reinterpret_cast<LiveEphemeral *>
203 (PR_NEXT_LINK(&lv_record->links));
204 lv_record->value->Invalidate();
205 lv_record = next;
209 void
210 jsds_InsertEphemeral (LiveEphemeral **listHead, LiveEphemeral *item)
212 if (*listHead) {
213 /* if the list exists, add to it */
214 PR_APPEND_LINK(&item->links, &(*listHead)->links);
215 } else {
216 /* otherwise create the list */
217 PR_INIT_CLIST(&item->links);
218 *listHead = item;
222 void
223 jsds_RemoveEphemeral (LiveEphemeral **listHead, LiveEphemeral *item)
225 LiveEphemeral *next = reinterpret_cast<LiveEphemeral *>
226 (PR_NEXT_LINK(&item->links));
228 if (next == item)
230 /* if the current item is also the next item, we're the only element,
231 * null out the list head */
232 NS_ASSERTION (*listHead == item,
233 "How could we not be the head of a one item list?");
234 *listHead = nsnull;
236 else if (item == *listHead)
238 /* otherwise, if we're currently the list head, change it */
239 *listHead = next;
242 PR_REMOVE_AND_INIT_LINK(&item->links);
245 /*******************************************************************************
246 * utility functions for filters
247 *******************************************************************************/
248 void
249 jsds_FreeFilter (FilterRecord *filter)
251 NS_IF_RELEASE (filter->filterObject);
252 if (filter->urlPattern)
253 nsMemory::Free(filter->urlPattern);
254 PR_Free (filter);
257 /* copies appropriate |filter| attributes into |rec|.
258 * False return indicates failure, the contents of |rec| will not be changed.
260 PRBool
261 jsds_SyncFilter (FilterRecord *rec, jsdIFilter *filter)
263 NS_ASSERTION (rec, "jsds_SyncFilter without rec");
264 NS_ASSERTION (filter, "jsds_SyncFilter without filter");
266 JSObject *glob_proper = nsnull;
267 nsCOMPtr<nsISupports> glob;
268 nsresult rv = filter->GetGlobalObject(getter_AddRefs(glob));
269 if (NS_FAILED(rv))
270 return PR_FALSE;
271 if (glob) {
272 nsCOMPtr<nsIScriptGlobalObject> nsiglob = do_QueryInterface(glob);
273 if (nsiglob)
274 glob_proper = nsiglob->GetGlobalJSObject();
277 PRUint32 startLine;
278 rv = filter->GetStartLine(&startLine);
279 if (NS_FAILED(rv))
280 return PR_FALSE;
282 PRUint32 endLine;
283 rv = filter->GetStartLine(&endLine);
284 if (NS_FAILED(rv))
285 return PR_FALSE;
287 char *urlPattern;
288 rv = filter->GetUrlPattern (&urlPattern);
289 if (NS_FAILED(rv))
290 return PR_FALSE;
292 if (urlPattern) {
293 PRUint32 len = PL_strlen(urlPattern);
294 if (urlPattern[0] == '*') {
295 /* pattern starts with a *, shift all chars once to the left,
296 * including the trailing null. */
297 memmove (&urlPattern[0], &urlPattern[1], len);
299 if (urlPattern[len - 2] == '*') {
300 /* pattern is in the format "*foo*", overwrite the final * with
301 * a null. */
302 urlPattern[len - 2] = '\0';
303 rec->patternType = ptContains;
304 rec->patternLength = len - 2;
305 } else {
306 /* pattern is in the format "*foo", just make a note of the
307 * new length. */
308 rec->patternType = ptEndsWith;
309 rec->patternLength = len - 1;
311 } else if (urlPattern[len - 1] == '*') {
312 /* pattern is in the format "foo*", overwrite the final * with a
313 * null. */
314 urlPattern[len - 1] = '\0';
315 rec->patternType = ptStartsWith;
316 rec->patternLength = len - 1;
317 } else {
318 /* pattern is in the format "foo". */
319 rec->patternType = ptEquals;
320 rec->patternLength = len;
322 } else {
323 rec->patternType = ptIgnore;
324 rec->patternLength = 0;
327 /* we got everything we need without failing, now copy it into rec. */
329 if (rec->filterObject != filter) {
330 NS_IF_RELEASE(rec->filterObject);
331 NS_ADDREF(filter);
332 rec->filterObject = filter;
335 rec->glob = glob_proper;
337 rec->startLine = startLine;
338 rec->endLine = endLine;
340 if (rec->urlPattern)
341 nsMemory::Free (rec->urlPattern);
342 rec->urlPattern = urlPattern;
344 return PR_TRUE;
348 FilterRecord *
349 jsds_FindFilter (jsdIFilter *filter)
351 if (!gFilters)
352 return nsnull;
354 FilterRecord *current = gFilters;
356 do {
357 if (current->filterObject == filter)
358 return current;
359 current = reinterpret_cast<FilterRecord *>
360 (PR_NEXT_LINK(&current->links));
361 } while (current != gFilters);
363 return nsnull;
366 /* returns true if the hook should be executed. */
367 PRBool
368 jsds_FilterHook (JSDContext *jsdc, JSDThreadState *state)
370 JSContext *cx = JSD_GetJSContext (jsdc, state);
371 void *glob = static_cast<void *>(JS_GetGlobalObject (cx));
373 if (!glob) {
374 NS_WARNING("No global in threadstate");
375 return PR_FALSE;
378 JSDStackFrameInfo *frame = JSD_GetStackFrame (jsdc, state);
380 if (!frame) {
381 NS_WARNING("No frame in threadstate");
382 return PR_FALSE;
385 JSDScript *script = JSD_GetScriptForStackFrame (jsdc, state, frame);
386 if (!script)
387 return PR_TRUE;
389 jsuint pc = JSD_GetPCForStackFrame (jsdc, state, frame);
391 const char *url = JSD_GetScriptFilename (jsdc, script);
392 if (!url) {
393 NS_WARNING ("Script with no filename");
394 return PR_FALSE;
397 if (!gFilters)
398 return PR_TRUE;
400 PRUint32 currentLine = JSD_GetClosestLine (jsdc, script, pc);
401 PRUint32 len = 0;
402 FilterRecord *currentFilter = gFilters;
403 do {
404 PRUint32 flags = 0;
405 nsresult rv = currentFilter->filterObject->GetFlags(&flags);
406 NS_ASSERTION(NS_SUCCEEDED(rv), "Error getting flags for filter");
407 if (flags & jsdIFilter::FLAG_ENABLED) {
408 /* if there is no glob, or the globs match */
409 if ((!currentFilter->glob || currentFilter->glob == glob) &&
410 /* and there is no start line, or the start line is before
411 * or equal to the current */
412 (!currentFilter->startLine ||
413 currentFilter->startLine <= currentLine) &&
414 /* and there is no end line, or the end line is after
415 * or equal to the current */
416 (!currentFilter->endLine ||
417 currentFilter->endLine >= currentLine)) {
418 /* then we're going to have to compare the url. */
419 if (currentFilter->patternType == ptIgnore)
420 return flags & jsdIFilter::FLAG_PASS;
422 if (!len)
423 len = PL_strlen(url);
425 if (len >= currentFilter->patternLength) {
426 switch (currentFilter->patternType) {
427 case ptEquals:
428 if (!PL_strcmp(currentFilter->urlPattern, url))
429 return flags & jsdIFilter::FLAG_PASS;
430 break;
431 case ptStartsWith:
432 if (!PL_strncmp(currentFilter->urlPattern, url,
433 currentFilter->patternLength))
434 return flags & jsdIFilter::FLAG_PASS;
435 break;
436 case ptEndsWith:
437 if (!PL_strcmp(currentFilter->urlPattern,
438 &url[len -
439 currentFilter->patternLength]))
440 return flags & jsdIFilter::FLAG_PASS;
441 break;
442 case ptContains:
443 if (PL_strstr(url, currentFilter->urlPattern))
444 return flags & jsdIFilter::FLAG_PASS;
445 break;
446 default:
447 NS_ASSERTION(0, "Invalid pattern type");
452 currentFilter = reinterpret_cast<FilterRecord *>
453 (PR_NEXT_LINK(&currentFilter->links));
454 } while (currentFilter != gFilters);
456 return PR_TRUE;
460 /*******************************************************************************
461 * c callbacks
462 *******************************************************************************/
464 JS_STATIC_DLL_CALLBACK (void)
465 jsds_NotifyPendingDeadScripts (JSContext *cx)
467 #ifdef CAUTIOUS_SCRIPTHOOK
468 JSRuntime *rt = JS_GetRuntime(cx);
469 #endif
470 jsdService *jsds = gJsds;
472 nsCOMPtr<jsdIScriptHook> hook;
473 if (jsds) {
474 NS_ADDREF(jsds);
475 jsds->GetScriptHook (getter_AddRefs(hook));
476 jsds->Pause(nsnull);
479 DeadScript *deadScripts = gDeadScripts;
480 gDeadScripts = nsnull;
481 while (deadScripts) {
482 DeadScript *ds = deadScripts;
483 /* get next deleted script */
484 deadScripts = reinterpret_cast<DeadScript *>
485 (PR_NEXT_LINK(&ds->links));
486 if (deadScripts == ds)
487 deadScripts = nsnull;
489 if (hook)
491 /* tell the user this script has been destroyed */
492 #ifdef CAUTIOUS_SCRIPTHOOK
493 JS_UNKEEP_ATOMS(rt);
494 #endif
495 hook->OnScriptDestroyed (ds->script);
496 #ifdef CAUTIOUS_SCRIPTHOOK
497 JS_KEEP_ATOMS(rt);
498 #endif
501 /* take it out of the circular list */
502 PR_REMOVE_LINK(&ds->links);
504 /* addref came from the FromPtr call in jsds_ScriptHookProc */
505 NS_RELEASE(ds->script);
506 /* free the struct! */
507 PR_Free(ds);
510 if (jsds) {
511 jsds->UnPause(nsnull);
512 NS_RELEASE(jsds);
516 JS_STATIC_DLL_CALLBACK (JSBool)
517 jsds_GCCallbackProc (JSContext *cx, JSGCStatus status)
519 #ifdef DEBUG_verbose
520 printf ("new gc status is %i\n", status);
521 #endif
522 if (status == JSGC_END) {
523 /* just to guard against reentering. */
524 gGCStatus = JSGC_BEGIN;
525 while (gDeadScripts)
526 jsds_NotifyPendingDeadScripts (cx);
529 gGCStatus = status;
530 if (gLastGCProc)
531 return gLastGCProc (cx, status);
533 return JS_TRUE;
536 JS_STATIC_DLL_CALLBACK (uintN)
537 jsds_ErrorHookProc (JSDContext *jsdc, JSContext *cx, const char *message,
538 JSErrorReport *report, void *callerdata)
540 static PRBool running = PR_FALSE;
542 nsCOMPtr<jsdIErrorHook> hook;
543 gJsds->GetErrorHook(getter_AddRefs(hook));
544 if (!hook)
545 return JSD_ERROR_REPORTER_PASS_ALONG;
547 if (running)
548 return JSD_ERROR_REPORTER_PASS_ALONG;
550 running = PR_TRUE;
552 nsCOMPtr<jsdIValue> val;
553 if (JS_IsExceptionPending(cx)) {
554 jsval jv;
555 JS_GetPendingException(cx, &jv);
556 JSDValue *jsdv = JSD_NewValue (jsdc, jv);
557 val = getter_AddRefs(jsdValue::FromPtr(jsdc, jsdv));
560 const char *fileName;
561 PRUint32 line;
562 PRUint32 pos;
563 PRUint32 flags;
564 PRUint32 errnum;
565 PRBool rval;
566 if (report) {
567 fileName = report->filename;
568 line = report->lineno;
569 pos = report->tokenptr - report->linebuf;
570 flags = report->flags;
571 errnum = report->errorNumber;
573 else
575 fileName = 0;
576 line = 0;
577 pos = 0;
578 flags = 0;
579 errnum = 0;
582 gJsds->Pause(nsnull);
583 hook->OnError (message, fileName, line, pos, flags, errnum, val, &rval);
584 gJsds->UnPause(nsnull);
586 running = PR_FALSE;
587 if (!rval)
588 return JSD_ERROR_REPORTER_DEBUG;
590 return JSD_ERROR_REPORTER_PASS_ALONG;
593 JS_STATIC_DLL_CALLBACK (JSBool)
594 jsds_CallHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate,
595 uintN type, void* callerdata)
597 nsCOMPtr<jsdICallHook> hook;
599 switch (type)
601 case JSD_HOOK_TOPLEVEL_START:
602 case JSD_HOOK_TOPLEVEL_END:
603 gJsds->GetTopLevelHook(getter_AddRefs(hook));
604 break;
606 case JSD_HOOK_FUNCTION_CALL:
607 case JSD_HOOK_FUNCTION_RETURN:
608 gJsds->GetFunctionHook(getter_AddRefs(hook));
609 break;
611 default:
612 NS_ASSERTION (0, "Unknown hook type.");
615 if (!hook)
616 return JS_TRUE;
618 if (!jsds_FilterHook (jsdc, jsdthreadstate))
619 return JS_FALSE;
621 JSDStackFrameInfo *native_frame = JSD_GetStackFrame (jsdc, jsdthreadstate);
622 nsCOMPtr<jsdIStackFrame> frame =
623 getter_AddRefs(jsdStackFrame::FromPtr(jsdc, jsdthreadstate,
624 native_frame));
625 gJsds->Pause(nsnull);
626 hook->OnCall(frame, type);
627 gJsds->UnPause(nsnull);
628 jsdStackFrame::InvalidateAll();
630 return JS_TRUE;
633 JS_STATIC_DLL_CALLBACK (PRUint32)
634 jsds_ExecutionHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate,
635 uintN type, void* callerdata, jsval* rval)
637 nsCOMPtr<jsdIExecutionHook> hook(0);
638 PRUint32 hook_rv = JSD_HOOK_RETURN_CONTINUE;
639 nsCOMPtr<jsdIValue> js_rv;
641 switch (type)
643 case JSD_HOOK_INTERRUPTED:
644 gJsds->GetInterruptHook(getter_AddRefs(hook));
645 break;
646 case JSD_HOOK_DEBUG_REQUESTED:
647 gJsds->GetDebugHook(getter_AddRefs(hook));
648 break;
649 case JSD_HOOK_DEBUGGER_KEYWORD:
650 gJsds->GetDebuggerHook(getter_AddRefs(hook));
651 break;
652 case JSD_HOOK_BREAKPOINT:
654 /* we can't pause breakpoints the way we pause the other
655 * execution hooks (at least, not easily.) Instead we bail
656 * here if the service is paused. */
657 PRUint32 level;
658 gJsds->GetPauseDepth(&level);
659 if (!level)
660 gJsds->GetBreakpointHook(getter_AddRefs(hook));
662 break;
663 case JSD_HOOK_THROW:
665 hook_rv = JSD_HOOK_RETURN_CONTINUE_THROW;
666 gJsds->GetThrowHook(getter_AddRefs(hook));
667 if (hook) {
668 JSDValue *jsdv = JSD_GetException (jsdc, jsdthreadstate);
669 js_rv = getter_AddRefs(jsdValue::FromPtr (jsdc, jsdv));
671 break;
673 default:
674 NS_ASSERTION (0, "Unknown hook type.");
677 if (!hook)
678 return hook_rv;
680 if (!jsds_FilterHook (jsdc, jsdthreadstate))
681 return JSD_HOOK_RETURN_CONTINUE;
683 JSDStackFrameInfo *native_frame = JSD_GetStackFrame (jsdc, jsdthreadstate);
684 nsCOMPtr<jsdIStackFrame> frame =
685 getter_AddRefs(jsdStackFrame::FromPtr(jsdc, jsdthreadstate,
686 native_frame));
687 gJsds->Pause(nsnull);
688 jsdIValue *inout_rv = js_rv;
689 NS_IF_ADDREF(inout_rv);
690 hook->OnExecute (frame, type, &inout_rv, &hook_rv);
691 js_rv = inout_rv;
692 NS_IF_RELEASE(inout_rv);
693 gJsds->UnPause(nsnull);
694 jsdStackFrame::InvalidateAll();
696 if (hook_rv == JSD_HOOK_RETURN_RET_WITH_VAL ||
697 hook_rv == JSD_HOOK_RETURN_THROW_WITH_VAL) {
698 *rval = JSVAL_VOID;
699 if (js_rv) {
700 JSDValue *jsdv;
701 if (NS_SUCCEEDED(js_rv->GetJSDValue (&jsdv)))
702 *rval = JSD_GetValueWrappedJSVal(jsdc, jsdv);
706 return hook_rv;
709 JS_STATIC_DLL_CALLBACK (void)
710 jsds_ScriptHookProc (JSDContext* jsdc, JSDScript* jsdscript, JSBool creating,
711 void* callerdata)
713 #ifdef CAUTIOUS_SCRIPTHOOK
714 JSContext *cx = JSD_GetDefaultJSContext(jsdc);
715 JSRuntime *rt = JS_GetRuntime(cx);
716 #endif
718 nsCOMPtr<jsdIScriptHook> hook;
719 gJsds->GetScriptHook (getter_AddRefs(hook));
721 if (creating) {
722 /* a script is being created */
723 if (!hook) {
724 /* nobody cares, just exit */
725 return;
728 nsCOMPtr<jsdIScript> script =
729 getter_AddRefs(jsdScript::FromPtr(jsdc, jsdscript));
730 #ifdef CAUTIOUS_SCRIPTHOOK
731 JS_UNKEEP_ATOMS(rt);
732 #endif
733 gJsds->Pause(nsnull);
734 hook->OnScriptCreated (script);
735 gJsds->UnPause(nsnull);
736 #ifdef CAUTIOUS_SCRIPTHOOK
737 JS_KEEP_ATOMS(rt);
738 #endif
739 } else {
740 /* a script is being destroyed. even if there is no registered hook
741 * we'll still need to invalidate the jsdIScript record, in order
742 * to remove the reference held in the JSDScript private data. */
743 nsCOMPtr<jsdIScript> jsdis =
744 static_cast<jsdIScript *>(JSD_GetScriptPrivate(jsdscript));
745 if (!jsdis)
746 return;
748 jsdis->Invalidate();
749 if (!hook)
750 return;
752 if (gGCStatus == JSGC_END) {
753 /* if GC *isn't* running, we can tell the user about the script
754 * delete now. */
755 #ifdef CAUTIOUS_SCRIPTHOOK
756 JS_UNKEEP_ATOMS(rt);
757 #endif
759 gJsds->Pause(nsnull);
760 hook->OnScriptDestroyed (jsdis);
761 gJsds->UnPause(nsnull);
762 #ifdef CAUTIOUS_SCRIPTHOOK
763 JS_KEEP_ATOMS(rt);
764 #endif
765 } else {
766 /* if a GC *is* running, we've got to wait until it's done before
767 * we can execute any JS, so we queue the notification in a PRCList
768 * until GC tells us it's done. See jsds_GCCallbackProc(). */
769 DeadScript *ds = PR_NEW(DeadScript);
770 if (!ds)
771 return; /* NS_ERROR_OUT_OF_MEMORY */
773 ds->jsdc = jsdc;
774 ds->script = jsdis;
775 NS_ADDREF(ds->script);
776 if (gDeadScripts)
777 /* if the queue exists, add to it */
778 PR_APPEND_LINK(&ds->links, &gDeadScripts->links);
779 else {
780 /* otherwise create the queue */
781 PR_INIT_CLIST(&ds->links);
782 gDeadScripts = ds;
788 /*******************************************************************************
789 * reflected jsd data structures
790 *******************************************************************************/
792 /* Contexts */
794 NS_IMPL_THREADSAFE_ISUPPORTS2(jsdContext, jsdIContext, jsdIEphemeral);
796 NS_IMETHODIMP
797 jsdContext::GetJSDContext(JSDContext **_rval)
799 *_rval = mCx;
800 return NS_OK;
804 /* Objects */
805 NS_IMPL_THREADSAFE_ISUPPORTS1(jsdObject, jsdIObject)
807 NS_IMETHODIMP
808 jsdObject::GetJSDContext(JSDContext **_rval)
810 *_rval = mCx;
811 return NS_OK;
814 NS_IMETHODIMP
815 jsdObject::GetJSDObject(JSDObject **_rval)
817 *_rval = mObject;
818 return NS_OK;
821 NS_IMETHODIMP
822 jsdObject::GetCreatorURL(char **_rval)
824 const char *url = JSD_GetObjectNewURL(mCx, mObject);
825 if (url) {
826 *_rval = PL_strdup(url);
827 if (!*_rval)
828 return NS_ERROR_OUT_OF_MEMORY;
829 } else {
830 *_rval = nsnull;
832 return NS_OK;
835 NS_IMETHODIMP
836 jsdObject::GetCreatorLine(PRUint32 *_rval)
838 *_rval = JSD_GetObjectNewLineNumber(mCx, mObject);
839 return NS_OK;
842 NS_IMETHODIMP
843 jsdObject::GetConstructorURL(char **_rval)
845 const char *url = JSD_GetObjectConstructorURL(mCx, mObject);
846 if (url) {
847 *_rval = PL_strdup(url);
848 if (!*_rval)
849 return NS_ERROR_OUT_OF_MEMORY;
850 } else {
851 *_rval = nsnull;
853 return NS_OK;
856 NS_IMETHODIMP
857 jsdObject::GetConstructorLine(PRUint32 *_rval)
859 *_rval = JSD_GetObjectConstructorLineNumber(mCx, mObject);
860 return NS_OK;
863 NS_IMETHODIMP
864 jsdObject::GetValue(jsdIValue **_rval)
866 JSDValue *jsdv = JSD_GetValueForObject (mCx, mObject);
868 *_rval = jsdValue::FromPtr (mCx, jsdv);
869 return NS_OK;
872 /* Properties */
873 NS_IMPL_THREADSAFE_ISUPPORTS2(jsdProperty, jsdIProperty, jsdIEphemeral)
875 jsdProperty::jsdProperty (JSDContext *aCx, JSDProperty *aProperty) :
876 mCx(aCx), mProperty(aProperty)
878 DEBUG_CREATE ("jsdProperty", gPropertyCount);
879 mValid = (aCx && aProperty);
880 mLiveListEntry.value = this;
881 jsds_InsertEphemeral (&gLiveProperties, &mLiveListEntry);
884 jsdProperty::~jsdProperty ()
886 DEBUG_DESTROY ("jsdProperty", gPropertyCount);
887 if (mValid)
888 Invalidate();
891 NS_IMETHODIMP
892 jsdProperty::Invalidate()
894 ASSERT_VALID_EPHEMERAL;
895 mValid = PR_FALSE;
896 jsds_RemoveEphemeral (&gLiveProperties, &mLiveListEntry);
897 JSD_DropProperty (mCx, mProperty);
898 return NS_OK;
901 void
902 jsdProperty::InvalidateAll()
904 if (gLiveProperties)
905 jsds_InvalidateAllEphemerals (&gLiveProperties);
908 NS_IMETHODIMP
909 jsdProperty::GetJSDContext(JSDContext **_rval)
911 *_rval = mCx;
912 return NS_OK;
915 NS_IMETHODIMP
916 jsdProperty::GetJSDProperty(JSDProperty **_rval)
918 *_rval = mProperty;
919 return NS_OK;
922 NS_IMETHODIMP
923 jsdProperty::GetIsValid(PRBool *_rval)
925 *_rval = mValid;
926 return NS_OK;
929 NS_IMETHODIMP
930 jsdProperty::GetAlias(jsdIValue **_rval)
932 JSDValue *jsdv = JSD_GetPropertyValue (mCx, mProperty);
934 *_rval = jsdValue::FromPtr (mCx, jsdv);
935 return NS_OK;
938 NS_IMETHODIMP
939 jsdProperty::GetFlags(PRUint32 *_rval)
941 *_rval = JSD_GetPropertyFlags (mCx, mProperty);
942 return NS_OK;
945 NS_IMETHODIMP
946 jsdProperty::GetName(jsdIValue **_rval)
948 JSDValue *jsdv = JSD_GetPropertyName (mCx, mProperty);
950 *_rval = jsdValue::FromPtr (mCx, jsdv);
951 return NS_OK;
954 NS_IMETHODIMP
955 jsdProperty::GetValue(jsdIValue **_rval)
957 JSDValue *jsdv = JSD_GetPropertyValue (mCx, mProperty);
959 *_rval = jsdValue::FromPtr (mCx, jsdv);
960 return NS_OK;
963 NS_IMETHODIMP
964 jsdProperty::GetVarArgSlot(PRUint32 *_rval)
966 *_rval = JSD_GetPropertyVarArgSlot (mCx, mProperty);
967 return NS_OK;
970 /* Scripts */
971 NS_IMPL_THREADSAFE_ISUPPORTS2(jsdScript, jsdIScript, jsdIEphemeral)
973 jsdScript::jsdScript (JSDContext *aCx, JSDScript *aScript) : mValid(PR_FALSE),
974 mTag(0),
975 mCx(aCx),
976 mScript(aScript),
977 mFileName(0),
978 mFunctionName(0),
979 mBaseLineNumber(0),
980 mLineExtent(0),
981 mPPLineMap(0),
982 mFirstPC(0)
984 DEBUG_CREATE ("jsdScript", gScriptCount);
986 if (mScript) {
987 /* copy the script's information now, so we have it later, when it
988 * gets destroyed. */
989 JSD_LockScriptSubsystem(mCx);
990 mFileName = new nsCString(JSD_GetScriptFilename(mCx, mScript));
991 mFunctionName =
992 new nsCString(JSD_GetScriptFunctionName(mCx, mScript));
993 mBaseLineNumber = JSD_GetScriptBaseLineNumber(mCx, mScript);
994 mLineExtent = JSD_GetScriptLineExtent(mCx, mScript);
995 mFirstPC = JSD_GetClosestPC(mCx, mScript, 0);
996 JSD_UnlockScriptSubsystem(mCx);
998 mValid = PR_TRUE;
1002 jsdScript::~jsdScript ()
1004 DEBUG_DESTROY ("jsdScript", gScriptCount);
1005 if (mFileName)
1006 delete mFileName;
1007 if (mFunctionName)
1008 delete mFunctionName;
1010 if (mPPLineMap)
1011 PR_Free(mPPLineMap);
1013 /* Invalidate() needs to be called to release an owning reference to
1014 * ourselves, so if we got here without being invalidated, something
1015 * has gone wrong with our ref count. */
1016 NS_ASSERTION (!mValid, "Script destroyed without being invalidated.");
1020 * This method populates a line <-> pc map for a pretty printed version of this
1021 * script. It does this by decompiling, and then recompiling the script. The
1022 * resulting script is scanned for the line map, and then left as GC fodder.
1024 PCMapEntry *
1025 jsdScript::CreatePPLineMap()
1027 JSContext *cx = JSD_GetDefaultJSContext (mCx);
1028 JSAutoRequest ar(cx);
1029 JSObject *obj = JS_NewObject(cx, NULL, NULL, NULL);
1030 JSFunction *fun = JSD_GetJSFunction (mCx, mScript);
1031 JSScript *script;
1032 PRUint32 baseLine;
1033 PRBool scriptOwner = PR_FALSE;
1035 if (fun) {
1036 if (fun->nargs > 12)
1037 return nsnull;
1038 JSString *jsstr = JS_DecompileFunctionBody (cx, fun, 4);
1039 if (!jsstr)
1040 return nsnull;
1042 const char *argnames[] = {"arg1", "arg2", "arg3", "arg4",
1043 "arg5", "arg6", "arg7", "arg8",
1044 "arg9", "arg10", "arg11", "arg12" };
1045 fun = JS_CompileUCFunction (cx, obj, "ppfun", fun->nargs, argnames,
1046 JS_GetStringChars(jsstr),
1047 JS_GetStringLength(jsstr),
1048 "x-jsd:ppbuffer?type=function", 3);
1049 if (!fun || !(script = JS_GetFunctionScript(cx, fun)))
1050 return nsnull;
1051 baseLine = 3;
1052 } else {
1053 JSString *jsstr = JS_DecompileScript (cx, JSD_GetJSScript(mCx, mScript),
1054 "ppscript", 4);
1055 if (!jsstr)
1056 return nsnull;
1058 script = JS_CompileUCScript (cx, obj,
1059 JS_GetStringChars(jsstr),
1060 JS_GetStringLength(jsstr),
1061 "x-jsd:ppbuffer?type=script", 1);
1062 if (!script)
1063 return nsnull;
1064 scriptOwner = PR_TRUE;
1065 baseLine = 1;
1068 PRUint32 scriptExtent = JS_GetScriptLineExtent (cx, script);
1069 jsbytecode* firstPC = JS_LineNumberToPC (cx, script, 0);
1070 /* allocate worst case size of map (number of lines in script + 1
1071 * for our 0 record), we'll shrink it with a realloc later. */
1072 mPPLineMap =
1073 static_cast<PCMapEntry *>
1074 (PR_Malloc((scriptExtent + 1) * sizeof (PCMapEntry)));
1075 if (mPPLineMap) {
1076 mPCMapSize = 0;
1077 for (PRUint32 line = baseLine; line < scriptExtent + baseLine; ++line) {
1078 jsbytecode* pc = JS_LineNumberToPC (cx, script, line);
1079 if (line == JS_PCToLineNumber (cx, script, pc)) {
1080 mPPLineMap[mPCMapSize].line = line;
1081 mPPLineMap[mPCMapSize].pc = pc - firstPC;
1082 ++mPCMapSize;
1085 if (scriptExtent != mPCMapSize) {
1086 mPPLineMap =
1087 static_cast<PCMapEntry *>
1088 (PR_Realloc(mPPLineMap,
1089 mPCMapSize * sizeof(PCMapEntry)));
1093 if (scriptOwner)
1094 JS_DestroyScript (cx, script);
1096 return mPPLineMap;
1099 PRUint32
1100 jsdScript::PPPcToLine (PRUint32 aPC)
1102 if (!mPPLineMap && !CreatePPLineMap())
1103 return 0;
1104 PRUint32 i;
1105 for (i = 1; i < mPCMapSize; ++i) {
1106 if (mPPLineMap[i].pc > aPC)
1107 return mPPLineMap[i - 1].line;
1110 return mPPLineMap[mPCMapSize - 1].line;
1113 PRUint32
1114 jsdScript::PPLineToPc (PRUint32 aLine)
1116 if (!mPPLineMap && !CreatePPLineMap())
1117 return 0;
1118 PRUint32 i;
1119 for (i = 1; i < mPCMapSize; ++i) {
1120 if (mPPLineMap[i].line > aLine)
1121 return mPPLineMap[i - 1].pc;
1124 return mPPLineMap[mPCMapSize - 1].pc;
1127 NS_IMETHODIMP
1128 jsdScript::GetJSDContext(JSDContext **_rval)
1130 ASSERT_VALID_EPHEMERAL;
1131 *_rval = mCx;
1132 return NS_OK;
1135 NS_IMETHODIMP
1136 jsdScript::GetJSDScript(JSDScript **_rval)
1138 ASSERT_VALID_EPHEMERAL;
1139 *_rval = mScript;
1140 return NS_OK;
1143 NS_IMETHODIMP
1144 jsdScript::GetVersion (PRInt32 *_rval)
1146 ASSERT_VALID_EPHEMERAL;
1147 JSContext *cx = JSD_GetDefaultJSContext (mCx);
1148 JSScript *script = JSD_GetJSScript(mCx, mScript);
1149 *_rval = static_cast<PRInt32>(JS_GetScriptVersion(cx, script));
1150 return NS_OK;
1153 NS_IMETHODIMP
1154 jsdScript::GetTag(PRUint32 *_rval)
1156 if (!mTag)
1157 mTag = ++jsdScript::LastTag;
1159 *_rval = mTag;
1160 return NS_OK;
1163 NS_IMETHODIMP
1164 jsdScript::Invalidate()
1166 ASSERT_VALID_EPHEMERAL;
1167 mValid = PR_FALSE;
1169 /* release the addref we do in FromPtr */
1170 jsdIScript *script = static_cast<jsdIScript *>
1171 (JSD_GetScriptPrivate(mScript));
1172 NS_ASSERTION (script == this, "That's not my script!");
1173 NS_RELEASE(script);
1174 JSD_SetScriptPrivate(mScript, NULL);
1175 return NS_OK;
1178 void
1179 jsdScript::InvalidateAll ()
1181 JSDContext *cx;
1182 if (NS_FAILED(gJsds->GetJSDContext (&cx)))
1183 return;
1185 JSDScript *script;
1186 JSDScript *iter = NULL;
1188 JSD_LockScriptSubsystem(cx);
1189 while((script = JSD_IterateScripts(cx, &iter)) != NULL) {
1190 nsCOMPtr<jsdIScript> jsdis =
1191 static_cast<jsdIScript *>(JSD_GetScriptPrivate(script));
1192 if (jsdis)
1193 jsdis->Invalidate();
1195 JSD_UnlockScriptSubsystem(cx);
1198 NS_IMETHODIMP
1199 jsdScript::GetIsValid(PRBool *_rval)
1201 *_rval = mValid;
1202 return NS_OK;
1205 NS_IMETHODIMP
1206 jsdScript::SetFlags(PRUint32 flags)
1208 ASSERT_VALID_EPHEMERAL;
1209 JSD_SetScriptFlags(mCx, mScript, flags);
1210 return NS_OK;
1213 NS_IMETHODIMP
1214 jsdScript::GetFlags(PRUint32 *_rval)
1216 ASSERT_VALID_EPHEMERAL;
1217 *_rval = JSD_GetScriptFlags(mCx, mScript);
1218 return NS_OK;
1221 NS_IMETHODIMP
1222 jsdScript::GetFileName(char **_rval)
1224 *_rval = ToNewCString(*mFileName);
1225 if (!*_rval)
1226 return NS_ERROR_OUT_OF_MEMORY;
1227 return NS_OK;
1230 NS_IMETHODIMP
1231 jsdScript::GetFunctionName(char **_rval)
1233 *_rval = ToNewCString(*mFunctionName);
1234 if (!*_rval)
1235 return NS_ERROR_OUT_OF_MEMORY;
1236 return NS_OK;
1239 NS_IMETHODIMP
1240 jsdScript::GetFunctionObject(jsdIValue **_rval)
1242 JSFunction *fun = JSD_GetJSFunction(mCx, mScript);
1243 if (!fun)
1244 return NS_ERROR_NOT_AVAILABLE;
1246 JSObject *obj = JS_GetFunctionObject(fun);
1247 if (!obj)
1248 return NS_ERROR_FAILURE;
1250 JSDContext *cx;
1251 if (NS_FAILED(gJsds->GetJSDContext (&cx)))
1252 return NS_ERROR_NOT_INITIALIZED;
1254 JSDValue *jsdv = JSD_NewValue(cx, OBJECT_TO_JSVAL(obj));
1255 if (!jsdv)
1256 return NS_ERROR_OUT_OF_MEMORY;
1258 *_rval = jsdValue::FromPtr(cx, jsdv);
1259 if (!*_rval) {
1260 JSD_DropValue(cx, jsdv);
1261 return NS_ERROR_OUT_OF_MEMORY;
1264 return NS_OK;
1267 NS_IMETHODIMP
1268 jsdScript::GetFunctionSource(nsAString & aFunctionSource)
1270 ASSERT_VALID_EPHEMERAL;
1271 JSContext *cx = JSD_GetDefaultJSContext (mCx);
1272 if (!cx) {
1273 NS_WARNING("No default context !?");
1274 return NS_ERROR_FAILURE;
1276 JSFunction *fun = JSD_GetJSFunction (mCx, mScript);
1278 JSAutoRequest ar(cx);
1280 JSString *jsstr;
1281 if (fun)
1282 jsstr = JS_DecompileFunction (cx, fun, 4);
1283 else {
1284 JSScript *script = JSD_GetJSScript (mCx, mScript);
1285 jsstr = JS_DecompileScript (cx, script, "ppscript", 4);
1287 if (!jsstr)
1288 return NS_ERROR_FAILURE;
1290 aFunctionSource = reinterpret_cast<PRUnichar*>(JS_GetStringChars(jsstr));
1291 return NS_OK;
1294 NS_IMETHODIMP
1295 jsdScript::GetBaseLineNumber(PRUint32 *_rval)
1297 *_rval = mBaseLineNumber;
1298 return NS_OK;
1301 NS_IMETHODIMP
1302 jsdScript::GetLineExtent(PRUint32 *_rval)
1304 *_rval = mLineExtent;
1305 return NS_OK;
1308 NS_IMETHODIMP
1309 jsdScript::GetCallCount(PRUint32 *_rval)
1311 ASSERT_VALID_EPHEMERAL;
1312 *_rval = JSD_GetScriptCallCount (mCx, mScript);
1313 return NS_OK;
1316 NS_IMETHODIMP
1317 jsdScript::GetMaxRecurseDepth(PRUint32 *_rval)
1319 ASSERT_VALID_EPHEMERAL;
1320 *_rval = JSD_GetScriptMaxRecurseDepth (mCx, mScript);
1321 return NS_OK;
1324 NS_IMETHODIMP
1325 jsdScript::GetMinExecutionTime(double *_rval)
1327 ASSERT_VALID_EPHEMERAL;
1328 *_rval = JSD_GetScriptMinExecutionTime (mCx, mScript);
1329 return NS_OK;
1332 NS_IMETHODIMP
1333 jsdScript::GetMaxExecutionTime(double *_rval)
1335 ASSERT_VALID_EPHEMERAL;
1336 *_rval = JSD_GetScriptMaxExecutionTime (mCx, mScript);
1337 return NS_OK;
1340 NS_IMETHODIMP
1341 jsdScript::GetTotalExecutionTime(double *_rval)
1343 ASSERT_VALID_EPHEMERAL;
1344 *_rval = JSD_GetScriptTotalExecutionTime (mCx, mScript);
1345 return NS_OK;
1348 NS_IMETHODIMP
1349 jsdScript::GetMinOwnExecutionTime(double *_rval)
1351 ASSERT_VALID_EPHEMERAL;
1352 *_rval = JSD_GetScriptMinOwnExecutionTime (mCx, mScript);
1353 return NS_OK;
1356 NS_IMETHODIMP
1357 jsdScript::GetMaxOwnExecutionTime(double *_rval)
1359 ASSERT_VALID_EPHEMERAL;
1360 *_rval = JSD_GetScriptMaxOwnExecutionTime (mCx, mScript);
1361 return NS_OK;
1364 NS_IMETHODIMP
1365 jsdScript::GetTotalOwnExecutionTime(double *_rval)
1367 ASSERT_VALID_EPHEMERAL;
1368 *_rval = JSD_GetScriptTotalOwnExecutionTime (mCx, mScript);
1369 return NS_OK;
1372 NS_IMETHODIMP
1373 jsdScript::ClearProfileData()
1375 ASSERT_VALID_EPHEMERAL;
1376 JSD_ClearScriptProfileData(mCx, mScript);
1377 return NS_OK;
1380 NS_IMETHODIMP
1381 jsdScript::PcToLine(PRUint32 aPC, PRUint32 aPcmap, PRUint32 *_rval)
1383 ASSERT_VALID_EPHEMERAL;
1384 if (aPcmap == PCMAP_SOURCETEXT) {
1385 *_rval = JSD_GetClosestLine (mCx, mScript, mFirstPC + aPC);
1386 } else if (aPcmap == PCMAP_PRETTYPRINT) {
1387 *_rval = PPPcToLine(aPC);
1388 } else {
1389 return NS_ERROR_INVALID_ARG;
1392 return NS_OK;
1395 NS_IMETHODIMP
1396 jsdScript::LineToPc(PRUint32 aLine, PRUint32 aPcmap, PRUint32 *_rval)
1398 ASSERT_VALID_EPHEMERAL;
1399 if (aPcmap == PCMAP_SOURCETEXT) {
1400 jsuword pc = JSD_GetClosestPC (mCx, mScript, aLine);
1401 *_rval = pc - mFirstPC;
1402 } else if (aPcmap == PCMAP_PRETTYPRINT) {
1403 *_rval = PPLineToPc(aLine);
1404 } else {
1405 return NS_ERROR_INVALID_ARG;
1408 return NS_OK;
1411 NS_IMETHODIMP
1412 jsdScript::IsLineExecutable(PRUint32 aLine, PRUint32 aPcmap, PRBool *_rval)
1414 ASSERT_VALID_EPHEMERAL;
1415 if (aPcmap == PCMAP_SOURCETEXT) {
1416 jsuword pc = JSD_GetClosestPC (mCx, mScript, aLine);
1417 *_rval = (aLine == JSD_GetClosestLine (mCx, mScript, pc));
1418 } else if (aPcmap == PCMAP_PRETTYPRINT) {
1419 if (!mPPLineMap && !CreatePPLineMap())
1420 return NS_ERROR_FAILURE;
1421 *_rval = PR_FALSE;
1422 for (PRUint32 i = 0; i < mPCMapSize; ++i) {
1423 if (mPPLineMap[i].line >= aLine) {
1424 *_rval = (mPPLineMap[i].line == aLine);
1425 break;
1428 } else {
1429 return NS_ERROR_INVALID_ARG;
1432 return NS_OK;
1435 NS_IMETHODIMP
1436 jsdScript::SetBreakpoint(PRUint32 aPC)
1438 ASSERT_VALID_EPHEMERAL;
1439 jsuword pc = mFirstPC + aPC;
1440 JSD_SetExecutionHook (mCx, mScript, pc, jsds_ExecutionHookProc,
1441 reinterpret_cast<void *>(PRIVATE_TO_JSVAL(NULL)));
1442 return NS_OK;
1445 NS_IMETHODIMP
1446 jsdScript::ClearBreakpoint(PRUint32 aPC)
1448 ASSERT_VALID_EPHEMERAL;
1449 jsuword pc = mFirstPC + aPC;
1450 JSD_ClearExecutionHook (mCx, mScript, pc);
1451 return NS_OK;
1454 NS_IMETHODIMP
1455 jsdScript::ClearAllBreakpoints()
1457 ASSERT_VALID_EPHEMERAL;
1458 JSD_LockScriptSubsystem(mCx);
1459 JSD_ClearAllExecutionHooksForScript (mCx, mScript);
1460 JSD_UnlockScriptSubsystem(mCx);
1461 return NS_OK;
1464 /* Contexts */
1465 NS_IMPL_THREADSAFE_ISUPPORTS2(jsdContext, jsdIContext, jsdIEphemeral)
1467 jsdIContext *
1468 jsdContext::FromPtr (JSDContext *aJSDCx, JSContext *aJSCx)
1470 if (!aJSDCx || !aJSCx ||
1471 !(JS_GetOptions(aJSCx) & JSOPTION_PRIVATE_IS_NSISUPPORTS))
1473 return nsnull;
1476 nsCOMPtr<jsdIContext> jsdicx;
1477 nsCOMPtr<jsdIEphemeral> eph =
1478 jsds_FindEphemeral (&gLiveContexts, static_cast<void *>(aJSCx));
1479 if (eph)
1481 jsdicx = do_QueryInterface(eph);
1483 else
1485 nsCOMPtr<nsISupports> iscx =
1486 static_cast<nsISupports *>(JS_GetContextPrivate(aJSCx));
1487 if (!iscx)
1488 return nsnull;
1490 jsdicx = new jsdContext (aJSDCx, aJSCx, iscx);
1493 jsdIContext *rv = jsdicx;
1494 NS_IF_ADDREF(rv);
1495 return rv;
1498 jsdContext::jsdContext (JSDContext *aJSDCx, JSContext *aJSCx,
1499 nsISupports *aISCx) : mValid(PR_TRUE), mTag(0),
1500 mJSDCx(aJSDCx),
1501 mJSCx(aJSCx), mISCx(aISCx)
1503 DEBUG_CREATE ("jsdContext", gContextCount);
1504 mLiveListEntry.value = this;
1505 mLiveListEntry.key = static_cast<void *>(aJSCx);
1506 jsds_InsertEphemeral (&gLiveContexts, &mLiveListEntry);
1509 jsdContext::~jsdContext()
1511 DEBUG_DESTROY ("jsdContext", gContextCount);
1512 if (mValid)
1514 /* call Invalidate() to take ourselves out of the live list */
1515 Invalidate();
1519 NS_IMETHODIMP
1520 jsdContext::GetIsValid(PRBool *_rval)
1522 *_rval = mValid;
1523 return NS_OK;
1526 NS_IMETHODIMP
1527 jsdContext::Invalidate()
1529 ASSERT_VALID_EPHEMERAL;
1530 mValid = PR_FALSE;
1531 jsds_RemoveEphemeral (&gLiveContexts, &mLiveListEntry);
1532 return NS_OK;
1535 void
1536 jsdContext::InvalidateAll()
1538 if (gLiveContexts)
1539 jsds_InvalidateAllEphemerals (&gLiveContexts);
1542 NS_IMETHODIMP
1543 jsdContext::GetJSContext(JSContext **_rval)
1545 ASSERT_VALID_EPHEMERAL;
1546 *_rval = mJSCx;
1547 return NS_OK;
1550 NS_IMETHODIMP
1551 jsdContext::GetOptions(PRUint32 *_rval)
1553 ASSERT_VALID_EPHEMERAL;
1554 *_rval = JS_GetOptions(mJSCx);
1555 return NS_OK;
1558 NS_IMETHODIMP
1559 jsdContext::SetOptions(PRUint32 options)
1561 ASSERT_VALID_EPHEMERAL;
1562 PRUint32 lastOptions = JS_GetOptions(mJSCx);
1564 /* don't let users change this option, they'd just be shooting themselves
1565 * in the foot. */
1566 if ((options ^ lastOptions) & JSOPTION_PRIVATE_IS_NSISUPPORTS)
1567 return NS_ERROR_ILLEGAL_VALUE;
1569 JS_SetOptions(mJSCx, options);
1570 return NS_OK;
1573 NS_IMETHODIMP
1574 jsdContext::GetPrivateData(nsISupports **_rval)
1576 ASSERT_VALID_EPHEMERAL;
1577 PRUint32 options = JS_GetOptions(mJSCx);
1578 if (options & JSOPTION_PRIVATE_IS_NSISUPPORTS)
1580 *_rval = static_cast<nsISupports*>(JS_GetContextPrivate(mJSCx));
1581 NS_IF_ADDREF(*_rval);
1583 else
1585 *_rval = nsnull;
1588 return NS_OK;
1591 NS_IMETHODIMP
1592 jsdContext::GetWrappedContext(nsISupports **_rval)
1594 ASSERT_VALID_EPHEMERAL;
1595 *_rval = mISCx;
1596 NS_IF_ADDREF(*_rval);
1597 return NS_OK;
1600 NS_IMETHODIMP
1601 jsdContext::GetTag(PRUint32 *_rval)
1603 ASSERT_VALID_EPHEMERAL;
1604 if (!mTag)
1605 mTag = ++jsdContext::LastTag;
1607 *_rval = mTag;
1608 return NS_OK;
1611 NS_IMETHODIMP
1612 jsdContext::GetVersion (PRInt32 *_rval)
1614 ASSERT_VALID_EPHEMERAL;
1615 *_rval = static_cast<PRInt32>(JS_GetVersion(mJSCx));
1616 return NS_OK;
1619 NS_IMETHODIMP
1620 jsdContext::SetVersion (PRInt32 id)
1622 ASSERT_VALID_EPHEMERAL;
1623 JSVersion ver = static_cast<JSVersion>(id);
1624 JS_SetVersion(mJSCx, ver);
1625 return NS_OK;
1628 NS_IMETHODIMP
1629 jsdContext::GetGlobalObject (jsdIValue **_rval)
1631 ASSERT_VALID_EPHEMERAL;
1632 JSObject *glob = JS_GetGlobalObject(mJSCx);
1633 JSDValue *jsdv = JSD_NewValue (mJSDCx, OBJECT_TO_JSVAL(glob));
1634 if (!jsdv)
1635 return NS_ERROR_FAILURE;
1636 *_rval = jsdValue::FromPtr (mJSDCx, jsdv);
1637 if (!*_rval)
1638 return NS_ERROR_FAILURE;
1639 return NS_OK;
1642 NS_IMETHODIMP
1643 jsdContext::GetScriptsEnabled (PRBool *_rval)
1645 ASSERT_VALID_EPHEMERAL;
1646 nsCOMPtr<nsIScriptContext> context = do_QueryInterface(mISCx);
1647 if (!context)
1648 return NS_ERROR_NO_INTERFACE;
1650 *_rval = context->GetScriptsEnabled();
1652 return NS_OK;
1655 NS_IMETHODIMP
1656 jsdContext::SetScriptsEnabled (PRBool _rval)
1658 ASSERT_VALID_EPHEMERAL;
1659 nsCOMPtr<nsIScriptContext> context = do_QueryInterface(mISCx);
1660 if (!context)
1661 return NS_ERROR_NO_INTERFACE;
1663 context->SetScriptsEnabled(_rval, PR_TRUE);
1665 return NS_OK;
1668 /* Stack Frames */
1669 NS_IMPL_THREADSAFE_ISUPPORTS2(jsdStackFrame, jsdIStackFrame, jsdIEphemeral)
1671 jsdStackFrame::jsdStackFrame (JSDContext *aCx, JSDThreadState *aThreadState,
1672 JSDStackFrameInfo *aStackFrameInfo) :
1673 mCx(aCx), mThreadState(aThreadState), mStackFrameInfo(aStackFrameInfo)
1675 DEBUG_CREATE ("jsdStackFrame", gFrameCount);
1676 mValid = (aCx && aThreadState && aStackFrameInfo);
1677 if (mValid) {
1678 mLiveListEntry.key = aStackFrameInfo;
1679 mLiveListEntry.value = this;
1680 jsds_InsertEphemeral (&gLiveStackFrames, &mLiveListEntry);
1684 jsdStackFrame::~jsdStackFrame()
1686 DEBUG_DESTROY ("jsdStackFrame", gFrameCount);
1687 if (mValid)
1689 /* call Invalidate() to take ourselves out of the live list */
1690 Invalidate();
1694 jsdIStackFrame *
1695 jsdStackFrame::FromPtr (JSDContext *aCx, JSDThreadState *aThreadState,
1696 JSDStackFrameInfo *aStackFrameInfo)
1698 if (!aStackFrameInfo)
1699 return nsnull;
1701 jsdIStackFrame *rv;
1702 nsCOMPtr<jsdIStackFrame> frame;
1704 nsCOMPtr<jsdIEphemeral> eph =
1705 jsds_FindEphemeral (&gLiveStackFrames,
1706 reinterpret_cast<void *>(aStackFrameInfo));
1708 if (eph)
1710 frame = do_QueryInterface(eph);
1711 rv = frame;
1713 else
1715 rv = new jsdStackFrame (aCx, aThreadState, aStackFrameInfo);
1718 NS_IF_ADDREF(rv);
1719 return rv;
1722 NS_IMETHODIMP
1723 jsdStackFrame::Invalidate()
1725 ASSERT_VALID_EPHEMERAL;
1726 mValid = PR_FALSE;
1727 jsds_RemoveEphemeral (&gLiveStackFrames, &mLiveListEntry);
1728 return NS_OK;
1731 void
1732 jsdStackFrame::InvalidateAll()
1734 if (gLiveStackFrames)
1735 jsds_InvalidateAllEphemerals (&gLiveStackFrames);
1738 NS_IMETHODIMP
1739 jsdStackFrame::GetJSDContext(JSDContext **_rval)
1741 ASSERT_VALID_EPHEMERAL;
1742 *_rval = mCx;
1743 return NS_OK;
1746 NS_IMETHODIMP
1747 jsdStackFrame::GetJSDThreadState(JSDThreadState **_rval)
1749 ASSERT_VALID_EPHEMERAL;
1750 *_rval = mThreadState;
1751 return NS_OK;
1754 NS_IMETHODIMP
1755 jsdStackFrame::GetJSDStackFrameInfo(JSDStackFrameInfo **_rval)
1757 ASSERT_VALID_EPHEMERAL;
1758 *_rval = mStackFrameInfo;
1759 return NS_OK;
1762 NS_IMETHODIMP
1763 jsdStackFrame::GetIsValid(PRBool *_rval)
1765 *_rval = mValid;
1766 return NS_OK;
1769 NS_IMETHODIMP
1770 jsdStackFrame::GetCallingFrame(jsdIStackFrame **_rval)
1772 ASSERT_VALID_EPHEMERAL;
1773 JSDStackFrameInfo *sfi = JSD_GetCallingStackFrame (mCx, mThreadState,
1774 mStackFrameInfo);
1775 *_rval = jsdStackFrame::FromPtr (mCx, mThreadState, sfi);
1776 return NS_OK;
1779 NS_IMETHODIMP
1780 jsdStackFrame::GetExecutionContext(jsdIContext **_rval)
1782 ASSERT_VALID_EPHEMERAL;
1783 JSContext *cx = JSD_GetJSContext (mCx, mThreadState);
1784 *_rval = jsdContext::FromPtr (mCx, cx);
1785 return NS_OK;
1788 NS_IMETHODIMP
1789 jsdStackFrame::GetFunctionName(char **_rval)
1791 ASSERT_VALID_EPHEMERAL;
1792 const char *name = JSD_GetNameForStackFrame(mCx, mThreadState,
1793 mStackFrameInfo);
1794 if (name) {
1795 *_rval = PL_strdup(name);
1796 if (!*_rval)
1797 return NS_ERROR_OUT_OF_MEMORY;
1798 } else {
1799 /* top level scripts have no function name */
1800 *_rval = nsnull;
1801 return NS_OK;
1803 return NS_OK;
1806 NS_IMETHODIMP
1807 jsdStackFrame::GetIsNative(PRBool *_rval)
1809 ASSERT_VALID_EPHEMERAL;
1810 *_rval = JSD_IsStackFrameNative (mCx, mThreadState, mStackFrameInfo);
1811 return NS_OK;
1814 NS_IMETHODIMP
1815 jsdStackFrame::GetIsDebugger(PRBool *_rval)
1817 ASSERT_VALID_EPHEMERAL;
1818 *_rval = JSD_IsStackFrameDebugger (mCx, mThreadState, mStackFrameInfo);
1819 return NS_OK;
1822 NS_IMETHODIMP
1823 jsdStackFrame::GetIsConstructing(PRBool *_rval)
1825 ASSERT_VALID_EPHEMERAL;
1826 *_rval = JSD_IsStackFrameConstructing (mCx, mThreadState, mStackFrameInfo);
1827 return NS_OK;
1830 NS_IMETHODIMP
1831 jsdStackFrame::GetScript(jsdIScript **_rval)
1833 ASSERT_VALID_EPHEMERAL;
1834 JSDScript *script = JSD_GetScriptForStackFrame (mCx, mThreadState,
1835 mStackFrameInfo);
1836 *_rval = jsdScript::FromPtr (mCx, script);
1837 return NS_OK;
1840 NS_IMETHODIMP
1841 jsdStackFrame::GetPc(PRUint32 *_rval)
1843 ASSERT_VALID_EPHEMERAL;
1844 JSDScript *script = JSD_GetScriptForStackFrame (mCx, mThreadState,
1845 mStackFrameInfo);
1846 if (!script)
1847 return NS_ERROR_FAILURE;
1848 jsuword pcbase = JSD_GetClosestPC(mCx, script, 0);
1850 jsuword pc = JSD_GetPCForStackFrame (mCx, mThreadState, mStackFrameInfo);
1851 if (pc)
1852 *_rval = pc - pcbase;
1853 else
1854 *_rval = pcbase;
1855 return NS_OK;
1858 NS_IMETHODIMP
1859 jsdStackFrame::GetLine(PRUint32 *_rval)
1861 ASSERT_VALID_EPHEMERAL;
1862 JSDScript *script = JSD_GetScriptForStackFrame (mCx, mThreadState,
1863 mStackFrameInfo);
1864 if (script) {
1865 jsuword pc = JSD_GetPCForStackFrame (mCx, mThreadState, mStackFrameInfo);
1866 *_rval = JSD_GetClosestLine (mCx, script, pc);
1867 } else {
1868 if (!JSD_IsStackFrameNative(mCx, mThreadState, mStackFrameInfo))
1869 return NS_ERROR_FAILURE;
1870 *_rval = 1;
1872 return NS_OK;
1875 NS_IMETHODIMP
1876 jsdStackFrame::GetCallee(jsdIValue **_rval)
1878 ASSERT_VALID_EPHEMERAL;
1879 JSDValue *jsdv = JSD_GetCallObjectForStackFrame (mCx, mThreadState,
1880 mStackFrameInfo);
1882 *_rval = jsdValue::FromPtr (mCx, jsdv);
1883 return NS_OK;
1886 NS_IMETHODIMP
1887 jsdStackFrame::GetScope(jsdIValue **_rval)
1889 ASSERT_VALID_EPHEMERAL;
1890 JSDValue *jsdv = JSD_GetScopeChainForStackFrame (mCx, mThreadState,
1891 mStackFrameInfo);
1893 *_rval = jsdValue::FromPtr (mCx, jsdv);
1894 return NS_OK;
1897 NS_IMETHODIMP
1898 jsdStackFrame::GetThisValue(jsdIValue **_rval)
1900 ASSERT_VALID_EPHEMERAL;
1901 JSDValue *jsdv = JSD_GetThisForStackFrame (mCx, mThreadState,
1902 mStackFrameInfo);
1904 *_rval = jsdValue::FromPtr (mCx, jsdv);
1905 return NS_OK;
1909 NS_IMETHODIMP
1910 jsdStackFrame::Eval (const nsAString &bytes, const char *fileName,
1911 PRUint32 line, jsdIValue **result, PRBool *_rval)
1913 ASSERT_VALID_EPHEMERAL;
1915 if (bytes.IsEmpty())
1916 return NS_ERROR_INVALID_ARG;
1918 // get pointer to buffer contained in |bytes|
1919 nsAString::const_iterator h;
1920 bytes.BeginReading(h);
1921 const jschar *char_bytes = reinterpret_cast<const jschar *>(h.get());
1923 JSExceptionState *estate = 0;
1924 jsval jv;
1926 JSContext *cx = JSD_GetJSContext (mCx, mThreadState);
1928 JSAutoRequest ar(cx);
1930 estate = JS_SaveExceptionState (cx);
1931 JS_ClearPendingException (cx);
1933 *_rval = JSD_AttemptUCScriptInStackFrame (mCx, mThreadState,
1934 mStackFrameInfo,
1935 char_bytes, bytes.Length(),
1936 fileName, line, &jv);
1937 if (!*_rval) {
1938 if (JS_IsExceptionPending(cx))
1939 JS_GetPendingException (cx, &jv);
1940 else
1941 jv = 0;
1944 JS_RestoreExceptionState (cx, estate);
1946 JSDValue *jsdv = JSD_NewValue (mCx, jv);
1947 if (!jsdv)
1948 return NS_ERROR_FAILURE;
1949 *result = jsdValue::FromPtr (mCx, jsdv);
1950 if (!*result)
1951 return NS_ERROR_FAILURE;
1953 return NS_OK;
1956 /* Values */
1957 NS_IMPL_THREADSAFE_ISUPPORTS2(jsdValue, jsdIValue, jsdIEphemeral)
1958 jsdIValue *
1959 jsdValue::FromPtr (JSDContext *aCx, JSDValue *aValue)
1961 /* value will be dropped by te jsdValue destructor. */
1963 if (!aValue)
1964 return nsnull;
1966 jsdIValue *rv = new jsdValue (aCx, aValue);
1967 NS_IF_ADDREF(rv);
1968 return rv;
1971 jsdValue::jsdValue (JSDContext *aCx, JSDValue *aValue) : mValid(PR_TRUE),
1972 mCx(aCx),
1973 mValue(aValue)
1975 DEBUG_CREATE ("jsdValue", gValueCount);
1976 mLiveListEntry.value = this;
1977 jsds_InsertEphemeral (&gLiveValues, &mLiveListEntry);
1980 jsdValue::~jsdValue()
1982 DEBUG_DESTROY ("jsdValue", gValueCount);
1983 if (mValid)
1984 /* call Invalidate() to take ourselves out of the live list */
1985 Invalidate();
1988 NS_IMETHODIMP
1989 jsdValue::GetIsValid(PRBool *_rval)
1991 *_rval = mValid;
1992 return NS_OK;
1995 NS_IMETHODIMP
1996 jsdValue::Invalidate()
1998 ASSERT_VALID_EPHEMERAL;
1999 mValid = PR_FALSE;
2000 jsds_RemoveEphemeral (&gLiveValues, &mLiveListEntry);
2001 JSD_DropValue (mCx, mValue);
2002 return NS_OK;
2005 void
2006 jsdValue::InvalidateAll()
2008 if (gLiveValues)
2009 jsds_InvalidateAllEphemerals (&gLiveValues);
2012 NS_IMETHODIMP
2013 jsdValue::GetJSDContext(JSDContext **_rval)
2015 ASSERT_VALID_EPHEMERAL;
2016 *_rval = mCx;
2017 return NS_OK;
2020 NS_IMETHODIMP
2021 jsdValue::GetJSDValue (JSDValue **_rval)
2023 ASSERT_VALID_EPHEMERAL;
2024 *_rval = mValue;
2025 return NS_OK;
2028 NS_IMETHODIMP
2029 jsdValue::GetIsNative (PRBool *_rval)
2031 ASSERT_VALID_EPHEMERAL;
2032 *_rval = JSD_IsValueNative (mCx, mValue);
2033 return NS_OK;
2036 NS_IMETHODIMP
2037 jsdValue::GetIsNumber (PRBool *_rval)
2039 ASSERT_VALID_EPHEMERAL;
2040 *_rval = JSD_IsValueNumber (mCx, mValue);
2041 return NS_OK;
2044 NS_IMETHODIMP
2045 jsdValue::GetIsPrimitive (PRBool *_rval)
2047 ASSERT_VALID_EPHEMERAL;
2048 *_rval = JSD_IsValuePrimitive (mCx, mValue);
2049 return NS_OK;
2052 NS_IMETHODIMP
2053 jsdValue::GetJsType (PRUint32 *_rval)
2055 ASSERT_VALID_EPHEMERAL;
2056 jsval val;
2058 val = JSD_GetValueWrappedJSVal (mCx, mValue);
2060 if (JSVAL_IS_NULL(val))
2061 *_rval = TYPE_NULL;
2062 else if (JSVAL_IS_BOOLEAN(val))
2063 *_rval = TYPE_BOOLEAN;
2064 else if (JSVAL_IS_DOUBLE(val))
2065 *_rval = TYPE_DOUBLE;
2066 else if (JSVAL_IS_INT(val))
2067 *_rval = TYPE_INT;
2068 else if (JSVAL_IS_STRING(val))
2069 *_rval = TYPE_STRING;
2070 else if (JSVAL_IS_VOID(val))
2071 *_rval = TYPE_VOID;
2072 else if (JSD_IsValueFunction (mCx, mValue))
2073 *_rval = TYPE_FUNCTION;
2074 else if (JSVAL_IS_OBJECT(val))
2075 *_rval = TYPE_OBJECT;
2076 else
2077 NS_ASSERTION (0, "Value has no discernible type.");
2079 return NS_OK;
2082 NS_IMETHODIMP
2083 jsdValue::GetJsPrototype (jsdIValue **_rval)
2085 ASSERT_VALID_EPHEMERAL;
2086 JSDValue *jsdv = JSD_GetValuePrototype (mCx, mValue);
2087 *_rval = jsdValue::FromPtr (mCx, jsdv);
2088 return NS_OK;
2091 NS_IMETHODIMP
2092 jsdValue::GetJsParent (jsdIValue **_rval)
2094 ASSERT_VALID_EPHEMERAL;
2095 JSDValue *jsdv = JSD_GetValueParent (mCx, mValue);
2096 *_rval = jsdValue::FromPtr (mCx, jsdv);
2097 return NS_OK;
2100 NS_IMETHODIMP
2101 jsdValue::GetJsClassName(char **_rval)
2103 ASSERT_VALID_EPHEMERAL;
2104 const char *name = JSD_GetValueClassName(mCx, mValue);
2105 if (name) {
2106 *_rval = PL_strdup(name);
2107 if (!*_rval)
2108 return NS_ERROR_OUT_OF_MEMORY;
2109 } else {
2110 *_rval = nsnull;
2113 return NS_OK;
2116 NS_IMETHODIMP
2117 jsdValue::GetJsConstructor (jsdIValue **_rval)
2119 ASSERT_VALID_EPHEMERAL;
2120 JSDValue *jsdv = JSD_GetValueConstructor (mCx, mValue);
2121 *_rval = jsdValue::FromPtr (mCx, jsdv);
2122 return NS_OK;
2125 NS_IMETHODIMP
2126 jsdValue::GetJsFunctionName(char **_rval)
2128 ASSERT_VALID_EPHEMERAL;
2129 const char *name = JSD_GetValueFunctionName(mCx, mValue);
2130 if (name) {
2131 *_rval = PL_strdup(name);
2132 if (!*_rval)
2133 return NS_ERROR_OUT_OF_MEMORY;
2134 } else {
2135 /* top level scripts have no function name */
2136 *_rval = nsnull;
2137 return NS_OK;
2140 return NS_OK;
2143 NS_IMETHODIMP
2144 jsdValue::GetBooleanValue(PRBool *_rval)
2146 ASSERT_VALID_EPHEMERAL;
2147 *_rval = JSD_GetValueBoolean (mCx, mValue);
2148 return NS_OK;
2151 NS_IMETHODIMP
2152 jsdValue::GetDoubleValue(double *_rval)
2154 ASSERT_VALID_EPHEMERAL;
2155 double *dp = JSD_GetValueDouble (mCx, mValue);
2156 if (!dp)
2157 return NS_ERROR_FAILURE;
2158 *_rval = *dp;
2159 return NS_OK;
2162 NS_IMETHODIMP
2163 jsdValue::GetIntValue(PRInt32 *_rval)
2165 ASSERT_VALID_EPHEMERAL;
2166 *_rval = JSD_GetValueInt (mCx, mValue);
2167 return NS_OK;
2170 NS_IMETHODIMP
2171 jsdValue::GetObjectValue(jsdIObject **_rval)
2173 ASSERT_VALID_EPHEMERAL;
2174 JSDObject *obj;
2175 obj = JSD_GetObjectForValue (mCx, mValue);
2176 *_rval = jsdObject::FromPtr (mCx, obj);
2177 if (!*_rval)
2178 return NS_ERROR_FAILURE;
2179 return NS_OK;
2182 NS_IMETHODIMP
2183 jsdValue::GetStringValue(char **_rval)
2185 ASSERT_VALID_EPHEMERAL;
2186 JSString *jstr_val = JSD_GetValueString(mCx, mValue);
2187 if (jstr_val) {
2188 char *bytes = JS_GetStringBytes(jstr_val);
2189 *_rval = PL_strdup(bytes);
2190 if (!*_rval)
2191 return NS_ERROR_OUT_OF_MEMORY;
2192 } else {
2193 *_rval = nsnull;
2195 return NS_OK;
2198 NS_IMETHODIMP
2199 jsdValue::GetPropertyCount (PRInt32 *_rval)
2201 ASSERT_VALID_EPHEMERAL;
2202 if (JSD_IsValueObject(mCx, mValue))
2203 *_rval = JSD_GetCountOfProperties (mCx, mValue);
2204 else
2205 *_rval = -1;
2206 return NS_OK;
2209 NS_IMETHODIMP
2210 jsdValue::GetProperties (jsdIProperty ***propArray, PRUint32 *length)
2212 ASSERT_VALID_EPHEMERAL;
2213 *propArray = nsnull;
2214 if (length)
2215 *length = 0;
2217 PRUint32 prop_count = JSD_IsValueObject(mCx, mValue)
2218 ? JSD_GetCountOfProperties (mCx, mValue)
2219 : 0;
2220 NS_ENSURE_TRUE(prop_count, NS_OK);
2222 jsdIProperty **pa_temp =
2223 static_cast<jsdIProperty **>
2224 (nsMemory::Alloc(sizeof (jsdIProperty *) *
2225 prop_count));
2226 NS_ENSURE_TRUE(pa_temp, NS_ERROR_OUT_OF_MEMORY);
2228 PRUint32 i = 0;
2229 JSDProperty *iter = NULL;
2230 JSDProperty *prop;
2231 while ((prop = JSD_IterateProperties (mCx, mValue, &iter))) {
2232 pa_temp[i] = jsdProperty::FromPtr (mCx, prop);
2233 ++i;
2236 NS_ASSERTION (prop_count == i, "property count mismatch");
2238 /* if caller doesn't care about length, don't bother telling them */
2239 *propArray = pa_temp;
2240 if (length)
2241 *length = prop_count;
2243 return NS_OK;
2246 NS_IMETHODIMP
2247 jsdValue::GetProperty (const char *name, jsdIProperty **_rval)
2249 ASSERT_VALID_EPHEMERAL;
2250 JSContext *cx = JSD_GetDefaultJSContext (mCx);
2252 JSAutoRequest ar(cx);
2254 /* not rooting this */
2255 JSString *jstr_name = JS_NewStringCopyZ (cx, name);
2256 if (!jstr_name)
2257 return NS_ERROR_OUT_OF_MEMORY;
2259 JSDProperty *prop = JSD_GetValueProperty (mCx, mValue, jstr_name);
2261 *_rval = jsdProperty::FromPtr (mCx, prop);
2262 return NS_OK;
2265 NS_IMETHODIMP
2266 jsdValue::Refresh()
2268 ASSERT_VALID_EPHEMERAL;
2269 JSD_RefreshValue (mCx, mValue);
2270 return NS_OK;
2273 NS_IMETHODIMP
2274 jsdValue::GetWrappedValue()
2276 ASSERT_VALID_EPHEMERAL;
2277 nsresult rv;
2278 nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID(), &rv);
2279 if (NS_FAILED(rv))
2280 return rv;
2282 nsAXPCNativeCallContext *cc = nsnull;
2283 rv = xpc->GetCurrentNativeCallContext(&cc);
2284 if (NS_FAILED(rv))
2285 return rv;
2287 jsval *result;
2288 rv = cc->GetRetValPtr(&result);
2289 if (NS_FAILED(rv))
2290 return rv;
2292 if (result)
2294 *result = JSD_GetValueWrappedJSVal (mCx, mValue);
2295 cc->SetReturnValueWasSet(PR_TRUE);
2298 return NS_OK;
2301 /******************************************************************************
2302 * debugger service implementation
2303 ******************************************************************************/
2304 NS_IMPL_THREADSAFE_ISUPPORTS1(jsdService, jsdIDebuggerService)
2306 NS_IMETHODIMP
2307 jsdService::GetJSDContext(JSDContext **_rval)
2309 *_rval = mCx;
2310 return NS_OK;
2313 NS_IMETHODIMP
2314 jsdService::GetInitAtStartup (PRBool *_rval)
2316 nsresult rv;
2317 nsCOMPtr<nsICategoryManager>
2318 categoryManager(do_GetService(NS_CATMAN_CTRID, &rv));
2320 if (NS_FAILED(rv))
2322 NS_WARNING("couldn't get category manager");
2323 return rv;
2326 if (mInitAtStartup == triUnknown) {
2327 nsXPIDLCString notused;
2328 nsresult autoreg_rv, appstart_rv;
2330 autoreg_rv = categoryManager->GetCategoryEntry(AUTOREG_CATEGORY,
2331 JSD_AUTOREG_ENTRY,
2332 getter_Copies(notused));
2333 appstart_rv = categoryManager->GetCategoryEntry(APPSTART_CATEGORY,
2334 JSD_STARTUP_ENTRY,
2335 getter_Copies(notused));
2336 if (autoreg_rv != appstart_rv) {
2337 /* we have an inconsistent state in the registry, attempt to fix.
2338 * we need to make mInitAtStartup disagree with the state passed
2339 * to SetInitAtStartup to make it actually do something.
2341 mInitAtStartup = triYes;
2342 rv = SetInitAtStartup (PR_FALSE);
2343 if (NS_FAILED(rv))
2345 NS_WARNING("SetInitAtStartup failed");
2346 return rv;
2348 } else if (autoreg_rv == NS_ERROR_NOT_AVAILABLE) {
2349 mInitAtStartup = triNo;
2350 } else if (NS_SUCCEEDED(autoreg_rv)) {
2351 mInitAtStartup = triYes;
2352 } else {
2353 NS_WARN_IF_FALSE(NS_SUCCEEDED(autoreg_rv),
2354 "couldn't get autoreg category");
2355 NS_WARN_IF_FALSE(NS_SUCCEEDED(appstart_rv),
2356 "couldn't get appstart category");
2357 return rv;
2361 if (_rval)
2362 *_rval = (mInitAtStartup == triYes);
2364 return NS_OK;
2368 * The initAtStartup property controls whether or not we register the
2369 * app start observer (jsdASObserver.) We register for both
2370 * "xpcom-autoregistration" and "app-startup" notifications if |state| is true.
2371 * the autoreg message is sent just before registration occurs (before
2372 * "app-startup".) We care about autoreg because it may load javascript
2373 * components. autoreg does *not* fire if components haven't changed since the
2374 * last autoreg, so we watch "app-startup" as a fallback.
2376 NS_IMETHODIMP
2377 jsdService::SetInitAtStartup (PRBool state)
2379 nsresult rv;
2381 if (mInitAtStartup == triUnknown) {
2382 /* side effect sets mInitAtStartup */
2383 rv = GetInitAtStartup(nsnull);
2384 if (NS_FAILED(rv))
2385 return rv;
2388 if (state && mInitAtStartup == triYes ||
2389 !state && mInitAtStartup == triNo) {
2390 /* already in the requested state */
2391 return NS_OK;
2394 nsCOMPtr<nsICategoryManager>
2395 categoryManager(do_GetService(NS_CATMAN_CTRID, &rv));
2396 if (NS_FAILED(rv))
2397 return rv;
2399 if (state) {
2400 rv = categoryManager->AddCategoryEntry(AUTOREG_CATEGORY,
2401 JSD_AUTOREG_ENTRY,
2402 jsdARObserverCtrID,
2403 PR_TRUE, PR_TRUE, nsnull);
2404 if (NS_FAILED(rv))
2405 return rv;
2406 rv = categoryManager->AddCategoryEntry(APPSTART_CATEGORY,
2407 JSD_STARTUP_ENTRY,
2408 jsdASObserverCtrID,
2409 PR_TRUE, PR_TRUE, nsnull);
2410 if (NS_FAILED(rv))
2411 return rv;
2412 mInitAtStartup = triYes;
2413 } else {
2414 rv = categoryManager->DeleteCategoryEntry(AUTOREG_CATEGORY,
2415 JSD_AUTOREG_ENTRY, PR_TRUE);
2416 if (NS_FAILED(rv))
2417 return rv;
2418 rv = categoryManager->DeleteCategoryEntry(APPSTART_CATEGORY,
2419 JSD_STARTUP_ENTRY, PR_TRUE);
2420 if (NS_FAILED(rv))
2421 return rv;
2422 mInitAtStartup = triNo;
2425 return NS_OK;
2428 NS_IMETHODIMP
2429 jsdService::GetFlags (PRUint32 *_rval)
2431 ASSERT_VALID_CONTEXT;
2432 *_rval = JSD_GetContextFlags (mCx);
2433 return NS_OK;
2436 NS_IMETHODIMP
2437 jsdService::SetFlags (PRUint32 flags)
2439 ASSERT_VALID_CONTEXT;
2440 JSD_SetContextFlags (mCx, flags);
2441 return NS_OK;
2444 NS_IMETHODIMP
2445 jsdService::GetImplementationString(char **_rval)
2447 *_rval = PL_strdup(implementationString);
2448 if (!*_rval)
2449 return NS_ERROR_OUT_OF_MEMORY;
2450 return NS_OK;
2453 NS_IMETHODIMP
2454 jsdService::GetImplementationMajor(PRUint32 *_rval)
2456 *_rval = JSDS_MAJOR_VERSION;
2457 return NS_OK;
2460 NS_IMETHODIMP
2461 jsdService::GetImplementationMinor(PRUint32 *_rval)
2463 *_rval = JSDS_MINOR_VERSION;
2464 return NS_OK;
2467 NS_IMETHODIMP
2468 jsdService::GetIsOn (PRBool *_rval)
2470 *_rval = mOn;
2471 return NS_OK;
2474 NS_IMETHODIMP
2475 jsdService::On (void)
2477 nsresult rv;
2479 /* get JS things from the CallContext */
2480 nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID(), &rv);
2481 if (NS_FAILED(rv)) return rv;
2483 nsAXPCNativeCallContext *cc = nsnull;
2484 rv = xpc->GetCurrentNativeCallContext(&cc);
2485 if (NS_FAILED(rv)) return rv;
2487 JSContext *cx;
2488 rv = cc->GetJSContext (&cx);
2489 if (NS_FAILED(rv)) return rv;
2491 return OnForRuntime(JS_GetRuntime (cx));
2495 NS_IMETHODIMP
2496 jsdService::OnForRuntime (JSRuntime *rt)
2498 if (mOn)
2499 return (rt == mRuntime) ? NS_OK : NS_ERROR_ALREADY_INITIALIZED;
2501 mRuntime = rt;
2503 if (gLastGCProc == jsds_GCCallbackProc)
2504 /* condition indicates that the callback proc has not been set yet */
2505 gLastGCProc = JS_SetGCCallbackRT (rt, jsds_GCCallbackProc);
2507 mCx = JSD_DebuggerOnForUser (rt, NULL, NULL);
2508 if (!mCx)
2509 return NS_ERROR_FAILURE;
2511 JSContext *cx = JSD_GetDefaultJSContext (mCx);
2512 JSObject *glob = JS_GetGlobalObject (cx);
2514 /* init xpconnect on the debugger's context in case xpconnect tries to
2515 * use it for stuff. */
2516 nsresult rv;
2517 nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID(), &rv);
2518 if (NS_FAILED(rv))
2519 return rv;
2521 xpc->InitClasses (cx, glob);
2523 /* If any of these mFooHook objects are installed, do the required JSD
2524 * hookup now. See also, jsdService::SetFooHook().
2526 if (mErrorHook)
2527 JSD_SetErrorReporter (mCx, jsds_ErrorHookProc, NULL);
2528 if (mThrowHook)
2529 JSD_SetThrowHook (mCx, jsds_ExecutionHookProc, NULL);
2530 /* can't ignore script callbacks, as we need to |Release| the wrapper
2531 * stored in private data when a script is deleted. */
2532 if (mInterruptHook)
2533 JSD_SetInterruptHook (mCx, jsds_ExecutionHookProc, NULL);
2534 if (mDebuggerHook)
2535 JSD_SetDebuggerHook (mCx, jsds_ExecutionHookProc, NULL);
2536 if (mDebugHook)
2537 JSD_SetDebugBreakHook (mCx, jsds_ExecutionHookProc, NULL);
2538 if (mTopLevelHook)
2539 JSD_SetTopLevelHook (mCx, jsds_CallHookProc, NULL);
2540 else
2541 JSD_ClearTopLevelHook (mCx);
2542 if (mFunctionHook)
2543 JSD_SetFunctionHook (mCx, jsds_CallHookProc, NULL);
2544 else
2545 JSD_ClearFunctionHook (mCx);
2546 mOn = PR_TRUE;
2548 #ifdef DEBUG
2549 printf ("+++ JavaScript debugging hooks installed.\n");
2550 #endif
2551 return NS_OK;
2554 NS_IMETHODIMP
2555 jsdService::Off (void)
2557 if (!mOn)
2558 return NS_OK;
2560 if (!mCx || !mRuntime)
2561 return NS_ERROR_NOT_INITIALIZED;
2563 if (gDeadScripts) {
2564 if (gGCStatus != JSGC_END)
2565 return NS_ERROR_NOT_AVAILABLE;
2567 JSContext *cx = JSD_GetDefaultJSContext(mCx);
2568 jsds_NotifyPendingDeadScripts(cx);
2572 if (gLastGCProc != jsds_GCCallbackProc)
2573 JS_SetGCCallbackRT (mRuntime, gLastGCProc);
2576 jsdContext::InvalidateAll();
2577 jsdScript::InvalidateAll();
2578 jsdValue::InvalidateAll();
2579 jsdProperty::InvalidateAll();
2580 ClearAllBreakpoints();
2582 JSD_SetErrorReporter (mCx, NULL, NULL);
2583 JSD_SetScriptHook (mCx, NULL, NULL);
2584 JSD_ClearThrowHook (mCx);
2585 JSD_ClearInterruptHook (mCx);
2586 JSD_ClearDebuggerHook (mCx);
2587 JSD_ClearDebugBreakHook (mCx);
2588 JSD_ClearTopLevelHook (mCx);
2589 JSD_ClearFunctionHook (mCx);
2591 JSD_DebuggerOff (mCx);
2593 mCx = nsnull;
2594 mRuntime = nsnull;
2595 mOn = PR_FALSE;
2597 #ifdef DEBUG
2598 printf ("+++ JavaScript debugging hooks removed.\n");
2599 #endif
2601 return NS_OK;
2604 NS_IMETHODIMP
2605 jsdService::GetPauseDepth(PRUint32 *_rval)
2607 NS_ENSURE_ARG_POINTER(_rval);
2608 *_rval = mPauseLevel;
2609 return NS_OK;
2612 NS_IMETHODIMP
2613 jsdService::Pause(PRUint32 *_rval)
2615 if (!mCx)
2616 return NS_ERROR_NOT_INITIALIZED;
2618 if (++mPauseLevel == 1) {
2619 JSD_SetErrorReporter (mCx, NULL, NULL);
2620 JSD_ClearThrowHook (mCx);
2621 JSD_ClearInterruptHook (mCx);
2622 JSD_ClearDebuggerHook (mCx);
2623 JSD_ClearDebugBreakHook (mCx);
2624 JSD_ClearTopLevelHook (mCx);
2625 JSD_ClearFunctionHook (mCx);
2628 if (_rval)
2629 *_rval = mPauseLevel;
2631 return NS_OK;
2634 NS_IMETHODIMP
2635 jsdService::UnPause(PRUint32 *_rval)
2637 if (!mCx)
2638 return NS_ERROR_NOT_INITIALIZED;
2640 if (mPauseLevel == 0)
2641 return NS_ERROR_NOT_AVAILABLE;
2643 /* check mOn before we muck with this stuff, it's possible the debugger
2644 * was turned off while we were paused.
2646 if (--mPauseLevel == 0 && mOn) {
2647 if (mErrorHook)
2648 JSD_SetErrorReporter (mCx, jsds_ErrorHookProc, NULL);
2649 if (mThrowHook)
2650 JSD_SetThrowHook (mCx, jsds_ExecutionHookProc, NULL);
2651 if (mInterruptHook)
2652 JSD_SetInterruptHook (mCx, jsds_ExecutionHookProc, NULL);
2653 if (mDebuggerHook)
2654 JSD_SetDebuggerHook (mCx, jsds_ExecutionHookProc, NULL);
2655 if (mDebugHook)
2656 JSD_SetDebugBreakHook (mCx, jsds_ExecutionHookProc, NULL);
2657 if (mTopLevelHook)
2658 JSD_SetTopLevelHook (mCx, jsds_CallHookProc, NULL);
2659 else
2660 JSD_ClearTopLevelHook (mCx);
2661 if (mFunctionHook)
2662 JSD_SetFunctionHook (mCx, jsds_CallHookProc, NULL);
2663 else
2664 JSD_ClearFunctionHook (mCx);
2667 if (_rval)
2668 *_rval = mPauseLevel;
2670 return NS_OK;
2673 NS_IMETHODIMP
2674 jsdService::EnumerateContexts (jsdIContextEnumerator *enumerator)
2676 ASSERT_VALID_CONTEXT;
2678 if (!enumerator)
2679 return NS_OK;
2681 JSContext *iter = NULL;
2682 JSContext *cx;
2684 while ((cx = JS_ContextIterator (mRuntime, &iter)))
2686 nsCOMPtr<jsdIContext> jsdicx =
2687 getter_AddRefs(jsdContext::FromPtr(mCx, cx));
2688 if (jsdicx)
2690 if (NS_FAILED(enumerator->EnumerateContext(jsdicx)))
2691 break;
2695 return NS_OK;
2698 NS_IMETHODIMP
2699 jsdService::EnumerateScripts (jsdIScriptEnumerator *enumerator)
2701 ASSERT_VALID_CONTEXT;
2703 JSDScript *script;
2704 JSDScript *iter = NULL;
2705 nsresult rv = NS_OK;
2707 JSD_LockScriptSubsystem(mCx);
2708 while((script = JSD_IterateScripts(mCx, &iter))) {
2709 nsCOMPtr<jsdIScript> jsdis =
2710 getter_AddRefs(jsdScript::FromPtr(mCx, script));
2711 rv = enumerator->EnumerateScript (jsdis);
2712 if (NS_FAILED(rv))
2713 break;
2715 JSD_UnlockScriptSubsystem(mCx);
2717 return rv;
2720 NS_IMETHODIMP
2721 jsdService::GC (void)
2723 ASSERT_VALID_CONTEXT;
2724 JSContext *cx = JSD_GetDefaultJSContext (mCx);
2725 JS_GC(cx);
2726 return NS_OK;
2729 NS_IMETHODIMP
2730 jsdService::DumpHeap(const char* fileName)
2732 ASSERT_VALID_CONTEXT;
2733 #ifndef DEBUG
2734 return NS_ERROR_NOT_IMPLEMENTED;
2735 #else
2736 nsresult rv = NS_OK;
2737 FILE *file = fileName ? fopen(fileName, "w") : stdout;
2738 if (!file) {
2739 rv = NS_ERROR_FAILURE;
2740 } else {
2741 JSContext *cx = JSD_GetDefaultJSContext (mCx);
2742 if (!JS_DumpHeap(cx, file, NULL, 0, NULL, (size_t)-1, NULL))
2743 rv = NS_ERROR_FAILURE;
2744 if (file != stdout)
2745 fclose(file);
2747 return rv;
2748 #endif
2751 NS_IMETHODIMP
2752 jsdService::ClearProfileData ()
2754 ASSERT_VALID_CONTEXT;
2755 JSD_ClearAllProfileData (mCx);
2756 return NS_OK;
2759 NS_IMETHODIMP
2760 jsdService::InsertFilter (jsdIFilter *filter, jsdIFilter *after)
2762 NS_ENSURE_ARG_POINTER (filter);
2763 if (jsds_FindFilter (filter))
2764 return NS_ERROR_INVALID_ARG;
2766 FilterRecord *rec = PR_NEWZAP (FilterRecord);
2767 if (!rec)
2768 return NS_ERROR_OUT_OF_MEMORY;
2770 if (!jsds_SyncFilter (rec, filter)) {
2771 PR_Free (rec);
2772 return NS_ERROR_FAILURE;
2775 if (gFilters) {
2776 if (!after) {
2777 /* insert at head of list */
2778 PR_INSERT_LINK(&rec->links, &gFilters->links);
2779 gFilters = rec;
2780 } else {
2781 /* insert somewhere in the list */
2782 FilterRecord *afterRecord = jsds_FindFilter (after);
2783 if (!afterRecord) {
2784 jsds_FreeFilter(rec);
2785 return NS_ERROR_INVALID_ARG;
2787 PR_INSERT_AFTER(&rec->links, &afterRecord->links);
2789 } else {
2790 if (after) {
2791 /* user asked to insert into the middle of an empty list, bail. */
2792 jsds_FreeFilter(rec);
2793 return NS_ERROR_NOT_INITIALIZED;
2795 PR_INIT_CLIST(&rec->links);
2796 gFilters = rec;
2799 return NS_OK;
2802 NS_IMETHODIMP
2803 jsdService::AppendFilter (jsdIFilter *filter)
2805 NS_ENSURE_ARG_POINTER (filter);
2806 if (jsds_FindFilter (filter))
2807 return NS_ERROR_INVALID_ARG;
2808 FilterRecord *rec = PR_NEWZAP (FilterRecord);
2810 if (!jsds_SyncFilter (rec, filter)) {
2811 PR_Free (rec);
2812 return NS_ERROR_FAILURE;
2815 if (gFilters) {
2816 PR_INSERT_BEFORE(&rec->links, &gFilters->links);
2817 } else {
2818 PR_INIT_CLIST(&rec->links);
2819 gFilters = rec;
2822 return NS_OK;
2825 NS_IMETHODIMP
2826 jsdService::RemoveFilter (jsdIFilter *filter)
2828 NS_ENSURE_ARG_POINTER(filter);
2829 FilterRecord *rec = jsds_FindFilter (filter);
2830 if (!rec)
2831 return NS_ERROR_INVALID_ARG;
2833 if (gFilters == rec) {
2834 gFilters = reinterpret_cast<FilterRecord *>
2835 (PR_NEXT_LINK(&rec->links));
2836 /* If we're the only filter left, null out the list head. */
2837 if (gFilters == rec)
2838 gFilters = nsnull;
2842 PR_REMOVE_LINK(&rec->links);
2843 jsds_FreeFilter (rec);
2845 return NS_OK;
2848 NS_IMETHODIMP
2849 jsdService::SwapFilters (jsdIFilter *filter_a, jsdIFilter *filter_b)
2851 NS_ENSURE_ARG_POINTER(filter_a);
2852 NS_ENSURE_ARG_POINTER(filter_b);
2854 FilterRecord *rec_a = jsds_FindFilter (filter_a);
2855 if (!rec_a)
2856 return NS_ERROR_INVALID_ARG;
2858 if (filter_a == filter_b) {
2859 /* just a refresh */
2860 if (!jsds_SyncFilter (rec_a, filter_a))
2861 return NS_ERROR_FAILURE;
2862 return NS_OK;
2865 FilterRecord *rec_b = jsds_FindFilter (filter_b);
2866 if (!rec_b) {
2867 /* filter_b is not in the list, replace filter_a with filter_b. */
2868 if (!jsds_SyncFilter (rec_a, filter_b))
2869 return NS_ERROR_FAILURE;
2870 } else {
2871 /* both filters are in the list, swap. */
2872 if (!jsds_SyncFilter (rec_a, filter_b))
2873 return NS_ERROR_FAILURE;
2874 if (!jsds_SyncFilter (rec_b, filter_a))
2875 return NS_ERROR_FAILURE;
2878 return NS_OK;
2881 NS_IMETHODIMP
2882 jsdService::EnumerateFilters (jsdIFilterEnumerator *enumerator)
2884 if (!gFilters)
2885 return NS_OK;
2887 FilterRecord *current = gFilters;
2888 do {
2889 jsds_SyncFilter (current, current->filterObject);
2890 /* SyncFilter failure would be bad, but what would we do about it? */
2891 if (enumerator) {
2892 nsresult rv = enumerator->EnumerateFilter (current->filterObject);
2893 if (NS_FAILED(rv))
2894 return rv;
2896 current = reinterpret_cast<FilterRecord *>
2897 (PR_NEXT_LINK (&current->links));
2898 } while (current != gFilters);
2900 return NS_OK;
2903 NS_IMETHODIMP
2904 jsdService::RefreshFilters ()
2906 return EnumerateFilters(nsnull);
2909 NS_IMETHODIMP
2910 jsdService::ClearFilters ()
2912 if (!gFilters)
2913 return NS_OK;
2915 FilterRecord *current = reinterpret_cast<FilterRecord *>
2916 (PR_NEXT_LINK (&gFilters->links));
2917 do {
2918 FilterRecord *next = reinterpret_cast<FilterRecord *>
2919 (PR_NEXT_LINK (&current->links));
2920 PR_REMOVE_AND_INIT_LINK(&current->links);
2921 jsds_FreeFilter(current);
2922 current = next;
2923 } while (current != gFilters);
2925 jsds_FreeFilter(current);
2926 gFilters = nsnull;
2928 return NS_OK;
2931 NS_IMETHODIMP
2932 jsdService::ClearAllBreakpoints (void)
2934 ASSERT_VALID_CONTEXT;
2936 JSD_LockScriptSubsystem(mCx);
2937 JSD_ClearAllExecutionHooks (mCx);
2938 JSD_UnlockScriptSubsystem(mCx);
2939 return NS_OK;
2942 NS_IMETHODIMP
2943 jsdService::WrapValue(jsdIValue **_rval)
2945 ASSERT_VALID_CONTEXT;
2947 nsresult rv;
2948 nsCOMPtr<nsIXPConnect> xpc = do_GetService (nsIXPConnect::GetCID(), &rv);
2949 if (NS_FAILED(rv))
2950 return rv;
2952 nsAXPCNativeCallContext *cc = nsnull;
2953 rv = xpc->GetCurrentNativeCallContext (&cc);
2954 if (NS_FAILED(rv))
2955 return rv;
2957 PRUint32 argc;
2958 rv = cc->GetArgc (&argc);
2959 if (NS_FAILED(rv))
2960 return rv;
2961 if (argc < 1)
2962 return NS_ERROR_INVALID_ARG;
2964 jsval *argv;
2965 rv = cc->GetArgvPtr (&argv);
2966 if (NS_FAILED(rv))
2967 return rv;
2969 JSDValue *jsdv = JSD_NewValue (mCx, argv[0]);
2970 if (!jsdv)
2971 return NS_ERROR_FAILURE;
2973 *_rval = jsdValue::FromPtr (mCx, jsdv);
2974 return NS_OK;
2978 NS_IMETHODIMP
2979 jsdService::EnterNestedEventLoop (jsdINestCallback *callback, PRUint32 *_rval)
2981 // Nesting event queues is a thing of the past. Now, we just spin the
2982 // current event loop.
2984 nsresult rv;
2985 nsCOMPtr<nsIJSContextStack>
2986 stack(do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv));
2987 if (NS_FAILED(rv))
2988 return rv;
2989 PRUint32 nestLevel = ++mNestedLoopLevel;
2991 nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
2993 if (NS_SUCCEEDED(stack->Push(nsnull))) {
2994 if (callback) {
2995 Pause(nsnull);
2996 rv = callback->OnNest();
2997 UnPause(nsnull);
3000 while (NS_SUCCEEDED(rv) && mNestedLoopLevel >= nestLevel) {
3001 if (!NS_ProcessNextEvent(thread))
3002 rv = NS_ERROR_UNEXPECTED;
3005 JSContext* cx;
3006 stack->Pop(&cx);
3007 NS_ASSERTION(cx == nsnull, "JSContextStack mismatch");
3009 else
3010 rv = NS_ERROR_FAILURE;
3012 NS_ASSERTION (mNestedLoopLevel <= nestLevel,
3013 "nested event didn't unwind properly");
3014 if (mNestedLoopLevel == nestLevel)
3015 --mNestedLoopLevel;
3017 *_rval = mNestedLoopLevel;
3018 return rv;
3021 NS_IMETHODIMP
3022 jsdService::ExitNestedEventLoop (PRUint32 *_rval)
3024 if (mNestedLoopLevel > 0)
3025 --mNestedLoopLevel;
3026 else
3027 return NS_ERROR_FAILURE;
3029 *_rval = mNestedLoopLevel;
3030 return NS_OK;
3033 /* hook attribute get/set functions */
3035 NS_IMETHODIMP
3036 jsdService::SetErrorHook (jsdIErrorHook *aHook)
3038 mErrorHook = aHook;
3040 /* if the debugger isn't initialized, that's all we can do for now. The
3041 * OnForRuntime() method will do the rest when the coast is clear.
3043 if (!mCx || mPauseLevel)
3044 return NS_OK;
3046 if (aHook)
3047 JSD_SetErrorReporter (mCx, jsds_ErrorHookProc, NULL);
3048 else
3049 JSD_SetErrorReporter (mCx, NULL, NULL);
3051 return NS_OK;
3054 NS_IMETHODIMP
3055 jsdService::GetErrorHook (jsdIErrorHook **aHook)
3057 *aHook = mErrorHook;
3058 NS_IF_ADDREF(*aHook);
3060 return NS_OK;
3063 NS_IMETHODIMP
3064 jsdService::SetBreakpointHook (jsdIExecutionHook *aHook)
3066 mBreakpointHook = aHook;
3067 return NS_OK;
3070 NS_IMETHODIMP
3071 jsdService::GetBreakpointHook (jsdIExecutionHook **aHook)
3073 *aHook = mBreakpointHook;
3074 NS_IF_ADDREF(*aHook);
3076 return NS_OK;
3079 NS_IMETHODIMP
3080 jsdService::SetDebugHook (jsdIExecutionHook *aHook)
3082 mDebugHook = aHook;
3084 /* if the debugger isn't initialized, that's all we can do for now. The
3085 * OnForRuntime() method will do the rest when the coast is clear.
3087 if (!mCx || mPauseLevel)
3088 return NS_OK;
3090 if (aHook)
3091 JSD_SetDebugBreakHook (mCx, jsds_ExecutionHookProc, NULL);
3092 else
3093 JSD_ClearDebugBreakHook (mCx);
3095 return NS_OK;
3098 NS_IMETHODIMP
3099 jsdService::GetDebugHook (jsdIExecutionHook **aHook)
3101 *aHook = mDebugHook;
3102 NS_IF_ADDREF(*aHook);
3104 return NS_OK;
3107 NS_IMETHODIMP
3108 jsdService::SetDebuggerHook (jsdIExecutionHook *aHook)
3110 mDebuggerHook = aHook;
3112 /* if the debugger isn't initialized, that's all we can do for now. The
3113 * OnForRuntime() method will do the rest when the coast is clear.
3115 if (!mCx || mPauseLevel)
3116 return NS_OK;
3118 if (aHook)
3119 JSD_SetDebuggerHook (mCx, jsds_ExecutionHookProc, NULL);
3120 else
3121 JSD_ClearDebuggerHook (mCx);
3123 return NS_OK;
3126 NS_IMETHODIMP
3127 jsdService::GetDebuggerHook (jsdIExecutionHook **aHook)
3129 *aHook = mDebuggerHook;
3130 NS_IF_ADDREF(*aHook);
3132 return NS_OK;
3135 NS_IMETHODIMP
3136 jsdService::SetInterruptHook (jsdIExecutionHook *aHook)
3138 mInterruptHook = aHook;
3140 /* if the debugger isn't initialized, that's all we can do for now. The
3141 * OnForRuntime() method will do the rest when the coast is clear.
3143 if (!mCx || mPauseLevel)
3144 return NS_OK;
3146 if (aHook)
3147 JSD_SetInterruptHook (mCx, jsds_ExecutionHookProc, NULL);
3148 else
3149 JSD_ClearInterruptHook (mCx);
3151 return NS_OK;
3154 NS_IMETHODIMP
3155 jsdService::GetInterruptHook (jsdIExecutionHook **aHook)
3157 *aHook = mInterruptHook;
3158 NS_IF_ADDREF(*aHook);
3160 return NS_OK;
3163 NS_IMETHODIMP
3164 jsdService::SetScriptHook (jsdIScriptHook *aHook)
3166 mScriptHook = aHook;
3168 /* if the debugger isn't initialized, that's all we can do for now. The
3169 * OnForRuntime() method will do the rest when the coast is clear.
3171 if (!mCx || mPauseLevel)
3172 return NS_OK;
3174 if (aHook)
3175 JSD_SetScriptHook (mCx, jsds_ScriptHookProc, NULL);
3176 /* we can't unset it if !aHook, because we still need to see script
3177 * deletes in order to Release the jsdIScripts held in JSDScript
3178 * private data. */
3179 return NS_OK;
3182 NS_IMETHODIMP
3183 jsdService::GetScriptHook (jsdIScriptHook **aHook)
3185 *aHook = mScriptHook;
3186 NS_IF_ADDREF(*aHook);
3188 return NS_OK;
3191 NS_IMETHODIMP
3192 jsdService::SetThrowHook (jsdIExecutionHook *aHook)
3194 mThrowHook = aHook;
3196 /* if the debugger isn't initialized, that's all we can do for now. The
3197 * OnForRuntime() method will do the rest when the coast is clear.
3199 if (!mCx || mPauseLevel)
3200 return NS_OK;
3202 if (aHook)
3203 JSD_SetThrowHook (mCx, jsds_ExecutionHookProc, NULL);
3204 else
3205 JSD_ClearThrowHook (mCx);
3207 return NS_OK;
3210 NS_IMETHODIMP
3211 jsdService::GetThrowHook (jsdIExecutionHook **aHook)
3213 *aHook = mThrowHook;
3214 NS_IF_ADDREF(*aHook);
3216 return NS_OK;
3219 NS_IMETHODIMP
3220 jsdService::SetTopLevelHook (jsdICallHook *aHook)
3222 mTopLevelHook = aHook;
3224 /* if the debugger isn't initialized, that's all we can do for now. The
3225 * OnForRuntime() method will do the rest when the coast is clear.
3227 if (!mCx || mPauseLevel)
3228 return NS_OK;
3230 if (aHook)
3231 JSD_SetTopLevelHook (mCx, jsds_CallHookProc, NULL);
3232 else
3233 JSD_ClearTopLevelHook (mCx);
3235 return NS_OK;
3238 NS_IMETHODIMP
3239 jsdService::GetTopLevelHook (jsdICallHook **aHook)
3241 *aHook = mTopLevelHook;
3242 NS_IF_ADDREF(*aHook);
3244 return NS_OK;
3247 NS_IMETHODIMP
3248 jsdService::SetFunctionHook (jsdICallHook *aHook)
3250 mFunctionHook = aHook;
3252 /* if the debugger isn't initialized, that's all we can do for now. The
3253 * OnForRuntime() method will do the rest when the coast is clear.
3255 if (!mCx || mPauseLevel)
3256 return NS_OK;
3258 if (aHook)
3259 JSD_SetFunctionHook (mCx, jsds_CallHookProc, NULL);
3260 else
3261 JSD_ClearFunctionHook (mCx);
3263 return NS_OK;
3266 NS_IMETHODIMP
3267 jsdService::GetFunctionHook (jsdICallHook **aHook)
3269 *aHook = mFunctionHook;
3270 NS_IF_ADDREF(*aHook);
3272 return NS_OK;
3275 /* virtual */
3276 jsdService::~jsdService()
3278 ClearFilters();
3279 mErrorHook = nsnull;
3280 mBreakpointHook = nsnull;
3281 mDebugHook = nsnull;
3282 mDebuggerHook = nsnull;
3283 mInterruptHook = nsnull;
3284 mScriptHook = nsnull;
3285 mThrowHook = nsnull;
3286 mTopLevelHook = nsnull;
3287 mFunctionHook = nsnull;
3288 gGCStatus = JSGC_END;
3289 Off();
3290 gJsds = nsnull;
3293 jsdService *
3294 jsdService::GetService ()
3296 if (!gJsds)
3297 gJsds = new jsdService();
3299 NS_IF_ADDREF(gJsds);
3300 return gJsds;
3303 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(jsdService, jsdService::GetService)
3305 /* app-start observer. turns on the debugger at app-start. this is inserted
3306 * and/or removed from the app-start category by the jsdService::initAtStartup
3307 * property.
3309 class jsdASObserver : public nsIObserver
3311 public:
3312 NS_DECL_ISUPPORTS
3313 NS_DECL_NSIOBSERVER
3315 jsdASObserver () {}
3318 NS_IMPL_THREADSAFE_ISUPPORTS1(jsdASObserver, nsIObserver)
3320 NS_IMETHODIMP
3321 jsdASObserver::Observe (nsISupports *aSubject, const char *aTopic,
3322 const PRUnichar *aData)
3324 nsresult rv;
3326 // Hmm. Why is the app-startup observer called multiple times?
3327 //NS_ASSERTION(!gJsds, "app startup observer called twice");
3328 nsCOMPtr<jsdIDebuggerService> jsds = do_GetService(jsdServiceCtrID, &rv);
3329 if (NS_FAILED(rv))
3330 return rv;
3332 PRBool on;
3333 rv = jsds->GetIsOn(&on);
3334 if (NS_FAILED(rv) || on)
3335 return rv;
3337 nsCOMPtr<nsIJSRuntimeService> rts = do_GetService(NS_JSRT_CTRID, &rv);
3338 if (NS_FAILED(rv))
3339 return rv;
3341 JSRuntime *rt;
3342 rts->GetRuntime (&rt);
3343 if (NS_FAILED(rv))
3344 return rv;
3346 rv = jsds->OnForRuntime(rt);
3347 if (NS_FAILED(rv))
3348 return rv;
3350 return jsds->SetFlags(JSD_DISABLE_OBJECT_TRACE);
3353 NS_GENERIC_FACTORY_CONSTRUCTOR(jsdASObserver)
3355 static const nsModuleComponentInfo components[] = {
3356 {"JSDService", JSDSERVICE_CID, jsdServiceCtrID, jsdServiceConstructor},
3357 {"JSDASObserver", JSDASO_CID, jsdARObserverCtrID, jsdASObserverConstructor}
3360 NS_IMPL_NSGETMODULE(JavaScript_Debugger, components)
3362 /********************************************************************************
3363 ********************************************************************************
3364 * graveyard
3367 #if 0
3368 /* Thread States */
3369 NS_IMPL_THREADSAFE_ISUPPORTS1(jsdThreadState, jsdIThreadState);
3371 NS_IMETHODIMP
3372 jsdThreadState::GetJSDContext(JSDContext **_rval)
3374 *_rval = mCx;
3375 return NS_OK;
3378 NS_IMETHODIMP
3379 jsdThreadState::GetJSDThreadState(JSDThreadState **_rval)
3381 *_rval = mThreadState;
3382 return NS_OK;
3385 NS_IMETHODIMP
3386 jsdThreadState::GetFrameCount (PRUint32 *_rval)
3388 *_rval = JSD_GetCountOfStackFrames (mCx, mThreadState);
3389 return NS_OK;
3392 NS_IMETHODIMP
3393 jsdThreadState::GetTopFrame (jsdIStackFrame **_rval)
3395 JSDStackFrameInfo *sfi = JSD_GetStackFrame (mCx, mThreadState);
3397 *_rval = jsdStackFrame::FromPtr (mCx, mThreadState, sfi);
3398 return NS_OK;
3401 NS_IMETHODIMP
3402 jsdThreadState::GetPendingException(jsdIValue **_rval)
3404 JSDValue *jsdv = JSD_GetException (mCx, mThreadState);
3406 *_rval = jsdValue::FromPtr (mCx, jsdv);
3407 return NS_OK;
3410 NS_IMETHODIMP
3411 jsdThreadState::SetPendingException(jsdIValue *aException)
3413 JSDValue *jsdv;
3415 nsresult rv = aException->GetJSDValue (&jsdv);
3416 if (NS_FAILED(rv))
3417 return NS_ERROR_FAILURE;
3419 if (!JSD_SetException (mCx, mThreadState, jsdv))
3420 return NS_ERROR_FAILURE;
3422 return NS_OK;
3425 #endif