Back to index

lightning-sunbird  0.9+nobinonly
xpccallcontext.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is Mozilla Communicator client code, released
00017  * March 31, 1998.
00018  *
00019  * The Initial Developer of the Original Code is
00020  * Netscape Communications Corporation.
00021  * Portions created by the Initial Developer are Copyright (C) 1998
00022  * the Initial Developer. All Rights Reserved.
00023  *
00024  * Contributor(s):
00025  *   John Bandhauer <jband@netscape.com> (original author)
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 /* Call context. */
00042 
00043 #include "xpcprivate.h"
00044 
00045 XPCCallContext::XPCCallContext(XPCContext::LangType callerLanguage,
00046                                JSContext* cx    /* = nsnull  */,
00047                                JSObject* obj    /* = nsnull  */,
00048                                JSObject* funobj /* = nsnull  */,
00049                                jsval name       /* = 0       */,
00050                                uintN argc       /* = NO_ARGS */,
00051                                jsval *argv      /* = nsnull  */,
00052                                jsval *rval      /* = nsnull  */)
00053     :   mState(INIT_FAILED),
00054         mXPC(nsXPConnect::GetXPConnect()),
00055         mThreadData(nsnull),
00056         mXPCContext(nsnull),
00057         mJSContext(cx),
00058         mContextPopRequired(JS_FALSE),
00059         mDestroyJSContextInDestructor(JS_FALSE),
00060         mCallerLanguage(callerLanguage),
00061         mCallee(nsnull)
00062 {
00063     if(!mXPC)
00064         return;
00065 
00066     NS_ADDREF(mXPC);
00067 
00068     if(!(mThreadData = XPCPerThreadData::GetData()))
00069         return;
00070 
00071     XPCJSContextStack* stack = mThreadData->GetJSContextStack();
00072     JSContext* topJSContext;
00073 
00074     if(!stack || NS_FAILED(stack->Peek(&topJSContext)))
00075     {
00076         NS_ERROR("bad!");
00077         mJSContext = nsnull;
00078         return;
00079     }
00080 
00081     if(!mJSContext)
00082     {
00083         // This is slightly questionable. If called without an explicit
00084         // JSContext (generally a call to a wrappedJS) we will use the JSContext
00085         // on the top of the JSContext stack - if there is one - *before*
00086         // falling back on the safe JSContext.
00087         // This is good AND bad because it makes calls from JS -> native -> JS
00088         // have JS stack 'continuity' for purposes of stack traces etc.
00089         // Note: this *is* what the pre-XPCCallContext xpconnect did too.
00090 
00091         if(topJSContext)
00092             mJSContext = topJSContext;
00093         else if(NS_FAILED(stack->GetSafeJSContext(&mJSContext)) || !mJSContext)
00094             return;
00095     }
00096 
00097     // Get into the request as early as we can to avoid problems with scanning
00098     // callcontexts on other threads from within the gc callbacks.
00099 
00100     if(mCallerLanguage == NATIVE_CALLER && JS_GetContextThread(mJSContext))
00101         JS_BeginRequest(mJSContext);
00102 
00103     if(topJSContext != mJSContext)
00104     {
00105         if(NS_FAILED(stack->Push(mJSContext)))
00106         {
00107             NS_ERROR("bad!");
00108             return;
00109         }
00110         mContextPopRequired = JS_TRUE;
00111     }
00112 
00113     // Try to get the JSContext -> XPCContext mapping from the cache.
00114     // FWIW... quicky tests show this hitting ~ 95% of the time.
00115     // That is a *lot* of locking we can skip in nsXPConnect::GetContext.
00116     mXPCContext = mThreadData->GetRecentXPCContext(mJSContext);
00117 
00118     if(!mXPCContext)
00119     {
00120         if(!(mXPCContext = nsXPConnect::GetContext(mJSContext, mXPC)))
00121             return;
00122 
00123         // Fill the cache.
00124         mThreadData->SetRecentContext(mJSContext, mXPCContext);
00125     }
00126 
00127     mPrevCallerLanguage = mXPCContext->SetCallingLangType(mCallerLanguage);
00128 
00129     // hook into call context chain for our thread
00130     mPrevCallContext = mThreadData->SetCallContext(this);
00131 
00132     mState = HAVE_CONTEXT;
00133 
00134     if(!obj)
00135         return;
00136 
00137     mMethodIndex = 0xDEAD;
00138     mOperandJSObject = obj;
00139 
00140     mState = HAVE_OBJECT;
00141 
00142     mTearOff = nsnull;
00143     mWrapper = XPCWrappedNative::GetWrappedNativeOfJSObject(mJSContext, obj,
00144                                                             funobj,
00145                                                             &mCurrentJSObject,
00146                                                             &mTearOff);
00147     if(!mWrapper)
00148         return;
00149 
00150     DEBUG_CheckWrapperThreadSafety(mWrapper);
00151 
00152     mFlattenedJSObject = mWrapper->GetFlatJSObject();
00153 
00154     if(mTearOff)
00155         mScriptableInfo = nsnull;
00156     else
00157         mScriptableInfo = mWrapper->GetScriptableInfo();
00158 
00159     if(name)
00160         SetName(name);
00161 
00162     if(argc != NO_ARGS)
00163         SetArgsAndResultPtr(argc, argv, rval);
00164 
00165     CHECK_STATE(HAVE_OBJECT);
00166 }
00167 
00168 void
00169 XPCCallContext::SetName(jsval name)
00170 {
00171     CHECK_STATE(HAVE_OBJECT);
00172 
00173     mName = name;
00174 
00175 #ifdef XPC_IDISPATCH_SUPPORT
00176     mIDispatchMember = nsnull;
00177 #endif
00178     if(mTearOff)
00179     {
00180         mSet = nsnull;
00181         mInterface = mTearOff->GetInterface();
00182         mMember = mInterface->FindMember(name);
00183         mStaticMemberIsLocal = JS_TRUE;
00184         if(mMember && !mMember->IsConstant())
00185             mMethodIndex = mMember->GetIndex();
00186     }
00187     else
00188     {
00189         mSet = mWrapper ? mWrapper->GetSet() : nsnull;
00190 
00191         if(mSet &&
00192            mSet->FindMember(name, &mMember, &mInterface,
00193                             mWrapper->HasProto() ?
00194                                 mWrapper->GetProto()->GetSet() :
00195                                 nsnull,
00196                             &mStaticMemberIsLocal))
00197         {
00198             if(mMember && !mMember->IsConstant())
00199                 mMethodIndex = mMember->GetIndex();
00200         }
00201         else
00202         {
00203             mMember = nsnull;
00204             mInterface = nsnull;
00205             mStaticMemberIsLocal = JS_FALSE;
00206         }
00207     }
00208 
00209     mState = HAVE_NAME;
00210 }
00211 
00212 void
00213 XPCCallContext::SetCallInfo(XPCNativeInterface* iface, XPCNativeMember* member,
00214                             JSBool isSetter)
00215 {
00216     // We are going straight to the method info and need not do a lookup
00217     // by id.
00218 
00219     // don't be tricked if method is called with wrong 'this'
00220     if(mTearOff && mTearOff->GetInterface() != iface)
00221         mTearOff = nsnull;
00222 
00223     mSet = nsnull;
00224     mInterface = iface;
00225     mMember = member;
00226     mMethodIndex = mMember->GetIndex() + (isSetter ? 1 : 0);
00227     mName = mMember->GetName();
00228 
00229     if(mState < HAVE_NAME)
00230         mState = HAVE_NAME;
00231 #ifdef XPC_IDISPATCH_SUPPORT
00232     mIDispatchMember = nsnull;
00233 #endif
00234 }
00235 
00236 void
00237 XPCCallContext::SetArgsAndResultPtr(uintN argc,
00238                                     jsval *argv,
00239                                     jsval *rval)
00240 {
00241     CHECK_STATE(HAVE_OBJECT);
00242 
00243     mArgc   = argc;
00244     mArgv   = argv;
00245     mRetVal = rval;
00246 
00247     mExceptionWasThrown = mReturnValueWasSet = JS_FALSE;
00248     mState = HAVE_ARGS;
00249 }
00250 
00251 nsresult
00252 XPCCallContext::CanCallNow()
00253 {
00254     nsresult rv;
00255     
00256     if(!HasInterfaceAndMember())
00257         return NS_ERROR_UNEXPECTED;
00258     if(mState < HAVE_ARGS)
00259         return NS_ERROR_UNEXPECTED;
00260 
00261     if(!mTearOff)
00262     {
00263         mTearOff = mWrapper->FindTearOff(*this, mInterface, JS_FALSE, &rv);
00264         if(!mTearOff || mTearOff->GetInterface() != mInterface)
00265         {
00266             mTearOff = nsnull;    
00267             return NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED;
00268         }
00269     }
00270 
00271     // Refresh in case FindTearOff extended the set
00272     mSet = mWrapper->GetSet();
00273 
00274     mState = READY_TO_CALL;
00275     return NS_OK;
00276 }
00277 
00278 void
00279 XPCCallContext::SystemIsBeingShutDown()
00280 {
00281     // XXX This is pretty questionable since the per thread cleanup stuff
00282     // can be making this call on one thread for call contexts on another
00283     // thread.
00284     NS_WARNING("Shutting Down XPConnect even through there is a live XPCCallContext");
00285     mThreadData = nsnull;
00286     mXPCContext = nsnull;
00287     mState = SYSTEM_SHUTDOWN;
00288     if(mPrevCallContext)
00289         mPrevCallContext->SystemIsBeingShutDown();
00290 }
00291 
00292 XPCCallContext::~XPCCallContext()
00293 {
00294     NS_ASSERTION(mRefCnt == 0, "Someone is holding a bad reference to a XPCCallContext");
00295 
00296     // do cleanup...
00297 
00298     if(mXPCContext)
00299     {
00300         mXPCContext->SetCallingLangType(mPrevCallerLanguage);
00301 
00302 #ifdef DEBUG
00303         XPCCallContext* old = mThreadData->SetCallContext(mPrevCallContext);
00304         NS_ASSERTION(old == this, "bad pop from per thread data");
00305 #else
00306         (void) mThreadData->SetCallContext(mPrevCallContext);
00307 #endif
00308     }
00309 
00310     if(mContextPopRequired)
00311     {
00312         XPCJSContextStack* stack = mThreadData->GetJSContextStack();
00313         NS_ASSERTION(stack, "bad!");
00314         if(stack)
00315         {
00316 #ifdef DEBUG
00317             JSContext* poppedCX;
00318             nsresult rv = stack->Pop(&poppedCX);
00319             NS_ASSERTION(NS_SUCCEEDED(rv) && poppedCX == mJSContext, "bad pop");
00320 #else
00321             (void) stack->Pop(nsnull);
00322 #endif
00323         }
00324     }
00325 
00326     if(mJSContext)
00327     {
00328         if(mCallerLanguage == NATIVE_CALLER && JS_GetContextThread(mJSContext))
00329             JS_EndRequest(mJSContext);
00330         
00331         if(mDestroyJSContextInDestructor)
00332         {
00333 #ifdef DEBUG_xpc_hacker
00334             printf("!xpc - doing deferred destruction of JSContext @ %0x\n", 
00335                    mJSContext);
00336 #endif
00337             NS_ASSERTION(!mThreadData->GetJSContextStack() || 
00338                          !mThreadData->GetJSContextStack()->
00339                             DEBUG_StackHasJSContext(mJSContext),
00340                          "JSContext still in threadjscontextstack!");
00341         
00342             JS_DestroyContext(mJSContext);
00343             mXPC->SyncJSContexts();
00344         }
00345         else
00346         {
00347             // Don't clear newborns if JS frames (compilation or execution)
00348             // are active!  Doing so violates ancient invariants in the JS
00349             // engine, and it's not necessary to fix JS component leaks.
00350             if (!mJSContext->fp)
00351                 JS_ClearNewbornRoots(mJSContext);
00352         }
00353     }
00354 
00355     NS_IF_RELEASE(mXPC);
00356 }
00357 
00358 NS_IMPL_QUERY_INTERFACE1(XPCCallContext, nsIXPCNativeCallContext)
00359 NS_IMPL_ADDREF(XPCCallContext)
00360 
00361 NS_IMETHODIMP_(nsrefcnt)
00362 XPCCallContext::Release(void)
00363 {
00364   NS_PRECONDITION(0 != mRefCnt, "dup release");
00365   NS_ASSERT_OWNINGTHREAD(XPCCallContext);
00366   --mRefCnt;
00367   NS_LOG_RELEASE(this, mRefCnt, "XPCCallContext");
00368   // no delete this!
00369   return mRefCnt;
00370 }
00371 
00372 /* readonly attribute nsISupports Callee; */
00373 NS_IMETHODIMP
00374 XPCCallContext::GetCallee(nsISupports * *aCallee)
00375 {
00376     nsISupports* temp = mWrapper ? mWrapper->GetIdentityObject() : nsnull;
00377     NS_IF_ADDREF(temp);
00378     *aCallee = temp;
00379     return NS_OK;
00380 }
00381 
00382 /* readonly attribute PRUint16 CalleeMethodIndex; */
00383 NS_IMETHODIMP
00384 XPCCallContext::GetCalleeMethodIndex(PRUint16 *aCalleeMethodIndex)
00385 {
00386     *aCalleeMethodIndex = mMethodIndex;
00387     return NS_OK;
00388 }
00389 
00390 /* readonly attribute nsIXPConnectWrappedNative CalleeWrapper; */
00391 NS_IMETHODIMP
00392 XPCCallContext::GetCalleeWrapper(nsIXPConnectWrappedNative * *aCalleeWrapper)
00393 {
00394     nsIXPConnectWrappedNative* temp = mWrapper;
00395     NS_IF_ADDREF(temp);
00396     *aCalleeWrapper = temp;
00397     return NS_OK;
00398 }
00399 
00400 /* readonly attribute XPCNativeInterface CalleeInterface; */
00401 NS_IMETHODIMP
00402 XPCCallContext::GetCalleeInterface(nsIInterfaceInfo * *aCalleeInterface)
00403 {
00404     nsIInterfaceInfo* temp = mInterface->GetInterfaceInfo();
00405     NS_IF_ADDREF(temp);
00406     *aCalleeInterface = temp;
00407     return NS_OK;
00408 }
00409 
00410 /* readonly attribute nsIClassInfo CalleeClassInfo; */
00411 NS_IMETHODIMP
00412 XPCCallContext::GetCalleeClassInfo(nsIClassInfo * *aCalleeClassInfo)
00413 {
00414     nsIClassInfo* temp = mWrapper ? mWrapper->GetClassInfo() : nsnull;
00415     NS_IF_ADDREF(temp);
00416     *aCalleeClassInfo = temp;
00417     return NS_OK;
00418 }
00419 
00420 /* readonly attribute JSContextPtr JSContext; */
00421 NS_IMETHODIMP
00422 XPCCallContext::GetJSContext(JSContext * *aJSContext)
00423 {
00424     *aJSContext = mJSContext;
00425     return NS_OK;
00426 }
00427 
00428 /* readonly attribute PRUint32 Argc; */
00429 NS_IMETHODIMP
00430 XPCCallContext::GetArgc(PRUint32 *aArgc)
00431 {
00432     *aArgc = (PRUint32) mArgc;
00433     return NS_OK;
00434 }
00435 
00436 /* readonly attribute JSValPtr ArgvPtr; */
00437 NS_IMETHODIMP
00438 XPCCallContext::GetArgvPtr(jsval * *aArgvPtr)
00439 {
00440     *aArgvPtr = mArgv;
00441     return NS_OK;
00442 }
00443 
00444 /* readonly attribute JSValPtr RetValPtr; */
00445 NS_IMETHODIMP
00446 XPCCallContext::GetRetValPtr(jsval * *aRetValPtr)
00447 {
00448     *aRetValPtr = mRetVal;
00449     return NS_OK;
00450 }
00451 
00452 /* attribute PRBool ExceptionWasThrown; */
00453 NS_IMETHODIMP
00454 XPCCallContext::GetExceptionWasThrown(PRBool *aExceptionWasThrown)
00455 {
00456     *aExceptionWasThrown = mExceptionWasThrown;
00457     return NS_OK;
00458 }
00459 NS_IMETHODIMP
00460 XPCCallContext::SetExceptionWasThrown(PRBool aExceptionWasThrown)
00461 {
00462     mExceptionWasThrown = aExceptionWasThrown;
00463     return NS_OK;
00464 }
00465 
00466 /* attribute PRBool ReturnValueWasSet; */
00467 NS_IMETHODIMP
00468 XPCCallContext::GetReturnValueWasSet(PRBool *aReturnValueWasSet)
00469 {
00470     *aReturnValueWasSet = mReturnValueWasSet;
00471     return NS_OK;
00472 }
00473 NS_IMETHODIMP
00474 XPCCallContext::SetReturnValueWasSet(PRBool aReturnValueWasSet)
00475 {
00476     mReturnValueWasSet = aReturnValueWasSet;
00477     return NS_OK;
00478 }
00479 
00480 #ifdef XPC_IDISPATCH_SUPPORT
00481 
00482 void
00483 XPCCallContext::SetIDispatchInfo(XPCNativeInterface* iface, 
00484                                  void * member)
00485 {
00486     // We are going straight to the method info and need not do a lookup
00487     // by id.
00488 
00489     // don't be tricked if method is called with wrong 'this'
00490     if(mTearOff && mTearOff->GetInterface() != iface)
00491         mTearOff = nsnull;
00492 
00493     mSet = nsnull;
00494     mInterface = iface;
00495     mMember = nsnull;
00496     mIDispatchMember = member;
00497     mName = NS_REINTERPRET_CAST(XPCDispInterface::Member*,member)->GetName();
00498 
00499     if(mState < HAVE_NAME)
00500         mState = HAVE_NAME;
00501 }
00502 
00503 #endif