Much of this is under the auspices of bug 15794. r=mccabe.

- map xpcshell's 'quit()' to a loop exit rather than calling
'exit(0)' so that the cleanup and leak detection code will still
get called.

- add NS_InitXPCOM and NS_ShutdownXPCOM to xpcshell to run said
cleanup and leak detection code.

- use more NS_IF_* macros

- fix numerous places where code assumed that
nsXPConnect::GetXPConnect() does not add a new ref on the
xpconnect singleton object (the behavior changed some time back
but not all the uses did - brainfade!).

- fix nsXPCException::NewException to automatically trim
'dataless' native stackframes off of the front of a stack trace.
The old system of manually telling it how many frames to trim was
not working well. We really want the first frame showing to be an
'interesting' frame so that callers who get exceptions thrown at
them will see some useful information rather than an empty native
frame that represents (but says nothing about) some native frame
in the xpconnect runtime.

- remove an extra addref from the trimming loop in
nsXPCException::NewException.

- Stop building XPCJSStack objects. XPConnect stacks are singly
linked lists of XPCJSStackFrame objects with refcounted links. I
had this stupid idea that each object would have a refcounted
link to a XPCJSStack object that would tie together the lifetimes
of all objects in the chain. This was overcomplex and
unnecessary. The linked list was enough. Any frame without a
refcount deserved to be deleted because it is simply unreachable.
There was no reason to tie together all the lifetimes of each
object in the chain. So this has been simplified in a big way.

- fixed place in xpcthrower.cpp where we were leaking a refcount
on the xpconnect singleton each time an xpcexception was thrown.

- do cleanup and gc() at the end of xpctest_echo.js to use for
leak testing - all wrappers should go away.


git-svn-id: svn://10.0.0.236/trunk@50309 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
jband%netscape.com 1999-10-09 06:01:57 +00:00
parent 580e6e380c
commit 8d3833cb33
13 changed files with 141 additions and 156 deletions

View File

@ -93,6 +93,8 @@ static nsIXPConnect* GetXPConnect()
FILE *gOutFile = NULL;
FILE *gErrFile = NULL;
JSBool gQuitting = JS_FALSE;
static void
my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
{
@ -228,7 +230,8 @@ Quit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
#ifdef LIVECONNECT
JSJ_SimpleShutdown();
#endif
exit(0);
gQuitting = JS_TRUE;
// exit(0);
return JS_FALSE;
}
@ -488,7 +491,7 @@ Process(JSContext *cx, JSObject *obj, char *filename)
#endif
JS_DestroyScript(cx, script);
}
} while (!hitEOF);
} while (!hitEOF && !gQuitting);
fprintf(gOutFile, "\n");
return;
}
@ -592,10 +595,14 @@ main(int argc, char **argv)
JSContext *jscontext;
JSObject *glob;
int result;
nsresult rv;
gErrFile = stderr;
gOutFile = stdout;
rv = NS_InitXPCOM(NULL, NULL, NULL);
NS_ASSERTION( NS_SUCCEEDED(rv), "NS_InitXPCOM failed" );
SetupRegistry();
rt = JS_NewRuntime(8L * 1024L * 1024L);
@ -615,7 +622,6 @@ main(int argc, char **argv)
return 1;
}
nsresult rv;
NS_WITH_SERVICE(nsIJSContextStack, cxstack, "nsThreadJSContextStack", &rv);
if(NS_FAILED(rv))
{
@ -649,6 +655,10 @@ main(int argc, char **argv)
JS_DestroyContext(jscontext);
JS_DestroyRuntime(rt);
JS_ShutDown();
rv = NS_ShutdownXPCOM( NULL );
NS_ASSERTION(NS_SUCCEEDED(rv), "NS_ShutdownXPCOM failed");
return result;
}

View File

@ -66,7 +66,7 @@ private:
xpcPerThreadData::xpcPerThreadData()
: mException(nsnull)
{
// empty
// empty...
}
xpcPerThreadData::~xpcPerThreadData()
@ -155,6 +155,7 @@ nsXPConnect::GetXPConnect()
{
// ctor failed to create an acceptable instance
delete gSelf;
gSelf = nsnull;
}
else
{
@ -176,7 +177,7 @@ nsXPConnect::FreeXPConnect()
if (xpc) {
nsrefcnt cnt;
NS_RELEASE2(xpc, cnt);
#ifdef DEBUG_kipp
#if defined(DEBUG_kipp) || defined(DEBUG_jband)
if (0 != cnt) {
printf("*** dangling reference to nsXPConnect: refcnt=%d\n", cnt);
}
@ -569,11 +570,9 @@ nsXPConnect::SetSecurityManagerForJSContext(JSContext* aJSContext,
return NS_ERROR_INVALID_ARG;
}
if(aManager)
NS_ADDREF(aManager);
NS_IF_ADDREF(aManager);
nsIXPCSecurityManager* oldManager = xpcc->GetSecurityManager();
if(oldManager)
NS_RELEASE(oldManager);
NS_IF_RELEASE(oldManager);
xpcc->SetSecurityManager(aManager);
xpcc->SetSecurityManagerFlags(flags);

View File

@ -986,7 +986,13 @@ XPC_IMPL_GET_OBJ_METHOD(Results);
NS_IMETHODIMP
nsXPCComponents::GetStack(nsIJSStackFrameLocation * *aStack)
{
return nsXPConnect::GetXPConnect()->GetCurrentJSStack(aStack);
nsresult rv;
nsXPConnect* xpc = nsXPConnect::GetXPConnect();
if(!xpc)
return NS_ERROR_FAILURE;
rv = xpc->GetCurrentJSStack(aStack);
NS_RELEASE(xpc);
return rv;
}
NS_IMETHODIMP

View File

@ -715,7 +715,7 @@ ConstructException(nsresult rv, const char* message,
else
sz = (char*) msg; // I promise to play nice after casting away const
e = nsXPCException::NewException(sz, rv, nsnull, data, 0);
e = nsXPCException::NewException(sz, rv, nsnull, data);
if(sz && sz != msg)
JS_smprintf_free(sz);

View File

@ -227,7 +227,11 @@ nsXPCException::Initialize(const char *aMessage, nsresult aResult, const char *a
else
{
nsresult rv;
rv = nsXPConnect::GetXPConnect()->GetCurrentJSStack(&mLocation);
nsXPConnect* xpc = nsXPConnect::GetXPConnect();
if(!xpc)
return NS_ERROR_FAILURE;
rv = xpc->GetCurrentJSStack(&mLocation);
NS_RELEASE(xpc);
if(NS_FAILED(rv))
return rv;
}
@ -295,8 +299,7 @@ nsXPCException*
nsXPCException::NewException(const char *aMessage,
nsresult aResult,
nsIJSStackFrameLocation *aLocation,
nsISupports *aData,
PRInt32 aLeadingFramesToTrim)
nsISupports *aData)
{
nsresult rv;
nsXPCException* e = new nsXPCException();
@ -312,7 +315,14 @@ nsXPCException::NewException(const char *aMessage,
}
else
{
rv = nsXPConnect::GetXPConnect()->GetCurrentJSStack(&location);
nsXPConnect* xpc = nsXPConnect::GetXPConnect();
if(!xpc)
{
NS_RELEASE(e);
return nsnull;
}
rv = xpc->GetCurrentJSStack(&location);
NS_RELEASE(xpc);
if(NS_FAILED(rv) || !location)
{
NS_RELEASE(e);
@ -320,17 +330,17 @@ nsXPCException::NewException(const char *aMessage,
}
}
// at this point we have non-null location with one extra addref
for(PRInt32 i = aLeadingFramesToTrim - 1; i >= 0; i--)
// We want to trim off any leading native 'dataless' frames
while(1)
{
PRBool isJSFrame;
PRInt32 lineNumber;
if(NS_FAILED(location->GetIsJSFrame(&isJSFrame)) || isJSFrame ||
NS_FAILED(location->GetLineNumber(&lineNumber)) || lineNumber)
break;
nsIJSStackFrameLocation* caller;
if(NS_FAILED(location->GetCaller(&caller)) || !caller)
{
NS_ASSERTION(0, "tried to trim too many frames");
NS_RELEASE(location);
NS_RELEASE(e);
return nsnull;
}
NS_ADDREF(caller);
break;
NS_RELEASE(location);
location = caller;
}

View File

@ -71,7 +71,6 @@ JSContext2XPCContextMap::newMap(int size)
return nsnull;
}
JSContext2XPCContextMap::JSContext2XPCContextMap(int size)
{
mHashtable = JS_NewHashTable(size, hash_root,
@ -99,7 +98,6 @@ JSObject2WrappedJSMap::newMap(int size)
return nsnull;
}
JSObject2WrappedJSMap::JSObject2WrappedJSMap(int size)
{
mHashtable = JS_NewHashTable(size, hash_root,

View File

@ -1031,7 +1031,6 @@ class nsJSRuntimeServiceImpl : public nsIJSRuntimeService
};
/***************************************************************************/
class XPCJSStackFrame;
class XPCJSStack
{
@ -1044,16 +1043,8 @@ public:
const char* aFunctionName,
PRInt32 aLineNumber,
nsIJSStackFrameLocation* aCaller);
friend class XPCJSStackFrame;
private:
XPCJSStack();
~XPCJSStack();
void AddRef();
void Release();
XPCJSStackFrame* mTopFrame;
int mRefCount;
XPCJSStack(); // not implemented
};
/***************************************************************************/
@ -1068,8 +1059,7 @@ public:
static nsXPCException* NewException(const char *aMessage,
nsresult aResult,
nsIJSStackFrameLocation *aLocation,
nsISupports *aData,
PRInt32 aLeadingFramesToTrim);
nsISupports *aData);
static JSBool NameAndFormatForNSResult(nsresult rv,
const char** name,

View File

@ -59,8 +59,7 @@ public:
/* string toString (); */
NS_IMETHOD ToString(char **_retval);
static XPCJSStackFrame* CreateStack(JSContext* cx, XPCJSStack* stack,
JSStackFrame* fp);
static XPCJSStackFrame* CreateStack(JSContext* cx, JSStackFrame* fp);
static XPCJSStackFrame* CreateStackFrameLocation(
JSBool isJSFrame,
@ -73,7 +72,6 @@ public:
virtual ~XPCJSStackFrame();
private:
XPCJSStack* mStack;
nsIJSStackFrameLocation* mCaller;
char* mFilename;
@ -84,51 +82,14 @@ private:
/**********************************************/
XPCJSStack::XPCJSStack()
: mTopFrame(nsnull),
mRefCount(0)
{
// empty body
}
XPCJSStack::~XPCJSStack()
{
if(mTopFrame)
NS_RELEASE(mTopFrame);
}
void
XPCJSStack::AddRef()
{
mRefCount++;
}
void
XPCJSStack::Release()
{
if(0 == --mRefCount)
delete this;
}
// static
nsIJSStackFrameLocation*
XPCJSStack::CreateStack(JSContext* cx)
{
if(!cx)
return nsnull;
if(!cx->fp)
if(!cx || !cx->fp)
return nsnull;
XPCJSStack* self = new XPCJSStack();
if(!self)
return nsnull;
if(!(self->mTopFrame = XPCJSStackFrame::CreateStack(cx, self, cx->fp)))
return nsnull;
NS_ADDREF(self->mTopFrame);
self->mRefCount = 1;
return self->mTopFrame;
return XPCJSStackFrame::CreateStack(cx, cx->fp);
}
// static
@ -151,8 +112,7 @@ XPCJSStack::CreateStackFrameLocation(JSBool isJSFrame,
/**********************************************/
XPCJSStackFrame::XPCJSStackFrame()
: mStack(nsnull),
mCaller(nsnull),
: mCaller(nsnull),
mFilename(nsnull),
mFunname(nsnull),
mLineno(0),
@ -171,49 +131,20 @@ XPCJSStackFrame::~XPCJSStackFrame()
NS_RELEASE(mCaller);
}
NS_IMPL_QUERY_INTERFACE1(XPCJSStackFrame, nsIJSStackFrameLocation)
// do chained ref counting
nsrefcnt
XPCJSStackFrame::AddRef(void)
{
if(mStack)
mStack->AddRef();
++mRefCnt;
NS_LOG_ADDREF(this, mRefCnt, "XPCJSStackFrame", sizeof(*this));
return mRefCnt;
}
nsrefcnt
XPCJSStackFrame::Release(void)
{
// use a local since there can be recursion here.
nsrefcnt count = --mRefCnt;
NS_LOG_RELEASE(this, mRefCnt, "XPCJSStackFrame");
if(0 == count)
{
NS_DELETEXPCOM(this);
return 0;
}
if(mStack)
mStack->Release();
return count;
}
NS_IMPL_ISUPPORTS1(XPCJSStackFrame, nsIJSStackFrameLocation)
XPCJSStackFrame*
XPCJSStackFrame::CreateStack(JSContext* cx, XPCJSStack* stack,
JSStackFrame* fp)
XPCJSStackFrame::CreateStack(JSContext* cx, JSStackFrame* fp)
{
XPCJSStackFrame* self = new XPCJSStackFrame();
JSBool failed = JS_FALSE;
if(self)
{
NS_ADDREF(self);
if(fp->down)
{
if(!(self->mCaller = CreateStack(cx, stack, fp->down)))
if(!(self->mCaller = CreateStack(cx, fp->down)))
failed = JS_TRUE;
}
@ -255,17 +186,8 @@ XPCJSStackFrame::CreateStack(JSContext* cx, XPCJSStack* stack,
}
}
}
if(!failed)
{
NS_ADDREF(self);
self->mStack = stack;
}
else
{
delete self;
self = nsnull;
}
if(failed)
NS_RELEASE(self);
}
return self;
@ -273,12 +195,11 @@ XPCJSStackFrame::CreateStack(JSContext* cx, XPCJSStack* stack,
// static
XPCJSStackFrame*
XPCJSStackFrame::CreateStackFrameLocation(
JSBool isJSFrame,
const char* aFilename,
const char* aFunctionName,
PRInt32 aLineNumber,
nsIJSStackFrameLocation* aCaller)
XPCJSStackFrame::CreateStackFrameLocation(JSBool isJSFrame,
const char* aFilename,
const char* aFunctionName,
PRInt32 aLineNumber,
nsIJSStackFrameLocation* aCaller)
{
JSBool failed = JS_FALSE;
XPCJSStackFrame* self = new XPCJSStackFrame();

View File

@ -90,16 +90,19 @@ GetMyStack()
* is using it.
*/
NS_IMPL_ISUPPORTS1(nsXPCThreadJSContextStackImpl, nsIJSContextStack)
static nsXPCThreadJSContextStackImpl* gXPCThreadJSContextStack = nsnull;
nsXPCThreadJSContextStackImpl::nsXPCThreadJSContextStackImpl()
{
NS_INIT_ISUPPORTS();
}
nsXPCThreadJSContextStackImpl::~nsXPCThreadJSContextStackImpl() {}
NS_IMPL_ISUPPORTS1(nsXPCThreadJSContextStackImpl, nsIJSContextStack)
static nsXPCThreadJSContextStackImpl* gXPCThreadJSContextStack = nsnull;
nsXPCThreadJSContextStackImpl::~nsXPCThreadJSContextStackImpl()
{
gXPCThreadJSContextStack = nsnull;
}
//static
nsXPCThreadJSContextStackImpl*
@ -108,7 +111,9 @@ nsXPCThreadJSContextStackImpl::GetSingleton()
if(!gXPCThreadJSContextStack)
{
gXPCThreadJSContextStack = new nsXPCThreadJSContextStackImpl();
if(gXPCThreadJSContextStack) {
if(gXPCThreadJSContextStack)
{
// hold an extra reference to lock it down
NS_ADDREF(gXPCThreadJSContextStack);
}
}
@ -119,7 +124,16 @@ nsXPCThreadJSContextStackImpl::GetSingleton()
void
nsXPCThreadJSContextStackImpl::FreeSingleton()
{
NS_IF_RELEASE(gXPCThreadJSContextStack);
nsXPCThreadJSContextStackImpl* tcs = gXPCThreadJSContextStack;
if (tcs) {
nsrefcnt cnt;
NS_RELEASE2(tcs, cnt);
#if defined(DEBUG_kipp) || defined(DEBUG_jband)
if (0 != cnt) {
printf("*** dangling reference to nsXPCThreadJSContextStackImpl: refcnt=%d\n", cnt);
}
#endif
}
}
/* readonly attribute PRInt32 Count; */

View File

@ -106,22 +106,26 @@ XPCJSThrower::ThrowBadResultException(nsresult rv,
nsIXPCException* e;
nsXPConnect* xpc = nsXPConnect::GetXPConnect();
if(xpc && NS_SUCCEEDED(xpc->GetPendingException(&e)) && e)
JSBool success = JS_FALSE;
if(xpc)
{
JSBool success = JS_FALSE;
xpc->SetPendingException(nsnull);
nsresult e_result;
if(NS_SUCCEEDED(e->GetResult(&e_result)) && e_result == result)
if(NS_SUCCEEDED(xpc->GetPendingException(&e)) && e)
{
if(!ThrowExceptionObject(cx, e))
JS_ReportOutOfMemory(cx);
success = JS_TRUE;
xpc->SetPendingException(nsnull);
nsresult e_result;
if(NS_SUCCEEDED(e->GetResult(&e_result)) && e_result == result)
{
if(!ThrowExceptionObject(cx, e))
JS_ReportOutOfMemory(cx);
success = JS_TRUE;
}
NS_RELEASE(e);
}
NS_RELEASE(e);
if(success)
return;
}
NS_IF_RELEASE(xpc);
if(success)
return;
// else...
@ -193,7 +197,7 @@ void
XPCJSThrower::BuildAndThrowException(JSContext* cx, nsresult rv, const char* sz)
{
JSBool success = JS_FALSE;
nsIXPCException* e = nsXPCException::NewException(sz,rv,nsnull,nsnull,1);
nsIXPCException* e = nsXPCException::NewException(sz, rv, nsnull, nsnull);
if(e)
{
@ -211,8 +215,13 @@ XPCJSThrower::ThrowExceptionObject(JSContext* cx, nsIXPCException* e)
if(e)
{
nsIXPConnectWrappedNative* wrapper;
if(NS_SUCCEEDED(nsXPConnect::GetXPConnect()->
WrapNative(cx, e, NS_GET_IID(nsIXPCException), &wrapper)))
nsresult rv;
nsXPConnect* xpc = nsXPConnect::GetXPConnect();
if(!xpc)
return JS_FALSE;
rv = xpc->WrapNative(cx, e, NS_GET_IID(nsIXPCException), &wrapper);
NS_RELEASE(xpc);
if(NS_SUCCEEDED(rv))
{
JSObject* obj;
if(NS_SUCCEEDED(wrapper->GetJSObject(&obj)))

View File

@ -92,7 +92,6 @@ nsXPCWrappedJSClass::nsXPCWrappedJSClass(XPCContext* xpcc, REFNSIID aIID,
mDescriptors(nsnull)
{
NS_ADDREF(mInfo);
NS_INIT_REFCNT();
NS_ADDREF_THIS();
@ -987,6 +986,8 @@ done:
if(cx)
JS_SetErrorReporter(cx, older);
NS_IF_RELEASE(xpc);
return retval;
}

View File

@ -443,7 +443,7 @@ nsXPCWrappedNativeClass::CallWrappedMethod(JSContext* cx,
if(!xpcc)
goto done;
nsXPConnect::GetXPConnect()->SetPendingException(nsnull);
xpcc->GetXPConnect()->SetPendingException(nsnull);
xpcc->SetLastResult(NS_ERROR_UNEXPECTED);
// make sure we have what we need...

View File

@ -442,3 +442,30 @@ print();
echoJS.SetReceiver(null);
echo.SetReceiver(null);
// cleanup...
NS_ISUPPORTS_IID = null;
NS_ITESTXPC_FOO_IID = null;
echo = null;
echoJS = null;
in_out_results = null;
in_out_results1 = null;
in_out_results2 = null;
inout_params = null;
interval = null;
iterations = null;
lastResult = null;
nsID = null;
receiver = null;
receiver2 = null;
receiver3 = null;
receiver_results = null;
resend_params = null;
send_params = null;
start_time = null;
test_string = null;
test_string2 = null;
gc();
gc();
dumpXPC(6);