|
Contents |
||||||||||||||||||||||||||||||||||||
Objective Terminology Loading a bridge Environment Mapping Microsoft Visual C++ - UNO bridge |
||||||||||||||||||||||||||||||||||||
Objective |
||||||||||||||||||||||||||||||||||||
This document is written for developers implementing a bridge from C++ to binary UNO. The last paragragraph covers the Microsoft Visual C++ - UNO bridge in detail. |
||||||||||||||||||||||||||||||||||||
Terminology |
||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
Loading a bridge |
||||||||||||||||||||||||||||||||||||
Each bridge is implemented in a separate shared library
loaded by the UNO runtime. The naming scheme of the library is a concatenation
with the following: void SAL_CALL uno_initEnvironment( uno_Environment * pEnv );The first function, uno_ext_getMapping() ,
is called by the UNO runtime to get the mappings for both directions. The
uno_ext_getMapping() call
receives a source and destination environment handle to distinguish which
mapping is demanded. It is quite clear that the bridge library cannot be
unloaded while any code of it is still needed, i.e. interfaces are held.
So both mappings and any wrapped interface (proxy) that is exported needs
to modify a shared library wide reference count.void SAL_CALL uno_ext_getMapping( uno_Mapping ** ppMapping, uno_Environment * pFrom, uno_Environment * pTo ); |
||||||||||||||||||||||||||||||||||||
Environment |
||||||||||||||||||||||||||||||||||||
The intention of an environment (and programmatically
environment handles) is to identify (given by its type name and context
pointer) and optionally to provide extra functionality like interface
registration. /** The binary specification of an UNO environment. */ typedef struct _uno_Environment { /** reserved for future use (0 if not used) */ void * pReserved; /** type name of environment */ rtl_uString * pTypeName; /** free context pointer to be used for specific classes of environments (e.g., a jvm pointer) */ void * pContext; /** pointer to extended environment (interface registration functionality), if supported */ uno_ExtEnvironment * pExtEnv; /** Acquires this environment. @param pEnv this environment */ void (SAL_CALL * acquire)( uno_Environment * pEnv ); /** Releases this environment; last release of environment will revoke the environment from runtime. @param pEnv this environment */ void (SAL_CALL * release)( uno_Environment * pEnv ); /** Call this function to explicitly dispose this environment (e.g., release all interfaces). You might want to call this function before shutting down due to a runtime error. @param pEnv this environment */ void (SAL_CALL * dispose)( uno_Environment * pEnv ); /* ===== the following part will be late initialized by a matching bridge ===== * * ===== and is NOT for public use. ===== */ /** CALLBACK Disposing callback function pointer that can be set to get signalled before the environment is destroyed. @param pEnv environment that is being disposed */ void (SAL_CALL * environmentDisposing)( uno_Environment * pEnv ); } uno_Environment; /** Generic function pointer declaration to free a proxy object if it is not needed by the environment anymore. Any proxy object must register itself on first acquire() call and revoke itself on last release() call. This can happen several times because the environment caches proxy objects until the environment explicitly frees the proxy object calling this function. @param pEnv environment @param pProxy proxy pointer */ typedef void (SAL_CALL * uno_freeProxyFunc)( uno_ExtEnvironment * pEnv, void * pProxy ); /** Generic function pointer declaration to allocate memory. Used with getRegisteredInterfaces(). @param nBytes amount of memory in bytes @return pointer to allocated memory */ typedef void * (SAL_CALL * uno_memAlloc)( sal_uInt32 nBytes ); /** The binary specification of an UNO environment supporting interface registration. */ typedef struct _uno_ExtEnvironment { /** inherits all members of an uno_Environment */ uno_Environment aBase; /** Registers an interface of this environment. @param pEnv this environment @param ppInterface inout parameter of interface to be registered @param pOId object id of interface @param pTypeDescr type description of interface */ void (SAL_CALL * registerInterface)( uno_ExtEnvironment * pEnv, void ** ppInterface, rtl_uString * pOId, typelib_InterfaceTypeDescription * pTypeDescr ); /** Registers a proxy interface of this environment that can be reanimated and is freed explicitly by this environment. @param pEnv this environment @param ppInterface inout parameter of interface to be registered @param freeProxy function to free proxy object @param pOId object id of interface @param pTypeDescr type description of interface */ void (SAL_CALL * registerProxyInterface)( uno_ExtEnvironment * pEnv, void ** ppProxy, uno_freeProxyFunc freeProxy, rtl_uString * pOId, typelib_InterfaceTypeDescription * pTypeDescr ); /** Revokes an interface from this environment. You have to revoke any interface that has been registered via this method. @param pEnv this environment @param pInterface interface to be revoked */ void (SAL_CALL * revokeInterface)( uno_ExtEnvironment * pEnv, void * pInterface ); /** Provides the object id of a given interface. @param ppOut inout oid @param pInterface interface of object */ void (SAL_CALL * getObjectIdentifier)( uno_ExtEnvironment * pEnv, rtl_uString ** ppOId, void * pInterface ); /** Retrieves an interface identified by its object id and type from this environment. Interfaces are retrieved in the same order as they are registered. @param pEnv this environment @param ppInterface inout parameter for the registered interface; (0) if none was found @param pOId object id of interface to be retrieved @param pTypeDescr type description of interface to be retrieved */ void (SAL_CALL * getRegisteredInterface)( uno_ExtEnvironment * pEnv, void ** ppInterface, rtl_uString * pOId, typelib_InterfaceTypeDescription * pTypeDescr ); /** Returns all currently registered interfaces of this environment. The memory block allocated might be slightly larger than (*pnLen * sizeof(void *)). @param pEnv this environment @param pppInterfaces out param; pointer to array of interface pointers @param pnLen out param; length of array @param memAlloc function for allocating memory that is passed back */ void (SAL_CALL * getRegisteredInterfaces)( uno_ExtEnvironment * pEnv, void *** pppInterfaces, sal_Int32 * pnLen, uno_memAlloc memAlloc ); /* ===== the following part will be late initialized by a matching bridge ===== * * ===== and is NOT for public use. ===== */ /** Computes an object id of the given interface; is called by the environment implementation. @param pEnv corresponding environment @param ppOId out param: computed id @param pInterface an interface */ void (SAL_CALL * computeObjectIdentifier)( uno_ExtEnvironment * pEnv, rtl_uString ** ppOId, void * pInterface ); /** Function to acquire an interface. @param pEnv corresponding environment @param pInterface an interface */ void (SAL_CALL * acquireInterface)( uno_ExtEnvironment * pEnv, void * pInterface ); /** Function to release an interface. @param pEnv corresponding environment @param pInterface an interface */ void (SAL_CALL * releaseInterface)( uno_ExtEnvironment * pEnv, void * pInterface ); } uno_ExtEnvironment; There is a distinction between registered environments
and anonymous environments. You can obtain an existing environment by
calling
|
||||||||||||||||||||||||||||||||||||
Mapping |
||||||||||||||||||||||||||||||||||||
A bridge consists of two mappings. Each mapping is dependent
on its counterpart mapping, because performing a call may bring up the
need to convert interfaces from one environment to the other (e.g., in
parameter interface) and vice versa (e.g., return values).
The whole scenario is shown in the
big picture. |
||||||||||||||||||||||||||||||||||||
Microsoft Visual C++ - UNO bridge |
||||||||||||||||||||||||||||||||||||
This is a short, source code based description
of an UNO bridge implementation for C++ objects compiled with the
Microsoft Visual C++ 4-6.
It covers the rudimental calling stuff which is very similar to other compilers
but omits exception handling. Mapping Mapping an interface from one environment to another is transparantly done via the right mapping, e.g. like the following XFoo interface from UNO to the Microsoft Visual C++ environment: Mapping aMapping( "uno", "msci" ); XFoo * pFoo = (XFoo *)aMapping.mapInterface( pUnoFoo, ::getCppuType( (const Reference< XFoo > *)0 ) ); ... pFoo->bar(); ... pFoo->release(); The given UNO interface is mapped to the microsoft
world transparently, so you can call methods and catch exceptions as if
the calls are performed on an "original" Visual C++ object.
struct cppu_cppInterfaceProxy : public ::com::sun::star::uno::XInterface { oslInterlockedCount nRef; cppu_Bridge * pBridge; // mapping information uno_Interface * pUnoI; // wrapped interface typelib_InterfaceTypeDescription * pTypeDescr; ::rtl::OUString oid; // non virtual methods called on incoming vtable calls #1, #2 inline void SAL_CALL acquireProxy(); inline void SAL_CALL releaseProxy(); // XInterface: these are only here for dummy, there will be a patched vtable! virtual ::com::sun::star::uno::Any SAL_CALL queryInterface( const ::com::sun::star::uno::Type & ) { return ::com::sun::star::uno::Any(); } // dont use this, use cppu_queryInterface()! virtual void SAL_CALL acquire() {} // dont use this, use cppu_acquire()! virtual void SAL_CALL release() {} // dont use this, use cppu_release()! // ctor inline cppu_cppInterfaceProxy( cppu_Bridge * pBridge_, uno_Interface * pUnoI_, typelib_InterfaceTypeDescription * pTypeDescr_, const ::rtl::OUString & rOId_ ); };
The proxy manages its reference count, a pointer to its bridge to get the counterpart mapping, the uno
interface it delegates calls to, the (interface) type it is emulating
and an object identifier (oid).
void SAL_CALL cppu_cppInterfaceProxy_patchVtable( XInterface * pCppI, typelib_InterfaceTypeDescription * pTypeDescr ) { static MediateVtables * s_pMediateVtables = 0; if (! s_pMediateVtables) { MutexGuard aGuard( Mutex::getGlobalMutex() ); if (! s_pMediateVtables) { static MediateVtables s_aMediateVtables; s_pMediateVtables = &s_aMediateVtables; } } // nMapFunctionIndexToMemberIndex: minimum size of demanded vtable *(const void **)pCppI = s_pMediateVtables->getMediateVtable( pTypeDescr->nMapFunctionIndexToMemberIndex ); } The call stack of a virtual function call looks like this:
The proxy vtable (i.e. function pointer array to perform polymorphic calls on C++ objects) determines which function should be called (there is an initial vtable of 256 function slots that is used for any proxy while no proxy demands a larger one):
The only task of the proxy vtable for Visual C++ is to determine the call index of the uno method that is to be called, so the proxy vtable looks like:
First the /** * is called on incoming vtable calls * (called by asm snippets) */ static __declspec(naked) void __cdecl cpp_vtable_call(void) { __asm { sub esp, 8 // space for immediate return type push esp push eax // vtable index mov eax, esp add eax, 16 push eax // original stack ptr call cpp_mediate add esp, 12 cmp eax, typelib_TypeClass_FLOAT je Lfloat cmp eax, typelib_TypeClass_DOUBLE je Ldouble cmp eax, typelib_TypeClass_HYPER je Lhyper cmp eax, typelib_TypeClass_UNSIGNED_HYPER je Lhyper // rest is eax pop eax add esp, 4 ret Lhyper: pop eax pop edx ret Lfloat: fld dword ptr [esp] add esp, 8 ret Ldouble: fld qword ptr [esp] add esp, 8 ret } }
The static typelib_TypeClass __cdecl cpp_mediate( void ** pCallStack, sal_Int32 nVtableCall, sal_Int64 * pRegisterReturn /* space for register return */ ) { OSL_ENSHURE( sizeof(sal_Int32)==sizeof(void *), "### unexpected!" ); // pCallStack: ret adr, this, [ret *], params // _this_ ptr is patched cppu_XInterfaceProxy object cppu_cppInterfaceProxy * pThis = static_cast< cppu_cppInterfaceProxy * >( reinterpret_cast< XInterface * >( pCallStack[1] ) ); typelib_InterfaceTypeDescription * pTypeDescr = pThis->pTypeDescr; OSL_ENSHURE( nVtableCall < pTypeDescr->nMapFunctionIndexToMemberIndex, "### illegal vtable index!" ); if (nVtableCall >= pTypeDescr->nMapFunctionIndexToMemberIndex) { throw RuntimeException( OUString( RTL_CONSTASCII_USTRINGPARAM("illegal vtable index!") ), (XInterface *)pThis ); } // determine called method sal_Int32 nMemberPos = pTypeDescr->pMapFunctionIndexToMemberIndex[nVtableCall]; OSL_ENSHURE( nMemberPos < pTypeDescr->nAllMembers, "### illegal member index!" ); TypeDescription aMemberDescr( pTypeDescr->ppAllMembers[nMemberPos] ); typelib_TypeClass eRet; switch (aMemberDescr.get()->eTypeClass) { case typelib_TypeClass_INTERFACE_ATTRIBUTE: { if (pTypeDescr->pMapMemberIndexToFunctionIndex[nMemberPos] == nVtableCall) { // is GET method eRet = cpp2uno_call( pThis, aMemberDescr.get(), ((typelib_InterfaceAttributeTypeDescription *)aMemberDescr.get())->pAttributeTypeRef, 0, 0, // no params pCallStack, pRegisterReturn ); } else { // is SET method typelib_MethodParameter aParam; aParam.pTypeRef = ((typelib_InterfaceAttributeTypeDescription *)aMemberDescr.get())->pAttributeTypeRef; aParam.bIn = sal_True; aParam.bOut = sal_False; eRet = cpp2uno_call( pThis, aMemberDescr.get(), 0, // indicates void return 1, &aParam, pCallStack, pRegisterReturn ); } break; } case typelib_TypeClass_INTERFACE_METHOD: { // is METHOD switch (nVtableCall) { // standard XInterface vtable calls case 1: // acquire() pThis->acquireProxy(); // non virtual call! eRet = typelib_TypeClass_VOID; break; case 2: // release() pThis->releaseProxy(); // non virtual call! eRet = typelib_TypeClass_VOID; break; case 0: // queryInterface() opt { typelib_TypeDescription * pTD = 0; TYPELIB_DANGER_GET( &pTD, reinterpret_cast< Type * >( pCallStack[3] )->getTypeLibType() ); OSL_ASSERT( pTD ); XInterface * pInterface = 0; (*pThis->pBridge->pCppEnv->getRegisteredInterface)( pThis->pBridge->pCppEnv, (void **)&pInterface, pThis->oid.pData, (typelib_InterfaceTypeDescription *)pTD ); if (pInterface) { uno_any_construct( reinterpret_cast< uno_Any * >( pCallStack[2] ), &pInterface, pTD, cpp_acquire ); pInterface->release(); TYPELIB_DANGER_RELEASE( pTD ); *(void **)pRegisterReturn = pCallStack[2]; eRet = typelib_TypeClass_ANY; break; } TYPELIB_DANGER_RELEASE( pTD ); } // else perform queryInterface() default: eRet = cpp2uno_call( pThis, aMemberDescr.get(), ((typelib_InterfaceMethodTypeDescription *)aMemberDescr.get())->pReturnTypeRef, ((typelib_InterfaceMethodTypeDescription *)aMemberDescr.get())->nParams, ((typelib_InterfaceMethodTypeDescription *)aMemberDescr.get())->pParams, pCallStack, pRegisterReturn ); } break; } default: { throw RuntimeException( OUString( RTL_CONSTASCII_USTRINGPARAM("no member description found!") ), (XInterface *)pThis ); // is here for dummy eRet = typelib_TypeClass_VOID; } } return eRet; }
The static typelib_TypeClass cpp2uno_call( cppu_cppInterfaceProxy * pThis, const typelib_TypeDescription * pMemberTypeDescr, typelib_TypeDescriptionReference * pReturnTypeRef, // 0 indicates void return sal_Int32 nParams, typelib_MethodParameter * pParams, void ** pCallStack, sal_Int64 * pRegisterReturn /* space for register return */ ) { // pCallStack: ret, this, [complex return ptr], params char * pCppStack = (char *)(pCallStack +2); // return typelib_TypeDescription * pReturnTypeDescr = 0; if (pReturnTypeRef) TYPELIB_DANGER_GET( &pReturnTypeDescr, pReturnTypeRef ); void * pUnoReturn = 0; void * pCppReturn = 0; // complex return ptr: if != 0 && != pUnoReturn, reconversion need if (pReturnTypeDescr) { if (cppu_isSimpleType( pReturnTypeDescr )) { pUnoReturn = pRegisterReturn; // direct way for simple types } else // complex return via ptr (pCppReturn) { pCppReturn = *(void **)pCppStack; pCppStack += sizeof(void *); pUnoReturn = (cppu_relatesToInterface( pReturnTypeDescr ) ? alloca( pReturnTypeDescr->nSize ) : pCppReturn); // direct way } } // stack space OSL_ENSHURE( sizeof(void *) == sizeof(sal_Int32), "### unexpected size!" ); // parameters void ** pUnoArgs = (void **)alloca( 4 * sizeof(void *) * nParams ); void ** pCppArgs = pUnoArgs + nParams; // indizes of values this have to be converted (interface conversion cpp<=>uno) sal_Int32 * pTempIndizes = (sal_Int32 *)(pUnoArgs + (2 * nParams)); // type descriptions for reconversions typelib_TypeDescription ** ppTempParamTypeDescr = (typelib_TypeDescription **)(pUnoArgs + (3 * nParams)); sal_Int32 nTempIndizes = 0; for ( sal_Int32 nPos = 0; nPos < nParams; ++nPos ) { const typelib_MethodParameter & rParam = pParams[nPos]; typelib_TypeDescription * pParamTypeDescr = 0; TYPELIB_DANGER_GET( &pParamTypeDescr, rParam.pTypeRef ); if (!rParam.bOut && cppu_isSimpleType( pParamTypeDescr )) // value { pCppArgs[nPos] = pCppStack; pUnoArgs[nPos] = pCppStack; switch (pParamTypeDescr->eTypeClass) { case typelib_TypeClass_HYPER: case typelib_TypeClass_UNSIGNED_HYPER: case typelib_TypeClass_DOUBLE: pCppStack += sizeof(sal_Int32); // extra long } // no longer needed TYPELIB_DANGER_RELEASE( pParamTypeDescr ); } else // ptr to complex value | ref { pCppArgs[nPos] = *(void **)pCppStack; if (! rParam.bIn) // is pure out { // uno out is unconstructed mem! pUnoArgs[nPos] = alloca( pParamTypeDescr->nSize ); pTempIndizes[nTempIndizes] = nPos; // will be released at reconversion ppTempParamTypeDescr[nTempIndizes++] = pParamTypeDescr; } // is in/inout else if (cppu_relatesToInterface( pParamTypeDescr )) { uno_copyAndConvertData( pUnoArgs[nPos] = alloca( pParamTypeDescr->nSize ), *(void **)pCppStack, pParamTypeDescr, &pThis->pBridge->aCpp2Uno ); pTempIndizes[nTempIndizes] = nPos; // has to be reconverted // will be released at reconversion ppTempParamTypeDescr[nTempIndizes++] = pParamTypeDescr; } else // direct way { pUnoArgs[nPos] = *(void **)pCppStack; // no longer needed TYPELIB_DANGER_RELEASE( pParamTypeDescr ); } } pCppStack += sizeof(sal_Int32); // standard parameter length } // ExceptionHolder uno_Any aUnoExc; // Any will be constructed by callee uno_Any * pUnoExc = &aUnoExc; // invoke uno dispatch call (*pThis->pUnoI->pDispatcher)( pThis->pUnoI, pMemberTypeDescr, pUnoReturn, pUnoArgs, &pUnoExc ); // in case an exception occured... if (pUnoExc) { // destruct temporary in/inout params while (nTempIndizes--) { sal_Int32 nIndex = pTempIndizes[nTempIndizes]; if (pParams[nIndex].bIn) // is in/inout => was constructed uno_destructData( pUnoArgs[nIndex], ppTempParamTypeDescr[nTempIndizes], 0 ); TYPELIB_DANGER_RELEASE( ppTempParamTypeDescr[nTempIndizes] ); } if (pReturnTypeDescr) TYPELIB_DANGER_RELEASE( pReturnTypeDescr ); msci_raiseException( &aUnoExc, &pThis->pBridge->aUno2Cpp ); // has to destruct the any // is here for dummy return typelib_TypeClass_VOID; } else // else no exception occured... { // temporary params while (nTempIndizes--) { sal_Int32 nIndex = pTempIndizes[nTempIndizes]; typelib_TypeDescription * pParamTypeDescr = ppTempParamTypeDescr[nTempIndizes]; if (pParams[nIndex].bOut) // inout/out { // convert and assign uno_destructData( pCppArgs[nIndex], pParamTypeDescr, cpp_release ); uno_copyAndConvertData( pCppArgs[nIndex], pUnoArgs[nIndex], pParamTypeDescr, &pThis->pBridge->aUno2Cpp ); } // destroy temp uno param uno_destructData( pUnoArgs[nIndex], pParamTypeDescr, 0 ); TYPELIB_DANGER_RELEASE( pParamTypeDescr ); } // return if (pCppReturn) // has complex return { if (pUnoReturn != pCppReturn) // needs reconversion { uno_copyAndConvertData( pCppReturn, pUnoReturn, pReturnTypeDescr, &pThis->pBridge->aUno2Cpp ); // destroy temp uno return uno_destructData( pUnoReturn, pReturnTypeDescr, 0 ); } // complex return ptr is set to eax *(void **)pRegisterReturn = pCppReturn; } if (pReturnTypeDescr) { typelib_TypeClass eRet = (typelib_TypeClass)pReturnTypeDescr->eTypeClass; TYPELIB_DANGER_RELEASE( pReturnTypeDescr ); return eRet; } else return typelib_TypeClass_VOID; } }
The UNO Stub
If a Visual C++ interface is be mapped to
binary UNO (i.e. the intermediate environment), and that interface is mapped
to another compiler environment, then an UNO stub is created
, delegating
all UNO dispatch calls to its corresponding C++ interface (in this case Microsoft Visual C++). struct cppu_unoInterfaceProxy : public uno_Interface { oslInterlockedCount nRef; cppu_Bridge * pBridge; // mapping information ::com::sun::star::uno::XInterface * pCppI; // wrapped interface typelib_InterfaceTypeDescription * pTypeDescr; ::rtl::OUString oid; // ctor inline cppu_unoInterfaceProxy( cppu_Bridge * pBridge_, ::com::sun::star::uno::XInterface * pCppI_, typelib_InterfaceTypeDescription * pTypeDescr_, const ::rtl::OUString & rOId_ ); };
The UNO stub manages its reference count, a pointer to its bridge to access its
counterpart mapping, the C++ interface it delegates incoming dispatch calls to,
the (interface) type it is emulating and an object identifier (oid).
void SAL_CALL cppu_unoInterfaceProxy_dispatch( uno_Interface * pUnoI, const typelib_TypeDescription * pMemberDescr, void * pReturn, void * pArgs[], uno_Any ** ppException ) { // is my surrogate cppu_unoInterfaceProxy * pThis = static_cast< cppu_unoInterfaceProxy * >( pUnoI ); typelib_InterfaceTypeDescription * pTypeDescr = pThis->pTypeDescr; switch (pMemberDescr->eTypeClass) { case typelib_TypeClass_INTERFACE_ATTRIBUTE: { // determine vtable call index sal_Int32 nMemberPos = ((typelib_InterfaceMemberTypeDescription *)pMemberDescr)->nPosition; OSL_ENSHURE( nMemberPos < pTypeDescr->nAllMembers, "### member pos out of range!" ); sal_Int32 nVtableCall = pTypeDescr->pMapMemberIndexToFunctionIndex[nMemberPos]; OSL_ENSHURE( nVtableCall < pTypeDescr->nMapFunctionIndexToMemberIndex, "### illegal vtable index!" ); typelib_TypeDescriptionReference * pRuntimeExcRef = 0; if (pReturn) { // dependent dispatch cpp_call( pThis, nVtableCall, ((typelib_InterfaceAttributeTypeDescription *)pMemberDescr)->pAttributeTypeRef, 0, 0, // no params 1, &pRuntimeExcRef, // RuntimeException pReturn, pArgs, ppException ); } else { // is SET typelib_MethodParameter aParam; aParam.pTypeRef = ((typelib_InterfaceAttributeTypeDescription *)pMemberDescr)->pAttributeTypeRef; aParam.bIn = sal_True; aParam.bOut = sal_False; typelib_TypeDescriptionReference * pReturnTypeRef = 0; OUString aVoidName( RTL_CONSTASCII_USTRINGPARAM("void") ); typelib_typedescriptionreference_new( &pReturnTypeRef, typelib_TypeClass_VOID, aVoidName.pData ); // dependent dispatch cpp_call( pThis, nVtableCall +1, // get, then set method pReturnTypeRef, 1, &aParam, 1, &pRuntimeExcRef, pReturn, pArgs, ppException ); typelib_typedescriptionreference_release( pReturnTypeRef ); } break; } case typelib_TypeClass_INTERFACE_METHOD: { // determine vtable call index sal_Int32 nMemberPos = ((typelib_InterfaceMemberTypeDescription *)pMemberDescr)->nPosition; OSL_ENSHURE( nMemberPos < pTypeDescr->nAllMembers, "### member pos out of range!" ); sal_Int32 nVtableCall = pTypeDescr->pMapMemberIndexToFunctionIndex[nMemberPos]; OSL_ENSHURE( nVtableCall < pTypeDescr->nMapFunctionIndexToMemberIndex, "### illegal vtable index!" ); switch (nVtableCall) { // standard calls case 1: // acquire uno interface (*pUnoI->acquire)( pUnoI ); *ppException = 0; break; case 2: // release uno interface (*pUnoI->release)( pUnoI ); *ppException = 0; break; case 0: // queryInterface() opt { typelib_TypeDescription * pTD = 0; TYPELIB_DANGER_GET( &pTD, reinterpret_cast< Type * >( pArgs[0] )->getTypeLibType() ); OSL_ASSERT( pTD ); uno_Interface * pInterface = 0; (*pThis->pBridge->pUnoEnv->getRegisteredInterface)( pThis->pBridge->pUnoEnv, (void **)&pInterface, pThis->oid.pData, (typelib_InterfaceTypeDescription *)pTD ); if (pInterface) { uno_any_construct( reinterpret_cast< uno_Any * >( pReturn ), &pInterface, pTD, 0 ); (*pInterface->release)( pInterface ); TYPELIB_DANGER_RELEASE( pTD ); *ppException = 0; break; } TYPELIB_DANGER_RELEASE( pTD ); } // else perform queryInterface() default: // dependent dispatch cpp_call( pThis, nVtableCall, ((typelib_InterfaceMethodTypeDescription *)pMemberDescr)->pReturnTypeRef, ((typelib_InterfaceMethodTypeDescription *)pMemberDescr)->nParams, ((typelib_InterfaceMethodTypeDescription *)pMemberDescr)->pParams, ((typelib_InterfaceMethodTypeDescription *)pMemberDescr)->nExceptions, ((typelib_InterfaceMethodTypeDescription *)pMemberDescr)->ppExceptions, pReturn, pArgs, ppException ); } break; } default: { ::com::sun::star::uno::RuntimeException aExc( OUString( RTL_CONSTASCII_USTRINGPARAM("illegal member type description!") ), pThis->pCppI ); typelib_TypeDescription * pTD = 0; const Type & rExcType = ::getCppuType( (const ::com::sun::star::uno::RuntimeException *)0 ); TYPELIB_DANGER_GET( &pTD, rExcType.getTypeLibType() ); uno_any_construct( *ppException, &aExc, pTD, 0 ); TYPELIB_DANGER_RELEASE( pTD ); } } }
Further on static void cpp_call( cppu_unoInterfaceProxy * pThis, sal_Int32 nVtableCall, typelib_TypeDescriptionReference * pReturnTypeRef, sal_Int32 nParams, typelib_MethodParameter * pParams, sal_Int32 nExceptions, typelib_TypeDescriptionReference ** ppExceptionRefs, void * pUnoReturn, void * pUnoArgs[], uno_Any ** ppUnoExc ) { // max space for: [complex ret ptr], values|ptr ... char * pCppStack = (char *)alloca( sizeof(sal_Int32) + (nParams * sizeof(sal_Int64)) ); char * pCppStackStart = pCppStack; // return typelib_TypeDescription * pReturnTypeDescr = 0; TYPELIB_DANGER_GET( &pReturnTypeDescr, pReturnTypeRef ); OSL_ENSHURE( pReturnTypeDescr, "### expected return type description!" ); void * pCppReturn = 0; // if != 0 && != pUnoReturn, needs reconversion if (pReturnTypeDescr) { if (cppu_isSimpleType( pReturnTypeDescr )) { pCppReturn = pUnoReturn; // direct way for simple types } else { // complex return via ptr pCppReturn = *(void **)pCppStack = (cppu_relatesToInterface( pReturnTypeDescr ) ? alloca( pReturnTypeDescr->nSize ) : pUnoReturn); // direct way pCppStack += sizeof(void *); } } // stack space OSL_ENSHURE( sizeof(void *) == sizeof(sal_Int32), "### unexpected size!" ); // args void ** pCppArgs = (void **)alloca( 3 * sizeof(void *) * nParams ); // indizes of values this have to be converted (interface conversion cpp<=>uno) sal_Int32 * pTempIndizes = (sal_Int32 *)(pCppArgs + nParams); // type descriptions for reconversions typelib_TypeDescription ** ppTempParamTypeDescr = (typelib_TypeDescription **)(pCppArgs + (2 * nParams)); sal_Int32 nTempIndizes = 0; for ( sal_Int32 nPos = 0; nPos < nParams; ++nPos ) { const typelib_MethodParameter & rParam = pParams[nPos]; typelib_TypeDescription * pParamTypeDescr = 0; TYPELIB_DANGER_GET( &pParamTypeDescr, rParam.pTypeRef ); if (!rParam.bOut && cppu_isSimpleType( pParamTypeDescr )) { uno_copyAndConvertData( pCppArgs[nPos] = pCppStack, pUnoArgs[nPos], pParamTypeDescr, &pThis->pBridge->aUno2Cpp ); switch (pParamTypeDescr->eTypeClass) { case typelib_TypeClass_HYPER: case typelib_TypeClass_UNSIGNED_HYPER: case typelib_TypeClass_DOUBLE: pCppStack += sizeof(sal_Int32); // extra long } // no longer needed TYPELIB_DANGER_RELEASE( pParamTypeDescr ); } else // ptr to complex value | ref { if (! rParam.bIn) // is pure out { // cpp out is constructed mem, uno out is not! uno_constructData( *(void **)pCppStack = pCppArgs[nPos] = alloca( pParamTypeDescr->nSize ), pParamTypeDescr ); pTempIndizes[nTempIndizes] = nPos; // default constructed for cpp call // will be released at reconversion ppTempParamTypeDescr[nTempIndizes++] = pParamTypeDescr; } // is in/inout else if (cppu_relatesToInterface( pParamTypeDescr )) { uno_copyAndConvertData( *(void **)pCppStack = pCppArgs[nPos] = alloca( pParamTypeDescr->nSize ), pUnoArgs[nPos], pParamTypeDescr, &pThis->pBridge->aUno2Cpp ); pTempIndizes[nTempIndizes] = nPos; // has to be reconverted // will be released at reconversion ppTempParamTypeDescr[nTempIndizes++] = pParamTypeDescr; } else // direct way { *(void **)pCppStack = pCppArgs[nPos] = pUnoArgs[nPos]; // no longer needed TYPELIB_DANGER_RELEASE( pParamTypeDescr ); } } pCppStack += sizeof(sal_Int32); // standard parameter length } // only try-finally/ try-except statements possible... __try { __try { // pCppI is msci this pointer callVirtualMethod( pThis->pCppI, nVtableCall, pCppReturn, pReturnTypeDescr->eTypeClass, (sal_Int32 *)pCppStackStart, (pCppStack - pCppStackStart) / sizeof(sal_Int32) ); // NO exception occured... *ppUnoExc = 0; // reconvert temporary params while (nTempIndizes--) { sal_Int32 nIndex = pTempIndizes[nTempIndizes]; typelib_TypeDescription * pParamTypeDescr = ppTempParamTypeDescr[nTempIndizes]; if (pParams[nIndex].bIn) { if (pParams[nIndex].bOut) // inout { uno_destructData( pUnoArgs[nIndex], pParamTypeDescr, 0 ); // destroy uno value uno_copyAndConvertData( pUnoArgs[nIndex], pCppArgs[nIndex], pParamTypeDescr, &pThis->pBridge->aCpp2Uno ); } } else // pure out { uno_copyAndConvertData( pUnoArgs[nIndex], pCppArgs[nIndex], pParamTypeDescr, &pThis->pBridge->aCpp2Uno ); } // destroy temp cpp param => cpp: every param was constructed uno_destructData( pCppArgs[nIndex], pParamTypeDescr, cpp_release ); TYPELIB_DANGER_RELEASE( pParamTypeDescr ); } // return value if (pCppReturn && pUnoReturn != pCppReturn) { uno_copyAndConvertData( pUnoReturn, pCppReturn, pReturnTypeDescr, &pThis->pBridge->aCpp2Uno ); uno_destructData( pCppReturn, pReturnTypeDescr, cpp_release ); } } __except (msci_filterCppException( GetExceptionInformation(), *ppUnoExc, &pThis->pBridge->aCpp2Uno )) { // *ppUnoExc is untouched and any was constructed by filter function // __finally block will be called return; } } __finally { // cleanup of params was already done in reconversion loop iff no exception occured; // this is quicker than getting all param descriptions twice! // so cleanup only iff an exception occured: if (*ppUnoExc) { // temporary params while (nTempIndizes--) { sal_Int32 nIndex = pTempIndizes[nTempIndizes]; // destroy temp cpp param => cpp: every param was constructed uno_destructData( pCppArgs[nIndex], ppTempParamTypeDescr[nTempIndizes], cpp_release ); TYPELIB_DANGER_RELEASE( ppTempParamTypeDescr[nTempIndizes] ); } } // return type if (pReturnTypeDescr) TYPELIB_DANGER_RELEASE( pReturnTypeDescr ); } }
Finally static void callVirtualMethod( void * pThis, sal_Int32 nVtableIndex, void * pRegisterReturn, typelib_TypeClass eReturnTypeClass, sal_Int32 * pStackLongs, sal_Int32 nStackLongs ) { // parameter list is mixed list of * and values // reference parameters are pointers OSL_ENSHURE( pStackLongs && pThis, "### null ptr!" ); OSL_ENSHURE( (sizeof(void *) == 4) && (sizeof(sal_Int32) == 4), "### unexpected size of int!" ); __asm { mov eax, nStackLongs test eax, eax je Lcall // copy values mov ecx, eax shl eax, 2 // sizeof(sal_Int32) == 4 add eax, pStackLongs // params stack space Lcopy: sub eax, 4 push dword ptr [eax] dec ecx jne Lcopy Lcall: // call mov ecx, pThis push ecx // this ptr mov edx, [ecx] // pvft mov eax, nVtableIndex shl eax, 2 // sizeof(void *) == 4 add edx, eax call [edx] // interface method call must be __cdecl!!! // register return mov ecx, eReturnTypeClass cmp ecx, typelib_TypeClass_VOID je Lcleanup mov ebx, pRegisterReturn // int32 cmp ecx, typelib_TypeClass_LONG je Lint32 cmp ecx, typelib_TypeClass_UNSIGNED_LONG je Lint32 cmp ecx, typelib_TypeClass_ENUM je Lint32 // int8 cmp ecx, typelib_TypeClass_BOOLEAN je Lint8 cmp ecx, typelib_TypeClass_BYTE je Lint8 // int16 cmp ecx, typelib_TypeClass_CHAR je Lint16 cmp ecx, typelib_TypeClass_SHORT je Lint16 cmp ecx, typelib_TypeClass_UNSIGNED_SHORT je Lint16 // float cmp ecx, typelib_TypeClass_FLOAT je Lfloat // double cmp ecx, typelib_TypeClass_DOUBLE je Ldouble // int64 cmp ecx, typelib_TypeClass_HYPER je Lint64 cmp ecx, typelib_TypeClass_UNSIGNED_HYPER je Lint64 jmp Lcleanup // no simple type Lint8: mov byte ptr [ebx], al jmp Lcleanup Lint16: mov word ptr [ebx], ax jmp Lcleanup Lfloat: fstp dword ptr [ebx] jmp Lcleanup Ldouble: fstp qword ptr [ebx] jmp Lcleanup Lint64: mov dword ptr [ebx], eax mov dword ptr [ebx+4], edx jmp Lcleanup Lint32: mov dword ptr [ebx], eax jmp Lcleanup Lcleanup: // cleanup stack (obsolete though because of function) mov eax, nStackLongs shl eax, 2 // sizeof(sal_Int32) == 4 add eax, 4 // this ptr add esp, eax } } |
||||||||||||||||||||||||||||||||||||
Auhor:
Daniel Bölzle (last change: 2000/10/09 20:28:08) | ||||||||||||||||||||||||||||||||||||