Bug 440982 To avoid calling JS at unsafe times from JS_GC, jsds_ScriptHookProc should...
[mozilla-central.git] / js / jsd / jsd_xpc.cpp
blobd210fba914b667b837e333aeb1e77ae39d9d1697
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 static 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 nsCString urlPattern;
155 PatternType patternType;
156 PRUint32 startLine;
157 PRUint32 endLine;
158 } *gFilters = nsnull;
160 static struct LiveEphemeral *gLiveValues = nsnull;
161 static struct LiveEphemeral *gLiveProperties = nsnull;
162 static struct LiveEphemeral *gLiveContexts = nsnull;
163 static struct LiveEphemeral *gLiveStackFrames = nsnull;
165 /*******************************************************************************
166 * utility functions for ephemeral lists
167 *******************************************************************************/
168 already_AddRefed<jsdIEphemeral>
169 jsds_FindEphemeral (LiveEphemeral **listHead, void *key)
171 if (!*listHead)
172 return nsnull;
174 LiveEphemeral *lv_record =
175 reinterpret_cast<LiveEphemeral *>
176 (PR_NEXT_LINK(&(*listHead)->links));
179 if (lv_record->key == key)
181 NS_IF_ADDREF(lv_record->value);
182 return lv_record->value;
184 lv_record = reinterpret_cast<LiveEphemeral *>
185 (PR_NEXT_LINK(&lv_record->links));
187 while (lv_record != *listHead);
189 return nsnull;
192 void
193 jsds_InvalidateAllEphemerals (LiveEphemeral **listHead)
195 LiveEphemeral *lv_record =
196 reinterpret_cast<LiveEphemeral *>
197 (PR_NEXT_LINK(&(*listHead)->links));
198 while (*listHead)
200 LiveEphemeral *next =
201 reinterpret_cast<LiveEphemeral *>
202 (PR_NEXT_LINK(&lv_record->links));
203 lv_record->value->Invalidate();
204 lv_record = next;
208 void
209 jsds_InsertEphemeral (LiveEphemeral **listHead, LiveEphemeral *item)
211 if (*listHead) {
212 /* if the list exists, add to it */
213 PR_APPEND_LINK(&item->links, &(*listHead)->links);
214 } else {
215 /* otherwise create the list */
216 PR_INIT_CLIST(&item->links);
217 *listHead = item;
221 void
222 jsds_RemoveEphemeral (LiveEphemeral **listHead, LiveEphemeral *item)
224 LiveEphemeral *next = reinterpret_cast<LiveEphemeral *>
225 (PR_NEXT_LINK(&item->links));
227 if (next == item)
229 /* if the current item is also the next item, we're the only element,
230 * null out the list head */
231 NS_ASSERTION (*listHead == item,
232 "How could we not be the head of a one item list?");
233 *listHead = nsnull;
235 else if (item == *listHead)
237 /* otherwise, if we're currently the list head, change it */
238 *listHead = next;
241 PR_REMOVE_AND_INIT_LINK(&item->links);
244 /*******************************************************************************
245 * utility functions for filters
246 *******************************************************************************/
247 void
248 jsds_FreeFilter (FilterRecord *filter)
250 NS_IF_RELEASE (filter->filterObject);
251 delete filter;
254 /* copies appropriate |filter| attributes into |rec|.
255 * False return indicates failure, the contents of |rec| will not be changed.
257 PRBool
258 jsds_SyncFilter (FilterRecord *rec, jsdIFilter *filter)
260 NS_ASSERTION (rec, "jsds_SyncFilter without rec");
261 NS_ASSERTION (filter, "jsds_SyncFilter without filter");
263 JSObject *glob_proper = nsnull;
264 nsCOMPtr<nsISupports> glob;
265 nsresult rv = filter->GetGlobalObject(getter_AddRefs(glob));
266 if (NS_FAILED(rv))
267 return PR_FALSE;
268 if (glob) {
269 nsCOMPtr<nsIScriptGlobalObject> nsiglob = do_QueryInterface(glob);
270 if (nsiglob)
271 glob_proper = nsiglob->GetGlobalJSObject();
274 PRUint32 startLine;
275 rv = filter->GetStartLine(&startLine);
276 if (NS_FAILED(rv))
277 return PR_FALSE;
279 PRUint32 endLine;
280 rv = filter->GetStartLine(&endLine);
281 if (NS_FAILED(rv))
282 return PR_FALSE;
284 nsCAutoString urlPattern;
285 rv = filter->GetUrlPattern (urlPattern);
286 if (NS_FAILED(rv))
287 return PR_FALSE;
289 PRUint32 len = urlPattern.Length();
290 if (len) {
291 if (urlPattern[0] == '*') {
292 /* pattern starts with a *, shift all chars once to the left,
293 * including the trailing null. */
294 urlPattern = Substring(urlPattern, 1, len);
296 if (urlPattern[len - 2] == '*') {
297 /* pattern is in the format "*foo*", overwrite the final * with
298 * a null. */
299 urlPattern.Truncate(len - 2);
300 rec->patternType = ptContains;
301 } else {
302 /* pattern is in the format "*foo", just make a note of the
303 * new length. */
304 rec->patternType = ptEndsWith;
306 } else if (urlPattern[len - 1] == '*') {
307 /* pattern is in the format "foo*", overwrite the final * with a
308 * null. */
309 urlPattern.Truncate(len - 1);
310 rec->patternType = ptStartsWith;
311 } else {
312 /* pattern is in the format "foo". */
313 rec->patternType = ptEquals;
315 } else {
316 rec->patternType = ptIgnore;
319 /* we got everything we need without failing, now copy it into rec. */
321 if (rec->filterObject != filter) {
322 NS_IF_RELEASE(rec->filterObject);
323 NS_ADDREF(filter);
324 rec->filterObject = filter;
327 rec->glob = glob_proper;
329 rec->startLine = startLine;
330 rec->endLine = endLine;
332 rec->urlPattern = urlPattern;
334 return PR_TRUE;
338 FilterRecord *
339 jsds_FindFilter (jsdIFilter *filter)
341 if (!gFilters)
342 return nsnull;
344 FilterRecord *current = gFilters;
346 do {
347 if (current->filterObject == filter)
348 return current;
349 current = reinterpret_cast<FilterRecord *>
350 (PR_NEXT_LINK(&current->links));
351 } while (current != gFilters);
353 return nsnull;
356 /* returns true if the hook should be executed. */
357 PRBool
358 jsds_FilterHook (JSDContext *jsdc, JSDThreadState *state)
360 JSContext *cx = JSD_GetJSContext (jsdc, state);
361 void *glob = static_cast<void *>(JS_GetGlobalObject (cx));
363 if (!glob) {
364 NS_WARNING("No global in threadstate");
365 return PR_FALSE;
368 JSDStackFrameInfo *frame = JSD_GetStackFrame (jsdc, state);
370 if (!frame) {
371 NS_WARNING("No frame in threadstate");
372 return PR_FALSE;
375 JSDScript *script = JSD_GetScriptForStackFrame (jsdc, state, frame);
376 if (!script)
377 return PR_TRUE;
379 jsuint pc = JSD_GetPCForStackFrame (jsdc, state, frame);
381 nsDependentCString url(JSD_GetScriptFilename (jsdc, script));
382 if (url.IsEmpty()) {
383 NS_WARNING ("Script with no filename");
384 return PR_FALSE;
387 if (!gFilters)
388 return PR_TRUE;
390 PRUint32 currentLine = JSD_GetClosestLine (jsdc, script, pc);
391 PRUint32 len = 0;
392 FilterRecord *currentFilter = gFilters;
393 do {
394 PRUint32 flags = 0;
395 nsresult rv = currentFilter->filterObject->GetFlags(&flags);
396 NS_ASSERTION(NS_SUCCEEDED(rv), "Error getting flags for filter");
397 if (flags & jsdIFilter::FLAG_ENABLED) {
398 /* if there is no glob, or the globs match */
399 if ((!currentFilter->glob || currentFilter->glob == glob) &&
400 /* and there is no start line, or the start line is before
401 * or equal to the current */
402 (!currentFilter->startLine ||
403 currentFilter->startLine <= currentLine) &&
404 /* and there is no end line, or the end line is after
405 * or equal to the current */
406 (!currentFilter->endLine ||
407 currentFilter->endLine >= currentLine)) {
408 /* then we're going to have to compare the url. */
409 if (currentFilter->patternType == ptIgnore)
410 return !!(flags & jsdIFilter::FLAG_PASS);
412 if (!len)
413 len = url.Length();
414 nsCString urlPattern = currentFilter->urlPattern;
415 PRUint32 patternLength = urlPattern.Length();
416 if (len >= patternLength) {
417 switch (currentFilter->patternType) {
418 case ptEquals:
419 if (urlPattern.Equals(url))
420 return !!(flags & jsdIFilter::FLAG_PASS);
421 break;
422 case ptStartsWith:
423 if (urlPattern.Equals(Substring(url, 0, patternLength)))
424 return !!(flags & jsdIFilter::FLAG_PASS);
425 break;
426 case ptEndsWith:
427 if (urlPattern.Equals(Substring(url, len - patternLength)))
428 return !!(flags & jsdIFilter::FLAG_PASS);
429 break;
430 case ptContains:
432 nsACString::const_iterator start, end;
433 url.BeginReading(start);
434 url.EndReading(end);
435 if (FindInReadable(currentFilter->urlPattern, start, end))
436 return !!(flags & jsdIFilter::FLAG_PASS);
438 break;
439 default:
440 NS_ASSERTION(0, "Invalid pattern type");
445 currentFilter = reinterpret_cast<FilterRecord *>
446 (PR_NEXT_LINK(&currentFilter->links));
447 } while (currentFilter != gFilters);
449 return PR_TRUE;
453 /*******************************************************************************
454 * c callbacks
455 *******************************************************************************/
457 static void
458 jsds_NotifyPendingDeadScripts (JSContext *cx)
460 #ifdef CAUTIOUS_SCRIPTHOOK
461 JSRuntime *rt = JS_GetRuntime(cx);
462 #endif
463 jsdService *jsds = gJsds;
465 nsCOMPtr<jsdIScriptHook> hook;
466 if (jsds) {
467 NS_ADDREF(jsds);
468 jsds->GetScriptHook (getter_AddRefs(hook));
469 jsds->Pause(nsnull);
472 DeadScript *deadScripts = gDeadScripts;
473 gDeadScripts = nsnull;
474 while (deadScripts) {
475 DeadScript *ds = deadScripts;
476 /* get next deleted script */
477 deadScripts = reinterpret_cast<DeadScript *>
478 (PR_NEXT_LINK(&ds->links));
479 if (deadScripts == ds)
480 deadScripts = nsnull;
482 if (hook)
484 /* tell the user this script has been destroyed */
485 #ifdef CAUTIOUS_SCRIPTHOOK
486 JS_UNKEEP_ATOMS(rt);
487 #endif
488 hook->OnScriptDestroyed (ds->script);
489 #ifdef CAUTIOUS_SCRIPTHOOK
490 JS_KEEP_ATOMS(rt);
491 #endif
494 /* take it out of the circular list */
495 PR_REMOVE_LINK(&ds->links);
497 /* addref came from the FromPtr call in jsds_ScriptHookProc */
498 NS_RELEASE(ds->script);
499 /* free the struct! */
500 PR_Free(ds);
503 if (jsds) {
504 jsds->UnPause(nsnull);
505 NS_RELEASE(jsds);
509 static JSBool
510 jsds_GCCallbackProc (JSContext *cx, JSGCStatus status)
512 #ifdef DEBUG_verbose
513 printf ("new gc status is %i\n", status);
514 #endif
515 if (status == JSGC_END) {
516 /* just to guard against reentering. */
517 gGCStatus = JSGC_BEGIN;
518 while (gDeadScripts)
519 jsds_NotifyPendingDeadScripts (cx);
522 gGCStatus = status;
523 if (gLastGCProc)
524 return gLastGCProc (cx, status);
526 return JS_TRUE;
529 static uintN
530 jsds_ErrorHookProc (JSDContext *jsdc, JSContext *cx, const char *message,
531 JSErrorReport *report, void *callerdata)
533 static PRBool running = PR_FALSE;
535 nsCOMPtr<jsdIErrorHook> hook;
536 gJsds->GetErrorHook(getter_AddRefs(hook));
537 if (!hook)
538 return JSD_ERROR_REPORTER_PASS_ALONG;
540 if (running)
541 return JSD_ERROR_REPORTER_PASS_ALONG;
543 running = PR_TRUE;
545 nsCOMPtr<jsdIValue> val;
546 if (JS_IsExceptionPending(cx)) {
547 jsval jv;
548 JS_GetPendingException(cx, &jv);
549 JSDValue *jsdv = JSD_NewValue (jsdc, jv);
550 val = getter_AddRefs(jsdValue::FromPtr(jsdc, jsdv));
553 nsCAutoString fileName;
554 PRUint32 line;
555 PRUint32 pos;
556 PRUint32 flags;
557 PRUint32 errnum;
558 PRBool rval;
559 if (report) {
560 fileName.Assign(report->filename);
561 line = report->lineno;
562 pos = report->tokenptr - report->linebuf;
563 flags = report->flags;
564 errnum = report->errorNumber;
566 else
568 line = 0;
569 pos = 0;
570 flags = 0;
571 errnum = 0;
574 gJsds->Pause(nsnull);
575 hook->OnError (nsDependentCString(message), fileName, line, pos, flags, errnum, val, &rval);
576 gJsds->UnPause(nsnull);
578 running = PR_FALSE;
579 if (!rval)
580 return JSD_ERROR_REPORTER_DEBUG;
582 return JSD_ERROR_REPORTER_PASS_ALONG;
585 static JSBool
586 jsds_CallHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate,
587 uintN type, void* callerdata)
589 nsCOMPtr<jsdICallHook> hook;
591 switch (type)
593 case JSD_HOOK_TOPLEVEL_START:
594 case JSD_HOOK_TOPLEVEL_END:
595 gJsds->GetTopLevelHook(getter_AddRefs(hook));
596 break;
598 case JSD_HOOK_FUNCTION_CALL:
599 case JSD_HOOK_FUNCTION_RETURN:
600 gJsds->GetFunctionHook(getter_AddRefs(hook));
601 break;
603 default:
604 NS_ASSERTION (0, "Unknown hook type.");
607 if (!hook)
608 return JS_TRUE;
610 if (!jsds_FilterHook (jsdc, jsdthreadstate))
611 return JS_FALSE;
613 JSDStackFrameInfo *native_frame = JSD_GetStackFrame (jsdc, jsdthreadstate);
614 nsCOMPtr<jsdIStackFrame> frame =
615 getter_AddRefs(jsdStackFrame::FromPtr(jsdc, jsdthreadstate,
616 native_frame));
617 gJsds->Pause(nsnull);
618 hook->OnCall(frame, type);
619 gJsds->UnPause(nsnull);
620 jsdStackFrame::InvalidateAll();
622 return JS_TRUE;
625 static PRUint32
626 jsds_ExecutionHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate,
627 uintN type, void* callerdata, jsval* rval)
629 nsCOMPtr<jsdIExecutionHook> hook(0);
630 PRUint32 hook_rv = JSD_HOOK_RETURN_CONTINUE;
631 nsCOMPtr<jsdIValue> js_rv;
633 switch (type)
635 case JSD_HOOK_INTERRUPTED:
636 gJsds->GetInterruptHook(getter_AddRefs(hook));
637 break;
638 case JSD_HOOK_DEBUG_REQUESTED:
639 gJsds->GetDebugHook(getter_AddRefs(hook));
640 break;
641 case JSD_HOOK_DEBUGGER_KEYWORD:
642 gJsds->GetDebuggerHook(getter_AddRefs(hook));
643 break;
644 case JSD_HOOK_BREAKPOINT:
646 /* we can't pause breakpoints the way we pause the other
647 * execution hooks (at least, not easily.) Instead we bail
648 * here if the service is paused. */
649 PRUint32 level;
650 gJsds->GetPauseDepth(&level);
651 if (!level)
652 gJsds->GetBreakpointHook(getter_AddRefs(hook));
654 break;
655 case JSD_HOOK_THROW:
657 hook_rv = JSD_HOOK_RETURN_CONTINUE_THROW;
658 gJsds->GetThrowHook(getter_AddRefs(hook));
659 if (hook) {
660 JSDValue *jsdv = JSD_GetException (jsdc, jsdthreadstate);
661 js_rv = getter_AddRefs(jsdValue::FromPtr (jsdc, jsdv));
663 break;
665 default:
666 NS_ASSERTION (0, "Unknown hook type.");
669 if (!hook)
670 return hook_rv;
672 if (!jsds_FilterHook (jsdc, jsdthreadstate))
673 return JSD_HOOK_RETURN_CONTINUE;
675 JSDStackFrameInfo *native_frame = JSD_GetStackFrame (jsdc, jsdthreadstate);
676 nsCOMPtr<jsdIStackFrame> frame =
677 getter_AddRefs(jsdStackFrame::FromPtr(jsdc, jsdthreadstate,
678 native_frame));
679 gJsds->Pause(nsnull);
680 jsdIValue *inout_rv = js_rv;
681 NS_IF_ADDREF(inout_rv);
682 hook->OnExecute (frame, type, &inout_rv, &hook_rv);
683 js_rv = inout_rv;
684 NS_IF_RELEASE(inout_rv);
685 gJsds->UnPause(nsnull);
686 jsdStackFrame::InvalidateAll();
688 if (hook_rv == JSD_HOOK_RETURN_RET_WITH_VAL ||
689 hook_rv == JSD_HOOK_RETURN_THROW_WITH_VAL) {
690 *rval = JSVAL_VOID;
691 if (js_rv) {
692 JSDValue *jsdv;
693 if (NS_SUCCEEDED(js_rv->GetJSDValue (&jsdv)))
694 *rval = JSD_GetValueWrappedJSVal(jsdc, jsdv);
698 return hook_rv;
701 static void
702 jsds_ScriptHookProc (JSDContext* jsdc, JSDScript* jsdscript, JSBool creating,
703 void* callerdata)
705 #ifdef CAUTIOUS_SCRIPTHOOK
706 JSContext *cx = JSD_GetDefaultJSContext(jsdc);
707 JSRuntime *rt = JS_GetRuntime(cx);
708 #endif
710 if (creating) {
711 nsCOMPtr<jsdIScriptHook> hook;
712 gJsds->GetScriptHook(getter_AddRefs(hook));
714 /* a script is being created */
715 if (!hook) {
716 /* nobody cares, just exit */
717 return;
720 nsCOMPtr<jsdIScript> script =
721 getter_AddRefs(jsdScript::FromPtr(jsdc, jsdscript));
722 #ifdef CAUTIOUS_SCRIPTHOOK
723 JS_UNKEEP_ATOMS(rt);
724 #endif
725 gJsds->Pause(nsnull);
726 hook->OnScriptCreated (script);
727 gJsds->UnPause(nsnull);
728 #ifdef CAUTIOUS_SCRIPTHOOK
729 JS_KEEP_ATOMS(rt);
730 #endif
731 } else {
732 /* a script is being destroyed. even if there is no registered hook
733 * we'll still need to invalidate the jsdIScript record, in order
734 * to remove the reference held in the JSDScript private data. */
735 nsCOMPtr<jsdIScript> jsdis =
736 static_cast<jsdIScript *>(JSD_GetScriptPrivate(jsdscript));
737 if (!jsdis)
738 return;
740 jsdis->Invalidate();
742 if (gGCStatus == JSGC_END) {
743 nsCOMPtr<jsdIScriptHook> hook;
744 gJsds->GetScriptHook(getter_AddRefs(hook));
745 if (!hook)
746 return;
748 /* if GC *isn't* running, we can tell the user about the script
749 * delete now. */
750 #ifdef CAUTIOUS_SCRIPTHOOK
751 JS_UNKEEP_ATOMS(rt);
752 #endif
754 gJsds->Pause(nsnull);
755 hook->OnScriptDestroyed (jsdis);
756 gJsds->UnPause(nsnull);
757 #ifdef CAUTIOUS_SCRIPTHOOK
758 JS_KEEP_ATOMS(rt);
759 #endif
760 } else {
761 /* if a GC *is* running, we've got to wait until it's done before
762 * we can execute any JS, so we queue the notification in a PRCList
763 * until GC tells us it's done. See jsds_GCCallbackProc(). */
764 DeadScript *ds = PR_NEW(DeadScript);
765 if (!ds)
766 return; /* NS_ERROR_OUT_OF_MEMORY */
768 ds->jsdc = jsdc;
769 ds->script = jsdis;
770 NS_ADDREF(ds->script);
771 if (gDeadScripts)
772 /* if the queue exists, add to it */
773 PR_APPEND_LINK(&ds->links, &gDeadScripts->links);
774 else {
775 /* otherwise create the queue */
776 PR_INIT_CLIST(&ds->links);
777 gDeadScripts = ds;
783 /*******************************************************************************
784 * reflected jsd data structures
785 *******************************************************************************/
787 /* Contexts */
789 NS_IMPL_THREADSAFE_ISUPPORTS2(jsdContext, jsdIContext, jsdIEphemeral);
791 NS_IMETHODIMP
792 jsdContext::GetJSDContext(JSDContext **_rval)
794 *_rval = mCx;
795 return NS_OK;
799 /* Objects */
800 NS_IMPL_THREADSAFE_ISUPPORTS1(jsdObject, jsdIObject)
802 NS_IMETHODIMP
803 jsdObject::GetJSDContext(JSDContext **_rval)
805 *_rval = mCx;
806 return NS_OK;
809 NS_IMETHODIMP
810 jsdObject::GetJSDObject(JSDObject **_rval)
812 *_rval = mObject;
813 return NS_OK;
816 NS_IMETHODIMP
817 jsdObject::GetCreatorURL(nsACString &_rval)
819 _rval.Assign(JSD_GetObjectNewURL(mCx, mObject));
820 return NS_OK;
823 NS_IMETHODIMP
824 jsdObject::GetCreatorLine(PRUint32 *_rval)
826 *_rval = JSD_GetObjectNewLineNumber(mCx, mObject);
827 return NS_OK;
830 NS_IMETHODIMP
831 jsdObject::GetConstructorURL(nsACString &_rval)
833 _rval.Assign(JSD_GetObjectConstructorURL(mCx, mObject));
834 return NS_OK;
837 NS_IMETHODIMP
838 jsdObject::GetConstructorLine(PRUint32 *_rval)
840 *_rval = JSD_GetObjectConstructorLineNumber(mCx, mObject);
841 return NS_OK;
844 NS_IMETHODIMP
845 jsdObject::GetValue(jsdIValue **_rval)
847 JSDValue *jsdv = JSD_GetValueForObject (mCx, mObject);
849 *_rval = jsdValue::FromPtr (mCx, jsdv);
850 return NS_OK;
853 /* Properties */
854 NS_IMPL_THREADSAFE_ISUPPORTS2(jsdProperty, jsdIProperty, jsdIEphemeral)
856 jsdProperty::jsdProperty (JSDContext *aCx, JSDProperty *aProperty) :
857 mCx(aCx), mProperty(aProperty)
859 DEBUG_CREATE ("jsdProperty", gPropertyCount);
860 mValid = (aCx && aProperty);
861 mLiveListEntry.value = this;
862 jsds_InsertEphemeral (&gLiveProperties, &mLiveListEntry);
865 jsdProperty::~jsdProperty ()
867 DEBUG_DESTROY ("jsdProperty", gPropertyCount);
868 if (mValid)
869 Invalidate();
872 NS_IMETHODIMP
873 jsdProperty::Invalidate()
875 ASSERT_VALID_EPHEMERAL;
876 mValid = PR_FALSE;
877 jsds_RemoveEphemeral (&gLiveProperties, &mLiveListEntry);
878 JSD_DropProperty (mCx, mProperty);
879 return NS_OK;
882 void
883 jsdProperty::InvalidateAll()
885 if (gLiveProperties)
886 jsds_InvalidateAllEphemerals (&gLiveProperties);
889 NS_IMETHODIMP
890 jsdProperty::GetJSDContext(JSDContext **_rval)
892 *_rval = mCx;
893 return NS_OK;
896 NS_IMETHODIMP
897 jsdProperty::GetJSDProperty(JSDProperty **_rval)
899 *_rval = mProperty;
900 return NS_OK;
903 NS_IMETHODIMP
904 jsdProperty::GetIsValid(PRBool *_rval)
906 *_rval = mValid;
907 return NS_OK;
910 NS_IMETHODIMP
911 jsdProperty::GetAlias(jsdIValue **_rval)
913 JSDValue *jsdv = JSD_GetPropertyValue (mCx, mProperty);
915 *_rval = jsdValue::FromPtr (mCx, jsdv);
916 return NS_OK;
919 NS_IMETHODIMP
920 jsdProperty::GetFlags(PRUint32 *_rval)
922 *_rval = JSD_GetPropertyFlags (mCx, mProperty);
923 return NS_OK;
926 NS_IMETHODIMP
927 jsdProperty::GetName(jsdIValue **_rval)
929 JSDValue *jsdv = JSD_GetPropertyName (mCx, mProperty);
931 *_rval = jsdValue::FromPtr (mCx, jsdv);
932 return NS_OK;
935 NS_IMETHODIMP
936 jsdProperty::GetValue(jsdIValue **_rval)
938 JSDValue *jsdv = JSD_GetPropertyValue (mCx, mProperty);
940 *_rval = jsdValue::FromPtr (mCx, jsdv);
941 return NS_OK;
944 NS_IMETHODIMP
945 jsdProperty::GetVarArgSlot(PRUint32 *_rval)
947 *_rval = JSD_GetPropertyVarArgSlot (mCx, mProperty);
948 return NS_OK;
951 /* Scripts */
952 NS_IMPL_THREADSAFE_ISUPPORTS2(jsdScript, jsdIScript, jsdIEphemeral)
954 jsdScript::jsdScript (JSDContext *aCx, JSDScript *aScript) : mValid(PR_FALSE),
955 mTag(0),
956 mCx(aCx),
957 mScript(aScript),
958 mFileName(0),
959 mFunctionName(0),
960 mBaseLineNumber(0),
961 mLineExtent(0),
962 mPPLineMap(0),
963 mFirstPC(0)
965 DEBUG_CREATE ("jsdScript", gScriptCount);
967 if (mScript) {
968 /* copy the script's information now, so we have it later, when it
969 * gets destroyed. */
970 JSD_LockScriptSubsystem(mCx);
971 mFileName = new nsCString(JSD_GetScriptFilename(mCx, mScript));
972 mFunctionName =
973 new nsCString(JSD_GetScriptFunctionName(mCx, mScript));
974 mBaseLineNumber = JSD_GetScriptBaseLineNumber(mCx, mScript);
975 mLineExtent = JSD_GetScriptLineExtent(mCx, mScript);
976 mFirstPC = JSD_GetClosestPC(mCx, mScript, 0);
977 JSD_UnlockScriptSubsystem(mCx);
979 mValid = PR_TRUE;
983 jsdScript::~jsdScript ()
985 DEBUG_DESTROY ("jsdScript", gScriptCount);
986 if (mFileName)
987 delete mFileName;
988 if (mFunctionName)
989 delete mFunctionName;
991 if (mPPLineMap)
992 PR_Free(mPPLineMap);
994 /* Invalidate() needs to be called to release an owning reference to
995 * ourselves, so if we got here without being invalidated, something
996 * has gone wrong with our ref count. */
997 NS_ASSERTION (!mValid, "Script destroyed without being invalidated.");
1001 * This method populates a line <-> pc map for a pretty printed version of this
1002 * script. It does this by decompiling, and then recompiling the script. The
1003 * resulting script is scanned for the line map, and then left as GC fodder.
1005 PCMapEntry *
1006 jsdScript::CreatePPLineMap()
1008 JSContext *cx = JSD_GetDefaultJSContext (mCx);
1009 JSAutoRequest ar(cx);
1010 JSObject *obj = JS_NewObject(cx, NULL, NULL, NULL);
1011 JSFunction *fun = JSD_GetJSFunction (mCx, mScript);
1012 JSScript *script;
1013 PRUint32 baseLine;
1014 PRBool scriptOwner = PR_FALSE;
1016 if (fun) {
1017 if (fun->nargs > 12)
1018 return nsnull;
1019 JSString *jsstr = JS_DecompileFunctionBody (cx, fun, 4);
1020 if (!jsstr)
1021 return nsnull;
1023 const char *argnames[] = {"arg1", "arg2", "arg3", "arg4",
1024 "arg5", "arg6", "arg7", "arg8",
1025 "arg9", "arg10", "arg11", "arg12" };
1026 fun = JS_CompileUCFunction (cx, obj, "ppfun", fun->nargs, argnames,
1027 JS_GetStringChars(jsstr),
1028 JS_GetStringLength(jsstr),
1029 "x-jsd:ppbuffer?type=function", 3);
1030 if (!fun || !(script = JS_GetFunctionScript(cx, fun)))
1031 return nsnull;
1032 baseLine = 3;
1033 } else {
1034 JSString *jsstr = JS_DecompileScript (cx, JSD_GetJSScript(mCx, mScript),
1035 "ppscript", 4);
1036 if (!jsstr)
1037 return nsnull;
1039 script = JS_CompileUCScript (cx, obj,
1040 JS_GetStringChars(jsstr),
1041 JS_GetStringLength(jsstr),
1042 "x-jsd:ppbuffer?type=script", 1);
1043 if (!script)
1044 return nsnull;
1045 scriptOwner = PR_TRUE;
1046 baseLine = 1;
1049 PRUint32 scriptExtent = JS_GetScriptLineExtent (cx, script);
1050 jsbytecode* firstPC = JS_LineNumberToPC (cx, script, 0);
1051 /* allocate worst case size of map (number of lines in script + 1
1052 * for our 0 record), we'll shrink it with a realloc later. */
1053 PCMapEntry *lineMap =
1054 static_cast<PCMapEntry *>
1055 (PR_Malloc((scriptExtent + 1) * sizeof (PCMapEntry)));
1056 PRUint32 lineMapSize = 0;
1058 if (lineMap) {
1059 for (PRUint32 line = baseLine; line < scriptExtent + baseLine; ++line) {
1060 jsbytecode* pc = JS_LineNumberToPC (cx, script, line);
1061 if (line == JS_PCToLineNumber (cx, script, pc)) {
1062 lineMap[lineMapSize].line = line;
1063 lineMap[lineMapSize].pc = pc - firstPC;
1064 ++lineMapSize;
1067 if (scriptExtent != lineMapSize) {
1068 lineMap =
1069 static_cast<PCMapEntry *>
1070 (PR_Realloc(mPPLineMap = lineMap,
1071 lineMapSize * sizeof(PCMapEntry)));
1072 if (!lineMap) {
1073 PR_Free(mPPLineMap);
1074 lineMapSize = 0;
1079 if (scriptOwner)
1080 JS_DestroyScript (cx, script);
1082 mPCMapSize = lineMapSize;
1083 return mPPLineMap = lineMap;
1086 PRUint32
1087 jsdScript::PPPcToLine (PRUint32 aPC)
1089 if (!mPPLineMap && !CreatePPLineMap())
1090 return 0;
1091 PRUint32 i;
1092 for (i = 1; i < mPCMapSize; ++i) {
1093 if (mPPLineMap[i].pc > aPC)
1094 return mPPLineMap[i - 1].line;
1097 return mPPLineMap[mPCMapSize - 1].line;
1100 PRUint32
1101 jsdScript::PPLineToPc (PRUint32 aLine)
1103 if (!mPPLineMap && !CreatePPLineMap())
1104 return 0;
1105 PRUint32 i;
1106 for (i = 1; i < mPCMapSize; ++i) {
1107 if (mPPLineMap[i].line > aLine)
1108 return mPPLineMap[i - 1].pc;
1111 return mPPLineMap[mPCMapSize - 1].pc;
1114 NS_IMETHODIMP
1115 jsdScript::GetJSDContext(JSDContext **_rval)
1117 ASSERT_VALID_EPHEMERAL;
1118 *_rval = mCx;
1119 return NS_OK;
1122 NS_IMETHODIMP
1123 jsdScript::GetJSDScript(JSDScript **_rval)
1125 ASSERT_VALID_EPHEMERAL;
1126 *_rval = mScript;
1127 return NS_OK;
1130 NS_IMETHODIMP
1131 jsdScript::GetVersion (PRInt32 *_rval)
1133 ASSERT_VALID_EPHEMERAL;
1134 JSContext *cx = JSD_GetDefaultJSContext (mCx);
1135 JSScript *script = JSD_GetJSScript(mCx, mScript);
1136 *_rval = static_cast<PRInt32>(JS_GetScriptVersion(cx, script));
1137 return NS_OK;
1140 NS_IMETHODIMP
1141 jsdScript::GetTag(PRUint32 *_rval)
1143 if (!mTag)
1144 mTag = ++jsdScript::LastTag;
1146 *_rval = mTag;
1147 return NS_OK;
1150 NS_IMETHODIMP
1151 jsdScript::Invalidate()
1153 ASSERT_VALID_EPHEMERAL;
1154 mValid = PR_FALSE;
1156 /* release the addref we do in FromPtr */
1157 jsdIScript *script = static_cast<jsdIScript *>
1158 (JSD_GetScriptPrivate(mScript));
1159 NS_ASSERTION (script == this, "That's not my script!");
1160 NS_RELEASE(script);
1161 JSD_SetScriptPrivate(mScript, NULL);
1162 return NS_OK;
1165 void
1166 jsdScript::InvalidateAll ()
1168 JSDContext *cx;
1169 if (NS_FAILED(gJsds->GetJSDContext (&cx)))
1170 return;
1172 JSDScript *script;
1173 JSDScript *iter = NULL;
1175 JSD_LockScriptSubsystem(cx);
1176 while((script = JSD_IterateScripts(cx, &iter)) != NULL) {
1177 nsCOMPtr<jsdIScript> jsdis =
1178 static_cast<jsdIScript *>(JSD_GetScriptPrivate(script));
1179 if (jsdis)
1180 jsdis->Invalidate();
1182 JSD_UnlockScriptSubsystem(cx);
1185 NS_IMETHODIMP
1186 jsdScript::GetIsValid(PRBool *_rval)
1188 *_rval = mValid;
1189 return NS_OK;
1192 NS_IMETHODIMP
1193 jsdScript::SetFlags(PRUint32 flags)
1195 ASSERT_VALID_EPHEMERAL;
1196 JSD_SetScriptFlags(mCx, mScript, flags);
1197 return NS_OK;
1200 NS_IMETHODIMP
1201 jsdScript::GetFlags(PRUint32 *_rval)
1203 ASSERT_VALID_EPHEMERAL;
1204 *_rval = JSD_GetScriptFlags(mCx, mScript);
1205 return NS_OK;
1208 NS_IMETHODIMP
1209 jsdScript::GetFileName(nsACString &_rval)
1211 _rval.Assign(*mFileName);
1212 return NS_OK;
1215 NS_IMETHODIMP
1216 jsdScript::GetFunctionName(nsACString &_rval)
1218 _rval.Assign(*mFunctionName);
1219 return NS_OK;
1222 NS_IMETHODIMP
1223 jsdScript::GetFunctionObject(jsdIValue **_rval)
1225 JSFunction *fun = JSD_GetJSFunction(mCx, mScript);
1226 if (!fun)
1227 return NS_ERROR_NOT_AVAILABLE;
1229 JSObject *obj = JS_GetFunctionObject(fun);
1230 if (!obj)
1231 return NS_ERROR_FAILURE;
1233 JSDContext *cx;
1234 if (NS_FAILED(gJsds->GetJSDContext (&cx)))
1235 return NS_ERROR_NOT_INITIALIZED;
1237 JSDValue *jsdv = JSD_NewValue(cx, OBJECT_TO_JSVAL(obj));
1238 if (!jsdv)
1239 return NS_ERROR_OUT_OF_MEMORY;
1241 *_rval = jsdValue::FromPtr(cx, jsdv);
1242 if (!*_rval) {
1243 JSD_DropValue(cx, jsdv);
1244 return NS_ERROR_OUT_OF_MEMORY;
1247 return NS_OK;
1250 NS_IMETHODIMP
1251 jsdScript::GetFunctionSource(nsAString & aFunctionSource)
1253 ASSERT_VALID_EPHEMERAL;
1254 JSContext *cx = JSD_GetDefaultJSContext (mCx);
1255 if (!cx) {
1256 NS_WARNING("No default context !?");
1257 return NS_ERROR_FAILURE;
1259 JSFunction *fun = JSD_GetJSFunction (mCx, mScript);
1261 JSAutoRequest ar(cx);
1263 JSString *jsstr;
1264 if (fun)
1265 jsstr = JS_DecompileFunction (cx, fun, 4);
1266 else {
1267 JSScript *script = JSD_GetJSScript (mCx, mScript);
1268 jsstr = JS_DecompileScript (cx, script, "ppscript", 4);
1270 if (!jsstr)
1271 return NS_ERROR_FAILURE;
1273 aFunctionSource =
1274 nsDependentString(
1275 reinterpret_cast<PRUnichar*>(JS_GetStringChars(jsstr)),
1276 JS_GetStringLength(jsstr));
1277 return NS_OK;
1280 NS_IMETHODIMP
1281 jsdScript::GetBaseLineNumber(PRUint32 *_rval)
1283 *_rval = mBaseLineNumber;
1284 return NS_OK;
1287 NS_IMETHODIMP
1288 jsdScript::GetLineExtent(PRUint32 *_rval)
1290 *_rval = mLineExtent;
1291 return NS_OK;
1294 NS_IMETHODIMP
1295 jsdScript::GetCallCount(PRUint32 *_rval)
1297 ASSERT_VALID_EPHEMERAL;
1298 *_rval = JSD_GetScriptCallCount (mCx, mScript);
1299 return NS_OK;
1302 NS_IMETHODIMP
1303 jsdScript::GetMaxRecurseDepth(PRUint32 *_rval)
1305 ASSERT_VALID_EPHEMERAL;
1306 *_rval = JSD_GetScriptMaxRecurseDepth (mCx, mScript);
1307 return NS_OK;
1310 NS_IMETHODIMP
1311 jsdScript::GetMinExecutionTime(double *_rval)
1313 ASSERT_VALID_EPHEMERAL;
1314 *_rval = JSD_GetScriptMinExecutionTime (mCx, mScript);
1315 return NS_OK;
1318 NS_IMETHODIMP
1319 jsdScript::GetMaxExecutionTime(double *_rval)
1321 ASSERT_VALID_EPHEMERAL;
1322 *_rval = JSD_GetScriptMaxExecutionTime (mCx, mScript);
1323 return NS_OK;
1326 NS_IMETHODIMP
1327 jsdScript::GetTotalExecutionTime(double *_rval)
1329 ASSERT_VALID_EPHEMERAL;
1330 *_rval = JSD_GetScriptTotalExecutionTime (mCx, mScript);
1331 return NS_OK;
1334 NS_IMETHODIMP
1335 jsdScript::GetMinOwnExecutionTime(double *_rval)
1337 ASSERT_VALID_EPHEMERAL;
1338 *_rval = JSD_GetScriptMinOwnExecutionTime (mCx, mScript);
1339 return NS_OK;
1342 NS_IMETHODIMP
1343 jsdScript::GetMaxOwnExecutionTime(double *_rval)
1345 ASSERT_VALID_EPHEMERAL;
1346 *_rval = JSD_GetScriptMaxOwnExecutionTime (mCx, mScript);
1347 return NS_OK;
1350 NS_IMETHODIMP
1351 jsdScript::GetTotalOwnExecutionTime(double *_rval)
1353 ASSERT_VALID_EPHEMERAL;
1354 *_rval = JSD_GetScriptTotalOwnExecutionTime (mCx, mScript);
1355 return NS_OK;
1358 NS_IMETHODIMP
1359 jsdScript::ClearProfileData()
1361 ASSERT_VALID_EPHEMERAL;
1362 JSD_ClearScriptProfileData(mCx, mScript);
1363 return NS_OK;
1366 NS_IMETHODIMP
1367 jsdScript::PcToLine(PRUint32 aPC, PRUint32 aPcmap, PRUint32 *_rval)
1369 ASSERT_VALID_EPHEMERAL;
1370 if (aPcmap == PCMAP_SOURCETEXT) {
1371 *_rval = JSD_GetClosestLine (mCx, mScript, mFirstPC + aPC);
1372 } else if (aPcmap == PCMAP_PRETTYPRINT) {
1373 *_rval = PPPcToLine(aPC);
1374 } else {
1375 return NS_ERROR_INVALID_ARG;
1378 return NS_OK;
1381 NS_IMETHODIMP
1382 jsdScript::LineToPc(PRUint32 aLine, PRUint32 aPcmap, PRUint32 *_rval)
1384 ASSERT_VALID_EPHEMERAL;
1385 if (aPcmap == PCMAP_SOURCETEXT) {
1386 jsuword pc = JSD_GetClosestPC (mCx, mScript, aLine);
1387 *_rval = pc - mFirstPC;
1388 } else if (aPcmap == PCMAP_PRETTYPRINT) {
1389 *_rval = PPLineToPc(aLine);
1390 } else {
1391 return NS_ERROR_INVALID_ARG;
1394 return NS_OK;
1397 NS_IMETHODIMP
1398 jsdScript::IsLineExecutable(PRUint32 aLine, PRUint32 aPcmap, PRBool *_rval)
1400 ASSERT_VALID_EPHEMERAL;
1401 if (aPcmap == PCMAP_SOURCETEXT) {
1402 jsuword pc = JSD_GetClosestPC (mCx, mScript, aLine);
1403 *_rval = (aLine == JSD_GetClosestLine (mCx, mScript, pc));
1404 } else if (aPcmap == PCMAP_PRETTYPRINT) {
1405 if (!mPPLineMap && !CreatePPLineMap())
1406 return NS_ERROR_OUT_OF_MEMORY;
1407 *_rval = PR_FALSE;
1408 for (PRUint32 i = 0; i < mPCMapSize; ++i) {
1409 if (mPPLineMap[i].line >= aLine) {
1410 *_rval = (mPPLineMap[i].line == aLine);
1411 break;
1414 } else {
1415 return NS_ERROR_INVALID_ARG;
1418 return NS_OK;
1421 NS_IMETHODIMP
1422 jsdScript::SetBreakpoint(PRUint32 aPC)
1424 ASSERT_VALID_EPHEMERAL;
1425 jsuword pc = mFirstPC + aPC;
1426 JSD_SetExecutionHook (mCx, mScript, pc, jsds_ExecutionHookProc,
1427 reinterpret_cast<void *>(PRIVATE_TO_JSVAL(NULL)));
1428 return NS_OK;
1431 NS_IMETHODIMP
1432 jsdScript::ClearBreakpoint(PRUint32 aPC)
1434 ASSERT_VALID_EPHEMERAL;
1435 jsuword pc = mFirstPC + aPC;
1436 JSD_ClearExecutionHook (mCx, mScript, pc);
1437 return NS_OK;
1440 NS_IMETHODIMP
1441 jsdScript::ClearAllBreakpoints()
1443 ASSERT_VALID_EPHEMERAL;
1444 JSD_LockScriptSubsystem(mCx);
1445 JSD_ClearAllExecutionHooksForScript (mCx, mScript);
1446 JSD_UnlockScriptSubsystem(mCx);
1447 return NS_OK;
1450 /* Contexts */
1451 NS_IMPL_THREADSAFE_ISUPPORTS2(jsdContext, jsdIContext, jsdIEphemeral)
1453 jsdIContext *
1454 jsdContext::FromPtr (JSDContext *aJSDCx, JSContext *aJSCx)
1456 if (!aJSDCx || !aJSCx)
1457 return nsnull;
1459 nsCOMPtr<jsdIContext> jsdicx;
1460 nsCOMPtr<jsdIEphemeral> eph =
1461 jsds_FindEphemeral (&gLiveContexts, static_cast<void *>(aJSCx));
1462 if (eph)
1464 jsdicx = do_QueryInterface(eph);
1466 else
1468 nsCOMPtr<nsISupports> iscx;
1469 if (JS_GetOptions(aJSCx) & JSOPTION_PRIVATE_IS_NSISUPPORTS)
1470 iscx = static_cast<nsISupports *>(JS_GetContextPrivate(aJSCx));
1471 jsdicx = new jsdContext (aJSDCx, aJSCx, iscx);
1474 jsdIContext *ctx = nsnull;
1475 jsdicx.swap(ctx);
1476 return ctx;
1479 jsdContext::jsdContext (JSDContext *aJSDCx, JSContext *aJSCx,
1480 nsISupports *aISCx) : mValid(PR_TRUE), mTag(0),
1481 mJSDCx(aJSDCx),
1482 mJSCx(aJSCx), mISCx(aISCx)
1484 DEBUG_CREATE ("jsdContext", gContextCount);
1485 mLiveListEntry.value = this;
1486 mLiveListEntry.key = static_cast<void *>(aJSCx);
1487 jsds_InsertEphemeral (&gLiveContexts, &mLiveListEntry);
1490 jsdContext::~jsdContext()
1492 DEBUG_DESTROY ("jsdContext", gContextCount);
1493 if (mValid)
1495 /* call Invalidate() to take ourselves out of the live list */
1496 Invalidate();
1500 NS_IMETHODIMP
1501 jsdContext::GetIsValid(PRBool *_rval)
1503 *_rval = mValid;
1504 return NS_OK;
1507 NS_IMETHODIMP
1508 jsdContext::Invalidate()
1510 ASSERT_VALID_EPHEMERAL;
1511 mValid = PR_FALSE;
1512 jsds_RemoveEphemeral (&gLiveContexts, &mLiveListEntry);
1513 return NS_OK;
1516 void
1517 jsdContext::InvalidateAll()
1519 if (gLiveContexts)
1520 jsds_InvalidateAllEphemerals (&gLiveContexts);
1523 NS_IMETHODIMP
1524 jsdContext::GetJSContext(JSContext **_rval)
1526 ASSERT_VALID_EPHEMERAL;
1527 *_rval = mJSCx;
1528 return NS_OK;
1531 NS_IMETHODIMP
1532 jsdContext::GetOptions(PRUint32 *_rval)
1534 ASSERT_VALID_EPHEMERAL;
1535 *_rval = JS_GetOptions(mJSCx);
1536 return NS_OK;
1539 NS_IMETHODIMP
1540 jsdContext::SetOptions(PRUint32 options)
1542 ASSERT_VALID_EPHEMERAL;
1543 PRUint32 lastOptions = JS_GetOptions(mJSCx);
1545 /* don't let users change this option, they'd just be shooting themselves
1546 * in the foot. */
1547 if ((options ^ lastOptions) & JSOPTION_PRIVATE_IS_NSISUPPORTS)
1548 return NS_ERROR_ILLEGAL_VALUE;
1550 JS_SetOptions(mJSCx, options);
1551 return NS_OK;
1554 NS_IMETHODIMP
1555 jsdContext::GetPrivateData(nsISupports **_rval)
1557 ASSERT_VALID_EPHEMERAL;
1558 PRUint32 options = JS_GetOptions(mJSCx);
1559 if (options & JSOPTION_PRIVATE_IS_NSISUPPORTS)
1561 *_rval = static_cast<nsISupports*>(JS_GetContextPrivate(mJSCx));
1562 NS_IF_ADDREF(*_rval);
1564 else
1566 *_rval = nsnull;
1569 return NS_OK;
1572 NS_IMETHODIMP
1573 jsdContext::GetWrappedContext(nsISupports **_rval)
1575 ASSERT_VALID_EPHEMERAL;
1576 NS_IF_ADDREF(*_rval = mISCx);
1577 return NS_OK;
1580 NS_IMETHODIMP
1581 jsdContext::GetTag(PRUint32 *_rval)
1583 ASSERT_VALID_EPHEMERAL;
1584 if (!mTag)
1585 mTag = ++jsdContext::LastTag;
1587 *_rval = mTag;
1588 return NS_OK;
1591 NS_IMETHODIMP
1592 jsdContext::GetVersion (PRInt32 *_rval)
1594 ASSERT_VALID_EPHEMERAL;
1595 *_rval = static_cast<PRInt32>(JS_GetVersion(mJSCx));
1596 return NS_OK;
1599 NS_IMETHODIMP
1600 jsdContext::SetVersion (PRInt32 id)
1602 ASSERT_VALID_EPHEMERAL;
1603 JSVersion ver = static_cast<JSVersion>(id);
1604 JS_SetVersion(mJSCx, ver);
1605 return NS_OK;
1608 NS_IMETHODIMP
1609 jsdContext::GetGlobalObject (jsdIValue **_rval)
1611 ASSERT_VALID_EPHEMERAL;
1612 JSObject *glob = JS_GetGlobalObject(mJSCx);
1613 JSDValue *jsdv = JSD_NewValue (mJSDCx, OBJECT_TO_JSVAL(glob));
1614 if (!jsdv)
1615 return NS_ERROR_FAILURE;
1616 *_rval = jsdValue::FromPtr (mJSDCx, jsdv);
1617 if (!*_rval)
1618 return NS_ERROR_FAILURE;
1619 return NS_OK;
1622 NS_IMETHODIMP
1623 jsdContext::GetScriptsEnabled (PRBool *_rval)
1625 ASSERT_VALID_EPHEMERAL;
1626 if (!mISCx) {
1627 *_rval = PR_TRUE;
1628 return NS_OK;
1631 nsCOMPtr<nsIScriptContext> context = do_QueryInterface(mISCx);
1632 if (!context)
1633 return NS_ERROR_NO_INTERFACE;
1635 *_rval = context->GetScriptsEnabled();
1637 return NS_OK;
1640 NS_IMETHODIMP
1641 jsdContext::SetScriptsEnabled (PRBool _rval)
1643 ASSERT_VALID_EPHEMERAL;
1644 if (!mISCx) {
1645 if (_rval)
1646 return NS_OK;
1647 return NS_ERROR_NO_INTERFACE;
1650 nsCOMPtr<nsIScriptContext> context = do_QueryInterface(mISCx);
1651 if (!context)
1652 return NS_ERROR_NO_INTERFACE;
1654 context->SetScriptsEnabled(_rval, PR_TRUE);
1656 return NS_OK;
1659 /* Stack Frames */
1660 NS_IMPL_THREADSAFE_ISUPPORTS2(jsdStackFrame, jsdIStackFrame, jsdIEphemeral)
1662 jsdStackFrame::jsdStackFrame (JSDContext *aCx, JSDThreadState *aThreadState,
1663 JSDStackFrameInfo *aStackFrameInfo) :
1664 mCx(aCx), mThreadState(aThreadState), mStackFrameInfo(aStackFrameInfo)
1666 DEBUG_CREATE ("jsdStackFrame", gFrameCount);
1667 mValid = (aCx && aThreadState && aStackFrameInfo);
1668 if (mValid) {
1669 mLiveListEntry.key = aStackFrameInfo;
1670 mLiveListEntry.value = this;
1671 jsds_InsertEphemeral (&gLiveStackFrames, &mLiveListEntry);
1675 jsdStackFrame::~jsdStackFrame()
1677 DEBUG_DESTROY ("jsdStackFrame", gFrameCount);
1678 if (mValid)
1680 /* call Invalidate() to take ourselves out of the live list */
1681 Invalidate();
1685 jsdIStackFrame *
1686 jsdStackFrame::FromPtr (JSDContext *aCx, JSDThreadState *aThreadState,
1687 JSDStackFrameInfo *aStackFrameInfo)
1689 if (!aStackFrameInfo)
1690 return nsnull;
1692 jsdIStackFrame *rv;
1693 nsCOMPtr<jsdIStackFrame> frame;
1695 nsCOMPtr<jsdIEphemeral> eph =
1696 jsds_FindEphemeral (&gLiveStackFrames,
1697 reinterpret_cast<void *>(aStackFrameInfo));
1699 if (eph)
1701 frame = do_QueryInterface(eph);
1702 rv = frame;
1704 else
1706 rv = new jsdStackFrame (aCx, aThreadState, aStackFrameInfo);
1709 NS_IF_ADDREF(rv);
1710 return rv;
1713 NS_IMETHODIMP
1714 jsdStackFrame::Invalidate()
1716 ASSERT_VALID_EPHEMERAL;
1717 mValid = PR_FALSE;
1718 jsds_RemoveEphemeral (&gLiveStackFrames, &mLiveListEntry);
1719 return NS_OK;
1722 void
1723 jsdStackFrame::InvalidateAll()
1725 if (gLiveStackFrames)
1726 jsds_InvalidateAllEphemerals (&gLiveStackFrames);
1729 NS_IMETHODIMP
1730 jsdStackFrame::GetJSDContext(JSDContext **_rval)
1732 ASSERT_VALID_EPHEMERAL;
1733 *_rval = mCx;
1734 return NS_OK;
1737 NS_IMETHODIMP
1738 jsdStackFrame::GetJSDThreadState(JSDThreadState **_rval)
1740 ASSERT_VALID_EPHEMERAL;
1741 *_rval = mThreadState;
1742 return NS_OK;
1745 NS_IMETHODIMP
1746 jsdStackFrame::GetJSDStackFrameInfo(JSDStackFrameInfo **_rval)
1748 ASSERT_VALID_EPHEMERAL;
1749 *_rval = mStackFrameInfo;
1750 return NS_OK;
1753 NS_IMETHODIMP
1754 jsdStackFrame::GetIsValid(PRBool *_rval)
1756 *_rval = mValid;
1757 return NS_OK;
1760 NS_IMETHODIMP
1761 jsdStackFrame::GetCallingFrame(jsdIStackFrame **_rval)
1763 ASSERT_VALID_EPHEMERAL;
1764 JSDStackFrameInfo *sfi = JSD_GetCallingStackFrame (mCx, mThreadState,
1765 mStackFrameInfo);
1766 *_rval = jsdStackFrame::FromPtr (mCx, mThreadState, sfi);
1767 return NS_OK;
1770 NS_IMETHODIMP
1771 jsdStackFrame::GetExecutionContext(jsdIContext **_rval)
1773 ASSERT_VALID_EPHEMERAL;
1774 JSContext *cx = JSD_GetJSContext (mCx, mThreadState);
1775 *_rval = jsdContext::FromPtr (mCx, cx);
1776 return NS_OK;
1779 NS_IMETHODIMP
1780 jsdStackFrame::GetFunctionName(nsACString &_rval)
1782 ASSERT_VALID_EPHEMERAL;
1783 _rval.Assign(JSD_GetNameForStackFrame(mCx, mThreadState, mStackFrameInfo));
1784 return NS_OK;
1787 NS_IMETHODIMP
1788 jsdStackFrame::GetIsNative(PRBool *_rval)
1790 ASSERT_VALID_EPHEMERAL;
1791 *_rval = JSD_IsStackFrameNative (mCx, mThreadState, mStackFrameInfo);
1792 return NS_OK;
1795 NS_IMETHODIMP
1796 jsdStackFrame::GetIsDebugger(PRBool *_rval)
1798 ASSERT_VALID_EPHEMERAL;
1799 *_rval = JSD_IsStackFrameDebugger (mCx, mThreadState, mStackFrameInfo);
1800 return NS_OK;
1803 NS_IMETHODIMP
1804 jsdStackFrame::GetIsConstructing(PRBool *_rval)
1806 ASSERT_VALID_EPHEMERAL;
1807 *_rval = JSD_IsStackFrameConstructing (mCx, mThreadState, mStackFrameInfo);
1808 return NS_OK;
1811 NS_IMETHODIMP
1812 jsdStackFrame::GetScript(jsdIScript **_rval)
1814 ASSERT_VALID_EPHEMERAL;
1815 JSDScript *script = JSD_GetScriptForStackFrame (mCx, mThreadState,
1816 mStackFrameInfo);
1817 *_rval = jsdScript::FromPtr (mCx, script);
1818 return NS_OK;
1821 NS_IMETHODIMP
1822 jsdStackFrame::GetPc(PRUint32 *_rval)
1824 ASSERT_VALID_EPHEMERAL;
1825 JSDScript *script = JSD_GetScriptForStackFrame (mCx, mThreadState,
1826 mStackFrameInfo);
1827 if (!script)
1828 return NS_ERROR_FAILURE;
1829 jsuword pcbase = JSD_GetClosestPC(mCx, script, 0);
1831 jsuword pc = JSD_GetPCForStackFrame (mCx, mThreadState, mStackFrameInfo);
1832 if (pc)
1833 *_rval = pc - pcbase;
1834 else
1835 *_rval = pcbase;
1836 return NS_OK;
1839 NS_IMETHODIMP
1840 jsdStackFrame::GetLine(PRUint32 *_rval)
1842 ASSERT_VALID_EPHEMERAL;
1843 JSDScript *script = JSD_GetScriptForStackFrame (mCx, mThreadState,
1844 mStackFrameInfo);
1845 if (script) {
1846 jsuword pc = JSD_GetPCForStackFrame (mCx, mThreadState, mStackFrameInfo);
1847 *_rval = JSD_GetClosestLine (mCx, script, pc);
1848 } else {
1849 if (!JSD_IsStackFrameNative(mCx, mThreadState, mStackFrameInfo))
1850 return NS_ERROR_FAILURE;
1851 *_rval = 1;
1853 return NS_OK;
1856 NS_IMETHODIMP
1857 jsdStackFrame::GetCallee(jsdIValue **_rval)
1859 ASSERT_VALID_EPHEMERAL;
1860 JSDValue *jsdv = JSD_GetCallObjectForStackFrame (mCx, mThreadState,
1861 mStackFrameInfo);
1863 *_rval = jsdValue::FromPtr (mCx, jsdv);
1864 return NS_OK;
1867 NS_IMETHODIMP
1868 jsdStackFrame::GetScope(jsdIValue **_rval)
1870 ASSERT_VALID_EPHEMERAL;
1871 JSDValue *jsdv = JSD_GetScopeChainForStackFrame (mCx, mThreadState,
1872 mStackFrameInfo);
1874 *_rval = jsdValue::FromPtr (mCx, jsdv);
1875 return NS_OK;
1878 NS_IMETHODIMP
1879 jsdStackFrame::GetThisValue(jsdIValue **_rval)
1881 ASSERT_VALID_EPHEMERAL;
1882 JSDValue *jsdv = JSD_GetThisForStackFrame (mCx, mThreadState,
1883 mStackFrameInfo);
1885 *_rval = jsdValue::FromPtr (mCx, jsdv);
1886 return NS_OK;
1890 NS_IMETHODIMP
1891 jsdStackFrame::Eval (const nsAString &bytes, const nsACString &fileName,
1892 PRUint32 line, jsdIValue **result, PRBool *_rval)
1894 ASSERT_VALID_EPHEMERAL;
1896 if (bytes.IsEmpty())
1897 return NS_ERROR_INVALID_ARG;
1899 // get pointer to buffer contained in |bytes|
1900 nsAString::const_iterator h;
1901 bytes.BeginReading(h);
1902 const jschar *char_bytes = reinterpret_cast<const jschar *>(h.get());
1904 JSExceptionState *estate = 0;
1905 jsval jv;
1907 JSContext *cx = JSD_GetJSContext (mCx, mThreadState);
1909 JSAutoRequest ar(cx);
1911 estate = JS_SaveExceptionState (cx);
1912 JS_ClearPendingException (cx);
1914 *_rval = JSD_AttemptUCScriptInStackFrame (mCx, mThreadState,
1915 mStackFrameInfo,
1916 char_bytes, bytes.Length(),
1917 PromiseFlatCString(fileName).get(),
1918 line, &jv);
1919 if (!*_rval) {
1920 if (JS_IsExceptionPending(cx))
1921 JS_GetPendingException (cx, &jv);
1922 else
1923 jv = 0;
1926 JS_RestoreExceptionState (cx, estate);
1928 JSDValue *jsdv = JSD_NewValue (mCx, jv);
1929 if (!jsdv)
1930 return NS_ERROR_FAILURE;
1931 *result = jsdValue::FromPtr (mCx, jsdv);
1932 if (!*result)
1933 return NS_ERROR_FAILURE;
1935 return NS_OK;
1938 /* Values */
1939 NS_IMPL_THREADSAFE_ISUPPORTS2(jsdValue, jsdIValue, jsdIEphemeral)
1940 jsdIValue *
1941 jsdValue::FromPtr (JSDContext *aCx, JSDValue *aValue)
1943 /* value will be dropped by te jsdValue destructor. */
1945 if (!aValue)
1946 return nsnull;
1948 jsdIValue *rv = new jsdValue (aCx, aValue);
1949 NS_IF_ADDREF(rv);
1950 return rv;
1953 jsdValue::jsdValue (JSDContext *aCx, JSDValue *aValue) : mValid(PR_TRUE),
1954 mCx(aCx),
1955 mValue(aValue)
1957 DEBUG_CREATE ("jsdValue", gValueCount);
1958 mLiveListEntry.value = this;
1959 jsds_InsertEphemeral (&gLiveValues, &mLiveListEntry);
1962 jsdValue::~jsdValue()
1964 DEBUG_DESTROY ("jsdValue", gValueCount);
1965 if (mValid)
1966 /* call Invalidate() to take ourselves out of the live list */
1967 Invalidate();
1970 NS_IMETHODIMP
1971 jsdValue::GetIsValid(PRBool *_rval)
1973 *_rval = mValid;
1974 return NS_OK;
1977 NS_IMETHODIMP
1978 jsdValue::Invalidate()
1980 ASSERT_VALID_EPHEMERAL;
1981 mValid = PR_FALSE;
1982 jsds_RemoveEphemeral (&gLiveValues, &mLiveListEntry);
1983 JSD_DropValue (mCx, mValue);
1984 return NS_OK;
1987 void
1988 jsdValue::InvalidateAll()
1990 if (gLiveValues)
1991 jsds_InvalidateAllEphemerals (&gLiveValues);
1994 NS_IMETHODIMP
1995 jsdValue::GetJSDContext(JSDContext **_rval)
1997 ASSERT_VALID_EPHEMERAL;
1998 *_rval = mCx;
1999 return NS_OK;
2002 NS_IMETHODIMP
2003 jsdValue::GetJSDValue (JSDValue **_rval)
2005 ASSERT_VALID_EPHEMERAL;
2006 *_rval = mValue;
2007 return NS_OK;
2010 NS_IMETHODIMP
2011 jsdValue::GetIsNative (PRBool *_rval)
2013 ASSERT_VALID_EPHEMERAL;
2014 *_rval = JSD_IsValueNative (mCx, mValue);
2015 return NS_OK;
2018 NS_IMETHODIMP
2019 jsdValue::GetIsNumber (PRBool *_rval)
2021 ASSERT_VALID_EPHEMERAL;
2022 *_rval = JSD_IsValueNumber (mCx, mValue);
2023 return NS_OK;
2026 NS_IMETHODIMP
2027 jsdValue::GetIsPrimitive (PRBool *_rval)
2029 ASSERT_VALID_EPHEMERAL;
2030 *_rval = JSD_IsValuePrimitive (mCx, mValue);
2031 return NS_OK;
2034 NS_IMETHODIMP
2035 jsdValue::GetJsType (PRUint32 *_rval)
2037 ASSERT_VALID_EPHEMERAL;
2038 jsval val;
2040 val = JSD_GetValueWrappedJSVal (mCx, mValue);
2042 if (JSVAL_IS_NULL(val))
2043 *_rval = TYPE_NULL;
2044 else if (JSVAL_IS_BOOLEAN(val))
2045 *_rval = TYPE_BOOLEAN;
2046 else if (JSVAL_IS_DOUBLE(val))
2047 *_rval = TYPE_DOUBLE;
2048 else if (JSVAL_IS_INT(val))
2049 *_rval = TYPE_INT;
2050 else if (JSVAL_IS_STRING(val))
2051 *_rval = TYPE_STRING;
2052 else if (JSVAL_IS_VOID(val))
2053 *_rval = TYPE_VOID;
2054 else if (JSD_IsValueFunction (mCx, mValue))
2055 *_rval = TYPE_FUNCTION;
2056 else if (JSVAL_IS_OBJECT(val))
2057 *_rval = TYPE_OBJECT;
2058 else
2059 NS_ASSERTION (0, "Value has no discernible type.");
2061 return NS_OK;
2064 NS_IMETHODIMP
2065 jsdValue::GetJsPrototype (jsdIValue **_rval)
2067 ASSERT_VALID_EPHEMERAL;
2068 JSDValue *jsdv = JSD_GetValuePrototype (mCx, mValue);
2069 *_rval = jsdValue::FromPtr (mCx, jsdv);
2070 return NS_OK;
2073 NS_IMETHODIMP
2074 jsdValue::GetJsParent (jsdIValue **_rval)
2076 ASSERT_VALID_EPHEMERAL;
2077 JSDValue *jsdv = JSD_GetValueParent (mCx, mValue);
2078 *_rval = jsdValue::FromPtr (mCx, jsdv);
2079 return NS_OK;
2082 NS_IMETHODIMP
2083 jsdValue::GetJsClassName(nsACString &_rval)
2085 ASSERT_VALID_EPHEMERAL;
2086 _rval.Assign(JSD_GetValueClassName(mCx, mValue));
2088 return NS_OK;
2091 NS_IMETHODIMP
2092 jsdValue::GetJsConstructor (jsdIValue **_rval)
2094 ASSERT_VALID_EPHEMERAL;
2095 JSDValue *jsdv = JSD_GetValueConstructor (mCx, mValue);
2096 *_rval = jsdValue::FromPtr (mCx, jsdv);
2097 return NS_OK;
2100 NS_IMETHODIMP
2101 jsdValue::GetJsFunctionName(nsACString &_rval)
2103 ASSERT_VALID_EPHEMERAL;
2104 _rval.Assign(JSD_GetValueFunctionName(mCx, mValue));
2105 return NS_OK;
2108 NS_IMETHODIMP
2109 jsdValue::GetBooleanValue(PRBool *_rval)
2111 ASSERT_VALID_EPHEMERAL;
2112 *_rval = JSD_GetValueBoolean (mCx, mValue);
2113 return NS_OK;
2116 NS_IMETHODIMP
2117 jsdValue::GetDoubleValue(double *_rval)
2119 ASSERT_VALID_EPHEMERAL;
2120 double *dp = JSD_GetValueDouble (mCx, mValue);
2121 if (!dp)
2122 return NS_ERROR_FAILURE;
2123 *_rval = *dp;
2124 return NS_OK;
2127 NS_IMETHODIMP
2128 jsdValue::GetIntValue(PRInt32 *_rval)
2130 ASSERT_VALID_EPHEMERAL;
2131 *_rval = JSD_GetValueInt (mCx, mValue);
2132 return NS_OK;
2135 NS_IMETHODIMP
2136 jsdValue::GetObjectValue(jsdIObject **_rval)
2138 ASSERT_VALID_EPHEMERAL;
2139 JSDObject *obj;
2140 obj = JSD_GetObjectForValue (mCx, mValue);
2141 *_rval = jsdObject::FromPtr (mCx, obj);
2142 if (!*_rval)
2143 return NS_ERROR_FAILURE;
2144 return NS_OK;
2147 NS_IMETHODIMP
2148 jsdValue::GetStringValue(nsACString &_rval)
2150 ASSERT_VALID_EPHEMERAL;
2151 JSString *jstr_val = JSD_GetValueString(mCx, mValue);
2152 if (jstr_val) {
2153 nsDependentString chars(
2154 reinterpret_cast<PRUnichar*>(JS_GetStringChars(jstr_val)),
2155 JS_GetStringLength(jstr_val));
2156 CopyUTF16toUTF8(chars, _rval);
2157 } else {
2158 _rval.Truncate();
2160 return NS_OK;
2163 NS_IMETHODIMP
2164 jsdValue::GetPropertyCount (PRInt32 *_rval)
2166 ASSERT_VALID_EPHEMERAL;
2167 if (JSD_IsValueObject(mCx, mValue))
2168 *_rval = JSD_GetCountOfProperties (mCx, mValue);
2169 else
2170 *_rval = -1;
2171 return NS_OK;
2174 NS_IMETHODIMP
2175 jsdValue::GetProperties (jsdIProperty ***propArray, PRUint32 *length)
2177 ASSERT_VALID_EPHEMERAL;
2178 *propArray = nsnull;
2179 if (length)
2180 *length = 0;
2182 PRUint32 prop_count = JSD_IsValueObject(mCx, mValue)
2183 ? JSD_GetCountOfProperties (mCx, mValue)
2184 : 0;
2185 NS_ENSURE_TRUE(prop_count, NS_OK);
2187 jsdIProperty **pa_temp =
2188 static_cast<jsdIProperty **>
2189 (nsMemory::Alloc(sizeof (jsdIProperty *) *
2190 prop_count));
2191 NS_ENSURE_TRUE(pa_temp, NS_ERROR_OUT_OF_MEMORY);
2193 PRUint32 i = 0;
2194 JSDProperty *iter = NULL;
2195 JSDProperty *prop;
2196 while ((prop = JSD_IterateProperties (mCx, mValue, &iter))) {
2197 pa_temp[i] = jsdProperty::FromPtr (mCx, prop);
2198 ++i;
2201 NS_ASSERTION (prop_count == i, "property count mismatch");
2203 /* if caller doesn't care about length, don't bother telling them */
2204 *propArray = pa_temp;
2205 if (length)
2206 *length = prop_count;
2208 return NS_OK;
2211 NS_IMETHODIMP
2212 jsdValue::GetProperty (const nsACString &name, jsdIProperty **_rval)
2214 ASSERT_VALID_EPHEMERAL;
2215 JSContext *cx = JSD_GetDefaultJSContext (mCx);
2217 JSAutoRequest ar(cx);
2219 /* not rooting this */
2220 JSString *jstr_name = JS_NewStringCopyZ(cx, PromiseFlatCString(name).get());
2221 if (!jstr_name)
2222 return NS_ERROR_OUT_OF_MEMORY;
2224 JSDProperty *prop = JSD_GetValueProperty (mCx, mValue, jstr_name);
2226 *_rval = jsdProperty::FromPtr (mCx, prop);
2227 return NS_OK;
2230 NS_IMETHODIMP
2231 jsdValue::Refresh()
2233 ASSERT_VALID_EPHEMERAL;
2234 JSD_RefreshValue (mCx, mValue);
2235 return NS_OK;
2238 NS_IMETHODIMP
2239 jsdValue::GetWrappedValue()
2241 ASSERT_VALID_EPHEMERAL;
2242 nsresult rv;
2243 nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID(), &rv);
2244 if (NS_FAILED(rv))
2245 return rv;
2247 nsAXPCNativeCallContext *cc = nsnull;
2248 rv = xpc->GetCurrentNativeCallContext(&cc);
2249 if (NS_FAILED(rv))
2250 return rv;
2252 jsval *result;
2253 rv = cc->GetRetValPtr(&result);
2254 if (NS_FAILED(rv))
2255 return rv;
2257 if (result)
2259 *result = JSD_GetValueWrappedJSVal (mCx, mValue);
2260 cc->SetReturnValueWasSet(PR_TRUE);
2263 return NS_OK;
2266 /******************************************************************************
2267 * debugger service implementation
2268 ******************************************************************************/
2269 NS_IMPL_THREADSAFE_ISUPPORTS1(jsdService, jsdIDebuggerService)
2271 NS_IMETHODIMP
2272 jsdService::GetJSDContext(JSDContext **_rval)
2274 *_rval = mCx;
2275 return NS_OK;
2278 NS_IMETHODIMP
2279 jsdService::GetInitAtStartup (PRBool *_rval)
2281 nsresult rv;
2282 nsCOMPtr<nsICategoryManager>
2283 categoryManager(do_GetService(NS_CATMAN_CTRID, &rv));
2285 if (NS_FAILED(rv))
2287 NS_WARNING("couldn't get category manager");
2288 return rv;
2291 if (mInitAtStartup == triUnknown) {
2292 nsXPIDLCString notused;
2293 nsresult autoreg_rv, appstart_rv;
2295 autoreg_rv = categoryManager->GetCategoryEntry(AUTOREG_CATEGORY,
2296 JSD_AUTOREG_ENTRY,
2297 getter_Copies(notused));
2298 appstart_rv = categoryManager->GetCategoryEntry(APPSTART_CATEGORY,
2299 JSD_STARTUP_ENTRY,
2300 getter_Copies(notused));
2301 if (autoreg_rv != appstart_rv) {
2302 /* we have an inconsistent state in the registry, attempt to fix.
2303 * we need to make mInitAtStartup disagree with the state passed
2304 * to SetInitAtStartup to make it actually do something.
2306 mInitAtStartup = triYes;
2307 rv = SetInitAtStartup (PR_FALSE);
2308 if (NS_FAILED(rv))
2310 NS_WARNING("SetInitAtStartup failed");
2311 return rv;
2313 } else if (autoreg_rv == NS_ERROR_NOT_AVAILABLE) {
2314 mInitAtStartup = triNo;
2315 } else if (NS_SUCCEEDED(autoreg_rv)) {
2316 mInitAtStartup = triYes;
2317 } else {
2318 NS_WARN_IF_FALSE(NS_SUCCEEDED(autoreg_rv),
2319 "couldn't get autoreg category");
2320 NS_WARN_IF_FALSE(NS_SUCCEEDED(appstart_rv),
2321 "couldn't get appstart category");
2322 return rv;
2326 if (_rval)
2327 *_rval = (mInitAtStartup == triYes);
2329 return NS_OK;
2333 * The initAtStartup property controls whether or not we register the
2334 * app start observer (jsdASObserver.) We register for both
2335 * "xpcom-autoregistration" and "app-startup" notifications if |state| is true.
2336 * the autoreg message is sent just before registration occurs (before
2337 * "app-startup".) We care about autoreg because it may load javascript
2338 * components. autoreg does *not* fire if components haven't changed since the
2339 * last autoreg, so we watch "app-startup" as a fallback.
2341 NS_IMETHODIMP
2342 jsdService::SetInitAtStartup (PRBool state)
2344 nsresult rv;
2346 if (mInitAtStartup == triUnknown) {
2347 /* side effect sets mInitAtStartup */
2348 rv = GetInitAtStartup(nsnull);
2349 if (NS_FAILED(rv))
2350 return rv;
2353 if (state && mInitAtStartup == triYes ||
2354 !state && mInitAtStartup == triNo) {
2355 /* already in the requested state */
2356 return NS_OK;
2359 nsCOMPtr<nsICategoryManager>
2360 categoryManager(do_GetService(NS_CATMAN_CTRID, &rv));
2361 if (NS_FAILED(rv))
2362 return rv;
2364 if (state) {
2365 rv = categoryManager->AddCategoryEntry(AUTOREG_CATEGORY,
2366 JSD_AUTOREG_ENTRY,
2367 jsdARObserverCtrID,
2368 PR_TRUE, PR_TRUE, nsnull);
2369 if (NS_FAILED(rv))
2370 return rv;
2371 rv = categoryManager->AddCategoryEntry(APPSTART_CATEGORY,
2372 JSD_STARTUP_ENTRY,
2373 jsdASObserverCtrID,
2374 PR_TRUE, PR_TRUE, nsnull);
2375 if (NS_FAILED(rv))
2376 return rv;
2377 mInitAtStartup = triYes;
2378 } else {
2379 rv = categoryManager->DeleteCategoryEntry(AUTOREG_CATEGORY,
2380 JSD_AUTOREG_ENTRY, PR_TRUE);
2381 if (NS_FAILED(rv))
2382 return rv;
2383 rv = categoryManager->DeleteCategoryEntry(APPSTART_CATEGORY,
2384 JSD_STARTUP_ENTRY, PR_TRUE);
2385 if (NS_FAILED(rv))
2386 return rv;
2387 mInitAtStartup = triNo;
2390 return NS_OK;
2393 NS_IMETHODIMP
2394 jsdService::GetFlags (PRUint32 *_rval)
2396 ASSERT_VALID_CONTEXT;
2397 *_rval = JSD_GetContextFlags (mCx);
2398 return NS_OK;
2401 NS_IMETHODIMP
2402 jsdService::SetFlags (PRUint32 flags)
2404 ASSERT_VALID_CONTEXT;
2405 JSD_SetContextFlags (mCx, flags);
2406 return NS_OK;
2409 NS_IMETHODIMP
2410 jsdService::GetImplementationString(nsACString &aImplementationString)
2412 aImplementationString.AssignLiteral(implementationString);
2413 return NS_OK;
2416 NS_IMETHODIMP
2417 jsdService::GetImplementationMajor(PRUint32 *_rval)
2419 *_rval = JSDS_MAJOR_VERSION;
2420 return NS_OK;
2423 NS_IMETHODIMP
2424 jsdService::GetImplementationMinor(PRUint32 *_rval)
2426 *_rval = JSDS_MINOR_VERSION;
2427 return NS_OK;
2430 NS_IMETHODIMP
2431 jsdService::GetIsOn (PRBool *_rval)
2433 *_rval = mOn;
2434 return NS_OK;
2437 NS_IMETHODIMP
2438 jsdService::On (void)
2440 nsresult rv;
2442 /* get JS things from the CallContext */
2443 nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID(), &rv);
2444 if (NS_FAILED(rv)) return rv;
2446 nsAXPCNativeCallContext *cc = nsnull;
2447 rv = xpc->GetCurrentNativeCallContext(&cc);
2448 if (NS_FAILED(rv)) return rv;
2450 JSContext *cx;
2451 rv = cc->GetJSContext (&cx);
2452 if (NS_FAILED(rv)) return rv;
2454 return OnForRuntime(JS_GetRuntime (cx));
2458 NS_IMETHODIMP
2459 jsdService::OnForRuntime (JSRuntime *rt)
2461 if (mOn)
2462 return (rt == mRuntime) ? NS_OK : NS_ERROR_ALREADY_INITIALIZED;
2464 mRuntime = rt;
2466 if (gLastGCProc == jsds_GCCallbackProc)
2467 /* condition indicates that the callback proc has not been set yet */
2468 gLastGCProc = JS_SetGCCallbackRT (rt, jsds_GCCallbackProc);
2470 mCx = JSD_DebuggerOnForUser (rt, NULL, NULL);
2471 if (!mCx)
2472 return NS_ERROR_FAILURE;
2474 JSContext *cx = JSD_GetDefaultJSContext (mCx);
2475 JSObject *glob = JS_GetGlobalObject (cx);
2477 /* init xpconnect on the debugger's context in case xpconnect tries to
2478 * use it for stuff. */
2479 nsresult rv;
2480 nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID(), &rv);
2481 if (NS_FAILED(rv))
2482 return rv;
2484 xpc->InitClasses (cx, glob);
2486 /* If any of these mFooHook objects are installed, do the required JSD
2487 * hookup now. See also, jsdService::SetFooHook().
2489 if (mErrorHook)
2490 JSD_SetErrorReporter (mCx, jsds_ErrorHookProc, NULL);
2491 if (mThrowHook)
2492 JSD_SetThrowHook (mCx, jsds_ExecutionHookProc, NULL);
2493 /* can't ignore script callbacks, as we need to |Release| the wrapper
2494 * stored in private data when a script is deleted. */
2495 if (mInterruptHook)
2496 JSD_SetInterruptHook (mCx, jsds_ExecutionHookProc, NULL);
2497 if (mDebuggerHook)
2498 JSD_SetDebuggerHook (mCx, jsds_ExecutionHookProc, NULL);
2499 if (mDebugHook)
2500 JSD_SetDebugBreakHook (mCx, jsds_ExecutionHookProc, NULL);
2501 if (mTopLevelHook)
2502 JSD_SetTopLevelHook (mCx, jsds_CallHookProc, NULL);
2503 else
2504 JSD_ClearTopLevelHook (mCx);
2505 if (mFunctionHook)
2506 JSD_SetFunctionHook (mCx, jsds_CallHookProc, NULL);
2507 else
2508 JSD_ClearFunctionHook (mCx);
2509 mOn = PR_TRUE;
2511 #ifdef DEBUG
2512 printf ("+++ JavaScript debugging hooks installed.\n");
2513 #endif
2514 return NS_OK;
2517 NS_IMETHODIMP
2518 jsdService::Off (void)
2520 if (!mOn)
2521 return NS_OK;
2523 if (!mCx || !mRuntime)
2524 return NS_ERROR_NOT_INITIALIZED;
2526 if (gDeadScripts) {
2527 if (gGCStatus != JSGC_END)
2528 return NS_ERROR_NOT_AVAILABLE;
2530 JSContext *cx = JSD_GetDefaultJSContext(mCx);
2531 jsds_NotifyPendingDeadScripts(cx);
2535 if (gLastGCProc != jsds_GCCallbackProc)
2536 JS_SetGCCallbackRT (mRuntime, gLastGCProc);
2539 jsdContext::InvalidateAll();
2540 jsdScript::InvalidateAll();
2541 jsdValue::InvalidateAll();
2542 jsdProperty::InvalidateAll();
2543 ClearAllBreakpoints();
2545 JSD_SetErrorReporter (mCx, NULL, NULL);
2546 JSD_SetScriptHook (mCx, NULL, NULL);
2547 JSD_ClearThrowHook (mCx);
2548 JSD_ClearInterruptHook (mCx);
2549 JSD_ClearDebuggerHook (mCx);
2550 JSD_ClearDebugBreakHook (mCx);
2551 JSD_ClearTopLevelHook (mCx);
2552 JSD_ClearFunctionHook (mCx);
2554 JSD_DebuggerOff (mCx);
2556 mCx = nsnull;
2557 mRuntime = nsnull;
2558 mOn = PR_FALSE;
2560 #ifdef DEBUG
2561 printf ("+++ JavaScript debugging hooks removed.\n");
2562 #endif
2564 return NS_OK;
2567 NS_IMETHODIMP
2568 jsdService::GetPauseDepth(PRUint32 *_rval)
2570 NS_ENSURE_ARG_POINTER(_rval);
2571 *_rval = mPauseLevel;
2572 return NS_OK;
2575 NS_IMETHODIMP
2576 jsdService::Pause(PRUint32 *_rval)
2578 if (!mCx)
2579 return NS_ERROR_NOT_INITIALIZED;
2581 if (++mPauseLevel == 1) {
2582 JSD_SetErrorReporter (mCx, NULL, NULL);
2583 JSD_ClearThrowHook (mCx);
2584 JSD_ClearInterruptHook (mCx);
2585 JSD_ClearDebuggerHook (mCx);
2586 JSD_ClearDebugBreakHook (mCx);
2587 JSD_ClearTopLevelHook (mCx);
2588 JSD_ClearFunctionHook (mCx);
2591 if (_rval)
2592 *_rval = mPauseLevel;
2594 return NS_OK;
2597 NS_IMETHODIMP
2598 jsdService::UnPause(PRUint32 *_rval)
2600 if (!mCx)
2601 return NS_ERROR_NOT_INITIALIZED;
2603 if (mPauseLevel == 0)
2604 return NS_ERROR_NOT_AVAILABLE;
2606 /* check mOn before we muck with this stuff, it's possible the debugger
2607 * was turned off while we were paused.
2609 if (--mPauseLevel == 0 && mOn) {
2610 if (mErrorHook)
2611 JSD_SetErrorReporter (mCx, jsds_ErrorHookProc, NULL);
2612 if (mThrowHook)
2613 JSD_SetThrowHook (mCx, jsds_ExecutionHookProc, NULL);
2614 if (mInterruptHook)
2615 JSD_SetInterruptHook (mCx, jsds_ExecutionHookProc, NULL);
2616 if (mDebuggerHook)
2617 JSD_SetDebuggerHook (mCx, jsds_ExecutionHookProc, NULL);
2618 if (mDebugHook)
2619 JSD_SetDebugBreakHook (mCx, jsds_ExecutionHookProc, NULL);
2620 if (mTopLevelHook)
2621 JSD_SetTopLevelHook (mCx, jsds_CallHookProc, NULL);
2622 else
2623 JSD_ClearTopLevelHook (mCx);
2624 if (mFunctionHook)
2625 JSD_SetFunctionHook (mCx, jsds_CallHookProc, NULL);
2626 else
2627 JSD_ClearFunctionHook (mCx);
2630 if (_rval)
2631 *_rval = mPauseLevel;
2633 return NS_OK;
2636 NS_IMETHODIMP
2637 jsdService::EnumerateContexts (jsdIContextEnumerator *enumerator)
2639 ASSERT_VALID_CONTEXT;
2641 if (!enumerator)
2642 return NS_OK;
2644 JSContext *iter = NULL;
2645 JSContext *cx;
2647 while ((cx = JS_ContextIterator (mRuntime, &iter)))
2649 nsCOMPtr<jsdIContext> jsdicx =
2650 getter_AddRefs(jsdContext::FromPtr(mCx, cx));
2651 if (jsdicx)
2653 if (NS_FAILED(enumerator->EnumerateContext(jsdicx)))
2654 break;
2658 return NS_OK;
2661 NS_IMETHODIMP
2662 jsdService::EnumerateScripts (jsdIScriptEnumerator *enumerator)
2664 ASSERT_VALID_CONTEXT;
2666 JSDScript *script;
2667 JSDScript *iter = NULL;
2668 nsresult rv = NS_OK;
2670 JSD_LockScriptSubsystem(mCx);
2671 while((script = JSD_IterateScripts(mCx, &iter))) {
2672 nsCOMPtr<jsdIScript> jsdis =
2673 getter_AddRefs(jsdScript::FromPtr(mCx, script));
2674 rv = enumerator->EnumerateScript (jsdis);
2675 if (NS_FAILED(rv))
2676 break;
2678 JSD_UnlockScriptSubsystem(mCx);
2680 return rv;
2683 NS_IMETHODIMP
2684 jsdService::GC (void)
2686 ASSERT_VALID_CONTEXT;
2687 JSContext *cx = JSD_GetDefaultJSContext (mCx);
2688 JS_GC(cx);
2689 return NS_OK;
2692 NS_IMETHODIMP
2693 jsdService::DumpHeap(const nsACString &fileName)
2695 ASSERT_VALID_CONTEXT;
2696 #ifndef DEBUG
2697 return NS_ERROR_NOT_IMPLEMENTED;
2698 #else
2699 nsresult rv = NS_OK;
2700 FILE *file = !fileName.IsEmpty() ? fopen(PromiseFlatCString(fileName).get(), "w") : stdout;
2701 if (!file) {
2702 rv = NS_ERROR_FAILURE;
2703 } else {
2704 JSContext *cx = JSD_GetDefaultJSContext (mCx);
2705 if (!JS_DumpHeap(cx, file, NULL, 0, NULL, (size_t)-1, NULL))
2706 rv = NS_ERROR_FAILURE;
2707 if (file != stdout)
2708 fclose(file);
2710 return rv;
2711 #endif
2714 NS_IMETHODIMP
2715 jsdService::ClearProfileData ()
2717 ASSERT_VALID_CONTEXT;
2718 JSD_ClearAllProfileData (mCx);
2719 return NS_OK;
2722 NS_IMETHODIMP
2723 jsdService::InsertFilter (jsdIFilter *filter, jsdIFilter *after)
2725 NS_ENSURE_ARG_POINTER (filter);
2726 if (jsds_FindFilter (filter))
2727 return NS_ERROR_INVALID_ARG;
2729 FilterRecord *rec = PR_NEWZAP (FilterRecord);
2730 if (!rec)
2731 return NS_ERROR_OUT_OF_MEMORY;
2733 if (!jsds_SyncFilter (rec, filter)) {
2734 PR_Free (rec);
2735 return NS_ERROR_FAILURE;
2738 if (gFilters) {
2739 if (!after) {
2740 /* insert at head of list */
2741 PR_INSERT_LINK(&rec->links, &gFilters->links);
2742 gFilters = rec;
2743 } else {
2744 /* insert somewhere in the list */
2745 FilterRecord *afterRecord = jsds_FindFilter (after);
2746 if (!afterRecord) {
2747 jsds_FreeFilter(rec);
2748 return NS_ERROR_INVALID_ARG;
2750 PR_INSERT_AFTER(&rec->links, &afterRecord->links);
2752 } else {
2753 if (after) {
2754 /* user asked to insert into the middle of an empty list, bail. */
2755 jsds_FreeFilter(rec);
2756 return NS_ERROR_NOT_INITIALIZED;
2758 PR_INIT_CLIST(&rec->links);
2759 gFilters = rec;
2762 return NS_OK;
2765 NS_IMETHODIMP
2766 jsdService::AppendFilter (jsdIFilter *filter)
2768 NS_ENSURE_ARG_POINTER (filter);
2769 if (jsds_FindFilter (filter))
2770 return NS_ERROR_INVALID_ARG;
2771 FilterRecord *rec = PR_NEWZAP (FilterRecord);
2773 if (!jsds_SyncFilter (rec, filter)) {
2774 PR_Free (rec);
2775 return NS_ERROR_FAILURE;
2778 if (gFilters) {
2779 PR_INSERT_BEFORE(&rec->links, &gFilters->links);
2780 } else {
2781 PR_INIT_CLIST(&rec->links);
2782 gFilters = rec;
2785 return NS_OK;
2788 NS_IMETHODIMP
2789 jsdService::RemoveFilter (jsdIFilter *filter)
2791 NS_ENSURE_ARG_POINTER(filter);
2792 FilterRecord *rec = jsds_FindFilter (filter);
2793 if (!rec)
2794 return NS_ERROR_INVALID_ARG;
2796 if (gFilters == rec) {
2797 gFilters = reinterpret_cast<FilterRecord *>
2798 (PR_NEXT_LINK(&rec->links));
2799 /* If we're the only filter left, null out the list head. */
2800 if (gFilters == rec)
2801 gFilters = nsnull;
2805 PR_REMOVE_LINK(&rec->links);
2806 jsds_FreeFilter (rec);
2808 return NS_OK;
2811 NS_IMETHODIMP
2812 jsdService::SwapFilters (jsdIFilter *filter_a, jsdIFilter *filter_b)
2814 NS_ENSURE_ARG_POINTER(filter_a);
2815 NS_ENSURE_ARG_POINTER(filter_b);
2817 FilterRecord *rec_a = jsds_FindFilter (filter_a);
2818 if (!rec_a)
2819 return NS_ERROR_INVALID_ARG;
2821 if (filter_a == filter_b) {
2822 /* just a refresh */
2823 if (!jsds_SyncFilter (rec_a, filter_a))
2824 return NS_ERROR_FAILURE;
2825 return NS_OK;
2828 FilterRecord *rec_b = jsds_FindFilter (filter_b);
2829 if (!rec_b) {
2830 /* filter_b is not in the list, replace filter_a with filter_b. */
2831 if (!jsds_SyncFilter (rec_a, filter_b))
2832 return NS_ERROR_FAILURE;
2833 } else {
2834 /* both filters are in the list, swap. */
2835 if (!jsds_SyncFilter (rec_a, filter_b))
2836 return NS_ERROR_FAILURE;
2837 if (!jsds_SyncFilter (rec_b, filter_a))
2838 return NS_ERROR_FAILURE;
2841 return NS_OK;
2844 NS_IMETHODIMP
2845 jsdService::EnumerateFilters (jsdIFilterEnumerator *enumerator)
2847 if (!gFilters)
2848 return NS_OK;
2850 FilterRecord *current = gFilters;
2851 do {
2852 jsds_SyncFilter (current, current->filterObject);
2853 /* SyncFilter failure would be bad, but what would we do about it? */
2854 if (enumerator) {
2855 nsresult rv = enumerator->EnumerateFilter (current->filterObject);
2856 if (NS_FAILED(rv))
2857 return rv;
2859 current = reinterpret_cast<FilterRecord *>
2860 (PR_NEXT_LINK (&current->links));
2861 } while (current != gFilters);
2863 return NS_OK;
2866 NS_IMETHODIMP
2867 jsdService::RefreshFilters ()
2869 return EnumerateFilters(nsnull);
2872 NS_IMETHODIMP
2873 jsdService::ClearFilters ()
2875 if (!gFilters)
2876 return NS_OK;
2878 FilterRecord *current = reinterpret_cast<FilterRecord *>
2879 (PR_NEXT_LINK (&gFilters->links));
2880 do {
2881 FilterRecord *next = reinterpret_cast<FilterRecord *>
2882 (PR_NEXT_LINK (&current->links));
2883 PR_REMOVE_AND_INIT_LINK(&current->links);
2884 jsds_FreeFilter(current);
2885 current = next;
2886 } while (current != gFilters);
2888 jsds_FreeFilter(current);
2889 gFilters = nsnull;
2891 return NS_OK;
2894 NS_IMETHODIMP
2895 jsdService::ClearAllBreakpoints (void)
2897 ASSERT_VALID_CONTEXT;
2899 JSD_LockScriptSubsystem(mCx);
2900 JSD_ClearAllExecutionHooks (mCx);
2901 JSD_UnlockScriptSubsystem(mCx);
2902 return NS_OK;
2905 NS_IMETHODIMP
2906 jsdService::WrapValue(jsdIValue **_rval)
2908 ASSERT_VALID_CONTEXT;
2910 nsresult rv;
2911 nsCOMPtr<nsIXPConnect> xpc = do_GetService (nsIXPConnect::GetCID(), &rv);
2912 if (NS_FAILED(rv))
2913 return rv;
2915 nsAXPCNativeCallContext *cc = nsnull;
2916 rv = xpc->GetCurrentNativeCallContext (&cc);
2917 if (NS_FAILED(rv))
2918 return rv;
2920 PRUint32 argc;
2921 rv = cc->GetArgc (&argc);
2922 if (NS_FAILED(rv))
2923 return rv;
2924 if (argc < 1)
2925 return NS_ERROR_INVALID_ARG;
2927 jsval *argv;
2928 rv = cc->GetArgvPtr (&argv);
2929 if (NS_FAILED(rv))
2930 return rv;
2932 JSDValue *jsdv = JSD_NewValue (mCx, argv[0]);
2933 if (!jsdv)
2934 return NS_ERROR_FAILURE;
2936 *_rval = jsdValue::FromPtr (mCx, jsdv);
2937 return NS_OK;
2941 NS_IMETHODIMP
2942 jsdService::EnterNestedEventLoop (jsdINestCallback *callback, PRUint32 *_rval)
2944 // Nesting event queues is a thing of the past. Now, we just spin the
2945 // current event loop.
2947 nsresult rv;
2948 nsCOMPtr<nsIJSContextStack>
2949 stack(do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv));
2950 if (NS_FAILED(rv))
2951 return rv;
2952 PRUint32 nestLevel = ++mNestedLoopLevel;
2954 nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
2956 if (NS_SUCCEEDED(stack->Push(nsnull))) {
2957 if (callback) {
2958 Pause(nsnull);
2959 rv = callback->OnNest();
2960 UnPause(nsnull);
2963 while (NS_SUCCEEDED(rv) && mNestedLoopLevel >= nestLevel) {
2964 if (!NS_ProcessNextEvent(thread))
2965 rv = NS_ERROR_UNEXPECTED;
2968 JSContext* cx;
2969 stack->Pop(&cx);
2970 NS_ASSERTION(cx == nsnull, "JSContextStack mismatch");
2972 else
2973 rv = NS_ERROR_FAILURE;
2975 NS_ASSERTION (mNestedLoopLevel <= nestLevel,
2976 "nested event didn't unwind properly");
2977 if (mNestedLoopLevel == nestLevel)
2978 --mNestedLoopLevel;
2980 *_rval = mNestedLoopLevel;
2981 return rv;
2984 NS_IMETHODIMP
2985 jsdService::ExitNestedEventLoop (PRUint32 *_rval)
2987 if (mNestedLoopLevel > 0)
2988 --mNestedLoopLevel;
2989 else
2990 return NS_ERROR_FAILURE;
2992 *_rval = mNestedLoopLevel;
2993 return NS_OK;
2996 /* hook attribute get/set functions */
2998 NS_IMETHODIMP
2999 jsdService::SetErrorHook (jsdIErrorHook *aHook)
3001 mErrorHook = aHook;
3003 /* if the debugger isn't initialized, that's all we can do for now. The
3004 * OnForRuntime() method will do the rest when the coast is clear.
3006 if (!mCx || mPauseLevel)
3007 return NS_OK;
3009 if (aHook)
3010 JSD_SetErrorReporter (mCx, jsds_ErrorHookProc, NULL);
3011 else
3012 JSD_SetErrorReporter (mCx, NULL, NULL);
3014 return NS_OK;
3017 NS_IMETHODIMP
3018 jsdService::GetErrorHook (jsdIErrorHook **aHook)
3020 *aHook = mErrorHook;
3021 NS_IF_ADDREF(*aHook);
3023 return NS_OK;
3026 NS_IMETHODIMP
3027 jsdService::SetBreakpointHook (jsdIExecutionHook *aHook)
3029 mBreakpointHook = aHook;
3030 return NS_OK;
3033 NS_IMETHODIMP
3034 jsdService::GetBreakpointHook (jsdIExecutionHook **aHook)
3036 *aHook = mBreakpointHook;
3037 NS_IF_ADDREF(*aHook);
3039 return NS_OK;
3042 NS_IMETHODIMP
3043 jsdService::SetDebugHook (jsdIExecutionHook *aHook)
3045 mDebugHook = aHook;
3047 /* if the debugger isn't initialized, that's all we can do for now. The
3048 * OnForRuntime() method will do the rest when the coast is clear.
3050 if (!mCx || mPauseLevel)
3051 return NS_OK;
3053 if (aHook)
3054 JSD_SetDebugBreakHook (mCx, jsds_ExecutionHookProc, NULL);
3055 else
3056 JSD_ClearDebugBreakHook (mCx);
3058 return NS_OK;
3061 NS_IMETHODIMP
3062 jsdService::GetDebugHook (jsdIExecutionHook **aHook)
3064 *aHook = mDebugHook;
3065 NS_IF_ADDREF(*aHook);
3067 return NS_OK;
3070 NS_IMETHODIMP
3071 jsdService::SetDebuggerHook (jsdIExecutionHook *aHook)
3073 mDebuggerHook = aHook;
3075 /* if the debugger isn't initialized, that's all we can do for now. The
3076 * OnForRuntime() method will do the rest when the coast is clear.
3078 if (!mCx || mPauseLevel)
3079 return NS_OK;
3081 if (aHook)
3082 JSD_SetDebuggerHook (mCx, jsds_ExecutionHookProc, NULL);
3083 else
3084 JSD_ClearDebuggerHook (mCx);
3086 return NS_OK;
3089 NS_IMETHODIMP
3090 jsdService::GetDebuggerHook (jsdIExecutionHook **aHook)
3092 *aHook = mDebuggerHook;
3093 NS_IF_ADDREF(*aHook);
3095 return NS_OK;
3098 NS_IMETHODIMP
3099 jsdService::SetInterruptHook (jsdIExecutionHook *aHook)
3101 mInterruptHook = aHook;
3103 /* if the debugger isn't initialized, that's all we can do for now. The
3104 * OnForRuntime() method will do the rest when the coast is clear.
3106 if (!mCx || mPauseLevel)
3107 return NS_OK;
3109 if (aHook)
3110 JSD_SetInterruptHook (mCx, jsds_ExecutionHookProc, NULL);
3111 else
3112 JSD_ClearInterruptHook (mCx);
3114 return NS_OK;
3117 NS_IMETHODIMP
3118 jsdService::GetInterruptHook (jsdIExecutionHook **aHook)
3120 *aHook = mInterruptHook;
3121 NS_IF_ADDREF(*aHook);
3123 return NS_OK;
3126 NS_IMETHODIMP
3127 jsdService::SetScriptHook (jsdIScriptHook *aHook)
3129 mScriptHook = aHook;
3131 /* if the debugger isn't initialized, that's all we can do for now. The
3132 * OnForRuntime() method will do the rest when the coast is clear.
3134 if (!mCx || mPauseLevel)
3135 return NS_OK;
3137 if (aHook)
3138 JSD_SetScriptHook (mCx, jsds_ScriptHookProc, NULL);
3139 /* we can't unset it if !aHook, because we still need to see script
3140 * deletes in order to Release the jsdIScripts held in JSDScript
3141 * private data. */
3142 return NS_OK;
3145 NS_IMETHODIMP
3146 jsdService::GetScriptHook (jsdIScriptHook **aHook)
3148 *aHook = mScriptHook;
3149 NS_IF_ADDREF(*aHook);
3151 return NS_OK;
3154 NS_IMETHODIMP
3155 jsdService::SetThrowHook (jsdIExecutionHook *aHook)
3157 mThrowHook = aHook;
3159 /* if the debugger isn't initialized, that's all we can do for now. The
3160 * OnForRuntime() method will do the rest when the coast is clear.
3162 if (!mCx || mPauseLevel)
3163 return NS_OK;
3165 if (aHook)
3166 JSD_SetThrowHook (mCx, jsds_ExecutionHookProc, NULL);
3167 else
3168 JSD_ClearThrowHook (mCx);
3170 return NS_OK;
3173 NS_IMETHODIMP
3174 jsdService::GetThrowHook (jsdIExecutionHook **aHook)
3176 *aHook = mThrowHook;
3177 NS_IF_ADDREF(*aHook);
3179 return NS_OK;
3182 NS_IMETHODIMP
3183 jsdService::SetTopLevelHook (jsdICallHook *aHook)
3185 mTopLevelHook = aHook;
3187 /* if the debugger isn't initialized, that's all we can do for now. The
3188 * OnForRuntime() method will do the rest when the coast is clear.
3190 if (!mCx || mPauseLevel)
3191 return NS_OK;
3193 if (aHook)
3194 JSD_SetTopLevelHook (mCx, jsds_CallHookProc, NULL);
3195 else
3196 JSD_ClearTopLevelHook (mCx);
3198 return NS_OK;
3201 NS_IMETHODIMP
3202 jsdService::GetTopLevelHook (jsdICallHook **aHook)
3204 *aHook = mTopLevelHook;
3205 NS_IF_ADDREF(*aHook);
3207 return NS_OK;
3210 NS_IMETHODIMP
3211 jsdService::SetFunctionHook (jsdICallHook *aHook)
3213 mFunctionHook = aHook;
3215 /* if the debugger isn't initialized, that's all we can do for now. The
3216 * OnForRuntime() method will do the rest when the coast is clear.
3218 if (!mCx || mPauseLevel)
3219 return NS_OK;
3221 if (aHook)
3222 JSD_SetFunctionHook (mCx, jsds_CallHookProc, NULL);
3223 else
3224 JSD_ClearFunctionHook (mCx);
3226 return NS_OK;
3229 NS_IMETHODIMP
3230 jsdService::GetFunctionHook (jsdICallHook **aHook)
3232 *aHook = mFunctionHook;
3233 NS_IF_ADDREF(*aHook);
3235 return NS_OK;
3238 /* virtual */
3239 jsdService::~jsdService()
3241 ClearFilters();
3242 mErrorHook = nsnull;
3243 mBreakpointHook = nsnull;
3244 mDebugHook = nsnull;
3245 mDebuggerHook = nsnull;
3246 mInterruptHook = nsnull;
3247 mScriptHook = nsnull;
3248 mThrowHook = nsnull;
3249 mTopLevelHook = nsnull;
3250 mFunctionHook = nsnull;
3251 gGCStatus = JSGC_END;
3252 Off();
3253 gJsds = nsnull;
3256 jsdService *
3257 jsdService::GetService ()
3259 if (!gJsds)
3260 gJsds = new jsdService();
3262 NS_IF_ADDREF(gJsds);
3263 return gJsds;
3266 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(jsdService, jsdService::GetService)
3268 /* app-start observer. turns on the debugger at app-start. this is inserted
3269 * and/or removed from the app-start category by the jsdService::initAtStartup
3270 * property.
3272 class jsdASObserver : public nsIObserver
3274 public:
3275 NS_DECL_ISUPPORTS
3276 NS_DECL_NSIOBSERVER
3278 jsdASObserver () {}
3281 NS_IMPL_THREADSAFE_ISUPPORTS1(jsdASObserver, nsIObserver)
3283 NS_IMETHODIMP
3284 jsdASObserver::Observe (nsISupports *aSubject, const char *aTopic,
3285 const PRUnichar *aData)
3287 nsresult rv;
3289 // Hmm. Why is the app-startup observer called multiple times?
3290 //NS_ASSERTION(!gJsds, "app startup observer called twice");
3291 nsCOMPtr<jsdIDebuggerService> jsds = do_GetService(jsdServiceCtrID, &rv);
3292 if (NS_FAILED(rv))
3293 return rv;
3295 PRBool on;
3296 rv = jsds->GetIsOn(&on);
3297 if (NS_FAILED(rv) || on)
3298 return rv;
3300 nsCOMPtr<nsIJSRuntimeService> rts = do_GetService(NS_JSRT_CTRID, &rv);
3301 if (NS_FAILED(rv))
3302 return rv;
3304 JSRuntime *rt;
3305 rts->GetRuntime (&rt);
3306 if (NS_FAILED(rv))
3307 return rv;
3309 rv = jsds->OnForRuntime(rt);
3310 if (NS_FAILED(rv))
3311 return rv;
3313 return jsds->SetFlags(JSD_DISABLE_OBJECT_TRACE);
3316 NS_GENERIC_FACTORY_CONSTRUCTOR(jsdASObserver)
3318 static const nsModuleComponentInfo components[] = {
3319 {"JSDService", JSDSERVICE_CID, jsdServiceCtrID, jsdServiceConstructor},
3320 {"JSDASObserver", JSDASO_CID, jsdARObserverCtrID, jsdASObserverConstructor}
3323 NS_IMPL_NSGETMODULE(JavaScript_Debugger, components)
3325 /********************************************************************************
3326 ********************************************************************************
3327 * graveyard
3330 #if 0
3331 /* Thread States */
3332 NS_IMPL_THREADSAFE_ISUPPORTS1(jsdThreadState, jsdIThreadState);
3334 NS_IMETHODIMP
3335 jsdThreadState::GetJSDContext(JSDContext **_rval)
3337 *_rval = mCx;
3338 return NS_OK;
3341 NS_IMETHODIMP
3342 jsdThreadState::GetJSDThreadState(JSDThreadState **_rval)
3344 *_rval = mThreadState;
3345 return NS_OK;
3348 NS_IMETHODIMP
3349 jsdThreadState::GetFrameCount (PRUint32 *_rval)
3351 *_rval = JSD_GetCountOfStackFrames (mCx, mThreadState);
3352 return NS_OK;
3355 NS_IMETHODIMP
3356 jsdThreadState::GetTopFrame (jsdIStackFrame **_rval)
3358 JSDStackFrameInfo *sfi = JSD_GetStackFrame (mCx, mThreadState);
3360 *_rval = jsdStackFrame::FromPtr (mCx, mThreadState, sfi);
3361 return NS_OK;
3364 NS_IMETHODIMP
3365 jsdThreadState::GetPendingException(jsdIValue **_rval)
3367 JSDValue *jsdv = JSD_GetException (mCx, mThreadState);
3369 *_rval = jsdValue::FromPtr (mCx, jsdv);
3370 return NS_OK;
3373 NS_IMETHODIMP
3374 jsdThreadState::SetPendingException(jsdIValue *aException)
3376 JSDValue *jsdv;
3378 nsresult rv = aException->GetJSDValue (&jsdv);
3379 if (NS_FAILED(rv))
3380 return NS_ERROR_FAILURE;
3382 if (!JSD_SetException (mCx, mThreadState, jsdv))
3383 return NS_ERROR_FAILURE;
3385 return NS_OK;
3388 #endif