/* -*- 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 . */ #ifdef DIAG #define CONTEXT_DIAG #endif #if OSL_DEBUG_LEVEL > 0 #include #endif #include #ifdef CONTEXT_DIAG #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SMGR_SINGLETON "/singletons/com.sun.star.lang.theServiceManager" #define TDMGR_SINGLETON "/singletons/com.sun.star.reflection.theTypeDescriptionManager" #define AC_SINGLETON "/singletons/com.sun.star.security.theAccessController" using namespace ::osl; using namespace ::com::sun::star::uno; using namespace ::com::sun::star; using rtl::OUString; using rtl::OUStringBuffer; using rtl::OUStringHash; namespace cppu { #ifdef CONTEXT_DIAG static OUString val2str( void const * pVal, typelib_TypeDescriptionReference * pTypeRef ) { OSL_ASSERT( pVal ); if (pTypeRef->eTypeClass == typelib_TypeClass_VOID) return "void"; OUStringBuffer buf( 64 ); buf.append( "(" + pTypeRef->pTypeName + ")" ); switch (pTypeRef->eTypeClass) { case typelib_TypeClass_INTERFACE: buf.append( "0x" ); buf.append( (sal_Int64)*(void **)pVal, 16 ); break; case typelib_TypeClass_STRUCT: case typelib_TypeClass_EXCEPTION: { buf.append( "{ " ); typelib_TypeDescription * pTypeDescr = 0; ::typelib_typedescriptionreference_getDescription( &pTypeDescr, pTypeRef ); OSL_ASSERT( pTypeDescr ); if (! pTypeDescr->bComplete) ::typelib_typedescription_complete( &pTypeDescr ); typelib_CompoundTypeDescription * pCompType = (typelib_CompoundTypeDescription *)pTypeDescr; sal_Int32 nDescr = pCompType->nMembers; if (pCompType->pBaseTypeDescription) { buf.append( val2str( pVal, ((typelib_TypeDescription *)pCompType->pBaseTypeDescription)->pWeakRef ) ); if (nDescr) buf.append( ", " ); } typelib_TypeDescriptionReference ** ppTypeRefs = pCompType->ppTypeRefs; sal_Int32 * pMemberOffsets = pCompType->pMemberOffsets; rtl_uString ** ppMemberNames = pCompType->ppMemberNames; for ( sal_Int32 nPos = 0; nPos < nDescr; ++nPos ) { buf.append( ppMemberNames[ nPos ] ); buf.append( " = " ); typelib_TypeDescription * pMemberType = 0; TYPELIB_DANGER_GET( &pMemberType, ppTypeRefs[ nPos ] ); buf.append( val2str( (char *)pVal + pMemberOffsets[ nPos ], pMemberType->pWeakRef ) ); TYPELIB_DANGER_RELEASE( pMemberType ); if (nPos < (nDescr -1)) buf.append( ", " ); } ::typelib_typedescription_release( pTypeDescr ); buf.append( " }" ); break; } case typelib_TypeClass_SEQUENCE: { typelib_TypeDescription * pTypeDescr = 0; TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef ); uno_Sequence * pSequence = *(uno_Sequence **)pVal; typelib_TypeDescription * pElementTypeDescr = 0; TYPELIB_DANGER_GET( &pElementTypeDescr, ((typelib_IndirectTypeDescription *)pTypeDescr)->pType ); sal_Int32 nElementSize = pElementTypeDescr->nSize; sal_Int32 nElements = pSequence->nElements; if (nElements) { buf.append( "{ " ); char * pElements = pSequence->elements; for ( sal_Int32 nPos = 0; nPos < nElements; ++nPos ) { buf.append( val2str( pElements + (nElementSize * nPos), pElementTypeDescr->pWeakRef ) ); if (nPos < (nElements -1)) buf.append( ", " ); } buf.append( " }" ); } else { buf.append( "{}" ); } TYPELIB_DANGER_RELEASE( pElementTypeDescr ); TYPELIB_DANGER_RELEASE( pTypeDescr ); break; } case typelib_TypeClass_ANY: buf.append( "{ " ); buf.append( val2str( ((uno_Any *)pVal)->pData, ((uno_Any *)pVal)->pType ) ); buf.append( " }" ); break; case typelib_TypeClass_TYPE: buf.append( (*(typelib_TypeDescriptionReference **)pVal)->pTypeName ); break; case typelib_TypeClass_STRING: buf.append( '\"' ); buf.append( *(rtl_uString **)pVal ); buf.append( '\"' ); break; case typelib_TypeClass_ENUM: { typelib_TypeDescription * pTypeDescr = 0; ::typelib_typedescriptionreference_getDescription( &pTypeDescr, pTypeRef ); OSL_ASSERT( pTypeDescr ); if (! pTypeDescr->bComplete) ::typelib_typedescription_complete( &pTypeDescr ); sal_Int32 * pValues = ((typelib_EnumTypeDescription *)pTypeDescr)->pEnumValues; sal_Int32 nPos = ((typelib_EnumTypeDescription *)pTypeDescr)->nEnumValues; while (nPos--) { if (pValues[ nPos ] == *(sal_Int32 *)pVal) break; } if (nPos >= 0) buf.append( ((typelib_EnumTypeDescription *)pTypeDescr)->ppEnumNames[ nPos ] ); else buf.append( '?' ); ::typelib_typedescription_release( pTypeDescr ); break; } case typelib_TypeClass_BOOLEAN: if (*(sal_Bool *)pVal) buf.append( "true" ); else buf.append( "false" ); break; case typelib_TypeClass_CHAR: buf.append( '\'' ); buf.append( *(sal_Unicode *)pVal ); buf.append( '\'' ); break; case typelib_TypeClass_FLOAT: buf.append( *(float *)pVal ); break; case typelib_TypeClass_DOUBLE: buf.append( *(double *)pVal ); break; case typelib_TypeClass_BYTE: buf.append( "0x" ); buf.append( (sal_Int32)*(sal_Int8 *)pVal, 16 ); break; case typelib_TypeClass_SHORT: buf.append( "0x" ); buf.append( (sal_Int32)*(sal_Int16 *)pVal, 16 ); break; case typelib_TypeClass_UNSIGNED_SHORT: buf.append( "0x" ); buf.append( (sal_Int32)*(sal_uInt16 *)pVal, 16 ); break; case typelib_TypeClass_LONG: buf.append( "0x" ); buf.append( *(sal_Int32 *)pVal, 16 ); break; case typelib_TypeClass_UNSIGNED_LONG: buf.append( "0x" ); buf.append( (sal_Int64)*(sal_uInt32 *)pVal, 16 ); break; case typelib_TypeClass_HYPER: case typelib_TypeClass_UNSIGNED_HYPER: buf.append( "0x" ); #if defined(__GNUC__) && defined(SPARC) // I guess this really should check if there are strict alignment // requirements, not just "GCC on SPARC". { sal_Int64 aVal; *(sal_Int32 *)&aVal = *(sal_Int32 *)pVal; *((sal_Int32 *)&aVal +1)= *((sal_Int32 *)pVal +1); buf.append( aVal, 16 ); } #else buf.append( *(sal_Int64 *)pVal, 16 ); #endif break; default: buf.append( '?' ); } return buf.makeStringAndClear(); } static void dumpEntry( OUString const & key, Any const & value ) { OUString val( val2str( value.getValue(), value.getValueTypeRef() ) ); OString key_str( OUStringToOString( key, RTL_TEXTENCODING_ASCII_US ) ); OString val_str( OUStringToOString( val, RTL_TEXTENCODING_ASCII_US ) ); ::fprintf( stderr, "| %s = %s\n", key_str.getStr(), val_str.getStr() ); } #endif static inline void try_dispose( Reference< XInterface > const & xInstance ) { Reference< lang::XComponent > xComp( xInstance, UNO_QUERY ); if (xComp.is()) { xComp->dispose(); } } static inline void try_dispose( Reference< lang::XComponent > const & xComp ) { if (xComp.is()) { xComp->dispose(); } } class DisposingForwarder : public WeakImplHelper1< lang::XEventListener > { Reference< lang::XComponent > m_xTarget; explicit DisposingForwarder( Reference< lang::XComponent > const & xTarget ) : m_xTarget( xTarget ) { OSL_ASSERT( m_xTarget.is() ); } public: // listens at source for disposing, then disposes target static inline void listen( Reference< lang::XComponent > const & xSource, Reference< lang::XComponent > const & xTarget ); virtual void SAL_CALL disposing( lang::EventObject const & rSource ) throw (RuntimeException, std::exception) override; }; inline void DisposingForwarder::listen( Reference< lang::XComponent > const & xSource, Reference< lang::XComponent > const & xTarget ) { if (xSource.is()) { xSource->addEventListener( new DisposingForwarder( xTarget ) ); } } void DisposingForwarder::disposing( lang::EventObject const & ) throw (RuntimeException, std::exception) { m_xTarget->dispose(); m_xTarget.clear(); } struct MutexHolder { protected: Mutex m_mutex; }; class ComponentContext : private MutexHolder , public WeakComponentImplHelper< XComponentContext, container::XNameContainer > { protected: Reference< XComponentContext > m_xDelegate; struct ContextEntry { Any value; bool lateInit; inline ContextEntry( Any const & value_, bool lateInit_ ) : value( value_ ) , lateInit( lateInit_ ) {} }; typedef std::unordered_map< OUString, ContextEntry * , OUStringHash > t_map; t_map m_map; Reference< lang::XMultiComponentFactory > m_xSMgr; protected: Any lookupMap( OUString const & rName ); virtual void SAL_CALL disposing() override; public: ComponentContext( ContextEntry_Init const * pEntries, sal_Int32 nEntries, Reference< XComponentContext > const & xDelegate ); virtual ~ComponentContext(); // XComponentContext virtual Any SAL_CALL getValueByName( OUString const & rName ) throw (RuntimeException, std::exception) override; virtual Reference SAL_CALL getServiceManager() throw (RuntimeException, std::exception) override; // XNameContainer virtual void SAL_CALL insertByName( OUString const & name, Any const & element ) throw (lang::IllegalArgumentException, container::ElementExistException, lang::WrappedTargetException, RuntimeException, std::exception) override; virtual void SAL_CALL removeByName( OUString const & name ) throw (container::NoSuchElementException, lang::WrappedTargetException, RuntimeException, std::exception) override; // XNameReplace virtual void SAL_CALL replaceByName( OUString const & name, Any const & element ) throw (lang::IllegalArgumentException,container::NoSuchElementException, lang::WrappedTargetException, RuntimeException, std::exception) override; // XNameAccess virtual Any SAL_CALL getByName( OUString const & name ) throw (container::NoSuchElementException, lang::WrappedTargetException, RuntimeException, std::exception) override; virtual Sequence SAL_CALL getElementNames() throw (RuntimeException, std::exception) override; virtual sal_Bool SAL_CALL hasByName( OUString const & name ) throw (RuntimeException, std::exception) override; // XElementAccess virtual Type SAL_CALL getElementType() throw (RuntimeException, std::exception) override; virtual sal_Bool SAL_CALL hasElements() throw (RuntimeException, std::exception) override; }; // XNameContainer void ComponentContext::insertByName( OUString const & name, Any const & element ) throw (lang::IllegalArgumentException, container::ElementExistException, lang::WrappedTargetException, RuntimeException, std::exception) { t_map::mapped_type entry( new ContextEntry( element, /* lateInit_: */ name.startsWith( "/singletons/" ) && !element.hasValue() ) ); MutexGuard guard( m_mutex ); ::std::pair insertion( m_map.insert( t_map::value_type( name, entry ) ) ); if (! insertion.second) throw container::ElementExistException( "element already exists: " + name, static_cast(this) ); } void ComponentContext::removeByName( OUString const & name ) throw (container::NoSuchElementException, lang::WrappedTargetException, RuntimeException, std::exception) { MutexGuard guard( m_mutex ); t_map::iterator iFind( m_map.find( name ) ); if (iFind == m_map.end()) throw container::NoSuchElementException( "no such element: " + name, static_cast(this) ); delete iFind->second; m_map.erase(iFind); } // XNameReplace void ComponentContext::replaceByName( OUString const & name, Any const & element ) throw (lang::IllegalArgumentException,container::NoSuchElementException, lang::WrappedTargetException, RuntimeException, std::exception) { MutexGuard guard( m_mutex ); t_map::const_iterator const iFind( m_map.find( name ) ); if (iFind == m_map.end()) throw container::NoSuchElementException( "no such element: " + name, static_cast(this) ); if (name.startsWith( "/singletons/" ) && !element.hasValue()) { iFind->second->value.clear(); iFind->second->lateInit = true; } else { iFind->second->value = element; iFind->second->lateInit = false; } } // XNameAccess Any ComponentContext::getByName( OUString const & name ) throw (container::NoSuchElementException, lang::WrappedTargetException, RuntimeException, std::exception) { return getValueByName( name ); } Sequence ComponentContext::getElementNames() throw (RuntimeException, std::exception) { MutexGuard guard( m_mutex ); Sequence ret( m_map.size() ); OUString * pret = ret.getArray(); sal_Int32 pos = 0; t_map::const_iterator iPos( m_map.begin() ); t_map::const_iterator const iEnd( m_map.end() ); for ( ; iPos != iEnd; ++iPos ) pret[pos++] = iPos->first; return ret; } sal_Bool ComponentContext::hasByName( OUString const & name ) throw (RuntimeException, std::exception) { MutexGuard guard( m_mutex ); return m_map.find( name ) != m_map.end(); } // XElementAccess Type ComponentContext::getElementType() throw (RuntimeException, std::exception) { return cppu::UnoType::get(); } sal_Bool ComponentContext::hasElements() throw (RuntimeException, std::exception) { MutexGuard guard( m_mutex ); return ! m_map.empty(); } Any ComponentContext::lookupMap( OUString const & rName ) { #ifdef CONTEXT_DIAG if ( rName == "dump_maps" ) { ::fprintf( stderr, ">>> dumping out ComponentContext %p m_map:\n", this ); typedef ::std::map< OUString, ContextEntry * > t_sorted; // sorted map t_sorted sorted; for ( t_map::const_iterator iPos( m_map.begin() ); iPos != m_map.end(); ++iPos ) { sorted[ iPos->first ] = iPos->second; } { for ( t_sorted::const_iterator iPos( sorted.begin() ); iPos != sorted.end(); ++iPos ) { dumpEntry( iPos->first, iPos->second->value ); } } return Any(); } #endif ResettableMutexGuard guard( m_mutex ); t_map::const_iterator iFind( m_map.find( rName ) ); if (iFind == m_map.end()) return Any(); t_map::mapped_type pEntry = iFind->second; if (! pEntry->lateInit) return pEntry->value; // late init singleton entry Reference< XInterface > xInstance; guard.clear(); try { Any usesService( getValueByName( rName + "/service" ) ); Any args_( getValueByName( rName + "/arguments" ) ); Sequence args; if (args_.hasValue() && !(args_ >>= args)) { args.realloc( 1 ); args[ 0 ] = args_; } Reference< lang::XSingleComponentFactory > xFac; if (usesService >>= xFac) // try via factory { xInstance = args.getLength() ? xFac->createInstanceWithArgumentsAndContext( args, this ) : xFac->createInstanceWithContext( this ); } else { Reference< lang::XSingleServiceFactory > xFac2; if (usesService >>= xFac2) { // try via old XSingleServiceFactory #if OSL_DEBUG_LEVEL > 0 ::fprintf( stderr, "### omitting context for service instantiation!\n" ); #endif xInstance = args.getLength() ? xFac2->createInstanceWithArguments( args ) : xFac2->createInstance(); } else if (m_xSMgr.is()) // optionally service name { OUString serviceName; if ((usesService >>= serviceName) && !serviceName.isEmpty()) { xInstance = args.getLength() ? m_xSMgr->createInstanceWithArgumentsAndContext( serviceName, args, this ) : m_xSMgr->createInstanceWithContext( serviceName, this ); } } } } catch (const RuntimeException &) { throw; } catch (const Exception & exc) { SAL_WARN( "cppuhelper", "exception occurred raising singleton \"" << rName << "\": " << exc.Message); } SAL_WARN_IF(!xInstance.is(), "cppuhelper", "no service object raising singleton " << rName); Any ret; guard.reset(); iFind = m_map.find( rName ); if (iFind != m_map.end()) { pEntry = iFind->second; if (pEntry->lateInit) { pEntry->value <<= xInstance; pEntry->lateInit = false; return pEntry->value; } else ret = pEntry->value; } guard.clear(); if (ret != xInstance) { try_dispose( xInstance ); } return ret; } Any ComponentContext::getValueByName( OUString const & rName ) throw (RuntimeException, std::exception) { // to determine the root context: if ( rName == "_root" ) { if (m_xDelegate.is()) return m_xDelegate->getValueByName( rName ); else return makeAny( Reference(this) ); } Any ret( lookupMap( rName ) ); if (!ret.hasValue() && m_xDelegate.is()) { return m_xDelegate->getValueByName( rName ); } return ret; } Reference< lang::XMultiComponentFactory > ComponentContext::getServiceManager() throw (RuntimeException, std::exception) { if ( !m_xSMgr.is() ) { throw DeploymentException( "null component context service manager", static_cast(this) ); } return m_xSMgr; } ComponentContext::~ComponentContext() { #ifdef CONTEXT_DIAG ::fprintf( stderr, "> destructed context %p\n", this ); #endif t_map::const_iterator iPos( m_map.begin() ); t_map::const_iterator const iEnd( m_map.end() ); for ( ; iPos != iEnd; ++iPos ) delete iPos->second; m_map.clear(); } void ComponentContext::disposing() { #ifdef CONTEXT_DIAG ::fprintf( stderr, "> disposing context %p\n", this ); #endif Reference< lang::XComponent > xTDMgr, xAC; // to be disposed separately // dispose all context objects t_map::const_iterator iPos( m_map.begin() ); t_map::const_iterator const iEnd( m_map.end() ); for ( ; iPos != iEnd; ++iPos ) { t_map::mapped_type pEntry = iPos->second; // service manager disposed separately if (!m_xSMgr.is() || !iPos->first.startsWith( SMGR_SINGLETON )) { if (pEntry->lateInit) { // late init MutexGuard guard( m_mutex ); if (pEntry->lateInit) { pEntry->value.clear(); // release factory pEntry->lateInit = false; continue; } } Reference< lang::XComponent > xComp; pEntry->value >>= xComp; if (xComp.is()) { if ( iPos->first == TDMGR_SINGLETON ) { xTDMgr = xComp; } else if ( iPos->first == AC_SINGLETON ) { xAC = xComp; } else // dispose immediately { xComp->dispose(); } } } } // dispose service manager try_dispose( m_xSMgr ); m_xSMgr.clear(); // dispose ac try_dispose( xAC ); // dispose tdmgr; revokes callback from cppu runtime try_dispose( xTDMgr ); iPos = m_map.begin(); for ( ; iPos != iEnd; ++iPos ) delete iPos->second; m_map.clear(); // Hack to terminate any JNI bridge's AsynchronousFinalizer thread (as JNI // proxies get finalized with arbitrary delay, so the bridge typically does // not dispose itself early enough before the process exits): uno_Environment ** envs; sal_Int32 envCount; uno_getRegisteredEnvironments( &envs, &envCount, &rtl_allocateMemory, OUString("java").pData); assert(envCount >= 0); assert(envCount == 0 || envs != nullptr); if (envs) { for (sal_Int32 i = 0; i != envCount; ++i) { assert(envs[i] != nullptr); assert(envs[i]->dispose != nullptr); (*envs[i]->dispose)(envs[i]); } rtl_freeMemory(envs); } } ComponentContext::ComponentContext( ContextEntry_Init const * pEntries, sal_Int32 nEntries, Reference< XComponentContext > const & xDelegate ) : WeakComponentImplHelper( m_mutex ), m_xDelegate( xDelegate ) { for ( sal_Int32 nPos = 0; nPos < nEntries; ++nPos ) { ContextEntry_Init const & rEntry = pEntries[ nPos ]; if ( rEntry.name == SMGR_SINGLETON ) { rEntry.value >>= m_xSMgr; } if (rEntry.bLateInitService) { // singleton entry m_map[ rEntry.name ] = new ContextEntry( Any(), true ); // service m_map[ rEntry.name + "/service" ] = new ContextEntry( rEntry.value, false ); // initial-arguments are provided as optional context entry } else { // only value, no late init factory nor string m_map[ rEntry.name ] = new ContextEntry( rEntry.value, false ); } } if (!m_xSMgr.is() && m_xDelegate.is()) { // wrap delegate's smgr XPropertySet into new smgr Reference< lang::XMultiComponentFactory > xMgr( m_xDelegate->getServiceManager() ); if (xMgr.is()) { osl_atomic_increment( &m_refCount ); try { // create new smgr based on delegate's one m_xSMgr.set( xMgr->createInstanceWithContext( "com.sun.star.comp.stoc.OServiceManagerWrapper", xDelegate ), UNO_QUERY ); // patch DefaultContext property of new one Reference< beans::XPropertySet > xProps( m_xSMgr, UNO_QUERY ); OSL_ASSERT( xProps.is() ); if (xProps.is()) { Reference< XComponentContext > xThis( this ); xProps->setPropertyValue( "DefaultContext", makeAny( xThis ) ); } } catch (...) { osl_atomic_decrement( &m_refCount ); throw; } osl_atomic_decrement( &m_refCount ); OSL_ASSERT( m_xSMgr.is() ); } } } extern "C" { static void s_createComponentContext_v(va_list * pParam) { ContextEntry_Init const * pEntries = va_arg(*pParam, ContextEntry_Init const *); sal_Int32 nEntries = va_arg(*pParam, sal_Int32); XComponentContext * pDelegatee = va_arg(*pParam, XComponentContext *); void ** ppContext = va_arg(*pParam, void **); uno::Mapping * pTarget2curr = va_arg(*pParam, uno::Mapping *); Reference xDelegate(pDelegatee, SAL_NO_ACQUIRE); Reference xContext; if (nEntries > 0) { try { ComponentContext * p = new ComponentContext( pEntries, nEntries, xDelegate ); xContext.set(p); // listen delegate for disposing, to dispose this (wrapping) context first. DisposingForwarder::listen( Reference< lang::XComponent >::query( xDelegate ), p ); } catch (Exception & exc) { (void) exc; // avoid warning about unused variable OSL_FAIL( OUStringToOString( exc.Message, RTL_TEXTENCODING_ASCII_US ).getStr() ); xContext.clear(); } } else { xContext = xDelegate; } *ppContext = pTarget2curr->mapInterface(xContext.get(), cppu::UnoType::get()); }} Reference< XComponentContext > SAL_CALL createComponentContext( ContextEntry_Init const * pEntries, sal_Int32 nEntries, Reference< XComponentContext > const & xDelegate ) { uno::Environment curr_env(Environment::getCurrent()); uno::Environment source_env(CPPU_CURRENT_LANGUAGE_BINDING_NAME); uno::Mapping curr2source(curr_env, source_env); uno::Mapping source2curr(source_env, curr_env); std::unique_ptr mapped_entries(new ContextEntry_Init[nEntries]); for (sal_Int32 nPos = 0; nPos < nEntries; ++ nPos) { mapped_entries[nPos].bLateInitService = pEntries[nPos].bLateInitService; mapped_entries[nPos].name = pEntries[nPos].name; uno_type_any_constructAndConvert(&mapped_entries[nPos].value, const_cast(pEntries[nPos].value.getValue()), pEntries[nPos].value.getValueTypeRef(), curr2source.get()); } void * mapped_delegate = curr2source.mapInterface(xDelegate.get(), cppu::UnoType::get()); XComponentContext * pXComponentContext = nullptr; source_env.invoke(s_createComponentContext_v, mapped_entries.get(), nEntries, mapped_delegate, &pXComponentContext, &source2curr); mapped_entries.reset(); return Reference(pXComponentContext, SAL_NO_ACQUIRE); } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */