summaryrefslogtreecommitdiffstats
path: root/bridges/source/jni_uno/jni_bridge.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'bridges/source/jni_uno/jni_bridge.cxx')
-rw-r--r--bridges/source/jni_uno/jni_bridge.cxx578
1 files changed, 578 insertions, 0 deletions
diff --git a/bridges/source/jni_uno/jni_bridge.cxx b/bridges/source/jni_uno/jni_bridge.cxx
new file mode 100644
index 000000000..341c466aa
--- /dev/null
+++ b/bridges/source/jni_uno/jni_bridge.cxx
@@ -0,0 +1,578 @@
+/* -*- 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 "jniunoenvironmentdata.hxx"
+
+#include <jvmaccess/unovirtualmachine.hxx>
+#include <rtl/ref.hxx>
+#include <rtl/strbuf.hxx>
+#include <uno/lbnames.h>
+
+using namespace ::osl;
+using namespace ::jni_uno;
+
+namespace
+{
+extern "C"
+{
+
+
+void Mapping_acquire( uno_Mapping * mapping )
+ SAL_THROW_EXTERN_C()
+{
+ Mapping const * that = static_cast< Mapping const * >( mapping );
+ that->m_bridge->acquire();
+}
+
+
+void Mapping_release( uno_Mapping * mapping )
+ SAL_THROW_EXTERN_C()
+{
+ 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 )
+ SAL_THROW_EXTERN_C()
+{
+ 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 )
+ SAL_THROW_EXTERN_C()
+{
+ 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 )
+ SAL_THROW_EXTERN_C()
+{
+ 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("error during JNI-UNO's uno_initEnvironment");
+ }
+
+ (*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;
+ {
+ osl::MutexGuard 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 )
+ SAL_THROW_EXTERN_C()
+{
+ 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 )
+ SAL_THROW_EXTERN_C()
+{
+ 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: */