Back to index

lightning-sunbird  0.9+nobinonly
XPCDispInterface.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 
00043 #include "xpcprivate.h"
00044 
00050 inline
00051 PRBool IsReflectable(FUNCDESC * pFuncDesc)
00052 {
00053     return (pFuncDesc->wFuncFlags&FUNCFLAG_FRESTRICTED) == 0 &&
00054            pFuncDesc->funckind == FUNC_DISPATCH || 
00055            pFuncDesc->funckind == FUNC_PUREVIRTUAL ||
00056            pFuncDesc->funckind == FUNC_VIRTUAL;
00057 }
00058 
00059 XPCDispInterface::Allocator::Allocator(JSContext * cx, ITypeInfo * pTypeInfo) :
00060     mMemIDs(nsnull), mCount(0), mIDispatchMembers(0), mCX(cx), 
00061     mTypeInfo(pTypeInfo)
00062 {
00063     TYPEATTR * attr;
00064     HRESULT hr = pTypeInfo->GetTypeAttr(&attr);
00065     if(SUCCEEDED(hr))
00066     {
00067         mIDispatchMembers = attr->cFuncs;
00068         mMemIDs = new DISPID[mIDispatchMembers];
00069         pTypeInfo->ReleaseTypeAttr(attr);
00070         // Bail if we couldn't create the buffer
00071         if(!mMemIDs)
00072             return;
00073     }
00074     for(UINT iMethod = 0; iMethod < mIDispatchMembers; iMethod++ )
00075     {
00076         FUNCDESC* pFuncDesc;
00077         if(SUCCEEDED(pTypeInfo->GetFuncDesc(iMethod, &pFuncDesc)))
00078         {
00079             // Only add the function to our list if it is at least at nesting level
00080             // 2 (i.e. defined in an interface derived from IDispatch).
00081             if(IsReflectable(pFuncDesc))
00082                 Add(pFuncDesc->memid);
00083             pTypeInfo->ReleaseFuncDesc(pFuncDesc);
00084         }
00085     }
00086 }
00087 
00088 void XPCDispInterface::Allocator::Add(DISPID memID)
00089 {
00090     NS_ASSERTION(Valid(), "Add should never be called if out of memory");
00091     // Start from the end and work backwards, the last item is the most
00092     // likely to match
00093     PRUint32 index = mCount;
00094     while(index > 0)
00095     {
00096         if(mMemIDs[--index] == memID)
00097             return;
00098     };
00099     NS_ASSERTION(Count() < mIDispatchMembers, "mCount should always be less "
00100                                              "than the IDispatch member count "
00101                                              "here");
00102     mMemIDs[mCount++] = memID;
00103     return;
00104 }
00105 
00106 inline
00107 PRUint32 XPCDispInterface::Allocator::Count() const 
00108 {
00109     return mCount;
00110 }
00111 
00112 XPCDispInterface*
00113 XPCDispInterface::NewInstance(JSContext* cx, nsISupports * pIface)
00114 {
00115     CComQIPtr<IDispatch> pDispatch(NS_REINTERPRET_CAST(IUnknown*,pIface));
00116 
00117     if(pDispatch)
00118     {
00119         unsigned int count;
00120         HRESULT hr = pDispatch->GetTypeInfoCount(&count);
00121         if(SUCCEEDED(hr) && count > 0)
00122         {
00123             CComPtr<ITypeInfo> pTypeInfo;
00124             hr = pDispatch->GetTypeInfo(0,LOCALE_SYSTEM_DEFAULT, &pTypeInfo);
00125             if(SUCCEEDED(hr))
00126             {
00127                 Allocator allocator(cx, pTypeInfo);
00128                 return allocator.Allocate();
00129             }
00130         }
00131     }
00132     return nsnull;
00133 }
00134 
00138 static
00139 void ConvertInvokeKind(INVOKEKIND invokeKind, XPCDispInterface::Member & member)
00140 {
00141     switch (invokeKind)
00142     {
00143         case INVOKE_FUNC:
00144         {
00145             member.SetFunction();
00146         }
00147         break;
00148         case INVOKE_PROPERTYGET:
00149         {
00150             member.MakeGetter();
00151         }
00152         break;
00153         case INVOKE_PROPERTYPUT:
00154         {
00155             member.MakeSetter();
00156         }
00157         break;
00158         // TODO: Handle putref
00159         default:
00160         {
00161             NS_ERROR("Invalid invoke kind found in COM type info");
00162         }
00163         break;
00164     }
00165 }
00166 
00167 static
00168 PRBool InitializeMember(JSContext * cx, ITypeInfo * pTypeInfo,
00169                         FUNCDESC * pFuncDesc, 
00170                         XPCDispInterface::Member * pInfo)
00171 {
00172     pInfo->SetMemID(pFuncDesc->memid);
00173     BSTR name;
00174     UINT nameCount;
00175     if(FAILED(pTypeInfo->GetNames(
00176         pFuncDesc->memid,
00177         &name,
00178         1,
00179         &nameCount)))
00180         return PR_FALSE;
00181     if(nameCount != 1)
00182         return PR_FALSE;
00183     JSString* str = JS_InternUCStringN(cx,
00184                                        NS_REINTERPRET_CAST(const jschar *, name),
00185                                        ::SysStringLen(name));
00186     ::SysFreeString(name);
00187     if(!str)
00188         return PR_FALSE;
00189     // Initialize
00190     pInfo = new (pInfo) XPCDispInterface::Member;
00191     if(!pInfo)
00192         return PR_FALSE;
00193     pInfo->SetName(STRING_TO_JSVAL(str));
00194     pInfo->ResetType();
00195     ConvertInvokeKind(pFuncDesc->invkind, *pInfo);
00196     pInfo->SetTypeInfo(pFuncDesc->memid, pTypeInfo, pFuncDesc);
00197     return PR_TRUE;
00198 }
00199 
00200 static
00201 XPCDispInterface::Member * FindExistingMember(XPCDispInterface::Member * first,
00202                                               XPCDispInterface::Member * last,
00203                                               MEMBERID memberID)
00204 {
00205     // Iterate backward since the last one in is the most likely match
00206     XPCDispInterface::Member * cur = last;
00207     if (cur != first)
00208     {
00209         do 
00210         {
00211             --cur;
00212             if(cur->GetMemID() == memberID)
00213                 return cur;
00214         } while(cur != first);
00215     } 
00216     // no existing property, return the new one
00217     return last;
00218 }
00219 
00220 PRBool XPCDispInterface::InspectIDispatch(JSContext * cx, ITypeInfo * pTypeInfo, PRUint32 members)
00221 {
00222     HRESULT hResult;
00223 
00224     XPCDispInterface::Member * pInfo = mMembers;
00225     mMemberCount = 0;
00226     for(PRUint32 index = 0; index < members; index++ )
00227     {
00228         FUNCDESC* pFuncDesc;
00229         hResult = pTypeInfo->GetFuncDesc(index, &pFuncDesc );
00230         if(FAILED(hResult))
00231             continue;
00232         if(IsReflectable(pFuncDesc))
00233         {
00234             switch(pFuncDesc->invkind)
00235             {
00236                 case INVOKE_PROPERTYPUT:
00237                 case INVOKE_PROPERTYPUTREF:
00238                 case INVOKE_PROPERTYGET:
00239                 {
00240                     XPCDispInterface::Member * pExisting = FindExistingMember(mMembers, pInfo, pFuncDesc->memid);
00241                     if(pExisting == pInfo)
00242                     {
00243                         if(InitializeMember(cx, pTypeInfo, pFuncDesc, pInfo))
00244                         {
00245                             ++pInfo;
00246                             ++mMemberCount;
00247                         }
00248                     }
00249                     else
00250                     {
00251                         ConvertInvokeKind(pFuncDesc->invkind, *pExisting);
00252                     }
00253                     if(pFuncDesc->invkind == INVOKE_PROPERTYGET)
00254                     {
00255                         pExisting->SetGetterFuncDesc(pFuncDesc);
00256                     }
00257                 }
00258                 break;
00259                 case INVOKE_FUNC:
00260                 {
00261                     if(InitializeMember(cx, pTypeInfo, pFuncDesc, pInfo))
00262                     {
00263                         ++pInfo;
00264                         ++mMemberCount;
00265                     }
00266                 }
00267                 break;
00268                 default:
00269                     pTypeInfo->ReleaseFuncDesc(pFuncDesc);
00270                 break;
00271             }
00272         }
00273         else
00274         {
00275             pTypeInfo->ReleaseFuncDesc(pFuncDesc);
00276         }
00277     }
00278     return PR_TRUE;
00279 }
00280 
00289 inline
00290 PRBool CaseInsensitiveCompare(XPCCallContext& ccx, const PRUnichar* lhs, size_t lhsLength, jsval rhs)
00291 {
00292     if(lhsLength == 0)
00293         return PR_FALSE;
00294     size_t rhsLength;
00295     PRUnichar* rhsString = xpc_JSString2PRUnichar(ccx, rhs, &rhsLength);
00296     return rhsString && 
00297         lhsLength == rhsLength &&
00298         _wcsnicmp(lhs, rhsString, lhsLength) == 0;
00299 }
00300 
00301 const XPCDispInterface::Member* XPCDispInterface::FindMemberCI(XPCCallContext& ccx, jsval name) const
00302 {
00303     size_t nameLength;
00304     PRUnichar* sName = xpc_JSString2PRUnichar(ccx, name, &nameLength);
00305     if(!sName)
00306         return nsnull;
00307     // Iterate backwards over the members array (more efficient)
00308     const Member* member = mMembers + mMemberCount;
00309     while(member > mMembers)
00310     {
00311         --member;
00312         if(CaseInsensitiveCompare(ccx, sName, nameLength, member->GetName()))
00313         {
00314             return member;
00315         }
00316     }
00317     return nsnull;
00318 }
00319 
00320 JSBool XPCDispInterface::Member::GetValue(XPCCallContext& ccx,
00321                                           XPCNativeInterface * iface, 
00322                                           jsval * retval) const
00323 {
00324     // This is a method or attribute - we'll be needing a function object
00325 
00326     // We need to use the safe context for this thread because we don't want
00327     // to parent the new (and cached forever!) function object to the current
00328     // JSContext's global object. That would be bad!
00329     if((mType & RESOLVED) == 0)
00330     {
00331         JSContext* cx = ccx.GetSafeJSContext();
00332         if(!cx)
00333             return JS_FALSE;
00334 
00335         intN argc;
00336         intN flags;
00337         JSNative callback;
00338         // Is this a function or a parameterized getter/setter
00339         if(IsFunction() || IsParameterizedProperty())
00340         {
00341             argc = GetParamCount();
00342             flags = 0;
00343             callback = XPC_IDispatch_CallMethod;
00344         }
00345         else
00346         {
00347             if(IsSetter())
00348             {
00349                 flags = JSFUN_GETTER | JSFUN_SETTER;
00350             }
00351             else
00352             {
00353                 flags = JSFUN_GETTER;
00354             }
00355             argc = 0;
00356             callback = XPC_IDispatch_GetterSetter;
00357         }
00358 
00359         JSFunction *fun = JS_NewFunction(cx, callback, argc, flags, nsnull,
00360                                          JS_GetStringBytes(JSVAL_TO_STRING(mName)));
00361         if(!fun)
00362             return JS_FALSE;
00363 
00364         JSObject* funobj = JS_GetFunctionObject(fun);
00365         if(!funobj)
00366             return JS_FALSE;
00367 
00368         // Store ourselves and our native interface within the JSObject
00369         if(!JS_SetReservedSlot(ccx, funobj, 0, PRIVATE_TO_JSVAL(this)))
00370             return JS_FALSE;
00371 
00372         if(!JS_SetReservedSlot(ccx, funobj, 1, PRIVATE_TO_JSVAL(iface)))
00373             return JS_FALSE;
00374 
00375         {   // scoped lock
00376             XPCAutoLock lock(ccx.GetRuntime()->GetMapLock());
00377             NS_CONST_CAST(Member*,this)->mVal = OBJECT_TO_JSVAL(funobj);
00378             NS_CONST_CAST(Member*,this)->mType |= RESOLVED;
00379         }
00380     }
00381     *retval = mVal;
00382     return JS_TRUE;
00383 }