From 940b4d1848e8c70ab7642901a68594e8016caffc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 27 Apr 2024 18:51:28 +0200 Subject: Adding upstream version 1:7.0.4. Signed-off-by: Daniel Baumann --- bridges/source/cpp_uno/shared/bridge.cxx | 210 +++++++++++ bridges/source/cpp_uno/shared/component.cxx | 227 ++++++++++++ .../source/cpp_uno/shared/cppinterfaceproxy.cxx | 143 ++++++++ bridges/source/cpp_uno/shared/types.cxx | 117 ++++++ .../source/cpp_uno/shared/unointerfaceproxy.cxx | 125 +++++++ bridges/source/cpp_uno/shared/vtablefactory.cxx | 399 +++++++++++++++++++++ bridges/source/cpp_uno/shared/vtables.cxx | 146 ++++++++ 7 files changed, 1367 insertions(+) create mode 100644 bridges/source/cpp_uno/shared/bridge.cxx create mode 100644 bridges/source/cpp_uno/shared/component.cxx create mode 100644 bridges/source/cpp_uno/shared/cppinterfaceproxy.cxx create mode 100644 bridges/source/cpp_uno/shared/types.cxx create mode 100644 bridges/source/cpp_uno/shared/unointerfaceproxy.cxx create mode 100644 bridges/source/cpp_uno/shared/vtablefactory.cxx create mode 100644 bridges/source/cpp_uno/shared/vtables.cxx (limited to 'bridges/source/cpp_uno/shared') diff --git a/bridges/source/cpp_uno/shared/bridge.cxx b/bridges/source/cpp_uno/shared/bridge.cxx new file mode 100644 index 000000000..3753341cc --- /dev/null +++ b/bridges/source/cpp_uno/shared/bridge.cxx @@ -0,0 +1,210 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 . + */ + + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace bridges::cpp_uno::shared { + +void freeMapping(uno_Mapping * pMapping) +{ + delete static_cast< Bridge::Mapping * >( pMapping )->pBridge; +} + +void acquireMapping(uno_Mapping * pMapping) +{ + static_cast< Bridge::Mapping * >( pMapping )->pBridge->acquire(); +} + +void releaseMapping(uno_Mapping * pMapping) +{ + static_cast< Bridge::Mapping * >( pMapping )->pBridge->release(); +} + +void cpp2unoMapping( + uno_Mapping * pMapping, void ** ppUnoI, void * pCppI, + typelib_InterfaceTypeDescription * pTypeDescr) +{ + assert(ppUnoI && pTypeDescr); + if (*ppUnoI) + { + (*static_cast< uno_Interface * >( *ppUnoI )->release)( + static_cast< uno_Interface * >( *ppUnoI ) ); + *ppUnoI = nullptr; + } + if (!pCppI) + return; + + Bridge * pBridge = static_cast< Bridge::Mapping * >( pMapping )->pBridge; + + // get object id of interface to be wrapped + rtl_uString * pOId = nullptr; + (*pBridge->pCppEnv->getObjectIdentifier)( + pBridge->pCppEnv, &pOId, pCppI ); + assert(pOId); + + // try to get any known interface from target environment + (*pBridge->pUnoEnv->getRegisteredInterface)( + pBridge->pUnoEnv, ppUnoI, pOId, pTypeDescr ); + + if (! *ppUnoI) // no existing interface, register new proxy interface + { + // try to publish a new proxy (refcount initially 1) + uno_Interface * pSurrogate + = bridges::cpp_uno::shared::UnoInterfaceProxy::create( + pBridge, + static_cast< ::com::sun::star::uno::XInterface * >( pCppI ), + pTypeDescr, pOId ); + + // proxy may be exchanged during registration + (*pBridge->pUnoEnv->registerProxyInterface)( + pBridge->pUnoEnv, reinterpret_cast< void ** >( &pSurrogate ), + freeUnoInterfaceProxy, pOId, + pTypeDescr ); + + *ppUnoI = pSurrogate; + } + ::rtl_uString_release( pOId ); +} + +void uno2cppMapping( + uno_Mapping * pMapping, void ** ppCppI, void * pUnoI, + typelib_InterfaceTypeDescription * pTypeDescr) +{ + assert(ppCppI && pTypeDescr); + if (*ppCppI) + { + static_cast< ::com::sun::star::uno::XInterface * >( *ppCppI )-> + release(); + *ppCppI = nullptr; + } + if (!pUnoI) + return; + + Bridge * pBridge = static_cast< Bridge::Mapping * >( pMapping )->pBridge; + + // get object id of uno interface to be wrapped + rtl_uString * pOId = nullptr; + (*pBridge->pUnoEnv->getObjectIdentifier)( + pBridge->pUnoEnv, &pOId, pUnoI ); + assert(pOId); + + // try to get any known interface from target environment + (*pBridge->pCppEnv->getRegisteredInterface)( + pBridge->pCppEnv, ppCppI, pOId, pTypeDescr ); + + if (! *ppCppI) // no existing interface, register new proxy interface + { + // try to publish a new proxy (ref count initially 1) + com::sun::star::uno::XInterface * pProxy + = bridges::cpp_uno::shared::CppInterfaceProxy::create( + pBridge, static_cast< uno_Interface * >( pUnoI ), + pTypeDescr, pOId ); + + // proxy may be exchanged during registration + (*pBridge->pCppEnv->registerProxyInterface)( + pBridge->pCppEnv, reinterpret_cast< void ** >( &pProxy ), + freeCppInterfaceProxy, pOId, + pTypeDescr ); + + *ppCppI = pProxy; + } + ::rtl_uString_release( pOId ); +} + +uno_Mapping * Bridge::createMapping( + uno_ExtEnvironment * pCppEnv, uno_ExtEnvironment * pUnoEnv, + bool bExportCpp2Uno) +{ + Bridge * bridge = new Bridge(pCppEnv, pUnoEnv, bExportCpp2Uno); + // coverity[leaked_storage] - on purpose + return bExportCpp2Uno ? &bridge->aCpp2Uno : &bridge->aUno2Cpp; +} + +void Bridge::acquire() +{ + if (++nRef != 1) + return; + + if (bExportCpp2Uno) + { + uno_Mapping * pMapping = &aCpp2Uno; + ::uno_registerMapping( + &pMapping, freeMapping, &pCppEnv->aBase, + &pUnoEnv->aBase, nullptr ); + } + else + { + uno_Mapping * pMapping = &aUno2Cpp; + ::uno_registerMapping( + &pMapping, freeMapping, &pUnoEnv->aBase, + &pCppEnv->aBase, nullptr ); + } +} + +void Bridge::release() +{ + if (! --nRef ) + { + ::uno_revokeMapping( bExportCpp2Uno ? &aCpp2Uno : &aUno2Cpp ); + } +} + +Bridge::Bridge( + uno_ExtEnvironment * pCppEnv_, uno_ExtEnvironment * pUnoEnv_, + bool bExportCpp2Uno_) + : nRef( 1 ) + , pCppEnv( pCppEnv_ ) + , pUnoEnv( pUnoEnv_ ) + , bExportCpp2Uno( bExportCpp2Uno_ ) +{ + aCpp2Uno.pBridge = this; + aCpp2Uno.acquire = acquireMapping; + aCpp2Uno.release = releaseMapping; + aCpp2Uno.mapInterface = cpp2unoMapping; + + aUno2Cpp.pBridge = this; + aUno2Cpp.acquire = acquireMapping; + aUno2Cpp.release = releaseMapping; + aUno2Cpp.mapInterface = uno2cppMapping; + + (*pCppEnv->aBase.acquire)( &pCppEnv->aBase ); + (*pUnoEnv->aBase.acquire)( &pUnoEnv->aBase ); +} + +Bridge::~Bridge() +{ + (*pUnoEnv->aBase.release)( &pUnoEnv->aBase ); + (*pCppEnv->aBase.release)( &pCppEnv->aBase ); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/bridges/source/cpp_uno/shared/component.cxx b/bridges/source/cpp_uno/shared/component.cxx new file mode 100644 index 000000000..5bdb0bb56 --- /dev/null +++ b/bridges/source/cpp_uno/shared/component.cxx @@ -0,0 +1,227 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace { + +const OUString & cppu_cppenv_getStaticOIdPart() +{ + static OUString s_aStaticOidPart = []() { + OUStringBuffer aRet(64); + aRet.append("];"); + // good guid + sal_uInt8 ar[16]; + ::rtl_getGlobalProcessId(ar); + for (unsigned char i : ar) + { + aRet.append(static_cast(i), 16); + } + return aRet.makeStringAndClear(); + }(); + return s_aStaticOidPart; +} + +} + +extern "C" { + +static void s_stub_computeObjectIdentifier(va_list * pParam) +{ + uno_ExtEnvironment * pEnv = va_arg(*pParam, uno_ExtEnvironment *); + rtl_uString ** ppOId = va_arg(*pParam, rtl_uString **); + void * pInterface = va_arg(*pParam, void *); + + + assert(pEnv && ppOId && pInterface); + if (!(pEnv && ppOId && pInterface)) + return; + + if (*ppOId) + { + rtl_uString_release( *ppOId ); + *ppOId = nullptr; + } + + try + { + ::com::sun::star::uno::Reference< + ::com::sun::star::uno::XInterface > xHome( + static_cast< ::com::sun::star::uno::XInterface * >( + pInterface ), + ::com::sun::star::uno::UNO_QUERY ); + assert(xHome.is() && "### query to XInterface failed!"); + if (xHome.is()) + { + // interface + OUStringBuffer oid( 64 ); + oid.append( reinterpret_cast< sal_Int64 >(xHome.get()), 16 ); + oid.append( ';' ); + // ;environment[context] + oid.append( OUString::unacquired(&pEnv->aBase.pTypeName) ); + oid.append( '[' ); + oid.append( + reinterpret_cast< sal_Int64 >(pEnv->aBase.pContext), + 16 ); + // ];good guid + oid.append( cppu_cppenv_getStaticOIdPart() ); + OUString aRet( oid.makeStringAndClear() ); + *ppOId = aRet.pData; + ::rtl_uString_acquire( *ppOId ); + } + } + catch (const ::com::sun::star::uno::RuntimeException & e) + { + SAL_WARN("bridges", + "### RuntimeException occurred during queryInterface(): " + << e.Message); + } +} + +static void computeObjectIdentifier( + uno_ExtEnvironment * pExtEnv, rtl_uString ** ppOId, void * pInterface ) +{ + uno_Environment_invoke(&pExtEnv->aBase, s_stub_computeObjectIdentifier, pExtEnv, ppOId, pInterface); +} + +static void s_stub_acquireInterface(va_list * pParam) +{ + /*uno_ExtEnvironment * pExtEnv = */va_arg(*pParam, uno_ExtEnvironment *); + void * pCppI = va_arg(*pParam, void *); + + static_cast< ::com::sun::star::uno::XInterface * >( pCppI )->acquire(); +} + +static void acquireInterface( uno_ExtEnvironment * pExtEnv, void * pCppI ) +{ + uno_Environment_invoke(&pExtEnv->aBase, s_stub_acquireInterface, pExtEnv, pCppI); +} + +static void s_stub_releaseInterface(va_list * pParam) +{ + /*uno_ExtEnvironment * pExtEnv = */va_arg(*pParam, uno_ExtEnvironment *); + void * pCppI = va_arg(*pParam, void *); + + static_cast< ::com::sun::star::uno::XInterface * >( pCppI )->release(); +} + +static void releaseInterface( uno_ExtEnvironment * pExtEnv, void * pCppI ) +{ + uno_Environment_invoke(&pExtEnv->aBase, s_stub_releaseInterface, pExtEnv, pCppI); +} + +static void environmentDisposing( + SAL_UNUSED_PARAMETER uno_Environment * ) +{ +} + +#ifdef DISABLE_DYNLOADING +#define uno_initEnvironment CPPU_ENV_uno_initEnvironment +#endif + +SAL_DLLPUBLIC_EXPORT void uno_initEnvironment(uno_Environment * pCppEnv) + SAL_THROW_EXTERN_C() +{ + assert(pCppEnv->pExtEnv); + assert( + ::rtl_ustr_ascii_compare_WithLength( + pCppEnv->pTypeName->buffer, rtl_str_getLength(CPPU_CURRENT_LANGUAGE_BINDING_NAME), CPPU_CURRENT_LANGUAGE_BINDING_NAME ) + == 0 + && "### wrong environment type!"); + reinterpret_cast(pCppEnv)->computeObjectIdentifier + = computeObjectIdentifier; + reinterpret_cast(pCppEnv)->acquireInterface = acquireInterface; + reinterpret_cast(pCppEnv)->releaseInterface = releaseInterface; + pCppEnv->environmentDisposing = environmentDisposing; +} + +#ifdef DISABLE_DYNLOADING +#define uno_ext_getMapping CPPU_ENV_uno_ext_getMapping +#endif + +SAL_DLLPUBLIC_EXPORT void uno_ext_getMapping( + uno_Mapping ** ppMapping, uno_Environment * pFrom, uno_Environment * pTo) + SAL_THROW_EXTERN_C() +{ + assert(ppMapping && pFrom && pTo); + if (!(ppMapping && pFrom && pTo && pFrom->pExtEnv && pTo->pExtEnv)) + return; + + uno_Mapping * pMapping = nullptr; + + OUString from_envTypeName(cppu::EnvDcp::getTypeName(pFrom->pTypeName)); + OUString to_envTypeName(cppu::EnvDcp::getTypeName(pTo->pTypeName)); + + if (rtl_ustr_ascii_compare( + from_envTypeName.pData->buffer, + CPPU_CURRENT_LANGUAGE_BINDING_NAME ) == 0 && + rtl_ustr_ascii_compare( + to_envTypeName.pData->buffer, UNO_LB_UNO ) == 0) + { + // ref count initially 1 + pMapping = bridges::cpp_uno::shared::Bridge::createMapping( + pFrom->pExtEnv, pTo->pExtEnv, true ); + ::uno_registerMapping( + &pMapping, bridges::cpp_uno::shared::freeMapping, + &pFrom->pExtEnv->aBase, + &pTo->pExtEnv->aBase, nullptr ); + } + else if (rtl_ustr_ascii_compare( + to_envTypeName.pData->buffer, + CPPU_CURRENT_LANGUAGE_BINDING_NAME ) == 0 && + rtl_ustr_ascii_compare( + from_envTypeName.pData->buffer, UNO_LB_UNO ) == 0) + { + // ref count initially 1 + pMapping = bridges::cpp_uno::shared::Bridge::createMapping( + pTo->pExtEnv, pFrom->pExtEnv, false ); + ::uno_registerMapping( + &pMapping, bridges::cpp_uno::shared::freeMapping, + &pFrom->pExtEnv->aBase, + &pTo->pExtEnv->aBase, nullptr ); + } + + if (*ppMapping) + { + (*(*ppMapping)->release)( *ppMapping ); + } + if (pMapping) + *ppMapping = pMapping; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/bridges/source/cpp_uno/shared/cppinterfaceproxy.cxx b/bridges/source/cpp_uno/shared/cppinterfaceproxy.cxx new file mode 100644 index 000000000..e4ee7800a --- /dev/null +++ b/bridges/source/cpp_uno/shared/cppinterfaceproxy.cxx @@ -0,0 +1,143 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include + +#include +#include + +#include +#include +#include + +namespace bridges::cpp_uno::shared { + +void freeCppInterfaceProxy(uno_ExtEnvironment * pEnv, void * pInterface) +{ + CppInterfaceProxy * pThis = CppInterfaceProxy::castInterfaceToProxy( + pInterface); + if (pEnv != pThis->pBridge->getCppEnv()) { + assert(false); + } + + (*pThis->pBridge->getUnoEnv()->revokeInterface)( + pThis->pBridge->getUnoEnv(), pThis->pUnoI ); + (*pThis->pUnoI->release)( pThis->pUnoI ); + ::typelib_typedescription_release( + &pThis->pTypeDescr->aBase ); + pThis->pBridge->release(); + +#if OSL_DEBUG_LEVEL > 1 + *(int *)pInterface = 0xdeadbabe; +#endif + pThis->~CppInterfaceProxy(); + delete[] reinterpret_cast< char * >(pThis); +} + +com::sun::star::uno::XInterface * CppInterfaceProxy::create( + bridges::cpp_uno::shared::Bridge * pBridge, uno_Interface * pUnoI, + typelib_InterfaceTypeDescription * pTypeDescr, OUString const & rOId) +{ + typelib_typedescription_complete( + reinterpret_cast< typelib_TypeDescription ** >(&pTypeDescr)); + static bridges::cpp_uno::shared::VtableFactory factory; + const bridges::cpp_uno::shared::VtableFactory::Vtables& rVtables( + factory.getVtables(pTypeDescr)); + std::unique_ptr< char[] > pMemory( + new char[ + sizeof (CppInterfaceProxy) + + (rVtables.count - 1) * sizeof (void **)]); + new(pMemory.get()) CppInterfaceProxy(pBridge, pUnoI, pTypeDescr, rOId); + CppInterfaceProxy * pProxy = reinterpret_cast< CppInterfaceProxy * >( + pMemory.release()); + for (sal_Int32 i = 0; i < rVtables.count; ++i) { + pProxy->vtables[i] = VtableFactory::mapBlockToVtable( + rVtables.blocks[i].start); + } + return castProxyToInterface(pProxy); +} + +void CppInterfaceProxy::acquireProxy() +{ + if (++nRef == 1) + { + // rebirth of proxy zombie + // register at cpp env + void * pThis = castProxyToInterface( this ); + (*pBridge->getCppEnv()->registerProxyInterface)( + pBridge->getCppEnv(), &pThis, freeCppInterfaceProxy, oid.pData, + pTypeDescr ); + assert(pThis == castProxyToInterface(this)); + } +} + +void CppInterfaceProxy::releaseProxy() +{ + if (! --nRef ) // last release + { + // revoke from cpp env + (*pBridge->getCppEnv()->revokeInterface)( + pBridge->getCppEnv(), castProxyToInterface( this ) ); + } +} + +CppInterfaceProxy::CppInterfaceProxy( + bridges::cpp_uno::shared::Bridge * pBridge_, uno_Interface * pUnoI_, + typelib_InterfaceTypeDescription * pTypeDescr_, OUString const & rOId_) + : nRef( 1 ) + , pBridge( pBridge_ ) + , pUnoI( pUnoI_ ) + , pTypeDescr( pTypeDescr_ ) + , oid( rOId_ ) +{ + pBridge->acquire(); + ::typelib_typedescription_acquire( &pTypeDescr->aBase ); + (*pUnoI->acquire)( pUnoI ); + (*pBridge->getUnoEnv()->registerInterface)( + pBridge->getUnoEnv(), reinterpret_cast< void ** >( &pUnoI ), oid.pData, + pTypeDescr ); +} + +CppInterfaceProxy::~CppInterfaceProxy() +{} + +com::sun::star::uno::XInterface * CppInterfaceProxy::castProxyToInterface( + CppInterfaceProxy * pProxy) +{ + return reinterpret_cast< com::sun::star::uno::XInterface * >( + &pProxy->vtables); +} + +CppInterfaceProxy * CppInterfaceProxy::castInterfaceToProxy(void * pInterface) +{ + // pInterface == &pProxy->vtables (this emulated offsetof is not truly + // portable): + char const * const base = reinterpret_cast< char const * >(16); + std::ptrdiff_t const offset = reinterpret_cast< char const * >( + &reinterpret_cast< CppInterfaceProxy const * >(base)->vtables) - base; + return reinterpret_cast< CppInterfaceProxy * >( + static_cast< char * >(pInterface) - offset); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/bridges/source/cpp_uno/shared/types.cxx b/bridges/source/cpp_uno/shared/types.cxx new file mode 100644 index 000000000..61dc3dabe --- /dev/null +++ b/bridges/source/cpp_uno/shared/types.cxx @@ -0,0 +1,117 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 . + */ + + +#include + +#include +#include + +namespace bridges::cpp_uno::shared { + +bool isSimpleType(typelib_TypeClass typeClass) { + return typeClass <= typelib_TypeClass_DOUBLE + || typeClass == typelib_TypeClass_ENUM; +} + +bool isSimpleType(typelib_TypeDescriptionReference const * type) { + return isSimpleType(type->eTypeClass); +} + +bool isSimpleType(typelib_TypeDescription const * type) { + return isSimpleType(type->eTypeClass); +} + +bool relatesToInterfaceType(typelib_TypeDescription const * type) { + switch (type->eTypeClass) { + case typelib_TypeClass_ANY: + case typelib_TypeClass_INTERFACE: + return true; + + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_EXCEPTION: + { + typelib_CompoundTypeDescription const * p + = reinterpret_cast< typelib_CompoundTypeDescription const * >( + type); + for (sal_Int32 i = 0; i < p->nMembers; ++i) { + switch (p->ppTypeRefs[i]->eTypeClass) { + case typelib_TypeClass_ANY: + case typelib_TypeClass_INTERFACE: + return true; + + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_EXCEPTION: + case typelib_TypeClass_SEQUENCE: + { + typelib_TypeDescription * t = nullptr; + TYPELIB_DANGER_GET(&t, p->ppTypeRefs[i]); + bool b = relatesToInterfaceType(t); + TYPELIB_DANGER_RELEASE(t); + if (b) { + return true; + } + } + break; + + default: + break; + } + } + if (p->pBaseTypeDescription != nullptr) { + return relatesToInterfaceType(&p->pBaseTypeDescription->aBase); + } + } + break; + + case typelib_TypeClass_SEQUENCE: + switch (reinterpret_cast< typelib_IndirectTypeDescription const * >( + type)->pType->eTypeClass) { + case typelib_TypeClass_ANY: + case typelib_TypeClass_INTERFACE: + return true; + + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_EXCEPTION: + case typelib_TypeClass_SEQUENCE: + { + typelib_TypeDescription * t = nullptr; + TYPELIB_DANGER_GET( + &t, + reinterpret_cast< typelib_IndirectTypeDescription const * >( + type)->pType); + bool b = relatesToInterfaceType(t); + TYPELIB_DANGER_RELEASE(t); + return b; + } + + default: + break; + } + break; + + default: + break; + } + return false; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/bridges/source/cpp_uno/shared/unointerfaceproxy.cxx b/bridges/source/cpp_uno/shared/unointerfaceproxy.cxx new file mode 100644 index 000000000..91578e999 --- /dev/null +++ b/bridges/source/cpp_uno/shared/unointerfaceproxy.cxx @@ -0,0 +1,125 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include + +namespace bridges::cpp_uno::shared { + +void freeUnoInterfaceProxy(uno_ExtEnvironment * pEnv, void * pProxy) +{ + UnoInterfaceProxy * pThis = + static_cast< UnoInterfaceProxy * >( + static_cast< uno_Interface * >( pProxy ) ); + if (pEnv != pThis->pBridge->getUnoEnv()) { + assert(false); + } + + (*pThis->pBridge->getCppEnv()->revokeInterface)( + pThis->pBridge->getCppEnv(), pThis->pCppI ); + pThis->pCppI->release(); + ::typelib_typedescription_release(&pThis->pTypeDescr->aBase); + pThis->pBridge->release(); + +#if OSL_DEBUG_LEVEL > 1 + *(int *)pProxy = 0xdeadbabe; +#endif + delete pThis; +} + +void acquireProxy(uno_Interface * pUnoI) +{ + if (++static_cast< UnoInterfaceProxy * >( pUnoI )->nRef != 1) + return; + + // rebirth of proxy zombie + // register at uno env +#if OSL_DEBUG_LEVEL > 1 + void * pThis = pUnoI; +#endif + (*static_cast< UnoInterfaceProxy * >( pUnoI )->pBridge->getUnoEnv()-> + registerProxyInterface)( + static_cast< UnoInterfaceProxy * >( pUnoI )->pBridge->getUnoEnv(), + reinterpret_cast< void ** >( &pUnoI ), freeUnoInterfaceProxy, + static_cast< UnoInterfaceProxy * >( pUnoI )->oid.pData, + static_cast< UnoInterfaceProxy * >( pUnoI )->pTypeDescr ); +#if OSL_DEBUG_LEVEL > 1 + assert(pThis == pUnoI); +#endif +} + +void releaseProxy(uno_Interface * pUnoI) +{ + if (! --static_cast< UnoInterfaceProxy * >( pUnoI )->nRef ) + { + // revoke from uno env on last release + (*static_cast< UnoInterfaceProxy * >( pUnoI )->pBridge->getUnoEnv()-> + revokeInterface)( + static_cast< UnoInterfaceProxy * >( pUnoI )->pBridge->getUnoEnv(), + pUnoI ); + } +} + +UnoInterfaceProxy * UnoInterfaceProxy::create( + bridges::cpp_uno::shared::Bridge * pBridge, + com::sun::star::uno::XInterface * pCppI, + typelib_InterfaceTypeDescription * pTypeDescr, + OUString const & rOId) +{ + return new UnoInterfaceProxy(pBridge, pCppI, pTypeDescr, rOId); +} + +UnoInterfaceProxy::UnoInterfaceProxy( + bridges::cpp_uno::shared::Bridge * pBridge_, + com::sun::star::uno::XInterface * pCppI_, + typelib_InterfaceTypeDescription * pTypeDescr_, OUString const & rOId_) + : nRef( 1 ) + , pBridge( pBridge_ ) + , pCppI( pCppI_ ) + , pTypeDescr( pTypeDescr_ ) + , oid( rOId_ ) +{ + pBridge->acquire(); + ::typelib_typedescription_acquire(&pTypeDescr->aBase); + if (!pTypeDescr->aBase.bComplete) + ::typelib_typedescription_complete( + reinterpret_cast(&pTypeDescr)); + assert(pTypeDescr->aBase.bComplete); + pCppI->acquire(); + (*pBridge->getCppEnv()->registerInterface)( + pBridge->getCppEnv(), reinterpret_cast< void ** >( &pCppI ), oid.pData, + pTypeDescr ); + + // uno_Interface + acquire = acquireProxy; + release = releaseProxy; + pDispatcher = unoInterfaceProxyDispatch; +} + +UnoInterfaceProxy::~UnoInterfaceProxy() +{} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/bridges/source/cpp_uno/shared/vtablefactory.cxx b/bridges/source/cpp_uno/shared/vtablefactory.cxx new file mode 100644 index 000000000..90c414290 --- /dev/null +++ b/bridges/source/cpp_uno/shared/vtablefactory.cxx @@ -0,0 +1,399 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 . + */ + + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#if defined SAL_UNX +#include +#include +#include +#include +#elif defined _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#else +#error Unsupported platform +#endif + +#if defined USE_DOUBLE_MMAP +#include +#endif + +#if defined MACOSX && defined __aarch64__ +#include +#endif + +using bridges::cpp_uno::shared::VtableFactory; + +namespace { + +extern "C" void * allocExec( + SAL_UNUSED_PARAMETER rtl_arena_type *, sal_Size * size) +{ + std::size_t pagesize; +#if defined SAL_UNX +#if defined FREEBSD || defined NETBSD || defined OPENBSD || defined DRAGONFLY || defined HAIKU + pagesize = getpagesize(); +#else + pagesize = sysconf(_SC_PAGESIZE); +#endif +#elif defined _WIN32 + SYSTEM_INFO info; + GetSystemInfo(&info); + pagesize = info.dwPageSize; +#else +#error Unsupported platform +#endif + std::size_t n = (*size + (pagesize - 1)) & ~(pagesize - 1); + void * p; +#if defined SAL_UNX +#if defined MACOSX + p = mmap( + nullptr, n, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON | MAP_JIT, -1, + 0); + if (p != MAP_FAILED) { + goto done; + } + { + auto const e = errno; + SAL_INFO("bridges.osx", "mmap failed with " << e); + if (e != EINVAL) { + p = nullptr; + goto done; + } + } + // At least some macOS 10.13 machines are reported to fail the above mmap with EINVAL (see + // tdf#134754 "Crash on macOS 10.13 opening local HSQLDB-based odb file in Base on LibreOffice 7 + // rc1", so in that case retry with the "traditional" approach: +#endif + p = mmap( + nullptr, n, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, + 0); + if (p == MAP_FAILED) { + p = nullptr; + } + else if (mprotect (p, n, PROT_READ | PROT_WRITE | PROT_EXEC) == -1) + { + munmap (p, n); + p = nullptr; + } +#if defined MACOSX +done: +#endif +#elif defined _WIN32 + p = VirtualAlloc(nullptr, n, MEM_COMMIT, PAGE_EXECUTE_READWRITE); +#endif + if (p != nullptr) { + *size = n; + } + return p; +} + +extern "C" void freeExec( + SAL_UNUSED_PARAMETER rtl_arena_type *, void * address, sal_Size size) +{ +#if defined SAL_UNX + munmap(address, size); +#elif defined _WIN32 + (void) size; // unused + VirtualFree(address, 0, MEM_RELEASE); +#endif +} + +} + +class VtableFactory::GuardedBlocks: + public std::vector +{ +public: + GuardedBlocks(const GuardedBlocks&) = delete; + const GuardedBlocks& operator=(const GuardedBlocks&) = delete; + + explicit GuardedBlocks(VtableFactory const & factory): + m_factory(factory), m_guarded(true) {} + + ~GuardedBlocks(); + + void unguard() { m_guarded = false; } + +private: + VtableFactory const & m_factory; + bool m_guarded; +}; + +VtableFactory::GuardedBlocks::~GuardedBlocks() { + if (m_guarded) { + for (iterator i(begin()); i != end(); ++i) { + m_factory.freeBlock(*i); + } + } +} + +class VtableFactory::BaseOffset { +public: + explicit BaseOffset(typelib_InterfaceTypeDescription * type) { calculate(type, 0); } + + sal_Int32 getFunctionOffset(OUString const & name) const + { return m_map.find(name)->second; } + +private: + sal_Int32 calculate( + typelib_InterfaceTypeDescription * type, sal_Int32 offset); + + std::unordered_map< OUString, sal_Int32 > m_map; +}; + +sal_Int32 VtableFactory::BaseOffset::calculate( + typelib_InterfaceTypeDescription * type, sal_Int32 offset) +{ + OUString name(type->aBase.pTypeName); + auto it = m_map.find(name); + if (it == m_map.end()) { + for (sal_Int32 i = 0; i < type->nBaseTypes; ++i) { + offset = calculate(type->ppBaseTypes[i], offset); + } + m_map.insert(it, {name, offset}); + typelib_typedescription_complete( + reinterpret_cast< typelib_TypeDescription ** >(&type)); + offset += bridges::cpp_uno::shared::getLocalFunctions(type); + } + return offset; +} + +VtableFactory::VtableFactory(): m_arena( + rtl_arena_create( + "bridges::cpp_uno::shared::VtableFactory", + sizeof (void *), // to satisfy alignment requirements + 0, nullptr, allocExec, freeExec, 0)) +{ + if (m_arena == nullptr) { + throw std::bad_alloc(); + } +} + +VtableFactory::~VtableFactory() { + { + osl::MutexGuard guard(m_mutex); + for (const auto& rEntry : m_map) { + for (sal_Int32 j = 0; j < rEntry.second.count; ++j) { + freeBlock(rEntry.second.blocks[j]); + } + } + } + rtl_arena_destroy(m_arena); +} + +const VtableFactory::Vtables& VtableFactory::getVtables( + typelib_InterfaceTypeDescription * type) +{ + OUString name(type->aBase.pTypeName); + osl::MutexGuard guard(m_mutex); + Map::iterator i(m_map.find(name)); + if (i == m_map.end()) { + GuardedBlocks blocks(*this); + createVtables(blocks, BaseOffset(type), type, 0, type, true); + Vtables vtables; + assert(blocks.size() <= SAL_MAX_INT32); + vtables.count = static_cast< sal_Int32 >(blocks.size()); + vtables.blocks.reset(new Block[vtables.count]); + for (sal_Int32 j = 0; j < vtables.count; ++j) { + vtables.blocks[j] = blocks[j]; + } + i = m_map.emplace(name, std::move(vtables)).first; + blocks.unguard(); + } + return i->second; +} + +#ifdef USE_DOUBLE_MMAP +bool VtableFactory::createBlock(Block &block, sal_Int32 slotCount) const +{ + std::size_t size = getBlockSize(slotCount); + std::size_t pagesize = sysconf(_SC_PAGESIZE); + block.size = (size + (pagesize - 1)) & ~(pagesize - 1); + block.fd = -1; + + // Try non-doublemmaped allocation first: + block.start = block.exec = rtl_arena_alloc(m_arena, &block.size); + if (block.start != nullptr) { + return true; + } + + osl::Security aSecurity; + OUString strDirectory; + OUString strURLDirectory; + if (aSecurity.getHomeDir(strURLDirectory)) + osl::File::getSystemPathFromFileURL(strURLDirectory, strDirectory); + + for (int i = strDirectory.isEmpty() ? 1 : 0; i < 2; ++i) + { + if (strDirectory.isEmpty()) + strDirectory = "/tmp"; + + strDirectory += "/.execoooXXXXXX"; + OString aTmpName = OUStringToOString(strDirectory, osl_getThreadTextEncoding()); + std::unique_ptr tmpfname(new char[aTmpName.getLength()+1]); + strncpy(tmpfname.get(), aTmpName.getStr(), aTmpName.getLength()+1); + // coverity[secure_temp] - https://communities.coverity.com/thread/3179 + if ((block.fd = mkstemp(tmpfname.get())) == -1) + fprintf(stderr, "mkstemp(\"%s\") failed: %s\n", tmpfname.get(), strerror(errno)); + if (block.fd == -1) + { + break; + } + unlink(tmpfname.get()); + tmpfname.reset(); +#if defined(HAVE_POSIX_FALLOCATE) + int err = posix_fallocate(block.fd, 0, block.size); +#else + int err = ftruncate(block.fd, block.size); +#endif + if (err != 0) + { +#if defined(HAVE_POSIX_FALLOCATE) + SAL_WARN("bridges", "posix_fallocate failed with code " << err); +#else + SAL_WARN("bridges", "truncation of executable memory area failed with code " << err); +#endif + close(block.fd); + block.fd = -1; + break; + } + block.start = mmap(nullptr, block.size, PROT_READ | PROT_WRITE, MAP_SHARED, block.fd, 0); + if (block.start== MAP_FAILED) { + block.start = nullptr; + } + block.exec = mmap(nullptr, block.size, PROT_READ | PROT_EXEC, MAP_SHARED, block.fd, 0); + if (block.exec == MAP_FAILED) { + block.exec = nullptr; + } + + //All good + if (block.start && block.exec && block.fd != -1) + break; + + freeBlock(block); + + strDirectory.clear(); + } + return (block.start != nullptr && block.exec != nullptr); +} + +void VtableFactory::freeBlock(Block const & block) const { + //if the double-map failed we were allocated on the arena + if (block.fd == -1 && block.start == block.exec && block.start != nullptr) + rtl_arena_free(m_arena, block.start, block.size); + else + { + if (block.start) munmap(block.start, block.size); + if (block.exec) munmap(block.exec, block.size); + if (block.fd != -1) close(block.fd); + } +} +#else +bool VtableFactory::createBlock(Block &block, sal_Int32 slotCount) const +{ + block.size = getBlockSize(slotCount); + block.start = rtl_arena_alloc(m_arena, &block.size); + return block.start != nullptr; +} + +void VtableFactory::freeBlock(Block const & block) const { + rtl_arena_free(m_arena, block.start, block.size); +} +#endif + +sal_Int32 VtableFactory::createVtables( + GuardedBlocks & blocks, BaseOffset const & baseOffset, + typelib_InterfaceTypeDescription * type, sal_Int32 vtableNumber, + typelib_InterfaceTypeDescription * mostDerived, bool includePrimary) const +{ +#if defined MACOSX && defined __aarch64__ + // TODO: Should we handle resetting this in a exception-throwing-safe way? + pthread_jit_write_protect_np(0); +#endif + if (includePrimary) { + sal_Int32 slotCount + = bridges::cpp_uno::shared::getPrimaryFunctions(type); + Block block; + if (!createBlock(block, slotCount)) { + throw std::bad_alloc(); + } + try { + Slot * slots = initializeBlock( + block.start, slotCount, vtableNumber, mostDerived); + unsigned char * codeBegin = + reinterpret_cast< unsigned char * >(slots); + unsigned char * code = codeBegin; + sal_Int32 vtableOffset = blocks.size() * sizeof (Slot *); + for (typelib_InterfaceTypeDescription const * type2 = type; + type2 != nullptr; type2 = type2->pBaseTypeDescription) + { + code = addLocalFunctions( + &slots, code, +#ifdef USE_DOUBLE_MMAP + reinterpret_cast(block.exec) - reinterpret_cast(block.start), +#endif + type2, + baseOffset.getFunctionOffset(type2->aBase.pTypeName), + bridges::cpp_uno::shared::getLocalFunctions(type2), + vtableOffset); + } + flushCode(codeBegin, code); +#ifdef USE_DOUBLE_MMAP + //Finished generating block, swap writable pointer with executable + //pointer + std::swap(block.start, block.exec); +#endif + blocks.push_back(block); + } catch (...) { + freeBlock(block); + throw; + } + } +#if defined MACOSX && defined __aarch64__ + pthread_jit_write_protect_np(1); +#endif + for (sal_Int32 i = 0; i < type->nBaseTypes; ++i) { + vtableNumber = createVtables( + blocks, baseOffset, type->ppBaseTypes[i], + vtableNumber + (i == 0 ? 0 : 1), mostDerived, i != 0); + } + return vtableNumber; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/bridges/source/cpp_uno/shared/vtables.cxx b/bridges/source/cpp_uno/shared/vtables.cxx new file mode 100644 index 000000000..beda5ad29 --- /dev/null +++ b/bridges/source/cpp_uno/shared/vtables.cxx @@ -0,0 +1,146 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 . + */ + + +#include + +#include +#include + +#include +#include + +namespace +{ + +/** + * Calculates the number of vtables associated with an interface type. + * + *

Multiple-inheritance C++ classes have more than one vtable.

+ * + * @param type a non-null pointer to an interface type description + * @return the number of vtables associated with the given interface type + */ +sal_Int32 getVtableCount(typelib_InterfaceTypeDescription const * type) { + sal_Int32 n = 0; + for (sal_Int32 i = 0; i < type->nBaseTypes; ++i) { + n += getVtableCount(type->ppBaseTypes[i]); + } + return std::max< sal_Int32 >(n, 1); +} + +/** + * Maps a local member index to a local function index. + * + *

Local members/functions are those not inherited from any base + * types. The number of functions is potentially larger than the + * number of members, as each read–write attribute member counts + * as two functions.

+ * + * @param type a non-null pointer to an interface type description + * @param localMember a local member index, relative to the given interface type + * @return the local function index corresponding to the given local member + * index, relative to the given interface type + */ +sal_Int32 mapLocalMemberToLocalFunction( + typelib_InterfaceTypeDescription * type, sal_Int32 localMember) +{ + typelib_typedescription_complete( + reinterpret_cast< typelib_TypeDescription ** >(&type)); + sal_Int32 localMemberOffset = type->nAllMembers - type->nMembers; + sal_Int32 localFunctionOffset = type->nMapFunctionIndexToMemberIndex + - bridges::cpp_uno::shared::getLocalFunctions(type); + return type->pMapMemberIndexToFunctionIndex[localMemberOffset + localMember] + - localFunctionOffset; +} + +// Since on Solaris we compile with --instances=static, getVtableSlot cannot be +// a template function, with explicit instantiates for +// T = typelib_InterfaceAttributeTypeDescription and +// T = typelib_InterfaceMethodTypeDescription in this file; hence, there are two +// overloaded versions of getVtableSlot that both delegate to this template +// function: +template< typename T > bridges::cpp_uno::shared::VtableSlot doGetVtableSlot( + T const * ifcMember) +{ + bridges::cpp_uno::shared::VtableSlot slot; + slot.offset = 0; + T * member = const_cast< T * >(ifcMember); + while (member->pBaseRef != 0) { + assert(member->nIndex < member->pInterface->nBaseTypes); + for (sal_Int32 i = 0; i < member->nIndex; ++i) { + slot.offset += getVtableCount(member->pInterface->ppBaseTypes[i]); + } + typelib_TypeDescription * desc = nullptr; + typelib_typedescriptionreference_getDescription( + &desc, member->pBaseRef); + assert( + desc != nullptr && desc->eTypeClass == member->aBase.aBase.eTypeClass); + if (member != ifcMember) { + typelib_typedescription_release(&member->aBase.aBase); + } + member = reinterpret_cast< T * >(desc); + } + slot.index + = bridges::cpp_uno::shared::getPrimaryFunctions( + member->pInterface->pBaseTypeDescription) + + mapLocalMemberToLocalFunction(member->pInterface, member->nIndex); + if (member != ifcMember) { + typelib_typedescription_release(&member->aBase.aBase); + } + return slot; +} + +} + +namespace bridges::cpp_uno::shared { + +sal_Int32 getLocalFunctions(typelib_InterfaceTypeDescription const * type) { + return type->nMembers == 0 + ? 0 + : (type->nMapFunctionIndexToMemberIndex + - type->pMapMemberIndexToFunctionIndex[ + type->nAllMembers - type->nMembers]); +} + +sal_Int32 getPrimaryFunctions(typelib_InterfaceTypeDescription * type) { + sal_Int32 n = 0; + for (; type != nullptr; type = type->pBaseTypeDescription) { + typelib_typedescription_complete( + reinterpret_cast< typelib_TypeDescription ** >(&type)); + n += getLocalFunctions(type); + } + return n; +} + +VtableSlot getVtableSlot( + typelib_InterfaceAttributeTypeDescription const * ifcMember) +{ + return doGetVtableSlot(ifcMember); +} + +VtableSlot getVtableSlot( + typelib_InterfaceMethodTypeDescription const * ifcMember) +{ + return doGetVtableSlot(ifcMember); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit v1.2.3