Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quellcode-Bibliothek lbmap.cxx   Sprache: C

 
/* -*- 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 DISABLE_DYNLOADING
#include <config_java.h>
#endif

#include "IdentityMapping.hxx"

#include <cassert>
#include <mutex>
#include <set>
#include <unordered_map>
#include <utility>

#include <rtl/ustring.hxx>
#include <rtl/ustrbuf.hxx>
#include <osl/module.hxx>
#include <osl/diagnose.h>
#include <osl/mutex.hxx>
#include <osl/interlck.h>
#include <sal/log.hxx>

#include <uno/dispatcher.h>
#include <uno/mapping.h>
#include <uno/lbnames.h>
#include <uno/environment.hxx>

#include <typelib/typedescription.h>

#include <cppu/EnvDcp.hxx>
#include "cascade_mapping.hxx"
#include "loadmodule.hxx"

using namespace osl;
using namespace com::sun::star::uno;

namespace cppu
{

namespace {

class Mapping
{
    uno_Mapping * _pMapping;

public:
    inline explicit Mapping( uno_Mapping * pMapping = nullptr );
    inline Mapping( const Mapping & rMapping );
    Mapping(Mapping && other) noexcept : _pMapping(other._pMapping)
    { other._pMapping = nullptr; }
    inline ~Mapping();
    inline Mapping & operator = ( uno_Mapping * pMapping );
    // move variant is sufficient
    Mapping & operator = ( const Mapping & rMapping ) = delete;
    Mapping & operator =(Mapping && other) noexcept {
        if (_pMapping != nullptr) {
            (*_pMapping->release)(_pMapping);
        }
        _pMapping = other._pMapping;
        other._pMapping = nullptr;
        return *this;
    }
    uno_Mapping * get() const
        { return _pMapping; }
    bool is() const
        { return (_pMapping != nullptr); }
};

}

inline Mapping::Mapping( uno_Mapping * pMapping )
    : _pMapping( pMapping )
{
    if (_pMapping)
        (*_pMapping->acquire)( _pMapping );
}

inline Mapping::Mapping( const Mapping & rMapping )
    : _pMapping( rMapping._pMapping )
{
    if (_pMapping)
        (*_pMapping->acquire)( _pMapping );
}

inline Mapping::~Mapping()
{
    if (_pMapping)
        (*_pMapping->release)( _pMapping );
}

inline Mapping & Mapping::operator = ( uno_Mapping * pMapping )
{
    if (pMapping)
        (*pMapping->acquire)( pMapping );
    if (_pMapping)
        (*_pMapping->release)( _pMapping );
    _pMapping = pMapping;
    return *this;
}

namespace {

struct MappingEntry
{
    sal_Int32           nRef;
    uno_Mapping *       pMapping;
    uno_freeMappingFunc freeMapping;
    OUString            aMappingName;

    MappingEntry(
        uno_Mapping * pMapping_, uno_freeMappingFunc freeMapping_,
        OUString aMappingName_ )
        : nRef( 1 )
        , pMapping( pMapping_ )
        , freeMapping( freeMapping_ )
        , aMappingName(std::move( aMappingName_ ))
        {}
};

struct FctPtrHash
{
    size_t operator()( uno_Mapping * pKey ) const
        { return reinterpret_cast<size_t>(pKey); }
};

}

typedef std::unordered_map<
    OUString, MappingEntry * > t_OUString2Entry;
typedef std::unordered_map<
    uno_Mapping *, MappingEntry *, FctPtrHash > t_Mapping2Entry;

namespace {

struct MappingsData
{
    Mutex               aMappingsMutex;
    t_OUString2Entry    aName2Entry;
    t_Mapping2Entry     aMapping2Entry;

    std::mutex          aCallbacksMutex;
    std::set< uno_getMappingFunc >
                        aCallbacks;

    std::mutex          aNegativeLibsMutex;
    std::set<OUString>       aNegativeLibs;
};

}

static MappingsData & getMappingsData()
{
    //TODO  This memory is leaked; see #i63473# for when this should be
    // changed again:
    static MappingsData * s_p(new MappingsData);

    return *s_p;
}

namespace {

/**
 * This class mediates two different mapping via uno, e.g. form any language to uno,
 * then from uno to any other language.
 */

struct uno_Mediate_Mapping : public uno_Mapping
{
    sal_Int32   nRef;

    Environment aFrom;
    Environment aTo;

    Mapping     aFrom2Uno;
    Mapping     aUno2To;

    OUString    aAddPurpose;

    uno_Mediate_Mapping(
        Environment aFrom_, Environment aTo_,
        Mapping aFrom2Uno_, Mapping aUno2To_,
        OUString aAddPurpose );
};

}

extern "C"
{

static void mediate_free( uno_Mapping * pMapping )
{
    delete static_cast< uno_Mediate_Mapping * >( pMapping );
}

static void mediate_acquire( uno_Mapping * pMapping )
{
    if (1 == osl_atomic_increment(
        & static_cast< uno_Mediate_Mapping * >( pMapping )->nRef ))
    {
        uno_registerMapping(
            &pMapping, mediate_free,
            static_cast< uno_Mediate_Mapping * >( pMapping )->aFrom.get(),
            static_cast< uno_Mediate_Mapping * >( pMapping )->aTo.get(),
            static_cast< uno_Mediate_Mapping * >( pMapping )->aAddPurpose.pData );
    }
}

static void mediate_release( uno_Mapping * pMapping )
{
    if (! osl_atomic_decrement(
        & static_cast< uno_Mediate_Mapping * >( pMapping )->nRef ))
    {
        uno_revokeMapping( pMapping );
    }
}

static void mediate_mapInterface(
    uno_Mapping * pMapping,
    void ** ppOut, void * pInterface,
    typelib_InterfaceTypeDescription * pInterfaceTypeDescr )
{
    OSL_ENSURE( pMapping && ppOut, "### null ptr!" );
    if (!(pMapping && ppOut))
        return;

    uno_Mediate_Mapping * that = static_cast< uno_Mediate_Mapping * >( pMapping );
    uno_Mapping * pFrom2Uno = that->aFrom2Uno.get();

    uno_Interface * pUnoI = nullptr;
    (*pFrom2Uno->mapInterface)( pFrom2Uno, reinterpret_cast<void **>(&pUnoI), pInterface, pInterfaceTypeDescr );
    if (nullptr == pUnoI)
    {
        void * pOut = *ppOut;
        if (nullptr != pOut)
        {
            uno_ExtEnvironment * pTo = that->aTo.get()->pExtEnv;
            OSL_ENSURE( nullptr != pTo, "### cannot release out interface: leaking!" );
            if (nullptr != pTo)
                (*pTo->releaseInterface)( pTo, pOut );
            *ppOut = nullptr; // set to 0 anyway, because mapping was not successful!
        }
    }
    else
    {
        uno_Mapping * pUno2To = that->aUno2To.get();
        (*pUno2To->mapInterface)( pUno2To, ppOut, pUnoI, pInterfaceTypeDescr );
        (*pUnoI->release)( pUnoI );
    }
}
}

uno_Mediate_Mapping::uno_Mediate_Mapping(
    Environment aFrom_, Environment aTo_,
    Mapping aFrom2Uno_, Mapping aUno2To_,
    OUString aAddPurpose_ )
    : nRef( 1 )
    , aFrom(std::move( aFrom_ ))
    , aTo(std::move( aTo_ ))
    , aFrom2Uno(std::move( aFrom2Uno_ ))
    , aUno2To(std::move( aUno2To_ ))
    , aAddPurpose(std::move( aAddPurpose_ ))
{
    uno_Mapping::acquire        = mediate_acquire;
    uno_Mapping::release        = mediate_release;
    uno_Mapping::mapInterface   = mediate_mapInterface;
}


static OUString getMappingName(
    const Environment & rFrom, const Environment & rTo, std::u16string_view rAddPurpose )
{
    return
        OUString::Concat(rAddPurpose)
        + ";"
        + rFrom.getTypeName()
        + "["
        + OUString::number( reinterpret_cast< sal_IntPtr >(rFrom.get()), 16 )
        + "];"
        + rTo.getTypeName()
        + "["
        + OUString::number( reinterpret_cast< sal_IntPtr >(rTo.get()), 16 )
        + "]";
}

static OUString getBridgeName(
    const Environment & rFrom, const Environment & rTo, std::u16string_view rAddPurpose )
{
    OUStringBuffer aBridgeName( 16 );
    if (!rAddPurpose.empty())
    {
        aBridgeName.append( OUString::Concat(rAddPurpose) + "_" );
    }
    aBridgeName.append(
        EnvDcp::getTypeName(rFrom.getTypeName())
        + "_"
        + EnvDcp::getTypeName(rTo.getTypeName()) );
    return aBridgeName.makeStringAndClear();
}

#ifndef DISABLE_DYNLOADING

static void setNegativeBridge( const OUString & rBridgeName )
{
    MappingsData & rData = getMappingsData();
    std::scoped_lock aGuard( rData.aNegativeLibsMutex );
    rData.aNegativeLibs.insert( rBridgeName );
}

#endif

#ifdef DISABLE_DYNLOADING

static uno_ext_getMappingFunc selectMapFunc( const OUString & rBridgeName )
{
    if (rBridgeName.equalsAscii( CPPU_CURRENT_LANGUAGE_BINDING_NAME "_uno" ))
        return CPPU_ENV_uno_ext_getMapping;
#if HAVE_FEATURE_JAVA
    if (rBridgeName.equalsAscii( "java" "_uno" ))
        return java_uno_ext_getMapping;
#endif

#if 0
    // I don't think the affine or log bridges will be needed on any
    // DISABLE_DYNLOADING platform (iOS at least, possibly Android), but if
    // somebody wants to experiment, need to find out then whether these are
    // needed.
    if (rBridgeName.equalsAscii( "affine_uno_uno" ))
        return affine_uno_uno_ext_getMapping;
    if (rBridgeName.equalsAscii( "log_uno_uno" ))
        return log_uno_uno_ext_getMapping;
#endif
    return 0;
}

#else

static bool loadModule(osl::Module & rModule, const OUString & rBridgeName)
{
    bool bNeg;
    {
    MappingsData & rData = getMappingsData();
    std::scoped_lock aGuard( rData.aNegativeLibsMutex );
    const auto iFind( rData.aNegativeLibs.find( rBridgeName ) );
    bNeg = (iFind != rData.aNegativeLibs.end());
    }

    if (!bNeg)
    {
        bool bModule;
        try {
            bModule = cppu::detail::loadModule(rModule, rBridgeName);
        }
        catch(...) {
            // convert throw to return false
            bModule = false;
        }

        if (bModule)
            return true;

        setNegativeBridge( rBridgeName ); // no load again
    }
    return false;
}

#endif


static Mapping loadExternalMapping(
    const Environment & rFrom, const Environment & rTo, const OUString & rAddPurpose )
{
    OSL_ASSERT( rFrom.is() && rTo.is() );
    if (rFrom.is() && rTo.is())
    {
#ifdef DISABLE_DYNLOADING
        OUString aName;
        uno_ext_getMappingFunc fpGetMapFunc = 0;

        if (EnvDcp::getTypeName(rFrom.getTypeName()) == UNO_LB_UNO)
        {
            aName = getBridgeName( rTo, rFrom, rAddPurpose );
            fpGetMapFunc = selectMapFunc( aName );
        }
        if (! fpGetMapFunc)
        {
            aName = getBridgeName( rFrom, rTo, rAddPurpose );
            fpGetMapFunc = selectMapFunc( aName );
        }
        if (! fpGetMapFunc)
        {
            aName = getBridgeName( rTo, rFrom, rAddPurpose );
            fpGetMapFunc = selectMapFunc( aName );
        }

        if (! fpGetMapFunc)
        {
            SAL_INFO("cppu""Could not find mapfunc for " << aName);
            return Mapping();
        }

        if (fpGetMapFunc)
        {
            Mapping aExt;
            (*fpGetMapFunc)( (uno_Mapping **)&aExt, rFrom.get(), rTo.get() );
            OSL_ASSERT( aExt.is() );
            if (aExt.is())
                return aExt;
            SAL_INFO("cppu""Could not load external mapping for " << aName);
        }
#else
        // find proper lib
        osl::Module aModule;
        bool bModule(false);
        OUString aName;

        if ( EnvDcp::getTypeName(rFrom.getTypeName()) == UNO_LB_UNO )
        {
            aName = getBridgeName( rTo, rFrom, rAddPurpose );
            bModule = loadModule( aModule, aName );
        }
        if (!bModule)
        {
            aName = getBridgeName( rFrom, rTo, rAddPurpose );
            bModule = loadModule( aModule, aName );
        }
        if (!bModule)
        {
            aName = getBridgeName( rTo, rFrom, rAddPurpose );
            bModule = loadModule( aModule, aName );
        }

        if (bModule)
        {
            uno_ext_getMappingFunc fpGetMapFunc =
                reinterpret_cast<uno_ext_getMappingFunc>(aModule.getSymbol( u"" UNO_EXT_GETMAPPING ""_ustr ));

            if (fpGetMapFunc)
            {
                Mapping aExt;
                (*fpGetMapFunc)( reinterpret_cast<uno_Mapping **>(&aExt), rFrom.get(), rTo.get() );
                OSL_ASSERT( aExt.is() );
                if (aExt.is())
                {
                    aModule.release();
                    return aExt;
                }
            }
            aModule.unload();
            setNegativeBridge( aName );
        }
#endif
    }
    return Mapping();
}


static Mapping getDirectMapping(
    const Environment & rFrom, const Environment & rTo, const OUString & rAddPurpose = OUString() )

{
    OSL_ASSERT( rFrom.is() && rTo.is() );
    if (rFrom.is() && rTo.is())
    {
        MappingsData & rData = getMappingsData();
        ClearableMutexGuard aGuard( rData.aMappingsMutex );

        // try to find registered mapping
        const t_OUString2Entry::const_iterator iFind( rData.aName2Entry.find(
            getMappingName( rFrom, rTo, rAddPurpose ) ) );

        if (iFind == rData.aName2Entry.end())
        {
            aGuard.clear();
            return loadExternalMapping( rFrom, rTo, rAddPurpose );
        }
        return Mapping( (*iFind).second->pMapping );
    }
    return Mapping();
}


static Mapping createMediateMapping(
    const Environment & rFrom, const Environment & rTo,
    const Mapping & rFrom2Uno, const Mapping & rUno2To,
    const OUString & rAddPurpose )
{
    uno_Mapping * pRet = new uno_Mediate_Mapping(
        rFrom, rTo, rFrom2Uno, rUno2To, rAddPurpose ); // ref count initially 1
    uno_registerMapping(
        &pRet, mediate_free, rFrom.get(), rTo.get(), rAddPurpose.pData );
    Mapping aRet( pRet );
    (*pRet->release)( pRet );
    return aRet;
}

static Mapping getMediateMapping(
    const Environment & rFrom, const Environment & rTo, const OUString & rAddPurpose )
{
    Environment aUno;
    Mapping aUno2To;

    // backwards: from dest to source of mapping chain

    // connect to uno
    OUString aUnoEnvTypeName( u"" UNO_LB_UNO ""_ustr );
    if (rTo.getTypeName() == aUnoEnvTypeName) // to is uno
    {
        aUno = rTo;
        // no Uno2To mapping necessary
    }
    else
    {
        // get registered uno env
        ::uno_getEnvironment( reinterpret_cast<uno_Environment **>(&aUno), aUnoEnvTypeName.pData, nullptr );

        aUno2To = getDirectMapping( aUno, rTo );
        // : uno <-> to
        if (! aUno2To.is())
            return Mapping();
    }

    // connect to uno
    if (!rAddPurpose.isEmpty()) // insert purpose mapping between new ano_uno <-> uno
    {
        // create anonymous uno env
        Environment aAnUno;
        ::uno_createEnvironment( reinterpret_cast<uno_Environment **>(&aAnUno), aUnoEnvTypeName.pData, nullptr );

        Mapping aAnUno2Uno( getDirectMapping( aAnUno, aUno, rAddPurpose ) );
        if (! aAnUno2Uno.is())
            return Mapping();

        if (aUno2To.is()) // to is not uno
        {
            // create another purposed mediate mapping
            aUno2To = createMediateMapping( aAnUno, rTo, aAnUno2Uno, aUno2To, rAddPurpose );
            // : ano_uno <-> uno <-> to
        }
        else
        {
            aUno2To = std::move(aAnUno2Uno);
            // : ano_uno <-> to (i.e., uno)
        }
        aUno = std::move(aAnUno);
    }

    Mapping aFrom2Uno( getDirectMapping( rFrom, aUno ) );
    if (aFrom2Uno.is() && aUno2To.is())
    {
        return createMediateMapping( rFrom, rTo, aFrom2Uno, aUno2To, rAddPurpose );
        // : from <-> some uno ...
    }

    return Mapping();
}
}

using namespace ::cppu;

extern "C"
{

void SAL_CALL uno_getMapping(
    uno_Mapping ** ppMapping, uno_Environment * pFrom, uno_Environment * pTo,
    rtl_uString * pAddPurpose ) noexcept
{
    assert(ppMapping != nullptr);
    assert(pFrom != nullptr);
    assert(pTo != nullptr);
    if (*ppMapping)
    {
        (*(*ppMapping)->release)( *ppMapping );
        *ppMapping = nullptr;
    }

    Mapping aRet;
    Environment aFrom( pFrom ), aTo( pTo );

    OUString aAddPurpose;
    if (pAddPurpose)
        aAddPurpose = pAddPurpose;

    MappingsData & rData = getMappingsData();

    // try registered mapping
    {
    MutexGuard aGuard( rData.aMappingsMutex );
    const t_OUString2Entry::const_iterator iFind( rData.aName2Entry.find(
        getMappingName( aFrom, aTo, aAddPurpose ) ) );
    if (iFind != rData.aName2Entry.end())
        aRet = (*iFind).second->pMapping;
    }

    // See if an identity mapping does fit.
    if (!aRet.is() && pFrom == pTo && aAddPurpose.isEmpty())
        aRet = createIdentityMapping(pFrom);

    if (!aRet.is())
    {
        getCascadeMapping(ppMapping, pFrom, pTo, pAddPurpose);

        if (*ppMapping)
            return;

        // try callback chain
        {
            std::unique_lock aGuard(rData.aCallbacksMutex);
            for (const auto& rCallback : rData.aCallbacks)
            {
                (*rCallback)(ppMapping, pFrom, pTo, aAddPurpose.pData);
                if (*ppMapping)
                    return;
            }
        }

        aRet = loadExternalMapping( aFrom, aTo, aAddPurpose ); // direct try
        if (! aRet.is())
            aRet = getMediateMapping( aFrom, aTo, aAddPurpose ); // try via uno
    }

    if (aRet.is())
    {
        (*aRet.get()->acquire)( aRet.get() );
        *ppMapping = aRet.get();
    }
}

void SAL_CALL uno_getMappingByName(
    uno_Mapping ** ppMapping, rtl_uString * pFrom, rtl_uString * pTo,
    rtl_uString * pAddPurpose ) noexcept
{
    assert(ppMapping && pFrom && pTo && "### null ptr!");
    if (*ppMapping)
    {
        (*(*ppMapping)->release)( *ppMapping );
        *ppMapping = nullptr;
    }

    uno_Environment * pEFrom = nullptr;
    uno_getEnvironment( &pEFrom, pFrom, nullptr );
    OSL_ENSURE( pEFrom, "### cannot get source environment!" );
    if (pEFrom)
    {
        uno_Environment * pETo = nullptr;
        uno_getEnvironment( &pETo, pTo, nullptr );
        OSL_ENSURE( pETo, "### cannot get target environment!" );
        if (pETo)
        {
            ::uno_getMapping( ppMapping, pEFrom, pETo, pAddPurpose );
            (*pETo->release)( pETo );
        }
        (*pEFrom->release)( pEFrom );
    }
}


void SAL_CALL uno_registerMapping(
    uno_Mapping ** ppMapping, uno_freeMappingFunc freeMapping,
    uno_Environment * pFrom, uno_Environment * pTo, rtl_uString * pAddPurpose ) noexcept
{
    MappingsData & rData = getMappingsData();
    ClearableMutexGuard aGuard( rData.aMappingsMutex );

    const t_Mapping2Entry::const_iterator iFind( rData.aMapping2Entry.find( *ppMapping ) );
    if (iFind == rData.aMapping2Entry.end())
    {
        OUString aMappingName(
            getMappingName( pFrom, pTo, pAddPurpose ? OUString(pAddPurpose) : OUString() ) );
        SAL_INFO("cppu""> inserting new mapping: " << aMappingName);
        // count initially 1
        MappingEntry * pEntry = new MappingEntry( *ppMapping, freeMapping, aMappingName );
        rData.aName2Entry[ aMappingName ] = pEntry;
        rData.aMapping2Entry[ *ppMapping ] = pEntry;
    }
    else
    {
        MappingEntry * pEntry = (*iFind).second;
        ++pEntry->nRef;

        if (pEntry->pMapping != *ppMapping) // exchange mapping to be registered
        {
            (*pEntry->pMapping->acquire)( pEntry->pMapping );
            --pEntry->nRef; // correct count; kill mapping to be registered
            aGuard.clear();
            (*freeMapping)( *ppMapping );
            *ppMapping = pEntry->pMapping;
        }
    }
}

void SAL_CALL uno_revokeMapping(
    uno_Mapping * pMapping ) noexcept
{
    MappingsData & rData = getMappingsData();
    ClearableMutexGuard aGuard( rData.aMappingsMutex );

    const t_Mapping2Entry::const_iterator iFind( rData.aMapping2Entry.find( pMapping ) );
    assert(iFind != rData.aMapping2Entry.end() && "pMapping must be registered to be removed");
    MappingEntry * pEntry = (*iFind).second;
    if (! --pEntry->nRef)
    {
        rData.aMapping2Entry.erase( pEntry->pMapping );
        rData.aName2Entry.erase( pEntry->aMappingName );
        aGuard.clear();
        SAL_INFO("cppu""> revoking mapping " << pEntry->aMappingName);
        (*pEntry->freeMapping)( pEntry->pMapping );
        delete pEntry;
    }
}


void SAL_CALL uno_registerMappingCallback(
    uno_getMappingFunc pCallback ) noexcept
{
    OSL_ENSURE( pCallback, "### null ptr!" );
    MappingsData & rData = getMappingsData();
    std::unique_lock aGuard( rData.aCallbacksMutex );
    rData.aCallbacks.insert( pCallback );
}

void SAL_CALL uno_revokeMappingCallback(
    uno_getMappingFunc pCallback ) noexcept
{
    OSL_ENSURE( pCallback, "### null ptr!" );
    MappingsData & rData = getMappingsData();
    std::unique_lock aGuard( rData.aCallbacksMutex );
    rData.aCallbacks.erase( pCallback );
}
// extern "C"

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

98%


¤ Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.0.33Bemerkung:  ¤

*Bot Zugriff






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.