Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/bridges/source/jni_uno/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 18 kB image not shown  

Quelle  jni_bridge.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 .
 */


#include <sal/config.h>
#include <sal/log.hxx>

#include <cassert>
#include <memory>

#include "jni_bridge.h"
#include "jni_helper.h"
#include "jniunoenvironmentdata.hxx"

#include <jvmaccess/unovirtualmachine.hxx>
#include <rtl/ref.hxx>
#include <uno/lbnames.h>

using namespace ::jni_uno;

namespace
{
extern "C"
{


void Mapping_acquire( uno_Mapping * mapping ) noexcept
{
    Mapping const * that = static_cast< Mapping const * >( mapping );
    that->m_bridge->acquire();
}


void Mapping_release( uno_Mapping * mapping ) noexcept
{
    Mapping const * that = static_cast< Mapping const * >( mapping );
    that->m_bridge->release();
}


void Mapping_map_to_uno(
    uno_Mapping * mapping, void ** ppOut,
    void * pIn, typelib_InterfaceTypeDescription * td ) noexcept
{
    uno_Interface ** ppUnoI = reinterpret_cast<uno_Interface **>(ppOut);
    jobject javaI = static_cast<jobject>(pIn);

    static_assert(sizeof (void *) == sizeof (jobject), "must be the same size");
    assert(ppUnoI != nullptr);
    assert(td != nullptr);

    if (javaI == nullptr)
    {
        if (*ppUnoI != nullptr)
        {
            uno_Interface * p = *ppUnoI;
            (*p->release)( p );
            *ppUnoI = nullptr;
        }
    }
    else
    {
        try
        {
            Bridge const * bridge =
                static_cast< Mapping const * >( mapping )->m_bridge;
            JNI_guarded_context jni(
                bridge->getJniInfo(),
                (static_cast<jni_uno::JniUnoEnvironmentData *>(
                    bridge->m_java_env->pContext)
                 ->machine));

            JNI_interface_type_info const * info =
                static_cast< JNI_interface_type_info const * >(
                    bridge->getJniInfo()->get_type_info(
                        jni, &td->aBase ) );
            uno_Interface * pUnoI = bridge->map_to_uno( jni, javaI, info );
            if (*ppUnoI != nullptr)
            {
                uno_Interface * p = *ppUnoI;
                (*p->release)( p );
            }
            *ppUnoI = pUnoI;
        }
        catch (const BridgeRuntimeError & err)
        {
            SAL_WARN(
                "bridges",
                "ignoring BridgeRuntimeError \"" << err.m_message << "\"");
        }
        catch (const ::jvmaccess::VirtualMachine::AttachGuard::CreationException &)
        {
            SAL_WARN("bridges""attaching current thread to java failed");
        }
    }
}


void Mapping_map_to_java(
    uno_Mapping * mapping, void ** ppOut,
    void * pIn, typelib_InterfaceTypeDescription * td ) noexcept
{
    jobject * ppJavaI = reinterpret_cast<jobject *>(ppOut);
    uno_Interface * pUnoI = static_cast<uno_Interface *>(pIn);

    static_assert(sizeof (void *) == sizeof (jobject), "must be the same size");
    assert(ppJavaI != nullptr);
    assert(td != nullptr);

    try
    {
        if (pUnoI == nullptr)
        {
            if (*ppJavaI != nullptr)
            {
                Bridge const * bridge =
                    static_cast< Mapping const * >( mapping )->m_bridge;
                JNI_guarded_context jni(
                    bridge->getJniInfo(),
                    (static_cast<jni_uno::JniUnoEnvironmentData *>(
                        bridge->m_java_env->pContext)
                     ->machine));
                jni->DeleteGlobalRef( *ppJavaI );
                *ppJavaI = nullptr;
            }
        }
        else
        {
            Bridge const * bridge =
                static_cast< Mapping const * >( mapping )->m_bridge;
            JNI_guarded_context jni(
                bridge->getJniInfo(),
                (static_cast<jni_uno::JniUnoEnvironmentData *>(
                    bridge->m_java_env->pContext)
                 ->machine));

            JNI_interface_type_info const * info =
                static_cast< JNI_interface_type_info const * >(
                    bridge->getJniInfo()->get_type_info(
                        jni, &td->aBase ) );
            jobject jlocal = bridge->map_to_java( jni, pUnoI, info );
            if (*ppJavaI != nullptr)
                jni->DeleteGlobalRef( *ppJavaI );
            *ppJavaI = jni->NewGlobalRef( jlocal );
            jni->DeleteLocalRef( jlocal );
        }
    }
    catch (const BridgeRuntimeError & err)
    {
        SAL_WARN(
            "bridges",
            "ignoring BridgeRuntimeError \"" << err.m_message << "\"");
    }
    catch (const ::jvmaccess::VirtualMachine::AttachGuard::CreationException &)
    {
        SAL_WARN("bridges""attaching current thread to java failed");
    }
}


void Bridge_free( uno_Mapping * mapping ) noexcept
{
    Mapping * that = static_cast< Mapping * >( mapping );
    delete that->m_bridge;
}

}

}

namespace jni_uno
{


void Bridge::acquire() const
{
    if (++m_ref != 1)
        return;

    if (m_registered_java2uno)
    {
        uno_Mapping * mapping = const_cast< Mapping * >( &m_java2uno );
        uno_registerMapping(
            &mapping, Bridge_free,
            m_java_env, &m_uno_env->aBase, nullptr );
    }
    else
    {
        uno_Mapping * mapping = const_cast< Mapping * >( &m_uno2java );
        uno_registerMapping(
            &mapping, Bridge_free,
            &m_uno_env->aBase, m_java_env, nullptr );
    }
}


void Bridge::release() const
{
    if (! --m_ref )
    {
        uno_revokeMapping(
            m_registered_java2uno
            ? const_cast< Mapping * >( &m_java2uno )
            : const_cast< Mapping * >( &m_uno2java ) );
    }
}


Bridge::Bridge(
    uno_Environment * java_env, uno_ExtEnvironment * uno_env,
    bool registered_java2uno )
    : m_ref( 1 ),
      m_uno_env( uno_env ),
      m_java_env( java_env ),
      m_registered_java2uno( registered_java2uno )
{
    assert(m_java_env != nullptr);
    assert(m_uno_env != nullptr);

    // uno_initEnvironment (below) cannot report errors directly, so it clears
    // its pContext upon error to indirectly report errors from here:
    if (static_cast<jni_uno::JniUnoEnvironmentData *>(m_java_env->pContext)
        == nullptr)
    {
        throw BridgeRuntimeError(u"error during JNI-UNO's uno_initEnvironment"_ustr);
    }

    (*m_uno_env->aBase.acquire)( &m_uno_env->aBase );
    (*m_java_env->acquire)( m_java_env );

    // java2uno
    m_java2uno.acquire = Mapping_acquire;
    m_java2uno.release = Mapping_release;
    m_java2uno.mapInterface = Mapping_map_to_uno;
    m_java2uno.m_bridge = this;
    // uno2java
    m_uno2java.acquire = Mapping_acquire;
    m_uno2java.release = Mapping_release;
    m_uno2java.mapInterface = Mapping_map_to_java;
    m_uno2java.m_bridge = this;
}


Bridge::~Bridge()
{
    (*m_java_env->release)( m_java_env );
    (*m_uno_env->aBase.release)( &m_uno_env->aBase );
}

JNI_info const * Bridge::getJniInfo() const {
    return static_cast<jni_uno::JniUnoEnvironmentData *>(m_java_env->pContext)
        ->info;
}

void JNI_context::java_exc_occurred() const
{
    // !don't rely on JNI_info!

    JLocalAutoRef jo_exc( *this, m_env->ExceptionOccurred() );
    m_env->ExceptionClear();
    assert(jo_exc.is());
    if (! jo_exc.is())
    {
        throw BridgeRuntimeError(
            "java exception occurred, but not available!?" +
            get_stack_trace() );
    }

    // call toString(); don't rely on m_jni_info
    jclass jo_class = m_env->FindClass( "java/lang/Object" );
    if (m_env->ExceptionCheck())
    {
        m_env->ExceptionClear();
        throw BridgeRuntimeError(
            "cannot get class java.lang.Object!" + get_stack_trace() );
    }
    JLocalAutoRef jo_Object( *this, jo_class );
    // method Object.toString()
    jmethodID method_Object_toString = m_env->GetMethodID(
        static_cast<jclass>(jo_Object.get()), "toString""()Ljava/lang/String;" );
    if (m_env->ExceptionCheck())
    {
        m_env->ExceptionClear();
        throw BridgeRuntimeError(
            "cannot get method id of java.lang.Object.toString()!" +
            get_stack_trace() );
    }
    assert(method_Object_toString != nullptr);

    JLocalAutoRef jo_descr(
        *this, m_env->CallObjectMethodA(
            jo_exc.get(), method_Object_toString, nullptr ) );
    if (m_env->ExceptionCheck()) // no chance at all
    {
        m_env->ExceptionClear();
        throw BridgeRuntimeError(
            "error examining java exception object!" +
            get_stack_trace() );
    }

    jsize len = m_env->GetStringLength( static_cast<jstring>(jo_descr.get()) );
    std::unique_ptr< rtl_mem > ustr_mem(
        rtl_mem::allocate(
            sizeof (rtl_uString) + (len * sizeof (sal_Unicode)) ) );
    rtl_uString * ustr = reinterpret_cast<rtl_uString *>(ustr_mem.get());
    m_env->GetStringRegion( static_cast<jstring>(jo_descr.get()), 0, len, reinterpret_cast<jchar *>(ustr->buffer) );
    if (m_env->ExceptionCheck())
    {
        m_env->ExceptionClear();
        throw BridgeRuntimeError(
            "invalid java string object!" + get_stack_trace() );
    }
    ustr->refCount = 1;
    ustr->length = len;
    ustr->buffer[ len ] = '\0';
    OUString message( reinterpret_cast<rtl_uString *>(ustr_mem.release()), SAL_NO_ACQUIRE );

    throw BridgeRuntimeError( message + get_stack_trace( jo_exc.get() ) );
}


void JNI_context::getClassForName(
    jclass * classClass, jmethodID * methodForName) const
{
    jclass c = m_env->FindClass("java/lang/Class");
    if (c != nullptr) {
        *methodForName = m_env->GetStaticMethodID(
            c, "forName",
            "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;");
    }
    *classClass = c;
}


jclass JNI_context::findClass(
    char const * name, jclass classClass, jmethodID methodForName,
    bool inException) const
{
    jclass c = nullptr;
    JLocalAutoRef s(*this, m_env->NewStringUTF(name));
    if (s.is()) {
        jvalue a[3];
        a[0].l = s.get();
        a[1].z = JNI_FALSE;
        a[2].l = m_class_loader;
        c = static_cast< jclass >(
            m_env->CallStaticObjectMethodA(classClass, methodForName, a));
    }
    if (!inException) {
        ensure_no_exception();
    }
    return c;
}


OUString JNI_context::get_stack_trace( jobject jo_exc ) const
{
    JLocalAutoRef jo_JNI_proxy(
        *this,
        find_class( *this"com.sun.star.bridges.jni_uno.JNI_proxy"true ) );
    if (assert_no_exception())
    {
        // static method JNI_proxy.get_stack_trace()
        jmethodID method = m_env->GetStaticMethodID(
            static_cast<jclass>(jo_JNI_proxy.get()), "get_stack_trace",
            "(Ljava/lang/Throwable;)Ljava/lang/String;" );
        if (assert_no_exception() && (method != nullptr))
        {
            jvalue arg;
            arg.l = jo_exc;
            JLocalAutoRef jo_stack_trace(
                *this, m_env->CallStaticObjectMethodA(
                    static_cast<jclass>(jo_JNI_proxy.get()), method, &arg ) );
            if (assert_no_exception())
            {
                jsize len =
                    m_env->GetStringLength( static_cast<jstring>(jo_stack_trace.get()) );
                std::unique_ptr< rtl_mem > ustr_mem(
                    rtl_mem::allocate(
                        sizeof (rtl_uString) + (len * sizeof (sal_Unicode)) ) );
                rtl_uString * ustr = reinterpret_cast<rtl_uString *>(ustr_mem.get());
                m_env->GetStringRegion(
                    static_cast<jstring>(jo_stack_trace.get()), 0, len, reinterpret_cast<jchar *>(ustr->buffer) );
                if (assert_no_exception())
                {
                    ustr->refCount = 1;
                    ustr->length = len;
                    ustr->buffer[ len ] = '\0';
                    return OUString(
                        reinterpret_cast<rtl_uString *>(ustr_mem.release()), SAL_NO_ACQUIRE );
                }
            }
        }
    }
    return OUString();
}

}

using namespace ::jni_uno;

extern "C" {

static void java_env_dispose(uno_Environment * env) {
    auto * envData
        = static_cast<jni_uno::JniUnoEnvironmentData *>(env->pContext);
    if (envData == nullptr)        return;

    jobject async;
    {
        std::unique_lock g(envData->mutex);
        async = envData->asynchronousFinalizer;
        envData->asynchronousFinalizer = nullptr;
    }
    if (async == nullptr)        return;

    try {
        JNI_guarded_context jni(envData->info, envData->machine);
        jni->CallObjectMethodA(
            async, envData->info->m_method_AsynchronousFinalizer_drain,
            nullptr);
        jni.ensure_no_exception();
        jni->DeleteGlobalRef(async);
    } catch (const BridgeRuntimeError & e) {
        SAL_WARN(
            "bridges",
            "ignoring BridgeRuntimeError \"" << e.m_message << "\"");
    } catch (
        jvmaccess::VirtualMachine::AttachGuard::CreationException &)
    {
        SAL_WARN(
            "bridges",
            ("ignoring jvmaccess::VirtualMachine::AttachGuard"
             "::CreationException"));
    }
}

static void java_env_disposing(uno_Environment * env) {
    java_env_dispose(env);
    delete static_cast<jni_uno::JniUnoEnvironmentData *>(env->pContext);
}

#ifdef DISABLE_DYNLOADING
#define uno_initEnvironment java_uno_initEnvironment
#endif


SAL_DLLPUBLIC_EXPORT void uno_initEnvironment( uno_Environment * java_env ) noexcept
{
    try {
        // JavaComponentLoader::getJavaLoader (in
        // stoc/source/javaloader/javaloader.cxx) stores a
        // jvmaccess::UnoVirtualMachine pointer into java_env->pContext; replace
        // it here with either a pointer to a full JniUnoEnvironmentData upon
        // success, or with a null pointer upon failure (as this function cannot
        // directly report back failure, so it uses that way to indirectly
        // report failure later from within the Bridge ctor):
        rtl::Reference<jvmaccess::UnoVirtualMachine> vm(
            static_cast<jvmaccess::UnoVirtualMachine *>(java_env->pContext));
        java_env->pContext = nullptr;
        java_env->dispose = java_env_dispose;
        java_env->environmentDisposing = java_env_disposing;
        java_env->pExtEnv = nullptr; // no extended support
        std::unique_ptr<jni_uno::JniUnoEnvironmentData> envData(
            new jni_uno::JniUnoEnvironmentData(vm));
        {
            JNI_guarded_context jni(envData->info, envData->machine);
            JLocalAutoRef ref(
                jni,
                jni->NewObject(
                    envData->info->m_class_AsynchronousFinalizer,
                    envData->info->m_ctor_AsynchronousFinalizer));
            jni.ensure_no_exception();
            envData->asynchronousFinalizer = jni->NewGlobalRef(ref.get());
            jni.ensure_no_exception();
        }
        java_env->pContext = envData.release();
    } catch (const BridgeRuntimeError & e) {
        SAL_WARN("bridges""BridgeRuntimeError \"" << e.m_message << "\"");
    } catch (jvmaccess::VirtualMachine::AttachGuard::CreationException &) {
        SAL_WARN(
            "bridges",
            "jvmaccess::VirtualMachine::AttachGuard::CreationException");
    }
}

#ifdef DISABLE_DYNLOADING
#define uno_ext_getMapping java_uno_ext_getMapping
#endif


SAL_DLLPUBLIC_EXPORT void uno_ext_getMapping(
    uno_Mapping ** ppMapping, uno_Environment * pFrom, uno_Environment * pTo ) noexcept
{
    assert(ppMapping != nullptr);
    assert(pFrom != nullptr);
    assert(pTo != nullptr);
    if (*ppMapping != nullptr)
    {
        (*(*ppMapping)->release)( *ppMapping );
        *ppMapping = nullptr;
    }

    static_assert(int(JNI_FALSE) == int(false), "must be equal");
    static_assert(int(JNI_TRUE) == int(true), "must be equal");
    static_assert(sizeof (jboolean) == sizeof (sal_Bool), "must be the same size");
    static_assert(sizeof (jchar) == sizeof (sal_Unicode), "must be the same size");
    static_assert(sizeof (jdouble) == sizeof (double), "must be the same size");
    static_assert(sizeof (jfloat) == sizeof (float), "must be the same size");
    static_assert(sizeof (jbyte) == sizeof (sal_Int8), "must be the same size");
    static_assert(sizeof (jshort) == sizeof (sal_Int16), "must be the same size");
    static_assert(sizeof (jint) == sizeof (sal_Int32), "must be the same size");
    static_assert(sizeof (jlong) == sizeof (sal_Int64), "must be the same size");

    OUString const & from_env_typename =
        OUString::unacquired( &pFrom->pTypeName );
    OUString const & to_env_typename =
        OUString::unacquired( &pTo->pTypeName );

    uno_Mapping * mapping = nullptr;

    try
    {
        if ( from_env_typename == UNO_LB_JAVA && to_env_typename == UNO_LB_UNO )
        {
            Bridge * bridge =
                new Bridge( pFrom, pTo->pExtEnv, true ); // ref count = 1
            mapping = &bridge->m_java2uno;
            uno_registerMapping(
                &mapping, Bridge_free,
                pFrom, &pTo->pExtEnv->aBase, nullptr );
            // coverity[leaked_storage] - on purpose
        }
        else if ( from_env_typename == UNO_LB_UNO && to_env_typename == UNO_LB_JAVA )
        {
            Bridge * bridge =
                new Bridge( pTo, pFrom->pExtEnv, false ); // ref count = 1
            mapping = &bridge->m_uno2java;
            uno_registerMapping(
                &mapping, Bridge_free,
                &pFrom->pExtEnv->aBase, pTo, nullptr );
            // coverity[leaked_storage] - on purpose
        }
    }
    catch (const BridgeRuntimeError & err)
    {
        SAL_WARN("bridges""BridgeRuntimeError \"" << err.m_message << "\"");
    }

    *ppMapping = mapping;
}

}

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

Messung V0.5
C=94 H=92 G=92

¤ Dauer der Verarbeitung: 0.14 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






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 und die Messung sind noch experimentell.