Back to index

lightning-sunbird  0.9+nobinonly
XPCDispTearOff.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  * ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the IDispatch implementation for XPConnect.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * David Bradley.
00019  * Portions created by the Initial Developer are Copyright (C) 2002
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00042 #include "xpcprivate.h"
00043 
00051 static HRESULT Error(HRESULT hResult, const CComBSTR & message)
00052 {
00053     CComPtr<ICreateErrorInfo> pCreateError;
00054     CComPtr<IErrorInfo> pError;
00055     HRESULT result = CreateErrorInfo(&pCreateError);
00056     if(FAILED(result))
00057         return E_NOTIMPL;
00058     result = pCreateError->QueryInterface(&pError);
00059     if(FAILED(result))
00060         return E_NOTIMPL;
00061     result = pCreateError->SetDescription(message);
00062     if(FAILED(result))
00063         return E_NOTIMPL;
00064     result = pCreateError->SetGUID(IID_IDispatch);
00065     if(FAILED(result))
00066         return E_NOTIMPL;
00067     CComBSTR source(L"@mozilla.XPCDispatchTearOff");
00068     result = pCreateError->SetSource(source);
00069     if(FAILED(result))
00070         return E_NOTIMPL;
00071     result = SetErrorInfo(0, pError);
00072     if(FAILED(result))
00073         return E_NOTIMPL;
00074     return hResult;
00075 }
00076 
00077 
00084 inline
00085 HRESULT Error(HRESULT hResult, const char * message)
00086 {
00087     CComBSTR someText(message);
00088     return Error(hResult, someText);
00089 }
00090 
00096 static void BuildMessage(nsIException * exception, nsCString & result)
00097 {
00098     nsXPIDLCString msg;
00099     exception->GetMessage(getter_Copies(msg));
00100     nsXPIDLCString filename;
00101     exception->GetFilename(getter_Copies(filename));
00102 
00103     PRUint32 lineNumber;
00104     if(NS_FAILED(exception->GetLineNumber(&lineNumber)))
00105         lineNumber = 0;
00106     result = "Error in file ";
00107     result += filename;
00108     result += ",#";
00109     result.AppendInt(lineNumber);
00110     result += " : ";
00111     result += msg;
00112 }
00113 
00118 inline
00119 static void SetCOMError(nsIException * exception)
00120 {
00121     nsCString message;
00122     BuildMessage(exception, message);
00123     Error(E_FAIL, message.get());
00124 }
00125 
00126 XPCDispatchTearOff::XPCDispatchTearOff(nsIXPConnectWrappedJS * wrappedJS) :
00127     mWrappedJS(wrappedJS),
00128     mCOMTypeInfo(nsnull),
00129     mRefCnt(0)
00130 {
00131 }
00132 
00133 XPCDispatchTearOff::~XPCDispatchTearOff()
00134 {
00135     NS_IF_RELEASE(mCOMTypeInfo);
00136 }
00137 
00138 NS_COM_IMPL_ADDREF(XPCDispatchTearOff)
00139 NS_COM_IMPL_RELEASE(XPCDispatchTearOff)
00140 
00141 // See bug 127982:
00142 //
00143 // Microsoft's InlineIsEqualGUID global function is multiply defined
00144 // in ATL and/or SDKs with varying namespace requirements. To save the control
00145 // from future grief, this method is used instead. 
00146 static inline BOOL _IsEqualGUID(REFGUID rguid1, REFGUID rguid2)
00147 {
00148    return (
00149          ((PLONG) &rguid1)[0] == ((PLONG) &rguid2)[0] &&
00150          ((PLONG) &rguid1)[1] == ((PLONG) &rguid2)[1] &&
00151          ((PLONG) &rguid1)[2] == ((PLONG) &rguid2)[2] &&
00152          ((PLONG) &rguid1)[3] == ((PLONG) &rguid2)[3]);
00153 }
00154 
00155 STDMETHODIMP XPCDispatchTearOff::InterfaceSupportsErrorInfo(REFIID riid)
00156 {
00157     static const IID* arr[] = 
00158     {
00159         &IID_IDispatch,
00160     };
00161 
00162     for(int i=0;i<sizeof(arr)/sizeof(arr[0]);i++)
00163     {
00164         if(_IsEqualGUID(*arr[i],riid))
00165             return S_OK;
00166     }
00167     return S_FALSE;
00168 }
00169 
00170 STDMETHODIMP XPCDispatchTearOff::QueryInterface(const struct _GUID & guid,
00171                                               void ** pPtr)
00172 {
00173     if(IsEqualIID(guid, IID_IDispatch))
00174     {
00175         *pPtr = NS_STATIC_CAST(IDispatch*,this);
00176         NS_ADDREF_THIS();
00177         return NS_OK;
00178     }
00179 
00180     if(IsEqualIID(guid, IID_ISupportErrorInfo))
00181     {
00182         *pPtr = NS_STATIC_CAST(IDispatch*,this);
00183         NS_ADDREF_THIS();
00184         return NS_OK;
00185     }
00186 
00187     return mWrappedJS->QueryInterface(XPCDispIID2nsIID(guid), pPtr);
00188 }
00189 
00190 STDMETHODIMP XPCDispatchTearOff::GetTypeInfoCount(unsigned int FAR * pctinfo)
00191 {
00192     *pctinfo = 1;
00193     return S_OK;
00194 }
00195 
00196 XPCDispTypeInfo * XPCDispatchTearOff::GetCOMTypeInfo()
00197 {
00198     // If one was already created return it
00199     if(mCOMTypeInfo)
00200         return mCOMTypeInfo;
00201     // Build a new one, save the pointer and return it
00202     XPCCallContext ccx(NATIVE_CALLER);
00203     if(!ccx.IsValid())
00204         return nsnull;
00205     JSObject* obj = GetJSObject();
00206     if(!obj)
00207         return nsnull;
00208     mCOMTypeInfo = XPCDispTypeInfo::New(ccx, obj);
00209     NS_IF_ADDREF(mCOMTypeInfo);
00210     return mCOMTypeInfo;
00211 }
00212 
00213 STDMETHODIMP XPCDispatchTearOff::GetTypeInfo(unsigned int, LCID, 
00214                                          ITypeInfo FAR* FAR* ppTInfo)
00215 {
00216     *ppTInfo = GetCOMTypeInfo();
00217     NS_ADDREF(*ppTInfo);
00218     return S_OK;
00219 }
00220 
00221 STDMETHODIMP XPCDispatchTearOff::GetIDsOfNames(REFIID riid, 
00222                                            OLECHAR FAR* FAR* rgszNames, 
00223                                            unsigned int cNames, LCID  lcid,
00224                                            DISPID FAR* rgDispId)
00225 {
00226     ITypeInfo * pTypeInfo = GetCOMTypeInfo();
00227     if(pTypeInfo != nsnull)
00228     {
00229         return pTypeInfo->GetIDsOfNames(rgszNames, cNames, rgDispId);
00230     }
00231     return S_OK;
00232 }
00233 
00234 void JS_DLL_CALLBACK
00235 xpcWrappedJSErrorReporter(JSContext *cx, const char *message,
00236                           JSErrorReport *report);
00237 
00238 STDMETHODIMP XPCDispatchTearOff::Invoke(DISPID dispIdMember, REFIID riid, 
00239                                         LCID lcid, WORD wFlags,
00240                                         DISPPARAMS FAR* pDispParams, 
00241                                         VARIANT FAR* pVarResult, 
00242                                         EXCEPINFO FAR* pExcepInfo, 
00243                                         unsigned int FAR* puArgErr)
00244 {
00245     XPCDispTypeInfo* pTypeInfo = GetCOMTypeInfo();
00246     if(!pTypeInfo)
00247     {
00248         return E_FAIL;
00249     }
00250     XPCCallContext ccx(NATIVE_CALLER);
00251     XPCContext* xpcc;
00252     JSContext* cx;
00253     if(ccx.IsValid())
00254     {
00255         xpcc = ccx.GetXPCContext();
00256         cx = ccx.GetJSContext();
00257     }
00258     else
00259     {
00260         xpcc = nsnull;
00261         cx = nsnull;
00262     }
00263     // Get the name as a flat string
00264     // This isn't that efficient, but we have to make the conversion somewhere
00265     NS_LossyConvertUCS2toASCII name(pTypeInfo->GetNameForDispID(dispIdMember));
00266     if(name.IsEmpty())
00267         return E_FAIL;
00268     // Decide if this is a getter or setter
00269     PRBool getter = (wFlags & DISPATCH_PROPERTYGET) != 0;
00270     PRBool setter = (wFlags & DISPATCH_PROPERTYPUT) != 0;
00271     // It's a property
00272     if(getter || setter)
00273     {
00274         jsval val;
00275         uintN err;
00276         JSObject* obj;
00277         if(getter)
00278         {
00279             // Get the property and convert the value
00280             obj = GetJSObject();
00281             if(!obj)
00282                 return E_FAIL;
00283             if(!JS_GetProperty(cx, obj, name.get(), &val))
00284             {
00285                 nsCString msg("Unable to retrieve property ");
00286                 msg += name;
00287                 return Error(E_FAIL, msg.get());
00288             }
00289             if(!XPCDispConvert::JSToCOM(ccx, val, *pVarResult, err))
00290             {
00291                 nsCString msg("Failed to convert value from JS property ");
00292                 msg += name;
00293                 return Error(E_FAIL, msg.get());
00294             }
00295         }
00296         else if(pDispParams->cArgs > 0)
00297         {
00298             // Convert the property and then set it
00299             if(!XPCDispConvert::COMToJS(ccx, pDispParams->rgvarg[0], val, err))
00300             {
00301                 nsCString msg("Failed to convert value for JS property ");
00302                 msg += name;
00303                 return Error(E_FAIL, msg.get());
00304             }
00305             AUTO_MARK_JSVAL(ccx, &val);
00306             obj = GetJSObject();
00307             if(!obj)
00308                 return Error(E_FAIL, "The JS wrapper did not return a JS object");
00309             if(!JS_SetProperty(cx, obj, name.get(), &val))
00310             {
00311                 nsCString msg("Unable to set property ");
00312                 msg += name;
00313                 return Error(E_FAIL, msg.get());
00314             }
00315         }
00316     }
00317     else // We're invoking a function
00318     {
00319         jsval* stackbase;
00320         jsval* sp = nsnull;
00321         uint8 i;
00322         uint8 argc = pDispParams->cArgs;
00323         uint8 stack_size;
00324         jsval result;
00325         uint8 paramCount=0;
00326         nsresult retval = NS_ERROR_FAILURE;
00327         nsresult pending_result = NS_OK;
00328         JSBool success;
00329         JSBool readyToDoTheCall = JS_FALSE;
00330         uint8 outConversionFailedIndex;
00331         JSObject* obj;
00332         jsval fval;
00333         nsCOMPtr<nsIException> xpc_exception;
00334         void* mark;
00335         JSBool foundDependentParam;
00336         JSObject* thisObj;
00337         AutoScriptEvaluate scriptEval(ccx);
00338         XPCJSRuntime* rt = ccx.GetRuntime();
00339 
00340         thisObj = obj = GetJSObject();;
00341 
00342         if(!cx || !xpcc)
00343             goto pre_call_clean_up;
00344 
00345         scriptEval.StartEvaluating(xpcWrappedJSErrorReporter);
00346 
00347         xpcc->SetPendingResult(pending_result);
00348         xpcc->SetException(nsnull);
00349         ccx.GetThreadData()->SetException(nsnull);
00350 
00351         // We use js_AllocStack, js_Invoke, and js_FreeStack so that the gcthings
00352         // we use as args will be rooted by the engine as we do conversions and
00353         // prepare to do the function call. This adds a fair amount of complexity,
00354         // but is a good optimization compared to calling JS_AddRoot for each item.
00355 
00356         // setup stack
00357 
00358         // allocate extra space for function and 'this'
00359         stack_size = argc + 2;
00360 
00361 
00362         // In the xpidl [function] case we are making sure now that the 
00363         // JSObject is callable. If it is *not* callable then we silently 
00364         // fallback to looking up the named property...
00365         // (because jst says he thinks this fallback is 'The Right Thing'.)
00366         //
00367         // In the normal (non-function) case we just lookup the property by 
00368         // name and as long as the object has such a named property we go ahead
00369         // and try to make the call. If it turns out the named property is not
00370         // a callable object then the JS engine will throw an error and we'll
00371         // pass this along to the caller as an exception/result code.
00372         fval = OBJECT_TO_JSVAL(obj);
00373         if(JS_TypeOfValue(ccx, fval) != JSTYPE_FUNCTION && 
00374             !JS_GetProperty(cx, obj, name.get(), &fval))
00375         {
00376             // XXX We really want to factor out the error reporting better and
00377             // specifically report the failure to find a function with this name.
00378             // This is what we do below if the property is found but is not a
00379             // function. We just need to factor better so we can get to that
00380             // reporting path from here.
00381             goto pre_call_clean_up;
00382         }
00383 
00384         // if stack_size is zero then we won't be needing a stack
00385         if(stack_size && !(stackbase = sp = js_AllocStack(cx, stack_size, &mark)))
00386         {
00387             retval = NS_ERROR_OUT_OF_MEMORY;
00388             goto pre_call_clean_up;
00389         }
00390 
00391         // this is a function call, so push function and 'this'
00392         if(stack_size != argc)
00393         {
00394             *sp++ = fval;
00395             *sp++ = OBJECT_TO_JSVAL(thisObj);
00396         }
00397 
00398         // make certain we leave no garbage in the stack
00399         for(i = 0; i < argc; i++)
00400         {
00401             sp[i] = JSVAL_VOID;
00402         }
00403 
00404         uintN err;
00405         // build the args
00406         for(i = 0; i < argc; i++)
00407         {
00408             jsval val;
00409             if((pDispParams->rgvarg[i].vt & VT_BYREF) == 0)
00410             {
00411                 if(!XPCDispConvert::COMToJS(ccx, pDispParams->rgvarg[i], val, err))
00412                     goto pre_call_clean_up;
00413                 *sp++ = val;
00414             }
00415             else
00416             {
00417                 // create an 'out' object
00418                 JSObject* out_obj = JS_NewObject(cx, nsnull, nsnull, nsnull);
00419                 if(!out_obj)
00420                 {
00421                     retval = NS_ERROR_OUT_OF_MEMORY;
00422                     goto pre_call_clean_up;
00423                 }
00424                 // We'll assume in/out
00425                 // TODO: I'm not sure we tell out vs in/out
00426                 OBJ_SET_PROPERTY(cx, out_obj,
00427                         rt->GetStringID(XPCJSRuntime::IDX_VALUE),
00428                         &val);
00429                 *sp++ = OBJECT_TO_JSVAL(out_obj);
00430             }
00431         }
00432 
00433         readyToDoTheCall = JS_TRUE;
00434 
00435 pre_call_clean_up:
00436 
00437         if(!readyToDoTheCall)
00438             goto done;
00439 
00440         // do the deed - note exceptions
00441 
00442         JS_ClearPendingException(cx);
00443 
00444         if(!JSVAL_IS_PRIMITIVE(fval))
00445         {
00446             // Lift current frame (or make new one) to include the args
00447             // and do the call.
00448             JSStackFrame *fp, *oldfp, frame;
00449             jsval *oldsp;
00450 
00451             fp = oldfp = cx->fp;
00452             if(!fp)
00453             {
00454                 memset(&frame, 0, sizeof(frame));
00455                 cx->fp = fp = &frame;
00456             }
00457             oldsp = fp->sp;
00458             fp->sp = sp;
00459 
00460             success = js_Invoke(cx, argc, JSINVOKE_INTERNAL);
00461 
00462             result = fp->sp[-1];
00463             fp->sp = oldsp;
00464             if(oldfp != fp)
00465                 cx->fp = oldfp;
00466         }
00467         else
00468         {
00469             // The property was not an object so can't be a function.
00470             // Let's build and 'throw' an exception.
00471 
00472             static const nsresult code =
00473                     NS_ERROR_XPC_JSOBJECT_HAS_NO_FUNCTION_NAMED;
00474             static const char format[] = "%s \"%s\"";
00475             const char * msg;
00476             char* sz = nsnull;
00477 
00478             if(nsXPCException::NameAndFormatForNSResult(code, nsnull, &msg) && msg)
00479                 sz = JS_smprintf(format, msg, name);
00480 
00481             nsCOMPtr<nsIException> e;
00482 
00483             XPCConvert::ConstructException(code, sz, "IDispatch", name.get(),
00484                                            nsnull, getter_AddRefs(e));
00485             xpcc->SetException(e);
00486             if(sz)
00487                 JS_smprintf_free(sz);
00488         }
00489 
00490         if (!success)
00491         {
00492             retval = nsXPCWrappedJSClass::CheckForException(ccx, name.get(), "IDispatch");
00493             goto done;
00494         }
00495 
00496         ccx.GetThreadData()->SetException(nsnull); // XXX necessary?
00497 
00498         // convert out args and result
00499         // NOTE: this is the total number of native params, not just the args
00500         // Convert independent params only.
00501         // When we later convert the dependent params (if any) we will know that
00502         // the params upon which they depend will have already been converted -
00503         // regardless of ordering.
00504 
00505         outConversionFailedIndex = paramCount;
00506         foundDependentParam = JS_FALSE;
00507         if(JSVAL_IS_VOID(result) || XPCDispConvert::JSToCOM(ccx, result, *pVarResult, err))
00508         {
00509             for(i = 0; i < paramCount; i++)
00510             {
00511                 jsval val;
00512                 if(JSVAL_IS_PRIMITIVE(stackbase[i+2]) ||
00513                         !OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(stackbase[i+2]),
00514                             rt->GetStringID(XPCJSRuntime::IDX_VALUE),
00515                             &val))
00516                 {
00517                     outConversionFailedIndex = i;
00518                     break;
00519                 }
00520 
00521             }
00522         }
00523 
00524         if(outConversionFailedIndex != paramCount)
00525         {
00526             // We didn't manage all the result conversions!
00527             // We have to cleanup any junk that *did* get converted.
00528 
00529             for(PRUint32 index = 0; index < outConversionFailedIndex; index++)
00530             {
00531                 if((pDispParams->rgvarg[index].vt & VT_BYREF) != 0)
00532                 {
00533                     VariantClear(pDispParams->rgvarg + i);
00534                 }
00535             }
00536         }
00537         else
00538         {
00539             // set to whatever the JS code might have set as the result
00540             retval = pending_result;
00541         }
00542 
00543 done:
00544         if(sp)
00545             js_FreeStack(cx, mark);
00546 
00547         // TODO: I think we may need to translate this error, 
00548         // for now we'll pass through
00549         return retval;
00550     }
00551     return S_OK;
00552 }
00553 
00554 inline
00555 JSObject* XPCDispatchTearOff::GetJSObject()
00556 {
00557     JSObject* obj;
00558     if(NS_SUCCEEDED(mWrappedJS->GetJSObject(&obj)))
00559         return obj;
00560     return nsnull;
00561 }