/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * 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 Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * John Bandhauer <jband@netscape.com> (original author) * Mark Hammond <MarkH@ActiveState.com> * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* An implementaion of nsIException. */ #include "xpcprivate.h" #include "nsNetError.h" /***************************************************************************/ /* Quick and dirty mapping of well known result codes to strings. We only * call this when building an exception object, so iterating the short array * is not too bad. * * It sure would be nice to have exceptions declared in idl and available * in some more global way at runtime. */ static struct ResultMap {nsresult rv; const char* name; const char* format;} map[] = { #define XPC_MSG_DEF(val, format) \ {(val), #val, format}, #include "xpc.msg" #undef XPC_MSG_DEF {0,0,0} // sentinel to mark end of array }; #define RESULT_COUNT ((sizeof(map) / sizeof(map[0]))-1) // static JSBool nsXPCException::NameAndFormatForNSResult(nsresult rv, const char** name, const char** format) { for(ResultMap* p = map; p->name; p++) { if(rv == p->rv) { if(name) *name = p->name; if(format) *format = p->format; return JS_TRUE; } } return JS_FALSE; } // static void* nsXPCException::IterateNSResults(nsresult* rv, const char** name, const char** format, void** iterp) { ResultMap* p = (ResultMap*) *iterp; if(!p) p = map; else p++; if(!p->name) p = nsnull; else { if(rv) *rv = p->rv; if(name) *name = p->name; if(format) *format = p->format; } *iterp = p; return p; } // static PRUint32 nsXPCException::GetNSResultCount() { return RESULT_COUNT; } /***************************************************************************/ NS_INTERFACE_MAP_BEGIN(nsXPCException) NS_INTERFACE_MAP_ENTRY(nsIException) NS_INTERFACE_MAP_ENTRY(nsIXPCException) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIException) NS_IMPL_QUERY_CLASSINFO(nsXPCException) NS_INTERFACE_MAP_END_THREADSAFE NS_IMPL_THREADSAFE_ADDREF(nsXPCException) NS_IMPL_THREADSAFE_RELEASE(nsXPCException) NS_IMPL_CI_INTERFACE_GETTER1(nsXPCException, nsIXPCException) nsXPCException::nsXPCException() : mMessage(nsnull), mResult(0), mName(nsnull), mLocation(nsnull), mData(nsnull), mFilename(nsnull), mLineNumber(0), mInner(nsnull), mInitialized(PR_FALSE) { MOZ_COUNT_CTOR(nsXPCException); } nsXPCException::~nsXPCException() { MOZ_COUNT_DTOR(nsXPCException); Reset(); } PRBool nsXPCException::StealThrownJSVal(jsval *vp) { if(mThrownJSVal.IsHeld()) { *vp = mThrownJSVal.Release(); return PR_TRUE; } return PR_FALSE; } void nsXPCException::StowThrownJSVal(JSContext *cx, jsval v) { if (mThrownJSVal.Hold(cx)) mThrownJSVal = v; } void nsXPCException::Reset() { if(mMessage) { nsMemory::Free(mMessage); mMessage = nsnull; } if(mName) { nsMemory::Free(mName); mName = nsnull; } if(mFilename) { nsMemory::Free(mFilename); mFilename = nsnull; } mLineNumber = (PRUint32)-1; NS_IF_RELEASE(mLocation); NS_IF_RELEASE(mData); NS_IF_RELEASE(mInner); } /* readonly attribute string message; */ NS_IMETHODIMP nsXPCException::GetMessageMoz(char * *aMessage) { if(!mInitialized) return NS_ERROR_NOT_INITIALIZED; XPC_STRING_GETTER_BODY(aMessage, mMessage); } /* readonly attribute nsresult result; */ NS_IMETHODIMP nsXPCException::GetResult(nsresult *aResult) { if(!aResult) return NS_ERROR_NULL_POINTER; if(!mInitialized) return NS_ERROR_NOT_INITIALIZED; *aResult = mResult; return NS_OK; } /* readonly attribute string name; */ NS_IMETHODIMP nsXPCException::GetName(char * *aName) { if(!mInitialized) return NS_ERROR_NOT_INITIALIZED; const char* name = mName; if(!name) NameAndFormatForNSResult(mResult, &name, nsnull); XPC_STRING_GETTER_BODY(aName, name); } /* readonly attribute string filename; */ NS_IMETHODIMP nsXPCException::GetFilename(char * *aFilename) { if(!mInitialized) return NS_ERROR_NOT_INITIALIZED; XPC_STRING_GETTER_BODY(aFilename, mFilename); } /* readonly attribute PRUint32 lineNumber; */ NS_IMETHODIMP nsXPCException::GetLineNumber(PRUint32 *aLineNumber) { if(!aLineNumber) return NS_ERROR_NULL_POINTER; if(!mInitialized) return NS_ERROR_NOT_INITIALIZED; *aLineNumber = mLineNumber; return NS_OK; } /* readonly attribute PRUint32 columnNumber; */ NS_IMETHODIMP nsXPCException::GetColumnNumber(PRUint32 *aColumnNumber) { NS_ENSURE_ARG_POINTER(aColumnNumber); if(!mInitialized) return NS_ERROR_NOT_INITIALIZED; *aColumnNumber = 0; return NS_OK; } /* readonly attribute nsIStackFrame location; */ NS_IMETHODIMP nsXPCException::GetLocation(nsIStackFrame * *aLocation) { if(!aLocation) return NS_ERROR_NULL_POINTER; if(!mInitialized) return NS_ERROR_NOT_INITIALIZED; *aLocation = mLocation; NS_IF_ADDREF(mLocation); return NS_OK; } /* readonly attribute nsISupports data; */ NS_IMETHODIMP nsXPCException::GetData(nsISupports * *aData) { if(!aData) return NS_ERROR_NULL_POINTER; if(!mInitialized) return NS_ERROR_NOT_INITIALIZED; *aData = mData; NS_IF_ADDREF(mData); return NS_OK; } /* readonly attribute nsIException inner; */ NS_IMETHODIMP nsXPCException::GetInner(nsIException* *aException) { if(!aException) return NS_ERROR_NULL_POINTER; if(!mInitialized) return NS_ERROR_NOT_INITIALIZED; *aException = mInner; NS_IF_ADDREF(mInner); return NS_OK; } /* void initialize (in string aMessage, in nsresult aResult, in string aName, in nsIStackFrame aLocation, in nsISupports aData, in nsIException aInner); */ NS_IMETHODIMP nsXPCException::Initialize(const char *aMessage, nsresult aResult, const char *aName, nsIStackFrame *aLocation, nsISupports *aData, nsIException *aInner) { if(mInitialized) return NS_ERROR_ALREADY_INITIALIZED; Reset(); if(aMessage) { if(!(mMessage = (char*) nsMemory::Clone(aMessage, sizeof(char)*(strlen(aMessage)+1)))) return NS_ERROR_OUT_OF_MEMORY; } if(aName) { if(!(mName = (char*) nsMemory::Clone(aName, sizeof(char)*(strlen(aName)+1)))) return NS_ERROR_OUT_OF_MEMORY; } mResult = aResult; if(aLocation) { mLocation = aLocation; NS_ADDREF(mLocation); // For now, fill in our location details from our stack frame. // Later we may allow other locations? nsresult rc; if(NS_FAILED(rc = aLocation->GetFilename(&mFilename))) return rc; if(NS_FAILED(rc = aLocation->GetLineNumber(&mLineNumber))) return rc; } else { nsresult rv; nsXPConnect* xpc = nsXPConnect::GetXPConnect(); if(!xpc) return NS_ERROR_FAILURE; rv = xpc->GetCurrentJSStack(&mLocation); if(NS_FAILED(rv)) return rv; } if(aData) { mData = aData; NS_ADDREF(mData); } if(aInner) { mInner = aInner; NS_ADDREF(mInner); } mInitialized = PR_TRUE; return NS_OK; } /* string toString (); */ NS_IMETHODIMP nsXPCException::ToString(char **_retval) { if(!_retval) return NS_ERROR_NULL_POINTER; if(!mInitialized) return NS_ERROR_NOT_INITIALIZED; static const char defaultMsg[] = "<no message>"; static const char defaultLocation[] = "<unknown>"; static const char format[] = "[Exception... \"%s\" nsresult: \"0x%x (%s)\" location: \"%s\" data: %s]"; char* indicatedLocation = nsnull; if(mLocation) { // we need to free this if it does not fail nsresult rv = mLocation->ToString(&indicatedLocation); if(NS_FAILED(rv)) return rv; } const char* msg = mMessage ? mMessage : nsnull; const char* location = indicatedLocation ? indicatedLocation : defaultLocation; const char* resultName = mName; if(!resultName && !NameAndFormatForNSResult(mResult, &resultName, (!msg) ? &msg : nsnull)) { if(!msg) msg = defaultMsg; resultName = "<unknown>"; } const char* data = mData ? "yes" : "no"; char* temp = JS_smprintf(format, msg, mResult, resultName, location, data); if(indicatedLocation) nsMemory::Free(indicatedLocation); char* final = nsnull; if(temp) { final = (char*) nsMemory::Clone(temp, sizeof(char)*(strlen(temp)+1)); JS_smprintf_free(temp); } *_retval = final; return final ? NS_OK : NS_ERROR_OUT_OF_MEMORY; } JSBool nsXPCException::sEverMadeOneFromFactory = JS_FALSE; // static nsresult nsXPCException::NewException(const char *aMessage, nsresult aResult, nsIStackFrame *aLocation, nsISupports *aData, nsIException** exceptn) { // A little hack... The nsIGenericModule nsIClassInfo scheme relies on there // having been at least one instance made via the factory. Otherwise, the // shared factory/classinsance object never gets created and our QI getter // for our instance's pointer to our nsIClassInfo will always return null. // This is bad because it means that wrapped exceptions will never have a // shared prototype. So... We force one to be created via the factory // *once* and then go about our business. if(!sEverMadeOneFromFactory) { nsCOMPtr<nsIXPCException> e = do_CreateInstance(XPC_EXCEPTION_CONTRACTID); sEverMadeOneFromFactory = JS_TRUE; } nsresult rv; nsXPCException* e = new nsXPCException(); if(e) { NS_ADDREF(e); nsIStackFrame* location; if(aLocation) { location = aLocation; NS_ADDREF(location); } else { nsXPConnect* xpc = nsXPConnect::GetXPConnect(); if(!xpc) { NS_RELEASE(e); return NS_ERROR_FAILURE; } rv = xpc->GetCurrentJSStack(&location); if(NS_FAILED(rv)) { NS_RELEASE(e); return NS_ERROR_FAILURE; } // it is legal for there to be no active JS stack, if C++ code // is operating on a JS-implemented interface pointer without // having been called in turn by JS. This happens in the JS // component loader, and will become more common as additional // components are implemented in JS. } // We want to trim off any leading native 'dataless' frames if(location) while(1) { PRUint32 language; PRInt32 lineNumber; if(NS_FAILED(location->GetLanguage(&language)) || language == nsIProgrammingLanguage::JAVASCRIPT || NS_FAILED(location->GetLineNumber(&lineNumber)) || lineNumber) { break; } nsCOMPtr<nsIStackFrame> caller; if(NS_FAILED(location->GetCaller(getter_AddRefs(caller))) || !caller) break; NS_RELEASE(location); caller->QueryInterface(NS_GET_IID(nsIStackFrame), (void **)&location); } // at this point we have non-null location with one extra addref, // or no location at all rv = e->Initialize(aMessage, aResult, nsnull, location, aData, nsnull); NS_IF_RELEASE(location); if(NS_FAILED(rv)) NS_RELEASE(e); } if(!e) return NS_ERROR_FAILURE; *exceptn = static_cast<nsIXPCException*>(e); return NS_OK; }