gecko-dev/js/jsd/jsd_xpc.cpp
rginda%netscape.com 67588ab2bd - not built -
remove spaces from non debug definitions of DEBUG_*, bug 89240
2001-07-05 09:06:24 +00:00

1639 lines
41 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation
* Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation.
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (the "GPL"), in which case the
* provisions of the GPL are applicable instead of those above.
* If you wish to allow use of your version of this file only
* under the terms of the GPL and not to allow others to use your
* version of this file under the MPL, indicate your decision by
* deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete
* the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* Contributor(s):
* Robert Ginda, <rginda@netscape.com>
*
*/
#include "jsd_xpc.h"
#include "nsIXPConnect.h"
#include "nsIGenericFactory.h"
#include "nsIServiceManager.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "nsIPref.h"
#include "nsICategoryManager.h"
#include "nsIJSRuntimeService.h"
#include "nsMemory.h"
#include "jsdebug.h"
/* XXX this stuff is used by NestEventLoop, a temporary hack to be refactored
* later */
#include "nsWidgetsCID.h"
#include "nsIAppShell.h"
#include "nsIJSContextStack.h"
#ifdef DEBUG_verbose
# define DEBUG_COUNT(name, count) \
{ if ((count % 10) == 0) printf (name ": %i\n", count); }
# define DEBUG_CREATE(name, count) {count++; DEBUG_COUNT ("+++++ "name,count)}
# define DEBUG_DESTROY(name, count) {count--; DEBUG_COUNT ("----- "name,count)}
#else
# define DEBUG_CREATE(name, count)
# define DEBUG_DESTROY(name, count)
#endif
#define ASSERT_VALID_CONTEXT { if (!mCx) return NS_ERROR_NOT_AVAILABLE; }
#define ASSERT_VALID_FRAME { if (!mValid) return NS_ERROR_NOT_AVAILABLE; }
#define ASSERT_VALID_PROPERTY { if (!mValid) return NS_ERROR_NOT_AVAILABLE; }
#define ASSERT_VALID_SCRIPT { if (!mValid) return NS_ERROR_NOT_AVAILABLE; }
#define ASSERT_VALID_VALUE { if (!mValid) return NS_ERROR_NOT_AVAILABLE; }
#define JSDSERVICE_CID \
{ /* f1299dc2-1dd1-11b2-a347-ee6b7660e048 */ \
0xf1299dc2, \
0x1dd1, \
0x11b2, \
{0xa3, 0x47, 0xee, 0x6b, 0x76, 0x60, 0xe0, 0x48} \
}
#define JSDASO_CID \
{ /* f2723a7e-1dd1-11b2-9f9e-ff701f717575 */ \
0xf2723a7e, \
0x1dd1, \
0x11b2, \
{0x9f, 0x9e, 0xff, 0x70, 0x1f, 0x71, 0x75, 0x75} \
}
#define NS_CATMAN_CTRID "@mozilla.org/categorymanager;1"
#define NS_PREF_CTRID "@mozilla.org/preferences;1"
#define NS_JSRT_CTRID "@mozilla.org/js/xpc/RuntimeService;1"
#define APPSTART_CATEGORY "app-startup"
#define PROFILE_CHANGE_EVENT "profile-after-change"
static JSBool
jsds_GCCallbackProc (JSContext *cx, JSGCStatus status);
/*******************************************************************************
* global vars
*******************************************************************************/
static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
const char jsdServiceContractID[] = "@mozilla.org/js/jsd/debugger-service;1";
const char jsdASObserverContractID[] = "@mozilla.org/js/jsd/app-start-observer;1";
#ifdef DEBUG_verbose
PRUint32 gScriptCount = 0;
PRUint32 gValueCount = 0;
PRUint32 gPropertyCount = 0;
#endif
static jsdService *gJsds = 0;
static JSGCCallback gLastGCProc = jsds_GCCallbackProc;
static JSGCStatus gGCStatus = JSGC_END;
static struct DeadScript {
PRCList links;
JSDContext *jsdc;
jsdIScript *script;
} *gDeadScripts = nsnull;
static struct LiveEphemeral *gLiveValues = nsnull, *gLiveProperties = nsnull;
/*******************************************************************************
* utility functions for ephemeral lists
*******************************************************************************/
void
jsds_InvalidateAllEphemerals (LiveEphemeral **listHead)
{
LiveEphemeral *lv_record =
NS_REINTERPRET_CAST (LiveEphemeral *,
PR_NEXT_LINK(&(*listHead)->links));
while (*listHead)
{
LiveEphemeral *next =
NS_REINTERPRET_CAST (LiveEphemeral *,
PR_NEXT_LINK(&lv_record->links));
lv_record->value->Invalidate();
lv_record = next;
}
}
void
jsds_InsertEphemeral (LiveEphemeral **listHead, LiveEphemeral *item)
{
if (*listHead) {
/* if the list exists, add to it */
PR_APPEND_LINK(&item->links, &(*listHead)->links);
} else {
/* otherwise create the list */
PR_INIT_CLIST(&item->links);
*listHead = item;
}
}
void
jsds_RemoveEphemeral (LiveEphemeral **listHead, LiveEphemeral *item)
{
LiveEphemeral *next =
NS_REINTERPRET_CAST (LiveEphemeral *,
PR_NEXT_LINK(&item->links));
if (next == item)
{
/* if the current item is also the next item, we're the only element,
* null out the list head */
NS_ASSERTION (*listHead == item,
"How could we not be the head of a one item list?");
*listHead = nsnull;
}
else if (item == *listHead)
{
/* otherwise, if we're currently the list head, change it */
*listHead = next;
}
PR_REMOVE_AND_INIT_LINK(&item->links);
}
/*******************************************************************************
* c callbacks
*******************************************************************************/
static void
jsds_NotifyPendingDeadScripts ()
{
nsCOMPtr<jsdIScriptHook> hook = 0;
gJsds->GetScriptHook (getter_AddRefs(hook));
if (hook)
{
DeadScript *ds;
do {
ds = gDeadScripts;
/* tell the user this script has been destroyed */
hook->OnScriptDestroyed (ds->script);
/* get next deleted script */
gDeadScripts = NS_REINTERPRET_CAST(DeadScript *,
PR_NEXT_LINK(&ds->links));
/* take ourselves out of the circular list */
PR_REMOVE_LINK(&ds->links);
/* addref came from the FromPtr call in jsds_ScriptHookProc */
NS_RELEASE(ds->script);
/* free the struct! */
PR_Free(ds);
} while (&gDeadScripts->links != &ds->links);
/* keep going until we catch up with our tail */
}
gDeadScripts = 0;
}
static JSBool
jsds_GCCallbackProc (JSContext *cx, JSGCStatus status)
{
gGCStatus = status;
#ifdef DEBUG
printf ("new gc status is %i\n", status);
#endif
if (status == JSGC_END && gDeadScripts)
jsds_NotifyPendingDeadScripts ();
if (gLastGCProc)
return gLastGCProc (cx, status);
return JS_TRUE;
}
static PRUint32
jsds_ExecutionHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate,
uintN type, void* callerdata, jsval* rval)
{
nsCOMPtr<jsdIExecutionHook> hook(0);
PRUint32 hook_rv = JSD_HOOK_RETURN_CONTINUE;
jsdIValue *js_rv = 0;
switch (type)
{
case JSD_HOOK_INTERRUPTED:
gJsds->GetInterruptHook(getter_AddRefs(hook));
break;
case JSD_HOOK_DEBUG_REQUESTED:
gJsds->GetErrorHook(getter_AddRefs(hook));
break;
case JSD_HOOK_DEBUGGER_KEYWORD:
gJsds->GetDebuggerHook(getter_AddRefs(hook));
break;
case JSD_HOOK_BREAKPOINT:
gJsds->GetBreakpointHook(getter_AddRefs(hook));
break;
case JSD_HOOK_THROW:
{
gJsds->GetThrowHook(getter_AddRefs(hook));
if (hook) {
JSDValue *jsdv = JSD_GetException (jsdc, jsdthreadstate);
js_rv = jsdValue::FromPtr (jsdc, jsdv);
}
break;
}
default:
NS_ASSERTION (0, "Unknown hook type.");
}
if (!hook)
return NS_OK;
JSDStackFrameInfo *native_frame = JSD_GetStackFrame (jsdc, jsdthreadstate);
nsCOMPtr<jsdIStackFrame> frame =
getter_AddRefs(jsdStackFrame::FromPtr(jsdc, jsdthreadstate,
native_frame));
hook->OnExecute (frame, type, &js_rv, &hook_rv);
frame->Invalidate();
if (hook_rv == JSD_HOOK_RETURN_RET_WITH_VAL ||
hook_rv == JSD_HOOK_RETURN_THROW_WITH_VAL)
{
JSDValue *jsdv;
js_rv->GetJSDValue (&jsdv);
*rval = JSD_GetValueWrappedJSVal(jsdc, jsdv);
}
NS_IF_RELEASE(js_rv);
return hook_rv;
}
static void
jsds_ScriptHookProc (JSDContext* jsdc, JSDScript* jsdscript, JSBool creating,
void* callerdata)
{
if (creating) {
jsdIScriptHook *hook = 0;
gJsds->GetScriptHook (&hook);
if (!hook)
return;
nsCOMPtr<jsdIScript> script =
getter_AddRefs(jsdScript::FromPtr(jsdc, jsdscript));
hook->OnScriptCreated (script);
} else {
jsdIScript *jsdis = jsdScript::FromPtr(jsdc, jsdscript);
/* the initial addref is owned by the DeadScript record */
jsdis->Invalidate();
if (gGCStatus == JSGC_END) {
/* if GC *isn't* running, we can tell the user about the script
* delete now. */
nsCOMPtr<jsdIScriptHook> hook = 0;
gJsds->GetScriptHook (getter_AddRefs(hook));
if (hook) {
hook->OnScriptDestroyed (jsdis);
}
} else {
/* if a GC *is* running, we've got to wait until it's done before
* we can execute any JS, so we queue the notification in a PRCList
* until GC tells us it's done. See jsds_GCCallbackProc(). */
DeadScript *ds = PR_NEW(DeadScript);
if (!ds) {
NS_RELEASE(jsdis);
return; /* NS_ERROR_OUT_OF_MEMORY */
}
ds->jsdc = jsdc;
ds->script = jsdis;
if (gDeadScripts)
/* if the queue exists, add to it */
PR_APPEND_LINK(&ds->links, &gDeadScripts->links);
else {
/* otherwise create the queue */
PR_INIT_CLIST(&ds->links);
gDeadScripts = ds;
}
}
}
}
/*******************************************************************************
* reflected jsd data structures
*******************************************************************************/
/* Contexts */
/*
NS_IMPL_THREADSAFE_ISUPPORTS1(jsdContext, jsdIContext);
NS_IMETHODIMP
jsdContext::GetJSDContext(JSDContext **_rval)
{
*_rval = mCx;
return NS_OK;
}
*/
/* Objects */
NS_IMPL_THREADSAFE_ISUPPORTS1(jsdObject, jsdIObject);
NS_IMETHODIMP
jsdObject::GetJSDContext(JSDContext **_rval)
{
*_rval = mCx;
return NS_OK;
}
NS_IMETHODIMP
jsdObject::GetJSDObject(JSDObject **_rval)
{
*_rval = mObject;
return NS_OK;
}
NS_IMETHODIMP
jsdObject::GetCreatorURL(char **_rval)
{
*_rval = nsCString(JSD_GetObjectNewURL(mCx, mObject)).ToNewCString();
return NS_OK;
}
NS_IMETHODIMP
jsdObject::GetCreatorLine(PRUint32 *_rval)
{
*_rval = JSD_GetObjectNewLineNumber(mCx, mObject);
return NS_OK;
}
NS_IMETHODIMP
jsdObject::GetConstructorURL(char **_rval)
{
*_rval = nsCString(JSD_GetObjectConstructorURL(mCx, mObject)).ToNewCString();
return NS_OK;
}
NS_IMETHODIMP
jsdObject::GetConstructorLine(PRUint32 *_rval)
{
*_rval = JSD_GetObjectConstructorLineNumber(mCx, mObject);
return NS_OK;
}
NS_IMETHODIMP
jsdObject::GetValue(jsdIValue **_rval)
{
JSDValue *jsdv = JSD_GetValueForObject (mCx, mObject);
*_rval = jsdValue::FromPtr (mCx, jsdv);
return NS_OK;
}
/* PC */
NS_IMPL_THREADSAFE_ISUPPORTS1(jsdPC, jsdIPC);
NS_IMETHODIMP
jsdPC::GetPc(jsuword *_rval)
{
*_rval = mPC;
return NS_OK;
}
/* Properties */
NS_IMPL_THREADSAFE_ISUPPORTS2(jsdProperty, jsdIProperty, jsdIEphemeral);
jsdProperty::jsdProperty (JSDContext *aCx, JSDProperty *aProperty) :
mCx(aCx), mProperty(aProperty)
{
DEBUG_CREATE ("jsdProperty", gPropertyCount);
mValid = (aCx && aProperty);
NS_INIT_ISUPPORTS();
mLiveListEntry.value = this;
jsds_InsertEphemeral (&gLiveProperties, &mLiveListEntry);
}
jsdProperty::~jsdProperty ()
{
DEBUG_DESTROY ("jsdProperty", gPropertyCount);
if (mValid)
Invalidate();
}
NS_IMETHODIMP
jsdProperty::Invalidate()
{
ASSERT_VALID_VALUE;
mValid = PR_FALSE;
jsds_RemoveEphemeral (&gLiveProperties, &mLiveListEntry);
JSD_DropProperty (mCx, mProperty);
return NS_OK;
}
void
jsdProperty::InvalidateAll()
{
if (gLiveProperties)
jsds_InvalidateAllEphemerals (&gLiveProperties);
}
NS_IMETHODIMP
jsdProperty::GetJSDContext(JSDContext **_rval)
{
*_rval = mCx;
return NS_OK;
}
NS_IMETHODIMP
jsdProperty::GetJSDProperty(JSDProperty **_rval)
{
*_rval = mProperty;
return NS_OK;
}
NS_IMETHODIMP
jsdProperty::GetIsValid(PRBool *_rval)
{
*_rval = mValid;
return NS_OK;
}
NS_IMETHODIMP
jsdProperty::GetAlias(jsdIValue **_rval)
{
JSDValue *jsdv = JSD_GetPropertyValue (mCx, mProperty);
*_rval = jsdValue::FromPtr (mCx, jsdv);
return NS_OK;
}
NS_IMETHODIMP
jsdProperty::GetFlags(PRUint32 *_rval)
{
*_rval = JSD_GetPropertyFlags (mCx, mProperty);
return NS_OK;
}
NS_IMETHODIMP
jsdProperty::GetName(jsdIValue **_rval)
{
JSDValue *jsdv = JSD_GetPropertyName (mCx, mProperty);
*_rval = jsdValue::FromPtr (mCx, jsdv);
return NS_OK;
}
NS_IMETHODIMP
jsdProperty::GetValue(jsdIValue **_rval)
{
JSDValue *jsdv = JSD_GetPropertyValue (mCx, mProperty);
*_rval = jsdValue::FromPtr (mCx, jsdv);
return NS_OK;
}
NS_IMETHODIMP
jsdProperty::GetVarArgSlot(PRUint32 *_rval)
{
*_rval = JSD_GetPropertyVarArgSlot (mCx, mProperty);
return NS_OK;
}
/* Scripts */
NS_IMPL_THREADSAFE_ISUPPORTS2(jsdScript, jsdIScript, jsdIEphemeral);
jsdScript::jsdScript (JSDContext *aCx, JSDScript *aScript) : mValid(PR_FALSE),
mCx(aCx),
mScript(aScript),
mFileName(0),
mFunctionName(0),
mBaseLineNumber(0),
mLineExtent(0)
{
DEBUG_CREATE ("jsdScript", gScriptCount);
NS_INIT_ISUPPORTS();
if (mScript)
{
/* copy the script's information now, so we have it later, when it
* gets destroyed. */
JSD_LockScriptSubsystem(mCx);
mFileName = new nsCString(JSD_GetScriptFilename(mCx, mScript));
mFunctionName =
new nsCString(JSD_GetScriptFunctionName(mCx, mScript));
mBaseLineNumber = JSD_GetScriptBaseLineNumber(mCx, mScript);
mLineExtent = JSD_GetScriptLineExtent(mCx, mScript);
JSD_UnlockScriptSubsystem(mCx);
mValid = true;
}
}
jsdScript::~jsdScript ()
{
DEBUG_DESTROY ("jsdScript", gScriptCount);
if (mFileName)
delete mFileName;
if (mFunctionName)
delete mFunctionName;
/* Invalidate() needs to be called to release an owning reference to
* ourselves, so if we got here without being invalidated, something
* has gone wrong with our ref count. */
NS_ASSERTION (!mValid, "Script destroyed without being invalidated.");
}
NS_IMETHODIMP
jsdScript::GetJSDContext(JSDContext **_rval)
{
ASSERT_VALID_SCRIPT;
*_rval = mCx;
return NS_OK;
}
NS_IMETHODIMP
jsdScript::GetJSDScript(JSDScript **_rval)
{
ASSERT_VALID_SCRIPT;
*_rval = mScript;
return NS_OK;
}
NS_IMETHODIMP
jsdScript::Invalidate()
{
ASSERT_VALID_SCRIPT;
mValid = PR_FALSE;
/* release the addref we do in FromPtr */
jsdIScript *script = NS_STATIC_CAST(jsdIScript *,
JSD_GetScriptPrivate(mScript));
NS_ASSERTION (script == this, "That's not my script!");
NS_RELEASE(script);
return NS_OK;
}
NS_IMETHODIMP
jsdScript::GetIsValid(PRBool *_rval)
{
*_rval = mValid;
return NS_OK;
}
NS_IMETHODIMP
jsdScript::GetIsActive(PRBool *_rval)
{
if (!mValid) {
*_rval = PR_FALSE;
return NS_OK;
}
JSD_LockScriptSubsystem(mCx);
*_rval = JSD_IsActiveScript(mCx, mScript);
JSD_UnlockScriptSubsystem(mCx);
return NS_OK;
}
NS_IMETHODIMP
jsdScript::GetFileName(char **_rval)
{
*_rval = mFileName->ToNewCString();
return NS_OK;
}
NS_IMETHODIMP
jsdScript::GetFunctionName(char **_rval)
{
*_rval = mFunctionName->ToNewCString();
return NS_OK;
}
NS_IMETHODIMP
jsdScript::GetBaseLineNumber(PRUint32 *_rval)
{
*_rval = mBaseLineNumber;
return NS_OK;
}
NS_IMETHODIMP
jsdScript::GetLineExtent(PRUint32 *_rval)
{
*_rval = mLineExtent;
return NS_OK;
}
NS_IMETHODIMP
jsdScript::PcToLine(jsdIPC *aPC, PRUint32 *_rval)
{
ASSERT_VALID_SCRIPT;
jsuword pc;
aPC->GetPc(&pc);
*_rval = JSD_GetClosestLine (mCx, mScript, pc);
return NS_OK;
}
NS_IMETHODIMP
jsdScript::LineToPc(PRUint32 aLine, jsdIPC **_rval)
{
ASSERT_VALID_SCRIPT;
jsuword pc = JSD_GetClosestPC (mCx, mScript, aLine);
*_rval = jsdPC::FromPtr (pc);
return NS_OK;
}
NS_IMETHODIMP
jsdScript::SetBreakpoint(jsdIPC *aPC)
{
ASSERT_VALID_SCRIPT;
jsuword pc;
aPC->GetPc (&pc);
JSD_SetExecutionHook (mCx, mScript, pc, jsds_ExecutionHookProc,
NS_REINTERPRET_CAST(void *, PRIVATE_TO_JSVAL(NULL)));
return NS_OK;
}
NS_IMETHODIMP
jsdScript::ClearBreakpoint(jsdIPC *aPC)
{
ASSERT_VALID_SCRIPT;
if (!aPC)
return NS_ERROR_INVALID_ARG;
jsuword pc;
aPC->GetPc (&pc);
JSD_ClearExecutionHook (mCx, mScript, pc);
return NS_OK;
}
NS_IMETHODIMP
jsdScript::ClearAllBreakpoints()
{
ASSERT_VALID_SCRIPT;
JSD_LockScriptSubsystem(mCx);
JSD_ClearAllExecutionHooksForScript (mCx, mScript);
JSD_UnlockScriptSubsystem(mCx);
return NS_OK;
}
/* Stack Frames */
NS_IMPL_THREADSAFE_ISUPPORTS2(jsdStackFrame, jsdIStackFrame, jsdIEphemeral);
NS_IMETHODIMP
jsdStackFrame::GetJSDContext(JSDContext **_rval)
{
ASSERT_VALID_FRAME;
*_rval = mCx;
return NS_OK;
}
NS_IMETHODIMP
jsdStackFrame::GetJSDThreadState(JSDThreadState **_rval)
{
ASSERT_VALID_FRAME;
*_rval = mThreadState;
return NS_OK;
}
NS_IMETHODIMP
jsdStackFrame::GetJSDStackFrameInfo(JSDStackFrameInfo **_rval)
{
ASSERT_VALID_FRAME;
*_rval = mStackFrameInfo;
return NS_OK;
}
NS_IMETHODIMP
jsdStackFrame::GetIsValid(PRBool *_rval)
{
*_rval = mValid;
return NS_OK;
}
NS_IMETHODIMP
jsdStackFrame::Invalidate()
{
ASSERT_VALID_FRAME;
mValid = PR_FALSE;
return NS_OK;
}
NS_IMETHODIMP
jsdStackFrame::GetCallingFrame(jsdIStackFrame **_rval)
{
ASSERT_VALID_FRAME;
JSDStackFrameInfo *sfi = JSD_GetCallingStackFrame (mCx, mThreadState,
mStackFrameInfo);
*_rval = jsdStackFrame::FromPtr (mCx, mThreadState, sfi);
return NS_OK;
}
NS_IMETHODIMP
jsdStackFrame::GetScript(jsdIScript **_rval)
{
ASSERT_VALID_FRAME;
JSDScript *script = JSD_GetScriptForStackFrame (mCx, mThreadState,
mStackFrameInfo);
*_rval = jsdScript::FromPtr (mCx, script);
return NS_OK;
}
NS_IMETHODIMP
jsdStackFrame::GetPc(jsdIPC **_rval)
{
ASSERT_VALID_FRAME;
jsuword pc;
pc = JSD_GetPCForStackFrame (mCx, mThreadState, mStackFrameInfo);
*_rval = jsdPC::FromPtr (pc);
return NS_OK;
}
NS_IMETHODIMP
jsdStackFrame::GetLine(PRUint32 *_rval)
{
ASSERT_VALID_FRAME;
JSDScript *script = JSD_GetScriptForStackFrame (mCx, mThreadState,
mStackFrameInfo);
jsuword pc = JSD_GetPCForStackFrame (mCx, mThreadState, mStackFrameInfo);
*_rval = JSD_GetClosestLine (mCx, script, pc);
return NS_OK;
}
NS_IMETHODIMP
jsdStackFrame::GetCallee(jsdIValue **_rval)
{
ASSERT_VALID_FRAME;
JSDValue *jsdv = JSD_GetCallObjectForStackFrame (mCx, mThreadState,
mStackFrameInfo);
*_rval = jsdValue::FromPtr (mCx, jsdv);
return NS_OK;
}
NS_IMETHODIMP
jsdStackFrame::GetScope(jsdIValue **_rval)
{
ASSERT_VALID_FRAME;
JSDValue *jsdv = JSD_GetScopeChainForStackFrame (mCx, mThreadState,
mStackFrameInfo);
*_rval = jsdValue::FromPtr (mCx, jsdv);
return NS_OK;
}
NS_IMETHODIMP
jsdStackFrame::GetThisValue(jsdIValue **_rval)
{
ASSERT_VALID_FRAME;
JSDValue *jsdv = JSD_GetThisForStackFrame (mCx, mThreadState,
mStackFrameInfo);
*_rval = jsdValue::FromPtr (mCx, jsdv);
return NS_OK;
}
NS_IMETHODIMP
jsdStackFrame::Eval (const nsAReadableString &bytes, const char *fileName,
PRUint32 line, jsdIValue **_rval)
{
ASSERT_VALID_FRAME;
jsval jv;
const nsSharedBufferHandle<PRUnichar> *h = bytes.GetSharedBufferHandle();
const jschar *char_bytes = NS_REINTERPRET_CAST(const jschar *,
h->DataStart());
if (!JSD_EvaluateUCScriptInStackFrame (mCx, mThreadState, mStackFrameInfo,
char_bytes, bytes.Length(), fileName,
line, &jv))
return NS_ERROR_FAILURE;
JSDValue *jsdv = JSD_NewValue (mCx, jv);
*_rval = jsdValue::FromPtr (mCx, jsdv);
return NS_OK;
}
/* Values */
NS_IMPL_THREADSAFE_ISUPPORTS2(jsdValue, jsdIValue, jsdIEphemeral);
jsdValue::jsdValue (JSDContext *aCx, JSDValue *aValue) : mValid(PR_TRUE),
mCx(aCx),
mValue(aValue)
{
DEBUG_CREATE ("jsdValue", gValueCount);
NS_INIT_ISUPPORTS();
mLiveListEntry.value = this;
jsds_InsertEphemeral (&gLiveValues, &mLiveListEntry);
}
jsdValue::~jsdValue()
{
DEBUG_DESTROY ("jsdValue", gValueCount);
if (mValid)
/* call Invalidate() to take ourselves out of the live list */
Invalidate();
}
NS_IMETHODIMP
jsdValue::GetIsValid(PRBool *_rval)
{
*_rval = mValid;
return NS_OK;
}
NS_IMETHODIMP
jsdValue::Invalidate()
{
ASSERT_VALID_VALUE;
mValid = PR_FALSE;
jsds_RemoveEphemeral (&gLiveValues, &mLiveListEntry);
JSD_DropValue (mCx, mValue);
return NS_OK;
}
void
jsdValue::InvalidateAll()
{
if (gLiveValues)
jsds_InvalidateAllEphemerals (&gLiveValues);
}
NS_IMETHODIMP
jsdValue::GetJSDContext(JSDContext **_rval)
{
ASSERT_VALID_VALUE;
*_rval = mCx;
return NS_OK;
}
NS_IMETHODIMP
jsdValue::GetJSDValue (JSDValue **_rval)
{
ASSERT_VALID_VALUE;
*_rval = mValue;
return NS_OK;
}
NS_IMETHODIMP
jsdValue::GetIsNative (PRBool *_rval)
{
ASSERT_VALID_VALUE;
*_rval = JSD_IsValueNative (mCx, mValue);
return NS_OK;
}
NS_IMETHODIMP
jsdValue::GetIsNumber (PRBool *_rval)
{
ASSERT_VALID_VALUE;
*_rval = JSD_IsValueNumber (mCx, mValue);
return NS_OK;
}
NS_IMETHODIMP
jsdValue::GetIsPrimitive (PRBool *_rval)
{
ASSERT_VALID_VALUE;
*_rval = JSD_IsValuePrimitive (mCx, mValue);
return NS_OK;
}
NS_IMETHODIMP
jsdValue::GetJsType (PRUint32 *_rval)
{
ASSERT_VALID_VALUE;
/* XXX surely this can be done better. */
if (JSD_IsValueBoolean(mCx, mValue))
*_rval = TYPE_BOOLEAN;
else if (JSD_IsValueDouble(mCx, mValue))
*_rval = TYPE_DOUBLE;
else if (JSD_IsValueInt(mCx, mValue))
*_rval = TYPE_INT;
else if (JSD_IsValueFunction(mCx, mValue))
*_rval = TYPE_FUNCTION;
else if (JSD_IsValueNull(mCx, mValue))
*_rval = TYPE_NULL;
else if (JSD_IsValueObject(mCx, mValue))
*_rval = TYPE_OBJECT;
else if (JSD_IsValueString(mCx, mValue))
*_rval = TYPE_STRING;
else if (JSD_IsValueVoid(mCx, mValue))
*_rval = TYPE_VOID;
else
*_rval = TYPE_UNKNOWN;
return NS_OK;
}
NS_IMETHODIMP
jsdValue::GetJsPrototype (jsdIValue **_rval)
{
ASSERT_VALID_VALUE;
JSDValue *jsdv = JSD_GetValuePrototype (mCx, mValue);
*_rval = jsdValue::FromPtr (mCx, jsdv);
return NS_OK;
}
NS_IMETHODIMP
jsdValue::GetJsParent (jsdIValue **_rval)
{
ASSERT_VALID_VALUE;
JSDValue *jsdv = JSD_GetValueParent (mCx, mValue);
*_rval = jsdValue::FromPtr (mCx, jsdv);
return NS_OK;
}
NS_IMETHODIMP
jsdValue::GetJsClassName(char **_rval)
{
ASSERT_VALID_VALUE;
*_rval = nsCString(JSD_GetValueClassName(mCx, mValue)).ToNewCString();
return NS_OK;
}
NS_IMETHODIMP
jsdValue::GetJsConstructor (jsdIValue **_rval)
{
ASSERT_VALID_VALUE;
JSDValue *jsdv = JSD_GetValueConstructor (mCx, mValue);
*_rval = jsdValue::FromPtr (mCx, jsdv);
return NS_OK;
}
NS_IMETHODIMP
jsdValue::GetJsFunctionName(char **_rval)
{
ASSERT_VALID_VALUE;
*_rval = nsCString(JSD_GetValueFunctionName(mCx, mValue)).ToNewCString();
return NS_OK;
}
NS_IMETHODIMP
jsdValue::GetBooleanValue(PRBool *_rval)
{
ASSERT_VALID_VALUE;
*_rval = JSD_GetValueBoolean (mCx, mValue);
return NS_OK;
}
NS_IMETHODIMP
jsdValue::GetDoubleValue(double *_rval)
{
ASSERT_VALID_VALUE;
*_rval = *JSD_GetValueDouble (mCx, mValue);
return NS_OK;
}
NS_IMETHODIMP
jsdValue::GetIntValue(PRInt32 *_rval)
{
ASSERT_VALID_VALUE;
*_rval = JSD_GetValueInt (mCx, mValue);
return NS_OK;
}
NS_IMETHODIMP
jsdValue::GetObjectValue(jsdIObject **_rval)
{
ASSERT_VALID_VALUE;
JSDObject *obj;
obj = JSD_GetObjectForValue (mCx, mValue);
*_rval = jsdObject::FromPtr (mCx, obj);
return NS_OK;
}
NS_IMETHODIMP
jsdValue::GetStringValue(char **_rval)
{
ASSERT_VALID_VALUE;
JSString *jstr_val = JSD_GetValueString(mCx, mValue);
*_rval = nsCString(JS_GetStringBytes(jstr_val)).ToNewCString();
return NS_OK;
}
NS_IMETHODIMP
jsdValue::GetPropertyCount (PRInt32 *_rval)
{
ASSERT_VALID_VALUE;
if (JSD_IsValueObject(mCx, mValue))
*_rval = JSD_GetCountOfProperties (mCx, mValue);
else
*_rval = -1;
return NS_OK;
}
NS_IMETHODIMP
jsdValue::GetProperties (jsdIProperty ***propArray, PRUint32 *length)
{
ASSERT_VALID_VALUE;
if (!JSD_IsValueObject(mCx, mValue)) {
*length = 0;
*propArray = 0;
return NS_OK;
}
jsdIProperty **pa_temp;
PRUint32 prop_count = JSD_GetCountOfProperties (mCx, mValue);
pa_temp = NS_STATIC_CAST(jsdIProperty **,
nsMemory::Alloc(sizeof (jsdIProperty *) *
prop_count));
PRUint32 i = 0;
JSDProperty *iter = NULL;
JSDProperty *prop;
while ((prop = JSD_IterateProperties (mCx, mValue, &iter))) {
pa_temp[i] = jsdProperty::FromPtr (mCx, prop);
++i;
}
NS_ASSERTION (prop_count == i, "property count mismatch");
/* if caller doesn't care about length, don't bother telling them */
*propArray = pa_temp;
if (length)
*length = prop_count;
return NS_OK;
}
NS_IMETHODIMP
jsdValue::GetProperty (const char *name, jsdIProperty **_rval)
{
ASSERT_VALID_VALUE;
JSContext *cx = JSD_GetDefaultJSContext (mCx);
/* not rooting this */
JSString *jstr_name = JS_NewStringCopyZ (cx, name);
JSDProperty *prop = JSD_GetValueProperty (mCx, mValue, jstr_name);
*_rval = jsdProperty::FromPtr (mCx, prop);
return NS_OK;
}
NS_IMETHODIMP
jsdValue::Refresh()
{
ASSERT_VALID_VALUE;
JSD_RefreshValue (mCx, mValue);
return NS_OK;
}
/******************************************************************************
* debugger service implementation
******************************************************************************/
NS_IMPL_THREADSAFE_ISUPPORTS1(jsdService, jsdIDebuggerService);
NS_IMETHODIMP
jsdService::GetIsOn (PRBool *_rval)
{
*_rval = mOn;
return NS_OK;
}
NS_IMETHODIMP
jsdService::On (void)
{
nsresult rv;
/* get JS things from the CallContext */
nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
if (!xpc) return NS_ERROR_FAILURE;
nsCOMPtr<nsIXPCNativeCallContext> cc;
rv = xpc->GetCurrentNativeCallContext(getter_AddRefs(cc));
if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
JSContext *cx;
rv = cc->GetJSContext (&cx);
if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
return OnForRuntime(JS_GetRuntime (cx));
}
NS_IMETHODIMP
jsdService::OnForRuntime (JSRuntime *rt)
{
if (mOn)
return (rt == mRuntime) ? NS_OK : NS_ERROR_ALREADY_INITIALIZED;
mRuntime = rt;
if (gLastGCProc == jsds_GCCallbackProc)
/* condition indicates that the callback proc has not been set yet */
gLastGCProc = JS_SetGCCallbackRT (rt, jsds_GCCallbackProc);
mCx = JSD_DebuggerOnForUser (rt, NULL, NULL);
if (!mCx)
return NS_ERROR_FAILURE;
/* If any of these mFooHook objects are installed, do the required JSD
* hookup now. See also, jsdService::SetFooHook().
*/
if (mThrowHook)
JSD_SetThrowHook (mCx, jsds_ExecutionHookProc, NULL);
if (mScriptHook)
JSD_SetScriptHook (mCx, jsds_ScriptHookProc, NULL);
if (mInterruptHook)
JSD_SetInterruptHook (mCx, jsds_ExecutionHookProc, NULL);
if (mDebuggerHook)
JSD_SetDebuggerHook (mCx, jsds_ExecutionHookProc, NULL);
if (mErrorHook)
JSD_SetDebugBreakHook (mCx, jsds_ExecutionHookProc, NULL);
mOn = PR_TRUE;
return NS_OK;
}
NS_IMETHODIMP
jsdService::Off (void)
{
if (!mCx || !mRuntime)
return NS_ERROR_NOT_INITIALIZED;
if (gDeadScripts)
if (gGCStatus == JSGC_END)
jsds_NotifyPendingDeadScripts();
else
return NS_ERROR_NOT_AVAILABLE;
/*
if (gLastGCProc != jsds_GCCallbackProc)
JS_SetGCCallbackRT (mRuntime, gLastGCProc);
*/
jsdValue::InvalidateAll();
jsdProperty::InvalidateAll();
ClearAllBreakpoints();
JSD_SetThrowHook (mCx, NULL, NULL);
JSD_SetScriptHook (mCx, NULL, NULL);
JSD_SetInterruptHook (mCx, NULL, NULL);
JSD_SetDebuggerHook (mCx, NULL, NULL);
JSD_SetDebugBreakHook (mCx, NULL, NULL);
JSD_DebuggerOff (mCx);
mCx = nsnull;
mRuntime = nsnull;
mOn = PR_FALSE;
return NS_OK;
}
NS_IMETHODIMP
jsdService::EnumerateScripts (jsdIScriptEnumerator *enumerator)
{
ASSERT_VALID_CONTEXT;
JSDScript *script;
JSDScript *iter = NULL;
PRBool cont_flag;
nsresult rv = NS_OK;
JSD_LockScriptSubsystem(mCx);
while((script = JSD_IterateScripts(mCx, &iter)) != NULL) {
rv = enumerator->EnumerateScript (jsdScript::FromPtr(mCx, script),
&cont_flag);
if (NS_FAILED(rv) || !cont_flag)
break;
}
JSD_UnlockScriptSubsystem(mCx);
return rv;
}
NS_IMETHODIMP
jsdService::ClearAllBreakpoints (void)
{
ASSERT_VALID_CONTEXT;
JSD_LockScriptSubsystem(mCx);
JSD_ClearAllExecutionHooks (mCx);
JSD_UnlockScriptSubsystem(mCx);
return NS_OK;
}
NS_IMETHODIMP
jsdService::EnterNestedEventLoop (PRUint32 *_rval)
{
nsCOMPtr<nsIAppShell> appShell(do_CreateInstance(kAppShellCID));
NS_ENSURE_TRUE(appShell, NS_ERROR_FAILURE);
appShell->Create(0, nsnull);
appShell->Spinup();
// Store locally so it doesn't die on us
nsCOMPtr<nsIJSContextStack>
stack(do_GetService("@mozilla.org/js/xpc/ContextStack;1"));
nsresult rv = NS_OK;
PRUint32 nestLevel = ++mNestedLoopLevel;
if(stack && NS_SUCCEEDED(stack->Push(nsnull)))
{
while(NS_SUCCEEDED(rv) && mNestedLoopLevel >= nestLevel)
{
void* data;
PRBool isRealEvent;
//PRBool processEvent;
rv = appShell->GetNativeEvent(isRealEvent, data);
if(NS_SUCCEEDED(rv))
{
appShell->DispatchNativeEvent(isRealEvent, data);
}
}
JSContext* cx;
stack->Pop(&cx);
NS_ASSERTION(cx == nsnull, "JSContextStack mismatch");
}
else
rv = NS_ERROR_FAILURE;
appShell->Spindown();
NS_ASSERTION (mNestedLoopLevel <= nestLevel,
"nested event didn't unwind properly");
if (mNestedLoopLevel == nestLevel)
--mNestedLoopLevel;
*_rval = mNestedLoopLevel;
return rv;
}
NS_IMETHODIMP
jsdService::ExitNestedEventLoop (PRUint32 *_rval)
{
if (mNestedLoopLevel > 0)
--mNestedLoopLevel;
else
return NS_ERROR_FAILURE;
*_rval = mNestedLoopLevel;
return NS_OK;
}
/* hook attribute get/set functions */
NS_IMETHODIMP
jsdService::SetBreakpointHook (jsdIExecutionHook *aHook)
{
mBreakpointHook = aHook;
return NS_OK;
}
NS_IMETHODIMP
jsdService::GetBreakpointHook (jsdIExecutionHook **aHook)
{
*aHook = mBreakpointHook;
NS_IF_ADDREF(*aHook);
return NS_OK;
}
NS_IMETHODIMP
jsdService::SetErrorHook (jsdIExecutionHook *aHook)
{
mErrorHook = aHook;
/* if the debugger isn't initialized, that's all we can do for now. The
* OnForRuntime() method will do the rest when the coast is clear.
*/
if (!mCx)
return NS_OK;
if (aHook)
JSD_SetDebugBreakHook (mCx, jsds_ExecutionHookProc, NULL);
else
JSD_ClearDebugBreakHook (mCx);
return NS_OK;
}
NS_IMETHODIMP
jsdService::GetErrorHook (jsdIExecutionHook **aHook)
{
*aHook = mErrorHook;
NS_IF_ADDREF(*aHook);
return NS_OK;
}
NS_IMETHODIMP
jsdService::SetDebuggerHook (jsdIExecutionHook *aHook)
{
mDebuggerHook = aHook;
/* if the debugger isn't initialized, that's all we can do for now. The
* OnForRuntime() method will do the rest when the coast is clear.
*/
if (!mCx)
return NS_OK;
if (aHook)
JSD_SetDebuggerHook (mCx, jsds_ExecutionHookProc, NULL);
else
JSD_ClearDebuggerHook (mCx);
return NS_OK;
}
NS_IMETHODIMP
jsdService::GetDebuggerHook (jsdIExecutionHook **aHook)
{
*aHook = mDebuggerHook;
NS_IF_ADDREF(*aHook);
return NS_OK;
}
NS_IMETHODIMP
jsdService::SetInterruptHook (jsdIExecutionHook *aHook)
{
mInterruptHook = aHook;
/* if the debugger isn't initialized, that's all we can do for now. The
* OnForRuntime() method will do the rest when the coast is clear.
*/
if (!mCx)
return NS_OK;
if (aHook)
JSD_SetInterruptHook (mCx, jsds_ExecutionHookProc, NULL);
else
JSD_ClearInterruptHook (mCx);
return NS_OK;
}
NS_IMETHODIMP
jsdService::GetInterruptHook (jsdIExecutionHook **aHook)
{
*aHook = mInterruptHook;
NS_IF_ADDREF(*aHook);
return NS_OK;
}
NS_IMETHODIMP
jsdService::SetScriptHook (jsdIScriptHook *aHook)
{
mScriptHook = aHook;
/* if the debugger isn't initialized, that's all we can do for now. The
* OnForRuntime() method will do the rest when the coast is clear.
*/
if (!mCx)
return NS_OK;
if (aHook)
JSD_SetScriptHook (mCx, jsds_ScriptHookProc, NULL);
else
JSD_SetScriptHook (mCx, NULL, NULL);
return NS_OK;
}
NS_IMETHODIMP
jsdService::GetScriptHook (jsdIScriptHook **aHook)
{
*aHook = mScriptHook;
NS_IF_ADDREF(*aHook);
return NS_OK;
}
NS_IMETHODIMP
jsdService::SetThrowHook (jsdIExecutionHook *aHook)
{
mThrowHook = aHook;
/* if the debugger isn't initialized, that's all we can do for now. The
* OnForRuntime() method will do the rest when the coast is clear.
*/
if (!mCx)
return NS_OK;
if (aHook)
JSD_SetThrowHook (mCx, jsds_ExecutionHookProc, NULL);
else
JSD_ClearThrowHook (mCx);
return NS_OK;
}
NS_IMETHODIMP
jsdService::GetThrowHook (jsdIExecutionHook **aHook)
{
*aHook = mThrowHook;
NS_IF_ADDREF(*aHook);
return NS_OK;
}
jsdService *
jsdService::GetService ()
{
if (!gJsds)
gJsds = new jsdService();
NS_IF_ADDREF(gJsds);
return gJsds;
}
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(jsdService, jsdService::GetService);
/* app-start observer. turns on the debugger at app-start if
* js.debugger.autostart pref is true
*/
class jsdASObserver : public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
jsdASObserver ()
{
NS_INIT_ISUPPORTS();
}
};
NS_IMPL_THREADSAFE_ISUPPORTS1(jsdASObserver, nsIObserver);
NS_IMETHODIMP
jsdASObserver::Observe (nsISupports *aSubject, const PRUnichar *aTopic,
const PRUnichar *aData)
{
nsresult rv;
if (!nsCRT::strcmp(aTopic,
NS_LITERAL_STRING(PROFILE_CHANGE_EVENT).get())) {
/* profile change means that the prefs file is loaded, so we can finally
* check to see if we're supposed to start the debugger.
*/
nsCOMPtr<nsIPref> pref = do_GetService (NS_PREF_CTRID);
if (!pref)
return NS_ERROR_FAILURE;
PRBool f = PR_FALSE;
rv = pref->GetBoolPref("js.debugger.autostart", &f);
if (NS_SUCCEEDED(rv) && f) {
jsdService *jsds = jsdService::GetService();
nsCOMPtr<nsIJSRuntimeService> rts = do_GetService(NS_JSRT_CTRID);
JSRuntime *rt;
rts->GetRuntime (&rt);
jsds->OnForRuntime(rt);
}
} else if (!nsCRT::strcmp(aTopic,
NS_LITERAL_STRING(APPSTART_CATEGORY).get())) {
/* on app-start, register an interest in hearing when the profile is
* loaded.
*/
nsCOMPtr<nsIObserverService> observerService =
do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv)) return rv;
if (observerService) {
rv = observerService->AddObserver(this,
NS_LITERAL_STRING(PROFILE_CHANGE_EVENT).get());
if (NS_FAILED(rv)) return rv;
}
}
return NS_OK;
}
NS_GENERIC_FACTORY_CONSTRUCTOR(jsdASObserver);
static NS_METHOD
RegisterASObserver (nsIComponentManager *aCompMgr, nsIFile *aPath,
const char *registryLocation, const char *componentType,
const nsModuleComponentInfo *info)
{
nsresult rv;
nsCOMPtr<nsICategoryManager>
categoryManager(do_GetService(NS_CATMAN_CTRID, &rv));
if (NS_SUCCEEDED(rv)) {
rv = categoryManager->AddCategoryEntry(APPSTART_CATEGORY,
"JSDebugger app-start observer",
jsdASObserverContractID,
PR_TRUE, PR_TRUE,nsnull);
}
return rv;
}
static NS_METHOD
UnRegisterASObserver(nsIComponentManager *aCompMgr, nsIFile *aPath,
const char *registryLocation,
const nsModuleComponentInfo *info)
{
nsresult rv;
nsCOMPtr<nsICategoryManager>
categoryManager(do_GetService(NS_CATMAN_CTRID, &rv));
if (NS_SUCCEEDED(rv)) {
rv =
categoryManager->DeleteCategoryEntry(APPSTART_CATEGORY,
"JSDebugger app-start observer",
PR_TRUE);
}
return rv;
}
static nsModuleComponentInfo components[] = {
{"JSDService", JSDSERVICE_CID, jsdServiceContractID, jsdServiceConstructor},
{"JSDASObserver", JSDASO_CID, jsdASObserverContractID,
jsdASObserverConstructor, RegisterASObserver, UnRegisterASObserver }
};
NS_IMPL_NSGETMODULE(JavaScript_Debugger, components);
/********************************************************************************
********************************************************************************
* graveyard
*/
#if 0
/* Thread States */
NS_IMPL_THREADSAFE_ISUPPORTS1(jsdThreadState, jsdIThreadState);
NS_IMETHODIMP
jsdThreadState::GetJSDContext(JSDContext **_rval)
{
*_rval = mCx;
return NS_OK;
}
NS_IMETHODIMP
jsdThreadState::GetJSDThreadState(JSDThreadState **_rval)
{
*_rval = mThreadState;
return NS_OK;
}
NS_IMETHODIMP
jsdThreadState::GetFrameCount (PRUint32 *_rval)
{
*_rval = JSD_GetCountOfStackFrames (mCx, mThreadState);
return NS_OK;
}
NS_IMETHODIMP
jsdThreadState::GetTopFrame (jsdIStackFrame **_rval)
{
JSDStackFrameInfo *sfi = JSD_GetStackFrame (mCx, mThreadState);
*_rval = jsdStackFrame::FromPtr (mCx, mThreadState, sfi);
return NS_OK;
}
NS_IMETHODIMP
jsdThreadState::GetPendingException(jsdIValue **_rval)
{
JSDValue *jsdv = JSD_GetException (mCx, mThreadState);
*_rval = jsdValue::FromPtr (mCx, jsdv);
return NS_OK;
}
NS_IMETHODIMP
jsdThreadState::SetPendingException(jsdIValue *aException)
{
JSDValue *jsdv;
nsresult rv = aException->GetJSDValue (&jsdv);
if (NS_FAILED(rv))
return NS_ERROR_FAILURE;
if (!JSD_SetException (mCx, mThreadState, jsdv))
return NS_ERROR_FAILURE;
return NS_OK;
}
#endif